diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml index 4d8e3767b..ec98563c9 100644 --- a/.builds/archlinux.yml +++ b/.builds/archlinux.yml @@ -23,14 +23,14 @@ sources: tasks: - setup: | cd wlroots - CC=gcc meson build-gcc --fatal-meson-warnings --default-library=both -Dauto_features=enabled --prefix /usr + CC=gcc meson build-gcc --fatal-meson-warnings --default-library=both -Dauto_features=enabled --prefix /usr -Db_sanitize=address,undefined CC=clang meson build-clang --fatal-meson-warnings -Dauto_features=enabled - gcc: | cd wlroots/build-gcc ninja sudo ninja install cd ../tinywl - make + CFLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer" make - clang: | cd wlroots/build-clang ninja diff --git a/.editorconfig b/.editorconfig index f392d812a..9dcb30d7d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,6 +7,7 @@ charset = utf-8 trim_trailing_whitespace = true indent_style = tab indent_size = 4 +max_line_length = 80 [*.xml] indent_style = space diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a5b474e0c..63e279b53 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -186,16 +186,23 @@ all of the characters, and replace any invalid characters with an underscore. ### Construction/Destruction Functions -For functions that are responsible for constructing and destructing an object, -they should be written as a pair of one of two forms: -* `init`/`finish`: These initialize/deinitialize a type, but are **NOT** -responsible for allocating it. They should accept a pointer to some -pre-allocated memory (e.g. a member of a struct). -* `create`/`destroy`: These also initialize/deinitialize, but will return a -pointer to a `malloc`ed chunk of memory, and will `free` it in `destroy`. +Functions that are responsible for constructing objects should take one of the +two following forms: -A destruction function should always be able to accept a NULL pointer or a -zeroed value and exit cleanly; this simplifies error handling a lot. +* `init`: for functions which accept a pointer to a pre-allocated object (e.g. +a member of a struct) and initialize it. +* `create`: for functions which allocate the memory for an object, initialize +it, and return a pointer. + +Likewise, functions that are responsible for destructing objects should take +one of the two following forms: + +* `finish`: for functions which accept a pointer to an object and deinitialize +it. Such functions should always be able to accept an already deinitialized +object. +* `destroy`: for functions which accept a pointer to an object, deinitialize +it, and free the memory. Such functions should always be able to accept a NULL +pointer. ### Error Codes diff --git a/README.md b/README.md index 33c59104b..418aab94b 100644 --- a/README.md +++ b/README.md @@ -32,9 +32,8 @@ to implement yourself. Check out our [wiki] to get started with wlroots. Join our IRC channel: [#sway-devel on Libera Chat]. -wlroots is developed under the direction of the [sway] project. A variety of -[wrapper libraries] are available for using it with your favorite programming -language. +A variety of [wrapper libraries] are available for using it with your favorite +programming language. ## Building @@ -77,7 +76,6 @@ See [CONTRIBUTING.md]. [Wayland]: https://wayland.freedesktop.org/ [wiki]: https://gitlab.freedesktop.org/wlroots/wlroots/-/wikis/Getting-started [#sway-devel on Libera Chat]: https://web.libera.chat/gamja/?channels=#sway-devel -[Sway]: https://github.com/swaywm/sway -[wrapper libraries]: https://github.com/search?q=topic%3Abindings+org%3Aswaywm&type=Repositories +[wrapper libraries]: https://gitlab.freedesktop.org/wlroots/wlroots/-/wikis/Projects-which-use-wlroots#wrapper-libraries [libseat]: https://git.sr.ht/~kennylevinsen/seatd [CONTRIBUTING.md]: https://gitlab.freedesktop.org/wlroots/wlroots/-/blob/master/CONTRIBUTING.md diff --git a/backend/backend.c b/backend/backend.c index f8fa45c4a..bfb43ba07 100644 --- a/backend/backend.c +++ b/backend/backend.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -22,6 +21,7 @@ #if WLR_HAS_DRM_BACKEND #include +#include "backend/drm/monitor.h" #endif #if WLR_HAS_LIBINPUT_BACKEND @@ -45,10 +45,6 @@ void wlr_backend_init(struct wlr_backend *backend, void wlr_backend_finish(struct wlr_backend *backend) { wlr_signal_emit_safe(&backend->events.destroy, backend); - wlr_allocator_destroy(backend->allocator); - if (backend->has_own_renderer) { - wlr_renderer_destroy(backend->renderer); - } } bool wlr_backend_start(struct wlr_backend *backend) { @@ -70,36 +66,6 @@ void wlr_backend_destroy(struct wlr_backend *backend) { } } -static bool backend_create_renderer(struct wlr_backend *backend) { - if (backend->renderer != NULL) { - return true; - } - - backend->renderer = wlr_renderer_autocreate(backend); - if (backend->renderer == NULL) { - return false; - } - - backend->has_own_renderer = true; - return true; -} - -struct wlr_renderer *wlr_backend_get_renderer(struct wlr_backend *backend) { - if (backend->impl->get_renderer) { - return backend->impl->get_renderer(backend); - } - if (backend_get_buffer_caps(backend) != 0) { - // If the backend is capable of presenting buffers, automatically create - // the renderer if necessary. - if (!backend_create_renderer(backend)) { - wlr_log(WLR_ERROR, "Failed to create backend renderer"); - return NULL; - } - return backend->renderer; - } - return NULL; -} - struct wlr_session *wlr_backend_get_session(struct wlr_backend *backend) { if (backend->impl->get_session) { return backend->impl->get_session(backend); @@ -175,23 +141,6 @@ uint32_t backend_get_buffer_caps(struct wlr_backend *backend) { return backend->impl->get_buffer_caps(backend); } -struct wlr_allocator *backend_get_allocator(struct wlr_backend *backend) { - if (backend->allocator != NULL) { - return backend->allocator; - } - - struct wlr_renderer *renderer = wlr_backend_get_renderer(backend); - if (renderer == NULL) { - return NULL; - } - - backend->allocator = wlr_allocator_autocreate(backend, renderer); - if (backend->allocator == NULL) { - wlr_log(WLR_ERROR, "Failed to create backend allocator"); - } - return backend->allocator; -} - static size_t parse_outputs_env(const char *name) { const char *outputs_str = getenv(name); if (outputs_str == NULL) { @@ -208,21 +157,6 @@ static size_t parse_outputs_env(const char *name) { return outputs; } -static struct wlr_backend *ensure_backend_renderer_and_allocator( - struct wlr_backend *backend) { - struct wlr_renderer *renderer = wlr_backend_get_renderer(backend); - if (renderer == NULL) { - wlr_backend_destroy(backend); - return NULL; - } - struct wlr_allocator *allocator = backend_get_allocator(backend); - if (allocator == NULL) { - wlr_backend_destroy(backend); - return NULL; - } - return backend; -} - static struct wlr_backend *attempt_wl_backend(struct wl_display *display) { struct wlr_backend *backend = wlr_wl_backend_create(display, NULL); if (backend == NULL) { @@ -234,7 +168,7 @@ static struct wlr_backend *attempt_wl_backend(struct wl_display *display) { wlr_wl_output_create(backend); } - return ensure_backend_renderer_and_allocator(backend); + return backend; } #if WLR_HAS_X11_BACKEND @@ -250,7 +184,7 @@ static struct wlr_backend *attempt_x11_backend(struct wl_display *display, wlr_x11_output_create(backend); } - return ensure_backend_renderer_and_allocator(backend); + return backend; } #endif @@ -266,20 +200,6 @@ static struct wlr_backend *attempt_headless_backend( wlr_headless_add_output(backend, 1280, 720); } - return ensure_backend_renderer_and_allocator(backend); -} - -static struct wlr_backend *attempt_noop_backend(struct wl_display *display) { - struct wlr_backend *backend = wlr_noop_backend_create(display); - if (backend == NULL) { - return NULL; - } - - size_t outputs = parse_outputs_env("WLR_NOOP_OUTPUTS"); - for (size_t i = 0; i < outputs; ++i) { - wlr_noop_add_output(backend); - } - return backend; } @@ -320,50 +240,48 @@ static struct wlr_backend *attempt_drm_backend(struct wl_display *display, return NULL; } - return ensure_backend_renderer_and_allocator(primary_drm); + return primary_drm; } #endif -static struct wlr_backend *attempt_backend_by_name(struct wl_display *display, - struct wlr_backend *backend, struct wlr_session **session, - const char *name) { +static bool attempt_backend_by_name(struct wl_display *display, + struct wlr_multi_backend *multi, char *name) { + struct wlr_backend *backend = NULL; if (strcmp(name, "wayland") == 0) { - return attempt_wl_backend(display); + backend = attempt_wl_backend(display); #if WLR_HAS_X11_BACKEND } else if (strcmp(name, "x11") == 0) { - return attempt_x11_backend(display, NULL); + backend = attempt_x11_backend(display, NULL); #endif } else if (strcmp(name, "headless") == 0) { - return attempt_headless_backend(display); - } else if (strcmp(name, "noop") == 0) { - return attempt_noop_backend(display); + backend = attempt_headless_backend(display); } else if (strcmp(name, "drm") == 0 || strcmp(name, "libinput") == 0) { // DRM and libinput need a session - if (!*session) { - *session = session_create_and_wait(display); - if (!*session) { + if (multi->session == NULL) { + multi->session = session_create_and_wait(display); + if (multi->session == NULL) { wlr_log(WLR_ERROR, "failed to start a session"); - return NULL; + return false; } } if (strcmp(name, "libinput") == 0) { #if WLR_HAS_LIBINPUT_BACKEND - return wlr_libinput_backend_create(display, *session); -#else - return NULL; + backend = wlr_libinput_backend_create(display, multi->session); #endif } else { #if WLR_HAS_DRM_BACKEND - return attempt_drm_backend(display, backend, *session); -#else - return NULL; + // attempt_drm_backend adds the multi drm backends itself + return attempt_drm_backend(display, &multi->backend, + multi->session) != NULL; #endif } + } else { + wlr_log(WLR_ERROR, "unrecognized backend '%s'", name); + return false; } - wlr_log(WLR_ERROR, "unrecognized backend '%s'", name); - return NULL; + return wlr_multi_backend_add(&multi->backend, backend); } struct wlr_backend *wlr_backend_autocreate(struct wl_display *display) { @@ -389,17 +307,7 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display) { char *saveptr; char *name = strtok_r(names, ",", &saveptr); while (name != NULL) { - struct wlr_backend *subbackend = attempt_backend_by_name(display, - backend, &multi->session, name); - if (subbackend == NULL) { - wlr_log(WLR_ERROR, "failed to start backend '%s'", name); - wlr_session_destroy(multi->session); - wlr_backend_destroy(backend); - free(names); - return NULL; - } - - if (!wlr_multi_backend_add(backend, subbackend)) { + if (!attempt_backend_by_name(display, multi, name)) { wlr_log(WLR_ERROR, "failed to add backend '%s'", name); wlr_session_destroy(multi->session); wlr_backend_destroy(backend); @@ -456,6 +364,19 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display) { return NULL; } wlr_multi_backend_add(backend, libinput); +#else + const char *no_devs = getenv("WLR_LIBINPUT_NO_DEVICES"); + if (no_devs && strcmp(no_devs, "1") == 0) { + wlr_log(WLR_INFO, "WLR_LIBINPUT_NO_DEVICES is set, " + "starting without libinput backend"); + } else { + wlr_log(WLR_ERROR, "libinput support is not compiled in, " + "refusing to start"); + wlr_log(WLR_ERROR, "Set WLR_LIBINPUT_NO_DEVICES=1 to suppress this check"); + wlr_session_destroy(multi->session); + wlr_backend_destroy(backend); + return NULL; + } #endif #if WLR_HAS_DRM_BACKEND @@ -468,6 +389,8 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display) { return NULL; } + drm_backend_monitor_create(backend, primary_drm, multi->session); + return backend; #endif diff --git a/backend/drm/backend.c b/backend/drm/backend.c index cd5b8ee0e..10f324a02 100644 --- a/backend/drm/backend.c +++ b/backend/drm/backend.c @@ -133,12 +133,15 @@ static void handle_dev_change(struct wl_listener *listener, void *data) { return; } - // TODO: add and handle lease uevents switch (change->type) { - case WLR_DEVICE_HOTPLUG:; + case WLR_DEVICE_HOTPLUG: wlr_log(WLR_DEBUG, "Received hotplug event for %s", drm->name); scan_drm_connectors(drm, &change->hotplug); break; + case WLR_DEVICE_LEASE: + wlr_log(WLR_DEBUG, "Received lease event for %s", drm->name); + scan_drm_leases(drm); + break; default: wlr_log(WLR_DEBUG, "Received unknown change event for %s", drm->name); } @@ -232,10 +235,6 @@ struct wlr_backend *wlr_drm_backend_create(struct wl_display *display, } if (drm->parent) { - // Ensure we use the same renderer as the parent backend - drm->backend.renderer = wlr_backend_get_renderer(&drm->parent->backend); - assert(drm->backend.renderer != NULL); - if (!init_drm_renderer(drm, &drm->mgpu_renderer)) { wlr_log(WLR_ERROR, "Failed to initialize renderer"); goto error_resources; diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 863861d6e..83279a0d0 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -118,9 +118,15 @@ static bool add_plane(struct wlr_drm_backend *drm, p->id = drm_plane->plane_id; p->props = *props; - for (size_t j = 0; j < drm_plane->count_formats; ++j) { - wlr_drm_format_set_add(&p->formats, drm_plane->formats[j], - DRM_FORMAT_MOD_INVALID); + for (size_t i = 0; i < drm_plane->count_formats; ++i) { + // Force a LINEAR layout for the cursor if the driver doesn't support + // modifiers + wlr_drm_format_set_add(&p->formats, drm_plane->formats[i], + DRM_FORMAT_MOD_LINEAR); + if (type != DRM_PLANE_TYPE_CURSOR) { + wlr_drm_format_set_add(&p->formats, drm_plane->formats[i], + DRM_FORMAT_MOD_INVALID); + } } if (p->props.in_formats && drm->addfb2_modifiers) { @@ -136,27 +142,12 @@ static bool add_plane(struct wlr_drm_backend *drm, goto error; } - struct drm_format_modifier_blob *data = blob->data; - uint32_t *fmts = (uint32_t *)((char *)data + data->formats_offset); - struct drm_format_modifier *mods = (struct drm_format_modifier *) - ((char *)data + data->modifiers_offset); - for (uint32_t i = 0; i < data->count_modifiers; ++i) { - for (int j = 0; j < 64; ++j) { - if (mods[i].formats & ((uint64_t)1 << j)) { - wlr_drm_format_set_add(&p->formats, - fmts[j + mods[i].offset], mods[i].modifier); - } - } + drmModeFormatModifierIterator iter = {0}; + while (drmModeFormatModifierBlobIterNext(blob, &iter)) { + wlr_drm_format_set_add(&p->formats, iter.fmt, iter.mod); } drmModeFreePropertyBlob(blob); - } else if (type == DRM_PLANE_TYPE_CURSOR) { - // Force a LINEAR layout for the cursor if the driver doesn't support - // modifiers - for (size_t i = 0; i < p->formats.len; ++i) { - wlr_drm_format_set_add(&p->formats, p->formats.formats[i]->format, - DRM_FORMAT_MOD_LINEAR); - } } switch (type) { @@ -1300,8 +1291,7 @@ void scan_drm_connectors(struct wlr_drm_backend *drm, 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_output_set_name(&wlr_conn->output, wlr_conn->name); wlr_conn->output.phys_width = drm_conn->mmWidth; wlr_conn->output.phys_height = drm_conn->mmHeight; @@ -1431,6 +1421,36 @@ void scan_drm_connectors(struct wlr_drm_backend *drm, } } +void scan_drm_leases(struct wlr_drm_backend *drm) { + drmModeLesseeListRes *list = drmModeListLessees(drm->fd); + if (list == NULL) { + wlr_log_errno(WLR_ERROR, "drmModeListLessees failed"); + return; + } + + struct wlr_drm_connector *conn; + wl_list_for_each(conn, &drm->outputs, link) { + if (conn->lease == NULL) { + continue; + } + + bool found = false; + for (size_t i = 0; i < list->count; i++) { + if (list->lessees[i] == conn->lease->lessee_id) { + found = true; + break; + } + } + if (!found) { + wlr_log(WLR_DEBUG, "DRM lease %"PRIu32" has been terminated", + conn->lease->lessee_id); + drm_lease_destroy(conn->lease); + } + } + + drmFree(list); +} + static int mhz_to_nsec(int mhz) { return 1000000000000LL / mhz; } @@ -1560,17 +1580,13 @@ int wlr_drm_backend_get_non_master_fd(struct wlr_backend *backend) { return fd; } -/* TODO: make the function return a `wlr_drm_lease` to provide a destroy event - * that can be fired when the kernel notifies us through uevent that the lease - * has been destroyed - */ -int wlr_drm_create_lease(struct wlr_output **outputs, size_t n_outputs, - uint32_t *lessee_id) { +struct wlr_drm_lease *wlr_drm_create_lease(struct wlr_output **outputs, + size_t n_outputs, int *lease_fd_ptr) { assert(outputs); if (n_outputs == 0) { wlr_log(WLR_ERROR, "Can't lease 0 outputs"); - return -1; + return NULL; } struct wlr_drm_backend *drm = @@ -1581,11 +1597,11 @@ int wlr_drm_create_lease(struct wlr_output **outputs, size_t n_outputs, for (size_t i = 0; i < n_outputs; ++i) { struct wlr_drm_connector *conn = get_drm_connector_from_output(outputs[i]); - assert(conn->lessee_id == 0); + assert(conn->lease == NULL); if (conn->backend != drm) { wlr_log(WLR_ERROR, "Can't lease output from different backends"); - return -1; + return NULL; } objects[n_objects++] = conn->id; @@ -1593,7 +1609,7 @@ int wlr_drm_create_lease(struct wlr_output **outputs, size_t n_outputs, if (!conn->crtc) { wlr_log(WLR_ERROR, "Connector has no CRTC"); - return -1; + return NULL; } objects[n_objects++] = conn->crtc->id; @@ -1610,50 +1626,63 @@ int wlr_drm_create_lease(struct wlr_output **outputs, size_t n_outputs, assert(n_objects != 0); - wlr_log(WLR_DEBUG, "Issuing DRM lease with the %d objects", n_objects); - int lease_fd = drmModeCreateLease(drm->fd, objects, n_objects, 0, - lessee_id); - if (lease_fd < 0) { - return lease_fd; + struct wlr_drm_lease *lease = calloc(1, sizeof(*lease)); + if (lease == NULL) { + return NULL; } - wlr_log(WLR_DEBUG, "Issued DRM lease %"PRIu32, *lessee_id); + lease->backend = drm; + wl_signal_init(&lease->events.destroy); + + wlr_log(WLR_DEBUG, "Issuing DRM lease with %d objects", n_objects); + int lease_fd = drmModeCreateLease(drm->fd, objects, n_objects, 0, + &lease->lessee_id); + if (lease_fd < 0) { + free(lease); + return NULL; + } + *lease_fd_ptr = lease_fd; + + wlr_log(WLR_DEBUG, "Issued DRM lease %"PRIu32, lease->lessee_id); for (size_t i = 0; i < n_outputs; ++i) { struct wlr_drm_connector *conn = get_drm_connector_from_output(outputs[i]); - conn->lessee_id = *lessee_id; - conn->crtc->lessee_id = *lessee_id; + conn->lease = lease; + conn->crtc->lease = lease; } - return lease_fd; + return lease; } -bool wlr_drm_backend_terminate_lease(struct wlr_backend *backend, - uint32_t lessee_id) { - wlr_log(WLR_DEBUG, "Terminating DRM lease %d", lessee_id); +void wlr_drm_lease_terminate(struct wlr_drm_lease *lease) { + struct wlr_drm_backend *drm = lease->backend; - assert(backend); - struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend); - - int r = drmModeRevokeLease(drm->fd, lessee_id); - if (r < 0) { - wlr_log_errno(WLR_DEBUG, "Failed to terminate lease"); + wlr_log(WLR_DEBUG, "Terminating DRM lease %d", lease->lessee_id); + int ret = drmModeRevokeLease(drm->fd, lease->lessee_id); + if (ret < 0) { + wlr_log_errno(WLR_ERROR, "Failed to terminate lease"); } + drm_lease_destroy(lease); +} + +void drm_lease_destroy(struct wlr_drm_lease *lease) { + struct wlr_drm_backend *drm = lease->backend; + + wlr_signal_emit_safe(&lease->events.destroy, NULL); + struct wlr_drm_connector *conn; wl_list_for_each(conn, &drm->outputs, link) { - if (conn->lessee_id == lessee_id) { - conn->lessee_id = 0; - /* Will be re-initialized in scan_drm_connectors */ + if (conn->lease == lease) { + conn->lease = NULL; } } for (size_t i = 0; i < drm->num_crtcs; ++i) { - if (drm->crtcs[i].lessee_id == lessee_id) { - drm->crtcs[i].lessee_id = 0; + if (drm->crtcs[i].lease == lease) { + drm->crtcs[i].lease = NULL; } } - return r >= 0; + free(lease); } - diff --git a/backend/drm/legacy.c b/backend/drm/legacy.c index cb672023b..84e8a914f 100644 --- a/backend/drm/legacy.c +++ b/backend/drm/legacy.c @@ -151,9 +151,13 @@ static bool legacy_crtc_commit(struct wlr_drm_connector *conn, int ret = drmModeSetCursor(drm->fd, crtc->id, cursor_handle, cursor_width, cursor_height); - close_bo_handle(drm->fd, cursor_handle); + int set_cursor_errno = errno; + if (drmCloseBufferHandle(drm->fd, cursor_handle) != 0) { + wlr_log_errno(WLR_ERROR, "drmCloseBufferHandle failed"); + } if (ret != 0) { - wlr_drm_conn_log_errno(conn, WLR_DEBUG, "drmModeSetCursor failed"); + wlr_drm_conn_log(conn, WLR_DEBUG, "drmModeSetCursor failed: %s", + strerror(set_cursor_errno)); return false; } diff --git a/backend/drm/meson.build b/backend/drm/meson.build index b076b4728..cc791f362 100644 --- a/backend/drm/meson.build +++ b/backend/drm/meson.build @@ -4,6 +4,7 @@ wlr_files += files( 'cvt.c', 'drm.c', 'legacy.c', + 'monitor.c', 'properties.c', 'renderer.c', 'util.c', diff --git a/backend/drm/monitor.c b/backend/drm/monitor.c new file mode 100644 index 000000000..539e7925f --- /dev/null +++ b/backend/drm/monitor.c @@ -0,0 +1,94 @@ +#include +#include +#include "backend/drm/monitor.h" +#include "backend/multi.h" +#include "backend/session/session.h" + +static void drm_backend_monitor_destroy(struct wlr_drm_backend_monitor* monitor) { + wl_list_remove(&monitor->session_add_drm_card.link); + wl_list_remove(&monitor->session_destroy.link); + wl_list_remove(&monitor->primary_drm_destroy.link); + wl_list_remove(&monitor->multi_destroy.link); + free(monitor); +} + +static void handle_add_drm_card(struct wl_listener *listener, void *data) { + struct wlr_session_add_event *event = data; + struct wlr_drm_backend_monitor *backend_monitor = + wl_container_of(listener, backend_monitor, session_add_drm_card); + + struct wlr_device *dev = + session_open_if_kms(backend_monitor->session, event->path); + if (!dev) { + wlr_log(WLR_ERROR, "Unable to open %s as DRM device", event->path); + return; + } + + wlr_log(WLR_DEBUG, "Creating DRM backend for %s after hotplug", event->path); + struct wlr_backend *child_drm = wlr_drm_backend_create( + backend_monitor->session->display, backend_monitor->session, + dev, backend_monitor->primary_drm); + if (!child_drm) { + wlr_log(WLR_ERROR, "Failed to create DRM backend after hotplug"); + return; + } + + if (!wlr_multi_backend_add(backend_monitor->multi, child_drm)) { + wlr_log(WLR_ERROR, "Failed to add new drm backend to multi backend"); + wlr_backend_destroy(child_drm); + return; + } + + if (!wlr_backend_start(child_drm)) { + wlr_log(WLR_ERROR, "Failed to start new child DRM backend"); + wlr_backend_destroy(child_drm); + } +} + +static void handle_session_destroy(struct wl_listener *listener, void *data) { + struct wlr_drm_backend_monitor *backend_monitor = + wl_container_of(listener, backend_monitor, session_destroy); + drm_backend_monitor_destroy(backend_monitor); +} + +static void handle_primary_drm_destroy(struct wl_listener *listener, void *data) { + struct wlr_drm_backend_monitor *backend_monitor = + wl_container_of(listener, backend_monitor, primary_drm_destroy); + drm_backend_monitor_destroy(backend_monitor); +} + +static void handle_multi_destroy(struct wl_listener *listener, void *data) { + struct wlr_drm_backend_monitor *backend_monitor = + wl_container_of(listener, backend_monitor, multi_destroy); + drm_backend_monitor_destroy(backend_monitor); +} + +struct wlr_drm_backend_monitor *drm_backend_monitor_create( + struct wlr_backend *multi, + struct wlr_backend *primary_drm, + struct wlr_session *session) { + struct wlr_drm_backend_monitor *monitor = + calloc(1, sizeof(struct wlr_drm_backend_monitor)); + if (!monitor) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + return NULL; + } + + monitor->multi = multi; + monitor->primary_drm = primary_drm; + monitor->session = session; + + monitor->session_add_drm_card.notify = handle_add_drm_card; + wl_signal_add(&session->events.add_drm_card, &monitor->session_add_drm_card); + + monitor->session_destroy.notify = handle_session_destroy; + wl_signal_add(&session->events.destroy, &monitor->session_destroy); + + monitor->primary_drm_destroy.notify = handle_primary_drm_destroy; + wl_signal_add(&primary_drm->events.destroy, &monitor->primary_drm_destroy); + + monitor->multi_destroy.notify = handle_multi_destroy; + wl_signal_add(&multi->events.destroy, &monitor->multi_destroy); + + return monitor; +} diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c index 40e068cd8..792de938f 100644 --- a/backend/drm/renderer.c +++ b/backend/drm/renderer.c @@ -149,7 +149,7 @@ struct wlr_drm_format *drm_plane_pick_render_format( const struct wlr_drm_format_set *plane_formats = &plane->formats; uint32_t fmt = DRM_FORMAT_ARGB8888; - if (!wlr_drm_format_set_has(&plane->formats, fmt, DRM_FORMAT_MOD_INVALID)) { + if (!wlr_drm_format_set_get(&plane->formats, fmt)) { const struct wlr_pixel_format_info *format_info = drm_get_pixel_format_info(fmt); assert(format_info != NULL && @@ -220,6 +220,13 @@ static uint32_t get_fb_for_bo(struct wlr_drm_backend *drm, wlr_log_errno(WLR_DEBUG, "drmModeAddFB2WithModifiers failed"); } } else { + if (dmabuf->modifier != DRM_FORMAT_MOD_INVALID && + dmabuf->modifier != DRM_FORMAT_MOD_LINEAR) { + wlr_log(WLR_ERROR, "Cannot import DRM framebuffer with explicit " + "modifier 0x%"PRIX64, dmabuf->modifier); + return 0; + } + int ret = drmModeAddFB2(drm->fd, dmabuf->width, dmabuf->height, dmabuf->format, handles, dmabuf->stride, dmabuf->offset, &id, 0); if (ret != 0 && dmabuf->format == DRM_FORMAT_ARGB8888 && @@ -264,30 +271,62 @@ static void close_all_bo_handles(struct wlr_drm_backend *drm, continue; } - close_bo_handle(drm->fd, handles[i]); + if (drmCloseBufferHandle(drm->fd, handles[i]) != 0) { + wlr_log_errno(WLR_ERROR, "drmCloseBufferHandle failed"); + } } } +static void drm_poisoned_fb_handle_destroy(struct wlr_addon *addon) { + wlr_addon_finish(addon); + free(addon); +} + +static const struct wlr_addon_interface poisoned_fb_addon_impl = { + .name = "wlr_drm_poisoned_fb", + .destroy = drm_poisoned_fb_handle_destroy, +}; + +static bool is_buffer_poisoned(struct wlr_drm_backend *drm, + struct wlr_buffer *buf) { + return wlr_addon_find(&buf->addons, drm, &poisoned_fb_addon_impl) != NULL; +} + +/** + * Mark the buffer as "poisoned", ie. it cannot be imported into KMS. This + * allows us to avoid repeatedly trying to import it when it's not + * scanout-capable. + */ +static void poison_buffer(struct wlr_drm_backend *drm, + struct wlr_buffer *buf) { + struct wlr_addon *addon = calloc(1, sizeof(*addon)); + if (addon == NULL) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + return; + } + wlr_addon_init(addon, &buf->addons, drm, &poisoned_fb_addon_impl); + wlr_log(WLR_DEBUG, "Poisoning buffer"); +} + static struct wlr_drm_fb *drm_fb_create(struct wlr_drm_backend *drm, struct wlr_buffer *buf, const struct wlr_drm_format_set *formats) { + struct wlr_dmabuf_attributes attribs; + if (!wlr_buffer_get_dmabuf(buf, &attribs)) { + wlr_log(WLR_DEBUG, "Failed to get DMA-BUF from buffer"); + return NULL; + } + + if (is_buffer_poisoned(drm, buf)) { + wlr_log(WLR_DEBUG, "Buffer is poisoned"); + return NULL; + } + struct wlr_drm_fb *fb = calloc(1, sizeof(*fb)); if (!fb) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return NULL; } - struct wlr_dmabuf_attributes attribs; - if (!wlr_buffer_get_dmabuf(buf, &attribs)) { - wlr_log(WLR_DEBUG, "Failed to get DMA-BUF from buffer"); - goto error_get_dmabuf; - } - - if (attribs.flags != 0) { - wlr_log(WLR_DEBUG, "Buffer with DMA-BUF flags 0x%"PRIX32" cannot be " - "scanned out", attribs.flags); - goto error_get_dmabuf; - } - if (formats && !wlr_drm_format_set_has(formats, attribs.format, attribs.modifier)) { // The format isn't supported by the plane. Try stripping the alpha @@ -301,7 +340,7 @@ static struct wlr_drm_fb *drm_fb_create(struct wlr_drm_backend *drm, wlr_log(WLR_DEBUG, "Buffer format 0x%"PRIX32" with modifier " "0x%"PRIX64" cannot be scanned out", attribs.format, attribs.modifier); - goto error_get_dmabuf; + goto error_fb; } } @@ -317,6 +356,7 @@ static struct wlr_drm_fb *drm_fb_create(struct wlr_drm_backend *drm, fb->id = get_fb_for_bo(drm, &attribs, handles); if (!fb->id) { wlr_log(WLR_DEBUG, "Failed to import BO in KMS"); + poison_buffer(drm, buf); goto error_bo_handle; } @@ -332,7 +372,7 @@ static struct wlr_drm_fb *drm_fb_create(struct wlr_drm_backend *drm, error_bo_handle: close_all_bo_handles(drm, handles); -error_get_dmabuf: +error_fb: free(fb); return NULL; } diff --git a/backend/drm/util.c b/backend/drm/util.c index b7ed7edaa..86a4b8eac 100644 --- a/backend/drm/util.c +++ b/backend/drm/util.c @@ -355,14 +355,3 @@ size_t match_obj(size_t num_objs, const uint32_t objs[static restrict num_objs], match_obj_(&st, 0, 0, 0, 0); return st.score; } - -void close_bo_handle(int drm_fd, uint32_t handle) { - if (handle == 0) { - return; - } - - struct drm_gem_close args = { .handle = handle }; - if (drmIoctl(drm_fd, DRM_IOCTL_GEM_CLOSE, &args) != 0) { - wlr_log_errno(WLR_ERROR, "drmIoctl(GEM_CLOSE) failed"); - } -} diff --git a/backend/headless/backend.c b/backend/headless/backend.c index 49c2c8bac..b14dda4a9 100644 --- a/backend/headless/backend.c +++ b/backend/headless/backend.c @@ -1,16 +1,8 @@ -#define _POSIX_C_SOURCE 200809L #include -#include -#include #include -#include -#include #include -#include #include -#include #include "backend/headless.h" -#include "render/drm_format_set.h" #include "util/signal.h" struct wlr_headless_backend *headless_backend_from_backend( @@ -32,13 +24,6 @@ static bool backend_start(struct wlr_backend *wlr_backend) { &output->wlr_output); } - struct wlr_headless_input_device *input_device; - wl_list_for_each(input_device, &backend->input_devices, - wlr_input_device.link) { - wlr_signal_emit_safe(&backend->backend.events.new_input, - &input_device->wlr_input_device); - } - backend->started = true; return true; } @@ -51,42 +36,17 @@ static void backend_destroy(struct wlr_backend *wlr_backend) { } wl_list_remove(&backend->display_destroy.link); - wl_list_remove(&backend->parent_renderer_destroy.link); struct wlr_headless_output *output, *output_tmp; wl_list_for_each_safe(output, output_tmp, &backend->outputs, link) { wlr_output_destroy(&output->wlr_output); } - struct wlr_headless_input_device *input_device, *input_device_tmp; - wl_list_for_each_safe(input_device, input_device_tmp, - &backend->input_devices, wlr_input_device.link) { - wlr_input_device_destroy(&input_device->wlr_input_device); - } - wlr_backend_finish(wlr_backend); - close(backend->drm_fd); free(backend); } -static struct wlr_renderer *backend_get_renderer( - struct wlr_backend *wlr_backend) { - struct wlr_headless_backend *backend = - headless_backend_from_backend(wlr_backend); - if (backend->parent_renderer != NULL) { - return backend->parent_renderer; - } else { - return wlr_backend->renderer; - } -} - -static int backend_get_drm_fd(struct wlr_backend *wlr_backend) { - struct wlr_headless_backend *backend = - headless_backend_from_backend(wlr_backend); - return backend->drm_fd; -} - static uint32_t get_buffer_caps(struct wlr_backend *wlr_backend) { return WLR_BUFFER_CAP_DATA_PTR | WLR_BUFFER_CAP_DMABUF @@ -96,8 +56,6 @@ static uint32_t get_buffer_caps(struct wlr_backend *wlr_backend) { static const struct wlr_backend_impl backend_impl = { .start = backend_start, .destroy = backend_destroy, - .get_renderer = backend_get_renderer, - .get_drm_fd = backend_get_drm_fd, .get_buffer_caps = get_buffer_caps, }; @@ -107,86 +65,6 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { backend_destroy(&backend->backend); } -static void handle_renderer_destroy(struct wl_listener *listener, void *data) { - struct wlr_headless_backend *backend = - wl_container_of(listener, backend, parent_renderer_destroy); - backend_destroy(&backend->backend); -} - -static bool backend_init(struct wlr_headless_backend *backend, - struct wl_display *display, 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); - wl_list_init(&backend->parent_renderer_destroy.link); - - if (renderer == NULL) { - renderer = wlr_renderer_autocreate(&backend->backend); - if (!renderer) { - wlr_log(WLR_ERROR, "Failed to create renderer"); - return false; - } - backend->backend.renderer = renderer; - } else { - backend->parent_renderer = renderer; - backend->parent_renderer_destroy.notify = handle_renderer_destroy; - wl_signal_add(&renderer->events.destroy, &backend->parent_renderer_destroy); - } - - backend->display_destroy.notify = handle_display_destroy; - wl_display_add_destroy_listener(display, &backend->display_destroy); - - 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: %s", strerror(-devices_len)); - 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: %s", strerror(-devices_len)); - 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_log(WLR_INFO, "Creating headless backend"); @@ -197,55 +75,15 @@ struct wlr_backend *wlr_headless_backend_create(struct wl_display *display) { return NULL; } - backend->drm_fd = open_drm_render_node(); - if (backend->drm_fd < 0) { - wlr_log(WLR_ERROR, "Failed to open DRM render node"); - } + wlr_backend_init(&backend->backend, &backend_impl); - if (!backend_init(backend, display, NULL)) { - goto error_init; - } + backend->display = display; + wl_list_init(&backend->outputs); + + backend->display_destroy.notify = handle_display_destroy; + wl_display_add_destroy_listener(display, &backend->display_destroy); return &backend->backend; - -error_init: - close(backend->drm_fd); - free(backend); - 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 with parent renderer"); - - 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; - } - - int drm_fd = wlr_renderer_get_drm_fd(renderer); - if (drm_fd < 0) { - wlr_log(WLR_ERROR, "Failed to get DRM device FD from parent renderer"); - backend->drm_fd = -1; - } else { - backend->drm_fd = fcntl(drm_fd, F_DUPFD_CLOEXEC, 0); - if (backend->drm_fd < 0) { - wlr_log_errno(WLR_ERROR, "fcntl(F_DUPFD_CLOEXEC) failed"); - } - } - - if (!backend_init(backend, display, renderer)) { - goto error_init; - } - - return &backend->backend; - -error_init: - close(backend->drm_fd); - free(backend); - return NULL; } bool wlr_backend_is_headless(struct wlr_backend *backend) { diff --git a/backend/headless/input_device.c b/backend/headless/input_device.c deleted file mode 100644 index 76e69c523..000000000 --- a/backend/headless/input_device.c +++ /dev/null @@ -1,99 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "backend/headless.h" -#include "util/signal.h" - -static const struct wlr_input_device_impl input_device_impl = { 0 }; - -bool wlr_input_device_is_headless(struct wlr_input_device *wlr_dev) { - return wlr_dev->impl == &input_device_impl; -} - -struct wlr_input_device *wlr_headless_add_input_device( - struct wlr_backend *wlr_backend, enum wlr_input_device_type type) { - struct wlr_headless_backend *backend = - headless_backend_from_backend(wlr_backend); - - struct wlr_headless_input_device *device = - calloc(1, sizeof(struct wlr_headless_input_device)); - if (device == NULL) { - return NULL; - } - device->backend = backend; - - int vendor = 0; - int product = 0; - const char *name = "headless"; - struct wlr_input_device *wlr_device = &device->wlr_input_device; - wlr_input_device_init(wlr_device, type, &input_device_impl, name, vendor, - product); - - switch (type) { - case WLR_INPUT_DEVICE_KEYBOARD: - wlr_device->keyboard = calloc(1, sizeof(struct wlr_keyboard)); - if (wlr_device->keyboard == NULL) { - wlr_log(WLR_ERROR, "Unable to allocate wlr_keyboard"); - goto error; - } - wlr_keyboard_init(wlr_device->keyboard, NULL); - break; - case WLR_INPUT_DEVICE_POINTER: - wlr_device->pointer = calloc(1, sizeof(struct wlr_pointer)); - if (wlr_device->pointer == NULL) { - wlr_log(WLR_ERROR, "Unable to allocate wlr_pointer"); - goto error; - } - wlr_pointer_init(wlr_device->pointer, NULL); - break; - case WLR_INPUT_DEVICE_TOUCH: - wlr_device->touch = calloc(1, sizeof(struct wlr_touch)); - if (wlr_device->touch == NULL) { - wlr_log(WLR_ERROR, "Unable to allocate wlr_touch"); - goto error; - } - wlr_touch_init(wlr_device->touch, NULL); - break; - case WLR_INPUT_DEVICE_TABLET_TOOL: - wlr_device->tablet = calloc(1, sizeof(struct wlr_tablet)); - if (wlr_device->tablet == NULL) { - wlr_log(WLR_ERROR, "Unable to allocate wlr_tablet"); - goto error; - } - wlr_tablet_init(wlr_device->tablet, NULL); - break; - case WLR_INPUT_DEVICE_TABLET_PAD: - wlr_device->tablet_pad = calloc(1, sizeof(struct wlr_tablet_pad)); - if (wlr_device->tablet_pad == NULL) { - wlr_log(WLR_ERROR, "Unable to allocate wlr_tablet_pad"); - goto error; - } - wlr_tablet_pad_init(wlr_device->tablet_pad, NULL); - break; - case WLR_INPUT_DEVICE_SWITCH: - wlr_device->switch_device = calloc(1, sizeof(struct wlr_switch)); - if (wlr_device->switch_device == NULL) { - wlr_log(WLR_ERROR, "Unable to allocate wlr_switch"); - goto error; - } - wlr_switch_init(wlr_device->switch_device, NULL); - } - - wl_list_insert(&backend->input_devices, &wlr_device->link); - - if (backend->started) { - wlr_signal_emit_safe(&backend->backend.events.new_input, wlr_device); - } - - return wlr_device; -error: - free(device); - return NULL; -} diff --git a/backend/headless/meson.build b/backend/headless/meson.build index e38ce1333..950c07160 100644 --- a/backend/headless/meson.build +++ b/backend/headless/meson.build @@ -1,5 +1,4 @@ wlr_files += files( 'backend.c', - 'input_device.c', 'output.c', ) diff --git a/backend/headless/output.c b/backend/headless/output.c index 43762b239..f03d6ee0d 100644 --- a/backend/headless/output.c +++ b/backend/headless/output.c @@ -2,7 +2,6 @@ #include #include #include -#include #include #include "backend/headless.h" #include "util/signal.h" @@ -117,12 +116,14 @@ struct wlr_output *wlr_headless_add_output(struct wlr_backend *wlr_backend, output_set_custom_mode(output, width, height, 0); strncpy(wlr_output->make, "headless", sizeof(wlr_output->make)); strncpy(wlr_output->model, "headless", sizeof(wlr_output->model)); - snprintf(wlr_output->name, sizeof(wlr_output->name), "HEADLESS-%zd", - ++backend->last_output_num); + + char name[64]; + snprintf(name, sizeof(name), "HEADLESS-%zu", ++backend->last_output_num); + wlr_output_set_name(wlr_output, name); char description[128]; snprintf(description, sizeof(description), - "Headless output %zd", backend->last_output_num); + "Headless output %zu", backend->last_output_num); wlr_output_set_description(wlr_output, description); struct wl_event_loop *ev = wl_display_get_event_loop(backend->display); diff --git a/backend/libinput/backend.c b/backend/libinput/backend.c index 00145e419..8023807fd 100644 --- a/backend/libinput/backend.c +++ b/backend/libinput/backend.c @@ -143,9 +143,9 @@ static void backend_destroy(struct wlr_backend *wlr_backend) { struct wl_list **wlr_devices_ptr; wl_array_for_each(wlr_devices_ptr, &backend->wlr_device_lists) { - struct wlr_input_device *wlr_dev, *next; - wl_list_for_each_safe(wlr_dev, next, *wlr_devices_ptr, link) { - wlr_input_device_destroy(wlr_dev); + struct wlr_libinput_input_device *dev, *tmp; + wl_list_for_each_safe(dev, tmp, *wlr_devices_ptr, link) { + destroy_libinput_input_device(dev); } free(*wlr_devices_ptr); } diff --git a/backend/libinput/events.c b/backend/libinput/events.c index 828837794..e8d99f251 100644 --- a/backend/libinput/events.c +++ b/backend/libinput/events.c @@ -4,18 +4,11 @@ #include #include #include -#include #include #include "backend/libinput.h" #include "util/array.h" #include "util/signal.h" -static struct wlr_libinput_input_device *get_libinput_device_from_device( - struct wlr_input_device *wlr_dev) { - assert(wlr_input_device_is_libinput(wlr_dev)); - return (struct wlr_libinput_input_device *)wlr_dev; -} - struct wlr_input_device *get_appropriate_device( enum wlr_input_device_type desired_type, struct libinput_device *libinput_dev) { @@ -23,33 +16,36 @@ struct wlr_input_device *get_appropriate_device( if (!wlr_devices) { return NULL; } - struct wlr_input_device *dev; + struct wlr_libinput_input_device *dev; wl_list_for_each(dev, wlr_devices, link) { - if (dev->type == desired_type) { - return dev; + if (dev->wlr_input_device.type == desired_type) { + return &dev->wlr_input_device; } } return NULL; } -static void input_device_destroy(struct wlr_input_device *wlr_dev) { - struct wlr_libinput_input_device *dev = - get_libinput_device_from_device(wlr_dev); +void destroy_libinput_input_device(struct wlr_libinput_input_device *dev) +{ + /** + * TODO remove the redundant wlr_input_device from wlr_libinput_input_device + * wlr_libinput_input_device::wlr_input_device is not owned by its input + * device type, which means we have 2 wlr_input_device to cleanup + */ + if (dev->wlr_input_device._device) { + wlr_input_device_destroy(&dev->wlr_input_device); + } + wlr_input_device_finish(&dev->wlr_input_device); + libinput_device_unref(dev->handle); - wl_list_remove(&dev->wlr_input_device.link); + wl_list_remove(&dev->link); free(dev); } -static const struct wlr_input_device_impl input_device_impl = { - .destroy = input_device_destroy, -}; - static struct wlr_input_device *allocate_device( struct wlr_libinput_backend *backend, struct libinput_device *libinput_dev, struct wl_list *wlr_devices, enum wlr_input_device_type type) { - int vendor = libinput_device_get_id_vendor(libinput_dev); - int product = libinput_device_get_id_product(libinput_dev); const char *name = libinput_device_get_name(libinput_dev); struct wlr_libinput_input_device *dev = calloc(1, sizeof(struct wlr_libinput_input_device)); @@ -63,16 +59,32 @@ static struct wlr_input_device *allocate_device( if (output_name != NULL) { wlr_dev->output_name = strdup(output_name); } - wl_list_insert(wlr_devices, &wlr_dev->link); + wl_list_insert(wlr_devices, &dev->link); dev->handle = libinput_dev; libinput_device_ref(libinput_dev); - wlr_input_device_init(wlr_dev, type, &input_device_impl, - name, vendor, product); + wlr_input_device_init(wlr_dev, type, name); + wlr_dev->vendor = libinput_device_get_id_vendor(libinput_dev); + wlr_dev->product = libinput_device_get_id_product(libinput_dev); return wlr_dev; } bool wlr_input_device_is_libinput(struct wlr_input_device *wlr_dev) { - return wlr_dev->impl == &input_device_impl; + switch (wlr_dev->type) { + case WLR_INPUT_DEVICE_KEYBOARD: + return wlr_dev->keyboard->impl == &libinput_keyboard_impl; + case WLR_INPUT_DEVICE_POINTER: + return wlr_dev->pointer->impl == &libinput_pointer_impl; + case WLR_INPUT_DEVICE_TOUCH: + return wlr_dev->touch->impl == &libinput_touch_impl; + case WLR_INPUT_DEVICE_TABLET_TOOL: + return wlr_dev->tablet->impl == &libinput_tablet_impl; + case WLR_INPUT_DEVICE_TABLET_PAD: + return wlr_dev->tablet_pad->impl == &libinput_tablet_pad_impl; + case WLR_INPUT_DEVICE_SWITCH: + return wlr_dev->switch_device->impl == &libinput_switch_impl; + default: + return false; + } } static void handle_device_added(struct wlr_libinput_backend *backend, @@ -198,7 +210,7 @@ static void handle_device_added(struct wlr_libinput_backend *backend, fail: wlr_log(WLR_ERROR, "Could not allocate new device"); - struct wlr_input_device *dev, *tmp_dev; + struct wlr_libinput_input_device *dev, *tmp_dev; wl_list_for_each_safe(dev, tmp_dev, wlr_devices, link) { free(dev); } @@ -215,9 +227,9 @@ static void handle_device_removed(struct wlr_libinput_backend *backend, if (!wlr_devices) { return; } - struct wlr_input_device *dev, *tmp_dev; + struct wlr_libinput_input_device *dev, *tmp_dev; wl_list_for_each_safe(dev, tmp_dev, wlr_devices, link) { - wlr_input_device_destroy(dev); + destroy_libinput_input_device(dev); } size_t i = 0; struct wl_list **ptr; diff --git a/backend/libinput/keyboard.c b/backend/libinput/keyboard.c index d5207f542..9127217a8 100644 --- a/backend/libinput/keyboard.c +++ b/backend/libinput/keyboard.c @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include "backend/libinput.h" @@ -12,11 +11,9 @@ struct wlr_libinput_keyboard { struct libinput_device *libinput_dev; }; -static const struct wlr_keyboard_impl impl; - static struct wlr_libinput_keyboard *get_libinput_keyboard_from_keyboard( struct wlr_keyboard *wlr_kb) { - assert(wlr_kb->impl == &impl); + assert(wlr_kb->impl == &libinput_keyboard_impl); return (struct wlr_libinput_keyboard *)wlr_kb; } @@ -33,7 +30,7 @@ static void keyboard_destroy(struct wlr_keyboard *wlr_kb) { free(kb); } -static const struct wlr_keyboard_impl impl = { +const struct wlr_keyboard_impl libinput_keyboard_impl = { .destroy = keyboard_destroy, .led_update = keyboard_set_leds }; @@ -49,7 +46,10 @@ struct wlr_keyboard *create_libinput_keyboard( libinput_device_ref(libinput_dev); libinput_device_led_update(libinput_dev, 0); struct wlr_keyboard *wlr_kb = &kb->wlr_keyboard; - wlr_keyboard_init(wlr_kb, &impl); + const char *name = libinput_device_get_name(libinput_dev); + wlr_keyboard_init(wlr_kb, &libinput_keyboard_impl, name); + wlr_kb->base.vendor = libinput_device_get_id_vendor(libinput_dev); + wlr_kb->base.product = libinput_device_get_id_product(libinput_dev); return wlr_kb; } diff --git a/backend/libinput/pointer.c b/backend/libinput/pointer.c index 520f98dcf..706ace7b5 100644 --- a/backend/libinput/pointer.c +++ b/backend/libinput/pointer.c @@ -2,12 +2,13 @@ #include #include #include -#include #include #include #include "backend/libinput.h" #include "util/signal.h" +const struct wlr_pointer_impl libinput_pointer_impl = {0}; + struct wlr_pointer *create_libinput_pointer( struct libinput_device *libinput_dev) { assert(libinput_dev); @@ -16,7 +17,10 @@ struct wlr_pointer *create_libinput_pointer( wlr_log(WLR_ERROR, "Unable to allocate wlr_pointer"); return NULL; } - wlr_pointer_init(wlr_pointer, NULL); + const char *name = libinput_device_get_name(libinput_dev); + wlr_pointer_init(wlr_pointer, &libinput_pointer_impl, name); + wlr_pointer->base.vendor = libinput_device_get_id_vendor(libinput_dev); + wlr_pointer->base.product = libinput_device_get_id_product(libinput_dev); return wlr_pointer; } diff --git a/backend/libinput/switch.c b/backend/libinput/switch.c index a9c33d3ab..0e710f090 100644 --- a/backend/libinput/switch.c +++ b/backend/libinput/switch.c @@ -8,6 +8,8 @@ #include "backend/libinput.h" #include "util/signal.h" +const struct wlr_switch_impl libinput_switch_impl; + struct wlr_switch *create_libinput_switch( struct libinput_device *libinput_dev) { assert(libinput_dev); @@ -16,8 +18,11 @@ struct wlr_switch *create_libinput_switch( wlr_log(WLR_ERROR, "Unable to allocate wlr_switch"); return NULL; } - wlr_switch_init(wlr_switch, NULL); - wlr_log(WLR_DEBUG, "Created switch for device %s", libinput_device_get_name(libinput_dev)); + const char *name = libinput_device_get_name(libinput_dev); + wlr_switch_init(wlr_switch, &libinput_switch_impl, name); + wlr_log(WLR_DEBUG, "Created switch for device %s", name); + wlr_switch->base.vendor = libinput_device_get_id_vendor(libinput_dev); + wlr_switch->base.product = libinput_device_get_id_product(libinput_dev); return wlr_switch; } diff --git a/backend/libinput/tablet_pad.c b/backend/libinput/tablet_pad.c index c9ec30ae8..50b8ad6e7 100644 --- a/backend/libinput/tablet_pad.c +++ b/backend/libinput/tablet_pad.c @@ -66,6 +66,8 @@ static void add_pad_group_from_libinput(struct wlr_tablet_pad *pad, wl_list_insert(&pad->groups, &group->link); } +const struct wlr_tablet_pad_impl libinput_tablet_pad_impl; + struct wlr_tablet_pad *create_libinput_tablet_pad( struct libinput_device *libinput_dev) { assert(libinput_dev); @@ -75,7 +77,10 @@ struct wlr_tablet_pad *create_libinput_tablet_pad( wlr_log(WLR_ERROR, "Unable to allocate wlr_tablet_pad"); return NULL; } - wlr_tablet_pad_init(wlr_tablet_pad, NULL); + const char *name = libinput_device_get_name(libinput_dev); + wlr_tablet_pad_init(wlr_tablet_pad, &libinput_tablet_pad_impl, name); + wlr_tablet_pad->base.vendor = libinput_device_get_id_vendor(libinput_dev); + wlr_tablet_pad->base.product = libinput_device_get_id_product(libinput_dev); wlr_tablet_pad->button_count = libinput_device_tablet_pad_get_num_buttons(libinput_dev); diff --git a/backend/libinput/tablet_tool.c b/backend/libinput/tablet_tool.c index 60208a87a..373f87d7b 100644 --- a/backend/libinput/tablet_tool.c +++ b/backend/libinput/tablet_tool.c @@ -14,10 +14,8 @@ #include "util/array.h" #include "util/signal.h" -static const struct wlr_tablet_impl tablet_impl; - static bool tablet_is_libinput(struct wlr_tablet *tablet) { - return tablet->impl == &tablet_impl; + return tablet->impl == &libinput_tablet_impl; } struct wlr_libinput_tablet_tool { @@ -42,7 +40,6 @@ static void destroy_tool(struct wlr_libinput_tablet_tool *tool) { free(tool); } - static void destroy_tablet(struct wlr_tablet *wlr_tablet) { assert(tablet_is_libinput(wlr_tablet)); struct wlr_libinput_tablet *tablet = @@ -60,7 +57,7 @@ static void destroy_tablet(struct wlr_tablet *wlr_tablet) { free(tablet); } -static const struct wlr_tablet_impl tablet_impl = { +const struct wlr_tablet_impl libinput_tablet_impl = { .destroy = destroy_tablet, }; @@ -75,7 +72,11 @@ struct wlr_tablet *create_libinput_tablet( } struct wlr_tablet *wlr_tablet = &libinput_tablet->wlr_tablet; - wlr_tablet_init(wlr_tablet, &tablet_impl); + const char *name = libinput_device_get_name(libinput_dev); + + wlr_tablet_init(wlr_tablet, &libinput_tablet_impl, name); + wlr_tablet->base.vendor = libinput_device_get_id_vendor(libinput_dev); + wlr_tablet->base.product = libinput_device_get_id_product(libinput_dev); struct udev_device *udev = libinput_device_get_udev_device(libinput_dev); char **dst = wl_array_add(&wlr_tablet->paths, sizeof(char *)); diff --git a/backend/libinput/touch.c b/backend/libinput/touch.c index ee987a91a..2e22a97a3 100644 --- a/backend/libinput/touch.c +++ b/backend/libinput/touch.c @@ -8,6 +8,8 @@ #include "backend/libinput.h" #include "util/signal.h" +const struct wlr_touch_impl libinput_touch_impl; + struct wlr_touch *create_libinput_touch( struct libinput_device *libinput_dev) { assert(libinput_dev); @@ -16,7 +18,10 @@ struct wlr_touch *create_libinput_touch( wlr_log(WLR_ERROR, "Unable to allocate wlr_touch"); return NULL; } - wlr_touch_init(wlr_touch, NULL); + const char *name = libinput_device_get_name(libinput_dev); + wlr_touch_init(wlr_touch, &libinput_touch_impl, name); + wlr_touch->base.vendor = libinput_device_get_id_vendor(libinput_dev); + wlr_touch->base.product = libinput_device_get_id_product(libinput_dev); return wlr_touch; } diff --git a/backend/meson.build b/backend/meson.build index 7fffc8395..71a00dc9b 100644 --- a/backend/meson.build +++ b/backend/meson.build @@ -16,7 +16,6 @@ endforeach subdir('multi') subdir('wayland') -subdir('noop') subdir('headless') subdir('session') diff --git a/backend/multi/backend.c b/backend/multi/backend.c index ffe794762..b06bc4865 100644 --- a/backend/multi/backend.c +++ b/backend/multi/backend.c @@ -64,20 +64,6 @@ static void multi_backend_destroy(struct wlr_backend *wlr_backend) { free(backend); } -static struct wlr_renderer *multi_backend_get_renderer( - struct wlr_backend *backend) { - struct wlr_multi_backend *multi = multi_backend_from_backend(backend); - - struct subbackend_state *sub; - wl_list_for_each(sub, &multi->backends, link) { - struct wlr_renderer *rend = wlr_backend_get_renderer(sub->backend); - if (rend != NULL) { - return rend; - } - } - return NULL; -} - static struct wlr_session *multi_backend_get_session( struct wlr_backend *_backend) { struct wlr_multi_backend *backend = multi_backend_from_backend(_backend); @@ -136,7 +122,6 @@ static uint32_t multi_backend_get_buffer_caps(struct wlr_backend *backend) { static const struct wlr_backend_impl backend_impl = { .start = multi_backend_start, .destroy = multi_backend_destroy, - .get_renderer = multi_backend_get_renderer, .get_session = multi_backend_get_session, .get_presentation_clock = multi_backend_get_presentation_clock, .get_drm_fd = multi_backend_get_drm_fd, @@ -204,6 +189,9 @@ static struct subbackend_state *multi_backend_get_subbackend(struct wlr_multi_ba bool wlr_multi_backend_add(struct wlr_backend *_multi, struct wlr_backend *backend) { + assert(_multi && backend); + assert(_multi != backend); + struct wlr_multi_backend *multi = multi_backend_from_backend(_multi); if (multi_backend_get_subbackend(multi, backend)) { @@ -211,15 +199,6 @@ bool wlr_multi_backend_add(struct wlr_backend *_multi, return true; } - struct wlr_renderer *multi_renderer = - multi_backend_get_renderer(&multi->backend); - struct wlr_renderer *backend_renderer = wlr_backend_get_renderer(backend); - if (multi_renderer != NULL && backend_renderer != NULL && multi_renderer != backend_renderer) { - wlr_log(WLR_ERROR, "Could not add backend: multiple renderers at the " - "same time aren't supported"); - return false; - } - struct subbackend_state *sub = calloc(1, sizeof(struct subbackend_state)); if (sub == NULL) { wlr_log(WLR_ERROR, "Could not add backend: allocation failed"); diff --git a/backend/noop/backend.c b/backend/noop/backend.c deleted file mode 100644 index 7bdda051e..000000000 --- a/backend/noop/backend.c +++ /dev/null @@ -1,79 +0,0 @@ -#include -#include -#include -#include -#include "backend/noop.h" -#include "util/signal.h" - -struct wlr_noop_backend *noop_backend_from_backend( - struct wlr_backend *wlr_backend) { - assert(wlr_backend_is_noop(wlr_backend)); - return (struct wlr_noop_backend *)wlr_backend; -} - -static bool backend_start(struct wlr_backend *wlr_backend) { - struct wlr_noop_backend *backend = noop_backend_from_backend(wlr_backend); - wlr_log(WLR_INFO, "Starting noop backend"); - - struct wlr_noop_output *output; - wl_list_for_each(output, &backend->outputs, link) { - wlr_output_update_enabled(&output->wlr_output, true); - wlr_signal_emit_safe(&backend->backend.events.new_output, - &output->wlr_output); - } - - backend->started = true; - return true; -} - -static void backend_destroy(struct wlr_backend *wlr_backend) { - struct wlr_noop_backend *backend = noop_backend_from_backend(wlr_backend); - if (!wlr_backend) { - return; - } - - struct wlr_noop_output *output, *output_tmp; - wl_list_for_each_safe(output, output_tmp, &backend->outputs, link) { - wlr_output_destroy(&output->wlr_output); - } - - wlr_backend_finish(wlr_backend); - - wl_list_remove(&backend->display_destroy.link); - - free(backend); -} - -static const struct wlr_backend_impl backend_impl = { - .start = backend_start, - .destroy = backend_destroy, -}; - -static void handle_display_destroy(struct wl_listener *listener, void *data) { - struct wlr_noop_backend *noop = - wl_container_of(listener, noop, display_destroy); - backend_destroy(&noop->backend); -} - -struct wlr_backend *wlr_noop_backend_create(struct wl_display *display) { - wlr_log(WLR_INFO, "Creating noop backend"); - - struct wlr_noop_backend *backend = - calloc(1, sizeof(struct wlr_noop_backend)); - if (!backend) { - wlr_log(WLR_ERROR, "Failed to allocate wlr_noop_backend"); - return NULL; - } - wlr_backend_init(&backend->backend, &backend_impl); - backend->display = display; - wl_list_init(&backend->outputs); - - backend->display_destroy.notify = handle_display_destroy; - wl_display_add_destroy_listener(display, &backend->display_destroy); - - return &backend->backend; -} - -bool wlr_backend_is_noop(struct wlr_backend *backend) { - return backend->impl == &backend_impl; -} diff --git a/backend/noop/meson.build b/backend/noop/meson.build deleted file mode 100644 index 950c07160..000000000 --- a/backend/noop/meson.build +++ /dev/null @@ -1,4 +0,0 @@ -wlr_files += files( - 'backend.c', - 'output.c', -) diff --git a/backend/noop/output.c b/backend/noop/output.c deleted file mode 100644 index bde808208..000000000 --- a/backend/noop/output.c +++ /dev/null @@ -1,87 +0,0 @@ -#include -#include -#include -#include -#include -#include "backend/noop.h" -#include "util/signal.h" - -static const uint32_t SUPPORTED_OUTPUT_STATE = - WLR_OUTPUT_STATE_BACKEND_OPTIONAL | - WLR_OUTPUT_STATE_MODE; - -static struct wlr_noop_output *noop_output_from_output( - struct wlr_output *wlr_output) { - assert(wlr_output_is_noop(wlr_output)); - return (struct wlr_noop_output *)wlr_output; -} - -static bool output_commit(struct wlr_output *wlr_output) { - uint32_t unsupported = - wlr_output->pending.committed & ~SUPPORTED_OUTPUT_STATE; - if (unsupported != 0) { - wlr_log(WLR_DEBUG, "Unsupported output state fields: 0x%"PRIx32, - unsupported); - return false; - } - - if (wlr_output->pending.committed & WLR_OUTPUT_STATE_MODE) { - assert(wlr_output->pending.mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM); - wlr_output_update_custom_mode(wlr_output, - wlr_output->pending.custom_mode.width, - wlr_output->pending.custom_mode.height, - wlr_output->pending.custom_mode.refresh); - } - - if (wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER) { - return false; - } - - return true; -} - -static void output_destroy(struct wlr_output *wlr_output) { - struct wlr_noop_output *output = - noop_output_from_output(wlr_output); - - wl_list_remove(&output->link); - - free(output); -} - -static const struct wlr_output_impl output_impl = { - .destroy = output_destroy, - .commit = output_commit, -}; - -bool wlr_output_is_noop(struct wlr_output *wlr_output) { - return wlr_output->impl == &output_impl; -} - -struct wlr_output *wlr_noop_add_output(struct wlr_backend *wlr_backend) { - struct wlr_noop_backend *backend = noop_backend_from_backend(wlr_backend); - - struct wlr_noop_output *output = calloc(1, sizeof(struct wlr_noop_output)); - if (output == NULL) { - wlr_log(WLR_ERROR, "Failed to allocate wlr_noop_output"); - return NULL; - } - output->backend = backend; - wlr_output_init(&output->wlr_output, &backend->backend, &output_impl, - backend->display); - struct wlr_output *wlr_output = &output->wlr_output; - - strncpy(wlr_output->make, "noop", sizeof(wlr_output->make)); - strncpy(wlr_output->model, "noop", sizeof(wlr_output->model)); - snprintf(wlr_output->name, sizeof(wlr_output->name), "NOOP-%zd", - ++backend->last_output_num); - - wl_list_insert(&backend->outputs, &output->link); - - if (backend->started) { - wlr_output_update_enabled(wlr_output, true); - wlr_signal_emit_safe(&backend->backend.events.new_output, wlr_output); - } - - return wlr_output; -} diff --git a/backend/session/meson.build b/backend/session/meson.build index 66c908a2e..27915506b 100644 --- a/backend/session/meson.build +++ b/backend/session/meson.build @@ -1,6 +1,6 @@ libseat = dependency('libseat', version: '>=0.2.0', - fallback: ['seatd', 'libseat'], + fallback: 'seatd', default_options: ['server=disabled', 'man-pages=disabled'], ) wlr_files += files('session.c') diff --git a/backend/session/session.c b/backend/session/session.c index 382f3d60b..7d6d080da 100644 --- a/backend/session/session.c +++ b/backend/session/session.c @@ -157,6 +157,14 @@ static void read_udev_change_event(struct wlr_device_change_event *event, if (prop != NULL) { hotplug->prop_id = strtoul(prop, NULL, 10); } + + return; + } + + const char *lease = udev_device_get_property_value(udev_dev, "LEASE"); + if (lease != NULL && strcmp(lease, "1") == 0) { + event->type = WLR_DEVICE_LEASE; + return; } } @@ -362,7 +370,7 @@ bool wlr_session_change_vt(struct wlr_session *session, unsigned vt) { /* Tests if 'path' is KMS compatible by trying to open it. Returns the opened * device on success. */ -static struct wlr_device *open_if_kms(struct wlr_session *restrict session, +struct wlr_device *session_open_if_kms(struct wlr_session *restrict session, const char *restrict path) { if (!path) { return NULL; @@ -398,7 +406,7 @@ static ssize_t explicit_find_gpus(struct wlr_session *session, break; } - ret[i] = open_if_kms(session, ptr); + ret[i] = session_open_if_kms(session, ptr); if (!ret[i]) { wlr_log(WLR_ERROR, "Unable to open %s as DRM device", ptr); } else { @@ -534,7 +542,7 @@ ssize_t wlr_session_find_gpus(struct wlr_session *session, } struct wlr_device *wlr_dev = - open_if_kms(session, udev_device_get_devnode(dev)); + session_open_if_kms(session, udev_device_get_devnode(dev)); if (!wlr_dev) { udev_device_unref(dev); continue; diff --git a/backend/wayland/backend.c b/backend/wayland/backend.c index ea216511c..c0e99a228 100644 --- a/backend/wayland/backend.c +++ b/backend/wayland/backend.c @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include @@ -11,7 +13,6 @@ #include #include -#include #include #include @@ -30,6 +31,21 @@ #include "tablet-unstable-v2-client-protocol.h" #include "relative-pointer-unstable-v1-client-protocol.h" +struct wlr_wl_linux_dmabuf_feedback_v1 { + struct wlr_wl_backend *backend; + dev_t main_device_id; + struct wlr_wl_linux_dmabuf_v1_table_entry *format_table; + size_t format_table_size; + + dev_t tranche_target_device_id; +}; + +struct wlr_wl_linux_dmabuf_v1_table_entry { + uint32_t format; + uint32_t pad; /* unused */ + uint64_t modifier; +}; + struct wlr_wl_backend *get_wl_backend_from_backend(struct wlr_backend *backend) { assert(wlr_backend_is_wl(backend)); return (struct wlr_wl_backend *)backend; @@ -75,6 +91,16 @@ static const struct xdg_wm_base_listener xdg_wm_base_listener = { xdg_wm_base_handle_ping, }; +static void presentation_handle_clock_id(void *data, + struct wp_presentation *presentation, uint32_t clock) { + struct wlr_wl_backend *wl = data; + wl->presentation_clock = clock; +} + +static const struct wp_presentation_listener presentation_listener = { + .clock_id = presentation_handle_clock_id, +}; + static void linux_dmabuf_v1_handle_format(void *data, struct zwp_linux_dmabuf_v1 *linux_dmabuf_v1, uint32_t format) { // Note, this event is deprecated @@ -98,6 +124,119 @@ static const struct zwp_linux_dmabuf_v1_listener linux_dmabuf_v1_listener = { .modifier = linux_dmabuf_v1_handle_modifier, }; +static void linux_dmabuf_feedback_v1_handle_done(void *data, + struct zwp_linux_dmabuf_feedback_v1 *feedback) { + // This space is intentionally left blank +} + +static void linux_dmabuf_feedback_v1_handle_format_table(void *data, + struct zwp_linux_dmabuf_feedback_v1 *feedback, int fd, uint32_t size) { + struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data; + + feedback_data->format_table = NULL; + + void *table_data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + if (table_data == MAP_FAILED) { + wlr_log_errno(WLR_ERROR, "failed to mmap DMA-BUF format table"); + } else { + feedback_data->format_table = table_data; + feedback_data->format_table_size = size; + } + close(fd); +} + +static void linux_dmabuf_feedback_v1_handle_main_device(void *data, + struct zwp_linux_dmabuf_feedback_v1 *feedback, + struct wl_array *dev_id_arr) { + struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data; + + dev_t dev_id; + assert(dev_id_arr->size == sizeof(dev_id)); + memcpy(&dev_id, dev_id_arr->data, sizeof(dev_id)); + + feedback_data->main_device_id = dev_id; + + drmDevice *device = NULL; + if (drmGetDeviceFromDevId(dev_id, 0, &device) != 0) { + wlr_log_errno(WLR_ERROR, "drmGetDeviceFromDevId failed"); + return; + } + + const char *name = NULL; + if (device->available_nodes & (1 << DRM_NODE_RENDER)) { + name = device->nodes[DRM_NODE_RENDER]; + } else { + // Likely a split display/render setup. Pick the primary node and hope + // Mesa will open the right render node under-the-hood. + assert(device->available_nodes & (1 << DRM_NODE_PRIMARY)); + name = device->nodes[DRM_NODE_PRIMARY]; + wlr_log(WLR_DEBUG, "DRM device %s has no render node, " + "falling back to primary node", name); + } + + feedback_data->backend->drm_render_name = strdup(name); + + drmFreeDevice(&device); +} + +static void linux_dmabuf_feedback_v1_handle_tranche_done(void *data, + struct zwp_linux_dmabuf_feedback_v1 *feedback) { + struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data; + feedback_data->tranche_target_device_id = 0; +} + +static void linux_dmabuf_feedback_v1_handle_tranche_target_device(void *data, + struct zwp_linux_dmabuf_feedback_v1 *feedback, + struct wl_array *dev_id_arr) { + struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data; + + dev_t dev_id; + assert(dev_id_arr->size == sizeof(dev_id)); + memcpy(&dev_id, dev_id_arr->data, sizeof(dev_id)); + + feedback_data->tranche_target_device_id = dev_id; +} + +static void linux_dmabuf_feedback_v1_handle_tranche_formats(void *data, + struct zwp_linux_dmabuf_feedback_v1 *feedback, + struct wl_array *indices_arr) { + struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data; + + if (feedback_data->format_table == NULL) { + return; + } + if (feedback_data->tranche_target_device_id != feedback_data->main_device_id) { + return; + } + + size_t table_cap = feedback_data->format_table_size / + sizeof(struct wlr_wl_linux_dmabuf_v1_table_entry); + uint16_t *index_ptr; + wl_array_for_each(index_ptr, indices_arr) { + assert(*index_ptr < table_cap); + const struct wlr_wl_linux_dmabuf_v1_table_entry *entry = + &feedback_data->format_table[*index_ptr]; + wlr_drm_format_set_add(&feedback_data->backend->linux_dmabuf_v1_formats, + entry->format, entry->modifier); + } +} + +static void linux_dmabuf_feedback_v1_handle_tranche_flags(void *data, + struct zwp_linux_dmabuf_feedback_v1 *feedback, uint32_t flags) { + // TODO: handle SCANOUT flag +} + +static const struct zwp_linux_dmabuf_feedback_v1_listener + linux_dmabuf_feedback_v1_listener = { + .done = linux_dmabuf_feedback_v1_handle_done, + .format_table = linux_dmabuf_feedback_v1_handle_format_table, + .main_device = linux_dmabuf_feedback_v1_handle_main_device, + .tranche_done = linux_dmabuf_feedback_v1_handle_tranche_done, + .tranche_target_device = linux_dmabuf_feedback_v1_handle_tranche_target_device, + .tranche_formats = linux_dmabuf_feedback_v1_handle_tranche_formats, + .tranche_flags = linux_dmabuf_feedback_v1_handle_tranche_flags, +}; + 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))) { @@ -162,8 +301,6 @@ static char *get_render_name(const char *name) { static void legacy_drm_handle_device(void *data, struct wl_drm *drm, const char *name) { struct wlr_wl_backend *wl = data; - - // TODO: get FD from linux-dmabuf hints instead wl->drm_render_name = get_render_name(name); } @@ -227,13 +364,15 @@ static void registry_global(void *data, struct wl_registry *registry, } else if (strcmp(iface, wp_presentation_interface.name) == 0) { wl->presentation = wl_registry_bind(registry, name, &wp_presentation_interface, 1); + wp_presentation_add_listener(wl->presentation, + &presentation_listener, wl); } else if (strcmp(iface, zwp_tablet_manager_v2_interface.name) == 0) { wl->tablet_manager = wl_registry_bind(registry, name, &zwp_tablet_manager_v2_interface, 1); } else if (strcmp(iface, zwp_linux_dmabuf_v1_interface.name) == 0 && version >= 3) { wl->zwp_linux_dmabuf_v1 = wl_registry_bind(registry, name, - &zwp_linux_dmabuf_v1_interface, 3); + &zwp_linux_dmabuf_v1_interface, version >= 4 ? 4 : version); zwp_linux_dmabuf_v1_add_listener(wl->zwp_linux_dmabuf_v1, &linux_dmabuf_v1_listener, wl); } else if (strcmp(iface, zwp_relative_pointer_manager_v1_interface.name) == 0) { @@ -302,9 +441,9 @@ static void backend_destroy(struct wlr_backend *backend) { wlr_output_destroy(&output->wlr_output); } - struct wlr_input_device *input_device, *tmp_input_device; + struct wlr_wl_input_device *input_device, *tmp_input_device; wl_list_for_each_safe(input_device, tmp_input_device, &wl->devices, link) { - wlr_input_device_destroy(input_device); + destroy_wl_input_device(input_device); } struct wlr_wl_buffer *buffer, *tmp_buffer; @@ -352,6 +491,11 @@ static void backend_destroy(struct wlr_backend *backend) { free(wl); } +static clockid_t backend_get_presentation_clock(struct wlr_backend *backend) { + struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend); + return wl->presentation_clock; +} + static int backend_get_drm_fd(struct wlr_backend *backend) { struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend); return wl->drm_fd; @@ -366,6 +510,7 @@ static uint32_t get_buffer_caps(struct wlr_backend *backend) { static const struct wlr_backend_impl backend_impl = { .start = backend_start, .destroy = backend_destroy, + .get_presentation_clock = backend_get_presentation_clock, .get_drm_fd = backend_get_drm_fd, .get_buffer_caps = get_buffer_caps, }; @@ -397,6 +542,7 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display, wl_list_init(&wl->outputs); wl_list_init(&wl->seats); wl_list_init(&wl->buffers); + wl->presentation_clock = CLOCK_MONOTONIC; wl->remote_display = wl_display_connect(remote); if (!wl->remote_display) { @@ -409,10 +555,9 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display, wlr_log_errno(WLR_ERROR, "Could not obtain reference to remote registry"); goto error_display; } - wl_registry_add_listener(wl->registry, ®istry_listener, wl); + 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, @@ -425,6 +570,35 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display, goto error_registry; } + struct zwp_linux_dmabuf_feedback_v1 *linux_dmabuf_feedback_v1 = NULL; + struct wlr_wl_linux_dmabuf_feedback_v1 feedback_data = { .backend = wl }; + if (wl->zwp_linux_dmabuf_v1 != NULL && + zwp_linux_dmabuf_v1_get_version(wl->zwp_linux_dmabuf_v1) >= + ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION) { + linux_dmabuf_feedback_v1 = + zwp_linux_dmabuf_v1_get_default_feedback(wl->zwp_linux_dmabuf_v1); + if (linux_dmabuf_feedback_v1 == NULL) { + wlr_log(WLR_ERROR, "Allocation failed"); + goto error_registry; + } + zwp_linux_dmabuf_feedback_v1_add_listener(linux_dmabuf_feedback_v1, + &linux_dmabuf_feedback_v1_listener, &feedback_data); + + if (wl->legacy_drm != NULL) { + wl_drm_destroy(wl->legacy_drm); + wl->legacy_drm = NULL; + } + } + + wl_display_roundtrip(wl->remote_display); // get linux-dmabuf formats + + if (feedback_data.format_table != NULL) { + munmap(feedback_data.format_table, feedback_data.format_table_size); + } + if (linux_dmabuf_feedback_v1 != NULL) { + zwp_linux_dmabuf_feedback_v1_destroy(linux_dmabuf_feedback_v1); + } + struct wl_event_loop *loop = wl_display_get_event_loop(wl->local_display); int fd = wl_display_get_fd(wl->remote_display); wl->remote_display_src = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE, diff --git a/backend/wayland/meson.build b/backend/wayland/meson.build index ebe315296..1831bbb30 100644 --- a/backend/wayland/meson.build +++ b/backend/wayland/meson.build @@ -1,3 +1,9 @@ +wayland_client = dependency('wayland-client', + fallback: 'wayland', + default_options: wayland_project_options, +) +wlr_deps += wayland_client + wlr_files += files( 'backend.c', 'output.c', diff --git a/backend/wayland/output.c b/backend/wayland/output.c index 8d8f036c8..de5d42edd 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -166,18 +166,8 @@ static struct wl_buffer *import_dmabuf(struct wlr_wl_backend *wl, dmabuf->offset[i], dmabuf->stride[i], modifier_hi, modifier_lo); } - uint32_t flags = 0; - if (dmabuf->flags & WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT) { - flags |= ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT; - } - if (dmabuf->flags & WLR_DMABUF_ATTRIBUTES_FLAGS_INTERLACED) { - flags |= ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_INTERLACED; - } - if (dmabuf->flags & WLR_DMABUF_ATTRIBUTES_FLAGS_BOTTOM_FIRST) { - flags |= ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_BOTTOM_FIRST; - } struct wl_buffer *wl_buffer = zwp_linux_buffer_params_v1_create_immed( - params, dmabuf->width, dmabuf->height, dmabuf->format, flags); + params, dmabuf->width, dmabuf->height, dmabuf->format, 0); // TODO: handle create() errors return wl_buffer; } @@ -530,12 +520,14 @@ struct wlr_output *wlr_wl_output_create(struct wlr_backend *wlr_backend) { wlr_output_update_custom_mode(wlr_output, 1280, 720, 0); strncpy(wlr_output->make, "wayland", sizeof(wlr_output->make)); strncpy(wlr_output->model, "wayland", sizeof(wlr_output->model)); - snprintf(wlr_output->name, sizeof(wlr_output->name), "WL-%zd", - ++backend->last_output_num); + + char name[64]; + snprintf(name, sizeof(name), "WL-%zu", ++backend->last_output_num); + wlr_output_set_name(wlr_output, name); char description[128]; snprintf(description, sizeof(description), - "Wayland output %zd", backend->last_output_num); + "Wayland output %zu", backend->last_output_num); wlr_output_set_description(wlr_output, description); output->backend = backend; diff --git a/backend/wayland/seat.c b/backend/wayland/seat.c index 508551a0f..28466bb33 100644 --- a/backend/wayland/seat.c +++ b/backend/wayland/seat.c @@ -9,11 +9,12 @@ #include -#include #include #include #include #include +#include +#include #include #include "pointer-gestures-unstable-v1-client-protocol.h" @@ -22,15 +23,20 @@ #include "util/signal.h" #include "util/time.h" +static const struct wlr_pointer_impl pointer_impl; +static const struct wlr_keyboard_impl keyboard_impl; +static const struct wlr_touch_impl touch_impl; + 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) { + struct wlr_wl_input_device *dev; + wl_list_for_each(dev, &output->backend->devices, link) { + if (dev->wlr_input_device.type != WLR_INPUT_DEVICE_POINTER) { continue; } - struct wlr_wl_pointer *pointer = pointer_get_wl(wlr_dev->pointer); + struct wlr_wl_pointer *pointer = + pointer_get_wl(dev->wlr_input_device.pointer); if (pointer->output == output && pointer->wl_pointer == wl_pointer) { return pointer; } @@ -430,26 +436,21 @@ static struct wlr_wl_seat *input_device_get_seat(struct wlr_input_device *wlr_de 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) { - 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); -} - -static const struct wlr_input_device_impl input_device_impl = { - .destroy = input_device_destroy, -}; - bool wlr_input_device_is_wl(struct wlr_input_device *dev) { - return dev->impl == &input_device_impl; + switch (dev->type) { + case WLR_INPUT_DEVICE_KEYBOARD: + return dev->keyboard->impl == &keyboard_impl; + case WLR_INPUT_DEVICE_POINTER: + return dev->pointer->impl == &pointer_impl; + case WLR_INPUT_DEVICE_TOUCH: + return dev->touch->impl == &touch_impl; + case WLR_INPUT_DEVICE_TABLET_TOOL: + return dev->tablet->impl == &tablet_impl; + case WLR_INPUT_DEVICE_TABLET_PAD: + return dev->tablet_pad->impl == &tablet_pad_impl; + default: + return false; + } } struct wlr_wl_input_device *create_wl_input_device( @@ -465,19 +466,51 @@ struct wlr_wl_input_device *create_wl_input_device( struct wlr_input_device *wlr_dev = &dev->wlr_input_device; - unsigned int vendor = 0, product = 0; + const char *type_name = "unknown"; - size_t name_size = 8 + strlen(seat->name) + 1; + switch (type) { + case WLR_INPUT_DEVICE_KEYBOARD: + type_name = "keyboard"; + break; + case WLR_INPUT_DEVICE_POINTER: + type_name = "pointer"; + break; + case WLR_INPUT_DEVICE_TOUCH: + type_name = "touch"; + break; + case WLR_INPUT_DEVICE_TABLET_TOOL: + type_name = "tablet-tool"; + break; + case WLR_INPUT_DEVICE_TABLET_PAD: + type_name = "tablet-pad"; + break; + case WLR_INPUT_DEVICE_SWITCH: + type_name = "switch"; + break; + } + + size_t name_size = 8 + strlen(type_name) + strlen(seat->name) + 1; char name[name_size]; - (void) snprintf(name, name_size, "wayland-%s", seat->name); + (void) snprintf(name, name_size, "wayland-%s-%s", type_name, seat->name); - wlr_input_device_init(wlr_dev, type, &input_device_impl, name, vendor, - product); - wl_list_insert(&seat->backend->devices, &wlr_dev->link); + wlr_input_device_init(wlr_dev, type, name); + wl_list_insert(&seat->backend->devices, &dev->link); return dev; } -static const struct wlr_pointer_impl pointer_impl; +void destroy_wl_input_device(struct wlr_wl_input_device *dev) { + /** + * TODO remove the redundant wlr_input_device from wlr_wl_input_device + * wlr_wl_input_device::wlr_input_device is not owned by its input device + * type, which means we have 2 wlr_input_device to cleanup + */ + wlr_input_device_finish(&dev->wlr_input_device); + if (dev->wlr_input_device._device) { + wlr_input_device_destroy(&dev->wlr_input_device); + } + wl_list_remove(&dev->link); + free(dev); +} struct wlr_wl_pointer *pointer_get_wl(struct wlr_pointer *wlr_pointer) { assert(wlr_pointer->impl == &pointer_impl); @@ -684,7 +717,7 @@ static void pointer_handle_output_destroy(struct wl_listener *listener, void *data) { struct wlr_wl_pointer *pointer = wl_container_of(listener, pointer, output_destroy); - wlr_input_device_destroy(&pointer->input_device->wlr_input_device); + destroy_wl_input_device(pointer->input_device); } void create_wl_pointer(struct wlr_wl_seat *seat, struct wlr_wl_output *output) { @@ -716,13 +749,14 @@ void create_wl_pointer(struct wlr_wl_seat *seat, struct wlr_wl_output *output) { } pointer->input_device = dev; - 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); + + wlr_pointer_init(&pointer->wlr_pointer, &pointer_impl, wlr_dev->name); + + wl_signal_add(&output->wlr_output.events.destroy, &pointer->output_destroy); + pointer->output_destroy.notify = pointer_handle_output_destroy; if (backend->zwp_pointer_gestures_v1) { uint32_t version = zwp_pointer_gestures_v1_get_version( @@ -764,14 +798,14 @@ void create_wl_keyboard(struct wlr_wl_seat *seat) { } struct wlr_input_device *wlr_dev = &dev->wlr_input_device; - wlr_dev->keyboard = calloc(1, sizeof(*wlr_dev->keyboard)); if (!wlr_dev->keyboard) { wlr_log_errno(WLR_ERROR, "Allocation failed"); - wlr_input_device_destroy(wlr_dev); + destroy_wl_input_device(dev); return; } - wlr_keyboard_init(wlr_dev->keyboard, NULL); + + wlr_keyboard_init(wlr_dev->keyboard, &keyboard_impl, wlr_dev->name); wl_keyboard_add_listener(wl_keyboard, &keyboard_listener, wlr_dev); wlr_signal_emit_safe(&seat->backend->backend.events.new_input, wlr_dev); @@ -791,16 +825,15 @@ void create_wl_touch(struct wlr_wl_seat *seat) { 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); + destroy_wl_input_device(dev); return; } - wlr_touch_init(wlr_dev->touch, NULL); + wlr_touch_init(wlr_dev->touch, &touch_impl, wlr_dev->name); 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_seat *seat = data; @@ -822,19 +855,20 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, struct wl_pointer *wl_pointer = seat->pointer; - struct wlr_input_device *device, *tmp; + struct wlr_wl_input_device *device, *tmp; wl_list_for_each_safe(device, tmp, &backend->devices, link) { - if (device->type != WLR_INPUT_DEVICE_POINTER) { + if (device->wlr_input_device.type != WLR_INPUT_DEVICE_POINTER) { continue; } - struct wlr_wl_pointer *pointer = pointer_get_wl(device->pointer); + struct wlr_wl_pointer *pointer = + pointer_get_wl(device->wlr_input_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); + destroy_wl_input_device(device); assert(seat->active_pointer != pointer); assert(output->cursor.pointer != pointer); } @@ -856,18 +890,16 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, 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; + struct wlr_wl_input_device *device, *tmp; wl_list_for_each_safe(device, tmp, &backend->devices, link) { - if (device->type != WLR_INPUT_DEVICE_KEYBOARD) { + if (device->wlr_input_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) { + if (device->seat != seat) { continue; } - wlr_input_device_destroy(device); + destroy_wl_input_device(device); } assert(seat->keyboard == NULL); // free'ed by input_device_destroy } @@ -883,10 +915,10 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_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; + struct wlr_wl_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); + if (device->wlr_input_device.type == WLR_INPUT_DEVICE_TOUCH) { + destroy_wl_input_device(device); } } diff --git a/backend/wayland/tablet_v2.c b/backend/wayland/tablet_v2.c index 9f90a3696..95cdd119d 100644 --- a/backend/wayland/tablet_v2.c +++ b/backend/wayland/tablet_v2.c @@ -10,7 +10,6 @@ #include #include #include -#include #include "util/signal.h" #include "util/time.h" @@ -402,7 +401,7 @@ static void handle_tablet_pad_removed(void *data, /* This doesn't free anything, but emits the destroy signal */ wlr_input_device_destroy(&dev->wlr_input_device); /* This is a bit ugly, but we need to remove it from our list */ - wl_list_remove(&dev->wlr_input_device.link); + wl_list_remove(&dev->link); struct wlr_wl_tablet_pad_group *group, *it; wl_list_for_each_safe(group, it, &tablet_pad->groups, group.link) { @@ -426,6 +425,8 @@ static const struct zwp_tablet_pad_v2_listener tablet_pad_listener = { .removed = handle_tablet_pad_removed, }; +const struct wlr_tablet_pad_impl tablet_pad_impl = {0}; + static void handle_pad_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_pad_v2 *id) { @@ -453,7 +454,7 @@ static void handle_pad_added(void *data, zwp_tablet_pad_v2_destroy(id); return; } - wlr_tablet_pad_init(wlr_dev->tablet_pad, NULL); + wlr_tablet_pad_init(wlr_dev->tablet_pad, &tablet_pad_impl, wlr_dev->name); zwp_tablet_pad_v2_add_listener(id, &tablet_pad_listener, dev); } @@ -873,7 +874,7 @@ static void handle_tablet_removed(void *data, /* This doesn't free anything, but emits the destroy signal */ wlr_input_device_destroy(&dev->wlr_input_device); /* This is a bit ugly, but we need to remove it from our list */ - wl_list_remove(&dev->wlr_input_device.link); + wl_list_remove(&dev->link); zwp_tablet_v2_destroy(dev->resource); free(dev); @@ -887,6 +888,8 @@ static const struct zwp_tablet_v2_listener tablet_listener = { .removed = handle_tablet_removed, }; +const struct wlr_tablet_impl tablet_impl = {0}; + static void handle_tab_added(void *data, struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2, struct zwp_tablet_v2 *id) { @@ -909,7 +912,7 @@ static void handle_tab_added(void *data, return; } zwp_tablet_v2_set_user_data(id, wlr_dev->tablet); - wlr_tablet_init(wlr_dev->tablet, NULL); + wlr_tablet_init(wlr_dev->tablet, &tablet_impl, wlr_dev->name); zwp_tablet_v2_add_listener(id, &tablet_listener, dev); } diff --git a/backend/x11/backend.c b/backend/x11/backend.c index 9eaf56644..64bcf475b 100644 --- a/backend/x11/backend.c +++ b/backend/x11/backend.c @@ -24,7 +24,6 @@ #include #include -#include #include #include #include @@ -165,7 +164,7 @@ static bool backend_start(struct wlr_backend *backend) { wlr_log(WLR_INFO, "Starting X11 backend"); - wlr_signal_emit_safe(&x11->backend.events.new_input, &x11->keyboard_dev); + wlr_signal_emit_safe(&x11->backend.events.new_input, &x11->keyboard.base); for (size_t i = 0; i < x11->requested_outputs; ++i) { wlr_x11_output_create(&x11->backend); @@ -186,7 +185,7 @@ static void backend_destroy(struct wlr_backend *backend) { wlr_output_destroy(&output->wlr_output); } - wlr_input_device_destroy(&x11->keyboard_dev); + wlr_keyboard_destroy(&x11->keyboard); wlr_backend_finish(backend); @@ -354,6 +353,7 @@ static bool query_formats(struct wlr_x11_backend *x11) { } if (x11->have_dri3) { + // X11 always supports implicit modifiers wlr_drm_format_set_add(&x11->dri3_formats, format->drm, DRM_FORMAT_MOD_INVALID); if (!query_dri3_modifiers(x11, format)) { @@ -572,9 +572,9 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, goto error_event; } - x11->depth = get_depth(x11->screen, 32); + x11->depth = get_depth(x11->screen, 24); if (!x11->depth) { - wlr_log(WLR_ERROR, "Failed to get 32-bit depth for X11 screen"); + wlr_log(WLR_ERROR, "Failed to get 24-bit depth for X11 screen"); goto error_event; } @@ -637,10 +637,7 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, } #endif - 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); - x11->keyboard_dev.keyboard = &x11->keyboard; + wlr_keyboard_init(&x11->keyboard, &x11_keyboard_impl, "x11-keyboard"); x11->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &x11->display_destroy); diff --git a/backend/x11/input_device.c b/backend/x11/input_device.c index f9541b062..f87f53a0d 100644 --- a/backend/x11/input_device.c +++ b/backend/x11/input_device.c @@ -10,7 +10,6 @@ #include #include -#include #include #include #include @@ -33,7 +32,7 @@ static void send_key_event(struct wlr_x11_backend *x11, uint32_t key, static void send_button_event(struct wlr_x11_output *output, uint32_t key, enum wlr_button_state st, xcb_timestamp_t time) { struct wlr_event_pointer_button ev = { - .device = &output->pointer_dev, + .device = &output->pointer.base, .time_msec = time, .button = key, .state = st, @@ -45,7 +44,7 @@ static void send_button_event(struct wlr_x11_output *output, uint32_t key, static void send_axis_event(struct wlr_x11_output *output, int32_t delta, xcb_timestamp_t time) { struct wlr_event_pointer_axis ev = { - .device = &output->pointer_dev, + .device = &output->pointer.base, .time_msec = time, .source = WLR_AXIS_SOURCE_WHEEL, .orientation = WLR_AXIS_ORIENTATION_VERTICAL, @@ -60,7 +59,7 @@ static void send_axis_event(struct wlr_x11_output *output, int32_t delta, static void send_pointer_position_event(struct wlr_x11_output *output, int16_t x, int16_t y, xcb_timestamp_t time) { struct wlr_event_pointer_motion_absolute ev = { - .device = &output->pointer_dev, + .device = &output->pointer.base, .time_msec = time, .x = (double)x / output->wlr_output.width, .y = (double)y / output->wlr_output.height, @@ -72,7 +71,7 @@ static void send_pointer_position_event(struct wlr_x11_output *output, static void send_touch_down_event(struct wlr_x11_output *output, int16_t x, int16_t y, int32_t touch_id, xcb_timestamp_t time) { struct wlr_event_touch_down ev = { - .device = &output->touch_dev, + .device = &output->touch.base, .time_msec = time, .x = (double)x / output->wlr_output.width, .y = (double)y / output->wlr_output.height, @@ -85,7 +84,7 @@ static void send_touch_down_event(struct wlr_x11_output *output, static void send_touch_motion_event(struct wlr_x11_output *output, int16_t x, int16_t y, int32_t touch_id, xcb_timestamp_t time) { struct wlr_event_touch_motion ev = { - .device = &output->touch_dev, + .device = &output->touch.base, .time_msec = time, .x = (double)x / output->wlr_output.width, .y = (double)y / output->wlr_output.height, @@ -98,7 +97,7 @@ static void send_touch_motion_event(struct wlr_x11_output *output, static void send_touch_up_event(struct wlr_x11_output *output, int32_t touch_id, xcb_timestamp_t time) { struct wlr_event_touch_up ev = { - .device = &output->touch_dev, + .device = &output->touch.base, .time_msec = time, .touch_id = touch_id, }; @@ -286,19 +285,11 @@ void handle_x11_xinput_event(struct wlr_x11_backend *x11, } } -static void input_device_destroy(struct wlr_input_device *wlr_device) { - // Don't free the input device, it's on the stack -} - -const struct wlr_input_device_impl input_device_impl = { - .destroy = input_device_destroy, -}; - static void keyboard_destroy(struct wlr_keyboard *wlr_keyboard) { // Don't free the keyboard, it's on the stack } -const struct wlr_keyboard_impl keyboard_impl = { +const struct wlr_keyboard_impl x11_keyboard_impl = { .destroy = keyboard_destroy, }; @@ -306,7 +297,7 @@ static void pointer_destroy(struct wlr_pointer *wlr_pointer) { // Don't free the pointer, it's on the stack } -const struct wlr_pointer_impl pointer_impl = { +const struct wlr_pointer_impl x11_pointer_impl = { .destroy = pointer_destroy, }; @@ -314,7 +305,7 @@ static void touch_destroy(struct wlr_touch *wlr_touch) { // Don't free the touch, it's on the stack } -const struct wlr_touch_impl touch_impl = { +const struct wlr_touch_impl x11_touch_impl = { .destroy = touch_destroy, }; @@ -336,5 +327,14 @@ void update_x11_pointer_position(struct wlr_x11_output *output, } bool wlr_input_device_is_x11(struct wlr_input_device *wlr_dev) { - return wlr_dev->impl == &input_device_impl; + switch (wlr_dev->type) { + case WLR_INPUT_DEVICE_KEYBOARD: + return wlr_dev->keyboard->impl == &x11_keyboard_impl; + case WLR_INPUT_DEVICE_POINTER: + return wlr_dev->pointer->impl == &x11_pointer_impl; + case WLR_INPUT_DEVICE_TOUCH: + return wlr_dev->touch->impl == &x11_touch_impl; + default: + return false; + } } diff --git a/backend/x11/output.c b/backend/x11/output.c index 3ad7b4a40..7037c36cc 100644 --- a/backend/x11/output.c +++ b/backend/x11/output.c @@ -76,8 +76,8 @@ static void output_destroy(struct wlr_output *wlr_output) { pixman_region32_fini(&output->exposed); - wlr_input_device_destroy(&output->pointer_dev); - wlr_input_device_destroy(&output->touch_dev); + wlr_pointer_destroy(&output->pointer); + wlr_touch_destroy(&output->touch); struct wlr_x11_buffer *buffer, *buffer_tmp; wl_list_for_each_safe(buffer, buffer_tmp, &output->buffers, link) { @@ -140,10 +140,6 @@ static xcb_pixmap_t import_dmabuf(struct wlr_x11_output *output, return XCB_PIXMAP_NONE; } - if (dmabuf->flags != 0) { - return XCB_PIXMAP_NONE; - } - // 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, dmabuf)) { @@ -380,7 +376,7 @@ static void update_x11_output_cursor(struct wlr_x11_output *output, static bool output_cursor_to_picture(struct wlr_x11_output *output, struct wlr_buffer *buffer) { struct wlr_x11_backend *x11 = output->x11; - struct wlr_renderer *renderer = wlr_backend_get_renderer(&x11->backend); + struct wlr_renderer *renderer = output->wlr_output.renderer; if (output->cursor.pic != XCB_NONE) { xcb_render_free_picture(x11->xcb, output->cursor.pic); @@ -516,13 +512,15 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) { wlr_output_update_custom_mode(wlr_output, 1024, 768, 0); - snprintf(wlr_output->name, sizeof(wlr_output->name), "X11-%zd", - ++x11->last_output_num); + char name[64]; + snprintf(name, sizeof(name), "X11-%zu", ++x11->last_output_num); + wlr_output_set_name(wlr_output, name); + parse_xcb_setup(wlr_output, x11->xcb); char description[128]; snprintf(description, sizeof(description), - "X11 output %zd", x11->last_output_num); + "X11 output %zu", x11->last_output_num); wlr_output_set_description(wlr_output, description); // The X11 protocol requires us to set a colormap and border pixel if the @@ -575,22 +573,16 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) { wlr_output_update_enabled(wlr_output, true); - wlr_input_device_init(&output->pointer_dev, WLR_INPUT_DEVICE_POINTER, - &input_device_impl, "X11 pointer", 0, 0); - wlr_pointer_init(&output->pointer, &pointer_impl); - output->pointer_dev.pointer = &output->pointer; - output->pointer_dev.output_name = strdup(wlr_output->name); + wlr_pointer_init(&output->pointer, &x11_pointer_impl, "x11-pointer"); + output->pointer.base.output_name = strdup(wlr_output->name); - wlr_input_device_init(&output->touch_dev, WLR_INPUT_DEVICE_TOUCH, - &input_device_impl, "X11 touch", 0, 0); - wlr_touch_init(&output->touch, &touch_impl); - output->touch_dev.touch = &output->touch; - output->touch_dev.output_name = strdup(wlr_output->name); + wlr_touch_init(&output->touch, &x11_touch_impl, "x11-touch"); + output->touch.base.output_name = strdup(wlr_output->name); wl_list_init(&output->touchpoints); wlr_signal_emit_safe(&x11->backend.events.new_output, wlr_output); - wlr_signal_emit_safe(&x11->backend.events.new_input, &output->pointer_dev); - wlr_signal_emit_safe(&x11->backend.events.new_input, &output->touch_dev); + wlr_signal_emit_safe(&x11->backend.events.new_input, &output->pointer.base); + wlr_signal_emit_safe(&x11->backend.events.new_input, &output->touch.base); // Start the rendering loop by requesting the compositor to render a frame wlr_output_schedule_frame(wlr_output); diff --git a/docs/env_vars.md b/docs/env_vars.md index 1d39a6249..609ca97bd 100644 --- a/docs/env_vars.md +++ b/docs/env_vars.md @@ -3,14 +3,15 @@ wlroots reads these environment variables # wlroots specific * *WLR_BACKENDS*: comma-separated list of backends to use (available backends: - libinput, drm, wayland, x11, headless, noop) + libinput, drm, wayland, x11, headless) * *WLR_NO_HARDWARE_CURSORS*: set to 1 to use software cursors instead of hardware cursors -* *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") * *WLR_RENDERER*: forces the creation of a specified renderer (available - renderers: gles2, pixman) + renderers: gles2, pixman, vulkan) +* *WLR_RENDER_DRM_DEVICE*: specifies the DRM node to use for + hardware-accelerated renderers. ## DRM backend diff --git a/examples/dmabuf-capture.c b/examples/dmabuf-capture.c index 4d05b78ba..58bf64195 100644 --- a/examples/dmabuf-capture.c +++ b/examples/dmabuf-capture.c @@ -1,4 +1,5 @@ #define _POSIX_C_SOURCE 199309L +#include #include #include #include @@ -619,12 +620,12 @@ static int init_encoding(struct capture_context *ctx) { } /* Find encoder */ - AVCodec *out_codec = avcodec_find_encoder_by_name(ctx->encoder_name); + const AVCodec *out_codec = avcodec_find_encoder_by_name(ctx->encoder_name); if (!out_codec) { av_log(ctx, AV_LOG_ERROR, "Codec not found (not compiled in lavc?)!\n"); return AVERROR(EINVAL); } - ctx->avf->oformat->video_codec = out_codec->id; + ctx->avf->oformat = av_guess_format(ctx->encoder_name, NULL, NULL); ctx->is_software_encoder = !(out_codec->capabilities & AV_CODEC_CAP_HARDWARE); ctx->avctx = avcodec_alloc_context3(out_codec); diff --git a/examples/fullscreen-shell.c b/examples/fullscreen-shell.c index 07320045c..bc4bb7a86 100644 --- a/examples/fullscreen-shell.c +++ b/examples/fullscreen-shell.c @@ -7,13 +7,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include #include @@ -25,6 +25,7 @@ struct fullscreen_server { struct wl_display *wl_display; struct wlr_backend *backend; struct wlr_renderer *renderer; + struct wlr_allocator *allocator; struct wlr_fullscreen_shell_v1 *fullscreen_shell; struct wl_listener present_surface; @@ -146,6 +147,8 @@ static void server_handle_new_output(struct wl_listener *listener, void *data) { wl_container_of(listener, server, new_output); struct wlr_output *wlr_output = data; + wlr_output_init_render(wlr_output, server->allocator, server->renderer); + struct fullscreen_output *output = calloc(1, sizeof(struct fullscreen_output)); output->wlr_output = wlr_output; @@ -203,8 +206,10 @@ int main(int argc, char *argv[]) { struct fullscreen_server server = {0}; server.wl_display = wl_display_create(); server.backend = wlr_backend_autocreate(server.wl_display); - server.renderer = wlr_backend_get_renderer(server.backend); + server.renderer = wlr_renderer_autocreate(server.backend); wlr_renderer_init_wl_display(server.renderer, server.wl_display); + server.allocator = wlr_allocator_autocreate(server.backend, + server.renderer); wlr_compositor_create(server.wl_display, server.renderer); diff --git a/examples/layer-shell.c b/examples/layer-shell.c index 274858d4f..56a8d4aa5 100644 --- a/examples/layer-shell.c +++ b/examples/layer-shell.c @@ -11,7 +11,6 @@ #include #include #include -#include #include "egl_common.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" #include "xdg-shell-client-protocol.h" @@ -177,8 +176,7 @@ static const struct xdg_surface_listener xdg_surface_listener = { static void xdg_popup_configure(void *data, struct xdg_popup *xdg_popup, int32_t x, int32_t y, int32_t width, int32_t height) { - wlr_log(WLR_DEBUG, "Popup configured %dx%d@%d,%d", - width, height, x, y); + fprintf(stderr, "Popup configured %dx%d@%d,%d\n", width, height, x, y); popup_width = width; popup_height = height; if (popup_egl_window) { @@ -197,7 +195,7 @@ static void popup_destroy(void) { } static void xdg_popup_done(void *data, struct xdg_popup *xdg_popup) { - wlr_log(WLR_DEBUG, "Popup done"); + fprintf(stderr, "Popup done\n"); popup_destroy(); } @@ -377,17 +375,17 @@ static void wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, static void wl_keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { - wlr_log(WLR_DEBUG, "Keyboard enter"); + fprintf(stderr, "Keyboard enter\n"); } static void wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface) { - wlr_log(WLR_DEBUG, "Keyboard leave"); + fprintf(stderr, "Keyboard leave\n"); } static void wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { - wlr_log(WLR_DEBUG, "Key event: %d %d", key, state); + fprintf(stderr, "Key event: %d %d\n", key, state); } static void wl_keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, @@ -473,7 +471,6 @@ static const struct wl_registry_listener registry_listener = { }; int main(int argc, char **argv) { - wlr_log_init(WLR_DEBUG, NULL); char *namespace = "wlroots"; int exclusive_zone = 0; int32_t margin_right = 0, margin_bottom = 0, margin_left = 0; diff --git a/examples/meson.build b/examples/meson.build index 1e428e81b..26d103bbb 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -1,6 +1,7 @@ threads = dependency('threads') wayland_egl = dependency('wayland-egl') wayland_cursor = dependency('wayland-cursor') +wayland_client = dependency('wayland-client') libpng = dependency('libpng', required: false, disabler: true) egl = dependency('egl', required: false, disabler: true) glesv2 = dependency('glesv2', required: false, disabler: true) @@ -81,7 +82,7 @@ clients = { }, 'layer-shell': { 'src': ['layer-shell.c', 'egl_common.c'], - 'dep': [wayland_egl, wayland_cursor, wlroots, egl, glesv2], + 'dep': [wayland_egl, wayland_cursor, egl, glesv2], 'proto': [ 'wlr-layer-shell-unstable-v1', 'xdg-shell', @@ -102,7 +103,7 @@ clients = { }, 'output-power-management': { 'src': 'output-power-management.c', - 'dep': [wayland_client, wlroots], + 'dep': [wayland_client], 'proto': ['wlr-output-power-management-unstable-v1'], }, 'pointer-constraints': { @@ -128,7 +129,7 @@ clients = { libavcodec, libavformat, libavutil, - drm.partial_dependency(compile_args: true), # + drm, threads, ], 'proto': ['wlr-export-dmabuf-unstable-v1'], @@ -173,12 +174,10 @@ clients = { }, 'foreign-toplevel': { 'src': 'foreign-toplevel.c', - 'dep': [wlroots], 'proto': ['wlr-foreign-toplevel-management-unstable-v1'], }, 'virtual-pointer': { 'src': 'virtual-pointer.c', - 'dep': wlroots, 'proto': ['wlr-virtual-pointer-unstable-v1'], }, 'input-method-keyboard-grab': { @@ -215,7 +214,7 @@ foreach name, info : clients executable( name, [info.get('src'), extra_src], - dependencies: [wayland_client, info.get('dep')], + dependencies: [wayland_client, info.get('dep', [])], build_by_default: get_option('examples'), ) endforeach diff --git a/examples/multi-pointer.c b/examples/multi-pointer.c index 495562802..5095cb729 100644 --- a/examples/multi-pointer.c +++ b/examples/multi-pointer.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -22,6 +23,8 @@ struct sample_state { struct wl_display *display; struct wlr_xcursor *xcursor; + struct wlr_renderer *renderer; + struct wlr_allocator *allocator; float default_color[4]; float clear_color[4]; struct wlr_output_layout *layout; @@ -90,7 +93,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) { struct sample_output *output = wl_container_of(listener, output, frame); struct sample_state *sample = output->sample; struct wlr_output *wlr_output = output->output; - struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend); + struct wlr_renderer *renderer = sample->renderer; wlr_output_attach_render(wlr_output, NULL); @@ -144,6 +147,9 @@ static void output_remove_notify(struct wl_listener *listener, void *data) { static void new_output_notify(struct wl_listener *listener, void *data) { struct wlr_output *output = data; struct sample_state *sample = wl_container_of(listener, sample, new_output); + + wlr_output_init_render(output, sample->allocator, sample->renderer); + struct sample_output *sample_output = calloc(1, sizeof(struct sample_output)); sample_output->output = output; sample_output->sample = sample; @@ -269,6 +275,10 @@ int main(int argc, char *argv[]) { if (!wlr) { exit(1); } + + state.renderer = wlr_renderer_autocreate(wlr); + state.allocator = wlr_allocator_autocreate(wlr, state.renderer); + wl_list_init(&state.cursors); wl_list_init(&state.pointers); wl_list_init(&state.outputs); diff --git a/examples/output-layout.c b/examples/output-layout.c index 356d5d56d..bc5cb7e90 100644 --- a/examples/output-layout.c +++ b/examples/output-layout.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,7 @@ struct sample_state { struct wl_listener new_output; struct wl_listener new_input; struct wlr_renderer *renderer; + struct wlr_allocator *allocator; struct wlr_texture *cat_texture; struct wlr_output_layout *layout; float x_offs, y_offs; @@ -158,6 +160,9 @@ static void output_remove_notify(struct wl_listener *listener, void *data) { static void new_output_notify(struct wl_listener *listener, void *data) { struct wlr_output *output = data; struct sample_state *sample = wl_container_of(listener, sample, new_output); + + wlr_output_init_render(output, sample->allocator, sample->renderer); + struct sample_output *sample_output = calloc(1, sizeof(struct sample_output)); wlr_output_layout_add_auto(sample->layout, output); sample_output->output = output; @@ -273,11 +278,13 @@ int main(int argc, char *argv[]) { wl_signal_add(&wlr->events.new_input, &state.new_input); state.new_input.notify = new_input_notify; - state.renderer = wlr_backend_get_renderer(wlr); + state.renderer = wlr_renderer_autocreate(wlr); state.cat_texture = wlr_texture_from_pixels(state.renderer, DRM_FORMAT_ABGR8888, cat_tex.width * 4, cat_tex.width, cat_tex.height, cat_tex.pixel_data); + state.allocator = wlr_allocator_autocreate(wlr, state.renderer); + if (!wlr_backend_start(wlr)) { wlr_log(WLR_ERROR, "Failed to start backend"); wlr_backend_destroy(wlr); diff --git a/examples/pointer.c b/examples/pointer.c index 1abdf1cfe..bf3701a45 100644 --- a/examples/pointer.c +++ b/examples/pointer.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -24,6 +25,8 @@ struct sample_state { struct wl_display *display; struct compositor_state *compositor; + struct wlr_renderer *renderer; + struct wlr_allocator *allocator; struct wlr_xcursor_manager *xcursor_manager; struct wlr_cursor *cursor; double cur_x, cur_y; @@ -95,7 +98,7 @@ 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 *state = sample_output->state; struct wlr_output *wlr_output = sample_output->output; - struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend); + struct wlr_renderer *renderer = state->renderer; assert(renderer); wlr_output_attach_render(wlr_output, NULL); @@ -250,6 +253,9 @@ static void output_remove_notify(struct wl_listener *listener, void *data) { static void new_output_notify(struct wl_listener *listener, void *data) { struct wlr_output *output = data; struct sample_state *sample = wl_container_of(listener, sample, new_output); + + wlr_output_init_render(output, sample->allocator, sample->renderer); + struct sample_output *sample_output = calloc(1, sizeof(struct sample_output)); sample_output->output = output; sample_output->state = sample; @@ -331,6 +337,10 @@ int main(int argc, char *argv[]) { if (!wlr) { exit(1); } + + state.renderer = wlr_renderer_autocreate(wlr); + state.allocator = wlr_allocator_autocreate(wlr, state.renderer); + state.cursor = wlr_cursor_create(); state.layout = wlr_output_layout_create(); wlr_cursor_attach_output_layout(state.cursor, state.layout); diff --git a/examples/quads.c b/examples/quads.c index 6696ef7ce..d74ef25e6 100644 --- a/examples/quads.c +++ b/examples/quads.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -24,6 +25,7 @@ struct sample_state { struct wl_listener new_input; struct timespec last_frame; struct wlr_renderer *renderer; + struct wlr_allocator *allocator; struct wl_list outputs; }; @@ -103,6 +105,9 @@ static void output_remove_notify(struct wl_listener *listener, void *data) { static void new_output_notify(struct wl_listener *listener, void *data) { struct wlr_output *output = data; struct sample_state *sample = wl_container_of(listener, sample, new_output); + + wlr_output_init_render(output, sample->allocator, sample->renderer); + struct sample_output *sample_output = calloc(1, sizeof(struct sample_output)); struct wlr_output_mode *mode = wlr_output_preferred_mode(output); @@ -195,13 +200,15 @@ int main(int argc, char *argv[]) { state.new_input.notify = new_input_notify; clock_gettime(CLOCK_MONOTONIC, &state.last_frame); - state.renderer = wlr_backend_get_renderer(wlr); + state.renderer = wlr_renderer_autocreate(wlr); if (!state.renderer) { wlr_log(WLR_ERROR, "Could not start compositor, OOM"); wlr_backend_destroy(wlr); exit(EXIT_FAILURE); } + state.allocator = wlr_allocator_autocreate(wlr, state.renderer); + if (!wlr_backend_start(wlr)) { wlr_log(WLR_ERROR, "Failed to start backend"); wlr_backend_destroy(wlr); diff --git a/examples/rotation.c b/examples/rotation.c index ff34630ea..cc1cfbb5b 100644 --- a/examples/rotation.c +++ b/examples/rotation.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,7 @@ struct sample_state { struct wl_listener new_input; struct timespec last_frame; struct wlr_renderer *renderer; + struct wlr_allocator *allocator; struct wlr_texture *cat_texture; struct wl_list outputs; enum wl_output_transform transform; @@ -105,6 +107,9 @@ static void output_remove_notify(struct wl_listener *listener, void *data) { static void new_output_notify(struct wl_listener *listener, void *data) { struct wlr_output *output = data; struct sample_state *sample = wl_container_of(listener, sample, new_output); + + wlr_output_init_render(output, sample->allocator, sample->renderer); + struct sample_output *sample_output = calloc(1, sizeof(struct sample_output)); sample_output->x_offs = sample_output->y_offs = 0; sample_output->x_vel = sample_output->y_vel = 128; @@ -245,7 +250,7 @@ int main(int argc, char *argv[]) { state.new_input.notify = new_input_notify; clock_gettime(CLOCK_MONOTONIC, &state.last_frame); - state.renderer = wlr_backend_get_renderer(wlr); + state.renderer = wlr_renderer_autocreate(wlr); if (!state.renderer) { wlr_log(WLR_ERROR, "Could not start compositor, OOM"); wlr_backend_destroy(wlr); @@ -259,6 +264,8 @@ int main(int argc, char *argv[]) { exit(EXIT_FAILURE); } + state.allocator = wlr_allocator_autocreate(wlr, state.renderer); + if (!wlr_backend_start(wlr)) { wlr_log(WLR_ERROR, "Failed to start backend"); wlr_backend_destroy(wlr); diff --git a/examples/scene-graph.c b/examples/scene-graph.c index d5c23a16b..c3ce05893 100644 --- a/examples/scene-graph.c +++ b/examples/scene-graph.c @@ -8,11 +8,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include @@ -25,10 +25,11 @@ static const int border_width = 3; struct server { struct wl_display *display; struct wlr_backend *backend; + struct wlr_renderer *renderer; + struct wlr_allocator *allocator; struct wlr_scene *scene; - struct wl_list outputs; - struct wl_list surfaces; + uint32_t surface_offset; struct wl_listener new_output; struct wl_listener new_surface; @@ -62,24 +63,21 @@ static void output_handle_frame(struct wl_listener *listener, void *data) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - - struct surface *surface; - wl_list_for_each(surface, &output->server->surfaces, link) { - wlr_surface_send_frame_done(surface->wlr, &now); - } + wlr_scene_output_send_frame_done(output->scene_output, &now); } static void server_handle_new_output(struct wl_listener *listener, void *data) { struct server *server = wl_container_of(listener, server, new_output); struct wlr_output *wlr_output = data; + wlr_output_init_render(wlr_output, server->allocator, server->renderer); + struct output *output = calloc(1, sizeof(struct output)); output->wlr = wlr_output; output->server = server; output->frame.notify = output_handle_frame; wl_signal_add(&wlr_output->events.frame, &output->frame); - wl_list_insert(&server->outputs, &output->link); output->scene_output = wlr_scene_output_create(server->scene, wlr_output); @@ -113,7 +111,8 @@ static void server_handle_new_surface(struct wl_listener *listener, struct server *server = wl_container_of(listener, server, new_surface); struct wlr_surface *wlr_surface = data; - int pos = 50 * wl_list_length(&server->surfaces); + int pos = server->surface_offset; + server->surface_offset += 50; struct surface *surface = calloc(1, sizeof(struct surface)); surface->wlr = wlr_surface; @@ -129,7 +128,6 @@ static void server_handle_new_surface(struct wl_listener *listener, surface->scene_surface = wlr_scene_surface_create(&server->scene->node, wlr_surface); - wl_list_insert(server->surfaces.prev, &surface->link); wlr_scene_node_set_position(&surface->scene_surface->node, pos + border_width, pos + border_width); @@ -157,21 +155,22 @@ int main(int argc, char *argv[]) { } struct server server = {0}; + server.surface_offset = 0; server.display = wl_display_create(); server.backend = wlr_backend_autocreate(server.display); server.scene = wlr_scene_create(); - struct wlr_renderer *renderer = wlr_backend_get_renderer(server.backend); - wlr_renderer_init_wl_display(renderer, server.display); + server.renderer = wlr_renderer_autocreate(server.backend); + wlr_renderer_init_wl_display(server.renderer, server.display); + + server.allocator = wlr_allocator_autocreate(server.backend, + server.renderer); struct wlr_compositor *compositor = - wlr_compositor_create(server.display, renderer); + wlr_compositor_create(server.display, server.renderer); wlr_xdg_shell_create(server.display); - wl_list_init(&server.outputs); - wl_list_init(&server.surfaces); - server.new_output.notify = server_handle_new_output; wl_signal_add(&server.backend->events.new_output, &server.new_output); diff --git a/examples/simple.c b/examples/simple.c index 9135d163b..94f3abe0e 100644 --- a/examples/simple.c +++ b/examples/simple.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -18,6 +19,8 @@ struct sample_state { struct wl_display *display; struct wl_listener new_output; struct wl_listener new_input; + struct wlr_renderer *renderer; + struct wlr_allocator *allocator; struct timespec last_frame; float color[4]; int dec; @@ -61,8 +64,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) { wlr_output_attach_render(wlr_output, NULL); - struct wlr_renderer *renderer = - wlr_backend_get_renderer(wlr_output->backend); + struct wlr_renderer *renderer = sample->renderer; wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height); wlr_renderer_clear(renderer, sample->color); wlr_renderer_end(renderer); @@ -84,6 +86,9 @@ static void new_output_notify(struct wl_listener *listener, void *data) { struct wlr_output *output = data; struct sample_state *sample = wl_container_of(listener, sample, new_output); + + wlr_output_init_render(output, sample->allocator, sample->renderer); + struct sample_output *sample_output = calloc(1, sizeof(struct sample_output)); sample_output->output = output; @@ -171,6 +176,10 @@ int main(void) { if (!backend) { exit(1); } + + state.renderer = wlr_renderer_autocreate(backend); + state.allocator = wlr_allocator_autocreate(backend, state.renderer); + wl_signal_add(&backend->events.new_output, &state.new_output); state.new_output.notify = new_output_notify; wl_signal_add(&backend->events.new_input, &state.new_input); diff --git a/examples/tablet.c b/examples/tablet.c index 5d0e8dcc3..234bfb991 100644 --- a/examples/tablet.c +++ b/examples/tablet.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -22,6 +23,7 @@ struct sample_state { struct wl_display *display; struct wlr_renderer *renderer; + struct wlr_allocator *allocator; bool proximity, tap, button; double distance; double pressure; @@ -237,6 +239,9 @@ static void output_remove_notify(struct wl_listener *listener, void *data) { static void new_output_notify(struct wl_listener *listener, void *data) { struct wlr_output *output = data; struct sample_state *sample = wl_container_of(listener, sample, new_output); + + wlr_output_init_render(output, sample->allocator, sample->renderer); + struct sample_output *sample_output = calloc(1, sizeof(struct sample_output)); sample_output->output = output; sample_output->sample = sample; @@ -361,11 +366,14 @@ int main(int argc, char *argv[]) { state.new_input.notify = new_input_notify; clock_gettime(CLOCK_MONOTONIC, &state.last_frame); - state.renderer = wlr_backend_get_renderer(wlr); + state.renderer = wlr_renderer_autocreate(wlr); if (!state.renderer) { wlr_log(WLR_ERROR, "Could not start compositor, OOM"); exit(EXIT_FAILURE); } + + state.allocator = wlr_allocator_autocreate(wlr, state.renderer); + if (!wlr_backend_start(wlr)) { wlr_log(WLR_ERROR, "Failed to start backend"); wlr_backend_destroy(wlr); diff --git a/examples/touch.c b/examples/touch.c index 0c9cd2b40..ae2dcf8fc 100644 --- a/examples/touch.c +++ b/examples/touch.c @@ -10,8 +10,9 @@ #include #include #include -#include +#include #include +#include #include #include #include @@ -23,6 +24,7 @@ struct sample_state { struct wl_display *display; struct wlr_renderer *renderer; + struct wlr_allocator *allocator; struct wlr_texture *cat_texture; struct wl_list touch_points; struct timespec last_frame; @@ -148,6 +150,9 @@ static void output_remove_notify(struct wl_listener *listener, void *data) { static void new_output_notify(struct wl_listener *listener, void *data) { struct wlr_output *output = data; struct sample_state *sample = wl_container_of(listener, sample, new_output); + + wlr_output_init_render(output, sample->allocator, sample->renderer); + struct sample_output *sample_output = calloc(1, sizeof(struct sample_output)); sample_output->output = output; sample_output->sample = sample; @@ -254,8 +259,7 @@ int main(int argc, char *argv[]) { state.new_input.notify = new_input_notify; clock_gettime(CLOCK_MONOTONIC, &state.last_frame); - - state.renderer = wlr_backend_get_renderer(wlr); + state.renderer = wlr_renderer_autocreate(wlr); if (!state.renderer) { wlr_log(WLR_ERROR, "Could not start compositor, OOM"); exit(EXIT_FAILURE); @@ -268,6 +272,8 @@ int main(int argc, char *argv[]) { exit(EXIT_FAILURE); } + state.allocator = wlr_allocator_autocreate(wlr, state.renderer); + if (!wlr_backend_start(wlr)) { wlr_log(WLR_ERROR, "Failed to start backend"); wlr_backend_destroy(wlr); diff --git a/include/backend/backend.h b/include/backend/backend.h index 7d88cd79d..8c7440c3d 100644 --- a/include/backend/backend.h +++ b/include/backend/backend.h @@ -10,10 +10,4 @@ */ uint32_t backend_get_buffer_caps(struct wlr_backend *backend); -/** - * Get the backend's allocator. Automatically creates the allocator if - * necessary. - */ -struct wlr_allocator *backend_get_allocator(struct wlr_backend *backend); - #endif diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h index e045a72ee..c5d3358f9 100644 --- a/include/backend/drm/drm.h +++ b/include/backend/drm/drm.h @@ -36,7 +36,7 @@ struct wlr_drm_plane { struct wlr_drm_crtc { uint32_t id; - uint32_t lessee_id; + struct wlr_drm_lease *lease; // Atomic modesetting only uint32_t mode_id; @@ -118,7 +118,7 @@ struct wlr_drm_connector { enum wlr_drm_connector_status status; bool desired_enabled; uint32_t id; - uint32_t lessee_id; + struct wlr_drm_lease *lease; struct wlr_drm_crtc *crtc; uint32_t possible_crtcs; @@ -149,6 +149,7 @@ bool init_drm_resources(struct wlr_drm_backend *drm); void finish_drm_resources(struct wlr_drm_backend *drm); void scan_drm_connectors(struct wlr_drm_backend *state, struct wlr_device_hotplug_event *event); +void scan_drm_leases(struct wlr_drm_backend *drm); int handle_drm_event(int fd, uint32_t mask, void *data); void destroy_drm_connector(struct wlr_drm_connector *conn); bool drm_connector_commit_state(struct wlr_drm_connector *conn, @@ -157,6 +158,7 @@ bool drm_connector_is_cursor_visible(struct wlr_drm_connector *conn); bool drm_connector_supports_vrr(struct wlr_drm_connector *conn); size_t drm_crtc_get_gamma_lut_size(struct wlr_drm_backend *drm, struct wlr_drm_crtc *crtc); +void drm_lease_destroy(struct wlr_drm_lease *lease); struct wlr_drm_fb *plane_get_next_fb(struct wlr_drm_plane *plane); diff --git a/include/backend/drm/monitor.h b/include/backend/drm/monitor.h new file mode 100644 index 000000000..518171932 --- /dev/null +++ b/include/backend/drm/monitor.h @@ -0,0 +1,24 @@ +#ifndef BACKEND_DRM_MONITOR_H +#define BACKEND_DRM_MONITOR_H + +#include + +/** + * Helper to create new DRM sub-backends on GPU hotplug. + */ +struct wlr_drm_backend_monitor { + struct wlr_backend *multi; + struct wlr_backend *primary_drm; + struct wlr_session *session; + + struct wl_listener multi_destroy; + struct wl_listener primary_drm_destroy; + struct wl_listener session_destroy; + struct wl_listener session_add_drm_card; +}; + +struct wlr_drm_backend_monitor *drm_backend_monitor_create( + struct wlr_backend *multi, struct wlr_backend *primary_drm, + struct wlr_session *session); + +#endif diff --git a/include/backend/drm/util.h b/include/backend/drm/util.h index b17db0048..1ce4b9104 100644 --- a/include/backend/drm/util.h +++ b/include/backend/drm/util.h @@ -39,11 +39,4 @@ size_t match_obj(size_t num_objs, const uint32_t objs[static restrict num_objs], size_t num_res, const uint32_t res[static restrict num_res], uint32_t out[static restrict num_res]); -/** - * Close a GEM buffer handle. - * - * TODO: replace with drmCloseBufferHandle. - */ -void close_bo_handle(int drm_fd, uint32_t handle); - #endif diff --git a/include/backend/headless.h b/include/backend/headless.h index 42a07db8d..047509009 100644 --- a/include/backend/headless.h +++ b/include/backend/headless.h @@ -8,14 +8,10 @@ struct wlr_headless_backend { struct wlr_backend backend; - int drm_fd; struct wl_display *display; struct wl_list outputs; size_t last_output_num; - struct wl_list input_devices; struct wl_listener display_destroy; - struct wlr_renderer *parent_renderer; - struct wl_listener parent_renderer_destroy; bool started; }; @@ -29,12 +25,6 @@ struct wlr_headless_output { int frame_delay; // ms }; -struct wlr_headless_input_device { - struct wlr_input_device wlr_input_device; - - struct wlr_headless_backend *backend; -}; - struct wlr_headless_backend *headless_backend_from_backend( struct wlr_backend *wlr_backend); diff --git a/include/backend/libinput.h b/include/backend/libinput.h index e85ad2913..39e6dfe5a 100644 --- a/include/backend/libinput.h +++ b/include/backend/libinput.h @@ -5,7 +5,12 @@ #include #include #include -#include +#include +#include +#include +#include +#include +#include #include struct wlr_libinput_backend { @@ -26,7 +31,7 @@ struct wlr_libinput_backend { struct wlr_libinput_input_device { struct wlr_input_device wlr_input_device; - + struct wl_list link; struct libinput_device *handle; }; @@ -39,6 +44,15 @@ struct wlr_input_device *get_appropriate_device( enum wlr_input_device_type desired_type, struct libinput_device *device); +void destroy_libinput_input_device(struct wlr_libinput_input_device *dev); + +extern const struct wlr_keyboard_impl libinput_keyboard_impl; +extern const struct wlr_pointer_impl libinput_pointer_impl; +extern const struct wlr_switch_impl libinput_switch_impl; +extern const struct wlr_tablet_impl libinput_tablet_impl; +extern const struct wlr_tablet_pad_impl libinput_tablet_pad_impl; +extern const struct wlr_touch_impl libinput_touch_impl; + struct wlr_keyboard *create_libinput_keyboard( struct libinput_device *device); void handle_keyboard_key(struct libinput_event *event, diff --git a/include/backend/noop.h b/include/backend/noop.h deleted file mode 100644 index 5a8ee6779..000000000 --- a/include/backend/noop.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef BACKEND_NOOP_H -#define BACKEND_NOOP_H - -#include -#include - -struct wlr_noop_backend { - struct wlr_backend backend; - struct wl_display *display; - struct wl_list outputs; - size_t last_output_num; - bool started; - - struct wl_listener display_destroy; -}; - -struct wlr_noop_output { - struct wlr_output wlr_output; - - struct wlr_noop_backend *backend; - struct wl_list link; -}; - -struct wlr_noop_backend *noop_backend_from_backend( - struct wlr_backend *wlr_backend); - -#endif diff --git a/include/backend/session/session.h b/include/backend/session/session.h index ebe6fc700..5eca7f576 100644 --- a/include/backend/session/session.h +++ b/include/backend/session/session.h @@ -11,4 +11,7 @@ bool libseat_change_vt(struct wlr_session *base, unsigned vt); void session_init(struct wlr_session *session); +struct wlr_device *session_open_if_kms(struct wlr_session *restrict session, + const char *restrict path); + #endif diff --git a/include/backend/wayland.h b/include/backend/wayland.h index 3235494df..668b69aed 100644 --- a/include/backend/wayland.h +++ b/include/backend/wayland.h @@ -40,6 +40,7 @@ struct wlr_wl_backend { struct zwp_relative_pointer_manager_v1 *zwp_relative_pointer_manager_v1; struct wl_list seats; // wlr_wl_seat.link struct zwp_tablet_manager_v2 *tablet_manager; + clockid_t presentation_clock; struct wlr_drm_format_set shm_formats; struct wlr_drm_format_set linux_dmabuf_v1_formats; struct wl_drm *legacy_drm; @@ -86,6 +87,7 @@ struct wlr_wl_output { struct wlr_wl_input_device { struct wlr_input_device wlr_input_device; + struct wl_list link; uint32_t fingers; struct wlr_wl_backend *backend; @@ -132,9 +134,12 @@ struct wlr_wl_input_device *create_wl_input_device( 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_input_device(struct wlr_wl_input_device *dev); void destroy_wl_buffer(struct wlr_wl_buffer *buffer); extern const struct wl_seat_listener seat_listener; +extern const struct wlr_tablet_pad_impl tablet_pad_impl; +extern const struct wlr_tablet_impl tablet_impl; struct wlr_wl_tablet_seat *wl_add_tablet_seat( struct zwp_tablet_manager_v2 *manager, diff --git a/include/backend/x11.h b/include/backend/x11.h index fadcbedc3..572fce189 100644 --- a/include/backend/x11.h +++ b/include/backend/x11.h @@ -15,11 +15,10 @@ #include #include -#include #include #include -#include #include +#include #include #define XCB_EVENT_RESPONSE_TYPE_MASK 0x7f @@ -35,10 +34,8 @@ struct wlr_x11_output { xcb_present_event_t present_event_id; struct wlr_pointer pointer; - struct wlr_input_device pointer_dev; struct wlr_touch touch; - struct wlr_input_device touch_dev; struct wl_list touchpoints; // wlr_x11_touchpoint::link struct wl_list buffers; // wlr_x11_buffer::link @@ -81,7 +78,6 @@ struct wlr_x11_backend { struct wl_list outputs; // wlr_x11_output::link struct wlr_keyboard keyboard; - struct wlr_input_device keyboard_dev; int drm_fd; struct wlr_drm_format_set dri3_formats; @@ -130,10 +126,9 @@ struct wlr_x11_backend *get_x11_backend_from_backend( struct wlr_x11_output *get_x11_output_from_window_id( struct wlr_x11_backend *x11, xcb_window_t window); -extern const struct wlr_keyboard_impl keyboard_impl; -extern const struct wlr_pointer_impl pointer_impl; -extern const struct wlr_touch_impl touch_impl; -extern const struct wlr_input_device_impl input_device_impl; +extern const struct wlr_keyboard_impl x11_keyboard_impl; +extern const struct wlr_pointer_impl x11_pointer_impl; +extern const struct wlr_touch_impl x11_touch_impl; void handle_x11_xinput_event(struct wlr_x11_backend *x11, xcb_ge_generic_event_t *event); diff --git a/include/render/drm_format_set.h b/include/render/drm_format_set.h index c438722bc..c94c2af29 100644 --- a/include/render/drm_format_set.h +++ b/include/render/drm_format_set.h @@ -4,6 +4,7 @@ #include struct wlr_drm_format *wlr_drm_format_create(uint32_t format); +bool wlr_drm_format_has(const struct wlr_drm_format *fmt, uint64_t modifier); 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); /** diff --git a/include/render/gles2.h b/include/render/gles2.h index fb2de1ab1..245b2804e 100644 --- a/include/render/gles2.h +++ b/include/render/gles2.h @@ -24,7 +24,6 @@ struct wlr_gles2_pixel_format { struct wlr_gles2_tex_shader { GLuint program; GLint proj; - GLint invert_y; GLint tex; GLint alpha; GLint pos_attrib; @@ -101,7 +100,6 @@ struct wlr_gles2_texture { EGLImageKHR image; - bool inverted_y; bool has_alpha; // Only affects target == GL_TEXTURE_2D diff --git a/include/render/vulkan.h b/include/render/vulkan.h index aa40198ce..1cba5a192 100644 --- a/include/render/vulkan.h +++ b/include/render/vulkan.h @@ -246,7 +246,6 @@ struct wlr_vk_texture { bool dmabuf_imported; bool owned; // if dmabuf_imported: whether we have ownership of the image bool transitioned; // if dma_imported: whether we transitioned it away from preinit - bool invert_y; // if dma_imported: whether we must flip y struct wl_list foreign_link; struct wl_list destroy_link; struct wl_list link; // wlr_gles2_renderer.textures diff --git a/include/types/wlr_output.h b/include/types/wlr_output.h index cde64e3ac..1159d00a1 100644 --- a/include/types/wlr_output.h +++ b/include/types/wlr_output.h @@ -8,7 +8,7 @@ void output_pending_resolution(struct wlr_output *output, int *width, int *height); struct wlr_drm_format *output_pick_format(struct wlr_output *output, - const struct wlr_drm_format_set *display_formats); + const struct wlr_drm_format_set *display_formats, uint32_t format); void output_clear_back_buffer(struct wlr_output *output); bool output_ensure_buffer(struct wlr_output *output); diff --git a/include/types/wlr_surface.h b/include/types/wlr_surface.h deleted file mode 100644 index b8c7d02d2..000000000 --- a/include/types/wlr_surface.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef TYPES_WLR_SURFACE_H -#define TYPES_WLR_SURFACE_H - -#include - -struct wlr_renderer; - -/** - * Create a new surface resource with the provided new ID. - */ -struct wlr_surface *surface_create(struct wl_client *client, - uint32_t version, uint32_t id, struct wlr_renderer *renderer); - -/** - * Create a new subsurface resource with the provided new ID. - */ -struct wlr_subsurface *subsurface_create(struct wlr_surface *surface, - struct wlr_surface *parent, uint32_t version, uint32_t id); - -#endif diff --git a/include/types/wlr_xdg_shell.h b/include/types/wlr_xdg_shell.h index 509b1d2c1..cfed6ee1d 100644 --- a/include/types/wlr_xdg_shell.h +++ b/include/types/wlr_xdg_shell.h @@ -5,41 +5,36 @@ #include #include "xdg-shell-protocol.h" -struct wlr_xdg_positioner_resource { - struct wl_resource *resource; - struct wlr_xdg_positioner attrs; -}; - extern const struct wlr_surface_role xdg_toplevel_surface_role; extern const struct wlr_surface_role xdg_popup_surface_role; struct wlr_xdg_surface *create_xdg_surface( - struct wlr_xdg_client *client, struct wlr_surface *surface, + struct wlr_xdg_client *client, struct wlr_surface *wlr_surface, uint32_t id); void unmap_xdg_surface(struct wlr_xdg_surface *surface); -void reset_xdg_surface(struct wlr_xdg_surface *xdg_surface); +void reset_xdg_surface(struct wlr_xdg_surface *surface); void destroy_xdg_surface(struct wlr_xdg_surface *surface); -void handle_xdg_surface_commit(struct wlr_surface *wlr_surface); -void handle_xdg_surface_precommit(struct wlr_surface *wlr_surface); +void xdg_surface_role_commit(struct wlr_surface *wlr_surface); +void xdg_surface_role_precommit(struct wlr_surface *wlr_surface, + const struct wlr_surface_state *state); void create_xdg_positioner(struct wlr_xdg_client *client, uint32_t id); -struct wlr_xdg_positioner_resource *get_xdg_positioner_from_resource( - struct wl_resource *resource); -void create_xdg_popup(struct wlr_xdg_surface *xdg_surface, +void create_xdg_popup(struct wlr_xdg_surface *surface, struct wlr_xdg_surface *parent, - struct wlr_xdg_positioner_resource *positioner, int32_t id); -void handle_xdg_surface_popup_committed(struct wlr_xdg_surface *surface); -struct wlr_xdg_popup_grab *get_xdg_shell_popup_grab_from_seat( - struct wlr_xdg_shell *shell, struct wlr_seat *seat); + struct wlr_xdg_positioner *positioner, uint32_t id); +void unmap_xdg_popup(struct wlr_xdg_popup *popup); +void destroy_xdg_popup(struct wlr_xdg_popup *popup); +void handle_xdg_popup_committed(struct wlr_xdg_popup *popup); -void create_xdg_toplevel(struct wlr_xdg_surface *xdg_surface, +void create_xdg_toplevel(struct wlr_xdg_surface *surface, uint32_t id); -void handle_xdg_surface_toplevel_committed(struct wlr_xdg_surface *surface); -void send_xdg_toplevel_configure(struct wlr_xdg_surface *surface, - struct wlr_xdg_surface_configure *configure); -void handle_xdg_toplevel_ack_configure(struct wlr_xdg_surface *surface, - struct wlr_xdg_surface_configure *configure); -void destroy_xdg_toplevel(struct wlr_xdg_surface *surface); +void unmap_xdg_toplevel(struct wlr_xdg_toplevel *toplevel); +void destroy_xdg_toplevel(struct wlr_xdg_toplevel *toplevel); +void handle_xdg_toplevel_committed(struct wlr_xdg_toplevel *toplevel); +struct wlr_xdg_toplevel_configure *send_xdg_toplevel_configure( + struct wlr_xdg_toplevel *toplevel); +void handle_xdg_toplevel_ack_configure(struct wlr_xdg_toplevel *toplevel, + struct wlr_xdg_toplevel_configure *configure); #endif diff --git a/include/util/global.h b/include/util/global.h index ea0fda83b..1c979ab11 100644 --- a/include/util/global.h +++ b/include/util/global.h @@ -9,7 +9,6 @@ * Globals that are created and destroyed on the fly need special handling to * prevent race conditions with wl_registry. Use this function to destroy them. */ -void wlr_global_destroy_safe(struct wl_global *global, - struct wl_display *display); +void wlr_global_destroy_safe(struct wl_global *global); #endif diff --git a/include/wlr/backend.h b/include/wlr/backend.h index d616fa1db..d781f74de 100644 --- a/include/wlr/backend.h +++ b/include/wlr/backend.h @@ -25,12 +25,6 @@ struct wlr_backend { /** Raised when new outputs are added, passed the wlr_output */ struct wl_signal new_output; } events; - - // Private state - - bool has_own_renderer; - struct wlr_renderer *renderer; - struct wlr_allocator *allocator; }; /** @@ -50,10 +44,6 @@ bool wlr_backend_start(struct wlr_backend *backend); * automatically when the wl_display is destroyed. */ void wlr_backend_destroy(struct wlr_backend *backend); -/** - * Obtains the wlr_renderer reference this backend is using. - */ -struct wlr_renderer *wlr_backend_get_renderer(struct wlr_backend *backend); /** * Obtains the wlr_session reference from this backend if there is any. * Might return NULL for backends that don't use a session. diff --git a/include/wlr/backend/drm.h b/include/wlr/backend/drm.h index 0e9f61081..800bc585f 100644 --- a/include/wlr/backend/drm.h +++ b/include/wlr/backend/drm.h @@ -14,6 +14,20 @@ #include #include +struct wlr_drm_backend; + +struct wlr_drm_lease { + int fd; + uint32_t lessee_id; + struct wlr_drm_backend *backend; + + struct { + struct wl_signal destroy; + } events; + + void *data; +}; + /** * Creates a DRM backend using the specified GPU file descriptor (typically from * a device node in /dev/dri). @@ -41,18 +55,20 @@ uint32_t wlr_drm_connector_get_id(struct wlr_output *output); int wlr_drm_backend_get_non_master_fd(struct wlr_backend *backend); /** - * Leases a given output to the caller. The output must be from the associated - * DRM backend. - * Returns a valid opened DRM FD or -1 on error. + * Leases the given outputs to the caller. The outputs must be from the + * associated DRM backend. + * + * Returns NULL on error. */ -int wlr_drm_create_lease(struct wlr_output **outputs, size_t n_outputs, - uint32_t *lessee_id); +struct wlr_drm_lease *wlr_drm_create_lease(struct wlr_output **outputs, + size_t n_outputs, int *lease_fd); /** - * Terminates a given lease. The output will be owned again by the backend + * Terminates and destroys a given lease. + * + * The outputs will be owned again by the backend. */ -bool wlr_drm_backend_terminate_lease(struct wlr_backend *backend, - uint32_t lessee_id); +void wlr_drm_lease_terminate(struct wlr_drm_lease *lease); /** * Add mode to the list of available modes diff --git a/include/wlr/backend/headless.h b/include/wlr/backend/headless.h index 3eb1677d2..f47354830 100644 --- a/include/wlr/backend/headless.h +++ b/include/wlr/backend/headless.h @@ -18,11 +18,6 @@ * default. */ struct wlr_backend *wlr_headless_backend_create(struct wl_display *display); -/** - * Creates a headless backend with an existing renderer. - */ -struct wlr_backend *wlr_headless_backend_create_with_renderer( - struct wl_display *display, struct wlr_renderer *renderer); /** * Create a new headless output backed by an in-memory EGL framebuffer. You can * read pixels from this framebuffer via wlr_renderer_read_pixels but it is @@ -30,15 +25,8 @@ struct wlr_backend *wlr_headless_backend_create_with_renderer( */ struct wlr_output *wlr_headless_add_output(struct wlr_backend *backend, unsigned int width, unsigned int height); -/** - * Creates a new input device. The caller is responsible for manually raising - * any event signals on the new input device if it wants to simulate input - * events. - */ -struct wlr_input_device *wlr_headless_add_input_device( - struct wlr_backend *backend, enum wlr_input_device_type type); + bool wlr_backend_is_headless(struct wlr_backend *backend); -bool wlr_input_device_is_headless(struct wlr_input_device *device); bool wlr_output_is_headless(struct wlr_output *output); #endif diff --git a/include/wlr/backend/interface.h b/include/wlr/backend/interface.h index 529f8f87a..f0cafb015 100644 --- a/include/wlr/backend/interface.h +++ b/include/wlr/backend/interface.h @@ -16,7 +16,6 @@ struct wlr_backend_impl { bool (*start)(struct wlr_backend *backend); void (*destroy)(struct wlr_backend *backend); - struct wlr_renderer *(*get_renderer)(struct wlr_backend *backend); struct wlr_session *(*get_session)(struct wlr_backend *backend); clockid_t (*get_presentation_clock)(struct wlr_backend *backend); int (*get_drm_fd)(struct wlr_backend *backend); diff --git a/include/wlr/backend/noop.h b/include/wlr/backend/noop.h deleted file mode 100644 index 592b8f35b..000000000 --- a/include/wlr/backend/noop.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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_BACKEND_NOOP_H -#define WLR_BACKEND_NOOP_H - -#include -#include - -/** - * Creates a noop backend. Noop backends do not have a framebuffer and are not - * capable of rendering anything. They are useful for when there's no real - * outputs connected; you can stash your views on a noop output until an output - * is connected. - */ -struct wlr_backend *wlr_noop_backend_create(struct wl_display *display); - -/** - * Create a new noop output. - */ -struct wlr_output *wlr_noop_add_output(struct wlr_backend *backend); - -bool wlr_backend_is_noop(struct wlr_backend *backend); -bool wlr_output_is_noop(struct wlr_output *output); - -#endif diff --git a/include/wlr/backend/session.h b/include/wlr/backend/session.h index 7d34bfebe..c8461ff7c 100644 --- a/include/wlr/backend/session.h +++ b/include/wlr/backend/session.h @@ -59,6 +59,7 @@ struct wlr_session_add_event { enum wlr_device_change_type { WLR_DEVICE_HOTPLUG = 1, + WLR_DEVICE_LEASE, }; struct wlr_device_hotplug_event { diff --git a/include/wlr/interfaces/wlr_input_device.h b/include/wlr/interfaces/wlr_input_device.h deleted file mode 100644 index 05248bf60..000000000 --- a/include/wlr/interfaces/wlr_input_device.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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_INTERFACES_WLR_INPUT_DEVICE_H -#define WLR_INTERFACES_WLR_INPUT_DEVICE_H - -#include - -struct wlr_input_device_impl { - void (*destroy)(struct wlr_input_device *wlr_device); -}; - -void wlr_input_device_init( - struct wlr_input_device *wlr_device, - enum wlr_input_device_type type, - const struct wlr_input_device_impl *impl, - const char *name, int vendor, int product); -void wlr_input_device_destroy(struct wlr_input_device *dev); - -#endif diff --git a/include/wlr/interfaces/wlr_keyboard.h b/include/wlr/interfaces/wlr_keyboard.h index 5d537827c..4bcef57b2 100644 --- a/include/wlr/interfaces/wlr_keyboard.h +++ b/include/wlr/interfaces/wlr_keyboard.h @@ -18,7 +18,7 @@ struct wlr_keyboard_impl { }; void wlr_keyboard_init(struct wlr_keyboard *keyboard, - const struct wlr_keyboard_impl *impl); + const struct wlr_keyboard_impl *impl, const char *name); void wlr_keyboard_destroy(struct wlr_keyboard *keyboard); void wlr_keyboard_notify_key(struct wlr_keyboard *keyboard, struct wlr_event_keyboard_key *event); diff --git a/include/wlr/interfaces/wlr_output.h b/include/wlr/interfaces/wlr_output.h index 100754f68..79cc8ee0a 100644 --- a/include/wlr/interfaces/wlr_output.h +++ b/include/wlr/interfaces/wlr_output.h @@ -21,6 +21,7 @@ (WLR_OUTPUT_STATE_DAMAGE | \ WLR_OUTPUT_STATE_SCALE | \ WLR_OUTPUT_STATE_TRANSFORM | \ + WLR_OUTPUT_STATE_RENDER_FORMAT | \ WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) /** @@ -85,7 +86,7 @@ struct wlr_output_impl { */ void (*get_cursor_size)(struct wlr_output *output, int *width, int *height); /** - * Get the list of DMA-BUF formats suitable for the primary buffer, + * Get the list of DRM formats suitable for the primary buffer, * assuming a buffer with the specified capabilities. * * If unimplemented, the primary buffer has no format constraint. If NULL diff --git a/include/wlr/interfaces/wlr_pointer.h b/include/wlr/interfaces/wlr_pointer.h index fd3ab1023..e39500175 100644 --- a/include/wlr/interfaces/wlr_pointer.h +++ b/include/wlr/interfaces/wlr_pointer.h @@ -16,7 +16,7 @@ struct wlr_pointer_impl { }; void wlr_pointer_init(struct wlr_pointer *pointer, - const struct wlr_pointer_impl *impl); + const struct wlr_pointer_impl *impl, const char *name); void wlr_pointer_destroy(struct wlr_pointer *pointer); #endif diff --git a/include/wlr/interfaces/wlr_switch.h b/include/wlr/interfaces/wlr_switch.h index 83a1365b5..fbf5e6cb9 100644 --- a/include/wlr/interfaces/wlr_switch.h +++ b/include/wlr/interfaces/wlr_switch.h @@ -16,7 +16,7 @@ struct wlr_switch_impl { }; void wlr_switch_init(struct wlr_switch *switch_device, - struct wlr_switch_impl *impl); + const struct wlr_switch_impl *impl, const char *name); void wlr_switch_destroy(struct wlr_switch *switch_device); #endif diff --git a/include/wlr/interfaces/wlr_tablet_pad.h b/include/wlr/interfaces/wlr_tablet_pad.h index 86bbe9c39..ff5c93594 100644 --- a/include/wlr/interfaces/wlr_tablet_pad.h +++ b/include/wlr/interfaces/wlr_tablet_pad.h @@ -16,7 +16,7 @@ struct wlr_tablet_pad_impl { }; void wlr_tablet_pad_init(struct wlr_tablet_pad *pad, - struct wlr_tablet_pad_impl *impl); + const struct wlr_tablet_pad_impl *impl, const char *name); void wlr_tablet_pad_destroy(struct wlr_tablet_pad *pad); #endif diff --git a/include/wlr/interfaces/wlr_tablet_tool.h b/include/wlr/interfaces/wlr_tablet_tool.h index 81e72d75e..de7430a2f 100644 --- a/include/wlr/interfaces/wlr_tablet_tool.h +++ b/include/wlr/interfaces/wlr_tablet_tool.h @@ -16,7 +16,7 @@ struct wlr_tablet_impl { }; void wlr_tablet_init(struct wlr_tablet *tablet, - const struct wlr_tablet_impl *impl); + const struct wlr_tablet_impl *impl, const char *name); void wlr_tablet_destroy(struct wlr_tablet *tablet); #endif diff --git a/include/wlr/interfaces/wlr_touch.h b/include/wlr/interfaces/wlr_touch.h index e4ea6a781..2b95d2bf8 100644 --- a/include/wlr/interfaces/wlr_touch.h +++ b/include/wlr/interfaces/wlr_touch.h @@ -16,7 +16,7 @@ struct wlr_touch_impl { }; void wlr_touch_init(struct wlr_touch *touch, - const struct wlr_touch_impl *impl); + const struct wlr_touch_impl *impl, const char *name); void wlr_touch_destroy(struct wlr_touch *touch); #endif diff --git a/include/wlr/render/allocator.h b/include/wlr/render/allocator.h index 3caeffa09..3150e36c3 100644 --- a/include/wlr/render/allocator.h +++ b/include/wlr/render/allocator.h @@ -51,6 +51,18 @@ void wlr_allocator_destroy(struct wlr_allocator *alloc); * * When the caller is done with it, they must unreference it by calling * wlr_buffer_drop. + * + * The `format` passed in indicates the format to use and the list of + * acceptable modifiers. The order in which modifiers are listed is not + * significant. + * + * When running with legacy drivers which don't support explicit modifiers, the + * allocator must recognize two modifiers: INVALID (for implicit tiling and/or + * compression) and LINEAR. + * + * The allocator must return a buffer using one of the modifiers listed. In + * particular, allocators must not return a buffer with an implicit modifier + * unless the user has allowed it by passing INVALID in the modifier list. */ struct wlr_buffer *wlr_allocator_create_buffer(struct wlr_allocator *alloc, int width, int height, const struct wlr_drm_format *format); diff --git a/include/wlr/render/dmabuf.h b/include/wlr/render/dmabuf.h index 75892d30f..62f8e0217 100644 --- a/include/wlr/render/dmabuf.h +++ b/include/wlr/render/dmabuf.h @@ -14,17 +14,27 @@ #define WLR_DMABUF_MAX_PLANES 4 -enum wlr_dmabuf_attributes_flags { - WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT = 1 << 0, - WLR_DMABUF_ATTRIBUTES_FLAGS_INTERLACED = 1 << 1, - WLR_DMABUF_ATTRIBUTES_FLAGS_BOTTOM_FIRST = 1 << 2, -}; - +/** + * A Linux DMA-BUF pixel buffer. + * + * If the buffer was allocated with explicit modifiers enabled, the `modifier` + * field must not be INVALID. + * + * If the buffer was allocated with explicit modifiers disabled (either because + * the driver doesn't support it, or because the user didn't specify a valid + * modifier list), the `modifier` field can have two values: INVALID means that + * an implicit vendor-defined modifier is in use, LINEAR means that the buffer + * is linear. The `modifier` field must not have any other value. + * + * When importing a DMA-BUF, users must not ignore the modifier unless it's + * INVALID or LINEAR. In particular, users must not import a DMA-BUF to a + * legacy API which doesn't support specifying an explicit modifier unless the + * modifier is set to INVALID or LINEAR. + */ struct wlr_dmabuf_attributes { int32_t width, height; - uint32_t format; - uint32_t flags; // enum wlr_dmabuf_attributes_flags - uint64_t modifier; + uint32_t format; // FourCC code, see DRM_FORMAT_* in + uint64_t modifier; // see DRM_FORMAT_MOD_* in int n_planes; uint32_t offset[WLR_DMABUF_MAX_PLANES]; diff --git a/include/wlr/render/drm_format_set.h b/include/wlr/render/drm_format_set.h index 15d4eea3f..9f4a86ea4 100644 --- a/include/wlr/render/drm_format_set.h +++ b/include/wlr/render/drm_format_set.h @@ -5,19 +5,55 @@ #include #include +/** A single DRM format, with a set of modifiers attached. */ struct wlr_drm_format { + // The actual DRM format, from `drm_fourcc.h` uint32_t format; - size_t len, cap; + // The number of modifiers + size_t len; + // The capacity of the array; do not use. + size_t capacity; + // The actual modifiers uint64_t modifiers[]; }; +/** + * A set of DRM formats and modifiers. + * + * This is used to describe the supported format + modifier combinations. For + * instance, backends will report the set they can display, and renderers will + * report the set they can render to. For a more general overview of formats + * and modifiers, see: + * https://lore.kernel.org/dri-devel/20210905122742.86029-1-daniels@collabora.com/ + * + * For compatibility with legacy drivers which don't support explicit + * modifiers, the special modifier DRM_FORMAT_MOD_INVALID is used to indicate + * that implicit modifiers are supported. Legacy drivers can also support the + * DRM_FORMAT_MOD_LINEAR modifier, which forces the buffer to have a linear + * layout. + * + * Users must not assume that implicit modifiers are supported unless INVALID + * is listed in the modifier list. + */ struct wlr_drm_format_set { - size_t len, cap; + // The number of formats + size_t len; + // The capacity of the array; private to wlroots + size_t capacity; + // A pointer to an array of `struct wlr_drm_format *` of length `len`. struct wlr_drm_format **formats; }; +/** + * Free all of the DRM formats in the set, making the set empty. Does not + * free the set itself. + */ void wlr_drm_format_set_finish(struct wlr_drm_format_set *set); +/** + * Return a pointer to a member of this `wlr_drm_format_set` of format + * `format`, or NULL if none exists. + */ const struct wlr_drm_format *wlr_drm_format_set_get( const struct wlr_drm_format_set *set, uint32_t format); @@ -27,4 +63,13 @@ 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); +/** + * Intersect two DRM format sets `a` and `b`, storing in the destination set + * `dst` the format + modifier pairs which are in both source sets. + * + * Returns false on failure or when the intersection is empty. + */ +bool wlr_drm_format_set_intersect(struct wlr_drm_format_set *dst, + const struct wlr_drm_format_set *a, const struct wlr_drm_format_set *b); + #endif diff --git a/include/wlr/render/egl.h b/include/wlr/render/egl.h index 903b70a3f..0d84958be 100644 --- a/include/wlr/render/egl.h +++ b/include/wlr/render/egl.h @@ -40,6 +40,7 @@ struct wlr_egl { bool KHR_image_base; bool EXT_image_dma_buf_import; bool EXT_image_dma_buf_import_modifiers; + bool IMG_context_priority; // Device extensions bool EXT_device_drm; @@ -64,10 +65,14 @@ struct wlr_egl { PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT; } procs; + bool has_modifiers; struct wlr_drm_format_set dmabuf_texture_formats; struct wlr_drm_format_set dmabuf_render_formats; }; +struct wlr_egl *wlr_egl_create_with_context(EGLDisplay display, + EGLContext context); + /** * Make the EGL context current. * diff --git a/include/wlr/render/gles2.h b/include/wlr/render/gles2.h index dabe49dde..e6844ce9d 100644 --- a/include/wlr/render/gles2.h +++ b/include/wlr/render/gles2.h @@ -30,7 +30,6 @@ struct wlr_gles2_texture_attribs { GLenum target; /* either GL_TEXTURE_2D or GL_TEXTURE_EXTERNAL_OES */ GLuint tex; - bool inverted_y; bool has_alpha; }; diff --git a/include/wlr/render/wlr_renderer.h b/include/wlr/render/wlr_renderer.h index bae2fa976..d8b04dc72 100644 --- a/include/wlr/render/wlr_renderer.h +++ b/include/wlr/render/wlr_renderer.h @@ -99,13 +99,19 @@ bool wlr_renderer_read_pixels(struct wlr_renderer *r, uint32_t fmt, uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y, void *data); /** - * Creates necessary shm and invokes the initialization of the implementation. + * Initializes wl_shm, linux-dmabuf and other buffer factory protocols. * * Returns false on failure. */ bool wlr_renderer_init_wl_display(struct wlr_renderer *r, struct wl_display *wl_display); +/** + * Initializes wl_shm on the provided wl_display. + */ +bool wlr_renderer_init_wl_shm(struct wlr_renderer *r, + struct wl_display *wl_display); + /** * Obtains the FD of the DRM device used for rendering, or -1 if unavailable. * diff --git a/include/wlr/render/wlr_texture.h b/include/wlr/render/wlr_texture.h index 3495c4439..1dbcba1de 100644 --- a/include/wlr/render/wlr_texture.h +++ b/include/wlr/render/wlr_texture.h @@ -33,16 +33,6 @@ struct wlr_texture *wlr_texture_from_pixels(struct wlr_renderer *renderer, uint32_t fmt, uint32_t stride, uint32_t width, uint32_t height, const void *data); -/** - * 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. * diff --git a/include/wlr/types/wlr_box.h b/include/wlr/types/wlr_box.h deleted file mode 100644 index 8720cef8f..000000000 --- a/include/wlr/types/wlr_box.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef WLR_TYPES_WLR_BOX_H -#define WLR_TYPES_WLR_BOX_H - -#warning "wlr_box has been moved to wlr/util/box.h" -#include - -#endif diff --git a/include/wlr/types/wlr_compositor.h b/include/wlr/types/wlr_compositor.h index 04391c741..071610b8f 100644 --- a/include/wlr/types/wlr_compositor.h +++ b/include/wlr/types/wlr_compositor.h @@ -9,21 +9,167 @@ #ifndef WLR_TYPES_WLR_COMPOSITOR_H #define WLR_TYPES_WLR_COMPOSITOR_H +#include +#include +#include +#include #include -#include +#include +#include +#include -struct wlr_surface; - -struct wlr_subcompositor { - struct wl_global *global; +enum wlr_surface_state_field { + WLR_SURFACE_STATE_BUFFER = 1 << 0, + WLR_SURFACE_STATE_SURFACE_DAMAGE = 1 << 1, + WLR_SURFACE_STATE_BUFFER_DAMAGE = 1 << 2, + WLR_SURFACE_STATE_OPAQUE_REGION = 1 << 3, + WLR_SURFACE_STATE_INPUT_REGION = 1 << 4, + WLR_SURFACE_STATE_TRANSFORM = 1 << 5, + WLR_SURFACE_STATE_SCALE = 1 << 6, + WLR_SURFACE_STATE_FRAME_CALLBACK_LIST = 1 << 7, + WLR_SURFACE_STATE_VIEWPORT = 1 << 8, }; +struct wlr_surface_state { + uint32_t committed; // enum wlr_surface_state_field + // Sequence number of the surface state. Incremented on each commit, may + // overflow. + uint32_t seq; + + struct wlr_buffer *buffer; + int32_t dx, dy; // relative to previous position + pixman_region32_t surface_damage, buffer_damage; // clipped to bounds + pixman_region32_t opaque, input; + enum wl_output_transform transform; + int32_t scale; + struct wl_list frame_callback_list; // wl_resource + + int width, height; // in surface-local coordinates + int buffer_width, buffer_height; + + struct wl_list subsurfaces_below; + struct wl_list subsurfaces_above; + + /** + * The viewport is applied after the surface transform and scale. + * + * If has_src is true, the surface content is cropped to the provided + * rectangle. If has_dst is true, the surface content is scaled to the + * provided rectangle. + */ + struct { + bool has_src, has_dst; + // In coordinates after scale/transform are applied, but before the + // destination rectangle is applied + struct wlr_fbox src; + int dst_width, dst_height; // in surface-local coordinates + } viewport; + + // Number of locks that prevent this surface state from being committed. + size_t cached_state_locks; + struct wl_list cached_state_link; // wlr_surface.cached +}; + +struct wlr_surface_role { + const char *name; + void (*commit)(struct wlr_surface *surface); + void (*precommit)(struct wlr_surface *surface, + const struct wlr_surface_state *state); +}; + +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; + /** + * The surface's buffer, if any. A surface has an attached buffer when it + * commits with a non-null buffer in its pending state. A surface will not + * have a buffer if it has never committed one, has committed a null buffer, + * or something went wrong with uploading the buffer. + */ + struct wlr_client_buffer *buffer; + /** + * The buffer position, in surface-local units. + */ + int sx, sy; + /** + * The last commit's buffer damage, in buffer-local coordinates. This + * contains both the damage accumulated by the client via + * `wlr_surface_state.surface_damage` and `wlr_surface_state.buffer_damage`. + * If the buffer has been resized, the whole buffer is damaged. + * + * This region needs to be scaled and transformed into output coordinates, + * just like the buffer's texture. In addition, if the buffer has shrunk the + * old size needs to be damaged and if the buffer has moved the old and new + * positions need to be damaged. + */ + pixman_region32_t buffer_damage; + /** + * The last commit's damage caused by surface and its subsurfaces' + * movement, in surface-local coordinates. + */ + pixman_region32_t external_damage; + /** + * The current opaque region, in surface-local coordinates. It is clipped to + * the surface bounds. If the surface's buffer is using a fully opaque + * format, this is set to the whole surface. + */ + pixman_region32_t opaque_region; + /** + * The current input region, in surface-local coordinates. It is clipped to + * the surface bounds. + */ + pixman_region32_t input_region; + /** + * `current` contains the current, committed surface state. `pending` + * accumulates state changes from the client between commits and shouldn't + * be accessed by the compositor directly. + */ + struct wlr_surface_state current, pending; + + struct wl_list cached; // wlr_surface_state.cached_link + + const struct wlr_surface_role *role; // the lifetime-bound role or NULL + void *role_data; // role-specific data + + struct { + struct wl_signal client_commit; + struct wl_signal commit; + struct wl_signal new_subsurface; + struct wl_signal destroy; + } events; + + struct wl_list current_outputs; // wlr_surface_output::link + + struct wlr_addon_set addons; + void *data; + + // private state + + struct wl_listener renderer_destroy; + + struct { + int32_t scale; + enum wl_output_transform transform; + int width, height; + int buffer_width, buffer_height; + } previous; +}; + +struct wlr_renderer; + struct wlr_compositor { struct wl_global *global; struct wlr_renderer *renderer; - struct wlr_subcompositor subcompositor; - struct wl_listener display_destroy; struct { @@ -32,16 +178,125 @@ struct wlr_compositor { } events; }; +typedef void (*wlr_surface_iterator_func_t)(struct wlr_surface *surface, + int sx, int sy, void *data); + +/** + * Set the lifetime role for this surface. Returns 0 on success or -1 if the + * role cannot be set. + */ +bool wlr_surface_set_role(struct wlr_surface *surface, + const struct wlr_surface_role *role, void *role_data, + struct wl_resource *error_resource, uint32_t error_code); + +/** + * Whether or not this surface currently has an attached buffer. A surface has + * an attached buffer when it commits with a non-null buffer in its pending + * state. A surface will not have a buffer if it has never committed one, has + * committed a null buffer, or something went wrong with uploading the buffer. + */ +bool wlr_surface_has_buffer(struct wlr_surface *surface); + +/** + * Get the texture of the buffer currently attached to this surface. Returns + * NULL if no buffer is currently attached or if something went wrong with + * uploading the buffer. + */ +struct wlr_texture *wlr_surface_get_texture(struct wlr_surface *surface); + +/** + * Get the root of the subsurface tree for this surface. Can return NULL if + * a surface in the tree has been destroyed. + */ +struct wlr_surface *wlr_surface_get_root_surface(struct wlr_surface *surface); + +/** + * Check if the surface accepts input events at the given surface-local + * coordinates. Does not check the surface's subsurfaces. + */ +bool wlr_surface_point_accepts_input(struct wlr_surface *surface, + double sx, double sy); + +/** + * Find a surface in this surface's tree that accepts input events and has all + * parents mapped (except this surface, which can be unmapped) 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_surface_surface_at(struct wlr_surface *surface, + double sx, double sy, double *sub_x, double *sub_y); + +void wlr_surface_send_enter(struct wlr_surface *surface, + struct wlr_output *output); + +void wlr_surface_send_leave(struct wlr_surface *surface, + struct wlr_output *output); + +void wlr_surface_send_frame_done(struct wlr_surface *surface, + const struct timespec *when); + +/** + * Get the bounding box that contains the surface and all subsurfaces in + * surface coordinates. + * X and y may be negative, if there are subsurfaces with negative position. + */ +void wlr_surface_get_extends(struct wlr_surface *surface, struct wlr_box *box); + +/** + * Get the wlr_surface corresponding to a wl_surface resource. This asserts + * that the resource is a valid wl_surface resource created by wlroots and + * will never return NULL. + */ +struct wlr_surface *wlr_surface_from_resource(struct wl_resource *resource); + +/** + * Call `iterator` on each mapped surface in the surface tree (whether or not + * this surface is mapped), with the surface's position relative to the root + * surface. The function is called from root to leaves (in rendering order). + */ +void wlr_surface_for_each_surface(struct wlr_surface *surface, + wlr_surface_iterator_func_t iterator, void *user_data); + +/** + * Get the effective surface damage in surface-local coordinate space. Besides + * buffer damage, this includes damage induced by resizing and moving the + * surface and its subsurfaces. The resulting damage is not expected to be + * bounded by the surface itself. + */ +void wlr_surface_get_effective_damage(struct wlr_surface *surface, + pixman_region32_t *damage); + +/** + * Get the source rectangle describing the region of the buffer that needs to + * be sampled to render this surface's current state. The box is in + * buffer-local coordinates. + * + * If the viewport's source rectangle is unset, the position is zero and the + * size is the buffer's. + */ +void wlr_surface_get_buffer_source_box(struct wlr_surface *surface, + struct wlr_fbox *box); + +/** + * Acquire a lock for the pending surface state. + * + * The state won't be committed before the caller releases the lock. Instead, + * the state becomes cached. The caller needs to use wlr_surface_unlock_cached + * to release the lock. + * + * Returns a surface commit sequence number for the cached state. + */ +uint32_t wlr_surface_lock_pending(struct wlr_surface *surface); + +/** + * Release a lock for a cached state. + * + * Callers should not assume that the cached state will immediately be + * committed. Another caller may still have an active lock. + */ +void wlr_surface_unlock_cached(struct wlr_surface *surface, uint32_t seq); + struct wlr_compositor *wlr_compositor_create(struct wl_display *display, struct wlr_renderer *renderer); -bool wlr_surface_is_subsurface(struct wlr_surface *surface); - -/** - * Get a subsurface from a surface. Can return NULL if the subsurface has been - * destroyed. - */ -struct wlr_subsurface *wlr_subsurface_from_wlr_surface( - struct wlr_surface *surface); - #endif diff --git a/include/wlr/types/wlr_cursor.h b/include/wlr/types/wlr_cursor.h index 739549c33..7dd73c48e 100644 --- a/include/wlr/types/wlr_cursor.h +++ b/include/wlr/types/wlr_cursor.h @@ -192,13 +192,13 @@ void wlr_cursor_map_input_to_output(struct wlr_cursor *cur, /** * Maps this cursor to an arbitrary region on the associated wlr_output_layout. */ -void wlr_cursor_map_to_region(struct wlr_cursor *cur, struct wlr_box *box); +void wlr_cursor_map_to_region(struct wlr_cursor *cur, const struct wlr_box *box); /** * Maps inputs from this input device to an arbitrary region on the associated * wlr_output_layout. */ void wlr_cursor_map_input_to_region(struct wlr_cursor *cur, - struct wlr_input_device *dev, struct wlr_box *box); + struct wlr_input_device *dev, const struct wlr_box *box); #endif diff --git a/include/wlr/types/wlr_drm_lease_v1.h b/include/wlr/types/wlr_drm_lease_v1.h index bf625235d..5a7827ea3 100644 --- a/include/wlr/types/wlr_drm_lease_v1.h +++ b/include/wlr/types/wlr_drm_lease_v1.h @@ -81,16 +81,17 @@ struct wlr_drm_lease_request_v1 { struct wlr_drm_lease_v1 { struct wl_resource *resource; + struct wlr_drm_lease *drm_lease; struct wlr_drm_lease_device_v1 *device; struct wlr_drm_lease_connector_v1 **connectors; size_t n_connectors; - uint32_t lessee_id; - struct wl_list link; // wlr_drm_lease_device_v1::leases + struct wl_listener destroy; + void *data; }; diff --git a/include/wlr/types/wlr_foreign_toplevel_management_v1.h b/include/wlr/types/wlr_foreign_toplevel_management_v1.h index 6ea9d47fd..d9030b2ea 100644 --- a/include/wlr/types/wlr_foreign_toplevel_management_v1.h +++ b/include/wlr/types/wlr_foreign_toplevel_management_v1.h @@ -36,10 +36,13 @@ enum wlr_foreign_toplevel_handle_v1_state { struct wlr_foreign_toplevel_handle_v1_output { struct wl_list link; // wlr_foreign_toplevel_handle_v1::outputs - struct wl_listener output_destroy; struct wlr_output *output; - struct wlr_foreign_toplevel_handle_v1 *toplevel; + + // private state + + struct wl_listener output_bind; + struct wl_listener output_destroy; }; struct wlr_foreign_toplevel_handle_v1 { diff --git a/include/wlr/types/wlr_input_device.h b/include/wlr/types/wlr_input_device.h index 8ed9b4659..1690a5b92 100644 --- a/include/wlr/types/wlr_input_device.h +++ b/include/wlr/types/wlr_input_device.h @@ -28,8 +28,6 @@ enum wlr_input_device_type { struct wlr_input_device_impl; struct wlr_input_device { - const struct wlr_input_device_impl *impl; - enum wlr_input_device_type type; unsigned int vendor, product; char *name; @@ -53,8 +51,21 @@ struct wlr_input_device { } events; void *data; - - struct wl_list link; }; +void wlr_input_device_init(struct wlr_input_device *wlr_device, + enum wlr_input_device_type type, const char *name); + +/** + * Clean up all of the provided wlr_input_device resources + */ +void wlr_input_device_finish(struct wlr_input_device *wlr_device); + +/** + * Calls the specialized input device destroy function. + * If the wlr_input_device is not owned by a specialized input device, the + * function will finish the wlr_input_device and free it. + */ +void wlr_input_device_destroy(struct wlr_input_device *dev); + #endif diff --git a/include/wlr/types/wlr_keyboard.h b/include/wlr/types/wlr_keyboard.h index ea16c4320..b3be053a5 100644 --- a/include/wlr/types/wlr_keyboard.h +++ b/include/wlr/types/wlr_keyboard.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #define WLR_LED_COUNT 3 @@ -48,6 +49,8 @@ struct wlr_keyboard_modifiers { }; struct wlr_keyboard { + struct wlr_input_device base; + const struct wlr_keyboard_impl *impl; struct wlr_keyboard_group *group; diff --git a/include/wlr/types/wlr_keyboard_group.h b/include/wlr/types/wlr_keyboard_group.h index cb73899a7..3906d9489 100644 --- a/include/wlr/types/wlr_keyboard_group.h +++ b/include/wlr/types/wlr_keyboard_group.h @@ -11,11 +11,9 @@ #include #include -#include struct wlr_keyboard_group { struct wlr_keyboard keyboard; - struct wlr_input_device *input_device; struct wl_list devices; // keyboard_group_device::link struct wl_list keys; // keyboard_group_key::link diff --git a/include/wlr/types/wlr_layer_shell_v1.h b/include/wlr/types/wlr_layer_shell_v1.h index 694fb3b29..370fad837 100644 --- a/include/wlr/types/wlr_layer_shell_v1.h +++ b/include/wlr/types/wlr_layer_shell_v1.h @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include "wlr-layer-shell-unstable-v1-protocol.h" /** @@ -58,7 +58,7 @@ struct wlr_layer_surface_v1_state { uint32_t anchor; int32_t exclusive_zone; struct { - uint32_t top, right, bottom, left; + int32_t top, right, bottom, left; } margin; enum zwlr_layer_surface_v1_keyboard_interactivity keyboard_interactive; uint32_t desired_width, desired_height; diff --git a/include/wlr/types/wlr_linux_dmabuf_v1.h b/include/wlr/types/wlr_linux_dmabuf_v1.h index a2db7fea3..df89a1219 100644 --- a/include/wlr/types/wlr_linux_dmabuf_v1.h +++ b/include/wlr/types/wlr_linux_dmabuf_v1.h @@ -10,16 +10,21 @@ #define WLR_TYPES_WLR_LINUX_DMABUF_H #include +#include #include #include #include +struct wlr_surface; + struct wlr_dmabuf_v1_buffer { struct wlr_buffer base; struct wl_resource *resource; // can be NULL if the client destroyed it struct wlr_dmabuf_attributes attributes; + // private state + struct wl_listener release; }; @@ -36,11 +41,16 @@ bool wlr_dmabuf_v1_resource_is_buffer(struct wl_resource *buffer_resource); struct wlr_dmabuf_v1_buffer *wlr_dmabuf_v1_buffer_from_buffer_resource( struct wl_resource *buffer_resource); -struct wlr_linux_buffer_params_v1 { - struct wl_resource *resource; - struct wlr_linux_dmabuf_v1 *linux_dmabuf; - struct wlr_dmabuf_attributes attributes; - bool has_modifier; +struct wlr_linux_dmabuf_feedback_v1 { + dev_t main_device; + size_t tranches_len; + const struct wlr_linux_dmabuf_feedback_v1_tranche *tranches; +}; + +struct wlr_linux_dmabuf_feedback_v1_tranche { + dev_t target_device; + uint32_t flags; // bitfield of enum zwp_linux_dmabuf_feedback_v1_tranche_flags + const struct wlr_drm_format_set *formats; }; /* the protocol interface */ @@ -52,6 +62,11 @@ struct wlr_linux_dmabuf_v1 { struct wl_signal destroy; } events; + // private state + + struct wlr_linux_dmabuf_feedback_v1_compiled *default_feedback; + struct wl_list surfaces; // wlr_linux_dmabuf_v1_surface.link + struct wl_listener display_destroy; struct wl_listener renderer_destroy; }; @@ -62,4 +77,13 @@ struct wlr_linux_dmabuf_v1 { struct wlr_linux_dmabuf_v1 *wlr_linux_dmabuf_v1_create(struct wl_display *display, struct wlr_renderer *renderer); +/** + * Set a surface's DMA-BUF feedback. + * + * Passing a NULL feedback resets it to the default feedback. + */ +bool wlr_linux_dmabuf_v1_set_surface_feedback( + struct wlr_linux_dmabuf_v1 *linux_dmabuf, struct wlr_surface *surface, + const struct wlr_linux_dmabuf_feedback_v1 *feedback); + #endif diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h index 78647cb97..e8399b40d 100644 --- a/include/wlr/types/wlr_output.h +++ b/include/wlr/types/wlr_output.h @@ -73,7 +73,8 @@ enum wlr_output_state_field { WLR_OUTPUT_STATE_TRANSFORM = 1 << 5, WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED = 1 << 6, WLR_OUTPUT_STATE_GAMMA_LUT = 1 << 7, - WLR_OUTPUT_STATE_SOURCE_BOX = 1 << 8, + WLR_OUTPUT_STATE_RENDER_FORMAT = 1 << 8, + WLR_OUTPUT_STATE_SOURCE_BOX = 1 << 9, }; enum wlr_output_state_mode_type { @@ -94,6 +95,7 @@ struct wlr_output_state { /* allow partial buffer scanout for tiling displays * only valid if WLR_OUTPUT_STATE_SOURCE_BOX */ struct wlr_box source_box; // source box for respective output + uint32_t render_format; // only valid if WLR_OUTPUT_STATE_BUFFER struct wlr_buffer *buffer; @@ -132,7 +134,7 @@ struct wlr_output { struct wl_global *global; struct wl_list resources; - char name[24]; + char *name; char *description; // may be NULL char make[56]; char model[16]; @@ -151,6 +153,7 @@ struct wlr_output { enum wl_output_subpixel subpixel; enum wl_output_transform transform; enum wlr_output_adaptive_sync_status adaptive_sync_status; + uint32_t render_format; bool needs_frame; // damage for cursors and fullscreen surface, in output-local coordinates @@ -199,8 +202,10 @@ struct wlr_output { struct wlr_buffer *cursor_front_buffer; int software_cursor_locks; // number of locks forcing software cursors + struct wlr_allocator *allocator; + struct wlr_renderer *renderer; struct wlr_swapchain *swapchain; - struct wlr_buffer *back_buffer, *front_buffer; + struct wlr_buffer *back_buffer; struct wl_listener display_destroy; @@ -223,6 +228,7 @@ struct wlr_output_event_commit { struct wlr_output *output; uint32_t committed; // bitmask of enum wlr_output_state_field struct timespec *when; + struct wlr_buffer *buffer; // NULL if no buffer is committed }; enum wlr_output_present_flag { @@ -273,6 +279,18 @@ struct wlr_surface; void wlr_output_enable(struct wlr_output *output, bool enable); void wlr_output_create_global(struct wlr_output *output); void wlr_output_destroy_global(struct wlr_output *output); +/** + * Initialize the output's rendering subsystem with the provided allocator and + * renderer. Can only be called once. + * + * Call this function prior to any call to wlr_output_attach_render, + * wlr_output_commit or wlr_output_cursor_create. + * + * The buffer capabilities of the provided must match the capabilities of the + * output's backend. Returns false otherwise. + */ +bool wlr_output_init_render(struct wlr_output *output, + struct wlr_allocator *allocator, struct wlr_renderer *renderer); /** * Returns the preferred mode for this output. If the output doesn't support * modes, returns NULL. @@ -311,6 +329,22 @@ void wlr_output_set_transform(struct wlr_output *output, * Adaptive sync is double-buffered state, see `wlr_output_commit`. */ void wlr_output_enable_adaptive_sync(struct wlr_output *output, bool enabled); +/** + * Set the output buffer render format. Default value: DRM_FORMAT_XRGB8888 + * + * While high bit depth render formats are necessary for a monitor to receive + * useful high bit data, they do not guarantee it; a DRM_FORMAT_XBGR2101010 + * buffer will only lead to sending 10-bpc image data to the monitor if + * hardware and software permit this. + * + * This only affects the format of the output buffer used when rendering, + * as with `wlr_output_attach_render`. It has no impact on the cursor buffer + * format, or on the formats supported for direct scan-out (see also + * `wlr_output_attach_buffer`). + * + * This format is double-buffered state, see `wlr_output_commit`. + */ +void wlr_output_set_render_format(struct wlr_output *output, uint32_t format); /** * Sets a scale for the output. * @@ -319,6 +353,17 @@ void wlr_output_enable_adaptive_sync(struct wlr_output *output, bool enabled); void wlr_output_set_scale(struct wlr_output *output, float scale); void wlr_output_set_subpixel(struct wlr_output *output, enum wl_output_subpixel subpixel); +/** + * Set the output name. + * + * Output names are subject to the following rules: + * + * - Each output name must be unique. + * - The name cannot change after the output has been advertised to clients. + * + * For more details, see the protocol documentation for wl_output.name. + */ +void wlr_output_set_name(struct wlr_output *output, const char *name); void wlr_output_set_description(struct wlr_output *output, const char *desc); /** * Schedule a done event. @@ -452,6 +497,16 @@ void wlr_output_lock_software_cursors(struct wlr_output *output, bool lock); */ void wlr_output_render_software_cursors(struct wlr_output *output, pixman_region32_t *damage); +/** + * Get the set of DRM formats suitable for the primary buffer, assuming a + * buffer with the specified capabilities. + * + * NULL is returned if the backend doesn't have any format constraint, ie. all + * formats are supported. An empty set is returned if the backend doesn't + * support any format. + */ +const struct wlr_drm_format_set *wlr_output_get_primary_formats( + struct wlr_output *output, uint32_t buffer_caps); struct wlr_output_cursor *wlr_output_cursor_create(struct wlr_output *output); diff --git a/include/wlr/types/wlr_output_layout.h b/include/wlr/types/wlr_output_layout.h index abbe4884e..3cb6e1e2d 100644 --- a/include/wlr/types/wlr_output_layout.h +++ b/include/wlr/types/wlr_output_layout.h @@ -15,7 +15,6 @@ #include struct wlr_box; -struct wlr_output_layout_state; /** * Helper to arrange outputs in a 2D coordinate space. The output effective @@ -27,7 +26,6 @@ struct wlr_output_layout_state; */ struct wlr_output_layout { struct wl_list outputs; - struct wlr_output_layout_state *state; struct { struct wl_signal add; @@ -107,10 +105,10 @@ void wlr_output_layout_closest_point(struct wlr_output_layout *layout, /** * Get the box of the layout for the given reference output in layout * coordinates. If `reference` is NULL, the box will be for the extents of the - * entire layout. + * entire layout. If the output isn't in the layout, the box will be empty. */ -struct wlr_box *wlr_output_layout_get_box( - struct wlr_output_layout *layout, struct wlr_output *reference); +void wlr_output_layout_get_box(struct wlr_output_layout *layout, + struct wlr_output *reference, struct wlr_box *dest_box); /** * Add an auto configured output to the layout. This will place the output in a diff --git a/include/wlr/types/wlr_pointer.h b/include/wlr/types/wlr_pointer.h index bcfb2ad3d..a58507189 100644 --- a/include/wlr/types/wlr_pointer.h +++ b/include/wlr/types/wlr_pointer.h @@ -17,6 +17,8 @@ struct wlr_pointer_impl; struct wlr_pointer { + struct wlr_input_device base; + const struct wlr_pointer_impl *impl; struct { diff --git a/include/wlr/types/wlr_pointer_gestures_v1.h b/include/wlr/types/wlr_pointer_gestures_v1.h index 0d91a517d..b05b1a30f 100644 --- a/include/wlr/types/wlr_pointer_gestures_v1.h +++ b/include/wlr/types/wlr_pointer_gestures_v1.h @@ -11,7 +11,8 @@ #include #include -#include + +struct wlr_surface; struct wlr_pointer_gestures_v1 { struct wl_global *global; diff --git a/include/wlr/types/wlr_scene.h b/include/wlr/types/wlr_scene.h index 538b89412..867753996 100644 --- a/include/wlr/types/wlr_scene.h +++ b/include/wlr/types/wlr_scene.h @@ -21,10 +21,12 @@ #include #include -#include +#include struct wlr_output; struct wlr_output_layout; +struct wlr_xdg_surface; +struct wlr_layer_surface_v1; enum wlr_scene_node_type { WLR_SCENE_NODE_ROOT, @@ -61,6 +63,15 @@ struct wlr_scene { struct wlr_scene_node node; struct wl_list outputs; // wlr_scene_output.link + + // private state + + // May be NULL + struct wlr_presentation *presentation; + struct wl_listener presentation_destroy; + + // List of buffers which need to be imported as textures + struct wl_list pending_buffers; // wlr_scene_buffer.pending_link }; /** A sub-tree in the scene-graph. */ @@ -73,8 +84,18 @@ struct wlr_scene_surface { struct wlr_scene_node node; struct wlr_surface *surface; + /** + * The output that the largest area of this surface is displayed on. + * This may be NULL if the surface is not currently displayed on any + * outputs. This is the output that should be used for frame callbacks, + * presentation feedback, etc. + */ + struct wlr_output *primary_output; + // private state + int prev_width, prev_height; + struct wl_listener surface_destroy; struct wl_listener surface_commit; }; @@ -97,6 +118,7 @@ struct wlr_scene_buffer { struct wlr_fbox src_box; int dst_width, dst_height; enum wl_output_transform transform; + struct wl_list pending_link; // wlr_scene.pending_buffers }; /** A viewport for an output in the scene-graph */ @@ -115,6 +137,19 @@ struct wlr_scene_output { bool prev_scanout; }; +/** A layer shell scene helper */ +struct wlr_scene_layer_surface_v1 { + struct wlr_scene_node *node; + struct wlr_layer_surface_v1 *layer_surface; + + // private state + + struct wl_listener tree_destroy; + struct wl_listener layer_surface_destroy; + struct wl_listener layer_surface_map; + struct wl_listener layer_surface_unmap; +}; + typedef void (*wlr_scene_node_iterator_func_t)(struct wlr_scene_node *node, int sx, int sy, void *data); @@ -190,6 +225,14 @@ struct wlr_scene *wlr_scene_create(void); */ void wlr_scene_render_output(struct wlr_scene *scene, struct wlr_output *output, int lx, int ly, pixman_region32_t *damage); +/** + * Handle presentation feedback for all surfaces in the scene, assuming that + * scene outputs and the scene rendering functions are used. + * + * Asserts that a wlr_presentation hasn't already been set for the scene. + */ +void wlr_scene_set_presentation(struct wlr_scene *scene, + struct wlr_presentation *presentation); /** * Add a node displaying nothing but its children. @@ -200,6 +243,10 @@ struct wlr_scene_tree *wlr_scene_tree_create(struct wlr_scene_node *parent); * Add a node displaying a single surface to the scene-graph. * * The child sub-surfaces are ignored. + * + * wlr_surface_send_enter()/wlr_surface_send_leave() will be called + * automatically based on the position of the surface and outputs in + * the scene. */ struct wlr_scene_surface *wlr_scene_surface_create(struct wlr_scene_node *parent, struct wlr_surface *surface); @@ -273,7 +320,13 @@ void wlr_scene_output_set_position(struct wlr_scene_output *scene_output, * Render and commit an output. */ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output); - +/** + * Call wlr_surface_send_frame_done() on all surfaces in the scene rendered by + * wlr_scene_output_commit() for which wlr_scene_surface->primary_output + * matches the given scene_output. + */ +void wlr_scene_output_send_frame_done(struct wlr_scene_output *scene_output, + struct timespec *now); /** * Call `iterator` on each surface in the scene-graph visible on the output, * with the surface's position in layout coordinates. The function is called @@ -281,6 +334,13 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output); */ void wlr_scene_output_for_each_surface(struct wlr_scene_output *scene_output, wlr_surface_iterator_func_t iterator, void *user_data); +/** + * Get a scene-graph output from a wlr_output. + * + * If the output hasn't been added to the scene-graph, returns NULL. + */ +struct wlr_scene_output *wlr_scene_get_scene_output(struct wlr_scene *scene, + struct wlr_output *output); /** * Attach an output layout to a scene. @@ -298,4 +358,38 @@ bool wlr_scene_attach_output_layout(struct wlr_scene *scene, struct wlr_scene_node *wlr_scene_subsurface_tree_create( struct wlr_scene_node *parent, struct wlr_surface *surface); +/** + * Add a node displaying an xdg_surface and all of its sub-surfaces to the + * scene-graph. + * + * The origin of the returned scene-graph node will match the top-left corner + * of the xdg_surface window geometry. + */ +struct wlr_scene_node *wlr_scene_xdg_surface_create( + struct wlr_scene_node *parent, struct wlr_xdg_surface *xdg_surface); + +/** + * Add a node displaying a layer_surface_v1 and all of its sub-surfaces to the + * scene-graph. + * + * The origin of the returned scene-graph node will match the top-left corner + * of the layer surface. + */ +struct wlr_scene_layer_surface_v1 *wlr_scene_layer_surface_v1_create( + struct wlr_scene_node *parent, struct wlr_layer_surface_v1 *layer_surface); + +/** + * Configure a layer_surface_v1, position its scene node in accordance to its + * current state, and update the remaining usable area. + * + * full_area represents the entire area that may be used by the layer surface + * if its exclusive_zone is -1, and is usually the output dimensions. + * usable_area represents what remains of full_area that can be used if + * exclusive_zone is >= 0. usable_area is updated if the surface has a positive + * exclusive_zone, so that it can be used for the next layer surface. + */ +void wlr_scene_layer_surface_v1_configure( + struct wlr_scene_layer_surface_v1 *scene_layer_surface, + const struct wlr_box *full_area, struct wlr_box *usable_area); + #endif diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h index 631b7dd3d..41f3918b0 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -14,7 +14,8 @@ #include #include #include -#include + +struct wlr_surface; #define WLR_SERIAL_RINGSET_SIZE 128 diff --git a/include/wlr/types/wlr_session_lock_v1.h b/include/wlr/types/wlr_session_lock_v1.h new file mode 100644 index 000000000..b410b9e56 --- /dev/null +++ b/include/wlr/types/wlr_session_lock_v1.h @@ -0,0 +1,98 @@ +/* + * 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_SESSION_LOCK_H +#define WLR_TYPES_WLR_SESSION_LOCK_H + +#include +#include +#include + +struct wlr_session_lock_manager_v1 { + struct wl_global *global; + + struct { + struct wl_signal new_lock; // struct wlr_session_lock_v1 * + struct wl_signal destroy; + } events; + + void *data; + + // private state + + struct wl_listener display_destroy; +}; + +struct wlr_session_lock_v1 { + struct wl_resource *resource; + + struct wl_list surfaces; // struct wlr_session_lock_surface_v1::link + + struct { + struct wl_signal new_surface; // struct wlr_session_lock_surface_v1 * + struct wl_signal unlock; + struct wl_signal destroy; + } events; + + void *data; +}; + +struct wlr_session_lock_surface_v1_state { + uint32_t width, height; + uint32_t configure_serial; +}; + +struct wlr_session_lock_surface_v1_configure { + struct wl_list link; // wlr_session_lock_surface_v1::configure_list + uint32_t serial; + + uint32_t width, height; +}; + +struct wlr_session_lock_surface_v1 { + struct wl_resource *resource; + struct wl_list link; // wlr_session_lock_v1::surfaces + + struct wlr_output *output; + struct wlr_surface *surface; + + bool configured, mapped; + + struct wl_list configure_list; // wlr_session_lock_surface_v1_configure::link + + struct wlr_session_lock_surface_v1_state current; + struct wlr_session_lock_surface_v1_state pending; + + struct { + struct wl_signal map; + struct wl_signal destroy; + } events; + + void *data; + + // private state + + struct wl_listener output_destroy; + struct wl_listener surface_destroy; +}; + +struct wlr_session_lock_manager_v1 *wlr_session_lock_manager_v1_create( + struct wl_display *display); + +void wlr_session_lock_v1_send_locked(struct wlr_session_lock_v1 *lock); +void wlr_session_lock_v1_destroy(struct wlr_session_lock_v1 *lock); + +uint32_t wlr_session_lock_surface_v1_configure( + struct wlr_session_lock_surface_v1 *lock_surface, + uint32_t width, uint32_t height); + +bool wlr_surface_is_session_lock_surface_v1(struct wlr_surface *surface); +struct wlr_session_lock_surface_v1 *wlr_session_lock_surface_v1_from_wlr_surface( + struct wlr_surface *surface); + +#endif diff --git a/include/wlr/types/wlr_subcompositor.h b/include/wlr/types/wlr_subcompositor.h new file mode 100644 index 000000000..d8e57ed83 --- /dev/null +++ b/include/wlr/types/wlr_subcompositor.h @@ -0,0 +1,77 @@ +/* + * 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_SUBCOMPOSITOR_H +#define WLR_TYPES_WLR_SUBCOMPOSITOR_H + +#include +#include +#include + +struct wlr_surface; + +/** + * The sub-surface state describing the sub-surface's relationship with its + * parent. Contrary to other states, this one is not applied on surface commit. + * Instead, it's applied on parent surface commit. + */ +struct wlr_subsurface_parent_state { + int32_t x, y; + struct wl_list link; +}; + +struct wlr_subsurface { + struct wl_resource *resource; + struct wlr_surface *surface; + struct wlr_surface *parent; + + struct wlr_subsurface_parent_state current, pending; + + uint32_t cached_seq; + bool has_cache; + + bool synchronized; + bool reordered; + bool mapped; + bool added; + + struct wl_listener surface_destroy; + struct wl_listener surface_client_commit; + struct wl_listener parent_destroy; + + struct { + struct wl_signal destroy; + struct wl_signal map; + struct wl_signal unmap; + } events; + + void *data; +}; + +struct wlr_subcompositor { + struct wl_global *global; + + struct wl_listener display_destroy; + + struct { + struct wl_signal destroy; + } events; +}; + +bool wlr_surface_is_subsurface(struct wlr_surface *surface); + +/** + * Get a subsurface from a surface. Can return NULL if the subsurface has been + * destroyed. + */ +struct wlr_subsurface *wlr_subsurface_from_wlr_surface( + struct wlr_surface *surface); + +struct wlr_subcompositor *wlr_subcompositor_create(struct wl_display *display); + +#endif diff --git a/include/wlr/types/wlr_surface.h b/include/wlr/types/wlr_surface.h index ae3ae80f6..9612dc085 100644 --- a/include/wlr/types/wlr_surface.h +++ b/include/wlr/types/wlr_surface.h @@ -1,313 +1,4 @@ -/* - * 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 +#warning "wlr/types/wlr_surface.h has been deprecated and will be removed in the future. Use wlr/types/wlr_compositor.h and wlr/types/wlr_subcompositor.h." -#ifndef WLR_TYPES_WLR_SURFACE_H -#define WLR_TYPES_WLR_SURFACE_H - -#include -#include -#include -#include -#include -#include -#include -#include - -enum wlr_surface_state_field { - WLR_SURFACE_STATE_BUFFER = 1 << 0, - WLR_SURFACE_STATE_SURFACE_DAMAGE = 1 << 1, - WLR_SURFACE_STATE_BUFFER_DAMAGE = 1 << 2, - WLR_SURFACE_STATE_OPAQUE_REGION = 1 << 3, - WLR_SURFACE_STATE_INPUT_REGION = 1 << 4, - WLR_SURFACE_STATE_TRANSFORM = 1 << 5, - WLR_SURFACE_STATE_SCALE = 1 << 6, - WLR_SURFACE_STATE_FRAME_CALLBACK_LIST = 1 << 7, - WLR_SURFACE_STATE_VIEWPORT = 1 << 8, -}; - -struct wlr_surface_state { - uint32_t committed; // enum wlr_surface_state_field - // Sequence number of the surface state. Incremented on each commit, may - // overflow. - uint32_t seq; - - struct wlr_buffer *buffer; - int32_t dx, dy; // relative to previous position - pixman_region32_t surface_damage, buffer_damage; // clipped to bounds - pixman_region32_t opaque, input; - enum wl_output_transform transform; - int32_t scale; - struct wl_list frame_callback_list; // wl_resource - - int width, height; // in surface-local coordinates - int buffer_width, buffer_height; - - struct wl_list subsurfaces_below; - struct wl_list subsurfaces_above; - - /** - * The viewport is applied after the surface transform and scale. - * - * If has_src is true, the surface content is cropped to the provided - * rectangle. If has_dst is true, the surface content is scaled to the - * provided rectangle. - */ - struct { - bool has_src, has_dst; - // In coordinates after scale/transform are applied, but before the - // destination rectangle is applied - struct wlr_fbox src; - int dst_width, dst_height; // in surface-local coordinates - } viewport; - - // Number of locks that prevent this surface state from being committed. - size_t cached_state_locks; - struct wl_list cached_state_link; // wlr_surface.cached -}; - -struct wlr_surface_role { - const char *name; - void (*commit)(struct wlr_surface *surface); - 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; - /** - * The surface's buffer, if any. A surface has an attached buffer when it - * commits with a non-null buffer in its pending state. A surface will not - * have a buffer if it has never committed one, has committed a null buffer, - * or something went wrong with uploading the buffer. - */ - struct wlr_client_buffer *buffer; - /** - * The buffer position, in surface-local units. - */ - int sx, sy; - /** - * The last commit's buffer damage, in buffer-local coordinates. This - * contains both the damage accumulated by the client via - * `wlr_surface_state.surface_damage` and `wlr_surface_state.buffer_damage`. - * If the buffer has been resized, the whole buffer is damaged. - * - * This region needs to be scaled and transformed into output coordinates, - * just like the buffer's texture. In addition, if the buffer has shrunk the - * old size needs to be damaged and if the buffer has moved the old and new - * positions need to be damaged. - */ - pixman_region32_t buffer_damage; - /** - * The current opaque region, in surface-local coordinates. It is clipped to - * the surface bounds. If the surface's buffer is using a fully opaque - * format, this is set to the whole surface. - */ - pixman_region32_t opaque_region; - /** - * The current input region, in surface-local coordinates. It is clipped to - * the surface bounds. - */ - pixman_region32_t input_region; - /** - * `current` contains the current, committed surface state. `pending` - * accumulates state changes from the client between commits and shouldn't - * be accessed by the compositor directly. - */ - struct wlr_surface_state current, pending; - - struct wl_list cached; // wlr_surface_state.cached_link - - const struct wlr_surface_role *role; // the lifetime-bound role or NULL - void *role_data; // role-specific data - - struct { - struct wl_signal commit; - struct wl_signal new_subsurface; - struct wl_signal destroy; - } events; - - struct wl_list current_outputs; // wlr_surface_output::link - - struct wlr_addon_set addons; - void *data; - - // private state - - struct wl_listener renderer_destroy; - - struct { - int32_t scale; - enum wl_output_transform transform; - int width, height; - int buffer_width, buffer_height; - } previous; -}; - -/** - * The sub-surface state describing the sub-surface's relationship with its - * parent. Contrary to other states, this one is not applied on surface commit. - * Instead, it's applied on parent surface commit. - */ -struct wlr_subsurface_parent_state { - int32_t x, y; - struct wl_list link; -}; - -struct wlr_subsurface { - struct wl_resource *resource; - struct wlr_surface *surface; - struct wlr_surface *parent; - - struct wlr_subsurface_parent_state current, pending; - - uint32_t cached_seq; - bool has_cache; - - bool synchronized; - bool reordered; - bool mapped; - - struct wl_listener surface_destroy; - struct wl_listener parent_destroy; - - struct { - struct wl_signal destroy; - struct wl_signal map; - struct wl_signal unmap; - } events; - - void *data; -}; - -typedef void (*wlr_surface_iterator_func_t)(struct wlr_surface *surface, - int sx, int sy, void *data); - -/** - * Set the lifetime role for this surface. Returns 0 on success or -1 if the - * role cannot be set. - */ -bool wlr_surface_set_role(struct wlr_surface *surface, - const struct wlr_surface_role *role, void *role_data, - struct wl_resource *error_resource, uint32_t error_code); - -/** - * Whether or not this surface currently has an attached buffer. A surface has - * an attached buffer when it commits with a non-null buffer in its pending - * state. A surface will not have a buffer if it has never committed one, has - * committed a null buffer, or something went wrong with uploading the buffer. - */ -bool wlr_surface_has_buffer(struct wlr_surface *surface); - -/** - * Get the texture of the buffer currently attached to this surface. Returns - * NULL if no buffer is currently attached or if something went wrong with - * uploading the buffer. - */ -struct wlr_texture *wlr_surface_get_texture(struct wlr_surface *surface); - -/** - * Get the root of the subsurface tree for this surface. Can return NULL if - * a surface in the tree has been destroyed. - */ -struct wlr_surface *wlr_surface_get_root_surface(struct wlr_surface *surface); - -/** - * Check if the surface accepts input events at the given surface-local - * coordinates. Does not check the surface's subsurfaces. - */ -bool wlr_surface_point_accepts_input(struct wlr_surface *surface, - double sx, double sy); - -/** - * Find a surface in this surface's tree that accepts input events and has all - * parents mapped (except this surface, which can be unmapped) 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_surface_surface_at(struct wlr_surface *surface, - double sx, double sy, double *sub_x, double *sub_y); - -void wlr_surface_send_enter(struct wlr_surface *surface, - struct wlr_output *output); - -void wlr_surface_send_leave(struct wlr_surface *surface, - struct wlr_output *output); - -void wlr_surface_send_frame_done(struct wlr_surface *surface, - const struct timespec *when); - -/** - * Get the bounding box that contains the surface and all subsurfaces in - * surface coordinates. - * X and y may be negative, if there are subsurfaces with negative position. - */ -void wlr_surface_get_extends(struct wlr_surface *surface, struct wlr_box *box); - -/** - * Get the wlr_surface corresponding to a wl_surface resource. This asserts - * that the resource is a valid wl_surface resource created by wlroots and - * will never return NULL. - */ -struct wlr_surface *wlr_surface_from_resource(struct wl_resource *resource); - -/** - * Call `iterator` on each mapped surface in the surface tree (whether or not - * this surface is mapped), with the surface's position relative to the root - * surface. The function is called from root to leaves (in rendering order). - */ -void wlr_surface_for_each_surface(struct wlr_surface *surface, - wlr_surface_iterator_func_t iterator, void *user_data); - -/** - * Get the effective damage to the surface in terms of surface local - * coordinates. This includes damage induced by resizing and moving the - * surface. The damage is not expected to be bounded by the surface itself. - */ -void wlr_surface_get_effective_damage(struct wlr_surface *surface, - pixman_region32_t *damage); - -/** - * Get the source rectangle describing the region of the buffer that needs to - * be sampled to render this surface's current state. The box is in - * buffer-local coordinates. - * - * If the viewport's source rectangle is unset, the position is zero and the - * size is the buffer's. - */ -void wlr_surface_get_buffer_source_box(struct wlr_surface *surface, - struct wlr_fbox *box); - -/** - * Acquire a lock for the pending surface state. - * - * The state won't be committed before the caller releases the lock. Instead, - * the state becomes cached. The caller needs to use wlr_surface_unlock_cached - * to release the lock. - * - * Returns a surface commit sequence number for the cached state. - */ -uint32_t wlr_surface_lock_pending(struct wlr_surface *surface); - -/** - * Release a lock for a cached state. - * - * Callers should not assume that the cached state will immediately be - * committed. Another caller may still have an active lock. - */ -void wlr_surface_unlock_cached(struct wlr_surface *surface, uint32_t seq); - -#endif +#include +#include diff --git a/include/wlr/types/wlr_switch.h b/include/wlr/types/wlr_switch.h index 93db04c5f..31811ec33 100644 --- a/include/wlr/types/wlr_switch.h +++ b/include/wlr/types/wlr_switch.h @@ -16,7 +16,9 @@ struct wlr_switch_impl; struct wlr_switch { - struct wlr_switch_impl *impl; + struct wlr_input_device base; + + const struct wlr_switch_impl *impl; struct { struct wl_signal toggle; diff --git a/include/wlr/types/wlr_tablet_pad.h b/include/wlr/types/wlr_tablet_pad.h index 6cd1b70e4..249233537 100644 --- a/include/wlr/types/wlr_tablet_pad.h +++ b/include/wlr/types/wlr_tablet_pad.h @@ -22,7 +22,9 @@ struct wlr_tablet_pad_impl; struct wlr_tablet_pad { - struct wlr_tablet_pad_impl *impl; + struct wlr_input_device base; + + const struct wlr_tablet_pad_impl *impl; struct { struct wl_signal button; diff --git a/include/wlr/types/wlr_tablet_tool.h b/include/wlr/types/wlr_tablet_tool.h index 75fc1ee80..b8b4000f7 100644 --- a/include/wlr/types/wlr_tablet_tool.h +++ b/include/wlr/types/wlr_tablet_tool.h @@ -60,6 +60,8 @@ struct wlr_tablet_tool { struct wlr_tablet_impl; struct wlr_tablet { + struct wlr_input_device base; + const struct wlr_tablet_impl *impl; struct { diff --git a/include/wlr/types/wlr_text_input_v3.h b/include/wlr/types/wlr_text_input_v3.h index c9ee0b221..5bac69a65 100644 --- a/include/wlr/types/wlr_text_input_v3.h +++ b/include/wlr/types/wlr_text_input_v3.h @@ -11,9 +11,10 @@ #include #include -#include #include +struct wlr_surface; + enum wlr_text_input_v3_features { WLR_TEXT_INPUT_V3_FEATURE_SURROUNDING_TEXT = 1 << 0, WLR_TEXT_INPUT_V3_FEATURE_CONTENT_TYPE = 1 << 1, diff --git a/include/wlr/types/wlr_touch.h b/include/wlr/types/wlr_touch.h index ca7cf2a64..b2c097e8f 100644 --- a/include/wlr/types/wlr_touch.h +++ b/include/wlr/types/wlr_touch.h @@ -10,11 +10,14 @@ #define WLR_TYPES_WLR_TOUCH_H #include +#include #include struct wlr_touch_impl; struct wlr_touch { + struct wlr_input_device base; + const struct wlr_touch_impl *impl; struct { diff --git a/include/wlr/types/wlr_virtual_keyboard_v1.h b/include/wlr/types/wlr_virtual_keyboard_v1.h index a3f4e4521..a818f1416 100644 --- a/include/wlr/types/wlr_virtual_keyboard_v1.h +++ b/include/wlr/types/wlr_virtual_keyboard_v1.h @@ -10,8 +10,7 @@ #define WLR_TYPES_WLR_VIRTUAL_KEYBOARD_V1_H #include -#include -#include +#include struct wlr_virtual_keyboard_manager_v1 { struct wl_global *global; @@ -26,7 +25,7 @@ struct wlr_virtual_keyboard_manager_v1 { }; struct wlr_virtual_keyboard_v1 { - struct wlr_input_device input_device; + struct wlr_keyboard keyboard; struct wl_resource *resource; struct wlr_seat *seat; bool has_keymap; diff --git a/include/wlr/types/wlr_virtual_pointer_v1.h b/include/wlr/types/wlr_virtual_pointer_v1.h index ede9bebf5..307d61586 100644 --- a/include/wlr/types/wlr_virtual_pointer_v1.h +++ b/include/wlr/types/wlr_virtual_pointer_v1.h @@ -11,7 +11,6 @@ #include #include -#include #include #include @@ -28,7 +27,7 @@ struct wlr_virtual_pointer_manager_v1 { }; struct wlr_virtual_pointer_v1 { - struct wlr_input_device input_device; + struct wlr_pointer pointer; struct wl_resource *resource; /* Vertical and horizontal */ struct wlr_event_pointer_axis axis_event[2]; diff --git a/include/wlr/types/wlr_xdg_activation_v1.h b/include/wlr/types/wlr_xdg_activation_v1.h index 463562151..97801af2a 100644 --- a/include/wlr/types/wlr_xdg_activation_v1.h +++ b/include/wlr/types/wlr_xdg_activation_v1.h @@ -81,4 +81,8 @@ struct wlr_xdg_activation_token_v1 *wlr_xdg_activation_v1_find_token( const char *wlr_xdg_activation_token_v1_get_name( struct wlr_xdg_activation_token_v1 *token); +// Add a token to the pool of known tokens +struct wlr_xdg_activation_token_v1 *wlr_xdg_activation_v1_add_token( + struct wlr_xdg_activation_v1 *activation, const char *token_str); + #endif diff --git a/include/wlr/types/wlr_xdg_shell.h b/include/wlr/types/wlr_xdg_shell.h index cf42e82a7..48bfbb975 100644 --- a/include/wlr/types/wlr_xdg_shell.h +++ b/include/wlr/types/wlr_xdg_shell.h @@ -10,6 +10,7 @@ #define WLR_TYPES_WLR_XDG_SHELL_H #include +#include #include #include #include "xdg-shell-protocol.h" @@ -48,7 +49,7 @@ struct wlr_xdg_client { struct wl_event_source *ping_timer; }; -struct wlr_xdg_positioner { +struct wlr_xdg_positioner_rules { struct wlr_box anchor_rect; enum xdg_positioner_anchor anchor; enum xdg_positioner_gravity gravity; @@ -63,6 +64,11 @@ struct wlr_xdg_positioner { } offset; }; +struct wlr_xdg_positioner { + struct wl_resource *resource; + struct wlr_xdg_positioner_rules rules; +}; + struct wlr_xdg_popup { struct wlr_xdg_surface *base; struct wl_list link; @@ -76,7 +82,7 @@ struct wlr_xdg_popup { // geometry of the parent surface struct wlr_box geometry; - struct wlr_xdg_positioner positioner; + struct wlr_xdg_positioner_rules positioner_rules; struct wl_list grab_link; // wlr_xdg_popup_grab::popups }; @@ -124,7 +130,7 @@ struct wlr_xdg_toplevel { struct wlr_xdg_surface *base; bool added; - struct wlr_xdg_surface *parent; + struct wlr_xdg_toplevel *parent; struct wl_listener parent_unmap; struct wlr_xdg_toplevel_state current, pending; @@ -133,8 +139,8 @@ struct wlr_xdg_toplevel { struct wlr_xdg_toplevel_configure scheduled; // Properties that the client has requested. Intended to be checked - // by the compositor on surface map and handled accordingly - // (e.g. a client might want to start already in a fullscreen state). + // by the compositor on surface map and state change requests (such as + // xdg_toplevel::set_fullscreen) and handled accordingly. struct wlr_xdg_toplevel_requested requested; char *title; @@ -230,26 +236,20 @@ struct wlr_xdg_surface { }; struct wlr_xdg_toplevel_move_event { - struct wlr_xdg_surface *surface; + struct wlr_xdg_toplevel *toplevel; struct wlr_seat_client *seat; uint32_t serial; }; struct wlr_xdg_toplevel_resize_event { - struct wlr_xdg_surface *surface; + struct wlr_xdg_toplevel *toplevel; struct wlr_seat_client *seat; uint32_t serial; uint32_t edges; }; -struct wlr_xdg_toplevel_set_fullscreen_event { - struct wlr_xdg_surface *surface; - bool fullscreen; - struct wlr_output *output; -}; - struct wlr_xdg_toplevel_show_window_menu_event { - struct wlr_xdg_surface *surface; + struct wlr_xdg_toplevel *toplevel; struct wlr_seat_client *seat; uint32_t serial; uint32_t x, y; @@ -257,16 +257,35 @@ struct wlr_xdg_toplevel_show_window_menu_event { struct wlr_xdg_shell *wlr_xdg_shell_create(struct wl_display *display); -/** Returns the wlr_xdg_surface from an xdg_surface resource. +/** Get the corresponding wlr_xdg_surface from a resource. * * Aborts if the resource doesn't have the correct type. Returns NULL if the * resource is inert. */ struct wlr_xdg_surface *wlr_xdg_surface_from_resource( struct wl_resource *resource); -struct wlr_xdg_surface *wlr_xdg_surface_from_popup_resource( + +/** Get the corresponding wlr_xdg_popup from a resource. + * + * Aborts if the resource doesn't have the correct type. Returns NULL if the + * resource is inert. + */ +struct wlr_xdg_popup *wlr_xdg_popup_from_resource( struct wl_resource *resource); -struct wlr_xdg_surface *wlr_xdg_surface_from_toplevel_resource( + +/** Get the corresponding wlr_xdg_toplevel from a resource. + * + * Aborts if the resource doesn't have the correct type. Returns NULL if the + * resource is inert. + */ +struct wlr_xdg_toplevel *wlr_xdg_toplevel_from_resource( + struct wl_resource *resource); + +/** Get the corresponding wlr_xdg_positioner from a resource. + * + * Aborts if the resource doesn't have the correct type. + */ +struct wlr_xdg_positioner *wlr_xdg_positioner_from_resource( struct wl_resource *resource); /** @@ -279,78 +298,79 @@ void wlr_xdg_surface_ping(struct wlr_xdg_surface *surface); * Request that this toplevel surface be the given size. Returns the associated * configure serial. */ -uint32_t wlr_xdg_toplevel_set_size(struct wlr_xdg_surface *surface, +uint32_t wlr_xdg_toplevel_set_size(struct wlr_xdg_toplevel *toplevel, uint32_t width, uint32_t height); /** - * Request that this toplevel surface show itself in an activated or deactivated + * Request that this toplevel show itself in an activated or deactivated * state. Returns the associated configure serial. */ -uint32_t wlr_xdg_toplevel_set_activated(struct wlr_xdg_surface *surface, +uint32_t wlr_xdg_toplevel_set_activated(struct wlr_xdg_toplevel *toplevel, bool activated); /** - * Request that this toplevel surface consider itself maximized or not + * Request that this toplevel consider itself maximized or not * maximized. Returns the associated configure serial. */ -uint32_t wlr_xdg_toplevel_set_maximized(struct wlr_xdg_surface *surface, +uint32_t wlr_xdg_toplevel_set_maximized(struct wlr_xdg_toplevel *toplevel, bool maximized); /** - * Request that this toplevel surface consider itself fullscreen or not + * Request that this toplevel consider itself fullscreen or not * fullscreen. Returns the associated configure serial. */ -uint32_t wlr_xdg_toplevel_set_fullscreen(struct wlr_xdg_surface *surface, +uint32_t wlr_xdg_toplevel_set_fullscreen(struct wlr_xdg_toplevel *toplevel, bool fullscreen); /** - * Request that this toplevel surface consider itself to be resizing or not + * Request that this toplevel consider itself to be resizing or not * resizing. Returns the associated configure serial. */ -uint32_t wlr_xdg_toplevel_set_resizing(struct wlr_xdg_surface *surface, +uint32_t wlr_xdg_toplevel_set_resizing(struct wlr_xdg_toplevel *toplevel, bool resizing); /** - * Request that this toplevel surface consider itself in a tiled layout and some + * Request that this toplevel consider itself in a tiled layout and some * edges are adjacent to another part of the tiling grid. `tiled_edges` is a * bitfield of `enum wlr_edges`. Returns the associated configure serial. */ -uint32_t wlr_xdg_toplevel_set_tiled(struct wlr_xdg_surface *surface, +uint32_t wlr_xdg_toplevel_set_tiled(struct wlr_xdg_toplevel *toplevel, uint32_t tiled_edges); /** - * Request that this xdg toplevel closes. + * Request that this toplevel closes. */ -void wlr_xdg_toplevel_send_close(struct wlr_xdg_surface *surface); +void wlr_xdg_toplevel_send_close(struct wlr_xdg_toplevel *toplevel); /** * Sets the parent of this toplevel. Parent can be NULL. */ -void wlr_xdg_toplevel_set_parent(struct wlr_xdg_surface *surface, - struct wlr_xdg_surface *parent); +void wlr_xdg_toplevel_set_parent(struct wlr_xdg_toplevel *toplevel, + struct wlr_xdg_toplevel *parent); /** - * Request that this xdg popup closes. + * Request that this popup closes. **/ -void wlr_xdg_popup_destroy(struct wlr_xdg_surface *surface); +void wlr_xdg_popup_destroy(struct wlr_xdg_popup *popup); /** * Get the position for this popup in the surface parent's coordinate system. */ void wlr_xdg_popup_get_position(struct wlr_xdg_popup *popup, double *popup_sx, double *popup_sy); -/** - * Get the geometry for this positioner based on the anchor rect, gravity, and - * size of this positioner. - */ -struct wlr_box wlr_xdg_positioner_get_geometry( - struct wlr_xdg_positioner *positioner); /** - * Get the anchor point for this popup in the toplevel parent's coordinate system. + * Get the geometry based on positioner rules. */ -void wlr_xdg_popup_get_anchor_point(struct wlr_xdg_popup *popup, - int *toplevel_sx, int *toplevel_sy); +void wlr_xdg_positioner_rules_get_geometry( + const struct wlr_xdg_positioner_rules *rules, struct wlr_box *box); + +/** + * Unconstrain the box from the constraint area according to positioner rules. + */ +void wlr_xdg_positioner_rules_unconstrain_box( + const struct wlr_xdg_positioner_rules *rules, + const struct wlr_box *constraint, struct wlr_box *box); /** * Convert the given coordinates in the popup coordinate system to the toplevel @@ -365,19 +385,7 @@ void wlr_xdg_popup_get_toplevel_coords(struct wlr_xdg_popup *popup, * surface coordinate system. */ void wlr_xdg_popup_unconstrain_from_box(struct wlr_xdg_popup *popup, - const 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_invert_x(struct wlr_xdg_positioner *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_invert_y(struct wlr_xdg_positioner *positioner); + const struct wlr_box *toplevel_space_box); /** * Find a surface within this xdg-surface tree at the given surface-local diff --git a/include/xcursor/xcursor.h b/include/xcursor/xcursor.h index 285f4de66..c21707b4b 100644 --- a/include/xcursor/xcursor.h +++ b/include/xcursor/xcursor.h @@ -54,9 +54,6 @@ typedef struct _XcursorImages { char *name; /* name used to load images */ } XcursorImages; -XcursorImages * -XcursorLibraryLoadImages (const char *file, const char *theme, int size); - void XcursorImagesDestroy (XcursorImages *images); diff --git a/meson.build b/meson.build index c546b74d0..f7fb659c4 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'wlroots', 'c', - version: '0.15.0', + version: '0.16.0', license: 'MIT', meson_version: '>=0.58.1', 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 = 10 +soversion = 11 little_endian = host_machine.endian() == 'little' big_endian = host_machine.endian() == 'big' @@ -99,16 +99,32 @@ internal_features = { wayland_project_options = ['tests=false', 'documentation=false'] wayland_server = dependency('wayland-server', - version: '>=1.19', - fallback: ['wayland', 'wayland_server_dep'], - default_options: wayland_project_options, -) -wayland_client = dependency('wayland-client', - fallback: ['wayland', 'wayland_client_dep'], + version: '>=1.20', + fallback: 'wayland', default_options: wayland_project_options, ) -drm = dependency('libdrm', version: '>=2.4.105') +drm = dependency('libdrm', + version: '>=2.4.109', + fallback: 'libdrm', + default_options: [ + 'libkms=false', + 'intel=false', + 'radeon=false', + 'amdgpu=false', + 'nouveau=false', + 'vmwgfx=false', + 'omap=false', + 'exynos=false', + 'freedreno=false', + 'tegra=false', + 'vc4=false', + 'etnaviv=false', + 'cairo-tests=false', + 'man-pages=false', + 'valgrind=false', + ], +) gbm = dependency('gbm', version: '>=17.1.0') xkbcommon = dependency('xkbcommon') udev = dependency('libudev') @@ -119,7 +135,6 @@ rt = cc.find_library('rt') wlr_files = [] wlr_deps = [ wayland_server, - wayland_client, drm, gbm, xkbcommon, @@ -180,6 +195,7 @@ summary(features + internal_features, bool_yn: true) if get_option('examples') subdir('examples') + subdir('tinywl') endif pkgconfig = import('pkgconfig') diff --git a/protocol/meson.build b/protocol/meson.build index 8d8b2502e..179c731dc 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -1,6 +1,6 @@ wayland_protos = dependency('wayland-protocols', - version: '>=1.23', - fallback: ['wayland-protocols', 'wayland_protocols'], + version: '>=1.25', + fallback: 'wayland-protocols', default_options: ['tests=false'], ) wl_protocol_dir = wayland_protos.get_variable('pkgdatadir') @@ -20,6 +20,7 @@ protocols = { # Staging upstream protocols 'xdg-activation-v1': wl_protocol_dir / 'staging/xdg-activation/xdg-activation-v1.xml', 'drm-lease-v1': wl_protocol_dir / 'staging/drm-lease/drm-lease-v1.xml', + 'ext-session-lock-v1': wl_protocol_dir / 'staging/ext-session-lock/ext-session-lock-v1.xml', # Unstable upstream protocols 'fullscreen-shell-unstable-v1': wl_protocol_dir / 'unstable/fullscreen-shell/fullscreen-shell-unstable-v1.xml', diff --git a/render/allocator/allocator.c b/render/allocator/allocator.c index 15d55a0de..5108ad043 100644 --- a/render/allocator/allocator.c +++ b/render/allocator/allocator.c @@ -139,6 +139,9 @@ struct wlr_allocator *wlr_allocator_autocreate(struct wlr_backend *backend, struct wlr_renderer *renderer) { // Note, drm_fd may be negative if unavailable int drm_fd = wlr_backend_get_drm_fd(backend); + if (drm_fd < 0) { + drm_fd = wlr_renderer_get_drm_fd(renderer); + } return allocator_autocreate_with_drm_fd(backend, renderer, drm_fd); } diff --git a/render/allocator/gbm.c b/render/allocator/gbm.c index b546e412c..8f73862a2 100644 --- a/render/allocator/gbm.c +++ b/render/allocator/gbm.c @@ -10,6 +10,7 @@ #include #include "render/allocator/gbm.h" +#include "render/drm_format_set.h" static const struct wlr_buffer_impl buffer_impl; @@ -89,17 +90,22 @@ static struct wlr_gbm_buffer *create_buffer(struct wlr_gbm_allocator *alloc, int width, int height, const struct wlr_drm_format *format) { struct gbm_device *gbm_device = alloc->gbm_device; - struct gbm_bo *bo = NULL; + assert(format->len > 0); + bool has_modifier = true; - if (format->len > 0) { - bo = gbm_bo_create_with_modifiers(gbm_device, width, height, - format->format, format->modifiers, format->len); - } + uint64_t fallback_modifier = DRM_FORMAT_MOD_INVALID; + struct gbm_bo *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; + fallback_modifier = DRM_FORMAT_MOD_LINEAR; + } else if (!wlr_drm_format_has(format, DRM_FORMAT_MOD_INVALID)) { + // If the format doesn't accept an implicit modifier, bail out. + wlr_log(WLR_ERROR, "gbm_bo_create_with_modifiers failed"); + return NULL; } bo = gbm_bo_create(gbm_device, width, height, format->format, usage); has_modifier = false; @@ -128,7 +134,7 @@ static struct wlr_gbm_buffer *create_buffer(struct wlr_gbm_allocator *alloc, // don't populate the modifier field: other parts of the stack may not // understand modifiers, and they can't strip the modifier. if (!has_modifier) { - buffer->dmabuf.modifier = DRM_FORMAT_MOD_INVALID; + buffer->dmabuf.modifier = fallback_modifier; } wlr_log(WLR_DEBUG, "Allocated %dx%d GBM buffer (format 0x%"PRIX32", " diff --git a/render/drm_format_set.c b/render/drm_format_set.c index bd8f0cb75..134717462 100644 --- a/render/drm_format_set.c +++ b/render/drm_format_set.c @@ -15,7 +15,7 @@ void wlr_drm_format_set_finish(struct wlr_drm_format_set *set) { free(set->formats); set->len = 0; - set->cap = 0; + set->capacity = 0; set->formats = NULL; } @@ -43,18 +43,7 @@ bool wlr_drm_format_set_has(const struct wlr_drm_format_set *set, if (!fmt) { return false; } - - if (modifier == DRM_FORMAT_MOD_INVALID) { - return true; - } - - for (size_t i = 0; i < fmt->len; ++i) { - if (fmt->modifiers[i] == modifier) { - return true; - } - } - - return false; + return wlr_drm_format_has(fmt, modifier); } bool wlr_drm_format_set_add(struct wlr_drm_format_set *set, uint32_t format, @@ -74,8 +63,8 @@ bool wlr_drm_format_set_add(struct wlr_drm_format_set *set, uint32_t format, return false; } - if (set->len == set->cap) { - size_t new = set->cap ? set->cap * 2 : 4; + if (set->len == set->capacity) { + size_t new = set->capacity ? set->capacity * 2 : 4; struct wlr_drm_format **tmp = realloc(set->formats, sizeof(*fmt) + sizeof(fmt->modifiers[0]) * new); @@ -85,7 +74,7 @@ bool wlr_drm_format_set_add(struct wlr_drm_format_set *set, uint32_t format, return false; } - set->cap = new; + set->capacity = new; set->formats = tmp; } @@ -94,41 +83,44 @@ bool wlr_drm_format_set_add(struct wlr_drm_format_set *set, uint32_t format, } struct wlr_drm_format *wlr_drm_format_create(uint32_t format) { - size_t cap = 4; + size_t capacity = 4; struct wlr_drm_format *fmt = - calloc(1, sizeof(*fmt) + sizeof(fmt->modifiers[0]) * cap); + calloc(1, sizeof(*fmt) + sizeof(fmt->modifiers[0]) * capacity); if (!fmt) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return NULL; } fmt->format = format; - fmt->cap = cap; + fmt->capacity = capacity; 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; - } - +bool wlr_drm_format_has(const struct wlr_drm_format *fmt, uint64_t modifier) { for (size_t i = 0; i < fmt->len; ++i) { if (fmt->modifiers[i] == modifier) { return true; } } + return false; +} - if (fmt->len == fmt->cap) { - size_t cap = fmt->cap ? fmt->cap * 2 : 4; +bool wlr_drm_format_add(struct wlr_drm_format **fmt_ptr, uint64_t modifier) { + struct wlr_drm_format *fmt = *fmt_ptr; - fmt = realloc(fmt, sizeof(*fmt) + sizeof(fmt->modifiers[0]) * cap); + if (wlr_drm_format_has(fmt, modifier)) { + return true; + } + + if (fmt->len == fmt->capacity) { + size_t capacity = fmt->capacity ? fmt->capacity * 2 : 4; + + fmt = realloc(fmt, sizeof(*fmt) + sizeof(fmt->modifiers[0]) * capacity); if (!fmt) { wlr_log_errno(WLR_ERROR, "Allocation failed"); return false; } - fmt->cap = cap; + fmt->capacity = capacity; *fmt_ptr = fmt; } @@ -137,9 +129,9 @@ 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) { - assert(format->len <= format->cap); + assert(format->len <= format->capacity); size_t format_size = sizeof(struct wlr_drm_format) + - format->cap * sizeof(format->modifiers[0]); + format->capacity * sizeof(format->modifiers[0]); struct wlr_drm_format *duped_format = malloc(format_size); if (duped_format == NULL) { return NULL; @@ -152,16 +144,6 @@ 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); - // Special case: if a format only supports LINEAR and the other doesn't - // support any modifier, force LINEAR. This will force the allocator to - // create a buffer with a LINEAR layout instead of an implicit modifier. - if (a->len == 0 && b->len == 1 && b->modifiers[0] == DRM_FORMAT_MOD_LINEAR) { - return wlr_drm_format_dup(b); - } - if (b->len == 0 && a->len == 1 && a->modifiers[0] == DRM_FORMAT_MOD_LINEAR) { - return wlr_drm_format_dup(a); - } - 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]); @@ -171,12 +153,12 @@ struct wlr_drm_format *wlr_drm_format_intersect( return NULL; } format->format = a->format; - format->cap = format_cap; + format->capacity = 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); + assert(format->len < format->capacity); format->modifiers[format->len] = a->modifiers[i]; format->len++; break; @@ -184,12 +166,50 @@ struct wlr_drm_format *wlr_drm_format_intersect( } } - // 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) { + // If the intersection is empty, then the formats aren't compatible with + // each other. + if (format->len == 0) { free(format); return NULL; } return format; } + +bool wlr_drm_format_set_intersect(struct wlr_drm_format_set *dst, + const struct wlr_drm_format_set *a, const struct wlr_drm_format_set *b) { + assert(dst != a && dst != b); + + struct wlr_drm_format_set out = {0}; + out.capacity = a->len < b->len ? a->len : b->len; + out.formats = calloc(out.capacity, sizeof(struct wlr_drm_format *)); + if (out.formats == NULL) { + wlr_log_errno(WLR_ERROR, "Allocation failed"); + return false; + } + + for (size_t i = 0; i < a->len; i++) { + for (size_t j = 0; j < b->len; j++) { + if (a->formats[i]->format == b->formats[j]->format) { + // When the two formats have no common modifier, keep + // intersecting the rest of the formats: they may be compatible + // with each other + struct wlr_drm_format *format = + wlr_drm_format_intersect(a->formats[i], b->formats[j]); + if (format != NULL) { + out.formats[out.len] = format; + out.len++; + } + break; + } + } + } + + if (out.len == 0) { + wlr_drm_format_set_finish(&out); + return false; + } + + *dst = out; + return true; +} diff --git a/render/egl.c b/render/egl.c index ec23ce8d8..d5d6cfb7d 100644 --- a/render/egl.c +++ b/render/egl.c @@ -119,11 +119,19 @@ static void init_dmabuf_formats(struct wlr_egl *egl) { has_modifiers = has_modifiers || modifiers_len > 0; + // EGL always supports implicit modifiers + 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); + if (modifiers_len == 0) { + // Asume the linear layout is supported if the driver doesn't + // explicitly say otherwise wlr_drm_format_set_add(&egl->dmabuf_texture_formats, fmt, - DRM_FORMAT_MOD_INVALID); + DRM_FORMAT_MOD_LINEAR); wlr_drm_format_set_add(&egl->dmabuf_render_formats, fmt, - DRM_FORMAT_MOD_INVALID); + DRM_FORMAT_MOD_LINEAR); } for (int j = 0; j < modifiers_len; j++) { @@ -152,6 +160,8 @@ static void init_dmabuf_formats(struct wlr_egl *egl) { has_modifiers ? "supported" : "unsupported"); free(str_formats); + egl->has_modifiers = has_modifiers; + out: free(formats); } @@ -224,14 +234,8 @@ static struct wlr_egl *egl_create(void) { return egl; } -static bool egl_init(struct wlr_egl *egl, EGLenum platform, - void *remote_display) { - egl->display = egl->procs.eglGetPlatformDisplayEXT(platform, - remote_display, NULL); - if (egl->display == EGL_NO_DISPLAY) { - wlr_log(WLR_ERROR, "Failed to create EGL display"); - return false; - } +static bool egl_init_display(struct wlr_egl *egl, EGLDisplay *display) { + egl->display = display; EGLint major, minor; if (eglInitialize(egl->display, &major, &minor) == EGL_FALSE) { @@ -316,11 +320,14 @@ static bool egl_init(struct wlr_egl *egl, EGLenum platform, return false; } + egl->exts.IMG_context_priority = + check_egl_ext(display_exts_str, "EGL_IMG_context_priority"); + + wlr_log(WLR_INFO, "Using EGL %d.%d", (int)major, (int)minor); 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, "Using EGL %d.%d", (int)major, (int)minor); wlr_log(WLR_INFO, "EGL vendor: %s", eglQueryString(egl->display, EGL_VENDOR)); if (driver_name != NULL) { wlr_log(WLR_INFO, "EGL driver name: %s", driver_name); @@ -328,8 +335,22 @@ static bool egl_init(struct wlr_egl *egl, EGLenum platform, init_dmabuf_formats(egl); - bool ext_context_priority = - check_egl_ext(display_exts_str, "EGL_IMG_context_priority"); + return true; +} + +static bool egl_init(struct wlr_egl *egl, EGLenum platform, + void *remote_display) { + EGLDisplay display = egl->procs.eglGetPlatformDisplayEXT(platform, + remote_display, NULL); + if (display == EGL_NO_DISPLAY) { + wlr_log(WLR_ERROR, "Failed to create EGL display"); + return false; + } + + if (!egl_init_display(egl, display)) { + eglTerminate(display); + return false; + } size_t atti = 0; EGLint attribs[5]; @@ -338,7 +359,7 @@ static bool egl_init(struct wlr_egl *egl, EGLenum platform, // Request a high priority context if possible // TODO: only do this if we're running as the DRM master - bool request_high_priority = ext_context_priority; + bool request_high_priority = egl->exts.IMG_context_priority; // Try to reschedule all of our rendering to be completed first. If it // fails, it will fallback to the default priority (MEDIUM). @@ -508,6 +529,36 @@ error: return NULL; } +struct wlr_egl *wlr_egl_create_with_context(EGLDisplay display, + EGLContext context) { + EGLint client_type; + if (!eglQueryContext(display, context, EGL_CONTEXT_CLIENT_TYPE, &client_type) || + client_type != EGL_OPENGL_ES_API) { + wlr_log(WLR_ERROR, "Unsupported EGL context client type (need OpenGL ES)"); + return NULL; + } + + EGLint client_version; + if (!eglQueryContext(display, context, EGL_CONTEXT_CLIENT_VERSION, &client_version) || + client_version < 2) { + wlr_log(WLR_ERROR, "Unsupported EGL context client version (need OpenGL ES >= 2)"); + return NULL; + } + + struct wlr_egl *egl = egl_create(); + if (egl == NULL) { + return NULL; + } + + if (!egl_init_display(egl, display)) { + return NULL; + } + + egl->context = context; + + return egl; +} + void wlr_egl_destroy(struct wlr_egl *egl) { if (egl == NULL) { return; @@ -595,19 +646,11 @@ EGLImageKHR wlr_egl_create_image_from_dmabuf(struct wlr_egl *egl, return NULL; } - bool has_modifier = false; - - // we assume the same way we assumed formats without the import_modifiers - // extension that mod_linear is supported. The special mod mod_invalid - // is sometimes used to signal modifier unawareness which is what we - // have here if (attributes->modifier != DRM_FORMAT_MOD_INVALID && - attributes->modifier != DRM_FORMAT_MOD_LINEAR) { - if (!egl->exts.EXT_image_dma_buf_import_modifiers) { - wlr_log(WLR_ERROR, "dmabuf modifiers extension not present"); - return NULL; - } - has_modifier = true; + attributes->modifier != DRM_FORMAT_MOD_LINEAR && + !egl->has_modifiers) { + wlr_log(WLR_ERROR, "EGL implementation doesn't support modifiers"); + return NULL; } unsigned int atti = 0; @@ -653,14 +696,15 @@ EGLImageKHR wlr_egl_create_image_from_dmabuf(struct wlr_egl *egl, } }; - for (int i=0; i < attributes->n_planes; i++) { + for (int i = 0; i < attributes->n_planes; i++) { attribs[atti++] = attr_names[i].fd; attribs[atti++] = attributes->fd[i]; attribs[atti++] = attr_names[i].offset; attribs[atti++] = attributes->offset[i]; attribs[atti++] = attr_names[i].pitch; attribs[atti++] = attributes->stride[i]; - if (has_modifier) { + if (egl->has_modifiers && + attributes->modifier != DRM_FORMAT_MOD_INVALID) { attribs[atti++] = attr_names[i].mod_lo; attribs[atti++] = attributes->modifier & 0xFFFFFFFF; attribs[atti++] = attr_names[i].mod_hi; diff --git a/render/gles2/pixel_format.c b/render/gles2/pixel_format.c index 31bb3908e..b155bbbe9 100644 --- a/render/gles2/pixel_format.c +++ b/render/gles2/pixel_format.c @@ -98,6 +98,10 @@ static const struct wlr_gles2_pixel_format formats[] = { // TODO: more pixel formats +/* + * Return true if supported for texturing, even if other operations like + * reading aren't supported. + */ bool is_gles2_pixel_format_supported(const struct wlr_gles2_renderer *renderer, const struct wlr_gles2_pixel_format *format) { if (format->gl_type == GL_UNSIGNED_INT_2_10_10_10_REV_EXT @@ -108,10 +112,12 @@ bool is_gles2_pixel_format_supported(const struct wlr_gles2_renderer *renderer, && !renderer->exts.OES_texture_half_float_linear) { return false; } - if (format->gl_format == GL_BGRA_EXT - && !renderer->exts.EXT_read_format_bgra) { - return false; - } + /* + * Note that we don't need to check for GL_EXT_texture_format_BGRA8888 + * here, since we've already checked if we have it at renderer creation + * time and bailed out if not. We do the check there because Wayland + * requires all compositors to support SHM buffers in that format. + */ return true; } diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c index 01efaf1de..67b8ead47 100644 --- a/render/gles2/renderer.c +++ b/render/gles2/renderer.c @@ -309,7 +309,6 @@ static bool gles2_render_subtexture_with_matrix( glUseProgram(shader->program); glUniformMatrix3fv(shader->proj, 1, GL_FALSE, gl_matrix); - glUniform1i(shader->invert_y, texture->inverted_y); glUniform1i(shader->tex, 0); glUniform1f(shader->alpha, alpha); @@ -442,6 +441,12 @@ static bool gles2_read_pixels(struct wlr_renderer *wlr_renderer, return false; } + if (fmt->gl_format == GL_BGRA_EXT && !renderer->exts.EXT_read_format_bgra) { + wlr_log(WLR_ERROR, + "Cannot read pixels: missing GL_EXT_read_format_bgra extension"); + return false; + } + const struct wlr_pixel_format_info *drm_fmt = drm_get_pixel_format_info(fmt->drm_format); assert(drm_fmt); @@ -810,7 +815,6 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { goto error; } renderer->shaders.tex_rgba.proj = glGetUniformLocation(prog, "proj"); - renderer->shaders.tex_rgba.invert_y = glGetUniformLocation(prog, "invert_y"); renderer->shaders.tex_rgba.tex = glGetUniformLocation(prog, "tex"); renderer->shaders.tex_rgba.alpha = glGetUniformLocation(prog, "alpha"); renderer->shaders.tex_rgba.pos_attrib = glGetAttribLocation(prog, "pos"); @@ -822,7 +826,6 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { goto error; } renderer->shaders.tex_rgbx.proj = glGetUniformLocation(prog, "proj"); - renderer->shaders.tex_rgbx.invert_y = glGetUniformLocation(prog, "invert_y"); renderer->shaders.tex_rgbx.tex = glGetUniformLocation(prog, "tex"); renderer->shaders.tex_rgbx.alpha = glGetUniformLocation(prog, "alpha"); renderer->shaders.tex_rgbx.pos_attrib = glGetAttribLocation(prog, "pos"); @@ -835,7 +838,6 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { goto error; } renderer->shaders.tex_ext.proj = glGetUniformLocation(prog, "proj"); - renderer->shaders.tex_ext.invert_y = glGetUniformLocation(prog, "invert_y"); renderer->shaders.tex_ext.tex = glGetUniformLocation(prog, "tex"); renderer->shaders.tex_ext.alpha = glGetUniformLocation(prog, "alpha"); renderer->shaders.tex_ext.pos_attrib = glGetAttribLocation(prog, "pos"); diff --git a/render/gles2/shaders.c b/render/gles2/shaders.c index d854b270c..7898059ed 100644 --- a/render/gles2/shaders.c +++ b/render/gles2/shaders.c @@ -28,18 +28,13 @@ const GLchar quad_fragment_src[] = // Textured quads const GLchar tex_vertex_src[] = "uniform mat3 proj;\n" -"uniform bool invert_y;\n" "attribute vec2 pos;\n" "attribute vec2 texcoord;\n" "varying vec2 v_texcoord;\n" "\n" "void main() {\n" " gl_Position = vec4(proj * vec3(pos, 1.0), 1.0);\n" -" if (invert_y) {\n" -" v_texcoord = vec2(texcoord.x, 1.0 - texcoord.y);\n" -" } else {\n" -" v_texcoord = texcoord;\n" -" }\n" +" v_texcoord = texcoord;\n" "}\n"; const GLchar tex_fragment_src_rgba[] = diff --git a/render/gles2/texture.c b/render/gles2/texture.c index 293b7a19b..8d6f3fc24 100644 --- a/render/gles2/texture.c +++ b/render/gles2/texture.c @@ -250,8 +250,6 @@ static struct wlr_texture *gles2_texture_from_dmabuf( return NULL; } texture->drm_format = DRM_FORMAT_INVALID; // texture can't be written anyways - texture->inverted_y = - (attribs->flags & WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT) != 0; const struct wlr_pixel_format_info *drm_fmt = drm_get_pixel_format_info(attribs->format); @@ -363,6 +361,5 @@ void wlr_gles2_texture_get_attribs(struct wlr_texture *wlr_texture, memset(attribs, 0, sizeof(*attribs)); attribs->target = texture->target; attribs->tex = texture->tex; - attribs->inverted_y = texture->inverted_y; attribs->has_alpha = texture->has_alpha; } diff --git a/render/pixman/renderer.c b/render/pixman/renderer.c index c65523b0f..13db08800 100644 --- a/render/pixman/renderer.c +++ b/render/pixman/renderer.c @@ -528,7 +528,7 @@ struct wlr_renderer *wlr_pixman_renderer_create(void) { for (size_t i = 0; i < len; ++i) { wlr_drm_format_set_add(&renderer->drm_formats, formats[i], - DRM_FORMAT_MOD_LINEAR); + DRM_FORMAT_MOD_INVALID); } return &renderer->wlr_renderer; diff --git a/render/vulkan/meson.build b/render/vulkan/meson.build index 6ae8df0c3..0c45a7136 100644 --- a/render/vulkan/meson.build +++ b/render/vulkan/meson.build @@ -33,6 +33,9 @@ if not glslang.found() endif endif +glslang_version_info = run_command(glslang, '--version', check: true).stdout() +glslang_version = glslang_version_info.split('\n')[0].split(':')[-1] + wlr_files += files( 'renderer.c', 'texture.c', diff --git a/render/vulkan/renderer.c b/render/vulkan/renderer.c index 31ce0b76c..21b36bdd6 100644 --- a/render/vulkan/renderer.c +++ b/render/vulkan/renderer.c @@ -433,15 +433,6 @@ static struct wlr_vk_render_buffer *create_render_buffer( wlr_log(WLR_DEBUG, "vulkan create_render_buffer: %.4s, %dx%d", (const char*) &dmabuf.format, dmabuf.width, dmabuf.height); - // NOTE: we could at least support WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT - // if it is needed by anyone. Can be implemented using negative viewport - // height or flipping matrix. - if (dmabuf.flags != 0) { - wlr_log(WLR_ERROR, "dmabuf flags %x not supported/implemented on vulkan", - dmabuf.flags); - goto error_buffer; - } - buffer->image = vulkan_import_dmabuf(renderer, &dmabuf, buffer->memories, &buffer->mem_count, true); if (!buffer->image) { @@ -607,7 +598,7 @@ static void vulkan_end(struct wlr_renderer *wlr_renderer) { wl_list_for_each_safe(texture, tmp_tex, &renderer->foreign_textures, foreign_link) { VkImageLayout src_layout = VK_IMAGE_LAYOUT_GENERAL; if (!texture->transitioned) { - src_layout = VK_IMAGE_LAYOUT_PREINITIALIZED; + src_layout = VK_IMAGE_LAYOUT_UNDEFINED; texture->transitioned = true; } @@ -789,11 +780,6 @@ static bool vulkan_render_subtexture_with_matrix(struct wlr_renderer *wlr_render vert_pcr_data.uv_size[0] = box->width / wlr_texture->width; vert_pcr_data.uv_size[1] = box->height / wlr_texture->height; - if (texture->invert_y) { - vert_pcr_data.uv_off[1] += vert_pcr_data.uv_size[1]; - vert_pcr_data.uv_size[1] = -vert_pcr_data.uv_size[1]; - } - vkCmdPushConstants(cb, renderer->pipe_layout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(vert_pcr_data), &vert_pcr_data); vkCmdPushConstants(cb, renderer->pipe_layout, diff --git a/render/vulkan/shaders/common.vert b/render/vulkan/shaders/common.vert index fa31d26c2..c6175d248 100644 --- a/render/vulkan/shaders/common.vert +++ b/render/vulkan/shaders/common.vert @@ -10,16 +10,9 @@ layout(push_constant, row_major) uniform UBO { layout(location = 0) out vec2 uv; -// 4 outlining points and uv coords -const vec2[] values = { - {0, 0}, - {1, 0}, - {1, 1}, - {0, 1}, -}; - void main() { - vec2 pos = values[gl_VertexIndex % 4]; + vec2 pos = vec2(float((gl_VertexIndex + 1) & 2) * 0.5f, + float(gl_VertexIndex & 2) * 0.5f); uv = data.uv_offset + pos * data.uv_size; gl_Position = data.proj * vec4(pos, 0.0, 1.0); } diff --git a/render/vulkan/shaders/meson.build b/render/vulkan/shaders/meson.build index b183c46ca..906618c29 100644 --- a/render/vulkan/shaders/meson.build +++ b/render/vulkan/shaders/meson.build @@ -8,6 +8,9 @@ vulkan_shaders = [] foreach shader : vulkan_shaders_src name = shader.underscorify() + '_data' args = [glslang, '-V', '@INPUT@', '-o', '@OUTPUT@', '--vn', name] + if glslang_version.version_compare('>=11.0.0') + args += '--quiet' + endif header = custom_target( shader + '_spv', output: shader + '.h', diff --git a/render/vulkan/texture.c b/render/vulkan/texture.c index f6fbec5a9..b705603cf 100644 --- a/render/vulkan/texture.c +++ b/render/vulkan/texture.c @@ -438,7 +438,7 @@ VkImage vulkan_import_dmabuf(struct wlr_vk_renderer *renderer, img_info.arrayLayers = 1; img_info.samples = VK_SAMPLE_COUNT_1_BIT; img_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - img_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; + img_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; img_info.extent = (VkExtent3D) { attribs->width, attribs->height, 1 }; img_info.usage = for_render ? VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT : @@ -605,19 +605,6 @@ static struct wlr_texture *vulkan_texture_from_dmabuf(struct wlr_renderer *wlr_r goto error; } - uint32_t flags = attribs->flags; - if (flags & WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT) { - texture->invert_y = true; - flags &= ~WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT; - } - - if (flags != 0) { - wlr_log(WLR_ERROR, "dmabuf flags %x not supported/implemented on vulkan", - attribs->flags); - // NOTE: should probably make this a critical error in future - // return VK_NULL_HANDLE; - } - const struct wlr_pixel_format_info *format_info = drm_get_pixel_format_info(attribs->format); assert(format_info); diff --git a/render/vulkan/vulkan.c b/render/vulkan/vulkan.c index 4932ec4da..7e9e93962 100644 --- a/render/vulkan/vulkan.c +++ b/render/vulkan/vulkan.c @@ -343,22 +343,40 @@ VkPhysicalDevice vulkan_find_drm_phdev(struct wlr_vk_instance *ini, int drm_fd) } const char *name = VK_EXT_PHYSICAL_DEVICE_DRM_EXTENSION_NAME; - if (find_extensions(avail_ext_props, avail_extc, &name, 1) != NULL) { + bool has_drm_props = find_extensions(avail_ext_props, avail_extc, &name, 1) == NULL; + name = VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME; + bool has_driver_props = find_extensions(avail_ext_props, avail_extc, &name, 1) == NULL; + + VkPhysicalDeviceProperties2 props = {0}; + props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + + VkPhysicalDeviceDrmPropertiesEXT drm_props = {0}; + drm_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRM_PROPERTIES_EXT; + if (has_drm_props) { + drm_props.pNext = props.pNext; + props.pNext = &drm_props; + } + + VkPhysicalDeviceDriverPropertiesKHR driver_props = {0}; + driver_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES; + if (has_driver_props) { + driver_props.pNext = props.pNext; + props.pNext = &driver_props; + } + + vkGetPhysicalDeviceProperties2(phdev, &props); + + if (has_driver_props) { + wlr_log(WLR_INFO, " Driver name: %s (%s)", driver_props.driverName, driver_props.driverInfo); + } + + if (!has_drm_props) { wlr_log(WLR_DEBUG, " Ignoring physical device \"%s\": " "VK_EXT_physical_device_drm not supported", phdev_props.deviceName); continue; } - VkPhysicalDeviceDrmPropertiesEXT drm_props = {0}; - drm_props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRM_PROPERTIES_EXT; - - VkPhysicalDeviceProperties2 props = {0}; - props.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; - props.pNext = &drm_props; - - vkGetPhysicalDeviceProperties2(phdev, &props); - dev_t primary_devid = makedev(drm_props.primaryMajor, drm_props.primaryMinor); dev_t render_devid = makedev(drm_props.renderMajor, drm_props.renderMinor); if (primary_devid == drm_stat.st_rdev || diff --git a/render/wlr_renderer.c b/render/wlr_renderer.c index 8a1f1e941..2b5cf9945 100644 --- a/render/wlr_renderer.c +++ b/render/wlr_renderer.c @@ -1,6 +1,9 @@ +#define _POSIX_C_SOURCE 200809L #include +#include #include #include +#include #include #include #include @@ -9,6 +12,7 @@ #include #include #include +#include #include @@ -21,6 +25,7 @@ #include #endif // WLR_HAS_VULKAN_RENDERER +#include "backend/backend.h" #include "util/signal.h" #include "render/pixel_format.h" #include "render/wlr_renderer.h" @@ -197,17 +202,18 @@ bool wlr_renderer_read_pixels(struct wlr_renderer *r, uint32_t fmt, src_x, src_y, dst_x, dst_y, data); } -bool wlr_renderer_init_wl_display(struct wlr_renderer *r, +bool wlr_renderer_init_wl_shm(struct wlr_renderer *r, struct wl_display *wl_display) { - if (wl_display_init_shm(wl_display)) { - wlr_log(WLR_ERROR, "Failed to initialize shm"); + if (wl_display_init_shm(wl_display) != 0) { + wlr_log(WLR_ERROR, "Failed to initialize wl_shm"); return false; } size_t len; const uint32_t *formats = wlr_renderer_get_shm_texture_formats(r, &len); if (formats == NULL) { - wlr_log(WLR_ERROR, "Failed to initialize shm: cannot get formats"); + wlr_log(WLR_ERROR, "Failed to initialize wl_shm: " + "cannot get renderer formats"); return false; } @@ -224,11 +230,24 @@ bool wlr_renderer_init_wl_display(struct wlr_renderer *r, xrgb8888 = true; break; default: - wl_display_add_shm_format(wl_display, fmt); + if (wl_display_add_shm_format(wl_display, fmt) == NULL) { + wlr_log(WLR_ERROR, "Failed to initialize wl_shm: " + "failed to add format"); + return false; + } } } assert(argb8888 && xrgb8888); + return true; +} + +bool wlr_renderer_init_wl_display(struct wlr_renderer *r, + struct wl_display *wl_display) { + if (!wlr_renderer_init_wl_shm(r, wl_display)) { + return false; + } + if (wlr_renderer_get_dmabuf_texture_formats(r) != NULL) { if (wlr_renderer_get_drm_fd(r) >= 0) { if (wlr_drm_create(wl_display, r) == NULL) { @@ -296,10 +315,95 @@ struct wlr_renderer *renderer_autocreate_with_drm_fd(int drm_fd) { return NULL; } +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: %s", strerror(-devices_len)); + 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: %s", strerror(-devices_len)); + 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_renderer *wlr_renderer_autocreate(struct wlr_backend *backend) { + int drm_fd = -1; + int render_drm_fd = -1; + + // Allow the user to override the render node + const char *render_name = getenv("WLR_RENDER_DRM_DEVICE"); + if (render_name != NULL) { + wlr_log(WLR_INFO, + "Opening DRM render node '%s' from WLR_RENDER_DRM_DEVICE", + render_name); + render_drm_fd = open(render_name, O_RDWR | O_CLOEXEC); + if (render_drm_fd < 0) { + wlr_log_errno(WLR_ERROR, "Failed to open '%s'", render_name); + return NULL; + } + if (drmGetNodeTypeFromFd(render_drm_fd) != DRM_NODE_RENDER) { + wlr_log(WLR_ERROR, "'%s' is not a DRM render node", render_name); + close(render_drm_fd); + return NULL; + } + drm_fd = render_drm_fd; + } + + if (drm_fd < 0) { + drm_fd = wlr_backend_get_drm_fd(backend); + } + + // If the backend hasn't picked a DRM FD, but accepts DMA-BUFs, pick an + // arbitrary render node + uint32_t backend_caps = backend_get_buffer_caps(backend); + if (drm_fd < 0 && (backend_caps & WLR_BUFFER_CAP_DMABUF) != 0) { + render_drm_fd = open_drm_render_node(); + drm_fd = render_drm_fd; + } + // Note, drm_fd may be negative if unavailable - int drm_fd = wlr_backend_get_drm_fd(backend); - return renderer_autocreate_with_drm_fd(drm_fd); + struct wlr_renderer *renderer = renderer_autocreate_with_drm_fd(drm_fd); + + if (render_drm_fd >= 0) { + close(render_drm_fd); + } + + return renderer; } int wlr_renderer_get_drm_fd(struct wlr_renderer *r) { diff --git a/tinywl/meson.build b/tinywl/meson.build new file mode 100644 index 000000000..82d31d22f --- /dev/null +++ b/tinywl/meson.build @@ -0,0 +1,5 @@ +executable( + 'tinywl', + ['tinywl.c', protocols_client_header['xdg-shell']], + dependencies: wlroots, +) diff --git a/tinywl/tinywl.c b/tinywl/tinywl.c index b11549bea..a4fcc2626 100644 --- a/tinywl/tinywl.c +++ b/tinywl/tinywl.c @@ -1,4 +1,5 @@ #define _POSIX_C_SOURCE 200112L +#include #include #include #include @@ -7,17 +8,19 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include #include #include +#include #include +#include #include #include #include @@ -34,6 +37,8 @@ struct tinywl_server { struct wl_display *wl_display; struct wlr_backend *backend; struct wlr_renderer *renderer; + struct wlr_allocator *allocator; + struct wlr_scene *scene; struct wlr_xdg_shell *xdg_shell; struct wl_listener new_xdg_surface; @@ -73,13 +78,13 @@ struct tinywl_output { struct tinywl_view { struct wl_list link; struct tinywl_server *server; - struct wlr_xdg_surface *xdg_surface; + struct wlr_xdg_toplevel *xdg_toplevel; + struct wlr_scene_node *scene_node; struct wl_listener map; struct wl_listener unmap; struct wl_listener destroy; struct wl_listener request_move; struct wl_listener request_resize; - bool mapped; int x, y; }; @@ -112,20 +117,22 @@ static void focus_view(struct tinywl_view *view, struct wlr_surface *surface) { */ struct wlr_xdg_surface *previous = wlr_xdg_surface_from_wlr_surface( seat->keyboard_state.focused_surface); - wlr_xdg_toplevel_set_activated(previous, false); + assert(previous->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); + wlr_xdg_toplevel_set_activated(previous->toplevel, false); } struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat); /* Move the view to the front */ + wlr_scene_node_raise_to_top(view->scene_node); wl_list_remove(&view->link); wl_list_insert(&server->views, &view->link); /* Activate the new surface */ - wlr_xdg_toplevel_set_activated(view->xdg_surface, true); + wlr_xdg_toplevel_set_activated(view->xdg_toplevel, true); /* * Tell the seat to have the keyboard enter this surface. wlroots will keep * track of this and automatically send key events to the appropriate * clients without additional work on your part. */ - wlr_seat_keyboard_notify_enter(seat, view->xdg_surface->surface, + wlr_seat_keyboard_notify_enter(seat, view->xdg_toplevel->base->surface, keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); } @@ -164,14 +171,9 @@ static bool handle_keybinding(struct tinywl_server *server, xkb_keysym_t sym) { if (wl_list_length(&server->views) < 2) { break; } - struct tinywl_view *current_view = wl_container_of( - server->views.next, current_view, link); struct tinywl_view *next_view = wl_container_of( - current_view->link.next, next_view, link); - focus_view(next_view, next_view->xdg_surface->surface); - /* Move the previous view to the end of the list */ - wl_list_remove(¤t_view->link); - wl_list_insert(server->views.prev, ¤t_view->link); + server->views.prev, next_view, link); + focus_view(next_view, next_view->xdg_toplevel->base->surface); break; default: return false; @@ -197,7 +199,8 @@ 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 == WL_KEYBOARD_KEY_STATE_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++) { @@ -308,52 +311,32 @@ static void seat_request_set_selection(struct wl_listener *listener, void *data) wlr_seat_set_selection(server->seat, event->source, event->serial); } -static bool view_at(struct tinywl_view *view, - double lx, double ly, struct wlr_surface **surface, - double *sx, double *sy) { - /* - * XDG toplevels may have nested surfaces, such as popup windows for context - * menus or tooltips. This function tests if any of those are underneath the - * coordinates lx and ly (in output Layout Coordinates). If so, it sets the - * surface pointer to that wlr_surface and the sx and sy coordinates to the - * coordinates relative to that surface's top-left corner. - */ - double view_sx = lx - view->x; - double view_sy = ly - view->y; - - double _sx, _sy; - struct wlr_surface *_surface = NULL; - _surface = wlr_xdg_surface_surface_at( - view->xdg_surface, view_sx, view_sy, &_sx, &_sy); - - if (_surface != NULL) { - *sx = _sx; - *sy = _sy; - *surface = _surface; - return true; - } - - return false; -} - static struct tinywl_view *desktop_view_at( struct tinywl_server *server, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { - /* This iterates over all of our surfaces and attempts to find one under the - * cursor. This relies on server->views being ordered from top-to-bottom. */ - struct tinywl_view *view; - wl_list_for_each(view, &server->views, link) { - if (view_at(view, lx, ly, surface, sx, sy)) { - return view; - } + /* This returns the topmost node in the scene at the given layout coords. + * we only care about surface nodes as we are specifically looking for a + * surface in the surface tree of a tinywl_view. */ + struct wlr_scene_node *node = wlr_scene_node_at( + &server->scene->node, lx, ly, sx, sy); + if (node == NULL || node->type != WLR_SCENE_NODE_SURFACE) { + return NULL; } - return NULL; + *surface = wlr_scene_surface_from_node(node)->surface; + /* Find the node corresponding to the tinywl_view at the root of this + * surface tree, it is the only one for which we set the data field. */ + while (node != NULL && node->data == NULL) { + node = node->parent; + } + return node->data; } static void process_cursor_move(struct tinywl_server *server, uint32_t time) { /* Move the grabbed view to the new position. */ - server->grabbed_view->x = server->cursor->x - server->grab_x; - server->grabbed_view->y = server->cursor->y - server->grab_y; + struct tinywl_view *view = server->grabbed_view; + view->x = server->cursor->x - server->grab_x; + view->y = server->cursor->y - server->grab_y; + wlr_scene_node_set_position(view->scene_node, view->x, view->y); } static void process_cursor_resize(struct tinywl_server *server, uint32_t time) { @@ -399,13 +382,14 @@ static void process_cursor_resize(struct tinywl_server *server, uint32_t time) { } struct wlr_box geo_box; - wlr_xdg_surface_get_geometry(view->xdg_surface, &geo_box); + wlr_xdg_surface_get_geometry(view->xdg_toplevel->base, &geo_box); view->x = new_left - geo_box.x; view->y = new_top - geo_box.y; + wlr_scene_node_set_position(view->scene_node, view->x, view->y); int new_width = new_right - new_left; int new_height = new_bottom - new_top; - wlr_xdg_toplevel_set_size(view->xdg_surface, new_width, new_height); + wlr_xdg_toplevel_set_size(view->xdg_toplevel, new_width, new_height); } static void process_cursor_motion(struct tinywl_server *server, uint32_t time) { @@ -493,7 +477,7 @@ static void server_cursor_button(struct wl_listener *listener, void *data) { wlr_seat_pointer_notify_button(server->seat, event->time_msec, event->button, event->state); double sx, sy; - struct wlr_surface *surface; + struct wlr_surface *surface = NULL; struct tinywl_view *view = desktop_view_at(server, server->cursor->x, server->cursor->y, &surface, &sx, &sy); if (event->state == WLR_BUTTON_RELEASED) { @@ -528,131 +512,21 @@ static void server_cursor_frame(struct wl_listener *listener, void *data) { wlr_seat_pointer_notify_frame(server->seat); } -/* Used to move all of the data necessary to render a surface from the top-level - * frame handler to the per-surface render function. */ -struct render_data { - struct wlr_output *output; - struct wlr_renderer *renderer; - struct tinywl_view *view; - struct timespec *when; -}; - -static void render_surface(struct wlr_surface *surface, - int sx, int sy, void *data) { - /* This function is called for every surface that needs to be rendered. */ - struct render_data *rdata = data; - struct tinywl_view *view = rdata->view; - struct wlr_output *output = rdata->output; - - /* We first obtain a wlr_texture, which is a GPU resource. wlroots - * automatically handles negotiating these with the client. The underlying - * resource could be an opaque handle passed from the client, or the client - * could have sent a pixel buffer which we copied to the GPU, or a few other - * means. You don't have to worry about this, wlroots takes care of it. */ - struct wlr_texture *texture = wlr_surface_get_texture(surface); - if (texture == NULL) { - return; - } - - /* The view has a position in layout coordinates. If you have two displays, - * one next to the other, both 1080p, a view on the rightmost display might - * have layout coordinates of 2000,100. We need to translate that to - * output-local coordinates, or (2000 - 1920). */ - double ox = 0, oy = 0; - wlr_output_layout_output_coords( - view->server->output_layout, output, &ox, &oy); - ox += view->x + sx, oy += view->y + sy; - - /* We also have to apply the scale factor for HiDPI outputs. This is only - * part of the puzzle, TinyWL does not fully support HiDPI. */ - struct wlr_box box = { - .x = ox * output->scale, - .y = oy * output->scale, - .width = surface->current.width * output->scale, - .height = surface->current.height * output->scale, - }; - - /* - * Those familiar with OpenGL are also familiar with the role of matricies - * in graphics programming. We need to prepare a matrix to render the view - * with. wlr_matrix_project_box is a helper which takes a box with a desired - * x, y coordinates, width and height, and an output geometry, then - * prepares an orthographic projection and multiplies the necessary - * transforms to produce a model-view-projection matrix. - * - * Naturally you can do this any way you like, for example to make a 3D - * compositor. - */ - float matrix[9]; - enum wl_output_transform transform = - wlr_output_transform_invert(surface->current.transform); - wlr_matrix_project_box(matrix, &box, transform, 0, - output->transform_matrix); - - /* This takes our matrix, the texture, and an alpha, and performs the actual - * rendering on the GPU. */ - wlr_render_texture_with_matrix(rdata->renderer, texture, matrix, 1); - - /* This lets the client know that we've displayed that frame and it can - * prepare another one now if it likes. */ - wlr_surface_send_frame_done(surface, rdata->when); -} - static void output_frame(struct wl_listener *listener, void *data) { /* This function is called every time an output is ready to display a frame, * generally at the output's refresh rate (e.g. 60Hz). */ - struct tinywl_output *output = - wl_container_of(listener, output, frame); - struct wlr_renderer *renderer = output->server->renderer; + struct tinywl_output *output = wl_container_of(listener, output, frame); + struct wlr_scene *scene = output->server->scene; + + struct wlr_scene_output *scene_output = wlr_scene_get_scene_output( + scene, output->wlr_output); + + /* Render the scene if needed and commit the output */ + wlr_scene_output_commit(scene_output); struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - - /* wlr_output_attach_render makes the OpenGL context current. */ - if (!wlr_output_attach_render(output->wlr_output, NULL)) { - return; - } - /* The "effective" resolution can change if you rotate your outputs. */ - int width, height; - wlr_output_effective_resolution(output->wlr_output, &width, &height); - /* Begin the renderer (calls glViewport and some other GL sanity checks) */ - wlr_renderer_begin(renderer, width, height); - - float color[4] = {0.3, 0.3, 0.3, 1.0}; - wlr_renderer_clear(renderer, color); - - /* Each subsequent window we render is rendered on top of the last. Because - * our view list is ordered front-to-back, we iterate over it backwards. */ - struct tinywl_view *view; - wl_list_for_each_reverse(view, &output->server->views, link) { - if (!view->mapped) { - /* An unmapped view should not be rendered. */ - continue; - } - struct render_data rdata = { - .output = output->wlr_output, - .view = view, - .renderer = renderer, - .when = &now, - }; - /* This calls our render_surface function for each surface among the - * xdg_surface's toplevel and popups. */ - wlr_xdg_surface_for_each_surface(view->xdg_surface, - render_surface, &rdata); - } - - /* Hardware cursors are rendered by the GPU on a separate plane, and can be - * moved around without re-rendering what's beneath them - which is more - * efficient. However, not all hardware supports hardware cursors. For this - * reason, wlroots provides a software fallback, which we ask it to render - * here. wlr_cursor handles configuring hardware vs software cursors for you, - * and this function is a no-op when hardware cursors are in use. */ - wlr_output_render_software_cursors(output->wlr_output, NULL); - - /* Conclude rendering and swap the buffers, showing the final frame - * on-screen. */ - wlr_renderer_end(renderer); - wlr_output_commit(output->wlr_output); + wlr_scene_output_send_frame_done(scene_output, &now); } static void server_new_output(struct wl_listener *listener, void *data) { @@ -662,6 +536,10 @@ static void server_new_output(struct wl_listener *listener, void *data) { wl_container_of(listener, server, new_output); struct wlr_output *wlr_output = data; + /* Configures the output created by the backend to use our allocator + * and our renderer. Must be done once, before commiting the output */ + wlr_output_init_render(wlr_output, server->allocator, server->renderer); + /* Some backends don't have modes. DRM+KMS does, and we need to set a mode * before we can use the output. The mode is a tuple of (width, height, * refresh rate), and each monitor supports only a specific set of modes. We @@ -698,23 +576,32 @@ static void server_new_output(struct wl_listener *listener, void *data) { wlr_output_layout_add_auto(server->output_layout, wlr_output); } -static void xdg_surface_map(struct wl_listener *listener, void *data) { +static void xdg_toplevel_map(struct wl_listener *listener, void *data) { /* Called when the surface is mapped, or ready to display on-screen. */ struct tinywl_view *view = wl_container_of(listener, view, map); - view->mapped = true; - focus_view(view, view->xdg_surface->surface); + + wl_list_insert(&view->server->views, &view->link); + + focus_view(view, view->xdg_toplevel->base->surface); } -static void xdg_surface_unmap(struct wl_listener *listener, void *data) { +static void xdg_toplevel_unmap(struct wl_listener *listener, void *data) { /* Called when the surface is unmapped, and should no longer be shown. */ struct tinywl_view *view = wl_container_of(listener, view, unmap); - view->mapped = false; + + wl_list_remove(&view->link); } -static void xdg_surface_destroy(struct wl_listener *listener, void *data) { +static void xdg_toplevel_destroy(struct wl_listener *listener, void *data) { /* Called when the surface is destroyed and should never be shown again. */ struct tinywl_view *view = wl_container_of(listener, view, destroy); - wl_list_remove(&view->link); + + wl_list_remove(&view->map.link); + wl_list_remove(&view->unmap.link); + wl_list_remove(&view->destroy.link); + wl_list_remove(&view->request_move.link); + wl_list_remove(&view->request_resize.link); + free(view); } @@ -726,7 +613,8 @@ static void begin_interactive(struct tinywl_view *view, struct tinywl_server *server = view->server; struct wlr_surface *focused_surface = server->seat->pointer_state.focused_surface; - if (view->xdg_surface->surface != focused_surface) { + if (view->xdg_toplevel->base->surface != + wlr_surface_get_root_surface(focused_surface)) { /* Deny move/resize requests from unfocused clients. */ return; } @@ -738,10 +626,12 @@ static void begin_interactive(struct tinywl_view *view, server->grab_y = server->cursor->y - view->y; } else { struct wlr_box geo_box; - wlr_xdg_surface_get_geometry(view->xdg_surface, &geo_box); + wlr_xdg_surface_get_geometry(view->xdg_toplevel->base, &geo_box); - double border_x = (view->x + geo_box.x) + ((edges & WLR_EDGE_RIGHT) ? geo_box.width : 0); - double border_y = (view->y + geo_box.y) + ((edges & WLR_EDGE_BOTTOM) ? geo_box.height : 0); + double border_x = (view->x + geo_box.x) + + ((edges & WLR_EDGE_RIGHT) ? geo_box.width : 0); + double border_y = (view->y + geo_box.y) + + ((edges & WLR_EDGE_BOTTOM) ? geo_box.height : 0); server->grab_x = server->cursor->x - border_x; server->grab_y = server->cursor->y - border_y; @@ -782,22 +672,38 @@ static void server_new_xdg_surface(struct wl_listener *listener, void *data) { struct tinywl_server *server = wl_container_of(listener, server, new_xdg_surface); struct wlr_xdg_surface *xdg_surface = data; - if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) { + + /* We must add xdg popups to the scene graph so they get rendered. The + * wlroots scene graph provides a helper for this, but to use it we must + * provide the proper parent scene node of the xdg popup. To enable this, + * we always set the user data field of xdg_surfaces to the corresponding + * scene node. */ + if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { + struct wlr_xdg_surface *parent = wlr_xdg_surface_from_wlr_surface( + xdg_surface->popup->parent); + struct wlr_scene_node *parent_node = parent->data; + xdg_surface->data = wlr_scene_xdg_surface_create( + parent_node, xdg_surface); return; } + assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); /* Allocate a tinywl_view for this surface */ struct tinywl_view *view = calloc(1, sizeof(struct tinywl_view)); view->server = server; - view->xdg_surface = xdg_surface; + view->xdg_toplevel = xdg_surface->toplevel; + view->scene_node = wlr_scene_xdg_surface_create( + &view->server->scene->node, view->xdg_toplevel->base); + view->scene_node->data = view; + xdg_surface->data = view->scene_node; /* Listen to the various events it can emit */ - view->map.notify = xdg_surface_map; + view->map.notify = xdg_toplevel_map; wl_signal_add(&xdg_surface->events.map, &view->map); - view->unmap.notify = xdg_surface_unmap; + view->unmap.notify = xdg_toplevel_unmap; wl_signal_add(&xdg_surface->events.unmap, &view->unmap); - view->destroy.notify = xdg_surface_destroy; + view->destroy.notify = xdg_toplevel_destroy; wl_signal_add(&xdg_surface->events.destroy, &view->destroy); /* cotd */ @@ -806,9 +712,6 @@ static void server_new_xdg_surface(struct wl_listener *listener, void *data) { wl_signal_add(&toplevel->events.request_move, &view->request_move); view->request_resize.notify = xdg_toplevel_request_resize; wl_signal_add(&toplevel->events.request_resize, &view->request_resize); - - /* Add it to the list of views. */ - wl_list_insert(&server->views, &view->link); } int main(int argc, char *argv[]) { @@ -841,19 +744,29 @@ int main(int argc, char *argv[]) { * if an X11 server is running. */ server.backend = wlr_backend_autocreate(server.wl_display); - /* If we don't provide a renderer, autocreate makes a GLES2 renderer for us. + /* Autocreates a renderer, either Pixman, GLES2 or Vulkan for us. The user + * can also specify a renderer using the WLR_RENDERER env var. * The renderer is responsible for defining the various pixel formats it * supports for shared memory, this configures that for clients. */ - server.renderer = wlr_backend_get_renderer(server.backend); + server.renderer = wlr_renderer_autocreate(server.backend); wlr_renderer_init_wl_display(server.renderer, server.wl_display); + /* Autocreates an allocator for us. + * The allocator is the bridge between the renderer and the backend. It + * handles the buffer creation, allowing wlroots to render onto the + * screen */ + server.allocator = wlr_allocator_autocreate(server.backend, + server.renderer); + /* This creates some hands-off wlroots interfaces. The compositor is - * necessary for clients to allocate surfaces and the data device manager + * necessary for clients to allocate surfaces, the subcompositor allows to + * assign the role of subsurfaces to surfaces and the data device manager * handles the clipboard. Each of these wlroots interfaces has room for you * to dig your fingers in and play with their behavior if you want. Note that * the clients cannot set the selection directly without compositor approval, * see the handling of the request_set_selection event below.*/ wlr_compositor_create(server.wl_display, server.renderer); + wlr_subcompositor_create(server.wl_display); wlr_data_device_manager_create(server.wl_display); /* Creates an output layout, which a wlroots utility for working with an @@ -866,9 +779,17 @@ int main(int argc, char *argv[]) { server.new_output.notify = server_new_output; wl_signal_add(&server.backend->events.new_output, &server.new_output); - /* Set up our list of views and the xdg-shell. The xdg-shell is a Wayland - * protocol which is used for application windows. For more detail on - * shells, refer to my article: + /* Create a scene graph. This is a wlroots abstraction that handles all + * rendering and damage tracking. All the compositor author needs to do + * is add things that should be rendered to the scene graph at the proper + * positions and then call wlr_scene_output_commit() to render a frame if + * necessary. + */ + server.scene = wlr_scene_create(); + wlr_scene_attach_output_layout(server.scene, server.output_layout); + + /* Set up the xdg-shell. The xdg-shell is a Wayland protocol which is used + * for application windows. For more detail on shells, refer to my article: * * https://drewdevault.com/2018/07/29/Wayland-shells.html */ diff --git a/types/data_device/wlr_data_device.c b/types/data_device/wlr_data_device.c index 6cd84ec0a..ceaf80e72 100644 --- a/types/data_device/wlr_data_device.c +++ b/types/data_device/wlr_data_device.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include diff --git a/types/data_device/wlr_drag.c b/types/data_device/wlr_drag.c index 53bf4066a..9865d9309 100644 --- a/types/data_device/wlr_drag.c +++ b/types/data_device/wlr_drag.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -138,9 +139,6 @@ static void drag_destroy(struct wlr_drag *drag) { } } - // We issue destroy after ending the grab to allow focus changes. - wlr_signal_emit_safe(&drag->events.destroy, drag); - if (drag->started) { drag_set_focus(drag, NULL, 0, 0); @@ -148,6 +146,13 @@ static void drag_destroy(struct wlr_drag *drag) { drag->seat->drag = NULL; } + // We issue destroy after ending the grab to allow focus changes. + // Furthermore, we wait until after clearing the drag focus in order + // to ensure that the wl_data_device.leave is sent before emitting the + // signal. This allows e.g. wl_pointer.enter to be sent in the destroy + // signal handler. + wlr_signal_emit_safe(&drag->events.destroy, drag); + if (drag->source) { wl_list_remove(&drag->source_destroy.link); } diff --git a/types/meson.build b/types/meson.build index 6bf20a6e1..315031cea 100644 --- a/types/meson.build +++ b/types/meson.build @@ -10,6 +10,8 @@ wlr_files += files( 'scene/subsurface_tree.c', 'scene/wlr_scene.c', 'scene/output_layout.c', + 'scene/xdg_shell.c', + 'scene/layer_shell_v1.c', 'seat/wlr_seat_keyboard.c', 'seat/wlr_seat_pointer.c', 'seat/wlr_seat_touch.c', @@ -58,7 +60,8 @@ wlr_files += files( 'wlr_relative_pointer_v1.c', 'wlr_screencopy_v1.c', 'wlr_server_decoration.c', - 'wlr_surface.c', + 'wlr_session_lock_v1.c', + 'wlr_subcompositor.c', 'wlr_switch.c', 'wlr_tablet_pad.c', 'wlr_tablet_tool.c', diff --git a/types/output/cursor.c b/types/output/cursor.c index 0a6cbb757..f3055c5c4 100644 --- a/types/output/cursor.c +++ b/types/output/cursor.c @@ -3,15 +3,34 @@ #include #include #include +#include #include -#include #include -#include "backend/backend.h" #include "render/allocator/allocator.h" #include "render/swapchain.h" #include "types/wlr_output.h" #include "util/signal.h" +static bool output_set_hardware_cursor(struct wlr_output *output, + struct wlr_buffer *buffer, int hotspot_x, int hotspot_y) { + if (!output->impl->set_cursor) { + return false; + } + + if (!output->impl->set_cursor(output, buffer, hotspot_x, hotspot_y)) { + return false; + } + + wlr_buffer_unlock(output->cursor_front_buffer); + output->cursor_front_buffer = NULL; + + if (buffer != NULL) { + output->cursor_front_buffer = wlr_buffer_lock(buffer); + } + + return true; +} + static void output_cursor_damage_whole(struct wlr_output_cursor *cursor); void wlr_output_lock_software_cursors(struct wlr_output *output, bool lock) { @@ -26,8 +45,7 @@ void wlr_output_lock_software_cursors(struct wlr_output *output, bool lock) { output->software_cursor_locks); if (output->software_cursor_locks > 0 && output->hardware_cursor != NULL) { - assert(output->impl->set_cursor); - output->impl->set_cursor(output, NULL, 0, 0); + output_set_hardware_cursor(output, NULL, 0, 0); output_cursor_damage_whole(output->hardware_cursor); output->hardware_cursor = NULL; } @@ -38,7 +56,7 @@ void wlr_output_lock_software_cursors(struct wlr_output *output, bool lock) { } static void output_scissor(struct wlr_output *output, pixman_box32_t *rect) { - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; assert(renderer); struct wlr_box box = { @@ -71,8 +89,7 @@ static void output_cursor_get_box(struct wlr_output_cursor *cursor, static void output_cursor_render(struct wlr_output_cursor *cursor, pixman_region32_t *damage) { - struct wlr_renderer *renderer = - wlr_backend_get_renderer(cursor->output->backend); + struct wlr_renderer *renderer = cursor->output->renderer; assert(renderer); struct wlr_texture *texture = cursor->texture; @@ -195,7 +212,7 @@ static void output_cursor_update_visible(struct wlr_output_cursor *cursor) { } static struct wlr_drm_format *output_pick_cursor_format(struct wlr_output *output) { - struct wlr_allocator *allocator = backend_get_allocator(output->backend); + struct wlr_allocator *allocator = output->allocator; assert(allocator != NULL); const struct wlr_drm_format_set *display_formats = NULL; @@ -208,7 +225,7 @@ static struct wlr_drm_format *output_pick_cursor_format(struct wlr_output *outpu } } - return output_pick_format(output, display_formats); + return output_pick_format(output, display_formats, DRM_FORMAT_ARGB8888); } static struct wlr_buffer *render_cursor_buffer(struct wlr_output_cursor *cursor) { @@ -226,17 +243,9 @@ static struct wlr_buffer *render_cursor_buffer(struct wlr_output_cursor *cursor) return NULL; } - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); - if (renderer == NULL) { - wlr_log(WLR_ERROR, "Failed to get backend renderer"); - return NULL; - } - - struct wlr_allocator *allocator = backend_get_allocator(output->backend); - if (allocator == NULL) { - wlr_log(WLR_ERROR, "Failed to get backend allocator"); - return NULL; - } + struct wlr_allocator *allocator = output->allocator; + struct wlr_renderer *renderer = output->renderer; + assert(allocator != NULL && renderer != NULL); int width = texture->width; int height = texture->height; @@ -355,14 +364,10 @@ static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) { wlr_output_transform_invert(output->transform), buffer ? buffer->width : 0, buffer ? buffer->height : 0); - bool ok = output->impl->set_cursor(cursor->output, buffer, - hotspot.x, hotspot.y); + bool ok = output_set_hardware_cursor(output, buffer, hotspot.x, hotspot.y); + wlr_buffer_unlock(buffer); if (ok) { - wlr_buffer_unlock(output->cursor_front_buffer); - output->cursor_front_buffer = buffer; output->hardware_cursor = cursor; - } else { - wlr_buffer_unlock(buffer); } return ok; } @@ -370,8 +375,7 @@ static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) { bool wlr_output_cursor_set_image(struct wlr_output_cursor *cursor, const uint8_t *pixels, int32_t stride, uint32_t width, uint32_t height, int32_t hotspot_x, int32_t hotspot_y) { - struct wlr_renderer *renderer = - wlr_backend_get_renderer(cursor->output->backend); + struct wlr_renderer *renderer = cursor->output->renderer; if (!renderer) { // if the backend has no renderer, we can't draw a cursor, but this is // actually okay, for ex. with the noop backend @@ -476,9 +480,8 @@ void wlr_output_cursor_set_surface(struct wlr_output_cursor *cursor, wlr_output_transform_invert(cursor->output->transform), buffer ? buffer->width : 0, buffer ? buffer->height : 0); - assert(cursor->output->impl->set_cursor); - cursor->output->impl->set_cursor(cursor->output, - buffer, hotspot.x, hotspot.y); + output_set_hardware_cursor(cursor->output, buffer, + hotspot.x, hotspot.y); } return; } @@ -501,8 +504,7 @@ void wlr_output_cursor_set_surface(struct wlr_output_cursor *cursor, cursor->height = 0; if (cursor->output->hardware_cursor == cursor) { - assert(cursor->output->impl->set_cursor); - cursor->output->impl->set_cursor(cursor->output, NULL, 0, 0); + output_set_hardware_cursor(cursor->output, NULL, 0, 0); } } } @@ -563,9 +565,7 @@ void wlr_output_cursor_destroy(struct wlr_output_cursor *cursor) { wlr_signal_emit_safe(&cursor->events.destroy, cursor); if (cursor->output->hardware_cursor == cursor) { // If this cursor was the hardware cursor, disable it - if (cursor->output->impl->set_cursor) { - cursor->output->impl->set_cursor(cursor->output, NULL, 0, 0); - } + output_set_hardware_cursor(cursor->output, NULL, 0, 0); cursor->output->hardware_cursor = NULL; } wlr_texture_destroy(cursor->texture); diff --git a/types/output/output.c b/types/output/output.c index 7d092abd9..5ed7e01d5 100644 --- a/types/output/output.c +++ b/types/output/output.c @@ -1,16 +1,19 @@ #define _POSIX_C_SOURCE 200809L #include +#include +#include #include #include +#include #include -#include #include +#include "render/allocator/allocator.h" #include "render/swapchain.h" #include "types/wlr_output.h" #include "util/global.h" #include "util/signal.h" -#define OUTPUT_VERSION 3 +#define OUTPUT_VERSION 4 static void send_geometry(struct wl_resource *resource) { struct wlr_output *output = wlr_output_from_resource(resource); @@ -40,6 +43,23 @@ static void send_scale(struct wl_resource *resource) { } } +static void send_name(struct wl_resource *resource) { + struct wlr_output *output = wlr_output_from_resource(resource); + uint32_t version = wl_resource_get_version(resource); + if (version >= WL_OUTPUT_NAME_SINCE_VERSION) { + wl_output_send_name(resource, output->name); + } +} + +static void send_description(struct wl_resource *resource) { + struct wlr_output *output = wlr_output_from_resource(resource); + uint32_t version = wl_resource_get_version(resource); + if (output->description != NULL && + version >= WL_OUTPUT_DESCRIPTION_SINCE_VERSION) { + wl_output_send_description(resource, output->description); + } +} + static void send_done(struct wl_resource *resource) { uint32_t version = wl_resource_get_version(resource); if (version >= WL_OUTPUT_DONE_SINCE_VERSION) { @@ -84,6 +104,8 @@ static void output_bind(struct wl_client *wl_client, void *data, send_geometry(resource); send_current_mode(resource); send_scale(resource); + send_name(resource); + send_description(resource); send_done(resource); struct wlr_output_event_bind evt = { @@ -118,7 +140,7 @@ void wlr_output_destroy_global(struct wlr_output *output) { wl_list_init(wl_resource_get_link(resource)); } - wlr_global_destroy_safe(output->global, output->display); + wlr_global_destroy_safe(output->global); output->global = NULL; } @@ -128,10 +150,7 @@ static void schedule_done_handle_idle_timer(void *data) { struct wl_resource *resource; wl_resource_for_each(resource, &output->resources) { - uint32_t version = wl_resource_get_version(resource); - if (version >= WL_OUTPUT_DONE_SINCE_VERSION) { - wl_output_send_done(resource); - } + send_done(resource); } } @@ -296,6 +315,16 @@ void wlr_output_enable_adaptive_sync(struct wlr_output *output, bool enabled) { output->pending.adaptive_sync_enabled = enabled; } +void wlr_output_set_render_format(struct wlr_output *output, uint32_t format) { + if (output->render_format == format) { + output->pending.committed &= ~WLR_OUTPUT_STATE_RENDER_FORMAT; + return; + } + + output->pending.committed |= WLR_OUTPUT_STATE_RENDER_FORMAT; + output->pending.render_format = format; +} + void wlr_output_set_subpixel(struct wlr_output *output, enum wl_output_subpixel subpixel) { if (output->subpixel == subpixel) { @@ -311,6 +340,13 @@ void wlr_output_set_subpixel(struct wlr_output *output, wlr_output_schedule_done(output); } +void wlr_output_set_name(struct wlr_output *output, const char *name) { + assert(output->global == NULL); + + free(output->name); + output->name = strdup(name); +} + void wlr_output_set_description(struct wlr_output *output, const char *desc) { if (output->description != NULL && desc != NULL && strcmp(output->description, desc) == 0) { @@ -324,6 +360,12 @@ void wlr_output_set_description(struct wlr_output *output, const char *desc) { output->description = NULL; } + struct wl_resource *resource; + wl_resource_for_each(resource, &output->resources) { + send_description(resource); + } + wlr_output_schedule_done(output); + wlr_signal_emit_safe(&output->events.description, output); } @@ -343,6 +385,7 @@ void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend, output->impl = impl; output->display = display; wl_list_init(&output->modes); + output->render_format = DRM_FORMAT_XRGB8888; output->transform = WL_OUTPUT_TRANSFORM_NORMAL; output->scale = 1; output->commit_seq = 0; @@ -379,9 +422,6 @@ void wlr_output_destroy(struct wlr_output *output) { return; } - wlr_buffer_unlock(output->front_buffer); - output->front_buffer = NULL; - wl_list_remove(&output->display_destroy.link); wlr_output_destroy_global(output); output_clear_back_buffer(output); @@ -409,6 +449,7 @@ void wlr_output_destroy(struct wlr_output *output) { wl_event_source_remove(output->idle_done); } + free(output->name); free(output->description); pixman_region32_fini(&output->pending.damage); @@ -542,6 +583,22 @@ static bool output_basic_test(struct wlr_output *output) { } } + if (output->pending.committed & WLR_OUTPUT_STATE_RENDER_FORMAT) { + struct wlr_allocator *allocator = output->allocator; + assert(allocator != NULL); + + const struct wlr_drm_format_set *display_formats = + wlr_output_get_primary_formats(output, allocator->buffer_caps); + struct wlr_drm_format *format = output_pick_format(output, display_formats, + output->pending.render_format); + if (format == NULL) { + wlr_log(WLR_ERROR, "Failed to pick primary buffer format for output"); + return false; + } + + free(format); + } + bool enabled = output->enabled; if (output->pending.committed & WLR_OUTPUT_STATE_ENABLED) { enabled = output->pending.enabled; @@ -569,6 +626,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_RENDER_FORMAT) { + wlr_log(WLR_DEBUG, "Tried to set format for 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; @@ -578,6 +639,9 @@ static bool output_basic_test(struct wlr_output *output) { } bool wlr_output_test(struct wlr_output *output) { + bool had_buffer = output->pending.committed & WLR_OUTPUT_STATE_BUFFER; + bool success; + if (!output_basic_test(output)) { return false; } @@ -587,7 +651,13 @@ bool wlr_output_test(struct wlr_output *output) { if (!output->impl->test) { return true; } - return output->impl->test(output); + + success = output->impl->test(output); + + if (!had_buffer) { + output_clear_back_buffer(output); + } + return success; } bool wlr_output_commit(struct wlr_output *output) { @@ -642,6 +712,10 @@ bool wlr_output_commit(struct wlr_output *output) { } } + if (output->pending.committed & WLR_OUTPUT_STATE_RENDER_FORMAT) { + output->render_format = output->pending.render_format; + } + output->commit_seq++; bool scale_updated = output->pending.committed & WLR_OUTPUT_STATE_SCALE; @@ -669,13 +743,13 @@ bool wlr_output_commit(struct wlr_output *output) { wlr_output_schedule_done(output); } - // Unset the front-buffer when a new buffer will replace it or when the - // output is getting disabled - if ((output->pending.committed & WLR_OUTPUT_STATE_BUFFER) || - ((output->pending.committed & WLR_OUTPUT_STATE_ENABLED) && - !output->pending.enabled)) { - wlr_buffer_unlock(output->front_buffer); - output->front_buffer = NULL; + // Destroy the swapchains when an output is disabled + if ((output->pending.committed & WLR_OUTPUT_STATE_ENABLED) && + !output->pending.enabled) { + wlr_swapchain_destroy(output->swapchain); + output->swapchain = NULL; + wlr_swapchain_destroy(output->cursor_swapchain); + output->cursor_swapchain = NULL; } if (output->pending.committed & WLR_OUTPUT_STATE_BUFFER) { @@ -685,8 +759,6 @@ bool wlr_output_commit(struct wlr_output *output) { if (back_buffer != NULL) { wlr_swapchain_set_buffer_submitted(output->swapchain, back_buffer); - wlr_buffer_unlock(output->front_buffer); - output->front_buffer = back_buffer; } uint32_t committed = output->pending.committed; @@ -696,9 +768,14 @@ bool wlr_output_commit(struct wlr_output *output) { .output = output, .committed = committed, .when = &now, + .buffer = back_buffer, }; wlr_signal_emit_safe(&output->events.commit, &event); + if (back_buffer != NULL) { + wlr_buffer_unlock(back_buffer); + } + return true; } @@ -819,3 +896,21 @@ void wlr_output_damage_whole(struct wlr_output *output) { pixman_region32_fini(&damage); } + +const struct wlr_drm_format_set *wlr_output_get_primary_formats( + struct wlr_output *output, uint32_t buffer_caps) { + if (!output->impl->get_primary_formats) { + return NULL; + } + + const struct wlr_drm_format_set *formats = + output->impl->get_primary_formats(output, buffer_caps); + if (formats == NULL) { + wlr_log(WLR_ERROR, "Failed to get primary display formats"); + + static const struct wlr_drm_format_set empty_format_set = {0}; + return &empty_format_set; + } + + return formats; +} diff --git a/types/output/render.c b/types/output/render.c index b37b8ea12..985b93a98 100644 --- a/types/output/render.c +++ b/types/output/render.c @@ -9,8 +9,33 @@ #include "render/drm_format_set.h" #include "render/swapchain.h" #include "render/wlr_renderer.h" +#include "render/pixel_format.h" #include "types/wlr_output.h" +bool wlr_output_init_render(struct wlr_output *output, + struct wlr_allocator *allocator, struct wlr_renderer *renderer) { + assert(output->allocator == NULL && allocator != NULL); + assert(output->renderer == NULL && renderer != NULL); + + uint32_t backend_caps = backend_get_buffer_caps(output->backend); + uint32_t renderer_caps = renderer_get_render_buffer_caps(renderer); + + if (!(backend_caps & allocator->buffer_caps)) { + wlr_log(WLR_ERROR, "output backend and allocator buffer capabilities " + "don't match"); + return false; + } else if (!(renderer_caps & allocator->buffer_caps)) { + wlr_log(WLR_ERROR, "renderer and allocator buffer capabilities " + "don't match"); + return false; + } + + output->allocator = allocator; + output->renderer = renderer; + + return true; +} + /** * Ensure the output has a suitable swapchain. The swapchain is re-created if * necessary. @@ -23,39 +48,40 @@ static bool output_create_swapchain(struct wlr_output *output, int width, height; output_pending_resolution(output, &width, &height); - if (output->swapchain != NULL && output->swapchain->width == width && - output->swapchain->height == height && - (allow_modifiers || output->swapchain->format->len == 0)) { - return true; - } + struct wlr_allocator *allocator = output->allocator; + assert(allocator != NULL); - struct wlr_allocator *allocator = backend_get_allocator(output->backend); - if (allocator == NULL) { - wlr_log(WLR_ERROR, "Failed to get backend allocator"); - return false; - } - - const struct wlr_drm_format_set *display_formats = NULL; - if (output->impl->get_primary_formats) { - display_formats = - output->impl->get_primary_formats(output, allocator->buffer_caps); - if (display_formats == NULL) { - wlr_log(WLR_ERROR, "Failed to get primary display formats"); - return false; - } - } - - struct wlr_drm_format *format = output_pick_format(output, display_formats); + const struct wlr_drm_format_set *display_formats = + wlr_output_get_primary_formats(output, allocator->buffer_caps); + struct wlr_drm_format *format = output_pick_format(output, display_formats, + output->render_format); if (format == NULL) { wlr_log(WLR_ERROR, "Failed to pick primary buffer format for output '%s'", output->name); return false; } + + if (output->swapchain != NULL && output->swapchain->width == width && + output->swapchain->height == height && + output->swapchain->format->format == format->format && + (allow_modifiers || output->swapchain->format->len == 0)) { + // no change, keep existing swapchain + free(format); + return true; + } + wlr_log(WLR_DEBUG, "Choosing primary buffer format 0x%"PRIX32" for output '%s'", format->format, output->name); if (!allow_modifiers && (format->len != 1 || format->modifiers[0] != DRM_FORMAT_MOD_LINEAR)) { + if (!wlr_drm_format_has(format, DRM_FORMAT_MOD_INVALID)) { + wlr_log(WLR_DEBUG, "Implicit modifiers not supported"); + free(format); + return false; + } + format->len = 0; + wlr_drm_format_add(&format, DRM_FORMAT_MOD_INVALID); } struct wlr_swapchain *swapchain = @@ -80,7 +106,7 @@ static bool output_attach_back_buffer(struct wlr_output *output, return false; } - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; assert(renderer != NULL); struct wlr_buffer *buffer = @@ -103,7 +129,7 @@ void output_clear_back_buffer(struct wlr_output *output) { return; } - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; assert(renderer != NULL); renderer_bind_buffer(renderer, NULL); @@ -130,7 +156,7 @@ static bool output_attach_empty_buffer(struct wlr_output *output) { int width, height; output_pending_resolution(output, &width, &height); - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; wlr_renderer_begin(renderer, width, height); wlr_renderer_clear(renderer, (float[]){0, 0, 0, 0}); wlr_renderer_end(renderer); @@ -149,6 +175,9 @@ bool output_ensure_buffer(struct wlr_output *output) { if (output->pending.committed & WLR_OUTPUT_STATE_MODE) { needs_new_buffer = true; } + if (output->pending.committed & WLR_OUTPUT_STATE_RENDER_FORMAT) { + needs_new_buffer = true; + } if (!needs_new_buffer || (output->pending.committed & WLR_OUTPUT_STATE_BUFFER)) { return true; @@ -210,9 +239,10 @@ void wlr_output_lock_attach_render(struct wlr_output *output, bool lock) { } struct wlr_drm_format *output_pick_format(struct wlr_output *output, - const struct wlr_drm_format_set *display_formats) { - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); - struct wlr_allocator *allocator = backend_get_allocator(output->backend); + const struct wlr_drm_format_set *display_formats, + uint32_t fmt) { + struct wlr_renderer *renderer = output->renderer; + struct wlr_allocator *allocator = output->allocator; assert(renderer != NULL && allocator != NULL); const struct wlr_drm_format_set *render_formats = @@ -222,41 +252,31 @@ struct wlr_drm_format *output_pick_format(struct wlr_output *output, return NULL; } - struct wlr_drm_format *format = NULL; - const uint32_t candidates[] = { DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB8888 }; - for (size_t i = 0; i < sizeof(candidates) / sizeof(candidates[0]); i++) { - uint32_t fmt = candidates[i]; - - const struct wlr_drm_format *render_format = - wlr_drm_format_set_get(render_formats, fmt); - if (render_format == NULL) { - wlr_log(WLR_DEBUG, "Renderer doesn't support format 0x%"PRIX32, fmt); - continue; - } - - if (display_formats != NULL) { - const struct wlr_drm_format *display_format = - wlr_drm_format_set_get(display_formats, fmt); - if (display_format == NULL) { - wlr_log(WLR_DEBUG, "Output doesn't support format 0x%"PRIX32, fmt); - continue; - } - format = wlr_drm_format_intersect(display_format, render_format); - } else { - // The output can display any format - format = wlr_drm_format_dup(render_format); - } - - if (format == NULL) { - wlr_log(WLR_DEBUG, "Failed to intersect display and render " - "modifiers for format 0x%"PRIX32, fmt); - } else { - break; - } + const struct wlr_drm_format *render_format = + wlr_drm_format_set_get(render_formats, fmt); + if (render_format == NULL) { + wlr_log(WLR_DEBUG, "Renderer doesn't support format 0x%"PRIX32, fmt); + return NULL; } + + struct wlr_drm_format *format = NULL; + if (display_formats != NULL) { + const struct wlr_drm_format *display_format = + wlr_drm_format_set_get(display_formats, fmt); + if (display_format == NULL) { + wlr_log(WLR_DEBUG, "Output doesn't support format 0x%"PRIX32, fmt); + return NULL; + } + format = wlr_drm_format_intersect(display_format, render_format); + } else { + // The output can display any format + format = wlr_drm_format_dup(render_format); + } + if (format == NULL) { - wlr_log(WLR_ERROR, "Failed to choose a format for output '%s'", - output->name); + wlr_log(WLR_DEBUG, "Failed to intersect display and render " + "modifiers for format 0x%"PRIX32 " on output '%s", + fmt, output->name); return NULL; } @@ -264,7 +284,9 @@ struct wlr_drm_format *output_pick_format(struct wlr_output *output, } uint32_t wlr_output_preferred_read_format(struct wlr_output *output) { - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; + assert(renderer != NULL); + if (!renderer->impl->preferred_read_format || !renderer->impl->read_pixels) { return DRM_FORMAT_INVALID; } diff --git a/types/scene/layer_shell_v1.c b/types/scene/layer_shell_v1.c new file mode 100644 index 000000000..1c2b6e4ff --- /dev/null +++ b/types/scene/layer_shell_v1.c @@ -0,0 +1,183 @@ +#include +#include +#include + +static void scene_layer_surface_handle_tree_destroy( + struct wl_listener *listener, void *data) { + struct wlr_scene_layer_surface_v1 *scene_layer_surface = + wl_container_of(listener, scene_layer_surface, tree_destroy); + // tree and surface_node will be cleaned up by scene_node_finish + wl_list_remove(&scene_layer_surface->tree_destroy.link); + wl_list_remove(&scene_layer_surface->layer_surface_destroy.link); + wl_list_remove(&scene_layer_surface->layer_surface_map.link); + wl_list_remove(&scene_layer_surface->layer_surface_unmap.link); + free(scene_layer_surface); +} + +static void scene_layer_surface_handle_layer_surface_destroy( + struct wl_listener *listener, void *data) { + struct wlr_scene_layer_surface_v1 *scene_layer_surface = + wl_container_of(listener, scene_layer_surface, layer_surface_destroy); + wlr_scene_node_destroy(scene_layer_surface->node); +} + +static void scene_layer_surface_handle_layer_surface_map( + struct wl_listener *listener, void *data) { + struct wlr_scene_layer_surface_v1 *scene_layer_surface = + wl_container_of(listener, scene_layer_surface, layer_surface_map); + wlr_scene_node_set_enabled(scene_layer_surface->node, true); +} + +static void scene_layer_surface_handle_layer_surface_unmap( + struct wl_listener *listener, void *data) { + struct wlr_scene_layer_surface_v1 *scene_layer_surface = + wl_container_of(listener, scene_layer_surface, layer_surface_unmap); + wlr_scene_node_set_enabled(scene_layer_surface->node, false); +} + +static void layer_surface_exclusive_zone( + struct wlr_layer_surface_v1_state *state, + struct wlr_box *usable_area) { + switch (state->anchor) { + case (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT): + // Anchor top + usable_area->y += state->exclusive_zone + state->margin.top; + usable_area->height -= state->exclusive_zone + state->margin.top; + break; + case (ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT): + // Anchor bottom + usable_area->height -= state->exclusive_zone + state->margin.bottom; + break; + case (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT): + // Anchor left + usable_area->x += state->exclusive_zone + state->margin.left; + usable_area->width -= state->exclusive_zone + state->margin.left; + break; + case (ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT): // Anchor right + // Anchor right + usable_area->width -= state->exclusive_zone + state->margin.right; + break; + } +} + +void wlr_scene_layer_surface_v1_configure( + struct wlr_scene_layer_surface_v1 *scene_layer_surface, + const struct wlr_box *full_area, struct wlr_box *usable_area) { + struct wlr_layer_surface_v1 *layer_surface = + scene_layer_surface->layer_surface; + struct wlr_layer_surface_v1_state *state = &layer_surface->current; + + // If the exclusive zone is set to -1, the layer surface will use the + // full area of the output, otherwise it is constrained to the + // remaining usable area. + struct wlr_box bounds; + if (state->exclusive_zone == -1) { + bounds = *full_area; + } else { + bounds = *usable_area; + } + + struct wlr_box box = { + .width = state->desired_width, + .height = state->desired_height, + }; + + // Horizontal positioning + if (box.width == 0) { + box.x = bounds.x + state->margin.left; + box.width = bounds.width - + state->margin.left + state->margin.right; + } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT && + state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) { + box.x = bounds.x + bounds.width/2 -box.width/2; + } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) { + box.x = bounds.x + state->margin.left; + } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT) { + box.x = bounds.x + bounds.width - box.width - state->margin.right; + } else { + box.x = bounds.x + bounds.width/2 - box.width/2; + } + + // Vertical positioning + if (box.height == 0) { + box.y = bounds.y + state->margin.top; + box.height = bounds.height - + state->margin.top + state->margin.bottom; + } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP && + state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM) { + box.y = bounds.y + bounds.height/2 - box.height/2; + } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) { + box.y = bounds.y + state->margin.top; + } else if (state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM) { + box.y = bounds.y + bounds.height - box.height - state->margin.bottom; + } else { + box.y = bounds.y + bounds.height/2 - box.height/2; + } + + wlr_scene_node_set_position(scene_layer_surface->node, box.x, box.y); + wlr_layer_surface_v1_configure(layer_surface, box.width, box.height); + + if (state->exclusive_zone > 0) { + layer_surface_exclusive_zone(state, usable_area); + } +} + +struct wlr_scene_layer_surface_v1 *wlr_scene_layer_surface_v1_create( + struct wlr_scene_node *parent, + struct wlr_layer_surface_v1 *layer_surface) { + struct wlr_scene_layer_surface_v1 *scene_layer_surface = + calloc(1, sizeof(*scene_layer_surface)); + if (scene_layer_surface == NULL) { + return NULL; + } + + scene_layer_surface->layer_surface = layer_surface; + + struct wlr_scene_tree *tree = wlr_scene_tree_create(parent); + if (tree == NULL) { + free(scene_layer_surface); + return NULL; + } + scene_layer_surface->node = &tree->node; + + struct wlr_scene_node *surface_node = wlr_scene_subsurface_tree_create( + scene_layer_surface->node, layer_surface->surface); + if (surface_node == NULL) { + wlr_scene_node_destroy(scene_layer_surface->node); + free(scene_layer_surface); + return NULL; + } + + scene_layer_surface->tree_destroy.notify = + scene_layer_surface_handle_tree_destroy; + wl_signal_add(&scene_layer_surface->node->events.destroy, + &scene_layer_surface->tree_destroy); + + scene_layer_surface->layer_surface_destroy.notify = + scene_layer_surface_handle_layer_surface_destroy; + wl_signal_add(&layer_surface->events.destroy, + &scene_layer_surface->layer_surface_destroy); + + scene_layer_surface->layer_surface_map.notify = + scene_layer_surface_handle_layer_surface_map; + wl_signal_add(&layer_surface->events.map, + &scene_layer_surface->layer_surface_map); + + scene_layer_surface->layer_surface_unmap.notify = + scene_layer_surface_handle_layer_surface_unmap; + wl_signal_add(&layer_surface->events.unmap, + &scene_layer_surface->layer_surface_unmap); + + wlr_scene_node_set_enabled(scene_layer_surface->node, + layer_surface->mapped); + + return scene_layer_surface; +} diff --git a/types/scene/subsurface_tree.c b/types/scene/subsurface_tree.c index 3f22ee3d6..8384db8fb 100644 --- a/types/scene/subsurface_tree.c +++ b/types/scene/subsurface_tree.c @@ -1,6 +1,7 @@ #include #include #include +#include #include /** @@ -13,15 +14,20 @@ struct wlr_scene_subsurface_tree { struct wlr_surface *surface; struct wlr_scene_surface *scene_surface; - struct wlr_scene_subsurface_tree *parent; // NULL for the top-level surface - struct wlr_addon surface_addon; // only set if there's a parent - struct wl_listener tree_destroy; struct wl_listener surface_destroy; struct wl_listener surface_commit; - struct wl_listener surface_map; - struct wl_listener surface_unmap; struct wl_listener surface_new_subsurface; + + struct wlr_scene_subsurface_tree *parent; // NULL for the top-level surface + + // Only valid if the surface is a sub-surface + + struct wlr_addon surface_addon; + + struct wl_listener subsurface_destroy; + struct wl_listener subsurface_map; + struct wl_listener subsurface_unmap; }; static void subsurface_tree_handle_tree_destroy(struct wl_listener *listener, @@ -31,23 +37,17 @@ static void subsurface_tree_handle_tree_destroy(struct wl_listener *listener, // tree and scene_surface will be cleaned up by scene_node_finish if (subsurface_tree->parent) { wlr_addon_finish(&subsurface_tree->surface_addon); + wl_list_remove(&subsurface_tree->subsurface_destroy.link); + wl_list_remove(&subsurface_tree->subsurface_map.link); + wl_list_remove(&subsurface_tree->subsurface_unmap.link); } wl_list_remove(&subsurface_tree->tree_destroy.link); wl_list_remove(&subsurface_tree->surface_destroy.link); wl_list_remove(&subsurface_tree->surface_commit.link); - wl_list_remove(&subsurface_tree->surface_map.link); - wl_list_remove(&subsurface_tree->surface_unmap.link); wl_list_remove(&subsurface_tree->surface_new_subsurface.link); free(subsurface_tree); } -static void subsurface_tree_handle_surface_destroy(struct wl_listener *listener, - void *data) { - struct wlr_scene_subsurface_tree *subsurface_tree = - wl_container_of(listener, subsurface_tree, surface_destroy); - wlr_scene_node_destroy(&subsurface_tree->tree->node); -} - static const struct wlr_addon_interface subsurface_tree_addon_impl; static struct wlr_scene_subsurface_tree *subsurface_tree_from_subsurface( @@ -97,6 +97,13 @@ static void subsurface_tree_reconfigure( } } +static void subsurface_tree_handle_surface_destroy(struct wl_listener *listener, + void *data) { + struct wlr_scene_subsurface_tree *subsurface_tree = + wl_container_of(listener, subsurface_tree, surface_destroy); + wlr_scene_node_destroy(&subsurface_tree->tree->node); +} + static void subsurface_tree_handle_surface_commit(struct wl_listener *listener, void *data) { struct wlr_scene_subsurface_tree *subsurface_tree = @@ -106,18 +113,25 @@ static void subsurface_tree_handle_surface_commit(struct wl_listener *listener, subsurface_tree_reconfigure(subsurface_tree); } -static void subsurface_tree_handle_surface_map(struct wl_listener *listener, +static void subsurface_tree_handle_subsurface_destroy(struct wl_listener *listener, void *data) { struct wlr_scene_subsurface_tree *subsurface_tree = - wl_container_of(listener, subsurface_tree, surface_map); + wl_container_of(listener, subsurface_tree, subsurface_destroy); + wlr_scene_node_destroy(&subsurface_tree->tree->node); +} + +static void subsurface_tree_handle_subsurface_map(struct wl_listener *listener, + void *data) { + struct wlr_scene_subsurface_tree *subsurface_tree = + wl_container_of(listener, subsurface_tree, subsurface_map); wlr_scene_node_set_enabled(&subsurface_tree->tree->node, true); } -static void subsurface_tree_handle_surface_unmap(struct wl_listener *listener, +static void subsurface_tree_handle_subsurface_unmap(struct wl_listener *listener, void *data) { struct wlr_scene_subsurface_tree *subsurface_tree = - wl_container_of(listener, subsurface_tree, surface_unmap); + wl_container_of(listener, subsurface_tree, subsurface_unmap); wlr_scene_node_set_enabled(&subsurface_tree->tree->node, false); } @@ -151,8 +165,14 @@ static bool subsurface_tree_create_subsurface( wlr_addon_init(&child->surface_addon, &subsurface->surface->addons, parent, &subsurface_tree_addon_impl); - wl_signal_add(&subsurface->events.map, &child->surface_map); - wl_signal_add(&subsurface->events.unmap, &child->surface_unmap); + child->subsurface_destroy.notify = subsurface_tree_handle_subsurface_destroy; + wl_signal_add(&subsurface->events.destroy, &child->subsurface_destroy); + + child->subsurface_map.notify = subsurface_tree_handle_subsurface_map; + wl_signal_add(&subsurface->events.map, &child->subsurface_map); + + child->subsurface_unmap.notify = subsurface_tree_handle_subsurface_unmap; + wl_signal_add(&subsurface->events.unmap, &child->subsurface_unmap); return true; } @@ -214,12 +234,6 @@ static struct wlr_scene_subsurface_tree *scene_surface_tree_create( subsurface_tree->surface_commit.notify = subsurface_tree_handle_surface_commit; wl_signal_add(&surface->events.commit, &subsurface_tree->surface_commit); - subsurface_tree->surface_map.notify = subsurface_tree_handle_surface_map; - wl_list_init(&subsurface_tree->surface_map.link); - - subsurface_tree->surface_unmap.notify = subsurface_tree_handle_surface_unmap; - wl_list_init(&subsurface_tree->surface_unmap.link); - subsurface_tree->surface_new_subsurface.notify = subsurface_tree_handle_surface_new_subsurface; wl_signal_add(&surface->events.new_subsurface, diff --git a/types/scene/wlr_scene.c b/types/scene/wlr_scene.c index 5a2528d1b..5d5ff9657 100644 --- a/types/scene/wlr_scene.c +++ b/types/scene/wlr_scene.c @@ -3,10 +3,11 @@ #include #include #include +#include #include #include +#include #include -#include #include #include #include "util/signal.h" @@ -39,6 +40,13 @@ static struct wlr_scene_buffer *scene_buffer_from_node( return (struct wlr_scene_buffer *)node; } +static struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node) { + while (node->parent != NULL) { + node = node->parent; + } + return scene_root_from_node(node); +} + static void scene_node_state_init(struct wlr_scene_node_state *state) { wl_list_init(&state->children); wl_list_init(&state->link); @@ -85,15 +93,17 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) { scene_node_damage_whole(node); scene_node_finish(node); + struct wlr_scene *scene = scene_node_get_root(node); + struct wlr_scene_output *scene_output; switch (node->type) { case WLR_SCENE_NODE_ROOT:; - struct wlr_scene *scene = scene_root_from_node(node); - - struct wlr_scene_output *scene_output, *scene_output_tmp; + struct wlr_scene_output *scene_output_tmp; wl_list_for_each_safe(scene_output, scene_output_tmp, &scene->outputs, link) { wlr_scene_output_destroy(scene_output); } + wl_list_remove(&scene->presentation_destroy.link); + free(scene); break; case WLR_SCENE_NODE_TREE:; @@ -102,8 +112,16 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) { break; case WLR_SCENE_NODE_SURFACE:; struct wlr_scene_surface *scene_surface = wlr_scene_surface_from_node(node); + + wl_list_for_each(scene_output, &scene->outputs, link) { + // This is a noop if wlr_surface_send_enter() wasn't previously called for + // the given output. + wlr_surface_send_leave(scene_surface->surface, scene_output->output); + } + wl_list_remove(&scene_surface->surface_commit.link); wl_list_remove(&scene_surface->surface_destroy.link); + free(scene_surface); break; case WLR_SCENE_NODE_RECT:; @@ -112,6 +130,7 @@ void wlr_scene_node_destroy(struct wlr_scene_node *node) { break; case WLR_SCENE_NODE_BUFFER:; struct wlr_scene_buffer *scene_buffer = scene_buffer_from_node(node); + wl_list_remove(&scene_buffer->pending_link); wlr_texture_destroy(scene_buffer->texture); wlr_buffer_unlock(scene_buffer->buffer); free(scene_buffer); @@ -126,6 +145,8 @@ struct wlr_scene *wlr_scene_create(void) { } scene_node_init(&scene->node, WLR_SCENE_NODE_ROOT, NULL); wl_list_init(&scene->outputs); + wl_list_init(&scene->presentation_destroy.link); + wl_list_init(&scene->pending_buffers); return scene; } @@ -146,11 +167,69 @@ static void scene_surface_handle_surface_destroy(struct wl_listener *listener, wlr_scene_node_destroy(&scene_surface->node); } -static struct wlr_scene *scene_node_get_root(struct wlr_scene_node *node) { - while (node->parent != NULL) { - node = node->parent; +// This function must be called whenever the coordinates/dimensions of a scene +// surface or scene output change. It is not necessary to call when a scene +// surface's node is enabled/disabled or obscured by other nodes. To quote the +// protocol: "The surface might be hidden even if no leave event has been sent." +static void scene_surface_update_outputs( + struct wlr_scene_surface *scene_surface, + int lx, int ly, struct wlr_scene *scene) { + struct wlr_box surface_box = { + .x = lx, + .y = ly, + .width = scene_surface->surface->current.width, + .height = scene_surface->surface->current.height, + }; + + int largest_overlap = 0; + scene_surface->primary_output = NULL; + + struct wlr_scene_output *scene_output; + wl_list_for_each(scene_output, &scene->outputs, link) { + struct wlr_box output_box = { + .x = scene_output->x, + .y = scene_output->y, + }; + wlr_output_effective_resolution(scene_output->output, + &output_box.width, &output_box.height); + + struct wlr_box intersection; + if (wlr_box_intersection(&intersection, &surface_box, &output_box)) { + int overlap = intersection.width * intersection.height; + if (overlap > largest_overlap) { + largest_overlap = overlap; + scene_surface->primary_output = scene_output->output; + } + + // These enter/leave functions are a noop if the event has already been + // sent for the given output. + wlr_surface_send_enter(scene_surface->surface, scene_output->output); + } else { + wlr_surface_send_leave(scene_surface->surface, scene_output->output); + } } - return scene_root_from_node(node); +} + +static void scene_node_update_surface_outputs_iterator( + struct wlr_scene_node *node, int lx, int ly, struct wlr_scene *scene) { + if (node->type == WLR_SCENE_NODE_SURFACE) { + struct wlr_scene_surface *scene_surface = + wlr_scene_surface_from_node(node); + scene_surface_update_outputs(scene_surface, lx, ly, scene); + } + + struct wlr_scene_node *child; + wl_list_for_each(child, &node->state.children, state.link) { + scene_node_update_surface_outputs_iterator(child, lx + child->state.x, + ly + child->state.y, scene); + } +} + +static void scene_node_update_surface_outputs(struct wlr_scene_node *node) { + struct wlr_scene *scene = scene_node_get_root(node); + int lx, ly; + wlr_scene_node_coords(node, &lx, &ly); + scene_node_update_surface_outputs_iterator(node, lx, ly, scene); } static void scene_surface_handle_surface_commit(struct wl_listener *listener, @@ -159,17 +238,33 @@ static void scene_surface_handle_surface_commit(struct wl_listener *listener, wl_container_of(listener, scene_surface, surface_commit); struct wlr_surface *surface = scene_surface->surface; + struct wlr_scene *scene = scene_node_get_root(&scene_surface->node); + + int lx, ly; + bool enabled = wlr_scene_node_coords(&scene_surface->node, &lx, &ly); + + if (surface->current.width != scene_surface->prev_width || + surface->current.height != scene_surface->prev_height) { + scene_surface_update_outputs(scene_surface, lx, ly, scene); + scene_surface->prev_width = surface->current.width; + scene_surface->prev_height = surface->current.height; + } + + if (!enabled) { + return; + } + + // Even if the surface hasn't submitted damage, schedule a new frame if + // the client has requested a wl_surface.frame callback. + if (!wl_list_empty(&surface->current.frame_callback_list) && + scene_surface->primary_output != NULL) { + wlr_output_schedule_frame(scene_surface->primary_output); + } + if (!pixman_region32_not_empty(&surface->buffer_damage)) { return; } - int lx, ly; - if (!wlr_scene_node_coords(&scene_surface->node, &lx, &ly)) { - return; - } - - struct wlr_scene *scene = scene_node_get_root(&scene_surface->node); - struct wlr_scene_output *scene_output; wl_list_for_each(scene_output, &scene->outputs, link) { struct wlr_output *output = scene_output->output; @@ -212,6 +307,8 @@ struct wlr_scene_surface *wlr_scene_surface_create(struct wlr_scene_node *parent scene_node_damage_whole(&scene_surface->node); + scene_node_update_surface_outputs(&scene_surface->node); + return scene_surface; } @@ -265,6 +362,9 @@ struct wlr_scene_buffer *wlr_scene_buffer_create(struct wlr_scene_node *parent, scene_node_damage_whole(&scene_buffer->node); + struct wlr_scene *scene = scene_node_get_root(parent); + wl_list_insert(&scene->pending_buffers, &scene_buffer->pending_link); + return scene_buffer; } @@ -438,6 +538,8 @@ void wlr_scene_node_set_position(struct wlr_scene_node *node, int x, int y) { node->state.x = x; node->state.y = y; scene_node_damage_whole(node); + + scene_node_update_surface_outputs(node); } void wlr_scene_node_place_above(struct wlr_scene_node *node, @@ -483,7 +585,7 @@ void wlr_scene_node_raise_to_top(struct wlr_scene_node *node) { void wlr_scene_node_lower_to_bottom(struct wlr_scene_node *node) { struct wlr_scene_node *current_bottom = wl_container_of( - node->parent->state.children.prev, current_bottom, state.link); + node->parent->state.children.next, current_bottom, state.link); if (node == current_bottom) { return; } @@ -511,6 +613,8 @@ void wlr_scene_node_reparent(struct wlr_scene_node *node, wl_list_insert(new_parent->state.children.prev, &node->state.link); scene_node_damage_whole(node); + + scene_node_update_surface_outputs(node); } bool wlr_scene_node_coords(struct wlr_scene_node *node, @@ -605,7 +709,7 @@ struct wlr_scene_node *wlr_scene_node_at(struct wlr_scene_node *node, } static void scissor_output(struct wlr_output *output, pixman_box32_t *rect) { - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; assert(renderer); struct wlr_box box = { @@ -628,7 +732,7 @@ static void scissor_output(struct wlr_output *output, pixman_box32_t *rect) { static void render_rect(struct wlr_output *output, pixman_region32_t *output_damage, const float color[static 4], const struct wlr_box *box, const float matrix[static 9]) { - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; assert(renderer); pixman_region32_t damage; @@ -650,7 +754,7 @@ static void render_texture(struct wlr_output *output, pixman_region32_t *output_damage, struct wlr_texture *texture, const struct wlr_fbox *src_box, const struct wlr_box *dst_box, const float matrix[static 9]) { - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; assert(renderer); struct wlr_fbox default_src_box = {0}; @@ -679,6 +783,9 @@ static void render_texture(struct wlr_output *output, struct render_data { struct wlr_output *output; pixman_region32_t *damage; + + // May be NULL + struct wlr_presentation *presentation; }; static void render_node_iterator(struct wlr_scene_node *node, @@ -715,7 +822,16 @@ static void render_node_iterator(struct wlr_scene_node *node, wlr_matrix_project_box(matrix, &dst_box, transform, 0.0, output->transform_matrix); - render_texture(output, output_damage, texture, NULL, &dst_box, matrix); + struct wlr_fbox src_box = {0}; + wlr_surface_get_buffer_source_box(surface, &src_box); + + render_texture(output, output_damage, texture, + &src_box, &dst_box, matrix); + + if (data->presentation != NULL && scene_surface->primary_output == output) { + wlr_presentation_surface_sampled_on_output(data->presentation, + surface, output); + } break; case WLR_SCENE_NODE_RECT:; struct wlr_scene_rect *scene_rect = scene_rect_from_node(node); @@ -726,7 +842,7 @@ static void render_node_iterator(struct wlr_scene_node *node, case WLR_SCENE_NODE_BUFFER:; struct wlr_scene_buffer *scene_buffer = scene_buffer_from_node(node); - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; texture = scene_buffer_get_texture(scene_buffer, renderer); if (texture == NULL) { return; @@ -768,14 +884,14 @@ void wlr_scene_render_output(struct wlr_scene *scene, struct wlr_output *output, damage = &full_region; } - struct wlr_renderer *renderer = - wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; assert(renderer); if (output->enabled && pixman_region32_not_empty(damage)) { struct render_data data = { .output = output, .damage = damage, + .presentation = scene->presentation, }; scene_node_for_each_node(&scene->node, -lx, -ly, render_node_iterator, &data); @@ -785,6 +901,23 @@ void wlr_scene_render_output(struct wlr_scene *scene, struct wlr_output *output, pixman_region32_fini(&full_region); } +static void scene_handle_presentation_destroy(struct wl_listener *listener, + void *data) { + struct wlr_scene *scene = + wl_container_of(listener, scene, presentation_destroy); + wl_list_remove(&scene->presentation_destroy.link); + wl_list_init(&scene->presentation_destroy.link); + scene->presentation = NULL; +} + +void wlr_scene_set_presentation(struct wlr_scene *scene, + struct wlr_presentation *presentation) { + assert(scene->presentation == NULL); + scene->presentation = presentation; + scene->presentation_destroy.notify = scene_handle_presentation_destroy; + wl_signal_add(&presentation->events.destroy, &scene->presentation_destroy); +} + static void scene_output_handle_destroy(struct wlr_addon *addon) { struct wlr_scene_output *scene_output = wl_container_of(addon, scene_output, addon); @@ -819,12 +952,34 @@ struct wlr_scene_output *wlr_scene_output_create(struct wlr_scene *scene, return scene_output; } +static void scene_output_send_leave_iterator(struct wlr_surface *surface, + int sx, int sy, void *data) { + struct wlr_output *output = data; + wlr_surface_send_leave(surface, output); +} + void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) { wlr_addon_finish(&scene_output->addon); wl_list_remove(&scene_output->link); + + wlr_scene_output_for_each_surface(scene_output, + scene_output_send_leave_iterator, scene_output->output); + free(scene_output); } +struct wlr_scene_output *wlr_scene_get_scene_output(struct wlr_scene *scene, + struct wlr_output *output) { + struct wlr_addon *addon = + wlr_addon_find(&output->addons, scene, &output_addon_impl); + if (addon == NULL) { + return NULL; + } + struct wlr_scene_output *scene_output = + wl_container_of(addon, scene_output, addon); + return scene_output; +} + void wlr_scene_output_set_position(struct wlr_scene_output *scene_output, int lx, int ly) { if (scene_output->x == lx && scene_output->y == ly) { @@ -834,6 +989,8 @@ void wlr_scene_output_set_position(struct wlr_scene_output *scene_output, scene_output->x = lx; scene_output->y = ly; wlr_output_damage_add_whole(scene_output->damage); + + scene_node_update_surface_outputs(&scene_output->scene->node); } struct check_scanout_data { @@ -913,13 +1070,25 @@ static bool scene_output_scanout(struct wlr_scene_output *scene_output) { return false; } + struct wlr_presentation *presentation = scene_output->scene->presentation; + if (presentation != NULL && node->type == WLR_SCENE_NODE_SURFACE) { + struct wlr_scene_surface *scene_surface = + wlr_scene_surface_from_node(node); + // Since outputs may overlap, we still need to check this even though + // we know that the surface size matches the size of this output. + if (scene_surface->primary_output == output) { + wlr_presentation_surface_sampled_on_output(presentation, + scene_surface->surface, output); + } + } + return wlr_output_commit(output); } bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) { struct wlr_output *output = scene_output->output; - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; assert(renderer != NULL); bool scanout = scene_output_scanout(scene_output); @@ -949,6 +1118,15 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) { return true; } + // Try to import new buffers as textures + struct wlr_scene_buffer *scene_buffer, *scene_buffer_tmp; + wl_list_for_each_safe(scene_buffer, scene_buffer_tmp, + &scene_output->scene->pending_buffers, pending_link) { + scene_buffer_get_texture(scene_buffer, renderer); + wl_list_remove(&scene_buffer->pending_link); + wl_list_init(&scene_buffer->pending_link); + } + wlr_renderer_begin(renderer, output->width, output->height); int nrects; @@ -981,6 +1159,32 @@ bool wlr_scene_output_commit(struct wlr_scene_output *scene_output) { return wlr_output_commit(output); } +static void scene_output_send_frame_done_iterator(struct wlr_scene_node *node, + struct wlr_output *output, struct timespec *now) { + if (!node->state.enabled) { + return; + } + + if (node->type == WLR_SCENE_NODE_SURFACE) { + struct wlr_scene_surface *scene_surface = + wlr_scene_surface_from_node(node); + if (scene_surface->primary_output == output) { + wlr_surface_send_frame_done(scene_surface->surface, now); + } + } + + struct wlr_scene_node *child; + wl_list_for_each(child, &node->state.children, state.link) { + scene_output_send_frame_done_iterator(child, output, now); + } +} + +void wlr_scene_output_send_frame_done(struct wlr_scene_output *scene_output, + struct timespec *now) { + scene_output_send_frame_done_iterator(&scene_output->scene->node, + scene_output->output, now); +} + static void scene_output_for_each_surface(const struct wlr_box *output_box, struct wlr_scene_node *node, int lx, int ly, wlr_surface_iterator_func_t user_iterator, void *user_data) { diff --git a/types/scene/xdg_shell.c b/types/scene/xdg_shell.c new file mode 100644 index 000000000..9b3ad71c3 --- /dev/null +++ b/types/scene/xdg_shell.c @@ -0,0 +1,124 @@ +#include +#include +#include + +struct wlr_scene_xdg_surface { + struct wlr_scene_tree *tree; + struct wlr_xdg_surface *xdg_surface; + struct wlr_scene_node *surface_node; + + struct wl_listener tree_destroy; + struct wl_listener xdg_surface_destroy; + struct wl_listener xdg_surface_map; + struct wl_listener xdg_surface_unmap; + struct wl_listener xdg_surface_commit; +}; + +static void scene_xdg_surface_handle_tree_destroy(struct wl_listener *listener, + void *data) { + struct wlr_scene_xdg_surface *scene_xdg_surface = + wl_container_of(listener, scene_xdg_surface, tree_destroy); + // tree and surface_node will be cleaned up by scene_node_finish + wl_list_remove(&scene_xdg_surface->tree_destroy.link); + wl_list_remove(&scene_xdg_surface->xdg_surface_destroy.link); + wl_list_remove(&scene_xdg_surface->xdg_surface_map.link); + wl_list_remove(&scene_xdg_surface->xdg_surface_unmap.link); + wl_list_remove(&scene_xdg_surface->xdg_surface_commit.link); + free(scene_xdg_surface); +} + +static void scene_xdg_surface_handle_xdg_surface_destroy(struct wl_listener *listener, + void *data) { + struct wlr_scene_xdg_surface *scene_xdg_surface = + wl_container_of(listener, scene_xdg_surface, xdg_surface_destroy); + wlr_scene_node_destroy(&scene_xdg_surface->tree->node); +} + +static void scene_xdg_surface_handle_xdg_surface_map(struct wl_listener *listener, + void *data) { + struct wlr_scene_xdg_surface *scene_xdg_surface = + wl_container_of(listener, scene_xdg_surface, xdg_surface_map); + wlr_scene_node_set_enabled(&scene_xdg_surface->tree->node, true); +} + +static void scene_xdg_surface_handle_xdg_surface_unmap(struct wl_listener *listener, + void *data) { + struct wlr_scene_xdg_surface *scene_xdg_surface = + wl_container_of(listener, scene_xdg_surface, xdg_surface_unmap); + wlr_scene_node_set_enabled(&scene_xdg_surface->tree->node, false); +} + +static void scene_xdg_surface_update_position( + struct wlr_scene_xdg_surface *scene_xdg_surface) { + struct wlr_xdg_surface *xdg_surface = scene_xdg_surface->xdg_surface; + + struct wlr_box geo = {0}; + wlr_xdg_surface_get_geometry(xdg_surface, &geo); + wlr_scene_node_set_position(scene_xdg_surface->surface_node, + -geo.x, -geo.y); + + if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { + struct wlr_xdg_popup *popup = xdg_surface->popup; + wlr_scene_node_set_position(&scene_xdg_surface->tree->node, + popup->geometry.x, popup->geometry.y); + } +} + +static void scene_xdg_surface_handle_xdg_surface_commit(struct wl_listener *listener, + void *data) { + struct wlr_scene_xdg_surface *scene_xdg_surface = + wl_container_of(listener, scene_xdg_surface, xdg_surface_commit); + scene_xdg_surface_update_position(scene_xdg_surface); +} + +struct wlr_scene_node *wlr_scene_xdg_surface_create( + struct wlr_scene_node *parent, struct wlr_xdg_surface *xdg_surface) { + struct wlr_scene_xdg_surface *scene_xdg_surface = + calloc(1, sizeof(*scene_xdg_surface)); + if (scene_xdg_surface == NULL) { + return NULL; + } + + scene_xdg_surface->xdg_surface = xdg_surface; + + scene_xdg_surface->tree = wlr_scene_tree_create(parent); + if (scene_xdg_surface->tree == NULL) { + free(scene_xdg_surface); + return NULL; + } + + scene_xdg_surface->surface_node = wlr_scene_subsurface_tree_create( + &scene_xdg_surface->tree->node, xdg_surface->surface); + if (scene_xdg_surface->surface_node == NULL) { + wlr_scene_node_destroy(&scene_xdg_surface->tree->node); + free(scene_xdg_surface); + return NULL; + } + + scene_xdg_surface->tree_destroy.notify = + scene_xdg_surface_handle_tree_destroy; + wl_signal_add(&scene_xdg_surface->tree->node.events.destroy, + &scene_xdg_surface->tree_destroy); + + scene_xdg_surface->xdg_surface_destroy.notify = + scene_xdg_surface_handle_xdg_surface_destroy; + wl_signal_add(&xdg_surface->events.destroy, &scene_xdg_surface->xdg_surface_destroy); + + scene_xdg_surface->xdg_surface_map.notify = + scene_xdg_surface_handle_xdg_surface_map; + wl_signal_add(&xdg_surface->events.map, &scene_xdg_surface->xdg_surface_map); + + scene_xdg_surface->xdg_surface_unmap.notify = + scene_xdg_surface_handle_xdg_surface_unmap; + wl_signal_add(&xdg_surface->events.unmap, &scene_xdg_surface->xdg_surface_unmap); + + scene_xdg_surface->xdg_surface_commit.notify = + scene_xdg_surface_handle_xdg_surface_commit; + wl_signal_add(&xdg_surface->surface->events.commit, + &scene_xdg_surface->xdg_surface_commit); + + wlr_scene_node_set_enabled(&scene_xdg_surface->tree->node, xdg_surface->mapped); + scene_xdg_surface_update_position(scene_xdg_surface); + + return &scene_xdg_surface->tree->node; +} diff --git a/types/seat/wlr_seat.c b/types/seat/wlr_seat.c index f93147443..4933133c6 100644 --- a/types/seat/wlr_seat.c +++ b/types/seat/wlr_seat.c @@ -191,7 +191,7 @@ void wlr_seat_destroy(struct wlr_seat *seat) { } } - wlr_global_destroy_safe(seat->global, seat->display); + wlr_global_destroy_safe(seat->global); free(seat->pointer_state.default_grab); free(seat->keyboard_state.default_grab); free(seat->touch_state.default_grab); diff --git a/types/seat/wlr_seat_keyboard.c b/types/seat/wlr_seat_keyboard.c index e97b48252..9ed5fb345 100644 --- a/types/seat/wlr_seat_keyboard.c +++ b/types/seat/wlr_seat_keyboard.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -415,7 +416,7 @@ void seat_client_create_keyboard(struct wlr_seat_client *seat_client, seat_client->seat->keyboard_state.focused_surface; // Send an enter event if there is a focused client/surface stored - if (focused_client != NULL && focused_surface != NULL) { + if (focused_client == seat_client && focused_surface != NULL) { uint32_t *keycodes = keyboard->keycodes; size_t num_keycodes = keyboard->num_keycodes; diff --git a/types/seat/wlr_seat_pointer.c b/types/seat/wlr_seat_pointer.c index 27ece5e88..ce4ca54e2 100644 --- a/types/seat/wlr_seat_pointer.c +++ b/types/seat/wlr_seat_pointer.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include "types/wlr_seat.h" @@ -446,7 +447,7 @@ void seat_client_create_pointer(struct wlr_seat_client *seat_client, seat_client->seat->pointer_state.focused_surface; // Send an enter event if there is a focused client/surface stored - if (focused_client != NULL && focused_surface != NULL) { + if (focused_client == seat_client && focused_surface != NULL) { double sx = seat_client->seat->pointer_state.sx; double sy = seat_client->seat->pointer_state.sy; diff --git a/types/seat/wlr_seat_touch.c b/types/seat/wlr_seat_touch.c index 195928a3b..99c50cf88 100644 --- a/types/seat/wlr_seat_touch.c +++ b/types/seat/wlr_seat_touch.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include "types/wlr_seat.h" diff --git a/types/tablet_v2/wlr_tablet_v2_pad.c b/types/tablet_v2/wlr_tablet_v2_pad.c index 020cba57c..58094f575 100644 --- a/types/tablet_v2/wlr_tablet_v2_pad.c +++ b/types/tablet_v2/wlr_tablet_v2_pad.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include diff --git a/types/tablet_v2/wlr_tablet_v2_tablet.c b/types/tablet_v2/wlr_tablet_v2_tablet.c index bf2a91593..1d3c08fd4 100644 --- a/types/tablet_v2/wlr_tablet_v2_tablet.c +++ b/types/tablet_v2/wlr_tablet_v2_tablet.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include diff --git a/types/tablet_v2/wlr_tablet_v2_tool.c b/types/tablet_v2/wlr_tablet_v2_tool.c index 51efc1e84..9e5b9658a 100644 --- a/types/tablet_v2/wlr_tablet_v2_tool.c +++ b/types/tablet_v2/wlr_tablet_v2_tool.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/types/wlr_compositor.c b/types/wlr_compositor.c index 7ad684543..bb4275ef3 100644 --- a/types/wlr_compositor.c +++ b/types/wlr_compositor.c @@ -1,110 +1,1072 @@ #include #include #include +#include +#include #include -#include +#include +#include +#include +#include #include +#include #include "types/wlr_region.h" -#include "types/wlr_surface.h" #include "util/signal.h" +#include "util/time.h" #define COMPOSITOR_VERSION 4 -#define SUBCOMPOSITOR_VERSION 1 +#define CALLBACK_VERSION 1 -extern const struct wlr_surface_role subsurface_role; - -bool wlr_surface_is_subsurface(struct wlr_surface *surface) { - return surface->role == &subsurface_role; +static int min(int fst, int snd) { + if (fst < snd) { + return fst; + } else { + return snd; + } } -struct wlr_subsurface *wlr_subsurface_from_wlr_surface( - struct wlr_surface *surface) { - assert(wlr_surface_is_subsurface(surface)); - return (struct wlr_subsurface *)surface->role_data; +static int max(int fst, int snd) { + if (fst > snd) { + return fst; + } else { + return snd; + } } -static void subcompositor_handle_destroy(struct wl_client *client, +static void surface_handle_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); } -static void subcompositor_handle_get_subsurface(struct wl_client *client, - struct wl_resource *resource, uint32_t id, - struct wl_resource *surface_resource, - struct wl_resource *parent_resource) { - struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); - struct wlr_surface *parent = wlr_surface_from_resource(parent_resource); +static void surface_handle_attach(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *buffer_resource, int32_t dx, int32_t dy) { + struct wlr_surface *surface = wlr_surface_from_resource(resource); - static const char msg[] = "get_subsurface: wl_subsurface@"; - - if (surface == parent) { - wl_resource_post_error(resource, - WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, - "%s%" PRIu32 ": wl_surface@%" PRIu32 " cannot be its own parent", - msg, id, wl_resource_get_id(surface_resource)); - return; + struct wlr_buffer *buffer = NULL; + if (buffer_resource != NULL) { + buffer = wlr_buffer_from_resource(buffer_resource); + if (buffer == NULL) { + wl_resource_post_error(buffer_resource, 0, "unknown buffer type"); + return; + } } - if (wlr_surface_is_subsurface(surface) && - wlr_subsurface_from_wlr_surface(surface) != NULL) { - wl_resource_post_error(resource, - WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, - "%s%" PRIu32 ": wl_surface@%" PRIu32 " is already a sub-surface", - msg, id, wl_resource_get_id(surface_resource)); - return; - } + surface->pending.committed |= WLR_SURFACE_STATE_BUFFER; + surface->pending.dx = dx; + surface->pending.dy = dy; - if (wlr_surface_get_root_surface(parent) == surface) { - wl_resource_post_error(resource, - WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, - "%s%" PRIu32 ": wl_surface@%" PRIu32 " is an ancestor of parent", - msg, id, wl_resource_get_id(surface_resource)); - return; - } - - if (!wlr_surface_set_role(surface, &subsurface_role, NULL, - resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE)) { - return; - } - - subsurface_create(surface, parent, wl_resource_get_version(resource), id); + wlr_buffer_unlock(surface->pending.buffer); + surface->pending.buffer = buffer; } -static const struct wl_subcompositor_interface subcompositor_impl = { - .destroy = subcompositor_handle_destroy, - .get_subsurface = subcompositor_handle_get_subsurface, +static void surface_handle_damage(struct wl_client *client, + struct wl_resource *resource, + int32_t x, int32_t y, int32_t width, int32_t height) { + struct wlr_surface *surface = wlr_surface_from_resource(resource); + if (width < 0 || height < 0) { + return; + } + surface->pending.committed |= WLR_SURFACE_STATE_SURFACE_DAMAGE; + pixman_region32_union_rect(&surface->pending.surface_damage, + &surface->pending.surface_damage, + x, y, width, height); +} + +static void callback_handle_resource_destroy(struct wl_resource *resource) { + wl_list_remove(wl_resource_get_link(resource)); +} + +static void surface_handle_frame(struct wl_client *client, + struct wl_resource *resource, uint32_t callback) { + struct wlr_surface *surface = wlr_surface_from_resource(resource); + + struct wl_resource *callback_resource = wl_resource_create(client, + &wl_callback_interface, CALLBACK_VERSION, callback); + if (callback_resource == NULL) { + wl_resource_post_no_memory(resource); + return; + } + wl_resource_set_implementation(callback_resource, NULL, NULL, + callback_handle_resource_destroy); + + wl_list_insert(surface->pending.frame_callback_list.prev, + wl_resource_get_link(callback_resource)); + + surface->pending.committed |= WLR_SURFACE_STATE_FRAME_CALLBACK_LIST; +} + +static void surface_handle_set_opaque_region(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *region_resource) { + struct wlr_surface *surface = wlr_surface_from_resource(resource); + surface->pending.committed |= WLR_SURFACE_STATE_OPAQUE_REGION; + if (region_resource) { + pixman_region32_t *region = wlr_region_from_resource(region_resource); + pixman_region32_copy(&surface->pending.opaque, region); + } else { + pixman_region32_clear(&surface->pending.opaque); + } +} + +static void surface_handle_set_input_region(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *region_resource) { + struct wlr_surface *surface = wlr_surface_from_resource(resource); + surface->pending.committed |= WLR_SURFACE_STATE_INPUT_REGION; + if (region_resource) { + pixman_region32_t *region = wlr_region_from_resource(region_resource); + pixman_region32_copy(&surface->pending.input, region); + } else { + pixman_region32_fini(&surface->pending.input); + pixman_region32_init_rect(&surface->pending.input, + INT32_MIN, INT32_MIN, UINT32_MAX, UINT32_MAX); + } +} + +static void surface_state_transformed_buffer_size(struct wlr_surface_state *state, + int *out_width, int *out_height) { + int width = state->buffer_width; + int height = state->buffer_height; + if ((state->transform & WL_OUTPUT_TRANSFORM_90) != 0) { + int tmp = width; + width = height; + height = tmp; + } + *out_width = width; + *out_height = height; +} + +/** + * Computes the surface viewport source size, ie. the size after applying the + * surface's scale, transform and cropping (via the viewport's source + * rectangle) but before applying the viewport scaling (via the viewport's + * destination rectangle). + */ +static void surface_state_viewport_src_size(struct wlr_surface_state *state, + int *out_width, int *out_height) { + if (state->buffer_width == 0 && state->buffer_height == 0) { + *out_width = *out_height = 0; + return; + } + + if (state->viewport.has_src) { + *out_width = state->viewport.src.width; + *out_height = state->viewport.src.height; + } else { + surface_state_transformed_buffer_size(state, + out_width, out_height); + *out_width /= state->scale; + *out_height /= state->scale; + } +} + +static void surface_finalize_pending(struct wlr_surface *surface) { + struct wlr_surface_state *pending = &surface->pending; + + if ((pending->committed & WLR_SURFACE_STATE_BUFFER)) { + if (pending->buffer != NULL) { + pending->buffer_width = pending->buffer->width; + pending->buffer_height = pending->buffer->height; + } else { + pending->buffer_width = pending->buffer_height = 0; + } + } + + if (!pending->viewport.has_src && + (pending->buffer_width % pending->scale != 0 || + pending->buffer_height % pending->scale != 0)) { + // TODO: send WL_SURFACE_ERROR_INVALID_SIZE error once this issue is + // resolved: + // https://gitlab.freedesktop.org/wayland/wayland/-/issues/194 + wlr_log(WLR_DEBUG, "Client bug: submitted a buffer whose size (%dx%d) " + "is not divisible by scale (%d)", pending->buffer_width, + pending->buffer_height, pending->scale); + } + + if (pending->viewport.has_dst) { + if (pending->buffer_width == 0 && pending->buffer_height == 0) { + pending->width = pending->height = 0; + } else { + pending->width = pending->viewport.dst_width; + pending->height = pending->viewport.dst_height; + } + } else { + surface_state_viewport_src_size(pending, &pending->width, &pending->height); + } + + pixman_region32_intersect_rect(&pending->surface_damage, + &pending->surface_damage, 0, 0, pending->width, pending->height); + + pixman_region32_intersect_rect(&pending->buffer_damage, + &pending->buffer_damage, 0, 0, pending->buffer_width, + pending->buffer_height); +} + +static void surface_update_damage(pixman_region32_t *buffer_damage, + struct wlr_surface_state *current, struct wlr_surface_state *pending) { + pixman_region32_clear(buffer_damage); + + if (pending->width != current->width || + pending->height != current->height || + pending->viewport.src.x != current->viewport.src.x || + pending->viewport.src.y != current->viewport.src.y || + pending->viewport.src.width != current->viewport.src.width || + pending->viewport.src.height != current->viewport.src.height) { + // Damage the whole buffer on resize or viewport source box change + pixman_region32_union_rect(buffer_damage, buffer_damage, 0, 0, + pending->buffer_width, pending->buffer_height); + } else { + // Copy over surface damage + buffer damage + pixman_region32_t surface_damage; + pixman_region32_init(&surface_damage); + + pixman_region32_copy(&surface_damage, &pending->surface_damage); + + if (pending->viewport.has_dst) { + int src_width, src_height; + surface_state_viewport_src_size(pending, &src_width, &src_height); + float scale_x = (float)pending->viewport.dst_width / src_width; + float scale_y = (float)pending->viewport.dst_height / src_height; + wlr_region_scale_xy(&surface_damage, &surface_damage, + 1.0 / scale_x, 1.0 / scale_y); + } + if (pending->viewport.has_src) { + // This is lossy: do a best-effort conversion + pixman_region32_translate(&surface_damage, + floor(pending->viewport.src.x), + floor(pending->viewport.src.y)); + } + + wlr_region_scale(&surface_damage, &surface_damage, pending->scale); + + int width, height; + surface_state_transformed_buffer_size(pending, &width, &height); + wlr_region_transform(&surface_damage, &surface_damage, + wlr_output_transform_invert(pending->transform), + width, height); + + pixman_region32_union(buffer_damage, + &pending->buffer_damage, &surface_damage); + + pixman_region32_fini(&surface_damage); + } +} + +/** + * Append pending state to current state and clear pending state. + */ +static void surface_state_move(struct wlr_surface_state *state, + struct wlr_surface_state *next) { + state->width = next->width; + state->height = next->height; + state->buffer_width = next->buffer_width; + state->buffer_height = next->buffer_height; + + if (next->committed & WLR_SURFACE_STATE_SCALE) { + state->scale = next->scale; + } + if (next->committed & WLR_SURFACE_STATE_TRANSFORM) { + state->transform = next->transform; + } + if (next->committed & WLR_SURFACE_STATE_BUFFER) { + state->dx = next->dx; + state->dy = next->dy; + next->dx = next->dy = 0; + + wlr_buffer_unlock(state->buffer); + state->buffer = NULL; + if (next->buffer) { + state->buffer = wlr_buffer_lock(next->buffer); + } + wlr_buffer_unlock(next->buffer); + next->buffer = NULL; + } else { + state->dx = state->dy = 0; + } + if (next->committed & WLR_SURFACE_STATE_SURFACE_DAMAGE) { + pixman_region32_copy(&state->surface_damage, &next->surface_damage); + pixman_region32_clear(&next->surface_damage); + } else { + pixman_region32_clear(&state->surface_damage); + } + if (next->committed & WLR_SURFACE_STATE_BUFFER_DAMAGE) { + pixman_region32_copy(&state->buffer_damage, &next->buffer_damage); + pixman_region32_clear(&next->buffer_damage); + } else { + pixman_region32_clear(&state->buffer_damage); + } + if (next->committed & WLR_SURFACE_STATE_OPAQUE_REGION) { + pixman_region32_copy(&state->opaque, &next->opaque); + } + if (next->committed & WLR_SURFACE_STATE_INPUT_REGION) { + pixman_region32_copy(&state->input, &next->input); + } + if (next->committed & WLR_SURFACE_STATE_VIEWPORT) { + memcpy(&state->viewport, &next->viewport, sizeof(state->viewport)); + } + if (next->committed & WLR_SURFACE_STATE_FRAME_CALLBACK_LIST) { + wl_list_insert_list(&state->frame_callback_list, + &next->frame_callback_list); + wl_list_init(&next->frame_callback_list); + } + + state->committed |= next->committed; + next->committed = 0; + + state->seq = next->seq; + + state->cached_state_locks = next->cached_state_locks; + next->cached_state_locks = 0; +} + +static void surface_apply_damage(struct wlr_surface *surface) { + if (surface->current.buffer == NULL) { + // NULL commit + if (surface->buffer != NULL) { + wlr_buffer_unlock(&surface->buffer->base); + } + surface->buffer = NULL; + return; + } + + if (surface->buffer != NULL) { + if (wlr_client_buffer_apply_damage(surface->buffer, + surface->current.buffer, &surface->buffer_damage)) { + wlr_buffer_unlock(surface->current.buffer); + surface->current.buffer = NULL; + return; + } + } + + struct wlr_client_buffer *buffer = wlr_client_buffer_create( + surface->current.buffer, surface->renderer); + + wlr_buffer_unlock(surface->current.buffer); + surface->current.buffer = NULL; + + if (buffer == NULL) { + wlr_log(WLR_ERROR, "Failed to upload buffer"); + return; + } + + if (surface->buffer != NULL) { + wlr_buffer_unlock(&surface->buffer->base); + } + surface->buffer = buffer; +} + +static void surface_update_opaque_region(struct wlr_surface *surface) { + struct wlr_texture *texture = wlr_surface_get_texture(surface); + if (texture == NULL) { + pixman_region32_clear(&surface->opaque_region); + return; + } + + if (wlr_texture_is_opaque(texture)) { + pixman_region32_init_rect(&surface->opaque_region, + 0, 0, surface->current.width, surface->current.height); + return; + } + + pixman_region32_intersect_rect(&surface->opaque_region, + &surface->current.opaque, + 0, 0, surface->current.width, surface->current.height); +} + +static void surface_update_input_region(struct wlr_surface *surface) { + pixman_region32_intersect_rect(&surface->input_region, + &surface->current.input, + 0, 0, surface->current.width, surface->current.height); +} + +static void surface_state_init(struct wlr_surface_state *state); + +static void subsurface_parent_commit(struct wlr_subsurface *subsurface); + +static void surface_cache_pending(struct wlr_surface *surface) { + struct wlr_surface_state *cached = calloc(1, sizeof(*cached)); + if (!cached) { + wl_resource_post_no_memory(surface->resource); + return; + } + + surface_state_init(cached); + surface_state_move(cached, &surface->pending); + + wl_list_insert(surface->cached.prev, &cached->cached_state_link); + + surface->pending.seq++; +} + +static void surface_commit_state(struct wlr_surface *surface, + struct wlr_surface_state *next) { + assert(next->cached_state_locks == 0); + + if (surface->role && surface->role->precommit) { + surface->role->precommit(surface, next); + } + + bool invalid_buffer = next->committed & WLR_SURFACE_STATE_BUFFER; + + surface->sx += next->dx; + surface->sy += next->dy; + surface_update_damage(&surface->buffer_damage, &surface->current, next); + + pixman_region32_clear(&surface->external_damage); + if (surface->current.width > next->width || + surface->current.height > next->height || + next->dx != 0 || next->dy != 0) { + pixman_region32_union_rect(&surface->external_damage, + &surface->external_damage, -next->dx, -next->dy, + surface->current.width, surface->current.height); + } + + surface->previous.scale = surface->current.scale; + surface->previous.transform = surface->current.transform; + surface->previous.width = surface->current.width; + surface->previous.height = surface->current.height; + surface->previous.buffer_width = surface->current.buffer_width; + surface->previous.buffer_height = surface->current.buffer_height; + + surface_state_move(&surface->current, next); + + if (invalid_buffer) { + surface_apply_damage(surface); + } + surface_update_opaque_region(surface); + surface_update_input_region(surface); + + // commit subsurface order + struct wlr_subsurface *subsurface; + wl_list_for_each_reverse(subsurface, &surface->pending.subsurfaces_above, + pending.link) { + wl_list_remove(&subsurface->current.link); + wl_list_insert(&surface->current.subsurfaces_above, + &subsurface->current.link); + + subsurface_parent_commit(subsurface); + } + wl_list_for_each_reverse(subsurface, &surface->pending.subsurfaces_below, + pending.link) { + wl_list_remove(&subsurface->current.link); + wl_list_insert(&surface->current.subsurfaces_below, + &subsurface->current.link); + + subsurface_parent_commit(subsurface); + } + + // If we're committing the pending state, bump the pending sequence number + // here, to allow commit listeners to lock the new pending state. + if (next == &surface->pending) { + surface->pending.seq++; + } + + if (surface->role && surface->role->commit) { + surface->role->commit(surface); + } + + wlr_signal_emit_safe(&surface->events.commit, surface); +} + +static void collect_subsurface_damage_iter(struct wlr_surface *surface, + int sx, int sy, void *data) { + struct wlr_subsurface *subsurface = data; + pixman_region32_t *damage = &subsurface->parent->external_damage; + pixman_region32_union_rect(damage, damage, + subsurface->current.x + sx, + subsurface->current.y + sy, + surface->current.width, surface->current.height); +} + +// TODO: untangle from wlr_surface +static void subsurface_parent_commit(struct wlr_subsurface *subsurface) { + struct wlr_surface *surface = subsurface->surface; + + bool moved = subsurface->current.x != subsurface->pending.x || + subsurface->current.y != subsurface->pending.y; + if (subsurface->mapped && moved) { + wlr_surface_for_each_surface(surface, + collect_subsurface_damage_iter, subsurface); + } + + if (subsurface->synchronized && subsurface->has_cache) { + wlr_surface_unlock_cached(surface, subsurface->cached_seq); + subsurface->has_cache = false; + } + + subsurface->current.x = subsurface->pending.x; + subsurface->current.y = subsurface->pending.y; + if (subsurface->mapped && (moved || subsurface->reordered)) { + subsurface->reordered = false; + wlr_surface_for_each_surface(surface, + collect_subsurface_damage_iter, subsurface); + } + + if (!subsurface->added) { + subsurface->added = true; + wlr_signal_emit_safe(&subsurface->parent->events.new_subsurface, + subsurface); + } +} + +static void surface_handle_commit(struct wl_client *client, + struct wl_resource *resource) { + struct wlr_surface *surface = wlr_surface_from_resource(resource); + surface_finalize_pending(surface); + + wlr_signal_emit_safe(&surface->events.client_commit, NULL); + + if (surface->pending.cached_state_locks > 0 || !wl_list_empty(&surface->cached)) { + surface_cache_pending(surface); + } else { + surface_commit_state(surface, &surface->pending); + } +} + +static void surface_handle_set_buffer_transform(struct wl_client *client, + struct wl_resource *resource, int32_t transform) { + if (transform < WL_OUTPUT_TRANSFORM_NORMAL || + transform > WL_OUTPUT_TRANSFORM_FLIPPED_270) { + wl_resource_post_error(resource, WL_SURFACE_ERROR_INVALID_TRANSFORM, + "Specified transform value (%d) is invalid", transform); + return; + } + struct wlr_surface *surface = wlr_surface_from_resource(resource); + surface->pending.committed |= WLR_SURFACE_STATE_TRANSFORM; + surface->pending.transform = transform; +} + +static void surface_handle_set_buffer_scale(struct wl_client *client, + struct wl_resource *resource, int32_t scale) { + if (scale <= 0) { + wl_resource_post_error(resource, WL_SURFACE_ERROR_INVALID_SCALE, + "Specified scale value (%d) is not positive", scale); + return; + } + struct wlr_surface *surface = wlr_surface_from_resource(resource); + surface->pending.committed |= WLR_SURFACE_STATE_SCALE; + surface->pending.scale = scale; +} + +static void surface_handle_damage_buffer(struct wl_client *client, + struct wl_resource *resource, + int32_t x, int32_t y, int32_t width, + int32_t height) { + struct wlr_surface *surface = wlr_surface_from_resource(resource); + if (width < 0 || height < 0) { + return; + } + surface->pending.committed |= WLR_SURFACE_STATE_BUFFER_DAMAGE; + pixman_region32_union_rect(&surface->pending.buffer_damage, + &surface->pending.buffer_damage, + x, y, width, height); +} + +static const struct wl_surface_interface surface_implementation = { + .destroy = surface_handle_destroy, + .attach = surface_handle_attach, + .damage = surface_handle_damage, + .frame = surface_handle_frame, + .set_opaque_region = surface_handle_set_opaque_region, + .set_input_region = surface_handle_set_input_region, + .commit = surface_handle_commit, + .set_buffer_transform = surface_handle_set_buffer_transform, + .set_buffer_scale = surface_handle_set_buffer_scale, + .damage_buffer = surface_handle_damage_buffer }; -static void subcompositor_bind(struct wl_client *client, void *data, - uint32_t version, uint32_t id) { - struct wlr_subcompositor *subcompositor = data; - struct wl_resource *resource = - wl_resource_create(client, &wl_subcompositor_interface, 1, id); - if (resource == NULL) { - wl_client_post_no_memory(client); - return; - } - wl_resource_set_implementation(resource, &subcompositor_impl, - subcompositor, NULL); +struct wlr_surface *wlr_surface_from_resource(struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &wl_surface_interface, + &surface_implementation)); + return wl_resource_get_user_data(resource); } -static bool subcompositor_init(struct wlr_subcompositor *subcompositor, - struct wl_display *display) { - subcompositor->global = wl_global_create(display, - &wl_subcompositor_interface, SUBCOMPOSITOR_VERSION, subcompositor, - subcompositor_bind); - if (subcompositor->global == NULL) { - wlr_log_errno(WLR_ERROR, "Could not allocate subcompositor global"); +static void surface_state_init(struct wlr_surface_state *state) { + state->scale = 1; + state->transform = WL_OUTPUT_TRANSFORM_NORMAL; + + wl_list_init(&state->subsurfaces_above); + wl_list_init(&state->subsurfaces_below); + + wl_list_init(&state->frame_callback_list); + + pixman_region32_init(&state->surface_damage); + pixman_region32_init(&state->buffer_damage); + pixman_region32_init(&state->opaque); + pixman_region32_init_rect(&state->input, + INT32_MIN, INT32_MIN, UINT32_MAX, UINT32_MAX); +} + +static void surface_state_finish(struct wlr_surface_state *state) { + wlr_buffer_unlock(state->buffer); + + struct wl_resource *resource, *tmp; + wl_resource_for_each_safe(resource, tmp, &state->frame_callback_list) { + wl_resource_destroy(resource); + } + + pixman_region32_fini(&state->surface_damage); + pixman_region32_fini(&state->buffer_damage); + pixman_region32_fini(&state->opaque); + pixman_region32_fini(&state->input); +} + +static void surface_state_destroy_cached(struct wlr_surface_state *state) { + surface_state_finish(state); + wl_list_remove(&state->cached_state_link); + free(state); +} + +static void surface_output_destroy(struct wlr_surface_output *surface_output); + +static void surface_handle_resource_destroy(struct wl_resource *resource) { + struct wlr_surface *surface = wlr_surface_from_resource(resource); + + struct wlr_surface_output *surface_output, *surface_output_tmp; + wl_list_for_each_safe(surface_output, surface_output_tmp, + &surface->current_outputs, link) { + surface_output_destroy(surface_output); + } + + wlr_signal_emit_safe(&surface->events.destroy, surface); + + wlr_addon_set_finish(&surface->addons); + + struct wlr_surface_state *cached, *cached_tmp; + wl_list_for_each_safe(cached, cached_tmp, &surface->cached, cached_state_link) { + surface_state_destroy_cached(cached); + } + + wl_list_remove(&surface->renderer_destroy.link); + surface_state_finish(&surface->pending); + surface_state_finish(&surface->current); + pixman_region32_fini(&surface->buffer_damage); + pixman_region32_fini(&surface->external_damage); + pixman_region32_fini(&surface->opaque_region); + pixman_region32_fini(&surface->input_region); + if (surface->buffer != NULL) { + wlr_buffer_unlock(&surface->buffer->base); + } + free(surface); +} + +static void surface_handle_renderer_destroy(struct wl_listener *listener, + void *data) { + struct wlr_surface *surface = + wl_container_of(listener, surface, renderer_destroy); + wl_resource_destroy(surface->resource); +} + +static struct wlr_surface *surface_create(struct wl_client *client, + uint32_t version, uint32_t id, struct wlr_renderer *renderer) { + struct wlr_surface *surface = calloc(1, sizeof(struct wlr_surface)); + if (!surface) { + wl_client_post_no_memory(client); + return NULL; + } + surface->resource = wl_resource_create(client, &wl_surface_interface, + version, id); + if (surface->resource == NULL) { + free(surface); + wl_client_post_no_memory(client); + return NULL; + } + wl_resource_set_implementation(surface->resource, &surface_implementation, + surface, surface_handle_resource_destroy); + + wlr_log(WLR_DEBUG, "New wlr_surface %p (res %p)", surface, surface->resource); + + surface->renderer = renderer; + + surface_state_init(&surface->current); + surface_state_init(&surface->pending); + surface->pending.seq = 1; + + wl_signal_init(&surface->events.client_commit); + wl_signal_init(&surface->events.commit); + wl_signal_init(&surface->events.destroy); + wl_signal_init(&surface->events.new_subsurface); + wl_list_init(&surface->current_outputs); + wl_list_init(&surface->cached); + pixman_region32_init(&surface->buffer_damage); + pixman_region32_init(&surface->external_damage); + pixman_region32_init(&surface->opaque_region); + pixman_region32_init(&surface->input_region); + wlr_addon_set_init(&surface->addons); + + wl_signal_add(&renderer->events.destroy, &surface->renderer_destroy); + surface->renderer_destroy.notify = surface_handle_renderer_destroy; + + return surface; +} + +struct wlr_texture *wlr_surface_get_texture(struct wlr_surface *surface) { + if (surface->buffer == NULL) { + return NULL; + } + return surface->buffer->texture; +} + +bool wlr_surface_has_buffer(struct wlr_surface *surface) { + return wlr_surface_get_texture(surface) != NULL; +} + +bool wlr_surface_set_role(struct wlr_surface *surface, + const struct wlr_surface_role *role, void *role_data, + struct wl_resource *error_resource, uint32_t error_code) { + assert(role != NULL); + + 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@%" PRIu32 ", already has role %s\n", + role->name, wl_resource_get_id(surface->resource), + surface->role->name); + } + return false; + } + 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@%" PRIu32 "," + "role object still exists", role->name, + wl_resource_get_id(surface->resource)); return false; } + surface->role = role; + surface->role_data = role_data; return true; } -static void subcompositor_finish(struct wlr_subcompositor *subcompositor) { - wl_global_destroy(subcompositor->global); +uint32_t wlr_surface_lock_pending(struct wlr_surface *surface) { + surface->pending.cached_state_locks++; + return surface->pending.seq; } +void wlr_surface_unlock_cached(struct wlr_surface *surface, uint32_t seq) { + if (surface->pending.seq == seq) { + assert(surface->pending.cached_state_locks > 0); + surface->pending.cached_state_locks--; + return; + } + + bool found = false; + struct wlr_surface_state *cached; + wl_list_for_each(cached, &surface->cached, cached_state_link) { + if (cached->seq == seq) { + found = true; + break; + } + } + assert(found); + + assert(cached->cached_state_locks > 0); + cached->cached_state_locks--; + + if (cached->cached_state_locks != 0) { + return; + } + + if (cached->cached_state_link.prev != &surface->cached) { + // This isn't the first cached state. This means we're blocked on a + // previous cached state. + return; + } + + // TODO: consider merging all committed states together + struct wlr_surface_state *next, *tmp; + wl_list_for_each_safe(next, tmp, &surface->cached, cached_state_link) { + if (next->cached_state_locks > 0) { + break; + } + + surface_commit_state(surface, next); + surface_state_destroy_cached(next); + } +} + +struct wlr_surface *wlr_surface_get_root_surface(struct wlr_surface *surface) { + while (wlr_surface_is_subsurface(surface)) { + struct wlr_subsurface *subsurface = + wlr_subsurface_from_wlr_surface(surface); + if (subsurface == NULL) { + break; + } + surface = subsurface->parent; + } + return surface; +} + +bool wlr_surface_point_accepts_input(struct wlr_surface *surface, + double sx, double sy) { + return sx >= 0 && sx < surface->current.width && + sy >= 0 && sy < surface->current.height && + pixman_region32_contains_point(&surface->current.input, floor(sx), floor(sy), NULL); +} + +struct wlr_surface *wlr_surface_surface_at(struct wlr_surface *surface, + double sx, double sy, double *sub_x, double *sub_y) { + struct wlr_subsurface *subsurface; + wl_list_for_each_reverse(subsurface, &surface->current.subsurfaces_above, + current.link) { + if (!subsurface->mapped) { + continue; + } + + double _sub_x = subsurface->current.x; + double _sub_y = subsurface->current.y; + struct wlr_surface *sub = wlr_surface_surface_at(subsurface->surface, + sx - _sub_x, sy - _sub_y, sub_x, sub_y); + if (sub != NULL) { + return sub; + } + } + + if (wlr_surface_point_accepts_input(surface, sx, sy)) { + if (sub_x) { + *sub_x = sx; + } + if (sub_y) { + *sub_y = sy; + } + return surface; + } + + wl_list_for_each_reverse(subsurface, &surface->current.subsurfaces_below, + current.link) { + if (!subsurface->mapped) { + continue; + } + + double _sub_x = subsurface->current.x; + double _sub_y = subsurface->current.y; + struct wlr_surface *sub = wlr_surface_surface_at(subsurface->surface, + sx - _sub_x, sy - _sub_y, sub_x, sub_y); + if (sub != NULL) { + return sub; + } + } + + 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); + } + } +} + +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_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; + } + } +} + +void wlr_surface_send_frame_done(struct wlr_surface *surface, + const struct timespec *when) { + struct wl_resource *resource, *tmp; + wl_resource_for_each_safe(resource, tmp, + &surface->current.frame_callback_list) { + wl_callback_send_done(resource, timespec_to_msec(when)); + wl_resource_destroy(resource); + } +} + +static void surface_for_each_surface(struct wlr_surface *surface, int x, int y, + wlr_surface_iterator_func_t iterator, void *user_data) { + struct wlr_subsurface *subsurface; + wl_list_for_each(subsurface, &surface->current.subsurfaces_below, current.link) { + if (!subsurface->mapped) { + continue; + } + + struct wlr_subsurface_parent_state *state = &subsurface->current; + int sx = state->x; + int sy = state->y; + + surface_for_each_surface(subsurface->surface, x + sx, y + sy, + iterator, user_data); + } + + iterator(surface, x, y, user_data); + + wl_list_for_each(subsurface, &surface->current.subsurfaces_above, current.link) { + if (!subsurface->mapped) { + continue; + } + + struct wlr_subsurface_parent_state *state = &subsurface->current; + int sx = state->x; + int sy = state->y; + + surface_for_each_surface(subsurface->surface, x + sx, y + sy, + iterator, user_data); + } +} + +void wlr_surface_for_each_surface(struct wlr_surface *surface, + wlr_surface_iterator_func_t iterator, void *user_data) { + surface_for_each_surface(surface, 0, 0, iterator, user_data); +} + +struct bound_acc { + int32_t min_x, min_y; + int32_t max_x, max_y; +}; + +static void handle_bounding_box_surface(struct wlr_surface *surface, + int x, int y, void *data) { + struct bound_acc *acc = data; + + acc->min_x = min(x, acc->min_x); + acc->min_y = min(y, acc->min_y); + + acc->max_x = max(x + surface->current.width, acc->max_x); + acc->max_y = max(y + surface->current.height, acc->max_y); +} + +void wlr_surface_get_extends(struct wlr_surface *surface, struct wlr_box *box) { + struct bound_acc acc = { + .min_x = 0, + .min_y = 0, + .max_x = surface->current.width, + .max_y = surface->current.height, + }; + + wlr_surface_for_each_surface(surface, handle_bounding_box_surface, &acc); + + box->x = acc.min_x; + box->y = acc.min_y; + box->width = acc.max_x - acc.min_x; + box->height = acc.max_y - acc.min_y; +} + +static void crop_region(pixman_region32_t *dst, pixman_region32_t *src, + const struct wlr_box *box) { + pixman_region32_intersect_rect(dst, src, + box->x, box->y, box->width, box->height); + pixman_region32_translate(dst, -box->x, -box->y); +} + +void wlr_surface_get_effective_damage(struct wlr_surface *surface, + pixman_region32_t *damage) { + pixman_region32_clear(damage); + + // Transform and copy the buffer damage in terms of surface coordinates. + wlr_region_transform(damage, &surface->buffer_damage, + surface->current.transform, surface->current.buffer_width, + surface->current.buffer_height); + wlr_region_scale(damage, damage, 1.0 / (float)surface->current.scale); + + if (surface->current.viewport.has_src) { + struct wlr_box src_box = { + .x = floor(surface->current.viewport.src.x), + .y = floor(surface->current.viewport.src.y), + .width = ceil(surface->current.viewport.src.width), + .height = ceil(surface->current.viewport.src.height), + }; + crop_region(damage, damage, &src_box); + } + if (surface->current.viewport.has_dst) { + int src_width, src_height; + surface_state_viewport_src_size(&surface->current, + &src_width, &src_height); + float scale_x = (float)surface->current.viewport.dst_width / src_width; + float scale_y = (float)surface->current.viewport.dst_height / src_height; + wlr_region_scale_xy(damage, damage, scale_x, scale_y); + } + + pixman_region32_union(damage, damage, &surface->external_damage); +} + +void wlr_surface_get_buffer_source_box(struct wlr_surface *surface, + struct wlr_fbox *box) { + box->x = box->y = 0; + box->width = surface->current.buffer_width; + box->height = surface->current.buffer_height; + + if (surface->current.viewport.has_src) { + box->x = surface->current.viewport.src.x * surface->current.scale; + box->y = surface->current.viewport.src.y * surface->current.scale; + box->width = surface->current.viewport.src.width * surface->current.scale; + box->height = surface->current.viewport.src.height * surface->current.scale; + + int width, height; + surface_state_transformed_buffer_size(&surface->current, &width, &height); + wlr_fbox_transform(box, box, + wlr_output_transform_invert(surface->current.transform), + width, height); + } +} static const struct wl_compositor_interface compositor_impl; @@ -152,11 +1114,11 @@ static void compositor_bind(struct wl_client *wl_client, void *data, wl_resource_set_implementation(resource, &compositor_impl, compositor, NULL); } -static void handle_display_destroy(struct wl_listener *listener, void *data) { +static void compositor_handle_display_destroy( + struct wl_listener *listener, void *data) { struct wlr_compositor *compositor = wl_container_of(listener, compositor, display_destroy); - wlr_signal_emit_safe(&compositor->events.destroy, compositor); - subcompositor_finish(&compositor->subcompositor); + wlr_signal_emit_safe(&compositor->events.destroy, NULL); wl_list_remove(&compositor->display_destroy.link); wl_global_destroy(compositor->global); free(compositor); @@ -164,10 +1126,8 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_compositor *wlr_compositor_create(struct wl_display *display, struct wlr_renderer *renderer) { - struct wlr_compositor *compositor = - calloc(1, sizeof(struct wlr_compositor)); + struct wlr_compositor *compositor = calloc(1, sizeof(*compositor)); if (!compositor) { - wlr_log_errno(WLR_ERROR, "Could not allocate wlr compositor"); return NULL; } @@ -175,7 +1135,6 @@ struct wlr_compositor *wlr_compositor_create(struct wl_display *display, COMPOSITOR_VERSION, compositor, compositor_bind); if (!compositor->global) { free(compositor); - wlr_log_errno(WLR_ERROR, "Could not allocate compositor global"); return NULL; } compositor->renderer = renderer; @@ -183,9 +1142,7 @@ struct wlr_compositor *wlr_compositor_create(struct wl_display *display, wl_signal_init(&compositor->events.new_surface); wl_signal_init(&compositor->events.destroy); - subcompositor_init(&compositor->subcompositor, display); - - compositor->display_destroy.notify = handle_display_destroy; + compositor->display_destroy.notify = compositor_handle_display_destroy; wl_display_add_destroy_listener(display, &compositor->display_destroy); return compositor; diff --git a/types/wlr_cursor.c b/types/wlr_cursor.c index 7ae9c46cd..3160ca03b 100644 --- a/types/wlr_cursor.c +++ b/types/wlr_cursor.c @@ -19,7 +19,7 @@ struct wlr_cursor_device { struct wlr_input_device *device; struct wl_list link; struct wlr_output *mapped_output; - struct wlr_box *mapped_box; + struct wlr_box mapped_box; // empty if unset struct wl_listener motion; struct wl_listener motion_absolute; @@ -63,7 +63,7 @@ struct wlr_cursor_state { struct wl_list output_cursors; // wlr_cursor_output_cursor::link struct wlr_output_layout *layout; struct wlr_output *mapped_output; - struct wlr_box *mapped_box; + struct wlr_box mapped_box; // empty if unset struct wl_listener layout_add; struct wl_listener layout_change; @@ -237,33 +237,33 @@ static void cursor_warp_unchecked(struct wlr_cursor *cur, * Absolute movement for touch and pen devices will be relative to this box and * pointer movement will be constrained to this box. * - * If none of these are set, returns NULL and absolute movement should be + * If none of these are set, empties the box and absolute movement should be * relative to the extents of the layout. */ -static struct wlr_box *get_mapping(struct wlr_cursor *cur, - struct wlr_input_device *dev) { +static void get_mapping(struct wlr_cursor *cur, + struct wlr_input_device *dev, struct wlr_box *box) { assert(cur->state->layout); struct wlr_cursor_device *c_device = get_cursor_device(cur, dev); if (c_device) { - if (c_device->mapped_box) { - return c_device->mapped_box; - } - if (c_device->mapped_output) { - return wlr_output_layout_get_box(cur->state->layout, - c_device->mapped_output); + if (!wlr_box_empty(&c_device->mapped_box)) { + *box = c_device->mapped_box; + return; + } else if (c_device->mapped_output) { + wlr_output_layout_get_box(cur->state->layout, + c_device->mapped_output, box); + return; } } - if (cur->state->mapped_box) { - return cur->state->mapped_box; + if (!wlr_box_empty(&cur->state->mapped_box)) { + *box = cur->state->mapped_box; + } else if (cur->state->mapped_output) { + wlr_output_layout_get_box(cur->state->layout, + cur->state->mapped_output, box); + } else { + box->width = box->height = 0; } - if (cur->state->mapped_output) { - return wlr_output_layout_get_box(cur->state->layout, - cur->state->mapped_output); - } - - return NULL; } bool wlr_cursor_warp(struct wlr_cursor *cur, struct wlr_input_device *dev, @@ -271,9 +271,10 @@ bool wlr_cursor_warp(struct wlr_cursor *cur, struct wlr_input_device *dev, assert(cur->state->layout); bool result = false; - struct wlr_box *mapping = get_mapping(cur, dev); - if (mapping) { - result = wlr_box_contains_point(mapping, lx, ly); + struct wlr_box mapping; + get_mapping(cur, dev, &mapping); + if (!wlr_box_empty(&mapping)) { + result = wlr_box_contains_point(&mapping, lx, ly); } else { result = wlr_output_layout_contains_point(cur->state->layout, NULL, lx, ly); @@ -288,9 +289,10 @@ bool wlr_cursor_warp(struct wlr_cursor *cur, struct wlr_input_device *dev, void wlr_cursor_warp_closest(struct wlr_cursor *cur, struct wlr_input_device *dev, double lx, double ly) { - struct wlr_box *mapping = get_mapping(cur, dev); - if (mapping) { - wlr_box_closest_point(mapping, lx, ly, &lx, &ly); + struct wlr_box mapping; + get_mapping(cur, dev, &mapping); + if (!wlr_box_empty(&mapping)) { + wlr_box_closest_point(&mapping, lx, ly, &lx, &ly); if (isnan(lx) || isnan(ly)) { lx = 0; ly = 0; @@ -308,13 +310,14 @@ void wlr_cursor_absolute_to_layout_coords(struct wlr_cursor *cur, double *lx, double *ly) { assert(cur->state->layout); - struct wlr_box *mapping = get_mapping(cur, dev); - if (!mapping) { - mapping = wlr_output_layout_get_box(cur->state->layout, NULL); + struct wlr_box mapping; + get_mapping(cur, dev, &mapping); + if (wlr_box_empty(&mapping)) { + wlr_output_layout_get_box(cur->state->layout, NULL, &mapping); } - *lx = !isnan(x) ? mapping->width * x + mapping->x : cur->x; - *ly = !isnan(y) ? mapping->height * y + mapping->y : cur->y; + *lx = !isnan(x) ? mapping.width * x + mapping.x : cur->x; + *ly = !isnan(y) ? mapping.height * y + mapping.y : cur->y; } void wlr_cursor_warp_absolute(struct wlr_cursor *cur, @@ -854,7 +857,7 @@ void wlr_cursor_map_input_to_output(struct wlr_cursor *cur, struct wlr_cursor_device *c_device = get_cursor_device(cur, dev); if (!c_device) { wlr_log(WLR_ERROR, "Cannot map device \"%s\" to output" - "(not found in this cursor)", dev->name); + " (not found in this cursor)", dev->name); return; } @@ -862,23 +865,20 @@ void wlr_cursor_map_input_to_output(struct wlr_cursor *cur, } void wlr_cursor_map_to_region(struct wlr_cursor *cur, - struct wlr_box *box) { - if (box && wlr_box_empty(box)) { - wlr_log(WLR_ERROR, "cannot map cursor to an empty region"); - return; + const struct wlr_box *box) { + if (box) { + if (wlr_box_empty(box)) { + wlr_log(WLR_ERROR, "cannot map cursor to an empty region"); + return; + } + cur->state->mapped_box = *box; + } else { + cur->state->mapped_box.width = cur->state->mapped_box.height = 0; } - - cur->state->mapped_box = box; } void wlr_cursor_map_input_to_region(struct wlr_cursor *cur, - struct wlr_input_device *dev, struct wlr_box *box) { - if (box && wlr_box_empty(box)) { - wlr_log(WLR_ERROR, "cannot map device \"%s\" input to an empty region", - dev->name); - return; - } - + struct wlr_input_device *dev, const struct wlr_box *box) { struct wlr_cursor_device *c_device = get_cursor_device(cur, dev); if (!c_device) { wlr_log(WLR_ERROR, "Cannot map device \"%s\" to geometry (not found in" @@ -886,5 +886,15 @@ void wlr_cursor_map_input_to_region(struct wlr_cursor *cur, return; } - c_device->mapped_box = box; + if (box) { + if (wlr_box_empty(box)) { + wlr_log(WLR_ERROR, + "cannot map device \"%s\" input to an empty region", + dev->name); + return; + } + c_device->mapped_box = *box; + } else { + c_device->mapped_box.width = c_device->mapped_box.height = 0; + } } diff --git a/types/wlr_drm_lease_v1.c b/types/wlr_drm_lease_v1.c index a917930ef..7cb9974d7 100644 --- a/types/wlr_drm_lease_v1.c +++ b/types/wlr_drm_lease_v1.c @@ -47,30 +47,6 @@ static struct wlr_drm_lease_v1 *drm_lease_v1_from_resource( return wl_resource_get_user_data(resource); } -static void drm_lease_v1_destroy(struct wlr_drm_lease_v1 *lease) { - if (!lease) { - return; - } - - wlr_log(WLR_DEBUG, "Destroying lease %"PRIu32, lease->lessee_id); - - wp_drm_lease_v1_send_finished(lease->resource); - - struct wlr_drm_lease_device_v1 *device = lease->device; - wlr_drm_backend_terminate_lease(device->backend, lease->lessee_id); - lease->lessee_id = 0; - - for (size_t i = 0; i < lease->n_connectors; ++i) { - lease->connectors[i]->active_lease = NULL; - } - - wl_list_remove(&lease->link); - wl_resource_set_user_data(lease->resource, NULL); - - free(lease->connectors); - free(lease); -} - static void drm_lease_request_v1_destroy( struct wlr_drm_lease_request_v1 *request) { if (!request) { @@ -95,7 +71,7 @@ static void drm_lease_connector_v1_destroy( wlr_log(WLR_DEBUG, "Destroying connector %s", connector->output->name); if (connector->active_lease) { - drm_lease_v1_destroy(connector->active_lease); + wlr_drm_lease_terminate(connector->active_lease->drm_lease); } struct wl_resource *resource, *tmp; @@ -142,7 +118,7 @@ static void drm_lease_device_v1_destroy( struct wlr_drm_lease_v1 *lease, *tmp_lease; wl_list_for_each_safe(lease, tmp_lease, &device->leases, link) { - drm_lease_v1_destroy(lease); + wlr_drm_lease_terminate(lease->drm_lease); } struct wlr_drm_lease_connector_v1 *connector, *tmp_connector; @@ -151,11 +127,31 @@ static void drm_lease_device_v1_destroy( } wl_list_remove(&device->link); - wlr_global_destroy_safe(device->global, device->manager->display); + wlr_global_destroy_safe(device->global); free(device); } +static void lease_handle_destroy(struct wl_listener *listener, void *data) { + struct wlr_drm_lease_v1 *lease = wl_container_of(listener, lease, destroy); + + wlr_log(WLR_DEBUG, "Destroying lease %"PRIu32, lease->drm_lease->lessee_id); + + wp_drm_lease_v1_send_finished(lease->resource); + + wl_list_remove(&lease->destroy.link); + + for (size_t i = 0; i < lease->n_connectors; ++i) { + lease->connectors[i]->active_lease = NULL; + } + + wl_list_remove(&lease->link); + wl_resource_set_user_data(lease->resource, NULL); + + free(lease->connectors); + free(lease); +} + struct wlr_drm_lease_v1 *wlr_drm_lease_request_v1_grant( struct wlr_drm_lease_request_v1 *request) { assert(request->lease); @@ -171,10 +167,10 @@ struct wlr_drm_lease_v1 *wlr_drm_lease_request_v1_grant( outputs[i] = request->connectors[i]->output; } - int fd = wlr_drm_create_lease(outputs, request->n_connectors, - &lease->lessee_id); - if (fd < 0) { - wlr_log_errno(WLR_ERROR, "drm_create_lease failed"); + int fd; + lease->drm_lease = wlr_drm_create_lease(outputs, request->n_connectors, &fd); + if (!lease->drm_lease) { + wlr_log(WLR_ERROR, "wlr_drm_create_lease failed"); wp_drm_lease_v1_send_finished(lease->resource); return NULL; } @@ -183,6 +179,7 @@ struct wlr_drm_lease_v1 *wlr_drm_lease_request_v1_grant( sizeof(struct wlr_drm_lease_connector_v1 *)); if (!lease->connectors) { wlr_log(WLR_ERROR, "Failed to allocate lease connectors list"); + close(fd); wp_drm_lease_v1_send_finished(lease->resource); return NULL; } @@ -192,6 +189,9 @@ struct wlr_drm_lease_v1 *wlr_drm_lease_request_v1_grant( lease->connectors[i]->active_lease = lease; } + lease->destroy.notify = lease_handle_destroy; + wl_signal_add(&lease->drm_lease->events.destroy, &lease->destroy); + wlr_log(WLR_DEBUG, "Granting request %p", request); wp_drm_lease_v1_send_lease_fd(lease->resource, fd); @@ -212,14 +212,15 @@ void wlr_drm_lease_request_v1_reject( void wlr_drm_lease_v1_revoke(struct wlr_drm_lease_v1 *lease) { assert(lease); - wlr_log(WLR_DEBUG, "Revoking lease %"PRIu32, lease->lessee_id); - - drm_lease_v1_destroy(lease); + wlr_log(WLR_DEBUG, "Revoking lease %"PRIu32, lease->drm_lease->lessee_id); + wlr_drm_lease_terminate(lease->drm_lease); } static void drm_lease_v1_handle_resource_destroy(struct wl_resource *resource) { struct wlr_drm_lease_v1 *lease = drm_lease_v1_from_resource(resource); - drm_lease_v1_destroy(lease); + if (lease != NULL) { + wlr_drm_lease_terminate(lease->drm_lease); + } } static void drm_lease_v1_handle_destroy( diff --git a/types/wlr_export_dmabuf_v1.c b/types/wlr_export_dmabuf_v1.c index 954a3270b..b47758c63 100644 --- a/types/wlr_export_dmabuf_v1.c +++ b/types/wlr_export_dmabuf_v1.c @@ -66,7 +66,7 @@ static void frame_output_handle_commit(struct wl_listener *listener, wl_list_init(&frame->output_commit.link); struct wlr_dmabuf_attributes attribs = {0}; - if (!wlr_buffer_get_dmabuf(frame->output->front_buffer, &attribs)) { + if (!wlr_buffer_get_dmabuf(event->buffer, &attribs)) { zwlr_export_dmabuf_frame_v1_send_cancel(frame->resource, ZWLR_EXPORT_DMABUF_FRAME_V1_CANCEL_REASON_TEMPORARY); frame_destroy(frame); @@ -77,7 +77,7 @@ static void frame_output_handle_commit(struct wl_listener *listener, 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.width, attribs.height, 0, 0, 0, frame_flags, attribs.format, mod_high, mod_low, attribs.n_planes); for (int i = 0; i < attribs.n_planes; ++i) { diff --git a/types/wlr_foreign_toplevel_management_v1.c b/types/wlr_foreign_toplevel_management_v1.c index 49805ef9f..a700a115b 100644 --- a/types/wlr_foreign_toplevel_management_v1.c +++ b/types/wlr_foreign_toplevel_management_v1.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -256,6 +257,23 @@ static void toplevel_send_output(struct wlr_foreign_toplevel_handle_v1 *toplevel toplevel_update_idle_source(toplevel); } +static void toplevel_handle_output_bind(struct wl_listener *listener, + void *data) { + struct wlr_foreign_toplevel_handle_v1_output *toplevel_output = + wl_container_of(listener, toplevel_output, output_bind); + struct wlr_output_event_bind *event = data; + struct wl_client *client = wl_resource_get_client(event->resource); + + struct wl_resource *resource; + wl_resource_for_each(resource, &toplevel_output->toplevel->resources) { + if (wl_resource_get_client(resource) == client) { + send_output_to_resource(resource, toplevel_output->output, true); + } + } + + toplevel_update_idle_source(toplevel_output->toplevel); +} + static void toplevel_handle_output_destroy(struct wl_listener *listener, void *data) { struct wlr_foreign_toplevel_handle_v1_output *toplevel_output = @@ -285,6 +303,9 @@ void wlr_foreign_toplevel_handle_v1_output_enter( toplevel_output->toplevel = toplevel; wl_list_insert(&toplevel->outputs, &toplevel_output->link); + toplevel_output->output_bind.notify = toplevel_handle_output_bind; + wl_signal_add(&output->events.bind, &toplevel_output->output_bind); + toplevel_output->output_destroy.notify = toplevel_handle_output_destroy; wl_signal_add(&output->events.destroy, &toplevel_output->output_destroy); @@ -294,6 +315,7 @@ void wlr_foreign_toplevel_handle_v1_output_enter( static void toplevel_output_destroy( struct wlr_foreign_toplevel_handle_v1_output *toplevel_output) { wl_list_remove(&toplevel_output->link); + wl_list_remove(&toplevel_output->output_bind.link); wl_list_remove(&toplevel_output->output_destroy.link); free(toplevel_output); } diff --git a/types/wlr_fullscreen_shell_v1.c b/types/wlr_fullscreen_shell_v1.c index 59636e4c7..7e9f85539 100644 --- a/types/wlr_fullscreen_shell_v1.c +++ b/types/wlr_fullscreen_shell_v1.c @@ -1,8 +1,8 @@ #include #include +#include #include #include -#include #include #include "util/signal.h" diff --git a/types/wlr_idle_inhibit_v1.c b/types/wlr_idle_inhibit_v1.c index 5acf740e5..61a66dc1f 100644 --- a/types/wlr_idle_inhibit_v1.c +++ b/types/wlr_idle_inhibit_v1.c @@ -3,8 +3,8 @@ #include #include #include +#include #include -#include #include #include "idle-inhibit-unstable-v1-protocol.h" diff --git a/types/wlr_input_device.c b/types/wlr_input_device.c index 1364aa3d9..6897febed 100644 --- a/types/wlr_input_device.c +++ b/types/wlr_input_device.c @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include @@ -14,25 +13,31 @@ #include "util/signal.h" void wlr_input_device_init(struct wlr_input_device *dev, - enum wlr_input_device_type type, - const struct wlr_input_device_impl *impl, - const char *name, int vendor, int product) { + enum wlr_input_device_type type, const char *name) { dev->type = type; - dev->impl = impl; dev->name = strdup(name); - dev->vendor = vendor; - dev->product = product; + dev->vendor = 0; + dev->product = 0; wl_signal_init(&dev->events.destroy); } +void wlr_input_device_finish(struct wlr_input_device *wlr_device) { + if (!wlr_device) { + return; + } + + wlr_signal_emit_safe(&wlr_device->events.destroy, wlr_device); + + free(wlr_device->name); + free(wlr_device->output_name); +} + void wlr_input_device_destroy(struct wlr_input_device *dev) { if (!dev) { return; } - wlr_signal_emit_safe(&dev->events.destroy, dev); - if (dev->_device) { switch (dev->type) { case WLR_INPUT_DEVICE_KEYBOARD: @@ -53,17 +58,9 @@ void wlr_input_device_destroy(struct wlr_input_device *dev) { case WLR_INPUT_DEVICE_TABLET_PAD: wlr_tablet_pad_destroy(dev->tablet_pad); break; - default: - wlr_log(WLR_DEBUG, "Warning: leaking memory %p %p %d", - dev->_device, dev, dev->type); - break; } - } - free(dev->name); - free(dev->output_name); - if (dev->impl && dev->impl->destroy) { - dev->impl->destroy(dev); } else { + wlr_input_device_finish(dev); free(dev); } } diff --git a/types/wlr_input_method_v2.c b/types/wlr_input_method_v2.c index 7ccd31ee0..559927eb1 100644 --- a/types/wlr_input_method_v2.c +++ b/types/wlr_input_method_v2.c @@ -6,8 +6,8 @@ #include #include #include +#include #include -#include #include #include #include "input-method-unstable-v2-protocol.h" @@ -157,9 +157,22 @@ static void popup_surface_surface_role_commit(struct wlr_surface *surface) { && popup_surface->input_method->client_active); } +static void popup_surface_surface_role_precommit(struct wlr_surface *surface, + const struct wlr_surface_state *state) { + struct wlr_input_popup_surface_v2 *popup_surface = surface->role_data; + if (popup_surface == NULL) { + return; + } + if (state->committed & WLR_SURFACE_STATE_BUFFER && state->buffer == NULL) { + // This is a NULL commit + popup_surface_set_mapped(popup_surface, false); + } +} + static const struct wlr_surface_role input_popup_surface_v2_role = { .name = "zwp_input_popup_surface_v2", .commit = popup_surface_surface_role_commit, + .precommit = popup_surface_surface_role_precommit, }; bool wlr_surface_is_input_popup_surface_v2(struct wlr_surface *surface) { diff --git a/types/wlr_keyboard.c b/types/wlr_keyboard.c index c54e33c6c..afa689b12 100644 --- a/types/wlr_keyboard.c +++ b/types/wlr_keyboard.c @@ -114,7 +114,10 @@ void wlr_keyboard_notify_key(struct wlr_keyboard *keyboard, } void wlr_keyboard_init(struct wlr_keyboard *kb, - const struct wlr_keyboard_impl *impl) { + const struct wlr_keyboard_impl *impl, const char *name) { + wlr_input_device_init(&kb->base, WLR_INPUT_DEVICE_KEYBOARD, name); + kb->base.keyboard = kb; + kb->impl = impl; wl_signal_init(&kb->events.key); wl_signal_init(&kb->events.modifiers); @@ -134,6 +137,8 @@ void wlr_keyboard_destroy(struct wlr_keyboard *kb) { return; } wlr_signal_emit_safe(&kb->events.destroy, kb); + wlr_input_device_finish(&kb->base); + xkb_state_unref(kb->xkb_state); xkb_keymap_unref(kb->keymap); free(kb->keymap_string); diff --git a/types/wlr_keyboard_group.c b/types/wlr_keyboard_group.c index d614f58db..35fc54702 100644 --- a/types/wlr_keyboard_group.c +++ b/types/wlr_keyboard_group.c @@ -60,16 +60,7 @@ struct wlr_keyboard_group *wlr_keyboard_group_create(void) { return NULL; } - group->input_device = calloc(1, sizeof(struct wlr_input_device)); - if (!group->input_device) { - wlr_log(WLR_ERROR, "Failed to allocate wlr_input_device for group"); - free(group); - return NULL; - } - wl_signal_init(&group->input_device->events.destroy); - group->input_device->keyboard = &group->keyboard; - - wlr_keyboard_init(&group->keyboard, &impl); + wlr_keyboard_init(&group->keyboard, &impl, "keyboard-group"); wl_list_init(&group->devices); wl_list_init(&group->keys); @@ -335,9 +326,7 @@ void wlr_keyboard_group_destroy(struct wlr_keyboard_group *group) { wlr_keyboard_group_remove_keyboard(group, device->keyboard); } wlr_keyboard_destroy(&group->keyboard); - wl_list_remove(&group->input_device->events.destroy.listener_list); wl_list_remove(&group->events.enter.listener_list); wl_list_remove(&group->events.leave.listener_list); - free(group->input_device); free(group); } diff --git a/types/wlr_keyboard_shortcuts_inhibit_v1.c b/types/wlr_keyboard_shortcuts_inhibit_v1.c index fe4e64b04..247d350d8 100644 --- a/types/wlr_keyboard_shortcuts_inhibit_v1.c +++ b/types/wlr_keyboard_shortcuts_inhibit_v1.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include "keyboard-shortcuts-inhibit-unstable-v1-protocol.h" diff --git a/types/wlr_layer_shell_v1.c b/types/wlr_layer_shell_v1.c index 4495472a1..c8d3a1cb5 100644 --- a/types/wlr_layer_shell_v1.c +++ b/types/wlr_layer_shell_v1.c @@ -3,9 +3,9 @@ #include #include #include +#include #include #include -#include #include #include #include "util/signal.h" @@ -103,7 +103,14 @@ static void layer_surface_handle_set_size(struct wl_client *client, if (!surface) { return; } - surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_DESIRED_SIZE; + + if (surface->current.desired_width == width + && surface->current.desired_height == height) { + surface->pending.committed &= ~WLR_LAYER_SURFACE_V1_STATE_DESIRED_SIZE; + } else { + surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_DESIRED_SIZE; + } + surface->pending.desired_width = width; surface->pending.desired_height = height; } @@ -125,7 +132,13 @@ static void layer_surface_handle_set_anchor(struct wl_client *client, if (!surface) { return; } - surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_ANCHOR; + + if (surface->current.anchor == anchor) { + surface->pending.committed &= ~WLR_LAYER_SURFACE_V1_STATE_ANCHOR; + } else { + surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_ANCHOR; + } + surface->pending.anchor = anchor; } @@ -136,7 +149,13 @@ static void layer_surface_handle_set_exclusive_zone(struct wl_client *client, if (!surface) { return; } - surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_EXCLUSIVE_ZONE; + + if (surface->current.exclusive_zone == zone) { + surface->pending.committed &= ~WLR_LAYER_SURFACE_V1_STATE_EXCLUSIVE_ZONE; + } else { + surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_EXCLUSIVE_ZONE; + } + surface->pending.exclusive_zone = zone; } @@ -148,7 +167,16 @@ static void layer_surface_handle_set_margin( if (!surface) { return; } - surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_MARGIN; + + if (surface->current.margin.top == top + && surface->current.margin.right == right + && surface->current.margin.bottom == bottom + && surface->current.margin.left == left) { + surface->pending.committed &= ~WLR_LAYER_SURFACE_V1_STATE_MARGIN; + } else { + surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_MARGIN; + } + surface->pending.margin.top = top; surface->pending.margin.right = right; surface->pending.margin.bottom = bottom; @@ -183,14 +211,12 @@ static void layer_surface_handle_get_popup(struct wl_client *client, struct wl_resource *popup_resource) { struct wlr_layer_surface_v1 *parent = layer_surface_from_resource(layer_resource); - struct wlr_xdg_surface *popup_surface = - wlr_xdg_surface_from_popup_resource(popup_resource); + struct wlr_xdg_popup *popup = + wlr_xdg_popup_from_resource(popup_resource); if (!parent) { return; } - assert(popup_surface->role == WLR_XDG_SURFACE_ROLE_POPUP); - struct wlr_xdg_popup *popup = popup_surface->popup; popup->parent = parent->surface; wl_list_insert(&parent->popups, &popup->link); wlr_signal_emit_safe(&parent->events.new_popup, popup); @@ -209,7 +235,13 @@ static void layer_surface_set_layer(struct wl_client *client, "Invalid layer %" PRIu32, layer); return; } - surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_LAYER; + + if (surface->current.layer == layer) { + surface->pending.committed &= ~WLR_LAYER_SURFACE_V1_STATE_LAYER; + } else { + surface->pending.committed |= WLR_LAYER_SURFACE_V1_STATE_LAYER; + } + surface->pending.layer = layer; } @@ -231,7 +263,7 @@ static void layer_surface_unmap(struct wlr_layer_surface_v1 *surface) { struct wlr_xdg_popup *popup, *popup_tmp; wl_list_for_each_safe(popup, popup_tmp, &surface->popups, link) { - wlr_xdg_popup_destroy(popup->base); + wlr_xdg_popup_destroy(popup); } struct wlr_layer_surface_v1_configure *configure, *tmp; @@ -341,15 +373,15 @@ static void layer_surface_role_commit(struct wlr_surface *wlr_surface) { } } -static void layer_surface_role_precommit(struct wlr_surface *wlr_surface) { +static void layer_surface_role_precommit(struct wlr_surface *wlr_surface, + const struct wlr_surface_state *state) { struct wlr_layer_surface_v1 *surface = wlr_layer_surface_v1_from_wlr_surface(wlr_surface); if (surface == NULL) { return; } - if (wlr_surface->pending.committed & WLR_SURFACE_STATE_BUFFER && - wlr_surface->pending.buffer == NULL) { + if (state->committed & WLR_SURFACE_STATE_BUFFER && state->buffer == NULL) { // This is a NULL commit if (surface->configured && surface->mapped) { layer_surface_unmap(surface); diff --git a/types/wlr_linux_dmabuf_v1.c b/types/wlr_linux_dmabuf_v1.c index 7b08b350c..5138e469f 100644 --- a/types/wlr_linux_dmabuf_v1.c +++ b/types/wlr_linux_dmabuf_v1.c @@ -2,16 +2,62 @@ #include #include #include +#include #include -#include -#include #include +#include #include #include #include "linux-dmabuf-unstable-v1-protocol.h" +#include "render/drm_format_set.h" #include "util/signal.h" +#include "util/shm.h" -#define LINUX_DMABUF_VERSION 3 +#define LINUX_DMABUF_VERSION 4 + +struct wlr_linux_buffer_params_v1 { + struct wl_resource *resource; + struct wlr_linux_dmabuf_v1 *linux_dmabuf; + struct wlr_dmabuf_attributes attributes; + bool has_modifier; +}; + +struct wlr_linux_dmabuf_feedback_v1_compiled_tranche { + dev_t target_device; + uint32_t flags; // bitfield of enum zwp_linux_dmabuf_feedback_v1_tranche_flags + struct wl_array indices; // uint16_t +}; + +struct wlr_linux_dmabuf_feedback_v1_compiled { + dev_t main_device; + int table_fd; + size_t table_size; + + size_t tranches_len; + struct wlr_linux_dmabuf_feedback_v1_compiled_tranche tranches[]; +}; + +struct wlr_linux_dmabuf_feedback_v1_table_entry { + uint32_t format; + uint32_t pad; // unused + uint64_t modifier; +}; + +// TODO: switch back to static_assert once this fix propagates in stable trees: +// https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=255290 +_Static_assert(sizeof(struct wlr_linux_dmabuf_feedback_v1_table_entry) == 16, + "Expected wlr_linux_dmabuf_feedback_v1_table_entry to be tightly packed"); + +struct wlr_linux_dmabuf_v1_surface { + struct wlr_surface *surface; + struct wlr_linux_dmabuf_v1 *linux_dmabuf; + struct wl_list link; // wlr_linux_dmabuf_v1.surfaces + + struct wlr_addon addon; + struct wlr_linux_dmabuf_feedback_v1_compiled *feedback; + + struct wl_list feedback_resources; // wl_resource_get_link +}; static void buffer_handle_destroy(struct wl_client *client, struct wl_resource *resource) { @@ -204,10 +250,25 @@ static void params_create_common(struct wl_resource *params_resource, goto err_out; } + /* reject unknown flags */ + uint32_t all_flags = ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT | + ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_INTERLACED | + ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_BOTTOM_FIRST; + if (flags & ~all_flags) { + wl_resource_post_error(params_resource, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT, + "Unknown dmabuf flags %"PRIu32, flags); + goto err_out; + } + + if (flags != 0) { + wlr_log(WLR_ERROR, "dmabuf flags aren't supported"); + goto err_failed; + } + attribs.width = width; attribs.height = height; attribs.format = format; - attribs.flags = flags; if (width < 1 || height < 1) { wl_resource_post_error(params_resource, @@ -265,14 +326,6 @@ static void params_create_common(struct wl_resource *params_resource, } } - /* reject unknown flags */ - if (attribs.flags & ~ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT) { - wl_resource_post_error(params_resource, - ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT, - "Unknown dmabuf flags %"PRIu32, attribs.flags); - goto err_out; - } - /* Check if dmabuf is usable */ if (!check_import_dmabuf(linux_dmabuf, &attribs)) { goto err_failed; @@ -400,6 +453,392 @@ static void linux_dmabuf_create_params(struct wl_client *client, &buffer_params_impl, params, params_handle_resource_destroy); } +static void linux_dmabuf_feedback_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static const struct zwp_linux_dmabuf_feedback_v1_interface + linux_dmabuf_feedback_impl = { + .destroy = linux_dmabuf_feedback_destroy, +}; + +static ssize_t get_drm_format_set_index(const struct wlr_drm_format_set *set, + uint32_t format, uint64_t modifier) { + bool format_found = false; + const struct wlr_drm_format *fmt; + size_t idx = 0; + for (size_t i = 0; i < set->len; i++) { + fmt = set->formats[i]; + if (fmt->format == format) { + format_found = true; + break; + } + idx += 1 + fmt->len; + } + if (!format_found) { + return -1; + } + + if (modifier == DRM_FORMAT_MOD_INVALID) { + return idx; + } + + for (size_t i = 0; i < fmt->len; i++) { + if (fmt->modifiers[i] == modifier) { + return idx; + } + idx++; + } + return -1; +} + +static struct wlr_linux_dmabuf_feedback_v1_compiled *feedback_compile( + const struct wlr_linux_dmabuf_feedback_v1 *feedback) { + assert(feedback->tranches_len > 0); + + // Require the last tranche to be the fallback tranche and contain all + // formats/modifiers + const struct wlr_linux_dmabuf_feedback_v1_tranche *fallback_tranche = + &feedback->tranches[feedback->tranches_len - 1]; + + size_t table_len = 0; + for (size_t i = 0; i < fallback_tranche->formats->len; i++) { + const struct wlr_drm_format *fmt = fallback_tranche->formats->formats[i]; + table_len += 1 + fmt->len; + } + assert(table_len > 0); + + size_t table_size = + table_len * sizeof(struct wlr_linux_dmabuf_feedback_v1_table_entry); + int rw_fd, ro_fd; + if (!allocate_shm_file_pair(table_size, &rw_fd, &ro_fd)) { + wlr_log(WLR_ERROR, "Failed to allocate shm file for format table"); + return NULL; + } + + struct wlr_linux_dmabuf_feedback_v1_table_entry *table = + mmap(NULL, table_size, PROT_READ | PROT_WRITE, MAP_SHARED, rw_fd, 0); + if (table == MAP_FAILED) { + wlr_log_errno(WLR_ERROR, "mmap failed"); + close(rw_fd); + close(ro_fd); + return NULL; + } + + close(rw_fd); + + size_t n = 0; + for (size_t i = 0; i < fallback_tranche->formats->len; i++) { + const struct wlr_drm_format *fmt = fallback_tranche->formats->formats[i]; + + table[n] = (struct wlr_linux_dmabuf_feedback_v1_table_entry){ + .format = fmt->format, + .modifier = DRM_FORMAT_MOD_INVALID, + }; + n++; + + for (size_t k = 0; k < fmt->len; k++) { + table[n] = (struct wlr_linux_dmabuf_feedback_v1_table_entry){ + .format = fmt->format, + .modifier = fmt->modifiers[k], + }; + n++; + } + } + assert(n == table_len); + + munmap(table, table_size); + + struct wlr_linux_dmabuf_feedback_v1_compiled *compiled = calloc(1, + sizeof(struct wlr_linux_dmabuf_feedback_v1_compiled) + + feedback->tranches_len * sizeof(struct wlr_linux_dmabuf_feedback_v1_compiled_tranche)); + if (compiled == NULL) { + close(ro_fd); + return NULL; + } + + compiled->main_device = feedback->main_device; + compiled->tranches_len = feedback->tranches_len; + compiled->table_fd = ro_fd; + compiled->table_size = table_size; + + // Build the indices lists for all but the last (fallback) tranches + for (size_t i = 0; i < feedback->tranches_len - 1; i++) { + const struct wlr_linux_dmabuf_feedback_v1_tranche *tranche = + &feedback->tranches[i]; + struct wlr_linux_dmabuf_feedback_v1_compiled_tranche *compiled_tranche = + &compiled->tranches[i]; + + compiled_tranche->target_device = tranche->target_device; + compiled_tranche->flags = tranche->flags; + + wl_array_init(&compiled_tranche->indices); + if (!wl_array_add(&compiled_tranche->indices, table_len * sizeof(uint16_t))) { + wlr_log(WLR_ERROR, "Failed to allocate tranche indices array"); + goto error_compiled; + } + + n = 0; + uint16_t *indices = compiled_tranche->indices.data; + for (size_t j = 0; j < tranche->formats->len; j++) { + const struct wlr_drm_format *fmt = tranche->formats->formats[j]; + + ssize_t index = get_drm_format_set_index( + fallback_tranche->formats, fmt->format, + DRM_FORMAT_MOD_INVALID); + if (index < 0) { + wlr_log(WLR_ERROR, "Format 0x%" PRIX32 " and modifier " + "INVALID are in tranche #%zu but are missing " + "from the fallback tranche", + fmt->format, i); + goto error_compiled; + } + indices[n] = index; + n++; + + for (size_t k = 0; k < fmt->len; k++) { + ssize_t index = get_drm_format_set_index( + fallback_tranche->formats, fmt->format, fmt->modifiers[k]); + if (index < 0) { + wlr_log(WLR_ERROR, "Format 0x%" PRIX32 " and modifier " + "0x%" PRIX64 " are in tranche #%zu but are missing " + "from the fallback tranche", + fmt->format, fmt->modifiers[k], i); + goto error_compiled; + } + indices[n] = index; + n++; + } + } + compiled_tranche->indices.size = n * sizeof(uint16_t); + } + + struct wlr_linux_dmabuf_feedback_v1_compiled_tranche *fallback_compiled_tranche = + &compiled->tranches[compiled->tranches_len - 1]; + fallback_compiled_tranche->target_device = fallback_tranche->target_device; + fallback_compiled_tranche->flags = fallback_tranche->flags; + + // Build the indices list for the last (fallback) tranche + wl_array_init(&fallback_compiled_tranche->indices); + if (!wl_array_add(&fallback_compiled_tranche->indices, + table_len * sizeof(uint16_t))) { + wlr_log(WLR_ERROR, "Failed to allocate fallback tranche indices array"); + goto error_compiled; + } + + n = 0; + uint16_t *index_ptr; + wl_array_for_each(index_ptr, &fallback_compiled_tranche->indices) { + *index_ptr = n; + n++; + } + + return compiled; + +error_compiled: + close(compiled->table_fd); + free(compiled); + return NULL; +} + +static void compiled_feedback_destroy( + struct wlr_linux_dmabuf_feedback_v1_compiled *feedback) { + if (feedback == NULL) { + return; + } + for (size_t i = 0; i < feedback->tranches_len; i++) { + wl_array_release(&feedback->tranches[i].indices); + } + close(feedback->table_fd); + free(feedback); +} + +static bool feedback_tranche_init_with_renderer( + struct wlr_linux_dmabuf_feedback_v1_tranche *tranche, + struct wlr_renderer *renderer) { + memset(tranche, 0, sizeof(*tranche)); + + int drm_fd = wlr_renderer_get_drm_fd(renderer); + if (drm_fd < 0) { + wlr_log(WLR_ERROR, "Failed to get DRM FD from renderer"); + return false; + } + + struct stat stat; + if (fstat(drm_fd, &stat) != 0) { + wlr_log_errno(WLR_ERROR, "fstat failed"); + return false; + } + tranche->target_device = stat.st_rdev; + + tranche->formats = wlr_renderer_get_dmabuf_texture_formats(renderer); + if (tranche->formats == NULL) { + wlr_log(WLR_ERROR, "Failed to get renderer DMA-BUF texture formats"); + return false; + } + + return true; +} + +static struct wlr_linux_dmabuf_feedback_v1_compiled *compile_default_feedback( + struct wlr_renderer *renderer) { + struct wlr_linux_dmabuf_feedback_v1_tranche tranche = {0}; + if (!feedback_tranche_init_with_renderer(&tranche, renderer)) { + return NULL; + } + + const struct wlr_linux_dmabuf_feedback_v1 feedback = { + .main_device = tranche.target_device, + .tranches = &tranche, + .tranches_len = 1, + }; + + return feedback_compile(&feedback); +} + +static void feedback_tranche_send( + const struct wlr_linux_dmabuf_feedback_v1_compiled_tranche *tranche, + struct wl_resource *resource) { + struct wl_array dev_array = { + .size = sizeof(tranche->target_device), + .data = (void *)&tranche->target_device, + }; + zwp_linux_dmabuf_feedback_v1_send_tranche_target_device(resource, &dev_array); + zwp_linux_dmabuf_feedback_v1_send_tranche_flags(resource, tranche->flags); + zwp_linux_dmabuf_feedback_v1_send_tranche_formats(resource, + (struct wl_array *)&tranche->indices); + zwp_linux_dmabuf_feedback_v1_send_tranche_done(resource); +} + +static void feedback_send(const struct wlr_linux_dmabuf_feedback_v1_compiled *feedback, + struct wl_resource *resource) { + struct wl_array dev_array = { + .size = sizeof(feedback->main_device), + .data = (void *)&feedback->main_device, + }; + zwp_linux_dmabuf_feedback_v1_send_main_device(resource, &dev_array); + + zwp_linux_dmabuf_feedback_v1_send_format_table(resource, + feedback->table_fd, feedback->table_size); + + for (size_t i = 0; i < feedback->tranches_len; i++) { + feedback_tranche_send(&feedback->tranches[i], resource); + } + + zwp_linux_dmabuf_feedback_v1_send_done(resource); +} + +static void linux_dmabuf_get_default_feedback(struct wl_client *client, + struct wl_resource *resource, uint32_t id) { + struct wlr_linux_dmabuf_v1 *linux_dmabuf = + linux_dmabuf_from_resource(resource); + + uint32_t version = wl_resource_get_version(resource); + struct wl_resource *feedback_resource = wl_resource_create(client, + &zwp_linux_dmabuf_feedback_v1_interface, version, id); + if (feedback_resource == NULL) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(feedback_resource, &linux_dmabuf_feedback_impl, + NULL, NULL); + + feedback_send(linux_dmabuf->default_feedback, feedback_resource); +} + +static void surface_destroy(struct wlr_linux_dmabuf_v1_surface *surface) { + struct wl_resource *resource, *resource_tmp; + wl_resource_for_each_safe(resource, resource_tmp, &surface->feedback_resources) { + struct wl_list *link = wl_resource_get_link(resource); + wl_list_remove(link); + wl_list_init(link); + } + + compiled_feedback_destroy(surface->feedback); + + wlr_addon_finish(&surface->addon); + wl_list_remove(&surface->link); + free(surface); +} + +static void surface_addon_destroy(struct wlr_addon *addon) { + struct wlr_linux_dmabuf_v1_surface *surface = + wl_container_of(addon, surface, addon); + surface_destroy(surface); +} + +static const struct wlr_addon_interface surface_addon_impl = { + .name = "wlr_linux_dmabuf_v1_surface", + .destroy = surface_addon_destroy, +}; + +static struct wlr_linux_dmabuf_v1_surface *surface_get_or_create( + struct wlr_linux_dmabuf_v1 *linux_dmabuf, + struct wlr_surface *wlr_surface) { + struct wlr_addon *addon = + wlr_addon_find(&wlr_surface->addons, linux_dmabuf, &surface_addon_impl); + if (addon != NULL) { + struct wlr_linux_dmabuf_v1_surface *surface = + wl_container_of(addon, surface, addon); + return surface; + } + + struct wlr_linux_dmabuf_v1_surface *surface = calloc(1, sizeof(*surface)); + if (surface == NULL) { + return NULL; + } + + surface->surface = wlr_surface; + surface->linux_dmabuf = linux_dmabuf; + wl_list_init(&surface->feedback_resources); + wlr_addon_init(&surface->addon, &wlr_surface->addons, linux_dmabuf, + &surface_addon_impl); + wl_list_insert(&linux_dmabuf->surfaces, &surface->link); + + return surface; +} + +static const struct wlr_linux_dmabuf_feedback_v1_compiled *surface_get_feedback( + struct wlr_linux_dmabuf_v1_surface *surface) { + if (surface->feedback != NULL) { + return surface->feedback; + } + return surface->linux_dmabuf->default_feedback; +} + +static void surface_feedback_handle_resource_destroy(struct wl_resource *resource) { + wl_list_remove(wl_resource_get_link(resource)); +} + +static void linux_dmabuf_get_surface_feedback(struct wl_client *client, + struct wl_resource *resource, uint32_t id, + struct wl_resource *surface_resource) { + struct wlr_linux_dmabuf_v1 *linux_dmabuf = + linux_dmabuf_from_resource(resource); + struct wlr_surface *wlr_surface = wlr_surface_from_resource(surface_resource); + + struct wlr_linux_dmabuf_v1_surface *surface = + surface_get_or_create(linux_dmabuf, wlr_surface); + if (surface == NULL) { + wl_client_post_no_memory(client); + return; + } + + uint32_t version = wl_resource_get_version(resource); + struct wl_resource *feedback_resource = wl_resource_create(client, + &zwp_linux_dmabuf_feedback_v1_interface, version, id); + if (feedback_resource == NULL) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(feedback_resource, &linux_dmabuf_feedback_impl, + NULL, surface_feedback_handle_resource_destroy); + wl_list_insert(&surface->feedback_resources, wl_resource_get_link(feedback_resource)); + + feedback_send(surface_get_feedback(surface), feedback_resource); +} + static void linux_dmabuf_destroy(struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy(resource); @@ -408,12 +847,26 @@ static void linux_dmabuf_destroy(struct wl_client *client, static const struct zwp_linux_dmabuf_v1_interface linux_dmabuf_impl = { .destroy = linux_dmabuf_destroy, .create_params = linux_dmabuf_create_params, + .get_default_feedback = linux_dmabuf_get_default_feedback, + .get_surface_feedback = linux_dmabuf_get_surface_feedback, }; static void linux_dmabuf_send_modifiers(struct wl_resource *resource, const struct wlr_drm_format *fmt) { if (wl_resource_get_version(resource) < ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) { - zwp_linux_dmabuf_v1_send_format(resource, fmt->format); + if (wlr_drm_format_has(fmt, DRM_FORMAT_MOD_INVALID)) { + zwp_linux_dmabuf_v1_send_format(resource, fmt->format); + } + return; + } + + // In case only INVALID and LINEAR are advertised, send INVALID only due to XWayland: + // https://gitlab.freedesktop.org/xorg/xserver/-/issues/1166 + if (fmt->len == 2 && wlr_drm_format_has(fmt, DRM_FORMAT_MOD_INVALID) + && wlr_drm_format_has(fmt, DRM_FORMAT_MOD_LINEAR)) { + uint64_t mod = DRM_FORMAT_MOD_INVALID; + zwp_linux_dmabuf_v1_send_modifier(resource, fmt->format, + mod >> 32, mod & 0xFFFFFFFF); return; } @@ -422,10 +875,6 @@ static void linux_dmabuf_send_modifiers(struct wl_resource *resource, zwp_linux_dmabuf_v1_send_modifier(resource, fmt->format, mod >> 32, mod & 0xFFFFFFFF); } - - // We always support buffers with an implicit modifier - zwp_linux_dmabuf_v1_send_modifier(resource, fmt->format, - DRM_FORMAT_MOD_INVALID >> 32, DRM_FORMAT_MOD_INVALID & 0xFFFFFFFF); } static void linux_dmabuf_send_formats(struct wlr_linux_dmabuf_v1 *linux_dmabuf, @@ -454,12 +903,22 @@ static void linux_dmabuf_bind(struct wl_client *client, void *data, } wl_resource_set_implementation(resource, &linux_dmabuf_impl, linux_dmabuf, NULL); - linux_dmabuf_send_formats(linux_dmabuf, resource); + + if (version < ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION) { + linux_dmabuf_send_formats(linux_dmabuf, resource); + } } static void linux_dmabuf_v1_destroy(struct wlr_linux_dmabuf_v1 *linux_dmabuf) { wlr_signal_emit_safe(&linux_dmabuf->events.destroy, linux_dmabuf); + struct wlr_linux_dmabuf_v1_surface *surface, *surface_tmp; + wl_list_for_each_safe(surface, surface_tmp, &linux_dmabuf->surfaces, link) { + surface_destroy(surface); + } + + compiled_feedback_destroy(linux_dmabuf->default_feedback); + wl_list_remove(&linux_dmabuf->display_destroy.link); wl_list_remove(&linux_dmabuf->renderer_destroy.link); @@ -489,6 +948,7 @@ struct wlr_linux_dmabuf_v1 *wlr_linux_dmabuf_v1_create(struct wl_display *displa } linux_dmabuf->renderer = renderer; + wl_list_init(&linux_dmabuf->surfaces); wl_signal_init(&linux_dmabuf->events.destroy); linux_dmabuf->global = @@ -500,6 +960,14 @@ struct wlr_linux_dmabuf_v1 *wlr_linux_dmabuf_v1_create(struct wl_display *displa return NULL; } + linux_dmabuf->default_feedback = compile_default_feedback(renderer); + if (linux_dmabuf->default_feedback == NULL) { + wlr_log(WLR_ERROR, "Failed to init default linux-dmabuf feedback"); + wl_global_destroy(linux_dmabuf->global); + free(linux_dmabuf); + return NULL; + } + linux_dmabuf->display_destroy.notify = handle_display_destroy; wl_display_add_destroy_listener(display, &linux_dmabuf->display_destroy); @@ -508,3 +976,32 @@ struct wlr_linux_dmabuf_v1 *wlr_linux_dmabuf_v1_create(struct wl_display *displa return linux_dmabuf; } + +bool wlr_linux_dmabuf_v1_set_surface_feedback( + struct wlr_linux_dmabuf_v1 *linux_dmabuf, + struct wlr_surface *wlr_surface, + const struct wlr_linux_dmabuf_feedback_v1 *feedback) { + struct wlr_linux_dmabuf_v1_surface *surface = + surface_get_or_create(linux_dmabuf, wlr_surface); + if (surface == NULL) { + return false; + } + + struct wlr_linux_dmabuf_feedback_v1_compiled *compiled = NULL; + if (feedback != NULL) { + compiled = feedback_compile(feedback); + if (compiled == NULL) { + return false; + } + } + + compiled_feedback_destroy(surface->feedback); + surface->feedback = compiled; + + struct wl_resource *resource; + wl_resource_for_each(resource, &surface->feedback_resources) { + feedback_send(surface_get_feedback(surface), resource); + } + + return true; +} diff --git a/types/wlr_output_layout.c b/types/wlr_output_layout.c index 782f5b71c..28d91e8d4 100644 --- a/types/wlr_output_layout.c +++ b/types/wlr_output_layout.c @@ -8,15 +8,10 @@ #include #include "util/signal.h" -struct wlr_output_layout_state { - struct wlr_box _box; // should never be read directly, use the getter -}; - struct wlr_output_layout_output_state { struct wlr_output_layout *layout; struct wlr_output_layout_output *l_output; - struct wlr_box _box; // should never be read directly, use the getter bool auto_configured; struct wl_listener mode; @@ -31,11 +26,6 @@ struct wlr_output_layout *wlr_output_layout_create(void) { if (layout == NULL) { return NULL; } - layout->state = calloc(1, sizeof(struct wlr_output_layout_state)); - if (layout->state == NULL) { - free(layout); - return NULL; - } wl_list_init(&layout->outputs); wl_signal_init(&layout->events.add); @@ -69,19 +59,16 @@ void wlr_output_layout_destroy(struct wlr_output_layout *layout) { output_layout_output_destroy(l_output); } - free(layout->state); free(layout); } -static struct wlr_box *output_layout_output_get_box( - struct wlr_output_layout_output *l_output) { - l_output->state->_box.x = l_output->x; - l_output->state->_box.y = l_output->y; - int width, height; - wlr_output_effective_resolution(l_output->output, &width, &height); - l_output->state->_box.width = width; - l_output->state->_box.height = height; - return &l_output->state->_box; +static void output_layout_output_get_box( + struct wlr_output_layout_output *l_output, + struct wlr_box *box) { + box->x = l_output->x; + box->y = l_output->y; + wlr_output_effective_resolution(l_output->output, + &box->width, &box->height); } /** @@ -98,15 +85,17 @@ static void output_layout_reconfigure(struct wlr_output_layout *layout) { // find the rightmost x coordinate occupied by a manually configured output // in the layout struct wlr_output_layout_output *l_output; + struct wlr_box output_box; + wl_list_for_each(l_output, &layout->outputs, link) { if (l_output->state->auto_configured) { continue; } - struct wlr_box *box = output_layout_output_get_box(l_output); - if (box->x + box->width > max_x) { - max_x = box->x + box->width; - max_x_y = box->y; + output_layout_output_get_box(l_output, &output_box); + if (output_box.x + output_box.width > max_x) { + max_x = output_box.x + output_box.width; + max_x_y = output_box.y; } } @@ -120,10 +109,10 @@ static void output_layout_reconfigure(struct wlr_output_layout *layout) { if (!l_output->state->auto_configured) { continue; } - struct wlr_box *box = output_layout_output_get_box(l_output); + output_layout_output_get_box(l_output, &output_box); l_output->x = max_x; l_output->y = max_x_y; - max_x += box->width; + max_x += output_box.width; } wlr_signal_emit_safe(&layout->events.change, layout); @@ -185,7 +174,12 @@ static struct wlr_output_layout_output *output_layout_output_create( l_output->state->layout = layout; l_output->output = output; wl_signal_init(&l_output->events.destroy); - wl_list_insert(&layout->outputs, &l_output->link); + + /* + * Insert at the end of the list so that auto-configuring the + * new output doesn't change the layout of other outputs + */ + wl_list_insert(layout->outputs.prev, &l_output->link); wl_signal_add(&output->events.mode, &l_output->state->mode); l_output->state->mode.notify = handle_output_mode; @@ -237,8 +231,9 @@ bool wlr_output_layout_contains_point(struct wlr_output_layout *layout, if (reference) { struct wlr_output_layout_output *l_output = wlr_output_layout_get(layout, reference); - struct wlr_box *box = output_layout_output_get_box(l_output); - return wlr_box_contains_point(box, lx, ly); + struct wlr_box output_box; + output_layout_output_get_box(l_output, &output_box); + return wlr_box_contains_point(&output_box, lx, ly); } else { return !!wlr_output_layout_output_at(layout, lx, ly); } @@ -251,9 +246,9 @@ bool wlr_output_layout_intersects(struct wlr_output_layout *layout, if (reference == NULL) { struct wlr_output_layout_output *l_output; wl_list_for_each(l_output, &layout->outputs, link) { - struct wlr_box *output_box = - output_layout_output_get_box(l_output); - if (wlr_box_intersection(&out_box, output_box, target_lbox)) { + struct wlr_box output_box; + output_layout_output_get_box(l_output, &output_box); + if (wlr_box_intersection(&out_box, &output_box, target_lbox)) { return true; } } @@ -265,8 +260,9 @@ bool wlr_output_layout_intersects(struct wlr_output_layout *layout, return false; } - struct wlr_box *output_box = output_layout_output_get_box(l_output); - return wlr_box_intersection(&out_box, output_box, target_lbox); + struct wlr_box output_box; + output_layout_output_get_box(l_output, &output_box); + return wlr_box_intersection(&out_box, &output_box, target_lbox); } } @@ -274,8 +270,9 @@ struct wlr_output *wlr_output_layout_output_at(struct wlr_output_layout *layout, double lx, double ly) { struct wlr_output_layout_output *l_output; wl_list_for_each(l_output, &layout->outputs, link) { - struct wlr_box *box = output_layout_output_get_box(l_output); - if (wlr_box_contains_point(box, lx, ly)) { + struct wlr_box output_box; + output_layout_output_get_box(l_output, &output_box); + if (wlr_box_contains_point(&output_box, lx, ly)) { return l_output->output; } } @@ -337,8 +334,9 @@ void wlr_output_layout_closest_point(struct wlr_output_layout *layout, } double output_x, output_y, output_distance; - struct wlr_box *box = output_layout_output_get_box(l_output); - wlr_box_closest_point(box, lx, ly, &output_x, &output_y); + struct wlr_box output_box; + output_layout_output_get_box(l_output, &output_box); + wlr_box_closest_point(&output_box, lx, ly, &output_x, &output_y); // calculate squared distance suitable for comparison output_distance = @@ -363,17 +361,17 @@ void wlr_output_layout_closest_point(struct wlr_output_layout *layout, } } -struct wlr_box *wlr_output_layout_get_box( - struct wlr_output_layout *layout, struct wlr_output *reference) { +void wlr_output_layout_get_box(struct wlr_output_layout *layout, + struct wlr_output *reference, struct wlr_box *dest_box) { struct wlr_output_layout_output *l_output; if (reference) { // output extents l_output = wlr_output_layout_get(layout, reference); if (l_output) { - return output_layout_output_get_box(l_output); + output_layout_output_get_box(l_output, dest_box); } else { - return NULL; + dest_box->width = dest_box->height = 0; } } else { // layout extents @@ -382,31 +380,28 @@ struct wlr_box *wlr_output_layout_get_box( min_x = min_y = INT_MAX; max_x = max_y = INT_MIN; wl_list_for_each(l_output, &layout->outputs, link) { - struct wlr_box *box = output_layout_output_get_box(l_output); - if (box->x < min_x) { - min_x = box->x; + struct wlr_box output_box; + output_layout_output_get_box(l_output, &output_box); + if (output_box.x < min_x) { + min_x = output_box.x; } - if (box->y < min_y) { - min_y = box->y; + if (output_box.y < min_y) { + min_y = output_box.y; } - if (box->x + box->width > max_x) { - max_x = box->x + box->width; + if (output_box.x + output_box.width > max_x) { + max_x = output_box.x + output_box.width; } - if (box->y + box->height > max_y) { - max_y = box->y + box->height; + if (output_box.y + output_box.height > max_y) { + max_y = output_box.y + output_box.height; } } } - layout->state->_box.x = min_x; - layout->state->_box.y = min_y; - layout->state->_box.width = max_x - min_x; - layout->state->_box.height = max_y - min_y; - - return &layout->state->_box; + dest_box->x = min_x; + dest_box->y = min_y; + dest_box->width = max_x - min_x; + dest_box->height = max_y - min_y; } - - // not reached } void wlr_output_layout_add_auto(struct wlr_output_layout *layout, @@ -437,9 +432,10 @@ struct wlr_output *wlr_output_layout_get_center_output( return NULL; } - struct wlr_box *extents = wlr_output_layout_get_box(layout, NULL); - double center_x = extents->width / 2. + extents->x; - double center_y = extents->height / 2. + extents->y; + struct wlr_box extents; + wlr_output_layout_get_box(layout, NULL, &extents); + double center_x = extents.width / 2. + extents.x; + double center_y = extents.height / 2. + extents.y; double dest_x = 0, dest_y = 0; wlr_output_layout_closest_point(layout, NULL, center_x, center_y, @@ -459,7 +455,8 @@ static struct wlr_output *wlr_output_layout_output_in_direction( enum distance_selection_method distance_method) { assert(reference); - struct wlr_box *ref_box = wlr_output_layout_get_box(layout, reference); + struct wlr_box ref_box; + wlr_output_layout_get_box(layout, reference, &ref_box); double min_distance = (distance_method == NEAREST) ? DBL_MAX : DBL_MIN; struct wlr_output *closest_output = NULL; @@ -468,21 +465,23 @@ static struct wlr_output *wlr_output_layout_output_in_direction( if (reference != NULL && reference == l_output->output) { continue; } - struct wlr_box *box = output_layout_output_get_box(l_output); + + struct wlr_box box; + output_layout_output_get_box(l_output, &box); bool match = false; // test to make sure this output is in the given direction if (direction & WLR_DIRECTION_LEFT) { - match = box->x + box->width <= ref_box->x || match; + match = box.x + box.width <= ref_box.x || match; } if (direction & WLR_DIRECTION_RIGHT) { - match = box->x >= ref_box->x + ref_box->width || match; + match = box.x >= ref_box.x + ref_box.width || match; } if (direction & WLR_DIRECTION_UP) { - match = box->y + box->height <= ref_box->y || match; + match = box.y + box.height <= ref_box.y || match; } if (direction & WLR_DIRECTION_DOWN) { - match = box->y >= ref_box->y + ref_box->height || match; + match = box.y >= ref_box.y + ref_box.height || match; } if (!match) { continue; diff --git a/types/wlr_pointer.c b/types/wlr_pointer.c index a6590e73d..caabec1b3 100644 --- a/types/wlr_pointer.c +++ b/types/wlr_pointer.c @@ -5,7 +5,10 @@ #include void wlr_pointer_init(struct wlr_pointer *pointer, - const struct wlr_pointer_impl *impl) { + const struct wlr_pointer_impl *impl, const char *name) { + wlr_input_device_init(&pointer->base, WLR_INPUT_DEVICE_POINTER, name); + pointer->base.pointer = pointer; + pointer->impl = impl; wl_signal_init(&pointer->events.motion); wl_signal_init(&pointer->events.motion_absolute); @@ -26,6 +29,7 @@ void wlr_pointer_destroy(struct wlr_pointer *pointer) { if (!pointer) { return; } + wlr_input_device_finish(&pointer->base); if (pointer->impl && pointer->impl->destroy) { pointer->impl->destroy(pointer); } else { diff --git a/types/wlr_pointer_constraints_v1.c b/types/wlr_pointer_constraints_v1.c index d608b778a..e90daf72e 100644 --- a/types/wlr_pointer_constraints_v1.c +++ b/types/wlr_pointer_constraints_v1.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include diff --git a/types/wlr_pointer_gestures_v1.c b/types/wlr_pointer_gestures_v1.c index 6b09a764f..b8229dfc4 100644 --- a/types/wlr_pointer_gestures_v1.c +++ b/types/wlr_pointer_gestures_v1.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include diff --git a/types/wlr_presentation_time.c b/types/wlr_presentation_time.c index 36b7fe95f..a6facbdbe 100644 --- a/types/wlr_presentation_time.c +++ b/types/wlr_presentation_time.c @@ -3,8 +3,8 @@ #include #include #include +#include #include -#include #include #include "presentation-time-protocol.h" #include "util/signal.h" diff --git a/types/wlr_screencopy_v1.c b/types/wlr_screencopy_v1.c index 7c7d7e95e..e2c020db5 100644 --- a/types/wlr_screencopy_v1.c +++ b/types/wlr_screencopy_v1.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -189,10 +190,10 @@ static void frame_send_ready(struct wlr_screencopy_frame_v1 *frame, } static bool frame_shm_copy(struct wlr_screencopy_frame_v1 *frame, - uint32_t *flags) { + struct wlr_buffer *src_buffer, uint32_t *flags) { struct wl_shm_buffer *shm_buffer = frame->shm_buffer; struct wlr_output *output = frame->output; - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; assert(renderer); int x = frame->box.x; @@ -208,7 +209,7 @@ static bool frame_shm_copy(struct wlr_screencopy_frame_v1 *frame, void *data = wl_shm_buffer_get_data(shm_buffer); uint32_t renderer_flags = 0; bool ok; - ok = wlr_renderer_begin_with_buffer(renderer, output->front_buffer); + ok = wlr_renderer_begin_with_buffer(renderer, src_buffer); ok = ok && wlr_renderer_read_pixels(renderer, drm_format, &renderer_flags, stride, width, height, x, y, 0, 0, data); wlr_renderer_end(renderer); @@ -255,24 +256,20 @@ error_src_tex: } static bool frame_dma_copy(struct wlr_screencopy_frame_v1 *frame, - uint32_t *flags) { - struct wlr_dmabuf_v1_buffer *dma_buffer = frame->dma_buffer; + struct wlr_buffer *src_buffer) { + struct wlr_dmabuf_v1_buffer *dst_buffer = frame->dma_buffer; struct wlr_output *output = frame->output; - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; assert(renderer); // 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) { + src_buffer->width != frame->box.width || + src_buffer->height != frame->box.height) { return false; } - bool ok = blit_dmabuf(renderer, dma_buffer, output->front_buffer); - *flags = dma_buffer->attributes.flags & WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT ? - ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT : 0; - - return ok; + return blit_dmabuf(renderer, dst_buffer, src_buffer); } static void frame_handle_output_commit(struct wl_listener *listener, @@ -281,8 +278,8 @@ static void frame_handle_output_commit(struct wl_listener *listener, 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); - uint32_t flags; + struct wlr_renderer *renderer = output->renderer; + struct wlr_buffer *buffer = event->buffer; assert(renderer); if (!(event->committed & WLR_OUTPUT_STATE_BUFFER)) { @@ -304,8 +301,10 @@ static void frame_handle_output_commit(struct wl_listener *listener, wl_list_remove(&frame->output_commit.link); wl_list_init(&frame->output_commit.link); + + uint32_t flags = 0; bool ok = frame->shm_buffer ? - frame_shm_copy(frame, &flags) : frame_dma_copy(frame, &flags); + frame_shm_copy(frame, buffer, &flags) : frame_dma_copy(frame, buffer); if (!ok) { zwlr_screencopy_frame_v1_send_failed(frame->resource); frame_destroy(frame); @@ -488,15 +487,6 @@ static struct wlr_screencopy_v1_client *client_from_resource( return wl_resource_get_user_data(resource); } -static uint32_t get_output_fourcc(struct wlr_output *output) { - struct wlr_dmabuf_attributes attr = {0}; - if (!output->front_buffer || - !wlr_buffer_get_dmabuf(output->front_buffer, &attr)) { - return DRM_FORMAT_INVALID; - } - return attr.format; -} - static void capture_output(struct wl_client *wl_client, struct wlr_screencopy_v1_client *client, uint32_t version, uint32_t id, int32_t overlay_cursor, struct wlr_output *output, @@ -543,7 +533,7 @@ static void capture_output(struct wl_client *wl_client, goto error; } - struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + struct wlr_renderer *renderer = output->renderer; assert(renderer); uint32_t drm_format = wlr_output_preferred_read_format(frame->output); @@ -554,7 +544,12 @@ static void capture_output(struct wl_client *wl_client, } frame->format = convert_drm_format_to_wl_shm(drm_format); - frame->fourcc = get_output_fourcc(output); + if (output->allocator && + (output->allocator->buffer_caps & WLR_BUFFER_CAP_DMABUF)) { + frame->fourcc = output->render_format; + } else { + frame->fourcc = DRM_FORMAT_INVALID; + } struct wlr_box buffer_box = {0}; if (box == NULL) { diff --git a/types/wlr_server_decoration.c b/types/wlr_server_decoration.c index 39ba624aa..a80793939 100644 --- a/types/wlr_server_decoration.c +++ b/types/wlr_server_decoration.c @@ -1,7 +1,7 @@ #include #include +#include #include -#include #include #include "server-decoration-protocol.h" #include "util/signal.h" diff --git a/types/wlr_session_lock_v1.c b/types/wlr_session_lock_v1.c new file mode 100644 index 000000000..ffba20614 --- /dev/null +++ b/types/wlr_session_lock_v1.c @@ -0,0 +1,457 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include +#include "util/signal.h" +#include "ext-session-lock-v1-protocol.h" + +#define SESSION_LOCK_VERSION 1 + +static void resource_handle_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static const struct ext_session_lock_manager_v1_interface lock_manager_implementation; +static const struct ext_session_lock_v1_interface lock_implementation; +static const struct ext_session_lock_surface_v1_interface lock_surface_implementation; + +static struct wlr_session_lock_manager_v1 *lock_manager_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &ext_session_lock_manager_v1_interface, &lock_manager_implementation)); + struct wlr_session_lock_manager_v1 *lock_manager = + wl_resource_get_user_data(resource); + assert(lock_manager != NULL); + return lock_manager; +} + +static struct wlr_session_lock_v1 *lock_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &ext_session_lock_v1_interface, &lock_implementation)); + return wl_resource_get_user_data(resource); +} + +static struct wlr_session_lock_surface_v1 *lock_surface_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &ext_session_lock_surface_v1_interface, &lock_surface_implementation)); + return wl_resource_get_user_data(resource); +} + +static const struct wlr_surface_role lock_surface_role; + +bool wlr_surface_is_session_lock_surface_v1(struct wlr_surface *surface) { + return surface->role == &lock_surface_role; +} + +struct wlr_session_lock_surface_v1 *wlr_session_lock_surface_v1_from_wlr_surface( + struct wlr_surface *surface) { + assert(wlr_surface_is_session_lock_surface_v1(surface)); + return (struct wlr_session_lock_surface_v1 *)surface->role_data; +} + +uint32_t wlr_session_lock_surface_v1_configure( + struct wlr_session_lock_surface_v1 *lock_surface, + uint32_t width, uint32_t height) { + struct wlr_session_lock_surface_v1_configure *configure = + calloc(1, sizeof(struct wlr_session_lock_surface_v1_configure)); + if (configure == NULL) { + wl_resource_post_no_memory(lock_surface->resource); + return lock_surface->pending.configure_serial; + } + + struct wl_display *display = + wl_client_get_display(wl_resource_get_client(lock_surface->resource)); + + configure->width = width; + configure->height = height; + configure->serial = wl_display_next_serial(display); + + wl_list_insert(lock_surface->configure_list.prev, &configure->link); + + ext_session_lock_surface_v1_send_configure(lock_surface->resource, + configure->serial, configure->width, configure->height); + + return configure->serial; +} + +static void lock_surface_handle_ack_configure(struct wl_client *client, + struct wl_resource *resource, uint32_t serial) { + struct wlr_session_lock_surface_v1 *lock_surface = + lock_surface_from_resource(resource); + if (lock_surface == NULL) { + return; + } + + // First find the ack'ed configure + bool found = false; + struct wlr_session_lock_surface_v1_configure *configure, *tmp; + wl_list_for_each(configure, &lock_surface->configure_list, link) { + if (configure->serial == serial) { + found = true; + break; + } + } + if (!found) { + wl_resource_post_error(resource, + EXT_SESSION_LOCK_SURFACE_V1_ERROR_INVALID_SERIAL, + "ack_configure serial %" PRIu32 + " does not match any configure serial", serial); + return; + } + // Then remove old configures from the list + wl_list_for_each_safe(configure, tmp, &lock_surface->configure_list, link) { + if (configure->serial == serial) { + break; + } + wl_list_remove(&configure->link); + free(configure); + } + + lock_surface->pending.configure_serial = configure->serial; + lock_surface->pending.width = configure->width; + lock_surface->pending.height = configure->height; + + lock_surface->configured = true; + + wl_list_remove(&configure->link); + free(configure); +} + + +static const struct ext_session_lock_surface_v1_interface lock_surface_implementation = { + .destroy = resource_handle_destroy, + .ack_configure = lock_surface_handle_ack_configure, +}; + +static void lock_surface_role_commit(struct wlr_surface *surface) { + struct wlr_session_lock_surface_v1 *lock_surface = + wlr_session_lock_surface_v1_from_wlr_surface(surface); + if (lock_surface == NULL) { + return; + } + + if (!lock_surface->configured) { + wl_resource_post_error(lock_surface->resource, + EXT_SESSION_LOCK_SURFACE_V1_ERROR_COMMIT_BEFORE_FIRST_ACK, + "session lock surface has never been configured"); + return; + } + + if (surface->current.width < 0 || surface->current.height < 0 || + (uint32_t)surface->current.width != lock_surface->pending.width || + (uint32_t)surface->current.height != lock_surface->pending.height) { + wl_resource_post_error(lock_surface->resource, + EXT_SESSION_LOCK_SURFACE_V1_ERROR_DIMENSIONS_MISMATCH, + "committed surface dimensions do not match last acked configure"); + return; + } + + lock_surface->current = lock_surface->pending; + + if (!lock_surface->mapped) { + lock_surface->mapped = true; + wlr_signal_emit_safe(&lock_surface->events.map, NULL); + } +} + +static void lock_surface_role_precommit(struct wlr_surface *surface, + const struct wlr_surface_state *state) { + struct wlr_session_lock_surface_v1 *lock_surface = + wlr_session_lock_surface_v1_from_wlr_surface(surface); + if (lock_surface == NULL) { + return; + } + + if (state->committed & WLR_SURFACE_STATE_BUFFER && state->buffer == NULL) { + wl_resource_post_error(lock_surface->resource, + EXT_SESSION_LOCK_SURFACE_V1_ERROR_NULL_BUFFER, + "session lock surfaces committed with null buffer"); + return; + } +} + +static const struct wlr_surface_role lock_surface_role = { + .name = "ext_session_lock_surface_v1", + .commit = lock_surface_role_commit, + .precommit = lock_surface_role_precommit, +}; + +static void lock_surface_destroy( + struct wlr_session_lock_surface_v1 *lock_surface) { + wlr_signal_emit_safe(&lock_surface->events.destroy, NULL); + + wl_list_remove(&lock_surface->link); + + struct wlr_session_lock_surface_v1_configure *configure, *tmp; + wl_list_for_each_safe(configure, tmp, &lock_surface->configure_list, link) { + wl_list_remove(&configure->link); + free(configure); + } + + assert(wl_list_empty(&lock_surface->events.map.listener_list)); + assert(wl_list_empty(&lock_surface->events.destroy.listener_list)); + + wl_list_remove(&lock_surface->output_destroy.link); + wl_list_remove(&lock_surface->surface_destroy.link); + + lock_surface->surface->role_data = NULL; + wl_resource_set_user_data(lock_surface->resource, NULL); + free(lock_surface); +} + +static void lock_surface_handle_output_destroy(struct wl_listener *listener, + void *data) { + struct wlr_session_lock_surface_v1 *lock_surface = + wl_container_of(listener, lock_surface, output_destroy); + lock_surface_destroy(lock_surface); +} + +static void lock_surface_handle_surface_destroy(struct wl_listener *listener, + void *data) { + struct wlr_session_lock_surface_v1 *lock_surface = + wl_container_of(listener, lock_surface, surface_destroy); + lock_surface_destroy(lock_surface); +} + +static void lock_surface_resource_destroy(struct wl_resource *resource) { + struct wlr_session_lock_surface_v1 *lock_surface = + lock_surface_from_resource(resource); + if (lock_surface != NULL) { + lock_surface_destroy(lock_surface); + } +} + +static void lock_handle_get_lock_surface(struct wl_client *client, + struct wl_resource *lock_resource, uint32_t id, + struct wl_resource *surface_resource, + struct wl_resource *output_resource) { + // We always need to create a lock surface resource to stay in sync + // with the client, even if the lock resource or output resource is + // inert. For example, if the compositor denies the lock and immediately + // calls wlr_session_lock_v1_destroy() the client may have already sent + // get_lock_surface requests. + struct wl_resource *lock_surface_resource = wl_resource_create( + client, &ext_session_lock_surface_v1_interface, + wl_resource_get_version(lock_resource), id); + if (lock_surface_resource == NULL) { + wl_client_post_no_memory(client); + return; + } + + // Leave the lock surface resource inert for now, we will set the + // user data at the end of this function if everything is successful. + wl_resource_set_implementation(lock_surface_resource, + &lock_surface_implementation, NULL, + lock_surface_resource_destroy); + + struct wlr_session_lock_v1 *lock = lock_from_resource(lock_resource); + if (lock == NULL) { + return; + } + + struct wlr_output *output = wlr_output_from_resource(output_resource); + if (output == NULL) { + return; + } + + struct wlr_session_lock_surface_v1 *other; + wl_list_for_each(other, &lock->surfaces, link) { + if (other->output == output) { + wl_resource_post_error(lock_resource, + EXT_SESSION_LOCK_V1_ERROR_DUPLICATE_OUTPUT, + "session lock surface already created for the given output"); + return; + } + } + + struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); + + if (wlr_surface_has_buffer(surface)) { + wl_resource_post_error(lock_resource, + EXT_SESSION_LOCK_V1_ERROR_ALREADY_CONSTRUCTED, + "surface already has a buffer attached"); + return; + } + + struct wlr_session_lock_surface_v1 *lock_surface = + calloc(1, sizeof(struct wlr_session_lock_surface_v1)); + if (lock_surface == NULL) { + wl_client_post_no_memory(client); + return; + } + + if (!wlr_surface_set_role(surface, &lock_surface_role, lock_surface, + lock_resource, EXT_SESSION_LOCK_V1_ERROR_ROLE)) { + free(lock_surface); + return; + } + + lock_surface->resource = lock_surface_resource; + wl_resource_set_user_data(lock_surface_resource, lock_surface); + + wl_list_insert(&lock->surfaces, &lock_surface->link); + + lock_surface->output = output; + lock_surface->surface = surface; + + wl_list_init(&lock_surface->configure_list); + + wl_signal_init(&lock_surface->events.map); + wl_signal_init(&lock_surface->events.destroy); + + wl_signal_add(&output->events.destroy, &lock_surface->output_destroy); + lock_surface->output_destroy.notify = lock_surface_handle_output_destroy; + + wl_signal_add(&surface->events.destroy, &lock_surface->surface_destroy); + lock_surface->surface_destroy.notify = lock_surface_handle_surface_destroy; + + wlr_signal_emit_safe(&lock->events.new_surface, lock_surface); +} + +static void lock_handle_unlock_and_destroy(struct wl_client *client, + struct wl_resource *lock_resource) { + struct wlr_session_lock_v1 *lock = lock_from_resource(lock_resource); + if (lock == NULL) { + return; + } + + wlr_signal_emit_safe(&lock->events.unlock, NULL); + + wl_resource_destroy(lock_resource); +} + +static const struct ext_session_lock_v1_interface lock_implementation = { + .destroy = resource_handle_destroy, + .get_lock_surface = lock_handle_get_lock_surface, + .unlock_and_destroy = lock_handle_unlock_and_destroy, +}; + +void wlr_session_lock_v1_send_locked(struct wlr_session_lock_v1 *lock) { + ext_session_lock_v1_send_locked(lock->resource); +} + +static void lock_destroy(struct wlr_session_lock_v1 *lock) { + struct wlr_session_lock_surface_v1 *lock_surface, *tmp; + wl_list_for_each_safe(lock_surface, tmp, &lock->surfaces, link) { + lock_surface_destroy(lock_surface); + } + assert(wl_list_empty(&lock->surfaces)); + + wlr_signal_emit_safe(&lock->events.destroy, NULL); + + assert(wl_list_empty(&lock->events.new_surface.listener_list)); + assert(wl_list_empty(&lock->events.unlock.listener_list)); + assert(wl_list_empty(&lock->events.destroy.listener_list)); + + wl_resource_set_user_data(lock->resource, NULL); + free(lock); +} + +void wlr_session_lock_v1_destroy(struct wlr_session_lock_v1 *lock) { + ext_session_lock_v1_send_finished(lock->resource); + lock_destroy(lock); +} + +static void lock_resource_destroy(struct wl_resource *lock_resource) { + struct wlr_session_lock_v1 *lock = lock_from_resource(lock_resource); + if (lock != NULL) { + lock_destroy(lock); + } +} + +static void lock_manager_handle_lock(struct wl_client *client, + struct wl_resource *manager_resource, uint32_t id) { + struct wlr_session_lock_manager_v1 *lock_manager = + lock_manager_from_resource(manager_resource); + + struct wlr_session_lock_v1 *lock = + calloc(1, sizeof(struct wlr_session_lock_v1)); + if (lock == NULL) { + wl_client_post_no_memory(client); + return; + } + + lock->resource = wl_resource_create(client, &ext_session_lock_v1_interface, + wl_resource_get_version(manager_resource), id); + if (lock->resource == NULL) { + free(lock); + wl_client_post_no_memory(client); + return; + } + + wl_list_init(&lock->surfaces); + + wl_signal_init(&lock->events.new_surface); + wl_signal_init(&lock->events.unlock); + wl_signal_init(&lock->events.destroy); + + wl_resource_set_implementation(lock->resource, &lock_implementation, + lock, lock_resource_destroy); + + wlr_signal_emit_safe(&lock_manager->events.new_lock, lock); +} + +static const struct ext_session_lock_manager_v1_interface lock_manager_implementation = { + .destroy = resource_handle_destroy, + .lock = lock_manager_handle_lock, +}; + +static void lock_manager_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id) { + struct wlr_session_lock_manager_v1 *lock_manager = data; + + struct wl_resource *resource = wl_resource_create( + client, &ext_session_lock_manager_v1_interface, version, id); + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &lock_manager_implementation, + lock_manager, NULL); +} + +static void handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_session_lock_manager_v1 *lock_manager = + wl_container_of(listener, lock_manager, display_destroy); + wlr_signal_emit_safe(&lock_manager->events.destroy, NULL); + wl_list_remove(&lock_manager->display_destroy.link); + + wl_global_destroy(lock_manager->global); + + assert(wl_list_empty(&lock_manager->events.new_lock.listener_list)); + assert(wl_list_empty(&lock_manager->events.destroy.listener_list)); + + free(lock_manager); +} + +struct wlr_session_lock_manager_v1 *wlr_session_lock_manager_v1_create(struct wl_display *display) { + struct wlr_session_lock_manager_v1 *lock_manager = + calloc(1, sizeof(struct wlr_session_lock_manager_v1)); + if (lock_manager == NULL) { + return NULL; + } + + struct wl_global *global = wl_global_create(display, + &ext_session_lock_manager_v1_interface, SESSION_LOCK_VERSION, + lock_manager, lock_manager_bind); + if (global == NULL) { + free(lock_manager); + return NULL; + } + lock_manager->global = global; + + wl_signal_init(&lock_manager->events.new_lock); + wl_signal_init(&lock_manager->events.destroy); + + lock_manager->display_destroy.notify = handle_display_destroy; + wl_display_add_destroy_listener(display, &lock_manager->display_destroy); + + return lock_manager; +} diff --git a/types/wlr_subcompositor.c b/types/wlr_subcompositor.c new file mode 100644 index 000000000..2b9b97cd2 --- /dev/null +++ b/types/wlr_subcompositor.c @@ -0,0 +1,486 @@ +#include +#include +#include +#include +#include +#include "types/wlr_region.h" +#include "util/signal.h" + +#define SUBCOMPOSITOR_VERSION 1 + +static bool subsurface_is_synchronized(struct wlr_subsurface *subsurface) { + while (subsurface != NULL) { + if (subsurface->synchronized) { + return true; + } + + if (!wlr_surface_is_subsurface(subsurface->parent)) { + break; + } + subsurface = wlr_subsurface_from_wlr_surface(subsurface->parent); + } + + return false; +} + +static void subsurface_unmap(struct wlr_subsurface *subsurface); + +static void subsurface_destroy(struct wlr_subsurface *subsurface) { + if (subsurface == NULL) { + return; + } + + if (subsurface->has_cache) { + wlr_surface_unlock_cached(subsurface->surface, + subsurface->cached_seq); + } + + subsurface_unmap(subsurface); + + wlr_signal_emit_safe(&subsurface->events.destroy, subsurface); + + wl_list_remove(&subsurface->surface_destroy.link); + wl_list_remove(&subsurface->surface_client_commit.link); + wl_list_remove(&subsurface->current.link); + wl_list_remove(&subsurface->pending.link); + wl_list_remove(&subsurface->parent_destroy.link); + + wl_resource_set_user_data(subsurface->resource, NULL); + if (subsurface->surface) { + subsurface->surface->role_data = NULL; + } + free(subsurface); +} + +static const struct wl_subsurface_interface subsurface_implementation; + +/** + * Get a wlr_subsurface from a wl_subsurface resource. + * + * Returns NULL if the subsurface is inert (e.g. the wl_surface object or the + * parent surface got destroyed). + */ +static struct wlr_subsurface *subsurface_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &wl_subsurface_interface, + &subsurface_implementation)); + return wl_resource_get_user_data(resource); +} + +static void subsurface_resource_destroy(struct wl_resource *resource) { + struct wlr_subsurface *subsurface = subsurface_from_resource(resource); + subsurface_destroy(subsurface); +} + +static void subsurface_handle_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static void subsurface_handle_set_position(struct wl_client *client, + struct wl_resource *resource, int32_t x, int32_t y) { + struct wlr_subsurface *subsurface = subsurface_from_resource(resource); + if (subsurface == NULL) { + return; + } + + subsurface->pending.x = x; + subsurface->pending.y = y; +} + +static struct wlr_subsurface *subsurface_find_sibling( + struct wlr_subsurface *subsurface, struct wlr_surface *surface) { + struct wlr_surface *parent = subsurface->parent; + + struct wlr_subsurface *sibling; + wl_list_for_each(sibling, &parent->pending.subsurfaces_below, pending.link) { + if (sibling->surface == surface && sibling != subsurface) { + return sibling; + } + } + wl_list_for_each(sibling, &parent->pending.subsurfaces_above, pending.link) { + if (sibling->surface == surface && sibling != subsurface) { + return sibling; + } + } + + return NULL; +} + +static void subsurface_handle_place_above(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *sibling_resource) { + struct wlr_subsurface *subsurface = subsurface_from_resource(resource); + if (subsurface == NULL) { + return; + } + + struct wlr_surface *sibling_surface = + wlr_surface_from_resource(sibling_resource); + + struct wl_list *node; + if (sibling_surface == subsurface->parent) { + node = &subsurface->parent->pending.subsurfaces_above; + } else { + struct wlr_subsurface *sibling = + subsurface_find_sibling(subsurface, sibling_surface); + if (!sibling) { + wl_resource_post_error(subsurface->resource, + WL_SUBSURFACE_ERROR_BAD_SURFACE, + "%s: wl_surface@%" PRIu32 "is not a parent or sibling", + "place_above", wl_resource_get_id(sibling_resource)); + return; + } + node = &sibling->pending.link; + } + + wl_list_remove(&subsurface->pending.link); + wl_list_insert(node, &subsurface->pending.link); + + subsurface->reordered = true; +} + +static void subsurface_handle_place_below(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *sibling_resource) { + struct wlr_subsurface *subsurface = subsurface_from_resource(resource); + if (subsurface == NULL) { + return; + } + + struct wlr_surface *sibling_surface = + wlr_surface_from_resource(sibling_resource); + + struct wl_list *node; + if (sibling_surface == subsurface->parent) { + node = &subsurface->parent->pending.subsurfaces_below; + } else { + struct wlr_subsurface *sibling = + subsurface_find_sibling(subsurface, sibling_surface); + if (!sibling) { + wl_resource_post_error(subsurface->resource, + WL_SUBSURFACE_ERROR_BAD_SURFACE, + "%s: wl_surface@%" PRIu32 " is not a parent or sibling", + "place_below", wl_resource_get_id(sibling_resource)); + return; + } + node = &sibling->pending.link; + } + + wl_list_remove(&subsurface->pending.link); + wl_list_insert(node->prev, &subsurface->pending.link); + + subsurface->reordered = true; +} + +static void subsurface_handle_set_sync(struct wl_client *client, + struct wl_resource *resource) { + struct wlr_subsurface *subsurface = subsurface_from_resource(resource); + if (subsurface == NULL) { + return; + } + + subsurface->synchronized = true; +} + +static void subsurface_handle_set_desync(struct wl_client *client, + struct wl_resource *resource) { + struct wlr_subsurface *subsurface = subsurface_from_resource(resource); + if (subsurface == NULL) { + return; + } + + if (subsurface->synchronized) { + subsurface->synchronized = false; + + if (!subsurface_is_synchronized(subsurface) && + subsurface->has_cache) { + wlr_surface_unlock_cached(subsurface->surface, + subsurface->cached_seq); + subsurface->has_cache = false; + } + } +} + +static const struct wl_subsurface_interface subsurface_implementation = { + .destroy = subsurface_handle_destroy, + .set_position = subsurface_handle_set_position, + .place_above = subsurface_handle_place_above, + .place_below = subsurface_handle_place_below, + .set_sync = subsurface_handle_set_sync, + .set_desync = subsurface_handle_set_desync, +}; + +/** + * Checks if this subsurface needs to be marked as mapped. This can happen if: + * - The subsurface has a buffer + * - Its parent is mapped + */ +static void subsurface_consider_map(struct wlr_subsurface *subsurface, + bool check_parent) { + if (subsurface->mapped || !wlr_surface_has_buffer(subsurface->surface)) { + return; + } + + if (check_parent && wlr_surface_is_subsurface(subsurface->parent)) { + struct wlr_subsurface *parent = + wlr_subsurface_from_wlr_surface(subsurface->parent); + if (parent == NULL || !parent->mapped) { + return; + } + } + + // Now we can map the subsurface + wlr_signal_emit_safe(&subsurface->events.map, subsurface); + subsurface->mapped = true; + + // Try mapping all children too + struct wlr_subsurface *child; + wl_list_for_each(child, &subsurface->surface->current.subsurfaces_below, + current.link) { + subsurface_consider_map(child, false); + } + wl_list_for_each(child, &subsurface->surface->current.subsurfaces_above, + current.link) { + subsurface_consider_map(child, false); + } +} + +static void subsurface_unmap(struct wlr_subsurface *subsurface) { + if (!subsurface->mapped) { + return; + } + + wlr_signal_emit_safe(&subsurface->events.unmap, subsurface); + subsurface->mapped = false; + + // Unmap all children + struct wlr_subsurface *child; + wl_list_for_each(child, &subsurface->surface->current.subsurfaces_below, + current.link) { + subsurface_unmap(child); + } + wl_list_for_each(child, &subsurface->surface->current.subsurfaces_above, + current.link) { + subsurface_unmap(child); + } +} + +static void subsurface_role_commit(struct wlr_surface *surface) { + struct wlr_subsurface *subsurface = + wlr_subsurface_from_wlr_surface(surface); + if (subsurface == NULL) { + return; + } + + subsurface_consider_map(subsurface, true); +} + +static void subsurface_role_precommit(struct wlr_surface *surface, + const struct wlr_surface_state *state) { + struct wlr_subsurface *subsurface = + wlr_subsurface_from_wlr_surface(surface); + if (subsurface == NULL) { + return; + } + + if (state->committed & WLR_SURFACE_STATE_BUFFER && state->buffer == NULL) { + // This is a NULL commit + subsurface_unmap(subsurface); + } +} + +const struct wlr_surface_role subsurface_role = { + .name = "wl_subsurface", + .commit = subsurface_role_commit, + .precommit = subsurface_role_precommit, +}; + +static void subsurface_handle_parent_destroy(struct wl_listener *listener, + void *data) { + struct wlr_subsurface *subsurface = + wl_container_of(listener, subsurface, parent_destroy); + // Once the parent is destroyed, the client has no way to use the + // wl_subsurface object anymore, so we can destroy it. + subsurface_destroy(subsurface); +} + +static void subsurface_handle_surface_destroy(struct wl_listener *listener, + void *data) { + struct wlr_subsurface *subsurface = + wl_container_of(listener, subsurface, surface_destroy); + subsurface_destroy(subsurface); +} + +static void subsurface_handle_surface_client_commit( + struct wl_listener *listener, void *data) { + struct wlr_subsurface *subsurface = + wl_container_of(listener, subsurface, surface_client_commit); + struct wlr_surface *surface = subsurface->surface; + + if (subsurface_is_synchronized(subsurface)) { + if (subsurface->has_cache) { + // We already lock a previous commit. The prevents any future + // commit to be applied before we release the previous commit. + return; + } + subsurface->has_cache = true; + subsurface->cached_seq = wlr_surface_lock_pending(surface); + } else if (subsurface->has_cache) { + wlr_surface_unlock_cached(surface, subsurface->cached_seq); + subsurface->has_cache = false; + } +} + +static struct wlr_subsurface *subsurface_create(struct wlr_surface *surface, + struct wlr_surface *parent, uint32_t version, uint32_t id) { + struct wl_client *client = wl_resource_get_client(surface->resource); + + struct wlr_subsurface *subsurface = + calloc(1, sizeof(struct wlr_subsurface)); + if (!subsurface) { + wl_client_post_no_memory(client); + return NULL; + } + subsurface->synchronized = true; + subsurface->surface = surface; + subsurface->resource = + wl_resource_create(client, &wl_subsurface_interface, version, id); + if (subsurface->resource == NULL) { + free(subsurface); + wl_client_post_no_memory(client); + return NULL; + } + wl_resource_set_implementation(subsurface->resource, + &subsurface_implementation, subsurface, subsurface_resource_destroy); + + wl_signal_init(&subsurface->events.destroy); + wl_signal_init(&subsurface->events.map); + wl_signal_init(&subsurface->events.unmap); + + wl_signal_add(&surface->events.destroy, &subsurface->surface_destroy); + subsurface->surface_destroy.notify = subsurface_handle_surface_destroy; + wl_signal_add(&surface->events.client_commit, + &subsurface->surface_client_commit); + subsurface->surface_client_commit.notify = + subsurface_handle_surface_client_commit; + + // link parent + subsurface->parent = parent; + wl_signal_add(&parent->events.destroy, &subsurface->parent_destroy); + subsurface->parent_destroy.notify = subsurface_handle_parent_destroy; + + wl_list_init(&subsurface->current.link); + wl_list_insert(parent->pending.subsurfaces_above.prev, + &subsurface->pending.link); + + surface->role_data = subsurface; + + return subsurface; +} + +bool wlr_surface_is_subsurface(struct wlr_surface *surface) { + return surface->role == &subsurface_role; +} + +struct wlr_subsurface *wlr_subsurface_from_wlr_surface( + struct wlr_surface *surface) { + assert(wlr_surface_is_subsurface(surface)); + return (struct wlr_subsurface *)surface->role_data; +} + +static void subcompositor_handle_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static void subcompositor_handle_get_subsurface(struct wl_client *client, + struct wl_resource *resource, uint32_t id, + struct wl_resource *surface_resource, + struct wl_resource *parent_resource) { + struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); + struct wlr_surface *parent = wlr_surface_from_resource(parent_resource); + + static const char msg[] = "get_subsurface: wl_subsurface@"; + + if (surface == parent) { + wl_resource_post_error(resource, + WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, + "%s%" PRIu32 ": wl_surface@%" PRIu32 " cannot be its own parent", + msg, id, wl_resource_get_id(surface_resource)); + return; + } + + if (wlr_surface_is_subsurface(surface) && + wlr_subsurface_from_wlr_surface(surface) != NULL) { + wl_resource_post_error(resource, + WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, + "%s%" PRIu32 ": wl_surface@%" PRIu32 " is already a sub-surface", + msg, id, wl_resource_get_id(surface_resource)); + return; + } + + if (wlr_surface_get_root_surface(parent) == surface) { + wl_resource_post_error(resource, + WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, + "%s%" PRIu32 ": wl_surface@%" PRIu32 " is an ancestor of parent", + msg, id, wl_resource_get_id(surface_resource)); + return; + } + + if (!wlr_surface_set_role(surface, &subsurface_role, NULL, + resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE)) { + return; + } + + subsurface_create(surface, parent, wl_resource_get_version(resource), id); +} + +static const struct wl_subcompositor_interface subcompositor_impl = { + .destroy = subcompositor_handle_destroy, + .get_subsurface = subcompositor_handle_get_subsurface, +}; + +static void subcompositor_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id) { + struct wlr_subcompositor *subcompositor = data; + struct wl_resource *resource = + wl_resource_create(client, &wl_subcompositor_interface, 1, id); + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &subcompositor_impl, + subcompositor, NULL); +} + +static void subcompositor_handle_display_destroy( + struct wl_listener *listener, void *data) { + struct wlr_subcompositor *subcompositor = + wl_container_of(listener, subcompositor, display_destroy); + wlr_signal_emit_safe(&subcompositor->events.destroy, NULL); + wl_list_remove(&subcompositor->display_destroy.link); + wl_global_destroy(subcompositor->global); + free(subcompositor); +} + +struct wlr_subcompositor *wlr_subcompositor_create(struct wl_display *display) { + struct wlr_subcompositor *subcompositor = + calloc(1, sizeof(*subcompositor)); + if (!subcompositor) { + return NULL; + } + + subcompositor->global = wl_global_create(display, + &wl_subcompositor_interface, SUBCOMPOSITOR_VERSION, + subcompositor, subcompositor_bind); + if (!subcompositor->global) { + free(subcompositor); + return NULL; + } + + wl_signal_init(&subcompositor->events.destroy); + + subcompositor->display_destroy.notify = subcompositor_handle_display_destroy; + wl_display_add_destroy_listener(display, &subcompositor->display_destroy); + + return subcompositor; +} diff --git a/types/wlr_surface.c b/types/wlr_surface.c deleted file mode 100644 index ffc77547a..000000000 --- a/types/wlr_surface.c +++ /dev/null @@ -1,1478 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "types/wlr_surface.h" -#include "util/signal.h" -#include "util/time.h" - -#define CALLBACK_VERSION 1 - -static int min(int fst, int snd) { - if (fst < snd) { - return fst; - } else { - return snd; - } -} - -static int max(int fst, int snd) { - if (fst > snd) { - return fst; - } else { - return snd; - } -} - -static void surface_handle_destroy(struct wl_client *client, - struct wl_resource *resource) { - wl_resource_destroy(resource); -} - -static void surface_handle_attach(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *buffer_resource, int32_t dx, int32_t dy) { - struct wlr_surface *surface = wlr_surface_from_resource(resource); - - struct wlr_buffer *buffer = NULL; - if (buffer_resource != NULL) { - buffer = wlr_buffer_from_resource(buffer_resource); - if (buffer == NULL) { - wl_resource_post_error(buffer_resource, 0, "unknown buffer type"); - return; - } - } - - surface->pending.committed |= WLR_SURFACE_STATE_BUFFER; - surface->pending.dx = dx; - surface->pending.dy = dy; - - wlr_buffer_unlock(surface->pending.buffer); - surface->pending.buffer = buffer; -} - -static void surface_handle_damage(struct wl_client *client, - struct wl_resource *resource, - int32_t x, int32_t y, int32_t width, int32_t height) { - struct wlr_surface *surface = wlr_surface_from_resource(resource); - if (width < 0 || height < 0) { - return; - } - surface->pending.committed |= WLR_SURFACE_STATE_SURFACE_DAMAGE; - pixman_region32_union_rect(&surface->pending.surface_damage, - &surface->pending.surface_damage, - x, y, width, height); -} - -static void callback_handle_resource_destroy(struct wl_resource *resource) { - wl_list_remove(wl_resource_get_link(resource)); -} - -static void surface_handle_frame(struct wl_client *client, - struct wl_resource *resource, uint32_t callback) { - struct wlr_surface *surface = wlr_surface_from_resource(resource); - - struct wl_resource *callback_resource = wl_resource_create(client, - &wl_callback_interface, CALLBACK_VERSION, callback); - if (callback_resource == NULL) { - wl_resource_post_no_memory(resource); - return; - } - wl_resource_set_implementation(callback_resource, NULL, NULL, - callback_handle_resource_destroy); - - wl_list_insert(surface->pending.frame_callback_list.prev, - wl_resource_get_link(callback_resource)); - - surface->pending.committed |= WLR_SURFACE_STATE_FRAME_CALLBACK_LIST; -} - -static void surface_handle_set_opaque_region(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *region_resource) { - struct wlr_surface *surface = wlr_surface_from_resource(resource); - surface->pending.committed |= WLR_SURFACE_STATE_OPAQUE_REGION; - if (region_resource) { - pixman_region32_t *region = wlr_region_from_resource(region_resource); - pixman_region32_copy(&surface->pending.opaque, region); - } else { - pixman_region32_clear(&surface->pending.opaque); - } -} - -static void surface_handle_set_input_region(struct wl_client *client, - struct wl_resource *resource, - struct wl_resource *region_resource) { - struct wlr_surface *surface = wlr_surface_from_resource(resource); - surface->pending.committed |= WLR_SURFACE_STATE_INPUT_REGION; - if (region_resource) { - pixman_region32_t *region = wlr_region_from_resource(region_resource); - pixman_region32_copy(&surface->pending.input, region); - } else { - pixman_region32_fini(&surface->pending.input); - pixman_region32_init_rect(&surface->pending.input, - INT32_MIN, INT32_MIN, UINT32_MAX, UINT32_MAX); - } -} - -/** - * Computes the surface viewport source size, ie. the size after applying the - * surface's scale, transform and cropping (via the viewport's source - * rectangle) but before applying the viewport scaling (via the viewport's - * destination rectangle). - */ -static void surface_state_viewport_src_size(struct wlr_surface_state *state, - int *out_width, int *out_height) { - if (state->buffer_width == 0 && state->buffer_height == 0) { - *out_width = *out_height = 0; - return; - } - - if (state->viewport.has_src) { - *out_width = state->viewport.src.width; - *out_height = state->viewport.src.height; - } else { - int width = state->buffer_width / state->scale; - int height = state->buffer_height / state->scale; - if ((state->transform & WL_OUTPUT_TRANSFORM_90) != 0) { - int tmp = width; - width = height; - height = tmp; - } - *out_width = width; - *out_height = height; - } -} - -static void surface_finalize_pending(struct wlr_surface *surface) { - struct wlr_surface_state *pending = &surface->pending; - - if ((pending->committed & WLR_SURFACE_STATE_BUFFER)) { - if (pending->buffer != NULL) { - pending->buffer_width = pending->buffer->width; - pending->buffer_height = pending->buffer->height; - } else { - pending->buffer_width = pending->buffer_height = 0; - } - } - - if (!pending->viewport.has_src && - (pending->buffer_width % pending->scale != 0 || - pending->buffer_height % pending->scale != 0)) { - // TODO: send WL_SURFACE_ERROR_INVALID_SIZE error once this issue is - // resolved: - // https://gitlab.freedesktop.org/wayland/wayland/-/issues/194 - wlr_log(WLR_DEBUG, "Client bug: submitted a buffer whose size (%dx%d) " - "is not divisible by scale (%d)", pending->buffer_width, - pending->buffer_height, pending->scale); - } - - if (pending->viewport.has_dst) { - if (pending->buffer_width == 0 && pending->buffer_height == 0) { - pending->width = pending->height = 0; - } else { - pending->width = pending->viewport.dst_width; - pending->height = pending->viewport.dst_height; - } - } else { - surface_state_viewport_src_size(pending, &pending->width, &pending->height); - } - - pixman_region32_intersect_rect(&pending->surface_damage, - &pending->surface_damage, 0, 0, pending->width, pending->height); - - pixman_region32_intersect_rect(&pending->buffer_damage, - &pending->buffer_damage, 0, 0, pending->buffer_width, - pending->buffer_height); -} - -static void surface_update_damage(pixman_region32_t *buffer_damage, - struct wlr_surface_state *current, struct wlr_surface_state *pending) { - pixman_region32_clear(buffer_damage); - - if (pending->width != current->width || - pending->height != current->height) { - // Damage the whole buffer on resize - pixman_region32_union_rect(buffer_damage, buffer_damage, 0, 0, - pending->buffer_width, pending->buffer_height); - } else { - // Copy over surface damage + buffer damage - pixman_region32_t surface_damage; - pixman_region32_init(&surface_damage); - - pixman_region32_copy(&surface_damage, &pending->surface_damage); - - if (pending->viewport.has_dst) { - int src_width, src_height; - surface_state_viewport_src_size(pending, &src_width, &src_height); - float scale_x = (float)pending->viewport.dst_width / src_width; - float scale_y = (float)pending->viewport.dst_height / src_height; - wlr_region_scale_xy(&surface_damage, &surface_damage, - 1.0 / scale_x, 1.0 / scale_y); - } - if (pending->viewport.has_src) { - // This is lossy: do a best-effort conversion - pixman_region32_translate(&surface_damage, - floor(pending->viewport.src.x), - floor(pending->viewport.src.y)); - } - - wlr_region_transform(&surface_damage, &surface_damage, - wlr_output_transform_invert(pending->transform), - pending->width, pending->height); - wlr_region_scale(&surface_damage, &surface_damage, pending->scale); - - pixman_region32_union(buffer_damage, - &pending->buffer_damage, &surface_damage); - - pixman_region32_fini(&surface_damage); - } -} - -/** - * Append pending state to current state and clear pending state. - */ -static void surface_state_move(struct wlr_surface_state *state, - struct wlr_surface_state *next) { - state->width = next->width; - state->height = next->height; - state->buffer_width = next->buffer_width; - state->buffer_height = next->buffer_height; - - if (next->committed & WLR_SURFACE_STATE_SCALE) { - state->scale = next->scale; - } - if (next->committed & WLR_SURFACE_STATE_TRANSFORM) { - state->transform = next->transform; - } - if (next->committed & WLR_SURFACE_STATE_BUFFER) { - state->dx = next->dx; - state->dy = next->dy; - next->dx = next->dy = 0; - - wlr_buffer_unlock(state->buffer); - state->buffer = NULL; - if (next->buffer) { - state->buffer = wlr_buffer_lock(next->buffer); - } - wlr_buffer_unlock(next->buffer); - next->buffer = NULL; - } else { - state->dx = state->dy = 0; - } - if (next->committed & WLR_SURFACE_STATE_SURFACE_DAMAGE) { - pixman_region32_copy(&state->surface_damage, &next->surface_damage); - pixman_region32_clear(&next->surface_damage); - } else { - pixman_region32_clear(&state->surface_damage); - } - if (next->committed & WLR_SURFACE_STATE_BUFFER_DAMAGE) { - pixman_region32_copy(&state->buffer_damage, &next->buffer_damage); - pixman_region32_clear(&next->buffer_damage); - } else { - pixman_region32_clear(&state->buffer_damage); - } - if (next->committed & WLR_SURFACE_STATE_OPAQUE_REGION) { - pixman_region32_copy(&state->opaque, &next->opaque); - } - if (next->committed & WLR_SURFACE_STATE_INPUT_REGION) { - pixman_region32_copy(&state->input, &next->input); - } - if (next->committed & WLR_SURFACE_STATE_VIEWPORT) { - memcpy(&state->viewport, &next->viewport, sizeof(state->viewport)); - } - if (next->committed & WLR_SURFACE_STATE_FRAME_CALLBACK_LIST) { - wl_list_insert_list(&state->frame_callback_list, - &next->frame_callback_list); - wl_list_init(&next->frame_callback_list); - } - - state->committed |= next->committed; - next->committed = 0; - - state->seq = next->seq; - - state->cached_state_locks = next->cached_state_locks; - next->cached_state_locks = 0; -} - -static void surface_damage_subsurfaces(struct wlr_subsurface *subsurface) { - // XXX: This is probably the wrong way to do it, because this damage should - // come from the client, but weston doesn't do it correctly either and it - // seems to work ok. See the comment on weston_surface_damage for more info - // about a better approach. - struct wlr_surface *surface = subsurface->surface; - pixman_region32_union_rect(&surface->buffer_damage, - &surface->buffer_damage, 0, 0, - surface->current.buffer_width, surface->current.buffer_height); - - subsurface->reordered = false; - - struct wlr_subsurface *child; - wl_list_for_each(child, &subsurface->surface->current.subsurfaces_below, - current.link) { - surface_damage_subsurfaces(child); - } - wl_list_for_each(child, &subsurface->surface->current.subsurfaces_above, - current.link) { - surface_damage_subsurfaces(child); - } -} - -static void surface_apply_damage(struct wlr_surface *surface) { - if (surface->current.buffer == NULL) { - // NULL commit - if (surface->buffer != NULL) { - wlr_buffer_unlock(&surface->buffer->base); - } - surface->buffer = NULL; - return; - } - - if (surface->buffer != NULL) { - if (wlr_client_buffer_apply_damage(surface->buffer, - surface->current.buffer, &surface->buffer_damage)) { - wlr_buffer_unlock(surface->current.buffer); - surface->current.buffer = NULL; - return; - } - } - - struct wlr_client_buffer *buffer = wlr_client_buffer_create( - surface->current.buffer, surface->renderer); - - wlr_buffer_unlock(surface->current.buffer); - surface->current.buffer = NULL; - - if (buffer == NULL) { - wlr_log(WLR_ERROR, "Failed to upload buffer"); - return; - } - - if (surface->buffer != NULL) { - wlr_buffer_unlock(&surface->buffer->base); - } - surface->buffer = buffer; -} - -static void surface_update_opaque_region(struct wlr_surface *surface) { - struct wlr_texture *texture = wlr_surface_get_texture(surface); - if (texture == NULL) { - pixman_region32_clear(&surface->opaque_region); - return; - } - - if (wlr_texture_is_opaque(texture)) { - pixman_region32_init_rect(&surface->opaque_region, - 0, 0, surface->current.width, surface->current.height); - return; - } - - pixman_region32_intersect_rect(&surface->opaque_region, - &surface->current.opaque, - 0, 0, surface->current.width, surface->current.height); -} - -static void surface_update_input_region(struct wlr_surface *surface) { - pixman_region32_intersect_rect(&surface->input_region, - &surface->current.input, - 0, 0, surface->current.width, surface->current.height); -} - -static void surface_state_init(struct wlr_surface_state *state); - -static void surface_cache_pending(struct wlr_surface *surface) { - struct wlr_surface_state *cached = calloc(1, sizeof(*cached)); - if (!cached) { - wl_resource_post_no_memory(surface->resource); - return; - } - - surface_state_init(cached); - surface_state_move(cached, &surface->pending); - - wl_list_insert(surface->cached.prev, &cached->cached_state_link); - - surface->pending.seq++; -} - -static void surface_commit_state(struct wlr_surface *surface, - struct wlr_surface_state *next) { - assert(next->cached_state_locks == 0); - - bool invalid_buffer = next->committed & WLR_SURFACE_STATE_BUFFER; - - surface->sx += next->dx; - surface->sy += next->dy; - surface_update_damage(&surface->buffer_damage, &surface->current, next); - - surface->previous.scale = surface->current.scale; - surface->previous.transform = surface->current.transform; - surface->previous.width = surface->current.width; - surface->previous.height = surface->current.height; - surface->previous.buffer_width = surface->current.buffer_width; - surface->previous.buffer_height = surface->current.buffer_height; - - surface_state_move(&surface->current, next); - - if (invalid_buffer) { - surface_apply_damage(surface); - } - surface_update_opaque_region(surface); - surface_update_input_region(surface); - - // commit subsurface order - struct wlr_subsurface *subsurface; - wl_list_for_each_reverse(subsurface, &surface->pending.subsurfaces_above, - pending.link) { - wl_list_remove(&subsurface->current.link); - wl_list_insert(&surface->current.subsurfaces_above, - &subsurface->current.link); - - if (subsurface->reordered) { - // TODO: damage all the subsurfaces - surface_damage_subsurfaces(subsurface); - } - } - wl_list_for_each_reverse(subsurface, &surface->pending.subsurfaces_below, - pending.link) { - wl_list_remove(&subsurface->current.link); - wl_list_insert(&surface->current.subsurfaces_below, - &subsurface->current.link); - - if (subsurface->reordered) { - // TODO: damage all the subsurfaces - surface_damage_subsurfaces(subsurface); - } - } - - // If we're committing the pending state, bump the pending sequence number - // here, to allow commit listeners to lock the new pending state. - if (next == &surface->pending) { - surface->pending.seq++; - } - - if (surface->role && surface->role->commit) { - surface->role->commit(surface); - } - - wlr_signal_emit_safe(&surface->events.commit, surface); -} - -static bool subsurface_is_synchronized(struct wlr_subsurface *subsurface) { - while (subsurface != NULL) { - if (subsurface->synchronized) { - return true; - } - - if (!subsurface->parent) { - return false; - } - - if (!wlr_surface_is_subsurface(subsurface->parent)) { - break; - } - subsurface = wlr_subsurface_from_wlr_surface(subsurface->parent); - } - - return false; -} - -/** - * Recursive function to commit the effectively synchronized children. - */ -static void subsurface_parent_commit(struct wlr_subsurface *subsurface, - bool synchronized) { - struct wlr_surface *surface = subsurface->surface; - if (synchronized || subsurface->synchronized) { - if (subsurface->has_cache) { - wlr_surface_unlock_cached(surface, subsurface->cached_seq); - subsurface->has_cache = false; - subsurface->cached_seq = 0; - } - - struct wlr_subsurface *subsurface; - wl_list_for_each(subsurface, &surface->current.subsurfaces_below, - current.link) { - subsurface_parent_commit(subsurface, true); - } - wl_list_for_each(subsurface, &surface->current.subsurfaces_above, - current.link) { - subsurface_parent_commit(subsurface, true); - } - } -} - -static void subsurface_commit(struct wlr_subsurface *subsurface) { - struct wlr_surface *surface = subsurface->surface; - - if (subsurface_is_synchronized(subsurface)) { - if (subsurface->has_cache) { - // We already lock a previous commit. The prevents any future - // commit to be applied before we release the previous commit. - return; - } - subsurface->has_cache = true; - subsurface->cached_seq = wlr_surface_lock_pending(surface); - } -} - -static void surface_handle_commit(struct wl_client *client, - struct wl_resource *resource) { - struct wlr_surface *surface = wlr_surface_from_resource(resource); - - struct wlr_subsurface *subsurface = wlr_surface_is_subsurface(surface) ? - wlr_subsurface_from_wlr_surface(surface) : NULL; - if (subsurface != NULL) { - subsurface_commit(subsurface); - } - - surface_finalize_pending(surface); - - if (surface->role && surface->role->precommit) { - surface->role->precommit(surface); - } - - if (surface->pending.cached_state_locks > 0 || !wl_list_empty(&surface->cached)) { - surface_cache_pending(surface); - } else { - surface_commit_state(surface, &surface->pending); - } - - wl_list_for_each(subsurface, &surface->current.subsurfaces_below, current.link) { - subsurface_parent_commit(subsurface, false); - } - wl_list_for_each(subsurface, &surface->current.subsurfaces_above, current.link) { - subsurface_parent_commit(subsurface, false); - } -} - -static void surface_handle_set_buffer_transform(struct wl_client *client, - struct wl_resource *resource, int32_t transform) { - if (transform < WL_OUTPUT_TRANSFORM_NORMAL || - transform > WL_OUTPUT_TRANSFORM_FLIPPED_270) { - wl_resource_post_error(resource, WL_SURFACE_ERROR_INVALID_TRANSFORM, - "Specified transform value (%d) is invalid", transform); - return; - } - struct wlr_surface *surface = wlr_surface_from_resource(resource); - surface->pending.committed |= WLR_SURFACE_STATE_TRANSFORM; - surface->pending.transform = transform; -} - -static void surface_handle_set_buffer_scale(struct wl_client *client, - struct wl_resource *resource, int32_t scale) { - if (scale <= 0) { - wl_resource_post_error(resource, WL_SURFACE_ERROR_INVALID_SCALE, - "Specified scale value (%d) is not positive", scale); - return; - } - struct wlr_surface *surface = wlr_surface_from_resource(resource); - surface->pending.committed |= WLR_SURFACE_STATE_SCALE; - surface->pending.scale = scale; -} - -static void surface_handle_damage_buffer(struct wl_client *client, - struct wl_resource *resource, - int32_t x, int32_t y, int32_t width, - int32_t height) { - struct wlr_surface *surface = wlr_surface_from_resource(resource); - if (width < 0 || height < 0) { - return; - } - surface->pending.committed |= WLR_SURFACE_STATE_BUFFER_DAMAGE; - pixman_region32_union_rect(&surface->pending.buffer_damage, - &surface->pending.buffer_damage, - x, y, width, height); -} - -static const struct wl_surface_interface surface_implementation = { - .destroy = surface_handle_destroy, - .attach = surface_handle_attach, - .damage = surface_handle_damage, - .frame = surface_handle_frame, - .set_opaque_region = surface_handle_set_opaque_region, - .set_input_region = surface_handle_set_input_region, - .commit = surface_handle_commit, - .set_buffer_transform = surface_handle_set_buffer_transform, - .set_buffer_scale = surface_handle_set_buffer_scale, - .damage_buffer = surface_handle_damage_buffer -}; - -struct wlr_surface *wlr_surface_from_resource(struct wl_resource *resource) { - assert(wl_resource_instance_of(resource, &wl_surface_interface, - &surface_implementation)); - return wl_resource_get_user_data(resource); -} - -static void surface_state_init(struct wlr_surface_state *state) { - state->scale = 1; - state->transform = WL_OUTPUT_TRANSFORM_NORMAL; - - wl_list_init(&state->subsurfaces_above); - wl_list_init(&state->subsurfaces_below); - - wl_list_init(&state->frame_callback_list); - - pixman_region32_init(&state->surface_damage); - pixman_region32_init(&state->buffer_damage); - pixman_region32_init(&state->opaque); - pixman_region32_init_rect(&state->input, - INT32_MIN, INT32_MIN, UINT32_MAX, UINT32_MAX); -} - -static void surface_state_finish(struct wlr_surface_state *state) { - wlr_buffer_unlock(state->buffer); - - struct wl_resource *resource, *tmp; - wl_resource_for_each_safe(resource, tmp, &state->frame_callback_list) { - wl_resource_destroy(resource); - } - - pixman_region32_fini(&state->surface_damage); - pixman_region32_fini(&state->buffer_damage); - pixman_region32_fini(&state->opaque); - pixman_region32_fini(&state->input); -} - -static void surface_state_destroy_cached(struct wlr_surface_state *state) { - surface_state_finish(state); - wl_list_remove(&state->cached_state_link); - free(state); -} - -static void subsurface_unmap(struct wlr_subsurface *subsurface); - -static void subsurface_destroy(struct wlr_subsurface *subsurface) { - if (subsurface == NULL) { - return; - } - - if (subsurface->has_cache) { - wlr_surface_unlock_cached(subsurface->surface, - subsurface->cached_seq); - } - - subsurface_unmap(subsurface); - - wlr_signal_emit_safe(&subsurface->events.destroy, subsurface); - - wl_list_remove(&subsurface->surface_destroy.link); - - if (subsurface->parent) { - wl_list_remove(&subsurface->current.link); - wl_list_remove(&subsurface->pending.link); - wl_list_remove(&subsurface->parent_destroy.link); - } - - wl_resource_set_user_data(subsurface->resource, NULL); - if (subsurface->surface) { - subsurface->surface->role_data = NULL; - } - 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 *surface = wlr_surface_from_resource(resource); - - struct wlr_surface_output *surface_output, *surface_output_tmp; - wl_list_for_each_safe(surface_output, surface_output_tmp, - &surface->current_outputs, link) { - surface_output_destroy(surface_output); - } - - wlr_signal_emit_safe(&surface->events.destroy, surface); - - wlr_addon_set_finish(&surface->addons); - - struct wlr_surface_state *cached, *cached_tmp; - wl_list_for_each_safe(cached, cached_tmp, &surface->cached, cached_state_link) { - surface_state_destroy_cached(cached); - } - - wl_list_remove(&surface->renderer_destroy.link); - surface_state_finish(&surface->pending); - surface_state_finish(&surface->current); - pixman_region32_fini(&surface->buffer_damage); - pixman_region32_fini(&surface->opaque_region); - pixman_region32_fini(&surface->input_region); - if (surface->buffer != NULL) { - wlr_buffer_unlock(&surface->buffer->base); - } - free(surface); -} - -static void surface_handle_renderer_destroy(struct wl_listener *listener, - void *data) { - struct wlr_surface *surface = - wl_container_of(listener, surface, renderer_destroy); - wl_resource_destroy(surface->resource); -} - -struct wlr_surface *surface_create(struct wl_client *client, - uint32_t version, uint32_t id, struct wlr_renderer *renderer) { - struct wlr_surface *surface = calloc(1, sizeof(struct wlr_surface)); - if (!surface) { - wl_client_post_no_memory(client); - return NULL; - } - surface->resource = wl_resource_create(client, &wl_surface_interface, - version, id); - if (surface->resource == NULL) { - free(surface); - wl_client_post_no_memory(client); - return NULL; - } - wl_resource_set_implementation(surface->resource, &surface_implementation, - surface, surface_handle_resource_destroy); - - wlr_log(WLR_DEBUG, "New wlr_surface %p (res %p)", surface, surface->resource); - - surface->renderer = renderer; - - surface_state_init(&surface->current); - surface_state_init(&surface->pending); - surface->pending.seq = 1; - - wl_signal_init(&surface->events.commit); - wl_signal_init(&surface->events.destroy); - wl_signal_init(&surface->events.new_subsurface); - wl_list_init(&surface->current_outputs); - wl_list_init(&surface->cached); - pixman_region32_init(&surface->buffer_damage); - pixman_region32_init(&surface->opaque_region); - pixman_region32_init(&surface->input_region); - wlr_addon_set_init(&surface->addons); - - wl_signal_add(&renderer->events.destroy, &surface->renderer_destroy); - surface->renderer_destroy.notify = surface_handle_renderer_destroy; - - return surface; -} - -struct wlr_texture *wlr_surface_get_texture(struct wlr_surface *surface) { - if (surface->buffer == NULL) { - return NULL; - } - return surface->buffer->texture; -} - -bool wlr_surface_has_buffer(struct wlr_surface *surface) { - return wlr_surface_get_texture(surface) != NULL; -} - -bool wlr_surface_set_role(struct wlr_surface *surface, - const struct wlr_surface_role *role, void *role_data, - struct wl_resource *error_resource, uint32_t error_code) { - assert(role != NULL); - - 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@%" PRIu32 ", already has role %s\n", - role->name, wl_resource_get_id(surface->resource), - surface->role->name); - } - return false; - } - 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@%" PRIu32 "," - "role object still exists", role->name, - wl_resource_get_id(surface->resource)); - return false; - } - - surface->role = role; - surface->role_data = role_data; - return true; -} - -uint32_t wlr_surface_lock_pending(struct wlr_surface *surface) { - surface->pending.cached_state_locks++; - return surface->pending.seq; -} - -void wlr_surface_unlock_cached(struct wlr_surface *surface, uint32_t seq) { - if (surface->pending.seq == seq) { - assert(surface->pending.cached_state_locks > 0); - surface->pending.cached_state_locks--; - return; - } - - bool found = false; - struct wlr_surface_state *cached; - wl_list_for_each(cached, &surface->cached, cached_state_link) { - if (cached->seq == seq) { - found = true; - break; - } - } - assert(found); - - assert(cached->cached_state_locks > 0); - cached->cached_state_locks--; - - if (cached->cached_state_locks != 0) { - return; - } - - if (cached->cached_state_link.prev != &surface->cached) { - // This isn't the first cached state. This means we're blocked on a - // previous cached state. - return; - } - - // TODO: consider merging all committed states together - struct wlr_surface_state *next, *tmp; - wl_list_for_each_safe(next, tmp, &surface->cached, cached_state_link) { - if (next->cached_state_locks > 0) { - break; - } - - surface_commit_state(surface, next); - surface_state_destroy_cached(next); - } -} - -static const struct wl_subsurface_interface subsurface_implementation; - -static struct wlr_subsurface *subsurface_from_resource( - struct wl_resource *resource) { - assert(wl_resource_instance_of(resource, &wl_subsurface_interface, - &subsurface_implementation)); - return wl_resource_get_user_data(resource); -} - -static void subsurface_resource_destroy(struct wl_resource *resource) { - struct wlr_subsurface *subsurface = subsurface_from_resource(resource); - subsurface_destroy(subsurface); -} - -static void subsurface_handle_destroy(struct wl_client *client, - struct wl_resource *resource) { - wl_resource_destroy(resource); -} - -static void subsurface_handle_set_position(struct wl_client *client, - struct wl_resource *resource, int32_t x, int32_t y) { - struct wlr_subsurface *subsurface = subsurface_from_resource(resource); - if (subsurface == NULL) { - return; - } - - subsurface->pending.x = x; - subsurface->pending.y = y; -} - -static struct wlr_subsurface *subsurface_find_sibling( - struct wlr_subsurface *subsurface, struct wlr_surface *surface) { - struct wlr_surface *parent = subsurface->parent; - - struct wlr_subsurface *sibling; - wl_list_for_each(sibling, &parent->current.subsurfaces_below, current.link) { - if (sibling->surface == surface && sibling != subsurface) { - return sibling; - } - } - wl_list_for_each(sibling, &parent->current.subsurfaces_above, current.link) { - if (sibling->surface == surface && sibling != subsurface) { - return sibling; - } - } - - return NULL; -} - -static void subsurface_handle_place_above(struct wl_client *client, - struct wl_resource *resource, struct wl_resource *sibling_resource) { - struct wlr_subsurface *subsurface = subsurface_from_resource(resource); - if (subsurface == NULL) { - return; - } - - struct wlr_surface *sibling_surface = - wlr_surface_from_resource(sibling_resource); - - struct wl_list *node; - if (sibling_surface == subsurface->parent) { - node = &subsurface->parent->pending.subsurfaces_above; - } else { - struct wlr_subsurface *sibling = - subsurface_find_sibling(subsurface, sibling_surface); - if (!sibling) { - wl_resource_post_error(subsurface->resource, - WL_SUBSURFACE_ERROR_BAD_SURFACE, - "%s: wl_surface@%" PRIu32 "is not a parent or sibling", - "place_above", wl_resource_get_id(sibling_resource)); - return; - } - node = &sibling->pending.link; - } - - wl_list_remove(&subsurface->pending.link); - wl_list_insert(node, &subsurface->pending.link); - - subsurface->reordered = true; -} - -static void subsurface_handle_place_below(struct wl_client *client, - struct wl_resource *resource, struct wl_resource *sibling_resource) { - struct wlr_subsurface *subsurface = subsurface_from_resource(resource); - if (subsurface == NULL) { - return; - } - - struct wlr_surface *sibling_surface = - wlr_surface_from_resource(sibling_resource); - - struct wl_list *node; - if (sibling_surface == subsurface->parent) { - node = &subsurface->parent->pending.subsurfaces_below; - } else { - struct wlr_subsurface *sibling = - subsurface_find_sibling(subsurface, sibling_surface); - if (!sibling) { - wl_resource_post_error(subsurface->resource, - WL_SUBSURFACE_ERROR_BAD_SURFACE, - "%s: wl_surface@%" PRIu32 " is not a parent or sibling", - "place_below", wl_resource_get_id(sibling_resource)); - return; - } - node = &sibling->pending.link; - } - - wl_list_remove(&subsurface->pending.link); - wl_list_insert(node->prev, &subsurface->pending.link); - - subsurface->reordered = true; -} - -static void subsurface_handle_set_sync(struct wl_client *client, - struct wl_resource *resource) { - struct wlr_subsurface *subsurface = subsurface_from_resource(resource); - if (subsurface == NULL) { - return; - } - - subsurface->synchronized = true; -} - -static void subsurface_handle_set_desync(struct wl_client *client, - struct wl_resource *resource) { - struct wlr_subsurface *subsurface = subsurface_from_resource(resource); - if (subsurface == NULL) { - return; - } - - if (subsurface->synchronized) { - subsurface->synchronized = false; - - if (!subsurface_is_synchronized(subsurface)) { - if (subsurface->has_cache) { - wlr_surface_unlock_cached(subsurface->surface, - subsurface->cached_seq); - subsurface->has_cache = false; - subsurface->cached_seq = 0; - } - - subsurface_parent_commit(subsurface, true); - } - } -} - -static const struct wl_subsurface_interface subsurface_implementation = { - .destroy = subsurface_handle_destroy, - .set_position = subsurface_handle_set_position, - .place_above = subsurface_handle_place_above, - .place_below = subsurface_handle_place_below, - .set_sync = subsurface_handle_set_sync, - .set_desync = subsurface_handle_set_desync, -}; - -/** - * Checks if this subsurface needs to be marked as mapped. This can happen if: - * - The subsurface has a buffer - * - Its parent is mapped - */ -static void subsurface_consider_map(struct wlr_subsurface *subsurface, - bool check_parent) { - if (subsurface->mapped || !wlr_surface_has_buffer(subsurface->surface)) { - return; - } - - if (check_parent) { - if (subsurface->parent == NULL) { - return; - } - if (wlr_surface_is_subsurface(subsurface->parent)) { - struct wlr_subsurface *parent = - wlr_subsurface_from_wlr_surface(subsurface->parent); - if (parent == NULL || !parent->mapped) { - return; - } - } - } - - // Now we can map the subsurface - wlr_signal_emit_safe(&subsurface->events.map, subsurface); - subsurface->mapped = true; - - // Try mapping all children too - struct wlr_subsurface *child; - wl_list_for_each(child, &subsurface->surface->current.subsurfaces_below, - current.link) { - subsurface_consider_map(child, false); - } - wl_list_for_each(child, &subsurface->surface->current.subsurfaces_above, - current.link) { - subsurface_consider_map(child, false); - } -} - -static void subsurface_unmap(struct wlr_subsurface *subsurface) { - if (!subsurface->mapped) { - return; - } - - wlr_signal_emit_safe(&subsurface->events.unmap, subsurface); - subsurface->mapped = false; - - // Unmap all children - struct wlr_subsurface *child; - wl_list_for_each(child, &subsurface->surface->current.subsurfaces_below, - current.link) { - subsurface_unmap(child); - } - wl_list_for_each(child, &subsurface->surface->current.subsurfaces_above, - current.link) { - subsurface_unmap(child); - } -} - -static void subsurface_role_commit(struct wlr_surface *surface) { - struct wlr_subsurface *subsurface = - wlr_subsurface_from_wlr_surface(surface); - if (subsurface == NULL) { - return; - } - - if (subsurface->current.x != subsurface->pending.x || - subsurface->current.y != subsurface->pending.y) { - // Subsurface has moved - int dx = subsurface->current.x - subsurface->pending.x; - int dy = subsurface->current.y - subsurface->pending.y; - - subsurface->current.x = subsurface->pending.x; - subsurface->current.y = subsurface->pending.y; - - if ((surface->current.transform & WL_OUTPUT_TRANSFORM_90) != 0) { - int tmp = dx; - dx = dy; - dy = tmp; - } - - pixman_region32_union_rect(&surface->buffer_damage, - &surface->buffer_damage, - dx * surface->previous.scale, dy * surface->previous.scale, - surface->previous.buffer_width, surface->previous.buffer_height); - pixman_region32_union_rect(&surface->buffer_damage, - &surface->buffer_damage, 0, 0, - surface->current.buffer_width, surface->current.buffer_height); - } - - subsurface_consider_map(subsurface, true); -} - -static void subsurface_role_precommit(struct wlr_surface *surface) { - struct wlr_subsurface *subsurface = - wlr_subsurface_from_wlr_surface(surface); - if (subsurface == NULL) { - return; - } - - if (surface->pending.committed & WLR_SURFACE_STATE_BUFFER && - surface->pending.buffer == NULL) { - // This is a NULL commit - subsurface_unmap(subsurface); - } -} - -const struct wlr_surface_role subsurface_role = { - .name = "wl_subsurface", - .commit = subsurface_role_commit, - .precommit = subsurface_role_precommit, -}; - -static void subsurface_handle_parent_destroy(struct wl_listener *listener, - void *data) { - struct wlr_subsurface *subsurface = - wl_container_of(listener, subsurface, parent_destroy); - subsurface_unmap(subsurface); - wl_list_remove(&subsurface->current.link); - wl_list_remove(&subsurface->pending.link); - wl_list_remove(&subsurface->parent_destroy.link); - subsurface->parent = NULL; -} - -static void subsurface_handle_surface_destroy(struct wl_listener *listener, - void *data) { - struct wlr_subsurface *subsurface = - wl_container_of(listener, subsurface, surface_destroy); - subsurface_destroy(subsurface); -} - -struct wlr_subsurface *subsurface_create(struct wlr_surface *surface, - struct wlr_surface *parent, uint32_t version, uint32_t id) { - struct wl_client *client = wl_resource_get_client(surface->resource); - - struct wlr_subsurface *subsurface = - calloc(1, sizeof(struct wlr_subsurface)); - if (!subsurface) { - wl_client_post_no_memory(client); - return NULL; - } - subsurface->synchronized = true; - subsurface->surface = surface; - subsurface->resource = - wl_resource_create(client, &wl_subsurface_interface, version, id); - if (subsurface->resource == NULL) { - free(subsurface); - wl_client_post_no_memory(client); - return NULL; - } - wl_resource_set_implementation(subsurface->resource, - &subsurface_implementation, subsurface, subsurface_resource_destroy); - - wl_signal_init(&subsurface->events.destroy); - wl_signal_init(&subsurface->events.map); - wl_signal_init(&subsurface->events.unmap); - - wl_signal_add(&surface->events.destroy, &subsurface->surface_destroy); - subsurface->surface_destroy.notify = subsurface_handle_surface_destroy; - - // link parent - subsurface->parent = parent; - wl_signal_add(&parent->events.destroy, &subsurface->parent_destroy); - subsurface->parent_destroy.notify = subsurface_handle_parent_destroy; - wl_list_insert(parent->current.subsurfaces_above.prev, &subsurface->current.link); - wl_list_insert(parent->pending.subsurfaces_above.prev, - &subsurface->pending.link); - - surface->role_data = subsurface; - - wlr_signal_emit_safe(&parent->events.new_subsurface, subsurface); - - return subsurface; -} - - -struct wlr_surface *wlr_surface_get_root_surface(struct wlr_surface *surface) { - while (wlr_surface_is_subsurface(surface)) { - struct wlr_subsurface *subsurface = - wlr_subsurface_from_wlr_surface(surface); - if (subsurface == NULL) { - break; - } - if (subsurface->parent == NULL) { - return NULL; - } - surface = subsurface->parent; - } - return surface; -} - -bool wlr_surface_point_accepts_input(struct wlr_surface *surface, - double sx, double sy) { - return sx >= 0 && sx < surface->current.width && - sy >= 0 && sy < surface->current.height && - pixman_region32_contains_point(&surface->current.input, floor(sx), floor(sy), NULL); -} - -struct wlr_surface *wlr_surface_surface_at(struct wlr_surface *surface, - double sx, double sy, double *sub_x, double *sub_y) { - struct wlr_subsurface *subsurface; - wl_list_for_each_reverse(subsurface, &surface->current.subsurfaces_above, - current.link) { - if (!subsurface->mapped) { - continue; - } - - double _sub_x = subsurface->current.x; - double _sub_y = subsurface->current.y; - struct wlr_surface *sub = wlr_surface_surface_at(subsurface->surface, - sx - _sub_x, sy - _sub_y, sub_x, sub_y); - if (sub != NULL) { - return sub; - } - } - - if (wlr_surface_point_accepts_input(surface, sx, sy)) { - if (sub_x) { - *sub_x = sx; - } - if (sub_y) { - *sub_y = sy; - } - return surface; - } - - wl_list_for_each_reverse(subsurface, &surface->current.subsurfaces_below, - current.link) { - if (!subsurface->mapped) { - continue; - } - - double _sub_x = subsurface->current.x; - double _sub_y = subsurface->current.y; - struct wlr_surface *sub = wlr_surface_surface_at(subsurface->surface, - sx - _sub_x, sy - _sub_y, sub_x, sub_y); - if (sub != NULL) { - return sub; - } - } - - 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); - } - } -} - -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_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; - } - } -} - -void wlr_surface_send_frame_done(struct wlr_surface *surface, - const struct timespec *when) { - struct wl_resource *resource, *tmp; - wl_resource_for_each_safe(resource, tmp, - &surface->current.frame_callback_list) { - wl_callback_send_done(resource, timespec_to_msec(when)); - wl_resource_destroy(resource); - } -} - -static void surface_for_each_surface(struct wlr_surface *surface, int x, int y, - wlr_surface_iterator_func_t iterator, void *user_data) { - struct wlr_subsurface *subsurface; - wl_list_for_each(subsurface, &surface->current.subsurfaces_below, current.link) { - if (!subsurface->mapped) { - continue; - } - - struct wlr_subsurface_parent_state *state = &subsurface->current; - int sx = state->x; - int sy = state->y; - - surface_for_each_surface(subsurface->surface, x + sx, y + sy, - iterator, user_data); - } - - iterator(surface, x, y, user_data); - - wl_list_for_each(subsurface, &surface->current.subsurfaces_above, current.link) { - if (!subsurface->mapped) { - continue; - } - - struct wlr_subsurface_parent_state *state = &subsurface->current; - int sx = state->x; - int sy = state->y; - - surface_for_each_surface(subsurface->surface, x + sx, y + sy, - iterator, user_data); - } -} - -void wlr_surface_for_each_surface(struct wlr_surface *surface, - wlr_surface_iterator_func_t iterator, void *user_data) { - surface_for_each_surface(surface, 0, 0, iterator, user_data); -} - -struct bound_acc { - int32_t min_x, min_y; - int32_t max_x, max_y; -}; - -static void handle_bounding_box_surface(struct wlr_surface *surface, - int x, int y, void *data) { - struct bound_acc *acc = data; - - acc->min_x = min(x, acc->min_x); - acc->min_y = min(y, acc->min_y); - - acc->max_x = max(x + surface->current.width, acc->max_x); - acc->max_y = max(y + surface->current.height, acc->max_y); -} - -void wlr_surface_get_extends(struct wlr_surface *surface, struct wlr_box *box) { - struct bound_acc acc = { - .min_x = 0, - .min_y = 0, - .max_x = surface->current.width, - .max_y = surface->current.height, - }; - - wlr_surface_for_each_surface(surface, handle_bounding_box_surface, &acc); - - box->x = acc.min_x; - box->y = acc.min_y; - box->width = acc.max_x - acc.min_x; - box->height = acc.max_y - acc.min_y; -} - -static void crop_region(pixman_region32_t *dst, pixman_region32_t *src, - const struct wlr_box *box) { - pixman_region32_intersect_rect(dst, src, - box->x, box->y, box->width, box->height); - pixman_region32_translate(dst, -box->x, -box->y); -} - -void wlr_surface_get_effective_damage(struct wlr_surface *surface, - pixman_region32_t *damage) { - pixman_region32_clear(damage); - - // Transform and copy the buffer damage in terms of surface coordinates. - wlr_region_transform(damage, &surface->buffer_damage, - surface->current.transform, surface->current.buffer_width, - surface->current.buffer_height); - wlr_region_scale(damage, damage, 1.0 / (float)surface->current.scale); - - if (surface->current.viewport.has_src) { - struct wlr_box src_box = { - .x = floor(surface->current.viewport.src.x), - .y = floor(surface->current.viewport.src.y), - .width = ceil(surface->current.viewport.src.width), - .height = ceil(surface->current.viewport.src.height), - }; - crop_region(damage, damage, &src_box); - } - if (surface->current.viewport.has_dst) { - int src_width, src_height; - surface_state_viewport_src_size(&surface->current, - &src_width, &src_height); - float scale_x = (float)surface->current.viewport.dst_width / src_width; - float scale_y = (float)surface->current.viewport.dst_height / src_height; - wlr_region_scale_xy(damage, damage, scale_x, scale_y); - } - - // On resize, damage the previous bounds of the surface. The current bounds - // have already been damaged in surface_update_damage. - if (surface->previous.width > surface->current.width || - surface->previous.height > surface->current.height) { - pixman_region32_union_rect(damage, damage, 0, 0, - surface->previous.width, surface->previous.height); - } - - // On move, damage where the surface was with its old dimensions. - if (surface->current.dx != 0 || surface->current.dy != 0) { - int prev_x = -surface->current.dx; - int prev_y = -surface->current.dy; - if ((surface->previous.transform & WL_OUTPUT_TRANSFORM_90) != 0) { - int temp = prev_x; - prev_x = prev_y; - prev_y = temp; - } - pixman_region32_union_rect(damage, damage, prev_x, prev_y, - surface->previous.width, surface->previous.height); - } -} - -void wlr_surface_get_buffer_source_box(struct wlr_surface *surface, - struct wlr_fbox *box) { - box->x = box->y = 0; - box->width = surface->current.buffer_width; - box->height = surface->current.buffer_height; - - if (surface->current.viewport.has_src) { - box->x = surface->current.viewport.src.x * surface->current.scale; - box->y = surface->current.viewport.src.y * surface->current.scale; - box->width = surface->current.viewport.src.width * surface->current.scale; - box->height = surface->current.viewport.src.height * surface->current.scale; - wlr_fbox_transform(box, box, surface->current.transform, - surface->current.buffer_width, surface->current.buffer_height); - } -} diff --git a/types/wlr_switch.c b/types/wlr_switch.c index 248997a06..42611da96 100644 --- a/types/wlr_switch.c +++ b/types/wlr_switch.c @@ -5,7 +5,10 @@ #include void wlr_switch_init(struct wlr_switch *switch_device, - struct wlr_switch_impl *impl) { + const struct wlr_switch_impl *impl, const char *name) { + wlr_input_device_init(&switch_device->base, WLR_INPUT_DEVICE_SWITCH, name); + switch_device->base.switch_device = switch_device; + switch_device->impl = impl; wl_signal_init(&switch_device->events.toggle); } @@ -14,6 +17,7 @@ void wlr_switch_destroy(struct wlr_switch *switch_device) { if (!switch_device) { return; } + wlr_input_device_finish(&switch_device->base); if (switch_device->impl && switch_device->impl->destroy) { switch_device->impl->destroy(switch_device); } else { diff --git a/types/wlr_tablet_pad.c b/types/wlr_tablet_pad.c index 2bd996a66..663ccd39e 100644 --- a/types/wlr_tablet_pad.c +++ b/types/wlr_tablet_pad.c @@ -5,7 +5,10 @@ #include void wlr_tablet_pad_init(struct wlr_tablet_pad *pad, - struct wlr_tablet_pad_impl *impl) { + const struct wlr_tablet_pad_impl *impl, const char *name) { + wlr_input_device_init(&pad->base, WLR_INPUT_DEVICE_TABLET_PAD, name); + pad->base.tablet_pad = pad; + pad->impl = impl; wl_signal_init(&pad->events.button); wl_signal_init(&pad->events.ring); @@ -27,6 +30,7 @@ void wlr_tablet_pad_destroy(struct wlr_tablet_pad *pad) { } wl_array_release(&pad->paths); + wlr_input_device_finish(&pad->base); if (pad->impl && pad->impl->destroy) { pad->impl->destroy(pad); } else { diff --git a/types/wlr_tablet_tool.c b/types/wlr_tablet_tool.c index 5c0037bd2..e50d29d1a 100644 --- a/types/wlr_tablet_tool.c +++ b/types/wlr_tablet_tool.c @@ -5,7 +5,10 @@ #include void wlr_tablet_init(struct wlr_tablet *tablet, - const struct wlr_tablet_impl *impl) { + const struct wlr_tablet_impl *impl, const char *name) { + wlr_input_device_init(&tablet->base, WLR_INPUT_DEVICE_TABLET_TOOL, name); + tablet->base.tablet = tablet; + tablet->impl = impl; wl_signal_init(&tablet->events.axis); wl_signal_init(&tablet->events.proximity); @@ -25,6 +28,7 @@ void wlr_tablet_destroy(struct wlr_tablet *tablet) { } wl_array_release(&tablet->paths); + wlr_input_device_finish(&tablet->base); if (tablet->impl && tablet->impl->destroy) { tablet->impl->destroy(tablet); } else { diff --git a/types/wlr_text_input_v3.c b/types/wlr_text_input_v3.c index f5267b359..65a8ebd6f 100644 --- a/types/wlr_text_input_v3.c +++ b/types/wlr_text_input_v3.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include "text-input-unstable-v3-protocol.h" diff --git a/types/wlr_touch.c b/types/wlr_touch.c index 9edfdaeee..366191101 100644 --- a/types/wlr_touch.c +++ b/types/wlr_touch.c @@ -5,7 +5,10 @@ #include void wlr_touch_init(struct wlr_touch *touch, - const struct wlr_touch_impl *impl) { + const struct wlr_touch_impl *impl, const char *name) { + wlr_input_device_init(&touch->base, WLR_INPUT_DEVICE_TOUCH, name); + touch->base.touch = touch; + touch->impl = impl; wl_signal_init(&touch->events.down); wl_signal_init(&touch->events.up); @@ -15,6 +18,7 @@ void wlr_touch_init(struct wlr_touch *touch, } void wlr_touch_destroy(struct wlr_touch *touch) { + wlr_input_device_finish(&touch->base); if (touch && touch->impl && touch->impl->destroy) { touch->impl->destroy(touch); } else { diff --git a/types/wlr_viewporter.c b/types/wlr_viewporter.c index f07c18feb..097af8576 100644 --- a/types/wlr_viewporter.c +++ b/types/wlr_viewporter.c @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include #include "util/signal.h" diff --git a/types/wlr_virtual_keyboard_v1.c b/types/wlr_virtual_keyboard_v1.c index db643ef34..07eeb3678 100644 --- a/types/wlr_virtual_keyboard_v1.c +++ b/types/wlr_virtual_keyboard_v1.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -11,26 +12,40 @@ #include "util/time.h" #include "virtual-keyboard-unstable-v1-protocol.h" - -static void keyboard_led_update(struct wlr_keyboard *wlr_kb, uint32_t leds) { - // unsupported by virtual keyboard protocol +/** + * Send release event for each pressed key to bring the keyboard back to + * neutral state. + * + * This may be needed for virtual keyboards. For physical devices, kernel + * or libinput will deal with the removal of devices. + */ +static void keyboard_release_pressed_keys(struct wlr_keyboard *keyboard) { + size_t orig_num_keycodes = keyboard->num_keycodes; + for (size_t i = 0; i < orig_num_keycodes; ++i) { + assert(keyboard->num_keycodes == orig_num_keycodes - i); + struct wlr_event_keyboard_key event = { + .time_msec = get_current_time_msec(), + .keycode = keyboard->keycodes[orig_num_keycodes - i - 1], + .update_state = false, + .state = WL_KEYBOARD_KEY_STATE_RELEASED, + }; + wlr_keyboard_notify_key(keyboard, &event); // updates num_keycodes + } } static void keyboard_destroy(struct wlr_keyboard *wlr_kb) { - // safe to ignore - keyboard will be destroyed only iff associated virtual - // keyboard is torn down, no need to tear down the keyboard separately + struct wlr_virtual_keyboard_v1 *keyboard = + (struct wlr_virtual_keyboard_v1 *)wlr_kb; + + keyboard_release_pressed_keys(&keyboard->keyboard); + wl_resource_set_user_data(keyboard->resource, NULL); + wlr_signal_emit_safe(&keyboard->events.destroy, keyboard); + wl_list_remove(&keyboard->link); + free(keyboard); } static const struct wlr_keyboard_impl keyboard_impl = { .destroy = keyboard_destroy, - .led_update = keyboard_led_update -}; - -static void input_device_destroy(struct wlr_input_device *dev) { -} - -static const struct wlr_input_device_impl input_device_impl = { - .destroy = input_device_destroy }; static const struct zwp_virtual_keyboard_v1_interface virtual_keyboard_impl; @@ -44,10 +59,11 @@ static struct wlr_virtual_keyboard_v1 *virtual_keyboard_from_resource( struct wlr_virtual_keyboard_v1 *wlr_input_device_get_virtual_keyboard( struct wlr_input_device *wlr_dev) { - if (wlr_dev->impl != &input_device_impl) { + if (wlr_dev->type != WLR_INPUT_DEVICE_KEYBOARD + || wlr_dev->keyboard->impl != &keyboard_impl) { return NULL; } - return (struct wlr_virtual_keyboard_v1 *)wlr_dev; + return (struct wlr_virtual_keyboard_v1 *)wlr_dev->keyboard; } static void virtual_keyboard_keymap(struct wl_client *client, @@ -70,7 +86,7 @@ static void virtual_keyboard_keymap(struct wl_client *client, if (!keymap) { goto keymap_fail; } - wlr_keyboard_set_keymap(keyboard->input_device.keyboard, keymap); + wlr_keyboard_set_keymap(&keyboard->keyboard, keymap); keyboard->has_keymap = true; xkb_keymap_unref(keymap); xkb_context_unref(context); @@ -101,7 +117,7 @@ static void virtual_keyboard_key(struct wl_client *client, .update_state = false, .state = state, }; - wlr_keyboard_notify_key(keyboard->input_device.keyboard, &event); + wlr_keyboard_notify_key(&keyboard->keyboard, &event); } static void virtual_keyboard_modifiers(struct wl_client *client, @@ -115,39 +131,16 @@ static void virtual_keyboard_modifiers(struct wl_client *client, "Cannot send a modifier state before defining a keymap"); return; } - wlr_keyboard_notify_modifiers(keyboard->input_device.keyboard, + wlr_keyboard_notify_modifiers(&keyboard->keyboard, mods_depressed, mods_latched, mods_locked, group); } -/** - * Send release event for each pressed key to bring the keyboard back to - * neutral state. - * - * This may be needed for virtual keyboards. For physical devices, kernel - * or libinput will deal with the removal of devices. - */ -static void keyboard_release_pressed_keys(struct wlr_keyboard *keyboard) { - size_t orig_num_keycodes = keyboard->num_keycodes; - for (size_t i = 0; i < orig_num_keycodes; ++i) { - assert(keyboard->num_keycodes == orig_num_keycodes - i); - struct wlr_event_keyboard_key event = { - .time_msec = get_current_time_msec(), - .keycode = keyboard->keycodes[orig_num_keycodes - i - 1], - .update_state = false, - .state = WL_KEYBOARD_KEY_STATE_RELEASED, - }; - wlr_keyboard_notify_key(keyboard, &event); // updates num_keycodes - } -} - static void virtual_keyboard_destroy_resource(struct wl_resource *resource) { struct wlr_virtual_keyboard_v1 *keyboard = virtual_keyboard_from_resource(resource); - keyboard_release_pressed_keys(keyboard->input_device.keyboard); - wlr_signal_emit_safe(&keyboard->events.destroy, keyboard); - wl_list_remove(&keyboard->link); - wlr_input_device_destroy(&keyboard->input_device); - free(keyboard); + if (keyboard != NULL) { + wlr_keyboard_destroy(&keyboard->keyboard); + } } static void virtual_keyboard_destroy(struct wl_client *client, @@ -184,20 +177,13 @@ static void virtual_keyboard_manager_create_virtual_keyboard( return; } - struct wlr_keyboard* keyboard = calloc(1, sizeof(struct wlr_keyboard)); - if (!keyboard) { - wlr_log(WLR_ERROR, "Cannot allocate wlr_keyboard"); - free(virtual_keyboard); - wl_client_post_no_memory(client); - return; - } - wlr_keyboard_init(keyboard, &keyboard_impl); + wlr_keyboard_init(&virtual_keyboard->keyboard, &keyboard_impl, + "virtual-keyboard"); struct wl_resource *keyboard_resource = wl_resource_create(client, &zwp_virtual_keyboard_v1_interface, wl_resource_get_version(resource), id); if (!keyboard_resource) { - free(keyboard); free(virtual_keyboard); wl_client_post_no_memory(client); return; @@ -206,13 +192,8 @@ static void virtual_keyboard_manager_create_virtual_keyboard( wl_resource_set_implementation(keyboard_resource, &virtual_keyboard_impl, virtual_keyboard, virtual_keyboard_destroy_resource); - wlr_input_device_init(&virtual_keyboard->input_device, - WLR_INPUT_DEVICE_KEYBOARD, &input_device_impl, "virtual keyboard", - 0x0, 0x0); - struct wlr_seat_client *seat_client = wlr_seat_client_from_resource(seat); - virtual_keyboard->input_device.keyboard = keyboard; virtual_keyboard->resource = keyboard_resource; virtual_keyboard->seat = seat_client->seat; wl_signal_init(&virtual_keyboard->events.destroy); diff --git a/types/wlr_virtual_pointer_v1.c b/types/wlr_virtual_pointer_v1.c index a9d1fd7bc..6215bbfff 100644 --- a/types/wlr_virtual_pointer_v1.c +++ b/types/wlr_virtual_pointer_v1.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -7,17 +8,18 @@ #include "util/signal.h" #include "wlr-virtual-pointer-unstable-v1-protocol.h" -static void input_device_destroy(struct wlr_input_device *dev) { - struct wlr_virtual_pointer_v1 *pointer = - (struct wlr_virtual_pointer_v1 *)dev; - wl_resource_set_user_data(pointer->resource, NULL); - wlr_signal_emit_safe(&pointer->events.destroy, pointer); - wl_list_remove(&pointer->link); - free(pointer); +static void pointer_destroy(struct wlr_pointer *pointer) { + struct wlr_virtual_pointer_v1 *virtual_pointer = + (struct wlr_virtual_pointer_v1 *)pointer; + + wl_resource_set_user_data(virtual_pointer->resource, NULL); + wlr_signal_emit_safe(&virtual_pointer->events.destroy, virtual_pointer); + wl_list_remove(&virtual_pointer->link); + free(virtual_pointer); } -static const struct wlr_input_device_impl input_device_impl = { - .destroy = input_device_destroy +static const struct wlr_pointer_impl pointer_impl = { + .destroy = pointer_destroy, }; static const struct zwlr_virtual_pointer_v1_interface virtual_pointer_impl; @@ -37,7 +39,7 @@ static void virtual_pointer_motion(struct wl_client *client, if (pointer == NULL) { return; } - struct wlr_input_device *wlr_dev = &pointer->input_device; + struct wlr_input_device *wlr_dev = &pointer->pointer.base; struct wlr_event_pointer_motion event = { .device = wlr_dev, .time_msec = time, @@ -46,7 +48,7 @@ static void virtual_pointer_motion(struct wl_client *client, .unaccel_dx = wl_fixed_to_double(dx), .unaccel_dy = wl_fixed_to_double(dy), }; - wlr_signal_emit_safe(&wlr_dev->pointer->events.motion, &event); + wlr_signal_emit_safe(&pointer->pointer.events.motion, &event); } static void virtual_pointer_motion_absolute(struct wl_client *client, @@ -60,7 +62,7 @@ static void virtual_pointer_motion_absolute(struct wl_client *client, if (x_extent == 0 || y_extent == 0) { return; } - struct wlr_input_device *wlr_dev = &pointer->input_device; + struct wlr_input_device *wlr_dev = &pointer->pointer.base; struct wlr_event_pointer_motion_absolute event = { .device = wlr_dev, .time_msec = time, @@ -78,7 +80,7 @@ static void virtual_pointer_button(struct wl_client *client, if (pointer == NULL) { return; } - struct wlr_input_device *wlr_dev = &pointer->input_device; + struct wlr_input_device *wlr_dev = &pointer->pointer.base; struct wlr_event_pointer_button event = { .device = wlr_dev, .time_msec = time, @@ -102,7 +104,7 @@ static void virtual_pointer_axis(struct wl_client *client, if (pointer == NULL) { return; } - struct wlr_input_device *wlr_dev = &pointer->input_device; + struct wlr_input_device *wlr_dev = &pointer->pointer.base; pointer->axis = axis; pointer->axis_valid[pointer->axis] = true; pointer->axis_event[pointer->axis].device = wlr_dev; @@ -118,7 +120,7 @@ static void virtual_pointer_frame(struct wl_client *client, if (pointer == NULL) { return; } - struct wlr_input_device *wlr_dev = &pointer->input_device; + struct wlr_input_device *wlr_dev = &pointer->pointer.base; for (size_t i = 0; i < sizeof(pointer->axis_valid) / sizeof(pointer->axis_valid[0]); @@ -148,7 +150,7 @@ static void virtual_pointer_axis_source(struct wl_client *client, if (pointer == NULL) { return; } - struct wlr_input_device *wlr_dev = &pointer->input_device; + struct wlr_input_device *wlr_dev = &pointer->pointer.base; pointer->axis_event[pointer->axis].device = wlr_dev; pointer->axis_event[pointer->axis].source = source; } @@ -166,7 +168,7 @@ static void virtual_pointer_axis_stop(struct wl_client *client, if (pointer == NULL) { return; } - struct wlr_input_device *wlr_dev = &pointer->input_device; + struct wlr_input_device *wlr_dev = &pointer->pointer.base; pointer->axis = axis; pointer->axis_valid[pointer->axis] = true; pointer->axis_event[pointer->axis].device = wlr_dev; @@ -190,7 +192,7 @@ static void virtual_pointer_axis_discrete(struct wl_client *client, if (pointer == NULL) { return; } - struct wlr_input_device *wlr_dev = &pointer->input_device; + struct wlr_input_device *wlr_dev = &pointer->pointer.base; pointer->axis = axis; pointer->axis_valid[pointer->axis] = true; pointer->axis_event[pointer->axis].device = wlr_dev; @@ -204,7 +206,7 @@ static void virtual_pointer_destroy_resource(struct wl_resource *resource) { struct wlr_virtual_pointer_v1 *pointer = virtual_pointer_from_resource(resource); if (pointer != NULL) { - wlr_input_device_destroy(&pointer->input_device); + wlr_pointer_destroy(&pointer->pointer); } } @@ -247,20 +249,13 @@ static void virtual_pointer_manager_create_virtual_pointer_with_output( return; } - struct wlr_pointer *pointer = calloc(1, sizeof(struct wlr_pointer)); - if (!pointer) { - wlr_log(WLR_ERROR, "Cannot allocate wlr_pointer"); - free(virtual_pointer); - wl_client_post_no_memory(client); - return; - } - wlr_pointer_init(pointer, NULL); + wlr_pointer_init(&virtual_pointer->pointer, &pointer_impl, + "virtual-pointer"); struct wl_resource *pointer_resource = wl_resource_create(client, &zwlr_virtual_pointer_v1_interface, wl_resource_get_version(resource), id); if (!pointer_resource) { - free(pointer); free(virtual_pointer); wl_client_post_no_memory(client); return; @@ -269,10 +264,6 @@ static void virtual_pointer_manager_create_virtual_pointer_with_output( wl_resource_set_implementation(pointer_resource, &virtual_pointer_impl, virtual_pointer, virtual_pointer_destroy_resource); - wlr_input_device_init(&virtual_pointer->input_device, - WLR_INPUT_DEVICE_POINTER, &input_device_impl, "virtual pointer", - 0x0, 0x0); - struct wlr_virtual_pointer_v1_new_pointer_event event = { .new_pointer = virtual_pointer, }; @@ -288,7 +279,6 @@ static void virtual_pointer_manager_create_virtual_pointer_with_output( event.suggested_output = wlr_output; } - virtual_pointer->input_device.pointer = pointer; virtual_pointer->resource = pointer_resource; wl_signal_init(&virtual_pointer->events.destroy); diff --git a/types/wlr_xdg_activation_v1.c b/types/wlr_xdg_activation_v1.c index 64864d882..02ba9e07f 100644 --- a/types/wlr_xdg_activation_v1.c +++ b/types/wlr_xdg_activation_v1.c @@ -2,8 +2,8 @@ #include #include #include +#include #include -#include #include #include #include "util/signal.h" @@ -407,3 +407,23 @@ const char *wlr_xdg_activation_token_v1_get_name( struct wlr_xdg_activation_token_v1 *token) { return token->token; } + +struct wlr_xdg_activation_token_v1 *wlr_xdg_activation_v1_add_token( + struct wlr_xdg_activation_v1 *activation, const char *token_str) { + assert(token_str); + + struct wlr_xdg_activation_token_v1 *token = calloc(1, sizeof(*token)); + if (token == NULL) { + return NULL; + } + wl_list_init(&token->link); + wl_list_init(&token->seat_destroy.link); + wl_list_init(&token->surface_destroy.link); + + token->activation = activation; + token->token = strdup(token_str); + + wl_list_insert(&activation->tokens, &token->link); + + return token; +} diff --git a/types/wlr_xdg_decoration_v1.c b/types/wlr_xdg_decoration_v1.c index b6dd003ef..d0f969f45 100644 --- a/types/wlr_xdg_decoration_v1.c +++ b/types/wlr_xdg_decoration_v1.c @@ -173,11 +173,10 @@ static void decoration_manager_handle_get_toplevel_decoration( uint32_t id, struct wl_resource *toplevel_resource) { struct wlr_xdg_decoration_manager_v1 *manager = decoration_manager_from_resource(manager_resource); - struct wlr_xdg_surface *surface = - wlr_xdg_surface_from_toplevel_resource(toplevel_resource); - assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); + struct wlr_xdg_toplevel *toplevel = + wlr_xdg_toplevel_from_resource(toplevel_resource); - if (wlr_surface_has_buffer(surface->surface)) { + if (wlr_surface_has_buffer(toplevel->base->surface)) { wl_resource_post_error(manager_resource, ZXDG_TOPLEVEL_DECORATION_V1_ERROR_UNCONFIGURED_BUFFER, "xdg_toplevel_decoration must not have a buffer at creation"); @@ -191,7 +190,7 @@ static void decoration_manager_handle_get_toplevel_decoration( return; } decoration->manager = manager; - decoration->surface = surface; + decoration->surface = toplevel->base; uint32_t version = wl_resource_get_version(manager_resource); decoration->resource = wl_resource_create(client, @@ -212,24 +211,26 @@ static void decoration_manager_handle_get_toplevel_decoration( wl_signal_init(&decoration->events.destroy); wl_signal_init(&decoration->events.request_mode); - wl_signal_add(&surface->events.destroy, &decoration->surface_destroy); + wl_signal_add(&toplevel->base->events.destroy, + &decoration->surface_destroy); decoration->surface_destroy.notify = toplevel_decoration_handle_surface_destroy; - wl_signal_add(&surface->events.configure, &decoration->surface_configure); + wl_signal_add(&toplevel->base->events.configure, + &decoration->surface_configure); decoration->surface_configure.notify = toplevel_decoration_handle_surface_configure; - wl_signal_add(&surface->events.ack_configure, + wl_signal_add(&toplevel->base->events.ack_configure, &decoration->surface_ack_configure); decoration->surface_ack_configure.notify = toplevel_decoration_handle_surface_ack_configure; - wl_signal_add(&surface->surface->events.commit, + wl_signal_add(&toplevel->base->surface->events.commit, &decoration->surface_commit); decoration->surface_commit.notify = toplevel_decoration_handle_surface_commit; wl_list_insert(&manager->decorations, &decoration->link); - if (surface->added) { + if (toplevel->base->added) { decoration->added = true; wlr_signal_emit_safe(&manager->events.new_toplevel_decoration, decoration); diff --git a/types/wlr_xdg_foreign_v1.c b/types/wlr_xdg_foreign_v1.c index 49216e684..c07959aaf 100644 --- a/types/wlr_xdg_foreign_v1.c +++ b/types/wlr_xdg_foreign_v1.c @@ -33,7 +33,7 @@ static bool verify_is_toplevel(struct wl_resource *client_resource, if (wlr_surface_is_xdg_surface(surface)) { struct wlr_xdg_surface *xdg_surface = wlr_xdg_surface_from_wlr_surface(surface); - if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) { + if (xdg_surface == NULL || xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) { wl_resource_post_error(client_resource, -1, "surface must be an xdg_toplevel"); return false; @@ -103,7 +103,7 @@ static void xdg_imported_handle_set_parent_of(struct wl_client *client, struct wlr_xdg_surface *surface_child = wlr_xdg_surface_from_wlr_surface(wlr_surface_child); - wlr_xdg_toplevel_set_parent(surface_child, surface); + wlr_xdg_toplevel_set_parent(surface_child->toplevel, surface->toplevel); wl_signal_add(&surface_child->events.unmap, &child->xdg_surface_unmap); wl_signal_add(&surface_child->toplevel->events.set_parent, @@ -151,7 +151,10 @@ static void destroy_imported(struct wlr_xdg_imported_v1 *imported) { wl_list_for_each_safe(child, child_tmp, &imported->children, link) { struct wlr_xdg_surface *xdg_child = wlr_xdg_surface_from_wlr_surface(child->surface); - wlr_xdg_toplevel_set_parent(xdg_child, NULL); + + if (xdg_child != NULL) { + wlr_xdg_toplevel_set_parent(xdg_child->toplevel, NULL); + } } wl_list_remove(&imported->exported_destroyed.link); diff --git a/types/wlr_xdg_foreign_v2.c b/types/wlr_xdg_foreign_v2.c index c9ef54966..abd997352 100644 --- a/types/wlr_xdg_foreign_v2.c +++ b/types/wlr_xdg_foreign_v2.c @@ -42,7 +42,7 @@ static bool verify_is_toplevel(struct wl_resource *client_resource, struct wlr_xdg_surface *xdg_surface = wlr_xdg_surface_from_wlr_surface(surface); - if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) { + if (xdg_surface == NULL || xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) { wl_resource_post_error(client_resource, ZXDG_EXPORTER_V2_ERROR_INVALID_SURFACE, "surface must be an xdg_toplevel"); @@ -109,7 +109,7 @@ static void xdg_imported_handle_set_parent_of(struct wl_client *client, struct wlr_xdg_surface *surface_child = wlr_xdg_surface_from_wlr_surface(wlr_surface_child); - wlr_xdg_toplevel_set_parent(surface_child, surface); + wlr_xdg_toplevel_set_parent(surface_child->toplevel, surface->toplevel); wl_signal_add(&surface_child->events.unmap, &child->xdg_surface_unmap); wl_signal_add(&surface_child->toplevel->events.set_parent, @@ -157,7 +157,10 @@ static void destroy_imported(struct wlr_xdg_imported_v2 *imported) { wl_list_for_each_safe(child, child_tmp, &imported->children, link) { struct wlr_xdg_surface *xdg_child = wlr_xdg_surface_from_wlr_surface(child->surface); - wlr_xdg_toplevel_set_parent(xdg_child, NULL); + + if (xdg_child != NULL) { + wlr_xdg_toplevel_set_parent(xdg_child->toplevel, NULL); + } } wl_list_remove(&imported->exported_destroyed.link); diff --git a/types/wlr_xdg_output_v1.c b/types/wlr_xdg_output_v1.c index 819ea6c63..8d5fee485 100644 --- a/types/wlr_xdg_output_v1.c +++ b/types/wlr_xdg_output_v1.c @@ -253,12 +253,6 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) { struct wlr_xdg_output_manager_v1 *wlr_xdg_output_manager_v1_create( struct wl_display *display, struct wlr_output_layout *layout) { - // TODO: require wayland-protocols 1.18 and remove this condition - int version = OUTPUT_MANAGER_VERSION; - if (version > zxdg_output_manager_v1_interface.version) { - version = zxdg_output_manager_v1_interface.version; - } - struct wlr_xdg_output_manager_v1 *manager = calloc(1, sizeof(struct wlr_xdg_output_manager_v1)); if (manager == NULL) { @@ -266,7 +260,7 @@ struct wlr_xdg_output_manager_v1 *wlr_xdg_output_manager_v1_create( } manager->layout = layout; manager->global = wl_global_create(display, - &zxdg_output_manager_v1_interface, version, manager, + &zxdg_output_manager_v1_interface, OUTPUT_MANAGER_VERSION, manager, output_manager_bind); if (!manager->global) { free(manager); diff --git a/types/xdg_shell/wlr_xdg_popup.c b/types/xdg_shell/wlr_xdg_popup.c index 3718ef749..973a339be 100644 --- a/types/xdg_shell/wlr_xdg_popup.c +++ b/types/xdg_shell/wlr_xdg_popup.c @@ -148,15 +148,15 @@ static const struct wlr_touch_grab_interface xdg_touch_grab_impl = { .cancel = xdg_touch_grab_cancel }; -static void xdg_popup_grab_handle_seat_destroy( - struct wl_listener *listener, void *data) { - struct wlr_xdg_popup_grab *xdg_grab = - wl_container_of(listener, xdg_grab, seat_destroy); +static void destroy_xdg_popup_grab(struct wlr_xdg_popup_grab *xdg_grab) { + if (xdg_grab == NULL) { + return; + } wl_list_remove(&xdg_grab->seat_destroy.link); - struct wlr_xdg_popup *popup, *next; - wl_list_for_each_safe(popup, next, &xdg_grab->popups, grab_link) { + struct wlr_xdg_popup *popup, *tmp; + wl_list_for_each_safe(popup, tmp, &xdg_grab->popups, grab_link) { destroy_xdg_surface(popup->base); } @@ -164,7 +164,14 @@ static void xdg_popup_grab_handle_seat_destroy( free(xdg_grab); } -struct wlr_xdg_popup_grab *get_xdg_shell_popup_grab_from_seat( +static void xdg_popup_grab_handle_seat_destroy( + struct wl_listener *listener, void *data) { + struct wlr_xdg_popup_grab *xdg_grab = + wl_container_of(listener, xdg_grab, seat_destroy); + destroy_xdg_popup_grab(xdg_grab); +} + +static struct wlr_xdg_popup_grab *get_xdg_shell_popup_grab_from_seat( struct wlr_xdg_shell *shell, struct wlr_seat *seat) { struct wlr_xdg_popup_grab *xdg_grab; wl_list_for_each(xdg_grab, &shell->popup_grabs, link) { @@ -196,25 +203,23 @@ struct wlr_xdg_popup_grab *get_xdg_shell_popup_grab_from_seat( return xdg_grab; } -void handle_xdg_surface_popup_committed(struct wlr_xdg_surface *surface) { - assert(surface->role == WLR_XDG_SURFACE_ROLE_POPUP); - - if (!surface->popup->parent) { - wl_resource_post_error(surface->resource, +void handle_xdg_popup_committed(struct wlr_xdg_popup *popup) { + if (!popup->parent) { + wl_resource_post_error(popup->base->resource, XDG_SURFACE_ERROR_NOT_CONSTRUCTED, "xdg_popup has no parent"); return; } - if (!surface->popup->committed) { - wlr_xdg_surface_schedule_configure(surface); - surface->popup->committed = true; + if (!popup->committed) { + wlr_xdg_surface_schedule_configure(popup->base); + popup->committed = true; } } static const struct xdg_popup_interface xdg_popup_implementation; -struct wlr_xdg_surface *wlr_xdg_surface_from_popup_resource( +struct wlr_xdg_popup *wlr_xdg_popup_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &xdg_popup_interface, &xdg_popup_implementation)); @@ -224,35 +229,35 @@ struct wlr_xdg_surface *wlr_xdg_surface_from_popup_resource( 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 *surface = - wlr_xdg_surface_from_popup_resource(resource); - struct wlr_seat_client *seat_client = - wlr_seat_client_from_resource(seat_resource); - if (!surface) { + struct wlr_xdg_popup *popup = + wlr_xdg_popup_from_resource(resource); + if (!popup) { return; } - if (surface->popup->committed) { - wl_resource_post_error(surface->popup->resource, + struct wlr_seat_client *seat_client = + wlr_seat_client_from_resource(seat_resource); + if (popup->committed) { + wl_resource_post_error(popup->resource, XDG_POPUP_ERROR_INVALID_GRAB, "xdg_popup is already mapped"); return; } struct wlr_xdg_popup_grab *popup_grab = get_xdg_shell_popup_grab_from_seat( - surface->client->shell, seat_client->seat); + popup->base->client->shell, seat_client->seat); - if (!wl_list_empty(&surface->popups)) { - wl_resource_post_error(surface->client->resource, + if (!wl_list_empty(&popup->base->popups)) { + wl_resource_post_error(popup->base->client->resource, XDG_WM_BASE_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; + popup_grab->client = popup->base->client->client; + popup->seat = seat_client->seat; - wl_list_insert(&popup_grab->popups, &surface->popup->grab_link); + wl_list_insert(&popup_grab->popups, &popup->grab_link); wlr_seat_pointer_start_grab(seat_client->seat, &popup_grab->pointer_grab); @@ -264,11 +269,11 @@ static void xdg_popup_handle_grab(struct wl_client *client, static void xdg_popup_handle_destroy(struct wl_client *client, struct wl_resource *resource) { - struct wlr_xdg_surface *surface = - wlr_xdg_surface_from_popup_resource(resource); + struct wlr_xdg_popup *popup = + wlr_xdg_popup_from_resource(resource); - if (surface && !wl_list_empty(&surface->popups)) { - wl_resource_post_error(surface->client->resource, + if (popup && !wl_list_empty(&popup->base->popups)) { + wl_resource_post_error(popup->base->client->resource, XDG_WM_BASE_ERROR_NOT_THE_TOPMOST_POPUP, "xdg_popup was destroyed while it was not the topmost popup"); return; @@ -283,117 +288,125 @@ static const struct xdg_popup_interface xdg_popup_implementation = { }; static void xdg_popup_handle_resource_destroy(struct wl_resource *resource) { - struct wlr_xdg_surface *xdg_surface = - wlr_xdg_surface_from_popup_resource(resource); - if (xdg_surface == NULL) { + struct wlr_xdg_popup *popup = + wlr_xdg_popup_from_resource(resource); + if (popup == NULL) { return; } - wlr_xdg_popup_destroy(xdg_surface); + wlr_xdg_popup_destroy(popup); } const struct wlr_surface_role xdg_popup_surface_role = { .name = "xdg_popup", - .commit = handle_xdg_surface_commit, - .precommit = handle_xdg_surface_precommit, + .commit = xdg_surface_role_commit, + .precommit = xdg_surface_role_precommit, }; -void create_xdg_popup(struct wlr_xdg_surface *xdg_surface, +void create_xdg_popup(struct wlr_xdg_surface *surface, struct wlr_xdg_surface *parent, - struct wlr_xdg_positioner_resource *positioner, int32_t id) { - if (positioner->attrs.size.width == 0 || - positioner->attrs.anchor_rect.width == 0) { - wl_resource_post_error(xdg_surface->resource, + struct wlr_xdg_positioner *positioner, uint32_t id) { + if (positioner->rules.size.width == 0 || + positioner->rules.anchor_rect.width == 0) { + wl_resource_post_error(surface->client->resource, XDG_WM_BASE_ERROR_INVALID_POSITIONER, "positioner object is not complete"); return; } - if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_NONE) { - wl_resource_post_error(xdg_surface->resource, + if (surface->role != WLR_XDG_SURFACE_ROLE_NONE) { + wl_resource_post_error(surface->resource, XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED, "xdg-surface has already been constructed"); return; } - if (!wlr_surface_set_role(xdg_surface->surface, &xdg_popup_surface_role, - xdg_surface, xdg_surface->resource, XDG_WM_BASE_ERROR_ROLE)) { + if (!wlr_surface_set_role(surface->surface, &xdg_popup_surface_role, + surface, surface->resource, XDG_WM_BASE_ERROR_ROLE)) { return; } - assert(xdg_surface->popup == NULL); - xdg_surface->popup = calloc(1, sizeof(struct wlr_xdg_popup)); - if (!xdg_surface->popup) { - wl_resource_post_no_memory(xdg_surface->resource); + assert(surface->popup == NULL); + surface->popup = calloc(1, sizeof(struct wlr_xdg_popup)); + if (!surface->popup) { + wl_resource_post_no_memory(surface->resource); return; } - xdg_surface->popup->base = xdg_surface; + surface->popup->base = surface; - xdg_surface->popup->resource = wl_resource_create( - xdg_surface->client->client, &xdg_popup_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); + surface->popup->resource = wl_resource_create( + surface->client->client, &xdg_popup_interface, + wl_resource_get_version(surface->resource), id); + if (surface->popup->resource == NULL) { + free(surface->popup); + surface->popup = NULL; + wl_resource_post_no_memory(surface->resource); return; } - wl_resource_set_implementation(xdg_surface->popup->resource, - &xdg_popup_implementation, xdg_surface, + wl_resource_set_implementation(surface->popup->resource, + &xdg_popup_implementation, surface->popup, xdg_popup_handle_resource_destroy); - xdg_surface->role = WLR_XDG_SURFACE_ROLE_POPUP; + surface->role = WLR_XDG_SURFACE_ROLE_POPUP; - // positioner properties - memcpy(&xdg_surface->popup->positioner, &positioner->attrs, - sizeof(struct wlr_xdg_positioner)); - xdg_surface->popup->geometry = - wlr_xdg_positioner_get_geometry(&positioner->attrs); + memcpy(&surface->popup->positioner_rules, + &positioner->rules, sizeof(positioner->rules)); + wlr_xdg_positioner_rules_get_geometry( + &positioner->rules, &surface->popup->geometry); if (parent) { - xdg_surface->popup->parent = parent->surface; - wl_list_insert(&parent->popups, &xdg_surface->popup->link); - wlr_signal_emit_safe(&parent->events.new_popup, xdg_surface->popup); + surface->popup->parent = parent->surface; + wl_list_insert(&parent->popups, &surface->popup->link); + wlr_signal_emit_safe(&parent->events.new_popup, surface->popup); } else { - wl_list_init(&xdg_surface->popup->link); + wl_list_init(&surface->popup->link); } } -void wlr_xdg_popup_get_anchor_point(struct wlr_xdg_popup *popup, - int *root_sx, int *root_sy) { - struct wlr_box rect = popup->positioner.anchor_rect; - enum xdg_positioner_anchor anchor = popup->positioner.anchor; - int sx = 0, sy = 0; +void unmap_xdg_popup(struct wlr_xdg_popup *popup) { + if (popup->seat != NULL) { + struct wlr_xdg_popup_grab *grab = + get_xdg_shell_popup_grab_from_seat( + popup->base->client->shell, popup->seat); - if (anchor == XDG_POSITIONER_ANCHOR_NONE) { - sx = (rect.x + rect.width) / 2; - sy = (rect.y + rect.height) / 2; - } else if (anchor == XDG_POSITIONER_ANCHOR_TOP) { - sx = (rect.x + rect.width) / 2; - sy = rect.y; - } else if (anchor == XDG_POSITIONER_ANCHOR_BOTTOM) { - sx = (rect.x + rect.width) / 2; - sy = rect.y + rect.height; - } else if (anchor == XDG_POSITIONER_ANCHOR_LEFT) { - sx = rect.x; - sy = (rect.y + rect.height) / 2; - } else if (anchor == XDG_POSITIONER_ANCHOR_RIGHT) { - sx = rect.x + rect.width; - sy = (rect.y + rect.height) / 2; - } else if (anchor == XDG_POSITIONER_ANCHOR_TOP_LEFT) { - sx = rect.x; - sy = rect.y; - } else if (anchor == XDG_POSITIONER_ANCHOR_TOP_RIGHT) { - sx = rect.x + rect.width; - sy = rect.y; - } else if (anchor == XDG_POSITIONER_ANCHOR_BOTTOM_LEFT) { - sx = rect.x; - sy = rect.y + rect.height; - } else if (anchor == XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT) { - sx = rect.x + rect.width; - sy = rect.y + rect.height; + wl_list_remove(&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); + } + if (grab->seat->touch_state.grab == &grab->touch_grab) { + wlr_seat_touch_end_grab(grab->seat); + } + + destroy_xdg_popup_grab(grab); + } + + popup->seat = NULL; + } +} + +void destroy_xdg_popup(struct wlr_xdg_popup *popup) { + wl_list_remove(&popup->link); + wl_resource_set_user_data(popup->resource, NULL); + free(popup); +} + +void wlr_xdg_popup_destroy(struct wlr_xdg_popup *popup) { + if (popup == NULL) { + return; } - *root_sx = sx; - *root_sy = sy; + struct wlr_xdg_popup *child, *child_tmp; + wl_list_for_each_safe(child, child_tmp, &popup->base->popups, link) { + wlr_xdg_popup_destroy(child); + } + + xdg_popup_send_popup_done(popup->resource); + wl_resource_set_user_data(popup->resource, NULL); + reset_xdg_surface(popup->base); } void wlr_xdg_popup_get_toplevel_coords(struct wlr_xdg_popup *popup, @@ -419,167 +432,17 @@ void wlr_xdg_popup_get_toplevel_coords(struct wlr_xdg_popup *popup, *toplevel_sy = popup_sy; } -static void xdg_popup_box_constraints(struct wlr_xdg_popup *popup, - const 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_get_anchor_point(popup, &anchor_sx, &anchor_sy); - int popup_sx = 0, popup_sy = 0; - wlr_xdg_popup_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_unconstrain_flip(struct wlr_xdg_popup *popup, - const struct wlr_box *toplevel_sx_box) { - int offset_x = 0, offset_y = 0; - xdg_popup_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 & - XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X); - - bool flip_y = offset_y && - (popup->positioner.constraint_adjustment & - XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y); - - if (flip_x) { - wlr_positioner_invert_x(&popup->positioner); - } - if (flip_y) { - wlr_positioner_invert_y(&popup->positioner); - } - - popup->geometry = - wlr_xdg_positioner_get_geometry(&popup->positioner); - - xdg_popup_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_invert_x(&popup->positioner); - } - if (offset_y && flip_y) { - wlr_positioner_invert_y(&popup->positioner); - } - - popup->geometry = - wlr_xdg_positioner_get_geometry(&popup->positioner); - - return false; -} - -static bool xdg_popup_unconstrain_slide(struct wlr_xdg_popup *popup, - const struct wlr_box *toplevel_sx_box) { - int offset_x = 0, offset_y = 0; - xdg_popup_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 & - XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X); - - bool slide_y = offset_y && - (popup->positioner.constraint_adjustment & - XDG_POSITIONER_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_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_box_constraints(popup, toplevel_sx_box, - &offset_x, &offset_y); - - return !offset_x && !offset_y; -} - -static bool xdg_popup_unconstrain_resize(struct wlr_xdg_popup *popup, - const struct wlr_box *toplevel_sx_box) { - int offset_x, offset_y; - xdg_popup_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 & - XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X); - - bool resize_y = offset_y && - (popup->positioner.constraint_adjustment & - XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y); - - if (resize_x) { - popup->geometry.width -= offset_x; - } - if (resize_y) { - popup->geometry.height -= offset_y; - } - - xdg_popup_box_constraints(popup, toplevel_sx_box, - &offset_x, &offset_y); - - return !offset_x && !offset_y; -} - void wlr_xdg_popup_unconstrain_from_box(struct wlr_xdg_popup *popup, - const struct wlr_box *toplevel_sx_box) { - if (xdg_popup_unconstrain_flip(popup, toplevel_sx_box)) { - return; - } - if (xdg_popup_unconstrain_slide(popup, toplevel_sx_box)) { - return; - } - if (xdg_popup_unconstrain_resize(popup, toplevel_sx_box)) { - return; - } + const struct wlr_box *toplevel_space_box) { + int toplevel_sx, toplevel_sy; + wlr_xdg_popup_get_toplevel_coords(popup, + 0, 0, &toplevel_sx, &toplevel_sy); + struct wlr_box popup_constraint = { + .x = toplevel_space_box->x - toplevel_sx, + .y = toplevel_space_box->y - toplevel_sy, + .width = toplevel_space_box->width, + .height = toplevel_space_box->height, + }; + wlr_xdg_positioner_rules_unconstrain_box(&popup->positioner_rules, + &popup_constraint, &popup->geometry); } diff --git a/types/xdg_shell/wlr_xdg_positioner.c b/types/xdg_shell/wlr_xdg_positioner.c index aa149cc2a..6fa1ed991 100644 --- a/types/xdg_shell/wlr_xdg_positioner.c +++ b/types/xdg_shell/wlr_xdg_positioner.c @@ -1,10 +1,11 @@ #include #include +#include #include "types/wlr_xdg_shell.h" static const struct xdg_positioner_interface xdg_positioner_implementation; -struct wlr_xdg_positioner_resource *get_xdg_positioner_from_resource( +struct wlr_xdg_positioner *wlr_xdg_positioner_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &xdg_positioner_interface, &xdg_positioner_implementation)); @@ -13,8 +14,8 @@ struct wlr_xdg_positioner_resource *get_xdg_positioner_from_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_resource *positioner = - get_xdg_positioner_from_resource(resource); + struct wlr_xdg_positioner *positioner = + wlr_xdg_positioner_from_resource(resource); if (width < 1 || height < 1) { wl_resource_post_error(resource, @@ -23,15 +24,15 @@ static void xdg_positioner_handle_set_size(struct wl_client *client, return; } - positioner->attrs.size.width = width; - positioner->attrs.size.height = height; + positioner->rules.size.width = width; + positioner->rules.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_resource *positioner = - get_xdg_positioner_from_resource(resource); + struct wlr_xdg_positioner *positioner = + wlr_xdg_positioner_from_resource(resource); if (width < 0 || height < 0) { wl_resource_post_error(resource, @@ -40,16 +41,16 @@ static void xdg_positioner_handle_set_anchor_rect(struct wl_client *client, 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; + positioner->rules.anchor_rect.x = x; + positioner->rules.anchor_rect.y = y; + positioner->rules.anchor_rect.width = width; + positioner->rules.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_resource *positioner = - get_xdg_positioner_from_resource(resource); + struct wlr_xdg_positioner *positioner = + wlr_xdg_positioner_from_resource(resource); if (anchor > XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT) { wl_resource_post_error(resource, @@ -58,13 +59,13 @@ static void xdg_positioner_handle_set_anchor(struct wl_client *client, return; } - positioner->attrs.anchor = anchor; + positioner->rules.anchor = anchor; } static void xdg_positioner_handle_set_gravity(struct wl_client *client, struct wl_resource *resource, uint32_t gravity) { - struct wlr_xdg_positioner_resource *positioner = - get_xdg_positioner_from_resource(resource); + struct wlr_xdg_positioner *positioner = + wlr_xdg_positioner_from_resource(resource); if (gravity > XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT) { wl_resource_post_error(resource, @@ -73,25 +74,25 @@ static void xdg_positioner_handle_set_gravity(struct wl_client *client, return; } - positioner->attrs.gravity = gravity; + positioner->rules.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_resource *positioner = - get_xdg_positioner_from_resource(resource); + struct wlr_xdg_positioner *positioner = + wlr_xdg_positioner_from_resource(resource); - positioner->attrs.constraint_adjustment = constraint_adjustment; + positioner->rules.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_resource *positioner = - get_xdg_positioner_from_resource(resource); + struct wlr_xdg_positioner *positioner = + wlr_xdg_positioner_from_resource(resource); - positioner->attrs.offset.x = x; - positioner->attrs.offset.y = y; + positioner->rules.offset.x = x; + positioner->rules.offset.y = y; } static void xdg_positioner_handle_destroy(struct wl_client *client, @@ -113,14 +114,13 @@ static const struct xdg_positioner_interface static void xdg_positioner_handle_resource_destroy( struct wl_resource *resource) { - struct wlr_xdg_positioner_resource *positioner = - get_xdg_positioner_from_resource(resource); + struct wlr_xdg_positioner *positioner = + wlr_xdg_positioner_from_resource(resource); free(positioner); } void create_xdg_positioner(struct wlr_xdg_client *client, uint32_t id) { - struct wlr_xdg_positioner_resource *positioner = - calloc(1, sizeof(struct wlr_xdg_positioner_resource)); + struct wlr_xdg_positioner *positioner = calloc(1, sizeof(*positioner)); if (positioner == NULL) { wl_client_post_no_memory(client->client); return; @@ -140,94 +140,79 @@ void create_xdg_positioner(struct wlr_xdg_client *client, uint32_t id) { positioner, xdg_positioner_handle_resource_destroy); } -static bool positioner_anchor_has_edge(enum xdg_positioner_anchor anchor, - enum xdg_positioner_anchor edge) { - switch (edge) { +static uint32_t xdg_positioner_anchor_to_wlr_edges( + enum xdg_positioner_anchor anchor) { + switch (anchor) { + case XDG_POSITIONER_ANCHOR_NONE: + return WLR_EDGE_NONE; case XDG_POSITIONER_ANCHOR_TOP: - return anchor == XDG_POSITIONER_ANCHOR_TOP || - anchor == XDG_POSITIONER_ANCHOR_TOP_LEFT || - anchor == XDG_POSITIONER_ANCHOR_TOP_RIGHT; + return WLR_EDGE_TOP; + case XDG_POSITIONER_ANCHOR_TOP_LEFT: + return WLR_EDGE_TOP | WLR_EDGE_LEFT; + case XDG_POSITIONER_ANCHOR_TOP_RIGHT: + return WLR_EDGE_TOP | WLR_EDGE_RIGHT; case XDG_POSITIONER_ANCHOR_BOTTOM: - return anchor == XDG_POSITIONER_ANCHOR_BOTTOM || - anchor == XDG_POSITIONER_ANCHOR_BOTTOM_LEFT || - anchor == XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT; + return WLR_EDGE_BOTTOM; + case XDG_POSITIONER_ANCHOR_BOTTOM_LEFT: + return WLR_EDGE_BOTTOM | WLR_EDGE_LEFT; + case XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT: + return WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT; case XDG_POSITIONER_ANCHOR_LEFT: - return anchor == XDG_POSITIONER_ANCHOR_LEFT || - anchor == XDG_POSITIONER_ANCHOR_TOP_LEFT || - anchor == XDG_POSITIONER_ANCHOR_BOTTOM_LEFT; + return WLR_EDGE_LEFT; case XDG_POSITIONER_ANCHOR_RIGHT: - return anchor == XDG_POSITIONER_ANCHOR_RIGHT || - anchor == XDG_POSITIONER_ANCHOR_TOP_RIGHT || - anchor == XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT; - default: - abort(); // unreachable + return WLR_EDGE_RIGHT; } + + abort(); // Unreachable } -static bool positioner_gravity_has_edge(enum xdg_positioner_gravity gravity, - enum xdg_positioner_gravity edge) { - // gravity and edge enums are the same - return positioner_anchor_has_edge((enum xdg_positioner_anchor)gravity, - (enum xdg_positioner_anchor)edge); +static uint32_t xdg_positioner_gravity_to_wlr_edges( + enum xdg_positioner_gravity gravity) { + // Gravity and edge enums are the same + return xdg_positioner_anchor_to_wlr_edges((enum xdg_positioner_anchor)gravity); } -struct wlr_box wlr_xdg_positioner_get_geometry( - struct wlr_xdg_positioner *positioner) { - struct wlr_box geometry = { - .x = positioner->offset.x, - .y = positioner->offset.y, - .width = positioner->size.width, - .height = positioner->size.height, - }; +void wlr_xdg_positioner_rules_get_geometry( + const struct wlr_xdg_positioner_rules *rules, struct wlr_box *box) { + box->x = rules->offset.x; + box->y = rules->offset.y; + box->width = rules->size.width; + box->height = rules->size.height; - if (positioner_anchor_has_edge(positioner->anchor, - XDG_POSITIONER_ANCHOR_TOP)) { - geometry.y += positioner->anchor_rect.y; - } else if (positioner_anchor_has_edge(positioner->anchor, - XDG_POSITIONER_ANCHOR_BOTTOM)) { - geometry.y += - positioner->anchor_rect.y + positioner->anchor_rect.height; + uint32_t edges = xdg_positioner_anchor_to_wlr_edges(rules->anchor); + + if (edges & WLR_EDGE_TOP) { + box->y += rules->anchor_rect.y; + } else if (edges & WLR_EDGE_BOTTOM) { + box->y += rules->anchor_rect.y + rules->anchor_rect.height; } else { - geometry.y += - positioner->anchor_rect.y + positioner->anchor_rect.height / 2; + box->y += rules->anchor_rect.y + rules->anchor_rect.height / 2; } - if (positioner_anchor_has_edge(positioner->anchor, - XDG_POSITIONER_ANCHOR_LEFT)) { - geometry.x += positioner->anchor_rect.x; - } else if (positioner_anchor_has_edge(positioner->anchor, - XDG_POSITIONER_ANCHOR_RIGHT)) { - geometry.x += positioner->anchor_rect.x + positioner->anchor_rect.width; + if (edges & WLR_EDGE_LEFT) { + box->x += rules->anchor_rect.x; + } else if (edges & WLR_EDGE_RIGHT) { + box->x += rules->anchor_rect.x + rules->anchor_rect.width; } else { - geometry.x += - positioner->anchor_rect.x + positioner->anchor_rect.width / 2; + box->x += rules->anchor_rect.x + rules->anchor_rect.width / 2; } - if (positioner_gravity_has_edge(positioner->gravity, - XDG_POSITIONER_GRAVITY_TOP)) { - geometry.y -= geometry.height; - } else if (!positioner_gravity_has_edge(positioner->gravity, - XDG_POSITIONER_GRAVITY_BOTTOM)) { - geometry.y -= geometry.height / 2; + edges = xdg_positioner_gravity_to_wlr_edges(rules->gravity); + + if (edges & WLR_EDGE_TOP) { + box->y -= box->height; + } else if (~edges & WLR_EDGE_BOTTOM) { + box->y -= box->height / 2; } - if (positioner_gravity_has_edge(positioner->gravity, - XDG_POSITIONER_GRAVITY_LEFT)) { - geometry.x -= geometry.width; - } else if (!positioner_gravity_has_edge(positioner->gravity, - XDG_POSITIONER_GRAVITY_RIGHT)) { - geometry.x -= geometry.width / 2; + if (edges & WLR_EDGE_LEFT) { + box->x -= box->width; + } else if (~edges & WLR_EDGE_RIGHT) { + box->x -= box->width / 2; } - - if (positioner->constraint_adjustment == - XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE) { - return geometry; - } - - return geometry; } -static enum xdg_positioner_anchor positioner_anchor_invert_x( +static enum xdg_positioner_anchor xdg_positioner_anchor_invert_x( enum xdg_positioner_anchor anchor) { switch (anchor) { case XDG_POSITIONER_ANCHOR_LEFT: @@ -247,14 +232,14 @@ static enum xdg_positioner_anchor positioner_anchor_invert_x( } } -static enum xdg_positioner_gravity positioner_gravity_invert_x( +static enum xdg_positioner_gravity xdg_positioner_gravity_invert_x( enum xdg_positioner_gravity gravity) { // gravity and edge enums are the same - return (enum xdg_positioner_gravity)positioner_anchor_invert_x( + return (enum xdg_positioner_gravity)xdg_positioner_anchor_invert_x( (enum xdg_positioner_anchor)gravity); } -static enum xdg_positioner_anchor positioner_anchor_invert_y( +static enum xdg_positioner_anchor xdg_positioner_anchor_invert_y( enum xdg_positioner_anchor anchor) { switch (anchor) { case XDG_POSITIONER_ANCHOR_TOP: @@ -274,20 +259,213 @@ static enum xdg_positioner_anchor positioner_anchor_invert_y( } } -static enum xdg_positioner_gravity positioner_gravity_invert_y( +static enum xdg_positioner_gravity xdg_positioner_gravity_invert_y( enum xdg_positioner_gravity gravity) { // gravity and edge enums are the same - return (enum xdg_positioner_gravity)positioner_anchor_invert_y( + return (enum xdg_positioner_gravity)xdg_positioner_anchor_invert_y( (enum xdg_positioner_anchor)gravity); } +/** + * Distances from each edge of the box to the corresponding edge of + * the anchor rect. Each distance is positive if the edge is outside + * the anchor rect, and negative if the edge is inside it. + */ +struct constraint_offsets { + int top; + int bottom; + int left; + int right; +}; -void wlr_positioner_invert_x(struct wlr_xdg_positioner *positioner) { - positioner->anchor = positioner_anchor_invert_x(positioner->anchor); - positioner->gravity = positioner_gravity_invert_x(positioner->gravity); +static bool is_unconstrained(const struct constraint_offsets *offsets) { + return offsets->top <= 0 && offsets->bottom <= 0 && + offsets->left <= 0 && offsets->right <= 0; } -void wlr_positioner_invert_y(struct wlr_xdg_positioner *positioner) { - positioner->anchor = positioner_anchor_invert_y(positioner->anchor); - positioner->gravity = positioner_gravity_invert_y(positioner->gravity); +static void get_constrained_box_offsets(const struct wlr_box *constraint, + const struct wlr_box *box, struct constraint_offsets *offsets) { + offsets->left = constraint->x - box->x; + offsets->right = box->x + box->width - constraint->x - constraint->width; + offsets->top = constraint->y - box->y; + offsets->bottom = box->y + box->height - constraint->y - constraint->height; +} + +static bool xdg_positioner_rules_unconstrain_by_flip( + const struct wlr_xdg_positioner_rules *rules, + const struct wlr_box *constraint, struct wlr_box *box, + struct constraint_offsets *offsets) { + // If none of the edges are constrained, no need to flip. + // If both edges are constrained, the box is bigger than + // the anchor rect and flipping won't help anyway. + bool flip_x = ((offsets->left > 0) ^ (offsets->right > 0)) && + (rules->constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X); + bool flip_y = ((offsets->top > 0) ^ (offsets->bottom > 0)) && + (rules->constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y); + + if (!flip_x && !flip_y) { + return false; + } + + struct wlr_xdg_positioner_rules flipped = *rules; + if (flip_x) { + flipped.anchor = xdg_positioner_anchor_invert_x(flipped.anchor); + flipped.gravity = xdg_positioner_gravity_invert_x(flipped.gravity); + } + if (flip_y) { + flipped.anchor = xdg_positioner_anchor_invert_y(flipped.anchor); + flipped.gravity = xdg_positioner_gravity_invert_y(flipped.gravity); + } + + struct wlr_box flipped_box; + wlr_xdg_positioner_rules_get_geometry(&flipped, &flipped_box); + struct constraint_offsets flipped_offsets; + get_constrained_box_offsets(constraint, &flipped_box, &flipped_offsets); + + // Only apply flipping if it helps + if (flipped_offsets.left <= 0 && flipped_offsets.right <= 0) { + box->x = flipped_box.x; + offsets->left = flipped_offsets.left; + offsets->right = flipped_offsets.right; + } + if (flipped_offsets.top <= 0 && flipped_offsets.bottom <= 0) { + box->y = flipped_box.y; + offsets->top = flipped_offsets.top; + offsets->bottom = flipped_offsets.bottom; + } + + return is_unconstrained(offsets); +} + +static bool xdg_positioner_rules_unconstrain_by_slide( + const struct wlr_xdg_positioner_rules *rules, + const struct wlr_box *constraint, struct wlr_box *box, + struct constraint_offsets *offsets) { + uint32_t gravity = xdg_positioner_gravity_to_wlr_edges(rules->gravity); + + // We can only slide if there is gravity on this axis + bool slide_x = (offsets->left > 0 || offsets->right > 0) && + (rules->constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X) && + (gravity & (WLR_EDGE_LEFT | WLR_EDGE_RIGHT)); + bool slide_y = (offsets->top > 0 || offsets->bottom > 0) && + (rules->constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y) && + (gravity & (WLR_EDGE_TOP | WLR_EDGE_BOTTOM)); + + if (!slide_x && !slide_y) { + return false; + } + + if (slide_x) { + if (offsets->left > 0 && offsets->right > 0) { + // The protocol states: "First try to slide towards the direction of + // the gravity [...] Then try to slide towards the opposite direction + // of the gravity". The only situation where this order matters is when + // the box is bigger than the anchor rect and completely includes it. + // In this case, the second slide will fail immediately, so simply + // slide towards the direction of the gravity. + if (gravity & WLR_EDGE_LEFT) { + box->x -= offsets->right; + } else if (gravity & WLR_EDGE_RIGHT) { + box->x += offsets->left; + } + } else { + // If at least one edge is already unconstrained, the order of slide + // attempts doesn't matter. Slide for the minimal distance needed to + // satisfy the requirement of constraining one edge or unconstraining + // another. + int abs_left = offsets->left > 0 ? offsets->left : -offsets->left; + int abs_right = offsets->right > 0 ? offsets->right : -offsets->right; + if (abs_left < abs_right) { + box->x += offsets->left; + } else { + box->x -= offsets->right; + } + } + } + if (slide_y) { + if (offsets->top > 0 && offsets->bottom > 0) { + if (gravity & WLR_EDGE_TOP) { + box->y -= offsets->bottom; + } else if (gravity & WLR_EDGE_BOTTOM) { + box->y += offsets->top; + } + } else { + int abs_top = offsets->top > 0 ? offsets->top : -offsets->top; + int abs_bottom = offsets->bottom > 0 ? offsets->bottom : -offsets->bottom; + if (abs_top < abs_bottom) { + box->y += offsets->top; + } else { + box->y -= offsets->bottom; + } + } + } + + get_constrained_box_offsets(constraint, box, offsets); + return is_unconstrained(offsets); +} + +static bool xdg_positioner_rules_unconstrain_by_resize( + const struct wlr_xdg_positioner_rules *rules, + const struct wlr_box *constraint, struct wlr_box *box, + struct constraint_offsets *offsets) { + bool resize_x = (offsets->left > 0 || offsets->right > 0) && + (rules->constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X); + bool resize_y = (offsets->top > 0 || offsets->bottom > 0) && + (rules->constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y); + + if (!resize_x && !resize_y) { + return false; + } + + if (offsets->left < 0) { + offsets->left = 0; + } + if (offsets->right < 0) { + offsets->right = 0; + } + if (offsets->top < 0) { + offsets->top = 0; + } + if (offsets->bottom < 0) { + offsets->bottom = 0; + } + + // Try to satisfy the constraints by clipping the box. + struct wlr_box resized_box = *box; + if (resize_x) { + resized_box.x += offsets->left; + resized_box.width -= offsets->left + offsets->right; + } + if (resize_y) { + resized_box.y += offsets->top; + resized_box.height -= offsets->top + offsets->bottom; + } + + if (wlr_box_empty(&resized_box)) { + return false; + } + + *box = resized_box; + get_constrained_box_offsets(constraint, box, offsets); + return is_unconstrained(offsets); +} + +void wlr_xdg_positioner_rules_unconstrain_box( + const struct wlr_xdg_positioner_rules *rules, + const struct wlr_box *constraint, struct wlr_box *box) { + struct constraint_offsets offsets; + get_constrained_box_offsets(constraint, box, &offsets); + if (is_unconstrained(&offsets)) { + // Already unconstrained + return; + } + if (xdg_positioner_rules_unconstrain_by_flip(rules, constraint, box, &offsets)) { + return; + } + if (xdg_positioner_rules_unconstrain_by_slide(rules, constraint, box, &offsets)) { + return; + } + if (xdg_positioner_rules_unconstrain_by_resize(rules, constraint, box, &offsets)) { + return; + } } diff --git a/types/xdg_shell/wlr_xdg_shell.c b/types/xdg_shell/wlr_xdg_shell.c index 0480d5f35..0b1302622 100644 --- a/types/xdg_shell/wlr_xdg_shell.c +++ b/types/xdg_shell/wlr_xdg_shell.c @@ -84,7 +84,7 @@ static int xdg_client_ping_timeout(void *user_data) { struct wlr_xdg_surface *surface; wl_list_for_each(surface, &client->surfaces, link) { - wlr_signal_emit_safe(&surface->events.ping_timeout, surface); + wlr_signal_emit_safe(&surface->events.ping_timeout, NULL); } client->ping_serial = 0; diff --git a/types/xdg_shell/wlr_xdg_surface.c b/types/xdg_shell/wlr_xdg_surface.c index c45baabb9..73ed66c92 100644 --- a/types/xdg_shell/wlr_xdg_surface.c +++ b/types/xdg_shell/wlr_xdg_surface.c @@ -32,44 +32,20 @@ void unmap_xdg_surface(struct wlr_xdg_surface *surface) { struct wlr_xdg_popup *popup, *popup_tmp; wl_list_for_each_safe(popup, popup_tmp, &surface->popups, link) { - wlr_xdg_popup_destroy(popup->base); + wlr_xdg_popup_destroy(popup); } // TODO: probably need to ungrab before this event if (surface->mapped) { - wlr_signal_emit_safe(&surface->events.unmap, surface); + wlr_signal_emit_safe(&surface->events.unmap, NULL); } switch (surface->role) { case WLR_XDG_SURFACE_ROLE_TOPLEVEL: - if (surface->toplevel->parent) { - wl_list_remove(&surface->toplevel->parent_unmap.link); - surface->toplevel->parent = NULL; - } - free(surface->toplevel->title); - surface->toplevel->title = NULL; - free(surface->toplevel->app_id); - surface->toplevel->app_id = NULL; + unmap_xdg_toplevel(surface->toplevel); break; case WLR_XDG_SURFACE_ROLE_POPUP: - if (surface->popup->seat != NULL) { - struct wlr_xdg_popup_grab *grab = - get_xdg_shell_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; - } + unmap_xdg_popup(surface->popup); break; case WLR_XDG_SURFACE_ROLE_NONE: assert(false && "not reached"); @@ -130,7 +106,8 @@ static void xdg_surface_handle_ack_configure(struct wl_client *client, assert(0 && "not reached"); break; case WLR_XDG_SURFACE_ROLE_TOPLEVEL: - handle_xdg_toplevel_ack_configure(surface, configure); + handle_xdg_toplevel_ack_configure(surface->toplevel, + configure->toplevel_configure); break; case WLR_XDG_SURFACE_ROLE_POPUP: break; @@ -164,7 +141,8 @@ static void surface_send_configure(void *user_data) { assert(0 && "not reached"); break; case WLR_XDG_SURFACE_ROLE_TOPLEVEL: - send_xdg_toplevel_configure(surface, configure); + configure->toplevel_configure = + send_xdg_toplevel_configure(surface->toplevel); break; case WLR_XDG_SURFACE_ROLE_POPUP: xdg_popup_send_configure(surface->popup->resource, @@ -208,8 +186,8 @@ static void xdg_surface_handle_get_popup(struct wl_client *client, if (xdg_surface == NULL) { return; // TODO: create an inert xdg_popup } - struct wlr_xdg_positioner_resource *positioner = - get_xdg_positioner_from_resource(positioner_resource); + struct wlr_xdg_positioner *positioner = + wlr_xdg_positioner_from_resource(positioner_resource); create_xdg_popup(xdg_surface, parent, positioner, id); } @@ -305,7 +283,7 @@ static void xdg_surface_handle_surface_commit(struct wl_listener *listener, } } -void handle_xdg_surface_commit(struct wlr_surface *wlr_surface) { +void xdg_surface_role_commit(struct wlr_surface *wlr_surface) { struct wlr_xdg_surface *surface = wlr_xdg_surface_from_wlr_surface(wlr_surface); if (surface == NULL) { @@ -319,10 +297,10 @@ void handle_xdg_surface_commit(struct wlr_surface *wlr_surface) { // inert toplevel or popup return; case WLR_XDG_SURFACE_ROLE_TOPLEVEL: - handle_xdg_surface_toplevel_committed(surface); + handle_xdg_toplevel_committed(surface->toplevel); break; case WLR_XDG_SURFACE_ROLE_POPUP: - handle_xdg_surface_popup_committed(surface); + handle_xdg_popup_committed(surface->popup); break; } @@ -334,19 +312,19 @@ void handle_xdg_surface_commit(struct wlr_surface *wlr_surface) { if (surface->configured && wlr_surface_has_buffer(surface->surface) && !surface->mapped) { surface->mapped = true; - wlr_signal_emit_safe(&surface->events.map, surface); + wlr_signal_emit_safe(&surface->events.map, NULL); } } -void handle_xdg_surface_precommit(struct wlr_surface *wlr_surface) { +void xdg_surface_role_precommit(struct wlr_surface *wlr_surface, + const struct wlr_surface_state *state) { struct wlr_xdg_surface *surface = wlr_xdg_surface_from_wlr_surface(wlr_surface); if (surface == NULL) { return; } - if (wlr_surface->pending.committed & WLR_SURFACE_STATE_BUFFER && - wlr_surface->pending.buffer == NULL) { + if (state->committed & WLR_SURFACE_STATE_BUFFER && state->buffer == NULL) { // This is a NULL commit if (surface->configured && surface->mapped) { unmap_xdg_surface(surface); @@ -362,102 +340,90 @@ static void xdg_surface_handle_surface_destroy(struct wl_listener *listener, } struct wlr_xdg_surface *create_xdg_surface( - struct wlr_xdg_client *client, struct wlr_surface *surface, + struct wlr_xdg_client *client, struct wlr_surface *wlr_surface, uint32_t id) { - struct wlr_xdg_surface *xdg_surface = + struct wlr_xdg_surface *surface = calloc(1, sizeof(struct wlr_xdg_surface)); - if (xdg_surface == NULL) { + if (surface == NULL) { wl_client_post_no_memory(client->client); return NULL; } - xdg_surface->client = client; - xdg_surface->role = WLR_XDG_SURFACE_ROLE_NONE; - xdg_surface->surface = surface; - xdg_surface->resource = wl_resource_create(client->client, + surface->client = client; + surface->role = WLR_XDG_SURFACE_ROLE_NONE; + surface->surface = wlr_surface; + surface->resource = wl_resource_create(client->client, &xdg_surface_interface, wl_resource_get_version(client->resource), id); - if (xdg_surface->resource == NULL) { - free(xdg_surface); + if (surface->resource == NULL) { + free(surface); wl_client_post_no_memory(client->client); return NULL; } - if (wlr_surface_has_buffer(xdg_surface->surface)) { - wl_resource_destroy(xdg_surface->resource); - free(xdg_surface); + if (wlr_surface_has_buffer(surface->surface)) { + wl_resource_destroy(surface->resource); + free(surface); wl_resource_post_error(client->resource, XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER, "xdg_surface must not have a buffer at creation"); return NULL; } - wl_list_init(&xdg_surface->configure_list); - wl_list_init(&xdg_surface->popups); + wl_list_init(&surface->configure_list); + wl_list_init(&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_init(&xdg_surface->events.configure); - wl_signal_init(&xdg_surface->events.ack_configure); + wl_signal_init(&surface->events.destroy); + wl_signal_init(&surface->events.ping_timeout); + wl_signal_init(&surface->events.new_popup); + wl_signal_init(&surface->events.map); + wl_signal_init(&surface->events.unmap); + wl_signal_init(&surface->events.configure); + wl_signal_init(&surface->events.ack_configure); - 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(&surface->surface->events.destroy, + &surface->surface_destroy); + 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; + wl_signal_add(&surface->surface->events.commit, + &surface->surface_commit); + 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_resource_set_implementation(xdg_surface->resource, - &xdg_surface_implementation, xdg_surface, + wlr_log(WLR_DEBUG, "new xdg_surface %p (res %p)", surface, + surface->resource); + wl_resource_set_implementation(surface->resource, + &xdg_surface_implementation, surface, xdg_surface_handle_resource_destroy); - wl_list_insert(&client->surfaces, &xdg_surface->link); + wl_list_insert(&client->surfaces, &surface->link); - return xdg_surface; + return surface; } -void reset_xdg_surface(struct wlr_xdg_surface *xdg_surface) { - if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_NONE) { - unmap_xdg_surface(xdg_surface); +void reset_xdg_surface(struct wlr_xdg_surface *surface) { + if (surface->role != WLR_XDG_SURFACE_ROLE_NONE) { + unmap_xdg_surface(surface); } - if (xdg_surface->added) { - wlr_signal_emit_safe(&xdg_surface->events.destroy, xdg_surface); - xdg_surface->added = false; + if (surface->added) { + wlr_signal_emit_safe(&surface->events.destroy, NULL); + surface->added = false; } - switch (xdg_surface->role) { + switch (surface->role) { case WLR_XDG_SURFACE_ROLE_TOPLEVEL: - wl_resource_set_user_data(xdg_surface->toplevel->resource, NULL); - xdg_surface->toplevel->resource = NULL; - struct wlr_xdg_toplevel_requested *req = - &xdg_surface->toplevel->requested; - if (req->fullscreen_output) { - wl_list_remove(&req->fullscreen_output_destroy.link); - } - free(xdg_surface->toplevel); - xdg_surface->toplevel = NULL; + destroy_xdg_toplevel(surface->toplevel); + surface->toplevel = NULL; break; case WLR_XDG_SURFACE_ROLE_POPUP: - wl_resource_set_user_data(xdg_surface->popup->resource, NULL); - xdg_surface->popup->resource = NULL; - - wl_list_remove(&xdg_surface->popup->link); - - free(xdg_surface->popup); - xdg_surface->popup = NULL; + destroy_xdg_popup(surface->popup); + surface->popup = NULL; break; case WLR_XDG_SURFACE_ROLE_NONE: // This space is intentionally left blank break; } - xdg_surface->role = WLR_XDG_SURFACE_ROLE_NONE; + surface->role = WLR_XDG_SURFACE_ROLE_NONE; } void destroy_xdg_surface(struct wlr_xdg_surface *surface) { @@ -493,29 +459,6 @@ void wlr_xdg_surface_ping(struct wlr_xdg_surface *surface) { surface->client->ping_serial); } -void wlr_xdg_toplevel_send_close(struct wlr_xdg_surface *surface) { - assert(surface->toplevel); - assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); - xdg_toplevel_send_close(surface->toplevel->resource); -} - -void wlr_xdg_popup_destroy(struct wlr_xdg_surface *surface) { - if (surface == NULL) { - return; - } - assert(surface->popup); - assert(surface->role == WLR_XDG_SURFACE_ROLE_POPUP); - - struct wlr_xdg_popup *popup, *popup_tmp; - wl_list_for_each_safe(popup, popup_tmp, &surface->popups, link) { - wlr_xdg_popup_destroy(popup->base); - } - - xdg_popup_send_popup_done(surface->popup->resource); - wl_resource_set_user_data(surface->popup->resource, NULL); - reset_xdg_surface(surface); -} - void wlr_xdg_popup_get_position(struct wlr_xdg_popup *popup, double *popup_sx, double *popup_sy) { struct wlr_xdg_surface *parent = @@ -542,19 +485,17 @@ struct wlr_surface *wlr_xdg_surface_surface_at( struct wlr_surface *wlr_xdg_surface_popup_surface_at( struct wlr_xdg_surface *surface, double sx, double sy, double *sub_x, double *sub_y) { - struct wlr_xdg_popup *popup_state; - wl_list_for_each(popup_state, &surface->popups, link) { - struct wlr_xdg_surface *popup = popup_state->base; - if (!popup->mapped) { + struct wlr_xdg_popup *popup; + wl_list_for_each(popup, &surface->popups, link) { + if (!popup->base->mapped) { continue; } double popup_sx, popup_sy; - wlr_xdg_popup_get_position(popup_state, &popup_sx, &popup_sy); + wlr_xdg_popup_get_position(popup, &popup_sx, &popup_sy); - struct wlr_surface *sub = wlr_xdg_surface_surface_at(popup, - sx - popup_sx, - sy - popup_sy, + struct wlr_surface *sub = wlr_xdg_surface_surface_at( + popup->base, sx - popup_sx, sy - popup_sy, sub_x, sub_y); if (sub != NULL) { return sub; @@ -579,28 +520,25 @@ static void xdg_surface_iterator(struct wlr_surface *surface, static void xdg_surface_for_each_popup_surface(struct wlr_xdg_surface *surface, int x, int y, 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; - if (!popup->configured || !popup->mapped) { + struct wlr_xdg_popup *popup; + wl_list_for_each(popup, &surface->popups, link) { + if (!popup->base->configured || !popup->base->mapped) { continue; } double popup_sx, popup_sy; - wlr_xdg_popup_get_position(popup_state, &popup_sx, &popup_sy); + wlr_xdg_popup_get_position(popup, &popup_sx, &popup_sy); struct xdg_surface_iterator_data data = { .user_iterator = iterator, .user_data = user_data, .x = x + popup_sx, .y = y + popup_sy, }; - wlr_surface_for_each_surface(popup->surface, xdg_surface_iterator, - &data); + wlr_surface_for_each_surface(popup->base->surface, + xdg_surface_iterator, &data); - xdg_surface_for_each_popup_surface(popup, - x + popup_sx, - y + popup_sy, - iterator, user_data); + xdg_surface_for_each_popup_surface(popup->base, + x + popup_sx, y + popup_sy, iterator, user_data); } } diff --git a/types/xdg_shell/wlr_xdg_toplevel.c b/types/xdg_shell/wlr_xdg_toplevel.c index b5a7239b0..ed304cfcd 100644 --- a/types/xdg_shell/wlr_xdg_toplevel.c +++ b/types/xdg_shell/wlr_xdg_toplevel.c @@ -8,38 +8,32 @@ #include "util/signal.h" void handle_xdg_toplevel_ack_configure( - struct wlr_xdg_surface *surface, - struct wlr_xdg_surface_configure *configure) { - assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); + struct wlr_xdg_toplevel *toplevel, + struct wlr_xdg_toplevel_configure *configure) { + toplevel->pending.maximized = configure->maximized; + toplevel->pending.fullscreen = configure->fullscreen; + toplevel->pending.resizing = configure->resizing; + toplevel->pending.activated = configure->activated; + toplevel->pending.tiled = configure->tiled; - struct wlr_xdg_toplevel_configure *acked = configure->toplevel_configure; - assert(acked != NULL); - - surface->toplevel->pending.maximized = acked->maximized; - surface->toplevel->pending.fullscreen = acked->fullscreen; - surface->toplevel->pending.resizing = acked->resizing; - surface->toplevel->pending.activated = acked->activated; - surface->toplevel->pending.tiled = acked->tiled; - - surface->toplevel->pending.width = acked->width; - surface->toplevel->pending.height = acked->height; + toplevel->pending.width = configure->width; + toplevel->pending.height = configure->height; } -void send_xdg_toplevel_configure(struct wlr_xdg_surface *surface, - struct wlr_xdg_surface_configure *configure) { - assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); - - configure->toplevel_configure = malloc(sizeof(*configure->toplevel_configure)); - if (configure->toplevel_configure == NULL) { +struct wlr_xdg_toplevel_configure *send_xdg_toplevel_configure( + struct wlr_xdg_toplevel *toplevel) { + struct wlr_xdg_toplevel_configure *configure = + calloc(1, sizeof(*configure)); + if (configure == NULL) { wlr_log(WLR_ERROR, "Allocation failed"); - wl_resource_post_no_memory(surface->toplevel->resource); - return; + wl_resource_post_no_memory(toplevel->resource); + return NULL; } - *configure->toplevel_configure = surface->toplevel->scheduled; + *configure = toplevel->scheduled; struct wl_array states; wl_array_init(&states); - if (surface->toplevel->scheduled.maximized) { + if (configure->maximized) { uint32_t *s = wl_array_add(&states, sizeof(uint32_t)); if (!s) { wlr_log(WLR_ERROR, "Could not allocate state for maximized xdg_toplevel"); @@ -47,7 +41,7 @@ void send_xdg_toplevel_configure(struct wlr_xdg_surface *surface, } *s = XDG_TOPLEVEL_STATE_MAXIMIZED; } - if (surface->toplevel->scheduled.fullscreen) { + if (configure->fullscreen) { uint32_t *s = wl_array_add(&states, sizeof(uint32_t)); if (!s) { wlr_log(WLR_ERROR, "Could not allocate state for fullscreen xdg_toplevel"); @@ -55,7 +49,7 @@ void send_xdg_toplevel_configure(struct wlr_xdg_surface *surface, } *s = XDG_TOPLEVEL_STATE_FULLSCREEN; } - if (surface->toplevel->scheduled.resizing) { + if (configure->resizing) { uint32_t *s = wl_array_add(&states, sizeof(uint32_t)); if (!s) { wlr_log(WLR_ERROR, "Could not allocate state for resizing xdg_toplevel"); @@ -63,7 +57,7 @@ void send_xdg_toplevel_configure(struct wlr_xdg_surface *surface, } *s = XDG_TOPLEVEL_STATE_RESIZING; } - if (surface->toplevel->scheduled.activated) { + if (configure->activated) { uint32_t *s = wl_array_add(&states, sizeof(uint32_t)); if (!s) { wlr_log(WLR_ERROR, "Could not allocate state for activated xdg_toplevel"); @@ -71,8 +65,8 @@ void send_xdg_toplevel_configure(struct wlr_xdg_surface *surface, } *s = XDG_TOPLEVEL_STATE_ACTIVATED; } - if (surface->toplevel->scheduled.tiled) { - if (wl_resource_get_version(surface->resource) >= + if (configure->tiled) { + if (wl_resource_get_version(toplevel->resource) >= XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION) { const struct { enum wlr_edges edge; @@ -85,8 +79,7 @@ void send_xdg_toplevel_configure(struct wlr_xdg_surface *surface, }; for (size_t i = 0; i < sizeof(tiled)/sizeof(tiled[0]); ++i) { - if ((surface->toplevel->scheduled.tiled & - tiled[i].edge) == 0) { + if ((configure->tiled & tiled[i].edge) == 0) { continue; } @@ -98,7 +91,7 @@ void send_xdg_toplevel_configure(struct wlr_xdg_surface *surface, } *s = tiled[i].state; } - } else if (!surface->toplevel->scheduled.maximized) { + } else if (!configure->maximized) { // This version doesn't support tiling, best we can do is make the // toplevel maximized uint32_t *s = wl_array_add(&states, sizeof(uint32_t)); @@ -111,36 +104,35 @@ void send_xdg_toplevel_configure(struct wlr_xdg_surface *surface, } } - uint32_t width = surface->toplevel->scheduled.width; - uint32_t height = surface->toplevel->scheduled.height; - xdg_toplevel_send_configure(surface->toplevel->resource, width, height, - &states); + uint32_t width = configure->width; + uint32_t height = configure->height; + xdg_toplevel_send_configure(toplevel->resource, width, height, &states); wl_array_release(&states); - return; + return configure; error_out: wl_array_release(&states); - wl_resource_post_no_memory(surface->toplevel->resource); + free(configure); + wl_resource_post_no_memory(toplevel->resource); + return NULL; } -void handle_xdg_surface_toplevel_committed(struct wlr_xdg_surface *surface) { - assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); - - if (!surface->toplevel->added) { +void handle_xdg_toplevel_committed(struct wlr_xdg_toplevel *toplevel) { + if (!toplevel->added) { // on the first commit, send a configure request to tell the client it // is added - wlr_xdg_surface_schedule_configure(surface); - surface->toplevel->added = true; + wlr_xdg_surface_schedule_configure(toplevel->base); + toplevel->added = true; return; } - surface->toplevel->current = surface->toplevel->pending; + toplevel->current = toplevel->pending; } static const struct xdg_toplevel_interface xdg_toplevel_implementation; -struct wlr_xdg_surface *wlr_xdg_surface_from_toplevel_resource( +struct wlr_xdg_toplevel *wlr_xdg_toplevel_from_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &xdg_toplevel_interface, &xdg_toplevel_implementation)); @@ -150,46 +142,42 @@ struct wlr_xdg_surface *wlr_xdg_surface_from_toplevel_resource( static void handle_parent_unmap(struct wl_listener *listener, void *data) { struct wlr_xdg_toplevel *toplevel = wl_container_of(listener, toplevel, parent_unmap); - wlr_xdg_toplevel_set_parent(toplevel->base, - toplevel->parent->toplevel->parent); + wlr_xdg_toplevel_set_parent(toplevel, toplevel->parent->parent); } -void wlr_xdg_toplevel_set_parent(struct wlr_xdg_surface *surface, - struct wlr_xdg_surface *parent) { - assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); - assert(!parent || parent->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); - - if (surface->toplevel->parent) { - wl_list_remove(&surface->toplevel->parent_unmap.link); +void wlr_xdg_toplevel_set_parent(struct wlr_xdg_toplevel *toplevel, + struct wlr_xdg_toplevel *parent) { + if (toplevel->parent) { + wl_list_remove(&toplevel->parent_unmap.link); + } + + toplevel->parent = parent; + if (parent) { + toplevel->parent_unmap.notify = handle_parent_unmap; + wl_signal_add(&toplevel->parent->base->events.unmap, + &toplevel->parent_unmap); } - surface->toplevel->parent = parent; - if (surface->toplevel->parent) { - surface->toplevel->parent_unmap.notify = handle_parent_unmap; - wl_signal_add(&surface->toplevel->parent->events.unmap, - &surface->toplevel->parent_unmap); - } - - wlr_signal_emit_safe(&surface->toplevel->events.set_parent, surface); + wlr_signal_emit_safe(&toplevel->events.set_parent, NULL); } static void xdg_toplevel_handle_set_parent(struct wl_client *client, struct wl_resource *resource, struct wl_resource *parent_resource) { - struct wlr_xdg_surface *surface = - wlr_xdg_surface_from_toplevel_resource(resource); - struct wlr_xdg_surface *parent = NULL; + struct wlr_xdg_toplevel *toplevel = + wlr_xdg_toplevel_from_resource(resource); + struct wlr_xdg_toplevel *parent = NULL; if (parent_resource != NULL) { - parent = wlr_xdg_surface_from_toplevel_resource(parent_resource); + parent = wlr_xdg_toplevel_from_resource(parent_resource); } - wlr_xdg_toplevel_set_parent(surface, parent); + wlr_xdg_toplevel_set_parent(toplevel, parent); } static void xdg_toplevel_handle_set_title(struct wl_client *client, struct wl_resource *resource, const char *title) { - struct wlr_xdg_surface *surface = - wlr_xdg_surface_from_toplevel_resource(resource); + struct wlr_xdg_toplevel *toplevel = + wlr_xdg_toplevel_from_resource(resource); char *tmp; tmp = strdup(title); @@ -197,15 +185,15 @@ static void xdg_toplevel_handle_set_title(struct wl_client *client, return; } - free(surface->toplevel->title); - surface->toplevel->title = tmp; - wlr_signal_emit_safe(&surface->toplevel->events.set_title, surface); + free(toplevel->title); + toplevel->title = tmp; + wlr_signal_emit_safe(&toplevel->events.set_title, NULL); } static void xdg_toplevel_handle_set_app_id(struct wl_client *client, struct wl_resource *resource, const char *app_id) { - struct wlr_xdg_surface *surface = - wlr_xdg_surface_from_toplevel_resource(resource); + struct wlr_xdg_toplevel *toplevel = + wlr_xdg_toplevel_from_resource(resource); char *tmp; tmp = strdup(app_id); @@ -213,21 +201,21 @@ static void xdg_toplevel_handle_set_app_id(struct wl_client *client, return; } - free(surface->toplevel->app_id); - surface->toplevel->app_id = tmp; - wlr_signal_emit_safe(&surface->toplevel->events.set_app_id, surface); + free(toplevel->app_id); + toplevel->app_id = tmp; + wlr_signal_emit_safe(&toplevel->events.set_app_id, NULL); } 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 *surface = - wlr_xdg_surface_from_toplevel_resource(resource); + struct wlr_xdg_toplevel *toplevel = + wlr_xdg_toplevel_from_resource(resource); struct wlr_seat_client *seat = wlr_seat_client_from_resource(seat_resource); - if (!surface->configured) { - wl_resource_post_error(surface->toplevel->resource, + if (!toplevel->base->configured) { + wl_resource_post_error(toplevel->base->resource, XDG_SURFACE_ERROR_NOT_CONSTRUCTED, "surface has not been configured yet"); return; @@ -239,26 +227,26 @@ static void xdg_toplevel_handle_show_window_menu(struct wl_client *client, } struct wlr_xdg_toplevel_show_window_menu_event event = { - .surface = surface, + .toplevel = toplevel, .seat = seat, .serial = serial, .x = x, .y = y, }; - wlr_signal_emit_safe(&surface->toplevel->events.request_show_window_menu, &event); + wlr_signal_emit_safe(&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 *surface = - wlr_xdg_surface_from_toplevel_resource(resource); + struct wlr_xdg_toplevel *toplevel = + wlr_xdg_toplevel_from_resource(resource); struct wlr_seat_client *seat = wlr_seat_client_from_resource(seat_resource); - if (!surface->configured) { - wl_resource_post_error(surface->toplevel->resource, + if (!toplevel->base->configured) { + wl_resource_post_error(toplevel->base->resource, XDG_SURFACE_ERROR_NOT_CONSTRUCTED, "surface has not been configured yet"); return; @@ -270,24 +258,24 @@ static void xdg_toplevel_handle_move(struct wl_client *client, } struct wlr_xdg_toplevel_move_event event = { - .surface = surface, + .toplevel = toplevel, .seat = seat, .serial = serial, }; - wlr_signal_emit_safe(&surface->toplevel->events.request_move, &event); + wlr_signal_emit_safe(&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 *surface = - wlr_xdg_surface_from_toplevel_resource(resource); + struct wlr_xdg_toplevel *toplevel = + wlr_xdg_toplevel_from_resource(resource); struct wlr_seat_client *seat = wlr_seat_client_from_resource(seat_resource); - if (!surface->configured) { - wl_resource_post_error(surface->toplevel->resource, + if (!toplevel->base->configured) { + wl_resource_post_error(toplevel->base->resource, XDG_SURFACE_ERROR_NOT_CONSTRUCTED, "surface has not been configured yet"); return; @@ -299,47 +287,47 @@ static void xdg_toplevel_handle_resize(struct wl_client *client, } struct wlr_xdg_toplevel_resize_event event = { - .surface = surface, + .toplevel = toplevel, .seat = seat, .serial = serial, .edges = edges, }; - wlr_signal_emit_safe(&surface->toplevel->events.request_resize, &event); + wlr_signal_emit_safe(&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 *surface = - wlr_xdg_surface_from_toplevel_resource(resource); - surface->toplevel->pending.max_width = width; - surface->toplevel->pending.max_height = height; + struct wlr_xdg_toplevel *toplevel = + wlr_xdg_toplevel_from_resource(resource); + toplevel->pending.max_width = width; + toplevel->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 *surface = - wlr_xdg_surface_from_toplevel_resource(resource); - surface->toplevel->pending.min_width = width; - surface->toplevel->pending.min_height = height; + struct wlr_xdg_toplevel *toplevel = + wlr_xdg_toplevel_from_resource(resource); + toplevel->pending.min_width = width; + toplevel->pending.min_height = height; } static void xdg_toplevel_handle_set_maximized(struct wl_client *client, struct wl_resource *resource) { - struct wlr_xdg_surface *surface = - wlr_xdg_surface_from_toplevel_resource(resource); - surface->toplevel->requested.maximized = true; - wlr_signal_emit_safe(&surface->toplevel->events.request_maximize, surface); - wlr_xdg_surface_schedule_configure(surface); + struct wlr_xdg_toplevel *toplevel = + wlr_xdg_toplevel_from_resource(resource); + toplevel->requested.maximized = true; + wlr_signal_emit_safe(&toplevel->events.request_maximize, NULL); + wlr_xdg_surface_schedule_configure(toplevel->base); } static void xdg_toplevel_handle_unset_maximized(struct wl_client *client, struct wl_resource *resource) { - struct wlr_xdg_surface *surface = - wlr_xdg_surface_from_toplevel_resource(resource); - surface->toplevel->requested.maximized = false; - wlr_signal_emit_safe(&surface->toplevel->events.request_maximize, surface); - wlr_xdg_surface_schedule_configure(surface); + struct wlr_xdg_toplevel *toplevel = + wlr_xdg_toplevel_from_resource(resource); + toplevel->requested.maximized = false; + wlr_signal_emit_safe(&toplevel->events.request_maximize, NULL); + wlr_xdg_surface_schedule_configure(toplevel->base); } static void handle_fullscreen_output_destroy(struct wl_listener *listener, @@ -350,9 +338,9 @@ static void handle_fullscreen_output_destroy(struct wl_listener *listener, wl_list_remove(&req->fullscreen_output_destroy.link); } -static void store_fullscreen_requested(struct wlr_xdg_surface *surface, +static void store_fullscreen_requested(struct wlr_xdg_toplevel *toplevel, bool fullscreen, struct wlr_output *output) { - struct wlr_xdg_toplevel_requested *req = &surface->toplevel->requested; + struct wlr_xdg_toplevel_requested *req = &toplevel->requested; req->fullscreen = fullscreen; if (req->fullscreen_output) { wl_list_remove(&req->fullscreen_output_destroy.link); @@ -368,49 +356,37 @@ static void store_fullscreen_requested(struct wlr_xdg_surface *surface, static void xdg_toplevel_handle_set_fullscreen(struct wl_client *client, struct wl_resource *resource, struct wl_resource *output_resource) { - struct wlr_xdg_surface *surface = - wlr_xdg_surface_from_toplevel_resource(resource); + struct wlr_xdg_toplevel *toplevel = + wlr_xdg_toplevel_from_resource(resource); struct wlr_output *output = NULL; if (output_resource != NULL) { output = wlr_output_from_resource(output_resource); } - store_fullscreen_requested(surface, true, output); + store_fullscreen_requested(toplevel, true, output); - struct wlr_xdg_toplevel_set_fullscreen_event event = { - .surface = surface, - .fullscreen = true, - .output = output, - }; - - wlr_signal_emit_safe(&surface->toplevel->events.request_fullscreen, &event); - wlr_xdg_surface_schedule_configure(surface); + wlr_signal_emit_safe(&toplevel->events.request_fullscreen, NULL); + wlr_xdg_surface_schedule_configure(toplevel->base); } static void xdg_toplevel_handle_unset_fullscreen(struct wl_client *client, struct wl_resource *resource) { - struct wlr_xdg_surface *surface = - wlr_xdg_surface_from_toplevel_resource(resource); + struct wlr_xdg_toplevel *toplevel = + wlr_xdg_toplevel_from_resource(resource); - store_fullscreen_requested(surface, false, NULL); + store_fullscreen_requested(toplevel, false, NULL); - struct wlr_xdg_toplevel_set_fullscreen_event event = { - .surface = surface, - .fullscreen = false, - .output = NULL, - }; - - wlr_signal_emit_safe(&surface->toplevel->events.request_fullscreen, &event); - wlr_xdg_surface_schedule_configure(surface); + wlr_signal_emit_safe(&toplevel->events.request_fullscreen, NULL); + wlr_xdg_surface_schedule_configure(toplevel->base); } static void xdg_toplevel_handle_set_minimized(struct wl_client *client, struct wl_resource *resource) { - struct wlr_xdg_surface *surface = - wlr_xdg_surface_from_toplevel_resource(resource); - surface->toplevel->requested.minimized = true; - wlr_signal_emit_safe(&surface->toplevel->events.request_minimize, surface); + struct wlr_xdg_toplevel *toplevel = + wlr_xdg_toplevel_from_resource(resource); + toplevel->requested.minimized = true; + wlr_signal_emit_safe(&toplevel->events.request_minimize, NULL); } static void xdg_toplevel_handle_destroy(struct wl_client *client, @@ -436,117 +412,129 @@ static const struct xdg_toplevel_interface xdg_toplevel_implementation = { }; static void xdg_toplevel_handle_resource_destroy(struct wl_resource *resource) { - struct wlr_xdg_surface *surface = - wlr_xdg_surface_from_toplevel_resource(resource); - destroy_xdg_toplevel(surface); + struct wlr_xdg_toplevel *toplevel = + wlr_xdg_toplevel_from_resource(resource); + if (toplevel == NULL) { + return; + } + reset_xdg_surface(toplevel->base); } const struct wlr_surface_role xdg_toplevel_surface_role = { .name = "xdg_toplevel", - .commit = handle_xdg_surface_commit, - .precommit = handle_xdg_surface_precommit, + .commit = xdg_surface_role_commit, + .precommit = xdg_surface_role_precommit, }; -void create_xdg_toplevel(struct wlr_xdg_surface *xdg_surface, +void create_xdg_toplevel(struct wlr_xdg_surface *surface, uint32_t id) { - if (!wlr_surface_set_role(xdg_surface->surface, &xdg_toplevel_surface_role, - xdg_surface, xdg_surface->resource, XDG_WM_BASE_ERROR_ROLE)) { + if (!wlr_surface_set_role(surface->surface, &xdg_toplevel_surface_role, + surface, surface->resource, XDG_WM_BASE_ERROR_ROLE)) { return; } - if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_NONE) { - wl_resource_post_error(xdg_surface->resource, + if (surface->role != WLR_XDG_SURFACE_ROLE_NONE) { + wl_resource_post_error(surface->resource, XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED, "xdg-surface has already been constructed"); return; } - assert(xdg_surface->toplevel == NULL); - xdg_surface->toplevel = calloc(1, sizeof(struct wlr_xdg_toplevel)); - if (xdg_surface->toplevel == NULL) { - wl_resource_post_no_memory(xdg_surface->resource); + assert(surface->toplevel == NULL); + surface->toplevel = calloc(1, sizeof(struct wlr_xdg_toplevel)); + if (surface->toplevel == NULL) { + wl_resource_post_no_memory(surface->resource); return; } - xdg_surface->toplevel->base = xdg_surface; + surface->toplevel->base = surface; - 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); + wl_signal_init(&surface->toplevel->events.request_maximize); + wl_signal_init(&surface->toplevel->events.request_fullscreen); + wl_signal_init(&surface->toplevel->events.request_minimize); + wl_signal_init(&surface->toplevel->events.request_move); + wl_signal_init(&surface->toplevel->events.request_resize); + wl_signal_init(&surface->toplevel->events.request_show_window_menu); + wl_signal_init(&surface->toplevel->events.set_parent); + wl_signal_init(&surface->toplevel->events.set_title); + wl_signal_init(&surface->toplevel->events.set_app_id); - xdg_surface->toplevel->resource = wl_resource_create( - xdg_surface->client->client, &xdg_toplevel_interface, - wl_resource_get_version(xdg_surface->resource), id); - if (xdg_surface->toplevel->resource == NULL) { - free(xdg_surface->toplevel); - wl_resource_post_no_memory(xdg_surface->resource); + surface->toplevel->resource = wl_resource_create( + surface->client->client, &xdg_toplevel_interface, + wl_resource_get_version(surface->resource), id); + if (surface->toplevel->resource == NULL) { + free(surface->toplevel); + surface->toplevel = NULL; + wl_resource_post_no_memory(surface->resource); return; } - wl_resource_set_implementation(xdg_surface->toplevel->resource, - &xdg_toplevel_implementation, xdg_surface, + wl_resource_set_implementation(surface->toplevel->resource, + &xdg_toplevel_implementation, surface->toplevel, xdg_toplevel_handle_resource_destroy); - xdg_surface->role = WLR_XDG_SURFACE_ROLE_TOPLEVEL; + surface->role = WLR_XDG_SURFACE_ROLE_TOPLEVEL; } -void destroy_xdg_toplevel(struct wlr_xdg_surface *xdg_surface) { - if (xdg_surface == NULL) { - return; +void unmap_xdg_toplevel(struct wlr_xdg_toplevel *toplevel) { + if (toplevel->parent) { + wl_list_remove(&toplevel->parent_unmap.link); + toplevel->parent = NULL; } - assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); - reset_xdg_surface(xdg_surface); + free(toplevel->title); + toplevel->title = NULL; + free(toplevel->app_id); + toplevel->app_id = NULL; + + if (toplevel->requested.fullscreen_output) { + wl_list_remove(&toplevel->requested.fullscreen_output_destroy.link); + toplevel->requested.fullscreen_output = NULL; + } + toplevel->requested.fullscreen = false; + toplevel->requested.maximized = false; + toplevel->requested.minimized = false; } -uint32_t wlr_xdg_toplevel_set_size(struct wlr_xdg_surface *surface, +void destroy_xdg_toplevel(struct wlr_xdg_toplevel *toplevel) { + wl_resource_set_user_data(toplevel->resource, NULL); + free(toplevel); +} + +void wlr_xdg_toplevel_send_close(struct wlr_xdg_toplevel *toplevel) { + xdg_toplevel_send_close(toplevel->resource); +} + +uint32_t wlr_xdg_toplevel_set_size(struct wlr_xdg_toplevel *toplevel, uint32_t width, uint32_t height) { - assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); - surface->toplevel->scheduled.width = width; - surface->toplevel->scheduled.height = height; - - return wlr_xdg_surface_schedule_configure(surface); + toplevel->scheduled.width = width; + toplevel->scheduled.height = height; + return wlr_xdg_surface_schedule_configure(toplevel->base); } -uint32_t wlr_xdg_toplevel_set_activated(struct wlr_xdg_surface *surface, +uint32_t wlr_xdg_toplevel_set_activated(struct wlr_xdg_toplevel *toplevel, bool activated) { - assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); - surface->toplevel->scheduled.activated = activated; - - return wlr_xdg_surface_schedule_configure(surface); + toplevel->scheduled.activated = activated; + return wlr_xdg_surface_schedule_configure(toplevel->base); } -uint32_t wlr_xdg_toplevel_set_maximized(struct wlr_xdg_surface *surface, +uint32_t wlr_xdg_toplevel_set_maximized(struct wlr_xdg_toplevel *toplevel, bool maximized) { - assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); - surface->toplevel->scheduled.maximized = maximized; - - return wlr_xdg_surface_schedule_configure(surface); + toplevel->scheduled.maximized = maximized; + return wlr_xdg_surface_schedule_configure(toplevel->base); } -uint32_t wlr_xdg_toplevel_set_fullscreen(struct wlr_xdg_surface *surface, +uint32_t wlr_xdg_toplevel_set_fullscreen(struct wlr_xdg_toplevel *toplevel, bool fullscreen) { - assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); - surface->toplevel->scheduled.fullscreen = fullscreen; - - return wlr_xdg_surface_schedule_configure(surface); + toplevel->scheduled.fullscreen = fullscreen; + return wlr_xdg_surface_schedule_configure(toplevel->base); } -uint32_t wlr_xdg_toplevel_set_resizing(struct wlr_xdg_surface *surface, +uint32_t wlr_xdg_toplevel_set_resizing(struct wlr_xdg_toplevel *toplevel, bool resizing) { - assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); - surface->toplevel->scheduled.resizing = resizing; - - return wlr_xdg_surface_schedule_configure(surface); + toplevel->scheduled.resizing = resizing; + return wlr_xdg_surface_schedule_configure(toplevel->base); } -uint32_t wlr_xdg_toplevel_set_tiled(struct wlr_xdg_surface *surface, +uint32_t wlr_xdg_toplevel_set_tiled(struct wlr_xdg_toplevel *toplevel, uint32_t tiled) { - assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); - surface->toplevel->scheduled.tiled = tiled; - - return wlr_xdg_surface_schedule_configure(surface); + toplevel->scheduled.tiled = tiled; + return wlr_xdg_surface_schedule_configure(toplevel->base); } diff --git a/util/global.c b/util/global.c index a0d84ed30..fa99a9946 100644 --- a/util/global.c +++ b/util/global.c @@ -14,8 +14,7 @@ static int destroy_global(void *_data) { return 0; } -void wlr_global_destroy_safe(struct wl_global *global, - struct wl_display *display) { +void wlr_global_destroy_safe(struct wl_global *global) { // Don't destroy the global immediately. If the global has been created // recently, clients might try to bind to it after we've destroyed it. // Instead, remove the global so that clients stop seeing it and wait an @@ -25,6 +24,7 @@ void wlr_global_destroy_safe(struct wl_global *global, wl_global_remove(global); wl_global_set_user_data(global, NULL); // safety net + struct wl_display *display = wl_global_get_display(global); struct wl_event_loop *event_loop = wl_display_get_event_loop(display); struct destroy_global_data *data = calloc(1, sizeof(*data)); if (data == NULL) { diff --git a/util/token.c b/util/token.c index cf6034a32..1b839aaa6 100644 --- a/util/token.c +++ b/util/token.c @@ -1,20 +1,31 @@ +#define _POSIX_C_SOURCE 200809L #include "util/token.h" #include "wlr/util/log.h" +#include #include #include #include #include +#include +#include +#include bool generate_token(char out[static TOKEN_STRLEN]) { static FILE *urandom = NULL; uint64_t data[2]; if (!urandom) { - if (!(urandom = fopen("/dev/urandom", "r"))) { + int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC); + if (fd < 0) { wlr_log_errno(WLR_ERROR, "Failed to open random device"); return false; } + if (!(urandom = fdopen(fd, "r"))) { + wlr_log_errno(WLR_ERROR, "fdopen failed"); + close(fd); + return false; + } } if (fread(data, sizeof(data), 1, urandom) != 1) { wlr_log_errno(WLR_ERROR, "Failed to read from random device"); diff --git a/xcursor/xcursor.c b/xcursor/xcursor.c index 4415a6596..293d6e9d8 100644 --- a/xcursor/xcursor.c +++ b/xcursor/xcursor.c @@ -795,85 +795,6 @@ _XcursorThemeInherits (const char *full) return result; } -static FILE * -XcursorScanTheme (const char *theme, const char *name) -{ - FILE *f = NULL; - char *full; - char *dir; - const char *path; - char *inherits = NULL; - const char *i; - - if (!theme || !name) - return NULL; - - /* - * Scan this theme - */ - for (path = XcursorLibraryPath (); - path && f == NULL; - path = _XcursorNextPath (path)) - { - dir = _XcursorBuildThemeDir (path, theme); - if (dir) - { - full = _XcursorBuildFullname (dir, "cursors", name); - if (full) - { - f = fopen (full, "r"); - free (full); - } - if (!f && !inherits) - { - full = _XcursorBuildFullname (dir, "", "index.theme"); - if (full) - { - inherits = _XcursorThemeInherits (full); - free (full); - } - } - free (dir); - } - } - /* - * Recurse to scan inherited themes - */ - for (i = inherits; i && f == NULL; i = _XcursorNextPath (i)) - { - if (strcmp(i, theme) != 0) - f = XcursorScanTheme (i, name); - else - printf("Not calling XcursorScanTheme because of circular dependency: %s. %s", i, name); - } - if (inherits != NULL) - free (inherits); - return f; -} - -XcursorImages * -XcursorLibraryLoadImages (const char *file, const char *theme, int size) -{ - FILE *f = NULL; - XcursorImages *images = NULL; - - if (!file) - return NULL; - - if (theme) - f = XcursorScanTheme (theme, file); - if (!f) - f = XcursorScanTheme ("default", file); - if (f) - { - images = XcursorFileLoadImages (f, size); - if (images) - XcursorImagesSetName (images, file); - fclose (f); - } - return images; -} - static void load_all_cursors_from_dir(const char *path, int size, void (*load_callback)(XcursorImages *, void *), diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 313bfc0a7..0c1992690 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -5,9 +5,9 @@ #include #include #include +#include #include #include -#include #include #include #include @@ -852,15 +852,15 @@ static void xwayland_surface_role_commit(struct wlr_surface *wlr_surface) { } } -static void xwayland_surface_role_precommit(struct wlr_surface *wlr_surface) { +static void xwayland_surface_role_precommit(struct wlr_surface *wlr_surface, + const struct wlr_surface_state *state) { assert(wlr_surface->role == &xwayland_surface_role); struct wlr_xwayland_surface *surface = wlr_surface->role_data; if (surface == NULL) { return; } - if (wlr_surface->pending.committed & WLR_SURFACE_STATE_BUFFER && - wlr_surface->pending.buffer == NULL) { + if (state->committed & WLR_SURFACE_STATE_BUFFER && state->buffer == NULL) { // This is a NULL commit if (surface->mapped) { wlr_signal_emit_safe(&surface->events.unmap, surface);