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:
Julian Orth authored and Dudemanguy committed 2026-04-24 15:36:44 +00:00
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
+5
View File
@@ -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``.
+1
View File
@@ -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
+1
View File
@@ -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;
+5 -1
View File
@@ -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',
+112
View File
@@ -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, &registry_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
+5
View File
@@ -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);