1 /*
2 * This file is part of mpv video player.
3 *
4 * mpv is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * mpv is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <errno.h>
19 #include <limits.h>
20 #include <linux/input-event-codes.h>
21 #include <poll.h>
22 #include <time.h>
23 #include <unistd.h>
24 #include <wayland-cursor.h>
25 #include <xkbcommon/xkbcommon.h>
26
27 #include "common/msg.h"
28 #include "input/input.h"
29 #include "input/keycodes.h"
30 #include "options/m_config.h"
31 #include "osdep/io.h"
32 #include "osdep/timer.h"
33 #include "wayland_common.h"
34 #include "win_state.h"
35
36 // Generated from wayland-protocols
37 #include "generated/wayland/idle-inhibit-unstable-v1.h"
38 #include "generated/wayland/presentation-time.h"
39 #include "generated/wayland/xdg-decoration-unstable-v1.h"
40 #include "generated/wayland/xdg-shell.h"
41
42 static const struct mp_keymap keymap[] = {
43 /* Special keys */
44 {XKB_KEY_Pause, MP_KEY_PAUSE}, {XKB_KEY_Escape, MP_KEY_ESC},
45 {XKB_KEY_BackSpace, MP_KEY_BS}, {XKB_KEY_Tab, MP_KEY_TAB},
46 {XKB_KEY_Return, MP_KEY_ENTER}, {XKB_KEY_Menu, MP_KEY_MENU},
47 {XKB_KEY_Print, MP_KEY_PRINT},
48
49 /* Cursor keys */
50 {XKB_KEY_Left, MP_KEY_LEFT}, {XKB_KEY_Right, MP_KEY_RIGHT},
51 {XKB_KEY_Up, MP_KEY_UP}, {XKB_KEY_Down, MP_KEY_DOWN},
52
53 /* Navigation keys */
54 {XKB_KEY_Insert, MP_KEY_INSERT}, {XKB_KEY_Delete, MP_KEY_DELETE},
55 {XKB_KEY_Home, MP_KEY_HOME}, {XKB_KEY_End, MP_KEY_END},
56 {XKB_KEY_Page_Up, MP_KEY_PAGE_UP}, {XKB_KEY_Page_Down, MP_KEY_PAGE_DOWN},
57
58 /* F-keys */
59 {XKB_KEY_F1, MP_KEY_F + 1}, {XKB_KEY_F2, MP_KEY_F + 2},
60 {XKB_KEY_F3, MP_KEY_F + 3}, {XKB_KEY_F4, MP_KEY_F + 4},
61 {XKB_KEY_F5, MP_KEY_F + 5}, {XKB_KEY_F6, MP_KEY_F + 6},
62 {XKB_KEY_F7, MP_KEY_F + 7}, {XKB_KEY_F8, MP_KEY_F + 8},
63 {XKB_KEY_F9, MP_KEY_F + 9}, {XKB_KEY_F10, MP_KEY_F +10},
64 {XKB_KEY_F11, MP_KEY_F +11}, {XKB_KEY_F12, MP_KEY_F +12},
65
66 /* Numpad independent of numlock */
67 {XKB_KEY_KP_Subtract, '-'}, {XKB_KEY_KP_Add, '+'},
68 {XKB_KEY_KP_Multiply, '*'}, {XKB_KEY_KP_Divide, '/'},
69 {XKB_KEY_KP_Enter, MP_KEY_KPENTER},
70
71 /* Numpad with numlock */
72 {XKB_KEY_KP_0, MP_KEY_KP0}, {XKB_KEY_KP_1, MP_KEY_KP1},
73 {XKB_KEY_KP_2, MP_KEY_KP2}, {XKB_KEY_KP_3, MP_KEY_KP3},
74 {XKB_KEY_KP_4, MP_KEY_KP4}, {XKB_KEY_KP_5, MP_KEY_KP5},
75 {XKB_KEY_KP_6, MP_KEY_KP6}, {XKB_KEY_KP_7, MP_KEY_KP7},
76 {XKB_KEY_KP_8, MP_KEY_KP8}, {XKB_KEY_KP_9, MP_KEY_KP9},
77 {XKB_KEY_KP_Decimal, MP_KEY_KPDEC}, {XKB_KEY_KP_Separator, MP_KEY_KPDEC},
78
79 /* Numpad without numlock */
80 {XKB_KEY_KP_Insert, MP_KEY_KPINS}, {XKB_KEY_KP_End, MP_KEY_KP1},
81 {XKB_KEY_KP_Down, MP_KEY_KP2}, {XKB_KEY_KP_Page_Down, MP_KEY_KP3},
82 {XKB_KEY_KP_Left, MP_KEY_KP4}, {XKB_KEY_KP_Begin, MP_KEY_KP5},
83 {XKB_KEY_KP_Right, MP_KEY_KP6}, {XKB_KEY_KP_Home, MP_KEY_KP7},
84 {XKB_KEY_KP_Up, MP_KEY_KP8}, {XKB_KEY_KP_Page_Up, MP_KEY_KP9},
85 {XKB_KEY_KP_Delete, MP_KEY_KPDEL},
86
87 /* Multimedia keys */
88 {XKB_KEY_XF86MenuKB, MP_KEY_MENU},
89 {XKB_KEY_XF86AudioPlay, MP_KEY_PLAY}, {XKB_KEY_XF86AudioPause, MP_KEY_PAUSE},
90 {XKB_KEY_XF86AudioStop, MP_KEY_STOP},
91 {XKB_KEY_XF86AudioPrev, MP_KEY_PREV}, {XKB_KEY_XF86AudioNext, MP_KEY_NEXT},
92 {XKB_KEY_XF86AudioRewind, MP_KEY_REWIND},
93 {XKB_KEY_XF86AudioForward, MP_KEY_FORWARD},
94 {XKB_KEY_XF86AudioMute, MP_KEY_MUTE},
95 {XKB_KEY_XF86AudioLowerVolume, MP_KEY_VOLUME_DOWN},
96 {XKB_KEY_XF86AudioRaiseVolume, MP_KEY_VOLUME_UP},
97 {XKB_KEY_XF86HomePage, MP_KEY_HOMEPAGE}, {XKB_KEY_XF86WWW, MP_KEY_WWW},
98 {XKB_KEY_XF86Mail, MP_KEY_MAIL}, {XKB_KEY_XF86Favorites, MP_KEY_FAVORITES},
99 {XKB_KEY_XF86Search, MP_KEY_SEARCH}, {XKB_KEY_XF86Sleep, MP_KEY_SLEEP},
100
101 {0, 0}
102 };
103
104 #define OPT_BASE_STRUCT struct wayland_opts
105 const struct m_sub_options wayland_conf = {
106 .opts = (const struct m_option[]) {
107 {"wayland-disable-vsync", OPT_FLAG(disable_vsync)},
108 {"wayland-edge-pixels-pointer", OPT_INT(edge_pixels_pointer),
109 M_RANGE(0, INT_MAX)},
110 {"wayland-edge-pixels-touch", OPT_INT(edge_pixels_touch),
111 M_RANGE(0, INT_MAX)},
112 {0},
113 },
114 .size = sizeof(struct wayland_opts),
115 .defaults = &(struct wayland_opts) {
116 .disable_vsync = false,
117 .edge_pixels_pointer = 10,
118 .edge_pixels_touch = 32,
119 },
120 };
121
122 struct vo_wayland_output {
123 struct vo_wayland_state *wl;
124 struct wl_output *output;
125 struct mp_rect geometry;
126 bool has_surface;
127 uint32_t id;
128 uint32_t flags;
129 int phys_width;
130 int phys_height;
131 int scale;
132 double refresh_rate;
133 char *make;
134 char *model;
135 struct wl_list link;
136 };
137
138 struct vo_wayland_sync {
139 int64_t ust;
140 int64_t msc;
141 int64_t sbc;
142 bool filled;
143 };
144
145 static int check_for_resize(struct vo_wayland_state *wl, wl_fixed_t x_w, wl_fixed_t y_w,
146 int edge_pixels, enum xdg_toplevel_resize_edge *edge);
147 static int get_mods(struct vo_wayland_state *wl);
148 static int last_available_sync(struct vo_wayland_state *wl);
149 static int lookupkey(int key);
150 static int set_cursor_visibility(struct vo_wayland_state *wl, bool on);
151 static int spawn_cursor(struct vo_wayland_state *wl);
152
153 static void greatest_common_divisor(struct vo_wayland_state *wl, int a, int b);
154 static void queue_new_sync(struct vo_wayland_state *wl);
155 static void remove_output(struct vo_wayland_output *out);
156 static void request_decoration_mode(struct vo_wayland_state *wl, uint32_t mode);
157 static void set_geometry(struct vo_wayland_state *wl);
158 static void set_surface_scaling(struct vo_wayland_state *wl);
159 static void sync_shift(struct vo_wayland_state *wl);
160 static void window_move(struct vo_wayland_state *wl, uint32_t serial);
161
162 /* Wayland listener boilerplate */
pointer_handle_enter(void * data,struct wl_pointer * pointer,uint32_t serial,struct wl_surface * surface,wl_fixed_t sx,wl_fixed_t sy)163 static void pointer_handle_enter(void *data, struct wl_pointer *pointer,
164 uint32_t serial, struct wl_surface *surface,
165 wl_fixed_t sx, wl_fixed_t sy)
166 {
167 struct vo_wayland_state *wl = data;
168
169 wl->pointer = pointer;
170 wl->pointer_id = serial;
171
172 set_cursor_visibility(wl, wl->cursor_visible);
173 mp_input_put_key(wl->vo->input_ctx, MP_KEY_MOUSE_ENTER);
174 }
175
pointer_handle_leave(void * data,struct wl_pointer * pointer,uint32_t serial,struct wl_surface * surface)176 static void pointer_handle_leave(void *data, struct wl_pointer *pointer,
177 uint32_t serial, struct wl_surface *surface)
178 {
179 struct vo_wayland_state *wl = data;
180 mp_input_put_key(wl->vo->input_ctx, MP_KEY_MOUSE_LEAVE);
181 }
182
pointer_handle_motion(void * data,struct wl_pointer * pointer,uint32_t time,wl_fixed_t sx,wl_fixed_t sy)183 static void pointer_handle_motion(void *data, struct wl_pointer *pointer,
184 uint32_t time, wl_fixed_t sx, wl_fixed_t sy)
185 {
186 struct vo_wayland_state *wl = data;
187
188 wl->mouse_x = wl_fixed_to_int(sx) * wl->scaling;
189 wl->mouse_y = wl_fixed_to_int(sy) * wl->scaling;
190 wl->mouse_unscaled_x = sx;
191 wl->mouse_unscaled_y = sy;
192
193 if (!wl->toplevel_configured)
194 mp_input_set_mouse_pos(wl->vo->input_ctx, wl->mouse_x, wl->mouse_y);
195 wl->toplevel_configured = false;
196 }
197
pointer_handle_button(void * data,struct wl_pointer * wl_pointer,uint32_t serial,uint32_t time,uint32_t button,uint32_t state)198 static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
199 uint32_t serial, uint32_t time, uint32_t button,
200 uint32_t state)
201 {
202 struct vo_wayland_state *wl = data;
203 int mpmod = 0;
204
205 state = state == WL_POINTER_BUTTON_STATE_PRESSED ? MP_KEY_STATE_DOWN
206 : MP_KEY_STATE_UP;
207
208 switch (button) {
209 case BTN_LEFT:
210 button = MP_MBTN_LEFT;
211 break;
212 case BTN_MIDDLE:
213 button = MP_MBTN_MID;
214 break;
215 case BTN_RIGHT:
216 button = MP_MBTN_RIGHT;
217 break;
218 case BTN_SIDE:
219 button = MP_MBTN_BACK;
220 break;
221 case BTN_EXTRA:
222 button = MP_MBTN_FORWARD;
223 break;
224 default:
225 button = 0;
226 break;
227 }
228
229 if (wl->keyboard)
230 mpmod = get_mods(wl);
231
232 if (button)
233 mp_input_put_key(wl->vo->input_ctx, button | state | mpmod);
234
235 if (!mp_input_test_dragging(wl->vo->input_ctx, wl->mouse_x, wl->mouse_y) &&
236 (!wl->vo_opts->fullscreen) && (!wl->vo_opts->window_maximized) &&
237 (button == MP_MBTN_LEFT) && (state == MP_KEY_STATE_DOWN)) {
238 uint32_t edges;
239 // Implement an edge resize zone if there are no decorations
240 if (!wl->xdg_toplevel_decoration &&
241 check_for_resize(wl, wl->mouse_unscaled_x, wl->mouse_unscaled_y,
242 wl->opts->edge_pixels_pointer, &edges))
243 xdg_toplevel_resize(wl->xdg_toplevel, wl->seat, serial, edges);
244 else
245 window_move(wl, serial);
246 // Explictly send an UP event after the client finishes a move/resize
247 mp_input_put_key(wl->vo->input_ctx, button | MP_KEY_STATE_UP);
248 }
249 }
250
pointer_handle_axis(void * data,struct wl_pointer * wl_pointer,uint32_t time,uint32_t axis,wl_fixed_t value)251 static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
252 uint32_t time, uint32_t axis, wl_fixed_t value)
253 {
254 struct vo_wayland_state *wl = data;
255
256 double val = wl_fixed_to_double(value) < 0 ? -1 : 1;
257 switch (axis) {
258 case WL_POINTER_AXIS_VERTICAL_SCROLL:
259 if (value > 0)
260 mp_input_put_wheel(wl->vo->input_ctx, MP_WHEEL_DOWN, +val);
261 if (value < 0)
262 mp_input_put_wheel(wl->vo->input_ctx, MP_WHEEL_UP, -val);
263 break;
264 case WL_POINTER_AXIS_HORIZONTAL_SCROLL:
265 if (value > 0)
266 mp_input_put_wheel(wl->vo->input_ctx, MP_WHEEL_RIGHT, +val);
267 if (value < 0)
268 mp_input_put_wheel(wl->vo->input_ctx, MP_WHEEL_LEFT, -val);
269 break;
270 }
271 }
272
273 static const struct wl_pointer_listener pointer_listener = {
274 pointer_handle_enter,
275 pointer_handle_leave,
276 pointer_handle_motion,
277 pointer_handle_button,
278 pointer_handle_axis,
279 };
280
touch_handle_down(void * data,struct wl_touch * wl_touch,uint32_t serial,uint32_t time,struct wl_surface * surface,int32_t id,wl_fixed_t x_w,wl_fixed_t y_w)281 static void touch_handle_down(void *data, struct wl_touch *wl_touch,
282 uint32_t serial, uint32_t time, struct wl_surface *surface,
283 int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
284 {
285 struct vo_wayland_state *wl = data;
286
287 wl->mouse_x = wl_fixed_to_int(x_w) * wl->scaling;
288 wl->mouse_y = wl_fixed_to_int(y_w) * wl->scaling;
289
290 mp_input_set_mouse_pos(wl->vo->input_ctx, wl->mouse_x, wl->mouse_y);
291 mp_input_put_key(wl->vo->input_ctx, MP_MBTN_LEFT | MP_KEY_STATE_DOWN);
292
293 enum xdg_toplevel_resize_edge edge;
294 if (check_for_resize(wl, x_w, y_w, wl->opts->edge_pixels_touch, &edge)) {
295 xdg_toplevel_resize(wl->xdg_toplevel, wl->seat, serial, edge);
296 } else {
297 xdg_toplevel_move(wl->xdg_toplevel, wl->seat, serial);
298 }
299 }
300
touch_handle_up(void * data,struct wl_touch * wl_touch,uint32_t serial,uint32_t time,int32_t id)301 static void touch_handle_up(void *data, struct wl_touch *wl_touch,
302 uint32_t serial, uint32_t time, int32_t id)
303 {
304 struct vo_wayland_state *wl = data;
305 mp_input_put_key(wl->vo->input_ctx, MP_MBTN_LEFT | MP_KEY_STATE_UP);
306 }
307
touch_handle_motion(void * data,struct wl_touch * wl_touch,uint32_t time,int32_t id,wl_fixed_t x_w,wl_fixed_t y_w)308 static void touch_handle_motion(void *data, struct wl_touch *wl_touch,
309 uint32_t time, int32_t id, wl_fixed_t x_w, wl_fixed_t y_w)
310 {
311 struct vo_wayland_state *wl = data;
312
313 wl->mouse_x = wl_fixed_to_int(x_w) * wl->scaling;
314 wl->mouse_y = wl_fixed_to_int(y_w) * wl->scaling;
315
316 mp_input_set_mouse_pos(wl->vo->input_ctx, wl->mouse_x, wl->mouse_y);
317 }
318
touch_handle_frame(void * data,struct wl_touch * wl_touch)319 static void touch_handle_frame(void *data, struct wl_touch *wl_touch)
320 {
321 }
322
touch_handle_cancel(void * data,struct wl_touch * wl_touch)323 static void touch_handle_cancel(void *data, struct wl_touch *wl_touch)
324 {
325 }
326
327 static const struct wl_touch_listener touch_listener = {
328 touch_handle_down,
329 touch_handle_up,
330 touch_handle_motion,
331 touch_handle_frame,
332 touch_handle_cancel,
333 };
334
keyboard_handle_keymap(void * data,struct wl_keyboard * wl_keyboard,uint32_t format,int32_t fd,uint32_t size)335 static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard,
336 uint32_t format, int32_t fd, uint32_t size)
337 {
338 struct vo_wayland_state *wl = data;
339 char *map_str;
340
341 if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
342 close(fd);
343 return;
344 }
345
346 map_str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
347 if (map_str == MAP_FAILED) {
348 close(fd);
349 return;
350 }
351
352 wl->xkb_keymap = xkb_keymap_new_from_string(wl->xkb_context, map_str,
353 XKB_KEYMAP_FORMAT_TEXT_V1, 0);
354
355 munmap(map_str, size);
356 close(fd);
357
358 if (!wl->xkb_keymap) {
359 MP_ERR(wl, "failed to compile keymap\n");
360 return;
361 }
362
363 wl->xkb_state = xkb_state_new(wl->xkb_keymap);
364 if (!wl->xkb_state) {
365 MP_ERR(wl, "failed to create XKB state\n");
366 xkb_keymap_unref(wl->xkb_keymap);
367 wl->xkb_keymap = NULL;
368 return;
369 }
370 }
371
keyboard_handle_enter(void * data,struct wl_keyboard * wl_keyboard,uint32_t serial,struct wl_surface * surface,struct wl_array * keys)372 static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard,
373 uint32_t serial, struct wl_surface *surface,
374 struct wl_array *keys)
375 {
376 struct vo_wayland_state *wl = data;
377 wl->has_keyboard_input = true;
378 }
379
keyboard_handle_leave(void * data,struct wl_keyboard * wl_keyboard,uint32_t serial,struct wl_surface * surface)380 static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard,
381 uint32_t serial, struct wl_surface *surface)
382 {
383 struct vo_wayland_state *wl = data;
384 wl->has_keyboard_input = false;
385 }
386
keyboard_handle_key(void * data,struct wl_keyboard * wl_keyboard,uint32_t serial,uint32_t time,uint32_t key,uint32_t state)387 static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard,
388 uint32_t serial, uint32_t time, uint32_t key,
389 uint32_t state)
390 {
391 struct vo_wayland_state *wl = data;
392
393 wl->keyboard_code = key + 8;
394 xkb_keysym_t sym = xkb_state_key_get_one_sym(wl->xkb_state, wl->keyboard_code);
395
396 state = state == WL_KEYBOARD_KEY_STATE_PRESSED ? MP_KEY_STATE_DOWN
397 : MP_KEY_STATE_UP;
398 int mpmod = get_mods(wl);
399 int mpkey = lookupkey(sym);
400 if (mpkey) {
401 mp_input_put_key(wl->vo->input_ctx, mpkey | state | mpmod);
402 } else {
403 char s[128];
404 if (xkb_keysym_to_utf8(sym, s, sizeof(s)) > 0)
405 mp_input_put_key_utf8(wl->vo->input_ctx, state | mpmod, bstr0(s));
406 }
407 }
408
keyboard_handle_modifiers(void * data,struct wl_keyboard * wl_keyboard,uint32_t serial,uint32_t mods_depressed,uint32_t mods_latched,uint32_t mods_locked,uint32_t group)409 static void keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboard,
410 uint32_t serial, uint32_t mods_depressed,
411 uint32_t mods_latched, uint32_t mods_locked,
412 uint32_t group)
413 {
414 struct vo_wayland_state *wl = data;
415
416 if (wl->xkb_state) {
417 xkb_state_update_mask(wl->xkb_state, mods_depressed, mods_latched,
418 mods_locked, 0, 0, group);
419 }
420 }
421
keyboard_handle_repeat_info(void * data,struct wl_keyboard * wl_keyboard,int32_t rate,int32_t delay)422 static void keyboard_handle_repeat_info(void *data, struct wl_keyboard *wl_keyboard,
423 int32_t rate, int32_t delay)
424 {
425 struct vo_wayland_state *wl = data;
426 if (wl->vo_opts->native_keyrepeat)
427 mp_input_set_repeat_info(wl->vo->input_ctx, rate, delay);
428 }
429
430 static const struct wl_keyboard_listener keyboard_listener = {
431 keyboard_handle_keymap,
432 keyboard_handle_enter,
433 keyboard_handle_leave,
434 keyboard_handle_key,
435 keyboard_handle_modifiers,
436 keyboard_handle_repeat_info,
437 };
438
seat_handle_caps(void * data,struct wl_seat * seat,enum wl_seat_capability caps)439 static void seat_handle_caps(void *data, struct wl_seat *seat,
440 enum wl_seat_capability caps)
441 {
442 struct vo_wayland_state *wl = data;
443
444 if ((caps & WL_SEAT_CAPABILITY_POINTER) && !wl->pointer) {
445 wl->pointer = wl_seat_get_pointer(seat);
446 wl_pointer_add_listener(wl->pointer, &pointer_listener, wl);
447 } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && wl->pointer) {
448 wl_pointer_destroy(wl->pointer);
449 wl->pointer = NULL;
450 }
451
452 if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !wl->keyboard) {
453 wl->keyboard = wl_seat_get_keyboard(seat);
454 wl_keyboard_add_listener(wl->keyboard, &keyboard_listener, wl);
455 } else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && wl->keyboard) {
456 wl_keyboard_destroy(wl->keyboard);
457 wl->keyboard = NULL;
458 }
459
460 if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !wl->touch) {
461 wl->touch = wl_seat_get_touch(seat);
462 wl_touch_set_user_data(wl->touch, wl);
463 wl_touch_add_listener(wl->touch, &touch_listener, wl);
464 } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && wl->touch) {
465 wl_touch_destroy(wl->touch);
466 wl->touch = NULL;
467 }
468 }
469
470 static const struct wl_seat_listener seat_listener = {
471 seat_handle_caps,
472 };
473
data_offer_handle_offer(void * data,struct wl_data_offer * offer,const char * mime_type)474 static void data_offer_handle_offer(void *data, struct wl_data_offer *offer,
475 const char *mime_type)
476 {
477 struct vo_wayland_state *wl = data;
478 int score = mp_event_get_mime_type_score(wl->vo->input_ctx, mime_type);
479 if (score > wl->dnd_mime_score) {
480 wl->dnd_mime_score = score;
481 talloc_free(wl->dnd_mime_type);
482 wl->dnd_mime_type = talloc_strdup(wl, mime_type);
483 MP_VERBOSE(wl, "Given DND offer with mime type %s\n", wl->dnd_mime_type);
484 }
485 }
486
data_offer_source_actions(void * data,struct wl_data_offer * offer,uint32_t source_actions)487 static void data_offer_source_actions(void *data, struct wl_data_offer *offer, uint32_t source_actions)
488 {
489 }
490
data_offer_action(void * data,struct wl_data_offer * wl_data_offer,uint32_t dnd_action)491 static void data_offer_action(void *data, struct wl_data_offer *wl_data_offer, uint32_t dnd_action)
492 {
493 struct vo_wayland_state *wl = data;
494 wl->dnd_action = dnd_action & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY ?
495 DND_REPLACE : DND_APPEND;
496 MP_VERBOSE(wl, "DND action is %s\n",
497 wl->dnd_action == DND_REPLACE ? "DND_REPLACE" : "DND_APPEND");
498 }
499
500 static const struct wl_data_offer_listener data_offer_listener = {
501 data_offer_handle_offer,
502 data_offer_source_actions,
503 data_offer_action,
504 };
505
data_device_handle_data_offer(void * data,struct wl_data_device * wl_ddev,struct wl_data_offer * id)506 static void data_device_handle_data_offer(void *data, struct wl_data_device *wl_ddev,
507 struct wl_data_offer *id)
508 {
509 struct vo_wayland_state *wl = data;
510 if (wl->dnd_offer)
511 wl_data_offer_destroy(wl->dnd_offer);
512
513 wl->dnd_offer = id;
514 wl_data_offer_add_listener(id, &data_offer_listener, wl);
515 }
516
data_device_handle_enter(void * data,struct wl_data_device * wl_ddev,uint32_t serial,struct wl_surface * surface,wl_fixed_t x,wl_fixed_t y,struct wl_data_offer * id)517 static void data_device_handle_enter(void *data, struct wl_data_device *wl_ddev,
518 uint32_t serial, struct wl_surface *surface,
519 wl_fixed_t x, wl_fixed_t y,
520 struct wl_data_offer *id)
521 {
522 struct vo_wayland_state *wl = data;
523 if (wl->dnd_offer != id) {
524 MP_FATAL(wl, "DND offer ID mismatch!\n");
525 return;
526 }
527
528 wl_data_offer_set_actions(id, WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY |
529 WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE,
530 WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY);
531
532 wl_data_offer_accept(id, serial, wl->dnd_mime_type);
533
534 MP_VERBOSE(wl, "Accepting DND offer with mime type %s\n", wl->dnd_mime_type);
535 }
536
data_device_handle_leave(void * data,struct wl_data_device * wl_ddev)537 static void data_device_handle_leave(void *data, struct wl_data_device *wl_ddev)
538 {
539 struct vo_wayland_state *wl = data;
540
541 if (wl->dnd_offer) {
542 if (wl->dnd_fd != -1)
543 return;
544 wl_data_offer_destroy(wl->dnd_offer);
545 wl->dnd_offer = NULL;
546 }
547
548 MP_VERBOSE(wl, "Releasing DND offer with mime type %s\n", wl->dnd_mime_type);
549
550 talloc_free(wl->dnd_mime_type);
551 wl->dnd_mime_type = NULL;
552 wl->dnd_mime_score = 0;
553 }
554
data_device_handle_motion(void * data,struct wl_data_device * wl_ddev,uint32_t time,wl_fixed_t x,wl_fixed_t y)555 static void data_device_handle_motion(void *data, struct wl_data_device *wl_ddev,
556 uint32_t time, wl_fixed_t x, wl_fixed_t y)
557 {
558 struct vo_wayland_state *wl = data;
559
560 wl_data_offer_accept(wl->dnd_offer, time, wl->dnd_mime_type);
561 }
562
data_device_handle_drop(void * data,struct wl_data_device * wl_ddev)563 static void data_device_handle_drop(void *data, struct wl_data_device *wl_ddev)
564 {
565 struct vo_wayland_state *wl = data;
566
567 int pipefd[2];
568
569 if (pipe2(pipefd, O_CLOEXEC) == -1) {
570 MP_ERR(wl, "Failed to create dnd pipe!\n");
571 return;
572 }
573
574 MP_VERBOSE(wl, "Receiving DND offer with mime %s\n", wl->dnd_mime_type);
575
576 wl_data_offer_receive(wl->dnd_offer, wl->dnd_mime_type, pipefd[1]);
577 close(pipefd[1]);
578
579 wl->dnd_fd = pipefd[0];
580 wl_data_offer_finish(wl->dnd_offer);
581 }
582
data_device_handle_selection(void * data,struct wl_data_device * wl_ddev,struct wl_data_offer * id)583 static void data_device_handle_selection(void *data, struct wl_data_device *wl_ddev,
584 struct wl_data_offer *id)
585 {
586 }
587
588 static const struct wl_data_device_listener data_device_listener = {
589 data_device_handle_data_offer,
590 data_device_handle_enter,
591 data_device_handle_leave,
592 data_device_handle_motion,
593 data_device_handle_drop,
594 data_device_handle_selection,
595 };
596
output_handle_geometry(void * data,struct wl_output * wl_output,int32_t x,int32_t y,int32_t phys_width,int32_t phys_height,int32_t subpixel,const char * make,const char * model,int32_t transform)597 static void output_handle_geometry(void *data, struct wl_output *wl_output,
598 int32_t x, int32_t y, int32_t phys_width,
599 int32_t phys_height, int32_t subpixel,
600 const char *make, const char *model,
601 int32_t transform)
602 {
603 struct vo_wayland_output *output = data;
604 output->make = talloc_strdup(output->wl, make);
605 output->model = talloc_strdup(output->wl, model);
606 output->geometry.x0 = x;
607 output->geometry.y0 = y;
608 output->phys_width = phys_width;
609 output->phys_height = phys_height;
610 }
611
output_handle_mode(void * data,struct wl_output * wl_output,uint32_t flags,int32_t width,int32_t height,int32_t refresh)612 static void output_handle_mode(void *data, struct wl_output *wl_output,
613 uint32_t flags, int32_t width,
614 int32_t height, int32_t refresh)
615 {
616 struct vo_wayland_output *output = data;
617
618 /* Only save current mode */
619 if (!(flags & WL_OUTPUT_MODE_CURRENT))
620 return;
621
622 output->geometry.x1 = width;
623 output->geometry.y1 = height;
624 output->flags = flags;
625 output->refresh_rate = (double)refresh * 0.001;
626 }
627
output_handle_done(void * data,struct wl_output * wl_output)628 static void output_handle_done(void* data, struct wl_output *wl_output)
629 {
630 struct vo_wayland_output *o = data;
631 struct vo_wayland_state *wl = o->wl;
632
633 o->geometry.x1 += o->geometry.x0;
634 o->geometry.y1 += o->geometry.y0;
635
636 MP_VERBOSE(o->wl, "Registered output %s %s (0x%x):\n"
637 "\tx: %dpx, y: %dpx\n"
638 "\tw: %dpx (%dmm), h: %dpx (%dmm)\n"
639 "\tscale: %d\n"
640 "\tHz: %f\n", o->make, o->model, o->id, o->geometry.x0,
641 o->geometry.y0, mp_rect_w(o->geometry), o->phys_width,
642 mp_rect_h(o->geometry), o->phys_height, o->scale, o->refresh_rate);
643
644 /* If we satisfy this conditional, something about the current
645 * output must have changed (resolution, scale, etc). All window
646 * geometry and scaling should be recalculated. */
647 if (wl->current_output && wl->current_output->output == wl_output) {
648 set_surface_scaling(wl);
649 if (!wl->vo_opts->fullscreen && !wl->vo_opts->window_maximized) {
650 wl_surface_set_buffer_scale(wl->surface, wl->scaling);
651 } else {
652 wl->scale_change = true;
653 }
654 spawn_cursor(wl);
655 set_geometry(wl);
656 wl->pending_vo_events |= VO_EVENT_DPI;
657 wl->pending_vo_events |= VO_EVENT_RESIZE;
658 }
659
660 wl->pending_vo_events |= VO_EVENT_WIN_STATE;
661 }
662
output_handle_scale(void * data,struct wl_output * wl_output,int32_t factor)663 static void output_handle_scale(void* data, struct wl_output *wl_output,
664 int32_t factor)
665 {
666 struct vo_wayland_output *output = data;
667 if (!factor) {
668 MP_ERR(output->wl, "Invalid output scale given by the compositor!\n");
669 return;
670 }
671 output->scale = factor;
672 }
673
674 static const struct wl_output_listener output_listener = {
675 output_handle_geometry,
676 output_handle_mode,
677 output_handle_done,
678 output_handle_scale,
679 };
680
surface_handle_enter(void * data,struct wl_surface * wl_surface,struct wl_output * output)681 static void surface_handle_enter(void *data, struct wl_surface *wl_surface,
682 struct wl_output *output)
683 {
684 struct vo_wayland_state *wl = data;
685 if (!wl->current_output)
686 return;
687
688 struct mp_rect old_output_geometry = wl->current_output->geometry;
689 struct mp_rect old_geometry = wl->geometry;
690 wl->current_output = NULL;
691
692 struct vo_wayland_output *o;
693 wl_list_for_each(o, &wl->output_list, link) {
694 if (o->output == output) {
695 wl->current_output = o;
696 break;
697 }
698 }
699
700 wl->current_output->has_surface = true;
701 bool force_resize = false;
702
703 if (wl->scaling != wl->current_output->scale) {
704 set_surface_scaling(wl);
705 if (!wl->vo_opts->fullscreen && !wl->vo_opts->window_maximized) {
706 wl->scale_change = true;
707 } else {
708 wl_surface_set_buffer_scale(wl->surface, wl->scaling);
709 }
710 spawn_cursor(wl);
711 wl->pending_vo_events |= VO_EVENT_DPI;
712 }
713
714 if (!mp_rect_equals(&old_output_geometry, &wl->current_output->geometry)) {
715 set_geometry(wl);
716 force_resize = true;
717 }
718
719 if (!mp_rect_equals(&old_geometry, &wl->geometry) || force_resize)
720 wl->pending_vo_events |= VO_EVENT_RESIZE;
721
722 MP_VERBOSE(wl, "Surface entered output %s %s (0x%x), scale = %i\n", o->make,
723 o->model, o->id, wl->scaling);
724
725 wl->pending_vo_events |= VO_EVENT_WIN_STATE;
726 }
727
surface_handle_leave(void * data,struct wl_surface * wl_surface,struct wl_output * output)728 static void surface_handle_leave(void *data, struct wl_surface *wl_surface,
729 struct wl_output *output)
730 {
731 struct vo_wayland_state *wl = data;
732
733 struct vo_wayland_output *o;
734 wl_list_for_each(o, &wl->output_list, link) {
735 if (o->output == output) {
736 o->has_surface = false;
737 wl->pending_vo_events |= VO_EVENT_WIN_STATE;
738 return;
739 }
740 }
741 }
742
743 static const struct wl_surface_listener surface_listener = {
744 surface_handle_enter,
745 surface_handle_leave,
746 };
747
xdg_wm_base_ping(void * data,struct xdg_wm_base * wm_base,uint32_t serial)748 static void xdg_wm_base_ping(void *data, struct xdg_wm_base *wm_base, uint32_t serial)
749 {
750 xdg_wm_base_pong(wm_base, serial);
751 }
752
753 static const struct xdg_wm_base_listener xdg_wm_base_listener = {
754 xdg_wm_base_ping,
755 };
756
handle_surface_config(void * data,struct xdg_surface * surface,uint32_t serial)757 static void handle_surface_config(void *data, struct xdg_surface *surface,
758 uint32_t serial)
759 {
760 xdg_surface_ack_configure(surface, serial);
761 }
762
763 static const struct xdg_surface_listener xdg_surface_listener = {
764 handle_surface_config,
765 };
766
handle_toplevel_config(void * data,struct xdg_toplevel * toplevel,int32_t width,int32_t height,struct wl_array * states)767 static void handle_toplevel_config(void *data, struct xdg_toplevel *toplevel,
768 int32_t width, int32_t height, struct wl_array *states)
769 {
770 struct vo_wayland_state *wl = data;
771 struct mp_vo_opts *vo_opts = wl->vo_opts;
772 struct mp_rect old_geometry = wl->geometry;
773
774 int old_toplevel_width = wl->toplevel_width;
775 int old_toplevel_height = wl->toplevel_height;
776 wl->toplevel_width = width;
777 wl->toplevel_height = height;
778
779 /* Don't do anything here if we haven't finished setting geometry. */
780 if (mp_rect_w(wl->geometry) == 0 || mp_rect_h(wl->geometry) == 0)
781 return;
782
783 bool is_maximized = false;
784 bool is_fullscreen = false;
785 bool is_activated = false;
786 enum xdg_toplevel_state *state;
787 wl_array_for_each(state, states) {
788 switch (*state) {
789 case XDG_TOPLEVEL_STATE_FULLSCREEN:
790 is_fullscreen = true;
791 break;
792 case XDG_TOPLEVEL_STATE_RESIZING:
793 break;
794 case XDG_TOPLEVEL_STATE_ACTIVATED:
795 is_activated = true;
796 /*
797 * If we get an ACTIVATED state, we know it cannot be
798 * minimized, but it may not have been minimized
799 * previously, so we can't detect the exact state.
800 */
801 vo_opts->window_minimized = false;
802 m_config_cache_write_opt(wl->vo_opts_cache,
803 &vo_opts->window_minimized);
804 break;
805 case XDG_TOPLEVEL_STATE_TILED_TOP:
806 case XDG_TOPLEVEL_STATE_TILED_LEFT:
807 case XDG_TOPLEVEL_STATE_TILED_RIGHT:
808 case XDG_TOPLEVEL_STATE_TILED_BOTTOM:
809 case XDG_TOPLEVEL_STATE_MAXIMIZED:
810 is_maximized = true;
811 break;
812 }
813 }
814
815 if (vo_opts->fullscreen != is_fullscreen) {
816 wl->state_change = true;
817 vo_opts->fullscreen = is_fullscreen;
818 m_config_cache_write_opt(wl->vo_opts_cache, &vo_opts->fullscreen);
819 }
820
821 if (vo_opts->window_maximized != is_maximized) {
822 wl->state_change = true;
823 vo_opts->window_maximized = is_maximized;
824 m_config_cache_write_opt(wl->vo_opts_cache, &vo_opts->window_maximized);
825 }
826
827 if (wl->requested_decoration)
828 request_decoration_mode(wl, wl->requested_decoration);
829
830 if (wl->activated != is_activated) {
831 wl->activated = is_activated;
832 if ((!wl->focused && wl->activated && wl->has_keyboard_input) ||
833 (wl->focused && !wl->activated))
834 {
835 wl->focused = !wl->focused;
836 wl->pending_vo_events |= VO_EVENT_FOCUS;
837 }
838 /* Just force a redraw to be on the safe side. */
839 if (wl->activated) {
840 wl->hidden = false;
841 wl->pending_vo_events |= VO_EVENT_EXPOSE;
842 }
843 }
844
845 if (wl->scale_change) {
846 wl_surface_set_buffer_scale(wl->surface, wl->scaling);
847 wl->scale_change = false;
848 }
849
850 if (wl->state_change) {
851 if (!is_fullscreen && !is_maximized) {
852 wl->geometry = wl->window_size;
853 wl->state_change = false;
854 goto resize;
855 }
856 }
857
858 if (old_toplevel_width == wl->toplevel_width &&
859 old_toplevel_height == wl->toplevel_height)
860 return;
861
862 if (!is_fullscreen && !is_maximized) {
863 if (vo_opts->keepaspect) {
864 double scale_factor = (double)width / wl->reduced_width;
865 width = ceil(wl->reduced_width * scale_factor);
866 if (vo_opts->keepaspect_window)
867 height = ceil(wl->reduced_height * scale_factor);
868 }
869 wl->window_size.x0 = 0;
870 wl->window_size.y0 = 0;
871 wl->window_size.x1 = width;
872 wl->window_size.y1 = height;
873 }
874 wl->geometry.x0 = 0;
875 wl->geometry.y0 = 0;
876 wl->geometry.x1 = width;
877 wl->geometry.y1 = height;
878
879 if (mp_rect_equals(&old_geometry, &wl->geometry))
880 return;
881
882 resize:
883 MP_VERBOSE(wl, "Resizing due to xdg from %ix%i to %ix%i\n",
884 mp_rect_w(old_geometry)*wl->scaling, mp_rect_h(old_geometry)*wl->scaling,
885 mp_rect_w(wl->geometry)*wl->scaling, mp_rect_h(wl->geometry)*wl->scaling);
886
887 wl->pending_vo_events |= VO_EVENT_RESIZE;
888 wl->toplevel_configured = true;
889 }
890
handle_toplevel_close(void * data,struct xdg_toplevel * xdg_toplevel)891 static void handle_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel)
892 {
893 struct vo_wayland_state *wl = data;
894 mp_input_put_key(wl->vo->input_ctx, MP_KEY_CLOSE_WIN);
895 }
896
897 static const struct xdg_toplevel_listener xdg_toplevel_listener = {
898 handle_toplevel_config,
899 handle_toplevel_close,
900 };
901
configure_decorations(void * data,struct zxdg_toplevel_decoration_v1 * xdg_toplevel_decoration,uint32_t mode)902 static void configure_decorations(void *data,
903 struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration,
904 uint32_t mode)
905 {
906 struct vo_wayland_state *wl = data;
907 struct mp_vo_opts *opts = wl->vo_opts;
908
909 if (wl->requested_decoration == mode)
910 wl->requested_decoration = 0;
911
912 if (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE) {
913 MP_VERBOSE(wl, "Enabling server decorations\n");
914 } else {
915 MP_VERBOSE(wl, "Disabling server decorations\n");
916 }
917 opts->border = mode - 1;
918 m_config_cache_write_opt(wl->vo_opts_cache, &opts->border);
919 }
920
921 static const struct zxdg_toplevel_decoration_v1_listener decoration_listener = {
922 configure_decorations,
923 };
924
pres_set_clockid(void * data,struct wp_presentation * pres,uint32_t clockid)925 static void pres_set_clockid(void *data, struct wp_presentation *pres,
926 uint32_t clockid)
927 {
928 struct vo_wayland_state *wl = data;
929
930 if (clockid == CLOCK_MONOTONIC)
931 wl->presentation = pres;
932 }
933
934 static const struct wp_presentation_listener pres_listener = {
935 pres_set_clockid,
936 };
937
feedback_sync_output(void * data,struct wp_presentation_feedback * fback,struct wl_output * output)938 static void feedback_sync_output(void *data, struct wp_presentation_feedback *fback,
939 struct wl_output *output)
940 {
941 }
942
feedback_presented(void * data,struct wp_presentation_feedback * fback,uint32_t tv_sec_hi,uint32_t tv_sec_lo,uint32_t tv_nsec,uint32_t refresh_nsec,uint32_t seq_hi,uint32_t seq_lo,uint32_t flags)943 static void feedback_presented(void *data, struct wp_presentation_feedback *fback,
944 uint32_t tv_sec_hi, uint32_t tv_sec_lo,
945 uint32_t tv_nsec, uint32_t refresh_nsec,
946 uint32_t seq_hi, uint32_t seq_lo,
947 uint32_t flags)
948 {
949 struct vo_wayland_state *wl = data;
950 sync_shift(wl);
951
952 if (fback)
953 wp_presentation_feedback_destroy(fback);
954
955 wl->refresh_interval = (int64_t)refresh_nsec / 1000;
956
957 // Very similar to oml_sync_control, in this case we assume that every
958 // time the compositor receives feedback, a buffer swap has been already
959 // been performed.
960 //
961 // Notes:
962 // - tv_sec_lo + tv_sec_hi is the equivalent of oml's ust
963 // - seq_lo + seq_hi is the equivalent of oml's msc
964 // - these values are updated everytime the compositor receives feedback.
965
966 int index = last_available_sync(wl);
967 if (index < 0) {
968 queue_new_sync(wl);
969 index = 0;
970 }
971 int64_t sec = (uint64_t) tv_sec_lo + ((uint64_t) tv_sec_hi << 32);
972 wl->sync[index].ust = sec * 1000000LL + (uint64_t) tv_nsec / 1000;
973 wl->sync[index].msc = (uint64_t) seq_lo + ((uint64_t) seq_hi << 32);
974 wl->sync[index].filled = true;
975 }
976
feedback_discarded(void * data,struct wp_presentation_feedback * fback)977 static void feedback_discarded(void *data, struct wp_presentation_feedback *fback)
978 {
979 }
980
981 static const struct wp_presentation_feedback_listener feedback_listener = {
982 feedback_sync_output,
983 feedback_presented,
984 feedback_discarded,
985 };
986
987 static const struct wl_callback_listener frame_listener;
988
frame_callback(void * data,struct wl_callback * callback,uint32_t time)989 static void frame_callback(void *data, struct wl_callback *callback, uint32_t time)
990 {
991 struct vo_wayland_state *wl = data;
992
993 if (callback)
994 wl_callback_destroy(callback);
995
996 wl->frame_callback = wl_surface_frame(wl->surface);
997 wl_callback_add_listener(wl->frame_callback, &frame_listener, wl);
998
999 if (wl->presentation) {
1000 wl->feedback = wp_presentation_feedback(wl->presentation, wl->surface);
1001 wp_presentation_feedback_add_listener(wl->feedback, &feedback_listener, wl);
1002 }
1003
1004 wl->frame_wait = false;
1005 wl->hidden = false;
1006 }
1007
1008 static const struct wl_callback_listener frame_listener = {
1009 frame_callback,
1010 };
1011
registry_handle_add(void * data,struct wl_registry * reg,uint32_t id,const char * interface,uint32_t ver)1012 static void registry_handle_add(void *data, struct wl_registry *reg, uint32_t id,
1013 const char *interface, uint32_t ver)
1014 {
1015 int found = 1;
1016 struct vo_wayland_state *wl = data;
1017
1018 if (!strcmp(interface, wl_compositor_interface.name) && (ver >= 3) && found++) {
1019 wl->compositor = wl_registry_bind(reg, id, &wl_compositor_interface, 3);
1020 wl->surface = wl_compositor_create_surface(wl->compositor);
1021 wl->cursor_surface = wl_compositor_create_surface(wl->compositor);
1022 wl_surface_add_listener(wl->surface, &surface_listener, wl);
1023 }
1024
1025 if (!strcmp(interface, wl_data_device_manager_interface.name) && (ver >= 3) && found++) {
1026 wl->dnd_devman = wl_registry_bind(reg, id, &wl_data_device_manager_interface, 3);
1027 }
1028
1029 if (!strcmp(interface, wl_output_interface.name) && (ver >= 2) && found++) {
1030 struct vo_wayland_output *output = talloc_zero(wl, struct vo_wayland_output);
1031
1032 output->wl = wl;
1033 output->id = id;
1034 output->scale = 1;
1035 output->output = wl_registry_bind(reg, id, &wl_output_interface, 2);
1036
1037 wl_output_add_listener(output->output, &output_listener, output);
1038 wl_list_insert(&wl->output_list, &output->link);
1039 }
1040
1041 if (!strcmp(interface, wl_seat_interface.name) && found++) {
1042 wl->seat = wl_registry_bind(reg, id, &wl_seat_interface, 1);
1043 wl_seat_add_listener(wl->seat, &seat_listener, wl);
1044 }
1045
1046 if (!strcmp(interface, wl_shm_interface.name) && found++) {
1047 wl->shm = wl_registry_bind(reg, id, &wl_shm_interface, 1);
1048 }
1049
1050 if (!strcmp(interface, wp_presentation_interface.name) && found++) {
1051 wl->presentation = wl_registry_bind(reg, id, &wp_presentation_interface, 1);
1052 wp_presentation_add_listener(wl->presentation, &pres_listener, wl);
1053 }
1054
1055 if (!strcmp(interface, xdg_wm_base_interface.name) && found++) {
1056 ver = MPMIN(ver, 2); /* We can use either 1 or 2 */
1057 wl->wm_base = wl_registry_bind(reg, id, &xdg_wm_base_interface, ver);
1058 xdg_wm_base_add_listener(wl->wm_base, &xdg_wm_base_listener, wl);
1059 }
1060
1061 if (!strcmp(interface, zxdg_decoration_manager_v1_interface.name) && found++) {
1062 wl->xdg_decoration_manager = wl_registry_bind(reg, id, &zxdg_decoration_manager_v1_interface, 1);
1063 }
1064
1065 if (!strcmp(interface, zwp_idle_inhibit_manager_v1_interface.name) && found++) {
1066 wl->idle_inhibit_manager = wl_registry_bind(reg, id, &zwp_idle_inhibit_manager_v1_interface, 1);
1067 }
1068
1069 if (found > 1)
1070 MP_VERBOSE(wl, "Registered for protocol %s\n", interface);
1071 }
1072
registry_handle_remove(void * data,struct wl_registry * reg,uint32_t id)1073 static void registry_handle_remove(void *data, struct wl_registry *reg, uint32_t id)
1074 {
1075 struct vo_wayland_state *wl = data;
1076 struct vo_wayland_output *output, *tmp;
1077 wl_list_for_each_safe(output, tmp, &wl->output_list, link) {
1078 if (output->id == id) {
1079 remove_output(output);
1080 return;
1081 }
1082 }
1083 }
1084
1085 static const struct wl_registry_listener registry_listener = {
1086 registry_handle_add,
1087 registry_handle_remove,
1088 };
1089
1090 /* Static functions */
check_dnd_fd(struct vo_wayland_state * wl)1091 static void check_dnd_fd(struct vo_wayland_state *wl)
1092 {
1093 if (wl->dnd_fd == -1)
1094 return;
1095
1096 struct pollfd fdp = { wl->dnd_fd, POLLIN | POLLERR | POLLHUP, 0 };
1097 if (poll(&fdp, 1, 0) <= 0)
1098 return;
1099
1100 if (fdp.revents & POLLIN) {
1101 ptrdiff_t offset = 0;
1102 size_t data_read = 0;
1103 const size_t chunk_size = 1;
1104 uint8_t *buffer = ta_zalloc_size(wl, chunk_size);
1105 if (!buffer)
1106 goto end;
1107
1108 while ((data_read = read(wl->dnd_fd, buffer + offset, chunk_size)) > 0) {
1109 offset += data_read;
1110 buffer = ta_realloc_size(wl, buffer, offset + chunk_size);
1111 memset(buffer + offset, 0, chunk_size);
1112 if (!buffer)
1113 goto end;
1114 }
1115
1116 MP_VERBOSE(wl, "Read %td bytes from the DND fd\n", offset);
1117
1118 struct bstr file_list = bstr0(buffer);
1119 mp_event_drop_mime_data(wl->vo->input_ctx, wl->dnd_mime_type,
1120 file_list, wl->dnd_action);
1121 talloc_free(buffer);
1122 end:
1123 talloc_free(wl->dnd_mime_type);
1124 wl->dnd_mime_type = NULL;
1125 wl->dnd_mime_score = 0;
1126 }
1127
1128 if (fdp.revents & (POLLIN | POLLERR | POLLHUP)) {
1129 close(wl->dnd_fd);
1130 wl->dnd_fd = -1;
1131 }
1132 }
1133
check_for_resize(struct vo_wayland_state * wl,wl_fixed_t x_w,wl_fixed_t y_w,int edge_pixels,enum xdg_toplevel_resize_edge * edge)1134 static int check_for_resize(struct vo_wayland_state *wl, wl_fixed_t x_w, wl_fixed_t y_w,
1135 int edge_pixels, enum xdg_toplevel_resize_edge *edge)
1136 {
1137 if (wl->vo_opts->fullscreen || wl->vo_opts->window_maximized)
1138 return 0;
1139
1140 int pos[2] = { wl_fixed_to_double(x_w), wl_fixed_to_double(y_w) };
1141 int left_edge = pos[0] < edge_pixels;
1142 int top_edge = pos[1] < edge_pixels;
1143 int right_edge = pos[0] > (mp_rect_w(wl->geometry) - edge_pixels);
1144 int bottom_edge = pos[1] > (mp_rect_h(wl->geometry) - edge_pixels);
1145
1146 if (left_edge) {
1147 *edge = XDG_TOPLEVEL_RESIZE_EDGE_LEFT;
1148 if (top_edge)
1149 *edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT;
1150 else if (bottom_edge)
1151 *edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT;
1152 } else if (right_edge) {
1153 *edge = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT;
1154 if (top_edge)
1155 *edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT;
1156 else if (bottom_edge)
1157 *edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT;
1158 } else if (top_edge) {
1159 *edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP;
1160 } else if (bottom_edge) {
1161 *edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM;
1162 } else {
1163 *edge = 0;
1164 return 0;
1165 }
1166
1167 return 1;
1168 }
1169
create_input(struct vo_wayland_state * wl)1170 static bool create_input(struct vo_wayland_state *wl)
1171 {
1172 wl->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
1173
1174 if (!wl->xkb_context) {
1175 MP_ERR(wl, "failed to initialize input: check xkbcommon\n");
1176 return 1;
1177 }
1178
1179 return 0;
1180 }
1181
create_xdg_surface(struct vo_wayland_state * wl)1182 static int create_xdg_surface(struct vo_wayland_state *wl)
1183 {
1184 wl->xdg_surface = xdg_wm_base_get_xdg_surface(wl->wm_base, wl->surface);
1185 xdg_surface_add_listener(wl->xdg_surface, &xdg_surface_listener, wl);
1186
1187 wl->xdg_toplevel = xdg_surface_get_toplevel(wl->xdg_surface);
1188 xdg_toplevel_add_listener(wl->xdg_toplevel, &xdg_toplevel_listener, wl);
1189
1190 if (!wl->xdg_surface || !wl->xdg_toplevel)
1191 return 1;
1192 return 0;
1193 }
1194
do_minimize(struct vo_wayland_state * wl)1195 static void do_minimize(struct vo_wayland_state *wl)
1196 {
1197 if (!wl->xdg_toplevel)
1198 return;
1199 if (wl->vo_opts->window_minimized)
1200 xdg_toplevel_set_minimized(wl->xdg_toplevel);
1201 }
1202
get_displays_spanned(struct vo_wayland_state * wl)1203 static char **get_displays_spanned(struct vo_wayland_state *wl)
1204 {
1205 char **names = NULL;
1206 int displays_spanned = 0;
1207 struct vo_wayland_output *output;
1208 wl_list_for_each(output, &wl->output_list, link) {
1209 if (output->has_surface)
1210 MP_TARRAY_APPEND(NULL, names, displays_spanned,
1211 talloc_strdup(NULL, output->model));
1212 }
1213 MP_TARRAY_APPEND(NULL, names, displays_spanned, NULL);
1214 return names;
1215 }
1216
get_mods(struct vo_wayland_state * wl)1217 static int get_mods(struct vo_wayland_state *wl)
1218 {
1219 static char* const mod_names[] = {
1220 XKB_MOD_NAME_SHIFT,
1221 XKB_MOD_NAME_CTRL,
1222 XKB_MOD_NAME_ALT,
1223 XKB_MOD_NAME_LOGO,
1224 };
1225
1226 static const int mods[] = {
1227 MP_KEY_MODIFIER_SHIFT,
1228 MP_KEY_MODIFIER_CTRL,
1229 MP_KEY_MODIFIER_ALT,
1230 MP_KEY_MODIFIER_META,
1231 };
1232
1233 int modifiers = 0;
1234
1235 for (int n = 0; n < MP_ARRAY_SIZE(mods); n++) {
1236 xkb_mod_index_t index = xkb_keymap_mod_get_index(wl->xkb_keymap, mod_names[n]);
1237 if (!xkb_state_mod_index_is_consumed(wl->xkb_state, wl->keyboard_code, index)
1238 && xkb_state_mod_index_is_active(wl->xkb_state, index,
1239 XKB_STATE_MODS_DEPRESSED))
1240 modifiers |= mods[n];
1241 }
1242 return modifiers;
1243 }
1244
greatest_common_divisor(struct vo_wayland_state * wl,int a,int b)1245 static void greatest_common_divisor(struct vo_wayland_state *wl, int a, int b) {
1246 // euclidean algorithm
1247 int larger;
1248 int smaller;
1249 if (a > b) {
1250 larger = a;
1251 smaller = b;
1252 } else {
1253 larger = b;
1254 smaller = a;
1255 }
1256 int remainder = larger - smaller * floor(larger/smaller);
1257 if (remainder == 0) {
1258 wl->gcd = smaller;
1259 } else {
1260 greatest_common_divisor(wl, smaller, remainder);
1261 }
1262 }
1263
find_output(struct vo_wayland_state * wl)1264 static struct vo_wayland_output *find_output(struct vo_wayland_state *wl)
1265 {
1266 int index = 0;
1267 int screen_id = wl->vo_opts->fsscreen_id;
1268 char *screen_name = wl->vo_opts->fsscreen_name;
1269 struct vo_wayland_output *output = NULL;
1270 struct vo_wayland_output *fallback_output = NULL;
1271 wl_list_for_each(output, &wl->output_list, link) {
1272 if (index == 0)
1273 fallback_output = output;
1274 if (screen_id == -1 && !screen_name)
1275 return output;
1276 if (screen_id == -1 && screen_name && !strcmp(screen_name, output->model))
1277 return output;
1278 if (screen_id == index++)
1279 return output;
1280 }
1281 if (!fallback_output) {
1282 MP_ERR(wl, "No screens could be found!\n");
1283 return NULL;
1284 } else if (wl->vo_opts->fsscreen_id >= 0) {
1285 MP_WARN(wl, "Screen index %i not found/unavailable! Falling back to screen 0!\n", screen_id);
1286 } else if (wl->vo_opts->fsscreen_name) {
1287 MP_WARN(wl, "Screen name %s not found/unavailable! Falling back to screen 0!\n", screen_name);
1288 }
1289 return fallback_output;
1290 }
1291
last_available_sync(struct vo_wayland_state * wl)1292 static int last_available_sync(struct vo_wayland_state *wl)
1293 {
1294 for (int i = wl->sync_size - 1; i > -1; --i) {
1295 if (!wl->sync[i].filled)
1296 return i;
1297 }
1298 return -1;
1299 }
1300
lookupkey(int key)1301 static int lookupkey(int key)
1302 {
1303 const char *passthrough_keys = " -+*/<>`~!@#$%^&()_{}:;\"\',.?\\|=[]";
1304
1305 int mpkey = 0;
1306 if ((key >= 'a' && key <= 'z') || (key >= 'A' && key <= 'Z') ||
1307 (key >= '0' && key <= '9') ||
1308 (key > 0 && key < 256 && strchr(passthrough_keys, key)))
1309 mpkey = key;
1310
1311 if (!mpkey)
1312 mpkey = lookup_keymap_table(keymap, key);
1313
1314 return mpkey;
1315 }
1316
queue_new_sync(struct vo_wayland_state * wl)1317 static void queue_new_sync(struct vo_wayland_state *wl)
1318 {
1319 wl->sync_size += 1;
1320 wl->sync = talloc_realloc(wl, wl->sync, struct vo_wayland_sync, wl->sync_size);
1321 sync_shift(wl);
1322 }
1323
request_decoration_mode(struct vo_wayland_state * wl,uint32_t mode)1324 static void request_decoration_mode(struct vo_wayland_state *wl, uint32_t mode)
1325 {
1326 wl->requested_decoration = mode;
1327 zxdg_toplevel_decoration_v1_set_mode(wl->xdg_toplevel_decoration, mode);
1328 }
1329
remove_output(struct vo_wayland_output * out)1330 static void remove_output(struct vo_wayland_output *out)
1331 {
1332 if (!out)
1333 return;
1334
1335 MP_VERBOSE(out->wl, "Deregistering output %s %s (0x%x)\n", out->make,
1336 out->model, out->id);
1337 wl_list_remove(&out->link);
1338 talloc_free(out->make);
1339 talloc_free(out->model);
1340 talloc_free(out);
1341 return;
1342 }
1343
set_cursor_visibility(struct vo_wayland_state * wl,bool on)1344 static int set_cursor_visibility(struct vo_wayland_state *wl, bool on)
1345 {
1346 wl->cursor_visible = on;
1347 if (on) {
1348 if (spawn_cursor(wl))
1349 return VO_FALSE;
1350 struct wl_cursor_image *img = wl->default_cursor->images[0];
1351 struct wl_buffer *buffer = wl_cursor_image_get_buffer(img);
1352 if (!buffer)
1353 return VO_FALSE;
1354 wl_pointer_set_cursor(wl->pointer, wl->pointer_id, wl->cursor_surface,
1355 img->hotspot_x/wl->scaling, img->hotspot_y/wl->scaling);
1356 wl_surface_set_buffer_scale(wl->cursor_surface, wl->scaling);
1357 wl_surface_attach(wl->cursor_surface, buffer, 0, 0);
1358 wl_surface_damage(wl->cursor_surface, 0, 0, img->width, img->height);
1359 wl_surface_commit(wl->cursor_surface);
1360 } else {
1361 wl_pointer_set_cursor(wl->pointer, wl->pointer_id, NULL, 0, 0);
1362 }
1363 return VO_TRUE;
1364 }
1365
set_geometry(struct vo_wayland_state * wl)1366 static void set_geometry(struct vo_wayland_state *wl)
1367 {
1368 struct vo *vo = wl->vo;
1369 assert(wl->current_output);
1370
1371 struct vo_win_geometry geo;
1372 struct mp_rect screenrc = wl->current_output->geometry;
1373 vo_calc_window_geometry(vo, &screenrc, &geo);
1374 vo_apply_window_geometry(vo, &geo);
1375
1376 greatest_common_divisor(wl, vo->dwidth, vo->dheight);
1377 wl->reduced_width = vo->dwidth / wl->gcd;
1378 wl->reduced_height = vo->dheight / wl->gcd;
1379
1380 wl->vdparams.x0 = 0;
1381 wl->vdparams.y0 = 0;
1382 wl->vdparams.x1 = vo->dwidth / wl->scaling;
1383 wl->vdparams.y1 = vo->dheight / wl->scaling;
1384 }
1385
set_screensaver_inhibitor(struct vo_wayland_state * wl,int state)1386 static int set_screensaver_inhibitor(struct vo_wayland_state *wl, int state)
1387 {
1388 if (!wl->idle_inhibit_manager)
1389 return VO_NOTIMPL;
1390 if (state == (!!wl->idle_inhibitor))
1391 return VO_TRUE;
1392 if (state) {
1393 MP_VERBOSE(wl, "Enabling idle inhibitor\n");
1394 struct zwp_idle_inhibit_manager_v1 *mgr = wl->idle_inhibit_manager;
1395 wl->idle_inhibitor = zwp_idle_inhibit_manager_v1_create_inhibitor(mgr, wl->surface);
1396 } else {
1397 MP_VERBOSE(wl, "Disabling the idle inhibitor\n");
1398 zwp_idle_inhibitor_v1_destroy(wl->idle_inhibitor);
1399 wl->idle_inhibitor = NULL;
1400 }
1401 return VO_TRUE;
1402 }
1403
set_surface_scaling(struct vo_wayland_state * wl)1404 static void set_surface_scaling(struct vo_wayland_state *wl)
1405 {
1406 int old_scale = wl->scaling;
1407 if (wl->vo_opts->hidpi_window_scale) {
1408 wl->scaling = wl->current_output->scale;
1409 } else {
1410 wl->scaling = 1;
1411 }
1412
1413 double factor = (double)old_scale / wl->scaling;
1414 wl->vdparams.x1 *= factor;
1415 wl->vdparams.y1 *= factor;
1416 wl->window_size.x1 *= factor;
1417 wl->window_size.y1 *= factor;
1418 }
1419
spawn_cursor(struct vo_wayland_state * wl)1420 static int spawn_cursor(struct vo_wayland_state *wl)
1421 {
1422 /* Reuse if size is identical */
1423 if (!wl->pointer || wl->allocated_cursor_scale == wl->scaling)
1424 return 0;
1425 else if (wl->cursor_theme)
1426 wl_cursor_theme_destroy(wl->cursor_theme);
1427
1428 const char *xcursor_theme = getenv("XCURSOR_THEME");
1429 const char *size_str = getenv("XCURSOR_SIZE");
1430 int size = 24;
1431 if (size_str != NULL) {
1432 errno = 0;
1433 char *end;
1434 long size_long = strtol(size_str, &end, 10);
1435 if (!*end && !errno && size_long > 0 && size_long <= INT_MAX)
1436 size = (int)size_long;
1437 }
1438
1439 wl->cursor_theme = wl_cursor_theme_load(xcursor_theme, size*wl->scaling, wl->shm);
1440 if (!wl->cursor_theme) {
1441 MP_ERR(wl, "Unable to load cursor theme!\n");
1442 return 1;
1443 }
1444
1445 wl->default_cursor = wl_cursor_theme_get_cursor(wl->cursor_theme, "left_ptr");
1446 if (!wl->default_cursor) {
1447 MP_ERR(wl, "Unable to load cursor theme!\n");
1448 return 1;
1449 }
1450
1451 wl->allocated_cursor_scale = wl->scaling;
1452
1453 return 0;
1454 }
1455
sync_shift(struct vo_wayland_state * wl)1456 static void sync_shift(struct vo_wayland_state *wl)
1457 {
1458 for (int i = wl->sync_size - 1; i > 0; --i) {
1459 wl->sync[i] = wl->sync[i-1];
1460 }
1461 struct vo_wayland_sync sync = {0, 0, 0, 0};
1462 wl->sync[0] = sync;
1463 }
1464
toggle_fullscreen(struct vo_wayland_state * wl)1465 static void toggle_fullscreen(struct vo_wayland_state *wl)
1466 {
1467 if (!wl->xdg_toplevel)
1468 return;
1469 wl->state_change = true;
1470 bool specific_screen = wl->vo_opts->fsscreen_id >= 0 || wl->vo_opts->fsscreen_name;
1471 if (wl->vo_opts->fullscreen && !specific_screen) {
1472 xdg_toplevel_set_fullscreen(wl->xdg_toplevel, NULL);
1473 } else if (wl->vo_opts->fullscreen && specific_screen) {
1474 struct vo_wayland_output *output = find_output(wl);
1475 xdg_toplevel_set_fullscreen(wl->xdg_toplevel, output->output);
1476 } else {
1477 xdg_toplevel_unset_fullscreen(wl->xdg_toplevel);
1478 }
1479 }
1480
toggle_maximized(struct vo_wayland_state * wl)1481 static void toggle_maximized(struct vo_wayland_state *wl)
1482 {
1483 if (!wl->xdg_toplevel)
1484 return;
1485 wl->state_change = true;
1486 if (wl->vo_opts->window_maximized) {
1487 xdg_toplevel_set_maximized(wl->xdg_toplevel);
1488 } else {
1489 xdg_toplevel_unset_maximized(wl->xdg_toplevel);
1490 }
1491 }
1492
update_app_id(struct vo_wayland_state * wl)1493 static void update_app_id(struct vo_wayland_state *wl)
1494 {
1495 if (!wl->xdg_toplevel)
1496 return;
1497 xdg_toplevel_set_app_id(wl->xdg_toplevel, wl->vo_opts->appid);
1498 }
1499
update_window_title(struct vo_wayland_state * wl,const char * title)1500 static int update_window_title(struct vo_wayland_state *wl, const char *title)
1501 {
1502 if (!wl->xdg_toplevel)
1503 return VO_NOTAVAIL;
1504 xdg_toplevel_set_title(wl->xdg_toplevel, title);
1505 return VO_TRUE;
1506 }
1507
window_move(struct vo_wayland_state * wl,uint32_t serial)1508 static void window_move(struct vo_wayland_state *wl, uint32_t serial)
1509 {
1510 if (wl->xdg_toplevel)
1511 xdg_toplevel_move(wl->xdg_toplevel, wl->seat, serial);
1512 }
1513
vo_wayland_dispatch_events(struct vo_wayland_state * wl,int nfds,int timeout)1514 static void vo_wayland_dispatch_events(struct vo_wayland_state *wl, int nfds, int timeout)
1515 {
1516 struct pollfd fds[2] = {
1517 {.fd = wl->display_fd, .events = POLLIN },
1518 {.fd = wl->wakeup_pipe[0], .events = POLLIN },
1519 };
1520
1521 while (wl_display_prepare_read(wl->display) != 0)
1522 wl_display_dispatch_pending(wl->display);
1523 wl_display_flush(wl->display);
1524
1525 poll(fds, nfds, timeout);
1526
1527 if (fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) {
1528 MP_FATAL(wl, "Error occurred on the display fd, closing\n");
1529 wl_display_cancel_read(wl->display);
1530 close(wl->display_fd);
1531 wl->display_fd = -1;
1532 mp_input_put_key(wl->vo->input_ctx, MP_KEY_CLOSE_WIN);
1533 } else {
1534 wl_display_read_events(wl->display);
1535 }
1536
1537 if (fds[0].revents & POLLIN)
1538 wl_display_dispatch_pending(wl->display);
1539
1540 if (fds[1].revents & POLLIN)
1541 mp_flush_wakeup_pipe(wl->wakeup_pipe[0]);
1542 }
1543
1544 /* Non-static */
vo_wayland_control(struct vo * vo,int * events,int request,void * arg)1545 int vo_wayland_control(struct vo *vo, int *events, int request, void *arg)
1546 {
1547 struct vo_wayland_state *wl = vo->wl;
1548 struct mp_vo_opts *opts = wl->vo_opts;
1549 wl_display_dispatch_pending(wl->display);
1550
1551 switch (request) {
1552 case VOCTRL_CHECK_EVENTS: {
1553 check_dnd_fd(wl);
1554 *events |= wl->pending_vo_events;
1555 wl->pending_vo_events = 0;
1556 return VO_TRUE;
1557 }
1558 case VOCTRL_VO_OPTS_CHANGED: {
1559 void *opt;
1560 while (m_config_cache_get_next_changed(wl->vo_opts_cache, &opt)) {
1561 if (opt == &opts->appid)
1562 update_app_id(wl);
1563 if (opt == &opts->border)
1564 {
1565 // This is stupid but the value of border shouldn't be written
1566 // unless we get a configure event. Change it back to its old
1567 // value and let configure_decorations handle it after the request.
1568 if (wl->xdg_toplevel_decoration) {
1569 opts->border = !opts->border;
1570 m_config_cache_write_opt(wl->vo_opts_cache,
1571 &opts->border);
1572 request_decoration_mode(wl, !opts->border + 1);
1573 } else {
1574 opts->border = false;
1575 m_config_cache_write_opt(wl->vo_opts_cache,
1576 &wl->vo_opts->border);
1577 }
1578 }
1579 if (opt == &opts->fullscreen)
1580 toggle_fullscreen(wl);
1581 if (opt == &opts->hidpi_window_scale)
1582 {
1583 set_surface_scaling(wl);
1584 if (!wl->vo_opts->fullscreen && !wl->vo_opts->window_maximized) {
1585 wl_surface_set_buffer_scale(wl->surface, wl->scaling);
1586 } else {
1587 wl->scale_change = true;
1588 }
1589 }
1590 if (opt == &opts->window_maximized)
1591 toggle_maximized(wl);
1592 if (opt == &opts->window_minimized)
1593 do_minimize(wl);
1594 if (opt == &opts->geometry || opt == &opts->autofit ||
1595 opt == &opts->autofit_smaller || opt == &opts->autofit_larger)
1596 {
1597 if (wl->current_output) {
1598 set_geometry(wl);
1599 wl->window_size = wl->vdparams;
1600 if (!wl->vo_opts->fullscreen && !wl->vo_opts->window_maximized)
1601 wl->geometry = wl->window_size;
1602 wl->pending_vo_events |= VO_EVENT_RESIZE;
1603 }
1604 }
1605 }
1606 return VO_TRUE;
1607 }
1608 case VOCTRL_GET_FOCUSED: {
1609 *(bool *)arg = wl->focused;
1610 return VO_TRUE;
1611 }
1612 case VOCTRL_GET_DISPLAY_NAMES: {
1613 *(char ***)arg = get_displays_spanned(wl);
1614 return VO_TRUE;
1615 }
1616 case VOCTRL_GET_UNFS_WINDOW_SIZE: {
1617 int *s = arg;
1618 if (wl->vo_opts->window_maximized) {
1619 s[0] = mp_rect_w(wl->geometry) * wl->scaling;
1620 s[1] = mp_rect_h(wl->geometry) * wl->scaling;
1621 } else {
1622 s[0] = mp_rect_w(wl->window_size) * wl->scaling;
1623 s[1] = mp_rect_h(wl->window_size) * wl->scaling;
1624 }
1625 return VO_TRUE;
1626 }
1627 case VOCTRL_SET_UNFS_WINDOW_SIZE: {
1628 int *s = arg;
1629 wl->window_size.x0 = 0;
1630 wl->window_size.y0 = 0;
1631 wl->window_size.x1 = s[0] / wl->scaling;
1632 wl->window_size.y1 = s[1] / wl->scaling;
1633 if (!wl->vo_opts->fullscreen) {
1634 if (wl->vo_opts->window_maximized) {
1635 xdg_toplevel_unset_maximized(wl->xdg_toplevel);
1636 wl_display_dispatch_pending(wl->display);
1637 /* Make sure the compositor let us unmaximize */
1638 if (wl->vo_opts->window_maximized)
1639 return VO_TRUE;
1640 }
1641 wl->geometry = wl->window_size;
1642 wl->pending_vo_events |= VO_EVENT_RESIZE;
1643 }
1644 return VO_TRUE;
1645 }
1646 case VOCTRL_GET_DISPLAY_FPS: {
1647 if (!wl->current_output)
1648 return VO_NOTAVAIL;
1649 *(double *)arg = wl->current_output->refresh_rate;
1650 return VO_TRUE;
1651 }
1652 case VOCTRL_GET_DISPLAY_RES: {
1653 if (!wl->current_output)
1654 return VO_NOTAVAIL;
1655 ((int *)arg)[0] = wl->current_output->geometry.x1;
1656 ((int *)arg)[1] = wl->current_output->geometry.y1;
1657 return VO_TRUE;
1658 }
1659 case VOCTRL_GET_HIDPI_SCALE: {
1660 if (!wl->scaling)
1661 return VO_NOTAVAIL;
1662 *(double *)arg = wl->scaling;
1663 return VO_TRUE;
1664 }
1665 case VOCTRL_UPDATE_WINDOW_TITLE:
1666 return update_window_title(wl, (const char *)arg);
1667 case VOCTRL_SET_CURSOR_VISIBILITY:
1668 if (!wl->pointer)
1669 return VO_NOTAVAIL;
1670 return set_cursor_visibility(wl, *(bool *)arg);
1671 case VOCTRL_KILL_SCREENSAVER:
1672 return set_screensaver_inhibitor(wl, true);
1673 case VOCTRL_RESTORE_SCREENSAVER:
1674 return set_screensaver_inhibitor(wl, false);
1675 }
1676
1677 return VO_NOTIMPL;
1678 }
1679
vo_wayland_init(struct vo * vo)1680 int vo_wayland_init(struct vo *vo)
1681 {
1682 vo->wl = talloc_zero(NULL, struct vo_wayland_state);
1683 struct vo_wayland_state *wl = vo->wl;
1684
1685 *wl = (struct vo_wayland_state) {
1686 .display = wl_display_connect(NULL),
1687 .vo = vo,
1688 .log = mp_log_new(wl, vo->log, "wayland"),
1689 .scaling = 1,
1690 .wakeup_pipe = {-1, -1},
1691 .dnd_fd = -1,
1692 .cursor_visible = true,
1693 .vo_opts_cache = m_config_cache_alloc(wl, vo->global, &vo_sub_opts),
1694 };
1695 wl->vo_opts = wl->vo_opts_cache->opts;
1696
1697 wl_list_init(&wl->output_list);
1698
1699 if (!wl->display)
1700 return false;
1701
1702 if (create_input(wl))
1703 return false;
1704
1705 wl->registry = wl_display_get_registry(wl->display);
1706 wl_registry_add_listener(wl->registry, ®istry_listener, wl);
1707
1708 /* Do a roundtrip to run the registry */
1709 wl_display_roundtrip(wl->display);
1710
1711 if (!wl->wm_base) {
1712 MP_FATAL(wl, "Compositor doesn't support the required %s protocol!\n",
1713 xdg_wm_base_interface.name);
1714 return false;
1715 }
1716
1717 if (!wl_list_length(&wl->output_list)) {
1718 MP_FATAL(wl, "No outputs found or compositor doesn't support %s (ver. 2)\n",
1719 wl_output_interface.name);
1720 return false;
1721 }
1722
1723 /* Can't be initialized during registry due to multi-protocol dependence */
1724 if (create_xdg_surface(wl))
1725 return false;
1726
1727 const char *xdg_current_desktop = getenv("XDG_CURRENT_DESKTOP");
1728 if (xdg_current_desktop != NULL && strstr(xdg_current_desktop, "GNOME"))
1729 MP_WARN(wl, "GNOME's wayland compositor lacks support for the idle inhibit protocol. This means the screen can blank during playback.\n");
1730
1731 if (wl->dnd_devman && wl->seat) {
1732 wl->dnd_ddev = wl_data_device_manager_get_data_device(wl->dnd_devman, wl->seat);
1733 wl_data_device_add_listener(wl->dnd_ddev, &data_device_listener, wl);
1734 } else if (!wl->dnd_devman) {
1735 MP_VERBOSE(wl, "Compositor doesn't support the %s (ver. 3) protocol!\n",
1736 wl_data_device_manager_interface.name);
1737 }
1738
1739 if (wl->presentation) {
1740 wl->sync = talloc_zero_array(wl, struct vo_wayland_sync, 1);
1741 struct vo_wayland_sync sync = {0, 0, 0, 0};
1742 wl->sync[0] = sync;
1743 wl->sync_size += 1;
1744 } else {
1745 MP_VERBOSE(wl, "Compositor doesn't support the %s protocol!\n",
1746 wp_presentation_interface.name);
1747 }
1748
1749 if (wl->xdg_decoration_manager) {
1750 wl->xdg_toplevel_decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(wl->xdg_decoration_manager, wl->xdg_toplevel);
1751 zxdg_toplevel_decoration_v1_add_listener(wl->xdg_toplevel_decoration, &decoration_listener, wl);
1752 request_decoration_mode(wl, wl->vo_opts->border + 1);
1753 } else {
1754 wl->vo_opts->border = false;
1755 m_config_cache_write_opt(wl->vo_opts_cache,
1756 &wl->vo_opts->border);
1757 MP_VERBOSE(wl, "Compositor doesn't support the %s protocol!\n",
1758 zxdg_decoration_manager_v1_interface.name);
1759 }
1760
1761 if (!wl->idle_inhibit_manager)
1762 MP_VERBOSE(wl, "Compositor doesn't support the %s protocol!\n",
1763 zwp_idle_inhibit_manager_v1_interface.name);
1764
1765 wl->opts = mp_get_config_group(wl, wl->vo->global, &wayland_conf);
1766 wl->display_fd = wl_display_get_fd(wl->display);
1767 wl->frame_callback = wl_surface_frame(wl->surface);
1768 wl_callback_add_listener(wl->frame_callback, &frame_listener, wl);
1769
1770 update_app_id(wl);
1771 mp_make_wakeup_pipe(wl->wakeup_pipe);
1772
1773 return true;
1774 }
1775
vo_wayland_reconfig(struct vo * vo)1776 int vo_wayland_reconfig(struct vo *vo)
1777 {
1778 struct vo_wayland_state *wl = vo->wl;
1779 bool configure = false;
1780
1781 MP_VERBOSE(wl, "Reconfiguring!\n");
1782
1783 if (!wl->current_output) {
1784 wl->current_output = find_output(wl);
1785 if (!wl->current_output)
1786 return false;
1787 set_surface_scaling(wl);
1788 wl_surface_set_buffer_scale(wl->surface, wl->scaling);
1789 wl_surface_commit(wl->surface);
1790 configure = true;
1791 }
1792
1793 set_geometry(wl);
1794
1795 wl->window_size = wl->vdparams;
1796
1797 if (!wl->vo_opts->fullscreen && !wl->vo_opts->window_maximized)
1798 wl->geometry = wl->window_size;
1799
1800 if (wl->vo_opts->fullscreen)
1801 toggle_fullscreen(wl);
1802
1803 if (wl->vo_opts->window_maximized)
1804 toggle_maximized(wl);
1805
1806 if (wl->vo_opts->window_minimized)
1807 do_minimize(wl);
1808
1809 if (configure) {
1810 wl->window_size = wl->vdparams;
1811 wl->geometry = wl->window_size;
1812 wl_display_roundtrip(wl->display);
1813 wl->pending_vo_events |= VO_EVENT_DPI;
1814 }
1815
1816 wl->pending_vo_events |= VO_EVENT_RESIZE;
1817
1818 return true;
1819 }
1820
vo_wayland_set_opaque_region(struct vo_wayland_state * wl,int alpha)1821 void vo_wayland_set_opaque_region(struct vo_wayland_state *wl, int alpha)
1822 {
1823 const int32_t width = wl->scaling * mp_rect_w(wl->geometry);
1824 const int32_t height = wl->scaling * mp_rect_h(wl->geometry);
1825 if (!alpha) {
1826 struct wl_region *region = wl_compositor_create_region(wl->compositor);
1827 wl_region_add(region, 0, 0, width, height);
1828 wl_surface_set_opaque_region(wl->surface, region);
1829 wl_region_destroy(region);
1830 } else {
1831 wl_surface_set_opaque_region(wl->surface, NULL);
1832 }
1833 }
1834
vo_wayland_sync_swap(struct vo_wayland_state * wl)1835 void vo_wayland_sync_swap(struct vo_wayland_state *wl)
1836 {
1837 int index = wl->sync_size - 1;
1838
1839 // If these are the same, presentation feedback has not been received.
1840 // This can happen if a frame takes too long and misses vblank.
1841 // Additionally, a compositor may return an ust value of 0. In either case,
1842 // Don't attempt to use these statistics and wait until the next presentation
1843 // event arrives.
1844 if (!wl->sync[index].ust || wl->sync[index].ust == wl->last_ust) {
1845 wl->last_skipped_vsyncs = -1;
1846 wl->vsync_duration = -1;
1847 wl->last_queue_display_time = -1;
1848 return;
1849 }
1850
1851 wl->last_skipped_vsyncs = 0;
1852
1853 int64_t ust_passed = wl->sync[index].ust ? wl->sync[index].ust - wl->last_ust: 0;
1854 wl->last_ust = wl->sync[index].ust;
1855 int64_t msc_passed = wl->sync[index].msc ? wl->sync[index].msc - wl->last_msc: 0;
1856 wl->last_msc = wl->sync[index].msc;
1857
1858 if (msc_passed && ust_passed)
1859 wl->vsync_duration = ust_passed / msc_passed;
1860
1861 struct timespec ts;
1862 if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
1863 return;
1864 }
1865
1866 uint64_t now_monotonic = ts.tv_sec * 1000000LL + ts.tv_nsec / 1000;
1867 uint64_t ust_mp_time = mp_time_us() - (now_monotonic - wl->sync[index].ust);
1868
1869 wl->last_queue_display_time = ust_mp_time + wl->vsync_duration;
1870 }
1871
vo_wayland_uninit(struct vo * vo)1872 void vo_wayland_uninit(struct vo *vo)
1873 {
1874 struct vo_wayland_state *wl = vo->wl;
1875 if (!wl)
1876 return;
1877
1878 mp_input_put_key(wl->vo->input_ctx, MP_INPUT_RELEASE_ALL);
1879
1880 if (wl->compositor)
1881 wl_compositor_destroy(wl->compositor);
1882
1883 if (wl->current_output && wl->current_output->output)
1884 wl_output_destroy(wl->current_output->output);
1885
1886 if (wl->cursor_surface)
1887 wl_surface_destroy(wl->cursor_surface);
1888
1889 if (wl->cursor_theme)
1890 wl_cursor_theme_destroy(wl->cursor_theme);
1891
1892 if (wl->dnd_ddev)
1893 wl_data_device_destroy(wl->dnd_ddev);
1894
1895 if (wl->dnd_devman)
1896 wl_data_device_manager_destroy(wl->dnd_devman);
1897
1898 if (wl->dnd_offer)
1899 wl_data_offer_destroy(wl->dnd_offer);
1900
1901 if (wl->feedback)
1902 wp_presentation_feedback_destroy(wl->feedback);
1903
1904 if (wl->frame_callback)
1905 wl_callback_destroy(wl->frame_callback);
1906
1907 if (wl->idle_inhibitor)
1908 zwp_idle_inhibitor_v1_destroy(wl->idle_inhibitor);
1909
1910 if (wl->idle_inhibit_manager)
1911 zwp_idle_inhibit_manager_v1_destroy(wl->idle_inhibit_manager);
1912
1913 if (wl->keyboard)
1914 wl_keyboard_destroy(wl->keyboard);
1915
1916 if (wl->pointer)
1917 wl_pointer_destroy(wl->pointer);
1918
1919 if (wl->presentation)
1920 wp_presentation_destroy(wl->presentation);
1921
1922 if (wl->registry)
1923 wl_registry_destroy(wl->registry);
1924
1925 if (wl->seat)
1926 wl_seat_destroy(wl->seat);
1927
1928 if (wl->shm)
1929 wl_shm_destroy(wl->shm);
1930
1931 if (wl->surface)
1932 wl_surface_destroy(wl->surface);
1933
1934 if (wl->wm_base)
1935 xdg_wm_base_destroy(wl->wm_base);
1936
1937 if (wl->xdg_decoration_manager)
1938 zxdg_decoration_manager_v1_destroy(wl->xdg_decoration_manager);
1939
1940 if (wl->xdg_toplevel)
1941 xdg_toplevel_destroy(wl->xdg_toplevel);
1942
1943 if (wl->xdg_toplevel_decoration)
1944 zxdg_toplevel_decoration_v1_destroy(wl->xdg_toplevel_decoration);
1945
1946 if (wl->xdg_surface)
1947 xdg_surface_destroy(wl->xdg_surface);
1948
1949 if (wl->xkb_context)
1950 xkb_context_unref(wl->xkb_context);
1951
1952 if (wl->xkb_keymap)
1953 xkb_keymap_unref(wl->xkb_keymap);
1954
1955 if (wl->xkb_state)
1956 xkb_state_unref(wl->xkb_state);
1957
1958 if (wl->display) {
1959 close(wl_display_get_fd(wl->display));
1960 wl_display_disconnect(wl->display);
1961 }
1962
1963 struct vo_wayland_output *output, *tmp;
1964 wl_list_for_each_safe(output, tmp, &wl->output_list, link)
1965 remove_output(output);
1966
1967 talloc_free(wl->dnd_mime_type);
1968
1969 for (int n = 0; n < 2; n++)
1970 close(wl->wakeup_pipe[n]);
1971 talloc_free(wl);
1972 vo->wl = NULL;
1973 }
1974
vo_wayland_wait_frame(struct vo_wayland_state * wl)1975 void vo_wayland_wait_frame(struct vo_wayland_state *wl)
1976 {
1977 int64_t vblank_time = 0;
1978 /* We need some vblank interval to use for the timeout in
1979 * this function. The order of preference of values to use is:
1980 * 1. vsync duration from presentation time
1981 * 2. refresh inteval reported by presentation time
1982 * 3. refresh rate of the output reported by the compositor
1983 * 4. make up crap if vblank_time is still <= 0 (better than nothing) */
1984
1985 if (wl->presentation)
1986 vblank_time = wl->vsync_duration;
1987
1988 if (vblank_time <= 0 && wl->refresh_interval > 0)
1989 vblank_time = wl->refresh_interval;
1990
1991 if (vblank_time <= 0 && wl->current_output->refresh_rate > 0)
1992 vblank_time = 1e6 / wl->current_output->refresh_rate;
1993
1994 // Ideally you should never reach this point.
1995 if (vblank_time <= 0)
1996 vblank_time = 1e6 / 60;
1997
1998 int64_t finish_time = mp_time_us() + vblank_time;
1999
2000 while (wl->frame_wait && finish_time > mp_time_us()) {
2001 int poll_time = ceil((double)(finish_time - mp_time_us()) / 1000);
2002 if (poll_time < 0) {
2003 poll_time = 0;
2004 }
2005 vo_wayland_dispatch_events(wl, 1, poll_time);
2006 }
2007
2008 /* If the compositor does not have presentation time, we cannot be sure
2009 * that this wait is accurate. Do a hacky block with wl_display_roundtrip. */
2010 if (!wl->presentation && !wl_display_get_error(wl->display))
2011 wl_display_roundtrip(wl->display);
2012
2013 if (wl->frame_wait) {
2014 // Only consider consecutive missed callbacks.
2015 if (wl->timeout_count > 1) {
2016 wl->hidden = true;
2017 return;
2018 } else {
2019 wl->timeout_count += 1;
2020 return;
2021 }
2022 }
2023
2024 wl->timeout_count = 0;
2025 }
2026
vo_wayland_wait_events(struct vo * vo,int64_t until_time_us)2027 void vo_wayland_wait_events(struct vo *vo, int64_t until_time_us)
2028 {
2029 struct vo_wayland_state *wl = vo->wl;
2030
2031 if (wl->display_fd == -1)
2032 return;
2033
2034 int64_t wait_us = until_time_us - mp_time_us();
2035 int timeout_ms = MPCLAMP((wait_us + 999) / 1000, 0, 10000);
2036
2037 vo_wayland_dispatch_events(wl, 2, timeout_ms);
2038 }
2039
vo_wayland_wakeup(struct vo * vo)2040 void vo_wayland_wakeup(struct vo *vo)
2041 {
2042 struct vo_wayland_state *wl = vo->wl;
2043 (void)write(wl->wakeup_pipe[1], &(char){0}, 1);
2044 }
2045