Merge branch 'master' into xwayland-dnd

master
emersion 7 years ago
commit 8836b4f024
No known key found for this signature in database
GPG Key ID: 0FDE7BE0E88F5E48

@ -61,26 +61,48 @@ struct wlr_renderer *wlr_backend_get_renderer(struct wlr_backend *backend) {
return NULL; return NULL;
} }
static struct wlr_backend *attempt_wl_backend(struct wl_display *display) { static size_t parse_outputs_env(const char *name) {
struct wlr_backend *backend = wlr_wl_backend_create(display, NULL); const char *outputs_str = getenv(name);
if (backend) { if (outputs_str == NULL) {
int outputs = 1; return 1;
const char *_outputs = getenv("WLR_WL_OUTPUTS"); }
if (_outputs) {
char *end; char *end;
outputs = (int)strtol(_outputs, &end, 10); int outputs = (int)strtol(outputs_str, &end, 10);
if (*end) { if (*end || outputs < 0) {
wlr_log(L_ERROR, "WLR_WL_OUTPUTS specified with invalid integer, ignoring"); wlr_log(L_ERROR, "%s specified with invalid integer, ignoring", name);
outputs = 1; return 1;
} else if (outputs < 0) {
wlr_log(L_ERROR, "WLR_WL_OUTPUTS specified with negative outputs, ignoring");
outputs = 1;
} }
return outputs;
}
static struct wlr_backend *attempt_wl_backend(struct wl_display *display) {
struct wlr_backend *backend = wlr_wl_backend_create(display, NULL);
if (backend == NULL) {
return NULL;
} }
while (outputs--) {
size_t outputs = parse_outputs_env("WLR_WL_OUTPUTS");
for (size_t i = 0; i < outputs; ++i) {
wlr_wl_output_create(backend); wlr_wl_output_create(backend);
} }
return backend;
}
static struct wlr_backend *attempt_x11_backend(struct wl_display *display,
const char *x11_display) {
struct wlr_backend *backend = wlr_x11_backend_create(display, x11_display);
if (backend == NULL) {
return NULL;
}
size_t outputs = parse_outputs_env("WLR_X11_OUTPUTS");
for (size_t i = 0; i < outputs; ++i) {
wlr_x11_output_create(backend);
} }
return backend; return backend;
} }
@ -91,7 +113,8 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display) {
return NULL; return NULL;
} }
if (getenv("WAYLAND_DISPLAY") || getenv("_WAYLAND_DISPLAY")) { if (getenv("WAYLAND_DISPLAY") || getenv("_WAYLAND_DISPLAY") ||
getenv("WAYLAND_SOCKET")) {
struct wlr_backend *wl_backend = attempt_wl_backend(display); struct wlr_backend *wl_backend = attempt_wl_backend(display);
if (wl_backend) { if (wl_backend) {
wlr_multi_backend_add(backend, wl_backend); wlr_multi_backend_add(backend, wl_backend);
@ -103,10 +126,12 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display) {
const char *x11_display = getenv("DISPLAY"); const char *x11_display = getenv("DISPLAY");
if (x11_display) { if (x11_display) {
struct wlr_backend *x11_backend = struct wlr_backend *x11_backend =
wlr_x11_backend_create(display, x11_display); attempt_x11_backend(display, x11_display);
if (x11_backend) {
wlr_multi_backend_add(backend, x11_backend); wlr_multi_backend_add(backend, x11_backend);
return backend; return backend;
} }
}
#endif #endif
// Attempt DRM+libinput // Attempt DRM+libinput

@ -181,9 +181,6 @@ void wlr_drm_resources_free(struct wlr_drm_backend *drm) {
if (plane->cursor_bo) { if (plane->cursor_bo) {
gbm_bo_destroy(plane->cursor_bo); gbm_bo_destroy(plane->cursor_bo);
} }
if (plane->wlr_tex) {
wlr_texture_destroy(plane->wlr_tex);
}
} }
free(drm->crtcs); free(drm->crtcs);
@ -582,16 +579,9 @@ static bool wlr_drm_connector_set_cursor(struct wlr_output *output,
return false; return false;
} }
enum wl_output_transform transform = enum wl_output_transform transform = output->transform;
wlr_output_transform_invert(output->transform);
wlr_matrix_projection(plane->matrix, plane->surf.width, wlr_matrix_projection(plane->matrix, plane->surf.width,
plane->surf.height, transform); plane->surf.height, transform);
plane->wlr_tex =
wlr_render_texture_create(plane->surf.renderer->wlr_rend);
if (!plane->wlr_tex) {
return false;
}
} }
struct wlr_box hotspot = { .x = hotspot_x, .y = hotspot_y }; struct wlr_box hotspot = { .x = hotspot_x, .y = hotspot_y };
@ -637,13 +627,18 @@ static bool wlr_drm_connector_set_cursor(struct wlr_output *output,
wlr_drm_surface_make_current(&plane->surf, NULL); wlr_drm_surface_make_current(&plane->surf, NULL);
wlr_texture_upload_pixels(plane->wlr_tex, WL_SHM_FORMAT_ARGB8888,
stride, width, height, buf);
struct wlr_renderer *rend = plane->surf.renderer->wlr_rend; struct wlr_renderer *rend = plane->surf.renderer->wlr_rend;
struct wlr_texture *texture = wlr_texture_from_pixels(rend,
WL_SHM_FORMAT_ARGB8888, stride, width, height, buf);
if (texture == NULL) {
wlr_log(L_ERROR, "Unable to create texture");
return false;
}
wlr_renderer_begin(rend, plane->surf.width, plane->surf.height); wlr_renderer_begin(rend, plane->surf.width, plane->surf.height);
wlr_renderer_clear(rend, (float[]){ 0.0, 0.0, 0.0, 0.0 }); wlr_renderer_clear(rend, (float[]){ 0.0, 0.0, 0.0, 0.0 });
wlr_render_texture(rend, plane->wlr_tex, plane->matrix, 0, 0, 1.0f); wlr_render_texture(rend, texture, plane->matrix, 0, 0, 1.0f);
wlr_renderer_end(rend); wlr_renderer_end(rend);
wlr_renderer_read_pixels(rend, WL_SHM_FORMAT_ARGB8888, bo_stride, wlr_renderer_read_pixels(rend, WL_SHM_FORMAT_ARGB8888, bo_stride,
@ -651,6 +646,7 @@ static bool wlr_drm_connector_set_cursor(struct wlr_output *output,
wlr_drm_surface_swap_buffers(&plane->surf, NULL); wlr_drm_surface_swap_buffers(&plane->surf, NULL);
wlr_texture_destroy(texture);
gbm_bo_unmap(plane->cursor_bo, bo_data); gbm_bo_unmap(plane->cursor_bo, bo_data);
} }

@ -47,7 +47,7 @@ bool legacy_crtc_set_cursor(struct wlr_drm_backend *drm,
if (drmModeSetCursor(drm->fd, crtc->id, gbm_bo_get_handle(bo).u32, if (drmModeSetCursor(drm->fd, crtc->id, gbm_bo_get_handle(bo).u32,
plane->surf.width, plane->surf.height)) { plane->surf.width, plane->surf.height)) {
wlr_log_errno(L_ERROR, "Failed to set hardware cursor"); wlr_log_errno(L_DEBUG, "Failed to set hardware cursor");
return false; return false;
} }

@ -14,6 +14,10 @@
#include "backend/drm/drm.h" #include "backend/drm/drm.h"
#include "glapi.h" #include "glapi.h"
#ifndef DRM_FORMAT_MOD_LINEAR
#define DRM_FORMAT_MOD_LINEAR 0
#endif
bool wlr_drm_renderer_init(struct wlr_drm_backend *drm, bool wlr_drm_renderer_init(struct wlr_drm_backend *drm,
struct wlr_drm_renderer *renderer) { struct wlr_drm_renderer *renderer) {
renderer->gbm = gbm_create_device(drm->fd); renderer->gbm = gbm_create_device(drm->fd);
@ -27,7 +31,7 @@ bool wlr_drm_renderer_init(struct wlr_drm_backend *drm,
goto error_gbm; goto error_gbm;
} }
renderer->wlr_rend = wlr_gles2_renderer_create(&drm->backend); renderer->wlr_rend = wlr_gles2_renderer_create(&renderer->egl);
if (!renderer->wlr_rend) { if (!renderer->wlr_rend) {
wlr_log(L_ERROR, "Failed to create WLR renderer"); wlr_log(L_ERROR, "Failed to create WLR renderer");
goto error_egl; goto error_egl;
@ -178,47 +182,33 @@ static void free_eglimage(struct gbm_bo *bo, void *data) {
static struct wlr_texture *get_tex_for_bo(struct wlr_drm_renderer *renderer, static struct wlr_texture *get_tex_for_bo(struct wlr_drm_renderer *renderer,
struct gbm_bo *bo) { struct gbm_bo *bo) {
struct tex *tex = gbm_bo_get_user_data(bo); struct tex *tex = gbm_bo_get_user_data(bo);
if (tex) { if (tex != NULL) {
return tex->tex; return tex->tex;
} }
// TODO: use wlr_texture_upload_dmabuf instead tex = calloc(1, sizeof(struct tex));
if (tex == NULL) {
tex = malloc(sizeof(*tex));
if (!tex) {
wlr_log_errno(L_ERROR, "Allocation failed");
return NULL; return NULL;
} }
tex->egl = &renderer->egl; struct wlr_dmabuf_buffer_attribs attribs = {
.n_planes = 1,
int dmabuf_fd = gbm_bo_get_fd(bo); .width = gbm_bo_get_width(bo),
uint32_t width = gbm_bo_get_width(bo); .height = gbm_bo_get_height(bo),
uint32_t height = gbm_bo_get_height(bo); .format = gbm_bo_get_format(bo),
EGLint attribs[] = {
EGL_WIDTH, width,
EGL_HEIGHT, height,
EGL_LINUX_DRM_FOURCC_EXT, gbm_bo_get_format(bo),
EGL_DMA_BUF_PLANE0_FD_EXT, dmabuf_fd,
EGL_DMA_BUF_PLANE0_OFFSET_EXT, gbm_bo_get_offset(bo, 0),
EGL_DMA_BUF_PLANE0_PITCH_EXT, gbm_bo_get_stride_for_plane(bo, 0),
EGL_IMAGE_PRESERVED_KHR, EGL_FALSE,
EGL_NONE,
}; };
attribs.offset[0] = 0;
attribs.stride[0] = gbm_bo_get_stride_for_plane(bo, 0);
attribs.modifier[0] = DRM_FORMAT_MOD_LINEAR;
attribs.fd[0] = gbm_bo_get_fd(bo);
tex->img = eglCreateImageKHR(renderer->egl.display, EGL_NO_CONTEXT, tex->tex = wlr_texture_from_dmabuf(renderer->wlr_rend, &attribs);
EGL_LINUX_DMA_BUF_EXT, NULL, attribs); if (tex->tex == NULL) {
if (!tex->img) { free(tex);
wlr_log(L_ERROR, "Failed to create EGL image"); return NULL;
abort();
} }
tex->tex = wlr_render_texture_create(renderer->wlr_rend);
wlr_texture_upload_eglimage(tex->tex, tex->img, width, height);
gbm_bo_set_user_data(bo, tex, free_eglimage); gbm_bo_set_user_data(bo, tex, free_eglimage);
return tex->tex; return tex->tex;
} }

@ -114,7 +114,7 @@ struct wlr_backend *wlr_headless_backend_create(struct wl_display *display) {
return NULL; return NULL;
} }
backend->renderer = wlr_gles2_renderer_create(&backend->backend); backend->renderer = wlr_gles2_renderer_create(&backend->egl);
if (backend->renderer == NULL) { if (backend->renderer == NULL) {
wlr_log(L_ERROR, "Failed to create renderer"); wlr_log(L_ERROR, "Failed to create renderer");
} }

@ -46,6 +46,8 @@ static struct wlr_input_device *allocate_device(
return NULL; return NULL;
} }
struct wlr_input_device *wlr_dev = &wlr_libinput_dev->wlr_input_device; struct wlr_input_device *wlr_dev = &wlr_libinput_dev->wlr_input_device;
libinput_device_get_size(libinput_dev,
&wlr_dev->width_mm, &wlr_dev->height_mm);
wl_list_insert(wlr_devices, &wlr_dev->link); wl_list_insert(wlr_devices, &wlr_dev->link);
wlr_libinput_dev->handle = libinput_dev; wlr_libinput_dev->handle = libinput_dev;
libinput_device_ref(libinput_dev); libinput_device_ref(libinput_dev);

@ -53,9 +53,8 @@ void handle_pointer_motion_abs(struct libinput_event *event,
wlr_event.device = wlr_dev; wlr_event.device = wlr_dev;
wlr_event.time_msec = wlr_event.time_msec =
usec_to_msec(libinput_event_pointer_get_time_usec(pevent)); usec_to_msec(libinput_event_pointer_get_time_usec(pevent));
wlr_event.x_mm = libinput_event_pointer_get_absolute_x(pevent); wlr_event.x = libinput_event_pointer_get_absolute_x_transformed(pevent, 1);
wlr_event.y_mm = libinput_event_pointer_get_absolute_y(pevent); wlr_event.y = libinput_event_pointer_get_absolute_y_transformed(pevent, 1);
libinput_device_get_size(libinput_dev, &wlr_event.width_mm, &wlr_event.height_mm);
wlr_signal_emit_safe(&wlr_dev->pointer->events.motion_absolute, &wlr_event); wlr_signal_emit_safe(&wlr_dev->pointer->events.motion_absolute, &wlr_event);
} }

@ -34,14 +34,13 @@ void handle_tablet_tool_axis(struct libinput_event *event,
wlr_event.device = wlr_dev; wlr_event.device = wlr_dev;
wlr_event.time_msec = wlr_event.time_msec =
usec_to_msec(libinput_event_tablet_tool_get_time_usec(tevent)); usec_to_msec(libinput_event_tablet_tool_get_time_usec(tevent));
libinput_device_get_size(libinput_dev, &wlr_event.width_mm, &wlr_event.height_mm);
if (libinput_event_tablet_tool_x_has_changed(tevent)) { if (libinput_event_tablet_tool_x_has_changed(tevent)) {
wlr_event.updated_axes |= WLR_TABLET_TOOL_AXIS_X; wlr_event.updated_axes |= WLR_TABLET_TOOL_AXIS_X;
wlr_event.x_mm = libinput_event_tablet_tool_get_x(tevent); wlr_event.x = libinput_event_tablet_tool_get_x_transformed(tevent, 1);
} }
if (libinput_event_tablet_tool_y_has_changed(tevent)) { if (libinput_event_tablet_tool_y_has_changed(tevent)) {
wlr_event.updated_axes |= WLR_TABLET_TOOL_AXIS_Y; wlr_event.updated_axes |= WLR_TABLET_TOOL_AXIS_Y;
wlr_event.y_mm = libinput_event_tablet_tool_get_y(tevent); wlr_event.y = libinput_event_tablet_tool_get_y_transformed(tevent, 1);
} }
if (libinput_event_tablet_tool_pressure_has_changed(tevent)) { if (libinput_event_tablet_tool_pressure_has_changed(tevent)) {
wlr_event.updated_axes |= WLR_TABLET_TOOL_AXIS_PRESSURE; wlr_event.updated_axes |= WLR_TABLET_TOOL_AXIS_PRESSURE;

@ -35,9 +35,8 @@ void handle_touch_down(struct libinput_event *event,
wlr_event.time_msec = wlr_event.time_msec =
usec_to_msec(libinput_event_touch_get_time_usec(tevent)); usec_to_msec(libinput_event_touch_get_time_usec(tevent));
wlr_event.touch_id = libinput_event_touch_get_slot(tevent); wlr_event.touch_id = libinput_event_touch_get_slot(tevent);
wlr_event.x_mm = libinput_event_touch_get_x(tevent); wlr_event.x = libinput_event_touch_get_x_transformed(tevent, 1);
wlr_event.y_mm = libinput_event_touch_get_y(tevent); wlr_event.y = libinput_event_touch_get_y_transformed(tevent, 1);
libinput_device_get_size(libinput_dev, &wlr_event.width_mm, &wlr_event.height_mm);
wlr_signal_emit_safe(&wlr_dev->touch->events.down, &wlr_event); wlr_signal_emit_safe(&wlr_dev->touch->events.down, &wlr_event);
} }
@ -74,9 +73,8 @@ void handle_touch_motion(struct libinput_event *event,
wlr_event.time_msec = wlr_event.time_msec =
usec_to_msec(libinput_event_touch_get_time_usec(tevent)); usec_to_msec(libinput_event_touch_get_time_usec(tevent));
wlr_event.touch_id = libinput_event_touch_get_slot(tevent); wlr_event.touch_id = libinput_event_touch_get_slot(tevent);
wlr_event.x_mm = libinput_event_touch_get_x(tevent); wlr_event.x = libinput_event_touch_get_x_transformed(tevent, 1);
wlr_event.y_mm = libinput_event_touch_get_y(tevent); wlr_event.y = libinput_event_touch_get_y_transformed(tevent, 1);
libinput_device_get_size(libinput_dev, &wlr_event.width_mm, &wlr_event.height_mm);
wlr_signal_emit_safe(&wlr_dev->touch->events.motion, &wlr_event); wlr_signal_emit_safe(&wlr_dev->touch->events.motion, &wlr_event);
} }

@ -50,7 +50,12 @@ if conf_data.get('WLR_HAS_SYSTEMD', false)
endif endif
if conf_data.get('WLR_HAS_X11_BACKEND', false) if conf_data.get('WLR_HAS_X11_BACKEND', false)
backend_files += files('x11/backend.c') backend_files += files(
'x11/backend.c',
'x11/input_device.c',
'x11/output.c',
)
backend_deps += xcb_xkb
endif endif
if conf_data.get('WLR_HAS_ELOGIND', false) if conf_data.get('WLR_HAS_ELOGIND', false)

@ -212,7 +212,7 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display, const char
backend->remote_display, NULL, WL_SHM_FORMAT_ARGB8888); backend->remote_display, NULL, WL_SHM_FORMAT_ARGB8888);
wlr_egl_bind_display(&backend->egl, backend->local_display); wlr_egl_bind_display(&backend->egl, backend->local_display);
backend->renderer = wlr_gles2_renderer_create(&backend->backend); backend->renderer = wlr_gles2_renderer_create(&backend->egl);
if (backend->renderer == NULL) { if (backend->renderer == NULL) {
wlr_log_errno(L_ERROR, "Could not create renderer"); wlr_log_errno(L_ERROR, "Could not create renderer");
} }

@ -99,8 +99,6 @@ static bool wlr_wl_output_set_cursor(struct wlr_output *_output,
return true; return true;
} }
stride *= 4; // stride is given in pixels, we need it in bytes
if (!backend->shm || !backend->pointer) { if (!backend->shm || !backend->pointer) {
wlr_log(L_INFO, "cannot set cursor, no shm or pointer"); wlr_log(L_INFO, "cannot set cursor, no shm or pointer");
return false; return false;

@ -60,30 +60,29 @@ static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer,
struct wlr_output *wlr_output = &wlr_wl_pointer->current_output->wlr_output; struct wlr_output *wlr_output = &wlr_wl_pointer->current_output->wlr_output;
int width, height;
wl_egl_window_get_attached_size(wlr_wl_pointer->current_output->egl_window,
&width, &height);
struct wlr_box box = { struct wlr_box box = {
.x = wl_fixed_to_int(surface_x), .x = wl_fixed_to_int(surface_x),
.y = wl_fixed_to_int(surface_y), .y = wl_fixed_to_int(surface_y),
}; };
struct wlr_box transformed; wlr_box_transform(&box, wlr_output->transform, wlr_output->width,
wlr_box_transform(&box, wlr_output->transform, width, height, &transformed); wlr_output->height, &box);
transformed.x /= wlr_output->scale; box.x /= wlr_output->scale;
transformed.y /= wlr_output->scale; box.y /= wlr_output->scale;
struct wlr_box layout_box; struct wlr_box layout_box;
wlr_wl_output_layout_get_box(wlr_wl_pointer->current_output->backend, wlr_wl_output_layout_get_box(wlr_wl_pointer->current_output->backend,
&layout_box); &layout_box);
struct wlr_event_pointer_motion_absolute wlr_event; double ox = wlr_output->lx / (double)layout_box.width;
wlr_event.device = dev; double oy = wlr_output->ly / (double)layout_box.height;
wlr_event.time_msec = time;
wlr_event.width_mm = layout_box.width; struct wlr_event_pointer_motion_absolute wlr_event = {
wlr_event.height_mm = layout_box.height; .device = dev,
wlr_event.x_mm = transformed.x + wlr_output->lx - layout_box.x; .time_msec = time,
wlr_event.y_mm = transformed.y + wlr_output->ly - layout_box.y; .x = box.x / (double)layout_box.width + ox,
.y = box.y / (double)layout_box.height + oy,
};
wlr_signal_emit_safe(&dev->pointer->events.motion_absolute, &wlr_event); wlr_signal_emit_safe(&dev->pointer->events.motion_absolute, &wlr_event);
} }

@ -1,5 +1,5 @@
#define _POSIX_C_SOURCE 200112L #define _POSIX_C_SOURCE 200112L
#include <EGL/egl.h> #include <limits.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -7,158 +7,102 @@
#include <wayland-server.h> #include <wayland-server.h>
#include <wlr/backend/interface.h> #include <wlr/backend/interface.h>
#include <wlr/backend/x11.h> #include <wlr/backend/x11.h>
#include <wlr/config.h>
#include <wlr/interfaces/wlr_input_device.h> #include <wlr/interfaces/wlr_input_device.h>
#include <wlr/interfaces/wlr_keyboard.h> #include <wlr/interfaces/wlr_keyboard.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/interfaces/wlr_pointer.h> #include <wlr/interfaces/wlr_pointer.h>
#include <wlr/render/egl.h> #include <wlr/render/egl.h>
#include <wlr/render/gles2.h> #include <wlr/render/gles2.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include <X11/Xlib-xcb.h> #include <X11/Xlib-xcb.h>
#include <xcb/xcb.h> #include <xcb/xcb.h>
#ifdef __linux__ #ifdef WLR_HAS_XCB_XKB
#include <linux/input-event-codes.h> #include <xcb/xkb.h>
#elif __FreeBSD__
#include <dev/evdev/input-event-codes.h>
#endif #endif
#include "backend/x11.h" #include "backend/x11.h"
#include "util/signal.h" #include "util/signal.h"
#define XCB_EVENT_RESPONSE_TYPE_MASK 0x7f struct wlr_x11_output *x11_output_from_window_id(struct wlr_x11_backend *x11,
xcb_window_t window) {
static const struct wlr_backend_impl backend_impl; struct wlr_x11_output *output;
static const struct wlr_output_impl output_impl; wl_list_for_each(output, &x11->outputs, link) {
static const struct wlr_input_device_impl input_device_impl = { 0 }; if (output->win == window) {
return output;
static uint32_t xcb_button_to_wl(uint32_t button) { }
switch (button) {
case XCB_BUTTON_INDEX_1: return BTN_LEFT;
case XCB_BUTTON_INDEX_2: return BTN_MIDDLE;
case XCB_BUTTON_INDEX_3: return BTN_RIGHT;
// XXX: I'm not sure the scroll-wheel direction is right
case XCB_BUTTON_INDEX_4: return BTN_GEAR_UP;
case XCB_BUTTON_INDEX_5: return BTN_GEAR_DOWN;
default: return 0;
} }
return NULL;
} }
static bool handle_x11_event(struct wlr_x11_backend *x11, xcb_generic_event_t *event) { void x11_output_layout_get_box(struct wlr_x11_backend *backend,
struct wlr_x11_output *output = &x11->output; struct wlr_box *box) {
int min_x = INT_MAX, min_y = INT_MAX;
int max_x = INT_MIN, max_y = INT_MIN;
switch (event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK) { struct wlr_x11_output *output;
case XCB_EXPOSE: { wl_list_for_each(output, &backend->outputs, link) {
wlr_output_send_frame(&output->wlr_output); struct wlr_output *wlr_output = &output->wlr_output;
break;
}
case XCB_KEY_PRESS:
case XCB_KEY_RELEASE: {
xcb_key_press_event_t *ev = (xcb_key_press_event_t *)event;
struct wlr_event_keyboard_key key = {
.time_msec = ev->time,
.keycode = ev->detail - 8,
.state = event->response_type == XCB_KEY_PRESS ?
WLR_KEY_PRESSED : WLR_KEY_RELEASED,
.update_state = true,
};
// TODO use xcb-xkb for more precise modifiers state? int width, height;
wlr_keyboard_notify_key(&x11->keyboard, &key); wlr_output_effective_resolution(wlr_output, &width, &height);
x11->time = ev->time;
break; if (wlr_output->lx < min_x) {
min_x = wlr_output->lx;
} }
case XCB_BUTTON_PRESS: { if (wlr_output->ly < min_y) {
xcb_button_press_event_t *ev = (xcb_button_press_event_t *)event; min_y = wlr_output->ly;
}
if (ev->detail == XCB_BUTTON_INDEX_4 || if (wlr_output->lx + width > max_x) {
ev->detail == XCB_BUTTON_INDEX_5) { max_x = wlr_output->lx + width;
double delta = (ev->detail == XCB_BUTTON_INDEX_4 ? -15 : 15); }
struct wlr_event_pointer_axis axis = { if (wlr_output->ly + height > max_y) {
.device = &x11->pointer_dev, max_y = wlr_output->ly + height;
.time_msec = ev->time,
.source = WLR_AXIS_SOURCE_WHEEL,
.orientation = WLR_AXIS_ORIENTATION_VERTICAL,
.delta = delta,
};
wlr_signal_emit_safe(&x11->pointer.events.axis, &axis);
x11->time = ev->time;
break;
} }
} }
/* fallthrough */
case XCB_BUTTON_RELEASE: {
xcb_button_press_event_t *ev = (xcb_button_press_event_t *)event;
if (ev->detail != XCB_BUTTON_INDEX_4 && box->x = min_x;
ev->detail != XCB_BUTTON_INDEX_5) { box->y = min_y;
struct wlr_event_pointer_button button = { box->width = max_x - min_x;
.device = &x11->pointer_dev, box->height = max_y - min_y;
.time_msec = ev->time, }
.button = xcb_button_to_wl(ev->detail),
.state = event->response_type == XCB_BUTTON_PRESS ?
WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED,
};
wlr_signal_emit_safe(&x11->pointer.events.button, &button); static bool handle_x11_event(struct wlr_x11_backend *x11,
} xcb_generic_event_t *event) {
x11->time = ev->time; if (x11_handle_input_event(x11, event)) {
break; return false;
} }
case XCB_MOTION_NOTIFY: {
xcb_motion_notify_event_t *ev = (xcb_motion_notify_event_t *)event;
struct wlr_event_pointer_motion_absolute abs = {
.device = &x11->pointer_dev,
.time_msec = ev->time,
.x_mm = ev->event_x,
.y_mm = ev->event_y,
.width_mm = output->wlr_output.width,
.height_mm = output->wlr_output.height,
};
wlr_signal_emit_safe(&x11->pointer.events.motion_absolute, &abs); switch (event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK) {
x11->time = ev->time; case XCB_EXPOSE: {
xcb_expose_event_t *ev = (xcb_expose_event_t *)event;
struct wlr_x11_output *output =
x11_output_from_window_id(x11, ev->window);
if (output != NULL) {
wlr_output_send_frame(&output->wlr_output);
}
break; break;
} }
case XCB_CONFIGURE_NOTIFY: { case XCB_CONFIGURE_NOTIFY: {
xcb_configure_notify_event_t *ev = (xcb_configure_notify_event_t *)event; xcb_configure_notify_event_t *ev =
(xcb_configure_notify_event_t *)event;
wlr_output_update_custom_mode(&output->wlr_output, ev->width, struct wlr_x11_output *output =
ev->height, 0); x11_output_from_window_id(x11, ev->window);
if (output != NULL) {
// Move the pointer to its new location x11_output_handle_configure_notify(output, ev);
xcb_query_pointer_cookie_t cookie =
xcb_query_pointer(x11->xcb_conn, output->win);
xcb_query_pointer_reply_t *pointer =
xcb_query_pointer_reply(x11->xcb_conn, cookie, NULL);
if (!pointer) {
break;
} }
struct wlr_event_pointer_motion_absolute abs = {
.device = &x11->pointer_dev,
.time_msec = x11->time,
.x_mm = pointer->root_x,
.y_mm = pointer->root_y,
.width_mm = output->wlr_output.width,
.height_mm = output->wlr_output.height,
};
wlr_signal_emit_safe(&x11->pointer.events.motion_absolute, &abs);
free(pointer);
break; break;
} }
case XCB_CLIENT_MESSAGE: { case XCB_CLIENT_MESSAGE: {
xcb_client_message_event_t *ev = (xcb_client_message_event_t *)event; xcb_client_message_event_t *ev = (xcb_client_message_event_t *)event;
if (ev->data.data32[0] == x11->atoms.wm_delete_window) { if (ev->data.data32[0] == x11->atoms.wm_delete_window) {
wl_display_terminate(x11->wl_display); struct wlr_x11_output *output =
return true; x11_output_from_window_id(x11, ev->window);
if (output != NULL) {
wlr_output_destroy(&output->wlr_output);
} }
break;
} }
default:
break; break;
} }
}
return false; return false;
} }
@ -172,64 +116,20 @@ static int x11_event(int fd, uint32_t mask, void *data) {
} }
xcb_generic_event_t *e; xcb_generic_event_t *e;
bool quit = false; while ((e = xcb_poll_for_event(x11->xcb_conn))) {
while (!quit && (e = xcb_poll_for_event(x11->xcb_conn))) { bool quit = handle_x11_event(x11, e);
quit = handle_x11_event(x11, e);
free(e); free(e);
if (quit) {
break;
}
} }
return 0; return 0;
} }
static int signal_frame(void *data) {
struct wlr_x11_backend *x11 = data;
wlr_output_send_frame(&x11->output.wlr_output);
wl_event_source_timer_update(x11->frame_timer, 16);
return 0;
}
static void parse_xcb_setup(struct wlr_output *output, xcb_connection_t *xcb_conn) {
const xcb_setup_t *xcb_setup = xcb_get_setup(xcb_conn);
snprintf(output->make, sizeof(output->make), "%.*s",
xcb_setup_vendor_length(xcb_setup),
xcb_setup_vendor(xcb_setup));
snprintf(output->model, sizeof(output->model), "%"PRIu16".%"PRIu16,
xcb_setup->protocol_major_version,
xcb_setup->protocol_minor_version);
}
static bool wlr_x11_backend_start(struct wlr_backend *backend) { static bool wlr_x11_backend_start(struct wlr_backend *backend) {
struct wlr_x11_backend *x11 = (struct wlr_x11_backend *)backend; struct wlr_x11_backend *x11 = (struct wlr_x11_backend *)backend;
struct wlr_x11_output *output = &x11->output; x11->started = true;
uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
uint32_t values[2] = {
x11->screen->white_pixel,
XCB_EVENT_MASK_EXPOSURE |
XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE |
XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE |
XCB_EVENT_MASK_POINTER_MOTION |
XCB_EVENT_MASK_STRUCTURE_NOTIFY
};
output->x11 = x11;
wlr_output_init(&output->wlr_output, &x11->backend, &output_impl,
x11->wl_display);
snprintf(output->wlr_output.name, sizeof(output->wlr_output.name), "X11-1");
parse_xcb_setup(&output->wlr_output, x11->xcb_conn);
output->win = xcb_generate_id(x11->xcb_conn);
xcb_create_window(x11->xcb_conn, XCB_COPY_FROM_PARENT, output->win,
x11->screen->root, 0, 0, 1024, 768, 1, XCB_WINDOW_CLASS_INPUT_OUTPUT,
x11->screen->root_visual, mask, values);
output->surf = wlr_egl_create_surface(&x11->egl, &output->win);
if (!output->surf) {
wlr_log(L_ERROR, "Failed to create EGL surface");
return false;
}
struct { struct {
const char *name; const char *name;
@ -271,26 +171,47 @@ static bool wlr_x11_backend_start(struct wlr_backend *backend) {
} }
} }
xcb_change_property(x11->xcb_conn, XCB_PROP_MODE_REPLACE, output->win, // create a blank cursor
x11->atoms.wm_protocols, XCB_ATOM_ATOM, 32, 1, xcb_pixmap_t pix = xcb_generate_id(x11->xcb_conn);
&x11->atoms.wm_delete_window); xcb_create_pixmap(x11->xcb_conn, 1, pix, x11->screen->root, 1, 1);
char title[32]; x11->cursor = xcb_generate_id(x11->xcb_conn);
if (snprintf(title, sizeof(title), "wlroots - %s", output->wlr_output.name)) { xcb_create_cursor(x11->xcb_conn, x11->cursor, pix, pix, 0, 0, 0, 0, 0, 0,
xcb_change_property(x11->xcb_conn, XCB_PROP_MODE_REPLACE, output->win, 0, 0);
x11->atoms.net_wm_name, x11->atoms.utf8_string, 8, xcb_free_pixmap(x11->xcb_conn, pix);
strlen(title), title);
#ifdef WLR_HAS_XCB_XKB
const xcb_query_extension_reply_t *reply =
xcb_get_extension_data(x11->xcb_conn, &xcb_xkb_id);
if (reply != NULL && reply->present) {
x11->xkb_base_event = reply->first_event;
x11->xkb_base_error = reply->first_error;
xcb_xkb_use_extension_cookie_t cookie = xcb_xkb_use_extension(
x11->xcb_conn, XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION);
xcb_xkb_use_extension_reply_t *reply =
xcb_xkb_use_extension_reply(x11->xcb_conn, cookie, NULL);
if (reply != NULL && reply->supported) {
x11->xkb_supported = true;
xcb_xkb_select_events(x11->xcb_conn,
XCB_XKB_ID_USE_CORE_KBD,
XCB_XKB_EVENT_TYPE_STATE_NOTIFY,
0,
XCB_XKB_EVENT_TYPE_STATE_NOTIFY,
0,
0,
0);
} }
}
#endif
xcb_map_window(x11->xcb_conn, output->win);
xcb_flush(x11->xcb_conn);
wlr_output_update_enabled(&output->wlr_output, true);
wlr_signal_emit_safe(&x11->backend.events.new_output, output);
wlr_signal_emit_safe(&x11->backend.events.new_input, &x11->keyboard_dev); wlr_signal_emit_safe(&x11->backend.events.new_input, &x11->keyboard_dev);
wlr_signal_emit_safe(&x11->backend.events.new_input, &x11->pointer_dev); wlr_signal_emit_safe(&x11->backend.events.new_input, &x11->pointer_dev);
wl_event_source_timer_update(x11->frame_timer, 16); for (size_t i = 0; i < x11->requested_outputs; ++i) {
wlr_x11_output_create(&x11->backend);
}
return true; return true;
} }
@ -302,8 +223,10 @@ static void wlr_x11_backend_destroy(struct wlr_backend *backend) {
struct wlr_x11_backend *x11 = (struct wlr_x11_backend *)backend; struct wlr_x11_backend *x11 = (struct wlr_x11_backend *)backend;
struct wlr_x11_output *output = &x11->output; struct wlr_x11_output *output, *tmp;
wl_list_for_each_safe(output, tmp, &x11->outputs, link) {
wlr_output_destroy(&output->wlr_output); wlr_output_destroy(&output->wlr_output);
}
wlr_signal_emit_safe(&x11->pointer_dev.events.destroy, &x11->pointer_dev); wlr_signal_emit_safe(&x11->pointer_dev.events.destroy, &x11->pointer_dev);
wlr_signal_emit_safe(&x11->keyboard_dev.events.destroy, &x11->keyboard_dev); wlr_signal_emit_safe(&x11->keyboard_dev.events.destroy, &x11->keyboard_dev);
@ -323,9 +246,11 @@ static void wlr_x11_backend_destroy(struct wlr_backend *backend) {
} }
wl_list_remove(&x11->display_destroy.link); wl_list_remove(&x11->display_destroy.link);
wl_event_source_remove(x11->frame_timer);
wlr_egl_finish(&x11->egl); wlr_egl_finish(&x11->egl);
if (x11->cursor) {
xcb_free_cursor(x11->xcb_conn, x11->cursor);
}
if (x11->xlib_conn) { if (x11->xlib_conn) {
XCloseDisplay(x11->xlib_conn); XCloseDisplay(x11->xlib_conn);
} }
@ -369,6 +294,7 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
wlr_backend_init(&x11->backend, &backend_impl); wlr_backend_init(&x11->backend, &backend_impl);
x11->wl_display = display; x11->wl_display = display;
wl_list_init(&x11->outputs);
x11->xlib_conn = XOpenDisplay(x11_display); x11->xlib_conn = XOpenDisplay(x11_display);
if (!x11->xlib_conn) { if (!x11->xlib_conn) {
@ -393,8 +319,6 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
goto error_x11; goto error_x11;
} }
x11->frame_timer = wl_event_loop_add_timer(ev, signal_frame, x11);
x11->screen = xcb_setup_roots_iterator(xcb_get_setup(x11->xcb_conn)).data; x11->screen = xcb_setup_roots_iterator(xcb_get_setup(x11->xcb_conn)).data;
if (!wlr_egl_init(&x11->egl, EGL_PLATFORM_X11_KHR, x11->xlib_conn, NULL, if (!wlr_egl_init(&x11->egl, EGL_PLATFORM_X11_KHR, x11->xlib_conn, NULL,
@ -402,9 +326,10 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
goto error_event; goto error_event;
} }
x11->renderer = wlr_gles2_renderer_create(&x11->backend); x11->renderer = wlr_gles2_renderer_create(&x11->egl);
if (x11->renderer == NULL) { if (x11->renderer == NULL) {
wlr_log(L_ERROR, "Failed to create renderer"); wlr_log(L_ERROR, "Failed to create renderer");
goto error_egl;
} }
wlr_input_device_init(&x11->keyboard_dev, WLR_INPUT_DEVICE_KEYBOARD, wlr_input_device_init(&x11->keyboard_dev, WLR_INPUT_DEVICE_KEYBOARD,
@ -422,6 +347,8 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
return &x11->backend; return &x11->backend;
error_egl:
wlr_egl_finish(&x11->egl);
error_event: error_event:
wl_event_source_remove(x11->event_source); wl_event_source_remove(x11->event_source);
error_x11: error_x11:
@ -429,59 +356,3 @@ error_x11:
free(x11); free(x11);
return NULL; return NULL;
} }
static bool output_set_custom_mode(struct wlr_output *wlr_output, int32_t width,
int32_t height, int32_t refresh) {
struct wlr_x11_output *output = (struct wlr_x11_output *)wlr_output;
struct wlr_x11_backend *x11 = output->x11;
const uint32_t values[] = { width, height };
xcb_configure_window(x11->xcb_conn, output->win,
XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, values);
return true;
}
static void output_transform(struct wlr_output *wlr_output, enum wl_output_transform transform) {
struct wlr_x11_output *output = (struct wlr_x11_output *)wlr_output;
output->wlr_output.transform = transform;
}
static void output_destroy(struct wlr_output *wlr_output) {
struct wlr_x11_output *output = (struct wlr_x11_output *)wlr_output;
struct wlr_x11_backend *x11 = output->x11;
eglDestroySurface(x11->egl.display, output->surf);
xcb_destroy_window(x11->xcb_conn, output->win);
// output has been allocated on the stack, do not free it
}
static bool output_make_current(struct wlr_output *wlr_output, int *buffer_age) {
struct wlr_x11_output *output = (struct wlr_x11_output *)wlr_output;
struct wlr_x11_backend *x11 = output->x11;
return wlr_egl_make_current(&x11->egl, output->surf, buffer_age);
}
static bool output_swap_buffers(struct wlr_output *wlr_output,
pixman_region32_t *damage) {
struct wlr_x11_output *output = (struct wlr_x11_output *)wlr_output;
struct wlr_x11_backend *x11 = output->x11;
return wlr_egl_swap_buffers(&x11->egl, output->surf, damage);
}
static const struct wlr_output_impl output_impl = {
.set_custom_mode = output_set_custom_mode,
.transform = output_transform,
.destroy = output_destroy,
.make_current = output_make_current,
.swap_buffers = output_swap_buffers,
};
bool wlr_output_is_x11(struct wlr_output *wlr_output) {
return wlr_output->impl == &output_impl;
}
bool wlr_input_device_is_x11(struct wlr_input_device *wlr_dev) {
return wlr_dev->impl == &input_device_impl;
}

@ -0,0 +1,136 @@
#include <wlr/interfaces/wlr_input_device.h>
#include <wlr/interfaces/wlr_keyboard.h>
#include <wlr/interfaces/wlr_pointer.h>
#include <wlr/util/log.h>
#include <xcb/xcb.h>
#ifdef __linux__
#include <linux/input-event-codes.h>
#elif __FreeBSD__
#include <dev/evdev/input-event-codes.h>
#endif
#include "backend/x11.h"
#include "util/signal.h"
static uint32_t xcb_button_to_wl(uint32_t button) {
switch (button) {
case XCB_BUTTON_INDEX_1: return BTN_LEFT;
case XCB_BUTTON_INDEX_2: return BTN_MIDDLE;
case XCB_BUTTON_INDEX_3: return BTN_RIGHT;
// XXX: I'm not sure the scroll-wheel direction is right
case XCB_BUTTON_INDEX_4: return BTN_GEAR_UP;
case XCB_BUTTON_INDEX_5: return BTN_GEAR_DOWN;
default: return 0;
}
}
bool x11_handle_input_event(struct wlr_x11_backend *x11,
xcb_generic_event_t *event) {
switch (event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK) {
case XCB_KEY_PRESS:
case XCB_KEY_RELEASE: {
xcb_key_press_event_t *ev = (xcb_key_press_event_t *)event;
struct wlr_event_keyboard_key key = {
.time_msec = ev->time,
.keycode = ev->detail - 8,
.state = event->response_type == XCB_KEY_PRESS ?
WLR_KEY_PRESSED : WLR_KEY_RELEASED,
.update_state = true,
};
// TODO use xcb-xkb for more precise modifiers state?
wlr_keyboard_notify_key(&x11->keyboard, &key);
x11->time = ev->time;
return true;
}
case XCB_BUTTON_PRESS: {
xcb_button_press_event_t *ev = (xcb_button_press_event_t *)event;
if (ev->detail == XCB_BUTTON_INDEX_4 ||
ev->detail == XCB_BUTTON_INDEX_5) {
double delta = (ev->detail == XCB_BUTTON_INDEX_4 ? -15 : 15);
struct wlr_event_pointer_axis axis = {
.device = &x11->pointer_dev,
.time_msec = ev->time,
.source = WLR_AXIS_SOURCE_WHEEL,
.orientation = WLR_AXIS_ORIENTATION_VERTICAL,
.delta = delta,
};
wlr_signal_emit_safe(&x11->pointer.events.axis, &axis);
x11->time = ev->time;
break;
}
}
/* fallthrough */
case XCB_BUTTON_RELEASE: {
xcb_button_press_event_t *ev = (xcb_button_press_event_t *)event;
if (ev->detail != XCB_BUTTON_INDEX_4 &&
ev->detail != XCB_BUTTON_INDEX_5) {
struct wlr_event_pointer_button button = {
.device = &x11->pointer_dev,
.time_msec = ev->time,
.button = xcb_button_to_wl(ev->detail),
.state = event->response_type == XCB_BUTTON_PRESS ?
WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED,
};
wlr_signal_emit_safe(&x11->pointer.events.button, &button);
}
x11->time = ev->time;
return true;
}
case XCB_MOTION_NOTIFY: {
xcb_motion_notify_event_t *ev = (xcb_motion_notify_event_t *)event;
struct wlr_x11_output *output =
x11_output_from_window_id(x11, ev->event);
if (output == NULL) {
return false;
}
struct wlr_output *wlr_output = &output->wlr_output;
struct wlr_box box = { .x = ev->event_x, .y = ev->event_y };
wlr_box_transform(&box, wlr_output->transform, wlr_output->width,
wlr_output->height, &box);
box.x /= wlr_output->scale;
box.y /= wlr_output->scale;
struct wlr_box layout_box;
x11_output_layout_get_box(x11, &layout_box);
double ox = wlr_output->lx / (double)layout_box.width;
double oy = wlr_output->ly / (double)layout_box.height;
struct wlr_event_pointer_motion_absolute wlr_event = {
.device = &x11->pointer_dev,
.time_msec = ev->time,
.x = box.x / (double)layout_box.width + ox,
.y = box.y / (double)layout_box.height + oy,
};
wlr_signal_emit_safe(&x11->pointer.events.motion_absolute, &wlr_event);
x11->time = ev->time;
return true;
}
default:
#ifdef WLR_HAS_XCB_XKB
if (x11->xkb_supported && event->response_type == x11->xkb_base_event) {
xcb_xkb_state_notify_event_t *ev =
(xcb_xkb_state_notify_event_t *)event;
wlr_keyboard_notify_modifiers(&x11->keyboard, ev->baseMods,
ev->latchedMods, ev->lockedMods, ev->lockedGroup);
return true;
}
#endif
break;
}
return false;
}
const struct wlr_input_device_impl input_device_impl = { 0 };
bool wlr_input_device_is_x11(struct wlr_input_device *wlr_dev) {
return wlr_dev->impl == &input_device_impl;
}

@ -0,0 +1,187 @@
#include <assert.h>
#include <stdlib.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/util/log.h>
#include "backend/x11.h"
#include "util/signal.h"
static int signal_frame(void *data) {
struct wlr_x11_output *output = data;
wlr_output_send_frame(&output->wlr_output);
wl_event_source_timer_update(output->frame_timer, output->frame_delay);
return 0;
}
static void parse_xcb_setup(struct wlr_output *output, xcb_connection_t *xcb_conn) {
const xcb_setup_t *xcb_setup = xcb_get_setup(xcb_conn);
snprintf(output->make, sizeof(output->make), "%.*s",
xcb_setup_vendor_length(xcb_setup),
xcb_setup_vendor(xcb_setup));
snprintf(output->model, sizeof(output->model), "%"PRIu16".%"PRIu16,
xcb_setup->protocol_major_version,
xcb_setup->protocol_minor_version);
}
static bool output_set_custom_mode(struct wlr_output *wlr_output, int32_t width,
int32_t height, int32_t refresh) {
struct wlr_x11_output *output = (struct wlr_x11_output *)wlr_output;
struct wlr_x11_backend *x11 = output->x11;
wlr_output_update_custom_mode(&output->wlr_output, wlr_output->width,
wlr_output->height, refresh);
output->frame_delay = 1000000 / refresh;
const uint32_t values[] = { width, height };
xcb_configure_window(x11->xcb_conn, output->win,
XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, values);
return true;
}
static void output_transform(struct wlr_output *wlr_output,
enum wl_output_transform transform) {
struct wlr_x11_output *output = (struct wlr_x11_output *)wlr_output;
output->wlr_output.transform = transform;
}
static void output_destroy(struct wlr_output *wlr_output) {
struct wlr_x11_output *output = (struct wlr_x11_output *)wlr_output;
struct wlr_x11_backend *x11 = output->x11;
wl_list_remove(&output->link);
wl_event_source_remove(output->frame_timer);
eglDestroySurface(x11->egl.display, output->surf);
xcb_destroy_window(x11->xcb_conn, output->win);
xcb_flush(x11->xcb_conn);
free(output);
}
static bool output_make_current(struct wlr_output *wlr_output, int *buffer_age) {
struct wlr_x11_output *output = (struct wlr_x11_output *)wlr_output;
struct wlr_x11_backend *x11 = output->x11;
return wlr_egl_make_current(&x11->egl, output->surf, buffer_age);
}
static bool output_swap_buffers(struct wlr_output *wlr_output,
pixman_region32_t *damage) {
struct wlr_x11_output *output = (struct wlr_x11_output *)wlr_output;
struct wlr_x11_backend *x11 = output->x11;
return wlr_egl_swap_buffers(&x11->egl, output->surf, damage);
}
static const struct wlr_output_impl output_impl = {
.set_custom_mode = output_set_custom_mode,
.transform = output_transform,
.destroy = output_destroy,
.make_current = output_make_current,
.swap_buffers = output_swap_buffers,
};
struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
assert(wlr_backend_is_x11(backend));
struct wlr_x11_backend *x11 = (struct wlr_x11_backend *)backend;
if (!x11->started) {
++x11->requested_outputs;
return NULL;
}
struct wlr_x11_output *output = calloc(1, sizeof(struct wlr_x11_output));
if (output == NULL) {
return NULL;
}
output->x11 = x11;
struct wlr_output *wlr_output = &output->wlr_output;
wlr_output_init(wlr_output, &x11->backend, &output_impl, x11->wl_display);
wlr_output->refresh = 60 * 1000000;
output->frame_delay = 16; // 60 Hz
snprintf(wlr_output->name, sizeof(wlr_output->name), "X11-%d",
wl_list_length(&x11->outputs) + 1);
parse_xcb_setup(wlr_output, x11->xcb_conn);
uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
uint32_t values[2] = {
x11->screen->white_pixel,
XCB_EVENT_MASK_EXPOSURE |
XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE |
XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE |
XCB_EVENT_MASK_POINTER_MOTION |
XCB_EVENT_MASK_STRUCTURE_NOTIFY
};
output->win = xcb_generate_id(x11->xcb_conn);
xcb_create_window(x11->xcb_conn, XCB_COPY_FROM_PARENT, output->win,
x11->screen->root, 0, 0, 1024, 768, 1, XCB_WINDOW_CLASS_INPUT_OUTPUT,
x11->screen->root_visual, mask, values);
output->surf = wlr_egl_create_surface(&x11->egl, &output->win);
if (!output->surf) {
wlr_log(L_ERROR, "Failed to create EGL surface");
free(output);
return NULL;
}
xcb_change_property(x11->xcb_conn, XCB_PROP_MODE_REPLACE, output->win,
x11->atoms.wm_protocols, XCB_ATOM_ATOM, 32, 1,
&x11->atoms.wm_delete_window);
char title[32];
if (snprintf(title, sizeof(title), "wlroots - %s", wlr_output->name)) {
xcb_change_property(x11->xcb_conn, XCB_PROP_MODE_REPLACE, output->win,
x11->atoms.net_wm_name, x11->atoms.utf8_string, 8,
strlen(title), title);
}
uint32_t cursor_values[] = { x11->cursor };
xcb_change_window_attributes(x11->xcb_conn, output->win, XCB_CW_CURSOR,
cursor_values);
xcb_map_window(x11->xcb_conn, output->win);
xcb_flush(x11->xcb_conn);
struct wl_event_loop *ev = wl_display_get_event_loop(x11->wl_display);
output->frame_timer = wl_event_loop_add_timer(ev, signal_frame, output);
wl_event_source_timer_update(output->frame_timer, output->frame_delay);
wlr_output_update_enabled(wlr_output, true);
wl_list_insert(&x11->outputs, &output->link);
wlr_signal_emit_safe(&x11->backend.events.new_output, wlr_output);
return wlr_output;
}
void x11_output_handle_configure_notify(struct wlr_x11_output *output,
xcb_configure_notify_event_t *ev) {
struct wlr_x11_backend *x11 = output->x11;
wlr_output_update_custom_mode(&output->wlr_output, ev->width,
ev->height, output->wlr_output.refresh);
// Move the pointer to its new location
xcb_query_pointer_cookie_t cookie =
xcb_query_pointer(x11->xcb_conn, output->win);
xcb_query_pointer_reply_t *pointer =
xcb_query_pointer_reply(x11->xcb_conn, cookie, NULL);
if (!pointer) {
return;
}
struct wlr_event_pointer_motion_absolute abs = {
.device = &x11->pointer_dev,
.time_msec = x11->time,
.x = (double)pointer->root_x / output->wlr_output.width,
.y = (double)pointer->root_y / output->wlr_output.height,
};
wlr_signal_emit_safe(&x11->pointer.events.motion_absolute, &abs);
free(pointer);
}
bool wlr_output_is_x11(struct wlr_output *wlr_output) {
return wlr_output->impl == &output_impl;
}

@ -0,0 +1,430 @@
#define _POSIX_C_SOURCE 199309L
#include <assert.h>
#include <GLES2/gl2.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <wayland-client.h>
#include <wayland-cursor.h>
#include <wayland-egl.h>
#include <wlr/render/egl.h>
#include <wlr/util/log.h>
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
static struct wl_compositor *compositor = NULL;
static struct wl_seat *seat = NULL;
static struct wl_shm *shm = NULL;
static struct wl_pointer *pointer = NULL;
//static struct wl_keyboard *keyboard = NULL;
static struct zwlr_layer_shell_v1 *layer_shell = NULL;
struct zwlr_layer_surface_v1 *layer_surface;
static struct wl_output *wl_output = NULL;
struct wl_surface *wl_surface;
struct wlr_egl egl;
struct wl_egl_window *egl_window;
struct wlr_egl_surface *egl_surface;
struct wl_callback *frame_callback;
static uint32_t output = 0;
static uint32_t layer = ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND;
static uint32_t anchor = 0;
static uint32_t width = 256, height = 256;
static int32_t margin_top = 0;
static double alpha = 1.0;
static bool run_display = true;
static bool animate = false;
static double frame = 0;
static int cur_x = -1, cur_y = -1;
static int buttons = 0;
struct wl_cursor_theme *cursor_theme;
struct wl_cursor_image *cursor_image;
struct wl_surface *cursor_surface;
static struct {
struct timespec last_frame;
float color[3];
int dec;
} demo;
static void draw(void);
static void surface_frame_callback(
void *data, struct wl_callback *cb, uint32_t time) {
wl_callback_destroy(cb);
frame_callback = NULL;
draw();
}
static struct wl_callback_listener frame_listener = {
.done = surface_frame_callback
};
static void draw(void) {
eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context);
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
long ms = (ts.tv_sec - demo.last_frame.tv_sec) * 1000 +
(ts.tv_nsec - demo.last_frame.tv_nsec) / 1000000;
int inc = (demo.dec + 1) % 3;
if (!buttons) {
demo.color[inc] += ms / 2000.0f;
demo.color[demo.dec] -= ms / 2000.0f;
if (demo.color[demo.dec] < 0.0f) {
demo.color[inc] = 1.0f;
demo.color[demo.dec] = 0.0f;
demo.dec = inc;
}
}
if (animate) {
frame += ms / 50.0;
int32_t old_top = margin_top;
margin_top = -(20 - ((int)frame % 20));
if (old_top != margin_top) {
zwlr_layer_surface_v1_set_margin(layer_surface,
margin_top, 0, 0, 0);
wl_surface_commit(wl_surface);
}
}
glViewport(0, 0, width, height);
if (buttons) {
glClearColor(1, 1, 1, alpha);
} else {
glClearColor(demo.color[0], demo.color[1], demo.color[2], alpha);
}
glClear(GL_COLOR_BUFFER_BIT);
if (cur_x != -1 && cur_y != -1) {
glEnable(GL_SCISSOR_TEST);
glScissor(cur_x, height - cur_y, 5, 5);
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
glDisable(GL_SCISSOR_TEST);
}
frame_callback = wl_surface_frame(wl_surface);
wl_callback_add_listener(frame_callback, &frame_listener, NULL);
eglSwapBuffers(egl.display, egl_surface);
demo.last_frame = ts;
}
static void layer_surface_configure(void *data,
struct zwlr_layer_surface_v1 *surface,
uint32_t serial, uint32_t w, uint32_t h) {
width = w;
height = h;
if (egl_window) {
wl_egl_window_resize(egl_window, width, height, 0, 0);
}
zwlr_layer_surface_v1_ack_configure(surface, serial);
}
static void layer_surface_closed(void *data,
struct zwlr_layer_surface_v1 *surface) {
eglDestroySurface(egl.display, egl_surface);
wl_egl_window_destroy(egl_window);
zwlr_layer_surface_v1_destroy(surface);
wl_surface_destroy(wl_surface);
run_display = false;
}
struct zwlr_layer_surface_v1_listener layer_surface_listener = {
.configure = layer_surface_configure,
.closed = layer_surface_closed,
};
static void wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, struct wl_surface *surface,
wl_fixed_t surface_x, wl_fixed_t surface_y) {
wl_surface_attach(cursor_surface,
wl_cursor_image_get_buffer(cursor_image), 0, 0);
wl_pointer_set_cursor(wl_pointer, serial, cursor_surface,
cursor_image->hotspot_x, cursor_image->hotspot_y);
wl_surface_commit(cursor_surface);
}
static void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, struct wl_surface *surface) {
cur_x = cur_y = -1;
buttons = 0;
}
static void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {
cur_x = wl_fixed_to_int(surface_x);
cur_y = wl_fixed_to_int(surface_y);
}
static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, uint32_t time, uint32_t button, uint32_t state) {
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
buttons++;
} else {
buttons--;
}
}
static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
uint32_t time, uint32_t axis, wl_fixed_t value) {
// Who cares
}
static void wl_pointer_frame(void *data, struct wl_pointer *wl_pointer) {
// Who cares
}
static void wl_pointer_axis_source(void *data, struct wl_pointer *wl_pointer,
uint32_t axis_source) {
// Who cares
}
static void wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer,
uint32_t time, uint32_t axis) {
// Who cares
}
static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer,
uint32_t axis, int32_t discrete) {
// Who cares
}
struct wl_pointer_listener pointer_listener = {
.enter = wl_pointer_enter,
.leave = wl_pointer_leave,
.motion = wl_pointer_motion,
.button = wl_pointer_button,
.axis = wl_pointer_axis,
.frame = wl_pointer_frame,
.axis_source = wl_pointer_axis_source,
.axis_stop = wl_pointer_axis_stop,
.axis_discrete = wl_pointer_axis_discrete,
};
static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
enum wl_seat_capability caps) {
if ((caps & WL_SEAT_CAPABILITY_POINTER)) {
pointer = wl_seat_get_pointer(wl_seat);
wl_pointer_add_listener(pointer, &pointer_listener, NULL);
}
if ((caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
// TODO
}
}
static void seat_handle_name(void *data, struct wl_seat *wl_seat,
const char *name) {
// Who cares
}
const struct wl_seat_listener seat_listener = {
.capabilities = seat_handle_capabilities,
.name = seat_handle_name,
};
static void handle_global(void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version) {
if (strcmp(interface, "wl_compositor") == 0) {
compositor = wl_registry_bind(registry, name,
&wl_compositor_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, "wl_output") == 0) {
if (output == 0 && !wl_output) {
wl_output = wl_registry_bind(registry, name,
&wl_output_interface, 1);
} else {
output--;
}
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
seat = wl_registry_bind(registry, name,
&wl_seat_interface, 1);
wl_seat_add_listener(seat, &seat_listener, NULL);
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
layer_shell = wl_registry_bind(
registry, name, &zwlr_layer_shell_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,
};
int main(int argc, char **argv) {
wlr_log_init(L_DEBUG, NULL);
char *namespace = "wlroots";
int exclusive_zone = 0;
int32_t margin_right = 0, margin_bottom = 0, margin_left = 0;
bool found;
int c;
while ((c = getopt(argc, argv, "nw:h:o:l:a:x:m:t:")) != -1) {
switch (c) {
case 'o':
output = atoi(optarg);
break;
case 'w':
width = atoi(optarg);
break;
case 'h':
height = atoi(optarg);
break;
case 'x':
exclusive_zone = atoi(optarg);
break;
case 'l': {
struct {
char *name;
enum zwlr_layer_shell_v1_layer value;
} layers[] = {
{ "background", ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND },
{ "bottom", ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM },
{ "top", ZWLR_LAYER_SHELL_V1_LAYER_TOP },
{ "overlay", ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY },
};
found = false;
for (size_t i = 0; i < sizeof(layers) / sizeof(layers[0]); ++i) {
if (strcmp(optarg, layers[i].name) == 0) {
layer = layers[i].value;
found = true;
break;
}
}
if (!found) {
fprintf(stderr, "invalid layer %s\n", optarg);
return 1;
}
break;
}
case 'a': {
struct {
char *name;
uint32_t value;
} anchors[] = {
{ "top", ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP },
{ "bottom", ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM },
{ "left", ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT },
{ "right", ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT },
};
found = false;
for (size_t i = 0; i < sizeof(anchors) / sizeof(anchors[0]); ++i) {
if (strcmp(optarg, anchors[i].name) == 0) {
anchor |= anchors[i].value;
found = true;
break;
}
}
if (!found) {
fprintf(stderr, "invalid anchor %s\n", optarg);
return 1;
}
break;
}
case 't':
alpha = atof(optarg);
break;
case 'm': {
char *endptr = optarg;
margin_top = strtol(endptr, &endptr, 10);
assert(*endptr == ',');
margin_right = strtol(endptr + 1, &endptr, 10);
assert(*endptr == ',');
margin_bottom = strtol(endptr + 1, &endptr, 10);
assert(*endptr == ',');
margin_left = strtol(endptr + 1, &endptr, 10);
assert(!*endptr);
break;
}
case 'n':
animate = true;
break;
default:
break;
}
}
struct wl_display *display = wl_display_connect(NULL);
if (display == NULL) {
fprintf(stderr, "Failed to create display\n");
return 1;
}
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_roundtrip(display);
if (compositor == NULL) {
fprintf(stderr, "wl_compositor not available\n");
return 1;
}
if (shm == NULL) {
fprintf(stderr, "wl_shm not available\n");
return 1;
}
if (layer_shell == NULL) {
fprintf(stderr, "layer_shell not available\n");
return 1;
}
if (wl_output == NULL) {
fprintf(stderr, "wl_output not available\n");
return 1;
}
assert(cursor_theme = wl_cursor_theme_load(NULL, 16, shm));
struct wl_cursor *cursor;
assert(cursor = wl_cursor_theme_get_cursor(cursor_theme, "crosshair"));
cursor_image = cursor->images[0];
assert(cursor_surface = wl_compositor_create_surface(compositor));
EGLint attribs[] = { EGL_ALPHA_SIZE, 8, EGL_NONE };
wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display,
attribs, WL_SHM_FORMAT_ARGB8888);
wl_surface = wl_compositor_create_surface(compositor);
assert(wl_surface);
layer_surface = zwlr_layer_shell_v1_get_layer_surface(layer_shell,
wl_surface, wl_output, layer, namespace);
assert(layer_surface);
zwlr_layer_surface_v1_set_size(layer_surface, width, height);
zwlr_layer_surface_v1_set_anchor(layer_surface, anchor);
zwlr_layer_surface_v1_set_exclusive_zone(layer_surface, exclusive_zone);
zwlr_layer_surface_v1_set_margin(layer_surface,
margin_top, margin_right, margin_bottom, margin_left);
zwlr_layer_surface_v1_add_listener(layer_surface,
&layer_surface_listener, layer_surface);
// TODO: interactivity
wl_surface_commit(wl_surface);
wl_display_roundtrip(display);
egl_window = wl_egl_window_create(wl_surface, width, height);
assert(egl_window);
egl_surface = wlr_egl_create_surface(&egl, egl_window);
assert(egl_surface);
wl_display_roundtrip(display);
draw();
while (wl_display_dispatch(display) != -1 && run_display) {
// This space intentionally left blank
}
wl_cursor_theme_destroy(cursor_theme);
return 0;
}

@ -6,6 +6,7 @@ lib_shared = static_library(
) )
threads = dependency('threads') threads = dependency('threads')
wayland_cursor = dependency('wayland-cursor')
executable('simple', 'simple.c', dependencies: wlroots, link_with: lib_shared) executable('simple', 'simple.c', dependencies: wlroots, link_with: lib_shared)
executable('pointer', 'pointer.c', dependencies: wlroots, link_with: lib_shared) executable('pointer', 'pointer.c', dependencies: wlroots, link_with: lib_shared)
@ -48,3 +49,9 @@ executable(
dependencies: [wayland_client, wlr_protos, wlroots, threads], dependencies: [wayland_client, wlr_protos, wlroots, threads],
link_with: lib_shared, link_with: lib_shared,
) )
executable(
'layer-shell',
'layer-shell.c',
dependencies: [wayland_cursor, wayland_client, wlr_protos, wlroots]
)

@ -118,8 +118,7 @@ static void handle_cursor_motion_absolute(struct wl_listener *listener,
struct sample_cursor *cursor = struct sample_cursor *cursor =
wl_container_of(listener, cursor, cursor_motion_absolute); wl_container_of(listener, cursor, cursor_motion_absolute);
struct wlr_event_pointer_motion_absolute *event = data; struct wlr_event_pointer_motion_absolute *event = data;
wlr_cursor_warp_absolute(cursor->cursor, event->device, wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, event->y);
event->x_mm / event->width_mm, event->y_mm / event->height_mm);
} }
static void handle_input_add(struct compositor_state *state, static void handle_input_add(struct compositor_state *state,
@ -149,7 +148,7 @@ static void handle_input_add(struct compositor_state *state,
sample->compositor); sample->compositor);
struct wlr_xcursor_image *image = sample->xcursor->images[0]; struct wlr_xcursor_image *image = sample->xcursor->images[0];
wlr_cursor_set_image(cursor->cursor, image->buffer, image->width, wlr_cursor_set_image(cursor->cursor, image->buffer, image->width * 4,
image->width, image->height, image->hotspot_x, image->hotspot_y, 0); image->width, image->height, image->hotspot_x, image->hotspot_y, 0);
wl_list_insert(&sample->cursors, &cursor->link); wl_list_insert(&sample->cursors, &cursor->link);

@ -13,7 +13,6 @@
#include <wayland-server.h> #include <wayland-server.h>
#include <wlr/backend.h> #include <wlr/backend.h>
#include <wlr/backend/session.h> #include <wlr/backend/session.h>
#include <wlr/render/gles2.h>
#include <wlr/render/wlr_renderer.h> #include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_keyboard.h> #include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_matrix.h> #include <wlr/types/wlr_matrix.h>
@ -196,10 +195,10 @@ int main(int argc, char *argv[]) {
compositor.keyboard_key_cb = handle_keyboard_key; compositor.keyboard_key_cb = handle_keyboard_key;
compositor_init(&compositor); compositor_init(&compositor);
state.renderer = wlr_gles2_renderer_create(compositor.backend); state.renderer = wlr_backend_get_renderer(compositor.backend);
state.cat_texture = wlr_render_texture_create(state.renderer); state.cat_texture = wlr_texture_from_pixels(state.renderer,
wlr_texture_upload_pixels(state.cat_texture, WL_SHM_FORMAT_ABGR8888, WL_SHM_FORMAT_ABGR8888, cat_tex.width * 4, cat_tex.width, cat_tex.height,
cat_tex.width, cat_tex.width, cat_tex.height, cat_tex.pixel_data); cat_tex.pixel_data);
if (!wlr_backend_start(compositor.backend)) { if (!wlr_backend_start(compositor.backend)) {
wlr_log(L_ERROR, "Failed to start backend"); wlr_log(L_ERROR, "Failed to start backend");

@ -112,7 +112,7 @@ static void handle_output_add(struct output_state *ostate) {
sample->compositor); sample->compositor);
struct wlr_xcursor_image *image = sample->xcursor->images[0]; struct wlr_xcursor_image *image = sample->xcursor->images[0];
wlr_cursor_set_image(sample->cursor, image->buffer, image->width, wlr_cursor_set_image(sample->cursor, image->buffer, image->width * 4,
image->width, image->height, image->hotspot_x, image->hotspot_y, 0); image->width, image->height, image->hotspot_x, image->hotspot_y, 0);
wlr_cursor_warp(sample->cursor, NULL, sample->cursor->x, sample->cursor->y); wlr_cursor_warp(sample->cursor, NULL, sample->cursor->x, sample->cursor->y);
@ -154,8 +154,8 @@ static void handle_cursor_motion_absolute(struct wl_listener *listener,
wl_container_of(listener, sample, cursor_motion_absolute); wl_container_of(listener, sample, cursor_motion_absolute);
struct wlr_event_pointer_motion_absolute *event = data; struct wlr_event_pointer_motion_absolute *event = data;
sample->cur_x = event->x_mm; sample->cur_x = event->x;
sample->cur_y = event->y_mm; sample->cur_y = event->y;
wlr_cursor_warp_absolute(sample->cursor, event->device, sample->cur_x, wlr_cursor_warp_absolute(sample->cursor, event->device, sample->cur_x,
sample->cur_y); sample->cur_y);
@ -217,8 +217,8 @@ static void handle_touch_down(struct wl_listener *listener, void *data) {
struct wlr_event_touch_down *event = data; struct wlr_event_touch_down *event = data;
struct touch_point *point = calloc(1, sizeof(struct touch_point)); struct touch_point *point = calloc(1, sizeof(struct touch_point));
point->touch_id = event->touch_id; point->touch_id = event->touch_id;
point->x = event->x_mm / event->width_mm; point->x = event->x;
point->y = event->y_mm / event->height_mm; point->y = event->y;
wl_list_insert(&sample->touch_points, &point->link); wl_list_insert(&sample->touch_points, &point->link);
warp_to_touch(sample, event->device); warp_to_touch(sample, event->device);
@ -232,8 +232,8 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) {
struct touch_point *point; struct touch_point *point;
wl_list_for_each(point, &sample->touch_points, link) { wl_list_for_each(point, &sample->touch_points, link) {
if (point->touch_id == event->touch_id) { if (point->touch_id == event->touch_id) {
point->x = event->x_mm / event->width_mm; point->x = event->x;
point->y = event->y_mm / event->height_mm; point->y = event->y;
break; break;
} }
} }
@ -251,8 +251,8 @@ static void handle_tablet_tool_axis(struct wl_listener *listener, void *data) {
struct wlr_event_tablet_tool_axis *event = data; struct wlr_event_tablet_tool_axis *event = data;
if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X) && if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X) &&
(event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) { (event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) {
wlr_cursor_warp_absolute(sample->cursor, event->device, wlr_cursor_warp_absolute(sample->cursor,
event->x_mm / event->width_mm, event->y_mm / event->height_mm); event->device, event->x, event->y);
} }
} }
@ -324,7 +324,7 @@ int main(int argc, char *argv[]) {
} }
struct wlr_xcursor_image *image = state.xcursor->images[0]; struct wlr_xcursor_image *image = state.xcursor->images[0];
wlr_cursor_set_image(state.cursor, image->buffer, image->width, wlr_cursor_set_image(state.cursor, image->buffer, image->width * 4,
image->width, image->height, image->hotspot_x, image->hotspot_y, 0); image->width, image->height, image->hotspot_x, image->hotspot_y, 0);
compositor_init(&compositor); compositor_init(&compositor);

@ -13,7 +13,6 @@
#include <wlr/backend.h> #include <wlr/backend.h>
#include <wlr/backend/session.h> #include <wlr/backend/session.h>
#include <wlr/render/wlr_renderer.h> #include <wlr/render/wlr_renderer.h>
#include <wlr/render/gles2.h>
#include <wlr/types/wlr_keyboard.h> #include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_matrix.h> #include <wlr/types/wlr_matrix.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
@ -137,18 +136,18 @@ int main(int argc, char *argv[]) {
compositor.keyboard_key_cb = handle_keyboard_key; compositor.keyboard_key_cb = handle_keyboard_key;
compositor_init(&compositor); compositor_init(&compositor);
state.renderer = wlr_gles2_renderer_create(compositor.backend); state.renderer = wlr_backend_get_renderer(compositor.backend);
if (!state.renderer) { if (!state.renderer) {
wlr_log(L_ERROR, "Could not start compositor, OOM"); wlr_log(L_ERROR, "Could not start compositor, OOM");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
state.cat_texture = wlr_render_texture_create(state.renderer); state.cat_texture = wlr_texture_from_pixels(state.renderer,
WL_SHM_FORMAT_ABGR8888, cat_tex.width * 4, cat_tex.width, cat_tex.height,
cat_tex.pixel_data);
if (!state.cat_texture) { if (!state.cat_texture) {
wlr_log(L_ERROR, "Could not start compositor, OOM"); wlr_log(L_ERROR, "Could not start compositor, OOM");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
wlr_texture_upload_pixels(state.cat_texture, WL_SHM_FORMAT_ABGR8888,
cat_tex.width, cat_tex.width, cat_tex.height, cat_tex.pixel_data);
if (!wlr_backend_start(compositor.backend)) { if (!wlr_backend_start(compositor.backend)) {
wlr_log(L_ERROR, "Failed to start backend"); wlr_log(L_ERROR, "Failed to start backend");

@ -108,8 +108,8 @@ static void pointer_motion_absolute_notify(struct wl_listener *listener, void *d
struct wlr_event_pointer_motion_absolute *event = data; struct wlr_event_pointer_motion_absolute *event = data;
struct pointer_state *pstate = wl_container_of(listener, pstate, motion_absolute); struct pointer_state *pstate = wl_container_of(listener, pstate, motion_absolute);
if (pstate->compositor->pointer_motion_absolute_cb) { if (pstate->compositor->pointer_motion_absolute_cb) {
pstate->compositor->pointer_motion_absolute_cb(pstate, pstate->compositor->pointer_motion_absolute_cb(
event->x_mm, event->y_mm); pstate, event->x, event->y);
} }
} }
@ -167,8 +167,8 @@ static void touch_down_notify(struct wl_listener *listener, void *data) {
struct wlr_event_touch_down *event = data; struct wlr_event_touch_down *event = data;
struct touch_state *tstate = wl_container_of(listener, tstate, down); struct touch_state *tstate = wl_container_of(listener, tstate, down);
if (tstate->compositor->touch_down_cb) { if (tstate->compositor->touch_down_cb) {
tstate->compositor->touch_down_cb(tstate, event->touch_id, tstate->compositor->touch_down_cb(tstate,
event->x_mm, event->y_mm, event->width_mm, event->height_mm); event->touch_id, event->x, event->y);
} }
} }
@ -176,8 +176,8 @@ static void touch_motion_notify(struct wl_listener *listener, void *data) {
struct wlr_event_touch_motion *event = data; struct wlr_event_touch_motion *event = data;
struct touch_state *tstate = wl_container_of(listener, tstate, motion); struct touch_state *tstate = wl_container_of(listener, tstate, motion);
if (tstate->compositor->touch_motion_cb) { if (tstate->compositor->touch_motion_cb) {
tstate->compositor->touch_motion_cb(tstate, event->touch_id, tstate->compositor->touch_motion_cb(tstate,
event->x_mm, event->y_mm, event->width_mm, event->height_mm); event->touch_id, event->x, event->y);
} }
} }

@ -102,10 +102,10 @@ struct compositor_state {
enum wlr_axis_source source, enum wlr_axis_source source,
enum wlr_axis_orientation orientation, enum wlr_axis_orientation orientation,
double delta); double delta);
void (*touch_down_cb)(struct touch_state *s, int32_t touch_id, void (*touch_down_cb)(struct touch_state *s,
double x, double y, double width, double height); int32_t touch_id, double x, double y);
void (*touch_motion_cb)(struct touch_state *s, int32_t touch_id, void (*touch_motion_cb)(struct touch_state *s,
double x, double y, double width, double height); int32_t touch_id, double x, double y);
void (*touch_up_cb)(struct touch_state *s, int32_t touch_id); void (*touch_up_cb)(struct touch_state *s, int32_t touch_id);
void (*touch_cancel_cb)(struct touch_state *s, int32_t touch_id); void (*touch_cancel_cb)(struct touch_state *s, int32_t touch_id);
void (*tool_axis_cb)(struct tablet_tool_state *s, void (*tool_axis_cb)(struct tablet_tool_state *s,

@ -11,7 +11,6 @@
#include <wayland-server.h> #include <wayland-server.h>
#include <wlr/backend.h> #include <wlr/backend.h>
#include <wlr/backend/session.h> #include <wlr/backend/session.h>
#include <wlr/render/gles2.h>
#include <wlr/render/wlr_renderer.h> #include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_box.h> #include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_matrix.h> #include <wlr/types/wlr_matrix.h>
@ -28,7 +27,7 @@ struct sample_state {
bool proximity, tap, button; bool proximity, tap, button;
double distance; double distance;
double pressure; double pressure;
double x_mm, y_mm; double x, y;
double x_tilt, y_tilt; double x_tilt, y_tilt;
double width_mm, height_mm; double width_mm, height_mm;
double ring; double ring;
@ -69,8 +68,8 @@ static void handle_output_frame(struct output_state *output, struct timespec *ts
if (sample->proximity) { if (sample->proximity) {
struct wlr_box box = { struct wlr_box box = {
.x = sample->x_mm * scale - 8 * (sample->pressure + 1) + left, .x = (sample->x * pad_width) - 8 * (sample->pressure + 1) + left,
.y = sample->y_mm * scale - 8 * (sample->pressure + 1) + top, .y = (sample->y * pad_height) - 8 * (sample->pressure + 1) + top,
.width = 16 * (sample->pressure + 1), .width = 16 * (sample->pressure + 1),
.height = 16 * (sample->pressure + 1), .height = 16 * (sample->pressure + 1),
}; };
@ -94,13 +93,11 @@ static void handle_output_frame(struct output_state *output, struct timespec *ts
static void handle_tool_axis(struct tablet_tool_state *tstate, static void handle_tool_axis(struct tablet_tool_state *tstate,
struct wlr_event_tablet_tool_axis *event) { struct wlr_event_tablet_tool_axis *event) {
struct sample_state *sample = tstate->compositor->data; struct sample_state *sample = tstate->compositor->data;
sample->width_mm = event->width_mm;
sample->height_mm = event->height_mm;
if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X)) { if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X)) {
sample->x_mm = event->x_mm; sample->x = event->x;
} }
if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) { if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) {
sample->y_mm = event->y_mm; sample->y = event->y;
} }
if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_DISTANCE)) { if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_DISTANCE)) {
sample->distance = event->distance; sample->distance = event->distance;
@ -164,13 +161,24 @@ static void handle_pad_ring(struct tablet_pad_state *pstate,
} }
} }
static void handle_input_add(struct compositor_state *cstate,
struct wlr_input_device *inputdev) {
struct sample_state *sample = cstate->data;
if (inputdev->type == WLR_INPUT_DEVICE_TABLET_TOOL) {
sample->width_mm = inputdev->width_mm == 0 ?
20 : inputdev->width_mm;
sample->height_mm = inputdev->height_mm == 0 ?
10 : inputdev->height_mm;
}
}
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
wlr_log_init(L_DEBUG, NULL); wlr_log_init(L_DEBUG, NULL);
struct sample_state state = { struct sample_state state = {
.tool_color = { 1, 1, 1, 1 }, .tool_color = { 1, 1, 1, 1 },
.pad_color = { 0.5, 0.5, 0.5, 1.0 } .pad_color = { 0.5, 0.5, 0.5, 1.0 }
}; };
struct compositor_state compositor = { 0, struct compositor_state compositor = {
.data = &state, .data = &state,
.output_frame_cb = handle_output_frame, .output_frame_cb = handle_output_frame,
.tool_axis_cb = handle_tool_axis, .tool_axis_cb = handle_tool_axis,
@ -178,10 +186,12 @@ int main(int argc, char *argv[]) {
.tool_button_cb = handle_tool_button, .tool_button_cb = handle_tool_button,
.pad_button_cb = handle_pad_button, .pad_button_cb = handle_pad_button,
.pad_ring_cb = handle_pad_ring, .pad_ring_cb = handle_pad_ring,
.input_add_cb = handle_input_add,
0
}; };
compositor_init(&compositor); compositor_init(&compositor);
state.renderer = wlr_gles2_renderer_create(compositor.backend); state.renderer = wlr_backend_get_renderer(compositor.backend);
if (!state.renderer) { if (!state.renderer) {
wlr_log(L_ERROR, "Could not start compositor, OOM"); wlr_log(L_ERROR, "Could not start compositor, OOM");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);

@ -12,7 +12,6 @@
#include <wayland-server.h> #include <wayland-server.h>
#include <wlr/backend.h> #include <wlr/backend.h>
#include <wlr/backend/session.h> #include <wlr/backend/session.h>
#include <wlr/render/gles2.h>
#include <wlr/render/wlr_renderer.h> #include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_list.h> #include <wlr/types/wlr_list.h>
#include <wlr/types/wlr_matrix.h> #include <wlr/types/wlr_matrix.h>
@ -45,10 +44,13 @@ static void handle_output_frame(struct output_state *output, struct timespec *ts
wlr_renderer_begin(sample->renderer, wlr_output->width, wlr_output->height); wlr_renderer_begin(sample->renderer, wlr_output->width, wlr_output->height);
wlr_renderer_clear(sample->renderer, (float[]){0.25f, 0.25f, 0.25f, 1}); wlr_renderer_clear(sample->renderer, (float[]){0.25f, 0.25f, 0.25f, 1});
int tex_width, tex_height;
wlr_texture_get_size(sample->cat_texture, &tex_width, &tex_height);
struct touch_point *p; struct touch_point *p;
wl_list_for_each(p, &sample->touch_points, link) { wl_list_for_each(p, &sample->touch_points, link) {
int x = (int)(p->x * width) - sample->cat_texture->width / 2; int x = (int)(p->x * width) - tex_width / 2;
int y = (int)(p->y * height) - sample->cat_texture->height / 2; int y = (int)(p->y * height) - tex_height / 2;
wlr_render_texture(sample->renderer, sample->cat_texture, wlr_render_texture(sample->renderer, sample->cat_texture,
wlr_output->transform_matrix, x, y, 1.0f); wlr_output->transform_matrix, x, y, 1.0f);
} }
@ -57,13 +59,13 @@ static void handle_output_frame(struct output_state *output, struct timespec *ts
wlr_output_swap_buffers(wlr_output, NULL, NULL); wlr_output_swap_buffers(wlr_output, NULL, NULL);
} }
static void handle_touch_down(struct touch_state *tstate, int32_t touch_id, static void handle_touch_down(struct touch_state *tstate,
double x, double y, double width, double height) { int32_t touch_id, double x, double y) {
struct sample_state *sample = tstate->compositor->data; struct sample_state *sample = tstate->compositor->data;
struct touch_point *point = calloc(1, sizeof(struct touch_point)); struct touch_point *point = calloc(1, sizeof(struct touch_point));
point->touch_id = touch_id; point->touch_id = touch_id;
point->x = x / width; point->x = x;
point->y = y / height; point->y = y;
wl_list_insert(&sample->touch_points, &point->link); wl_list_insert(&sample->touch_points, &point->link);
} }
@ -78,14 +80,14 @@ static void handle_touch_up(struct touch_state *tstate, int32_t touch_id) {
} }
} }
static void handle_touch_motion(struct touch_state *tstate, int32_t touch_id, static void handle_touch_motion(struct touch_state *tstate,
double x, double y, double width, double height) { int32_t touch_id, double x, double y) {
struct sample_state *sample = tstate->compositor->data; struct sample_state *sample = tstate->compositor->data;
struct touch_point *point; struct touch_point *point;
wl_list_for_each(point, &sample->touch_points, link) { wl_list_for_each(point, &sample->touch_points, link) {
if (point->touch_id == touch_id) { if (point->touch_id == touch_id) {
point->x = x / width; point->x = x;
point->y = y / height; point->y = y;
break; break;
} }
} }
@ -105,18 +107,18 @@ int main(int argc, char *argv[]) {
}; };
compositor_init(&compositor); compositor_init(&compositor);
state.renderer = wlr_gles2_renderer_create(compositor.backend); state.renderer = wlr_backend_get_renderer(compositor.backend);
if (!state.renderer) { if (!state.renderer) {
wlr_log(L_ERROR, "Could not start compositor, OOM"); wlr_log(L_ERROR, "Could not start compositor, OOM");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
state.cat_texture = wlr_render_texture_create(state.renderer); state.cat_texture = wlr_texture_from_pixels(state.renderer,
WL_SHM_FORMAT_ARGB8888, cat_tex.width * 4, cat_tex.width, cat_tex.height,
cat_tex.pixel_data);
if (!state.cat_texture) { if (!state.cat_texture) {
wlr_log(L_ERROR, "Could not start compositor, OOM"); wlr_log(L_ERROR, "Could not start compositor, OOM");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
wlr_texture_upload_pixels(state.cat_texture, WL_SHM_FORMAT_ARGB8888,
cat_tex.width, cat_tex.width, cat_tex.height, cat_tex.pixel_data);
if (!wlr_backend_start(compositor.backend)) { if (!wlr_backend_start(compositor.backend)) {
wlr_log(L_ERROR, "Failed to start backend"); wlr_log(L_ERROR, "Failed to start backend");

@ -27,7 +27,6 @@ struct wlr_drm_plane {
// Only used by cursor // Only used by cursor
float matrix[9]; float matrix[9];
struct wlr_texture *wlr_tex;
struct gbm_bo *cursor_bo; struct gbm_bo *cursor_bo;
bool cursor_enabled; bool cursor_enabled;
int32_t cursor_hotspot_x, cursor_hotspot_y; int32_t cursor_hotspot_x, cursor_hotspot_y;

@ -3,29 +3,40 @@
#include <stdbool.h> #include <stdbool.h>
#include <wayland-server.h> #include <wayland-server.h>
#include <wlr/backend/x11.h>
#include <wlr/interfaces/wlr_input_device.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/render/egl.h> #include <wlr/render/egl.h>
#include <X11/Xlib-xcb.h> #include <X11/Xlib-xcb.h>
#include <xcb/xcb.h> #include <xcb/xcb.h>
#define XCB_EVENT_RESPONSE_TYPE_MASK 0x7f
struct wlr_x11_backend; struct wlr_x11_backend;
struct wlr_x11_output { struct wlr_x11_output {
struct wlr_output wlr_output; struct wlr_output wlr_output;
struct wlr_x11_backend *x11; struct wlr_x11_backend *x11;
struct wl_list link; // wlr_x11_backend::outputs
xcb_window_t win; xcb_window_t win;
EGLSurface surf; EGLSurface surf;
struct wl_event_source *frame_timer;
int frame_delay;
}; };
struct wlr_x11_backend { struct wlr_x11_backend {
struct wlr_backend backend; struct wlr_backend backend;
struct wl_display *wl_display; struct wl_display *wl_display;
bool started;
Display *xlib_conn; Display *xlib_conn;
xcb_connection_t *xcb_conn; xcb_connection_t *xcb_conn;
xcb_screen_t *screen; xcb_screen_t *screen;
struct wlr_x11_output output; size_t requested_outputs;
struct wl_list outputs; // wlr_x11_output::link
struct wlr_keyboard keyboard; struct wlr_keyboard keyboard;
struct wlr_input_device keyboard_dev; struct wlr_input_device keyboard_dev;
@ -36,7 +47,6 @@ struct wlr_x11_backend {
struct wlr_egl egl; struct wlr_egl egl;
struct wlr_renderer *renderer; struct wlr_renderer *renderer;
struct wl_event_source *event_source; struct wl_event_source *event_source;
struct wl_event_source *frame_timer;
struct { struct {
xcb_atom_t wm_protocols; xcb_atom_t wm_protocols;
@ -48,7 +58,29 @@ struct wlr_x11_backend {
// The time we last received an event // The time we last received an event
xcb_timestamp_t time; xcb_timestamp_t time;
// A blank cursor
xcb_cursor_t cursor;
#ifdef WLR_HAS_XCB_XKB
bool xkb_supported;
uint8_t xkb_base_event;
uint8_t xkb_base_error;
#endif
struct wl_listener display_destroy; struct wl_listener display_destroy;
}; };
struct wlr_x11_output *x11_output_from_window_id(struct wlr_x11_backend *x11,
xcb_window_t window);
void x11_output_layout_get_box(struct wlr_x11_backend *backend,
struct wlr_box *box);
const struct wlr_input_device_impl input_device_impl;
bool x11_handle_input_event(struct wlr_x11_backend *x11,
xcb_generic_event_t *event);
void x11_output_handle_configure_notify(struct wlr_x11_output *output,
xcb_configure_notify_event_t *event);
#endif #endif

@ -10,6 +10,7 @@
#include <string.h> #include <string.h>
#include <wlr/backend.h> #include <wlr/backend.h>
#include <wlr/render/egl.h> #include <wlr/render/egl.h>
#include <wlr/render/gles2.h>
#include <wlr/render/interface.h> #include <wlr/render/interface.h>
#include <wlr/render/wlr_renderer.h> #include <wlr/render/wlr_renderer.h>
#include <wlr/render/wlr_texture.h> #include <wlr/render/wlr_texture.h>
@ -28,6 +29,7 @@ struct wlr_gles2_renderer {
struct wlr_renderer wlr_renderer; struct wlr_renderer wlr_renderer;
struct wlr_egl *egl; struct wlr_egl *egl;
const char *exts_str;
struct { struct {
GLuint quad; GLuint quad;
@ -38,21 +40,37 @@ struct wlr_gles2_renderer {
} shaders; } shaders;
}; };
enum wlr_gles2_texture_type {
WLR_GLES2_TEXTURE_GLTEX,
WLR_GLES2_TEXTURE_WL_DRM_GL,
WLR_GLES2_TEXTURE_WL_DRM_EXT,
WLR_GLES2_TEXTURE_DMABUF,
};
struct wlr_gles2_texture { struct wlr_gles2_texture {
struct wlr_texture wlr_texture; struct wlr_texture wlr_texture;
struct wlr_egl *egl; struct wlr_egl *egl;
GLuint tex_id; enum wlr_gles2_texture_type type;
const struct gles2_pixel_format *pixel_format; int width, height;
bool has_alpha;
bool inverted_y;
// Not set if WLR_GLES2_TEXTURE_GLTEX
EGLImageKHR image; EGLImageKHR image;
GLenum target; GLuint image_tex;
union {
GLuint gl_tex;
struct wl_resource *wl_drm;
};
}; };
const struct gles2_pixel_format *gles2_format_from_wl(enum wl_shm_format fmt); const struct gles2_pixel_format *gles2_format_from_wl(enum wl_shm_format fmt);
const enum wl_shm_format *gles2_formats(size_t *len); const enum wl_shm_format *gles2_formats(size_t *len);
struct wlr_texture *gles2_texture_create(); struct wlr_gles2_texture *gles2_get_texture_in_context(
struct wlr_gles2_texture *gles2_get_texture(struct wlr_texture *wlr_texture); struct wlr_texture *wlr_texture);
void gles2_push_marker(const char *file, const char *func); void gles2_push_marker(const char *file, const char *func);
void gles2_pop_marker(void); void gles2_pop_marker(void);

@ -1,12 +1,12 @@
#ifndef ROOTSTON_DESKTOP_H #ifndef ROOTSTON_DESKTOP_H
#define ROOTSTON_DESKTOP_H #define ROOTSTON_DESKTOP_H
#include <time.h> #include <time.h>
#include <wayland-server.h> #include <wayland-server.h>
#include <wlr/config.h> #include <wlr/config.h>
#include <wlr/types/wlr_compositor.h> #include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_gamma_control.h> #include <wlr/types/wlr_gamma_control.h>
#include <wlr/types/wlr_idle.h> #include <wlr/types/wlr_idle.h>
#include <wlr/types/wlr_layer_shell.h>
#include <wlr/types/wlr_linux_dmabuf.h> #include <wlr/types/wlr_linux_dmabuf.h>
#include <wlr/types/wlr_list.h> #include <wlr/types/wlr_list.h>
#include <wlr/types/wlr_output_layout.h> #include <wlr/types/wlr_output_layout.h>
@ -48,12 +48,14 @@ struct roots_desktop {
struct wlr_idle *idle; struct wlr_idle *idle;
struct wlr_idle_inhibit_manager_v1 *idle_inhibit; struct wlr_idle_inhibit_manager_v1 *idle_inhibit;
struct wlr_linux_dmabuf *linux_dmabuf; struct wlr_linux_dmabuf *linux_dmabuf;
struct wlr_layer_shell *layer_shell;
struct wl_listener new_output; struct wl_listener new_output;
struct wl_listener layout_change; struct wl_listener layout_change;
struct wl_listener xdg_shell_v6_surface; struct wl_listener xdg_shell_v6_surface;
struct wl_listener xdg_shell_surface; struct wl_listener xdg_shell_surface;
struct wl_listener wl_shell_surface; struct wl_listener wl_shell_surface;
struct wl_listener layer_shell_surface;
struct wl_listener decoration_new; struct wl_listener decoration_new;
#ifdef WLR_HAS_XWAYLAND #ifdef WLR_HAS_XWAYLAND
@ -70,8 +72,10 @@ struct roots_desktop *desktop_create(struct roots_server *server,
void desktop_destroy(struct roots_desktop *desktop); void desktop_destroy(struct roots_desktop *desktop);
struct roots_output *desktop_output_from_wlr_output( struct roots_output *desktop_output_from_wlr_output(
struct roots_desktop *desktop, struct wlr_output *output); struct roots_desktop *desktop, struct wlr_output *output);
struct roots_view *desktop_view_at(struct roots_desktop *desktop, double lx,
double ly, struct wlr_surface **surface, double *sx, double *sy); struct wlr_surface *desktop_surface_at(struct roots_desktop *desktop,
double lx, double ly, double *sx, double *sy,
struct roots_view **view);
struct roots_view *view_create(struct roots_desktop *desktop); struct roots_view *view_create(struct roots_desktop *desktop);
void view_destroy(struct roots_view *view); void view_destroy(struct roots_view *view);
@ -83,10 +87,12 @@ void view_update_size(struct roots_view *view, uint32_t width, uint32_t height);
void view_initial_focus(struct roots_view *view); void view_initial_focus(struct roots_view *view);
void view_map(struct roots_view *view, struct wlr_surface *surface); void view_map(struct roots_view *view, struct wlr_surface *surface);
void view_unmap(struct roots_view *view); void view_unmap(struct roots_view *view);
void view_arrange_maximized(struct roots_view *view);
void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data); void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data);
void handle_xdg_shell_surface(struct wl_listener *listener, void *data); void handle_xdg_shell_surface(struct wl_listener *listener, void *data);
void handle_wl_shell_surface(struct wl_listener *listener, void *data); void handle_wl_shell_surface(struct wl_listener *listener, void *data);
void handle_layer_shell_surface(struct wl_listener *listener, void *data);
void handle_xwayland_surface(struct wl_listener *listener, void *data); void handle_xwayland_surface(struct wl_listener *listener, void *data);
#endif #endif

@ -0,0 +1,25 @@
#ifndef ROOTSTON_LAYERS_H
#define ROOTSTON_LAYERS_H
#include <stdbool.h>
#include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_surface.h>
#include <wlr/types/wlr_layer_shell.h>
struct roots_layer_surface {
struct wlr_layer_surface *layer_surface;
struct wl_list link;
struct wl_listener destroy;
struct wl_listener map;
struct wl_listener unmap;
struct wl_listener surface_commit;
struct wl_listener output_destroy;
bool configured;
struct wlr_box geo;
};
struct roots_output;
void arrange_layers(struct roots_output *output);
#endif

@ -1,9 +1,9 @@
#ifndef ROOTSTON_OUTPUT_H #ifndef ROOTSTON_OUTPUT_H
#define ROOTSTON_OUTPUT_H #define ROOTSTON_OUTPUT_H
#include <pixman.h> #include <pixman.h>
#include <time.h> #include <time.h>
#include <wayland-server.h> #include <wayland-server.h>
#include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_output_damage.h> #include <wlr/types/wlr_output_damage.h>
struct roots_desktop; struct roots_desktop;
@ -14,11 +14,16 @@ struct roots_output {
struct wl_list link; // roots_desktop:outputs struct wl_list link; // roots_desktop:outputs
struct roots_view *fullscreen_view; struct roots_view *fullscreen_view;
struct wl_list layers[4]; // layer_surface::link
struct timespec last_frame; struct timespec last_frame;
struct wlr_output_damage *damage; struct wlr_output_damage *damage;
struct wlr_box usable_area;
struct wl_listener destroy; struct wl_listener destroy;
struct wl_listener mode;
struct wl_listener transform;
struct wl_listener damage_frame; struct wl_listener damage_frame;
struct wl_listener damage_destroy; struct wl_listener damage_destroy;
}; };
@ -35,5 +40,9 @@ void output_damage_from_view(struct roots_output *output,
struct roots_view *view); struct roots_view *view);
void output_damage_whole_drag_icon(struct roots_output *output, void output_damage_whole_drag_icon(struct roots_output *output,
struct roots_drag_icon *icon); struct roots_drag_icon *icon);
void output_damage_from_local_surface(struct roots_output *output,
struct wlr_surface *surface, double ox, double oy, float rotation);
void output_damage_whole_local_surface(struct roots_output *output,
struct wlr_surface *surface, double ox, double oy, float rotation);
#endif #endif

@ -1,6 +1,5 @@
#ifndef ROOTSTON_VIEW_H #ifndef ROOTSTON_VIEW_H
#define ROOTSTON_VIEW_H #define ROOTSTON_VIEW_H
#include <stdbool.h> #include <stdbool.h>
#include <wlr/config.h> #include <wlr/config.h>
#include <wlr/types/wlr_box.h> #include <wlr/types/wlr_box.h>
@ -65,8 +64,8 @@ struct roots_xwayland_surface {
struct wl_listener request_resize; struct wl_listener request_resize;
struct wl_listener request_maximize; struct wl_listener request_maximize;
struct wl_listener request_fullscreen; struct wl_listener request_fullscreen;
struct wl_listener map_notify; struct wl_listener map;
struct wl_listener unmap_notify; struct wl_listener unmap;
struct wl_listener surface_commit; struct wl_listener surface_commit;
}; };
@ -177,6 +176,8 @@ struct roots_xdg_popup_v6 {
struct roots_view_child view_child; struct roots_view_child view_child;
struct wlr_xdg_popup_v6 *wlr_popup; struct wlr_xdg_popup_v6 *wlr_popup;
struct wl_listener destroy; struct wl_listener destroy;
struct wl_listener map;
struct wl_listener unmap;
struct wl_listener new_popup; struct wl_listener new_popup;
}; };
@ -184,6 +185,8 @@ struct roots_xdg_popup {
struct roots_view_child view_child; struct roots_view_child view_child;
struct wlr_xdg_popup *wlr_popup; struct wlr_xdg_popup *wlr_popup;
struct wl_listener destroy; struct wl_listener destroy;
struct wl_listener map;
struct wl_listener unmap;
struct wl_listener new_popup; struct wl_listener new_popup;
}; };

@ -9,6 +9,7 @@
struct wlr_backend *wlr_x11_backend_create(struct wl_display *display, struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
const char *x11_display); const char *x11_display);
struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend);
bool wlr_backend_is_x11(struct wlr_backend *backend); bool wlr_backend_is_x11(struct wlr_backend *backend);
bool wlr_input_device_is_x11(struct wlr_input_device *device); bool wlr_input_device_is_x11(struct wlr_input_device *device);

@ -13,14 +13,14 @@ struct wlr_egl {
EGLConfig config; EGLConfig config;
EGLContext context; EGLContext context;
const char *egl_exts_str; const char *exts_str;
const char *gl_exts_str;
struct { struct {
bool buffer_age; bool buffer_age;
bool swap_buffers_with_damage; bool swap_buffers_with_damage;
bool dmabuf_import; bool dmabuf_import;
bool dmabuf_import_modifiers; bool dmabuf_import_modifiers;
bool bind_wayland_display;
} egl_exts; } egl_exts;
struct wl_display *wl_display; struct wl_display *wl_display;
@ -28,30 +28,25 @@ struct wlr_egl {
// TODO: Allocate and return a wlr_egl // TODO: Allocate and return a wlr_egl
/** /**
* Initializes an egl context for the given platform and remote display. * Initializes an EGL context for the given platform and remote display.
* Will attempt to load all possibly required api functions. * Will attempt to load all possibly required api functions.
*/ */
bool wlr_egl_init(struct wlr_egl *egl, EGLenum platform, void *remote_display, bool wlr_egl_init(struct wlr_egl *egl, EGLenum platform, void *remote_display,
EGLint *config_attribs, EGLint visual_id); EGLint *config_attribs, EGLint visual_id);
/** /**
* Frees all related egl resources, makes the context not-current and * Frees all related EGL resources, makes the context not-current and
* unbinds a bound wayland display. * unbinds a bound wayland display.
*/ */
void wlr_egl_finish(struct wlr_egl *egl); void wlr_egl_finish(struct wlr_egl *egl);
/** /**
* Binds the given display to the egl instance. * Binds the given display to the EGL instance.
* This will allow clients to create egl surfaces from wayland ones and render to it. * This will allow clients to create EGL surfaces from wayland ones and render
* to it.
*/ */
bool wlr_egl_bind_display(struct wlr_egl *egl, struct wl_display *local_display); bool wlr_egl_bind_display(struct wlr_egl *egl, struct wl_display *local_display);
/**
* Refer to the eglQueryWaylandBufferWL extension function.
*/
bool wlr_egl_query_buffer(struct wlr_egl *egl, struct wl_resource *buf,
EGLint attrib, EGLint *value);
/** /**
* Returns a surface for the given native window * Returns a surface for the given native window
* The window must match the remote display the wlr_egl was created with. * The window must match the remote display the wlr_egl was created with.
@ -59,13 +54,14 @@ bool wlr_egl_query_buffer(struct wlr_egl *egl, struct wl_resource *buf,
EGLSurface wlr_egl_create_surface(struct wlr_egl *egl, void *window); EGLSurface wlr_egl_create_surface(struct wlr_egl *egl, void *window);
/** /**
* Creates an egl image from the given client buffer and attributes. * Creates an EGL image from the given wl_drm buffer resource.
*/ */
EGLImageKHR wlr_egl_create_image(struct wlr_egl *egl, EGLImageKHR wlr_egl_create_image_from_wl_drm(struct wlr_egl *egl,
EGLenum target, EGLClientBuffer buffer, const EGLint *attribs); struct wl_resource *data, EGLint *fmt, int *width, int *height,
bool *inverted_y);
/** /**
* Creates an egl image from the given dmabuf attributes. Check usability * Creates an EGL image from the given dmabuf attributes. Check usability
* of the dmabuf with wlr_egl_check_import_dmabuf once first. * of the dmabuf with wlr_egl_check_import_dmabuf once first.
*/ */
EGLImageKHR wlr_egl_create_image_from_dmabuf(struct wlr_egl *egl, EGLImageKHR wlr_egl_create_image_from_dmabuf(struct wlr_egl *egl,
@ -90,13 +86,15 @@ int wlr_egl_get_dmabuf_modifiers(struct wlr_egl *egl, int format,
uint64_t **modifiers); uint64_t **modifiers);
/** /**
* Destroys an egl image created with the given wlr_egl. * Destroys an EGL image created with the given wlr_egl.
*/ */
bool wlr_egl_destroy_image(struct wlr_egl *egl, EGLImageKHR image); bool wlr_egl_destroy_image(struct wlr_egl *egl, EGLImageKHR image);
bool wlr_egl_make_current(struct wlr_egl *egl, EGLSurface surface, bool wlr_egl_make_current(struct wlr_egl *egl, EGLSurface surface,
int *buffer_age); int *buffer_age);
bool wlr_egl_is_current(struct wlr_egl *egl);
bool wlr_egl_swap_buffers(struct wlr_egl *egl, EGLSurface surface, bool wlr_egl_swap_buffers(struct wlr_egl *egl, EGLSurface surface,
pixman_region32_t *damage); pixman_region32_t *damage);

@ -5,6 +5,15 @@
#include <wlr/render/wlr_renderer.h> #include <wlr/render/wlr_renderer.h>
struct wlr_egl; struct wlr_egl;
struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_backend *backend);
struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl);
struct wlr_texture *wlr_gles2_texture_from_pixels(struct wlr_egl *egl,
enum wl_shm_format wl_fmt, uint32_t stride, uint32_t width, uint32_t height,
const void *data);
struct wlr_texture *wlr_gles2_texture_from_wl_drm(struct wlr_egl *egl,
struct wl_resource *data);
struct wlr_texture *wlr_gles2_texture_from_dmabuf(struct wlr_egl *egl,
struct wlr_dmabuf_buffer_attribs *attribs);
#endif #endif

@ -23,7 +23,6 @@ struct wlr_renderer_impl {
void (*end)(struct wlr_renderer *renderer); void (*end)(struct wlr_renderer *renderer);
void (*clear)(struct wlr_renderer *renderer, const float color[static 4]); void (*clear)(struct wlr_renderer *renderer, const float color[static 4]);
void (*scissor)(struct wlr_renderer *renderer, struct wlr_box *box); void (*scissor)(struct wlr_renderer *renderer, struct wlr_box *box);
struct wlr_texture *(*texture_create)(struct wlr_renderer *renderer);
bool (*render_texture_with_matrix)(struct wlr_renderer *renderer, bool (*render_texture_with_matrix)(struct wlr_renderer *renderer,
struct wlr_texture *texture, const float matrix[static 9], struct wlr_texture *texture, const float matrix[static 9],
float alpha); float alpha);
@ -33,14 +32,23 @@ struct wlr_renderer_impl {
const float color[static 4], const float matrix[static 9]); const float color[static 4], const float matrix[static 9]);
const enum wl_shm_format *(*formats)( const enum wl_shm_format *(*formats)(
struct wlr_renderer *renderer, size_t *len); struct wlr_renderer *renderer, size_t *len);
bool (*buffer_is_drm)(struct wlr_renderer *renderer, bool (*resource_is_wl_drm_buffer)(struct wlr_renderer *renderer,
struct wl_resource *buffer); struct wl_resource *resource);
void (*wl_drm_buffer_get_size)(struct wlr_renderer *renderer,
struct wl_resource *buffer, int *width, int *height);
bool (*read_pixels)(struct wlr_renderer *renderer, enum wl_shm_format fmt, bool (*read_pixels)(struct wlr_renderer *renderer, enum wl_shm_format fmt,
uint32_t stride, uint32_t width, uint32_t height, 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, uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y,
void *data); void *data);
bool (*format_supported)(struct wlr_renderer *renderer, bool (*format_supported)(struct wlr_renderer *renderer,
enum wl_shm_format fmt); enum wl_shm_format fmt);
struct wlr_texture *(*texture_from_pixels)(struct wlr_renderer *renderer,
enum wl_shm_format fmt, uint32_t stride, uint32_t width,
uint32_t height, const void *data);
struct wlr_texture *(*texture_from_wl_drm)(struct wlr_renderer *renderer,
struct wl_resource *data);
struct wlr_texture *(*texture_from_dmabuf)(struct wlr_renderer *renderer,
struct wlr_dmabuf_buffer_attribs *attribs);
void (*destroy)(struct wlr_renderer *renderer); void (*destroy)(struct wlr_renderer *renderer);
}; };
@ -48,30 +56,15 @@ void wlr_renderer_init(struct wlr_renderer *renderer,
const struct wlr_renderer_impl *impl); const struct wlr_renderer_impl *impl);
struct wlr_texture_impl { struct wlr_texture_impl {
bool (*upload_pixels)(struct wlr_texture *texture, void (*get_size)(struct wlr_texture *texture, int *width, int *height);
enum wl_shm_format format, int stride, int width, int height, bool (*write_pixels)(struct wlr_texture *texture,
const unsigned char *pixels); enum wl_shm_format wl_fmt, uint32_t stride, uint32_t width,
bool (*update_pixels)(struct wlr_texture *texture, uint32_t height, uint32_t src_x, uint32_t src_y, uint32_t dst_x,
enum wl_shm_format format, int stride, int x, int y, uint32_t dst_y, const void *data);
int width, int height, const unsigned char *pixels);
bool (*upload_shm)(struct wlr_texture *texture, uint32_t format,
struct wl_shm_buffer *shm);
bool (*update_shm)(struct wlr_texture *texture, uint32_t format,
int x, int y, int width, int height, struct wl_shm_buffer *shm);
bool (*upload_drm)(struct wlr_texture *texture,
struct wl_resource *drm_buf);
bool (*upload_eglimage)(struct wlr_texture *texture, EGLImageKHR image,
uint32_t width, uint32_t height);
bool (*upload_dmabuf)(struct wlr_texture *texture,
struct wl_resource *dmabuf_resource);
void (*get_buffer_size)(struct wlr_texture *texture,
struct wl_resource *resource, int *width, int *height);
void (*destroy)(struct wlr_texture *texture); void (*destroy)(struct wlr_texture *texture);
}; };
void wlr_texture_init(struct wlr_texture *texture, void wlr_texture_init(struct wlr_texture *texture,
const struct wlr_texture_impl *impl); const struct wlr_texture_impl *impl);
void wlr_texture_get_buffer_size(struct wlr_texture *texture,
struct wl_resource *resource, int *width, int *height);
#endif #endif

@ -1,8 +1,6 @@
#ifndef WLR_RENDER_WLR_RENDERER_H #ifndef WLR_RENDER_WLR_RENDERER_H
#define WLR_RENDER_WLR_RENDERER_H #define WLR_RENDER_WLR_RENDERER_H
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <stdint.h> #include <stdint.h>
#include <wayland-server-protocol.h> #include <wayland-server-protocol.h>
#include <wlr/render/wlr_texture.h> #include <wlr/render/wlr_texture.h>
@ -21,10 +19,6 @@ void wlr_renderer_clear(struct wlr_renderer *r, const float color[static 4]);
* box. * box.
*/ */
void wlr_renderer_scissor(struct wlr_renderer *r, struct wlr_box *box); void wlr_renderer_scissor(struct wlr_renderer *r, struct wlr_box *box);
/**
* Requests a texture handle from this renderer.
*/
struct wlr_texture *wlr_render_texture_create(struct wlr_renderer *r);
/** /**
* Renders the requested texture. * Renders the requested texture.
*/ */
@ -61,10 +55,15 @@ void wlr_render_ellipse_with_matrix(struct wlr_renderer *r,
const enum wl_shm_format *wlr_renderer_get_formats(struct wlr_renderer *r, const enum wl_shm_format *wlr_renderer_get_formats(struct wlr_renderer *r,
size_t *len); size_t *len);
/** /**
* Returns true if this wl_buffer is a DRM buffer. * Returns true if this wl_buffer is a wl_drm buffer.
*/ */
bool wlr_renderer_buffer_is_drm(struct wlr_renderer *renderer, bool wlr_renderer_resource_is_wl_drm_buffer(struct wlr_renderer *renderer,
struct wl_resource *buffer); struct wl_resource *buffer);
/**
* Gets the width and height of a wl_drm buffer.
*/
void wlr_renderer_wl_drm_buffer_get_size(struct wlr_renderer *renderer,
struct wl_resource *buffer, int *width, int *height);
/** /**
* Reads out of pixels of the currently bound surface into data. `stride` is in * Reads out of pixels of the currently bound surface into data. `stride` is in
* bytes. * bytes.

@ -5,62 +5,49 @@
#include <EGL/eglext.h> #include <EGL/eglext.h>
#include <stdint.h> #include <stdint.h>
#include <wayland-server-protocol.h> #include <wayland-server-protocol.h>
#include <wlr/types/wlr_linux_dmabuf.h>
struct wlr_renderer;
struct wlr_texture_impl; struct wlr_texture_impl;
struct wlr_texture { struct wlr_texture {
const struct wlr_texture_impl *impl; const struct wlr_texture_impl *impl;
bool valid;
uint32_t format;
int width, height;
bool inverted_y;
struct wl_signal destroy_signal;
struct wl_resource *resource;
}; };
/** /**
* Copies pixels to this texture. The buffer is not accessed after this function * Create a new texture from raw pixel data. `stride` is in bytes. The returned
* returns. * texture is mutable.
*/ */
bool wlr_texture_upload_pixels(struct wlr_texture *tex, struct wlr_texture *wlr_texture_from_pixels(struct wlr_renderer *renderer,
enum wl_shm_format format, int stride, int width, int height, enum wl_shm_format wl_fmt, uint32_t stride, uint32_t width, uint32_t height,
const unsigned char *pixels); const void *data);
/** /**
* Copies pixels to this texture. The buffer is not accessed after this function * Create a new texture from a wl_drm resource. The returned texture is
* returns. Under some circumstances, this function may re-upload the entire * immutable.
* buffer - therefore, the entire buffer must be valid.
*/ */
bool wlr_texture_update_pixels(struct wlr_texture *surf, struct wlr_texture *wlr_texture_from_wl_drm(struct wlr_renderer *renderer,
enum wl_shm_format format, int stride, int x, int y, struct wl_resource *data);
int width, int height, const unsigned char *pixels);
/** /**
* Copies pixels from a wl_shm_buffer into this texture. The buffer is not * Create a new texture from a DMA-BUF. The returned texture is immutable.
* accessed after this function returns.
*/ */
bool wlr_texture_upload_shm(struct wlr_texture *tex, uint32_t format, struct wlr_texture *wlr_texture_from_dmabuf(struct wlr_renderer *renderer,
struct wl_shm_buffer *shm); struct wlr_dmabuf_buffer_attribs *attribs);
/** /**
* Attaches the contents from the given wl_drm wl_buffer resource onto the * Get the texture width and height.
* texture. The wl_resource is not used after this call.
* Will fail (return false) if the given resource is no drm buffer.
*/ */
bool wlr_texture_upload_drm(struct wlr_texture *tex, void wlr_texture_get_size(struct wlr_texture *texture, int *width, int *height);
struct wl_resource *drm_buffer);
bool wlr_texture_upload_eglimage(struct wlr_texture *tex,
EGLImageKHR image, uint32_t width, uint32_t height);
bool wlr_texture_upload_dmabuf(struct wlr_texture *tex,
struct wl_resource *dmabuf_resource);
/** /**
* Copies a rectangle of pixels from a wl_shm_buffer onto the texture. The * Update a texture with raw pixels. The texture must be mutable.
* buffer is not accessed after this function returns. Under some circumstances,
* this function may re-upload the entire buffer - therefore, the entire buffer
* must be valid.
*/ */
bool wlr_texture_update_shm(struct wlr_texture *surf, uint32_t format, bool wlr_texture_write_pixels(struct wlr_texture *texture,
int x, int y, int width, int height, struct wl_shm_buffer *shm); 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,
const void *data);
/** /**
* Destroys this wlr_texture. * Destroys this wlr_texture.
*/ */

@ -73,7 +73,7 @@ bool wlr_cursor_warp(struct wlr_cursor *cur, struct wlr_input_device *dev,
double x, double y); double x, double y);
void wlr_cursor_warp_absolute(struct wlr_cursor *cur, void wlr_cursor_warp_absolute(struct wlr_cursor *cur,
struct wlr_input_device *dev, double x_mm, double y_mm); struct wlr_input_device *dev, double x, double y);
/** /**
* Move the cursor in the direction of the given x and y coordinates. * Move the cursor in the direction of the given x and y coordinates.
@ -155,7 +155,7 @@ void wlr_cursor_map_input_to_region(struct wlr_cursor *cur,
* Convert absolute coordinates to layout coordinates for the device. * Convert absolute coordinates to layout coordinates for the device.
*/ */
bool wlr_cursor_absolute_to_layout_coords(struct wlr_cursor *cur, bool wlr_cursor_absolute_to_layout_coords(struct wlr_cursor *cur,
struct wlr_input_device *device, double x_mm, double y_mm, struct wlr_input_device *device, double x, double y,
double width_mm, double height_mm, double *lx, double *ly); double *lx, double *ly);
#endif #endif

@ -29,6 +29,8 @@ struct wlr_input_device {
enum wlr_input_device_type type; enum wlr_input_device_type type;
int vendor, product; int vendor, product;
char *name; char *name;
// Or 0 if not applicable to this device
double width_mm, height_mm;
/* wlr_input_device.type determines which of these is valid */ /* wlr_input_device.type determines which of these is valid */
union { union {

@ -0,0 +1,102 @@
#ifndef WLR_TYPES_WLR_LAYER_SHELL_H
#define WLR_TYPES_WLR_LAYER_SHELL_H
#include <stdbool.h>
#include <stdint.h>
#include <wayland-server.h>
#include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_surface.h>
#include "wlr-layer-shell-unstable-v1-protocol.h"
/**
* wlr_layer_shell allows clients to arrange themselves in "layers" on the
* desktop in accordance with the wlr-layer-shell protocol. When a client is
* added, the new_surface signal will be raised and passed a reference to our
* wlr_layer_surface. At this time, the client will have configured the surface
* as it desires, including information like desired anchors and margins. The
* compositor should use this information to decide how to arrange the layer
* on-screen, then determine the dimensions of the layer and call
* wlr_layer_surface_configure. The client will then attach a buffer and commit
* the surface, at which point the wlr_layer_surface map signal is raised and
* the compositor should begin rendering the surface.
*/
struct wlr_layer_shell {
struct wl_global *wl_global;
struct wl_list client_resources; // wl_resource
struct wl_list surfaces; // wl_layer_surface
struct wl_listener display_destroy;
struct {
struct wl_signal new_surface;
} events;
void *data;
};
struct wlr_layer_surface_state {
uint32_t anchor;
int32_t exclusive_zone;
struct {
uint32_t top, right, bottom, left;
} margin;
bool keyboard_interactive;
uint32_t desired_width, desired_height;
uint32_t actual_width, actual_height;
};
struct wlr_layer_surface_configure {
struct wl_list link; // wlr_layer_surface::configure_list
uint32_t serial;
struct wlr_layer_surface_state state;
};
struct wlr_layer_surface {
struct wl_list link; // wlr_layer_shell::surfaces
struct wlr_surface *surface;
struct wlr_output *output;
struct wl_resource *resource;
struct wlr_layer_shell *shell;
const char *namespace;
enum zwlr_layer_shell_v1_layer layer;
bool added, configured, mapped, closed;
uint32_t configure_serial;
struct wl_event_source *configure_idle;
uint32_t configure_next_serial;
struct wl_list configure_list;
struct wlr_layer_surface_configure *acked_configure;
struct wlr_layer_surface_state client_pending;
struct wlr_layer_surface_state server_pending;
struct wlr_layer_surface_state current;
struct wl_listener surface_destroy_listener;
struct {
struct wl_signal destroy;
struct wl_signal map;
struct wl_signal unmap;
} events;
void *data;
};
struct wlr_layer_shell *wlr_layer_shell_create(struct wl_display *display);
void wlr_layer_shell_destroy(struct wlr_layer_shell *layer_shell);
/**
* Notifies the layer surface to configure itself with this width/height. The
* layer_surface will signal its map event when the surface is ready to assume
* this size.
*/
void wlr_layer_surface_configure(struct wlr_layer_surface *surface,
uint32_t width, uint32_t height);
/**
* Unmaps this layer surface and notifies the client that it has been closed.
*/
void wlr_layer_surface_close(struct wlr_layer_surface *surface);
#endif

@ -11,6 +11,12 @@
#define DRM_FORMAT_MOD_INVALID ((1ULL<<56) - 1) #define DRM_FORMAT_MOD_INVALID ((1ULL<<56) - 1)
#endif #endif
enum {
WLR_DMABUF_BUFFER_ATTRIBS_FLAGS_Y_INVERT = 1,
WLR_DMABUF_BUFFER_ATTRIBS_FLAGS_INTERLACED = 2,
WLR_DMABUF_BUFFER_ATTRIBS_FLAGS_BOTTOM_FIRST = 4,
};
struct wlr_dmabuf_buffer_attribs { struct wlr_dmabuf_buffer_attribs {
/* set via params_add */ /* set via params_add */
int n_planes; int n_planes;
@ -22,7 +28,7 @@ struct wlr_dmabuf_buffer_attribs {
int32_t width; int32_t width;
int32_t height; int32_t height;
uint32_t format; uint32_t format;
uint32_t flags; /* enum zlinux_buffer_params_flags */ uint32_t flags;
}; };
struct wlr_dmabuf_buffer { struct wlr_dmabuf_buffer {
@ -52,11 +58,6 @@ struct wlr_dmabuf_buffer *wlr_dmabuf_buffer_from_buffer_resource(
struct wlr_dmabuf_buffer *wlr_dmabuf_buffer_from_params_resource( struct wlr_dmabuf_buffer *wlr_dmabuf_buffer_from_params_resource(
struct wl_resource *params_resource); struct wl_resource *params_resource);
/**
* Returns true if the given dmabuf has y-axis inverted, false otherwise
*/
bool wlr_dmabuf_buffer_has_inverted_y(struct wlr_dmabuf_buffer *dmabuf);
/* the protocol interface */ /* the protocol interface */
struct wlr_linux_dmabuf { struct wlr_linux_dmabuf {
struct wl_global *wl_global; struct wl_global *wl_global;

@ -3,6 +3,7 @@
#include <pixman.h> #include <pixman.h>
#include <time.h> #include <time.h>
#include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_output.h> #include <wlr/types/wlr_output.h>
/** /**

@ -40,41 +40,42 @@ struct wlr_output_layout_output *wlr_output_layout_get(
struct wlr_output_layout *layout, struct wlr_output *reference); struct wlr_output_layout *layout, struct wlr_output *reference);
struct wlr_output *wlr_output_layout_output_at(struct wlr_output_layout *layout, struct wlr_output *wlr_output_layout_output_at(struct wlr_output_layout *layout,
double x, double y); double lx, double ly);
void wlr_output_layout_add(struct wlr_output_layout *layout, void wlr_output_layout_add(struct wlr_output_layout *layout,
struct wlr_output *output, int x, int y); struct wlr_output *output, int lx, int ly);
void wlr_output_layout_move(struct wlr_output_layout *layout, void wlr_output_layout_move(struct wlr_output_layout *layout,
struct wlr_output *output, int x, int y); struct wlr_output *output, int lx, int ly);
void wlr_output_layout_remove(struct wlr_output_layout *layout, void wlr_output_layout_remove(struct wlr_output_layout *layout,
struct wlr_output *output); struct wlr_output *output);
/** /**
* Given x and y as pointers to global coordinates, adjusts them to local output * Given x and y in layout coordinates, adjusts them to local output
* coordinates relative to the given reference output. * coordinates relative to the given reference output.
*/ */
void wlr_output_layout_output_coords(struct wlr_output_layout *layout, void wlr_output_layout_output_coords(struct wlr_output_layout *layout,
struct wlr_output *reference, double *x, double *y); struct wlr_output *reference, double *lx, double *ly);
bool wlr_output_layout_contains_point(struct wlr_output_layout *layout, bool wlr_output_layout_contains_point(struct wlr_output_layout *layout,
struct wlr_output *reference, int x, int y); struct wlr_output *reference, int lx, int ly);
bool wlr_output_layout_intersects(struct wlr_output_layout *layout, bool wlr_output_layout_intersects(struct wlr_output_layout *layout,
struct wlr_output *reference, const struct wlr_box *target_box); struct wlr_output *reference, const struct wlr_box *target_lbox);
/** /**
* Get the closest point on this layout from the given point from the reference * Get the closest point on this layout from the given point from the reference
* output. If reference is NULL, gets the closest point from the entire layout. * output. If reference is NULL, gets the closest point from the entire layout.
*/ */
void wlr_output_layout_closest_point(struct wlr_output_layout *layout, void wlr_output_layout_closest_point(struct wlr_output_layout *layout,
struct wlr_output *reference, double x, double y, double *dest_x, struct wlr_output *reference, double lx, double ly, double *dest_lx,
double *dest_y); double *dest_ly);
/** /**
* Get the box of the layout for the given reference output. If `reference` * Get the box of the layout for the given reference output in layout
* is NULL, the box will be for the extents of the entire layout. * coordinates. If `reference` is NULL, the box will be for the extents of the
* entire layout.
*/ */
struct wlr_box *wlr_output_layout_get_box( struct wlr_box *wlr_output_layout_get_box(
struct wlr_output_layout *layout, struct wlr_output *reference); struct wlr_output_layout *layout, struct wlr_output *reference);
@ -109,6 +110,6 @@ enum wlr_direction {
*/ */
struct wlr_output *wlr_output_layout_adjacent_output( struct wlr_output *wlr_output_layout_adjacent_output(
struct wlr_output_layout *layout, enum wlr_direction direction, struct wlr_output_layout *layout, enum wlr_direction direction,
struct wlr_output *reference, double ref_x, double ref_y); struct wlr_output *reference, double ref_lx, double ref_ly);
#endif #endif

@ -29,8 +29,8 @@ struct wlr_event_pointer_motion {
struct wlr_event_pointer_motion_absolute { struct wlr_event_pointer_motion_absolute {
struct wlr_input_device *device; struct wlr_input_device *device;
uint32_t time_msec; uint32_t time_msec;
double x_mm, y_mm; // From 0..1
double width_mm, height_mm; double x, y;
}; };
struct wlr_event_pointer_button { struct wlr_event_pointer_button {

@ -134,6 +134,9 @@ struct wlr_surface *wlr_surface_get_main_surface(struct wlr_surface *surface);
struct wlr_subsurface *wlr_surface_subsurface_at(struct wlr_surface *surface, struct wlr_subsurface *wlr_surface_subsurface_at(struct wlr_surface *surface,
double sx, double sy, double *sub_x, double *sub_y); double sx, double sy, double *sub_x, double *sub_y);
bool wlr_surface_point_accepts_input(
struct wlr_surface *surface, double sx, double sy);
void wlr_surface_send_enter(struct wlr_surface *surface, void wlr_surface_send_enter(struct wlr_surface *surface,
struct wlr_output *output); struct wlr_output *output);

@ -36,8 +36,8 @@ struct wlr_event_tablet_tool_axis {
struct wlr_input_device *device; struct wlr_input_device *device;
uint32_t time_msec; uint32_t time_msec;
uint32_t updated_axes; uint32_t updated_axes;
double x_mm, y_mm; // From 0..1
double width_mm, height_mm; double x, y;
double pressure; double pressure;
double distance; double distance;
double tilt_x, tilt_y; double tilt_x, tilt_y;
@ -54,8 +54,8 @@ enum wlr_tablet_tool_proximity_state {
struct wlr_event_tablet_tool_proximity { struct wlr_event_tablet_tool_proximity {
struct wlr_input_device *device; struct wlr_input_device *device;
uint32_t time_msec; uint32_t time_msec;
double x_mm, y_mm; // From 0..1
double width_mm, height_mm; double x, y;
enum wlr_tablet_tool_proximity_state state; enum wlr_tablet_tool_proximity_state state;
}; };
@ -67,8 +67,8 @@ enum wlr_tablet_tool_tip_state {
struct wlr_event_tablet_tool_tip { struct wlr_event_tablet_tool_tip {
struct wlr_input_device *device; struct wlr_input_device *device;
uint32_t time_msec; uint32_t time_msec;
double x_mm, y_mm; // From 0..1
double width_mm, height_mm; double x, y;
enum wlr_tablet_tool_tip_state state; enum wlr_tablet_tool_tip_state state;
}; };

@ -23,8 +23,8 @@ struct wlr_event_touch_down {
struct wlr_input_device *device; struct wlr_input_device *device;
uint32_t time_msec; uint32_t time_msec;
int32_t touch_id; int32_t touch_id;
double x_mm, y_mm; // From 0..1
double width_mm, height_mm; double x, y;
}; };
struct wlr_event_touch_up { struct wlr_event_touch_up {
@ -37,8 +37,8 @@ struct wlr_event_touch_motion {
struct wlr_input_device *device; struct wlr_input_device *device;
uint32_t time_msec; uint32_t time_msec;
int32_t touch_id; int32_t touch_id;
double x_mm, y_mm; // From 0..1
double width_mm, height_mm; double x, y;
}; };
struct wlr_event_touch_cancel { struct wlr_event_touch_cancel {

@ -77,9 +77,22 @@ struct wlr_xdg_toplevel {
struct wlr_xdg_surface *base; struct wlr_xdg_surface *base;
struct wlr_xdg_surface *parent; struct wlr_xdg_surface *parent;
bool added; bool added;
struct wlr_xdg_toplevel_state next; // client protocol requests
struct wlr_xdg_toplevel_state pending; // user configure requests struct wlr_xdg_toplevel_state client_pending;
struct wlr_xdg_toplevel_state server_pending;
struct wlr_xdg_toplevel_state current; struct wlr_xdg_toplevel_state current;
char *title;
char *app_id;
struct {
struct wl_signal request_maximize;
struct wl_signal request_fullscreen;
struct wl_signal request_minimize;
struct wl_signal request_move;
struct wl_signal request_resize;
struct wl_signal request_show_window_menu;
} events;
}; };
struct wlr_xdg_surface_configure { struct wlr_xdg_surface_configure {
@ -109,9 +122,6 @@ struct wlr_xdg_surface {
uint32_t configure_next_serial; uint32_t configure_next_serial;
struct wl_list configure_list; struct wl_list configure_list;
char *title;
char *app_id;
bool has_next_geometry; bool has_next_geometry;
struct wlr_box next_geometry; struct wlr_box next_geometry;
struct wlr_box geometry; struct wlr_box geometry;
@ -124,13 +134,6 @@ struct wlr_xdg_surface {
struct wl_signal new_popup; struct wl_signal new_popup;
struct wl_signal map; struct wl_signal map;
struct wl_signal unmap; struct wl_signal unmap;
struct wl_signal request_maximize;
struct wl_signal request_fullscreen;
struct wl_signal request_minimize;
struct wl_signal request_move;
struct wl_signal request_resize;
struct wl_signal request_show_window_menu;
} events; } events;
void *data; void *data;

@ -4,6 +4,7 @@
#include <wayland-server.h> #include <wayland-server.h>
#include <wlr/types/wlr_box.h> #include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_seat.h> #include <wlr/types/wlr_seat.h>
#include "xdg-shell-unstable-v6-protocol.h"
struct wlr_xdg_shell_v6 { struct wlr_xdg_shell_v6 {
struct wl_global *wl_global; struct wl_global *wl_global;
@ -32,6 +33,21 @@ struct wlr_xdg_client_v6 {
struct wl_event_source *ping_timer; struct wl_event_source *ping_timer;
}; };
struct wlr_xdg_positioner_v6 {
struct wlr_box anchor_rect;
enum zxdg_positioner_v6_anchor anchor;
enum zxdg_positioner_v6_gravity gravity;
enum zxdg_positioner_v6_constraint_adjustment constraint_adjustment;
struct {
int32_t width, height;
} size;
struct {
int32_t x, y;
} offset;
};
struct wlr_xdg_popup_v6 { struct wlr_xdg_popup_v6 {
struct wlr_xdg_surface_v6 *base; struct wlr_xdg_surface_v6 *base;
struct wl_list link; struct wl_list link;
@ -45,6 +61,8 @@ struct wlr_xdg_popup_v6 {
// geometry of the parent surface // geometry of the parent surface
struct wlr_box geometry; struct wlr_box geometry;
struct wlr_xdg_positioner_v6 positioner;
struct wl_list grab_link; // wlr_xdg_popup_grab_v6::popups struct wl_list grab_link; // wlr_xdg_popup_grab_v6::popups
}; };
@ -77,9 +95,22 @@ struct wlr_xdg_toplevel_v6 {
struct wlr_xdg_surface_v6 *base; struct wlr_xdg_surface_v6 *base;
struct wlr_xdg_surface_v6 *parent; struct wlr_xdg_surface_v6 *parent;
bool added; bool added;
struct wlr_xdg_toplevel_v6_state next; // client protocol requests
struct wlr_xdg_toplevel_v6_state pending; // user configure requests struct wlr_xdg_toplevel_v6_state client_pending;
struct wlr_xdg_toplevel_v6_state server_pending;
struct wlr_xdg_toplevel_v6_state current; struct wlr_xdg_toplevel_v6_state current;
char *title;
char *app_id;
struct {
struct wl_signal request_maximize;
struct wl_signal request_fullscreen;
struct wl_signal request_minimize;
struct wl_signal request_move;
struct wl_signal request_resize;
struct wl_signal request_show_window_menu;
} events;
}; };
struct wlr_xdg_surface_v6_configure { struct wlr_xdg_surface_v6_configure {
@ -109,9 +140,6 @@ struct wlr_xdg_surface_v6 {
uint32_t configure_next_serial; uint32_t configure_next_serial;
struct wl_list configure_list; struct wl_list configure_list;
char *title;
char *app_id;
bool has_next_geometry; bool has_next_geometry;
struct wlr_box next_geometry; struct wlr_box next_geometry;
struct wlr_box geometry; struct wlr_box geometry;
@ -124,13 +152,6 @@ struct wlr_xdg_surface_v6 {
struct wl_signal new_popup; struct wl_signal new_popup;
struct wl_signal map; struct wl_signal map;
struct wl_signal unmap; struct wl_signal unmap;
struct wl_signal request_maximize;
struct wl_signal request_fullscreen;
struct wl_signal request_minimize;
struct wl_signal request_move;
struct wl_signal request_resize;
struct wl_signal request_show_window_menu;
} events; } events;
void *data; void *data;
@ -226,4 +247,46 @@ struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6_popup_at(
struct wlr_xdg_surface_v6 *surface, double sx, double sy, struct wlr_xdg_surface_v6 *surface, double sx, double sy,
double *popup_sx, double *popup_sy); double *popup_sx, double *popup_sy);
/**
* Get the geometry for this positioner based on the anchor rect, gravity, and
* size of this positioner.
*/
struct wlr_box wlr_xdg_positioner_v6_get_geometry(
struct wlr_xdg_positioner_v6 *positioner);
/**
* Get the anchor point for this popup in the toplevel parent's coordinate system.
*/
void wlr_xdg_popup_v6_get_anchor_point(struct wlr_xdg_popup_v6 *popup,
int *toplevel_sx, int *toplevel_sy);
/**
* Convert the given coordinates in the popup coordinate system to the toplevel
* surface coordinate system.
*/
void wlr_xdg_popup_v6_get_toplevel_coords(struct wlr_xdg_popup_v6 *popup,
int popup_sx, int popup_sy, int *toplevel_sx, int *toplevel_sy);
/**
* Set the geometry of this popup to unconstrain it according to its
* xdg-positioner rules. The box should be in the popup's root toplevel parent
* surface coordinate system.
*/
void wlr_xdg_popup_v6_unconstrain_from_box(struct wlr_xdg_popup_v6 *popup,
struct wlr_box *toplevel_sx_box);
/**
Invert the right/left anchor and gravity for this positioner. This can be
used to "flip" the positioner around the anchor rect in the x direction.
*/
void wlr_positioner_v6_invert_x(
struct wlr_xdg_positioner_v6 *positioner);
/**
Invert the top/bottom anchor and gravity for this positioner. This can be
used to "flip" the positioner around the anchor rect in the y direction.
*/
void wlr_positioner_v6_invert_y(
struct wlr_xdg_positioner_v6 *positioner);
#endif #endif

@ -126,8 +126,8 @@ struct wlr_xwayland_surface {
struct wl_signal request_maximize; struct wl_signal request_maximize;
struct wl_signal request_fullscreen; struct wl_signal request_fullscreen;
struct wl_signal map_notify; struct wl_signal map;
struct wl_signal unmap_notify; struct wl_signal unmap;
struct wl_signal set_title; struct wl_signal set_title;
struct wl_signal set_class; struct wl_signal set_class;
struct wl_signal set_parent; struct wl_signal set_parent;
@ -182,5 +182,7 @@ void wlr_xwayland_surface_set_fullscreen(struct wlr_xwayland_surface *surface,
void wlr_xwayland_set_seat(struct wlr_xwayland *xwayland, void wlr_xwayland_set_seat(struct wlr_xwayland *xwayland,
struct wlr_seat *seat); struct wlr_seat *seat);
bool wlr_xwayland_surface_is_unmanaged(const struct wlr_xwayland_surface *surface); bool wlr_xwayland_surface_is_unmanaged(
const struct wlr_xwayland_surface *surface);
#endif #endif

@ -90,12 +90,17 @@ if get_option('enable_x11_backend') or get_option('enable_xwayland')
x11_xcb = dependency('x11-xcb') x11_xcb = dependency('x11-xcb')
xcb_icccm = dependency('xcb-icccm', required: false) xcb_icccm = dependency('xcb-icccm', required: false)
xcb_xkb = dependency('xcb-xkb', required: false)
xcb_errors = dependency('xcb-errors', required: get_option('enable_xcb_errors') == 'true') xcb_errors = dependency('xcb-errors', required: get_option('enable_xcb_errors') == 'true')
if xcb_icccm.found() if xcb_icccm.found()
conf_data.set('WLR_HAS_XCB_ICCCM', true) conf_data.set('WLR_HAS_XCB_ICCCM', true)
endif endif
if xcb_xkb.found()
conf_data.set('WLR_HAS_XCB_XKB', true)
endif
if xcb_errors.found() and get_option('enable_xcb_errors') != 'false' if xcb_errors.found() and get_option('enable_xcb_errors') != 'false'
conf_data.set('WLR_HAS_XCB_ERRORS', true) conf_data.set('WLR_HAS_XCB_ERRORS', true)
endif endif

@ -30,6 +30,7 @@ protocols = [
'idle.xml', 'idle.xml',
'screenshooter.xml', 'screenshooter.xml',
'server-decoration.xml', 'server-decoration.xml',
'wlr-layer-shell-unstable-v1.xml',
] ]
client_protocols = [ client_protocols = [
@ -38,6 +39,7 @@ client_protocols = [
[wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'], [wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'],
'idle.xml', 'idle.xml',
'screenshooter.xml', 'screenshooter.xml',
'wlr-layer-shell-unstable-v1.xml',
] ]
wl_protos_src = [] wl_protos_src = []

@ -0,0 +1,281 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="wlr_layer_shell_unstable_v1">
<copyright>
Copyright © 2017 Drew DeVault
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that the above copyright notice appear in
all copies and that both that copyright notice and this permission
notice appear in supporting documentation, and that the name of
the copyright holders not be used in advertising or publicity
pertaining to distribution of the software without specific,
written prior permission. The copyright holders make no
representations about the suitability of this software for any
purpose. It is provided "as is" without express or implied
warranty.
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
</copyright>
<interface name="zwlr_layer_shell_v1" version="1">
<description summary="create surfaces that are layers of the desktop">
Clients can use this interface to assign the surface_layer role to
wl_surfaces. Such surfaces are assigned to a "layer" of the output and
rendered with a defined z-depth respective to each other. They may also be
anchored to the edges and corners of a screen and specify input handling
semantics. This interface should be suitable for the implementation of
many desktop shell components, and a broad number of other applications
that interact with the desktop.
</description>
<request name="get_layer_surface">
<description summary="create a layer_surface from a surface">
Create a layer surface for an existing surface. This assigns the role of
layer_surface, or raises a protocol error if another role is already
assigned.
Creating a layer surface from a wl_surface which has a buffer attached
or committed is a client error, and any attempts by a client to attach
or manipulate a buffer prior to the first layer_surface.configure call
must also be treated as errors.
Clients can specify a namespace that defines the purpose of the layer
surface.
</description>
<arg name="id" type="new_id" interface="zwlr_layer_surface_v1"/>
<arg name="surface" type="object" interface="wl_surface"/>
<arg name="output" type="object" interface="wl_output"/>
<arg name="layer" type="uint" enum="layer" summary="layer to add this surface to"/>
<arg name="namespace" type="string" summary="namespace for the layer surface"/>
</request>
<enum name="error">
<entry name="role" value="0" summary="wl_surface has another role"/>
<entry name="invalid_layer" value="1" summary="layer value is invalid"/>
<entry name="already_constructed" value="2" summary="wl_surface has a buffer attached or committed"/>
</enum>
<enum name="layer">
<description summary="available layers for surfaces">
These values indicate which layers a surface can be rendered in. They
are ordered by z depth, bottom-most first. Traditional shell surfaces
will typically be rendered between the bottom and top layers.
Fullscreen shell surfaces are typically rendered at the top layer.
Multiple surfaces can share a single layer, and ordering within a
single layer is undefined.
</description>
<entry name="background" value="0"/>
<entry name="bottom" value="1"/>
<entry name="top" value="2"/>
<entry name="overlay" value="3"/>
</enum>
</interface>
<interface name="zwlr_layer_surface_v1" version="1">
<description summary="layer metadata interface">
An interface that may be implemented by a wl_surface, for surfaces that
are designed to be rendered as a layer of a stacked desktop-like
environment.
Layer surface state (size, anchor, exclusive zone, margin, interactivity)
is double-buffered, and will be applied at the time wl_surface.commit of
the corresponding wl_surface is called.
</description>
<request name="set_size">
<description summary="sets the size of the surface">
Sets the size of the surface in surface-local coordinates. The
compositor will display the surface centered with respect to its
anchors.
If you pass 0 for either value, the compositor will assign it and
inform you of the assignment in the configure event. You must set your
anchor to opposite edges in the dimensions you omit; not doing so is a
protocol error. Both values are 0 by default.
Size is double-buffered, see wl_surface.commit.
</description>
<arg name="width" type="uint"/>
<arg name="height" type="uint"/>
</request>
<request name="set_anchor">
<description summary="configures the anchor point of the surface">
Requests that the compositor anchor the surface to the specified edges
and corners. If two orthoginal edges are specified (e.g. 'top' and
'left'), then the anchor point will be the intersection of the edges
(e.g. the top left corner of the output); otherwise the anchor point
will be centered on that edge, or in the center if none is specified.
Anchor is double-buffered, see wl_surface.commit.
</description>
<arg name="anchor" type="uint" enum="anchor"/>
</request>
<request name="set_exclusive_zone">
<description summary="configures the exclusive geometry of this surface">
Requests that the compositor avoids occluding an area of the surface
with other surfaces. The compositor's use of this information is
implementation-dependent - do not assume that this region will not
actually be occluded.
A positive value is only meaningful if the surface is anchored to an
edge, rather than a corner. The zone is the number of surface-local
coordinates from the edge that are considered exclusive.
Surfaces that do not wish to have an exclusive zone may instead specify
how they should interact with surfaces that do. If set to zero, the
surface indicates that it would like to be moved to avoid occluding
surfaces with a positive excluzive zone. If set to -1, the surface
indicates that it would not like to be moved to accomodate for other
surfaces, and the compositor should extend it all the way to the edges
it is anchored to.
For example, a panel might set its exclusive zone to 10, so that
maximized shell surfaces are not shown on top of it. A notification
might set its exclusive zone to 0, so that it is moved to avoid
occluding the panel, but shell surfaces are shown underneath it. A
wallpaper or lock screen might set their exclusive zone to -1, so that
they stretch below or over the panel.
The default value is 0.
Exclusive zone is double-buffered, see wl_surface.commit.
</description>
<arg name="zone" type="int"/>
</request>
<request name="set_margin">
<description summary="sets a margin from the anchor point">
Requests that the surface be placed some distance away from the anchor
point on the output, in surface-local coordinates. Setting this value
for edges you are not anchored to has no effect.
The exclusive zone includes the margin.
Margin is double-buffered, see wl_surface.commit.
</description>
<arg name="top" type="int"/>
<arg name="right" type="int"/>
<arg name="bottom" type="int"/>
<arg name="left" type="int"/>
</request>
<request name="set_keyboard_interactivity">
<description summary="requests keyboard events">
Set to 1 to request that the seat send keyboard events to this layer
surface. For layers below the shell surface layer, the seat will use
normal focus semantics. For layers above the shell surface layers, the
seat will always give exclusive keyboard focus to the top-most layer
which has keyboard interactivity set to true.
Layer surfaces receive pointer, touch, and tablet events normally. If
you do not want to receive them, set the input region on your surface
to an empty region.
Events is double-buffered, see wl_surface.commit.
</description>
<arg name="keyboard_interactivity" type="uint"/>
</request>
<request name="get_popup">
<description summary="assign this layer_surface as an xdg_popup parent">
This assigns an xdg_popup's parent to this layer_surface. This popup
should have been created via xdg_surface::get_popup with the parent set
to NULL, and this request must be invoked before committing the popup's
initial state.
See the documentation of xdg_popup for more details about what an
xdg_popup is and how it is used.
</description>
<arg name="popup" type="object" interface="xdg_popup"/>
</request>
<request name="ack_configure">
<description summary="ack a configure event">
When a configure event is received, if a client commits the
surface in response to the configure event, then the client
must make an ack_configure request sometime before the commit
request, passing along the serial of the configure event.
If the client receives multiple configure events before it
can respond to one, it only has to ack the last configure event.
A client is not required to commit immediately after sending
an ack_configure request - it may even ack_configure several times
before its next surface commit.
A client may send multiple ack_configure requests before committing, but
only the last request sent before a commit indicates which configure
event the client really is responding to.
</description>
<arg name="serial" type="uint" summary="the serial from the configure event"/>
</request>
<request name="destroy" type="destructor">
<description summary="destroy the layer_surface">
This request destroys the layer surface.
</description>
</request>
<event name="configure">
<description summary="suggest a surface change">
The configure event asks the client to resize its surface.
Clients should arrange their surface for the new states, and then send
an ack_configure request with the serial sent in this configure event at
some point before committing the new surface.
The client is free to dismiss all but the last configure event it
received.
The width and height arguments specify the size of the window in
surface-local coordinates.
The size is a hint, in the sense that the client is free to ignore it if
it doesn't resize, pick a smaller size (to satisfy aspect ratio or
resize in steps of NxM pixels). If the client picks a smaller size and
is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the
surface will be centered on this axis.
If the width or height arguments are zero, it means the client should
decide its own window dimension.
</description>
<arg name="serial" type="uint"/>
<arg name="width" type="uint"/>
<arg name="height" type="uint"/>
</event>
<event name="closed">
<description summary="surface should be closed">
The closed event is sent by the compositor when the surface will no
longer be shown. The output may have been destroyed or the user may
have asked for it to be removed. Further changes to the surface will be
ignored. The client should destroy the resource after receiving this
event, and create a new surface if they so choose.
</description>
</event>
<enum name="error">
<entry name="invalid_surface_state" value="0" summary="provided surface state is invalid"/>
<entry name="invalid_size" value="1" summary="size is invalid"/>
<entry name="invalid_anchor" value="2" summary="anchor bitfield is invalid"/>
</enum>
<enum name="anchor" bitfield="true">
<entry name="top" value="1" summary="the top edge of the anchor rectangle"/>
<entry name="bottom" value="2" summary="the bottom edge of the anchor rectangle"/>
<entry name="left" value="4" summary="the left edge of the anchor rectangle"/>
<entry name="right" value="8" summary="the right edge of the anchor rectangle"/>
</enum>
</interface>
</protocol>

@ -2,7 +2,6 @@
#include <stdio.h> #include <stdio.h>
#include <EGL/egl.h> #include <EGL/egl.h>
#include <EGL/eglext.h> #include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <stdlib.h> #include <stdlib.h>
#include <wlr/render/egl.h> #include <wlr/render/egl.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
@ -94,9 +93,10 @@ static void print_dmabuf_formats(struct wlr_egl *egl) {
char str_formats[num * 5 + 1]; char str_formats[num * 5 + 1];
for (int i = 0; i < num; i++) { for (int i = 0; i < num; i++) {
snprintf(&str_formats[i*5], (num - i) * 5 + 1, "%.4s ", (char*)&formats[i]); snprintf(&str_formats[i*5], (num - i) * 5 + 1, "%.4s ",
(char*)&formats[i]);
} }
wlr_log(L_INFO, "Supported dmabuf buffer formats: %s", str_formats); wlr_log(L_DEBUG, "Supported dmabuf buffer formats: %s", str_formats);
free(formats); free(formats);
} }
@ -155,33 +155,31 @@ bool wlr_egl_init(struct wlr_egl *egl, EGLenum platform, void *remote_display,
} }
eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl->context); eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl->context);
egl->egl_exts_str = eglQueryString(egl->display, EGL_EXTENSIONS); egl->exts_str = eglQueryString(egl->display, EGL_EXTENSIONS);
egl->gl_exts_str = (const char*) glGetString(GL_EXTENSIONS);
wlr_log(L_INFO, "Using EGL %d.%d", (int)major, (int)minor); wlr_log(L_INFO, "Using EGL %d.%d", (int)major, (int)minor);
wlr_log(L_INFO, "Supported EGL extensions: %s", egl->egl_exts_str); wlr_log(L_INFO, "Supported EGL extensions: %s", egl->exts_str);
wlr_log(L_INFO, "EGL vendor: %s", eglQueryString(egl->display, EGL_VENDOR)); wlr_log(L_INFO, "EGL vendor: %s", eglQueryString(egl->display, EGL_VENDOR));
wlr_log(L_INFO, "Using %s", glGetString(GL_VERSION));
wlr_log(L_INFO, "GL vendor: %s", glGetString(GL_VENDOR));
wlr_log(L_INFO, "Supported OpenGL ES extensions: %s", egl->gl_exts_str);
if (!check_egl_ext(egl->egl_exts_str, "EGL_WL_bind_wayland_display") || if (!check_egl_ext(egl->exts_str, "EGL_KHR_image_base")) {
!check_egl_ext(egl->egl_exts_str, "EGL_KHR_image_base")) {
wlr_log(L_ERROR, "Required egl extensions not supported"); wlr_log(L_ERROR, "Required egl extensions not supported");
goto error; goto error;
} }
egl->egl_exts.buffer_age = egl->egl_exts.buffer_age =
check_egl_ext(egl->egl_exts_str, "EGL_EXT_buffer_age"); check_egl_ext(egl->exts_str, "EGL_EXT_buffer_age");
egl->egl_exts.swap_buffers_with_damage = egl->egl_exts.swap_buffers_with_damage =
check_egl_ext(egl->egl_exts_str, "EGL_EXT_swap_buffers_with_damage") || check_egl_ext(egl->exts_str, "EGL_EXT_swap_buffers_with_damage") ||
check_egl_ext(egl->egl_exts_str, "EGL_KHR_swap_buffers_with_damage"); check_egl_ext(egl->exts_str, "EGL_KHR_swap_buffers_with_damage");
egl->egl_exts.dmabuf_import = egl->egl_exts.dmabuf_import =
check_egl_ext(egl->egl_exts_str, "EGL_EXT_image_dma_buf_import"); check_egl_ext(egl->exts_str, "EGL_EXT_image_dma_buf_import");
egl->egl_exts.dmabuf_import_modifiers = egl->egl_exts.dmabuf_import_modifiers =
check_egl_ext(egl->egl_exts_str, "EGL_EXT_image_dma_buf_import_modifiers") check_egl_ext(egl->exts_str, "EGL_EXT_image_dma_buf_import_modifiers")
&& eglQueryDmaBufFormatsEXT && eglQueryDmaBufModifiersEXT; && eglQueryDmaBufFormatsEXT && eglQueryDmaBufModifiersEXT;
egl->egl_exts.bind_wayland_display =
check_egl_ext(egl->exts_str, "EGL_WL_bind_wayland_display");
print_dmabuf_formats(egl); print_dmabuf_formats(egl);
return true; return true;
@ -223,24 +221,6 @@ bool wlr_egl_bind_display(struct wlr_egl *egl, struct wl_display *local_display)
return false; return false;
} }
bool wlr_egl_query_buffer(struct wlr_egl *egl, struct wl_resource *buf,
int attrib, int *value) {
if (!eglQueryWaylandBufferWL) {
return false;
}
return eglQueryWaylandBufferWL(egl->display, buf, attrib, value);
}
EGLImage wlr_egl_create_image(struct wlr_egl *egl, EGLenum target,
EGLClientBuffer buffer, const EGLint *attribs) {
if (!eglCreateImageKHR) {
return NULL;
}
return eglCreateImageKHR(egl->display, egl->context, target,
buffer, attribs);
}
bool wlr_egl_destroy_image(struct wlr_egl *egl, EGLImage image) { bool wlr_egl_destroy_image(struct wlr_egl *egl, EGLImage image) {
if (!eglDestroyImageKHR) { if (!eglDestroyImageKHR) {
return false; return false;
@ -251,8 +231,9 @@ bool wlr_egl_destroy_image(struct wlr_egl *egl, EGLImage image) {
} }
EGLSurface wlr_egl_create_surface(struct wlr_egl *egl, void *window) { EGLSurface wlr_egl_create_surface(struct wlr_egl *egl, void *window) {
EGLSurface surf = eglCreatePlatformWindowSurfaceEXT(egl->display, egl->config, assert(eglCreatePlatformWindowSurfaceEXT);
window, NULL); EGLSurface surf = eglCreatePlatformWindowSurfaceEXT(egl->display,
egl->config, window, NULL);
if (surf == EGL_NO_SURFACE) { if (surf == EGL_NO_SURFACE) {
wlr_log(L_ERROR, "Failed to create EGL surface"); wlr_log(L_ERROR, "Failed to create EGL surface");
return EGL_NO_SURFACE; return EGL_NO_SURFACE;
@ -289,6 +270,10 @@ bool wlr_egl_make_current(struct wlr_egl *egl, EGLSurface surface,
return true; return true;
} }
bool wlr_egl_is_current(struct wlr_egl *egl) {
return eglGetCurrentContext() == egl->context;
}
bool wlr_egl_swap_buffers(struct wlr_egl *egl, EGLSurface surface, bool wlr_egl_swap_buffers(struct wlr_egl *egl, EGLSurface surface,
pixman_region32_t *damage) { pixman_region32_t *damage) {
EGLBoolean ret; EGLBoolean ret;
@ -323,7 +308,37 @@ bool wlr_egl_swap_buffers(struct wlr_egl *egl, EGLSurface surface,
return true; return true;
} }
EGLImage wlr_egl_create_image_from_dmabuf(struct wlr_egl *egl, 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) {
return NULL;
}
if (!eglQueryWaylandBufferWL(egl->display, data, EGL_TEXTURE_FORMAT, fmt)) {
return NULL;
}
eglQueryWaylandBufferWL(egl->display, data, EGL_WIDTH, width);
eglQueryWaylandBufferWL(egl->display, data, EGL_HEIGHT, height);
EGLint _inverted_y;
if (eglQueryWaylandBufferWL(egl->display, data, EGL_WAYLAND_Y_INVERTED_WL,
&_inverted_y)) {
*inverted_y = !!_inverted_y;
} else {
*inverted_y = false;
}
const EGLint attribs[] = {
EGL_WAYLAND_PLANE_WL, 0,
EGL_NONE,
};
return eglCreateImageKHR(egl->display, egl->context, EGL_WAYLAND_BUFFER_WL,
data, attribs);
}
EGLImageKHR wlr_egl_create_image_from_dmabuf(struct wlr_egl *egl,
struct wlr_dmabuf_buffer_attribs *attributes) { struct wlr_dmabuf_buffer_attribs *attributes) {
bool has_modifier = false; bool has_modifier = false;
if (attributes->modifier[0] != DRM_FORMAT_MOD_INVALID) { if (attributes->modifier[0] != DRM_FORMAT_MOD_INVALID) {
@ -431,7 +446,7 @@ int wlr_egl_get_dmabuf_formats(struct wlr_egl *egl,
int **formats) { int **formats) {
if (!egl->egl_exts.dmabuf_import || if (!egl->egl_exts.dmabuf_import ||
!egl->egl_exts.dmabuf_import_modifiers) { !egl->egl_exts.dmabuf_import_modifiers) {
wlr_log(L_ERROR, "dmabuf extension not present"); wlr_log(L_DEBUG, "dmabuf extension not present");
return -1; return -1;
} }

@ -6,29 +6,32 @@
#include <stdlib.h> #include <stdlib.h>
#include <wayland-server-protocol.h> #include <wayland-server-protocol.h>
#include <wayland-util.h> #include <wayland-util.h>
#include <wlr/backend.h>
#include <wlr/render/egl.h> #include <wlr/render/egl.h>
#include <wlr/render/interface.h> #include <wlr/render/interface.h>
#include <wlr/render/wlr_renderer.h> #include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_matrix.h> #include <wlr/types/wlr_matrix.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include "render/gles2.h"
#include "glapi.h" #include "glapi.h"
#include "render/gles2.h"
static const struct wlr_renderer_impl renderer_impl; static const struct wlr_renderer_impl renderer_impl;
static struct wlr_gles2_renderer *gles2_get_renderer( static struct wlr_gles2_renderer *gles2_get_renderer(
struct wlr_renderer *wlr_renderer) { struct wlr_renderer *wlr_renderer) {
assert(wlr_renderer->impl == &renderer_impl); assert(wlr_renderer->impl == &renderer_impl);
struct wlr_gles2_renderer *renderer = return (struct wlr_gles2_renderer *)wlr_renderer;
(struct wlr_gles2_renderer *)wlr_renderer; }
assert(eglGetCurrentContext() == renderer->egl->context);
static struct wlr_gles2_renderer *gles2_get_renderer_in_context(
struct wlr_renderer *wlr_renderer) {
struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer);
assert(wlr_egl_is_current(renderer->egl));
return renderer; return renderer;
} }
static void gles2_begin(struct wlr_renderer *wlr_renderer, uint32_t width, static void gles2_begin(struct wlr_renderer *wlr_renderer, uint32_t width,
uint32_t height) { uint32_t height) {
gles2_get_renderer(wlr_renderer); gles2_get_renderer_in_context(wlr_renderer);
GLES2_DEBUG_PUSH; GLES2_DEBUG_PUSH;
@ -45,13 +48,13 @@ static void gles2_begin(struct wlr_renderer *wlr_renderer, uint32_t width,
} }
static void gles2_end(struct wlr_renderer *wlr_renderer) { static void gles2_end(struct wlr_renderer *wlr_renderer) {
gles2_get_renderer(wlr_renderer); gles2_get_renderer_in_context(wlr_renderer);
// no-op // no-op
} }
static void gles2_clear(struct wlr_renderer *wlr_renderer, static void gles2_clear(struct wlr_renderer *wlr_renderer,
const float color[static 4]) { const float color[static 4]) {
gles2_get_renderer(wlr_renderer); gles2_get_renderer_in_context(wlr_renderer);
GLES2_DEBUG_PUSH; GLES2_DEBUG_PUSH;
glClearColor(color[0], color[1], color[2], color[3]); glClearColor(color[0], color[1], color[2], color[3]);
@ -61,7 +64,7 @@ static void gles2_clear(struct wlr_renderer *wlr_renderer,
static void gles2_scissor(struct wlr_renderer *wlr_renderer, static void gles2_scissor(struct wlr_renderer *wlr_renderer,
struct wlr_box *box) { struct wlr_box *box) {
gles2_get_renderer(wlr_renderer); gles2_get_renderer_in_context(wlr_renderer);
GLES2_DEBUG_PUSH; GLES2_DEBUG_PUSH;
if (box != NULL) { if (box != NULL) {
@ -73,14 +76,6 @@ static void gles2_scissor(struct wlr_renderer *wlr_renderer,
GLES2_DEBUG_POP; GLES2_DEBUG_POP;
} }
static struct wlr_texture *gles2_renderer_texture_create(
struct wlr_renderer *wlr_renderer) {
assert(wlr_renderer->impl == &renderer_impl);
struct wlr_gles2_renderer *renderer =
(struct wlr_gles2_renderer *)wlr_renderer;
return gles2_texture_create(renderer->egl);
}
static void draw_quad() { static void draw_quad() {
GLfloat verts[] = { GLfloat verts[] = {
1, 0, // top right 1, 0, // top right
@ -107,21 +102,28 @@ static void draw_quad() {
glDisableVertexAttribArray(1); glDisableVertexAttribArray(1);
} }
static bool gles2_render_texture_with_matrix( static bool gles2_render_texture_with_matrix(struct wlr_renderer *wlr_renderer,
struct wlr_renderer *wlr_renderer, struct wlr_texture *wlr_texture, struct wlr_texture *wlr_texture, const float matrix[static 9],
const float matrix[static 9], float alpha) { float alpha) {
struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); struct wlr_gles2_renderer *renderer =
struct wlr_gles2_texture *texture = gles2_get_texture(wlr_texture); gles2_get_renderer_in_context(wlr_renderer);
if (!wlr_texture->valid) { struct wlr_gles2_texture *texture =
wlr_log(L_ERROR, "attempt to render invalid texture"); gles2_get_texture_in_context(wlr_texture);
return false;
} GLuint prog = 0;
GLenum target = 0;
GLuint prog = renderer->shaders.tex_rgba; switch (texture->type) {
if (texture->target == GL_TEXTURE_EXTERNAL_OES) { case WLR_GLES2_TEXTURE_GLTEX:
case WLR_GLES2_TEXTURE_WL_DRM_GL:
prog = texture->has_alpha ? renderer->shaders.tex_rgba :
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; prog = renderer->shaders.tex_ext;
} else if (!texture->pixel_format->has_alpha) { target = GL_TEXTURE_EXTERNAL_OES;
prog = renderer->shaders.tex_rgbx; break;
} }
// OpenGL ES 2 requires the glUniformMatrix3fv transpose parameter to be set // OpenGL ES 2 requires the glUniformMatrix3fv transpose parameter to be set
@ -130,15 +132,23 @@ static bool gles2_render_texture_with_matrix(
wlr_matrix_transpose(transposition, matrix); wlr_matrix_transpose(transposition, matrix);
GLES2_DEBUG_PUSH; GLES2_DEBUG_PUSH;
glBindTexture(texture->target, texture->tex_id);
glTexParameteri(texture->target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); GLuint tex_id = texture->type == WLR_GLES2_TEXTURE_GLTEX ?
glTexParameteri(texture->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); texture->gl_tex : texture->image_tex;
glActiveTexture(GL_TEXTURE0);
glBindTexture(target, tex_id);
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glUseProgram(prog); glUseProgram(prog);
glUniformMatrix3fv(0, 1, GL_FALSE, transposition); glUniformMatrix3fv(0, 1, GL_FALSE, transposition);
glUniform1i(1, wlr_texture->inverted_y); glUniform1i(1, texture->inverted_y);
glUniform1f(3, alpha); glUniform1f(3, alpha);
draw_quad(); draw_quad();
GLES2_DEBUG_POP; GLES2_DEBUG_POP;
return true; return true;
} }
@ -146,7 +156,8 @@ static bool gles2_render_texture_with_matrix(
static void gles2_render_quad_with_matrix(struct wlr_renderer *wlr_renderer, static void gles2_render_quad_with_matrix(struct wlr_renderer *wlr_renderer,
const float color[static 4], const float matrix[static 9]) { const float color[static 4], const float matrix[static 9]) {
struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); struct wlr_gles2_renderer *renderer =
gles2_get_renderer_in_context(wlr_renderer);
// OpenGL ES 2 requires the glUniformMatrix3fv transpose parameter to be set // OpenGL ES 2 requires the glUniformMatrix3fv transpose parameter to be set
// to GL_FALSE // to GL_FALSE
@ -163,7 +174,8 @@ static void gles2_render_quad_with_matrix(struct wlr_renderer *wlr_renderer,
static void gles2_render_ellipse_with_matrix(struct wlr_renderer *wlr_renderer, static void gles2_render_ellipse_with_matrix(struct wlr_renderer *wlr_renderer,
const float color[static 4], const float matrix[static 9]) { const float color[static 4], const float matrix[static 9]) {
struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); struct wlr_gles2_renderer *renderer =
gles2_get_renderer_in_context(wlr_renderer);
// OpenGL ES 2 requires the glUniformMatrix3fv transpose parameter to be set // OpenGL ES 2 requires the glUniformMatrix3fv transpose parameter to be set
// to GL_FALSE // to GL_FALSE
@ -183,20 +195,38 @@ static const enum wl_shm_format *gles2_renderer_formats(
return gles2_formats(len); return gles2_formats(len);
} }
static bool gles2_buffer_is_drm(struct wlr_renderer *wlr_renderer, static bool gles2_resource_is_wl_drm_buffer(struct wlr_renderer *wlr_renderer,
struct wl_resource *buffer) { struct wl_resource *resource) {
struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); struct wlr_gles2_renderer *renderer =
gles2_get_renderer_in_context(wlr_renderer);
EGLint format; if (!eglQueryWaylandBufferWL) {
return wlr_egl_query_buffer(renderer->egl, buffer, EGL_TEXTURE_FORMAT, return false;
&format); }
EGLint fmt;
return eglQueryWaylandBufferWL(renderer->egl->display, resource,
EGL_TEXTURE_FORMAT, &fmt);
}
static void gles2_wl_drm_buffer_get_size(struct wlr_renderer *wlr_renderer,
struct wl_resource *buffer, int *width, int *height) {
struct wlr_gles2_renderer *renderer =
gles2_get_renderer_in_context(wlr_renderer);
if (!eglQueryWaylandBufferWL) {
return;
}
eglQueryWaylandBufferWL(renderer->egl->display, buffer, EGL_WIDTH, width);
eglQueryWaylandBufferWL(renderer->egl->display, buffer, EGL_HEIGHT, height);
} }
static bool gles2_read_pixels(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, 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 height, uint32_t src_x, uint32_t src_y, uint32_t dst_x,
uint32_t dst_y, void *data) { uint32_t dst_y, void *data) {
gles2_get_renderer(wlr_renderer); gles2_get_renderer_in_context(wlr_renderer);
const struct gles2_pixel_format *fmt = gles2_format_from_wl(wl_fmt); const struct gles2_pixel_format *fmt = gles2_format_from_wl(wl_fmt);
if (fmt == NULL) { if (fmt == NULL) {
@ -222,11 +252,32 @@ static bool gles2_read_pixels(struct wlr_renderer *wlr_renderer,
return true; return true;
} }
static bool gles2_format_supported(struct wlr_renderer *r, static bool gles2_format_supported(struct wlr_renderer *wlr_renderer,
enum wl_shm_format wl_fmt) { enum wl_shm_format wl_fmt) {
return gles2_format_from_wl(wl_fmt) != NULL; return gles2_format_from_wl(wl_fmt) != NULL;
} }
static struct wlr_texture *gles2_texture_from_pixels(
struct wlr_renderer *wlr_renderer, enum wl_shm_format wl_fmt,
uint32_t stride, uint32_t width, uint32_t height, const void *data) {
struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer);
return wlr_gles2_texture_from_pixels(renderer->egl, wl_fmt, stride, width,
height, data);
}
static struct wlr_texture *gles2_texture_from_wl_drm(
struct wlr_renderer *wlr_renderer, struct wl_resource *data) {
struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer);
return wlr_gles2_texture_from_wl_drm(renderer->egl, data);
}
static struct wlr_texture *gles2_texture_from_dmabuf(
struct wlr_renderer *wlr_renderer,
struct wlr_dmabuf_buffer_attribs *attribs) {
struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer);
return wlr_gles2_texture_from_dmabuf(renderer->egl, attribs);
}
static void gles2_destroy(struct wlr_renderer *wlr_renderer) { static void gles2_destroy(struct wlr_renderer *wlr_renderer) {
struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer); struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer);
@ -254,14 +305,17 @@ static const struct wlr_renderer_impl renderer_impl = {
.end = gles2_end, .end = gles2_end,
.clear = gles2_clear, .clear = gles2_clear,
.scissor = gles2_scissor, .scissor = gles2_scissor,
.texture_create = gles2_renderer_texture_create,
.render_texture_with_matrix = gles2_render_texture_with_matrix, .render_texture_with_matrix = gles2_render_texture_with_matrix,
.render_quad_with_matrix = gles2_render_quad_with_matrix, .render_quad_with_matrix = gles2_render_quad_with_matrix,
.render_ellipse_with_matrix = gles2_render_ellipse_with_matrix, .render_ellipse_with_matrix = gles2_render_ellipse_with_matrix,
.formats = gles2_renderer_formats, .formats = gles2_renderer_formats,
.buffer_is_drm = gles2_buffer_is_drm, .resource_is_wl_drm_buffer = gles2_resource_is_wl_drm_buffer,
.wl_drm_buffer_get_size = gles2_wl_drm_buffer_get_size,
.read_pixels = gles2_read_pixels, .read_pixels = gles2_read_pixels,
.format_supported = gles2_format_supported, .format_supported = gles2_format_supported,
.texture_from_pixels = gles2_texture_from_pixels,
.texture_from_wl_drm = gles2_texture_from_wl_drm,
.texture_from_dmabuf = gles2_texture_from_dmabuf,
}; };
void gles2_push_marker(const char *file, const char *func) { void gles2_push_marker(const char *file, const char *func) {
@ -288,11 +342,11 @@ static log_importance_t gles2_log_importance_to_wlr(GLenum type) {
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR: return L_ERROR; case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR: return L_ERROR;
case GL_DEBUG_TYPE_PORTABILITY_KHR: return L_DEBUG; case GL_DEBUG_TYPE_PORTABILITY_KHR: return L_DEBUG;
case GL_DEBUG_TYPE_PERFORMANCE_KHR: return L_DEBUG; case GL_DEBUG_TYPE_PERFORMANCE_KHR: return L_DEBUG;
case GL_DEBUG_TYPE_OTHER_KHR: return L_INFO; case GL_DEBUG_TYPE_OTHER_KHR: return L_DEBUG;
case GL_DEBUG_TYPE_MARKER_KHR: return L_DEBUG; case GL_DEBUG_TYPE_MARKER_KHR: return L_DEBUG;
case GL_DEBUG_TYPE_PUSH_GROUP_KHR: return L_DEBUG; case GL_DEBUG_TYPE_PUSH_GROUP_KHR: return L_DEBUG;
case GL_DEBUG_TYPE_POP_GROUP_KHR: return L_DEBUG; case GL_DEBUG_TYPE_POP_GROUP_KHR: return L_DEBUG;
default: return L_INFO; default: return L_DEBUG;
} }
} }
@ -366,7 +420,11 @@ extern const GLchar tex_fragment_src_rgba[];
extern const GLchar tex_fragment_src_rgbx[]; extern const GLchar tex_fragment_src_rgbx[];
extern const GLchar tex_fragment_src_external[]; extern const GLchar tex_fragment_src_external[];
struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_backend *backend) { struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) {
if (!load_glapi()) {
return NULL;
}
struct wlr_gles2_renderer *renderer = struct wlr_gles2_renderer *renderer =
calloc(1, sizeof(struct wlr_gles2_renderer)); calloc(1, sizeof(struct wlr_gles2_renderer));
if (renderer == NULL) { if (renderer == NULL) {
@ -374,9 +432,14 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_backend *backend) {
} }
wlr_renderer_init(&renderer->wlr_renderer, &renderer_impl); wlr_renderer_init(&renderer->wlr_renderer, &renderer_impl);
renderer->egl = wlr_backend_get_egl(backend); renderer->egl = egl;
wlr_egl_make_current(renderer->egl, EGL_NO_SURFACE, NULL); wlr_egl_make_current(renderer->egl, EGL_NO_SURFACE, NULL);
renderer->exts_str = (const char*) glGetString(GL_EXTENSIONS);
wlr_log(L_INFO, "Using %s", glGetString(GL_VERSION));
wlr_log(L_INFO, "GL vendor: %s", glGetString(GL_VENDOR));
wlr_log(L_INFO, "Supported GLES2 extensions: %s", renderer->exts_str);
if (glDebugMessageCallbackKHR && glDebugMessageControlKHR) { if (glDebugMessageCallbackKHR && glDebugMessageControlKHR) {
glEnable(GL_DEBUG_OUTPUT_KHR); glEnable(GL_DEBUG_OUTPUT_KHR);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR);

@ -10,366 +10,238 @@
#include <wlr/render/interface.h> #include <wlr/render/interface.h>
#include <wlr/types/wlr_matrix.h> #include <wlr/types/wlr_matrix.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include "glapi.h"
#include "render/gles2.h" #include "render/gles2.h"
#include "util/signal.h" #include "util/signal.h"
static struct gles2_pixel_format external_pixel_format = {
.wl_format = 0,
.depth = 0,
.bpp = 0,
.gl_format = 0,
.gl_type = 0,
};
static void gles2_texture_ensure(struct wlr_gles2_texture *texture,
GLenum target) {
if (texture->tex_id) {
return;
}
texture->target = target;
glGenTextures(1, &texture->tex_id);
glBindTexture(target, texture->tex_id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
static const struct wlr_texture_impl texture_impl; static const struct wlr_texture_impl texture_impl;
struct wlr_gles2_texture *gles2_get_texture(struct wlr_texture *wlr_texture) { static struct wlr_gles2_texture *gles2_get_texture(
struct wlr_texture *wlr_texture) {
assert(wlr_texture->impl == &texture_impl); assert(wlr_texture->impl == &texture_impl);
return (struct wlr_gles2_texture *)wlr_texture; return (struct wlr_gles2_texture *)wlr_texture;
} }
static bool gles2_texture_upload_pixels(struct wlr_texture *wlr_texture, struct wlr_gles2_texture *gles2_get_texture_in_context(
enum wl_shm_format format, int stride, int width, int height, struct wlr_texture *wlr_texture) {
const unsigned char *pixels) {
struct wlr_gles2_texture *texture = gles2_get_texture(wlr_texture); struct wlr_gles2_texture *texture = gles2_get_texture(wlr_texture);
assert(wlr_egl_is_current(texture->egl));
const struct gles2_pixel_format *fmt = gles2_format_from_wl(format); return texture;
if (!fmt || !fmt->gl_format) {
wlr_log(L_ERROR, "No supported pixel format for this texture");
return false;
}
texture->wlr_texture.width = width;
texture->wlr_texture.height = height;
texture->wlr_texture.format = format;
texture->pixel_format = fmt;
GLES2_DEBUG_PUSH;
gles2_texture_ensure(texture, GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture->tex_id);
glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, stride);
glTexImage2D(GL_TEXTURE_2D, 0, fmt->gl_format, width, height, 0,
fmt->gl_format, fmt->gl_type, pixels);
GLES2_DEBUG_POP;
texture->wlr_texture.valid = true;
return true;
} }
static bool gles2_texture_update_pixels(struct wlr_texture *wlr_texture, static void gles2_texture_get_size(struct wlr_texture *wlr_texture, int *width,
enum wl_shm_format format, int stride, int x, int y, int *height) {
int width, int height, const unsigned char *pixels) {
struct wlr_gles2_texture *texture = gles2_get_texture(wlr_texture); struct wlr_gles2_texture *texture = gles2_get_texture(wlr_texture);
*width = texture->width;
// TODO: Test if the unpack subimage extension is supported and adjust the *height = texture->height;
// upload strategy if not
if (!texture->wlr_texture.valid
|| texture->wlr_texture.format != format
/* || unpack not supported */) {
return gles2_texture_upload_pixels(&texture->wlr_texture, format,
stride, width, height, pixels);
}
const struct gles2_pixel_format *fmt = texture->pixel_format;
GLES2_DEBUG_PUSH;
glBindTexture(GL_TEXTURE_2D, texture->tex_id);
glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, stride);
glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, x);
glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, y);
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, fmt->gl_format,
fmt->gl_type, pixels);
glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0);
glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0);
GLES2_DEBUG_POP;
return true;
} }
static bool gles2_texture_upload_shm(struct wlr_texture *wlr_texture, static bool gles2_texture_write_pixels(struct wlr_texture *wlr_texture,
uint32_t format, struct wl_shm_buffer *buffer) { enum wl_shm_format wl_fmt, uint32_t stride, uint32_t width,
struct wlr_gles2_texture *texture = gles2_get_texture(wlr_texture); uint32_t height, uint32_t src_x, uint32_t src_y, uint32_t dst_x,
uint32_t dst_y, const void *data) {
struct wlr_gles2_texture *texture =
gles2_get_texture_in_context(wlr_texture);
const struct gles2_pixel_format *fmt = gles2_format_from_wl(format); if (texture->type != WLR_GLES2_TEXTURE_GLTEX) {
if (!fmt || !fmt->gl_format) { wlr_log(L_ERROR, "Cannot write pixels to immutable texture");
wlr_log(L_ERROR, "Unsupported pixel format %"PRIu32" for this texture",
format);
return false; return false;
} }
wl_shm_buffer_begin_access(buffer);
uint8_t *pixels = wl_shm_buffer_get_data(buffer);
int width = wl_shm_buffer_get_width(buffer);
int height = wl_shm_buffer_get_height(buffer);
int pitch = wl_shm_buffer_get_stride(buffer) / (fmt->bpp / 8);
texture->wlr_texture.width = width;
texture->wlr_texture.height = height;
texture->wlr_texture.format = format;
texture->pixel_format = fmt;
const struct gles2_pixel_format *fmt = gles2_format_from_wl(wl_fmt);
if (fmt == NULL) {
wlr_log(L_ERROR, "Unsupported pixel format %"PRIu32, wl_fmt);
return false;
}
// TODO: what if the unpack subimage extension isn't supported?
GLES2_DEBUG_PUSH; GLES2_DEBUG_PUSH;
gles2_texture_ensure(texture, GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture->tex_id);
glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, pitch);
glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0);
glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0);
glTexImage2D(GL_TEXTURE_2D, 0, fmt->gl_format, width, height, 0,
fmt->gl_format, fmt->gl_type, pixels);
GLES2_DEBUG_POP;
texture->wlr_texture.valid = true; glBindTexture(GL_TEXTURE_2D, texture->gl_tex);
wl_shm_buffer_end_access(buffer);
return true;
}
static bool gles2_texture_update_shm(struct wlr_texture *wlr_texture, glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, stride / (fmt->bpp / 8));
uint32_t format, int x, int y, int width, int height, glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, src_x);
struct wl_shm_buffer *buffer) { glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, src_y);
struct wlr_gles2_texture *texture = gles2_get_texture(wlr_texture);
// TODO: Test if the unpack subimage extension is supported and adjust the glTexSubImage2D(GL_TEXTURE_2D, 0, dst_x, dst_y, width, height,
// upload strategy if not fmt->gl_format, fmt->gl_type, data);
assert(texture);
if (!texture->wlr_texture.valid
|| texture->wlr_texture.format != format
/* || unpack not supported */) {
return gles2_texture_upload_shm(&texture->wlr_texture, format, buffer);
}
const struct gles2_pixel_format *fmt = texture->pixel_format;
wl_shm_buffer_begin_access(buffer);
uint8_t *pixels = wl_shm_buffer_get_data(buffer);
int pitch = wl_shm_buffer_get_stride(buffer) / (fmt->bpp / 8);
GLES2_DEBUG_PUSH; glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0);
glBindTexture(GL_TEXTURE_2D, texture->tex_id);
glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, pitch);
glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, x);
glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, y);
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height,
fmt->gl_format, fmt->gl_type, pixels);
glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0); glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0);
glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0); glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0);
GLES2_DEBUG_POP;
wl_shm_buffer_end_access(buffer);
GLES2_DEBUG_POP;
return true; return true;
} }
static bool gles2_texture_upload_drm(struct wlr_texture *wlr_texture, static void gles2_texture_destroy(struct wlr_texture *wlr_texture) {
struct wl_resource *buf) { if (wlr_texture == NULL) {
struct wlr_gles2_texture *tex = gles2_get_texture(wlr_texture); return;
if (!glEGLImageTargetTexture2DOES) {
return false;
} }
EGLint format; struct wlr_gles2_texture *texture = gles2_get_texture(wlr_texture);
if (!wlr_egl_query_buffer(tex->egl, buf, EGL_TEXTURE_FORMAT, &format)) {
wlr_log(L_INFO, "upload_drm called with no drm buffer");
return false;
}
wlr_egl_query_buffer(tex->egl, buf, EGL_WIDTH, wlr_egl_make_current(texture->egl, EGL_NO_SURFACE, NULL);
(EGLint*)&tex->wlr_texture.width);
wlr_egl_query_buffer(tex->egl, buf, EGL_HEIGHT,
(EGLint*)&tex->wlr_texture.height);
EGLint inverted_y; GLES2_DEBUG_PUSH;
if (wlr_egl_query_buffer(tex->egl, buf, EGL_WAYLAND_Y_INVERTED_WL,
&inverted_y)) { if (texture->image_tex) {
tex->wlr_texture.inverted_y = !!inverted_y; glDeleteTextures(1, &texture->image_tex);
}
if (texture->image) {
assert(eglDestroyImageKHR);
wlr_egl_destroy_image(texture->egl, texture->image);
} }
GLenum target; if (texture->type == WLR_GLES2_TEXTURE_GLTEX) {
const struct gles2_pixel_format *pf; glDeleteTextures(1, &texture->gl_tex);
switch (format) {
case EGL_TEXTURE_RGB:
case EGL_TEXTURE_RGBA:
target = GL_TEXTURE_2D;
pf = gles2_format_from_wl(WL_SHM_FORMAT_ARGB8888);
break;
case EGL_TEXTURE_EXTERNAL_WL:
target = GL_TEXTURE_EXTERNAL_OES;
pf = &external_pixel_format;
break;
default:
wlr_log(L_ERROR, "invalid/unsupported egl buffer format");
return false;
} }
GLES2_DEBUG_PUSH;
gles2_texture_ensure(tex, target);
glBindTexture(GL_TEXTURE_2D, tex->tex_id);
GLES2_DEBUG_POP; GLES2_DEBUG_POP;
EGLint attribs[] = { EGL_WAYLAND_PLANE_WL, 0, EGL_NONE }; free(texture);
}
static const struct wlr_texture_impl texture_impl = {
.get_size = gles2_texture_get_size,
.write_pixels = gles2_texture_write_pixels,
.destroy = gles2_texture_destroy,
};
struct wlr_texture *wlr_gles2_texture_from_pixels(struct wlr_egl *egl,
enum wl_shm_format wl_fmt, uint32_t stride, uint32_t width,
uint32_t height, const void *data) {
assert(wlr_egl_is_current(egl));
if (tex->image) { const struct gles2_pixel_format *fmt = gles2_format_from_wl(wl_fmt);
wlr_egl_destroy_image(tex->egl, tex->image); if (fmt == NULL) {
wlr_log(L_ERROR, "Unsupported pixel format %"PRIu32, wl_fmt);
return NULL;
} }
tex->image = wlr_egl_create_image(tex->egl, EGL_WAYLAND_BUFFER_WL, struct wlr_gles2_texture *texture =
(EGLClientBuffer*) buf, attribs); calloc(1, sizeof(struct wlr_gles2_texture));
if (!tex->image) { if (texture == NULL) {
wlr_log(L_ERROR, "failed to create EGL image"); wlr_log(L_ERROR, "Allocation failed");
return false; return NULL;
} }
wlr_texture_init(&texture->wlr_texture, &texture_impl);
texture->egl = egl;
texture->width = width;
texture->height = height;
texture->type = WLR_GLES2_TEXTURE_GLTEX;
texture->has_alpha = fmt->has_alpha;
GLES2_DEBUG_PUSH; GLES2_DEBUG_PUSH;
glActiveTexture(GL_TEXTURE0);
glBindTexture(target, tex->tex_id);
glEGLImageTargetTexture2DOES(target, tex->image);
GLES2_DEBUG_POP;
tex->wlr_texture.valid = true;
tex->pixel_format = pf;
return true;
}
static bool gles2_texture_upload_eglimage(struct wlr_texture *wlr_texture, glGenTextures(1, &texture->gl_tex);
EGLImageKHR image, uint32_t width, uint32_t height) { glBindTexture(GL_TEXTURE_2D, texture->gl_tex);
struct wlr_gles2_texture *tex = gles2_get_texture(wlr_texture);
tex->image = image; glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, stride / (fmt->bpp / 8));
tex->pixel_format = &external_pixel_format; glTexImage2D(GL_TEXTURE_2D, 0, fmt->gl_format, width, height, 0,
tex->wlr_texture.valid = true; fmt->gl_format, fmt->gl_type, data);
tex->wlr_texture.width = width; glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0);
tex->wlr_texture.height = height;
GLES2_DEBUG_PUSH;
gles2_texture_ensure(tex, GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_EXTERNAL_OES, tex->tex_id);
glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, tex->image);
GLES2_DEBUG_POP; GLES2_DEBUG_POP;
return &texture->wlr_texture;
return true;
} }
static bool gles2_texture_upload_dmabuf(struct wlr_texture *wlr_texture, struct wlr_texture *wlr_gles2_texture_from_wl_drm(struct wlr_egl *egl,
struct wl_resource *dmabuf_resource) { struct wl_resource *data) {
struct wlr_gles2_texture *tex = gles2_get_texture(wlr_texture); assert(wlr_egl_is_current(egl));
struct wlr_dmabuf_buffer *dmabuf =
wlr_dmabuf_buffer_from_buffer_resource(dmabuf_resource);
if (!tex->egl->egl_exts.dmabuf_import) { if (!glEGLImageTargetTexture2DOES) {
wlr_log(L_ERROR, "Want dmabuf but extension not present"); return NULL;
return false;
} }
tex->wlr_texture.width = dmabuf->attributes.width; struct wlr_gles2_texture *texture =
tex->wlr_texture.height = dmabuf->attributes.height; calloc(1, sizeof(struct wlr_gles2_texture));
if (texture == NULL) {
if (tex->image) { wlr_log(L_ERROR, "Allocation failed");
wlr_egl_destroy_image(tex->egl, tex->image); return NULL;
} }
wlr_texture_init(&texture->wlr_texture, &texture_impl);
texture->egl = egl;
texture->wl_drm = data;
if (wlr_dmabuf_buffer_has_inverted_y(dmabuf)) { EGLint fmt;
wlr_texture->inverted_y = true; texture->image = wlr_egl_create_image_from_wl_drm(egl, data, &fmt,
&texture->width, &texture->height, &texture->inverted_y);
if (texture->image == NULL) {
free(texture);
return NULL;
} }
GLenum target; GLenum target;
const struct gles2_pixel_format *pf; switch (fmt) {
if (dmabuf->attributes.n_planes > 1) { case EGL_TEXTURE_RGB:
target = GL_TEXTURE_EXTERNAL_OES; case EGL_TEXTURE_RGBA:
pf = &external_pixel_format;
} else {
target = GL_TEXTURE_2D; target = GL_TEXTURE_2D;
pf = gles2_format_from_wl(WL_SHM_FORMAT_ARGB8888); texture->type = WLR_GLES2_TEXTURE_WL_DRM_GL;
texture->has_alpha = fmt == EGL_TEXTURE_RGBA;
break;
case EGL_TEXTURE_EXTERNAL_WL:
target = GL_TEXTURE_EXTERNAL_OES;
texture->type = WLR_GLES2_TEXTURE_WL_DRM_EXT;
texture->has_alpha = true;
break;
default:
wlr_log(L_ERROR, "Invalid or unsupported EGL buffer format");
free(texture);
return NULL;
} }
GLES2_DEBUG_PUSH; GLES2_DEBUG_PUSH;
gles2_texture_ensure(tex, target);
glBindTexture(target, tex->tex_id);
tex->image = wlr_egl_create_image_from_dmabuf(tex->egl, &dmabuf->attributes);
glActiveTexture(GL_TEXTURE0);
glEGLImageTargetTexture2DOES(target, tex->image);
GLES2_DEBUG_POP;
tex->pixel_format = pf;
tex->wlr_texture.valid = true;
return true;
}
static bool gles2_texture_get_dmabuf_size(struct wlr_texture *texture, struct glGenTextures(1, &texture->image_tex);
wl_resource *resource, int *width, int *height) { glBindTexture(target, texture->image_tex);
if (!wlr_dmabuf_resource_is_buffer(resource)) { glEGLImageTargetTexture2DOES(target, texture->image);
return false;
}
struct wlr_dmabuf_buffer *dmabuf = GLES2_DEBUG_POP;
wlr_dmabuf_buffer_from_buffer_resource(resource); return &texture->wlr_texture;
*width = dmabuf->attributes.width;
*height = dmabuf->attributes.height;
return true;
} }
static void gles2_texture_get_buffer_size(struct wlr_texture *texture, struct wlr_texture *wlr_gles2_texture_from_dmabuf(struct wlr_egl *egl,
struct wl_resource *resource, int *width, int *height) { struct wlr_dmabuf_buffer_attribs *attribs) {
struct wl_shm_buffer *buffer = wl_shm_buffer_get(resource); assert(wlr_egl_is_current(egl));
if (!buffer) {
struct wlr_gles2_texture *tex = (struct wlr_gles2_texture *)texture;
if (!glEGLImageTargetTexture2DOES) { if (!glEGLImageTargetTexture2DOES) {
return; return NULL;
}
if (!wlr_egl_query_buffer(tex->egl, resource, EGL_WIDTH,
(EGLint*)width)) {
if (!gles2_texture_get_dmabuf_size(texture, resource, width,
height)) {
wlr_log(L_ERROR, "could not get size of the buffer");
return;
}
} }
wlr_egl_query_buffer(tex->egl, resource, EGL_HEIGHT, (EGLint*)height);
return; if (!egl->egl_exts.dmabuf_import) {
wlr_log(L_ERROR, "Cannot create DMA-BUF texture: EGL extension "
"unavailable");
return NULL;
} }
*width = wl_shm_buffer_get_width(buffer); struct wlr_gles2_texture *texture =
*height = wl_shm_buffer_get_height(buffer); calloc(1, sizeof(struct wlr_gles2_texture));
} if (texture == NULL) {
wlr_log(L_ERROR, "Allocation failed");
static void gles2_texture_destroy(struct wlr_texture *wlr_texture) { return NULL;
struct wlr_gles2_texture *texture = gles2_get_texture(wlr_texture);
wlr_signal_emit_safe(&texture->wlr_texture.destroy_signal,
&texture->wlr_texture);
if (texture->tex_id) {
GLES2_DEBUG_PUSH;
glDeleteTextures(1, &texture->tex_id);
GLES2_DEBUG_POP;
} }
wlr_texture_init(&texture->wlr_texture, &texture_impl);
if (texture->image) { texture->egl = egl;
wlr_egl_destroy_image(texture->egl, texture->image); texture->width = attribs->width;
texture->height = attribs->height;
texture->type = WLR_GLES2_TEXTURE_DMABUF;
texture->has_alpha = true;
texture->inverted_y =
(attribs->flags & WLR_DMABUF_BUFFER_ATTRIBS_FLAGS_Y_INVERT) != 0;
texture->image = wlr_egl_create_image_from_dmabuf(egl, attribs);
if (texture->image == NULL) {
free(texture);
return NULL;
} }
free(texture); GLES2_DEBUG_PUSH;
}
static const struct wlr_texture_impl texture_impl = { glGenTextures(1, &texture->image_tex);
.upload_pixels = gles2_texture_upload_pixels, glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture->image_tex);
.update_pixels = gles2_texture_update_pixels, glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, texture->image);
.upload_shm = gles2_texture_upload_shm,
.update_shm = gles2_texture_update_shm,
.upload_drm = gles2_texture_upload_drm,
.upload_dmabuf = gles2_texture_upload_dmabuf,
.upload_eglimage = gles2_texture_upload_eglimage,
.get_buffer_size = gles2_texture_get_buffer_size,
.destroy = gles2_texture_destroy,
};
struct wlr_texture *gles2_texture_create(struct wlr_egl *egl) { GLES2_DEBUG_POP;
struct wlr_gles2_texture *texture;
if (!(texture = calloc(1, sizeof(struct wlr_gles2_texture)))) {
return NULL;
}
wlr_texture_init(&texture->wlr_texture, &texture_impl);
texture->egl = egl;
return &texture->wlr_texture; return &texture->wlr_texture;
} }

@ -1,3 +1,4 @@
#include <assert.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include <wlr/render/interface.h> #include <wlr/render/interface.h>
@ -6,6 +7,15 @@
void wlr_renderer_init(struct wlr_renderer *renderer, void wlr_renderer_init(struct wlr_renderer *renderer,
const struct wlr_renderer_impl *impl) { const struct wlr_renderer_impl *impl) {
assert(impl->begin);
assert(impl->clear);
assert(impl->scissor);
assert(impl->render_texture_with_matrix);
assert(impl->render_quad_with_matrix);
assert(impl->render_ellipse_with_matrix);
assert(impl->formats);
assert(impl->format_supported);
assert(impl->texture_from_pixels);
renderer->impl = impl; renderer->impl = impl;
} }
@ -22,7 +32,9 @@ void wlr_renderer_begin(struct wlr_renderer *r, int width, int height) {
} }
void wlr_renderer_end(struct wlr_renderer *r) { void wlr_renderer_end(struct wlr_renderer *r) {
if (r->impl->end) {
r->impl->end(r); r->impl->end(r);
}
} }
void wlr_renderer_clear(struct wlr_renderer *r, const float color[static 4]) { void wlr_renderer_clear(struct wlr_renderer *r, const float color[static 4]) {
@ -33,16 +45,10 @@ void wlr_renderer_scissor(struct wlr_renderer *r, struct wlr_box *box) {
r->impl->scissor(r, box); r->impl->scissor(r, box);
} }
struct wlr_texture *wlr_render_texture_create(struct wlr_renderer *r) {
return r->impl->texture_create(r);
}
bool wlr_render_texture(struct wlr_renderer *r, struct wlr_texture *texture, bool wlr_render_texture(struct wlr_renderer *r, struct wlr_texture *texture,
const float projection[static 9], int x, int y, float alpha) { const float projection[static 9], int x, int y, float alpha) {
const struct wlr_box box = { struct wlr_box box = { .x = x, .y = y };
.x = x, .y = y, wlr_texture_get_size(texture, &box.width, &box.height);
.width = texture->width, .height = texture->height,
};
float matrix[9]; float matrix[9];
wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0,
@ -90,15 +96,29 @@ const enum wl_shm_format *wlr_renderer_get_formats(
return r->impl->formats(r, len); return r->impl->formats(r, len);
} }
bool wlr_renderer_buffer_is_drm(struct wlr_renderer *r, bool wlr_renderer_resource_is_wl_drm_buffer(struct wlr_renderer *r,
struct wl_resource *buffer) { struct wl_resource *resource) {
return r->impl->buffer_is_drm(r, buffer); if (!r->impl->resource_is_wl_drm_buffer) {
return false;
}
return r->impl->resource_is_wl_drm_buffer(r, resource);
}
void wlr_renderer_wl_drm_buffer_get_size(struct wlr_renderer *r,
struct wl_resource *buffer, int *width, int *height) {
if (!r->impl->wl_drm_buffer_get_size) {
return;
}
return r->impl->wl_drm_buffer_get_size(r, buffer, width, height);
} }
bool wlr_renderer_read_pixels(struct wlr_renderer *r, enum wl_shm_format fmt, 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 stride, uint32_t width, uint32_t height,
uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y, uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y,
void *data) { void *data) {
if (!r->impl->read_pixels) {
return false;
}
return r->impl->read_pixels(r, fmt, stride, width, height, src_x, src_y, return r->impl->read_pixels(r, fmt, stride, width, height, src_x, src_y,
dst_x, dst_y, data); dst_x, dst_y, data);
} }

@ -1,3 +1,4 @@
#include <assert.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include <wlr/render/interface.h> #include <wlr/render/interface.h>
@ -5,8 +6,9 @@
void wlr_texture_init(struct wlr_texture *texture, void wlr_texture_init(struct wlr_texture *texture,
const struct wlr_texture_impl *impl) { const struct wlr_texture_impl *impl) {
assert(impl->get_size);
assert(impl->write_pixels);
texture->impl = impl; texture->impl = impl;
wl_signal_init(&texture->destroy_signal);
} }
void wlr_texture_destroy(struct wlr_texture *texture) { void wlr_texture_destroy(struct wlr_texture *texture) {
@ -17,45 +19,38 @@ void wlr_texture_destroy(struct wlr_texture *texture) {
} }
} }
bool wlr_texture_upload_pixels(struct wlr_texture *texture, uint32_t format, struct wlr_texture *wlr_texture_from_pixels(struct wlr_renderer *renderer,
int stride, int width, int height, const unsigned char *pixels) { enum wl_shm_format wl_fmt, uint32_t stride, uint32_t width,
return texture->impl->upload_pixels(texture, format, stride, uint32_t height, const void *data) {
width, height, pixels); return renderer->impl->texture_from_pixels(renderer, wl_fmt, stride, width,
height, data);
} }
bool wlr_texture_update_pixels(struct wlr_texture *texture, struct wlr_texture *wlr_texture_from_wl_drm(struct wlr_renderer *renderer,
enum wl_shm_format format, int stride, int x, int y, struct wl_resource *data) {
int width, int height, const unsigned char *pixels) { if (!renderer->impl->texture_from_wl_drm) {
return texture->impl->update_pixels(texture, format, stride, x, y, return NULL;
width, height, pixels); }
} return renderer->impl->texture_from_wl_drm(renderer, data);
bool wlr_texture_upload_shm(struct wlr_texture *texture, uint32_t format,
struct wl_shm_buffer *shm) {
return texture->impl->upload_shm(texture, format, shm);
}
bool wlr_texture_update_shm(struct wlr_texture *texture, uint32_t format,
int x, int y, int width, int height, struct wl_shm_buffer *shm) {
return texture->impl->update_shm(texture, format, x, y, width, height, shm);
}
bool wlr_texture_upload_drm(struct wlr_texture *texture,
struct wl_resource *drm_buffer) {
return texture->impl->upload_drm(texture, drm_buffer);
} }
bool wlr_texture_upload_eglimage(struct wlr_texture *texture, struct wlr_texture *wlr_texture_from_dmabuf(struct wlr_renderer *renderer,
EGLImageKHR image, uint32_t width, uint32_t height) { struct wlr_dmabuf_buffer_attribs *attribs) {
return texture->impl->upload_eglimage(texture, image, width, height); if (!renderer->impl->texture_from_dmabuf) {
return NULL;
}
return renderer->impl->texture_from_dmabuf(renderer, attribs);
} }
bool wlr_texture_upload_dmabuf(struct wlr_texture *texture, void wlr_texture_get_size(struct wlr_texture *texture, int *width,
struct wl_resource *dmabuf_resource) { int *height) {
return texture->impl->upload_dmabuf(texture, dmabuf_resource); return texture->impl->get_size(texture, width, height);
} }
void wlr_texture_get_buffer_size(struct wlr_texture *texture, struct wl_resource bool wlr_texture_write_pixels(struct wlr_texture *texture,
*resource, int *width, int *height) { enum wl_shm_format wl_fmt, uint32_t stride, uint32_t width,
texture->impl->get_buffer_size(texture, resource, width, height); uint32_t height, uint32_t src_x, uint32_t src_y, uint32_t dst_x,
uint32_t dst_y, const void *data) {
return texture->impl->write_pixels(texture, wl_fmt, stride, width, height,
src_x, src_y, dst_x, dst_y, data);
} }

@ -10,6 +10,7 @@
#include <dev/evdev/input-event-codes.h> #include <dev/evdev/input-event-codes.h>
#endif #endif
#include "rootston/cursor.h" #include "rootston/cursor.h"
#include "rootston/desktop.h"
#include "rootston/xcursor.h" #include "rootston/xcursor.h"
struct roots_cursor *roots_cursor_create(struct roots_seat *seat) { struct roots_cursor *roots_cursor_create(struct roots_seat *seat) {
@ -98,42 +99,40 @@ static void seat_view_deco_button(struct roots_seat_view *view, double sx,
} }
} }
static void roots_cursor_update_position(struct roots_cursor *cursor, static void roots_passthrough_cursor(struct roots_cursor *cursor,
uint32_t time) { uint32_t time) {
struct roots_desktop *desktop = cursor->seat->input->server->desktop;
struct roots_seat *seat = cursor->seat;
struct roots_view *view;
struct wlr_surface *surface = NULL;
double sx, sy; double sx, sy;
switch (cursor->mode) { struct roots_view *view = NULL;
case ROOTS_CURSOR_PASSTHROUGH: struct roots_seat *seat = cursor->seat;
view = desktop_view_at(desktop, cursor->cursor->x, cursor->cursor->y, struct roots_desktop *desktop = seat->input->server->desktop;
&surface, &sx, &sy); struct wlr_surface *surface = desktop_surface_at(desktop,
cursor->cursor->x, cursor->cursor->y, &sx, &sy, &view);
struct wl_client *client = NULL;
if (surface) {
client = wl_resource_get_client(surface->resource);
}
if (cursor->cursor_client != client) {
wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager,
cursor->default_xcursor, cursor->cursor);
cursor->cursor_client = client;
}
if (view) {
struct roots_seat_view *seat_view = struct roots_seat_view *seat_view =
roots_seat_view_from_view(seat, view); roots_seat_view_from_view(seat, view);
if (cursor->pointer_view && (surface || seat_view != cursor->pointer_view)) { if (cursor->pointer_view && (surface ||
seat_view != cursor->pointer_view)) {
seat_view_deco_leave(cursor->pointer_view); seat_view_deco_leave(cursor->pointer_view);
cursor->pointer_view = NULL; cursor->pointer_view = NULL;
} }
bool set_compositor_cursor = !view && !surface && cursor->cursor_client; if (!surface) {
if (view && surface) {
struct wl_client *view_client =
wl_resource_get_client(view->wlr_surface->resource);
set_compositor_cursor = view_client != cursor->cursor_client;
}
if (set_compositor_cursor) {
wlr_xcursor_manager_set_cursor_image(cursor->xcursor_manager,
cursor->default_xcursor, cursor->cursor);
cursor->cursor_client = NULL;
}
if (view && !surface) {
if (seat_view) {
cursor->pointer_view = seat_view; cursor->pointer_view = seat_view;
seat_view_deco_motion(seat_view, sx, sy); seat_view_deco_motion(seat_view, sx, sy);
} }
} }
if (view && surface) {
// motion over a view surface if (surface) {
wlr_seat_pointer_notify_enter(seat->seat, surface, sx, sy); wlr_seat_pointer_notify_enter(seat->seat, surface, sx, sy);
wlr_seat_pointer_notify_motion(seat->seat, time, sx, sy); wlr_seat_pointer_notify_motion(seat->seat, time, sx, sy);
} else { } else {
@ -144,6 +143,15 @@ static void roots_cursor_update_position(struct roots_cursor *cursor,
wl_list_for_each(drag_icon, &seat->drag_icons, link) { wl_list_for_each(drag_icon, &seat->drag_icons, link) {
roots_drag_icon_update_position(drag_icon); roots_drag_icon_update_position(drag_icon);
} }
}
static void roots_cursor_update_position(
struct roots_cursor *cursor, uint32_t time) {
struct roots_seat *seat = cursor->seat;
struct roots_view *view;
switch (cursor->mode) {
case ROOTS_CURSOR_PASSTHROUGH:
roots_passthrough_cursor(cursor, time);
break; break;
case ROOTS_CURSOR_MOVE: case ROOTS_CURSOR_MOVE:
view = roots_seat_get_focus(seat); view = roots_seat_get_focus(seat);
@ -181,15 +189,9 @@ static void roots_cursor_update_position(struct roots_cursor *cursor,
} else if (cursor->resize_edges & WLR_EDGE_RIGHT) { } else if (cursor->resize_edges & WLR_EDGE_RIGHT) {
width += dx; width += dx;
} }
view_move_resize(view, x, y,
if (width < 1) { width < 1 ? 1 : width,
width = 1; height < 1 ? 1 : height);
}
if (height < 1) {
height = 1;
}
view_move_resize(view, x, y, width, height);
} }
break; break;
case ROOTS_CURSOR_ROTATE: case ROOTS_CURSOR_ROTATE:
@ -215,15 +217,15 @@ static void roots_cursor_press_button(struct roots_cursor *cursor,
uint32_t state, double lx, double ly) { uint32_t state, double lx, double ly) {
struct roots_seat *seat = cursor->seat; struct roots_seat *seat = cursor->seat;
struct roots_desktop *desktop = seat->input->server->desktop; struct roots_desktop *desktop = seat->input->server->desktop;
bool is_touch = device->type == WLR_INPUT_DEVICE_TOUCH; bool is_touch = device->type == WLR_INPUT_DEVICE_TOUCH;
struct wlr_surface *surface = NULL;
double sx, sy; double sx, sy;
struct roots_view *view = struct roots_view *view;
desktop_view_at(desktop, lx, ly, &surface, &sx, &sy); struct wlr_surface *surface = desktop_surface_at(desktop,
lx, ly, &sx, &sy, &view);
if (state == WLR_BUTTON_PRESSED && if (state == WLR_BUTTON_PRESSED && view &&
view &&
roots_seat_has_meta_pressed(seat)) { roots_seat_has_meta_pressed(seat)) {
roots_seat_set_focus(seat, view); roots_seat_set_focus(seat, view);
@ -251,11 +253,9 @@ static void roots_cursor_press_button(struct roots_cursor *cursor,
break; break;
} }
} else { } else {
if (view && !surface && cursor->pointer_view) {
if (view && !surface) { seat_view_deco_button(cursor->pointer_view,
if (cursor->pointer_view) { sx, sy, button, state);
seat_view_deco_button(cursor->pointer_view, sx, sy, button, state);
}
} }
if (state == WLR_BUTTON_RELEASED && if (state == WLR_BUTTON_RELEASED &&
@ -289,8 +289,8 @@ void roots_cursor_handle_motion(struct roots_cursor *cursor,
void roots_cursor_handle_motion_absolute(struct roots_cursor *cursor, void roots_cursor_handle_motion_absolute(struct roots_cursor *cursor,
struct wlr_event_pointer_motion_absolute *event) { struct wlr_event_pointer_motion_absolute *event) {
wlr_cursor_warp_absolute(cursor->cursor, event->device, wlr_cursor_warp_absolute(cursor->cursor,
event->x_mm / event->width_mm, event->y_mm / event->height_mm); event->device, event->x, event->y);
roots_cursor_update_position(cursor, event->time_msec); roots_cursor_update_position(cursor, event->time_msec);
} }
@ -309,17 +309,15 @@ void roots_cursor_handle_axis(struct roots_cursor *cursor,
void roots_cursor_handle_touch_down(struct roots_cursor *cursor, void roots_cursor_handle_touch_down(struct roots_cursor *cursor,
struct wlr_event_touch_down *event) { struct wlr_event_touch_down *event) {
struct roots_desktop *desktop = cursor->seat->input->server->desktop; struct roots_desktop *desktop = cursor->seat->input->server->desktop;
struct wlr_surface *surface = NULL;
double lx, ly; double lx, ly;
bool result = bool result = wlr_cursor_absolute_to_layout_coords(cursor->cursor,
wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, event->x, event->y, &lx, &ly);
event->device, event->x_mm, event->y_mm, event->width_mm,
event->height_mm, &lx, &ly);
if (!result) { if (!result) {
return; return;
} }
double sx, sy; double sx, sy;
desktop_view_at(desktop, lx, ly, &surface, &sx, &sy); struct wlr_surface *surface = desktop_surface_at(
desktop, lx, ly, &sx, &sy, NULL);
uint32_t serial = 0; uint32_t serial = 0;
if (surface) { if (surface) {
@ -362,18 +360,16 @@ void roots_cursor_handle_touch_motion(struct roots_cursor *cursor,
return; return;
} }
struct wlr_surface *surface = NULL;
double lx, ly; double lx, ly;
bool result = bool result = wlr_cursor_absolute_to_layout_coords(cursor->cursor,
wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, event->x, event->y, &lx, &ly);
event->device, event->x_mm, event->y_mm, event->width_mm,
event->height_mm, &lx, &ly);
if (!result) { if (!result) {
return; return;
} }
double sx, sy; double sx, sy;
desktop_view_at(desktop, lx, ly, &surface, &sx, &sy); struct wlr_surface *surface = desktop_surface_at(
desktop, lx, ly, &sx, &sy, NULL);
if (surface) { if (surface) {
wlr_seat_touch_point_focus(cursor->seat->seat, surface, wlr_seat_touch_point_focus(cursor->seat->seat, surface,
@ -396,15 +392,13 @@ void roots_cursor_handle_tool_axis(struct roots_cursor *cursor,
if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X) && if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X) &&
(event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) { (event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) {
wlr_cursor_warp_absolute(cursor->cursor, event->device, wlr_cursor_warp_absolute(cursor->cursor, event->device,
event->x_mm / event->width_mm, event->y_mm / event->height_mm); event->x, event->y);
roots_cursor_update_position(cursor, event->time_msec); roots_cursor_update_position(cursor, event->time_msec);
} else if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X)) { } else if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X)) {
wlr_cursor_warp_absolute(cursor->cursor, event->device, wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, -1);
event->x_mm / event->width_mm, -1);
roots_cursor_update_position(cursor, event->time_msec); roots_cursor_update_position(cursor, event->time_msec);
} else if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) { } else if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) {
wlr_cursor_warp_absolute(cursor->cursor, event->device, wlr_cursor_warp_absolute(cursor->cursor, event->device, -1, event->y);
-1, event->y_mm / event->height_mm);
roots_cursor_update_position(cursor, event->time_msec); roots_cursor_update_position(cursor, event->time_msec);
} }
} }
@ -432,7 +426,6 @@ void roots_cursor_handle_request_set_cursor(struct roots_cursor *cursor,
return; return;
} }
wlr_log(L_DEBUG, "Setting client cursor");
wlr_cursor_set_surface(cursor->cursor, event->surface, event->hotspot_x, wlr_cursor_set_surface(cursor->cursor, event->surface, event->hotspot_x,
event->hotspot_y); event->hotspot_y);
cursor->cursor_client = event->seat_client->client; cursor->cursor_client = event->seat_client->client;

@ -9,9 +9,10 @@
#include <wlr/types/wlr_cursor.h> #include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_gamma_control.h> #include <wlr/types/wlr_gamma_control.h>
#include <wlr/types/wlr_idle.h> #include <wlr/types/wlr_idle.h>
#include <wlr/types/wlr_idle_inhibit_v1.h>
#include <wlr/types/wlr_layer_shell.h>
#include <wlr/types/wlr_linux_dmabuf.h> #include <wlr/types/wlr_linux_dmabuf.h>
#include <wlr/types/wlr_output_layout.h> #include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_idle_inhibit_v1.h>
#include <wlr/types/wlr_primary_selection.h> #include <wlr/types/wlr_primary_selection.h>
#include <wlr/types/wlr_server_decoration.h> #include <wlr/types/wlr_server_decoration.h>
#include <wlr/types/wlr_wl_shell.h> #include <wlr/types/wlr_wl_shell.h>
@ -19,10 +20,12 @@
#include <wlr/types/wlr_xdg_shell_v6.h> #include <wlr/types/wlr_xdg_shell_v6.h>
#include <wlr/types/wlr_xdg_shell.h> #include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include "rootston/layers.h"
#include "rootston/seat.h" #include "rootston/seat.h"
#include "rootston/server.h" #include "rootston/server.h"
#include "rootston/view.h" #include "rootston/view.h"
#include "rootston/xcursor.h" #include "rootston/xcursor.h"
#include "wlr-layer-shell-unstable-v1-protocol.h"
struct roots_view *view_create(struct roots_desktop *desktop) { struct roots_view *view_create(struct roots_desktop *desktop) {
struct roots_view *view = calloc(1, sizeof(struct roots_view)); struct roots_view *view = calloc(1, sizeof(struct roots_view));
@ -187,6 +190,25 @@ static struct wlr_output *view_get_output(struct roots_view *view) {
output_y); output_y);
} }
void view_arrange_maximized(struct roots_view *view) {
struct wlr_box view_box;
view_get_box(view, &view_box);
struct wlr_output *output = view_get_output(view);
struct roots_output *roots_output = output->data;
struct wlr_box *output_box =
wlr_output_layout_get_box(view->desktop->layout, output);
struct wlr_box usable_area;
memcpy(&usable_area, &roots_output->usable_area,
sizeof(struct wlr_box));
usable_area.x += output_box->x;
usable_area.y += output_box->y;
view_move_resize(view, usable_area.x, usable_area.y,
usable_area.width, usable_area.height);
view_rotate(view, 0);
}
void view_maximize(struct roots_view *view, bool maximized) { void view_maximize(struct roots_view *view, bool maximized) {
if (view->maximized == maximized) { if (view->maximized == maximized) {
return; return;
@ -197,23 +219,14 @@ void view_maximize(struct roots_view *view, bool maximized) {
} }
if (!view->maximized && maximized) { if (!view->maximized && maximized) {
struct wlr_box view_box;
view_get_box(view, &view_box);
view->maximized = true; view->maximized = true;
view->saved.x = view->x; view->saved.x = view->x;
view->saved.y = view->y; view->saved.y = view->y;
view->saved.rotation = view->rotation; view->saved.rotation = view->rotation;
view->saved.width = view_box.width; view->saved.width = view->width;
view->saved.height = view_box.height; view->saved.height = view->height;
struct wlr_output *output = view_get_output(view);
struct wlr_box *output_box =
wlr_output_layout_get_box(view->desktop->layout, output);
view_move_resize(view, output_box->x, output_box->y, output_box->width, view_arrange_maximized(view);
output_box->height);
view_rotate(view, 0);
} }
if (view->maximized && !maximized) { if (view->maximized && !maximized) {
@ -609,9 +622,7 @@ static bool view_at(struct roots_view *view, double lx, double ly,
return true; return true;
} }
if (wlr_box_contains_point(&box, view_sx, view_sy) && if (wlr_surface_point_accepts_input(view->wlr_surface, view_sx, view_sy)) {
pixman_region32_contains_point(&view->wlr_surface->current->input,
view_sx, view_sy, NULL)) {
*sx = view_sx; *sx = view_sx;
*sy = view_sy; *sy = view_sy;
*surface = view->wlr_surface; *surface = view->wlr_surface;
@ -621,8 +632,9 @@ static bool view_at(struct roots_view *view, double lx, double ly,
return false; return false;
} }
struct roots_view *desktop_view_at(struct roots_desktop *desktop, double lx, static struct roots_view *desktop_view_at(struct roots_desktop *desktop,
double ly, struct wlr_surface **surface, double *sx, double *sy) { double lx, double ly, struct wlr_surface **surface,
double *sx, double *sy) {
struct wlr_output *wlr_output = struct wlr_output *wlr_output =
wlr_output_layout_output_at(desktop->layout, lx, ly); wlr_output_layout_output_at(desktop->layout, lx, ly);
if (wlr_output != NULL) { if (wlr_output != NULL) {
@ -646,6 +658,75 @@ struct roots_view *desktop_view_at(struct roots_desktop *desktop, double lx,
return NULL; return NULL;
} }
static struct wlr_surface *layer_surface_at(struct roots_output *output,
struct wl_list *layer, double ox, double oy, double *sx, double *sy) {
struct roots_layer_surface *roots_surface;
wl_list_for_each_reverse(roots_surface, layer, link) {
struct wlr_surface *wlr_surface =
roots_surface->layer_surface->surface;
double _sx = ox - roots_surface->geo.x;
double _sy = oy - roots_surface->geo.y;
// TODO: Test popups/subsurfaces
if (wlr_surface_point_accepts_input(wlr_surface, _sx, _sy)) {
*sx = _sx;
*sy = _sy;
return wlr_surface;
}
}
return NULL;
}
struct wlr_surface *desktop_surface_at(struct roots_desktop *desktop,
double lx, double ly, double *sx, double *sy,
struct roots_view **view) {
struct wlr_surface *surface = NULL;
struct wlr_output *wlr_output =
wlr_output_layout_output_at(desktop->layout, lx, ly);
struct roots_output *roots_output = NULL;
double ox = lx, oy = ly;
if (view) {
*view = NULL;
}
if (wlr_output) {
roots_output = wlr_output->data;
wlr_output_layout_output_coords(desktop->layout, wlr_output, &ox, &oy);
if ((surface = layer_surface_at(roots_output,
&roots_output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
ox, oy, sx, sy))) {
return surface;
}
if ((surface = layer_surface_at(roots_output,
&roots_output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
ox, oy, sx, sy))) {
return surface;
}
}
struct roots_view *_view;
if ((_view = desktop_view_at(desktop, lx, ly, &surface, sx, sy))) {
if (view) {
*view = _view;
}
return surface;
}
if (wlr_output) {
if ((surface = layer_surface_at(roots_output,
&roots_output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
ox, oy, sx, sy))) {
return surface;
}
if ((surface = layer_surface_at(roots_output,
&roots_output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
ox, oy, sx, sy))) {
return surface;
}
}
return NULL;
}
static void handle_layout_change(struct wl_listener *listener, void *data) { static void handle_layout_change(struct wl_listener *listener, void *data) {
struct roots_desktop *desktop = struct roots_desktop *desktop =
wl_container_of(listener, desktop, layout_change); wl_container_of(listener, desktop, layout_change);
@ -714,6 +795,11 @@ struct roots_desktop *desktop_create(struct roots_server *server,
&desktop->wl_shell_surface); &desktop->wl_shell_surface);
desktop->wl_shell_surface.notify = handle_wl_shell_surface; desktop->wl_shell_surface.notify = handle_wl_shell_surface;
desktop->layer_shell = wlr_layer_shell_create(server->wl_display);
wl_signal_add(&desktop->layer_shell->events.new_surface,
&desktop->layer_shell_surface);
desktop->layer_shell_surface.notify = handle_layer_shell_surface;
#ifdef WLR_HAS_XWAYLAND #ifdef WLR_HAS_XWAYLAND
const char *cursor_theme = NULL; const char *cursor_theme = NULL;
const char *cursor_default = ROOTS_XCURSOR_DEFAULT; const char *cursor_default = ROOTS_XCURSOR_DEFAULT;
@ -750,7 +836,7 @@ struct roots_desktop *desktop_create(struct roots_server *server,
if (xcursor != NULL) { if (xcursor != NULL) {
struct wlr_xcursor_image *image = xcursor->images[0]; struct wlr_xcursor_image *image = xcursor->images[0];
wlr_xwayland_set_cursor(desktop->xwayland, image->buffer, wlr_xwayland_set_cursor(desktop->xwayland, image->buffer,
image->width, image->width, image->height, image->hotspot_x, image->width * 4, image->width, image->height, image->hotspot_x,
image->hotspot_y); image->hotspot_y);
} }
} }

@ -0,0 +1,315 @@
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <wayland-server.h>
#include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_surface.h>
#include <wlr/types/wlr_layer_shell.h>
#include <wlr/util/log.h>
#include "rootston/desktop.h"
#include "rootston/layers.h"
#include "rootston/output.h"
#include "rootston/server.h"
static void apply_exclusive(struct wlr_box *usable_area,
uint32_t anchor, int32_t exclusive,
int32_t margin_top, int32_t margin_right,
int32_t margin_bottom, int32_t margin_left) {
if (exclusive <= 0) {
return;
}
struct {
uint32_t anchors;
int *positive_axis;
int *negative_axis;
int margin;
} edges[] = {
{
.anchors =
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP,
.positive_axis = &usable_area->y,
.negative_axis = &usable_area->height,
.margin = margin_top,
},
{
.anchors =
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
.positive_axis = NULL,
.negative_axis = &usable_area->height,
.margin = margin_bottom,
},
{
.anchors =
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
.positive_axis = &usable_area->x,
.negative_axis = &usable_area->width,
.margin = margin_left,
},
{
.anchors =
ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
.positive_axis = NULL,
.negative_axis = &usable_area->width,
.margin = margin_right,
},
};
for (size_t i = 0; i < sizeof(edges) / sizeof(edges[0]); ++i) {
if ((anchor & edges[i].anchors) == edges[i].anchors) {
if (edges[i].positive_axis) {
*edges[i].positive_axis += exclusive + edges[i].margin;
}
if (edges[i].negative_axis) {
*edges[i].negative_axis -= exclusive + edges[i].margin;
}
}
}
}
static void arrange_layer(struct wlr_output *output, struct wl_list *list,
struct wlr_box *usable_area, bool exclusive) {
struct roots_layer_surface *roots_surface;
struct wlr_box full_area = { 0 };
wlr_output_effective_resolution(output,
&full_area.width, &full_area.height);
wl_list_for_each(roots_surface, list, link) {
struct wlr_layer_surface *layer = roots_surface->layer_surface;
struct wlr_layer_surface_state *state = &layer->current;
if (exclusive != (state->exclusive_zone > 0)) {
continue;
}
struct wlr_box bounds;
if (state->exclusive_zone == -1) {
bounds = full_area;
} else {
bounds = *usable_area;
}
struct wlr_box box = {
.width = state->desired_width,
.height = state->desired_height
};
// Horizontal axis
const uint32_t both_horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
| ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
if ((state->anchor & both_horiz) && box.width == 0) {
box.x = bounds.x;
box.width = bounds.width;
} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
box.x = bounds.x;
} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
box.x = bounds.x + (bounds.width - box.width);
} else {
box.x = bounds.x + ((bounds.width / 2) - (box.width / 2));
}
// Vertical axis
const uint32_t both_vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
| ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
if ((state->anchor & both_vert) && box.height == 0) {
box.y = bounds.y;
box.height = bounds.height;
} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
box.y = bounds.y;
} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
box.y = bounds.y + (bounds.height - box.height);
} else {
box.y = bounds.y + ((bounds.height / 2) - (box.height / 2));
}
// Margin
if ((state->anchor & both_horiz) == both_horiz) {
box.x += state->margin.left;
box.width -= state->margin.left + state->margin.right;
} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
box.x += state->margin.left;
} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
box.x -= state->margin.right;
}
if ((state->anchor & both_vert) == both_vert) {
box.y += state->margin.top;
box.height -= state->margin.top + state->margin.bottom;
} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
box.y += state->margin.top;
} else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
box.y -= state->margin.bottom;
}
if (box.width < 0 || box.height < 0) {
// TODO: Bubble up a protocol error?
wlr_layer_surface_close(layer);
continue;
}
// Apply
roots_surface->geo = box;
apply_exclusive(usable_area, state->anchor, state->exclusive_zone,
state->margin.top, state->margin.right,
state->margin.bottom, state->margin.left);
wlr_layer_surface_configure(layer, box.width, box.height);
}
}
void arrange_layers(struct roots_output *output) {
struct wlr_box usable_area = { 0 };
wlr_output_effective_resolution(output->wlr_output,
&usable_area.width, &usable_area.height);
// Arrange exclusive surfaces from top->bottom
arrange_layer(output->wlr_output,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
&usable_area, true);
arrange_layer(output->wlr_output,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
&usable_area, true);
arrange_layer(output->wlr_output,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
&usable_area, true);
arrange_layer(output->wlr_output,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
&usable_area, true);
memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box));
struct roots_view *view;
wl_list_for_each(view, &output->desktop->views, link) {
if (view->maximized) {
view_arrange_maximized(view);
}
}
// Arrange non-exlusive surfaces from top->bottom
arrange_layer(output->wlr_output,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
&usable_area, false);
arrange_layer(output->wlr_output,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
&usable_area, false);
arrange_layer(output->wlr_output,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
&usable_area, false);
arrange_layer(output->wlr_output,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
&usable_area, false);
}
static void handle_output_destroy(struct wl_listener *listener, void *data) {
struct roots_layer_surface *layer =
wl_container_of(listener, layer, output_destroy);
layer->layer_surface->output = NULL;
wl_list_remove(&layer->output_destroy.link);
wlr_layer_surface_close(layer->layer_surface);
}
static void handle_surface_commit(struct wl_listener *listener, void *data) {
struct roots_layer_surface *layer =
wl_container_of(listener, layer, surface_commit);
struct wlr_layer_surface *layer_surface = layer->layer_surface;
struct wlr_output *wlr_output = layer_surface->output;
if (wlr_output != NULL) {
struct roots_output *output = wlr_output->data;
struct wlr_box old_geo = layer->geo;
arrange_layers(output);
if (memcmp(&old_geo, &layer->geo, sizeof(struct wlr_box)) != 0) {
output_damage_whole_local_surface(output, layer_surface->surface,
old_geo.x, old_geo.y, 0);
output_damage_whole_local_surface(output, layer_surface->surface,
layer->geo.x, layer->geo.y, 0);
} else {
output_damage_from_local_surface(output, layer_surface->surface,
layer->geo.x, layer->geo.y, 0);
}
}
}
static void unmap(struct wlr_layer_surface *layer_surface) {
struct roots_layer_surface *layer = layer_surface->data;
struct wlr_output *wlr_output = layer_surface->output;
if (wlr_output != NULL) {
struct roots_output *output = wlr_output->data;
wlr_output_damage_add_box(output->damage, &layer->geo);
}
}
static void handle_destroy(struct wl_listener *listener, void *data) {
struct roots_layer_surface *layer = wl_container_of(
listener, layer, destroy);
if (layer->layer_surface->mapped) {
unmap(layer->layer_surface);
}
wl_list_remove(&layer->link);
wl_list_remove(&layer->destroy.link);
wl_list_remove(&layer->map.link);
wl_list_remove(&layer->unmap.link);
wl_list_remove(&layer->surface_commit.link);
wl_list_remove(&layer->output_destroy.link);
arrange_layers((struct roots_output *)layer->layer_surface->output->data);
free(layer);
}
static void handle_map(struct wl_listener *listener, void *data) {
struct wlr_layer_surface *layer_surface = data;
struct roots_layer_surface *layer = layer_surface->data;
struct wlr_output *wlr_output = layer_surface->output;
struct roots_output *output = wlr_output->data;
wlr_output_damage_add_box(output->damage, &layer->geo);
}
static void handle_unmap(struct wl_listener *listener, void *data) {
struct roots_layer_surface *layer = wl_container_of(
listener, layer, unmap);
unmap(layer->layer_surface);
}
void handle_layer_shell_surface(struct wl_listener *listener, void *data) {
struct wlr_layer_surface *layer_surface = data;
struct roots_desktop *desktop =
wl_container_of(listener, desktop, layer_shell_surface);
wlr_log(L_DEBUG, "new layer surface: namespace %s layer %d anchor %d "
"size %dx%d margin %d,%d,%d,%d",
layer_surface->namespace, layer_surface->layer, layer_surface->layer,
layer_surface->client_pending.desired_width,
layer_surface->client_pending.desired_height,
layer_surface->client_pending.margin.top,
layer_surface->client_pending.margin.right,
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;
}
roots_surface->surface_commit.notify = handle_surface_commit;
wl_signal_add(&layer_surface->surface->events.commit,
&roots_surface->surface_commit);
roots_surface->output_destroy.notify = handle_output_destroy;
wl_signal_add(&layer_surface->output->events.destroy,
&roots_surface->output_destroy);
roots_surface->destroy.notify = handle_destroy;
wl_signal_add(&layer_surface->events.destroy, &roots_surface->destroy);
roots_surface->map.notify = handle_map;
wl_signal_add(&layer_surface->events.map, &roots_surface->map);
roots_surface->unmap.notify = handle_unmap;
wl_signal_add(&layer_surface->events.unmap, &roots_surface->unmap);
// TODO: Listen for subsurfaces
roots_surface->layer_surface = layer_surface;
layer_surface->data = roots_surface;
struct roots_output *output = layer_surface->output->data;
wl_list_insert(&output->layers[layer_surface->layer], &roots_surface->link);
// Temporarily set the layer's current state to client_pending
// So that we can easily arrange it
struct wlr_layer_surface_state old_state = layer_surface->current;
layer_surface->current = layer_surface->client_pending;
arrange_layers(output);
layer_surface->current = old_state;
}

@ -5,6 +5,7 @@ sources = [
'ini.c', 'ini.c',
'input.c', 'input.c',
'keyboard.c', 'keyboard.c',
'layer_shell.c',
'main.c', 'main.c',
'output.c', 'output.c',
'seat.c', 'seat.c',

@ -13,6 +13,7 @@
#include <wlr/util/log.h> #include <wlr/util/log.h>
#include <wlr/util/region.h> #include <wlr/util/region.h>
#include "rootston/config.h" #include "rootston/config.h"
#include "rootston/layers.h"
#include "rootston/output.h" #include "rootston/output.h"
#include "rootston/server.h" #include "rootston/server.h"
@ -417,6 +418,33 @@ static void surface_send_frame_done(struct wlr_surface *surface, double lx,
wlr_surface_send_frame_done(surface, when); wlr_surface_send_frame_done(surface, when);
} }
static void render_layer(
struct roots_output *output,
const struct wlr_box *output_layout_box,
struct render_data *data,
struct wl_list *layer) {
struct roots_layer_surface *roots_surface;
wl_list_for_each(roots_surface, layer, link) {
struct wlr_layer_surface *layer = roots_surface->layer_surface;
render_surface(layer->surface,
roots_surface->geo.x + output_layout_box->x,
roots_surface->geo.y + output_layout_box->y,
0, data);
}
}
static void layers_send_done(
struct roots_output *output, struct timespec *when) {
size_t len = sizeof(output->layers) / sizeof(output->layers[0]);
for (size_t i = 0; i < len; ++i) {
struct roots_layer_surface *roots_surface;
wl_list_for_each(roots_surface, &output->layers[i], link) {
struct wlr_layer_surface *layer = roots_surface->layer_surface;
wlr_surface_send_frame_done(layer->surface, when);
}
}
}
static void render_output(struct roots_output *output) { static void render_output(struct roots_output *output) {
struct wlr_output *wlr_output = output->wlr_output; struct wlr_output *wlr_output = output->wlr_output;
struct roots_desktop *desktop = output->desktop; struct roots_desktop *desktop = output->desktop;
@ -433,14 +461,15 @@ static void render_output(struct roots_output *output) {
float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f}; float clear_color[] = {0.25f, 0.25f, 0.25f, 1.0f};
const struct wlr_box *output_box =
wlr_output_layout_get_box(desktop->layout, wlr_output);
// Check if we can delegate the fullscreen surface to the output // Check if we can delegate the fullscreen surface to the output
if (output->fullscreen_view != NULL && if (output->fullscreen_view != NULL &&
output->fullscreen_view->wlr_surface != NULL) { output->fullscreen_view->wlr_surface != NULL) {
struct roots_view *view = output->fullscreen_view; struct roots_view *view = output->fullscreen_view;
// Make sure the view is centered on screen // Make sure the view is centered on screen
const struct wlr_box *output_box =
wlr_output_layout_get_box(desktop->layout, wlr_output);
struct wlr_box view_box; struct wlr_box view_box;
view_get_box(view, &view_box); view_get_box(view, &view_box);
double view_x = (double)(output_box->width - view_box.width) / 2 + double view_x = (double)(output_box->width - view_box.width) / 2 +
@ -498,6 +527,11 @@ static void render_output(struct roots_output *output) {
wlr_renderer_clear(renderer, clear_color); wlr_renderer_clear(renderer, clear_color);
} }
render_layer(output, output_box, &data,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
render_layer(output, output_box, &data,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
// If a view is fullscreen on this output, render it // If a view is fullscreen on this output, render it
if (output->fullscreen_view != NULL) { if (output->fullscreen_view != NULL) {
struct roots_view *view = output->fullscreen_view; struct roots_view *view = output->fullscreen_view;
@ -520,20 +554,24 @@ static void render_output(struct roots_output *output) {
render_surface, &data); render_surface, &data);
} }
#endif #endif
} else {
goto renderer_end;
}
// Render all views // Render all views
struct roots_view *view; struct roots_view *view;
wl_list_for_each_reverse(view, &desktop->views, link) { wl_list_for_each_reverse(view, &desktop->views, link) {
render_view(view, &data); render_view(view, &data);
} }
// Render top layer above shell views
render_layer(output, output_box, &data,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
}
// Render drag icons // Render drag icons
data.alpha = 1.0; data.alpha = 1.0;
drag_icons_for_each_surface(server->input, render_surface, &data); drag_icons_for_each_surface(server->input, render_surface, &data);
render_layer(output, output_box, &data,
&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
renderer_end: renderer_end:
wlr_renderer_scissor(renderer, NULL); wlr_renderer_scissor(renderer, NULL);
wlr_renderer_end(renderer); wlr_renderer_end(renderer);
@ -570,6 +608,7 @@ damage_finish:
drag_icons_for_each_surface(server->input, surface_send_frame_done, drag_icons_for_each_surface(server->input, surface_send_frame_done,
&data); &data);
} }
layers_send_done(output, data.when);
} }
void output_damage_whole(struct roots_output *output) { void output_damage_whole(struct roots_output *output) {
@ -626,6 +665,15 @@ static void damage_whole_surface(struct wlr_surface *surface,
wlr_output_damage_add_box(output->damage, &box); wlr_output_damage_add_box(output->damage, &box);
} }
void output_damage_whole_local_surface(struct roots_output *output,
struct wlr_surface *surface, double ox, double oy, float rotation) {
struct wlr_output_layout_output *layout = wlr_output_layout_get(
output->desktop->layout, output->wlr_output);
damage_whole_surface(surface, ox + layout->x, oy + layout->y,
rotation, output);
// TODO: subsurfaces
}
static void damage_whole_decoration(struct roots_view *view, static void damage_whole_decoration(struct roots_view *view,
struct roots_output *output) { struct roots_output *output) {
if (!view->decorated || view->wlr_surface == NULL) { if (!view->decorated || view->wlr_surface == NULL) {
@ -691,6 +739,15 @@ static void damage_from_surface(struct wlr_surface *surface,
pixman_region32_fini(&damage); pixman_region32_fini(&damage);
} }
void output_damage_from_local_surface(struct roots_output *output,
struct wlr_surface *surface, double ox, double oy, float rotation) {
struct wlr_output_layout_output *layout = wlr_output_layout_get(
output->desktop->layout, output->wlr_output);
damage_from_surface(surface, ox + layout->x, oy + layout->y,
rotation, output);
// TODO: Subsurfaces
}
void output_damage_from_view(struct roots_output *output, void output_damage_from_view(struct roots_output *output,
struct roots_view *view) { struct roots_view *view) {
if (!view_accept_damage(output, view)) { if (!view_accept_damage(output, view)) {
@ -759,6 +816,18 @@ static void output_damage_handle_destroy(struct wl_listener *listener,
output_destroy(output); output_destroy(output);
} }
static void output_handle_mode(struct wl_listener *listener, void *data) {
struct roots_output *output =
wl_container_of(listener, output, mode);
arrange_layers(output);
}
static void output_handle_transform(struct wl_listener *listener, void *data) {
struct roots_output *output =
wl_container_of(listener, output, transform);
arrange_layers(output);
}
void handle_new_output(struct wl_listener *listener, void *data) { void handle_new_output(struct wl_listener *listener, void *data) {
struct roots_desktop *desktop = wl_container_of(listener, desktop, struct roots_desktop *desktop = wl_container_of(listener, desktop,
new_output); new_output);
@ -781,17 +850,28 @@ void handle_new_output(struct wl_listener *listener, void *data) {
clock_gettime(CLOCK_MONOTONIC, &output->last_frame); clock_gettime(CLOCK_MONOTONIC, &output->last_frame);
output->desktop = desktop; output->desktop = desktop;
output->wlr_output = wlr_output; output->wlr_output = wlr_output;
wlr_output->data = output;
wl_list_insert(&desktop->outputs, &output->link); wl_list_insert(&desktop->outputs, &output->link);
output->damage = wlr_output_damage_create(wlr_output); output->damage = wlr_output_damage_create(wlr_output);
output->destroy.notify = output_handle_destroy; output->destroy.notify = output_handle_destroy;
wl_signal_add(&wlr_output->events.destroy, &output->destroy); wl_signal_add(&wlr_output->events.destroy, &output->destroy);
output->mode.notify = output_handle_mode;
wl_signal_add(&wlr_output->events.mode, &output->mode);
output->transform.notify = output_handle_transform;
wl_signal_add(&wlr_output->events.transform, &output->transform);
output->damage_frame.notify = output_damage_handle_frame; output->damage_frame.notify = output_damage_handle_frame;
wl_signal_add(&output->damage->events.frame, &output->damage_frame); wl_signal_add(&output->damage->events.frame, &output->damage_frame);
output->damage_destroy.notify = output_damage_handle_destroy; output->damage_destroy.notify = output_damage_handle_destroy;
wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); wl_signal_add(&output->damage->events.destroy, &output->damage_destroy);
size_t len = sizeof(output->layers) / sizeof(output->layers[0]);
for (size_t i = 0; i < len; ++i) {
wl_list_init(&output->layers[i]);
}
struct roots_output_config *output_config = struct roots_output_config *output_config =
roots_config_get_output(config, wlr_output); roots_config_get_output(config, wlr_output);
if (output_config) { if (output_config) {
@ -816,5 +896,6 @@ void handle_new_output(struct wl_listener *listener, void *data) {
roots_seat_configure_xcursor(seat); roots_seat_configure_xcursor(seat);
} }
arrange_layers(output);
output_damage_whole(output); output_damage_whole(output);
} }

@ -28,6 +28,16 @@ static void popup_handle_destroy(struct wl_listener *listener, void *data) {
popup_destroy((struct roots_view_child *)popup); popup_destroy((struct roots_view_child *)popup);
} }
static void popup_handle_map(struct wl_listener *listener, void *data) {
struct roots_xdg_popup *popup = wl_container_of(listener, popup, map);
view_damage_whole(popup->view_child.view);
}
static void popup_handle_unmap(struct wl_listener *listener, void *data) {
struct roots_xdg_popup *popup = wl_container_of(listener, popup, unmap);
view_damage_whole(popup->view_child.view);
}
static struct roots_xdg_popup *popup_create(struct roots_view *view, static struct roots_xdg_popup *popup_create(struct roots_view *view,
struct wlr_xdg_popup *wlr_popup); struct wlr_xdg_popup *wlr_popup);
@ -50,6 +60,10 @@ static struct roots_xdg_popup *popup_create(struct roots_view *view,
view_child_init(&popup->view_child, view, wlr_popup->base->surface); view_child_init(&popup->view_child, view, wlr_popup->base->surface);
popup->destroy.notify = popup_handle_destroy; popup->destroy.notify = popup_handle_destroy;
wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy);
popup->map.notify = popup_handle_map;
wl_signal_add(&wlr_popup->base->events.map, &popup->map);
popup->unmap.notify = popup_handle_unmap;
wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap);
popup->new_popup.notify = popup_handle_new_popup; popup->new_popup.notify = popup_handle_new_popup;
wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup);
return popup; return popup;
@ -238,7 +252,7 @@ static void handle_request_maximize(struct wl_listener *listener, void *data) {
return; return;
} }
view_maximize(view, surface->toplevel->next.maximized); view_maximize(view, surface->toplevel->client_pending.maximized);
} }
static void handle_request_fullscreen(struct wl_listener *listener, static void handle_request_fullscreen(struct wl_listener *listener,
@ -339,7 +353,7 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) {
wl_container_of(listener, desktop, xdg_shell_surface); wl_container_of(listener, desktop, xdg_shell_surface);
wlr_log(L_DEBUG, "new xdg toplevel: title=%s, app_id=%s", wlr_log(L_DEBUG, "new xdg toplevel: title=%s, app_id=%s",
surface->title, surface->app_id); surface->toplevel->title, surface->toplevel->app_id);
wlr_xdg_surface_ping(surface); wlr_xdg_surface_ping(surface);
struct roots_xdg_surface *roots_surface = struct roots_xdg_surface *roots_surface =
@ -357,15 +371,16 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) {
roots_surface->unmap.notify = handle_unmap; roots_surface->unmap.notify = handle_unmap;
wl_signal_add(&surface->events.unmap, &roots_surface->unmap); wl_signal_add(&surface->events.unmap, &roots_surface->unmap);
roots_surface->request_move.notify = handle_request_move; roots_surface->request_move.notify = handle_request_move;
wl_signal_add(&surface->events.request_move, &roots_surface->request_move); wl_signal_add(&surface->toplevel->events.request_move,
&roots_surface->request_move);
roots_surface->request_resize.notify = handle_request_resize; roots_surface->request_resize.notify = handle_request_resize;
wl_signal_add(&surface->events.request_resize, wl_signal_add(&surface->toplevel->events.request_resize,
&roots_surface->request_resize); &roots_surface->request_resize);
roots_surface->request_maximize.notify = handle_request_maximize; roots_surface->request_maximize.notify = handle_request_maximize;
wl_signal_add(&surface->events.request_maximize, wl_signal_add(&surface->toplevel->events.request_maximize,
&roots_surface->request_maximize); &roots_surface->request_maximize);
roots_surface->request_fullscreen.notify = handle_request_fullscreen; roots_surface->request_fullscreen.notify = handle_request_fullscreen;
wl_signal_add(&surface->events.request_fullscreen, wl_signal_add(&surface->toplevel->events.request_fullscreen,
&roots_surface->request_fullscreen); &roots_surface->request_fullscreen);
roots_surface->new_popup.notify = handle_new_popup; roots_surface->new_popup.notify = handle_new_popup;
wl_signal_add(&surface->events.new_popup, &roots_surface->new_popup); wl_signal_add(&surface->events.new_popup, &roots_surface->new_popup);
@ -388,10 +403,10 @@ void handle_xdg_shell_surface(struct wl_listener *listener, void *data) {
view->destroy = destroy; view->destroy = destroy;
roots_surface->view = view; roots_surface->view = view;
if (surface->toplevel->next.maximized) { if (surface->toplevel->client_pending.maximized) {
view_maximize(view, true); view_maximize(view, true);
} }
if (surface->toplevel->next.fullscreen) { if (surface->toplevel->client_pending.fullscreen) {
view_set_fullscreen(view, true, NULL); view_set_fullscreen(view, true, NULL);
} }
} }

@ -28,6 +28,18 @@ static void popup_handle_destroy(struct wl_listener *listener, void *data) {
popup_destroy((struct roots_view_child *)popup); popup_destroy((struct roots_view_child *)popup);
} }
static void popup_handle_map(struct wl_listener *listener, void *data) {
struct roots_xdg_popup_v6 *popup =
wl_container_of(listener, popup, map);
view_damage_whole(popup->view_child.view);
}
static void popup_handle_unmap(struct wl_listener *listener, void *data) {
struct roots_xdg_popup_v6 *popup =
wl_container_of(listener, popup, unmap);
view_damage_whole(popup->view_child.view);
}
static struct roots_xdg_popup_v6 *popup_create(struct roots_view *view, static struct roots_xdg_popup_v6 *popup_create(struct roots_view *view,
struct wlr_xdg_popup_v6 *wlr_popup); struct wlr_xdg_popup_v6 *wlr_popup);
@ -38,6 +50,58 @@ static void popup_handle_new_popup(struct wl_listener *listener, void *data) {
popup_create(popup->view_child.view, wlr_popup); popup_create(popup->view_child.view, wlr_popup);
} }
static void popup_unconstrain(struct roots_xdg_popup_v6 *popup) {
// get the output of the popup's positioner anchor point and convert it to
// the toplevel parent's coordinate system and then pass it to
// wlr_xdg_popup_v6_unconstrain_from_box
// TODO: unconstrain popups for rotated windows
if (popup->view_child.view->rotation != 0.0) {
return;
}
struct roots_view *view = popup->view_child.view;
struct wlr_output_layout *layout = view->desktop->layout;
struct wlr_xdg_popup_v6 *wlr_popup = popup->wlr_popup;
int anchor_lx, anchor_ly;
wlr_xdg_popup_v6_get_anchor_point(wlr_popup, &anchor_lx, &anchor_ly);
int popup_lx, popup_ly;
wlr_xdg_popup_v6_get_toplevel_coords(wlr_popup, wlr_popup->geometry.x,
wlr_popup->geometry.y, &popup_lx, &popup_ly);
popup_lx += view->x;
popup_ly += view->y;
anchor_lx += popup_lx;
anchor_ly += popup_ly;
double dest_x = 0, dest_y = 0;
wlr_output_layout_closest_point(layout, NULL, anchor_lx, anchor_ly,
&dest_x, &dest_y);
struct wlr_output *output =
wlr_output_layout_output_at(layout, dest_x, dest_y);
if (output == NULL) {
return;
}
int width = 0, height = 0;
wlr_output_effective_resolution(output, &width, &height);
// the output box expressed in the coordinate system of the toplevel parent
// of the popup
struct wlr_box output_toplevel_sx_box = {
.x = output->lx - view->x,
.y = output->ly - view->y,
.width = width,
.height = height
};
wlr_xdg_popup_v6_unconstrain_from_box(popup->wlr_popup, &output_toplevel_sx_box);
}
static struct roots_xdg_popup_v6 *popup_create(struct roots_view *view, static struct roots_xdg_popup_v6 *popup_create(struct roots_view *view,
struct wlr_xdg_popup_v6 *wlr_popup) { struct wlr_xdg_popup_v6 *wlr_popup) {
struct roots_xdg_popup_v6 *popup = struct roots_xdg_popup_v6 *popup =
@ -50,8 +114,15 @@ static struct roots_xdg_popup_v6 *popup_create(struct roots_view *view,
view_child_init(&popup->view_child, view, wlr_popup->base->surface); view_child_init(&popup->view_child, view, wlr_popup->base->surface);
popup->destroy.notify = popup_handle_destroy; popup->destroy.notify = popup_handle_destroy;
wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy);
popup->map.notify = popup_handle_map;
wl_signal_add(&wlr_popup->base->events.map, &popup->map);
popup->unmap.notify = popup_handle_unmap;
wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap);
popup->new_popup.notify = popup_handle_new_popup; popup->new_popup.notify = popup_handle_new_popup;
wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup);
popup_unconstrain(popup);
return popup; return popup;
} }
@ -238,7 +309,7 @@ static void handle_request_maximize(struct wl_listener *listener, void *data) {
return; return;
} }
view_maximize(view, surface->toplevel->next.maximized); view_maximize(view, surface->toplevel->client_pending.maximized);
} }
static void handle_request_fullscreen(struct wl_listener *listener, static void handle_request_fullscreen(struct wl_listener *listener,
@ -339,7 +410,7 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) {
wl_container_of(listener, desktop, xdg_shell_v6_surface); wl_container_of(listener, desktop, xdg_shell_v6_surface);
wlr_log(L_DEBUG, "new xdg toplevel: title=%s, app_id=%s", wlr_log(L_DEBUG, "new xdg toplevel: title=%s, app_id=%s",
surface->title, surface->app_id); surface->toplevel->title, surface->toplevel->app_id);
wlr_xdg_surface_v6_ping(surface); wlr_xdg_surface_v6_ping(surface);
struct roots_xdg_surface_v6 *roots_surface = struct roots_xdg_surface_v6 *roots_surface =
@ -357,15 +428,16 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) {
roots_surface->unmap.notify = handle_unmap; roots_surface->unmap.notify = handle_unmap;
wl_signal_add(&surface->events.unmap, &roots_surface->unmap); wl_signal_add(&surface->events.unmap, &roots_surface->unmap);
roots_surface->request_move.notify = handle_request_move; roots_surface->request_move.notify = handle_request_move;
wl_signal_add(&surface->events.request_move, &roots_surface->request_move); wl_signal_add(&surface->toplevel->events.request_move,
&roots_surface->request_move);
roots_surface->request_resize.notify = handle_request_resize; roots_surface->request_resize.notify = handle_request_resize;
wl_signal_add(&surface->events.request_resize, wl_signal_add(&surface->toplevel->events.request_resize,
&roots_surface->request_resize); &roots_surface->request_resize);
roots_surface->request_maximize.notify = handle_request_maximize; roots_surface->request_maximize.notify = handle_request_maximize;
wl_signal_add(&surface->events.request_maximize, wl_signal_add(&surface->toplevel->events.request_maximize,
&roots_surface->request_maximize); &roots_surface->request_maximize);
roots_surface->request_fullscreen.notify = handle_request_fullscreen; roots_surface->request_fullscreen.notify = handle_request_fullscreen;
wl_signal_add(&surface->events.request_fullscreen, wl_signal_add(&surface->toplevel->events.request_fullscreen,
&roots_surface->request_fullscreen); &roots_surface->request_fullscreen);
roots_surface->new_popup.notify = handle_new_popup; roots_surface->new_popup.notify = handle_new_popup;
wl_signal_add(&surface->events.new_popup, &roots_surface->new_popup); wl_signal_add(&surface->events.new_popup, &roots_surface->new_popup);
@ -388,10 +460,10 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) {
view->destroy = destroy; view->destroy = destroy;
roots_surface->view = view; roots_surface->view = view;
if (surface->toplevel->next.maximized) { if (surface->toplevel->client_pending.maximized) {
view_maximize(view, true); view_maximize(view, true);
} }
if (surface->toplevel->next.fullscreen) { if (surface->toplevel->client_pending.fullscreen) {
view_set_fullscreen(view, true, NULL); view_set_fullscreen(view, true, NULL);
} }
} }

@ -114,8 +114,8 @@ static void destroy(struct roots_view *view) {
wl_list_remove(&roots_surface->request_move.link); wl_list_remove(&roots_surface->request_move.link);
wl_list_remove(&roots_surface->request_resize.link); wl_list_remove(&roots_surface->request_resize.link);
wl_list_remove(&roots_surface->request_maximize.link); wl_list_remove(&roots_surface->request_maximize.link);
wl_list_remove(&roots_surface->map_notify.link); wl_list_remove(&roots_surface->map.link);
wl_list_remove(&roots_surface->unmap_notify.link); wl_list_remove(&roots_surface->unmap.link);
free(roots_surface); free(roots_surface);
} }
@ -225,9 +225,9 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) {
view_update_position(view, x, y); view_update_position(view, x, y);
} }
static void handle_map_notify(struct wl_listener *listener, void *data) { static void handle_map(struct wl_listener *listener, void *data) {
struct roots_xwayland_surface *roots_surface = struct roots_xwayland_surface *roots_surface =
wl_container_of(listener, roots_surface, map_notify); wl_container_of(listener, roots_surface, map);
struct wlr_xwayland_surface *xsurface = data; struct wlr_xwayland_surface *xsurface = data;
struct roots_view *view = roots_surface->view; struct roots_view *view = roots_surface->view;
@ -243,9 +243,9 @@ static void handle_map_notify(struct wl_listener *listener, void *data) {
&roots_surface->surface_commit); &roots_surface->surface_commit);
} }
static void handle_unmap_notify(struct wl_listener *listener, void *data) { static void handle_unmap(struct wl_listener *listener, void *data) {
struct roots_xwayland_surface *roots_surface = struct roots_xwayland_surface *roots_surface =
wl_container_of(listener, roots_surface, unmap_notify); wl_container_of(listener, roots_surface, unmap);
struct roots_view *view = roots_surface->view; struct roots_view *view = roots_surface->view;
wl_list_remove(&roots_surface->surface_commit.link); wl_list_remove(&roots_surface->surface_commit.link);
@ -272,10 +272,10 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) {
roots_surface->request_configure.notify = handle_request_configure; roots_surface->request_configure.notify = handle_request_configure;
wl_signal_add(&surface->events.request_configure, wl_signal_add(&surface->events.request_configure,
&roots_surface->request_configure); &roots_surface->request_configure);
roots_surface->map_notify.notify = handle_map_notify; roots_surface->map.notify = handle_map;
wl_signal_add(&surface->events.map_notify, &roots_surface->map_notify); wl_signal_add(&surface->events.map, &roots_surface->map);
roots_surface->unmap_notify.notify = handle_unmap_notify; roots_surface->unmap.notify = handle_unmap;
wl_signal_add(&surface->events.unmap_notify, &roots_surface->unmap_notify); wl_signal_add(&surface->events.unmap, &roots_surface->unmap);
roots_surface->request_move.notify = handle_request_move; roots_surface->request_move.notify = handle_request_move;
wl_signal_add(&surface->events.request_move, &roots_surface->request_move); wl_signal_add(&surface->events.request_move, &roots_surface->request_move);
roots_surface->request_resize.notify = handle_request_resize; roots_surface->request_resize.notify = handle_request_resize;

@ -8,8 +8,10 @@ lib_wlr_types = static_library(
'wlr_gamma_control.c', 'wlr_gamma_control.c',
'wlr_idle_inhibit_v1.c', 'wlr_idle_inhibit_v1.c',
'wlr_idle.c', 'wlr_idle.c',
'wlr_idle_inhibit_v1.c',
'wlr_input_device.c', 'wlr_input_device.c',
'wlr_keyboard.c', 'wlr_keyboard.c',
'wlr_layer_shell.c',
'wlr_linux_dmabuf.c', 'wlr_linux_dmabuf.c',
'wlr_list.c', 'wlr_list.c',
'wlr_matrix.c', 'wlr_matrix.c',

@ -254,7 +254,7 @@ bool wlr_cursor_warp(struct wlr_cursor *cur, struct wlr_input_device *dev,
} }
void wlr_cursor_warp_absolute(struct wlr_cursor *cur, void wlr_cursor_warp_absolute(struct wlr_cursor *cur,
struct wlr_input_device *dev, double x_mm, double y_mm) { struct wlr_input_device *dev, double x, double y) {
assert(cur->state->layout); assert(cur->state->layout);
struct wlr_box *mapping = get_mapping(cur, dev); struct wlr_box *mapping = get_mapping(cur, dev);
@ -262,8 +262,8 @@ void wlr_cursor_warp_absolute(struct wlr_cursor *cur,
mapping = wlr_output_layout_get_box(cur->state->layout, NULL); mapping = wlr_output_layout_get_box(cur->state->layout, NULL);
} }
double x = x_mm > 0 ? mapping->width * x_mm + mapping->x : cur->x; x = x > 0 ? mapping->width * x + mapping->x : cur->x;
double y = y_mm > 0 ? mapping->height * y_mm + mapping->y : cur->y; y = y > 0 ? mapping->height * y + mapping->y : cur->y;
wlr_cursor_warp_unchecked(cur, x, y); wlr_cursor_warp_unchecked(cur, x, y);
} }
@ -640,19 +640,15 @@ void wlr_cursor_map_input_to_region(struct wlr_cursor *cur,
} }
bool wlr_cursor_absolute_to_layout_coords(struct wlr_cursor *cur, bool wlr_cursor_absolute_to_layout_coords(struct wlr_cursor *cur,
struct wlr_input_device *device, double x_mm, double y_mm, struct wlr_input_device *device, double x, double y,
double width_mm, double height_mm, double *lx, double *ly) { double *lx, double *ly) {
if (width_mm <= 0 || height_mm <= 0) {
return false;
}
struct wlr_box *mapping = get_mapping(cur, device); struct wlr_box *mapping = get_mapping(cur, device);
if (!mapping) { if (!mapping) {
mapping = wlr_output_layout_get_box(cur->state->layout, NULL); mapping = wlr_output_layout_get_box(cur->state->layout, NULL);
} }
*lx = x_mm > 0 ? mapping->width * (x_mm / width_mm) + mapping->x : cur->x; *lx = x > 0 ? mapping->width * x + mapping->x : cur->x;
*ly = y_mm > 0 ? mapping->height * (y_mm / height_mm) + mapping->y : cur->y; *ly = y > 0 ? mapping->height * y + mapping->y : cur->y;
return true; return true;
} }

@ -0,0 +1,427 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <wayland-server.h>
#include <wlr/types/wlr_layer_shell.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_surface.h>
#include <wlr/util/log.h>
#include "util/signal.h"
#include "wlr-layer-shell-unstable-v1-protocol.h"
static const char *zwlr_layer_surface_role = "zwlr_layer_surface";
static void resource_handle_destroy(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
static const struct zwlr_layer_shell_v1_interface layer_shell_implementation;
static const struct zwlr_layer_surface_v1_interface layer_surface_implementation;
static struct wlr_layer_shell *layer_shell_from_resource(
struct wl_resource *resource) {
assert(wl_resource_instance_of(resource, &zwlr_layer_shell_v1_interface,
&layer_shell_implementation));
return wl_resource_get_user_data(resource);
}
static struct wlr_layer_surface *layer_surface_from_resource(
struct wl_resource *resource) {
assert(wl_resource_instance_of(resource, &zwlr_layer_surface_v1_interface,
&layer_surface_implementation));
return wl_resource_get_user_data(resource);
}
static void layer_surface_configure_destroy(
struct wlr_layer_surface_configure *configure) {
if (configure == NULL) {
return;
}
wl_list_remove(&configure->link);
free(configure);
}
static void layer_surface_handle_ack_configure(struct wl_client *client,
struct wl_resource *resource, uint32_t serial) {
struct wlr_layer_surface *surface = layer_surface_from_resource(resource);
bool found = false;
struct wlr_layer_surface_configure *configure, *tmp;
wl_list_for_each_safe(configure, tmp, &surface->configure_list, link) {
if (configure->serial < serial) {
layer_surface_configure_destroy(configure);
} else if (configure->serial == serial) {
found = true;
break;
} else {
break;
}
}
if (!found) {
wl_resource_post_error(resource,
ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_SURFACE_STATE,
"wrong configure serial: %u", serial);
return;
}
if (surface->acked_configure) {
layer_surface_configure_destroy(surface->acked_configure);
}
surface->acked_configure = configure;
wl_list_remove(&configure->link);
wl_list_init(&configure->link);
}
static void layer_surface_handle_set_size(struct wl_client *client,
struct wl_resource *resource, uint32_t width, uint32_t height) {
struct wlr_layer_surface *surface = layer_surface_from_resource(resource);
surface->client_pending.desired_width = width;
surface->client_pending.desired_height = height;
}
static void layer_surface_handle_set_anchor(struct wl_client *client,
struct wl_resource *resource, uint32_t anchor) {
const uint32_t max_anchor =
ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM |
ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
if (anchor > max_anchor) {
wl_resource_post_error(resource,
ZWLR_LAYER_SURFACE_V1_ERROR_INVALID_ANCHOR,
"invalid anchor %d", anchor);
}
struct wlr_layer_surface *surface = layer_surface_from_resource(resource);
surface->client_pending.anchor = anchor;
}
static void layer_surface_handle_set_exclusive_zone(struct wl_client *client,
struct wl_resource *resource, int32_t zone) {
struct wlr_layer_surface *surface = layer_surface_from_resource(resource);
surface->client_pending.exclusive_zone = zone;
}
static void layer_surface_handle_set_margin(
struct wl_client *client, struct wl_resource *resource,
int32_t top, int32_t right, int32_t bottom, int32_t left) {
struct wlr_layer_surface *surface = layer_surface_from_resource(resource);
surface->client_pending.margin.top = top;
surface->client_pending.margin.right = right;
surface->client_pending.margin.bottom = bottom;
surface->client_pending.margin.left = left;
}
static void layer_surface_handle_set_keyboard_interactivity(
struct wl_client *client, struct wl_resource *resource,
uint32_t interactive) {
struct wlr_layer_surface *surface = layer_surface_from_resource(resource);
surface->client_pending.keyboard_interactive = !!interactive;
}
static void layer_surface_handle_get_popup(struct wl_client *client,
struct wl_resource *resource, struct wl_resource *popup) {
// TODO
}
static const struct zwlr_layer_surface_v1_interface layer_surface_implementation = {
.destroy = resource_handle_destroy,
.ack_configure = layer_surface_handle_ack_configure,
.set_size = layer_surface_handle_set_size,
.set_anchor = layer_surface_handle_set_anchor,
.set_exclusive_zone = layer_surface_handle_set_exclusive_zone,
.set_margin = layer_surface_handle_set_margin,
.set_keyboard_interactivity = layer_surface_handle_set_keyboard_interactivity,
.get_popup = layer_surface_handle_get_popup,
};
static void layer_surface_unmap(struct wlr_layer_surface *surface) {
// TODO: probably need to ungrab before this event
wlr_signal_emit_safe(&surface->events.unmap, surface);
struct wlr_layer_surface_configure *configure, *tmp;
wl_list_for_each_safe(configure, tmp, &surface->configure_list, link) {
layer_surface_configure_destroy(configure);
}
surface->configured = surface->mapped = false;
surface->configure_serial = 0;
if (surface->configure_idle) {
wl_event_source_remove(surface->configure_idle);
surface->configure_idle = NULL;
}
surface->configure_next_serial = 0;
}
static void layer_surface_destroy(struct wlr_layer_surface *surface) {
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);
wl_list_init(&surface->surface_destroy_listener.link);
wlr_surface_set_role_committed(surface->surface, NULL, NULL);
wl_list_remove(&surface->link);
free(surface);
}
static void layer_surface_resource_destroy(struct wl_resource *resource) {
struct wlr_layer_surface *surface =
layer_surface_from_resource(resource);
if (surface != NULL) {
layer_surface_destroy(surface);
}
}
static bool wlr_layer_surface_state_changed(struct wlr_layer_surface *surface) {
struct wlr_layer_surface_state *state;
if (wl_list_empty(&surface->configure_list)) {
if (surface->acked_configure) {
state = &surface->acked_configure->state;
} else if (!surface->configured) {
return true;
} else {
state = &surface->current;
}
} else {
struct wlr_layer_surface_configure *configure =
wl_container_of(surface->configure_list.prev, configure, link);
state = &configure->state;
}
bool changed = state->actual_width != surface->server_pending.actual_width
|| state->actual_height != surface->server_pending.actual_height;
return changed;
}
void wlr_layer_surface_configure(struct wlr_layer_surface *surface,
uint32_t width, uint32_t height) {
surface->server_pending.actual_width = width;
surface->server_pending.actual_height = height;
if (wlr_layer_surface_state_changed(surface)) {
struct wl_display *display =
wl_client_get_display(wl_resource_get_client(surface->resource));
struct wlr_layer_surface_configure *configure =
calloc(1, sizeof(struct wlr_layer_surface_configure));
if (configure == NULL) {
wl_client_post_no_memory(wl_resource_get_client(surface->resource));
return;
}
surface->configure_next_serial = wl_display_next_serial(display);
wl_list_insert(surface->configure_list.prev, &configure->link);
configure->state.actual_width = width;
configure->state.actual_height = height;
configure->serial = surface->configure_next_serial;
zwlr_layer_surface_v1_send_configure(surface->resource,
configure->serial, configure->state.actual_width,
configure->state.actual_height);
}
}
void wlr_layer_surface_close(struct wlr_layer_surface *surface) {
if (surface->closed) {
return;
}
surface->closed = true;
layer_surface_unmap(surface);
zwlr_layer_surface_v1_send_closed(surface->resource);
}
static void handle_wlr_surface_committed(struct wlr_surface *wlr_surface,
void *role_data) {
struct wlr_layer_surface *surface = role_data;
if (surface->closed) {
// Ignore commits after the compositor has closed it
return;
}
if (surface->acked_configure) {
struct wlr_layer_surface_configure *configure =
surface->acked_configure;
surface->configured = true;
surface->configure_serial = configure->serial;
surface->current.actual_width = configure->state.actual_width;
surface->current.actual_height = configure->state.actual_height;
layer_surface_configure_destroy(configure);
surface->acked_configure = NULL;
}
if (wlr_surface_has_buffer(surface->surface) && !surface->configured) {
wl_resource_post_error(surface->resource,
ZWLR_LAYER_SHELL_V1_ERROR_ALREADY_CONSTRUCTED,
"layer_surface has never been configured");
return;
}
surface->current.anchor = surface->client_pending.anchor;
surface->current.exclusive_zone = surface->client_pending.exclusive_zone;
surface->current.margin = surface->client_pending.margin;
surface->current.keyboard_interactive =
surface->client_pending.keyboard_interactive;
surface->current.desired_width = surface->client_pending.desired_width;
surface->current.desired_height = surface->client_pending.desired_height;
if (!surface->added) {
surface->added = true;
wlr_signal_emit_safe(&surface->shell->events.new_surface,
surface);
}
if (surface->configured && wlr_surface_has_buffer(surface->surface) &&
!surface->mapped) {
surface->mapped = true;
wlr_signal_emit_safe(&surface->events.map, surface);
}
if (surface->configured && !wlr_surface_has_buffer(surface->surface) &&
surface->mapped) {
layer_surface_unmap(surface);
}
}
static void handle_wlr_surface_destroyed(struct wl_listener *listener,
void *data) {
struct wlr_layer_surface *layer_surface =
wl_container_of(listener, layer_surface, surface_destroy_listener);
layer_surface_destroy(layer_surface);
}
static void layer_shell_handle_get_layer_surface(struct wl_client *wl_client,
struct wl_resource *client_resource, uint32_t id,
struct wl_resource *surface_resource,
struct wl_resource *output_resource,
uint32_t layer, const char *namespace) {
struct wlr_layer_shell *shell =
layer_shell_from_resource(client_resource);
struct wlr_surface *wlr_surface =
wlr_surface_from_resource(surface_resource);
if (wlr_surface_set_role(wlr_surface, zwlr_layer_surface_role,
client_resource, ZWLR_LAYER_SHELL_V1_ERROR_ROLE)) {
return;
}
struct wlr_layer_surface *surface =
calloc(1, sizeof(struct wlr_layer_surface));
if (surface == NULL) {
wl_client_post_no_memory(wl_client);
return;
}
surface->shell = shell;
surface->surface = wlr_surface;
surface->output = wlr_output_from_resource(output_resource);
surface->resource = wl_resource_create(wl_client,
&zwlr_layer_surface_v1_interface,
wl_resource_get_version(client_resource),
id);
surface->namespace = strdup(namespace);
surface->layer = layer;
if (surface->resource == NULL || surface->namespace == NULL) {
free(surface);
wl_client_post_no_memory(wl_client);
return;
}
if (layer > ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY) {
wl_resource_post_error(surface->resource,
ZWLR_LAYER_SHELL_V1_ERROR_INVALID_LAYER,
"Invalid layer %d", layer);
free(surface);
return;
}
wl_list_init(&surface->configure_list);
wl_signal_init(&surface->events.destroy);
wl_signal_add(&surface->surface->events.destroy,
&surface->surface_destroy_listener);
surface->surface_destroy_listener.notify = handle_wlr_surface_destroyed;
wl_signal_init(&surface->events.map);
wl_signal_init(&surface->events.unmap);
wlr_surface_set_role_committed(surface->surface,
handle_wlr_surface_committed, surface);
wlr_log(L_DEBUG, "new layer_surface %p (res %p)",
surface, surface->resource);
wl_resource_set_implementation(surface->resource,
&layer_surface_implementation, surface, layer_surface_resource_destroy);
wl_list_insert(&shell->surfaces, &surface->link);
}
static const struct zwlr_layer_shell_v1_interface layer_shell_implementation = {
.get_layer_surface = layer_shell_handle_get_layer_surface,
};
static void client_handle_destroy(struct wl_resource *resource) {
struct wl_client *client = wl_resource_get_client(resource);
struct wlr_layer_shell *shell = layer_shell_from_resource(resource);
struct wlr_layer_surface *surface, *tmp = NULL;
wl_list_for_each_safe(surface, tmp, &shell->surfaces, link) {
if (wl_resource_get_client(surface->resource) == client) {
layer_surface_destroy(surface);
}
}
wl_list_remove(wl_resource_get_link(resource));
}
static void layer_shell_bind(struct wl_client *wl_client, void *data,
uint32_t version, uint32_t id) {
struct wlr_layer_shell *layer_shell = data;
assert(wl_client && layer_shell);
struct wl_resource *resource = wl_resource_create(
wl_client, &zwlr_layer_shell_v1_interface, version, id);
if (resource == NULL) {
wl_client_post_no_memory(wl_client);
return;
}
wl_resource_set_implementation(resource,
&layer_shell_implementation, layer_shell, client_handle_destroy);
wl_list_insert(&layer_shell->client_resources,
wl_resource_get_link(resource));
}
static void handle_display_destroy(struct wl_listener *listener, void *data) {
struct wlr_layer_shell *layer_shell =
wl_container_of(listener, layer_shell, display_destroy);
wlr_layer_shell_destroy(layer_shell);
}
struct wlr_layer_shell *wlr_layer_shell_create(struct wl_display *display) {
struct wlr_layer_shell *layer_shell =
calloc(1, sizeof(struct wlr_layer_shell));
if (!layer_shell) {
return NULL;
}
wl_list_init(&layer_shell->client_resources);
wl_list_init(&layer_shell->surfaces);
struct wl_global *wl_global = wl_global_create(display,
&zwlr_layer_shell_v1_interface, 1, layer_shell, layer_shell_bind);
if (!wl_global) {
free(layer_shell);
return NULL;
}
layer_shell->wl_global = wl_global;
wl_signal_init(&layer_shell->events.new_surface);
layer_shell->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(display, &layer_shell->display_destroy);
return layer_shell;
}
void wlr_layer_shell_destroy(struct wlr_layer_shell *layer_shell) {
if (!layer_shell) {
return;
}
struct wl_resource *client, *tmp;
wl_resource_for_each_safe(client, tmp, &layer_shell->client_resources) {
wl_resource_destroy(client);
}
wl_list_remove(&layer_shell->display_destroy.link);
wl_global_destroy(layer_shell->wl_global);
free(layer_shell);
}

@ -19,11 +19,6 @@ static const struct wl_buffer_interface wl_buffer_impl = {
wl_buffer_destroy, wl_buffer_destroy,
}; };
bool wlr_dmabuf_buffer_has_inverted_y(struct wlr_dmabuf_buffer *dmabuf) {
return dmabuf->attributes.flags
& ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT;
}
bool wlr_dmabuf_resource_is_buffer(struct wl_resource *buffer_resource) { bool wlr_dmabuf_resource_is_buffer(struct wl_resource *buffer_resource) {
if (!wl_resource_instance_of(buffer_resource, &wl_buffer_interface, if (!wl_resource_instance_of(buffer_resource, &wl_buffer_interface,
&wl_buffer_impl)) { &wl_buffer_impl)) {

@ -731,15 +731,11 @@ bool wlr_output_cursor_set_image(struct wlr_output_cursor *cursor,
return true; return true;
} }
if (cursor->texture == NULL) { wlr_texture_destroy(cursor->texture);
cursor->texture = wlr_render_texture_create(renderer);
if (cursor->texture == NULL) {
return false;
}
}
return wlr_texture_upload_pixels(cursor->texture, WL_SHM_FORMAT_ARGB8888, cursor->texture = wlr_texture_from_pixels(renderer, WL_SHM_FORMAT_ARGB8888,
stride, width, height, pixels); stride, width, height, pixels);
return cursor->texture != NULL;
} }
static void output_cursor_commit(struct wlr_output_cursor *cursor) { static void output_cursor_commit(struct wlr_output_cursor *cursor) {
@ -901,9 +897,7 @@ void wlr_output_cursor_destroy(struct wlr_output_cursor *cursor) {
} }
cursor->output->hardware_cursor = NULL; cursor->output->hardware_cursor = NULL;
} }
if (cursor->texture != NULL) {
wlr_texture_destroy(cursor->texture); wlr_texture_destroy(cursor->texture);
}
wl_list_remove(&cursor->link); wl_list_remove(&cursor->link);
free(cursor); free(cursor);
} }

@ -191,7 +191,7 @@ static struct wlr_output_layout_output *wlr_output_layout_output_create(
} }
void wlr_output_layout_add(struct wlr_output_layout *layout, void wlr_output_layout_add(struct wlr_output_layout *layout,
struct wlr_output *output, int x, int y) { struct wlr_output *output, int lx, int ly) {
struct wlr_output_layout_output *l_output = struct wlr_output_layout_output *l_output =
wlr_output_layout_get(layout, output); wlr_output_layout_get(layout, output);
if (!l_output) { if (!l_output) {
@ -201,8 +201,8 @@ void wlr_output_layout_add(struct wlr_output_layout *layout,
return; return;
} }
} }
l_output->x = x; l_output->x = lx;
l_output->y = y; l_output->y = ly;
l_output->state->auto_configured = false; l_output->state->auto_configured = false;
wlr_output_layout_reconfigure(layout); wlr_output_layout_reconfigure(layout);
wlr_output_create_global(output); wlr_output_create_global(output);
@ -221,19 +221,19 @@ struct wlr_output_layout_output *wlr_output_layout_get(
} }
bool wlr_output_layout_contains_point(struct wlr_output_layout *layout, bool wlr_output_layout_contains_point(struct wlr_output_layout *layout,
struct wlr_output *reference, int x, int y) { struct wlr_output *reference, int lx, int ly) {
if (reference) { if (reference) {
struct wlr_output_layout_output *l_output = struct wlr_output_layout_output *l_output =
wlr_output_layout_get(layout, reference); wlr_output_layout_get(layout, reference);
struct wlr_box *box = wlr_output_layout_output_get_box(l_output); struct wlr_box *box = wlr_output_layout_output_get_box(l_output);
return wlr_box_contains_point(box, x, y); return wlr_box_contains_point(box, lx, ly);
} else { } else {
return !!wlr_output_layout_output_at(layout, x, y); return !!wlr_output_layout_output_at(layout, lx, ly);
} }
} }
bool wlr_output_layout_intersects(struct wlr_output_layout *layout, bool wlr_output_layout_intersects(struct wlr_output_layout *layout,
struct wlr_output *reference, const struct wlr_box *target_box) { struct wlr_output *reference, const struct wlr_box *target_lbox) {
struct wlr_box out_box; struct wlr_box out_box;
if (reference == NULL) { if (reference == NULL) {
@ -241,7 +241,7 @@ bool wlr_output_layout_intersects(struct wlr_output_layout *layout,
wl_list_for_each(l_output, &layout->outputs, link) { wl_list_for_each(l_output, &layout->outputs, link) {
struct wlr_box *output_box = struct wlr_box *output_box =
wlr_output_layout_output_get_box(l_output); wlr_output_layout_output_get_box(l_output);
if (wlr_box_intersection(output_box, target_box, &out_box)) { if (wlr_box_intersection(output_box, target_lbox, &out_box)) {
return true; return true;
} }
} }
@ -254,16 +254,16 @@ bool wlr_output_layout_intersects(struct wlr_output_layout *layout,
} }
struct wlr_box *output_box = wlr_output_layout_output_get_box(l_output); struct wlr_box *output_box = wlr_output_layout_output_get_box(l_output);
return wlr_box_intersection(output_box, target_box, &out_box); return wlr_box_intersection(output_box, target_lbox, &out_box);
} }
} }
struct wlr_output *wlr_output_layout_output_at(struct wlr_output_layout *layout, struct wlr_output *wlr_output_layout_output_at(struct wlr_output_layout *layout,
double x, double y) { double lx, double ly) {
struct wlr_output_layout_output *l_output; struct wlr_output_layout_output *l_output;
wl_list_for_each(l_output, &layout->outputs, link) { wl_list_for_each(l_output, &layout->outputs, link) {
struct wlr_box *box = wlr_output_layout_output_get_box(l_output); struct wlr_box *box = wlr_output_layout_output_get_box(l_output);
if (wlr_box_contains_point(box, x, y)) { if (wlr_box_contains_point(box, lx, ly)) {
return l_output->output; return l_output->output;
} }
} }
@ -271,12 +271,12 @@ struct wlr_output *wlr_output_layout_output_at(struct wlr_output_layout *layout,
} }
void wlr_output_layout_move(struct wlr_output_layout *layout, void wlr_output_layout_move(struct wlr_output_layout *layout,
struct wlr_output *output, int x, int y) { struct wlr_output *output, int lx, int ly) {
struct wlr_output_layout_output *l_output = struct wlr_output_layout_output *l_output =
wlr_output_layout_get(layout, output); wlr_output_layout_get(layout, output);
if (l_output) { if (l_output) {
l_output->x = x; l_output->x = lx;
l_output->y = y; l_output->y = ly;
l_output->state->auto_configured = false; l_output->state->auto_configured = false;
wlr_output_layout_reconfigure(layout); wlr_output_layout_reconfigure(layout);
} else { } else {
@ -295,24 +295,28 @@ void wlr_output_layout_remove(struct wlr_output_layout *layout,
} }
void wlr_output_layout_output_coords(struct wlr_output_layout *layout, void wlr_output_layout_output_coords(struct wlr_output_layout *layout,
struct wlr_output *reference, double *x, double *y) { struct wlr_output *reference, double *lx, double *ly) {
assert(layout && reference); assert(layout && reference);
double src_x = *x; double src_x = *lx;
double src_y = *y; double src_y = *ly;
struct wlr_output_layout_output *l_output; struct wlr_output_layout_output *l_output;
wl_list_for_each(l_output, &layout->outputs, link) { wl_list_for_each(l_output, &layout->outputs, link) {
if (l_output->output == reference) { if (l_output->output == reference) {
*x = src_x - (double)l_output->x; *lx = src_x - (double)l_output->x;
*y = src_y - (double)l_output->y; *ly = src_y - (double)l_output->y;
return; return;
} }
} }
} }
void wlr_output_layout_closest_point(struct wlr_output_layout *layout, void wlr_output_layout_closest_point(struct wlr_output_layout *layout,
struct wlr_output *reference, double x, double y, double *dest_x, struct wlr_output *reference, double lx, double ly, double *dest_lx,
double *dest_y) { double *dest_ly) {
if (dest_lx == NULL && dest_ly == NULL) {
return;
}
double min_x = DBL_MAX, min_y = DBL_MAX, min_distance = DBL_MAX; double min_x = DBL_MAX, min_y = DBL_MAX, min_distance = DBL_MAX;
struct wlr_output_layout_output *l_output; struct wlr_output_layout_output *l_output;
wl_list_for_each(l_output, &layout->outputs, link) { wl_list_for_each(l_output, &layout->outputs, link) {
@ -322,11 +326,11 @@ void wlr_output_layout_closest_point(struct wlr_output_layout *layout,
double output_x, output_y, output_distance; double output_x, output_y, output_distance;
struct wlr_box *box = wlr_output_layout_output_get_box(l_output); struct wlr_box *box = wlr_output_layout_output_get_box(l_output);
wlr_box_closest_point(box, x, y, &output_x, &output_y); wlr_box_closest_point(box, lx, ly, &output_x, &output_y);
// calculate squared distance suitable for comparison // calculate squared distance suitable for comparison
output_distance = output_distance =
(x - output_x) * (x - output_x) + (y - output_y) * (y - output_y); (lx - output_x) * (lx - output_x) + (ly - output_y) * (ly - output_y);
if (!isfinite(output_distance)) { if (!isfinite(output_distance)) {
output_distance = DBL_MAX; output_distance = DBL_MAX;
@ -339,8 +343,12 @@ void wlr_output_layout_closest_point(struct wlr_output_layout *layout,
} }
} }
*dest_x = min_x; if (dest_lx) {
*dest_y = min_y; *dest_lx = min_x;
}
if (dest_ly) {
*dest_ly = min_y;
}
} }
struct wlr_box *wlr_output_layout_get_box( struct wlr_box *wlr_output_layout_get_box(
@ -425,7 +433,7 @@ struct wlr_output *wlr_output_layout_get_center_output(
struct wlr_output *wlr_output_layout_adjacent_output( struct wlr_output *wlr_output_layout_adjacent_output(
struct wlr_output_layout *layout, enum wlr_direction direction, struct wlr_output_layout *layout, enum wlr_direction direction,
struct wlr_output *reference, double ref_x, double ref_y) { struct wlr_output *reference, double ref_lx, double ref_ly) {
assert(reference); assert(reference);
struct wlr_box *ref_box = wlr_output_layout_get_box(layout, reference); struct wlr_box *ref_box = wlr_output_layout_get_box(layout, reference);
@ -460,9 +468,9 @@ struct wlr_output *wlr_output_layout_adjacent_output(
// calculate distance from the given reference point // calculate distance from the given reference point
double x, y; double x, y;
wlr_output_layout_closest_point(layout, l_output->output, wlr_output_layout_closest_point(layout, l_output->output,
ref_x, ref_y, &x, &y); ref_lx, ref_ly, &x, &y);
double distance = double distance =
(x - ref_x) * (x - ref_x) + (y - ref_y) * (y - ref_y); (x - ref_lx) * (x - ref_lx) + (y - ref_ly) * (y - ref_ly);
if (distance < min_distance) { if (distance < min_distance) {
min_distance = distance; min_distance = distance;
closest_output = l_output->output; closest_output = l_output->output;

@ -1258,8 +1258,9 @@ bool wlr_seat_touch_has_grab(struct wlr_seat *seat) {
} }
bool wlr_seat_validate_grab_serial(struct wlr_seat *seat, uint32_t serial) { bool wlr_seat_validate_grab_serial(struct wlr_seat *seat, uint32_t serial) {
return serial == seat->pointer_state.grab_serial || return true;
serial == seat->touch_state.grab_serial; //return serial == seat->pointer_state.grab_serial ||
// serial == seat->touch_state.grab_serial;
} }
struct wlr_seat_client *wlr_seat_client_from_resource( struct wlr_seat_client *wlr_seat_client_from_resource(

@ -155,8 +155,24 @@ static bool wlr_surface_update_size(struct wlr_surface *surface,
int scale = state->scale; int scale = state->scale;
enum wl_output_transform transform = state->transform; enum wl_output_transform transform = state->transform;
wlr_texture_get_buffer_size(surface->texture, state->buffer, 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); &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;
}
int width = state->buffer_width / scale; int width = state->buffer_width / scale;
int height = state->buffer_height / scale; int height = state->buffer_height / scale;
@ -315,44 +331,41 @@ static void wlr_surface_damage_subsurfaces(struct wlr_subsurface *subsurface) {
} }
static void wlr_surface_apply_damage(struct wlr_surface *surface, static void wlr_surface_apply_damage(struct wlr_surface *surface,
bool reupload_buffer) { bool invalid_buffer, bool reupload_buffer) {
if (!surface->current->buffer) { struct wl_resource *resource = surface->current->buffer;
return; if (resource == NULL) {
}
struct wl_shm_buffer *buffer = wl_shm_buffer_get(surface->current->buffer);
if (!buffer) {
if (wlr_renderer_buffer_is_drm(surface->renderer,
surface->current->buffer)) {
wlr_texture_upload_drm(surface->texture, surface->current->buffer);
goto release;
} else if (wlr_dmabuf_resource_is_buffer(surface->current->buffer)) {
wlr_texture_upload_dmabuf(surface->texture, surface->current->buffer);
goto release;
} else {
wlr_log(L_INFO, "Unknown buffer handle attached");
return; return;
} }
}
uint32_t format = wl_shm_buffer_get_format(buffer); struct wl_shm_buffer *buf = wl_shm_buffer_get(resource);
if (reupload_buffer) { if (buf != NULL) {
wlr_texture_upload_shm(surface->texture, format, buffer); wl_shm_buffer_begin_access(buf);
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->texture == NULL || reupload_buffer) {
wlr_texture_destroy(surface->texture);
surface->texture = wlr_texture_from_pixels(surface->renderer, fmt,
stride, width, height, data);
} else { } else {
pixman_region32_t damage; pixman_region32_t damage;
pixman_region32_init(&damage); pixman_region32_init(&damage);
pixman_region32_copy(&damage, &surface->current->buffer_damage); pixman_region32_copy(&damage, &surface->current->buffer_damage);
pixman_region32_intersect_rect(&damage, &damage, 0, 0, pixman_region32_intersect_rect(&damage, &damage, 0, 0,
surface->current->buffer_width, surface->current->buffer_height); surface->current->buffer_width,
surface->current->buffer_height);
int n; int n;
pixman_box32_t *rects = pixman_region32_rectangles(&damage, &n); pixman_box32_t *rects = pixman_region32_rectangles(&damage, &n);
for (int i = 0; i < n; ++i) { for (int i = 0; i < n; ++i) {
pixman_box32_t rect = rects[i]; pixman_box32_t *r = &rects[i];
if (!wlr_texture_update_shm(surface->texture, format, if (!wlr_texture_write_pixels(surface->texture, fmt, stride,
rect.x1, rect.y1, r->x2 - r->x1, r->y2 - r->y1, r->x1, r->y1,
rect.x2 - rect.x1, r->x1, r->y1, data)) {
rect.y2 - rect.y1,
buffer)) {
break; break;
} }
} }
@ -360,7 +373,24 @@ static void wlr_surface_apply_damage(struct wlr_surface *surface,
pixman_region32_fini(&damage); pixman_region32_fini(&damage);
} }
release: 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");
}
}
wlr_surface_state_release_buffer(surface->current); wlr_surface_state_release_buffer(surface->current);
} }
@ -368,19 +398,19 @@ static void wlr_surface_commit_pending(struct wlr_surface *surface) {
int32_t oldw = surface->current->buffer_width; int32_t oldw = surface->current->buffer_width;
int32_t oldh = surface->current->buffer_height; int32_t oldh = surface->current->buffer_height;
bool null_buffer_commit = bool invalid_buffer = surface->pending->invalid & WLR_SURFACE_INVALID_BUFFER;
(surface->pending->invalid & WLR_SURFACE_INVALID_BUFFER && bool null_buffer_commit = invalid_buffer && surface->pending->buffer == NULL;
surface->pending->buffer == NULL);
wlr_surface_move_state(surface, surface->pending, surface->current); wlr_surface_move_state(surface, surface->pending, surface->current);
if (null_buffer_commit) { if (null_buffer_commit) {
surface->texture->valid = false; wlr_texture_destroy(surface->texture);
surface->texture = NULL;
} }
bool reupload_buffer = oldw != surface->current->buffer_width || bool reupload_buffer = oldw != surface->current->buffer_width ||
oldh != surface->current->buffer_height; oldh != surface->current->buffer_height;
wlr_surface_apply_damage(surface, reupload_buffer); wlr_surface_apply_damage(surface, invalid_buffer, reupload_buffer);
// commit subsurface order // commit subsurface order
struct wlr_subsurface *subsurface; struct wlr_subsurface *subsurface;
@ -611,7 +641,6 @@ struct wlr_surface *wlr_surface_create(struct wl_resource *res,
} }
wlr_log(L_DEBUG, "New wlr_surface %p (res %p)", surface, res); wlr_log(L_DEBUG, "New wlr_surface %p (res %p)", surface, res);
surface->renderer = renderer; surface->renderer = renderer;
surface->texture = wlr_render_texture_create(renderer);
surface->resource = res; surface->resource = res;
surface->current = wlr_surface_state_create(); surface->current = wlr_surface_state_create();
@ -628,7 +657,7 @@ struct wlr_surface *wlr_surface_create(struct wl_resource *res,
} }
bool wlr_surface_has_buffer(struct wlr_surface *surface) { bool wlr_surface_has_buffer(struct wlr_surface *surface) {
return surface->texture && surface->texture->valid; return surface->texture != NULL;
} }
int wlr_surface_set_role(struct wlr_surface *surface, const char *role, int wlr_surface_set_role(struct wlr_surface *surface, const char *role,
@ -923,3 +952,10 @@ void wlr_surface_set_role_committed(struct wlr_surface *surface,
surface->role_committed = role_committed; surface->role_committed = role_committed;
surface->role_data = role_data; surface->role_data = role_data;
} }
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);
}

@ -77,7 +77,7 @@ void wlr_xcursor_manager_set_cursor_image(struct wlr_xcursor_manager *manager,
} }
struct wlr_xcursor_image *image = xcursor->images[0]; struct wlr_xcursor_image *image = xcursor->images[0];
wlr_cursor_set_image(cursor, image->buffer, image->width, wlr_cursor_set_image(cursor, image->buffer, image->width * 4,
image->width, image->height, image->hotspot_x, image->hotspot_y, image->width, image->height, image->hotspot_x, image->hotspot_y,
theme->scale); theme->scale);
} }

@ -198,16 +198,15 @@ static void xdg_surface_unmap(struct wlr_xdg_surface *surface) {
wlr_signal_emit_safe(&surface->events.unmap, surface); wlr_signal_emit_safe(&surface->events.unmap, surface);
} }
if (surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) { switch (surface->role) {
wl_resource_set_user_data(surface->toplevel->resource, NULL); case WLR_XDG_SURFACE_ROLE_TOPLEVEL:
free(surface->toplevel); free(surface->toplevel->title);
surface->toplevel = NULL; surface->toplevel->title = NULL;
} free(surface->toplevel->app_id);
surface->toplevel->app_id = NULL;
if (surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { break;
wl_resource_set_user_data(surface->popup->resource, NULL); case WLR_XDG_SURFACE_ROLE_POPUP:
if (surface->popup->seat != NULL) {
if (surface->popup->seat) {
struct wlr_xdg_popup_grab *grab = struct wlr_xdg_popup_grab *grab =
xdg_shell_popup_grab_from_seat(surface->client->shell, xdg_shell_popup_grab_from_seat(surface->client->shell,
surface->popup->seat); surface->popup->seat);
@ -222,11 +221,12 @@ static void xdg_surface_unmap(struct wlr_xdg_surface *surface) {
wlr_seat_keyboard_end_grab(grab->seat); wlr_seat_keyboard_end_grab(grab->seat);
} }
} }
}
wl_list_remove(&surface->popup->link); surface->popup->seat = NULL;
free(surface->popup); }
surface->popup = NULL; break;
case WLR_XDG_SURFACE_ROLE_NONE:
assert(false && "not reached");
} }
struct wlr_xdg_surface_configure *configure, *tmp; struct wlr_xdg_surface_configure *configure, *tmp;
@ -234,13 +234,7 @@ static void xdg_surface_unmap(struct wlr_xdg_surface *surface) {
xdg_surface_configure_destroy(configure); xdg_surface_configure_destroy(configure);
} }
surface->role = WLR_XDG_SURFACE_ROLE_NONE; surface->configured = surface->mapped = false;
free(surface->title);
surface->title = NULL;
free(surface->app_id);
surface->app_id = NULL;
surface->added = surface->configured = surface->mapped = false;
surface->configure_serial = 0; surface->configure_serial = 0;
if (surface->configure_idle) { if (surface->configure_idle) {
wl_event_source_remove(surface->configure_idle); wl_event_source_remove(surface->configure_idle);
@ -253,6 +247,29 @@ static void xdg_surface_unmap(struct wlr_xdg_surface *surface) {
memset(&surface->next_geometry, 0, sizeof(struct wlr_box)); memset(&surface->next_geometry, 0, sizeof(struct wlr_box));
} }
static void xdg_toplevel_destroy(struct wlr_xdg_surface *surface) {
assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL);
xdg_surface_unmap(surface);
wl_resource_set_user_data(surface->toplevel->resource, NULL);
free(surface->toplevel);
surface->toplevel = NULL;
surface->role = WLR_XDG_SURFACE_ROLE_NONE;
}
static void xdg_popup_destroy(struct wlr_xdg_surface *surface) {
assert(surface->role == WLR_XDG_SURFACE_ROLE_POPUP);
xdg_surface_unmap(surface);
wl_resource_set_user_data(surface->popup->resource, NULL);
wl_list_remove(&surface->popup->link);
free(surface->popup);
surface->popup = NULL;
surface->role = WLR_XDG_SURFACE_ROLE_NONE;
}
static void xdg_surface_destroy(struct wlr_xdg_surface *surface) { static void xdg_surface_destroy(struct wlr_xdg_surface *surface) {
if (surface->role != WLR_XDG_SURFACE_ROLE_NONE) { if (surface->role != WLR_XDG_SURFACE_ROLE_NONE) {
xdg_surface_unmap(surface); xdg_surface_unmap(surface);
@ -260,6 +277,18 @@ static void xdg_surface_destroy(struct wlr_xdg_surface *surface) {
wlr_signal_emit_safe(&surface->events.destroy, surface); wlr_signal_emit_safe(&surface->events.destroy, surface);
switch (surface->role) {
case WLR_XDG_SURFACE_ROLE_TOPLEVEL:
xdg_toplevel_destroy(surface);
break;
case WLR_XDG_SURFACE_ROLE_POPUP:
xdg_popup_destroy(surface);
break;
case WLR_XDG_SURFACE_ROLE_NONE:
// This space is intentionally left blank
break;
}
wl_resource_set_user_data(surface->resource, NULL); wl_resource_set_user_data(surface->resource, NULL);
wl_list_remove(&surface->link); wl_list_remove(&surface->link);
wl_list_remove(&surface->surface_destroy_listener.link); wl_list_remove(&surface->surface_destroy_listener.link);
@ -574,7 +603,7 @@ static void xdg_popup_resource_destroy(struct wl_resource *resource) {
struct wlr_xdg_surface *surface = struct wlr_xdg_surface *surface =
xdg_surface_from_xdg_popup_resource(resource); xdg_surface_from_xdg_popup_resource(resource);
if (surface != NULL) { if (surface != NULL) {
xdg_surface_unmap(surface); xdg_popup_destroy(surface);
} }
} }
@ -673,8 +702,8 @@ static void xdg_toplevel_handle_set_title(struct wl_client *client,
return; return;
} }
free(surface->title); free(surface->toplevel->title);
surface->title = tmp; surface->toplevel->title = tmp;
} }
static void xdg_toplevel_handle_set_app_id(struct wl_client *client, static void xdg_toplevel_handle_set_app_id(struct wl_client *client,
@ -688,8 +717,8 @@ static void xdg_toplevel_handle_set_app_id(struct wl_client *client,
return; return;
} }
free(surface->app_id); free(surface->toplevel->app_id);
surface->app_id = tmp; surface->toplevel->app_id = tmp;
} }
static void xdg_toplevel_handle_show_window_menu(struct wl_client *client, static void xdg_toplevel_handle_show_window_menu(struct wl_client *client,
@ -720,7 +749,7 @@ static void xdg_toplevel_handle_show_window_menu(struct wl_client *client,
.y = y, .y = y,
}; };
wlr_signal_emit_safe(&surface->events.request_show_window_menu, &event); wlr_signal_emit_safe(&surface->toplevel->events.request_show_window_menu, &event);
} }
static void xdg_toplevel_handle_move(struct wl_client *client, static void xdg_toplevel_handle_move(struct wl_client *client,
@ -749,7 +778,7 @@ static void xdg_toplevel_handle_move(struct wl_client *client,
.serial = serial, .serial = serial,
}; };
wlr_signal_emit_safe(&surface->events.request_move, &event); wlr_signal_emit_safe(&surface->toplevel->events.request_move, &event);
} }
static void xdg_toplevel_handle_resize(struct wl_client *client, static void xdg_toplevel_handle_resize(struct wl_client *client,
@ -779,39 +808,39 @@ static void xdg_toplevel_handle_resize(struct wl_client *client,
.edges = edges, .edges = edges,
}; };
wlr_signal_emit_safe(&surface->events.request_resize, &event); wlr_signal_emit_safe(&surface->toplevel->events.request_resize, &event);
} }
static void xdg_toplevel_handle_set_max_size(struct wl_client *client, static void xdg_toplevel_handle_set_max_size(struct wl_client *client,
struct wl_resource *resource, int32_t width, int32_t height) { struct wl_resource *resource, int32_t width, int32_t height) {
struct wlr_xdg_surface *surface = struct wlr_xdg_surface *surface =
xdg_surface_from_xdg_toplevel_resource(resource); xdg_surface_from_xdg_toplevel_resource(resource);
surface->toplevel->next.max_width = width; surface->toplevel->client_pending.max_width = width;
surface->toplevel->next.max_height = height; surface->toplevel->client_pending.max_height = height;
} }
static void xdg_toplevel_handle_set_min_size(struct wl_client *client, static void xdg_toplevel_handle_set_min_size(struct wl_client *client,
struct wl_resource *resource, int32_t width, int32_t height) { struct wl_resource *resource, int32_t width, int32_t height) {
struct wlr_xdg_surface *surface = struct wlr_xdg_surface *surface =
xdg_surface_from_xdg_toplevel_resource(resource); xdg_surface_from_xdg_toplevel_resource(resource);
surface->toplevel->next.min_width = width; surface->toplevel->client_pending.min_width = width;
surface->toplevel->next.min_height = height; surface->toplevel->client_pending.min_height = height;
} }
static void xdg_toplevel_handle_set_maximized(struct wl_client *client, static void xdg_toplevel_handle_set_maximized(struct wl_client *client,
struct wl_resource *resource) { struct wl_resource *resource) {
struct wlr_xdg_surface *surface = struct wlr_xdg_surface *surface =
xdg_surface_from_xdg_toplevel_resource(resource); xdg_surface_from_xdg_toplevel_resource(resource);
surface->toplevel->next.maximized = true; surface->toplevel->client_pending.maximized = true;
wlr_signal_emit_safe(&surface->events.request_maximize, surface); wlr_signal_emit_safe(&surface->toplevel->events.request_maximize, surface);
} }
static void xdg_toplevel_handle_unset_maximized(struct wl_client *client, static void xdg_toplevel_handle_unset_maximized(struct wl_client *client,
struct wl_resource *resource) { struct wl_resource *resource) {
struct wlr_xdg_surface *surface = struct wlr_xdg_surface *surface =
xdg_surface_from_xdg_toplevel_resource(resource); xdg_surface_from_xdg_toplevel_resource(resource);
surface->toplevel->next.maximized = false; surface->toplevel->client_pending.maximized = false;
wlr_signal_emit_safe(&surface->events.request_maximize, surface); wlr_signal_emit_safe(&surface->toplevel->events.request_maximize, surface);
} }
static void xdg_toplevel_handle_set_fullscreen(struct wl_client *client, static void xdg_toplevel_handle_set_fullscreen(struct wl_client *client,
@ -824,7 +853,7 @@ static void xdg_toplevel_handle_set_fullscreen(struct wl_client *client,
output = wlr_output_from_resource(output_resource); output = wlr_output_from_resource(output_resource);
} }
surface->toplevel->next.fullscreen = true; surface->toplevel->client_pending.fullscreen = true;
struct wlr_xdg_toplevel_set_fullscreen_event event = { struct wlr_xdg_toplevel_set_fullscreen_event event = {
.surface = surface, .surface = surface,
@ -832,7 +861,7 @@ static void xdg_toplevel_handle_set_fullscreen(struct wl_client *client,
.output = output, .output = output,
}; };
wlr_signal_emit_safe(&surface->events.request_fullscreen, &event); wlr_signal_emit_safe(&surface->toplevel->events.request_fullscreen, &event);
} }
static void xdg_toplevel_handle_unset_fullscreen(struct wl_client *client, static void xdg_toplevel_handle_unset_fullscreen(struct wl_client *client,
@ -840,7 +869,7 @@ static void xdg_toplevel_handle_unset_fullscreen(struct wl_client *client,
struct wlr_xdg_surface *surface = struct wlr_xdg_surface *surface =
xdg_surface_from_xdg_toplevel_resource(resource); xdg_surface_from_xdg_toplevel_resource(resource);
surface->toplevel->next.fullscreen = false; surface->toplevel->client_pending.fullscreen = false;
struct wlr_xdg_toplevel_set_fullscreen_event event = { struct wlr_xdg_toplevel_set_fullscreen_event event = {
.surface = surface, .surface = surface,
@ -848,14 +877,14 @@ static void xdg_toplevel_handle_unset_fullscreen(struct wl_client *client,
.output = NULL, .output = NULL,
}; };
wlr_signal_emit_safe(&surface->events.request_fullscreen, &event); wlr_signal_emit_safe(&surface->toplevel->events.request_fullscreen, &event);
} }
static void xdg_toplevel_handle_set_minimized(struct wl_client *client, static void xdg_toplevel_handle_set_minimized(struct wl_client *client,
struct wl_resource *resource) { struct wl_resource *resource) {
struct wlr_xdg_surface *surface = struct wlr_xdg_surface *surface =
xdg_surface_from_xdg_toplevel_resource(resource); xdg_surface_from_xdg_toplevel_resource(resource);
wlr_signal_emit_safe(&surface->events.request_minimize, surface); wlr_signal_emit_safe(&surface->toplevel->events.request_minimize, surface);
} }
static const struct xdg_toplevel_interface xdg_toplevel_implementation = { static const struct xdg_toplevel_interface xdg_toplevel_implementation = {
@ -887,7 +916,7 @@ static void xdg_toplevel_resource_destroy(struct wl_resource *resource) {
struct wlr_xdg_surface *surface = struct wlr_xdg_surface *surface =
xdg_surface_from_xdg_toplevel_resource(resource); xdg_surface_from_xdg_toplevel_resource(resource);
if (surface != NULL) { if (surface != NULL) {
xdg_surface_unmap(surface); xdg_toplevel_destroy(surface);
} }
} }
@ -905,6 +934,12 @@ static void xdg_surface_handle_get_toplevel(struct wl_client *client,
wl_resource_post_no_memory(resource); wl_resource_post_no_memory(resource);
return; return;
} }
wl_signal_init(&surface->toplevel->events.request_maximize);
wl_signal_init(&surface->toplevel->events.request_fullscreen);
wl_signal_init(&surface->toplevel->events.request_minimize);
wl_signal_init(&surface->toplevel->events.request_move);
wl_signal_init(&surface->toplevel->events.request_resize);
wl_signal_init(&surface->toplevel->events.request_show_window_menu);
surface->role = WLR_XDG_SURFACE_ROLE_TOPLEVEL; surface->role = WLR_XDG_SURFACE_ROLE_TOPLEVEL;
surface->toplevel->base = surface; surface->toplevel->base = surface;
@ -1052,25 +1087,25 @@ static bool wlr_xdg_surface_toplevel_state_compare(
configured.height = configure->toplevel_state->height; configured.height = configure->toplevel_state->height;
} }
if (state->pending.activated != configured.state.activated) { if (state->server_pending.activated != configured.state.activated) {
return false; return false;
} }
if (state->pending.fullscreen != configured.state.fullscreen) { if (state->server_pending.fullscreen != configured.state.fullscreen) {
return false; return false;
} }
if (state->pending.maximized != configured.state.maximized) { if (state->server_pending.maximized != configured.state.maximized) {
return false; return false;
} }
if (state->pending.resizing != configured.state.resizing) { if (state->server_pending.resizing != configured.state.resizing) {
return false; return false;
} }
if (state->pending.width == configured.width && if (state->server_pending.width == configured.width &&
state->pending.height == configured.height) { state->server_pending.height == configured.height) {
return true; return true;
} }
if (state->pending.width == 0 && state->pending.height == 0) { if (state->server_pending.width == 0 && state->server_pending.height == 0) {
return true; return true;
} }
@ -1088,12 +1123,12 @@ static void wlr_xdg_toplevel_send_configure(
wl_resource_post_no_memory(surface->toplevel->resource); wl_resource_post_no_memory(surface->toplevel->resource);
return; return;
} }
*configure->toplevel_state = surface->toplevel->pending; *configure->toplevel_state = surface->toplevel->server_pending;
uint32_t *s; uint32_t *s;
struct wl_array states; struct wl_array states;
wl_array_init(&states); wl_array_init(&states);
if (surface->toplevel->pending.maximized) { if (surface->toplevel->server_pending.maximized) {
s = wl_array_add(&states, sizeof(uint32_t)); s = wl_array_add(&states, sizeof(uint32_t));
if (!s) { if (!s) {
wlr_log(L_ERROR, "Could not allocate state for maximized xdg_toplevel"); wlr_log(L_ERROR, "Could not allocate state for maximized xdg_toplevel");
@ -1101,7 +1136,7 @@ static void wlr_xdg_toplevel_send_configure(
} }
*s = XDG_TOPLEVEL_STATE_MAXIMIZED; *s = XDG_TOPLEVEL_STATE_MAXIMIZED;
} }
if (surface->toplevel->pending.fullscreen) { if (surface->toplevel->server_pending.fullscreen) {
s = wl_array_add(&states, sizeof(uint32_t)); s = wl_array_add(&states, sizeof(uint32_t));
if (!s) { if (!s) {
wlr_log(L_ERROR, "Could not allocate state for fullscreen xdg_toplevel"); wlr_log(L_ERROR, "Could not allocate state for fullscreen xdg_toplevel");
@ -1109,7 +1144,7 @@ static void wlr_xdg_toplevel_send_configure(
} }
*s = XDG_TOPLEVEL_STATE_FULLSCREEN; *s = XDG_TOPLEVEL_STATE_FULLSCREEN;
} }
if (surface->toplevel->pending.resizing) { if (surface->toplevel->server_pending.resizing) {
s = wl_array_add(&states, sizeof(uint32_t)); s = wl_array_add(&states, sizeof(uint32_t));
if (!s) { if (!s) {
wlr_log(L_ERROR, "Could not allocate state for resizing xdg_toplevel"); wlr_log(L_ERROR, "Could not allocate state for resizing xdg_toplevel");
@ -1117,7 +1152,7 @@ static void wlr_xdg_toplevel_send_configure(
} }
*s = XDG_TOPLEVEL_STATE_RESIZING; *s = XDG_TOPLEVEL_STATE_RESIZING;
} }
if (surface->toplevel->pending.activated) { if (surface->toplevel->server_pending.activated) {
s = wl_array_add(&states, sizeof(uint32_t)); s = wl_array_add(&states, sizeof(uint32_t));
if (!s) { if (!s) {
wlr_log(L_ERROR, "Could not allocate state for activated xdg_toplevel"); wlr_log(L_ERROR, "Could not allocate state for activated xdg_toplevel");
@ -1126,8 +1161,8 @@ static void wlr_xdg_toplevel_send_configure(
*s = XDG_TOPLEVEL_STATE_ACTIVATED; *s = XDG_TOPLEVEL_STATE_ACTIVATED;
} }
uint32_t width = surface->toplevel->pending.width; uint32_t width = surface->toplevel->server_pending.width;
uint32_t height = surface->toplevel->pending.height; uint32_t height = surface->toplevel->server_pending.height;
xdg_toplevel_send_configure(surface->toplevel->resource, width, height, xdg_toplevel_send_configure(surface->toplevel->resource, width, height,
&states); &states);
@ -1235,13 +1270,13 @@ static void wlr_xdg_surface_toplevel_committed(
// update state that doesn't need compositor approval // update state that doesn't need compositor approval
surface->toplevel->current.max_width = surface->toplevel->current.max_width =
surface->toplevel->next.max_width; surface->toplevel->client_pending.max_width;
surface->toplevel->current.min_width = surface->toplevel->current.min_width =
surface->toplevel->next.min_width; surface->toplevel->client_pending.min_width;
surface->toplevel->current.max_height = surface->toplevel->current.max_height =
surface->toplevel->next.max_height; surface->toplevel->client_pending.max_height;
surface->toplevel->current.min_height = surface->toplevel->current.min_height =
surface->toplevel->next.min_height; surface->toplevel->client_pending.min_height;
} }
static void wlr_xdg_surface_popup_committed( static void wlr_xdg_surface_popup_committed(
@ -1349,12 +1384,6 @@ static void xdg_shell_handle_get_xdg_surface(struct wl_client *wl_client,
wl_list_init(&surface->configure_list); wl_list_init(&surface->configure_list);
wl_list_init(&surface->popups); wl_list_init(&surface->popups);
wl_signal_init(&surface->events.request_maximize);
wl_signal_init(&surface->events.request_fullscreen);
wl_signal_init(&surface->events.request_minimize);
wl_signal_init(&surface->events.request_move);
wl_signal_init(&surface->events.request_resize);
wl_signal_init(&surface->events.request_show_window_menu);
wl_signal_init(&surface->events.destroy); wl_signal_init(&surface->events.destroy);
wl_signal_init(&surface->events.ping_timeout); wl_signal_init(&surface->events.ping_timeout);
wl_signal_init(&surface->events.new_popup); wl_signal_init(&surface->events.new_popup);
@ -1532,8 +1561,8 @@ void wlr_xdg_surface_ping(struct wlr_xdg_surface *surface) {
uint32_t wlr_xdg_toplevel_set_size(struct wlr_xdg_surface *surface, uint32_t wlr_xdg_toplevel_set_size(struct wlr_xdg_surface *surface,
uint32_t width, uint32_t height) { uint32_t width, uint32_t height) {
assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL);
surface->toplevel->pending.width = width; surface->toplevel->server_pending.width = width;
surface->toplevel->pending.height = height; surface->toplevel->server_pending.height = height;
return wlr_xdg_surface_schedule_configure(surface); return wlr_xdg_surface_schedule_configure(surface);
} }
@ -1541,7 +1570,7 @@ uint32_t wlr_xdg_toplevel_set_size(struct wlr_xdg_surface *surface,
uint32_t wlr_xdg_toplevel_set_activated(struct wlr_xdg_surface *surface, uint32_t wlr_xdg_toplevel_set_activated(struct wlr_xdg_surface *surface,
bool activated) { bool activated) {
assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL);
surface->toplevel->pending.activated = activated; surface->toplevel->server_pending.activated = activated;
return wlr_xdg_surface_schedule_configure(surface); return wlr_xdg_surface_schedule_configure(surface);
} }
@ -1549,7 +1578,7 @@ uint32_t wlr_xdg_toplevel_set_activated(struct wlr_xdg_surface *surface,
uint32_t wlr_xdg_toplevel_set_maximized(struct wlr_xdg_surface *surface, uint32_t wlr_xdg_toplevel_set_maximized(struct wlr_xdg_surface *surface,
bool maximized) { bool maximized) {
assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL);
surface->toplevel->pending.maximized = maximized; surface->toplevel->server_pending.maximized = maximized;
return wlr_xdg_surface_schedule_configure(surface); return wlr_xdg_surface_schedule_configure(surface);
} }
@ -1557,7 +1586,7 @@ uint32_t wlr_xdg_toplevel_set_maximized(struct wlr_xdg_surface *surface,
uint32_t wlr_xdg_toplevel_set_fullscreen(struct wlr_xdg_surface *surface, uint32_t wlr_xdg_toplevel_set_fullscreen(struct wlr_xdg_surface *surface,
bool fullscreen) { bool fullscreen) {
assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL);
surface->toplevel->pending.fullscreen = fullscreen; surface->toplevel->server_pending.fullscreen = fullscreen;
return wlr_xdg_surface_schedule_configure(surface); return wlr_xdg_surface_schedule_configure(surface);
} }
@ -1565,7 +1594,7 @@ uint32_t wlr_xdg_toplevel_set_fullscreen(struct wlr_xdg_surface *surface,
uint32_t wlr_xdg_toplevel_set_resizing(struct wlr_xdg_surface *surface, uint32_t wlr_xdg_toplevel_set_resizing(struct wlr_xdg_surface *surface,
bool resizing) { bool resizing) {
assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); assert(surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL);
surface->toplevel->pending.resizing = resizing; surface->toplevel->server_pending.resizing = resizing;
return wlr_xdg_surface_schedule_configure(surface); return wlr_xdg_surface_schedule_configure(surface);
} }

@ -16,24 +16,11 @@
static const char *wlr_desktop_xdg_toplevel_role = "xdg_toplevel_v6"; static const char *wlr_desktop_xdg_toplevel_role = "xdg_toplevel_v6";
static const char *wlr_desktop_xdg_popup_role = "xdg_popup_v6"; static const char *wlr_desktop_xdg_popup_role = "xdg_popup_v6";
struct wlr_xdg_positioner_v6 { struct wlr_xdg_positioner_v6_resource {
struct wl_resource *resource; struct wl_resource *resource;
struct wlr_xdg_positioner_v6 attrs;
struct wlr_box anchor_rect;
enum zxdg_positioner_v6_anchor anchor;
enum zxdg_positioner_v6_gravity gravity;
enum zxdg_positioner_v6_constraint_adjustment constraint_adjustment;
struct {
int32_t width, height;
} size;
struct {
int32_t x, y;
} offset;
}; };
static void resource_handle_destroy(struct wl_client *client, static void resource_handle_destroy(struct wl_client *client,
struct wl_resource *resource) { struct wl_resource *resource) {
wl_resource_destroy(resource); wl_resource_destroy(resource);
@ -110,8 +97,8 @@ static void xdg_keyboard_grab_enter(struct wlr_seat_keyboard_grab *grab,
// keyboard focus should remain on the popup // keyboard focus should remain on the popup
} }
static void xdg_keyboard_grab_key(struct wlr_seat_keyboard_grab *grab, uint32_t time, static void xdg_keyboard_grab_key(struct wlr_seat_keyboard_grab *grab,
uint32_t key, uint32_t state) { uint32_t time, uint32_t key, uint32_t state) {
wlr_seat_keyboard_send_key(grab->seat, time, key, state); wlr_seat_keyboard_send_key(grab->seat, time, key, state);
} }
@ -198,16 +185,15 @@ static void xdg_surface_unmap(struct wlr_xdg_surface_v6 *surface) {
wlr_signal_emit_safe(&surface->events.unmap, surface); wlr_signal_emit_safe(&surface->events.unmap, surface);
} }
if (surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) { switch (surface->role) {
wl_resource_set_user_data(surface->toplevel->resource, NULL); case WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL:
free(surface->toplevel); free(surface->toplevel->title);
surface->toplevel = NULL; surface->toplevel->title = NULL;
} free(surface->toplevel->app_id);
surface->toplevel->app_id = NULL;
if (surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) { break;
wl_resource_set_user_data(surface->popup->resource, NULL); case WLR_XDG_SURFACE_V6_ROLE_POPUP:
if (surface->popup->seat != NULL) {
if (surface->popup->seat) {
struct wlr_xdg_popup_grab_v6 *grab = struct wlr_xdg_popup_grab_v6 *grab =
xdg_shell_popup_grab_from_seat(surface->client->shell, xdg_shell_popup_grab_from_seat(surface->client->shell,
surface->popup->seat); surface->popup->seat);
@ -222,11 +208,12 @@ static void xdg_surface_unmap(struct wlr_xdg_surface_v6 *surface) {
wlr_seat_keyboard_end_grab(grab->seat); wlr_seat_keyboard_end_grab(grab->seat);
} }
} }
}
wl_list_remove(&surface->popup->link); surface->popup->seat = NULL;
free(surface->popup); }
surface->popup = NULL; break;
case WLR_XDG_SURFACE_V6_ROLE_NONE:
assert(false && "not reached");
} }
struct wlr_xdg_surface_v6_configure *configure, *tmp; struct wlr_xdg_surface_v6_configure *configure, *tmp;
@ -234,13 +221,7 @@ static void xdg_surface_unmap(struct wlr_xdg_surface_v6 *surface) {
xdg_surface_configure_destroy(configure); xdg_surface_configure_destroy(configure);
} }
surface->role = WLR_XDG_SURFACE_V6_ROLE_NONE; surface->configured = surface->mapped = false;
free(surface->title);
surface->title = NULL;
free(surface->app_id);
surface->app_id = NULL;
surface->added = surface->configured = surface->mapped = false;
surface->configure_serial = 0; surface->configure_serial = 0;
if (surface->configure_idle) { if (surface->configure_idle) {
wl_event_source_remove(surface->configure_idle); wl_event_source_remove(surface->configure_idle);
@ -253,6 +234,29 @@ static void xdg_surface_unmap(struct wlr_xdg_surface_v6 *surface) {
memset(&surface->next_geometry, 0, sizeof(struct wlr_box)); memset(&surface->next_geometry, 0, sizeof(struct wlr_box));
} }
static void xdg_toplevel_destroy(struct wlr_xdg_surface_v6 *surface) {
assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL);
xdg_surface_unmap(surface);
wl_resource_set_user_data(surface->toplevel->resource, NULL);
free(surface->toplevel);
surface->toplevel = NULL;
surface->role = WLR_XDG_SURFACE_V6_ROLE_NONE;
}
static void xdg_popup_destroy(struct wlr_xdg_surface_v6 *surface) {
assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP);
xdg_surface_unmap(surface);
wl_resource_set_user_data(surface->popup->resource, NULL);
wl_list_remove(&surface->popup->link);
free(surface->popup);
surface->popup = NULL;
surface->role = WLR_XDG_SURFACE_V6_ROLE_NONE;
}
static void xdg_surface_destroy(struct wlr_xdg_surface_v6 *surface) { static void xdg_surface_destroy(struct wlr_xdg_surface_v6 *surface) {
if (surface->role != WLR_XDG_SURFACE_V6_ROLE_NONE) { if (surface->role != WLR_XDG_SURFACE_V6_ROLE_NONE) {
xdg_surface_unmap(surface); xdg_surface_unmap(surface);
@ -260,6 +264,18 @@ static void xdg_surface_destroy(struct wlr_xdg_surface_v6 *surface) {
wlr_signal_emit_safe(&surface->events.destroy, surface); wlr_signal_emit_safe(&surface->events.destroy, surface);
switch (surface->role) {
case WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL:
xdg_toplevel_destroy(surface);
break;
case WLR_XDG_SURFACE_V6_ROLE_POPUP:
xdg_popup_destroy(surface);
break;
case WLR_XDG_SURFACE_V6_ROLE_NONE:
// This space is intentionally left blank
break;
}
wl_resource_set_user_data(surface->resource, NULL); wl_resource_set_user_data(surface->resource, NULL);
wl_list_remove(&surface->link); wl_list_remove(&surface->link);
wl_list_remove(&surface->surface_destroy_listener.link); wl_list_remove(&surface->surface_destroy_listener.link);
@ -271,7 +287,7 @@ static void xdg_surface_destroy(struct wlr_xdg_surface_v6 *surface) {
static const struct zxdg_positioner_v6_interface static const struct zxdg_positioner_v6_interface
zxdg_positioner_v6_implementation; zxdg_positioner_v6_implementation;
static struct wlr_xdg_positioner_v6 *xdg_positioner_from_resource( static struct wlr_xdg_positioner_v6_resource *xdg_positioner_from_resource(
struct wl_resource *resource) { struct wl_resource *resource) {
assert(wl_resource_instance_of(resource, &zxdg_positioner_v6_interface, assert(wl_resource_instance_of(resource, &zxdg_positioner_v6_interface,
&zxdg_positioner_v6_implementation)); &zxdg_positioner_v6_implementation));
@ -279,14 +295,14 @@ static struct wlr_xdg_positioner_v6 *xdg_positioner_from_resource(
} }
static void xdg_positioner_destroy(struct wl_resource *resource) { static void xdg_positioner_destroy(struct wl_resource *resource) {
struct wlr_xdg_positioner_v6 *positioner = struct wlr_xdg_positioner_v6_resource *positioner =
xdg_positioner_from_resource(resource); xdg_positioner_from_resource(resource);
free(positioner); free(positioner);
} }
static void xdg_positioner_handle_set_size(struct wl_client *client, static void xdg_positioner_handle_set_size(struct wl_client *client,
struct wl_resource *resource, int32_t width, int32_t height) { struct wl_resource *resource, int32_t width, int32_t height) {
struct wlr_xdg_positioner_v6 *positioner = struct wlr_xdg_positioner_v6_resource *positioner =
xdg_positioner_from_resource(resource); xdg_positioner_from_resource(resource);
if (width < 1 || height < 1) { if (width < 1 || height < 1) {
@ -296,14 +312,14 @@ static void xdg_positioner_handle_set_size(struct wl_client *client,
return; return;
} }
positioner->size.width = width; positioner->attrs.size.width = width;
positioner->size.height = height; positioner->attrs.size.height = height;
} }
static void xdg_positioner_handle_set_anchor_rect(struct wl_client *client, static void xdg_positioner_handle_set_anchor_rect(struct wl_client *client,
struct wl_resource *resource, int32_t x, int32_t y, int32_t width, struct wl_resource *resource, int32_t x, int32_t y, int32_t width,
int32_t height) { int32_t height) {
struct wlr_xdg_positioner_v6 *positioner = struct wlr_xdg_positioner_v6_resource *positioner =
xdg_positioner_from_resource(resource); xdg_positioner_from_resource(resource);
if (width < 1 || height < 1) { if (width < 1 || height < 1) {
@ -313,15 +329,15 @@ static void xdg_positioner_handle_set_anchor_rect(struct wl_client *client,
return; return;
} }
positioner->anchor_rect.x = x; positioner->attrs.anchor_rect.x = x;
positioner->anchor_rect.y = y; positioner->attrs.anchor_rect.y = y;
positioner->anchor_rect.width = width; positioner->attrs.anchor_rect.width = width;
positioner->anchor_rect.height = height; positioner->attrs.anchor_rect.height = height;
} }
static void xdg_positioner_handle_set_anchor(struct wl_client *client, static void xdg_positioner_handle_set_anchor(struct wl_client *client,
struct wl_resource *resource, uint32_t anchor) { struct wl_resource *resource, uint32_t anchor) {
struct wlr_xdg_positioner_v6 *positioner = struct wlr_xdg_positioner_v6_resource *positioner =
xdg_positioner_from_resource(resource); xdg_positioner_from_resource(resource);
if (((anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP ) && if (((anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP ) &&
@ -334,12 +350,12 @@ static void xdg_positioner_handle_set_anchor(struct wl_client *client,
return; return;
} }
positioner->anchor = anchor; positioner->attrs.anchor = anchor;
} }
static void xdg_positioner_handle_set_gravity(struct wl_client *client, static void xdg_positioner_handle_set_gravity(struct wl_client *client,
struct wl_resource *resource, uint32_t gravity) { struct wl_resource *resource, uint32_t gravity) {
struct wlr_xdg_positioner_v6 *positioner = struct wlr_xdg_positioner_v6_resource *positioner =
xdg_positioner_from_resource(resource); xdg_positioner_from_resource(resource);
if (((gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP) && if (((gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP) &&
@ -352,25 +368,25 @@ static void xdg_positioner_handle_set_gravity(struct wl_client *client,
return; return;
} }
positioner->gravity = gravity; positioner->attrs.gravity = gravity;
} }
static void xdg_positioner_handle_set_constraint_adjustment( static void xdg_positioner_handle_set_constraint_adjustment(
struct wl_client *client, struct wl_resource *resource, struct wl_client *client, struct wl_resource *resource,
uint32_t constraint_adjustment) { uint32_t constraint_adjustment) {
struct wlr_xdg_positioner_v6 *positioner = struct wlr_xdg_positioner_v6_resource *positioner =
xdg_positioner_from_resource(resource); xdg_positioner_from_resource(resource);
positioner->constraint_adjustment = constraint_adjustment; positioner->attrs.constraint_adjustment = constraint_adjustment;
} }
static void xdg_positioner_handle_set_offset(struct wl_client *client, static void xdg_positioner_handle_set_offset(struct wl_client *client,
struct wl_resource *resource, int32_t x, int32_t y) { struct wl_resource *resource, int32_t x, int32_t y) {
struct wlr_xdg_positioner_v6 *positioner = struct wlr_xdg_positioner_v6_resource *positioner =
xdg_positioner_from_resource(resource); xdg_positioner_from_resource(resource);
positioner->offset.x = x; positioner->attrs.offset.x = x;
positioner->offset.y = y; positioner->attrs.offset.y = y;
} }
static const struct zxdg_positioner_v6_interface static const struct zxdg_positioner_v6_interface
@ -387,8 +403,8 @@ static const struct zxdg_positioner_v6_interface
static void xdg_shell_handle_create_positioner(struct wl_client *wl_client, static void xdg_shell_handle_create_positioner(struct wl_client *wl_client,
struct wl_resource *resource, uint32_t id) { struct wl_resource *resource, uint32_t id) {
struct wlr_xdg_positioner_v6 *positioner = struct wlr_xdg_positioner_v6_resource *positioner =
calloc(1, sizeof(struct wlr_xdg_positioner_v6)); calloc(1, sizeof(struct wlr_xdg_positioner_v6_resource));
if (positioner == NULL) { if (positioner == NULL) {
wl_client_post_no_memory(wl_client); wl_client_post_no_memory(wl_client);
return; return;
@ -409,9 +425,8 @@ static void xdg_shell_handle_create_positioner(struct wl_client *wl_client,
positioner, xdg_positioner_destroy); positioner, xdg_positioner_destroy);
} }
static struct wlr_box xdg_positioner_get_geometry( struct wlr_box wlr_xdg_positioner_v6_get_geometry(
struct wlr_xdg_positioner_v6 *positioner, struct wlr_xdg_positioner_v6 *positioner) {
struct wlr_xdg_surface_v6 *surface, struct wlr_xdg_surface_v6 *parent) {
struct wlr_box geometry = { struct wlr_box geometry = {
.x = positioner->offset.x, .x = positioner->offset.x,
.y = positioner->offset.y, .y = positioner->offset.y,
@ -464,7 +479,6 @@ static struct wlr_box xdg_positioner_get_geometry(
return geometry; return geometry;
} }
static const struct zxdg_popup_v6_interface zxdg_popup_v6_implementation; static const struct zxdg_popup_v6_interface zxdg_popup_v6_implementation;
static struct wlr_xdg_surface_v6 *xdg_surface_from_xdg_popup_resource( static struct wlr_xdg_surface_v6 *xdg_surface_from_xdg_popup_resource(
@ -545,7 +559,7 @@ static void xdg_popup_resource_destroy(struct wl_resource *resource) {
struct wlr_xdg_surface_v6 *surface = struct wlr_xdg_surface_v6 *surface =
xdg_surface_from_xdg_popup_resource(resource); xdg_surface_from_xdg_popup_resource(resource);
if (surface != NULL) { if (surface != NULL) {
xdg_surface_unmap(surface); xdg_popup_destroy(surface);
} }
} }
@ -566,10 +580,11 @@ static void xdg_surface_handle_get_popup(struct wl_client *client,
xdg_surface_from_resource(resource); xdg_surface_from_resource(resource);
struct wlr_xdg_surface_v6 *parent = struct wlr_xdg_surface_v6 *parent =
xdg_surface_from_resource(parent_resource); xdg_surface_from_resource(parent_resource);
struct wlr_xdg_positioner_v6 *positioner = struct wlr_xdg_positioner_v6_resource *positioner =
xdg_positioner_from_resource(positioner_resource); xdg_positioner_from_resource(positioner_resource);
if (positioner->size.width == 0 || positioner->anchor_rect.width == 0) { if (positioner->attrs.size.width == 0 ||
positioner->attrs.anchor_rect.width == 0) {
wl_resource_post_error(resource, wl_resource_post_error(resource,
ZXDG_SHELL_V6_ERROR_INVALID_POSITIONER, ZXDG_SHELL_V6_ERROR_INVALID_POSITIONER,
"positioner object is not complete"); "positioner object is not complete");
@ -600,7 +615,12 @@ static void xdg_surface_handle_get_popup(struct wl_client *client,
surface->popup->base = surface; surface->popup->base = surface;
surface->popup->parent = parent; surface->popup->parent = parent;
surface->popup->geometry = surface->popup->geometry =
xdg_positioner_get_geometry(positioner, surface, parent); wlr_xdg_positioner_v6_get_geometry(&positioner->attrs);
// positioner properties
memcpy(&surface->popup->positioner, &positioner->attrs,
sizeof(struct wlr_xdg_positioner_v6));
wl_list_insert(&parent->popups, &surface->popup->link); wl_list_insert(&parent->popups, &surface->popup->link);
wl_resource_set_implementation(surface->popup->resource, wl_resource_set_implementation(surface->popup->resource,
@ -610,7 +630,6 @@ static void xdg_surface_handle_get_popup(struct wl_client *client,
wlr_signal_emit_safe(&parent->events.new_popup, surface->popup); wlr_signal_emit_safe(&parent->events.new_popup, surface->popup);
} }
static const struct zxdg_toplevel_v6_interface zxdg_toplevel_v6_implementation; static const struct zxdg_toplevel_v6_interface zxdg_toplevel_v6_implementation;
static struct wlr_xdg_surface_v6 *xdg_surface_from_xdg_toplevel_resource( static struct wlr_xdg_surface_v6 *xdg_surface_from_xdg_toplevel_resource(
@ -643,8 +662,8 @@ static void xdg_toplevel_handle_set_title(struct wl_client *client,
return; return;
} }
free(surface->title); free(surface->toplevel->title);
surface->title = tmp; surface->toplevel->title = tmp;
} }
static void xdg_toplevel_handle_set_app_id(struct wl_client *client, static void xdg_toplevel_handle_set_app_id(struct wl_client *client,
@ -657,8 +676,8 @@ static void xdg_toplevel_handle_set_app_id(struct wl_client *client,
return; return;
} }
free(surface->app_id); free(surface->toplevel->app_id);
surface->app_id = tmp; surface->toplevel->app_id = tmp;
} }
static void xdg_toplevel_handle_show_window_menu(struct wl_client *client, static void xdg_toplevel_handle_show_window_menu(struct wl_client *client,
@ -689,7 +708,8 @@ static void xdg_toplevel_handle_show_window_menu(struct wl_client *client,
.y = y, .y = y,
}; };
wlr_signal_emit_safe(&surface->events.request_show_window_menu, &event); wlr_signal_emit_safe(&surface->toplevel->events.request_show_window_menu,
&event);
} }
static void xdg_toplevel_handle_move(struct wl_client *client, static void xdg_toplevel_handle_move(struct wl_client *client,
@ -718,7 +738,7 @@ static void xdg_toplevel_handle_move(struct wl_client *client,
.serial = serial, .serial = serial,
}; };
wlr_signal_emit_safe(&surface->events.request_move, &event); wlr_signal_emit_safe(&surface->toplevel->events.request_move, &event);
} }
static void xdg_toplevel_handle_resize(struct wl_client *client, static void xdg_toplevel_handle_resize(struct wl_client *client,
@ -748,39 +768,39 @@ static void xdg_toplevel_handle_resize(struct wl_client *client,
.edges = edges, .edges = edges,
}; };
wlr_signal_emit_safe(&surface->events.request_resize, &event); wlr_signal_emit_safe(&surface->toplevel->events.request_resize, &event);
} }
static void xdg_toplevel_handle_set_max_size(struct wl_client *client, static void xdg_toplevel_handle_set_max_size(struct wl_client *client,
struct wl_resource *resource, int32_t width, int32_t height) { struct wl_resource *resource, int32_t width, int32_t height) {
struct wlr_xdg_surface_v6 *surface = struct wlr_xdg_surface_v6 *surface =
xdg_surface_from_xdg_toplevel_resource(resource); xdg_surface_from_xdg_toplevel_resource(resource);
surface->toplevel->next.max_width = width; surface->toplevel->client_pending.max_width = width;
surface->toplevel->next.max_height = height; surface->toplevel->client_pending.max_height = height;
} }
static void xdg_toplevel_handle_set_min_size(struct wl_client *client, static void xdg_toplevel_handle_set_min_size(struct wl_client *client,
struct wl_resource *resource, int32_t width, int32_t height) { struct wl_resource *resource, int32_t width, int32_t height) {
struct wlr_xdg_surface_v6 *surface = struct wlr_xdg_surface_v6 *surface =
xdg_surface_from_xdg_toplevel_resource(resource); xdg_surface_from_xdg_toplevel_resource(resource);
surface->toplevel->next.min_width = width; surface->toplevel->client_pending.min_width = width;
surface->toplevel->next.min_height = height; surface->toplevel->client_pending.min_height = height;
} }
static void xdg_toplevel_handle_set_maximized(struct wl_client *client, static void xdg_toplevel_handle_set_maximized(struct wl_client *client,
struct wl_resource *resource) { struct wl_resource *resource) {
struct wlr_xdg_surface_v6 *surface = struct wlr_xdg_surface_v6 *surface =
xdg_surface_from_xdg_toplevel_resource(resource); xdg_surface_from_xdg_toplevel_resource(resource);
surface->toplevel->next.maximized = true; surface->toplevel->client_pending.maximized = true;
wlr_signal_emit_safe(&surface->events.request_maximize, surface); wlr_signal_emit_safe(&surface->toplevel->events.request_maximize, surface);
} }
static void xdg_toplevel_handle_unset_maximized(struct wl_client *client, static void xdg_toplevel_handle_unset_maximized(struct wl_client *client,
struct wl_resource *resource) { struct wl_resource *resource) {
struct wlr_xdg_surface_v6 *surface = struct wlr_xdg_surface_v6 *surface =
xdg_surface_from_xdg_toplevel_resource(resource); xdg_surface_from_xdg_toplevel_resource(resource);
surface->toplevel->next.maximized = false; surface->toplevel->client_pending.maximized = false;
wlr_signal_emit_safe(&surface->events.request_maximize, surface); wlr_signal_emit_safe(&surface->toplevel->events.request_maximize, surface);
} }
static void xdg_toplevel_handle_set_fullscreen(struct wl_client *client, static void xdg_toplevel_handle_set_fullscreen(struct wl_client *client,
@ -793,7 +813,7 @@ static void xdg_toplevel_handle_set_fullscreen(struct wl_client *client,
output = wlr_output_from_resource(output_resource); output = wlr_output_from_resource(output_resource);
} }
surface->toplevel->next.fullscreen = true; surface->toplevel->client_pending.fullscreen = true;
struct wlr_xdg_toplevel_v6_set_fullscreen_event event = { struct wlr_xdg_toplevel_v6_set_fullscreen_event event = {
.surface = surface, .surface = surface,
@ -801,7 +821,7 @@ static void xdg_toplevel_handle_set_fullscreen(struct wl_client *client,
.output = output, .output = output,
}; };
wlr_signal_emit_safe(&surface->events.request_fullscreen, &event); wlr_signal_emit_safe(&surface->toplevel->events.request_fullscreen, &event);
} }
static void xdg_toplevel_handle_unset_fullscreen(struct wl_client *client, static void xdg_toplevel_handle_unset_fullscreen(struct wl_client *client,
@ -809,7 +829,7 @@ static void xdg_toplevel_handle_unset_fullscreen(struct wl_client *client,
struct wlr_xdg_surface_v6 *surface = struct wlr_xdg_surface_v6 *surface =
xdg_surface_from_xdg_toplevel_resource(resource); xdg_surface_from_xdg_toplevel_resource(resource);
surface->toplevel->next.fullscreen = false; surface->toplevel->client_pending.fullscreen = false;
struct wlr_xdg_toplevel_v6_set_fullscreen_event event = { struct wlr_xdg_toplevel_v6_set_fullscreen_event event = {
.surface = surface, .surface = surface,
@ -817,14 +837,14 @@ static void xdg_toplevel_handle_unset_fullscreen(struct wl_client *client,
.output = NULL, .output = NULL,
}; };
wlr_signal_emit_safe(&surface->events.request_fullscreen, &event); wlr_signal_emit_safe(&surface->toplevel->events.request_fullscreen, &event);
} }
static void xdg_toplevel_handle_set_minimized(struct wl_client *client, static void xdg_toplevel_handle_set_minimized(struct wl_client *client,
struct wl_resource *resource) { struct wl_resource *resource) {
struct wlr_xdg_surface_v6 *surface = struct wlr_xdg_surface_v6 *surface =
xdg_surface_from_xdg_toplevel_resource(resource); xdg_surface_from_xdg_toplevel_resource(resource);
wlr_signal_emit_safe(&surface->events.request_minimize, surface); wlr_signal_emit_safe(&surface->toplevel->events.request_minimize, surface);
} }
static const struct zxdg_toplevel_v6_interface static const struct zxdg_toplevel_v6_interface
@ -856,7 +876,7 @@ static void xdg_toplevel_resource_destroy(struct wl_resource *resource) {
struct wlr_xdg_surface_v6 *surface = struct wlr_xdg_surface_v6 *surface =
xdg_surface_from_xdg_toplevel_resource(resource); xdg_surface_from_xdg_toplevel_resource(resource);
if (surface != NULL) { if (surface != NULL) {
xdg_surface_unmap(surface); xdg_toplevel_destroy(surface);
} }
} }
@ -874,6 +894,12 @@ static void xdg_surface_handle_get_toplevel(struct wl_client *client,
wl_resource_post_no_memory(resource); wl_resource_post_no_memory(resource);
return; return;
} }
wl_signal_init(&surface->toplevel->events.request_maximize);
wl_signal_init(&surface->toplevel->events.request_fullscreen);
wl_signal_init(&surface->toplevel->events.request_minimize);
wl_signal_init(&surface->toplevel->events.request_move);
wl_signal_init(&surface->toplevel->events.request_resize);
wl_signal_init(&surface->toplevel->events.request_show_window_menu);
surface->role = WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL; surface->role = WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL;
surface->toplevel->base = surface; surface->toplevel->base = surface;
@ -1021,25 +1047,25 @@ static bool wlr_xdg_surface_v6_toplevel_state_compare(
configured.height = configure->toplevel_state->height; configured.height = configure->toplevel_state->height;
} }
if (state->pending.activated != configured.state.activated) { if (state->server_pending.activated != configured.state.activated) {
return false; return false;
} }
if (state->pending.fullscreen != configured.state.fullscreen) { if (state->server_pending.fullscreen != configured.state.fullscreen) {
return false; return false;
} }
if (state->pending.maximized != configured.state.maximized) { if (state->server_pending.maximized != configured.state.maximized) {
return false; return false;
} }
if (state->pending.resizing != configured.state.resizing) { if (state->server_pending.resizing != configured.state.resizing) {
return false; return false;
} }
if (state->pending.width == configured.width && if (state->server_pending.width == configured.width &&
state->pending.height == configured.height) { state->server_pending.height == configured.height) {
return true; return true;
} }
if (state->pending.width == 0 && state->pending.height == 0) { if (state->server_pending.width == 0 && state->server_pending.height == 0) {
return true; return true;
} }
@ -1057,46 +1083,50 @@ static void wlr_xdg_toplevel_v6_send_configure(
wl_resource_post_no_memory(surface->toplevel->resource); wl_resource_post_no_memory(surface->toplevel->resource);
return; return;
} }
*configure->toplevel_state = surface->toplevel->pending; *configure->toplevel_state = surface->toplevel->server_pending;
uint32_t *s; uint32_t *s;
struct wl_array states; struct wl_array states;
wl_array_init(&states); wl_array_init(&states);
if (surface->toplevel->pending.maximized) { if (surface->toplevel->server_pending.maximized) {
s = wl_array_add(&states, sizeof(uint32_t)); s = wl_array_add(&states, sizeof(uint32_t));
if (!s) { if (!s) {
wlr_log(L_ERROR, "Could not allocate state for maximized xdg_toplevel"); wlr_log(L_ERROR,
"Could not allocate state for maximized xdg_toplevel");
goto error_out; goto error_out;
} }
*s = ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED; *s = ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED;
} }
if (surface->toplevel->pending.fullscreen) { if (surface->toplevel->server_pending.fullscreen) {
s = wl_array_add(&states, sizeof(uint32_t)); s = wl_array_add(&states, sizeof(uint32_t));
if (!s) { if (!s) {
wlr_log(L_ERROR, "Could not allocate state for fullscreen xdg_toplevel"); wlr_log(L_ERROR,
"Could not allocate state for fullscreen xdg_toplevel");
goto error_out; goto error_out;
} }
*s = ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN; *s = ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN;
} }
if (surface->toplevel->pending.resizing) { if (surface->toplevel->server_pending.resizing) {
s = wl_array_add(&states, sizeof(uint32_t)); s = wl_array_add(&states, sizeof(uint32_t));
if (!s) { if (!s) {
wlr_log(L_ERROR, "Could not allocate state for resizing xdg_toplevel"); wlr_log(L_ERROR,
"Could not allocate state for resizing xdg_toplevel");
goto error_out; goto error_out;
} }
*s = ZXDG_TOPLEVEL_V6_STATE_RESIZING; *s = ZXDG_TOPLEVEL_V6_STATE_RESIZING;
} }
if (surface->toplevel->pending.activated) { if (surface->toplevel->server_pending.activated) {
s = wl_array_add(&states, sizeof(uint32_t)); s = wl_array_add(&states, sizeof(uint32_t));
if (!s) { if (!s) {
wlr_log(L_ERROR, "Could not allocate state for activated xdg_toplevel"); wlr_log(L_ERROR,
"Could not allocate state for activated xdg_toplevel");
goto error_out; goto error_out;
} }
*s = ZXDG_TOPLEVEL_V6_STATE_ACTIVATED; *s = ZXDG_TOPLEVEL_V6_STATE_ACTIVATED;
} }
uint32_t width = surface->toplevel->pending.width; uint32_t width = surface->toplevel->server_pending.width;
uint32_t height = surface->toplevel->pending.height; uint32_t height = surface->toplevel->server_pending.height;
zxdg_toplevel_v6_send_configure(surface->toplevel->resource, width, zxdg_toplevel_v6_send_configure(surface->toplevel->resource, width,
height, &states); height, &states);
@ -1204,13 +1234,13 @@ static void wlr_xdg_surface_v6_toplevel_committed(
// update state that doesn't need compositor approval // update state that doesn't need compositor approval
surface->toplevel->current.max_width = surface->toplevel->current.max_width =
surface->toplevel->next.max_width; surface->toplevel->client_pending.max_width;
surface->toplevel->current.min_width = surface->toplevel->current.min_width =
surface->toplevel->next.min_width; surface->toplevel->client_pending.min_width;
surface->toplevel->current.max_height = surface->toplevel->current.max_height =
surface->toplevel->next.max_height; surface->toplevel->client_pending.max_height;
surface->toplevel->current.min_height = surface->toplevel->current.min_height =
surface->toplevel->next.min_height; surface->toplevel->client_pending.min_height;
} }
static void wlr_xdg_surface_v6_popup_committed( static void wlr_xdg_surface_v6_popup_committed(
@ -1318,12 +1348,6 @@ static void xdg_shell_handle_get_xdg_surface(struct wl_client *wl_client,
wl_list_init(&surface->configure_list); wl_list_init(&surface->configure_list);
wl_list_init(&surface->popups); wl_list_init(&surface->popups);
wl_signal_init(&surface->events.request_maximize);
wl_signal_init(&surface->events.request_fullscreen);
wl_signal_init(&surface->events.request_minimize);
wl_signal_init(&surface->events.request_move);
wl_signal_init(&surface->events.request_resize);
wl_signal_init(&surface->events.request_show_window_menu);
wl_signal_init(&surface->events.destroy); wl_signal_init(&surface->events.destroy);
wl_signal_init(&surface->events.ping_timeout); wl_signal_init(&surface->events.ping_timeout);
wl_signal_init(&surface->events.new_popup); wl_signal_init(&surface->events.new_popup);
@ -1501,8 +1525,8 @@ void wlr_xdg_surface_v6_ping(struct wlr_xdg_surface_v6 *surface) {
uint32_t wlr_xdg_toplevel_v6_set_size(struct wlr_xdg_surface_v6 *surface, uint32_t wlr_xdg_toplevel_v6_set_size(struct wlr_xdg_surface_v6 *surface,
uint32_t width, uint32_t height) { uint32_t width, uint32_t height) {
assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL); assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL);
surface->toplevel->pending.width = width; surface->toplevel->server_pending.width = width;
surface->toplevel->pending.height = height; surface->toplevel->server_pending.height = height;
return wlr_xdg_surface_v6_schedule_configure(surface); return wlr_xdg_surface_v6_schedule_configure(surface);
} }
@ -1510,7 +1534,7 @@ uint32_t wlr_xdg_toplevel_v6_set_size(struct wlr_xdg_surface_v6 *surface,
uint32_t wlr_xdg_toplevel_v6_set_activated(struct wlr_xdg_surface_v6 *surface, uint32_t wlr_xdg_toplevel_v6_set_activated(struct wlr_xdg_surface_v6 *surface,
bool activated) { bool activated) {
assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL); assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL);
surface->toplevel->pending.activated = activated; surface->toplevel->server_pending.activated = activated;
return wlr_xdg_surface_v6_schedule_configure(surface); return wlr_xdg_surface_v6_schedule_configure(surface);
} }
@ -1518,7 +1542,7 @@ uint32_t wlr_xdg_toplevel_v6_set_activated(struct wlr_xdg_surface_v6 *surface,
uint32_t wlr_xdg_toplevel_v6_set_maximized(struct wlr_xdg_surface_v6 *surface, uint32_t wlr_xdg_toplevel_v6_set_maximized(struct wlr_xdg_surface_v6 *surface,
bool maximized) { bool maximized) {
assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL); assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL);
surface->toplevel->pending.maximized = maximized; surface->toplevel->server_pending.maximized = maximized;
return wlr_xdg_surface_v6_schedule_configure(surface); return wlr_xdg_surface_v6_schedule_configure(surface);
} }
@ -1526,7 +1550,7 @@ uint32_t wlr_xdg_toplevel_v6_set_maximized(struct wlr_xdg_surface_v6 *surface,
uint32_t wlr_xdg_toplevel_v6_set_fullscreen(struct wlr_xdg_surface_v6 *surface, uint32_t wlr_xdg_toplevel_v6_set_fullscreen(struct wlr_xdg_surface_v6 *surface,
bool fullscreen) { bool fullscreen) {
assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL); assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL);
surface->toplevel->pending.fullscreen = fullscreen; surface->toplevel->server_pending.fullscreen = fullscreen;
return wlr_xdg_surface_v6_schedule_configure(surface); return wlr_xdg_surface_v6_schedule_configure(surface);
} }
@ -1534,7 +1558,7 @@ uint32_t wlr_xdg_toplevel_v6_set_fullscreen(struct wlr_xdg_surface_v6 *surface,
uint32_t wlr_xdg_toplevel_v6_set_resizing(struct wlr_xdg_surface_v6 *surface, uint32_t wlr_xdg_toplevel_v6_set_resizing(struct wlr_xdg_surface_v6 *surface,
bool resizing) { bool resizing) {
assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL); assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL);
surface->toplevel->pending.resizing = resizing; surface->toplevel->server_pending.resizing = resizing;
return wlr_xdg_surface_v6_schedule_configure(surface); return wlr_xdg_surface_v6_schedule_configure(surface);
} }
@ -1609,3 +1633,264 @@ struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6_popup_at(
return NULL; return NULL;
} }
void wlr_xdg_popup_v6_get_anchor_point(struct wlr_xdg_popup_v6 *popup,
int *root_sx, int *root_sy) {
struct wlr_box rect = popup->positioner.anchor_rect;
enum zxdg_positioner_v6_anchor anchor = popup->positioner.anchor;
int sx = 0, sy = 0;
if (anchor == ZXDG_POSITIONER_V6_ANCHOR_NONE) {
sx = (rect.x + rect.width) / 2;
sy = (rect.y + rect.height) / 2;
} else if (anchor == ZXDG_POSITIONER_V6_ANCHOR_TOP) {
sx = (rect.x + rect.width) / 2;
sy = rect.y;
} else if (anchor == ZXDG_POSITIONER_V6_ANCHOR_BOTTOM) {
sx = (rect.x + rect.width) / 2;
sy = rect.y + rect.height;
} else if (anchor == ZXDG_POSITIONER_V6_ANCHOR_LEFT) {
sx = rect.x;
sy = (rect.y + rect.height) / 2;
} else if (anchor == ZXDG_POSITIONER_V6_ANCHOR_RIGHT) {
sx = rect.x + rect.width;
sy = (rect.y + rect.height) / 2;
} else if (anchor == (ZXDG_POSITIONER_V6_ANCHOR_TOP |
ZXDG_POSITIONER_V6_ANCHOR_LEFT)) {
sx = rect.x;
sy = rect.y;
} else if (anchor == (ZXDG_POSITIONER_V6_ANCHOR_TOP |
ZXDG_POSITIONER_V6_ANCHOR_RIGHT)) {
sx = rect.x + rect.width;
sy = rect.y;
} else if (anchor == (ZXDG_POSITIONER_V6_ANCHOR_BOTTOM |
ZXDG_POSITIONER_V6_ANCHOR_LEFT)) {
sx = rect.x;
sy = rect.y + rect.height;
} else if (anchor == (ZXDG_POSITIONER_V6_ANCHOR_BOTTOM |
ZXDG_POSITIONER_V6_ANCHOR_RIGHT)) {
sx = rect.x + rect.width;
sy = rect.y + rect.height;
}
*root_sx = sx;
*root_sy = sy;
}
void wlr_xdg_popup_v6_get_toplevel_coords(struct wlr_xdg_popup_v6 *popup,
int popup_sx, int popup_sy, int *toplevel_sx, int *toplevel_sy) {
struct wlr_xdg_surface_v6 *parent = popup->parent;
while (parent != NULL && parent->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) {
popup_sx += parent->popup->geometry.x;
popup_sy += parent->popup->geometry.y;
parent = parent->popup->parent;
}
assert(parent);
*toplevel_sx = popup_sx + parent->geometry.x;
*toplevel_sy = popup_sy + parent->geometry.y;
}
static void wlr_xdg_popup_v6_box_constraints(struct wlr_xdg_popup_v6 *popup,
struct wlr_box *toplevel_sx_box, int *offset_x, int *offset_y) {
int popup_width = popup->geometry.width;
int popup_height = popup->geometry.height;
int anchor_sx = 0, anchor_sy = 0;
wlr_xdg_popup_v6_get_anchor_point(popup, &anchor_sx, &anchor_sy);
int popup_sx = 0, popup_sy = 0;
wlr_xdg_popup_v6_get_toplevel_coords(popup, popup->geometry.x,
popup->geometry.y, &popup_sx, &popup_sy);
*offset_x = 0, *offset_y = 0;
if (popup_sx < toplevel_sx_box->x) {
*offset_x = toplevel_sx_box->x - popup_sx;
} else if (popup_sx + popup_width >
toplevel_sx_box->x + toplevel_sx_box->width) {
*offset_x = toplevel_sx_box->x + toplevel_sx_box->width -
(popup_sx + popup_width);
}
if (popup_sy < toplevel_sx_box->y) {
*offset_y = toplevel_sx_box->y - popup_sy;
} else if (popup_sy + popup_height >
toplevel_sx_box->y + toplevel_sx_box->height) {
*offset_y = toplevel_sx_box->y + toplevel_sx_box->height -
(popup_sy + popup_height);
}
}
static bool wlr_xdg_popup_v6_unconstrain_flip(struct wlr_xdg_popup_v6 *popup,
struct wlr_box *toplevel_sx_box) {
int offset_x = 0, offset_y = 0;
wlr_xdg_popup_v6_box_constraints(popup, toplevel_sx_box,
&offset_x, &offset_y);
if (!offset_x && !offset_y) {
return true;
}
bool flip_x = offset_x &&
(popup->positioner.constraint_adjustment &
ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_X);
bool flip_y = offset_y &&
(popup->positioner.constraint_adjustment &
ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_Y);
if (flip_x) {
wlr_positioner_v6_invert_x(&popup->positioner);
}
if (flip_y) {
wlr_positioner_v6_invert_y(&popup->positioner);
}
popup->geometry =
wlr_xdg_positioner_v6_get_geometry(&popup->positioner);
wlr_xdg_popup_v6_box_constraints(popup, toplevel_sx_box,
&offset_x, &offset_y);
if (!offset_x && !offset_y) {
// no longer constrained
return true;
}
// revert the positioner back if it didn't fix it and go to the next part
if (flip_x) {
wlr_positioner_v6_invert_x(&popup->positioner);
}
if (flip_y) {
wlr_positioner_v6_invert_y(&popup->positioner);
}
popup->geometry =
wlr_xdg_positioner_v6_get_geometry(&popup->positioner);
return false;
}
static bool wlr_xdg_popup_v6_unconstrain_slide(struct wlr_xdg_popup_v6 *popup,
struct wlr_box *toplevel_sx_box) {
int offset_x = 0, offset_y = 0;
wlr_xdg_popup_v6_box_constraints(popup, toplevel_sx_box,
&offset_x, &offset_y);
if (!offset_x && !offset_y) {
return true;
}
bool slide_x = offset_x &&
(popup->positioner.constraint_adjustment &
ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_X);
bool slide_y = offset_x &&
(popup->positioner.constraint_adjustment &
ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_Y);
if (slide_x) {
popup->geometry.x += offset_x;
}
if (slide_y) {
popup->geometry.y += offset_y;
}
int toplevel_x = 0, toplevel_y = 0;
wlr_xdg_popup_v6_get_toplevel_coords(popup, popup->geometry.x,
popup->geometry.y, &toplevel_x, &toplevel_y);
if (slide_x && toplevel_x < toplevel_sx_box->x) {
popup->geometry.x += toplevel_sx_box->x - toplevel_x;
}
if (slide_y && toplevel_y < toplevel_sx_box->y) {
popup->geometry.y += toplevel_sx_box->y - toplevel_y;
}
wlr_xdg_popup_v6_box_constraints(popup, toplevel_sx_box,
&offset_x, &offset_y);
return !offset_x && !offset_y;
}
static bool wlr_xdg_popup_v6_unconstrain_resize(struct wlr_xdg_popup_v6 *popup,
struct wlr_box *toplevel_sx_box) {
int offset_x, offset_y;
wlr_xdg_popup_v6_box_constraints(popup, toplevel_sx_box,
&offset_x, &offset_y);
if (!offset_x && !offset_y) {
return true;
}
bool resize_x = offset_x &&
(popup->positioner.constraint_adjustment &
ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_X);
bool resize_y = offset_x &&
(popup->positioner.constraint_adjustment &
ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y);
if (resize_x) {
popup->geometry.width -= offset_x;
}
if (resize_y) {
popup->geometry.height -= offset_y;
}
wlr_xdg_popup_v6_box_constraints(popup, toplevel_sx_box,
&offset_y, &offset_y);
return !offset_x && !offset_y;
}
void wlr_xdg_popup_v6_unconstrain_from_box(struct wlr_xdg_popup_v6 *popup,
struct wlr_box *toplevel_sx_box) {
if (wlr_xdg_popup_v6_unconstrain_flip(popup, toplevel_sx_box)) {
return;
}
if (wlr_xdg_popup_v6_unconstrain_slide(popup, toplevel_sx_box)) {
return;
}
if (wlr_xdg_popup_v6_unconstrain_resize(popup, toplevel_sx_box)) {
return;
}
}
void wlr_positioner_v6_invert_x(struct wlr_xdg_positioner_v6 *positioner) {
if (positioner->anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT) {
positioner->anchor &= ~ZXDG_POSITIONER_V6_ANCHOR_LEFT;
positioner->anchor |= ZXDG_POSITIONER_V6_ANCHOR_RIGHT;
} else if (positioner->anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT) {
positioner->anchor &= ~ZXDG_POSITIONER_V6_ANCHOR_RIGHT;
positioner->anchor |= ZXDG_POSITIONER_V6_ANCHOR_LEFT;
}
if (positioner->gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT) {
positioner->gravity &= ~ZXDG_POSITIONER_V6_GRAVITY_RIGHT;
positioner->gravity |= ZXDG_POSITIONER_V6_GRAVITY_LEFT;
} else if (positioner->gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT) {
positioner->gravity &= ~ZXDG_POSITIONER_V6_GRAVITY_LEFT;
positioner->gravity |= ZXDG_POSITIONER_V6_GRAVITY_RIGHT;
}
}
void wlr_positioner_v6_invert_y(
struct wlr_xdg_positioner_v6 *positioner) {
if (positioner->anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP) {
positioner->anchor &= ~ZXDG_POSITIONER_V6_ANCHOR_TOP;
positioner->anchor |= ZXDG_POSITIONER_V6_ANCHOR_BOTTOM;
} else if (positioner->anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM) {
positioner->anchor &= ~ZXDG_POSITIONER_V6_ANCHOR_BOTTOM;
positioner->anchor |= ZXDG_POSITIONER_V6_ANCHOR_TOP;
}
if (positioner->gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP) {
positioner->gravity &= ~ZXDG_POSITIONER_V6_GRAVITY_TOP;
positioner->gravity |= ZXDG_POSITIONER_V6_GRAVITY_BOTTOM;
} else if (positioner->gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM) {
positioner->gravity &= ~ZXDG_POSITIONER_V6_GRAVITY_BOTTOM;
positioner->gravity |= ZXDG_POSITIONER_V6_GRAVITY_TOP;
}
}

@ -120,8 +120,8 @@ static struct wlr_xwayland_surface *wlr_xwayland_surface_create(
wl_signal_init(&surface->events.request_resize); wl_signal_init(&surface->events.request_resize);
wl_signal_init(&surface->events.request_maximize); wl_signal_init(&surface->events.request_maximize);
wl_signal_init(&surface->events.request_fullscreen); wl_signal_init(&surface->events.request_fullscreen);
wl_signal_init(&surface->events.map_notify); wl_signal_init(&surface->events.map);
wl_signal_init(&surface->events.unmap_notify); wl_signal_init(&surface->events.unmap);
wl_signal_init(&surface->events.set_class); wl_signal_init(&surface->events.set_class);
wl_signal_init(&surface->events.set_title); wl_signal_init(&surface->events.set_title);
wl_signal_init(&surface->events.set_parent); wl_signal_init(&surface->events.set_parent);
@ -617,7 +617,7 @@ static void xwm_map_shell_surface(struct wlr_xwm *xwm,
wl_signal_add(&surface->events.destroy, &xsurface->surface_destroy); wl_signal_add(&surface->events.destroy, &xsurface->surface_destroy);
xsurface->mapped = true; xsurface->mapped = true;
wlr_signal_emit_safe(&xsurface->events.map_notify, xsurface); wlr_signal_emit_safe(&xsurface->events.map, xsurface);
} }
static void xwm_handle_create_notify(struct wlr_xwm *xwm, static void xwm_handle_create_notify(struct wlr_xwm *xwm,
@ -749,7 +749,7 @@ static void xwm_handle_unmap_notify(struct wlr_xwm *xwm,
if (xsurface->mapped) { if (xsurface->mapped) {
xsurface->mapped = false; xsurface->mapped = false;
wlr_signal_emit_safe(&xsurface->events.unmap_notify, xsurface); wlr_signal_emit_safe(&xsurface->events.unmap, xsurface);
} }
xsurface_set_wm_state(xsurface, ICCCM_WITHDRAWN_STATE); xsurface_set_wm_state(xsurface, ICCCM_WITHDRAWN_STATE);
@ -1417,7 +1417,6 @@ void xwm_set_cursor(struct wlr_xwm *xwm, const uint8_t *pixels, uint32_t stride,
xcb_free_cursor(xwm->xcb_conn, xwm->cursor); xcb_free_cursor(xwm->xcb_conn, xwm->cursor);
} }
stride *= 4;
int depth = 32; int depth = 32;
xcb_pixmap_t pix = xcb_generate_id(xwm->xcb_conn); xcb_pixmap_t pix = xcb_generate_id(xwm->xcb_conn);

Loading…
Cancel
Save