diff --git a/backend/libinput/backend.c b/backend/libinput/backend.c index f4d54c97..1dde5854 100644 --- a/backend/libinput/backend.c +++ b/backend/libinput/backend.c @@ -55,8 +55,8 @@ static bool backend_start(struct wlr_backend *_backend) { return false; } - // TODO: Let user customize seat used - if (libinput_udev_assign_seat(backend->libinput_context, "seat0") != 0) { + if (libinput_udev_assign_seat(backend->libinput_context, + backend->session->seat) != 0) { wlr_log(L_ERROR, "Failed to assign libinput seat"); return false; } diff --git a/backend/session/direct.c b/backend/session/direct.c index 13a26d99..7fa7d05b 100644 --- a/backend/session/direct.c +++ b/backend/session/direct.c @@ -1,5 +1,6 @@ #define _POSIX_C_SOURCE 200809L #include +#include #include #include #include @@ -76,30 +77,39 @@ static void direct_session_close(struct wlr_session *base, int fd) { static bool direct_change_vt(struct wlr_session *base, unsigned vt) { struct direct_session *session = wl_container_of(base, session, base); + + // Only seat0 has VTs associated with it + if (strcmp(session->base.seat, "seat0") != 0) { + return true; + } + return ioctl(session->tty_fd, VT_ACTIVATE, (int)vt) == 0; } static void direct_session_destroy(struct wlr_session *base) { struct direct_session *session = wl_container_of(base, session, base); - struct vt_mode mode = { - .mode = VT_AUTO, - }; - errno = 0; + if (strcmp(session->base.seat, "seat0") == 0) { + struct vt_mode mode = { + .mode = VT_AUTO, + }; + errno = 0; + + ioctl(session->tty_fd, KDSKBMODE, session->old_kbmode); + ioctl(session->tty_fd, KDSETMODE, KD_TEXT); + ioctl(session->tty_fd, VT_SETMODE, &mode); - ioctl(session->tty_fd, KDSKBMODE, session->old_kbmode); - ioctl(session->tty_fd, KDSETMODE, KD_TEXT); - ioctl(session->tty_fd, VT_SETMODE, &mode); + if (errno) { + wlr_log(L_ERROR, "Failed to restore tty"); + } - if (errno) { - wlr_log(L_ERROR, "Failed to restore tty"); + wl_event_source_remove(session->vt_source); + close(session->tty_fd); } direct_ipc_finish(session->sock, session->child); close(session->sock); - wl_event_source_remove(session->vt_source); - close(session->tty_fd); free(session); } @@ -138,19 +148,19 @@ static int vt_handler(int signo, void *data) { } static bool setup_tty(struct direct_session *session, struct wl_display *display) { - int fd = dup(STDIN_FILENO); + int fd = open("/dev/tty", O_RDWR); if (fd == -1) { - wlr_log_errno(L_ERROR, "Cannot open tty"); + wlr_log_errno(L_ERROR, "Cannot open /dev/tty"); return false; } - struct stat st; - if (fstat(fd, &st) == -1 || major(st.st_rdev) != TTY_MAJOR || minor(st.st_rdev) == 0) { - wlr_log(L_ERROR, "Not running from a virtual terminal"); + struct vt_stat vt_stat; + if (ioctl(fd, VT_GETSTATE, &vt_stat)) { + wlr_log_errno(L_ERROR, "Could not get current tty number"); goto error; } - int tty = minor(st.st_rdev); + int tty = vt_stat.v_active; int ret, kd_mode, old_kbmode; ret = ioctl(fd, KDGETMODE, &kd_mode); @@ -224,20 +234,24 @@ static struct wlr_session *direct_session_create(struct wl_display *disp) { goto error_session; } - if (!setup_tty(session, disp)) { - goto error_ipc; - } - - // XXX: Is it okay to trust the environment like this? const char *seat = getenv("XDG_SEAT"); if (!seat) { seat = "seat0"; } - wlr_log(L_INFO, "Successfully loaded direct session"); + if (strcmp(seat, "seat0") == 0) { + if (!setup_tty(session, disp)) { + goto error_ipc; + } + } else { + session->base.vtnr = 0; + session->tty_fd = -1; + } snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat); session->base.impl = &session_direct; + + wlr_log(L_INFO, "Successfully loaded direct session"); return &session->base; error_ipc: diff --git a/backend/session/logind.c b/backend/session/logind.c index f0ac1e93..3a3cc57d 100644 --- a/backend/session/logind.c +++ b/backend/session/logind.c @@ -109,6 +109,11 @@ static void logind_release_device(struct wlr_session *base, int fd) { static bool logind_change_vt(struct wlr_session *base, unsigned vt) { struct logind_session *session = wl_container_of(base, session, base); + // Only seat0 has VTs associated with it + if (strcmp(session->base.seat, "seat0") != 0) { + return true; + } + int ret; sd_bus_message *msg = NULL; sd_bus_error error = SD_BUS_ERROR_NULL; diff --git a/backend/session/session.c b/backend/session/session.c index 2d5d9776..026040fd 100644 --- a/backend/session/session.c +++ b/backend/session/session.c @@ -19,9 +19,7 @@ extern const struct session_impl session_logind; extern const struct session_impl session_direct; static const struct session_impl *impls[] = { -#ifdef WLR_HAS_SYSTEMD - &session_logind, -#elif defined(WLR_HAS_ELOGIND) +#if defined(WLR_HAS_SYSTEMD) || defined(WLR_HAS_ELOGIND) &session_logind, #endif &session_direct, diff --git a/backend/wayland/wl_seat.c b/backend/wayland/wl_seat.c index cf9b9372..8ed61409 100644 --- a/backend/wayland/wl_seat.c +++ b/backend/wayland/wl_seat.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -169,12 +170,51 @@ static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard, // TODO: set keymap } +static uint32_t get_current_time_msec() { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + return now.tv_nsec / 1000; +} + static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) { + struct wlr_input_device *dev = data; + + uint32_t time = get_current_time_msec(); + + uint32_t *keycode_ptr; + wl_array_for_each(keycode_ptr, keys) { + struct wlr_event_keyboard_key event = { + .keycode = *keycode_ptr, + .state = WLR_KEY_PRESSED, + .time_msec = time, + .update_state = false, + }; + wlr_keyboard_notify_key(dev->keyboard, &event); + } } static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface) { + struct wlr_input_device *dev = data; + + uint32_t time = get_current_time_msec(); + + uint32_t pressed[dev->keyboard->num_keycodes]; + memcpy(pressed, dev->keyboard->keycodes, + dev->keyboard->num_keycodes * sizeof(uint32_t)); + + for (size_t i = 0; i < sizeof(pressed)/sizeof(pressed[0]); ++i) { + uint32_t keycode = pressed[i]; + + struct wlr_event_keyboard_key event = { + .keycode = keycode, + .state = WLR_KEY_RELEASED, + .time_msec = time, + .update_state = false, + }; + wlr_keyboard_notify_key(dev->keyboard, &event); + } } static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard, diff --git a/include/render/gles2.h b/include/render/gles2.h index 99beff29..67d4e9f5 100644 --- a/include/render/gles2.h +++ b/include/render/gles2.h @@ -25,6 +25,14 @@ struct wlr_gles2_pixel_format { bool has_alpha; }; +struct wlr_gles2_tex_shader { + GLuint program; + GLint proj; + GLint invert_y; + GLint tex; + GLint alpha; +}; + struct wlr_gles2_renderer { struct wlr_renderer wlr_renderer; @@ -32,11 +40,19 @@ struct wlr_gles2_renderer { const char *exts_str; struct { - GLuint quad; - GLuint ellipse; - GLuint tex_rgba; - GLuint tex_rgbx; - GLuint tex_ext; + struct { + GLuint program; + GLint proj; + GLint color; + } quad; + struct { + GLuint program; + GLint proj; + GLint color; + } ellipse; + struct wlr_gles2_tex_shader tex_rgba; + struct wlr_gles2_tex_shader tex_rgbx; + struct wlr_gles2_tex_shader tex_ext; } shaders; uint32_t viewport_width, viewport_height; diff --git a/include/rootston/seat.h b/include/rootston/seat.h index d2ef90f3..0e3043dd 100644 --- a/include/rootston/seat.h +++ b/include/rootston/seat.h @@ -59,6 +59,7 @@ struct roots_drag_icon { struct wl_listener surface_commit; struct wl_listener map; + struct wl_listener unmap; struct wl_listener destroy; }; @@ -94,9 +95,6 @@ void roots_seat_destroy(struct roots_seat *seat); void roots_seat_add_device(struct roots_seat *seat, struct wlr_input_device *device); -void roots_seat_remove_device(struct roots_seat *seat, - struct wlr_input_device *device); - void roots_seat_configure_cursor(struct roots_seat *seat); void roots_seat_configure_xcursor(struct roots_seat *seat); diff --git a/include/wlr/backend/session.h b/include/wlr/backend/session.h index 4d04b363..1cf41939 100644 --- a/include/wlr/backend/session.h +++ b/include/wlr/backend/session.h @@ -25,8 +25,12 @@ struct wlr_session { struct wl_signal session_signal; bool active; + /* + * 0 if virtual terminals are not supported + * i.e. seat != "seat0" + */ unsigned vtnr; - char seat[8]; + char seat[256]; struct udev *udev; struct udev_monitor *mon; diff --git a/include/wlr/render/egl.h b/include/wlr/render/egl.h index 39b1d3d9..6b887f4f 100644 --- a/include/wlr/render/egl.h +++ b/include/wlr/render/egl.h @@ -16,13 +16,15 @@ struct wlr_egl { const char *exts_str; struct { - bool buffer_age; - bool swap_buffers_with_damage; - bool dmabuf_import; - bool dmabuf_import_modifiers; - bool dmabuf_export; - bool bind_wayland_display; - } egl_exts; + bool bind_wayland_display_wl; + bool buffer_age_ext; + bool image_base_khr; + bool image_dma_buf_export_mesa; + bool image_dmabuf_import_ext; + bool image_dmabuf_import_modifiers_ext; + bool swap_buffers_with_damage_ext; + bool swap_buffers_with_damage_khr; + } exts; struct wl_display *wl_display; }; @@ -68,13 +70,6 @@ EGLImageKHR wlr_egl_create_image_from_wl_drm(struct wlr_egl *egl, EGLImageKHR wlr_egl_create_image_from_dmabuf(struct wlr_egl *egl, struct wlr_dmabuf_attributes *attributes); -/** - * Try to import the given dmabuf. On success return true false otherwise. - * If this succeeds the dmabuf can be used for rendering on a texture - */ -bool wlr_egl_check_import_dmabuf(struct wlr_egl *egl, - struct wlr_dmabuf_attributes *attributes); - /** * Get the available dmabuf formats */ diff --git a/include/wlr/render/interface.h b/include/wlr/render/interface.h index af4bc75e..fb427c89 100644 --- a/include/wlr/render/interface.h +++ b/include/wlr/render/interface.h @@ -30,8 +30,6 @@ struct wlr_renderer_impl { struct wl_resource *resource); void (*wl_drm_buffer_get_size)(struct wlr_renderer *renderer, struct wl_resource *buffer, int *width, int *height); - bool (*check_import_dmabuf)(struct wlr_renderer *renderer, - struct wlr_dmabuf_attributes *attribs); int (*get_dmabuf_formats)(struct wlr_renderer *renderer, int **formats); int (*get_dmabuf_modifiers)(struct wlr_renderer *renderer, int format, uint64_t **modifiers); diff --git a/include/wlr/render/wlr_renderer.h b/include/wlr/render/wlr_renderer.h index dd62944f..c715e4b0 100644 --- a/include/wlr/render/wlr_renderer.h +++ b/include/wlr/render/wlr_renderer.h @@ -84,12 +84,6 @@ int wlr_renderer_get_dmabuf_formats(struct wlr_renderer *renderer, */ int wlr_renderer_get_dmabuf_modifiers(struct wlr_renderer *renderer, int format, uint64_t **modifiers); -/** - * Try to import the given dmabuf. On success return true false otherwise. - * If this succeeds the dmabuf can be used for rendering on a texture - */ -bool wlr_renderer_check_import_dmabuf(struct wlr_renderer *renderer, - struct wlr_dmabuf_attributes *attributes); /** * Reads out of pixels of the currently bound surface into data. `stride` is in * bytes. diff --git a/include/wlr/types/wlr_buffer.h b/include/wlr/types/wlr_buffer.h new file mode 100644 index 00000000..eabc8b51 --- /dev/null +++ b/include/wlr/types/wlr_buffer.h @@ -0,0 +1,63 @@ +#ifndef WLR_TYPES_WLR_BUFFER_H +#define WLR_TYPES_WLR_BUFFER_H + +#include +#include + +/** + * A client buffer. + */ +struct wlr_buffer { + /** + * The buffer resource, if any. Will be NULL if the client destroys it. + */ + struct wl_resource *resource; + /** + * The buffer's texture, if any. A buffer will not have a texture if the + * client destroys the buffer before it has been released. + */ + struct wlr_texture *texture; + bool released; + size_t n_refs; + + struct wl_listener resource_destroy; +}; + +struct wlr_renderer; + +/** + * Check if a resource is a wl_buffer resource. + */ +bool wlr_resource_is_buffer(struct wl_resource *resource); +/** + * Get the size of a wl_buffer resource. + */ +bool wlr_buffer_get_resource_size(struct wl_resource *resource, + struct wlr_renderer *renderer, int *width, int *height); + +/** + * Upload a buffer to the GPU and reference it. + */ +struct wlr_buffer *wlr_buffer_create(struct wlr_renderer *renderer, + struct wl_resource *resource); +/** + * Reference the buffer. + */ +struct wlr_buffer *wlr_buffer_ref(struct wlr_buffer *buffer); +/** + * Unreference the buffer. After this call, `buffer` may not be accessed + * anymore. + */ +void wlr_buffer_unref(struct wlr_buffer *buffer); +/** + * Try to update the buffer's content. On success, returns the updated buffer + * and destroys the provided `buffer`. On error, `buffer` is intact and NULL is + * returned. + * + * Fails if there's more than one reference to the buffer or if the texture + * isn't mutable. + */ +struct wlr_buffer *wlr_buffer_apply_damage(struct wlr_buffer *buffer, + struct wl_resource *resource, pixman_region32_t *damage); + +#endif diff --git a/include/wlr/types/wlr_data_device.h b/include/wlr/types/wlr_data_device.h index 80d4bc8b..4de4d610 100644 --- a/include/wlr/types/wlr_data_device.h +++ b/include/wlr/types/wlr_data_device.h @@ -88,12 +88,15 @@ struct wlr_drag_icon { int32_t sx, sy; struct { - struct wl_signal map; // emitted when mapped or unmapped + struct wl_signal map; + struct wl_signal unmap; struct wl_signal destroy; } events; struct wl_listener surface_destroy; struct wl_listener seat_client_destroy; + + void *data; }; struct wlr_drag { diff --git a/include/wlr/types/wlr_keyboard.h b/include/wlr/types/wlr_keyboard.h index 97288508..67d4e5be 100644 --- a/include/wlr/types/wlr_keyboard.h +++ b/include/wlr/types/wlr_keyboard.h @@ -89,7 +89,7 @@ enum wlr_key_state { struct wlr_event_keyboard_key { uint32_t time_msec; uint32_t keycode; - bool update_state; + bool update_state; // if backend doesn't update modifiers on its own enum wlr_key_state state; }; diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h index f4840c89..5e04003d 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -13,11 +13,11 @@ * managed by wlr_seat; some may be NULL. */ struct wlr_seat_client { - struct wl_resource *wl_resource; struct wl_client *client; struct wlr_seat *seat; // lists of wl_resource + struct wl_list wl_resources; struct wl_list pointers; struct wl_list keyboards; struct wl_list touches; diff --git a/include/wlr/types/wlr_surface.h b/include/wlr/types/wlr_surface.h index d1127c86..8517934a 100644 --- a/include/wlr/types/wlr_surface.h +++ b/include/wlr/types/wlr_surface.h @@ -62,12 +62,20 @@ struct wlr_subsurface { struct { struct wl_signal destroy; } events; + + void *data; }; struct wlr_surface { struct wl_resource *resource; struct wlr_renderer *renderer; - struct wlr_texture *texture; + /** + * The surface's buffer, if any. A surface has an attached buffer when it + * commits with a non-null buffer in its pending state. A surface will not + * have a buffer if it has never committed one, has committed a null buffer, + * or something went wrong with uploading the buffer. + */ + struct wlr_buffer *buffer; struct wlr_surface_state *current, *pending; const char *role; // the lifetime-bound role or null @@ -122,6 +130,13 @@ int wlr_surface_set_role(struct wlr_surface *surface, const char *role, */ bool wlr_surface_has_buffer(struct wlr_surface *surface); +/** + * Get the texture of the buffer currently attached to this surface. Returns + * NULL if no buffer is currently attached or if something went wrong with + * uploading the buffer. + */ +struct wlr_texture *wlr_surface_get_texture(struct wlr_surface *surface); + /** * Create a new subsurface resource with the provided new ID. If `resource_list` * is non-NULL, adds the subsurface's resource to the list. @@ -159,6 +174,14 @@ void wlr_surface_send_leave(struct wlr_surface *surface, void wlr_surface_send_frame_done(struct wlr_surface *surface, const struct timespec *when); +struct wlr_box; +/** + * Get the bounding box that contains the surface and all subsurfaces in + * surface coordinates. + * X and y may be negative, if there are subsurfaces with negative position. + */ +void wlr_surface_get_extends(struct wlr_surface *surface, struct wlr_box *box); + /** * Set a callback for surface commit that runs before all the other callbacks. * This is intended for use by the surface role. diff --git a/include/wlr/types/wlr_xdg_shell.h b/include/wlr/types/wlr_xdg_shell.h index 5eb30a16..01dc17fe 100644 --- a/include/wlr/types/wlr_xdg_shell.h +++ b/include/wlr/types/wlr_xdg_shell.h @@ -118,6 +118,7 @@ struct wlr_xdg_toplevel { struct wl_signal request_move; struct wl_signal request_resize; struct wl_signal request_show_window_menu; + struct wl_signal set_parent; } events; }; @@ -341,6 +342,15 @@ bool wlr_surface_is_xdg_surface(struct wlr_surface *surface); struct wlr_xdg_surface *wlr_xdg_surface_from_wlr_surface( struct wlr_surface *surface); +/** + * Get the surface geometry. + * This is either the geometry as set by the client, or defaulted to the bounds + * of the surface + the subsurfaces (as specified by the protocol). + * + * The x and y value can be <0 + */ +void wlr_xdg_surface_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 diff --git a/include/wlr/types/wlr_xdg_shell_v6.h b/include/wlr/types/wlr_xdg_shell_v6.h index 2fdf49e5..5f98eb13 100644 --- a/include/wlr/types/wlr_xdg_shell_v6.h +++ b/include/wlr/types/wlr_xdg_shell_v6.h @@ -126,6 +126,7 @@ struct wlr_xdg_toplevel_v6 { struct wl_signal request_move; struct wl_signal request_resize; struct wl_signal request_show_window_menu; + struct wl_signal set_parent; } events; }; @@ -318,6 +319,15 @@ bool wlr_surface_is_xdg_surface_v6(struct wlr_surface *surface); struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6_from_wlr_surface( struct wlr_surface *surface); +/** + * Get the surface geometry. + * This is either the geometry as set by the client, or defaulted to the bounds + * of the surface + the subsurfaces (as specified by the protocol). + * + * The x and y value can be <0 + */ +void wlr_xdg_surface_v6_get_geometry(struct wlr_xdg_surface_v6 *surface, struct wlr_box *box); + /** * Call `iterator` on each surface in the xdg-surface tree, with the surface's * position relative to the root xdg-surface. The function is called from root to diff --git a/render/egl.c b/render/egl.c index 93e4ec55..7f910e86 100644 --- a/render/egl.c +++ b/render/egl.c @@ -7,10 +7,6 @@ #include #include "glapi.h" -// Extension documentation -// https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_image_base.txt. -// https://cgit.freedesktop.org/mesa/mesa/tree/docs/specs/WL_bind_wayland_display.spec - static bool egl_get_config(EGLDisplay disp, EGLint *attribs, EGLConfig *out, EGLint visual_id) { EGLint count = 0, matched = 0, ret; @@ -61,27 +57,27 @@ static void egl_log(EGLenum error, const char *command, EGLint msg_type, _wlr_log(egl_log_importance_to_wlr(msg_type), "[EGL] %s: %s", command, msg); } -static bool check_egl_ext(const char *egl_exts, const char *ext) { +static bool check_egl_ext(const char *exts, const char *ext) { size_t extlen = strlen(ext); - const char *end = egl_exts + strlen(egl_exts); + const char *end = exts + strlen(exts); - while (egl_exts < end) { - if (*egl_exts == ' ') { - egl_exts++; + while (exts < end) { + if (*exts == ' ') { + exts++; continue; } - size_t n = strcspn(egl_exts, " "); - if (n == extlen && strncmp(ext, egl_exts, n) == 0) { + size_t n = strcspn(exts, " "); + if (n == extlen && strncmp(ext, exts, n) == 0) { return true; } - egl_exts += n; + exts += n; } return false; } static void print_dmabuf_formats(struct wlr_egl *egl) { /* Avoid log msg if extension is not present */ - if (!egl->egl_exts.dmabuf_import_modifiers) { + if (!egl->exts.image_dmabuf_import_modifiers_ext) { return; } @@ -144,48 +140,88 @@ bool wlr_egl_init(struct wlr_egl *egl, EGLenum platform, void *remote_display, goto error; } - static const EGLint attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; - - egl->context = eglCreateContext(egl->display, egl->config, - EGL_NO_CONTEXT, attribs); - - if (egl->context == EGL_NO_CONTEXT) { - wlr_log(L_ERROR, "Failed to create EGL context"); - goto error; - } - - eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl->context); egl->exts_str = eglQueryString(egl->display, EGL_EXTENSIONS); wlr_log(L_INFO, "Using EGL %d.%d", (int)major, (int)minor); wlr_log(L_INFO, "Supported EGL extensions: %s", egl->exts_str); wlr_log(L_INFO, "EGL vendor: %s", eglQueryString(egl->display, EGL_VENDOR)); - if (!check_egl_ext(egl->exts_str, "EGL_KHR_image_base")) { - wlr_log(L_ERROR, "Required EGL_KHR_image_base extension not supported"); - goto error; - } + egl->exts.image_base_khr = + check_egl_ext(egl->exts_str, "EGL_KHR_image_base") + && eglCreateImageKHR && eglDestroyImageKHR; - egl->egl_exts.buffer_age = + egl->exts.buffer_age_ext = check_egl_ext(egl->exts_str, "EGL_EXT_buffer_age"); - egl->egl_exts.swap_buffers_with_damage = - check_egl_ext(egl->exts_str, "EGL_EXT_swap_buffers_with_damage") || - check_egl_ext(egl->exts_str, "EGL_KHR_swap_buffers_with_damage"); - - egl->egl_exts.dmabuf_import = + egl->exts.swap_buffers_with_damage_ext = + (check_egl_ext(egl->exts_str, "EGL_EXT_swap_buffers_with_damage") && + eglSwapBuffersWithDamageEXT); + egl->exts.swap_buffers_with_damage_khr = + (check_egl_ext(egl->exts_str, "EGL_KHR_swap_buffers_with_damage") && + eglSwapBuffersWithDamageKHR); + + egl->exts.image_dmabuf_import_ext = check_egl_ext(egl->exts_str, "EGL_EXT_image_dma_buf_import"); - egl->egl_exts.dmabuf_import_modifiers = + egl->exts.image_dmabuf_import_modifiers_ext = check_egl_ext(egl->exts_str, "EGL_EXT_image_dma_buf_import_modifiers") && eglQueryDmaBufFormatsEXT && eglQueryDmaBufModifiersEXT; - egl->egl_exts.dmabuf_export = - check_egl_ext(egl->exts_str, "EGL_MESA_image_dma_buf_export"); - - egl->egl_exts.bind_wayland_display = - check_egl_ext(egl->exts_str, "EGL_WL_bind_wayland_display"); + egl->exts.image_dma_buf_export_mesa = + check_egl_ext(egl->exts_str, "EGL_MESA_image_dma_buf_export") && + eglExportDMABUFImageQueryMESA && eglExportDMABUFImageMESA; print_dmabuf_formats(egl); + egl->exts.bind_wayland_display_wl = + check_egl_ext(egl->exts_str, "EGL_WL_bind_wayland_display") + && eglBindWaylandDisplayWL && eglUnbindWaylandDisplayWL + && eglQueryWaylandBufferWL; + + bool ext_context_priority = + check_egl_ext(egl->exts_str, "EGL_IMG_context_priority"); + + size_t atti = 0; + EGLint attribs[5]; + attribs[atti++] = EGL_CONTEXT_CLIENT_VERSION; + attribs[atti++] = 2; + + // On DRM, request a high priority context if possible + bool request_high_priority = ext_context_priority && + platform == EGL_PLATFORM_GBM_MESA; + + // Try to reschedule all of our rendering to be completed first. If it + // fails, it will fallback to the default priority (MEDIUM). + if (request_high_priority) { + attribs[atti++] = EGL_CONTEXT_PRIORITY_LEVEL_IMG; + attribs[atti++] = EGL_CONTEXT_PRIORITY_HIGH_IMG; + } + + attribs[atti++] = EGL_NONE; + assert(atti <= sizeof(attribs)/sizeof(attribs[0])); + + egl->context = eglCreateContext(egl->display, egl->config, + EGL_NO_CONTEXT, attribs); + if (egl->context == EGL_NO_CONTEXT) { + wlr_log(L_ERROR, "Failed to create EGL context"); + goto error; + } + + if (request_high_priority) { + EGLint priority = EGL_CONTEXT_PRIORITY_MEDIUM_IMG; + eglQueryContext(egl->display, egl->context, + EGL_CONTEXT_PRIORITY_LEVEL_IMG, &priority); + if (priority != EGL_CONTEXT_PRIORITY_HIGH_IMG) { + wlr_log(L_INFO, "Failed to obtain a high priority context"); + } else { + wlr_log(L_DEBUG, "Obtained high priority context"); + } + } + + if (!eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, + egl->context)) { + wlr_log(L_ERROR, "Failed to make EGL context current"); + goto error; + } + return true; error: @@ -203,7 +239,8 @@ void wlr_egl_finish(struct wlr_egl *egl) { } eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - if (egl->wl_display && egl->egl_exts.bind_wayland_display) { + if (egl->wl_display) { + assert(egl->exts.bind_wayland_display_wl); eglUnbindWaylandDisplayWL(egl->display, egl->wl_display); } @@ -213,7 +250,7 @@ void wlr_egl_finish(struct wlr_egl *egl) { } bool wlr_egl_bind_display(struct wlr_egl *egl, struct wl_display *local_display) { - if (!egl->egl_exts.bind_wayland_display || !eglBindWaylandDisplayWL) { + if (!egl->exts.bind_wayland_display_wl) { return false; } @@ -226,7 +263,7 @@ bool wlr_egl_bind_display(struct wlr_egl *egl, struct wl_display *local_display) } bool wlr_egl_destroy_image(struct wlr_egl *egl, EGLImage image) { - if (!eglDestroyImageKHR) { + if (!egl->exts.image_base_khr) { return false; } if (!image) { @@ -247,7 +284,7 @@ EGLSurface wlr_egl_create_surface(struct wlr_egl *egl, void *window) { } static int egl_get_buffer_age(struct wlr_egl *egl, EGLSurface surface) { - if (!egl->egl_exts.buffer_age) { + if (!egl->exts.buffer_age_ext) { return -1; } @@ -282,7 +319,8 @@ bool wlr_egl_is_current(struct wlr_egl *egl) { bool wlr_egl_swap_buffers(struct wlr_egl *egl, EGLSurface surface, pixman_region32_t *damage) { EGLBoolean ret; - if (damage != NULL && egl->egl_exts.swap_buffers_with_damage) { + if (damage != NULL && (egl->exts.swap_buffers_with_damage_ext || + egl->exts.swap_buffers_with_damage_khr)) { int nrects; pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects); @@ -294,8 +332,7 @@ bool wlr_egl_swap_buffers(struct wlr_egl *egl, EGLSurface surface, egl_damage[4*i + 3] = rects[i].y2 - rects[i].y1; } - assert(eglSwapBuffersWithDamageEXT || eglSwapBuffersWithDamageKHR); - if (eglSwapBuffersWithDamageEXT) { + if (egl->exts.swap_buffers_with_damage_ext) { ret = eglSwapBuffersWithDamageEXT(egl->display, surface, egl_damage, nrects); } else { @@ -316,7 +353,7 @@ bool wlr_egl_swap_buffers(struct wlr_egl *egl, EGLSurface surface, EGLImageKHR wlr_egl_create_image_from_wl_drm(struct wlr_egl *egl, struct wl_resource *data, EGLint *fmt, int *width, int *height, bool *inverted_y) { - if (!eglQueryWaylandBufferWL || !eglCreateImageKHR) { + if (!egl->exts.bind_wayland_display_wl || !egl->exts.image_base_khr) { return NULL; } @@ -345,9 +382,13 @@ EGLImageKHR wlr_egl_create_image_from_wl_drm(struct wlr_egl *egl, EGLImageKHR wlr_egl_create_image_from_dmabuf(struct wlr_egl *egl, struct wlr_dmabuf_attributes *attributes) { + if (!egl->exts.image_base_khr) { + return NULL; + } + bool has_modifier = false; if (attributes->modifier != DRM_FORMAT_MOD_INVALID) { - if (!egl->egl_exts.dmabuf_import_modifiers) { + if (!egl->exts.image_dmabuf_import_modifiers_ext) { return NULL; } has_modifier = true; @@ -417,39 +458,10 @@ EGLImageKHR wlr_egl_create_image_from_dmabuf(struct wlr_egl *egl, EGL_LINUX_DMA_BUF_EXT, NULL, attribs); } -#ifndef DRM_FORMAT_BIG_ENDIAN -#define DRM_FORMAT_BIG_ENDIAN 0x80000000 -#endif -bool wlr_egl_check_import_dmabuf(struct wlr_egl *egl, - struct wlr_dmabuf_attributes *attribs) { - switch (attribs->format & ~DRM_FORMAT_BIG_ENDIAN) { - /* TODO: YUV based formats not yet supported, require multiple - * wlr_create_image_from_dmabuf */ - case WL_SHM_FORMAT_YUYV: - case WL_SHM_FORMAT_YVYU: - case WL_SHM_FORMAT_UYVY: - case WL_SHM_FORMAT_VYUY: - case WL_SHM_FORMAT_AYUV: - return false; - default: - break; - } - - EGLImage egl_image = wlr_egl_create_image_from_dmabuf(egl, attribs); - if (egl_image) { - /* We can import the image, good. No need to keep it - since wlr_texture_upload_dmabuf will import it again */ - wlr_egl_destroy_image(egl, egl_image); - return true; - } - /* TODO: import yuv dmabufs */ - return false; -} - int wlr_egl_get_dmabuf_formats(struct wlr_egl *egl, int **formats) { - if (!egl->egl_exts.dmabuf_import || - !egl->egl_exts.dmabuf_import_modifiers) { + if (!egl->exts.image_dmabuf_import_ext || + !egl->exts.image_dmabuf_import_modifiers_ext) { wlr_log(L_DEBUG, "dmabuf extension not present"); return -1; } @@ -476,8 +488,8 @@ int wlr_egl_get_dmabuf_formats(struct wlr_egl *egl, int wlr_egl_get_dmabuf_modifiers(struct wlr_egl *egl, int format, uint64_t **modifiers) { - if (!egl->egl_exts.dmabuf_import || - !egl->egl_exts.dmabuf_import_modifiers) { + if (!egl->exts.image_dmabuf_import_ext || + !egl->exts.image_dmabuf_import_modifiers_ext) { wlr_log(L_DEBUG, "dmabuf extension not present"); return -1; } @@ -509,8 +521,7 @@ bool wlr_egl_export_image_to_dmabuf(struct wlr_egl *egl, EGLImageKHR image, struct wlr_dmabuf_attributes *attribs) { memset(attribs, 0, sizeof(struct wlr_dmabuf_attributes)); - if (!egl->egl_exts.dmabuf_export || !eglExportDMABUFImageQueryMESA || - !eglExportDMABUFImageMESA) { + if (!egl->exts.image_dma_buf_export_mesa) { return false; } diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c index 00a5c285..05426fa9 100644 --- a/render/gles2/renderer.c +++ b/render/gles2/renderer.c @@ -118,18 +118,22 @@ static bool gles2_render_texture_with_matrix(struct wlr_renderer *wlr_renderer, struct wlr_gles2_texture *texture = get_gles2_texture_in_context(wlr_texture); - GLuint prog = 0; + struct wlr_gles2_tex_shader *shader = NULL; GLenum target = 0; + switch (texture->type) { case WLR_GLES2_TEXTURE_GLTEX: case WLR_GLES2_TEXTURE_WL_DRM_GL: - prog = texture->has_alpha ? renderer->shaders.tex_rgba : - renderer->shaders.tex_rgbx; + if (texture->has_alpha) { + shader = &renderer->shaders.tex_rgba; + } else { + shader = &renderer->shaders.tex_rgbx; + } target = GL_TEXTURE_2D; break; case WLR_GLES2_TEXTURE_WL_DRM_EXT: case WLR_GLES2_TEXTURE_DMABUF: - prog = renderer->shaders.tex_ext; + shader = &renderer->shaders.tex_ext; target = GL_TEXTURE_EXTERNAL_OES; break; } @@ -149,11 +153,12 @@ static bool gles2_render_texture_with_matrix(struct wlr_renderer *wlr_renderer, glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glUseProgram(prog); + glUseProgram(shader->program); - glUniformMatrix3fv(0, 1, GL_FALSE, transposition); - glUniform1i(1, texture->inverted_y); - glUniform1f(3, alpha); + glUniformMatrix3fv(shader->proj, 1, GL_FALSE, transposition); + glUniform1i(shader->invert_y, texture->inverted_y); + glUniform1i(shader->tex, 0); + glUniform1f(shader->alpha, alpha); draw_quad(); @@ -173,9 +178,10 @@ static void gles2_render_quad_with_matrix(struct wlr_renderer *wlr_renderer, wlr_matrix_transpose(transposition, matrix); PUSH_GLES2_DEBUG; - glUseProgram(renderer->shaders.quad); - glUniformMatrix3fv(0, 1, GL_FALSE, transposition); - glUniform4f(1, color[0], color[1], color[2], color[3]); + glUseProgram(renderer->shaders.quad.program); + + glUniformMatrix3fv(renderer->shaders.quad.proj, 1, GL_FALSE, transposition); + glUniform4f(renderer->shaders.quad.color, color[0], color[1], color[2], color[3]); draw_quad(); POP_GLES2_DEBUG; } @@ -191,9 +197,10 @@ static void gles2_render_ellipse_with_matrix(struct wlr_renderer *wlr_renderer, wlr_matrix_transpose(transposition, matrix); PUSH_GLES2_DEBUG; - glUseProgram(renderer->shaders.ellipse); - glUniformMatrix3fv(0, 1, GL_FALSE, transposition); - glUniform4f(1, color[0], color[1], color[2], color[3]); + glUseProgram(renderer->shaders.ellipse.program); + + glUniformMatrix3fv(renderer->shaders.ellipse.proj, 1, GL_FALSE, transposition); + glUniform4f(renderer->shaders.ellipse.color, color[0], color[1], color[2], color[3]); draw_quad(); POP_GLES2_DEBUG; } @@ -242,12 +249,6 @@ static int gles2_get_dmabuf_modifiers(struct wlr_renderer *wlr_renderer, return wlr_egl_get_dmabuf_modifiers(renderer->egl, format, modifiers); } -static bool gles2_check_import_dmabuf(struct wlr_renderer *wlr_renderer, - struct wlr_dmabuf_attributes *attribs) { - struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); - return wlr_egl_check_import_dmabuf(renderer->egl, attribs); -} - 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, @@ -319,11 +320,11 @@ static void gles2_destroy(struct wlr_renderer *wlr_renderer) { wlr_egl_make_current(renderer->egl, EGL_NO_SURFACE, NULL); PUSH_GLES2_DEBUG; - glDeleteProgram(renderer->shaders.quad); - glDeleteProgram(renderer->shaders.ellipse); - glDeleteProgram(renderer->shaders.tex_rgba); - glDeleteProgram(renderer->shaders.tex_rgbx); - glDeleteProgram(renderer->shaders.tex_ext); + glDeleteProgram(renderer->shaders.quad.program); + glDeleteProgram(renderer->shaders.ellipse.program); + glDeleteProgram(renderer->shaders.tex_rgba.program); + glDeleteProgram(renderer->shaders.tex_rgbx.program); + glDeleteProgram(renderer->shaders.tex_ext.program); POP_GLES2_DEBUG; if (glDebugMessageCallbackKHR) { @@ -348,7 +349,6 @@ static const struct wlr_renderer_impl renderer_impl = { .wl_drm_buffer_get_size = gles2_wl_drm_buffer_get_size, .get_dmabuf_formats = gles2_get_dmabuf_formats, .get_dmabuf_modifiers = gles2_get_dmabuf_modifiers, - .check_import_dmabuf = gles2_check_import_dmabuf, .read_pixels = gles2_read_pixels, .format_supported = gles2_format_supported, .texture_from_pixels = gles2_texture_from_pixels, @@ -493,31 +493,53 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { PUSH_GLES2_DEBUG; - renderer->shaders.quad = link_program(quad_vertex_src, quad_fragment_src); - if (!renderer->shaders.quad) { + GLuint prog; + renderer->shaders.quad.program = prog = + link_program(quad_vertex_src, quad_fragment_src); + if (!renderer->shaders.quad.program) { goto error; } - renderer->shaders.ellipse = + renderer->shaders.quad.proj = glGetUniformLocation(prog, "proj"); + renderer->shaders.quad.color = glGetUniformLocation(prog, "color"); + + renderer->shaders.ellipse.program = prog = link_program(quad_vertex_src, ellipse_fragment_src); - if (!renderer->shaders.ellipse) { + if (!renderer->shaders.ellipse.program) { goto error; } - renderer->shaders.tex_rgba = + renderer->shaders.ellipse.proj = glGetUniformLocation(prog, "proj"); + renderer->shaders.ellipse.color = glGetUniformLocation(prog, "color"); + + renderer->shaders.tex_rgba.program = prog = link_program(tex_vertex_src, tex_fragment_src_rgba); - if (!renderer->shaders.tex_rgba) { + if (!renderer->shaders.tex_rgba.program) { goto error; } - renderer->shaders.tex_rgbx = + renderer->shaders.tex_rgba.proj = glGetUniformLocation(prog, "proj"); + renderer->shaders.tex_rgba.invert_y = glGetUniformLocation(prog, "invert_y"); + renderer->shaders.tex_rgba.tex = glGetUniformLocation(prog, "tex"); + renderer->shaders.tex_rgba.alpha = glGetUniformLocation(prog, "alpha"); + + renderer->shaders.tex_rgbx.program = prog = link_program(tex_vertex_src, tex_fragment_src_rgbx); - if (!renderer->shaders.tex_rgbx) { + if (!renderer->shaders.tex_rgbx.program) { goto error; } + renderer->shaders.tex_rgbx.proj = glGetUniformLocation(prog, "proj"); + renderer->shaders.tex_rgbx.invert_y = glGetUniformLocation(prog, "invert_y"); + renderer->shaders.tex_rgbx.tex = glGetUniformLocation(prog, "tex"); + renderer->shaders.tex_rgbx.alpha = glGetUniformLocation(prog, "alpha"); + if (glEGLImageTargetTexture2DOES) { - renderer->shaders.tex_ext = + renderer->shaders.tex_ext.program = prog = link_program(tex_vertex_src, tex_fragment_src_external); - if (!renderer->shaders.tex_ext) { + if (!renderer->shaders.tex_ext.program) { goto error; } + renderer->shaders.tex_ext.proj = glGetUniformLocation(prog, "proj"); + renderer->shaders.tex_ext.invert_y = glGetUniformLocation(prog, "invert_y"); + renderer->shaders.tex_ext.tex = glGetUniformLocation(prog, "tex"); + renderer->shaders.tex_ext.alpha = glGetUniformLocation(prog, "alpha"); } POP_GLES2_DEBUG; @@ -525,11 +547,11 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) { return &renderer->wlr_renderer; error: - glDeleteProgram(renderer->shaders.quad); - glDeleteProgram(renderer->shaders.ellipse); - glDeleteProgram(renderer->shaders.tex_rgba); - glDeleteProgram(renderer->shaders.tex_rgbx); - glDeleteProgram(renderer->shaders.tex_ext); + glDeleteProgram(renderer->shaders.quad.program); + glDeleteProgram(renderer->shaders.ellipse.program); + glDeleteProgram(renderer->shaders.tex_rgba.program); + glDeleteProgram(renderer->shaders.tex_rgbx.program); + glDeleteProgram(renderer->shaders.tex_ext.program); POP_GLES2_DEBUG; diff --git a/render/gles2/texture.c b/render/gles2/texture.c index 0f061f59..0ef9d759 100644 --- a/render/gles2/texture.c +++ b/render/gles2/texture.c @@ -227,6 +227,10 @@ struct wlr_texture *wlr_gles2_texture_from_wl_drm(struct wlr_egl *egl, return &texture->wlr_texture; } +#ifndef DRM_FORMAT_BIG_ENDIAN +#define DRM_FORMAT_BIG_ENDIAN 0x80000000 +#endif + struct wlr_texture *wlr_gles2_texture_from_dmabuf(struct wlr_egl *egl, struct wlr_dmabuf_attributes *attribs) { assert(wlr_egl_is_current(egl)); @@ -235,12 +239,24 @@ struct wlr_texture *wlr_gles2_texture_from_dmabuf(struct wlr_egl *egl, return NULL; } - if (!egl->egl_exts.dmabuf_import) { + if (!egl->exts.image_dmabuf_import_ext) { wlr_log(L_ERROR, "Cannot create DMA-BUF texture: EGL extension " "unavailable"); return NULL; } + switch (attribs->format & ~DRM_FORMAT_BIG_ENDIAN) { + case WL_SHM_FORMAT_YUYV: + case WL_SHM_FORMAT_YVYU: + case WL_SHM_FORMAT_UYVY: + case WL_SHM_FORMAT_VYUY: + case WL_SHM_FORMAT_AYUV: + // TODO: YUV based formats not yet supported, require multiple images + return false; + default: + break; + } + struct wlr_gles2_texture *texture = calloc(1, sizeof(struct wlr_gles2_texture)); if (texture == NULL) { diff --git a/render/wlr_renderer.c b/render/wlr_renderer.c index 32b0a779..98c91132 100644 --- a/render/wlr_renderer.c +++ b/render/wlr_renderer.c @@ -135,14 +135,6 @@ int wlr_renderer_get_dmabuf_modifiers(struct wlr_renderer *r, int format, return r->impl->get_dmabuf_modifiers(r, format, modifiers); } -bool wlr_renderer_check_import_dmabuf(struct wlr_renderer *r, - struct wlr_dmabuf_attributes *attribs) { - if (!r->impl->check_import_dmabuf) { - return false; - } - return r->impl->check_import_dmabuf(r, attribs); -} - 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 src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y, @@ -187,8 +179,8 @@ void wlr_renderer_init_wl_display(struct wlr_renderer *r, } struct wlr_renderer *wlr_renderer_autocreate(struct wlr_egl *egl, - EGLenum platform, void *remote_display, EGLint *config_attribs, EGLint visual_id) { - + EGLenum platform, void *remote_display, EGLint *config_attribs, + EGLint visual_id) { if (!wlr_egl_init(egl, platform, remote_display, config_attribs, visual_id)) { wlr_log(L_ERROR, "Could not initialize EGL"); return NULL; diff --git a/rootston/main.c b/rootston/main.c index 07a41d5d..8a0205f2 100644 --- a/rootston/main.c +++ b/rootston/main.c @@ -72,6 +72,9 @@ int main(int argc, char **argv) { } wl_display_run(server.wl_display); +#ifdef WLR_HAS_XWAYLAND + wlr_xwayland_destroy(server.desktop->xwayland); +#endif wl_display_destroy_clients(server.wl_display); wl_display_destroy(server.wl_display); return 0; diff --git a/rootston/output.c b/rootston/output.c index faa808d1..353d431f 100644 --- a/rootston/output.c +++ b/rootston/output.c @@ -189,7 +189,8 @@ static void render_surface(struct wlr_surface *surface, int sx, int sy, struct roots_output *output = data->output; float rotation = data->layout.rotation; - if (!wlr_surface_has_buffer(surface)) { + struct wlr_texture *texture = wlr_surface_get_texture(surface); + if (texture == NULL) { return; } @@ -230,8 +231,7 @@ static void render_surface(struct wlr_surface *surface, int sx, int sy, pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects); for (int i = 0; i < nrects; ++i) { scissor_output(output, &rects[i]); - wlr_render_texture_with_matrix(renderer, surface->texture, matrix, - data->alpha); + wlr_render_texture_with_matrix(renderer, texture, matrix, data->alpha); } damage_finish: diff --git a/rootston/seat.c b/rootston/seat.c index b137ff11..91561567 100644 --- a/rootston/seat.c +++ b/rootston/seat.c @@ -260,7 +260,7 @@ static void roots_drag_icon_handle_surface_commit(struct wl_listener *listener, void *data) { struct roots_drag_icon *icon = wl_container_of(listener, icon, surface_commit); - roots_drag_icon_damage_whole(icon); + roots_drag_icon_update_position(icon); } static void roots_drag_icon_handle_map(struct wl_listener *listener, @@ -270,6 +270,13 @@ static void roots_drag_icon_handle_map(struct wl_listener *listener, roots_drag_icon_damage_whole(icon); } +static void roots_drag_icon_handle_unmap(struct wl_listener *listener, + void *data) { + struct roots_drag_icon *icon = + wl_container_of(listener, icon, unmap); + roots_drag_icon_damage_whole(icon); +} + static void roots_drag_icon_handle_destroy(struct wl_listener *listener, void *data) { struct roots_drag_icon *icon = @@ -278,7 +285,7 @@ static void roots_drag_icon_handle_destroy(struct wl_listener *listener, wl_list_remove(&icon->link); wl_list_remove(&icon->surface_commit.link); - wl_list_remove(&icon->map.link); + wl_list_remove(&icon->unmap.link); wl_list_remove(&icon->destroy.link); free(icon); } @@ -297,12 +304,16 @@ static void roots_seat_handle_new_drag_icon(struct wl_listener *listener, icon->surface_commit.notify = roots_drag_icon_handle_surface_commit; wl_signal_add(&wlr_drag_icon->surface->events.commit, &icon->surface_commit); + icon->unmap.notify = roots_drag_icon_handle_unmap; + wl_signal_add(&wlr_drag_icon->events.unmap, &icon->unmap); icon->map.notify = roots_drag_icon_handle_map; wl_signal_add(&wlr_drag_icon->events.map, &icon->map); icon->destroy.notify = roots_drag_icon_handle_destroy; wl_signal_add(&wlr_drag_icon->events.destroy, &icon->destroy); wl_list_insert(&seat->drag_icons, &icon->link); + + roots_drag_icon_update_position(icon); } void roots_drag_icon_update_position(struct roots_drag_icon *icon) { diff --git a/rootston/xdg_shell.c b/rootston/xdg_shell.c index 03ae1dc6..805fb874 100644 --- a/rootston/xdg_shell.c +++ b/rootston/xdg_shell.c @@ -132,14 +132,10 @@ static void get_size(const struct roots_view *view, struct wlr_box *box) { assert(view->type == ROOTS_XDG_SHELL_VIEW); struct wlr_xdg_surface *surface = view->xdg_surface; - if (surface->geometry.width > 0 && surface->geometry.height > 0) { - box->width = surface->geometry.width; - box->height = surface->geometry.height; - } else { - assert(surface->surface); - box->width = surface->surface->current->width; - box->height = surface->surface->current->height; - } + struct wlr_box geo_box; + wlr_xdg_surface_get_geometry(surface, &geo_box); + box->width = geo_box.width; + box->height = geo_box.height; } static void activate(struct roots_view *view, bool active) { diff --git a/rootston/xdg_shell_v6.c b/rootston/xdg_shell_v6.c index 90b11690..02ad867d 100644 --- a/rootston/xdg_shell_v6.c +++ b/rootston/xdg_shell_v6.c @@ -133,14 +133,10 @@ static void get_size(const struct roots_view *view, struct wlr_box *box) { assert(view->type == ROOTS_XDG_SHELL_V6_VIEW); struct wlr_xdg_surface_v6 *surface = view->xdg_surface_v6; - if (surface->geometry.width > 0 && surface->geometry.height > 0) { - box->width = surface->geometry.width; - box->height = surface->geometry.height; - } else { - assert(surface->surface); - box->width = surface->surface->current->width; - box->height = surface->surface->current->height; - } + struct wlr_box geo_box; + wlr_xdg_surface_v6_get_geometry(surface, &geo_box); + box->width = geo_box.width; + box->height = geo_box.height; } static void activate(struct roots_view *view, bool active) { diff --git a/types/data_device/wlr_drag.c b/types/data_device/wlr_drag.c index 331fba2f..4f0b2521 100644 --- a/types/data_device/wlr_drag.c +++ b/types/data_device/wlr_drag.c @@ -44,7 +44,7 @@ static void drag_set_focus(struct wlr_drag *drag, if (!drag->source && wl_resource_get_client(surface->resource) != - wl_resource_get_client(drag->seat_client->wl_resource)) { + drag->seat_client->client) { return; } @@ -98,6 +98,16 @@ static void drag_set_focus(struct wlr_drag *drag, wlr_signal_emit_safe(&drag->events.focus, drag); } +static void drag_icon_set_mapped(struct wlr_drag_icon *icon, bool mapped) { + if (mapped && !icon->mapped) { + icon->mapped = true; + wlr_signal_emit_safe(&icon->events.map, icon); + } else if (!mapped && icon->mapped) { + icon->mapped = false; + wlr_signal_emit_safe(&icon->events.unmap, icon); + } +} + static void drag_end(struct wlr_drag *drag) { if (!drag->cancelling) { drag->cancelling = true; @@ -115,9 +125,8 @@ static void drag_end(struct wlr_drag *drag) { drag_set_focus(drag, NULL, 0, 0); if (drag->icon) { - drag->icon->mapped = false; wl_list_remove(&drag->icon_destroy.link); - wlr_signal_emit_safe(&drag->icon->events.map, drag->icon); + drag_icon_set_mapped(drag->icon, false); } wlr_signal_emit_safe(&drag->events.destroy, drag); @@ -310,9 +319,10 @@ static void drag_handle_drag_source_destroy(struct wl_listener *listener, static void drag_icon_destroy(struct wlr_drag_icon *icon) { - if (!icon) { + if (icon == NULL) { return; } + drag_icon_set_mapped(icon, false); wlr_signal_emit_safe(&icon->events.destroy, icon); wlr_surface_set_role_committed(icon->surface, NULL, NULL); wl_list_remove(&icon->surface_destroy.link); @@ -333,6 +343,8 @@ static void drag_icon_handle_surface_commit(struct wlr_surface *surface, struct wlr_drag_icon *icon = role_data; icon->sx += icon->surface->current->sx; icon->sy += icon->surface->current->sy; + + drag_icon_set_mapped(icon, wlr_surface_has_buffer(surface)); } static void drag_icon_handle_seat_client_destroy(struct wl_listener *listener, @@ -355,9 +367,9 @@ static struct wlr_drag_icon *drag_icon_create( icon->client = client; icon->is_pointer = is_pointer; icon->touch_id = touch_id; - icon->mapped = true; wl_signal_init(&icon->events.map); + wl_signal_init(&icon->events.unmap); wl_signal_init(&icon->events.destroy); wl_signal_add(&icon->surface->events.destroy, &icon->surface_destroy); @@ -372,6 +384,10 @@ static struct wlr_drag_icon *drag_icon_create( wl_list_insert(&client->seat->drag_icons, &icon->link); wlr_signal_emit_safe(&client->seat->events.new_drag_icon, icon); + if (wlr_surface_has_buffer(icon_surface)) { + drag_icon_set_mapped(icon, true); + } + return icon; } diff --git a/types/meson.build b/types/meson.build index 0842f98c..6d6e0fa3 100644 --- a/types/meson.build +++ b/types/meson.build @@ -20,6 +20,7 @@ lib_wlr_types = static_library( 'xdg_shell_v6/wlr_xdg_surface_v6.c', 'xdg_shell_v6/wlr_xdg_toplevel_v6.c', 'wlr_box.c', + 'wlr_buffer.c', 'wlr_compositor.c', 'wlr_cursor.c', 'wlr_export_dmabuf_v1.c', diff --git a/types/seat/wlr_seat.c b/types/seat/wlr_seat.c index 4a680157..ead4b0e7 100644 --- a/types/seat/wlr_seat.c +++ b/types/seat/wlr_seat.c @@ -60,6 +60,12 @@ static void seat_client_handle_resource_destroy( struct wl_resource *seat_resource) { struct wlr_seat_client *client = wlr_seat_client_from_resource(seat_resource); + + wl_list_remove(wl_resource_get_link(seat_resource)); + if (!wl_list_empty(&client->wl_resources)) { + return; + } + wlr_signal_emit_safe(&client->events.destroy, client); if (client == client->seat->pointer_state.focused_client) { @@ -108,34 +114,43 @@ static void seat_handle_bind(struct wl_client *client, void *_wlr_seat, struct wlr_seat *wlr_seat = _wlr_seat; assert(client && wlr_seat); - struct wlr_seat_client *seat_client = - calloc(1, sizeof(struct wlr_seat_client)); - if (seat_client == NULL) { - wl_client_post_no_memory(client); - return; - } - seat_client->wl_resource = + struct wl_resource *wl_resource = wl_resource_create(client, &wl_seat_interface, version, id); - if (seat_client->wl_resource == NULL) { - free(seat_client); + if (wl_resource == NULL) { wl_client_post_no_memory(client); return; } - seat_client->client = client; - seat_client->seat = wlr_seat; - wl_list_init(&seat_client->pointers); - wl_list_init(&seat_client->keyboards); - wl_list_init(&seat_client->touches); - wl_list_init(&seat_client->data_devices); - wl_list_init(&seat_client->primary_selection_devices); - wl_resource_set_implementation(seat_client->wl_resource, &seat_impl, + + struct wlr_seat_client *seat_client = + wlr_seat_client_for_wl_client(wlr_seat, client); + if (seat_client == NULL) { + seat_client = calloc(1, sizeof(struct wlr_seat_client)); + if (seat_client == NULL) { + wl_resource_destroy(wl_resource); + wl_client_post_no_memory(client); + return; + } + + seat_client->client = client; + seat_client->seat = wlr_seat; + wl_list_init(&seat_client->wl_resources); + wl_list_init(&seat_client->pointers); + wl_list_init(&seat_client->keyboards); + wl_list_init(&seat_client->touches); + wl_list_init(&seat_client->data_devices); + wl_list_init(&seat_client->primary_selection_devices); + wl_signal_init(&seat_client->events.destroy); + + wl_list_insert(&wlr_seat->clients, &seat_client->link); + } + + wl_resource_set_implementation(wl_resource, &seat_impl, seat_client, seat_client_handle_resource_destroy); - wl_list_insert(&wlr_seat->clients, &seat_client->link); + wl_list_insert(&seat_client->wl_resources, wl_resource_get_link(wl_resource)); if (version >= WL_SEAT_NAME_SINCE_VERSION) { - wl_seat_send_name(seat_client->wl_resource, wlr_seat->name); + wl_seat_send_name(wl_resource, wlr_seat->name); } - wl_seat_send_capabilities(seat_client->wl_resource, wlr_seat->capabilities); - wl_signal_init(&seat_client->events.destroy); + wl_seat_send_capabilities(wl_resource, wlr_seat->capabilities); } void wlr_seat_destroy(struct wlr_seat *seat) { @@ -160,8 +175,11 @@ void wlr_seat_destroy(struct wlr_seat *seat) { struct wlr_seat_client *client, *tmp; wl_list_for_each_safe(client, tmp, &seat->clients, link) { - // will destroy other resources as well - wl_resource_destroy(client->wl_resource); + struct wl_resource *resource, *next_resource; + wl_resource_for_each_safe(resource, next_resource, &client->wl_resources) { + // will destroy other resources as well + wl_resource_destroy(resource); + } } wl_global_destroy(seat->wl_global); @@ -308,7 +326,10 @@ void wlr_seat_set_capabilities(struct wlr_seat *wlr_seat, } } - wl_seat_send_capabilities(client->wl_resource, capabilities); + struct wl_resource *resource; + wl_resource_for_each(resource, &client->wl_resources) { + wl_seat_send_capabilities(resource, capabilities); + } } } @@ -317,7 +338,10 @@ void wlr_seat_set_name(struct wlr_seat *wlr_seat, const char *name) { wlr_seat->name = strdup(name); struct wlr_seat_client *client; wl_list_for_each(client, &wlr_seat->clients, link) { - wl_seat_send_name(client->wl_resource, name); + struct wl_resource *resource; + wl_resource_for_each(resource, &client->wl_resources) { + wl_seat_send_name(resource, name); + } } } diff --git a/types/wlr_box.c b/types/wlr_box.c index c92b0aa4..ada6a733 100644 --- a/types/wlr_box.c +++ b/types/wlr_box.c @@ -11,8 +11,8 @@ void wlr_box_closest_point(const struct wlr_box *box, double x, double y, // find the closest x point if (x < box->x) { *dest_x = box->x; - } else if (x > box->x + box->width) { - *dest_x = box->x + box->width; + } else if (x >= box->x + box->width) { + *dest_x = box->x + box->width - 1; } else { *dest_x = x; } @@ -20,8 +20,8 @@ void wlr_box_closest_point(const struct wlr_box *box, double x, double y, // find closest y point if (y < box->y) { *dest_y = box->y; - } else if (y > box->y + box->height) { - *dest_y = box->y + box->height; + } else if (y >= box->y + box->height) { + *dest_y = box->y + box->height - 1; } else { *dest_y = y; } diff --git a/types/wlr_buffer.c b/types/wlr_buffer.c new file mode 100644 index 00000000..673d7088 --- /dev/null +++ b/types/wlr_buffer.c @@ -0,0 +1,193 @@ +#include +#include +#include +#include +#include +#include + +bool wlr_resource_is_buffer(struct wl_resource *resource) { + return strcmp(wl_resource_get_class(resource), wl_buffer_interface.name) == 0; +} + +bool wlr_buffer_get_resource_size(struct wl_resource *resource, + struct wlr_renderer *renderer, int *width, int *height) { + assert(wlr_resource_is_buffer(resource)); + + struct wl_shm_buffer *shm_buf = wl_shm_buffer_get(resource); + if (shm_buf != NULL) { + *width = wl_shm_buffer_get_width(shm_buf); + *height = wl_shm_buffer_get_height(shm_buf); + } else if (wlr_renderer_resource_is_wl_drm_buffer(renderer, + resource)) { + wlr_renderer_wl_drm_buffer_get_size(renderer, resource, + width, height); + } else if (wlr_dmabuf_resource_is_buffer(resource)) { + struct wlr_dmabuf_buffer *dmabuf = + wlr_dmabuf_buffer_from_buffer_resource(resource); + *width = dmabuf->attributes.width; + *height = dmabuf->attributes.height; + } else { + *width = *height = 0; + return false; + } + + return true; +} + + +static void buffer_resource_handle_destroy(struct wl_listener *listener, + void *data) { + struct wlr_buffer *buffer = + wl_container_of(listener, buffer, resource_destroy); + wl_list_remove(&buffer->resource_destroy.link); + wl_list_init(&buffer->resource_destroy.link); + buffer->resource = NULL; + + // At this point, if the wl_buffer comes from linux-dmabuf or wl_drm, we + // still haven't released it (ie. we'll read it in the future) but the + // client destroyed it. Reading the texture itself should be fine because + // we still hold a reference to the DMA-BUF via the texture. However the + // client could decide to re-use the same DMA-BUF for something else, in + // which case we'll read garbage. We decide to accept this risk. +} + +struct wlr_buffer *wlr_buffer_create(struct wlr_renderer *renderer, + struct wl_resource *resource) { + assert(wlr_resource_is_buffer(resource)); + + struct wlr_texture *texture = NULL; + bool released = false; + + struct wl_shm_buffer *shm_buf = wl_shm_buffer_get(resource); + if (shm_buf != NULL) { + enum wl_shm_format fmt = wl_shm_buffer_get_format(shm_buf); + int32_t stride = wl_shm_buffer_get_stride(shm_buf); + int32_t width = wl_shm_buffer_get_width(shm_buf); + int32_t height = wl_shm_buffer_get_height(shm_buf); + + wl_shm_buffer_begin_access(shm_buf); + void *data = wl_shm_buffer_get_data(shm_buf); + texture = wlr_texture_from_pixels(renderer, fmt, stride, + width, height, data); + wl_shm_buffer_end_access(shm_buf); + + // We have uploaded the data, we don't need to access the wl_buffer + // anymore + wl_buffer_send_release(resource); + released = true; + } else if (wlr_renderer_resource_is_wl_drm_buffer(renderer, resource)) { + texture = wlr_texture_from_wl_drm(renderer, resource); + } else if (wlr_dmabuf_resource_is_buffer(resource)) { + struct wlr_dmabuf_buffer *dmabuf = + wlr_dmabuf_buffer_from_buffer_resource(resource); + texture = wlr_texture_from_dmabuf(renderer, &dmabuf->attributes); + + // We have imported the DMA-BUF, but we need to prevent the client from + // re-using the same DMA-BUF for the next frames, so we don't release + // the buffer yet. + } else { + wlr_log(L_ERROR, "Cannot upload texture: unknown buffer type"); + return NULL; + } + + if (texture == NULL) { + wlr_log(L_ERROR, "Failed to upload texture"); + return NULL; + } + + struct wlr_buffer *buffer = calloc(1, sizeof(struct wlr_buffer)); + if (buffer == NULL) { + wlr_texture_destroy(texture); + return NULL; + } + buffer->resource = resource; + buffer->texture = texture; + buffer->released = released; + buffer->n_refs = 1; + + wl_resource_add_destroy_listener(resource, &buffer->resource_destroy); + buffer->resource_destroy.notify = buffer_resource_handle_destroy; + + return buffer; +} + +struct wlr_buffer *wlr_buffer_ref(struct wlr_buffer *buffer) { + buffer->n_refs++; + return buffer; +} + +void wlr_buffer_unref(struct wlr_buffer *buffer) { + if (buffer == NULL) { + return; + } + + assert(buffer->n_refs > 0); + buffer->n_refs--; + if (buffer->n_refs > 0) { + return; + } + + if (!buffer->released && buffer->resource != NULL) { + wl_buffer_send_release(buffer->resource); + } + + wl_list_remove(&buffer->resource_destroy.link); + wlr_texture_destroy(buffer->texture); + free(buffer); +} + +struct wlr_buffer *wlr_buffer_apply_damage(struct wlr_buffer *buffer, + struct wl_resource *resource, pixman_region32_t *damage) { + assert(wlr_resource_is_buffer(resource)); + + if (buffer->n_refs > 1) { + // Someone else still has a reference to the buffer + return NULL; + } + + struct wl_shm_buffer *shm_buf = wl_shm_buffer_get(resource); + if (shm_buf == NULL) { + // Uploading only damaged regions only works for wl_shm buffers + return NULL; + } + + enum wl_shm_format fmt = wl_shm_buffer_get_format(shm_buf); + int32_t stride = wl_shm_buffer_get_stride(shm_buf); + int32_t width = wl_shm_buffer_get_width(shm_buf); + int32_t height = wl_shm_buffer_get_height(shm_buf); + + int32_t texture_width, texture_height; + wlr_texture_get_size(buffer->texture, &texture_width, &texture_height); + if (width != texture_width || height != texture_height) { + return NULL; + } + + wl_shm_buffer_begin_access(shm_buf); + void *data = wl_shm_buffer_get_data(shm_buf); + + int n; + pixman_box32_t *rects = pixman_region32_rectangles(damage, &n); + for (int i = 0; i < n; ++i) { + pixman_box32_t *r = &rects[i]; + if (!wlr_texture_write_pixels(buffer->texture, fmt, stride, + r->x2 - r->x1, r->y2 - r->y1, r->x1, r->y1, + r->x1, r->y1, data)) { + wl_shm_buffer_end_access(shm_buf); + return NULL; + } + } + + wl_shm_buffer_end_access(shm_buf); + + // We have uploaded the data, we don't need to access the wl_buffer + // anymore + wl_buffer_send_release(resource); + + wl_list_remove(&buffer->resource_destroy.link); + wl_resource_add_destroy_listener(resource, &buffer->resource_destroy); + buffer->resource_destroy.notify = buffer_resource_handle_destroy; + + buffer->resource = resource; + buffer->released = true; + return buffer; +} diff --git a/types/wlr_layer_shell.c b/types/wlr_layer_shell.c index e64d7937..76d6d721 100644 --- a/types/wlr_layer_shell.c +++ b/types/wlr_layer_shell.c @@ -176,7 +176,9 @@ static void layer_surface_unmap(struct wlr_layer_surface *surface) { } static void layer_surface_destroy(struct wlr_layer_surface *surface) { - layer_surface_unmap(surface); + if (surface->configured && surface->mapped) { + layer_surface_unmap(surface); + } wlr_signal_emit_safe(&surface->events.destroy, surface); wl_resource_set_user_data(surface->resource, NULL); wl_list_remove(&surface->surface_destroy_listener.link); diff --git a/types/wlr_linux_dmabuf.c b/types/wlr_linux_dmabuf.c index fb20ad74..08f44243 100644 --- a/types/wlr_linux_dmabuf.c +++ b/types/wlr_linux_dmabuf.c @@ -97,7 +97,8 @@ static void params_add(struct wl_client *client, if (buffer->has_modifier && modifier != buffer->attributes.modifier) { wl_resource_post_error(params_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT, - "sent modifier %lu for plane %u, expected modifier %lu like other planes", + "sent modifier %" PRIu64 " for plane %u, expected" + " modifier %" PRIu64 " like other planes", modifier, plane_idx, buffer->attributes.modifier); close(fd); return; @@ -117,6 +118,19 @@ static void buffer_handle_resource_destroy(struct wl_resource *buffer_resource) linux_dmabuf_buffer_destroy(buffer); } +static bool check_import_dmabuf(struct wlr_dmabuf_buffer *buffer) { + struct wlr_texture *texture = + wlr_texture_from_dmabuf(buffer->renderer, &buffer->attributes); + if (texture == NULL) { + return false; + } + + // We can import the image, good. No need to keep it since wlr_surface will + // import it again on commit. + wlr_texture_destroy(texture); + return true; +} + static void params_create_common(struct wl_client *client, struct wl_resource *params_resource, uint32_t buffer_id, int32_t width, int32_t height, uint32_t format, uint32_t flags) { @@ -190,7 +204,7 @@ static void params_create_common(struct wl_client *client, // Skip checks if kernel does no support seek on buffer continue; } - if (buffer->attributes.offset[i] >= size) { + if (buffer->attributes.offset[i] > size) { wl_resource_post_error(params_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, "invalid offset %i for plane %d", @@ -198,7 +212,8 @@ static void params_create_common(struct wl_client *client, goto err_out; } - if (buffer->attributes.offset[i] + buffer->attributes.stride[i] > size) { + if (buffer->attributes.offset[i] + buffer->attributes.stride[i] > size || + buffer->attributes.stride[i] == 0) { wl_resource_post_error(params_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, "invalid stride %i for plane %d", @@ -208,7 +223,7 @@ static void params_create_common(struct wl_client *client, // planes > 0 might be subsampled according to fourcc format if (i == 0 && buffer->attributes.offset[i] + - buffer->attributes.stride[i] * height >= size) { + buffer->attributes.stride[i] * height > size) { wl_resource_post_error(params_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, "invalid buffer stride or height for plane %d", i); @@ -225,8 +240,7 @@ static void params_create_common(struct wl_client *client, } /* Check if dmabuf is usable */ - if (!wlr_renderer_check_import_dmabuf(buffer->renderer, - &buffer->attributes)) { + if (!check_import_dmabuf(buffer)) { goto err_failed; } diff --git a/types/wlr_output.c b/types/wlr_output.c index 2c8aef66..40332efd 100644 --- a/types/wlr_output.c +++ b/types/wlr_output.c @@ -367,7 +367,8 @@ static void output_fullscreen_surface_render(struct wlr_output *output, struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); assert(renderer); - if (!wlr_surface_has_buffer(surface)) { + struct wlr_texture *texture = wlr_surface_get_texture(surface); + if (texture == NULL) { wlr_renderer_clear(renderer, (float[]){0, 0, 0, 1}); return; } @@ -386,8 +387,7 @@ static void output_fullscreen_surface_render(struct wlr_output *output, for (int i = 0; i < nrects; ++i) { output_scissor(output, &rects[i]); wlr_renderer_clear(renderer, (float[]){0, 0, 0, 1}); - wlr_render_texture_with_matrix(surface->renderer, surface->texture, - matrix, 1.0f); + wlr_render_texture_with_matrix(surface->renderer, texture, matrix, 1.0f); } wlr_renderer_scissor(renderer, NULL); @@ -418,7 +418,7 @@ static void output_cursor_render(struct wlr_output_cursor *cursor, struct wlr_texture *texture = cursor->texture; if (cursor->surface != NULL) { - texture = cursor->surface->texture; + texture = wlr_surface_get_texture(cursor->surface); } if (texture == NULL) { return; @@ -713,7 +713,7 @@ static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) { enum wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; struct wlr_texture *texture = cursor->texture; if (cursor->surface != NULL) { - texture = cursor->surface->texture; + texture = wlr_surface_get_texture(cursor->surface); scale = cursor->surface->current->scale; transform = cursor->surface->current->transform; } diff --git a/types/wlr_surface.c b/types/wlr_surface.c index 61284416..af1e9446 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -1,10 +1,9 @@ #include #include #include -#include #include +#include #include -#include #include #include #include @@ -16,6 +15,22 @@ #define SURFACE_VERSION 4 #define SUBSURFACE_VERSION 1 +static int min(int fst, int snd) { + if (fst < snd) { + return fst; + } else { + return snd; + } +} + +static int max(int fst, int snd) { + if (fst > snd) { + return fst; + } else { + return snd; + } +} + static void surface_state_reset_buffer(struct wlr_surface_state *state) { if (state->buffer) { wl_list_remove(&state->buffer_destroy_listener.link); @@ -30,13 +45,6 @@ static void surface_handle_buffer_destroy(struct wl_listener *listener, surface_state_reset_buffer(state); } -static void surface_state_release_buffer(struct wlr_surface_state *state) { - if (state->buffer) { - wl_buffer_send_release(state->buffer); - surface_state_reset_buffer(state); - } -} - static void surface_state_set_buffer(struct wlr_surface_state *state, struct wl_resource *buffer) { surface_state_reset_buffer(state); @@ -159,24 +167,8 @@ static bool surface_update_size(struct wlr_surface *surface, int scale = state->scale; enum wl_output_transform transform = state->transform; - struct wl_shm_buffer *buf = wl_shm_buffer_get(state->buffer); - if (buf != NULL) { - state->buffer_width = wl_shm_buffer_get_width(buf); - state->buffer_height = wl_shm_buffer_get_height(buf); - } else if (wlr_renderer_resource_is_wl_drm_buffer(surface->renderer, - state->buffer)) { - wlr_renderer_wl_drm_buffer_get_size(surface->renderer, state->buffer, - &state->buffer_width, &state->buffer_height); - } else if (wlr_dmabuf_resource_is_buffer(state->buffer)) { - struct wlr_dmabuf_buffer *dmabuf = - wlr_dmabuf_buffer_from_buffer_resource(state->buffer); - state->buffer_width = dmabuf->attributes.width; - state->buffer_height = dmabuf->attributes.height; - } else { - wlr_log(L_ERROR, "Unknown buffer handle attached"); - state->buffer_width = 0; - state->buffer_height = 0; - } + 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; @@ -227,7 +219,6 @@ static void surface_move_state(struct wlr_surface *surface, update_size = true; } if ((next->invalid & WLR_SURFACE_INVALID_BUFFER)) { - surface_state_release_buffer(state); surface_state_set_buffer(state, next->buffer); surface_state_reset_buffer(next); state->sx = next->sx; @@ -335,86 +326,57 @@ static void surface_damage_subsurfaces(struct wlr_subsurface *subsurface) { } static void surface_apply_damage(struct wlr_surface *surface, - bool invalid_buffer, bool reupload_buffer) { + bool invalid_buffer) { struct wl_resource *resource = surface->current->buffer; if (resource == NULL) { + // NULL commit + wlr_buffer_unref(surface->buffer); + surface->buffer = NULL; return; } - struct wl_shm_buffer *buf = wl_shm_buffer_get(resource); - if (buf != NULL) { - wl_shm_buffer_begin_access(buf); + if (surface->buffer != NULL && !surface->buffer->released && + !invalid_buffer) { + // The buffer is still the same, no need to re-upload + return; + } - enum wl_shm_format fmt = wl_shm_buffer_get_format(buf); - int32_t stride = wl_shm_buffer_get_stride(buf); - int32_t width = wl_shm_buffer_get_width(buf); - int32_t height = wl_shm_buffer_get_height(buf); - void *data = wl_shm_buffer_get_data(buf); + 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_intersect_rect(&damage, &damage, 0, 0, + surface->current->buffer_width, surface->current->buffer_height); - if (surface->texture == NULL || reupload_buffer) { - wlr_texture_destroy(surface->texture); - surface->texture = wlr_texture_from_pixels(surface->renderer, fmt, - stride, width, height, data); - } else { - pixman_region32_t damage; - pixman_region32_init(&damage); - pixman_region32_copy(&damage, &surface->current->buffer_damage); - pixman_region32_intersect_rect(&damage, &damage, 0, 0, - surface->current->buffer_width, - surface->current->buffer_height); - - int n; - pixman_box32_t *rects = pixman_region32_rectangles(&damage, &n); - for (int i = 0; i < n; ++i) { - pixman_box32_t *r = &rects[i]; - if (!wlr_texture_write_pixels(surface->texture, fmt, stride, - r->x2 - r->x1, r->y2 - r->y1, r->x1, r->y1, - r->x1, r->y1, data)) { - break; - } - } - - pixman_region32_fini(&damage); - } + struct wlr_buffer *updated_buffer = + wlr_buffer_apply_damage(surface->buffer, resource, &damage); - wl_shm_buffer_end_access(buf); - } else if (invalid_buffer || reupload_buffer) { - wlr_texture_destroy(surface->texture); - - if (wlr_renderer_resource_is_wl_drm_buffer(surface->renderer, resource)) { - surface->texture = - wlr_texture_from_wl_drm(surface->renderer, resource); - } else if (wlr_dmabuf_resource_is_buffer(resource)) { - struct wlr_dmabuf_buffer *dmabuf = - wlr_dmabuf_buffer_from_buffer_resource(resource); - surface->texture = - wlr_texture_from_dmabuf(surface->renderer, &dmabuf->attributes); - } else { - surface->texture = NULL; - wlr_log(L_ERROR, "Unknown buffer handle attached"); + pixman_region32_fini(&damage); + + if (updated_buffer != NULL) { + surface->buffer = updated_buffer; + return; } } - surface_state_release_buffer(surface->current); + wlr_buffer_unref(surface->buffer); + surface->buffer = NULL; + + struct wlr_buffer *buffer = wlr_buffer_create(surface->renderer, resource); + if (buffer == NULL) { + wlr_log(L_ERROR, "Failed to upload buffer"); + return; + } + + surface->buffer = buffer; } static void surface_commit_pending(struct wlr_surface *surface) { - int32_t oldw = surface->current->buffer_width; - int32_t oldh = surface->current->buffer_height; - bool invalid_buffer = surface->pending->invalid & WLR_SURFACE_INVALID_BUFFER; - bool null_buffer_commit = invalid_buffer && surface->pending->buffer == NULL; surface_move_state(surface, surface->pending, surface->current); - if (null_buffer_commit) { - wlr_texture_destroy(surface->texture); - surface->texture = NULL; - } - - bool reupload_buffer = oldw != surface->current->buffer_width || - oldh != surface->current->buffer_height; - surface_apply_damage(surface, invalid_buffer, reupload_buffer); + surface_apply_damage(surface, invalid_buffer); // commit subsurface order struct wlr_subsurface *subsurface; @@ -636,10 +598,9 @@ 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); - wlr_texture_destroy(surface->texture); surface_state_destroy(surface->pending); surface_state_destroy(surface->current); - + wlr_buffer_unref(surface->buffer); free(surface); } @@ -696,8 +657,15 @@ struct wlr_surface *wlr_surface_create(struct wl_client *client, return surface; } +struct wlr_texture *wlr_surface_get_texture(struct wlr_surface *surface) { + if (surface->buffer == NULL) { + return NULL; + } + return surface->buffer->texture; +} + bool wlr_surface_has_buffer(struct wlr_surface *surface) { - return surface->texture != NULL; + return wlr_surface_get_texture(surface) != NULL; } int wlr_surface_set_role(struct wlr_surface *surface, const char *role, @@ -789,7 +757,7 @@ static void subsurface_handle_place_above(struct wl_client *client, } wl_list_remove(&subsurface->parent_pending_link); - wl_list_insert(sibling->parent_pending_link.prev, + wl_list_insert(&sibling->parent_pending_link, &subsurface->parent_pending_link); subsurface->reordered = true; @@ -816,7 +784,7 @@ static void subsurface_handle_place_below(struct wl_client *client, } wl_list_remove(&subsurface->parent_pending_link); - wl_list_insert(&sibling->parent_pending_link, + wl_list_insert(sibling->parent_pending_link.prev, &subsurface->parent_pending_link); subsurface->reordered = true; @@ -917,8 +885,8 @@ struct wlr_subsurface *wlr_subsurface_create(struct wlr_surface *surface, subsurface->parent = parent; wl_signal_add(&parent->events.destroy, &subsurface->parent_destroy); subsurface->parent_destroy.notify = subsurface_handle_parent_destroy; - wl_list_insert(&parent->subsurfaces, &subsurface->parent_link); - wl_list_insert(&parent->subsurface_pending_list, + wl_list_insert(parent->subsurfaces.prev, &subsurface->parent_link); + wl_list_insert(parent->subsurface_pending_list.prev, &subsurface->parent_pending_link); surface->role_data = subsurface; @@ -940,7 +908,7 @@ 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); - surface = subsurface->surface; + surface = subsurface->parent; } return surface; } @@ -955,7 +923,7 @@ bool wlr_surface_point_accepts_input(struct wlr_surface *surface, 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(subsurface, &surface->subsurfaces, parent_link) { + 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; struct wlr_surface *sub = wlr_surface_surface_at(subsurface->surface, @@ -1036,3 +1004,35 @@ void wlr_surface_for_each_surface(struct wlr_surface *surface, wlr_surface_iterator_func_t iterator, void *user_data) { surface_for_each_surface(surface, 0, 0, iterator, user_data); } + +struct bound_acc { + int32_t min_x, min_y; + int32_t max_x, max_y; +}; + +static void handle_bounding_box_surface(struct wlr_surface *surface, + int x, int y, void *data) { + struct bound_acc *acc = data; + + acc->min_x = min(x, acc->min_x); + acc->min_y = min(y, acc->min_y); + + acc->max_x = max(x + surface->current->width, acc->max_x); + acc->max_y = max(y + surface->current->height, acc->max_y); +} + +void wlr_surface_get_extends(struct wlr_surface *surface, struct wlr_box *box) { + struct bound_acc acc = { + .min_x = 0, + .min_y = 0, + .max_x = surface->current->width, + .max_y = surface->current->height, + }; + + wlr_surface_for_each_surface(surface, handle_bounding_box_surface, &acc); + + box->x = acc.min_x; + box->y = acc.min_y; + box->width = acc.max_x - acc.min_x; + box->height = acc.max_y - acc.min_y; +} diff --git a/types/xdg_shell/wlr_xdg_popup.c b/types/xdg_shell/wlr_xdg_popup.c index 9c8b6eb2..e7204def 100644 --- a/types/xdg_shell/wlr_xdg_popup.c +++ b/types/xdg_shell/wlr_xdg_popup.c @@ -416,7 +416,7 @@ static bool xdg_popup_unconstrain_slide(struct wlr_xdg_popup *popup, (popup->positioner.constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X); - bool slide_y = offset_x && + bool slide_y = offset_y && (popup->positioner.constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y); @@ -459,7 +459,7 @@ static bool xdg_popup_unconstrain_resize(struct wlr_xdg_popup *popup, (popup->positioner.constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X); - bool resize_y = offset_x && + bool resize_y = offset_y && (popup->positioner.constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y); @@ -471,7 +471,7 @@ static bool xdg_popup_unconstrain_resize(struct wlr_xdg_popup *popup, } xdg_popup_box_constraints(popup, toplevel_sx_box, - &offset_y, &offset_y); + &offset_x, &offset_y); return !offset_x && !offset_y; } diff --git a/types/xdg_shell/wlr_xdg_positioner.c b/types/xdg_shell/wlr_xdg_positioner.c index 0213c572..6f902f92 100644 --- a/types/xdg_shell/wlr_xdg_positioner.c +++ b/types/xdg_shell/wlr_xdg_positioner.c @@ -256,6 +256,8 @@ static enum xdg_positioner_anchor positioner_anchor_invert_x( return XDG_POSITIONER_ANCHOR_LEFT; case XDG_POSITIONER_ANCHOR_TOP_LEFT: return XDG_POSITIONER_ANCHOR_TOP_RIGHT; + case XDG_POSITIONER_ANCHOR_TOP_RIGHT: + return XDG_POSITIONER_ANCHOR_TOP_LEFT; case XDG_POSITIONER_ANCHOR_BOTTOM_LEFT: return XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT; case XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT: diff --git a/types/xdg_shell/wlr_xdg_surface.c b/types/xdg_shell/wlr_xdg_surface.c index 6f0e7264..c5d177b2 100644 --- a/types/xdg_shell/wlr_xdg_surface.c +++ b/types/xdg_shell/wlr_xdg_surface.c @@ -251,6 +251,13 @@ static void xdg_surface_handle_set_window_geometry(struct wl_client *client, return; } + if (width <= 0 || height <= 0) { + wlr_log(L_ERROR, "Client tried to set invalid geometry"); + //XXX: Switch to the proper error value once available + wl_resource_post_error(resource, -1, "Tried to set invalid xdg-surface geometry"); + return; + } + surface->has_next_geometry = true; surface->next_geometry.height = height; surface->next_geometry.width = width; @@ -474,9 +481,11 @@ static void xdg_popup_get_position(struct wlr_xdg_popup *popup, double *popup_sx, double *popup_sy) { struct wlr_xdg_surface *parent = wlr_xdg_surface_from_wlr_surface(popup->parent); - *popup_sx = parent->geometry.x + popup->geometry.x - + struct wlr_box parent_geo; + wlr_xdg_surface_get_geometry(parent, &parent_geo); + *popup_sx = parent_geo.x + popup->geometry.x - popup->base->geometry.x; - *popup_sy = parent->geometry.y + popup->geometry.y - + *popup_sy = parent_geo.y + popup->geometry.y - popup->base->geometry.y; } @@ -546,3 +555,13 @@ 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_get_geometry(struct wlr_xdg_surface *surface, struct wlr_box *box) { + wlr_surface_get_extends(surface->surface, box); + /* The client never set the geometry */ + if (!surface->geometry.width) { + return; + } + + wlr_box_intersection(&surface->geometry, box, box); +} diff --git a/types/xdg_shell/wlr_xdg_toplevel.c b/types/xdg_shell/wlr_xdg_toplevel.c index 5968c835..fa0ec929 100644 --- a/types/xdg_shell/wlr_xdg_toplevel.c +++ b/types/xdg_shell/wlr_xdg_toplevel.c @@ -218,6 +218,7 @@ static void xdg_toplevel_handle_set_parent(struct wl_client *client, } surface->toplevel->parent = parent; + wlr_signal_emit_safe(&surface->toplevel->events.set_parent, surface); } static void xdg_toplevel_handle_set_title(struct wl_client *client, @@ -464,6 +465,7 @@ void create_xdg_toplevel(struct wlr_xdg_surface *xdg_surface, wl_signal_init(&xdg_surface->toplevel->events.request_move); wl_signal_init(&xdg_surface->toplevel->events.request_resize); wl_signal_init(&xdg_surface->toplevel->events.request_show_window_menu); + wl_signal_init(&xdg_surface->toplevel->events.set_parent); xdg_surface->role = WLR_XDG_SURFACE_ROLE_TOPLEVEL; xdg_surface->toplevel->base = xdg_surface; diff --git a/types/xdg_shell_v6/wlr_xdg_popup_v6.c b/types/xdg_shell_v6/wlr_xdg_popup_v6.c index a75201d0..ff66e812 100644 --- a/types/xdg_shell_v6/wlr_xdg_popup_v6.c +++ b/types/xdg_shell_v6/wlr_xdg_popup_v6.c @@ -443,7 +443,7 @@ static bool xdg_popup_v6_unconstrain_slide(struct wlr_xdg_popup_v6 *popup, (popup->positioner.constraint_adjustment & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_X); - bool slide_y = offset_x && + bool slide_y = offset_y && (popup->positioner.constraint_adjustment & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_Y); @@ -486,7 +486,7 @@ static bool xdg_popup_v6_unconstrain_resize(struct wlr_xdg_popup_v6 *popup, (popup->positioner.constraint_adjustment & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_X); - bool resize_y = offset_x && + bool resize_y = offset_y && (popup->positioner.constraint_adjustment & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y); @@ -498,7 +498,7 @@ static bool xdg_popup_v6_unconstrain_resize(struct wlr_xdg_popup_v6 *popup, } xdg_popup_v6_box_constraints(popup, toplevel_sx_box, - &offset_y, &offset_y); + &offset_x, &offset_y); return !offset_x && !offset_y; } diff --git a/types/xdg_shell_v6/wlr_xdg_surface_v6.c b/types/xdg_shell_v6/wlr_xdg_surface_v6.c index a1214a0a..e111adad 100644 --- a/types/xdg_shell_v6/wlr_xdg_surface_v6.c +++ b/types/xdg_shell_v6/wlr_xdg_surface_v6.c @@ -198,6 +198,12 @@ static void xdg_surface_handle_set_window_geometry(struct wl_client *client, return; } + if (width <= 0 || height <= 0) { + wlr_log(L_ERROR, "Client tried to set invalid geometry"); + wl_resource_post_error(resource, -1, "Tried to set invalid xdg-surface geometry"); + } + + surface->has_next_geometry = true; surface->next_geometry.height = height; surface->next_geometry.width = width; @@ -454,9 +460,11 @@ struct wlr_xdg_surface_v6 *create_xdg_surface_v6( static void xdg_popup_v6_get_position(struct wlr_xdg_popup_v6 *popup, double *popup_sx, double *popup_sy) { struct wlr_xdg_surface_v6 *parent = popup->parent; - *popup_sx = parent->geometry.x + popup->geometry.x - + struct wlr_box parent_geo; + wlr_xdg_surface_v6_get_geometry(parent, &parent_geo); + *popup_sx = parent_geo.x + popup->geometry.x - popup->base->geometry.x; - *popup_sy = parent->geometry.y + popup->geometry.y - + *popup_sy = parent_geo.y + popup->geometry.y - popup->base->geometry.y; } @@ -526,3 +534,13 @@ 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_get_geometry(struct wlr_xdg_surface_v6 *surface, struct wlr_box *box) { + wlr_surface_get_extends(surface->surface, box); + /* The client never set the geometry */ + if (!surface->geometry.width) { + return; + } + + wlr_box_intersection(&surface->geometry, box, box); +} diff --git a/types/xdg_shell_v6/wlr_xdg_toplevel_v6.c b/types/xdg_shell_v6/wlr_xdg_toplevel_v6.c index 2bcfaf0d..0f9a26d3 100644 --- a/types/xdg_shell_v6/wlr_xdg_toplevel_v6.c +++ b/types/xdg_shell_v6/wlr_xdg_toplevel_v6.c @@ -42,6 +42,7 @@ static void xdg_toplevel_handle_set_parent(struct wl_client *client, } surface->toplevel->parent = parent; + wlr_signal_emit_safe(&surface->toplevel->events.set_parent, surface); } static void xdg_toplevel_handle_set_title(struct wl_client *client, @@ -434,6 +435,7 @@ void create_xdg_toplevel_v6(struct wlr_xdg_surface_v6 *xdg_surface, wl_signal_init(&xdg_surface->toplevel->events.request_move); wl_signal_init(&xdg_surface->toplevel->events.request_resize); wl_signal_init(&xdg_surface->toplevel->events.request_show_window_menu); + wl_signal_init(&xdg_surface->toplevel->events.set_parent); xdg_surface->role = WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL; xdg_surface->toplevel->base = xdg_surface;