aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.builds/archlinux.yml (renamed from .build.yml)0
-rw-r--r--.builds/freebsd.yml79
-rw-r--r--README.md10
-rw-r--r--backend/backend.c49
-rw-r--r--backend/drm/atomic.c34
-rw-r--r--backend/drm/backend.c21
-rw-r--r--backend/drm/drm.c185
-rw-r--r--backend/drm/legacy.c10
-rw-r--r--backend/drm/properties.c13
-rw-r--r--backend/drm/renderer.c11
-rw-r--r--backend/drm/util.c19
-rw-r--r--backend/headless/output.c4
-rw-r--r--backend/multi/backend.c36
-rw-r--r--backend/session/logind.c127
-rw-r--r--backend/wayland/output.c27
-rw-r--r--backend/x11/output.c7
-rw-r--r--docs/env_vars.md3
-rw-r--r--examples/dmabuf-capture.c2
-rw-r--r--examples/input-method.c402
-rw-r--r--examples/layer-shell.c6
-rw-r--r--examples/meson.build20
-rw-r--r--examples/pointer-constraints.c260
-rw-r--r--examples/text-input.c394
-rw-r--r--include/backend/drm/drm.h11
-rw-r--r--include/backend/drm/iface.h8
-rw-r--r--include/backend/drm/properties.h3
-rw-r--r--include/backend/multi.h1
-rw-r--r--include/backend/x11.h1
-rw-r--r--include/render/gles2.h13
-rw-r--r--include/rootston/cursor.h41
-rw-r--r--include/rootston/desktop.h8
-rw-r--r--include/rootston/input.h2
-rw-r--r--include/rootston/output.h4
-rw-r--r--include/rootston/seat.h12
-rw-r--r--include/rootston/text_input.h63
-rw-r--r--include/types/wlr_data_device.h1
-rw-r--r--include/util/os-compatibility.h9
-rw-r--r--include/util/shm.h7
-rw-r--r--include/wlr/backend.h9
-rw-r--r--include/wlr/backend/drm.h2
-rw-r--r--include/wlr/backend/interface.h3
-rw-r--r--include/wlr/backend/multi.h1
-rw-r--r--include/wlr/interfaces/wlr_output.h9
-rw-r--r--include/wlr/render/dmabuf.h4
-rw-r--r--include/wlr/render/egl.h1
-rw-r--r--include/wlr/render/interface.h11
-rw-r--r--include/wlr/render/wlr_renderer.h5
-rw-r--r--include/wlr/render/wlr_texture.h7
-rw-r--r--include/wlr/types/meson.build11
-rw-r--r--include/wlr/types/wlr_box.h3
-rw-r--r--include/wlr/types/wlr_cursor.h12
-rw-r--r--include/wlr/types/wlr_data_device.h1
-rw-r--r--include/wlr/types/wlr_input_method_v2.h87
-rw-r--r--include/wlr/types/wlr_output.h40
-rw-r--r--include/wlr/types/wlr_pointer_constraints_v1.h102
-rw-r--r--include/wlr/types/wlr_presentation_time.h59
-rw-r--r--include/wlr/types/wlr_region.h3
-rw-r--r--include/wlr/types/wlr_seat.h15
-rw-r--r--include/wlr/types/wlr_surface.h5
-rw-r--r--include/wlr/types/wlr_text_input_v3.h93
-rw-r--r--include/wlr/util/region.h5
-rw-r--r--include/wlr/xwayland.h1
-rw-r--r--include/xwayland/xwm.h2
-rw-r--r--meson.build18
-rw-r--r--protocol/input-method-unstable-v2.xml490
-rw-r--r--protocol/meson.build7
-rw-r--r--protocol/text-input-unstable-v3.xml441
-rw-r--r--render/egl.c58
-rw-r--r--render/gles2/pixel_format.c20
-rw-r--r--render/gles2/renderer.c105
-rw-r--r--render/gles2/texture.c17
-rw-r--r--render/meson.build8
-rw-r--r--render/wlr_renderer.c9
-rw-r--r--render/wlr_texture.c8
-rw-r--r--rootston/config.c3
-rw-r--r--rootston/cursor.c219
-rw-r--r--rootston/desktop.c99
-rw-r--r--rootston/keyboard.c22
-rw-r--r--rootston/meson.build1
-rw-r--r--rootston/output.c185
-rw-r--r--rootston/rootston.ini.example2
-rw-r--r--rootston/seat.c47
-rw-r--r--rootston/text_input.c310
-rw-r--r--types/data_device/wlr_data_device.c32
-rw-r--r--types/data_device/wlr_data_offer.c3
-rw-r--r--types/data_device/wlr_data_source.c5
-rw-r--r--types/meson.build26
-rw-r--r--types/seat/wlr_seat.c2
-rw-r--r--types/seat/wlr_seat_keyboard.c4
-rw-r--r--types/seat/wlr_seat_pointer.c25
-rw-r--r--types/tablet_v2/wlr_tablet_v2_pad.c23
-rw-r--r--types/wlr_box.c9
-rw-r--r--types/wlr_buffer.c16
-rw-r--r--types/wlr_cursor.c16
-rw-r--r--types/wlr_gamma_control_v1.c6
-rw-r--r--types/wlr_idle.c15
-rw-r--r--types/wlr_input_method_v2.c298
-rw-r--r--types/wlr_linux_dmabuf_v1.c29
-rw-r--r--types/wlr_output.c55
-rw-r--r--types/wlr_output_layout.c13
-rw-r--r--types/wlr_pointer_constraints_v1.c372
-rw-r--r--types/wlr_presentation_time.c223
-rw-r--r--types/wlr_screencopy_v1.c20
-rw-r--r--types/wlr_surface.c20
-rw-r--r--types/wlr_text_input_v3.c337
-rw-r--r--types/wlr_xdg_decoration_v1.c6
-rw-r--r--util/meson.build4
-rw-r--r--util/os-compatibility.c149
-rw-r--r--util/region.c71
-rw-r--r--util/shm.c67
-rw-r--r--xwayland/xwayland.c12
-rw-r--r--xwayland/xwm.c42
112 files changed, 5798 insertions, 565 deletions
diff --git a/.build.yml b/.builds/archlinux.yml
index 4b3e197d..4b3e197d 100644
--- a/.build.yml
+++ b/.builds/archlinux.yml
diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml
new file mode 100644
index 00000000..65e9d41d
--- /dev/null
+++ b/.builds/freebsd.yml
@@ -0,0 +1,79 @@
+image: freebsd
+packages:
+- devel/automake
+- multimedia/ffmpeg
+- devel/gmake
+- devel/json-c
+- devel/libtool
+- x11/libxkbcommon
+- textproc/libxslt
+- x11-toolkits/pango
+- devel/pkgconf
+- print/texinfo
+- x11/xcb-util-image
+- x11/xcb-util-wm
+- python36
+- py36-setuptools
+sources:
+- https://github.com/swaywm/wlroots
+tasks:
+- setup: |
+ # Don't build unnecessary stuff
+ echo "OPTIONS_UNSET+= NLS DOCS EXAMPLES LIBWACOM" | sudo tee -a /etc/make.conf
+ # Note: this could probably be set in the FreeBSD base image
+ echo "BATCH=yes" | sudo tee -a /etc/make.conf
+- ports_tree: |
+ # This is ugly, but fetching and extracting the whole ports tree takes a
+ # really-really long time...
+ # First we need a clean tree, and renaming is faster than deleting.
+ sudo mv /usr/ports /usr/ports.orig
+ sudo mkdir /usr/ports
+ # Fetch only needed ports
+ sudo svnlite export --force https://svn.FreeBSD.org/ports/head/GIDs /usr/ports/GIDs
+ sudo svnlite export --force https://svn.FreeBSD.org/ports/head/Keywords /usr/ports/Keywords
+ sudo svnlite export --force https://svn.FreeBSD.org/ports/head/Makefile /usr/ports/Makefile
+ sudo svnlite export --force https://svn.FreeBSD.org/ports/head/Mk /usr/ports/Mk
+ sudo svnlite export --force https://svn.FreeBSD.org/ports/head/Templates /usr/ports/Templates
+ sudo svnlite export --force https://svn.FreeBSD.org/ports/head/Tools /usr/ports/Tools
+ sudo svnlite export --force https://svn.FreeBSD.org/ports/head/UIDs /usr/ports/UIDs
+ sudo svnlite export --force https://svn.FreeBSD.org/ports/head/devel/libevdev /usr/ports/devel/libevdev
+ sudo svnlite export --force https://svn.FreeBSD.org/ports/head/devel/libmtdev /usr/ports/devel/libmtdev
+ sudo svnlite export --force https://svn.FreeBSD.org/ports/head/devel/libudev-devd /usr/ports/devel/libudev-devd
+ sudo svnlite export --force https://svn.FreeBSD.org/ports/head/devel/meson /usr/ports/devel/meson
+ sudo svnlite export --force https://svn.FreeBSD.org/ports/head/devel/ninja /usr/ports/devel/ninja
+ sudo svnlite export --force https://svn.FreeBSD.org/ports/head/devel/py-evdev /usr/ports/devel/py-evdev
+ sudo svnlite export --force https://svn.FreeBSD.org/ports/head/devel/py-six /usr/ports/devel/py-six
+ sudo svnlite export --force https://svn.FreeBSD.org/ports/head/graphics/wayland-protocols /usr/ports/graphics/wayland-protocols
+ sudo svnlite export --force https://svn.FreeBSD.org/ports/head/graphics/wayland /usr/ports/graphics/wayland
+ sudo svnlite export --force https://svn.FreeBSD.org/ports/head/lang/python27 /usr/ports/lang/python27
+ sudo svnlite export --force https://svn.FreeBSD.org/ports/head/lang/python36 /usr/ports/lang/python36
+- fixup_libinput: |
+ sudo svnlite export https://github.com/FreeBSDDesktop/freebsd-ports/branches/feature/input/devel/libepoll-shim /usr/ports/devel/libepoll-shim
+ sudo svnlite export https://github.com/FreeBSDDesktop/freebsd-ports/branches/feature/input/x11/libinput /usr/ports/x11/libinput
+ sudo svnlite export https://github.com/FreeBSDDesktop/freebsd-ports/branches/feature/input/devel/evdev-proto /usr/ports/devel/evdev-proto
+ sudo svnlite export https://github.com/FreeBSDDesktop/freebsd-ports/branches/feature/input/devel/py-pyudev /usr/ports/devel/py-pyudev
+- ports_build: |
+ cd /usr/ports/graphics/wayland-protocols/ && sudo make install
+ cd /usr/ports/x11/libinput/ && sudo make install clean
+ # v4l_compat is a dependency of libinput, but the version in the ports tree
+ # conflicts with the new evdev-proto. It can be safely removed though.
+ sudo pkg remove -fy v4l_compat
+ cd /usr/ports/devel/evdev-proto && sudo make install clean
+- fixup_epoll: |
+ cat << 'EOF' | sudo tee /usr/local/libdata/pkgconfig/epoll-shim.pc
+ prefix=/usr/local
+ exec_prefix=\$\{\$prefix\}
+ libdir=${prefix}/lib
+ sharedlibdir=${prefix}/lib
+ includedir=${prefix}/include/libepoll-shim
+ Name: epoll-shim
+ Description: epoll shim implemented using kevent
+ Version: 0
+ Requires:
+ Libs: -L${libdir} -L${sharedlibdir} -lepoll-shim -lthr
+ Cflags: -I${includedir}
+ EOF
+- wlroots: |
+ cd wlroots
+ meson build
+ ninja -C build
diff --git a/README.md b/README.md
index 08844313..1519d0b3 100644
--- a/README.md
+++ b/README.md
@@ -30,8 +30,8 @@ development tools - or any subset of these features you like, because all of
them work independently of one another and freely compose with anything you want
to implement yourself.
-**Status**: prior to 1.0 the API is not stable, but we've done most of the work
-and various projects are using wlroots to build Wayland compositors with.
+Check out our [wiki](https://github.com/swaywm/wlroots/wiki/Getting-started) to
+get started with wlroots.
wlroots is developed under the direction of the
[sway](https://github.com/swaywm/sway) project. A variety of wrapper libraries
@@ -74,9 +74,6 @@ Run these commands:
meson build
ninja -C build
-On FreeBSD, you need to pass an extra flag to prevent a linking error:
-`meson build -D b_lundef=false`.
-
Install like so:
sudo ninja -C build install
@@ -86,7 +83,8 @@ Install like so:
wlroots comes with a test compositor called rootston, which demonstrates the
features of the library and is used as a testbed for the development of the
library. It may also be useful as a reference for understanding how to use
-various wlroots features.
+various wlroots features, but it's not considered a production-quality codebase
+and is not designed for daily use.
If you followed the build instructions above the rootston executable can be
found at `./build/rootston/rootston`. To use it, refer to the example config at
diff --git a/backend/backend.c b/backend/backend.c
index 78b90007..c5dcdb48 100644
--- a/backend/backend.c
+++ b/backend/backend.c
@@ -15,6 +15,7 @@
#include <wlr/backend/wayland.h>
#include <wlr/config.h>
#include <wlr/util/log.h>
+#include "backend/multi.h"
/* WLR_HAS_X11_BACKEND needs to be after wlr/config.h */
#ifdef WLR_HAS_X11_BACKEND
@@ -56,6 +57,20 @@ struct wlr_renderer *wlr_backend_get_renderer(struct wlr_backend *backend) {
return NULL;
}
+struct wlr_session *wlr_backend_get_session(struct wlr_backend *backend) {
+ if (backend->impl->get_session) {
+ return backend->impl->get_session(backend);
+ }
+ return NULL;
+}
+
+clockid_t wlr_backend_get_presentation_clock(struct wlr_backend *backend) {
+ if (backend->impl->get_presentation_clock) {
+ return backend->impl->get_presentation_clock(backend);
+ }
+ return CLOCK_MONOTONIC;
+}
+
static size_t parse_outputs_env(const char *name) {
const char *outputs_str = getenv(name);
if (outputs_str == NULL) {
@@ -158,10 +173,12 @@ static struct wlr_backend *attempt_backend_by_name(struct wl_display *display,
return attempt_headless_backend(display, create_renderer_func);
} else if (strcmp(name, "drm") == 0 || strcmp(name, "libinput") == 0) {
// DRM and libinput need a session
- *session = wlr_session_create(display);
if (!*session) {
- wlr_log(WLR_ERROR, "failed to start a session");
- return NULL;
+ *session = wlr_session_create(display);
+ if (!*session) {
+ wlr_log(WLR_ERROR, "failed to start a session");
+ return NULL;
+ }
}
if (strcmp(name, "libinput") == 0) {
@@ -178,13 +195,12 @@ static struct wlr_backend *attempt_backend_by_name(struct wl_display *display,
struct wlr_backend *wlr_backend_autocreate(struct wl_display *display,
wlr_renderer_create_func_t create_renderer_func) {
struct wlr_backend *backend = wlr_multi_backend_create(display);
+ struct wlr_multi_backend *multi = (struct wlr_multi_backend *)backend;
if (!backend) {
wlr_log(WLR_ERROR, "could not allocate multibackend");
return NULL;
}
- struct wlr_session *session = NULL;
-
char *names = getenv("WLR_BACKENDS");
if (names) {
names = strdup(names);
@@ -197,20 +213,20 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display,
char *saveptr;
char *name = strtok_r(names, ",", &saveptr);
while (name != NULL) {
- struct wlr_backend *subbackend =
- attempt_backend_by_name(display, backend, &session, name, create_renderer_func);
+ struct wlr_backend *subbackend = attempt_backend_by_name(display,
+ backend, &multi->session, name, create_renderer_func);
if (subbackend == NULL) {
wlr_log(WLR_ERROR, "failed to start backend '%s'", name);
+ wlr_session_destroy(multi->session);
wlr_backend_destroy(backend);
- wlr_session_destroy(session);
free(names);
return NULL;
}
if (!wlr_multi_backend_add(backend, subbackend)) {
wlr_log(WLR_ERROR, "failed to add backend '%s'", name);
+ wlr_session_destroy(multi->session);
wlr_backend_destroy(backend);
- wlr_session_destroy(session);
free(names);
return NULL;
}
@@ -245,29 +261,30 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display,
#endif
// Attempt DRM+libinput
- session = wlr_session_create(display);
- if (!session) {
+ multi->session = wlr_session_create(display);
+ if (!multi->session) {
wlr_log(WLR_ERROR, "Failed to start a DRM session");
wlr_backend_destroy(backend);
return NULL;
}
- struct wlr_backend *libinput = wlr_libinput_backend_create(display, session);
+ struct wlr_backend *libinput = wlr_libinput_backend_create(display,
+ multi->session);
if (!libinput) {
wlr_log(WLR_ERROR, "Failed to start libinput backend");
+ wlr_session_destroy(multi->session);
wlr_backend_destroy(backend);
- wlr_session_destroy(session);
return NULL;
}
wlr_multi_backend_add(backend, libinput);
- struct wlr_backend *primary_drm =
- attempt_drm_backend(display, backend, session, create_renderer_func);
+ struct wlr_backend *primary_drm = attempt_drm_backend(display, backend,
+ multi->session, create_renderer_func);
if (!primary_drm) {
wlr_log(WLR_ERROR, "Failed to open any DRM device");
wlr_backend_destroy(libinput);
+ wlr_session_destroy(multi->session);
wlr_backend_destroy(backend);
- wlr_session_destroy(session);
return NULL;
}
diff --git a/backend/drm/atomic.c b/backend/drm/atomic.c
index 90acac25..fc649d68 100644
--- a/backend/drm/atomic.c
+++ b/backend/drm/atomic.c
@@ -104,7 +104,8 @@ static bool atomic_crtc_pageflip(struct wlr_drm_backend *drm,
drmModeDestroyPropertyBlob(drm->fd, crtc->mode_id);
}
- if (drmModeCreatePropertyBlob(drm->fd, mode, sizeof(*mode), &crtc->mode_id)) {
+ if (drmModeCreatePropertyBlob(drm->fd, mode, sizeof(*mode),
+ &crtc->mode_id)) {
wlr_log_errno(WLR_ERROR, "Unable to create property blob");
return false;
}
@@ -120,6 +121,10 @@ static bool atomic_crtc_pageflip(struct wlr_drm_backend *drm,
struct atomic atom;
atomic_begin(crtc, &atom);
atomic_add(&atom, conn->id, conn->props.crtc_id, crtc->id);
+ if (mode != NULL && conn->props.link_status != 0) {
+ atomic_add(&atom, conn->id, conn->props.link_status,
+ DRM_MODE_LINK_STATUS_GOOD);
+ }
atomic_add(&atom, crtc->id, crtc->props.mode_id, crtc->mode_id);
atomic_add(&atom, crtc->id, crtc->props.active, 1);
set_plane_props(&atom, crtc->primary, crtc->id, fb_id, true);
@@ -200,14 +205,16 @@ static bool atomic_crtc_move_cursor(struct wlr_drm_backend *drm,
}
static bool atomic_crtc_set_gamma(struct wlr_drm_backend *drm,
- struct wlr_drm_crtc *crtc, uint16_t *r, uint16_t *g, uint16_t *b,
- uint32_t size) {
+ struct wlr_drm_crtc *crtc, size_t size,
+ uint16_t *r, uint16_t *g, uint16_t *b) {
// Fallback to legacy gamma interface when gamma properties are not available
- // (can happen on older intel gpu's that support gamma but not degamma)
- // TEMP: This is broken on AMDGPU. Always fallback to legacy until they get
- // it fixed. Ref https://bugs.freedesktop.org/show_bug.cgi?id=107459
- if (crtc->props.gamma_lut == 0 || true) {
- return legacy_iface.crtc_set_gamma(drm, crtc, r, g, b, size);
+ // (can happen on older Intel GPUs that support gamma but not degamma).
+ // TEMP: This is broken on AMDGPU. Provide a fallback to legacy until they
+ // get it fixed. Ref https://bugs.freedesktop.org/show_bug.cgi?id=107459
+ const char *no_atomic_str = getenv("WLR_DRM_NO_ATOMIC_GAMMA");
+ bool no_atomic = no_atomic_str != NULL && strcmp(no_atomic_str, "1") == 0;
+ if (crtc->props.gamma_lut == 0 || no_atomic) {
+ return legacy_iface.crtc_set_gamma(drm, crtc, size, r, g, b);
}
struct drm_color_lut *gamma = malloc(size * sizeof(struct drm_color_lut));
@@ -216,7 +223,7 @@ static bool atomic_crtc_set_gamma(struct wlr_drm_backend *drm,
return false;
}
- for (uint32_t i = 0; i < size; i++) {
+ for (size_t i = 0; i < size; i++) {
gamma[i].red = r[i];
gamma[i].green = g[i];
gamma[i].blue = b[i];
@@ -240,21 +247,20 @@ static bool atomic_crtc_set_gamma(struct wlr_drm_backend *drm,
return atomic_end(drm->fd, &atom);
}
-static uint32_t atomic_crtc_get_gamma_size(struct wlr_drm_backend *drm,
+static size_t atomic_crtc_get_gamma_size(struct wlr_drm_backend *drm,
struct wlr_drm_crtc *crtc) {
- uint64_t gamma_lut_size;
-
if (crtc->props.gamma_lut_size == 0) {
return legacy_iface.crtc_get_gamma_size(drm, crtc);
}
+ uint64_t gamma_lut_size;
if (!get_drm_prop(drm->fd, crtc->id, crtc->props.gamma_lut_size,
- &gamma_lut_size)) {
+ &gamma_lut_size)) {
wlr_log(WLR_ERROR, "Unable to get gamma lut size");
return 0;
}
- return (uint32_t)gamma_lut_size;
+ return (size_t)gamma_lut_size;
}
const struct wlr_drm_interface atomic_iface = {
diff --git a/backend/drm/backend.c b/backend/drm/backend.c
index bba79bda..a9082077 100644
--- a/backend/drm/backend.c
+++ b/backend/drm/backend.c
@@ -65,10 +65,16 @@ static struct wlr_renderer *backend_get_renderer(
}
}
+static clockid_t backend_get_presentation_clock(struct wlr_backend *backend) {
+ struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend);
+ return drm->clock;
+}
+
static struct wlr_backend_impl backend_impl = {
.start = backend_start,
.destroy = backend_destroy,
.get_renderer = backend_get_renderer,
+ .get_presentation_clock = backend_get_presentation_clock,
};
bool wlr_backend_is_drm(struct wlr_backend *b) {
@@ -101,6 +107,16 @@ static void session_signal(struct wl_listener *listener, void *data) {
(plane && plane->cursor_enabled) ? plane->cursor_bo : NULL);
drm->iface->crtc_move_cursor(drm, conn->crtc, conn->cursor_x,
conn->cursor_y);
+
+ if (conn->crtc->gamma_table != NULL) {
+ size_t size = conn->crtc->gamma_table_size;
+ uint16_t *r = conn->crtc->gamma_table;
+ uint16_t *g = conn->crtc->gamma_table + size;
+ uint16_t *b = conn->crtc->gamma_table + 2 * size;
+ drm->iface->crtc_set_gamma(drm, conn->crtc, size, r, g, b);
+ } else {
+ set_drm_connector_gamma(&conn->output, 0, NULL, NULL, NULL);
+ }
}
} else {
wlr_log(WLR_INFO, "DRM fd paused");
@@ -193,8 +209,3 @@ error_fd:
free(drm);
return NULL;
}
-
-struct wlr_session *wlr_drm_backend_get_session(struct wlr_backend *backend) {
- struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend);
- return drm->session;
-}
diff --git a/backend/drm/drm.c b/backend/drm/drm.c
index 8e7c421d..9fd5c6a3 100644
--- a/backend/drm/drm.c
+++ b/backend/drm/drm.c
@@ -1,3 +1,4 @@
+#define _POSIX_C_SOURCE 200112L
#include <assert.h>
#include <drm_mode.h>
#include <EGL/egl.h>
@@ -27,8 +28,8 @@
#include "util/signal.h"
bool check_drm_features(struct wlr_drm_backend *drm) {
+ uint64_t cap;
if (drm->parent) {
- uint64_t cap;
if (drmGetCap(drm->fd, DRM_CAP_PRIME, &cap) ||
!(cap & DRM_PRIME_CAP_IMPORT)) {
wlr_log(WLR_ERROR,
@@ -51,16 +52,21 @@ bool check_drm_features(struct wlr_drm_backend *drm) {
const char *no_atomic = getenv("WLR_DRM_NO_ATOMIC");
if (no_atomic && strcmp(no_atomic, "1") == 0) {
- wlr_log(WLR_DEBUG, "WLR_DRM_NO_ATOMIC set, forcing legacy DRM interface");
+ wlr_log(WLR_DEBUG,
+ "WLR_DRM_NO_ATOMIC set, forcing legacy DRM interface");
drm->iface = &legacy_iface;
} else if (drmSetClientCap(drm->fd, DRM_CLIENT_CAP_ATOMIC, 1)) {
- wlr_log(WLR_DEBUG, "Atomic modesetting unsupported, using legacy DRM interface");
+ wlr_log(WLR_DEBUG,
+ "Atomic modesetting unsupported, using legacy DRM interface");
drm->iface = &legacy_iface;
} else {
wlr_log(WLR_DEBUG, "Using atomic DRM interface");
drm->iface = &atomic_iface;
}
+ int ret = drmGetCap(drm->fd, DRM_CAP_TIMESTAMP_MONOTONIC, &cap);
+ drm->clock = (ret == 0 && cap == 1) ? CLOCK_MONOTONIC : CLOCK_REALTIME;
+
return true;
}
@@ -150,6 +156,11 @@ bool init_drm_resources(struct wlr_drm_backend *drm) {
wlr_log(WLR_INFO, "Found %d DRM CRTCs", res->count_crtcs);
drm->num_crtcs = res->count_crtcs;
+ if (drm->num_crtcs == 0) {
+ drmModeFreeResources(res);
+ return true;
+ }
+
drm->crtcs = calloc(drm->num_crtcs, sizeof(drm->crtcs[0]));
if (!drm->crtcs) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
@@ -193,6 +204,7 @@ void finish_drm_resources(struct wlr_drm_backend *drm) {
if (crtc->gamma_lut) {
drmModeDestroyPropertyBlob(drm->fd, crtc->gamma_lut);
}
+ free(crtc->gamma_table);
}
for (size_t i = 0; i < drm->num_planes; ++i) {
struct wlr_drm_plane *plane = &drm->planes[i];
@@ -251,7 +263,7 @@ static bool drm_connector_swap_buffers(struct wlr_output *output,
return true;
}
-static void fill_empty_gamma_table(uint32_t size,
+static void fill_empty_gamma_table(size_t size,
uint16_t *r, uint16_t *g, uint16_t *b) {
for (uint32_t i = 0; i < size; ++i) {
uint16_t val = (uint32_t)0xffff * i / (size - 1);
@@ -259,7 +271,7 @@ static void fill_empty_gamma_table(uint32_t size,
}
}
-static uint32_t drm_connector_get_gamma_size(struct wlr_output *output) {
+static size_t drm_connector_get_gamma_size(struct wlr_output *output) {
struct wlr_drm_connector *conn = get_drm_connector_from_output(output);
struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend);
@@ -270,8 +282,8 @@ static uint32_t drm_connector_get_gamma_size(struct wlr_output *output) {
return 0;
}
-static bool drm_connector_set_gamma(struct wlr_output *output,
- uint32_t size, uint16_t *r, uint16_t *g, uint16_t *b) {
+bool set_drm_connector_gamma(struct wlr_output *output, size_t size,
+ const uint16_t *r, const uint16_t *g, const uint16_t *b) {
struct wlr_drm_connector *conn = get_drm_connector_from_output(output);
struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend);
@@ -279,25 +291,42 @@ static bool drm_connector_set_gamma(struct wlr_output *output,
return false;
}
- uint16_t *reset_table = NULL;
+ bool reset = false;
if (size == 0) {
+ reset = true;
size = drm_connector_get_gamma_size(output);
- reset_table = malloc(3 * size * sizeof(uint16_t));
- if (reset_table == NULL) {
- wlr_log(WLR_ERROR, "Failed to allocate gamma table");
+ if (size == 0) {
return false;
}
- r = reset_table;
- g = reset_table + size;
- b = reset_table + 2 * size;
- fill_empty_gamma_table(size, r, g, b);
}
- bool ok = drm->iface->crtc_set_gamma(drm, conn->crtc, r, g, b, size);
+ uint16_t *gamma_table = malloc(3 * size * sizeof(uint16_t));
+ if (gamma_table == NULL) {
+ wlr_log(WLR_ERROR, "Failed to allocate gamma table");
+ return false;
+ }
+ uint16_t *_r = gamma_table;
+ uint16_t *_g = gamma_table + size;
+ uint16_t *_b = gamma_table + 2 * size;
+
+ if (reset) {
+ fill_empty_gamma_table(size, _r, _g, _b);
+ } else {
+ memcpy(_r, r, size * sizeof(uint16_t));
+ memcpy(_g, g, size * sizeof(uint16_t));
+ memcpy(_b, b, size * sizeof(uint16_t));
+ }
+
+ bool ok = drm->iface->crtc_set_gamma(drm, conn->crtc, size, _r, _g, _b);
if (ok) {
wlr_output_update_needs_swap(output);
+
+ free(conn->crtc->gamma_table);
+ conn->crtc->gamma_table = gamma_table;
+ conn->crtc->gamma_table_size = size;
+ } else {
+ free(gamma_table);
}
- free(reset_table);
return ok;
}
@@ -409,9 +438,9 @@ static void realloc_planes(struct wlr_drm_backend *drm, const uint32_t *crtc_in,
continue;
}
- uint32_t possible[drm->num_type_planes[type]];
- uint32_t crtc[drm->num_crtcs];
- uint32_t crtc_res[drm->num_crtcs];
+ uint32_t possible[drm->num_type_planes[type] + 1];
+ uint32_t crtc[drm->num_crtcs + 1];
+ uint32_t crtc_res[drm->num_crtcs + 1];
for (size_t i = 0; i < drm->num_type_planes[type]; ++i) {
possible[i] = drm->type_planes[type][i].possible_crtcs;
@@ -706,9 +735,51 @@ static bool drm_connector_move_cursor(struct wlr_output *output,
return ok;
}
+static bool drm_connector_schedule_frame(struct wlr_output *output) {
+ struct wlr_drm_connector *conn = get_drm_connector_from_output(output);
+ struct wlr_drm_backend *drm = get_drm_backend_from_backend(output->backend);
+ if (!drm->session->active) {
+ return false;
+ }
+
+ // We need to figure out where we are in the vblank cycle
+ // TODO: try using drmWaitVBlank and fallback to pageflipping
+
+ struct wlr_drm_crtc *crtc = conn->crtc;
+ if (!crtc) {
+ return false;
+ }
+ struct wlr_drm_plane *plane = crtc->primary;
+ struct gbm_bo *bo = plane->surf.back;
+ if (!bo) {
+ // We haven't swapped buffers yet -- can't do a pageflip
+ wlr_output_send_frame(output);
+ return true;
+ }
+ if (drm->parent) {
+ bo = copy_drm_surface_mgpu(&plane->mgpu_surf, bo);
+ }
+ uint32_t fb_id = get_fb_for_bo(bo);
+
+ if (conn->pageflip_pending) {
+ wlr_log(WLR_ERROR, "Skipping pageflip on output '%s'",
+ conn->output.name);
+ return true;
+ }
+
+ if (!drm->iface->crtc_pageflip(drm, conn, crtc, fb_id, NULL)) {
+ return false;
+ }
+
+ conn->pageflip_pending = true;
+ wlr_output_update_enabled(output, true);
+ return true;
+}
+
static void drm_connector_destroy(struct wlr_output *output) {
struct wlr_drm_connector *conn = get_drm_connector_from_output(output);
drm_connector_cleanup(conn);
+ drmModeFreeCrtc(conn->old_crtc);
wl_event_source_remove(conn->retry_pageflip);
wl_list_remove(&conn->link);
free(conn);
@@ -723,9 +794,10 @@ static const struct wlr_output_impl output_impl = {
.destroy = drm_connector_destroy,
.make_current = drm_connector_make_current,
.swap_buffers = drm_connector_swap_buffers,
- .set_gamma = drm_connector_set_gamma,
+ .set_gamma = set_drm_connector_gamma,
.get_gamma_size = drm_connector_get_gamma_size,
.export_dmabuf = drm_connector_export_dmabuf,
+ .schedule_frame = drm_connector_schedule_frame,
};
bool wlr_output_is_drm(struct wlr_output *output) {
@@ -758,6 +830,8 @@ static void dealloc_crtc(struct wlr_drm_connector *conn) {
wlr_log(WLR_DEBUG, "De-allocating CRTC %zu for output '%s'",
conn->crtc - drm->crtcs, conn->output.name);
+ set_drm_connector_gamma(&conn->output, 0, NULL, NULL, NULL);
+
for (size_t type = 0; type < 3; ++type) {
struct wlr_drm_plane *plane = conn->crtc->planes[type];
if (plane == NULL) {
@@ -1013,6 +1087,24 @@ void scan_drm_connectors(struct wlr_drm_backend *drm) {
wlr_conn->crtc = NULL;
}
+ // This can only happen *after* hotplug, since we haven't read the
+ // connector properties yet
+ if (wlr_conn->props.link_status != 0) {
+ uint64_t link_status;
+ if (!get_drm_prop(drm->fd, wlr_conn->id,
+ wlr_conn->props.link_status, &link_status)) {
+ wlr_log(WLR_ERROR, "Failed to get link status for '%s'",
+ wlr_conn->output.name);
+ continue;
+ }
+
+ if (link_status == DRM_MODE_LINK_STATUS_BAD) {
+ // We need to reload our list of modes and force a modeset
+ wlr_log(WLR_INFO, "Bad link for '%s'", wlr_conn->output.name);
+ drm_connector_cleanup(wlr_conn);
+ }
+ }
+
if (wlr_conn->state == WLR_DRM_CONN_DISCONNECTED &&
drm_conn->connection == DRM_MODE_CONNECTED) {
wlr_log(WLR_INFO, "'%s' connected", wlr_conn->output.name);
@@ -1088,13 +1180,14 @@ void scan_drm_connectors(struct wlr_drm_backend *drm) {
wlr_log(WLR_INFO, "'%s' disappeared", conn->output.name);
drm_connector_cleanup(conn);
- drmModeFreeCrtc(conn->old_crtc);
- wl_event_source_remove(conn->retry_pageflip);
- wl_list_remove(&conn->link);
- free(conn);
+ if (conn->pageflip_pending) {
+ conn->state = WLR_DRM_CONN_DISAPPEARED;
+ } else {
+ wlr_output_destroy(&conn->output);
+ }
}
- bool changed_outputs[wl_list_length(&drm->outputs)];
+ bool changed_outputs[wl_list_length(&drm->outputs) + 1];
memset(changed_outputs, false, sizeof(changed_outputs));
for (size_t i = 0; i < new_outputs_len; ++i) {
struct wlr_drm_connector *conn = new_outputs[i];
@@ -1126,13 +1219,23 @@ void scan_drm_connectors(struct wlr_drm_backend *drm) {
attempt_enable_needs_modeset(drm);
}
+static int mhz_to_nsec(int mhz) {
+ return 1000000000000LL / mhz;
+}
+
static void page_flip_handler(int fd, unsigned seq,
- unsigned tv_sec, unsigned tv_usec, void *user) {
- struct wlr_drm_connector *conn = user;
+ unsigned tv_sec, unsigned tv_usec, void *data) {
+ struct wlr_drm_connector *conn = data;
struct wlr_drm_backend *drm =
get_drm_backend_from_backend(conn->output.backend);
conn->pageflip_pending = false;
+
+ if (conn->state == WLR_DRM_CONN_DISAPPEARED) {
+ wlr_output_destroy(&conn->output);
+ return;
+ }
+
if (conn->state != WLR_DRM_CONN_CONNECTED || conn->crtc == NULL) {
return;
}
@@ -1142,6 +1245,19 @@ static void page_flip_handler(int fd, unsigned seq,
post_drm_surface(&conn->crtc->primary->mgpu_surf);
}
+ struct timespec present_time = {
+ .tv_sec = tv_sec,
+ .tv_nsec = tv_usec * 1000,
+ };
+ struct wlr_output_event_present present_event = {
+ .when = &present_time,
+ .seq = seq,
+ .refresh = mhz_to_nsec(conn->output.refresh),
+ .flags = WLR_OUTPUT_PRESENT_VSYNC | WLR_OUTPUT_PRESENT_HW_CLOCK |
+ WLR_OUTPUT_PRESENT_HW_COMPLETION,
+ };
+ wlr_output_send_present(&conn->output, &present_event);
+
if (drm->session->active) {
wlr_output_send_frame(&conn->output);
}
@@ -1149,7 +1265,7 @@ static void page_flip_handler(int fd, unsigned seq,
int handle_drm_event(int fd, uint32_t mask, void *data) {
drmEventContext event = {
- .version = DRM_EVENT_CONTEXT_VERSION,
+ .version = 2,
.page_flip_handler = page_flip_handler,
};
@@ -1193,7 +1309,6 @@ void restore_drm_outputs(struct wlr_drm_backend *drm) {
drmModeSetCrtc(drm->fd, crtc->crtc_id, crtc->buffer_id, crtc->x, crtc->y,
&conn->id, 1, &crtc->mode);
- drmModeFreeCrtc(crtc);
}
}
@@ -1236,7 +1351,13 @@ static void drm_connector_cleanup(struct wlr_drm_connector *conn) {
memset(&conn->output.model, 0, sizeof(conn->output.model));
memset(&conn->output.serial, 0, sizeof(conn->output.serial));
- conn->pageflip_pending = false;
+ if (conn->output.idle_frame != NULL) {
+ wl_event_source_remove(conn->output.idle_frame);
+ conn->output.idle_frame = NULL;
+ }
+ conn->output.needs_swap = false;
+ conn->output.frame_pending = false;
+
/* Fallthrough */
case WLR_DRM_CONN_NEEDS_MODESET:
wlr_log(WLR_INFO, "Emitting destruction signal for '%s'",
@@ -1248,6 +1369,8 @@ static void drm_connector_cleanup(struct wlr_drm_connector *conn) {
break;
case WLR_DRM_CONN_DISCONNECTED:
break;
+ case WLR_DRM_CONN_DISAPPEARED:
+ return; // don't change state
}
conn->state = WLR_DRM_CONN_DISCONNECTED;
diff --git a/backend/drm/legacy.c b/backend/drm/legacy.c
index c205e167..182c7a95 100644
--- a/backend/drm/legacy.c
+++ b/backend/drm/legacy.c
@@ -63,14 +63,14 @@ bool legacy_crtc_move_cursor(struct wlr_drm_backend *drm,
}
bool legacy_crtc_set_gamma(struct wlr_drm_backend *drm,
- struct wlr_drm_crtc *crtc, uint16_t *r, uint16_t *g, uint16_t *b,
- uint32_t size) {
- return !drmModeCrtcSetGamma(drm->fd, crtc->id, size, r, g, b);
+ struct wlr_drm_crtc *crtc, size_t size,
+ uint16_t *r, uint16_t *g, uint16_t *b) {
+ return !drmModeCrtcSetGamma(drm->fd, crtc->id, (uint32_t)size, r, g, b);
}
-uint32_t legacy_crtc_get_gamma_size(struct wlr_drm_backend *drm,
+size_t legacy_crtc_get_gamma_size(struct wlr_drm_backend *drm,
struct wlr_drm_crtc *crtc) {
- return crtc->legacy_crtc->gamma_size;
+ return (size_t)crtc->legacy_crtc->gamma_size;
}
const struct wlr_drm_interface legacy_iface = {
diff --git a/backend/drm/properties.c b/backend/drm/properties.c
index 372de232..5ca4c8ac 100644
--- a/backend/drm/properties.c
+++ b/backend/drm/properties.c
@@ -19,9 +19,10 @@ struct prop_info {
static const struct prop_info connector_info[] = {
#define INDEX(name) (offsetof(union wlr_drm_connector_props, name) / sizeof(uint32_t))
- { "CRTC_ID", INDEX(crtc_id) },
- { "DPMS", INDEX(dpms) },
- { "EDID", INDEX(edid) },
+ { "CRTC_ID", INDEX(crtc_id) },
+ { "DPMS", INDEX(dpms) },
+ { "EDID", INDEX(edid) },
+ { "link-status", INDEX(link_status) },
#undef INDEX
};
@@ -87,7 +88,8 @@ static bool scan_properties(int fd, uint32_t id, uint32_t type, uint32_t *result
return true;
}
-bool get_drm_connector_props(int fd, uint32_t id, union wlr_drm_connector_props *out) {
+bool get_drm_connector_props(int fd, uint32_t id,
+ union wlr_drm_connector_props *out) {
return scan_properties(fd, id, DRM_MODE_OBJECT_CONNECTOR, out->props,
connector_info, sizeof(connector_info) / sizeof(connector_info[0]));
}
@@ -103,7 +105,8 @@ bool get_drm_plane_props(int fd, uint32_t id, union wlr_drm_plane_props *out) {
}
bool get_drm_prop(int fd, uint32_t obj, uint32_t prop, uint64_t *ret) {
- drmModeObjectProperties *props = drmModeObjectGetProperties(fd, obj, DRM_MODE_OBJECT_ANY);
+ drmModeObjectProperties *props =
+ drmModeObjectGetProperties(fd, obj, DRM_MODE_OBJECT_ANY);
if (!props) {
return false;
}
diff --git a/backend/drm/renderer.c b/backend/drm/renderer.c
index fa7d090e..70b1bcbe 100644
--- a/backend/drm/renderer.c
+++ b/backend/drm/renderer.c
@@ -30,8 +30,17 @@ bool init_drm_renderer(struct wlr_drm_backend *drm,
create_renderer_func = wlr_renderer_autocreate;
}
+ static EGLint config_attribs[] = {
+ EGL_RED_SIZE, 1,
+ EGL_GREEN_SIZE, 1,
+ EGL_BLUE_SIZE, 1,
+ EGL_ALPHA_SIZE, 1,
+ EGL_NONE,
+ };
+
renderer->wlr_rend = create_renderer_func(&renderer->egl,
- EGL_PLATFORM_GBM_MESA, renderer->gbm, NULL, GBM_FORMAT_ARGB8888);
+ EGL_PLATFORM_GBM_MESA, renderer->gbm,
+ config_attribs, GBM_FORMAT_ARGB8888);
if (!renderer->wlr_rend) {
wlr_log(WLR_ERROR, "Failed to create EGL/WLR renderer");
diff --git a/backend/drm/util.c b/backend/drm/util.c
index f9637c33..6b2c0981 100644
--- a/backend/drm/util.c
+++ b/backend/drm/util.c
@@ -32,21 +32,30 @@ static const char *get_manufacturer(uint16_t id) {
case ID('A', 'A', 'A'): return "Avolites Ltd";
case ID('A', 'C', 'I'): return "Ancor Communications Inc";
case ID('A', 'C', 'R'): return "Acer Technologies";
+ case ID('A', 'D', 'A'): return "Addi-Data GmbH";
case ID('A', 'P', 'P'): return "Apple Computer Inc";
+ case ID('A', 'S', 'K'): return "Ask A/S";
+ case ID('A', 'V', 'T'): return "Avtek (Electronics) Pty Ltd";
case ID('B', 'N', 'O'): return "Bang & Olufsen";
case ID('C', 'M', 'N'): return "Chimei Innolux Corporation";
case ID('C', 'M', 'O'): return "Chi Mei Optoelectronics corp.";
case ID('C', 'R', 'O'): return "Extraordinary Technologies PTY Limited";
case ID('D', 'E', 'L'): return "Dell Inc.";
+ case ID('D', 'G', 'C'): return "Data General Corporation";
case ID('D', 'O', 'N'): return "DENON, Ltd.";
case ID('E', 'N', 'C'): return "Eizo Nanao Corporation";
case ID('E', 'P', 'H'): return "Epiphan Systems Inc.";
+ case ID('E', 'X', 'P'): return "Data Export Corporation";
+ case ID('F', 'N', 'I'): return "Funai Electric Co., Ltd.";
case ID('F', 'U', 'S'): return "Fujitsu Siemens Computers GmbH";
case ID('G', 'S', 'M'): return "Goldstar Company Ltd";
case ID('H', 'I', 'Q'): return "Kaohsiung Opto Electronics Americas, Inc.";
case ID('H', 'S', 'D'): return "HannStar Display Corp";
+ case ID('H', 'T', 'C'): return "Hitachi Ltd";
case ID('H', 'W', 'P'): return "Hewlett Packard";
case ID('I', 'N', 'T'): return "Interphase Corporation";
+ case ID('I', 'N', 'X'): return "Communications Supply Corporation (A division of WESCO)";
+ case ID('I', 'T', 'E'): return "Integrated Tech Express Inc";
case ID('I', 'V', 'M'): return "Iiyama North America";
case ID('L', 'E', 'N'): return "Lenovo Group Limited";
case ID('M', 'A', 'X'): return "Rogen Tech Distribution Inc";
@@ -55,6 +64,7 @@ static const char *get_manufacturer(uint16_t id) {
case ID('M', 'T', 'C'): return "Mars-Tech Corporation";
case ID('M', 'T', 'X'): return "Matrox";
case ID('N', 'E', 'C'): return "NEC Corporation";
+ case ID('N', 'E', 'X'): return "Nexgen Mediatech Inc.";
case ID('O', 'N', 'K'): return "ONKYO Corporation";
case ID('O', 'R', 'N'): return "ORION ELECTRIC CO., LTD.";
case ID('O', 'T', 'M'): return "Optoma Corporation";
@@ -63,15 +73,24 @@ static const char *get_manufacturer(uint16_t id) {
case ID('P', 'I', 'O'): return "Pioneer Electronic Corporation";
case ID('P', 'N', 'R'): return "Planar Systems, Inc.";
case ID('Q', 'D', 'S'): return "Quanta Display Inc.";
+ case ID('R', 'A', 'T'): return "Rent-A-Tech";
+ case ID('R', 'E', 'N'): return "Renesas Technology Corp.";
case ID('S', 'A', 'M'): return "Samsung Electric Company";
+ case ID('S', 'A', 'N'): return "Sanyo Electric Co., Ltd.";
case ID('S', 'E', 'C'): return "Seiko Epson Corporation";
case ID('S', 'H', 'P'): return "Sharp Corporation";
case ID('S', 'I', 'I'): return "Silicon Image, Inc.";
case ID('S', 'N', 'Y'): return "Sony";
+ case ID('S', 'T', 'D'): return "STD Computer Inc";
+ case ID('S', 'V', 'S'): return "SVSI";
+ case ID('S', 'Y', 'N'): return "Synaptics Inc";
+ case ID('T', 'C', 'L'): return "Technical Concepts Ltd";
case ID('T', 'O', 'P'): return "Orion Communications Co., Ltd.";
case ID('T', 'S', 'B'): return "Toshiba America Info Systems Inc";
case ID('T', 'S', 'T'): return "Transtream Inc";
case ID('U', 'N', 'K'): return "Unknown";
+ case ID('V', 'E', 'S'): return "Vestel Elektronik Sanayi ve Ticaret A. S.";
+ case ID('V', 'I', 'T'): return "Visitech AS";
case ID('V', 'I', 'Z'): return "VIZIO, Inc";
case ID('V', 'S', 'C'): return "ViewSonic Corporation";
case ID('Y', 'M', 'H'): return "Yamaha Corporation";
diff --git a/backend/headless/output.c b/backend/headless/output.c
index 143900d4..3cb35dce 100644
--- a/backend/headless/output.c
+++ b/backend/headless/output.c
@@ -67,7 +67,9 @@ static bool output_make_current(struct wlr_output *wlr_output, int *buffer_age)
static bool output_swap_buffers(struct wlr_output *wlr_output,
pixman_region32_t *damage) {
- return true; // No-op
+ // Nothing needs to be done for pbuffers
+ wlr_output_send_present(wlr_output, NULL);
+ return true;
}
static void output_destroy(struct wlr_output *wlr_output) {
diff --git a/backend/multi/backend.c b/backend/multi/backend.c
index 3707fe34..50851109 100644
--- a/backend/multi/backend.c
+++ b/backend/multi/backend.c
@@ -1,7 +1,8 @@
+#define _POSIX_C_SOURCE 200112L
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
-#include <wlr/backend/drm.h>
+#include <time.h>
#include <wlr/backend/interface.h>
#include <wlr/backend/session.h>
#include <wlr/util/log.h>
@@ -72,10 +73,32 @@ static struct wlr_renderer *multi_backend_get_renderer(
return NULL;
}
+static struct wlr_session *multi_backend_get_session(
+ struct wlr_backend *_backend) {
+ struct wlr_multi_backend *backend = multi_backend_from_backend(_backend);
+ return backend->session;
+}
+
+static clockid_t multi_backend_get_presentation_clock(
+ struct wlr_backend *backend) {
+ struct wlr_multi_backend *multi = multi_backend_from_backend(backend);
+
+ struct subbackend_state *sub;
+ wl_list_for_each(sub, &multi->backends, link) {
+ if (sub->backend->impl->get_presentation_clock) {
+ return wlr_backend_get_presentation_clock(sub->backend);
+ }
+ }
+
+ return CLOCK_MONOTONIC;
+}
+
struct wlr_backend_impl backend_impl = {
.start = multi_backend_start,
.destroy = multi_backend_destroy,
.get_renderer = multi_backend_get_renderer,
+ .get_session = multi_backend_get_session,
+ .get_presentation_clock = multi_backend_get_presentation_clock,
};
static void handle_display_destroy(struct wl_listener *listener, void *data) {
@@ -191,17 +214,6 @@ void wlr_multi_backend_remove(struct wlr_backend *_multi,
}
}
-struct wlr_session *wlr_multi_get_session(struct wlr_backend *_backend) {
- struct wlr_multi_backend *backend = multi_backend_from_backend(_backend);
- struct subbackend_state *sub;
- wl_list_for_each(sub, &backend->backends, link) {
- if (wlr_backend_is_drm(sub->backend)) {
- return wlr_drm_backend_get_session(sub->backend);
- }
- }
- return NULL;
-}
-
bool wlr_multi_is_empty(struct wlr_backend *_backend) {
assert(wlr_backend_is_multi(_backend));
struct wlr_multi_backend *backend = (struct wlr_multi_backend *)_backend;
diff --git a/backend/session/logind.c b/backend/session/logind.c
index 39143a2f..b44112e6 100644
--- a/backend/session/logind.c
+++ b/backend/session/logind.c
@@ -35,6 +35,11 @@ struct logind_session {
char *id;
char *path;
+
+ // specifies whether a drm device was taken
+ // if so, the session will be (de)activated with the drm fd,
+ // otherwise with the dbus PropertiesChanged on "active" signal
+ bool has_drm;
};
static struct logind_session *logind_session_from_session(
@@ -57,6 +62,10 @@ static int logind_take_device(struct wlr_session *base, const char *path) {
return -1;
}
+ if (major(st.st_rdev) == DRM_MAJOR) {
+ session->has_drm = true;
+ }
+
ret = sd_bus_call_method(session->bus, "org.freedesktop.login1",
session->path, "org.freedesktop.login1.Session", "TakeDevice",
&error, &msg, "uu", major(st.st_rdev), minor(st.st_rdev));
@@ -262,6 +271,7 @@ static int pause_device(sd_bus_message *msg, void *userdata, sd_bus_error *ret_e
}
if (major == DRM_MAJOR) {
+ assert(session->has_drm);
session->base.active = false;
wlr_signal_emit_safe(&session->base.session_signal, session);
}
@@ -307,6 +317,115 @@ error:
return 0;
}
+static int properties_changed(sd_bus_message *msg, void *userdata,
+ sd_bus_error *ret_error) {
+ struct logind_session *session = userdata;
+ int ret = 0;
+
+ // if we have a drm fd we don't depend on this
+ if (session->has_drm) {
+ return 0;
+ }
+
+ // PropertiesChanged arg 1: interface
+ const char *interface;
+ ret = sd_bus_message_read_basic(msg, 's', &interface); // skip path
+ if (ret < 0) {
+ goto error;
+ }
+
+ if (strcmp(interface, "org.freedesktop.login1.Session") != 0) {
+ // not interesting for us; ignore
+ wlr_log(WLR_DEBUG, "ignoring PropertiesChanged from %s", interface);
+ return 0;
+ }
+
+ // PropertiesChanged arg 2: changed properties with values
+ ret = sd_bus_message_enter_container(msg, 'a', "{sv}");
+ if (ret < 0) {
+ goto error;
+ }
+
+ const char *s;
+ while ((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) {
+ ret = sd_bus_message_read_basic(msg, 's', &s);
+ if (ret < 0) {
+ goto error;
+ }
+
+ if (strcmp(s, "Active") == 0) {
+ int ret;
+ ret = sd_bus_message_enter_container(msg, 'v', "b");
+ if (ret < 0) {
+ goto error;
+ }
+
+ bool active;
+ ret = sd_bus_message_read_basic(msg, 'b', &active);
+ if (ret < 0) {
+ goto error;
+ }
+
+ if (session->base.active != active) {
+ session->base.active = active;
+ wlr_signal_emit_safe(&session->base.session_signal, session);
+ }
+ return 0;
+ } else {
+ sd_bus_message_skip(msg, "{sv}");
+ }
+
+ ret = sd_bus_message_exit_container(msg);
+ if (ret < 0) {
+ goto error;
+ }
+ }
+
+ if (ret < 0) {
+ goto error;
+ }
+
+ ret = sd_bus_message_exit_container(msg);
+ if (ret < 0) {
+ goto error;
+ }
+
+ // PropertiesChanged arg 3: changed properties without values
+ sd_bus_message_enter_container(msg, 'a', "s");
+ while ((ret = sd_bus_message_read_basic(msg, 's', &s)) > 0) {
+ if (strcmp(s, "Active") == 0) {
+ sd_bus_error error = SD_BUS_ERROR_NULL;
+ bool active;
+ ret = sd_bus_get_property_trivial(session->bus,
+ "org.freedesktop.login1", session->path,
+ "org.freedesktop.login1.Session", "Active", &error,
+ 'b', &active);
+ if (ret < 0) {
+ wlr_log(WLR_ERROR, "Failed to get 'Active' property: '%s' (%s)",
+ error.message, strerror(ret));
+ return 0;
+ }
+
+ if (session->base.active != active) {
+ session->base.active = active;
+ wlr_signal_emit_safe(&session->base.session_signal, session);
+ }
+ return 0;
+ }
+ }
+
+ if (ret < 0) {
+ goto error;
+ }
+
+ return 0;
+
+error:
+ wlr_log(WLR_ERROR, "Failed to parse D-Bus PropertiesChanged %s",
+ strerror(-ret));
+ return 0;
+}
+
static bool add_signal_matches(struct logind_session *session) {
int ret;
@@ -338,6 +457,14 @@ static bool add_signal_matches(struct logind_session *session) {
return false;
}
+ ret = sd_bus_match_signal(session->bus, NULL, "org.freedesktop.login1",
+ session->path, "org.freedesktop.DBus.Properties", "PropertiesChanged",
+ properties_changed, session);
+ if (ret < 0) {
+ wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret));
+ return false;
+ }
+
return true;
}
diff --git a/backend/wayland/output.c b/backend/wayland/output.c
index 4eb99a3b..f90c6009 100644
--- a/backend/wayland/output.c
+++ b/backend/wayland/output.c
@@ -66,8 +66,14 @@ static bool output_swap_buffers(struct wlr_output *wlr_output,
output->frame_callback = wl_surface_frame(output->surface);
wl_callback_add_listener(output->frame_callback, &frame_listener, output);
- return wlr_egl_swap_buffers(&output->backend->egl, output->egl_surface,
- damage);
+ if (!wlr_egl_swap_buffers(&output->backend->egl,
+ output->egl_surface, damage)) {
+ return false;
+ }
+
+ // TODO: if available, use the presentation-time protocol
+ wlr_output_send_present(wlr_output, NULL);
+ return true;
}
static void output_transform(struct wlr_output *wlr_output,
@@ -186,11 +192,25 @@ void update_wl_output_cursor(struct wlr_wl_output *output) {
}
}
-bool output_move_cursor(struct wlr_output *_output, int x, int y) {
+static bool output_move_cursor(struct wlr_output *_output, int x, int y) {
// TODO: only return true if x == current x and y == current y
return true;
}
+static bool output_schedule_frame(struct wlr_output *wlr_output) {
+ struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
+
+ if (output->frame_callback != NULL) {
+ wlr_log(WLR_ERROR, "Skipping frame scheduling");
+ return true;
+ }
+
+ output->frame_callback = wl_surface_frame(output->surface);
+ wl_callback_add_listener(output->frame_callback, &frame_listener, output);
+ wl_surface_commit(output->surface);
+ return true;
+}
+
static const struct wlr_output_impl output_impl = {
.set_custom_mode = output_set_custom_mode,
.transform = output_transform,
@@ -199,6 +219,7 @@ static const struct wlr_output_impl output_impl = {
.swap_buffers = output_swap_buffers,
.set_cursor = output_set_cursor,
.move_cursor = output_move_cursor,
+ .schedule_frame = output_schedule_frame,
};
bool wlr_output_is_wl(struct wlr_output *wlr_output) {
diff --git a/backend/x11/output.c b/backend/x11/output.c
index e0210b1a..1ac12a8d 100644
--- a/backend/x11/output.c
+++ b/backend/x11/output.c
@@ -102,7 +102,12 @@ static bool output_swap_buffers(struct wlr_output *wlr_output,
struct wlr_x11_output *output = (struct wlr_x11_output *)wlr_output;
struct wlr_x11_backend *x11 = output->x11;
- return wlr_egl_swap_buffers(&x11->egl, output->surf, damage);
+ if (!wlr_egl_swap_buffers(&x11->egl, output->surf, damage)) {
+ return false;
+ }
+
+ wlr_output_send_present(wlr_output, NULL);
+ return true;
}
static const struct wlr_output_impl output_impl = {
diff --git a/docs/env_vars.md b/docs/env_vars.md
index ef12e0b3..b67d1652 100644
--- a/docs/env_vars.md
+++ b/docs/env_vars.md
@@ -7,6 +7,8 @@ wlroots specific
considered the primary DRM device.
* *WLR_DRM_NO_ATOMIC*: set to 1 to use legacy DRM interface instead of atomic
mode setting
+* *WLR_DRM_NO_ATOMIC_GAMMA*: set to 1 to use legacy DRM interface for gamma
+ control instead of the atomic interface
* *WLR_LIBINPUT_NO_DEVICES*: set to 1 to not fail without any input devices
* *WLR_BACKENDS*: comma-separated list of backends to use (available backends:
wayland, x11, headless)
@@ -30,4 +32,3 @@ generic
* *WAYLAND_DISPLAY*, *_WAYLAND_DISPLAY*, *WAYLAND_SOCKET*: if set probe Wayland
backend in *wlr_backend_autocreate*
* *XCURSOR_PATH*: directory where xcursors are located
-* *XDG_RUNTIME_DIR*: used to store non persistent temporary files
diff --git a/examples/dmabuf-capture.c b/examples/dmabuf-capture.c
index 2c510b5a..abdb146a 100644
--- a/examples/dmabuf-capture.c
+++ b/examples/dmabuf-capture.c
@@ -12,7 +12,7 @@
#include <unistd.h>
#include <pthread.h>
#include <stdbool.h>
-#include <libdrm/drm_fourcc.h>
+#include <drm_fourcc.h>
#include "wlr-export-dmabuf-unstable-v1-client-protocol.h"
struct wayland_output {
diff --git a/examples/input-method.c b/examples/input-method.c
new file mode 100644
index 00000000..3e485d1c
--- /dev/null
+++ b/examples/input-method.c
@@ -0,0 +1,402 @@
+#define _POSIX_C_SOURCE 200809L
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/timerfd.h>
+#include <unistd.h>
+#include <wayland-client.h>
+#include <wayland-egl.h>
+#include <wlr/render/egl.h>
+#include "input-method-unstable-v2-client-protocol.h"
+#include "text-input-unstable-v3-client-protocol.h"
+#include "xdg-shell-client-protocol.h"
+
+const char usage[] = "Usage: input-method [seconds]\n\
+\n\
+Creates an input method using the input-method protocol.\n\
+\n\
+Whenever a text input is activated, this program sends a few sequences of\n\
+commands and checks the validity of the responses, relying on returned\n\
+surrounding text.\n\
+\n\
+The \"seconds\" argument is optional and defines the maximum delay between\n\
+stages.";
+
+struct input_method_state {
+ enum zwp_text_input_v3_change_cause change_cause;
+ struct {
+ enum zwp_text_input_v3_content_hint hint;
+ enum zwp_text_input_v3_content_purpose purpose;
+ } content_type;
+ struct {
+ char *text;
+ uint32_t cursor;
+ uint32_t anchor;
+ } surrounding;
+};
+
+static int sleeptime = 0;
+
+static struct wl_display *display = NULL;
+static struct wl_compositor *compositor = NULL;
+static struct wl_seat *seat = NULL;
+static struct zwp_input_method_manager_v2 *input_method_manager = NULL;
+static struct zwp_input_method_v2 *input_method = NULL;
+
+struct input_method_state pending;
+struct input_method_state current;
+
+static uint32_t serial = 0;
+bool active = false;
+bool pending_active = false;
+bool unavailable = false;
+bool running = false;
+
+uint32_t update_stage = 0;
+
+int timer_fd = 0;
+
+static void print_state_diff(struct input_method_state previous,
+ struct input_method_state future) {
+ if (previous.content_type.hint != future.content_type.hint) {
+ char *strs[] = { "COMPLETION", "SPELLCHECK", "AUTO_CAPITALIZATION",
+ "LOWERCASE", "UPPERCASE", "TITLECASE", "HIDDEN_TEXT",
+ "SENSITIVE_DATA", "LATIN", "MULTILINE"};
+ printf("content_type.hint:");
+ uint32_t hint = future.content_type.hint;
+ if (!hint) {
+ printf(" NONE");
+ }
+ for (unsigned i = 0; i < sizeof(strs) / sizeof(*strs); i++) {
+ if (hint & 1 << i) {
+ printf(" %s", strs[i]);
+ }
+ }
+ printf("\n");
+ }
+ if (previous.content_type.purpose != future.content_type.purpose) {
+ char *strs[] = { "NORMAL", "ALPHA", "DIGITS", "NUMBER", "PHONE", "URL",
+ "EMAIL", "NAME", "PASSWORD", "PIN", "DATE", "TIME", "DATETIME",
+ "TERMINAL" };
+ printf("content_type.purpose: %s\n", strs[future.content_type.purpose]);
+ }
+ if (!!previous.surrounding.text != !!future.surrounding.text
+ || (previous.surrounding.text && future.surrounding.text
+ && strcmp(previous.surrounding.text, future.surrounding.text) != 0)
+ || previous.surrounding.anchor != future.surrounding.anchor
+ || previous.surrounding.cursor != future.surrounding.cursor) {
+ char *text = future.surrounding.text;
+ if (!text) {
+ printf("Removed surrounding text\n");
+ } else {
+ printf("Surrounding text: %s\n", text);
+ uint32_t anchor = future.surrounding.anchor;
+ uint32_t cursor = future.surrounding.cursor;
+ if (cursor == anchor) {
+ char *temp = strndup(text, cursor);
+ printf("Cursor after %d: %s\n", cursor, temp);
+ free(temp);
+ } else {
+ if (cursor > anchor) {
+ uint32_t tmp = anchor;
+ anchor = cursor;
+ cursor = tmp;
+ }
+ char *temp = strndup(&text[cursor], anchor - cursor);
+ printf("Selection: %s\n", temp);
+ free(temp);
+ }
+ }
+ }
+ if (previous.change_cause != future.change_cause) {
+ char *strs[] = { "INPUT_METHOD", "OTHER" };
+ printf("Change cause: %s\n", strs[future.change_cause]);
+ }
+}
+
+static void handle_content_type(void *data,
+ struct zwp_input_method_v2 *zwp_input_method_v2,
+ uint32_t hint, uint32_t purpose) {
+ pending.content_type.hint = hint;
+ pending.content_type.purpose = purpose;
+}
+
+static void handle_surrounding_text(void *data,
+ struct zwp_input_method_v2 *zwp_input_method_v2,
+ const char *text, uint32_t cursor, uint32_t anchor) {
+ free(pending.surrounding.text);
+ pending.surrounding.text = strdup(text);
+ pending.surrounding.cursor = cursor;
+ pending.surrounding.anchor = anchor;
+}
+
+static void handle_text_change_cause(void *data,
+ struct zwp_input_method_v2 *zwp_input_method_v2,
+ uint32_t cause) {
+ pending.change_cause = cause;
+}
+
+static void handle_activate(void *data,
+ struct zwp_input_method_v2 *zwp_input_method_v2) {
+ pending_active = true;
+}
+
+static void handle_deactivate(void *data,
+ struct zwp_input_method_v2 *zwp_input_method_v2) {
+ pending_active = false;
+}
+
+static void handle_unavailable(void *data,
+ struct zwp_input_method_v2 *zwp_input_method_v2) {
+ printf("IM disappeared\n");
+ zwp_input_method_v2_destroy(zwp_input_method_v2);
+ input_method = NULL;
+ running = false;
+}
+
+static void im_activate(void *data,
+ struct zwp_input_method_v2 *id) {
+ update_stage = 0;
+}
+
+static void timer_arm(unsigned seconds) {
+ printf("Timer armed\n");
+ struct itimerspec spec = {
+ .it_interval = {0},
+ .it_value = {
+ .tv_sec = seconds,
+ .tv_nsec = 0
+ }
+ };
+ if (timerfd_settime(timer_fd, 0, &spec, NULL)) {
+ fprintf(stderr, "Failed to arm timer: %s\n", strerror(errno));
+ }
+}
+
+static void do_updates() {
+ printf("Update %d\n", update_stage);
+ switch (update_stage) {
+ case 0:
+ // TODO: remember initial surrounding text
+ zwp_input_method_v2_set_preedit_string(input_method, "Preedit", 2, 4);
+ zwp_input_method_v2_commit(input_method, serial);
+ // don't expect an answer, preedit doesn't change anything visible
+ timer_arm(sleeptime);
+ update_stage++;
+ return;
+ case 1:
+ zwp_input_method_v2_set_preedit_string(input_method, "Præedit2", strlen("Pr"), strlen("Præed"));
+ zwp_input_method_v2_commit_string(input_method, "_Commit_");
+ zwp_input_method_v2_commit(input_method, serial);
+ update_stage++;
+ break;
+ case 2:
+ if (strcmp(current.surrounding.text, "_Commit_") != 0) {
+ return;
+ }
+ zwp_input_method_v2_commit_string(input_method, "_CommitNoPreed_");
+ zwp_input_method_v2_commit(input_method, serial);
+ timer_arm(sleeptime);
+ update_stage++;
+ break;
+ case 3:
+ if (strcmp(current.surrounding.text, "_Commit__CommitNoPreed_") != 0) {
+ return;
+ }
+ zwp_input_method_v2_commit_string(input_method, "_WaitNo_");
+ zwp_input_method_v2_delete_surrounding_text(input_method, strlen("_CommitNoPreed_"), 0);
+ zwp_input_method_v2_commit(input_method, serial);
+ update_stage++;
+ break;
+ case 4:
+ if (strcmp(current.surrounding.text, "_Commit__WaitNo_") != 0) {
+ return;
+ }
+ zwp_input_method_v2_set_preedit_string(input_method, "PreedWithDel", strlen("Preed"), strlen("Preed"));
+ zwp_input_method_v2_delete_surrounding_text(input_method, strlen("_WaitNo_"), 0);
+ zwp_input_method_v2_commit(input_method, serial);
+ update_stage++;
+ break;
+ case 5:
+ if (strcmp(current.surrounding.text, "_Commit_") != 0) {
+ return;
+ }
+ zwp_input_method_v2_delete_surrounding_text(input_method, strlen("mit_"), 0);
+ zwp_input_method_v2_commit(input_method, serial);
+ update_stage++;
+ break;
+ case 6:
+ if (strcmp(current.surrounding.text, "_Com") != 0) {
+ printf("Failed\n");
+ }
+ update_stage++;
+ break;
+ default:
+ printf("Submitted everything\n");
+ return;
+ };
+}
+
+static void handle_timer() {
+ printf("Timer dispatched at %d\n", update_stage);
+ do_updates();
+}
+
+static void im_deactivate(void *data,
+ struct zwp_input_method_v2 *context) {
+ // No special action needed
+}
+
+static void handle_done(void *data,
+ struct zwp_input_method_v2 *zwp_input_method_v2) {
+ bool prev_active = active;
+ serial++;
+ printf("Handle serial %d\n", serial);
+ if (active != pending_active) {
+ printf("Now %s\n", pending_active ? "active" : "inactive");
+ }
+ if (pending_active) {
+ print_state_diff(current, pending);
+ }
+ active = pending_active;
+ free(current.surrounding.text);
+ struct input_method_state default_state = {0};
+ current = pending;
+ pending = default_state;
+ if (active && !prev_active) {
+ im_activate(data, zwp_input_method_v2);
+ } else if (!active && prev_active) {
+ im_deactivate(data, zwp_input_method_v2);
+ }
+
+ do_updates();
+}
+
+static const struct zwp_input_method_v2_listener im_listener = {
+ .activate = handle_activate,
+ .deactivate = handle_deactivate,
+ .surrounding_text = handle_surrounding_text,
+ .text_change_cause = handle_text_change_cause,
+ .content_type = handle_content_type,
+ .done = handle_done,
+ .unavailable = handle_unavailable,
+};
+
+static void handle_global(void *data, struct wl_registry *registry,
+ uint32_t name, const char *interface, uint32_t version) {
+ if (strcmp(interface, "wl_compositor") == 0) {
+ compositor = wl_registry_bind(registry, name,
+ &wl_compositor_interface, 1);
+ } else if (strcmp(interface, zwp_input_method_manager_v2_interface.name) == 0) {
+ input_method_manager = wl_registry_bind(registry, name,
+ &zwp_input_method_manager_v2_interface, 1);
+ } else if (strcmp(interface, wl_seat_interface.name) == 0) {
+ seat = wl_registry_bind(registry, name, &wl_seat_interface, version);
+ }
+}
+
+static void handle_global_remove(void *data, struct wl_registry *registry,
+ uint32_t name) {
+ // who cares
+}
+
+static const struct wl_registry_listener registry_listener = {
+ .global = handle_global,
+ .global_remove = handle_global_remove,
+};
+
+int main(int argc, char **argv) {
+ if (argc > 1) {
+ if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) {
+ printf(usage);
+ return 0;
+ }
+ sleeptime = atoi(argv[1]);
+ }
+ display = wl_display_connect(NULL);
+ if (display == NULL) {
+ fprintf(stderr, "Failed to create display\n");
+ return EXIT_FAILURE;
+ }
+
+ 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 (compositor == NULL) {
+ fprintf(stderr, "wl-compositor not available\n");
+ return EXIT_FAILURE;
+ }
+ if (input_method_manager == NULL) {
+ fprintf(stderr, "input-method not available\n");
+ return EXIT_FAILURE;
+ }
+ if (seat == NULL) {
+ fprintf(stderr, "seat not available\n");
+ return EXIT_FAILURE;
+ }
+
+ input_method = zwp_input_method_manager_v2_get_input_method(
+ input_method_manager, seat);
+ running = true;
+ zwp_input_method_v2_add_listener(input_method, &im_listener, NULL);
+
+ int display_fd = wl_display_get_fd(display);
+ timer_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
+ if (timer_fd < 0) {
+ fprintf(stderr, "Failed to start timer\n");
+ return EXIT_FAILURE;
+ }
+ int epoll = epoll_create1(EPOLL_CLOEXEC);
+ if (epoll < 0) {
+ fprintf(stderr, "Failed to start epoll\n");
+ return EXIT_FAILURE;
+ }
+
+ struct epoll_event epoll_display = {
+ .events = EPOLLIN | EPOLLOUT,
+ .data = {.fd = display_fd},
+ };
+ if (epoll_ctl(epoll, EPOLL_CTL_ADD, display_fd, &epoll_display)) {
+ fprintf(stderr, "Failed to epoll display\n");
+ return EXIT_FAILURE;
+ }
+
+ wl_display_roundtrip(display); // timer may be armed here
+
+ struct epoll_event epoll_timer = {
+ .events = EPOLLIN,
+ .data = {.fd = timer_fd},
+ };
+ if (epoll_ctl(epoll, EPOLL_CTL_ADD, timer_fd, &epoll_timer)) {
+ fprintf(stderr, "Failed to epoll timer\n");
+ return EXIT_FAILURE;
+ }
+
+ timer_arm(2);
+
+ struct epoll_event caught;
+ while (epoll_wait(epoll, &caught, 1, -1)) {
+ if (!running) {
+ printf("Exiting\n");
+ return EXIT_SUCCESS;
+ }
+ if (caught.data.fd == display_fd) {
+ if (wl_display_dispatch(display) == -1) {
+ break;
+ }
+ } else if (caught.data.fd == timer_fd) {
+ uint64_t expirations;
+ ssize_t n = read(timer_fd, &expirations, sizeof(expirations));
+ assert(n >= 0);
+ handle_timer();
+ } else {
+ printf("Unknown source\n");
+ }
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/examples/layer-shell.c b/examples/layer-shell.c
index f56825c3..114b98f6 100644
--- a/examples/layer-shell.c
+++ b/examples/layer-shell.c
@@ -596,10 +596,16 @@ int main(int argc, char **argv) {
assert(cursor_theme);
struct wl_cursor *cursor =
wl_cursor_theme_get_cursor(cursor_theme, "crosshair");
+ if (cursor == NULL) {
+ cursor = wl_cursor_theme_get_cursor(cursor_theme, "left_ptr");
+ }
assert(cursor);
cursor_image = cursor->images[0];
cursor = wl_cursor_theme_get_cursor(cursor_theme, "tcross");
+ if (cursor == NULL) {
+ cursor = wl_cursor_theme_get_cursor(cursor_theme, "left_ptr");
+ }
assert(cursor);
popup_cursor_image = cursor->images[0];
diff --git a/examples/meson.build b/examples/meson.build
index b5ad6c98..369c7049 100644
--- a/examples/meson.build
+++ b/examples/meson.build
@@ -6,6 +6,13 @@ libavutil = dependency('libavutil', version: '>=56.14.100', required: false)
libavcodec = dependency('libavcodec', version: '>=58.18.100', required: false)
libavformat = dependency('libavformat', version: '>=58.12.100', required: false)
+# epoll is a separate library in FreeBSD
+if host_machine.system() == 'freebsd'
+ libepoll = [dependency('epoll-shim')]
+else
+ libepoll = []
+endif
+
# Small hack until https://github.com/mesonbuild/meson/pull/3386/ is merged
foreach dep : ['libpng', 'libavutil', 'libavcodec', 'libavformat']
if not get_variable(dep).found()
@@ -70,12 +77,17 @@ examples = {
'src': 'gamma-control.c',
'dep': [wayland_client, wayland_cursor, wlr_protos, wlroots],
},
+ 'pointer-constraints': {
+ 'src': 'pointer-constraints.c',
+ 'dep': [wayland_client, wlr_protos, wlroots],
+ },
'dmabuf-capture': {
'src': 'dmabuf-capture.c',
'dep': [
libavcodec,
libavformat,
libavutil,
+ drm.partial_dependency(compile_args: true), # <drm_fourcc.h>
threads,
wayland_client,
wlr_protos,
@@ -90,6 +102,14 @@ examples = {
'src': 'toplevel-decoration.c',
'dep': [wayland_client, wlr_protos, wlroots],
},
+ 'input-method': {
+ 'src': 'input-method.c',
+ 'dep': [wayland_client, wlr_protos, wlroots] + libepoll,
+ },
+ 'text-input': {
+ 'src': 'text-input.c',
+ 'dep': [wayland_cursor, wayland_client, wlr_protos, wlroots],
+ },
}
foreach name, info : examples
diff --git a/examples/pointer-constraints.c b/examples/pointer-constraints.c
new file mode 100644
index 00000000..1df9f6ce
--- /dev/null
+++ b/examples/pointer-constraints.c
@@ -0,0 +1,260 @@
+#include <GLES2/gl2.h>
+#include <linux/input-event-codes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wayland-client.h>
+#include <wayland-egl.h>
+#include <wlr/render/egl.h>
+#include "xdg-shell-client-protocol.h"
+#include "pointer-constraints-unstable-v1-client-protocol.h"
+
+static int width = 512, height = 512;
+
+static struct wl_compositor *compositor = NULL;
+static struct wl_seat *seat = NULL;
+static struct xdg_wm_base *wm_base = NULL;
+static struct zwp_pointer_constraints_v1 *pointer_constraints = NULL;
+
+struct wlr_egl egl;
+struct wl_egl_window *egl_window;
+struct wlr_egl_surface *egl_surface;
+struct zwp_locked_pointer_v1* locked_pointer;
+struct zwp_confined_pointer_v1* confined_pointer;
+
+enum {
+ REGION_TYPE_NONE,
+ REGION_TYPE_DISJOINT,
+ REGION_TYPE_JOINT,
+ REGION_TYPE_MAX
+} region_type = REGION_TYPE_NONE;
+
+struct wl_region *regions[3];
+
+static void draw(void) {
+ eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context);
+
+ float color[] = {1.0, 1.0, 0.0, 1.0};
+
+ glViewport(0, 0, width, height);
+ glClearColor(color[0], color[1], color[2], 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ eglSwapBuffers(egl.display, egl_surface);
+}
+
+static void pointer_handle_button(void *data, struct wl_pointer *pointer,
+ uint32_t serial, uint32_t time, uint32_t button, uint32_t state_w) {
+ struct wl_surface *surface = data;
+
+ if (button == BTN_LEFT && state_w == WL_POINTER_BUTTON_STATE_PRESSED) {
+ region_type = (region_type + 1) % REGION_TYPE_MAX;
+
+ if (locked_pointer) {
+ zwp_locked_pointer_v1_set_region(locked_pointer,
+ regions[region_type]);
+ } else if (confined_pointer) {
+ zwp_confined_pointer_v1_set_region(confined_pointer,
+ regions[region_type]);
+ }
+
+ wl_surface_commit(surface);
+ }
+}
+
+static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer,
+ uint32_t serial, struct wl_surface *surface,
+ wl_fixed_t surface_x, wl_fixed_t surface_y) {
+ // This space intentionally left blank
+}
+
+static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer,
+ uint32_t serial, struct wl_surface *surface) {
+ // This space intentionally left blank
+}
+
+static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer,
+ uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {
+ // This space intentionally left blank
+}
+
+static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
+ uint32_t time, uint32_t axis, wl_fixed_t value) {
+ // This space intentionally left blank
+}
+
+static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) {
+ // This space intentionally left blank
+}
+
+static void pointer_handle_axis_source(void *data,
+ struct wl_pointer *wl_pointer, uint32_t axis_source) {
+ // This space intentionally left blank
+}
+
+static void pointer_handle_axis_stop(void *data,
+ struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis) {
+ // This space intentionally left blank
+}
+
+static void pointer_handle_axis_discrete(void *data,
+ struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) {
+ // This space intentionally left blank
+}
+
+static const struct wl_pointer_listener pointer_listener = {
+ .enter = pointer_handle_enter,
+ .leave = pointer_handle_leave,
+ .motion = pointer_handle_motion,
+ .button = pointer_handle_button,
+ .axis = pointer_handle_axis,
+ .frame = pointer_handle_frame,
+ .axis_source = pointer_handle_axis_source,
+ .axis_stop = pointer_handle_axis_stop,
+ .axis_discrete = pointer_handle_axis_discrete,
+};
+
+static void xdg_surface_handle_configure(void *data,
+ struct xdg_surface *xdg_surface, uint32_t serial) {
+ xdg_surface_ack_configure(xdg_surface, serial);
+ wl_egl_window_resize(egl_window, width, height, 0, 0);
+ draw();
+}
+
+static const struct xdg_surface_listener xdg_surface_listener = {
+ .configure = xdg_surface_handle_configure,
+};
+
+static void xdg_toplevel_handle_configure(void *data,
+ struct xdg_toplevel *xdg_toplevel, int32_t w, int32_t h,
+ struct wl_array *states) {
+ width = w;
+ height = h;
+}
+
+static void xdg_toplevel_handle_close(void *data,
+ struct xdg_toplevel *xdg_toplevel) {
+ exit(EXIT_SUCCESS);
+}
+
+static const struct xdg_toplevel_listener xdg_toplevel_listener = {
+ .configure = xdg_toplevel_handle_configure,
+ .close = xdg_toplevel_handle_close,
+};
+
+static void handle_global(void *data, struct wl_registry *registry,
+ uint32_t name, const char *interface, uint32_t version) {
+ if (strcmp(interface, wl_compositor_interface.name) == 0) {
+ compositor = wl_registry_bind(registry, name,
+ &wl_compositor_interface, 1);
+ } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
+ wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);
+ } else if (strcmp(interface, wl_seat_interface.name) == 0) {
+ seat = wl_registry_bind(registry, name, &wl_seat_interface, version);
+ } else if (strcmp(interface,
+ zwp_pointer_constraints_v1_interface.name) == 0) {
+ pointer_constraints = wl_registry_bind(registry, name,
+ &zwp_pointer_constraints_v1_interface, version);
+ }
+}
+
+static const struct wl_registry_listener registry_listener = {
+ .global = handle_global,
+ .global_remove = NULL,
+};
+
+int main(int argc, char **argv) {
+ if (argc != 4) {
+ goto invalid_args;
+ }
+
+ bool lock;
+ if (strcmp(argv[1], "lock") == 0) {
+ lock = true;
+ } else if (strcmp(argv[1], "confine") == 0) {
+ lock = false;
+ } else {
+ goto invalid_args;
+ }
+
+ enum zwp_pointer_constraints_v1_lifetime lifetime;
+ if (strcmp(argv[2], "oneshot") == 0) {
+ lifetime = ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT;
+ } else if (strcmp(argv[2], "persistent") == 0) {
+ lifetime = ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT;
+ } else {
+ goto invalid_args;
+ }
+
+ if (strcmp(argv[3], "no-region") == 0) {
+ region_type = REGION_TYPE_NONE;
+ } else if (strcmp(argv[3], "disjoint-region") == 0) {
+ region_type = REGION_TYPE_DISJOINT;
+ } else if (strcmp(argv[3], "joint-region") == 0) {
+ region_type = REGION_TYPE_JOINT;
+ }
+
+ struct wl_display *display = wl_display_connect(NULL);
+
+ 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);
+
+ struct wl_region *disjoint_region = wl_compositor_create_region(compositor);
+ wl_region_add(disjoint_region, 0, 0, 255, 256);
+ wl_region_add(disjoint_region, 257, 0, 255, 256);
+ regions[REGION_TYPE_DISJOINT] = disjoint_region;
+
+ struct wl_region *joint_region = wl_compositor_create_region(compositor);
+ wl_region_add(joint_region, 0, 0, 256, 256);
+ wl_region_add(joint_region, 256, 0, 256, 256);
+ wl_region_add(joint_region, 256, 256, 256, 256);
+ regions[REGION_TYPE_JOINT] = joint_region;
+
+ wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL,
+ WL_SHM_FORMAT_ARGB8888);
+
+ struct wl_surface *surface = wl_compositor_create_surface(compositor);
+ struct xdg_surface *xdg_surface =
+ xdg_wm_base_get_xdg_surface(wm_base, surface);
+ struct xdg_toplevel *xdg_toplevel = xdg_surface_get_toplevel(xdg_surface);
+
+ xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL);
+ xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, NULL);
+
+ struct wl_pointer *pointer = wl_seat_get_pointer(seat);
+ wl_pointer_add_listener(pointer, &pointer_listener, surface);
+
+ if (lock) {
+ locked_pointer = zwp_pointer_constraints_v1_lock_pointer(
+ pointer_constraints, surface, pointer,
+ regions[region_type], lifetime);
+
+ zwp_locked_pointer_v1_set_cursor_position_hint(locked_pointer,
+ wl_fixed_from_int(128), wl_fixed_from_int(128));
+ } else {
+ confined_pointer = zwp_pointer_constraints_v1_confine_pointer(
+ pointer_constraints, surface, pointer,
+ regions[region_type], lifetime);
+ }
+
+ wl_surface_commit(surface);
+
+ egl_window = wl_egl_window_create(surface, width, height);
+ egl_surface = wlr_egl_create_surface(&egl, egl_window);
+
+ wl_display_roundtrip(display);
+
+ draw();
+
+ while (wl_display_dispatch(display) != -1) {}
+
+ return EXIT_SUCCESS;
+
+invalid_args:
+ fprintf(stderr, "pointer-constraints <lock | confine> "
+ "<oneshot | persistent> "
+ "<no-region | disjoint-rejoin | joint-region>\n");
+ return EXIT_FAILURE;
+}
diff --git a/examples/text-input.c b/examples/text-input.c
new file mode 100644
index 00000000..3ccd99a0
--- /dev/null
+++ b/examples/text-input.c
@@ -0,0 +1,394 @@
+#define _POSIX_C_SOURCE 200809L
+#include <GLES2/gl2.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <wayland-client.h>
+#include <wayland-egl.h>
+#include <wlr/render/egl.h>
+#include "text-input-unstable-v3-client-protocol.h"
+#include "xdg-shell-client-protocol.h"
+
+const char usage[] = "Usage: text-input [seconds [width height]]\n\
+\n\
+Creates a xdg-toplevel using the text-input protocol.\n\
+It will be solid black when it has no text input focus, yellow when it\n\
+has focus, and red when it was notified that the focus moved away\n\
+but still didn't give up the text input ability.\n\
+\n\
+The \"seconds\" argument is optional and defines the delay between getting\n\
+notified of lost focus and releasing text input.\n\
+\n\
+The \"width\" and \"height\" arguments define the window shape.\n\
+\n\
+The console will print the internal state of the text field:\n\
+- the text in the 1st line\n\
+- \".\" under each preedit character\n\
+- \"_\" under each selected preedit character\n\
+- \"|\" at the cursor position if there are no selected characters in the\n\
+preedit.\n\
+\n\
+The cursor positions may be inaccurate, especially in presence of zero-width\n\
+characters or non-monospaced fonts.\n";
+
+struct text_input_state {
+ char *commit;
+ struct {
+ char *text;
+ int32_t cursor_begin;
+ int32_t cursor_end;
+ } preedit;
+ struct {
+ uint32_t after_length;
+ uint32_t before_length;
+ } delete_surrounding;
+};
+
+static struct text_input_state pending = {0};
+static struct text_input_state current = {0};
+static bool entered = false;
+static uint32_t serial;
+static char *buffer; // text buffer
+// cursor is not present, there's no way to move it outside of preedit
+
+static int sleeptime = 0;
+static int width = 100, height = 200;
+static int enabled = 0;
+
+static struct wl_display *display = NULL;
+static struct wl_compositor *compositor = NULL;
+static struct wl_seat *seat = NULL;
+static struct xdg_wm_base *wm_base = NULL;
+static struct zwp_text_input_manager_v3 *text_input_manager = NULL;
+static struct zwp_text_input_v3 *text_input = NULL;
+
+struct wlr_egl egl;
+struct wl_egl_window *egl_window;
+struct wlr_egl_surface *egl_surface;
+
+static void draw(void) {
+ eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context);
+
+ float color[] = {1.0, 1.0, 0.0, 1.0};
+ color[0] = enabled * 1.0;
+ color[1] = entered * 1.0;
+
+ glViewport(0, 0, width, height);
+ glClearColor(color[0], color[1], color[2], 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ eglSwapBuffers(egl.display, egl_surface);
+}
+
+static size_t utf8_strlen(char *str) {
+ size_t cp_count = 0;
+ for (; *str != '\0'; str++) {
+ if ((*str & 0xc0) != 0x80) {
+ cp_count++;
+ }
+ }
+ return cp_count;
+}
+
+static size_t utf8_offset(char *utf8_str, size_t byte_offset) {
+ size_t cp_count = 0;
+ for (char *c = utf8_str; c < utf8_str + byte_offset; c++) {
+ if ((*c & 0xc0) != 0x80) {
+ cp_count++;
+ }
+ }
+ return cp_count;
+}
+
+// TODO: would be nicer to have this text display inside the window
+static void show_status() {
+ printf("State %d:", serial);
+ if (!enabled) {
+ printf(" disabled");
+ }
+
+ char *preedit_text = current.preedit.text;
+ if (!preedit_text) {
+ preedit_text = "";
+ }
+
+ printf("\n");
+ printf("%s", buffer);
+ printf("%s\n", preedit_text);
+
+ // Positioning of the cursor requires UTF8 offsets to match monospaced
+ // glyphs
+ for (unsigned i = 0; i < utf8_strlen(buffer); i++) {
+ printf(" ");
+ }
+ char *cursor_mark = calloc(utf8_strlen(preedit_text) + 2, sizeof(char));
+ for (unsigned i = 0; i < utf8_strlen(preedit_text); i++) {
+ cursor_mark[i] = '.';
+ }
+ if (current.preedit.cursor_begin == -1
+ && current.preedit.cursor_end == -1) {
+ goto end;
+ }
+ if (current.preedit.cursor_begin == -1
+ || current.preedit.cursor_end == -1) {
+ printf("Only one cursor side is defined: %d to %d\n",
+ current.preedit.cursor_begin, current.preedit.cursor_end);
+ goto end;
+ }
+
+ if ((unsigned)current.preedit.cursor_begin > strlen(preedit_text)
+ || (unsigned)current.preedit.cursor_begin > strlen(preedit_text)) {
+ printf("Cursor out of bounds\n");
+ goto end;
+ }
+
+ if (current.preedit.cursor_begin == current.preedit.cursor_end) {
+ cursor_mark[utf8_offset(preedit_text, current.preedit.cursor_begin)]
+ = '|';
+ goto print;
+ }
+
+ if (current.preedit.cursor_begin > current.preedit.cursor_end) {
+ printf("End cursor is before start cursor\n");
+ goto end;
+ }
+
+ // negative offsets already checked before
+ for (unsigned i = utf8_offset(preedit_text, current.preedit.cursor_begin);
+ i < utf8_offset(preedit_text, current.preedit.cursor_end); i++) {
+ cursor_mark[i] = '_';
+ }
+print:
+ printf("%s\n", cursor_mark);
+end:
+ free(cursor_mark);
+}
+
+static void commit(struct zwp_text_input_v3 *text_input) {
+ zwp_text_input_v3_commit(text_input);
+ serial++;
+}
+
+static void send_status_update(struct zwp_text_input_v3 *text_input) {
+ zwp_text_input_v3_set_surrounding_text(text_input, buffer, strlen(buffer), strlen(buffer));
+ zwp_text_input_v3_set_text_change_cause(text_input, ZWP_TEXT_INPUT_V3_CHANGE_CAUSE_INPUT_METHOD);
+ commit(text_input);
+}
+
+static void text_input_handle_enter(void *data,
+ struct zwp_text_input_v3 *zwp_text_input_v3,
+ struct wl_surface *surface) {
+ entered = true;
+ zwp_text_input_v3_enable(zwp_text_input_v3);
+ commit(zwp_text_input_v3);
+ enabled = true;
+ draw();
+ show_status();
+}
+
+static void text_input_handle_leave(void *data,
+ struct zwp_text_input_v3 *zwp_text_input_v3,
+ struct wl_surface *surface) {
+ entered = false;
+ draw();
+ wl_display_roundtrip(display);
+ sleep(sleeptime);
+ zwp_text_input_v3_disable(zwp_text_input_v3);
+ commit(zwp_text_input_v3);
+ enabled = false;
+ draw();
+ show_status();
+}
+
+static void text_input_commit_string(void *data,
+ struct zwp_text_input_v3 *zwp_text_input_v3,
+ const char *text) {
+ free(pending.commit);
+ pending.commit = strdup(text);
+}
+
+static void text_input_delete_surrounding_text(void *data,
+ struct zwp_text_input_v3 *zwp_text_input_v3,
+ uint32_t before_length, uint32_t after_length) {
+ pending.delete_surrounding.before_length = before_length;
+ pending.delete_surrounding.after_length = after_length;
+}
+
+static void text_input_preedit_string(void *data,
+ struct zwp_text_input_v3 *zwp_text_input_v3,
+ const char *text, int32_t cursor_begin, int32_t cursor_end) {
+ free(pending.preedit.text);
+ pending.preedit.text = strdup(text);
+ pending.preedit.cursor_begin = cursor_begin;
+ pending.preedit.cursor_end = cursor_end;
+}
+
+static void text_input_handle_done(void *data,
+ struct zwp_text_input_v3 *zwp_text_input_v3,
+ uint32_t incoming_serial) {
+ if (serial != incoming_serial) {
+ fprintf(stderr, "Received serial %d while expecting %d\n", incoming_serial, serial);
+ return;
+ }
+ free(current.preedit.text);
+ free(current.commit);
+ current = pending;
+ struct text_input_state empty = {0};
+ pending = empty;
+
+ if (current.delete_surrounding.after_length + current.delete_surrounding.before_length > 0) {
+ // cursor is always after committed text, after_length != 0 will never happen
+ unsigned delete_before = current.delete_surrounding.before_length;
+ if (delete_before > strlen(buffer)) {
+ delete_before = strlen(buffer);
+ }
+ buffer[strlen(buffer) - delete_before] = '\0';
+ }
+
+ char *commit_string = current.commit;
+ if (!commit_string) {
+ commit_string = "";
+ }
+ char *old_buffer = buffer;
+ buffer = calloc(strlen(buffer) + strlen(commit_string) + 1, sizeof(char)); // realloc may fail anyway
+ strcpy(buffer, old_buffer);
+ free(old_buffer);
+ strcat(buffer, commit_string);
+
+ send_status_update(zwp_text_input_v3);
+ show_status();
+}
+
+static const struct zwp_text_input_v3_listener text_input_listener = {
+ .enter = text_input_handle_enter,
+ .leave = text_input_handle_leave,
+ .commit_string = text_input_commit_string,
+ .delete_surrounding_text = text_input_delete_surrounding_text,
+ .preedit_string = text_input_preedit_string,
+ .done = text_input_handle_done,
+};
+
+static void xdg_surface_handle_configure(void *data,
+ struct xdg_surface *xdg_surface, uint32_t serial) {
+ xdg_surface_ack_configure(xdg_surface, serial);
+ wl_egl_window_resize(egl_window, width, height, 0, 0);
+ draw();
+}
+
+static const struct xdg_surface_listener xdg_surface_listener = {
+ .configure = xdg_surface_handle_configure,
+};
+
+static void xdg_toplevel_handle_configure(void *data,
+ struct xdg_toplevel *xdg_toplevel, int32_t w, int32_t h,
+ struct wl_array *states) {
+ width = w;
+ height = h;
+}
+
+static void xdg_toplevel_handle_close(void *data,
+ struct xdg_toplevel *xdg_toplevel) {
+ exit(EXIT_SUCCESS);
+}
+
+static const struct xdg_toplevel_listener xdg_toplevel_listener = {
+ .configure = xdg_toplevel_handle_configure,
+ .close = xdg_toplevel_handle_close,
+};
+
+static void handle_global(void *data, struct wl_registry *registry,
+ uint32_t name, const char *interface, uint32_t version) {
+ if (strcmp(interface, "wl_compositor") == 0) {
+ compositor = wl_registry_bind(registry, name,
+ &wl_compositor_interface, 1);
+ } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
+ wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);
+ } else if (strcmp(interface, zwp_text_input_manager_v3_interface.name) == 0) {
+ text_input_manager = wl_registry_bind(registry, name,
+ &zwp_text_input_manager_v3_interface, 1);
+ } else if (strcmp(interface, wl_seat_interface.name) == 0) {
+ seat = wl_registry_bind(registry, name, &wl_seat_interface, version);
+ }
+}
+
+static void handle_global_remove(void *data, struct wl_registry *registry,
+ uint32_t name) {
+ // who cares
+}
+
+static const struct wl_registry_listener registry_listener = {
+ .global = handle_global,
+ .global_remove = handle_global_remove,
+};
+
+int main(int argc, char **argv) {
+ if (argc > 1) {
+ if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) {
+ printf(usage);
+ return 0;
+ }
+ sleeptime = atoi(argv[1]);
+ if (argc > 3) {
+ width = atoi(argv[2]);
+ height = atoi(argv[3]);
+ }
+ }
+
+ buffer = calloc(1, sizeof(char));
+
+ display = wl_display_connect(NULL);
+ if (display == NULL) {
+ fprintf(stderr, "Failed to create display\n");
+ return EXIT_FAILURE;
+ }
+
+ 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 (compositor == NULL) {
+ fprintf(stderr, "wl-compositor not available\n");
+ return EXIT_FAILURE;
+ }
+ if (wm_base == NULL) {
+ fprintf(stderr, "xdg-shell not available\n");
+ return EXIT_FAILURE;
+ }
+ if (text_input_manager == NULL) {
+ fprintf(stderr, "text-input not available\n");
+ return EXIT_FAILURE;
+ }
+
+ text_input = zwp_text_input_manager_v3_get_text_input(text_input_manager, seat);
+
+ zwp_text_input_v3_add_listener(text_input, &text_input_listener, NULL);
+
+
+ wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL,
+ WL_SHM_FORMAT_ARGB8888);
+
+ struct wl_surface *surface = wl_compositor_create_surface(compositor);
+ struct xdg_surface *xdg_surface =
+ xdg_wm_base_get_xdg_surface(wm_base, surface);
+ struct xdg_toplevel *xdg_toplevel = xdg_surface_get_toplevel(xdg_surface);
+
+ xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL);
+ xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, NULL);
+
+ wl_surface_commit(surface);
+
+ egl_window = wl_egl_window_create(surface, width, height);
+ egl_surface = wlr_egl_create_surface(&egl, egl_window);
+
+ wl_display_roundtrip(display);
+
+ draw();
+
+ while (wl_display_dispatch(display) != -1) {
+ // This space intentionally left blank
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/include/backend/drm/drm.h b/include/backend/drm/drm.h
index ac23cd9a..de5212d3 100644
--- a/include/backend/drm/drm.h
+++ b/include/backend/drm/drm.h
@@ -6,6 +6,7 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
+#include <time.h>
#include <wayland-server.h>
#include <wayland-util.h>
#include <wlr/backend/drm.h>
@@ -57,6 +58,9 @@ struct wlr_drm_crtc {
union wlr_drm_crtc_props props;
struct wl_list connectors;
+
+ uint16_t *gamma_table;
+ size_t gamma_table_size;
};
struct wlr_drm_backend {
@@ -64,6 +68,7 @@ struct wlr_drm_backend {
struct wlr_drm_backend *parent;
const struct wlr_drm_interface *iface;
+ clockid_t clock;
int fd;
@@ -104,10 +109,14 @@ struct wlr_drm_backend {
};
enum wlr_drm_connector_state {
+ // Connector is available but no output is plugged in
WLR_DRM_CONN_DISCONNECTED,
+ // An output just has been plugged in and is waiting for a modeset
WLR_DRM_CONN_NEEDS_MODESET,
WLR_DRM_CONN_CLEANUP,
WLR_DRM_CONN_CONNECTED,
+ // Connector disappeared, waiting for being destroyed on next page-flip
+ WLR_DRM_CONN_DISAPPEARED,
};
struct wlr_drm_mode {
@@ -147,5 +156,7 @@ void restore_drm_outputs(struct wlr_drm_backend *drm);
void scan_drm_connectors(struct wlr_drm_backend *state);
int handle_drm_event(int fd, uint32_t mask, void *data);
bool enable_drm_connector(struct wlr_output *output, bool enable);
+bool set_drm_connector_gamma(struct wlr_output *output, size_t size,
+ const uint16_t *r, const uint16_t *g, const uint16_t *b);
#endif
diff --git a/include/backend/drm/iface.h b/include/backend/drm/iface.h
index ef0e7bf8..5308b136 100644
--- a/include/backend/drm/iface.h
+++ b/include/backend/drm/iface.h
@@ -28,11 +28,11 @@ struct wlr_drm_interface {
struct wlr_drm_crtc *crtc, int x, int y);
// Set the gamma lut on crtc
bool (*crtc_set_gamma)(struct wlr_drm_backend *drm,
- struct wlr_drm_crtc *crtc, uint16_t *r, uint16_t *g, uint16_t *b,
- uint32_t size);
+ struct wlr_drm_crtc *crtc, size_t size,
+ uint16_t *r, uint16_t *g, uint16_t *b);
// Get the gamma lut size of a crtc
- uint32_t (*crtc_get_gamma_size)(struct wlr_drm_backend *drm,
- struct wlr_drm_crtc *crtc);
+ size_t (*crtc_get_gamma_size)(struct wlr_drm_backend *drm,
+ struct wlr_drm_crtc *crtc);
};
extern const struct wlr_drm_interface atomic_iface;
diff --git a/include/backend/drm/properties.h b/include/backend/drm/properties.h
index 5b17e77e..321b4492 100644
--- a/include/backend/drm/properties.h
+++ b/include/backend/drm/properties.h
@@ -14,12 +14,13 @@ union wlr_drm_connector_props {
struct {
uint32_t edid;
uint32_t dpms;
+ uint32_t link_status; // not guaranteed to exist
// atomic-modesetting only
uint32_t crtc_id;
};
- uint32_t props[3];
+ uint32_t props[4];
};
union wlr_drm_crtc_props {
diff --git a/include/backend/multi.h b/include/backend/multi.h
index c57c48f3..2f5f1bd4 100644
--- a/include/backend/multi.h
+++ b/include/backend/multi.h
@@ -8,6 +8,7 @@
struct wlr_multi_backend {
struct wlr_backend backend;
+ struct wlr_session *session;
struct wl_list backends;
diff --git a/include/backend/x11.h b/include/backend/x11.h
index 38aaad50..37e3e4b6 100644
--- a/include/backend/x11.h
+++ b/include/backend/x11.h
@@ -3,6 +3,7 @@
#include <stdbool.h>
#include <wayland-server.h>
+#include <wlr/config.h>
#include <wlr/backend/x11.h>
#include <wlr/interfaces/wlr_input_device.h>
#include <wlr/interfaces/wlr_output.h>
diff --git a/include/render/gles2.h b/include/render/gles2.h
index f649f3e2..1857383f 100644
--- a/include/render/gles2.h
+++ b/include/render/gles2.h
@@ -19,7 +19,7 @@
extern PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
struct wlr_gles2_pixel_format {
- uint32_t wl_format;
+ enum wl_shm_format wl_format;
GLint gl_format, gl_type;
int depth, bpp;
bool has_alpha;
@@ -40,6 +40,12 @@ struct wlr_gles2_renderer {
const char *exts_str;
struct {
+ bool read_format_bgra_ext;
+ bool debug_khr;
+ bool egl_image_external_oes;
+ } exts;
+
+ struct {
struct {
GLuint program;
GLint proj;
@@ -72,6 +78,7 @@ struct wlr_gles2_texture {
enum wlr_gles2_texture_type type;
int width, height;
bool has_alpha;
+ enum wl_shm_format wl_format; // used to interpret upload data
bool inverted_y;
// Not set if WLR_GLES2_TEXTURE_GLTEX
@@ -86,7 +93,9 @@ struct wlr_gles2_texture {
const struct wlr_gles2_pixel_format *get_gles2_format_from_wl(
enum wl_shm_format fmt);
-const enum wl_shm_format *get_gles2_formats(size_t *len);
+const struct wlr_gles2_pixel_format *get_gles2_format_from_gl(
+ GLint gl_format, GLint gl_type, bool alpha);
+const enum wl_shm_format *get_gles2_wl_formats(size_t *len);
struct wlr_gles2_texture *gles2_get_texture(
struct wlr_texture *wlr_texture);
diff --git a/include/rootston/cursor.h b/include/rootston/cursor.h
index 2c687a39..b5bb682f 100644
--- a/include/rootston/cursor.h
+++ b/include/rootston/cursor.h
@@ -1,6 +1,7 @@
#ifndef ROOTSTON_CURSOR_H
#define ROOTSTON_CURSOR_H
+#include <wlr/types/wlr_pointer_constraints_v1.h>
#include "rootston/seat.h"
enum roots_cursor_mode {
@@ -14,6 +15,9 @@ struct roots_cursor {
struct roots_seat *seat;
struct wlr_cursor *cursor;
+ struct wlr_pointer_constraint_v1 *active_constraint;
+ pixman_region32_t confine; // invalid if active_constraint == NULL
+
const char *default_xcursor;
enum roots_cursor_mode mode;
@@ -28,6 +32,7 @@ struct roots_cursor {
uint32_t resize_edges;
struct roots_seat_view *pointer_view;
+ struct wlr_surface *wlr_surface;
struct wl_listener motion;
struct wl_listener motion_absolute;
@@ -44,6 +49,10 @@ struct roots_cursor {
struct wl_listener tool_button;
struct wl_listener request_set_cursor;
+
+ struct wl_listener focus_change;
+
+ struct wl_listener constraint_commit;
};
struct roots_cursor *roots_cursor_create(struct roots_seat *seat);
@@ -51,36 +60,46 @@ struct roots_cursor *roots_cursor_create(struct roots_seat *seat);
void roots_cursor_destroy(struct roots_cursor *cursor);
void roots_cursor_handle_motion(struct roots_cursor *cursor,
- struct wlr_event_pointer_motion *event);
+ struct wlr_event_pointer_motion *event);
void roots_cursor_handle_motion_absolute(struct roots_cursor *cursor,
- struct wlr_event_pointer_motion_absolute *event);
+ struct wlr_event_pointer_motion_absolute *event);
void roots_cursor_handle_button(struct roots_cursor *cursor,
- struct wlr_event_pointer_button *event);
+ struct wlr_event_pointer_button *event);
void roots_cursor_handle_axis(struct roots_cursor *cursor,
- struct wlr_event_pointer_axis *event);
+ struct wlr_event_pointer_axis *event);
void roots_cursor_handle_touch_down(struct roots_cursor *cursor,
- struct wlr_event_touch_down *event);
+ struct wlr_event_touch_down *event);
void roots_cursor_handle_touch_up(struct roots_cursor *cursor,
- struct wlr_event_touch_up *event);
+ struct wlr_event_touch_up *event);
void roots_cursor_handle_touch_motion(struct roots_cursor *cursor,
- struct wlr_event_touch_motion *event);
+ struct wlr_event_touch_motion *event);
void roots_cursor_handle_tool_axis(struct roots_cursor *cursor,
- struct wlr_event_tablet_tool_axis *event);
+ struct wlr_event_tablet_tool_axis *event);
void roots_cursor_handle_tool_tip(struct roots_cursor *cursor,
- struct wlr_event_tablet_tool_tip *event);
+ struct wlr_event_tablet_tool_tip *event);
void roots_cursor_handle_request_set_cursor(struct roots_cursor *cursor,
- struct wlr_seat_pointer_request_set_cursor_event *event);
+ struct wlr_seat_pointer_request_set_cursor_event *event);
+
+void roots_cursor_handle_focus_change(struct roots_cursor *cursor,
+ struct wlr_seat_pointer_focus_change_event *event);
+
+void roots_cursor_handle_constraint_commit(struct roots_cursor *cursor);
void roots_cursor_update_position(struct roots_cursor *cursor,
- uint32_t time);
+ uint32_t time);
+
+void roots_cursor_update_focus(struct roots_cursor *cursor);
+
+void roots_cursor_constrain(struct roots_cursor *cursor,
+ struct wlr_pointer_constraint_v1 *constraint, double sx, double sy);
#endif
diff --git a/include/rootston/desktop.h b/include/rootston/desktop.h
index 89d8af4a..345c9c09 100644
--- a/include/rootston/desktop.h
+++ b/include/rootston/desktop.h
@@ -9,13 +9,16 @@
#include <wlr/types/wlr_idle_inhibit_v1.h>
#include <wlr/types/wlr_idle.h>
#include <wlr/types/wlr_input_inhibitor.h>
+#include <wlr/types/wlr_input_method_v2.h>
#include <wlr/types/wlr_layer_shell_v1.h>
#include <wlr/types/wlr_list.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_output.h>
+#include <wlr/types/wlr_presentation_time.h>
#include <wlr/types/wlr_primary_selection.h>
#include <wlr/types/wlr_screencopy_v1.h>
#include <wlr/types/wlr_screenshooter.h>
+#include <wlr/types/wlr_text_input_v3.h>
#include <wlr/types/wlr_virtual_keyboard_v1.h>
#include <wlr/types/wlr_wl_shell.h>
#include <wlr/types/wlr_xcursor_manager.h>
@@ -53,9 +56,13 @@ struct roots_desktop {
struct wlr_idle_inhibit_manager_v1 *idle_inhibit;
struct wlr_input_inhibit_manager *input_inhibit;
struct wlr_layer_shell_v1 *layer_shell;
+ struct wlr_input_method_manager_v2 *input_method;
+ struct wlr_text_input_manager_v3 *text_input;
struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard;
struct wlr_screencopy_manager_v1 *screencopy;
struct wlr_tablet_manager_v2 *tablet_v2;
+ struct wlr_pointer_constraints_v1 *pointer_constraints;
+ struct wlr_presentation *presentation;
struct wl_listener new_output;
struct wl_listener layout_change;
@@ -67,6 +74,7 @@ struct roots_desktop {
struct wl_listener input_inhibit_activate;
struct wl_listener input_inhibit_deactivate;
struct wl_listener virtual_keyboard_new;
+ struct wl_listener pointer_constraint;
#ifdef WLR_HAS_XWAYLAND
struct wlr_xwayland *xwayland;
diff --git a/include/rootston/input.h b/include/rootston/input.h
index 2cdb13e6..31810b4d 100644
--- a/include/rootston/input.h
+++ b/include/rootston/input.h
@@ -16,7 +16,7 @@ struct roots_input {
struct wl_listener new_input;
- struct wl_list seats;
+ struct wl_list seats; // roots_seat::link
};
struct roots_input *input_create(struct roots_server *server,
diff --git a/include/rootston/output.h b/include/rootston/output.h
index f78ee81d..3f07ab6f 100644
--- a/include/rootston/output.h
+++ b/include/rootston/output.h
@@ -24,10 +24,14 @@ struct roots_output {
struct wl_listener destroy;
struct wl_listener mode;
struct wl_listener transform;
+ struct wl_listener present;
struct wl_listener damage_frame;
struct wl_listener damage_destroy;
};
+void rotate_child_position(double *sx, double *sy, double sw, double sh,
+ double pw, double ph, float rotation);
+
void handle_new_output(struct wl_listener *listener, void *data);
struct roots_view;
diff --git a/include/rootston/seat.h b/include/rootston/seat.h
index c5e584b6..0187c6cc 100644
--- a/include/rootston/seat.h
+++ b/include/rootston/seat.h
@@ -5,12 +5,13 @@
#include "rootston/input.h"
#include "rootston/keyboard.h"
#include "rootston/layers.h"
+#include "rootston/text_input.h"
struct roots_seat {
struct roots_input *input;
struct wlr_seat *seat;
struct roots_cursor *cursor;
- struct wl_list link;
+ struct wl_list link; // roots_input::seats
// coordinates of the first touch point if it exists
int32_t touch_id;
@@ -19,6 +20,8 @@ struct roots_seat {
// If the focused layer is set, views cannot receive keyboard focus
struct wlr_layer_surface_v1 *focused_layer;
+ struct roots_input_method_relay im_relay;
+
// If non-null, only this client can receive input events
struct wl_client *exclusive_client;
@@ -114,6 +117,7 @@ struct roots_tablet_tool {
struct wlr_tablet_v2_tablet_tool *tablet_v2_tool;
struct roots_seat *seat;
+ double tilt_x, tilt_y;
struct wl_listener set_cursor;
struct wl_listener tool_destroy;
@@ -122,6 +126,12 @@ struct roots_tablet_tool {
struct wl_listener tablet_destroy;
};
+struct roots_pointer_constraint {
+ struct wlr_pointer_constraint_v1 *constraint;
+
+ struct wl_listener destroy;
+};
+
struct roots_seat *roots_seat_create(struct roots_input *input, char *name);
void roots_seat_destroy(struct roots_seat *seat);
diff --git a/include/rootston/text_input.h b/include/rootston/text_input.h
new file mode 100644
index 00000000..82e45e3e
--- /dev/null
+++ b/include/rootston/text_input.h
@@ -0,0 +1,63 @@
+#ifndef ROOTSTON_TEXT_INPUT_H
+#define ROOTSTON_TEXT_INPUT_H
+
+#include <wlr/types/wlr_text_input_v3.h>
+#include <wlr/types/wlr_input_method_v2.h>
+#include <wlr/types/wlr_surface.h>
+#include "rootston/seat.h"
+
+/**
+ * The relay structure manages the relationship between text-input and
+ * input_method interfaces on a given seat. Multiple text-input interfaces may
+ * be bound to a relay, but at most one will be focused (reveiving events) at
+ * a time. At most one input-method interface may be bound to the seat. The
+ * relay manages life cycle of both sides. When both sides are present and
+ * focused, the relay passes messages between them.
+ *
+ * Text input focus is a subset of keyboard focus - if the text-input is
+ * in the focused state, wl_keyboard sent an enter as well. However, having
+ * wl_keyboard focused doesn't mean that text-input will be focused.
+ */
+struct roots_input_method_relay {
+ struct roots_seat *seat;
+
+ struct wl_list text_inputs; // roots_text_input::link
+ struct wlr_input_method_v2 *input_method; // doesn't have to be present
+
+ struct wl_listener text_input_new;
+ struct wl_listener text_input_enable;
+ struct wl_listener text_input_commit;
+ struct wl_listener text_input_disable;
+ struct wl_listener text_input_destroy;
+
+ struct wl_listener input_method_new;
+ struct wl_listener input_method_commit;
+ struct wl_listener input_method_destroy;
+};
+
+struct roots_text_input {
+ struct roots_input_method_relay *relay;
+
+ struct wlr_text_input_v3 *input;
+ // The surface getting seat's focus. Stored for when text-input cannot
+ // be sent an enter event immediately after getting focus, e.g. when
+ // there's no input method available. Cleared once text-input is entered.
+ struct wlr_surface *pending_focused_surface;
+
+ struct wl_list link;
+
+ struct wl_listener pending_focused_surface_destroy;
+};
+
+void roots_input_method_relay_init(struct roots_seat *seat,
+ struct roots_input_method_relay *relay);
+
+// Updates currently focused surface. Surface must belong to the same seat.
+void roots_input_method_relay_set_focus(struct roots_input_method_relay *relay,
+ struct wlr_surface *surface);
+
+struct roots_text_input *roots_text_input_create(
+ struct roots_input_method_relay *relay,
+ struct wlr_text_input_v3 *text_input);
+
+#endif
diff --git a/include/types/wlr_data_device.h b/include/types/wlr_data_device.h
index 972294ff..388e91a5 100644
--- a/include/types/wlr_data_device.h
+++ b/include/types/wlr_data_device.h
@@ -11,6 +11,7 @@ struct wlr_client_data_source {
struct wlr_data_source source;
struct wlr_data_source_impl impl;
struct wl_resource *resource;
+ bool finalized;
};
extern const struct wlr_surface_role drag_icon_surface_role;
diff --git a/include/util/os-compatibility.h b/include/util/os-compatibility.h
deleted file mode 100644
index 2038025e..00000000
--- a/include/util/os-compatibility.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef UTIL_OS_COMPATIBILITY_H
-#define UTIL_OS_COMPATIBILITY_H
-
-int os_fd_set_cloexec(int fd);
-int set_cloexec_or_close(int fd);
-int create_tmpfile_cloexec(char *tmpname);
-int os_create_anonymous_file(off_t size);
-
-#endif
diff --git a/include/util/shm.h b/include/util/shm.h
new file mode 100644
index 00000000..fb67b711
--- /dev/null
+++ b/include/util/shm.h
@@ -0,0 +1,7 @@
+#ifndef UTIL_SHM_H
+#define UTIL_SHM_H
+
+int create_shm_file(void);
+int allocate_shm_file(size_t size);
+
+#endif
diff --git a/include/wlr/backend.h b/include/wlr/backend.h
index 39d072e2..54f2b5e8 100644
--- a/include/wlr/backend.h
+++ b/include/wlr/backend.h
@@ -57,5 +57,14 @@ void wlr_backend_destroy(struct wlr_backend *backend);
* Obtains the wlr_renderer reference this backend is using.
*/
struct wlr_renderer *wlr_backend_get_renderer(struct wlr_backend *backend);
+/**
+ * Obtains the wlr_session reference from this backend if there is any.
+ * Might return NULL for backends that don't use a session.
+ */
+struct wlr_session *wlr_backend_get_session(struct wlr_backend *backend);
+/**
+ * Returns the clock used by the backend for presentation feedback.
+ */
+clockid_t wlr_backend_get_presentation_clock(struct wlr_backend *backend);
#endif
diff --git a/include/wlr/backend/drm.h b/include/wlr/backend/drm.h
index 5d47647d..3724adfb 100644
--- a/include/wlr/backend/drm.h
+++ b/include/wlr/backend/drm.h
@@ -34,6 +34,4 @@ bool wlr_output_is_drm(struct wlr_output *output);
typedef struct _drmModeModeInfo drmModeModeInfo;
bool wlr_drm_connector_add_mode(struct wlr_output *output, const drmModeModeInfo *mode);
-struct wlr_session *wlr_drm_backend_get_session(struct wlr_backend *backend);
-
#endif
diff --git a/include/wlr/backend/interface.h b/include/wlr/backend/interface.h
index f3dee69b..4a6a5cbb 100644
--- a/include/wlr/backend/interface.h
+++ b/include/wlr/backend/interface.h
@@ -10,6 +10,7 @@
#define WLR_BACKEND_INTERFACE_H
#include <stdbool.h>
+#include <time.h>
#include <wlr/backend.h>
#include <wlr/render/egl.h>
@@ -17,6 +18,8 @@ struct wlr_backend_impl {
bool (*start)(struct wlr_backend *backend);
void (*destroy)(struct wlr_backend *backend);
struct wlr_renderer *(*get_renderer)(struct wlr_backend *backend);
+ struct wlr_session *(*get_session)(struct wlr_backend *backend);
+ clockid_t (*get_presentation_clock)(struct wlr_backend *backend);
};
/**
diff --git a/include/wlr/backend/multi.h b/include/wlr/backend/multi.h
index ef908d26..0687f4b6 100644
--- a/include/wlr/backend/multi.h
+++ b/include/wlr/backend/multi.h
@@ -28,7 +28,6 @@ void wlr_multi_backend_remove(struct wlr_backend *multi,
struct wlr_backend *backend);
bool wlr_backend_is_multi(struct wlr_backend *backend);
-struct wlr_session *wlr_multi_get_session(struct wlr_backend *base);
bool wlr_multi_is_empty(struct wlr_backend *backend);
void wlr_multi_for_each_backend(struct wlr_backend *backend,
diff --git a/include/wlr/interfaces/wlr_output.h b/include/wlr/interfaces/wlr_output.h
index 4cbf0d67..f7ffe3b4 100644
--- a/include/wlr/interfaces/wlr_output.h
+++ b/include/wlr/interfaces/wlr_output.h
@@ -28,11 +28,12 @@ struct wlr_output_impl {
void (*destroy)(struct wlr_output *output);
bool (*make_current)(struct wlr_output *output, int *buffer_age);
bool (*swap_buffers)(struct wlr_output *output, pixman_region32_t *damage);
- bool (*set_gamma)(struct wlr_output *output,
- uint32_t size, uint16_t *r, uint16_t *g, uint16_t *b);
- uint32_t (*get_gamma_size)(struct wlr_output *output);
+ bool (*set_gamma)(struct wlr_output *output, size_t size,
+ const uint16_t *r, const uint16_t *g, const uint16_t *b);
+ size_t (*get_gamma_size)(struct wlr_output *output);
bool (*export_dmabuf)(struct wlr_output *output,
struct wlr_dmabuf_attributes *attribs);
+ bool (*schedule_frame)(struct wlr_output *output);
};
void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend,
@@ -45,5 +46,7 @@ void wlr_output_update_enabled(struct wlr_output *output, bool enabled);
void wlr_output_update_needs_swap(struct wlr_output *output);
void wlr_output_damage_whole(struct wlr_output *output);
void wlr_output_send_frame(struct wlr_output *output);
+void wlr_output_send_present(struct wlr_output *output,
+ struct wlr_output_event_present *event);
#endif
diff --git a/include/wlr/render/dmabuf.h b/include/wlr/render/dmabuf.h
index 33c3a129..32cfe874 100644
--- a/include/wlr/render/dmabuf.h
+++ b/include/wlr/render/dmabuf.h
@@ -16,6 +16,10 @@
#define DRM_FORMAT_MOD_INVALID ((1ULL<<56) - 1)
#endif
+#ifndef DRM_FORMAT_MOD_LINEAR
+#define DRM_FORMAT_MOD_LINEAR 0
+#endif
+
#define WLR_DMABUF_MAX_PLANES 4
enum wlr_dmabuf_attributes_flags {
diff --git a/include/wlr/render/egl.h b/include/wlr/render/egl.h
index c42b0325..30de3d26 100644
--- a/include/wlr/render/egl.h
+++ b/include/wlr/render/egl.h
@@ -17,6 +17,7 @@
#include <wlr/render/dmabuf.h>
struct wlr_egl {
+ EGLenum platform;
EGLDisplay display;
EGLConfig config;
EGLContext context;
diff --git a/include/wlr/render/interface.h b/include/wlr/render/interface.h
index 63f4265c..6b80a077 100644
--- a/include/wlr/render/interface.h
+++ b/include/wlr/render/interface.h
@@ -34,6 +34,8 @@ struct wlr_renderer_impl {
const float color[static 4], const float matrix[static 9]);
const enum wl_shm_format *(*formats)(
struct wlr_renderer *renderer, size_t *len);
+ bool (*format_supported)(struct wlr_renderer *renderer,
+ enum wl_shm_format fmt);
bool (*resource_is_wl_drm_buffer)(struct wlr_renderer *renderer,
struct wl_resource *resource);
void (*wl_drm_buffer_get_size)(struct wlr_renderer *renderer,
@@ -41,12 +43,11 @@ struct wlr_renderer_impl {
int (*get_dmabuf_formats)(struct wlr_renderer *renderer, int **formats);
int (*get_dmabuf_modifiers)(struct wlr_renderer *renderer, int format,
uint64_t **modifiers);
+ enum wl_shm_format (*preferred_read_format)(struct wlr_renderer *renderer);
bool (*read_pixels)(struct wlr_renderer *renderer, enum wl_shm_format fmt,
uint32_t *flags, uint32_t stride, uint32_t width, uint32_t height,
uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y,
void *data);
- bool (*format_supported)(struct wlr_renderer *renderer,
- enum wl_shm_format fmt);
struct wlr_texture *(*texture_from_pixels)(struct wlr_renderer *renderer,
enum wl_shm_format fmt, uint32_t stride, uint32_t width,
uint32_t height, const void *data);
@@ -66,9 +67,9 @@ struct wlr_texture_impl {
void (*get_size)(struct wlr_texture *texture, int *width, int *height);
bool (*is_opaque)(struct wlr_texture *texture);
bool (*write_pixels)(struct wlr_texture *texture,
- enum wl_shm_format wl_fmt, uint32_t stride, uint32_t width,
- uint32_t height, uint32_t src_x, uint32_t src_y, uint32_t dst_x,
- uint32_t dst_y, const void *data);
+ uint32_t stride, uint32_t width, uint32_t height,
+ uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y,
+ const void *data);
bool (*to_dmabuf)(struct wlr_texture *texture,
struct wlr_dmabuf_attributes *attribs);
void (*destroy)(struct wlr_texture *texture);
diff --git a/include/wlr/render/wlr_renderer.h b/include/wlr/render/wlr_renderer.h
index 9c031b7f..02b4a11e 100644
--- a/include/wlr/render/wlr_renderer.h
+++ b/include/wlr/render/wlr_renderer.h
@@ -97,6 +97,11 @@ int wlr_renderer_get_dmabuf_formats(struct wlr_renderer *renderer,
int wlr_renderer_get_dmabuf_modifiers(struct wlr_renderer *renderer, int format,
uint64_t **modifiers);
/**
+ * Get the preferred format for reading pixels.
+ */
+bool wlr_renderer_preferred_read_format(struct wlr_renderer *renderer,
+ enum wl_shm_format *fmt);
+/**
* Reads out of pixels of the currently bound surface into data. `stride` is in
* bytes.
*
diff --git a/include/wlr/render/wlr_texture.h b/include/wlr/render/wlr_texture.h
index dbfabfee..f210717a 100644
--- a/include/wlr/render/wlr_texture.h
+++ b/include/wlr/render/wlr_texture.h
@@ -54,10 +54,11 @@ void wlr_texture_get_size(struct wlr_texture *texture, int *width, int *height);
bool wlr_texture_is_opaque(struct wlr_texture *texture);
/**
- * Update a texture with raw pixels. The texture must be mutable.
- */
+ * Update a texture with raw pixels. The texture must be mutable, and the input
+ * data must have the same pixel format that the texture was created with.
+ */
bool wlr_texture_write_pixels(struct wlr_texture *texture,
- enum wl_shm_format wl_fmt, uint32_t stride, uint32_t width, uint32_t height,
+ uint32_t stride, uint32_t width, uint32_t height,
uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y,
const void *data);
diff --git a/include/wlr/types/meson.build b/include/wlr/types/meson.build
index 6a8955c3..3f61ae20 100644
--- a/include/wlr/types/meson.build
+++ b/include/wlr/types/meson.build
@@ -5,21 +5,23 @@ install_headers(
'wlr_cursor.h',
'wlr_data_device.h',
'wlr_export_dmabuf_v1.h',
- 'wlr_gamma_control.h',
'wlr_gamma_control_v1.h',
- 'wlr_idle.h',
+ 'wlr_gamma_control.h',
'wlr_idle_inhibit_v1.h',
+ 'wlr_idle.h',
'wlr_input_device.h',
'wlr_input_inhibitor.h',
+ 'wlr_input_method_v2.h',
'wlr_keyboard.h',
'wlr_layer_shell_v1.h',
'wlr_linux_dmabuf_v1.h',
'wlr_list.h',
'wlr_matrix.h',
- 'wlr_output.h',
'wlr_output_damage.h',
'wlr_output_layout.h',
+ 'wlr_output.h',
'wlr_pointer.h',
+ 'wlr_presentation_time.h',
'wlr_primary_selection.h',
'wlr_region.h',
'wlr_screencopy_v1.h',
@@ -30,13 +32,14 @@ install_headers(
'wlr_tablet_pad.h',
'wlr_tablet_tool.h',
'wlr_tablet_v2.h',
+ 'wlr_text_input_v3.h',
'wlr_touch.h',
'wlr_virtual_keyboard_v1.h',
'wlr_wl_shell.h',
'wlr_xcursor_manager.h',
'wlr_xdg_decoration_v1.h',
'wlr_xdg_output_v1.h',
- 'wlr_xdg_shell.h',
'wlr_xdg_shell_v6.h',
+ 'wlr_xdg_shell.h',
subdir: 'wlr/types',
)
diff --git a/include/wlr/types/wlr_box.h b/include/wlr/types/wlr_box.h
index 11a53b56..8e25918b 100644
--- a/include/wlr/types/wlr_box.h
+++ b/include/wlr/types/wlr_box.h
@@ -9,6 +9,7 @@
#ifndef WLR_TYPES_WLR_BOX_H
#define WLR_TYPES_WLR_BOX_H
+#include <pixman.h>
#include <stdbool.h>
#include <wayland-server.h>
@@ -40,4 +41,6 @@ void wlr_box_transform(const struct wlr_box *box,
void wlr_box_rotated_bounds(const struct wlr_box *box, float rotation,
struct wlr_box *dest);
+void wlr_box_from_pixman_box32(const pixman_box32_t box, struct wlr_box *dest);
+
#endif
diff --git a/include/wlr/types/wlr_cursor.h b/include/wlr/types/wlr_cursor.h
index ba582be9..44ced1f0 100644
--- a/include/wlr/types/wlr_cursor.h
+++ b/include/wlr/types/wlr_cursor.h
@@ -91,6 +91,18 @@ bool wlr_cursor_warp(struct wlr_cursor *cur, struct wlr_input_device *dev,
void wlr_cursor_absolute_to_layout_coords(struct wlr_cursor *cur,
struct wlr_input_device *dev, double x, double y, double *lx, double *ly);
+
+/**
+ * Warp the cursor to the given x and y coordinates. If the given point is out
+ * of the layout boundaries or constraints, the closest point will be used.
+ * If one coordinate is NAN, it will be ignored.
+ *
+ * `dev` may be passed to respect device mapping constraints. If `dev` is NULL,
+ * device mapping constraints will be ignored.
+ */
+void wlr_cursor_warp_closest(struct wlr_cursor *cur,
+ struct wlr_input_device *dev, double x, double y);
+
/**
* Warp the cursor to the given x and y in absolute 0..1 coordinates. If the
* given point is out of the layout boundaries or constraints, the closest point
diff --git a/include/wlr/types/wlr_data_device.h b/include/wlr/types/wlr_data_device.h
index c45e8d1c..9ce8f400 100644
--- a/include/wlr/types/wlr_data_device.h
+++ b/include/wlr/types/wlr_data_device.h
@@ -73,7 +73,6 @@ struct wlr_data_source {
// source status
bool accepted;
struct wlr_data_offer *offer;
- struct wlr_seat_client *seat_client;
// drag'n'drop status
enum wl_data_device_manager_dnd_action current_dnd_action;
diff --git a/include/wlr/types/wlr_input_method_v2.h b/include/wlr/types/wlr_input_method_v2.h
new file mode 100644
index 00000000..d22d54d1
--- /dev/null
+++ b/include/wlr/types/wlr_input_method_v2.h
@@ -0,0 +1,87 @@
+/*
+ * This an unstable interface of wlroots. No guarantees are made regarding the
+ * future consistency of this API.
+ */
+#ifndef WLR_USE_UNSTABLE
+#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features"
+#endif
+
+#ifndef WLR_TYPES_WLR_INPUT_METHOD_V2_H
+#define WLR_TYPES_WLR_INPUT_METHOD_V2_H
+#include <stdint.h>
+#include <stdlib.h>
+#include <wayland-server.h>
+#include <wlr/types/wlr_seat.h>
+
+struct wlr_input_method_v2_preedit_string {
+ char *text;
+ int32_t cursor_begin;
+ int32_t cursor_end;
+};
+
+struct wlr_input_method_v2_delete_surrounding_text {
+ uint32_t before_length;
+ uint32_t after_length;
+};
+
+struct wlr_input_method_v2_state {
+ struct wlr_input_method_v2_preedit_string preedit;
+ char *commit_text;
+ struct wlr_input_method_v2_delete_surrounding_text delete;
+};
+
+struct wlr_input_method_v2 {
+ struct wl_resource *resource;
+
+ struct wlr_seat *seat;
+
+ struct wlr_input_method_v2_state pending;
+ struct wlr_input_method_v2_state current;
+ bool active; // pending compositor-side state
+ bool client_active; // state known to the client
+ uint32_t current_serial; // received in last commit call
+
+ struct wl_list link;
+
+ struct wl_listener seat_destroy;
+
+ struct {
+ struct wl_signal commit; // (struct wlr_input_method_v2*)
+ struct wl_signal destroy; // (struct wlr_input_method_v2*)
+ } events;
+};
+
+struct wlr_input_method_manager_v2 {
+ struct wl_global *global;
+ struct wl_list bound_resources; // struct wl_resource*::link
+ struct wl_list input_methods; // struct wlr_input_method_v2*::link
+
+ struct wl_listener display_destroy;
+
+ struct {
+ struct wl_signal input_method; // (struct wlr_input_method_v2*)
+ struct wl_signal destroy; // (struct wlr_input_method_manager_v2*)
+ } events;
+};
+
+struct wlr_input_method_manager_v2 *wlr_input_method_manager_v2_create(
+ struct wl_display *display);
+void wlr_input_method_manager_v2_destroy(
+ struct wlr_input_method_manager_v2 *manager);
+
+void wlr_input_method_v2_send_activate(
+ struct wlr_input_method_v2 *input_method);
+void wlr_input_method_v2_send_deactivate(
+ struct wlr_input_method_v2 *input_method);
+void wlr_input_method_v2_send_surrounding_text(
+ struct wlr_input_method_v2 *input_method, const char *text,
+ uint32_t cursor, uint32_t anchor);
+void wlr_input_method_v2_send_content_type(
+ struct wlr_input_method_v2 *input_method, uint32_t hint,
+ uint32_t purpose);
+void wlr_input_method_v2_send_text_change_cause(
+ struct wlr_input_method_v2 *input_method, uint32_t cause);
+void wlr_input_method_v2_send_done(struct wlr_input_method_v2 *input_method);
+void wlr_input_method_v2_send_unavailable(
+ struct wlr_input_method_v2 *input_method);
+#endif
diff --git a/include/wlr/types/wlr_output.h b/include/wlr/types/wlr_output.h
index ecd4f759..9ccfbbb5 100644
--- a/include/wlr/types/wlr_output.h
+++ b/include/wlr/types/wlr_output.h
@@ -65,7 +65,7 @@ struct wlr_output {
struct wl_list resources;
char name[24];
- char make[48];
+ char make[56];
char model[16];
char serial[16];
int32_t phys_width, phys_height; // mm
@@ -88,9 +88,15 @@ struct wlr_output {
float transform_matrix[9];
struct {
+ // Request to render a frame
struct wl_signal frame;
+ // Emitted when buffers need to be swapped (because software cursors or
+ // fullscreen damage or because of backend-specific logic)
struct wl_signal needs_swap;
+ // Emitted right before buffer swap
struct wl_signal swap_buffers; // wlr_output_event_swap_buffers
+ // Emitted right after the buffer has been presented to the user
+ struct wl_signal present; // wlr_output_event_present
struct wl_signal enable;
struct wl_signal mode;
struct wl_signal scale;
@@ -123,6 +129,32 @@ struct wlr_output_event_swap_buffers {
pixman_region32_t *damage;
};
+enum wlr_output_present_flag {
+ // The presentation was synchronized to the "vertical retrace" by the
+ // display hardware such that tearing does not happen.
+ WLR_OUTPUT_PRESENT_VSYNC = 0x1,
+ // The display hardware provided measurements that the hardware driver
+ // converted into a presentation timestamp.
+ WLR_OUTPUT_PRESENT_HW_CLOCK = 0x2,
+ // The display hardware signalled that it started using the new image
+ // content.
+ WLR_OUTPUT_PRESENT_HW_COMPLETION = 0x4,
+ // The presentation of this update was done zero-copy.
+ WLR_OUTPUT_PRESENT_ZERO_COPY = 0x8,
+};
+
+struct wlr_output_event_present {
+ struct wlr_output *output;
+ // Time when the content update turned into light the first time.
+ struct timespec *when;
+ // Vertical retrace counter. Zero if unavailable.
+ unsigned seq;
+ // Prediction of how many nanoseconds after `when` the very next output
+ // refresh may occur. Zero if unknown.
+ int refresh; // nsec
+ uint32_t flags; // enum wlr_output_present_flag
+};
+
struct wlr_surface;
/**
@@ -182,7 +214,7 @@ void wlr_output_schedule_frame(struct wlr_output *output);
/**
* Returns the maximum length of each gamma ramp, or 0 if unsupported.
*/
-uint32_t wlr_output_get_gamma_size(struct wlr_output *output);
+size_t wlr_output_get_gamma_size(struct wlr_output *output);
/**
* Sets the gamma table for this output. `r`, `g` and `b` are gamma ramps for
* red, green and blue. `size` is the length of the ramps and must not exceed
@@ -190,8 +222,8 @@ uint32_t wlr_output_get_gamma_size(struct wlr_output *output);
*
* Providing zero-sized ramps resets the gamma table.
*/
-bool wlr_output_set_gamma(struct wlr_output *output,
- uint32_t size, uint16_t *r, uint16_t *g, uint16_t *b);
+bool wlr_output_set_gamma(struct wlr_output *output, size_t size,
+ const uint16_t *r, const uint16_t *g, const uint16_t *b);
bool wlr_output_export_dmabuf(struct wlr_output *output,
struct wlr_dmabuf_attributes *attribs);
void wlr_output_set_fullscreen_surface(struct wlr_output *output,
diff --git a/include/wlr/types/wlr_pointer_constraints_v1.h b/include/wlr/types/wlr_pointer_constraints_v1.h
new file mode 100644
index 00000000..fef8c2e9
--- /dev/null
+++ b/include/wlr/types/wlr_pointer_constraints_v1.h
@@ -0,0 +1,102 @@
+/*
+ * This an unstable interface of wlroots. No guarantees are made regarding the
+ * future consistency of this API.
+ */
+#ifndef WLR_USE_UNSTABLE
+#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features"
+#endif
+
+#ifndef WLR_TYPES_WLR_POINTER_CONSTRAINTS_V1_H
+#define WLR_TYPES_WLR_POINTER_CONSTRAINTS_V1_H
+
+#include <stdint.h>
+#include <wayland-server.h>
+#include <pixman.h>
+#include <wlr/types/wlr_box.h>
+#include <wlr/types/wlr_seat.h>
+#include "pointer-constraints-unstable-v1-protocol.h"
+
+struct wlr_seat;
+
+enum wlr_pointer_constraint_v1_type {
+ WLR_POINTER_CONSTRAINT_V1_LOCKED,
+ WLR_POINTER_CONSTRAINT_V1_CONFINED,
+};
+
+enum wlr_pointer_constraint_v1_state_field {
+ WLR_POINTER_CONSTRAINT_V1_STATE_REGION = 1 << 0,
+ WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT = 1 << 1,
+};
+
+struct wlr_pointer_constraint_v1_state {
+ uint32_t committed; // enum wlr_pointer_constraint_v1_state_field
+ pixman_region32_t region;
+
+ // only valid for locked_pointer
+ struct {
+ double x, y;
+ } cursor_hint;
+};
+
+struct wlr_pointer_constraint_v1 {
+ struct wlr_pointer_constraints_v1 *pointer_constraints;
+
+ struct wl_resource *resource;
+ struct wlr_surface *surface;
+ struct wlr_seat *seat;
+ enum zwp_pointer_constraints_v1_lifetime lifetime;
+ enum wlr_pointer_constraint_v1_type type;
+ pixman_region32_t region;
+
+ struct wlr_pointer_constraint_v1_state current, pending;
+
+ struct wl_listener surface_commit;
+ struct wl_listener surface_destroy;
+ struct wl_listener seat_destroy;
+
+ struct wl_list link; // wlr_pointer_constraints_v1::constraints
+
+ struct {
+ struct wl_signal destroy;
+ } events;
+
+ void *data;
+};
+
+struct wlr_pointer_constraints_v1 {
+ struct wl_list resources; // wl_resource_get_link
+ struct wl_global *global;
+
+ struct {
+ /**
+ * Called when a new pointer constraint is created.
+ *
+ * data: struct wlr_pointer_constraint_v1 *
+ */
+ struct wl_signal new_constraint;
+ } events;
+
+ struct wl_list constraints; // wlr_pointer_constraint_v1::link
+
+ void *data;
+};
+
+struct wlr_pointer_constraints_v1 *wlr_pointer_constraints_v1_create(
+ struct wl_display *display);
+void wlr_pointer_constraints_v1_destroy(
+ struct wlr_pointer_constraints_v1 *pointer_constraints);
+
+struct wlr_pointer_constraint_v1 *
+ wlr_pointer_constraints_v1_constraint_for_surface(
+ struct wlr_pointer_constraints_v1 *pointer_constraints,
+ struct wlr_surface *surface, struct wlr_seat *seat);
+
+void wlr_pointer_constraint_v1_send_activated(
+ struct wlr_pointer_constraint_v1 *constraint);
+/**
+ * Deactivate the constraint. May destroy the constraint.
+ */
+void wlr_pointer_constraint_v1_send_deactivated(
+ struct wlr_pointer_constraint_v1 *constraint);
+
+#endif
diff --git a/include/wlr/types/wlr_presentation_time.h b/include/wlr/types/wlr_presentation_time.h
new file mode 100644
index 00000000..9f9f0e87
--- /dev/null
+++ b/include/wlr/types/wlr_presentation_time.h
@@ -0,0 +1,59 @@
+/*
+ * This an unstable interface of wlroots. No guarantees are made regarding the
+ * future consistency of this API.
+ */
+#ifndef WLR_USE_UNSTABLE
+#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features"
+#endif
+
+#ifndef WLR_TYPES_WLR_PRESENTATION_TIME_H
+#define WLR_TYPES_WLR_PRESENTATION_TIME_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <time.h>
+#include <wayland-server.h>
+
+struct wlr_presentation {
+ struct wl_global *global;
+ struct wl_list resources; // wl_resource_get_link
+ struct wl_list feedbacks; // wlr_presentation_feedback::link
+ clockid_t clock;
+
+ struct {
+ struct wl_signal destroy;
+ } events;
+
+ struct wl_listener display_destroy;
+};
+
+struct wlr_presentation_feedback {
+ struct wl_resource *resource;
+ struct wlr_presentation *presentation;
+ struct wlr_surface *surface;
+ bool committed;
+ struct wl_list link; // wlr_presentation::feedbacks
+
+ struct wl_listener surface_commit;
+ struct wl_listener surface_destroy;
+};
+
+struct wlr_presentation_event {
+ struct wlr_output *output;
+ uint64_t tv_sec;
+ uint32_t tv_nsec;
+ uint32_t refresh;
+ uint64_t seq;
+ uint32_t flags; // wp_presentation_feedback_kind
+};
+
+struct wlr_backend;
+
+struct wlr_presentation *wlr_presentation_create(struct wl_display *display,
+ struct wlr_backend *backend);
+void wlr_presentation_destroy(struct wlr_presentation *presentation);
+void wlr_presentation_send_surface_presented(
+ struct wlr_presentation *presentation, struct wlr_surface *surface,
+ struct wlr_presentation_event *event);
+
+#endif
diff --git a/include/wlr/types/wlr_region.h b/include/wlr/types/wlr_region.h
index 3c4a0532..ec7f73aa 100644
--- a/include/wlr/types/wlr_region.h
+++ b/include/wlr/types/wlr_region.h
@@ -10,8 +10,7 @@
#define WLR_TYPES_WLR_REGION_H
#include <pixman.h>
-
-struct wl_resource;
+#include <wayland-server-protocol.h>
/*
* Creates a new region resource with the provided new ID. If `resource_list` is
diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h
index b3c02cf2..59de8207 100644
--- a/include/wlr/types/wlr_seat.h
+++ b/include/wlr/types/wlr_seat.h
@@ -146,6 +146,10 @@ struct wlr_seat_pointer_state {
uint32_t grab_time;
struct wl_listener surface_destroy;
+
+ struct {
+ struct wl_signal focus_change; // wlr_seat_pointer_focus_change_event
+ } events;
};
// TODO: May be useful to be able to simulate keyboard input events
@@ -238,6 +242,12 @@ struct wlr_seat_pointer_request_set_cursor_event {
int32_t hotspot_x, hotspot_y;
};
+struct wlr_seat_pointer_focus_change_event {
+ struct wlr_seat *seat;
+ struct wlr_surface *old_surface, *new_surface;
+ double sx, sy;
+};
+
/**
* Allocates a new wlr_seat and adds a wl_seat global to the display.
*/
@@ -546,6 +556,9 @@ bool wlr_seat_touch_has_grab(struct wlr_seat *seat);
bool wlr_seat_validate_grab_serial(struct wlr_seat *seat, uint32_t serial);
struct wlr_seat_client *wlr_seat_client_from_resource(
- struct wl_resource *resource);
+ struct wl_resource *resource);
+
+struct wlr_seat_client *wlr_seat_client_from_pointer_resource(
+ struct wl_resource *resource);
#endif
diff --git a/include/wlr/types/wlr_surface.h b/include/wlr/types/wlr_surface.h
index 6177e059..b8f8c02a 100644
--- a/include/wlr/types/wlr_surface.h
+++ b/include/wlr/types/wlr_surface.h
@@ -83,6 +83,11 @@ struct wlr_surface {
*/
pixman_region32_t opaque_region;
/**
+ * The current input region, in surface-local coordinates. It is clipped to
+ * the surface bounds.
+ */
+ pixman_region32_t input_region;
+ /**
* `current` contains the current, committed surface state. `pending`
* accumulates state changes from the client between commits and shouldn't
* be accessed by the compositor directly. `previous` contains the state of
diff --git a/include/wlr/types/wlr_text_input_v3.h b/include/wlr/types/wlr_text_input_v3.h
new file mode 100644
index 00000000..0db0cf47
--- /dev/null
+++ b/include/wlr/types/wlr_text_input_v3.h
@@ -0,0 +1,93 @@
+/*
+ * This an unstable interface of wlroots. No guarantees are made regarding the
+ * future consistency of this API.
+ */
+#ifndef WLR_USE_UNSTABLE
+#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features"
+#endif
+
+#ifndef WLR_TYPES_WLR_TEXT_INPUT_V3_H
+#define WLR_TYPES_WLR_TEXT_INPUT_V3_H
+
+#include <wayland-server.h>
+#include <wlr/types/wlr_seat.h>
+#include <wlr/types/wlr_surface.h>
+
+struct wlr_text_input_v3_state {
+ struct {
+ char *text; // NULL is allowed and equivalent to empty string
+ uint32_t cursor;
+ uint32_t anchor;
+ } surrounding;
+
+ uint32_t text_change_cause;
+
+ struct {
+ uint32_t hint;
+ uint32_t purpose;
+ } content_type;
+
+ struct {
+ int32_t x;
+ int32_t y;
+ int32_t width;
+ int32_t height;
+ } cursor_rectangle;
+};
+
+struct wlr_text_input_v3 {
+ struct wlr_seat *seat; // becomes null when seat destroyed
+ struct wl_resource *resource;
+ struct wlr_surface *focused_surface;
+ struct wlr_text_input_v3_state pending;
+ struct wlr_text_input_v3_state current;
+ uint32_t current_serial; // next in line to send
+ bool pending_enabled;
+ bool current_enabled;
+
+ struct wl_list link;
+
+ struct wl_listener surface_destroy;
+ struct wl_listener seat_destroy;
+
+ struct {
+ struct wl_signal enable; // (struct wlr_text_input_v3*)
+ struct wl_signal commit; // (struct wlr_text_input_v3*)
+ struct wl_signal disable; // (struct wlr_text_input_v3*)
+ struct wl_signal destroy; // (struct wlr_text_input_v3*)
+ } events;
+};
+
+struct wlr_text_input_manager_v3 {
+ struct wl_global *global;
+
+ struct wl_list bound_resources; // struct wl_resource*::link
+ struct wl_list text_inputs; // struct wlr_text_input_v3::resource::link
+
+ struct wl_listener display_destroy;
+
+ struct {
+ struct wl_signal text_input; // (struct wlr_text_input_v3*)
+ struct wl_signal destroy; // (struct wlr_input_method_manager_v3*)
+ } events;
+};
+
+struct wlr_text_input_manager_v3 *wlr_text_input_manager_v3_create(
+ struct wl_display *wl_display);
+void wlr_text_input_manager_v3_destroy(
+ struct wlr_text_input_manager_v3 *manager);
+
+// Sends enter to the surface and saves it
+void wlr_text_input_v3_send_enter(struct wlr_text_input_v3 *text_input,
+ struct wlr_surface *wlr_surface);
+// Sends leave to the currently focused surface and clears it
+void wlr_text_input_v3_send_leave(struct wlr_text_input_v3 *text_input);
+void wlr_text_input_v3_send_preedit_string(struct wlr_text_input_v3 *text_input,
+ const char *text, uint32_t cursor_begin, uint32_t cursor_end);
+void wlr_text_input_v3_send_commit_string(struct wlr_text_input_v3 *text_input,
+ const char *text);
+void wlr_text_input_v3_send_delete_surrounding_text(
+ struct wlr_text_input_v3 *text_input, uint32_t before_length,
+ uint32_t after_length);
+void wlr_text_input_v3_send_done(struct wlr_text_input_v3 *text_input);
+#endif
diff --git a/include/wlr/util/region.h b/include/wlr/util/region.h
index 32387bfb..4aca07e1 100644
--- a/include/wlr/util/region.h
+++ b/include/wlr/util/region.h
@@ -16,6 +16,8 @@
#ifndef WLR_UTIL_REGION_H
#define WLR_UTIL_REGION_H
+
+#include <stdbool.h>
#include <pixman.h>
#include <wayland-server.h>
@@ -48,4 +50,7 @@ void wlr_region_expand(pixman_region32_t *dst, pixman_region32_t *src,
void wlr_region_rotated_bounds(pixman_region32_t *dst, pixman_region32_t *src,
float rotation, int ox, int oy);
+bool wlr_region_confine(pixman_region32_t *region, double x1, double y1, double x2,
+ double y2, double *x2_out, double *y2_out);
+
#endif
diff --git a/include/wlr/xwayland.h b/include/wlr/xwayland.h
index eb5d6985..8247aa15 100644
--- a/include/wlr/xwayland.h
+++ b/include/wlr/xwayland.h
@@ -163,6 +163,7 @@ struct wlr_xwayland_surface {
struct wl_signal set_pid;
struct wl_signal set_window_type;
struct wl_signal set_hints;
+ struct wl_signal set_decorations;
struct wl_signal set_override_redirect;
struct wl_signal ping_timeout;
} events;
diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h
index 3536bbc8..dc40cc3e 100644
--- a/include/xwayland/xwm.h
+++ b/include/xwayland/xwm.h
@@ -30,7 +30,7 @@ enum atom_name {
UTF8_STRING,
WM_S0,
NET_SUPPORTED,
- NET_WM_S0,
+ NET_WM_CM_S0,
NET_WM_PID,
NET_WM_NAME,
NET_WM_STATE,
diff --git a/meson.build b/meson.build
index ff4e5e5f..18a5d908 100644
--- a/meson.build
+++ b/meson.build
@@ -1,9 +1,9 @@
project(
'wlroots',
'c',
- version: '0.0.1',
+ version: '0.1.0',
license: 'MIT',
- meson_version: '>=0.47.1',
+ meson_version: '>=0.48.0',
default_options: [
'c_std=c11',
'warning_level=2',
@@ -42,7 +42,7 @@ endif
# Avoid wl_buffer deprecation warnings
add_project_arguments('-DWL_HIDE_DEPRECATED', language: 'c')
-wayland_server = dependency('wayland-server', version: '>=1.15')
+wayland_server = dependency('wayland-server', version: '>=1.16')
wayland_client = dependency('wayland-client')
wayland_egl = dependency('wayland-egl')
wayland_protos = dependency('wayland-protocols', version: '>=1.15')
@@ -55,8 +55,9 @@ xkbcommon = dependency('xkbcommon')
udev = dependency('libudev')
pixman = dependency('pixman-1')
libcap = dependency('libcap', required: get_option('libcap'))
-logind = dependency('lib' + get_option('logind-provider'), required: get_option('logind'))
-math = cc.find_library('m', required: false)
+logind = dependency('lib' + get_option('logind-provider'), required: get_option('logind'), version: '>=237')
+math = cc.find_library('m')
+rt = cc.find_library('rt')
wlr_parts = []
wlr_deps = []
@@ -111,6 +112,12 @@ wlr_deps += [
math,
]
+if host_machine.system() == 'freebsd'
+ override_options = ['b_lundef=false']
+else
+ override_options = []
+endif
+
symbols_file = 'wlroots.syms'
symbols_flag = '-Wl,--version-script,@0@/@1@'.format(meson.current_source_dir(), symbols_file)
lib_wlr = library(
@@ -122,6 +129,7 @@ lib_wlr = library(
install: true,
link_args : symbols_flag,
link_depends: symbols_file,
+ override_options: override_options,
)
wlroots = declare_dependency(
diff --git a/protocol/input-method-unstable-v2.xml b/protocol/input-method-unstable-v2.xml
new file mode 100644
index 00000000..62be9d94
--- /dev/null
+++ b/protocol/input-method-unstable-v2.xml
@@ -0,0 +1,490 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="input_method_unstable_v2">
+
+ <copyright>
+ Copyright © 2008-2011 Kristian Høgsberg
+ Copyright © 2010-2011 Intel Corporation
+ Copyright © 2012-2013 Collabora, Ltd.
+ Copyright © 2012, 2013 Intel Corporation
+ Copyright © 2015, 2016 Jan Arne Petersen
+ Copyright © 2017, 2018 Red Hat, Inc.
+ Copyright © 2018 Purism SPC
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+ </copyright>
+
+ <description summary="Protocol for creating input methods">
+ This protocol allows applications to act as input methods for compositors.
+
+ An input method context is used to manage the state of the input method.
+
+ Text strings are UTF-8 encoded, their indices and lengths are in bytes.
+
+ This document adheres to the RFC 2119 when using words like "must",
+ "should", "may", etc.
+
+ Warning! The protocol described in this file is experimental and
+ backward incompatible changes may be made. Backward compatible changes
+ may be added together with the corresponding interface version bump.
+ Backward incompatible changes are done by bumping the version number in
+ the protocol and interface names and resetting the interface version.
+ Once the protocol is to be declared stable, the 'z' prefix and the
+ version number in the protocol and interface names are removed and the
+ interface version number is reset.
+ </description>
+
+ <interface name="zwp_input_method_v2" version="1">
+ <description summary="input method">
+ An input method object allows for clients to compose text.
+
+ The objects connects the client to a text input in an application, and
+ lets the client to serve as an input method for a seat.
+
+ The zwp_input_method_v2 object can occupy two distinct states: active and
+ inactive. In the active state, the object is associated to and
+ communicates with a text input. In the inactive state, there is no
+ associated text input, and the only communication is with the compositor.
+ Initially, the input method is in the inactive state.
+
+ Requests issued in the inactive state must be accepted by the compositor.
+ Because of the serial mechanism, and the state reset on activate event,
+ they will not have any effect on the state of the next text input.
+
+ There must be no more than one input method object per seat.
+ </description>
+
+ <event name="activate">
+ <description summary="input method has been requested">
+ Notification that a text input focused on this seat requested the input
+ method to be activated.
+
+ This event serves the purpose of providing the compositor with an
+ active input method.
+
+ This event resets all state associated with previous enable, disable,
+ surrounding_text, text_change_cause, and content_type events, as well
+ as the state associated with set_preedit_string, commit_string, and
+ delete_surrounding_text requests. In addition, it marks the
+ zwp_input_method_v2 object as active, and makes any existing
+ zwp_input_popup_surface_v2 objects visible.
+
+ The surrounding_text, and content_type events must follow before the
+ next done event if the text input supports the respective
+ functionality.
+
+ State set with this event is double-buffered. It will get applied on
+ the next zwp_input_method_v2.done event, and stay valid until changed.
+ </description>
+ </event>
+
+ <event name="deactivate">
+ <description summary="deactivate event">
+ Notification that no focused text input currently needs an active
+ input method on this seat.
+
+ This event marks the zwp_input_method_v2 object as inactive. The
+ compositor must make all existing zwp_input_popup_surface_v2 objects
+ invisible until the next activate event.
+
+ State set with this event is double-buffered. It will get applied on
+ the next zwp_input_method_v2.done event, and stay valid until changed.
+ </description>
+ </event>
+
+ <event name="surrounding_text">
+ <description summary="surrounding text event">
+ Updates the surrounding plain text around the cursor, excluding the
+ preedit text.
+
+ If any preedit text is present, it is replaced with the cursor for the
+ purpose of this event.
+
+ The argument text is a buffer containing the preedit string, and must
+ include the cursor position, and the complete selection. It should
+ contain additional characters before and after these. There is a
+ maximum length of wayland messages, so text can not be longer than 4000
+ bytes.
+
+ cursor is the byte offset of the cursor within the text buffer.
+
+ anchor is the byte offset of the selection anchor within the text
+ buffer. If there is no selected text, anchor must be the same as
+ cursor.
+
+ If this event does not arrive before the first done event, the input
+ method may assume that the text input does not support this
+ functionality and ignore following surrounding_text events.
+
+ Values set with this event are double-buffered. They will get applied
+ and set to initial values on the next zwp_input_method_v2.done
+ event.
+
+ The initial state for affected fields is empty, meaning that the text
+ input does not support sending surrounding text. If the empty values
+ get applied, subsequent attempts to change them may have no effect.
+ </description>
+ <arg name="text" type="string"/>
+ <arg name="cursor" type="uint"/>
+ <arg name="anchor" type="uint"/>
+ </event>
+
+ <event name="text_change_cause">
+ <description summary="indicates the cause of surrounding text change">
+ Tells the input method why the text surrounding the cursor changed.
+
+ Whenever the client detects an external change in text, cursor, or
+ anchor position, it must issue this request to the compositor. This
+ request is intended to give the input method a chance to update the
+ preedit text in an appropriate way, e.g. by removing it when the user
+ starts typing with a keyboard.
+
+ cause describes the source of the change.
+
+ The value set with this event is double-buffered. It will get applied
+ and set to its initial value on the next zwp_input_method_v2.done
+ event.
+
+ The initial value of cause is input_method.
+ </description>
+ <arg name="cause" type="uint" enum="zwp_text_input_v3.change_cause"/>
+ </event>
+
+ <event name="content_type">
+ <description summary="content purpose and hint">
+ Indicates the content type and hint for the current
+ zwp_input_method_v2 instance.
+
+ Values set with this event are double-buffered. They will get applied
+ on the next zwp_input_method_v2.done event.
+
+ The initial value for hint is none, and the initial value for purpose
+ is normal.
+ </description>
+ <arg name="hint" type="uint" enum="zwp_text_input_v3.content_hint"/>
+ <arg name="purpose" type="uint" enum="zwp_text_input_v3.content_purpose"/>
+ </event>
+
+ <event name="done">
+ <description summary="apply state">
+ Atomically applies state changes recently sent to the client.
+
+ The done event establishes and updates the state of the client, and
+ must be issued after any changes to apply them.
+
+ Text input state (content purpose, content hint, surrounding text, and
+ change cause) is conceptually double-buffered within an input method
+ context.
+
+ Events modify the pending state, as opposed to the current state in use
+ by the input method. A done event atomically applies all pending state,
+ replacing the current state. After done, the new pending state is as
+ documented for each related request.
+
+ Events must be applied in the order of arrival.
+
+ Neither current nor pending state are modified unless noted otherwise.
+ </description>
+ </event>
+
+ <request name="commit_string">
+ <description summary="commit string">
+ Send the commit string text for insertion to the application.
+
+ Inserts a string at current cursor position (see commit event
+ sequence). The string to commit could be either just a single character
+ after a key press or the result of some composing.
+
+ The argument text is a buffer containing the string to insert. There is
+ a maximum length of wayland messages, so text can not be longer than
+ 4000 bytes.
+
+ Values set with this event are double-buffered. They must be applied
+ and reset to initial on the next zwp_text_input_v3.commit request.
+
+ The initial value of text is an empty string.
+ </description>
+ <arg name="text" type="string"/>
+ </request>
+
+ <request name="set_preedit_string">
+ <description summary="pre-edit string">
+ Send the pre-edit string text to the application text input.
+
+ Place a new composing text (pre-edit) at the current cursor position.
+ Any previously set composing text must be removed. Any previously
+ existing selected text must be removed. The cursor is moved to a new
+ position within the preedit string.
+
+ The argument text is a buffer containing the preedit string. There is
+ a maximum length of wayland messages, so text can not be longer than
+ 4000 bytes.
+
+ The arguments cursor_begin and cursor_end are counted in bytes relative
+ to the beginning of the submitted string buffer. Cursor should be
+ hidden by the text input when both are equal to -1.
+
+ cursor_begin indicates the beginning of the cursor. cursor_end
+ indicates the end of the cursor. It may be equal or different than
+ cursor_begin.
+
+ Values set with this event are double-buffered. They must be applied on
+ the next zwp_input_method_v2.commit event.
+
+ The initial value of text is an empty string. The initial value of
+ cursor_begin, and cursor_end are both 0.
+ </description>
+ <arg name="text" type="string"/>
+ <arg name="cursor_begin" type="int"/>
+ <arg name="cursor_end" type="int"/>
+ </request>
+
+ <request name="delete_surrounding_text">
+ <description summary="delete text">
+ Remove the surrounding text.
+
+ before_length and after_length are the number of bytes before and after
+ the current cursor index (excluding the preedit text) to delete.
+
+ If any preedit text is present, it is replaced with the cursor for the
+ purpose of this event. In effect before_length is counted from the
+ beginning of preedit text, and after_length from its end (see commit
+ event sequence).
+
+ Values set with this event are double-buffered. They must be applied
+ and reset to initial on the next zwp_input_method_v2.commit request.
+
+ The initial values of both before_length and after_length are 0.
+ </description>
+ <arg name="before_length" type="uint"/>
+ <arg name="after_length" type="uint"/>
+ </request>
+
+ <request name="commit">
+ <description summary="apply state">
+ Apply state changes from commit_string, set_preedit_string and
+ delete_surrounding_text requests.
+
+ The state relating to these events is double-buffered, and each one
+ modifies the pending state. This request replaces the current state
+ with the pending state.
+
+ The connected text input is expected to proceed by evaluating the
+ changes in the following order:
+
+ 1. Replace existing preedit string with the cursor.
+ 2. Delete requested surrounding text.
+ 3. Insert commit string with the cursor at its end.
+ 4. Calculate surrounding text to send.
+ 5. Insert new preedit text in cursor position.
+ 6. Place cursor inside preedit text.
+
+ The serial number reflects the last state of the zwp_input_method_v2
+ object known to the client. The value of the serial argument must be
+ equal to the number of done events already issued by that object. When
+ the compositor receives a commit request with a serial different than
+ the number of past done events, it must proceed as normal, except it
+ should not change the current state of the zwp_input_method_v2 object.
+ </description>
+ <arg name="serial" type="uint"/>
+ </request>
+
+ <request name="get_input_popup_surface">
+ <description summary="create popup surface">
+ Creates a new zwp_input_popup_surface_v2 object wrapping a given
+ surface.
+
+ The surface gets assigned the "input_popup" role. If the surface
+ already has an assigned role, the compositor must issue a protocol
+ error.
+ </description>
+ <arg name="id" type="new_id" interface="zwp_input_popup_surface_v2"/>
+ <arg name="surface" type="object" interface="wl_surface"/>
+ </request>
+
+ <request name="grab_keyboard">
+ <description summary="grab hardware keyboard">
+ Allow an input method to receive hardware keyboard input and process
+ key events to generate text events (with pre-edit) over the wire. This
+ allows input methods which compose multiple key events for inputting
+ text like it is done for CJK languages.
+
+ The compositor should send all keyboard events on the seat to the grab
+ holder via the returned wl_keyboard object. Nevertheless, the
+ compositor may decide not to forward any particular event. The
+ compositor must not further process any event after it has been
+ forwarded to the grab holder.
+
+ Releasing the resulting wl_keyboard object releases the grab.
+ </description>
+ <arg name="keyboard" type="new_id"
+ interface="zwp_input_method_keyboard_grab_v2"/>
+ </request>
+
+ <event name="unavailable">
+ <description summary="input method unavailable">
+ The input method ceased to be available.
+
+ The compositor must issue this event as the only event on the object if
+ there was another input_method object associated with the same seat at
+ the time of its creation.
+
+ The compositor must issue this request when the object is no longer
+ useable, e.g. due to seat removal.
+
+ The input method context becomes inert and should be destroyed after
+ deactivation is handled. Any further requests and events except for the
+ destroy request must be ignored.
+ </description>
+ </event>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the text input">
+ Destroys the zwp_text_input_v2 object and any associated child
+ objects, i.e. zwp_input_popup_surface_v2 and
+ zwp_input_method_keyboard_grab_v2.
+ </description>
+ </request>
+ </interface>
+
+ <interface name="zwp_input_popup_surface_v2" version="1">
+ <description summary="popup surface">
+ This interface marks a surface as a popup for interacting with an input
+ method.
+
+ The compositor should place it near the active text input area. It must
+ be visible if and only if the input method is in the active state.
+
+ The client must not destroy the underlying wl_surface while the
+ zwp_input_popup_surface_v2 object exists.
+ </description>
+
+ <event name="text_input_rectangle">
+ <description summary="set text input area position">
+ Notify about the position of the area of the text input expressed as a
+ rectangle in surface local coordinates.
+
+ This is a hint to the input method telling it the relative position of
+ the text being entered.
+ </description>
+ <arg name="x" type="int"/>
+ <arg name="y" type="int"/>
+ <arg name="width" type="int"/>
+ <arg name="height" type="int"/>
+ </event>
+
+ <request name="destroy" type="destructor"/>
+ </interface>
+
+ <interface name="zwp_input_method_keyboard_grab_v2" version="1">
+ <!-- Closely follows wl_keyboard version 6 -->
+ <description summary="keyboard grab">
+ The zwp_input_method_keyboard_grab_v2 interface represents an exclusive
+ grab of the wl_keyboard interface associated with the seat.
+ </description>
+
+ <event name="keymap">
+ <description summary="keyboard mapping">
+ This event provides a file descriptor to the client which can be
+ memory-mapped to provide a keyboard mapping description.
+ </description>
+ <arg name="format" type="uint" enum="wl_keyboard.keymap_format"
+ summary="keymap format"/>
+ <arg name="fd" type="fd" summary="keymap file descriptor"/>
+ <arg name="size" type="uint" summary="keymap size, in bytes"/>
+ </event>
+
+ <event name="key">
+ <description summary="key event">
+ A key was pressed or released.
+ The time argument is a timestamp with millisecond granularity, with an
+ undefined base.
+ </description>
+ <arg name="serial" type="uint" summary="serial number of the key event"/>
+ <arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
+ <arg name="key" type="uint" summary="key that produced the event"/>
+ <arg name="state" type="uint" enum="wl_keyboard.key_state"
+ summary="physical state of the key"/>
+ </event>
+
+ <event name="modifiers">
+ <description summary="modifier and group state">
+ Notifies clients that the modifier and/or group state has changed, and
+ it should update its local state.
+ </description>
+ <arg name="serial" type="uint" summary="serial number of the modifiers event"/>
+ <arg name="mods_depressed" type="uint" summary="depressed modifiers"/>
+ <arg name="mods_latched" type="uint" summary="latched modifiers"/>
+ <arg name="mods_locked" type="uint" summary="locked modifiers"/>
+ <arg name="group" type="uint" summary="keyboard layout"/>
+ </event>
+
+ <request name="release" type="destructor">
+ <description summary="release the grab object"/>
+ </request>
+
+ <event name="repeat_info">
+ <description summary="repeat rate and delay">
+ Informs the client about the keyboard's repeat rate and delay.
+
+ This event is sent as soon as the zwp_input_method_keyboard_grab_v2
+ object has been created, and is guaranteed to be received by the
+ client before any key press event.
+
+ Negative values for either rate or delay are illegal. A rate of zero
+ will disable any repeating (regardless of the value of delay).
+
+ This event can be sent later on as well with a new value if necessary,
+ so clients should continue listening for the event past the creation
+ of zwp_input_method_keyboard_grab_v2.
+ </description>
+ <arg name="rate" type="int"
+ summary="the rate of repeating keys in characters per second"/>
+ <arg name="delay" type="int"
+ summary="delay in milliseconds since key down until repeating starts"/>
+ </event>
+ </interface>
+
+ <interface name="zwp_input_method_manager_v2" version="1">
+ <description summary="input method manager">
+ The input method manager allows the client to become the input method on
+ a chosen seat.
+
+ No more than one input method must be associated with any seat at any
+ given time.
+ </description>
+
+ <request name="get_input_method">
+ <description summary="request an input method object">
+ Request a new input zwp_input_method_v2 object associated with a given
+ seat.
+ </description>
+ <arg name="seat" type="object" interface="wl_seat"/>
+ <arg name="input_method" type="new_id" interface="zwp_input_method_v2"/>
+ </request>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the input method manager">
+ Destroys the zwp_input_method_manager_v2 object.
+
+ The zwp_input_method_v2 objects originating from it remain valid.
+ </description>
+ </request>
+ </interface>
+</protocol>
diff --git a/protocol/meson.build b/protocol/meson.build
index c46ad131..dfe2a5ec 100644
--- a/protocol/meson.build
+++ b/protocol/meson.build
@@ -11,6 +11,7 @@ else
endif
protocols = [
+ [wl_protocol_dir, 'stable/presentation-time/presentation-time.xml'],
[wl_protocol_dir, 'stable/xdg-shell/xdg-shell.xml'],
[wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml'],
@@ -18,11 +19,14 @@ protocols = [
[wl_protocol_dir, 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'],
+ [wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'],
'gamma-control.xml',
'gtk-primary-selection.xml',
'idle.xml',
+ 'input-method-unstable-v2.xml',
'screenshooter.xml',
'server-decoration.xml',
+ 'text-input-unstable-v3.xml',
'virtual-keyboard-unstable-v1.xml',
'wlr-export-dmabuf-unstable-v1.xml',
'wlr-gamma-control-unstable-v1.xml',
@@ -36,8 +40,11 @@ client_protocols = [
[wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml'],
[wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'],
+ [wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'],
'idle.xml',
+ 'input-method-unstable-v2.xml',
'screenshooter.xml',
+ 'text-input-unstable-v3.xml',
'wlr-export-dmabuf-unstable-v1.xml',
'wlr-gamma-control-unstable-v1.xml',
'wlr-input-inhibitor-unstable-v1.xml',
diff --git a/protocol/text-input-unstable-v3.xml b/protocol/text-input-unstable-v3.xml
new file mode 100644
index 00000000..8b710fd6
--- /dev/null
+++ b/protocol/text-input-unstable-v3.xml
@@ -0,0 +1,441 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<protocol name="text_input_unstable_v3">
+ <copyright>
+ Copyright © 2012, 2013 Intel Corporation
+ Copyright © 2015, 2016 Jan Arne Petersen
+ Copyright © 2017, 2018 Red Hat, Inc.
+ Copyright © 2018 Purism SPC
+
+ Permission to use, copy, modify, distribute, and sell this
+ software and its documentation for any purpose is hereby granted
+ without fee, provided that the above copyright notice appear in
+ all copies and that both that copyright notice and this permission
+ notice appear in supporting documentation, and that the name of
+ the copyright holders not be used in advertising or publicity
+ pertaining to distribution of the software without specific,
+ written prior permission. The copyright holders make no
+ representations about the suitability of this software for any
+ purpose. It is provided "as is" without express or implied
+ warranty.
+
+ THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+ THIS SOFTWARE.
+ </copyright>
+
+ <description summary="Protocol for composing text">
+ This protocol allows compositors to act as input methods and to send text
+ to applications. A text input object is used to manage state of what are
+ typically text entry fields in the application.
+
+ This document adheres to the RFC 2119 when using words like "must",
+ "should", "may", etc.
+
+ Warning! The protocol described in this file is experimental and
+ backward incompatible changes may be made. Backward compatible changes
+ may be added together with the corresponding interface version bump.
+ Backward incompatible changes are done by bumping the version number in
+ the protocol and interface names and resetting the interface version.
+ Once the protocol is to be declared stable, the 'z' prefix and the
+ version number in the protocol and interface names are removed and the
+ interface version number is reset.
+ </description>
+
+ <interface name="zwp_text_input_v3" version="1">
+ <description summary="text input">
+ The zwp_text_input_v3 interface represents text input and input methods
+ associated with a seat. It provides enter/leave events to follow the
+ text input focus for a seat.
+
+ Requests are used to enable/disable the text-input object and set
+ state information like surrounding and selected text or the content type.
+ The information about the entered text is sent to the text-input object
+ via the preedit_string and commit_string events.
+
+ Text is valid UTF-8 encoded, indices and lengths are in bytes. Indices
+ must not point to middle bytes inside a code point: they must either
+ point to the first byte of a code point or to the end of the buffer.
+ Lengths must be measured between two valid indices.
+
+ Focus moving throughout surfaces will result in the emission of
+ zwp_text_input_v3.enter and zwp_text_input_v3.leave events. The focused
+ surface must commit zwp_text_input_v3.enable and
+ zwp_text_input_v3.disable requests as the keyboard focus moves across
+ editable and non-editable elements of the UI. Those two requests are not
+ expected to be paired with each other, the compositor must be able to
+ handle consecutive series of the same request.
+
+ State is sent by the state requests (set_surrounding_text,
+ set_content_type and set_cursor_rectangle) and a commit request. After an
+ enter event or disable request all state information is invalidated and
+ needs to be resent by the client.
+ </description>
+
+ <request name="destroy" type="destructor">
+ <description summary="Destroy the wp_text_input">
+ Destroy the wp_text_input object. Also disables all surfaces enabled
+ through this wp_text_input object.
+ </description>
+ </request>
+
+ <request name="enable">
+ <description summary="Request text input to be enabled">
+ Requests text input on the surface previously obtained from the enter
+ event.
+
+ This request must be issued every time the active text input changes
+ to a new one, including within the current surface. Use
+ zwp_text_input_v3.disable when there is no longer any input focus on
+ the current surface.
+
+ This request resets all state associated with previous enable, disable,
+ set_surrounding_text, set_text_change_cause, set_content_type, and
+ set_cursor_rectangle requests, as well as the state associated with
+ preedit_string, commit_string, and delete_surrounding_text events.
+
+ The set_surrounding_text, set_content_type and set_cursor_rectangle
+ requests must follow if the text input supports the necessary
+ functionality.
+
+ State set with this request is double-buffered. It will get applied on
+ the next zwp_text_input_v3.commit request, and stay valid until the
+ next committed enable or disable request.
+
+ The changes must be applied by the compositor after issuing a
+ zwp_text_input_v3.commit request.
+ </description>
+ </request>
+
+ <request name="disable">
+ <description summary="Disable text input on a surface">
+ Explicitly disable text input on the current surface (typically when
+ there is no focus on any text entry inside the surface).
+
+ State set with this request is double-buffered. It will get applied on
+ the next zwp_text_input_v3.commit request.
+ </description>
+ </request>
+
+ <request name="set_surrounding_text">
+ <description summary="sets the surrounding text">
+ Sets the surrounding plain text around the input, excluding the preedit
+ text.
+
+ The client should notify the compositor of any changes in any of the
+ values carried with this request, including changes caused by handling
+ incoming text-input events as well as changes caused by other
+ mechanisms like keyboard typing.
+
+ If the client is unaware of the text around the cursor, it should not
+ issue this request, to signify lack of support to the compositor.
+
+ Text is UTF-8 encoded, and should include the cursor position, the
+ complete selection and additional characters before and after them.
+ There is a maximum length of wayland messages, so text can not be
+ longer than 4000 bytes.
+
+ Cursor is the byte offset of the cursor within text buffer.
+
+ Anchor is the byte offset of the selection anchor within text buffer.
+ If there is no selected text, anchor is the same as cursor.
+
+ If any preedit text is present, it is replaced with a cursor for the
+ purpose of this event.
+
+ Values set with this request are double-buffered. They will get applied
+ on the next zwp_text_input_v3.commit request, and stay valid until the
+ next committed enable or disable request.
+
+ The initial state for affected fields is empty, meaning that the text
+ input does not support sending surrounding text. If the empty values
+ get applied, subsequent attempts to change them may have no effect.
+ </description>
+ <arg name="text" type="string"/>
+ <arg name="cursor" type="int"/>
+ <arg name="anchor" type="int"/>
+ </request>
+
+ <enum name="change_cause">
+ <description summary="text change reason">
+ Reason for the change of surrounding text or cursor posision.
+ </description>
+ <entry name="input_method" value="0" summary="input method caused the change"/>
+ <entry name="other" value="1" summary="something else than the input method caused the change"/>
+ </enum>
+
+ <request name="set_text_change_cause">
+ <description summary="indicates the cause of surrounding text change">
+ Tells the compositor why the text surrounding the cursor changed.
+
+ Whenever the client detects an external change in text, cursor, or
+ anchor posision, it must issue this request to the compositor. This
+ request is intended to give the input method a chance to update the
+ preedit text in an appropriate way, e.g. by removing it when the user
+ starts typing with a keyboard.
+
+ cause describes the source of the change.
+
+ The value set with this request is double-buffered. It must be applied
+ and reset to initial at the next zwp_text_input_v3.commit request.
+
+ The initial value of cause is input_method.
+ </description>
+ <arg name="cause" type="uint" enum="change_cause"/>
+ </request>
+
+ <enum name="content_hint" bitfield="true">
+ <description summary="content hint">
+ Content hint is a bitmask to allow to modify the behavior of the text
+ input.
+ </description>
+ <entry name="none" value="0x0" summary="no special behavior"/>
+ <entry name="completion" value="0x1" summary="suggest word completions"/>
+ <entry name="spellcheck" value="0x2" summary="suggest word corrections"/>
+ <entry name="auto_capitalization" value="0x4" summary="switch to uppercase letters at the start of a sentence"/>
+ <entry name="lowercase" value="0x8" summary="prefer lowercase letters"/>
+ <entry name="uppercase" value="0x10" summary="prefer uppercase letters"/>
+ <entry name="titlecase" value="0x20" summary="prefer casing for titles and headings (can be language dependent)"/>
+ <entry name="hidden_text" value="0x40" summary="characters should be hidden"/>
+ <entry name="sensitive_data" value="0x80" summary="typed text should not be stored"/>
+ <entry name="latin" value="0x100" summary="just Latin characters should be entered"/>
+ <entry name="multiline" value="0x200" summary="the text input is multiline"/>
+ </enum>
+
+ <enum name="content_purpose">
+ <description summary="content purpose">
+ The content purpose allows to specify the primary purpose of a text
+ input.
+
+ This allows an input method to show special purpose input panels with
+ extra characters or to disallow some characters.
+ </description>
+ <entry name="normal" value="0" summary="default input, allowing all characters"/>
+ <entry name="alpha" value="1" summary="allow only alphabetic characters"/>
+ <entry name="digits" value="2" summary="allow only digits"/>
+ <entry name="number" value="3" summary="input a number (including decimal separator and sign)"/>
+ <entry name="phone" value="4" summary="input a phone number"/>
+ <entry name="url" value="5" summary="input an URL"/>
+ <entry name="email" value="6" summary="input an email address"/>
+ <entry name="name" value="7" summary="input a name of a person"/>
+ <entry name="password" value="8" summary="input a password (combine with sensitive_data hint)"/>
+ <entry name="pin" value="9" summary="input is a numeric password (combine with sensitive_data hint)"/>
+ <entry name="date" value="10" summary="input a date"/>
+ <entry name="time" value="11" summary="input a time"/>
+ <entry name="datetime" value="12" summary="input a date and time"/>
+ <entry name="terminal" value="13" summary="input for a terminal"/>
+ </enum>
+
+ <request name="set_content_type">
+ <description summary="set content purpose and hint">
+ Sets the content purpose and content hint. While the purpose is the
+ basic purpose of an input field, the hint flags allow to modify some of
+ the behavior.
+
+ Values set with this request are double-buffered. They will get applied
+ on the next zwp_text_input_v3.commit request.
+ Subsequent attempts to update them may have no effect. The values
+ remain valid until the next committed enable or disable request.
+
+ The initial value for hint is none, and the initial value for purpose
+ is normal.
+ </description>
+ <arg name="hint" type="uint" enum="content_hint"/>
+ <arg name="purpose" type="uint" enum="content_purpose"/>
+ </request>
+
+ <request name="set_cursor_rectangle">
+ <description summary="set cursor position">
+ Marks an area around the cursor as a x, y, width, height rectangle in
+ surface local coordinates.
+
+ Allows the compositor to put a window with word suggestions near the
+ cursor, without obstructing the text being input.
+
+ If the client is unaware of the position of edited text, it should not
+ issue this request, to signify lack of support to the compositor.
+
+ Values set with this request are double-buffered. They will get applied
+ on the next zwp_text_input_v3.commit request, and stay valid until the
+ next committed enable or disable request.
+
+ The initial values describing a cursor rectangle are empty. That means
+ the text input does not support describing the cursor area. If the
+ empty values get applied, subsequent attempts to change them may have
+ no effect.
+ </description>
+ <arg name="x" type="int"/>
+ <arg name="y" type="int"/>
+ <arg name="width" type="int"/>
+ <arg name="height" type="int"/>
+ </request>
+
+ <request name="commit">
+ <description summary="commit state">
+ Atomically applies state changes recently sent to the compositor.
+
+ The commit request establishes and updates the state of the client, and
+ must be issued after any changes to apply them.
+
+ Text input state (enabled status, content purpose, content hint,
+ surrounding text and change cause, cursor rectangle) is conceptually
+ double-buffered within the context of a text input, i.e. between a
+ committed enable request and the following committed enable or disable
+ request.
+
+ Protocol requests modify the pending state, as opposed to the current
+ state in use by the input method. A commit request atomically applies
+ all pending state, replacing the current state. After commit, the new
+ pending state is as documented for each related request.
+
+ Requests are applied in the order of arrival.
+
+ Neither current nor pending state are modified unless noted otherwise.
+
+ The compositor must count the number of commit requests coming from
+ each zwp_text_input_v3 object and use the count as the serial in done
+ events.
+ </description>
+ </request>
+
+ <event name="enter">
+ <description summary="enter event">
+ Notification that this seat's text-input focus is on a certain surface.
+
+ When the seat has the keyboard capability the text-input focus follows
+ the keyboard focus. This event sets the current surface for the
+ text-input object.
+ </description>
+ <arg name="surface" type="object" interface="wl_surface"/>
+ </event>
+
+ <event name="leave">
+ <description summary="leave event">
+ Notification that this seat's text-input focus is no longer on a
+ certain surface. The client should reset any preedit string previously
+ set.
+
+ The leave notification clears the current surface. It is sent before
+ the enter notification for the new focus.
+
+ When the seat has the keyboard capability the text-input focus follows
+ the keyboard focus.
+ </description>
+ <arg name="surface" type="object" interface="wl_surface"/>
+ </event>
+
+ <event name="preedit_string">
+ <description summary="pre-edit">
+ Notify when a new composing text (pre-edit) should be set at the
+ current cursor position. Any previously set composing text must be
+ removed. Any previously existing selected text must be removed.
+
+ The argument text contains the pre-edit string buffer.
+
+ The parameters cursor_begin and cursor_end are counted in bytes
+ relative to the beginning of the submitted text buffer. Cursor should
+ be hidden when both are equal to -1.
+
+ They could be represented by the client as a line if both values are
+ the same, or as a text highlight otherwise.
+
+ Values set with this event are double-buffered. They must be applied
+ and reset to initial on the next zwp_text_input_v3.done event.
+
+ The initial value of text is an empty string, and cursor_begin,
+ cursor_end and cursor_hidden are all 0.
+ </description>
+ <arg name="text" type="string" allow-null="true"/>
+ <arg name="cursor_begin" type="int"/>
+ <arg name="cursor_end" type="int"/>
+ </event>
+
+ <event name="commit_string">
+ <description summary="text commit">
+ Notify when text should be inserted into the editor widget. The text to
+ commit could be either just a single character after a key press or the
+ result of some composing (pre-edit).
+
+ Values set with this event are double-buffered. They must be applied
+ and reset to initial on the next zwp_text_input_v3.done event.
+
+ The initial value of text is an empty string.
+ </description>
+ <arg name="text" type="string" allow-null="true"/>
+ </event>
+
+ <event name="delete_surrounding_text">
+ <description summary="delete surrounding text">
+ Notify when the text around the current cursor position should be
+ deleted.
+
+ Before_length and after_length are the number of bytes before and after
+ the current cursor index (excluding the selection) to delete.
+
+ If a preedit text is present, in effect before_length is counted from
+ the beginning of it, and after_length from its end (see done event
+ sequence).
+
+ Values set with this event are double-buffered. They must be applied
+ and reset to initial on the next zwp_text_input_v3.done event.
+
+ The initial values of both before_length and after_length are 0.
+ </description>
+ <arg name="before_length" type="uint" summary="length of text before current cursor position"/>
+ <arg name="after_length" type="uint" summary="length of text after current cursor position"/>
+ </event>
+
+ <event name="done">
+ <description summary="apply changes">
+ Instruct the application to apply changes to state requested by the
+ preedit_string, commit_string and delete_surrounding_text events. The
+ state relating to these events is double-buffered, and each one
+ modifies the pending state. This event replaces the current state with
+ the pending state.
+
+ The application must proceed by evaluating the changes in the following
+ order:
+
+ 1. Replace existing preedit string with the cursor.
+ 2. Delete requested surrounding text.
+ 3. Insert commit string with the cursor at its end.
+ 4. Calculate surrounding text to send.
+ 5. Insert new preedit text in cursor position.
+ 6. Place cursor inside preedit text.
+
+ The serial number reflects the last state of the zwp_text_input_v3
+ object known to the compositor. The value of the serial argument must
+ be equal to the number of commit requests already issued on that object.
+ When the client receives a done event with a serial different than the
+ number of past commit requests, it must proceed as normal, except it
+ should not change the current state of the zwp_text_input_v3 object.
+ </description>
+ <arg name="serial" type="uint"/>
+ </event>
+ </interface>
+
+ <interface name="zwp_text_input_manager_v3" version="1">
+ <description summary="text input manager">
+ A factory for text-input objects. This object is a global singleton.
+ </description>
+
+ <request name="destroy" type="destructor">
+ <description summary="Destroy the wp_text_input_manager">
+ Destroy the wp_text_input_manager object.
+ </description>
+ </request>
+
+ <request name="get_text_input">
+ <description summary="create a new text input object">
+ Creates a new text-input object for a given seat.
+ </description>
+ <arg name="id" type="new_id" interface="zwp_text_input_v3"/>
+ <arg name="seat" type="object" interface="wl_seat"/>
+ </request>
+ </interface>
+</protocol>
diff --git a/render/egl.c b/render/egl.c
index 96e48c06..644f94ac 100644
--- a/render/egl.c
+++ b/render/egl.c
@@ -3,6 +3,7 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <stdlib.h>
+#include <drm_fourcc.h>
#include <wlr/render/egl.h>
#include <wlr/util/log.h>
#include "glapi.h"
@@ -120,7 +121,7 @@ bool wlr_egl_init(struct wlr_egl *egl, EGLenum platform, void *remote_display,
if (platform == EGL_PLATFORM_SURFACELESS_MESA) {
assert(remote_display == NULL);
- egl->display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ egl->display = eglGetPlatformDisplayEXT(platform, EGL_DEFAULT_DISPLAY, NULL);
} else {
egl->display = eglGetPlatformDisplayEXT(platform, remote_display, NULL);
}
@@ -129,6 +130,8 @@ bool wlr_egl_init(struct wlr_egl *egl, EGLenum platform, void *remote_display,
goto error;
}
+ egl->platform = platform;
+
EGLint major, minor;
if (eglInitialize(egl->display, &major, &minor) == EGL_FALSE) {
wlr_log(WLR_ERROR, "Failed to initialize EGL");
@@ -318,6 +321,11 @@ bool wlr_egl_is_current(struct wlr_egl *egl) {
bool wlr_egl_swap_buffers(struct wlr_egl *egl, EGLSurface surface,
pixman_region32_t *damage) {
+ // Never block when swapping buffers on Wayland
+ if (egl->platform == EGL_PLATFORM_WAYLAND_EXT) {
+ eglSwapInterval(egl->display, 0);
+ }
+
EGLBoolean ret;
if (damage != NULL && (egl->exts.swap_buffers_with_damage_ext ||
egl->exts.swap_buffers_with_damage_khr)) {
@@ -382,13 +390,21 @@ EGLImageKHR wlr_egl_create_image_from_wl_drm(struct wlr_egl *egl,
EGLImageKHR wlr_egl_create_image_from_dmabuf(struct wlr_egl *egl,
struct wlr_dmabuf_attributes *attributes) {
- if (!egl->exts.image_base_khr) {
+ if (!egl->exts.image_base_khr || !egl->exts.image_dmabuf_import_ext) {
+ wlr_log(WLR_ERROR, "dmabuf import extension not present");
return NULL;
}
bool has_modifier = false;
- if (attributes->modifier != DRM_FORMAT_MOD_INVALID) {
+
+ // we assume the same way we assumed formats without the import_modifiers
+ // extension that mod_linear is supported. The special mod mod_invalid
+ // is sometimes used to signal modifier unawareness which is what we
+ // have here
+ if (attributes->modifier != DRM_FORMAT_MOD_INVALID &&
+ attributes->modifier != DRM_FORMAT_MOD_LINEAR) {
if (!egl->exts.image_dmabuf_import_modifiers_ext) {
+ wlr_log(WLR_ERROR, "dmabuf modifiers extension not present");
return NULL;
}
has_modifier = true;
@@ -460,12 +476,34 @@ EGLImageKHR wlr_egl_create_image_from_dmabuf(struct wlr_egl *egl,
int wlr_egl_get_dmabuf_formats(struct wlr_egl *egl,
int **formats) {
- if (!egl->exts.image_dmabuf_import_ext ||
- !egl->exts.image_dmabuf_import_modifiers_ext) {
- wlr_log(WLR_DEBUG, "dmabuf extension not present");
+ if (!egl->exts.image_dmabuf_import_ext) {
+ wlr_log(WLR_DEBUG, "dmabuf import extension not present");
return -1;
}
+ // when we only have the image_dmabuf_import extension we can't query
+ // which formats are supported. These two are on almost always
+ // supported; it's the intended way to just try to create buffers.
+ // Just a guess but better than not supporting dmabufs at all,
+ // given that the modifiers extension isn't supported everywhere.
+ if (!egl->exts.image_dmabuf_import_modifiers_ext) {
+ static const int fallback_formats[] = {
+ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_XRGB8888,
+ };
+ static unsigned num = sizeof(fallback_formats) /
+ sizeof(fallback_formats[0]);
+
+ *formats = calloc(num, sizeof(int));
+ if (!*formats) {
+ wlr_log_errno(WLR_ERROR, "Allocation failed");
+ return -1;
+ }
+
+ memcpy(*formats, fallback_formats, num * sizeof(**formats));
+ return num;
+ }
+
EGLint num;
if (!eglQueryDmaBufFormatsEXT(egl->display, 0, NULL, &num)) {
wlr_log(WLR_ERROR, "failed to query number of dmabuf formats");
@@ -488,12 +526,16 @@ int wlr_egl_get_dmabuf_formats(struct wlr_egl *egl,
int wlr_egl_get_dmabuf_modifiers(struct wlr_egl *egl,
int format, uint64_t **modifiers) {
- if (!egl->exts.image_dmabuf_import_ext ||
- !egl->exts.image_dmabuf_import_modifiers_ext) {
+ if (!egl->exts.image_dmabuf_import_ext) {
wlr_log(WLR_DEBUG, "dmabuf extension not present");
return -1;
}
+ if(!egl->exts.image_dmabuf_import_modifiers_ext) {
+ *modifiers = NULL;
+ return 0;
+ }
+
EGLint num;
if (!eglQueryDmaBufModifiersEXT(egl->display, format, 0,
NULL, NULL, &num)) {
diff --git a/render/gles2/pixel_format.c b/render/gles2/pixel_format.c
index 6f42e72e..a4b4c101 100644
--- a/render/gles2/pixel_format.c
+++ b/render/gles2/pixel_format.c
@@ -3,9 +3,9 @@
#include "render/gles2.h"
/*
-* The wayland formats are little endian while the GL formats are big endian,
-* so WL_SHM_FORMAT_ARGB8888 is actually compatible with GL_BGRA_EXT.
-*/
+ * The wayland formats are little endian while the GL formats are big endian,
+ * so WL_SHM_FORMAT_ARGB8888 is actually compatible with GL_BGRA_EXT.
+ */
static const struct wlr_gles2_pixel_format formats[] = {
{
.wl_format = WL_SHM_FORMAT_ARGB8888,
@@ -60,7 +60,19 @@ const struct wlr_gles2_pixel_format *get_gles2_format_from_wl(
return NULL;
}
-const enum wl_shm_format *get_gles2_formats(size_t *len) {
+const struct wlr_gles2_pixel_format *get_gles2_format_from_gl(
+ GLint gl_format, GLint gl_type, bool alpha) {
+ for (size_t i = 0; i < sizeof(formats) / sizeof(*formats); ++i) {
+ if (formats[i].gl_format == gl_format &&
+ formats[i].gl_type == gl_type &&
+ formats[i].has_alpha == alpha) {
+ return &formats[i];
+ }
+ }
+ return NULL;
+}
+
+const enum wl_shm_format *get_gles2_wl_formats(size_t *len) {
*len = sizeof(wl_formats) / sizeof(wl_formats[0]);
return wl_formats;
}
diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c
index b6e97041..a5521909 100644
--- a/render/gles2/renderer.c
+++ b/render/gles2/renderer.c
@@ -135,6 +135,12 @@ static bool gles2_render_texture_with_matrix(struct wlr_renderer *wlr_renderer,
case WLR_GLES2_TEXTURE_DMABUF:
shader = &renderer->shaders.tex_ext;
target = GL_TEXTURE_EXTERNAL_OES;
+
+ if (!renderer->exts.egl_image_external_oes) {
+ wlr_log(WLR_ERROR, "Failed to render texture: "
+ "GL_TEXTURE_EXTERNAL_OES not supported");
+ return false;
+ }
break;
}
@@ -207,13 +213,17 @@ static void gles2_render_ellipse_with_matrix(struct wlr_renderer *wlr_renderer,
static const enum wl_shm_format *gles2_renderer_formats(
struct wlr_renderer *wlr_renderer, size_t *len) {
- return get_gles2_formats(len);
+ return get_gles2_wl_formats(len);
+}
+
+static bool gles2_format_supported(struct wlr_renderer *wlr_renderer,
+ enum wl_shm_format wl_fmt) {
+ return get_gles2_format_from_wl(wl_fmt) != NULL;
}
static bool gles2_resource_is_wl_drm_buffer(struct wlr_renderer *wlr_renderer,
struct wl_resource *resource) {
- struct wlr_gles2_renderer *renderer =
- gles2_get_renderer(wlr_renderer);
+ struct wlr_gles2_renderer *renderer = gles2_get_renderer(wlr_renderer);
if (!eglQueryWaylandBufferWL) {
return false;
@@ -249,6 +259,33 @@ static int gles2_get_dmabuf_modifiers(struct wlr_renderer *wlr_renderer,
return wlr_egl_get_dmabuf_modifiers(renderer->egl, format, modifiers);
}
+static enum wl_shm_format gles2_preferred_read_format(
+ struct wlr_renderer *wlr_renderer) {
+ struct wlr_gles2_renderer *renderer =
+ gles2_get_renderer_in_context(wlr_renderer);
+
+ GLint gl_format = -1, gl_type = -1;
+ PUSH_GLES2_DEBUG;
+ glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT, &gl_format);
+ glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE, &gl_type);
+ POP_GLES2_DEBUG;
+
+ EGLint alpha_size = -1;
+ eglGetConfigAttrib(renderer->egl->display, renderer->egl->config,
+ EGL_ALPHA_SIZE, &alpha_size);
+
+ const struct wlr_gles2_pixel_format *fmt =
+ get_gles2_format_from_gl(gl_format, gl_type, alpha_size > 0);
+ if (fmt != NULL) {
+ return fmt->wl_format;
+ }
+
+ if (renderer->exts.read_format_bgra_ext) {
+ return WL_SHM_FORMAT_XRGB8888;
+ }
+ return WL_SHM_FORMAT_XBGR8888;
+}
+
static bool gles2_read_pixels(struct wlr_renderer *wlr_renderer,
enum wl_shm_format wl_fmt, uint32_t *flags, uint32_t stride,
uint32_t width, uint32_t height, uint32_t src_x, uint32_t src_y,
@@ -262,6 +299,12 @@ static bool gles2_read_pixels(struct wlr_renderer *wlr_renderer,
return false;
}
+ if (fmt->gl_format == GL_BGRA_EXT && !renderer->exts.read_format_bgra_ext) {
+ wlr_log(WLR_ERROR,
+ "Cannot read pixels: missing GL_EXT_read_format_bgra extension");
+ return false;
+ }
+
PUSH_GLES2_DEBUG;
// Make sure any pending drawing is finished before we try to read it
@@ -294,11 +337,6 @@ static bool gles2_read_pixels(struct wlr_renderer *wlr_renderer,
return glGetError() == GL_NO_ERROR;
}
-static bool gles2_format_supported(struct wlr_renderer *wlr_renderer,
- enum wl_shm_format wl_fmt) {
- return get_gles2_format_from_wl(wl_fmt) != NULL;
-}
-
static struct wlr_texture *gles2_texture_from_pixels(
struct wlr_renderer *wlr_renderer, enum wl_shm_format wl_fmt,
uint32_t stride, uint32_t width, uint32_t height, const void *data) {
@@ -342,7 +380,7 @@ static void gles2_destroy(struct wlr_renderer *wlr_renderer) {
glDeleteProgram(renderer->shaders.tex_ext.program);
POP_GLES2_DEBUG;
- if (glDebugMessageCallbackKHR) {
+ if (renderer->exts.debug_khr) {
glDisable(GL_DEBUG_OUTPUT_KHR);
glDebugMessageCallbackKHR(NULL, NULL);
}
@@ -360,12 +398,13 @@ static const struct wlr_renderer_impl renderer_impl = {
.render_quad_with_matrix = gles2_render_quad_with_matrix,
.render_ellipse_with_matrix = gles2_render_ellipse_with_matrix,
.formats = gles2_renderer_formats,
+ .format_supported = gles2_format_supported,
.resource_is_wl_drm_buffer = gles2_resource_is_wl_drm_buffer,
.wl_drm_buffer_get_size = gles2_wl_drm_buffer_get_size,
.get_dmabuf_formats = gles2_get_dmabuf_formats,
.get_dmabuf_modifiers = gles2_get_dmabuf_modifiers,
+ .preferred_read_format = gles2_preferred_read_format,
.read_pixels = gles2_read_pixels,
- .format_supported = gles2_format_supported,
.texture_from_pixels = gles2_texture_from_pixels,
.texture_from_wl_drm = gles2_texture_from_wl_drm,
.texture_from_dmabuf = gles2_texture_from_dmabuf,
@@ -466,6 +505,24 @@ error:
return 0;
}
+static bool check_gl_ext(const char *exts, const char *ext) {
+ size_t extlen = strlen(ext);
+ const char *end = exts + strlen(exts);
+
+ while (exts < end) {
+ if (exts[0] == ' ') {
+ exts++;
+ continue;
+ }
+ size_t n = strcspn(exts, " ");
+ if (n == extlen && strncmp(ext, exts, n) == 0) {
+ return true;
+ }
+ exts += n;
+ }
+ return false;
+}
+
extern const GLchar quad_vertex_src[];
extern const GLchar quad_fragment_src[];
extern const GLchar ellipse_fragment_src[];
@@ -487,14 +544,32 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) {
wlr_renderer_init(&renderer->wlr_renderer, &renderer_impl);
renderer->egl = egl;
- wlr_egl_make_current(renderer->egl, EGL_NO_SURFACE, NULL);
+ if (!wlr_egl_make_current(renderer->egl, EGL_NO_SURFACE, NULL)) {
+ free(renderer);
+ return NULL;
+ }
- renderer->exts_str = (const char*) glGetString(GL_EXTENSIONS);
+ renderer->exts_str = (const char *)glGetString(GL_EXTENSIONS);
wlr_log(WLR_INFO, "Using %s", glGetString(GL_VERSION));
wlr_log(WLR_INFO, "GL vendor: %s", glGetString(GL_VENDOR));
wlr_log(WLR_INFO, "Supported GLES2 extensions: %s", renderer->exts_str);
- if (glDebugMessageCallbackKHR && glDebugMessageControlKHR) {
+ if (!check_gl_ext(renderer->exts_str, "GL_EXT_texture_format_BGRA8888")) {
+ wlr_log(WLR_ERROR, "BGRA8888 format not supported by GLES2");
+ free(renderer);
+ return NULL;
+ }
+
+ renderer->exts.read_format_bgra_ext =
+ check_gl_ext(renderer->exts_str, "GL_EXT_read_format_bgra");
+ renderer->exts.debug_khr =
+ check_gl_ext(renderer->exts_str, "GL_KHR_debug") &&
+ glDebugMessageCallbackKHR && glDebugMessageControlKHR;
+ renderer->exts.egl_image_external_oes =
+ check_gl_ext(renderer->exts_str, "GL_OES_EGL_image_external") &&
+ glEGLImageTargetTexture2DOES;
+
+ if (renderer->exts.debug_khr) {
glEnable(GL_DEBUG_OUTPUT_KHR);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR);
glDebugMessageCallbackKHR(gles2_log, NULL);
@@ -545,7 +620,7 @@ struct wlr_renderer *wlr_gles2_renderer_create(struct wlr_egl *egl) {
renderer->shaders.tex_rgbx.tex = glGetUniformLocation(prog, "tex");
renderer->shaders.tex_rgbx.alpha = glGetUniformLocation(prog, "alpha");
- if (glEGLImageTargetTexture2DOES) {
+ if (renderer->exts.egl_image_external_oes) {
renderer->shaders.tex_ext.program = prog =
link_program(tex_vertex_src, tex_fragment_src_external);
if (!renderer->shaders.tex_ext.program) {
@@ -570,7 +645,7 @@ error:
POP_GLES2_DEBUG;
- if (glDebugMessageCallbackKHR) {
+ if (renderer->exts.debug_khr) {
glDisable(GL_DEBUG_OUTPUT_KHR);
glDebugMessageCallbackKHR(NULL, NULL);
}
diff --git a/render/gles2/texture.c b/render/gles2/texture.c
index 22d02cde..d035841e 100644
--- a/render/gles2/texture.c
+++ b/render/gles2/texture.c
@@ -44,9 +44,9 @@ static bool gles2_texture_is_opaque(struct wlr_texture *wlr_texture) {
}
static bool gles2_texture_write_pixels(struct wlr_texture *wlr_texture,
- enum wl_shm_format wl_fmt, uint32_t stride, uint32_t width,
- uint32_t height, uint32_t src_x, uint32_t src_y, uint32_t dst_x,
- uint32_t dst_y, const void *data) {
+ uint32_t stride, uint32_t width, uint32_t height,
+ uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y,
+ const void *data) {
struct wlr_gles2_texture *texture =
get_gles2_texture_in_context(wlr_texture);
@@ -55,11 +55,9 @@ static bool gles2_texture_write_pixels(struct wlr_texture *wlr_texture,
return false;
}
- const struct wlr_gles2_pixel_format *fmt = get_gles2_format_from_wl(wl_fmt);
- if (fmt == NULL) {
- wlr_log(WLR_ERROR, "Unsupported pixel format %"PRIu32, wl_fmt);
- return false;
- }
+ const struct wlr_gles2_pixel_format *fmt =
+ get_gles2_format_from_wl(texture->wl_format);
+ assert(fmt);
// TODO: what if the unpack subimage extension isn't supported?
PUSH_GLES2_DEBUG;
@@ -167,6 +165,7 @@ struct wlr_texture *wlr_gles2_texture_from_pixels(struct wlr_egl *egl,
texture->height = height;
texture->type = WLR_GLES2_TEXTURE_GLTEX;
texture->has_alpha = fmt->has_alpha;
+ texture->wl_format = fmt->wl_format;
PUSH_GLES2_DEBUG;
@@ -203,6 +202,7 @@ struct wlr_texture *wlr_gles2_texture_from_wl_drm(struct wlr_egl *egl,
texture->wl_drm = data;
EGLint fmt;
+ texture->wl_format = 0xFFFFFFFF; // texture can't be written anyways
texture->image = wlr_egl_create_image_from_wl_drm(egl, data, &fmt,
&texture->width, &texture->height, &texture->inverted_y);
if (texture->image == NULL) {
@@ -283,6 +283,7 @@ struct wlr_texture *wlr_gles2_texture_from_dmabuf(struct wlr_egl *egl,
texture->height = attribs->height;
texture->type = WLR_GLES2_TEXTURE_DMABUF;
texture->has_alpha = true;
+ texture->wl_format = 0xFFFFFFFF; // texture can't be written anyways
texture->inverted_y =
(attribs->flags & WLR_DMABUF_ATTRIBUTES_FLAGS_Y_INVERT) != 0;
diff --git a/render/meson.build b/render/meson.build
index ab66eab0..e45ea90b 100644
--- a/render/meson.build
+++ b/render/meson.build
@@ -22,7 +22,13 @@ lib_wlr_render = static_library(
),
glapi,
include_directories: wlr_inc,
- dependencies: [egl, glesv2, pixman, wayland_server],
+ dependencies: [
+ egl,
+ drm.partial_dependency(compile_args: true), # <drm_fourcc.h>
+ glesv2,
+ pixman,
+ wayland_server
+ ],
)
wlr_render = declare_dependency(
diff --git a/render/wlr_renderer.c b/render/wlr_renderer.c
index 31bf2b18..10dc6db2 100644
--- a/render/wlr_renderer.c
+++ b/render/wlr_renderer.c
@@ -139,6 +139,15 @@ int wlr_renderer_get_dmabuf_modifiers(struct wlr_renderer *r, int format,
return r->impl->get_dmabuf_modifiers(r, format, modifiers);
}
+bool wlr_renderer_preferred_read_format(struct wlr_renderer *r,
+ enum wl_shm_format *fmt) {
+ if (!r->impl->preferred_read_format || !r->impl->read_pixels) {
+ return false;
+ }
+ *fmt = r->impl->preferred_read_format(r);
+ return true;
+}
+
bool wlr_renderer_read_pixels(struct wlr_renderer *r, enum wl_shm_format fmt,
uint32_t *flags, uint32_t stride, uint32_t width, uint32_t height,
uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y,
diff --git a/render/wlr_texture.c b/render/wlr_texture.c
index 06872f1e..833032c9 100644
--- a/render/wlr_texture.c
+++ b/render/wlr_texture.c
@@ -55,10 +55,10 @@ bool wlr_texture_is_opaque(struct wlr_texture *texture) {
}
bool wlr_texture_write_pixels(struct wlr_texture *texture,
- enum wl_shm_format wl_fmt, uint32_t stride, uint32_t width,
- uint32_t height, uint32_t src_x, uint32_t src_y, uint32_t dst_x,
- uint32_t dst_y, const void *data) {
- return texture->impl->write_pixels(texture, wl_fmt, stride, width, height,
+ uint32_t stride, uint32_t width, uint32_t height,
+ uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y,
+ const void *data) {
+ return texture->impl->write_pixels(texture, stride, width, height,
src_x, src_y, dst_x, dst_y, data);
}
diff --git a/rootston/config.c b/rootston/config.c
index 92d90de1..119a9e2c 100644
--- a/rootston/config.c
+++ b/rootston/config.c
@@ -506,6 +506,7 @@ struct roots_config *roots_config_create_from_args(int argc, char *argv[]) {
add_binding_config(&config->bindings, "Logo+Shift+E", "exit");
add_binding_config(&config->bindings, "Ctrl+q", "close");
add_binding_config(&config->bindings, "Alt+Tab", "next_window");
+ add_binding_config(&config->bindings, "Logo+Escape", "break_pointer_constraint");
struct roots_keyboard_config *kc =
calloc(1, sizeof(struct roots_keyboard_config));
kc->meta_key = WLR_MODIFIER_LOGO;
@@ -576,7 +577,7 @@ void roots_config_destroy(struct roots_config *config) {
struct roots_output_config *roots_config_get_output(struct roots_config *config,
struct wlr_output *output) {
- char name[83];
+ char name[88];
snprintf(name, sizeof(name), "%s %s %s", output->make, output->model,
output->serial);
diff --git a/rootston/cursor.c b/rootston/cursor.c
index 2b8d9a3e..9a163c63 100644
--- a/rootston/cursor.c
+++ b/rootston/cursor.c
@@ -1,9 +1,12 @@
#define _XOPEN_SOURCE 700
+#include <assert.h>
#include <math.h>
#include <stdlib.h>
+#include <wlr/types/wlr_region.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/util/edges.h>
#include <wlr/util/log.h>
+#include <wlr/util/region.h>
#ifdef __linux__
#include <linux/input-event-codes.h>
#elif __FreeBSD__
@@ -11,6 +14,7 @@
#endif
#include "rootston/cursor.h"
#include "rootston/desktop.h"
+#include "rootston/view.h"
#include "rootston/xcursor.h"
struct roots_cursor *roots_cursor_create(struct roots_seat *seat) {
@@ -100,7 +104,7 @@ static void seat_view_deco_button(struct roots_seat_view *view, double sx,
}
static void roots_passthrough_cursor(struct roots_cursor *cursor,
- uint32_t time) {
+ int64_t time) {
bool focus_changed;
double sx, sy;
struct roots_view *view = NULL;
@@ -108,11 +112,13 @@ static void roots_passthrough_cursor(struct roots_cursor *cursor,
struct roots_desktop *desktop = seat->input->server->desktop;
struct wlr_surface *surface = desktop_surface_at(desktop,
cursor->cursor->x, cursor->cursor->y, &sx, &sy, &view);
+
struct wl_client *client = NULL;
if (surface) {
client = wl_resource_get_client(surface->resource);
}
- if (surface && !roots_seat_allow_input(cursor->seat, surface->resource)) {
+
+ if (surface && !roots_seat_allow_input(seat, surface->resource)) {
return;
}
@@ -125,21 +131,27 @@ static void roots_passthrough_cursor(struct roots_cursor *cursor,
if (view) {
struct roots_seat_view *seat_view =
roots_seat_view_from_view(seat, view);
- if (cursor->pointer_view && (surface ||
- seat_view != cursor->pointer_view)) {
+
+ if (cursor->pointer_view &&
+ !cursor->wlr_surface && (surface || seat_view != cursor->pointer_view)) {
seat_view_deco_leave(cursor->pointer_view);
- cursor->pointer_view = NULL;
}
+
+ cursor->pointer_view = seat_view;
+
if (!surface) {
- cursor->pointer_view = seat_view;
seat_view_deco_motion(seat_view, sx, sy);
}
+ } else {
+ cursor->pointer_view = NULL;
}
+ cursor->wlr_surface = surface;
+
if (surface) {
focus_changed = (seat->seat->pointer_state.focused_surface != surface);
wlr_seat_pointer_notify_enter(seat->seat, surface, sx, sy);
- if (!focus_changed) {
+ if (!focus_changed && time > 0) {
wlr_seat_pointer_notify_motion(seat->seat, time, sx, sy);
}
} else {
@@ -152,6 +164,10 @@ static void roots_passthrough_cursor(struct roots_cursor *cursor,
}
}
+void roots_cursor_update_focus(struct roots_cursor *cursor) {
+ roots_passthrough_cursor(cursor, -1);
+}
+
void roots_cursor_update_position(struct roots_cursor *cursor,
uint32_t time) {
struct roots_seat *seat = cursor->seat;
@@ -262,7 +278,7 @@ static void roots_cursor_press_button(struct roots_cursor *cursor,
} else {
if (view && !surface && cursor->pointer_view) {
seat_view_deco_button(cursor->pointer_view,
- sx, sy, button, state);
+ sx, sy, button, state);
}
if (state == WLR_BUTTON_RELEASED &&
@@ -291,15 +307,59 @@ static void roots_cursor_press_button(struct roots_cursor *cursor,
void roots_cursor_handle_motion(struct roots_cursor *cursor,
struct wlr_event_pointer_motion *event) {
- wlr_cursor_move(cursor->cursor, event->device,
- event->delta_x, event->delta_y);
+ double dx = event->delta_x;
+ double dy = event->delta_y;
+
+ if (cursor->active_constraint) {
+ struct roots_view *view = cursor->pointer_view->view;
+ assert(view);
+
+ // TODO: handle rotated views
+ if (view->rotation == 0.0) {
+ double lx1 = cursor->cursor->x;
+ double ly1 = cursor->cursor->y;
+
+ double lx2 = lx1 + dx;
+ double ly2 = ly1 + dy;
+
+ double sx1 = lx1 - view->x;
+ double sy1 = ly1 - view->y;
+
+ double sx2 = lx2 - view->x;
+ double sy2 = ly2 - view->y;
+
+ double sx2_confined, sy2_confined;
+ if (!wlr_region_confine(&cursor->confine, sx1, sy1, sx2, sy2,
+ &sx2_confined, &sy2_confined)) {
+ return;
+ }
+
+ dx = sx2_confined - sx1;
+ dy = sy2_confined - sy1;
+ }
+ }
+
+ wlr_cursor_move(cursor->cursor, event->device, dx, dy);
roots_cursor_update_position(cursor, event->time_msec);
}
void roots_cursor_handle_motion_absolute(struct roots_cursor *cursor,
struct wlr_event_pointer_motion_absolute *event) {
- wlr_cursor_warp_absolute(cursor->cursor,
- event->device, event->x, event->y);
+ double lx, ly;
+ wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, event->x,
+ event->y, &lx, &ly);
+
+ if (cursor->pointer_view) {
+ struct roots_view *view = cursor->pointer_view->view;
+
+ if (cursor->active_constraint &&
+ !pixman_region32_contains_point(&cursor->confine,
+ floor(lx - view->x), floor(ly - view->y), NULL)) {
+ return;
+ }
+ }
+
+ wlr_cursor_warp_closest(cursor->cursor, event->device, lx, ly);
roots_cursor_update_position(cursor, event->time_msec);
}
@@ -324,7 +384,7 @@ void roots_cursor_handle_touch_down(struct roots_cursor *cursor,
double sx, sy;
struct wlr_surface *surface = desktop_surface_at(
- desktop, lx, ly, &sx, &sy, NULL);
+ desktop, lx, ly, &sx, &sy, NULL);
uint32_t serial = 0;
if (surface && roots_seat_allow_input(cursor->seat, surface->resource)) {
@@ -393,18 +453,34 @@ void roots_cursor_handle_touch_motion(struct roots_cursor *cursor,
void roots_cursor_handle_tool_axis(struct roots_cursor *cursor,
struct wlr_event_tablet_tool_axis *event) {
+ double x = NAN, y = NAN;
if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X) &&
(event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) {
- wlr_cursor_warp_absolute(cursor->cursor, event->device,
- event->x, event->y);
- roots_cursor_update_position(cursor, event->time_msec);
+ x = event->x;
+ y = event->y;
} else if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X)) {
- wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, -1);
- roots_cursor_update_position(cursor, event->time_msec);
+ x = event->x;
} else if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) {
- wlr_cursor_warp_absolute(cursor->cursor, event->device, -1, event->y);
- roots_cursor_update_position(cursor, event->time_msec);
+ y = event->y;
+ }
+
+ double lx, ly;
+ wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device,
+ x, y, &lx, &ly);
+
+
+ if (cursor->pointer_view) {
+ struct roots_view *view = cursor->pointer_view->view;
+
+ if (cursor->active_constraint &&
+ !pixman_region32_contains_point(&cursor->confine,
+ floor(lx - view->x), floor(ly - view->y), NULL)) {
+ return;
+ }
}
+
+ wlr_cursor_warp_closest(cursor->cursor, event->device, lx, ly);
+ roots_cursor_update_position(cursor, event->time_msec);
}
void roots_cursor_handle_tool_tip(struct roots_cursor *cursor,
@@ -434,3 +510,106 @@ void roots_cursor_handle_request_set_cursor(struct roots_cursor *cursor,
event->hotspot_y);
cursor->cursor_client = event->seat_client->client;
}
+
+void roots_cursor_handle_focus_change(struct roots_cursor *cursor,
+ struct wlr_seat_pointer_focus_change_event *event) {
+ double sx = event->sx;
+ double sy = event->sy;
+
+ double lx = cursor->cursor->x;
+ double ly = cursor->cursor->y;
+
+ wlr_log(WLR_DEBUG, "entered surface %p, lx: %f, ly: %f, sx: %f, sy: %f",
+ event->new_surface, lx, ly, sx, sy);
+
+ roots_cursor_constrain(cursor,
+ wlr_pointer_constraints_v1_constraint_for_surface(
+ cursor->seat->input->server->desktop->pointer_constraints,
+ event->new_surface, cursor->seat->seat),
+ sx, sy);
+}
+
+void roots_cursor_handle_constraint_commit(struct roots_cursor *cursor) {
+ struct roots_desktop *desktop = cursor->seat->input->server->desktop;
+
+ struct roots_view *view;
+ double sx, sy;
+ struct wlr_surface *surface = desktop_surface_at(desktop,
+ cursor->cursor->x, cursor->cursor->y, &sx, &sy, &view);
+ // This should never happen but views move around right when they're
+ // created from (0, 0) to their actual coordinates.
+ if (surface != cursor->active_constraint->surface) {
+ roots_cursor_update_focus(cursor);
+ } else {
+ roots_cursor_constrain(cursor, cursor->active_constraint, sx, sy);
+ }
+}
+
+static void handle_constraint_commit(struct wl_listener *listener,
+ void *data) {
+ struct roots_cursor *cursor =
+ wl_container_of(listener, cursor, constraint_commit);
+ assert(cursor->active_constraint->surface == data);
+ roots_cursor_handle_constraint_commit(cursor);
+}
+
+void roots_cursor_constrain(struct roots_cursor *cursor,
+ struct wlr_pointer_constraint_v1 *constraint, double sx, double sy) {
+ if (cursor->active_constraint == constraint) {
+ return;
+ }
+
+ wlr_log(WLR_DEBUG, "roots_cursor_constrain(%p, %p)",
+ cursor, constraint);
+ wlr_log(WLR_DEBUG, "cursor->active_constraint: %p",
+ cursor->active_constraint);
+
+ wl_list_remove(&cursor->constraint_commit.link);
+ wl_list_init(&cursor->constraint_commit.link);
+ if (cursor->active_constraint) {
+ wlr_pointer_constraint_v1_send_deactivated(
+ cursor->active_constraint);
+ }
+
+ cursor->active_constraint = constraint;
+
+ if (constraint == NULL) {
+ return;
+ }
+
+ wlr_pointer_constraint_v1_send_activated(constraint);
+
+ wl_list_remove(&cursor->constraint_commit.link);
+ wl_signal_add(&constraint->surface->events.commit,
+ &cursor->constraint_commit);
+ cursor->constraint_commit.notify = handle_constraint_commit;
+
+ pixman_region32_clear(&cursor->confine);
+
+ pixman_region32_t *region = &constraint->region;
+
+ if (!pixman_region32_contains_point(region, floor(sx), floor(sy), NULL)) {
+ // Warp into region if possible
+ int nboxes;
+ pixman_box32_t *boxes = pixman_region32_rectangles(region, &nboxes);
+ if (nboxes > 0) {
+ struct roots_view *view = cursor->pointer_view->view;
+
+ double sx = (boxes[0].x1 + boxes[0].x2) / 2.;
+ double sy = (boxes[0].y1 + boxes[0].y2) / 2.;
+
+ rotate_child_position(&sx, &sy, 0, 0, view->width, view->height,
+ view->rotation);
+
+ double lx = view->x + sx;
+ double ly = view->y + sy;
+
+ wlr_cursor_warp_closest(cursor->cursor, NULL, lx, ly);
+ }
+ }
+
+ // A locked pointer will result in an empty region, thus disallowing all movement
+ if (constraint->type == WLR_POINTER_CONSTRAINT_V1_CONFINED) {
+ pixman_region32_copy(&cursor->confine, region);
+ }
+}
diff --git a/rootston/desktop.c b/rootston/desktop.c
index 3f6d977e..7da64ef8 100644
--- a/rootston/desktop.c
+++ b/rootston/desktop.c
@@ -1,4 +1,4 @@
-#define _POSIX_C_SOURCE 199309L
+#define _POSIX_C_SOURCE 200112L
#include <assert.h>
#include <math.h>
#include <stdlib.h>
@@ -15,6 +15,7 @@
#include <wlr/types/wlr_input_inhibitor.h>
#include <wlr/types/wlr_layer_shell_v1.h>
#include <wlr/types/wlr_output_layout.h>
+#include <wlr/types/wlr_pointer_constraints_v1.h>
#include <wlr/types/wlr_primary_selection.h>
#include <wlr/types/wlr_server_decoration.h>
#include <wlr/types/wlr_wl_shell.h>
@@ -438,6 +439,11 @@ void view_destroy(struct roots_view *view) {
view_unmap(view);
}
+ // Can happen if fullscreened while unmapped, and hasn't been mapped
+ if (view->fullscreen_output != NULL) {
+ view->fullscreen_output->fullscreen_view = NULL;
+ }
+
if (view->destroy) {
view->destroy(view);
}
@@ -575,6 +581,9 @@ static bool view_at(struct roots_view *view, double lx, double ly,
view->wl_shell_surface->state == WLR_WL_SHELL_SURFACE_STATE_POPUP) {
return false;
}
+ if (view->wlr_surface == NULL) {
+ return false;
+ }
double view_sx = lx - view->x;
double view_sy = ly - view->y;
@@ -776,6 +785,62 @@ static void input_inhibit_deactivate(struct wl_listener *listener, void *data) {
}
}
+static void handle_constraint_destroy(struct wl_listener *listener,
+ void *data) {
+ struct roots_pointer_constraint *constraint =
+ wl_container_of(listener, constraint, destroy);
+ struct wlr_pointer_constraint_v1 *wlr_constraint = data;
+ struct roots_seat *seat = wlr_constraint->seat->data;
+
+ wl_list_remove(&constraint->destroy.link);
+
+ if (seat->cursor->active_constraint == wlr_constraint) {
+ wl_list_remove(&seat->cursor->constraint_commit.link);
+ wl_list_init(&seat->cursor->constraint_commit.link);
+ seat->cursor->active_constraint = NULL;
+
+ if (wlr_constraint->current.committed &
+ WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT &&
+ seat->cursor->pointer_view) {
+ double sx = wlr_constraint->current.cursor_hint.x;
+ double sy = wlr_constraint->current.cursor_hint.y;
+
+ struct roots_view *view = seat->cursor->pointer_view->view;
+ rotate_child_position(&sx, &sy, 0, 0, view->width, view->height,
+ view->rotation);
+ double lx = view->x + sx;
+ double ly = view->y + sy;
+
+ wlr_cursor_warp(seat->cursor->cursor, NULL, lx, ly);
+ }
+ }
+
+ free(constraint);
+}
+
+static void handle_pointer_constraint(struct wl_listener *listener,
+ void *data) {
+ struct wlr_pointer_constraint_v1 *wlr_constraint = data;
+ struct roots_seat *seat = wlr_constraint->seat->data;
+
+ struct roots_pointer_constraint *constraint =
+ calloc(1, sizeof(struct roots_pointer_constraint));
+ constraint->constraint = wlr_constraint;
+
+ constraint->destroy.notify = handle_constraint_destroy;
+ wl_signal_add(&wlr_constraint->events.destroy, &constraint->destroy);
+
+ double sx, sy;
+ struct wlr_surface *surface = desktop_surface_at(
+ seat->input->server->desktop,
+ seat->cursor->cursor->x, seat->cursor->cursor->y, &sx, &sy, NULL);
+
+ if (surface == wlr_constraint->surface) {
+ assert(!seat->cursor->active_constraint);
+ roots_cursor_constrain(seat->cursor, wlr_constraint, sx, sy);
+ }
+}
+
struct roots_desktop *desktop_create(struct roots_server *server,
struct roots_config *config) {
wlr_log(WLR_DEBUG, "Initializing roots desktop");
@@ -824,18 +889,30 @@ struct roots_desktop *desktop_create(struct roots_server *server,
desktop->tablet_v2 = wlr_tablet_v2_create(server->wl_display);
-#ifdef WLR_HAS_XWAYLAND
const char *cursor_theme = NULL;
+#ifdef WLR_HAS_XWAYLAND
const char *cursor_default = ROOTS_XCURSOR_DEFAULT;
+#endif
struct roots_cursor_config *cc =
roots_config_get_cursor(config, ROOTS_CONFIG_DEFAULT_SEAT_NAME);
if (cc != NULL) {
cursor_theme = cc->theme;
+#ifdef WLR_HAS_XWAYLAND
if (cc->default_image != NULL) {
cursor_default = cc->default_image;
}
+#endif
+ }
+
+ char cursor_size_fmt[16];
+ snprintf(cursor_size_fmt, sizeof(cursor_size_fmt),
+ "%d", ROOTS_XCURSOR_SIZE);
+ setenv("XCURSOR_SIZE", cursor_size_fmt, 1);
+ if (cursor_theme != NULL) {
+ setenv("XCURSOR_THEME", cursor_theme, 1);
}
+#ifdef WLR_HAS_XWAYLAND
desktop->xcursor_manager = wlr_xcursor_manager_create(cursor_theme,
ROOTS_XCURSOR_SIZE);
if (desktop->xcursor_manager == NULL) {
@@ -887,10 +964,15 @@ struct roots_desktop *desktop_create(struct roots_server *server,
wlr_input_inhibit_manager_create(server->wl_display);
desktop->input_inhibit_activate.notify = input_inhibit_activate;
wl_signal_add(&desktop->input_inhibit->events.activate,
- &desktop->input_inhibit_activate);
+ &desktop->input_inhibit_activate);
desktop->input_inhibit_deactivate.notify = input_inhibit_deactivate;
wl_signal_add(&desktop->input_inhibit->events.deactivate,
- &desktop->input_inhibit_deactivate);
+ &desktop->input_inhibit_deactivate);
+
+ desktop->input_method =
+ wlr_input_method_manager_v2_create(server->wl_display);
+
+ desktop->text_input = wlr_text_input_manager_v3_create(server->wl_display);
desktop->virtual_keyboard = wlr_virtual_keyboard_manager_v1_create(
server->wl_display);
@@ -906,6 +988,15 @@ struct roots_desktop *desktop_create(struct roots_server *server,
&desktop->xdg_toplevel_decoration);
desktop->xdg_toplevel_decoration.notify = handle_xdg_toplevel_decoration;
+ desktop->pointer_constraints =
+ wlr_pointer_constraints_v1_create(server->wl_display);
+ desktop->pointer_constraint.notify = handle_pointer_constraint;
+ wl_signal_add(&desktop->pointer_constraints->events.new_constraint,
+ &desktop->pointer_constraint);
+
+ desktop->presentation =
+ wlr_presentation_create(server->wl_display, server->backend);
+
return desktop;
}
diff --git a/rootston/keyboard.c b/rootston/keyboard.c
index b5a8093b..66c373cf 100644
--- a/rootston/keyboard.c
+++ b/rootston/keyboard.c
@@ -5,9 +5,9 @@
#include <sys/wait.h>
#include <unistd.h>
#include <wayland-server.h>
-#include <wlr/backend/multi.h>
#include <wlr/backend/session.h>
#include <wlr/types/wlr_input_device.h>
+#include <wlr/types/wlr_pointer_constraints_v1.h>
#include <wlr/types/wlr_pointer.h>
#include <wlr/util/log.h>
#include <xkbcommon/xkbcommon.h>
@@ -176,6 +176,13 @@ static void keyboard_binding_execute(struct roots_keyboard *keyboard,
decoration->wlr_decoration, mode);
}
}
+ } else if (strcmp(command, "break_pointer_constraint") == 0) {
+ struct wl_list *list =
+ &keyboard->input->seats;
+ struct roots_seat *seat;
+ wl_list_for_each(seat, list, link) {
+ roots_cursor_constrain(seat->cursor, NULL, NAN, NAN);
+ }
} else {
wlr_log(WLR_ERROR, "unknown binding command: %s", command);
}
@@ -193,14 +200,13 @@ static bool keyboard_execute_compositor_binding(struct roots_keyboard *keyboard,
if (keysym >= XKB_KEY_XF86Switch_VT_1 &&
keysym <= XKB_KEY_XF86Switch_VT_12) {
struct roots_server *server = keyboard->input->server;
- if (wlr_backend_is_multi(server->backend)) {
- struct wlr_session *session =
- wlr_multi_get_session(server->backend);
- if (session) {
- unsigned vt = keysym - XKB_KEY_XF86Switch_VT_1 + 1;
- wlr_session_change_vt(session, vt);
- }
+
+ struct wlr_session *session = wlr_backend_get_session(server->backend);
+ if (session) {
+ unsigned vt = keysym - XKB_KEY_XF86Switch_VT_1 + 1;
+ wlr_session_change_vt(session, vt);
}
+
return true;
}
diff --git a/rootston/meson.build b/rootston/meson.build
index 9d1decce..d650dc51 100644
--- a/rootston/meson.build
+++ b/rootston/meson.build
@@ -9,6 +9,7 @@ sources = [
'main.c',
'output.c',
'seat.c',
+ 'text_input.c',
'virtual_keyboard.c',
'wl_shell.c',
'xdg_shell.c',
diff --git a/rootston/output.c b/rootston/output.c
index 674cda2d..9aa7de65 100644
--- a/rootston/output.c
+++ b/rootston/output.c
@@ -5,9 +5,10 @@
#include <time.h>
#include <wlr/backend/drm.h>
#include <wlr/config.h>
-#include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_compositor.h>
+#include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_output_layout.h>
+#include <wlr/types/wlr_presentation_time.h>
#include <wlr/types/wlr_wl_shell.h>
#include <wlr/types/wlr_xdg_shell_v6.h>
#include <wlr/types/wlr_xdg_shell.h>
@@ -22,7 +23,7 @@
* Rotate a child's position relative to a parent. The parent size is (pw, ph),
* the child position is (*sx, *sy) and its size is (sw, sh).
*/
-static void rotate_child_position(double *sx, double *sy, double sw, double sh,
+void rotate_child_position(double *sx, double *sy, double sw, double sh,
double pw, double ph, float rotation) {
if (rotation != 0.0) {
// Coordinates relative to the center of the subsurface
@@ -127,6 +128,65 @@ static void drag_icons_for_each_surface(struct roots_input *input,
}
}
+static void layer_for_each_surface(struct wl_list *layer,
+ const struct wlr_box *output_layout_box,
+ wlr_surface_iterator_func_t iterator, struct layout_data *layout_data,
+ void *user_data) {
+ struct roots_layer_surface *roots_surface;
+ wl_list_for_each(roots_surface, layer, link) {
+ struct wlr_layer_surface_v1 *layer = roots_surface->layer_surface;
+
+ layout_data->x = roots_surface->geo.x + output_layout_box->x;
+ layout_data->y = roots_surface->geo.y + output_layout_box->y;
+ layout_data->width = roots_surface->geo.width;
+ layout_data->height = roots_surface->geo.height;
+ layout_data->rotation = 0;
+ wlr_layer_surface_v1_for_each_surface(layer, iterator, user_data);
+ }
+}
+
+static void output_for_each_surface(struct roots_output *output,
+ wlr_surface_iterator_func_t iterator, struct layout_data *layout_data,
+ void *user_data) {
+ struct wlr_output *wlr_output = output->wlr_output;
+ struct roots_desktop *desktop = output->desktop;
+ struct roots_server *server = desktop->server;
+
+ const struct wlr_box *output_box =
+ wlr_output_layout_get_box(desktop->layout, wlr_output);
+
+ if (output->fullscreen_view != NULL) {
+ struct roots_view *view = output->fullscreen_view;
+ if (wlr_output->fullscreen_surface == view->wlr_surface) {
+ // The surface is managed by the wlr_output
+ return;
+ }
+
+ view_for_each_surface(view, layout_data, iterator, user_data);
+
+#ifdef WLR_HAS_XWAYLAND
+ if (view->type == ROOTS_XWAYLAND_VIEW) {
+ xwayland_children_for_each_surface(view->xwayland_surface,
+ iterator, layout_data, user_data);
+ }
+#endif
+ } else {
+ struct roots_view *view;
+ wl_list_for_each_reverse(view, &desktop->views, link) {
+ view_for_each_surface(view, layout_data, iterator, user_data);
+ }
+
+ drag_icons_for_each_surface(server->input, iterator,
+ layout_data, user_data);
+ }
+
+ size_t len = sizeof(output->layers) / sizeof(output->layers[0]);
+ for (size_t i = 0; i < len; ++i) {
+ layer_for_each_surface(&output->layers[i], output_box,
+ iterator, layout_data, user_data);
+ }
+}
+
struct render_data {
struct layout_data layout;
@@ -320,6 +380,14 @@ static void render_view(struct roots_view *view, struct render_data *data) {
view_for_each_surface(view, &data->layout, render_surface, data);
}
+static void render_layer(struct roots_output *output,
+ const struct wlr_box *output_layout_box, struct render_data *data,
+ struct wl_list *layer) {
+ data->alpha = 1;
+ layer_for_each_surface(layer, output_layout_box, render_surface,
+ &data->layout, data);
+}
+
static bool has_standalone_surface(struct roots_view *view) {
if (!wl_list_empty(&view->wlr_surface->subsurfaces)) {
return false;
@@ -358,38 +426,6 @@ static void surface_send_frame_done(struct wlr_surface *surface, int sx, int sy,
wlr_surface_send_frame_done(surface, when);
}
-static void render_layer(struct roots_output *output,
- const struct wlr_box *output_layout_box, struct render_data *data,
- struct wl_list *layer) {
- struct roots_layer_surface *roots_surface;
- wl_list_for_each(roots_surface, layer, link) {
- struct wlr_layer_surface_v1 *layer = roots_surface->layer_surface;
-
- surface_for_each_surface(layer->surface,
- roots_surface->geo.x + output_layout_box->x,
- roots_surface->geo.y + output_layout_box->y,
- 0, &data->layout, render_surface, data);
-
- wlr_layer_surface_v1_for_each_surface(layer, render_surface, data);
- }
-}
-
-static void layers_send_done(
- struct roots_output *output, struct timespec *when) {
- size_t len = sizeof(output->layers) / sizeof(output->layers[0]);
- for (size_t i = 0; i < len; ++i) {
- struct roots_layer_surface *roots_surface;
- wl_list_for_each(roots_surface, &output->layers[i], link) {
- struct wlr_layer_surface_v1 *layer = roots_surface->layer_surface;
- wlr_surface_send_frame_done(layer->surface, when);
- struct wlr_xdg_popup *popup;
- wl_list_for_each(popup, &roots_surface->layer_surface->popups, link) {
- wlr_surface_send_frame_done(popup->base->surface, when);
- }
- }
- }
-}
-
static void render_output(struct roots_output *output) {
struct wlr_output *wlr_output = output->wlr_output;
struct roots_desktop *desktop = output->desktop;
@@ -423,7 +459,8 @@ static void render_output(struct roots_output *output) {
output_box->y;
view_move(view, view_x, view_y);
- if (has_standalone_surface(view)) {
+ if (has_standalone_surface(view) &&
+ wl_list_empty(&output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY])) {
wlr_output_set_fullscreen_surface(wlr_output, view->wlr_surface);
} else {
wlr_output_set_fullscreen_surface(wlr_output, NULL);
@@ -537,33 +574,8 @@ damage_finish:
pixman_region32_fini(&damage);
// Send frame done events to all surfaces
- if (output->fullscreen_view != NULL) {
- struct roots_view *view = output->fullscreen_view;
- if (wlr_output->fullscreen_surface == view->wlr_surface) {
- // The surface is managed by the wlr_output
- return;
- }
-
- view_for_each_surface(view, &data.layout, surface_send_frame_done,
- &data);
-
-#ifdef WLR_HAS_XWAYLAND
- if (view->type == ROOTS_XWAYLAND_VIEW) {
- xwayland_children_for_each_surface(view->xwayland_surface,
- surface_send_frame_done, &data.layout, &data);
- }
-#endif
- } else {
- struct roots_view *view;
- wl_list_for_each_reverse(view, &desktop->views, link) {
- view_for_each_surface(view, &data.layout, surface_send_frame_done,
- &data);
- }
-
- drag_icons_for_each_surface(server->input, surface_send_frame_done,
- &data.layout, &data);
- }
- layers_send_done(output, data.when);
+ output_for_each_surface(output, surface_send_frame_done,
+ &data.layout, &data);
}
void output_damage_whole(struct roots_output *output) {
@@ -769,6 +781,7 @@ static void output_destroy(struct roots_output *output) {
wl_list_remove(&output->destroy.link);
wl_list_remove(&output->mode.link);
wl_list_remove(&output->transform.link);
+ wl_list_remove(&output->present.link);
wl_list_remove(&output->damage_frame.link);
wl_list_remove(&output->damage_destroy.link);
free(output);
@@ -805,6 +818,52 @@ static void output_handle_transform(struct wl_listener *listener, void *data) {
arrange_layers(output);
}
+struct presentation_data {
+ struct layout_data layout;
+ struct roots_output *output;
+ struct wlr_presentation_event *event;
+};
+
+static void surface_send_presented(struct wlr_surface *surface, int sx, int sy,
+ void *_data) {
+ struct presentation_data *data = _data;
+ struct roots_output *output = data->output;
+ float rotation = data->layout.rotation;
+
+ double lx, ly;
+ get_layout_position(&data->layout, &lx, &ly, surface, sx, sy);
+
+ if (!surface_intersect_output(surface, output->desktop->layout,
+ output->wlr_output, lx, ly, rotation, NULL)) {
+ return;
+ }
+
+ wlr_presentation_send_surface_presented(output->desktop->presentation,
+ surface, data->event);
+}
+
+static void output_handle_present(struct wl_listener *listener, void *data) {
+ struct roots_output *output =
+ wl_container_of(listener, output, present);
+ struct wlr_output_event_present *output_event = data;
+
+ struct wlr_presentation_event event = {
+ .output = output->wlr_output,
+ .tv_sec = (uint64_t)output_event->when->tv_sec,
+ .tv_nsec = (uint32_t)output_event->when->tv_nsec,
+ .refresh = (uint32_t)output_event->refresh,
+ .seq = (uint64_t)output_event->seq,
+ .flags = output_event->flags,
+ };
+
+ struct presentation_data presentation_data = {
+ .output = output,
+ .event = &event,
+ };
+ output_for_each_surface(output, surface_send_presented,
+ &presentation_data.layout, &presentation_data);
+}
+
void handle_new_output(struct wl_listener *listener, void *data) {
struct roots_desktop *desktop = wl_container_of(listener, desktop,
new_output);
@@ -832,6 +891,8 @@ void handle_new_output(struct wl_listener *listener, void *data) {
wl_signal_add(&wlr_output->events.mode, &output->mode);
output->transform.notify = output_handle_transform;
wl_signal_add(&wlr_output->events.transform, &output->transform);
+ output->present.notify = output_handle_present;
+ wl_signal_add(&wlr_output->events.present, &output->present);
output->damage_frame.notify = output_damage_handle_frame;
wl_signal_add(&output->damage->events.frame, &output->damage_frame);
diff --git a/rootston/rootston.ini.example b/rootston/rootston.ini.example
index bb0efa44..4b75e9c6 100644
--- a/rootston/rootston.ini.example
+++ b/rootston/rootston.ini.example
@@ -53,9 +53,11 @@ meta-key = Logo
# - "close" to close the current view
# - "next_window" to cycle through windows
# - "alpha" to cycle a window's alpha channel
+# - "break_pointer_constraint" to decline and deactivate all pointer constraints
[bindings]
Logo+Shift+e = exit
Logo+q = close
Logo+m = maximize
+Logo+Escape = break_pointer_constraint
Alt+Tab = next_window
Ctrl+Shift+a = alpha
diff --git a/rootston/seat.c b/rootston/seat.c
index 507254d4..82444dcb 100644
--- a/rootston/seat.c
+++ b/rootston/seat.c
@@ -16,6 +16,7 @@
#include "rootston/input.h"
#include "rootston/keyboard.h"
#include "rootston/seat.h"
+#include "rootston/text_input.h"
#include "rootston/xcursor.h"
@@ -178,9 +179,18 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) {
roots_tool->tablet_v2_tool, event->distance);
}
+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_X) {
+ roots_tool->tilt_x = event->tilt_x;
+ }
+
+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_Y) {
+ roots_tool->tilt_y = event->tilt_y;
+ }
+
if (event->updated_axes & (WLR_TABLET_TOOL_AXIS_TILT_X | WLR_TABLET_TOOL_AXIS_TILT_Y)) {
wlr_tablet_v2_tablet_tool_notify_tilt(
- roots_tool->tablet_v2_tool, event->tilt_x, event->tilt_y);
+ roots_tool->tablet_v2_tool,
+ roots_tool->tilt_x, roots_tool->tilt_y);
}
if (event->updated_axes & WLR_TABLET_TOOL_AXIS_ROTATION) {
@@ -285,6 +295,12 @@ static void handle_tool_proximity(struct wl_listener *listener, void *data) {
wl_list_init(&roots_tool->tool_link);
}
+ if (event->state == WLR_TABLET_TOOL_PROXIMITY_OUT) {
+ struct roots_tablet_tool *roots_tool = tool->data;
+ wlr_tablet_v2_tablet_tool_notify_proximity_out(roots_tool->tablet_v2_tool);
+ return;
+ }
+
handle_tablet_tool_position(cursor, event->device->data, event->tool,
true, true, event->x, event->y, 0, 0);
}
@@ -299,6 +315,14 @@ static void handle_request_set_cursor(struct wl_listener *listener,
roots_cursor_handle_request_set_cursor(cursor, event);
}
+static void handle_pointer_focus_change(struct wl_listener *listener,
+ void *data) {
+ struct roots_cursor *cursor =
+ wl_container_of(listener, cursor, focus_change);
+ struct wlr_seat_pointer_focus_change_event *event = data;
+ roots_cursor_handle_focus_change(cursor, event);
+}
+
static void seat_reset_device_mappings(struct roots_seat *seat,
struct wlr_input_device *device) {
struct wlr_cursor *cursor = seat->cursor->cursor;
@@ -434,6 +458,12 @@ static void roots_seat_init_cursor(struct roots_seat *seat) {
wl_signal_add(&seat->seat->events.request_set_cursor,
&seat->cursor->request_set_cursor);
seat->cursor->request_set_cursor.notify = handle_request_set_cursor;
+
+ wl_signal_add(&seat->seat->pointer_state.events.focus_change,
+ &seat->cursor->focus_change);
+ seat->cursor->focus_change.notify = handle_pointer_focus_change;
+
+ wl_list_init(&seat->cursor->constraint_commit.link);
}
static void roots_drag_icon_handle_surface_commit(struct wl_listener *listener,
@@ -567,6 +597,7 @@ struct roots_seat *roots_seat_create(struct roots_input *input, char *name) {
free(seat);
return NULL;
}
+ seat->seat->data = seat;
roots_seat_init_cursor(seat);
if (!seat->cursor) {
@@ -575,6 +606,8 @@ struct roots_seat *roots_seat_create(struct roots_input *input, char *name) {
return NULL;
}
+ roots_input_method_relay_init(seat, &seat->im_relay);
+
wl_list_insert(&input->seats, &seat->link);
seat->new_drag_icon.notify = roots_seat_handle_new_drag_icon;
@@ -1152,6 +1185,7 @@ void roots_seat_set_focus(struct roots_seat *seat, struct roots_view *view) {
if (view == NULL) {
seat->cursor->mode = ROOTS_CURSOR_PASSTHROUGH;
wlr_seat_keyboard_clear_focus(seat->seat);
+ roots_input_method_relay_set_focus(&seat->im_relay, NULL);
return;
}
@@ -1186,6 +1220,12 @@ void roots_seat_set_focus(struct roots_seat *seat, struct roots_view *view) {
wlr_seat_keyboard_notify_enter(seat->seat, view->wlr_surface,
NULL, 0, NULL);
}
+
+ if (seat->cursor) {
+ roots_cursor_update_focus(seat->cursor);
+ }
+
+ roots_input_method_relay_set_focus(&seat->im_relay, view->wlr_surface);
}
/**
@@ -1220,6 +1260,11 @@ void roots_seat_set_focus_layer(struct roots_seat *seat,
wlr_seat_keyboard_notify_enter(seat->seat, layer->surface,
NULL, 0, NULL);
}
+
+
+ if (seat->cursor) {
+ roots_cursor_update_focus(seat->cursor);
+ }
}
void roots_seat_set_exclusive_client(struct roots_seat *seat,
diff --git a/rootston/text_input.c b/rootston/text_input.c
new file mode 100644
index 00000000..70c92761
--- /dev/null
+++ b/rootston/text_input.c
@@ -0,0 +1,310 @@
+#include <assert.h>
+#include <stdlib.h>
+#include <wlr/util/log.h>
+#include "rootston/seat.h"
+#include "rootston/text_input.h"
+
+static struct roots_text_input *relay_get_focusable_text_input(
+ struct roots_input_method_relay *relay) {
+ struct roots_text_input *text_input = NULL;
+ wl_list_for_each(text_input, &relay->text_inputs, link) {
+ if (text_input->pending_focused_surface) {
+ return text_input;
+ }
+ }
+ return NULL;
+}
+
+static struct roots_text_input *relay_get_focused_text_input(
+ struct roots_input_method_relay *relay) {
+ struct roots_text_input *text_input = NULL;
+ wl_list_for_each(text_input, &relay->text_inputs, link) {
+ if (text_input->input->focused_surface) {
+ return text_input;
+ }
+ }
+ return NULL;
+}
+
+static void handle_im_commit(struct wl_listener *listener, void *data) {
+ struct roots_input_method_relay *relay = wl_container_of(listener, relay,
+ input_method_commit);
+
+ struct roots_text_input *text_input = relay_get_focused_text_input(relay);
+ if (!text_input) {
+ return;
+ }
+ struct wlr_input_method_v2 *context = data;
+ assert(context == relay->input_method);
+ if (context->current.preedit.text) {
+ wlr_text_input_v3_send_preedit_string(text_input->input,
+ context->current.preedit.text,
+ context->current.preedit.cursor_begin,
+ context->current.preedit.cursor_end);
+ }
+ if (context->current.commit_text) {
+ wlr_text_input_v3_send_commit_string(text_input->input,
+ context->current.commit_text);
+ }
+ if (context->current.delete.before_length
+ || context->current.delete.after_length) {
+ wlr_text_input_v3_send_delete_surrounding_text(text_input->input,
+ context->current.delete.before_length,
+ context->current.delete.after_length);
+ }
+ wlr_text_input_v3_send_done(text_input->input);
+}
+
+static void text_input_set_pending_focused_surface(
+ struct roots_text_input *text_input, struct wlr_surface *surface) {
+ text_input->pending_focused_surface = surface;
+ wl_signal_add(&surface->events.destroy,
+ &text_input->pending_focused_surface_destroy);
+}
+
+static void text_input_clear_pending_focused_surface(
+ struct roots_text_input *text_input) {
+ wl_list_remove(&text_input->pending_focused_surface_destroy.link);
+ wl_list_init(&text_input->pending_focused_surface_destroy.link);
+ text_input->pending_focused_surface = NULL;
+}
+
+static void handle_im_destroy(struct wl_listener *listener, void *data) {
+ struct roots_input_method_relay *relay = wl_container_of(listener, relay,
+ input_method_destroy);
+ struct wlr_input_method_v2 *context = data;
+ assert(context == relay->input_method);
+ relay->input_method = NULL;
+ struct roots_text_input *text_input = relay_get_focused_text_input(relay);
+ if (text_input) {
+ // keyboard focus is still there, so keep the surface at hand in case
+ // the input method returns
+ text_input_set_pending_focused_surface(text_input,
+ text_input->input->focused_surface);
+ wlr_text_input_v3_send_leave(text_input->input);
+ }
+}
+
+static void relay_send_im_done(struct roots_input_method_relay *relay,
+ struct wlr_text_input_v3 *input) {
+ struct wlr_input_method_v2 *input_method = relay->input_method;
+ if (!input_method) {
+ wlr_log(WLR_INFO, "Sending IM_DONE but im is gone");
+ return;
+ }
+ // TODO: only send each of those if they were modified
+ wlr_input_method_v2_send_surrounding_text(input_method,
+ input->current.surrounding.text, input->current.surrounding.cursor,
+ input->current.surrounding.anchor);
+ wlr_input_method_v2_send_text_change_cause(input_method,
+ input->current.text_change_cause);
+ wlr_input_method_v2_send_content_type(input_method,
+ input->current.content_type.hint, input->current.content_type.purpose);
+ wlr_input_method_v2_send_done(input_method);
+ // TODO: pass intent, display popup size
+}
+
+static struct roots_text_input *text_input_to_roots(
+ struct roots_input_method_relay *relay,
+ struct wlr_text_input_v3 *text_input) {
+ struct roots_text_input *roots_text_input = NULL;
+ wl_list_for_each(roots_text_input, &relay->text_inputs, link) {
+ if (roots_text_input->input == text_input) {
+ return roots_text_input;
+ }
+ }
+ return NULL;
+}
+
+static void handle_text_input_enable(struct wl_listener *listener, void *data) {
+ struct roots_input_method_relay *relay = wl_container_of(listener, relay,
+ text_input_enable);
+ if (relay->input_method == NULL) {
+ wlr_log(WLR_INFO, "Enabling text input when input method is gone");
+ return;
+ }
+ struct roots_text_input *text_input = text_input_to_roots(relay,
+ (struct wlr_text_input_v3*)data);
+ wlr_input_method_v2_send_activate(relay->input_method);
+ relay_send_im_done(relay, text_input->input);
+}
+
+static void handle_text_input_commit(struct wl_listener *listener,
+ void *data) {
+ struct roots_input_method_relay *relay = wl_container_of(listener, relay,
+ text_input_commit);
+ struct roots_text_input *text_input = text_input_to_roots(relay,
+ (struct wlr_text_input_v3*)data);
+ if (!text_input->input->current_enabled) {
+ wlr_log(WLR_INFO, "Inactive text input tried to commit an update");
+ return;
+ }
+ wlr_log(WLR_DEBUG, "Text input committed update");
+ if (relay->input_method == NULL) {
+ wlr_log(WLR_INFO, "Text input committed, but input method is gone");
+ return;
+ }
+ relay_send_im_done(relay, text_input->input);
+}
+
+static void relay_disable_text_input(struct roots_input_method_relay *relay,
+ struct roots_text_input *text_input) {
+ if (relay->input_method == NULL) {
+ wlr_log(WLR_DEBUG, "Disabling text input, but input method is gone");
+ return;
+ }
+ wlr_input_method_v2_send_deactivate(relay->input_method);
+ relay_send_im_done(relay, text_input->input);
+}
+
+static void handle_text_input_disable(struct wl_listener *listener,
+ void *data) {
+ struct roots_input_method_relay *relay = wl_container_of(listener, relay,
+ text_input_disable);
+ struct roots_text_input *text_input = text_input_to_roots(relay,
+ (struct wlr_text_input_v3*)data);
+ relay_disable_text_input(relay, text_input);
+}
+
+static void handle_text_input_destroy(struct wl_listener *listener,
+ void *data) {
+ struct roots_input_method_relay *relay = wl_container_of(listener, relay,
+ text_input_destroy);
+ struct roots_text_input *text_input = text_input_to_roots(relay,
+ (struct wlr_text_input_v3*)data);
+
+ if (text_input->input->current_enabled) {
+ relay_disable_text_input(relay, text_input);
+ }
+ text_input_clear_pending_focused_surface(text_input);
+ wl_list_remove(&text_input->link);
+ text_input->input = NULL;
+ free(text_input);
+}
+
+static void handle_pending_focused_surface_destroy(struct wl_listener *listener,
+ void *data) {
+ struct roots_text_input *text_input = wl_container_of(listener, text_input,
+ pending_focused_surface_destroy);
+ struct wlr_surface *surface = data;
+ assert(text_input->pending_focused_surface == surface);
+ text_input->pending_focused_surface = NULL;
+}
+
+struct roots_text_input *roots_text_input_create(
+ struct roots_input_method_relay *relay,
+ struct wlr_text_input_v3 *text_input) {
+ struct roots_text_input *input = calloc(1, sizeof(struct roots_text_input));
+ if (!input) {
+ return NULL;
+ }
+ input->input = text_input;
+ input->relay = relay;
+
+ wl_signal_add(&text_input->events.enable, &relay->text_input_enable);
+ relay->text_input_enable.notify = handle_text_input_enable;
+
+ wl_signal_add(&text_input->events.commit, &relay->text_input_commit);
+ relay->text_input_commit.notify = handle_text_input_commit;
+
+ wl_signal_add(&text_input->events.disable, &relay->text_input_disable);
+ relay->text_input_disable.notify = handle_text_input_disable;
+
+ wl_signal_add(&text_input->events.destroy, &relay->text_input_destroy);
+ relay->text_input_destroy.notify = handle_text_input_destroy;
+
+ input->pending_focused_surface_destroy.notify =
+ handle_pending_focused_surface_destroy;
+ wl_list_init(&input->pending_focused_surface_destroy.link);
+ return input;
+}
+
+static void relay_handle_text_input(struct wl_listener *listener,
+ void *data) {
+ struct roots_input_method_relay *relay = wl_container_of(listener, relay,
+ text_input_new);
+ struct wlr_text_input_v3 *wlr_text_input = data;
+ if (relay->seat->seat != wlr_text_input->seat) {
+ return;
+ }
+
+ struct roots_text_input *text_input = roots_text_input_create(relay,
+ wlr_text_input);
+ if (!text_input) {
+ return;
+ }
+ wl_list_insert(&relay->text_inputs, &text_input->link);
+}
+
+static void relay_handle_input_method(struct wl_listener *listener,
+ void *data) {
+ struct roots_input_method_relay *relay = wl_container_of(listener, relay,
+ input_method_new);
+ struct wlr_input_method_v2 *input_method = data;
+ if (relay->seat->seat != input_method->seat) {
+ return;
+ }
+
+ if (relay->input_method != NULL) {
+ wlr_log(WLR_INFO, "Attempted to connect second input method to a seat");
+ wlr_input_method_v2_send_unavailable(input_method);
+ return;
+ }
+
+ relay->input_method = input_method;
+ wl_signal_add(&relay->input_method->events.commit,
+ &relay->input_method_commit);
+ relay->input_method_commit.notify = handle_im_commit;
+ wl_signal_add(&relay->input_method->events.destroy,
+ &relay->input_method_destroy);
+ relay->input_method_destroy.notify = handle_im_destroy;
+
+ struct roots_text_input *text_input = relay_get_focusable_text_input(relay);
+ if (text_input) {
+ wlr_text_input_v3_send_enter(text_input->input,
+ text_input->pending_focused_surface);
+ text_input_clear_pending_focused_surface(text_input);
+ }
+}
+
+void roots_input_method_relay_init(struct roots_seat *seat,
+ struct roots_input_method_relay *relay) {
+ relay->seat = seat;
+ wl_list_init(&relay->text_inputs);
+
+ relay->text_input_new.notify = relay_handle_text_input;
+ wl_signal_add(&seat->input->server->desktop->text_input->events.text_input,
+ &relay->text_input_new);
+
+ relay->input_method_new.notify = relay_handle_input_method;
+ wl_signal_add(
+ &seat->input->server->desktop->input_method->events.input_method,
+ &relay->input_method_new);
+}
+
+void roots_input_method_relay_set_focus(struct roots_input_method_relay *relay,
+ struct wlr_surface *surface) {
+ struct roots_text_input *text_input;
+ wl_list_for_each(text_input, &relay->text_inputs, link) {
+ if (text_input->pending_focused_surface) {
+ assert(text_input->input->focused_surface == NULL);
+ if (surface != text_input->pending_focused_surface) {
+ text_input_clear_pending_focused_surface(text_input);
+ }
+ } else if (text_input->input->focused_surface) {
+ assert(text_input->pending_focused_surface == NULL);
+ if (surface != text_input->input->focused_surface) {
+ relay_disable_text_input(relay, text_input);
+ wlr_text_input_v3_send_leave(text_input->input);
+ }
+ } else if (surface
+ && wl_resource_get_client(text_input->input->resource)
+ == wl_resource_get_client(surface->resource)) {
+ if (relay->input_method) {
+ wlr_text_input_v3_send_enter(text_input->input, surface);
+ } else {
+ text_input_set_pending_focused_surface(text_input, surface);
+ }
+ }
+ }
+}
diff --git a/types/data_device/wlr_data_device.c b/types/data_device/wlr_data_device.c
index 40974de7..111c2de0 100644
--- a/types/data_device/wlr_data_device.c
+++ b/types/data_device/wlr_data_device.c
@@ -25,16 +25,21 @@ static struct wlr_seat_client *seat_client_from_data_device_resource(
static void data_device_set_selection(struct wl_client *client,
struct wl_resource *device_resource,
struct wl_resource *source_resource, uint32_t serial) {
+ struct wlr_seat_client *seat_client =
+ seat_client_from_data_device_resource(device_resource);
+
struct wlr_client_data_source *source = NULL;
if (source_resource != NULL) {
source = client_data_source_from_resource(source_resource);
}
- struct wlr_seat_client *seat_client =
- seat_client_from_data_device_resource(device_resource);
-
- struct wlr_data_source *wlr_source = (struct wlr_data_source *)source;
+ struct wlr_data_source *wlr_source =
+ source != NULL ? &source->source : NULL;
wlr_seat_set_selection(seat_client->seat, wlr_source, serial);
+
+ if (source != NULL) {
+ source->finalized = true;
+ }
}
static void data_device_start_drag(struct wl_client *client,
@@ -45,15 +50,13 @@ static void data_device_start_drag(struct wl_client *client,
struct wlr_seat_client *seat_client =
seat_client_from_data_device_resource(device_resource);
struct wlr_surface *origin = wlr_surface_from_resource(origin_resource);
- struct wlr_data_source *source = NULL;
- struct wlr_surface *icon = NULL;
- if (source_resource) {
- struct wlr_client_data_source *client_source =
- client_data_source_from_resource(source_resource);
- source = (struct wlr_data_source *)client_source;
+ struct wlr_client_data_source *source = NULL;
+ if (source_resource != NULL) {
+ source = client_data_source_from_resource(source_resource);
}
+ struct wlr_surface *icon = NULL;
if (icon_resource) {
icon = wlr_surface_from_resource(icon_resource);
if (!wlr_surface_set_role(icon, &drag_icon_surface_role, NULL,
@@ -62,13 +65,16 @@ static void data_device_start_drag(struct wl_client *client,
}
}
- if (!seat_client_start_drag(seat_client, source, icon, origin, serial)) {
+ struct wlr_data_source *wlr_source =
+ source != NULL ? &source->source : NULL;
+ if (!seat_client_start_drag(seat_client, wlr_source, icon,
+ origin, serial)) {
wl_resource_post_no_memory(device_resource);
return;
}
- if (source) {
- source->seat_client = seat_client;
+ if (source != NULL) {
+ source->finalized = true;
}
}
diff --git a/types/data_device/wlr_data_offer.c b/types/data_device/wlr_data_offer.c
index a5ea9183..9847e07c 100644
--- a/types/data_device/wlr_data_offer.c
+++ b/types/data_device/wlr_data_offer.c
@@ -41,8 +41,7 @@ static uint32_t data_offer_choose_action(struct wlr_data_offer *offer) {
return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
}
- if (offer->source->seat_client &&
- offer->source->compositor_action & available_actions) {
+ if (offer->source->compositor_action & available_actions) {
return offer->source->compositor_action;
}
diff --git a/types/data_device/wlr_data_source.c b/types/data_device/wlr_data_source.c
index bf638f5a..59650170 100644
--- a/types/data_device/wlr_data_source.c
+++ b/types/data_device/wlr_data_source.c
@@ -207,11 +207,10 @@ static void data_source_set_actions(struct wl_client *client,
return;
}
- if (source->source.seat_client) {
+ if (source->finalized) {
wl_resource_post_error(source->resource,
WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK,
- "invalid action change after "
- "wl_data_device.start_drag");
+ "invalid action change after wl_data_device.start_drag");
return;
}
diff --git a/types/meson.build b/types/meson.build
index 1329060e..bd7fd1a0 100644
--- a/types/meson.build
+++ b/types/meson.build
@@ -9,27 +9,32 @@ lib_wlr_types = static_library(
'seat/wlr_seat_pointer.c',
'seat/wlr_seat_touch.c',
'seat/wlr_seat.c',
- 'xdg_shell/wlr_xdg_popup.c',
- 'xdg_shell/wlr_xdg_positioner.c',
- 'xdg_shell/wlr_xdg_shell.c',
- 'xdg_shell/wlr_xdg_surface.c',
- 'xdg_shell/wlr_xdg_toplevel.c',
+ 'tablet_v2/wlr_tablet_v2_pad.c',
+ 'tablet_v2/wlr_tablet_v2_tablet.c',
+ 'tablet_v2/wlr_tablet_v2_tool.c',
+ 'tablet_v2/wlr_tablet_v2.c',
'xdg_shell_v6/wlr_xdg_popup_v6.c',
'xdg_shell_v6/wlr_xdg_positioner_v6.c',
'xdg_shell_v6/wlr_xdg_shell_v6.c',
'xdg_shell_v6/wlr_xdg_surface_v6.c',
'xdg_shell_v6/wlr_xdg_toplevel_v6.c',
+ 'xdg_shell/wlr_xdg_popup.c',
+ 'xdg_shell/wlr_xdg_positioner.c',
+ 'xdg_shell/wlr_xdg_shell.c',
+ 'xdg_shell/wlr_xdg_surface.c',
+ 'xdg_shell/wlr_xdg_toplevel.c',
'wlr_box.c',
'wlr_buffer.c',
'wlr_compositor.c',
'wlr_cursor.c',
'wlr_export_dmabuf_v1.c',
- 'wlr_gamma_control.c',
'wlr_gamma_control_v1.c',
+ 'wlr_gamma_control.c',
'wlr_idle_inhibit_v1.c',
'wlr_idle.c',
'wlr_input_device.c',
'wlr_input_inhibitor.c',
+ 'wlr_input_method_v2.c',
'wlr_keyboard.c',
'wlr_layer_shell_v1.c',
'wlr_linux_dmabuf_v1.c',
@@ -38,25 +43,24 @@ lib_wlr_types = static_library(
'wlr_output_damage.c',
'wlr_output_layout.c',
'wlr_output.c',
+ 'wlr_pointer_constraints_v1.c',
'wlr_pointer.c',
+ 'wlr_presentation_time.c',
'wlr_primary_selection.c',
'wlr_region.c',
+ 'wlr_screencopy_v1.c',
'wlr_screenshooter.c',
'wlr_server_decoration.c',
'wlr_surface.c',
- 'tablet_v2/wlr_tablet_v2.c',
- 'tablet_v2/wlr_tablet_v2_pad.c',
- 'tablet_v2/wlr_tablet_v2_tablet.c',
- 'tablet_v2/wlr_tablet_v2_tool.c',
'wlr_tablet_pad.c',
'wlr_tablet_tool.c',
+ 'wlr_text_input_v3.c',
'wlr_touch.c',
'wlr_virtual_keyboard_v1.c',
'wlr_wl_shell.c',
'wlr_xcursor_manager.c',
'wlr_xdg_decoration_v1.c',
'wlr_xdg_output_v1.c',
- 'wlr_screencopy_v1.c',
),
include_directories: wlr_inc,
dependencies: [pixman, xkbcommon, wayland_server, wlr_protos, libinput],
diff --git a/types/seat/wlr_seat.c b/types/seat/wlr_seat.c
index 2e7da0a4..1940d7d6 100644
--- a/types/seat/wlr_seat.c
+++ b/types/seat/wlr_seat.c
@@ -225,6 +225,8 @@ struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) {
seat->pointer_state.default_grab = pointer_grab;
seat->pointer_state.grab = pointer_grab;
+ wl_signal_init(&seat->pointer_state.events.focus_change);
+
// keyboard state
struct wlr_seat_keyboard_grab *keyboard_grab =
calloc(1, sizeof(struct wlr_seat_keyboard_grab));
diff --git a/types/seat/wlr_seat_keyboard.c b/types/seat/wlr_seat_keyboard.c
index 0400db14..53415f0c 100644
--- a/types/seat/wlr_seat_keyboard.c
+++ b/types/seat/wlr_seat_keyboard.c
@@ -12,7 +12,7 @@
#include <wlr/util/log.h>
#include "types/wlr_seat.h"
#include "util/signal.h"
-#include "util/os-compatibility.h"
+#include "util/shm.h"
static void default_keyboard_enter(struct wlr_seat_keyboard_grab *grab,
struct wlr_surface *surface, uint32_t keycodes[], size_t num_keycodes,
@@ -341,7 +341,7 @@ static void seat_client_send_keymap(struct wlr_seat_client *client,
continue;
}
- int keymap_fd = os_create_anonymous_file(keyboard->keymap_size);
+ int keymap_fd = allocate_shm_file(keyboard->keymap_size);
if (keymap_fd < 0) {
wlr_log(WLR_ERROR, "creating a keymap file for %zu bytes failed", keyboard->keymap_size);
continue;
diff --git a/types/seat/wlr_seat_pointer.c b/types/seat/wlr_seat_pointer.c
index 899fb64f..594a5b81 100644
--- a/types/seat/wlr_seat_pointer.c
+++ b/types/seat/wlr_seat_pointer.c
@@ -53,7 +53,7 @@ static void pointer_send_frame(struct wl_resource *resource) {
static const struct wl_pointer_interface pointer_impl;
-static struct wlr_seat_client *seat_client_from_pointer_resource(
+struct wlr_seat_client *wlr_seat_client_from_pointer_resource(
struct wl_resource *resource) {
assert(wl_resource_instance_of(resource, &wl_pointer_interface,
&pointer_impl));
@@ -69,7 +69,7 @@ static void pointer_set_cursor(struct wl_client *client,
struct wl_resource *surface_resource,
int32_t hotspot_x, int32_t hotspot_y) {
struct wlr_seat_client *seat_client =
- seat_client_from_pointer_resource(pointer_resource);
+ wlr_seat_client_from_pointer_resource(pointer_resource);
if (seat_client == NULL) {
return;
}
@@ -146,7 +146,7 @@ void wlr_seat_pointer_enter(struct wlr_seat *wlr_seat,
uint32_t serial = wl_display_next_serial(wlr_seat->display);
struct wl_resource *resource;
wl_resource_for_each(resource, &focused_client->pointers) {
- if (seat_client_from_pointer_resource(resource) == NULL) {
+ if (wlr_seat_client_from_pointer_resource(resource) == NULL) {
continue;
}
@@ -160,7 +160,7 @@ void wlr_seat_pointer_enter(struct wlr_seat *wlr_seat,
uint32_t serial = wl_display_next_serial(wlr_seat->display);
struct wl_resource *resource;
wl_resource_for_each(resource, &client->pointers) {
- if (seat_client_from_pointer_resource(resource) == NULL) {
+ if (wlr_seat_client_from_pointer_resource(resource) == NULL) {
continue;
}
@@ -183,7 +183,14 @@ void wlr_seat_pointer_enter(struct wlr_seat *wlr_seat,
wlr_seat->pointer_state.focused_client = client;
wlr_seat->pointer_state.focused_surface = surface;
- // TODO: send focus change event
+ struct wlr_seat_pointer_focus_change_event event = {
+ .seat = wlr_seat,
+ .new_surface = surface,
+ .old_surface = focused_surface,
+ .sx = sx,
+ .sy = sy,
+ };
+ wlr_signal_emit_safe(&wlr_seat->pointer_state.events.focus_change, &event);
}
void wlr_seat_pointer_clear_focus(struct wlr_seat *wlr_seat) {
@@ -199,7 +206,7 @@ void wlr_seat_pointer_send_motion(struct wlr_seat *wlr_seat, uint32_t time,
struct wl_resource *resource;
wl_resource_for_each(resource, &client->pointers) {
- if (seat_client_from_pointer_resource(resource) == NULL) {
+ if (wlr_seat_client_from_pointer_resource(resource) == NULL) {
continue;
}
@@ -219,7 +226,7 @@ uint32_t wlr_seat_pointer_send_button(struct wlr_seat *wlr_seat, uint32_t time,
uint32_t serial = wl_display_next_serial(wlr_seat->display);
struct wl_resource *resource;
wl_resource_for_each(resource, &client->pointers) {
- if (seat_client_from_pointer_resource(resource) == NULL) {
+ if (wlr_seat_client_from_pointer_resource(resource) == NULL) {
continue;
}
@@ -239,7 +246,7 @@ void wlr_seat_pointer_send_axis(struct wlr_seat *wlr_seat, uint32_t time,
struct wl_resource *resource;
wl_resource_for_each(resource, &client->pointers) {
- if (seat_client_from_pointer_resource(resource) == NULL) {
+ if (wlr_seat_client_from_pointer_resource(resource) == NULL) {
continue;
}
@@ -349,7 +356,7 @@ void seat_client_create_pointer(struct wlr_seat_client *seat_client,
void seat_client_destroy_pointer(struct wl_resource *resource) {
struct wlr_seat_client *seat_client =
- seat_client_from_pointer_resource(resource);
+ wlr_seat_client_from_pointer_resource(resource);
if (seat_client == NULL) {
return;
}
diff --git a/types/tablet_v2/wlr_tablet_v2_pad.c b/types/tablet_v2/wlr_tablet_v2_pad.c
index 185abfe8..578eef06 100644
--- a/types/tablet_v2/wlr_tablet_v2_pad.c
+++ b/types/tablet_v2/wlr_tablet_v2_pad.c
@@ -188,10 +188,9 @@ static void add_tablet_pad_group(struct wlr_tablet_v2_tablet_pad *pad,
struct wlr_tablet_pad_client_v2 *client,
struct wlr_tablet_pad_group *group, size_t index) {
- int version = wl_resource_get_version(client->resource);
- client->groups[index] =
- wl_resource_create(client->client, &zwp_tablet_pad_group_v2_interface,
- version, 0);
+ uint32_t version = wl_resource_get_version(client->resource);
+ client->groups[index] = wl_resource_create(client->client,
+ &zwp_tablet_pad_group_v2_interface, version, 0);
if (!client->groups[index]) {
wl_client_post_no_memory(client->client);
return;
@@ -228,15 +227,17 @@ static void add_tablet_pad_group(struct wlr_tablet_v2_tablet_pad *pad,
}
user_data->pad = client;
user_data->index = strip;
- client->strips[strip] =
- wl_resource_create(client->client, &zwp_tablet_pad_strip_v2_interface, 1, 0);
+ client->strips[strip] = wl_resource_create(client->client,
+ &zwp_tablet_pad_strip_v2_interface, version, 0);
if (!client->strips[strip]) {
+ free(user_data);
wl_client_post_no_memory(client->client);
return;
}
wl_resource_set_implementation(client->strips[strip],
&tablet_pad_strip_impl, user_data, destroy_tablet_pad_strip_v2);
- zwp_tablet_pad_group_v2_send_strip(client->groups[index], client->strips[strip]);
+ zwp_tablet_pad_group_v2_send_strip(client->groups[index],
+ client->strips[strip]);
}
client->ring_count = group->ring_count;
@@ -250,15 +251,17 @@ static void add_tablet_pad_group(struct wlr_tablet_v2_tablet_pad *pad,
}
user_data->pad = client;
user_data->index = ring;
- client->rings[ring] =
- wl_resource_create(client->client, &zwp_tablet_pad_ring_v2_interface, 1, 0);
+ client->rings[ring] = wl_resource_create(client->client,
+ &zwp_tablet_pad_ring_v2_interface, version, 0);
if (!client->rings[ring]) {
+ free(user_data);
wl_client_post_no_memory(client->client);
return;
}
wl_resource_set_implementation(client->rings[ring],
&tablet_pad_ring_impl, user_data, destroy_tablet_pad_ring_v2);
- zwp_tablet_pad_group_v2_send_ring(client->groups[index], client->rings[ring]);
+ zwp_tablet_pad_group_v2_send_ring(client->groups[index],
+ client->rings[ring]);
}
zwp_tablet_pad_group_v2_send_done(client->groups[index]);
diff --git a/types/wlr_box.c b/types/wlr_box.c
index ada6a733..44a80dc2 100644
--- a/types/wlr_box.c
+++ b/types/wlr_box.c
@@ -143,3 +143,12 @@ void wlr_box_rotated_bounds(const struct wlr_box *box, float rotation,
dest->y = floor(fmin(y1, y2));
dest->height = ceil(fmax(y1, y2) - fmin(y1, y2));
}
+
+void wlr_box_from_pixman_box32(const pixman_box32_t box, struct wlr_box *dest) {
+ *dest = (struct wlr_box){
+ .x = box.x1,
+ .y = box.y1,
+ .width = box.x2 - box.x1,
+ .height = box.y2 - box.y1,
+ };
+}
diff --git a/types/wlr_buffer.c b/types/wlr_buffer.c
index ce733f40..cec3475c 100644
--- a/types/wlr_buffer.c
+++ b/types/wlr_buffer.c
@@ -150,12 +150,20 @@ struct wlr_buffer *wlr_buffer_apply_damage(struct wlr_buffer *buffer,
}
struct wl_shm_buffer *shm_buf = wl_shm_buffer_get(resource);
- if (shm_buf == NULL) {
- // Uploading only damaged regions only works for wl_shm buffers
+ struct wl_shm_buffer *old_shm_buf = wl_shm_buffer_get(buffer->resource);
+ if (shm_buf == NULL || old_shm_buf == NULL) {
+ // Uploading only damaged regions only works for wl_shm buffers and
+ // mutable textures (created from wl_shm buffer)
+ return NULL;
+ }
+
+ enum wl_shm_format new_fmt = wl_shm_buffer_get_format(shm_buf);
+ enum wl_shm_format old_fmt = wl_shm_buffer_get_format(old_shm_buf);
+ if (new_fmt != old_fmt) {
+ // Uploading to textures can't change the format
return NULL;
}
- enum wl_shm_format fmt = wl_shm_buffer_get_format(shm_buf);
int32_t stride = wl_shm_buffer_get_stride(shm_buf);
int32_t width = wl_shm_buffer_get_width(shm_buf);
int32_t height = wl_shm_buffer_get_height(shm_buf);
@@ -173,7 +181,7 @@ struct wlr_buffer *wlr_buffer_apply_damage(struct wlr_buffer *buffer,
pixman_box32_t *rects = pixman_region32_rectangles(damage, &n);
for (int i = 0; i < n; ++i) {
pixman_box32_t *r = &rects[i];
- if (!wlr_texture_write_pixels(buffer->texture, fmt, stride,
+ if (!wlr_texture_write_pixels(buffer->texture, stride,
r->x2 - r->x1, r->y2 - r->y1, r->x1, r->y1,
r->x1, r->y1, data)) {
wl_shm_buffer_end_access(shm_buf);
diff --git a/types/wlr_cursor.c b/types/wlr_cursor.c
index 49f11b20..8094ff18 100644
--- a/types/wlr_cursor.c
+++ b/types/wlr_cursor.c
@@ -252,7 +252,7 @@ bool wlr_cursor_warp(struct wlr_cursor *cur, struct wlr_input_device *dev,
return result;
}
-static void cursor_warp_closest(struct wlr_cursor *cur,
+void wlr_cursor_warp_closest(struct wlr_cursor *cur,
struct wlr_input_device *dev, double lx, double ly) {
struct wlr_box *mapping = get_mapping(cur, dev);
if (mapping) {
@@ -286,7 +286,7 @@ void wlr_cursor_warp_absolute(struct wlr_cursor *cur,
double lx, ly;
wlr_cursor_absolute_to_layout_coords(cur, dev, x, y, &lx, &ly);
- cursor_warp_closest(cur, dev, lx, ly);
+ wlr_cursor_warp_closest(cur, dev, lx, ly);
}
void wlr_cursor_move(struct wlr_cursor *cur, struct wlr_input_device *dev,
@@ -296,7 +296,7 @@ void wlr_cursor_move(struct wlr_cursor *cur, struct wlr_input_device *dev,
double lx = !isnan(delta_x) ? cur->x + delta_x : cur->x;
double ly = !isnan(delta_y) ? cur->y + delta_y : cur->y;
- cursor_warp_closest(cur, dev, lx, ly);
+ wlr_cursor_warp_closest(cur, dev, lx, ly);
}
void wlr_cursor_set_image(struct wlr_cursor *cur, const uint8_t *pixels,
@@ -620,8 +620,14 @@ static void handle_layout_output_destroy(struct wl_listener *listener,
static void layout_add(struct wlr_cursor_state *state,
struct wlr_output_layout_output *l_output) {
- struct wlr_cursor_output_cursor *output_cursor =
- calloc(1, sizeof(struct wlr_cursor_output_cursor));
+ struct wlr_cursor_output_cursor *output_cursor;
+ wl_list_for_each(output_cursor, &state->output_cursors, link) {
+ if (output_cursor->output_cursor->output == l_output->output) {
+ return; // already added
+ }
+ }
+
+ output_cursor = calloc(1, sizeof(struct wlr_cursor_output_cursor));
if (output_cursor == NULL) {
wlr_log(WLR_ERROR, "Failed to allocate wlr_cursor_output_cursor");
return;
diff --git a/types/wlr_gamma_control_v1.c b/types/wlr_gamma_control_v1.c
index 9f049a0b..8ccc96e5 100644
--- a/types/wlr_gamma_control_v1.c
+++ b/types/wlr_gamma_control_v1.c
@@ -189,9 +189,15 @@ static void gamma_control_manager_get_gamma_control(struct wl_client *client,
wlr_output_get_gamma_size(output));
}
+static void gamma_control_manager_destroy(struct wl_client *client,
+ struct wl_resource *manager_resource) {
+ wl_resource_destroy(manager_resource);
+}
+
static const struct zwlr_gamma_control_manager_v1_interface
gamma_control_manager_impl = {
.get_gamma_control = gamma_control_manager_get_gamma_control,
+ .destroy = gamma_control_manager_destroy,
};
static void gamma_control_manager_handle_resource_destroy(
diff --git a/types/wlr_idle.c b/types/wlr_idle.c
index 2d0e8eed..29367ceb 100644
--- a/types/wlr_idle.c
+++ b/types/wlr_idle.c
@@ -27,6 +27,9 @@ static void idle_timeout_destroy(struct wlr_idle_timeout *timer) {
static int idle_notify(void *data) {
struct wlr_idle_timeout *timer = data;
+ if (timer->idle_state) {
+ return 0;
+ }
timer->idle_state = true;
org_kde_kwin_idle_timeout_send_idle(timer->resource);
return 1;
@@ -36,13 +39,18 @@ static void handle_activity(struct wlr_idle_timeout *timer) {
if (!timer->enabled) {
return;
}
- // rearm the timer
- wl_event_source_timer_update(timer->idle_source, timer->timeout);
+
// in case the previous state was sleeping send a resume event and switch state
if (timer->idle_state) {
timer->idle_state = false;
org_kde_kwin_idle_timeout_send_resumed(timer->resource);
}
+
+ // rearm the timer
+ wl_event_source_timer_update(timer->idle_source, timer->timeout);
+ if (timer->timeout == 0) {
+ idle_notify(timer);
+ }
}
static void handle_timer_resource_destroy(struct wl_resource *timer_resource) {
@@ -142,6 +150,9 @@ static void create_idle_timer(struct wl_client *client,
if (timer->enabled) {
// arm the timer
wl_event_source_timer_update(timer->idle_source, timer->timeout);
+ if (timer->timeout == 0) {
+ idle_notify(timer);
+ }
}
}
diff --git a/types/wlr_input_method_v2.c b/types/wlr_input_method_v2.c
new file mode 100644
index 00000000..c68ff88b
--- /dev/null
+++ b/types/wlr_input_method_v2.c
@@ -0,0 +1,298 @@
+#ifndef _POSIX_C_SOURCE
+#define _POSIX_C_SOURCE 200809L
+#endif
+#include <assert.h>
+#include <string.h>
+#include <wayland-util.h>
+#include <wlr/types/wlr_input_method_v2.h>
+#include <wlr/types/wlr_surface.h>
+#include <wlr/util/log.h>
+#include <xkbcommon/xkbcommon.h>
+#include "input-method-unstable-v2-protocol.h"
+#include "util/signal.h"
+
+static const struct zwp_input_method_v2_interface input_method_impl;
+
+static struct wlr_input_method_v2 *input_method_from_resource(
+ struct wl_resource *resource) {
+ assert(wl_resource_instance_of(resource,
+ &zwp_input_method_v2_interface, &input_method_impl));
+ return wl_resource_get_user_data(resource);
+}
+
+static void input_method_destroy(struct wlr_input_method_v2 *input_method) {
+ wlr_signal_emit_safe(&input_method->events.destroy, input_method);
+ wl_list_remove(&input_method->seat_destroy.link);
+ free(input_method->pending.commit_text);
+ free(input_method->pending.preedit.text);
+ free(input_method->current.commit_text);
+ free(input_method->current.preedit.text);
+ free(input_method);
+}
+
+static void input_method_resource_destroy(struct wl_resource *resource) {
+ struct wlr_input_method_v2 *input_method =
+ input_method_from_resource(resource);
+ if (!input_method) {
+ return;
+ }
+ input_method_destroy(input_method);
+}
+
+static void im_destroy(struct wl_client *client, struct wl_resource *resource) {
+ wl_resource_destroy(resource);
+}
+
+static void im_commit(struct wl_client *client, struct wl_resource *resource,
+ uint32_t serial) {
+ struct wlr_input_method_v2 *input_method =
+ input_method_from_resource(resource);
+ if (!input_method) {
+ return;
+ }
+ input_method->current = input_method->pending;
+ input_method->current_serial = serial;
+ struct wlr_input_method_v2_state default_state = {0};
+ input_method->pending = default_state;
+ wlr_signal_emit_safe(&input_method->events.commit, (void*)input_method);
+}
+
+static void im_commit_string(struct wl_client *client,
+ struct wl_resource *resource, const char *text) {
+ struct wlr_input_method_v2 *input_method =
+ input_method_from_resource(resource);
+ if (!input_method) {
+ return;
+ }
+ free(input_method->pending.commit_text);
+ input_method->pending.commit_text = strdup(text);
+}
+
+static void im_set_preedit_string(struct wl_client *client,
+ struct wl_resource *resource, const char *text, int32_t cursor_begin,
+ int32_t cursor_end) {
+ struct wlr_input_method_v2 *input_method =
+ input_method_from_resource(resource);
+ if (!input_method) {
+ return;
+ }
+ input_method->pending.preedit.cursor_begin = cursor_begin;
+ input_method->pending.preedit.cursor_end = cursor_end;
+ free(input_method->pending.preedit.text);
+ input_method->pending.preedit.text = strdup(text);
+}
+
+static void im_delete_surrounding_text(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t before_length, uint32_t after_length) {
+ struct wlr_input_method_v2 *input_method =
+ input_method_from_resource(resource);
+ if (!input_method) {
+ return;
+ }
+ input_method->pending.delete.before_length = before_length;
+ input_method->pending.delete.after_length = after_length;
+}
+
+static void im_get_input_popup_surface(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id,
+ struct wl_resource *surface) {
+ wlr_log(WLR_INFO, "Stub: zwp_input_method_v2::get_input_popup_surface");
+}
+
+
+static void im_grab_keyboard(struct wl_client *client,
+ struct wl_resource *resource, uint32_t keyboard) {
+ wlr_log(WLR_INFO, "Stub: zwp_input_method_v2::grab_keyboard");
+}
+
+static const struct zwp_input_method_v2_interface input_method_impl = {
+ .destroy = im_destroy,
+ .commit = im_commit,
+ .commit_string = im_commit_string,
+ .set_preedit_string = im_set_preedit_string,
+ .delete_surrounding_text = im_delete_surrounding_text,
+ .get_input_popup_surface = im_get_input_popup_surface,
+ .grab_keyboard = im_grab_keyboard,
+};
+
+void wlr_input_method_v2_send_activate(
+ struct wlr_input_method_v2 *input_method) {
+ zwp_input_method_v2_send_activate(input_method->resource);
+ input_method->active = true;
+}
+
+void wlr_input_method_v2_send_deactivate(
+ struct wlr_input_method_v2 *input_method) {
+ zwp_input_method_v2_send_deactivate(input_method->resource);
+ input_method->active = false;
+}
+
+void wlr_input_method_v2_send_surrounding_text(
+ struct wlr_input_method_v2 *input_method, const char *text,
+ uint32_t cursor, uint32_t anchor) {
+ const char *send_text = text;
+ if (!send_text) {
+ send_text = "";
+ }
+ zwp_input_method_v2_send_surrounding_text(input_method->resource, send_text,
+ cursor, anchor);
+}
+
+void wlr_input_method_v2_send_text_change_cause(
+ struct wlr_input_method_v2 *input_method, uint32_t cause) {
+ zwp_input_method_v2_send_text_change_cause(input_method->resource, cause);
+}
+
+void wlr_input_method_v2_send_content_type(
+ struct wlr_input_method_v2 *input_method,
+ uint32_t hint, uint32_t purpose) {
+ zwp_input_method_v2_send_content_type(input_method->resource, hint,
+ purpose);
+}
+
+void wlr_input_method_v2_send_done(struct wlr_input_method_v2 *input_method) {
+ zwp_input_method_v2_send_done(input_method->resource);
+ input_method->client_active = input_method->active;
+ input_method->current_serial++;
+}
+
+void wlr_input_method_v2_send_unavailable(
+ struct wlr_input_method_v2 *input_method) {
+ zwp_input_method_v2_send_unavailable(input_method->resource);
+ struct wl_resource *resource = input_method->resource;
+ input_method_destroy(input_method);
+ wl_resource_set_user_data(resource, NULL);
+}
+
+static const struct zwp_input_method_manager_v2_interface
+ input_method_manager_impl;
+
+static struct wlr_input_method_manager_v2 *input_method_manager_from_resource(
+ struct wl_resource *resource) {
+ assert(wl_resource_instance_of(resource,
+ &zwp_input_method_manager_v2_interface, &input_method_manager_impl));
+ return wl_resource_get_user_data(resource);
+}
+
+static void input_method_handle_seat_destroy(struct wl_listener *listener,
+ void *data) {
+ struct wlr_input_method_v2 *input_method = wl_container_of(listener,
+ input_method, seat_destroy);
+ wlr_input_method_v2_send_unavailable(input_method);
+}
+
+static void manager_get_input_method(struct wl_client *client,
+ struct wl_resource *resource, struct wl_resource *seat,
+ uint32_t input_method_id) {
+ struct wlr_input_method_manager_v2 *im_manager =
+ input_method_manager_from_resource(resource);
+
+ struct wlr_input_method_v2 *input_method = calloc(1,
+ sizeof(struct wlr_input_method_v2));
+ if (!input_method) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+ wl_signal_init(&input_method->events.commit);
+ wl_signal_init(&input_method->events.destroy);
+ int version = wl_resource_get_version(resource);
+ struct wl_resource *im_resource = wl_resource_create(client,
+ &zwp_input_method_v2_interface, version, input_method_id);
+ if (im_resource == NULL) {
+ free(input_method);
+ wl_client_post_no_memory(client);
+ return;
+ }
+ wl_resource_set_implementation(im_resource, &input_method_impl,
+ input_method, input_method_resource_destroy);
+
+ struct wlr_seat_client *seat_client = wlr_seat_client_from_resource(seat);
+ wl_signal_add(&seat_client->events.destroy,
+ &input_method->seat_destroy);
+ input_method->seat_destroy.notify =
+ input_method_handle_seat_destroy;
+
+ input_method->resource = im_resource;
+ input_method->seat = seat_client->seat;
+ wl_list_insert(&im_manager->input_methods,
+ wl_resource_get_link(input_method->resource));
+ wlr_signal_emit_safe(&im_manager->events.input_method, input_method);
+}
+
+static void manager_destroy(struct wl_client *client,
+ struct wl_resource *resource) {
+ wl_resource_destroy(resource);
+}
+
+static const struct zwp_input_method_manager_v2_interface
+ input_method_manager_impl = {
+ .get_input_method = manager_get_input_method,
+ .destroy = manager_destroy,
+};
+
+static void input_method_manager_unbind(struct wl_resource *resource) {
+ wl_list_remove(wl_resource_get_link(resource));
+}
+
+static void input_method_manager_bind(struct wl_client *wl_client, void *data,
+ uint32_t version, uint32_t id) {
+ assert(wl_client);
+ struct wlr_input_method_manager_v2 *im_manager = data;
+
+ struct wl_resource *bound_resource = wl_resource_create(wl_client,
+ &zwp_input_method_manager_v2_interface, version, id);
+ if (bound_resource == NULL) {
+ wl_client_post_no_memory(wl_client);
+ return;
+ }
+ wl_resource_set_implementation(bound_resource, &input_method_manager_impl,
+ im_manager, input_method_manager_unbind);
+ wl_list_insert(&im_manager->bound_resources,
+ wl_resource_get_link(bound_resource));
+}
+
+static void handle_display_destroy(struct wl_listener *listener, void *data) {
+ struct wlr_input_method_manager_v2 *manager =
+ wl_container_of(listener, manager, display_destroy);
+ wlr_input_method_manager_v2_destroy(manager);
+}
+
+struct wlr_input_method_manager_v2 *wlr_input_method_manager_v2_create(
+ struct wl_display *display) {
+ struct wlr_input_method_manager_v2 *im_manager = calloc(1,
+ sizeof(struct wlr_input_method_manager_v2));
+ if (!im_manager) {
+ return NULL;
+ }
+ wl_signal_init(&im_manager->events.input_method);
+ wl_signal_init(&im_manager->events.destroy);
+ wl_list_init(&im_manager->bound_resources);
+ wl_list_init(&im_manager->input_methods);
+
+ im_manager->display_destroy.notify = handle_display_destroy;
+ wl_display_add_destroy_listener(display, &im_manager->display_destroy);
+
+ im_manager->global = wl_global_create(display,
+ &zwp_input_method_manager_v2_interface, 1, im_manager,
+ input_method_manager_bind);
+ return im_manager;
+}
+
+void wlr_input_method_manager_v2_destroy(
+ struct wlr_input_method_manager_v2 *manager) {
+ wlr_signal_emit_safe(&manager->events.destroy, manager);
+ wl_list_remove(&manager->display_destroy.link);
+
+ struct wl_resource *resource, *resource_tmp;
+ wl_resource_for_each_safe(resource, resource_tmp,
+ &manager->bound_resources) {
+ wl_resource_destroy(resource);
+ }
+ struct wlr_input_method_v2 *im, *im_tmp;
+ wl_list_for_each_safe(im, im_tmp, &manager->input_methods, link) {
+ wl_resource_destroy(im->resource);
+ }
+ wl_global_destroy(manager->global);
+ free(manager);
+}
diff --git a/types/wlr_linux_dmabuf_v1.c b/types/wlr_linux_dmabuf_v1.c
index f72e7c07..eb7b2f9e 100644
--- a/types/wlr_linux_dmabuf_v1.c
+++ b/types/wlr_linux_dmabuf_v1.c
@@ -103,6 +103,7 @@ static void params_add(struct wl_client *client,
close(fd);
return;
}
+
buffer->attributes.modifier = modifier;
buffer->has_modifier = true;
@@ -382,13 +383,9 @@ struct wlr_linux_dmabuf_v1 *wlr_linux_dmabuf_v1_from_resource(
return dmabuf;
}
-static void linux_dmabuf_send_modifiers(struct wlr_linux_dmabuf_v1 *linux_dmabuf,
- struct wl_resource *resource) {
+static void linux_dmabuf_send_formats(struct wlr_linux_dmabuf_v1 *linux_dmabuf,
+ struct wl_resource *resource, uint32_t version) {
struct wlr_renderer *renderer = linux_dmabuf->renderer;
- /*
- * Use EGL_EXT_image_dma_buf_import_modifiers to query and advertise
- * format/modifier codes.
- */
uint64_t modifier_invalid = DRM_FORMAT_MOD_INVALID;
int *formats = NULL;
int num_formats = wlr_renderer_get_dmabuf_formats(renderer, &formats);
@@ -410,10 +407,17 @@ static void linux_dmabuf_send_modifiers(struct wlr_linux_dmabuf_v1 *linux_dmabuf
modifiers = &modifier_invalid;
}
for (int j = 0; j < num_modifiers; j++) {
- uint32_t modifier_lo = modifiers[j] & 0xFFFFFFFF;
- uint32_t modifier_hi = modifiers[j] >> 32;
- zwp_linux_dmabuf_v1_send_modifier(resource, formats[i],
- modifier_hi, modifier_lo);
+ if (version >= ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) {
+ uint32_t modifier_lo = modifiers[j] & 0xFFFFFFFF;
+ uint32_t modifier_hi = modifiers[j] >> 32;
+ zwp_linux_dmabuf_v1_send_modifier(resource,
+ formats[i],
+ modifier_hi,
+ modifier_lo);
+ } else if (modifiers[j] == DRM_FORMAT_MOD_LINEAR ||
+ modifiers == &modifier_invalid) {
+ zwp_linux_dmabuf_v1_send_format(resource, formats[i]);
+ }
}
if (modifiers != &modifier_invalid) {
free(modifiers);
@@ -439,10 +443,7 @@ static void linux_dmabuf_bind(struct wl_client *client, void *data,
wl_resource_set_implementation(resource, &linux_dmabuf_impl,
linux_dmabuf, linux_dmabuf_resource_destroy);
wl_list_insert(&linux_dmabuf->resources, wl_resource_get_link(resource));
-
- if (version >= ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) {
- linux_dmabuf_send_modifiers(linux_dmabuf, resource);
- }
+ linux_dmabuf_send_formats(linux_dmabuf, resource, version);
}
void wlr_linux_dmabuf_v1_destroy(struct wlr_linux_dmabuf_v1 *linux_dmabuf) {
diff --git a/types/wlr_output.c b/types/wlr_output.c
index 62fd9fb7..9936e015 100644
--- a/types/wlr_output.c
+++ b/types/wlr_output.c
@@ -266,6 +266,7 @@ void wlr_output_init(struct wlr_output *output, struct wlr_backend *backend,
wl_signal_init(&output->events.frame);
wl_signal_init(&output->events.needs_swap);
wl_signal_init(&output->events.swap_buffers);
+ wl_signal_init(&output->events.present);
wl_signal_init(&output->events.enable);
wl_signal_init(&output->events.mode);
wl_signal_init(&output->events.scale);
@@ -297,17 +298,17 @@ void wlr_output_destroy(struct wlr_output *output) {
wlr_signal_emit_safe(&output->events.destroy, output);
- struct wlr_output_mode *mode, *tmp_mode;
- wl_list_for_each_safe(mode, tmp_mode, &output->modes, link) {
- wl_list_remove(&mode->link);
- free(mode);
- }
+ // The backend is responsible for free-ing the list of modes
struct wlr_output_cursor *cursor, *tmp_cursor;
wl_list_for_each_safe(cursor, tmp_cursor, &output->cursors, link) {
wlr_output_cursor_destroy(cursor);
}
+ if (output->idle_frame != NULL) {
+ wl_event_source_remove(output->idle_frame);
+ }
+
pixman_region32_fini(&output->damage);
if (output->impl && output->impl->destroy) {
@@ -544,8 +545,11 @@ void wlr_output_send_frame(struct wlr_output *output) {
static void schedule_frame_handle_idle_timer(void *data) {
struct wlr_output *output = data;
output->idle_frame = NULL;
- if (!output->frame_pending) {
- wlr_output_send_frame(output);
+ if (!output->frame_pending && output->impl->schedule_frame) {
+ // Ask the backend to send a frame event when appropriate
+ if (output->impl->schedule_frame(output)) {
+ output->frame_pending = true;
+ }
}
}
@@ -554,21 +558,46 @@ void wlr_output_schedule_frame(struct wlr_output *output) {
return;
}
- // TODO: ask the backend to send a frame event when appropriate instead
+ // We're using an idle timer here in case a buffer swap happens right after
+ // this function is called
struct wl_event_loop *ev = wl_display_get_event_loop(output->display);
output->idle_frame =
wl_event_loop_add_idle(ev, schedule_frame_handle_idle_timer, output);
}
-bool wlr_output_set_gamma(struct wlr_output *output, uint32_t size,
- uint16_t *r, uint16_t *g, uint16_t *b) {
+void wlr_output_send_present(struct wlr_output *output,
+ struct wlr_output_event_present *event) {
+ struct wlr_output_event_present _event = {0};
+ if (event == NULL) {
+ event = &_event;
+ }
+
+ event->output = output;
+
+ struct timespec now;
+ if (event->when == NULL) {
+ clockid_t clock = wlr_backend_get_presentation_clock(output->backend);
+ errno = 0;
+ if (clock_gettime(clock, &now) != 0) {
+ wlr_log_errno(WLR_ERROR, "failed to send output present event: "
+ "failed to read clock");
+ return;
+ }
+ event->when = &now;
+ }
+
+ wlr_signal_emit_safe(&output->events.present, event);
+}
+
+bool wlr_output_set_gamma(struct wlr_output *output, size_t size,
+ const uint16_t *r, const uint16_t *g, const uint16_t *b) {
if (!output->impl->set_gamma) {
return false;
}
return output->impl->set_gamma(output, size, r, g, b);
}
-uint32_t wlr_output_get_gamma_size(struct wlr_output *output) {
+size_t wlr_output_get_gamma_size(struct wlr_output *output) {
if (!output->impl->get_gamma_size) {
return 0;
}
@@ -825,6 +854,7 @@ static void output_cursor_commit(struct wlr_output_cursor *cursor,
cursor->enabled = wlr_surface_has_buffer(surface);
cursor->width = surface->current.width * cursor->output->scale;
cursor->height = surface->current.height * cursor->output->scale;
+ output_cursor_update_visible(cursor);
if (update_hotspot) {
cursor->hotspot_x -= surface->current.dx * cursor->output->scale;
cursor->hotspot_y -= surface->current.dy * cursor->output->scale;
@@ -887,10 +917,9 @@ void wlr_output_cursor_set_surface(struct wlr_output_cursor *cursor,
if (surface != NULL) {
wl_signal_add(&surface->events.commit, &cursor->surface_commit);
wl_signal_add(&surface->events.destroy, &cursor->surface_destroy);
- output_cursor_commit(cursor, false);
cursor->visible = false;
- output_cursor_update_visible(cursor);
+ output_cursor_commit(cursor, false);
} else {
cursor->enabled = false;
cursor->width = 0;
diff --git a/types/wlr_output_layout.c b/types/wlr_output_layout.c
index b51153ae..6ba856f8 100644
--- a/types/wlr_output_layout.c
+++ b/types/wlr_output_layout.c
@@ -204,6 +204,7 @@ void wlr_output_layout_add(struct wlr_output_layout *layout,
struct wlr_output *output, int lx, int ly) {
struct wlr_output_layout_output *l_output =
wlr_output_layout_get(layout, output);
+ bool is_new = l_output == NULL;
if (!l_output) {
l_output = output_layout_output_create(layout, output);
if (!l_output) {
@@ -211,12 +212,16 @@ void wlr_output_layout_add(struct wlr_output_layout *layout,
return;
}
}
+
l_output->x = lx;
l_output->y = ly;
l_output->state->auto_configured = false;
output_layout_reconfigure(layout);
output_update_global(output);
- wlr_signal_emit_safe(&layout->events.add, l_output);
+
+ if (is_new) {
+ wlr_signal_emit_safe(&layout->events.add, l_output);
+ }
}
struct wlr_output_layout_output *wlr_output_layout_get(
@@ -409,6 +414,7 @@ void wlr_output_layout_add_auto(struct wlr_output_layout *layout,
struct wlr_output *output) {
struct wlr_output_layout_output *l_output =
wlr_output_layout_get(layout, output);
+ bool is_new = l_output == NULL;
if (!l_output) {
l_output = output_layout_output_create(layout, output);
if (!l_output) {
@@ -420,7 +426,10 @@ void wlr_output_layout_add_auto(struct wlr_output_layout *layout,
l_output->state->auto_configured = true;
output_layout_reconfigure(layout);
output_update_global(output);
- wlr_signal_emit_safe(&layout->events.add, l_output);
+
+ if (is_new) {
+ wlr_signal_emit_safe(&layout->events.add, l_output);
+ }
}
struct wlr_output *wlr_output_layout_get_center_output(
diff --git a/types/wlr_pointer_constraints_v1.c b/types/wlr_pointer_constraints_v1.c
new file mode 100644
index 00000000..196af92e
--- /dev/null
+++ b/types/wlr_pointer_constraints_v1.c
@@ -0,0 +1,372 @@
+#include <assert.h>
+#include <limits.h>
+#include <pixman.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <wayland-server.h>
+#include <wlr/types/wlr_box.h>
+#include <wlr/types/wlr_pointer_constraints_v1.h>
+#include <wlr/types/wlr_region.h>
+#include <wlr/util/log.h>
+#include "util/signal.h"
+
+static const struct zwp_locked_pointer_v1_interface locked_pointer_impl;
+static const struct zwp_confined_pointer_v1_interface confined_pointer_impl;
+static const struct zwp_pointer_constraints_v1_interface pointer_constraints_impl;
+static void pointer_constraint_destroy(struct wlr_pointer_constraint_v1 *constraint);
+
+static struct wlr_pointer_constraint_v1 *pointer_constraint_from_resource(
+ struct wl_resource *resource) {
+ assert(
+ wl_resource_instance_of(
+ resource, &zwp_confined_pointer_v1_interface,
+ &confined_pointer_impl) ||
+ wl_resource_instance_of(
+ resource, &zwp_locked_pointer_v1_interface,
+ &locked_pointer_impl));
+ return wl_resource_get_user_data(resource);
+}
+
+static struct wlr_pointer_constraints_v1 *pointer_constraints_from_resource(
+ struct wl_resource *resource) {
+ assert(wl_resource_instance_of(resource, &zwp_pointer_constraints_v1_interface,
+ &pointer_constraints_impl));
+ return wl_resource_get_user_data(resource);
+}
+
+static void resource_destroy(struct wl_client *client,
+ struct wl_resource *resource) {
+ wl_resource_destroy(resource);
+}
+
+static void pointer_constraint_destroy(struct wlr_pointer_constraint_v1 *constraint) {
+ if (constraint == NULL) {
+ return;
+ }
+
+ wlr_log(WLR_DEBUG, "destroying constraint %p", constraint);
+
+ wlr_signal_emit_safe(&constraint->events.destroy, constraint);
+
+ wl_resource_set_user_data(constraint->resource, NULL);
+ wl_list_remove(&constraint->link);
+ wl_list_remove(&constraint->surface_commit.link);
+ wl_list_remove(&constraint->surface_destroy.link);
+ wl_list_remove(&constraint->seat_destroy.link);
+ pixman_region32_fini(&constraint->current.region);
+ pixman_region32_fini(&constraint->pending.region);
+ pixman_region32_fini(&constraint->region);
+ free(constraint);
+}
+
+static void pointer_constraint_destroy_resource(struct wl_resource *resource) {
+ struct wlr_pointer_constraint_v1 *constraint =
+ pointer_constraint_from_resource(resource);
+
+ pointer_constraint_destroy(constraint);
+}
+
+static void pointer_constraint_set_region(
+ struct wlr_pointer_constraint_v1 *constraint,
+ struct wl_resource *region_resource) {
+ pixman_region32_clear(&constraint->pending.region);
+
+ if (region_resource) {
+ pixman_region32_t *region = wlr_region_from_resource(region_resource);
+ pixman_region32_copy(&constraint->pending.region, region);
+ }
+
+ constraint->pending.committed |= WLR_POINTER_CONSTRAINT_V1_STATE_REGION;
+}
+
+static void pointer_constraint_handle_set_region(struct wl_client *client,
+ struct wl_resource *resource, struct wl_resource *region_resource) {
+ struct wlr_pointer_constraint_v1 *constraint =
+ pointer_constraint_from_resource(resource);
+ if (constraint == NULL) {
+ return;
+ }
+
+ pointer_constraint_set_region(constraint, region_resource);
+}
+
+static void pointer_constraint_set_cursor_position_hint(struct wl_client *client,
+ struct wl_resource *resource, wl_fixed_t x, wl_fixed_t y) {
+ struct wlr_pointer_constraint_v1 *constraint =
+ pointer_constraint_from_resource(resource);
+ if (constraint == NULL) {
+ return;
+ }
+
+ constraint->pending.cursor_hint.x = wl_fixed_to_double(x);
+ constraint->pending.cursor_hint.y = wl_fixed_to_double(y);
+ constraint->pending.committed |= WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT;
+}
+
+static void pointer_constraint_commit(
+ struct wlr_pointer_constraint_v1 *constraint) {
+ if (constraint->pending.committed &
+ WLR_POINTER_CONSTRAINT_V1_STATE_REGION) {
+ pixman_region32_copy(&constraint->current.region,
+ &constraint->pending.region);
+ }
+ if (constraint->pending.committed &
+ WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT) {
+ constraint->current.cursor_hint = constraint->pending.cursor_hint;
+ }
+ constraint->current.committed |= constraint->pending.committed;
+
+ constraint->pending.committed = 0;
+
+ pixman_region32_clear(&constraint->region);
+ if (pixman_region32_not_empty(&constraint->current.region)) {
+ pixman_region32_intersect(&constraint->region,
+ &constraint->surface->input_region, &constraint->current.region);
+ } else {
+ pixman_region32_copy(&constraint->region,
+ &constraint->surface->input_region);
+ }
+}
+
+static void handle_surface_commit(struct wl_listener *listener, void *data) {
+ struct wlr_pointer_constraint_v1 *constraint =
+ wl_container_of(listener, constraint, surface_commit);
+
+ pointer_constraint_commit(constraint);
+}
+
+static void handle_surface_destroy(struct wl_listener *listener, void *data) {
+ struct wlr_pointer_constraint_v1 *constraint =
+ wl_container_of(listener, constraint, surface_destroy);
+
+ pointer_constraint_destroy(constraint);
+}
+
+static void handle_seat_destroy(struct wl_listener *listener, void *data) {
+ struct wlr_pointer_constraint_v1 *constraint =
+ wl_container_of(listener, constraint, seat_destroy);
+
+ pointer_constraint_destroy(constraint);
+}
+
+static const struct zwp_confined_pointer_v1_interface confined_pointer_impl = {
+ .destroy = resource_destroy,
+ .set_region = pointer_constraint_handle_set_region,
+};
+
+static const struct zwp_locked_pointer_v1_interface locked_pointer_impl = {
+ .destroy = resource_destroy,
+ .set_region = pointer_constraint_handle_set_region,
+ .set_cursor_position_hint = pointer_constraint_set_cursor_position_hint,
+};
+
+static void pointer_constraint_create(struct wl_client *client,
+ struct wl_resource *pointer_constraints_resource, uint32_t id,
+ struct wl_resource *surface_resource,
+ struct wl_resource *pointer_resource,
+ struct wl_resource *region_resource,
+ enum zwp_pointer_constraints_v1_lifetime lifetime,
+ enum wlr_pointer_constraint_v1_type type) {
+ struct wlr_pointer_constraints_v1 *pointer_constraints =
+ pointer_constraints_from_resource(pointer_constraints_resource);
+
+ struct wlr_surface *surface = wlr_surface_from_resource(surface_resource);
+ struct wlr_seat *seat =
+ wlr_seat_client_from_pointer_resource(pointer_resource)->seat;
+
+ if (wlr_pointer_constraints_v1_constraint_for_surface(pointer_constraints,
+ surface, seat)) {
+ wl_resource_post_error(pointer_constraints_resource,
+ ZWP_POINTER_CONSTRAINTS_V1_ERROR_ALREADY_CONSTRAINED,
+ "a pointer constraint with a wl_pointer of the same wl_seat"
+ " is already on this surface");
+ return;
+ }
+
+ uint32_t version = wl_resource_get_version(pointer_constraints_resource);
+
+ bool locked_pointer = type == WLR_POINTER_CONSTRAINT_V1_LOCKED;
+
+ struct wl_resource *resource = locked_pointer ?
+ wl_resource_create(client, &zwp_locked_pointer_v1_interface, version, id) :
+ wl_resource_create(client, &zwp_confined_pointer_v1_interface, version, id);
+ if (resource == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ struct wlr_pointer_constraint_v1 *constraint = calloc(1, sizeof(*constraint));
+ if (constraint == NULL) {
+ wl_resource_destroy(resource);
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ constraint->resource = resource;
+ constraint->surface = surface;
+ constraint->seat = seat;
+ constraint->lifetime = lifetime;
+ constraint->type = type;
+ constraint->pointer_constraints = pointer_constraints;
+
+ wl_signal_init(&constraint->events.destroy);
+
+ pixman_region32_init(&constraint->region);
+
+ pixman_region32_init(&constraint->pending.region);
+ pixman_region32_init(&constraint->current.region);
+
+ pointer_constraint_set_region(constraint, region_resource);
+ pointer_constraint_commit(constraint);
+
+ constraint->surface_commit.notify = handle_surface_commit;
+ wl_signal_add(&surface->events.commit, &constraint->surface_commit);
+
+ constraint->surface_destroy.notify = handle_surface_destroy;
+ wl_signal_add(&surface->events.destroy, &constraint->surface_destroy);
+
+ constraint->seat_destroy.notify = handle_seat_destroy;
+ wl_signal_add(&seat->events.destroy, &constraint->seat_destroy);
+
+ void *impl = locked_pointer ?
+ (void *)&locked_pointer_impl : (void *)&confined_pointer_impl;
+ wl_resource_set_implementation(constraint->resource, impl, constraint,
+ pointer_constraint_destroy_resource);
+
+ wlr_log(WLR_DEBUG, "new %s_pointer %p (res %p)",
+ locked_pointer ? "locked" : "confined",
+ constraint, constraint->resource);
+
+ wl_list_insert(&pointer_constraints->constraints, &constraint->link);
+
+ wlr_signal_emit_safe(&pointer_constraints->events.new_constraint,
+ constraint);
+}
+
+static void pointer_constraints_lock_pointer(struct wl_client *client,
+ struct wl_resource *cons_resource, uint32_t id,
+ struct wl_resource *surface, struct wl_resource *pointer,
+ struct wl_resource *region, enum zwp_pointer_constraints_v1_lifetime lifetime) {
+ pointer_constraint_create(client, cons_resource, id, surface, pointer,
+ region, lifetime, WLR_POINTER_CONSTRAINT_V1_LOCKED);
+}
+
+static void pointer_constraints_confine_pointer(struct wl_client *client,
+ struct wl_resource *cons_resource, uint32_t id,
+ struct wl_resource *surface, struct wl_resource *pointer,
+ struct wl_resource *region,
+ enum zwp_pointer_constraints_v1_lifetime lifetime) {
+ pointer_constraint_create(client, cons_resource, id, surface, pointer,
+ region, lifetime, WLR_POINTER_CONSTRAINT_V1_CONFINED);
+}
+
+static const struct zwp_pointer_constraints_v1_interface
+ pointer_constraints_impl = {
+ .destroy = resource_destroy,
+ .lock_pointer = pointer_constraints_lock_pointer,
+ .confine_pointer = pointer_constraints_confine_pointer,
+};
+
+static void pointer_constraints_destroy(struct wl_resource *resource) {
+ wl_list_remove(wl_resource_get_link(resource));
+}
+
+static void pointer_constraints_bind(struct wl_client *client, void *data,
+ uint32_t version, uint32_t id) {
+ struct wlr_pointer_constraints_v1 *pointer_constraints = data;
+ assert(client && pointer_constraints);
+
+ struct wl_resource *resource = wl_resource_create(client,
+ &zwp_pointer_constraints_v1_interface, version, id);
+ if (resource == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ wl_list_insert(&pointer_constraints->resources,
+ wl_resource_get_link(resource));
+ wl_resource_set_implementation(resource, &pointer_constraints_impl,
+ pointer_constraints, pointer_constraints_destroy);
+}
+
+struct wlr_pointer_constraints_v1 *wlr_pointer_constraints_v1_create(
+ struct wl_display *display) {
+ struct wlr_pointer_constraints_v1 *pointer_constraints =
+ calloc(1, sizeof(*pointer_constraints));
+
+ if (!pointer_constraints) {
+ return NULL;
+ }
+
+ struct wl_global *wl_global = wl_global_create(display,
+ &zwp_pointer_constraints_v1_interface, 1, pointer_constraints,
+ pointer_constraints_bind);
+ if (!wl_global) {
+ free(pointer_constraints);
+ return NULL;
+ }
+ pointer_constraints->global = wl_global;
+
+ wl_list_init(&pointer_constraints->resources);
+ wl_list_init(&pointer_constraints->constraints);
+ wl_signal_init(&pointer_constraints->events.new_constraint);
+
+ return pointer_constraints;
+}
+
+void wlr_pointer_constraints_v1_destroy(
+ struct wlr_pointer_constraints_v1 *pointer_constraints) {
+ struct wl_resource *resource, *_tmp_res;
+ wl_resource_for_each_safe(resource, _tmp_res,
+ &pointer_constraints->resources) {
+ wl_resource_destroy(resource);
+ }
+
+ struct wlr_pointer_constraint_v1 *constraint, *_tmp_cons;
+ wl_list_for_each_safe(constraint, _tmp_cons,
+ &pointer_constraints->constraints, link) {
+ wl_resource_destroy(constraint->resource);
+ }
+
+ wl_global_destroy(pointer_constraints->global);
+ free(pointer_constraints);
+}
+
+struct wlr_pointer_constraint_v1 *
+ wlr_pointer_constraints_v1_constraint_for_surface(
+ struct wlr_pointer_constraints_v1 *pointer_constraints,
+ struct wlr_surface *surface, struct wlr_seat *seat) {
+ struct wlr_pointer_constraint_v1 *constraint;
+ wl_list_for_each(constraint, &pointer_constraints->constraints, link) {
+ if (constraint->surface == surface && constraint->seat == seat) {
+ return constraint;
+ }
+ }
+
+ return NULL;
+}
+
+void wlr_pointer_constraint_v1_send_activated(
+ struct wlr_pointer_constraint_v1 *constraint) {
+ wlr_log(WLR_DEBUG, "constrained %p", constraint);
+ if (constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED) {
+ zwp_locked_pointer_v1_send_locked(constraint->resource);
+ } else {
+ zwp_confined_pointer_v1_send_confined(constraint->resource);
+ }
+}
+
+void wlr_pointer_constraint_v1_send_deactivated(
+ struct wlr_pointer_constraint_v1 *constraint) {
+ wlr_log(WLR_DEBUG, "unconstrained %p", constraint);
+ if (constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED) {
+ zwp_locked_pointer_v1_send_unlocked(constraint->resource);
+ } else {
+ zwp_confined_pointer_v1_send_unconfined(constraint->resource);
+ }
+
+ if (constraint->lifetime ==
+ ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT) {
+ pointer_constraint_destroy(constraint);
+ }
+}
diff --git a/types/wlr_presentation_time.c b/types/wlr_presentation_time.c
new file mode 100644
index 00000000..eeba47b6
--- /dev/null
+++ b/types/wlr_presentation_time.c
@@ -0,0 +1,223 @@
+#define _POSIX_C_SOURCE 199309L
+#include <assert.h>
+#include <stdlib.h>
+#include <wlr/types/wlr_presentation_time.h>
+#include <wlr/types/wlr_surface.h>
+#include <wlr/backend.h>
+#include "presentation-time-protocol.h"
+#include "util/signal.h"
+
+#define PRESENTATION_VERSION 1
+
+static struct wlr_presentation_feedback *presentation_feedback_from_resource(
+ struct wl_resource *resource) {
+ assert(wl_resource_instance_of(resource,
+ &wp_presentation_feedback_interface, NULL));
+ return wl_resource_get_user_data(resource);
+}
+
+static void feedback_handle_resource_destroy(struct wl_resource *resource) {
+ struct wlr_presentation_feedback *feedback =
+ presentation_feedback_from_resource(resource);
+ wl_list_remove(&feedback->surface_commit.link);
+ wl_list_remove(&feedback->surface_destroy.link);
+ wl_list_remove(&feedback->link);
+ free(feedback);
+}
+
+// Destroys the feedback
+static void feedback_send_presented(struct wlr_presentation_feedback *feedback,
+ struct wlr_presentation_event *event) {
+ struct wl_client *client = wl_resource_get_client(feedback->resource);
+ struct wl_resource *resource;
+ wl_resource_for_each(resource, &event->output->resources) {
+ if (wl_resource_get_client(resource) == client) {
+ wp_presentation_feedback_send_sync_output(feedback->resource,
+ resource);
+ }
+ }
+
+ uint32_t tv_sec_hi = event->tv_sec >> 32;
+ uint32_t tv_sec_lo = event->tv_sec & 0xFFFFFFFF;
+ uint32_t seq_hi = event->seq >> 32;
+ uint32_t seq_lo = event->seq & 0xFFFFFFFF;
+ wp_presentation_feedback_send_presented(feedback->resource,
+ tv_sec_hi, tv_sec_lo, event->tv_nsec, event->refresh,
+ seq_hi, seq_lo, event->flags);
+
+ wl_resource_destroy(feedback->resource);
+}
+
+// Destroys the feedback
+static void feedback_send_discarded(
+ struct wlr_presentation_feedback *feedback) {
+ wp_presentation_feedback_send_discarded(feedback->resource);
+ wl_resource_destroy(feedback->resource);
+}
+
+static void feedback_handle_surface_commit(struct wl_listener *listener,
+ void *data) {
+ struct wlr_presentation_feedback *feedback =
+ wl_container_of(listener, feedback, surface_commit);
+
+ if (feedback->committed) {
+ // The content update has been superseded
+ feedback_send_discarded(feedback);
+ } else {
+ feedback->committed = true;
+ }
+}
+
+static void feedback_handle_surface_destroy(struct wl_listener *listener,
+ void *data) {
+ struct wlr_presentation_feedback *feedback =
+ wl_container_of(listener, feedback, surface_destroy);
+ feedback_send_discarded(feedback);
+}
+
+static const struct wp_presentation_interface presentation_impl;
+
+static struct wlr_presentation *presentation_from_resource(
+ struct wl_resource *resource) {
+ assert(wl_resource_instance_of(resource, &wp_presentation_interface,
+ &presentation_impl));
+ return wl_resource_get_user_data(resource);
+}
+
+static void presentation_handle_feedback(struct wl_client *client,
+ struct wl_resource *resource, struct wl_resource *surface_resource,
+ uint32_t id) {
+ struct wlr_presentation *presentation =
+ presentation_from_resource(resource);
+ struct wlr_surface *surface = wlr_surface_from_resource(surface_resource);
+
+ struct wlr_presentation_feedback *feedback =
+ calloc(1, sizeof(struct wlr_presentation_feedback));
+ if (feedback == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ uint32_t version = wl_resource_get_version(resource);
+ feedback->resource = wl_resource_create(client,
+ &wp_presentation_feedback_interface, version, id);
+ if (feedback->resource == NULL) {
+ free(feedback);
+ wl_client_post_no_memory(client);
+ return;
+ }
+ wl_resource_set_implementation(feedback->resource, NULL, feedback,
+ feedback_handle_resource_destroy);
+
+ feedback->surface = surface;
+
+ feedback->surface_commit.notify = feedback_handle_surface_commit;
+ wl_signal_add(&surface->events.commit, &feedback->surface_commit);
+
+ feedback->surface_destroy.notify = feedback_handle_surface_destroy;
+ wl_signal_add(&surface->events.destroy, &feedback->surface_destroy);
+
+ wl_list_insert(&presentation->feedbacks, &feedback->link);
+}
+
+static void presentation_handle_destroy(struct wl_client *client,
+ struct wl_resource *resource) {
+ wl_resource_destroy(resource);
+}
+
+static const struct wp_presentation_interface presentation_impl = {
+ .feedback = presentation_handle_feedback,
+ .destroy = presentation_handle_destroy,
+};
+
+static void presentation_handle_resource_destroy(struct wl_resource *resource) {
+ wl_list_remove(wl_resource_get_link(resource));
+}
+
+static void presentation_bind(struct wl_client *client, void *data,
+ uint32_t version, uint32_t id) {
+ struct wlr_presentation *presentation = data;
+
+ struct wl_resource *resource = wl_resource_create(client,
+ &wp_presentation_interface, version, id);
+ if (resource == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+ wl_resource_set_implementation(resource, &presentation_impl, presentation,
+ presentation_handle_resource_destroy);
+ wl_list_insert(&presentation->resources, wl_resource_get_link(resource));
+
+ wp_presentation_send_clock_id(resource, (uint32_t)presentation->clock);
+}
+
+static void handle_display_destroy(struct wl_listener *listener, void *data) {
+ struct wlr_presentation *presentation =
+ wl_container_of(listener, presentation, display_destroy);
+ wlr_presentation_destroy(presentation);
+}
+
+struct wlr_presentation *wlr_presentation_create(struct wl_display *display,
+ struct wlr_backend *backend) {
+ struct wlr_presentation *presentation =
+ calloc(1, sizeof(struct wlr_presentation));
+ if (presentation == NULL) {
+ return NULL;
+ }
+
+ presentation->global = wl_global_create(display, &wp_presentation_interface,
+ PRESENTATION_VERSION, presentation, presentation_bind);
+ if (presentation->global == NULL) {
+ free(presentation);
+ return NULL;
+ }
+
+ presentation->clock = wlr_backend_get_presentation_clock(backend);
+
+ wl_list_init(&presentation->resources);
+ wl_list_init(&presentation->feedbacks);
+ wl_signal_init(&presentation->events.destroy);
+
+ presentation->display_destroy.notify = handle_display_destroy;
+ wl_display_add_destroy_listener(display, &presentation->display_destroy);
+
+ return presentation;
+}
+
+void wlr_presentation_destroy(struct wlr_presentation *presentation) {
+ if (presentation == NULL) {
+ return;
+ }
+
+ wlr_signal_emit_safe(&presentation->events.destroy, presentation);
+
+ wl_global_destroy(presentation->global);
+
+ struct wlr_presentation_feedback *feedback, *feedback_tmp;
+ wl_list_for_each_safe(feedback, feedback_tmp, &presentation->feedbacks,
+ link) {
+ wl_resource_destroy(feedback->resource);
+ }
+
+ struct wl_resource *resource, *resource_tmp;
+ wl_resource_for_each_safe(resource, resource_tmp,
+ &presentation->resources) {
+ wl_resource_destroy(resource);
+ }
+
+ wl_list_remove(&presentation->display_destroy.link);
+ free(presentation);
+}
+
+void wlr_presentation_send_surface_presented(
+ struct wlr_presentation *presentation, struct wlr_surface *surface,
+ struct wlr_presentation_event *event) {
+ // TODO: maybe use a hashtable to optimize this function
+ struct wlr_presentation_feedback *feedback, *feedback_tmp;
+ wl_list_for_each_safe(feedback, feedback_tmp,
+ &presentation->feedbacks, link) {
+ if (feedback->surface == surface && feedback->committed) {
+ feedback_send_presented(feedback, event);
+ }
+ }
+}
diff --git a/types/wlr_screencopy_v1.c b/types/wlr_screencopy_v1.c
index 0819e48d..b186b89b 100644
--- a/types/wlr_screencopy_v1.c
+++ b/types/wlr_screencopy_v1.c
@@ -4,6 +4,7 @@
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_screencopy_v1.h>
#include <wlr/backend.h>
+#include <wlr/util/log.h>
#include "wlr-screencopy-unstable-v1-protocol.h"
#include "util/signal.h"
@@ -40,6 +41,7 @@ static void frame_handle_output_swap_buffers(struct wl_listener *listener,
struct wlr_output_event_swap_buffers *event = _data;
struct wlr_output *output = frame->output;
struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend);
+ assert(renderer);
wl_list_remove(&frame->output_swap_buffers.link);
wl_list_init(&frame->output_swap_buffers.link);
@@ -214,11 +216,25 @@ static void capture_output(struct wl_client *client,
wl_list_init(&frame->output_swap_buffers.link);
wl_list_init(&frame->buffer_destroy.link);
- frame->format = WL_SHM_FORMAT_XRGB8888;
+ struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend);
+ assert(renderer);
+
+ if (!wlr_renderer_preferred_read_format(renderer, &frame->format)) {
+ wlr_log(WLR_ERROR,
+ "Failed to capture output: no read format supported by renderer");
+ goto error;
+ }
+
frame->box = buffer_box;
- frame->stride = 4 * buffer_box.width;
+ frame->stride = 4 * buffer_box.width; // TODO: depends on read format
+
zwlr_screencopy_frame_v1_send_buffer(frame->resource, frame->format,
buffer_box.width, buffer_box.height, frame->stride);
+ return;
+
+error:
+ zwlr_screencopy_frame_v1_send_failed(frame->resource);
+ frame_destroy(frame);
}
static void manager_handle_capture_output(struct wl_client *client,
diff --git a/types/wlr_surface.c b/types/wlr_surface.c
index 12931dad..b19387c8 100644
--- a/types/wlr_surface.c
+++ b/types/wlr_surface.c
@@ -114,9 +114,6 @@ static void surface_set_opaque_region(struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *region_resource) {
struct wlr_surface *surface = wlr_surface_from_resource(resource);
- if ((surface->pending.committed & WLR_SURFACE_STATE_OPAQUE_REGION)) {
- pixman_region32_clear(&surface->pending.opaque);
- }
surface->pending.committed |= WLR_SURFACE_STATE_OPAQUE_REGION;
if (region_resource) {
pixman_region32_t *region = wlr_region_from_resource(region_resource);
@@ -127,7 +124,8 @@ static void surface_set_opaque_region(struct wl_client *client,
}
static void surface_set_input_region(struct wl_client *client,
- struct wl_resource *resource, struct wl_resource *region_resource) {
+ struct wl_resource *resource,
+ struct wl_resource *region_resource) {
struct wlr_surface *surface = wlr_surface_from_resource(resource);
surface->pending.committed |= WLR_SURFACE_STATE_INPUT_REGION;
if (region_resource) {
@@ -323,9 +321,14 @@ static void surface_update_opaque_region(struct wlr_surface *surface) {
return;
}
- pixman_region32_copy(&surface->opaque_region, &surface->current.opaque);
pixman_region32_intersect_rect(&surface->opaque_region,
- &surface->opaque_region,
+ &surface->current.opaque,
+ 0, 0, surface->current.width, surface->current.height);
+}
+
+static void surface_update_input_region(struct wlr_surface *surface) {
+ pixman_region32_intersect_rect(&surface->input_region,
+ &surface->current.input,
0, 0, surface->current.width, surface->current.height);
}
@@ -350,6 +353,7 @@ static void surface_commit_pending(struct wlr_surface *surface) {
surface_apply_damage(surface);
}
surface_update_opaque_region(surface);
+ surface_update_input_region(surface);
// commit subsurface order
struct wlr_subsurface *subsurface;
@@ -557,6 +561,7 @@ static void surface_handle_resource_destroy(struct wl_resource *resource) {
surface_state_finish(&surface->previous);
pixman_region32_fini(&surface->buffer_damage);
pixman_region32_fini(&surface->opaque_region);
+ pixman_region32_fini(&surface->input_region);
wlr_buffer_unref(surface->buffer);
free(surface);
}
@@ -603,6 +608,7 @@ struct wlr_surface *wlr_surface_create(struct wl_client *client,
wl_list_init(&surface->subsurface_pending_list);
pixman_region32_init(&surface->buffer_damage);
pixman_region32_init(&surface->opaque_region);
+ pixman_region32_init(&surface->input_region);
wl_signal_add(&renderer->events.destroy, &surface->renderer_destroy);
surface->renderer_destroy.notify = surface_handle_renderer_destroy;
@@ -907,7 +913,7 @@ bool wlr_surface_point_accepts_input(struct wlr_surface *surface,
double sx, double sy) {
return sx >= 0 && sx < surface->current.width &&
sy >= 0 && sy < surface->current.height &&
- pixman_region32_contains_point(&surface->current.input, sx, sy, NULL);
+ pixman_region32_contains_point(&surface->current.input, floor(sx), floor(sy), NULL);
}
struct wlr_surface *wlr_surface_surface_at(struct wlr_surface *surface,
diff --git a/types/wlr_text_input_v3.c b/types/wlr_text_input_v3.c
new file mode 100644
index 00000000..6ec0762a
--- /dev/null
+++ b/types/wlr_text_input_v3.c
@@ -0,0 +1,337 @@
+#ifndef _POSIX_C_SOURCE
+#define _POSIX_C_SOURCE 200809L
+#endif
+#include <assert.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <wlr/types/wlr_text_input_v3.h>
+#include <wlr/util/log.h>
+#include "text-input-unstable-v3-protocol.h"
+#include "util/signal.h"
+
+static void text_input_clear_focused_surface(struct wlr_text_input_v3 *text_input) {
+ wl_list_remove(&text_input->surface_destroy.link);
+ wl_list_init(&text_input->surface_destroy.link);
+ text_input->focused_surface = NULL;
+}
+
+static const struct zwp_text_input_v3_interface text_input_impl;
+
+static struct wlr_text_input_v3 *text_input_from_resource(
+ struct wl_resource *resource) {
+ assert(wl_resource_instance_of(resource, &zwp_text_input_v3_interface,
+ &text_input_impl));
+ return wl_resource_get_user_data(resource);
+}
+
+void wlr_text_input_v3_send_enter(struct wlr_text_input_v3 *text_input,
+ struct wlr_surface *surface) {
+ assert(wl_resource_get_client(text_input->resource)
+ == wl_resource_get_client(surface->resource));
+ text_input->focused_surface = surface;
+ wl_signal_add(&text_input->focused_surface->events.destroy,
+ &text_input->surface_destroy);
+ zwp_text_input_v3_send_enter(text_input->resource,
+ text_input->focused_surface->resource);
+}
+
+void wlr_text_input_v3_send_leave(struct wlr_text_input_v3 *text_input) {
+ zwp_text_input_v3_send_leave(text_input->resource,
+ text_input->focused_surface->resource);
+ text_input_clear_focused_surface(text_input);
+}
+
+void wlr_text_input_v3_send_preedit_string(struct wlr_text_input_v3 *text_input,
+ const char *text, uint32_t cursor_begin, uint32_t cursor_end) {
+ zwp_text_input_v3_send_preedit_string(text_input->resource, text,
+ cursor_begin, cursor_end);
+}
+
+void wlr_text_input_v3_send_commit_string(struct wlr_text_input_v3 *text_input,
+ const char *text) {
+ zwp_text_input_v3_send_commit_string(text_input->resource, text);
+}
+
+void wlr_text_input_v3_send_delete_surrounding_text(
+ struct wlr_text_input_v3 *text_input, uint32_t before_length,
+ uint32_t after_length) {
+ zwp_text_input_v3_send_delete_surrounding_text(text_input->resource,
+ before_length, after_length);
+}
+
+void wlr_text_input_v3_send_done(struct wlr_text_input_v3 *text_input) {
+ zwp_text_input_v3_send_done(text_input->resource,
+ text_input->current_serial);
+}
+
+static void wlr_text_input_destroy(struct wlr_text_input_v3 *text_input) {
+ wlr_signal_emit_safe(&text_input->events.destroy, text_input);
+ text_input_clear_focused_surface(text_input);
+ wl_list_remove(&text_input->seat_destroy.link);
+ // remove from manager::text_inputs
+ wl_list_remove(&text_input->link);
+ free(text_input->current.surrounding.text);
+ free(text_input->pending.surrounding.text);
+ free(text_input);
+}
+
+static void text_input_resource_destroy(struct wl_resource *resource) {
+ struct wlr_text_input_v3 *text_input = text_input_from_resource(resource);
+ if (!text_input) {
+ return;
+ }
+ wlr_text_input_destroy(text_input);
+}
+
+static void text_input_destroy(struct wl_client *client,
+ struct wl_resource *resource) {
+ wl_resource_destroy(resource);
+}
+
+static void text_input_enable(struct wl_client *client,
+ struct wl_resource *resource) {
+ struct wlr_text_input_v3 *text_input = text_input_from_resource(resource);
+ if (!text_input) {
+ return;
+ }
+ struct wlr_text_input_v3_state defaults = {0};
+ free(text_input->pending.surrounding.text);
+ text_input->pending = defaults;
+ text_input->pending_enabled = true;
+}
+
+static void text_input_disable(struct wl_client *client,
+ struct wl_resource *resource) {
+ struct wlr_text_input_v3 *text_input = text_input_from_resource(resource);
+ if (!text_input) {
+ return;
+ }
+ text_input->pending_enabled = false;
+}
+
+static void text_input_set_surrounding_text(struct wl_client *client,
+ struct wl_resource *resource, const char *text, int32_t cursor,
+ int32_t anchor) {
+ struct wlr_text_input_v3 *text_input = text_input_from_resource(resource);
+ if (!text_input) {
+ return;
+ }
+ free(text_input->pending.surrounding.text);
+ text_input->pending.surrounding.text = strdup(text);
+ if (!text_input->pending.surrounding.text) {
+ wl_client_post_no_memory(client);
+ }
+
+ text_input->pending.surrounding.cursor = cursor;
+ text_input->pending.surrounding.anchor = anchor;
+}
+
+static void text_input_set_text_change_cause(struct wl_client *client,
+ struct wl_resource *resource, uint32_t cause) {
+ struct wlr_text_input_v3 *text_input = text_input_from_resource(resource);
+ if (!text_input) {
+ return;
+ }
+ text_input->pending.text_change_cause = cause;
+}
+
+static void text_input_set_content_type(struct wl_client *client,
+ struct wl_resource *resource, uint32_t hint, uint32_t purpose) {
+ struct wlr_text_input_v3 *text_input = text_input_from_resource(resource);
+ if (!text_input) {
+ return;
+ }
+ text_input->pending.content_type.hint = hint;
+ text_input->pending.content_type.purpose = purpose;
+}
+
+static void text_input_set_cursor_rectangle(struct wl_client *client,
+ struct wl_resource *resource, int32_t x, int32_t y, int32_t width,
+ int32_t height) {
+ struct wlr_text_input_v3 *text_input = text_input_from_resource(resource);
+ if (!text_input) {
+ return;
+ }
+ text_input->pending.cursor_rectangle.x = x;
+ text_input->pending.cursor_rectangle.y = y;
+ text_input->pending.cursor_rectangle.width = width;
+ text_input->pending.cursor_rectangle.height = height;
+}
+
+static void text_input_commit(struct wl_client *client,
+ struct wl_resource *resource) {
+ struct wlr_text_input_v3 *text_input = text_input_from_resource(resource);
+ if (!text_input) {
+ return;
+ }
+ free(text_input->current.surrounding.text);
+ text_input->current = text_input->pending;
+ if (text_input->pending.surrounding.text) {
+ text_input->current.surrounding.text =
+ strdup(text_input->pending.surrounding.text);
+ }
+
+ bool old_enabled = text_input->current_enabled;
+ text_input->current_enabled = text_input->pending_enabled;
+ text_input->current_serial++;
+
+ if (text_input->focused_surface == NULL) {
+ wlr_log(WLR_DEBUG, "Text input commit received without focus\n");
+ }
+
+ if (!old_enabled && text_input->current_enabled) {
+ wlr_signal_emit_safe(&text_input->events.enable, text_input);
+ } else if (old_enabled && !text_input->current_enabled) {
+ wlr_signal_emit_safe(&text_input->events.disable, text_input);
+ } else { // including never enabled
+ wlr_signal_emit_safe(&text_input->events.commit, text_input);
+ }
+}
+
+static const struct zwp_text_input_v3_interface text_input_impl = {
+ .destroy = text_input_destroy,
+ .enable = text_input_enable,
+ .disable = text_input_disable,
+ .set_surrounding_text = text_input_set_surrounding_text,
+ .set_text_change_cause = text_input_set_text_change_cause,
+ .set_content_type = text_input_set_content_type,
+ .set_cursor_rectangle = text_input_set_cursor_rectangle,
+ .commit = text_input_commit,
+};
+
+static const struct zwp_text_input_manager_v3_interface text_input_manager_impl;
+
+static struct wlr_text_input_manager_v3 *text_input_manager_from_resource(
+ struct wl_resource *resource) {
+ assert(wl_resource_instance_of(resource,
+ &zwp_text_input_manager_v3_interface, &text_input_manager_impl));
+ return wl_resource_get_user_data(resource);
+}
+
+static void text_input_manager_destroy(struct wl_client *client,
+ struct wl_resource *resource) {
+ wl_resource_destroy(resource);
+}
+
+static void text_input_handle_seat_destroy(struct wl_listener *listener,
+ void *data) {
+ struct wlr_text_input_v3 *text_input = wl_container_of(listener, text_input,
+ seat_destroy);
+ struct wl_resource *resource = text_input->resource;
+ wlr_text_input_destroy(text_input);
+ wl_resource_set_user_data(resource, NULL);
+}
+
+static void text_input_handle_focused_surface_destroy(
+ struct wl_listener *listener, void *data) {
+ struct wlr_text_input_v3 *text_input = wl_container_of(listener, text_input,
+ surface_destroy);
+ text_input_clear_focused_surface(text_input);
+}
+
+static void text_input_manager_get_text_input(struct wl_client *client,
+ struct wl_resource *resource, uint32_t id, struct wl_resource *seat) {
+ struct wlr_text_input_v3 *text_input =
+ calloc(1, sizeof(struct wlr_text_input_v3));
+ if (text_input == NULL) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ wl_signal_init(&text_input->events.enable);
+ wl_signal_init(&text_input->events.commit);
+ wl_signal_init(&text_input->events.disable);
+ wl_signal_init(&text_input->events.destroy);
+
+ int version = wl_resource_get_version(resource);
+ struct wl_resource *text_input_resource = wl_resource_create(client,
+ &zwp_text_input_v3_interface, version, id);
+ if (text_input_resource == NULL) {
+ free(text_input);
+ wl_client_post_no_memory(client);
+ return;
+ }
+ text_input->resource = text_input_resource;
+
+ wl_resource_set_implementation(text_input->resource, &text_input_impl,
+ text_input, text_input_resource_destroy);
+
+ struct wlr_seat_client *seat_client = wlr_seat_client_from_resource(seat);
+ struct wlr_seat *wlr_seat = seat_client->seat;
+ text_input->seat = wlr_seat;
+ wl_signal_add(&seat_client->events.destroy,
+ &text_input->seat_destroy);
+ text_input->seat_destroy.notify =
+ text_input_handle_seat_destroy;
+ text_input->surface_destroy.notify =
+ text_input_handle_focused_surface_destroy;
+ wl_list_init(&text_input->surface_destroy.link);
+
+ struct wlr_text_input_manager_v3 *manager =
+ text_input_manager_from_resource(resource);
+ wl_list_insert(&manager->text_inputs, &text_input->link);
+
+ wlr_signal_emit_safe(&manager->events.text_input, text_input);
+}
+
+static const struct zwp_text_input_manager_v3_interface
+ text_input_manager_impl = {
+ .destroy = text_input_manager_destroy,
+ .get_text_input = text_input_manager_get_text_input,
+};
+
+static void text_input_manager_unbind(struct wl_resource *resource) {
+ wl_list_remove(wl_resource_get_link(resource));
+}
+
+static void text_input_manager_bind(struct wl_client *wl_client, void *data,
+ uint32_t version, uint32_t id) {
+ struct wlr_text_input_manager_v3 *manager = data;
+ assert(wl_client && manager);
+
+ struct wl_resource *resource = wl_resource_create(wl_client,
+ &zwp_text_input_manager_v3_interface, version, id);
+ if (resource == NULL) {
+ wl_client_post_no_memory(wl_client);
+ return;
+ }
+ wl_list_insert(&manager->bound_resources, wl_resource_get_link(resource));
+ wl_resource_set_implementation(resource, &text_input_manager_impl,
+ manager, text_input_manager_unbind);
+}
+
+struct wlr_text_input_manager_v3 *wlr_text_input_manager_v3_create(
+ struct wl_display *wl_display) {
+ struct wlr_text_input_manager_v3 *manager =
+ calloc(1, sizeof(struct wlr_text_input_manager_v3));
+ wl_list_init(&manager->bound_resources);
+ wl_list_init(&manager->text_inputs);
+ wl_signal_init(&manager->events.text_input);
+ manager->global = wl_global_create(wl_display,
+ &zwp_text_input_manager_v3_interface, 1, manager,
+ text_input_manager_bind);
+ if (!manager->global) {
+ free(manager);
+ return NULL;
+ }
+ return manager;
+}
+
+void wlr_text_input_manager_v3_destroy(
+ struct wlr_text_input_manager_v3 *manager) {
+ wlr_signal_emit_safe(&manager->events.destroy, manager);
+ wl_list_remove(&manager->display_destroy.link);
+
+ struct wl_resource *resource, *resource_tmp;
+ wl_resource_for_each_safe(resource, resource_tmp,
+ &manager->bound_resources) {
+ wl_resource_destroy(resource);
+ }
+ struct wlr_text_input_v3 *text_input, *text_input_tmp;
+ wl_list_for_each_safe(text_input, text_input_tmp, &manager->text_inputs,
+ link) {
+ wl_resource_destroy(text_input->resource);
+ }
+ wl_global_destroy(manager->global);
+ free(manager);
+}
diff --git a/types/wlr_xdg_decoration_v1.c b/types/wlr_xdg_decoration_v1.c
index f5182daa..607b75e4 100644
--- a/types/wlr_xdg_decoration_v1.c
+++ b/types/wlr_xdg_decoration_v1.c
@@ -156,6 +156,11 @@ static struct wlr_xdg_decoration_manager_v1 *
return wl_resource_get_user_data(resource);
}
+static void decoration_manager_handle_destroy(
+ struct wl_client *client, struct wl_resource *manager_resource) {
+ wl_resource_destroy(manager_resource);
+}
+
static void decoration_manager_handle_get_toplevel_decoration(
struct wl_client *client, struct wl_resource *manager_resource,
uint32_t id, struct wl_resource *toplevel_resource) {
@@ -229,6 +234,7 @@ static void decoration_manager_handle_get_toplevel_decoration(
static const struct zxdg_decoration_manager_v1_interface
decoration_manager_impl = {
+ .destroy = decoration_manager_handle_destroy,
.get_toplevel_decoration = decoration_manager_handle_get_toplevel_decoration,
};
diff --git a/util/meson.build b/util/meson.build
index f9d1997d..dca3e9a4 100644
--- a/util/meson.build
+++ b/util/meson.build
@@ -3,10 +3,10 @@ lib_wlr_util = static_library(
files(
'array.c',
'log.c',
- 'os-compatibility.c',
'region.c',
+ 'shm.c',
'signal.c',
),
include_directories: wlr_inc,
- dependencies: [wayland_server, pixman],
+ dependencies: [wayland_server, pixman, rt],
)
diff --git a/util/os-compatibility.c b/util/os-compatibility.c
deleted file mode 100644
index bd39705d..00000000
--- a/util/os-compatibility.c
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright © 2012 Collabora, Ltd.
- *
- * 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 _POSIX_C_SOURCE 200809L
-#include <errno.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <wlr/config.h>
-#include "util/os-compatibility.h"
-
-int os_fd_set_cloexec(int fd) {
- if (fd == -1) {
- return -1;
- }
-
- long flags = fcntl(fd, F_GETFD);
- if (flags == -1) {
- return -1;
- }
-
- if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) {
- return -1;
- }
-
- return 0;
-}
-
-int set_cloexec_or_close(int fd) {
- if (os_fd_set_cloexec(fd) != 0) {
- close(fd);
- return -1;
- }
- return fd;
-}
-
-int create_tmpfile_cloexec(char *tmpname) {
- int fd;
- mode_t prev_umask = umask(0066);
-#ifdef HAVE_MKOSTEMP
- fd = mkostemp(tmpname, O_CLOEXEC);
- if (fd >= 0) {
- unlink(tmpname);
- }
-#else
- fd = mkstemp(tmpname);
- if (fd >= 0) {
- fd = set_cloexec_or_close(fd);
- unlink(tmpname);
- }
-#endif
- umask(prev_umask);
-
- return fd;
-}
-
-/*
- * Create a new, unique, anonymous file of the given size, and
- * return the file descriptor for it. The file descriptor is set
- * CLOEXEC. The file is immediately suitable for mmap()'ing
- * the given size at offset zero.
- *
- * The file should not have a permanent backing store like a disk,
- * but may have if XDG_RUNTIME_DIR is not properly implemented in OS.
- *
- * The file name is deleted from the file system.
- *
- * The file is suitable for buffer sharing between processes by
- * transmitting the file descriptor over Unix sockets using the
- * SCM_RIGHTS methods.
- *
- * If the C library implements posix_fallocate(), it is used to
- * guarantee that disk space is available for the file at the
- * given size. If disk space is insufficient, errno is set to ENOSPC.
- * If posix_fallocate() is not supported, program may receive
- * SIGBUS on accessing mmap()'ed file contents instead.
- */
-int os_create_anonymous_file(off_t size) {
- static const char template[] = "/wlroots-shared-XXXXXX";
-
- const char *path = getenv("XDG_RUNTIME_DIR");
- if (!path) {
- errno = ENOENT;
- return -1;
- }
-
- char *name = malloc(strlen(path) + sizeof(template));
- if (!name) {
- return -1;
- }
-
- strcpy(name, path);
- strcat(name, template);
-
- int fd = create_tmpfile_cloexec(name);
- free(name);
- if (fd < 0) {
- return -1;
- }
-
-#ifdef WLR_HAS_POSIX_FALLOCATE
- int ret;
- do {
- ret = posix_fallocate(fd, 0, size);
- } while (ret == EINTR);
- if (ret != 0) {
- close(fd);
- errno = ret;
- return -1;
- }
-#else
- int ret;
- do {
- ret = ftruncate(fd, size);
- } while (ret < 0 && errno == EINTR);
- if (ret < 0) {
- close(fd);
- return -1;
- }
-#endif
-
- return fd;
-}
diff --git a/util/region.c b/util/region.c
index 38f84c5e..61f9c7c7 100644
--- a/util/region.c
+++ b/util/region.c
@@ -1,5 +1,8 @@
+#include <assert.h>
#include <math.h>
+#include <limits.h>
#include <stdlib.h>
+#include <wlr/types/wlr_box.h>
#include <wlr/util/region.h>
void wlr_region_scale(pixman_region32_t *dst, pixman_region32_t *src,
@@ -177,3 +180,71 @@ void wlr_region_rotated_bounds(pixman_region32_t *dst, pixman_region32_t *src,
pixman_region32_init_rects(dst, dst_rects, nrects);
free(dst_rects);
}
+
+static void region_confine(pixman_region32_t *region, double x1, double y1, double x2,
+ double y2, double *x2_out, double *y2_out, pixman_box32_t box) {
+ double x_clamped = fmax(fmin(x2, box.x2 - 1), box.x1);
+ double y_clamped = fmax(fmin(y2, box.y2 - 1), box.y1);
+
+ // If the target coordinates are above box.{x,y}2 - 1, but less than
+ // box.{x,y}2, then they are still within the box.
+ if (floor(x_clamped) == floor(x2) && floor(y_clamped) == floor(y2)) {
+ *x2_out = x2;
+ *y2_out = y2;
+ return;
+ }
+
+ double dx = x2 - x1;
+ double dy = y2 - y1;
+
+ // We use fabs to avoid negative zeroes and thus avoid a bug
+ // with negative infinity.
+ double delta = fmin(fabs(x_clamped - x1) / fabs(dx), fabs(y_clamped - y1) / fabs(dy));
+
+ // We clamp it again due to precision errors.
+ double x = fmax(fmin(delta * dx + x1, box.x2 - 1), box.x1);
+ double y = fmax(fmin(delta * dy + y1, box.y2 - 1), box.y1);
+
+ // Go one unit past the boundary to find an adjacent box.
+ int x_ext = floor(x) + (dx == 0 ? 0 : dx > 0 ? 1 : -1);
+ int y_ext = floor(y) + (dy == 0 ? 0 : dy > 0 ? 1 : -1);
+
+ if (pixman_region32_contains_point(region, x_ext, y_ext, &box)) {
+ return region_confine(region, x1, y1, x2, y2, x2_out, y2_out, box);
+ } else if (dx == 0 || dy == 0) {
+ *x2_out = x;
+ *y2_out = y;
+ } else {
+ bool bordering_x = x == box.x1 || x == box.x2 - 1;
+ bool bordering_y = y == box.y1 || y == box.y2 - 1;
+
+ if ((bordering_x && bordering_y) || (!bordering_x && !bordering_y)) {
+ double x2_potential, y2_potential;
+ double tmp1, tmp2;
+ region_confine(region, x, y, x, y2, &tmp1, &y2_potential, box);
+ region_confine(region, x, y, x2, y, &x2_potential, &tmp2, box);
+ if (fabs(x2_potential - x) > fabs(y2_potential - y)) {
+ *x2_out = x2_potential;
+ *y2_out = y;
+ } else {
+ *x2_out = x;
+ *y2_out = y2_potential;
+ }
+ } else if (bordering_x) {
+ return region_confine(region, x, y, x, y2, x2_out, y2_out, box);
+ } else if (bordering_y) {
+ return region_confine(region, x, y, x2, y, x2_out, y2_out, box);
+ }
+ }
+}
+
+bool wlr_region_confine(pixman_region32_t *region, double x1, double y1, double x2,
+ double y2, double *x2_out, double *y2_out) {
+ pixman_box32_t box;
+ if (pixman_region32_contains_point(region, floor(x1), floor(y1), &box)) {
+ region_confine(region, x1, y1, x2, y2, x2_out, y2_out, box);
+ return true;
+ } else {
+ return false;
+ }
+}
diff --git a/util/shm.c b/util/shm.c
new file mode 100644
index 00000000..3783e473
--- /dev/null
+++ b/util/shm.c
@@ -0,0 +1,67 @@
+#define _POSIX_C_SOURCE 200112L
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <time.h>
+#include <unistd.h>
+#include <wlr/config.h>
+#include "util/shm.h"
+
+static void randname(char *buf) {
+ struct timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ long r = ts.tv_nsec;
+ for (int i = 0; i < 6; ++i) {
+ buf[i] = 'A'+(r&15)+(r&16)*2;
+ r >>= 5;
+ }
+}
+
+int create_shm_file(void) {
+ int retries = 100;
+ do {
+ char name[] = "/wlroots-XXXXXX";
+ randname(name + strlen(name) - 6);
+
+ --retries;
+ // CLOEXEC is guaranteed to be set by shm_open
+ int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
+ if (fd >= 0) {
+ shm_unlink(name);
+ return fd;
+ }
+ } while (retries > 0 && errno == EEXIST);
+
+ return -1;
+}
+
+int allocate_shm_file(size_t size) {
+ int fd = create_shm_file();
+ if (fd < 0) {
+ return -1;
+ }
+
+#ifdef WLR_HAS_POSIX_FALLOCATE
+ int ret;
+ do {
+ ret = posix_fallocate(fd, 0, size);
+ } while (ret == EINTR);
+ if (ret != 0) {
+ close(fd);
+ errno = ret;
+ return -1;
+ }
+#else
+ int ret;
+ do {
+ ret = ftruncate(fd, size);
+ } while (ret < 0 && errno == EINTR);
+ if (ret < 0) {
+ close(fd);
+ return -1;
+ }
+#endif
+
+ return fd;
+}
diff --git a/xwayland/xwayland.c b/xwayland/xwayland.c
index fe09ea5e..d92d58fb 100644
--- a/xwayland/xwayland.c
+++ b/xwayland/xwayland.c
@@ -105,17 +105,15 @@ static void exec_xwayland(struct wlr_xwayland *wlr_xwayland) {
const char *xdg_runtime = getenv("XDG_RUNTIME_DIR");
const char *path_var = getenv("PATH");
- if (!xdg_runtime) {
- wlr_log(WLR_ERROR, "XDG_RUNTIME_DIR is not set");
- _exit(EXIT_FAILURE);
- }
-
- if (clearenv()) {
+ if (clearenv() != 0) {
wlr_log_errno(WLR_ERROR, "clearenv failed");
_exit(EXIT_FAILURE);
}
- setenv("XDG_RUNTIME_DIR", xdg_runtime, true);
+ if (xdg_runtime != NULL) {
+ setenv("XDG_RUNTIME_DIR", xdg_runtime, true);
+ }
setenv("PATH", path_var, true);
+
char wayland_socket_str[16];
snprintf(wayland_socket_str, sizeof(wayland_socket_str), "%d", wlr_xwayland->wl_fd[1]);
setenv("WAYLAND_SOCKET", wayland_socket_str, true);
diff --git a/xwayland/xwm.c b/xwayland/xwm.c
index 9c803543..c61c3ced 100644
--- a/xwayland/xwm.c
+++ b/xwayland/xwm.c
@@ -12,7 +12,6 @@
#include <wlr/xwayland.h>
#include <xcb/composite.h>
#include <xcb/render.h>
-#include <xcb/xcb_image.h>
#include <xcb/xfixes.h>
#include "util/signal.h"
#include "xwayland/xwm.h"
@@ -29,7 +28,7 @@ const char *atom_map[ATOM_LAST] = {
"UTF8_STRING",
"WM_S0",
"_NET_SUPPORTED",
- "_NET_WM_S0",
+ "_NET_WM_CM_S0",
"_NET_WM_PID",
"_NET_WM_NAME",
"_NET_WM_STATE",
@@ -159,6 +158,7 @@ static struct wlr_xwayland_surface *xwayland_surface_create(
wl_signal_init(&surface->events.set_pid);
wl_signal_init(&surface->events.set_window_type);
wl_signal_init(&surface->events.set_hints);
+ wl_signal_init(&surface->events.set_decorations);
wl_signal_init(&surface->events.set_override_redirect);
wl_signal_init(&surface->events.ping_timeout);
@@ -313,6 +313,7 @@ static void xwayland_surface_destroy(
wl_list_for_each_safe(child, next, &xsurface->children, parent_link) {
wl_list_remove(&child->parent_link);
wl_list_init(&child->parent_link);
+ child->parent = NULL;
}
if (xsurface->surface_id) {
@@ -522,6 +523,12 @@ static void read_surface_hints(struct wlr_xwm *xwm,
memcpy(xsurface->hints, &hints, sizeof(struct wlr_xwayland_surface_hints));
xsurface->hints_urgency = xcb_icccm_wm_hints_get_urgency(&hints);
+ if (!(xsurface->hints->flags & XCB_ICCCM_WM_HINT_INPUT)) {
+ // The client didn't specify whether it wants input.
+ // Assume it does.
+ xsurface->hints->input = true;
+ }
+
wlr_log(WLR_DEBUG, "WM_HINTS (%d)", reply->value_len);
wlr_signal_emit_safe(&xsurface->events.set_hints, xsurface);
}
@@ -553,6 +560,15 @@ static void read_surface_normal_hints(struct wlr_xwm *xwm,
memcpy(xsurface->size_hints, &size_hints,
sizeof(struct wlr_xwayland_surface_size_hints));
+ if ((size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE) == 0) {
+ xsurface->size_hints->min_width = -1;
+ xsurface->size_hints->min_height = -1;
+ }
+ if ((size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE) == 0) {
+ xsurface->size_hints->max_width = -1;
+ xsurface->size_hints->max_height = -1;
+ }
+
wlr_log(WLR_DEBUG, "WM_NORMAL_HINTS (%d)", reply->value_len);
}
#else
@@ -594,6 +610,7 @@ static void read_surface_motif_hints(struct wlr_xwm *xwm,
WLR_XWAYLAND_SURFACE_DECORATIONS_NO_TITLE;
}
}
+ wlr_signal_emit_safe(&xsurface->events.set_decorations, xsurface);
}
wlr_log(WLR_DEBUG, "MOTIF_WM_HINTS (%d)", reply->value_len);
@@ -796,8 +813,6 @@ static void xwm_handle_destroy_notify(struct wlr_xwm *xwm,
static void xwm_handle_configure_request(struct wlr_xwm *xwm,
xcb_configure_request_event_t *ev) {
- wlr_log(WLR_DEBUG, "XCB_CONFIGURE_REQUEST (%u) [%ux%u+%d,%d]", ev->window,
- ev->width, ev->height, ev->x, ev->y);
struct wlr_xwayland_surface *surface = lookup_surface(xwm, ev->window);
if (surface == NULL) {
return;
@@ -805,13 +820,22 @@ static void xwm_handle_configure_request(struct wlr_xwm *xwm,
// TODO: handle ev->{parent,sibling}?
+ uint16_t mask = ev->value_mask;
+ uint16_t geo_mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y |
+ XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
+ if ((mask & geo_mask) == 0) {
+ return;
+ }
+
struct wlr_xwayland_surface_configure_event wlr_event = {
.surface = surface,
- .x = ev->x,
- .y = ev->y,
- .width = ev->width,
- .height = ev->height,
+ .x = mask & XCB_CONFIG_WINDOW_X ? ev->x : surface->x,
+ .y = mask & XCB_CONFIG_WINDOW_Y ? ev->y : surface->y,
+ .width = mask & XCB_CONFIG_WINDOW_WIDTH ? ev->width : surface->width,
+ .height = mask & XCB_CONFIG_WINDOW_HEIGHT ? ev->height : surface->height,
};
+ wlr_log(WLR_DEBUG, "XCB_CONFIGURE_REQUEST (%u) [%ux%u+%d,%d]", ev->window,
+ wlr_event.width, wlr_event.height, wlr_event.x, wlr_event.y);
wlr_signal_emit_safe(&surface->events.request_configure, &wlr_event);
}
@@ -1508,7 +1532,7 @@ static void xwm_create_wm_window(struct wlr_xwm *xwm) {
xcb_set_selection_owner(xwm->xcb_conn,
xwm->window,
- xwm->atoms[NET_WM_S0],
+ xwm->atoms[NET_WM_CM_S0],
XCB_CURRENT_TIME);
}