diff options
52 files changed, 1484 insertions, 428 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index aca308b3..c6110522 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -236,7 +236,7 @@ error_session: ## Wayland protocol implementation Each protocol generally lives in a file with the same name, usually containing -at leats one struct for each interface in the protocol. For instance, +at least one struct for each interface in the protocol. For instance, `xdg_shell` lives in `types/wlr_xdg_shell.h` and has a `wlr_xdg_surface` struct. ### Globals @@ -334,9 +334,9 @@ static void subsurface_destroy(struct wlr_subsurface *subsurface) { return; } - wl_resource_set_user_data(subsurface->resource, NULL); - … + + wl_resource_set_user_data(subsurface->resource, NULL); free(subsurface); } diff --git a/backend/backend.c b/backend/backend.c index 07c171bc..07e05fca 100644 --- a/backend/backend.c +++ b/backend/backend.c @@ -203,6 +203,7 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display, wlr_log(L_ERROR, "failed to start backend '%s'", name); wlr_backend_destroy(backend); wlr_session_destroy(session); + free(names); return NULL; } @@ -210,12 +211,14 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display, wlr_log(L_ERROR, "failed to add backend '%s'", name); wlr_backend_destroy(backend); wlr_session_destroy(session); + free(names); return NULL; } name = strtok_r(NULL, ",", &saveptr); } + free(names); return backend; } diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c index acc56e65..41b6424c 100644 --- a/backend/drm/atomic.c +++ b/backend/drm/atomic.c @@ -83,8 +83,8 @@ static void set_plane_props(struct atomic *atom, struct wlr_drm_plane *plane, // The src_* properties are in 16.16 fixed point atomic_add(atom, id, props->src_x, 0); atomic_add(atom, id, props->src_y, 0); - atomic_add(atom, id, props->src_w, plane->surf.width << 16); - atomic_add(atom, id, props->src_h, plane->surf.height << 16); + atomic_add(atom, id, props->src_w, (uint64_t)plane->surf.width << 16); + atomic_add(atom, id, props->src_h, (uint64_t)plane->surf.height << 16); atomic_add(atom, id, props->crtc_w, plane->surf.width); atomic_add(atom, id, props->crtc_h, plane->surf.height); atomic_add(atom, id, props->fb_id, fb_id); diff --git a/backend/drm/drm.c b/backend/drm/drm.c index 39906342..05d3d1bd 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -710,7 +710,7 @@ static bool drm_connector_set_cursor(struct wlr_output *output, wlr_render_texture_with_matrix(rend, texture, matrix, 1.0); wlr_renderer_end(rend); - wlr_renderer_read_pixels(rend, WL_SHM_FORMAT_ARGB8888, bo_stride, + wlr_renderer_read_pixels(rend, WL_SHM_FORMAT_ARGB8888, NULL, bo_stride, plane->surf.width, plane->surf.height, 0, 0, 0, 0, bo_data); swap_drm_surface_buffers(&plane->surf, NULL); @@ -1000,7 +1000,7 @@ int handle_drm_event(int fd, uint32_t mask, void *data) { } void restore_drm_outputs(struct wlr_drm_backend *drm) { - uint64_t to_close = (1 << wl_list_length(&drm->outputs)) - 1; + uint64_t to_close = (1L << wl_list_length(&drm->outputs)) - 1; struct wlr_drm_connector *conn; wl_list_for_each(conn, &drm->outputs, link) { diff --git a/backend/headless/input_device.c b/backend/headless/input_device.c index a1e18428..63d28e8e 100644 --- a/backend/headless/input_device.c +++ b/backend/headless/input_device.c @@ -39,7 +39,7 @@ struct wlr_input_device *wlr_headless_add_input_device( wlr_device->keyboard = calloc(1, sizeof(struct wlr_keyboard)); if (wlr_device->keyboard == NULL) { wlr_log(L_ERROR, "Unable to allocate wlr_keyboard"); - return NULL; + goto error; } wlr_keyboard_init(wlr_device->keyboard, NULL); break; @@ -47,7 +47,7 @@ struct wlr_input_device *wlr_headless_add_input_device( wlr_device->pointer = calloc(1, sizeof(struct wlr_pointer)); if (wlr_device->pointer == NULL) { wlr_log(L_ERROR, "Unable to allocate wlr_pointer"); - return NULL; + goto error; } wlr_pointer_init(wlr_device->pointer, NULL); break; @@ -55,7 +55,7 @@ struct wlr_input_device *wlr_headless_add_input_device( wlr_device->touch = calloc(1, sizeof(struct wlr_touch)); if (wlr_device->touch == NULL) { wlr_log(L_ERROR, "Unable to allocate wlr_touch"); - return NULL; + goto error; } wlr_touch_init(wlr_device->touch, NULL); break; @@ -63,7 +63,7 @@ struct wlr_input_device *wlr_headless_add_input_device( wlr_device->tablet_tool = calloc(1, sizeof(struct wlr_tablet_tool)); if (wlr_device->tablet_tool == NULL) { wlr_log(L_ERROR, "Unable to allocate wlr_tablet_tool"); - return NULL; + goto error; } wlr_tablet_tool_init(wlr_device->tablet_tool, NULL); break; @@ -71,7 +71,7 @@ struct wlr_input_device *wlr_headless_add_input_device( wlr_device->tablet_pad = calloc(1, sizeof(struct wlr_tablet_pad)); if (wlr_device->tablet_pad == NULL) { wlr_log(L_ERROR, "Unable to allocate wlr_tablet_pad"); - return NULL; + goto error; } wlr_tablet_pad_init(wlr_device->tablet_pad, NULL); break; @@ -84,4 +84,7 @@ struct wlr_input_device *wlr_headless_add_input_device( } return wlr_device; +error: + free(device); + return NULL; } diff --git a/backend/session/direct-ipc.c b/backend/session/direct-ipc.c index f8ba07f7..2dd777c8 100644 --- a/backend/session/direct-ipc.c +++ b/backend/session/direct-ipc.c @@ -159,7 +159,9 @@ static void communicate(int sock) { } error: send_msg(sock, ret ? -1 : fd, &ret, sizeof(ret)); - close(fd); + if (fd >= 0) { + close(fd); + } break; diff --git a/backend/session/logind.c b/backend/session/logind.c index 3a3cc57d..e01047e3 100644 --- a/backend/session/logind.c +++ b/backend/session/logind.c @@ -104,6 +104,7 @@ static void logind_release_device(struct wlr_session *base, int fd) { sd_bus_error_free(&error); sd_bus_message_unref(msg); + close(fd); } static bool logind_change_vt(struct wlr_session *base, unsigned vt) { diff --git a/backend/wayland/output.c b/backend/wayland/output.c index 42b41508..6aa59537 100644 --- a/backend/wayland/output.c +++ b/backend/wayland/output.c @@ -220,7 +220,7 @@ static void xdg_toplevel_handle_configure(void *data, struct zxdg_toplevel_v6 *x struct wlr_wl_output *output = data; assert(output && output->xdg_toplevel == xdg_toplevel); - if (width == 0 && height == 0) { + if (width == 0 || height == 0) { return; } // loop over states for maximized etc? diff --git a/backend/wayland/wl_seat.c b/backend/wayland/wl_seat.c index 8ed61409..d5001a51 100644 --- a/backend/wayland/wl_seat.c +++ b/backend/wayland/wl_seat.c @@ -38,10 +38,8 @@ static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer, } struct wlr_wl_output *output = wl_surface_get_user_data(surface); + assert(output); struct wlr_wl_pointer *pointer = output_get_pointer(output); - if (output == NULL) { - return; - } output->enter_serial = serial; backend->current_pointer = pointer; @@ -56,6 +54,7 @@ static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer, } struct wlr_wl_output *output = wl_surface_get_user_data(surface); + assert(output); output->enter_serial = 0; if (backend->current_pointer == NULL || diff --git a/backend/x11/backend.c b/backend/x11/backend.c index d4793b9c..e35cbed7 100644 --- a/backend/x11/backend.c +++ b/backend/x11/backend.c @@ -245,13 +245,13 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, x11->xlib_conn = XOpenDisplay(x11_display); if (!x11->xlib_conn) { wlr_log(L_ERROR, "Failed to open X connection"); - return NULL; + goto error_x11; } x11->xcb_conn = XGetXCBConnection(x11->xlib_conn); if (!x11->xcb_conn || xcb_connection_has_error(x11->xcb_conn)) { wlr_log(L_ERROR, "Failed to open xcb connection"); - goto error_x11; + goto error_display; } XSetEventQueueOwner(x11->xlib_conn, XCBOwnsEventQueue); @@ -262,7 +262,7 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, x11->event_source = wl_event_loop_add_fd(ev, fd, events, x11_event, x11); if (!x11->event_source) { wlr_log(L_ERROR, "Could not create event source"); - goto error_x11; + goto error_display; } x11->screen = xcb_setup_roots_iterator(xcb_get_setup(x11->xcb_conn)).data; @@ -291,8 +291,9 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, error_event: wl_event_source_remove(x11->event_source); -error_x11: +error_display: XCloseDisplay(x11->xlib_conn); +error_x11: free(x11); return NULL; } diff --git a/examples/meson.build b/examples/meson.build index d8df0bfc..18f44fde 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -1,6 +1,8 @@ threads = dependency('threads') wayland_cursor = dependency('wayland-cursor') +libpng = dependency('libpng', required: false) + # These versions correspond to ffmpeg 4.0 libavutil = dependency('libavutil', version: '>=56.14.100', required: false) libavcodec = dependency('libavcodec', version: '>=58.18.100', required: false) @@ -56,3 +58,11 @@ if libavutil.found() and libavcodec.found() and libavformat.found() libavformat, wlroots, threads ] ) endif + +if libpng.found() + executable( + 'screencopy', + 'screencopy.c', + dependencies: [wayland_client, wlr_protos, wlroots, libpng] + ) +endif diff --git a/examples/screencopy.c b/examples/screencopy.c new file mode 100644 index 00000000..4e58cd3b --- /dev/null +++ b/examples/screencopy.c @@ -0,0 +1,270 @@ +/* + * Copyright © 2008 Kristian Høgsberg + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#define _XOPEN_SOURCE 700 +#define _POSIX_C_SOURCE 199309L +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <png.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/param.h> +#include <sys/wait.h> +#include <unistd.h> +#include <wayland-client-protocol.h> +#include "wlr-screencopy-unstable-v1-client-protocol.h" + +struct format { + enum wl_shm_format wl_format; + bool is_bgr; +}; + +static struct wl_shm *shm = NULL; +static struct zwlr_screencopy_manager_v1 *screencopy_manager = NULL; +static struct wl_output *output = NULL; + +static struct { + struct wl_buffer *wl_buffer; + void *data; + enum wl_shm_format format; + int width, height, stride; + bool y_invert; +} buffer; +bool buffer_copy_done = false; + +// wl_shm_format describes little-endian formats, libpng uses big-endian +// formats (so Wayland's ABGR is libpng's RGBA). +static const struct format formats[] = { + {WL_SHM_FORMAT_XRGB8888, true}, + {WL_SHM_FORMAT_ARGB8888, true}, + {WL_SHM_FORMAT_XBGR8888, false}, + {WL_SHM_FORMAT_ABGR8888, false}, +}; + +static int backingfile(off_t size) { + char template[] = "/tmp/wlroots-shared-XXXXXX"; + int fd = mkstemp(template); + if (fd < 0) { + return -1; + } + + int ret; + while ((ret = ftruncate(fd, size)) == EINTR) { + // No-op + } + if (ret < 0) { + close(fd); + return -1; + } + + unlink(template); + return fd; +} + +static struct wl_buffer *create_shm_buffer(enum wl_shm_format fmt, + int width, int height, int stride, void **data_out) { + int size = stride * height; + + int fd = backingfile(size); + if (fd < 0) { + fprintf(stderr, "creating a buffer file for %d B failed: %m\n", size); + return NULL; + } + + void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (data == MAP_FAILED) { + fprintf(stderr, "mmap failed: %m\n"); + close(fd); + return NULL; + } + + struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size); + close(fd); + struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, width, height, + stride, fmt); + wl_shm_pool_destroy(pool); + + *data_out = data; + return buffer; +} + +static void frame_handle_buffer(void *data, + struct zwlr_screencopy_frame_v1 *frame, uint32_t format, + uint32_t width, uint32_t height, uint32_t stride) { + buffer.format = format; + buffer.width = width; + buffer.height = height; + buffer.stride = stride; + buffer.wl_buffer = + create_shm_buffer(format, width, height, stride, &buffer.data); + if (buffer.wl_buffer == NULL) { + fprintf(stderr, "failed to create buffer\n"); + exit(EXIT_FAILURE); + } + + zwlr_screencopy_frame_v1_copy(frame, buffer.wl_buffer); +} + +static void frame_handle_flags(void *data, + struct zwlr_screencopy_frame_v1 *frame, uint32_t flags) { + buffer.y_invert = flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT; +} + +static void frame_handle_ready(void *data, + struct zwlr_screencopy_frame_v1 *frame, uint32_t tv_sec_hi, + uint32_t tv_sec_lo, uint32_t tv_nsec) { + buffer_copy_done = true; +} + +static void frame_handle_failed(void *data, + struct zwlr_screencopy_frame_v1 *frame) { + fprintf(stderr, "failed to copy frame\n"); + exit(EXIT_FAILURE); +} + +static const struct zwlr_screencopy_frame_v1_listener frame_listener = { + .buffer = frame_handle_buffer, + .flags = frame_handle_flags, + .ready = frame_handle_ready, + .failed = frame_handle_failed, +}; + +static void handle_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) { + if (strcmp(interface, wl_output_interface.name) == 0 && output == NULL) { + output = wl_registry_bind(registry, name, &wl_output_interface, 1); + } else if (strcmp(interface, wl_shm_interface.name) == 0) { + shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); + } else if (strcmp(interface, + zwlr_screencopy_manager_v1_interface.name) == 0) { + screencopy_manager = wl_registry_bind(registry, name, + &zwlr_screencopy_manager_v1_interface, 1); + } +} + +static void handle_global_remove(void *data, struct wl_registry *registry, + uint32_t name) { + // Who cares? +} + +static const struct wl_registry_listener registry_listener = { + .global = handle_global, + .global_remove = handle_global_remove, +}; + +static void write_image(char *filename, enum wl_shm_format wl_fmt, int width, + int height, int stride, bool y_invert, png_bytep data) { + const struct format *fmt = NULL; + for (size_t i = 0; i < sizeof(formats) / sizeof(formats[0]); ++i) { + if (formats[i].wl_format == wl_fmt) { + fmt = &formats[i]; + break; + } + } + if (fmt == NULL) { + fprintf(stderr, "unsupported format %"PRIu32"\n", wl_fmt); + exit(EXIT_FAILURE); + } + + FILE *f = fopen(filename, "wb"); + if (f == NULL) { + fprintf(stderr, "failed to open output file\n"); + exit(EXIT_FAILURE); + } + + png_structp png = + png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + png_infop info = png_create_info_struct(png); + + png_init_io(png, f); + + png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_RGBA, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + if (fmt->is_bgr) { + png_set_bgr(png); + } + + png_write_info(png, info); + + for (size_t i = 0; i < (size_t)height; ++i) { + png_bytep row; + if (y_invert) { + row = data + (height - i - 1) * stride; + } else { + row = data + i * stride; + } + png_write_row(png, row); + } + + png_write_end(png, NULL); + + png_destroy_write_struct(&png, &info); + + fclose(f); +} + +int main(int argc, char *argv[]) { + struct wl_display * display = wl_display_connect(NULL); + if (display == NULL) { + fprintf(stderr, "failed to create display: %m\n"); + return EXIT_FAILURE; + } + + struct wl_registry *registry = wl_display_get_registry(display); + wl_registry_add_listener(registry, ®istry_listener, NULL); + wl_display_dispatch(display); + wl_display_roundtrip(display); + + if (shm == NULL) { + fprintf(stderr, "compositor is missing wl_shm\n"); + return EXIT_FAILURE; + } + if (screencopy_manager == NULL) { + fprintf(stderr, "compositor doesn't support wlr-screencopy-unstable-v1\n"); + return EXIT_FAILURE; + } + if (output == NULL) { + fprintf(stderr, "no output available\n"); + return EXIT_FAILURE; + } + + struct zwlr_screencopy_frame_v1 *frame = + zwlr_screencopy_manager_v1_capture_output(screencopy_manager, 0, output); + zwlr_screencopy_frame_v1_add_listener(frame, &frame_listener, NULL); + + while (!buffer_copy_done && wl_display_dispatch(display) != -1) { + // This space is intentionally left blank + } + + write_image("wayland-screenshot.png", buffer.format, buffer.width, + buffer.height, buffer.stride, buffer.y_invert, buffer.data); + wl_buffer_destroy(buffer.wl_buffer); + + return EXIT_SUCCESS; +} diff --git a/include/rootston/desktop.h b/include/rootston/desktop.h index dfe070ca..88c5ca90 100644 --- a/include/rootston/desktop.h +++ b/include/rootston/desktop.h @@ -22,6 +22,7 @@ #include <wlr/types/wlr_list.h> #include <wlr/types/wlr_idle.h> #include <wlr/types/wlr_idle_inhibit_v1.h> +#include <wlr/types/wlr_screencopy_v1.h> #include "rootston/view.h" #include "rootston/config.h" #include "rootston/output.h" @@ -54,6 +55,7 @@ struct roots_desktop { struct wlr_linux_dmabuf *linux_dmabuf; struct wlr_layer_shell *layer_shell; struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard; + struct wlr_screencopy_manager_v1 *screencopy; struct wl_listener new_output; struct wl_listener layout_change; diff --git a/include/wlr/render/interface.h b/include/wlr/render/interface.h index fb427c89..1b138ea3 100644 --- a/include/wlr/render/interface.h +++ b/include/wlr/render/interface.h @@ -34,7 +34,7 @@ struct wlr_renderer_impl { int (*get_dmabuf_modifiers)(struct wlr_renderer *renderer, int format, uint64_t **modifiers); bool (*read_pixels)(struct wlr_renderer *renderer, enum wl_shm_format fmt, - uint32_t stride, uint32_t width, uint32_t height, + uint32_t *flags, uint32_t stride, uint32_t width, uint32_t height, uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y, void *data); bool (*format_supported)(struct wlr_renderer *renderer, diff --git a/include/wlr/render/wlr_renderer.h b/include/wlr/render/wlr_renderer.h index c715e4b0..0650bf1b 100644 --- a/include/wlr/render/wlr_renderer.h +++ b/include/wlr/render/wlr_renderer.h @@ -7,6 +7,10 @@ #include <wlr/render/wlr_texture.h> #include <wlr/types/wlr_box.h> +enum wlr_renderer_read_pixels_flags { + WLR_RENDERER_READ_PIXELS_Y_INVERT = 1, +}; + struct wlr_renderer_impl; struct wlr_renderer { @@ -87,9 +91,12 @@ int wlr_renderer_get_dmabuf_modifiers(struct wlr_renderer *renderer, int format, /** * Reads out of pixels of the currently bound surface into data. `stride` is in * bytes. + * + * If `flags` is not NULl, the caller indicates that it accepts frame flags + * defined in `enum wlr_renderer_read_pixels_flags`. */ bool wlr_renderer_read_pixels(struct wlr_renderer *r, enum wl_shm_format fmt, - uint32_t stride, uint32_t width, uint32_t height, + uint32_t *flags, uint32_t stride, uint32_t width, uint32_t height, uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y, void *data); /** * Checks if a format is supported. diff --git a/include/wlr/types/wlr_compositor.h b/include/wlr/types/wlr_compositor.h index f6be0a74..51702884 100644 --- a/include/wlr/types/wlr_compositor.h +++ b/include/wlr/types/wlr_compositor.h @@ -31,10 +31,11 @@ struct wlr_compositor { void wlr_compositor_destroy(struct wlr_compositor *wlr_compositor); struct wlr_compositor *wlr_compositor_create(struct wl_display *display, - struct wlr_renderer *renderer); + struct wlr_renderer *renderer); bool wlr_surface_is_subsurface(struct wlr_surface *surface); -struct wlr_subsurface *wlr_subsurface_from_surface(struct wlr_surface *surface); +struct wlr_subsurface *wlr_subsurface_from_wlr_surface( + struct wlr_surface *surface); #endif diff --git a/include/wlr/types/wlr_idle.h b/include/wlr/types/wlr_idle.h index 45f350be..aca9146c 100644 --- a/include/wlr/types/wlr_idle.h +++ b/include/wlr/types/wlr_idle.h @@ -17,6 +17,7 @@ struct wlr_idle { struct wl_global *wl_global; struct wl_list idle_timers; // wlr_idle_timeout::link struct wl_event_loop *event_loop; + bool enabled; struct wl_listener display_destroy; struct { @@ -33,6 +34,7 @@ struct wlr_idle_timeout { struct wl_event_source *idle_source; bool idle_state; + bool enabled; uint32_t timeout; // milliseconds struct wl_listener input_listener; @@ -50,4 +52,11 @@ void wlr_idle_destroy(struct wlr_idle *idle); * compositor when there is an user activity event on that seat. */ void wlr_idle_notify_activity(struct wlr_idle *idle, struct wlr_seat *seat); + +/** + * Enable or disable timers for a given idle resource by seat. + * Passing a NULL seat means update timers for all seats. + */ +void wlr_idle_set_enabled(struct wlr_idle *idle, struct wlr_seat *seat, + bool enabled); #endif diff --git a/include/wlr/types/wlr_idle_inhibit_v1.h b/include/wlr/types/wlr_idle_inhibit_v1.h index a777ff43..8c20d2c8 100644 --- a/include/wlr/types/wlr_idle_inhibit_v1.h +++ b/include/wlr/types/wlr_idle_inhibit_v1.h @@ -25,6 +25,8 @@ struct wlr_idle_inhibit_manager_v1 { struct { struct wl_signal new_inhibitor; } events; + + void *data; }; struct wlr_idle_inhibitor_v1 { @@ -37,6 +39,8 @@ struct wlr_idle_inhibitor_v1 { struct { struct wl_signal destroy; } events; + + void *data; }; struct wlr_idle_inhibit_manager_v1 *wlr_idle_inhibit_v1_create(struct wl_display *display); diff --git a/include/wlr/types/wlr_screencopy_v1.h b/include/wlr/types/wlr_screencopy_v1.h new file mode 100644 index 00000000..892687aa --- /dev/null +++ b/include/wlr/types/wlr_screencopy_v1.h @@ -0,0 +1,39 @@ +#ifndef WLR_TYPES_WLR_SCREENCOPY_V1_H +#define WLR_TYPES_WLR_SCREENCOPY_V1_H + +#include <wayland-server.h> + +struct wlr_screencopy_manager_v1 { + struct wl_global *global; + struct wl_list resources; // wl_resource + struct wl_list frames; // wlr_screencopy_frame_v1::link + + struct wl_listener display_destroy; + + void *data; +}; + +struct wlr_screencopy_frame_v1 { + struct wl_resource *resource; + struct wlr_screencopy_manager_v1 *manager; + struct wl_list link; + + enum wl_shm_format format; + struct wlr_box box; + int stride; + + struct wl_shm_buffer *buffer; + struct wl_listener buffer_destroy; + + struct wlr_output *output; + struct wl_listener output_swap_buffers; + + void *data; +}; + +struct wlr_screencopy_manager_v1 *wlr_screencopy_manager_v1_create( + struct wl_display *display); +void wlr_screencopy_manager_v1_destroy( + struct wlr_screencopy_manager_v1 *screencopy); + +#endif diff --git a/include/wlr/types/wlr_surface.h b/include/wlr/types/wlr_surface.h index 46588f0a..b89da87a 100644 --- a/include/wlr/types/wlr_surface.h +++ b/include/wlr/types/wlr_surface.h @@ -8,57 +8,32 @@ #include <wayland-server.h> #include <wlr/types/wlr_output.h> -#define WLR_SURFACE_INVALID_BUFFER 1 -#define WLR_SURFACE_INVALID_SURFACE_DAMAGE 2 -#define WLR_SURFACE_INVALID_BUFFER_DAMAGE 4 -#define WLR_SURFACE_INVALID_OPAQUE_REGION 8 -#define WLR_SURFACE_INVALID_INPUT_REGION 16 -#define WLR_SURFACE_INVALID_TRANSFORM 32 -#define WLR_SURFACE_INVALID_SCALE 64 -#define WLR_SURFACE_INVALID_SUBSURFACE_POSITION 128 -#define WLR_SURFACE_INVALID_FRAME_CALLBACK_LIST 256 +enum wlr_surface_state_field { + WLR_SURFACE_STATE_BUFFER = 1, + WLR_SURFACE_STATE_SURFACE_DAMAGE = 2, + WLR_SURFACE_STATE_BUFFER_DAMAGE = 4, + WLR_SURFACE_STATE_OPAQUE_REGION = 8, + WLR_SURFACE_STATE_INPUT_REGION = 16, + WLR_SURFACE_STATE_TRANSFORM = 32, + WLR_SURFACE_STATE_SCALE = 64, + WLR_SURFACE_STATE_FRAME_CALLBACK_LIST = 128, +}; struct wlr_surface_state { - uint32_t invalid; - struct wl_resource *buffer; - struct wl_listener buffer_destroy_listener; - int32_t sx, sy; + uint32_t committed; // enum wlr_surface_state_field + + struct wl_resource *buffer_resource; + int32_t dx, dy; // relative to previous position pixman_region32_t surface_damage, buffer_damage; pixman_region32_t opaque, input; enum wl_output_transform transform; int32_t scale; - int width, height; - int buffer_width, buffer_height; - - struct { - int32_t x, y; - } subsurface_position; - - struct wl_list frame_callback_list; // wl_surface.frame -}; - -struct wlr_subsurface { - struct wl_resource *resource; - struct wlr_surface *surface; - struct wlr_surface *parent; - - struct wlr_surface_state *cached; - bool has_cache; - - bool synchronized; - bool reordered; - - struct wl_list parent_link; - struct wl_list parent_pending_link; - - struct wl_listener surface_destroy; - struct wl_listener parent_destroy; + struct wl_list frame_callback_list; // wl_resource - struct { - struct wl_signal destroy; - } events; + int width, height; // in surface-local coordinates + int buffer_width, buffer_height; - void *data; + struct wl_listener buffer_destroy; }; struct wlr_surface { @@ -71,7 +46,28 @@ struct wlr_surface { * or something went wrong with uploading the buffer. */ struct wlr_buffer *buffer; - struct wlr_surface_state *current, *pending; + /** + * 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 changed its size or moved, the whole buffer is + * damaged. + * + * This region needs to be scaled and transformed into output coordinates, + * just like the buffer's texture. + */ + pixman_region32_t buffer_damage; + /** + * `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. `previous` contains the state of + * the previous commit. + */ + struct wlr_surface_state current, pending, previous; const char *role; // the lifetime-bound role or null struct { @@ -94,6 +90,36 @@ struct wlr_surface { void *data; }; +struct wlr_subsurface_state { + int32_t x, y; +}; + +struct wlr_subsurface { + struct wl_resource *resource; + struct wlr_surface *surface; + struct wlr_surface *parent; + + struct wlr_subsurface_state current, pending; + + struct wlr_surface_state cached; + bool has_cache; + + bool synchronized; + bool reordered; + + struct wl_list parent_link; + struct wl_list parent_pending_link; + + struct wl_listener surface_destroy; + struct wl_listener parent_destroy; + + struct { + struct wl_signal destroy; + } events; + + void *data; +}; + typedef void (*wlr_surface_iterator_func_t)(struct wlr_surface *surface, int sx, int sy, void *data); diff --git a/include/wlr/types/wlr_xdg_shell.h b/include/wlr/types/wlr_xdg_shell.h index 01dc17fe..d2dfaacb 100644 --- a/include/wlr/types/wlr_xdg_shell.h +++ b/include/wlr/types/wlr_xdg_shell.h @@ -352,11 +352,19 @@ struct wlr_xdg_surface *wlr_xdg_surface_from_wlr_surface( void wlr_xdg_surface_get_geometry(struct wlr_xdg_surface *surface, struct wlr_box *box); /** - * Call `iterator` on each surface in the xdg-surface tree, with the surface's - * position relative to the root xdg-surface. The function is called from root to - * leaves (in rendering order). + * Call `iterator` on each surface and popup in the xdg-surface tree, with the + * surface's position relative to the root xdg-surface. The function is called + * from root to leaves (in rendering order). */ void wlr_xdg_surface_for_each_surface(struct wlr_xdg_surface *surface, wlr_surface_iterator_func_t iterator, void *user_data); +/** + * Call `iterator` on each popup in the xdg-surface tree, with the popup's + * position relative to the root xdg-surface. The function is called from root + * to leaves (in rendering order). + */ +void wlr_xdg_surface_for_each_popup(struct wlr_xdg_surface *surface, + wlr_surface_iterator_func_t iterator, void *user_data); + #endif diff --git a/include/wlr/types/wlr_xdg_shell_v6.h b/include/wlr/types/wlr_xdg_shell_v6.h index 5f98eb13..38f85635 100644 --- a/include/wlr/types/wlr_xdg_shell_v6.h +++ b/include/wlr/types/wlr_xdg_shell_v6.h @@ -329,11 +329,19 @@ struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6_from_wlr_surface( void wlr_xdg_surface_v6_get_geometry(struct wlr_xdg_surface_v6 *surface, struct wlr_box *box); /** - * Call `iterator` on each surface in the xdg-surface tree, with the surface's - * position relative to the root xdg-surface. The function is called from root to - * leaves (in rendering order). + * Call `iterator` on each surface and popup in the xdg-surface tree, with the + * surface's position relative to the root xdg-surface. The function is called + * from root to leaves (in rendering order). */ void wlr_xdg_surface_v6_for_each_surface(struct wlr_xdg_surface_v6 *surface, wlr_surface_iterator_func_t iterator, void *user_data); +/** + * Call `iterator` on each popup in the xdg-surface tree, with the popup's + * position relative to the root xdg-surface. The function is called from root + * to leaves (in rendering order). + */ +void wlr_xdg_surface_v6_for_each_popup(struct wlr_xdg_surface_v6 *surface, + wlr_surface_iterator_func_t iterator, void *user_data); + #endif diff --git a/protocol/meson.build b/protocol/meson.build index ca0d82b5..5ab82d84 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -42,6 +42,7 @@ protocols = [ 'wlr-export-dmabuf-unstable-v1.xml', 'wlr-input-inhibitor-unstable-v1.xml', 'wlr-layer-shell-unstable-v1.xml', + 'wlr-screencopy-unstable-v1.xml', ] client_protocols = [ @@ -51,8 +52,9 @@ client_protocols = [ 'idle.xml', 'screenshooter.xml', 'wlr-export-dmabuf-unstable-v1.xml', - 'wlr-layer-shell-unstable-v1.xml', 'wlr-input-inhibitor-unstable-v1.xml', + 'wlr-layer-shell-unstable-v1.xml', + 'wlr-screencopy-unstable-v1.xml', ] wl_protos_src = [] diff --git a/protocol/wlr-screencopy-unstable-v1.xml b/protocol/wlr-screencopy-unstable-v1.xml new file mode 100644 index 00000000..a7a2d172 --- /dev/null +++ b/protocol/wlr-screencopy-unstable-v1.xml @@ -0,0 +1,179 @@ +<?xml version="1.0" encoding="UTF-8"?> +<protocol name="wlr_screencopy_unstable_v1"> + <copyright> + Copyright © 2018 Simon Ser + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + </copyright> + + <description summary="screen content capturing on client buffers"> + This protocol allows clients to ask the compositor to copy part of the + screen content to a client buffer. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. + </description> + + <interface name="zwlr_screencopy_manager_v1" version="1"> + <description summary="manager to inform clients and begin capturing"> + This object is a manager which offers requests to start capturing from a + source. + </description> + + <request name="capture_output"> + <description summary="capture an output"> + Capture the next frame of an entire output. + </description> + <arg name="frame" type="new_id" interface="zwlr_screencopy_frame_v1"/> + <arg name="overlay_cursor" type="int" + summary="composite cursor onto the frame"/> + <arg name="output" type="object" interface="wl_output"/> + </request> + + <request name="capture_output_region"> + <description summary="capture an output's region"> + Capture the next frame of an output's region. + + The region is given in output logical coordinates, see + xdg_output.logical_size. The region will be clipped to the output's + extents. + </description> + <arg name="frame" type="new_id" interface="zwlr_screencopy_frame_v1"/> + <arg name="overlay_cursor" type="int" + summary="composite cursor onto the frame"/> + <arg name="output" type="object" interface="wl_output"/> + <arg name="x" type="int"/> + <arg name="y" type="int"/> + <arg name="width" type="int"/> + <arg name="height" type="int"/> + </request> + + <request name="destroy" type="destructor"> + <description summary="destroy the manager"> + All objects created by the manager will still remain valid, until their + appropriate destroy request has been called. + </description> + </request> + </interface> + + <interface name="zwlr_screencopy_frame_v1" version="1"> + <description summary="a frame ready for copy"> + This object represents a single frame. + + When created, a "buffer" event will be sent. The client will then be able + to send a "copy" request. If the capture is successful, the compositor + will send a "flags" followed by a "ready" event. + + If the capture failed, the "failed" event is sent. This can happen anytime + before the "ready" event. + + Once either a "ready" or a "failed" event is received, the client should + destroy the frame. + </description> + + <event name="buffer"> + <description summary="buffer information"> + Provides information about the frame's buffer. This event is sent once + as soon as the frame is created. + + The client should then create a buffer with the provided attributes, and + send a "copy" request. + </description> + <arg name="format" type="uint" summary="buffer format"/> + <arg name="width" type="uint" summary="buffer width"/> + <arg name="height" type="uint" summary="buffer height"/> + <arg name="stride" type="uint" summary="buffer stride"/> + </event> + + <request name="copy"> + <description summary="copy the frame"> + Copy the frame to the supplied buffer. The buffer must have a the + correct size, see zwlr_screencopy_frame_v1.buffer. The buffer needs to + have a supported format. + + If the frame is successfully copied, a "flags" and a "ready" events are + sent. Otherwise, a "failed" event is sent. + </description> + <arg name="buffer" type="object" interface="wl_buffer"/> + </request> + + <enum name="error"> + <entry name="already_used" value="0" + summary="the object has already been used to copy a wl_buffer"/> + <entry name="invalid_buffer" value="1" + summary="buffer attributes are invalid"/> + </enum> + + <enum name="flags" bitfield="true"> + <entry name="y_invert" value="1" summary="contents are y-inverted"/> + </enum> + + <event name="flags"> + <description summary="frame flags"> + Provides flags about the frame. This event is sent once before the + "ready" event. + </description> + <arg name="flags" type="uint" enum="flags" summary="frame flags"/> + </event> + + <event name="ready"> + <description summary="indicates frame is available for reading"> + Called as soon as the frame is copied, indicating it is available + for reading. This event includes the time at which presentation happened + at. + + The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples, + each component being an unsigned 32-bit value. Whole seconds are in + tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo, + and the additional fractional part in tv_nsec as nanoseconds. Hence, + for valid timestamps tv_nsec must be in [0, 999999999]. The seconds part + may have an arbitrary offset at start. + + After receiving this event, the client should destroy the object. + </description> + <arg name="tv_sec_hi" type="uint" + summary="high 32 bits of the seconds part of the timestamp"/> + <arg name="tv_sec_lo" type="uint" + summary="low 32 bits of the seconds part of the timestamp"/> + <arg name="tv_nsec" type="uint" + summary="nanoseconds part of the timestamp"/> + </event> + + <event name="failed"> + <description summary="frame copy failed"> + This event indicates that the attempted frame copy has failed. + + After receiving this event, the client should destroy the object. + </description> + </event> + + <request name="destroy" type="destructor"> + <description summary="delete this object, used or not"> + Destroys the frame. This request can be sent at any time by the client. + </description> + </request> + </interface> +</protocol> diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c index 05426fa9..5233ed1b 100644 --- a/render/gles2/renderer.c +++ b/render/gles2/renderer.c @@ -250,10 +250,11 @@ static int gles2_get_dmabuf_modifiers(struct wlr_renderer *wlr_renderer, } static bool gles2_read_pixels(struct wlr_renderer *wlr_renderer, - enum wl_shm_format wl_fmt, uint32_t stride, uint32_t width, - uint32_t height, uint32_t src_x, uint32_t src_y, uint32_t dst_x, - uint32_t dst_y, void *data) { - gles2_get_renderer_in_context(wlr_renderer); + enum wl_shm_format wl_fmt, uint32_t *flags, uint32_t stride, + uint32_t width, uint32_t height, uint32_t src_x, uint32_t src_y, + uint32_t dst_x, uint32_t dst_y, void *data) { + struct wlr_gles2_renderer *renderer = + gles2_get_renderer_in_context(wlr_renderer); const struct wlr_gles2_pixel_format *fmt = get_gles2_format_from_wl(wl_fmt); if (fmt == NULL) { @@ -266,17 +267,31 @@ static bool gles2_read_pixels(struct wlr_renderer *wlr_renderer, // Make sure any pending drawing is finished before we try to read it glFinish(); - // Unfortunately GLES2 doesn't support GL_PACK_*, so we have to read - // the lines out row by row + glGetError(); // Clear the error flag + unsigned char *p = data + dst_y * stride; - for (size_t i = src_y; i < src_y + height; ++i) { - glReadPixels(src_x, src_y + height - i - 1, width, 1, fmt->gl_format, - fmt->gl_type, p + i * stride + dst_x * fmt->bpp / 8); + uint32_t pack_stride = width * fmt->bpp / 8; + if (pack_stride == stride && dst_x == 0 && flags != NULL) { + // Under these particular conditions, we can read the pixels with only + // one glReadPixels call + glReadPixels(src_x, renderer->viewport_height - height - src_y, + width, height, fmt->gl_format, fmt->gl_type, p); + *flags = WLR_RENDERER_READ_PIXELS_Y_INVERT; + } else { + // Unfortunately GLES2 doesn't support GL_PACK_*, so we have to read + // the lines out row by row + for (size_t i = src_y; i < src_y + height; ++i) { + glReadPixels(src_x, src_y + height - i - 1, width, 1, fmt->gl_format, + fmt->gl_type, p + i * stride + dst_x * fmt->bpp / 8); + } + if (flags != NULL) { + *flags = 0; + } } POP_GLES2_DEBUG; - return true; + return glGetError() == GL_NO_ERROR; } static bool gles2_format_supported(struct wlr_renderer *wlr_renderer, diff --git a/render/wlr_renderer.c b/render/wlr_renderer.c index 98c91132..d66edc13 100644 --- a/render/wlr_renderer.c +++ b/render/wlr_renderer.c @@ -25,9 +25,12 @@ void wlr_renderer_init(struct wlr_renderer *renderer, } void wlr_renderer_destroy(struct wlr_renderer *r) { + if (!r) { + return; + } wlr_signal_emit_safe(&r->events.destroy, r); - if (r && r->impl && r->impl->destroy) { + if (r->impl && r->impl->destroy) { r->impl->destroy(r); } else { free(r); @@ -136,14 +139,14 @@ int wlr_renderer_get_dmabuf_modifiers(struct wlr_renderer *r, int format, } bool wlr_renderer_read_pixels(struct wlr_renderer *r, enum wl_shm_format fmt, - uint32_t stride, uint32_t width, uint32_t height, + uint32_t *flags, uint32_t stride, uint32_t width, uint32_t height, uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y, void *data) { if (!r->impl->read_pixels) { return false; } - return r->impl->read_pixels(r, fmt, stride, width, height, src_x, src_y, - dst_x, dst_y, data); + return r->impl->read_pixels(r, fmt, flags, stride, width, height, + src_x, src_y, dst_x, dst_y, data); } bool wlr_renderer_format_supported(struct wlr_renderer *r, diff --git a/rootston/cursor.c b/rootston/cursor.c index 1cf81704..904af67f 100644 --- a/rootston/cursor.c +++ b/rootston/cursor.c @@ -200,8 +200,8 @@ static void roots_cursor_update_position( case ROOTS_CURSOR_ROTATE: view = roots_seat_get_focus(seat); if (view != NULL) { - int ox = view->x + view->wlr_surface->current->width/2, - oy = view->y + view->wlr_surface->current->height/2; + int ox = view->x + view->wlr_surface->current.width/2, + oy = view->y + view->wlr_surface->current.height/2; int ux = cursor->offs_x - ox, uy = cursor->offs_y - oy; int vx = cursor->cursor->x - ox, @@ -239,12 +239,12 @@ static void roots_cursor_press_button(struct roots_cursor *cursor, break; case BTN_RIGHT: edges = 0; - if (sx < view->wlr_surface->current->width/2) { + if (sx < view->wlr_surface->current.width/2) { edges |= WLR_EDGE_LEFT; } else { edges |= WLR_EDGE_RIGHT; } - if (sy < view->wlr_surface->current->height/2) { + if (sy < view->wlr_surface->current.height/2) { edges |= WLR_EDGE_TOP; } else { edges |= WLR_EDGE_BOTTOM; diff --git a/rootston/desktop.c b/rootston/desktop.c index 2bba06e2..4b5a5c9a 100644 --- a/rootston/desktop.c +++ b/rootston/desktop.c @@ -69,8 +69,8 @@ enum roots_deco_part view_get_deco_part(struct roots_view *view, double sx, return ROOTS_DECO_PART_NONE; } - int sw = view->wlr_surface->current->width; - int sh = view->wlr_surface->current->height; + int sw = view->wlr_surface->current.width; + int sh = view->wlr_surface->current.height; int bw = view->border_width; int titlebar_h = view->titlebar_height; @@ -558,7 +558,7 @@ static bool view_at(struct roots_view *view, double lx, double ly, double view_sx = lx - view->x; double view_sy = ly - view->y; - struct wlr_surface_state *state = view->wlr_surface->current; + struct wlr_surface_state *state = &view->wlr_surface->current; struct wlr_box box = { .x = 0, .y = 0, .width = state->width, .height = state->height, @@ -875,6 +875,9 @@ struct roots_desktop *desktop_create(struct roots_server *server, wl_signal_add(&desktop->virtual_keyboard->events.new_virtual_keyboard, &desktop->virtual_keyboard_new); desktop->virtual_keyboard_new.notify = handle_virtual_keyboard; + + desktop->screencopy = wlr_screencopy_manager_v1_create(server->wl_display); + return desktop; } diff --git a/rootston/layer_shell.c b/rootston/layer_shell.c index db0aeb59..2adf11a5 100644 --- a/rootston/layer_shell.c +++ b/rootston/layer_shell.c @@ -381,12 +381,6 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { layer_surface->client_pending.margin.bottom, layer_surface->client_pending.margin.left); - struct roots_layer_surface *roots_surface = - calloc(1, sizeof(struct roots_layer_surface)); - if (!roots_surface) { - return; - } - if (!layer_surface->output) { struct roots_input *input = desktop->server->input; struct roots_seat *seat = input_last_active_seat(input); @@ -409,6 +403,12 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { } } + struct roots_layer_surface *roots_surface = + calloc(1, sizeof(struct roots_layer_surface)); + if (!roots_surface) { + return; + } + roots_surface->surface_commit.notify = handle_surface_commit; wl_signal_add(&layer_surface->surface->events.commit, &roots_surface->surface_commit); diff --git a/rootston/output.c b/rootston/output.c index bf671d6b..e3b03984 100644 --- a/rootston/output.c +++ b/rootston/output.c @@ -45,8 +45,8 @@ struct layout_data { static void get_layout_position(struct layout_data *data, double *lx, double *ly, const struct wlr_surface *surface, int sx, int sy) { double _sx = sx, _sy = sy; - rotate_child_position(&_sx, &_sy, surface->current->width, - surface->current->height, data->width, data->height, data->rotation); + rotate_child_position(&_sx, &_sy, surface->current.width, + surface->current.height, data->width, data->height, data->rotation); *lx = data->x + _sx; *ly = data->y + _sy; } @@ -56,8 +56,8 @@ static void surface_for_each_surface(struct wlr_surface *surface, wlr_surface_iterator_func_t iterator, void *user_data) { layout_data->x = lx; layout_data->y = ly; - layout_data->width = surface->current->width; - layout_data->height = surface->current->height; + layout_data->width = surface->current.width; + layout_data->height = surface->current.height; layout_data->rotation = rotation; wlr_surface_for_each_surface(surface, iterator, user_data); @@ -68,8 +68,8 @@ static void view_for_each_surface(struct roots_view *view, void *user_data) { layout_data->x = view->x; layout_data->y = view->y; - layout_data->width = view->wlr_surface->current->width; - layout_data->height = view->wlr_surface->current->height; + layout_data->width = view->wlr_surface->current.width; + layout_data->height = view->wlr_surface->current.height; layout_data->rotation = view->rotation; switch (view->type) { @@ -147,16 +147,19 @@ static bool surface_intersect_output(struct wlr_surface *surface, double ox = lx, oy = ly; wlr_output_layout_output_coords(output_layout, wlr_output, &ox, &oy); + ox += surface->sx; + oy += surface->sy; + if (box != NULL) { box->x = ox * wlr_output->scale; box->y = oy * wlr_output->scale; - box->width = surface->current->width * wlr_output->scale; - box->height = surface->current->height * wlr_output->scale; + box->width = surface->current.width * wlr_output->scale; + box->height = surface->current.height * wlr_output->scale; } struct wlr_box layout_box = { .x = lx, .y = ly, - .width = surface->current->width, .height = surface->current->height, + .width = surface->current.width, .height = surface->current.height, }; wlr_box_rotated_bounds(&layout_box, rotation, &layout_box); return wlr_output_layout_intersects(output_layout, wlr_output, &layout_box); @@ -224,7 +227,7 @@ static void render_surface(struct wlr_surface *surface, int sx, int sy, float matrix[9]; enum wl_output_transform transform = - wlr_output_transform_invert(surface->current->transform); + wlr_output_transform_invert(surface->current.transform); wlr_matrix_project_box(matrix, &box, transform, rotation, output->wlr_output->transform_matrix); @@ -248,8 +251,8 @@ static void get_decoration_box(struct roots_view *view, double sx = deco_box.x - view->x; double sy = deco_box.y - view->y; rotate_child_position(&sx, &sy, deco_box.width, deco_box.height, - view->wlr_surface->current->width, - view->wlr_surface->current->height, view->rotation); + view->wlr_surface->current.width, + view->wlr_surface->current.height, view->rotation); double x = sx + view->x; double y = sy + view->y; @@ -518,6 +521,13 @@ static void render_output(struct roots_output *output) { renderer_end: wlr_renderer_scissor(renderer, NULL); wlr_renderer_end(renderer); + + if (server->config->debug_damage_tracking) { + int width, height; + wlr_output_transformed_resolution(wlr_output, &width, &height); + pixman_region32_union_rect(&damage, &damage, 0, 0, width, height); + } + if (!wlr_output_damage_swap_buffers(output->damage, &now, &damage)) { goto damage_finish; } @@ -686,15 +696,21 @@ static void damage_from_surface(struct wlr_surface *surface, int sx, int sy, int center_x = box.x + box.width/2; int center_y = box.y + box.height/2; + enum wl_output_transform transform = + wlr_output_transform_invert(surface->current.transform); + pixman_region32_t damage; pixman_region32_init(&damage); - pixman_region32_copy(&damage, &surface->current->surface_damage); - wlr_region_scale(&damage, &damage, wlr_output->scale); - if (ceil(wlr_output->scale) > surface->current->scale) { + pixman_region32_copy(&damage, &surface->buffer_damage); + wlr_region_transform(&damage, &damage, transform, + surface->current.buffer_width, surface->current.buffer_height); + wlr_region_scale(&damage, &damage, + wlr_output->scale / (float)surface->current.scale); + if (ceil(wlr_output->scale) > surface->current.scale) { // When scaling up a surface, it'll become blurry so we need to // expand the damage region wlr_region_expand(&damage, &damage, - ceil(wlr_output->scale) - surface->current->scale); + ceil(wlr_output->scale) - surface->current.scale); } pixman_region32_translate(&damage, box.x, box.y); wlr_region_rotated_bounds(&damage, &damage, rotation, center_x, center_y); diff --git a/rootston/wl_shell.c b/rootston/wl_shell.c index d58f030a..02160773 100644 --- a/rootston/wl_shell.c +++ b/rootston/wl_shell.c @@ -158,8 +158,8 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) { view_apply_damage(view); - int width = wlr_surface->current->width; - int height = wlr_surface->current->height; + int width = wlr_surface->current.width; + int height = wlr_surface->current.height; view_update_size(view, width, height); double x = view->x; @@ -236,8 +236,8 @@ void handle_wl_shell_surface(struct wl_listener *listener, void *data) { return; } view->type = ROOTS_WL_SHELL_VIEW; - view->width = surface->surface->current->width; - view->height = surface->surface->current->height; + view->width = surface->surface->current.width; + view->height = surface->surface->current.height; view->wl_shell_surface = surface; view->roots_wl_shell_surface = roots_surface; diff --git a/rootston/xwayland.c b/rootston/xwayland.c index b7dbab54..d0b80821 100644 --- a/rootston/xwayland.c +++ b/rootston/xwayland.c @@ -206,8 +206,8 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) { view_apply_damage(view); - int width = wlr_surface->current->width; - int height = wlr_surface->current->height; + int width = wlr_surface->current.width; + int height = wlr_surface->current.height; view_update_size(view, width, height); double x = view->x; @@ -233,8 +233,8 @@ static void handle_map(struct wl_listener *listener, void *data) { view->x = surface->x; view->y = surface->y; - view->width = surface->surface->current->width; - view->height = surface->surface->current->height; + view->width = surface->surface->current.width; + view->height = surface->surface->current.height; roots_surface->surface_commit.notify = handle_surface_commit; wl_signal_add(&surface->surface->events.commit, diff --git a/types/data_device/wlr_drag.c b/types/data_device/wlr_drag.c index 4f0b2521..bae098c5 100644 --- a/types/data_device/wlr_drag.c +++ b/types/data_device/wlr_drag.c @@ -341,8 +341,8 @@ static void drag_icon_handle_surface_destroy(struct wl_listener *listener, static void drag_icon_handle_surface_commit(struct wlr_surface *surface, void *role_data) { struct wlr_drag_icon *icon = role_data; - icon->sx += icon->surface->current->sx; - icon->sy += icon->surface->current->sy; + icon->sx += icon->surface->current.dx; + icon->sy += icon->surface->current.dy; drag_icon_set_mapped(icon, wlr_surface_has_buffer(surface)); } diff --git a/types/meson.build b/types/meson.build index 6d6e0fa3..49f87bf8 100644 --- a/types/meson.build +++ b/types/meson.build @@ -50,6 +50,7 @@ lib_wlr_types = static_library( 'wlr_wl_shell.c', 'wlr_xcursor_manager.c', 'wlr_xdg_output.c', + 'wlr_screencopy_v1.c', ), include_directories: wlr_inc, dependencies: [pixman, xkbcommon, wayland_server, wlr_protos], diff --git a/types/seat/wlr_seat.c b/types/seat/wlr_seat.c index ead4b0e7..3c415483 100644 --- a/types/seat/wlr_seat.c +++ b/types/seat/wlr_seat.c @@ -168,17 +168,25 @@ void wlr_seat_destroy(struct wlr_seat *seat) { seat->selection_source = NULL; } if (seat->primary_selection_source) { + wl_list_remove(&seat->primary_selection_source_destroy.link); seat->primary_selection_source->cancel(seat->primary_selection_source); seat->primary_selection_source = NULL; - wl_list_remove(&seat->primary_selection_source_destroy.link); } struct wlr_seat_client *client, *tmp; wl_list_for_each_safe(client, tmp, &seat->clients, link) { - struct wl_resource *resource, *next_resource; - wl_resource_for_each_safe(resource, next_resource, &client->wl_resources) { + struct wl_resource *resource, *next; + /* wl_resource_for_each_safe isn't safe to use here, because the last + * wl_resource_destroy will also destroy the head we cannot do the last + * 'next' update that usually is harmless here. + * Work around this by breaking one step ahead + */ + wl_resource_for_each_safe(resource, next, &client->wl_resources) { // will destroy other resources as well wl_resource_destroy(resource); + if (wl_resource_get_link(next) == &client->wl_resources) { + break; + } } } diff --git a/types/wlr_compositor.c b/types/wlr_compositor.c index e4fd41dc..d2f7dbd7 100644 --- a/types/wlr_compositor.c +++ b/types/wlr_compositor.c @@ -17,7 +17,7 @@ bool wlr_surface_is_subsurface(struct wlr_surface *surface) { strcmp(surface->role, subsurface_role) == 0; } -struct wlr_subsurface *wlr_subsurface_from_surface( +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; @@ -57,7 +57,7 @@ static void subcompositor_handle_get_subsurface(struct wl_client *client, } if (wlr_surface_is_subsurface(surface) && - wlr_subsurface_from_surface(surface) != NULL) { + wlr_subsurface_from_wlr_surface(surface) != NULL) { wl_resource_post_error(resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, "%s%d: wl_surface@%d is already a sub-surface", diff --git a/types/wlr_export_dmabuf_v1.c b/types/wlr_export_dmabuf_v1.c index 68adda02..e68b0fef 100644 --- a/types/wlr_export_dmabuf_v1.c +++ b/types/wlr_export_dmabuf_v1.c @@ -84,6 +84,7 @@ static void manager_handle_capture_output(struct wl_client *client, &zwlr_export_dmabuf_frame_v1_interface, version, id); if (frame->resource == NULL) { wl_client_post_no_memory(client); + free(frame); return; } wl_resource_set_implementation(frame->resource, &frame_impl, frame, diff --git a/types/wlr_idle.c b/types/wlr_idle.c index a3981479..fffb8d55 100644 --- a/types/wlr_idle.c +++ b/types/wlr_idle.c @@ -33,6 +33,9 @@ static int idle_notify(void *data) { } static void handle_activity(struct wlr_idle_timeout *timer) { + if (!timer->enabled) { + return; + } // rearm the timer wl_event_source_timer_update(timer->idle_source, timer->timeout); // in case the previous state was sleeping send a resume event and switch state @@ -106,6 +109,7 @@ static void create_idle_timer(struct wl_client *client, timer->seat = client_seat->seat; timer->timeout = timeout; timer->idle_state = false; + timer->enabled = idle->enabled; timer->resource = wl_resource_create(client, &org_kde_kwin_idle_timeout_interface, wl_resource_get_version(idle_resource), id); @@ -135,14 +139,36 @@ static void create_idle_timer(struct wl_client *client, wl_resource_post_no_memory(idle_resource); return; } - // arm the timer - wl_event_source_timer_update(timer->idle_source, timer->timeout); + if (timer->enabled) { + // arm the timer + wl_event_source_timer_update(timer->idle_source, timer->timeout); + } } static const struct org_kde_kwin_idle_interface idle_impl = { .get_idle_timeout = create_idle_timer, }; +void wlr_idle_set_enabled(struct wlr_idle *idle, struct wlr_seat *seat, + bool enabled) { + if (idle->enabled == enabled) { + return; + } + wlr_log(L_DEBUG, "%s idle timers for %s", + enabled ? "Enabling" : "Disabling", + seat ? seat->name : "all seats"); + idle->enabled = enabled; + struct wlr_idle_timeout *timer; + wl_list_for_each(timer, &idle->idle_timers, link) { + if (seat != NULL && timer->seat != seat) { + continue; + } + int timeout = enabled ? timer->timeout : 0; + wl_event_source_timer_update(timer->idle_source, timeout); + timer->enabled = enabled; + } +} + static void idle_bind(struct wl_client *wl_client, void *data, uint32_t version, uint32_t id) { struct wlr_idle *idle = data; @@ -182,6 +208,7 @@ struct wlr_idle *wlr_idle_create(struct wl_display *display) { } wl_list_init(&idle->idle_timers); wl_signal_init(&idle->events.activity_notify); + idle->enabled = true; idle->event_loop = wl_display_get_event_loop(display); if (idle->event_loop == NULL) { diff --git a/types/wlr_idle_inhibit_v1.c b/types/wlr_idle_inhibit_v1.c index a36992d5..cfadb4a3 100644 --- a/types/wlr_idle_inhibit_v1.c +++ b/types/wlr_idle_inhibit_v1.c @@ -26,23 +26,30 @@ wlr_idle_inhibitor_v1_from_resource(struct wl_resource *resource) { return wl_resource_get_user_data(resource); } -static void idle_inhibitor_destroy(struct wl_resource *resource) { - struct wlr_idle_inhibitor_v1 *inhibitor = - wlr_idle_inhibitor_v1_from_resource(resource); +static void idle_inhibitor_v1_destroy(struct wlr_idle_inhibitor_v1 *inhibitor) { + if (!inhibitor) { + return; + } wlr_signal_emit_safe(&inhibitor->events.destroy, inhibitor->surface); + wl_resource_set_user_data(inhibitor->resource, NULL); wl_list_remove(&inhibitor->link); wl_list_remove(&inhibitor->surface_destroy.link); free(inhibitor); } +static void idle_inhibitor_v1_handle_resource_destroy(struct wl_resource *resource) { + struct wlr_idle_inhibitor_v1 *inhibitor = + wlr_idle_inhibitor_v1_from_resource(resource); + idle_inhibitor_v1_destroy(inhibitor); +} + static void idle_inhibitor_handle_surface_destroy( struct wl_listener *listener, void *data) { struct wlr_idle_inhibitor_v1 *inhibitor = wl_container_of(listener, inhibitor, surface_destroy); - - wl_resource_destroy(inhibitor->resource); + idle_inhibitor_v1_destroy(inhibitor); } static void idle_inhibitor_v1_handle_destroy(struct wl_client *client, @@ -54,7 +61,7 @@ static const struct zwp_idle_inhibitor_v1_interface idle_inhibitor_impl = { .destroy = idle_inhibitor_v1_handle_destroy, }; -static void manager_create_inhibitor(struct wl_client *client, +static void manager_handle_create_inhibitor(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface_resource) { struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); @@ -85,25 +92,25 @@ static void manager_create_inhibitor(struct wl_client *client, wl_resource_set_implementation(wl_resource, &idle_inhibitor_impl, - inhibitor, idle_inhibitor_destroy); + inhibitor, idle_inhibitor_v1_handle_resource_destroy); wl_list_insert(&manager->inhibitors, &inhibitor->link); wlr_signal_emit_safe(&manager->events.new_inhibitor, inhibitor); } - -static void idle_inhibit_manager_v1_destroy(struct wl_resource *resource) { +static void idle_inhibit_manager_v1_handle_resource_destroy( + struct wl_resource *resource) { wl_list_remove(wl_resource_get_link(resource)); } -static void manager_destroy(struct wl_client *client, +static void manager_handle_destroy(struct wl_client *client, struct wl_resource *manager_resource) { wl_resource_destroy(manager_resource); } static const struct zwp_idle_inhibit_manager_v1_interface idle_inhibit_impl = { - .destroy = manager_destroy, - .create_inhibitor = manager_create_inhibitor, + .destroy = manager_handle_destroy, + .create_inhibitor = manager_handle_create_inhibitor, }; static void handle_display_destroy(struct wl_listener *listener, void *data) { @@ -128,7 +135,7 @@ static void idle_inhibit_bind(struct wl_client *wl_client, void *data, wl_list_insert(&idle_inhibit->wl_resources, wl_resource_get_link(wl_resource)); wl_resource_set_implementation(wl_resource, &idle_inhibit_impl, - idle_inhibit, idle_inhibit_manager_v1_destroy); + idle_inhibit, idle_inhibit_manager_v1_handle_resource_destroy); wlr_log(L_DEBUG, "idle_inhibit bound"); } @@ -142,13 +149,13 @@ void wlr_idle_inhibit_v1_destroy(struct wlr_idle_inhibit_manager_v1 *idle_inhibi struct wlr_idle_inhibitor_v1 *inhibitor; struct wlr_idle_inhibitor_v1 *tmp; wl_list_for_each_safe(inhibitor, tmp, &idle_inhibit->inhibitors, link) { - wl_resource_destroy(inhibitor->resource); + idle_inhibitor_v1_destroy(inhibitor); } struct wl_resource *resource; struct wl_resource *tmp_resource; wl_resource_for_each_safe(resource, tmp_resource, &idle_inhibit->wl_resources) { - wl_resource_destroy(inhibitor->resource); + wl_resource_destroy(resource); } wl_global_destroy(idle_inhibit->global); diff --git a/types/wlr_keyboard.c b/types/wlr_keyboard.c index 0f0cb1cf..3afbe517 100644 --- a/types/wlr_keyboard.c +++ b/types/wlr_keyboard.c @@ -144,6 +144,7 @@ void wlr_keyboard_init(struct wlr_keyboard *kb, wl_signal_init(&kb->events.modifiers); wl_signal_init(&kb->events.keymap); wl_signal_init(&kb->events.repeat_info); + kb->keymap_fd = -1; // Sane defaults kb->repeat_info.rate = 25; @@ -156,7 +157,9 @@ void wlr_keyboard_destroy(struct wlr_keyboard *kb) { } xkb_state_unref(kb->xkb_state); xkb_keymap_unref(kb->keymap); - close(kb->keymap_fd); + if (kb->keymap_fd >= 0) { + close(kb->keymap_fd); + } if (kb->impl && kb->impl->destroy) { kb->impl->destroy(kb); } else { @@ -212,7 +215,7 @@ void wlr_keyboard_set_keymap(struct wlr_keyboard *kb, keymap_str = xkb_keymap_get_as_string(kb->keymap, XKB_KEYMAP_FORMAT_TEXT_V1); kb->keymap_size = strlen(keymap_str) + 1; - if (kb->keymap_fd) { + if (kb->keymap_fd >= 0) { close(kb->keymap_fd); } kb->keymap_fd = os_create_anonymous_file(kb->keymap_size); @@ -228,6 +231,7 @@ void wlr_keyboard_set_keymap(struct wlr_keyboard *kb, } strcpy(ptr, keymap_str); free(keymap_str); + munmap(ptr, kb->keymap_size); for (size_t i = 0; i < kb->num_keycodes; ++i) { xkb_keycode_t keycode = kb->keycodes[i] + 8; @@ -244,7 +248,10 @@ err: kb->xkb_state = NULL; xkb_keymap_unref(keymap); kb->keymap = NULL; - close(kb->keymap_fd); + if (kb->keymap_fd >= 0) { + close(kb->keymap_fd); + kb->keymap_fd = -1; + } free(keymap_str); } diff --git a/types/wlr_output.c b/types/wlr_output.c index 40332efd..fc86f411 100644 --- a/types/wlr_output.c +++ b/types/wlr_output.c @@ -352,13 +352,13 @@ static void output_fullscreen_surface_get_box(struct wlr_output *output, int width, height; wlr_output_effective_resolution(output, &width, &height); - int x = (width - surface->current->width) / 2; - int y = (height - surface->current->height) / 2; + int x = (width - surface->current.width) / 2; + int y = (height - surface->current.height) / 2; box->x = x * output->scale; box->y = y * output->scale; - box->width = surface->current->width * output->scale; - box->height = surface->current->height * output->scale; + box->width = surface->current.width * output->scale; + box->height = surface->current.height * output->scale; } static void output_fullscreen_surface_render(struct wlr_output *output, @@ -378,7 +378,7 @@ static void output_fullscreen_surface_render(struct wlr_output *output, float matrix[9]; enum wl_output_transform transform = - wlr_output_transform_invert(surface->current->transform); + wlr_output_transform_invert(surface->current.transform); wlr_matrix_project_box(matrix, &box, transform, 0, output->transform_matrix); @@ -403,11 +403,6 @@ static void output_cursor_get_box(struct wlr_output_cursor *cursor, box->y = cursor->y - cursor->hotspot_y; box->width = cursor->width; box->height = cursor->height; - - if (cursor->surface != NULL) { - box->x += cursor->surface->current->sx * cursor->output->scale; - box->y += cursor->surface->current->sy * cursor->output->scale; - } } static void output_cursor_render(struct wlr_output_cursor *cursor, @@ -486,8 +481,8 @@ bool wlr_output_swap_buffers(struct wlr_output *output, struct timespec *when, pixman_region32_intersect(&render_damage, &render_damage, damage); } + struct timespec now; if (when == NULL) { - struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); when = &now; } @@ -602,10 +597,10 @@ static void output_fullscreen_surface_handle_commit( fullscreen_surface_commit); struct wlr_surface *surface = output->fullscreen_surface; - if (output->fullscreen_width != surface->current->width || - output->fullscreen_height != surface->current->height) { - output->fullscreen_width = surface->current->width; - output->fullscreen_height = surface->current->height; + if (output->fullscreen_width != surface->current.width || + output->fullscreen_height != surface->current.height) { + output->fullscreen_width = surface->current.width; + output->fullscreen_height = surface->current.height; wlr_output_damage_whole(output); return; } @@ -615,7 +610,7 @@ static void output_fullscreen_surface_handle_commit( pixman_region32_t damage; pixman_region32_init(&damage); - pixman_region32_copy(&damage, &surface->current->surface_damage); + pixman_region32_copy(&damage, &surface->current.surface_damage); wlr_region_scale(&damage, &damage, output->scale); pixman_region32_translate(&damage, box.x, box.y); pixman_region32_union(&output->damage, &output->damage, &damage); @@ -714,8 +709,8 @@ static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) { struct wlr_texture *texture = cursor->texture; if (cursor->surface != NULL) { texture = wlr_surface_get_texture(cursor->surface); - scale = cursor->surface->current->scale; - transform = cursor->surface->current->transform; + scale = cursor->surface->current.scale; + transform = cursor->surface->current.transform; } struct wlr_output_cursor *hwcur = cursor->output->hardware_cursor; @@ -771,20 +766,28 @@ bool wlr_output_cursor_set_image(struct wlr_output_cursor *cursor, return true; } -static void output_cursor_commit(struct wlr_output_cursor *cursor) { +static void output_cursor_commit(struct wlr_output_cursor *cursor, + bool update_hotspot) { if (cursor->output->hardware_cursor != cursor) { output_cursor_damage_whole(cursor); } + struct wlr_surface *surface = cursor->surface; + assert(surface != NULL); + // Some clients commit a cursor surface with a NULL buffer to hide it. - cursor->enabled = wlr_surface_has_buffer(cursor->surface); - cursor->width = cursor->surface->current->width * cursor->output->scale; - cursor->height = cursor->surface->current->height * cursor->output->scale; + cursor->enabled = wlr_surface_has_buffer(surface); + cursor->width = surface->current.width * cursor->output->scale; + cursor->height = surface->current.height * cursor->output->scale; + if (update_hotspot) { + cursor->hotspot_x -= surface->current.dx * cursor->output->scale; + cursor->hotspot_y -= surface->current.dy * cursor->output->scale; + } if (output_cursor_attempt_hardware(cursor)) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - wlr_surface_send_frame_done(cursor->surface, &now); + wlr_surface_send_frame_done(surface, &now); return; } @@ -794,9 +797,9 @@ static void output_cursor_commit(struct wlr_output_cursor *cursor) { static void output_cursor_handle_commit(struct wl_listener *listener, void *data) { - struct wlr_output_cursor *cursor = wl_container_of(listener, cursor, - surface_commit); - output_cursor_commit(cursor); + struct wlr_output_cursor *cursor = + wl_container_of(listener, cursor, surface_commit); + output_cursor_commit(cursor, true); } static void output_cursor_handle_destroy(struct wl_listener *listener, @@ -842,7 +845,7 @@ void wlr_output_cursor_set_surface(struct wlr_output_cursor *cursor, if (surface != NULL) { wl_signal_add(&surface->events.commit, &cursor->surface_commit); wl_signal_add(&surface->events.destroy, &cursor->surface_destroy); - output_cursor_commit(cursor); + output_cursor_commit(cursor, false); cursor->visible = false; output_cursor_update_visible(cursor); diff --git a/types/wlr_screencopy_v1.c b/types/wlr_screencopy_v1.c new file mode 100644 index 00000000..0760eaaf --- /dev/null +++ b/types/wlr_screencopy_v1.c @@ -0,0 +1,318 @@ +#include <assert.h> +#include <stdlib.h> +#include <wlr/render/wlr_renderer.h> +#include <wlr/types/wlr_output.h> +#include <wlr/types/wlr_screencopy_v1.h> +#include <wlr/backend.h> +#include "wlr-screencopy-unstable-v1-protocol.h" + +#define SCREENCOPY_MANAGER_VERSION 1 + +static const struct zwlr_screencopy_frame_v1_interface frame_impl; + +static struct wlr_screencopy_frame_v1 *frame_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &zwlr_screencopy_frame_v1_interface, &frame_impl)); + return wl_resource_get_user_data(resource); +} + +static void frame_destroy(struct wlr_screencopy_frame_v1 *frame) { + if (frame == NULL) { + return; + } + wl_list_remove(&frame->link); + wl_list_remove(&frame->output_swap_buffers.link); + wl_list_remove(&frame->buffer_destroy.link); + // Make the frame resource inert + wl_resource_set_user_data(frame->resource, NULL); + free(frame); +} + +static void frame_handle_output_swap_buffers(struct wl_listener *listener, + void *_data) { + struct wlr_screencopy_frame_v1 *frame = + wl_container_of(listener, frame, output_swap_buffers); + struct wlr_output_event_swap_buffers *event = _data; + struct wlr_output *output = frame->output; + struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); + + wl_list_remove(&frame->output_swap_buffers.link); + wl_list_init(&frame->output_swap_buffers.link); + + int x = frame->box.x; + int y = frame->box.y; + + struct wl_shm_buffer *buffer = frame->buffer; + assert(buffer != NULL); + + enum wl_shm_format fmt = wl_shm_buffer_get_format(buffer); + int32_t width = wl_shm_buffer_get_width(buffer); + int32_t height = wl_shm_buffer_get_height(buffer); + int32_t stride = wl_shm_buffer_get_stride(buffer); + + wl_shm_buffer_begin_access(buffer); + void *data = wl_shm_buffer_get_data(buffer); + uint32_t flags = 0; + bool ok = wlr_renderer_read_pixels(renderer, fmt, &flags, stride, + width, height, x, y, 0, 0, data); + wl_shm_buffer_end_access(buffer); + + if (!ok) { + zwlr_screencopy_frame_v1_send_failed(frame->resource); + frame_destroy(frame); + return; + } + + zwlr_screencopy_frame_v1_send_flags(frame->resource, flags); + + uint32_t tv_sec_hi = event->when->tv_sec >> 32; + uint32_t tv_sec_lo = event->when->tv_sec & 0xFFFFFFFF; + zwlr_screencopy_frame_v1_send_ready(frame->resource, + tv_sec_hi, tv_sec_lo, event->when->tv_nsec); + + frame_destroy(frame); +} + +static void frame_handle_buffer_destroy(struct wl_listener *listener, + void *data) { + struct wlr_screencopy_frame_v1 *frame = + wl_container_of(listener, frame, buffer_destroy); + zwlr_screencopy_frame_v1_send_failed(frame->resource); + frame_destroy(frame); +} + +static void frame_handle_copy(struct wl_client *client, + struct wl_resource *frame_resource, + struct wl_resource *buffer_resource) { + struct wlr_screencopy_frame_v1 *frame = frame_from_resource(frame_resource); + if (frame == NULL) { + return; + } + + struct wlr_output *output = frame->output; + + struct wl_shm_buffer *buffer = wl_shm_buffer_get(buffer_resource); + if (buffer == NULL) { + wl_resource_post_error(frame->resource, + ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, + "unsupported buffer type"); + return; + } + + enum wl_shm_format fmt = wl_shm_buffer_get_format(buffer); + int32_t width = wl_shm_buffer_get_width(buffer); + int32_t height = wl_shm_buffer_get_height(buffer); + int32_t stride = wl_shm_buffer_get_stride(buffer); + if (fmt != frame->format || width != frame->box.width || + height != frame->box.height || stride != frame->stride) { + wl_resource_post_error(frame->resource, + ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, + "invalid buffer attributes"); + return; + } + + if (!wl_list_empty(&frame->output_swap_buffers.link) || + frame->buffer != NULL) { + wl_resource_post_error(frame->resource, + ZWLR_SCREENCOPY_FRAME_V1_ERROR_ALREADY_USED, + "frame already used"); + return; + } + + frame->buffer = buffer; + + wl_signal_add(&output->events.swap_buffers, &frame->output_swap_buffers); + frame->output_swap_buffers.notify = frame_handle_output_swap_buffers; + + wl_resource_add_destroy_listener(buffer_resource, &frame->buffer_destroy); + frame->buffer_destroy.notify = frame_handle_buffer_destroy; + + // Schedule a buffer swap + output->needs_swap = true; + wlr_output_schedule_frame(output); +} + +static void frame_handle_destroy(struct wl_client *client, + struct wl_resource *frame_resource) { + wl_resource_destroy(frame_resource); +} + +static const struct zwlr_screencopy_frame_v1_interface frame_impl = { + .copy = frame_handle_copy, + .destroy = frame_handle_destroy, +}; + +static void frame_handle_resource_destroy(struct wl_resource *frame_resource) { + struct wlr_screencopy_frame_v1 *frame = frame_from_resource(frame_resource); + frame_destroy(frame); +} + + +static const struct zwlr_screencopy_manager_v1_interface manager_impl; + +static struct wlr_screencopy_manager_v1 *manager_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, + &zwlr_screencopy_manager_v1_interface, &manager_impl)); + return wl_resource_get_user_data(resource); +} + +static void capture_output(struct wl_client *client, + struct wlr_screencopy_manager_v1 *manager, uint32_t version, uint32_t id, + int32_t overlay_cursor, struct wlr_output *output, + const struct wlr_box *box) { + struct wlr_box buffer_box = {0}; + if (box == NULL) { + buffer_box.width = output->width; + buffer_box.height = output->height; + } else { + int ow, oh; + wlr_output_effective_resolution(output, &ow, &oh); + + buffer_box = *box; + + wlr_box_transform(&buffer_box, output->transform, ow, oh, &buffer_box); + buffer_box.x *= output->scale; + buffer_box.y *= output->scale; + buffer_box.width *= output->scale; + buffer_box.height *= output->scale; + } + + struct wlr_screencopy_frame_v1 *frame = + calloc(1, sizeof(struct wlr_screencopy_frame_v1)); + if (frame == NULL) { + wl_client_post_no_memory(client); + return; + } + frame->manager = manager; + frame->output = output; + + frame->resource = wl_resource_create(client, + &zwlr_screencopy_frame_v1_interface, version, id); + if (frame->resource == NULL) { + free(frame); + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(frame->resource, &frame_impl, frame, + frame_handle_resource_destroy); + + wl_list_insert(&manager->frames, &frame->link); + + wl_list_init(&frame->output_swap_buffers.link); + + frame->format = WL_SHM_FORMAT_XRGB8888; + frame->box = buffer_box; + frame->stride = 4 * buffer_box.width; + zwlr_screencopy_frame_v1_send_buffer(frame->resource, frame->format, + buffer_box.width, buffer_box.height, frame->stride); +} + +static void manager_handle_capture_output(struct wl_client *client, + struct wl_resource *manager_resource, uint32_t id, + int32_t overlay_cursor, struct wl_resource *output_resource) { + struct wlr_screencopy_manager_v1 *manager = + manager_from_resource(manager_resource); + uint32_t version = wl_resource_get_version(manager_resource); + struct wlr_output *output = wlr_output_from_resource(output_resource); + + capture_output(client, manager, version, id, overlay_cursor, output, NULL); +} + +static void manager_handle_capture_output_region(struct wl_client *client, + struct wl_resource *manager_resource, uint32_t id, + int32_t overlay_cursor, struct wl_resource *output_resource, + int32_t x, int32_t y, int32_t width, int32_t height) { + struct wlr_screencopy_manager_v1 *manager = + manager_from_resource(manager_resource); + uint32_t version = wl_resource_get_version(manager_resource); + struct wlr_output *output = wlr_output_from_resource(output_resource); + + struct wlr_box box = { + .x = x, + .y = y, + .width = width, + .height = height, + }; + capture_output(client, manager, version, id, overlay_cursor, output, &box); +} + +static void manager_handle_destroy(struct wl_client *client, + struct wl_resource *manager_resource) { + wl_resource_destroy(manager_resource); +} + +static const struct zwlr_screencopy_manager_v1_interface manager_impl = { + .capture_output = manager_handle_capture_output, + .capture_output_region = manager_handle_capture_output_region, + .destroy = manager_handle_destroy, +}; + +void manager_handle_resource_destroy(struct wl_resource *resource) { + wl_list_remove(wl_resource_get_link(resource)); +} + +static void manager_bind(struct wl_client *client, void *data, uint32_t version, + uint32_t id) { + struct wlr_screencopy_manager_v1 *manager = data; + + struct wl_resource *resource = wl_resource_create(client, + &zwlr_screencopy_manager_v1_interface, version, id); + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &manager_impl, manager, + manager_handle_resource_destroy); + + wl_list_insert(&manager->resources, wl_resource_get_link(resource)); +} + +static void handle_display_destroy(struct wl_listener *listener, void *data) { + struct wlr_screencopy_manager_v1 *manager = + wl_container_of(listener, manager, display_destroy); + wlr_screencopy_manager_v1_destroy(manager); +} + +struct wlr_screencopy_manager_v1 *wlr_screencopy_manager_v1_create( + struct wl_display *display) { + struct wlr_screencopy_manager_v1 *manager = + calloc(1, sizeof(struct wlr_screencopy_manager_v1)); + if (manager == NULL) { + return NULL; + } + + manager->global = wl_global_create(display, + &zwlr_screencopy_manager_v1_interface, SCREENCOPY_MANAGER_VERSION, + manager, manager_bind); + if (manager->global == NULL) { + free(manager); + return NULL; + } + wl_list_init(&manager->resources); + wl_list_init(&manager->frames); + + manager->display_destroy.notify = handle_display_destroy; + wl_display_add_destroy_listener(display, &manager->display_destroy); + + return manager; +} + +void wlr_screencopy_manager_v1_destroy( + struct wlr_screencopy_manager_v1 *manager) { + if (manager == NULL) { + return; + } + wl_list_remove(&manager->display_destroy.link); + struct wlr_screencopy_frame_v1 *frame, *tmp_frame; + wl_list_for_each_safe(frame, tmp_frame, &manager->frames, link) { + wl_resource_destroy(frame->resource); + } + struct wl_resource *resource, *tmp_resource; + wl_resource_for_each_safe(resource, tmp_resource, &manager->resources) { + wl_resource_destroy(resource); + } + wl_global_destroy(manager->global); + free(manager); +} diff --git a/types/wlr_screenshooter.c b/types/wlr_screenshooter.c index e1386be6..5d9e9de3 100644 --- a/types/wlr_screenshooter.c +++ b/types/wlr_screenshooter.c @@ -50,8 +50,8 @@ static void output_handle_frame(struct wl_listener *listener, void *_data) { int32_t stride = wl_shm_buffer_get_stride(shm_buffer); wl_shm_buffer_begin_access(shm_buffer); void *data = wl_shm_buffer_get_data(shm_buffer); - bool ok = wlr_renderer_read_pixels(renderer, format, stride, width, height, - 0, 0, 0, 0, data); + bool ok = wlr_renderer_read_pixels(renderer, format, NULL, stride, + width, height, 0, 0, 0, 0, data); wl_shm_buffer_end_access(shm_buffer); if (!ok) { diff --git a/types/wlr_surface.c b/types/wlr_surface.c index 59a6fc2c..973686ef 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -32,28 +32,28 @@ static int max(int fst, int snd) { } static void surface_state_reset_buffer(struct wlr_surface_state *state) { - if (state->buffer) { - wl_list_remove(&state->buffer_destroy_listener.link); - state->buffer = NULL; + if (state->buffer_resource) { + wl_list_remove(&state->buffer_destroy.link); + state->buffer_resource = NULL; } } static void surface_handle_buffer_destroy(struct wl_listener *listener, void *data) { struct wlr_surface_state *state = - wl_container_of(listener, state, buffer_destroy_listener); + wl_container_of(listener, state, buffer_destroy); surface_state_reset_buffer(state); } static void surface_state_set_buffer(struct wlr_surface_state *state, - struct wl_resource *buffer) { + struct wl_resource *buffer_resource) { surface_state_reset_buffer(state); - state->buffer = buffer; - if (buffer) { - wl_resource_add_destroy_listener(buffer, - &state->buffer_destroy_listener); - state->buffer_destroy_listener.notify = surface_handle_buffer_destroy; + state->buffer_resource = buffer_resource; + if (buffer_resource != NULL) { + wl_resource_add_destroy_listener(buffer_resource, + &state->buffer_destroy); + state->buffer_destroy.notify = surface_handle_buffer_destroy; } } @@ -64,13 +64,13 @@ static void surface_destroy(struct wl_client *client, static void surface_attach(struct wl_client *client, struct wl_resource *resource, - struct wl_resource *buffer, int32_t sx, int32_t sy) { + struct wl_resource *buffer, int32_t dx, int32_t dy) { struct wlr_surface *surface = wlr_surface_from_resource(resource); - surface->pending->invalid |= WLR_SURFACE_INVALID_BUFFER; - surface->pending->sx = sx; - surface->pending->sy = sy; - surface_state_set_buffer(surface->pending, buffer); + surface->pending.committed |= WLR_SURFACE_STATE_BUFFER; + surface->pending.dx = dx; + surface->pending.dy = dy; + surface_state_set_buffer(&surface->pending, buffer); } static void surface_damage(struct wl_client *client, @@ -80,9 +80,9 @@ static void surface_damage(struct wl_client *client, if (width < 0 || height < 0) { return; } - surface->pending->invalid |= WLR_SURFACE_INVALID_SURFACE_DAMAGE; - pixman_region32_union_rect(&surface->pending->surface_damage, - &surface->pending->surface_damage, + surface->pending.committed |= WLR_SURFACE_STATE_SURFACE_DAMAGE; + pixman_region32_union_rect(&surface->pending.surface_damage, + &surface->pending.surface_damage, x, y, width, height); } @@ -103,192 +103,172 @@ static void surface_frame(struct wl_client *client, wl_resource_set_implementation(callback_resource, NULL, NULL, callback_handle_resource_destroy); - wl_list_insert(surface->pending->frame_callback_list.prev, + wl_list_insert(surface->pending.frame_callback_list.prev, wl_resource_get_link(callback_resource)); - surface->pending->invalid |= WLR_SURFACE_INVALID_FRAME_CALLBACK_LIST; + surface->pending.committed |= WLR_SURFACE_STATE_FRAME_CALLBACK_LIST; } static void surface_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); - if ((surface->pending->invalid & WLR_SURFACE_INVALID_OPAQUE_REGION)) { - pixman_region32_clear(&surface->pending->opaque); + if ((surface->pending.committed & WLR_SURFACE_STATE_OPAQUE_REGION)) { + pixman_region32_clear(&surface->pending.opaque); } - surface->pending->invalid |= WLR_SURFACE_INVALID_OPAQUE_REGION; + 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); + pixman_region32_copy(&surface->pending.opaque, region); } else { - pixman_region32_clear(&surface->pending->opaque); + pixman_region32_clear(&surface->pending.opaque); } } static void surface_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->invalid |= WLR_SURFACE_INVALID_INPUT_REGION; + 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); + pixman_region32_copy(&surface->pending.input, region); } else { - pixman_region32_fini(&surface->pending->input); - pixman_region32_init_rect(&surface->pending->input, + pixman_region32_fini(&surface->pending.input); + pixman_region32_init_rect(&surface->pending.input, INT32_MIN, INT32_MIN, UINT32_MAX, UINT32_MAX); } } -static bool surface_update_size(struct wlr_surface *surface, +static void surface_state_finalize(struct wlr_surface *surface, struct wlr_surface_state *state) { - if (!state->buffer) { - pixman_region32_union_rect(&state->surface_damage, - &state->surface_damage, 0, 0, state->width, state->height); - state->height = 0; - state->width = 0; - return true; + if ((state->committed & WLR_SURFACE_STATE_BUFFER)) { + if (state->buffer_resource != NULL) { + wlr_buffer_get_resource_size(state->buffer_resource, + surface->renderer, &state->buffer_width, &state->buffer_height); + } else { + state->buffer_width = state->buffer_height = 0; + } } - int scale = state->scale; - enum wl_output_transform transform = state->transform; - - wlr_buffer_get_resource_size(state->buffer, surface->renderer, - &state->buffer_width, &state->buffer_height); - - int width = state->buffer_width / scale; - int height = state->buffer_height / scale; - - if (transform == WL_OUTPUT_TRANSFORM_90 || - transform == WL_OUTPUT_TRANSFORM_270 || - transform == WL_OUTPUT_TRANSFORM_FLIPPED_90 || - transform == WL_OUTPUT_TRANSFORM_FLIPPED_270) { + 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; } - - bool update_damage = false; - if (width != state->width || height != state->height) { - // Damage the whole surface on resize - // This isn't in the spec, but Weston does it and QT expects it - pixman_region32_union_rect(&state->surface_damage, - &state->surface_damage, 0, 0, state->width, state->height); - pixman_region32_union_rect(&state->surface_damage, - &state->surface_damage, 0, 0, width, height); - update_damage = true; - } - state->width = width; state->height = height; - return update_damage; + pixman_region32_intersect_rect(&state->surface_damage, + &state->surface_damage, 0, 0, state->width, state->height); + + pixman_region32_intersect_rect(&state->buffer_damage, + &state->buffer_damage, 0, 0, state->buffer_width, + state->buffer_height); } -/** - * Append pending state to current state and clear pending state. - */ -static void surface_move_state(struct wlr_surface *surface, - struct wlr_surface_state *next, struct wlr_surface_state *state) { - bool update_damage = false; - bool update_size = false; +static void surface_update_damage(pixman_region32_t *buffer_damage, + struct wlr_surface_state *previous, struct wlr_surface_state *current) { + pixman_region32_clear(buffer_damage); + + if (current->buffer_width != previous->buffer_width || + current->buffer_height != previous->buffer_height || + current->dx != 0 || current->dy != 0) { + // Damage the whole surface on resize or move + int prev_x = -current->dx; + int prev_y = -current->dy; + if ((previous->transform & WL_OUTPUT_TRANSFORM_90) != 0) { + int tmp = prev_x; + prev_x = prev_y; + prev_y = tmp; + } + + pixman_region32_union_rect(buffer_damage, buffer_damage, + prev_x * previous->scale, prev_y * previous->scale, + previous->buffer_width, previous->buffer_height); + pixman_region32_union_rect(buffer_damage, buffer_damage, 0, 0, + current->buffer_width, current->buffer_height); + } else { + // Copy over surface damage + buffer damage + pixman_region32_union(buffer_damage, buffer_damage, + ¤t->buffer_damage); + + pixman_region32_t surface_damage; + pixman_region32_init(&surface_damage); + pixman_region32_copy(&surface_damage, ¤t->surface_damage); + wlr_region_transform(&surface_damage, &surface_damage, + current->transform, current->buffer_width, current->buffer_height); + wlr_region_scale(&surface_damage, &surface_damage, current->scale); + pixman_region32_union(buffer_damage, buffer_damage, &surface_damage); + pixman_region32_fini(&surface_damage); + } +} - int oldw = state->width; - int oldh = state->height; +static void surface_state_copy(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->invalid & WLR_SURFACE_INVALID_SCALE)) { + if (next->committed & WLR_SURFACE_STATE_SCALE) { state->scale = next->scale; - update_size = true; } - if ((next->invalid & WLR_SURFACE_INVALID_TRANSFORM)) { + if (next->committed & WLR_SURFACE_STATE_TRANSFORM) { state->transform = next->transform; - update_size = true; } - if ((next->invalid & WLR_SURFACE_INVALID_BUFFER)) { - surface_state_set_buffer(state, next->buffer); - surface_state_reset_buffer(next); - state->sx = next->sx; - state->sy = next->sy; - update_size = true; - } - if (update_size) { - update_damage = surface_update_size(surface, state); - } - if ((next->invalid & WLR_SURFACE_INVALID_SURFACE_DAMAGE)) { - pixman_region32_intersect_rect(&next->surface_damage, - &next->surface_damage, 0, 0, state->width, state->height); - pixman_region32_union(&state->surface_damage, &state->surface_damage, - &next->surface_damage); - pixman_region32_clear(&next->surface_damage); - update_damage = true; - } - if ((next->invalid & WLR_SURFACE_INVALID_BUFFER_DAMAGE)) { - pixman_region32_intersect_rect(&next->buffer_damage, - &next->buffer_damage, 0, 0, state->buffer_width, - state->buffer_height); - pixman_region32_union(&state->buffer_damage, &state->buffer_damage, - &next->buffer_damage); - pixman_region32_clear(&next->buffer_damage); - update_damage = true; + if (next->committed & WLR_SURFACE_STATE_BUFFER) { + state->dx = next->dx; + state->dy = next->dy; + } else { + state->dx = state->dy = 0; } - if (update_damage) { - pixman_region32_t buffer_damage, surface_damage; - pixman_region32_init(&buffer_damage); - pixman_region32_init(&surface_damage); - - // Surface to buffer damage - pixman_region32_copy(&buffer_damage, &state->surface_damage); - wlr_region_transform(&buffer_damage, &buffer_damage, - wlr_output_transform_invert(state->transform), - state->width, state->height); - wlr_region_scale(&buffer_damage, &buffer_damage, state->scale); - - // Buffer to surface damage - pixman_region32_copy(&surface_damage, &state->buffer_damage); - wlr_region_transform(&surface_damage, &surface_damage, state->transform, - state->buffer_width, state->buffer_height); - wlr_region_scale(&surface_damage, &surface_damage, 1.0f/state->scale); - - pixman_region32_union(&state->buffer_damage, &state->buffer_damage, - &buffer_damage); - pixman_region32_union(&state->surface_damage, &state->surface_damage, - &surface_damage); - - pixman_region32_fini(&buffer_damage); - pixman_region32_fini(&surface_damage); + if (next->committed & WLR_SURFACE_STATE_SURFACE_DAMAGE) { + pixman_region32_copy(&state->surface_damage, &next->surface_damage); + } else { + pixman_region32_clear(&state->surface_damage); } - if ((next->invalid & WLR_SURFACE_INVALID_OPAQUE_REGION)) { - // TODO: process buffer - pixman_region32_clear(&next->opaque); + if (next->committed & WLR_SURFACE_STATE_BUFFER_DAMAGE) { + pixman_region32_copy(&state->buffer_damage, &next->buffer_damage); + } else { + pixman_region32_clear(&state->buffer_damage); } - if ((next->invalid & WLR_SURFACE_INVALID_INPUT_REGION)) { - // TODO: process buffer + 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->invalid & WLR_SURFACE_INVALID_SUBSURFACE_POSITION)) { - // Subsurface has moved - int dx = state->subsurface_position.x - next->subsurface_position.x; - int dy = state->subsurface_position.y - next->subsurface_position.y; - - state->subsurface_position.x = next->subsurface_position.x; - state->subsurface_position.y = next->subsurface_position.y; - next->subsurface_position.x = 0; - next->subsurface_position.y = 0; - - if (dx != 0 || dy != 0) { - pixman_region32_union_rect(&state->surface_damage, - &state->surface_damage, dx, dy, oldw, oldh); - pixman_region32_union_rect(&state->surface_damage, - &state->surface_damage, 0, 0, state->width, state->height); - } + + state->committed |= next->committed; +} + +/** + * 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) { + surface_state_copy(state, next); + + if (next->committed & WLR_SURFACE_STATE_BUFFER) { + surface_state_set_buffer(state, next->buffer_resource); + surface_state_reset_buffer(next); + next->dx = next->dy = 0; } - if ((next->invalid & WLR_SURFACE_INVALID_FRAME_CALLBACK_LIST)) { + if (next->committed & WLR_SURFACE_STATE_SURFACE_DAMAGE) { + pixman_region32_clear(&next->surface_damage); + } + if (next->committed & WLR_SURFACE_STATE_BUFFER_DAMAGE) { + pixman_region32_clear(&next->buffer_damage); + } + 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->invalid |= next->invalid; - next->invalid = 0; + next->committed = 0; } static void surface_damage_subsurfaces(struct wlr_subsurface *subsurface) { @@ -297,10 +277,9 @@ static void surface_damage_subsurfaces(struct wlr_subsurface *subsurface) { // 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->current->surface_damage, - &surface->current->surface_damage, - 0, 0, surface->current->width, - surface->current->height); + pixman_region32_union_rect(&surface->buffer_damage, + &surface->buffer_damage, 0, 0, + surface->current.buffer_width, surface->current.buffer_height); subsurface->reordered = false; @@ -311,7 +290,7 @@ static void surface_damage_subsurfaces(struct wlr_subsurface *subsurface) { } static void surface_apply_damage(struct wlr_surface *surface) { - struct wl_resource *resource = surface->current->buffer; + struct wl_resource *resource = surface->current.buffer_resource; if (resource == NULL) { // NULL commit wlr_buffer_unref(surface->buffer); @@ -322,9 +301,21 @@ static void surface_apply_damage(struct wlr_surface *surface) { if (surface->buffer != NULL && surface->buffer->released) { pixman_region32_t damage; pixman_region32_init(&damage); - pixman_region32_copy(&damage, &surface->current->buffer_damage); + pixman_region32_copy(&damage, &surface->current.buffer_damage); + + pixman_region32_t surface_damage; + pixman_region32_init(&surface_damage); + pixman_region32_copy(&surface_damage, &surface->current.surface_damage); + wlr_region_transform(&surface_damage, &surface_damage, + surface->current.transform, + surface->current.buffer_width, surface->current.buffer_height); + wlr_region_scale(&surface_damage, &surface_damage, + surface->current.scale); + pixman_region32_union(&damage, &damage, &surface_damage); + pixman_region32_fini(&surface_damage); + pixman_region32_intersect_rect(&damage, &damage, 0, 0, - surface->current->buffer_width, surface->current->buffer_height); + surface->current.buffer_width, surface->current.buffer_height); struct wlr_buffer *updated_buffer = wlr_buffer_apply_damage(surface->buffer, resource, &damage); @@ -350,9 +341,17 @@ static void surface_apply_damage(struct wlr_surface *surface) { } static void surface_commit_pending(struct wlr_surface *surface) { - bool invalid_buffer = surface->pending->invalid & WLR_SURFACE_INVALID_BUFFER; + bool invalid_buffer = surface->pending.committed & WLR_SURFACE_STATE_BUFFER; + + surface_state_finalize(surface, &surface->pending); - surface_move_state(surface, surface->pending, surface->current); + surface->sx += surface->pending.dx; + surface->sy += surface->pending.dy; + surface_update_damage(&surface->buffer_damage, + &surface->current, &surface->pending); + + surface_state_copy(&surface->previous, &surface->current); + surface_state_move(&surface->current, &surface->pending); if (invalid_buffer) { surface_apply_damage(surface); @@ -375,11 +374,7 @@ static void surface_commit_pending(struct wlr_surface *surface) { surface->role_committed(surface, surface->role_data); } - // TODO: add the invalid bitfield to this callback wlr_signal_emit_safe(&surface->events.commit, surface); - - pixman_region32_clear(&surface->current->surface_damage); - pixman_region32_clear(&surface->current->buffer_damage); } static bool subsurface_is_synchronized(struct wlr_subsurface *subsurface) { @@ -395,7 +390,7 @@ static bool subsurface_is_synchronized(struct wlr_subsurface *subsurface) { if (!wlr_surface_is_subsurface(subsurface->parent)) { break; } - subsurface = wlr_subsurface_from_surface(subsurface->parent); + subsurface = wlr_subsurface_from_wlr_surface(subsurface->parent); } return false; @@ -406,18 +401,18 @@ static bool subsurface_is_synchronized(struct wlr_subsurface *subsurface) { */ static void subsurface_parent_commit(struct wlr_subsurface *subsurface, bool synchronized) { - struct wlr_surface *surface = subsurface->surface; + struct wlr_surface *surface = subsurface->surface; if (synchronized || subsurface->synchronized) { if (subsurface->has_cache) { - surface_move_state(surface, subsurface->cached, surface->pending); + surface_state_move(&surface->pending, &subsurface->cached); surface_commit_pending(surface); subsurface->has_cache = false; - subsurface->cached->invalid = 0; + subsurface->cached.committed = 0; } - struct wlr_subsurface *tmp; - wl_list_for_each(tmp, &surface->subsurfaces, parent_link) { - subsurface_parent_commit(tmp, true); + struct wlr_subsurface *subsurface; + wl_list_for_each(subsurface, &surface->subsurfaces, parent_link) { + subsurface_parent_commit(subsurface, true); } } } @@ -426,22 +421,16 @@ static void subsurface_commit(struct wlr_subsurface *subsurface) { struct wlr_surface *surface = subsurface->surface; if (subsurface_is_synchronized(subsurface)) { - surface_move_state(surface, surface->pending, subsurface->cached); + surface_state_move(&subsurface->cached, &surface->pending); subsurface->has_cache = true; } else { if (subsurface->has_cache) { - surface_move_state(surface, subsurface->cached, surface->pending); + surface_state_move(&surface->pending, &subsurface->cached); surface_commit_pending(surface); subsurface->has_cache = false; - } else { surface_commit_pending(surface); } - - struct wlr_subsurface *tmp; - wl_list_for_each(tmp, &surface->subsurfaces, parent_link) { - subsurface_parent_commit(tmp, false); - } } } @@ -451,32 +440,31 @@ static void surface_commit(struct wl_client *client, if (wlr_surface_is_subsurface(surface)) { struct wlr_subsurface *subsurface = - wlr_subsurface_from_surface(surface); + wlr_subsurface_from_wlr_surface(surface); subsurface_commit(subsurface); - return; + } else { + surface_commit_pending(surface); } - surface_commit_pending(surface); - - struct wlr_subsurface *tmp; - wl_list_for_each(tmp, &surface->subsurfaces, parent_link) { - subsurface_parent_commit(tmp, false); + struct wlr_subsurface *subsurface; + wl_list_for_each(subsurface, &surface->subsurfaces, parent_link) { + subsurface_parent_commit(subsurface, false); } } static void surface_set_buffer_transform(struct wl_client *client, struct wl_resource *resource, int transform) { struct wlr_surface *surface = wlr_surface_from_resource(resource); - surface->pending->invalid |= WLR_SURFACE_INVALID_TRANSFORM; - surface->pending->transform = transform; + surface->pending.committed |= WLR_SURFACE_STATE_TRANSFORM; + surface->pending.transform = transform; } static void surface_set_buffer_scale(struct wl_client *client, struct wl_resource *resource, int32_t scale) { struct wlr_surface *surface = wlr_surface_from_resource(resource); - surface->pending->invalid |= WLR_SURFACE_INVALID_SCALE; - surface->pending->scale = scale; + surface->pending.committed |= WLR_SURFACE_STATE_SCALE; + surface->pending.scale = scale; } static void surface_damage_buffer(struct wl_client *client, @@ -487,9 +475,9 @@ static void surface_damage_buffer(struct wl_client *client, if (width < 0 || height < 0) { return; } - surface->pending->invalid |= WLR_SURFACE_INVALID_BUFFER_DAMAGE; - pixman_region32_union_rect(&surface->pending->buffer_damage, - &surface->pending->buffer_damage, + surface->pending.committed |= WLR_SURFACE_STATE_BUFFER_DAMAGE; + pixman_region32_union_rect(&surface->pending.buffer_damage, + &surface->pending.buffer_damage, x, y, width, height); } @@ -512,12 +500,7 @@ struct wlr_surface *wlr_surface_from_resource(struct wl_resource *resource) { return wl_resource_get_user_data(resource); } -static struct wlr_surface_state *surface_state_create(void) { - struct wlr_surface_state *state = - calloc(1, sizeof(struct wlr_surface_state)); - if (state == NULL) { - return NULL; - } +static void surface_state_init(struct wlr_surface_state *state) { state->scale = 1; state->transform = WL_OUTPUT_TRANSFORM_NORMAL; @@ -528,12 +511,11 @@ static struct wlr_surface_state *surface_state_create(void) { pixman_region32_init(&state->opaque); pixman_region32_init_rect(&state->input, INT32_MIN, INT32_MIN, UINT32_MAX, UINT32_MAX); - - return state; } -static void surface_state_destroy(struct wlr_surface_state *state) { +static void surface_state_finish(struct wlr_surface_state *state) { surface_state_reset_buffer(state); + struct wl_resource *resource, *tmp; wl_resource_for_each_safe(resource, tmp, &state->frame_callback_list) { wl_resource_destroy(resource); @@ -543,8 +525,6 @@ static void surface_state_destroy(struct wlr_surface_state *state) { pixman_region32_fini(&state->buffer_damage); pixman_region32_fini(&state->opaque); pixman_region32_fini(&state->input); - - free(state); } static void subsurface_destroy(struct wlr_subsurface *subsurface) { @@ -555,7 +535,7 @@ static void subsurface_destroy(struct wlr_subsurface *subsurface) { wlr_signal_emit_safe(&subsurface->events.destroy, subsurface); wl_list_remove(&subsurface->surface_destroy.link); - surface_state_destroy(subsurface->cached); + surface_state_finish(&subsurface->cached); if (subsurface->parent) { wl_list_remove(&subsurface->parent_link); @@ -578,8 +558,10 @@ static void surface_handle_resource_destroy(struct wl_resource *resource) { wl_list_remove(wl_resource_get_link(surface->resource)); wl_list_remove(&surface->renderer_destroy.link); - surface_state_destroy(surface->pending); - surface_state_destroy(surface->current); + surface_state_finish(&surface->pending); + surface_state_finish(&surface->current); + surface_state_finish(&surface->previous); + pixman_region32_fini(&surface->buffer_damage); wlr_buffer_unref(surface->buffer); free(surface); } @@ -615,14 +597,16 @@ struct wlr_surface *wlr_surface_create(struct wl_client *client, surface->renderer = renderer; - surface->current = surface_state_create(); - surface->pending = surface_state_create(); + surface_state_init(&surface->current); + surface_state_init(&surface->pending); + surface_state_init(&surface->previous); wl_signal_init(&surface->events.commit); wl_signal_init(&surface->events.destroy); wl_signal_init(&surface->events.new_subsurface); wl_list_init(&surface->subsurfaces); wl_list_init(&surface->subsurface_pending_list); + pixman_region32_init(&surface->buffer_damage); wl_signal_add(&renderer->events.destroy, &surface->renderer_destroy); surface->renderer_destroy.notify = surface_handle_renderer_destroy; @@ -696,10 +680,8 @@ static void subsurface_handle_set_position(struct wl_client *client, return; } - struct wlr_surface *surface = subsurface->surface; - surface->pending->invalid |= WLR_SURFACE_INVALID_SUBSURFACE_POSITION; - surface->pending->subsurface_position.x = x; - surface->pending->subsurface_position.y = y; + subsurface->pending.x = x; + subsurface->pending.y = y; } static struct wlr_subsurface *subsurface_find_sibling( @@ -806,6 +788,34 @@ static const struct wl_subsurface_interface subsurface_implementation = { .set_desync = subsurface_handle_set_desync, }; +static void subsurface_role_committed(struct wlr_surface *surface, void *data) { + struct wlr_subsurface *subsurface = data; + + 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); + } +} + static void subsurface_handle_parent_destroy(struct wl_listener *listener, void *data) { struct wlr_subsurface *subsurface = @@ -836,18 +846,13 @@ struct wlr_subsurface *wlr_subsurface_create(struct wlr_surface *surface, wl_client_post_no_memory(client); return NULL; } - subsurface->cached = surface_state_create(); - if (subsurface->cached == NULL) { - free(subsurface); - wl_client_post_no_memory(client); - return NULL; - } + surface_state_init(&subsurface->cached); subsurface->synchronized = true; subsurface->surface = surface; subsurface->resource = wl_resource_create(client, &wl_subsurface_interface, version, id); if (subsurface->resource == NULL) { - surface_state_destroy(subsurface->cached); + surface_state_finish(&subsurface->cached); free(subsurface); wl_client_post_no_memory(client); return NULL; @@ -869,7 +874,8 @@ struct wlr_subsurface *wlr_subsurface_create(struct wlr_surface *surface, wl_list_insert(parent->subsurface_pending_list.prev, &subsurface->parent_pending_link); - surface->role_data = subsurface; + wlr_surface_set_role_committed(surface, subsurface_role_committed, + subsurface); struct wl_list *resource_link = wl_resource_get_link(subsurface->resource); if (resource_list != NULL) { @@ -887,7 +893,7 @@ struct wlr_subsurface *wlr_subsurface_create(struct wlr_surface *surface, 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_surface(surface); + wlr_subsurface_from_wlr_surface(surface); surface = subsurface->parent; } return surface; @@ -895,17 +901,17 @@ struct wlr_surface *wlr_surface_get_root_surface(struct wlr_surface *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, sx, sy, NULL); + return sx >= 0 && sx < surface->current.width && + sy >= 0 && sy < surface->current.height && + pixman_region32_contains_point(&surface->current.input, sx, 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->subsurfaces, parent_link) { - double _sub_x = subsurface->surface->current->subsurface_position.x; - double _sub_y = subsurface->surface->current->subsurface_position.y; + 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) { @@ -952,7 +958,7 @@ 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) { + &surface->current.frame_callback_list) { wl_callback_send_done(resource, timespec_to_msec(when)); wl_resource_destroy(resource); } @@ -971,9 +977,9 @@ static void surface_for_each_surface(struct wlr_surface *surface, int x, int y, struct wlr_subsurface *subsurface; wl_list_for_each(subsurface, &surface->subsurfaces, parent_link) { - struct wlr_surface_state *state = subsurface->surface->current; - int sx = state->subsurface_position.x; - int sy = state->subsurface_position.y; + struct wlr_subsurface_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); @@ -997,16 +1003,16 @@ static void handle_bounding_box_surface(struct wlr_surface *surface, 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); + 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, + .max_x = surface->current.width, + .max_y = surface->current.height, }; wlr_surface_for_each_surface(surface, handle_bounding_box_surface, &acc); diff --git a/types/xdg_shell/wlr_xdg_popup.c b/types/xdg_shell/wlr_xdg_popup.c index 284f36ad..bd969147 100644 --- a/types/xdg_shell/wlr_xdg_popup.c +++ b/types/xdg_shell/wlr_xdg_popup.c @@ -152,6 +152,9 @@ static void xdg_popup_handle_grab(struct wl_client *client, wlr_xdg_surface_from_popup_resource(resource); struct wlr_seat_client *seat_client = wlr_seat_client_from_resource(seat_resource); + if (!surface) { + return; + } if (surface->popup->committed) { wl_resource_post_error(surface->popup->resource, @@ -186,7 +189,7 @@ static void xdg_popup_handle_destroy(struct wl_client *client, struct wlr_xdg_surface *surface = wlr_xdg_surface_from_popup_resource(resource); - if (!wl_list_empty(&surface->popups)) { + if (surface && !wl_list_empty(&surface->popups)) { wl_resource_post_error(surface->client->resource, XDG_WM_BASE_ERROR_NOT_THE_TOPMOST_POPUP, "xdg_popup was destroyed while it was not the topmost popup"); diff --git a/types/xdg_shell/wlr_xdg_surface.c b/types/xdg_shell/wlr_xdg_surface.c index 6931b92f..9eda8aa1 100644 --- a/types/xdg_shell/wlr_xdg_surface.c +++ b/types/xdg_shell/wlr_xdg_surface.c @@ -557,11 +557,36 @@ static void xdg_surface_for_each_surface(struct wlr_xdg_surface *surface, } } +static void xdg_surface_for_each_popup(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) { + continue; + } + + double popup_sx, popup_sy; + xdg_popup_get_position(popup_state, &popup_sx, &popup_sy); + iterator(popup->surface, x + popup_sx, y + popup_sy, user_data); + + xdg_surface_for_each_popup(popup, + x + popup_sx, + y + popup_sy, + iterator, user_data); + } +} + void wlr_xdg_surface_for_each_surface(struct wlr_xdg_surface *surface, wlr_surface_iterator_func_t iterator, void *user_data) { xdg_surface_for_each_surface(surface, 0, 0, iterator, user_data); } +void wlr_xdg_surface_for_each_popup(struct wlr_xdg_surface *surface, + wlr_surface_iterator_func_t iterator, void *user_data) { + xdg_surface_for_each_popup(surface, 0, 0, iterator, user_data); +} + void wlr_xdg_surface_get_geometry(struct wlr_xdg_surface *surface, struct wlr_box *box) { wlr_surface_get_extends(surface->surface, box); /* The client never set the geometry */ diff --git a/types/xdg_shell/wlr_xdg_toplevel.c b/types/xdg_shell/wlr_xdg_toplevel.c index c1350931..5d0bfa1f 100644 --- a/types/xdg_shell/wlr_xdg_toplevel.c +++ b/types/xdg_shell/wlr_xdg_toplevel.c @@ -39,8 +39,8 @@ bool compare_xdg_surface_toplevel_state(struct wlr_xdg_toplevel *state) { if (wl_list_empty(&state->base->configure_list)) { // last configure is actually the current state, just use it configured.state = state->current; - configured.width = state->base->surface->current->width; - configured.height = state->base->surface->current->height; + configured.width = state->base->surface->current.width; + configured.height = state->base->surface->current.height; } else { struct wlr_xdg_surface_configure *configure = wl_container_of(state->base->configure_list.prev, configure, link); diff --git a/types/xdg_shell_v6/wlr_xdg_popup_v6.c b/types/xdg_shell_v6/wlr_xdg_popup_v6.c index 334519a5..378ae963 100644 --- a/types/xdg_shell_v6/wlr_xdg_popup_v6.c +++ b/types/xdg_shell_v6/wlr_xdg_popup_v6.c @@ -174,6 +174,9 @@ static void xdg_popup_handle_grab(struct wl_client *client, xdg_surface_from_xdg_popup_resource(resource); struct wlr_seat_client *seat_client = wlr_seat_client_from_resource(seat_resource); + if (!surface) { + return; + } if (surface->popup->committed) { wl_resource_post_error(surface->popup->resource, @@ -214,7 +217,7 @@ static void xdg_popup_handle_destroy(struct wl_client *client, struct wlr_xdg_surface_v6 *surface = xdg_surface_from_xdg_popup_resource(resource); - if (!wl_list_empty(&surface->popups)) { + if (surface && !wl_list_empty(&surface->popups)) { wl_resource_post_error(surface->client->resource, ZXDG_SHELL_V6_ERROR_NOT_THE_TOPMOST_POPUP, "xdg_popup was destroyed while it was not the topmost popup"); diff --git a/types/xdg_shell_v6/wlr_xdg_surface_v6.c b/types/xdg_shell_v6/wlr_xdg_surface_v6.c index 7343906f..046dd4ed 100644 --- a/types/xdg_shell_v6/wlr_xdg_surface_v6.c +++ b/types/xdg_shell_v6/wlr_xdg_surface_v6.c @@ -536,11 +536,36 @@ static void xdg_surface_v6_for_each_surface(struct wlr_xdg_surface_v6 *surface, } } +static void xdg_surface_v6_for_each_popup(struct wlr_xdg_surface_v6 *surface, + int x, int y, wlr_surface_iterator_func_t iterator, void *user_data) { + struct wlr_xdg_popup_v6 *popup_state; + wl_list_for_each(popup_state, &surface->popups, link) { + struct wlr_xdg_surface_v6 *popup = popup_state->base; + if (!popup->configured) { + continue; + } + + double popup_sx, popup_sy; + xdg_popup_v6_get_position(popup_state, &popup_sx, &popup_sy); + iterator(popup->surface, x + popup_sx, y + popup_sy, user_data); + + xdg_surface_v6_for_each_popup(popup, + x + popup_sx, + y + popup_sy, + iterator, user_data); + } +} + void wlr_xdg_surface_v6_for_each_surface(struct wlr_xdg_surface_v6 *surface, wlr_surface_iterator_func_t iterator, void *user_data) { xdg_surface_v6_for_each_surface(surface, 0, 0, iterator, user_data); } +void wlr_xdg_surface_v6_for_each_popup(struct wlr_xdg_surface_v6 *surface, + wlr_surface_iterator_func_t iterator, void *user_data) { + xdg_surface_v6_for_each_popup(surface, 0, 0, iterator, user_data); +} + void wlr_xdg_surface_v6_get_geometry(struct wlr_xdg_surface_v6 *surface, struct wlr_box *box) { wlr_surface_get_extends(surface->surface, box); /* The client never set the geometry */ diff --git a/types/xdg_shell_v6/wlr_xdg_toplevel_v6.c b/types/xdg_shell_v6/wlr_xdg_toplevel_v6.c index e2e1e480..29cbe2dd 100644 --- a/types/xdg_shell_v6/wlr_xdg_toplevel_v6.c +++ b/types/xdg_shell_v6/wlr_xdg_toplevel_v6.c @@ -287,8 +287,8 @@ bool compare_xdg_surface_v6_toplevel_state(struct wlr_xdg_toplevel_v6 *state) { if (wl_list_empty(&state->base->configure_list)) { // last configure is actually the current state, just use it configured.state = state->current; - configured.width = state->base->surface->current->width; - configured.height = state->base->surface->current->height; + configured.width = state->base->surface->current.width; + configured.height = state->base->surface->current.height; } else { struct wlr_xdg_surface_v6_configure *configure = wl_container_of(state->base->configure_list.prev, configure, link); diff --git a/util/os-compatibility.c b/util/os-compatibility.c index bd3067d2..38333605 100644 --- a/util/os-compatibility.c +++ b/util/os-compatibility.c @@ -29,6 +29,7 @@ #include <stdlib.h> #include <string.h> #include <sys/socket.h> +#include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include "util/os-compatibility.h" @@ -61,6 +62,7 @@ int create_tmpfile_cloexec(char *tmpname) { int fd; + mode_t prev_umask = umask(0066); #ifdef HAVE_MKOSTEMP fd = mkostemp(tmpname, O_CLOEXEC); if (fd >= 0) @@ -72,6 +74,7 @@ int create_tmpfile_cloexec(char *tmpname) unlink(tmpname); } #endif + umask(prev_umask); return fd; } diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 66a85d05..62d6a861 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -214,18 +214,26 @@ static void xwm_send_focus_window(struct wlr_xwm *xwm, XCB_INPUT_FOCUS_POINTER_ROOT, XCB_NONE, XCB_CURRENT_TIME); return; - } else if (xsurface->override_redirect) { + } + + if (xsurface->override_redirect) { return; } xcb_client_message_data_t message_data = { 0 }; message_data.data32[0] = xwm->atoms[WM_TAKE_FOCUS]; message_data.data32[1] = XCB_TIME_CURRENT_TIME; - xwm_send_wm_message(xsurface, &message_data, - XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT); - xcb_set_input_focus(xwm->xcb_conn, XCB_INPUT_FOCUS_POINTER_ROOT, - xsurface->window_id, XCB_CURRENT_TIME); + if (xsurface->hints && !xsurface->hints->input) { + // if the surface doesn't allow the focus request, we will send him + // only the take focus event. It will get the focus by itself. + xwm_send_wm_message(xsurface, &message_data, XCB_EVENT_MASK_NO_EVENT); + } else { + xwm_send_wm_message(xsurface, &message_data, XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT); + + xcb_set_input_focus(xwm->xcb_conn, XCB_INPUT_FOCUS_POINTER_ROOT, + xsurface->window_id, XCB_CURRENT_TIME); + } uint32_t values[1]; values[0] = XCB_STACK_MODE_ABOVE; |