Merge pull request #1368 from ascent12/x11_backend
X11 backend improvements
This commit is contained in:
commit
df7d4a71fb
|
@ -62,12 +62,12 @@ If you choose to enable X11 support:
|
|||
* xcb
|
||||
* xcb-composite
|
||||
* xcb-xfixes
|
||||
* xcb-xinput
|
||||
* xcb-image
|
||||
* xcb-render
|
||||
* x11-xcb
|
||||
* xcb-errors (optional, for improved error reporting)
|
||||
* x11-icccm (optional, for improved Xwayland introspection)
|
||||
* xcb-xkb (optional, for improved keyboard handling on the X11 backend)
|
||||
|
||||
Run these commands:
|
||||
|
||||
|
|
|
@ -1,25 +1,29 @@
|
|||
#define _POSIX_C_SOURCE 200112L
|
||||
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <wlr/config.h>
|
||||
|
||||
#include <X11/Xlib-xcb.h>
|
||||
#include <wayland-server.h>
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xfixes.h>
|
||||
#include <xcb/xinput.h>
|
||||
|
||||
#include <wlr/backend/interface.h>
|
||||
#include <wlr/backend/x11.h>
|
||||
#include <wlr/config.h>
|
||||
#include <wlr/interfaces/wlr_input_device.h>
|
||||
#include <wlr/interfaces/wlr_keyboard.h>
|
||||
#include <wlr/interfaces/wlr_pointer.h>
|
||||
#include <wlr/render/egl.h>
|
||||
#include <wlr/render/gles2.h>
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include <X11/Xlib-xcb.h>
|
||||
#include <xcb/xcb.h>
|
||||
#if WLR_HAS_XCB_XKB
|
||||
#include <xcb/xkb.h>
|
||||
#endif
|
||||
|
||||
#include "backend/x11.h"
|
||||
#include "util/signal.h"
|
||||
|
||||
|
@ -36,8 +40,6 @@ struct wlr_x11_output *get_x11_output_from_window_id(
|
|||
|
||||
static void handle_x11_event(struct wlr_x11_backend *x11,
|
||||
xcb_generic_event_t *event) {
|
||||
handle_x11_input_event(x11, event);
|
||||
|
||||
switch (event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK) {
|
||||
case XCB_EXPOSE: {
|
||||
xcb_expose_event_t *ev = (xcb_expose_event_t *)event;
|
||||
|
@ -69,6 +71,12 @@ static void handle_x11_event(struct wlr_x11_backend *x11,
|
|||
}
|
||||
break;
|
||||
}
|
||||
case XCB_GE_GENERIC: {
|
||||
xcb_ge_generic_event_t *ev = (xcb_ge_generic_event_t *)event;
|
||||
if (ev->extension == x11->xinput_opcode) {
|
||||
handle_x11_xinput_event(x11, ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -81,7 +89,7 @@ static int x11_event(int fd, uint32_t mask, void *data) {
|
|||
}
|
||||
|
||||
xcb_generic_event_t *e;
|
||||
while ((e = xcb_poll_for_event(x11->xcb_conn))) {
|
||||
while ((e = xcb_poll_for_event(x11->xcb))) {
|
||||
handle_x11_event(x11, e);
|
||||
free(e);
|
||||
}
|
||||
|
@ -99,83 +107,6 @@ static bool backend_start(struct wlr_backend *backend) {
|
|||
struct wlr_x11_backend *x11 = get_x11_backend_from_backend(backend);
|
||||
x11->started = true;
|
||||
|
||||
struct {
|
||||
const char *name;
|
||||
xcb_intern_atom_cookie_t cookie;
|
||||
xcb_atom_t *atom;
|
||||
} atom[] = {
|
||||
{
|
||||
.name = "WM_PROTOCOLS",
|
||||
.atom = &x11->atoms.wm_protocols,
|
||||
},
|
||||
{
|
||||
.name = "WM_DELETE_WINDOW",
|
||||
.atom = &x11->atoms.wm_delete_window,
|
||||
},
|
||||
{
|
||||
.name = "_NET_WM_NAME",
|
||||
.atom = &x11->atoms.net_wm_name,
|
||||
},
|
||||
{
|
||||
.name = "UTF8_STRING",
|
||||
.atom = &x11->atoms.utf8_string,
|
||||
},
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < sizeof(atom) / sizeof(atom[0]); ++i) {
|
||||
atom[i].cookie = xcb_intern_atom(x11->xcb_conn,
|
||||
true, strlen(atom[i].name), atom[i].name);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < sizeof(atom) / sizeof(atom[0]); ++i) {
|
||||
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(
|
||||
x11->xcb_conn, atom[i].cookie, NULL);
|
||||
|
||||
if (reply) {
|
||||
*atom[i].atom = reply->atom;
|
||||
free(reply);
|
||||
} else {
|
||||
*atom[i].atom = XCB_ATOM_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
// create a blank cursor
|
||||
xcb_pixmap_t pix = xcb_generate_id(x11->xcb_conn);
|
||||
xcb_create_pixmap(x11->xcb_conn, 1, pix, x11->screen->root, 1, 1);
|
||||
|
||||
x11->cursor = xcb_generate_id(x11->xcb_conn);
|
||||
xcb_create_cursor(x11->xcb_conn, x11->cursor, pix, pix, 0, 0, 0, 0, 0, 0,
|
||||
0, 0);
|
||||
xcb_free_pixmap(x11->xcb_conn, pix);
|
||||
|
||||
#if WLR_HAS_XCB_XKB
|
||||
const xcb_query_extension_reply_t *reply =
|
||||
xcb_get_extension_data(x11->xcb_conn, &xcb_xkb_id);
|
||||
if (reply != NULL && reply->present) {
|
||||
x11->xkb_base_event = reply->first_event;
|
||||
x11->xkb_base_error = reply->first_error;
|
||||
|
||||
xcb_xkb_use_extension_cookie_t cookie = xcb_xkb_use_extension(
|
||||
x11->xcb_conn, XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION);
|
||||
xcb_xkb_use_extension_reply_t *reply =
|
||||
xcb_xkb_use_extension_reply(x11->xcb_conn, cookie, NULL);
|
||||
if (reply != NULL && reply->supported) {
|
||||
x11->xkb_supported = true;
|
||||
|
||||
xcb_xkb_select_events(x11->xcb_conn,
|
||||
XCB_XKB_ID_USE_CORE_KBD,
|
||||
XCB_XKB_EVENT_TYPE_STATE_NOTIFY,
|
||||
0,
|
||||
XCB_XKB_EVENT_TYPE_STATE_NOTIFY,
|
||||
0,
|
||||
0,
|
||||
0);
|
||||
|
||||
free(reply);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
wlr_signal_emit_safe(&x11->backend.events.new_input, &x11->keyboard_dev);
|
||||
|
||||
for (size_t i = 0; i < x11->requested_outputs; ++i) {
|
||||
|
@ -209,9 +140,6 @@ static void backend_destroy(struct wlr_backend *backend) {
|
|||
wlr_renderer_destroy(x11->renderer);
|
||||
wlr_egl_finish(&x11->egl);
|
||||
|
||||
if (x11->cursor) {
|
||||
xcb_free_cursor(x11->xcb_conn, x11->cursor);
|
||||
}
|
||||
if (x11->xlib_conn) {
|
||||
XCloseDisplay(x11->xlib_conn);
|
||||
}
|
||||
|
@ -258,15 +186,82 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
|
|||
goto error_x11;
|
||||
}
|
||||
|
||||
x11->xcb_conn = XGetXCBConnection(x11->xlib_conn);
|
||||
if (!x11->xcb_conn || xcb_connection_has_error(x11->xcb_conn)) {
|
||||
x11->xcb = XGetXCBConnection(x11->xlib_conn);
|
||||
if (!x11->xcb || xcb_connection_has_error(x11->xcb)) {
|
||||
wlr_log(WLR_ERROR, "Failed to open xcb connection");
|
||||
goto error_display;
|
||||
}
|
||||
|
||||
XSetEventQueueOwner(x11->xlib_conn, XCBOwnsEventQueue);
|
||||
|
||||
int fd = xcb_get_file_descriptor(x11->xcb_conn);
|
||||
struct {
|
||||
const char *name;
|
||||
xcb_intern_atom_cookie_t cookie;
|
||||
xcb_atom_t *atom;
|
||||
} atom[] = {
|
||||
{ .name = "WM_PROTOCOLS", .atom = &x11->atoms.wm_protocols },
|
||||
{ .name = "WM_DELETE_WINDOW", .atom = &x11->atoms.wm_delete_window },
|
||||
{ .name = "_NET_WM_NAME", .atom = &x11->atoms.net_wm_name },
|
||||
{ .name = "UTF8_STRING", .atom = &x11->atoms.utf8_string },
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < sizeof(atom) / sizeof(atom[0]); ++i) {
|
||||
atom[i].cookie = xcb_intern_atom(x11->xcb,
|
||||
true, strlen(atom[i].name), atom[i].name);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < sizeof(atom) / sizeof(atom[0]); ++i) {
|
||||
xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(
|
||||
x11->xcb, atom[i].cookie, NULL);
|
||||
|
||||
if (reply) {
|
||||
*atom[i].atom = reply->atom;
|
||||
free(reply);
|
||||
} else {
|
||||
*atom[i].atom = XCB_ATOM_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
const xcb_query_extension_reply_t *ext;
|
||||
|
||||
ext = xcb_get_extension_data(x11->xcb, &xcb_xfixes_id);
|
||||
if (!ext || !ext->present) {
|
||||
wlr_log(WLR_ERROR, "X11 does not support Xfixes extension");
|
||||
goto error_display;
|
||||
}
|
||||
|
||||
xcb_xfixes_query_version_cookie_t fixes_cookie =
|
||||
xcb_xfixes_query_version(x11->xcb, 4, 0);
|
||||
xcb_xfixes_query_version_reply_t *fixes_reply =
|
||||
xcb_xfixes_query_version_reply(x11->xcb, fixes_cookie, NULL);
|
||||
|
||||
if (!fixes_reply || fixes_reply->major_version < 4) {
|
||||
wlr_log(WLR_ERROR, "X11 does not support required Xfixes version");
|
||||
free(fixes_reply);
|
||||
goto error_display;
|
||||
}
|
||||
free(fixes_reply);
|
||||
|
||||
ext = xcb_get_extension_data(x11->xcb, &xcb_input_id);
|
||||
if (!ext || !ext->present) {
|
||||
wlr_log(WLR_ERROR, "X11 does not support Xinput extension");
|
||||
goto error_display;
|
||||
}
|
||||
x11->xinput_opcode = ext->major_opcode;
|
||||
|
||||
xcb_input_xi_query_version_cookie_t xi_cookie =
|
||||
xcb_input_xi_query_version(x11->xcb, 2, 0);
|
||||
xcb_input_xi_query_version_reply_t *xi_reply =
|
||||
xcb_input_xi_query_version_reply(x11->xcb, xi_cookie, NULL);
|
||||
|
||||
if (!xi_reply || xi_reply->major_version < 2) {
|
||||
wlr_log(WLR_ERROR, "X11 does not support required Xinput version");
|
||||
free(xi_reply);
|
||||
goto error_display;
|
||||
}
|
||||
free(xi_reply);
|
||||
|
||||
int fd = xcb_get_file_descriptor(x11->xcb);
|
||||
struct wl_event_loop *ev = wl_display_get_event_loop(display);
|
||||
uint32_t events = WL_EVENT_READABLE | WL_EVENT_ERROR | WL_EVENT_HANGUP;
|
||||
x11->event_source = wl_event_loop_add_fd(ev, fd, events, x11_event, x11);
|
||||
|
@ -276,7 +271,7 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
|
|||
}
|
||||
wl_event_source_check(x11->event_source);
|
||||
|
||||
x11->screen = xcb_setup_roots_iterator(xcb_get_setup(x11->xcb_conn)).data;
|
||||
x11->screen = xcb_setup_roots_iterator(xcb_get_setup(x11->xcb)).data;
|
||||
|
||||
if (!create_renderer_func) {
|
||||
create_renderer_func = wlr_renderer_autocreate;
|
||||
|
|
|
@ -1,140 +1,201 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include <wlr/config.h>
|
||||
#include <wlr/interfaces/wlr_input_device.h>
|
||||
#include <wlr/interfaces/wlr_keyboard.h>
|
||||
#include <wlr/interfaces/wlr_pointer.h>
|
||||
#include <wlr/util/log.h>
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <linux/input-event-codes.h>
|
||||
#elif __FreeBSD__
|
||||
#include <dev/evdev/input-event-codes.h>
|
||||
#endif
|
||||
#if WLR_HAS_XCB_XKB
|
||||
#include <xcb/xkb.h>
|
||||
#endif
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xfixes.h>
|
||||
#include <xcb/xinput.h>
|
||||
|
||||
#include <wlr/interfaces/wlr_input_device.h>
|
||||
#include <wlr/interfaces/wlr_keyboard.h>
|
||||
#include <wlr/interfaces/wlr_pointer.h>
|
||||
#include <wlr/util/log.h>
|
||||
|
||||
#include "backend/x11.h"
|
||||
#include "util/signal.h"
|
||||
|
||||
static uint32_t xcb_button_to_wl(uint32_t button) {
|
||||
switch (button) {
|
||||
case XCB_BUTTON_INDEX_1: return BTN_LEFT;
|
||||
case XCB_BUTTON_INDEX_2: return BTN_MIDDLE;
|
||||
case XCB_BUTTON_INDEX_3: return BTN_RIGHT;
|
||||
// XXX: I'm not sure the scroll-wheel direction is right
|
||||
case XCB_BUTTON_INDEX_4: return BTN_GEAR_UP;
|
||||
case XCB_BUTTON_INDEX_5: return BTN_GEAR_DOWN;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void x11_handle_pointer_position(struct wlr_x11_output *output,
|
||||
int16_t x, int16_t y, xcb_timestamp_t time) {
|
||||
struct wlr_x11_backend *x11 = output->x11;
|
||||
struct wlr_output *wlr_output = &output->wlr_output;
|
||||
struct wlr_event_pointer_motion_absolute event = {
|
||||
.device = &output->pointer_dev,
|
||||
static void send_key_event(struct wlr_x11_backend *x11, uint32_t key,
|
||||
enum wlr_key_state st, xcb_timestamp_t time) {
|
||||
struct wlr_event_keyboard_key ev = {
|
||||
.time_msec = time,
|
||||
.x = (double)x / wlr_output->width,
|
||||
.y = (double)y / wlr_output->height,
|
||||
};
|
||||
wlr_signal_emit_safe(&output->pointer.events.motion_absolute, &event);
|
||||
|
||||
x11->time = time;
|
||||
}
|
||||
|
||||
void handle_x11_input_event(struct wlr_x11_backend *x11,
|
||||
xcb_generic_event_t *event) {
|
||||
switch (event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK) {
|
||||
case XCB_KEY_PRESS:
|
||||
case XCB_KEY_RELEASE: {
|
||||
xcb_key_press_event_t *ev = (xcb_key_press_event_t *)event;
|
||||
struct wlr_event_keyboard_key key = {
|
||||
.time_msec = ev->time,
|
||||
.keycode = ev->detail - 8,
|
||||
.state = event->response_type == XCB_KEY_PRESS ?
|
||||
WLR_KEY_PRESSED : WLR_KEY_RELEASED,
|
||||
.keycode = key,
|
||||
.state = st,
|
||||
.update_state = true,
|
||||
};
|
||||
|
||||
// TODO use xcb-xkb for more precise modifiers state?
|
||||
wlr_keyboard_notify_key(&x11->keyboard, &key);
|
||||
x11->time = ev->time;
|
||||
return;
|
||||
}
|
||||
case XCB_BUTTON_PRESS: {
|
||||
xcb_button_press_event_t *ev = (xcb_button_press_event_t *)event;
|
||||
|
||||
struct wlr_x11_output *output =
|
||||
get_x11_output_from_window_id(x11, ev->event);
|
||||
if (output == NULL) {
|
||||
break;
|
||||
wlr_keyboard_notify_key(&x11->keyboard, &ev);
|
||||
}
|
||||
|
||||
if (ev->detail == XCB_BUTTON_INDEX_4 ||
|
||||
ev->detail == XCB_BUTTON_INDEX_5) {
|
||||
int32_t delta_discrete = ev->detail == XCB_BUTTON_INDEX_4 ? -1 : 1;
|
||||
struct wlr_event_pointer_axis axis = {
|
||||
static void send_button_event(struct wlr_x11_output *output, uint32_t key,
|
||||
enum wlr_button_state st, xcb_timestamp_t time) {
|
||||
struct wlr_event_pointer_button ev = {
|
||||
.device = &output->pointer_dev,
|
||||
.time_msec = ev->time,
|
||||
.time_msec = time,
|
||||
.button = key,
|
||||
.state = st,
|
||||
};
|
||||
wlr_signal_emit_safe(&output->pointer.events.button, &ev);
|
||||
}
|
||||
|
||||
static void send_axis_event(struct wlr_x11_output *output, int32_t delta,
|
||||
xcb_timestamp_t time) {
|
||||
struct wlr_event_pointer_axis ev = {
|
||||
.device = &output->pointer_dev,
|
||||
.time_msec = time,
|
||||
.source = WLR_AXIS_SOURCE_WHEEL,
|
||||
.orientation = WLR_AXIS_ORIENTATION_VERTICAL,
|
||||
// 15 is a typical value libinput sends for one scroll
|
||||
.delta = delta_discrete * 15,
|
||||
.delta_discrete = delta_discrete,
|
||||
.delta = delta * 15,
|
||||
.delta_discrete = delta,
|
||||
};
|
||||
wlr_signal_emit_safe(&output->pointer.events.axis, &axis);
|
||||
x11->time = ev->time;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* fallthrough */
|
||||
case XCB_BUTTON_RELEASE: {
|
||||
xcb_button_press_event_t *ev = (xcb_button_press_event_t *)event;
|
||||
|
||||
struct wlr_x11_output *output =
|
||||
get_x11_output_from_window_id(x11, ev->event);
|
||||
if (output == NULL) {
|
||||
break;
|
||||
wlr_signal_emit_safe(&output->pointer.events.axis, &ev);
|
||||
}
|
||||
|
||||
if (ev->detail != XCB_BUTTON_INDEX_4 &&
|
||||
ev->detail != XCB_BUTTON_INDEX_5) {
|
||||
struct wlr_event_pointer_button button = {
|
||||
static void send_pointer_position_event(struct wlr_x11_output *output,
|
||||
int16_t x, int16_t y, xcb_timestamp_t time) {
|
||||
struct wlr_event_pointer_motion_absolute ev = {
|
||||
.device = &output->pointer_dev,
|
||||
.time_msec = ev->time,
|
||||
.button = xcb_button_to_wl(ev->detail),
|
||||
.state = event->response_type == XCB_BUTTON_PRESS ?
|
||||
WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED,
|
||||
.time_msec = time,
|
||||
.x = (double)x / output->wlr_output.width,
|
||||
.y = (double)y / output->wlr_output.height,
|
||||
};
|
||||
|
||||
wlr_signal_emit_safe(&output->pointer.events.button, &button);
|
||||
wlr_signal_emit_safe(&output->pointer.events.motion_absolute, &ev);
|
||||
}
|
||||
|
||||
void handle_x11_xinput_event(struct wlr_x11_backend *x11,
|
||||
xcb_ge_generic_event_t *event) {
|
||||
struct wlr_x11_output *output;
|
||||
|
||||
switch (event->event_type) {
|
||||
case XCB_INPUT_KEY_PRESS: {
|
||||
xcb_input_key_press_event_t *ev =
|
||||
(xcb_input_key_press_event_t *)event;
|
||||
|
||||
wlr_keyboard_notify_modifiers(&x11->keyboard, ev->mods.base,
|
||||
ev->mods.latched, ev->mods.locked, ev->mods.effective);
|
||||
send_key_event(x11, ev->detail - 8, WLR_KEY_PRESSED, ev->time);
|
||||
x11->time = ev->time;
|
||||
return;
|
||||
}
|
||||
case XCB_MOTION_NOTIFY: {
|
||||
xcb_motion_notify_event_t *ev = (xcb_motion_notify_event_t *)event;
|
||||
|
||||
struct wlr_x11_output *output =
|
||||
get_x11_output_from_window_id(x11, ev->event);
|
||||
if (output != NULL) {
|
||||
x11_handle_pointer_position(output, ev->event_x, ev->event_y, ev->time);
|
||||
}
|
||||
return;
|
||||
}
|
||||
default:
|
||||
#if WLR_HAS_XCB_XKB
|
||||
if (x11->xkb_supported && event->response_type == x11->xkb_base_event) {
|
||||
xcb_xkb_state_notify_event_t *ev =
|
||||
(xcb_xkb_state_notify_event_t *)event;
|
||||
wlr_keyboard_notify_modifiers(&x11->keyboard, ev->baseMods,
|
||||
ev->latchedMods, ev->lockedMods, ev->lockedGroup);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case XCB_INPUT_KEY_RELEASE: {
|
||||
xcb_input_key_release_event_t *ev =
|
||||
(xcb_input_key_release_event_t *)event;
|
||||
|
||||
wlr_keyboard_notify_modifiers(&x11->keyboard, ev->mods.base,
|
||||
ev->mods.latched, ev->mods.locked, ev->mods.effective);
|
||||
send_key_event(x11, ev->detail - 8, WLR_KEY_RELEASED, ev->time);
|
||||
x11->time = ev->time;
|
||||
break;
|
||||
}
|
||||
case XCB_INPUT_BUTTON_PRESS: {
|
||||
xcb_input_button_press_event_t *ev =
|
||||
(xcb_input_button_press_event_t *)event;
|
||||
|
||||
output = get_x11_output_from_window_id(x11, ev->event);
|
||||
if (!output) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (ev->detail) {
|
||||
case XCB_BUTTON_INDEX_1:
|
||||
send_button_event(output, BTN_LEFT, WLR_BUTTON_PRESSED,
|
||||
ev->time);
|
||||
break;
|
||||
case XCB_BUTTON_INDEX_2:
|
||||
send_button_event(output, BTN_MIDDLE, WLR_BUTTON_PRESSED,
|
||||
ev->time);
|
||||
break;
|
||||
case XCB_BUTTON_INDEX_3:
|
||||
send_button_event(output, BTN_RIGHT, WLR_BUTTON_PRESSED,
|
||||
ev->time);
|
||||
break;
|
||||
case XCB_BUTTON_INDEX_4:
|
||||
send_axis_event(output, -1, ev->time);
|
||||
break;
|
||||
case XCB_BUTTON_INDEX_5:
|
||||
send_axis_event(output, 1, ev->time);
|
||||
break;
|
||||
}
|
||||
|
||||
x11->time = ev->time;
|
||||
break;
|
||||
}
|
||||
case XCB_INPUT_BUTTON_RELEASE: {
|
||||
xcb_input_button_release_event_t *ev =
|
||||
(xcb_input_button_release_event_t *)event;
|
||||
|
||||
output = get_x11_output_from_window_id(x11, ev->event);
|
||||
if (!output) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (ev->detail) {
|
||||
case XCB_BUTTON_INDEX_1:
|
||||
send_button_event(output, BTN_LEFT, WLR_BUTTON_RELEASED,
|
||||
ev->time);
|
||||
break;
|
||||
case XCB_BUTTON_INDEX_2:
|
||||
send_button_event(output, BTN_MIDDLE, WLR_BUTTON_RELEASED,
|
||||
ev->time);
|
||||
break;
|
||||
case XCB_BUTTON_INDEX_3:
|
||||
send_button_event(output, BTN_RIGHT, WLR_BUTTON_RELEASED,
|
||||
ev->time);
|
||||
break;
|
||||
}
|
||||
|
||||
x11->time = ev->time;
|
||||
break;
|
||||
}
|
||||
case XCB_INPUT_MOTION: {
|
||||
xcb_input_motion_event_t *ev = (xcb_input_motion_event_t *)event;
|
||||
|
||||
output = get_x11_output_from_window_id(x11, ev->event);
|
||||
if (!output) {
|
||||
return;
|
||||
}
|
||||
|
||||
send_pointer_position_event(output, ev->event_x >> 16,
|
||||
ev->event_y >> 16, ev->time);
|
||||
x11->time = ev->time;
|
||||
break;
|
||||
}
|
||||
case XCB_INPUT_ENTER: {
|
||||
xcb_input_enter_event_t *ev = (xcb_input_enter_event_t *)event;
|
||||
|
||||
output = get_x11_output_from_window_id(x11, ev->event);
|
||||
if (!output) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!output->cursor_hidden) {
|
||||
xcb_xfixes_hide_cursor(x11->xcb, output->win);
|
||||
xcb_flush(x11->xcb);
|
||||
output->cursor_hidden = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case XCB_INPUT_LEAVE: {
|
||||
xcb_input_leave_event_t *ev = (xcb_input_leave_event_t *)event;
|
||||
|
||||
output = get_x11_output_from_window_id(x11, ev->event);
|
||||
if (!output) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (output->cursor_hidden) {
|
||||
xcb_xfixes_show_cursor(x11->xcb, output->win);
|
||||
xcb_flush(x11->xcb);
|
||||
output->cursor_hidden = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void input_device_destroy(struct wlr_input_device *wlr_device) {
|
||||
|
@ -166,14 +227,14 @@ void update_x11_pointer_position(struct wlr_x11_output *output,
|
|||
struct wlr_x11_backend *x11 = output->x11;
|
||||
|
||||
xcb_query_pointer_cookie_t cookie =
|
||||
xcb_query_pointer(x11->xcb_conn, output->win);
|
||||
xcb_query_pointer(x11->xcb, output->win);
|
||||
xcb_query_pointer_reply_t *reply =
|
||||
xcb_query_pointer_reply(x11->xcb_conn, cookie, NULL);
|
||||
xcb_query_pointer_reply(x11->xcb, cookie, NULL);
|
||||
if (!reply) {
|
||||
return;
|
||||
}
|
||||
|
||||
x11_handle_pointer_position(output, reply->win_x, reply->win_y, time);
|
||||
send_pointer_position_event(output, reply->win_x, reply->win_y, time);
|
||||
|
||||
free(reply);
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
x11_libs = []
|
||||
x11_required = [
|
||||
'xcb',
|
||||
'x11-xcb',
|
||||
]
|
||||
x11_optional = [
|
||||
'xcb-xkb',
|
||||
'xcb',
|
||||
'xcb-xinput',
|
||||
'xcb-xfixes',
|
||||
]
|
||||
|
||||
foreach lib : x11_required
|
||||
|
@ -16,14 +15,6 @@ foreach lib : x11_required
|
|||
x11_libs += dep
|
||||
endforeach
|
||||
|
||||
foreach lib : x11_optional
|
||||
dep = dependency(lib, required: get_option(lib))
|
||||
if dep.found()
|
||||
x11_libs += dep
|
||||
conf_data.set10('WLR_HAS_' + lib.underscorify().to_upper(), true)
|
||||
endif
|
||||
endforeach
|
||||
|
||||
lib_wlr_backend_x11 = static_library(
|
||||
'wlr_backend_x11',
|
||||
files(
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
#define _POSIX_C_SOURCE 200809L
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xinput.h>
|
||||
|
||||
#include <wlr/interfaces/wlr_output.h>
|
||||
#include <wlr/interfaces/wlr_pointer.h>
|
||||
#include <wlr/util/log.h>
|
||||
|
||||
#include "backend/x11.h"
|
||||
#include "util/signal.h"
|
||||
|
||||
|
@ -16,8 +22,8 @@ static int signal_frame(void *data) {
|
|||
}
|
||||
|
||||
static void parse_xcb_setup(struct wlr_output *output,
|
||||
xcb_connection_t *xcb_conn) {
|
||||
const xcb_setup_t *xcb_setup = xcb_get_setup(xcb_conn);
|
||||
xcb_connection_t *xcb) {
|
||||
const xcb_setup_t *xcb_setup = xcb_get_setup(xcb);
|
||||
|
||||
snprintf(output->make, sizeof(output->make), "%.*s",
|
||||
xcb_setup_vendor_length(xcb_setup),
|
||||
|
@ -55,11 +61,11 @@ static bool output_set_custom_mode(struct wlr_output *wlr_output,
|
|||
|
||||
const uint32_t values[] = { width, height };
|
||||
xcb_void_cookie_t cookie = xcb_configure_window_checked(
|
||||
x11->xcb_conn, output->win,
|
||||
x11->xcb, output->win,
|
||||
XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, values);
|
||||
|
||||
xcb_generic_error_t *error;
|
||||
if ((error = xcb_request_check(x11->xcb_conn, cookie))) {
|
||||
if ((error = xcb_request_check(x11->xcb, cookie))) {
|
||||
wlr_log(WLR_ERROR, "Could not set window size to %dx%d\n",
|
||||
width, height);
|
||||
free(error);
|
||||
|
@ -84,8 +90,8 @@ static void output_destroy(struct wlr_output *wlr_output) {
|
|||
wl_list_remove(&output->link);
|
||||
wl_event_source_remove(output->frame_timer);
|
||||
wlr_egl_destroy_surface(&x11->egl, output->surf);
|
||||
xcb_destroy_window(x11->xcb_conn, output->win);
|
||||
xcb_flush(x11->xcb_conn);
|
||||
xcb_destroy_window(x11->xcb, output->win);
|
||||
xcb_flush(x11->xcb);
|
||||
free(output);
|
||||
}
|
||||
|
||||
|
@ -142,21 +148,32 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
|
|||
|
||||
snprintf(wlr_output->name, sizeof(wlr_output->name), "X11-%d",
|
||||
wl_list_length(&x11->outputs) + 1);
|
||||
parse_xcb_setup(wlr_output, x11->xcb_conn);
|
||||
parse_xcb_setup(wlr_output, x11->xcb);
|
||||
|
||||
uint32_t mask = XCB_CW_EVENT_MASK;
|
||||
uint32_t values[] = {
|
||||
XCB_EVENT_MASK_EXPOSURE |
|
||||
XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE |
|
||||
XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE |
|
||||
XCB_EVENT_MASK_POINTER_MOTION |
|
||||
XCB_EVENT_MASK_STRUCTURE_NOTIFY
|
||||
XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY
|
||||
};
|
||||
output->win = xcb_generate_id(x11->xcb_conn);
|
||||
xcb_create_window(x11->xcb_conn, XCB_COPY_FROM_PARENT, output->win,
|
||||
output->win = xcb_generate_id(x11->xcb);
|
||||
xcb_create_window(x11->xcb, XCB_COPY_FROM_PARENT, output->win,
|
||||
x11->screen->root, 0, 0, wlr_output->width, wlr_output->height, 1,
|
||||
XCB_WINDOW_CLASS_INPUT_OUTPUT, x11->screen->root_visual, mask, values);
|
||||
|
||||
struct {
|
||||
xcb_input_event_mask_t head;
|
||||
xcb_input_xi_event_mask_t mask;
|
||||
} xinput_mask = {
|
||||
.head = { .deviceid = XCB_INPUT_DEVICE_ALL_MASTER, .mask_len = 1 },
|
||||
.mask = XCB_INPUT_XI_EVENT_MASK_KEY_PRESS |
|
||||
XCB_INPUT_XI_EVENT_MASK_KEY_RELEASE |
|
||||
XCB_INPUT_XI_EVENT_MASK_BUTTON_PRESS |
|
||||
XCB_INPUT_XI_EVENT_MASK_BUTTON_RELEASE |
|
||||
XCB_INPUT_XI_EVENT_MASK_MOTION |
|
||||
XCB_INPUT_XI_EVENT_MASK_ENTER |
|
||||
XCB_INPUT_XI_EVENT_MASK_LEAVE,
|
||||
};
|
||||
xcb_input_xi_select_events(x11->xcb, output->win, 1, &xinput_mask.head);
|
||||
|
||||
output->surf = wlr_egl_create_surface(&x11->egl, &output->win);
|
||||
if (!output->surf) {
|
||||
wlr_log(WLR_ERROR, "Failed to create EGL surface");
|
||||
|
@ -164,23 +181,19 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
xcb_change_property(x11->xcb_conn, XCB_PROP_MODE_REPLACE, output->win,
|
||||
xcb_change_property(x11->xcb, XCB_PROP_MODE_REPLACE, output->win,
|
||||
x11->atoms.wm_protocols, XCB_ATOM_ATOM, 32, 1,
|
||||
&x11->atoms.wm_delete_window);
|
||||
|
||||
char title[32];
|
||||
if (snprintf(title, sizeof(title), "wlroots - %s", wlr_output->name)) {
|
||||
xcb_change_property(x11->xcb_conn, XCB_PROP_MODE_REPLACE, output->win,
|
||||
xcb_change_property(x11->xcb, XCB_PROP_MODE_REPLACE, output->win,
|
||||
x11->atoms.net_wm_name, x11->atoms.utf8_string, 8,
|
||||
strlen(title), title);
|
||||
}
|
||||
|
||||
uint32_t cursor_values[] = { x11->cursor };
|
||||
xcb_change_window_attributes(x11->xcb_conn, output->win, XCB_CW_CURSOR,
|
||||
cursor_values);
|
||||
|
||||
xcb_map_window(x11->xcb_conn, output->win);
|
||||
xcb_flush(x11->xcb_conn);
|
||||
xcb_map_window(x11->xcb, output->win);
|
||||
xcb_flush(x11->xcb);
|
||||
|
||||
struct wl_event_loop *ev = wl_display_get_event_loop(x11->wl_display);
|
||||
output->frame_timer = wl_event_loop_add_timer(ev, signal_frame, output);
|
||||
|
|
|
@ -2,14 +2,17 @@
|
|||
#define BACKEND_X11_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <X11/Xlib-xcb.h>
|
||||
#include <wayland-server.h>
|
||||
#include <wlr/config.h>
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
#include <wlr/backend/x11.h>
|
||||
#include <wlr/config.h>
|
||||
#include <wlr/interfaces/wlr_input_device.h>
|
||||
#include <wlr/interfaces/wlr_output.h>
|
||||
#include <wlr/render/egl.h>
|
||||
#include <X11/Xlib-xcb.h>
|
||||
#include <xcb/xcb.h>
|
||||
#include <wlr/render/wlr_renderer.h>
|
||||
|
||||
#define XCB_EVENT_RESPONSE_TYPE_MASK 0x7f
|
||||
|
||||
|
@ -30,6 +33,8 @@ struct wlr_x11_output {
|
|||
|
||||
struct wl_event_source *frame_timer;
|
||||
int frame_delay;
|
||||
|
||||
bool cursor_hidden;
|
||||
};
|
||||
|
||||
struct wlr_x11_backend {
|
||||
|
@ -38,7 +43,7 @@ struct wlr_x11_backend {
|
|||
bool started;
|
||||
|
||||
Display *xlib_conn;
|
||||
xcb_connection_t *xcb_conn;
|
||||
xcb_connection_t *xcb;
|
||||
xcb_screen_t *screen;
|
||||
|
||||
size_t requested_outputs;
|
||||
|
@ -61,14 +66,7 @@ struct wlr_x11_backend {
|
|||
// The time we last received an event
|
||||
xcb_timestamp_t time;
|
||||
|
||||
// A blank cursor
|
||||
xcb_cursor_t cursor;
|
||||
|
||||
#if WLR_HAS_XCB_XKB
|
||||
bool xkb_supported;
|
||||
uint8_t xkb_base_event;
|
||||
uint8_t xkb_base_error;
|
||||
#endif
|
||||
uint8_t xinput_opcode;
|
||||
|
||||
struct wl_listener display_destroy;
|
||||
};
|
||||
|
@ -82,8 +80,8 @@ extern const struct wlr_keyboard_impl keyboard_impl;
|
|||
extern const struct wlr_pointer_impl pointer_impl;
|
||||
extern const struct wlr_input_device_impl input_device_impl;
|
||||
|
||||
void handle_x11_input_event(struct wlr_x11_backend *x11,
|
||||
xcb_generic_event_t *event);
|
||||
void handle_x11_xinput_event(struct wlr_x11_backend *x11,
|
||||
xcb_ge_generic_event_t *event);
|
||||
void update_x11_pointer_position(struct wlr_x11_output *output,
|
||||
xcb_timestamp_t time);
|
||||
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
#define WLR_BACKEND_X11_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <wayland-server.h>
|
||||
|
||||
#include <wlr/backend.h>
|
||||
#include <wlr/types/wlr_input_device.h>
|
||||
#include <wlr/types/wlr_output.h>
|
||||
|
|
|
@ -12,6 +12,5 @@
|
|||
|
||||
#mesondefine WLR_HAS_XCB_ERRORS
|
||||
#mesondefine WLR_HAS_XCB_ICCCM
|
||||
#mesondefine WLR_HAS_XCB_XKB
|
||||
|
||||
#endif
|
||||
|
|
|
@ -35,7 +35,6 @@ conf_data.set10('WLR_HAS_X11_BACKEND', false)
|
|||
conf_data.set10('WLR_HAS_XWAYLAND', false)
|
||||
conf_data.set10('WLR_HAS_XCB_ERRORS', false)
|
||||
conf_data.set10('WLR_HAS_XCB_ICCCM', false)
|
||||
conf_data.set10('WLR_HAS_XCB_XKB', false)
|
||||
|
||||
wlr_inc = include_directories('.', 'include')
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ option('logind', type: 'feature', value: 'auto', description: 'Enable support fo
|
|||
option('logind-provider', type: 'combo', choices: ['systemd', 'elogind'], value: 'systemd', description: 'Provider of logind support library')
|
||||
option('xcb-errors', type: 'feature', value: 'auto', description: 'Use xcb-errors util library')
|
||||
option('xcb-icccm', type: 'feature', value: 'auto', description: 'Use xcb-icccm util library')
|
||||
option('xcb-xkb', type: 'feature', value: 'auto', description: 'Use xcb-xkb util library')
|
||||
option('xwayland', type: 'feature', value: 'auto', description: 'Enable support for X11 applications')
|
||||
option('x11-backend', type: 'feature', value: 'auto', description: 'Enable X11 backend')
|
||||
option('rootston', type: 'boolean', value: true, description: 'Build the rootston example compositor')
|
||||
|
|
Loading…
Reference in New Issue