wayland: implement session management
If the compositor supports session management, the mpv window is always added to a session. The user can manage multiple sessions by using `--wayland-session=<human-readable-name>`. Human-readable names are mapped to session ids via files under `~/.local/state/mpv/sessions`. The file name is computed as a hash of XDG_CURRENT_DESKTOP and the human-readable name. If multiple mpv instances are started with the same session name, the latter mpv instance takes over the session. Upon session restoration, the mpv window is restored by the compositor according to its state from the previous session. What that entails is compositor policy.
This commit is contained in:
1 parent
98a6960a3d
commit
4c9262664c
7 files changed
+131
-1
No files matched your search
@@ -0,0 +1,2 @@
|
||||
add `--wayland-session=<string>`
|
||||
restore the mpv window on wayland using xdg-session-management-v1
|
||||
@@ -6351,6 +6351,11 @@ them.
|
||||
frame presentation if it is supported by the compositor (default: ``yes``).
|
||||
This only has an effect if ``--video-sync=display-...`` is being used.
|
||||
|
||||
``--wayland-session=<string>``
|
||||
Set the wayland session name for window restoration (default: unset).
|
||||
Not setting this or setting it to the empty string disables session
|
||||
management.
|
||||
|
||||
``--spirv-compiler=<compiler>``
|
||||
Controls which compiler is used to translate GLSL to SPIR-V. This is
|
||||
only relevant for ``--gpu-api=d3d11`` with ``--vo=gpu``.
|
||||
|
||||
@@ -220,6 +220,7 @@ static const m_option_t mp_vo_opt_list[] = {
|
||||
{"wayland-edge-pixels-touch", OPT_INT(wl_edge_pixels_touch),
|
||||
M_RANGE(0, INT_MAX)},
|
||||
{"wayland-present", OPT_BOOL(wl_present)},
|
||||
{"wayland-session", OPT_STRING(wayland_session)},
|
||||
#endif
|
||||
#if HAVE_WIN32_DESKTOP
|
||||
// For old MinGW-w64 compatibility
|
||||
|
||||
@@ -28,6 +28,7 @@ typedef struct mp_vo_opts {
|
||||
char *fsscreen_name;
|
||||
char *winname;
|
||||
char *appid;
|
||||
char *wayland_session;
|
||||
int x11_netwm;
|
||||
int x11_bypass_compositor;
|
||||
int x11_present;
|
||||
|
||||
@@ -17,7 +17,7 @@ protocols = [[wl_protocol_dir, 'stable/linux-dmabuf/linux-dmabuf-v1.xml'],
|
||||
wl_protocols_source = []
|
||||
wl_protocols_headers = []
|
||||
|
||||
foreach v: ['1.39', '1.41', '1.44', '1.47']
|
||||
foreach v: ['1.39', '1.41', '1.44', '1.47', '1.48']
|
||||
features += {'wayland-protocols-' + v.replace('.', '-'):
|
||||
wayland['deps'][2].version().version_compare('>=' + v)}
|
||||
endforeach
|
||||
@@ -34,6 +34,10 @@ if features['wayland-protocols-1-44']
|
||||
protocols += [[wl_protocol_dir, 'staging/color-representation/color-representation-v1.xml']]
|
||||
endif
|
||||
|
||||
if features['wayland-protocols-1-48']
|
||||
protocols += [[wl_protocol_dir, 'staging/xdg-session-management/xdg-session-management-v1.xml']]
|
||||
endif
|
||||
|
||||
foreach p: protocols
|
||||
xml = join_paths(p)
|
||||
wl_protocols_source += custom_target(xml.underscorify() + '_c',
|
||||
|
||||
@@ -27,11 +27,16 @@
|
||||
#include "common/msg.h"
|
||||
#include "input/input.h"
|
||||
#include "input/keycodes.h"
|
||||
#include "misc/hash.h"
|
||||
#include "misc/io_utils.h"
|
||||
#include "misc/path_utils.h"
|
||||
#include "options/m_config.h"
|
||||
#include "options/path.h"
|
||||
#include "osdep/io.h"
|
||||
#include "osdep/poll_wrapper.h"
|
||||
#include "osdep/timer.h"
|
||||
#include "present_sync.h"
|
||||
#include "stream/stream.h"
|
||||
#include "video/out/gpu/video.h"
|
||||
#include "wayland_common.h"
|
||||
#include "win_state.h"
|
||||
@@ -60,6 +65,10 @@
|
||||
#include "color-representation-v1.h"
|
||||
#endif
|
||||
|
||||
#if HAVE_WAYLAND_PROTOCOLS_1_48
|
||||
#include "xdg-session-management-v1.h"
|
||||
#endif
|
||||
|
||||
#ifndef CLOCK_MONOTONIC_RAW
|
||||
#define CLOCK_MONOTONIC_RAW 4
|
||||
#endif
|
||||
@@ -331,6 +340,10 @@ static void set_surface_scaling(struct vo_wayland_state *wl);
|
||||
static void update_output_scaling(struct vo_wayland_state *wl);
|
||||
static void update_output_geometry(struct vo_wayland_state *wl);
|
||||
static void destroy_offer(struct vo_wayland_data_offer *o);
|
||||
#if HAVE_WAYLAND_PROTOCOLS_1_48
|
||||
static char *session_file(void *talloc_ctx, const char *session, struct vo *vo);
|
||||
static char *read_session_id(void *talloc_ctx, struct vo_wayland_state *wl, const char *path);
|
||||
#endif
|
||||
|
||||
/* Wayland listener boilerplate */
|
||||
static void pointer_handle_enter(void *data, struct wl_pointer *pointer,
|
||||
@@ -2717,6 +2730,31 @@ static const struct zwp_linux_dmabuf_feedback_v1_listener dmabuf_feedback_listen
|
||||
.tranche_flags = tranche_flags,
|
||||
};
|
||||
|
||||
#if HAVE_WAYLAND_PROTOCOLS_1_48
|
||||
static void xdg_session_created(void *data, struct xdg_session_v1 *xdg_session_v1, const char *session_id)
|
||||
{
|
||||
struct vo_wayland_state *wl = data;
|
||||
mp_save_to_file(wl->session_file, session_id, strlen(session_id));
|
||||
}
|
||||
|
||||
static void xdg_session_restored(void *data, struct xdg_session_v1 *xdg_session_v1)
|
||||
{
|
||||
// nothing
|
||||
}
|
||||
|
||||
static void xdg_session_replaced(void *data, struct xdg_session_v1 *xdg_session_v1)
|
||||
{
|
||||
struct vo_wayland_state *wl = data;
|
||||
MP_WARN(wl, "Session has been replaced!\n");
|
||||
}
|
||||
|
||||
static const struct xdg_session_v1_listener xdg_session_listener = {
|
||||
.created = xdg_session_created,
|
||||
.restored = xdg_session_restored,
|
||||
.replaced = xdg_session_replaced,
|
||||
};
|
||||
#endif
|
||||
|
||||
static void registry_handle_add(void *data, struct wl_registry *reg, uint32_t id,
|
||||
const char *interface, uint32_t ver)
|
||||
{
|
||||
@@ -2895,6 +2933,25 @@ static void registry_handle_add(void *data, struct wl_registry *reg, uint32_t id
|
||||
wl->wp_tablet_manager = wl_registry_bind(reg, id, &zwp_tablet_manager_v2_interface, ver);
|
||||
}
|
||||
|
||||
#if HAVE_WAYLAND_PROTOCOLS_1_48
|
||||
if (wl->session_file &&
|
||||
!strcmp(interface, xdg_session_manager_v1_interface.name) &&
|
||||
found++)
|
||||
{
|
||||
ver = 1;
|
||||
struct xdg_session_manager_v1 *xdg_session_manager =
|
||||
wl_registry_bind(reg, id, &xdg_session_manager_v1_interface, ver);
|
||||
void *tmp = talloc_new(NULL);
|
||||
char *session_id = read_session_id(tmp, wl, wl->session_file);
|
||||
wl->xdg_session = xdg_session_manager_v1_get_session(xdg_session_manager,
|
||||
XDG_SESSION_MANAGER_V1_REASON_LAUNCH,
|
||||
session_id);
|
||||
xdg_session_v1_add_listener(wl->xdg_session, &xdg_session_listener, wl);
|
||||
talloc_free(tmp);
|
||||
xdg_session_manager_v1_destroy(xdg_session_manager);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (found > 1)
|
||||
MP_VERBOSE(wl, "Registered interface %s at version %d\n", interface, ver);
|
||||
}
|
||||
@@ -3167,6 +3224,14 @@ static int create_xdg_surface(struct vo_wayland_state *wl)
|
||||
MP_ERR(wl, "failed to create xdg_surface and xdg_toplevel!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if HAVE_WAYLAND_PROTOCOLS_1_48
|
||||
if (wl->xdg_session) {
|
||||
wl->xdg_toplevel_session =
|
||||
xdg_session_v1_restore_toplevel(wl->xdg_session, wl->xdg_toplevel, "mpv");
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -4450,6 +4515,11 @@ bool vo_wayland_init(struct vo *vo)
|
||||
if (create_input(wl))
|
||||
goto err;
|
||||
|
||||
#if HAVE_WAYLAND_PROTOCOLS_1_48
|
||||
if (wl->opts->wayland_session && *wl->opts->wayland_session)
|
||||
wl->session_file = session_file(wl, wl->opts->wayland_session, vo);
|
||||
#endif
|
||||
|
||||
wl->registry = wl_display_get_registry(wl->display);
|
||||
wl_registry_add_listener(wl->registry, ®istry_listener, wl);
|
||||
|
||||
@@ -4852,6 +4922,14 @@ void vo_wayland_uninit(struct vo *vo)
|
||||
if (wl->wp_tablet_manager)
|
||||
zwp_tablet_manager_v2_destroy(wl->wp_tablet_manager);
|
||||
|
||||
#if HAVE_WAYLAND_PROTOCOLS_1_48
|
||||
if (wl->xdg_toplevel_session)
|
||||
xdg_toplevel_session_v1_destroy(wl->xdg_toplevel_session);
|
||||
|
||||
if (wl->xdg_session)
|
||||
xdg_session_v1_destroy(wl->xdg_session);
|
||||
#endif
|
||||
|
||||
if (wl->display)
|
||||
wl_display_disconnect(wl->display);
|
||||
|
||||
@@ -4933,3 +5011,37 @@ void vo_wayland_wakeup(struct vo *vo)
|
||||
struct vo_wayland_state *wl = vo->wl;
|
||||
(void)write(wl->wakeup_pipe[1], &(char){0}, 1);
|
||||
}
|
||||
|
||||
#if HAVE_WAYLAND_PROTOCOLS_1_48
|
||||
static char *session_file(void *talloc_ctx, const char *session, struct vo *vo)
|
||||
{
|
||||
void *tmp = talloc_new(NULL);
|
||||
char *xdg_current_desktop = getenv("XDG_CURRENT_DESKTOP");
|
||||
if (!xdg_current_desktop)
|
||||
xdg_current_desktop = "";
|
||||
bstr full_name = {0};
|
||||
bstr_xappend_asprintf(tmp, &full_name, "%16" PRIx64 "%s%16" PRIx64 "%s",
|
||||
(uint64_t)strlen(xdg_current_desktop),
|
||||
xdg_current_desktop, (uint64_t)strlen(session),
|
||||
session);
|
||||
bstr file_name = mp_hash_to_bstr(tmp, full_name.start, full_name.len,
|
||||
"SHA256");
|
||||
char *file_path = mp_find_user_file(tmp, vo->global, "state", "sessions");
|
||||
mp_mkdirp(file_path);
|
||||
file_path = mp_path_join_bstr(talloc_ctx, bstr0(file_path), file_name);
|
||||
talloc_free(tmp);
|
||||
return file_path;
|
||||
}
|
||||
|
||||
static char *read_session_id(void *talloc_ctx, struct vo_wayland_state *wl, const char *path)
|
||||
{
|
||||
if (!mp_path_exists(path))
|
||||
return NULL;
|
||||
bstr id = stream_read_file(path, talloc_ctx, wl->vo->global, 4096);
|
||||
if (!id.len)
|
||||
return NULL;
|
||||
if (bstr_validate_utf8(id) < 0)
|
||||
return NULL;
|
||||
return bstrto0(talloc_ctx, id);
|
||||
}
|
||||
#endif
|
||||
@@ -198,6 +198,11 @@ struct vo_wayland_state {
|
||||
bool cursor_visible;
|
||||
int allocated_cursor_scale;
|
||||
struct vo_wayland_seat *last_button_seat;
|
||||
|
||||
/* Session Management */
|
||||
char *session_file;
|
||||
struct xdg_session_v1 *xdg_session;
|
||||
struct xdg_toplevel_session_v1 *xdg_toplevel_session;
|
||||
};
|
||||
|
||||
bool vo_wayland_check_visible(struct vo *vo);
|
||||
|
||||
Reference in new issue
Block a user