Skip to content

Commit 8181ccf

Browse files
committed
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 my 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.
1 parent 76df24f commit 8181ccf

7 files changed

Lines changed: 139 additions & 1 deletion

File tree

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
add `--wayland-session=<string>`
2+
restore the mpv window on wayland using xdg-session-management-v1

DOCS/man/options.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6356,6 +6356,11 @@ them.
63566356
frame presentation if it is supported by the compositor (default: ``yes``).
63576357
This only has an effect if ``--video-sync=display-...`` is being used.
63586358

6359+
``--wayland-session=<string>``
6360+
Set the wayland session name for window restoration (default: unset).
6361+
Not setting this or setting it to the empty string disables session
6362+
management.
6363+
63596364
``--spirv-compiler=<compiler>``
63606365
Controls which compiler is used to translate GLSL to SPIR-V. This is
63616366
only relevant for ``--gpu-api=d3d11`` with ``--vo=gpu``.

options/options.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ static const m_option_t mp_vo_opt_list[] = {
220220
{"wayland-edge-pixels-touch", OPT_INT(wl_edge_pixels_touch),
221221
M_RANGE(0, INT_MAX)},
222222
{"wayland-present", OPT_BOOL(wl_present)},
223+
{"wayland-session", OPT_STRING(wayland_session)},
223224
#endif
224225
#if HAVE_WIN32_DESKTOP
225226
// For old MinGW-w64 compatibility

options/options.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ typedef struct mp_vo_opts {
2828
char *fsscreen_name;
2929
char *winname;
3030
char *appid;
31+
char *wayland_session;
3132
int x11_netwm;
3233
int x11_bypass_compositor;
3334
int x11_present;

video/out/meson.build

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ protocols = [[wl_protocol_dir, 'stable/linux-dmabuf/linux-dmabuf-v1.xml'],
1717
wl_protocols_source = []
1818
wl_protocols_headers = []
1919

20-
foreach v: ['1.39', '1.41', '1.44', '1.47']
20+
foreach v: ['1.39', '1.41', '1.44', '1.47', '1.48']
2121
features += {'wayland-protocols-' + v.replace('.', '-'):
2222
wayland['deps'][2].version().version_compare('>=' + v)}
2323
endforeach
@@ -34,6 +34,10 @@ if features['wayland-protocols-1-44']
3434
protocols += [[wl_protocol_dir, 'staging/color-representation/color-representation-v1.xml']]
3535
endif
3636

37+
if features['wayland-protocols-1-48']
38+
protocols += [[wl_protocol_dir, 'staging/xdg-session-management/xdg-session-management-v1.xml']]
39+
endif
40+
3741
foreach p: protocols
3842
xml = join_paths(p)
3943
wl_protocols_source += custom_target(xml.underscorify() + '_c',

video/out/wayland_common.c

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,16 @@
2727
#include "common/msg.h"
2828
#include "input/input.h"
2929
#include "input/keycodes.h"
30+
#include "misc/hash.h"
31+
#include "misc/io_utils.h"
32+
#include "misc/path_utils.h"
3033
#include "options/m_config.h"
34+
#include "options/path.h"
3135
#include "osdep/io.h"
3236
#include "osdep/poll_wrapper.h"
3337
#include "osdep/timer.h"
3438
#include "present_sync.h"
39+
#include "stream/stream.h"
3540
#include "video/out/gpu/video.h"
3641
#include "wayland_common.h"
3742
#include "win_state.h"
@@ -60,6 +65,10 @@
6065
#include "color-representation-v1.h"
6166
#endif
6267

68+
#if HAVE_WAYLAND_PROTOCOLS_1_48
69+
#include "xdg-session-management-v1.h"
70+
#endif
71+
6372
#ifndef CLOCK_MONOTONIC_RAW
6473
#define CLOCK_MONOTONIC_RAW 4
6574
#endif
@@ -332,6 +341,10 @@ static void set_surface_scaling(struct vo_wayland_state *wl);
332341
static void update_output_scaling(struct vo_wayland_state *wl);
333342
static void update_output_geometry(struct vo_wayland_state *wl);
334343
static void destroy_offer(struct vo_wayland_data_offer *o);
344+
#if HAVE_WAYLAND_PROTOCOLS_1_48
345+
static char *read_session_id(void *talloc_ctx, struct vo_wayland_state *wl);
346+
static void write_session_id(void *talloc_ctx, struct vo_wayland_state *wl, const char *id);
347+
#endif
335348

336349
/* Wayland listener boilerplate */
337350
static void pointer_handle_enter(void *data, struct wl_pointer *pointer,
@@ -2718,6 +2731,33 @@ static const struct zwp_linux_dmabuf_feedback_v1_listener dmabuf_feedback_listen
27182731
.tranche_flags = tranche_flags,
27192732
};
27202733

2734+
#if HAVE_WAYLAND_PROTOCOLS_1_48
2735+
static void xdg_session_created(void *data, struct xdg_session_v1 *xdg_session_v1, const char *session_id)
2736+
{
2737+
struct vo_wayland_state *wl = data;
2738+
void *tmp = talloc_new(NULL);
2739+
write_session_id(tmp, wl, session_id);
2740+
talloc_free(tmp);
2741+
}
2742+
2743+
static void xdg_session_restored(void *data, struct xdg_session_v1 *xdg_session_v1)
2744+
{
2745+
// nothing
2746+
}
2747+
2748+
static void xdg_session_replaced(void *data, struct xdg_session_v1 *xdg_session_v1)
2749+
{
2750+
struct vo_wayland_state *wl = data;
2751+
MP_WARN(wl, "Session has been replaced!\n");
2752+
}
2753+
2754+
static const struct xdg_session_v1_listener xdg_session_listener = {
2755+
.created = xdg_session_created,
2756+
.restored = xdg_session_restored,
2757+
.replaced = xdg_session_replaced,
2758+
};
2759+
#endif
2760+
27212761
static void registry_handle_add(void *data, struct wl_registry *reg, uint32_t id,
27222762
const char *interface, uint32_t ver)
27232763
{
@@ -2896,6 +2936,25 @@ static void registry_handle_add(void *data, struct wl_registry *reg, uint32_t id
28962936
wl->wp_tablet_manager = wl_registry_bind(reg, id, &zwp_tablet_manager_v2_interface, ver);
28972937
}
28982938

2939+
#if HAVE_WAYLAND_PROTOCOLS_1_48
2940+
if (wl->wayland_session &&
2941+
!strcmp(interface, xdg_session_manager_v1_interface.name) &&
2942+
found++)
2943+
{
2944+
ver = 1;
2945+
struct xdg_session_manager_v1 *xdg_session_manager =
2946+
wl_registry_bind(reg, id, &xdg_session_manager_v1_interface, ver);
2947+
void *tmp = talloc_new(NULL);
2948+
char *session_id = read_session_id(tmp, wl);
2949+
wl->xdg_session = xdg_session_manager_v1_get_session(xdg_session_manager,
2950+
XDG_SESSION_MANAGER_V1_REASON_LAUNCH,
2951+
session_id);
2952+
xdg_session_v1_add_listener(wl->xdg_session, &xdg_session_listener, wl);
2953+
talloc_free(tmp);
2954+
xdg_session_manager_v1_destroy(xdg_session_manager);
2955+
}
2956+
#endif
2957+
28992958
if (found > 1)
29002959
MP_VERBOSE(wl, "Registered interface %s at version %d\n", interface, ver);
29012960
}
@@ -3168,6 +3227,14 @@ static int create_xdg_surface(struct vo_wayland_state *wl)
31683227
MP_ERR(wl, "failed to create xdg_surface and xdg_toplevel!\n");
31693228
return 1;
31703229
}
3230+
3231+
#if HAVE_WAYLAND_PROTOCOLS_1_48
3232+
if (wl->xdg_session) {
3233+
wl->xdg_toplevel_session =
3234+
xdg_session_v1_restore_toplevel(wl->xdg_session, wl->xdg_toplevel, "mpv");
3235+
}
3236+
#endif
3237+
31713238
return 0;
31723239
}
31733240

@@ -4459,6 +4526,9 @@ bool vo_wayland_init(struct vo *vo)
44594526
if (create_input(wl))
44604527
goto err;
44614528

4529+
if (wl->opts->wayland_session && *wl->opts->wayland_session)
4530+
wl->wayland_session = talloc_strdup(wl, wl->opts->wayland_session);
4531+
44624532
wl->registry = wl_display_get_registry(wl->display);
44634533
wl_registry_add_listener(wl->registry, &registry_listener, wl);
44644534

@@ -4861,6 +4931,14 @@ void vo_wayland_uninit(struct vo *vo)
48614931
if (wl->wp_tablet_manager)
48624932
zwp_tablet_manager_v2_destroy(wl->wp_tablet_manager);
48634933

4934+
#if HAVE_WAYLAND_PROTOCOLS_1_48
4935+
if (wl->xdg_toplevel_session)
4936+
xdg_toplevel_session_v1_destroy(wl->xdg_toplevel_session);
4937+
4938+
if (wl->xdg_session)
4939+
xdg_session_v1_destroy(wl->xdg_session);
4940+
#endif
4941+
48644942
if (wl->display)
48654943
wl_display_disconnect(wl->display);
48664944

@@ -4942,3 +5020,43 @@ void vo_wayland_wakeup(struct vo *vo)
49425020
struct vo_wayland_state *wl = vo->wl;
49435021
(void)write(wl->wakeup_pipe[1], &(char){0}, 1);
49445022
}
5023+
5024+
#if HAVE_WAYLAND_PROTOCOLS_1_48
5025+
static char *session_file(void *talloc_ctx, struct vo_wayland_state *wl)
5026+
{
5027+
char *xdg_current_desktop = getenv("XDG_CURRENT_DESKTOP");
5028+
if (!xdg_current_desktop)
5029+
xdg_current_desktop = "";
5030+
const char *session = wl->wayland_session;
5031+
bstr full_name = {0};
5032+
bstr_xappend_asprintf(talloc_ctx, &full_name, "%16" PRIx64 "%s%16" PRIx64 "%s",
5033+
(uint64_t)strlen(xdg_current_desktop),
5034+
xdg_current_desktop, (uint64_t)strlen(session),
5035+
session);
5036+
bstr file_name = mp_hash_to_bstr(talloc_ctx, full_name.start, full_name.len,
5037+
"SHA256");
5038+
char *file_path = mp_find_user_file(talloc_ctx, wl->vo->global, "state", "sessions");
5039+
mp_mkdirp(file_path);
5040+
file_path = mp_path_join_bstr(talloc_ctx, bstr0(file_path), file_name);
5041+
return file_path;
5042+
}
5043+
5044+
static char *read_session_id(void *talloc_ctx, struct vo_wayland_state *wl)
5045+
{
5046+
char *path = session_file(talloc_ctx, wl);
5047+
if (!mp_path_exists(path))
5048+
return NULL;
5049+
bstr id = stream_read_file(path, talloc_ctx, wl->vo->global, 4096);
5050+
if (!id.len)
5051+
return NULL;
5052+
if (bstr_validate_utf8(id) < 0)
5053+
return NULL;
5054+
return bstrto0(talloc_ctx, id);
5055+
}
5056+
5057+
static void write_session_id(void *talloc_ctx, struct vo_wayland_state *wl, const char *id)
5058+
{
5059+
char *path = session_file(talloc_ctx, wl);
5060+
mp_save_to_file(path, id, strlen(id));
5061+
}
5062+
#endif

video/out/wayland_common.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,13 @@ struct vo_wayland_state {
198198
bool cursor_visible;
199199
int allocated_cursor_scale;
200200
struct vo_wayland_seat *last_button_seat;
201+
202+
#if HAVE_WAYLAND_PROTOCOLS_1_48
203+
/* Session Management */
204+
char *wayland_session;
205+
struct xdg_session_v1 *xdg_session;
206+
struct xdg_toplevel_session_v1 *xdg_toplevel_session;
207+
#endif
201208
};
202209

203210
bool vo_wayland_check_visible(struct vo *vo);

0 commit comments

Comments
 (0)