Skip to content

Commit 0552713

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 75939c2 commit 0552713

7 files changed

Lines changed: 131 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: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6354,6 +6354,9 @@ them.
63546354
frame presentation if it is supported by the compositor (default: ``yes``).
63556355
This only has an effect if ``--video-sync=display-...`` is being used.
63566356

6357+
``--wayland-session=<string>``
6358+
Set the wayland session name for window restoration (default: ````).
6359+
63576360
``--spirv-compiler=<compiler>``
63586361
Controls which compiler is used to translate GLSL to SPIR-V. This is
63596362
only relevant for ``--gpu-api=d3d11`` with ``--vo=gpu``.

options/options.c

Lines changed: 2 additions & 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
@@ -292,6 +293,7 @@ const struct m_sub_options vo_sub_opts = {
292293
.timing_offset = 0.050,
293294
.swapchain_depth = 2,
294295
.focus_on = 1,
296+
.wayland_session = "",
295297
},
296298
};
297299

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: 112 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"
3134
#include "osdep/io.h"
35+
#include "osdep/path.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 *vo);
346+
static void write_session_id(void *talloc_ctx, struct vo *vo, const char *id);
347+
#endif
335348

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

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

2940+
#if HAVE_WAYLAND_PROTOCOLS_1_48
2941+
if (!strcmp(interface, xdg_session_manager_v1_interface.name) && found++) {
2942+
ver = 1;
2943+
struct xdg_session_manager_v1 *xdg_session_manager =
2944+
wl_registry_bind(reg, id, &xdg_session_manager_v1_interface, ver);
2945+
void *tmp = talloc_new(NULL);
2946+
char *session_id = read_session_id(tmp, wl->vo);
2947+
wl->xdg_session = xdg_session_manager_v1_get_session(xdg_session_manager,
2948+
XDG_SESSION_MANAGER_V1_REASON_LAUNCH,
2949+
session_id);
2950+
xdg_session_v1_add_listener(wl->xdg_session, &xdg_session_listener, wl);
2951+
talloc_free(tmp);
2952+
}
2953+
#endif
2954+
29002955
if (found > 1)
29012956
MP_VERBOSE(wl, "Registered interface %s at version %d\n", interface, ver);
29022957
}
@@ -3169,6 +3224,14 @@ static int create_xdg_surface(struct vo_wayland_state *wl)
31693224
MP_ERR(wl, "failed to create xdg_surface and xdg_toplevel!\n");
31703225
return 1;
31713226
}
3227+
3228+
#if HAVE_WAYLAND_PROTOCOLS_1_48
3229+
if (wl->xdg_session) {
3230+
wl->xdg_toplevel_session =
3231+
xdg_session_v1_restore_toplevel(wl->xdg_session, wl->xdg_toplevel, "mpv");
3232+
}
3233+
#endif
3234+
31723235
return 0;
31733236
}
31743237

@@ -4859,6 +4922,14 @@ void vo_wayland_uninit(struct vo *vo)
48594922
if (wl->wp_tablet_manager)
48604923
zwp_tablet_manager_v2_destroy(wl->wp_tablet_manager);
48614924

4925+
#if HAVE_WAYLAND_PROTOCOLS_1_48
4926+
if (wl->xdg_toplevel_session)
4927+
xdg_toplevel_session_v1_destroy(wl->xdg_toplevel_session);
4928+
4929+
if (wl->xdg_session)
4930+
xdg_session_v1_destroy(wl->xdg_session);
4931+
#endif
4932+
48624933
if (wl->display)
48634934
wl_display_disconnect(wl->display);
48644935

@@ -4940,3 +5011,44 @@ void vo_wayland_wakeup(struct vo *vo)
49405011
struct vo_wayland_state *wl = vo->wl;
49415012
(void)write(wl->wakeup_pipe[1], &(char){0}, 1);
49425013
}
5014+
5015+
#if HAVE_WAYLAND_PROTOCOLS_1_48
5016+
static char *session_file(void *talloc_ctx, struct vo *vo)
5017+
{
5018+
char *xdg_current_desktop = getenv("XDG_CURRENT_DESKTOP");
5019+
if (!xdg_current_desktop)
5020+
xdg_current_desktop = "";
5021+
const char *session = vo->opts->wayland_session;
5022+
char *full_name = ta_asprintf(talloc_ctx, "%16" PRIx64 "%s%16" PRIx64 "%s",
5023+
(uint64_t)strlen(xdg_current_desktop),
5024+
xdg_current_desktop, (uint64_t)strlen(session),
5025+
session);
5026+
bstr file_name = mp_hash_to_bstr(talloc_ctx, (uint8_t*)full_name,
5027+
strlen(full_name), "SHA256");
5028+
char *file_path;
5029+
file_path = (char*)mp_get_platform_path_unix(talloc_ctx, "state");
5030+
file_path = mp_path_join(talloc_ctx, file_path, "sessions");
5031+
mp_mkdirp(file_path);
5032+
file_path = mp_path_join(talloc_ctx, file_path, (char*)file_name.start);
5033+
return file_path;
5034+
}
5035+
5036+
static char *read_session_id(void *talloc_ctx, struct vo *vo)
5037+
{
5038+
char *path = session_file(talloc_ctx, vo);
5039+
if (!mp_path_exists(path))
5040+
return NULL;
5041+
bstr id = stream_read_file(path, talloc_ctx, vo->global, 4096);
5042+
if (!id.len)
5043+
return NULL;
5044+
if (bstr_validate_utf8(id) < 0)
5045+
return NULL;
5046+
return (char*)id.start;
5047+
}
5048+
5049+
static void write_session_id(void *talloc_ctx, struct vo *vo, const char *id)
5050+
{
5051+
char *path = session_file(talloc_ctx, vo);
5052+
mp_save_to_file(path, id, strlen(id));
5053+
}
5054+
#endif

video/out/wayland_common.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,12 @@ 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+
struct xdg_session_v1 *xdg_session;
205+
struct xdg_toplevel_session_v1 *xdg_toplevel_session;
206+
#endif
201207
};
202208

203209
bool vo_wayland_check_visible(struct vo *vo);

0 commit comments

Comments
 (0)