summaryrefslogtreecommitdiffstats
path: root/video/out/wayland_common.c
diff options
context:
space:
mode:
Diffstat (limited to 'video/out/wayland_common.c')
-rw-r--r--video/out/wayland_common.c3256
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,