diff --git a/.builds/alpine.yml b/.builds/alpine.yml index 13221d258..402f72f65 100644 --- a/.builds/alpine.yml +++ b/.builds/alpine.yml @@ -16,7 +16,7 @@ sources: tasks: - setup: | cd wlroots - meson build -Dauto_features=enabled -Dlogind=disabled -Dxcb-errors=disabled + meson build -Dauto_features=enabled -Dlogind=disabled -Dlibseat=disabled -Dxcb-errors=disabled - build: | cd wlroots ninja -C build diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml index b3b1a3c05..9cf2f84fc 100644 --- a/.builds/archlinux.yml +++ b/.builds/archlinux.yml @@ -12,6 +12,7 @@ packages: - xcb-util-errors - xcb-util-image - xcb-util-wm + - seatd sources: - https://github.com/swaywm/wlroots tasks: diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index a0dfc442e..74553f7b1 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -18,6 +18,7 @@ packages: - x11/pixman - x11/xcb-util-errors - x11/xcb-util-wm +- sysutils/seatd sources: - https://github.com/swaywm/wlroots tasks: diff --git a/backend/backend.c b/backend/backend.c index c71b072f7..aa0d9668d 100644 --- a/backend/backend.c +++ b/backend/backend.c @@ -254,8 +254,7 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display, return backend; } - if (getenv("WAYLAND_DISPLAY") || getenv("_WAYLAND_DISPLAY") || - getenv("WAYLAND_SOCKET")) { + if (getenv("WAYLAND_DISPLAY") || getenv("WAYLAND_SOCKET")) { struct wlr_backend *wl_backend = attempt_wl_backend(display, create_renderer_func); if (!wl_backend) { diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 6978c22c1..40b73925a 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -373,6 +373,12 @@ static bool drm_crtc_page_flip(struct wlr_drm_connector *conn) { } conn->pageflip_pending = true; + + // wlr_output's API guarantees that submitting a buffer will schedule a + // frame event. However the DRM backend will also schedule a frame event + // when performing a modeset. Set frame_pending to true so that + // wlr_output_schedule_frame doesn't trigger a synthetic frame event. + conn->output.frame_pending = true; return true; } @@ -633,13 +639,15 @@ static bool drm_connector_export_dmabuf(struct wlr_output *output, return false; } - struct wlr_drm_plane *plane = crtc->primary; - - if (plane->current_fb.type == WLR_DRM_FB_TYPE_NONE) { + struct wlr_drm_fb *fb = &crtc->primary->queued_fb; + if (fb->type == WLR_DRM_FB_TYPE_NONE) { + fb = &crtc->primary->current_fb; + } + if (fb->type == WLR_DRM_FB_TYPE_NONE) { return false; } - return export_drm_bo(plane->current_fb.bo, attribs); + return export_drm_bo(fb->bo, attribs); } struct wlr_drm_fb *plane_get_next_fb(struct wlr_drm_plane *plane) { @@ -800,7 +808,7 @@ bool drm_connector_set_mode(struct wlr_drm_connector *conn, return false; } - wlr_log(WLR_INFO, "Modesetting '%s' with '%ux%u@%u mHz'", + wlr_log(WLR_INFO, "Modesetting '%s' with '%" PRId32 "x%" PRId32 "@%" PRId32 "mHz'", conn->output.name, wlr_mode->width, wlr_mode->height, wlr_mode->refresh); @@ -1398,7 +1406,7 @@ void scan_drm_connectors(struct wlr_drm_backend *drm) { bool is_mst = false; char *path = get_drm_prop_blob(drm->fd, wlr_conn->id, wlr_conn->props.path, &path_len); - if (path_len > 4 && path && strncmp(path, "mst:", 4) == 0) { + if (path && path_len > 4 && strncmp(path, "mst:", 4) == 0) { is_mst = true; } free(path); @@ -1523,7 +1531,7 @@ static void page_flip_handler(int fd, unsigned seq, }; wlr_output_send_present(&conn->output, &present_event); - if (drm->session->active) { + if (drm->session->active && conn->output.enabled) { wlr_output_send_frame(&conn->output); } } diff --git a/backend/libinput/backend.c b/backend/libinput/backend.c index 87497b11d..3c42d7f8f 100644 --- a/backend/libinput/backend.c +++ b/backend/libinput/backend.c @@ -44,9 +44,24 @@ static int handle_libinput_readable(int fd, uint32_t mask, void *_backend) { return 0; } +static enum wlr_log_importance libinput_log_priority_to_wlr( + enum libinput_log_priority priority) { + switch (priority) { + case LIBINPUT_LOG_PRIORITY_ERROR: + return WLR_ERROR; + case LIBINPUT_LOG_PRIORITY_INFO: + return WLR_INFO; + default: + return WLR_DEBUG; + } +} + static void log_libinput(struct libinput *libinput_context, enum libinput_log_priority priority, const char *fmt, va_list args) { - _wlr_vlog(WLR_ERROR, fmt, args); + enum wlr_log_importance importance = libinput_log_priority_to_wlr(priority); + static char wlr_fmt[1024]; + snprintf(wlr_fmt, sizeof(wlr_fmt), "[libinput] %s", fmt); + _wlr_vlog(importance, wlr_fmt, args); } static bool backend_start(struct wlr_backend *wlr_backend) { diff --git a/backend/session/direct-freebsd.c b/backend/session/direct-freebsd.c index c6e1f7204..e4b6c7929 100644 --- a/backend/session/direct-freebsd.c +++ b/backend/session/direct-freebsd.c @@ -18,6 +18,7 @@ #include #include #include "backend/session/direct-ipc.h" +#include "backend/session/session.h" #include "util/signal.h" const struct session_impl session_direct; @@ -274,6 +275,7 @@ static struct wlr_session *direct_session_create(struct wl_display *disp) { return NULL; } + session_init(&session->base); session->sock = direct_ipc_init(&session->child); if (session->sock == -1) { goto error_session; @@ -297,6 +299,7 @@ static struct wlr_session *direct_session_create(struct wl_display *disp) { snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat); session->base.impl = &session_direct; + session->base.active = true; return &session->base; error_ipc: diff --git a/backend/session/direct-ipc.c b/backend/session/direct-ipc.c index 94f34a88d..d98d4e669 100644 --- a/backend/session/direct-ipc.c +++ b/backend/session/direct-ipc.c @@ -24,16 +24,6 @@ enum { DRM_MAJOR = 226 }; -static bool have_permissions(void) { -#ifdef __linux__ - if (geteuid() != 0) { - wlr_log(WLR_ERROR, "Do not have root privileges; cannot become DRM master"); - return false; - } -#endif - return true; -} - static void send_msg(int sock, int fd, void *buf, size_t buf_len) { char control[CMSG_SPACE(sizeof(fd))] = {0}; struct iovec iovec = { .iov_base = buf, .iov_len = buf_len }; @@ -223,10 +213,6 @@ void direct_ipc_finish(int sock, pid_t pid) { } int direct_ipc_init(pid_t *pid_out) { - if (!have_permissions()) { - return -1; - } - int sock[2]; if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sock) < 0) { wlr_log_errno(WLR_ERROR, "Failed to create socket pair"); diff --git a/backend/session/direct.c b/backend/session/direct.c index 4ffd711d7..f9041070b 100644 --- a/backend/session/direct.c +++ b/backend/session/direct.c @@ -18,6 +18,7 @@ #include #include #include "backend/session/direct-ipc.h" +#include "backend/session/session.h" #include "util/signal.h" enum { DRM_MAJOR = 226 }; @@ -246,6 +247,7 @@ static struct wlr_session *direct_session_create(struct wl_display *disp) { return NULL; } + session_init(&session->base); session->sock = direct_ipc_init(&session->child); if (session->sock == -1) { goto error_session; @@ -267,6 +269,7 @@ static struct wlr_session *direct_session_create(struct wl_display *disp) { snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat); session->base.impl = &session_direct; + session->base.active = true; wlr_log(WLR_INFO, "Successfully loaded direct session"); return &session->base; diff --git a/backend/session/libseat.c b/backend/session/libseat.c new file mode 100644 index 000000000..751adcd3e --- /dev/null +++ b/backend/session/libseat.c @@ -0,0 +1,215 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "backend/session/session.h" +#include "util/signal.h" + +#include + +const struct session_impl session_libseat; + +struct libseat_device { + struct wl_list link; + int fd; + int device_id; +}; + +struct libseat_session { + struct wlr_session base; + + struct libseat *seat; + struct wl_event_source *event; + struct wl_list devices; +}; + +static void handle_enable_seat(struct libseat *seat, void *data) { + struct libseat_session *session = data; + session->base.active = true; + wlr_signal_emit_safe(&session->base.session_signal, session); +} + +static void handle_disable_seat(struct libseat *seat, void *data) { + struct libseat_session *session = data; + session->base.active = false; + wlr_signal_emit_safe(&session->base.session_signal, session); + libseat_disable_seat(session->seat); +} + +static int libseat_event(int fd, uint32_t mask, void *data) { + struct libseat *seat = data; + libseat_dispatch(seat, 0); + return 1; +} + +static struct libseat_seat_listener seat_listener = { + .enable_seat = handle_enable_seat, + .disable_seat = handle_disable_seat, +}; + +static struct libseat_session *libseat_session_from_session( + struct wlr_session *base) { + assert(base->impl == &session_libseat); + return (struct libseat_session *)base; +} + +static enum wlr_log_importance libseat_log_level_to_wlr( + enum libseat_log_level level) { + switch (level) { + case LIBSEAT_LOG_LEVEL_ERROR: + return WLR_ERROR; + case LIBSEAT_LOG_LEVEL_INFO: + return WLR_INFO; + default: + return WLR_DEBUG; + } +} + +static void log_libseat(enum libseat_log_level level, + const char *fmt, va_list args) { + enum wlr_log_importance importance = libseat_log_level_to_wlr(level); + + static char wlr_fmt[1024]; + snprintf(wlr_fmt, sizeof(wlr_fmt), "[libseat] %s", fmt); + + _wlr_vlog(importance, wlr_fmt, args); +} + +static struct wlr_session *libseat_session_create(struct wl_display *disp) { + struct libseat_session *session = calloc(1, sizeof(*session)); + if (!session) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + return NULL; + } + + session_init(&session->base); + wl_list_init(&session->devices); + + libseat_set_log_handler(log_libseat); + libseat_set_log_level(LIBSEAT_LOG_LEVEL_ERROR); + + session->seat = libseat_open_seat(&seat_listener, session); + if (session->seat == NULL) { + wlr_log_errno(WLR_ERROR, "Unable to create seat"); + goto error; + } + + const char *seat_name = libseat_seat_name(session->seat); + if (seat_name == NULL) { + wlr_log_errno(WLR_ERROR, "Unable to get seat info"); + goto error; + } + snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat_name); + + struct wl_event_loop *event_loop = wl_display_get_event_loop(disp); + session->event = wl_event_loop_add_fd(event_loop, libseat_get_fd(session->seat), + WL_EVENT_READABLE, libseat_event, session->seat); + if (session->event == NULL) { + wlr_log(WLR_ERROR, "Failed to create libseat event source"); + goto error; + } + + // We may have received enable_seat immediately after the open_seat result, + // so, dispatch once without timeout to speed up activation. + if (libseat_dispatch(session->seat, 0) == -1) { + wlr_log_errno(WLR_ERROR, "libseat dispatch failed"); + goto error; + } + + wlr_log(WLR_INFO, "Successfully loaded libseat session"); + session->base.impl = &session_libseat; + return &session->base; + +error: + if (session->seat != NULL) { + libseat_close_seat(session->seat); + } + if (session->event != NULL) { + wl_event_source_remove(session->event); + } + free(session); + return NULL; +} + +static void libseat_session_destroy(struct wlr_session *base) { + struct libseat_session *session = libseat_session_from_session(base); + + libseat_close_seat(session->seat); + wl_event_source_remove(session->event); + free(session); +} + +static struct libseat_device *find_device_by_fd(struct libseat_session *session, int fd) { + struct libseat_device *dev; + wl_list_for_each(dev, &session->devices, link) { + if (dev->fd == fd) { + return dev; + } + } + return NULL; +} + +static int libseat_session_open_device(struct wlr_session *base, const char *path) { + struct libseat_session *session = libseat_session_from_session(base); + + int fd; + int device_id = libseat_open_device(session->seat, path, &fd); + if (device_id == -1) { + wlr_log_errno(WLR_ERROR, "Failed to open device '%s'", path); + return -1; + } + + struct libseat_device *dev = calloc(1, sizeof(struct libseat_device)); + if (dev == NULL) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + libseat_close_device(session->seat, device_id); + return -1; + } + + dev->fd = fd; + dev->device_id = device_id; + wl_list_insert(&session->devices, &dev->link); + + return fd; +} + +static void libseat_session_close_device(struct wlr_session *base, int fd) { + struct libseat_session *session = libseat_session_from_session(base); + + struct libseat_device *dev = find_device_by_fd(session, fd); + if (dev == NULL) { + wlr_log(WLR_ERROR, "No device with fd %d found", fd); + close(fd); + return; + } + + if (libseat_close_device(session->seat, dev->device_id) == -1) { + wlr_log_errno(WLR_ERROR, "Failed to close device %d", dev->device_id); + } + + wl_list_remove(&dev->link); + free(dev); + close(fd); +} + +static bool libseat_change_vt(struct wlr_session *base, unsigned vt) { + struct libseat_session *session = libseat_session_from_session(base); + return libseat_switch_session(session->seat, vt); +} + +const struct session_impl session_libseat = { + .create = libseat_session_create, + .destroy = libseat_session_destroy, + .open = libseat_session_open_device, + .close = libseat_session_close_device, + .change_vt = libseat_change_vt, +}; diff --git a/backend/session/logind.c b/backend/session/logind.c index b5acd4e4d..9c2515a87 100644 --- a/backend/session/logind.c +++ b/backend/session/logind.c @@ -13,6 +13,7 @@ #include #include #include +#include "backend/session/session.h" #include "util/signal.h" #if WLR_HAS_SYSTEMD @@ -129,8 +130,8 @@ static void logind_release_device(struct wlr_session *base, int fd) { static bool logind_change_vt(struct wlr_session *base, unsigned vt) { struct logind_session *session = logind_session_from_session(base); - // Only seat0 has VTs associated with it - if (strcmp(session->base.seat, "seat0") != 0) { + // Only if seat has VTs associated with it + if (!sd_seat_can_tty(session->base.seat)) { return true; } @@ -142,7 +143,7 @@ static bool logind_change_vt(struct wlr_session *base, unsigned vt) { "/org/freedesktop/login1/seat/seat0", "org.freedesktop.login1.Seat", "SwitchTo", &error, &msg, "u", (uint32_t)vt); if (ret < 0) { - wlr_log(WLR_ERROR, "Failed to change to vt '%d'", vt); + wlr_log(WLR_ERROR, "Failed to change to vt '%u'", vt); } sd_bus_error_free(&error); @@ -241,6 +242,32 @@ static bool take_control(struct logind_session *session) { return ret >= 0; } +static void set_type(struct logind_session *session) { + int ret; + sd_bus_message *msg = NULL; + sd_bus_error error = SD_BUS_ERROR_NULL; + + ret = sd_bus_call_method(session->bus, "org.freedesktop.login1", + session->path, "org.freedesktop.login1.Session", "SetType", + &error, &msg, "s", "wayland"); + if (ret < 0) { + wlr_log(WLR_DEBUG, "Failed to set logind session type for session: %s", + error.message); + } + + sd_bus_error_free(&error); + sd_bus_message_unref(msg); + + if (ret < 0) { + return; + } + + ret = setenv("XDG_SESSION_TYPE", "wayland", 1); + if (ret < 0) { + wlr_log(WLR_ERROR, "Failed to set XDG_SESSION_TYPE for session"); + } +} + static void release_control(struct logind_session *session) { int ret; sd_bus_message *msg = NULL; @@ -750,6 +777,8 @@ static struct wlr_session *logind_session_create(struct wl_display *disp) { return NULL; } + session_init(&session->base); + if (!get_display_session(&session->id)) { goto error; } @@ -762,7 +791,7 @@ static struct wlr_session *logind_session_create(struct wl_display *disp) { } snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat); - if (strcmp(seat, "seat0") == 0) { + if (sd_seat_can_tty(seat)) { ret = sd_session_get_vt(session->id, &session->base.vtnr); if (ret < 0) { wlr_log(WLR_ERROR, "Session not running in virtual terminal"); @@ -819,9 +848,13 @@ static struct wlr_session *logind_session_create(struct wl_display *disp) { } } + set_type(session); + wlr_log(WLR_INFO, "Successfully loaded logind session"); session->base.impl = &session_logind; + session->base.active = true; + return &session->base; error_bus: diff --git a/backend/session/meson.build b/backend/session/meson.build index 81ff6d85c..140d41059 100644 --- a/backend/session/meson.build +++ b/backend/session/meson.build @@ -62,3 +62,12 @@ if logind_found wlr_files += files('logind.c') wlr_deps += logind endif + +# libseat + +libseat = dependency('libseat', required: get_option('libseat'), version: '>=0.2.0') +if libseat.found() + wlr_files += files('libseat.c') + wlr_deps += libseat + conf_data.set10('WLR_HAS_LIBSEAT', true) +endif diff --git a/backend/session/noop.c b/backend/session/noop.c index 0e13a1778..3f293775f 100644 --- a/backend/session/noop.c +++ b/backend/session/noop.c @@ -6,6 +6,7 @@ #include #include #include +#include "backend/session/session.h" #include "util/signal.h" const struct session_impl session_noop; @@ -33,7 +34,9 @@ static struct wlr_session *noop_session_create(struct wl_display *disp) { return NULL; } + session_init(session); session->impl = &session_noop; + session->active = true; wlr_log(WLR_INFO, "Successfully initialized noop session"); return session; diff --git a/backend/session/session.c b/backend/session/session.c index 01eeffd98..d8e9509d8 100644 --- a/backend/session/session.c +++ b/backend/session/session.c @@ -13,13 +13,18 @@ #include #include #include +#include "backend/session/session.h" #include "util/signal.h" +extern const struct session_impl session_libseat; extern const struct session_impl session_logind; extern const struct session_impl session_direct; extern const struct session_impl session_noop; static const struct session_impl *impls[] = { +#if WLR_HAS_LIBSEAT + &session_libseat, +#endif #if WLR_HAS_SYSTEMD || WLR_HAS_ELOGIND &session_logind, #endif @@ -65,12 +70,24 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { wlr_session_destroy(session); } +void session_init(struct wlr_session *session) { + wl_signal_init(&session->session_signal); + wl_signal_init(&session->events.destroy); + wl_list_init(&session->devices); +} + struct wlr_session *wlr_session_create(struct wl_display *disp) { struct wlr_session *session = NULL; const char *env_wlr_session = getenv("WLR_SESSION"); if (env_wlr_session) { - if (strcmp(env_wlr_session, "logind") == 0 || + if (strcmp(env_wlr_session, "libseat") == 0) { +#if WLR_HAS_LIBSEAT + session = session_libseat.create(disp); +#else + wlr_log(WLR_ERROR, "wlroots is not compiled with libseat support"); +#endif + } else if (strcmp(env_wlr_session, "logind") == 0 || strcmp(env_wlr_session, "systemd") == 0) { #if WLR_HAS_SYSTEMD || WLR_HAS_ELOGIND session = session_logind.create(disp); @@ -97,11 +114,6 @@ struct wlr_session *wlr_session_create(struct wl_display *disp) { return NULL; } - session->active = true; - wl_signal_init(&session->session_signal); - wl_signal_init(&session->events.destroy); - wl_list_init(&session->devices); - session->udev = udev_new(); if (!session->udev) { wlr_log_errno(WLR_ERROR, "Failed to create udev context"); diff --git a/backend/wayland/backend.c b/backend/wayland/backend.c index 8de63acf7..459baaa6d 100644 --- a/backend/wayland/backend.c +++ b/backend/wayland/backend.c @@ -97,15 +97,17 @@ static void registry_global(void *data, struct wl_registry *registry, uint32_t name, const char *iface, uint32_t version) { struct wlr_wl_backend *wl = data; - wlr_log(WLR_DEBUG, "Remote wayland global: %s v%d", iface, version); + wlr_log(WLR_DEBUG, "Remote wayland global: %s v%" PRIu32, iface, version); if (strcmp(iface, wl_compositor_interface.name) == 0) { wl->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 4); } else if (strcmp(iface, wl_seat_interface.name) == 0) { - wl->seat = wl_registry_bind(registry, name, + struct wl_seat *wl_seat = wl_registry_bind(registry, name, &wl_seat_interface, 5); - wl_seat_add_listener(wl->seat, &seat_listener, wl); + if (!create_wl_seat(wl_seat, wl)) { + wl_seat_destroy(wl_seat); + } } else if (strcmp(iface, xdg_wm_base_interface.name) == 0) { wl->xdg_wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); @@ -155,13 +157,16 @@ static bool backend_start(struct wlr_backend *backend) { wl->started = true; - if (wl->keyboard) { - create_wl_keyboard(wl->keyboard, wl); - } + struct wlr_wl_seat *seat = wl->seat; + if (seat != NULL) { + if (seat->keyboard) { + create_wl_keyboard(seat->keyboard, wl); + } - if (wl->tablet_manager && wl->seat) { - wl_add_tablet_seat(wl->tablet_manager, - wl->seat, wl); + if (wl->tablet_manager) { + wl_add_tablet_seat(wl->tablet_manager, + seat->wl_seat, wl); + } } for (size_t i = 0; i < wl->requested_outputs; ++i) { @@ -192,8 +197,6 @@ static void backend_destroy(struct wlr_backend *backend) { wl_list_remove(&wl->local_display_destroy.link); - free(wl->seat_name); - wl_event_source_remove(wl->remote_display_src); wlr_renderer_destroy(wl->renderer); @@ -201,12 +204,7 @@ static void backend_destroy(struct wlr_backend *backend) { wlr_drm_format_set_finish(&wl->linux_dmabuf_v1_formats); - if (wl->pointer) { - wl_pointer_destroy(wl->pointer); - } - if (wl->seat) { - wl_seat_destroy(wl->seat); - } + destroy_wl_seats(wl); if (wl->zxdg_decoration_manager_v1) { zxdg_decoration_manager_v1_destroy(wl->zxdg_decoration_manager_v1); } @@ -279,7 +277,6 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display, } wl_registry_add_listener(wl->registry, ®istry_listener, wl); - wl_display_dispatch(wl->remote_display); wl_display_roundtrip(wl->remote_display); if (!wl->compositor) { diff --git a/backend/wayland/output.c b/backend/wayland/output.c index bd17fa208..568d90db6 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -419,8 +419,9 @@ static void output_destroy(struct wlr_output *wlr_output) { } void update_wl_output_cursor(struct wlr_wl_output *output) { - if (output->backend->pointer && output->enter_serial) { - wl_pointer_set_cursor(output->backend->pointer, output->enter_serial, + struct wlr_wl_pointer *pointer = output->backend->current_pointer; + if (pointer && pointer->output == output && output->enter_serial) { + wl_pointer_set_cursor(pointer->wl_pointer, output->enter_serial, output->cursor.surface, output->cursor.hotspot_x, output->cursor.hotspot_y); } @@ -585,8 +586,11 @@ struct wlr_output *wlr_wl_output_create(struct wlr_backend *wlr_backend) { wlr_signal_emit_safe(&backend->backend.events.new_output, wlr_output); - if (backend->pointer != NULL) { - create_wl_pointer(backend->pointer, output); + struct wlr_wl_seat *seat = backend->seat; + if (seat != NULL) { + if (seat->pointer) { + create_wl_pointer(seat->pointer, output); + } } return wlr_output; diff --git a/backend/wayland/seat.c b/backend/wayland/seat.c index 1fcb93e5f..1697616c5 100644 --- a/backend/wayland/seat.c +++ b/backend/wayland/seat.c @@ -48,6 +48,7 @@ static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer, struct wlr_wl_output *output = wl_surface_get_user_data(surface); assert(output); struct wlr_wl_pointer *pointer = output_get_pointer(output); + assert(!backend->current_pointer || backend->current_pointer == pointer); output->enter_serial = serial; backend->current_pointer = pointer; @@ -276,18 +277,156 @@ static struct wl_keyboard_listener keyboard_listener = { .repeat_info = keyboard_handle_repeat_info }; +static void touch_coordinates_to_absolute(struct wlr_wl_input_device *device, + wl_fixed_t x, wl_fixed_t y, double *sx, double *sy) { + // TODO: each output needs its own touch + struct wlr_wl_output *output, *tmp; + wl_list_for_each_safe(output, tmp, &device->backend->outputs, link) { + *sx = wl_fixed_to_double(x) / output->wlr_output.width; + *sy = wl_fixed_to_double(y) / output->wlr_output.height; + return; // Choose the first output in the list + } + + *sx = *sy = 0; +} + +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, wl_fixed_t y) { + struct wlr_wl_input_device *device = data; + assert(device && device->wlr_input_device.touch); + + double sx, sy; + touch_coordinates_to_absolute(device, x, y, &sx, &sy); + struct wlr_event_touch_down event = { + .device = &device->wlr_input_device, + .time_msec = time, + .touch_id = id, + .x = sx, + .y = sy + }; + wlr_signal_emit_safe(&device->wlr_input_device.touch->events.down, &event); +} + +static void touch_handle_up(void *data, struct wl_touch *wl_touch, + uint32_t serial, uint32_t time, int32_t id) { + struct wlr_wl_input_device *device = data; + assert(device && device->wlr_input_device.touch); + + struct wlr_event_touch_up event = { + .device = &device->wlr_input_device, + .time_msec = time, + .touch_id = id, + }; + wlr_signal_emit_safe(&device->wlr_input_device.touch->events.up, &event); +} + +static void touch_handle_motion(void *data, struct wl_touch *wl_touch, + uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y) { + struct wlr_wl_input_device *device = data; + assert(device && device->wlr_input_device.touch); + + double sx, sy; + touch_coordinates_to_absolute(device, x, y, &sx, &sy); + struct wlr_event_touch_motion event = { + .device = &device->wlr_input_device, + .time_msec = time, + .touch_id = id, + .x = sx, + .y = sy + }; + wlr_signal_emit_safe(&device->wlr_input_device.touch->events.motion, &event); +} + +static void touch_handle_frame(void *data, struct wl_touch *wl_touch) { + // no-op +} + +static void touch_handle_cancel(void *data, struct wl_touch *wl_touch) { + // no-op +} + +static void touch_handle_shape(void *data, struct wl_touch *wl_touch, + int32_t id, wl_fixed_t major, wl_fixed_t minor) { + // no-op +} + +static void touch_handle_orientation(void *data, struct wl_touch *wl_touch, + int32_t id, wl_fixed_t orientation) { + // no-op +} + +static struct wl_touch_listener touch_listener = { + .down = touch_handle_down, + .up = touch_handle_up, + .motion = touch_handle_motion, + .frame = touch_handle_frame, + .cancel = touch_handle_cancel, + .shape = touch_handle_shape, + .orientation = touch_handle_orientation, +}; + static struct wlr_wl_input_device *get_wl_input_device_from_input_device( struct wlr_input_device *wlr_dev) { assert(wlr_input_device_is_wl(wlr_dev)); return (struct wlr_wl_input_device *)wlr_dev; } +bool create_wl_seat(struct wl_seat *wl_seat, struct wlr_wl_backend *wl) { + assert(!wl->seat); // only one seat supported at the moment + struct wlr_wl_seat *seat = calloc(1, sizeof(struct wlr_wl_seat)); + if (!seat) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + return false; + } + seat->wl_seat = wl_seat; + wl->seat = seat; + wl_seat_add_listener(wl_seat, &seat_listener, wl); + return true; +} + +void destroy_wl_seats(struct wlr_wl_backend *wl) { + struct wlr_wl_seat *seat = wl->seat; + if (!seat) { + return; + } + + if (seat->touch) { + wl_touch_destroy(seat->touch); + } + if (seat->pointer) { + wl_pointer_destroy(seat->pointer); + } + if (seat->keyboard && !wl->started) { + // early termination will not be handled by input_device_destroy + wl_keyboard_destroy(seat->keyboard); + } + free(seat->name); + if (seat->wl_seat) { + wl_seat_destroy(seat->wl_seat); + } + free(seat); +} + +static struct wlr_wl_seat *backend_get_seat(struct wlr_wl_backend *backend, struct wl_seat *wl_seat) { + assert(backend->seat && backend->seat->wl_seat == wl_seat); + return backend->seat; +} + +static struct wlr_wl_seat *input_device_get_seat(struct wlr_input_device *wlr_dev) { + struct wlr_wl_input_device *dev = + get_wl_input_device_from_input_device(wlr_dev); + assert(dev->backend->seat); + return dev->backend->seat; +} + static void input_device_destroy(struct wlr_input_device *wlr_dev) { struct wlr_wl_input_device *dev = get_wl_input_device_from_input_device(wlr_dev); if (dev->wlr_input_device.type == WLR_INPUT_DEVICE_KEYBOARD) { - wl_keyboard_release(dev->backend->keyboard); - dev->backend->keyboard = NULL; + struct wlr_wl_seat *seat = input_device_get_seat(wlr_dev); + wl_keyboard_release(seat->keyboard); + seat->keyboard = NULL; } wl_list_remove(&dev->wlr_input_device.link); free(dev); @@ -450,6 +589,9 @@ static void relative_pointer_handle_relative_motion(void *data, wl_fixed_t dy_unaccel) { struct wlr_wl_input_device *input_device = data; struct wlr_input_device *wlr_dev = &input_device->wlr_input_device; + if (pointer_get_wl(wlr_dev->pointer) != input_device->backend->current_pointer) { + return; + } uint64_t time_usec = (uint64_t)utime_hi << 32 | utime_lo; @@ -473,6 +615,9 @@ static void pointer_handle_output_destroy(struct wl_listener *listener, void *data) { struct wlr_wl_pointer *pointer = wl_container_of(listener, pointer, output_destroy); + if (pointer->relative_pointer) { + zwp_relative_pointer_v1_destroy(pointer->relative_pointer); + } wlr_input_device_destroy(&pointer->input_device->wlr_input_device); } @@ -498,9 +643,6 @@ void create_wl_pointer(struct wl_pointer *wl_pointer, struct wlr_wl_output *outp pointer->wl_pointer = wl_pointer; pointer->output = output; - wl_signal_add(&output->wlr_output.events.destroy, &pointer->output_destroy); - pointer->output_destroy.notify = pointer_handle_output_destroy; - struct wlr_wl_input_device *dev = create_wl_input_device(backend, WLR_INPUT_DEVICE_POINTER); if (dev == NULL) { @@ -510,6 +652,9 @@ void create_wl_pointer(struct wl_pointer *wl_pointer, struct wlr_wl_output *outp } pointer->input_device = dev; + wl_signal_add(&output->wlr_output.events.destroy, &pointer->output_destroy); + pointer->output_destroy.notify = pointer_handle_output_destroy; + wlr_dev = &dev->wlr_input_device; wlr_dev->pointer = &pointer->wlr_pointer; wlr_dev->output_name = strdup(output->wlr_output.name); @@ -548,7 +693,7 @@ void create_wl_keyboard(struct wl_keyboard *wl_keyboard, struct wlr_wl_backend * wlr_dev->keyboard = calloc(1, sizeof(*wlr_dev->keyboard)); if (!wlr_dev->keyboard) { wlr_log_errno(WLR_ERROR, "Allocation failed"); - free(dev); + wlr_input_device_destroy(wlr_dev); return; } wlr_keyboard_init(wlr_dev->keyboard, NULL); @@ -557,23 +702,45 @@ void create_wl_keyboard(struct wl_keyboard *wl_keyboard, struct wlr_wl_backend * wlr_signal_emit_safe(&wl->backend.events.new_input, wlr_dev); } +void create_wl_touch(struct wl_touch *wl_touch, struct wlr_wl_backend *wl) { + struct wlr_wl_input_device *dev = + create_wl_input_device(wl, WLR_INPUT_DEVICE_TOUCH); + if (!dev) { + return; + } + + struct wlr_input_device *wlr_dev = &dev->wlr_input_device; + + wlr_dev->touch = calloc(1, sizeof(*wlr_dev->touch)); + if (!wlr_dev->touch) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + wlr_input_device_destroy(wlr_dev); + return; + } + wlr_touch_init(wlr_dev->touch, NULL); + + wl_touch_add_listener(wl_touch, &touch_listener, dev); + wlr_signal_emit_safe(&wl->backend.events.new_input, wlr_dev); +} + + static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum wl_seat_capability caps) { struct wlr_wl_backend *backend = data; - assert(backend->seat == wl_seat); + struct wlr_wl_seat *seat = backend_get_seat(backend, wl_seat); - if ((caps & WL_SEAT_CAPABILITY_POINTER) && backend->pointer == NULL) { + if ((caps & WL_SEAT_CAPABILITY_POINTER) && seat->pointer == NULL) { wlr_log(WLR_DEBUG, "seat %p offered pointer", (void *)wl_seat); struct wl_pointer *wl_pointer = wl_seat_get_pointer(wl_seat); - backend->pointer = wl_pointer; + seat->pointer = wl_pointer; struct wlr_wl_output *output; wl_list_for_each(output, &backend->outputs, link) { create_wl_pointer(wl_pointer, output); } } - if (!(caps & WL_SEAT_CAPABILITY_POINTER) && backend->pointer != NULL) { + if (!(caps & WL_SEAT_CAPABILITY_POINTER) && seat->pointer != NULL) { wlr_log(WLR_DEBUG, "seat %p dropped pointer", (void *)wl_seat); struct wlr_input_device *device, *tmp; @@ -583,21 +750,21 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, } } - wl_pointer_release(backend->pointer); - backend->pointer = NULL; + wl_pointer_release(seat->pointer); + seat->pointer = NULL; } - if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && backend->keyboard == NULL) { + if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && seat->keyboard == NULL) { wlr_log(WLR_DEBUG, "seat %p offered keyboard", (void *)wl_seat); struct wl_keyboard *wl_keyboard = wl_seat_get_keyboard(wl_seat); - backend->keyboard = wl_keyboard; + seat->keyboard = wl_keyboard; if (backend->started) { create_wl_keyboard(wl_keyboard, backend); } } - if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && backend->keyboard != NULL) { + if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && seat->keyboard != NULL) { wlr_log(WLR_DEBUG, "seat %p dropped keyboard", (void *)wl_seat); struct wlr_input_device *device, *tmp; @@ -606,17 +773,38 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, wlr_input_device_destroy(device); } } - assert(backend->keyboard == NULL); // free'ed by input_device_destroy + assert(seat->keyboard == NULL); // free'ed by input_device_destroy + } + + if ((caps & WL_SEAT_CAPABILITY_TOUCH) && seat->touch == NULL) { + wlr_log(WLR_DEBUG, "seat %p offered touch", (void *)wl_seat); + + seat->touch = wl_seat_get_touch(wl_seat); + if (backend->started) { + create_wl_touch(seat->touch, backend); + } + } + if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && seat->touch != NULL) { + wlr_log(WLR_DEBUG, "seat %p dropped touch", (void *)wl_seat); + + struct wlr_input_device *device, *tmp; + wl_list_for_each_safe(device, tmp, &backend->devices, link) { + if (device->type == WLR_INPUT_DEVICE_TOUCH) { + wlr_input_device_destroy(device); + } + } + + wl_touch_release(seat->touch); + seat->touch = NULL; } } static void seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name) { struct wlr_wl_backend *backend = data; - assert(backend->seat == wl_seat); - // Do we need to check if seatName was previously set for name change? - free(backend->seat_name); - backend->seat_name = strdup(name); + struct wlr_wl_seat *seat = backend_get_seat(backend, wl_seat); + free(seat->name); + seat->name = strdup(name); } const struct wl_seat_listener seat_listener = { @@ -625,7 +813,5 @@ const struct wl_seat_listener seat_listener = { }; struct wl_seat *wlr_wl_input_device_get_seat(struct wlr_input_device *wlr_dev) { - struct wlr_wl_input_device *dev = - get_wl_input_device_from_input_device(wlr_dev); - return dev->backend->seat; + return input_device_get_seat(wlr_dev)->wl_seat; } diff --git a/docs/env_vars.md b/docs/env_vars.md index 48f96f7fd..9fca81656 100644 --- a/docs/env_vars.md +++ b/docs/env_vars.md @@ -9,6 +9,8 @@ wlroots reads these environment variables * *WLR_SESSION*: specifies the wlr\_session to be used (available sessions: logind/systemd, direct) * *WLR_DIRECT_TTY*: specifies the tty to be used (instead of using /dev/tty) +* *WLR_XWAYLAND*: specifies the path to an Xwayland binary to be used (instead + of following shell search semantics for "Xwayland") ## DRM backend diff --git a/examples/dmabuf-capture.c b/examples/dmabuf-capture.c index 203a1ac4a..60cdb6577 100644 --- a/examples/dmabuf-capture.c +++ b/examples/dmabuf-capture.c @@ -753,8 +753,11 @@ static int init(struct capture_context *ctx) { ctx->registry = wl_display_get_registry(ctx->display); wl_registry_add_listener(ctx->registry, ®istry_listener, ctx); + // First roundtrip to fetch globals + wl_display_roundtrip(ctx->display); + + // Second roundtrip to fetch wl_output information wl_display_roundtrip(ctx->display); - wl_display_dispatch(ctx->display); if (!ctx->export_manager) { av_log(ctx, AV_LOG_ERROR, "Compositor doesn't support %s!\n", diff --git a/examples/foreign-toplevel.c b/examples/foreign-toplevel.c index 9184e7374..995d4f6cc 100644 --- a/examples/foreign-toplevel.c +++ b/examples/foreign-toplevel.c @@ -7,7 +7,7 @@ #include #include "wlr-foreign-toplevel-management-unstable-v1-client-protocol.h" -#define WLR_FOREIGN_TOPLEVEL_MANAGEMENT_VERSION 2 +#define WLR_FOREIGN_TOPLEVEL_MANAGEMENT_VERSION 3 /** * Usage: @@ -38,11 +38,14 @@ enum toplevel_state_field { TOPLEVEL_STATE_INVALID = (1 << 4), }; +static const uint32_t no_parent = (uint32_t)-1; + struct toplevel_state { char *title; char *app_id; uint32_t state; + uint32_t parent_id; }; static void copy_state(struct toplevel_state *current, @@ -67,6 +70,8 @@ static void copy_state(struct toplevel_state *current, current->state = pending->state; } + current->parent_id = pending->parent_id; + pending->state = TOPLEVEL_STATE_INVALID; } @@ -84,6 +89,12 @@ static void print_toplevel(struct toplevel_v1 *toplevel, bool print_endl) { toplevel->current.title ?: "(nil)", toplevel->current.app_id ?: "(nil)"); + if (toplevel->current.parent_id != no_parent) { + printf(" parent=%u", toplevel->current.parent_id); + } else { + printf(" no parent"); + } + if (print_endl) { printf("\n"); } @@ -172,6 +183,28 @@ static void toplevel_handle_state(void *data, toplevel->pending.state = array_to_state(state); } +static struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager = NULL; +static struct wl_list toplevel_list; + +static void toplevel_handle_parent(void *data, + struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel, + struct zwlr_foreign_toplevel_handle_v1 *zwlr_parent) { + struct toplevel_v1 *toplevel = data; + toplevel->pending.parent_id = no_parent; + if (zwlr_parent) { + struct toplevel_v1 *toplevel_tmp; + wl_list_for_each(toplevel_tmp, &toplevel_list, link) { + if (toplevel_tmp->zwlr_toplevel == zwlr_parent) { + toplevel->pending.parent_id = toplevel_tmp->id; + break; + } + } + if (toplevel->pending.parent_id == no_parent) { + fprintf(stderr, "Cannot find parent toplevel!\n"); + } + } +} + static void toplevel_handle_done(void *data, struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel) { struct toplevel_v1 *toplevel = data; @@ -202,11 +235,9 @@ static const struct zwlr_foreign_toplevel_handle_v1_listener toplevel_impl = { .state = toplevel_handle_state, .done = toplevel_handle_done, .closed = toplevel_handle_closed, + .parent = toplevel_handle_parent }; -static struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager = NULL; -static struct wl_list toplevel_list; - static void toplevel_manager_handle_toplevel(void *data, struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager, struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel) { @@ -218,6 +249,8 @@ static void toplevel_manager_handle_toplevel(void *data, toplevel->id = global_id++; toplevel->zwlr_toplevel = zwlr_toplevel; + toplevel->current.parent_id = no_parent; + toplevel->pending.parent_id = no_parent; wl_list_insert(&toplevel_list, &toplevel->link); zwlr_foreign_toplevel_handle_v1_add_listener(zwlr_toplevel, &toplevel_impl, @@ -332,7 +365,6 @@ int main(int argc, char **argv) { struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); - wl_display_dispatch(display); wl_display_roundtrip(display); if (toplevel_manager == NULL) { diff --git a/examples/gamma-control.c b/examples/gamma-control.c index a060b8837..9fa00ce36 100644 --- a/examples/gamma-control.c +++ b/examples/gamma-control.c @@ -162,7 +162,6 @@ int main(int argc, char *argv[]) { struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); - wl_display_dispatch(display); wl_display_roundtrip(display); if (gamma_control_manager == NULL) { diff --git a/examples/idle-inhibit.c b/examples/idle-inhibit.c index 348892ab3..5de32dbd7 100644 --- a/examples/idle-inhibit.c +++ b/examples/idle-inhibit.c @@ -177,7 +177,6 @@ int main(int argc, char **argv) { struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); - wl_display_dispatch(display); wl_display_roundtrip(display); if (compositor == NULL) { diff --git a/examples/idle.c b/examples/idle.c index 6faa1d7d3..254f1c79b 100644 --- a/examples/idle.c +++ b/examples/idle.c @@ -125,7 +125,6 @@ int main(int argc, char *argv[]) { struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); - wl_display_dispatch(display); wl_display_roundtrip(display); wl_registry_destroy(registry); diff --git a/examples/input-inhibitor.c b/examples/input-inhibitor.c index 25e46c735..7192b45b2 100644 --- a/examples/input-inhibitor.c +++ b/examples/input-inhibitor.c @@ -150,7 +150,6 @@ int main(int argc, char **argv) { struct wl_registry *registry = wl_display_get_registry(display); assert(registry); wl_registry_add_listener(registry, ®istry_listener, NULL); - wl_display_dispatch(display); wl_display_roundtrip(display); assert(compositor && seat && wm_base && input_inhibit_manager); diff --git a/examples/input-method-keyboard-grab.c b/examples/input-method-keyboard-grab.c index 881f91a92..055abeab8 100644 --- a/examples/input-method-keyboard-grab.c +++ b/examples/input-method-keyboard-grab.c @@ -191,7 +191,6 @@ int main(int argc, char **argv) { struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); - wl_display_dispatch(display); wl_display_roundtrip(display); if (input_method_manager == NULL) { diff --git a/examples/input-method.c b/examples/input-method.c index 9276e1599..479c87613 100644 --- a/examples/input-method.c +++ b/examples/input-method.c @@ -324,7 +324,6 @@ int main(int argc, char **argv) { struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); - wl_display_dispatch(display); wl_display_roundtrip(display); if (compositor == NULL) { diff --git a/examples/keyboard-shortcuts-inhibit.c b/examples/keyboard-shortcuts-inhibit.c index 3d066c0e0..e9a837b61 100644 --- a/examples/keyboard-shortcuts-inhibit.c +++ b/examples/keyboard-shortcuts-inhibit.c @@ -209,7 +209,6 @@ int main(int argc, char **argv) { struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); - wl_display_dispatch(display); wl_display_roundtrip(display); if (compositor == NULL) { diff --git a/examples/multi-pointer.c b/examples/multi-pointer.c index 9f7bd6c8e..eceeb6d68 100644 --- a/examples/multi-pointer.c +++ b/examples/multi-pointer.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include diff --git a/examples/output-power-management.c b/examples/output-power-management.c index 8d91c6c05..00d4122a3 100644 --- a/examples/output-power-management.c +++ b/examples/output-power-management.c @@ -109,12 +109,11 @@ int main(int argc, char *argv[]) { struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); - wl_display_dispatch(display); wl_display_roundtrip(display); if (output_power_manager == NULL) { fprintf(stderr, - "compositor doesn't support wlr-output-power-managment-unstable-v1\n"); + "compositor doesn't support wlr-output-power-management-unstable-v1\n"); return EXIT_FAILURE; } diff --git a/examples/pointer-constraints.c b/examples/pointer-constraints.c index 1df9f6ce0..f67f1edec 100644 --- a/examples/pointer-constraints.c +++ b/examples/pointer-constraints.c @@ -198,7 +198,6 @@ int main(int argc, char **argv) { struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); - wl_display_dispatch(display); wl_display_roundtrip(display); struct wl_region *disjoint_region = wl_compositor_create_region(compositor); diff --git a/examples/pointer.c b/examples/pointer.c index a60807163..703aa59ea 100644 --- a/examples/pointer.c +++ b/examples/pointer.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include diff --git a/examples/relative-pointer-unstable-v1.c b/examples/relative-pointer-unstable-v1.c index ce671f995..02f9d1c52 100644 --- a/examples/relative-pointer-unstable-v1.c +++ b/examples/relative-pointer-unstable-v1.c @@ -412,7 +412,6 @@ int main(int argc, char **argv) { struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); - wl_display_dispatch(display); wl_display_roundtrip(display); /* Check that all the global interfaces were captured */ diff --git a/examples/screencopy-dmabuf.c b/examples/screencopy-dmabuf.c index 74eb8a75d..e7657e1a9 100644 --- a/examples/screencopy-dmabuf.c +++ b/examples/screencopy-dmabuf.c @@ -297,7 +297,7 @@ int main(int argc, char *argv[]) { drm_fd = open(render_node, O_RDWR); if (drm_fd < 0) { - fprintf(stderr, "Failed to open drm render node: %m\n"); + perror("Failed to open drm render node"); return EXIT_FAILURE; } @@ -309,13 +309,12 @@ int main(int argc, char *argv[]) { struct wl_display *display = wl_display_connect(NULL); if (display == NULL) { - fprintf(stderr, "failed to create display: %m\n"); + perror("failed to create display"); return EXIT_FAILURE; } struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); - wl_display_dispatch(display); wl_display_roundtrip(display); if (dmabuf == NULL) { @@ -344,7 +343,7 @@ int main(int argc, char *argv[]) { void *data = gbm_bo_map(buffer.bo, 0, 0, buffer.width, buffer.height, GBM_BO_TRANSFER_READ, &stride, &map_data); if (!data) { - fprintf(stderr, "Failed to map gbm bo: %m\n"); + perror("Failed to map gbm bo"); return EXIT_FAILURE; } diff --git a/examples/screencopy.c b/examples/screencopy.c index cf4652f49..42123e55e 100644 --- a/examples/screencopy.c +++ b/examples/screencopy.c @@ -90,7 +90,7 @@ static struct wl_buffer *create_shm_buffer(enum wl_shm_format fmt, void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (data == MAP_FAILED) { - fprintf(stderr, "mmap failed: %m\n"); + perror("mmap failed"); close(fd); return NULL; } @@ -228,13 +228,12 @@ static void write_image(char *filename, enum wl_shm_format wl_fmt, int width, int main(int argc, char *argv[]) { struct wl_display * display = wl_display_connect(NULL); if (display == NULL) { - fprintf(stderr, "failed to create display: %m\n"); + perror("failed to create display"); return EXIT_FAILURE; } struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); - wl_display_dispatch(display); wl_display_roundtrip(display); if (shm == NULL) { diff --git a/examples/simple.c b/examples/simple.c index 8540892d6..e3af1a118 100644 --- a/examples/simple.c +++ b/examples/simple.c @@ -1,5 +1,4 @@ #define _POSIX_C_SOURCE 200112L -#include #include #include #include @@ -8,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -18,7 +18,7 @@ struct sample_state { struct wl_listener new_output; struct wl_listener new_input; struct timespec last_frame; - float color[3]; + float color[4]; int dec; }; @@ -40,6 +40,8 @@ static void output_frame_notify(struct wl_listener *listener, void *data) { struct sample_output *sample_output = wl_container_of(listener, sample_output, frame); struct sample_state *sample = sample_output->sample; + struct wlr_output *wlr_output = sample_output->output; + struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); @@ -56,12 +58,15 @@ static void output_frame_notify(struct wl_listener *listener, void *data) { sample->dec = inc; } - wlr_output_attach_render(sample_output->output, NULL); + wlr_output_attach_render(wlr_output, NULL); - glClearColor(sample->color[0], sample->color[1], sample->color[2], 1.0); - glClear(GL_COLOR_BUFFER_BIT); + struct wlr_renderer *renderer = + wlr_backend_get_renderer(wlr_output->backend); + wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); + wlr_renderer_clear(renderer, sample->color); + wlr_renderer_end(renderer); - wlr_output_commit(sample_output->output); + wlr_output_commit(wlr_output); sample->last_frame = now; } @@ -80,9 +85,9 @@ static void new_output_notify(struct wl_listener *listener, void *data) { wl_container_of(listener, sample, new_output); struct sample_output *sample_output = calloc(1, sizeof(struct sample_output)); - if (!wl_list_empty(&output->modes)) { - struct wlr_output_mode *mode = - wl_container_of(output->modes.prev, mode, link); + + struct wlr_output_mode *mode = wlr_output_preferred_mode(output); + if (mode != NULL) { wlr_output_set_mode(output, mode); } sample_output->output = output; @@ -162,7 +167,7 @@ int main(void) { wlr_log_init(WLR_DEBUG, NULL); struct wl_display *display = wl_display_create(); struct sample_state state = { - .color = { 1.0, 0.0, 0.0 }, + .color = { 1.0, 0.0, 0.0, 1.0 }, .dec = 0, .last_frame = { 0 }, .display = display diff --git a/examples/text-input.c b/examples/text-input.c index bfdbc07b4..ec75d3078 100644 --- a/examples/text-input.c +++ b/examples/text-input.c @@ -344,7 +344,6 @@ int main(int argc, char **argv) { struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); - wl_display_dispatch(display); wl_display_roundtrip(display); if (compositor == NULL) { diff --git a/examples/toplevel-decoration.c b/examples/toplevel-decoration.c index e930c4170..cb8caacec 100644 --- a/examples/toplevel-decoration.c +++ b/examples/toplevel-decoration.c @@ -203,7 +203,6 @@ int main(int argc, char **argv) { struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); - wl_display_dispatch(display); wl_display_roundtrip(display); if (compositor == NULL) { diff --git a/examples/virtual-pointer.c b/examples/virtual-pointer.c index 84af7ffdc..b91768a47 100644 --- a/examples/virtual-pointer.c +++ b/examples/virtual-pointer.c @@ -61,13 +61,12 @@ int main(int argc, char *argv[]) { } struct wl_display * display = wl_display_connect(NULL); if (display == NULL) { - fprintf(stderr, "failed to create display: %m\n"); + perror("failed to create display"); return EXIT_FAILURE; } struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, ®istry_listener, NULL); - wl_display_dispatch(display); wl_display_roundtrip(display); if (pointer_manager == NULL) { @@ -132,7 +131,6 @@ int main(int argc, char *argv[]) { zwlr_virtual_pointer_v1_frame(pointer); zwlr_virtual_pointer_v1_destroy(pointer); - wl_display_dispatch(display); return EXIT_SUCCESS; } diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index c56850791..80bc76698 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -134,7 +134,7 @@ struct wlr_drm_connector { struct wl_list link; /* - * We've asked for a state change in the kernel, and yet to recieve a + * We've asked for a state change in the kernel, and yet to receive a * notification for its completion. Currently, the kernel only has a * queue length of 1, and no way to modify your submissions after * they're sent. diff --git a/include/backend/session/session.h b/include/backend/session/session.h new file mode 100644 index 000000000..626f470a3 --- /dev/null +++ b/include/backend/session/session.h @@ -0,0 +1,8 @@ +#ifndef BACKEND_SESSION_SESSION_H +#define BACKEND_SESSION_SESSION_H + +struct wlr_session; + +void session_init(struct wlr_session *session); + +#endif diff --git a/include/backend/wayland.h b/include/backend/wayland.h index 9a8a404bb..d782584f6 100644 --- a/include/backend/wayland.h +++ b/include/backend/wayland.h @@ -38,12 +38,9 @@ struct wlr_wl_backend { struct wp_presentation *presentation; struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1; struct zwp_relative_pointer_manager_v1 *zwp_relative_pointer_manager_v1; - struct wl_seat *seat; - struct wl_pointer *pointer; - struct wl_keyboard *keyboard; + struct wlr_wl_seat *seat; struct wlr_wl_pointer *current_pointer; struct zwp_tablet_manager_v2 *tablet_manager; - char *seat_name; struct wlr_drm_format_set linux_dmabuf_v1_formats; }; @@ -107,13 +104,25 @@ struct wlr_wl_pointer { struct wl_listener output_destroy; }; +struct wlr_wl_seat { + struct wl_seat *wl_seat; + + char *name; + struct wl_touch *touch; + struct wl_pointer *pointer; + struct wl_keyboard *keyboard; +}; + struct wlr_wl_backend *get_wl_backend_from_backend(struct wlr_backend *backend); void update_wl_output_cursor(struct wlr_wl_output *output); struct wlr_wl_pointer *pointer_get_wl(struct wlr_pointer *wlr_pointer); void create_wl_pointer(struct wl_pointer *wl_pointer, struct wlr_wl_output *output); void create_wl_keyboard(struct wl_keyboard *wl_keyboard, struct wlr_wl_backend *wl); +void create_wl_touch(struct wl_touch *wl_touch, struct wlr_wl_backend *wl); struct wlr_wl_input_device *create_wl_input_device( struct wlr_wl_backend *backend, enum wlr_input_device_type type); +bool create_wl_seat(struct wl_seat *wl_seat, struct wlr_wl_backend *wl); +void destroy_wl_seats(struct wlr_wl_backend *wl); extern const struct wl_seat_listener seat_listener; diff --git a/include/render/gles2.h b/include/render/gles2.h index 4ebaf6809..af69e32ca 100644 --- a/include/render/gles2.h +++ b/include/render/gles2.h @@ -15,17 +15,6 @@ #include #include -struct wlr_gles2_procs { - PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES; - PFNGLDEBUGMESSAGECALLBACKKHRPROC glDebugMessageCallbackKHR; - PFNGLDEBUGMESSAGECONTROLKHRPROC glDebugMessageControlKHR; - PFNGLPOPDEBUGGROUPKHRPROC glPopDebugGroupKHR; - PFNGLPUSHDEBUGGROUPKHRPROC glPushDebugGroupKHR; - PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES; -}; - -extern struct wlr_gles2_procs gles2_procs; - struct wlr_gles2_pixel_format { enum wl_shm_format wl_format; GLint gl_format, gl_type; @@ -62,6 +51,15 @@ struct wlr_gles2_renderer { bool egl_image_oes; } exts; + struct { + PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES; + PFNGLDEBUGMESSAGECALLBACKKHRPROC glDebugMessageCallbackKHR; + PFNGLDEBUGMESSAGECONTROLKHRPROC glDebugMessageControlKHR; + PFNGLPOPDEBUGGROUPKHRPROC glPopDebugGroupKHR; + PFNGLPUSHDEBUGGROUPKHRPROC glPushDebugGroupKHR; + PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC glEGLImageTargetRenderbufferStorageOES; + } procs; + struct { struct { GLuint program; @@ -87,7 +85,7 @@ struct wlr_gles2_renderer { struct wlr_gles2_texture { struct wlr_texture wlr_texture; - struct wlr_egl *egl; + struct wlr_gles2_renderer *renderer; // Basically: // GL_TEXTURE_2D == mutable @@ -110,13 +108,23 @@ const struct wlr_gles2_pixel_format *get_gles2_format_from_gl( GLint gl_format, GLint gl_type, bool alpha); const enum wl_shm_format *get_gles2_wl_formats(size_t *len); +struct wlr_gles2_renderer *gles2_get_renderer( + struct wlr_renderer *wlr_renderer); struct wlr_gles2_texture *gles2_get_texture( struct wlr_texture *wlr_texture); -void push_gles2_marker(const char *file, const char *func); -void pop_gles2_marker(void); -#define PUSH_GLES2_DEBUG push_gles2_marker(_WLR_FILENAME, __func__) -#define POP_GLES2_DEBUG pop_gles2_marker() +struct wlr_texture *gles2_texture_from_pixels(struct wlr_renderer *wlr_renderer, + enum wl_shm_format wl_fmt, uint32_t stride, uint32_t width, uint32_t height, + const void *data); +struct wlr_texture *gles2_texture_from_wl_drm(struct wlr_renderer *wlr_renderer, + struct wl_resource *data); +struct wlr_texture *gles2_texture_from_dmabuf(struct wlr_renderer *wlr_renderer, + struct wlr_dmabuf_attributes *attribs); + +void push_gles2_debug_(struct wlr_gles2_renderer *renderer, + const char *file, const char *func); +#define push_gles2_debug(renderer) push_gles2_debug_(renderer, _WLR_FILENAME, __func__) +void pop_gles2_debug(struct wlr_gles2_renderer *renderer); // color engine diff --git a/include/types/wlr_xdg_shell_v6.h b/include/types/wlr_xdg_shell_v6.h deleted file mode 100644 index 4c9d1fa0e..000000000 --- a/include/types/wlr_xdg_shell_v6.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef TYPES_WLR_XDG_SHELL_V6_H -#define TYPES_WLR_XDG_SHELL_V6_H - -#include -#include -#include "xdg-shell-unstable-v6-protocol.h" - -struct wlr_xdg_positioner_v6_resource { - struct wl_resource *resource; - struct wlr_xdg_positioner_v6 attrs; -}; - -extern const struct wlr_surface_role xdg_toplevel_v6_surface_role; -extern const struct wlr_surface_role xdg_popup_v6_surface_role; - -uint32_t schedule_xdg_surface_v6_configure(struct wlr_xdg_surface_v6 *surface); -struct wlr_xdg_surface_v6 *create_xdg_surface_v6( - struct wlr_xdg_client_v6 *client, struct wlr_surface *surface, - uint32_t id); -void unmap_xdg_surface_v6(struct wlr_xdg_surface_v6 *surface); -void destroy_xdg_surface_v6(struct wlr_xdg_surface_v6 *surface); -void handle_xdg_surface_v6_commit(struct wlr_surface *wlr_surface); -void handle_xdg_surface_v6_precommit(struct wlr_surface *wlr_surface); - -void create_xdg_positioner_v6(struct wlr_xdg_client_v6 *client, uint32_t id); -struct wlr_xdg_positioner_v6_resource *get_xdg_positioner_v6_from_resource( - struct wl_resource *resource); - -void create_xdg_popup_v6(struct wlr_xdg_surface_v6 *xdg_surface, - struct wlr_xdg_surface_v6 *parent, - struct wlr_xdg_positioner_v6_resource *positioner, int32_t id); -void handle_xdg_surface_v6_popup_committed(struct wlr_xdg_surface_v6 *surface); -struct wlr_xdg_popup_grab_v6 *get_xdg_shell_v6_popup_grab_from_seat( - struct wlr_xdg_shell_v6 *shell, struct wlr_seat *seat); -void destroy_xdg_popup_v6(struct wlr_xdg_surface_v6 *surface); - -void create_xdg_toplevel_v6(struct wlr_xdg_surface_v6 *xdg_surface, - uint32_t id); -void handle_xdg_surface_v6_toplevel_committed(struct wlr_xdg_surface_v6 *surface); -void send_xdg_toplevel_v6_configure(struct wlr_xdg_surface_v6 *surface, - struct wlr_xdg_surface_v6_configure *configure); -void handle_xdg_toplevel_v6_ack_configure(struct wlr_xdg_surface_v6 *surface, - struct wlr_xdg_surface_v6_configure *configure); -bool compare_xdg_surface_v6_toplevel_state(struct wlr_xdg_toplevel_v6 *state); -void destroy_xdg_toplevel_v6(struct wlr_xdg_surface_v6 *surface); - -#endif diff --git a/include/wlr/config.h.in b/include/wlr/config.h.in index a7691ff91..77a155982 100644 --- a/include/wlr/config.h.in +++ b/include/wlr/config.h.in @@ -8,6 +8,8 @@ #mesondefine WLR_HAS_SYSTEMD #mesondefine WLR_HAS_ELOGIND +#mesondefine WLR_HAS_LIBSEAT + #mesondefine WLR_HAS_X11_BACKEND #mesondefine WLR_HAS_XWAYLAND diff --git a/include/wlr/render/dmabuf.h b/include/wlr/render/dmabuf.h index 93e50eb30..b9c548967 100644 --- a/include/wlr/render/dmabuf.h +++ b/include/wlr/render/dmabuf.h @@ -15,9 +15,9 @@ #define WLR_DMABUF_MAX_PLANES 4 enum wlr_dmabuf_attributes_flags { - WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT = 1, - WLR_DMABUF_ATTRIBUTES_FLAGS_INTERLACED = 2, - WLR_DMABUF_ATTRIBUTES_FLAGS_BOTTOM_FIRST = 4, + WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT = 1 << 0, + WLR_DMABUF_ATTRIBUTES_FLAGS_INTERLACED = 1 << 1, + WLR_DMABUF_ATTRIBUTES_FLAGS_BOTTOM_FIRST = 1 << 2, }; struct wlr_dmabuf_attributes { diff --git a/include/wlr/render/egl.h b/include/wlr/render/egl.h index d46d95aff..b23931770 100644 --- a/include/wlr/render/egl.h +++ b/include/wlr/render/egl.h @@ -15,6 +15,9 @@ #ifndef EGL_NO_X11 #define EGL_NO_X11 #endif +#ifndef EGL_NO_PLATFORM_SPECIFIC_TYPES +#define EGL_NO_PLATFORM_SPECIFIC_TYPES +#endif #include diff --git a/include/wlr/render/gles2.h b/include/wlr/render/gles2.h index 631b11a2a..5a3e9d5c7 100644 --- a/include/wlr/render/gles2.h +++ b/include/wlr/render/gles2.h @@ -21,14 +21,6 @@ struct wlr_egl *wlr_gles2_renderer_get_egl(struct wlr_renderer *renderer); bool wlr_gles2_renderer_check_ext(struct wlr_renderer *renderer, const char *ext); -struct wlr_texture *wlr_gles2_texture_from_pixels(struct wlr_egl *egl, - enum wl_shm_format wl_fmt, uint32_t stride, uint32_t width, uint32_t height, - const void *data); -struct wlr_texture *wlr_gles2_texture_from_wl_drm(struct wlr_egl *egl, - struct wl_resource *data); -struct wlr_texture *wlr_gles2_texture_from_dmabuf(struct wlr_egl *egl, - struct wlr_dmabuf_attributes *attribs); - struct wlr_gles2_texture_attribs { GLenum target; /* either GL_TEXTURE_2D or GL_TEXTURE_EXTERNAL_OES */ GLuint tex; diff --git a/include/wlr/render/interface.h b/include/wlr/render/interface.h index d53916c79..ead409afe 100644 --- a/include/wlr/render/interface.h +++ b/include/wlr/render/interface.h @@ -15,6 +15,9 @@ #ifndef EGL_NO_X11 #define EGL_NO_X11 #endif +#ifndef EGL_NO_PLATFORM_SPECIFIC_TYPES +#define EGL_NO_PLATFORM_SPECIFIC_TYPES +#endif #include #include diff --git a/include/wlr/types/wlr_data_device.h b/include/wlr/types/wlr_data_device.h index 555eeab01..87c578aed 100644 --- a/include/wlr/types/wlr_data_device.h +++ b/include/wlr/types/wlr_data_device.h @@ -136,7 +136,6 @@ struct wlr_drag { struct wl_signal destroy; } events; - struct wl_listener point_destroy; struct wl_listener source_destroy; struct wl_listener seat_client_destroy; struct wl_listener icon_destroy; diff --git a/include/wlr/types/wlr_export_dmabuf_v1.h b/include/wlr/types/wlr_export_dmabuf_v1.h index 817bb1a05..344e6cbf9 100644 --- a/include/wlr/types/wlr_export_dmabuf_v1.h +++ b/include/wlr/types/wlr_export_dmabuf_v1.h @@ -29,12 +29,11 @@ struct wlr_export_dmabuf_frame_v1 { struct wlr_export_dmabuf_manager_v1 *manager; struct wl_list link; // wlr_export_dmabuf_manager_v1::frames - struct wlr_dmabuf_attributes attribs; struct wlr_output *output; bool cursor_locked; - struct wl_listener output_precommit; + struct wl_listener output_commit; }; struct wlr_export_dmabuf_manager_v1 *wlr_export_dmabuf_manager_v1_create( diff --git a/include/wlr/types/wlr_foreign_toplevel_management_v1.h b/include/wlr/types/wlr_foreign_toplevel_management_v1.h index 90d88637c..6ea9d47fd 100644 --- a/include/wlr/types/wlr_foreign_toplevel_management_v1.h +++ b/include/wlr/types/wlr_foreign_toplevel_management_v1.h @@ -50,6 +50,7 @@ struct wlr_foreign_toplevel_handle_v1 { char *title; char *app_id; + struct wlr_foreign_toplevel_handle_v1 *parent; struct wl_list outputs; // wlr_foreign_toplevel_v1_output uint32_t state; // wlr_foreign_toplevel_v1_state @@ -104,6 +105,12 @@ struct wlr_foreign_toplevel_manager_v1 *wlr_foreign_toplevel_manager_v1_create( struct wlr_foreign_toplevel_handle_v1 *wlr_foreign_toplevel_handle_v1_create( struct wlr_foreign_toplevel_manager_v1 *manager); +/* Destroy the given toplevel handle, sending the closed event to any + * client. Also, if the destroyed toplevel is set as a parent of any + * other valid toplevel, clients still holding a handle to both are + * sent a parent signal with NULL parent. If this is not desired, the + * caller should ensure that any child toplevels are destroyed before + * the parent. */ void wlr_foreign_toplevel_handle_v1_destroy( struct wlr_foreign_toplevel_handle_v1 *toplevel); @@ -126,4 +133,14 @@ void wlr_foreign_toplevel_handle_v1_set_activated( void wlr_foreign_toplevel_handle_v1_set_fullscreen( struct wlr_foreign_toplevel_handle_v1* toplevel, bool fullscreen); +/* Set the parent of a toplevel. If the parent changed from its previous + * value, also sends a parent event to all clients that hold handles to + * both toplevel and parent (no message is sent to clients that have + * previously destroyed their parent handle). NULL is allowed as the + * parent, meaning no parent exists. */ +void wlr_foreign_toplevel_handle_v1_set_parent( + struct wlr_foreign_toplevel_handle_v1 *toplevel, + struct wlr_foreign_toplevel_handle_v1 *parent); + + #endif diff --git a/include/wlr/types/wlr_gamma_control_v1.h b/include/wlr/types/wlr_gamma_control_v1.h index 290a86e94..b247bae34 100644 --- a/include/wlr/types/wlr_gamma_control_v1.h +++ b/include/wlr/types/wlr_gamma_control_v1.h @@ -21,6 +21,10 @@ struct wlr_gamma_control_v1 { struct wlr_output *output; struct wl_list link; + uint16_t *table; + size_t ramp_size; + + struct wl_listener output_commit_listener; struct wl_listener output_destroy_listener; void *data; diff --git a/include/wlr/types/wlr_keyboard.h b/include/wlr/types/wlr_keyboard.h index c3e360548..c1a19a8eb 100644 --- a/include/wlr/types/wlr_keyboard.h +++ b/include/wlr/types/wlr_keyboard.h @@ -18,22 +18,22 @@ #define WLR_LED_COUNT 3 enum wlr_keyboard_led { - WLR_LED_NUM_LOCK = 1, - WLR_LED_CAPS_LOCK = 2, - WLR_LED_SCROLL_LOCK = 4, + WLR_LED_NUM_LOCK = 1 << 0, + WLR_LED_CAPS_LOCK = 1 << 1, + WLR_LED_SCROLL_LOCK = 1 << 2, }; #define WLR_MODIFIER_COUNT 8 enum wlr_keyboard_modifier { - WLR_MODIFIER_SHIFT = 1, - WLR_MODIFIER_CAPS = 2, - WLR_MODIFIER_CTRL = 4, - WLR_MODIFIER_ALT = 8, - WLR_MODIFIER_MOD2 = 16, - WLR_MODIFIER_MOD3 = 32, - WLR_MODIFIER_LOGO = 64, - WLR_MODIFIER_MOD5 = 128, + WLR_MODIFIER_SHIFT = 1 << 0, + WLR_MODIFIER_CAPS = 1 << 1, + WLR_MODIFIER_CTRL = 1 << 2, + WLR_MODIFIER_ALT = 1 << 3, + WLR_MODIFIER_MOD2 = 1 << 4, + WLR_MODIFIER_MOD3 = 1 << 5, + WLR_MODIFIER_LOGO = 1 << 6, + WLR_MODIFIER_MOD5 = 1 << 7, }; #define WLR_KEYBOARD_KEYS_CAP 32 diff --git a/include/wlr/types/wlr_layer_shell_v1.h b/include/wlr/types/wlr_layer_shell_v1.h index b6a0a1352..f13eadb7f 100644 --- a/include/wlr/types/wlr_layer_shell_v1.h +++ b/include/wlr/types/wlr_layer_shell_v1.h @@ -138,6 +138,10 @@ struct wlr_layer_surface_v1 *wlr_layer_surface_v1_from_wlr_surface( void wlr_layer_surface_v1_for_each_surface(struct wlr_layer_surface_v1 *surface, wlr_surface_iterator_func_t iterator, void *user_data); +/* Calls the iterator function for each popup of this surface */ +void wlr_layer_surface_v1_for_each_popup(struct wlr_layer_surface_v1 *surface, + wlr_surface_iterator_func_t iterator, void *user_data); + /** * Find a surface within this layer-surface tree at the given surface-local * coordinates. Returns the surface and coordinates in the leaf surface diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 16b1cf98d..75770d743 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -163,7 +163,7 @@ struct wlr_output { // Emitted right before commit struct wl_signal precommit; // wlr_output_event_precommit // Emitted right after commit - struct wl_signal commit; + struct wl_signal commit; // wlr_output_event_commit // Emitted right after the buffer has been presented to the user struct wl_signal present; // wlr_output_event_present struct wl_signal enable; @@ -200,6 +200,12 @@ struct wlr_output_event_precommit { struct timespec *when; }; +struct wlr_output_event_commit { + struct wlr_output *output; + uint32_t committed; // bitmask of enum wlr_output_state_field + struct timespec *when; +}; + enum wlr_output_present_flag { // The presentation was synchronized to the "vertical retrace" by the // display hardware such that tearing does not happen. @@ -388,8 +394,7 @@ size_t wlr_output_get_gamma_size(struct wlr_output *output); void wlr_output_set_gamma(struct wlr_output *output, size_t size, const uint16_t *r, const uint16_t *g, const uint16_t *b); /** - * Exports the output's current back-buffer as a DMA-BUF (ie. the buffer that - * will be displayed on next commit). + * Exports the last committed buffer as a DMA-BUF. * * The caller is responsible for cleaning up the DMA-BUF attributes. */ diff --git a/include/wlr/types/wlr_output_layout.h b/include/wlr/types/wlr_output_layout.h index cdbc446f9..51c1e4cab 100644 --- a/include/wlr/types/wlr_output_layout.h +++ b/include/wlr/types/wlr_output_layout.h @@ -124,10 +124,10 @@ struct wlr_output *wlr_output_layout_get_center_output( struct wlr_output_layout *layout); enum wlr_direction { - WLR_DIRECTION_UP = 1, - WLR_DIRECTION_DOWN = 2, - WLR_DIRECTION_LEFT = 4, - WLR_DIRECTION_RIGHT = 8, + WLR_DIRECTION_UP = 1 << 0, + WLR_DIRECTION_DOWN = 1 << 1, + WLR_DIRECTION_LEFT = 1 << 2, + WLR_DIRECTION_RIGHT = 1 << 3, }; /** diff --git a/include/wlr/types/wlr_output_power_management_v1.h b/include/wlr/types/wlr_output_power_management_v1.h index 2ceb86002..23ce814a0 100644 --- a/include/wlr/types/wlr_output_power_management_v1.h +++ b/include/wlr/types/wlr_output_power_management_v1.h @@ -25,7 +25,7 @@ struct wlr_output_power_v1 { struct wl_list link; struct wl_listener output_destroy_listener; - struct wl_listener output_enable_listener; + struct wl_listener output_commit_listener; void *data; }; diff --git a/include/wlr/types/wlr_screencopy_v1.h b/include/wlr/types/wlr_screencopy_v1.h index b778dd414..1c45f9057 100644 --- a/include/wlr/types/wlr_screencopy_v1.h +++ b/include/wlr/types/wlr_screencopy_v1.h @@ -53,6 +53,7 @@ struct wlr_screencopy_frame_v1 { struct wlr_output *output; struct wl_listener output_precommit; + struct wl_listener output_commit; struct wl_listener output_destroy; struct wl_listener output_enable; diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h index 7b47da089..332ab0d30 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -409,6 +409,12 @@ void wlr_seat_pointer_notify_enter(struct wlr_seat *wlr_seat, */ void wlr_seat_pointer_notify_clear_focus(struct wlr_seat *wlr_seat); +/** + * Warp the pointer of this seat to the given surface-local coordinates, without + * generating motion events. + */ +void wlr_seat_pointer_warp(struct wlr_seat *wlr_seat, double sx, double sy); + /** * Notify the seat of motion over the given surface. Pass surface-local * coordinates where the pointer motion occurred. Defers to any grab of the diff --git a/include/wlr/types/wlr_xdg_shell.h b/include/wlr/types/wlr_xdg_shell.h index 4bd286ceb..0b9167d2c 100644 --- a/include/wlr/types/wlr_xdg_shell.h +++ b/include/wlr/types/wlr_xdg_shell.h @@ -48,8 +48,6 @@ struct wlr_xdg_client { }; struct wlr_xdg_positioner { - struct wl_resource *resource; - struct wlr_box anchor_rect; enum xdg_positioner_anchor anchor; enum xdg_positioner_gravity gravity; @@ -124,6 +122,7 @@ struct wlr_xdg_toplevel { struct wlr_xdg_toplevel_state client_pending; struct wlr_xdg_toplevel_state server_pending; + struct wlr_xdg_toplevel_state last_acked; struct wlr_xdg_toplevel_state current; char *title; diff --git a/include/wlr/types/wlr_xdg_shell_v6.h b/include/wlr/types/wlr_xdg_shell_v6.h deleted file mode 100644 index 5a6cf72f9..000000000 --- a/include/wlr/types/wlr_xdg_shell_v6.h +++ /dev/null @@ -1,374 +0,0 @@ -/* - * This protocol is obsolete and will be removed in a future version. The - * recommended replacement is xdg-shell. - */ - -/* - * This an unstable interface of wlroots. No guarantees are made regarding the - * future consistency of this API. - */ -#ifndef WLR_USE_UNSTABLE -#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" -#endif - -#ifndef WLR_TYPES_WLR_XDG_SHELL_V6_H -#define WLR_TYPES_WLR_XDG_SHELL_V6_H - -#include -#include -#include -#include "xdg-shell-unstable-v6-protocol.h" - -/** - * An interface enabling clients to turn their wl_surfaces into windows in a - * desktop environment. - */ -struct wlr_xdg_shell_v6 { - struct wl_global *global; - struct wl_list clients; - struct wl_list popup_grabs; - uint32_t ping_timeout; - - struct wl_listener display_destroy; - - struct { - /** - * The `new_surface` event signals that a client has requested to - * create a new shell surface. At this point, the surface is ready to - * be configured but is not mapped or ready receive input events. The - * surface will be ready to be managed on the `map` event. - */ - struct wl_signal new_surface; - struct wl_signal destroy; - } events; - - void *data; -}; - -struct wlr_xdg_client_v6 { - struct wlr_xdg_shell_v6 *shell; - struct wl_resource *resource; - struct wl_client *client; - struct wl_list surfaces; - - struct wl_list link; // wlr_xdg_shell_v6::clients - - uint32_t ping_serial; - struct wl_event_source *ping_timer; -}; - -struct wlr_xdg_positioner_v6 { - struct wlr_box anchor_rect; - enum zxdg_positioner_v6_anchor anchor; - enum zxdg_positioner_v6_gravity gravity; - enum zxdg_positioner_v6_constraint_adjustment constraint_adjustment; - - struct { - int32_t width, height; - } size; - - struct { - int32_t x, y; - } offset; -}; - -struct wlr_xdg_popup_v6 { - struct wlr_xdg_surface_v6 *base; - struct wl_list link; - - struct wl_resource *resource; - bool committed; - struct wlr_xdg_surface_v6 *parent; - struct wlr_seat *seat; - - // Position of the popup relative to the upper left corner of the window - // geometry of the parent surface - struct wlr_box geometry; - - struct wlr_xdg_positioner_v6 positioner; - - struct wl_list grab_link; // wlr_xdg_popup_grab_v6::popups -}; - -// each seat gets a popup grab -struct wlr_xdg_popup_grab_v6 { - struct wl_client *client; - struct wlr_seat_pointer_grab pointer_grab; - struct wlr_seat_keyboard_grab keyboard_grab; - struct wlr_seat_touch_grab touch_grab; - struct wlr_seat *seat; - struct wl_list popups; - struct wl_list link; // wlr_xdg_shell_v6::popup_grabs - struct wl_listener seat_destroy; -}; - -enum wlr_xdg_surface_v6_role { - WLR_XDG_SURFACE_V6_ROLE_NONE, - WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL, - WLR_XDG_SURFACE_V6_ROLE_POPUP, -}; - -struct wlr_xdg_toplevel_v6_state { - bool maximized, fullscreen, resizing, activated; - uint32_t width, height; - uint32_t max_width, max_height; - uint32_t min_width, min_height; - - // Since the fullscreen request may be made before the toplevel's surface - // is mapped, this is used to store the requested fullscreen output (if - // any) for wlr_xdg_toplevel_v6::client_pending. - struct wlr_output *fullscreen_output; - struct wl_listener fullscreen_output_destroy; -}; - -/** - * An xdg-surface is a user interface element requiring management by the - * compositor. An xdg-surface alone isn't useful, a role should be assigned to - * it in order to map it. - * - * When a surface has a role and is ready to be displayed, the `map` event is - * emitted. When a surface should no longer be displayed, the `unmap` event is - * emitted. The `unmap` event is guaranteed to be emitted before the `destroy` - * event if the view is destroyed when mapped. - */ -struct wlr_xdg_toplevel_v6 { - struct wl_resource *resource; - struct wlr_xdg_surface_v6 *base; - struct wlr_xdg_surface_v6 *parent; - bool added; - - struct wlr_xdg_toplevel_v6_state client_pending; - struct wlr_xdg_toplevel_v6_state server_pending; - struct wlr_xdg_toplevel_v6_state current; - - char *title; - char *app_id; - - struct { - struct wl_signal request_maximize; - struct wl_signal request_fullscreen; - struct wl_signal request_minimize; - struct wl_signal request_move; - struct wl_signal request_resize; - struct wl_signal request_show_window_menu; - struct wl_signal set_parent; - struct wl_signal set_title; - struct wl_signal set_app_id; - } events; -}; - -struct wlr_xdg_surface_v6_configure { - struct wl_list link; // wlr_xdg_surface_v6::configure_list - uint32_t serial; - - struct wlr_xdg_toplevel_v6_state *toplevel_state; -}; - -struct wlr_xdg_surface_v6 { - struct wlr_xdg_client_v6 *client; - struct wl_resource *resource; - struct wlr_surface *surface; - struct wl_list link; // wlr_xdg_client_v6::surfaces - enum wlr_xdg_surface_v6_role role; - - union { - struct wlr_xdg_toplevel_v6 *toplevel; - struct wlr_xdg_popup_v6 *popup; - }; - - struct wl_list popups; // wlr_xdg_popup_v6::link - - bool added, configured, mapped; - uint32_t configure_serial; - struct wl_event_source *configure_idle; - uint32_t configure_next_serial; - struct wl_list configure_list; - - bool has_next_geometry; - struct wlr_box next_geometry; - struct wlr_box geometry; - - struct wl_listener surface_destroy; - struct wl_listener surface_commit; - - struct { - struct wl_signal destroy; - struct wl_signal ping_timeout; - struct wl_signal new_popup; - /** - * The `map` event signals that the shell surface is ready to be - * managed by the compositor and rendered on the screen. At this point, - * the surface has configured its properties, has had the opportunity - * to bind to the seat to receive input events, and has a buffer that - * is ready to be rendered. You can now safely add this surface to a - * list of views. - */ - struct wl_signal map; - /** - * The `unmap` event signals that the surface is no longer in a state - * where it should be shown on the screen. This might happen if the - * surface no longer has a displayable buffer because either the - * surface has been hidden or is about to be destroyed. - */ - struct wl_signal unmap; - } events; - - void *data; -}; - -struct wlr_xdg_toplevel_v6_move_event { - struct wlr_xdg_surface_v6 *surface; - struct wlr_seat_client *seat; - uint32_t serial; -}; - -struct wlr_xdg_toplevel_v6_resize_event { - struct wlr_xdg_surface_v6 *surface; - struct wlr_seat_client *seat; - uint32_t serial; - uint32_t edges; -}; - -struct wlr_xdg_toplevel_v6_set_fullscreen_event { - struct wlr_xdg_surface_v6 *surface; - bool fullscreen; - struct wlr_output *output; -}; - -struct wlr_xdg_toplevel_v6_show_window_menu_event { - struct wlr_xdg_surface_v6 *surface; - struct wlr_seat_client *seat; - uint32_t serial; - uint32_t x, y; -}; - -struct wlr_xdg_shell_v6 *wlr_xdg_shell_v6_create(struct wl_display *display); - -/** - * Send a ping to the surface. If the surface does not respond in a reasonable - * amount of time, the ping_timeout event will be emitted. - */ -void wlr_xdg_surface_v6_ping(struct wlr_xdg_surface_v6 *surface); - -/** - * Request that this toplevel surface be the given size. Returns the associated - * configure serial. - */ -uint32_t wlr_xdg_toplevel_v6_set_size(struct wlr_xdg_surface_v6 *surface, - uint32_t width, uint32_t height); - -/** - * Request that this toplevel surface show itself in an activated or deactivated - * state. Returns the associated configure serial. - */ -uint32_t wlr_xdg_toplevel_v6_set_activated(struct wlr_xdg_surface_v6 *surface, - bool activated); - -/** - * Request that this toplevel surface consider itself maximized or not - * maximized. Returns the associated configure serial. - */ -uint32_t wlr_xdg_toplevel_v6_set_maximized(struct wlr_xdg_surface_v6 *surface, - bool maximized); - -/** - * Request that this toplevel surface consider itself fullscreen or not - * fullscreen. Returns the associated configure serial. - */ -uint32_t wlr_xdg_toplevel_v6_set_fullscreen(struct wlr_xdg_surface_v6 *surface, - bool fullscreen); - -/** - * Request that this toplevel surface consider itself to be resizing or not - * resizing. Returns the associated configure serial. - */ -uint32_t wlr_xdg_toplevel_v6_set_resizing(struct wlr_xdg_surface_v6 *surface, - bool resizing); - -/** - * Request that this xdg surface closes. - */ -void wlr_xdg_surface_v6_send_close(struct wlr_xdg_surface_v6 *surface); - -/** - * Find a surface within this xdg-surface tree at the given surface-local - * coordinates. Returns the surface and coordinates in the leaf surface - * coordinate system or NULL if no surface is found at that location. - */ -struct wlr_surface *wlr_xdg_surface_v6_surface_at( - struct wlr_xdg_surface_v6 *surface, double sx, double sy, - double *sub_x, double *sub_y); - -/** - * Get the geometry for this positioner based on the anchor rect, gravity, and - * size of this positioner. - */ -struct wlr_box wlr_xdg_positioner_v6_get_geometry( - struct wlr_xdg_positioner_v6 *positioner); - -/** - * Get the anchor point for this popup in the toplevel parent's coordinate system. - */ -void wlr_xdg_popup_v6_get_anchor_point(struct wlr_xdg_popup_v6 *popup, - int *toplevel_sx, int *toplevel_sy); - -/** - * Convert the given coordinates in the popup coordinate system to the toplevel - * surface coordinate system. - */ -void wlr_xdg_popup_v6_get_toplevel_coords(struct wlr_xdg_popup_v6 *popup, - int popup_sx, int popup_sy, int *toplevel_sx, int *toplevel_sy); - -/** - * Set the geometry of this popup to unconstrain it according to its - * xdg-positioner rules. The box should be in the popup's root toplevel parent - * surface coordinate system. - */ -void wlr_xdg_popup_v6_unconstrain_from_box(struct wlr_xdg_popup_v6 *popup, - struct wlr_box *toplevel_sx_box); - -/** - Invert the right/left anchor and gravity for this positioner. This can be - used to "flip" the positioner around the anchor rect in the x direction. - */ -void wlr_positioner_v6_invert_x( - struct wlr_xdg_positioner_v6 *positioner); - -/** - Invert the top/bottom anchor and gravity for this positioner. This can be - used to "flip" the positioner around the anchor rect in the y direction. - */ -void wlr_positioner_v6_invert_y( - struct wlr_xdg_positioner_v6 *positioner); - -bool wlr_surface_is_xdg_surface_v6(struct wlr_surface *surface); - -struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6_from_wlr_surface( - struct wlr_surface *surface); - -/** - * Get the surface geometry. - * This is either the geometry as set by the client, or defaulted to the bounds - * of the surface + the subsurfaces (as specified by the protocol). - * - * The x and y value can be <0 - */ -void wlr_xdg_surface_v6_get_geometry(struct wlr_xdg_surface_v6 *surface, struct wlr_box *box); - -/** - * Call `iterator` on each surface and popup in the xdg-surface tree, with the - * surface's position relative to the root xdg-surface. The function is called - * from root to leaves (in rendering order). - */ -void wlr_xdg_surface_v6_for_each_surface(struct wlr_xdg_surface_v6 *surface, - wlr_surface_iterator_func_t iterator, void *user_data); - -/** - * Call `iterator` on each popup in the xdg-surface tree, with the popup's - * position relative to the root xdg-surface. The function is called from root - * to leaves (in rendering order). - */ -void wlr_xdg_surface_v6_for_each_popup(struct wlr_xdg_surface_v6 *surface, - wlr_surface_iterator_func_t iterator, void *user_data); - -#endif diff --git a/include/wlr/util/edges.h b/include/wlr/util/edges.h index bf1eb1e72..90a1167ab 100644 --- a/include/wlr/util/edges.h +++ b/include/wlr/util/edges.h @@ -19,10 +19,10 @@ enum wlr_edges { WLR_EDGE_NONE = 0, - WLR_EDGE_TOP = 1, - WLR_EDGE_BOTTOM = 2, - WLR_EDGE_LEFT = 4, - WLR_EDGE_RIGHT = 8, + WLR_EDGE_TOP = 1 << 0, + WLR_EDGE_BOTTOM = 1 << 1, + WLR_EDGE_LEFT = 1 << 2, + WLR_EDGE_RIGHT = 1 << 3, }; #endif diff --git a/include/wlr/xwayland.h b/include/wlr/xwayland.h index 03201718c..8de9ed07e 100644 --- a/include/wlr/xwayland.h +++ b/include/wlr/xwayland.h @@ -84,7 +84,6 @@ struct wlr_xwayland { struct wl_listener server_ready; struct wl_listener server_destroy; - struct wl_listener client_destroy; struct wl_listener seat_destroy; void *data; @@ -173,6 +172,7 @@ struct wlr_xwayland_surface { bool modal; bool fullscreen; bool maximized_vert, maximized_horz; + bool minimized; bool has_alpha; @@ -181,6 +181,7 @@ struct wlr_xwayland_surface { struct wl_signal request_configure; struct wl_signal request_move; struct wl_signal request_resize; + struct wl_signal request_minimize; struct wl_signal request_maximize; struct wl_signal request_fullscreen; struct wl_signal request_activate; @@ -196,6 +197,7 @@ struct wlr_xwayland_surface { struct wl_signal set_hints; struct wl_signal set_decorations; struct wl_signal set_override_redirect; + struct wl_signal set_geometry; struct wl_signal ping_timeout; } events; @@ -221,6 +223,11 @@ struct wlr_xwayland_resize_event { uint32_t edges; }; +struct wlr_xwayland_minimize_event { + struct wlr_xwayland_surface *surface; + bool minimize; +}; + struct wlr_xwayland_server *wlr_xwayland_server_create( struct wl_display *display, struct wlr_xwayland_server_options *options); void wlr_xwayland_server_destroy(struct wlr_xwayland_server *server); @@ -250,6 +257,9 @@ void wlr_xwayland_surface_configure(struct wlr_xwayland_surface *surface, void wlr_xwayland_surface_close(struct wlr_xwayland_surface *surface); +void wlr_xwayland_surface_set_minimized(struct wlr_xwayland_surface *surface, + bool minimized); + void wlr_xwayland_surface_set_maximized(struct wlr_xwayland_surface *surface, bool maximized); diff --git a/include/xcursor/xcursor.h b/include/xcursor/xcursor.h index 62e232202..285f4de66 100644 --- a/include/xcursor/xcursor.h +++ b/include/xcursor/xcursor.h @@ -26,8 +26,10 @@ #ifndef XCURSOR_H #define XCURSOR_H -typedef int XcursorBool; -typedef unsigned int XcursorUInt; +#include + +typedef int XcursorBool; +typedef uint32_t XcursorUInt; typedef XcursorUInt XcursorDim; typedef XcursorUInt XcursorPixel; diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h index 2569092fa..362b4cef0 100644 --- a/include/xwayland/xwm.h +++ b/include/xwayland/xwm.h @@ -40,11 +40,14 @@ enum atom_name { NET_ACTIVE_WINDOW, NET_WM_MOVERESIZE, NET_SUPPORTING_WM_CHECK, + NET_WM_STATE_FOCUSED, NET_WM_STATE_MODAL, NET_WM_STATE_FULLSCREEN, NET_WM_STATE_MAXIMIZED_VERT, NET_WM_STATE_MAXIMIZED_HORZ, + NET_WM_STATE_HIDDEN, NET_WM_PING, + WM_CHANGE_STATE, WM_STATE, CLIPBOARD, PRIMARY, @@ -125,6 +128,7 @@ struct wlr_xwm { #if WLR_HAS_XCB_ERRORS xcb_errors_context_t *errors_context; #endif + unsigned int last_focus_seq; struct wl_listener compositor_new_surface; struct wl_listener compositor_destroy; diff --git a/meson.build b/meson.build index 749a16da4..8bb301790 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'wlroots', 'c', - version: '0.10.0', + version: '0.12.0', license: 'MIT', meson_version: '>=0.54.0', default_options: [ @@ -15,7 +15,7 @@ project( # necessary for bugfix releases. Increasing soversion is required because # wlroots never guarantees ABI stability -- only API stability is guaranteed # between minor releases. -soversion = 5 +soversion = 7 add_project_arguments([ '-DWLR_USE_UNSTABLE', @@ -82,6 +82,7 @@ endif conf_data = configuration_data() conf_data.set10('WLR_HAS_SYSTEMD', false) conf_data.set10('WLR_HAS_ELOGIND', false) +conf_data.set10('WLR_HAS_LIBSEAT', false) conf_data.set10('WLR_HAS_X11_BACKEND', false) conf_data.set10('WLR_HAS_XWAYLAND', false) conf_data.set10('WLR_HAS_XCB_ERRORS', false) @@ -178,6 +179,7 @@ meson.override_dependency('wlroots', wlroots) summary({ 'systemd': conf_data.get('WLR_HAS_SYSTEMD', 0), 'elogind': conf_data.get('WLR_HAS_ELOGIND', 0), + 'libseat': conf_data.get('WLR_HAS_LIBSEAT', 0), 'xwayland': conf_data.get('WLR_HAS_XWAYLAND', 0), 'x11_backend': conf_data.get('WLR_HAS_X11_BACKEND', 0), 'xcb-icccm': conf_data.get('WLR_HAS_XCB_ICCCM', 0), diff --git a/meson_options.txt b/meson_options.txt index 894113e7a..382ef2a66 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,5 +1,6 @@ option('logind', type: 'feature', value: 'auto', description: 'Enable support for rootless session via logind') option('logind-provider', type: 'combo', choices: ['auto', 'systemd', 'elogind'], value: 'auto', description: 'Provider of logind support library') +option('libseat', type: 'feature', value: 'auto', description: 'Enable support for rootless session via libseat') 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('xwayland', type: 'feature', value: 'auto', yield: true, description: 'Enable support for X11 applications') diff --git a/protocol/meson.build b/protocol/meson.build index 2369b70e3..b9b74ca57 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -28,7 +28,6 @@ protocols = { 'text-input-unstable-v3': wl_protocol_dir / 'unstable/text-input/text-input-unstable-v3.xml', 'xdg-decoration-unstable-v1': wl_protocol_dir / 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml', 'xdg-output-unstable-v1': wl_protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml', - 'xdg-shell-unstable-v6': wl_protocol_dir / 'unstable/xdg-shell/xdg-shell-unstable-v6.xml', # Other protocols 'gtk-primary-selection': 'gtk-primary-selection.xml', 'kde-idle': 'idle.xml', diff --git a/protocol/wlr-foreign-toplevel-management-unstable-v1.xml b/protocol/wlr-foreign-toplevel-management-unstable-v1.xml index a97738f8a..108133715 100644 --- a/protocol/wlr-foreign-toplevel-management-unstable-v1.xml +++ b/protocol/wlr-foreign-toplevel-management-unstable-v1.xml @@ -25,7 +25,7 @@ THIS SOFTWARE. - + The purpose of this protocol is to enable the creation of taskbars and docks by providing them with a list of opened applications and @@ -68,7 +68,7 @@ - + A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel window. Each app may have multiple opened toplevels. @@ -255,5 +255,16 @@ actually changes, this will be indicated by the state event. + + + + + + This event is emitted whenever the parent of the toplevel changes. + + No event is emitted when the parent handle is destroyed by the client. + + + diff --git a/protocol/wlr-output-management-unstable-v1.xml b/protocol/wlr-output-management-unstable-v1.xml index 35f7ca4e3..cadc45fb2 100644 --- a/protocol/wlr-output-management-unstable-v1.xml +++ b/protocol/wlr-output-management-unstable-v1.xml @@ -39,7 +39,7 @@ interface version number is reset. - + This interface is a manager that allows reading and writing the current output device configuration. @@ -125,7 +125,7 @@ - + A head is an output device. The difference between a wl_output object and a head is that heads are advertised even if they are turned off. A head @@ -257,9 +257,80 @@ resources associated with it. + + + + + This event describes the manufacturer of the head. + + This must report the same make as the wl_output interface does in its + geometry event. + + Together with the model and serial_number events the purpose is to + allow clients to recognize heads from previous sessions and for example + load head-specific configurations back. + + It is not guaranteed this event will be ever sent. A reason for that + can be that the compositor does not have information about the make of + the head or the definition of a make is not sensible in the current + setup, for example in a virtual session. Clients can still try to + identify the head by available information from other events but should + be aware that there is an increased risk of false positives. + + It is not recommended to display the make string in UI to users. For + that the string provided by the description event should be preferred. + + + + + + + This event describes the model of the head. + + This must report the same model as the wl_output interface does in its + geometry event. + + Together with the make and serial_number events the purpose is to + allow clients to recognize heads from previous sessions and for example + load head-specific configurations back. + + It is not guaranteed this event will be ever sent. A reason for that + can be that the compositor does not have information about the model of + the head or the definition of a model is not sensible in the current + setup, for example in a virtual session. Clients can still try to + identify the head by available information from other events but should + be aware that there is an increased risk of false positives. + + It is not recommended to display the model string in UI to users. For + that the string provided by the description event should be preferred. + + + + + + + This event describes the serial number of the head. + + Together with the make and model events the purpose is to allow clients + to recognize heads from previous sessions and for example load head- + specific configurations back. + + It is not guaranteed this event will be ever sent. A reason for that + can be that the compositor does not have information about the serial + number of the head or the definition of a serial number is not sensible + in the current setup. Clients can still try to identify the head by + available information from other events but should be aware that there + is an increased risk of false positives. + + It is not recommended to display the serial_number string in UI to + users. For that the string provided by the description event should be + preferred. + + + - + This object describes an output mode. @@ -305,7 +376,7 @@ - + This object is used by the client to describe a full output configuration. @@ -423,7 +494,7 @@ - + This object is used by the client to update a single head's configuration. diff --git a/render/egl.c b/render/egl.c index 797d8f15a..f99b37f90 100644 --- a/render/egl.c +++ b/render/egl.c @@ -459,7 +459,20 @@ void wlr_egl_save_context(struct wlr_egl_context *context) { } bool wlr_egl_restore_context(struct wlr_egl_context *context) { - return eglMakeCurrent(context->display, context->draw_surface, + // If the saved context is a null-context, we must use the current + // display instead of the saved display because eglMakeCurrent() can't + // handle EGL_NO_DISPLAY. + EGLDisplay display = context->display == EGL_NO_DISPLAY ? + eglGetCurrentDisplay() : context->display; + + // If the current display is also EGL_NO_DISPLAY, we assume that there + // is currently no context set and no action needs to be taken to unset + // the context. + if (display == EGL_NO_DISPLAY) { + return true; + } + + return eglMakeCurrent(display, context->draw_surface, context->read_surface, context->context); } diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c index eefa745b3..c0ab2c072 100644 --- a/render/gles2/renderer.c +++ b/render/gles2/renderer.c @@ -21,11 +21,9 @@ static const GLfloat verts[] = { 0, 1, // bottom left }; -struct wlr_gles2_procs gles2_procs = {0}; - static const struct wlr_renderer_impl renderer_impl; -static struct wlr_gles2_renderer *gles2_get_renderer( +struct wlr_gles2_renderer *gles2_get_renderer( struct wlr_renderer *wlr_renderer) { assert(wlr_renderer->impl == &renderer_impl); return (struct wlr_gles2_renderer *)wlr_renderer; @@ -43,7 +41,7 @@ static void gles2_begin(struct wlr_renderer *wlr_renderer, uint32_t width, struct wlr_gles2_renderer *renderer = gles2_get_renderer_in_context(wlr_renderer); - PUSH_GLES2_DEBUG; + push_gles2_debug(renderer); glViewport(0, 0, width, height); renderer->viewport_width = width; @@ -58,7 +56,7 @@ static void gles2_begin(struct wlr_renderer *wlr_renderer, uint32_t width, // XXX: maybe we should save output projection and remove some of the need // for users to sling matricies themselves - POP_GLES2_DEBUG; + pop_gles2_debug(renderer); } static void gles2_end(struct wlr_renderer *wlr_renderer) { @@ -68,12 +66,13 @@ static void gles2_end(struct wlr_renderer *wlr_renderer) { static void gles2_clear(struct wlr_renderer *wlr_renderer, const float color[static 4]) { - gles2_get_renderer_in_context(wlr_renderer); + struct wlr_gles2_renderer *renderer = + gles2_get_renderer_in_context(wlr_renderer); - PUSH_GLES2_DEBUG; + push_gles2_debug(renderer); glClearColor(color[0], color[1], color[2], color[3]); glClear(GL_COLOR_BUFFER_BIT); - POP_GLES2_DEBUG; + pop_gles2_debug(renderer); } static void gles2_scissor(struct wlr_renderer *wlr_renderer, @@ -81,7 +80,7 @@ static void gles2_scissor(struct wlr_renderer *wlr_renderer, struct wlr_gles2_renderer *renderer = gles2_get_renderer_in_context(wlr_renderer); - PUSH_GLES2_DEBUG; + push_gles2_debug(renderer); if (box != NULL) { struct wlr_box gl_box; wlr_box_transform(&gl_box, box, WL_OUTPUT_TRANSFORM_FLIPPED_180, @@ -92,7 +91,7 @@ static void gles2_scissor(struct wlr_renderer *wlr_renderer, } else { glDisable(GL_SCISSOR_TEST); } - POP_GLES2_DEBUG; + pop_gles2_debug(renderer); } static void gles2_color_config(struct wlr_renderer *wlr_renderer, struct wlr_color_config *color) { @@ -139,7 +138,7 @@ static bool gles2_render_subtexture_with_matrix( float transposition[9]; wlr_matrix_transpose(transposition, matrix); - PUSH_GLES2_DEBUG; + push_gles2_debug(renderer); glActiveTexture(GL_TEXTURE0); glBindTexture(texture->target, texture->tex); @@ -186,7 +185,7 @@ static bool gles2_render_subtexture_with_matrix( glBindTexture(texture->target, 0); - POP_GLES2_DEBUG; + pop_gles2_debug(renderer); return true; } @@ -203,7 +202,7 @@ static void gles2_render_quad_with_matrix(struct wlr_renderer *wlr_renderer, float converted[4]; color_convert(NULL, renderer->color, color, converted); - PUSH_GLES2_DEBUG; + push_gles2_debug(renderer); glUseProgram(renderer->shaders.quad.program); glUniformMatrix3fv(renderer->shaders.quad.proj, 1, GL_FALSE, transposition); @@ -218,7 +217,7 @@ static void gles2_render_quad_with_matrix(struct wlr_renderer *wlr_renderer, glDisableVertexAttribArray(renderer->shaders.quad.pos_attrib); - POP_GLES2_DEBUG; + pop_gles2_debug(renderer); } static void gles2_render_ellipse_with_matrix(struct wlr_renderer *wlr_renderer, @@ -241,7 +240,7 @@ static void gles2_render_ellipse_with_matrix(struct wlr_renderer *wlr_renderer, float converted[4]; color_convert(NULL, renderer->color, color, converted); - PUSH_GLES2_DEBUG; + push_gles2_debug(renderer); glUseProgram(renderer->shaders.ellipse.program); glUniformMatrix3fv(renderer->shaders.ellipse.proj, 1, GL_FALSE, transposition); @@ -259,7 +258,7 @@ static void gles2_render_ellipse_with_matrix(struct wlr_renderer *wlr_renderer, glDisableVertexAttribArray(renderer->shaders.ellipse.pos_attrib); glDisableVertexAttribArray(renderer->shaders.ellipse.tex_attrib); - POP_GLES2_DEBUG; + pop_gles2_debug(renderer); } static const enum wl_shm_format *gles2_renderer_formats( @@ -312,10 +311,10 @@ static enum wl_shm_format gles2_preferred_read_format( gles2_get_renderer_in_context(wlr_renderer); GLint gl_format = -1, gl_type = -1; - PUSH_GLES2_DEBUG; + push_gles2_debug(renderer); glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &gl_format); glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &gl_type); - POP_GLES2_DEBUG; + pop_gles2_debug(renderer); EGLint alpha_size = -1; eglGetConfigAttrib(renderer->egl->display, renderer->egl->config, @@ -352,7 +351,7 @@ static bool gles2_read_pixels(struct wlr_renderer *wlr_renderer, return false; } - PUSH_GLES2_DEBUG; + push_gles2_debug(renderer); // Make sure any pending drawing is finished before we try to read it glFinish(); @@ -379,7 +378,7 @@ static bool gles2_read_pixels(struct wlr_renderer *wlr_renderer, } } - POP_GLES2_DEBUG; + pop_gles2_debug(renderer); return glGetError() == GL_NO_ERROR; } @@ -387,7 +386,8 @@ static bool gles2_read_pixels(struct wlr_renderer *wlr_renderer, static bool gles2_blit_dmabuf(struct wlr_renderer *wlr_renderer, struct wlr_dmabuf_attributes *dst_attr, struct wlr_dmabuf_attributes *src_attr) { - if (!gles2_procs.glEGLImageTargetRenderbufferStorageOES) { + struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); + if (!renderer->procs.glEGLImageTargetRenderbufferStorageOES) { return false; } @@ -412,15 +412,14 @@ static bool gles2_blit_dmabuf(struct wlr_renderer *wlr_renderer, // texture. gles2_src_tex->inverted_y = !(src_inverted_y ^ dst_inverted_y); - struct wlr_egl *egl = wlr_gles2_renderer_get_egl(wlr_renderer); - if (!wlr_egl_make_current(egl, EGL_NO_SURFACE, NULL)) { + if (!wlr_egl_make_current(renderer->egl, EGL_NO_SURFACE, NULL)) { goto texture_destroy_out; } // TODO: The imported buffer should be checked with // eglQueryDmaBufModifiersEXT to see if it may be modified. bool external_only = false; - EGLImageKHR image = wlr_egl_create_image_from_dmabuf(egl, dst_attr, + EGLImageKHR image = wlr_egl_create_image_from_dmabuf(renderer->egl, dst_attr, &external_only); if (image == EGL_NO_IMAGE_KHR) { goto texture_destroy_out; @@ -429,7 +428,7 @@ static bool gles2_blit_dmabuf(struct wlr_renderer *wlr_renderer, GLuint rbo = 0; glGenRenderbuffers(1, &rbo); glBindRenderbuffer(GL_RENDERBUFFER, rbo); - gles2_procs.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, + renderer->procs.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, image); glBindRenderbuffer(GL_RENDERBUFFER, 0); @@ -458,7 +457,7 @@ out: glBindFramebuffer(GL_FRAMEBUFFER, 0); glDeleteFramebuffers(1, &fbo); glDeleteRenderbuffers(1, &rbo); - wlr_egl_destroy_image(egl, image); + wlr_egl_destroy_image(renderer->egl, image); texture_destroy_out: wlr_texture_destroy(src_tex); restore_context_out: @@ -466,27 +465,6 @@ restore_context_out: return r; } -static struct wlr_texture *gles2_texture_from_pixels( - struct wlr_renderer *wlr_renderer, enum wl_shm_format wl_fmt, - uint32_t stride, uint32_t width, uint32_t height, const void *data) { - struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); - return wlr_gles2_texture_from_pixels(renderer->egl, wl_fmt, stride, width, - height, data); -} - -static struct wlr_texture *gles2_texture_from_wl_drm( - struct wlr_renderer *wlr_renderer, struct wl_resource *data) { - struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); - return wlr_gles2_texture_from_wl_drm(renderer->egl, data); -} - -static struct wlr_texture *gles2_texture_from_dmabuf( - struct wlr_renderer *wlr_renderer, - struct wlr_dmabuf_attributes *attribs) { - struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); - return wlr_gles2_texture_from_dmabuf(renderer->egl, attribs); -} - static bool gles2_init_wl_display(struct wlr_renderer *wlr_renderer, struct wl_display *wl_display) { struct wlr_gles2_renderer *renderer = @@ -523,17 +501,17 @@ static void gles2_destroy(struct wlr_renderer *wlr_renderer) { wlr_egl_make_current(renderer->egl, EGL_NO_SURFACE, NULL); - PUSH_GLES2_DEBUG; + push_gles2_debug(renderer); glDeleteProgram(renderer->shaders.quad.program); glDeleteProgram(renderer->shaders.ellipse.program); glDeleteProgram(renderer->shaders.tex_rgba.program); glDeleteProgram(renderer->shaders.tex_rgbx.program); glDeleteProgram(renderer->shaders.tex_ext.program); - POP_GLES2_DEBUG; + pop_gles2_debug(renderer); if (renderer->exts.debug_khr) { glDisable(GL_DEBUG_OUTPUT_KHR); - gles2_procs.glDebugMessageCallbackKHR(NULL, NULL); + renderer->procs.glDebugMessageCallbackKHR(NULL, NULL); } wlr_egl_unset_current(renderer->egl); @@ -565,20 +543,21 @@ static const struct wlr_renderer_impl renderer_impl = { .blit_dmabuf = gles2_blit_dmabuf, }; -void push_gles2_marker(const char *file, const char *func) { - if (!gles2_procs.glPushDebugGroupKHR) { +void push_gles2_debug_(struct wlr_gles2_renderer *renderer, + const char *file, const char *func) { + if (!renderer->procs.glPushDebugGroupKHR) { return; } int len = snprintf(NULL, 0, "%s:%s", file, func) + 1; char str[len]; snprintf(str, len, "%s:%s", file, func); - gles2_procs.glPushDebugGroupKHR(GL_DEBUG_SOURCE_APPLICATION_KHR, 1, -1, str); + renderer->procs.glPushDebugGroupKHR(GL_DEBUG_SOURCE_APPLICATION_KHR, 1, -1, str); } -void pop_gles2_marker(void) { - if (gles2_procs.glPopDebugGroupKHR) { - gles2_procs.glPopDebugGroupKHR(); +void pop_gles2_debug(struct wlr_gles2_renderer *renderer) { + if (renderer->procs.glPopDebugGroupKHR) { + renderer->procs.glPopDebugGroupKHR(); } } @@ -602,8 +581,9 @@ static void gles2_log(GLenum src, GLenum type, GLuint id, GLenum severity, _wlr_log(gles2_log_importance_to_wlr(type), "[GLES2] %s", msg); } -static GLuint compile_shader(GLuint type, const GLchar *src) { - PUSH_GLES2_DEBUG; +static GLuint compile_shader(struct wlr_gles2_renderer *renderer, + GLuint type, const GLchar *src) { + push_gles2_debug(renderer); GLuint shader = glCreateShader(type); glShaderSource(shader, 1, &src, NULL); @@ -616,19 +596,20 @@ static GLuint compile_shader(GLuint type, const GLchar *src) { shader = 0; } - POP_GLES2_DEBUG; + pop_gles2_debug(renderer); return shader; } -static GLuint link_program(const GLchar *vert_src, const GLchar *frag_src) { - PUSH_GLES2_DEBUG; +static GLuint link_program(struct wlr_gles2_renderer *renderer, + const GLchar *vert_src, const GLchar *frag_src) { + push_gles2_debug(renderer); - GLuint vert = compile_shader(GL_VERTEX_SHADER, vert_src); + GLuint vert = compile_shader(renderer, GL_VERTEX_SHADER, vert_src); if (!vert) { goto error; } - GLuint frag = compile_shader(GL_FRAGMENT_SHADER, frag_src); + GLuint frag = compile_shader(renderer, GL_FRAGMENT_SHADER, frag_src); if (!frag) { glDeleteShader(vert); goto error; @@ -651,11 +632,11 @@ static GLuint link_program(const GLchar *vert_src, const GLchar *frag_src) { goto error; } - POP_GLES2_DEBUG; + pop_gles2_debug(renderer); return prog; error: - POP_GLES2_DEBUG; + pop_gles2_debug(renderer); return 0; } @@ -696,7 +677,7 @@ extern struct wlr_gles2_tex_shader tex_fragment_src_external; // return true on success -static bool build_tex_shader(struct wlr_gles2_tex_shader *shader) +static bool build_tex_shader(struct wlr_gles2_renderer *renderer, struct wlr_gles2_tex_shader *shader) { GLuint prog; @@ -708,7 +689,7 @@ static bool build_tex_shader(struct wlr_gles2_tex_shader *shader) assert(n < (int)sizeof(fragsrc)); - shader->program = prog = link_program(tex_vertex_src, fragsrc); + shader->program = prog = link_program(renderer, tex_vertex_src, fragsrc); if (!prog) { return false; } @@ -765,41 +746,41 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { if (check_gl_ext(exts_str, "GL_KHR_debug")) { renderer->exts.debug_khr = true; - load_gl_proc(&gles2_procs.glDebugMessageCallbackKHR, + load_gl_proc(&renderer->procs.glDebugMessageCallbackKHR, "glDebugMessageCallbackKHR"); - load_gl_proc(&gles2_procs.glDebugMessageControlKHR, + load_gl_proc(&renderer->procs.glDebugMessageControlKHR, "glDebugMessageControlKHR"); } if (check_gl_ext(exts_str, "GL_OES_EGL_image_external")) { renderer->exts.egl_image_external_oes = true; - load_gl_proc(&gles2_procs.glEGLImageTargetTexture2DOES, + load_gl_proc(&renderer->procs.glEGLImageTargetTexture2DOES, "glEGLImageTargetTexture2DOES"); } if (check_gl_ext(exts_str, "GL_OES_EGL_image")) { renderer->exts.egl_image_oes = true; - load_gl_proc(&gles2_procs.glEGLImageTargetRenderbufferStorageOES, + load_gl_proc(&renderer->procs.glEGLImageTargetRenderbufferStorageOES, "glEGLImageTargetRenderbufferStorageOES"); } if (renderer->exts.debug_khr) { glEnable(GL_DEBUG_OUTPUT_KHR); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR); - gles2_procs.glDebugMessageCallbackKHR(gles2_log, NULL); + renderer->procs.glDebugMessageCallbackKHR(gles2_log, NULL); // Silence unwanted message types - gles2_procs.glDebugMessageControlKHR(GL_DONT_CARE, - GL_DEBUG_TYPE_POP_GROUP_KHR, GL_DONT_CARE, 0, NULL, GL_FALSE); - gles2_procs.glDebugMessageControlKHR(GL_DONT_CARE, - GL_DEBUG_TYPE_PUSH_GROUP_KHR, GL_DONT_CARE, 0, NULL, GL_FALSE); + renderer->procs.glDebugMessageControlKHR(GL_DONT_CARE, + GL_DEBUG_TYPE_POP_GROUP_KHR, GL_DONT_CARE, 0, NULL, GL_FALSE); + renderer->procs.glDebugMessageControlKHR(GL_DONT_CARE, + GL_DEBUG_TYPE_PUSH_GROUP_KHR, GL_DONT_CARE, 0, NULL, GL_FALSE); } - PUSH_GLES2_DEBUG; + push_gles2_debug(renderer); GLuint prog; renderer->shaders.quad.program = prog = - link_program(quad_vertex_src, quad_fragment_src); + link_program(renderer, quad_vertex_src, quad_fragment_src); if (!renderer->shaders.quad.program) { goto error; } @@ -808,7 +789,7 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { renderer->shaders.quad.pos_attrib = glGetAttribLocation(prog, "pos"); renderer->shaders.ellipse.program = prog = - link_program(quad_vertex_src, ellipse_fragment_src); + link_program(renderer, quad_vertex_src, ellipse_fragment_src); if (!renderer->shaders.ellipse.program) { goto error; } @@ -818,23 +799,23 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { renderer->shaders.ellipse.tex_attrib = glGetAttribLocation(prog, "texcoord"); renderer->shaders.tex_rgba = tex_fragment_src_rgba; - if(! build_tex_shader(&renderer->shaders.tex_rgba)) { + if(! build_tex_shader(renderer, &renderer->shaders.tex_rgba)) { goto error; } renderer->shaders.tex_rgbx = tex_fragment_src_rgbx; - if(! build_tex_shader(&renderer->shaders.tex_rgbx)) { + if(! build_tex_shader(renderer, &renderer->shaders.tex_rgbx)) { goto error; } if (renderer->exts.egl_image_external_oes) { renderer->shaders.tex_ext = tex_fragment_src_external; - if(! build_tex_shader(&renderer->shaders.tex_ext)) { + if(! build_tex_shader(renderer, &renderer->shaders.tex_ext)) { goto error; } } - POP_GLES2_DEBUG; + pop_gles2_debug(renderer); wlr_egl_unset_current(renderer->egl); @@ -847,11 +828,11 @@ error: glDeleteProgram(renderer->shaders.tex_rgbx.program); glDeleteProgram(renderer->shaders.tex_ext.program); - POP_GLES2_DEBUG; + pop_gles2_debug(renderer); if (renderer->exts.debug_khr) { glDisable(GL_DEBUG_OUTPUT_KHR); - gles2_procs.glDebugMessageCallbackKHR(NULL, NULL); + renderer->procs.glDebugMessageCallbackKHR(NULL, NULL); } wlr_egl_unset_current(renderer->egl); diff --git a/render/gles2/texture.c b/render/gles2/texture.c index 784290762..179bfd891 100644 --- a/render/gles2/texture.c +++ b/render/gles2/texture.c @@ -29,7 +29,7 @@ struct wlr_gles2_texture *gles2_get_texture( static struct wlr_gles2_texture *get_gles2_texture_in_context( struct wlr_texture *wlr_texture) { struct wlr_gles2_texture *texture = gles2_get_texture(wlr_texture); - wlr_egl_make_current(texture->egl, EGL_NO_SURFACE, NULL); + wlr_egl_make_current(texture->renderer->egl, EGL_NO_SURFACE, NULL); return texture; } @@ -47,7 +47,7 @@ static bool gles2_texture_write_pixels(struct wlr_texture *wlr_texture, if (texture->target != GL_TEXTURE_2D) { wlr_log(WLR_ERROR, "Cannot write pixels to immutable texture"); - wlr_egl_unset_current(texture->egl); + wlr_egl_unset_current(texture->renderer->egl); return false; } @@ -56,7 +56,7 @@ static bool gles2_texture_write_pixels(struct wlr_texture *wlr_texture, assert(fmt); // TODO: what if the unpack subimage extension isn't supported? - PUSH_GLES2_DEBUG; + push_gles2_debug(texture->renderer); glBindTexture(GL_TEXTURE_2D, texture->tex); @@ -73,9 +73,9 @@ static bool gles2_texture_write_pixels(struct wlr_texture *wlr_texture, glBindTexture(GL_TEXTURE_2D, 0); - POP_GLES2_DEBUG; + pop_gles2_debug(texture->renderer); - wlr_egl_unset_current(texture->egl); + wlr_egl_unset_current(texture->renderer->egl); return true; } @@ -86,12 +86,12 @@ static bool gles2_texture_to_dmabuf(struct wlr_texture *wlr_texture, if (!texture->image) { assert(texture->target == GL_TEXTURE_2D); - if (!texture->egl->exts.image_base_khr) { + if (!texture->renderer->egl->exts.image_base_khr) { return false; } - texture->image = texture->egl->procs.eglCreateImageKHR( - texture->egl->display, texture->egl->context, EGL_GL_TEXTURE_2D_KHR, + texture->image = texture->renderer->egl->procs.eglCreateImageKHR( + texture->renderer->egl->display, texture->renderer->egl->context, EGL_GL_TEXTURE_2D_KHR, (EGLClientBuffer)(uintptr_t)texture->tex, NULL); if (texture->image == EGL_NO_IMAGE_KHR) { return false; @@ -103,7 +103,7 @@ static bool gles2_texture_to_dmabuf(struct wlr_texture *wlr_texture, flags |= WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT; } - return wlr_egl_export_image_to_dmabuf(texture->egl, texture->image, + return wlr_egl_export_image_to_dmabuf(texture->renderer->egl, texture->image, wlr_texture->width, wlr_texture->height, flags, attribs); } @@ -115,14 +115,14 @@ static void gles2_texture_destroy(struct wlr_texture *wlr_texture) { struct wlr_gles2_texture *texture = get_gles2_texture_in_context(wlr_texture); - PUSH_GLES2_DEBUG; + push_gles2_debug(texture->renderer); glDeleteTextures(1, &texture->tex); - wlr_egl_destroy_image(texture->egl, texture->image); + wlr_egl_destroy_image(texture->renderer->egl, texture->image); - POP_GLES2_DEBUG; + pop_gles2_debug(texture->renderer); - wlr_egl_unset_current(texture->egl); + wlr_egl_unset_current(texture->renderer->egl); free(texture); } @@ -134,11 +134,12 @@ static const struct wlr_texture_impl texture_impl = { .destroy = gles2_texture_destroy, }; -struct wlr_texture *wlr_gles2_texture_from_pixels(struct wlr_egl *egl, +struct wlr_texture *gles2_texture_from_pixels(struct wlr_renderer *wlr_renderer, enum wl_shm_format wl_fmt, uint32_t stride, uint32_t width, uint32_t height, const void *data) { - - wlr_egl_make_current(egl, EGL_NO_SURFACE, NULL); + struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); + + wlr_egl_make_current(renderer->egl, EGL_NO_SURFACE, NULL); const struct wlr_gles2_pixel_format *fmt = get_gles2_format_from_wl(wl_fmt); if (fmt == NULL) { @@ -153,12 +154,12 @@ struct wlr_texture *wlr_gles2_texture_from_pixels(struct wlr_egl *egl, return NULL; } wlr_texture_init(&texture->wlr_texture, &texture_impl, width, height); - texture->egl = egl; + texture->renderer = renderer; texture->target = GL_TEXTURE_2D; texture->has_alpha = fmt->has_alpha; texture->wl_format = fmt->wl_format; - PUSH_GLES2_DEBUG; + push_gles2_debug(renderer); glGenTextures(1, &texture->tex); glBindTexture(GL_TEXTURE_2D, texture->tex); @@ -170,24 +171,26 @@ struct wlr_texture *wlr_gles2_texture_from_pixels(struct wlr_egl *egl, glBindTexture(GL_TEXTURE_2D, 0); - POP_GLES2_DEBUG; + pop_gles2_debug(renderer); - wlr_egl_unset_current(egl); + wlr_egl_unset_current(renderer->egl); return &texture->wlr_texture; } -struct wlr_texture *wlr_gles2_texture_from_wl_drm(struct wlr_egl *egl, +struct wlr_texture *gles2_texture_from_wl_drm(struct wlr_renderer *wlr_renderer, struct wl_resource *resource) { - wlr_egl_make_current(egl, EGL_NO_SURFACE, NULL); + struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); - if (!gles2_procs.glEGLImageTargetTexture2DOES) { + wlr_egl_make_current(renderer->egl, EGL_NO_SURFACE, NULL); + + if (!renderer->procs.glEGLImageTargetTexture2DOES) { return NULL; } EGLint fmt; int width, height; bool inverted_y; - EGLImageKHR image = wlr_egl_create_image_from_wl_drm(egl, resource, + EGLImageKHR image = wlr_egl_create_image_from_wl_drm(renderer->egl, resource, &fmt, &width, &height, &inverted_y); if (image == EGL_NO_IMAGE_KHR) { wlr_log(WLR_ERROR, "Failed to create EGL image from wl_drm resource"); @@ -198,11 +201,11 @@ struct wlr_texture *wlr_gles2_texture_from_wl_drm(struct wlr_egl *egl, calloc(1, sizeof(struct wlr_gles2_texture)); if (texture == NULL) { wlr_log(WLR_ERROR, "Allocation failed"); - wlr_egl_destroy_image(egl, image); + wlr_egl_destroy_image(renderer->egl, image); return NULL; } wlr_texture_init(&texture->wlr_texture, &texture_impl, width, height); - texture->egl = egl; + texture->renderer = renderer; texture->wl_format = 0xFFFFFFFF; // texture can't be written anyways texture->image = image; @@ -218,36 +221,38 @@ struct wlr_texture *wlr_gles2_texture_from_wl_drm(struct wlr_egl *egl, break; default: wlr_log(WLR_ERROR, "Invalid or unsupported EGL buffer format"); - wlr_egl_destroy_image(egl, image); + wlr_egl_destroy_image(renderer->egl, image); free(texture); return NULL; } texture->target = GL_TEXTURE_EXTERNAL_OES; - PUSH_GLES2_DEBUG; + push_gles2_debug(renderer); glGenTextures(1, &texture->tex); glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture->tex); - gles2_procs.glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, + renderer->procs.glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, texture->image); glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0); - POP_GLES2_DEBUG; + pop_gles2_debug(renderer); - wlr_egl_unset_current(egl); + wlr_egl_unset_current(renderer->egl); return &texture->wlr_texture; } -struct wlr_texture *wlr_gles2_texture_from_dmabuf(struct wlr_egl *egl, +struct wlr_texture *gles2_texture_from_dmabuf(struct wlr_renderer *wlr_renderer, struct wlr_dmabuf_attributes *attribs) { - wlr_egl_make_current(egl, EGL_NO_SURFACE, NULL); + struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); - if (!gles2_procs.glEGLImageTargetTexture2DOES) { + wlr_egl_make_current(renderer->egl, EGL_NO_SURFACE, NULL); + + if (!renderer->procs.glEGLImageTargetTexture2DOES) { return NULL; } - if (!egl->exts.image_dmabuf_import_ext) { + if (!renderer->egl->exts.image_dmabuf_import_ext) { wlr_log(WLR_ERROR, "Cannot create DMA-BUF texture: EGL extension " "unavailable"); return NULL; @@ -273,7 +278,7 @@ struct wlr_texture *wlr_gles2_texture_from_dmabuf(struct wlr_egl *egl, } wlr_texture_init(&texture->wlr_texture, &texture_impl, attribs->width, attribs->height); - texture->egl = egl; + texture->renderer = renderer; texture->has_alpha = true; texture->wl_format = 0xFFFFFFFF; // texture can't be written anyways texture->inverted_y = @@ -281,7 +286,7 @@ struct wlr_texture *wlr_gles2_texture_from_dmabuf(struct wlr_egl *egl, bool external_only; texture->image = - wlr_egl_create_image_from_dmabuf(egl, attribs, &external_only); + wlr_egl_create_image_from_dmabuf(renderer->egl, attribs, &external_only); if (texture->image == EGL_NO_IMAGE_KHR) { wlr_log(WLR_ERROR, "Failed to create EGL image from DMA-BUF"); free(texture); @@ -290,16 +295,16 @@ struct wlr_texture *wlr_gles2_texture_from_dmabuf(struct wlr_egl *egl, texture->target = external_only ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D; - PUSH_GLES2_DEBUG; + push_gles2_debug(renderer); glGenTextures(1, &texture->tex); glBindTexture(texture->target, texture->tex); - gles2_procs.glEGLImageTargetTexture2DOES(texture->target, texture->image); + renderer->procs.glEGLImageTargetTexture2DOES(texture->target, texture->image); glBindTexture(texture->target, 0); - POP_GLES2_DEBUG; + pop_gles2_debug(renderer); - wlr_egl_unset_current(egl); + wlr_egl_unset_current(renderer->egl); return &texture->wlr_texture; } diff --git a/render/wlr_renderer.c b/render/wlr_renderer.c index 328f0f786..096585ce1 100644 --- a/render/wlr_renderer.c +++ b/render/wlr_renderer.c @@ -104,6 +104,9 @@ bool wlr_render_subtexture_with_matrix(struct wlr_renderer *r, void wlr_render_rect(struct wlr_renderer *r, const struct wlr_box *box, const float color[static 4], const float projection[static 9]) { + if (box->width == 0 || box->height == 0) { + return; + } assert(box->width > 0 && box->height > 0); float matrix[9]; wlr_matrix_project_box(matrix, box, WL_OUTPUT_TRANSFORM_NORMAL, 0, @@ -120,6 +123,9 @@ void wlr_render_quad_with_matrix(struct wlr_renderer *r, void wlr_render_ellipse(struct wlr_renderer *r, const struct wlr_box *box, const float color[static 4], const float projection[static 9]) { + if (box->width == 0 || box->height == 0) { + return; + } assert(box->width > 0 && box->height > 0); float matrix[9]; wlr_matrix_project_box(matrix, box, WL_OUTPUT_TRANSFORM_NORMAL, 0, diff --git a/types/meson.build b/types/meson.build index 998e6b455..80978176e 100644 --- a/types/meson.build +++ b/types/meson.build @@ -11,11 +11,6 @@ wlr_files += files( 'tablet_v2/wlr_tablet_v2_tablet.c', 'tablet_v2/wlr_tablet_v2_tool.c', 'tablet_v2/wlr_tablet_v2.c', - 'xdg_shell_v6/wlr_xdg_popup_v6.c', - 'xdg_shell_v6/wlr_xdg_positioner_v6.c', - 'xdg_shell_v6/wlr_xdg_shell_v6.c', - 'xdg_shell_v6/wlr_xdg_surface_v6.c', - 'xdg_shell_v6/wlr_xdg_toplevel_v6.c', 'xdg_shell/wlr_xdg_popup.c', 'xdg_shell/wlr_xdg_positioner.c', 'xdg_shell/wlr_xdg_shell.c', diff --git a/types/seat/wlr_seat_keyboard.c b/types/seat/wlr_seat_keyboard.c index 715bd4ba1..5328de82f 100644 --- a/types/seat/wlr_seat_keyboard.c +++ b/types/seat/wlr_seat_keyboard.c @@ -267,7 +267,7 @@ void wlr_seat_keyboard_enter(struct wlr_seat *seat, for (size_t i = 0; i < num_keycodes; ++i) { uint32_t *p = wl_array_add(&keys, sizeof(uint32_t)); if (!p) { - wlr_log(WLR_ERROR, "Cannot allocate memory, skipping keycode: %d\n", + wlr_log(WLR_ERROR, "Cannot allocate memory, skipping keycode: %" PRIu32 "\n", keycodes[i]); continue; } @@ -449,7 +449,7 @@ void seat_client_create_keyboard(struct wlr_seat_client *seat_client, for (size_t i = 0; i < num_keycodes; ++i) { uint32_t *p = wl_array_add(&keys, sizeof(uint32_t)); if (!p) { - wlr_log(WLR_ERROR, "Cannot allocate memory, skipping keycode: %d\n", + wlr_log(WLR_ERROR, "Cannot allocate memory, skipping keycode: %" PRIu32 "\n", keycodes[i]); continue; } diff --git a/types/seat/wlr_seat_pointer.c b/types/seat/wlr_seat_pointer.c index 9ad5f88c6..37c637b53 100644 --- a/types/seat/wlr_seat_pointer.c +++ b/types/seat/wlr_seat_pointer.c @@ -199,11 +199,9 @@ void wlr_seat_pointer_enter(struct wlr_seat *wlr_seat, wlr_seat->pointer_state.focused_client = client; wlr_seat->pointer_state.focused_surface = surface; if (surface != NULL) { - wlr_seat->pointer_state.sx = sx; - wlr_seat->pointer_state.sy = sy; + wlr_seat_pointer_warp(wlr_seat, sx, sy); } else { - wlr_seat->pointer_state.sx = NAN; - wlr_seat->pointer_state.sy = NAN; + wlr_seat_pointer_warp(wlr_seat, NAN, NAN); } struct wlr_seat_pointer_focus_change_event event = { @@ -220,6 +218,11 @@ void wlr_seat_pointer_clear_focus(struct wlr_seat *wlr_seat) { wlr_seat_pointer_enter(wlr_seat, NULL, 0, 0); } +void wlr_seat_pointer_warp(struct wlr_seat *wlr_seat, double sx, double sy) { + wlr_seat->pointer_state.sx = sx; + wlr_seat->pointer_state.sy = sy; +} + void wlr_seat_pointer_send_motion(struct wlr_seat *wlr_seat, uint32_t time, double sx, double sy) { struct wlr_seat_client *client = wlr_seat->pointer_state.focused_client; @@ -241,8 +244,7 @@ void wlr_seat_pointer_send_motion(struct wlr_seat *wlr_seat, uint32_t time, wl_fixed_from_double(sy)); } - wlr_seat->pointer_state.sx = sx; - wlr_seat->pointer_state.sy = sy; + wlr_seat_pointer_warp(wlr_seat, sx, sy); } uint32_t wlr_seat_pointer_send_button(struct wlr_seat *wlr_seat, uint32_t time, diff --git a/types/wlr_compositor.c b/types/wlr_compositor.c index 11f22923e..d49dcc18d 100644 --- a/types/wlr_compositor.c +++ b/types/wlr_compositor.c @@ -39,7 +39,7 @@ static void subcompositor_handle_get_subsurface(struct wl_client *client, if (surface == parent) { wl_resource_post_error(resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, - "%s%d: wl_surface@%d cannot be its own parent", + "%s%" PRIu32 ": wl_surface@%" PRIu32 " cannot be its own parent", msg, id, wl_resource_get_id(surface_resource)); return; } @@ -48,7 +48,7 @@ static void subcompositor_handle_get_subsurface(struct wl_client *client, wlr_subsurface_from_wlr_surface(surface) != NULL) { wl_resource_post_error(resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, - "%s%d: wl_surface@%d is already a sub-surface", + "%s%" PRIu32 ": wl_surface@%" PRIu32 " is already a sub-surface", msg, id, wl_resource_get_id(surface_resource)); return; } @@ -56,7 +56,7 @@ static void subcompositor_handle_get_subsurface(struct wl_client *client, if (wlr_surface_get_root_surface(parent) == surface) { wl_resource_post_error(resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, - "%s%d: wl_surface@%d is an ancestor of parent", + "%s%" PRIu32 ": wl_surface@%" PRIu32 " is an ancestor of parent", msg, id, wl_resource_get_id(surface_resource)); return; } diff --git a/types/wlr_export_dmabuf_v1.c b/types/wlr_export_dmabuf_v1.c index c2ec34c89..a8e2a4bde 100644 --- a/types/wlr_export_dmabuf_v1.c +++ b/types/wlr_export_dmabuf_v1.c @@ -41,8 +41,7 @@ static void frame_destroy(struct wlr_export_dmabuf_frame_v1 *frame) { } } wl_list_remove(&frame->link); - wl_list_remove(&frame->output_precommit.link); - wlr_dmabuf_attributes_finish(&frame->attribs); + wl_list_remove(&frame->output_commit.link); // Make the frame resource inert wl_resource_set_user_data(frame->resource, NULL); free(frame); @@ -53,18 +52,41 @@ static void frame_handle_resource_destroy(struct wl_resource *resource) { frame_destroy(frame); } -static void frame_output_handle_precommit(struct wl_listener *listener, +static void frame_output_handle_commit(struct wl_listener *listener, void *data) { struct wlr_export_dmabuf_frame_v1 *frame = - wl_container_of(listener, frame, output_precommit); - struct wlr_output_event_precommit *event = data; + wl_container_of(listener, frame, output_commit); + struct wlr_output_event_commit *event = data; - if (!(event->output->pending.committed & WLR_OUTPUT_STATE_BUFFER)) { + if (!(event->committed & WLR_OUTPUT_STATE_BUFFER)) { return; } - wl_list_remove(&frame->output_precommit.link); - wl_list_init(&frame->output_precommit.link); + wl_list_remove(&frame->output_commit.link); + wl_list_init(&frame->output_commit.link); + + struct wlr_dmabuf_attributes attribs = {0}; + if (!wlr_output_export_dmabuf(frame->output, &attribs)) { + zwlr_export_dmabuf_frame_v1_send_cancel(frame->resource, + ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_TEMPORARY); + frame_destroy(frame); + return; + } + + uint32_t frame_flags = ZWLR_EXPORT_DMABUF_FRAME_V1_FLAGS_TRANSIENT; + uint32_t mod_high = attribs.modifier >> 32; + uint32_t mod_low = attribs.modifier & 0xFFFFFFFF; + zwlr_export_dmabuf_frame_v1_send_frame(frame->resource, + attribs.width, attribs.height, 0, 0, attribs.flags, frame_flags, + attribs.format, mod_high, mod_low, attribs.n_planes); + + for (int i = 0; i < attribs.n_planes; ++i) { + off_t size = lseek(attribs.fd[i], 0, SEEK_END); + zwlr_export_dmabuf_frame_v1_send_object(frame->resource, i, + attribs.fd[i], size, attribs.offset[i], attribs.stride[i], i); + } + + wlr_dmabuf_attributes_finish(&attribs); time_t tv_sec = event->when->tv_sec; uint32_t tv_sec_hi = (sizeof(tv_sec) > 4) ? tv_sec >> 32 : 0; @@ -98,7 +120,7 @@ static void manager_handle_capture_output(struct wl_client *client, return; } frame->manager = manager; - wl_list_init(&frame->output_precommit.link); + wl_list_init(&frame->output_commit.link); uint32_t version = wl_resource_get_version(manager_resource); frame->resource = wl_resource_create(client, @@ -120,14 +142,6 @@ static void manager_handle_capture_output(struct wl_client *client, return; } - struct wlr_dmabuf_attributes *attribs = &frame->attribs; - if (!wlr_output_export_dmabuf(output, attribs)) { - zwlr_export_dmabuf_frame_v1_send_cancel(frame->resource, - ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_TEMPORARY); - frame_destroy(frame); - return; - } - frame->output = output; wlr_output_lock_attach_render(frame->output, true); @@ -136,26 +150,11 @@ static void manager_handle_capture_output(struct wl_client *client, frame->cursor_locked = true; } - uint32_t frame_flags = ZWLR_EXPORT_DMABUF_FRAME_V1_FLAGS_TRANSIENT; - uint32_t mod_high = attribs->modifier >> 32; - uint32_t mod_low = attribs->modifier & 0xFFFFFFFF; - - zwlr_export_dmabuf_frame_v1_send_frame(frame->resource, - output->width, output->height, 0, 0, attribs->flags, frame_flags, - attribs->format, mod_high, mod_low, attribs->n_planes); - - for (int i = 0; i < attribs->n_planes; ++i) { - off_t size = lseek(attribs->fd[i], 0, SEEK_END); - - zwlr_export_dmabuf_frame_v1_send_object(frame->resource, i, - attribs->fd[i], size, attribs->offset[i], attribs->stride[i], i); - } + wl_list_remove(&frame->output_commit.link); + wl_signal_add(&output->events.commit, &frame->output_commit); + frame->output_commit.notify = frame_output_handle_commit; wlr_output_schedule_frame(output); - - wl_list_remove(&frame->output_precommit.link); - wl_signal_add(&output->events.precommit, &frame->output_precommit); - frame->output_precommit.notify = frame_output_handle_precommit; } static void manager_handle_destroy(struct wl_client *client, diff --git a/types/wlr_foreign_toplevel_management_v1.c b/types/wlr_foreign_toplevel_management_v1.c index aed0e203c..154f790c3 100644 --- a/types/wlr_foreign_toplevel_management_v1.c +++ b/types/wlr_foreign_toplevel_management_v1.c @@ -9,7 +9,7 @@ #include "util/signal.h" #include "wlr-foreign-toplevel-management-unstable-v1-protocol.h" -#define FOREIGN_TOPLEVEL_MANAGEMENT_V1_VERSION 2 +#define FOREIGN_TOPLEVEL_MANAGEMENT_V1_VERSION 3 static const struct zwlr_foreign_toplevel_handle_v1_interface toplevel_handle_impl; @@ -405,6 +405,41 @@ void wlr_foreign_toplevel_handle_v1_set_fullscreen( toplevel_send_state(toplevel); } +static void toplevel_resource_send_parent( + struct wl_resource *toplevel_resource, + struct wlr_foreign_toplevel_handle_v1 *parent) { + if (wl_resource_get_version(toplevel_resource) < + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_PARENT_SINCE_VERSION) { + return; + } + struct wl_client *client = wl_resource_get_client(toplevel_resource); + struct wl_resource *parent_resource = NULL; + if (parent) { + parent_resource = wl_resource_find_for_client(&parent->resources, client); + if (!parent_resource) { + /* don't send an event if this client destroyed the parent handle */ + return; + } + } + zwlr_foreign_toplevel_handle_v1_send_parent(toplevel_resource, + parent_resource); +} + +void wlr_foreign_toplevel_handle_v1_set_parent( + struct wlr_foreign_toplevel_handle_v1 *toplevel, + struct wlr_foreign_toplevel_handle_v1 *parent) { + if (parent == toplevel->parent) { + /* only send parent event to the clients if there was a change */ + return; + } + struct wl_resource *toplevel_resource, *tmp; + wl_resource_for_each_safe(toplevel_resource, tmp, &toplevel->resources) { + toplevel_resource_send_parent(toplevel_resource, parent); + } + toplevel->parent = parent; + toplevel_update_idle_source(toplevel); +} + void wlr_foreign_toplevel_handle_v1_destroy( struct wlr_foreign_toplevel_handle_v1 *toplevel) { if (!toplevel) { @@ -432,6 +467,19 @@ void wlr_foreign_toplevel_handle_v1_destroy( wl_list_remove(&toplevel->link); + /* need to ensure no other toplevels hold a pointer to this one as + * a parent, so that a later call to foreign_toplevel_manager_bind() + * will not result in a segfault */ + struct wlr_foreign_toplevel_handle_v1 *tl, *tmp3; + wl_list_for_each_safe(tl, tmp3, &toplevel->manager->toplevels, link) { + if (tl->parent == toplevel) { + /* Note: we send a parent signal to all clients in this case; + * the caller should first destroy the child handles if it + * wishes to avoid this behavior. */ + wlr_foreign_toplevel_handle_v1_set_parent(tl, NULL); + } + } + free(toplevel->title); free(toplevel->app_id); free(toplevel); @@ -542,6 +590,8 @@ static void toplevel_send_details_to_toplevel_resource( zwlr_foreign_toplevel_handle_v1_send_state(resource, &states); wl_array_release(&states); + toplevel_resource_send_parent(resource, toplevel->parent); + zwlr_foreign_toplevel_handle_v1_send_done(resource); } @@ -560,9 +610,17 @@ static void foreign_toplevel_manager_bind(struct wl_client *client, void *data, wl_list_insert(&manager->resources, wl_resource_get_link(resource)); struct wlr_foreign_toplevel_handle_v1 *toplevel, *tmp; + /* First loop: create a handle for all toplevels for all clients. + * Separation into two loops avoid the case where a child handle + * is created before a parent handle, so the parent relationship + * could not be sent to a client. */ + wl_list_for_each_safe(toplevel, tmp, &manager->toplevels, link) { + create_toplevel_resource_for_resource(toplevel, resource); + } + /* Second loop: send details about each toplevel. */ wl_list_for_each_safe(toplevel, tmp, &manager->toplevels, link) { struct wl_resource *toplevel_resource = - create_toplevel_resource_for_resource(toplevel, resource); + wl_resource_find_for_client(&toplevel->resources, client); toplevel_send_details_to_toplevel_resource(toplevel, toplevel_resource); } diff --git a/types/wlr_gamma_control_v1.c b/types/wlr_gamma_control_v1.c index 591c6aa3a..2fdaaaab9 100644 --- a/types/wlr_gamma_control_v1.c +++ b/types/wlr_gamma_control_v1.c @@ -24,7 +24,9 @@ static void gamma_control_destroy(struct wlr_gamma_control_v1 *gamma_control) { wlr_output_set_gamma(gamma_control->output, 0, NULL, NULL, NULL); wl_resource_set_user_data(gamma_control->resource, NULL); wl_list_remove(&gamma_control->output_destroy_listener.link); + wl_list_remove(&gamma_control->output_commit_listener.link); wl_list_remove(&gamma_control->link); + free(gamma_control->table); free(gamma_control); } @@ -34,6 +36,22 @@ static void gamma_control_send_failed( gamma_control_destroy(gamma_control); } +static void gamma_control_apply(struct wlr_gamma_control_v1 *gamma_control) { + uint16_t *r = gamma_control->table; + uint16_t *g = gamma_control->table + gamma_control->ramp_size; + uint16_t *b = gamma_control->table + 2 * gamma_control->ramp_size; + + wlr_output_set_gamma(gamma_control->output, gamma_control->ramp_size, r, g, b); + if (!wlr_output_test(gamma_control->output)) { + wlr_output_rollback(gamma_control->output); + gamma_control_send_failed(gamma_control); + return; + } + + // Gamma LUT will be applied on next output commit + wlr_output_schedule_frame(gamma_control->output); +} + static const struct zwlr_gamma_control_v1_interface gamma_control_impl; static struct wlr_gamma_control_v1 *gamma_control_from_resource( @@ -56,6 +74,17 @@ static void gamma_control_handle_output_destroy(struct wl_listener *listener, gamma_control_destroy(gamma_control); } +static void gamma_control_handle_output_commit(struct wl_listener *listener, + void *data) { + struct wlr_gamma_control_v1 *gamma_control = + wl_container_of(listener, gamma_control, output_commit_listener); + struct wlr_output_event_commit *event = data; + if ((event->committed & WLR_OUTPUT_STATE_ENABLED) && + gamma_control->output->enabled) { + gamma_control_apply(gamma_control); + } +} + static void gamma_control_handle_set_gamma(struct wl_client *client, struct wl_resource *gamma_control_resource, int fd) { struct wlr_gamma_control_v1 *gamma_control = @@ -101,19 +130,13 @@ static void gamma_control_handle_set_gamma(struct wl_client *client, close(fd); fd = -1; - uint16_t *r = table; - uint16_t *g = table + ramp_size; - uint16_t *b = table + 2 * ramp_size; + free(gamma_control->table); + gamma_control->table = table; + gamma_control->ramp_size = ramp_size; - wlr_output_set_gamma(gamma_control->output, ramp_size, r, g, b); - if (!wlr_output_test(gamma_control->output)) { - gamma_control_send_failed(gamma_control); - goto error_table; + if (gamma_control->output->enabled) { + gamma_control_apply(gamma_control); } - free(table); - - // Gamma LUT will be applied on next output commit - wlr_output_schedule_frame(gamma_control->output); return; @@ -176,6 +199,11 @@ static void gamma_control_manager_get_gamma_control(struct wl_client *client, gamma_control->output_destroy_listener.notify = gamma_control_handle_output_destroy; + wl_signal_add(&output->events.commit, + &gamma_control->output_commit_listener); + gamma_control->output_commit_listener.notify = + gamma_control_handle_output_commit; + wl_list_init(&gamma_control->link); size_t gamma_size = wlr_output_get_gamma_size(output); diff --git a/types/wlr_input_method_v2.c b/types/wlr_input_method_v2.c index cb2482888..29b0d0289 100644 --- a/types/wlr_input_method_v2.c +++ b/types/wlr_input_method_v2.c @@ -259,6 +259,9 @@ void wlr_input_method_keyboard_grab_v2_set_keyboard( handle_keyboard_destroy; wl_signal_add(&keyboard->events.destroy, &keyboard_grab->keyboard_destroy); + + wlr_input_method_keyboard_grab_v2_send_modifiers(keyboard_grab, + &keyboard->modifiers); } keyboard_grab->keyboard = keyboard; diff --git a/types/wlr_layer_shell_v1.c b/types/wlr_layer_shell_v1.c index 0002f4cbf..bc6811179 100644 --- a/types/wlr_layer_shell_v1.c +++ b/types/wlr_layer_shell_v1.c @@ -74,7 +74,7 @@ static void layer_surface_handle_ack_configure(struct wl_client *client, if (!found) { wl_resource_post_error(resource, ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SURFACE_STATE, - "wrong configure serial: %u", serial); + "wrong configure serial: %" PRIu32, serial); return; } // Then remove old configures from the list @@ -114,7 +114,7 @@ static void layer_surface_handle_set_anchor(struct wl_client *client, if (anchor > max_anchor) { wl_resource_post_error(resource, ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_ANCHOR, - "invalid anchor %d", anchor); + "invalid anchor %" PRIu32, anchor); } struct wlr_layer_surface_v1 *surface = layer_surface_from_resource(resource); @@ -187,7 +187,7 @@ static void layer_surface_set_layer(struct wl_client *client, if (layer > ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY) { wl_resource_post_error(surface->resource, ZWLR_LAYER_SHELL_V1_ERROR_INVALID_LAYER, - "Invalid layer %d", layer); + "Invalid layer %" PRIu32, layer); return; } surface->client_pending.layer = layer; @@ -307,6 +307,26 @@ static void layer_surface_role_commit(struct wlr_surface *wlr_surface) { return; } + const uint32_t horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + if (surface->client_pending.desired_width == 0 && + (surface->client_pending.anchor & horiz) != horiz) { + wl_resource_post_error(surface->resource, + ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SIZE, + "width 0 requested without setting left and right anchors"); + return; + } + + const uint32_t vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; + if (surface->client_pending.desired_height == 0 && + (surface->client_pending.anchor & vert) != vert) { + wl_resource_post_error(surface->resource, + ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SIZE, + "height 0 requested without setting top and bottom anchors"); + return; + } + if (surface->closed) { // Ignore commits after the compositor has closed it return; @@ -403,7 +423,7 @@ static void layer_shell_handle_get_layer_surface(struct wl_client *wl_client, free(surface); wl_resource_post_error(client_resource, ZWLR_LAYER_SHELL_V1_ERROR_INVALID_LAYER, - "Invalid layer %d", layer); + "Invalid layer %" PRIu32, layer); return; } surface->namespace = strdup(namespace); @@ -534,16 +554,20 @@ static void xdg_surface_for_each_surface(struct wlr_xdg_surface *surface, } } -static void layer_surface_for_each_surface(struct wlr_layer_surface_v1 *surface, - int x, int y, wlr_surface_iterator_func_t iterator, void *user_data) { +void wlr_layer_surface_v1_for_each_surface(struct wlr_layer_surface_v1 *surface, + wlr_surface_iterator_func_t iterator, void *user_data) { struct layer_surface_iterator_data data = { .user_iterator = iterator, .user_data = user_data, - .x = x, .y = y, + .x = 0, .y = 0, }; wlr_surface_for_each_surface(surface->surface, layer_surface_iterator, &data); + wlr_layer_surface_v1_for_each_popup(surface, iterator, user_data); +} +void wlr_layer_surface_v1_for_each_popup(struct wlr_layer_surface_v1 *surface, + wlr_surface_iterator_func_t iterator, void *user_data){ struct wlr_xdg_popup *popup_state; wl_list_for_each(popup_state, &surface->popups, link) { struct wlr_xdg_surface *popup = popup_state->base; @@ -560,11 +584,6 @@ static void layer_surface_for_each_surface(struct wlr_layer_surface_v1 *surface, } } -void wlr_layer_surface_v1_for_each_surface(struct wlr_layer_surface_v1 *surface, - wlr_surface_iterator_func_t iterator, void *user_data) { - layer_surface_for_each_surface(surface, 0, 0, iterator, user_data); -} - struct wlr_surface *wlr_layer_surface_v1_surface_at( struct wlr_layer_surface_v1 *surface, double sx, double sy, double *sub_x, double *sub_y) { diff --git a/types/wlr_linux_dmabuf_v1.c b/types/wlr_linux_dmabuf_v1.c index 7db99eb6c..acd4cb4c9 100644 --- a/types/wlr_linux_dmabuf_v1.c +++ b/types/wlr_linux_dmabuf_v1.c @@ -210,7 +210,7 @@ static void params_create_common(struct wl_client *client, if (buffer->attributes.offset[i] > size) { wl_resource_post_error(params_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, - "invalid offset %i for plane %d", + "invalid offset %" PRIu32 " for plane %d", buffer->attributes.offset[i], i); goto err_out; } @@ -219,7 +219,7 @@ static void params_create_common(struct wl_client *client, buffer->attributes.stride[i] == 0) { wl_resource_post_error(params_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, - "invalid stride %i for plane %d", + "invalid stride %" PRIu32 " for plane %d", buffer->attributes.stride[i], i); goto err_out; } diff --git a/types/wlr_output.c b/types/wlr_output.c index ca6a99916..78b860632 100644 --- a/types/wlr_output.c +++ b/types/wlr_output.c @@ -552,6 +552,10 @@ static bool output_basic_test(struct wlr_output *output) { wlr_log(WLR_DEBUG, "Tried to enable adaptive sync on a disabled output"); return false; } + if (!enabled && output->pending.committed & WLR_OUTPUT_STATE_GAMMA_LUT) { + wlr_log(WLR_DEBUG, "Tried to set the gamma lut on a disabled output"); + return false; + } return true; } @@ -578,11 +582,11 @@ bool wlr_output_commit(struct wlr_output *output) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - struct wlr_output_event_precommit event = { + struct wlr_output_event_precommit pre_event = { .output = output, .when = &now, }; - wlr_signal_emit_safe(&output->events.precommit, &event); + wlr_signal_emit_safe(&output->events.precommit, &pre_event); if (!output->impl->commit(output)) { output_state_clear(&output->pending); @@ -601,7 +605,12 @@ bool wlr_output_commit(struct wlr_output *output) { output->commit_seq++; - wlr_signal_emit_safe(&output->events.commit, output); + struct wlr_output_event_commit event = { + .output = output, + .committed = output->pending.committed, + .when = &now, + }; + wlr_signal_emit_safe(&output->events.commit, &event); bool scale_updated = output->pending.committed & WLR_OUTPUT_STATE_SCALE; if (scale_updated) { diff --git a/types/wlr_output_management_v1.c b/types/wlr_output_management_v1.c index 15a971f60..728abee7c 100644 --- a/types/wlr_output_management_v1.c +++ b/types/wlr_output_management_v1.c @@ -6,7 +6,7 @@ #include "util/signal.h" #include "wlr-output-management-unstable-v1-protocol.h" -#define OUTPUT_MANAGER_VERSION 1 +#define OUTPUT_MANAGER_VERSION 2 enum { HEAD_STATE_ENABLED = 1 << 0, @@ -680,6 +680,9 @@ static void head_send_state(struct wlr_output_head_v1 *head, if (state & HEAD_STATE_ENABLED) { zwlr_output_head_v1_send_enabled(head_resource, head->state.enabled); + // On enabling we send all current data since clients have not been + // notified about potential data changes while the head was disabled. + state = HEAD_STATE_ALL; } if (!head->state.enabled) { @@ -761,6 +764,16 @@ static void manager_send_head(struct wlr_output_manager_v1 *manager, output->phys_width, output->phys_height); } + if (version >= ZWLR_OUTPUT_HEAD_V1_MAKE_SINCE_VERSION && output->make[0] != '\0') { + zwlr_output_head_v1_send_make(head_resource, output->make); + } + if (version >= ZWLR_OUTPUT_HEAD_V1_MODEL_SINCE_VERSION && output->model[0] != '\0') { + zwlr_output_head_v1_send_model(head_resource, output->model); + } + if (version >= ZWLR_OUTPUT_HEAD_V1_SERIAL_NUMBER_SINCE_VERSION && output->serial[0] != '\0') { + zwlr_output_head_v1_send_serial_number(head_resource, output->serial); + } + struct wlr_output_mode *mode; wl_list_for_each(mode, &output->modes, link) { head_send_mode(head, head_resource, mode); diff --git a/types/wlr_output_power_management_v1.c b/types/wlr_output_power_management_v1.c index 115c74f74..e607eb161 100644 --- a/types/wlr_output_power_management_v1.c +++ b/types/wlr_output_power_management_v1.c @@ -23,7 +23,7 @@ static void output_power_destroy(struct wlr_output_power_v1 *output_power) { } wl_resource_set_user_data(output_power->resource, NULL); wl_list_remove(&output_power->output_destroy_listener.link); - wl_list_remove(&output_power->output_enable_listener.link); + wl_list_remove(&output_power->output_commit_listener.link); wl_list_remove(&output_power->link); free(output_power); } @@ -58,11 +58,14 @@ static void output_power_v1_send_mode(struct wlr_output_power_v1 *output_power) zwlr_output_power_v1_send_mode(output_power->resource, mode); } -static void output_power_handle_output_enable(struct wl_listener *listener, +static void output_power_handle_output_commit(struct wl_listener *listener, void *data) { struct wlr_output_power_v1 *output_power = - wl_container_of(listener, output_power, output_enable_listener); - output_power_v1_send_mode(output_power); + wl_container_of(listener, output_power, output_commit_listener); + struct wlr_output_event_commit *event = data; + if (event->committed & WLR_OUTPUT_STATE_ENABLED) { + output_power_v1_send_mode(output_power); + } } static void output_power_handle_set_mode(struct wl_client *client, @@ -147,10 +150,10 @@ static void output_power_manager_get_output_power(struct wl_client *client, &output_power->output_destroy_listener); output_power->output_destroy_listener.notify = output_power_handle_output_destroy; - wl_signal_add(&output->events.enable, - &output_power->output_enable_listener); - output_power->output_enable_listener.notify = - output_power_handle_output_enable; + wl_signal_add(&output->events.commit, + &output_power->output_commit_listener); + output_power->output_commit_listener.notify = + output_power_handle_output_commit; struct wlr_output_power_v1 *mgmt; wl_list_for_each(mgmt, &manager->output_powers, link) { diff --git a/types/wlr_screencopy_v1.c b/types/wlr_screencopy_v1.c index 702a62b50..2d81ff7be 100644 --- a/types/wlr_screencopy_v1.c +++ b/types/wlr_screencopy_v1.c @@ -143,6 +143,7 @@ static void frame_destroy(struct wlr_screencopy_frame_v1 *frame) { } wl_list_remove(&frame->link); wl_list_remove(&frame->output_precommit.link); + wl_list_remove(&frame->output_commit.link); wl_list_remove(&frame->output_destroy.link); wl_list_remove(&frame->output_enable.link); wl_list_remove(&frame->buffer_destroy.link); @@ -152,6 +153,41 @@ static void frame_destroy(struct wlr_screencopy_frame_v1 *frame) { free(frame); } +static void frame_send_damage(struct wlr_screencopy_frame_v1 *frame) { + if (!frame->with_damage) { + return; + } + + struct screencopy_damage *damage = + screencopy_damage_get_or_create(frame->client, frame->output); + if (damage == NULL) { + return; + } + + // TODO: send fine-grained damage events + struct pixman_box32 *damage_box = + pixman_region32_extents(&damage->damage); + + int damage_x = damage_box->x1; + int damage_y = damage_box->y1; + int damage_width = damage_box->x2 - damage_box->x1; + int damage_height = damage_box->y2 - damage_box->y1; + + zwlr_screencopy_frame_v1_send_damage(frame->resource, + damage_x, damage_y, damage_width, damage_height); + + pixman_region32_clear(&damage->damage); +} + +static void frame_send_ready(struct wlr_screencopy_frame_v1 *frame, + struct timespec *when) { + time_t tv_sec = when->tv_sec; + uint32_t tv_sec_hi = (sizeof(tv_sec) > 4) ? tv_sec >> 32 : 0; + uint32_t tv_sec_lo = tv_sec & 0xFFFFFFFF; + zwlr_screencopy_frame_v1_send_ready(frame->resource, + tv_sec_hi, tv_sec_lo, when->tv_nsec); +} + static void frame_handle_output_precommit(struct wl_listener *listener, void *_data) { struct wlr_screencopy_frame_v1 *frame = @@ -165,9 +201,14 @@ static void frame_handle_output_precommit(struct wl_listener *listener, return; } - struct screencopy_damage *damage = NULL; + struct wl_shm_buffer *shm_buffer = frame->shm_buffer; + if (shm_buffer == NULL) { + return; + } + if (frame->with_damage) { - damage = screencopy_damage_get_or_create(frame->client, output); + struct screencopy_damage *damage = + screencopy_damage_get_or_create(frame->client, output); if (damage) { screencopy_damage_accumulate(damage); if (!pixman_region32_not_empty(&damage->damage)) { @@ -182,36 +223,19 @@ static void frame_handle_output_precommit(struct wl_listener *listener, int x = frame->box.x; int y = frame->box.y; - bool ok = false; - uint32_t flags = 0; + enum wl_shm_format fmt = wl_shm_buffer_get_format(shm_buffer); + int32_t width = wl_shm_buffer_get_width(shm_buffer); + int32_t height = wl_shm_buffer_get_height(shm_buffer); + int32_t stride = wl_shm_buffer_get_stride(shm_buffer); - struct wl_shm_buffer *shm_buffer = frame->shm_buffer; - struct wlr_dmabuf_v1_buffer *dma_buffer = frame->dma_buffer; - assert(shm_buffer || dma_buffer); - - if (shm_buffer) { - enum wl_shm_format fmt = wl_shm_buffer_get_format(shm_buffer); - int32_t width = wl_shm_buffer_get_width(shm_buffer); - int32_t height = wl_shm_buffer_get_height(shm_buffer); - int32_t stride = wl_shm_buffer_get_stride(shm_buffer); - - wl_shm_buffer_begin_access(shm_buffer); - void *data = wl_shm_buffer_get_data(shm_buffer); - uint32_t renderer_flags = 0; - ok = wlr_renderer_read_pixels(renderer, fmt, &renderer_flags, - stride, width, height, x, y, 0, 0, data); - flags |= renderer_flags & WLR_RENDERER_READ_PIXELS_Y_INVERT ? - ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT : 0; - wl_shm_buffer_end_access(shm_buffer); - } else if (dma_buffer) { - struct wlr_dmabuf_attributes attr = { 0 }; - ok = wlr_output_export_dmabuf(frame->output, &attr); - ok = ok && wlr_renderer_blit_dmabuf(renderer, - &dma_buffer->attributes, &attr); - flags |= dma_buffer->attributes.flags & WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT ? - ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT : 0; - wlr_dmabuf_attributes_finish(&attr); - } + wl_shm_buffer_begin_access(shm_buffer); + void *data = wl_shm_buffer_get_data(shm_buffer); + uint32_t renderer_flags = 0; + bool ok = wlr_renderer_read_pixels(renderer, fmt, &renderer_flags, + stride, width, height, x, y, 0, 0, data); + uint32_t flags = renderer_flags & WLR_RENDERER_READ_PIXELS_Y_INVERT ? + ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT : 0; + wl_shm_buffer_end_access(shm_buffer); if (!ok) { zwlr_screencopy_frame_v1_send_failed(frame->resource); @@ -220,29 +244,66 @@ static void frame_handle_output_precommit(struct wl_listener *listener, } zwlr_screencopy_frame_v1_send_flags(frame->resource, flags); + frame_send_damage(frame); + frame_send_ready(frame, event->when); + frame_destroy(frame); +} - // TODO: send fine-grained damage events - if (damage) { - struct pixman_box32 *damage_box = - pixman_region32_extents(&damage->damage); +static void frame_handle_output_commit(struct wl_listener *listener, + void *data) { + struct wlr_screencopy_frame_v1 *frame = + wl_container_of(listener, frame, output_commit); + struct wlr_output_event_commit *event = data; + struct wlr_output *output = frame->output; + struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + assert(renderer); - int damage_x = damage_box->x1; - int damage_y = damage_box->y1; - int damage_width = damage_box->x2 - damage_box->x1; - int damage_height = damage_box->y2 - damage_box->y1; - - zwlr_screencopy_frame_v1_send_damage(frame->resource, - damage_x, damage_y, damage_width, damage_height); - - pixman_region32_clear(&damage->damage); + if (!(event->committed & WLR_OUTPUT_STATE_BUFFER)) { + return; } - time_t tv_sec = event->when->tv_sec; - uint32_t tv_sec_hi = (sizeof(tv_sec) > 4) ? tv_sec >> 32 : 0; - uint32_t tv_sec_lo = tv_sec & 0xFFFFFFFF; - zwlr_screencopy_frame_v1_send_ready(frame->resource, - tv_sec_hi, tv_sec_lo, event->when->tv_nsec); + struct wlr_dmabuf_v1_buffer *dma_buffer = frame->dma_buffer; + if (dma_buffer == NULL) { + return; + } + if (frame->with_damage) { + struct screencopy_damage *damage = + screencopy_damage_get_or_create(frame->client, output); + if (damage && !pixman_region32_not_empty(&damage->damage)) { + return; + } + } + + wl_list_remove(&frame->output_commit.link); + wl_list_init(&frame->output_commit.link); + + // TODO: add support for copying regions with DMA-BUFs + if (frame->box.x != 0 || frame->box.y != 0 || + output->width != frame->box.width || + output->height != frame->box.height) { + zwlr_screencopy_frame_v1_send_failed(frame->resource); + frame_destroy(frame); + return; + } + + struct wlr_dmabuf_attributes attr = { 0 }; + bool ok = wlr_output_export_dmabuf(output, &attr); + ok = ok && wlr_renderer_blit_dmabuf(renderer, + &dma_buffer->attributes, &attr); + uint32_t flags = dma_buffer->attributes.flags & WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT ? + ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT : 0; + wlr_dmabuf_attributes_finish(&attr); + + if (!ok) { + zwlr_screencopy_frame_v1_send_failed(frame->resource); + frame_destroy(frame); + return; + } + + zwlr_screencopy_frame_v1_send_flags(frame->resource, flags); + frame_send_damage(frame); + frame_send_ready(frame, event->when); frame_destroy(frame); } @@ -364,6 +425,9 @@ static void frame_handle_copy(struct wl_client *wl_client, wl_signal_add(&output->events.precommit, &frame->output_precommit); frame->output_precommit.notify = frame_handle_output_precommit; + wl_signal_add(&output->events.commit, &frame->output_commit); + frame->output_commit.notify = frame_handle_output_commit; + wl_signal_add(&output->events.destroy, &frame->output_enable); frame->output_enable.notify = frame_handle_output_enable; @@ -374,7 +438,6 @@ static void frame_handle_copy(struct wl_client *wl_client, frame->buffer_destroy.notify = frame_handle_buffer_destroy; // Schedule a buffer commit - output->needs_frame = true; wlr_output_schedule_frame(output); wlr_output_lock_attach_render(output, true); @@ -467,6 +530,7 @@ static void capture_output(struct wl_client *wl_client, wl_list_insert(&client->manager->frames, &frame->link); wl_list_init(&frame->output_precommit.link); + wl_list_init(&frame->output_commit.link); wl_list_init(&frame->output_enable.link); wl_list_init(&frame->output_destroy.link); wl_list_init(&frame->buffer_destroy.link); diff --git a/types/wlr_surface.c b/types/wlr_surface.c index 30546431f..1f30116f2 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -713,7 +713,7 @@ bool wlr_surface_set_role(struct wlr_surface *surface, if (surface->role != NULL && surface->role != role) { if (error_resource != NULL) { wl_resource_post_error(error_resource, error_code, - "Cannot assign role %s to wl_surface@%d, already has role %s\n", + "Cannot assign role %s to wl_surface@%" PRIu32 ", already has role %s\n", role->name, wl_resource_get_id(surface->resource), surface->role->name); } @@ -721,7 +721,7 @@ bool wlr_surface_set_role(struct wlr_surface *surface, } if (surface->role_data != NULL && surface->role_data != role_data) { wl_resource_post_error(error_resource, error_code, - "Cannot reassign role %s to wl_surface@%d," + "Cannot reassign role %s to wl_surface@%" PRIu32 "," "role object still exists", role->name, wl_resource_get_id(surface->resource)); return false; @@ -792,7 +792,7 @@ static void subsurface_handle_place_above(struct wl_client *client, if (!sibling) { wl_resource_post_error(subsurface->resource, WL_SUBSURFACE_ERROR_BAD_SURFACE, - "%s: wl_surface@%d is not a parent or sibling", + "%s: wl_surface@%" PRIu32 "is not a parent or sibling", "place_above", wl_resource_get_id(sibling_surface->resource)); return; } @@ -819,7 +819,7 @@ static void subsurface_handle_place_below(struct wl_client *client, if (!sibling) { wl_resource_post_error(subsurface->resource, WL_SUBSURFACE_ERROR_BAD_SURFACE, - "%s: wl_surface@%d is not a parent or sibling", + "%s: wl_surface@%" PRIu32 " is not a parent or sibling", "place_below", wl_resource_get_id(sibling_surface->resource)); return; } diff --git a/types/wlr_virtual_keyboard_v1.c b/types/wlr_virtual_keyboard_v1.c index 7ae030eec..25c94545c 100644 --- a/types/wlr_virtual_keyboard_v1.c +++ b/types/wlr_virtual_keyboard_v1.c @@ -1,6 +1,7 @@ #define _POSIX_C_SOURCE 199309L #include #include +#include #include #include #include @@ -72,12 +73,14 @@ static void virtual_keyboard_keymap(struct wl_client *client, keyboard->has_keymap = true; xkb_keymap_unref(keymap); xkb_context_unref(context); + close(fd); return; keymap_fail: fd_fail: xkb_context_unref(context); context_fail: wl_client_post_no_memory(client); + close(fd); } static void virtual_keyboard_key(struct wl_client *client, diff --git a/types/wlr_virtual_pointer_v1.c b/types/wlr_virtual_pointer_v1.c index aed3647be..a9d1fd7bc 100644 --- a/types/wlr_virtual_pointer_v1.c +++ b/types/wlr_virtual_pointer_v1.c @@ -94,7 +94,7 @@ static void virtual_pointer_axis(struct wl_client *client, if (axis > WL_POINTER_AXIS_HORIZONTAL_SCROLL) { wl_resource_post_error(resource, ZWLR_VIRTUAL_POINTER_V1_ERROR_INVALID_AXIS, - "Invalid enumeration value %d", axis); + "Invalid enumeration value %" PRIu32, axis); return; } struct wlr_virtual_pointer_v1 *pointer = @@ -103,7 +103,8 @@ static void virtual_pointer_axis(struct wl_client *client, return; } struct wlr_input_device *wlr_dev = &pointer->input_device; - pointer->axis_valid[axis] = true; + pointer->axis = axis; + pointer->axis_valid[pointer->axis] = true; pointer->axis_event[pointer->axis].device = wlr_dev; pointer->axis_event[pointer->axis].time_msec = time; pointer->axis_event[pointer->axis].orientation = axis; @@ -139,7 +140,7 @@ static void virtual_pointer_axis_source(struct wl_client *client, if (source > WL_POINTER_AXIS_SOURCE_WHEEL_TILT) { wl_resource_post_error(resource, ZWLR_VIRTUAL_POINTER_V1_ERROR_INVALID_AXIS_SOURCE, - "Invalid enumeration value %d", source); + "Invalid enumeration value %" PRIu32, source); return; } struct wlr_virtual_pointer_v1 *pointer = @@ -157,7 +158,7 @@ static void virtual_pointer_axis_stop(struct wl_client *client, if (axis > WL_POINTER_AXIS_HORIZONTAL_SCROLL) { wl_resource_post_error(resource, ZWLR_VIRTUAL_POINTER_V1_ERROR_INVALID_AXIS, - "Invalid enumeration value %d", axis); + "Invalid enumeration value %" PRIu32, axis); return; } struct wlr_virtual_pointer_v1 *pointer = @@ -181,7 +182,7 @@ static void virtual_pointer_axis_discrete(struct wl_client *client, if (axis > WL_POINTER_AXIS_HORIZONTAL_SCROLL) { wl_resource_post_error(resource, ZWLR_VIRTUAL_POINTER_V1_ERROR_INVALID_AXIS, - "Invalid enumeration value %d", axis); + "Invalid enumeration value %" PRIu32, axis); return; } struct wlr_virtual_pointer_v1 *pointer = diff --git a/types/xdg_shell/wlr_xdg_surface.c b/types/xdg_shell/wlr_xdg_surface.c index 7cd12040d..b65490d0a 100644 --- a/types/xdg_shell/wlr_xdg_surface.c +++ b/types/xdg_shell/wlr_xdg_surface.c @@ -336,7 +336,9 @@ static void xdg_surface_handle_surface_commit(struct wl_listener *listener, return; } - if (surface->role == WLR_XDG_SURFACE_ROLE_NONE) { + // surface->role might be NONE for inert popups + // So we check surface->surface->role + if (surface->surface->role == NULL) { wl_resource_post_error(surface->resource, XDG_SURFACE_ERROR_NOT_CONSTRUCTED, "xdg_surface must have a role"); @@ -361,7 +363,8 @@ void handle_xdg_surface_commit(struct wlr_surface *wlr_surface) { switch (surface->role) { case WLR_XDG_SURFACE_ROLE_NONE: - assert(false); + // inert toplevel or popup + return; case WLR_XDG_SURFACE_ROLE_TOPLEVEL: handle_xdg_surface_toplevel_committed(surface); break; @@ -494,7 +497,7 @@ void reset_xdg_surface(struct wlr_xdg_surface *xdg_surface) { break; case WLR_XDG_SURFACE_ROLE_POPUP: wl_resource_set_user_data(xdg_surface->popup->resource, NULL); - xdg_surface->toplevel->resource = NULL; + xdg_surface->popup->resource = NULL; wl_list_remove(&xdg_surface->popup->link); diff --git a/types/xdg_shell/wlr_xdg_toplevel.c b/types/xdg_shell/wlr_xdg_toplevel.c index 3524bd34c..51d0687b5 100644 --- a/types/xdg_shell/wlr_xdg_toplevel.c +++ b/types/xdg_shell/wlr_xdg_toplevel.c @@ -13,60 +13,44 @@ void handle_xdg_toplevel_ack_configure( assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); assert(configure->toplevel_state != NULL); - surface->toplevel->current.maximized = - configure->toplevel_state->maximized; - surface->toplevel->current.fullscreen = - configure->toplevel_state->fullscreen; - surface->toplevel->current.resizing = - configure->toplevel_state->resizing; - surface->toplevel->current.activated = - configure->toplevel_state->activated; - surface->toplevel->current.tiled = - configure->toplevel_state->tiled; + surface->toplevel->last_acked = *configure->toplevel_state; } bool compare_xdg_surface_toplevel_state(struct wlr_xdg_toplevel *state) { - struct { - struct wlr_xdg_toplevel_state state; - uint32_t width, height; - } configured; - // is pending state different from current state? if (!state->base->configured) { return false; } + struct wlr_xdg_toplevel_state *configured = NULL; if (wl_list_empty(&state->base->configure_list)) { - // last configure is actually the current state, just use it - configured.state = state->current; - configured.width = state->base->surface->current.width; - configured.height = state->base->surface->current.height; + // There are currently no pending configures, so check against the last + // state acked by the client. + configured = &state->last_acked; } else { struct wlr_xdg_surface_configure *configure = wl_container_of(state->base->configure_list.prev, configure, link); - configured.state = *configure->toplevel_state; - configured.width = configure->toplevel_state->width; - configured.height = configure->toplevel_state->height; + configured = configure->toplevel_state; } - if (state->server_pending.activated != configured.state.activated) { + if (state->server_pending.activated != configured->activated) { return false; } - if (state->server_pending.fullscreen != configured.state.fullscreen) { + if (state->server_pending.fullscreen != configured->fullscreen) { return false; } - if (state->server_pending.maximized != configured.state.maximized) { + if (state->server_pending.maximized != configured->maximized) { return false; } - if (state->server_pending.resizing != configured.state.resizing) { + if (state->server_pending.resizing != configured->resizing) { return false; } - if (state->server_pending.tiled != configured.state.tiled) { + if (state->server_pending.tiled != configured->tiled) { return false; } - if (state->server_pending.width == configured.width && - state->server_pending.height == configured.height) { + if (state->server_pending.width == configured->width && + state->server_pending.height == configured->height) { return true; } @@ -187,7 +171,10 @@ void handle_xdg_surface_toplevel_committed(struct wlr_xdg_surface *surface) { return; } - // update state that doesn't need compositor approval + // apply state from the last acked configure now that the client committed + surface->toplevel->current = surface->toplevel->last_acked; + + // update state from the client that doesn't need compositor approval surface->toplevel->current.max_width = surface->toplevel->client_pending.max_width; surface->toplevel->current.min_width = diff --git a/types/xdg_shell_v6/wlr_xdg_popup_v6.c b/types/xdg_shell_v6/wlr_xdg_popup_v6.c deleted file mode 100644 index bed7e6d90..000000000 --- a/types/xdg_shell_v6/wlr_xdg_popup_v6.c +++ /dev/null @@ -1,585 +0,0 @@ -#include -#include -#include -#include "types/wlr_xdg_shell_v6.h" -#include "util/signal.h" - -static struct wlr_xdg_surface_v6 *xdg_popup_grab_get_topmost( - struct wlr_xdg_popup_grab_v6 *grab) { - struct wlr_xdg_popup_v6 *popup; - wl_list_for_each(popup, &grab->popups, grab_link) { - return popup->base; - } - - return NULL; -} - -static void xdg_popup_grab_end(struct wlr_xdg_popup_grab_v6 *popup_grab) { - struct wlr_xdg_popup_v6 *popup, *tmp; - wl_list_for_each_safe(popup, tmp, &popup_grab->popups, grab_link) { - zxdg_popup_v6_send_popup_done(popup->resource); - } - - wlr_seat_pointer_end_grab(popup_grab->seat); - wlr_seat_keyboard_end_grab(popup_grab->seat); - wlr_seat_touch_end_grab(popup_grab->seat); -} - -static void xdg_pointer_grab_enter(struct wlr_seat_pointer_grab *grab, - struct wlr_surface *surface, double sx, double sy) { - struct wlr_xdg_popup_grab_v6 *popup_grab = grab->data; - if (wl_resource_get_client(surface->resource) == popup_grab->client) { - wlr_seat_pointer_enter(grab->seat, surface, sx, sy); - } else { - wlr_seat_pointer_clear_focus(grab->seat); - } -} - -static void xdg_pointer_grab_clear_focus(struct wlr_seat_pointer_grab *grab) { - wlr_seat_pointer_clear_focus(grab->seat); -} - -static void xdg_pointer_grab_motion(struct wlr_seat_pointer_grab *grab, - uint32_t time, double sx, double sy) { - wlr_seat_pointer_send_motion(grab->seat, time, sx, sy); -} - -static uint32_t xdg_pointer_grab_button(struct wlr_seat_pointer_grab *grab, - uint32_t time, uint32_t button, uint32_t state) { - uint32_t serial = - wlr_seat_pointer_send_button(grab->seat, time, button, state); - if (serial) { - return serial; - } else { - xdg_popup_grab_end(grab->data); - return 0; - } -} - -static void xdg_pointer_grab_axis(struct wlr_seat_pointer_grab *grab, - uint32_t time, enum wlr_axis_orientation orientation, double value, - int32_t value_discrete, enum wlr_axis_source source) { - wlr_seat_pointer_send_axis(grab->seat, time, orientation, value, - value_discrete, source); -} - -static void xdg_pointer_grab_frame(struct wlr_seat_pointer_grab *grab) { - wlr_seat_pointer_send_frame(grab->seat); -} - -static void xdg_pointer_grab_cancel(struct wlr_seat_pointer_grab *grab) { - xdg_popup_grab_end(grab->data); -} - -static const struct wlr_pointer_grab_interface xdg_pointer_grab_impl = { - .enter = xdg_pointer_grab_enter, - .clear_focus = xdg_pointer_grab_clear_focus, - .motion = xdg_pointer_grab_motion, - .button = xdg_pointer_grab_button, - .cancel = xdg_pointer_grab_cancel, - .axis = xdg_pointer_grab_axis, - .frame = xdg_pointer_grab_frame, -}; - -static void xdg_keyboard_grab_enter(struct wlr_seat_keyboard_grab *grab, - struct wlr_surface *surface, uint32_t keycodes[], size_t num_keycodes, - struct wlr_keyboard_modifiers *modifiers) { - // keyboard focus should remain on the popup -} - -static void xdg_keyboard_grab_clear_focus(struct wlr_seat_keyboard_grab *grab) { - // keyboard focus should remain on the popup -} - -static void xdg_keyboard_grab_key(struct wlr_seat_keyboard_grab *grab, - uint32_t time, uint32_t key, uint32_t state) { - wlr_seat_keyboard_send_key(grab->seat, time, key, state); -} - -static void xdg_keyboard_grab_modifiers(struct wlr_seat_keyboard_grab *grab, - struct wlr_keyboard_modifiers *modifiers) { - wlr_seat_keyboard_send_modifiers(grab->seat, modifiers); -} - -static void xdg_keyboard_grab_cancel(struct wlr_seat_keyboard_grab *grab) { - wlr_seat_pointer_end_grab(grab->seat); -} - -static const struct wlr_keyboard_grab_interface xdg_keyboard_grab_impl = { - .enter = xdg_keyboard_grab_enter, - .clear_focus = xdg_keyboard_grab_clear_focus, - .key = xdg_keyboard_grab_key, - .modifiers = xdg_keyboard_grab_modifiers, - .cancel = xdg_keyboard_grab_cancel, -}; - -static uint32_t xdg_touch_grab_down(struct wlr_seat_touch_grab *grab, - uint32_t time, struct wlr_touch_point *point) { - struct wlr_xdg_popup_grab_v6 *popup_grab = grab->data; - - if (wl_resource_get_client(point->surface->resource) != popup_grab->client) { - xdg_popup_grab_end(grab->data); - return 0; - } - - return wlr_seat_touch_send_down(grab->seat, point->surface, time, - point->touch_id, point->sx, point->sy); -} - -static void xdg_touch_grab_up(struct wlr_seat_touch_grab *grab, - uint32_t time, struct wlr_touch_point *point) { - wlr_seat_touch_send_up(grab->seat, time, point->touch_id); -} - -static void xdg_touch_grab_motion(struct wlr_seat_touch_grab *grab, - uint32_t time, struct wlr_touch_point *point) { - wlr_seat_touch_send_motion(grab->seat, time, point->touch_id, point->sx, - point->sy); -} - -static void xdg_touch_grab_enter(struct wlr_seat_touch_grab *grab, - uint32_t time, struct wlr_touch_point *point) { -} - -static void xdg_touch_grab_cancel(struct wlr_seat_touch_grab *grab) { - wlr_seat_touch_end_grab(grab->seat); -} - -static const struct wlr_touch_grab_interface xdg_touch_grab_impl = { - .down = xdg_touch_grab_down, - .up = xdg_touch_grab_up, - .motion = xdg_touch_grab_motion, - .enter = xdg_touch_grab_enter, - .cancel = xdg_touch_grab_cancel -}; - -static void xdg_popup_grab_handle_seat_destroy( - struct wl_listener *listener, void *data) { - struct wlr_xdg_popup_grab_v6 *xdg_grab = - wl_container_of(listener, xdg_grab, seat_destroy); - - wl_list_remove(&xdg_grab->seat_destroy.link); - - struct wlr_xdg_popup_v6 *popup, *next; - wl_list_for_each_safe(popup, next, &xdg_grab->popups, grab_link) { - destroy_xdg_surface_v6(popup->base); - } - - wl_list_remove(&xdg_grab->link); - free(xdg_grab); -} - -struct wlr_xdg_popup_grab_v6 *get_xdg_shell_v6_popup_grab_from_seat( - struct wlr_xdg_shell_v6 *shell, struct wlr_seat *seat) { - struct wlr_xdg_popup_grab_v6 *xdg_grab; - wl_list_for_each(xdg_grab, &shell->popup_grabs, link) { - if (xdg_grab->seat == seat) { - return xdg_grab; - } - } - - xdg_grab = calloc(1, sizeof(struct wlr_xdg_popup_grab_v6)); - if (!xdg_grab) { - return NULL; - } - - xdg_grab->pointer_grab.data = xdg_grab; - xdg_grab->pointer_grab.interface = &xdg_pointer_grab_impl; - xdg_grab->keyboard_grab.data = xdg_grab; - xdg_grab->keyboard_grab.interface = &xdg_keyboard_grab_impl; - xdg_grab->touch_grab.data = xdg_grab; - xdg_grab->touch_grab.interface = &xdg_touch_grab_impl; - - wl_list_init(&xdg_grab->popups); - - wl_list_insert(&shell->popup_grabs, &xdg_grab->link); - xdg_grab->seat = seat; - - xdg_grab->seat_destroy.notify = xdg_popup_grab_handle_seat_destroy; - wl_signal_add(&seat->events.destroy, &xdg_grab->seat_destroy); - - return xdg_grab; -} - - -static const struct zxdg_popup_v6_interface zxdg_popup_v6_implementation; - -static struct wlr_xdg_surface_v6 *xdg_surface_from_xdg_popup_resource( - struct wl_resource *resource) { - assert(wl_resource_instance_of(resource, &zxdg_popup_v6_interface, - &zxdg_popup_v6_implementation)); - return wl_resource_get_user_data(resource); -} - -void destroy_xdg_popup_v6(struct wlr_xdg_surface_v6 *surface) { - assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP); - unmap_xdg_surface_v6(surface); - - wl_resource_set_user_data(surface->popup->resource, NULL); - wl_list_remove(&surface->popup->link); - free(surface->popup); - surface->popup = NULL; - - surface->role = WLR_XDG_SURFACE_V6_ROLE_NONE; -} - -static void xdg_popup_handle_grab(struct wl_client *client, - struct wl_resource *resource, struct wl_resource *seat_resource, - uint32_t serial) { - struct wlr_xdg_surface_v6 *surface = - xdg_surface_from_xdg_popup_resource(resource); - struct wlr_seat_client *seat_client = - wlr_seat_client_from_resource(seat_resource); - if (!surface) { - return; - } - - if (surface->popup->committed) { - wl_resource_post_error(surface->popup->resource, - ZXDG_POPUP_V6_ERROR_INVALID_GRAB, - "xdg_popup is already mapped"); - return; - } - - struct wlr_xdg_popup_grab_v6 *popup_grab = - get_xdg_shell_v6_popup_grab_from_seat(surface->client->shell, - seat_client->seat); - - struct wlr_xdg_surface_v6 *topmost = xdg_popup_grab_get_topmost(popup_grab); - bool parent_is_toplevel = - surface->popup->parent->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL; - - if ((topmost == NULL && !parent_is_toplevel) || - (topmost != NULL && topmost != surface->popup->parent)) { - wl_resource_post_error(surface->client->resource, - ZXDG_SHELL_V6_ERROR_NOT_THE_TOPMOST_POPUP, - "xdg_popup was not created on the topmost popup"); - return; - } - - popup_grab->client = surface->client->client; - surface->popup->seat = seat_client->seat; - - wl_list_insert(&popup_grab->popups, &surface->popup->grab_link); - - wlr_seat_pointer_start_grab(seat_client->seat, - &popup_grab->pointer_grab); - wlr_seat_keyboard_start_grab(seat_client->seat, - &popup_grab->keyboard_grab); - wlr_seat_touch_start_grab(seat_client->seat, - &popup_grab->touch_grab); -} - -static void xdg_popup_handle_destroy(struct wl_client *client, - struct wl_resource *resource) { - struct wlr_xdg_surface_v6 *surface = - xdg_surface_from_xdg_popup_resource(resource); - - if (surface && !wl_list_empty(&surface->popups)) { - wl_resource_post_error(surface->client->resource, - ZXDG_SHELL_V6_ERROR_NOT_THE_TOPMOST_POPUP, - "xdg_popup was destroyed while it was not the topmost popup"); - return; - } - - wl_resource_destroy(resource); -} - -static const struct zxdg_popup_v6_interface zxdg_popup_v6_implementation = { - .destroy = xdg_popup_handle_destroy, - .grab = xdg_popup_handle_grab, -}; - -static void xdg_popup_handle_resource_destroy(struct wl_resource *resource) { - struct wlr_xdg_surface_v6 *surface = - xdg_surface_from_xdg_popup_resource(resource); - if (surface != NULL) { - destroy_xdg_popup_v6(surface); - } -} - -void handle_xdg_surface_v6_popup_committed(struct wlr_xdg_surface_v6 *surface) { - assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP); - - if (!surface->popup->committed) { - schedule_xdg_surface_v6_configure(surface); - surface->popup->committed = true; - } -} - -const struct wlr_surface_role xdg_popup_v6_surface_role = { - .name = "xdg_popup_v6", - .commit = handle_xdg_surface_v6_commit, - .precommit = handle_xdg_surface_v6_precommit, -}; - -void create_xdg_popup_v6(struct wlr_xdg_surface_v6 *xdg_surface, - struct wlr_xdg_surface_v6 *parent, - struct wlr_xdg_positioner_v6_resource *positioner, int32_t id) { - if (positioner->attrs.size.width == 0 || - positioner->attrs.anchor_rect.width == 0) { - wl_resource_post_error(xdg_surface->resource, - ZXDG_SHELL_V6_ERROR_INVALID_POSITIONER, - "positioner object is not complete"); - return; - } - - if (!wlr_surface_set_role(xdg_surface->surface, &xdg_popup_v6_surface_role, - xdg_surface, xdg_surface->resource, ZXDG_SHELL_V6_ERROR_ROLE)) { - return; - } - - xdg_surface->popup = calloc(1, sizeof(struct wlr_xdg_popup_v6)); - if (!xdg_surface->popup) { - wl_resource_post_no_memory(xdg_surface->resource); - return; - } - - xdg_surface->popup->resource = - wl_resource_create(xdg_surface->client->client, &zxdg_popup_v6_interface, - wl_resource_get_version(xdg_surface->resource), id); - if (xdg_surface->popup->resource == NULL) { - free(xdg_surface->popup); - wl_resource_post_no_memory(xdg_surface->resource); - return; - } - wl_resource_set_implementation(xdg_surface->popup->resource, - &zxdg_popup_v6_implementation, xdg_surface, - xdg_popup_handle_resource_destroy); - - xdg_surface->role = WLR_XDG_SURFACE_V6_ROLE_POPUP; - xdg_surface->popup->base = xdg_surface; - xdg_surface->popup->parent = parent; - xdg_surface->popup->geometry = - wlr_xdg_positioner_v6_get_geometry(&positioner->attrs); - - // positioner properties - memcpy(&xdg_surface->popup->positioner, &positioner->attrs, - sizeof(struct wlr_xdg_positioner_v6)); - - wl_list_insert(&parent->popups, &xdg_surface->popup->link); - - wlr_signal_emit_safe(&parent->events.new_popup, xdg_surface->popup); -} - -void wlr_xdg_popup_v6_get_anchor_point(struct wlr_xdg_popup_v6 *popup, - int *root_sx, int *root_sy) { - struct wlr_box rect = popup->positioner.anchor_rect; - enum zxdg_positioner_v6_anchor anchor = popup->positioner.anchor; - int sx = 0, sy = 0; - - if (anchor == ZXDG_POSITIONER_V6_ANCHOR_NONE) { - sx = (rect.x + rect.width) / 2; - sy = (rect.y + rect.height) / 2; - } else if (anchor == ZXDG_POSITIONER_V6_ANCHOR_TOP) { - sx = (rect.x + rect.width) / 2; - sy = rect.y; - } else if (anchor == ZXDG_POSITIONER_V6_ANCHOR_BOTTOM) { - sx = (rect.x + rect.width) / 2; - sy = rect.y + rect.height; - } else if (anchor == ZXDG_POSITIONER_V6_ANCHOR_LEFT) { - sx = rect.x; - sy = (rect.y + rect.height) / 2; - } else if (anchor == ZXDG_POSITIONER_V6_ANCHOR_RIGHT) { - sx = rect.x + rect.width; - sy = (rect.y + rect.height) / 2; - } else if (anchor == (ZXDG_POSITIONER_V6_ANCHOR_TOP | - ZXDG_POSITIONER_V6_ANCHOR_LEFT)) { - sx = rect.x; - sy = rect.y; - } else if (anchor == (ZXDG_POSITIONER_V6_ANCHOR_TOP | - ZXDG_POSITIONER_V6_ANCHOR_RIGHT)) { - sx = rect.x + rect.width; - sy = rect.y; - } else if (anchor == (ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | - ZXDG_POSITIONER_V6_ANCHOR_LEFT)) { - sx = rect.x; - sy = rect.y + rect.height; - } else if (anchor == (ZXDG_POSITIONER_V6_ANCHOR_BOTTOM | - ZXDG_POSITIONER_V6_ANCHOR_RIGHT)) { - sx = rect.x + rect.width; - sy = rect.y + rect.height; - } - - *root_sx = sx; - *root_sy = sy; -} - -void wlr_xdg_popup_v6_get_toplevel_coords(struct wlr_xdg_popup_v6 *popup, - int popup_sx, int popup_sy, int *toplevel_sx, int *toplevel_sy) { - struct wlr_xdg_surface_v6 *parent = popup->parent; - while (parent != NULL && parent->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) { - popup_sx += parent->popup->geometry.x; - popup_sy += parent->popup->geometry.y; - parent = parent->popup->parent; - } - assert(parent); - - *toplevel_sx = popup_sx + parent->geometry.x; - *toplevel_sy = popup_sy + parent->geometry.y; -} - -static void xdg_popup_v6_box_constraints(struct wlr_xdg_popup_v6 *popup, - struct wlr_box *toplevel_sx_box, int *offset_x, int *offset_y) { - int popup_width = popup->geometry.width; - int popup_height = popup->geometry.height; - int anchor_sx = 0, anchor_sy = 0; - wlr_xdg_popup_v6_get_anchor_point(popup, &anchor_sx, &anchor_sy); - int popup_sx = 0, popup_sy = 0; - wlr_xdg_popup_v6_get_toplevel_coords(popup, popup->geometry.x, - popup->geometry.y, &popup_sx, &popup_sy); - *offset_x = 0, *offset_y = 0; - - if (popup_sx < toplevel_sx_box->x) { - *offset_x = toplevel_sx_box->x - popup_sx; - } else if (popup_sx + popup_width > - toplevel_sx_box->x + toplevel_sx_box->width) { - *offset_x = toplevel_sx_box->x + toplevel_sx_box->width - - (popup_sx + popup_width); - } - - if (popup_sy < toplevel_sx_box->y) { - *offset_y = toplevel_sx_box->y - popup_sy; - } else if (popup_sy + popup_height > - toplevel_sx_box->y + toplevel_sx_box->height) { - *offset_y = toplevel_sx_box->y + toplevel_sx_box->height - - (popup_sy + popup_height); - } -} - -static bool xdg_popup_v6_unconstrain_flip(struct wlr_xdg_popup_v6 *popup, - struct wlr_box *toplevel_sx_box) { - int offset_x = 0, offset_y = 0; - xdg_popup_v6_box_constraints(popup, toplevel_sx_box, - &offset_x, &offset_y); - - if (!offset_x && !offset_y) { - return true; - } - - bool flip_x = offset_x && - (popup->positioner.constraint_adjustment & - ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_X); - - bool flip_y = offset_y && - (popup->positioner.constraint_adjustment & - ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_Y); - - if (flip_x) { - wlr_positioner_v6_invert_x(&popup->positioner); - } - if (flip_y) { - wlr_positioner_v6_invert_y(&popup->positioner); - } - - popup->geometry = - wlr_xdg_positioner_v6_get_geometry(&popup->positioner); - - xdg_popup_v6_box_constraints(popup, toplevel_sx_box, - &offset_x, &offset_y); - - if (!offset_x && !offset_y) { - // no longer constrained - return true; - } - - // revert the positioner back if it didn't fix it and go to the next part - if (offset_x && flip_x) { - wlr_positioner_v6_invert_x(&popup->positioner); - } - if (offset_y && flip_y) { - wlr_positioner_v6_invert_y(&popup->positioner); - } - - popup->geometry = - wlr_xdg_positioner_v6_get_geometry(&popup->positioner); - - return false; -} - -static bool xdg_popup_v6_unconstrain_slide(struct wlr_xdg_popup_v6 *popup, - struct wlr_box *toplevel_sx_box) { - int offset_x = 0, offset_y = 0; - xdg_popup_v6_box_constraints(popup, toplevel_sx_box, - &offset_x, &offset_y); - - if (!offset_x && !offset_y) { - return true; - } - - bool slide_x = offset_x && - (popup->positioner.constraint_adjustment & - ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_X); - - bool slide_y = offset_y && - (popup->positioner.constraint_adjustment & - ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_Y); - - if (slide_x) { - popup->geometry.x += offset_x; - } - - if (slide_y) { - popup->geometry.y += offset_y; - } - - int toplevel_x = 0, toplevel_y = 0; - wlr_xdg_popup_v6_get_toplevel_coords(popup, popup->geometry.x, - popup->geometry.y, &toplevel_x, &toplevel_y); - - if (slide_x && toplevel_x < toplevel_sx_box->x) { - popup->geometry.x += toplevel_sx_box->x - toplevel_x; - } - if (slide_y && toplevel_y < toplevel_sx_box->y) { - popup->geometry.y += toplevel_sx_box->y - toplevel_y; - } - - xdg_popup_v6_box_constraints(popup, toplevel_sx_box, - &offset_x, &offset_y); - - return !offset_x && !offset_y; -} - -static bool xdg_popup_v6_unconstrain_resize(struct wlr_xdg_popup_v6 *popup, - struct wlr_box *toplevel_sx_box) { - int offset_x, offset_y; - xdg_popup_v6_box_constraints(popup, toplevel_sx_box, - &offset_x, &offset_y); - - if (!offset_x && !offset_y) { - return true; - } - - bool resize_x = offset_x && - (popup->positioner.constraint_adjustment & - ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_X); - - bool resize_y = offset_y && - (popup->positioner.constraint_adjustment & - ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y); - - if (resize_x) { - popup->geometry.width -= offset_x; - } - if (resize_y) { - popup->geometry.height -= offset_y; - } - - xdg_popup_v6_box_constraints(popup, toplevel_sx_box, - &offset_x, &offset_y); - - return !offset_x && !offset_y; -} - -void wlr_xdg_popup_v6_unconstrain_from_box(struct wlr_xdg_popup_v6 *popup, - struct wlr_box *toplevel_sx_box) { - if (xdg_popup_v6_unconstrain_flip(popup, toplevel_sx_box)) { - return; - } - if (xdg_popup_v6_unconstrain_slide(popup, toplevel_sx_box)) { - return; - } - if (xdg_popup_v6_unconstrain_resize(popup, toplevel_sx_box)) { - return; - } -} diff --git a/types/xdg_shell_v6/wlr_xdg_positioner_v6.c b/types/xdg_shell_v6/wlr_xdg_positioner_v6.c deleted file mode 100644 index eda19f30c..000000000 --- a/types/xdg_shell_v6/wlr_xdg_positioner_v6.c +++ /dev/null @@ -1,230 +0,0 @@ -#include -#include -#include "types/wlr_xdg_shell_v6.h" - -static const struct zxdg_positioner_v6_interface - zxdg_positioner_v6_implementation; - -struct wlr_xdg_positioner_v6_resource *get_xdg_positioner_v6_from_resource( - struct wl_resource *resource) { - assert(wl_resource_instance_of(resource, &zxdg_positioner_v6_interface, - &zxdg_positioner_v6_implementation)); - return wl_resource_get_user_data(resource); -} - -static void xdg_positioner_destroy(struct wl_resource *resource) { - struct wlr_xdg_positioner_v6_resource *positioner = - get_xdg_positioner_v6_from_resource(resource); - free(positioner); -} - -static void xdg_positioner_handle_destroy(struct wl_client *client, - struct wl_resource *resource) { - wl_resource_destroy(resource); -} - -static void xdg_positioner_handle_set_size(struct wl_client *client, - struct wl_resource *resource, int32_t width, int32_t height) { - struct wlr_xdg_positioner_v6_resource *positioner = - get_xdg_positioner_v6_from_resource(resource); - - if (width < 1 || height < 1) { - wl_resource_post_error(resource, - ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, - "width and height must be positive and non-zero"); - return; - } - - positioner->attrs.size.width = width; - positioner->attrs.size.height = height; -} - -static void xdg_positioner_handle_set_anchor_rect(struct wl_client *client, - struct wl_resource *resource, int32_t x, int32_t y, int32_t width, - int32_t height) { - struct wlr_xdg_positioner_v6_resource *positioner = - get_xdg_positioner_v6_from_resource(resource); - - if (width < 1 || height < 1) { - wl_resource_post_error(resource, - ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, - "width and height must be positive and non-zero"); - return; - } - - positioner->attrs.anchor_rect.x = x; - positioner->attrs.anchor_rect.y = y; - positioner->attrs.anchor_rect.width = width; - positioner->attrs.anchor_rect.height = height; -} - -static void xdg_positioner_handle_set_anchor(struct wl_client *client, - struct wl_resource *resource, uint32_t anchor) { - struct wlr_xdg_positioner_v6_resource *positioner = - get_xdg_positioner_v6_from_resource(resource); - - if (((anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP ) && - (anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM)) || - ((anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT) && - (anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT))) { - wl_resource_post_error(resource, - ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, - "same-axis values are not allowed"); - return; - } - - positioner->attrs.anchor = anchor; -} - -static void xdg_positioner_handle_set_gravity(struct wl_client *client, - struct wl_resource *resource, uint32_t gravity) { - struct wlr_xdg_positioner_v6_resource *positioner = - get_xdg_positioner_v6_from_resource(resource); - - if (((gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP) && - (gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM)) || - ((gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT) && - (gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT))) { - wl_resource_post_error(resource, - ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, - "same-axis values are not allowed"); - return; - } - - positioner->attrs.gravity = gravity; -} - -static void xdg_positioner_handle_set_constraint_adjustment( - struct wl_client *client, struct wl_resource *resource, - uint32_t constraint_adjustment) { - struct wlr_xdg_positioner_v6_resource *positioner = - get_xdg_positioner_v6_from_resource(resource); - - positioner->attrs.constraint_adjustment = constraint_adjustment; -} - -static void xdg_positioner_handle_set_offset(struct wl_client *client, - struct wl_resource *resource, int32_t x, int32_t y) { - struct wlr_xdg_positioner_v6_resource *positioner = - get_xdg_positioner_v6_from_resource(resource); - - positioner->attrs.offset.x = x; - positioner->attrs.offset.y = y; -} - -static const struct zxdg_positioner_v6_interface - zxdg_positioner_v6_implementation = { - .destroy = xdg_positioner_handle_destroy, - .set_size = xdg_positioner_handle_set_size, - .set_anchor_rect = xdg_positioner_handle_set_anchor_rect, - .set_anchor = xdg_positioner_handle_set_anchor, - .set_gravity = xdg_positioner_handle_set_gravity, - .set_constraint_adjustment = - xdg_positioner_handle_set_constraint_adjustment, - .set_offset = xdg_positioner_handle_set_offset, -}; - -struct wlr_box wlr_xdg_positioner_v6_get_geometry( - struct wlr_xdg_positioner_v6 *positioner) { - struct wlr_box geometry = { - .x = positioner->offset.x, - .y = positioner->offset.y, - .width = positioner->size.width, - .height = positioner->size.height, - }; - - if (positioner->anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP) { - geometry.y += positioner->anchor_rect.y; - } else if (positioner->anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM) { - geometry.y += - positioner->anchor_rect.y + positioner->anchor_rect.height; - } else { - geometry.y += - positioner->anchor_rect.y + positioner->anchor_rect.height / 2; - } - - if (positioner->anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT) { - geometry.x += positioner->anchor_rect.x; - } else if (positioner->anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT) { - geometry.x += positioner->anchor_rect.x + positioner->anchor_rect.width; - } else { - geometry.x += - positioner->anchor_rect.x + positioner->anchor_rect.width / 2; - } - - if (positioner->gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP) { - geometry.y -= geometry.height; - } else if (!(positioner->gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM)) { - geometry.y -= geometry.height / 2; - } - - if (positioner->gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT) { - geometry.x -= geometry.width; - } else if (!(positioner->gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT)) { - geometry.x -= geometry.width / 2; - } - - if (positioner->constraint_adjustment == - ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE) { - return geometry; - } - - return geometry; -} - -void wlr_positioner_v6_invert_x(struct wlr_xdg_positioner_v6 *positioner) { - if (positioner->anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT) { - positioner->anchor &= ~ZXDG_POSITIONER_V6_ANCHOR_LEFT; - positioner->anchor |= ZXDG_POSITIONER_V6_ANCHOR_RIGHT; - } else if (positioner->anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT) { - positioner->anchor &= ~ZXDG_POSITIONER_V6_ANCHOR_RIGHT; - positioner->anchor |= ZXDG_POSITIONER_V6_ANCHOR_LEFT; - } - - if (positioner->gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT) { - positioner->gravity &= ~ZXDG_POSITIONER_V6_GRAVITY_RIGHT; - positioner->gravity |= ZXDG_POSITIONER_V6_GRAVITY_LEFT; - } else if (positioner->gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT) { - positioner->gravity &= ~ZXDG_POSITIONER_V6_GRAVITY_LEFT; - positioner->gravity |= ZXDG_POSITIONER_V6_GRAVITY_RIGHT; - } -} - -void wlr_positioner_v6_invert_y( - struct wlr_xdg_positioner_v6 *positioner) { - if (positioner->anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP) { - positioner->anchor &= ~ZXDG_POSITIONER_V6_ANCHOR_TOP; - positioner->anchor |= ZXDG_POSITIONER_V6_ANCHOR_BOTTOM; - } else if (positioner->anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM) { - positioner->anchor &= ~ZXDG_POSITIONER_V6_ANCHOR_BOTTOM; - positioner->anchor |= ZXDG_POSITIONER_V6_ANCHOR_TOP; - } - - if (positioner->gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP) { - positioner->gravity &= ~ZXDG_POSITIONER_V6_GRAVITY_TOP; - positioner->gravity |= ZXDG_POSITIONER_V6_GRAVITY_BOTTOM; - } else if (positioner->gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM) { - positioner->gravity &= ~ZXDG_POSITIONER_V6_GRAVITY_BOTTOM; - positioner->gravity |= ZXDG_POSITIONER_V6_GRAVITY_TOP; - } -} - -void create_xdg_positioner_v6(struct wlr_xdg_client_v6 *client, uint32_t id) { - struct wlr_xdg_positioner_v6_resource *positioner = - calloc(1, sizeof(struct wlr_xdg_positioner_v6_resource)); - if (positioner == NULL) { - wl_client_post_no_memory(client->client); - return; - } - - positioner->resource = wl_resource_create(client->client, - &zxdg_positioner_v6_interface, - wl_resource_get_version(client->resource), id); - if (positioner->resource == NULL) { - free(positioner); - wl_client_post_no_memory(client->client); - return; - } - wl_resource_set_implementation(positioner->resource, - &zxdg_positioner_v6_implementation, positioner, xdg_positioner_destroy); -} diff --git a/types/xdg_shell_v6/wlr_xdg_shell_v6.c b/types/xdg_shell_v6/wlr_xdg_shell_v6.c deleted file mode 100644 index fcaa41e16..000000000 --- a/types/xdg_shell_v6/wlr_xdg_shell_v6.c +++ /dev/null @@ -1,168 +0,0 @@ -#include -#include -#include "types/wlr_xdg_shell_v6.h" -#include "util/signal.h" - -#define SHELL_VERSION 1 - -static const struct zxdg_shell_v6_interface xdg_shell_impl; - -static struct wlr_xdg_client_v6 *xdg_client_from_resource( - struct wl_resource *resource) { - assert(wl_resource_instance_of(resource, &zxdg_shell_v6_interface, - &xdg_shell_impl)); - return wl_resource_get_user_data(resource); -} - -static void xdg_shell_handle_create_positioner(struct wl_client *wl_client, - struct wl_resource *resource, uint32_t id) { - struct wlr_xdg_client_v6 *client = - xdg_client_from_resource(resource); - create_xdg_positioner_v6(client, id); -} - -static void xdg_shell_handle_get_xdg_surface(struct wl_client *wl_client, - struct wl_resource *client_resource, uint32_t id, - struct wl_resource *surface_resource) { - struct wlr_xdg_client_v6 *client = - xdg_client_from_resource(client_resource); - struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); - create_xdg_surface_v6(client, surface, id); -} - -static void xdg_shell_handle_pong(struct wl_client *wl_client, - struct wl_resource *resource, uint32_t serial) { - struct wlr_xdg_client_v6 *client = xdg_client_from_resource(resource); - - if (client->ping_serial != serial) { - return; - } - - wl_event_source_timer_update(client->ping_timer, 0); - client->ping_serial = 0; -} - -static void xdg_shell_handle_destroy(struct wl_client *wl_client, - struct wl_resource *resource) { - struct wlr_xdg_client_v6 *client = xdg_client_from_resource(resource); - - if (!wl_list_empty(&client->surfaces)) { - wl_resource_post_error(client->resource, - ZXDG_SHELL_V6_ERROR_DEFUNCT_SURFACES, - "xdg_wm_base was destroyed before children"); - return; - } - - wl_resource_destroy(resource); -} - -static const struct zxdg_shell_v6_interface xdg_shell_impl = { - .destroy = xdg_shell_handle_destroy, - .create_positioner = xdg_shell_handle_create_positioner, - .get_xdg_surface = xdg_shell_handle_get_xdg_surface, - .pong = xdg_shell_handle_pong, -}; - -static void xdg_client_v6_handle_resource_destroy( - struct wl_resource *resource) { - struct wlr_xdg_client_v6 *client = xdg_client_from_resource(resource); - - struct wlr_xdg_surface_v6 *surface, *tmp = NULL; - wl_list_for_each_safe(surface, tmp, &client->surfaces, link) { - wl_resource_destroy(surface->resource); - } - - if (client->ping_timer != NULL) { - wl_event_source_remove(client->ping_timer); - } - - wl_list_remove(&client->link); - free(client); -} - -static int xdg_client_v6_ping_timeout(void *user_data) { - struct wlr_xdg_client_v6 *client = user_data; - - struct wlr_xdg_surface_v6 *surface; - wl_list_for_each(surface, &client->surfaces, link) { - wlr_signal_emit_safe(&surface->events.ping_timeout, surface); - } - - client->ping_serial = 0; - return 1; -} - -static void xdg_shell_bind(struct wl_client *wl_client, void *data, - uint32_t version, uint32_t id) { - struct wlr_xdg_shell_v6 *xdg_shell = data; - assert(wl_client && xdg_shell); - - struct wlr_xdg_client_v6 *client = - calloc(1, sizeof(struct wlr_xdg_client_v6)); - if (client == NULL) { - wl_client_post_no_memory(wl_client); - return; - } - - wl_list_init(&client->surfaces); - - client->resource = - wl_resource_create(wl_client, &zxdg_shell_v6_interface, version, id); - if (client->resource == NULL) { - free(client); - wl_client_post_no_memory(wl_client); - return; - } - client->client = wl_client; - client->shell = xdg_shell; - - wl_resource_set_implementation(client->resource, &xdg_shell_impl, client, - xdg_client_v6_handle_resource_destroy); - wl_list_insert(&xdg_shell->clients, &client->link); - - struct wl_display *display = wl_client_get_display(client->client); - struct wl_event_loop *loop = wl_display_get_event_loop(display); - client->ping_timer = wl_event_loop_add_timer(loop, - xdg_client_v6_ping_timeout, client); - if (client->ping_timer == NULL) { - wl_client_post_no_memory(client->client); - } -} - -static void handle_display_destroy(struct wl_listener *listener, void *data) { - struct wlr_xdg_shell_v6 *xdg_shell = - wl_container_of(listener, xdg_shell, display_destroy); - wlr_signal_emit_safe(&xdg_shell->events.destroy, xdg_shell); - wl_list_remove(&xdg_shell->display_destroy.link); - wl_global_destroy(xdg_shell->global); - free(xdg_shell); -} - -struct wlr_xdg_shell_v6 *wlr_xdg_shell_v6_create(struct wl_display *display) { - struct wlr_xdg_shell_v6 *xdg_shell = - calloc(1, sizeof(struct wlr_xdg_shell_v6)); - if (!xdg_shell) { - return NULL; - } - - xdg_shell->ping_timeout = 10000; - - wl_list_init(&xdg_shell->clients); - wl_list_init(&xdg_shell->popup_grabs); - - struct wl_global *global = wl_global_create(display, - &zxdg_shell_v6_interface, SHELL_VERSION, xdg_shell, xdg_shell_bind); - if (!global) { - free(xdg_shell); - return NULL; - } - xdg_shell->global = global; - - wl_signal_init(&xdg_shell->events.new_surface); - wl_signal_init(&xdg_shell->events.destroy); - - xdg_shell->display_destroy.notify = handle_display_destroy; - wl_display_add_destroy_listener(display, &xdg_shell->display_destroy); - - return xdg_shell; -} diff --git a/types/xdg_shell_v6/wlr_xdg_surface_v6.c b/types/xdg_shell_v6/wlr_xdg_surface_v6.c deleted file mode 100644 index d9559b988..000000000 --- a/types/xdg_shell_v6/wlr_xdg_surface_v6.c +++ /dev/null @@ -1,606 +0,0 @@ -#include -#include -#include -#include -#include "types/wlr_xdg_shell_v6.h" -#include "util/signal.h" - -bool wlr_surface_is_xdg_surface_v6(struct wlr_surface *surface) { - return surface->role == &xdg_toplevel_v6_surface_role || - surface->role == &xdg_popup_v6_surface_role; -} - -struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6_from_wlr_surface( - struct wlr_surface *surface) { - assert(wlr_surface_is_xdg_surface_v6(surface)); - return (struct wlr_xdg_surface_v6 *)surface->role_data; -} - -static const struct zxdg_surface_v6_interface zxdg_surface_v6_implementation; - -static struct wlr_xdg_surface_v6 *xdg_surface_from_resource( - struct wl_resource *resource) { - assert(wl_resource_instance_of(resource, &zxdg_surface_v6_interface, - &zxdg_surface_v6_implementation)); - return wl_resource_get_user_data(resource); -} - -static void xdg_surface_configure_destroy( - struct wlr_xdg_surface_v6_configure *configure) { - if (configure == NULL) { - return; - } - wl_list_remove(&configure->link); - free(configure->toplevel_state); - free(configure); -} - -void unmap_xdg_surface_v6(struct wlr_xdg_surface_v6 *surface) { - assert(surface->role != WLR_XDG_SURFACE_V6_ROLE_NONE); - - // TODO: probably need to ungrab before this event - if (surface->mapped) { - wlr_signal_emit_safe(&surface->events.unmap, surface); - } - - switch (surface->role) { - case WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL: - free(surface->toplevel->title); - surface->toplevel->title = NULL; - free(surface->toplevel->app_id); - surface->toplevel->app_id = NULL; - break; - case WLR_XDG_SURFACE_V6_ROLE_POPUP: - if (surface->popup->seat != NULL) { - struct wlr_xdg_popup_grab_v6 *grab = - get_xdg_shell_v6_popup_grab_from_seat(surface->client->shell, - surface->popup->seat); - - wl_list_remove(&surface->popup->grab_link); - - if (wl_list_empty(&grab->popups)) { - if (grab->seat->pointer_state.grab == &grab->pointer_grab) { - wlr_seat_pointer_end_grab(grab->seat); - } - if (grab->seat->keyboard_state.grab == &grab->keyboard_grab) { - wlr_seat_keyboard_end_grab(grab->seat); - } - } - - surface->popup->seat = NULL; - } - break; - case WLR_XDG_SURFACE_V6_ROLE_NONE: - assert(false && "not reached"); - } - - struct wlr_xdg_surface_v6_configure *configure, *tmp; - wl_list_for_each_safe(configure, tmp, &surface->configure_list, link) { - xdg_surface_configure_destroy(configure); - } - - surface->configured = surface->mapped = false; - surface->configure_serial = 0; - if (surface->configure_idle) { - wl_event_source_remove(surface->configure_idle); - surface->configure_idle = NULL; - } - surface->configure_next_serial = 0; - - surface->has_next_geometry = false; - memset(&surface->geometry, 0, sizeof(struct wlr_box)); - memset(&surface->next_geometry, 0, sizeof(struct wlr_box)); -} - -void destroy_xdg_surface_v6(struct wlr_xdg_surface_v6 *surface) { - if (surface->role != WLR_XDG_SURFACE_V6_ROLE_NONE) { - unmap_xdg_surface_v6(surface); - } - - wlr_signal_emit_safe(&surface->events.destroy, surface); - - struct wlr_xdg_popup_v6 *popup_state, *next; - wl_list_for_each_safe(popup_state, next, &surface->popups, link) { - zxdg_popup_v6_send_popup_done(popup_state->resource); - destroy_xdg_popup_v6(popup_state->base); - } - - switch (surface->role) { - case WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL: - destroy_xdg_toplevel_v6(surface); - break; - case WLR_XDG_SURFACE_V6_ROLE_POPUP: - destroy_xdg_popup_v6(surface); - break; - case WLR_XDG_SURFACE_V6_ROLE_NONE: - // This space is intentionally left blank - break; - } - - wl_resource_set_user_data(surface->resource, NULL); - surface->surface->role_data = NULL; - wl_list_remove(&surface->link); - wl_list_remove(&surface->surface_destroy.link); - wl_list_remove(&surface->surface_commit.link); - free(surface); -} - -static void xdg_surface_handle_get_toplevel(struct wl_client *client, - struct wl_resource *resource, uint32_t id) { - struct wlr_xdg_surface_v6 *xdg_surface = xdg_surface_from_resource(resource); - create_xdg_toplevel_v6(xdg_surface, id); -} - -static void xdg_surface_handle_get_popup(struct wl_client *wl_client, - struct wl_resource *resource, uint32_t id, - struct wl_resource *parent_resource, - struct wl_resource *positioner_resource) { - struct wlr_xdg_surface_v6 *xdg_surface = - xdg_surface_from_resource(resource); - struct wlr_xdg_surface_v6 *parent = - xdg_surface_from_resource(parent_resource); - struct wlr_xdg_positioner_v6_resource *positioner = - get_xdg_positioner_v6_from_resource(positioner_resource); - - create_xdg_popup_v6(xdg_surface, parent, positioner, id); -} - -static void xdg_surface_handle_ack_configure(struct wl_client *client, - struct wl_resource *resource, uint32_t serial) { - struct wlr_xdg_surface_v6 *surface = xdg_surface_from_resource(resource); - - if (surface->role == WLR_XDG_SURFACE_V6_ROLE_NONE) { - wl_resource_post_error(surface->resource, - ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED, - "xdg_surface must have a role"); - return; - } - - // First find the ack'ed configure - bool found = false; - struct wlr_xdg_surface_v6_configure *configure, *tmp; - wl_list_for_each(configure, &surface->configure_list, link) { - if (configure->serial == serial) { - found = true; - break; - } - } - if (!found) { - wl_resource_post_error(surface->client->resource, - ZXDG_SHELL_V6_ERROR_INVALID_SURFACE_STATE, - "wrong configure serial: %u", serial); - return; - } - // Then remove old configures from the list - wl_list_for_each_safe(configure, tmp, &surface->configure_list, link) { - if (configure->serial == serial) { - break; - } - xdg_surface_configure_destroy(configure); - } - - switch (surface->role) { - case WLR_XDG_SURFACE_V6_ROLE_NONE: - assert(0 && "not reached"); - break; - case WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL: - handle_xdg_toplevel_v6_ack_configure(surface, configure); - break; - case WLR_XDG_SURFACE_V6_ROLE_POPUP: - break; - } - - surface->configured = true; - surface->configure_serial = serial; - - xdg_surface_configure_destroy(configure); -} - -static void xdg_surface_handle_set_window_geometry(struct wl_client *client, - struct wl_resource *resource, int32_t x, int32_t y, int32_t width, - int32_t height) { - struct wlr_xdg_surface_v6 *surface = xdg_surface_from_resource(resource); - - if (surface->role == WLR_XDG_SURFACE_V6_ROLE_NONE) { - wl_resource_post_error(surface->resource, - ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED, - "xdg_surface must have a role"); - return; - } - - if (width <= 0 || height <= 0) { - wlr_log(WLR_ERROR, "Client tried to set invalid geometry"); - wl_resource_post_error(resource, -1, "Tried to set invalid xdg-surface geometry"); - } - - - surface->has_next_geometry = true; - surface->next_geometry.height = height; - surface->next_geometry.width = width; - surface->next_geometry.x = x; - surface->next_geometry.y = y; -} - -static void xdg_surface_handle_destroy(struct wl_client *client, - struct wl_resource *resource) { - struct wlr_xdg_surface_v6 *surface = xdg_surface_from_resource(resource); - - if (surface->role != WLR_XDG_SURFACE_V6_ROLE_NONE) { - wlr_log(WLR_ERROR, "Tried to destroy an xdg_surface before its role " - "object"); - return; - } - - wl_resource_destroy(resource); -} - -static const struct zxdg_surface_v6_interface zxdg_surface_v6_implementation = { - .destroy = xdg_surface_handle_destroy, - .get_toplevel = xdg_surface_handle_get_toplevel, - .get_popup = xdg_surface_handle_get_popup, - .ack_configure = xdg_surface_handle_ack_configure, - .set_window_geometry = xdg_surface_handle_set_window_geometry, -}; - -static void xdg_surface_send_configure(void *user_data) { - struct wlr_xdg_surface_v6 *surface = user_data; - - surface->configure_idle = NULL; - - struct wlr_xdg_surface_v6_configure *configure = - calloc(1, sizeof(struct wlr_xdg_surface_v6_configure)); - if (configure == NULL) { - wl_client_post_no_memory(surface->client->client); - return; - } - - wl_list_insert(surface->configure_list.prev, &configure->link); - configure->serial = surface->configure_next_serial; - - switch (surface->role) { - case WLR_XDG_SURFACE_V6_ROLE_NONE: - assert(0 && "not reached"); - break; - case WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL: - send_xdg_toplevel_v6_configure(surface, configure); - break; - case WLR_XDG_SURFACE_V6_ROLE_POPUP: - zxdg_popup_v6_send_configure(surface->popup->resource, - surface->popup->geometry.x, - surface->popup->geometry.y, - surface->popup->geometry.width, - surface->popup->geometry.height); - break; - } - - zxdg_surface_v6_send_configure(surface->resource, configure->serial); -} - -uint32_t schedule_xdg_surface_v6_configure(struct wlr_xdg_surface_v6 *surface) { - struct wl_display *display = wl_client_get_display(surface->client->client); - struct wl_event_loop *loop = wl_display_get_event_loop(display); - bool pending_same = false; - - switch (surface->role) { - case WLR_XDG_SURFACE_V6_ROLE_NONE: - assert(0 && "not reached"); - break; - case WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL: - pending_same = - compare_xdg_surface_v6_toplevel_state(surface->toplevel); - break; - case WLR_XDG_SURFACE_V6_ROLE_POPUP: - break; - } - - if (surface->configure_idle != NULL) { - if (!pending_same) { - // configure request already scheduled - return surface->configure_next_serial; - } - - // configure request not necessary anymore - wl_event_source_remove(surface->configure_idle); - surface->configure_idle = NULL; - return 0; - } else { - if (pending_same) { - // configure request not necessary - return 0; - } - - surface->configure_next_serial = wl_display_next_serial(display); - surface->configure_idle = wl_event_loop_add_idle(loop, - xdg_surface_send_configure, surface); - return surface->configure_next_serial; - } -} - -void wlr_xdg_surface_v6_ping(struct wlr_xdg_surface_v6 *surface) { - if (surface->client->ping_serial != 0) { - // already pinged - return; - } - - surface->client->ping_serial = - wl_display_next_serial(wl_client_get_display(surface->client->client)); - wl_event_source_timer_update(surface->client->ping_timer, - surface->client->shell->ping_timeout); - zxdg_shell_v6_send_ping(surface->client->resource, - surface->client->ping_serial); -} - -void wlr_xdg_surface_v6_send_close(struct wlr_xdg_surface_v6 *surface) { - switch (surface->role) { - case WLR_XDG_SURFACE_V6_ROLE_NONE: - assert(0 && "not reached"); - break; - case WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL: - if (surface->toplevel) { - zxdg_toplevel_v6_send_close(surface->toplevel->resource); - } - break; - case WLR_XDG_SURFACE_V6_ROLE_POPUP: - if (surface->popup) { - zxdg_popup_v6_send_popup_done(surface->popup->resource); - } - break; - } -} - -static void xdg_surface_handle_surface_destroy(struct wl_listener *listener, - void *data) { - struct wlr_xdg_surface_v6 *xdg_surface = - wl_container_of(listener, xdg_surface, surface_destroy); - destroy_xdg_surface_v6(xdg_surface); -} - -static void xdg_surface_handle_surface_commit(struct wl_listener *listener, - void *data) { - struct wlr_xdg_surface_v6 *surface = - wl_container_of(listener, surface, surface_commit); - - if (wlr_surface_has_buffer(surface->surface) && !surface->configured) { - wl_resource_post_error(surface->resource, - ZXDG_SURFACE_V6_ERROR_UNCONFIGURED_BUFFER, - "xdg_surface has never been configured"); - return; - } - - if (surface->role == WLR_XDG_SURFACE_V6_ROLE_NONE) { - wl_resource_post_error(surface->resource, - ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED, - "xdg_surface must have a role"); - return; - } -} - -void handle_xdg_surface_v6_commit(struct wlr_surface *wlr_surface) { - struct wlr_xdg_surface_v6 *surface = - wlr_xdg_surface_v6_from_wlr_surface(wlr_surface); - if (surface == NULL) { - return; - } - - if (surface->has_next_geometry) { - surface->has_next_geometry = false; - surface->geometry.x = surface->next_geometry.x; - surface->geometry.y = surface->next_geometry.y; - surface->geometry.width = surface->next_geometry.width; - surface->geometry.height = surface->next_geometry.height; - } - - switch (surface->role) { - case WLR_XDG_SURFACE_V6_ROLE_NONE: - assert(false); - case WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL: - handle_xdg_surface_v6_toplevel_committed(surface); - break; - case WLR_XDG_SURFACE_V6_ROLE_POPUP: - handle_xdg_surface_v6_popup_committed(surface); - break; - } - - if (!surface->added) { - surface->added = true; - wlr_signal_emit_safe(&surface->client->shell->events.new_surface, - surface); - } - if (surface->configured && wlr_surface_has_buffer(surface->surface) && - !surface->mapped) { - surface->mapped = true; - wlr_signal_emit_safe(&surface->events.map, surface); - } -} - -void handle_xdg_surface_v6_precommit(struct wlr_surface *wlr_surface) { - struct wlr_xdg_surface_v6 *surface = - wlr_xdg_surface_v6_from_wlr_surface(wlr_surface); - if (surface == NULL) { - return; - } - - if (wlr_surface->pending.committed & WLR_SURFACE_STATE_BUFFER && - wlr_surface->pending.buffer_resource == NULL) { - // This is a NULL commit - if (surface->configured && surface->mapped) { - unmap_xdg_surface_v6(surface); - } - } -} - -static void xdg_surface_handle_resource_destroy(struct wl_resource *resource) { - struct wlr_xdg_surface_v6 *surface = xdg_surface_from_resource(resource); - if (surface != NULL) { - destroy_xdg_surface_v6(surface); - } -} - -struct wlr_xdg_surface_v6 *create_xdg_surface_v6( - struct wlr_xdg_client_v6 *client, struct wlr_surface *surface, - uint32_t id) { - if (wlr_surface_has_buffer(surface)) { - wl_resource_post_error(client->resource, - ZXDG_SURFACE_V6_ERROR_UNCONFIGURED_BUFFER, - "xdg_surface must not have a buffer at creation"); - return NULL; - } - - struct wlr_xdg_surface_v6 *xdg_surface = - calloc(1, sizeof(struct wlr_xdg_surface_v6)); - if (xdg_surface == NULL) { - wl_client_post_no_memory(client->client); - return NULL; - } - - xdg_surface->client = client; - xdg_surface->role = WLR_XDG_SURFACE_V6_ROLE_NONE; - xdg_surface->surface = surface; - xdg_surface->resource = wl_resource_create(client->client, - &zxdg_surface_v6_interface, wl_resource_get_version(client->resource), - id); - if (xdg_surface->resource == NULL) { - free(xdg_surface); - wl_client_post_no_memory(client->client); - return NULL; - } - wl_resource_set_implementation(xdg_surface->resource, - &zxdg_surface_v6_implementation, xdg_surface, - xdg_surface_handle_resource_destroy); - - wl_list_init(&xdg_surface->configure_list); - wl_list_init(&xdg_surface->popups); - - wl_signal_init(&xdg_surface->events.destroy); - wl_signal_init(&xdg_surface->events.ping_timeout); - wl_signal_init(&xdg_surface->events.new_popup); - wl_signal_init(&xdg_surface->events.map); - wl_signal_init(&xdg_surface->events.unmap); - - wl_signal_add(&xdg_surface->surface->events.destroy, - &xdg_surface->surface_destroy); - xdg_surface->surface_destroy.notify = xdg_surface_handle_surface_destroy; - - wl_signal_add(&xdg_surface->surface->events.commit, - &xdg_surface->surface_commit); - xdg_surface->surface_commit.notify = xdg_surface_handle_surface_commit; - - wlr_log(WLR_DEBUG, "new xdg_surface %p (res %p)", xdg_surface, - xdg_surface->resource); - wl_list_insert(&client->surfaces, &xdg_surface->link); - - return xdg_surface; -} - -static void xdg_popup_v6_get_position(struct wlr_xdg_popup_v6 *popup, - double *popup_sx, double *popup_sy) { - struct wlr_xdg_surface_v6 *parent = popup->parent; - struct wlr_box parent_geo; - wlr_xdg_surface_v6_get_geometry(parent, &parent_geo); - *popup_sx = parent_geo.x + popup->geometry.x - - popup->base->geometry.x; - *popup_sy = parent_geo.y + popup->geometry.y - - popup->base->geometry.y; -} - -struct wlr_surface *wlr_xdg_surface_v6_surface_at( - struct wlr_xdg_surface_v6 *surface, double sx, double sy, - double *sub_x, double *sub_y) { - struct wlr_xdg_popup_v6 *popup_state; - wl_list_for_each(popup_state, &surface->popups, link) { - struct wlr_xdg_surface_v6 *popup = popup_state->base; - - double popup_sx, popup_sy; - xdg_popup_v6_get_position(popup_state, &popup_sx, &popup_sy); - - struct wlr_surface *sub = wlr_xdg_surface_v6_surface_at(popup, - sx - popup_sx, - sy - popup_sy, - sub_x, sub_y); - if (sub != NULL) { - return sub; - } - } - - return wlr_surface_surface_at(surface->surface, sx, sy, sub_x, sub_y); -} - -struct xdg_surface_v6_iterator_data { - wlr_surface_iterator_func_t user_iterator; - void *user_data; - int x, y; -}; - -static void xdg_surface_v6_iterator(struct wlr_surface *surface, - int sx, int sy, void *data) { - struct xdg_surface_v6_iterator_data *iter_data = data; - iter_data->user_iterator(surface, iter_data->x + sx, iter_data->y + sy, - iter_data->user_data); -} - -static void xdg_surface_v6_for_each_surface(struct wlr_xdg_surface_v6 *surface, - int x, int y, wlr_surface_iterator_func_t iterator, void *user_data) { - struct xdg_surface_v6_iterator_data data = { - .user_iterator = iterator, - .user_data = user_data, - .x = x, .y = y, - }; - wlr_surface_for_each_surface(surface->surface, xdg_surface_v6_iterator, - &data); - - struct wlr_xdg_popup_v6 *popup_state; - wl_list_for_each(popup_state, &surface->popups, link) { - struct wlr_xdg_surface_v6 *popup = popup_state->base; - if (!popup->configured) { - continue; - } - - double popup_sx, popup_sy; - xdg_popup_v6_get_position(popup_state, &popup_sx, &popup_sy); - - xdg_surface_v6_for_each_surface(popup, - x + popup_sx, - y + popup_sy, - iterator, user_data); - } -} - -static void xdg_surface_v6_for_each_popup(struct wlr_xdg_surface_v6 *surface, - int x, int y, wlr_surface_iterator_func_t iterator, void *user_data) { - struct wlr_xdg_popup_v6 *popup_state; - wl_list_for_each(popup_state, &surface->popups, link) { - struct wlr_xdg_surface_v6 *popup = popup_state->base; - if (!popup->configured) { - continue; - } - - double popup_sx, popup_sy; - xdg_popup_v6_get_position(popup_state, &popup_sx, &popup_sy); - iterator(popup->surface, x + popup_sx, y + popup_sy, user_data); - - xdg_surface_v6_for_each_popup(popup, - x + popup_sx, - y + popup_sy, - iterator, user_data); - } -} - -void wlr_xdg_surface_v6_for_each_surface(struct wlr_xdg_surface_v6 *surface, - wlr_surface_iterator_func_t iterator, void *user_data) { - xdg_surface_v6_for_each_surface(surface, 0, 0, iterator, user_data); -} - -void wlr_xdg_surface_v6_for_each_popup(struct wlr_xdg_surface_v6 *surface, - wlr_surface_iterator_func_t iterator, void *user_data) { - xdg_surface_v6_for_each_popup(surface, 0, 0, iterator, user_data); -} - -void wlr_xdg_surface_v6_get_geometry(struct wlr_xdg_surface_v6 *surface, struct wlr_box *box) { - wlr_surface_get_extends(surface->surface, box); - /* The client never set the geometry */ - if (!surface->geometry.width) { - return; - } - - wlr_box_intersection(box, &surface->geometry, box); -} diff --git a/types/xdg_shell_v6/wlr_xdg_toplevel_v6.c b/types/xdg_shell_v6/wlr_xdg_toplevel_v6.c deleted file mode 100644 index a7a7d12bc..000000000 --- a/types/xdg_shell_v6/wlr_xdg_toplevel_v6.c +++ /dev/null @@ -1,536 +0,0 @@ -#define _POSIX_C_SOURCE 200809L -#include -#include -#include -#include -#include "types/wlr_xdg_shell_v6.h" -#include "util/signal.h" - -static const struct zxdg_toplevel_v6_interface zxdg_toplevel_v6_implementation; - -static struct wlr_xdg_surface_v6 *xdg_surface_from_xdg_toplevel_resource( - struct wl_resource *resource) { - assert(wl_resource_instance_of(resource, &zxdg_toplevel_v6_interface, - &zxdg_toplevel_v6_implementation)); - return wl_resource_get_user_data(resource); -} - -void destroy_xdg_toplevel_v6(struct wlr_xdg_surface_v6 *surface) { - assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL); - unmap_xdg_surface_v6(surface); - - if (surface->toplevel->client_pending.fullscreen_output) { - struct wlr_xdg_toplevel_v6_state *client_pending = - &surface->toplevel->client_pending; - wl_list_remove(&client_pending->fullscreen_output_destroy.link); - } - wl_resource_set_user_data(surface->toplevel->resource, NULL); - free(surface->toplevel); - surface->toplevel = NULL; - - surface->role = WLR_XDG_SURFACE_V6_ROLE_NONE; -} - -static void xdg_toplevel_handle_destroy(struct wl_client *client, - struct wl_resource *resource) { - wl_resource_destroy(resource); -} - -static void xdg_toplevel_handle_set_parent(struct wl_client *client, - struct wl_resource *resource, struct wl_resource *parent_resource) { - struct wlr_xdg_surface_v6 *surface = - xdg_surface_from_xdg_toplevel_resource(resource); - - struct wlr_xdg_surface_v6 *parent = NULL; - if (parent_resource != NULL) { - parent = xdg_surface_from_xdg_toplevel_resource(parent_resource); - } - - surface->toplevel->parent = parent; - wlr_signal_emit_safe(&surface->toplevel->events.set_parent, surface); -} - -static void xdg_toplevel_handle_set_title(struct wl_client *client, - struct wl_resource *resource, const char *title) { - struct wlr_xdg_surface_v6 *surface = - xdg_surface_from_xdg_toplevel_resource(resource); - - char *tmp = strdup(title); - if (tmp == NULL) { - return; - } - - free(surface->toplevel->title); - surface->toplevel->title = tmp; - wlr_signal_emit_safe(&surface->toplevel->events.set_title, surface); -} - -static void xdg_toplevel_handle_set_app_id(struct wl_client *client, - struct wl_resource *resource, const char *app_id) { - struct wlr_xdg_surface_v6 *surface = - xdg_surface_from_xdg_toplevel_resource(resource); - - char *tmp = strdup(app_id); - if (tmp == NULL) { - return; - } - - free(surface->toplevel->app_id); - surface->toplevel->app_id = tmp; - wlr_signal_emit_safe(&surface->toplevel->events.set_app_id, surface); -} - -static void xdg_toplevel_handle_show_window_menu(struct wl_client *client, - struct wl_resource *resource, struct wl_resource *seat_resource, - uint32_t serial, int32_t x, int32_t y) { - struct wlr_xdg_surface_v6 *surface = - xdg_surface_from_xdg_toplevel_resource(resource); - struct wlr_seat_client *seat = - wlr_seat_client_from_resource(seat_resource); - - if (!surface->configured) { - wl_resource_post_error(surface->toplevel->resource, - ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED, - "surface has not been configured yet"); - return; - } - - if (!wlr_seat_validate_grab_serial(seat->seat, serial)) { - wlr_log(WLR_DEBUG, "invalid serial for grab"); - return; - } - - struct wlr_xdg_toplevel_v6_show_window_menu_event event = { - .surface = surface, - .seat = seat, - .serial = serial, - .x = x, - .y = y, - }; - - wlr_signal_emit_safe(&surface->toplevel->events.request_show_window_menu, - &event); -} - -static void xdg_toplevel_handle_move(struct wl_client *client, - struct wl_resource *resource, struct wl_resource *seat_resource, - uint32_t serial) { - struct wlr_xdg_surface_v6 *surface = - xdg_surface_from_xdg_toplevel_resource(resource); - struct wlr_seat_client *seat = - wlr_seat_client_from_resource(seat_resource); - - if (!surface->configured) { - wl_resource_post_error(surface->toplevel->resource, - ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED, - "surface has not been configured yet"); - return; - } - - if (!wlr_seat_validate_grab_serial(seat->seat, serial)) { - wlr_log(WLR_DEBUG, "invalid serial for grab"); - return; - } - - struct wlr_xdg_toplevel_v6_move_event event = { - .surface = surface, - .seat = seat, - .serial = serial, - }; - - wlr_signal_emit_safe(&surface->toplevel->events.request_move, &event); -} - -static void xdg_toplevel_handle_resize(struct wl_client *client, - struct wl_resource *resource, struct wl_resource *seat_resource, - uint32_t serial, uint32_t edges) { - struct wlr_xdg_surface_v6 *surface = - xdg_surface_from_xdg_toplevel_resource(resource); - struct wlr_seat_client *seat = - wlr_seat_client_from_resource(seat_resource); - - if (!surface->configured) { - wl_resource_post_error(surface->toplevel->resource, - ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED, - "surface has not been configured yet"); - return; - } - - if (!wlr_seat_validate_grab_serial(seat->seat, serial)) { - wlr_log(WLR_DEBUG, "invalid serial for grab"); - return; - } - - struct wlr_xdg_toplevel_v6_resize_event event = { - .surface = surface, - .seat = seat, - .serial = serial, - .edges = edges, - }; - - wlr_signal_emit_safe(&surface->toplevel->events.request_resize, &event); -} - -static void xdg_toplevel_handle_set_max_size(struct wl_client *client, - struct wl_resource *resource, int32_t width, int32_t height) { - struct wlr_xdg_surface_v6 *surface = - xdg_surface_from_xdg_toplevel_resource(resource); - surface->toplevel->client_pending.max_width = width; - surface->toplevel->client_pending.max_height = height; -} - -static void xdg_toplevel_handle_set_min_size(struct wl_client *client, - struct wl_resource *resource, int32_t width, int32_t height) { - struct wlr_xdg_surface_v6 *surface = - xdg_surface_from_xdg_toplevel_resource(resource); - surface->toplevel->client_pending.min_width = width; - surface->toplevel->client_pending.min_height = height; -} - -static void xdg_toplevel_handle_set_maximized(struct wl_client *client, - struct wl_resource *resource) { - struct wlr_xdg_surface_v6 *surface = - xdg_surface_from_xdg_toplevel_resource(resource); - surface->toplevel->client_pending.maximized = true; - wlr_signal_emit_safe(&surface->toplevel->events.request_maximize, surface); -} - -static void xdg_toplevel_handle_unset_maximized(struct wl_client *client, - struct wl_resource *resource) { - struct wlr_xdg_surface_v6 *surface = - xdg_surface_from_xdg_toplevel_resource(resource); - surface->toplevel->client_pending.maximized = false; - wlr_signal_emit_safe(&surface->toplevel->events.request_maximize, surface); -} - -static void handle_fullscreen_output_destroy(struct wl_listener *listener, - void *data) { - struct wlr_xdg_toplevel_v6_state *state = - wl_container_of(listener, state, fullscreen_output_destroy); - state->fullscreen_output = NULL; - wl_list_remove(&state->fullscreen_output_destroy.link); -} - -static void store_fullscreen_pending(struct wlr_xdg_surface_v6 *surface, - bool fullscreen, struct wlr_output *output) { - struct wlr_xdg_toplevel_v6_state *state = - &surface->toplevel->client_pending; - state->fullscreen = fullscreen; - if (state->fullscreen_output) { - wl_list_remove(&state->fullscreen_output_destroy.link); - } - state->fullscreen_output = output; - if (state->fullscreen_output) { - state->fullscreen_output_destroy.notify = - handle_fullscreen_output_destroy; - wl_signal_add(&state->fullscreen_output->events.destroy, - &state->fullscreen_output_destroy); - } -} - -static void xdg_toplevel_handle_set_fullscreen(struct wl_client *client, - struct wl_resource *resource, struct wl_resource *output_resource) { - struct wlr_xdg_surface_v6 *surface = - xdg_surface_from_xdg_toplevel_resource(resource); - - struct wlr_output *output = NULL; - if (output_resource != NULL) { - output = wlr_output_from_resource(output_resource); - } - - store_fullscreen_pending(surface, true, output); - - struct wlr_xdg_toplevel_v6_set_fullscreen_event event = { - .surface = surface, - .fullscreen = true, - .output = output, - }; - - wlr_signal_emit_safe(&surface->toplevel->events.request_fullscreen, &event); -} - -static void xdg_toplevel_handle_unset_fullscreen(struct wl_client *client, - struct wl_resource *resource) { - struct wlr_xdg_surface_v6 *surface = - xdg_surface_from_xdg_toplevel_resource(resource); - - store_fullscreen_pending(surface, false, NULL); - - struct wlr_xdg_toplevel_v6_set_fullscreen_event event = { - .surface = surface, - .fullscreen = false, - .output = NULL, - }; - - wlr_signal_emit_safe(&surface->toplevel->events.request_fullscreen, &event); -} - -static void xdg_toplevel_handle_set_minimized(struct wl_client *client, - struct wl_resource *resource) { - struct wlr_xdg_surface_v6 *surface = - xdg_surface_from_xdg_toplevel_resource(resource); - wlr_signal_emit_safe(&surface->toplevel->events.request_minimize, surface); -} - -static const struct zxdg_toplevel_v6_interface - zxdg_toplevel_v6_implementation = { - .destroy = xdg_toplevel_handle_destroy, - .set_parent = xdg_toplevel_handle_set_parent, - .set_title = xdg_toplevel_handle_set_title, - .set_app_id = xdg_toplevel_handle_set_app_id, - .show_window_menu = xdg_toplevel_handle_show_window_menu, - .move = xdg_toplevel_handle_move, - .resize = xdg_toplevel_handle_resize, - .set_max_size = xdg_toplevel_handle_set_max_size, - .set_min_size = xdg_toplevel_handle_set_min_size, - .set_maximized = xdg_toplevel_handle_set_maximized, - .unset_maximized = xdg_toplevel_handle_unset_maximized, - .set_fullscreen = xdg_toplevel_handle_set_fullscreen, - .unset_fullscreen = xdg_toplevel_handle_unset_fullscreen, - .set_minimized = xdg_toplevel_handle_set_minimized, -}; - -void handle_xdg_toplevel_v6_ack_configure(struct wlr_xdg_surface_v6 *surface, - struct wlr_xdg_surface_v6_configure *configure) { - assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL); - assert(configure->toplevel_state != NULL); - - surface->toplevel->current.maximized = - configure->toplevel_state->maximized; - surface->toplevel->current.fullscreen = - configure->toplevel_state->fullscreen; - surface->toplevel->current.resizing = - configure->toplevel_state->resizing; - surface->toplevel->current.activated = - configure->toplevel_state->activated; -} - -bool compare_xdg_surface_v6_toplevel_state(struct wlr_xdg_toplevel_v6 *state) { - struct { - struct wlr_xdg_toplevel_v6_state state; - uint32_t width, height; - } configured; - - // is pending state different from current state? - if (!state->base->configured) { - return false; - } - - if (wl_list_empty(&state->base->configure_list)) { - // last configure is actually the current state, just use it - configured.state = state->current; - configured.width = state->base->surface->current.width; - configured.height = state->base->surface->current.height; - } else { - struct wlr_xdg_surface_v6_configure *configure = - wl_container_of(state->base->configure_list.prev, configure, link); - configured.state = *configure->toplevel_state; - configured.width = configure->toplevel_state->width; - configured.height = configure->toplevel_state->height; - } - - if (state->server_pending.activated != configured.state.activated) { - return false; - } - if (state->server_pending.fullscreen != configured.state.fullscreen) { - return false; - } - if (state->server_pending.maximized != configured.state.maximized) { - return false; - } - if (state->server_pending.resizing != configured.state.resizing) { - return false; - } - - if (state->server_pending.width == configured.width && - state->server_pending.height == configured.height) { - return true; - } - - if (state->server_pending.width == 0 && state->server_pending.height == 0) { - return true; - } - - return false; -} - -void send_xdg_toplevel_v6_configure(struct wlr_xdg_surface_v6 *surface, - struct wlr_xdg_surface_v6_configure *configure) { - assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL); - - configure->toplevel_state = malloc(sizeof(*configure->toplevel_state)); - if (configure->toplevel_state == NULL) { - wlr_log(WLR_ERROR, "Allocation failed"); - wl_resource_post_no_memory(surface->toplevel->resource); - return; - } - *configure->toplevel_state = surface->toplevel->server_pending; - - uint32_t *s; - struct wl_array states; - wl_array_init(&states); - if (surface->toplevel->server_pending.maximized) { - s = wl_array_add(&states, sizeof(uint32_t)); - if (!s) { - wlr_log(WLR_ERROR, - "Could not allocate state for maximized xdg_toplevel"); - goto error_out; - } - *s = ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED; - } - if (surface->toplevel->server_pending.fullscreen) { - s = wl_array_add(&states, sizeof(uint32_t)); - if (!s) { - wlr_log(WLR_ERROR, - "Could not allocate state for fullscreen xdg_toplevel"); - goto error_out; - } - *s = ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN; - } - if (surface->toplevel->server_pending.resizing) { - s = wl_array_add(&states, sizeof(uint32_t)); - if (!s) { - wlr_log(WLR_ERROR, - "Could not allocate state for resizing xdg_toplevel"); - goto error_out; - } - *s = ZXDG_TOPLEVEL_V6_STATE_RESIZING; - } - if (surface->toplevel->server_pending.activated) { - s = wl_array_add(&states, sizeof(uint32_t)); - if (!s) { - wlr_log(WLR_ERROR, - "Could not allocate state for activated xdg_toplevel"); - goto error_out; - } - *s = ZXDG_TOPLEVEL_V6_STATE_ACTIVATED; - } - - uint32_t width = surface->toplevel->server_pending.width; - uint32_t height = surface->toplevel->server_pending.height; - zxdg_toplevel_v6_send_configure(surface->toplevel->resource, width, - height, &states); - - wl_array_release(&states); - return; - -error_out: - wl_array_release(&states); - wl_resource_post_no_memory(surface->toplevel->resource); -} - -void handle_xdg_surface_v6_toplevel_committed(struct wlr_xdg_surface_v6 *surface) { - assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL); - - if (!surface->toplevel->added) { - // on the first commit, send a configure request to tell the client it - // is added - schedule_xdg_surface_v6_configure(surface); - surface->toplevel->added = true; - return; - } - - // update state that doesn't need compositor approval - surface->toplevel->current.max_width = - surface->toplevel->client_pending.max_width; - surface->toplevel->current.min_width = - surface->toplevel->client_pending.min_width; - surface->toplevel->current.max_height = - surface->toplevel->client_pending.max_height; - surface->toplevel->current.min_height = - surface->toplevel->client_pending.min_height; -} - -static void xdg_toplevel_handle_resource_destroy(struct wl_resource *resource) { - struct wlr_xdg_surface_v6 *surface = - xdg_surface_from_xdg_toplevel_resource(resource); - if (surface != NULL) { - destroy_xdg_toplevel_v6(surface); - } -} - -const struct wlr_surface_role xdg_toplevel_v6_surface_role = { - .name = "xdg_toplevel_v6", - .commit = handle_xdg_surface_v6_commit, - .precommit = handle_xdg_surface_v6_precommit, -}; - -void create_xdg_toplevel_v6(struct wlr_xdg_surface_v6 *xdg_surface, - uint32_t id) { - if (!wlr_surface_set_role(xdg_surface->surface, &xdg_toplevel_v6_surface_role, - xdg_surface, xdg_surface->resource, ZXDG_SHELL_V6_ERROR_ROLE)) { - return; - } - - xdg_surface->toplevel = calloc(1, sizeof(struct wlr_xdg_toplevel_v6)); - if (xdg_surface->toplevel == NULL) { - wl_resource_post_no_memory(xdg_surface->resource); - return; - } - wl_signal_init(&xdg_surface->toplevel->events.request_maximize); - wl_signal_init(&xdg_surface->toplevel->events.request_fullscreen); - wl_signal_init(&xdg_surface->toplevel->events.request_minimize); - wl_signal_init(&xdg_surface->toplevel->events.request_move); - wl_signal_init(&xdg_surface->toplevel->events.request_resize); - wl_signal_init(&xdg_surface->toplevel->events.request_show_window_menu); - wl_signal_init(&xdg_surface->toplevel->events.set_parent); - wl_signal_init(&xdg_surface->toplevel->events.set_title); - wl_signal_init(&xdg_surface->toplevel->events.set_app_id); - - xdg_surface->role = WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL; - xdg_surface->toplevel->base = xdg_surface; - - struct wl_resource *toplevel_resource = wl_resource_create( - xdg_surface->client->client, &zxdg_toplevel_v6_interface, - wl_resource_get_version(xdg_surface->resource), id); - if (toplevel_resource == NULL) { - free(xdg_surface->toplevel); - wl_resource_post_no_memory(xdg_surface->resource); - return; - } - xdg_surface->toplevel->resource = toplevel_resource; - wl_resource_set_implementation(toplevel_resource, - &zxdg_toplevel_v6_implementation, xdg_surface, - xdg_toplevel_handle_resource_destroy); -} - -uint32_t wlr_xdg_toplevel_v6_set_size(struct wlr_xdg_surface_v6 *surface, - uint32_t width, uint32_t height) { - assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL); - surface->toplevel->server_pending.width = width; - surface->toplevel->server_pending.height = height; - - return schedule_xdg_surface_v6_configure(surface); -} - -uint32_t wlr_xdg_toplevel_v6_set_activated(struct wlr_xdg_surface_v6 *surface, - bool activated) { - assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL); - surface->toplevel->server_pending.activated = activated; - - return schedule_xdg_surface_v6_configure(surface); -} - -uint32_t wlr_xdg_toplevel_v6_set_maximized(struct wlr_xdg_surface_v6 *surface, - bool maximized) { - assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL); - surface->toplevel->server_pending.maximized = maximized; - - return schedule_xdg_surface_v6_configure(surface); -} - -uint32_t wlr_xdg_toplevel_v6_set_fullscreen(struct wlr_xdg_surface_v6 *surface, - bool fullscreen) { - assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL); - surface->toplevel->server_pending.fullscreen = fullscreen; - - return schedule_xdg_surface_v6_configure(surface); -} - -uint32_t wlr_xdg_toplevel_v6_set_resizing(struct wlr_xdg_surface_v6 *surface, - bool resizing) { - assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL); - surface->toplevel->server_pending.resizing = resizing; - - return schedule_xdg_surface_v6_configure(surface); -} diff --git a/xcursor/wlr_xcursor.c b/xcursor/wlr_xcursor.c index 4e1d02ecf..85e76a556 100644 --- a/xcursor/wlr_xcursor.c +++ b/xcursor/wlr_xcursor.c @@ -24,7 +24,7 @@ */ #define _POSIX_C_SOURCE 200809L -#include +#include #include #include #include @@ -245,7 +245,7 @@ struct wlr_xcursor_theme *wlr_xcursor_theme_load(const char *name, int size) { for (size_t i = 0; i < theme->cursor_count; ++i) { struct wlr_xcursor *c = theme->cursors[i]; struct wlr_xcursor_image *i = c->images[0]; - wlr_log(WLR_DEBUG, "%s (%u images) %dx%d+%d,%d", + wlr_log(WLR_DEBUG, "%s (%u images) %" PRIu32 "x%" PRIu32 "+%" PRIu32 ",%" PRIu32, c->name, c->image_count, i->width, i->height, i->hotspot_x, i->hotspot_y); } diff --git a/xcursor/xcursor.c b/xcursor/xcursor.c index aabef4837..51ce83237 100644 --- a/xcursor/xcursor.c +++ b/xcursor/xcursor.c @@ -285,11 +285,12 @@ _XcursorReadUInt (XcursorFile *file, XcursorUInt *u) return XcursorFalse; if ((*file->read) (file, bytes, 4) != 4) - return XcursorFalse; - *u = ((bytes[0] << 0) | - (bytes[1] << 8) | - (bytes[2] << 16) | - (bytes[3] << 24)); + return XcursorFalse; + + *u = ((XcursorUInt)(bytes[0]) << 0) | + ((XcursorUInt)(bytes[1]) << 8) | + ((XcursorUInt)(bytes[2]) << 16) | + ((XcursorUInt)(bytes[3]) << 24); return XcursorTrue; } diff --git a/xwayland/selection/dnd.c b/xwayland/selection/dnd.c index ec5f16c7f..16941558a 100644 --- a/xwayland/selection/dnd.c +++ b/xwayland/selection/dnd.c @@ -192,7 +192,7 @@ int xwm_handle_selection_client_message(struct wlr_xwm *xwm, drag->source->accepted = accepted; wlr_data_source_dnd_action(drag->source, action); - wlr_log(WLR_DEBUG, "DND_STATUS window=%d accepted=%d action=%d", + wlr_log(WLR_DEBUG, "DND_STATUS window=%" PRIu32 " accepted=%d action=%d", target_window, accepted, action); return 1; } else if (ev->type == xwm->atoms[DND_FINISHED]) { @@ -226,7 +226,7 @@ int xwm_handle_selection_client_message(struct wlr_xwm *xwm, wlr_data_source_dnd_finish(source); } - wlr_log(WLR_DEBUG, "DND_FINISH window=%d performed=%d action=%d", + wlr_log(WLR_DEBUG, "DND_FINISH window=%" PRIu32 " performed=%d action=%d", target_window, performed, action); return 1; } else { diff --git a/xwayland/selection/outgoing.c b/xwayland/selection/outgoing.c index 169f516e7..a5ce2fd26 100644 --- a/xwayland/selection/outgoing.c +++ b/xwayland/selection/outgoing.c @@ -23,9 +23,9 @@ static void xwm_selection_send_notify(struct wlr_xwm *xwm, .property = success ? req->property : XCB_ATOM_NONE, }; - wlr_log(WLR_DEBUG, "SendEvent destination=%d SelectionNotify(31) time=%d " - "requestor=%d selection=%d target=%d property=%d", req->requestor, - req->time, req->requestor, req->selection, req->target, + wlr_log(WLR_DEBUG, "SendEvent destination=%" PRIu32 " SelectionNotify(31) time=%" PRIu32 + " requestor=%" PRIu32 " selection=%" PRIu32 " target=%" PRIu32 " property=%" PRIu32, + req->requestor, req->time, req->requestor, req->selection, req->target, selection_notify.property); xcb_send_event(xwm->xcb_conn, 0, // propagate @@ -55,16 +55,29 @@ static int xwm_selection_flush_source_data( static void xwm_selection_transfer_start_outgoing( struct wlr_xwm_selection_transfer *transfer); +static struct wlr_xwm_selection_transfer *xwm_selection_transfer_get_first( + struct wlr_xwm_selection *selection) { + struct wlr_xwm_selection_transfer *first = NULL; + if (!wl_list_empty(&selection->outgoing)) { + first = wl_container_of(selection->outgoing.prev, first, + outgoing_link); + } + + return first; +} + static void xwm_selection_transfer_destroy_outgoing( struct wlr_xwm_selection_transfer *transfer) { + struct wlr_xwm_selection *selection = transfer->selection; + bool was_first = transfer == xwm_selection_transfer_get_first(selection); wl_list_remove(&transfer->outgoing_link); + wlr_log(WLR_DEBUG, "Destroying transfer %p", transfer); - // Start next queued transfer - struct wlr_xwm_selection_transfer *first = NULL; - if (!wl_list_empty(&transfer->selection->outgoing)) { - first = wl_container_of(transfer->selection->outgoing.prev, first, - outgoing_link); - xwm_selection_transfer_start_outgoing(first); + // Start next queued transfer if we just removed the active one. + if (was_first && !wl_list_empty(&selection->outgoing)) { + wlr_log(WLR_DEBUG, "Destroyed transfer was active, starting next"); + xwm_selection_transfer_start_outgoing( + xwm_selection_transfer_get_first(selection)); } xwm_selection_transfer_remove_source(transfer); @@ -92,7 +105,7 @@ static int xwm_data_source_read(int fd, uint32_t mask, void *data) { size_t available = transfer->source_data.alloc - current; ssize_t len = read(fd, p, available); if (len == -1) { - wlr_log(WLR_ERROR, "read error from data source: %m"); + wlr_log_errno(WLR_ERROR, "read error from data source"); goto error_out; } @@ -219,6 +232,8 @@ static void xwm_selection_transfer_start_outgoing( struct wlr_xwm *xwm = transfer->selection->xwm; struct wl_event_loop *loop = wl_display_get_event_loop(xwm->xwayland->wl_display); + wlr_log(WLR_DEBUG, "Starting transfer %p", transfer); + assert(transfer == xwm_selection_transfer_get_first(transfer->selection)); transfer->source = wl_event_loop_add_fd(loop, transfer->source_fd, WL_EVENT_READABLE, xwm_data_source_read, transfer); } @@ -250,15 +265,14 @@ static struct wl_array *xwm_selection_source_get_mime_types( /** * Read the Wayland selection and send it to an Xwayland client. */ -static void xwm_selection_send_data(struct wlr_xwm_selection *selection, +static bool xwm_selection_send_data(struct wlr_xwm_selection *selection, xcb_selection_request_event_t *req, const char *mime_type) { // Check MIME type struct wl_array *mime_types = xwm_selection_source_get_mime_types(selection); if (mime_types == NULL) { wlr_log(WLR_ERROR, "not sending selection: no MIME type list available"); - xwm_selection_send_notify(selection->xwm, req, false); - return; + return false; } bool found = false; @@ -273,26 +287,26 @@ static void xwm_selection_send_data(struct wlr_xwm_selection *selection, if (!found) { wlr_log(WLR_ERROR, "not sending selection: " "requested an unsupported MIME type %s", mime_type); - xwm_selection_send_notify(selection->xwm, req, false); - return; + return false; } struct wlr_xwm_selection_transfer *transfer = calloc(1, sizeof(struct wlr_xwm_selection_transfer)); if (transfer == NULL) { wlr_log(WLR_ERROR, "Allocation failed"); - return; + return false; } + transfer->selection = selection; transfer->request = *req; wl_array_init(&transfer->source_data); int p[2]; if (pipe(p) == -1) { - wlr_log(WLR_ERROR, "pipe() failed: %m"); - xwm_selection_send_notify(selection->xwm, req, false); - return; + wlr_log_errno(WLR_ERROR, "pipe() failed"); + return false; } + fcntl(p[0], F_SETFD, FD_CLOEXEC); fcntl(p[0], F_SETFL, O_NONBLOCK); fcntl(p[1], F_SETFD, FD_CLOEXEC); @@ -301,15 +315,37 @@ static void xwm_selection_send_data(struct wlr_xwm_selection *selection, transfer->source_fd = p[0]; wlr_log(WLR_DEBUG, "Sending Wayland selection %u to Xwayland window with " - "MIME type %s, target %u", req->target, mime_type, req->target); + "MIME type %s, target %u, transfer %p", req->target, mime_type, + req->target, transfer); xwm_selection_source_send(selection, mime_type, p[1]); + // It seems that if we ever try to reply to a selection request after + // another has been sent by the same requestor, the requestor never reads + // from it. It appears to only ever read from the latest, so purge stale + // transfers to prevent clipboard hangs. + struct wlr_xwm_selection_transfer *outgoing, *tmp; + wl_list_for_each_safe(outgoing, tmp, &selection->outgoing, outgoing_link) { + if (transfer->request.requestor == outgoing->request.requestor) { + wlr_log(WLR_DEBUG, "Destroying stale transfer %p", outgoing); + xwm_selection_send_notify(selection->xwm, &outgoing->request, false); + xwm_selection_transfer_destroy_outgoing(outgoing); + } + } + wl_list_insert(&selection->outgoing, &transfer->outgoing_link); // We can only handle one transfer at a time if (wl_list_length(&selection->outgoing) == 1) { + wlr_log(WLR_DEBUG, "No transfer active, starting %p now", transfer); xwm_selection_transfer_start_outgoing(transfer); + } else { + struct wlr_xwm_selection_transfer *outgoing; + wl_list_for_each(outgoing, &selection->outgoing, outgoing_link) { + wlr_log(WLR_DEBUG, "Transfer %p still queued", outgoing); + } } + + return true; } static void xwm_selection_send_targets(struct wlr_xwm_selection *selection, @@ -381,12 +417,12 @@ void xwm_handle_selection_request(struct wlr_xwm *xwm, xwm_get_selection(xwm, req->selection); if (selection == NULL) { wlr_log(WLR_DEBUG, "received selection request for unknown selection"); - return; + goto fail_notify_requestor; } if (selection->window != req->owner) { wlr_log(WLR_DEBUG, "received selection request with invalid owner"); - return; + goto fail_notify_requestor; } // No xwayland surface focused, deny access to clipboard @@ -395,8 +431,7 @@ void xwm_handle_selection_request(struct wlr_xwm *xwm, wlr_log(WLR_DEBUG, "denying read access to selection %u (%s): " "no xwayland surface focused", selection->atom, selection_name); free(selection_name); - xwm_selection_send_notify(xwm, req, false); - return; + goto fail_notify_requestor; } if (req->target == xwm->atoms[TARGETS]) { @@ -411,12 +446,22 @@ void xwm_handle_selection_request(struct wlr_xwm *xwm, if (mime_type == NULL) { wlr_log(WLR_ERROR, "ignoring selection request: unknown atom %u", req->target); - xwm_selection_send_notify(xwm, req, false); - return; + goto fail_notify_requestor; } - xwm_selection_send_data(selection, req, mime_type); + + bool send_success = xwm_selection_send_data(selection, req, mime_type); free(mime_type); + if (!send_success) { + goto fail_notify_requestor; + } } + + return; + +fail_notify_requestor: + // Something went wrong, and there won't be any data being sent to the + // requestor, so let them know. + xwm_selection_send_notify(xwm, req, false); } void xwm_handle_selection_destroy_notify(struct wlr_xwm *xwm, diff --git a/xwayland/server.c b/xwayland/server.c index b257f2451..d0057c66a 100644 --- a/xwayland/server.c +++ b/xwayland/server.c @@ -107,8 +107,16 @@ noreturn static void exec_xwayland(struct wlr_xwayland_server *server) { dup2(devnull, STDERR_FILENO); } + const char *xwayland_path = getenv("WLR_XWAYLAND"); + if (xwayland_path) { + wlr_log(WLR_INFO, "Using Xwayland binary '%s' due to WLR_XWAYLAND", + xwayland_path); + } else { + xwayland_path = "Xwayland"; + } + // This returns if and only if the call fails - execvp("Xwayland", argv); + execvp(xwayland_path, argv); wlr_log_errno(WLR_ERROR, "failed to exec Xwayland"); close(devnull); @@ -150,7 +158,13 @@ static void server_finish_process(struct wlr_xwayland_server *server) { } static void server_finish_display(struct wlr_xwayland_server *server) { - if (!server || server->display == -1) { + if (!server) { + return; + } + + wl_list_remove(&server->display_destroy.link); + + if (server->display == -1) { return; } @@ -158,8 +172,6 @@ static void server_finish_display(struct wlr_xwayland_server *server) { safe_close(server->x_fd[1]); server->x_fd[0] = server->x_fd[1] = -1; - wl_list_remove(&server->display_destroy.link); - unlink_display_sockets(server->display); server->display = -1; server->display_name[0] = '\0'; diff --git a/xwayland/xwayland.c b/xwayland/xwayland.c index ccec7c670..4cb103af0 100644 --- a/xwayland/xwayland.c +++ b/xwayland/xwayland.c @@ -89,7 +89,7 @@ struct wlr_xwayland *wlr_xwayland_create(struct wl_display *wl_display, }; xwayland->server = wlr_xwayland_server_create(wl_display, &options); if (xwayland->server == NULL) { - free(xwayland->server); + free(xwayland); return NULL; } diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 28e329f39..fefba7fcc 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -38,11 +38,14 @@ const char *atom_map[ATOM_LAST] = { [NET_ACTIVE_WINDOW] = "_NET_ACTIVE_WINDOW", [NET_WM_MOVERESIZE] = "_NET_WM_MOVERESIZE", [NET_SUPPORTING_WM_CHECK] = "_NET_SUPPORTING_WM_CHECK", + [NET_WM_STATE_FOCUSED] = "_NET_WM_STATE_FOCUSED", [NET_WM_STATE_MODAL] = "_NET_WM_STATE_MODAL", [NET_WM_STATE_FULLSCREEN] = "_NET_WM_STATE_FULLSCREEN", [NET_WM_STATE_MAXIMIZED_VERT] = "_NET_WM_STATE_MAXIMIZED_VERT", [NET_WM_STATE_MAXIMIZED_HORZ] = "_NET_WM_STATE_MAXIMIZED_HORZ", + [NET_WM_STATE_HIDDEN] = "_NET_WM_STATE_HIDDEN", [NET_WM_PING] = "_NET_WM_PING", + [WM_CHANGE_STATE] = "WM_CHANGE_STATE", [WM_STATE] = "WM_STATE", [CLIPBOARD] = "CLIPBOARD", [PRIMARY] = "PRIMARY", @@ -139,13 +142,13 @@ static struct wlr_xwayland_surface *xwayland_surface_create( surface->width = width; surface->height = height; surface->override_redirect = override_redirect; - wl_list_insert(&xwm->surfaces, &surface->link); wl_list_init(&surface->children); wl_list_init(&surface->parent_link); wl_signal_init(&surface->events.destroy); wl_signal_init(&surface->events.request_configure); wl_signal_init(&surface->events.request_move); wl_signal_init(&surface->events.request_resize); + wl_signal_init(&surface->events.request_minimize); wl_signal_init(&surface->events.request_maximize); wl_signal_init(&surface->events.request_fullscreen); wl_signal_init(&surface->events.request_activate); @@ -161,6 +164,7 @@ static struct wlr_xwayland_surface *xwayland_surface_create( wl_signal_init(&surface->events.set_decorations); wl_signal_init(&surface->events.set_override_redirect); wl_signal_init(&surface->events.ping_timeout); + wl_signal_init(&surface->events.set_geometry); xcb_get_geometry_reply_t *geometry_reply = xcb_get_geometry_reply(xwm->xcb_conn, geometry_cookie, NULL); @@ -179,6 +183,8 @@ static struct wlr_xwayland_surface *xwayland_surface_create( return NULL; } + wl_list_insert(&xwm->surfaces, &surface->link); + wlr_signal_emit_safe(&xwm->xwayland->events.new_surface, surface); return surface; @@ -234,8 +240,20 @@ static void xwm_set_net_client_list(struct wlr_xwm *xwm) { XCB_ATOM_WINDOW, 32, mapped_surfaces, windows); } -static void xwm_send_focus_window(struct wlr_xwm *xwm, +static void xsurface_set_net_wm_state(struct wlr_xwayland_surface *xsurface); + +static void xwm_set_focus_window(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface) { + struct wlr_xwayland_surface *unfocus_surface = xwm->focus_surface; + + // We handle cases where focus_surface == xsurface because we + // want to be able to deny FocusIn events. + xwm->focus_surface = xsurface; + + if (unfocus_surface) { + xsurface_set_net_wm_state(unfocus_surface); + } + if (!xsurface) { xcb_set_input_focus_checked(xwm->xcb_conn, XCB_INPUT_FOCUS_POINTER_ROOT, @@ -258,14 +276,17 @@ static void xwm_send_focus_window(struct wlr_xwm *xwm, } else { xwm_send_wm_message(xsurface, &message_data, XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT); - xcb_set_input_focus(xwm->xcb_conn, XCB_INPUT_FOCUS_POINTER_ROOT, - xsurface->window_id, XCB_CURRENT_TIME); + xcb_void_cookie_t cookie = xcb_set_input_focus(xwm->xcb_conn, + XCB_INPUT_FOCUS_POINTER_ROOT, xsurface->window_id, XCB_CURRENT_TIME); + xwm->last_focus_seq = cookie.sequence; } uint32_t values[1]; values[0] = XCB_STACK_MODE_ABOVE; xcb_configure_window(xwm->xcb_conn, xsurface->window_id, XCB_CONFIG_WINDOW_STACK_MODE, values); + + xsurface_set_net_wm_state(xsurface); } static void xwm_surface_activate(struct wlr_xwm *xwm, @@ -281,19 +302,16 @@ static void xwm_surface_activate(struct wlr_xwm *xwm, xwm_set_net_active_window(xwm, XCB_WINDOW_NONE); } - xwm_send_focus_window(xwm, xsurface); - - xwm->focus_surface = xsurface; + xwm_set_focus_window(xwm, xsurface); xcb_flush(xwm->xcb_conn); } static void xsurface_set_net_wm_state(struct wlr_xwayland_surface *xsurface) { struct wlr_xwm *xwm = xsurface->xwm; - uint32_t property[4]; - int i; - i = 0; + uint32_t property[6]; + size_t i = 0; if (xsurface->modal) { property[i++] = xwm->atoms[NET_WM_STATE_MODAL]; } @@ -306,6 +324,13 @@ static void xsurface_set_net_wm_state(struct wlr_xwayland_surface *xsurface) { if (xsurface->maximized_horz) { property[i++] = xwm->atoms[NET_WM_STATE_MAXIMIZED_HORZ]; } + if (xsurface->minimized) { + property[i++] = xwm->atoms[NET_WM_STATE_HIDDEN]; + } + if (xsurface == xwm->focus_surface) { + property[i++] = xwm->atoms[NET_WM_STATE_FOCUSED]; + } + assert(i <= sizeof(property) / sizeof(property[0])); xcb_change_property(xwm->xcb_conn, XCB_PROP_MODE_REPLACE, @@ -386,8 +411,9 @@ static void read_surface_class(struct wlr_xwm *xwm, surface->class = NULL; } - wlr_log(WLR_DEBUG, "XCB_ATOM_WM_CLASS: %s %s", surface->instance, - surface->class); + wlr_log(WLR_DEBUG, "XCB_ATOM_WM_CLASS: %s %s", + surface->instance ? surface->instance : "(null)", + surface->class ? surface->class : "(null)"); wlr_signal_emit_safe(&surface->events.set_class, surface); } @@ -409,7 +435,8 @@ static void read_surface_role(struct wlr_xwm *xwm, xsurface->role = NULL; } - wlr_log(WLR_DEBUG, "XCB_ATOM_WM_WINDOW_ROLE: %s", xsurface->role); + wlr_log(WLR_DEBUG, "XCB_ATOM_WM_WINDOW_ROLE: %s", + xsurface->role ? xsurface->role : "(null)"); wlr_signal_emit_safe(&xsurface->events.set_role, xsurface); } @@ -437,24 +464,45 @@ static void read_surface_title(struct wlr_xwm *xwm, } xsurface->has_utf8_title = is_utf8; - wlr_log(WLR_DEBUG, "XCB_ATOM_WM_NAME: %s", xsurface->title); + wlr_log(WLR_DEBUG, "XCB_ATOM_WM_NAME: %s", xsurface->title ? xsurface->title : "(null)"); wlr_signal_emit_safe(&xsurface->events.set_title, xsurface); } +static bool has_parent(struct wlr_xwayland_surface *parent, + struct wlr_xwayland_surface *child) { + while (parent) { + if (child == parent) { + return true; + } + + parent = parent->parent; + } + + return false; +} + static void read_surface_parent(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface, xcb_get_property_reply_t *reply) { + struct wlr_xwayland_surface *found_parent = NULL; if (reply->type != XCB_ATOM_WINDOW) { return; } xcb_window_t *xid = xcb_get_property_value(reply); if (xid != NULL) { - xsurface->parent = lookup_surface(xwm, *xid); + found_parent = lookup_surface(xwm, *xid); + if (!has_parent(found_parent, xsurface)) { + xsurface->parent = found_parent; + } else { + wlr_log(WLR_INFO, "%p with %p would create a loop", xsurface, + found_parent); + } } else { xsurface->parent = NULL; } + wl_list_remove(&xsurface->parent_link); if (xsurface->parent != NULL) { wl_list_insert(&xsurface->parent->children, &xsurface->parent_link); @@ -551,7 +599,7 @@ static void read_surface_hints(struct wlr_xwm *xwm, xsurface->hints->input = true; } - wlr_log(WLR_DEBUG, "WM_HINTS (%d)", reply->value_len); + wlr_log(WLR_DEBUG, "WM_HINTS (%" PRIu32 ")", reply->value_len); wlr_signal_emit_safe(&xsurface->events.set_hints, xsurface); } #else @@ -603,7 +651,7 @@ static void read_surface_normal_hints(struct wlr_xwm *xwm, xsurface->size_hints->max_height = -1; } - wlr_log(WLR_DEBUG, "WM_NORMAL_HINTS (%d)", reply->value_len); + wlr_log(WLR_DEBUG, "WM_NORMAL_HINTS (%" PRIu32 ")", reply->value_len); } #else static void read_surface_normal_hints(struct wlr_xwm *xwm, @@ -647,7 +695,7 @@ static void read_surface_motif_hints(struct wlr_xwm *xwm, wlr_signal_emit_safe(&xsurface->events.set_decorations, xsurface); } - wlr_log(WLR_DEBUG, "MOTIF_WM_HINTS (%d)", reply->value_len); + wlr_log(WLR_DEBUG, "MOTIF_WM_HINTS (%" PRIu32 ")", reply->value_len); } static void read_surface_net_wm_state(struct wlr_xwm *xwm, @@ -664,6 +712,8 @@ static void read_surface_net_wm_state(struct wlr_xwm *xwm, xsurface->maximized_vert = true; } else if (atom[i] == xwm->atoms[NET_WM_STATE_MAXIMIZED_HORZ]) { xsurface->maximized_horz = true; + } else if (atom[i] == xwm->atoms[NET_WM_STATE_HIDDEN]) { + xsurface->minimized = true; } } } @@ -718,8 +768,8 @@ static void read_surface_property(struct wlr_xwm *xwm, read_surface_role(xwm, xsurface, reply); } else { char *prop_name = xwm_get_atom_name(xwm, property); - wlr_log(WLR_DEBUG, "unhandled X11 property %u (%s) for window %u", - property, prop_name, xsurface->window_id); + wlr_log(WLR_DEBUG, "unhandled X11 property %" PRIu32 " (%s) for window %" PRIu32, + property, prop_name ? prop_name : "(null)", xsurface->window_id); free(prop_name); } @@ -886,15 +936,25 @@ static void xwm_handle_configure_notify(struct wlr_xwm *xwm, return; } - xsurface->x = ev->x; - xsurface->y = ev->y; - xsurface->width = ev->width; - xsurface->height = ev->height; + bool geometry_changed = + (xsurface->x != ev->x || xsurface->y != ev->y || + xsurface->width != ev->width || xsurface->height != ev->height); + + if (geometry_changed) { + xsurface->x = ev->x; + xsurface->y = ev->y; + xsurface->width = ev->width; + xsurface->height = ev->height; + } if (xsurface->override_redirect != ev->override_redirect) { xsurface->override_redirect = ev->override_redirect; wlr_signal_emit_safe(&xsurface->events.set_override_redirect, xsurface); } + + if (geometry_changed) { + wlr_signal_emit_safe(&xsurface->events.set_geometry, NULL); + } } #define ICCCM_WITHDRAWN_STATE 0 @@ -904,10 +964,7 @@ static void xwm_handle_configure_notify(struct wlr_xwm *xwm, static void xsurface_set_wm_state(struct wlr_xwayland_surface *xsurface, int32_t state) { struct wlr_xwm *xwm = xsurface->xwm; - uint32_t property[2]; - - property[0] = state; - property[1] = XCB_WINDOW_NONE; + uint32_t property[] = { state, XCB_WINDOW_NONE }; xcb_change_property(xwm->xcb_conn, XCB_PROP_MODE_REPLACE, @@ -915,7 +972,7 @@ static void xsurface_set_wm_state(struct wlr_xwayland_surface *xsurface, xwm->atoms[WM_STATE], xwm->atoms[WM_STATE], 32, // format - 2, property); + sizeof(property) / sizeof(property[0]), property); } static void xwm_handle_map_request(struct wlr_xwm *xwm, @@ -997,49 +1054,49 @@ static void xwm_handle_surface_id_message(struct wlr_xwm *xwm, } } -#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0 -#define _NET_WM_MOVERESIZE_SIZE_TOP 1 -#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2 -#define _NET_WM_MOVERESIZE_SIZE_RIGHT 3 -#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4 -#define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5 -#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6 -#define _NET_WM_MOVERESIZE_SIZE_LEFT 7 -#define _NET_WM_MOVERESIZE_MOVE 8 // movement only -#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD 9 // size via keyboard -#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 // move via keyboard -#define _NET_WM_MOVERESIZE_CANCEL 11 // cancel operation +#define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0 +#define _NET_WM_MOVERESIZE_SIZE_TOP 1 +#define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2 +#define _NET_WM_MOVERESIZE_SIZE_RIGHT 3 +#define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4 +#define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5 +#define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6 +#define _NET_WM_MOVERESIZE_SIZE_LEFT 7 +#define _NET_WM_MOVERESIZE_MOVE 8 // movement only +#define _NET_WM_MOVERESIZE_SIZE_KEYBOARD 9 // size via keyboard +#define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 // move via keyboard +#define _NET_WM_MOVERESIZE_CANCEL 11 // cancel operation static enum wlr_edges net_wm_edges_to_wlr(uint32_t net_wm_edges) { enum wlr_edges edges = WLR_EDGE_NONE; switch(net_wm_edges) { - case _NET_WM_MOVERESIZE_SIZE_TOPLEFT: - edges = WLR_EDGE_TOP | WLR_EDGE_LEFT; - break; - case _NET_WM_MOVERESIZE_SIZE_TOP: - edges = WLR_EDGE_TOP; - break; - case _NET_WM_MOVERESIZE_SIZE_TOPRIGHT: - edges = WLR_EDGE_TOP | WLR_EDGE_RIGHT; - break; - case _NET_WM_MOVERESIZE_SIZE_RIGHT: - edges = WLR_EDGE_RIGHT; - break; - case _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT: - edges = WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT; - break; - case _NET_WM_MOVERESIZE_SIZE_BOTTOM: - edges = WLR_EDGE_BOTTOM; - break; - case _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT: - edges = WLR_EDGE_BOTTOM | WLR_EDGE_LEFT; - break; - case _NET_WM_MOVERESIZE_SIZE_LEFT: - edges = WLR_EDGE_LEFT; - break; - default: - break; + case _NET_WM_MOVERESIZE_SIZE_TOPLEFT: + edges = WLR_EDGE_TOP | WLR_EDGE_LEFT; + break; + case _NET_WM_MOVERESIZE_SIZE_TOP: + edges = WLR_EDGE_TOP; + break; + case _NET_WM_MOVERESIZE_SIZE_TOPRIGHT: + edges = WLR_EDGE_TOP | WLR_EDGE_RIGHT; + break; + case _NET_WM_MOVERESIZE_SIZE_RIGHT: + edges = WLR_EDGE_RIGHT; + break; + case _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT: + edges = WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT; + break; + case _NET_WM_MOVERESIZE_SIZE_BOTTOM: + edges = WLR_EDGE_BOTTOM; + break; + case _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT: + edges = WLR_EDGE_BOTTOM | WLR_EDGE_LEFT; + break; + case _NET_WM_MOVERESIZE_SIZE_LEFT: + edges = WLR_EDGE_LEFT; + break; + default: + break; } return edges; @@ -1081,25 +1138,25 @@ static void xwm_handle_net_wm_moveresize_message(struct wlr_xwm *xwm, } } -#define _NET_WM_STATE_REMOVE 0 -#define _NET_WM_STATE_ADD 1 -#define _NET_WM_STATE_TOGGLE 2 +#define _NET_WM_STATE_REMOVE 0 +#define _NET_WM_STATE_ADD 1 +#define _NET_WM_STATE_TOGGLE 2 static bool update_state(int action, bool *state) { int new_state, changed; switch (action) { - case _NET_WM_STATE_REMOVE: - new_state = false; - break; - case _NET_WM_STATE_ADD: - new_state = true; - break; - case _NET_WM_STATE_TOGGLE: - new_state = !*state; - break; - default: - return false; + case _NET_WM_STATE_REMOVE: + new_state = false; + break; + case _NET_WM_STATE_ADD: + new_state = true; + break; + case _NET_WM_STATE_TOGGLE: + new_state = !*state; + break; + default: + return false; } changed = (*state != new_state); @@ -1126,10 +1183,11 @@ static void xwm_handle_net_wm_state_message(struct wlr_xwm *xwm, bool fullscreen = xsurface->fullscreen; bool maximized = xsurface_is_maximized(xsurface); + bool minimized = xsurface->minimized; uint32_t action = client_message->data.data32[0]; for (size_t i = 0; i < 2; ++i) { - uint32_t property = client_message->data.data32[1 + i]; + xcb_atom_t property = client_message->data.data32[1 + i]; if (property == xwm->atoms[NET_WM_STATE_MODAL] && update_state(action, &xsurface->modal)) { @@ -1143,6 +1201,14 @@ static void xwm_handle_net_wm_state_message(struct wlr_xwm *xwm, } else if (property == xwm->atoms[NET_WM_STATE_MAXIMIZED_HORZ] && update_state(action, &xsurface->maximized_horz)) { xsurface_set_net_wm_state(xsurface); + } else if (property == xwm->atoms[NET_WM_STATE_HIDDEN] && + update_state(action, &xsurface->minimized)) { + xsurface_set_net_wm_state(xsurface); + } else if (property != XCB_ATOM_NONE) { + char *prop_name = xwm_get_atom_name(xwm, property); + wlr_log(WLR_DEBUG, "Unhandled NET_WM_STATE property change " + "%"PRIu32" (%s)", property, prop_name ? prop_name : "(null)"); + free(prop_name); } } // client_message->data.data32[3] is the source indication @@ -1165,6 +1231,19 @@ static void xwm_handle_net_wm_state_message(struct wlr_xwm *xwm, wlr_signal_emit_safe(&xsurface->events.request_maximize, xsurface); } + + if (minimized != xsurface->minimized) { + if (xsurface->minimized) { + xsurface->saved_width = xsurface->width; + xsurface->saved_height = xsurface->height; + } + + struct wlr_xwayland_minimize_event minimize_event = { + .surface = xsurface, + .minimize = xsurface->minimized, + }; + wlr_signal_emit_safe(&xsurface->events.request_minimize, &minimize_event); + } } static void xwm_handle_wm_protocols_message(struct wlr_xwm *xwm, @@ -1187,8 +1266,8 @@ static void xwm_handle_wm_protocols_message(struct wlr_xwm *xwm, surface->pinging = false; } else { char *type_name = xwm_get_atom_name(xwm, type); - wlr_log(WLR_DEBUG, "unhandled WM_PROTOCOLS client message %u (%s)", - type, type_name); + wlr_log(WLR_DEBUG, "unhandled WM_PROTOCOLS client message %" PRIu32 " (%s)", + type, type_name ? type_name : "(null)"); free(type_name); } } @@ -1202,6 +1281,32 @@ static void xwm_handle_net_active_window_message(struct wlr_xwm *xwm, wlr_signal_emit_safe(&surface->events.request_activate, surface); } +static void xwm_handle_wm_change_state_message(struct wlr_xwm *xwm, + xcb_client_message_event_t *ev) { + struct wlr_xwayland_surface *xsurface = lookup_surface(xwm, ev->window); + uint32_t detail = ev->data.data32[0]; + + if (xsurface == NULL) { + return; + } + + bool minimize; + if (detail == ICCCM_ICONIC_STATE) { + minimize = true; + } else if (detail == ICCCM_NORMAL_STATE) { + minimize = false; + } else { + wlr_log(WLR_DEBUG, "unhandled wm_change_state event %u", detail); + return; + } + + struct wlr_xwayland_minimize_event minimize_event = { + .surface = xsurface, + .minimize = minimize, + }; + wlr_signal_emit_safe(&xsurface->events.request_minimize, &minimize_event); +} + static void xwm_handle_client_message(struct wlr_xwm *xwm, xcb_client_message_event_t *ev) { wlr_log(WLR_DEBUG, "XCB_CLIENT_MESSAGE (%u)", ev->window); @@ -1216,14 +1321,26 @@ static void xwm_handle_client_message(struct wlr_xwm *xwm, xwm_handle_wm_protocols_message(xwm, ev); } else if (ev->type == xwm->atoms[NET_ACTIVE_WINDOW]) { xwm_handle_net_active_window_message(xwm, ev); + } else if (ev->type == xwm->atoms[WM_CHANGE_STATE]) { + xwm_handle_wm_change_state_message(xwm, ev); } else if (!xwm_handle_selection_client_message(xwm, ev)) { char *type_name = xwm_get_atom_name(xwm, ev->type); - wlr_log(WLR_DEBUG, "unhandled x11 client message %u (%s)", ev->type, - type_name); + wlr_log(WLR_DEBUG, "unhandled x11 client message %" PRIu32 " (%s)", ev->type, + type_name ? type_name : "(null)"); free(type_name); } } +static bool validate_focus_serial(uint16_t last_focus_seq, uint16_t event_seq) { + uint16_t rev_dist = event_seq - last_focus_seq; + if (rev_dist >= UINT16_MAX / 2) { + // Probably overflow or too old + return false; + } + + return true; +} + static void xwm_handle_focus_in(struct wlr_xwm *xwm, xcb_focus_in_event_t *ev) { // Do not interfere with grabs @@ -1242,14 +1359,17 @@ static void xwm_handle_focus_in(struct wlr_xwm *xwm, // Note: Some applications rely on being able to change focus, for ex. Steam: // https://github.com/swaywm/sway/issues/1865 // Because of that, we allow changing focus between surfaces belonging to the - // same application. + // same application. We must be careful to ignore requests that are too old + // though, because otherwise it may lead to race conditions: + // https://github.com/swaywm/wlroots/issues/2324 struct wlr_xwayland_surface *requested_focus = lookup_surface(xwm, ev->event); if (xwm->focus_surface && requested_focus && - requested_focus->pid == xwm->focus_surface->pid) { - xwm->focus_surface = requested_focus; + requested_focus->pid == xwm->focus_surface->pid && + validate_focus_serial(xwm->last_focus_seq, ev->sequence)) { + xwm_set_focus_window(xwm, requested_focus); + } else { + xwm_set_focus_window(xwm, xwm->focus_surface); } - - xwm_send_focus_window(xwm, xwm->focus_surface); } static void xwm_handle_xcb_error(struct wlr_xwm *xwm, xcb_value_error_t *ev) { @@ -1545,7 +1665,7 @@ static void xwm_get_resources(struct wlr_xwm *xwm) { xfixes_reply = xcb_xfixes_query_version_reply(xwm->xcb_conn, xfixes_cookie, NULL); - wlr_log(WLR_DEBUG, "xfixes version: %d.%d", + wlr_log(WLR_DEBUG, "xfixes version: %" PRIu32 ".%" PRIu32, xfixes_reply->major_version, xfixes_reply->minor_version); free(xfixes_reply); @@ -1764,10 +1884,12 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) { xwm->atoms[NET_WM_STATE], xwm->atoms[NET_ACTIVE_WINDOW], xwm->atoms[NET_WM_MOVERESIZE], + xwm->atoms[NET_WM_STATE_FOCUSED], xwm->atoms[NET_WM_STATE_MODAL], xwm->atoms[NET_WM_STATE_FULLSCREEN], xwm->atoms[NET_WM_STATE_MAXIMIZED_VERT], xwm->atoms[NET_WM_STATE_MAXIMIZED_HORZ], + xwm->atoms[NET_WM_STATE_HIDDEN], xwm->atoms[NET_CLIENT_LIST], }; xcb_change_property(xwm->xcb_conn, @@ -1799,6 +1921,20 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) { return xwm; } +void wlr_xwayland_surface_set_minimized(struct wlr_xwayland_surface *surface, + bool minimized) { + surface->minimized = minimized; + + if (minimized) { + xsurface_set_wm_state(surface, ICCCM_ICONIC_STATE); + } else { + xsurface_set_wm_state(surface, ICCCM_NORMAL_STATE); + } + + xsurface_set_net_wm_state(surface); + xcb_flush(surface->xwm->xcb_conn); +} + void wlr_xwayland_surface_set_maximized(struct wlr_xwayland_surface *surface, bool maximized) { surface->maximized_horz = maximized;