aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--backend/drm/drm.c43
-rw-r--r--backend/drm/renderer.c3
-rw-r--r--backend/libinput/keyboard.c3
-rw-r--r--backend/meson.build2
-rw-r--r--backend/wayland/output.c11
-rw-r--r--backend/wayland/wl_seat.c9
-rw-r--r--backend/x11/backend.c44
-rw-r--r--examples/meson.build6
-rw-r--r--examples/pointer.c3
-rw-r--r--examples/screenshot.c288
-rwxr-xr-xglgen.sh102
-rw-r--r--include/backend/wayland.h3
-rw-r--r--include/rootston/desktop.h2
-rw-r--r--include/rootston/input.h7
-rw-r--r--include/rootston/view.h15
-rw-r--r--include/wlr/egl.h9
-rw-r--r--include/wlr/interfaces/wlr_keyboard.h5
-rw-r--r--include/wlr/interfaces/wlr_output.h3
-rw-r--r--include/wlr/render.h15
-rw-r--r--include/wlr/render/interface.h2
-rw-r--r--include/wlr/types/wlr_keyboard.h2
-rw-r--r--include/wlr/types/wlr_output.h5
-rw-r--r--include/wlr/types/wlr_screenshooter.h26
-rw-r--r--include/wlr/types/wlr_seat.h155
-rw-r--r--include/wlr/types/wlr_surface.h2
-rw-r--r--include/wlr/types/wlr_xdg_shell_v6.h38
-rw-r--r--meson.build6
-rw-r--r--protocol/meson.build7
-rw-r--r--protocol/screenshooter.xml16
-rw-r--r--render/egl.c61
-rw-r--r--render/glapi.txt8
-rw-r--r--render/gles2/renderer.c39
-rw-r--r--render/meson.build20
-rw-r--r--render/wlr_renderer.c5
-rw-r--r--rootston/cursor.c40
-rw-r--r--rootston/desktop.c153
-rw-r--r--rootston/keyboard.c15
-rw-r--r--rootston/output.c27
-rw-r--r--rootston/rootston.ini.example5
-rw-r--r--rootston/wl_shell.c29
-rw-r--r--rootston/xdg_shell_v6.c39
-rw-r--r--rootston/xwayland.c21
-rw-r--r--types/meson.build1
-rw-r--r--types/wlr_cursor.c14
-rw-r--r--types/wlr_data_device_manager.c83
-rw-r--r--types/wlr_data_source.c20
-rw-r--r--types/wlr_keyboard.c18
-rw-r--r--types/wlr_output.c23
-rw-r--r--types/wlr_screenshooter.c163
-rw-r--r--types/wlr_seat.c192
-rw-r--r--types/wlr_surface.c31
-rw-r--r--types/wlr_wl_shell.c2
-rw-r--r--types/wlr_xdg_shell_v6.c600
-rw-r--r--xwayland/xwm.c33
54 files changed, 2148 insertions, 326 deletions
diff --git a/backend/drm/drm.c b/backend/drm/drm.c
index fc376b54..9e5346a1 100644
--- a/backend/drm/drm.c
+++ b/backend/drm/drm.c
@@ -469,7 +469,8 @@ static void wlr_drm_connector_transform(struct wlr_output *output,
}
static bool wlr_drm_connector_set_cursor(struct wlr_output *output,
- const uint8_t *buf, int32_t stride, uint32_t width, uint32_t height) {
+ const uint8_t *buf, int32_t stride, uint32_t width, uint32_t height,
+ int32_t hotspot_x, int32_t hotspot_y) {
struct wlr_drm_connector *conn = (struct wlr_drm_connector *)output;
struct wlr_drm_backend *drm = conn->drm;
struct wlr_drm_renderer *renderer = &drm->renderer;
@@ -534,6 +535,37 @@ static bool wlr_drm_connector_set_cursor(struct wlr_output *output,
}
}
+ switch (output->transform) {
+ case WL_OUTPUT_TRANSFORM_90:
+ output->cursor.hotspot_x = hotspot_x;
+ output->cursor.hotspot_y = -plane->surf.height + hotspot_y;
+ break;
+ case WL_OUTPUT_TRANSFORM_180:
+ output->cursor.hotspot_x = plane->surf.width - hotspot_x;
+ output->cursor.hotspot_y = plane->surf.height - hotspot_y;
+ break;
+ case WL_OUTPUT_TRANSFORM_270:
+ output->cursor.hotspot_x = -plane->surf.height + hotspot_x;
+ output->cursor.hotspot_y = hotspot_y;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ output->cursor.hotspot_x = plane->surf.width - hotspot_x;
+ output->cursor.hotspot_y = hotspot_y;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ output->cursor.hotspot_x = hotspot_x;
+ output->cursor.hotspot_y = -hotspot_y;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ output->cursor.hotspot_x = hotspot_x;
+ output->cursor.hotspot_y = plane->surf.height - hotspot_y;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ output->cursor.hotspot_x = -plane->surf.height + hotspot_x;
+ output->cursor.hotspot_y = plane->surf.width - hotspot_y;
+ break;
+ }
+
struct gbm_bo *bo = plane->cursor_bo;
uint32_t bo_width = gbm_bo_get_width(bo);
uint32_t bo_height = gbm_bo_get_height(bo);
@@ -581,23 +613,22 @@ static bool wlr_drm_connector_move_cursor(struct wlr_output *output,
switch (output->transform) {
case WL_OUTPUT_TRANSFORM_NORMAL:
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
// nothing to do
break;
case WL_OUTPUT_TRANSFORM_270:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
tmp = x;
x = y;
y = -(tmp - width);
break;
case WL_OUTPUT_TRANSFORM_90:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
tmp = x;
x = -(y - height);
y = tmp;
break;
- default:
- // TODO other transformations
- wlr_log(L_ERROR, "TODO: handle surface to crtc for transformation = %d",
- output->transform);
- break;
}
return drm->iface->crtc_move_cursor(drm, conn->crtc, x, y);
diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c
index c5840436..0d338490 100644
--- a/backend/drm/renderer.c
+++ b/backend/drm/renderer.c
@@ -14,6 +14,7 @@
#include <wlr/render/gles2.h>
#include <wlr/render.h>
#include "backend/drm/drm.h"
+#include "render/glapi.h"
bool wlr_drm_renderer_init(struct wlr_drm_backend *drm,
struct wlr_drm_renderer *renderer) {
@@ -191,7 +192,7 @@ static struct wlr_texture *get_tex_for_bo(struct wlr_drm_renderer *renderer, str
EGL_NONE,
};
- tex->img = renderer->egl.eglCreateImageKHR(renderer->egl.display, EGL_NO_CONTEXT,
+ tex->img = eglCreateImageKHR(renderer->egl.display, EGL_NO_CONTEXT,
EGL_LINUX_DMA_BUF_EXT, NULL, attribs);
if (!tex->img) {
wlr_log(L_ERROR, "Failed to create EGL image: %s", egl_error());
diff --git a/backend/libinput/keyboard.c b/backend/libinput/keyboard.c
index 53c3a61b..00a7ecdf 100644
--- a/backend/libinput/keyboard.c
+++ b/backend/libinput/keyboard.c
@@ -67,5 +67,6 @@ void handle_keyboard_key(struct libinput_event *event,
wlr_event.state = WLR_KEY_PRESSED;
break;
}
- wlr_keyboard_update_state(wlr_dev->keyboard, &wlr_event);
+ wlr_event.update_state = true;
+ wlr_keyboard_notify_key(wlr_dev->keyboard, &wlr_event);
}
diff --git a/backend/meson.build b/backend/meson.build
index c5c73288..cf62a56f 100644
--- a/backend/meson.build
+++ b/backend/meson.build
@@ -38,5 +38,5 @@ lib_wlr_backend = static_library(
'wlr_backend',
backend_files,
include_directories: wlr_inc,
- dependencies: [wayland_server, egl, gbm, libinput, systemd, elogind, wlr_protos],
+ dependencies: [wayland_server, egl, gbm, libinput, systemd, elogind, wlr_render, wlr_protos],
)
diff --git a/backend/wayland/output.c b/backend/wayland/output.c
index ba04aede..062a91a1 100644
--- a/backend/wayland/output.c
+++ b/backend/wayland/output.c
@@ -53,7 +53,8 @@ static void wlr_wl_output_transform(struct wlr_output *_output,
}
static bool wlr_wl_output_set_cursor(struct wlr_output *_output,
- const uint8_t *buf, int32_t stride, uint32_t width, uint32_t height) {
+ const uint8_t *buf, int32_t stride, uint32_t width, uint32_t height,
+ int32_t hotspot_x, int32_t hotspot_y) {
struct wlr_wl_backend_output *output = (struct wlr_wl_backend_output *)_output;
struct wlr_wl_backend *backend = output->backend;
@@ -110,7 +111,8 @@ static bool wlr_wl_output_set_cursor(struct wlr_output *_output,
wl_surface_damage(output->cursor_surface, 0, 0, width, height);
wl_surface_commit(output->cursor_surface);
- wlr_wl_output_update_cursor(output, output->enter_serial);
+ wlr_wl_output_update_cursor(output, output->enter_serial,
+ hotspot_x, hotspot_y);
return true;
}
@@ -143,10 +145,11 @@ static void wlr_wl_output_destroy(struct wlr_output *_output) {
free(output);
}
-void wlr_wl_output_update_cursor(struct wlr_wl_backend_output *output, uint32_t serial) {
+void wlr_wl_output_update_cursor(struct wlr_wl_backend_output *output,
+ uint32_t serial, int32_t hotspot_x, int32_t hotspot_y) {
if (output->cursor_surface && output->backend->pointer && serial) {
wl_pointer_set_cursor(output->backend->pointer, serial,
- output->cursor_surface, 0, 0);
+ output->cursor_surface, hotspot_x, hotspot_y);
}
}
diff --git a/backend/wayland/wl_seat.c b/backend/wayland/wl_seat.c
index ba3feb8d..d3ff9b64 100644
--- a/backend/wayland/wl_seat.c
+++ b/backend/wayland/wl_seat.c
@@ -24,7 +24,7 @@ static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer,
assert(output);
wlr_wl_pointer->current_output = output;
wlr_wl_pointer->current_output->enter_serial = serial;
- wlr_wl_output_update_cursor(wlr_wl_pointer->current_output, serial);
+ wlr_wl_output_update_cursor(wlr_wl_pointer->current_output, serial, 0, 0);
}
static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer,
@@ -149,13 +149,16 @@ static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard,
wlr_event.state = state;
wlr_event.time_sec = time / 1000;
wlr_event.time_usec = time * 1000;
- wlr_keyboard_update_state(dev->keyboard, &wlr_event);
+ wlr_keyboard_notify_key(dev->keyboard, &wlr_event);
}
static void keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched,
uint32_t mods_locked, uint32_t group) {
-
+ struct wlr_input_device *dev = data;
+ assert(dev && dev->keyboard);
+ wlr_keyboard_notify_modifiers(dev->keyboard, mods_depressed, mods_latched,
+ mods_locked, group);
}
static void keyboard_handle_repeat_info(void *data, struct wl_keyboard *wl_keyboard,
diff --git a/backend/x11/backend.c b/backend/x11/backend.c
index 88c022d1..1eb5a952 100644
--- a/backend/x11/backend.c
+++ b/backend/x11/backend.c
@@ -47,29 +47,46 @@ static bool handle_x11_event(struct wlr_x11_backend *x11, xcb_generic_event_t *e
xcb_key_press_event_t *ev = (xcb_key_press_event_t *)event;
struct wlr_event_keyboard_key key = {
.time_sec = ev->time / 1000,
- .time_usec = (ev->time % 1000) * 1000,
+ .time_usec = ev->time * 1000,
.keycode = ev->detail - 8,
.state = event->response_type == XCB_KEY_PRESS ?
WLR_KEY_PRESSED : WLR_KEY_RELEASED,
+ .update_state = true,
};
- wl_signal_emit(&x11->keyboard.events.key, &key);
+ // TODO use xcb-xkb for more precise modifiers state?
+ wlr_keyboard_notify_key(&x11->keyboard, &key);
x11->time = ev->time;
break;
}
case XCB_BUTTON_PRESS:
case XCB_BUTTON_RELEASE: {
xcb_button_press_event_t *ev = (xcb_button_press_event_t *)event;
- struct wlr_event_pointer_button button = {
- .device = &x11->pointer_dev,
- .time_sec = ev->time / 1000,
- .time_usec = (ev->time % 1000) * 1000,
- .button = xcb_button_to_wl(ev->detail),
- .state = event->response_type == XCB_BUTTON_PRESS ?
- WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED,
- };
- wl_signal_emit(&x11->pointer.events.button, &button);
+ 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_sec = ev->time / 1000,
+ .time_usec = ev->time * 1000,
+ .source = WLR_AXIS_SOURCE_WHEEL,
+ .orientation = WLR_AXIS_ORIENTATION_VERTICAL,
+ .delta = delta,
+ };
+ wl_signal_emit(&x11->pointer.events.axis, &axis);
+ } else {
+ struct wlr_event_pointer_button button = {
+ .device = &x11->pointer_dev,
+ .time_sec = ev->time / 1000,
+ .time_usec = ev->time * 1000,
+ .button = xcb_button_to_wl(ev->detail),
+ .state = event->response_type == XCB_BUTTON_PRESS ?
+ WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED,
+ };
+
+ wl_signal_emit(&x11->pointer.events.button, &button);
+ }
x11->time = ev->time;
break;
}
@@ -78,7 +95,7 @@ static bool handle_x11_event(struct wlr_x11_backend *x11, xcb_generic_event_t *e
struct wlr_event_pointer_motion_absolute abs = {
.device = &x11->pointer_dev,
.time_sec = ev->time / 1000,
- .time_usec = (ev->time % 1000) * 1000,
+ .time_usec = ev->time * 1000,
.x_mm = ev->event_x,
.y_mm = ev->event_y,
.width_mm = output->wlr_output.width,
@@ -109,7 +126,7 @@ static bool handle_x11_event(struct wlr_x11_backend *x11, xcb_generic_event_t *e
struct wlr_event_pointer_motion_absolute abs = {
.device = &x11->pointer_dev,
.time_sec = x11->time / 1000,
- .time_usec = (x11->time % 1000) * 1000,
+ .time_usec = x11->time * 1000,
.x_mm = pointer->root_x,
.y_mm = pointer->root_y,
.width_mm = output->wlr_output.width,
@@ -258,6 +275,7 @@ static bool wlr_x11_backend_start(struct wlr_backend *backend) {
xcb_map_window(x11->xcb_conn, output->win);
xcb_flush(x11->xcb_conn);
+ wlr_output_create_global(&output->wlr_output, x11->wl_display);
wl_signal_emit(&x11->backend.events.output_add, output);
wl_signal_emit(&x11->backend.events.input_add, &x11->keyboard_dev);
diff --git a/examples/meson.build b/examples/meson.build
index 6ec40843..a29a3310 100644
--- a/examples/meson.build
+++ b/examples/meson.build
@@ -22,3 +22,9 @@ executable(
dependencies: wlroots,
link_with: lib_shared,
)
+
+executable(
+ 'screenshot',
+ 'screenshot.c',
+ dependencies: [wayland_client, wlr_protos],
+)
diff --git a/examples/pointer.c b/examples/pointer.c
index 11f67f9b..238be9b3 100644
--- a/examples/pointer.c
+++ b/examples/pointer.c
@@ -112,7 +112,8 @@ static void handle_output_add(struct output_state *ostate) {
// TODO the cursor must be set depending on which surface it is displayed
// over which should happen in the compositor.
if (!wlr_output_set_cursor(wlr_output, image->buffer,
- image->width, image->width, image->height)) {
+ image->width, image->width, image->height,
+ image->hotspot_x, image->hotspot_y)) {
wlr_log(L_DEBUG, "Failed to set hardware cursor");
return;
}
diff --git a/examples/screenshot.c b/examples/screenshot.c
new file mode 100644
index 00000000..95af49ca
--- /dev/null
+++ b/examples/screenshot.c
@@ -0,0 +1,288 @@
+/*
+ * Copyright © 2008 Kristian Høgsberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#define _XOPEN_SOURCE 700
+#define _POSIX_C_SOURCE 199309L
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <wayland-client.h>
+#include <limits.h>
+#include <sys/param.h>
+#include <screenshooter-client-protocol.h>
+#include "../backend/wayland/os-compatibility.c"
+
+static struct wl_shm *shm = NULL;
+static struct orbital_screenshooter *screenshooter = NULL;
+static struct wl_list output_list;
+int min_x, min_y, max_x, max_y;
+int buffer_copy_done;
+
+struct screenshooter_output {
+ struct wl_output *output;
+ struct wl_buffer *buffer;
+ int width, height, offset_x, offset_y;
+ void *data;
+ struct wl_list link;
+};
+
+static void output_handle_geometry(void *data, struct wl_output *wl_output,
+ int x, int y, int physical_width, int physical_height, int subpixel,
+ const char *make, const char *model, int transform) {
+ struct screenshooter_output *output = wl_output_get_user_data(wl_output);
+
+ if (wl_output == output->output) {
+ output->offset_x = x;
+ output->offset_y = y;
+ }
+}
+
+static void output_handle_mode(void *data, struct wl_output *wl_output,
+ uint32_t flags, int width, int height, int refresh) {
+ struct screenshooter_output *output = wl_output_get_user_data(wl_output);
+
+ if (wl_output == output->output && (flags & WL_OUTPUT_MODE_CURRENT)) {
+ output->width = width;
+ output->height = height;
+ }
+}
+
+static void output_handle_done(void *data, struct wl_output *wl_output) {
+
+}
+
+static const struct wl_output_listener output_listener = {
+ .geometry = output_handle_geometry,
+ .mode = output_handle_mode,
+ .done = output_handle_done,
+};
+
+static void screenshot_done(void *data, struct orbital_screenshot *screenshot) {
+ buffer_copy_done = 1;
+}
+
+static const struct orbital_screenshot_listener screenshot_listener = {
+ .done = screenshot_done,
+};
+
+static void handle_global(void *data, struct wl_registry *registry,
+ uint32_t name, const char *interface, uint32_t version) {
+ static struct screenshooter_output *output;
+
+ if (strcmp(interface, "wl_output") == 0) {
+ output = calloc(1, sizeof *output);
+ output->output = wl_registry_bind(registry, name, &wl_output_interface,
+ 1);
+ wl_list_insert(&output_list, &output->link);
+ wl_output_add_listener(output->output, &output_listener, output);
+ } else if (strcmp(interface, "wl_shm") == 0) {
+ shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
+ } else if (strcmp(interface, "orbital_screenshooter") == 0) {
+ screenshooter = wl_registry_bind(registry, name,
+ &orbital_screenshooter_interface, 1);
+ }
+}
+
+static void handle_global_remove(void *data, struct wl_registry *registry,
+ uint32_t name) {
+ // Unimplemented
+}
+
+static const struct wl_registry_listener registry_listener = {
+ .global = handle_global,
+ .global_remove = handle_global_remove,
+};
+
+static struct wl_buffer *create_shm_buffer(int width, int height,
+ void **data_out) {
+ int stride = width * 4;
+ int size = stride * height;
+
+ int fd = os_create_anonymous_file(size);
+ if (fd < 0) {
+ fprintf(stderr, "creating a buffer file for %d B failed: %m\n", size);
+ return NULL;
+ }
+
+ void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (data == MAP_FAILED) {
+ fprintf(stderr, "mmap failed: %m\n");
+ close(fd);
+ return NULL;
+ }
+
+ struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size);
+ close(fd);
+ struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, width, height,
+ stride, WL_SHM_FORMAT_XRGB8888);
+ wl_shm_pool_destroy(pool);
+
+ *data_out = data;
+
+ return buffer;
+}
+
+static void argb_to_rgba(uint32_t *data, size_t height, size_t stride) {
+ size_t n = height*stride/4;
+ for (size_t i = 0; i < n; ++i) {
+ uint32_t v = data[i];
+ uint32_t rgb = v & 0x00ffffff;
+ uint32_t a = (v & 0xff000000) >> 24;
+ data[i] = (rgb << 8) | a;
+ }
+}
+
+static void write_image(const char *filename, int width, int height) {
+ int buffer_stride = width * 4;
+
+ void *data = calloc(1, buffer_stride * height);
+ if (!data) {
+ return;
+ }
+
+ struct screenshooter_output *output, *next;
+ wl_list_for_each_safe(output, next, &output_list, link) {
+ int output_stride = output->width * 4;
+ void *s = output->data;
+ void *d = data + (output->offset_y - min_y) * buffer_stride +
+ (output->offset_x - min_x) * 4;
+
+ for (int i = 0; i < output->height; i++) {
+ memcpy(d, s, output_stride);
+ d += buffer_stride;
+ s += output_stride;
+ }
+
+ free(output);
+ }
+
+ argb_to_rgba(data, height, buffer_stride);
+
+ char size[10 + 1 + 10 + 2 + 1]; // int32_t are max 10 digits
+ sprintf(size, "%dx%d+0", width, height);
+
+ int fd[2];
+ pipe(fd);
+
+ pid_t child = fork();
+ if (child < 0) {
+ fprintf(stderr, "fork() failed\n");
+ exit(EXIT_FAILURE);
+ } else if (child != 0) {
+ close(fd[0]);
+ write(fd[1], data, buffer_stride * height);
+ close(fd[1]);
+ free(data);
+ waitpid(child, NULL, 0);
+ } else {
+ close(fd[1]);
+ if (dup2(fd[0], 0) != 0) {
+ fprintf(stderr, "cannot dup the pipe\n");
+ exit(EXIT_FAILURE);
+ }
+ close(fd[0]);
+ execlp("convert", "convert", "-depth", "8", "-size", size, "rgba:-",
+ "-alpha", "opaque", filename, NULL);
+ fprintf(stderr, "cannot execute convert\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
+static int set_buffer_size(int *width, int *height) {
+ struct screenshooter_output *output;
+ min_x = min_y = INT_MAX;
+ max_x = max_y = INT_MIN;
+ int position = 0;
+
+ wl_list_for_each_reverse(output, &output_list, link) {
+ output->offset_x = position;
+ position += output->width;
+ }
+
+ wl_list_for_each(output, &output_list, link) {
+ min_x = MIN(min_x, output->offset_x);
+ min_y = MIN(min_y, output->offset_y);
+ max_x = MAX(max_x, output->offset_x + output->width);
+ max_y = MAX(max_y, output->offset_y + output->height);
+ }
+
+ if (max_x <= min_x || max_y <= min_y) {
+ return -1;
+ }
+
+ *width = max_x - min_x;
+ *height = max_y - min_y;
+
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ struct wl_display * display = wl_display_connect(NULL);
+ if (display == NULL) {
+ fprintf(stderr, "failed to create display: %m\n");
+ return -1;
+ }
+
+ wl_list_init(&output_list);
+ struct wl_registry *registry = wl_display_get_registry(display);
+ wl_registry_add_listener(registry, &registry_listener, NULL);
+ wl_display_dispatch(display);
+ wl_display_roundtrip(display);
+
+ if (screenshooter == NULL) {
+ fprintf(stderr, "display doesn't support screenshooter\n");
+ return -1;
+ }
+
+ int width, height;
+ if (set_buffer_size(&width, &height)) {
+ fprintf(stderr, "cannot set buffer size\n");
+ return -1;
+ }
+
+ struct screenshooter_output *output;
+ wl_list_for_each(output, &output_list, link) {
+ if (output->width == 0 || output->height == 0) {
+ continue;
+ }
+
+ output->buffer = create_shm_buffer(output->width, output->height, &output->data);
+ if (output->buffer == NULL) {
+ return -1;
+ }
+ struct orbital_screenshot *screenshot = orbital_screenshooter_shoot(
+ screenshooter, output->output, output->buffer);
+ orbital_screenshot_add_listener(screenshot, &screenshot_listener, screenshot);
+ buffer_copy_done = 0;
+ while (!buffer_copy_done)
+ wl_display_roundtrip(display);
+ }
+
+ write_image("wayland-screenshot.png", width, height);
+ return EXIT_SUCCESS;
+}
diff --git a/glgen.sh b/glgen.sh
new file mode 100755
index 00000000..75d93c3b
--- /dev/null
+++ b/glgen.sh
@@ -0,0 +1,102 @@
+#!/bin/sh
+
+# Generates a simple GL/EGL extension function loader.
+#
+# The input is a .txt file, with each function to load on its own line.
+# If a line starts with a -, it is optional, and will not cause the loader
+# to fail if it can't load the function. You'll need to check if that function
+# is NULL before using it.
+
+if [ $# -ne 2 ]; then
+ exit 1
+fi
+
+SPEC=$1
+OUT=$2
+
+BASE=$(basename "$SPEC" .txt)
+INCLUDE_GUARD=$(printf %s "$SPEC" | tr -c [:alnum:] _ | tr [:lower:] [:upper:])
+
+DECL=""
+DEFN=""
+LOADER=""
+
+DECL_FMT='extern %s %s;'
+DEFN_FMT='%s %s;'
+LOADER_FMT='%s = (%s)eglGetProcAddress("%s");'
+CHECK_FMT='if (!%s) {
+ wlr_log(L_ERROR, "Unable to load %s");
+ return false;
+}'
+
+while read -r COMMAND; do
+ OPTIONAL=0
+ FUNC_PTR_FMT='PFN%sPROC'
+
+ case $COMMAND in
+ -*)
+ OPTIONAL=1
+ ;;
+ esac
+
+ case $COMMAND in
+ *WL)
+ FUNC_PTR_FMT='PFN%s'
+ ;;
+ esac
+
+ COMMAND=${COMMAND#-}
+ FUNC_PTR=$(printf "$FUNC_PTR_FMT" "$COMMAND" | tr [:lower:] [:upper:])
+
+ DECL="$DECL$(printf "\n$DECL_FMT" "$FUNC_PTR" "$COMMAND")"
+ DEFN="$DEFN$(printf "\n$DEFN_FMT" "$FUNC_PTR" "$COMMAND")"
+ LOADER="$LOADER$(printf "\n$LOADER_FMT" "$COMMAND" "$FUNC_PTR" "$COMMAND")"
+
+ if [ $OPTIONAL -eq 0 ]; then
+ LOADER="$LOADER$(printf "\n$CHECK_FMT" "$COMMAND" "$COMMAND")"
+ fi
+done < $SPEC
+
+
+case $OUT in
+*.h)
+ cat > $OUT << EOF
+ #ifndef $INCLUDE_GUARD
+ #define $INCLUDE_GUARD
+
+ #include <stdbool.h>
+
+ #include <EGL/egl.h>
+ #include <EGL/eglext.h>
+ #include <EGL/eglmesaext.h>
+ #include <GLES2/gl2.h>
+ #include <GLES2/gl2ext.h>
+
+ bool load_$BASE(void);
+ $DECL
+
+ #endif
+EOF
+ ;;
+*.c)
+ cat > $OUT << EOF
+ #include <wlr/util/log.h>
+ #include "$BASE.h"
+ $DEFN
+
+ bool load_$BASE(void) {
+ static bool done = false;
+ if (done) {
+ return true;
+ }
+ $LOADER
+
+ done = true;
+ return true;
+ }
+EOF
+ ;;
+*)
+ exit 1
+ ;;
+esac
diff --git a/include/backend/wayland.h b/include/backend/wayland.h
index 752dab69..508a7f52 100644
--- a/include/backend/wayland.h
+++ b/include/backend/wayland.h
@@ -67,7 +67,8 @@ struct wlr_wl_pointer {
};
void wlr_wl_registry_poll(struct wlr_wl_backend *backend);
-void wlr_wl_output_update_cursor(struct wlr_wl_backend_output *output, uint32_t serial);
+void wlr_wl_output_update_cursor(struct wlr_wl_backend_output *output,
+ uint32_t serial, int32_t hotspot_x, int32_t hotspot_y);
struct wlr_wl_backend_output *wlr_wl_output_for_surface(
struct wlr_wl_backend *backend, struct wl_surface *surface);
diff --git a/include/rootston/desktop.h b/include/rootston/desktop.h
index aa74ad3e..1225bdcd 100644
--- a/include/rootston/desktop.h
+++ b/include/rootston/desktop.h
@@ -8,6 +8,7 @@
#include <wlr/types/wlr_wl_shell.h>
#include <wlr/types/wlr_xdg_shell_v6.h>
#include <wlr/types/wlr_gamma_control.h>
+#include <wlr/types/wlr_screenshooter.h>
#include <wlr/util/list.h>
#include "rootston/view.h"
#include "rootston/config.h"
@@ -35,6 +36,7 @@ struct roots_desktop {
struct wlr_wl_shell *wl_shell;
struct wlr_xdg_shell_v6 *xdg_shell_v6;
struct wlr_gamma_control_manager *gamma_control_manager;
+ struct wlr_screenshooter *screenshooter;
struct wl_listener output_add;
struct wl_listener output_remove;
diff --git a/include/rootston/input.h b/include/rootston/input.h
index ae3e3b80..9caf66c0 100644
--- a/include/rootston/input.h
+++ b/include/rootston/input.h
@@ -81,7 +81,7 @@ struct roots_input {
struct wlr_seat *wl_seat;
enum roots_cursor_mode mode;
- struct roots_view *active_view;
+ struct roots_view *active_view, *last_active_view;
int offs_x, offs_y;
int view_x, view_y, view_width, view_height;
float view_rotation;
@@ -105,6 +105,8 @@ struct roots_input {
struct wl_listener cursor_axis;
struct wl_listener cursor_tool_axis;
struct wl_listener cursor_tool_tip;
+
+ struct wl_listener pointer_grab_end;
};
struct roots_input *input_create(struct roots_server *server,
@@ -130,4 +132,7 @@ void view_begin_move(struct roots_input *input, struct wlr_cursor *cursor,
void view_begin_resize(struct roots_input *input, struct wlr_cursor *cursor,
struct roots_view *view, uint32_t edges);
+void set_view_focus(struct roots_input *input, struct roots_desktop *desktop,
+ struct roots_view *view);
+
#endif
diff --git a/include/rootston/view.h b/include/rootston/view.h
index 2a90670e..af087182 100644
--- a/include/rootston/view.h
+++ b/include/rootston/view.h
@@ -1,5 +1,6 @@
#ifndef _ROOTSTON_VIEW_H
#define _ROOTSTON_VIEW_H
+
#include <stdbool.h>
#include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_surface.h>
@@ -14,17 +15,25 @@ struct roots_wl_shell_surface {
struct wl_listener request_resize;
struct wl_listener request_set_fullscreen;
struct wl_listener request_set_maximized;
+
+ struct wl_listener surface_commit;
+
+ bool initialized;
};
struct roots_xdg_surface_v6 {
struct roots_view *view;
+
// TODO: Maybe destroy listener should go in roots_view
+ struct wl_listener commit;
struct wl_listener destroy;
struct wl_listener ping_timeout;
struct wl_listener request_minimize;
struct wl_listener request_move;
struct wl_listener request_resize;
struct wl_listener request_show_window_menu;
+
+ bool initialized;
};
struct roots_xwayland_surface {
@@ -66,14 +75,16 @@ struct roots_view {
// If not then this should follow the typical type/impl pattern we use
// elsewhere
void (*get_size)(struct roots_view *view, struct wlr_box *box);
- void (*get_input_bounds)(struct roots_view *view, struct wlr_box *box);
void (*activate)(struct roots_view *view, bool active);
void (*resize)(struct roots_view *view, uint32_t width, uint32_t height);
+ void (*close)(struct roots_view *view);
};
void view_get_size(struct roots_view *view, struct wlr_box *box);
-void view_get_input_bounds(struct roots_view *view, struct wlr_box *box);
void view_activate(struct roots_view *view, bool active);
void view_resize(struct roots_view *view, uint32_t width, uint32_t height);
+void view_close(struct roots_view *view);
+bool view_center(struct roots_view *view);
+bool view_initialize(struct roots_view *view);
#endif
diff --git a/include/wlr/egl.h b/include/wlr/egl.h
index 25329175..9ab4d9ce 100644
--- a/include/wlr/egl.h
+++ b/include/wlr/egl.h
@@ -10,15 +10,6 @@ struct wlr_egl {
EGLConfig config;
EGLContext context;
- PFNEGLGETPLATFORMDISPLAYEXTPROC get_platform_display;
- PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC create_platform_window_surface;
-
- PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR;
- PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR;
- PFNEGLQUERYWAYLANDBUFFERWL eglQueryWaylandBufferWL;
- PFNEGLBINDWAYLANDDISPLAYWL eglBindWaylandDisplayWL;
- PFNEGLUNBINDWAYLANDDISPLAYWL eglUnbindWaylandDisplayWL;
-
const char *egl_exts;
const char *gl_exts;
diff --git a/include/wlr/interfaces/wlr_keyboard.h b/include/wlr/interfaces/wlr_keyboard.h
index 78c1f753..570f5721 100644
--- a/include/wlr/interfaces/wlr_keyboard.h
+++ b/include/wlr/interfaces/wlr_keyboard.h
@@ -11,7 +11,10 @@ struct wlr_keyboard_impl {
void wlr_keyboard_init(struct wlr_keyboard *keyboard, struct wlr_keyboard_impl *impl);
void wlr_keyboard_destroy(struct wlr_keyboard *keyboard);
-void wlr_keyboard_update_state(struct wlr_keyboard *keyboard,
+void wlr_keyboard_notify_key(struct wlr_keyboard *keyboard,
struct wlr_event_keyboard_key *event);
+void wlr_keyboard_notify_modifiers(struct wlr_keyboard *keyboard,
+ uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked,
+ uint32_t group);
#endif
diff --git a/include/wlr/interfaces/wlr_output.h b/include/wlr/interfaces/wlr_output.h
index 7ed19ed9..7d2821e0 100644
--- a/include/wlr/interfaces/wlr_output.h
+++ b/include/wlr/interfaces/wlr_output.h
@@ -10,7 +10,8 @@ struct wlr_output_impl {
void (*transform)(struct wlr_output *output,
enum wl_output_transform transform);
bool (*set_cursor)(struct wlr_output *output, const uint8_t *buf,
- int32_t stride, uint32_t width, uint32_t height);
+ int32_t stride, uint32_t width, uint32_t height,
+ int32_t hotspot_x, int32_t hotspot_y);
bool (*move_cursor)(struct wlr_output *output, int x, int y);
void (*destroy)(struct wlr_output *output);
void (*make_current)(struct wlr_output *output);
diff --git a/include/wlr/render.h b/include/wlr/render.h
index 2fbfb476..5027064d 100644
--- a/include/wlr/render.h
+++ b/include/wlr/render.h
@@ -30,27 +30,32 @@ struct wlr_texture *wlr_render_texture_create(struct wlr_renderer *r);
* This will render the texture at <123, 321>.
*/
bool wlr_render_with_matrix(struct wlr_renderer *r,
- struct wlr_texture *texture, const float (*matrix)[16]);
+ struct wlr_texture *texture, const float (*matrix)[16]);
/**
* Renders a solid quad in the specified color.
*/
void wlr_render_colored_quad(struct wlr_renderer *r,
- const float (*color)[4], const float (*matrix)[16]);
+ const float (*color)[4], const float (*matrix)[16]);
/**
* Renders a solid ellipse in the specified color.
*/
void wlr_render_colored_ellipse(struct wlr_renderer *r,
- const float (*color)[4], const float (*matrix)[16]);
+ const float (*color)[4], const float (*matrix)[16]);
/**
* Returns a list of pixel formats supported by this renderer.
*/
const enum wl_shm_format *wlr_renderer_get_formats(
- struct wlr_renderer *r, size_t *len);
+ struct wlr_renderer *r, size_t *len);
/**
* Returns true if this wl_buffer is a DRM buffer.
*/
bool wlr_renderer_buffer_is_drm(struct wlr_renderer *renderer,
- struct wl_resource *buffer);
+ struct wl_resource *buffer);
+/**
+ * Reads pixels and stores them in out_data as ARGB8888.
+ */
+void wlr_renderer_read_pixels(struct wlr_renderer *r, int x, int y,
+ int width, int height, void *out_data);
/**
* Destroys this wlr_renderer. Textures must be destroyed separately.
*/
diff --git a/include/wlr/render/interface.h b/include/wlr/render/interface.h
index cbe33822..bbc5acb4 100644
--- a/include/wlr/render/interface.h
+++ b/include/wlr/render/interface.h
@@ -28,6 +28,8 @@ struct wlr_renderer_impl {
struct wlr_renderer *renderer, size_t *len);
bool (*buffer_is_drm)(struct wlr_renderer *renderer,
struct wl_resource *buffer);
+ void (*read_pixels)(struct wlr_renderer *renderer, int x, int y, int width,
+ int height, void *out_data);
void (*destroy)(struct wlr_renderer *renderer);
};
diff --git a/include/wlr/types/wlr_keyboard.h b/include/wlr/types/wlr_keyboard.h
index 9ec8ddd4..a6726a7c 100644
--- a/include/wlr/types/wlr_keyboard.h
+++ b/include/wlr/types/wlr_keyboard.h
@@ -3,6 +3,7 @@
#include <wayland-server.h>
#include <stdint.h>
+#include <stdbool.h>
#include <wayland-server.h>
#include <xkbcommon/xkbcommon.h>
@@ -65,6 +66,7 @@ struct wlr_event_keyboard_key {
uint32_t time_sec;
uint64_t time_usec;
uint32_t keycode;
+ bool update_state;
enum wlr_key_state state;
};
diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h
index 3208acac..52d377e3 100644
--- a/include/wlr/types/wlr_output.h
+++ b/include/wlr/types/wlr_output.h
@@ -37,6 +37,7 @@ struct wlr_output {
struct {
struct wl_signal frame;
+ struct wl_signal swap_buffers;
struct wl_signal resolution;
struct wl_signal destroy;
} events;
@@ -45,6 +46,7 @@ struct wlr_output {
bool is_sw;
int32_t x, y;
uint32_t width, height;
+ int32_t hotspot_x, hotspot_y;
struct wlr_renderer *renderer;
struct wlr_texture *texture;
} cursor;
@@ -58,7 +60,8 @@ bool wlr_output_set_mode(struct wlr_output *output,
void wlr_output_transform(struct wlr_output *output,
enum wl_output_transform transform);
bool wlr_output_set_cursor(struct wlr_output *output,
- const uint8_t *buf, int32_t stride, uint32_t width, uint32_t height);
+ const uint8_t *buf, int32_t stride, uint32_t width, uint32_t height,
+ int32_t hotspot_x, int32_t hotspot_y);
bool wlr_output_move_cursor(struct wlr_output *output, int x, int y);
void wlr_output_destroy(struct wlr_output *output);
void wlr_output_effective_resolution(struct wlr_output *output,
diff --git a/include/wlr/types/wlr_screenshooter.h b/include/wlr/types/wlr_screenshooter.h
new file mode 100644
index 00000000..4bda3d3c
--- /dev/null
+++ b/include/wlr/types/wlr_screenshooter.h
@@ -0,0 +1,26 @@
+#ifndef _WLR_SCREENSHOOTER_H
+#define _WLR_SCREENSHOOTER_H
+#include <wayland-server.h>
+
+struct wlr_screenshooter {
+ struct wl_global *wl_global;
+ struct wlr_renderer *renderer;
+
+ void *data;
+};
+
+struct wlr_screenshot {
+ struct wl_resource *resource;
+ struct wl_resource *output_resource;
+
+ struct wlr_output *output;
+ struct wlr_screenshooter *screenshooter;
+
+ void* data;
+};
+
+struct wlr_screenshooter *wlr_screenshooter_create(struct wl_display *display,
+ struct wlr_renderer *renderer);
+void wlr_screenshooter_destroy(struct wlr_screenshooter *screenshooter);
+
+#endif
diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h
index 489bd529..d3d3e00d 100644
--- a/include/wlr/types/wlr_seat.h
+++ b/include/wlr/types/wlr_seat.h
@@ -24,6 +24,52 @@ struct wlr_seat_handle {
struct wl_list link;
};
+struct wlr_seat_pointer_grab;
+
+struct wlr_pointer_grab_interface {
+ void (*enter)(struct wlr_seat_pointer_grab *grab,
+ struct wlr_surface *surface, double sx, double sy);
+ void (*motion)(struct wlr_seat_pointer_grab *grab, uint32_t time,
+ double sx, double sy);
+ uint32_t (*button)(struct wlr_seat_pointer_grab *grab, uint32_t time,
+ uint32_t button, uint32_t state);
+ void (*axis)(struct wlr_seat_pointer_grab *grab, uint32_t time,
+ enum wlr_axis_orientation orientation, double value);
+ void (*cancel)(struct wlr_seat_pointer_grab *grab);
+};
+
+struct wlr_seat_keyboard_grab;
+
+struct wlr_keyboard_grab_interface {
+ void (*enter)(struct wlr_seat_keyboard_grab *grab, struct wlr_surface *surface);
+ void (*key)(struct wlr_seat_keyboard_grab *grab, uint32_t time,
+ uint32_t key, uint32_t state);
+ void (*modifiers)(struct wlr_seat_keyboard_grab *grab,
+ uint32_t mods_depressed, uint32_t mods_latched,
+ uint32_t mods_locked, uint32_t group);
+ void (*cancel)(struct wlr_seat_keyboard_grab *grab);
+};
+
+/**
+ * Passed to `wlr_seat_keyboard_start_grab()` to start a grab of the keyboard.
+ * The grabber is responsible for handling keyboard events for the seat.
+ */
+struct wlr_seat_keyboard_grab {
+ const struct wlr_keyboard_grab_interface *interface;
+ struct wlr_seat *seat;
+ void *data;
+};
+
+/**
+ * Passed to `wlr_seat_pointer_start_grab()` to start a grab of the pointer. The
+ * grabber is responsible for handling pointer events for the seat.
+ */
+struct wlr_seat_pointer_grab {
+ const struct wlr_pointer_grab_interface *interface;
+ struct wlr_seat *seat;
+ void *data;
+};
+
struct wlr_seat_pointer_state {
struct wlr_seat *wlr_seat;
struct wlr_seat_handle *focused_handle;
@@ -31,6 +77,9 @@ struct wlr_seat_pointer_state {
struct wl_listener surface_destroy;
struct wl_listener resource_destroy;
+
+ struct wlr_seat_pointer_grab *grab;
+ struct wlr_seat_pointer_grab *default_grab;
};
struct wlr_seat_keyboard {
@@ -50,6 +99,9 @@ struct wlr_seat_keyboard_state {
struct wl_listener surface_destroy;
struct wl_listener resource_destroy;
+
+ struct wlr_seat_keyboard_grab *grab;
+ struct wlr_seat_keyboard_grab *default_grab;
};
struct wlr_seat {
@@ -67,12 +119,17 @@ struct wlr_seat {
struct {
struct wl_signal client_bound;
struct wl_signal client_unbound;
+
+ struct wl_signal pointer_grab_begin;
+ struct wl_signal pointer_grab_end;
+
+ struct wl_signal keyboard_grab_begin;
+ struct wl_signal keyboard_grab_end;
} events;
void *data;
};
-
/**
* Allocates a new wlr_seat and adds a wl_seat global to the display.
*/
@@ -109,6 +166,8 @@ bool wlr_seat_pointer_surface_has_focus(struct wlr_seat *wlr_seat,
* Send a pointer enter event to the given surface and consider it to be the
* focused surface for the pointer. This will send a leave event to the last
* surface that was entered. Coordinates for the enter event are surface-local.
+ * Compositor should use `wlr_seat_pointer_notify_enter()` to change pointer
+ * focus to respect pointer grabs.
*/
void wlr_seat_pointer_enter(struct wlr_seat *wlr_seat,
struct wlr_surface *surface, double sx, double sy);
@@ -120,22 +179,72 @@ void wlr_seat_pointer_clear_focus(struct wlr_seat *wlr_seat);
/**
* Send a motion event to the surface with pointer focus. Coordinates for the
- * motion event are surface-local.
+ * motion event are surface-local. Compositors should use
+ * `wlr_seat_pointer_notify_motion()` to send motion events to respect pointer
+ * grabs.
*/
void wlr_seat_pointer_send_motion(struct wlr_seat *wlr_seat, uint32_t time,
double sx, double sy);
/**
* Send a button event to the surface with pointer focus. Coordinates for the
- * button event are surface-local. Returns the serial.
+ * button event are surface-local. Returns the serial. Compositors should use
+ * `wlr_seat_pointer_notify_button()` to send button events to respect pointer
+ * grabs.
*/
uint32_t wlr_seat_pointer_send_button(struct wlr_seat *wlr_seat, uint32_t time,
uint32_t button, uint32_t state);
+/**
+ * Send an axis event to the surface with pointer focus. Compositors should use
+ * `wlr_seat_pointer_notify_axis()` to send axis events to respect pointer
+ * grabs.
+ **/
void wlr_seat_pointer_send_axis(struct wlr_seat *wlr_seat, uint32_t time,
enum wlr_axis_orientation orientation, double value);
/**
+ * Start a grab of the pointer of this seat. The grabber is responsible for
+ * handling all pointer events until the grab ends.
+ */
+void wlr_seat_pointer_start_grab(struct wlr_seat *wlr_seat,
+ struct wlr_seat_pointer_grab *grab);
+
+/**
+ * End the grab of the pointer of this seat. This reverts the grab back to the
+ * default grab for the pointer.
+ */
+void wlr_seat_pointer_end_grab(struct wlr_seat *wlr_seat);
+
+/**
+ * Notify the seat of a pointer enter event to the given surface and request it to be the
+ * focused surface for the pointer. Pass surface-local coordinates where the
+ * enter occurred.
+ */
+void wlr_seat_pointer_notify_enter(struct wlr_seat *wlr_seat,
+ struct wlr_surface *surface, double sx, double sy);
+
+/**
+ * Notify the seat of motion over the given surface. Pass surface-local
+ * coordinates where the pointer motion occurred.
+ */
+void wlr_seat_pointer_notify_motion(struct wlr_seat *wlr_seat, uint32_t time,
+ double sx, double sy);
+
+/**
+ * Notify the seat that a button has been pressed. Returns the serial of the
+ * button press or zero if no button press was sent.
+ */
+uint32_t wlr_seat_pointer_notify_button(struct wlr_seat *wlr_seat,
+ uint32_t time, uint32_t button, uint32_t state);
+
+/**
+ * Notify the seat of an axis event.
+ */
+void wlr_seat_pointer_notify_axis(struct wlr_seat *wlr_seat, uint32_t time,
+ enum wlr_axis_orientation orientation, double value);
+
+/**
* Attaches this keyboard to the seat. Key events from this keyboard will be
* propegated to the focused client.
*/
@@ -150,9 +259,47 @@ void wlr_seat_attach_keyboard(struct wlr_seat *seat,
void wlr_seat_detach_keyboard(struct wlr_seat *seat, struct wlr_keyboard *kb);
/**
+ * Start a grab of the keyboard of this seat. The grabber is responsible for
+ * handling all keyboard events until the grab ends.
+ */
+void wlr_seat_keyboard_start_grab(struct wlr_seat *wlr_seat,
+ struct wlr_seat_keyboard_grab *grab);
+
+/**
+ * End the grab of the keyboard of this seat. This reverts the grab back to the
+ * default grab for the keyboard.
+ */
+void wlr_seat_keyboard_end_grab(struct wlr_seat *wlr_seat);
+
+/**
+ * Send the keyboard key to focused keyboard resources. Compositors should use
+ * `wlr_seat_attach_keyboard()` to automatically handle keyboard events.
+ */
+void wlr_seat_keyboard_send_key(struct wlr_seat *seat, uint32_t time,
+ uint32_t key, uint32_t state);
+
+/**
+ * Send the modifier state to focused keyboard resources. Compositors should use
+ * `wlr_seat_attach_keyboard()` to automatically handle keyboard events.
+ */
+void wlr_seat_keyboard_send_modifiers(struct wlr_seat *seat,
+ uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked,
+ uint32_t group);
+
+/**
+ * Notify the seat that the keyboard focus has changed and request it to be the
+ * focused surface for this keyboard. Defers to any current grab of the seat's
+ * keyboard.
+ */
+void wlr_seat_keyboard_notify_enter(struct wlr_seat *wlr_seat,
+ struct wlr_surface *surface);
+
+/**
* Send a keyboard enter event to the given surface and consider it to be the
* focused surface for the keyboard. This will send a leave event to the last
- * surface that was entered. Pass an array of currently pressed keys.
+ * surface that was entered. Compositors should use
+ * `wlr_seat_keyboard_notify_enter()` to change keyboard focus to respect
+ * keyboard grabs.
*/
void wlr_seat_keyboard_enter(struct wlr_seat *wlr_seat,
struct wlr_surface *surface);
diff --git a/include/wlr/types/wlr_surface.h b/include/wlr/types/wlr_surface.h
index 23e53811..9f898b38 100644
--- a/include/wlr/types/wlr_surface.h
+++ b/include/wlr/types/wlr_surface.h
@@ -71,7 +71,7 @@ struct wlr_surface {
struct {
struct wl_signal commit;
struct wl_signal destroy;
- } signals;
+ } events;
// destroy listener used by compositor
struct wl_listener compositor_listener;
diff --git a/include/wlr/types/wlr_xdg_shell_v6.h b/include/wlr/types/wlr_xdg_shell_v6.h
index cc52d9c7..00295ec1 100644
--- a/include/wlr/types/wlr_xdg_shell_v6.h
+++ b/include/wlr/types/wlr_xdg_shell_v6.h
@@ -2,11 +2,13 @@
#define WLR_TYPES_WLR_XDG_SHELL_V6_H
#include <wlr/types/wlr_box.h>
+#include <wlr/types/wlr_seat.h>
#include <wayland-server.h>
struct wlr_xdg_shell_v6 {
struct wl_global *wl_global;
struct wl_list clients;
+ struct wl_list popup_grabs;
uint32_t ping_timeout;
struct {
@@ -28,6 +30,27 @@ struct wlr_xdg_client_v6 {
struct wl_event_source *ping_timer;
};
+struct wlr_xdg_popup_v6 {
+ struct wlr_xdg_surface_v6 *base;
+
+ struct wl_resource *resource;
+ bool committed;
+ struct wlr_xdg_surface_v6 *parent;
+ struct wlr_seat *seat;
+ struct wlr_box geometry;
+
+ struct wl_list grab_link; // wlr_xdg_popup_grab_v6::popups
+};
+
+// each seat gets a popup grab
+struct wlr_xdg_popup_grab_v6 {
+ struct wl_client *client;
+ struct wlr_seat_pointer_grab pointer_grab;
+ struct wlr_seat_keyboard_grab keyboard_grab;
+ struct wlr_seat *seat;
+ struct wl_list popups;
+ struct wl_list link; // wlr_xdg_shell_v6::popup_grabs
+};
enum wlr_xdg_surface_v6_role {
WLR_XDG_SURFACE_V6_ROLE_NONE,
@@ -61,7 +84,6 @@ struct wlr_xdg_toplevel_v6 {
struct wlr_xdg_toplevel_v6_state current;
};
-// TODO split up into toplevel and popup configure
struct wlr_xdg_surface_v6_configure {
struct wl_list link; // wlr_xdg_surface_v6::configure_list
uint32_t serial;
@@ -74,7 +96,14 @@ struct wlr_xdg_surface_v6 {
struct wlr_surface *surface;
struct wl_list link; // wlr_xdg_client_v6::surfaces
enum wlr_xdg_surface_v6_role role;
- struct wlr_xdg_toplevel_v6 *toplevel_state;
+
+ union {
+ struct wlr_xdg_toplevel_v6 *toplevel_state;
+ struct wlr_xdg_popup_v6 *popup_state;
+ };
+
+ struct wl_list popups;
+ struct wl_list popup_link;
bool configured;
struct wl_event_source *configure_idle;
@@ -172,4 +201,9 @@ void wlr_xdg_toplevel_v6_set_fullscreen(struct wlr_xdg_surface_v6 *surface,
void wlr_xdg_toplevel_v6_set_resizing(struct wlr_xdg_surface_v6 *surface,
bool resizing);
+/**
+ * Request that this toplevel surface closes.
+ */
+void wlr_xdg_toplevel_v6_send_close(struct wlr_xdg_surface_v6 *surface);
+
#endif
diff --git a/meson.build b/meson.build
index e3205aae..d885484b 100644
--- a/meson.build
+++ b/meson.build
@@ -14,6 +14,10 @@ add_project_arguments(
'-DWLR_SRC_DIR="@0@"'.format(meson.source_root()),
language: 'c',
)
+add_project_arguments(
+ '-I@0@'.format(meson.build_root()),
+ language: 'c',
+)
add_project_link_arguments(
'-Wl,-rpath,@0@'.format(meson.build_root()),
language: 'c',
@@ -83,8 +87,8 @@ install_subdir('include/wlr', install_dir: 'include', exclude_files: exclude_fil
subdir('protocol')
-subdir('backend')
subdir('render')
+subdir('backend')
subdir('types')
subdir('util')
subdir('xcursor')
diff --git a/protocol/meson.build b/protocol/meson.build
index 6dc88d76..79871fea 100644
--- a/protocol/meson.build
+++ b/protocol/meson.build
@@ -22,11 +22,14 @@ wayland_scanner_client = generator(
protocols = [
[wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'],
- 'gamma-control.xml'
+ 'gamma-control.xml',
+ 'screenshooter.xml',
]
client_protocols = [
- [wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml']
+ [wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'],
+ 'gamma-control.xml',
+ 'screenshooter.xml',
]
wl_protos_src = []
diff --git a/protocol/screenshooter.xml b/protocol/screenshooter.xml
new file mode 100644
index 00000000..fc48eac9
--- /dev/null
+++ b/protocol/screenshooter.xml
@@ -0,0 +1,16 @@
+<protocol name="orbital_screenshooter">
+
+ <interface name="orbital_screenshooter" version="1">
+ <request name="shoot">
+ <arg name="id" type="new_id" interface="orbital_screenshot"/>
+ <arg name="output" type="object" interface="wl_output"/>
+ <arg name="buffer" type="object" interface="wl_buffer"/>
+ </request>
+ </interface>
+
+ <interface name="orbital_screenshot" version="1">
+ <event name="done">
+ </event>
+ </interface>
+
+</protocol>
diff --git a/render/egl.c b/render/egl.c
index 82dea50c..9815b923 100644
--- a/render/egl.c
+++ b/render/egl.c
@@ -4,6 +4,7 @@
#include <stdlib.h>
#include <wlr/util/log.h>
#include <wlr/egl.h>
+#include "render/glapi.h"
// Extension documentation
// https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_image_base.txt.
@@ -46,27 +47,6 @@ const char *egl_error(void) {
}
}
-static bool egl_exts(struct wlr_egl *egl) {
- egl->get_platform_display = (PFNEGLGETPLATFORMDISPLAYEXTPROC)
- eglGetProcAddress("eglGetPlatformDisplayEXT");
-
- if (!egl->get_platform_display) {
- wlr_log(L_ERROR, "Failed to load EGL extension 'eglGetPlatformDisplayEXT'");
- return false;
- }
-
- egl->create_platform_window_surface = (PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)
- eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT");
-
- if (!egl->create_platform_window_surface) {
- wlr_log(L_ERROR,
- "Failed to load EGL extension 'eglCreatePlatformWindowSurfaceEXT'");
- return false;
- }
-
- return true;
-}
-
static bool egl_get_config(EGLDisplay disp, EGLConfig *out, EGLint visual_id) {
EGLint count = 0, matched = 0, ret;
@@ -103,7 +83,7 @@ static bool egl_get_config(EGLDisplay disp, EGLConfig *out, EGLint visual_id) {
bool wlr_egl_init(struct wlr_egl *egl, EGLenum platform, EGLint visual_id,
void *remote_display) {
- if (!egl_exts(egl)) {
+ if (!load_glapi()) {
return false;
}
@@ -112,7 +92,7 @@ bool wlr_egl_init(struct wlr_egl *egl, EGLenum platform, EGLint visual_id,
goto error;
}
- egl->display = egl->get_platform_display(platform, remote_display, NULL);
+ egl->display = eglGetPlatformDisplayEXT(platform, remote_display, NULL);
if (egl->display == EGL_NO_DISPLAY) {
wlr_log(L_ERROR, "Failed to create EGL display: %s", egl_error());
goto error;
@@ -142,22 +122,11 @@ bool wlr_egl_init(struct wlr_egl *egl, EGLenum platform, EGLint visual_id,
eglMakeCurrent(egl->display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl->context);
egl->egl_exts = eglQueryString(egl->display, EGL_EXTENSIONS);
if (strstr(egl->egl_exts, "EGL_WL_bind_wayland_display") == NULL ||
- strstr(egl->egl_exts, "EGL_KHR_image_base") == NULL) {
+ strstr(egl->egl_exts, "EGL_KHR_image_base") == NULL) {
wlr_log(L_ERROR, "Required egl extensions not supported");
goto error;
}
- egl->eglCreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC)
- eglGetProcAddress("eglCreateImageKHR");
- egl->eglDestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC)
- eglGetProcAddress("eglDestroyImageKHR");
- egl->eglQueryWaylandBufferWL = (PFNEGLQUERYWAYLANDBUFFERWL)
- (void*) eglGetProcAddress("eglQueryWaylandBufferWL");
- egl->eglBindWaylandDisplayWL = (PFNEGLBINDWAYLANDDISPLAYWL)
- (void*) eglGetProcAddress("eglBindWaylandDisplayWL");
- egl->eglUnbindWaylandDisplayWL = (PFNEGLUNBINDWAYLANDDISPLAYWL)
- (void*) eglGetProcAddress("eglUnbindWaylandDisplayWL");
-
egl->gl_exts = (const char*) glGetString(GL_EXTENSIONS);
wlr_log(L_INFO, "Using EGL %d.%d", (int)major, (int)minor);
wlr_log(L_INFO, "Supported EGL extensions: %s", egl->egl_exts);
@@ -174,8 +143,8 @@ error:
void wlr_egl_free(struct wlr_egl *egl) {
eglMakeCurrent(EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- if (egl->wl_display && egl->eglUnbindWaylandDisplayWL) {
- egl->eglUnbindWaylandDisplayWL(egl->display, egl->wl_display);
+ if (egl->wl_display && eglUnbindWaylandDisplayWL) {
+ eglUnbindWaylandDisplayWL(egl->display, egl->wl_display);
}
eglDestroyContext(egl->display, egl->context);
@@ -184,11 +153,11 @@ void wlr_egl_free(struct wlr_egl *egl) {
}
bool wlr_egl_bind_display(struct wlr_egl *egl, struct wl_display *local_display) {
- if (!egl->eglBindWaylandDisplayWL) {
+ if (!eglBindWaylandDisplayWL) {
return false;
}
- if (egl->eglBindWaylandDisplayWL(egl->display, local_display)) {
+ if (eglBindWaylandDisplayWL(egl->display, local_display)) {
egl->wl_display = local_display;
return true;
}
@@ -198,33 +167,33 @@ bool wlr_egl_bind_display(struct wlr_egl *egl, struct wl_display *local_display)
bool wlr_egl_query_buffer(struct wlr_egl *egl, struct wl_resource *buf,
int attrib, int *value) {
- if (!egl->eglQueryWaylandBufferWL) {
+ if (!eglQueryWaylandBufferWL) {
return false;
}
- return egl->eglQueryWaylandBufferWL(egl->display, buf, attrib, value);
+ return eglQueryWaylandBufferWL(egl->display, buf, attrib, value);
}
EGLImage wlr_egl_create_image(struct wlr_egl *egl, EGLenum target,
EGLClientBuffer buffer, const EGLint *attribs) {
- if (!egl->eglCreateImageKHR) {
+ if (!eglCreateImageKHR) {
return false;
}
- return egl->eglCreateImageKHR(egl->display, egl->context, target,
+ return eglCreateImageKHR(egl->display, egl->context, target,
buffer, attribs);
}
bool wlr_egl_destroy_image(struct wlr_egl *egl, EGLImage image) {
- if (!egl->eglDestroyImageKHR) {
+ if (!eglDestroyImageKHR) {
return false;
}
- egl->eglDestroyImageKHR(egl->display, image);
+ eglDestroyImageKHR(egl->display, image);
return true;
}
EGLSurface wlr_egl_create_surface(struct wlr_egl *egl, void *window) {
- EGLSurface surf = egl->create_platform_window_surface(egl->display, egl->config,
+ EGLSurface surf = eglCreatePlatformWindowSurfaceEXT(egl->display, egl->config,
window, NULL);
if (surf == EGL_NO_SURFACE) {
wlr_log(L_ERROR, "Failed to create EGL surface: %s", egl_error());
diff --git a/render/glapi.txt b/render/glapi.txt
new file mode 100644
index 00000000..81791204
--- /dev/null
+++ b/render/glapi.txt
@@ -0,0 +1,8 @@
+eglGetPlatformDisplayEXT
+eglCreatePlatformWindowSurfaceEXT
+-eglCreateImageKHR
+-eglDestroyImageKHR
+-eglQueryWaylandBufferWL
+-eglBindWaylandDisplayWL
+-eglUnbindWaylandDisplayWL
+-glEGLImageTargetTexture2DOES
diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c
index d6c22ebe..94c50b9a 100644
--- a/render/gles2/renderer.c
+++ b/render/gles2/renderer.c
@@ -12,8 +12,8 @@
#include <wlr/render/matrix.h>
#include <wlr/util/log.h>
#include "render/gles2.h"
+#include "render/glapi.h"
-PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES = NULL;
struct shaders shaders;
static bool compile_shader(GLuint type, const GLchar *src, GLuint *shader) {
@@ -101,25 +101,7 @@ error:
wlr_log(L_ERROR, "Failed to set up default shaders!");
}
-static void init_image_ext() {
- if (glEGLImageTargetTexture2DOES) {
- return;
- }
-
- const char *exts = (const char*) glGetString(GL_EXTENSIONS);
- if (strstr(exts, "GL_OES_EGL_image_external")) {
- glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)
- eglGetProcAddress("glEGLImageTargetTexture2DOES");
- }
-
- if (!glEGLImageTargetTexture2DOES) {
- wlr_log(L_INFO, "Failed to load glEGLImageTargetTexture2DOES "
- "Will not be able to attach drm buffers");
- }
-}
-
static void init_globals() {
- init_image_ext();
init_default_shaders();
}
@@ -226,7 +208,23 @@ static bool wlr_gles2_buffer_is_drm(struct wlr_renderer *_renderer,
(struct wlr_gles2_renderer *)_renderer;
EGLint format;
return wlr_egl_query_buffer(renderer->egl, buffer,
- EGL_TEXTURE_FORMAT, &format);
+ EGL_TEXTURE_FORMAT, &format);
+}
+
+static void rgba_to_argb(uint32_t *data, size_t height, size_t stride) {
+ size_t n = height*stride/4;
+ for (size_t i = 0; i < n; ++i) {
+ uint32_t v = data[i];
+ uint32_t rgb = (v & 0xffffff00) >> 8;
+ uint32_t a = v & 0x000000ff;
+ data[i] = rgb | (a << 24);
+ }
+}
+
+static void wlr_gles2_read_pixels(struct wlr_renderer *renderer, int x, int y,
+ int width, int height, void *out_data) {
+ glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, out_data);
+ rgba_to_argb(out_data, height, width*4);
}
static struct wlr_renderer_impl wlr_renderer_impl = {
@@ -238,6 +236,7 @@ static struct wlr_renderer_impl wlr_renderer_impl = {
.render_ellipse = wlr_gles2_render_ellipse,
.formats = wlr_gles2_formats,
.buffer_is_drm = wlr_gles2_buffer_is_drm,
+ .read_pixels = wlr_gles2_read_pixels,
};
struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_backend *backend) {
diff --git a/render/meson.build b/render/meson.build
index dc0ceeb9..749d1393 100644
--- a/render/meson.build
+++ b/render/meson.build
@@ -1,3 +1,16 @@
+glgen = find_program('../glgen.sh')
+
+glapi_c = custom_target('glapi.c',
+ input: 'glapi.txt',
+ output: '@BASENAME@.c',
+ command: [glgen, '@INPUT@', '@OUTPUT@'],
+)
+glapi_h = custom_target('glapi.h',
+ input: 'glapi.txt',
+ output: '@BASENAME@.h',
+ command: [glgen, '@INPUT@', '@OUTPUT@'],
+)
+
lib_wlr_render = static_library(
'wlr_render',
files(
@@ -11,6 +24,13 @@ lib_wlr_render = static_library(
'wlr_renderer.c',
'wlr_texture.c',
),
+ glapi_c,
+ glapi_h,
include_directories: wlr_inc,
dependencies: [glesv2, egl],
)
+
+wlr_render = declare_dependency(
+ link_with: lib_wlr_render,
+ sources: glapi_h,
+)
diff --git a/render/wlr_renderer.c b/render/wlr_renderer.c
index fec5e38a..ef0c31be 100644
--- a/render/wlr_renderer.c
+++ b/render/wlr_renderer.c
@@ -51,3 +51,8 @@ bool wlr_renderer_buffer_is_drm(struct wlr_renderer *r,
struct wl_resource *buffer) {
return r->impl->buffer_is_drm(r, buffer);
}
+
+void wlr_renderer_read_pixels(struct wlr_renderer *r, int x, int y,
+ int width, int height, void *out_data) {
+ r->impl->read_pixels(r, x, y, width, height, out_data);
+}
diff --git a/rootston/cursor.c b/rootston/cursor.c
index eec8eb5d..605920cc 100644
--- a/rootston/cursor.c
+++ b/rootston/cursor.c
@@ -68,8 +68,8 @@ void cursor_update_position(struct roots_input *input, uint32_t time) {
view = view_at(desktop, input->cursor->x, input->cursor->y, &surface,
&sx, &sy);
if (view) {
- wlr_seat_pointer_enter(input->wl_seat, surface, sx, sy);
- wlr_seat_pointer_send_motion(input->wl_seat, time, sx, sy);
+ wlr_seat_pointer_notify_enter(input->wl_seat, surface, sx, sy);
+ wlr_seat_pointer_notify_motion(input->wl_seat, time, sx, sy);
} else {
wlr_seat_pointer_clear_focus(input->wl_seat);
}
@@ -123,8 +123,8 @@ void cursor_update_position(struct roots_input *input, uint32_t time) {
}
}
-static void set_view_focus(struct roots_input *input,
- struct roots_desktop *desktop, struct roots_view *view) {
+void set_view_focus(struct roots_input *input, struct roots_desktop *desktop,
+ struct roots_view *view) {
if (input->active_view == view) {
return;
}
@@ -133,15 +133,18 @@ static void set_view_focus(struct roots_input *input,
if (!view) {
return;
}
+ input->last_active_view = view;
size_t index = 0;
for (size_t i = 0; i < desktop->views->length; ++i) {
struct roots_view *_view = desktop->views->items[i];
- view_activate(_view, _view == view);
- if (view == _view) {
+ if (_view != view) {
+ view_activate(_view, false);
+ } else {
index = i;
}
}
+ view_activate(view, true);
// TODO: list_swap
list_del(desktop->views, index);
list_add(desktop->views, view);
@@ -152,7 +155,7 @@ static void handle_cursor_motion(struct wl_listener *listener, void *data) {
struct wlr_event_pointer_motion *event = data;
wlr_cursor_move(input->cursor, event->device,
event->delta_x, event->delta_y);
- cursor_update_position(input, (uint32_t)event->time_usec);
+ cursor_update_position(input, (uint32_t)(event->time_usec / 1000));
}
static void handle_cursor_motion_absolute(struct wl_listener *listener,
@@ -162,14 +165,14 @@ static void handle_cursor_motion_absolute(struct wl_listener *listener,
struct wlr_event_pointer_motion_absolute *event = data;
wlr_cursor_warp_absolute(input->cursor, event->device,
event->x_mm / event->width_mm, event->y_mm / event->height_mm);
- cursor_update_position(input, (uint32_t)event->time_usec);
+ cursor_update_position(input, (uint32_t)(event->time_usec / 1000));
}
static void handle_cursor_axis(struct wl_listener *listener, void *data) {
struct roots_input *input =
wl_container_of(listener, input, cursor_axis);
struct wlr_event_pointer_axis *event = data;
- wlr_seat_pointer_send_axis(input->wl_seat, event->time_sec,
+ wlr_seat_pointer_notify_axis(input->wl_seat, event->time_sec,
event->orientation, event->delta);
}
@@ -217,7 +220,7 @@ static void do_cursor_button_press(struct roots_input *input,
return;
}
- uint32_t serial = wlr_seat_pointer_send_button(input->wl_seat, time, button,
+ uint32_t serial = wlr_seat_pointer_notify_button(input->wl_seat, time, button,
state);
int i;
@@ -234,7 +237,7 @@ static void do_cursor_button_press(struct roots_input *input,
% (sizeof(input->input_events) / sizeof(input->input_events[0]));
set_view_focus(input, desktop, view);
if (view) {
- wlr_seat_keyboard_enter(input->wl_seat, surface);
+ wlr_seat_keyboard_notify_enter(input->wl_seat, surface);
}
break;
}
@@ -244,7 +247,7 @@ static void handle_cursor_button(struct wl_listener *listener, void *data) {
struct roots_input *input = wl_container_of(listener, input, cursor_button);
struct wlr_event_pointer_button *event = data;
do_cursor_button_press(input, input->cursor, event->device,
- (uint32_t)event->time_usec, event->button, event->state);
+ (uint32_t)(event->time_usec / 1000), event->button, event->state);
}
static void handle_tool_axis(struct wl_listener *listener, void *data) {
@@ -254,7 +257,7 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) {
(event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) {
wlr_cursor_warp_absolute(input->cursor, event->device,
event->x_mm / event->width_mm, event->y_mm / event->height_mm);
- cursor_update_position(input, (uint32_t)event->time_usec);
+ cursor_update_position(input, (uint32_t)(event->time_usec / 1000));
}
}
@@ -262,7 +265,13 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) {
struct roots_input *input = wl_container_of(listener, input, cursor_tool_tip);
struct wlr_event_tablet_tool_tip *event = data;
do_cursor_button_press(input, input->cursor, event->device,
- (uint32_t)event->time_usec, BTN_LEFT, event->state);
+ (uint32_t)(event->time_usec / 1000), BTN_LEFT, event->state);
+}
+
+static void handle_pointer_grab_end(struct wl_listener *listener, void *data) {
+ struct roots_input *input =
+ wl_container_of(listener, input, pointer_grab_end);
+ cursor_update_position(input, 0);
}
void cursor_initialize(struct roots_input *input) {
@@ -292,6 +301,9 @@ void cursor_initialize(struct roots_input *input) {
wl_list_init(&input->cursor_tool_tip.link);
wl_signal_add(&cursor->events.tablet_tool_tip, &input->cursor_tool_tip);
input->cursor_tool_tip.notify = handle_tool_tip;
+
+ wl_signal_add(&input->wl_seat->events.pointer_grab_end, &input->pointer_grab_end);
+ input->pointer_grab_end.notify = handle_pointer_grab_end;
}
static void reset_device_mappings(struct roots_config *config,
diff --git a/rootston/desktop.c b/rootston/desktop.c
index f0f8320d..001cefe0 100644
--- a/rootston/desktop.c
+++ b/rootston/desktop.c
@@ -16,6 +16,16 @@
void view_destroy(struct roots_view *view) {
struct roots_desktop *desktop = view->desktop;
+
+ struct roots_input *input = desktop->server->input;
+ if (input->active_view == view) {
+ input->active_view = NULL;
+ input->mode = ROOTS_CURSOR_PASSTHROUGH;
+ }
+ if (input->last_active_view == view) {
+ input->last_active_view = NULL;
+ }
+
for (size_t i = 0; i < desktop->views->length; ++i) {
struct roots_view *_view = desktop->views->items[i];
if (view == _view) {
@@ -36,16 +46,6 @@ void view_get_size(struct roots_view *view, struct wlr_box *box) {
box->height = view->wlr_surface->current->height;
}
-void view_get_input_bounds(struct roots_view *view, struct wlr_box *box) {
- if (view->get_input_bounds) {
- view->get_input_bounds(view, box);
- return;
- }
- box->x = box->y = 0;
- box->width = view->wlr_surface->current->width;
- box->height = view->wlr_surface->current->height;
-}
-
void view_activate(struct roots_view *view, bool activate) {
if (view->activate) {
view->activate(view, activate);
@@ -58,6 +58,46 @@ void view_resize(struct roots_view *view, uint32_t width, uint32_t height) {
}
}
+void view_close(struct roots_view *view) {
+ if (view->close) {
+ view->close(view);
+ }
+}
+
+bool view_center(struct roots_view *view) {
+ struct wlr_box size;
+ view_get_size(view, &size);
+ if (size.width == 0 && size.height == 0) {
+ return false;
+ }
+
+ struct roots_desktop *desktop = view->desktop;
+ struct wlr_cursor *cursor = desktop->server->input->cursor;
+ struct wlr_output *output = wlr_output_layout_output_at(desktop->layout,
+ cursor->x, cursor->y);
+ const struct wlr_output_layout_output *output_layout =
+ wlr_output_layout_get(desktop->layout, output);
+ if (!output) {
+ return false;
+ }
+
+ view->x = (double)(output->width - size.width) / 2
+ + output_layout->x;
+ view->y = (double)(output->height - size.height) / 2
+ + output_layout->y;
+ return true;
+}
+
+bool view_initialize(struct roots_view *view) {
+ bool centered = view_center(view);
+ if (centered) {
+ struct roots_input *input = view->desktop->server->input;
+ set_view_focus(input, view->desktop, view);
+ wlr_seat_keyboard_notify_enter(input->wl_seat, view->wlr_surface);
+ }
+ return centered;
+}
+
static struct wlr_subsurface *subsurface_at(struct wlr_surface *surface,
double sx, double sy, double *sub_x, double *sub_y) {
struct wlr_subsurface *subsurface;
@@ -68,17 +108,59 @@ static struct wlr_subsurface *subsurface_at(struct wlr_surface *surface,
subsurface_at(subsurface->surface, _sub_x + sx, _sub_y + sy,
sub_x, sub_y);
if (sub) {
- // TODO: convert sub_x and sub_y to the parent coordinate system
+ // TODO: This won't work for nested subsurfaces. Convert sub_x and
+ // sub_y to the parent coordinate system
return sub;
}
int sub_width = subsurface->surface->current->buffer_width;
int sub_height = subsurface->surface->current->buffer_height;
if ((sx > _sub_x && sx < _sub_x + sub_width) &&
- (sy > _sub_y && sub_y < sub_y + sub_height)) {
- *sub_x = _sub_x;
- *sub_y = _sub_y;
- return subsurface;
+ (sy > _sub_y && sy < _sub_y + sub_height)) {
+ if (pixman_region32_contains_point(
+ &subsurface->surface->current->input,
+ sx - _sub_x, sy - _sub_y, NULL)) {
+ *sub_x = _sub_x;
+ *sub_y = _sub_y;
+ return subsurface;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static struct wlr_xdg_surface_v6 *xdg_v6_popup_at(
+ struct wlr_xdg_surface_v6 *surface, double sx, double sy,
+ double *popup_sx, double *popup_sy) {
+ // XXX: I think this is so complicated because we're mixing geometry
+ // coordinates with surface coordinates. Input handling should only deal
+ // with surface coordinates.
+ struct wlr_xdg_surface_v6 *popup;
+ wl_list_for_each(popup, &surface->popups, popup_link) {
+ double _popup_sx = surface->geometry->x + popup->popup_state->geometry.x;
+ double _popup_sy = surface->geometry->y + popup->popup_state->geometry.y;
+ int popup_width = popup->popup_state->geometry.width;
+ int popup_height = popup->popup_state->geometry.height;
+
+ struct wlr_xdg_surface_v6 *_popup =
+ xdg_v6_popup_at(popup, sx - _popup_sx + popup->geometry->x,
+ sy - _popup_sy + popup->geometry->y, popup_sx, popup_sy);
+ if (_popup) {
+ *popup_sx = *popup_sx + _popup_sx - popup->geometry->x;
+ *popup_sy = *popup_sy + _popup_sy - popup->geometry->y;
+ return _popup;
+ }
+
+ if ((sx > _popup_sx && sx < _popup_sx + popup_width) &&
+ (sy > _popup_sy && sy < _popup_sy + popup_height)) {
+ if (pixman_region32_contains_point(&popup->surface->current->input,
+ sx - _popup_sx + popup->geometry->x,
+ sy - _popup_sy + popup->geometry->y, NULL)) {
+ *popup_sx = _popup_sx - popup->geometry->x;
+ *popup_sy = _popup_sy - popup->geometry->y;
+ return popup;
+ }
}
}
@@ -93,8 +175,12 @@ struct roots_view *view_at(struct roots_desktop *desktop, double lx, double ly,
double view_sx = lx - view->x;
double view_sy = ly - view->y;
- struct wlr_box box;
- view_get_input_bounds(view, &box);
+ struct wlr_box box = {
+ .x = 0,
+ .y = 0,
+ .width = view->wlr_surface->current->buffer_width,
+ .height = view->wlr_surface->current->buffer_height,
+ };
if (view->rotation != 0.0) {
// Coordinates relative to the center of the view
double ox = view_sx - (double)box.width/2,
@@ -106,6 +192,21 @@ struct roots_view *view_at(struct roots_desktop *desktop, double lx, double ly,
view_sy = ry + (double)box.height/2;
}
+ if (view->type == ROOTS_XDG_SHELL_V6_VIEW) {
+ // TODO: test if this works with rotated views
+ double popup_sx, popup_sy;
+ struct wlr_xdg_surface_v6 *popup =
+ xdg_v6_popup_at(view->xdg_surface_v6, view_sx, view_sy,
+ &popup_sx, &popup_sy);
+
+ if (popup) {
+ *sx = view_sx - popup_sx;
+ *sy = view_sy - popup_sy;
+ *surface = popup->surface;
+ return view;
+ }
+ }
+
double sub_x, sub_y;
struct wlr_subsurface *subsurface =
subsurface_at(view->wlr_surface, view_sx, view_sy, &sub_x, &sub_y);
@@ -116,7 +217,10 @@ struct roots_view *view_at(struct roots_desktop *desktop, double lx, double ly,
return view;
}
- if (wlr_box_contains_point(&box, view_sx, view_sy)) {
+ if (wlr_box_contains_point(&box, view_sx, view_sy) &&
+ pixman_region32_contains_point(
+ &view->wlr_surface->current->input,
+ view_sx, view_sy, NULL)) {
*sx = view_sx;
*sy = view_sy;
*surface = view->wlr_surface;
@@ -138,16 +242,15 @@ struct roots_desktop *desktop_create(struct roots_server *server,
wl_list_init(&desktop->output_remove.link);
desktop->output_remove.notify = output_remove_notify;
- wl_signal_add(&server->backend->events.output_add,
- &desktop->output_add);
+ wl_signal_add(&server->backend->events.output_add, &desktop->output_add);
wl_signal_add(&server->backend->events.output_remove,
- &desktop->output_remove);
+ &desktop->output_remove);
desktop->server = server;
desktop->config = config;
desktop->layout = wlr_output_layout_create();
- desktop->compositor = wlr_compositor_create(
- server->wl_display, server->renderer);
+ desktop->compositor = wlr_compositor_create(server->wl_display,
+ server->renderer);
desktop->xdg_shell_v6 = wlr_xdg_shell_v6_create(server->wl_display);
wl_signal_add(&desktop->xdg_shell_v6->events.new_surface,
@@ -170,7 +273,9 @@ struct roots_desktop *desktop_create(struct roots_server *server,
#endif
desktop->gamma_control_manager = wlr_gamma_control_manager_create(
- server->wl_display);
+ server->wl_display);
+ desktop->screenshooter = wlr_screenshooter_create(server->wl_display,
+ server->renderer);
return desktop;
}
diff --git a/rootston/keyboard.c b/rootston/keyboard.c
index 6f6fa0e8..6f4334af 100644
--- a/rootston/keyboard.c
+++ b/rootston/keyboard.c
@@ -21,19 +21,28 @@ static ssize_t keyboard_pressed_keysym_index(struct roots_keyboard *keyboard,
return -1;
}
+static const char *exec_prefix = "exec ";
+
static void keyboard_binding_execute(struct roots_keyboard *keyboard,
- char *command) {
+ const char *command) {
struct roots_server *server = keyboard->input->server;
if (strcmp(command, "exit") == 0) {
wl_display_terminate(server->wl_display);
- } else {
+ } else if (strcmp(command, "close") == 0) {
+ if (keyboard->input->last_active_view != NULL) {
+ view_close(keyboard->input->last_active_view);
+ }
+ } else if (strncmp(exec_prefix, command, strlen(exec_prefix)) == 0) {
+ const char *shell_cmd = command + strlen(exec_prefix);
pid_t pid = fork();
if (pid < 0) {
wlr_log(L_ERROR, "cannot execute binding command: fork() failed");
return;
} else if (pid == 0) {
- execl("/bin/sh", "/bin/sh", "-c", command, (void *)NULL);
+ execl("/bin/sh", "/bin/sh", "-c", shell_cmd, (void *)NULL);
}
+ } else {
+ wlr_log(L_ERROR, "unknown binding command: %s", command);
}
}
diff --git a/rootston/output.c b/rootston/output.c
index d7aade3d..6c7fbf51 100644
--- a/rootston/output.c
+++ b/rootston/output.c
@@ -78,10 +78,34 @@ static void render_surface(struct wlr_surface *surface,
}
}
+static void render_xdg_v6_popups(struct wlr_xdg_surface_v6 *surface,
+ struct roots_desktop *desktop, struct wlr_output *wlr_output,
+ struct timespec *when, double base_x, double base_y, float rotation) {
+ // TODO: make sure this works with view rotation
+ struct wlr_xdg_surface_v6 *popup;
+ wl_list_for_each(popup, &surface->popups, popup_link) {
+ if (!popup->configured) {
+ continue;
+ }
+
+ double popup_x = base_x + surface->geometry->x +
+ popup->popup_state->geometry.x - popup->geometry->x;
+ double popup_y = base_y + surface->geometry->y +
+ popup->popup_state->geometry.y - popup->geometry->y;
+ render_surface(popup->surface, desktop, wlr_output, when, popup_x,
+ popup_y, rotation);
+ render_xdg_v6_popups(popup, desktop, wlr_output, when, popup_x, popup_y, rotation);
+ }
+}
+
static void render_view(struct roots_view *view, struct roots_desktop *desktop,
struct wlr_output *wlr_output, struct timespec *when) {
render_surface(view->wlr_surface, desktop, wlr_output, when,
view->x, view->y, view->rotation);
+ if (view->type == ROOTS_XDG_SHELL_V6_VIEW) {
+ render_xdg_v6_popups(view->xdg_surface_v6, desktop, wlr_output,
+ when, view->x, view->y, view->rotation);
+ }
}
static void output_frame_notify(struct wl_listener *listener, void *data) {
@@ -145,7 +169,8 @@ void output_add_notify(struct wl_listener *listener, void *data) {
// TODO the cursor must be set depending on which surface it is displayed
// over which should happen in the compositor.
if (!wlr_output_set_cursor(wlr_output, image->buffer,
- image->width, image->width, image->height)) {
+ image->width, image->width, image->height,
+ image->hotspot_x, image->hotspot_y)) {
wlr_log(L_DEBUG, "Failed to set hardware cursor");
return;
}
diff --git a/rootston/rootston.ini.example b/rootston/rootston.ini.example
index cc7d8baa..460efa13 100644
--- a/rootston/rootston.ini.example
+++ b/rootston/rootston.ini.example
@@ -34,6 +34,7 @@ meta-key = Logo
# Keybindings
# Maps key combinations with commands to execute
-# The special command "exit" stops the compositor
+# Use the prefix "exec" to execute a shell command
[bindings]
-Logo+q = exit
+Logo+Shift+e = exit # Stop the compositor
+Logo+q = close # Close the current view
diff --git a/rootston/wl_shell.c b/rootston/wl_shell.c
index 1991d332..009a8c06 100644
--- a/rootston/wl_shell.c
+++ b/rootston/wl_shell.c
@@ -17,6 +17,12 @@ static void resize(struct roots_view *view, uint32_t width, uint32_t height) {
height);
}
+static void close(struct roots_view *view) {
+ assert(view->type == ROOTS_WL_SHELL_VIEW);
+ struct wlr_wl_shell_surface *surf = view->wl_shell_surface;
+ wl_client_destroy(surf->client);
+}
+
static void handle_request_move(struct wl_listener *listener, void *data) {
struct roots_wl_shell_surface *roots_surface =
wl_container_of(listener, roots_surface, request_move);
@@ -43,6 +49,17 @@ static void handle_request_resize(struct wl_listener *listener, void *data) {
view_begin_resize(input, event->cursor, view, e->edges);
}
+static void handle_surface_commit(struct wl_listener *listener, void *data) {
+ struct roots_wl_shell_surface *roots_surface =
+ wl_container_of(listener, roots_surface, surface_commit);
+ struct roots_view *view = roots_surface->view;
+
+ if (view->wl_shell_surface->state == WLR_WL_SHELL_SURFACE_STATE_TOPLEVEL &&
+ !roots_surface->initialized) {
+ roots_surface->initialized = view_initialize(view);
+ }
+}
+
static void handle_destroy(struct wl_listener *listener, void *data) {
struct roots_wl_shell_surface *roots_surface =
wl_container_of(listener, roots_surface, destroy);
@@ -67,7 +84,9 @@ void handle_wl_shell_surface(struct wl_listener *listener, void *data) {
struct roots_wl_shell_surface *roots_surface =
calloc(1, sizeof(struct roots_wl_shell_surface));
- // TODO: all of the trimmings
+ if (!roots_surface) {
+ return;
+ }
wl_list_init(&roots_surface->destroy.link);
roots_surface->destroy.notify = handle_destroy;
wl_signal_add(&surface->events.destroy, &roots_surface->destroy);
@@ -77,9 +96,14 @@ void handle_wl_shell_surface(struct wl_listener *listener, void *data) {
wl_signal_add(&surface->events.request_move, &roots_surface->request_move);
wl_list_init(&roots_surface->request_resize.link);
roots_surface->request_resize.notify = handle_request_resize;
- wl_signal_add(&surface->events.request_resize, &roots_surface->request_resize);
+ wl_signal_add(&surface->events.request_resize,
+ &roots_surface->request_resize);
wl_list_init(&roots_surface->request_set_fullscreen.link);
wl_list_init(&roots_surface->request_set_maximized.link);
+ wl_list_init(&roots_surface->surface_commit.link);
+ roots_surface->surface_commit.notify = handle_surface_commit;
+ wl_signal_add(&surface->surface->events.commit,
+ &roots_surface->surface_commit);
struct roots_view *view = calloc(1, sizeof(struct roots_view));
view->type = ROOTS_WL_SHELL_VIEW;
@@ -88,6 +112,7 @@ void handle_wl_shell_surface(struct wl_listener *listener, void *data) {
view->roots_wl_shell_surface = roots_surface;
view->wlr_surface = surface->surface;
view->resize = resize;
+ view->close = close;
view->desktop = desktop;
roots_surface->view = view;
list_add(desktop->views, view);
diff --git a/rootston/xdg_shell_v6.c b/rootston/xdg_shell_v6.c
index ab34f52a..9b8d8882 100644
--- a/rootston/xdg_shell_v6.c
+++ b/rootston/xdg_shell_v6.c
@@ -33,6 +33,14 @@ static void resize(struct roots_view *view, uint32_t width, uint32_t height) {
}
}
+static void close(struct roots_view *view) {
+ assert(view->type == ROOTS_XDG_SHELL_V6_VIEW);
+ struct wlr_xdg_surface_v6 *surf = view->xdg_surface_v6;
+ if (surf->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) {
+ wlr_xdg_toplevel_v6_send_close(surf);
+ }
+}
+
static void handle_request_move(struct wl_listener *listener, void *data) {
struct roots_xdg_surface_v6 *roots_xdg_surface =
wl_container_of(listener, roots_xdg_surface, request_move);
@@ -59,6 +67,17 @@ static void handle_request_resize(struct wl_listener *listener, void *data) {
view_begin_resize(input, event->cursor, view, e->edges);
}
+static void handle_commit(struct wl_listener *listener, void *data) {
+ struct roots_xdg_surface_v6 *roots_xdg_surface =
+ wl_container_of(listener, roots_xdg_surface, commit);
+ struct roots_view *view = roots_xdg_surface->view;
+
+ if (view->xdg_surface_v6->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL &&
+ !roots_xdg_surface->initialized) {
+ roots_xdg_surface->initialized = view_initialize(view);
+ }
+}
+
static void handle_destroy(struct wl_listener *listener, void *data) {
struct roots_xdg_surface_v6 *roots_xdg_surface =
wl_container_of(listener, roots_xdg_surface, destroy);
@@ -73,17 +92,29 @@ static void handle_destroy(struct wl_listener *listener, void *data) {
}
void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) {
+ struct wlr_xdg_surface_v6 *surface = data;
+ assert(surface->role != WLR_XDG_SURFACE_V6_ROLE_NONE);
+
+ if (surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) {
+ wlr_log(L_DEBUG, "new xdg popup");
+ return;
+ }
+
struct roots_desktop *desktop =
wl_container_of(listener, desktop, xdg_shell_v6_surface);
- struct wlr_xdg_surface_v6 *surface = data;
- wlr_log(L_DEBUG, "new xdg surface: title=%s, app_id=%s",
+ wlr_log(L_DEBUG, "new xdg toplevel: title=%s, app_id=%s",
surface->title, surface->app_id);
wlr_xdg_surface_v6_ping(surface);
struct roots_xdg_surface_v6 *roots_surface =
calloc(1, sizeof(struct roots_xdg_surface_v6));
- // TODO: all of the trimmings
+ if (!roots_surface) {
+ return;
+ }
+ wl_list_init(&roots_surface->commit.link);
+ roots_surface->commit.notify = handle_commit;
+ wl_signal_add(&surface->events.commit, &roots_surface->commit);
wl_list_init(&roots_surface->destroy.link);
roots_surface->destroy.notify = handle_destroy;
wl_signal_add(&surface->events.destroy, &roots_surface->destroy);
@@ -100,13 +131,13 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) {
struct roots_view *view = calloc(1, sizeof(struct roots_view));
view->type = ROOTS_XDG_SHELL_V6_VIEW;
- view->x = view->y = 200;
view->xdg_surface_v6 = surface;
view->roots_xdg_surface_v6 = roots_surface;
view->wlr_surface = surface->surface;
view->get_size = get_size;
view->activate = activate;
view->resize = resize;
+ view->close = close;
view->desktop = desktop;
roots_surface->view = view;
list_add(desktop->views, view);
diff --git a/rootston/xwayland.c b/rootston/xwayland.c
index 7ecc4d4f..c32ee15b 100644
--- a/rootston/xwayland.c
+++ b/rootston/xwayland.c
@@ -9,6 +9,16 @@
#include "rootston/desktop.h"
#include "rootston/server.h"
+static void activate(struct roots_view *view, bool active) {
+ assert(view->type == ROOTS_XWAYLAND_VIEW);
+ if (active) {
+ wlr_xwayland_surface_activate(view->desktop->xwayland,
+ view->xwayland_surface);
+ } else {
+ wlr_xwayland_surface_activate(view->desktop->xwayland, NULL);
+ }
+}
+
static void resize(struct roots_view *view, uint32_t width, uint32_t height) {
assert(view->type == ROOTS_XWAYLAND_VIEW);
struct wlr_xwayland_surface *xwayland_surface = view->xwayland_surface;
@@ -16,6 +26,11 @@ static void resize(struct roots_view *view, uint32_t width, uint32_t height) {
xwayland_surface->x, xwayland_surface->y, width, height);
}
+static void close(struct roots_view *view) {
+ assert(view->type == ROOTS_XWAYLAND_VIEW);
+ wlr_xwayland_surface_close(view->desktop->xwayland, view->xwayland_surface);
+}
+
static void handle_destroy(struct wl_listener *listener, void *data) {
struct roots_xwayland_surface *roots_surface =
wl_container_of(listener, roots_surface, destroy);
@@ -38,11 +53,6 @@ static void handle_request_configure(struct wl_listener *listener, void *data) {
xwayland_surface, event->x, event->y, event->width, event->height);
}
-static void activate(struct roots_view *view, bool active) {
- wlr_xwayland_surface_activate(view->desktop->xwayland,
- view->xwayland_surface);
-}
-
void handle_xwayland_surface(struct wl_listener *listener, void *data) {
struct roots_desktop *desktop =
wl_container_of(listener, desktop, xwayland_surface);
@@ -78,6 +88,7 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) {
view->desktop = desktop;
view->activate = activate;
view->resize = resize;
+ view->close = close;
roots_surface->view = view;
list_add(desktop->views, view);
}
diff --git a/types/meson.build b/types/meson.build
index cff40150..a151e8a3 100644
--- a/types/meson.build
+++ b/types/meson.build
@@ -20,6 +20,7 @@ lib_wlr_types = static_library(
'wlr_compositor.c',
'wlr_box.c',
'wlr_gamma_control.c',
+ 'wlr_screenshooter.c',
),
include_directories: wlr_inc,
dependencies: [wayland_server, pixman, wlr_protos],
diff --git a/types/wlr_cursor.c b/types/wlr_cursor.c
index 79972745..20781f0a 100644
--- a/types/wlr_cursor.c
+++ b/types/wlr_cursor.c
@@ -131,15 +131,6 @@ static struct wlr_cursor_device *get_cursor_device(struct wlr_cursor *cur,
static void wlr_cursor_warp_unchecked(struct wlr_cursor *cur,
double x, double y) {
assert(cur->state->layout);
- int hotspot_x = 0;
- int hotspot_y = 0;
-
- if (cur->state->xcursor && cur->state->xcursor->image_count > 0) {
- struct wlr_xcursor_image *image = cur->state->xcursor->images[0];
- hotspot_x = image->hotspot_x;
- hotspot_y = image->hotspot_y;
- }
-
struct wlr_output_layout_output *l_output;
wl_list_for_each(l_output, &cur->state->layout->outputs, link) {
@@ -148,8 +139,9 @@ static void wlr_cursor_warp_unchecked(struct wlr_cursor *cur,
wlr_output_layout_output_coords(cur->state->layout,
l_output->output, &output_x, &output_y);
- wlr_output_move_cursor(l_output->output, output_x - hotspot_x,
- output_y - hotspot_y);
+ wlr_output_move_cursor(l_output->output,
+ output_x - l_output->output->cursor.hotspot_x,
+ output_y - l_output->output->cursor.hotspot_y);
}
cur->x = x;
diff --git a/types/wlr_data_device_manager.c b/types/wlr_data_device_manager.c
index fb3bd901..a813754c 100644
--- a/types/wlr_data_device_manager.c
+++ b/types/wlr_data_device_manager.c
@@ -7,15 +7,16 @@
#include <wlr/types/wlr_data_source.h>
#include <wlr/types/wlr_seat.h>
-static void resource_destroy(struct wl_client *client, struct wl_resource *resource) {
+static void resource_destroy(struct wl_client *client,
+ struct wl_resource *resource) {
wl_resource_destroy(resource);
}
-static void wl_cb_data_device_start_drag(struct wl_client *client,
- struct wl_resource *res, struct wl_resource *src_res,
+static void data_device_start_drag(struct wl_client *client,
+ struct wl_resource *res, struct wl_resource *source_resource,
struct wl_resource *origin_res, struct wl_resource *icon_res,
uint32_t serial) {
- wlr_log(L_DEBUG, "implement data_device:start_drag");
+ wlr_log(L_DEBUG, "TODO: implement data_device:start_drag");
// Will probably look like this:
// struct wlr_seat_handle *handle = wl_resource_get_user_data(res);
@@ -29,68 +30,79 @@ static void wl_cb_data_device_start_drag(struct wl_client *client,
// origin, icon); // will set surface roles and emit signal for user
}
-static void wl_cb_data_device_set_selection(struct wl_client *client,
- struct wl_resource *res, struct wl_resource *src_res,
+static void data_device_set_selection(struct wl_client *client,
+ struct wl_resource *resource, struct wl_resource *source_resource,
uint32_t serial) {
+ if (!source_resource) {
+ return;
+ }
+
// TODO: serial validation
- struct wlr_seat_handle *handle = wl_resource_get_user_data(res);
+ struct wlr_seat_handle *handle = wl_resource_get_user_data(resource);
struct wlr_data_device *device = handle->wlr_seat->data_device;
- struct wlr_data_source *src = wl_resource_get_user_data(src_res);
- wlr_data_device_set_selection(device, src);
+ struct wlr_data_source *source = wl_resource_get_user_data(source_resource);
+ wlr_data_device_set_selection(device, source);
}
static struct wl_data_device_interface data_device_impl = {
- .start_drag = wl_cb_data_device_start_drag,
- .set_selection = wl_cb_data_device_set_selection,
+ .start_drag = data_device_start_drag,
+ .set_selection = data_device_set_selection,
.release = resource_destroy
};
-static void data_device_selection_destroy(struct wl_listener *listener, void *data) {
- struct wlr_data_device *device = wl_container_of(listener, device, selection_destroyed);
+static void data_device_selection_destroy(struct wl_listener *listener,
+ void *data) {
+ struct wlr_data_device *device =
+ wl_container_of(listener, device, selection_destroyed);
assert(data == device->selection);
device->selection = NULL; // make sure no cancel is sent
wlr_data_device_set_selection(device, NULL);
}
-static struct wlr_data_device *seat_ensure_data_device(struct wlr_data_device_manager *manager,
- struct wlr_seat *seat) {
+static struct wlr_data_device *seat_ensure_data_device(
+ struct wlr_data_device_manager *manager, struct wlr_seat *seat) {
if (seat->data_device) {
return seat->data_device;
}
- if (!(seat->data_device = calloc(1, sizeof(*seat->data_device)))) {
+ seat->data_device = calloc(1, sizeof(*seat->data_device));
+ if (!seat->data_device) {
wlr_log(L_ERROR, "Failed to allocate wlr_data_device");
return NULL;
}
seat->data_device->seat = seat;
wl_signal_init(&seat->data_device->events.selection_change);
- seat->data_device->selection_destroyed.notify = data_device_selection_destroy;
+ seat->data_device->selection_destroyed.notify =
+ data_device_selection_destroy;
return seat->data_device;
}
-static void data_device_destroy(struct wl_resource *res) {
- struct wlr_seat_handle *handle = wl_resource_get_user_data(res);
+static void data_device_destroy(struct wl_resource *resource) {
+ struct wlr_seat_handle *handle = wl_resource_get_user_data(resource);
handle->data_device = NULL;
}
static void data_device_manager_create_data_source(struct wl_client *client,
- struct wl_resource *res, uint32_t id) {
- uint32_t version = wl_resource_get_version(res);
+ struct wl_resource *resource, uint32_t id) {
+ uint32_t version = wl_resource_get_version(resource);
if (!wlr_wl_data_source_create(client, version, id)) {
wlr_log(L_ERROR, "Failed to create wlr_wl_data_source");
- wl_resource_post_no_memory(res);
+ wl_resource_post_no_memory(resource);
return;
}
}
static void data_device_manager_get_data_device(struct wl_client *client,
- struct wl_resource *res, uint32_t id, struct wl_resource *seat_res) {
- struct wlr_data_device_manager *manager = wl_resource_get_user_data(res);
- struct wlr_seat_handle *seat_handle = wl_resource_get_user_data(seat_res);
+ struct wl_resource *resource, uint32_t id,
+ struct wl_resource *seat_resource) {
+ struct wlr_data_device_manager *manager =
+ wl_resource_get_user_data(resource);
+ struct wlr_seat_handle *seat_handle =
+ wl_resource_get_user_data(seat_resource);
struct wlr_data_device *device;
if (!(device = seat_ensure_data_device(manager, seat_handle->wlr_seat))) {
- wl_resource_post_no_memory(res);
+ wl_resource_post_no_memory(resource);
return;
}
@@ -101,10 +113,10 @@ static void data_device_manager_get_data_device(struct wl_client *client,
}
seat_handle->data_device = wl_resource_create(client,
- &wl_data_device_interface, wl_resource_get_version(res), id);
+ &wl_data_device_interface, wl_resource_get_version(resource), id);
if (!seat_handle->data_device) {
wlr_log(L_ERROR, "Failed to create wl_data_device resource");
- wl_resource_post_no_memory(res);
+ wl_resource_post_no_memory(resource);
return;
}
@@ -135,18 +147,21 @@ static void data_device_manager_bind(struct wl_client *client, void *data,
return;
}
wl_resource_set_implementation(resource, &data_device_manager_impl,
- manager, NULL);
+ manager, NULL);
}
-struct wlr_data_device_manager *wlr_data_device_manager_create(struct wl_display *dpy) {
+struct wlr_data_device_manager *wlr_data_device_manager_create(
+ struct wl_display *display) {
struct wlr_data_device_manager *manager = calloc(1, sizeof(*manager));
if (!manager) {
wlr_log(L_ERROR, "Failed to allocated wlr_data_device_manager");
return NULL;
}
- manager->global = wl_global_create(dpy, &wl_data_device_manager_interface, 3,
- manager, data_device_manager_bind);
+ manager->global =
+ wl_global_create(display, &wl_data_device_manager_interface, 3, manager,
+ data_device_manager_bind);
+
if (!manager->global) {
wlr_log(L_ERROR, "Failed to create global for wlr_data_device_manager");
free(manager);
@@ -165,6 +180,10 @@ void wlr_data_device_manager_destroy(struct wlr_data_device_manager *manager) {
void wlr_data_device_set_selection(struct wlr_data_device *device,
struct wlr_data_source *source) {
+ if (device->selection == source) {
+ return;
+ }
+
if (device->selection) {
wl_list_remove(&device->selection_destroyed.link);
wlr_data_source_cancelled(device->selection);
diff --git a/types/wlr_data_source.c b/types/wlr_data_source.c
index 542b1135..83064fac 100644
--- a/types/wlr_data_source.c
+++ b/types/wlr_data_source.c
@@ -27,7 +27,8 @@ void wlr_data_source_finish(struct wlr_data_source *source) {
}
}
-void wlr_data_source_send(struct wlr_data_source *src, const char *type, int fd) {
+void wlr_data_source_send(struct wlr_data_source *src, const char *type,
+ int fd) {
assert(src && src->impl && src->impl->send);
src->impl->send(src, type, fd);
}
@@ -53,7 +54,8 @@ static void data_source_send(struct wlr_data_source *src,
close(fd);
}
-static void data_source_accepted(struct wlr_data_source *src, const char *type) {
+static void data_source_accepted(struct wlr_data_source *src,
+ const char *type) {
struct wlr_wl_data_source *wl_src = (struct wlr_wl_data_source *) src;
wl_data_source_send_target(wl_src->resource, type);
}
@@ -69,7 +71,8 @@ static struct wlr_data_source_impl data_source_wl_impl = {
.cancelled = data_source_cancelled,
};
-static void data_source_offer(struct wl_client *client, struct wl_resource *resource,
+static void data_source_offer(struct wl_client *client,
+ struct wl_resource *resource,
const char *type) {
struct wlr_wl_data_source *src = wl_resource_get_user_data(resource);
char *dtype = strdup(type);
@@ -81,13 +84,20 @@ static void data_source_offer(struct wl_client *client, struct wl_resource *reso
list_add(src->base.types, dtype);
}
-static void data_source_destroy(struct wl_client *client, struct wl_resource *resource) {
+static void data_source_destroy(struct wl_client *client,
+ struct wl_resource *resource) {
wl_resource_destroy(resource);
}
+static void data_source_set_actions(struct wl_client *client,
+ struct wl_resource *resource, uint32_t dnd_actions) {
+ wlr_log(L_DEBUG, "TODO: data source set actions");
+}
+
static struct wl_data_source_interface wl_data_source_impl = {
.offer = data_source_offer,
- .destroy = data_source_destroy
+ .destroy = data_source_destroy,
+ .set_actions = data_source_set_actions,
};
static void destroy_wl_data_source(struct wl_resource *resource) {
diff --git a/types/wlr_keyboard.c b/types/wlr_keyboard.c
index 5dbcf064..f37895b0 100644
--- a/types/wlr_keyboard.c
+++ b/types/wlr_keyboard.c
@@ -45,11 +45,21 @@ static void keyboard_modifier_update(struct wlr_keyboard *keyboard) {
wl_signal_emit(&keyboard->events.modifiers, keyboard);
}
-void wlr_keyboard_update_state(struct wlr_keyboard *keyboard,
+void wlr_keyboard_notify_modifiers(struct wlr_keyboard *keyboard,
+ uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked,
+ uint32_t group) {
+ xkb_state_update_mask(keyboard->xkb_state, mods_depressed, mods_latched,
+ mods_locked, 0, 0, group);
+ keyboard_modifier_update(keyboard);
+}
+
+void wlr_keyboard_notify_key(struct wlr_keyboard *keyboard,
struct wlr_event_keyboard_key *event) {
- uint32_t keycode = event->keycode + 8;
- xkb_state_update_key(keyboard->xkb_state, keycode,
- event->state == WLR_KEY_PRESSED ? XKB_KEY_DOWN : XKB_KEY_UP);
+ if (event->update_state) {
+ uint32_t keycode = event->keycode + 8;
+ xkb_state_update_key(keyboard->xkb_state, keycode,
+ event->state == WLR_KEY_PRESSED ? XKB_KEY_DOWN : XKB_KEY_UP);
+ }
keyboard_led_update(keyboard);
keyboard_modifier_update(keyboard);
wl_signal_emit(&keyboard->events.key, event);
diff --git a/types/wlr_output.c b/types/wlr_output.c
index 962685b8..64f67f2d 100644
--- a/types/wlr_output.c
+++ b/types/wlr_output.c
@@ -19,8 +19,8 @@ static void wl_output_send_to_resource(struct wl_resource *resource) {
const uint32_t version = wl_resource_get_version(resource);
if (version >= WL_OUTPUT_GEOMETRY_SINCE_VERSION) {
wl_output_send_geometry(resource, 0, 0, // TODO: get position from layout?
- output->phys_width, output->phys_height, output->subpixel,
- output->make, output->model, output->transform);
+ output->phys_width, output->phys_height, output->subpixel,
+ output->make, output->model, output->transform);
}
if (version >= WL_OUTPUT_MODE_SINCE_VERSION) {
for (size_t i = 0; i < output->modes->length; ++i) {
@@ -31,7 +31,13 @@ static void wl_output_send_to_resource(struct wl_resource *resource) {
flags |= WL_OUTPUT_MODE_CURRENT;
}
wl_output_send_mode(resource, flags,
- mode->width, mode->height, mode->refresh);
+ mode->width, mode->height, mode->refresh);
+ }
+
+ if (output->modes->length == 0) {
+ // Output has no mode, send the current width/height
+ wl_output_send_mode(resource, WL_OUTPUT_MODE_CURRENT,
+ output->width, output->height, 0);
}
}
if (version >= WL_OUTPUT_SCALE_SINCE_VERSION) {
@@ -99,6 +105,7 @@ void wlr_output_init(struct wlr_output *output,
output->transform = WL_OUTPUT_TRANSFORM_NORMAL;
output->scale = 1;
wl_signal_init(&output->events.frame);
+ wl_signal_init(&output->events.swap_buffers);
wl_signal_init(&output->events.resolution);
wl_signal_init(&output->events.destroy);
}
@@ -125,9 +132,11 @@ void wlr_output_transform(struct wlr_output *output,
}
bool wlr_output_set_cursor(struct wlr_output *output,
- const uint8_t *buf, int32_t stride, uint32_t width, uint32_t height) {
+ const uint8_t *buf, int32_t stride, uint32_t width, uint32_t height,
+ int32_t hotspot_x, int32_t hotspot_y) {
if (output->impl->set_cursor
- && output->impl->set_cursor(output, buf, stride, width, height)) {
+ && output->impl->set_cursor(output, buf, stride, width, height,
+ hotspot_x, hotspot_y)) {
output->cursor.is_sw = false;
return true;
}
@@ -137,6 +146,8 @@ bool wlr_output_set_cursor(struct wlr_output *output,
output->cursor.is_sw = true;
output->cursor.width = width;
output->cursor.height = height;
+ output->cursor.hotspot_x = hotspot_x;
+ output->cursor.hotspot_y = hotspot_y;
if (!output->cursor.renderer) {
/* NULL egl is okay given that we are only using pixel buffers */
@@ -221,6 +232,8 @@ void wlr_output_swap_buffers(struct wlr_output *output) {
wlr_render_with_matrix(output->cursor.renderer, output->cursor.texture, &matrix);
}
+ wl_signal_emit(&output->events.swap_buffers, &output);
+
output->impl->swap_buffers(output);
}
diff --git a/types/wlr_screenshooter.c b/types/wlr_screenshooter.c
new file mode 100644
index 00000000..38204aa1
--- /dev/null
+++ b/types/wlr_screenshooter.c
@@ -0,0 +1,163 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wayland-server.h>
+#include <wlr/render.h>
+#include <wlr/types/wlr_screenshooter.h>
+#include <wlr/types/wlr_output.h>
+#include <wlr/util/log.h>
+#include "screenshooter-protocol.h"
+
+static void copy_yflip(uint8_t *dst, uint8_t *src, int32_t height,
+ int32_t stride) {
+ uint8_t *end = dst + height * stride;
+ while (dst < end) {
+ memcpy(dst, src, stride);
+ dst += stride;
+ src -= stride;
+ }
+}
+
+struct screenshot_state {
+ int32_t width, height, stride;
+ uint8_t *pixels;
+ struct wl_shm_buffer *shm_buffer;
+ struct wlr_screenshot *screenshot;
+ struct wl_listener frame_listener;
+};
+
+static void output_frame_notify(struct wl_listener *listener, void *_data) {
+ struct screenshot_state *state = wl_container_of(listener, state,
+ frame_listener);
+ struct wlr_renderer *renderer = state->screenshot->screenshooter->renderer;
+ struct wlr_output *output = state->screenshot->output;
+
+ wlr_output_make_current(output);
+ wlr_renderer_read_pixels(renderer, 0, 0, output->width, output->height,
+ state->pixels);
+
+ void *data = wl_shm_buffer_get_data(state->shm_buffer);
+ wl_shm_buffer_begin_access(state->shm_buffer);
+ copy_yflip(data, state->pixels + state->stride * (state->height - 1),
+ state->height, state->stride);
+ wl_shm_buffer_end_access(state->shm_buffer);
+
+ free(state->pixels);
+ wl_list_remove(&listener->link);
+
+ orbital_screenshot_send_done(state->screenshot->resource);
+
+ free(state);
+}
+
+static void screenshooter_shoot(struct wl_client *client,
+ struct wl_resource *_screenshooter, uint32_t id,
+ struct wl_resource *_output, struct wl_resource *_buffer) {
+ struct wlr_screenshooter *screenshooter =
+ wl_resource_get_user_data(_screenshooter);
+ struct wlr_output *output = wl_resource_get_user_data(_output);
+ if (!wl_shm_buffer_get(_buffer)) {
+ wlr_log(L_ERROR, "Invalid buffer: not a shared memory buffer");
+ return;
+ }
+ struct wl_shm_buffer *shm_buffer = wl_shm_buffer_get(_buffer);
+ int32_t width = wl_shm_buffer_get_width(shm_buffer);
+ int32_t height = wl_shm_buffer_get_height(shm_buffer);
+ int32_t stride = wl_shm_buffer_get_stride(shm_buffer);
+ if (width < output->width || height < output->height) {
+ wlr_log(L_ERROR, "Invalid buffer: too small");
+ return;
+ }
+
+ uint32_t format = wl_shm_buffer_get_format(shm_buffer);
+ if (format != WL_SHM_FORMAT_XRGB8888) {
+ wlr_log(L_ERROR, "Invalid buffer: unsupported format");
+ return;
+ }
+
+ uint8_t *pixels = malloc(stride * height);
+ if (pixels == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ struct wlr_screenshot *screenshot =
+ calloc(1, sizeof(struct wlr_screenshot));
+ if (!screenshot) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+ screenshot->output_resource = _output;
+ screenshot->output = output;
+ screenshot->screenshooter = screenshooter;
+ screenshot->resource = wl_resource_create(client,
+ &orbital_screenshot_interface, wl_resource_get_version(_screenshooter),
+ id);
+ wlr_log(L_DEBUG, "new screenshot %p (res %p)", screenshot,
+ screenshot->resource);
+ wl_resource_set_implementation(screenshot->resource, NULL, screenshot,
+ NULL);
+
+ struct screenshot_state *state = calloc(1, sizeof(struct screenshot_state));
+ if (!state) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+ state->width = width;
+ state->height = height;
+ state->stride = stride;
+ state->pixels = pixels;
+ state->shm_buffer = shm_buffer;
+ state->screenshot = screenshot;
+ state->frame_listener.notify = output_frame_notify;
+ wl_signal_add(&output->events.swap_buffers, &state->frame_listener);
+}
+
+static struct orbital_screenshooter_interface screenshooter_impl = {
+ .shoot = screenshooter_shoot,
+};
+
+static void screenshooter_bind(struct wl_client *wl_client,
+ void *_screenshooter, uint32_t version, uint32_t id) {
+ struct wlr_screenshooter *screenshooter = _screenshooter;
+ assert(wl_client && screenshooter);
+ if (version > 1) {
+ wlr_log(L_ERROR, "Client requested unsupported screenshooter version,"
+ "disconnecting");
+ wl_client_destroy(wl_client);
+ return;
+ }
+ struct wl_resource *wl_resource = wl_resource_create(wl_client,
+ &orbital_screenshooter_interface, version, id);
+ wl_resource_set_implementation(wl_resource, &screenshooter_impl,
+ screenshooter, NULL);
+}
+
+struct wlr_screenshooter *wlr_screenshooter_create(struct wl_display *display,
+ struct wlr_renderer *renderer) {
+ struct wlr_screenshooter *screenshooter =
+ calloc(1, sizeof(struct wlr_screenshooter));
+ if (!screenshooter) {
+ return NULL;
+ }
+ screenshooter->renderer = renderer;
+
+ struct wl_global *wl_global = wl_global_create(display,
+ &orbital_screenshooter_interface, 1, screenshooter, screenshooter_bind);
+ if (!wl_global) {
+ free(screenshooter);
+ return NULL;
+ }
+ screenshooter->wl_global = wl_global;
+
+ return screenshooter;
+}
+
+void wlr_screenshooter_destroy(struct wlr_screenshooter *screenshooter) {
+ if (!screenshooter) {
+ return;
+ }
+ // TODO: this segfault (wl_display->registry_resource_list is not init)
+ // wl_global_destroy(screenshooter->wl_global);
+ free(screenshooter);
+}
diff --git a/types/wlr_seat.c b/types/wlr_seat.c
index bb24fa15..4566053d 100644
--- a/types/wlr_seat.c
+++ b/types/wlr_seat.c
@@ -173,6 +173,66 @@ static void wl_seat_bind(struct wl_client *wl_client, void *_wlr_seat,
wl_signal_emit(&wlr_seat->events.client_bound, handle);
}
+static void default_pointer_enter(struct wlr_seat_pointer_grab *grab,
+ struct wlr_surface *surface, double sx, double sy) {
+ wlr_seat_pointer_enter(grab->seat, surface, sx, sy);
+}
+
+static void default_pointer_motion(struct wlr_seat_pointer_grab *grab,
+ uint32_t time, double sx, double sy) {
+ wlr_seat_pointer_send_motion(grab->seat, time, sx, sy);
+}
+
+static uint32_t default_pointer_button(struct wlr_seat_pointer_grab *grab,
+ uint32_t time, uint32_t button, uint32_t state) {
+ return wlr_seat_pointer_send_button(grab->seat, time, button, state);
+}
+
+static void default_pointer_axis(struct wlr_seat_pointer_grab *grab,
+ uint32_t time, enum wlr_axis_orientation orientation, double value) {
+ wlr_seat_pointer_send_axis(grab->seat, time, orientation, value);
+}
+
+static void default_pointer_cancel(struct wlr_seat_pointer_grab *grab) {
+ // cannot be cancelled
+}
+
+static const struct wlr_pointer_grab_interface default_pointer_grab_impl = {
+ .enter = default_pointer_enter,
+ .motion = default_pointer_motion,
+ .button = default_pointer_button,
+ .axis = default_pointer_axis,
+ .cancel = default_pointer_cancel,
+};
+
+static void default_keyboard_enter(struct wlr_seat_keyboard_grab *grab,
+ struct wlr_surface *surface) {
+ wlr_seat_keyboard_enter(grab->seat, surface);
+}
+
+static void default_keyboard_key(struct wlr_seat_keyboard_grab *grab,
+ uint32_t time, uint32_t key, uint32_t state) {
+ wlr_seat_keyboard_send_key(grab->seat, time, key, state);
+}
+
+static void default_keyboard_modifiers(struct wlr_seat_keyboard_grab *grab,
+ uint32_t mods_depressed, uint32_t mods_latched,
+ uint32_t mods_locked, uint32_t group) {
+ wlr_seat_keyboard_send_modifiers(grab->seat, mods_depressed,
+ mods_latched, mods_locked, group);
+}
+
+static void default_keyboard_cancel(struct wlr_seat_keyboard_grab *grab) {
+ // cannot be cancelled
+}
+
+static const struct wlr_keyboard_grab_interface default_keyboard_grab_impl = {
+ .enter = default_keyboard_enter,
+ .key = default_keyboard_key,
+ .modifiers = default_keyboard_modifiers,
+ .cancel = default_keyboard_cancel,
+};
+
struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) {
struct wlr_seat *wlr_seat = calloc(1, sizeof(struct wlr_seat));
if (!wlr_seat) {
@@ -183,6 +243,29 @@ struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) {
wl_list_init(&wlr_seat->pointer_state.surface_destroy.link);
wl_list_init(&wlr_seat->pointer_state.resource_destroy.link);
+ struct wlr_seat_pointer_grab *pointer_grab =
+ calloc(1, sizeof(struct wlr_seat_pointer_grab));
+ if (!pointer_grab) {
+ free(wlr_seat);
+ return NULL;
+ }
+ pointer_grab->interface = &default_pointer_grab_impl;
+ pointer_grab->seat = wlr_seat;
+ wlr_seat->pointer_state.default_grab = pointer_grab;
+ wlr_seat->pointer_state.grab = pointer_grab;
+
+ struct wlr_seat_keyboard_grab *keyboard_grab =
+ calloc(1, sizeof(struct wlr_seat_keyboard_grab));
+ if (!keyboard_grab) {
+ free(pointer_grab);
+ free(wlr_seat);
+ return NULL;
+ }
+ keyboard_grab->interface = &default_keyboard_grab_impl;
+ keyboard_grab->seat = wlr_seat;
+ wlr_seat->keyboard_state.default_grab = keyboard_grab;
+ wlr_seat->keyboard_state.grab = keyboard_grab;
+
wlr_seat->keyboard_state.wlr_seat = wlr_seat;
wl_list_init(&wlr_seat->keyboard_state.resource_destroy.link);
wl_list_init(
@@ -203,6 +286,12 @@ struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) {
wl_signal_init(&wlr_seat->events.client_bound);
wl_signal_init(&wlr_seat->events.client_unbound);
+ wl_signal_init(&wlr_seat->events.pointer_grab_begin);
+ wl_signal_init(&wlr_seat->events.pointer_grab_end);
+
+ wl_signal_init(&wlr_seat->events.keyboard_grab_begin);
+ wl_signal_init(&wlr_seat->events.keyboard_grab_end);
+
return wlr_seat;
}
@@ -218,6 +307,8 @@ void wlr_seat_destroy(struct wlr_seat *wlr_seat) {
}
wl_global_destroy(wlr_seat->wl_global);
+ free(wlr_seat->pointer_state.default_grab);
+ free(wlr_seat->keyboard_state.default_grab);
free(wlr_seat->data_device);
free(wlr_seat->name);
free(wlr_seat);
@@ -326,7 +417,7 @@ void wlr_seat_pointer_enter(struct wlr_seat *wlr_seat,
wl_list_remove(&wlr_seat->pointer_state.resource_destroy.link);
wl_list_init(&wlr_seat->pointer_state.resource_destroy.link);
if (surface) {
- wl_signal_add(&surface->signals.destroy,
+ wl_signal_add(&surface->events.destroy,
&wlr_seat->pointer_state.surface_destroy);
wl_resource_add_destroy_listener(surface->resource,
&wlr_seat->pointer_state.resource_destroy);
@@ -390,6 +481,44 @@ void wlr_seat_pointer_send_axis(struct wlr_seat *wlr_seat, uint32_t time,
pointer_send_frame(pointer);
}
+void wlr_seat_pointer_start_grab(struct wlr_seat *wlr_seat,
+ struct wlr_seat_pointer_grab *grab) {
+ grab->seat = wlr_seat;
+ wlr_seat->pointer_state.grab = grab;
+
+ wl_signal_emit(&wlr_seat->events.pointer_grab_begin, grab);
+}
+
+void wlr_seat_pointer_end_grab(struct wlr_seat *wlr_seat) {
+ struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab;
+ wlr_seat->pointer_state.grab = wlr_seat->pointer_state.default_grab;
+ wl_signal_emit(&wlr_seat->events.pointer_grab_end, grab);
+}
+
+void wlr_seat_pointer_notify_enter(struct wlr_seat *wlr_seat,
+ struct wlr_surface *surface, double sx, double sy) {
+ struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab;
+ grab->interface->enter(grab, surface, sx, sy);
+}
+
+void wlr_seat_pointer_notify_motion(struct wlr_seat *wlr_seat, uint32_t time,
+ double sx, double sy) {
+ struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab;
+ grab->interface->motion(grab, time, sx, sy);
+}
+
+uint32_t wlr_seat_pointer_notify_button(struct wlr_seat *wlr_seat,
+ uint32_t time, uint32_t button, uint32_t state) {
+ struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab;
+ return grab->interface->button(grab, time, button, state);
+}
+
+void wlr_seat_pointer_notify_axis(struct wlr_seat *wlr_seat, uint32_t time,
+ enum wlr_axis_orientation orientation, double value) {
+ struct wlr_seat_pointer_grab *grab = wlr_seat->pointer_state.grab;
+ grab->interface->axis(grab, time, orientation, value);
+}
+
static void keyboard_switch_seat_keyboard(struct wlr_seat_handle *handle,
struct wlr_seat_keyboard *seat_kb) {
if (handle->seat_keyboard == seat_kb) {
@@ -410,6 +539,18 @@ static void keyboard_switch_seat_keyboard(struct wlr_seat_handle *handle,
handle->seat_keyboard = seat_kb;
}
+void wlr_seat_keyboard_send_key(struct wlr_seat *wlr_seat, uint32_t time,
+ uint32_t key, uint32_t state) {
+ struct wlr_seat_handle *handle = wlr_seat->keyboard_state.focused_handle;
+ if (!handle || !handle->keyboard) {
+ return;
+ }
+
+ uint32_t serial = wl_display_next_serial(wlr_seat->display);
+ wl_keyboard_send_key(handle->keyboard, serial,
+ time, key, state);
+}
+
static void keyboard_key_notify(struct wl_listener *listener, void *data) {
struct wlr_seat_keyboard *seat_kb = wl_container_of(listener, seat_kb, key);
struct wlr_seat *seat = seat_kb->seat;
@@ -423,9 +564,9 @@ static void keyboard_key_notify(struct wl_listener *listener, void *data) {
struct wlr_event_keyboard_key *event = data;
enum wlr_key_state key_state = event->state;
- uint32_t key_serial = wl_display_next_serial(seat->display);
- wl_keyboard_send_key(handle->keyboard, key_serial,
- (uint32_t)event->time_usec, event->keycode, key_state);
+ struct wlr_seat_keyboard_grab *grab = seat->keyboard_state.grab;
+ grab->interface->key(grab, (uint32_t)(event->time_usec / 1000),
+ event->keycode, key_state);
}
static void keyboard_modifiers_notify(struct wl_listener *listener,
@@ -442,8 +583,9 @@ static void keyboard_modifiers_notify(struct wl_listener *listener,
struct wlr_keyboard *keyboard = seat_kb->keyboard;
- uint32_t modifiers_serial = wl_display_next_serial(seat->display);
- wl_keyboard_send_modifiers(handle->keyboard, modifiers_serial,
+ struct wlr_seat_keyboard_grab *grab = seat->keyboard_state.grab;
+
+ grab->interface->modifiers(grab,
keyboard->modifiers.depressed, keyboard->modifiers.latched,
keyboard->modifiers.locked, keyboard->modifiers.group);
}
@@ -499,6 +641,21 @@ void wlr_seat_detach_keyboard(struct wlr_seat *seat, struct wlr_keyboard *kb) {
}
}
+void wlr_seat_keyboard_start_grab(struct wlr_seat *wlr_seat,
+ struct wlr_seat_keyboard_grab *grab) {
+ grab->seat = wlr_seat;
+ wlr_seat->keyboard_state.grab = grab;
+
+ wl_signal_emit(&wlr_seat->events.keyboard_grab_begin, grab);
+}
+
+void wlr_seat_keyboard_end_grab(struct wlr_seat *wlr_seat) {
+ struct wlr_seat_keyboard_grab *grab = wlr_seat->keyboard_state.grab;
+ wlr_seat->keyboard_state.grab = wlr_seat->keyboard_state.default_grab;
+
+ wl_signal_emit(&wlr_seat->events.keyboard_grab_end, grab);
+}
+
static void keyboard_surface_destroy_notify(struct wl_listener *listener,
void *data) {
struct wlr_seat_keyboard_state *state = wl_container_of(
@@ -519,6 +676,21 @@ static void keyboard_resource_destroy_notify(struct wl_listener *listener,
wlr_seat_keyboard_clear_focus(state->wlr_seat);
}
+void wlr_seat_keyboard_send_modifiers(struct wlr_seat *seat,
+ uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked,
+ uint32_t group) {
+ struct wlr_seat_handle *handle = seat->keyboard_state.focused_handle;
+ if (!handle || !handle->keyboard) {
+ return;
+ }
+
+ uint32_t serial = wl_display_next_serial(seat->display);
+
+ wl_keyboard_send_modifiers(handle->keyboard, serial,
+ mods_depressed, mods_latched,
+ mods_locked, group);
+}
+
void wlr_seat_keyboard_enter(struct wlr_seat *wlr_seat,
struct wlr_surface *surface) {
if (wlr_seat->keyboard_state.focused_surface == surface) {
@@ -561,7 +733,7 @@ void wlr_seat_keyboard_enter(struct wlr_seat *wlr_seat,
wl_list_remove(&wlr_seat->keyboard_state.resource_destroy.link);
wl_list_init(&wlr_seat->keyboard_state.resource_destroy.link);
if (surface) {
- wl_signal_add(&surface->signals.destroy,
+ wl_signal_add(&surface->events.destroy,
&wlr_seat->keyboard_state.surface_destroy);
wl_resource_add_destroy_listener(surface->resource,
&wlr_seat->keyboard_state.resource_destroy);
@@ -575,6 +747,12 @@ void wlr_seat_keyboard_enter(struct wlr_seat *wlr_seat,
wlr_seat->keyboard_state.focused_surface = surface;
}
+void wlr_seat_keyboard_notify_enter(struct wlr_seat *wlr_seat, struct
+ wlr_surface *surface) {
+ struct wlr_seat_keyboard_grab *grab = wlr_seat->keyboard_state.grab;
+ grab->interface->enter(grab, surface);
+}
+
void wlr_seat_keyboard_clear_focus(struct wlr_seat *wlr_seat) {
struct wl_array keys;
wl_array_init(&keys);
diff --git a/types/wlr_surface.c b/types/wlr_surface.c
index 45b51363..3b4ff42c 100644
--- a/types/wlr_surface.c
+++ b/types/wlr_surface.c
@@ -121,9 +121,6 @@ static void surface_set_input_region(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *region_resource) {
struct wlr_surface *surface = wl_resource_get_user_data(resource);
- if ((surface->pending->invalid & WLR_SURFACE_INVALID_INPUT_REGION)) {
- pixman_region32_clear(&surface->pending->input);
- }
surface->pending->invalid |= WLR_SURFACE_INVALID_INPUT_REGION;
if (region_resource) {
pixman_region32_t *region = wl_resource_get_user_data(region_resource);
@@ -135,6 +132,12 @@ static void surface_set_input_region(struct wl_client *client,
}
static void wlr_surface_update_size(struct wlr_surface *surface, struct wlr_surface_state *state) {
+ if (!state->buffer) {
+ state->height = 0;
+ state->width = 0;
+ return;
+ }
+
int scale = state->scale;
enum wl_output_transform transform = state->transform;
@@ -304,7 +307,7 @@ static void wlr_surface_move_state(struct wlr_surface *surface, struct wlr_surfa
}
if ((next->invalid & WLR_SURFACE_INVALID_INPUT_REGION)) {
// TODO: process buffer
- pixman_region32_clear(&next->input);
+ pixman_region32_copy(&state->input, &next->input);
}
if ((next->invalid & WLR_SURFACE_INVALID_SUBSURFACE_POSITION)) {
state->subsurface_position.x = next->subsurface_position.x;
@@ -345,6 +348,9 @@ static void wlr_surface_commit_pending(struct wlr_surface *surface) {
int32_t oldh = surface->current->buffer_height;
wlr_surface_move_state(surface, surface->pending, surface->current);
+ if (!surface->current->buffer) {
+ surface->texture->valid = false;
+ }
// commit subsurface order
struct wlr_subsurface *subsurface;
@@ -363,7 +369,7 @@ static void wlr_surface_commit_pending(struct wlr_surface *surface) {
oldh != surface->current->buffer_height;
// TODO: add the invalid bitfield to this callback
- wl_signal_emit(&surface->signals.commit, surface);
+ wl_signal_emit(&surface->events.commit, surface);
}
static bool wlr_subsurface_is_synchronized(struct wlr_subsurface *subsurface) {
@@ -448,9 +454,6 @@ static void surface_commit(struct wl_client *client,
void wlr_surface_flush_damage(struct wlr_surface *surface) {
if (!surface->current->buffer) {
- if (surface->texture->valid) {
- // TODO: Detach buffers
- }
return;
}
struct wl_shm_buffer *buffer = wl_shm_buffer_get(surface->current->buffer);
@@ -546,7 +549,9 @@ static struct wlr_surface_state *wlr_surface_state_create() {
pixman_region32_init(&state->surface_damage);
pixman_region32_init(&state->buffer_damage);
pixman_region32_init(&state->opaque);
- pixman_region32_init(&state->input);
+ pixman_region32_init_rect(&state->input,
+ INT32_MIN, INT32_MIN, UINT32_MAX, UINT32_MAX);
+
return state;
}
@@ -584,7 +589,7 @@ void wlr_subsurface_destroy(struct wlr_subsurface *subsurface) {
static void destroy_surface(struct wl_resource *resource) {
struct wlr_surface *surface = wl_resource_get_user_data(resource);
- wl_signal_emit(&surface->signals.destroy, surface);
+ wl_signal_emit(&surface->events.destroy, surface);
if (surface->subsurface) {
wlr_subsurface_destroy(surface->subsurface);
@@ -612,8 +617,8 @@ struct wlr_surface *wlr_surface_create(struct wl_resource *res,
surface->current = wlr_surface_state_create();
surface->pending = wlr_surface_state_create();
- wl_signal_init(&surface->signals.commit);
- wl_signal_init(&surface->signals.destroy);
+ wl_signal_init(&surface->events.commit);
+ wl_signal_init(&surface->events.destroy);
wl_list_init(&surface->subsurface_list);
wl_list_init(&surface->subsurface_pending_list);
wl_resource_set_implementation(res, &surface_interface,
@@ -804,7 +809,7 @@ void wlr_surface_make_subsurface(struct wlr_surface *surface,
// link parent
subsurface->parent = parent;
- wl_signal_add(&parent->signals.destroy,
+ wl_signal_add(&parent->events.destroy,
&subsurface->parent_destroy_listener);
subsurface->parent_destroy_listener.notify =
subsurface_handle_parent_destroy;
diff --git a/types/wlr_wl_shell.c b/types/wlr_wl_shell.c
index 80b8567b..e1507e2f 100644
--- a/types/wlr_wl_shell.c
+++ b/types/wlr_wl_shell.c
@@ -334,7 +334,7 @@ static void wl_shell_get_shell_surface(struct wl_client *client,
wl_signal_init(&wl_surface->events.set_title);
wl_signal_init(&wl_surface->events.set_class);
- wl_signal_add(&wl_surface->surface->signals.destroy,
+ wl_signal_add(&wl_surface->surface->events.destroy,
&wl_surface->surface_destroy_listener);
wl_surface->surface_destroy_listener.notify = handle_wlr_surface_destroyed;
diff --git a/types/wlr_xdg_shell_v6.c b/types/wlr_xdg_shell_v6.c
index c30291d1..5f2554f8 100644
--- a/types/wlr_xdg_shell_v6.c
+++ b/types/wlr_xdg_shell_v6.c
@@ -12,12 +12,504 @@
#include "xdg-shell-unstable-v6-protocol.h"
static const char *wlr_desktop_xdg_toplevel_role = "xdg_toplevel";
+static const char *wlr_desktop_xdg_popup_role = "xdg_popup";
+
+struct wlr_xdg_positioner_v6 {
+ struct wl_resource *resource;
+
+ 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_destroy(struct wl_client *client,
struct wl_resource *resource) {
wl_resource_destroy(resource);
}
+static struct wlr_xdg_surface_v6 *xdg_popup_grab_get_topmost(
+ struct wlr_xdg_popup_grab_v6 *grab) {
+ struct wlr_xdg_popup_v6 *popup;
+ wl_list_for_each(popup, &grab->popups, grab_link) {
+ return popup->base;
+ }
+
+ return NULL;
+}
+
+static void xdg_pointer_grab_end(struct wlr_seat_pointer_grab *grab) {
+ struct wlr_xdg_popup_grab_v6 *popup_grab = grab->data;
+
+ struct wlr_xdg_popup_v6 *popup, *tmp;
+ wl_list_for_each_safe(popup, tmp, &popup_grab->popups, grab_link) {
+ zxdg_popup_v6_send_popup_done(popup->resource);
+ }
+
+ wlr_seat_pointer_end_grab(grab->seat);
+}
+
+static void xdg_pointer_grab_enter(struct wlr_seat_pointer_grab *grab,
+ struct wlr_surface *surface, double sx, double sy) {
+ struct wlr_xdg_popup_grab_v6 *popup_grab = grab->data;
+ if (wl_resource_get_client(surface->resource) == popup_grab->client) {
+ wlr_seat_pointer_enter(grab->seat, surface, sx, sy);
+ } else {
+ wlr_seat_pointer_clear_focus(grab->seat);
+ }
+}
+
+static void xdg_pointer_grab_motion(struct wlr_seat_pointer_grab *grab,
+ uint32_t time, double sx, double sy) {
+ wlr_seat_pointer_send_motion(grab->seat, time, sx, sy);
+}
+
+static uint32_t xdg_pointer_grab_button(struct wlr_seat_pointer_grab *grab,
+ uint32_t time, uint32_t button, uint32_t state) {
+ uint32_t serial =
+ wlr_seat_pointer_send_button(grab->seat, time, button, state);
+ if (serial) {
+ return serial;
+ } else {
+ xdg_pointer_grab_end(grab);
+ return 0;
+ }
+}
+
+static void xdg_pointer_grab_axis(struct wlr_seat_pointer_grab *grab,
+ uint32_t time, enum wlr_axis_orientation orientation, double value) {
+ wlr_seat_pointer_send_axis(grab->seat, time, orientation, value);
+}
+
+static void xdg_pointer_grab_cancel(struct wlr_seat_pointer_grab *grab) {
+ xdg_pointer_grab_end(grab);
+}
+
+static const struct wlr_pointer_grab_interface xdg_pointer_grab_impl = {
+ .enter = xdg_pointer_grab_enter,
+ .motion = xdg_pointer_grab_motion,
+ .button = xdg_pointer_grab_button,
+ .cancel = xdg_pointer_grab_cancel,
+ .axis = xdg_pointer_grab_axis,
+};
+
+static void xdg_keyboard_grab_enter(struct wlr_seat_keyboard_grab *grab, struct wlr_surface *surface) {
+ // keyboard focus should remain on the popup
+}
+
+static void xdg_keyboard_grab_key(struct wlr_seat_keyboard_grab *grab, uint32_t time,
+ uint32_t key, uint32_t state) {
+ wlr_seat_keyboard_send_key(grab->seat, time, key, state);
+}
+
+static void xdg_keyboard_grab_modifiers(struct wlr_seat_keyboard_grab *grab,
+ uint32_t mods_depressed, uint32_t mods_latched,
+ uint32_t mods_locked, uint32_t group) {
+ wlr_seat_keyboard_send_modifiers(grab->seat, mods_depressed, mods_latched,
+ mods_locked, group);
+}
+
+static void xdg_keyboard_grab_cancel(struct wlr_seat_keyboard_grab *grab) {
+ wlr_seat_keyboard_end_grab(grab->seat);
+}
+
+static const struct wlr_keyboard_grab_interface xdg_keyboard_grab_impl = {
+ .enter = xdg_keyboard_grab_enter,
+ .key = xdg_keyboard_grab_key,
+ .modifiers = xdg_keyboard_grab_modifiers,
+ .cancel = xdg_keyboard_grab_cancel,
+};
+
+static struct wlr_xdg_popup_grab_v6 *xdg_shell_popup_grab_from_seat(
+ struct wlr_xdg_shell_v6 *shell, struct wlr_seat *seat) {
+ struct wlr_xdg_popup_grab_v6 *xdg_grab;
+ wl_list_for_each(xdg_grab, &shell->popup_grabs, link) {
+ if (xdg_grab->seat == seat) {
+ return xdg_grab;
+ }
+ }
+
+ xdg_grab = calloc(1, sizeof(struct wlr_xdg_popup_grab_v6));
+ if (!xdg_grab) {
+ return NULL;
+ }
+
+ xdg_grab->pointer_grab.data = xdg_grab;
+ xdg_grab->pointer_grab.interface = &xdg_pointer_grab_impl;
+ xdg_grab->keyboard_grab.data = xdg_grab;
+ xdg_grab->keyboard_grab.interface = &xdg_keyboard_grab_impl;
+
+ wl_list_init(&xdg_grab->popups);
+
+ wl_list_insert(&shell->popup_grabs, &xdg_grab->link);
+ xdg_grab->seat = seat;
+
+ return xdg_grab;
+}
+
+
+static void xdg_surface_destroy(struct wlr_xdg_surface_v6 *surface) {
+ // TODO: probably need to ungrab before this event
+ wl_signal_emit(&surface->events.destroy, surface);
+
+ if (surface->configure_idle) {
+ wl_event_source_remove(surface->configure_idle);
+ }
+
+ struct wlr_xdg_surface_v6_configure *configure, *tmp;
+ wl_list_for_each_safe(configure, tmp, &surface->configure_list, link) {
+ free(configure);
+ }
+
+ if (surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) {
+ wl_resource_set_user_data(surface->toplevel_state->resource, NULL);
+ free(surface->toplevel_state);
+ }
+
+ if (surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP) {
+ wl_resource_set_user_data(surface->popup_state->resource, NULL);
+
+ if (surface->popup_state->seat) {
+ struct wlr_xdg_popup_grab_v6 *grab =
+ xdg_shell_popup_grab_from_seat(surface->client->shell,
+ surface->popup_state->seat);
+
+ struct wlr_xdg_surface_v6 *topmost =
+ xdg_popup_grab_get_topmost(grab);
+
+ if (topmost != surface) {
+ wl_resource_post_error(surface->client->resource,
+ ZXDG_SHELL_V6_ERROR_NOT_THE_TOPMOST_POPUP,
+ "xdg_popup was destroyed while it was not the topmost "
+ "popup.");
+ }
+
+ wl_list_remove(&surface->popup_state->grab_link);
+
+ if (wl_list_empty(&grab->popups)) {
+ if (grab->seat->pointer_state.grab == &grab->pointer_grab) {
+ wlr_seat_pointer_end_grab(grab->seat);
+ }
+ if (grab->seat->keyboard_state.grab == &grab->keyboard_grab) {
+ wlr_seat_keyboard_end_grab(grab->seat);
+ }
+ }
+ }
+
+ wl_list_remove(&surface->popup_link);
+ free(surface->popup_state);
+ }
+
+ wl_resource_set_user_data(surface->resource, NULL);
+ wl_list_remove(&surface->link);
+ wl_list_remove(&surface->surface_destroy_listener.link);
+ wl_list_remove(&surface->surface_commit_listener.link);
+ free(surface->geometry);
+ free(surface->next_geometry);
+ free(surface->title);
+ free(surface->app_id);
+ free(surface);
+}
+
+
+static void xdg_positioner_destroy(struct wl_resource *resource) {
+ struct wlr_xdg_positioner_v6 *positioner =
+ wl_resource_get_user_data(resource);
+ free(positioner);
+
+}
+
+static void xdg_positioner_protocol_set_size(struct wl_client *client,
+ struct wl_resource *resource, int32_t width, int32_t height) {
+ struct wlr_xdg_positioner_v6 *positioner =
+ wl_resource_get_user_data(resource);
+
+ if (width < 1 || height < 1) {
+ wl_resource_post_error(resource,
+ ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT,
+ "width and height must be positives and non-zero");
+ return;
+ }
+
+ positioner->size.width = width;
+ positioner->size.height = height;
+}
+
+static void xdg_positioner_protocol_set_anchor_rect(struct wl_client *client,
+ struct wl_resource *resource, int32_t x, int32_t y, int32_t width,
+ int32_t height) {
+ struct wlr_xdg_positioner_v6 *positioner =
+ wl_resource_get_user_data(resource);
+
+ if (width < 1 || height < 1) {
+ wl_resource_post_error(resource,
+ ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT,
+ "width and height must be positives and non-zero");
+ return;
+ }
+
+ positioner->anchor_rect.x = x;
+ positioner->anchor_rect.y = y;
+ positioner->anchor_rect.width = width;
+ positioner->anchor_rect.height = height;
+}
+
+static void xdg_positioner_protocol_set_anchor(struct wl_client *client,
+ struct wl_resource *resource, uint32_t anchor) {
+ struct wlr_xdg_positioner_v6 *positioner =
+ wl_resource_get_user_data(resource);
+
+ if (((anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP ) &&
+ (anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM)) ||
+ ((anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT) &&
+ (anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT))) {
+ wl_resource_post_error(resource,
+ ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT,
+ "same-axis values are not allowed");
+ return;
+ }
+
+ positioner->anchor = anchor;
+}
+
+static void xdg_positioner_protocol_set_gravity(struct wl_client *client,
+ struct wl_resource *resource, uint32_t gravity) {
+ struct wlr_xdg_positioner_v6 *positioner =
+ wl_resource_get_user_data(resource);
+
+ if (((gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP) &&
+ (gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM)) ||
+ ((gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT) &&
+ (gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT))) {
+ wl_resource_post_error(resource,
+ ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT,
+ "same-axis values are not allowed");
+ return;
+ }
+
+ positioner->gravity = gravity;
+}
+
+static void xdg_positioner_protocol_set_constraint_adjustment(
+ struct wl_client *client, struct wl_resource *resource,
+ uint32_t constraint_adjustment) {
+ struct wlr_xdg_positioner_v6 *positioner =
+ wl_resource_get_user_data(resource);
+
+ positioner->constraint_adjustment = constraint_adjustment;
+}
+
+static void xdg_positioner_protocol_set_offset(struct wl_client *client,
+ struct wl_resource *resource, int32_t x, int32_t y) {
+ struct wlr_xdg_positioner_v6 *positioner =
+ wl_resource_get_user_data(resource);
+
+ positioner->offset.x = x;
+ positioner->offset.y = y;
+}
+
+static const struct zxdg_positioner_v6_interface
+ zxdg_positioner_v6_implementation = {
+ .destroy = resource_destroy,
+ .set_size = xdg_positioner_protocol_set_size,
+ .set_anchor_rect = xdg_positioner_protocol_set_anchor_rect,
+ .set_anchor = xdg_positioner_protocol_set_anchor,
+ .set_gravity = xdg_positioner_protocol_set_gravity,
+ .set_constraint_adjustment =
+ xdg_positioner_protocol_set_constraint_adjustment,
+ .set_offset = xdg_positioner_protocol_set_offset,
+};
+
+static void xdg_shell_create_positioner(struct wl_client *wl_client,
+ struct wl_resource *resource, uint32_t id) {
+ struct wlr_xdg_positioner_v6 *positioner =
+ calloc(1, sizeof(struct wlr_xdg_positioner_v6));
+ if (positioner == NULL) {
+ wl_client_post_no_memory(wl_client);
+ return;
+ }
+
+ positioner->resource = wl_resource_create(wl_client,
+ &zxdg_positioner_v6_interface,
+ wl_resource_get_version(resource),
+ id);
+ if (positioner->resource == NULL) {
+ wl_client_post_no_memory(wl_client);
+ free(positioner);
+ return;
+ }
+
+ wl_resource_set_implementation(positioner->resource,
+ &zxdg_positioner_v6_implementation,
+ positioner, xdg_positioner_destroy);
+}
+
+static void xdg_popup_protocol_grab(struct wl_client *client,
+ struct wl_resource *resource, struct wl_resource *seat_resource,
+ uint32_t serial) {
+ struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource);
+ struct wlr_seat_handle *handle = wl_resource_get_user_data(seat_resource);
+
+ if (surface->popup_state->committed) {
+ wl_resource_post_error(surface->popup_state->resource,
+ ZXDG_POPUP_V6_ERROR_INVALID_GRAB,
+ "xdg_popup is already mapped");
+ return;
+ }
+
+ struct wlr_xdg_popup_grab_v6 *popup_grab =
+ xdg_shell_popup_grab_from_seat(surface->client->shell,
+ handle->wlr_seat);
+
+ struct wlr_xdg_surface_v6 *topmost = xdg_popup_grab_get_topmost(popup_grab);
+ bool parent_is_toplevel =
+ surface->popup_state->parent->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL;
+
+ if ((topmost == NULL && !parent_is_toplevel) ||
+ (topmost != NULL && topmost != surface->popup_state->parent)) {
+ wl_resource_post_error(surface->client->resource,
+ ZXDG_SHELL_V6_ERROR_NOT_THE_TOPMOST_POPUP,
+ "xdg_popup was not created on the topmost popup");
+ return;
+ }
+
+ popup_grab->client = surface->client->client;
+ surface->popup_state->seat = handle->wlr_seat;
+
+ wl_list_insert(&popup_grab->popups, &surface->popup_state->grab_link);
+
+ wlr_seat_pointer_start_grab(handle->wlr_seat, &popup_grab->pointer_grab);
+ wlr_seat_keyboard_start_grab(handle->wlr_seat, &popup_grab->keyboard_grab);
+}
+
+static const struct zxdg_popup_v6_interface zxdg_popup_v6_implementation = {
+ .destroy = resource_destroy,
+ .grab = xdg_popup_protocol_grab,
+};
+
+
+static struct wlr_box xdg_positioner_get_geometry(
+ struct wlr_xdg_positioner_v6 *positioner,
+ struct wlr_xdg_surface_v6 *surface, struct wlr_xdg_surface_v6 *parent) {
+ struct wlr_box geometry = {
+ .x = positioner->offset.x,
+ .y = positioner->offset.y,
+ .width = positioner->size.width,
+ .height = positioner->size.height,
+ };
+
+ if (positioner->anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP) {
+ geometry.y += positioner->anchor_rect.y;
+ } else if (positioner->anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM) {
+ geometry.y +=
+ positioner->anchor_rect.y + positioner->anchor_rect.height;
+ } else {
+ geometry.y +=
+ positioner->anchor_rect.y + positioner->anchor_rect.height / 2;
+ }
+
+ if (positioner->anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT) {
+ geometry.x += positioner->anchor_rect.x;
+ } else if (positioner->anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT) {
+ geometry.x += positioner->anchor_rect.x + positioner->anchor_rect.width;
+ } else {
+ geometry.x +=
+ positioner->anchor_rect.x + positioner->anchor_rect.width / 2;
+ }
+
+ if (positioner->gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP) {
+ geometry.y -= geometry.height;
+ } else if (positioner->gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM) {
+ geometry.y = geometry.y;
+ } else {
+ geometry.y -= geometry.height / 2;
+ }
+
+ if (positioner->gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT) {
+ geometry.x -= geometry.width;
+ } else if (positioner->gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT) {
+ geometry.x = geometry.x;
+ } else {
+ geometry.x -= geometry.width / 2;
+ }
+
+ if (positioner->constraint_adjustment ==
+ ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_NONE) {
+ return geometry;
+ }
+
+ // TODO: add compositor policy configuration and the code here
+
+ return geometry;
+}
+
+static void xdg_popup_resource_destroy(struct wl_resource *resource) {
+ struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource);
+ if (surface != NULL) {
+ xdg_surface_destroy(surface);
+ }
+}
+
+static void xdg_surface_get_popup(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id,
+ struct wl_resource *parent_resource,
+ struct wl_resource *positioner_resource) {
+ struct wlr_xdg_surface_v6 *surface =
+ wl_resource_get_user_data(resource);
+ struct wlr_xdg_surface_v6 *parent =
+ wl_resource_get_user_data(parent_resource);
+ struct wlr_xdg_positioner_v6 *positioner =
+ wl_resource_get_user_data(positioner_resource);
+
+ if (positioner->size.width == 0 || positioner->anchor_rect.width == 0) {
+ wl_resource_post_error(resource,
+ ZXDG_SHELL_V6_ERROR_INVALID_POSITIONER,
+ "positioner object is not complete");
+ return;
+ }
+
+ if (wlr_surface_set_role(surface->surface, wlr_desktop_xdg_popup_role,
+ resource, ZXDG_SHELL_V6_ERROR_ROLE)) {
+ return;
+ }
+
+ surface->popup_state = calloc(1, sizeof(struct wlr_xdg_popup_v6));
+ if (!surface->popup_state) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ surface->popup_state->resource =
+ wl_resource_create(client, &zxdg_popup_v6_interface,
+ wl_resource_get_version(resource), id);
+ if (surface->popup_state->resource == NULL) {
+ free(surface->popup_state);
+ return;
+ }
+
+ surface->role = WLR_XDG_SURFACE_V6_ROLE_POPUP;
+ surface->popup_state->base = surface;
+ surface->popup_state->parent = parent;
+ surface->popup_state->geometry =
+ xdg_positioner_get_geometry(positioner, surface, parent);
+ wl_list_insert(&surface->popup_state->parent->popups,
+ &surface->popup_link);
+
+ wl_resource_set_implementation(surface->popup_state->resource,
+ &zxdg_popup_v6_implementation, surface,
+ xdg_popup_resource_destroy);
+}
+
+
static void xdg_toplevel_protocol_set_parent(struct wl_client *client,
struct wl_resource *resource, struct wl_resource *parent_resource) {
struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource);
@@ -216,34 +708,6 @@ static const struct zxdg_toplevel_v6_interface zxdg_toplevel_v6_implementation =
.set_minimized = xdg_toplevel_protocol_set_minimized
};
-static void xdg_surface_destroy(struct wlr_xdg_surface_v6 *surface) {
- wl_signal_emit(&surface->events.destroy, surface);
- wl_resource_set_user_data(surface->resource, NULL);
-
- if (surface->configure_idle) {
- wl_event_source_remove(surface->configure_idle);
- }
-
- struct wlr_xdg_surface_v6_configure *configure, *tmp;
- wl_list_for_each_safe(configure, tmp, &surface->configure_list, link) {
- free(configure);
- }
-
- if (surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) {
- wl_resource_set_user_data(surface->toplevel_state->resource, NULL);
- free(surface->toplevel_state);
- }
-
- wl_list_remove(&surface->link);
- wl_list_remove(&surface->surface_destroy_listener.link);
- wl_list_remove(&surface->surface_commit_listener.link);
- free(surface->geometry);
- free(surface->next_geometry);
- free(surface->title);
- free(surface->app_id);
- free(surface);
-}
-
static void xdg_surface_resource_destroy(struct wl_resource *resource) {
struct wlr_xdg_surface_v6 *surface = wl_resource_get_user_data(resource);
if (surface != NULL) {
@@ -286,12 +750,6 @@ static void xdg_surface_get_toplevel(struct wl_client *client,
xdg_toplevel_resource_destroy);
}
-static void xdg_surface_get_popup(struct wl_client *client,
- struct wl_resource *resource, uint32_t id, struct wl_resource *parent,
- struct wl_resource *wl_positioner) {
- wlr_log(L_DEBUG, "TODO xdg surface get popup");
-}
-
static void wlr_xdg_toplevel_v6_ack_configure(
struct wlr_xdg_surface_v6 *surface,
struct wlr_xdg_surface_v6_configure *configure) {
@@ -333,9 +791,15 @@ static void xdg_surface_ack_configure(struct wl_client *client,
return;
}
- // TODO handle popups
- if (surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL) {
+ switch (surface->role) {
+ case WLR_XDG_SURFACE_V6_ROLE_NONE:
+ assert(0 && "not reached");
+ break;
+ case WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL:
wlr_xdg_toplevel_v6_ack_configure(surface, configure);
+ break;
+ case WLR_XDG_SURFACE_V6_ROLE_POPUP:
+ break;
}
if (!surface->configured) {
@@ -376,11 +840,6 @@ static const struct zxdg_surface_v6_interface zxdg_surface_v6_implementation = {
.set_window_geometry = xdg_surface_set_window_geometry,
};
-static void xdg_shell_create_positioner(struct wl_client *client,
- struct wl_resource *resource, uint32_t id) {
- wlr_log(L_DEBUG, "TODO: xdg shell create positioner");
-}
-
static bool wlr_xdg_surface_v6_toplevel_state_compare(
struct wlr_xdg_toplevel_v6 *state) {
// is pending state different from current state?
@@ -454,9 +913,6 @@ static void wlr_xdg_surface_send_configure(void *user_data) {
struct wlr_xdg_surface_v6 *surface = user_data;
struct wl_display *display = wl_client_get_display(surface->client->client);
- // TODO handle popups
- assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL);
-
surface->configure_idle = NULL;
struct wlr_xdg_surface_v6_configure *configure =
@@ -469,21 +925,42 @@ static void wlr_xdg_surface_send_configure(void *user_data) {
wl_list_insert(surface->configure_list.prev, &configure->link);
configure->serial = wl_display_next_serial(display);
- wlr_xdg_toplevel_v6_send_configure(surface, configure);
+ switch (surface->role) {
+ case WLR_XDG_SURFACE_V6_ROLE_NONE:
+ assert(0 && "not reached");
+ break;
+ case WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL:
+ wlr_xdg_toplevel_v6_send_configure(surface, configure);
+ break;
+ case WLR_XDG_SURFACE_V6_ROLE_POPUP:
+ zxdg_popup_v6_send_configure(surface->popup_state->resource,
+ surface->popup_state->geometry.x,
+ surface->popup_state->geometry.y,
+ surface->popup_state->geometry.width,
+ surface->popup_state->geometry.height);
+ break;
+ }
zxdg_surface_v6_send_configure(surface->resource, configure->serial);
}
static void wlr_xdg_surface_v6_schedule_configure(
struct wlr_xdg_surface_v6 *surface, bool force) {
- // TODO handle popups
- assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL);
-
struct wl_display *display = wl_client_get_display(surface->client->client);
struct wl_event_loop *loop = wl_display_get_event_loop(display);
+ bool pending_same = false;
- bool pending_same = !force &&
- wlr_xdg_surface_v6_toplevel_state_compare(surface->toplevel_state);
+ switch (surface->role) {
+ case WLR_XDG_SURFACE_V6_ROLE_NONE:
+ assert(0 && "not reached");
+ break;
+ case WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL:
+ pending_same = !force &&
+ wlr_xdg_surface_v6_toplevel_state_compare(surface->toplevel_state);
+ break;
+ case WLR_XDG_SURFACE_V6_ROLE_POPUP:
+ break;
+ }
if (surface->configure_idle != NULL) {
if (!pending_same) {
@@ -534,6 +1011,16 @@ static void wlr_xdg_surface_v6_toplevel_committed(
surface->toplevel_state->current = surface->toplevel_state->next;
}
+static void wlr_xdg_surface_v6_popup_committed(
+ struct wlr_xdg_surface_v6 *surface) {
+ assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_POPUP);
+
+ if (!surface->popup_state->committed) {
+ wlr_xdg_surface_v6_schedule_configure(surface, true);
+ surface->popup_state->committed = true;
+ }
+}
+
static void handle_wlr_surface_committed(struct wl_listener *listener,
void *data) {
struct wlr_xdg_surface_v6 *surface =
@@ -564,7 +1051,7 @@ static void handle_wlr_surface_committed(struct wl_listener *listener,
wlr_xdg_surface_v6_toplevel_committed(surface);
break;
case WLR_XDG_SURFACE_V6_ROLE_POPUP:
- wlr_log(L_DEBUG, "TODO: popup surface committed");
+ wlr_xdg_surface_v6_popup_committed(surface);
break;
}
@@ -611,6 +1098,7 @@ static void xdg_shell_get_xdg_surface(struct wl_client *wl_client,
}
wl_list_init(&surface->configure_list);
+ wl_list_init(&surface->popups);
wl_signal_init(&surface->events.request_minimize);
wl_signal_init(&surface->events.request_move);
@@ -621,11 +1109,11 @@ static void xdg_shell_get_xdg_surface(struct wl_client *wl_client,
wl_signal_init(&surface->events.ack_configure);
wl_signal_init(&surface->events.ping_timeout);
- wl_signal_add(&surface->surface->signals.destroy,
+ wl_signal_add(&surface->surface->events.destroy,
&surface->surface_destroy_listener);
surface->surface_destroy_listener.notify = handle_wlr_surface_destroyed;
- wl_signal_add(&surface->surface->signals.commit,
+ wl_signal_add(&surface->surface->events.commit,
&surface->surface_commit_listener);
surface->surface_commit_listener.notify = handle_wlr_surface_committed;
@@ -730,6 +1218,7 @@ struct wlr_xdg_shell_v6 *wlr_xdg_shell_v6_create(struct wl_display *display) {
xdg_shell->ping_timeout = 10000;
wl_list_init(&xdg_shell->clients);
+ wl_list_init(&xdg_shell->popup_grabs);
struct wl_global *wl_global = wl_global_create(display,
&zxdg_shell_v6_interface, 1, xdg_shell, xdg_shell_bind);
@@ -807,3 +1296,8 @@ void wlr_xdg_toplevel_v6_set_resizing(struct wlr_xdg_surface_v6 *surface,
wlr_xdg_surface_v6_schedule_configure(surface, false);
}
+
+void wlr_xdg_toplevel_v6_send_close(struct wlr_xdg_surface_v6 *surface) {
+ assert(surface->role == WLR_XDG_SURFACE_V6_ROLE_TOPLEVEL);
+ zxdg_toplevel_v6_send_close(surface->toplevel_state->resource);
+}
diff --git a/xwayland/xwm.c b/xwayland/xwm.c
index a4091d1f..bc1bb4de 100644
--- a/xwayland/xwm.c
+++ b/xwayland/xwm.c
@@ -705,19 +705,25 @@ static void xcb_init_wm(struct wlr_xwm *xwm) {
void wlr_xwayland_surface_activate(struct wlr_xwayland *wlr_xwayland,
struct wlr_xwayland_surface *surface) {
struct wlr_xwm *xwm = wlr_xwayland->xwm;
- xcb_client_message_event_t m = {0};
- m.response_type = XCB_CLIENT_MESSAGE;
- m.format = 32;
- m.window = surface->window_id;
- m.type = xwm->atoms[WM_PROTOCOLS];
- m.data.data32[0] = xwm->atoms[WM_TAKE_FOCUS];
- m.data.data32[1] = XCB_TIME_CURRENT_TIME;
- xcb_send_event_checked(xwm->xcb_conn, 0, surface->window_id,
- XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (char*)&m);
- xcb_set_input_focus_checked(xwm->xcb_conn, XCB_INPUT_FOCUS_POINTER_ROOT,
- surface->window_id, XCB_CURRENT_TIME);
- xcb_configure_window_checked(xwm->xcb_conn, surface->window_id,
- XCB_CONFIG_WINDOW_STACK_MODE, (uint32_t[]){XCB_STACK_MODE_ABOVE});
+ if (surface) {
+ xcb_client_message_event_t m = {0};
+ m.response_type = XCB_CLIENT_MESSAGE;
+ m.format = 32;
+ m.window = surface->window_id;
+ m.type = xwm->atoms[WM_PROTOCOLS];
+ m.data.data32[0] = xwm->atoms[WM_TAKE_FOCUS];
+ m.data.data32[1] = XCB_TIME_CURRENT_TIME;
+ xcb_send_event_checked(xwm->xcb_conn, 0, surface->window_id,
+ XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (char*)&m);
+ xcb_set_input_focus_checked(xwm->xcb_conn, XCB_INPUT_FOCUS_POINTER_ROOT,
+ surface->window_id, XCB_CURRENT_TIME);
+ xcb_configure_window_checked(xwm->xcb_conn, surface->window_id,
+ XCB_CONFIG_WINDOW_STACK_MODE, (uint32_t[]){XCB_STACK_MODE_ABOVE});
+ } else {
+ wlr_log(L_DEBUG, "Deactivating xwayland");
+ xcb_set_input_focus_checked(xwm->xcb_conn, XCB_INPUT_FOCUS_NONE,
+ -1, XCB_CURRENT_TIME);
+ }
xcb_flush(xwm->xcb_conn);
}
@@ -735,6 +741,7 @@ void wlr_xwayland_surface_configure(struct wlr_xwayland *wlr_xwayland,
XCB_CONFIG_WINDOW_BORDER_WIDTH;
uint32_t values[] = {x, y, width, height, 0};
xcb_configure_window(xwm->xcb_conn, surface->window_id, mask, values);
+ xcb_flush(xwm->xcb_conn);
}
void wlr_xwayland_surface_close(struct wlr_xwayland *wlr_xwayland,