mirror of
https://github.com/swaywm/sway.git
synced 2026-04-16 08:21:30 -04:00
Add support for xdg-session-management-v1
See https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5321
This commit is contained in:
parent
1606311553
commit
c542c69170
9 changed files with 500 additions and 2 deletions
|
|
@ -130,6 +130,12 @@ struct sway_server {
|
|||
struct wlr_ext_workspace_manager_v1 *workspace_manager_v1;
|
||||
struct wl_listener workspace_manager_v1_commit;
|
||||
|
||||
struct {
|
||||
char *dir;
|
||||
FILE *urandom;
|
||||
struct wl_listener new_session;
|
||||
} xdg_session_manager_v1;
|
||||
|
||||
struct wl_list pending_launcher_ctxs; // launcher_ctx::link
|
||||
|
||||
// The timeout for transactions, after which a transaction is applied
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ struct sway_view_impl {
|
|||
struct sway_view *ancestor);
|
||||
void (*close)(struct sway_view *view);
|
||||
void (*close_popups)(struct sway_view *view);
|
||||
void (*notify_state_update)(struct sway_view *view);
|
||||
void (*destroy)(struct sway_view *view);
|
||||
};
|
||||
|
||||
|
|
@ -113,6 +114,12 @@ struct sway_view {
|
|||
#endif
|
||||
};
|
||||
|
||||
struct {
|
||||
bool pending;
|
||||
char *workspace;
|
||||
bool floating;
|
||||
} session_restore;
|
||||
|
||||
struct {
|
||||
struct wl_signal unmap;
|
||||
} events;
|
||||
|
|
@ -131,6 +138,12 @@ struct sway_xdg_shell_view {
|
|||
struct wlr_scene_tree *image_capture_tree;
|
||||
char *tag;
|
||||
|
||||
struct {
|
||||
struct sway_xdg_session_v1 *session;
|
||||
char *name;
|
||||
struct wl_list link; // sway_xdg_session_v1.toplevels
|
||||
} xdg_session_v1;
|
||||
|
||||
struct wl_listener commit;
|
||||
struct wl_listener request_move;
|
||||
struct wl_listener request_resize;
|
||||
|
|
@ -366,4 +379,6 @@ bool view_can_tear(struct sway_view *view);
|
|||
|
||||
void xdg_toplevel_tag_manager_v1_handle_set_tag(struct wl_listener *listener, void *data);
|
||||
|
||||
void view_notify_state_update(struct sway_view *view);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
12
include/sway/xdg_session_management_v1.h
Normal file
12
include/sway/xdg_session_management_v1.h
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef _SWAY_XDG_SESSION_MANAGEMENT_V1_H
|
||||
#define _SWAY_XDG_SESSION_MANAGEMENT_V1_H
|
||||
|
||||
struct sway_xdg_session_v1;
|
||||
|
||||
bool init_xdg_session_management_v1(struct sway_server *server);
|
||||
void finish_xdg_session_management_v1(struct sway_server *server);
|
||||
|
||||
void notify_xdg_session_management_v1_toplevel_initial_configure(struct sway_xdg_shell_view *view);
|
||||
void notify_xdg_session_management_v1_toplevel_update(struct sway_xdg_shell_view *view);
|
||||
|
||||
#endif
|
||||
|
|
@ -18,6 +18,7 @@
|
|||
#include "sway/tree/view.h"
|
||||
#include "sway/tree/workspace.h"
|
||||
#include "sway/xdg_decoration.h"
|
||||
#include "sway/xdg_session_management_v1.h"
|
||||
|
||||
static struct sway_xdg_popup *popup_create(
|
||||
struct wlr_xdg_popup *wlr_popup, struct sway_view *view,
|
||||
|
|
@ -193,7 +194,8 @@ static void set_activated(struct sway_view *view, bool activated) {
|
|||
}
|
||||
|
||||
static void set_tiled(struct sway_view *view, bool tiled) {
|
||||
if (xdg_shell_view_from_view(view) == NULL) {
|
||||
struct sway_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
|
||||
if (xdg_shell_view == NULL) {
|
||||
return;
|
||||
}
|
||||
if (wl_resource_get_version(view->wlr_xdg_toplevel->resource) >=
|
||||
|
|
@ -209,6 +211,8 @@ static void set_tiled(struct sway_view *view, bool tiled) {
|
|||
// to stop the client from drawing decorations outside of the toplevel geometry.
|
||||
wlr_xdg_toplevel_set_maximized(view->wlr_xdg_toplevel, tiled);
|
||||
}
|
||||
|
||||
notify_xdg_session_management_v1_toplevel_update(xdg_shell_view);
|
||||
}
|
||||
|
||||
static void set_fullscreen(struct sway_view *view, bool fullscreen) {
|
||||
|
|
@ -263,6 +267,11 @@ static void close_popups(struct sway_view *view) {
|
|||
}
|
||||
}
|
||||
|
||||
static void notify_state_update(struct sway_view *view) {
|
||||
struct sway_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
|
||||
notify_xdg_session_management_v1_toplevel_update(xdg_shell_view);
|
||||
}
|
||||
|
||||
static void destroy(struct sway_view *view) {
|
||||
struct sway_xdg_shell_view *xdg_shell_view =
|
||||
xdg_shell_view_from_view(view);
|
||||
|
|
@ -285,6 +294,7 @@ static const struct sway_view_impl view_impl = {
|
|||
.is_transient_for = is_transient_for,
|
||||
.close = _close,
|
||||
.close_popups = close_popups,
|
||||
.notify_state_update = notify_state_update,
|
||||
.destroy = destroy,
|
||||
};
|
||||
|
||||
|
|
@ -303,6 +313,8 @@ static void handle_commit(struct wl_listener *listener, void *data) {
|
|||
wlr_xdg_toplevel_set_wm_capabilities(view->wlr_xdg_toplevel,
|
||||
WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN);
|
||||
// TODO: wlr_xdg_toplevel_set_bounds()
|
||||
|
||||
notify_xdg_session_management_v1_toplevel_initial_configure(xdg_shell_view);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -528,6 +540,8 @@ static void handle_map(struct wl_listener *listener, void *data) {
|
|||
xdg_shell_view->set_app_id.notify = handle_set_app_id;
|
||||
wl_signal_add(&toplevel->events.set_app_id,
|
||||
&xdg_shell_view->set_app_id);
|
||||
|
||||
notify_xdg_session_management_v1_toplevel_update(xdg_shell_view);
|
||||
}
|
||||
|
||||
static void handle_destroy(struct wl_listener *listener, void *data) {
|
||||
|
|
@ -545,6 +559,8 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
|
|||
if (view->xdg_decoration) {
|
||||
view->xdg_decoration->view = NULL;
|
||||
}
|
||||
wl_list_remove(&xdg_shell_view->xdg_session_v1.link);
|
||||
free(xdg_shell_view->xdg_session_v1.name);
|
||||
view_begin_destroy(view);
|
||||
}
|
||||
|
||||
|
|
@ -572,6 +588,8 @@ void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data) {
|
|||
}
|
||||
xdg_shell_view->view.wlr_xdg_toplevel = xdg_toplevel;
|
||||
|
||||
wl_list_init(&xdg_shell_view->xdg_session_v1.link);
|
||||
|
||||
xdg_shell_view->map.notify = handle_map;
|
||||
wl_signal_add(&xdg_toplevel->base->surface->events.map, &xdg_shell_view->map);
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ sway_sources = files(
|
|||
'swaynag.c',
|
||||
'xdg_activation_v1.c',
|
||||
'xdg_decoration.c',
|
||||
'xdg_session_management_v1.c',
|
||||
|
||||
'desktop/idle_inhibit_v1.c',
|
||||
'desktop/layer_shell.c',
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@
|
|||
#include "sway/input/cursor.h"
|
||||
#include "sway/tree/root.h"
|
||||
#include "sway/tree/workspace.h"
|
||||
#include "sway/xdg_session_management_v1.h"
|
||||
|
||||
#if WLR_HAS_XWAYLAND
|
||||
#include <wlr/xwayland/shell.h>
|
||||
|
|
@ -662,6 +663,11 @@ bool server_init(struct sway_server *server) {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!init_xdg_session_management_v1(server)) {
|
||||
sway_log(SWAY_ERROR, "Failed to create XDG session manager");
|
||||
return false;
|
||||
}
|
||||
|
||||
wl_list_init(&server->pending_launcher_ctxs);
|
||||
|
||||
// Avoid using "wayland-0" as display socket
|
||||
|
|
@ -739,6 +745,7 @@ void server_fini(struct sway_server *server) {
|
|||
wl_list_remove(&server->request_set_cursor_shape.link);
|
||||
wl_list_remove(&server->new_foreign_toplevel_capture_request.link);
|
||||
wl_list_remove(&server->workspace_manager_v1_commit.link);
|
||||
finish_xdg_session_management_v1(server);
|
||||
input_manager_finish(server->input);
|
||||
|
||||
// TODO: free sway-specific resources
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@ void view_destroy(struct sway_view *view) {
|
|||
}
|
||||
wl_list_remove(&view->events.unmap.listener_list);
|
||||
list_free(view->executed_criteria);
|
||||
free(view->session_restore.workspace);
|
||||
|
||||
view_assign_ctx(view, NULL);
|
||||
wlr_scene_node_destroy(&view->image_capture_scene->tree.node);
|
||||
|
|
@ -618,6 +619,15 @@ static struct sway_workspace *select_workspace(struct sway_view *view) {
|
|||
return ws;
|
||||
}
|
||||
|
||||
// Check session restoration
|
||||
if (view->session_restore.pending && view->session_restore.workspace != NULL) {
|
||||
ws = workspace_by_name(view->session_restore.workspace);
|
||||
if (ws) {
|
||||
view_assign_ctx(view, NULL);
|
||||
return ws;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if there's a PID mapping
|
||||
ws = view->ctx ? launcher_ctx_get_workspace(view->ctx) : NULL;
|
||||
if (ws) {
|
||||
|
|
@ -859,7 +869,14 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
|
|||
view_update_csd_from_client(view, decoration);
|
||||
}
|
||||
|
||||
if (view->impl->wants_floating && view->impl->wants_floating(view)) {
|
||||
bool floating;
|
||||
if (view->session_restore.pending) {
|
||||
floating = view->session_restore.floating;
|
||||
} else {
|
||||
floating = view->impl->wants_floating && view->impl->wants_floating(view);
|
||||
}
|
||||
|
||||
if (floating) {
|
||||
view->container->pending.border = config->floating_border;
|
||||
view->container->pending.border_thickness = config->floating_border_thickness;
|
||||
container_set_floating(view->container, true);
|
||||
|
|
@ -909,6 +926,10 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface,
|
|||
input_manager_set_focus(&view->container->node);
|
||||
}
|
||||
|
||||
view->session_restore.pending = false;
|
||||
free(view->session_restore.workspace);
|
||||
view->session_restore.workspace = NULL;
|
||||
|
||||
if (view->ext_foreign_toplevel) {
|
||||
update_ext_foreign_toplevel(view);
|
||||
}
|
||||
|
|
@ -1278,3 +1299,9 @@ void view_send_frame_done(struct sway_view *view) {
|
|||
wlr_scene_node_for_each_buffer(node, send_frame_done_iterator, &when);
|
||||
}
|
||||
}
|
||||
|
||||
void view_notify_state_update(struct sway_view *view) {
|
||||
if (view->impl->notify_state_update) {
|
||||
view->impl->notify_state_update(view);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -881,7 +881,11 @@ struct sway_container *workspace_find_container(struct sway_workspace *ws,
|
|||
}
|
||||
|
||||
static void set_workspace(struct sway_container *container, void *data) {
|
||||
bool changed = container->pending.workspace != container->pending.parent->pending.workspace;
|
||||
container->pending.workspace = container->pending.parent->pending.workspace;
|
||||
if (changed && container->view) {
|
||||
view_notify_state_update(container->view);
|
||||
}
|
||||
}
|
||||
|
||||
static void workspace_attach_tiling(struct sway_workspace *ws,
|
||||
|
|
|
|||
408
sway/xdg_session_management_v1.c
Normal file
408
sway/xdg_session_management_v1.c
Normal file
|
|
@ -0,0 +1,408 @@
|
|||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <json_object.h>
|
||||
#include <json_util.h>
|
||||
#include <wlr/types/wlr_security_context_v1.h>
|
||||
#include <wlr/types/wlr_xdg_session_management_v1.h>
|
||||
#include <wlr/types/wlr_xdg_shell.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "stringop.h"
|
||||
#include "sway/server.h"
|
||||
#include "sway/tree/view.h"
|
||||
#include "sway/tree/workspace.h"
|
||||
#include "sway/xdg_session_management_v1.h"
|
||||
|
||||
struct sway_xdg_session_v1 {
|
||||
struct wlr_xdg_session_v1 *wlr; // may be NULL
|
||||
char *path;
|
||||
json_object *restorable_toplevels;
|
||||
struct wl_list toplevels; // sway_xdg_shell_view.xdg_session_v1_link
|
||||
|
||||
bool save_pending;
|
||||
struct wl_event_source *save_timer;
|
||||
|
||||
struct wl_listener destroy;
|
||||
struct wl_listener remove;
|
||||
struct wl_listener add_toplevel;
|
||||
struct wl_listener restore_toplevel;
|
||||
struct wl_listener remove_toplevel;
|
||||
};
|
||||
|
||||
static char *get_directory(void) {
|
||||
const char *home = getenv("HOME");
|
||||
const char *xdg_state_home = getenv("XDG_STATE_HOME");
|
||||
char *xdg_state_home_default = NULL;
|
||||
if (xdg_state_home == NULL && home != NULL) {
|
||||
xdg_state_home_default = format_str("%s/.local/state", home);
|
||||
xdg_state_home = xdg_state_home_default;
|
||||
}
|
||||
if (xdg_state_home == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *path = format_str("%s/sway", xdg_state_home);
|
||||
free(xdg_state_home_default);
|
||||
return path;
|
||||
}
|
||||
|
||||
static char *get_session_path(struct wl_client *client, const char *session_id) {
|
||||
char *prefix = NULL;
|
||||
const struct wlr_security_context_v1_state *security_context =
|
||||
wlr_security_context_manager_v1_lookup_client(server.security_context_manager_v1, client);
|
||||
if (security_context != NULL &&
|
||||
strchr(security_context->sandbox_engine, '/') == NULL &&
|
||||
strchr(security_context->app_id, '/') == NULL) {
|
||||
prefix = format_str("%s_%s_", security_context->sandbox_engine, security_context->app_id);
|
||||
}
|
||||
|
||||
char *path = format_str("%s/%s%s.json", server.xdg_session_manager_v1.dir,
|
||||
prefix ? prefix : "", session_id);
|
||||
free(prefix);
|
||||
return path;
|
||||
}
|
||||
|
||||
static FILE *open_urandom(void) {
|
||||
int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
sway_log_errno(SWAY_ERROR, "Failed to open /dev/urandom");
|
||||
return NULL;
|
||||
}
|
||||
FILE *f = fdopen(fd, "r");
|
||||
if (f == NULL) {
|
||||
sway_log_errno(SWAY_ERROR, "fdopen() failed");
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
#define TOKEN_SIZE 33
|
||||
|
||||
static bool generate_token(char out[static TOKEN_SIZE]) {
|
||||
FILE *urandom = server.xdg_session_manager_v1.urandom;
|
||||
uint64_t data[2];
|
||||
|
||||
if (fread(data, sizeof(data), 1, urandom) != 1) {
|
||||
sway_log_errno(SWAY_ERROR, "Failed to read from random device");
|
||||
return false;
|
||||
}
|
||||
if (snprintf(out, TOKEN_SIZE, "%016" PRIx64 "%016" PRIx64, data[0], data[1]) != TOKEN_SIZE - 1) {
|
||||
sway_log_errno(SWAY_ERROR, "Failed to format hex string token");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static json_object *session_to_json(struct sway_xdg_session_v1 *session) {
|
||||
json_object *toplevels_obj = json_object_new_object();
|
||||
struct sway_xdg_shell_view *view;
|
||||
wl_list_for_each(view, &session->toplevels, xdg_session_v1.link) {
|
||||
struct sway_container *container = view->view.container;
|
||||
json_object *toplevel_obj = json_object_new_object();
|
||||
json_object_object_add(toplevel_obj, "floating",
|
||||
json_object_new_boolean(container_is_floating(container)));
|
||||
json_object_object_add(toplevel_obj, "floating",
|
||||
json_object_new_string(container->pending.workspace->name));
|
||||
// TODO: more
|
||||
json_object_object_add(toplevels_obj, view->xdg_session_v1.name, toplevel_obj);
|
||||
}
|
||||
|
||||
json_object *session_obj = json_object_new_object();
|
||||
json_object_object_add(session_obj, "toplevels", toplevels_obj);
|
||||
return session_obj;
|
||||
}
|
||||
|
||||
static void session_save(struct sway_xdg_session_v1 *session) {
|
||||
json_object *session_obj = session_to_json(session);
|
||||
int ret = json_object_to_file(session->path, session_obj);
|
||||
json_object_put(session_obj);
|
||||
if (ret < 0) {
|
||||
sway_log(SWAY_ERROR, "Failed to save XDG session to '%s'", session->path);
|
||||
}
|
||||
}
|
||||
|
||||
int session_handle_save_timer(void *data) {
|
||||
struct sway_xdg_session_v1 *session = data;
|
||||
session->save_pending = false;
|
||||
session_save(session);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void session_schedule_save(struct sway_xdg_session_v1 *session) {
|
||||
if (!session->save_pending) {
|
||||
wl_event_source_timer_update(session->save_timer, 30 * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
static json_object *load_session(const char *path) {
|
||||
int fd = open(path, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
if (errno != ENOENT) {
|
||||
sway_log_errno(SWAY_ERROR, "Failed to read XDG session from '%s'", path);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
json_object *session_obj = json_object_from_fd(fd);
|
||||
close(fd);
|
||||
if (session_obj == NULL) {
|
||||
sway_log(SWAY_ERROR, "Failed to load XDG session from '%s'", path);
|
||||
}
|
||||
return session_obj;
|
||||
}
|
||||
|
||||
static void session_consider_destroy(struct sway_xdg_session_v1 *session) {
|
||||
// TODO: call this function on toplevel destroy
|
||||
if (session->wlr || !wl_list_empty(&session->toplevels)) {
|
||||
return;
|
||||
}
|
||||
if (session->save_pending) {
|
||||
session_save(session);
|
||||
}
|
||||
wl_event_source_remove(session->save_timer);
|
||||
json_object_put(session->restorable_toplevels);
|
||||
free(session->path);
|
||||
free(session);
|
||||
}
|
||||
|
||||
static void session_handle_destroy(struct wl_listener *listener, void *data) {
|
||||
struct sway_xdg_session_v1 *session = wl_container_of(listener, session, destroy);
|
||||
wl_list_remove(&session->destroy.link);
|
||||
wl_list_remove(&session->remove.link);
|
||||
wl_list_remove(&session->add_toplevel.link);
|
||||
wl_list_remove(&session->restore_toplevel.link);
|
||||
wl_list_remove(&session->remove_toplevel.link);
|
||||
session->wlr = NULL;
|
||||
session_consider_destroy(session);
|
||||
}
|
||||
|
||||
static void session_handle_remove(struct wl_listener *listener, void *data) {
|
||||
struct sway_xdg_session_v1 *session = wl_container_of(listener, session, remove);
|
||||
|
||||
if (unlink(session->path) != 0 && errno != ENOENT) {
|
||||
sway_log_errno(SWAY_ERROR, "Failed to delete XDG session '%s'", session->path);
|
||||
}
|
||||
}
|
||||
|
||||
static void session_add_toplevel(struct sway_xdg_session_v1 *session,
|
||||
struct wlr_xdg_toplevel_session_v1 *toplevel_session, bool restore) {
|
||||
struct sway_xdg_shell_view *view = toplevel_session->toplevel->base->data;
|
||||
|
||||
char *name = strdup(toplevel_session->name);
|
||||
if (name == NULL) {
|
||||
wl_resource_post_no_memory(toplevel_session->resource);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: send error on duplicate session or name
|
||||
|
||||
view->xdg_session_v1.session = session;
|
||||
view->xdg_session_v1.name = name;
|
||||
wl_list_remove(&view->xdg_session_v1.link);
|
||||
wl_list_insert(&session->toplevels, &view->xdg_session_v1.link);
|
||||
|
||||
// TODO: listen to rename event
|
||||
|
||||
if (!restore) {
|
||||
session_schedule_save(session);
|
||||
}
|
||||
}
|
||||
|
||||
static void session_handle_add_toplevel(struct wl_listener *listener, void *data) {
|
||||
struct sway_xdg_session_v1 *session = wl_container_of(listener, session, add_toplevel);
|
||||
struct wlr_xdg_toplevel_session_v1 *toplevel_session = data;
|
||||
session_add_toplevel(session, toplevel_session, false);
|
||||
}
|
||||
|
||||
static void session_handle_restore_toplevel(struct wl_listener *listener, void *data) {
|
||||
struct sway_xdg_session_v1 *session = wl_container_of(listener, session, restore_toplevel);
|
||||
struct wlr_xdg_toplevel_session_v1 *toplevel_session = data;
|
||||
session_add_toplevel(session, toplevel_session, true);
|
||||
}
|
||||
|
||||
static void session_handle_remove_toplevel(struct wl_listener *listener, void *data) {
|
||||
struct sway_xdg_session_v1 *session = wl_container_of(listener, session, remove_toplevel);
|
||||
const struct wlr_xdg_session_v1_remove_toplevel_event *event = data;
|
||||
|
||||
json_object_object_del(session->restorable_toplevels, event->name);
|
||||
|
||||
bool found;
|
||||
struct sway_xdg_shell_view *view;
|
||||
wl_list_for_each(view, &session->toplevels, xdg_session_v1.link) {
|
||||
if (strcmp(view->xdg_session_v1.name, event->name) == 0) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: deduplicate
|
||||
view->xdg_session_v1.session = NULL;
|
||||
free(view->xdg_session_v1.name);
|
||||
view->xdg_session_v1.name = NULL;
|
||||
wl_list_remove(&view->xdg_session_v1.link);
|
||||
wl_list_init(&view->xdg_session_v1.link);
|
||||
|
||||
session_schedule_save(session);
|
||||
}
|
||||
|
||||
static void handle_new_session(struct wl_listener *listener, void *data) {
|
||||
struct wlr_xdg_session_v1 *wlr_session = data;
|
||||
struct wl_client *client = wl_resource_get_client(wlr_session->resource);
|
||||
|
||||
char *path = NULL;
|
||||
json_object *restorable_toplevels = NULL;
|
||||
if (wlr_session->id != NULL) {
|
||||
path = get_session_path(client, wlr_session->id);
|
||||
if (path == NULL) {
|
||||
wl_resource_post_no_memory(wlr_session->resource);
|
||||
return;
|
||||
}
|
||||
|
||||
json_object *session_obj = load_session(path);
|
||||
restorable_toplevels = json_object_get(json_object_object_get(session_obj, "toplevels"));
|
||||
json_object_put(session_obj);
|
||||
}
|
||||
|
||||
char new_session_id[TOKEN_SIZE];
|
||||
if (restorable_toplevels == NULL) {
|
||||
free(path);
|
||||
path = NULL;
|
||||
|
||||
if (!generate_token(new_session_id)) {
|
||||
wl_resource_post_no_memory(wlr_session->resource);
|
||||
return;
|
||||
}
|
||||
|
||||
path = get_session_path(client, new_session_id);
|
||||
if (path == NULL) {
|
||||
wl_resource_post_no_memory(wlr_session->resource);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
struct sway_xdg_session_v1 *session = calloc(1, sizeof(*session));
|
||||
if (session == NULL) {
|
||||
wl_resource_post_no_memory(wlr_session->resource);
|
||||
free(path);
|
||||
return;
|
||||
}
|
||||
|
||||
session->save_timer = wl_event_loop_add_timer(server.wl_event_loop,
|
||||
session_handle_save_timer, session);
|
||||
if (session->save_timer == NULL) {
|
||||
wl_resource_post_no_memory(wlr_session->resource);
|
||||
free(session);
|
||||
free(path);
|
||||
return;
|
||||
}
|
||||
|
||||
session->wlr = wlr_session;
|
||||
session->path = path;
|
||||
session->restorable_toplevels = restorable_toplevels;
|
||||
|
||||
session->destroy.notify = session_handle_destroy;
|
||||
wl_signal_add(&session->wlr->events.destroy, &session->destroy);
|
||||
|
||||
session->remove.notify = session_handle_remove;
|
||||
wl_signal_add(&session->wlr->events.remove, &session->remove);
|
||||
|
||||
session->add_toplevel.notify = session_handle_add_toplevel;
|
||||
wl_signal_add(&session->wlr->events.add_toplevel, &session->add_toplevel);
|
||||
|
||||
session->restore_toplevel.notify = session_handle_restore_toplevel;
|
||||
wl_signal_add(&session->wlr->events.restore_toplevel, &session->restore_toplevel);
|
||||
|
||||
session->remove_toplevel.notify = session_handle_remove_toplevel;
|
||||
wl_signal_add(&session->wlr->events.remove_toplevel, &session->remove_toplevel);
|
||||
|
||||
if (restorable_toplevels != NULL) {
|
||||
wlr_xdg_session_v1_notify_restored(session->wlr);
|
||||
} else {
|
||||
wlr_xdg_session_v1_notify_created(session->wlr, new_session_id);
|
||||
}
|
||||
}
|
||||
|
||||
bool init_xdg_session_management_v1(struct sway_server *server) {
|
||||
char *dir = get_directory();
|
||||
if (dir == NULL) {
|
||||
sway_log(SWAY_ERROR, "Failed to pick XDG session management directory");
|
||||
return false;
|
||||
}
|
||||
server->xdg_session_manager_v1.dir = dir;
|
||||
|
||||
FILE *urandom = open_urandom();
|
||||
if (urandom == NULL) {
|
||||
return false;
|
||||
}
|
||||
server->xdg_session_manager_v1.urandom = urandom;
|
||||
|
||||
struct wlr_xdg_session_manager_v1 *xdg_session_manager_v1 =
|
||||
wlr_xdg_session_manager_v1_create(server->wl_display, 1);
|
||||
if (xdg_session_manager_v1 == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
server->xdg_session_manager_v1.new_session.notify = handle_new_session;
|
||||
wl_signal_add(&xdg_session_manager_v1->events.new_session,
|
||||
&server->xdg_session_manager_v1.new_session);
|
||||
|
||||
// TODO: regularly clean up stale files
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void finish_xdg_session_management_v1(struct sway_server *server) {
|
||||
wl_list_remove(&server->xdg_session_manager_v1.new_session.link);
|
||||
free(server->xdg_session_manager_v1.dir);
|
||||
fclose(server->xdg_session_manager_v1.urandom);
|
||||
}
|
||||
|
||||
void notify_xdg_session_management_v1_toplevel_update(struct sway_xdg_shell_view *view) {
|
||||
if (view->xdg_session_v1.session) {
|
||||
session_schedule_save(view->xdg_session_v1.session);
|
||||
}
|
||||
}
|
||||
|
||||
void notify_xdg_session_management_v1_toplevel_initial_configure(struct sway_xdg_shell_view *view) {
|
||||
struct sway_xdg_session_v1 *session = view->xdg_session_v1.session;
|
||||
if (session == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
json_object *toplevel_obj = json_object_get(json_object_object_get(
|
||||
session->restorable_toplevels, view->xdg_session_v1.name));
|
||||
json_object_object_del(session->restorable_toplevels, view->xdg_session_v1.name);
|
||||
if (toplevel_obj == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (view->view.session_restore.pending) {
|
||||
return;
|
||||
}
|
||||
|
||||
view->view.session_restore.pending = true;
|
||||
view->view.session_restore.floating =
|
||||
json_object_get_boolean(json_object_object_get(toplevel_obj, "floating"));
|
||||
view->view.session_restore.workspace =
|
||||
strdup(json_object_get_string(json_object_object_get(toplevel_obj, "workspace")));
|
||||
|
||||
if (session->wlr != NULL) {
|
||||
struct wlr_xdg_toplevel_session_v1 *toplevel_session;
|
||||
wl_list_for_each(toplevel_session, &session->wlr->toplevels, link) {
|
||||
if (toplevel_session->toplevel == view->view.wlr_xdg_toplevel) {
|
||||
wlr_xdg_toplevel_session_v1_notify_restored(toplevel_session);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
json_object_put(toplevel_obj);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue