diff options
Diffstat (limited to 'video/out/wayland_common.c')
-rw-r--r-- | video/out/wayland_common.c | 3256 |
1 files changed, 2248 insertions, 1008 deletions
diff --git a/video/out/wayland_common.c b/video/out/wayland_common.c index 0d6b68c4f0..4a86c21c7e 100644 --- a/video/out/wayland_common.c +++ b/video/out/wayland_common.c @@ -17,305 +17,463 @@ #include <errno.h> #include <limits.h> +#include <linux/input-event-codes.h> #include <poll.h> -#include <unistd.h> -#include <linux/input.h> #include <time.h> +#include <unistd.h> +#include <wayland-cursor.h> +#include <xkbcommon/xkbcommon.h> + #include "common/msg.h" -#include "options/m_config.h" #include "input/input.h" #include "input/keycodes.h" +#include "options/m_config.h" #include "osdep/io.h" +#include "osdep/poll_wrapper.h" #include "osdep/timer.h" -#include "win_state.h" +#include "present_sync.h" #include "wayland_common.h" +#include "win_state.h" + +// Generated from wayland-protocols +#include "idle-inhibit-unstable-v1.h" +#include "linux-dmabuf-unstable-v1.h" +#include "presentation-time.h" +#include "xdg-decoration-unstable-v1.h" +#include "xdg-shell.h" +#include "viewporter.h" + +#if HAVE_WAYLAND_PROTOCOLS_1_27 +#include "content-type-v1.h" +#include "single-pixel-buffer-v1.h" +#endif + +#if HAVE_WAYLAND_PROTOCOLS_1_31 +#include "fractional-scale-v1.h" +#endif + +#if HAVE_WAYLAND_PROTOCOLS_1_32 +#include "cursor-shape-v1.h" +#endif + +#if WAYLAND_VERSION_MAJOR > 1 || WAYLAND_VERSION_MINOR >= 21 +#define HAVE_WAYLAND_1_21 +#endif + +#if WAYLAND_VERSION_MAJOR > 1 || WAYLAND_VERSION_MINOR >= 22 +#define HAVE_WAYLAND_1_22 +#endif + +#ifndef CLOCK_MONOTONIC_RAW +#define CLOCK_MONOTONIC_RAW 4 +#endif + +#ifndef XDG_TOPLEVEL_STATE_SUSPENDED +#define XDG_TOPLEVEL_STATE_SUSPENDED 9 +#endif + + +static const struct mp_keymap keymap[] = { + /* Special keys */ + {XKB_KEY_Pause, MP_KEY_PAUSE}, {XKB_KEY_Escape, MP_KEY_ESC}, + {XKB_KEY_BackSpace, MP_KEY_BS}, {XKB_KEY_Tab, MP_KEY_TAB}, + {XKB_KEY_Return, MP_KEY_ENTER}, {XKB_KEY_Menu, MP_KEY_MENU}, + {XKB_KEY_Print, MP_KEY_PRINT}, {XKB_KEY_ISO_Left_Tab, MP_KEY_TAB}, + + /* Cursor keys */ + {XKB_KEY_Left, MP_KEY_LEFT}, {XKB_KEY_Right, MP_KEY_RIGHT}, + {XKB_KEY_Up, MP_KEY_UP}, {XKB_KEY_Down, MP_KEY_DOWN}, + + /* Navigation keys */ + {XKB_KEY_Insert, MP_KEY_INSERT}, {XKB_KEY_Delete, MP_KEY_DELETE}, + {XKB_KEY_Home, MP_KEY_HOME}, {XKB_KEY_End, MP_KEY_END}, + {XKB_KEY_Page_Up, MP_KEY_PAGE_UP}, {XKB_KEY_Page_Down, MP_KEY_PAGE_DOWN}, -// Generated from xdg-shell.xml -#include "video/out/wayland/xdg-shell.h" + /* F-keys */ + {XKB_KEY_F1, MP_KEY_F + 1}, {XKB_KEY_F2, MP_KEY_F + 2}, + {XKB_KEY_F3, MP_KEY_F + 3}, {XKB_KEY_F4, MP_KEY_F + 4}, + {XKB_KEY_F5, MP_KEY_F + 5}, {XKB_KEY_F6, MP_KEY_F + 6}, + {XKB_KEY_F7, MP_KEY_F + 7}, {XKB_KEY_F8, MP_KEY_F + 8}, + {XKB_KEY_F9, MP_KEY_F + 9}, {XKB_KEY_F10, MP_KEY_F +10}, + {XKB_KEY_F11, MP_KEY_F +11}, {XKB_KEY_F12, MP_KEY_F +12}, + {XKB_KEY_F13, MP_KEY_F +13}, {XKB_KEY_F14, MP_KEY_F +14}, + {XKB_KEY_F15, MP_KEY_F +15}, {XKB_KEY_F16, MP_KEY_F +16}, + {XKB_KEY_F17, MP_KEY_F +17}, {XKB_KEY_F18, MP_KEY_F +18}, + {XKB_KEY_F19, MP_KEY_F +19}, {XKB_KEY_F20, MP_KEY_F +20}, + {XKB_KEY_F21, MP_KEY_F +21}, {XKB_KEY_F22, MP_KEY_F +22}, + {XKB_KEY_F23, MP_KEY_F +23}, {XKB_KEY_F24, MP_KEY_F +24}, -// Generated from idle-inhibit-unstable-v1.xml -#include "video/out/wayland/idle-inhibit-v1.h" + /* Numpad independent of numlock */ + {XKB_KEY_KP_Subtract, '-'}, {XKB_KEY_KP_Add, '+'}, + {XKB_KEY_KP_Multiply, '*'}, {XKB_KEY_KP_Divide, '/'}, + {XKB_KEY_KP_Enter, MP_KEY_KPENTER}, -// Generated from xdg-decoration-unstable-v1.xml -#include "video/out/wayland/xdg-decoration-v1.h" + /* Numpad with numlock */ + {XKB_KEY_KP_0, MP_KEY_KP0}, {XKB_KEY_KP_1, MP_KEY_KP1}, + {XKB_KEY_KP_2, MP_KEY_KP2}, {XKB_KEY_KP_3, MP_KEY_KP3}, + {XKB_KEY_KP_4, MP_KEY_KP4}, {XKB_KEY_KP_5, MP_KEY_KP5}, + {XKB_KEY_KP_6, MP_KEY_KP6}, {XKB_KEY_KP_7, MP_KEY_KP7}, + {XKB_KEY_KP_8, MP_KEY_KP8}, {XKB_KEY_KP_9, MP_KEY_KP9}, + {XKB_KEY_KP_Decimal, MP_KEY_KPDEC}, {XKB_KEY_KP_Separator, MP_KEY_KPDEC}, -// Generated from presentation-time.xml -#include "video/out/wayland/presentation-time.h" + /* Numpad without numlock */ + {XKB_KEY_KP_Insert, MP_KEY_KPINS}, {XKB_KEY_KP_End, MP_KEY_KPEND}, + {XKB_KEY_KP_Down, MP_KEY_KPDOWN}, {XKB_KEY_KP_Page_Down, MP_KEY_KPPGDOWN}, + {XKB_KEY_KP_Left, MP_KEY_KPLEFT}, {XKB_KEY_KP_Begin, MP_KEY_KP5}, + {XKB_KEY_KP_Right, MP_KEY_KPRIGHT}, {XKB_KEY_KP_Home, MP_KEY_KPHOME}, + {XKB_KEY_KP_Up, MP_KEY_KPUP}, {XKB_KEY_KP_Page_Up, MP_KEY_KPPGUP}, + {XKB_KEY_KP_Delete, MP_KEY_KPDEL}, + + /* Multimedia keys */ + {XKB_KEY_XF86MenuKB, MP_KEY_MENU}, + {XKB_KEY_XF86AudioPlay, MP_KEY_PLAY}, {XKB_KEY_XF86AudioPause, MP_KEY_PAUSE}, + {XKB_KEY_XF86AudioStop, MP_KEY_STOP}, + {XKB_KEY_XF86AudioPrev, MP_KEY_PREV}, {XKB_KEY_XF86AudioNext, MP_KEY_NEXT}, + {XKB_KEY_XF86AudioRewind, MP_KEY_REWIND}, + {XKB_KEY_XF86AudioForward, MP_KEY_FORWARD}, + {XKB_KEY_XF86AudioMute, MP_KEY_MUTE}, + {XKB_KEY_XF86AudioLowerVolume, MP_KEY_VOLUME_DOWN}, + {XKB_KEY_XF86AudioRaiseVolume, MP_KEY_VOLUME_UP}, + {XKB_KEY_XF86HomePage, MP_KEY_HOMEPAGE}, {XKB_KEY_XF86WWW, MP_KEY_WWW}, + {XKB_KEY_XF86Mail, MP_KEY_MAIL}, {XKB_KEY_XF86Favorites, MP_KEY_FAVORITES}, + {XKB_KEY_XF86Search, MP_KEY_SEARCH}, {XKB_KEY_XF86Sleep, MP_KEY_SLEEP}, + {XKB_KEY_XF86Back, MP_KEY_GO_BACK}, {XKB_KEY_XF86Forward, MP_KEY_GO_FORWARD}, + {XKB_KEY_XF86Tools, MP_KEY_TOOLS}, + {XKB_KEY_XF86ZoomIn, MP_KEY_ZOOMIN}, {XKB_KEY_XF86ZoomOut, MP_KEY_ZOOMOUT}, + + {0, 0} +}; #define OPT_BASE_STRUCT struct wayland_opts const struct m_sub_options wayland_conf = { .opts = (const struct m_option[]) { - OPT_FLAG("wayland-disable-vsync", disable_vsync, 0), - OPT_INTRANGE("wayland-edge-pixels-pointer", edge_pixels_pointer, 10, 0, INT_MAX), - OPT_INTRANGE("wayland-edge-pixels-touch", edge_pixels_touch, 64, 0, INT_MAX), + {"wayland-configure-bounds", OPT_CHOICE(configure_bounds, + {"auto", -1}, {"no", 0}, {"yes", 1})}, + {"wayland-disable-vsync", OPT_BOOL(disable_vsync)}, + {"wayland-edge-pixels-pointer", OPT_INT(edge_pixels_pointer), + M_RANGE(0, INT_MAX)}, + {"wayland-edge-pixels-touch", OPT_INT(edge_pixels_touch), + M_RANGE(0, INT_MAX)}, {0}, }, .size = sizeof(struct wayland_opts), .defaults = &(struct wayland_opts) { - .disable_vsync = false, - .edge_pixels_pointer = 10, - .edge_pixels_touch = 64, + .configure_bounds = -1, + .edge_pixels_pointer = 16, + .edge_pixels_touch = 32, }, }; -static void xdg_wm_base_ping(void *data, struct xdg_wm_base *wm_base, uint32_t serial) -{ - xdg_wm_base_pong(wm_base, serial); -} - -static const struct xdg_wm_base_listener xdg_wm_base_listener = { - xdg_wm_base_ping, +struct vo_wayland_feedback_pool { + struct wp_presentation_feedback **fback; + struct vo_wayland_state *wl; + int len; }; -static int spawn_cursor(struct vo_wayland_state *wl) -{ - if (wl->allocated_cursor_scale == wl->scaling) /* Reuse if size is identical */ - return 0; - else if (wl->cursor_theme) - wl_cursor_theme_destroy(wl->cursor_theme); - - const char *size_str = getenv("XCURSOR_SIZE"); - int size = 32; - if (size_str != NULL) { - errno = 0; - char *end; - long size_long = strtol(size_str, &end, 10); - if (!*end && !errno && size_long > 0 && size_long <= INT_MAX) - size = (int)size_long; - } - - wl->cursor_theme = wl_cursor_theme_load(NULL, size*wl->scaling, wl->shm); - if (!wl->cursor_theme) { - MP_ERR(wl, "Unable to load cursor theme!\n"); - return 1; - } - - wl->default_cursor = wl_cursor_theme_get_cursor(wl->cursor_theme, "left_ptr"); - if (!wl->default_cursor) { - MP_ERR(wl, "Unable to load cursor theme!\n"); - return 1; - } - - wl->allocated_cursor_scale = wl->scaling; - - return 0; -} +struct vo_wayland_output { + struct vo_wayland_state *wl; + struct wl_output *output; + struct mp_rect geometry; + bool has_surface; + uint32_t id; + uint32_t flags; + int phys_width; + int phys_height; + int scale; + double refresh_rate; + char *make; + char *model; + char *name; + struct wl_list link; +}; -static int set_cursor_visibility(struct vo_wayland_state *wl, bool on) -{ - wl->cursor_visible = on; - if (on) { - if (spawn_cursor(wl)) - return VO_FALSE; - struct wl_cursor_image *img = wl->default_cursor->images[0]; - struct wl_buffer *buffer = wl_cursor_image_get_buffer(img); - if (!buffer) - return VO_FALSE; - wl_pointer_set_cursor(wl->pointer, wl->pointer_id, wl->cursor_surface, - img->hotspot_x/wl->scaling, img->hotspot_y/wl->scaling); - wl_surface_set_buffer_scale(wl->cursor_surface, wl->scaling); - wl_surface_attach(wl->cursor_surface, buffer, 0, 0); - wl_surface_damage(wl->cursor_surface, 0, 0, img->width, img->height); - wl_surface_commit(wl->cursor_surface); - } else { - wl_pointer_set_cursor(wl->pointer, wl->pointer_id, NULL, 0, 0); - } - return VO_TRUE; -} +struct vo_wayland_seat { + struct vo_wayland_state *wl; + struct wl_seat *seat; + uint32_t id; + struct wl_keyboard *keyboard; + struct wl_pointer *pointer; + struct wl_touch *touch; + struct wl_data_device *dnd_ddev; + /* TODO: unvoid this if required wayland protocols is bumped to 1.32+ */ + void *cursor_shape_device; + uint32_t pointer_enter_serial; + uint32_t pointer_button_serial; + struct xkb_keymap *xkb_keymap; + struct xkb_state *xkb_state; + uint32_t keyboard_code; + int mpkey; + int mpmod; + double axis_value_vertical; + int32_t axis_value120_vertical; + double axis_value_horizontal; + int32_t axis_value120_horizontal; + bool axis_value120_scroll; + bool has_keyboard_input; + struct wl_list link; +}; +static bool single_output_spanned(struct vo_wayland_state *wl); + +static int check_for_resize(struct vo_wayland_state *wl, int edge_pixels, + enum xdg_toplevel_resize_edge *edge); +static int get_mods(struct vo_wayland_seat *seat); +static int greatest_common_divisor(int a, int b); +static int lookupkey(int key); +static int set_cursor_visibility(struct vo_wayland_seat *s, bool on); +static int spawn_cursor(struct vo_wayland_state *wl); + +static void add_feedback(struct vo_wayland_feedback_pool *fback_pool, + struct wp_presentation_feedback *fback); +static void apply_keepaspect(struct vo_wayland_state *wl, int *width, int *height); +static void get_shape_device(struct vo_wayland_state *wl, struct vo_wayland_seat *s); +static void guess_focus(struct vo_wayland_state *wl); +static void prepare_resize(struct vo_wayland_state *wl); +static void remove_feedback(struct vo_wayland_feedback_pool *fback_pool, + struct wp_presentation_feedback *fback); +static void remove_output(struct vo_wayland_output *out); +static void remove_seat(struct vo_wayland_seat *seat); +static void request_decoration_mode(struct vo_wayland_state *wl, uint32_t mode); +static void rescale_geometry(struct vo_wayland_state *wl, double old_scale); +static void set_geometry(struct vo_wayland_state *wl, bool resize); +static void set_surface_scaling(struct vo_wayland_state *wl); +static void update_output_scaling(struct vo_wayland_state *wl); +static void update_output_geometry(struct vo_wayland_state *wl, struct mp_rect old_geometry, + struct mp_rect old_output_geometry); + +/* Wayland listener boilerplate */ static void pointer_handle_enter(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t sx, wl_fixed_t sy) { - struct vo_wayland_state *wl = data; - - wl->pointer = pointer; - wl->pointer_id = serial; + struct vo_wayland_seat *s = data; + struct vo_wayland_state *wl = s->wl; - set_cursor_visibility(wl, wl->cursor_visible); + s->pointer_enter_serial = serial; + set_cursor_visibility(s, wl->cursor_visible); mp_input_put_key(wl->vo->input_ctx, MP_KEY_MOUSE_ENTER); + + wl->mouse_x = wl_fixed_to_int(sx) * wl->scaling; + wl->mouse_y = wl_fixed_to_int(sy) * wl->scaling; + + if (!wl->toplevel_configured) + mp_input_set_mouse_pos(wl->vo->input_ctx, wl->mouse_x, wl->mouse_y); + wl->toplevel_configured = false; } static void pointer_handle_leave(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface) { - struct vo_wayland_state *wl = data; + struct vo_wayland_seat *s = data; + struct vo_wayland_state *wl = s->wl; mp_input_put_key(wl->vo->input_ctx, MP_KEY_MOUSE_LEAVE); } static void pointer_handle_motion(void *data, struct wl_pointer *pointer, uint32_t time, wl_fixed_t sx, wl_fixed_t sy) { - struct vo_wayland_state *wl = data; + struct vo_wayland_seat *s = data; + struct vo_wayland_state *wl = s->wl; wl->mouse_x = wl_fixed_to_int(sx) * wl->scaling; wl->mouse_y = wl_fixed_to_int(sy) * wl->scaling; - wl->mouse_unscaled_x = sx; - wl->mouse_unscaled_y = sy; - - mp_input_set_mouse_pos(wl->vo->input_ctx, wl->mouse_x, wl->mouse_y); -} - -static void window_move(struct vo_wayland_state *wl, uint32_t serial) -{ - if (wl->xdg_toplevel) - xdg_toplevel_move(wl->xdg_toplevel, wl->seat, serial); -} - -static int 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) -{ - if (wl->touch_entries || wl->vo_opts->fullscreen || wl->vo_opts->window_maximized) - return 0; - - int pos[2] = { wl_fixed_to_double(x_w), wl_fixed_to_double(y_w) }; - int left_edge = pos[0] < edge_pixels; - int top_edge = pos[1] < edge_pixels; - int right_edge = pos[0] > (mp_rect_w(wl->geometry) - edge_pixels); - int bottom_edge = pos[1] > (mp_rect_h(wl->geometry) - edge_pixels); - - if (left_edge) { - *edge = XDG_TOPLEVEL_RESIZE_EDGE_LEFT; - if (top_edge) - *edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT; - else if (bottom_edge) - *edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; - } else if (right_edge) { - *edge = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT; - if (top_edge) - *edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT; - else if (bottom_edge) - *edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; - } else if (top_edge) { - *edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP; - } else if (bottom_edge) { - *edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM; - } else { - *edge = 0; - return 0; - } - return 1; + if (!wl->toplevel_configured) + mp_input_set_mouse_pos(wl->vo->input_ctx, wl->mouse_x, wl->mouse_y); + wl->toplevel_configured = false; } static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { - struct vo_wayland_state *wl = data; - + struct vo_wayland_seat *s = data; + struct vo_wayland_state *wl = s->wl; state = state == WL_POINTER_BUTTON_STATE_PRESSED ? MP_KEY_STATE_DOWN : MP_KEY_STATE_UP; - switch (button) { - case BTN_LEFT: - button = MP_MBTN_LEFT; - break; - case BTN_MIDDLE: - button = MP_MBTN_MID; - break; - case BTN_RIGHT: - button = MP_MBTN_RIGHT; - break; - case BTN_SIDE: - button = MP_MBTN_BACK; - break; - case BTN_EXTRA: - button = MP_MBTN_FORWARD; - break; - default: + if (button >= BTN_MOUSE && button < BTN_JOYSTICK) { + switch (button) { + case BTN_LEFT: + button = MP_MBTN_LEFT; + break; + case BTN_MIDDLE: + button = MP_MBTN_MID; + break; + case BTN_RIGHT: + button = MP_MBTN_RIGHT; + break; + case BTN_SIDE: + button = MP_MBTN_BACK; + break; + case BTN_EXTRA: + button = MP_MBTN_FORWARD; + break; + default: + button += MP_MBTN9 - BTN_FORWARD; + break; + } + } else { button = 0; - break; } - if (button) { - mp_input_put_key(wl->vo->input_ctx, button | state); - } + if (button) + mp_input_put_key(wl->vo->input_ctx, button | state | s->mpmod); + enum xdg_toplevel_resize_edge edges; if (!mp_input_test_dragging(wl->vo->input_ctx, wl->mouse_x, wl->mouse_y) && - (button == MP_MBTN_LEFT) && (state == MP_KEY_STATE_DOWN)) { - uint32_t edges; + !wl->locked_size && (button == MP_MBTN_LEFT) && (state == MP_KEY_STATE_DOWN) && + !wl->vo_opts->border && check_for_resize(wl, wl->opts->edge_pixels_pointer, &edges)) + { // Implement an edge resize zone if there are no decorations - if (!wl->xdg_toplevel_decoration && - check_for_resize(wl, wl->mouse_unscaled_x, wl->mouse_unscaled_y, - wl->opts->edge_pixels_pointer, &edges)) - xdg_toplevel_resize(wl->xdg_toplevel, wl->seat, serial, edges); - else - window_move(wl, serial); + xdg_toplevel_resize(wl->xdg_toplevel, s->seat, serial, edges); + // Explicitly send an UP event after the client finishes a resize + mp_input_put_key(wl->vo->input_ctx, button | MP_KEY_STATE_UP); + } else if (state == MP_KEY_STATE_DOWN) { + // Save the serial and seat for voctrl-initialized dragging requests. + s->pointer_button_serial = serial; + wl->last_button_seat = s; + } else { + wl->last_button_seat = NULL; } } static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { - struct vo_wayland_state *wl = data; - if (wl_fixed_to_double(value) == 0) - return; - double val = wl_fixed_to_double(value)/abs(wl_fixed_to_double(value)); + struct vo_wayland_seat *s = data; switch (axis) { case WL_POINTER_AXIS_VERTICAL_SCROLL: - if (value > 0) - mp_input_put_wheel(wl->vo->input_ctx, MP_WHEEL_DOWN, +val); - if (value < 0) - mp_input_put_wheel(wl->vo->input_ctx, MP_WHEEL_UP, -val); + s->axis_value_vertical += wl_fixed_to_double(value); break; case WL_POINTER_AXIS_HORIZONTAL_SCROLL: - if (value > 0) - mp_input_put_wheel(wl->vo->input_ctx, MP_WHEEL_RIGHT, +val); - if (value < 0) - mp_input_put_wheel(wl->vo->input_ctx, MP_WHEEL_LEFT, -val); + s->axis_value_horizontal += wl_fixed_to_double(value); break; } } +static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) +{ + struct vo_wayland_seat *s = data; + struct vo_wayland_state *wl = s->wl; + double value_vertical, value_horizontal; + if (s->axis_value120_scroll) { + // Prefer axis_value120 if supported and the axis event is from mouse wheel. + value_vertical = s->axis_value120_vertical / 120.0; + value_horizontal = s->axis_value120_horizontal / 120.0; + } else { + // The axis value is specified in logical coordinates, but the exact value emitted + // by one mouse wheel click is unspecified. In practice, most compositors use either + // 10 (GNOME, Weston) or 15 (wlroots, same as libinput) as the value. + // Divide the value by 10 and clamp it between -1 and 1 so that mouse wheel clicks + // work as intended on all compositors while still allowing high resolution trackpads. + value_vertical = MPCLAMP(s->axis_value_vertical / 10.0, -1, 1); + value_horizontal = MPCLAMP(s->axis_value_horizontal / 10.0, -1, 1); + } + + if (value_vertical > 0) + mp_input_put_wheel(wl->vo->input_ctx, MP_WHEEL_DOWN | s->mpmod, +value_vertical); + if (value_vertical < 0) + mp_input_put_wheel(wl->vo->input_ctx, MP_WHEEL_UP | s->mpmod, -value_vertical); + if (value_horizontal > 0) + mp_input_put_wheel(wl->vo->input_ctx, MP_WHEEL_RIGHT | s->mpmod, +value_horizontal); + if (value_horizontal < 0) + mp_input_put_wheel(wl->vo->input_ctx, MP_WHEEL_LEFT | s->mpmod, -value_horizontal); + + s->axis_value120_scroll = false; + s->axis_value_vertical = 0; + s->axis_value_horizontal = 0; + s->axis_value120_vertical = 0; + s->axis_value120_horizontal = 0; +} + +static void pointer_handle_axis_source(void *data, struct wl_pointer *wl_pointer, + uint32_t axis_source) +{ +} + +static void pointer_handle_axis_stop(void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis) +{ +} + +static void pointer_handle_axis_discrete(void *data, struct wl_pointer *wl_pointer, + uint32_t axis, int32_t discrete) +{ +} + +#ifdef HAVE_WAYLAND_1_21 +static void pointer_handle_axis_value120(void *data, struct wl_pointer *wl_pointer, + uint32_t axis, int32_t value120) +{ + struct vo_wayland_seat *s = data; + s->axis_value120_scroll = true; + switch (axis) { + case WL_POINTER_AXIS_VERTICAL_SCROLL: + s->axis_value120_vertical += value120; + break; + case WL_POINTER_AXIS_HORIZONTAL_SCROLL: + s->axis_value120_horizontal += value120; + break; + } +} +#endif + static const struct wl_pointer_listener pointer_listener = { pointer_handle_enter, pointer_handle_leave, pointer_handle_motion, pointer_handle_button, pointer_handle_axis, + pointer_handle_frame, + pointer_handle_axis_source, + pointer_handle_axis_stop, + pointer_handle_axis_discrete, +#ifdef HAVE_WAYLAND_1_21 + pointer_handle_axis_value120, +#endif }; static void 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) { - struct vo_wayland_state *wl = data; - - enum xdg_toplevel_resize_edge edge; - if (check_for_resize(wl, x_w, y_w, wl->opts->edge_pixels_touch, &edge)) { - wl->touch_entries = 0; - xdg_toplevel_resize(wl->xdg_toplevel, wl->seat, serial, edge); - return; - } else if (wl->touch_entries) { - wl->touch_entries = 0; - xdg_toplevel_move(wl->xdg_toplevel, wl->seat, serial); - return; - } - - wl->touch_entries = 1; - + struct vo_wayland_seat *s = data; + struct vo_wayland_state *wl = s->wl; wl->mouse_x = wl_fixed_to_int(x_w) * wl->scaling; wl->mouse_y = wl_fixed_to_int(y_w) * wl->scaling; mp_input_set_mouse_pos(wl->vo->input_ctx, wl->mouse_x, wl->mouse_y); mp_input_put_key(wl->vo->input_ctx, MP_MBTN_LEFT | MP_KEY_STATE_DOWN); + + enum xdg_toplevel_resize_edge edge; + if (!mp_input_test_dragging(wl->vo->input_ctx, wl->mouse_x, wl->mouse_y) && + !wl->locked_size && check_for_resize(wl, wl->opts->edge_pixels_touch, &edge)) + { + xdg_toplevel_resize(wl->xdg_toplevel, s->seat, serial, edge); + // Explicitly send an UP event after the client finishes a resize + mp_input_put_key(wl->vo->input_ctx, MP_MBTN_LEFT | MP_KEY_STATE_UP); + } else { + // Save the serial and seat for voctrl-initialized dragging requests. + s->pointer_button_serial = serial; + wl->last_button_seat = s; + } } static void touch_handle_up(void *data, struct wl_touch *wl_touch, uint32_t serial, uint32_t time, int32_t id) { - struct vo_wayland_state *wl = data; - - wl->touch_entries = 0; - + struct vo_wayland_seat *s = data; + struct vo_wayland_state *wl = s->wl; mp_input_put_key(wl->vo->input_ctx, MP_MBTN_LEFT | MP_KEY_STATE_UP); + wl->last_button_seat = NULL; } static void 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) { - struct vo_wayland_state *wl = data; + struct vo_wayland_seat *s = data; + struct vo_wayland_state *wl = s->wl; wl->mouse_x = wl_fixed_to_int(x_w) * wl->scaling; wl->mouse_y = wl_fixed_to_int(y_w) * wl->scaling; @@ -331,80 +489,31 @@ static void touch_handle_cancel(void *data, struct wl_touch *wl_touch) { } +static void touch_handle_shape(void *data, struct wl_touch *wl_touch, + int32_t id, wl_fixed_t major, wl_fixed_t minor) +{ +} + +static void touch_handle_orientation(void *data, struct wl_touch *wl_touch, + int32_t id, wl_fixed_t orientation) +{ +} + static const struct wl_touch_listener touch_listener = { touch_handle_down, touch_handle_up, touch_handle_motion, touch_handle_frame, touch_handle_cancel, -}; - -static const struct mp_keymap keymap[] = { - /* Special keys */ - {XKB_KEY_Pause, MP_KEY_PAUSE}, {XKB_KEY_Escape, MP_KEY_ESC}, - {XKB_KEY_BackSpace, MP_KEY_BS}, {XKB_KEY_Tab, MP_KEY_TAB}, - {XKB_KEY_Return, MP_KEY_ENTER}, {XKB_KEY_Menu, MP_KEY_MENU}, - {XKB_KEY_Print, MP_KEY_PRINT}, - - /* Cursor keys */ - {XKB_KEY_Left, MP_KEY_LEFT}, {XKB_KEY_Right, MP_KEY_RIGHT}, - {XKB_KEY_Up, MP_KEY_UP}, {XKB_KEY_Down, MP_KEY_DOWN}, - - /* Navigation keys */ - {XKB_KEY_Insert, MP_KEY_INSERT}, {XKB_KEY_Delete, MP_KEY_DELETE}, - {XKB_KEY_Home, MP_KEY_HOME}, {XKB_KEY_End, MP_KEY_END}, - {XKB_KEY_Page_Up, MP_KEY_PAGE_UP}, {XKB_KEY_Page_Down, MP_KEY_PAGE_DOWN}, - - /* F-keys */ - {XKB_KEY_F1, MP_KEY_F + 1}, {XKB_KEY_F2, MP_KEY_F + 2}, - {XKB_KEY_F3, MP_KEY_F + 3}, {XKB_KEY_F4, MP_KEY_F + 4}, - {XKB_KEY_F5, MP_KEY_F + 5}, {XKB_KEY_F6, MP_KEY_F + 6}, - {XKB_KEY_F7, MP_KEY_F + 7}, {XKB_KEY_F8, MP_KEY_F + 8}, - {XKB_KEY_F9, MP_KEY_F + 9}, {XKB_KEY_F10, MP_KEY_F +10}, - {XKB_KEY_F11, MP_KEY_F +11}, {XKB_KEY_F12, MP_KEY_F +12}, - - /* Numpad independent of numlock */ - {XKB_KEY_KP_Subtract, '-'}, {XKB_KEY_KP_Add, '+'}, - {XKB_KEY_KP_Multiply, '*'}, {XKB_KEY_KP_Divide, '/'}, - {XKB_KEY_KP_Enter, MP_KEY_KPENTER}, - - /* Numpad with numlock */ - {XKB_KEY_KP_0, MP_KEY_KP0}, {XKB_KEY_KP_1, MP_KEY_KP1}, - {XKB_KEY_KP_2, MP_KEY_KP2}, {XKB_KEY_KP_3, MP_KEY_KP3}, - {XKB_KEY_KP_4, MP_KEY_KP4}, {XKB_KEY_KP_5, MP_KEY_KP5}, - {XKB_KEY_KP_6, MP_KEY_KP6}, {XKB_KEY_KP_7, MP_KEY_KP7}, - {XKB_KEY_KP_8, MP_KEY_KP8}, {XKB_KEY_KP_9, MP_KEY_KP9}, - {XKB_KEY_KP_Decimal, MP_KEY_KPDEC}, {XKB_KEY_KP_Separator, MP_KEY_KPDEC}, - - /* Numpad without numlock */ - {XKB_KEY_KP_Insert, MP_KEY_KPINS}, {XKB_KEY_KP_End, MP_KEY_KP1}, - {XKB_KEY_KP_Down, MP_KEY_KP2}, {XKB_KEY_KP_Page_Down, MP_KEY_KP3}, - {XKB_KEY_KP_Left, MP_KEY_KP4}, {XKB_KEY_KP_Begin, MP_KEY_KP5}, - {XKB_KEY_KP_Right, MP_KEY_KP6}, {XKB_KEY_KP_Home, MP_KEY_KP7}, - {XKB_KEY_KP_Up, MP_KEY_KP8}, {XKB_KEY_KP_Page_Up, MP_KEY_KP9}, - {XKB_KEY_KP_Delete, MP_KEY_KPDEL}, - - /* Multimedia keys */ - {XKB_KEY_XF86MenuKB, MP_KEY_MENU}, - {XKB_KEY_XF86AudioPlay, MP_KEY_PLAY}, {XKB_KEY_XF86AudioPause, MP_KEY_PAUSE}, - {XKB_KEY_XF86AudioStop, MP_KEY_STOP}, - {XKB_KEY_XF86AudioPrev, MP_KEY_PREV}, {XKB_KEY_XF86AudioNext, MP_KEY_NEXT}, - {XKB_KEY_XF86AudioRewind, MP_KEY_REWIND}, - {XKB_KEY_XF86AudioForward, MP_KEY_FORWARD}, - {XKB_KEY_XF86AudioMute, MP_KEY_MUTE}, - {XKB_KEY_XF86AudioLowerVolume, MP_KEY_VOLUME_DOWN}, - {XKB_KEY_XF86AudioRaiseVolume, MP_KEY_VOLUME_UP}, - {XKB_KEY_XF86HomePage, MP_KEY_HOMEPAGE}, {XKB_KEY_XF86WWW, MP_KEY_WWW}, - {XKB_KEY_XF86Mail, MP_KEY_MAIL}, {XKB_KEY_XF86Favorites, MP_KEY_FAVORITES}, - {XKB_KEY_XF86Search, MP_KEY_SEARCH}, {XKB_KEY_XF86Sleep, MP_KEY_SLEEP}, - - {0, 0} + touch_handle_shape, + touch_handle_orientation, }; static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size) { - struct vo_wayland_state *wl = data; + struct vo_wayland_seat *s = data; + struct vo_wayland_state *wl = s->wl; char *map_str; if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { @@ -412,28 +521,31 @@ static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard, return; } - map_str = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + map_str = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); if (map_str == MAP_FAILED) { close(fd); return; } - wl->xkb_keymap = xkb_keymap_new_from_string(wl->xkb_context, map_str, - XKB_KEYMAP_FORMAT_TEXT_V1, 0); + if (!s->xkb_keymap) + s->xkb_keymap = xkb_keymap_new_from_buffer(wl->xkb_context, map_str, + strnlen(map_str, size), + XKB_KEYMAP_FORMAT_TEXT_V1, 0); munmap(map_str, size); close(fd); - if (!wl->xkb_keymap) { + if (!s->xkb_keymap) { MP_ERR(wl, "failed to compile keymap\n"); return; } - wl->xkb_state = xkb_state_new(wl->xkb_keymap); - if (!wl->xkb_state) { + if (!s->xkb_state) + s->xkb_state = xkb_state_new(s->xkb_keymap); + if (!s->xkb_state) { MP_ERR(wl, "failed to create XKB state\n"); - xkb_keymap_unref(wl->xkb_keymap); - wl->xkb_keymap = NULL; + xkb_keymap_unref(s->xkb_keymap); + s->xkb_keymap = NULL; return; } } @@ -442,85 +554,60 @@ static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { + struct vo_wayland_seat *s = data; + struct vo_wayland_state *wl = s->wl; + s->has_keyboard_input = true; + guess_focus(wl); } static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface) { -} - -static bool create_input(struct vo_wayland_state *wl) -{ - wl->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - - if (!wl->xkb_context) { - MP_ERR(wl, "failed to initialize input: check xkbcommon\n"); - return 1; - } - - return 0; -} - -static int lookupkey(int key) -{ - const char *passthrough_keys = " -+*/<>`~!@#$%^&()_{}:;\"\',.?\\|=[]"; - - int mpkey = 0; - if ((key >= 'a' && key <= 'z') || (key >= 'A' && key <= 'Z') || - (key >= '0' && key <= '9') || - (key > 0 && key < 256 && strchr(passthrough_keys, key))) - mpkey = key; - - if (!mpkey) - mpkey = lookup_keymap_table(keymap, key); - - return mpkey; + struct vo_wayland_seat *s = data; + struct vo_wayland_state *wl = s->wl; + s->has_keyboard_input = false; + s->keyboard_code = 0; + s->mpkey = 0; + s->mpmod = 0; + mp_input_put_key(wl->vo->input_ctx, MP_INPUT_RELEASE_ALL); + guess_focus(wl); } static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { - struct vo_wayland_state *wl = data; - - uint32_t code = code = key + 8; - xkb_keysym_t sym = xkb_state_key_get_one_sym(wl->xkb_state, code); - - int mpmod = state == WL_KEYBOARD_KEY_STATE_PRESSED ? MP_KEY_STATE_DOWN - : MP_KEY_STATE_UP; - - static const char *mod_names[] = { - XKB_MOD_NAME_SHIFT, - XKB_MOD_NAME_CTRL, - XKB_MOD_NAME_ALT, - XKB_MOD_NAME_LOGO, - 0, - }; + struct vo_wayland_seat *s = data; + struct vo_wayland_state *wl = s->wl; - static int mods[] = { - MP_KEY_MODIFIER_SHIFT, - MP_KEY_MODIFIER_CTRL, - MP_KEY_MODIFIER_ALT, - MP_KEY_MODIFIER_META, - 0, - }; + s->keyboard_code = key + 8; + xkb_keysym_t sym = xkb_state_key_get_one_sym(s->xkb_state, s->keyboard_code); + int mpkey = lookupkey(sym); - for (int n = 0; mods[n]; n++) { - xkb_mod_index_t index = xkb_keymap_mod_get_index(wl->xkb_keymap, mod_names[n]); - if (!xkb_state_mod_index_is_consumed(wl->xkb_state, code, index) - && xkb_state_mod_index_is_active(wl->xkb_state, index, - XKB_STATE_MODS_DEPRESSED)) - mpmod |= mods[n]; - } + state = state == WL_KEYBOARD_KEY_STATE_PRESSED ? MP_KEY_STATE_DOWN + : MP_KEY_STATE_UP; - int mpkey = lookupkey(sym); if (mpkey) { - mp_input_put_key(wl->vo->input_ctx, mpkey | mpmod); + mp_input_put_key(wl->vo->input_ctx, mpkey | state | s->mpmod); } else { - char s[128]; - if (xkb_keysym_to_utf8(sym, s, sizeof(s)) > 0) - mp_input_put_key_utf8(wl->vo->input_ctx, mpmod, bstr0(s)); + char str[128]; + if (xkb_keysym_to_utf8(sym, str, sizeof(str)) > 0) { + mp_input_put_key_utf8(wl->vo->input_ctx, state | s->mpmod, bstr0(str)); + } else { + // Assume a modifier was pressed and handle it in the mod event instead. + // If a modifier is released before a regular key, also release that + // key to not activate it again by accident. + if (state == MP_KEY_STATE_UP) { + s->mpkey = 0; + mp_input_put_key(wl->vo->input_ctx, MP_INPUT_RELEASE_ALL); + } + return; + } } + if (state == MP_KEY_STATE_DOWN) + s->mpkey = mpkey; + if (mpkey && state == MP_KEY_STATE_UP) + s->mpkey = 0; } static void keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboard, @@ -528,16 +615,23 @@ static void keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboar uint32_t mods_latched, uint32_t mods_locked, uint32_t group) { - struct vo_wayland_state *wl = data; + struct vo_wayland_seat *s = data; + struct vo_wayland_state *wl = s->wl; - xkb_state_update_mask(wl->xkb_state, mods_depressed, mods_latched, - mods_locked, 0, 0, group); + if (s->xkb_state) { + xkb_state_update_mask(s->xkb_state, mods_depressed, mods_latched, + mods_locked, 0, 0, group); + s->mpmod = get_mods(s); + if (s->mpkey) + mp_input_put_key(wl->vo->input_ctx, s->mpkey | MP_KEY_STATE_DOWN | s->mpmod); + } } static void keyboard_handle_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay) { - struct vo_wayland_state *wl = data; + struct vo_wayland_seat *s = data; + struct vo_wayland_state *wl = s->wl; if (wl->vo_opts->native_keyrepeat) mp_input_set_repeat_info(wl->vo->input_ctx, rate, delay); } @@ -554,111 +648,55 @@ static const struct wl_keyboard_listener keyboard_listener = { static void seat_handle_caps(void *data, struct wl_seat *seat, enum wl_seat_capability caps) { - struct vo_wayland_state *wl = data; + struct vo_wayland_seat *s = data; - if ((caps & WL_SEAT_CAPABILITY_POINTER) && !wl->pointer) { - wl->pointer = wl_seat_get_pointer(seat); - wl_pointer_add_listener(wl->pointer, |