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..7df1cfbdb 100644 --- a/backend/backend.c +++ b/backend/backend.c @@ -151,7 +151,7 @@ static struct wlr_backend *attempt_noop_backend(struct wl_display *display) { static struct wlr_backend *attempt_drm_backend(struct wl_display *display, struct wlr_backend *backend, struct wlr_session *session, wlr_renderer_create_func_t create_renderer_func) { - int gpus[8]; + struct wlr_device *gpus[8]; size_t num_gpus = wlr_session_find_gpus(session, 8, gpus); struct wlr_backend *primary_drm = NULL; wlr_log(WLR_INFO, "Found %zu GPUs", num_gpus); @@ -160,7 +160,7 @@ static struct wlr_backend *attempt_drm_backend(struct wl_display *display, struct wlr_backend *drm = wlr_drm_backend_create(display, session, gpus[i], primary_drm, create_renderer_func); if (!drm) { - wlr_log(WLR_ERROR, "Failed to open DRM device %d", gpus[i]); + wlr_log(WLR_ERROR, "Failed to create DRM backend"); continue; } @@ -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/atomic.c b/backend/drm/atomic.c index cf5367464..fe67244f0 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -25,8 +25,7 @@ static void atomic_begin(struct atomic *atom) { static bool atomic_commit(struct atomic *atom, struct wlr_drm_connector *conn, uint32_t flags) { - struct wlr_drm_backend *drm = - get_drm_backend_from_backend(conn->output.backend); + struct wlr_drm_backend *drm = conn->backend; if (atom->failed) { return false; } @@ -34,7 +33,7 @@ static bool atomic_commit(struct atomic *atom, int ret = drmModeAtomicCommit(drm->fd, atom->req, flags, drm); if (ret) { wlr_log_errno(WLR_ERROR, "%s: Atomic %s failed (%s)", - conn->output.name, + conn->name, (flags & DRM_MODE_ATOMIC_TEST_ONLY) ? "test" : "commit", (flags & DRM_MODE_ATOMIC_ALLOW_MODESET) ? "modeset" : "pageflip"); return false; diff --git a/backend/drm/backend.c b/backend/drm/backend.c index da6e1ebc7..ea87532f9 100644 --- a/backend/drm/backend.c +++ b/backend/drm/backend.c @@ -38,7 +38,7 @@ static void backend_destroy(struct wlr_backend *backend) { struct wlr_drm_connector *conn, *next; wl_list_for_each_safe(conn, next, &drm->outputs, link) { - wlr_output_destroy(&conn->output); + destroy_drm_connector(conn); } wlr_signal_emit_safe(&backend->events.destroy, backend); @@ -50,7 +50,9 @@ static void backend_destroy(struct wlr_backend *backend) { finish_drm_resources(drm); finish_drm_renderer(&drm->renderer); - wlr_session_close_file(drm->session, drm->fd); + + free(drm->name); + wlr_session_close_file(drm->session, drm->dev); wl_event_source_remove(drm->drm_event); free(drm); } @@ -85,7 +87,7 @@ bool wlr_backend_is_drm(struct wlr_backend *b) { static void session_signal(struct wl_listener *listener, void *data) { struct wlr_drm_backend *drm = wl_container_of(listener, drm, session_signal); - struct wlr_session *session = data; + struct wlr_session *session = drm->session; if (session->active) { wlr_log(WLR_INFO, "DRM fd resumed"); @@ -108,9 +110,7 @@ static void drm_invalidated(struct wl_listener *listener, void *data) { struct wlr_drm_backend *drm = wl_container_of(listener, drm, drm_invalidated); - char *name = drmGetDeviceNameFromFd2(drm->fd); - wlr_log(WLR_DEBUG, "%s invalidated", name); - free(name); + wlr_log(WLR_DEBUG, "%s invalidated", drm->name); scan_drm_connectors(drm); } @@ -128,15 +128,15 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { } struct wlr_backend *wlr_drm_backend_create(struct wl_display *display, - struct wlr_session *session, int gpu_fd, struct wlr_backend *parent, + struct wlr_session *session, struct wlr_device *dev, + struct wlr_backend *parent, wlr_renderer_create_func_t create_renderer_func) { - assert(display && session && gpu_fd >= 0); + assert(display && session && dev); assert(!parent || wlr_backend_is_drm(parent)); - char *name = drmGetDeviceNameFromFd2(gpu_fd); - drmVersion *version = drmGetVersion(gpu_fd); + char *name = drmGetDeviceNameFromFd2(dev->fd); + drmVersion *version = drmGetVersion(dev->fd); wlr_log(WLR_INFO, "Initializing DRM backend for %s (%s)", name, version->name); - free(name); drmFreeVersion(version); struct wlr_drm_backend *drm = calloc(1, sizeof(struct wlr_drm_backend)); @@ -149,13 +149,15 @@ struct wlr_backend *wlr_drm_backend_create(struct wl_display *display, drm->session = session; wl_list_init(&drm->outputs); - drm->fd = gpu_fd; + drm->dev = dev; + drm->fd = dev->fd; + drm->name = name; if (parent != NULL) { drm->parent = get_drm_backend_from_backend(parent); } drm->drm_invalidated.notify = drm_invalidated; - wlr_session_signal_add(session, gpu_fd, &drm->drm_invalidated); + wl_signal_add(&dev->events.change, &drm->drm_invalidated); drm->display = display; struct wl_event_loop *event_loop = wl_display_get_event_loop(display); @@ -168,7 +170,7 @@ struct wlr_backend *wlr_drm_backend_create(struct wl_display *display, } drm->session_signal.notify = session_signal; - wl_signal_add(&session->session_signal, &drm->session_signal); + wl_signal_add(&session->events.active, &drm->session_signal); if (!check_drm_features(drm)) { goto error_event; @@ -195,7 +197,7 @@ error_event: wl_list_remove(&drm->session_signal.link); wl_event_source_remove(drm->drm_event); error_fd: - wlr_session_close_file(drm->session, drm->fd); + wlr_session_close_file(drm->session, dev); free(drm); return NULL; } diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 5b6f5e6fd..50256abd8 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -27,6 +27,7 @@ #include "backend/drm/drm.h" #include "backend/drm/iface.h" #include "backend/drm/util.h" +#include "render/swapchain.h" #include "util/signal.h" bool check_drm_features(struct wlr_drm_backend *drm) { @@ -104,22 +105,6 @@ static bool add_plane(struct wlr_drm_backend *drm, DRM_FORMAT_MOD_INVALID); } - // Choose an RGB format for the plane - uint32_t rgb_format = DRM_FORMAT_INVALID; - for (size_t j = 0; j < drm_plane->count_formats; ++j) { - uint32_t fmt = drm_plane->formats[j]; - - if (fmt == DRM_FORMAT_ARGB2101010 || fmt == DRM_FORMAT_XRGB2101010 || - fmt == DRM_FORMAT_ARGB8888) { - // Prefer formats with 10 bit color and/or alpha channel. - rgb_format = fmt; - break; - } else if (fmt == DRM_FORMAT_XRGB8888) { - rgb_format = fmt; - } - } - p->drm_format = rgb_format; - if (p->props.in_formats) { uint64_t blob_id; if (!get_drm_prop(drm->fd, p->id, p->props.in_formats, &blob_id)) { @@ -197,35 +182,29 @@ static bool init_planes(struct wlr_drm_backend *drm) { goto error; } - /* - * This is a very naive implementation of the plane matching - * logic. Primary and cursor planes should only work on a - * single CRTC, and this should be perfectly adequate, but - * overlay planes can potentially work with multiple CRTCs, - * meaning this could return inefficient/skewed results. - * - * However, we don't really care about overlay planes, as we - * don't support them yet. We only bother to keep basic - * tracking of them for DRM lease clients. - * - * possible_crtcs is a bitmask of crtcs, where each bit is an - * index into drmModeRes.crtcs. So if bit 0 is set (ffs starts - * counting from 1), crtc 0 is possible. - */ - int crtc_bit = ffs(plane->possible_crtcs) - 1; - - // This would be a kernel bug - assert(crtc_bit >= 0 && (size_t)crtc_bit < drm->num_crtcs); - - struct wlr_drm_crtc *crtc = &drm->crtcs[crtc_bit]; - + // We don't really care about overlay planes, as we don't support them + // yet. if (type == DRM_PLANE_TYPE_OVERLAY) { - uint32_t *tmp = realloc(crtc->overlays, - sizeof(*crtc->overlays) * (crtc->num_overlays + 1)); - if (tmp) { - crtc->overlays = tmp; - crtc->overlays[crtc->num_overlays++] = id; + drmModeFreePlane(plane); + continue; + } + + assert(drm->num_crtcs <= 32); + struct wlr_drm_crtc *crtc = NULL; + for (size_t j = 0; j < drm->num_crtcs ; j++) { + uint32_t crtc_bit = 1 << j; + if ((plane->possible_crtcs & crtc_bit) == 0) { + continue; } + + struct wlr_drm_crtc *candidate = &drm->crtcs[j]; + if ((type == DRM_PLANE_TYPE_PRIMARY && !candidate->primary) || + (type == DRM_PLANE_TYPE_CURSOR && !candidate->cursor)) { + crtc = candidate; + break; + } + } + if (!crtc) { drmModeFreePlane(plane); continue; } @@ -314,7 +293,6 @@ void finish_drm_resources(struct wlr_drm_backend *drm) { wlr_drm_format_set_finish(&crtc->cursor->formats); free(crtc->cursor); } - free(crtc->overlays); } free(drm->crtcs); @@ -332,16 +310,24 @@ static bool drm_connector_attach_render(struct wlr_output *output, return drm_surface_make_current(&conn->crtc->primary->surf, buffer_age); } +static void drm_plane_set_committed(struct wlr_drm_plane *plane) { + drm_fb_move(&plane->queued_fb, &plane->pending_fb); + + struct wlr_buffer *queued = plane->queued_fb.wlr_buf; + if (queued != NULL) { + wlr_swapchain_set_buffer_submitted(plane->surf.swapchain, queued); + } +} + static bool drm_crtc_commit(struct wlr_drm_connector *conn, uint32_t flags) { - struct wlr_drm_backend *drm = - get_drm_backend_from_backend(conn->output.backend); + struct wlr_drm_backend *drm = conn->backend; struct wlr_drm_crtc *crtc = conn->crtc; bool ok = drm->iface->crtc_commit(drm, conn, flags); if (ok && !(flags & DRM_MODE_ATOMIC_TEST_ONLY)) { memcpy(&crtc->current, &crtc->pending, sizeof(struct wlr_drm_crtc_state)); - drm_fb_move(&crtc->primary->queued_fb, &crtc->primary->pending_fb); + drm_plane_set_committed(crtc->primary); if (crtc->cursor != NULL) { - drm_fb_move(&crtc->cursor->queued_fb, &crtc->cursor->pending_fb); + drm_plane_set_committed(crtc->cursor); } } else { memcpy(&crtc->pending, &crtc->current, sizeof(struct wlr_drm_crtc_state)); @@ -362,18 +348,24 @@ static bool drm_crtc_page_flip(struct wlr_drm_connector *conn) { // we'll wait for all queued page-flips to complete, so we don't need this // safeguard. if (conn->pageflip_pending && !crtc->pending_modeset) { - wlr_log(WLR_ERROR, "Failed to page-flip output '%s': " - "a page-flip is already pending", conn->output.name); + wlr_drm_conn_log(conn, WLR_ERROR, "Failed to page-flip output: " + "a page-flip is already pending"); return false; } assert(crtc->pending.active); - assert(plane_get_next_fb(crtc->primary)->type != WLR_DRM_FB_TYPE_NONE); + assert(plane_get_next_fb(crtc->primary)->bo); if (!drm_crtc_commit(conn, DRM_MODE_PAGE_FLIP_EVENT)) { return false; } 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; } @@ -381,8 +373,6 @@ static uint32_t strip_alpha_channel(uint32_t format) { switch (format) { case DRM_FORMAT_ARGB8888: return DRM_FORMAT_XRGB8888; - case DRM_FORMAT_ARGB2101010: - return DRM_FORMAT_XRGB2101010; default: return DRM_FORMAT_INVALID; } @@ -390,8 +380,7 @@ static uint32_t strip_alpha_channel(uint32_t format) { static bool test_buffer(struct wlr_drm_connector *conn, struct wlr_buffer *wlr_buffer) { - struct wlr_output *output = &conn->output; - struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); + struct wlr_drm_backend *drm = conn->backend; if (!drm->session->active) { return false; @@ -441,7 +430,8 @@ static bool drm_connector_test(struct wlr_output *output) { output->pending.enabled) { if (output->current_mode == NULL && !(output->pending.committed & WLR_OUTPUT_STATE_MODE)) { - wlr_log(WLR_DEBUG, "Can't enable an output without a mode"); + wlr_drm_conn_log(conn, WLR_DEBUG, + "Can't enable an output without a mode"); return false; } } @@ -476,7 +466,7 @@ static struct wlr_output_mode *drm_connector_get_pending_mode( static bool drm_connector_commit_buffer(struct wlr_output *output) { struct wlr_drm_connector *conn = get_drm_connector_from_output(output); - struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); + struct wlr_drm_backend *drm = conn->backend; struct wlr_drm_crtc *crtc = conn->crtc; if (!crtc) { @@ -488,7 +478,7 @@ static bool drm_connector_commit_buffer(struct wlr_output *output) { switch (output->pending.buffer_type) { case WLR_OUTPUT_STATE_BUFFER_RENDER: if (!drm_fb_lock_surface(&plane->pending_fb, &plane->surf)) { - wlr_log(WLR_ERROR, "drm_fb_lock_surface failed"); + wlr_drm_conn_log(conn, WLR_ERROR, "drm_fb_lock_surface failed"); return false; } break; @@ -512,8 +502,7 @@ static bool drm_connector_commit_buffer(struct wlr_output *output) { } bool drm_connector_supports_vrr(struct wlr_drm_connector *conn) { - struct wlr_drm_backend *drm = - get_drm_backend_from_backend(conn->output.backend); + struct wlr_drm_backend *drm = conn->backend; struct wlr_drm_crtc *crtc = conn->crtc; if (!crtc) { @@ -524,13 +513,13 @@ bool drm_connector_supports_vrr(struct wlr_drm_connector *conn) { if (conn->props.vrr_capable == 0 || !get_drm_prop(drm->fd, conn->id, conn->props.vrr_capable, &vrr_capable) || !vrr_capable) { - wlr_log(WLR_DEBUG, "Failed to enable adaptive sync: " - "connector '%s' doesn't support VRR", conn->output.name); + wlr_drm_conn_log(conn, WLR_DEBUG, "Failed to enable adaptive sync: " + "connector doesn't support VRR"); return false; } if (crtc->props.vrr_enabled == 0) { - wlr_log(WLR_DEBUG, "Failed to enable adaptive sync: " + wlr_drm_conn_log(conn, WLR_DEBUG, "Failed to enable adaptive sync: " "CRTC %"PRIu32" doesn't support VRR", crtc->id); return false; } @@ -540,7 +529,7 @@ bool drm_connector_supports_vrr(struct wlr_drm_connector *conn) { static bool drm_connector_commit(struct wlr_output *output) { struct wlr_drm_connector *conn = get_drm_connector_from_output(output); - struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); + struct wlr_drm_backend *drm = conn->backend; if (!drm_connector_test(output)) { return false; @@ -590,13 +579,13 @@ static bool drm_connector_commit(struct wlr_output *output) { } static void drm_connector_rollback_render(struct wlr_output *output) { - struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); - wlr_egl_unset_current(&drm->renderer.egl); + struct wlr_drm_connector *conn = get_drm_connector_from_output(output); + return drm_surface_unset_current(&conn->crtc->primary->surf); } size_t drm_crtc_get_gamma_lut_size(struct wlr_drm_backend *drm, struct wlr_drm_crtc *crtc) { - if (crtc->props.gamma_lut_size == 0) { + if (crtc->props.gamma_lut_size == 0 || drm->iface == &legacy_iface) { return (size_t)crtc->legacy_crtc->gamma_size; } @@ -612,7 +601,7 @@ size_t drm_crtc_get_gamma_lut_size(struct wlr_drm_backend *drm, static size_t drm_connector_get_gamma_size(struct wlr_output *output) { struct wlr_drm_connector *conn = get_drm_connector_from_output(output); - struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); + struct wlr_drm_backend *drm = conn->backend; struct wlr_drm_crtc *crtc = conn->crtc; if (crtc == NULL) { @@ -625,7 +614,7 @@ static size_t drm_connector_get_gamma_size(struct wlr_output *output) { static bool drm_connector_export_dmabuf(struct wlr_output *output, struct wlr_dmabuf_attributes *attribs) { struct wlr_drm_connector *conn = get_drm_connector_from_output(output); - struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); + struct wlr_drm_backend *drm = conn->backend; struct wlr_drm_crtc *crtc = conn->crtc; if (!drm->session->active) { @@ -636,20 +625,22 @@ 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->bo == NULL) { + fb = &crtc->primary->current_fb; + } + if (fb->bo == NULL) { 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) { - if (plane->pending_fb.type != WLR_DRM_FB_TYPE_NONE) { + if (plane->pending_fb.bo) { return &plane->pending_fb; } - if (plane->queued_fb.type != WLR_DRM_FB_TYPE_NONE) { + if (plane->queued_fb.bo) { return &plane->queued_fb; } return &plane->current_fb; @@ -658,15 +649,16 @@ struct wlr_drm_fb *plane_get_next_fb(struct wlr_drm_plane *plane) { static bool drm_connector_pageflip_renderer(struct wlr_drm_connector *conn) { struct wlr_drm_crtc *crtc = conn->crtc; if (!crtc) { - wlr_log(WLR_ERROR, "Page-flip failed on connector '%s': no CRTC", - conn->output.name); + wlr_drm_conn_log(conn, WLR_ERROR, "Page-flip failed: no CRTC"); return false; } // drm_crtc_page_flip expects a FB to be available struct wlr_drm_plane *plane = crtc->primary; - if (plane_get_next_fb(plane)->type == WLR_DRM_FB_TYPE_NONE) { - drm_surface_render_black_frame(&plane->surf); + if (!plane_get_next_fb(plane)->bo) { + if (!drm_surface_render_black_frame(&plane->surf)) { + return false; + } if (!drm_fb_lock_surface(&plane->pending_fb, &plane->surf)) { return false; } @@ -677,21 +669,19 @@ static bool drm_connector_pageflip_renderer(struct wlr_drm_connector *conn) { static bool drm_connector_init_renderer(struct wlr_drm_connector *conn, struct wlr_drm_mode *mode) { - struct wlr_drm_backend *drm = - get_drm_backend_from_backend(conn->output.backend); + struct wlr_drm_backend *drm = conn->backend; if (conn->state != WLR_DRM_CONN_CONNECTED && conn->state != WLR_DRM_CONN_NEEDS_MODESET) { return false; } - wlr_log(WLR_DEBUG, "Initializing renderer on connector '%s'", - conn->output.name); + wlr_drm_conn_log(conn, WLR_DEBUG, "Initializing renderer"); struct wlr_drm_crtc *crtc = conn->crtc; if (!crtc) { - wlr_log(WLR_ERROR, "Failed to initialize renderer on connector '%s': " - "no CRTC", conn->output.name); + wlr_drm_conn_log(conn, WLR_ERROR, + "Failed to initialize renderer: no CRTC"); return false; } struct wlr_drm_plane *plane = crtc->primary; @@ -702,28 +692,28 @@ static bool drm_connector_init_renderer(struct wlr_drm_connector *conn, int width = mode->wlr_mode.width; int height = mode->wlr_mode.height; - uint32_t format = drm->renderer.gbm_format; + uint32_t format = DRM_FORMAT_ARGB8888; bool modifiers = true; const char *no_modifiers = getenv("WLR_DRM_NO_MODIFIERS"); if (no_modifiers != NULL && strcmp(no_modifiers, "1") == 0) { - wlr_log(WLR_DEBUG, + wlr_drm_conn_log(conn, WLR_DEBUG, "WLR_DRM_NO_MODIFIERS set, initializing planes without modifiers"); modifiers = false; } - if (!drm_plane_init_surface(plane, drm, width, height, format, 0, modifiers) || + if (!drm_plane_init_surface(plane, drm, width, height, format, false, modifiers) || !drm_connector_pageflip_renderer(conn)) { if (!modifiers) { - wlr_log(WLR_ERROR, "Failed to initialize renderer " - "on connector '%s': initial page-flip failed", - conn->output.name); + wlr_drm_conn_log(conn, WLR_ERROR, "Failed to initialize renderer:" + "initial page-flip failed"); return false; } // If page-flipping with modifiers enabled doesn't work, retry without // modifiers - wlr_log(WLR_INFO, "Page-flip failed with primary FB modifiers enabled, " + wlr_drm_conn_log(conn, WLR_INFO, + "Page-flip failed with primary FB modifiers enabled, " "retrying without modifiers"); modifiers = false; @@ -732,13 +722,12 @@ static bool drm_connector_init_renderer(struct wlr_drm_connector *conn, crtc->pending.mode = mode; if (!drm_plane_init_surface(plane, drm, width, height, format, - 0, modifiers)) { + false, modifiers)) { return false; } if (!drm_connector_pageflip_renderer(conn)) { - wlr_log(WLR_ERROR, "Failed to initialize renderer " - "on connector '%s': initial page-flip failed", - conn->output.name); + wlr_drm_conn_log(conn, WLR_ERROR, "Failed to initialize renderer:" + "initial page-flip failed"); return false; } } @@ -756,19 +745,16 @@ static void attempt_enable_needs_modeset(struct wlr_drm_backend *drm) { if (conn->state == WLR_DRM_CONN_NEEDS_MODESET && conn->crtc != NULL && conn->desired_mode != NULL && conn->desired_enabled) { - wlr_log(WLR_DEBUG, "Output %s has a desired mode and a CRTC, " - "attempting a modeset", conn->output.name); + wlr_drm_conn_log(conn, WLR_DEBUG, + "Output has a desired mode and a CRTC, attempting a modeset"); drm_connector_set_mode(conn, conn->desired_mode); } } } -static void drm_connector_cleanup(struct wlr_drm_connector *conn); - bool drm_connector_set_mode(struct wlr_drm_connector *conn, struct wlr_output_mode *wlr_mode) { - struct wlr_drm_backend *drm = - get_drm_backend_from_backend(conn->output.backend); + struct wlr_drm_backend *drm = conn->backend; conn->desired_enabled = wlr_mode != NULL; conn->desired_mode = wlr_mode; @@ -789,7 +775,8 @@ bool drm_connector_set_mode(struct wlr_drm_connector *conn, if (conn->state != WLR_DRM_CONN_CONNECTED && conn->state != WLR_DRM_CONN_NEEDS_MODESET) { - wlr_log(WLR_ERROR, "Cannot modeset a disconnected output"); + wlr_drm_conn_log(conn, WLR_ERROR, + "Cannot modeset a disconnected output"); return false; } @@ -798,18 +785,19 @@ bool drm_connector_set_mode(struct wlr_drm_connector *conn, realloc_crtcs(drm); } if (conn->crtc == NULL) { - wlr_log(WLR_ERROR, "Cannot modeset '%s': no CRTC for this connector", - conn->output.name); + wlr_drm_conn_log(conn, WLR_ERROR, + "Cannot perform modeset: no CRTC for this connector"); return false; } - wlr_log(WLR_INFO, "Modesetting '%s' with '%ux%u@%u mHz'", - conn->output.name, wlr_mode->width, wlr_mode->height, - wlr_mode->refresh); + wlr_drm_conn_log(conn, WLR_INFO, + "Modesetting with '%" PRId32 "x%" PRId32 "@%" PRId32 "mHz'", + wlr_mode->width, wlr_mode->height, wlr_mode->refresh); struct wlr_drm_mode *mode = (struct wlr_drm_mode *)wlr_mode; if (!drm_connector_init_renderer(conn, mode)) { - wlr_log(WLR_ERROR, "Failed to initialize renderer for plane"); + wlr_drm_conn_log(conn, WLR_ERROR, + "Failed to initialize renderer for plane"); return false; } @@ -852,7 +840,7 @@ struct wlr_output_mode *wlr_drm_connector_add_mode(struct wlr_output *output, mode->wlr_mode.height = mode->drm_mode.vdisplay; mode->wlr_mode.refresh = calculate_refresh_rate(modeinfo); - wlr_log(WLR_INFO, "Registered custom mode " + wlr_drm_conn_log(conn, WLR_INFO, "Registered custom mode " "%"PRId32"x%"PRId32"@%"PRId32, mode->wlr_mode.width, mode->wlr_mode.height, mode->wlr_mode.refresh); @@ -866,7 +854,7 @@ static bool drm_connector_set_cursor(struct wlr_output *output, enum wl_output_transform transform, int32_t hotspot_x, int32_t hotspot_y, bool update_texture) { struct wlr_drm_connector *conn = get_drm_connector_from_output(output); - struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend); + struct wlr_drm_backend *drm = conn->backend; struct wlr_drm_crtc *crtc = conn->crtc; if (!crtc) { @@ -878,7 +866,7 @@ static bool drm_connector_set_cursor(struct wlr_output *output, return false; } - if (!plane->surf.gbm) { + if (!plane->surf.swapchain) { int ret; uint64_t w, h; ret = drmGetCap(drm->fd, DRM_CAP_CURSOR_WIDTH, &w); @@ -887,8 +875,8 @@ static bool drm_connector_set_cursor(struct wlr_output *output, h = ret ? 64 : h; if (!drm_plane_init_surface(plane, drm, w, h, - DRM_FORMAT_ARGB8888, GBM_BO_USE_LINEAR, false)) { - wlr_log(WLR_ERROR, "Cannot allocate cursor resources"); + DRM_FORMAT_ARGB8888, true, false)) { + wlr_drm_conn_log(conn, WLR_ERROR, "Cannot allocate cursor resources"); return false; } } @@ -926,7 +914,7 @@ static bool drm_connector_set_cursor(struct wlr_output *output, height = height * output->scale / scale; if (width > (int)plane->surf.width || height > (int)plane->surf.height) { - wlr_log(WLR_ERROR, "Cursor too large (max %dx%d)", + wlr_drm_conn_log(conn, WLR_ERROR, "Cursor too large (max %dx%d)", (int)plane->surf.width, (int)plane->surf.height); return false; } @@ -954,19 +942,6 @@ static bool drm_connector_set_cursor(struct wlr_output *output, plane->cursor_enabled = true; } - if (plane->cursor_enabled) { - drm_fb_acquire(&plane->pending_fb, drm, &plane->mgpu_surf); - /* Workaround for nouveau buffers created with GBM_BO_USER_LINEAR are - * placed in NOUVEAU_GEM_DOMAIN_GART. When the bo is attached to the - * cursor plane it is moved to NOUVEAU_GEM_DOMAIN_VRAM. However, this - * does not wait for the render operations to complete, leaving an - * empty surface. See - * https://gitlab.freedesktop.org/xorg/driver/xf86-video-nouveau/issues/480 - * The render operations can be waited for using: - */ - glFinish(); - } - wlr_output_update_needs_frame(output); return true; } @@ -991,10 +966,8 @@ static bool drm_connector_move_cursor(struct wlr_output *output, wlr_output_transform_invert(output->transform); wlr_box_transform(&box, &box, transform, width, height); - if (plane != NULL) { - box.x -= plane->cursor_hotspot_x; - box.y -= plane->cursor_hotspot_y; - } + box.x -= plane->cursor_hotspot_x; + box.y -= plane->cursor_hotspot_y; conn->cursor_x = box.x; conn->cursor_y = box.y; @@ -1014,18 +987,38 @@ bool drm_connector_is_cursor_visible(struct wlr_drm_connector *conn) { conn->cursor_y + (int)plane->surf.height >= 0; } -static void drm_connector_destroy(struct wlr_output *output) { +static void dealloc_crtc(struct wlr_drm_connector *conn); + +/** + * Destroy the compositor-facing part of a connector. + * + * The connector isn't destroyed when disconnected. Only the compositor-facing + * wlr_output interface is cleaned up. + */ +static void drm_connector_destroy_output(struct wlr_output *output) { struct wlr_drm_connector *conn = get_drm_connector_from_output(output); - drm_connector_cleanup(conn); - drmModeFreeCrtc(conn->old_crtc); - wl_list_remove(&conn->link); - free(conn); + + dealloc_crtc(conn); + + conn->state = WLR_DRM_CONN_DISCONNECTED; + conn->desired_enabled = false; + conn->desired_mode = NULL; + conn->possible_crtc = 0; + conn->pageflip_pending = false; + + struct wlr_drm_mode *mode, *mode_tmp; + wl_list_for_each_safe(mode, mode_tmp, &conn->output.modes, wlr_mode.link) { + wl_list_remove(&mode->wlr_mode.link); + free(mode); + } + + memset(&conn->output, 0, sizeof(struct wlr_output)); } static const struct wlr_output_impl output_impl = { .set_cursor = drm_connector_set_cursor, .move_cursor = drm_connector_move_cursor, - .destroy = drm_connector_destroy, + .destroy = drm_connector_destroy_output, .attach_render = drm_connector_attach_render, .test = drm_connector_test, .commit = drm_connector_commit, @@ -1048,14 +1041,13 @@ static const int32_t subpixel_map[] = { }; static void dealloc_crtc(struct wlr_drm_connector *conn) { - struct wlr_drm_backend *drm = - get_drm_backend_from_backend(conn->output.backend); + struct wlr_drm_backend *drm = conn->backend; if (conn->crtc == NULL) { return; } - wlr_log(WLR_DEBUG, "De-allocating CRTC %zu for output '%s'", - conn->crtc - drm->crtcs, conn->output.name); + wlr_drm_conn_log(conn, WLR_DEBUG, "De-allocating CRTC %zu", + conn->crtc - drm->crtcs); conn->crtc->pending_modeset = true; conn->crtc->pending.active = false; @@ -1098,8 +1090,7 @@ static void realloc_crtcs(struct wlr_drm_backend *drm) { connectors[i] = conn; wlr_log(WLR_DEBUG, " '%s' crtc=%d state=%d desired_enabled=%d", - conn->output.name, - conn->crtc ? (int)(conn->crtc - drm->crtcs) : -1, + conn->name, conn->crtc ? (int)(conn->crtc - drm->crtcs) : -1, conn->state, conn->desired_enabled); if (conn->crtc) { @@ -1157,9 +1148,7 @@ static void realloc_crtcs(struct wlr_drm_backend *drm) { bool prev_enabled = conn->crtc; wlr_log(WLR_DEBUG, " '%s' crtc=%zd state=%d desired_enabled=%d", - conn->output.name, - connector_match[i], - conn->state, conn->desired_enabled); + conn->name, connector_match[i], conn->state, conn->desired_enabled); // We don't need to change anything. if (prev_enabled && connector_match[i] == conn->crtc - drm->crtcs) { @@ -1170,8 +1159,7 @@ static void realloc_crtcs(struct wlr_drm_backend *drm) { if (connector_match[i] == -1) { if (prev_enabled) { - wlr_log(WLR_DEBUG, "Output has %s lost its CRTC", - conn->output.name); + wlr_drm_conn_log(conn, WLR_DEBUG, "Output has lost its CRTC"); conn->state = WLR_DRM_CONN_NEEDS_MODESET; wlr_output_update_enabled(&conn->output, false); conn->desired_mode = conn->output.current_mode; @@ -1190,8 +1178,7 @@ static void realloc_crtcs(struct wlr_drm_backend *drm) { struct wlr_drm_mode *mode = (struct wlr_drm_mode *)conn->output.current_mode; if (!drm_connector_init_renderer(conn, mode)) { - wlr_log(WLR_ERROR, "Failed to initialize renderer on output %s", - conn->output.name); + wlr_drm_conn_log(conn, WLR_ERROR, "Failed to initialize renderer"); wlr_output_update_enabled(&conn->output, false); continue; } @@ -1201,8 +1188,8 @@ static void realloc_crtcs(struct wlr_drm_backend *drm) { } static uint32_t get_possible_crtcs(int fd, drmModeRes *res, - drmModeConnector *conn, bool is_mst) { - uint32_t ret = 0; + drmModeConnector *conn) { + uint32_t possible_crtcs = 0; for (int i = 0; i < conn->count_encoders; ++i) { drmModeEncoder *enc = drmModeGetEncoder(fd, conn->encoders[i]); @@ -1210,35 +1197,16 @@ static uint32_t get_possible_crtcs(int fd, drmModeRes *res, continue; } - ret |= enc->possible_crtcs; + possible_crtcs |= enc->possible_crtcs; drmModeFreeEncoder(enc); } - // Sometimes DP MST connectors report no encoders, so we'll loop though - // all of the encoders of the MST type instead. - // TODO: See if there is a better solution. - - if (!is_mst || ret) { - return ret; - } - - for (int i = 0; i < res->count_encoders; ++i) { - drmModeEncoder *enc = drmModeGetEncoder(fd, res->encoders[i]); - if (!enc) { - continue; - } - - if (enc->encoder_type == DRM_MODE_ENCODER_DPMST) { - ret |= enc->possible_crtcs; - } - - drmModeFreeEncoder(enc); - } - - return ret; + return possible_crtcs; } +static void disconnect_drm_connector(struct wlr_drm_connector *conn); + void scan_drm_connectors(struct wlr_drm_backend *drm) { /* * This GPU is not really a modesetting device. @@ -1248,7 +1216,7 @@ void scan_drm_connectors(struct wlr_drm_backend *drm) { return; } - wlr_log(WLR_INFO, "Scanning DRM connectors"); + wlr_log(WLR_INFO, "Scanning DRM connectors on %s", drm->name); drmModeRes *res = drmModeGetResources(drm->fd); if (!res) { @@ -1292,13 +1260,12 @@ void scan_drm_connectors(struct wlr_drm_backend *drm) { drmModeFreeConnector(drm_conn); continue; } - wlr_output_init(&wlr_conn->output, &drm->backend, &output_impl, - drm->display); + wlr_conn->backend = drm; wlr_conn->state = WLR_DRM_CONN_DISCONNECTED; wlr_conn->id = drm_conn->connector_id; - snprintf(wlr_conn->output.name, sizeof(wlr_conn->output.name), + snprintf(wlr_conn->name, sizeof(wlr_conn->name), "%s-%"PRIu32, conn_get_name(drm_conn->connector_type), drm_conn->connector_type_id); @@ -1307,7 +1274,7 @@ void scan_drm_connectors(struct wlr_drm_backend *drm) { } wl_list_insert(drm->outputs.prev, &wlr_conn->link); - wlr_log(WLR_INFO, "Found connector '%s'", wlr_conn->output.name); + wlr_log(WLR_INFO, "Found connector '%s'", wlr_conn->name); } else { seen[index] = true; } @@ -1329,24 +1296,30 @@ void scan_drm_connectors(struct wlr_drm_backend *drm) { uint64_t link_status; if (!get_drm_prop(drm->fd, wlr_conn->id, wlr_conn->props.link_status, &link_status)) { - wlr_log(WLR_ERROR, "Failed to get link status for '%s'", - wlr_conn->output.name); + wlr_drm_conn_log(wlr_conn, WLR_ERROR, + "Failed to get link status prop"); continue; } if (link_status == DRM_MODE_LINK_STATUS_BAD) { // We need to reload our list of modes and force a modeset - wlr_log(WLR_INFO, "Bad link for '%s'", wlr_conn->output.name); - drm_connector_cleanup(wlr_conn); + wlr_drm_conn_log(wlr_conn, WLR_INFO, "Bad link detected"); + disconnect_drm_connector(wlr_conn); } } if (wlr_conn->state == WLR_DRM_CONN_DISCONNECTED && drm_conn->connection == DRM_MODE_CONNECTED) { - wlr_log(WLR_INFO, "'%s' connected", wlr_conn->output.name); + wlr_log(WLR_INFO, "'%s' connected", wlr_conn->name); wlr_log(WLR_DEBUG, "Current CRTC: %d", wlr_conn->crtc ? (int)wlr_conn->crtc->id : -1); + wlr_output_init(&wlr_conn->output, &drm->backend, &output_impl, + drm->display); + + memcpy(wlr_conn->output.name, wlr_conn->name, + sizeof(wlr_conn->output.name)); + wlr_conn->output.phys_width = drm_conn->mmWidth; wlr_conn->output.phys_height = drm_conn->mmHeight; wlr_log(WLR_INFO, "Physical size: %"PRId32"x%"PRId32, @@ -1397,20 +1370,9 @@ void scan_drm_connectors(struct wlr_drm_backend *drm) { wl_list_insert(&wlr_conn->output.modes, &mode->wlr_mode.link); } - size_t path_len; - 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) { - is_mst = true; - } - free(path); - - wlr_conn->possible_crtc = get_possible_crtcs(drm->fd, res, drm_conn, - is_mst); + wlr_conn->possible_crtc = get_possible_crtcs(drm->fd, res, drm_conn); if (wlr_conn->possible_crtc == 0) { - wlr_log(WLR_ERROR, "No CRTC possible for connector '%s'", - wlr_conn->output.name); + wlr_drm_conn_log(wlr_conn, WLR_ERROR, "No CRTC possible"); } // TODO: this results in connectors being enabled without a mode @@ -1423,9 +1385,8 @@ void scan_drm_connectors(struct wlr_drm_backend *drm) { } else if ((wlr_conn->state == WLR_DRM_CONN_CONNECTED || wlr_conn->state == WLR_DRM_CONN_NEEDS_MODESET) && drm_conn->connection != DRM_MODE_CONNECTED) { - wlr_log(WLR_INFO, "'%s' disconnected", wlr_conn->output.name); - - drm_connector_cleanup(wlr_conn); + wlr_log(WLR_INFO, "'%s' disconnected", wlr_conn->name); + disconnect_drm_connector(wlr_conn); } drmModeFreeEncoder(curr_enc); @@ -1444,10 +1405,8 @@ void scan_drm_connectors(struct wlr_drm_backend *drm) { continue; } - wlr_log(WLR_INFO, "'%s' disappeared", conn->output.name); - drm_connector_cleanup(conn); - - wlr_output_destroy(&conn->output); + wlr_log(WLR_INFO, "'%s' disappeared", conn->name); + disconnect_drm_connector(conn); } realloc_crtcs(drm); @@ -1455,8 +1414,7 @@ void scan_drm_connectors(struct wlr_drm_backend *drm) { for (size_t i = 0; i < new_outputs_len; ++i) { struct wlr_drm_connector *conn = new_outputs[i]; - wlr_log(WLR_INFO, "Requesting modeset for '%s'", - conn->output.name); + wlr_drm_conn_log(conn, WLR_INFO, "Requesting modeset"); wlr_signal_emit_safe(&drm->backend.events.new_output, &conn->output); } @@ -1481,7 +1439,7 @@ static void page_flip_handler(int fd, unsigned seq, } } if (!conn) { - wlr_log(WLR_DEBUG, "No connector for crtc_id %u", crtc_id); + wlr_log(WLR_DEBUG, "No connector for CRTC %u", crtc_id); return; } @@ -1492,11 +1450,10 @@ static void page_flip_handler(int fd, unsigned seq, } struct wlr_drm_plane *plane = conn->crtc->primary; - if (plane->queued_fb.type != WLR_DRM_FB_TYPE_NONE) { + if (plane->queued_fb.bo) { drm_fb_move(&plane->current_fb, &plane->queued_fb); } - if (conn->crtc->cursor && - conn->crtc->cursor->queued_fb.type != WLR_DRM_FB_TYPE_NONE) { + if (conn->crtc->cursor && conn->crtc->cursor->queued_fb.bo) { drm_fb_move(&conn->crtc->cursor->current_fb, &conn->crtc->cursor->queued_fb); } @@ -1507,7 +1464,8 @@ static void page_flip_handler(int fd, unsigned seq, * data between the GPUs, even if we were using the direct scanout * interface. */ - if (!drm->parent && plane->current_fb.type == WLR_DRM_FB_TYPE_WLR_BUFFER) { + if (!drm->parent && plane->current_fb.wlr_buf && + wlr_client_buffer_get(plane->current_fb.wlr_buf)) { present_flags |= WLR_OUTPUT_PRESENT_ZERO_COPY; } @@ -1526,7 +1484,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); } } @@ -1581,49 +1539,20 @@ void restore_drm_outputs(struct wlr_drm_backend *drm) { } } -static void drm_connector_cleanup(struct wlr_drm_connector *conn) { - if (!conn) { +static void disconnect_drm_connector(struct wlr_drm_connector *conn) { + if (conn->state == WLR_DRM_CONN_DISCONNECTED) { return; } - switch (conn->state) { - case WLR_DRM_CONN_CONNECTED: - case WLR_DRM_CONN_CLEANUP: - conn->output.current_mode = NULL; - conn->desired_mode = NULL; - struct wlr_drm_mode *mode, *tmp; - wl_list_for_each_safe(mode, tmp, &conn->output.modes, wlr_mode.link) { - wl_list_remove(&mode->wlr_mode.link); - free(mode); - } - - conn->output.enabled = false; - conn->output.width = conn->output.height = conn->output.refresh = 0; - - memset(&conn->output.make, 0, sizeof(conn->output.make)); - memset(&conn->output.model, 0, sizeof(conn->output.model)); - memset(&conn->output.serial, 0, sizeof(conn->output.serial)); - - if (conn->output.idle_frame != NULL) { - wl_event_source_remove(conn->output.idle_frame); - conn->output.idle_frame = NULL; - } - conn->output.needs_frame = false; - conn->output.frame_pending = false; - - /* Fallthrough */ - case WLR_DRM_CONN_NEEDS_MODESET: - wlr_log(WLR_INFO, "Emitting destruction signal for '%s'", - conn->output.name); - dealloc_crtc(conn); - conn->possible_crtc = 0; - conn->desired_mode = NULL; - conn->pageflip_pending = false; - wlr_signal_emit_safe(&conn->output.events.destroy, &conn->output); - break; - case WLR_DRM_CONN_DISCONNECTED: - break; - } - - conn->state = WLR_DRM_CONN_DISCONNECTED; + // This will cleanup the compositor-facing wlr_output, but won't destroy + // our wlr_drm_connector. + wlr_output_destroy(&conn->output); +} + +void destroy_drm_connector(struct wlr_drm_connector *conn) { + disconnect_drm_connector(conn); + + drmModeFreeCrtc(conn->old_crtc); + wl_list_remove(&conn->link); + free(conn); } diff --git a/backend/drm/legacy.c b/backend/drm/legacy.c index 3380461ca..fc3158e16 100644 --- a/backend/drm/legacy.c +++ b/backend/drm/legacy.c @@ -42,15 +42,14 @@ static bool legacy_crtc_commit(struct wlr_drm_backend *drm, DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF; if (drmModeConnectorSetProperty(drm->fd, conn->id, conn->props.dpms, dpms) != 0) { - wlr_log_errno(WLR_ERROR, "%s: failed to set DPMS property", - conn->output.name); + wlr_drm_conn_log_errno(conn, WLR_ERROR, + "Failed to set DPMS property"); return false; } if (drmModeSetCrtc(drm->fd, crtc->id, fb_id, 0, 0, conns, conns_len, mode)) { - wlr_log_errno(WLR_ERROR, "%s: failed to set CRTC", - conn->output.name); + wlr_drm_conn_log_errno(conn, WLR_ERROR, "Failed to set CRTC"); return false; } } @@ -67,16 +66,15 @@ static bool legacy_crtc_commit(struct wlr_drm_backend *drm, if (drmModeObjectSetProperty(drm->fd, crtc->id, DRM_MODE_OBJECT_CRTC, crtc->props.vrr_enabled, output->pending.adaptive_sync_enabled) != 0) { - wlr_log_errno(WLR_ERROR, + wlr_drm_conn_log_errno(conn, WLR_ERROR, "drmModeObjectSetProperty(VRR_ENABLED) failed"); return false; } output->adaptive_sync_status = output->pending.adaptive_sync_enabled ? WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED : WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED; - wlr_log(WLR_DEBUG, "VRR %s on connector '%s'", - output->pending.adaptive_sync_enabled ? "enabled" : "disabled", - output->name); + wlr_drm_conn_log(conn, WLR_DEBUG, "VRR %s", + output->pending.adaptive_sync_enabled ? "enabled" : "disabled"); } if (cursor != NULL && drm_connector_is_cursor_visible(conn)) { @@ -84,29 +82,26 @@ static bool legacy_crtc_commit(struct wlr_drm_backend *drm, struct gbm_bo *cursor_bo = drm_fb_acquire(cursor_fb, drm, &cursor->mgpu_surf); if (!cursor_bo) { - wlr_log_errno(WLR_DEBUG, "%s: failed to acquire cursor FB", - conn->output.name); + wlr_drm_conn_log_errno(conn, WLR_DEBUG, + "Failed to acquire cursor FB"); return false; } if (drmModeSetCursor(drm->fd, crtc->id, gbm_bo_get_handle(cursor_bo).u32, cursor->surf.width, cursor->surf.height)) { - wlr_log_errno(WLR_DEBUG, "%s: failed to set hardware cursor", - conn->output.name); + wlr_drm_conn_log_errno(conn, WLR_DEBUG, "drmModeSetCursor failed"); return false; } if (drmModeMoveCursor(drm->fd, crtc->id, conn->cursor_x, conn->cursor_y) != 0) { - wlr_log_errno(WLR_ERROR, "%s: failed to move cursor", - conn->output.name); + wlr_drm_conn_log_errno(conn, WLR_ERROR, "drmModeMoveCursor failed"); return false; } } else { if (drmModeSetCursor(drm->fd, crtc->id, 0, 0, 0)) { - wlr_log_errno(WLR_DEBUG, "%s: failed to unset hardware cursor", - conn->output.name); + wlr_drm_conn_log_errno(conn, WLR_DEBUG, "drmModeSetCursor failed"); return false; } } @@ -114,7 +109,7 @@ static bool legacy_crtc_commit(struct wlr_drm_backend *drm, if (flags & DRM_MODE_PAGE_FLIP_EVENT) { if (drmModePageFlip(drm->fd, crtc->id, fb_id, DRM_MODE_PAGE_FLIP_EVENT, drm)) { - wlr_log_errno(WLR_ERROR, "%s: Failed to page flip", conn->output.name); + wlr_drm_conn_log_errno(conn, WLR_ERROR, "drmModePageFlip failed"); return false; } } diff --git a/backend/drm/properties.c b/backend/drm/properties.c index 0fafca7b7..de51a3dea 100644 --- a/backend/drm/properties.c +++ b/backend/drm/properties.c @@ -35,8 +35,6 @@ static const struct prop_info crtc_info[] = { { "GAMMA_LUT_SIZE", INDEX(gamma_lut_size) }, { "MODE_ID", INDEX(mode_id) }, { "VRR_ENABLED", INDEX(vrr_enabled) }, - { "rotation", INDEX(rotation) }, - { "scaling mode", INDEX(scaling_mode) }, #undef INDEX }; @@ -53,6 +51,7 @@ static const struct prop_info plane_info[] = { { "SRC_W", INDEX(src_w) }, { "SRC_X", INDEX(src_x) }, { "SRC_Y", INDEX(src_y) }, + { "rotation", INDEX(rotation) }, { "type", INDEX(type) }, #undef INDEX }; diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c index a739c8e54..61b838bc2 100644 --- a/backend/drm/renderer.c +++ b/backend/drm/renderer.c @@ -1,5 +1,7 @@ +#define _POSIX_C_SOURCE 200809L #include #include +#include #include #include #include @@ -12,9 +14,14 @@ #include #include #include "backend/drm/drm.h" +#include "render/drm_format_set.h" +#include "render/gbm_allocator.h" +#include "render/swapchain.h" +#include "render/wlr_renderer.h" bool init_drm_renderer(struct wlr_drm_backend *drm, struct wlr_drm_renderer *renderer, wlr_renderer_create_func_t create_renderer_func) { + // TODO: get rid of renderer->gbm renderer->gbm = gbm_create_device(drm->fd); if (!renderer->gbm) { wlr_log(WLR_ERROR, "Failed to create GBM device"); @@ -25,29 +32,31 @@ bool init_drm_renderer(struct wlr_drm_backend *drm, create_renderer_func = wlr_renderer_autocreate; } - static EGLint config_attribs[] = { - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL_RED_SIZE, 1, - EGL_GREEN_SIZE, 1, - EGL_BLUE_SIZE, 1, - EGL_ALPHA_SIZE, 1, - EGL_NONE, - }; - - // TODO: allow setting from sway config/args. Have sane default set. - renderer->gbm_format = GBM_FORMAT_ARGB2101010; renderer->wlr_rend = create_renderer_func(&renderer->egl, - EGL_PLATFORM_GBM_MESA, renderer->gbm, - config_attribs, renderer->gbm_format); + EGL_PLATFORM_GBM_KHR, renderer->gbm, NULL, 0); if (!renderer->wlr_rend) { wlr_log(WLR_ERROR, "Failed to create EGL/WLR renderer"); goto error_gbm; } + int alloc_fd = fcntl(drm->fd, F_DUPFD_CLOEXEC, 0); + if (alloc_fd < 0) { + wlr_log_errno(WLR_ERROR, "fcntl(F_DUPFD_CLOEXEC) failed"); + goto error_wlr_rend; + } + + renderer->allocator = wlr_gbm_allocator_create(alloc_fd); + if (renderer->allocator == NULL) { + wlr_log(WLR_ERROR, "Failed to create allocator"); + close(alloc_fd); + goto error_wlr_rend; + } + renderer->fd = drm->fd; return true; +error_wlr_rend: + wlr_renderer_destroy(renderer->wlr_rend); error_gbm: gbm_device_destroy(renderer->gbm); return false; @@ -58,6 +67,7 @@ void finish_drm_renderer(struct wlr_drm_renderer *renderer) { return; } + wlr_allocator_destroy(&renderer->allocator->base); wlr_renderer_destroy(renderer->wlr_rend); wlr_egl_finish(&renderer->egl); gbm_device_destroy(renderer->gbm); @@ -65,7 +75,7 @@ void finish_drm_renderer(struct wlr_drm_renderer *renderer) { static bool init_drm_surface(struct wlr_drm_surface *surf, struct wlr_drm_renderer *renderer, uint32_t width, uint32_t height, - uint32_t format, struct wlr_drm_format_set *set, uint32_t flags) { + const struct wlr_drm_format *drm_format) { if (surf->width == width && surf->height == height) { return true; } @@ -74,43 +84,20 @@ static bool init_drm_surface(struct wlr_drm_surface *surf, surf->width = width; surf->height = height; - if (surf->gbm) { - gbm_surface_destroy(surf->gbm); - surf->gbm = NULL; - } - wlr_egl_destroy_surface(&surf->renderer->egl, surf->egl); + wlr_buffer_unlock(surf->back_buffer); + surf->back_buffer = NULL; + wlr_swapchain_destroy(surf->swapchain); + surf->swapchain = NULL; - if (!(flags & GBM_BO_USE_LINEAR) && set != NULL) { - const struct wlr_drm_format *drm_format = - wlr_drm_format_set_get(set, format); - if (drm_format != NULL) { - surf->gbm = gbm_surface_create_with_modifiers(renderer->gbm, - width, height, format, drm_format->modifiers, drm_format->len); - } - } - - if (surf->gbm == NULL) { - surf->gbm = gbm_surface_create(renderer->gbm, width, height, - format, GBM_BO_USE_RENDERING | flags); - } - if (!surf->gbm) { - wlr_log_errno(WLR_ERROR, "Failed to create GBM surface"); - goto error_zero; - } - - surf->egl = wlr_egl_create_surface(&renderer->egl, surf->gbm); - if (surf->egl == EGL_NO_SURFACE) { - wlr_log(WLR_ERROR, "Failed to create EGL surface"); - goto error_gbm; + surf->swapchain = wlr_swapchain_create(&renderer->allocator->base, + width, height, drm_format); + if (surf->swapchain == NULL) { + wlr_log(WLR_ERROR, "Failed to create swapchain"); + memset(surf, 0, sizeof(*surf)); + return false; } return true; - -error_gbm: - gbm_surface_destroy(surf->gbm); -error_zero: - memset(surf, 0, sizeof(*surf)); - return false; } static void finish_drm_surface(struct wlr_drm_surface *surf) { @@ -118,17 +105,40 @@ static void finish_drm_surface(struct wlr_drm_surface *surf) { return; } - wlr_egl_destroy_surface(&surf->renderer->egl, surf->egl); - if (surf->gbm) { - gbm_surface_destroy(surf->gbm); - } + wlr_buffer_unlock(surf->back_buffer); + wlr_swapchain_destroy(surf->swapchain); memset(surf, 0, sizeof(*surf)); } bool drm_surface_make_current(struct wlr_drm_surface *surf, int *buffer_age) { - return wlr_egl_make_current(&surf->renderer->egl, surf->egl, buffer_age); + wlr_buffer_unlock(surf->back_buffer); + surf->back_buffer = wlr_swapchain_acquire(surf->swapchain, buffer_age); + if (surf->back_buffer == NULL) { + wlr_log(WLR_ERROR, "Failed to acquire swapchain buffer"); + return false; + } + + if (!wlr_egl_make_current(&surf->renderer->egl, EGL_NO_SURFACE, NULL)) { + return false; + } + if (!wlr_renderer_bind_buffer(surf->renderer->wlr_rend, surf->back_buffer)) { + wlr_log(WLR_ERROR, "Failed to attach buffer to renderer"); + return false; + } + + return true; +} + +void drm_surface_unset_current(struct wlr_drm_surface *surf) { + assert(surf->back_buffer != NULL); + + wlr_renderer_bind_buffer(surf->renderer->wlr_rend, NULL); + wlr_egl_unset_current(&surf->renderer->egl); + + wlr_buffer_unlock(surf->back_buffer); + surf->back_buffer = NULL; } bool export_drm_bo(struct gbm_bo *bo, struct wlr_dmabuf_attributes *attribs) { @@ -203,8 +213,6 @@ static uint32_t strip_alpha_channel(uint32_t format) { switch (format) { case DRM_FORMAT_ARGB8888: return DRM_FORMAT_XRGB8888; - case DRM_FORMAT_ARGB2101010: - return DRM_FORMAT_XRGB2101010; default: return DRM_FORMAT_INVALID; } @@ -212,85 +220,117 @@ static uint32_t strip_alpha_channel(uint32_t format) { bool drm_plane_init_surface(struct wlr_drm_plane *plane, struct wlr_drm_backend *drm, int32_t width, uint32_t height, - uint32_t format, uint32_t flags, bool with_modifiers) { + uint32_t format, bool force_linear, bool with_modifiers) { if (!wlr_drm_format_set_has(&plane->formats, format, DRM_FORMAT_MOD_INVALID)) { format = strip_alpha_channel(format); } - if (!wlr_drm_format_set_has(&plane->formats, format, DRM_FORMAT_MOD_INVALID)) { + const struct wlr_drm_format *plane_format = + wlr_drm_format_set_get(&plane->formats, format); + if (plane_format == NULL) { wlr_log(WLR_ERROR, "Plane %"PRIu32" doesn't support format 0x%"PRIX32, plane->id, format); return false; } - struct wlr_drm_format_set *format_set = - with_modifiers ? &plane->formats : NULL; + const struct wlr_drm_format_set *render_formats = + wlr_renderer_get_dmabuf_render_formats(drm->renderer.wlr_rend); + if (render_formats == NULL) { + wlr_log(WLR_ERROR, "Failed to get render formats"); + return false; + } + const struct wlr_drm_format *render_format = + wlr_drm_format_set_get(render_formats, format); + if (render_format == NULL) { + wlr_log(WLR_ERROR, "Renderer doesn't support format 0x%"PRIX32, + format); + return false; + } + + struct wlr_drm_format *drm_format = NULL; + if (with_modifiers) { + drm_format = wlr_drm_format_intersect(plane_format, render_format); + if (drm_format == NULL) { + wlr_log(WLR_ERROR, + "Failed to intersect plane and render formats 0x%"PRIX32, + format); + return false; + } + } else { + drm_format = wlr_drm_format_create(format); + } + + struct wlr_drm_format *drm_format_linear = wlr_drm_format_create(format); + if (drm_format_linear == NULL) { + free(drm_format); + return false; + } + if (!wlr_drm_format_add(&drm_format_linear, DRM_FORMAT_MOD_LINEAR)) { + free(drm_format_linear); + free(drm_format); + return false; + } + + if (force_linear) { + free(drm_format); + drm_format = wlr_drm_format_dup(drm_format_linear); + } drm_plane_finish_surface(plane); + bool ok = true; if (!drm->parent) { - return init_drm_surface(&plane->surf, &drm->renderer, width, height, - format, format_set, flags | GBM_BO_USE_SCANOUT); + ok = init_drm_surface(&plane->surf, &drm->renderer, + width, height, drm_format); + } else { + ok = init_drm_surface(&plane->surf, &drm->parent->renderer, + width, height, drm_format_linear); + if (ok && !init_drm_surface(&plane->mgpu_surf, &drm->renderer, + width, height, drm_format)) { + finish_drm_surface(&plane->surf); + ok = false; + } } - if (!init_drm_surface(&plane->surf, &drm->parent->renderer, - width, height, format, NULL, - flags | GBM_BO_USE_LINEAR)) { - return false; - } + free(drm_format_linear); + free(drm_format); - if (!init_drm_surface(&plane->mgpu_surf, &drm->renderer, - width, height, format, format_set, - flags | GBM_BO_USE_SCANOUT)) { - finish_drm_surface(&plane->surf); - return false; - } - - return true; + return ok; } void drm_fb_clear(struct wlr_drm_fb *fb) { - switch (fb->type) { - case WLR_DRM_FB_TYPE_NONE: - assert(!fb->bo); - break; - case WLR_DRM_FB_TYPE_SURFACE: - gbm_surface_release_buffer(fb->surf->gbm, fb->bo); - break; - case WLR_DRM_FB_TYPE_WLR_BUFFER: - gbm_bo_destroy(fb->bo); - wlr_buffer_unlock(fb->wlr_buf); - fb->wlr_buf = NULL; - break; + if (!fb->bo) { + assert(!fb->wlr_buf); + return; } - fb->type = WLR_DRM_FB_TYPE_NONE; + gbm_bo_destroy(fb->bo); + wlr_buffer_unlock(fb->wlr_buf); + + fb->wlr_buf = NULL; fb->bo = NULL; if (fb->mgpu_bo) { assert(fb->mgpu_surf); - gbm_surface_release_buffer(fb->mgpu_surf->gbm, fb->mgpu_bo); + gbm_bo_destroy(fb->mgpu_bo); + wlr_buffer_unlock(fb->mgpu_wlr_buf); fb->mgpu_bo = NULL; + fb->mgpu_wlr_buf = NULL; fb->mgpu_surf = NULL; } } bool drm_fb_lock_surface(struct wlr_drm_fb *fb, struct wlr_drm_surface *surf) { - drm_fb_clear(fb); + assert(surf->back_buffer != NULL); - if (!wlr_egl_swap_buffers(&surf->renderer->egl, surf->egl, NULL)) { - wlr_log(WLR_ERROR, "Failed to swap buffers"); - return false; - } + struct wlr_buffer *buffer = wlr_buffer_lock(surf->back_buffer); - fb->bo = gbm_surface_lock_front_buffer(surf->gbm); - if (!fb->bo) { - wlr_log(WLR_ERROR, "Failed to lock front buffer"); - return false; - } + // Unset the current EGL context ASAP, because other operations may require + // making another context current. + drm_surface_unset_current(surf); - fb->type = WLR_DRM_FB_TYPE_SURFACE; - fb->surf = surf; - return true; + bool ok = drm_fb_import_wlr(fb, surf->renderer, buffer, NULL); + wlr_buffer_unlock(buffer); + return ok; } bool drm_fb_import_wlr(struct wlr_drm_fb *fb, struct wlr_drm_renderer *renderer, @@ -302,7 +342,7 @@ bool drm_fb_import_wlr(struct wlr_drm_fb *fb, struct wlr_drm_renderer *renderer, return false; } - if (!wlr_drm_format_set_has(set, attribs.format, attribs.modifier)) { + if (set && !wlr_drm_format_set_has(set, attribs.format, attribs.modifier)) { // The format isn't supported by the plane. Try stripping the alpha // channel, if any. uint32_t format = strip_alpha_channel(attribs.format); @@ -352,7 +392,6 @@ bool drm_fb_import_wlr(struct wlr_drm_fb *fb, struct wlr_drm_renderer *renderer, return false; } - fb->type = WLR_DRM_FB_TYPE_WLR_BUFFER; fb->wlr_buf = wlr_buffer_lock(buf); return true; @@ -413,17 +452,15 @@ struct gbm_bo *drm_fb_acquire(struct wlr_drm_fb *fb, struct wlr_drm_backend *drm wlr_render_texture_with_matrix(renderer, tex, mat, 1.0f); wlr_renderer_end(renderer); - if (!wlr_egl_swap_buffers(&mgpu->renderer->egl, mgpu->egl, NULL)) { - wlr_log(WLR_ERROR, "Failed to swap buffers"); - return NULL; + struct wlr_drm_fb mgpu_fb = { + .bo = fb->mgpu_bo, + .wlr_buf = fb->mgpu_wlr_buf, + }; + if (!drm_fb_lock_surface(&mgpu_fb, mgpu)) { + return false; } - - fb->mgpu_bo = gbm_surface_lock_front_buffer(mgpu->gbm); - if (!fb->mgpu_bo) { - wlr_log(WLR_ERROR, "Failed to lock front buffer"); - return NULL; - } - + fb->mgpu_bo = mgpu_fb.bo; + fb->mgpu_wlr_buf = mgpu_fb.wlr_buf; fb->mgpu_surf = mgpu; return fb->mgpu_bo; } diff --git a/backend/drm/util.c b/backend/drm/util.c index 86d66be42..b926e1187 100644 --- a/backend/drm/util.c +++ b/backend/drm/util.c @@ -221,7 +221,7 @@ uint32_t get_fb_for_bo(struct gbm_bo *bo, bool with_modifiers) { return id; } -static inline bool is_taken(size_t n, const uint32_t arr[static n], uint32_t key) { +static bool is_taken(size_t n, const uint32_t arr[static n], uint32_t key) { for (size_t i = 0; i < n; ++i) { if (arr[i] == key) { return true; diff --git a/backend/headless/backend.c b/backend/headless/backend.c index 7758ece49..fe893ddf1 100644 --- a/backend/headless/backend.c +++ b/backend/headless/backend.c @@ -1,12 +1,20 @@ +#define _POSIX_C_SOURCE 200809L #include -#include -#include +#include +#include #include +#include #include #include #include +#include +#include #include +#include #include "backend/headless.h" +#include "render/drm_format_set.h" +#include "render/gbm_allocator.h" +#include "render/wlr_renderer.h" #include "util/signal.h" struct wlr_headless_backend *headless_backend_from_backend( @@ -62,10 +70,12 @@ static void backend_destroy(struct wlr_backend *wlr_backend) { wlr_signal_emit_safe(&wlr_backend->events.destroy, backend); + free(backend->format); if (backend->egl == &backend->priv_egl) { wlr_renderer_destroy(backend->renderer); wlr_egl_finish(&backend->priv_egl); } + wlr_allocator_destroy(backend->allocator); free(backend); } @@ -95,26 +105,30 @@ static void handle_renderer_destroy(struct wl_listener *listener, void *data) { } static bool backend_init(struct wlr_headless_backend *backend, - struct wl_display *display, struct wlr_renderer *renderer) { + struct wl_display *display, struct wlr_allocator *allocator, + struct wlr_renderer *renderer) { wlr_backend_init(&backend->backend, &backend_impl); backend->display = display; wl_list_init(&backend->outputs); wl_list_init(&backend->input_devices); + backend->allocator = allocator; backend->renderer = renderer; backend->egl = wlr_gles2_renderer_get_egl(renderer); - if (wlr_gles2_renderer_check_ext(backend->renderer, "GL_OES_rgb8_rgba8") || - wlr_gles2_renderer_check_ext(backend->renderer, - "GL_OES_required_internalformat") || - wlr_gles2_renderer_check_ext(backend->renderer, "GL_ARM_rgba8")) { - backend->internal_format = GL_RGBA8_OES; - } else { - wlr_log(WLR_INFO, "GL_RGBA8_OES not supported, " - "falling back to GL_RGBA4 internal format " - "(performance may be affected)"); - backend->internal_format = GL_RGBA4; + const struct wlr_drm_format_set *formats = + wlr_renderer_get_dmabuf_render_formats(backend->renderer); + if (formats == NULL) { + wlr_log(WLR_ERROR, "Failed to get available DMA-BUF formats from renderer"); + return false; } + const struct wlr_drm_format *format = + wlr_drm_format_set_get(formats, DRM_FORMAT_XRGB8888); + if (format == NULL) { + wlr_log(WLR_ERROR, "Renderer doesn't support XRGB8888"); + return false; + } + backend->format = wlr_drm_format_dup(format); backend->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &backend->display_destroy); @@ -124,68 +138,144 @@ static bool backend_init(struct wlr_headless_backend *backend, return true; } +static int open_drm_render_node(void) { + uint32_t flags = 0; + int devices_len = drmGetDevices2(flags, NULL, 0); + if (devices_len < 0) { + wlr_log(WLR_ERROR, "drmGetDevices2 failed"); + return -1; + } + drmDevice **devices = calloc(devices_len, sizeof(drmDevice *)); + if (devices == NULL) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + return -1; + } + devices_len = drmGetDevices2(flags, devices, devices_len); + if (devices_len < 0) { + free(devices); + wlr_log(WLR_ERROR, "drmGetDevices2 failed"); + return -1; + } + + int fd = -1; + for (int i = 0; i < devices_len; i++) { + drmDevice *dev = devices[i]; + if (dev->available_nodes & (1 << DRM_NODE_RENDER)) { + const char *name = dev->nodes[DRM_NODE_RENDER]; + wlr_log(WLR_DEBUG, "Opening DRM render node '%s'", name); + fd = open(name, O_RDWR | O_CLOEXEC); + if (fd < 0) { + wlr_log_errno(WLR_ERROR, "Failed to open '%s'", name); + goto out; + } + break; + } + } + if (fd < 0) { + wlr_log(WLR_ERROR, "Failed to find any DRM render node"); + } + +out: + for (int i = 0; i < devices_len; i++) { + drmFreeDevice(&devices[i]); + } + free(devices); + + return fd; +} + struct wlr_backend *wlr_headless_backend_create(struct wl_display *display, wlr_renderer_create_func_t create_renderer_func) { wlr_log(WLR_INFO, "Creating headless backend"); + int drm_fd = open_drm_render_node(); + if (drm_fd < 0) { + wlr_log(WLR_ERROR, "Failed to open DRM render node"); + return NULL; + } + + struct wlr_gbm_allocator *gbm_alloc = wlr_gbm_allocator_create(drm_fd); + if (gbm_alloc == NULL) { + wlr_log(WLR_ERROR, "Failed to create GBM allocator"); + return false; + } + struct wlr_headless_backend *backend = calloc(1, sizeof(struct wlr_headless_backend)); if (!backend) { wlr_log(WLR_ERROR, "Failed to allocate wlr_headless_backend"); - return NULL; + goto error_backend; } - static const EGLint config_attribs[] = { - EGL_SURFACE_TYPE, 0, - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL_BLUE_SIZE, 1, - EGL_GREEN_SIZE, 1, - EGL_RED_SIZE, 1, - EGL_NONE, - }; - if (!create_renderer_func) { create_renderer_func = wlr_renderer_autocreate; } struct wlr_renderer *renderer = create_renderer_func(&backend->priv_egl, - EGL_PLATFORM_SURFACELESS_MESA, EGL_DEFAULT_DISPLAY, - (EGLint*)config_attribs, 0); + EGL_PLATFORM_GBM_KHR, gbm_alloc->gbm_device, NULL, 0); if (!renderer) { wlr_log(WLR_ERROR, "Failed to create renderer"); - free(backend); - return NULL; + goto error_renderer; } - if (!backend_init(backend, display, renderer)) { - wlr_renderer_destroy(backend->renderer); - free(backend); - return NULL; + if (!backend_init(backend, display, &gbm_alloc->base, renderer)) { + goto error_init; } return &backend->backend; + +error_init: + wlr_renderer_destroy(renderer); +error_renderer: + free(backend); +error_backend: + wlr_allocator_destroy(&gbm_alloc->base); + return NULL; } struct wlr_backend *wlr_headless_backend_create_with_renderer( struct wl_display *display, struct wlr_renderer *renderer) { - wlr_log(WLR_INFO, "Creating headless backend"); + wlr_log(WLR_INFO, "Creating headless backend with parent renderer"); + + int drm_fd = wlr_renderer_get_drm_fd(renderer); + if (drm_fd < 0) { + wlr_log(WLR_ERROR, "Failed to get DRM device FD from renderer"); + return false; + } + + drm_fd = fcntl(drm_fd, F_DUPFD_CLOEXEC, 0); + if (drm_fd < 0) { + wlr_log_errno(WLR_ERROR, "fcntl(F_DUPFD_CLOEXEC) failed"); + return false; + } + + struct wlr_gbm_allocator *gbm_alloc = wlr_gbm_allocator_create(drm_fd); + if (gbm_alloc == NULL) { + wlr_log(WLR_ERROR, "Failed to create GBM allocator"); + return false; + } struct wlr_headless_backend *backend = calloc(1, sizeof(struct wlr_headless_backend)); if (!backend) { wlr_log(WLR_ERROR, "Failed to allocate wlr_headless_backend"); - return NULL; + goto error_backend; } - if (!backend_init(backend, display, renderer)) { - free(backend); - return NULL; + if (!backend_init(backend, display, &gbm_alloc->base, renderer)) { + goto error_init; } backend->renderer_destroy.notify = handle_renderer_destroy; wl_signal_add(&renderer->events.destroy, &backend->renderer_destroy); return &backend->backend; + +error_init: + free(backend); +error_backend: + wlr_allocator_destroy(&gbm_alloc->base); + return NULL; } bool wlr_backend_is_headless(struct wlr_backend *backend) { diff --git a/backend/headless/output.c b/backend/headless/output.c index 32c2a7fde..bb15c23d5 100644 --- a/backend/headless/output.c +++ b/backend/headless/output.c @@ -1,11 +1,12 @@ #include -#include -#include #include +#include #include #include #include #include "backend/headless.h" +#include "render/swapchain.h" +#include "render/wlr_renderer.h" #include "util/signal.h" static struct wlr_headless_output *headless_output_from_output( @@ -14,53 +15,6 @@ static struct wlr_headless_output *headless_output_from_output( return (struct wlr_headless_output *)wlr_output; } -static bool create_fbo(struct wlr_headless_output *output, - unsigned int width, unsigned int height) { - if (!wlr_egl_make_current(output->backend->egl, EGL_NO_SURFACE, NULL)) { - return false; - } - - GLuint rbo; - glGenRenderbuffers(1, &rbo); - glBindRenderbuffer(GL_RENDERBUFFER, rbo); - glRenderbufferStorage(GL_RENDERBUFFER, output->backend->internal_format, - width, height); - glBindRenderbuffer(GL_RENDERBUFFER, 0); - - GLuint fbo; - glGenFramebuffers(1, &fbo); - glBindFramebuffer(GL_FRAMEBUFFER, fbo); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_RENDERBUFFER, rbo); - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - - wlr_egl_unset_current(output->backend->egl); - - if (status != GL_FRAMEBUFFER_COMPLETE) { - wlr_log(WLR_ERROR, "Failed to create FBO"); - return false; - } - - output->fbo = fbo; - output->rbo = rbo; - return true; -} - -static void destroy_fbo(struct wlr_headless_output *output) { - if (!wlr_egl_make_current(output->backend->egl, EGL_NO_SURFACE, NULL)) { - return; - } - - glDeleteFramebuffers(1, &output->fbo); - glDeleteRenderbuffers(1, &output->rbo); - - wlr_egl_unset_current(output->backend->egl); - - output->fbo = 0; - output->rbo = 0; -} - static bool output_set_custom_mode(struct wlr_output *wlr_output, int32_t width, int32_t height, int32_t refresh) { struct wlr_headless_output *output = @@ -70,8 +24,10 @@ static bool output_set_custom_mode(struct wlr_output *wlr_output, int32_t width, refresh = HEADLESS_DEFAULT_REFRESH; } - destroy_fbo(output); - if (!create_fbo(output, width, height)) { + wlr_swapchain_destroy(output->swapchain); + output->swapchain = wlr_swapchain_create(output->backend->allocator, + width, height, output->backend->format); + if (!output->swapchain) { wlr_output_destroy(wlr_output); return false; } @@ -87,15 +43,20 @@ static bool output_attach_render(struct wlr_output *wlr_output, struct wlr_headless_output *output = headless_output_from_output(wlr_output); - if (!wlr_egl_make_current(output->backend->egl, EGL_NO_SURFACE, NULL)) { + wlr_buffer_unlock(output->back_buffer); + output->back_buffer = wlr_swapchain_acquire(output->swapchain, buffer_age); + if (!output->back_buffer) { return false; } - glBindFramebuffer(GL_FRAMEBUFFER, output->fbo); - - if (buffer_age != NULL) { - *buffer_age = 0; // We only have one buffer + if (!wlr_egl_make_current(output->backend->egl, EGL_NO_SURFACE, NULL)) { + return false; } + if (!wlr_renderer_bind_buffer(output->backend->renderer, + output->back_buffer)) { + return false; + } + return true; } @@ -130,10 +91,28 @@ static bool output_commit(struct wlr_output *wlr_output) { } if (wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER) { - glBindFramebuffer(GL_FRAMEBUFFER, 0); - wlr_egl_unset_current(output->backend->egl); + struct wlr_buffer *buffer = NULL; + switch (wlr_output->pending.buffer_type) { + case WLR_OUTPUT_STATE_BUFFER_RENDER: + assert(output->back_buffer != NULL); + + wlr_renderer_bind_buffer(output->backend->renderer, NULL); + wlr_egl_unset_current(output->backend->egl); + + buffer = output->back_buffer; + output->back_buffer = NULL; + break; + case WLR_OUTPUT_STATE_BUFFER_SCANOUT: + buffer = wlr_buffer_lock(wlr_output->pending.buffer); + break; + } + assert(buffer != NULL); + + wlr_buffer_unlock(output->front_buffer); + output->front_buffer = buffer; + + wlr_swapchain_set_buffer_submitted(output->swapchain, buffer); - // Nothing needs to be done for FBOs wlr_output_send_present(wlr_output, NULL); } @@ -144,8 +123,29 @@ static void output_rollback_render(struct wlr_output *wlr_output) { struct wlr_headless_output *output = headless_output_from_output(wlr_output); assert(wlr_egl_is_current(output->backend->egl)); - glBindFramebuffer(GL_FRAMEBUFFER, 0); + + wlr_renderer_bind_buffer(output->backend->renderer, NULL); wlr_egl_unset_current(output->backend->egl); + + wlr_buffer_unlock(output->back_buffer); + output->back_buffer = NULL; +} + +static bool output_export_dmabuf(struct wlr_output *wlr_output, + struct wlr_dmabuf_attributes *attribs) { + struct wlr_headless_output *output = + headless_output_from_output(wlr_output); + + if (!output->front_buffer) { + return false; + } + + struct wlr_dmabuf_attributes tmp; + if (!wlr_buffer_get_dmabuf(output->front_buffer, &tmp)) { + return false; + } + + return wlr_dmabuf_attributes_copy(attribs, &tmp); } static void output_destroy(struct wlr_output *wlr_output) { @@ -153,7 +153,9 @@ static void output_destroy(struct wlr_output *wlr_output) { headless_output_from_output(wlr_output); wl_list_remove(&output->link); wl_event_source_remove(output->frame_timer); - destroy_fbo(output); + wlr_swapchain_destroy(output->swapchain); + wlr_buffer_unlock(output->back_buffer); + wlr_buffer_unlock(output->front_buffer); free(output); } @@ -162,6 +164,7 @@ static const struct wlr_output_impl output_impl = { .attach_render = output_attach_render, .commit = output_commit, .rollback_render = output_rollback_render, + .export_dmabuf = output_export_dmabuf, }; bool wlr_output_is_headless(struct wlr_output *wlr_output) { @@ -191,7 +194,9 @@ struct wlr_output *wlr_headless_add_output(struct wlr_backend *wlr_backend, backend->display); struct wlr_output *wlr_output = &output->wlr_output; - if (!create_fbo(output, width, height)) { + output->swapchain = wlr_swapchain_create(backend->allocator, + width, height, backend->format); + if (!output->swapchain) { goto error; } @@ -206,14 +211,6 @@ struct wlr_output *wlr_headless_add_output(struct wlr_backend *wlr_backend, "Headless output %zd", backend->last_output_num); wlr_output_set_description(wlr_output, description); - if (!output_attach_render(wlr_output, NULL)) { - goto error; - } - - wlr_renderer_begin(backend->renderer, wlr_output->width, wlr_output->height); - wlr_renderer_clear(backend->renderer, (float[]){ 1.0, 1.0, 1.0, 1.0 }); - wlr_renderer_end(backend->renderer); - struct wl_event_loop *ev = wl_display_get_event_loop(backend->display); output->frame_timer = wl_event_loop_add_timer(ev, signal_frame, output); diff --git a/backend/libinput/backend.c b/backend/libinput/backend.c index 87497b11d..e31ecf066 100644 --- a/backend/libinput/backend.c +++ b/backend/libinput/backend.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -16,12 +17,27 @@ static struct wlr_libinput_backend *get_libinput_backend_from_backend( static int libinput_open_restricted(const char *path, int flags, void *_backend) { struct wlr_libinput_backend *backend = _backend; - return wlr_session_open_file(backend->session, path); + struct wlr_device *dev = wlr_session_open_file(backend->session, path); + if (dev == NULL) { + return -1; + } + return dev->fd; } static void libinput_close_restricted(int fd, void *_backend) { struct wlr_libinput_backend *backend = _backend; - wlr_session_close_file(backend->session, fd); + + struct wlr_device *dev; + bool found = false; + wl_list_for_each(dev, &backend->session->devices, link) { + if (dev->fd == fd) { + found = true; + break; + } + } + if (found) { + wlr_session_close_file(backend->session, dev); + } } static const struct libinput_interface libinput_impl = { @@ -44,9 +60,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) { @@ -144,7 +175,7 @@ bool wlr_backend_is_libinput(struct wlr_backend *b) { static void session_signal(struct wl_listener *listener, void *data) { struct wlr_libinput_backend *backend = wl_container_of(listener, backend, session_signal); - struct wlr_session *session = data; + struct wlr_session *session = backend->session; if (!backend->libinput_context) { return; @@ -188,7 +219,7 @@ struct wlr_backend *wlr_libinput_backend_create(struct wl_display *display, backend->display = display; backend->session_signal.notify = session_signal; - wl_signal_add(&session->session_signal, &backend->session_signal); + wl_signal_add(&session->events.active, &backend->session_signal); backend->session_destroy.notify = handle_session_destroy; wl_signal_add(&session->events.destroy, &backend->session_destroy); diff --git a/backend/libinput/keyboard.c b/backend/libinput/keyboard.c index 93605e778..d5207f542 100644 --- a/backend/libinput/keyboard.c +++ b/backend/libinput/keyboard.c @@ -72,10 +72,10 @@ void handle_keyboard_key(struct libinput_event *event, libinput_event_keyboard_get_key_state(kbevent); switch (state) { case LIBINPUT_KEY_STATE_RELEASED: - wlr_event.state = WLR_KEY_RELEASED; + wlr_event.state = WL_KEYBOARD_KEY_STATE_RELEASED; break; case LIBINPUT_KEY_STATE_PRESSED: - wlr_event.state = WLR_KEY_PRESSED; + wlr_event.state = WL_KEYBOARD_KEY_STATE_PRESSED; break; } wlr_event.update_state = true; diff --git a/backend/libinput/tablet_tool.c b/backend/libinput/tablet_tool.c index 845ef2209..e07491d1b 100644 --- a/backend/libinput/tablet_tool.c +++ b/backend/libinput/tablet_tool.c @@ -113,10 +113,8 @@ static enum wlr_tablet_tool_type wlr_type_from_libinput_type( return WLR_TABLET_TOOL_TYPE_MOUSE; case LIBINPUT_TABLET_TOOL_TYPE_LENS: return WLR_TABLET_TOOL_TYPE_LENS; -#if LIBINPUT_MINOR >= 14 case LIBINPUT_TABLET_TOOL_TYPE_TOTEM: return WLR_TABLET_TOOL_TYPE_TOTEM; -#endif } abort(); // unreachable } diff --git a/backend/session/direct-freebsd.c b/backend/session/direct-freebsd.c index c6e1f7204..302b0ff42 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; @@ -114,7 +115,7 @@ static int vt_handler(int signo, void *data) { if (session->base.active) { session->base.active = false; - wlr_signal_emit_safe(&session->base.session_signal, session); + wlr_signal_emit_safe(&session->base.events.active, NULL); wl_list_for_each(dev, &session->base.devices, link) { if (ioctl(dev->fd, DRM_IOCTL_VERSION, &dv) == 0) { @@ -133,7 +134,7 @@ static int vt_handler(int signo, void *data) { } session->base.active = true; - wlr_signal_emit_safe(&session->base.session_signal, session); + wlr_signal_emit_safe(&session->base.events.active, NULL); } return 1; @@ -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..b6d9f06af 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 }; @@ -125,7 +126,7 @@ static int vt_handler(int signo, void *data) { if (session->base.active) { session->base.active = false; - wlr_signal_emit_safe(&session->base.session_signal, session); + wlr_signal_emit_safe(&session->base.events.active, NULL); struct wlr_device *dev; wl_list_for_each(dev, &session->base.devices, link) { @@ -148,7 +149,7 @@ static int vt_handler(int signo, void *data) { } session->base.active = true; - wlr_signal_emit_safe(&session->base.session_signal, session); + wlr_signal_emit_safe(&session->base.events.active, NULL); } return 1; @@ -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..ccbc77bdd --- /dev/null +++ b/backend/session/libseat.c @@ -0,0 +1,218 @@ +#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.events.active, NULL); +} + +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.events.active, NULL); + 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); + + // libseat will take care of updating the logind state if necessary + setenv("XDG_SESSION_TYPE", "wayland", 1); + + 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) == 0; +} + +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..0cef13560 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 @@ -35,9 +36,7 @@ struct logind_session { char *id; char *path; - char *seat_path; - bool can_graphical; // specifies whether a drm device was taken // if so, the session will be (de)activated with the drm fd, // otherwise with the dbus PropertiesChanged on "active" signal @@ -129,8 +128,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 +141,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); @@ -178,34 +177,6 @@ out: return ret >= 0; } -static bool find_seat_path(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", - "/org/freedesktop/login1", "org.freedesktop.login1.Manager", - "GetSeat", &error, &msg, "s", session->base.seat); - if (ret < 0) { - wlr_log(WLR_ERROR, "Failed to get seat path: %s", error.message); - goto out; - } - - const char *path; - ret = sd_bus_message_read(msg, "o", &path); - if (ret < 0) { - wlr_log(WLR_ERROR, "Could not parse seat path: %s", error.message); - goto out; - } - session->seat_path = strdup(path); - -out: - sd_bus_error_free(&error); - sd_bus_message_unref(msg); - - return ret >= 0; -} - static bool session_activate(struct logind_session *session) { int ret; sd_bus_message *msg = NULL; @@ -241,6 +212,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; @@ -267,7 +264,6 @@ static void logind_session_destroy(struct wlr_session *base) { sd_bus_unref(session->bus); free(session->id); free(session->path); - free(session->seat_path); free(session); } @@ -310,7 +306,7 @@ static int pause_device(sd_bus_message *msg, void *userdata, if (major == DRM_MAJOR && strcmp(type, "gone") != 0) { assert(session->has_drm); session->base.active = false; - wlr_signal_emit_safe(&session->base.session_signal, session); + wlr_signal_emit_safe(&session->base.events.active, NULL); } if (strcmp(type, "pause") == 0) { @@ -352,7 +348,7 @@ static int resume_device(sd_bus_message *msg, void *userdata, if (!session->base.active) { session->base.active = true; - wlr_signal_emit_safe(&session->base.session_signal, session); + wlr_signal_emit_safe(&session->base.events.active, NULL); } } @@ -411,7 +407,7 @@ static int session_properties_changed(sd_bus_message *msg, void *userdata, if (session->base.active != active) { session->base.active = active; - wlr_signal_emit_safe(&session->base.session_signal, session); + wlr_signal_emit_safe(&session->base.events.active, NULL); } return 0; } else { @@ -451,7 +447,7 @@ static int session_properties_changed(sd_bus_message *msg, void *userdata, if (session->base.active != active) { session->base.active = active; - wlr_signal_emit_safe(&session->base.session_signal, session); + wlr_signal_emit_safe(&session->base.events.active, NULL); } return 0; } @@ -469,95 +465,6 @@ error: return 0; } -static int seat_properties_changed(sd_bus_message *msg, void *userdata, - sd_bus_error *ret_error) { - struct logind_session *session = userdata; - int ret = 0; - - // if we have a drm fd we don't depend on this - if (session->has_drm) { - return 0; - } - - // PropertiesChanged arg 1: interface - const char *interface; - ret = sd_bus_message_read_basic(msg, 's', &interface); // skip path - if (ret < 0) { - goto error; - } - - if (strcmp(interface, "org.freedesktop.login1.Seat") != 0) { - // not interesting for us; ignore - wlr_log(WLR_DEBUG, "ignoring PropertiesChanged from %s", interface); - return 0; - } - - // PropertiesChanged arg 2: changed properties with values - ret = sd_bus_message_enter_container(msg, 'a', "{sv}"); - if (ret < 0) { - goto error; - } - - const char *s; - while ((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) { - ret = sd_bus_message_read_basic(msg, 's', &s); - if (ret < 0) { - goto error; - } - - if (strcmp(s, "CanGraphical") == 0) { - int ret; - ret = sd_bus_message_enter_container(msg, 'v', "b"); - if (ret < 0) { - goto error; - } - - ret = sd_bus_message_read_basic(msg, 'b', &session->can_graphical); - if (ret < 0) { - goto error; - } - - return 0; - } else { - sd_bus_message_skip(msg, "{sv}"); - } - - ret = sd_bus_message_exit_container(msg); - if (ret < 0) { - goto error; - } - } - - if (ret < 0) { - goto error; - } - - ret = sd_bus_message_exit_container(msg); - if (ret < 0) { - goto error; - } - - // PropertiesChanged arg 3: changed properties without values - sd_bus_message_enter_container(msg, 'a', "s"); - while ((ret = sd_bus_message_read_basic(msg, 's', &s)) > 0) { - if (strcmp(s, "CanGraphical") == 0) { - session->can_graphical = sd_seat_can_graphical(session->base.seat); - return 0; - } - } - - if (ret < 0) { - goto error; - } - - return 0; - -error: - wlr_log(WLR_ERROR, "Failed to parse D-Bus PropertiesChanged: %s", - strerror(-ret)); - return 0; -} - static bool add_signal_matches(struct logind_session *session) { static const char *logind = "org.freedesktop.login1"; static const char *logind_path = "/org/freedesktop/login1"; @@ -595,14 +502,6 @@ static bool add_signal_matches(struct logind_session *session) { return false; } - ret = sd_bus_match_signal(session->bus, NULL, logind, session->seat_path, - property_interface, "PropertiesChanged", - seat_properties_changed, session); - if (ret < 0) { - wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret)); - return false; - } - return true; } @@ -670,6 +569,8 @@ static bool get_display_session(char **session_id) { char *xdg_session_id = getenv("XDG_SESSION_ID"); if (xdg_session_id) { + wlr_log(WLR_INFO, "Selecting session from XDG_SESSION_ID: %s", xdg_session_id); + // This just checks whether the supplied session ID is valid if (sd_session_is_active(xdg_session_id) < 0) { wlr_log(WLR_ERROR, "Invalid XDG_SESSION_ID: '%s'", xdg_session_id); @@ -750,6 +651,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 +665,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"); @@ -782,12 +685,6 @@ static struct wlr_session *logind_session_create(struct wl_display *disp) { goto error; } - if (!find_seat_path(session)) { - sd_bus_unref(session->bus); - free(session->path); - goto error; - } - if (!add_signal_matches(session)) { goto error_bus; } @@ -804,30 +701,18 @@ static struct wlr_session *logind_session_create(struct wl_display *disp) { goto error_bus; } - // Check for CanGraphical first - session->can_graphical = sd_seat_can_graphical(session->base.seat); - if (!session->can_graphical) { - wlr_log(WLR_INFO, "Waiting for 'CanGraphical' on seat %s", session->base.seat); - } - - while (!session->can_graphical) { - ret = wl_event_loop_dispatch(event_loop, -1); - if (ret < 0) { - wlr_log(WLR_ERROR, "Polling error waiting for 'CanGraphical' on seat %s", - session->base.seat); - goto error_bus; - } - } + 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: sd_bus_unref(session->bus); free(session->path); - free(session->seat_path); error: free(session->id); 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..b58bd9679 100644 --- a/backend/session/session.c +++ b/backend/session/session.c @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include #include #include @@ -13,13 +15,20 @@ #include #include #include +#include "backend/session/session.h" #include "util/signal.h" +#define WAIT_GPU_TIMEOUT 10000 // ms + +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 @@ -27,6 +36,19 @@ static const struct session_impl *impls[] = { NULL, }; +static bool is_drm_card(const char *sysname) { + const char prefix[] = "card"; + if (strncmp(sysname, prefix, strlen(prefix)) != 0) { + return false; + } + for (size_t i = strlen(prefix); sysname[i] != '\0'; i++) { + if (sysname[i] < '0' || sysname[i] > '9') { + return false; + } + } + return true; +} + static int udev_event(int fd, uint32_t mask, void *data) { struct wlr_session *session = data; @@ -35,22 +57,38 @@ static int udev_event(int fd, uint32_t mask, void *data) { return 1; } + const char *sysname = udev_device_get_sysname(udev_dev); + const char *devnode = udev_device_get_devnode(udev_dev); const char *action = udev_device_get_action(udev_dev); + wlr_log(WLR_DEBUG, "udev event for %s (%s)", sysname, action); - wlr_log(WLR_DEBUG, "udev event for %s (%s)", - udev_device_get_sysname(udev_dev), action); - - if (!action || strcmp(action, "change") != 0) { + if (!is_drm_card(sysname) || !action || !devnode) { goto out; } - dev_t devnum = udev_device_get_devnum(udev_dev); - struct wlr_device *dev; + const char *seat = udev_device_get_property_value(udev_dev, "ID_SEAT"); + if (!seat) { + seat = "seat0"; + } + if (session->seat[0] != '\0' && strcmp(session->seat, seat) != 0) { + goto out; + } - wl_list_for_each(dev, &session->devices, link) { - if (dev->dev == devnum) { - wlr_signal_emit_safe(&dev->signal, session); - break; + if (strcmp(action, "add") == 0) { + wlr_log(WLR_DEBUG, "DRM device %s added", sysname); + struct wlr_session_add_event event = { + .path = devnode, + }; + wlr_signal_emit_safe(&session->events.add_drm_card, &event); + } else if (strcmp(action, "change") == 0) { + dev_t devnum = udev_device_get_devnum(udev_dev); + struct wlr_device *dev; + wl_list_for_each(dev, &session->devices, link) { + if (dev->dev == devnum) { + wlr_log(WLR_DEBUG, "DRM device %s changed", sysname); + wlr_signal_emit_safe(&dev->events.change, NULL); + break; + } } } @@ -65,12 +103,25 @@ 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->events.active); + wl_signal_init(&session->events.add_drm_card); + 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 +148,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"); @@ -127,6 +173,8 @@ struct wlr_session *wlr_session_create(struct wl_display *disp) { goto error_mon; } + session->display = disp; + session->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(disp, &session->display_destroy); @@ -156,10 +204,11 @@ void wlr_session_destroy(struct wlr_session *session) { session->impl->destroy(session); } -int wlr_session_open_file(struct wlr_session *session, const char *path) { +struct wlr_device *wlr_session_open_file(struct wlr_session *session, + const char *path) { int fd = session->impl->open(session, path); if (fd < 0) { - return fd; + return NULL; } struct wlr_device *dev = malloc(sizeof(*dev)); @@ -176,45 +225,24 @@ int wlr_session_open_file(struct wlr_session *session, const char *path) { dev->fd = fd; dev->dev = st.st_rdev; - wl_signal_init(&dev->signal); + wl_signal_init(&dev->events.change); wl_list_insert(&session->devices, &dev->link); - return fd; + return dev; error: free(dev); - return fd; -} - -static struct wlr_device *find_device(struct wlr_session *session, int fd) { - struct wlr_device *dev; - - wl_list_for_each(dev, &session->devices, link) { - if (dev->fd == fd) { - return dev; - } - } - - wlr_log(WLR_ERROR, "Tried to use fd %d not opened by session", fd); - assert(0); + close(fd); return NULL; } -void wlr_session_close_file(struct wlr_session *session, int fd) { - struct wlr_device *dev = find_device(session, fd); - - session->impl->close(session, fd); +void wlr_session_close_file(struct wlr_session *session, + struct wlr_device *dev) { + session->impl->close(session, dev->fd); wl_list_remove(&dev->link); free(dev); } -void wlr_session_signal_add(struct wlr_session *session, int fd, - struct wl_listener *listener) { - struct wlr_device *dev = find_device(session, fd); - - wl_signal_add(&dev->signal, listener); -} - bool wlr_session_change_vt(struct wlr_session *session, unsigned vt) { if (!session) { return false; @@ -226,32 +254,32 @@ bool wlr_session_change_vt(struct wlr_session *session, unsigned vt) { /* Tests if 'path' is KMS compatible by trying to open it. * It leaves the open device in *fd_out it it succeeds. */ -static int open_if_kms(struct wlr_session *restrict session, +static struct wlr_device *open_if_kms(struct wlr_session *restrict session, const char *restrict path) { if (!path) { - return -1; + return NULL; } - int fd = wlr_session_open_file(session, path); - if (fd < 0) { - return -1; + struct wlr_device *dev = wlr_session_open_file(session, path); + if (!dev) { + return NULL; } - drmVersion *ver = drmGetVersion(fd); + drmVersion *ver = drmGetVersion(dev->fd); if (!ver) { - goto out_fd; + goto out_dev; } drmFreeVersion(ver); - return fd; + return dev; -out_fd: - wlr_session_close_file(session, fd); - return -1; +out_dev: + wlr_session_close_file(session, dev); + return NULL; } static size_t explicit_find_gpus(struct wlr_session *session, - size_t ret_len, int ret[static ret_len], const char *str) { + size_t ret_len, struct wlr_device *ret[static ret_len], const char *str) { char *gpus = strdup(str); if (!gpus) { wlr_log_errno(WLR_ERROR, "Allocation failed"); @@ -267,7 +295,7 @@ static size_t explicit_find_gpus(struct wlr_session *session, } ret[i] = open_if_kms(session, ptr); - if (ret[i] < 0) { + if (!ret[i]) { wlr_log(WLR_ERROR, "Unable to open %s as DRM device", ptr); } else { ++i; @@ -278,25 +306,92 @@ static size_t explicit_find_gpus(struct wlr_session *session, return i; } +static struct udev_enumerate *enumerate_drm_cards(struct udev *udev) { + struct udev_enumerate *en = udev_enumerate_new(udev); + if (!en) { + wlr_log(WLR_ERROR, "udev_enumerate_new failed"); + return NULL; + } + + udev_enumerate_add_match_subsystem(en, "drm"); + udev_enumerate_add_match_sysname(en, "card[0-9]*"); + + if (udev_enumerate_scan_devices(en) != 0) { + wlr_log(WLR_ERROR, "udev_enumerate_scan_devices failed"); + udev_enumerate_unref(en); + return NULL; + } + + return en; +} + +static uint64_t get_current_time_ms(void) { + struct timespec ts = {0}; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000; +} + +struct find_gpus_add_handler { + bool added; + struct wl_listener listener; +}; + +static void find_gpus_handle_add(struct wl_listener *listener, void *data) { + struct find_gpus_add_handler *handler = + wl_container_of(listener, handler, listener); + handler->added = true; +} + /* Tries to find the primary GPU by checking for the "boot_vga" attribute. * If it's not found, it returns the first valid GPU it finds. */ size_t wlr_session_find_gpus(struct wlr_session *session, - size_t ret_len, int *ret) { + size_t ret_len, struct wlr_device **ret) { const char *explicit = getenv("WLR_DRM_DEVICES"); if (explicit) { return explicit_find_gpus(session, ret_len, ret, explicit); } - struct udev_enumerate *en = udev_enumerate_new(session->udev); + struct udev_enumerate *en = enumerate_drm_cards(session->udev); if (!en) { - wlr_log(WLR_ERROR, "Failed to create udev enumeration"); return -1; } - udev_enumerate_add_match_subsystem(en, "drm"); - udev_enumerate_add_match_sysname(en, "card[0-9]*"); - udev_enumerate_scan_devices(en); + if (udev_enumerate_get_list_entry(en) == NULL) { + udev_enumerate_unref(en); + wlr_log(WLR_INFO, "Waiting for a DRM card device"); + + struct find_gpus_add_handler handler = {0}; + handler.listener.notify = find_gpus_handle_add; + wl_signal_add(&session->events.add_drm_card, &handler.listener); + + uint64_t started_at = get_current_time_ms(); + uint64_t timeout = WAIT_GPU_TIMEOUT; + struct wl_event_loop *event_loop = + wl_display_get_event_loop(session->display); + while (!handler.added) { + int ret = wl_event_loop_dispatch(event_loop, (int)timeout); + if (ret < 0) { + wlr_log_errno(WLR_ERROR, "Failed to wait for DRM card device: " + "wl_event_loop_dispatch failed"); + udev_enumerate_unref(en); + return -1; + } + + uint64_t now = get_current_time_ms(); + if (now >= started_at + WAIT_GPU_TIMEOUT) { + break; + } + timeout = started_at + WAIT_GPU_TIMEOUT - now; + } + + wl_list_remove(&handler.listener.link); + + en = enumerate_drm_cards(session->udev); + if (!en) { + return -1; + } + } struct udev_list_entry *entry; size_t i = 0; @@ -334,17 +429,18 @@ size_t wlr_session_find_gpus(struct wlr_session *session, } } - int fd = open_if_kms(session, udev_device_get_devnode(dev)); - if (fd < 0) { + struct wlr_device *wlr_dev = + open_if_kms(session, udev_device_get_devnode(dev)); + if (!wlr_dev) { udev_device_unref(dev); continue; } udev_device_unref(dev); - ret[i] = fd; + ret[i] = wlr_dev; if (is_boot_vga) { - int tmp = ret[0]; + struct wlr_device *tmp = ret[0]; ret[0] = ret[i]; ret[i] = tmp; } diff --git a/backend/wayland/backend.c b/backend/wayland/backend.c index 8de63acf7..11b769bf9 100644 --- a/backend/wayland/backend.c +++ b/backend/wayland/backend.c @@ -1,7 +1,10 @@ +#define _POSIX_C_SOURCE 200809L #include +#include #include #include #include +#include #include @@ -16,7 +19,11 @@ #include #include "backend/wayland.h" +#include "render/drm_format_set.h" +#include "render/gbm_allocator.h" +#include "render/wlr_renderer.h" #include "util/signal.h" + #include "linux-dmabuf-unstable-v1-client-protocol.h" #include "pointer-gestures-unstable-v1-client-protocol.h" #include "presentation-time-client-protocol.h" @@ -97,15 +104,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 +164,15 @@ 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_list_for_each(seat, &wl->seats, link) { + if (seat->keyboard) { + create_wl_keyboard(seat); + } - 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); + } } for (size_t i = 0; i < wl->requested_outputs; ++i) { @@ -192,8 +203,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 +210,12 @@ 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); + struct wlr_wl_buffer *buffer, *tmp_buffer; + wl_list_for_each_safe(buffer, tmp_buffer, &wl->buffers, link) { + destroy_wl_buffer(buffer); } + + destroy_wl_seats(wl); if (wl->zxdg_decoration_manager_v1) { zxdg_decoration_manager_v1_destroy(wl->zxdg_decoration_manager_v1); } @@ -265,6 +274,8 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display, wl->local_display = display; wl_list_init(&wl->devices); wl_list_init(&wl->outputs); + wl_list_init(&wl->seats); + wl_list_init(&wl->buffers); wl->remote_display = wl_display_connect(remote); if (!wl->remote_display) { @@ -279,8 +290,8 @@ 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); + wl_display_roundtrip(wl->remote_display); // get globals + wl_display_roundtrip(wl->remote_display); // get linux-dmabuf formats if (!wl->compositor) { wlr_log(WLR_ERROR, @@ -304,28 +315,66 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display, } wl_event_source_check(wl->remote_display_src); - static EGLint config_attribs[] = { - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL_RED_SIZE, 1, - EGL_GREEN_SIZE, 1, - EGL_BLUE_SIZE, 1, - EGL_ALPHA_SIZE, 1, - EGL_NONE, - }; - if (!create_renderer_func) { create_renderer_func = wlr_renderer_autocreate; } wl->renderer = create_renderer_func(&wl->egl, EGL_PLATFORM_WAYLAND_EXT, - wl->remote_display, config_attribs, WL_SHM_FORMAT_ARGB8888); - + wl->remote_display, NULL, 0); if (!wl->renderer) { wlr_log(WLR_ERROR, "Could not create renderer"); goto error_event; } + // TODO: get FD from linux-dmabuf hints instead + int drm_fd = wlr_renderer_get_drm_fd(wl->renderer); + if (drm_fd < 0) { + wlr_log(WLR_ERROR, "Failed to get DRM device FD from renderer"); + goto error_event; + } + + drm_fd = fcntl(drm_fd, F_DUPFD_CLOEXEC, 0); + if (drm_fd < 0) { + wlr_log_errno(WLR_ERROR, "fcntl(F_DUPFD_CLOEXEC) failed"); + goto error_event; + } + + struct wlr_gbm_allocator *alloc = wlr_gbm_allocator_create(drm_fd); + if (alloc == NULL) { + wlr_log(WLR_ERROR, "Failed to create GBM allocator"); + goto error_event; + } + wl->allocator = &alloc->base; + + uint32_t fmt = DRM_FORMAT_ARGB8888; + const struct wlr_drm_format *remote_format = + wlr_drm_format_set_get(&wl->linux_dmabuf_v1_formats, fmt); + if (remote_format == NULL) { + wlr_log(WLR_ERROR, "Remote compositor doesn't support format " + "0x%"PRIX32" via linux-dmabuf-unstable-v1", fmt); + goto error_event; + } + + const struct wlr_drm_format_set *render_formats = + wlr_renderer_get_dmabuf_render_formats(wl->renderer); + if (render_formats == NULL) { + wlr_log(WLR_ERROR, "Failed to get available DMA-BUF formats from renderer"); + return false; + } + const struct wlr_drm_format *render_format = + wlr_drm_format_set_get(render_formats, fmt); + if (render_format == NULL) { + wlr_log(WLR_ERROR, "Renderer doesn't support DRM format 0x%"PRIX32, fmt); + return false; + } + + wl->format = wlr_drm_format_intersect(remote_format, render_format); + if (wl->format == NULL) { + wlr_log(WLR_ERROR, "Failed to intersect remote and render modifiers " + "for format 0x%"PRIX32, fmt); + return false; + } + wl->local_display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &wl->local_display_destroy); diff --git a/backend/wayland/output.c b/backend/wayland/output.c index bd17fa208..db9056d51 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -15,7 +15,10 @@ #include #include "backend/wayland.h" +#include "render/swapchain.h" +#include "render/wlr_renderer.h" #include "util/signal.h" + #include "linux-dmabuf-unstable-v1-client-protocol.h" #include "presentation-time-client-protocol.h" #include "xdg-decoration-unstable-v1-client-protocol.h" @@ -94,37 +97,69 @@ static const struct wp_presentation_feedback_listener static bool output_set_custom_mode(struct wlr_output *wlr_output, int32_t width, int32_t height, int32_t refresh) { struct wlr_wl_output *output = get_wl_output_from_output(wlr_output); - wl_egl_window_resize(output->egl_window, width, height, 0, 0); + + if (wlr_output->width != width || wlr_output->height != height) { + struct wlr_swapchain *swapchain = wlr_swapchain_create( + output->backend->allocator, width, height, output->backend->format); + if (swapchain == NULL) { + return false; + } + wlr_swapchain_destroy(output->swapchain); + output->swapchain = swapchain; + } + wlr_output_update_custom_mode(&output->wlr_output, width, height, 0); return true; } static bool output_attach_render(struct wlr_output *wlr_output, int *buffer_age) { - struct wlr_wl_output *output = - get_wl_output_from_output(wlr_output); - return wlr_egl_make_current(&output->backend->egl, output->egl_surface, - buffer_age); + struct wlr_wl_output *output = get_wl_output_from_output(wlr_output); + + wlr_buffer_unlock(output->back_buffer); + output->back_buffer = wlr_swapchain_acquire(output->swapchain, buffer_age); + if (!output->back_buffer) { + return false; + } + + if (!wlr_egl_make_current(&output->backend->egl, EGL_NO_SURFACE, NULL)) { + return false; + } + if (!wlr_renderer_bind_buffer(output->backend->renderer, + output->back_buffer)) { + return false; + } + + return true; } -static void destroy_wl_buffer(struct wlr_wl_buffer *buffer) { +void destroy_wl_buffer(struct wlr_wl_buffer *buffer) { if (buffer == NULL) { return; } + wl_list_remove(&buffer->buffer_destroy.link); + wl_list_remove(&buffer->link); wl_buffer_destroy(buffer->wl_buffer); - wlr_buffer_unlock(buffer->buffer); free(buffer); } static void buffer_handle_release(void *data, struct wl_buffer *wl_buffer) { struct wlr_wl_buffer *buffer = data; - destroy_wl_buffer(buffer); + buffer->released = true; + wlr_buffer_unlock(buffer->buffer); // might free buffer } static const struct wl_buffer_listener buffer_listener = { .release = buffer_handle_release, }; +static void buffer_handle_buffer_destroy(struct wl_listener *listener, + void *data) { + struct wlr_wl_buffer *buffer = + wl_container_of(listener, buffer, buffer_destroy); + destroy_wl_buffer(buffer); +} + static bool test_buffer(struct wlr_wl_backend *wl, struct wlr_buffer *wlr_buffer) { struct wlr_dmabuf_attributes attribs; @@ -181,12 +216,33 @@ static struct wlr_wl_buffer *create_wl_buffer(struct wlr_wl_backend *wl, } buffer->wl_buffer = wl_buffer; buffer->buffer = wlr_buffer_lock(wlr_buffer); + wl_list_insert(&wl->buffers, &buffer->link); wl_buffer_add_listener(wl_buffer, &buffer_listener, buffer); + buffer->buffer_destroy.notify = buffer_handle_buffer_destroy; + wl_signal_add(&wlr_buffer->events.destroy, &buffer->buffer_destroy); + return buffer; } +static struct wlr_wl_buffer *get_or_create_wl_buffer(struct wlr_wl_backend *wl, + struct wlr_buffer *wlr_buffer) { + struct wlr_wl_buffer *buffer; + wl_list_for_each(buffer, &wl->buffers, link) { + // We can only re-use a wlr_wl_buffer if the parent compositor has + // released it, because wl_buffer.release is per-wl_buffer, not per + // wl_surface.commit. + if (buffer->buffer == wlr_buffer && buffer->released) { + buffer->released = false; + wlr_buffer_lock(buffer->buffer); + return buffer; + } + } + + return create_wl_buffer(wl, wlr_buffer); +} + static bool output_test(struct wlr_output *wlr_output) { struct wlr_wl_output *output = get_wl_output_from_output(wlr_output); @@ -246,39 +302,49 @@ static bool output_commit(struct wlr_output *wlr_output) { output->frame_callback = wl_surface_frame(output->surface); wl_callback_add_listener(output->frame_callback, &frame_listener, output); + struct wlr_buffer *wlr_buffer = NULL; switch (wlr_output->pending.buffer_type) { case WLR_OUTPUT_STATE_BUFFER_RENDER: - if (!wlr_egl_swap_buffers(&output->backend->egl, - output->egl_surface, damage)) { - return false; - } + assert(output->back_buffer != NULL); + wlr_buffer = output->back_buffer; + + wlr_renderer_bind_buffer(output->backend->renderer, NULL); + wlr_egl_unset_current(&output->backend->egl); break; case WLR_OUTPUT_STATE_BUFFER_SCANOUT:; - struct wlr_wl_buffer *buffer = - create_wl_buffer(output->backend, wlr_output->pending.buffer); - if (buffer == NULL) { - return false; - } - - wl_surface_attach(output->surface, buffer->wl_buffer, 0, 0); - - if (damage == NULL) { - wl_surface_damage_buffer(output->surface, - 0, 0, INT32_MAX, INT32_MAX); - } else { - int rects_len; - pixman_box32_t *rects = - pixman_region32_rectangles(damage, &rects_len); - for (int i = 0; i < rects_len; i++) { - pixman_box32_t *r = &rects[i]; - wl_surface_damage_buffer(output->surface, r->x1, r->y1, - r->x2 - r->x1, r->y2 - r->y1); - } - } - wl_surface_commit(output->surface); + wlr_buffer = wlr_output->pending.buffer; break; } + struct wlr_wl_buffer *buffer = + get_or_create_wl_buffer(output->backend, wlr_buffer); + if (buffer == NULL) { + return false; + } + + wl_surface_attach(output->surface, buffer->wl_buffer, 0, 0); + + if (damage == NULL) { + wl_surface_damage_buffer(output->surface, + 0, 0, INT32_MAX, INT32_MAX); + } else { + int rects_len; + pixman_box32_t *rects = + pixman_region32_rectangles(damage, &rects_len); + for (int i = 0; i < rects_len; i++) { + pixman_box32_t *r = &rects[i]; + wl_surface_damage_buffer(output->surface, r->x1, r->y1, + r->x2 - r->x1, r->y2 - r->y1); + } + } + + wl_surface_commit(output->surface); + + wlr_buffer_unlock(output->back_buffer); + output->back_buffer = NULL; + + wlr_swapchain_set_buffer_submitted(output->swapchain, wlr_buffer); + if (wp_feedback != NULL) { struct wlr_wl_presentation_feedback *feedback = calloc(1, sizeof(*feedback)); @@ -302,8 +368,8 @@ static bool output_commit(struct wlr_output *wlr_output) { } static void output_rollback_render(struct wlr_output *wlr_output) { - struct wlr_wl_output *output = - get_wl_output_from_output(wlr_output); + struct wlr_wl_output *output = get_wl_output_from_output(wlr_output); + wlr_renderer_bind_buffer(output->backend->renderer, NULL); wlr_egl_unset_current(&output->backend->egl); } @@ -341,19 +407,30 @@ static bool output_set_cursor(struct wlr_output *wlr_output, width = width * wlr_output->scale / scale; height = height * wlr_output->scale / scale; - output->cursor.width = width; - output->cursor.height = height; - - if (output->cursor.egl_window == NULL) { - output->cursor.egl_window = - wl_egl_window_create(surface, width, height); + if (output->cursor.swapchain == NULL || + output->cursor.swapchain->width != width || + output->cursor.swapchain->height != height) { + wlr_swapchain_destroy(output->cursor.swapchain); + output->cursor.swapchain = wlr_swapchain_create( + output->backend->allocator, width, height, + output->backend->format); + if (output->cursor.swapchain == NULL) { + return false; + } } - wl_egl_window_resize(output->cursor.egl_window, width, height, 0, 0); - EGLSurface egl_surface = - wlr_egl_create_surface(&backend->egl, output->cursor.egl_window); + struct wlr_buffer *wlr_buffer = + wlr_swapchain_acquire(output->cursor.swapchain, NULL); + if (wlr_buffer == NULL) { + return false; + } - wlr_egl_make_current(&backend->egl, egl_surface, NULL); + if (!wlr_egl_make_current(&output->backend->egl, EGL_NO_SURFACE, NULL)) { + return false; + } + if (!wlr_renderer_bind_buffer(output->backend->renderer, wlr_buffer)) { + return false; + } struct wlr_box cursor_box = { .width = width, @@ -371,8 +448,23 @@ static bool output_set_cursor(struct wlr_output *wlr_output, wlr_render_texture_with_matrix(backend->renderer, texture, matrix, 1.0); wlr_renderer_end(backend->renderer); - wlr_egl_swap_buffers(&backend->egl, egl_surface, NULL); - wlr_egl_destroy_surface(&backend->egl, egl_surface); + wlr_renderer_bind_buffer(output->backend->renderer, NULL); + wlr_egl_unset_current(&output->backend->egl); + + struct wlr_wl_buffer *buffer = + create_wl_buffer(output->backend, wlr_buffer); + if (buffer == NULL) { + return false; + } + + wl_surface_attach(surface, buffer->wl_buffer, 0, 0); + wl_surface_damage_buffer(surface, 0, 0, INT32_MAX, INT32_MAX); + wl_surface_commit(surface); + + wlr_buffer_unlock(wlr_buffer); + + output->cursor.width = width; + output->cursor.height = height; } else { wl_surface_attach(surface, NULL, 0, 0); wl_surface_commit(surface); @@ -390,9 +482,7 @@ static void output_destroy(struct wlr_output *wlr_output) { wl_list_remove(&output->link); - if (output->cursor.egl_window != NULL) { - wl_egl_window_destroy(output->cursor.egl_window); - } + wlr_swapchain_destroy(output->cursor.swapchain); if (output->cursor.surface) { wl_surface_destroy(output->cursor.surface); } @@ -407,8 +497,8 @@ static void output_destroy(struct wlr_output *wlr_output) { presentation_feedback_destroy(feedback); } - wlr_egl_destroy_surface(&output->backend->egl, output->egl_surface); - wl_egl_window_destroy(output->egl_window); + wlr_buffer_unlock(output->back_buffer); + wlr_swapchain_destroy(output->swapchain); if (output->zxdg_toplevel_decoration_v1) { zxdg_toplevel_decoration_v1_destroy(output->zxdg_toplevel_decoration_v1); } @@ -419,8 +509,11 @@ 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->cursor.pointer; + if (pointer) { + assert(pointer->output == output); + assert(output->enter_serial); + wl_pointer_set_cursor(pointer->wl_pointer, output->enter_serial, output->cursor.surface, output->cursor.hotspot_x, output->cursor.hotspot_y); } @@ -555,40 +648,29 @@ struct wlr_output *wlr_wl_output_create(struct wlr_backend *wlr_backend) { &xdg_toplevel_listener, output); wl_surface_commit(output->surface); - output->egl_window = wl_egl_window_create(output->surface, - wlr_output->width, wlr_output->height); - output->egl_surface = wlr_egl_create_surface(&backend->egl, - output->egl_window); + output->swapchain = wlr_swapchain_create(output->backend->allocator, + wlr_output->width, wlr_output->height, output->backend->format); + if (output->swapchain == NULL) { + goto error; + } wl_display_roundtrip(output->backend->remote_display); - // start rendering loop per callbacks by rendering first frame - if (!wlr_egl_make_current(&output->backend->egl, output->egl_surface, - NULL)) { - goto error; - } - - wlr_renderer_begin(backend->renderer, wlr_output->width, wlr_output->height); - wlr_renderer_clear(backend->renderer, (float[]){ 1.0, 1.0, 1.0, 1.0 }); - wlr_renderer_end(backend->renderer); - - output->frame_callback = wl_surface_frame(output->surface); - wl_callback_add_listener(output->frame_callback, &frame_listener, output); - - if (!wlr_egl_swap_buffers(&output->backend->egl, output->egl_surface, - NULL)) { - goto error; - } - wl_list_insert(&backend->outputs, &output->link); wlr_output_update_enabled(wlr_output, true); 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; + wl_list_for_each(seat, &backend->seats, link) { + if (seat->pointer) { + create_wl_pointer(seat, output); + } } + // Start the rendering loop by requesting the compositor to render a frame + wlr_output_schedule_frame(wlr_output); + return wlr_output; error: diff --git a/backend/wayland/seat.c b/backend/wayland/seat.c index 1fcb93e5f..4b97bd489 100644 --- a/backend/wayland/seat.c +++ b/backend/wayland/seat.c @@ -22,14 +22,16 @@ #include "util/signal.h" #include "util/time.h" -static struct wlr_wl_pointer *output_get_pointer(struct wlr_wl_output *output) { +static struct wlr_wl_pointer *output_get_pointer( + struct wlr_wl_output *output, + const struct wl_pointer *wl_pointer) { struct wlr_input_device *wlr_dev; wl_list_for_each(wlr_dev, &output->backend->devices, link) { if (wlr_dev->type != WLR_INPUT_DEVICE_POINTER) { continue; } struct wlr_wl_pointer *pointer = pointer_get_wl(wlr_dev->pointer); - if (pointer->output == output) { + if (pointer->output == output && pointer->wl_pointer == wl_pointer) { return pointer; } } @@ -40,43 +42,54 @@ static struct wlr_wl_pointer *output_get_pointer(struct wlr_wl_output *output) { static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t sx, wl_fixed_t sy) { - struct wlr_wl_backend *backend = data; + struct wlr_wl_seat *seat = data; if (surface == NULL) { return; } struct wlr_wl_output *output = wl_surface_get_user_data(surface); assert(output); - struct wlr_wl_pointer *pointer = output_get_pointer(output); + struct wlr_wl_pointer *pointer = output_get_pointer(output, wl_pointer); + seat->active_pointer = pointer; + + // Manage cursor icon/rendering on output + struct wlr_wl_pointer *current_pointer = output->cursor.pointer; + if (current_pointer && current_pointer != pointer) { + wlr_log(WLR_INFO, "Ignoring seat %s pointer cursor in favor of seat %s", + seat->name, current_pointer->input_device->seat->name); + return; + } output->enter_serial = serial; - backend->current_pointer = pointer; + output->cursor.pointer = pointer; update_wl_output_cursor(output); } static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface) { - struct wlr_wl_backend *backend = data; + struct wlr_wl_seat *seat = data; if (surface == NULL) { return; } struct wlr_wl_output *output = wl_surface_get_user_data(surface); assert(output); - output->enter_serial = 0; - if (backend->current_pointer == NULL || - backend->current_pointer->output != output) { - return; + if (seat->active_pointer != NULL && + seat->active_pointer->output == output) { + seat->active_pointer = NULL; } - backend->current_pointer = NULL; + if (output->cursor.pointer == seat->active_pointer) { + output->enter_serial = 0; + output->cursor.pointer = NULL; + } } static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t sx, wl_fixed_t sy) { - struct wlr_wl_backend *backend = data; - struct wlr_wl_pointer *pointer = backend->current_pointer; + struct wlr_wl_seat *seat = data; + struct wlr_wl_pointer *pointer = seat->active_pointer; if (pointer == NULL) { return; } @@ -93,8 +106,8 @@ static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer, static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { - struct wlr_wl_backend *backend = data; - struct wlr_wl_pointer *pointer = backend->current_pointer; + struct wlr_wl_seat *seat = data; + struct wlr_wl_pointer *pointer = seat->active_pointer; if (pointer == NULL) { return; } @@ -110,8 +123,8 @@ static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer, static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { - struct wlr_wl_backend *backend = data; - struct wlr_wl_pointer *pointer = backend->current_pointer; + struct wlr_wl_seat *seat = data; + struct wlr_wl_pointer *pointer = seat->active_pointer; if (pointer == NULL) { return; } @@ -130,8 +143,8 @@ static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, } static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) { - struct wlr_wl_backend *backend = data; - struct wlr_wl_pointer *pointer = backend->current_pointer; + struct wlr_wl_seat *seat = data; + struct wlr_wl_pointer *pointer = seat->active_pointer; if (pointer == NULL) { return; } @@ -142,8 +155,8 @@ static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) { static void pointer_handle_axis_source(void *data, struct wl_pointer *wl_pointer, uint32_t axis_source) { - struct wlr_wl_backend *backend = data; - struct wlr_wl_pointer *pointer = backend->current_pointer; + struct wlr_wl_seat *seat = data; + struct wlr_wl_pointer *pointer = seat->active_pointer; if (pointer == NULL) { return; } @@ -153,8 +166,8 @@ static void pointer_handle_axis_source(void *data, static void pointer_handle_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis) { - struct wlr_wl_backend *backend = data; - struct wlr_wl_pointer *pointer = backend->current_pointer; + struct wlr_wl_seat *seat = data; + struct wlr_wl_pointer *pointer = seat->active_pointer; if (pointer == NULL) { return; } @@ -172,8 +185,8 @@ static void pointer_handle_axis_stop(void *data, struct wl_pointer *wl_pointer, static void pointer_handle_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) { - struct wlr_wl_backend *backend = data; - struct wlr_wl_pointer *pointer = backend->current_pointer; + struct wlr_wl_seat *seat = data; + struct wlr_wl_pointer *pointer = seat->active_pointer; if (pointer == NULL) { return; } @@ -208,7 +221,7 @@ static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard, wl_array_for_each(keycode_ptr, keys) { struct wlr_event_keyboard_key event = { .keycode = *keycode_ptr, - .state = WLR_KEY_PRESSED, + .state = WL_KEYBOARD_KEY_STATE_PRESSED, .time_msec = time, .update_state = false, }; @@ -231,7 +244,7 @@ static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard, struct wlr_event_keyboard_key event = { .keycode = keycode, - .state = WLR_KEY_RELEASED, + .state = WL_KEYBOARD_KEY_STATE_RELEASED, .time_msec = time, .update_state = false, }; @@ -276,19 +289,153 @@ 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) { + 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; + seat->backend = wl; + wl_list_insert(&wl->seats, &seat->link); + wl_seat_add_listener(wl_seat, &seat_listener, seat); + return true; +} + +void destroy_wl_seats(struct wlr_wl_backend *wl) { + struct wlr_wl_seat *seat, *tmp_seat; + wl_list_for_each_safe(seat, tmp_seat, &wl->seats, link) { + 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); + assert(seat->wl_seat); + wl_seat_destroy(seat->wl_seat); + + wl_list_remove(&seat->link); + free(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->seat); + return dev->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; } + // We can't destroy pointer here because we might have multiple devices + // exposing it to compositor. wl_list_remove(&dev->wlr_input_device.link); free(dev); } @@ -302,22 +449,27 @@ bool wlr_input_device_is_wl(struct wlr_input_device *dev) { } struct wlr_wl_input_device *create_wl_input_device( - struct wlr_wl_backend *backend, enum wlr_input_device_type type) { + struct wlr_wl_seat *seat, enum wlr_input_device_type type) { struct wlr_wl_input_device *dev = calloc(1, sizeof(struct wlr_wl_input_device)); if (dev == NULL) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return NULL; } - dev->backend = backend; + dev->backend = seat->backend; + dev->seat = seat; struct wlr_input_device *wlr_dev = &dev->wlr_input_device; unsigned int vendor = 0, product = 0; - const char *name = "wayland"; + + size_t name_size = 8 + strlen(seat->name) + 1; + char name[name_size]; + (void) snprintf(name, name_size, "wayland-%s", seat->name); + wlr_input_device_init(wlr_dev, type, &input_device_impl, name, vendor, product); - wl_list_insert(&backend->devices, &wlr_dev->link); + wl_list_insert(&seat->backend->devices, &wlr_dev->link); return dev; } @@ -331,8 +483,13 @@ struct wlr_wl_pointer *pointer_get_wl(struct wlr_pointer *wlr_pointer) { static void pointer_destroy(struct wlr_pointer *wlr_pointer) { struct wlr_wl_pointer *pointer = pointer_get_wl(wlr_pointer); - if (pointer->output->backend->current_pointer == pointer) { - pointer->output->backend->current_pointer = NULL; + if (pointer->output->cursor.pointer == pointer) { + pointer->output->cursor.pointer = NULL; + } + + struct wlr_wl_seat *seat = pointer->input_device->seat; + if (seat->active_pointer == pointer) { + seat->active_pointer = NULL; } wl_list_remove(&pointer->output_destroy.link); @@ -450,6 +607,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->seat->active_pointer) { + return; + } uint64_t time_usec = (uint64_t)utime_hi << 32 | utime_lo; @@ -473,21 +633,22 @@ 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); } -void create_wl_pointer(struct wl_pointer *wl_pointer, struct wlr_wl_output *output) { +void create_wl_pointer(struct wlr_wl_seat *seat, struct wlr_wl_output *output) { + assert(seat->pointer); + struct wl_pointer *wl_pointer = seat->pointer; struct wlr_wl_backend *backend = output->backend; - struct wlr_input_device *wlr_dev; - wl_list_for_each(wlr_dev, &output->backend->devices, link) { - if (wlr_dev->type != WLR_INPUT_DEVICE_POINTER) { - continue; - } - struct wlr_wl_pointer *pointer = pointer_get_wl(wlr_dev->pointer); - if (pointer->output == output) { - return; - } + if (output_get_pointer(output, wl_pointer)) { + wlr_log(WLR_DEBUG, + "Pointer for seat %s and output %s already exists (ignoring)", + seat->name, output->wlr_output.name); + return; } struct wlr_wl_pointer *pointer = calloc(1, sizeof(struct wlr_wl_pointer)); @@ -496,13 +657,10 @@ void create_wl_pointer(struct wl_pointer *wl_pointer, struct wlr_wl_output *outp return; } 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; + pointer->output = output; // we need output to map absolute coordinates onto struct wlr_wl_input_device *dev = - create_wl_input_device(backend, WLR_INPUT_DEVICE_POINTER); + create_wl_input_device(seat, WLR_INPUT_DEVICE_POINTER); if (dev == NULL) { free(pointer); wlr_log(WLR_ERROR, "Allocation failed"); @@ -510,7 +668,10 @@ void create_wl_pointer(struct wl_pointer *wl_pointer, struct wlr_wl_output *outp } pointer->input_device = dev; - wlr_dev = &dev->wlr_input_device; + wl_signal_add(&output->wlr_output.events.destroy, &pointer->output_destroy); + pointer->output_destroy.notify = pointer_handle_output_destroy; + + struct wlr_input_device *wlr_dev = &dev->wlr_input_device; wlr_dev->pointer = &pointer->wlr_pointer; wlr_dev->output_name = strdup(output->wlr_output.name); wlr_pointer_init(wlr_dev->pointer, &pointer_impl); @@ -532,13 +693,15 @@ void create_wl_pointer(struct wl_pointer *wl_pointer, struct wlr_wl_output *outp &relative_pointer_listener, dev); } - wl_pointer_add_listener(wl_pointer, &pointer_listener, backend); + wl_pointer_add_listener(wl_pointer, &pointer_listener, seat); wlr_signal_emit_safe(&backend->backend.events.new_input, wlr_dev); } -void create_wl_keyboard(struct wl_keyboard *wl_keyboard, struct wlr_wl_backend *wl) { +void create_wl_keyboard(struct wlr_wl_seat *seat) { + assert(seat->keyboard); + struct wl_keyboard *wl_keyboard = seat->keyboard; struct wlr_wl_input_device *dev = - create_wl_input_device(wl, WLR_INPUT_DEVICE_KEYBOARD); + create_wl_input_device(seat, WLR_INPUT_DEVICE_KEYBOARD); if (!dev) { return; } @@ -548,75 +711,138 @@ 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); wl_keyboard_add_listener(wl_keyboard, &keyboard_listener, wlr_dev); - wlr_signal_emit_safe(&wl->backend.events.new_input, wlr_dev); + wlr_signal_emit_safe(&seat->backend->backend.events.new_input, wlr_dev); } +void create_wl_touch(struct wlr_wl_seat *seat) { + assert(seat->touch); + struct wl_touch *wl_touch = seat->touch; + struct wlr_wl_input_device *dev = + create_wl_input_device(seat, 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(&seat->backend->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 = data; + struct wlr_wl_backend *backend = seat->backend; - 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); + create_wl_pointer(seat, 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 wl_pointer *wl_pointer = seat->pointer; + struct wlr_input_device *device, *tmp; wl_list_for_each_safe(device, tmp, &backend->devices, link) { - if (device->type == WLR_INPUT_DEVICE_POINTER) { - wlr_input_device_destroy(device); + if (device->type != WLR_INPUT_DEVICE_POINTER) { + continue; } + struct wlr_wl_pointer *pointer = pointer_get_wl(device->pointer); + if (pointer->wl_pointer != wl_pointer) { + continue; + } + wlr_log(WLR_DEBUG, "dropping pointer %s", + pointer->input_device->wlr_input_device.name); + struct wlr_wl_output *output = pointer->output; + wlr_input_device_destroy(device); + assert(seat->active_pointer != pointer); + assert(output->cursor.pointer != pointer); } - 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); + create_wl_keyboard(seat); } } - 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; wl_list_for_each_safe(device, tmp, &backend->devices, link) { - if (device->type == WLR_INPUT_DEVICE_KEYBOARD) { + if (device->type != WLR_INPUT_DEVICE_KEYBOARD) { + continue; + } + + struct wlr_wl_input_device *input_device = + get_wl_input_device_from_input_device(device); + if (input_device->seat != seat) { + continue; + } + wlr_input_device_destroy(device); + } + 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); + } + } + 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); } } - assert(backend->keyboard == NULL); // free'ed by input_device_destroy + + 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 = data; + free(seat->name); + seat->name = strdup(name); } const struct wl_seat_listener seat_listener = { @@ -625,7 +851,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/backend/wayland/tablet_v2.c b/backend/wayland/tablet_v2.c index 826c36ba1..b7ad73ea4 100644 --- a/backend/wayland/tablet_v2.c +++ b/backend/wayland/tablet_v2.c @@ -429,9 +429,9 @@ static void handle_pad_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_pad_v2 *id) { wlr_log(WLR_DEBUG, "New tablet pad"); - struct wlr_wl_backend *backend = data; + struct wlr_wl_seat *seat = data; struct wlr_wl_input_device *dev = create_wl_input_device( - backend, WLR_INPUT_DEVICE_TABLET_PAD); + seat, WLR_INPUT_DEVICE_TABLET_PAD); if (!dev) { /* This leaks a couple of server-sent resource ids. iirc this * shouldn't ever be a problem, but it isn't exactly nice @@ -889,9 +889,9 @@ static void handle_tab_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_v2 *id) { wlr_log(WLR_DEBUG, "New tablet"); - struct wlr_wl_backend *backend = data; + struct wlr_wl_seat *seat = data; struct wlr_wl_input_device *dev = create_wl_input_device( - backend, WLR_INPUT_DEVICE_TABLET_TOOL); + seat, WLR_INPUT_DEVICE_TABLET_TOOL); if (!dev) { zwp_tablet_v2_destroy(id); @@ -919,18 +919,18 @@ static const struct zwp_tablet_seat_v2_listener tablet_seat_listener = { struct wlr_wl_tablet_seat *wl_add_tablet_seat( struct zwp_tablet_manager_v2 *manager, - struct wl_seat *seat, struct wlr_wl_backend *backend) { + struct wlr_wl_seat *seat) { struct wlr_wl_tablet_seat *ret = calloc(1, sizeof(struct wlr_wl_tablet_seat)); if (!(ret->tablet_seat = - zwp_tablet_manager_v2_get_tablet_seat(manager, seat))) { + zwp_tablet_manager_v2_get_tablet_seat(manager, seat->wl_seat))) { free(ret); return NULL; } zwp_tablet_seat_v2_add_listener(ret->tablet_seat, - &tablet_seat_listener, backend); + &tablet_seat_listener, seat); return ret; } diff --git a/backend/x11/backend.c b/backend/x11/backend.c index 6dc650ab3..6067c89f4 100644 --- a/backend/x11/backend.c +++ b/backend/x11/backend.c @@ -1,17 +1,21 @@ -#define _POSIX_C_SOURCE 200112L - +#define _POSIX_C_SOURCE 200809L #include +#include #include #include #include #include #include +#include #include +#include #include #include #include +#include +#include #include #include @@ -25,8 +29,26 @@ #include #include "backend/x11.h" +#include "render/drm_format_set.h" +#include "render/gbm_allocator.h" +#include "render/wlr_renderer.h" #include "util/signal.h" +// See dri2_format_for_depth in mesa +const struct wlr_x11_format formats[] = { + { .drm = DRM_FORMAT_XRGB8888, .depth = 24, .bpp = 32 }, + { .drm = DRM_FORMAT_ARGB8888, .depth = 32, .bpp = 32 }, +}; + +static const struct wlr_x11_format *x11_format_from_depth(uint8_t depth) { + for (size_t i = 0; i < sizeof(formats) / sizeof(formats[0]); i++) { + if (formats[i].depth == depth) { + return &formats[i]; + } + } + return NULL; +} + struct wlr_x11_output *get_x11_output_from_window_id( struct wlr_x11_backend *x11, xcb_window_t window) { struct wlr_x11_output *output; @@ -38,6 +60,10 @@ struct wlr_x11_output *get_x11_output_from_window_id( return NULL; } +static void handle_x11_error(struct wlr_x11_backend *x11, xcb_value_error_t *ev); +static void handle_x11_unknown_event(struct wlr_x11_backend *x11, + xcb_generic_event_t *ev); + static void handle_x11_event(struct wlr_x11_backend *x11, xcb_generic_event_t *event) { switch (event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK) { @@ -68,6 +94,9 @@ static void handle_x11_event(struct wlr_x11_backend *x11, if (output != NULL) { wlr_output_destroy(&output->wlr_output); } + } else { + wlr_log(WLR_DEBUG, "Unhandled client message %"PRIu32, + ev->data.data32[0]); } break; } @@ -75,8 +104,21 @@ static void handle_x11_event(struct wlr_x11_backend *x11, xcb_ge_generic_event_t *ev = (xcb_ge_generic_event_t *)event; if (ev->extension == x11->xinput_opcode) { handle_x11_xinput_event(x11, ev); + } else if (ev->extension == x11->present_opcode) { + handle_x11_present_event(x11, ev); + } else { + handle_x11_unknown_event(x11, event); } + break; } + case 0: { + xcb_value_error_t *ev = (xcb_value_error_t *)event; + handle_x11_error(x11, ev); + break; + } + default: + handle_x11_unknown_event(x11, event); + break; } } @@ -139,6 +181,13 @@ static void backend_destroy(struct wlr_backend *backend) { wlr_renderer_destroy(x11->renderer); wlr_egl_finish(&x11->egl); + wlr_allocator_destroy(x11->allocator); + wlr_drm_format_set_finish(&x11->dri3_formats); + free(x11->drm_format); + +#if WLR_HAS_XCB_ERRORS + xcb_errors_context_free(x11->errors_context); +#endif if (x11->xlib_conn) { XCloseDisplay(x11->xlib_conn); @@ -168,6 +217,110 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { backend_destroy(&x11->backend); } +static xcb_depth_t *get_depth(xcb_screen_t *screen, uint8_t depth) { + xcb_depth_iterator_t iter = xcb_screen_allowed_depths_iterator(screen); + while (iter.rem > 0) { + if (iter.data->depth == depth) { + return iter.data; + } + xcb_depth_next(&iter); + } + return NULL; +} + +static xcb_visualid_t pick_visualid(xcb_depth_t *depth) { + xcb_visualtype_t *visuals = xcb_depth_visuals(depth); + for (int i = 0; i < xcb_depth_visuals_length(depth); i++) { + if (visuals[i]._class == XCB_VISUAL_CLASS_TRUE_COLOR) { + return visuals[i].visual_id; + } + } + return 0; +} + +static int query_dri3_drm_fd(struct wlr_x11_backend *x11) { + xcb_dri3_open_cookie_t open_cookie = + xcb_dri3_open(x11->xcb, x11->screen->root, 0); + xcb_dri3_open_reply_t *open_reply = + xcb_dri3_open_reply(x11->xcb, open_cookie, NULL); + if (open_reply == NULL) { + return -1; + } + + int *open_fds = xcb_dri3_open_reply_fds(x11->xcb, open_reply); + if (open_fds == NULL) { + free(open_reply); + return -1; + } + + assert(open_reply->nfd == 1); + int drm_fd = open_fds[0]; + + free(open_reply); + + int flags = fcntl(drm_fd, F_GETFD); + if (flags < 0) { + close(drm_fd); + return -1; + } + if (fcntl(drm_fd, F_SETFD, flags | FD_CLOEXEC) < 0) { + close(drm_fd); + return -1; + } + + return drm_fd; +} + +static bool query_dri3_modifiers(struct wlr_x11_backend *x11, + const struct wlr_x11_format *format) { + // Query the root window's supported modifiers, because we only care about + // screen_modifiers for now + xcb_dri3_get_supported_modifiers_cookie_t modifiers_cookie = + xcb_dri3_get_supported_modifiers(x11->xcb, x11->screen->root, + format->depth, format->bpp); + xcb_dri3_get_supported_modifiers_reply_t *modifiers_reply = + xcb_dri3_get_supported_modifiers_reply(x11->xcb, modifiers_cookie, + NULL); + if (!modifiers_reply) { + wlr_log(WLR_ERROR, "Failed to get DMA-BUF modifiers supported by " + "the X11 server for the format 0x%"PRIX32, format->drm); + return false; + } + + // If modifiers aren't supported, DRI3 will return an empty list + const uint64_t *modifiers = + xcb_dri3_get_supported_modifiers_screen_modifiers(modifiers_reply); + int modifiers_len = + xcb_dri3_get_supported_modifiers_screen_modifiers_length(modifiers_reply); + for (int i = 0; i < modifiers_len; i++) { + wlr_drm_format_set_add(&x11->dri3_formats, format->drm, modifiers[i]); + } + + free(modifiers_reply); + return true; +} + +static bool query_dri3_formats(struct wlr_x11_backend *x11) { + xcb_depth_iterator_t iter = xcb_screen_allowed_depths_iterator(x11->screen); + while (iter.rem > 0) { + uint8_t depth = iter.data->depth; + + const struct wlr_x11_format *format = x11_format_from_depth(depth); + if (format != NULL) { + wlr_drm_format_set_add(&x11->dri3_formats, format->drm, + DRM_FORMAT_MOD_INVALID); + + if (!query_dri3_modifiers(x11, format)) { + return false; + } + } + + xcb_depth_next(&iter); + } + + return true; +} + struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, const char *x11_display, wlr_renderer_create_func_t create_renderer_func) { @@ -225,6 +378,47 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, const xcb_query_extension_reply_t *ext; + // DRI3 extension + + ext = xcb_get_extension_data(x11->xcb, &xcb_dri3_id); + if (!ext || !ext->present) { + wlr_log(WLR_ERROR, "X11 does not support DRI3 extension"); + goto error_display; + } + + xcb_dri3_query_version_cookie_t dri3_cookie = + xcb_dri3_query_version(x11->xcb, 1, 2); + xcb_dri3_query_version_reply_t *dri3_reply = + xcb_dri3_query_version_reply(x11->xcb, dri3_cookie, NULL); + if (!dri3_reply || dri3_reply->major_version < 1 || + dri3_reply->minor_version < 2) { + wlr_log(WLR_ERROR, "X11 does not support required DRI3 version"); + goto error_display; + } + free(dri3_reply); + + // Present extension + + ext = xcb_get_extension_data(x11->xcb, &xcb_present_id); + if (!ext || !ext->present) { + wlr_log(WLR_ERROR, "X11 does not support Present extension"); + goto error_display; + } + x11->present_opcode = ext->major_opcode; + + xcb_present_query_version_cookie_t present_cookie = + xcb_present_query_version(x11->xcb, 1, 2); + xcb_present_query_version_reply_t *present_reply = + xcb_present_query_version_reply(x11->xcb, present_cookie, NULL); + if (!present_reply || present_reply->major_version < 1) { + wlr_log(WLR_ERROR, "X11 does not support required Present version"); + free(present_reply); + goto error_display; + } + free(present_reply); + + // Xfixes extension + ext = xcb_get_extension_data(x11->xcb, &xcb_xfixes_id); if (!ext || !ext->present) { wlr_log(WLR_ERROR, "X11 does not support Xfixes extension"); @@ -243,6 +437,8 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, } free(fixes_reply); + // Xinput extension + ext = xcb_get_extension_data(x11->xcb, &xcb_input_id); if (!ext || !ext->present) { wlr_log(WLR_ERROR, "X11 does not support Xinput extension"); @@ -273,29 +469,103 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, wl_event_source_check(x11->event_source); x11->screen = xcb_setup_roots_iterator(xcb_get_setup(x11->xcb)).data; + if (!x11->screen) { + wlr_log(WLR_ERROR, "Failed to get X11 screen"); + goto error_event; + } + + x11->depth = get_depth(x11->screen, 32); + if (!x11->depth) { + wlr_log(WLR_ERROR, "Failed to get 32-bit depth for X11 screen"); + goto error_event; + } + + x11->visualid = pick_visualid(x11->depth); + if (!x11->visualid) { + wlr_log(WLR_ERROR, "Failed to pick X11 visual"); + goto error_event; + } + + x11->x11_format = x11_format_from_depth(x11->depth->depth); + if (!x11->x11_format) { + wlr_log(WLR_ERROR, "Unsupported depth %"PRIu8, x11->depth->depth); + goto error_event; + } + + x11->colormap = xcb_generate_id(x11->xcb); + xcb_create_colormap(x11->xcb, XCB_COLORMAP_ALLOC_NONE, x11->colormap, + x11->screen->root, x11->visualid); + + // DRI3 may return a render node (Xwayland) or an authenticated primary + // node (plain Glamor). + int drm_fd = query_dri3_drm_fd(x11); + if (drm_fd < 0) { + wlr_log(WLR_ERROR, "Failed to query DRI3 DRM FD"); + goto error_event; + } + + struct wlr_gbm_allocator *gbm_alloc = wlr_gbm_allocator_create(drm_fd); + if (gbm_alloc == NULL) { + wlr_log(WLR_ERROR, "Failed to create GBM allocator"); + close(drm_fd); + goto error_event; + } + x11->allocator = &gbm_alloc->base; if (!create_renderer_func) { create_renderer_func = wlr_renderer_autocreate; } - static EGLint config_attribs[] = { - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL_RED_SIZE, 1, - EGL_GREEN_SIZE, 1, - EGL_BLUE_SIZE, 1, - EGL_ALPHA_SIZE, 0, - EGL_NONE, - }; - - x11->renderer = create_renderer_func(&x11->egl, EGL_PLATFORM_X11_KHR, - x11->xlib_conn, config_attribs, x11->screen->root_visual); - + x11->renderer = create_renderer_func(&x11->egl, EGL_PLATFORM_GBM_KHR, + gbm_alloc->gbm_device, NULL, 0); if (x11->renderer == NULL) { wlr_log(WLR_ERROR, "Failed to create renderer"); goto error_event; } + const struct wlr_drm_format_set *render_formats = + wlr_renderer_get_dmabuf_render_formats(x11->renderer); + if (render_formats == NULL) { + wlr_log(WLR_ERROR, "Failed to get available DMA-BUF formats from renderer"); + return false; + } + const struct wlr_drm_format *render_format = + wlr_drm_format_set_get(render_formats, x11->x11_format->drm); + if (render_format == NULL) { + wlr_log(WLR_ERROR, "Renderer doesn't support DRM format 0x%"PRIX32, + x11->x11_format->drm); + return false; + } + + if (!query_dri3_formats(x11)) { + wlr_log(WLR_ERROR, "Failed to query supported DRI3 formats"); + return false; + } + + const struct wlr_drm_format *dri3_format = + wlr_drm_format_set_get(&x11->dri3_formats, x11->x11_format->drm); + if (dri3_format == NULL) { + wlr_log(WLR_ERROR, "X11 server doesn't support DRM format 0x%"PRIX32, + x11->x11_format->drm); + return false; + } + + x11->drm_format = wlr_drm_format_intersect(dri3_format, render_format); + if (x11->drm_format == NULL) { + wlr_log(WLR_ERROR, "Failed to intersect DRI3 and render modifiers for " + "format 0x%"PRIX32, x11->x11_format->drm); + return false; + } + +#if WLR_HAS_XCB_ERRORS + if (xcb_errors_context_new(x11->xcb, &x11->errors_context) != 0) { + wlr_log(WLR_ERROR, "Failed to create error context"); + return false; + } +#endif + + x11->present_event_id = xcb_generate_id(x11->xcb); + wlr_input_device_init(&x11->keyboard_dev, WLR_INPUT_DEVICE_KEYBOARD, &input_device_impl, "X11 keyboard", 0, 0); wlr_keyboard_init(&x11->keyboard, &keyboard_impl); @@ -314,3 +584,58 @@ error_x11: free(x11); return NULL; } + +static void handle_x11_error(struct wlr_x11_backend *x11, xcb_value_error_t *ev) { +#if WLR_HAS_XCB_ERRORS + const char *major_name = xcb_errors_get_name_for_major_code( + x11->errors_context, ev->major_opcode); + if (!major_name) { + wlr_log(WLR_DEBUG, "X11 error happened, but could not get major name"); + goto log_raw; + } + + const char *minor_name = xcb_errors_get_name_for_minor_code( + x11->errors_context, ev->major_opcode, ev->minor_opcode); + + const char *extension; + const char *error_name = xcb_errors_get_name_for_error(x11->errors_context, + ev->error_code, &extension); + if (!error_name) { + wlr_log(WLR_DEBUG, "X11 error happened, but could not get error name"); + goto log_raw; + } + + wlr_log(WLR_ERROR, "X11 error: op %s (%s), code %s (%s), " + "sequence %"PRIu16", value %"PRIu32, + major_name, minor_name ? minor_name : "no minor", + error_name, extension ? extension : "no extension", + ev->sequence, ev->bad_value); + + return; + +log_raw: +#endif + + wlr_log(WLR_ERROR, "X11 error: op %"PRIu8":%"PRIu16", code %"PRIu8", " + "sequence %"PRIu16", value %"PRIu32, + ev->major_opcode, ev->minor_opcode, ev->error_code, + ev->sequence, ev->bad_value); +} + +static void handle_x11_unknown_event(struct wlr_x11_backend *x11, + xcb_generic_event_t *ev) { +#if WLR_HAS_XCB_ERRORS + const char *extension; + const char *event_name = xcb_errors_get_name_for_xcb_event( + x11->errors_context, ev, &extension); + if (!event_name) { + wlr_log(WLR_DEBUG, "No name for unhandled event: %u", + ev->response_type); + return; + } + + wlr_log(WLR_DEBUG, "Unhandled X11 event: %s (%u)", event_name, ev->response_type); +#else + wlr_log(WLR_DEBUG, "Unhandled X11 event: %u", ev->response_type); +#endif +} diff --git a/backend/x11/input_device.c b/backend/x11/input_device.c index ced52baef..1bcd30607 100644 --- a/backend/x11/input_device.c +++ b/backend/x11/input_device.c @@ -4,6 +4,8 @@ #include +#include + #include #include #include @@ -18,7 +20,7 @@ #include "util/signal.h" static void send_key_event(struct wlr_x11_backend *x11, uint32_t key, - enum wlr_key_state st, xcb_timestamp_t time) { + enum wl_keyboard_key_state st, xcb_timestamp_t time) { struct wlr_event_keyboard_key ev = { .time_msec = time, .keycode = key, @@ -123,7 +125,7 @@ void handle_x11_xinput_event(struct wlr_x11_backend *x11, wlr_keyboard_notify_modifiers(&x11->keyboard, ev->mods.base, ev->mods.latched, ev->mods.locked, ev->mods.effective); - send_key_event(x11, ev->detail - 8, WLR_KEY_PRESSED, ev->time); + send_key_event(x11, ev->detail - 8, WL_KEYBOARD_KEY_STATE_PRESSED, ev->time); x11->time = ev->time; break; } @@ -133,7 +135,7 @@ void handle_x11_xinput_event(struct wlr_x11_backend *x11, wlr_keyboard_notify_modifiers(&x11->keyboard, ev->mods.base, ev->mods.latched, ev->mods.locked, ev->mods.effective); - send_key_event(x11, ev->detail - 8, WLR_KEY_RELEASED, ev->time); + send_key_event(x11, ev->detail - 8, WL_KEYBOARD_KEY_STATE_RELEASED, ev->time); x11->time = ev->time; break; } diff --git a/backend/x11/meson.build b/backend/x11/meson.build index 40530bb0f..51c6dd04e 100644 --- a/backend/x11/meson.build +++ b/backend/x11/meson.build @@ -2,8 +2,10 @@ x11_libs = [] x11_required = [ 'x11-xcb', 'xcb', - 'xcb-xinput', + 'xcb-dri3', + 'xcb-present', 'xcb-xfixes', + 'xcb-xinput', ] msg = [] diff --git a/backend/x11/output.c b/backend/x11/output.c index 07374b614..eb35e5842 100644 --- a/backend/x11/output.c +++ b/backend/x11/output.c @@ -4,6 +4,9 @@ #include #include +#include +#include +#include #include #include @@ -13,7 +16,10 @@ #include #include "backend/x11.h" +#include "render/swapchain.h" +#include "render/wlr_renderer.h" #include "util/signal.h" +#include "util/time.h" static int signal_frame(void *data) { struct wlr_x11_output *output = data; @@ -76,6 +82,8 @@ static bool output_set_custom_mode(struct wlr_output *wlr_output, return true; } +static void destroy_x11_buffer(struct wlr_x11_buffer *buffer); + static void output_destroy(struct wlr_output *wlr_output) { struct wlr_x11_output *output = get_x11_output_from_output(wlr_output); struct wlr_x11_backend *x11 = output->x11; @@ -83,9 +91,15 @@ static void output_destroy(struct wlr_output *wlr_output) { wlr_input_device_destroy(&output->pointer_dev); wlr_input_device_destroy(&output->touch_dev); + struct wlr_x11_buffer *buffer, *buffer_tmp; + wl_list_for_each_safe(buffer, buffer_tmp, &output->buffers, link) { + destroy_x11_buffer(buffer); + } + wl_list_remove(&output->link); wl_event_source_remove(output->frame_timer); - wlr_egl_destroy_surface(&x11->egl, output->surf); + wlr_buffer_unlock(output->back_buffer); + wlr_swapchain_destroy(output->swapchain); xcb_destroy_window(x11->xcb, output->win); xcb_flush(x11->xcb); free(output); @@ -96,7 +110,20 @@ static bool output_attach_render(struct wlr_output *wlr_output, struct wlr_x11_output *output = get_x11_output_from_output(wlr_output); struct wlr_x11_backend *x11 = output->x11; - return wlr_egl_make_current(&x11->egl, output->surf, buffer_age); + wlr_buffer_unlock(output->back_buffer); + output->back_buffer = wlr_swapchain_acquire(output->swapchain, buffer_age); + if (!output->back_buffer) { + return false; + } + + if (!wlr_egl_make_current(&x11->egl, EGL_NO_SURFACE, NULL)) { + return false; + } + if (!wlr_renderer_bind_buffer(x11->renderer, output->back_buffer)) { + return false; + } + + return true; } static bool output_test(struct wlr_output *wlr_output) { @@ -112,6 +139,147 @@ static bool output_test(struct wlr_output *wlr_output) { return true; } +static void destroy_x11_buffer(struct wlr_x11_buffer *buffer) { + if (!buffer) { + return; + } + wl_list_remove(&buffer->buffer_destroy.link); + wl_list_remove(&buffer->link); + xcb_free_pixmap(buffer->x11->xcb, buffer->pixmap); + free(buffer); +} + +static void buffer_handle_buffer_destroy(struct wl_listener *listener, + void *data) { + struct wlr_x11_buffer *buffer = + wl_container_of(listener, buffer, buffer_destroy); + destroy_x11_buffer(buffer); +} + +static struct wlr_x11_buffer *create_x11_buffer(struct wlr_x11_output *output, + struct wlr_buffer *wlr_buffer) { + struct wlr_x11_backend *x11 = output->x11; + + struct wlr_dmabuf_attributes attrs = {0}; + if (!wlr_buffer_get_dmabuf(wlr_buffer, &attrs)) { + return NULL; + } + + if (attrs.format != x11->x11_format->drm) { + // The pixmap's depth must match the window's depth, otherwise Present + // will throw a Match error + return NULL; + } + + // xcb closes the FDs after sending them, so we need to dup them here + struct wlr_dmabuf_attributes dup_attrs = {0}; + if (!wlr_dmabuf_attributes_copy(&dup_attrs, &attrs)) { + return NULL; + } + + const struct wlr_x11_format *x11_fmt = x11->x11_format; + xcb_pixmap_t pixmap = xcb_generate_id(x11->xcb); + xcb_dri3_pixmap_from_buffers(x11->xcb, pixmap, output->win, + attrs.n_planes, attrs.width, attrs.height, attrs.stride[0], + attrs.offset[0], attrs.stride[1], attrs.offset[1], attrs.stride[2], + attrs.offset[2], attrs.stride[3], attrs.offset[3], x11_fmt->depth, + x11_fmt->bpp, attrs.modifier, dup_attrs.fd); + + struct wlr_x11_buffer *buffer = calloc(1, sizeof(struct wlr_x11_buffer)); + if (!buffer) { + xcb_free_pixmap(x11->xcb, pixmap); + return NULL; + } + buffer->buffer = wlr_buffer_lock(wlr_buffer); + buffer->pixmap = pixmap; + buffer->x11 = x11; + wl_list_insert(&output->buffers, &buffer->link); + + buffer->buffer_destroy.notify = buffer_handle_buffer_destroy; + wl_signal_add(&wlr_buffer->events.destroy, &buffer->buffer_destroy); + + return buffer; +} + +static struct wlr_x11_buffer *get_or_create_x11_buffer( + struct wlr_x11_output *output, struct wlr_buffer *wlr_buffer) { + struct wlr_x11_buffer *buffer; + wl_list_for_each(buffer, &output->buffers, link) { + if (buffer->buffer == wlr_buffer) { + wlr_buffer_lock(buffer->buffer); + return buffer; + } + } + + return create_x11_buffer(output, wlr_buffer); +} + +static bool output_commit_buffer(struct wlr_x11_output *output) { + struct wlr_x11_backend *x11 = output->x11; + + assert(output->back_buffer != NULL); + + wlr_renderer_bind_buffer(x11->renderer, NULL); + wlr_egl_unset_current(&x11->egl); + + struct wlr_x11_buffer *x11_buffer = + get_or_create_x11_buffer(output, output->back_buffer); + if (!x11_buffer) { + goto error; + } + + xcb_xfixes_region_t region = XCB_NONE; + if (output->wlr_output.pending.committed & WLR_OUTPUT_STATE_DAMAGE) { + pixman_region32_t *damage = &output->wlr_output.pending.damage; + + int rects_len = 0; + pixman_box32_t *rects = pixman_region32_rectangles(damage, &rects_len); + + xcb_rectangle_t *xcb_rects = calloc(rects_len, sizeof(xcb_rectangle_t)); + if (!xcb_rects) { + goto error; + } + + for (int i = 0; i < rects_len; i++) { + pixman_box32_t *box = &rects[i]; + xcb_rects[i] = (struct xcb_rectangle_t){ + .x = box->x1, + .y = box->y1, + .width = box->x2 - box->x1, + .height = box->y2 - box->y1, + }; + } + + xcb_xfixes_region_t region = xcb_generate_id(x11->xcb); + xcb_xfixes_create_region(x11->xcb, region, rects_len, xcb_rects); + + free(xcb_rects); + } + + uint32_t serial = output->wlr_output.commit_seq; + uint32_t options = 0; + xcb_present_pixmap(x11->xcb, output->win, x11_buffer->pixmap, serial, + 0, region, 0, 0, XCB_NONE, XCB_NONE, XCB_NONE, options, 0, 0, 0, + 0, NULL); + + if (region != XCB_NONE) { + xcb_xfixes_destroy_region(x11->xcb, region); + } + + wlr_buffer_unlock(output->back_buffer); + output->back_buffer = NULL; + + wlr_swapchain_set_buffer_submitted(output->swapchain, x11_buffer->buffer); + + return true; + +error: + destroy_x11_buffer(x11_buffer); + wlr_buffer_unlock(output->back_buffer); + output->back_buffer = NULL; + return false; +} + static bool output_commit(struct wlr_output *wlr_output) { struct wlr_x11_output *output = get_x11_output_from_output(wlr_output); struct wlr_x11_backend *x11 = output->x11; @@ -145,24 +313,21 @@ static bool output_commit(struct wlr_output *wlr_output) { } if (wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER) { - pixman_region32_t *damage = NULL; - if (wlr_output->pending.committed & WLR_OUTPUT_STATE_DAMAGE) { - damage = &wlr_output->pending.damage; - } - - if (!wlr_egl_swap_buffers(&x11->egl, output->surf, damage)) { + if (!output_commit_buffer(output)) { return false; } - - wlr_output_send_present(wlr_output, NULL); } + xcb_flush(x11->xcb); + return true; } static void output_rollback_render(struct wlr_output *wlr_output) { struct wlr_x11_output *output = get_x11_output_from_output(wlr_output); - wlr_egl_unset_current(&output->x11->egl); + struct wlr_x11_backend *x11 = output->x11; + wlr_renderer_bind_buffer(x11->renderer, NULL); + wlr_egl_unset_current(&x11->egl); } static const struct wlr_output_impl output_impl = { @@ -186,6 +351,7 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) { return NULL; } output->x11 = x11; + wl_list_init(&output->buffers); struct wlr_output *wlr_output = &output->wlr_output; wlr_output_init(wlr_output, &x11->backend, &output_impl, x11->wl_display); @@ -193,6 +359,14 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) { wlr_output->width = 1024; wlr_output->height = 768; + output->swapchain = wlr_swapchain_create(x11->allocator, + wlr_output->width, wlr_output->height, x11->drm_format); + if (!output->swapchain) { + wlr_log(WLR_ERROR, "Failed to create swapchain"); + free(output); + return NULL; + } + output_set_refresh(&output->wlr_output, 0); snprintf(wlr_output->name, sizeof(wlr_output->name), "X11-%zd", @@ -204,14 +378,18 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) { "X11 output %zd", x11->last_output_num); wlr_output_set_description(wlr_output, description); - uint32_t mask = XCB_CW_EVENT_MASK; + // The X11 protocol requires us to set a colormap and border pixel if the + // depth doesn't match the root window's + uint32_t mask = XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP; uint32_t values[] = { - XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY + 0, + XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY, + x11->colormap, }; output->win = xcb_generate_id(x11->xcb); - xcb_create_window(x11->xcb, XCB_COPY_FROM_PARENT, output->win, - x11->screen->root, 0, 0, wlr_output->width, wlr_output->height, 1, - XCB_WINDOW_CLASS_INPUT_OUTPUT, x11->screen->root_visual, mask, values); + xcb_create_window(x11->xcb, x11->depth->depth, output->win, + x11->screen->root, 0, 0, wlr_output->width, wlr_output->height, 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, x11->visualid, mask, values); struct { xcb_input_event_mask_t head; @@ -231,12 +409,10 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) { }; xcb_input_xi_select_events(x11->xcb, output->win, 1, &xinput_mask.head); - output->surf = wlr_egl_create_surface(&x11->egl, &output->win); - if (!output->surf) { - wlr_log(WLR_ERROR, "Failed to create EGL surface"); - free(output); - return NULL; - } + uint32_t present_mask = XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY | + XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY; + xcb_present_select_input(x11->xcb, x11->present_event_id, output->win, + present_mask); xcb_change_property(x11->xcb, XCB_PROP_MODE_REPLACE, output->win, x11->atoms.wm_protocols, XCB_ATOM_ATOM, 32, 1, @@ -278,17 +454,30 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) { void handle_x11_configure_notify(struct wlr_x11_output *output, xcb_configure_notify_event_t *ev) { // ignore events that set an invalid size: - if (ev->width > 0 && ev->height > 0) { - wlr_output_update_custom_mode(&output->wlr_output, ev->width, - ev->height, output->wlr_output.refresh); - - // Move the pointer to its new location - update_x11_pointer_position(output, output->x11->time); - } else { + if (ev->width == 0 || ev->height == 0) { wlr_log(WLR_DEBUG, "Ignoring X11 configure event for height=%d, width=%d", ev->width, ev->height); + return; } + + if (output->swapchain->width != ev->width || + output->swapchain->height != ev->height) { + struct wlr_swapchain *swapchain = wlr_swapchain_create( + output->x11->allocator, ev->width, ev->height, + output->x11->drm_format); + if (!swapchain) { + return; + } + wlr_swapchain_destroy(output->swapchain); + output->swapchain = swapchain; + } + + wlr_output_update_custom_mode(&output->wlr_output, ev->width, + ev->height, output->wlr_output.refresh); + + // Move the pointer to its new location + update_x11_pointer_position(output, output->x11->time); } bool wlr_output_is_x11(struct wlr_output *wlr_output) { @@ -310,3 +499,70 @@ void wlr_x11_output_set_title(struct wlr_output *output, const char *title) { x11_output->x11->atoms.net_wm_name, x11_output->x11->atoms.utf8_string, 8, strlen(title), title); } + +static struct wlr_x11_buffer *get_x11_buffer(struct wlr_x11_output *output, + xcb_pixmap_t pixmap) { + struct wlr_x11_buffer *buffer; + wl_list_for_each(buffer, &output->buffers, link) { + if (buffer->pixmap == pixmap) { + return buffer; + } + } + return NULL; +} + +void handle_x11_present_event(struct wlr_x11_backend *x11, + xcb_ge_generic_event_t *event) { + struct wlr_x11_output *output; + + switch (event->event_type) { + case XCB_PRESENT_EVENT_IDLE_NOTIFY:; + xcb_present_idle_notify_event_t *idle_notify = + (xcb_present_idle_notify_event_t *)event; + + output = get_x11_output_from_window_id(x11, idle_notify->window); + if (!output) { + wlr_log(WLR_DEBUG, "Got PresentIdleNotify event for unknown window"); + return; + } + + struct wlr_x11_buffer *buffer = + get_x11_buffer(output, idle_notify->pixmap); + if (!buffer) { + wlr_log(WLR_DEBUG, "Got PresentIdleNotify event for unknown buffer"); + return; + } + + wlr_buffer_unlock(buffer->buffer); // may destroy buffer + break; + case XCB_PRESENT_COMPLETE_NOTIFY:; + xcb_present_complete_notify_event_t *complete_notify = + (xcb_present_complete_notify_event_t *)event; + + output = get_x11_output_from_window_id(x11, complete_notify->window); + if (!output) { + wlr_log(WLR_DEBUG, "Got PresentCompleteNotify event for unknown window"); + return; + } + + struct timespec t; + timespec_from_nsec(&t, complete_notify->ust * 1000); + + uint32_t flags = 0; + if (complete_notify->mode == XCB_PRESENT_COMPLETE_MODE_FLIP) { + flags |= WLR_OUTPUT_PRESENT_ZERO_COPY; + } + + struct wlr_output_event_present present_event = { + .output = &output->wlr_output, + .commit_seq = complete_notify->serial, + .when = &t, + .seq = complete_notify->msc, + .flags = flags, + }; + wlr_output_send_present(&output->wlr_output, &present_event); + break; + default: + wlr_log(WLR_DEBUG, "Unhandled Present event %"PRIu16, event->event_type); + } +} 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/meson.build b/examples/meson.build index c8016d3a0..ede858328 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -1,4 +1,5 @@ threads = dependency('threads') +wayland_egl = dependency('wayland-egl') wayland_cursor = dependency('wayland-cursor') libpng = dependency('libpng', required: false, disabler: true) # These versions correspond to ffmpeg 4.0 @@ -54,7 +55,7 @@ clients = { }, 'idle-inhibit': { 'src': 'idle-inhibit.c', - 'dep': wlroots, + 'dep': [wayland_egl, wlroots], 'proto': [ 'idle-inhibit-unstable-v1', 'xdg-shell', @@ -62,7 +63,7 @@ clients = { }, 'keyboard-shortcuts-inhibit': { 'src': 'keyboard-shortcuts-inhibit.c', - 'dep': [wayland_cursor, wlroots], + 'dep': [wayland_egl, wayland_cursor, wlroots], 'proto': [ 'keyboard-shortcuts-inhibit-unstable-v1', 'xdg-shell', @@ -70,7 +71,7 @@ clients = { }, 'layer-shell': { 'src': 'layer-shell.c', - 'dep': [wayland_cursor, wlroots], + 'dep': [wayland_egl, wayland_cursor, wlroots], 'proto': [ 'wlr-layer-shell-unstable-v1', 'xdg-shell', @@ -78,7 +79,7 @@ clients = { }, 'input-inhibitor': { 'src': 'input-inhibitor.c', - 'dep': [wayland_cursor, wlroots], + 'dep': [wayland_egl, wayland_cursor, wlroots], 'proto': [ 'wlr-input-inhibitor-unstable-v1', 'xdg-shell', @@ -96,7 +97,7 @@ clients = { }, 'pointer-constraints': { 'src': 'pointer-constraints.c', - 'dep': wlroots, + 'dep': [wayland_egl, wlroots], 'proto': [ 'pointer-constraints-unstable-v1', 'xdg-shell', @@ -104,7 +105,7 @@ clients = { }, 'relative-pointer': { 'src': 'relative-pointer-unstable-v1.c', - 'dep': wlroots, + 'dep': [wayland_egl, wlroots], 'proto': [ 'pointer-constraints-unstable-v1', 'relative-pointer-unstable-v1', @@ -137,7 +138,7 @@ clients = { }, 'toplevel-decoration': { 'src': 'toplevel-decoration.c', - 'dep': wlroots, + 'dep': [wayland_egl, wlroots], 'proto': [ 'xdg-decoration-unstable-v1', 'xdg-shell', @@ -145,7 +146,7 @@ clients = { }, 'input-method': { 'src': 'input-method.c', - 'dep': libepoll, + 'dep': [wayland_egl, libepoll], 'proto': [ 'input-method-unstable-v2', 'text-input-unstable-v3', @@ -154,7 +155,7 @@ clients = { }, 'text-input': { 'src': 'text-input.c', - 'dep': [wayland_cursor, wlroots], + 'dep': [wayland_egl, wayland_cursor, wlroots], 'proto': [ 'text-input-unstable-v3', 'xdg-shell', diff --git a/examples/multi-pointer.c b/examples/multi-pointer.c index 9f7bd6c8e..a48f58886 100644 --- a/examples/multi-pointer.c +++ b/examples/multi-pointer.c @@ -10,13 +10,13 @@ #include #include #include -#include #include #include #include #include #include #include +#include #include #include #include diff --git a/examples/output-layout.c b/examples/output-layout.c index c9ca53a46..b3fff4196 100644 --- a/examples/output-layout.c +++ b/examples/output-layout.c @@ -190,7 +190,7 @@ static void keyboard_key_notify(struct wl_listener *listener, void *data) { // and make this change in pixels/sec^2 // Also, key repeat int delta = 75; - if (event->state == WLR_KEY_PRESSED) { + if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { switch (sym) { case XKB_KEY_Left: update_velocities(sample, -delta, 0); 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..92a5934e8 100644 --- a/examples/pointer.c +++ b/examples/pointer.c @@ -9,13 +9,15 @@ #include #include #include -#include #include #include #include #include #include #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/rotation.c b/examples/rotation.c index e76cc2a4d..edd100e9b 100644 --- a/examples/rotation.c +++ b/examples/rotation.c @@ -140,7 +140,7 @@ static void keyboard_key_notify(struct wl_listener *listener, void *data) { if (sym == XKB_KEY_Escape) { wl_display_terminate(sample->display); } - if (event->state == WLR_KEY_PRESSED) { + if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { switch (sym) { case XKB_KEY_Left: update_velocities(sample, -16, 0); 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..ce58dfa94 100644 --- a/examples/simple.c +++ b/examples/simple.c @@ -1,5 +1,4 @@ #define _POSIX_C_SOURCE 200112L -#include #include #include #include @@ -8,8 +7,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -18,7 +19,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 +41,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 +59,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 +86,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 +168,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/tablet.c b/examples/tablet.c index 7721ef720..59bf69798 100644 --- a/examples/tablet.c +++ b/examples/tablet.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include 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/touch.c b/examples/touch.c index 39dd1d457..3d18dfd0c 100644 --- a/examples/touch.c +++ b/examples/touch.c @@ -14,7 +14,9 @@ #include #include #include +#include #include +#include #include #include #include "cat.h" 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 886c69688..6c8939e83 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -32,7 +32,6 @@ struct wlr_drm_plane { /* Buffer currently displayed on screen */ struct wlr_drm_fb current_fb; - uint32_t drm_format; // ARGB2101010, XRGB2101010, ARGB8888, or XRGB8888 struct wlr_drm_format_set formats; // Only used by cursor @@ -63,13 +62,6 @@ struct wlr_drm_crtc { struct wlr_drm_plane *primary; struct wlr_drm_plane *cursor; - /* - * We don't support overlay planes yet, but we keep track of them to - * give to DRM lease clients. - */ - size_t num_overlays; - uint32_t *overlays; - union wlr_drm_crtc_props props; }; @@ -82,6 +74,8 @@ struct wlr_drm_backend { bool addfb2_modifiers; int fd; + char *name; + struct wlr_device *dev; size_t num_crtcs; struct wlr_drm_crtc *crtcs; @@ -115,8 +109,10 @@ struct wlr_drm_mode { }; struct wlr_drm_connector { - struct wlr_output output; + struct wlr_output output; // only valid if state != DISCONNECTED + struct wlr_drm_backend *backend; + char name[24]; enum wlr_drm_connector_state state; struct wlr_output_mode *desired_mode; bool desired_enabled; @@ -134,7 +130,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. @@ -150,6 +146,7 @@ void finish_drm_resources(struct wlr_drm_backend *drm); void restore_drm_outputs(struct wlr_drm_backend *drm); void scan_drm_connectors(struct wlr_drm_backend *state); int handle_drm_event(int fd, uint32_t mask, void *data); +void destroy_drm_connector(struct wlr_drm_connector *conn); bool drm_connector_set_mode(struct wlr_drm_connector *conn, struct wlr_output_mode *mode); bool drm_connector_is_cursor_visible(struct wlr_drm_connector *conn); @@ -159,4 +156,9 @@ size_t drm_crtc_get_gamma_lut_size(struct wlr_drm_backend *drm, struct wlr_drm_fb *plane_get_next_fb(struct wlr_drm_plane *plane); +#define wlr_drm_conn_log(conn, verb, fmt, ...) \ + wlr_log(verb, "connector %s: " fmt, conn->name, ##__VA_ARGS__) +#define wlr_drm_conn_log_errno(conn, verb, fmt, ...) \ + wlr_log_errno(verb, "connector %s: " fmt, conn->name, ##__VA_ARGS__) + #endif diff --git a/include/backend/drm/properties.h b/include/backend/drm/properties.h index fba444aa2..c2027c4f3 100644 --- a/include/backend/drm/properties.h +++ b/include/backend/drm/properties.h @@ -28,8 +28,6 @@ union wlr_drm_connector_props { union wlr_drm_crtc_props { struct { // Neither of these are guaranteed to exist - uint32_t rotation; - uint32_t scaling_mode; uint32_t vrr_enabled; uint32_t gamma_lut; uint32_t gamma_lut_size; diff --git a/include/backend/drm/renderer.h b/include/backend/drm/renderer.h index bfccf9d5c..7989fa0bf 100644 --- a/include/backend/drm/renderer.h +++ b/include/backend/drm/renderer.h @@ -17,9 +17,8 @@ struct wlr_drm_renderer { struct gbm_device *gbm; struct wlr_egl egl; - uint32_t gbm_format; - struct wlr_renderer *wlr_rend; + struct wlr_gbm_allocator *allocator; }; struct wlr_drm_surface { @@ -28,27 +27,17 @@ struct wlr_drm_surface { uint32_t width; uint32_t height; - struct gbm_surface *gbm; - EGLSurface egl; -}; - -enum wlr_drm_fb_type { - WLR_DRM_FB_TYPE_NONE, - WLR_DRM_FB_TYPE_SURFACE, - WLR_DRM_FB_TYPE_WLR_BUFFER + struct wlr_swapchain *swapchain; + struct wlr_buffer *back_buffer; }; struct wlr_drm_fb { - enum wlr_drm_fb_type type; struct gbm_bo *bo; + struct wlr_buffer *wlr_buf; struct wlr_drm_surface *mgpu_surf; struct gbm_bo *mgpu_bo; - - union { - struct wlr_drm_surface *surf; - struct wlr_buffer *wlr_buf; - }; + struct wlr_buffer *mgpu_wlr_buf; }; bool init_drm_renderer(struct wlr_drm_backend *drm, @@ -56,6 +45,7 @@ bool init_drm_renderer(struct wlr_drm_backend *drm, void finish_drm_renderer(struct wlr_drm_renderer *renderer); bool drm_surface_make_current(struct wlr_drm_surface *surf, int *buffer_age); +void drm_surface_unset_current(struct wlr_drm_surface *surf); bool export_drm_bo(struct gbm_bo *bo, struct wlr_dmabuf_attributes *attribs); void drm_fb_clear(struct wlr_drm_fb *fb); @@ -71,7 +61,7 @@ struct gbm_bo *drm_fb_acquire(struct wlr_drm_fb *fb, struct wlr_drm_backend *drm bool drm_plane_init_surface(struct wlr_drm_plane *plane, struct wlr_drm_backend *drm, int32_t width, uint32_t height, - uint32_t format, uint32_t flags, bool with_modifiers); + uint32_t format, bool force_linear, bool with_modifiers); void drm_plane_finish_surface(struct wlr_drm_plane *plane); #endif diff --git a/include/backend/headless.h b/include/backend/headless.h index 5681c2bd2..4a29b0338 100644 --- a/include/backend/headless.h +++ b/include/backend/headless.h @@ -3,7 +3,6 @@ #include #include -#include #define HEADLESS_DEFAULT_REFRESH (60 * 1000) // 60 Hz @@ -12,6 +11,8 @@ struct wlr_headless_backend { struct wlr_egl priv_egl; // may be uninitialized struct wlr_egl *egl; struct wlr_renderer *renderer; + struct wlr_allocator *allocator; + struct wlr_drm_format *format; struct wl_display *display; struct wl_list outputs; size_t last_output_num; @@ -19,7 +20,6 @@ struct wlr_headless_backend { struct wl_listener display_destroy; struct wl_listener renderer_destroy; bool started; - GLenum internal_format; }; struct wlr_headless_output { @@ -28,7 +28,8 @@ struct wlr_headless_output { struct wlr_headless_backend *backend; struct wl_list link; - GLuint fbo, rbo; + struct wlr_swapchain *swapchain; + struct wlr_buffer *back_buffer, *front_buffer; struct wl_event_source *frame_timer; int frame_delay; // ms 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..1c874b35b 100644 --- a/include/backend/wayland.h +++ b/include/backend/wayland.h @@ -4,14 +4,13 @@ #include #include -#include #include -#include #include #include #include #include +#include #include struct wlr_wl_backend { @@ -24,9 +23,13 @@ struct wlr_wl_backend { struct wl_list outputs; struct wlr_egl egl; struct wlr_renderer *renderer; + struct wlr_drm_format *format; + struct wlr_allocator *allocator; + struct wl_list buffers; // wlr_wl_buffer.link size_t requested_outputs; size_t last_output_num; struct wl_listener local_display_destroy; + /* remote state */ struct wl_display *remote_display; struct wl_event_source *remote_display_src; @@ -38,18 +41,17 @@ 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_pointer *current_pointer; + struct wl_list seats; // wlr_wl_seat.link struct zwp_tablet_manager_v2 *tablet_manager; - char *seat_name; struct wlr_drm_format_set linux_dmabuf_v1_formats; }; struct wlr_wl_buffer { struct wlr_buffer *buffer; struct wl_buffer *wl_buffer; + bool released; + struct wl_list link; // wlr_wl_backend.buffers + struct wl_listener buffer_destroy; }; struct wlr_wl_presentation_feedback { @@ -70,15 +72,17 @@ struct wlr_wl_output { struct xdg_surface *xdg_surface; struct xdg_toplevel *xdg_toplevel; struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1; - struct wl_egl_window *egl_window; - EGLSurface egl_surface; struct wl_list presentation_feedbacks; + struct wlr_swapchain *swapchain; + struct wlr_buffer *back_buffer; + uint32_t enter_serial; struct { + struct wlr_wl_pointer *pointer; struct wl_surface *surface; - struct wl_egl_window *egl_window; + struct wlr_swapchain *swapchain; int32_t hotspot_x, hotspot_y; int32_t width, height; } cursor; @@ -89,6 +93,7 @@ struct wlr_wl_input_device { uint32_t fingers; struct wlr_wl_backend *backend; + struct wlr_wl_seat *seat; void *resource; }; @@ -107,18 +112,35 @@ struct wlr_wl_pointer { struct wl_listener output_destroy; }; +struct wlr_wl_seat { + struct wl_seat *wl_seat; + + struct wl_list link; // wlr_wl_backend.seats + char *name; + struct wl_touch *touch; + struct wl_pointer *pointer; + struct wl_keyboard *keyboard; + + struct wlr_wl_backend *backend; + struct wlr_wl_pointer *active_pointer; +}; + 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_pointer(struct wlr_wl_seat *seat, struct wlr_wl_output *output); +void create_wl_keyboard(struct wlr_wl_seat *seat); +void create_wl_touch(struct wlr_wl_seat *seat); struct wlr_wl_input_device *create_wl_input_device( - struct wlr_wl_backend *backend, enum wlr_input_device_type type); + struct wlr_wl_seat *seat, 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); +void destroy_wl_buffer(struct wlr_wl_buffer *buffer); extern const struct wl_seat_listener seat_listener; struct wlr_wl_tablet_seat *wl_add_tablet_seat( struct zwp_tablet_manager_v2 *manager, - struct wl_seat *seat, struct wlr_wl_backend *backend); + struct wlr_wl_seat *seat); #endif diff --git a/include/backend/x11.h b/include/backend/x11.h index 549f6a77e..7ffcf465d 100644 --- a/include/backend/x11.h +++ b/include/backend/x11.h @@ -1,16 +1,26 @@ #ifndef BACKEND_X11_H #define BACKEND_X11_H +#include + #include #include #include #include +#include + +#if WLR_HAS_XCB_ERRORS +#include +#endif #include -#include #include +#include #include +#include +#include +#include #include #include @@ -26,7 +36,9 @@ struct wlr_x11_output { struct wl_list link; // wlr_x11_backend::outputs xcb_window_t win; - EGLSurface surf; + + struct wlr_swapchain *swapchain; + struct wlr_buffer *back_buffer; struct wlr_pointer pointer; struct wlr_input_device pointer_dev; @@ -38,6 +50,8 @@ struct wlr_x11_output { struct wl_event_source *frame_timer; int frame_delay; + struct wl_list buffers; // wlr_x11_buffer::link + bool cursor_hidden; }; @@ -55,6 +69,10 @@ struct wlr_x11_backend { Display *xlib_conn; xcb_connection_t *xcb; xcb_screen_t *screen; + xcb_depth_t *depth; + xcb_visualid_t visualid; + xcb_colormap_t colormap; + xcb_present_event_t present_event_id; size_t requested_outputs; size_t last_output_num; @@ -65,6 +83,10 @@ struct wlr_x11_backend { struct wlr_egl egl; struct wlr_renderer *renderer; + struct wlr_drm_format_set dri3_formats; + const struct wlr_x11_format *x11_format; + struct wlr_drm_format *drm_format; + struct wlr_allocator *allocator; struct wl_event_source *event_source; struct { @@ -78,11 +100,29 @@ struct wlr_x11_backend { // The time we last received an event xcb_timestamp_t time; +#if WLR_HAS_XCB_ERRORS + xcb_errors_context_t *errors_context; +#endif + + uint8_t present_opcode; uint8_t xinput_opcode; struct wl_listener display_destroy; }; +struct wlr_x11_buffer { + struct wlr_x11_backend *x11; + struct wlr_buffer *buffer; + xcb_pixmap_t pixmap; + struct wl_list link; // wlr_x11_output::buffers + struct wl_listener buffer_destroy; +}; + +struct wlr_x11_format { + uint32_t drm; + uint8_t depth, bpp; +}; + struct wlr_x11_backend *get_x11_backend_from_backend( struct wlr_backend *wlr_backend); struct wlr_x11_output *get_x11_output_from_window_id( @@ -100,5 +140,7 @@ void update_x11_pointer_position(struct wlr_x11_output *output, void handle_x11_configure_notify(struct wlr_x11_output *output, xcb_configure_notify_event_t *event); +void handle_x11_present_event(struct wlr_x11_backend *x11, + xcb_ge_generic_event_t *event); #endif diff --git a/include/render/allocator.h b/include/render/allocator.h new file mode 100644 index 000000000..d47184af1 --- /dev/null +++ b/include/render/allocator.h @@ -0,0 +1,42 @@ +#ifndef RENDER_ALLOCATOR +#define RENDER_ALLOCATOR + +#include +#include +#include +#include + +struct wlr_allocator; + +struct wlr_allocator_interface { + struct wlr_buffer *(*create_buffer)(struct wlr_allocator *alloc, + int width, int height, const struct wlr_drm_format *format); + void (*destroy)(struct wlr_allocator *alloc); +}; + +struct wlr_allocator { + const struct wlr_allocator_interface *impl; + + struct { + struct wl_signal destroy; + } events; +}; + +/** + * Destroy the allocator. + */ +void wlr_allocator_destroy(struct wlr_allocator *alloc); +/** + * Allocate a new buffer. + * + * When the caller is done with it, they must unreference it by calling + * wlr_buffer_drop. + */ +struct wlr_buffer *wlr_allocator_create_buffer(struct wlr_allocator *alloc, + int width, int height, const struct wlr_drm_format *format); + +// For wlr_allocator implementors +void wlr_allocator_init(struct wlr_allocator *alloc, + const struct wlr_allocator_interface *impl); + +#endif diff --git a/include/render/drm_format_set.h b/include/render/drm_format_set.h new file mode 100644 index 000000000..c438722bc --- /dev/null +++ b/include/render/drm_format_set.h @@ -0,0 +1,19 @@ +#ifndef RENDER_DRM_FORMAT_SET_H +#define RENDER_DRM_FORMAT_SET_H + +#include + +struct wlr_drm_format *wlr_drm_format_create(uint32_t format); +bool wlr_drm_format_add(struct wlr_drm_format **fmt_ptr, uint64_t modifier); +struct wlr_drm_format *wlr_drm_format_dup(const struct wlr_drm_format *format); +/** + * Intersect modifiers for two DRM formats. + * + * Both arguments must have the same format field. If the formats aren't + * compatible, NULL is returned. If either format doesn't support any modifier, + * a format that doesn't support any modifier is returned. + */ +struct wlr_drm_format *wlr_drm_format_intersect( + const struct wlr_drm_format *a, const struct wlr_drm_format *b); + +#endif diff --git a/include/render/gbm_allocator.h b/include/render/gbm_allocator.h new file mode 100644 index 000000000..f4627b905 --- /dev/null +++ b/include/render/gbm_allocator.h @@ -0,0 +1,29 @@ +#ifndef RENDER_GBM_ALLOCATOR_H +#define RENDER_GBM_ALLOCATOR_H + +#include +#include +#include "render/allocator.h" + +struct wlr_gbm_buffer { + struct wlr_buffer base; + + struct gbm_bo *gbm_bo; + struct wlr_dmabuf_attributes dmabuf; +}; + +struct wlr_gbm_allocator { + struct wlr_allocator base; + + int fd; + struct gbm_device *gbm_device; +}; + +/** + * Creates a new GBM allocator from a render FD. + * + * Takes ownership over the FD. + */ +struct wlr_gbm_allocator *wlr_gbm_allocator_create(int render_fd); + +#endif diff --git a/include/render/gles2.h b/include/render/gles2.h index 6d25bea05..a2173fbb5 100644 --- a/include/render/gles2.h +++ b/include/render/gles2.h @@ -14,17 +14,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; @@ -46,6 +35,7 @@ struct wlr_gles2_renderer { struct wlr_renderer wlr_renderer; struct wlr_egl *egl; + int drm_fd; const char *exts_str; struct { @@ -55,6 +45,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; @@ -74,12 +73,27 @@ struct wlr_gles2_renderer { struct wlr_gles2_tex_shader tex_ext; } shaders; + struct wl_list buffers; // wlr_gles2_buffer.link + + struct wlr_gles2_buffer *current_buffer; uint32_t viewport_width, viewport_height; }; +struct wlr_gles2_buffer { + struct wlr_buffer *buffer; + struct wlr_gles2_renderer *renderer; + struct wl_list link; // wlr_gles2_renderer.buffers + + EGLImageKHR image; + GLuint rbo; + GLuint fbo; + + struct wl_listener buffer_destroy; +}; + struct wlr_gles2_texture { struct wlr_texture wlr_texture; - struct wlr_egl *egl; + struct wlr_gles2_renderer *renderer; // Basically: // GL_TEXTURE_2D == mutable @@ -102,12 +116,22 @@ 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); #endif diff --git a/include/render/swapchain.h b/include/render/swapchain.h new file mode 100644 index 000000000..243f0404c --- /dev/null +++ b/include/render/swapchain.h @@ -0,0 +1,50 @@ +#ifndef RENDER_SWAPCHAIN_H +#define RENDER_SWAPCHAIN_H + +#include +#include +#include + +#define WLR_SWAPCHAIN_CAP 4 + +struct wlr_swapchain_slot { + struct wlr_buffer *buffer; + bool acquired; // waiting for release + int age; + + struct wl_listener release; +}; + +struct wlr_swapchain { + struct wlr_allocator *allocator; // NULL if destroyed + + int width, height; + struct wlr_drm_format *format; + + struct wlr_swapchain_slot slots[WLR_SWAPCHAIN_CAP]; + + struct wl_listener allocator_destroy; +}; + +struct wlr_swapchain *wlr_swapchain_create( + struct wlr_allocator *alloc, int width, int height, + const struct wlr_drm_format *format); +void wlr_swapchain_destroy(struct wlr_swapchain *swapchain); +/** + * Acquire a buffer from the swap chain. + * + * The returned buffer is locked. When the caller is done with it, they must + * unlock it by calling wlr_buffer_unlock. + */ +struct wlr_buffer *wlr_swapchain_acquire(struct wlr_swapchain *swapchain, + int *age); +/** + * Mark the buffer as submitted for presentation. This needs to be called by + * swap chain users on frame boundaries. + * + * If the buffer hasn't been created via the swap chain, the call is ignored. + */ +void wlr_swapchain_set_buffer_submitted(struct wlr_swapchain *swapchain, + struct wlr_buffer *buffer); + +#endif diff --git a/include/render/wlr_renderer.h b/include/render/wlr_renderer.h new file mode 100644 index 000000000..8e113ac8b --- /dev/null +++ b/include/render/wlr_renderer.h @@ -0,0 +1,14 @@ +#ifndef RENDER_WLR_RENDERER_H +#define RENDER_WLR_RENDERER_H + +#include + +bool wlr_renderer_bind_buffer(struct wlr_renderer *r, struct wlr_buffer *buffer); +/** + * Get the DMA-BUF formats supporting rendering usage. Buffers allocated with + * a format from this list may be attached via wlr_renderer_bind_buffer. + */ +const struct wlr_drm_format_set *wlr_renderer_get_dmabuf_render_formats( + struct wlr_renderer *renderer); + +#endif 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/util/time.h b/include/util/time.h index 6b35ef5f7..287698dec 100644 --- a/include/util/time.h +++ b/include/util/time.h @@ -13,6 +13,11 @@ uint32_t get_current_time_msec(void); */ int64_t timespec_to_msec(const struct timespec *a); +/** + * Convert nanoseconds to a timespec. + */ +void timespec_from_nsec(struct timespec *r, int64_t nsec); + /** * Subtracts timespec `b` from timespec `a`, and stores the difference in `r`. */ diff --git a/include/wlr/backend/drm.h b/include/wlr/backend/drm.h index c1380323a..cd0f3405d 100644 --- a/include/wlr/backend/drm.h +++ b/include/wlr/backend/drm.h @@ -22,7 +22,8 @@ * a DRM backend, other kinds of backends raise SIGABRT). */ struct wlr_backend *wlr_drm_backend_create(struct wl_display *display, - struct wlr_session *session, int gpu_fd, struct wlr_backend *parent, + struct wlr_session *session, struct wlr_device *dev, + struct wlr_backend *parent, wlr_renderer_create_func_t create_renderer_func); bool wlr_backend_is_drm(struct wlr_backend *backend); diff --git a/include/wlr/backend/session.h b/include/wlr/backend/session.h index 36b80a8d6..06c23d737 100644 --- a/include/wlr/backend/session.h +++ b/include/wlr/backend/session.h @@ -11,9 +11,11 @@ struct session_impl; struct wlr_device { int fd; dev_t dev; - struct wl_signal signal; - struct wl_list link; + + struct { + struct wl_signal change; + } events; }; struct wlr_session { @@ -22,7 +24,6 @@ struct wlr_session { * Signal for when the session becomes active/inactive. * It's called when we swap virtual terminal. */ - struct wl_signal session_signal; bool active; /* @@ -38,13 +39,20 @@ struct wlr_session { struct wl_list devices; + struct wl_display *display; struct wl_listener display_destroy; struct { + struct wl_signal active; + struct wl_signal add_drm_card; // struct wlr_session_add_event struct wl_signal destroy; } events; }; +struct wlr_session_add_event { + const char *path; +}; + /* * Opens a session, taking control of the current virtual terminal. * This should not be called if another program is already in control @@ -74,21 +82,21 @@ void wlr_session_destroy(struct wlr_session *session); * * Returns -errno on error. */ -int wlr_session_open_file(struct wlr_session *session, const char *path); +struct wlr_device *wlr_session_open_file(struct wlr_session *session, + const char *path); /* * Closes a file previously opened with wlr_session_open_file. */ -void wlr_session_close_file(struct wlr_session *session, int fd); +void wlr_session_close_file(struct wlr_session *session, + struct wlr_device *device); -void wlr_session_signal_add(struct wlr_session *session, int fd, - struct wl_listener *listener); /* * Changes the virtual terminal. */ bool wlr_session_change_vt(struct wlr_session *session, unsigned vt); size_t wlr_session_find_gpus(struct wlr_session *session, - size_t ret_len, int *ret); + size_t ret_len, struct wlr_device **ret); #endif diff --git a/include/wlr/config.h.in b/include/wlr/config.h.in index a7691ff91..c139a4d61 100644 --- a/include/wlr/config.h.in +++ b/include/wlr/config.h.in @@ -1,13 +1,11 @@ #ifndef WLR_CONFIG_H #define WLR_CONFIG_H -#mesondefine WLR_HAS_EGLMESAEXT_H - -#mesondefine WLR_HAS_LIBCAP - #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..114dd9c78 100644 --- a/include/wlr/render/egl.h +++ b/include/wlr/render/egl.h @@ -15,15 +15,14 @@ #ifndef EGL_NO_X11 #define EGL_NO_X11 #endif +#ifndef EGL_NO_PLATFORM_SPECIFIC_TYPES +#define EGL_NO_PLATFORM_SPECIFIC_TYPES +#endif #include #include #include -#if WLR_HAS_EGLMESAEXT_H -// TODO: remove eglmesaext.h -#include -#endif #include #include #include @@ -42,8 +41,10 @@ struct wlr_egl { EGLDisplay display; EGLConfig config; EGLContext context; + EGLDeviceEXT device; // may be EGL_NO_DEVICE_EXT struct { + // Display extensions bool bind_wayland_display_wl; bool buffer_age_ext; bool image_base_khr; @@ -51,6 +52,9 @@ struct wlr_egl { bool image_dmabuf_import_ext; bool image_dmabuf_import_modifiers_ext; bool swap_buffers_with_damage; + + // Device extensions + bool device_drm_ext; } exts; struct { @@ -67,12 +71,14 @@ struct wlr_egl { PFNEGLEXPORTDMABUFIMAGEQUERYMESAPROC eglExportDMABUFImageQueryMESA; PFNEGLEXPORTDMABUFIMAGEMESAPROC eglExportDMABUFImageMESA; PFNEGLDEBUGMESSAGECONTROLKHRPROC eglDebugMessageControlKHR; + PFNEGLQUERYDISPLAYATTRIBEXTPROC eglQueryDisplayAttribEXT; + PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT; } procs; struct wl_display *wl_display; - struct wlr_drm_format_set dmabuf_formats; - EGLBoolean **external_only_dmabuf_formats; + struct wlr_drm_format_set dmabuf_texture_formats; + struct wlr_drm_format_set dmabuf_render_formats; }; // TODO: Allocate and return a wlr_egl @@ -117,9 +123,15 @@ EGLImageKHR wlr_egl_create_image_from_dmabuf(struct wlr_egl *egl, struct wlr_dmabuf_attributes *attributes, bool *external_only); /** - * Get the available dmabuf formats + * Get DMA-BUF formats suitable for sampling usage. */ -const struct wlr_drm_format_set *wlr_egl_get_dmabuf_formats(struct wlr_egl *egl); +const struct wlr_drm_format_set *wlr_egl_get_dmabuf_texture_formats( + struct wlr_egl *egl); +/** + * Get DMA-BUF formats suitable for rendering usage. + */ +const struct wlr_drm_format_set *wlr_egl_get_dmabuf_render_formats( + struct wlr_egl *egl); bool wlr_egl_export_image_to_dmabuf(struct wlr_egl *egl, EGLImageKHR image, int32_t width, int32_t height, uint32_t flags, @@ -161,4 +173,6 @@ bool wlr_egl_swap_buffers(struct wlr_egl *egl, EGLSurface surface, bool wlr_egl_destroy_surface(struct wlr_egl *egl, EGLSurface surface); +int wlr_egl_dup_drm_fd(struct wlr_egl *egl); + #endif 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 93c987b7d..3ce91d326 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 @@ -27,6 +30,8 @@ #include struct wlr_renderer_impl { + bool (*bind_buffer)(struct wlr_renderer *renderer, + struct wlr_buffer *buffer); void (*begin)(struct wlr_renderer *renderer, uint32_t width, uint32_t height); void (*end)(struct wlr_renderer *renderer); @@ -39,15 +44,15 @@ struct wlr_renderer_impl { const float color[static 4], const float matrix[static 9]); void (*render_ellipse_with_matrix)(struct wlr_renderer *renderer, const float color[static 4], const float matrix[static 9]); - const enum wl_shm_format *(*formats)( + const enum wl_shm_format *(*get_shm_texture_formats)( struct wlr_renderer *renderer, size_t *len); - bool (*format_supported)(struct wlr_renderer *renderer, - enum wl_shm_format fmt); bool (*resource_is_wl_drm_buffer)(struct wlr_renderer *renderer, struct wl_resource *resource); void (*wl_drm_buffer_get_size)(struct wlr_renderer *renderer, struct wl_resource *buffer, int *width, int *height); - const struct wlr_drm_format_set *(*get_dmabuf_formats)( + const struct wlr_drm_format_set *(*get_dmabuf_texture_formats)( + struct wlr_renderer *renderer); + const struct wlr_drm_format_set *(*get_dmabuf_render_formats)( struct wlr_renderer *renderer); enum wl_shm_format (*preferred_read_format)(struct wlr_renderer *renderer); bool (*read_pixels)(struct wlr_renderer *renderer, enum wl_shm_format fmt, @@ -67,6 +72,7 @@ struct wlr_renderer_impl { bool (*blit_dmabuf)(struct wlr_renderer *renderer, struct wlr_dmabuf_attributes *dst, struct wlr_dmabuf_attributes *src); + int (*get_drm_fd)(struct wlr_renderer *renderer); }; void wlr_renderer_init(struct wlr_renderer *renderer, diff --git a/include/wlr/render/wlr_renderer.h b/include/wlr/render/wlr_renderer.h index 513f412a1..fecc1a13a 100644 --- a/include/wlr/render/wlr_renderer.h +++ b/include/wlr/render/wlr_renderer.h @@ -21,6 +21,7 @@ enum wlr_renderer_read_pixels_flags { struct wlr_renderer_impl; struct wlr_drm_format_set; +struct wlr_buffer; struct wlr_renderer { const struct wlr_renderer_impl *impl; @@ -35,7 +36,7 @@ struct wlr_renderer { struct wlr_renderer *wlr_renderer_autocreate(struct wlr_egl *egl, EGLenum platform, void *remote_display, EGLint *config_attribs, EGLint visual_id); -void wlr_renderer_begin(struct wlr_renderer *r, int width, int height); +void wlr_renderer_begin(struct wlr_renderer *r, uint32_t width, uint32_t height); void wlr_renderer_end(struct wlr_renderer *r); void wlr_renderer_clear(struct wlr_renderer *r, const float color[static 4]); /** @@ -82,10 +83,11 @@ void wlr_render_ellipse(struct wlr_renderer *r, const struct wlr_box *box, void wlr_render_ellipse_with_matrix(struct wlr_renderer *r, const float color[static 4], const float matrix[static 9]); /** - * Returns a list of pixel formats supported by this renderer. + * Get the shared-memory formats supporting import usage. Buffers allocated + * with a format from this list may be imported via wlr_texture_from_pixels. */ -const enum wl_shm_format *wlr_renderer_get_formats(struct wlr_renderer *r, - size_t *len); +const enum wl_shm_format *wlr_renderer_get_shm_texture_formats( + struct wlr_renderer *r, size_t *len); /** * Returns true if this wl_buffer is a wl_drm buffer. */ @@ -97,9 +99,10 @@ bool wlr_renderer_resource_is_wl_drm_buffer(struct wlr_renderer *renderer, void wlr_renderer_wl_drm_buffer_get_size(struct wlr_renderer *renderer, struct wl_resource *buffer, int *width, int *height); /** - * Get the available DMA-BUF formats. + * Get the DMA-BUF formats supporting sampling usage. Buffers allocated with + * a format from this list may be imported via wlr_texture_from_dmabuf. */ -const struct wlr_drm_format_set *wlr_renderer_get_dmabuf_formats( +const struct wlr_drm_format_set *wlr_renderer_get_dmabuf_texture_formats( struct wlr_renderer *renderer); /** * Reads out of pixels of the currently bound surface into data. `stride` is in @@ -117,11 +120,6 @@ bool wlr_renderer_read_pixels(struct wlr_renderer *r, enum wl_shm_format fmt, */ bool wlr_renderer_blit_dmabuf(struct wlr_renderer *r, struct wlr_dmabuf_attributes *dst, struct wlr_dmabuf_attributes *src); -/** - * Checks if a format is supported. - */ -bool wlr_renderer_format_supported(struct wlr_renderer *r, - enum wl_shm_format fmt); /** * Creates necessary shm and invokes the initialization of the implementation. * @@ -130,6 +128,13 @@ bool wlr_renderer_format_supported(struct wlr_renderer *r, bool wlr_renderer_init_wl_display(struct wlr_renderer *r, struct wl_display *wl_display); +/** + * Obtains the FD of the DRM device used for rendering, or -1 if unavailable. + * + * The caller doesn't have ownership of the FD, it must not close it. + */ +int wlr_renderer_get_drm_fd(struct wlr_renderer *r); + /** * Destroys this wlr_renderer. Textures must be destroyed separately. */ diff --git a/include/wlr/render/wlr_texture.h b/include/wlr/render/wlr_texture.h index 71ce5e7c9..3a4840acd 100644 --- a/include/wlr/render/wlr_texture.h +++ b/include/wlr/render/wlr_texture.h @@ -24,6 +24,9 @@ struct wlr_texture { /** * Create a new texture from raw pixel data. `stride` is in bytes. The returned * texture is mutable. + * + * Should not be called in a rendering block like renderer_begin()/end() or + * between attaching a renderer to an output and committing it. */ struct wlr_texture *wlr_texture_from_pixels(struct wlr_renderer *renderer, enum wl_shm_format wl_fmt, uint32_t stride, uint32_t width, uint32_t height, @@ -32,12 +35,18 @@ struct wlr_texture *wlr_texture_from_pixels(struct wlr_renderer *renderer, /** * Create a new texture from a wl_drm resource. The returned texture is * immutable. + * + * Should not be called in a rendering block like renderer_begin()/end() or + * between attaching a renderer to an output and committing it. */ struct wlr_texture *wlr_texture_from_wl_drm(struct wlr_renderer *renderer, struct wl_resource *data); /** * Create a new texture from a DMA-BUF. The returned texture is immutable. + * + * Should not be called in a rendering block like renderer_begin()/end() or + * between attaching a renderer to an output and committing it. */ struct wlr_texture *wlr_texture_from_dmabuf(struct wlr_renderer *renderer, struct wlr_dmabuf_attributes *attribs); @@ -58,6 +67,9 @@ bool wlr_texture_is_opaque(struct wlr_texture *texture); /** * Update a texture with raw pixels. The texture must be mutable, and the input * data must have the same pixel format that the texture was created with. + * + * Should not be called in a rendering block like renderer_begin()/end() or + * between attaching a renderer to an output and committing it. */ bool wlr_texture_write_pixels(struct wlr_texture *texture, uint32_t stride, uint32_t width, uint32_t height, diff --git a/include/wlr/types/wlr_buffer.h b/include/wlr/types/wlr_buffer.h index 14bb2a8da..845558999 100644 --- a/include/wlr/types/wlr_buffer.h +++ b/include/wlr/types/wlr_buffer.h @@ -103,6 +103,11 @@ struct wlr_client_buffer { struct wlr_renderer; +/** + * Get a client buffer from a generic buffer. If the buffer isn't a client + * buffer, returns NULL. + */ +struct wlr_client_buffer *wlr_client_buffer_get(struct wlr_buffer *buffer); /** * Check if a resource is a wl_buffer resource. */ 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_input_device.h b/include/wlr/types/wlr_input_device.h index 214620232..8ed9b4659 100644 --- a/include/wlr/types/wlr_input_device.h +++ b/include/wlr/types/wlr_input_device.h @@ -9,6 +9,8 @@ #ifndef WLR_TYPES_WLR_INPUT_DEVICE_H #define WLR_TYPES_WLR_INPUT_DEVICE_H +#include + enum wlr_button_state { WLR_BUTTON_RELEASED, WLR_BUTTON_PRESSED, @@ -23,14 +25,6 @@ enum wlr_input_device_type { WLR_INPUT_DEVICE_SWITCH, }; -/* Note: these are circular dependencies */ -#include -#include -#include -#include -#include -#include - struct wlr_input_device_impl; struct wlr_input_device { diff --git a/include/wlr/types/wlr_keyboard.h b/include/wlr/types/wlr_keyboard.h index c3e360548..b51114628 100644 --- a/include/wlr/types/wlr_keyboard.h +++ b/include/wlr/types/wlr_keyboard.h @@ -12,28 +12,28 @@ #include #include #include -#include +#include #include #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 @@ -91,16 +91,11 @@ struct wlr_keyboard { void *data; }; -enum wlr_key_state { - WLR_KEY_RELEASED, - WLR_KEY_PRESSED, -}; - struct wlr_event_keyboard_key { uint32_t time_msec; uint32_t keycode; bool update_state; // if backend doesn't update modifiers on its own - enum wlr_key_state state; + enum wl_keyboard_key_state state; }; bool wlr_keyboard_set_keymap(struct wlr_keyboard *kb, 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 c9c52b175..ae79598b8 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -164,9 +164,11 @@ 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 + // Emitted after a client bound the wl_output global + struct wl_signal bind; // wlr_output_event_bind struct wl_signal enable; struct wl_signal mode; struct wl_signal scale; @@ -199,6 +201,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. @@ -228,6 +236,11 @@ struct wlr_output_event_present { uint32_t flags; // enum wlr_output_present_flag }; +struct wlr_output_event_bind { + struct wlr_output *output; + struct wl_resource *resource; +}; + struct wlr_surface; /** @@ -342,8 +355,7 @@ bool wlr_output_preferred_read_format(struct wlr_output *output, * the screen that has changed since the last frame. * * Compositors implementing damage tracking should call this function with the - * damaged region in output-buffer-local coordinates (ie. scaled and - * transformed). + * damaged region in output-buffer-local coordinates. * * This region is not to be confused with the renderer's buffer damage, ie. the * region compositors need to repaint. Compositors usually need to repaint more @@ -392,8 +404,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_pointer.h b/include/wlr/types/wlr_pointer.h index 696990429..ac168d600 100644 --- a/include/wlr/types/wlr_pointer.h +++ b/include/wlr/types/wlr_pointer.h @@ -11,6 +11,7 @@ #include #include +#include #include struct wlr_pointer_impl; 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 332ab0d30..5094874c6 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #define WLR_SERIAL_RINGSET_SIZE 128 diff --git a/include/wlr/types/wlr_surface.h b/include/wlr/types/wlr_surface.h index 2cd4ac5db..abb056002 100644 --- a/include/wlr/types/wlr_surface.h +++ b/include/wlr/types/wlr_surface.h @@ -67,6 +67,15 @@ struct wlr_surface_role { void (*precommit)(struct wlr_surface *surface); }; +struct wlr_surface_output { + struct wlr_surface *surface; + struct wlr_output *output; + + struct wl_list link; // wlr_surface::current_outputs + struct wl_listener bind; + struct wl_listener destroy; +}; + struct wlr_surface { struct wl_resource *resource; struct wlr_renderer *renderer; @@ -126,6 +135,8 @@ struct wlr_surface { // wlr_subsurface::parent_pending_link struct wl_list subsurface_pending_list; + struct wl_list current_outputs; // wlr_surface_output::link + struct wl_listener renderer_destroy; void *data; 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..fd14ff254 100644 --- a/include/wlr/xwayland.h +++ b/include/wlr/xwayland.h @@ -22,7 +22,7 @@ struct wlr_xwayland_cursor; struct wlr_xwayland_server { pid_t pid; struct wl_client *client; - struct wl_event_source *sigusr1_source; + struct wl_event_source *pipe_source; int wm_fd[2], wl_fd[2]; time_t server_start; @@ -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); @@ -229,9 +236,6 @@ void wlr_xwayland_server_destroy(struct wlr_xwayland_server *server); * * The server supports a lazy mode in which Xwayland is only started when a * client tries to connect. - * - * Note: wlr_xwayland will setup a global SIGUSR1 handler on the compositor - * process. */ struct wlr_xwayland *wlr_xwayland_create(struct wl_display *wl_display, struct wlr_compositor *compositor, bool lazy); @@ -245,11 +249,22 @@ void wlr_xwayland_set_cursor(struct wlr_xwayland *wlr_xwayland, void wlr_xwayland_surface_activate(struct wlr_xwayland_surface *surface, bool activated); +/** + * Restack surface relative to sibling. + * If sibling is NULL, then the surface is moved to the top or the bottom + * of the stack (depending on the mode). + */ +void wlr_xwayland_surface_restack(struct wlr_xwayland_surface *surface, + struct wlr_xwayland_surface *sibling, enum xcb_stack_mode_t mode); + void wlr_xwayland_surface_configure(struct wlr_xwayland_surface *surface, int16_t x, int16_t y, uint16_t width, uint16_t height); 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 4abe63181..aa6598bfe 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'wlroots', 'c', - version: '0.11.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 = 6 +soversion = 7 add_project_arguments([ '-DWLR_USE_UNSTABLE', @@ -82,11 +82,11 @@ 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) conf_data.set10('WLR_HAS_XCB_ICCCM', false) -conf_data.set10('WLR_HAS_EGLMESAEXT_H', false) # Clang complains about some zeroed initializer lists (= {0}), even though they # are valid @@ -97,28 +97,22 @@ endif wayland_server = dependency('wayland-server', version: '>=1.18') wayland_client = dependency('wayland-client') -wayland_egl = dependency('wayland-egl') wayland_protos = dependency('wayland-protocols', version: '>=1.17') egl = dependency('egl') glesv2 = dependency('glesv2') drm = dependency('libdrm', version: '>=2.4.95') gbm = dependency('gbm', version: '>=17.1.0') -libinput = dependency('libinput', version: '>=1.9.0') +libinput = dependency('libinput', version: '>=1.14.0') xkbcommon = dependency('xkbcommon') udev = dependency('libudev') pixman = dependency('pixman-1') math = cc.find_library('m') rt = cc.find_library('rt') -if cc.has_header('EGL/eglmesaext.h', dependencies: egl) - conf_data.set10('WLR_HAS_EGLMESAEXT_H', true) -endif - wlr_files = [] wlr_deps = [ wayland_server, wayland_client, - wayland_egl, wayland_protos, egl, glesv2, @@ -132,13 +126,6 @@ wlr_deps = [ rt, ] -libinput_ver = libinput.version().split('.') -add_project_arguments([ - '-DLIBINPUT_MAJOR=' + libinput_ver[0], - '-DLIBINPUT_MINOR=' + libinput_ver[1], - '-DLIBINPUT_PATCH=' + libinput_ver[2], -], language: 'c') - subdir('protocol') subdir('render') @@ -174,13 +161,14 @@ wlroots = declare_dependency( meson.override_dependency('wlroots', wlroots) summary({ - 'systemd': conf_data.get('WLR_HAS_SYSTEMD', 0), - 'elogind': conf_data.get('WLR_HAS_ELOGIND', 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), - 'xcb-errors': conf_data.get('WLR_HAS_XCB_ERRORS', 0), -}) + 'systemd': conf_data.get('WLR_HAS_SYSTEMD', 0) == 1, + 'elogind': conf_data.get('WLR_HAS_ELOGIND', 0) == 1, + 'libseat': conf_data.get('WLR_HAS_LIBSEAT', 0) == 1, + 'xwayland': conf_data.get('WLR_HAS_XWAYLAND', 0) == 1, + 'x11_backend': conf_data.get('WLR_HAS_X11_BACKEND', 0) == 1, + 'xcb-icccm': conf_data.get('WLR_HAS_XCB_ICCCM', 0) == 1, + 'xcb-errors': conf_data.get('WLR_HAS_XCB_ERRORS', 0) == 1, +}, bool_yn: true) if get_option('examples') subdir('examples') 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/allocator.c b/render/allocator.c new file mode 100644 index 000000000..ea1712e96 --- /dev/null +++ b/render/allocator.c @@ -0,0 +1,23 @@ +#include +#include +#include "render/allocator.h" + +void wlr_allocator_init(struct wlr_allocator *alloc, + const struct wlr_allocator_interface *impl) { + assert(impl && impl->destroy && impl->create_buffer); + alloc->impl = impl; + wl_signal_init(&alloc->events.destroy); +} + +void wlr_allocator_destroy(struct wlr_allocator *alloc) { + if (alloc == NULL) { + return; + } + wl_signal_emit(&alloc->events.destroy, NULL); + alloc->impl->destroy(alloc); +} + +struct wlr_buffer *wlr_allocator_create_buffer(struct wlr_allocator *alloc, + int width, int height, const struct wlr_drm_format *format) { + return alloc->impl->create_buffer(alloc, width, height, format); +} diff --git a/render/drm_format_set.c b/render/drm_format_set.c index cbcc0875f..634d90707 100644 --- a/render/drm_format_set.c +++ b/render/drm_format_set.c @@ -6,6 +6,7 @@ #include #include #include +#include "render/drm_format_set.h" void wlr_drm_format_set_finish(struct wlr_drm_format_set *set) { for (size_t i = 0; i < set->len; ++i) { @@ -59,52 +60,18 @@ bool wlr_drm_format_set_has(const struct wlr_drm_format_set *set, bool wlr_drm_format_set_add(struct wlr_drm_format_set *set, uint32_t format, uint64_t modifier) { assert(format != DRM_FORMAT_INVALID); + struct wlr_drm_format **ptr = format_set_get_ref(set, format); - if (ptr) { - struct wlr_drm_format *fmt = *ptr; - - if (modifier == DRM_FORMAT_MOD_INVALID) { - return true; - } - - for (size_t i = 0; i < fmt->len; ++i) { - if (fmt->modifiers[i] == modifier) { - return true; - } - } - - if (fmt->len == fmt->cap) { - size_t cap = fmt->cap ? fmt->cap * 2 : 4; - - fmt = realloc(fmt, sizeof(*fmt) + sizeof(fmt->modifiers[0]) * cap); - if (!fmt) { - wlr_log_errno(WLR_ERROR, "Allocation failed"); - return false; - } - - fmt->cap = cap; - *ptr = fmt; - } - - fmt->modifiers[fmt->len++] = modifier; - return true; + return wlr_drm_format_add(ptr, modifier); } - size_t cap = modifier != DRM_FORMAT_MOD_INVALID ? 4 : 0; - - struct wlr_drm_format *fmt = - calloc(1, sizeof(*fmt) + sizeof(fmt->modifiers[0]) * cap); + struct wlr_drm_format *fmt = wlr_drm_format_create(format); if (!fmt) { - wlr_log_errno(WLR_ERROR, "Allocation failed"); return false; } - - fmt->format = format; - if (cap) { - fmt->cap = cap; - fmt->len = 1; - fmt->modifiers[0] = modifier; + if (!wlr_drm_format_add(&fmt, modifier)) { + return false; } if (set->len == set->cap) { @@ -125,3 +92,94 @@ bool wlr_drm_format_set_add(struct wlr_drm_format_set *set, uint32_t format, set->formats[set->len++] = fmt; return true; } + +struct wlr_drm_format *wlr_drm_format_create(uint32_t format) { + size_t cap = 4; + struct wlr_drm_format *fmt = + calloc(1, sizeof(*fmt) + sizeof(fmt->modifiers[0]) * cap); + if (!fmt) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + return NULL; + } + fmt->format = format; + fmt->cap = cap; + return fmt; +} + +bool wlr_drm_format_add(struct wlr_drm_format **fmt_ptr, uint64_t modifier) { + struct wlr_drm_format *fmt = *fmt_ptr; + + if (modifier == DRM_FORMAT_MOD_INVALID) { + return true; + } + + for (size_t i = 0; i < fmt->len; ++i) { + if (fmt->modifiers[i] == modifier) { + return true; + } + } + + if (fmt->len == fmt->cap) { + size_t cap = fmt->cap ? fmt->cap * 2 : 4; + + fmt = realloc(fmt, sizeof(*fmt) + sizeof(fmt->modifiers[0]) * cap); + if (!fmt) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + return false; + } + + fmt->cap = cap; + *fmt_ptr = fmt; + } + + fmt->modifiers[fmt->len++] = modifier; + return true; +} + +struct wlr_drm_format *wlr_drm_format_dup(const struct wlr_drm_format *format) { + assert(format->len <= format->cap); + size_t format_size = sizeof(struct wlr_drm_format) + + format->cap * sizeof(format->modifiers[0]); + struct wlr_drm_format *duped_format = malloc(format_size); + if (duped_format == NULL) { + return NULL; + } + memcpy(duped_format, format, format_size); + return duped_format; +} + +struct wlr_drm_format *wlr_drm_format_intersect( + const struct wlr_drm_format *a, const struct wlr_drm_format *b) { + assert(a->format == b->format); + + size_t format_cap = a->len < b->len ? a->len : b->len; + size_t format_size = sizeof(struct wlr_drm_format) + + format_cap * sizeof(a->modifiers[0]); + struct wlr_drm_format *format = calloc(1, format_size); + if (format == NULL) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + return NULL; + } + format->format = a->format; + format->cap = format_cap; + + for (size_t i = 0; i < a->len; i++) { + for (size_t j = 0; j < b->len; j++) { + if (a->modifiers[i] == b->modifiers[j]) { + assert(format->len < format->cap); + format->modifiers[format->len] = a->modifiers[i]; + format->len++; + break; + } + } + } + + // If both formats support modifiers, but the intersection is empty, then + // the formats aren't compatible with each other + if (format->len == 0 && a->len > 0 && b->len > 0) { + free(format); + return NULL; + } + + return format; +} diff --git a/render/egl.c b/render/egl.c index f99b37f90..8914801bd 100644 --- a/render/egl.c +++ b/render/egl.c @@ -1,10 +1,14 @@ +#define _POSIX_C_SOURCE 200809L #include #include +#include #include #include +#include #include #include #include +#include static bool egl_get_config(EGLDisplay disp, const EGLint *attribs, EGLConfig *out, EGLint visual_id) { @@ -71,6 +75,8 @@ static const char *egl_error_str(EGLint error) { return "EGL_BAD_CURRENT_SURFACE"; case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY"; + case EGL_BAD_DEVICE_EXT: + return "EGL_BAD_DEVICE_EXT"; case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE"; case EGL_BAD_MATCH: @@ -132,13 +138,6 @@ static void init_dmabuf_formats(struct wlr_egl *egl) { return; } - egl->external_only_dmabuf_formats = calloc(formats_len, sizeof(EGLBoolean *)); - if (egl->external_only_dmabuf_formats == NULL) { - wlr_log_errno(WLR_ERROR, "Allocation failed"); - goto out; - } - - size_t external_formats_len = 0; for (int i = 0; i < formats_len; i++) { uint32_t fmt = formats[i]; @@ -151,17 +150,23 @@ static void init_dmabuf_formats(struct wlr_egl *egl) { } if (modifiers_len == 0) { - wlr_drm_format_set_add(&egl->dmabuf_formats, fmt, DRM_FORMAT_MOD_INVALID); + wlr_drm_format_set_add(&egl->dmabuf_texture_formats, fmt, + DRM_FORMAT_MOD_INVALID); + wlr_drm_format_set_add(&egl->dmabuf_render_formats, fmt, + DRM_FORMAT_MOD_INVALID); } for (int j = 0; j < modifiers_len; j++) { - wlr_drm_format_set_add(&egl->dmabuf_formats, fmt, modifiers[j]); + wlr_drm_format_set_add(&egl->dmabuf_texture_formats, fmt, + modifiers[j]); + if (!external_only[j]) { + wlr_drm_format_set_add(&egl->dmabuf_render_formats, fmt, + modifiers[j]); + } } free(modifiers); - - egl->external_only_dmabuf_formats[external_formats_len] = external_only; - external_formats_len++; + free(external_only); } char *str_formats = malloc(formats_len * 5 + 1); @@ -289,14 +294,53 @@ bool wlr_egl_init(struct wlr_egl *egl, EGLenum platform, void *remote_display, "eglQueryWaylandBufferWL"); } - if (!egl_get_config(egl->display, config_attribs, &egl->config, visual_id)) { - wlr_log(WLR_ERROR, "Failed to get EGL config"); - goto error; + const char *device_exts_str = NULL; + if (check_egl_ext(client_exts_str, "EGL_EXT_device_query")) { + load_egl_proc(&egl->procs.eglQueryDisplayAttribEXT, + "eglQueryDisplayAttribEXT"); + load_egl_proc(&egl->procs.eglQueryDeviceStringEXT, + "eglQueryDeviceStringEXT"); + + EGLAttrib device_attrib; + if (!egl->procs.eglQueryDisplayAttribEXT(egl->display, + EGL_DEVICE_EXT, &device_attrib)) { + wlr_log(WLR_ERROR, "eglQueryDisplayAttribEXT(EGL_DEVICE_EXT) failed"); + goto error; + } + egl->device = (EGLDeviceEXT)device_attrib; + + device_exts_str = + egl->procs.eglQueryDeviceStringEXT(egl->device, EGL_EXTENSIONS); + if (device_exts_str == NULL) { + wlr_log(WLR_ERROR, "eglQueryDeviceStringEXT(EGL_EXTENSIONS) failed"); + goto error; + } + + egl->exts.device_drm_ext = + check_egl_ext(device_exts_str, "EGL_EXT_device_drm"); + } + + if (config_attribs != NULL) { + if (!egl_get_config(egl->display, config_attribs, &egl->config, visual_id)) { + wlr_log(WLR_ERROR, "Failed to get EGL config"); + goto error; + } + } else { + if (!check_egl_ext(display_exts_str, "EGL_KHR_no_config_context") && + !check_egl_ext(display_exts_str, "EGL_MESA_configless_context")) { + wlr_log(WLR_ERROR, "EGL_KHR_no_config_context or " + "EGL_MESA_configless_context not supported"); + goto error; + } + egl->config = EGL_NO_CONFIG_KHR; } wlr_log(WLR_INFO, "Using EGL %d.%d", (int)major, (int)minor); wlr_log(WLR_INFO, "Supported EGL client extensions: %s", client_exts_str); wlr_log(WLR_INFO, "Supported EGL display extensions: %s", display_exts_str); + if (device_exts_str != NULL) { + wlr_log(WLR_INFO, "Supported EGL device extensions: %s", device_exts_str); + } wlr_log(WLR_INFO, "EGL vendor: %s", eglQueryString(egl->display, EGL_VENDOR)); init_dmabuf_formats(egl); @@ -357,12 +401,8 @@ void wlr_egl_finish(struct wlr_egl *egl) { return; } - for (size_t i = 0; i < egl->dmabuf_formats.len; i++) { - free(egl->external_only_dmabuf_formats[i]); - } - free(egl->external_only_dmabuf_formats); - - wlr_drm_format_set_finish(&egl->dmabuf_formats); + wlr_drm_format_set_finish(&egl->dmabuf_render_formats); + wlr_drm_format_set_finish(&egl->dmabuf_texture_formats); eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); if (egl->wl_display) { @@ -561,27 +601,6 @@ EGLImageKHR wlr_egl_create_image_from_wl_drm(struct wlr_egl *egl, EGL_WAYLAND_BUFFER_WL, data, attribs); } -static bool dmabuf_format_is_external_only(struct wlr_egl *egl, - uint32_t format, uint64_t modifier) { - for (size_t i = 0; i < egl->dmabuf_formats.len; i++) { - struct wlr_drm_format *fmt = egl->dmabuf_formats.formats[i]; - if (fmt->format == format) { - if (egl->external_only_dmabuf_formats[i] == NULL) { - break; - } - for (size_t j = 0; j < fmt->len; j++) { - if (fmt->modifiers[j] == modifier) { - return egl->external_only_dmabuf_formats[i][j]; - } - } - break; - } - } - // No info, we're doomed. Choose GL_TEXTURE_EXTERNAL_OES and hope for the - // best. - return true; -} - EGLImageKHR wlr_egl_create_image_from_dmabuf(struct wlr_egl *egl, struct wlr_dmabuf_attributes *attributes, bool *external_only) { if (!egl->exts.image_base_khr || !egl->exts.image_dmabuf_import_ext) { @@ -671,7 +690,7 @@ EGLImageKHR wlr_egl_create_image_from_dmabuf(struct wlr_egl *egl, return EGL_NO_IMAGE_KHR; } - *external_only = dmabuf_format_is_external_only(egl, + *external_only = !wlr_drm_format_set_has(&egl->dmabuf_render_formats, attributes->format, attributes->modifier); return image; } @@ -765,13 +784,20 @@ static int get_egl_dmabuf_modifiers(struct wlr_egl *egl, int format, *modifiers, *external_only, &num)) { wlr_log(WLR_ERROR, "Failed to query dmabuf modifiers"); free(*modifiers); + free(*external_only); return -1; } return num; } -const struct wlr_drm_format_set *wlr_egl_get_dmabuf_formats(struct wlr_egl *egl) { - return &egl->dmabuf_formats; +const struct wlr_drm_format_set *wlr_egl_get_dmabuf_texture_formats( + struct wlr_egl *egl) { + return &egl->dmabuf_texture_formats; +} + +const struct wlr_drm_format_set *wlr_egl_get_dmabuf_render_formats( + struct wlr_egl *egl) { + return &egl->dmabuf_render_formats; } bool wlr_egl_export_image_to_dmabuf(struct wlr_egl *egl, EGLImageKHR image, @@ -818,3 +844,91 @@ bool wlr_egl_destroy_surface(struct wlr_egl *egl, EGLSurface surface) { } return eglDestroySurface(egl->display, surface); } + +static bool device_has_name(const drmDevice *device, const char *name) { + for (size_t i = 0; i < DRM_NODE_MAX; i++) { + if (!(device->available_nodes & (1 << i))) { + continue; + } + if (strcmp(device->nodes[i], name) == 0) { + return true; + } + } + return false; +} + +static char *get_render_name(const char *name) { + uint32_t flags = 0; + int devices_len = drmGetDevices2(flags, NULL, 0); + if (devices_len < 0) { + wlr_log(WLR_ERROR, "drmGetDevices2 failed"); + return NULL; + } + drmDevice **devices = calloc(devices_len, sizeof(drmDevice *)); + if (devices == NULL) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + return NULL; + } + devices_len = drmGetDevices2(flags, devices, devices_len); + if (devices_len < 0) { + free(devices); + wlr_log(WLR_ERROR, "drmGetDevices2 failed"); + return NULL; + } + + const drmDevice *match = NULL; + for (int i = 0; i < devices_len; i++) { + if (device_has_name(devices[i], name)) { + match = devices[i]; + break; + } + } + + char *render_name = NULL; + if (match == NULL) { + wlr_log(WLR_ERROR, "Cannot find DRM device %s", name); + } else if (!(match->available_nodes & (1 << DRM_NODE_RENDER))) { + wlr_log(WLR_ERROR, "DRM device %s has no render node", name); + } else { + render_name = strdup(match->nodes[DRM_NODE_RENDER]); + } + + for (int i = 0; i < devices_len; i++) { + drmFreeDevice(&devices[i]); + } + free(devices); + + return render_name; +} + +int wlr_egl_dup_drm_fd(struct wlr_egl *egl) { + if (egl->device == EGL_NO_DEVICE_EXT || !egl->exts.device_drm_ext) { + return -1; + } + + const char *primary_name = egl->procs.eglQueryDeviceStringEXT(egl->device, + EGL_DRM_DEVICE_FILE_EXT); + if (primary_name == NULL) { + wlr_log(WLR_ERROR, + "eglQueryDeviceStringEXT(EGL_DRM_DEVICE_FILE_EXT) failed"); + return -1; + } + + char *render_name = get_render_name(primary_name); + if (render_name == NULL) { + wlr_log(WLR_ERROR, "Can't find render node name for device %s", + primary_name); + return -1; + } + + int render_fd = open(render_name, O_RDWR | O_NONBLOCK | O_CLOEXEC); + if (render_fd < 0) { + wlr_log_errno(WLR_ERROR, "Failed to open DRM render node %s", + render_name); + free(render_name); + return -1; + } + free(render_name); + + return render_fd; +} diff --git a/render/gbm_allocator.c b/render/gbm_allocator.c new file mode 100644 index 000000000..da29501ae --- /dev/null +++ b/render/gbm_allocator.c @@ -0,0 +1,182 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include +#include "render/gbm_allocator.h" + +static const struct wlr_buffer_impl buffer_impl; + +static struct wlr_gbm_buffer *get_gbm_buffer_from_buffer( + struct wlr_buffer *buffer) { + assert(buffer->impl == &buffer_impl); + return (struct wlr_gbm_buffer *)buffer; +} + +static struct wlr_gbm_buffer *create_buffer(struct gbm_device *gbm_device, + int width, int height, const struct wlr_drm_format *format) { + struct gbm_bo *bo = NULL; + if (format->len > 0) { + bo = gbm_bo_create_with_modifiers(gbm_device, width, height, + format->format, format->modifiers, format->len); + } + if (bo == NULL) { + uint32_t usage = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING; + if (format->len == 1 && + format->modifiers[0] == DRM_FORMAT_MOD_LINEAR) { + usage |= GBM_BO_USE_LINEAR; + } + bo = gbm_bo_create(gbm_device, width, height, format->format, usage); + } + if (bo == NULL) { + wlr_log(WLR_ERROR, "gbm_bo_create failed"); + return NULL; + } + + struct wlr_gbm_buffer *buffer = calloc(1, sizeof(*buffer)); + if (buffer == NULL) { + gbm_bo_destroy(bo); + return NULL; + } + wlr_buffer_init(&buffer->base, &buffer_impl, width, height); + buffer->gbm_bo = bo; + + wlr_log(WLR_DEBUG, "Allocated %dx%d GBM buffer (format 0x%"PRIX32", " + "modifier 0x%"PRIX64")", buffer->base.width, buffer->base.height, + gbm_bo_get_format(bo), gbm_bo_get_modifier(bo)); + + return buffer; +} + +static void buffer_destroy(struct wlr_buffer *wlr_buffer) { + struct wlr_gbm_buffer *buffer = + get_gbm_buffer_from_buffer(wlr_buffer); + wlr_dmabuf_attributes_finish(&buffer->dmabuf); + gbm_bo_destroy(buffer->gbm_bo); + free(buffer); +} + +static bool buffer_create_dmabuf(struct wlr_gbm_buffer *buffer) { + assert(buffer->dmabuf.n_planes == 0); + + struct gbm_bo *bo = buffer->gbm_bo; + struct wlr_dmabuf_attributes attribs = {0}; + + attribs.n_planes = gbm_bo_get_plane_count(bo); + if (attribs.n_planes > WLR_DMABUF_MAX_PLANES) { + wlr_log(WLR_ERROR, "GBM BO contains too many planes (%d)", + attribs.n_planes); + return false; + } + + attribs.width = gbm_bo_get_width(bo); + attribs.height = gbm_bo_get_height(bo); + attribs.format = gbm_bo_get_format(bo); + attribs.modifier = gbm_bo_get_modifier(bo); + + int i; + for (i = 0; i < attribs.n_planes; ++i) { + union gbm_bo_handle handle = gbm_bo_get_handle_for_plane(bo, i); + if (handle.s32 < 0) { + wlr_log(WLR_ERROR, "gbm_bo_get_handle_for_plane failed"); + goto error_fd; + } + + int drm_fd = gbm_device_get_fd(gbm_bo_get_device(bo)); + int ret = drmPrimeHandleToFD(drm_fd, handle.s32, + DRM_CLOEXEC, &attribs.fd[i]); + if (ret < 0 || attribs.fd[i] < 0) { + wlr_log_errno(WLR_ERROR, "drmPrimeHandleToFD failed"); + goto error_fd; + } + + attribs.offset[i] = gbm_bo_get_offset(bo, i); + attribs.stride[i] = gbm_bo_get_stride_for_plane(bo, i); + } + + memcpy(&buffer->dmabuf, &attribs, sizeof(attribs)); + return true; + +error_fd: + for (int j = 0; j < i; ++j) { + close(attribs.fd[j]); + } + return false; +} + +static bool buffer_get_dmabuf(struct wlr_buffer *wlr_buffer, + struct wlr_dmabuf_attributes *attribs) { + struct wlr_gbm_buffer *buffer = + get_gbm_buffer_from_buffer(wlr_buffer); + + memset(attribs, 0, sizeof(*attribs)); + + // Only export the buffer once + if (buffer->dmabuf.n_planes == 0) { + if (!buffer_create_dmabuf(buffer)) { + return false; + } + } + + memcpy(attribs, &buffer->dmabuf, sizeof(buffer->dmabuf)); + return true; +} + +static const struct wlr_buffer_impl buffer_impl = { + .destroy = buffer_destroy, + .get_dmabuf = buffer_get_dmabuf, +}; + +static const struct wlr_allocator_interface allocator_impl; + +static struct wlr_gbm_allocator *get_gbm_alloc_from_alloc( + struct wlr_allocator *alloc) { + assert(alloc->impl == &allocator_impl); + return (struct wlr_gbm_allocator *)alloc; +} + +struct wlr_gbm_allocator *wlr_gbm_allocator_create(int fd) { + struct wlr_gbm_allocator *alloc = calloc(1, sizeof(*alloc)); + if (alloc == NULL) { + return NULL; + } + wlr_allocator_init(&alloc->base, &allocator_impl); + + alloc->fd = fd; + + alloc->gbm_device = gbm_create_device(fd); + if (alloc->gbm_device == NULL) { + wlr_log(WLR_ERROR, "gbm_create_device failed"); + free(alloc); + return NULL; + } + + return alloc; +} + +static void allocator_destroy(struct wlr_allocator *wlr_alloc) { + struct wlr_gbm_allocator *alloc = get_gbm_alloc_from_alloc(wlr_alloc); + gbm_device_destroy(alloc->gbm_device); + close(alloc->fd); + free(alloc); +} + +static struct wlr_buffer *allocator_create_buffer( + struct wlr_allocator *wlr_alloc, int width, int height, + const struct wlr_drm_format *format) { + struct wlr_gbm_allocator *alloc = get_gbm_alloc_from_alloc(wlr_alloc); + struct wlr_gbm_buffer *buffer = + create_buffer(alloc->gbm_device, width, height, format); + if (buffer == NULL) { + return NULL; + } + return &buffer->base; +} + +static const struct wlr_allocator_interface allocator_impl = { + .destroy = allocator_destroy, + .create_buffer = allocator_create_buffer, +}; diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c index 914f5b77f..761142781 100644 --- a/render/gles2/renderer.c +++ b/render/gles2/renderer.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -21,11 +22,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; @@ -38,12 +37,145 @@ static struct wlr_gles2_renderer *gles2_get_renderer_in_context( return renderer; } +static void destroy_buffer(struct wlr_gles2_buffer *buffer) { + wl_list_remove(&buffer->link); + wl_list_remove(&buffer->buffer_destroy.link); + + wlr_egl_make_current(buffer->renderer->egl, EGL_NO_SURFACE, NULL); + + push_gles2_debug(buffer->renderer); + + glDeleteFramebuffers(1, &buffer->fbo); + glDeleteRenderbuffers(1, &buffer->rbo); + + pop_gles2_debug(buffer->renderer); + + wlr_egl_destroy_image(buffer->renderer->egl, buffer->image); + wlr_egl_unset_current(buffer->renderer->egl); + free(buffer); +} + +static struct wlr_gles2_buffer *get_buffer(struct wlr_gles2_renderer *renderer, + struct wlr_buffer *wlr_buffer) { + struct wlr_gles2_buffer *buffer; + wl_list_for_each(buffer, &renderer->buffers, link) { + if (buffer->buffer == wlr_buffer) { + return buffer; + } + } + return NULL; +} + +static void handle_buffer_destroy(struct wl_listener *listener, void *data) { + struct wlr_gles2_buffer *buffer = + wl_container_of(listener, buffer, buffer_destroy); + destroy_buffer(buffer); +} + +static struct wlr_gles2_buffer *create_buffer(struct wlr_gles2_renderer *renderer, + struct wlr_buffer *wlr_buffer) { + struct wlr_gles2_buffer *buffer = calloc(1, sizeof(*buffer)); + if (buffer == NULL) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + return NULL; + } + buffer->buffer = wlr_buffer; + buffer->renderer = renderer; + + struct wlr_dmabuf_attributes dmabuf = {0}; + if (!wlr_buffer_get_dmabuf(wlr_buffer, &dmabuf)) { + goto error_buffer; + } + + bool external_only; + buffer->image = wlr_egl_create_image_from_dmabuf(renderer->egl, + &dmabuf, &external_only); + if (buffer->image == EGL_NO_IMAGE_KHR) { + goto error_buffer; + } + + push_gles2_debug(renderer); + + glGenRenderbuffers(1, &buffer->rbo); + glBindRenderbuffer(GL_RENDERBUFFER, buffer->rbo); + renderer->procs.glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, + buffer->image); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + glGenFramebuffers(1, &buffer->fbo); + glBindFramebuffer(GL_FRAMEBUFFER, buffer->fbo); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_RENDERBUFFER, buffer->rbo); + GLenum fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + pop_gles2_debug(renderer); + + if (fb_status != GL_FRAMEBUFFER_COMPLETE) { + wlr_log(WLR_ERROR, "Failed to create FBO"); + goto error_image; + } + + buffer->buffer_destroy.notify = handle_buffer_destroy; + wl_signal_add(&wlr_buffer->events.destroy, &buffer->buffer_destroy); + + wl_list_insert(&renderer->buffers, &buffer->link); + + wlr_log(WLR_DEBUG, "Created GL FBO for buffer %dx%d", + wlr_buffer->width, wlr_buffer->height); + + return buffer; + +error_image: + wlr_egl_destroy_image(renderer->egl, buffer->image); +error_buffer: + free(buffer); + return NULL; +} + +static bool gles2_bind_buffer(struct wlr_renderer *wlr_renderer, + struct wlr_buffer *wlr_buffer) { + struct wlr_gles2_renderer *renderer = + gles2_get_renderer_in_context(wlr_renderer); + + if (renderer->current_buffer != NULL) { + push_gles2_debug(renderer); + glFlush(); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + pop_gles2_debug(renderer); + + wlr_buffer_unlock(renderer->current_buffer->buffer); + renderer->current_buffer = NULL; + } + + if (wlr_buffer == NULL) { + return true; + } + + struct wlr_gles2_buffer *buffer = get_buffer(renderer, wlr_buffer); + if (buffer == NULL) { + buffer = create_buffer(renderer, wlr_buffer); + } + if (buffer == NULL) { + return false; + } + + wlr_buffer_lock(wlr_buffer); + renderer->current_buffer = buffer; + + push_gles2_debug(renderer); + glBindFramebuffer(GL_FRAMEBUFFER, renderer->current_buffer->fbo); + pop_gles2_debug(renderer); + + return true; +} + static void gles2_begin(struct wlr_renderer *wlr_renderer, uint32_t width, uint32_t height) { 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; @@ -56,7 +188,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) { @@ -66,12 +198,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, @@ -79,20 +212,30 @@ 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, - renderer->viewport_width, renderer->viewport_height); + if (renderer->current_buffer != NULL) { + memcpy(&gl_box, box, sizeof(gl_box)); + } else { + wlr_box_transform(&gl_box, box, WL_OUTPUT_TRANSFORM_FLIPPED_180, + renderer->viewport_width, renderer->viewport_height); + } glScissor(gl_box.x, gl_box.y, gl_box.width, gl_box.height); glEnable(GL_SCISSOR_TEST); } else { glDisable(GL_SCISSOR_TEST); } - POP_GLES2_DEBUG; + pop_gles2_debug(renderer); } +static const float flip_180[9] = { + 1.0f, 0.0f, 0.0f, + 0.0f, -1.0f, 0.0f, + 0.0f, 0.0f, 1.0f, +}; + static bool gles2_render_subtexture_with_matrix( struct wlr_renderer *wlr_renderer, struct wlr_texture *wlr_texture, const struct wlr_fbox *box, const float matrix[static 9], @@ -125,12 +268,17 @@ static bool gles2_render_subtexture_with_matrix( abort(); } + float gl_matrix[9]; + if (renderer->current_buffer != NULL) { + wlr_matrix_multiply(gl_matrix, flip_180, matrix); + } else { + memcpy(gl_matrix, matrix, sizeof(gl_matrix)); + } // OpenGL ES 2 requires the glUniformMatrix3fv transpose parameter to be set // to GL_FALSE - float transposition[9]; - wlr_matrix_transpose(transposition, matrix); + wlr_matrix_transpose(gl_matrix, gl_matrix); - PUSH_GLES2_DEBUG; + push_gles2_debug(renderer); glActiveTexture(GL_TEXTURE0); glBindTexture(texture->target, texture->tex); @@ -139,7 +287,7 @@ static bool gles2_render_subtexture_with_matrix( glUseProgram(shader->program); - glUniformMatrix3fv(shader->proj, 1, GL_FALSE, transposition); + glUniformMatrix3fv(shader->proj, 1, GL_FALSE, gl_matrix); glUniform1i(shader->invert_y, texture->inverted_y); glUniform1i(shader->tex, 0); glUniform1f(shader->alpha, alpha); @@ -168,7 +316,7 @@ static bool gles2_render_subtexture_with_matrix( glBindTexture(texture->target, 0); - POP_GLES2_DEBUG; + pop_gles2_debug(renderer); return true; } @@ -177,15 +325,20 @@ static void gles2_render_quad_with_matrix(struct wlr_renderer *wlr_renderer, struct wlr_gles2_renderer *renderer = gles2_get_renderer_in_context(wlr_renderer); + float gl_matrix[9]; + if (renderer->current_buffer != NULL) { + wlr_matrix_multiply(gl_matrix, flip_180, matrix); + } else { + memcpy(gl_matrix, matrix, sizeof(gl_matrix)); + } // OpenGL ES 2 requires the glUniformMatrix3fv transpose parameter to be set // to GL_FALSE - float transposition[9]; - wlr_matrix_transpose(transposition, matrix); + wlr_matrix_transpose(gl_matrix, gl_matrix); - PUSH_GLES2_DEBUG; + push_gles2_debug(renderer); glUseProgram(renderer->shaders.quad.program); - glUniformMatrix3fv(renderer->shaders.quad.proj, 1, GL_FALSE, transposition); + glUniformMatrix3fv(renderer->shaders.quad.proj, 1, GL_FALSE, gl_matrix); glUniform4f(renderer->shaders.quad.color, color[0], color[1], color[2], color[3]); glVertexAttribPointer(renderer->shaders.quad.pos_attrib, 2, GL_FLOAT, GL_FALSE, @@ -197,7 +350,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, @@ -205,10 +358,15 @@ static void gles2_render_ellipse_with_matrix(struct wlr_renderer *wlr_renderer, struct wlr_gles2_renderer *renderer = gles2_get_renderer_in_context(wlr_renderer); + float gl_matrix[9]; + if (renderer->current_buffer != NULL) { + wlr_matrix_multiply(gl_matrix, flip_180, matrix); + } else { + memcpy(gl_matrix, matrix, sizeof(gl_matrix)); + } // OpenGL ES 2 requires the glUniformMatrix3fv transpose parameter to be set // to GL_FALSE - float transposition[9]; - wlr_matrix_transpose(transposition, matrix); + wlr_matrix_transpose(gl_matrix, gl_matrix); static const GLfloat texcoord[] = { 1, 0, // top right @@ -217,10 +375,10 @@ static void gles2_render_ellipse_with_matrix(struct wlr_renderer *wlr_renderer, 0, 1, // bottom left }; - PUSH_GLES2_DEBUG; + push_gles2_debug(renderer); glUseProgram(renderer->shaders.ellipse.program); - glUniformMatrix3fv(renderer->shaders.ellipse.proj, 1, GL_FALSE, transposition); + glUniformMatrix3fv(renderer->shaders.ellipse.proj, 1, GL_FALSE, gl_matrix); glUniform4f(renderer->shaders.ellipse.color, color[0], color[1], color[2], color[3]); glVertexAttribPointer(renderer->shaders.ellipse.pos_attrib, 2, GL_FLOAT, @@ -235,19 +393,14 @@ 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( +static const enum wl_shm_format *gles2_get_shm_texture_formats( struct wlr_renderer *wlr_renderer, size_t *len) { return get_gles2_wl_formats(len); } -static bool gles2_format_supported(struct wlr_renderer *wlr_renderer, - enum wl_shm_format wl_fmt) { - return get_gles2_format_from_wl(wl_fmt) != NULL; -} - static bool gles2_resource_is_wl_drm_buffer(struct wlr_renderer *wlr_renderer, struct wl_resource *resource) { struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); @@ -276,10 +429,16 @@ static void gles2_wl_drm_buffer_get_size(struct wlr_renderer *wlr_renderer, buffer, EGL_HEIGHT, height); } -static const struct wlr_drm_format_set *gles2_get_dmabuf_formats( +static const struct wlr_drm_format_set *gles2_get_dmabuf_texture_formats( struct wlr_renderer *wlr_renderer) { struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); - return wlr_egl_get_dmabuf_formats(renderer->egl); + return wlr_egl_get_dmabuf_texture_formats(renderer->egl); +} + +static const struct wlr_drm_format_set *gles2_get_dmabuf_render_formats( + struct wlr_renderer *wlr_renderer) { + struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); + return wlr_egl_get_dmabuf_render_formats(renderer->egl); } static enum wl_shm_format gles2_preferred_read_format( @@ -288,14 +447,21 @@ 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, - EGL_ALPHA_SIZE, &alpha_size); + if (renderer->current_buffer != NULL) { + glBindRenderbuffer(GL_RENDERBUFFER, renderer->current_buffer->rbo); + glGetRenderbufferParameteriv(GL_RENDERBUFFER, + GL_RENDERBUFFER_ALPHA_SIZE, &alpha_size); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + } else if (renderer->egl->config != EGL_NO_CONFIG_KHR) { + eglGetConfigAttrib(renderer->egl->display, renderer->egl->config, + EGL_ALPHA_SIZE, &alpha_size); + } const struct wlr_gles2_pixel_format *fmt = get_gles2_format_from_gl(gl_format, gl_type, alpha_size > 0); @@ -328,7 +494,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(); @@ -340,22 +506,38 @@ static bool gles2_read_pixels(struct wlr_renderer *wlr_renderer, if (pack_stride == stride && dst_x == 0 && flags != NULL) { // Under these particular conditions, we can read the pixels with only // one glReadPixels call - glReadPixels(src_x, renderer->viewport_height - height - src_y, - width, height, fmt->gl_format, fmt->gl_type, p); - *flags = WLR_RENDERER_READ_PIXELS_Y_INVERT; + + uint32_t y = src_y; + if (renderer->current_buffer == NULL) { + y = renderer->viewport_height - height - src_y; + } + + glReadPixels(src_x, y, width, height, fmt->gl_format, fmt->gl_type, p); + + if (renderer->current_buffer != NULL) { + *flags = 0; + } else { + *flags = WLR_RENDERER_READ_PIXELS_Y_INVERT; + } } else { // Unfortunately GLES2 doesn't support GL_PACK_*, so we have to read // the lines out row by row for (size_t i = 0; i < height; ++i) { - glReadPixels(src_x, renderer->viewport_height - src_y - i - 1, width, 1, fmt->gl_format, + uint32_t y = src_y + i; + if (renderer->current_buffer == NULL) { + y = renderer->viewport_height - src_y - i - 1; + } + + glReadPixels(src_x, y, width, 1, fmt->gl_format, fmt->gl_type, p + i * stride + dst_x * fmt->bpp / 8); } + if (flags != NULL) { *flags = 0; } } - POP_GLES2_DEBUG; + pop_gles2_debug(renderer); return glGetError() == GL_NO_ERROR; } @@ -363,7 +545,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; } @@ -377,6 +560,7 @@ static bool gles2_blit_dmabuf(struct wlr_renderer *wlr_renderer, goto restore_context_out; } + // TODO: get inverted_y right when current_buffer != NULL // This is to take into account y-inversion on both buffers rather than // just the source buffer. bool src_inverted_y = @@ -384,19 +568,21 @@ static bool gles2_blit_dmabuf(struct wlr_renderer *wlr_renderer, bool dst_inverted_y = !!(dst_attr->flags & WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT); struct wlr_gles2_texture *gles2_src_tex = gles2_get_texture(src_tex); - // The result is negated because wlr_matrix_projection y-inverts the - // texture. - gles2_src_tex->inverted_y = !(src_inverted_y ^ dst_inverted_y); + gles2_src_tex->inverted_y = src_inverted_y ^ dst_inverted_y; + if (renderer->current_buffer == NULL) { + // The result is negated because wlr_matrix_projection y-inverts the + // texture. + gles2_src_tex->inverted_y = !gles2_src_tex->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; @@ -405,7 +591,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); @@ -434,7 +620,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: @@ -442,27 +628,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 = @@ -488,6 +653,17 @@ static bool gles2_init_wl_display(struct wlr_renderer *wlr_renderer, return true; } +static int gles2_get_drm_fd(struct wlr_renderer *wlr_renderer) { + struct wlr_gles2_renderer *renderer = + gles2_get_renderer(wlr_renderer); + + if (renderer->drm_fd < 0) { + renderer->drm_fd = wlr_egl_dup_drm_fd(renderer->egl); + } + + return renderer->drm_fd; +} + struct wlr_egl *wlr_gles2_renderer_get_egl(struct wlr_renderer *wlr_renderer) { struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); @@ -499,26 +675,36 @@ static void gles2_destroy(struct wlr_renderer *wlr_renderer) { wlr_egl_make_current(renderer->egl, EGL_NO_SURFACE, NULL); - PUSH_GLES2_DEBUG; + struct wlr_gles2_buffer *buffer, *buffer_tmp; + wl_list_for_each_safe(buffer, buffer_tmp, &renderer->buffers, link) { + destroy_buffer(buffer); + } + + 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); + if (renderer->drm_fd >= 0) { + close(renderer->drm_fd); + } + free(renderer); } static const struct wlr_renderer_impl renderer_impl = { .destroy = gles2_destroy, + .bind_buffer = gles2_bind_buffer, .begin = gles2_begin, .end = gles2_end, .clear = gles2_clear, @@ -526,11 +712,11 @@ static const struct wlr_renderer_impl renderer_impl = { .render_subtexture_with_matrix = gles2_render_subtexture_with_matrix, .render_quad_with_matrix = gles2_render_quad_with_matrix, .render_ellipse_with_matrix = gles2_render_ellipse_with_matrix, - .formats = gles2_renderer_formats, - .format_supported = gles2_format_supported, + .get_shm_texture_formats = gles2_get_shm_texture_formats, .resource_is_wl_drm_buffer = gles2_resource_is_wl_drm_buffer, .wl_drm_buffer_get_size = gles2_wl_drm_buffer_get_size, - .get_dmabuf_formats = gles2_get_dmabuf_formats, + .get_dmabuf_texture_formats = gles2_get_dmabuf_texture_formats, + .get_dmabuf_render_formats = gles2_get_dmabuf_render_formats, .preferred_read_format = gles2_preferred_read_format, .read_pixels = gles2_read_pixels, .texture_from_pixels = gles2_texture_from_pixels, @@ -538,22 +724,24 @@ static const struct wlr_renderer_impl renderer_impl = { .texture_from_dmabuf = gles2_texture_from_dmabuf, .init_wl_display = gles2_init_wl_display, .blit_dmabuf = gles2_blit_dmabuf, + .get_drm_fd = gles2_get_drm_fd, }; -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(); } } @@ -577,8 +765,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); @@ -591,19 +780,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; @@ -626,11 +816,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; } @@ -687,8 +877,11 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { } wlr_renderer_init(&renderer->wlr_renderer, &renderer_impl); + wl_list_init(&renderer->buffers); + renderer->egl = egl; renderer->exts_str = exts_str; + renderer->drm_fd = -1; wlr_log(WLR_INFO, "Using %s", glGetString(GL_VERSION)); wlr_log(WLR_INFO, "GL vendor: %s", glGetString(GL_VENDOR)); @@ -700,47 +893,52 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { free(renderer); return NULL; } + if (!check_gl_ext(exts_str, "GL_EXT_unpack_subimage")) { + wlr_log(WLR_ERROR, "GL_EXT_unpack_subimage not supported"); + free(renderer); + return NULL; + } renderer->exts.read_format_bgra_ext = check_gl_ext(exts_str, "GL_EXT_read_format_bgra"); 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, + renderer->procs.glDebugMessageControlKHR(GL_DONT_CARE, GL_DEBUG_TYPE_POP_GROUP_KHR, GL_DONT_CARE, 0, NULL, GL_FALSE); - gles2_procs.glDebugMessageControlKHR(GL_DONT_CARE, + 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; } @@ -749,7 +947,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; } @@ -759,7 +957,7 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { renderer->shaders.ellipse.tex_attrib = glGetAttribLocation(prog, "texcoord"); renderer->shaders.tex_rgba.program = prog = - link_program(tex_vertex_src, tex_fragment_src_rgba); + link_program(renderer, tex_vertex_src, tex_fragment_src_rgba); if (!renderer->shaders.tex_rgba.program) { goto error; } @@ -771,7 +969,7 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { renderer->shaders.tex_rgba.tex_attrib = glGetAttribLocation(prog, "texcoord"); renderer->shaders.tex_rgbx.program = prog = - link_program(tex_vertex_src, tex_fragment_src_rgbx); + link_program(renderer, tex_vertex_src, tex_fragment_src_rgbx); if (!renderer->shaders.tex_rgbx.program) { goto error; } @@ -784,7 +982,7 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { if (renderer->exts.egl_image_external_oes) { renderer->shaders.tex_ext.program = prog = - link_program(tex_vertex_src, tex_fragment_src_external); + link_program(renderer, tex_vertex_src, tex_fragment_src_external); if (!renderer->shaders.tex_ext.program) { goto error; } @@ -796,7 +994,7 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { renderer->shaders.tex_ext.tex_attrib = glGetAttribLocation(prog, "texcoord"); } - POP_GLES2_DEBUG; + pop_gles2_debug(renderer); wlr_egl_unset_current(renderer->egl); @@ -809,11 +1007,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 0187a462f..ea3a11612 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; } @@ -55,8 +55,7 @@ static bool gles2_texture_write_pixels(struct wlr_texture *wlr_texture, get_gles2_format_from_wl(texture->wl_format); 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 +72,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 +85,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 +102,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 +114,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,10 +133,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) { @@ -152,12 +153,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); @@ -169,24 +170,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"); @@ -197,11 +200,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; @@ -217,36 +220,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; @@ -272,7 +277,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 = @@ -280,7 +285,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); @@ -289,16 +294,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/meson.build b/render/meson.build index 9486c22de..f8609ef83 100644 --- a/render/meson.build +++ b/render/meson.build @@ -1,11 +1,14 @@ wlr_files += files( + 'allocator.c', 'dmabuf.c', 'egl.c', 'drm_format_set.c', + 'gbm_allocator.c', 'gles2/pixel_format.c', 'gles2/renderer.c', 'gles2/shaders.c', 'gles2/texture.c', + 'swapchain.c', 'wlr_renderer.c', 'wlr_texture.c', ) diff --git a/render/swapchain.c b/render/swapchain.c new file mode 100644 index 000000000..fd9f87253 --- /dev/null +++ b/render/swapchain.c @@ -0,0 +1,144 @@ +#include +#include +#include +#include +#include "render/allocator.h" +#include "render/drm_format_set.h" +#include "render/swapchain.h" + +static void swapchain_handle_allocator_destroy(struct wl_listener *listener, + void *data) { + struct wlr_swapchain *swapchain = + wl_container_of(listener, swapchain, allocator_destroy); + swapchain->allocator = NULL; +} + +struct wlr_swapchain *wlr_swapchain_create( + struct wlr_allocator *alloc, int width, int height, + const struct wlr_drm_format *format) { + struct wlr_swapchain *swapchain = calloc(1, sizeof(*swapchain)); + if (swapchain == NULL) { + return NULL; + } + swapchain->allocator = alloc; + swapchain->width = width; + swapchain->height = height; + + swapchain->format = wlr_drm_format_dup(format); + if (swapchain->format == NULL) { + free(swapchain); + return NULL; + } + + swapchain->allocator_destroy.notify = swapchain_handle_allocator_destroy; + wl_signal_add(&alloc->events.destroy, &swapchain->allocator_destroy); + + return swapchain; +} + +static void slot_reset(struct wlr_swapchain_slot *slot) { + if (slot->acquired) { + wl_list_remove(&slot->release.link); + } + wlr_buffer_drop(slot->buffer); + memset(slot, 0, sizeof(*slot)); +} + +void wlr_swapchain_destroy(struct wlr_swapchain *swapchain) { + if (swapchain == NULL) { + return; + } + for (size_t i = 0; i < WLR_SWAPCHAIN_CAP; i++) { + slot_reset(&swapchain->slots[i]); + } + wl_list_remove(&swapchain->allocator_destroy.link); + free(swapchain->format); + free(swapchain); +} + +static void slot_handle_release(struct wl_listener *listener, void *data) { + struct wlr_swapchain_slot *slot = + wl_container_of(listener, slot, release); + wl_list_remove(&slot->release.link); + slot->acquired = false; +} + +static struct wlr_buffer *slot_acquire(struct wlr_swapchain *swapchain, + struct wlr_swapchain_slot *slot, int *age) { + assert(!slot->acquired); + assert(slot->buffer != NULL); + + slot->acquired = true; + + slot->release.notify = slot_handle_release; + wl_signal_add(&slot->buffer->events.release, &slot->release); + + if (age != NULL) { + *age = slot->age; + } + + return wlr_buffer_lock(slot->buffer); +} + +struct wlr_buffer *wlr_swapchain_acquire(struct wlr_swapchain *swapchain, + int *age) { + struct wlr_swapchain_slot *free_slot = NULL; + for (size_t i = 0; i < WLR_SWAPCHAIN_CAP; i++) { + struct wlr_swapchain_slot *slot = &swapchain->slots[i]; + if (slot->acquired) { + continue; + } + if (slot->buffer != NULL) { + return slot_acquire(swapchain, slot, age); + } + free_slot = slot; + } + if (free_slot == NULL) { + wlr_log(WLR_ERROR, "No free output buffer slot"); + return NULL; + } + + if (swapchain->allocator == NULL) { + return NULL; + } + + wlr_log(WLR_DEBUG, "Allocating new swapchain buffer"); + free_slot->buffer = wlr_allocator_create_buffer(swapchain->allocator, + swapchain->width, swapchain->height, swapchain->format); + if (free_slot->buffer == NULL) { + wlr_log(WLR_ERROR, "Failed to allocate buffer"); + return NULL; + } + return slot_acquire(swapchain, free_slot, age); +} + +static bool swapchain_has_buffer(struct wlr_swapchain *swapchain, + struct wlr_buffer *buffer) { + for (size_t i = 0; i < WLR_SWAPCHAIN_CAP; i++) { + struct wlr_swapchain_slot *slot = &swapchain->slots[i]; + if (slot->buffer == buffer) { + return true; + } + } + return false; +} + +void wlr_swapchain_set_buffer_submitted(struct wlr_swapchain *swapchain, + struct wlr_buffer *buffer) { + assert(buffer != NULL); + + if (!swapchain_has_buffer(swapchain, buffer)) { + return; + } + + // See the algorithm described in: + // https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_buffer_age.txt + for (size_t i = 0; i < WLR_SWAPCHAIN_CAP; i++) { + struct wlr_swapchain_slot *slot = &swapchain->slots[i]; + if (slot->buffer == buffer) { + slot->age = 1; + } else if (slot->age > 0) { + slot->age++; + } + } +} diff --git a/render/wlr_renderer.c b/render/wlr_renderer.c index 593d165f4..b8374be9a 100644 --- a/render/wlr_renderer.c +++ b/render/wlr_renderer.c @@ -7,6 +7,7 @@ #include #include #include "util/signal.h" +#include "render/wlr_renderer.h" void wlr_renderer_init(struct wlr_renderer *renderer, const struct wlr_renderer_impl *impl) { @@ -16,8 +17,7 @@ void wlr_renderer_init(struct wlr_renderer *renderer, assert(impl->render_subtexture_with_matrix); assert(impl->render_quad_with_matrix); assert(impl->render_ellipse_with_matrix); - assert(impl->formats); - assert(impl->format_supported); + assert(impl->get_shm_texture_formats); assert(impl->texture_from_pixels); renderer->impl = impl; @@ -37,7 +37,16 @@ void wlr_renderer_destroy(struct wlr_renderer *r) { } } -void wlr_renderer_begin(struct wlr_renderer *r, int width, int height) { +bool wlr_renderer_bind_buffer(struct wlr_renderer *r, + struct wlr_buffer *buffer) { + assert(!r->rendering); + if (!r->impl->bind_buffer) { + return false; + } + return r->impl->bind_buffer(r, buffer); +} + +void wlr_renderer_begin(struct wlr_renderer *r, uint32_t width, uint32_t height) { assert(!r->rendering); r->impl->begin(r, width, height); @@ -99,6 +108,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, @@ -115,6 +127,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, @@ -129,9 +144,9 @@ void wlr_render_ellipse_with_matrix(struct wlr_renderer *r, r->impl->render_ellipse_with_matrix(r, color, matrix); } -const enum wl_shm_format *wlr_renderer_get_formats( +const enum wl_shm_format *wlr_renderer_get_shm_texture_formats( struct wlr_renderer *r, size_t *len) { - return r->impl->formats(r, len); + return r->impl->get_shm_texture_formats(r, len); } bool wlr_renderer_resource_is_wl_drm_buffer(struct wlr_renderer *r, @@ -150,12 +165,20 @@ void wlr_renderer_wl_drm_buffer_get_size(struct wlr_renderer *r, return r->impl->wl_drm_buffer_get_size(r, buffer, width, height); } -const struct wlr_drm_format_set *wlr_renderer_get_dmabuf_formats( +const struct wlr_drm_format_set *wlr_renderer_get_dmabuf_texture_formats( struct wlr_renderer *r) { - if (!r->impl->get_dmabuf_formats) { + if (!r->impl->get_dmabuf_texture_formats) { return NULL; } - return r->impl->get_dmabuf_formats(r); + return r->impl->get_dmabuf_texture_formats(r); +} + +const struct wlr_drm_format_set *wlr_renderer_get_dmabuf_render_formats( + struct wlr_renderer *r) { + if (!r->impl->get_dmabuf_render_formats) { + return NULL; + } + return r->impl->get_dmabuf_render_formats(r); } bool wlr_renderer_read_pixels(struct wlr_renderer *r, enum wl_shm_format fmt, @@ -179,11 +202,6 @@ bool wlr_renderer_blit_dmabuf(struct wlr_renderer *r, return r->impl->blit_dmabuf(r, dst, src); } -bool wlr_renderer_format_supported(struct wlr_renderer *r, - enum wl_shm_format fmt) { - return r->impl->format_supported(r, fmt); -} - bool wlr_renderer_init_wl_display(struct wlr_renderer *r, struct wl_display *wl_display) { if (wl_display_init_shm(wl_display)) { @@ -192,19 +210,29 @@ bool wlr_renderer_init_wl_display(struct wlr_renderer *r, } size_t len; - const enum wl_shm_format *formats = wlr_renderer_get_formats(r, &len); + const enum wl_shm_format *formats = + wlr_renderer_get_shm_texture_formats(r, &len); if (formats == NULL) { wlr_log(WLR_ERROR, "Failed to initialize shm: cannot get formats"); return false; } + bool argb8888 = false, xrgb8888 = false; for (size_t i = 0; i < len; ++i) { - // These formats are already added by default - if (formats[i] != WL_SHM_FORMAT_ARGB8888 && - formats[i] != WL_SHM_FORMAT_XRGB8888) { + // ARGB8888 and XRGB8888 must be supported and are implicitly + // advertised by wl_display_init_shm + switch (formats[i]) { + case WL_SHM_FORMAT_ARGB8888: + argb8888 = true; + break; + case WL_SHM_FORMAT_XRGB8888: + xrgb8888 = true; + break; + default: wl_display_add_shm_format(wl_display, formats[i]); } } + assert(argb8888 && xrgb8888); if (r->impl->init_wl_display) { if (!r->impl->init_wl_display(r, wl_display)) { @@ -240,7 +268,11 @@ struct wlr_renderer *wlr_renderer_autocreate(struct wlr_egl *egl, memcpy(&all_config_attribs[config_attribs_len], gles2_config_attribs, sizeof(gles2_config_attribs)); - if (!wlr_egl_init(egl, platform, remote_display, all_config_attribs, + if (config_attribs != NULL) { + config_attribs = all_config_attribs; + } + + if (!wlr_egl_init(egl, platform, remote_display, config_attribs, visual_id)) { wlr_log(WLR_ERROR, "Could not initialize EGL"); return NULL; @@ -253,3 +285,10 @@ struct wlr_renderer *wlr_renderer_autocreate(struct wlr_egl *egl, return renderer; } + +int wlr_renderer_get_drm_fd(struct wlr_renderer *r) { + if (!r->impl->get_drm_fd) { + return -1; + } + return r->impl->get_drm_fd(r); +} diff --git a/tinywl/tinywl.c b/tinywl/tinywl.c index 84a7636b8..e2cf30a0b 100644 --- a/tinywl/tinywl.c +++ b/tinywl/tinywl.c @@ -197,7 +197,7 @@ static void keyboard_handle_key( bool handled = false; uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->device->keyboard); - if ((modifiers & WLR_MODIFIER_ALT) && event->state == WLR_KEY_PRESSED) { + if ((modifiers & WLR_MODIFIER_ALT) && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { /* If alt is held down and this button was _pressed_, we attempt to * process it as a compositor keybinding. */ for (int i = 0; i < nsyms; i++) { @@ -374,7 +374,7 @@ static void process_cursor_resize(struct tinywl_server *server, uint32_t time) { int new_left = server->grab_geobox.x; int new_right = server->grab_geobox.x + server->grab_geobox.width; int new_top = server->grab_geobox.y; - int new_bottom = server->grab_geobox.y + server->grab_geobox.height; + int new_bottom = server->grab_geobox.y + server->grab_geobox.height; if (server->resize_edges & WLR_EDGE_TOP) { new_top = border_y; 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/tablet_v2/wlr_tablet_v2_pad.c b/types/tablet_v2/wlr_tablet_v2_pad.c index fdffd4755..779e24e57 100644 --- a/types/tablet_v2/wlr_tablet_v2_pad.c +++ b/types/tablet_v2/wlr_tablet_v2_pad.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include diff --git a/types/wlr_buffer.c b/types/wlr_buffer.c index 2f914c127..29b264120 100644 --- a/types/wlr_buffer.c +++ b/types/wlr_buffer.c @@ -96,10 +96,18 @@ bool wlr_resource_get_buffer_size(struct wl_resource *resource, static const struct wlr_buffer_impl client_buffer_impl; +struct wlr_client_buffer *wlr_client_buffer_get(struct wlr_buffer *buffer) { + if (buffer->impl != &client_buffer_impl) { + return NULL; + } + return (struct wlr_client_buffer *)buffer; +} + static struct wlr_client_buffer *client_buffer_from_buffer( struct wlr_buffer *buffer) { - assert(buffer->impl == &client_buffer_impl); - return (struct wlr_client_buffer *) buffer; + struct wlr_client_buffer *client_buffer = wlr_client_buffer_get(buffer); + assert(client_buffer != NULL); + return client_buffer; } static void client_buffer_destroy(struct wlr_buffer *_buffer) { 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_cursor.c b/types/wlr_cursor.c index 429acd6db..aff7859de 100644 --- a/types/wlr_cursor.c +++ b/types/wlr_cursor.c @@ -7,6 +7,9 @@ #include #include #include +#include +#include +#include #include #include "util/signal.h" 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_keyboard.c b/types/wlr_keyboard.c index 50363fed3..fe94330f6 100644 --- a/types/wlr_keyboard.c +++ b/types/wlr_keyboard.c @@ -58,11 +58,11 @@ bool keyboard_modifier_update(struct wlr_keyboard *keyboard) { void keyboard_key_update(struct wlr_keyboard *keyboard, struct wlr_event_keyboard_key *event) { - if (event->state == WLR_KEY_PRESSED) { + if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { set_add(keyboard->keycodes, &keyboard->num_keycodes, WLR_KEYBOARD_KEYS_CAP, event->keycode); } - if (event->state == WLR_KEY_RELEASED) { + if (event->state == WL_KEYBOARD_KEY_STATE_RELEASED) { set_remove(keyboard->keycodes, &keyboard->num_keycodes, WLR_KEYBOARD_KEYS_CAP, event->keycode); } @@ -99,7 +99,7 @@ void wlr_keyboard_notify_key(struct wlr_keyboard *keyboard, if (event->update_state) { uint32_t keycode = event->keycode + 8; xkb_state_update_key(keyboard->xkb_state, keycode, - event->state == WLR_KEY_PRESSED ? XKB_KEY_DOWN : XKB_KEY_UP); + event->state == WL_KEYBOARD_KEY_STATE_PRESSED ? XKB_KEY_DOWN : XKB_KEY_UP); } bool updated = keyboard_modifier_update(keyboard); diff --git a/types/wlr_keyboard_group.c b/types/wlr_keyboard_group.c index ace1fcd22..d614f58db 100644 --- a/types/wlr_keyboard_group.c +++ b/types/wlr_keyboard_group.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "types/wlr_keyboard.h" #include "util/signal.h" @@ -95,11 +96,11 @@ static bool process_key(struct keyboard_group_device *group_device, if (key->keycode != event->keycode) { continue; } - if (event->state == WLR_KEY_PRESSED) { + if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { key->count++; return false; } - if (event->state == WLR_KEY_RELEASED) { + if (event->state == WL_KEYBOARD_KEY_STATE_RELEASED) { key->count--; if (key->count > 0) { return false; @@ -110,7 +111,7 @@ static bool process_key(struct keyboard_group_device *group_device, break; } - if (event->state == WLR_KEY_PRESSED) { + if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { struct keyboard_group_key *key = calloc(1, sizeof(struct keyboard_group_key)); if (!key) { @@ -199,7 +200,7 @@ static void handle_keyboard_repeat_info(struct wl_listener *listener, } static void refresh_state(struct keyboard_group_device *device, - enum wlr_key_state state) { + enum wl_keyboard_key_state state) { struct wl_array keys; wl_array_init(&keys); @@ -229,7 +230,7 @@ static void refresh_state(struct keyboard_group_device *device, // If there are any unique keys, emit the enter/leave event if (keys.size > 0) { - if (state == WLR_KEY_PRESSED) { + if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { wlr_signal_emit_safe(&device->keyboard->group->events.enter, &keys); } else { wlr_signal_emit_safe(&device->keyboard->group->events.leave, &keys); @@ -240,7 +241,7 @@ static void refresh_state(struct keyboard_group_device *device, } static void remove_keyboard_group_device(struct keyboard_group_device *device) { - refresh_state(device, WLR_KEY_RELEASED); + refresh_state(device, WL_KEYBOARD_KEY_STATE_RELEASED); device->keyboard->group = NULL; wl_list_remove(&device->link); wl_list_remove(&device->key.link); @@ -312,7 +313,7 @@ bool wlr_keyboard_group_add_keyboard(struct wlr_keyboard_group *group, group_kb->repeat_info.delay); } - refresh_state(device, WLR_KEY_PRESSED); + refresh_state(device, WL_KEYBOARD_KEY_STATE_PRESSED); return true; } 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..87fe3a7c8 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; } @@ -389,7 +389,7 @@ static void linux_dmabuf_send_formats(struct wlr_linux_dmabuf_v1 *linux_dmabuf, struct wl_resource *resource, uint32_t version) { uint64_t modifier_invalid = DRM_FORMAT_MOD_INVALID; const struct wlr_drm_format_set *formats = - wlr_renderer_get_dmabuf_formats(linux_dmabuf->renderer); + wlr_renderer_get_dmabuf_texture_formats(linux_dmabuf->renderer); if (formats == NULL) { return; } diff --git a/types/wlr_output.c b/types/wlr_output.c index 7989f03d2..efe70a318 100644 --- a/types/wlr_output.c +++ b/types/wlr_output.c @@ -93,6 +93,13 @@ static void output_bind(struct wl_client *wl_client, void *data, send_current_mode(resource); send_scale(resource); send_done(resource); + + struct wlr_output_event_bind evt = { + .output = output, + .resource = resource, + }; + + wlr_signal_emit_safe(&output->events.bind, &evt); } void wlr_output_create_global(struct wlr_output *output) { @@ -342,6 +349,7 @@ void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend, wl_signal_init(&output->events.precommit); wl_signal_init(&output->events.commit); wl_signal_init(&output->events.present); + wl_signal_init(&output->events.bind); wl_signal_init(&output->events.enable); wl_signal_init(&output->events.mode); wl_signal_init(&output->events.scale); @@ -359,8 +367,6 @@ void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend, output->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &output->display_destroy); - - output->frame_pending = true; } void wlr_output_destroy(struct wlr_output *output) { @@ -519,6 +525,7 @@ static bool output_basic_test(struct wlr_output *output) { if (output->pending.buffer_type == WLR_OUTPUT_STATE_BUFFER_SCANOUT) { if (output->attach_render_locks > 0) { + wlr_log(WLR_DEBUG, "Direct scan-out disabled by lock"); return false; } @@ -528,6 +535,8 @@ static bool output_basic_test(struct wlr_output *output) { wl_list_for_each(cursor, &output->cursors, link) { if (cursor->enabled && cursor->visible && cursor != output->hardware_cursor) { + wlr_log(WLR_DEBUG, + "Direct scan-out disabled by software cursor"); return false; } } @@ -538,6 +547,7 @@ static bool output_basic_test(struct wlr_output *output) { output_pending_resolution(output, &pending_width, &pending_height); if (output->pending.buffer->width != pending_width || output->pending.buffer->height != pending_height) { + wlr_log(WLR_DEBUG, "Direct scan-out buffer size mismatch"); return false; } } @@ -560,6 +570,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; } @@ -573,7 +587,7 @@ bool wlr_output_test(struct wlr_output *output) { bool wlr_output_commit(struct wlr_output *output) { if (!output_basic_test(output)) { - wlr_log(WLR_ERROR, "Basic output test failed"); + wlr_log(WLR_ERROR, "Basic output test failed for %s", output->name); return false; } @@ -586,11 +600,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); @@ -609,7 +623,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..ed53171f2 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, @@ -118,6 +118,9 @@ struct wlr_output_configuration_head_v1 * struct wlr_output_configuration_v1 *config, struct wlr_output *output) { struct wlr_output_configuration_head_v1 *config_head = config_head_create(config, output); + if (config_head == NULL) { + return NULL; + } config_head->state.enabled = output->enabled; config_head->state.mode = output->current_mode; config_head->state.custom_mode.width = output->width; @@ -680,6 +683,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 +767,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); @@ -803,6 +819,26 @@ static bool manager_update_head(struct wlr_output_manager_v1 *manager, state |= HEAD_STATE_SCALE; } + // If a mode was added to wlr_output.modes we need to add the new mode + // to the wlr_output_head + struct wlr_output_mode *mode; + wl_list_for_each(mode, &head->state.output->modes, link) { + bool found = false; + struct wl_resource *mode_resource; + wl_resource_for_each(mode_resource, &head->mode_resources) { + if (mode_from_resource(mode_resource) == mode) { + found = true; + break; + } + } + if (!found) { + struct wl_resource *resource; + wl_resource_for_each(resource, &head->resources) { + head_send_mode(head, resource, mode); + } + } + } + if (state != 0) { *current = *next; 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..6feb07427 100644 --- a/types/wlr_screencopy_v1.c +++ b/types/wlr_screencopy_v1.c @@ -48,9 +48,18 @@ static void screencopy_damage_accumulate(struct screencopy_damage *damage) { return; } - pixman_region32_union(region, region, &output->pending.damage); - pixman_region32_intersect_rect(region, region, 0, 0, output->width, - output->height); + if (output->pending.committed & WLR_OUTPUT_STATE_DAMAGE) { + // If the compositor submitted damage, copy it over + pixman_region32_union(region, region, &output->pending.damage); + pixman_region32_intersect_rect(region, region, 0, 0, + output->width, output->height); + } else if (output->pending.committed & WLR_OUTPUT_STATE_BUFFER) { + // If the compositor did not submit damage but did submit a buffer + // damage everything + pixman_region32_union_rect(region, region, 0, 0, + output->width, output->height); + } + damage->last_commit_seq = output->commit_seq; } @@ -143,6 +152,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 +162,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 +210,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 +232,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 +253,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 +434,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 +447,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 +539,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 f0a0b1fb1..c251816e9 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -616,9 +616,16 @@ static void subsurface_destroy(struct wlr_subsurface *subsurface) { free(subsurface); } +static void surface_output_destroy(struct wlr_surface_output *surface_output); + static void surface_handle_resource_destroy(struct wl_resource *resource) { + struct wlr_surface_output *surface_output, *tmp; struct wlr_surface *surface = wlr_surface_from_resource(resource); + wl_list_for_each_safe(surface_output, tmp, &surface->current_outputs, link) { + surface_output_destroy(surface_output); + } + wlr_signal_emit_safe(&surface->events.destroy, surface); wl_list_remove(wl_resource_get_link(surface->resource)); @@ -676,6 +683,7 @@ struct wlr_surface *wlr_surface_create(struct wl_client *client, wl_signal_init(&surface->events.new_subsurface); wl_list_init(&surface->subsurfaces); wl_list_init(&surface->subsurface_pending_list); + wl_list_init(&surface->current_outputs); pixman_region32_init(&surface->buffer_damage); pixman_region32_init(&surface->opaque_region); pixman_region32_init(&surface->input_region); @@ -712,7 +720,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); } @@ -720,7 +728,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; @@ -791,7 +799,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; } @@ -818,7 +826,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; } @@ -1091,10 +1099,59 @@ struct wlr_surface *wlr_surface_surface_at(struct wlr_surface *surface, return NULL; } +static void surface_output_destroy(struct wlr_surface_output *surface_output) { + wl_list_remove(&surface_output->bind.link); + wl_list_remove(&surface_output->destroy.link); + wl_list_remove(&surface_output->link); + + free(surface_output); +} + +static void surface_handle_output_bind(struct wl_listener *listener, + void *data) { + struct wlr_output_event_bind *evt = data; + struct wlr_surface_output *surface_output = + wl_container_of(listener, surface_output, bind); + struct wl_client *client = wl_resource_get_client( + surface_output->surface->resource); + if (client == wl_resource_get_client(evt->resource)) { + wl_surface_send_enter(surface_output->surface->resource, evt->resource); + } +} + +static void surface_handle_output_destroy(struct wl_listener *listener, + void *data) { + struct wlr_surface_output *surface_output = + wl_container_of(listener, surface_output, destroy); + surface_output_destroy(surface_output); +} + void wlr_surface_send_enter(struct wlr_surface *surface, struct wlr_output *output) { struct wl_client *client = wl_resource_get_client(surface->resource); + struct wlr_surface_output *surface_output; struct wl_resource *resource; + + wl_list_for_each(surface_output, &surface->current_outputs, link) { + if (surface_output->output == output) { + return; + } + } + + surface_output = calloc(1, sizeof(struct wlr_surface_output)); + if (surface_output == NULL) { + return; + } + surface_output->bind.notify = surface_handle_output_bind; + surface_output->destroy.notify = surface_handle_output_destroy; + + wl_signal_add(&output->events.bind, &surface_output->bind); + wl_signal_add(&output->events.destroy, &surface_output->destroy); + + surface_output->surface = surface; + surface_output->output = output; + wl_list_insert(&surface->current_outputs, &surface_output->link); + wl_resource_for_each(resource, &output->resources) { if (client == wl_resource_get_client(resource)) { wl_surface_send_enter(surface->resource, resource); @@ -1105,10 +1162,19 @@ void wlr_surface_send_enter(struct wlr_surface *surface, void wlr_surface_send_leave(struct wlr_surface *surface, struct wlr_output *output) { struct wl_client *client = wl_resource_get_client(surface->resource); + struct wlr_surface_output *surface_output, *tmp; struct wl_resource *resource; - wl_resource_for_each(resource, &output->resources) { - if (client == wl_resource_get_client(resource)) { - wl_surface_send_leave(surface->resource, resource); + + wl_list_for_each_safe(surface_output, tmp, + &surface->current_outputs, link) { + if (surface_output->output == output) { + surface_output_destroy(surface_output); + wl_resource_for_each(resource, &output->resources) { + if (client == wl_resource_get_client(resource)) { + wl_surface_send_leave(surface->resource, resource); + } + } + break; } } } 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/util/time.c b/util/time.c index aed577de4..e1bc2b5c4 100644 --- a/util/time.c +++ b/util/time.c @@ -10,6 +10,11 @@ int64_t timespec_to_msec(const struct timespec *a) { return (int64_t)a->tv_sec * 1000 + a->tv_nsec / 1000000; } +void timespec_from_nsec(struct timespec *r, int64_t nsec) { + r->tv_sec = nsec / NSEC_PER_SEC; + r->tv_nsec = nsec % NSEC_PER_SEC; +} + uint32_t get_current_time_msec(void) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); 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..6a0c9fc30 100644 --- a/xwayland/server.c +++ b/xwayland/server.c @@ -57,11 +57,12 @@ noreturn static void exec_xwayland(struct wlr_xwayland_server *server) { } /* Make Xwayland signal us when it's ready */ + /* TODO: can we use -displayfd instead? */ signal(SIGUSR1, SIG_IGN); char *argv[] = { "Xwayland", NULL /* display, e.g. :1 */, - "-rootless", "-terminate", + "-rootless", "-terminate", "-core", "-listen", NULL /* x_fd[0] */, "-listen", NULL /* x_fd[1] */, "-wm", NULL /* wm_fd[1] */, @@ -89,7 +90,7 @@ noreturn static void exec_xwayland(struct wlr_xwayland_server *server) { snprintf(wayland_socket_str, sizeof(wayland_socket_str), "%d", server->wl_fd[1]); setenv("WAYLAND_SOCKET", wayland_socket_str, true); - wlr_log(WLR_INFO, "WAYLAND_SOCKET=%d Xwayland :%d -rootless -terminate -listen %d -listen %d -wm %d", + wlr_log(WLR_INFO, "WAYLAND_SOCKET=%d Xwayland :%d -rootless -terminate -core -listen %d -listen %d -wm %d", server->wl_fd[1], server->display, server->x_fd[0], server->x_fd[1], server->wm_fd[1]); @@ -107,8 +108,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); @@ -131,8 +140,8 @@ static void server_finish_process(struct wlr_xwayland_server *server) { wl_list_remove(&server->client_destroy.link); wl_client_destroy(server->client); } - if (server->sigusr1_source) { - wl_event_source_remove(server->sigusr1_source); + if (server->pipe_source) { + wl_event_source_remove(server->pipe_source); } safe_close(server->wl_fd[0]); @@ -150,7 +159,14 @@ 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); + wl_list_init(&server->display_destroy.link); + + if (server->display == -1) { return; } @@ -158,8 +174,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'; @@ -172,8 +186,8 @@ static void handle_client_destroy(struct wl_listener *listener, void *data) { struct wlr_xwayland_server *server = wl_container_of(listener, server, client_destroy); - if (server->sigusr1_source) { - // Xwayland failed to start, let the sigusr1 handler deal with it + if (server->pipe_source) { + // Xwayland failed to start, let the readiness handler deal with it return; } @@ -207,7 +221,21 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { wlr_xwayland_server_destroy(server); } -static int xserver_handle_ready(int signal_number, void *data) { +static int xserver_handle_ready(int fd, uint32_t mask, void *data) { + // There are three ways in which we can end up here, from server_start: + // 1. the second fork failed + // 2. the exec failed + // 3. Xwayland sent a SIGUSR1 + // + // All three cases result in a write to the pipe, which triggers us. + // + // For the first two cases, the first fork will exit with + // EXIT_FAILURE, notifying us that startup failed. + // + // For the third case, the first fork will exit with EXIT_SUCCESS + // and we'll know that Xwayland started successfully. + + close(fd); struct wlr_xwayland_server *server = data; int stat_val = -1; @@ -224,8 +252,8 @@ static int xserver_handle_ready(int signal_number, void *data) { } wlr_log(WLR_DEBUG, "Xserver is ready"); - wl_event_source_remove(server->sigusr1_source); - server->sigusr1_source = NULL; + wl_event_source_remove(server->pipe_source); + server->pipe_source = NULL; struct wlr_xwayland_server_ready_event event = { .server = server, @@ -298,19 +326,33 @@ static bool server_start(struct wlr_xwayland_server *server) { server->client_destroy.notify = handle_client_destroy; wl_client_add_destroy_listener(server->client, &server->client_destroy); + int p[2]; + if (pipe(p) == -1) { + wlr_log_errno(WLR_ERROR, "pipe failed"); + server_finish_process(server); + return false; + } + if (!set_cloexec(p[1], true) || !set_cloexec(p[0], true)) { + wlr_log(WLR_ERROR, "Failed to set CLOEXEC on FD"); + server_finish_process(server); + return false; + } + struct wl_event_loop *loop = wl_display_get_event_loop(server->wl_display); - server->sigusr1_source = wl_event_loop_add_signal(loop, SIGUSR1, - xserver_handle_ready, server); + server->pipe_source = wl_event_loop_add_fd(loop, p[0], + WL_EVENT_READABLE, xserver_handle_ready, server); server->pid = fork(); if (server->pid < 0) { wlr_log_errno(WLR_ERROR, "fork failed"); + close(p[0]); + close(p[1]); server_finish_process(server); return false; } else if (server->pid == 0) { /* Double-fork, but we need to forward SIGUSR1 once Xserver(1) * is ready, or error if there was one. */ - pid_t ppid = getppid(); + close(p[0]); sigset_t sigset; sigemptyset(&sigset); sigaddset(&sigset, SIGUSR1); @@ -320,6 +362,7 @@ static bool server_start(struct wlr_xwayland_server *server) { pid_t pid = fork(); if (pid < 0) { wlr_log_errno(WLR_ERROR, "second fork failed"); + (void)!write(p[1], "\n", 1); _exit(EXIT_FAILURE); } else if (pid == 0) { exec_xwayland(server); @@ -327,8 +370,16 @@ static bool server_start(struct wlr_xwayland_server *server) { int sig; sigwait(&sigset, &sig); - kill(ppid, SIGUSR1); - wlr_log(WLR_DEBUG, "sent SIGUSR1 to process %d", ppid); + if (write(p[1], "\n", 1) < 1) { + // Note: if this write failed and we've leaked the write + // end of the pipe (due to a race between another thread + // exec'ing and our call to fcntl), then our handler will + // never wake up and never notice this failure. Hopefully + // that combination of events is extremely unlikely. This + // applies to the other write, too. + wlr_log_errno(WLR_ERROR, "write to pipe failed"); + _exit(EXIT_FAILURE); + } if (sig == SIGCHLD) { waitpid(pid, NULL, 0); _exit(EXIT_FAILURE); @@ -339,6 +390,7 @@ static bool server_start(struct wlr_xwayland_server *server) { /* close child fds */ /* remain managing x sockets for lazy start */ + close(p[1]); close(server->wl_fd[1]); safe_close(server->wm_fd[1]); server->wl_fd[1] = server->wm_fd[1] = -1; 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..781bf15dc 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); @@ -1108,7 +1165,7 @@ static bool update_state(int action, bool *state) { return changed; } -static inline bool xsurface_is_maximized( +static bool xsurface_is_maximized( struct wlr_xwayland_surface *xsurface) { return xsurface->maximized_horz && xsurface->maximized_vert; } @@ -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) { @@ -1428,6 +1548,23 @@ void wlr_xwayland_surface_activate(struct wlr_xwayland_surface *xsurface, } } +void wlr_xwayland_surface_restack(struct wlr_xwayland_surface *surface, + struct wlr_xwayland_surface *sibling, enum xcb_stack_mode_t mode) { + struct wlr_xwm *xwm = surface->xwm; + uint32_t values[2]; + size_t idx = 0; + uint32_t flags = XCB_CONFIG_WINDOW_STACK_MODE; + + if (sibling != NULL) { + values[idx++] = sibling->window_id; + flags |= XCB_CONFIG_WINDOW_SIBLING; + } + values[idx++] = mode; + + xcb_configure_window(xwm->xcb_conn, surface->window_id, flags, values); + xcb_flush(xwm->xcb_conn); +} + void wlr_xwayland_surface_configure(struct wlr_xwayland_surface *xsurface, int16_t x, int16_t y, uint16_t width, uint16_t height) { xsurface->x = x; @@ -1545,7 +1682,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 +1901,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 +1938,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;