git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

commit502b54807459a797411f14f19b2ecaa441a61da3
parentf1c1eac3cc
authorLucas Galante <[email protected]>
date2026-06-14 16:26
Reorganize project structure: promote client and server to root modules

 cce-client/.gitignore => .gitignore                |    3 +-
 cce-server/Cargo.lock => Cargo.lock                |  460 ++++-
 Cargo.toml                                         |   40 +-
 Makefile                                           |   18 +
 cce-server/build.rs => build.rs                    |   15 +-
 cce-client/Cargo.lock                              |  574 ------
 cce-client/Cargo.toml                              |   31 -
 cce-client/Makefile                                |   28 -
 cce-client/build.rs                                |    7 -
 cce-client/test_proto                              |  Bin 439040 -> 0 bytes
 cce-server/.gitignore                              |    3 -
 cce-server/Cargo.toml                              |   20 -
 cce-server/Makefile                                |   22 -
 cce-server/protocol/clear-inspector-v1.xml         |   54 -
 cce-server/protocol/river-input-management-v1.xml  |  244 ---
 cce-server/protocol/river-layer-shell-v1.xml       |  191 --
 cce-server/protocol/river-libinput-config-v1.xml   |  901 ----------
 cce-server/protocol/river-window-management-v1.xml | 1897 --------------------
 cce-server/protocol/river-xkb-bindings-v1.xml      |  314 ----
 cce-server/protocol/river-xkb-config-v1.xml        |  287 ---
 .../protocol => protocol}/clear-inspector-v1.xml   |    0
 .../river-input-management-v1.xml                  |    0
 .../protocol => protocol}/river-layer-shell-v1.xml |    0
 .../river-libinput-config-v1.xml                   |    0
 .../river-window-management-v1.xml                 |    0
 .../river-xkb-bindings-v1.xml                      |    0
 .../protocol => protocol}/river-xkb-config-v1.xml  |    0
 .../upstream/virtual-keyboard-unstable-v1.xml      |    0
 .../upstream/wlr-layer-shell-unstable-v1.xml       |    0
 .../wlr-output-power-management-unstable-v1.xml    |    0
 .../wlr-output-management-unstable-v1.xml          |    0
 scratch/print_c_sizes                              |  Bin 0 -> 15976 bytes
 scratch/print_c_sizes.c                            |   38 +
 scratch/print_sizes.rs                             |    4 +
 scratch/test_bindgen.rs                            |   39 +
 src/bin/cce.rs                                     |   40 +
 {cce-client/src => src/client}/borders.rs          |    0
 .../client/clear_inspector.rs                      |   10 +-
 {cce-client/src => src/client}/clearctl.rs         |    3 +-
 {cce-client/src => src/client}/config.rs           |    0
 {cce-client/src => src/client}/decorations.rs      |    0
 {cce-client/src => src/client}/input.rs            |    0
 {cce-client/src => src/client}/ipc.rs              |    0
 {cce-client/src => src/client}/ipc_server.rs       |    0
 {cce-client/src => src/client}/lib.rs              |    3 +-
 {cce-client/src => src/client}/paths.rs            |    0
 {cce-client/src => src/client}/protocol.rs         |    0
 {cce-client/src => src/client}/restart.rs          |    0
 cce-client/src/main.rs => src/client/run.rs        |   30 +-
 {cce-client/src => src/client}/state.rs            |    0
 {cce-client/src => src/client}/status.rs           |    0
 {cce-client/src => src/client}/status_server.rs    |    0
 {cce-client/src => src/client}/tiling.rs           |    0
 {cce-client/src => src/client}/types.rs            |    0
 {cce-client/src => src/client}/wayland.rs          |    0
 {cce-client/src => src/client}/wm.rs               |    0
 src/lib.rs                                         |  141 ++
 {cce-server/src => src/server}/cursor.rs           |    0
 {cce-server/src => src/server}/drag_icon.rs        |    0
 {cce-server/src => src/server}/ffi.rs              |   41 +
 .../src => src/server}/idle_inhibit_manager.rs     |    0
 {cce-server/src => src/server}/input_device.rs     |    0
 {cce-server/src => src/server}/input_manager.rs    |    0
 {cce-server/src => src/server}/input_popup.rs      |    0
 {cce-server/src => src/server}/input_relay.rs      |    0
 {cce-server/src => src/server}/inspector.rs        |    0
 {cce-server/src => src/server}/keyboard.rs         |    0
 {cce-server/src => src/server}/keyboard_group.rs   |    0
 {cce-server/src => src/server}/layer_shell.rs      |    0
 .../src => src/server}/libinput_accel_config.rs    |    0
 {cce-server/src => src/server}/libinput_config.rs  |    0
 {cce-server/src => src/server}/libinput_device.rs  |  120 +-
 {cce-server/src => src/server}/lock_manager.rs     |    0
 {cce-server/src => src/server}/output.rs           |    0
 {cce-server/src => src/server}/output_manager.rs   |    0
 {cce-server/src => src/server}/pointer_binding.rs  |    0
 .../src => src/server}/pointer_constraint.rs       |    0
 {cce-server/src => src/server}/process.rs          |    0
 cce-server/src/main.rs => src/server/run_server.rs |   61 +-
 {cce-server/src => src/server}/scene.rs            |    0
 {cce-server/src => src/server}/scene_node_data.rs  |    0
 {cce-server/src => src/server}/seat.rs             |    0
 {cce-server/src => src/server}/server.rs           |    0
 {cce-server/src => src/server}/shell_surface.rs    |    0
 {cce-server/src => src/server}/slotmap.rs          |    0
 {cce-server/src => src/server}/tablet.rs           |    0
 {cce-server/src => src/server}/tablet_tool.rs      |    0
 {cce-server/src => src/server}/text_input.rs       |    0
 {cce-server/src => src/server}/util.rs             |    0
 {cce-server/src => src/server}/window.rs           |   17 +-
 {cce-server/src => src/server}/window_manager.rs   |    0
 .../src => src/server}/wlroots_log_wrapper.c       |    0
 {cce-server/src => src/server}/wm_node.rs          |    0
 {cce-server/src => src/server}/xdg_popup.rs        |    0
 {cce-server/src => src/server}/xdg_toplevel.rs     |    5 +-
 {cce-server/src => src/server}/xkb_bindings.rs     |    0
 {cce-server/src => src/server}/xkb_config.rs       |    0
 {cce-server/src => src/server}/xkb_keyboard.rs     |    0
 .../server}/xwayland_override_redirect.rs          |    0
 {cce-server/src => src/server}/xwayland_window.rs  |    0
 cce-client/start-river.sh => start-river.sh        |    4 +-
 cce-server/start-rust.sh => start-rust.sh          |   18 +-
 cce-server/wrapper.h => wrapper.h                  |    0
 103 files changed, 973 insertions(+), 4710 deletions(-)

diff --git a/cce-client/.gitignore b/.gitignore
similarity index 59%
rename from cce-client/.gitignore
rename to .gitignore
index 95c7048..00420f1 100644
--- a/cce-client/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
 /target
 .antigravitycli/
 .gemini/
-
+*.log
+check_errors.txt
diff --git a/cce-server/Cargo.lock b/Cargo.lock
similarity index 58%
rename from cce-server/Cargo.lock
rename to Cargo.lock
index a0b1734..2378892 100644
--- a/cce-server/Cargo.lock
+++ b/Cargo.lock
@@ -11,6 +11,12 @@ dependencies = [
  "memchr",
 ]
 
+[[package]]
+name = "allocator-api2"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
+
 [[package]]
 name = "anstream"
 version = "1.0.0"
@@ -79,29 +85,35 @@ dependencies = [
  "quote",
  "regex",
  "rustc-hash",
- "shlex",
+ "shlex 1.3.0",
  "syn",
  "which",
 ]
 
 [[package]]
 name = "bitflags"
-version = "2.11.1"
+version = "2.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
+
+[[package]]
+name = "bytes"
+version = "1.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
+checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
 
 [[package]]
 name = "cc"
-version = "1.2.62"
+version = "1.2.64"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98"
+checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f"
 dependencies = [
  "find-msvc-tools",
- "shlex",
+ "shlex 2.0.1",
 ]
 
 [[package]]
-name = "cce-server"
+name = "cce"
 version = "0.1.0"
 dependencies = [
  "bindgen",
@@ -109,10 +121,20 @@ dependencies = [
  "cc",
  "clap",
  "env_logger",
+ "fontdue",
  "libc",
  "log",
  "nix",
  "pkg-config",
+ "serde",
+ "serde_json",
+ "tokio",
+ "toml",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols",
+ "wayland-scanner",
+ "xkbcommon",
 ]
 
 [[package]]
@@ -193,6 +215,12 @@ version = "1.0.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
 
+[[package]]
+name = "downcast-rs"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
+
 [[package]]
 name = "either"
 version = "1.16.0"
@@ -222,6 +250,12 @@ dependencies = [
  "log",
 ]
 
+[[package]]
+name = "equivalent"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+
 [[package]]
 name = "errno"
 version = "0.3.14"
@@ -238,12 +272,45 @@ version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
 
+[[package]]
+name = "foldhash"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
+
+[[package]]
+name = "fontdue"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e57e16b3fe8ff4364c0661fdaac543fb38b29ea9bc9c2f45612d90adf931d2b"
+dependencies = [
+ "hashbrown 0.15.5",
+ "ttf-parser",
+]
+
 [[package]]
 name = "glob"
 version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
 
+[[package]]
+name = "hashbrown"
+version = "0.15.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
+dependencies = [
+ "allocator-api2",
+ "equivalent",
+ "foldhash",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
+
 [[package]]
 name = "heck"
 version = "0.5.0"
@@ -259,6 +326,16 @@ dependencies = [
  "windows-sys 0.61.2",
 ]
 
+[[package]]
+name = "indexmap"
+version = "2.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.17.1",
+]
+
 [[package]]
 name = "is_terminal_polyfill"
 version = "1.70.2"
@@ -274,11 +351,17 @@ dependencies = [
  "either",
 ]
 
+[[package]]
+name = "itoa"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
+
 [[package]]
 name = "jiff"
-version = "0.2.27"
+version = "0.2.28"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "392c70591e8749fe235ddaf513e6f58b26bce3dcc16524cecc8936f75afa161e"
+checksum = "4603d3033e49e2b0e31229fcab20a5d40089c607d975cd9c80551dc69eed9102"
 dependencies = [
  "jiff-static",
  "log",
@@ -289,9 +372,9 @@ dependencies = [
 
 [[package]]
 name = "jiff-static"
-version = "0.2.27"
+version = "0.2.28"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "47b605b0c050d845fc355bb11eb3f9a8deddc218ea60c76e61aa1f2adfb2c96a"
+checksum = "782d32378dddf207193ac91cefb848ad41abb58195c95168e1291227a0832b47"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -332,17 +415,41 @@ version = "0.4.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
 
+[[package]]
+name = "linux-raw-sys"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
+
+[[package]]
+name = "lock_api"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
+dependencies = [
+ "scopeguard",
+]
+
 [[package]]
 name = "log"
-version = "0.4.30"
+version = "0.4.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5"
+checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a"
 
 [[package]]
 name = "memchr"
-version = "2.8.1"
+version = "2.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8"
+checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
+
+[[package]]
+name = "memmap2"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed"
+dependencies = [
+ "libc",
+]
 
 [[package]]
 name = "minimal-lexical"
@@ -350,6 +457,17 @@ version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
 
+[[package]]
+name = "mio"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda"
+dependencies = [
+ "libc",
+ "wasi",
+ "windows-sys 0.61.2",
+]
+
 [[package]]
 name = "nix"
 version = "0.29.0"
@@ -384,6 +502,35 @@ version = "1.70.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
 
+[[package]]
+name = "parking_lot"
+version = "0.12.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-link",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
+
 [[package]]
 name = "pkg-config"
 version = "0.3.33"
@@ -424,6 +571,15 @@ dependencies = [
  "unicode-ident",
 ]
 
+[[package]]
+name = "quick-xml"
+version = "0.39.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e"
+dependencies = [
+ "memchr",
+]
+
 [[package]]
 name = "quote"
 version = "1.0.45"
@@ -433,11 +589,20 @@ dependencies = [
  "proc-macro2",
 ]
 
+[[package]]
+name = "redox_syscall"
+version = "0.5.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
+dependencies = [
+ "bitflags",
+]
+
 [[package]]
 name = "regex"
-version = "1.12.3"
+version = "1.12.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
+checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -458,9 +623,9 @@ dependencies = [
 
 [[package]]
 name = "regex-syntax"
-version = "0.8.10"
+version = "0.8.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
+checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
 
 [[package]]
 name = "rustc-hash"
@@ -477,10 +642,39 @@ dependencies = [
  "bitflags",
  "errno",
  "libc",
- "linux-raw-sys",
+ "linux-raw-sys 0.4.15",
  "windows-sys 0.59.0",
 ]
 
+[[package]]
+name = "rustix"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
+dependencies = [
+ "bitflags",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.12.1",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "serde"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+dependencies = [
+ "serde_core",
+ "serde_derive",
+]
+
 [[package]]
 name = "serde_core"
 version = "1.0.228"
@@ -501,12 +695,66 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "serde_json"
+version = "1.0.150"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
+dependencies = [
+ "itoa",
+ "memchr",
+ "serde",
+ "serde_core",
+ "zmij",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "0.6.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
+dependencies = [
+ "serde",
+]
+
 [[package]]
 name = "shlex"
 version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
 
+[[package]]
+name = "shlex"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
+dependencies = [
+ "errno",
+ "libc",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.15.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
+
+[[package]]
+name = "socket2"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51"
+dependencies = [
+ "libc",
+ "windows-sys 0.61.2",
+]
+
 [[package]]
 name = "strsim"
 version = "0.11.1"
@@ -524,6 +772,81 @@ dependencies = [
  "unicode-ident",
 ]
 
+[[package]]
+name = "tokio"
+version = "1.52.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe"
+dependencies = [
+ "bytes",
+ "libc",
+ "mio",
+ "parking_lot",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "socket2",
+ "tokio-macros",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "toml"
+version = "0.8.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
+dependencies = [
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_edit",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "0.6.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.22.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
+dependencies = [
+ "indexmap",
+ "serde",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_write",
+ "winnow",
+]
+
+[[package]]
+name = "toml_write"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
+
+[[package]]
+name = "ttf-parser"
+version = "0.21.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8"
+
 [[package]]
 name = "unicode-ident"
 version = "1.0.24"
@@ -536,6 +859,69 @@ version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
 
+[[package]]
+name = "wasi"
+version = "0.11.1+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
+
+[[package]]
+name = "wayland-backend"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d"
+dependencies = [
+ "cc",
+ "downcast-rs",
+ "rustix 1.1.4",
+ "smallvec",
+ "wayland-sys",
+]
+
+[[package]]
+name = "wayland-client"
+version = "0.31.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144"
+dependencies = [
+ "bitflags",
+ "rustix 1.1.4",
+ "wayland-backend",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-protocols"
+version = "0.32.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "563a85523cade2429938e790815fd7319062103b9f4a2dc806e9b53b95982d8f"
+dependencies = [
+ "bitflags",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-scanner"
+version = "0.31.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a"
+dependencies = [
+ "proc-macro2",
+ "quick-xml",
+ "quote",
+]
+
+[[package]]
+name = "wayland-sys"
+version = "0.31.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be"
+dependencies = [
+ "pkg-config",
+]
+
 [[package]]
 name = "which"
 version = "4.4.2"
@@ -545,7 +931,7 @@ dependencies = [
  "either",
  "home",
  "once_cell",
- "rustix",
+ "rustix 0.38.44",
 ]
 
 [[package]]
@@ -635,3 +1021,35 @@ name = "windows_x86_64_msvc"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "winnow"
+version = "0.7.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "xkbcommon"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13867d259930edc7091a6c41b4ce6eee464328c6ff9659b7e4c668ca20d4c91e"
+dependencies = [
+ "libc",
+ "memmap2",
+ "xkeysym",
+]
+
+[[package]]
+name = "xkeysym"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"
+
+[[package]]
+name = "zmij"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
diff --git a/Cargo.toml b/Cargo.toml
index 7b869ae..62cd018 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,36 @@
+[package]
+name = "cce"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+wayland-client = "0.31"
+wayland-backend = "0.3"
+wayland-protocols = { version = "0.32", features = ["client", "unstable", "staging"] }
+wayland-scanner = "0.31"
+xkbcommon = "0.7"
+toml = "0.8"
+serde = { version = "1", features = ["derive"] }
+nix = { version = "0.29", features = ["signal", "process", "fs", "poll", "resource"] }
+libc = "0.2"
+bitflags = "2"
+tokio = { version = "1.35", features = ["full"] }
+serde_json = "1.0"
+fontdue = "0.9.3"
+clap = { version = "4.4", features = ["derive"] }
+log = "0.4"
+env_logger = "0.11"
+
+[build-dependencies]
+bindgen = "0.69"
+pkg-config = "0.3"
+cc = "1.0"
+
+[[bin]]
+name = "cce"
+path = "src/bin/cce.rs"
+
+[profile.release]
+debug = true
+
 [workspace]
-resolver = "2"
-members = [
-    "cce-client",
-    "cce-server",
-]
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..338b6f7
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,18 @@
+.PHONY: build install run clean
+
+build:
+	cargo build --release
+
+install: build
+	mkdir -p ~/.local/bin
+	@if [ -f target/release/cce ]; then \
+		install -m 755 target/release/cce ~/.local/bin/cce; \
+	else \
+		echo "Error: cce binary not found"; exit 1; \
+	fi
+
+run:
+	cargo run --bin cce
+
+clean:
+	cargo clean
diff --git a/cce-server/build.rs b/build.rs
similarity index 91%
rename from cce-server/build.rs
rename to build.rs
index 5c94b3f..5d8e1fe 100644
--- a/cce-server/build.rs
+++ b/build.rs
@@ -2,9 +2,12 @@ use std::env;
 use std::path::PathBuf;
 
 fn main() {
-    println!("cargo:rerun-if-changed=src/wlroots_log_wrapper.c");
+    println!("cargo:rerun-if-changed=src/server/wlroots_log_wrapper.c");
     println!("cargo:rerun-if-changed=wrapper.h");
     println!("cargo:rerun-if-changed=protocol/river-window-management-v1.xml");
+    println!("cargo:rerun-if-changed=protocol/river-xkb-bindings-v1.xml");
+    println!("cargo:rerun-if-changed=protocol/river-layer-shell-v1.xml");
+    println!("cargo:rerun-if-changed=protocol/river-input-management-v1.xml");
 
     // Probe system libraries
     let scenefx = pkg_config::Config::new()
@@ -120,7 +123,7 @@ fn main() {
 
     // Compile the C wrapper and protocol C files
     let mut build = cc::Build::new();
-    build.file("src/wlroots_log_wrapper.c")
+    build.file("src/server/wlroots_log_wrapper.c")
         .define("WLR_USE_UNSTABLE", None)
         .flag("-std=c99")
         .flag("-O2")
@@ -173,6 +176,14 @@ fn main() {
         .blocklist_item("wl_listener")
         .blocklist_item("wlr_addon")
         .blocklist_item("wlr_input_device")
+        .blocklist_item("pixman_region32")
+        .blocklist_item("pixman_region32_t")
+        .blocklist_item("pixman_box32")
+        .blocklist_item("pixman_box32_t")
+        .blocklist_item("pixman_rectangle32")
+        .blocklist_item("pixman_rectangle32_t")
+        .blocklist_item("pixman_region32_data")
+        .blocklist_item("pixman_region32_data_t")
         .generate()
         .expect("Unable to generate bindings");
 
diff --git a/cce-client/Cargo.lock b/cce-client/Cargo.lock
deleted file mode 100644
index 9f7ae0e..0000000
--- a/cce-client/Cargo.lock
+++ /dev/null
@@ -1,574 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 4
-
-[[package]]
-name = "allocator-api2"
-version = "0.2.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
-
-[[package]]
-name = "bitflags"
-version = "2.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
-
-[[package]]
-name = "bytes"
-version = "1.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
-
-[[package]]
-name = "cc"
-version = "1.2.62"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98"
-dependencies = [
- "find-msvc-tools",
- "shlex",
-]
-
-[[package]]
-name = "cce-client"
-version = "0.1.0"
-dependencies = [
- "bitflags",
- "fontdue",
- "libc",
- "nix",
- "serde",
- "serde_json",
- "tokio",
- "toml",
- "wayland-backend",
- "wayland-client",
- "wayland-protocols",
- "wayland-scanner",
- "xkbcommon",
-]
-
-[[package]]
-name = "cfg-if"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
-
-[[package]]
-name = "cfg_aliases"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
-
-[[package]]
-name = "downcast-rs"
-version = "1.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
-
-[[package]]
-name = "equivalent"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
-
-[[package]]
-name = "errno"
-version = "0.3.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
-dependencies = [
- "libc",
- "windows-sys",
-]
-
-[[package]]
-name = "find-msvc-tools"
-version = "0.1.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
-
-[[package]]
-name = "foldhash"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
-
-[[package]]
-name = "fontdue"
-version = "0.9.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e57e16b3fe8ff4364c0661fdaac543fb38b29ea9bc9c2f45612d90adf931d2b"
-dependencies = [
- "hashbrown 0.15.5",
- "ttf-parser",
-]
-
-[[package]]
-name = "hashbrown"
-version = "0.15.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
-dependencies = [
- "allocator-api2",
- "equivalent",
- "foldhash",
-]
-
-[[package]]
-name = "hashbrown"
-version = "0.17.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
-
-[[package]]
-name = "indexmap"
-version = "2.14.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
-dependencies = [
- "equivalent",
- "hashbrown 0.17.1",
-]
-
-[[package]]
-name = "itoa"
-version = "1.0.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
-
-[[package]]
-name = "libc"
-version = "0.2.186"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
-
-[[package]]
-name = "linux-raw-sys"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
-
-[[package]]
-name = "lock_api"
-version = "0.4.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
-dependencies = [
- "scopeguard",
-]
-
-[[package]]
-name = "memchr"
-version = "2.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
-
-[[package]]
-name = "memmap2"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "mio"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1"
-dependencies = [
- "libc",
- "wasi",
- "windows-sys",
-]
-
-[[package]]
-name = "nix"
-version = "0.29.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
-dependencies = [
- "bitflags",
- "cfg-if",
- "cfg_aliases",
- "libc",
-]
-
-[[package]]
-name = "parking_lot"
-version = "0.12.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
-dependencies = [
- "lock_api",
- "parking_lot_core",
-]
-
-[[package]]
-name = "parking_lot_core"
-version = "0.9.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
-dependencies = [
- "cfg-if",
- "libc",
- "redox_syscall",
- "smallvec",
- "windows-link",
-]
-
-[[package]]
-name = "pin-project-lite"
-version = "0.2.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
-
-[[package]]
-name = "pkg-config"
-version = "0.3.33"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
-
-[[package]]
-name = "proc-macro2"
-version = "1.0.106"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
-dependencies = [
- "unicode-ident",
-]
-
-[[package]]
-name = "quick-xml"
-version = "0.39.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "quote"
-version = "1.0.45"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
-dependencies = [
- "proc-macro2",
-]
-
-[[package]]
-name = "redox_syscall"
-version = "0.5.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
-dependencies = [
- "bitflags",
-]
-
-[[package]]
-name = "rustix"
-version = "1.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
-dependencies = [
- "bitflags",
- "errno",
- "libc",
- "linux-raw-sys",
- "windows-sys",
-]
-
-[[package]]
-name = "scopeguard"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
-
-[[package]]
-name = "serde"
-version = "1.0.228"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
-dependencies = [
- "serde_core",
- "serde_derive",
-]
-
-[[package]]
-name = "serde_core"
-version = "1.0.228"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
-dependencies = [
- "serde_derive",
-]
-
-[[package]]
-name = "serde_derive"
-version = "1.0.228"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "serde_json"
-version = "1.0.150"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
-dependencies = [
- "itoa",
- "memchr",
- "serde",
- "serde_core",
- "zmij",
-]
-
-[[package]]
-name = "serde_spanned"
-version = "0.6.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "shlex"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
-
-[[package]]
-name = "signal-hook-registry"
-version = "1.4.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
-dependencies = [
- "errno",
- "libc",
-]
-
-[[package]]
-name = "smallvec"
-version = "1.15.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
-
-[[package]]
-name = "socket2"
-version = "0.6.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
-dependencies = [
- "libc",
- "windows-sys",
-]
-
-[[package]]
-name = "syn"
-version = "2.0.117"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
-[[package]]
-name = "tokio"
-version = "1.52.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe"
-dependencies = [
- "bytes",
- "libc",
- "mio",
- "parking_lot",
- "pin-project-lite",
- "signal-hook-registry",
- "socket2",
- "tokio-macros",
- "windows-sys",
-]
-
-[[package]]
-name = "tokio-macros"
-version = "2.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "toml"
-version = "0.8.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
-dependencies = [
- "serde",
- "serde_spanned",
- "toml_datetime",
- "toml_edit",
-]
-
-[[package]]
-name = "toml_datetime"
-version = "0.6.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "toml_edit"
-version = "0.22.27"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
-dependencies = [
- "indexmap",
- "serde",
- "serde_spanned",
- "toml_datetime",
- "toml_write",
- "winnow",
-]
-
-[[package]]
-name = "toml_write"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
-
-[[package]]
-name = "ttf-parser"
-version = "0.21.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8"
-
-[[package]]
-name = "unicode-ident"
-version = "1.0.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
-
-[[package]]
-name = "wasi"
-version = "0.11.1+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
-
-[[package]]
-name = "wayland-backend"
-version = "0.3.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d"
-dependencies = [
- "cc",
- "downcast-rs",
- "rustix",
- "smallvec",
- "wayland-sys",
-]
-
-[[package]]
-name = "wayland-client"
-version = "0.31.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144"
-dependencies = [
- "bitflags",
- "rustix",
- "wayland-backend",
- "wayland-scanner",
-]
-
-[[package]]
-name = "wayland-protocols"
-version = "0.32.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "563a85523cade2429938e790815fd7319062103b9f4a2dc806e9b53b95982d8f"
-dependencies = [
- "bitflags",
- "wayland-backend",
- "wayland-client",
- "wayland-scanner",
-]
-
-[[package]]
-name = "wayland-scanner"
-version = "0.31.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a"
-dependencies = [
- "proc-macro2",
- "quick-xml",
- "quote",
-]
-
-[[package]]
-name = "wayland-sys"
-version = "0.31.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be"
-dependencies = [
- "pkg-config",
-]
-
-[[package]]
-name = "windows-link"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
-
-[[package]]
-name = "windows-sys"
-version = "0.61.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
-dependencies = [
- "windows-link",
-]
-
-[[package]]
-name = "winnow"
-version = "0.7.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "xkbcommon"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13867d259930edc7091a6c41b4ce6eee464328c6ff9659b7e4c668ca20d4c91e"
-dependencies = [
- "libc",
- "memmap2",
- "xkeysym",
-]
-
-[[package]]
-name = "xkeysym"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"
-
-[[package]]
-name = "zmij"
-version = "1.0.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
diff --git a/cce-client/Cargo.toml b/cce-client/Cargo.toml
deleted file mode 100644
index 2a1be1b..0000000
--- a/cce-client/Cargo.toml
+++ /dev/null
@@ -1,31 +0,0 @@
-[package]
-name = "cce-client"
-version = "0.1.0"
-edition = "2021"
-
-[[bin]]
-name = "cce-client"
-path = "src/main.rs"
-
-[[bin]]
-name = "clearctl"
-path = "src/clearctl.rs"
-
-[[bin]]
-name = "clear-inspector"
-path = "src/inspector_cli.rs"
-
-[dependencies]
-wayland-client = "0.31"
-wayland-backend = "0.3"
-wayland-protocols = { version = "0.32", features = ["client", "unstable", "staging"] }
-wayland-scanner = "0.31"
-xkbcommon = "0.7"
-toml = "0.8"
-serde = { version = "1", features = ["derive"] }
-nix = { version = "0.29", features = ["signal", "process", "fs", "poll"] }
-libc = "0.2"
-bitflags = "2"
-tokio = { version = "1.35", features = ["full"] }
-serde_json = "1.0"
-fontdue = "0.9.3"
diff --git a/cce-client/Makefile b/cce-client/Makefile
deleted file mode 100644
index 050d604..0000000
--- a/cce-client/Makefile
+++ /dev/null
@@ -1,28 +0,0 @@
-.PHONY: build install run clean
-
-build:
-	cargo build --release
-
-install: build
-	mkdir -p ~/.local/bin
-	@if [ -f target/release/cce-client ]; then \
-		install -m 755 target/release/cce-client ~/.local/bin/cce-client; \
-		install -m 755 target/release/clearctl ~/.local/bin/clearctl; \
-		install -m 755 target/release/clear-inspector ~/.local/bin/clear-inspector; \
-	elif [ -f ../target/release/cce-client ]; then \
-		install -m 755 ../target/release/cce-client ~/.local/bin/cce-client; \
-		install -m 755 ../target/release/clearctl ~/.local/bin/clearctl; \
-		install -m 755 ../target/release/clear-inspector ~/.local/bin/clear-inspector; \
-	elif [ -f ../../target/release/cce-client ]; then \
-		install -m 755 ../../target/release/cce-client ~/.local/bin/cce-client; \
-		install -m 755 ../../target/release/clearctl ~/.local/bin/clearctl; \
-		install -m 755 ../../target/release/clear-inspector ~/.local/bin/clear-inspector; \
-	else \
-		echo "Error: cce-client binaries not found"; exit 1; \
-	fi
-
-run:
-	cargo run
-
-clean:
-	cargo clean
diff --git a/cce-client/build.rs b/cce-client/build.rs
deleted file mode 100644
index 1031b05..0000000
--- a/cce-client/build.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-fn main() {
-    // Track protocol XML files for rebuild
-    println!("cargo:rerun-if-changed=protocol/river-window-management-v1.xml");
-    println!("cargo:rerun-if-changed=protocol/river-xkb-bindings-v1.xml");
-    println!("cargo:rerun-if-changed=protocol/river-layer-shell-v1.xml");
-    println!("cargo:rerun-if-changed=protocol/river-input-management-v1.xml");
-}
diff --git a/cce-client/test_proto b/cce-client/test_proto
deleted file mode 100755
index c7eed87..0000000
Binary files a/cce-client/test_proto and /dev/null differ
diff --git a/cce-server/.gitignore b/cce-server/.gitignore
deleted file mode 100644
index b1df094..0000000
--- a/cce-server/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-/target
-*.log
-check_errors.txt
diff --git a/cce-server/Cargo.toml b/cce-server/Cargo.toml
deleted file mode 100644
index 63fc739..0000000
--- a/cce-server/Cargo.toml
+++ /dev/null
@@ -1,20 +0,0 @@
-[package]
-name = "cce-server"
-version = "0.1.0"
-edition = "2021"
-
-[dependencies]
-libc = "0.2"
-nix = { version = "0.29", features = ["signal", "process", "fs", "poll", "resource"] }
-bitflags = "2"
-clap = { version = "4.4", features = ["derive"] }
-log = "0.4"
-env_logger = "0.11"
-
-[build-dependencies]
-bindgen = "0.69"
-pkg-config = "0.3"
-cc = "1.0"
-
-[profile.release]
-debug = true
diff --git a/cce-server/Makefile b/cce-server/Makefile
deleted file mode 100644
index ef745a2..0000000
--- a/cce-server/Makefile
+++ /dev/null
@@ -1,22 +0,0 @@
-.PHONY: build install run clean
-
-build:
-	cargo build --release
-
-install: build
-	mkdir -p ~/.local/bin
-	@if [ -f target/release/cce-server ]; then \
-		install -m 755 target/release/cce-server ~/.local/bin/cce-server; \
-	elif [ -f ../target/release/cce-server ]; then \
-		install -m 755 ../target/release/cce-server ~/.local/bin/cce-server; \
-	elif [ -f ../../target/release/cce-server ]; then \
-		install -m 755 ../../target/release/cce-server ~/.local/bin/cce-server; \
-	else \
-		echo "Error: cce-server binary not found"; exit 1; \
-	fi
-
-run:
-	cargo run
-
-clean:
-	cargo clean
diff --git a/cce-server/protocol/clear-inspector-v1.xml b/cce-server/protocol/clear-inspector-v1.xml
deleted file mode 100644
index 4dca611..0000000
--- a/cce-server/protocol/clear-inspector-v1.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<protocol name="clear_inspector_v1">
-  <copyright>
-    Copyright © 2026 Antigravity
-  </copyright>
-
-  <interface name="zclear_inspector_v1" version="1">
-    <description summary="query information about client surface states and layouts">
-      This interface allows clear-ui client applications to register their surfaces
-      and publish their internal widget trees and state to the compositor.
-      It also allows inspector tools to request the list of active surfaces and their states.
-    </description>
-
-    <request name="destroy" type="destructor">
-      <description summary="destroy the inspector object"/>
-    </request>
-
-    <request name="register_client">
-      <description summary="register a wl_surface as a clear-ui client window">
-        Associate a client wl_surface with this inspector.
-      </description>
-      <arg name="surface" type="object" interface="wl_surface"/>
-    </request>
-
-    <request name="update_state">
-      <description summary="update the published state of a registered client surface">
-        The state is serialized as a JSON string containing widget layout and state data.
-      </description>
-      <arg name="surface" type="object" interface="wl_surface"/>
-      <arg name="fd" type="fd" summary="JSON state file descriptor"/>
-      <arg name="len" type="uint" summary="JSON state length in bytes"/>
-    </request>
-
-    <request name="get_inspected_surfaces">
-      <description summary="request the list of all registered surfaces from the compositor"/>
-    </request>
-
-    <event name="inspected_surface">
-      <description summary="reports a registered surface's state and absolute coordinates"/>
-      <arg name="title" type="string" summary="window title"/>
-      <arg name="app_id" type="string" summary="application ID"/>
-      <arg name="x" type="int" summary="absolute screen X coordinate"/>
-      <arg name="y" type="int" summary="absolute screen Y coordinate"/>
-      <arg name="width" type="int" summary="surface width"/>
-      <arg name="height" type="int" summary="surface height"/>
-      <arg name="fd" type="fd" summary="JSON state file descriptor"/>
-      <arg name="len" type="uint" summary="JSON state length in bytes"/>
-    </event>
-
-    <event name="inspected_surface_done">
-      <description summary="indicates all inspected surfaces have been reported"/>
-    </event>
-  </interface>
-</protocol>
diff --git a/cce-server/protocol/river-input-management-v1.xml b/cce-server/protocol/river-input-management-v1.xml
deleted file mode 100644
index e0f5498..0000000
--- a/cce-server/protocol/river-input-management-v1.xml
+++ /dev/null
@@ -1,244 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<protocol name="river_input_management_v1">
-  <copyright>
-    SPDX-FileCopyrightText: © 2025 Isaac Freund
-    SPDX-License-Identifier: MIT
-
-    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 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="manage seats and input devices">
-    This protocol supports creating/destroying seats, assigning input devices to
-    seats, and configuring input devices (e.g. setting keyboard repeat rate).
-
-    The key words "must", "must not", "required", "shall", "shall not",
-    "should", "should not", "recommended", "may", and "optional" in this
-    document are to be interpreted as described in IETF RFC 2119.
-  </description>
-
-  <interface name="river_input_manager_v1" version="2">
-    <description summary="input manager global interface">
-      Input manager global interface.
-    </description>
-
-    <enum name="error">
-      <entry name="invalid_destroy" value="0"/>
-    </enum>
-
-    <request name="stop">
-      <description summary="stop sending events">
-        This request indicates that the client no longer wishes to receive
-        events on this object.
-
-        The Wayland protocol is asynchronous, which means the server may send
-        further events until the stop request is processed. The client must wait
-        for a river_input_manager_v1.finished event before destroying this
-        object.
-      </description>
-    </request>
-
-    <event name="finished">
-      <description summary="the server has finished with the input manager">
-        This event indicates that the server will send no further events on this
-        object. The client should destroy the object. See
-        river_input_manager_v1.destroy for more information.
-      </description>
-    </event>
-
-    <request name="destroy" type="destructor">
-      <description summary="destroy the river_input_manager_v1 object">
-        This request should be called after the finished event has been received
-        to complete destruction of the object.
-
-        It is a protocol error to make this request before the finished event
-        has been received.
-
-        If a client wishes to destroy this object it should send a
-        river_input_manager_v1.stop request and wait for a
-        river_input_manager_v1.finished event. Once the finished event is
-        received it is safe to destroy this object and any other objects created
-        through this interface.
-      </description>
-    </request>
-
-    <request name="create_seat">
-      <description summary="create a new seat">
-        Create a new seat with the given name. Has no effect if a seat with the
-        given name already exists.
-
-        The default seat with name "default" always exists and does not need to
-        be explicitly created.
-      </description>
-      <arg name="name" type="string"/>
-    </request>
-
-    <request name="destroy_seat">
-      <description summary="destroy a seat">
-        Destroy the seat with the given name. Has no effect if a seat with the
-        given name does not exist.
-
-        The default seat with name "default" cannot be destroyed and attempting
-        to destroy it will have no effect.
-
-        Any input devices assigned to the destroyed seat at the time of
-        destruction are assigned to the default seat.
-      </description>
-      <arg name="name" type="string"/>
-    </request>
-
-    <event name="input_device">
-      <description summary="new input device">
-        A new input device has been created.
-      </description>
-      <arg name="id" type="new_id" interface="river_input_device_v1"/>
-    </event>
-  </interface>
-
-  <interface name="river_input_device_v1" version="2">
-    <description summary="an input device">
-      An input device represents a physical keyboard, mouse, touchscreen, or
-      drawing tablet tool. It is assigned to exactly one seat at a time.
-      By default, all input devices are assigned to the default seat.
-    </description>
-
-    <enum name="error">
-      <entry name="invalid_repeat_info" value="0"/>
-      <entry name="invalid_scroll_factor" value="1"/>
-      <entry name="invalid_map_to_rectangle" value="2"/>
-    </enum>
-
-    <request name="destroy" type="destructor">
-      <description summary="destroy the input device object">
-        This request indicates that the client will no longer use the input
-        device object and that it may be safely destroyed.
-      </description>
-    </request>
-
-    <event name="removed">
-      <description summary="the input device is removed">
-        This event indicates that the input device has been removed.
-
-        The server will send no further events on this object and ignore any
-        request (other than river_input_device_v1.destroy) made after this event is
-        sent. The client should destroy this object with the
-        river_input_device_v1.destroy request to free up resources.
-      </description>
-    </event>
-
-    <enum name="type">
-      <entry name="keyboard" value="0"/>
-      <entry name="pointer" value="1"/>
-      <entry name="touch" value="2"/>
-      <entry name="tablet" value="3"/>
-    </enum>
-
-    <event name="type">
-      <description summary="the type of the input device">
-        The type of the input device. This event is sent once when the
-        river_input_device_v1 object is created. The device type cannot
-        change during the lifetime of the object.
-      </description>
-      <arg name="type" type="uint" enum="type"/>
-    </event>
-
-    <event name="name">
-      <description summary="the name of the input device">
-        The name of the input device. This event is sent once when the
-        river_input_device_v1 object is created. The device name cannot
-        change during the lifetime of the object.
-      </description>
-      <arg name="name" type="string"/>
-    </event>
-
-    <request name="assign_to_seat">
-      <description summary="assign the input device to a seat">
-        Assign the input device to a seat. All input devices not explicitly
-        assigned to a seat are considered assigned to the default seat.
-
-        Has no effect if a seat with the given name does not exist.
-      </description>
-      <arg name="name" type="string" summary="name of the seat"/>
-    </request>
-
-    <request name="set_repeat_info">
-      <description summary="set keyboard repeat rate and delay">
-        Set repeat rate and delay for a keyboard input device. Has no effect if
-        the device is not a keyboard.
-
-        Negative values for either rate or delay are illegal. A rate of zero
-        will disable any repeating (regardless of the value of delay).
-      </description>
-      <arg name="rate" type="int" summary="rate in key repeats per second"/>
-      <arg name="delay" type="int" summary="delay in milliseconds"/>
-    </request>
-
-    <request name="set_scroll_factor">
-      <description summary="set scroll factor">
-        Set the scroll factor for a pointer input device. Has no effect if the
-        device is not a pointer.
-
-        For example, a factor of 0.5 will make scrolling twice as slow while a
-        factor of 3.0 will make scrolling 3 times as fast.
-
-        Setting a scroll factor less than 0 is a protocol error.
-      </description>
-      <arg name="factor" type="fixed"/>
-    </request>
-
-    <request name="map_to_output">
-      <description summary="map input device to the given output">
-        Map the input device to the given output. Has no effect if the device is
-        not a pointer, touch, or tablet device.
-
-        If mapped to both an output and a rectangle, the rectangle has priority.
-
-        Passing null clears an existing mapping.
-      </description>
-      <arg name="output" type="object" interface="wl_output" allow-null="true"/>
-    </request>
-
-    <request name="map_to_rectangle">
-      <description summary="map input device to the given rectangle">
-        Map the input device to the given rectangle in the global compositor
-        coordinate space. Has no effect if the device is not a pointer, touch,
-        or tablet device.
-
-        If mapped to both an output and a rectangle, the rectangle has priority.
-
-        Width and height must be greater than or equal to 0.
-
-        Passing 0 for width or height clears an existing mapping.
-      </description>
-      <arg name="x" type="int"/>
-      <arg name="y" type="int"/>
-      <arg name="width" type="int"/>
-      <arg name="height" type="int"/>
-    </request>
-
-    <event name="done" since="2">
-      <description summary="all information has been sent">
-        This event is sent after all information about the input device has
-        been sent.
-
-        This allows changes to one or more river_input_device_v1 properties to
-        be seen as atomic, even if they happen via multiple events.
-      </description>
-    </event>
-  </interface>
-</protocol>
diff --git a/cce-server/protocol/river-layer-shell-v1.xml b/cce-server/protocol/river-layer-shell-v1.xml
deleted file mode 100644
index 0167e9d..0000000
--- a/cce-server/protocol/river-layer-shell-v1.xml
+++ /dev/null
@@ -1,191 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<protocol name="river_layer_shell_v1">
-  <copyright>
-    SPDX-FileCopyrightText: © 2025 Isaac Freund
-    SPDX-License-Identifier: MIT
-
-    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 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="optional layer shell support">
-    This protocol allows the river-window-management-v1 window manager to
-    support the wlr-layer-shell-unstable-v1 protocol.
-
-    The key words "must", "must not", "required", "shall", "shall not",
-    "should", "should not", "recommended", "may", and "optional" in this
-    document are to be interpreted as described in IETF RFC 2119.
-  </description>
-
-  <interface name="river_layer_shell_v1" version="1">
-    <description summary="river layer shell global interface">
-      This global interface should only be advertised to the client if the
-      river_window_manager_v1 global is also advertised. Binding this interface
-      indicates that the window manager supports layer shell.
-
-      If the window manager does not bind this interface, the compositor should
-      not allow clients to map layer surfaces. This can be achieved by
-      closing layer surfaces immediately.
-    </description>
-
-    <enum name="error">
-      <entry name="object_already_created" value="0"
-        summary="the layer_shell_output/seat object was already created."/>
-    </enum>
-
-    <request name="destroy" type="destructor">
-      <description summary="destroy the river_layer_shell_v1 object">
-        This request indicates that the client will no longer use the
-        river_layer_shell_v1 object.
-      </description>
-    </request>
-
-    <request name="get_output">
-      <description summary="get layer shell output state">
-        It is a protocol error to make this request more than once for a given
-        river_output_v1 object.
-      </description>
-      <arg name="id" type="new_id" interface="river_layer_shell_output_v1"/>
-      <arg name="output" type="object" interface="river_output_v1"/>
-    </request>
-
-    <request name="get_seat">
-      <description summary="get layer shell seat state">
-        It is a protocol error to make this request more than once for a given
-        river_seat_v1 object.
-      </description>
-      <arg name="id" type="new_id" interface="river_layer_shell_seat_v1"/>
-      <arg name="seat" type="object" interface="river_seat_v1"/>
-    </request>
-  </interface>
-
-  <interface name="river_layer_shell_output_v1" version="1">
-    <description summary="layer shell output state">
-      The lifetime of this object is tied to the corresponding river_output_v1.
-      This object is made inert when the river_output_v1.removed event is sent
-      and should be destroyed.
-    </description>
-
-    <request name="destroy" type="destructor">
-      <description summary="destroy the object">
-        This request indicates that the client will no longer use the
-        river_layer_shell_output_v1 object and that it may be safely destroyed.
-
-        This request should be made after the river_output_v1.removed event is
-        received to complete destruction of the output.
-      </description>
-    </request>
-
-    <event name="non_exclusive_area">
-      <description summary="area left after subtracting exclusive zones">
-        This event indicates the area of the output remaining after subtracting
-        the exclusive zones of layer surfaces. Exclusive zones are a hint, the
-        window manager is free to ignore this area hint if it wishes.
-
-        The x and y values are in the global coordinate space, not relative to
-        the position of the output.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-      <arg name="x" type="int" summary="global x coordinate"/>
-      <arg name="y" type="int" summary="global y coordinate"/>
-      <arg name="width" type="int" summary="area width"/>
-      <arg name="height" type="int" summary="area height"/>
-    </event>
-
-    <request name="set_default">
-      <description summary="Set default output for layer surfaces">
-        Mark this output as the default for new layer surfaces which do not
-        request a specific output themselves. This request overrides any
-        previous set_default request on any river_layer_shell_output_v1 object.
-
-        If no set_default request is made or if the default output is destroyed,
-        the default output is undefined until the next set_default request.
-
-        This request modifies window management state and may only be made as
-        part of a manage sequence, see the river_window_manager_v1 description.
-      </description>
-    </request>
-  </interface>
-
-  <interface name="river_layer_shell_seat_v1" version="1">
-    <description summary="layer shell seat state">
-      The lifetime of this object is tied to the corresponding river_seat_v1.
-      This object is made inert when the river_seat_v1.removed event is sent and
-      should be destroyed.
-    </description>
-
-    <request name="destroy" type="destructor">
-      <description summary="destroy the object">
-        This request indicates that the client will no longer use the
-        river_layer_shell_seat_v1 object and that it may be safely destroyed.
-
-        This request should be made after the river_seat_v1.removed event is
-        received to complete destruction of the seat.
-      </description>
-    </request>
-
-    <event name="focus_exclusive">
-      <description summary="layer shell surface has exclusive focus">
-        A layer shell surface will be given exclusive keyboard focus at the end
-        of the manage sequence in which this event is sent. The window manager
-        may want to update window decorations or similar to indicate that no
-        window is focused.
-
-        Until the focus_non_exclusive or focus_none event is sent, all window
-        manager requests to change focus are ignored.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-    </event>
-
-    <event name="focus_non_exclusive">
-      <description summary="layer shell surface wants non-exclusive focus">
-        A layer shell surface will be given non-exclusive keyboard focus at the
-        end of the manage sequence in which this event is sent. The window
-        manager may want to update window decorations or similar to indicate
-        that no window is focused.
-
-        The window manager continues to control focus and may choose to focus a
-        different window/shell surface at any time. If the window manager sets
-        focus during the same manage sequence in which this event is sent, the
-        layer surface will not be focused.
-
-        If the layer surface with non-exclusive focus is closed or the window
-        manager chooses to move focus away from the layer surface, a focus_none
-        event will be sent in the next manage sequence.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-    </event>
-
-    <event name="focus_none">
-      <description summary="no layer shell surface has focus">
-        No layer shell surface will have keyboard focus at the end of the manage
-        sequence in which this event is sent. The window manager may want to
-        return focus to whichever window last had focus, for example.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-    </event>
-  </interface>
-</protocol>
diff --git a/cce-server/protocol/river-libinput-config-v1.xml b/cce-server/protocol/river-libinput-config-v1.xml
deleted file mode 100644
index 46fad57..0000000
--- a/cce-server/protocol/river-libinput-config-v1.xml
+++ /dev/null
@@ -1,901 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<protocol name="river_libinput_config_v1">
-  <copyright>
-    SPDX-FileCopyrightText: © 2025 Isaac Freund
-    SPDX-License-Identifier: MIT
-
-    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 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="configure libinput devices">
-    This protocol exposes libinput device configuration APIs. The libinput
-    documentation should be referred to for detailed information on libinput's
-    behavior.
-
-    Note that the compositor will not be able to expose libinput devices through
-    this protocol when it does not have access to the hardware, for example when
-    running nested in another Wayland compositor or X11 session.
-
-    This protocol is designed so that (hopefully) any backwards compatible
-    change to libinput's API can be matched with a backwards compatible change
-    to this protocol.
-
-    Note: the libinput API uses floating point types (float and double in C)
-    which are not (yet?) natively supported by the Wayland protocol. However,
-    the Wayland protocol does support sending arbitrary bytes through the array
-    argument type. This protocol uses e.g. type="array" summary="double" to
-    indicate a native-endian IEEE-754 64-bit double value.
-
-    The key words "must", "must not", "required", "shall", "shall not",
-    "should", "should not", "recommended", "may", and "optional" in this
-    document are to be interpreted as described in IETF RFC 2119.
-  </description>
-
-  <interface name="river_libinput_config_v1" version="2">
-    <description summary="libinput config global interface">
-      Global interface for configuring libinput devices. This global should
-      only be advertised if river_input_manager_v1 is advertised as well.
-    </description>
-
-    <enum name="error">
-      <entry name="invalid_arg" value="0"
-        summary="invalid enum value or similar"/>
-      <entry name="invalid_destroy" value="1"/>
-    </enum>
-
-    <request name="stop">
-      <description summary="stop sending events">
-        This request indicates that the client no longer wishes to receive
-        events on this object.
-
-        The Wayland protocol is asynchronous, which means the server may send
-        further events until the stop request is processed. The client must wait
-        for a river_libinput_config_v1.finished event before destroying this
-        object.
-      </description>
-    </request>
-
-    <event name="finished">
-      <description summary="the server has finished with the object">
-        This event indicates that the server will send no further events on this
-        object. The client should destroy the object. See
-        river_libinput_config_v1.destroy for more information.
-      </description>
-    </event>
-
-    <request name="destroy" type="destructor">
-      <description summary="destroy the river_libinput_config_v1 object">
-        This request should be called after the finished event has been received
-        to complete destruction of the object.
-
-        It is a protocol error to make this request before the finished event
-        has been received.
-
-        If a client wishes to destroy this object it should send a
-        river_libinput_config_v1.stop request and wait for a
-        river_libinput_config_v1.finished event. Once the finished event is
-        received it is safe to destroy this object and any other objects created
-        through this interface.
-      </description>
-    </request>
-
-    <event name="libinput_device">
-      <description summary="new libinput device">
-        A new libinput device has been created. Not every river_input_device_v1
-        is necessarily a libinput device as well.
-      </description>
-      <arg name="id" type="new_id" interface="river_libinput_device_v1"/>
-    </event>
-
-    <request name="create_accel_config">
-      <description summary="create a acceleration config">
-        Create a acceleration config which can be applied
-        with river_libinput_device_v1.apply_accel_config.
-      </description>
-      <arg name="id" type="new_id"
-        interface="river_libinput_accel_config_v1"/>
-      <arg name="profile" type="uint"
-        enum="river_libinput_device_v1.accel_profile"/>
-    </request>
-  </interface>
-
-  <interface name="river_libinput_device_v1" version="2">
-    <description summary="a libinput device">
-      In general, *_support events will be sent exactly once directly after the
-      river_libinput_device_v1 is created. *_default events will be sent after
-      *_support events if the config option is supported, and *_current events
-      willl be sent after the *_default events and again whenever the config
-      option is changed.
-    </description>
-
-    <enum name="error">
-      <entry name="invalid_arg" value="0"
-        summary="invalid enum value or similar"/>
-    </enum>
-
-    <request name="destroy" type="destructor">
-      <description summary="destroy the libinput device object">
-        This request indicates that the client will no longer use the input
-        device object and that it may be safely destroyed.
-      </description>
-    </request>
-
-    <event name="removed">
-      <description summary="the libinput device is removed">
-        This event indicates that the libinput device has been removed.
-
-        The server will send no further events on this object and ignore any
-        request (other than river_libinput_device_v1.destroy) made after this
-        event is sent. The client should destroy this object with the
-        river_libinput_device_v1.destroy request to free up resources.
-      </description>
-    </event>
-
-    <event name="input_device">
-      <description summary="corresponding river input device">
-        The river_input_device_v1 corresponding to this libinput device.
-        This event will always be the first event sent on the
-        river_libinput_device_v1 object, and it will be sent exactly once.
-      </description>
-      <arg name="device" type="object" interface="river_input_device_v1"/>
-    </event>
-
-    <enum name="send_events_modes" bitfield="true">
-      <entry name="enabled" value="0"/>
-      <entry name="disabled" value="1"/>
-      <entry name="disabled_on_external_mouse" value="2"/>
-    </enum>
-
-    <event name="send_events_support">
-      <description summary="supported send events modes">
-        Supported send events modes.
-      </description>
-      <arg name="modes" type="uint" enum="send_events_modes"/>
-    </event>
-
-    <event name="send_events_default">
-      <description summary="default send events mode">
-        Default send events mode.
-      </description>
-      <arg name="mode" type="uint" enum="send_events_modes"/>
-    </event>
-
-    <event name="send_events_current">
-      <description summary="current send events mode">
-        Current send events mode.
-      </description>
-      <arg name="mode" type="uint" enum="send_events_modes"/>
-    </event>
-
-    <request name="set_send_events">
-      <description summary="set send events mode">
-        Set the send events mode for the device.
-      </description>
-      <arg name="result" type="new_id" interface="river_libinput_result_v1"/>
-      <arg name="mode" type="uint" enum="send_events_modes"/>
-    </request>
-
-    <enum name="tap_state">
-      <entry name="disabled" value="0"/>
-      <entry name="enabled" value="1"/>
-    </enum>
-
-    <event name="tap_support">
-      <description summary="tap-to-click/drag support">
-        The number of fingers supported for tap-to-click/drag.
-        If finger_count is 0, tap-to-click and drag are unsupported.
-      </description>
-      <arg name="finger_count" type="int"/>
-    </event>
-
-    <event name="tap_default">
-      <description summary="default tap-to-click state">
-        Default tap-to-click state.
-      </description>
-      <arg name="state" type="uint" enum="tap_state"/>
-    </event>
-
-    <event name="tap_current">
-      <description summary="current tap-to-click state">
-        Current tap-to-click state.
-      </description>
-      <arg name="state" type="uint" enum="tap_state"/>
-    </event>
-
-    <request name="set_tap">
-      <description summary="enable/disable tap-to-click">
-        Configure tap-to-click on this device, with a default mapping of
-        1, 2, 3 finger tap mapping to left, right, middle click, respectively.
-      </description>
-      <arg name="result" type="new_id" interface="river_libinput_result_v1"/>
-      <arg name="state" type="uint" enum="tap_state"/>
-    </request>
-
-    <enum name="tap_button_map">
-      <entry name="lrm" value="0"
-        summary="1/2/3 finger tap maps to left/right/middle"/>
-      <entry name="lmr" value="1"
-        summary="1/2/3 finger tap maps to left/middle/right"/>
-    </enum>
-
-    <event name="tap_button_map_default">
-      <description summary="default tap-to-click button map">
-        Default tap-to-click button map.
-      </description>
-      <arg name="button_map" type="uint" enum="tap_button_map"/>
-    </event>
-
-    <event name="tap_button_map_current">
-      <description summary="current tap-to-click button map">
-        Current tap-to-click button map.
-      </description>
-      <arg name="button_map" type="uint" enum="tap_button_map"/>
-    </event>
-
-    <request name="set_tap_button_map">
-      <description summary="set tap-to-click button map">
-        Set the finger number to button number mapping for tap-to-click. The
-        default mapping on most devices is to have a 1, 2 and 3 finger tap to
-        map to the left, right and middle button, respectively.
-      </description>
-      <arg name="result" type="new_id" interface="river_libinput_result_v1"/>
-      <arg name="button_map" type="uint" enum="tap_button_map"/>
-    </request>
-
-    <enum name="drag_state">
-      <entry name="disabled" value="0"/>
-      <entry name="enabled" value="1"/>
-    </enum>
-
-    <event name="drag_default">
-      <description summary="default tap-and-drag state">
-        Default tap-and-drag state.
-      </description>
-      <arg name="state" type="uint" enum="drag_state"/>
-    </event>
-
-    <event name="drag_current">
-      <description summary="current tap-and-drag state">
-        Current tap-and-drag state.
-      </description>
-      <arg name="state" type="uint" enum="drag_state"/>
-    </event>
-
-    <request name="set_drag">
-      <description summary="set tap-and-drag state">
-        Configure tap-and-drag functionality on the device.
-      </description>
-      <arg name="result" type="new_id" interface="river_libinput_result_v1"/>
-      <arg name="state" type="uint" enum="drag_state"/>
-    </request>
-
-    <enum name="drag_lock_state">
-      <entry name="disabled" value="0"/>
-      <entry name="enabled_timeout" value="1"/>
-      <entry name="enabled_sticky" value="2"/>
-    </enum>
-
-    <event name="drag_lock_default">
-      <description summary="default drag lock state">
-        Default drag lock state.
-      </description>
-      <arg name="state" type="uint" enum="drag_lock_state"/>
-    </event>
-
-    <event name="drag_lock_current">
-      <description summary="current drag lock state">
-        Current drag lock state.
-      </description>
-      <arg name="state" type="uint" enum="drag_lock_state"/>
-    </event>
-
-    <request name="set_drag_lock">
-      <description summary="set drag lock state">
-        Configure drag-lock during tapping on this device. When enabled, a
-        finger may be lifted and put back on the touchpad and the drag process
-        continues. A timeout for lifting the finger is optional. When disabled,
-        lifting the finger during a tap-and-drag will immediately stop the drag.
-        See the libinput documentation for more details.
-       </description>
-      <arg name="result" type="new_id" interface="river_libinput_result_v1"/>
-      <arg name="state" type="uint" enum="drag_lock_state"/>
-    </request>
-
-    <event name="three_finger_drag_support">
-      <description summary="three finger drag support">
-        The number of fingers supported for three/four finger drag.
-        If finger_count is less than 3, three finger drag is unsupported.
-      </description>
-      <arg name="finger_count" type="int"/>
-    </event>
-
-    <enum name="three_finger_drag_state">
-      <entry name="disabled" value="0"/>
-      <entry name="enabled_3fg" value="1"/>
-      <entry name="enabled_4fg" value="2"/>
-    </enum>
-
-    <event name="three_finger_drag_default">
-      <description summary="default three finger drag state">
-        Default three finger drag state.
-      </description>
-      <arg name="state" type="uint" enum="three_finger_drag_state"/>
-    </event>
-
-    <event name="three_finger_drag_current">
-      <description summary="current three finger drag state">
-        Current three finger drag state.
-      </description>
-      <arg name="state" type="uint" enum="three_finger_drag_state"/>
-    </event>
-
-    <request name="set_three_finger_drag">
-      <description summary="set three finger drag state">
-        Configure three finger drag functionality for the device.
-      </description>
-      <arg name="result" type="new_id" interface="river_libinput_result_v1"/>
-      <arg name="state" type="uint" enum="three_finger_drag_state"/>
-    </request>
-
-    <event name="calibration_matrix_support">
-      <description summary="support for a calibration matrix">
-        A calibration matrix is supported if the supported argument is non-zero.
-      </description>
-      <arg name="supported" type="int" summary="boolean"/>
-    </event>
-
-    <event name="calibration_matrix_default">
-      <description summary="default calibration matrix">
-        Default calibration matrix.
-      </description>
-      <arg name="matrix" type="array" summary="array of 6 floats"/>
-    </event>
-
-    <event name="calibration_matrix_current">
-      <description summary="current calibration matrix">
-        Current calibration matrix.
-      </description>
-      <arg name="matrix" type="array" summary="array of 6 floats"/>
-    </event>
-
-    <request name="set_calibration_matrix">
-      <description summary="set calibration matrix">
-        Set calibration matrix.
-      </description>
-      <arg name="result" type="new_id" interface="river_libinput_result_v1"/>
-      <arg name="matrix" type="array" summary="array of 6 floats"/>
-    </request>
-
-    <enum name="accel_profile">
-      <entry name="none" value="0"/>
-      <entry name="flat" value="1"/>
-      <entry name="adaptive" value="2"/>
-      <entry name="custom" value="4"/>
-    </enum>
-
-    <enum name="accel_profiles" bitfield="true">
-      <entry name="none" value="0"/>
-      <entry name="flat" value="1"/>
-      <entry name="adaptive" value="2"/>
-      <entry name="custom" value="4"/>
-    </enum>
-
-    <event name="accel_profiles_support">
-      <description summary="supported acceleration profiles">
-        Supported acceleration profiles.
-      </description>
-      <arg name="profiles" type="uint" enum="accel_profiles"/>
-    </event>
-
-    <event name="accel_profile_default">
-      <description summary="default acceleration profile">
-        Default acceleration profile.
-      </description>
-      <arg name="profile" type="uint" enum="accel_profile"/>
-    </event>
-
-    <event name="accel_profile_current">
-      <description summary="current acceleration profile">
-        Current acceleration profile.
-      </description>
-      <arg name="profile" type="uint" enum="accel_profile"/>
-    </event>
-
-    <request name="set_accel_profile">
-      <description summary="set acceleration profile">
-        Set the acceleration profile.
-      </description>
-      <arg name="result" type="new_id" interface="river_libinput_result_v1"/>
-      <arg name="profile" type="uint" enum="accel_profile"/>
-    </request>
-
-    <event name="accel_speed_default">
-      <description summary="default acceleration speed">
-        Default acceleration speed.
-      </description>
-      <arg name="speed" type="array" summary="double"/>
-    </event>
-
-    <event name="accel_speed_current">
-      <description summary="current acceleration speed">
-        Current acceleration speed.
-      </description>
-      <arg name="speed" type="array" summary="double"/>
-    </event>
-
-    <request name="set_accel_speed">
-      <description summary="set acceleration speed">
-        Set the acceleration speed within a range of [-1, 1], where 0 is
-        the default acceleration for this device, -1 is the slowest acceleration
-        and 1 is the maximum acceleration available on this device.
-      </description>
-      <arg name="result" type="new_id" interface="river_libinput_result_v1"/>
-      <arg name="speed" type="array" summary="double"/>
-    </request>
-
-    <request name="apply_accel_config">
-      <description summary="apply acceleration config">
-        Apply a pointer accleration config.
-      </description>
-      <arg name="result" type="new_id" interface="river_libinput_result_v1"/>
-      <arg name="config" type="object" interface="river_libinput_accel_config_v1"/>
-    </request>
-
-    <event name="natural_scroll_support">
-      <description summary="support for natural scroll">
-        Natural scroll is supported if the supported argument is non-zero.
-      </description>
-      <arg name="supported" type="int" summary="boolean"/>
-    </event>
-
-    <enum name="natural_scroll_state">
-      <entry name="disabled" value="0"/>
-      <entry name="enabled" value="1"/>
-    </enum>
-
-    <event name="natural_scroll_default">
-      <description summary="default natural scroll">
-        Default natural scroll.
-      </description>
-      <arg name="state" type="uint" enum="natural_scroll_state"/>
-    </event>
-
-    <event name="natural_scroll_current">
-      <description summary="current natural scroll state">
-        Current natural scroll.
-      </description>
-      <arg name="state" type="uint" enum="natural_scroll_state"/>
-    </event>
-
-    <request name="set_natural_scroll">
-      <description summary="set natural scroll state">
-        Set natural scroll state.
-      </description>
-      <arg name="result" type="new_id" interface="river_libinput_result_v1"/>
-      <arg name="state" type="uint" enum="natural_scroll_state"/>
-    </request>
-
-    <event name="left_handed_support">
-      <description summary="support for left-handed mode">
-        Left-handed mode is supported if the supported argument is non-zero.
-      </description>
-      <arg name="supported" type="int" summary="boolean"/>
-    </event>
-
-    <enum name="left_handed_state">
-      <entry name="disabled" value="0"/>
-      <entry name="enabled" value="1"/>
-    </enum>
-
-    <event name="left_handed_default">
-      <description summary="default left-handed mode">
-        Default left-handed mode.
-      </description>
-      <arg name="state" type="uint" enum="left_handed_state"/>
-    </event>
-
-    <event name="left_handed_current">
-      <description summary="current left-handed mode state">
-        Current left-handed mode.
-      </description>
-      <arg name="state" type="uint" enum="left_handed_state"/>
-    </event>
-
-    <request name="set_left_handed">
-      <description summary="set left-handed mode state">
-        Set left-handed mode state.
-      </description>
-      <arg name="result" type="new_id" interface="river_libinput_result_v1"/>
-      <arg name="state" type="uint" enum="left_handed_state"/>
-    </request>
-
-    <enum name="click_method">
-      <entry name="none" value="0"/>
-      <entry name="button_areas" value="1"/>
-      <entry name="clickfinger" value="2"/>
-    </enum>
-
-    <enum name="click_methods" bitfield="true">
-      <entry name="none" value="0"/>
-      <entry name="button_areas" value="1"/>
-      <entry name="clickfinger" value="2"/>
-    </enum>
-
-    <event name="click_method_support">
-      <description summary="supported click methods">
-        The click methods supported by the device.
-      </description>
-      <arg name="methods" type="uint" enum="click_methods"/>
-    </event>
-
-    <event name="click_method_default">
-      <description summary="default click method">
-        Default click method.
-      </description>
-      <arg name="method" type="uint" enum="click_method"/>
-    </event>
-
-    <event name="click_method_current">
-      <description summary="current click method">
-        Current click method.
-      </description>
-      <arg name="method" type="uint" enum="click_method"/>
-    </event>
-
-    <request name="set_click_method">
-      <description summary="set click method">
-        Set click method.
-      </description>
-      <arg name="result" type="new_id" interface="river_libinput_result_v1"/>
-      <arg name="method" type="uint" enum="click_method"/>
-    </request>
-
-    <enum name="clickfinger_button_map">
-      <entry name="lrm" value="0"/>
-      <entry name="lmr" value="1"/>
-    </enum>
-
-    <event name="clickfinger_button_map_default">
-      <description summary="default clickfinger button map">
-        Default clickfinger button map.
-        Supported if click_methods.clickfinger is supported.
-      </description>
-      <arg name="button_map" type="uint" enum="clickfinger_button_map"/>
-    </event>
-
-    <event name="clickfinger_button_map_current">
-      <description summary="current clickfinger button map">
-        Current clickfinger button map.
-        Supported if click_methods.clickfinger is supported.
-      </description>
-      <arg name="button_map" type="uint" enum="clickfinger_button_map"/>
-    </event>
-
-    <request name="set_clickfinger_button_map">
-      <description summary="set clickfinger button map">
-        Set clickfinger button map.
-        Supported if click_methods.clickfinger is supported.
-      </description>
-      <arg name="result" type="new_id" interface="river_libinput_result_v1"/>
-      <arg name="button_map" type="uint" enum="clickfinger_button_map"/>
-    </request>
-
-    <event name="middle_emulation_support">
-      <description summary="support for middle mouse button emulation">
-        Middle mouse button emulation is supported if the supported argument is
-        non-zero.
-      </description>
-      <arg name="supported" type="int" summary="boolean"/>
-    </event>
-
-    <enum name="middle_emulation_state">
-      <entry name="disabled" value="0"/>
-      <entry name="enabled" value="1"/>
-    </enum>
-
-    <event name="middle_emulation_default">
-      <description summary="default middle mouse button emulation">
-        Default middle mouse button emulation.
-      </description>
-      <arg name="state" type="uint" enum="middle_emulation_state"/>
-    </event>
-
-    <event name="middle_emulation_current">
-      <description summary="current middle mouse button emulation state">
-        Current middle mouse button emulation.
-      </description>
-      <arg name="state" type="uint" enum="middle_emulation_state"/>
-    </event>
-
-    <request name="set_middle_emulation">
-      <description summary="set middle mouse button emulation state">
-        Set middle mouse button emulation state.
-      </description>
-      <arg name="result" type="new_id" interface="river_libinput_result_v1"/>
-      <arg name="state" type="uint" enum="middle_emulation_state"/>
-    </request>
-
-    <enum name="scroll_method">
-      <entry name="no_scroll" value="0"/>
-      <entry name="two_finger" value="1"/>
-      <entry name="edge" value="2"/>
-      <entry name="on_button_down" value="4"/>
-    </enum>
-
-    <enum name="scroll_methods" bitfield="true">
-      <entry name="no_scroll" value="0"/>
-      <entry name="two_finger" value="1"/>
-      <entry name="edge" value="2"/>
-      <entry name="on_button_down" value="4"/>
-    </enum>
-
-    <event name="scroll_method_support">
-      <description summary="supported scroll methods">
-        The scroll methods supported by the device.
-      </description>
-      <arg name="methods" type="uint" enum="scroll_methods"/>
-    </event>
-
-    <event name="scroll_method_default">
-      <description summary="default scroll method">
-        Default scroll method.
-      </description>
-      <arg name="method" type="uint" enum="scroll_method"/>
-    </event>
-
-    <event name="scroll_method_current">
-      <description summary="current scroll method">
-        Current scroll method.
-      </description>
-      <arg name="method" type="uint" enum="scroll_method"/>
-    </event>
-
-    <request name="set_scroll_method">
-      <description summary="set scroll method">
-        Set scroll method.
-      </description>
-      <arg name="result" type="new_id" interface="river_libinput_result_v1"/>
-      <arg name="method" type="uint" enum="scroll_method"/>
-    </request>
-
-    <event name="scroll_button_default">
-      <description summary="default scroll button">
-        Default scroll button.
-        Supported if scroll_methods.on_button_down is supported.
-      </description>
-      <arg name="button" type="uint"/>
-    </event>
-
-    <event name="scroll_button_current">
-      <description summary="current scroll button">
-        Current scroll button.
-        Supported if scroll_methods.on_button_down is supported.
-      </description>
-      <arg name="button" type="uint"/>
-    </event>
-
-    <request name="set_scroll_button">
-      <description summary="set scroll button">
-        Set scroll button.
-        Supported if scroll_methods.on_button_down is supported.
-      </description>
-      <arg name="result" type="new_id" interface="river_libinput_result_v1"/>
-      <arg name="button" type="uint"/>
-    </request>
-
-    <enum name="scroll_button_lock_state">
-      <entry name="disabled" value="0"/>
-      <entry name="enabled" value="1"/>
-    </enum>
-
-    <event name="scroll_button_lock_default">
-      <description summary="default scroll button lock state">
-        Default scroll button lock state.
-        Supported if scroll_methods.on_button_down is supported.
-      </description>
-      <arg name="state" type="uint" enum="scroll_button_lock_state"/>
-    </event>
-
-    <event name="scroll_button_lock_current">
-      <description summary="current scroll button lock state">
-        Current scroll button lock state.
-        Supported if scroll_methods.on_button_down is supported.
-      </description>
-      <arg name="state" type="uint" enum="scroll_button_lock_state"/>
-    </event>
-
-    <request name="set_scroll_button_lock">
-      <description summary="set scroll button lock state">
-        Set scroll button lock state.
-        Supported if scroll_methods.on_button_down is supported.
-      </description>
-      <arg name="result" type="new_id" interface="river_libinput_result_v1"/>
-      <arg name="state" type="uint" enum="scroll_button_lock_state"/>
-    </request>
-
-    <event name="dwt_support">
-      <description summary="support for disable-while-typing">
-        Disable-while-typing is supported if the supported argument is
-        non-zero.
-      </description>
-      <arg name="supported" type="int" summary="boolean"/>
-    </event>
-
-    <enum name="dwt_state">
-      <entry name="disabled" value="0"/>
-      <entry name="enabled" value="1"/>
-    </enum>
-
-    <event name="dwt_default">
-      <description summary="default disable-while-typing state">
-        Default disable-while-typing state.
-      </description>
-      <arg name="state" type="uint" enum="dwt_state"/>
-    </event>
-
-    <event name="dwt_current">
-      <description summary="current disable-while-typing state">
-        Current disable-while-typing state.
-      </description>
-      <arg name="state" type="uint" enum="dwt_state"/>
-    </event>
-
-    <request name="set_dwt">
-      <description summary="set disable-while-typing state">
-        Set disable-while-typing state.
-      </description>
-      <arg name="result" type="new_id" interface="river_libinput_result_v1"/>
-      <arg name="state" type="uint" enum="dwt_state"/>
-    </request>
-
-    <event name="dwtp_support">
-      <description summary="support for disable-while-trackpointing">
-        Disable-while-trackpointing is supported if the supported argument is
-        non-zero.
-      </description>
-      <arg name="supported" type="int" summary="boolean"/>
-    </event>
-
-    <enum name="dwtp_state">
-      <entry name="disabled" value="0"/>
-      <entry name="enabled" value="1"/>
-    </enum>
-
-    <event name="dwtp_default">
-      <description summary="default disable-while-trackpointing state">
-        Default disable-while-trackpointing state.
-      </description>
-      <arg name="state" type="uint" enum="dwtp_state"/>
-    </event>
-
-    <event name="dwtp_current">
-      <description summary="current disable-while-trackpointing state">
-        Current disable-while-trackpointing state.
-      </description>
-      <arg name="state" type="uint" enum="dwtp_state"/>
-    </event>
-
-    <request name="set_dwtp">
-      <description summary="set disable-while-trackpointing state">
-        Set disable-while-trackpointing state.
-      </description>
-      <arg name="result" type="new_id" interface="river_libinput_result_v1"/>
-      <arg name="state" type="uint" enum="dwtp_state"/>
-    </request>
-
-    <event name="rotation_support">
-      <description summary="support for rotation">
-        Rotation is supported if the supported argument is non-zero.
-      </description>
-      <arg name="supported" type="int" summary="boolean"/>
-    </event>
-
-    <event name="rotation_default">
-      <description summary="default rotation angle">
-        Default rotation angle.
-      </description>
-      <arg name="angle" type="uint"/>
-    </event>
-
-    <event name="rotation_current">
-      <description summary="current rotation angle">
-        Current rotation angle.
-      </description>
-      <arg name="angle" type="uint"/>
-    </event>
-
-    <request name="set_rotation">
-      <description summary="set rotation angle">
-        Set rotation angle in degrees clockwise off the logical neutral
-        position. Angle must be in the range [0-360).
-      </description>
-      <arg name="result" type="new_id" interface="river_libinput_result_v1"/>
-      <arg name="angle" type="uint"/>
-    </request>
-
-    <event name="done" since="2">
-      <description summary="all information has been sent">
-        This event is sent after all information about the libinput device has
-        been sent.
-
-        This allows changes to one or more river_libinput_device_v1 properties
-        to be seen as atomic, even if they happen via multiple events.
-      </description>
-    </event>
-  </interface>
-
-  <interface name="river_libinput_accel_config_v1" version="1">
-    <description summary="acceleration config">
-      The result returned by libinput on setting configuration for a device.
-    </description>
-
-    <enum name="error">
-      <entry name="invalid_arg" value="0"
-        summary="invalid enum value or similar"/>
-    </enum>
-
-    <request name="destroy" type="destructor">
-      <description summary="destroy the accel object">
-        This request indicates that the client will no longer use the accel
-        config object and that it may be safely destroyed.
-      </description>
-    </request>
-
-    <enum name="accel_type">
-      <entry name="fallback" value="0"/>
-      <entry name="motion" value="1"/>
-      <entry name="scroll" value="2"/>
-    </enum>
-
-    <request name="set_points">
-      <description summary="define custom acceleration function">
-        Defines the acceleration function for a given movement type
-        in an acceleration configuration with custom accel profile.
-      </description>
-      <arg name="result" type="new_id" interface="river_libinput_result_v1"/>
-      <arg name="type" type="uint" enum="accel_type"/>
-      <arg name="step" type="array" summary="double"/>
-      <arg name="points" type="array" summary="array of doubles"/>
-    </request>
-  </interface>
-
-  <interface name="river_libinput_result_v1" version="1">
-    <description summary="config application result">
-      The result returned by libinput on setting configuration for a device.
-    </description>
-
-    <event name="success" type="destructor">
-      <description summary="config success">
-        The configuration was successfully applied to the device.
-      </description>
-    </event>
-
-    <event name="unsupported" type="destructor">
-      <description summary="config unsupported">
-        The configuration is unsupported by the device and was ignored.
-      </description>
-    </event>
-
-    <event name="invalid" type="destructor">
-      <description summary="config invalid">
-        The configuration is invalid and was ignored.
-      </description>
-    </event>
-  </interface>
-</protocol>
diff --git a/cce-server/protocol/river-window-management-v1.xml b/cce-server/protocol/river-window-management-v1.xml
deleted file mode 100644
index cbc1015..0000000
--- a/cce-server/protocol/river-window-management-v1.xml
+++ /dev/null
@@ -1,1897 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<protocol name="river_window_management_v1">
-  <copyright>
-    SPDX-FileCopyrightText: © 2024 Isaac Freund
-    SPDX-License-Identifier: MIT
-
-    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 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="frame-perfect window management">
-    This protocol allows a single "window manager" client to determine the
-    window management policy of the compositor. State is globally
-    double-buffered allowing for frame perfect state changes involving multiple
-    windows.
-
-    The key words "must", "must not", "required", "shall", "shall not",
-    "should", "should not", "recommended", "may", and "optional" in this
-    document are to be interpreted as described in IETF RFC 2119.
-  </description>
-
-  <interface name="river_window_manager_v1" version="4">
-    <description summary="window manager global interface">
-      This global interface should only be advertised to the window manager
-      process. Only one window management client may be active at a time. The
-      compositor should use the unavailable event if necessary to enforce this.
-
-      There are two disjoint categories of state managed by this protocol:
-
-      Window management state influences the communication between the
-      compositor and individual windows (e.g. xdg_toplevels). Window management
-      state includes window dimensions, fullscreen state, keyboard focus,
-      keyboard bindings, and more.
-
-      Rendering state only affects the rendered output of the compositor and
-      does not influence communication between the compositor and individual
-      windows. Rendering state includes the position and rendering order of
-      windows, shell surfaces, decoration surfaces, borders, and more.
-
-      Window management state may only be modified by the window manager as part
-      of a manage sequence. A manage sequence is started with the manage_start
-      event and ended with the manage_finish request. It is a protocol error to
-      modify window management state outside of a manage sequence.
-
-      A manage sequence is always followed by at least one render sequence. A
-      render sequence is started with the render_start event and ended with the
-      render_finish request.
-
-      Rendering state may be modified by the window manager during a manage
-      sequence or a render sequence. Regardless of when the rendering state is
-      modified, it is applied with the next render_finish request. It is a
-      protocol error to modify rendering state outside of a manage or render
-      sequence.
-
-      The server will start a manage sequence by sending new state and the
-      manage_start event as soon as possible whenever there is a change in state
-      that must be communicated with the window manager.
-
-      If the window manager client needs to ensure a manage sequence is started
-      due to a state change the compositor is not aware of, it may send the
-      manage_dirty request.
-
-      The server will start a render sequence by sending new state and the
-      render_start event as soon as possible whenever there is a change in
-      window dimensions that must be communicated with the window manager.
-      Multiple render sequences may be made consecutively without a manage
-      sequence in between, for example if a window independently changes its own
-      dimensions.
-
-      To summarize, the main loop of this protocol is as follows:
-
-      1. The server sends events indicating all changes since the last
-         manage sequence followed by the manage_start event.
-
-      2. The client sends requests modifying window management state or
-         rendering state (as defined above) followed by the manage_finish
-         request.
-
-      3. The server sends new state to windows and waits for responses.
-
-      4. The server sends new window dimensions to the client followed by the
-         render_start event.
-
-      5. The client sends requests modifying rendering state (as defined above)
-         followed by the render_finish request.
-
-      6. If window dimensions change, loop back to step 4.
-         If state that requires a manage sequence changes or if the client makes
-         a manage_dirty request, loop back to step 1.
-
-      For the purposes of frame perfection, the server may delay rendering new
-      state committed by the windows in step 3 until after step 5 is finished.
-
-      It is a protocol error for the client to make a manage_finish or
-      render_finish request that violates this ordering.
-    </description>
-
-    <enum name="error">
-      <entry name="sequence_order" value="0"
-        summary="request violates manage/render sequence ordering"/>
-      <entry name="role" value="1"
-        summary="given wl_surface already has a role"/>
-      <entry name="unresponsive" value="2"
-        summary="window manager unresponsive"/>
-    </enum>
-
-    <event name="unavailable">
-      <description summary="window management unavailable">
-        This event indicates that window management is not available to the
-        client, perhaps due to another window management client already running.
-        The circumstances causing this event to be sent are compositor policy.
-
-        If sent, this event is guaranteed to be the first and only event sent by
-        the server.
-
-        The server will send no further events on this object. The client should
-        destroy this object and all objects created through this interface.
-      </description>
-    </event>
-
-    <request name="stop">
-      <description summary="stop sending events">
-        This request indicates that the client no longer wishes to receive
-        events on this object.
-
-        The Wayland protocol is asynchronous, which means the server may send
-        further events until the stop request is processed. The client must wait
-        for a river_window_manager_v1.finished event before destroying this
-        object.
-      </description>
-    </request>
-
-    <event name="finished">
-      <description summary="the server has finished with the window manager">
-        This event indicates that the server will send no further events on this
-        object. The client should destroy the object. See
-        river_window_manager_v1.destroy for more information.
-      </description>
-    </event>
-
-    <request name="destroy" type="destructor">
-      <description summary="destroy the river_window_manager_v1 object">
-        This request should be called after the finished event has been received
-        to complete destruction of the object.
-
-        If a client wishes to destroy this object it should send a
-        river_window_manager_v1.stop request and wait for a
-        river_window_manager_v1.finished event. Once the finished event is
-        received it is safe to destroy this object and any other objects created
-        through this interface.
-      </description>
-    </request>
-
-    <event name="manage_start">
-      <description summary="start a manage sequence">
-        This event indicates that the server has sent events indicating all
-        state changes since the last manage sequence.
-
-        In response to this event, the client should make requests modifying
-        window management state as it chooses. Then, the client must make the
-        manage_finish request.
-
-        See the description of the river_window_manager_v1 interface for a
-        complete overview of the manage/render sequence loop.
-      </description>
-    </event>
-
-    <request name="manage_finish">
-      <description summary="finish a manage sequence">
-        This request indicates that the client has made all changes to window
-        management state it wishes to include in the current manage sequence and
-        that the server should atomically send these state changes to the
-        windows and continue with the manage sequence.
-
-        After sending this request, it is a protocol error for the client to
-        make further changes to window management state until the next
-        manage_start event is received.
-
-        See the description of the river_window_manager_v1 interface for a
-        complete overview of the manage/render sequence loop.
-      </description>
-    </request>
-
-    <request name="manage_dirty">
-      <description summary="ensure a manage sequence is started">
-        This request ensures a manage sequence is started and that a
-        manage_start event is sent by the server. If this request is made during
-        an ongoing manage sequence, a new manage sequence will be started as
-        soon as the current one is completed.
-
-        The client may want to use this request due to an internal state change
-        that the compositor is not aware of (e.g. a dbus event) which should
-        affect window management or rendering state.
-      </description>
-    </request>
-
-    <event name="render_start">
-      <description summary="start a render sequence">
-        This event indicates that the server has sent all
-        river_window_v1.dimensions events necessary.
-
-        In response to this event, the client should make requests modifying
-        rendering state as it chooses. Then, the client must make the
-        render_finish request.
-
-        See the description of the river_window_manager_v1 interface for a
-        complete overview of the manage/render sequence loop.
-      </description>
-    </event>
-
-    <request name="render_finish">
-      <description summary="finish a render sequence">
-        This request indicates that the client has made all changes to rendering
-        state it wishes to include in the current manage sequence and that the
-        server should atomically apply and display these state changes to the
-        user.
-
-        After sending this request, it is a protocol error for the client to
-        make further changes to rendering state until the next manage_start or
-        render_start event is received, whichever comes first.
-
-        See the description of the river_window_manager_v1 interface for a
-        complete overview of the manage/render sequence loop.
-      </description>
-    </request>
-
-    <event name="session_locked">
-      <description summary="the session has been locked">
-        This event indicates that the session has been locked.
-
-        The window manager may wish to restrict which key bindings are available
-        while locked or otherwise use this information.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-    </event>
-
-    <event name="session_unlocked">
-      <description summary="the session has been unlocked">
-        This event indicates that the session has been unlocked.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-    </event>
-
-    <event name="window">
-      <description summary="new window">
-        A new window has been created.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-      <arg name="id" type="new_id" interface="river_window_v1" summary="new window"/>
-    </event>
-
-    <event name="output">
-      <description summary="new output">
-        A new logical output has been created, perhaps due to a new physical
-        monitor being plugged in or perhaps due to a change in configuration.
-
-        This event will be followed by river_output_v1.position and dimensions
-        events as well as a manage_start event after all other new state has
-        been sent by the server.
-      </description>
-      <arg name="id" type="new_id" interface="river_output_v1" summary="new output"/>
-    </event>
-
-    <event name="seat">
-      <description summary="new seat">
-        A new seat has been created.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-      <arg name="id" type="new_id" interface="river_seat_v1" summary="new seat"/>
-    </event>
-
-    <request name="get_shell_surface">
-      <description summary="assign the river_shell_surface_v1 surface role">
-        Create a new shell surface for window manager UI and assign the
-        river_shell_surface_v1 role to the surface.
-
-        Providing a wl_surface which already has a role or already has a buffer
-        attached or committed is a protocol error.
-      </description>
-      <arg name="id" type="new_id" interface="river_shell_surface_v1"
-        summary="new river shell surface"/>
-      <arg name="surface" type="object" interface="wl_surface"
-        summary="base surface"/>
-    </request>
-
-    <request name="exit_session" since="4">
-      <description summary="exit the Wayland session">
-        End the current Wayland session and exit the compositor.
-        All Wayland clients running in the current session, including
-        the window manager, will be disconnected.
-
-        Window managers should only make this request if the user explicitly
-        asks to exit the Wayland session, not for example on normal window
-        manager termination.
-      </description>
-    </request>
-  </interface>
-
-  <interface name="river_window_v1" version="4">
-    <description summary="a logical window">
-      This represents a logical window. For example, a window may correspond to
-      an xdg_toplevel or Xwayland window.
-
-      A newly created window will not be displayed until the window manager
-      makes a propose_dimensions or fullscreen request as part of a manage
-      sequence, the server replies with a dimensions event as part of a render
-      sequence, and that render sequence is finished.
-    </description>
-
-    <enum name="error">
-      <entry name="node_exists" value="0"
-        summary="window already has a node object"/>
-      <entry name="invalid_dimensions" value="1"
-        summary="proposed dimensions out of bounds"/>
-      <entry name="invalid_border" value="2"
-        summary="invalid arg to set_borders"/>
-      <entry name="invalid_clip_box" value="3"
-        summary="invalid arg to set_clip_box"/>
-    </enum>
-
-    <request name="destroy" type="destructor">
-      <description summary="destroy the window object">
-        This request indicates that the client will no longer use the window
-        object and that it may be safely destroyed.
-
-        This request should be made after the river_window_v1.closed event or
-        river_window_manager_v1.finished is received to complete destruction of
-        the window.
-      </description>
-    </request>
-
-    <event name="closed">
-      <description summary="the window has been closed">
-        The window has been closed by the server, perhaps due to an
-        xdg_toplevel.close request or similar.
-
-        The server will send no further events on this object and ignore any
-        request other than river_window_v1.destroy made after this event is
-        sent. The client should destroy this object with the
-        river_window_v1.destroy request to free up resources.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-    </event>
-
-    <request name="close">
-      <description summary="request that the window be closed">
-        Request that the window be closed. The window may ignore this request or
-        only close after some delay, perhaps opening a dialog asking the user to
-        save their work or similar.
-
-        The server will send a river_window_v1.closed event if/when the window
-        has been closed.
-
-        This request modifies window management state and may only be made as
-        part of a manage sequence, see the river_window_manager_v1 description.
-      </description>
-    </request>
-
-    <request name="get_node">
-      <description summary="get the window's render list node">
-        Get the node in the render list corresponding to the window.
-
-        It is a protocol error to make this request more than once for a single
-        window.
-      </description>
-      <arg name="id" type="new_id" interface="river_node_v1" summary="new node"/>
-    </request>
-
-    <event name="dimensions_hint">
-      <description summary="the window's preferred min/max dimensions">
-        This event informs the window manager of the window's preferred min/max
-        dimensions. These preferences are a hint, and the window manager is free
-        to propose dimensions outside of these bounds.
-
-        All min/max width/height values must be strictly greater than or equal
-        to 0. A value of 0 indicates that the window has no preference for that
-        value.
-
-        The min_width/min_height must be strictly less than or equal to the
-        max_width/max_height.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-      <arg name="min_width" type="int" summary="minimum width"/>
-      <arg name="min_height" type="int" summary="minimum height"/>
-      <arg name="max_width" type="int" summary="maximum width"/>
-      <arg name="max_height" type="int" summary="maximum height"/>
-    </event>
-
-    <event name="dimensions">
-      <description summary="window dimensions">
-        This event indicates the dimensions of the window in the compositor's
-        logical coordinate space. The width and height must be strictly greater
-        than zero.
-
-        Note that the dimensions of a river_window_v1 refer to the dimensions of
-        the window content and are unaffected by the presence of borders or
-        decoration surfaces.
-
-        This event is sent as part of a render sequence before the render_start
-        event.
-
-        It may be sent due to a propose_dimensions or fullscreen request in a
-        previous manage sequence or because a window independently decides to
-        change its dimensions.
-
-        The window will not be displayed until the first dimensions event is
-        received and the render sequence is finished.
-      </description>
-      <arg name="width" type="int" summary="window content width"/>
-      <arg name="height" type="int" summary="window content height"/>
-    </event>
-
-    <request name="propose_dimensions">
-      <description summary="propose window dimensions">
-        This request proposes dimensions for the window in the compositor's
-        logical coordinate space.
-
-        The width and height must be greater than or equal to zero. If the width
-        or height is zero the window will be allowed to decide its own
-        dimensions.
-
-        The window may not take the exact dimensions proposed. The actual
-        dimensions taken by the window will be sent in a subsequent
-        river_window_v1.dimensions event. For example, a terminal emulator may
-        only allow dimensions that are multiple of the cell size.
-
-        When a propose_dimensions request is made, the server must send a
-        dimensions event in response as soon as possible. It may not be possible
-        to send a dimensions event in the very next render sequence if, for
-        example, the window takes too long to respond to the proposed
-        dimensions. In this case, the server will send the dimensions event in a
-        future render sequence.
-
-        Note that the dimensions of a river_window_v1 refer to the dimensions of
-        the window content and are unaffected by the presence of borders or
-        decoration surfaces.
-
-        This request modifies window management state and may only be made as
-        part of a manage sequence, see the river_window_manager_v1 description.
-      </description>
-      <arg name="width" type="int" summary="proposed content width"/>
-      <arg name="height" type="int" summary="proposed content height"/>
-    </request>
-
-    <request name="hide">
-      <description summary="request that the window be hidden">
-        Request that the window be hidden. Has no effect if the window is
-        already hidden. Hides any window borders and decorations as well.
-
-        Newly created windows are considered shown unless explicitly hidden with
-        the hide request.
-
-        This request modifies rendering state and may only be made as part of a
-        render sequence, see the river_window_manager_v1 description.
-      </description>
-    </request>
-
-    <request name="show">
-      <description summary="request that the window be shown">
-        Request that the window be shown. Has no effect if the window is not
-        hidden. Does not guarantee that the window is visible as it may be
-        completely obscured by other windows placed above it for example.
-
-        Newly created windows are considered shown unless explicitly hidden with
-        the hide request.
-
-        This request modifies rendering state and may only be made as part of a
-        render sequence, see the river_window_manager_v1 description.
-      </description>
-    </request>
-
-    <event name="app_id">
-      <description summary="the window set an application ID">
-        The window set an application ID.
-
-        The app_id argument will be null if the window has never set an
-        application ID or if the window cleared its application ID. (Xwayland
-        windows may do this for example, though xdg-toplevels may not.)
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-      <arg name="app_id" type="string" allow-null="true"
-        summary="window application ID"/>
-    </event>
-
-    <event name="title">
-      <description summary="the window set a title">
-        The window set a title.
-
-        The title argument will be null if the window has never set a title or
-        if the window cleared its title. (Xwayland windows may do this for
-        example, though xdg-toplevels may not.)
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-      <arg name="title" type="string" allow-null="true" summary="window title"/>
-    </event>
-
-    <event name="parent">
-      <description summary="the window set a parent">
-        The window set a parent window. If this event is never received or if
-        the parent argument is null then the window has no parent.
-
-        A surface with a parent set might be a dialog, file picker, or similar
-        for the parent window.
-
-        Child windows should generally be rendered directly above their parent.
-
-        The compositor must guarantee that there are no loops in the window
-        tree: a parent must not be the descendant of one of its children.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-      <arg name="parent" type="object" allow-null="true"
-        interface="river_window_v1" summary="parent window, if any"/>
-    </event>
-
-    <enum name="decoration_hint">
-      <entry name="only_supports_csd" value="0"
-        summary="only supports client side decoration"/>
-      <entry name="prefers_csd" value="1"
-        summary="client side decoration preferred, both CSD and SSD supported"/>
-      <entry name="prefers_ssd" value="2"
-        summary="server side decoration preferred, both CSD and SSD supported"/>
-      <entry name="no_preference" value="3"
-        summary="no preference, both CSD and SSD supported"/>
-    </enum>
-
-    <event name="decoration_hint">
-      <description summary="supported/preferred decoration style">
-        Information from the window about the supported and preferred client
-        side/server side decoration options.
-
-        This event may be sent multiple times over the lifetime of the window if
-        the window changes its preferences.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-      <arg name="hint" type="uint" enum="decoration_hint" summary="decoration hint"/>
-    </event>
-
-    <request name="use_csd">
-      <description summary="tell the client to use CSD">
-        Tell the client to use client side decoration and draw its own title
-        bar, borders, etc.
-
-        This is the default if neither this request nor the use_ssd request is
-        ever made.
-
-        This request modifies window management state and may only be made as
-        part of a manage sequence, see the river_window_manager_v1 description.
-      </description>
-    </request>
-
-    <request name="use_ssd">
-      <description summary="tell the client to use SSD">
-        Tell the client to use server side decoration and not draw any client
-        side decorations.
-
-        This request will have no effect if the client only supports client side
-        decoration, see the decoration_hint event.
-
-        This request modifies window management state and may only be made as
-        part of a manage sequence, see the river_window_manager_v1 description.
-      </description>
-    </request>
-
-    <enum name="edges" bitfield="true">
-      <entry name="none" value="0"/>
-      <entry name="top" value="1"/>
-      <entry name="bottom" value="2"/>
-      <entry name="left" value="4"/>
-      <entry name="right" value="8"/>
-    </enum>
-
-    <request name="set_borders">
-      <description summary="set window borders">
-        This request decorates the window with borders drawn by the compositor
-        on the specified edges of the window. Borders are drawn above the window
-        content.
-
-        Corners are drawn only between borders on adjacent edges. If e.g. the
-        left edge has a border and the top edge does not, the border drawn on
-        the left edge will not extend vertically beyond the top edge of the
-        window.
-
-        Borders are not drawn while the window is fullscreen.
-
-        The color is defined by four 32-bit RGBA values. Unless specified in
-        another protocol extension, the RGBA values use pre-multiplied alpha.
-
-        The valid range for the RGBA values is from 0x00000000 to 0xffffffff.
-        These values are interpreted as a percentage:
-        - 0x00000000 means 0% of the given color component
-        - 0xffffffff means 100% of the given color component
-
-        Setting the edges to none or the width to 0 disables the borders.
-        Setting a negative width is a protocol error.
-
-        This request completely overrides all previous set_borders requests.
-        Only the most recent set_borders request has an effect.
-
-        Note that the position/dimensions of a river_window_v1 refer to the
-        position/dimensions of the window content and are unaffected by the
-        presence of borders or decoration surfaces.
-
-        This request modifies rendering state and may only be made as part of a
-        render sequence, see the river_window_manager_v1 description.
-      </description>
-      <arg name="edges" type="uint" enum="edges" summary="border edges"/>
-      <arg name="width" type="int" summary="border width"/>
-      <arg name="r" type="uint" summary="32-bit red value"/>
-      <arg name="g" type="uint" summary="32-bit green value"/>
-      <arg name="b" type="uint" summary="32-bit blue value"/>
-      <arg name="a" type="uint" summary="32-bit alpha value"/>
-    </request>
-
-    <request name="set_tiled">
-      <description summary="set window tiled state">
-        Inform the window that it is part of a tiled layout and adjacent to
-        other elements in the tiled layout on the given edges.
-
-        The window should use this information to change the style of its client
-        side decorations and avoid drawing e.g. drop shadows outside of the
-        window dimensions on the tiled edges.
-
-        Setting the edges argument to none informs the window that it is not
-        part of a tiled layout. If this request is never made, the window is
-        informed that it is not part of a tiled layout.
-
-        This request modifies window management state and may only be made as
-        part of a manage sequence, see the river_window_manager_v1 description.
-      </description>
-      <arg name="edges" type="uint" enum="edges" summary="tiled edges"/>
-    </request>
-
-    <request name="get_decoration_above">
-      <description summary="create a decoration above the window in z-order">
-        Create a decoration surface and assign the river_decoration_v1 role to
-        the surface. The created decoration is placed above the window in
-        rendering order, see the description of river_decoration_v1.
-
-        Providing a wl_surface which already has a role or already has a buffer
-        attached or committed is a protocol error.
-      </description>
-      <arg name="id" type="new_id" interface="river_decoration_v1"
-        summary="new decoration surface"/>
-      <arg name="surface" type="object" interface="wl_surface"
-        summary="base surface"/>
-    </request>
-
-    <request name="get_decoration_below">
-      <description summary="create a decoration below the window in z-order">
-        Create a decoration surface and assign the river_decoration_v1 role to
-        the surface. The created decoration is placed below the window in
-        rendering order, see the description of river_decoration_v1.
-
-        Providing a wl_surface which already has a role or already has a buffer
-        attached or committed is a protocol error.
-      </description>
-      <arg name="id" type="new_id" interface="river_decoration_v1"
-        summary="new decoration surface"/>
-      <arg name="surface" type="object" interface="wl_surface"
-        summary="base surface"/>
-    </request>
-
-    <event name="pointer_move_requested">
-      <description summary="window requested interactive pointer move">
-        This event informs the window manager that the window has requested to
-        be interactively moved using the pointer. The seat argument indicates the
-        seat for the move.
-
-        The xdg-shell protocol for example allows windows to request that an
-        interactive move be started, perhaps when a client-side rendered
-        titlebar is dragged.
-
-        The window manager may use the river_seat_v1.op_start_pointer request to
-        interactively move the window or ignore this event entirely.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-      <arg name="seat" type="object" interface="river_seat_v1"
-        summary="requested seat"/>
-    </event>
-
-    <event name="pointer_resize_requested">
-      <description summary="window requested interactive pointer resize">
-        This event informs the window manager that the window has requested to
-        be interactively resized using the pointer. The seat argument indicates
-        the seat for the resize.
-
-        The edges argument indicates which edges the window has requested to be
-        resized from. The edges argument will never be none and will never have
-        both top and bottom or both left and right edges set.
-
-        The xdg-shell protocol for example allows windows to request that an
-        interactive resize be started, perhaps when the corner of client-side
-        rendered decorations is dragged.
-
-        The window manager may use the river_seat_v1.op_start_pointer request to
-        interactively resize the window or ignore this event entirely.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-      <arg name="seat" type="object" interface="river_seat_v1"
-        summary="requested seat"/>
-      <arg name="edges" type="uint" enum="edges"
-        summary="requested edges"/>
-    </event>
-
-    <request name="inform_resize_start">
-      <description summary="inform the window it is being resized">
-        Inform the window that it is being resized. The window manager should
-        use this request to inform windows that are the target of an interactive
-        resize for example.
-
-        The window manager remains responsible for handling the position and
-        dimensions of the window while it is resizing.
-
-        This request modifies window management state and may only be made as
-        part of a manage sequence, see the river_window_manager_v1 description.
-      </description>
-    </request>
-
-    <request name="inform_resize_end">
-      <description summary="inform the window it no longer being resized">
-        Inform the window that it is no longer being resized. The window manager
-        should use this request to inform windows that are the target of an
-        interactive resize that the interactive resize has ended for example.
-
-        This request modifies window management state and may only be made as
-        part of a manage sequence, see the river_window_manager_v1 description.
-      </description>
-    </request>
-
-    <enum name="capabilities" bitfield="true">
-      <entry name="window_menu" value="1"/>
-      <entry name="maximize" value="2"/>
-      <entry name="fullscreen" value="4"/>
-      <entry name="minimize" value="8"/>
-    </enum>
-
-    <request name="set_capabilities">
-      <description summary="inform window of supported capabilities">
-        This request informs the window of the capabilities supported by the
-        window manager. If the window manager, for example, ignores requests to
-        be maximized from the window it should not tell the window that it
-        supports the maximize capability.
-
-        The window might use this information to, for example, only show a
-        maximize button if the window manager supports the maximize capability.
-
-        The window manager client should use this request to set capabilities
-        for all new windows. If this request is never made, the compositor will
-        inform windows that all capabilities are supported.
-
-        This request modifies window management state and may only be made as
-        part of a manage sequence, see the river_window_manager_v1 description.
-      </description>
-      <arg name="caps" type="uint" enum="capabilities"
-        summary="supported capabilities"/>
-    </request>
-
-    <event name="show_window_menu_requested">
-      <description summary="window requested that the window menu be shown">
-        The xdg-shell protocol for example allows windows to request that a
-        window menu be shown, for example when the user right clicks on client
-        side window decorations.
-
-        A window menu might include options to maximize or minimize the window.
-
-        The window manager is free to ignore this request and decide what the
-        window menu contains if it does choose to show one.
-
-        The x and y arguments indicate where the window requested that the
-        window menu be shown.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-      <arg name="x" type="int" summary="x offset from top left corner"/>
-      <arg name="y" type="int" summary="y offset from top left corner"/>
-    </event>
-
-    <event name="maximize_requested">
-      <description summary="the window requested to be maximized">
-        The xdg-shell protocol for example allows windows to request to be
-        maximized.
-
-        The window manager is free to honor this request using
-        river_window_v1.inform_maximized or ignore it.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-    </event>
-
-    <event name="unmaximize_requested">
-      <description summary="the window requested to be unmaximized">
-        The xdg-shell protocol for example allows windows to request to be
-        unmaximized.
-
-        The window manager is free to honor this request using
-        river_window_v1.inform_unmaximized or ignore it.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-    </event>
-
-    <request name="inform_maximized">
-      <description summary="inform the window that it is maximized">
-        Inform the window that it is maximized. The window might use this
-        information to adapt the style of its client-side window decorations for
-        example.
-
-        The window manager remains responsible for handling the position and
-        dimensions of the window while it is maximized.
-
-        This request modifies window management state and may only be made as
-        part of a manage sequence, see the river_window_manager_v1 description.
-      </description>
-    </request>
-
-    <request name="inform_unmaximized">
-      <description summary="inform the window that it is unmaximized">
-        Inform the window that it is unmaximized. The window might use this
-        information to adapt the style of its client-side window decorations for
-        example.
-
-        This request modifies window management state and may only be made as
-        part of a manage sequence, see the river_window_manager_v1 description.
-      </description>
-    </request>
-
-    <event name="fullscreen_requested">
-      <description summary="the window requested to be fullscreen">
-        The xdg-shell protocol for example allows windows to request that they
-        be made fullscreen and allows them to provide an optional output hint.
-
-        If the output argument is null, the window has no preference and the
-        window manager should choose an output.
-
-        The window manager is free to honor this request using
-        river_window_v1.fullscreen or ignore it.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-      <arg name="output" type="object" allow-null="true"
-        interface="river_output_v1" summary="fullscreen output requested"/>
-    </event>
-
-    <event name="exit_fullscreen_requested">
-      <description summary="the window requested to exit fullscreen">
-        The xdg-shell protocol for example allows windows to request to exit
-        fullscreen.
-
-        The window manager is free to honor this request using
-        river_window_v1.exit_fullscreen or ignore it.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-    </event>
-
-    <request name="inform_fullscreen">
-      <description summary="inform the window that it is fullscreen">
-        Inform the window that it is fullscreen. The window might use this
-        information to adapt the style of its client-side window decorations for
-        example.
-
-        This request does not affect the size/position of the window or cause it
-        to become the only window rendered, see the river_window_v1.fullscreen
-        and exit_fullscreen requests for that.
-
-        This request modifies window management state and may only be made as
-        part of a manage sequence, see the river_window_manager_v1 description.
-      </description>
-    </request>
-
-    <request name="inform_not_fullscreen">
-      <description summary="inform the window that it is not fullscreen">
-        Inform the window that it is not fullscreen. The window might use this
-        information to adapt the style of its client-side window decorations for
-        example.
-
-        This request does not affect the size/position of the window or cause it
-        to become the only window rendered, see the river_window_v1.fullscreen
-        and exit_fullscreen requests for that.
-
-        This request modifies window management state and may only be made as
-        part of a manage sequence, see the river_window_manager_v1 description.
-      </description>
-    </request>
-
-    <request name="fullscreen">
-      <description summary="make the window fullscreen">
-        Make the window fullscreen on the given output. If multiple windows are
-        fullscreen on the same output at the same time only the "top" window in
-        rendering order shall be displayed.
-
-        All river_shell_surface_v1 objects above the top fullscreen window in
-        the rendering order will continue to be rendered.
-
-        The compositor will handle the position and dimensions of the window
-        while it is fullscreen. The set_position and propose_dimensions requests
-        shall not affect the current position and dimensions of a fullscreen
-        window.
-
-        When a fullscreen request is made, the server must send a dimensions
-        event in response as soon as possible. It may not be possible to send a
-        dimensions event in the very next render sequence if, for example, the
-        window takes too long to respond. In this case, the server will send the
-        dimensions event in a future render sequence.
-
-        The compositor will clip window content, decoration surfaces, and
-        borders to the given output's dimensions while the window is fullscreen.
-        The effects of set_clip_box and set_content_clip_box are ignored while
-        the window is fullscreen.
-
-        If the output on which a window is currently fullscreen is removed, the
-        windowing state is modified as if there were an exit_fullscreen request
-        made in the same manage sequence as the river_output_v1.removed event.
-
-        This request does not inform the window that it is fullscreen, see the
-        river_window_v1.inform_fullscreen and inform_not_fullscreen requests.
-
-        This request modifies window management state and may only be made as
-        part of a manage sequence, see the river_window_manager_v1 description.
-      </description>
-      <arg name="output" type="object" interface="river_output_v1"
-        summary="fullscreen output"/>
-    </request>
-
-    <request name="exit_fullscreen">
-      <description summary="make the window not fullscreen">
-        Make the window not fullscreen.
-
-        The position and dimensions are undefined after this request is made
-        until a manage sequence in which the window manager makes the
-        propose_dimensions and set_position requests is completed.
-
-        The window manager should make propose_dimensions and set_position
-        requests in the same manage sequence as the exit_fullscreen request for
-        frame perfection.
-
-        This request does not inform the window that it is fullscreen, see the
-        river_window_v1.inform_fullscreen and inform_not_fullscreen requests.
-
-        This request modifies window management state and may only be made as
-        part of a manage sequence, see the river_window_manager_v1 description.
-      </description>
-    </request>
-
-    <event name="minimize_requested">
-      <description summary="the window requested to be minimized">
-        The xdg-shell protocol for example allows windows to request to be
-        minimized.
-
-        The window manager is free to ignore this request, hide the window, or
-        do whatever else it chooses.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-    </event>
-
-    <request name="set_clip_box" since="2">
-      <description summary="clip the window to a given box">
-        Clip the window, including borders and decoration surfaces, to the box
-        specified by the x, y, width, and height arguments. The x/y position of
-        the box is relative to the top left corner of the window.
-
-        The width and height arguments must be greater than or equal to 0.
-
-        Setting a clip box with 0 width or height disables clipping.
-
-        The clip box is ignored while the window is fullscreen.
-
-        Both set_clip_box and set_content_clip_box may be enabled simultaneously.
-
-        This request modifies rendering state and may only be made as part of a
-        render sequence, see the river_window_manager_v1 description.
-      </description>
-      <arg name="x" type="int" summary="x relative to top left window corner"/>
-      <arg name="y" type="int" summary="y relative to top left window corner"/>
-      <arg name="width" type="int" summary="clip box width"/>
-      <arg name="height" type="int" summary="clip box height"/>
-    </request>
-
-    <event name="unreliable_pid" since="2">
-      <description summary="unreliable PID of the window's creator">
-        This event gives an unreliable PID of the process that created the
-        window. Obtaining this information is inherently racy due to PID reuse.
-        Therefore, this PID must not be used for anything security sensitive.
-
-        Note also that a single process may create multiple windows, so there is
-        not necessarily a 1-to-1 mapping from PID to window. Multiple windows
-        may have the same PID.
-
-        This event is sent once when the river_window_v1 is created and never
-        sent again.
-      </description>
-      <arg name="unreliable_pid" type="int" summary="unreliable PID"/>
-    </event>
-
-    <request name="set_content_clip_box" since="3">
-      <description summary="clip the window content to a given box">
-        Clip the content of the window, excluding borders and decoration
-        surfaces, to the box specified by the x, y, width, and height arguments.
-        The x/y position of the box is relative to the top left corner of the
-        window.
-
-        Borders drawn by the compositor (see set_borders) are placed around the
-        intersection of the window content (as defined by the dimensions event)
-        and the content clip box when content clipping is enabled.
-
-        The width and height arguments must be greater than or equal to 0.
-
-        Setting a box with 0 width or height disables content clipping.
-
-        The content clip box is ignored while the window is fullscreen.
-
-        Both set_clip_box and set_content_clip_box may be enabled simultaneously.
-
-        This request modifies rendering state and may only be made as part of a
-        render sequence, see the river_window_manager_v1 description.
-      </description>
-      <arg name="x" type="int" summary="x relative to top left window corner"/>
-      <arg name="y" type="int" summary="y relative to top left window corner"/>
-      <arg name="width" type="int" summary="clip box width"/>
-      <arg name="height" type="int" summary="clip box height"/>
-    </request>
-
-    <event name="presentation_hint" since="4">
-      <description summary="presentation hint set by the window">
-        This event communicates the window's preferred presentation mode.
-
-        This event will be followed by a render_start event after all other new
-        state has been sent by the server.
-      </description>
-      <arg name="hint" type="uint" enum="river_output_v1.presentation_mode"
-        summary="presentation hint"/>
-    </event>
-
-    <event name="identifier" since="4">
-      <description summary="unique window identifier">
-        The identifier is a string that contains up to 32 printable ASCII bytes.
-        The identifier must not be an empty string.
-
-        It is compositor policy how the identifier is generated, but the following
-        properties must be upheld:
-
-        1. The identifier must uniquely identify the window. Two windows must not
-           share the same identifier.
-
-        2. The identifier must not be reused. This avoids races around window
-           creation/destruction when identifiers are used in out-of-band IPC.
-
-        If the compositor implements the ext-foreign-toplevel-list-v1 protocol,
-        the river_window_v1.identifier event must match the corresponding
-        ext_foreign_toplevel_handle_v1.identifier event.
-
-        This event is sent once when the river_window_v1 is created and never
-        sent again.
-      </description>
-      <arg name="identifier" type="string" summary="unique identifier"/>
-    </event>
-
-    <request name="set_dimension_bounds" since="4">
-      <description summary="recommend maximum dimensions to the window">
-        Recommend that the window keep its dimensions within a given
-        maximum width/height. This recommendation is only a hint and the window
-        may ignore it.
-
-        Setting the width and height to 0 indicates that there are no bounds
-        and is equivalent to having never made this request.
-
-        Setting width or height to a negative value is a protocol error.
-
-        The server should communicate this hint to an xdg_toplevel window with
-        the xdg_toplevel.configure_bounds event for example.
-
-        This request modifies window management state and may only be made as
-        part of a manage sequence, see the river_window_manager_v1 description.
-      </description>
-      <arg name="max_width" type="int" summary="maximum width"/>
-      <arg name="max_height" type="int" summary="maximum height"/>
-    </request>
-
-    <request name="set_opacity" since="4">
-      <description summary="set the window opacity">
-        Set the window opacity, from 0 (fully transparent) to 0xffffffff (fully opaque).
-        This request modifies rendering state and may only be made as part of a
-        render sequence, see the river_window_manager_v1 description.
-      </description>
-      <arg name="opacity" type="uint" summary="opacity value from 0 to 0xffffffff"/>
-    </request>
-
-    <request name="set_circular" since="4">
-      <description summary="set whether the window is circular">
-        Set whether the window is circular (1 for circular, 0 for rectangular).
-        This request modifies rendering state and may only be made as part of a
-        render sequence, see the river_window_manager_v1 description.
-      </description>
-      <arg name="circular" type="uint" summary="1 if circular, 0 otherwise"/>
-    </request>
-
-    <request name="set_blur" since="4">
-      <description summary="set whether backdrop blur is enabled">
-        Set whether backdrop blur should be enabled for this window.
-        1 to enable, 0 to disable.
-        This request modifies rendering state and may only be made as part of a
-        render sequence, see the river_window_manager_v1 description.
-      </description>
-      <arg name="blur" type="uint" summary="1 to enable, 0 to disable"/>
-    </request>
-  </interface>
-
-  <interface name="river_decoration_v1" version="4">
-    <description summary="a window decoration">
-      The rendering order of windows with decorations is follows:
-
-      1. Decorations created with get_decoration_below at the bottom
-      2. Window content
-      3. Borders configured with river_window_v1.set_borders
-      4. Decorations created with get_decoration_above at the top
-
-      The relative ordering of decoration surfaces above/below a window is
-      undefined by this protocol and left up to the compositor.
-    </description>
-
-    <enum name="error">
-      <entry name="no_commit" value="0"
-        summary="failed to commit the surface before the window manager commit"/>
-    </enum>
-
-    <request name="destroy" type="destructor">
-      <description summary="destroy the decoration object">
-        This request indicates that the client will no longer use the decoration
-        object and that it may be safely destroyed.
-      </description>
-    </request>
-
-    <request name="set_offset">
-      <description summary="set offset from the window's top left corner">
-        This request sets the offset of the decoration surface from the top left
-        corner of the window.
-
-        If this request is never sent, the x and y offsets are undefined by this
-        protocol and left up to the compositor.
-
-        This request modifies rendering state and may only be made as part of a
-        render sequence, see the river_window_manager_v1 description.
-      </description>
-      <arg name="x" type="int" summary="x relative to top left window corner"/>
-      <arg name="y" type="int" summary="y relative to top left window corner"/>
-    </request>
-
-    <request name="sync_next_commit">
-      <description summary="sync next commit with other rendering state">
-        Synchronize application of the next wl_surface.commit request on the
-        decoration surface with rest of the state atomically applied with the
-        next river_window_manager_v1.render_finish request.
-
-        The client must make a wl_surface.commit request on the decoration
-        surface after this request and before the render_finish request, failure
-        to do so is a protocol error.
-
-        This request modifies rendering state and may only be made as part of a
-        render sequence, see the river_window_manager_v1 description.
-      </description>
-    </request>
-
-    <request name="set_blur" since="4">
-      <description summary="set whether backdrop blur is enabled">
-        Set whether backdrop blur should be enabled for this decoration.
-        1 to enable, 0 to disable.
-        This request modifies rendering state and may only be made as part of a
-        render sequence, see the river_window_manager_v1 description.
-      </description>
-      <arg name="blur" type="uint" summary="1 to enable, 0 to disable"/>
-    </request>
-  </interface>
-
-  <interface name="river_shell_surface_v1" version="4">
-    <description summary="a surface for window manager UI">
-      The window manager might use a shell surface to display a status bar,
-      background image, desktop notifications, launcher, desktop menu, or
-      whatever else it wants.
-    </description>
-
-    <enum name="error">
-      <entry name="node_exists" value="0"
-        summary="shell surface already has a node object"/>
-      <entry name="no_commit" value="1"
-        summary="failed to commit the surface before the window manager commit"/>
-    </enum>
-
-    <request name="destroy" type="destructor">
-      <description summary="destroy the shell surface object">
-        This request indicates that the client will no longer use the shell
-        surface object and that it may be safely destroyed.
-      </description>
-    </request>
-
-    <request name="get_node">
-      <description summary="get the shell surface's render list node">
-        Get the node in the render list corresponding to the shell surface.
-
-        It is a protocol error to make this request more than once for a single
-        shell surface.
-      </description>
-      <arg name="id" type="new_id" interface="river_node_v1" summary="new node"/>
-    </request>
-
-    <request name="sync_next_commit">
-      <description summary="sync next surface commit to window manager commit">
-        Synchronize application of the next wl_surface.commit request on the
-        shell surface with rest of the rendering state atomically applied with
-        the next river_window_manager_v1.render_finish request.
-
-        The client must make a wl_surface.commit request on the shell surface
-        after this request and before the render_finish request, failure to do
-        so is a protocol error.
-
-        This request modifies rendering state and may only be made as part of a
-        render sequence, see the river_window_manager_v1 description.
-      </description>
-    </request>
-  </interface>
-
-  <interface name="river_node_v1" version="4">
-    <description summary="a node in the render list">
-      The render list is a list of nodes that determines the rendering order of
-      the compositor. Nodes may correspond to windows or shell surfaces. The
-      relative ordering of nodes may be changed with the place_above and
-      place_below requests, changing the rendering order.
-
-      The initial position of a node in the render list is undefined, the window
-      manager client must use the place_above or place_below request to
-      guarantee a specific rendering order.
-    </description>
-
-    <request name="destroy" type="destructor">
-      <description summary="destroy the decoration object">
-        This request indicates that the client will no longer use the node
-        object and that it may be safely destroyed.
-      </description>
-    </request>
-
-    <request name="set_position">
-      <description summary="set absolute position of the node">
-        Set the absolute position of the node in the compositor's logical
-        coordinate space. The x and y coordinates may be positive or negative.
-
-        Note that the position of a river_window_v1 refers to the position of
-        the window content and is unaffected by the presence of borders or
-        decoration surfaces.
-
-        If this request is never sent, the position of the node is undefined by
-        this protocol and left up to the compositor.
-
-        This request modifies rendering state and may only be made as part of a
-        render sequence, see the river_window_manager_v1 description.
-      </description>
-      <arg name="x" type="int" summary="global x coordinate"/>
-      <arg name="y" type="int" summary="global y coordinate"/>
-    </request>
-
-    <request name="place_top">
-      <description summary="place node above all other nodes">
-        This request places the node above all other nodes in the compositor's
-        render list.
-
-        This request modifies rendering state and may only be made as part of a
-        render sequence, see the river_window_manager_v1 description.
-      </description>
-    </request>
-
-    <request name="place_bottom">
-      <description summary="place node below all other nodes">
-        This request places the node below all other nodes in the compositor's
-        render list.
-
-        This request modifies rendering state and may only be made as part of a
-        render sequence, see the river_window_manager_v1 description.
-      </description>
-    </request>
-
-    <request name="place_above">
-      <description summary="place node above another node">
-        This request places the node directly above another node in the
-        compositor's render list.
-
-        Attempting to place a node above itself has no effect.
-
-        This request modifies rendering state and may only be made as part of a
-        render sequence, see the river_window_manager_v1 description.
-      </description>
-      <arg name="other" type="object" interface="river_node_v1"
-        summary="other node"/>
-    </request>
-
-    <request name="place_below">
-      <description summary="place node below another node">
-        This request places the node directly below another node in the
-        compositor's render list.
-
-        Attempting to place a node below itself has no effect.
-
-        This request modifies rendering state and may only be made as part of a
-        render sequence, see the river_window_manager_v1 description.
-      </description>
-      <arg name="other" type="object" interface="river_node_v1"
-        summary="other node"/>
-    </request>
-  </interface>
-
-  <interface name="river_output_v1" version="4">
-    <description summary="a logical output">
-      An area in the compositor's logical coordinate space that should be
-      treated as a single output for window management purposes. This area may
-      correspond to a single physical output or multiple physical outputs in the
-      case of mirroring or tiled monitors depending on the hardware and
-      compositor configuration.
-    </description>
-
-    <request name="destroy" type="destructor">
-      <description summary="destroy the output object">
-        This request indicates that the client will no longer use the output
-        object and that it may be safely destroyed.
-
-        This request should be made after the river_output_v1.removed event is
-        received to complete destruction of the output.
-      </description>
-    </request>
-
-    <event name="removed">
-      <description summary="the output is removed">
-        This event indicates that the logical output is no longer conceptually
-        part of window management space.
-
-        The server will send no further events on this object and ignore any
-        request (other than river_output_v1.destroy) made after this event is
-        sent. The client should destroy this object with the
-        river_output_v1.destroy request to free up resources.
-
-        This event may be sent because a corresponding physical output has been
-        physically unplugged or because some output configuration has changed.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-    </event>
-
-    <event name="wl_output">
-      <description summary="corresponding wl_output">
-        The wl_output object corresponding to the river_output_v1. The argument
-        is the global name of the wl_output advertised with wl_registry.global.
-
-        It is guaranteed that the corresponding wl_output is advertised before
-        this event is sent.
-
-        This event is sent exactly once. The wl_output associated with a
-        river_output_v1 cannot change. It is guaranteed that there is a 1-to-1
-        mapping between wl_output and river_output_v1 objects.
-
-        The global_remove event for the corresponding wl_output may be sent
-        before the river_output_v1.removed event. This is due to the fact that
-        river_output_v1 state changes are synced to the river window management
-        manage sequence while changes to globals are not.
-
-        Rationale: The window manager may need information provided by the
-        wl_output interface such as the name/description. It also may need the
-        wl_output object to start screencopy for example.
-      </description>
-      <arg name="name" type="uint" summary="name of the wl_output global"/>
-    </event>
-
-    <event name="position">
-      <description summary="output position">
-        This event indicates the position of the output in the compositor's
-        logical coordinate space. The x and y coordinates may be positive or
-        negative.
-
-        This event is sent once when the river_output_v1 is created and again
-        whenever the position changes.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-
-        The server must guarantee that the position and dimensions events do not
-        cause the areas of multiple logical outputs to overlap when the
-        corresponding manage_start event is received.
-      </description>
-      <arg name="x" type="int" summary="global x coordinate"/>
-      <arg name="y" type="int" summary="global y coordinate"/>
-    </event>
-
-    <event name="dimensions">
-      <description summary="output dimensions">
-        This event indicates the dimensions of the output in the compositor's
-        logical coordinate space. The width and height will always be strictly
-        greater than zero.
-
-        This event is sent once when the river_output_v1 is created and again
-        whenever the dimensions change.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-
-        The server must guarantee that the position and dimensions events do not
-        cause the areas of multiple logical outputs to overlap when the
-        corresponding manage_start event is received.
-      </description>
-      <arg name="width" type="int" summary="output width"/>
-      <arg name="height" type="int" summary="output height"/>
-    </event>
-
-    <enum name="error" since="4">
-      <entry name="invalid_presentation_mode" value="0" since="4"
-        summary="invalid presentation mode enum value"/>
-    </enum>
-
-    <enum name="presentation_mode" since="4">
-      <entry name="vsync" value="0">
-        <description summary="tearing-free presentation">
-          Output page-flips should be synchronized to the vertical blanking
-          period, eliminating tearing. This is the default presentation mode.
-        </description>
-      </entry>
-      <entry name="async" value="1">
-        <description summary="asynchronous presentation">
-          Output page-flips should not be synchronized to the vertical blanking
-          period, visual screen tearing may occur.
-        </description>
-      </entry>
-    </enum>
-
-    <request name="set_presentation_mode" since="4">
-      <description summary="set the preferred presentation mode">
-        Set the preferred presentation mode of the output. The compositor should
-        always respect the preference of the window manager if possible. If this
-        request is never made, the preferred presentation mode is vsync.
-
-        This request modifies rendering state and may only be made as part of a
-        render sequence, see the river_window_manager_v1 description.
-      </description>
-      <arg name="mode" type="uint" enum="presentation_mode"
-        summary="preferred presentation mode"/>
-    </request>
-  </interface>
-
-  <interface name="river_seat_v1" version="4">
-    <description summary="a window management seat">
-      This object represents a single user's collection of input devices. It
-      allows the window manager to route keyboard input to windows, get
-      high-level information about pointer input, define pointer bindings, etc.
-
-      For keyboard bindings, see the river-xkb-bindings-v1 protocol.
-
-      Since version 4: The cursor surface/shape set by the window manager on the
-      wl_pointer of this seat is used when no client has pointer focus, for
-      example during a pointer operation. Since the window manager is allowed to
-      set cursor surface/shape even when it does not have pointer focus, the
-      compositor must ignore the serial argument of wl_pointer.set_cursor and
-      wp_cursor_shape_device_v1.set_shape requests made by the window manager.
-
-      The most recent cursor surface/shape set by the window manager is
-      remembered by the compositor and restored whenever no client has pointer
-      focus. If the window manager never sets a cursor surface/shape, the
-      "default" shape is used.
-    </description>
-
-    <request name="destroy" type="destructor">
-      <description summary="destroy the seat object">
-        This request indicates that the client will no longer use the seat
-        object and that it may be safely destroyed.
-
-        This request should be made after the river_seat_v1.removed event is
-        received to complete destruction of the seat.
-      </description>
-    </request>
-
-    <event name="removed">
-      <description summary="the seat is removed">
-        This event indicates that seat is no longer in use and should be
-        destroyed.
-
-        The server will send no further events on this object and ignore any
-        request (other than river_seat_v1.destroy) made after this event is
-        sent.  The client should destroy this object with the
-        river_seat_v1.destroy request to free up resources.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-    </event>
-
-    <event name="wl_seat">
-      <description summary="corresponding wl_seat">
-        The wl_seat object corresponding to the river_seat_v1. The argument is
-        the global name of the wl_seat advertised with wl_registry.global.
-
-        It is guaranteed that the corresponding wl_seat is advertised before
-        this event is sent.
-
-        This event is sent exactly once. The wl_seat associated with a
-        river_seat_v1 cannot change. It is guaranteed that there is a 1-to-1
-        mapping between wl_seat and river_seat_v1 objects.
-
-        The global_remove event for the corresponding wl_seat may be sent before
-        the river_seat_v1.removed event. This is due to the fact that
-        river_seat_v1 state changes are synced to the river window management
-        manage sequence while changes to globals are not.
-
-        Rationale: The window manager may want to trigger window management
-        state changes based on normal input events received by its shell
-        surfaces for example.
-      </description>
-      <arg name="name" type="uint" summary="name of the wl_seat global"/>
-    </event>
-
-    <request name="focus_window">
-      <description summary="give keyboard focus to a window">
-        Request that the compositor send keyboard input to the given window.
-
-        This request modifies window management state and may only be made as
-        part of a manage sequence, see the river_window_manager_v1 description.
-      </description>
-      <arg name="window" type="object" interface="river_window_v1"
-        summary="window to focus"/>
-    </request>
-
-    <request name="focus_shell_surface">
-      <description summary="give keyboard focus to a shell_surface">
-        Request that the compositor send keyboard input to the given shell
-        surface.
-
-        This request modifies window management state and may only be made as
-        part of a manage sequence, see the river_window_manager_v1 description.
-      </description>
-      <arg name="shell_surface" type="object" interface="river_shell_surface_v1"
-        summary="shell surface to focus"/>
-    </request>
-
-    <request name="clear_focus">
-      <description summary="clear keyboard focus">
-        Request that the compositor not send keyboard input to any client.
-
-        This request modifies window management state and may only be made as
-        part of a manage sequence, see the river_window_manager_v1 description.
-      </description>
-    </request>
-
-    <event name="pointer_enter">
-      <description summary="pointer entered a window">
-        The seat's pointer entered the given window's area.
-
-        The area of a window is defined to include the area defined by the
-        window dimensions, borders configured using river_window_v1.set_borders,
-        and the input regions of decoration surfaces. In particular, it does not
-        include input regions of surfaces belonging to the window that extend
-        outside the window dimensions.
-
-        The pointer of a seat may only enter a single window at a time. When the
-        pointer moves between windows, the pointer_leave event for the old
-        window must be sent before the pointer_enter event for the new window.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-      <arg name="window" type="object" interface="river_window_v1"
-        summary="window entered"/>
-    </event>
-
-    <event name="pointer_leave">
-      <description summary="pointer left the entered window">
-        The seat's pointer left the window for which pointer_enter was most
-        recently sent. See pointer_enter for details.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-    </event>
-
-    <event name="window_interaction">
-      <description summary="a window has been interacted with">
-        A window has been interacted with beyond the pointer merely passing over
-        it. This event might be sent due to a pointer button press or due to a
-        touch/tablet tool interaction with the window.
-
-        There are no guarantees regarding how this event is sent in relation to
-        the pointer_enter and pointer_leave events as the interaction may use
-        touch or tablet tool input.
-
-        Rationale: this event gives window managers necessary information to
-        determine when to send keyboard focus, raise a window that already has
-        keyboard focus, etc. Rather than expose all pointer, touch, and tablet
-        events to window managers, a policy over mechanism approach is taken.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-      <arg name="window" type="object" interface="river_window_v1"
-        summary="window interacted with"/>
-    </event>
-
-    <event name="shell_surface_interaction">
-      <description summary="a shell surface has been interacted with">
-        A shell surface has been interacted with beyond the pointer merely
-        passing over it. This event might be sent due to a pointer button press
-        or due to a touch/tablet tool interaction with the shell_surface.
-
-        There are no guarantees regarding how this event is sent in relation to
-        the pointer_enter and pointer_leave events as the interaction may use
-        touch or tablet tool input.
-
-        Rationale: While the shell surface does receive all wl_pointer,
-        wl_touch, etc. input events for the surface directly, these events do
-        not necessarily trigger a manage sequence and therefore do not allow the
-        window manager to update focus or perform other actions in response to
-        the input in a race-free way.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-      <arg name="shell_surface" type="object" interface="river_shell_surface_v1"
-        summary="shell surface interacted with"/>
-    </event>
-
-    <request name="op_start_pointer">
-      <description summary="start an interactive pointer operation">
-        Start an interactive pointer operation. During the operation, op_delta
-        events will be sent based on pointer input.
-
-        When all pointer buttons are released, the op_release event is sent.
-
-        The pointer operation continues until the op_end request is made during
-        a manage sequence and that manage sequence is finished.
-
-        The window manager may use this operation to implement interactive
-        move/resize of windows by setting the position of windows and proposing
-        dimensions based off of the op_delta events.
-
-        This request is ignored if an operation is already in progress.
-
-        The compositor must ensure that no client has pointer focus from this
-        seat during the pointer operation. This means that the window manager
-        has control over the pointer's cursor surface/shape during the pointer
-        operation. See the river_seat_v1 description.
-
-        This request modifies window management state and may only be made as
-        part of a manage sequence, see the river_window_manager_v1 description.
-      </description>
-    </request>
-
-    <event name="op_delta">
-      <description summary="total cumulative motion since op start">
-        This event indicates the total change in position since the start of the
-        operation of the pointer/touch point/etc.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-      <arg name="dx" type="int" summary="total change in x"/>
-      <arg name="dy" type="int" summary="total change in y"/>
-    </event>
-
-    <event name="op_release">
-      <description summary="operation input has been released">
-        The input driving the current interactive operation has been released.
-        For a pointer op for example, all pointer buttons have been released.
-
-        Depending on the op type, op_delta events may continue to be sent until
-        the op is ended with the op_end request.
-
-        This event is sent at most once during an interactive operation.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-    </event>
-
-    <request name="op_end">
-      <description summary="end an interactive operation">
-        End an interactive operation.
-
-        This request is ignored if there is no operation in progress.
-
-        This request modifies window management state and may only be made as
-        part of a manage sequence, see the river_window_manager_v1 description.
-      </description>
-    </request>
-
-    <enum name="modifiers" bitfield="true">
-      <description summary="a set of keyboard modifiers">
-        This enum is used to describe the keyboard modifiers that must be held
-        down to trigger a key binding or pointer binding.
-
-        Note that river and wlroots use the values 2 and 16 for capslock and
-        numlock internally. It doesn't make sense to use locked modifiers for
-        bindings however so these values are not included in this enum.
-      </description>
-      <entry name="none" value="0"/>
-      <entry name="shift" value="1"/>
-      <entry name="ctrl" value="4"/>
-      <entry name="mod1" value="8" summary="commonly called alt"/>
-      <entry name="mod3" value="32"/>
-      <entry name="mod4" value="64" summary="commonly called super or logo"/>
-      <entry name="mod5" value="128"/>
-    </enum>
-
-    <request name="get_pointer_binding">
-      <description summary="define a new pointer binding">
-        Define a pointer binding in terms of a pointer button, keyboard
-        modifiers, and other configurable properties.
-
-        The button argument is a Linux input event code defined in the
-        linux/input-event-codes.h header file (e.g. BTN_RIGHT).
-
-        The new pointer binding is not enabled until initial configuration is
-        completed and the enable request is made during a manage sequence.
-      </description>
-      <arg name="id" type="new_id" interface="river_pointer_binding_v1"
-        summary="new pointer binding"/>
-      <arg name="button" type="uint" summary="a Linux input event code"/>
-      <arg name="modifiers" type="uint" enum="modifiers"
-        summary="keyboard modifiers"/>
-    </request>
-
-    <request name="set_xcursor_theme" since="2">
-      <description summary="set the xcursor theme for the seat">
-        Set the XCursor theme for the seat. This theme is used for cursors
-        rendered by the compositor, but not necessarily for cursors rendered by
-        clients.
-
-        Note: The window manager may also wish to set the XCURSOR_THEME and
-        XCURSOR_SIZE environment variable for programs it starts.
-      </description>
-      <arg name="name" type="string" summary="xcursor theme name"/>
-      <arg name="size" type="uint" summary="cursor size"/>
-    </request>
-
-    <event name="pointer_position" since="2">
-      <description summary="The current position of the pointer">
-        The current position of the pointer in the compositor's logical
-        coordinate space.
-
-        This state is special in that a change in pointer position alone must
-        not cause the compositor to start a manage sequence.
-
-        Assuming the seat has a pointer, this event must be sent in every manage
-        sequence unless there is no change in x/y position since the last time this
-        event was sent.
-      </description>
-      <arg name="x" type="int" summary="global x coordinate"/>
-      <arg name="y" type="int" summary="global y coordinate"/>
-    </event>
-
-    <request name="pointer_warp" since="3">
-      <description summary="warp the pointer to a given position">
-        Warp the pointer to the given position in the compositor's logical
-        coordinate space.
-
-        If the given position is outside the bounds of all outputs, the pointer
-        will be warped to the closest point inside an output instead.
-
-        This request modifies window management state and may only be made as
-        part of a manage sequence, see the river_window_manager_v1 description.
-      </description>
-      <arg name="x" type="int" summary="global x coordinate"/>
-      <arg name="y" type="int" summary="global y coordinate"/>
-    </request>
-  </interface>
-
-  <interface name="river_pointer_binding_v1" version="4">
-    <description summary="configure a pointer binding, receive trigger events">
-      This object allows the window manager to configure a pointer binding and
-      receive events when the binding is triggered.
-
-      The new pointer binding is not enabled until the enable request is made
-      during a manage sequence.
-
-      Normally, all pointer button events are sent to the surface with pointer
-      focus by the compositor. Pointer button events that trigger a pointer
-      binding are not sent to the surface with pointer focus.
-
-      If multiple pointer bindings would be triggered by a single physical
-      pointer event on the compositor side, it is compositor policy which
-      pointer binding(s) will receive press/release events or if all of the
-      matched pointer bindings receive press/release events.
-    </description>
-
-    <request name="destroy" type="destructor">
-      <description summary="destroy the pointer binding object">
-        This request indicates that the client will no longer use the pointer
-        binding object and that it may be safely destroyed.
-      </description>
-    </request>
-
-    <request name="enable">
-      <description summary="enable the pointer binding">
-        This request should be made after all initial configuration has been
-        completed and the window manager wishes the pointer binding to be able
-        to be triggered.
-
-        This request modifies window management state and may only be made as
-        part of a manage sequence, see the river_window_manager_v1 description.
-      </description>
-    </request>
-
-    <request name="disable">
-      <description summary="disable the pointer binding">
-        This request may be used to temporarily disable the pointer binding. It
-        may be later re-enabled with the enable request.
-
-        This request modifies window management state and may only be made as
-        part of a manage sequence, see the river_window_manager_v1 description.
-      </description>
-    </request>
-
-    <event name="pressed">
-      <description summary="the bound pointer button has been pressed">
-        This event indicates that the pointer button triggering the binding has
-        been pressed.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-
-        The compositor should wait for the manage sequence to complete before
-        processing further input events. This allows the window manager client
-        to, for example, modify key bindings and keyboard focus without racing
-        against future input events. The window manager should of course respond
-        as soon as possible as the capacity of the compositor to buffer incoming
-        input events is finite.
-      </description>
-    </event>
-
-    <event name="released">
-      <description summary="the bound pointer button has been released">
-        This event indicates that the pointer button triggering the binding has
-        been released.
-
-        Releasing the modifiers for the binding without releasing the pointer
-        button does not trigger the release event. This event is sent when the
-        pointer button is released, even if the modifiers have changed since the
-        pressed event.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-
-        The compositor should wait for the manage sequence to complete before
-        processing further input events. This allows the window manager client
-        to, for example, modify key bindings and keyboard focus without racing
-        against future input events. The window manager should of course respond
-        as soon as possible as the capacity of the compositor to buffer incoming
-        input events is finite.
-      </description>
-    </event>
-  </interface>
-</protocol>
diff --git a/cce-server/protocol/river-xkb-bindings-v1.xml b/cce-server/protocol/river-xkb-bindings-v1.xml
deleted file mode 100644
index 55fb72f..0000000
--- a/cce-server/protocol/river-xkb-bindings-v1.xml
+++ /dev/null
@@ -1,314 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<protocol name="river_xkb_bindings_v1">
-  <copyright>
-    SPDX-FileCopyrightText: © 2025 Isaac Freund
-    SPDX-License-Identifier: MIT
-
-    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 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="xkbcommon-based key bindings">
-    This protocol allows the river-window-management-v1 window manager to
-    define key bindings in terms of xkbcommon keysyms and other configurable
-    properties.
-
-    The key words "must", "must not", "required", "shall", "shall not",
-    "should", "should not", "recommended", "may", and "optional" in this
-    document are to be interpreted as described in IETF RFC 2119.
-  </description>
-
-  <interface name="river_xkb_bindings_v1" version="3">
-    <description summary="xkbcommon bindings global interface">
-      This global interface should only be advertised to the client if the
-      river_window_manager_v1 global is also advertised.
-    </description>
-
-    <enum name="error" since="2">
-      <entry name="object_already_created" value="0" since="2"/>
-    </enum>
-
-    <request name="destroy" type="destructor">
-      <description summary="destroy the river_xkb_bindings_v1 object">
-        This request indicates that the client will no longer use the
-        river_xkb_bindings_v1 object.
-      </description>
-    </request>
-
-    <request name="get_xkb_binding">
-      <description summary="define a new xkbcommon key binding">
-        Define a key binding for the given seat in terms of an xkbcommon keysym
-        and other configurable properties.
-
-        The new key binding is not enabled until initial configuration is
-        completed and the enable request is made during a manage sequence.
-      </description>
-      <arg name="seat" type="object" interface="river_seat_v1"/>
-      <arg name="id" type="new_id" interface="river_xkb_binding_v1"/>
-      <arg name="keysym" type="uint" summary="an xkbcommon keysym"/>
-      <arg name="modifiers" type="uint" enum="river_seat_v1.modifiers"/>
-    </request>
-
-    <request name="get_seat" since="2">
-      <description summary="manage seat-specific state">
-        Create an object to manage seat-specific xkb bindings state.
-
-        It is a protocol error to make this request more than once for a given
-        river_seat_v1 object.
-      </description>
-      <arg name="id" type="new_id" interface="river_xkb_bindings_seat_v1"/>
-      <arg name="seat" type="object" interface="river_seat_v1"/>
-    </request>
-  </interface>
-
-  <interface name="river_xkb_binding_v1" version="3">
-    <description summary="configure a xkb key binding, receive trigger events">
-      This object allows the window manager to configure a xkbcommon key binding
-      and receive events when the key binding is triggered.
-
-      The new key binding is not enabled until the enable request is made during
-      a manage sequence.
-
-      Normally, all key events are sent to the surface with keyboard focus by
-      the compositor. Key events that trigger a key binding are not sent to the
-      surface with keyboard focus.
-
-      If multiple key bindings would be triggered by a single physical key event
-      on the compositor side, it is compositor policy which key binding(s) will
-      receive press/release events or if all of the matched key bindings receive
-      press/release events.
-
-      Key bindings might be matched by the same physical key event due to shared
-      keysym and modifiers. The layout override feature may also cause the same
-      physical key event to trigger two key bindings with different keysyms and
-      different layout overrides configured.
-    </description>
-
-    <request name="destroy" type="destructor">
-      <description summary="destroy the xkb binding object">
-        This request indicates that the client will no longer use the xkb key
-        binding object and that it may be safely destroyed.
-      </description>
-    </request>
-
-    <request name="set_layout_override">
-      <description summary="override currently active xkb layout">
-        Specify an xkb layout that should be used to translate key events for
-        the purpose of triggering this key binding irrespective of the currently
-        active xkb layout.
-
-        The layout argument is a 0-indexed xkbcommon layout number for the
-        keyboard that generated the key event.
-
-        If this request is never made, the currently active xkb layout of the
-        keyboard that generated the key event will be used.
-
-        This request modifies window management state and may only be made as
-        part of a manage sequence, see the river_window_manager_v1 description.
-      </description>
-      <arg name="layout" type="uint" summary="0-indexed xkbcommon layout"/>
-    </request>
-
-    <request name="enable">
-      <description summary="enable the key binding">
-        This request should be made after all initial configuration has been
-        completed and the window manager wishes the key binding to be able to be
-        triggered.
-
-        This request modifies window management state and may only be made as
-        part of a manage sequence, see the river_window_manager_v1 description.
-      </description>
-    </request>
-
-    <request name="disable">
-      <description summary="disable the key binding">
-        This request may be used to temporarily disable the key binding. It may
-        be later re-enabled with the enable request.
-
-        This request modifies window management state and may only be made as
-        part of a manage sequence, see the river_window_manager_v1 description.
-      </description>
-    </request>
-
-    <event name="pressed">
-      <description summary="the key triggering the binding has been pressed">
-        This event indicates that the physical key triggering the binding has
-        been pressed.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-
-        The compositor should wait for the manage sequence to complete before
-        processing further input events. This allows the window manager client
-        to, for example, modify key bindings and keyboard focus without racing
-        against future input events. The window manager should of course respond
-        as soon as possible as the capacity of the compositor to buffer incoming
-        input events is finite.
-      </description>
-    </event>
-
-    <event name="released">
-      <description summary="the key triggering the binding has been released">
-        This event indicates that the physical key triggering the binding has
-        been released.
-
-        Releasing the modifiers for the binding without releasing the "main"
-        physical key that produces the bound keysym does not trigger the release
-        event. This event is sent when the "main" key is released, even if the
-        modifiers have changed since the pressed event.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-
-        The compositor should wait for the manage sequence to complete before
-        processing further input events. This allows the window manager client
-        to, for example, modify key bindings and keyboard focus without racing
-        against future input events. The window manager should of course respond
-        as soon as possible as the capacity of the compositor to buffer incoming
-        input events is finite.
-      </description>
-    </event>
-
-    <event name="stop_repeat" since="2">
-      <description summary="repeating should be stopped">
-        This event indicates that repeating should be stopped for the binding if
-        the window manager has been repeating some action since the pressed
-        event.
-
-        This event is generally sent when some other (possibly unbound) key is
-        pressed after the pressed event is sent and before the released event
-        is sent for this binding.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-    </event>
-  </interface>
-
-  <interface name="river_xkb_bindings_seat_v1" version="3">
-    <description summary="xkb bindings seat">
-      This object manages xkb bindings state associated with a specific seat.
-    </description>
-
-    <request name="destroy" type="destructor" since="2">
-      <description summary="destroy the object">
-        This request indicates that the client will no longer use the object and
-        that it may be safely destroyed.
-      </description>
-    </request>
-
-    <request name="ensure_next_key_eaten" since="2">
-      <description summary="ensure the next key press event is eaten">
-        Ensure that the next non-modifier key press and corresponding release
-        events for this seat are not sent to the currently focused surface.
-
-        If the next non-modifier key press triggers a binding, the
-        pressed/released events are sent to the river_xkb_binding_v1 object as
-        usual.
-
-        If the next non-modifier key press does not trigger a binding, the
-        ate_unbound_key event is sent instead.
-
-        Rationale: the window manager may wish to implement "chorded"
-        keybindings where triggering a binding activates a "submap" with a
-        different set of keybindings. Without a way to eat the next key
-        press event, there is no good way for the window manager to know that it
-        should error out and exit the submap when a key not bound in the submap
-        is pressed.
-
-        This request modifies window management state and may only be made as
-        part of a manage sequence, see the river_window_manager_v1 description.
-      </description>
-    </request>
-
-    <request name="cancel_ensure_next_key_eaten" since="2">
-      <description summary="cancel an ensure_next_key_eaten request">
-        This requests cancels the effect of the latest ensure_next_key_eaten
-        request if no key has been eaten due to the request yet. This request
-        has no effect if a key has already been eaten or no
-        ensure_next_key_eaten was made.
-
-        Rationale: the window manager may wish cancel an uncompleted "chorded"
-        keybinding after a timeout of a few seconds. Note that since this
-        timeout use-case requires the window manager to trigger a manage sequence
-        with the river_window_manager_v1.manage_dirty request it is possible that
-        the ate_unbound_key key event may be sent before the window manager has
-        a chance to make the cancel_ensure_next_key_eaten request.
-
-        This request modifies window management state and may only be made as
-        part of a manage sequence, see the river_window_manager_v1 description.
-      </description>
-    </request>
-
-    <event name="ate_unbound_key" since="2">
-      <description summary="an unbound key press event was eaten">
-        An unbound key press event was eaten due to the ensure_next_key_eaten
-        request.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-      </description>
-    </event>
-
-    <request name="modifiers_watch" since="3">
-      <description summary="watch for change in active modifiers">
-        Request that the server send the modifiers_update event whenever a state
-        change occurs for at least one of the modifiers specified by the
-        modifiers argument.
-
-        The window manager should make this request with the modifiers argument
-        set to 0 when it no longer wishes to take action based on a change in
-        modifiers.
-
-        This request modifies window management state and may only be made as
-        part of a manage sequence, see the river_window_manager_v1 description.
-      </description>
-      <arg name="modifiers" type="uint" enum="river_seat_v1.modifiers"/>
-    </request>
-
-    <event name="modifiers_update" since="3">
-      <description summary="active modifiers for the seat changed">
-        The set of currently active modifiers for the seat changed. This event
-        is only sent when there is a change in state for modifiers marked as
-        watched using the modifiers_watch request.
-
-        The old and new arguments convey the set of modifiers active before and
-        after the change. All modifiers are included in the old and new
-        arguments, including modifiers that are not watched.
-
-        Since this event is only sent when there is a change in state for
-        watched modifiers, it follows that at least one watched modifier is
-        active in old but inactive in new or vice-versa.
-
-        This event will be followed by a manage_start event after all other new
-        state has been sent by the server.
-
-        The compositor should wait for the manage sequence to complete before
-        processing further input events. This allows the window manager client
-        to, for example, modify key bindings and keyboard focus without racing
-        against future input events. The window manager should of course respond
-        as soon as possible as the capacity of the compositor to buffer incoming
-        input events is finite.
-      </description>
-      <arg name="old" type="uint" enum="river_seat_v1.modifiers"
-        summary="previously active modifiers"/>
-      <arg name="new" type="uint" enum="river_seat_v1.modifiers"
-        summary="currently active modifiers"/>
-    </event>
-  </interface>
-</protocol>
diff --git a/cce-server/protocol/river-xkb-config-v1.xml b/cce-server/protocol/river-xkb-config-v1.xml
deleted file mode 100644
index f73ec67..0000000
--- a/cce-server/protocol/river-xkb-config-v1.xml
+++ /dev/null
@@ -1,287 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<protocol name="river_xkb_config_v1">
-  <copyright>
-    SPDX-FileCopyrightText: © 2026 Isaac Freund
-    SPDX-License-Identifier: MIT
-
-    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 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="configure xkbcommon keyboards">
-    This protocol allow a client to set the xkbcommon keymap of individual
-    keyboard input devices. It also allows switching between the layouts of a
-    keymap and toggling capslock/numlock state.
-
-    The key words "must", "must not", "required", "shall", "shall not",
-    "should", "should not", "recommended", "may", and "optional" in this
-    document are to be interpreted as described in IETF RFC 2119.
-  </description>
-
-  <interface name="river_xkb_config_v1" version="2">
-    <description summary="xkb config global interface">
-      Global interface for configuring xkb devices.
-
-      This global should only be advertised if river_input_manager_v1 is
-      advertised as well.
-    </description>
-
-    <enum name="error">
-      <entry name="invalid_destroy" value="0"/>
-      <entry name="invalid_format" value="1"/>
-    </enum>
-
-    <request name="stop">
-      <description summary="stop sending events">
-        This request indicates that the client no longer wishes to receive
-        events on this object.
-
-        The Wayland protocol is asynchronous, which means the server may send
-        further events until the stop request is processed. The client must wait
-        for a river_xkb_config_v1.finished event before destroying this object.
-      </description>
-    </request>
-
-    <event name="finished">
-      <description summary="the server has finished with the object">
-        This event indicates that the server will send no further events on this
-        object. The client should destroy the object. See
-        river_xkb_config_v1.destroy for more information.
-      </description>
-    </event>
-
-    <request name="destroy" type="destructor">
-      <description summary="destroy the river_xkb_config_v1 object">
-        This request should be called after the finished event has been received
-        to complete destruction of the object.
-
-        It is a protocol error to make this request before the finished event
-        has been received.
-
-        If a client wishes to destroy this object it should send a
-        river_xkb_config_v1.stop request and wait for a
-        river_xkb_config_v1.finished event. Once the finished event is received
-        it is safe to destroy this object and any other objects created through
-        this interface.
-      </description>
-    </request>
-
-    <enum name="keymap_format">
-      <entry name="text_v1" value="1" summary="XKB_KEYMAP_FORMAT_TEXT_V1"/>
-      <entry name="text_v2" value="2" summary="XKB_KEYMAP_FORMAT_TEXT_V2"/>
-    </enum>
-
-    <request name="create_keymap">
-      <description summary="create a keymap object">
-        The server must be able to mmap the fd with MAP_PRIVATE.
-        The server will fstat the fd to obtain the size of the keymap.
-        The client must not modify the contents of the fd after making this request.
-        The client should seal the fd with fcntl.
-      </description>
-      <arg name="id" type="new_id" interface="river_xkb_keymap_v1"/>
-      <arg name="fd" type="fd"/>
-      <arg name="format" type="uint" enum="keymap_format"/>
-    </request>
-
-    <event name="xkb_keyboard">
-      <description summary="new xkb keyboard">
-        A new xkbcommon keyboard has been created. Not every
-        river_input_device_v1 is necessarily an xkbcommon keyboard as well.
-      </description>
-      <arg name="id" type="new_id" interface="river_xkb_keyboard_v1"/>
-    </event>
-  </interface>
-
-  <interface name="river_xkb_keymap_v1" version="2">
-    <description summary="xkbcommon keymap">
-      This object is the result of attempting to create an xkbcommon keymap.
-    </description>
-
-    <request name="destroy" type="destructor">
-      <description summary="destroy the keymap object">
-        This request indicates that the client will no longer use the keymap
-        object and that it may be safely destroyed.
-      </description>
-    </request>
-
-    <event name="success">
-      <description summary="keymap creation succeeded">
-        The keymap object was successfully created and may be used with the
-        river_xkb_keyboard_v1.set_keymap request.
-      </description>
-    </event>
-
-    <event name="failure">
-      <description summary="keymap creation failed">
-        The compositor failed to create a keymap from the given parameters.
-
-        It is a protocol error to use this keymap object with
-        river_xkb_keyboard_v1.set_keymap.
-      </description>
-      <arg name="error_msg" type="string"/>
-    </event>
-  </interface>
-
-  <interface name="river_xkb_keyboard_v1" version="2">
-    <description summary="xkbcommon keyboard device">
-      This object represent a physical keyboard which has its configuration and
-      state managed by xkbcommon.
-    </description>
-
-    <enum name="error">
-      <entry name="invalid_keymap" value="0"/>
-    </enum>
-
-    <request name="destroy" type="destructor">
-      <description summary="destroy the xkb keyboard object">
-        This request indicates that the client will no longer use the keyboard
-        object and that it may be safely destroyed.
-      </description>
-    </request>
-
-    <event name="removed">
-      <description summary="the xkb keyboard is removed">
-        This event indicates that the xkb keyboard has been removed.
-
-        The server will send no further events on this object and ignore any
-        request (other than river_xkb_keyboard_v1.destroy) made after this event
-        is sent. The client should destroy this object with the
-        river_xkb_keyboard_v1.destroy request to free up resources.
-      </description>
-    </event>
-
-    <event name="input_device">
-      <description summary="corresponding river input device">
-        The river_input_device_v1 corresponding to this xkb keyboard. This event
-        will always be the first event sent on the river_xkb_keyboard_v1 object,
-        and it will be sent exactly once.
-      </description>
-      <arg name="device" type="object" interface="river_input_device_v1"/>
-    </event>
-
-    <request name="set_keymap">
-      <description summary="set the keymap">
-        Set the keymap for the keyboard.
-
-        Setting a keymap will reset all layout/modifier state.
-
-        It is a protocol error to pass a keymap object for which the
-        river_xkb_keymap_v1.success event was not received.
-      </description>
-      <arg name="keymap" type="object" interface="river_xkb_keymap_v1"/>
-    </request>
-
-    <request name="set_layout_by_index">
-      <description summary="set the active layout by index">
-        Set the active layout for the keyboard's keymap. Has no effect if the
-        layout index is out of bounds for the current keymap.
-      </description>
-      <arg name="index" type="int"/>
-    </request>
-
-    <request name="set_layout_by_name">
-      <description summary="set the active layout by name">
-        Set the active layout for the keyboard's keymap. Has no effect if there
-        is no layout with the give name for the keyboard's keymap.
-      </description>
-      <arg name="name" type="string"/>
-    </request>
-
-    <event name="layout">
-      <description summary="currently active layout">
-        The currently active layout index and name. The name arg may be null if
-        the active layout does not have a name.
-
-        This event is sent once when the river_xkb_keyboard_v1 is created and
-        again whenever the layout changes.
-      </description>
-      <arg name="index" type="uint"/>
-      <arg name="name" type="string" allow-null="true"/>
-    </event>
-
-    <request name="capslock_enable">
-      <description summary="enable capslock">
-        Enable capslock for the keyboard.
-      </description>
-    </request>
-
-    <request name="capslock_disable">
-      <description summary="disable capslock">
-        Disable capslock for the keyboard.
-      </description>
-    </request>
-
-    <event name="capslock_enabled">
-      <description summary="capslock is currently enabled">
-        Capslock is currently enabled for the keyboard.
-
-        This event is sent once when the river_xkb_keyboard_v1 is created and
-        again whenever the capslock state changes.
-      </description>
-    </event>
-
-    <event name="capslock_disabled">
-      <description summary="capslock is currently disabled">
-        Capslock is currently disabled for the keyboard.
-
-        This event is sent once when the river_xkb_keyboard_v1 is created and
-        again whenever the capslock state changes.
-      </description>
-    </event>
-
-    <request name="numlock_enable">
-      <description summary="enable numlock">
-        Enable numlock for the keyboard.
-      </description>
-    </request>
-
-    <request name="numlock_disable">
-      <description summary="disable numlock">
-        Disable numlock for the keyboard.
-      </description>
-    </request>
-
-    <event name="numlock_enabled">
-      <description summary="numlock is currently enabled">
-        Numlock is currently enabled for the keyboard.
-
-        This event is sent once when the river_xkb_keyboard_v1 is created and
-        again whenever the numlock state changes.
-      </description>
-    </event>
-
-    <event name="numlock_disabled">
-      <description summary="numlock is currently disabled">
-        Numlock is currently disabled for the keyboard.
-
-        This event is sent once when the river_xkb_keyboard_v1 is created and
-        again whenever the numlock state changes.
-      </description>
-    </event>
-
-    <event name="done" since="2">
-      <description summary="all information has been sent">
-        This event is sent after all information about the keyboard has been
-        sent.
-
-        This allows changes to one or more river_xkb_keyboard_v1 properties to
-        be seen as atomic, even if they happen via multiple events.
-      </description>
-    </event>
-  </interface>
-</protocol>
diff --git a/cce-client/protocol/clear-inspector-v1.xml b/protocol/clear-inspector-v1.xml
similarity index 100%
rename from cce-client/protocol/clear-inspector-v1.xml
rename to protocol/clear-inspector-v1.xml
diff --git a/cce-client/protocol/river-input-management-v1.xml b/protocol/river-input-management-v1.xml
similarity index 100%
rename from cce-client/protocol/river-input-management-v1.xml
rename to protocol/river-input-management-v1.xml
diff --git a/cce-client/protocol/river-layer-shell-v1.xml b/protocol/river-layer-shell-v1.xml
similarity index 100%
rename from cce-client/protocol/river-layer-shell-v1.xml
rename to protocol/river-layer-shell-v1.xml
diff --git a/cce-client/protocol/river-libinput-config-v1.xml b/protocol/river-libinput-config-v1.xml
similarity index 100%
rename from cce-client/protocol/river-libinput-config-v1.xml
rename to protocol/river-libinput-config-v1.xml
diff --git a/cce-client/protocol/river-window-management-v1.xml b/protocol/river-window-management-v1.xml
similarity index 100%
rename from cce-client/protocol/river-window-management-v1.xml
rename to protocol/river-window-management-v1.xml
diff --git a/cce-client/protocol/river-xkb-bindings-v1.xml b/protocol/river-xkb-bindings-v1.xml
similarity index 100%
rename from cce-client/protocol/river-xkb-bindings-v1.xml
rename to protocol/river-xkb-bindings-v1.xml
diff --git a/cce-client/protocol/river-xkb-config-v1.xml b/protocol/river-xkb-config-v1.xml
similarity index 100%
rename from cce-client/protocol/river-xkb-config-v1.xml
rename to protocol/river-xkb-config-v1.xml
diff --git a/cce-server/protocol/upstream/virtual-keyboard-unstable-v1.xml b/protocol/upstream/virtual-keyboard-unstable-v1.xml
similarity index 100%
rename from cce-server/protocol/upstream/virtual-keyboard-unstable-v1.xml
rename to protocol/upstream/virtual-keyboard-unstable-v1.xml
diff --git a/cce-server/protocol/upstream/wlr-layer-shell-unstable-v1.xml b/protocol/upstream/wlr-layer-shell-unstable-v1.xml
similarity index 100%
rename from cce-server/protocol/upstream/wlr-layer-shell-unstable-v1.xml
rename to protocol/upstream/wlr-layer-shell-unstable-v1.xml
diff --git a/cce-server/protocol/upstream/wlr-output-power-management-unstable-v1.xml b/protocol/upstream/wlr-output-power-management-unstable-v1.xml
similarity index 100%
rename from cce-server/protocol/upstream/wlr-output-power-management-unstable-v1.xml
rename to protocol/upstream/wlr-output-power-management-unstable-v1.xml
diff --git a/cce-client/protocol/wlr-output-management-unstable-v1.xml b/protocol/wlr-output-management-unstable-v1.xml
similarity index 100%
rename from cce-client/protocol/wlr-output-management-unstable-v1.xml
rename to protocol/wlr-output-management-unstable-v1.xml
diff --git a/scratch/print_c_sizes b/scratch/print_c_sizes
new file mode 100755
index 0000000..e80861a
Binary files /dev/null and b/scratch/print_c_sizes differ
diff --git a/scratch/print_c_sizes.c b/scratch/print_c_sizes.c
new file mode 100644
index 0000000..64a06a9
--- /dev/null
+++ b/scratch/print_c_sizes.c
@@ -0,0 +1,38 @@
+#define WLR_USE_UNSTABLE
+#include <wlr/types/wlr_output.h>
+#include <pixman.h>
+#include <stdio.h>
+#include <stddef.h>
+
+int main() {
+    printf("C sizeof(struct wlr_output_state): %zu\n", sizeof(struct wlr_output_state));
+    printf("C alignof(struct wlr_output_state): %zu\n", _Alignof(struct wlr_output_state));
+    printf("C sizeof(pixman_region32_t): %zu\n", sizeof(pixman_region32_t));
+    printf("C alignof(pixman_region32_t): %zu\n", _Alignof(pixman_region32_t));
+
+    printf("committed: %zu\n", offsetof(struct wlr_output_state, committed));
+    printf("allow_reconfiguration: %zu\n", offsetof(struct wlr_output_state, allow_reconfiguration));
+    printf("damage: %zu\n", offsetof(struct wlr_output_state, damage));
+    printf("enabled: %zu\n", offsetof(struct wlr_output_state, enabled));
+    printf("scale: %zu\n", offsetof(struct wlr_output_state, scale));
+    printf("transform: %zu\n", offsetof(struct wlr_output_state, transform));
+    printf("adaptive_sync_enabled: %zu\n", offsetof(struct wlr_output_state, adaptive_sync_enabled));
+    printf("render_format: %zu\n", offsetof(struct wlr_output_state, render_format));
+    printf("subpixel: %zu\n", offsetof(struct wlr_output_state, subpixel));
+    printf("buffer: %zu\n", offsetof(struct wlr_output_state, buffer));
+    printf("buffer_src_box: %zu\n", offsetof(struct wlr_output_state, buffer_src_box));
+    printf("buffer_dst_box: %zu\n", offsetof(struct wlr_output_state, buffer_dst_box));
+    printf("tearing_page_flip: %zu\n", offsetof(struct wlr_output_state, tearing_page_flip));
+    printf("mode_type: %zu\n", offsetof(struct wlr_output_state, mode_type));
+    printf("mode: %zu\n", offsetof(struct wlr_output_state, mode));
+    printf("custom_mode: %zu\n", offsetof(struct wlr_output_state, custom_mode));
+    printf("gamma_lut: %zu\n", offsetof(struct wlr_output_state, gamma_lut));
+    printf("gamma_lut_size: %zu\n", offsetof(struct wlr_output_state, gamma_lut_size));
+    printf("layers: %zu\n", offsetof(struct wlr_output_state, layers));
+    printf("layers_len: %zu\n", offsetof(struct wlr_output_state, layers_len));
+    printf("wait_timeline: %zu\n", offsetof(struct wlr_output_state, wait_timeline));
+    printf("wait_point: %zu\n", offsetof(struct wlr_output_state, wait_point));
+    printf("signal_timeline: %zu\n", offsetof(struct wlr_output_state, signal_timeline));
+    printf("signal_point: %zu\n", offsetof(struct wlr_output_state, signal_point));
+    return 0;
+}
diff --git a/scratch/print_sizes.rs b/scratch/print_sizes.rs
new file mode 100644
index 0000000..2b36347
--- /dev/null
+++ b/scratch/print_sizes.rs
@@ -0,0 +1,4 @@
+fn main() {
+    println!("wlr_output_state size: {}", std::mem::size_of::<cce::ffi::wlr_output_state>());
+    println!("wlr_output_state alignment: {}", std::mem::align_of::<cce::ffi::wlr_output_state>());
+}
diff --git a/scratch/test_bindgen.rs b/scratch/test_bindgen.rs
new file mode 100644
index 0000000..8a8334c
--- /dev/null
+++ b/scratch/test_bindgen.rs
@@ -0,0 +1,39 @@
+fn main() {
+    let mut builder = bindgen::Builder::default()
+        .header("wrapper.h")
+        .clang_arg("-DWLR_USE_UNSTABLE")
+        .allowlist_type("pixman_region32")
+        .allowlist_type("pixman_box32")
+        .layout_tests(false);
+
+    // Let's query pkg-config for the paths programmatically
+    let pixman = pkg_config::probe_library("pixman-1").unwrap();
+    let wlroots = pkg_config::probe_library("wlroots-0.19").unwrap();
+    let scenefx = pkg_config::probe_library("scenefx-0.4").unwrap();
+    let libinput = pkg_config::probe_library("libinput").unwrap();
+    let libevdev = pkg_config::probe_library("libevdev").unwrap();
+    let wayland_server = pkg_config::probe_library("wayland-server").unwrap();
+    let xkbcommon = pkg_config::probe_library("xkbcommon").unwrap();
+
+    let mut include_paths = vec![std::path::PathBuf::from("target/debug/build/cce-84dd41093ae4212e/out")];
+    include_paths.extend(pixman.include_paths);
+    include_paths.extend(wlroots.include_paths);
+    include_paths.extend(scenefx.include_paths);
+    include_paths.extend(libinput.include_paths);
+    include_paths.extend(libevdev.include_paths);
+    include_paths.extend(wayland_server.include_paths);
+    include_paths.extend(xkbcommon.include_paths);
+
+    for path in include_paths {
+        builder = builder.clang_arg(format!("-I{}", path.display()));
+    }
+
+    let bindings = builder.generate().expect("Failed to generate bindings");
+    let code = bindings.to_string();
+    if let Some(idx) = code.find("pub struct pixman_region32 ") {
+        println!("=== pixman_region32 ===");
+        println!("{}", &code[idx..idx+500]);
+    } else {
+        println!("pixman_region32 not found in generated bindings!");
+    }
+}
diff --git a/src/bin/cce.rs b/src/bin/cce.rs
new file mode 100644
index 0000000..64cda9a
--- /dev/null
+++ b/src/bin/cce.rs
@@ -0,0 +1,40 @@
+use std::env;
+
+fn main() {
+    let args: Vec<String> = env::args().collect();
+    if args.len() > 1 {
+        match args[1].as_str() {
+            "client" => {
+                cce::run_client();
+            }
+            "control" => {
+                let mut control_args = vec!["clearctl".to_string()];
+                control_args.extend(args.iter().skip(2).cloned());
+                cce::run_clearctl(control_args);
+            }
+            "inspect" | "inspector" => {
+                let mut inspect_args = vec!["clear-inspector".to_string()];
+                inspect_args.extend(args.iter().skip(2).cloned());
+                cce::run_inspector(inspect_args);
+            }
+            "--help" | "-h" | "help" => {
+                print_help();
+            }
+            _ => {
+                cce::run_server();
+            }
+        }
+    } else {
+        cce::run_server();
+    }
+}
+
+fn print_help() {
+    println!("usage: cce <subcommand> [options]");
+    println!();
+    println!("subcommands:");
+    println!("  (default)          Start the compositor server and embedded client");
+    println!("  client             Start the standalone Wayland window manager client");
+    println!("  control            Run IPC control commands (e.g. cce control layout gap 10)");
+    println!("  inspect            Run the widget tree inspector");
+}
diff --git a/cce-client/src/borders.rs b/src/client/borders.rs
similarity index 100%
rename from cce-client/src/borders.rs
rename to src/client/borders.rs
diff --git a/cce-client/src/inspector_cli.rs b/src/client/clear_inspector.rs
similarity index 98%
rename from cce-client/src/inspector_cli.rs
rename to src/client/clear_inspector.rs
index 3711f57..2e2becc 100644
--- a/cce-client/src/inspector_cli.rs
+++ b/src/client/clear_inspector.rs
@@ -5,9 +5,8 @@ use wayland_client::{
 };
 use serde_json::Value;
 
-// Import generated client protocols from cce-client crate
-mod protocol;
-use protocol::clear_inspector::client::zclear_inspector_v1::{self, ZclearInspectorV1};
+// Import generated client protocols from cce library
+use crate::protocol::clear_inspector::client::zclear_inspector_v1::{self, ZclearInspectorV1};
 
 struct InspectorState {
     inspector: Option<ZclearInspectorV1>,
@@ -52,6 +51,7 @@ impl Dispatch<ZclearInspectorV1, ()> for InspectorState {
         _conn: &Connection,
         _qh: &QueueHandle<Self>,
     ) {
+        #[allow(unreachable_patterns)]
         match event {
             zclear_inspector_v1::Event::InspectedSurface { title, app_id, x, y, width, height, fd, len } => {
                 let mut surface_state = String::new();
@@ -73,6 +73,7 @@ impl Dispatch<ZclearInspectorV1, ()> for InspectorState {
             zclear_inspector_v1::Event::InspectedSurfaceDone => {
                 state.done = true;
             }
+            _ => {}
         }
     }
 }
@@ -169,8 +170,7 @@ fn print_usage(bin_name: &str) {
     println!("  -l, --label <widget_label>  Optional widget label to filter by");
 }
 
-fn main() {
-    let args: Vec<String> = std::env::args().collect();
+pub fn run_inspector(args: Vec<String>) {
     let bin_name = args.get(0).map(|s| s.as_str()).unwrap_or("clear-inspector");
     
     let mut app_id = None;
diff --git a/cce-client/src/clearctl.rs b/src/client/clearctl.rs
similarity index 98%
rename from cce-client/src/clearctl.rs
rename to src/client/clearctl.rs
index b40d0b5..992598c 100644
--- a/cce-client/src/clearctl.rs
+++ b/src/client/clearctl.rs
@@ -65,8 +65,7 @@ fn usage(name: &str, to_stderr: bool) {
     print("  key-release <key>");
 }
 
-fn main() {
-    let args: Vec<String> = env::args().collect();
+pub fn run_clearctl(args: Vec<String>) {
     if args.len() < 2 {
         usage(&args[0], true);
         process::exit(1);
diff --git a/cce-client/src/config.rs b/src/client/config.rs
similarity index 100%
rename from cce-client/src/config.rs
rename to src/client/config.rs
diff --git a/cce-client/src/decorations.rs b/src/client/decorations.rs
similarity index 100%
rename from cce-client/src/decorations.rs
rename to src/client/decorations.rs
diff --git a/cce-client/src/input.rs b/src/client/input.rs
similarity index 100%
rename from cce-client/src/input.rs
rename to src/client/input.rs
diff --git a/cce-client/src/ipc.rs b/src/client/ipc.rs
similarity index 100%
rename from cce-client/src/ipc.rs
rename to src/client/ipc.rs
diff --git a/cce-client/src/ipc_server.rs b/src/client/ipc_server.rs
similarity index 100%
rename from cce-client/src/ipc_server.rs
rename to src/client/ipc_server.rs
diff --git a/cce-client/src/lib.rs b/src/client/lib.rs
similarity index 91%
rename from cce-client/src/lib.rs
rename to src/client/lib.rs
index caaa726..ac40d31 100644
--- a/cce-client/src/lib.rs
+++ b/src/client/lib.rs
@@ -17,5 +17,6 @@ pub mod wayland;
 pub mod wm;
 pub mod input;
 pub mod paths;
+pub mod run;
 
-
+pub use run::run_client;
diff --git a/cce-client/src/paths.rs b/src/client/paths.rs
similarity index 100%
rename from cce-client/src/paths.rs
rename to src/client/paths.rs
diff --git a/cce-client/src/protocol.rs b/src/client/protocol.rs
similarity index 100%
rename from cce-client/src/protocol.rs
rename to src/client/protocol.rs
diff --git a/cce-client/src/restart.rs b/src/client/restart.rs
similarity index 100%
rename from cce-client/src/restart.rs
rename to src/client/restart.rs
diff --git a/cce-client/src/main.rs b/src/client/run.rs
similarity index 95%
rename from cce-client/src/main.rs
rename to src/client/run.rs
index 2abc99e..2cee73f 100644
--- a/cce-client/src/main.rs
+++ b/src/client/run.rs
@@ -1,15 +1,15 @@
-// cce-client — Wayland window manager for river
-
-use cce_client::config::parse_config;
-use cce_client::ipc;
-use cce_client::ipc_server;
-use cce_client::restart;
-use cce_client::status_server;
-use cce_client::wayland::wayland_init;
+// cce-client — Wayland window manager run loop
+
+use crate::config::parse_config;
+use crate::ipc;
+use crate::ipc_server;
+use crate::restart;
+use crate::status_server;
+use crate::wayland::{wayland_init, AppState};
+use crate::paths;
 use std::env;
 use std::fs;
-
-use cce_client::paths;
+use wayland_client::EventQueue;
 
 /// Write a crash/exit trace to /tmp/cce-client-death.log so we can diagnose
 /// why cce-client dies even when the normal log gets overwritten on restart.
@@ -26,7 +26,7 @@ fn log_death(msg: &str) {
     let _ = writeln!(std::io::stderr(), "{}", msg);
 }
 
-fn main() {
+pub fn run_client() {
     // Install a panic hook that writes to a separate log file before aborting.
     std::panic::set_hook(Box::new(|info| {
         let msg = format!("[PANIC] {}", info);
@@ -103,7 +103,7 @@ fn main() {
     let ipc_tx = ipc_server.tx;
 
     // Setup channel for configuration updates:
-    let (config_tx, config_rx) = tokio::sync::mpsc::unbounded_channel::<cce_client::input::InputDaemonMsg>();
+    let (config_tx, config_rx) = tokio::sync::mpsc::unbounded_channel::<crate::input::InputDaemonMsg>();
     state.wm.input_controller = Some(config_tx);
 
     // Spawn the input subsystem background thread:
@@ -112,7 +112,7 @@ fn main() {
     std::thread::Builder::new()
         .name("cce-client-input-subsystem".into())
         .spawn(move || {
-            if let Err(e) = cce_client::input::run_input_daemon(config_rx, ipc_tx_clone, pipe_write_clone) {
+            if let Err(e) = crate::input::run_input_daemon(config_rx, ipc_tx_clone, pipe_write_clone) {
                 eprintln!("[input-subsystem] Fatal error: {:?}", e);
             }
         })
@@ -158,7 +158,7 @@ fn main() {
     if state.wm.pending_scale_apply && state.wm.output_scale > 0.0 && !state.output_heads.is_empty()
     {
         let qh = event_queue.handle();
-        cce_client::wayland::apply_output_scale(&mut state, &qh);
+        crate::wayland::apply_output_scale(&mut state, &qh);
     }
 
     // Flush any queued requests from config loading (bindings, etc.)
@@ -279,7 +279,7 @@ fn main() {
             state.manage_dirty();
             if !state.wm.tap_config_applied && !state.libinput_devices.is_empty() {
                 let qh = event_queue.handle();
-                cce_client::wayland::apply_input_config(&mut state, &qh);
+                crate::wayland::apply_input_config(&mut state, &qh);
             }
         }
 
diff --git a/cce-client/src/state.rs b/src/client/state.rs
similarity index 100%
rename from cce-client/src/state.rs
rename to src/client/state.rs
diff --git a/cce-client/src/status.rs b/src/client/status.rs
similarity index 100%
rename from cce-client/src/status.rs
rename to src/client/status.rs
diff --git a/cce-client/src/status_server.rs b/src/client/status_server.rs
similarity index 100%
rename from cce-client/src/status_server.rs
rename to src/client/status_server.rs
diff --git a/cce-client/src/tiling.rs b/src/client/tiling.rs
similarity index 100%
rename from cce-client/src/tiling.rs
rename to src/client/tiling.rs
diff --git a/cce-client/src/types.rs b/src/client/types.rs
similarity index 100%
rename from cce-client/src/types.rs
rename to src/client/types.rs
diff --git a/cce-client/src/wayland.rs b/src/client/wayland.rs
similarity index 100%
rename from cce-client/src/wayland.rs
rename to src/client/wayland.rs
diff --git a/cce-client/src/wm.rs b/src/client/wm.rs
similarity index 100%
rename from cce-client/src/wm.rs
rename to src/client/wm.rs
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..c0c2339
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,141 @@
+// cce — Unified compositor server and window manager client
+
+// ==========================================
+// Compositor Server Modules
+// ==========================================
+#[path = "server/ffi.rs"]
+pub mod ffi;
+#[path = "server/server.rs"]
+pub mod server;
+#[path = "server/process.rs"]
+pub mod process;
+#[path = "server/util.rs"]
+pub mod util;
+#[path = "server/slotmap.rs"]
+pub mod slotmap;
+#[path = "server/window_manager.rs"]
+pub mod window_manager;
+#[path = "server/xkb_bindings.rs"]
+pub mod xkb_bindings;
+#[path = "server/layer_shell.rs"]
+pub mod layer_shell;
+#[path = "server/scene.rs"]
+pub mod scene;
+#[path = "server/scene_node_data.rs"]
+pub mod scene_node_data;
+#[path = "server/output.rs"]
+pub mod output;
+#[path = "server/output_manager.rs"]
+pub mod output_manager;
+#[path = "server/input_manager.rs"]
+pub mod input_manager;
+#[path = "server/libinput_config.rs"]
+pub mod libinput_config;
+#[path = "server/libinput_device.rs"]
+pub mod libinput_device;
+#[path = "server/libinput_accel_config.rs"]
+pub mod libinput_accel_config;
+#[path = "server/xkb_keyboard.rs"]
+pub mod xkb_keyboard;
+#[path = "server/xkb_config.rs"]
+pub mod xkb_config;
+#[path = "server/idle_inhibit_manager.rs"]
+pub mod idle_inhibit_manager;
+#[path = "server/lock_manager.rs"]
+pub mod lock_manager;
+#[path = "server/input_device.rs"]
+pub mod input_device;
+#[path = "server/pointer_constraint.rs"]
+pub mod pointer_constraint;
+#[path = "server/keyboard.rs"]
+pub mod keyboard;
+#[path = "server/cursor.rs"]
+pub mod cursor;
+#[path = "server/seat.rs"]
+pub mod seat;
+#[path = "server/tablet.rs"]
+pub mod tablet;
+#[path = "server/tablet_tool.rs"]
+pub mod tablet_tool;
+#[path = "server/window.rs"]
+pub mod window;
+#[path = "server/xdg_toplevel.rs"]
+pub mod xdg_toplevel;
+#[path = "server/xdg_popup.rs"]
+pub mod xdg_popup;
+#[path = "server/shell_surface.rs"]
+pub mod shell_surface;
+#[path = "server/wm_node.rs"]
+pub mod wm_node;
+#[path = "server/xwayland_window.rs"]
+pub mod xwayland_window;
+#[path = "server/xwayland_override_redirect.rs"]
+pub mod xwayland_override_redirect;
+#[path = "server/text_input.rs"]
+pub mod text_input;
+#[path = "server/input_relay.rs"]
+pub mod input_relay;
+#[path = "server/input_popup.rs"]
+pub mod input_popup;
+#[path = "server/drag_icon.rs"]
+pub mod drag_icon;
+#[path = "server/pointer_binding.rs"]
+pub mod pointer_binding;
+#[path = "server/keyboard_group.rs"]
+pub mod keyboard_group;
+#[path = "server/inspector.rs"]
+pub mod inspector;
+
+#[path = "server/run_server.rs"]
+pub mod run_server;
+pub use run_server::run_server;
+
+
+// ==========================================
+// Window Manager Client Modules
+// ==========================================
+#[path = "client/borders.rs"]
+pub mod borders;
+#[path = "client/config.rs"]
+pub mod config;
+#[path = "client/decorations.rs"]
+pub mod decorations;
+#[path = "client/ipc.rs"]
+pub mod ipc;
+#[path = "client/ipc_server.rs"]
+pub mod ipc_server;
+#[path = "client/protocol.rs"]
+pub mod protocol;
+#[path = "client/restart.rs"]
+pub mod restart;
+#[path = "client/state.rs"]
+pub mod state;
+#[path = "client/status.rs"]
+pub mod status;
+#[path = "client/status_server.rs"]
+pub mod status_server;
+#[path = "client/tiling.rs"]
+pub mod tiling;
+#[path = "client/types.rs"]
+pub mod types;
+#[allow(unreachable_patterns)]
+#[path = "client/wayland.rs"]
+pub mod wayland;
+#[path = "client/wm.rs"]
+pub mod wm;
+#[path = "client/input.rs"]
+pub mod input;
+#[path = "client/paths.rs"]
+pub mod paths;
+#[path = "client/run.rs"]
+pub mod run;
+
+pub use run::run_client;
+
+#[path = "client/clearctl.rs"]
+pub mod clearctl;
+pub use clearctl::run_clearctl;
+
+#[path = "client/clear_inspector.rs"]
+pub mod clear_inspector;
+pub use clear_inspector::run_inspector;
diff --git a/cce-server/src/cursor.rs b/src/server/cursor.rs
similarity index 100%
rename from cce-server/src/cursor.rs
rename to src/server/cursor.rs
diff --git a/cce-server/src/drag_icon.rs b/src/server/drag_icon.rs
similarity index 100%
rename from cce-server/src/drag_icon.rs
rename to src/server/drag_icon.rs
diff --git a/cce-server/src/ffi.rs b/src/server/ffi.rs
similarity index 56%
rename from cce-server/src/ffi.rs
rename to src/server/ffi.rs
index 6361257..3b5cbda 100644
--- a/cce-server/src/ffi.rs
+++ b/src/server/ffi.rs
@@ -2,6 +2,8 @@
 #![allow(non_camel_case_types)]
 #![allow(non_snake_case)]
 #![allow(dead_code)]
+#![allow(clippy::all)]
+#![allow(clippy::approx_constant)]
 
 #[repr(C)]
 #[derive(Debug, Copy, Clone)]
@@ -12,6 +14,45 @@ pub struct wl_listener {
     >,
 }
 
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct pixman_region32_data {
+    _unused: [u8; 0],
+}
+
+pub type pixman_region32_data_t = pixman_region32_data;
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct pixman_box32 {
+    pub x1: i32,
+    pub y1: i32,
+    pub x2: i32,
+    pub y2: i32,
+}
+
+pub type pixman_box32_t = pixman_box32;
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct pixman_region32 {
+    pub extents: pixman_box32,
+    pub data: *mut pixman_region32_data,
+}
+
+pub type pixman_region32_t = pixman_region32;
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct pixman_rectangle32 {
+    pub x: i32,
+    pub y: i32,
+    pub width: u32,
+    pub height: u32,
+}
+
+pub type pixman_rectangle32_t = pixman_rectangle32;
+
 include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
 
 #[repr(C)]
diff --git a/cce-server/src/idle_inhibit_manager.rs b/src/server/idle_inhibit_manager.rs
similarity index 100%
rename from cce-server/src/idle_inhibit_manager.rs
rename to src/server/idle_inhibit_manager.rs
diff --git a/cce-server/src/input_device.rs b/src/server/input_device.rs
similarity index 100%
rename from cce-server/src/input_device.rs
rename to src/server/input_device.rs
diff --git a/cce-server/src/input_manager.rs b/src/server/input_manager.rs
similarity index 100%
rename from cce-server/src/input_manager.rs
rename to src/server/input_manager.rs
diff --git a/cce-server/src/input_popup.rs b/src/server/input_popup.rs
similarity index 100%
rename from cce-server/src/input_popup.rs
rename to src/server/input_popup.rs
diff --git a/cce-server/src/input_relay.rs b/src/server/input_relay.rs
similarity index 100%
rename from cce-server/src/input_relay.rs
rename to src/server/input_relay.rs
diff --git a/cce-server/src/inspector.rs b/src/server/inspector.rs
similarity index 100%
rename from cce-server/src/inspector.rs
rename to src/server/inspector.rs
diff --git a/cce-server/src/keyboard.rs b/src/server/keyboard.rs
similarity index 100%
rename from cce-server/src/keyboard.rs
rename to src/server/keyboard.rs
diff --git a/cce-server/src/keyboard_group.rs b/src/server/keyboard_group.rs
similarity index 100%
rename from cce-server/src/keyboard_group.rs
rename to src/server/keyboard_group.rs
diff --git a/cce-server/src/layer_shell.rs b/src/server/layer_shell.rs
similarity index 100%
rename from cce-server/src/layer_shell.rs
rename to src/server/layer_shell.rs
diff --git a/cce-server/src/libinput_accel_config.rs b/src/server/libinput_accel_config.rs
similarity index 100%
rename from cce-server/src/libinput_accel_config.rs
rename to src/server/libinput_accel_config.rs
diff --git a/cce-server/src/libinput_config.rs b/src/server/libinput_config.rs
similarity index 100%
rename from cce-server/src/libinput_config.rs
rename to src/server/libinput_config.rs
diff --git a/cce-server/src/libinput_device.rs b/src/server/libinput_device.rs
similarity index 90%
rename from cce-server/src/libinput_device.rs
rename to src/server/libinput_device.rs
index 7dbd900..d5642c4 100644
--- a/cce-server/src/libinput_device.rs
+++ b/src/server/libinput_device.rs
@@ -59,14 +59,14 @@ impl LibinputDevice {
             crate::server::wl_list_remove(curr);
             ffi::wl_resource_post_event((*obj).resource, ffi::RIVER_LIBINPUT_DEVICE_V1_REMOVED);
 
-            ffi::wl_resource_set_implementation(
-                (*obj).resource,
-                std::ptr::null(),
-                std::ptr::null_mut(),
-                None,
-            );
-
-            let _ = Box::from_raw(obj);
+            // Mark the object as inert by setting device to NULL, but do NOT set the resource's
+            // implementation to NULL or free the `obj` memory here. Let handle_device_object_destroy
+            // do the cleanup when the resource is eventually destroyed by the client/server.
+            (*obj).device = std::ptr::null_mut();
+
+            // Re-initialize the link so it doesn't point to the freed self.objects list.
+            ffi::wl_list_init(&mut (*obj).link);
+
             curr = next;
         }
 
@@ -280,7 +280,9 @@ impl LibinputDevice {
 unsafe extern "C" fn handle_device_object_destroy(resource: *mut ffi::wl_resource) {
     let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
     if !obj.is_null() {
-        crate::server::wl_list_remove(&mut (*obj).link as *mut ffi::wl_list as *mut WlList);
+        if !(*obj).device.is_null() {
+            crate::server::wl_list_remove(&mut (*obj).link as *mut ffi::wl_list as *mut WlList);
+        }
         let _ = Box::from_raw(obj);
     }
 }
@@ -369,6 +371,10 @@ unsafe extern "C" fn device_set_send_events(
     let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
     if obj.is_null() { return; }
     let dev = (*obj).device;
+    if dev.is_null() {
+        make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+        return;
+    }
 
     let status = ffi::libinput_device_config_send_events_set_mode((*dev).libinput, mode);
     if make_result(client, resource, result_id, status) {
@@ -386,6 +392,10 @@ unsafe extern "C" fn device_set_tap(
     let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
     if obj.is_null() { return; }
     let dev = (*obj).device;
+    if dev.is_null() {
+        make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+        return;
+    }
 
     let status = ffi::libinput_device_config_tap_set_enabled((*dev).libinput, state);
     if make_result(client, resource, result_id, status) {
@@ -403,6 +413,10 @@ unsafe extern "C" fn device_set_tap_button_map(
     let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
     if obj.is_null() { return; }
     let dev = (*obj).device;
+    if dev.is_null() {
+        make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+        return;
+    }
 
     let status = ffi::libinput_device_config_tap_set_button_map((*dev).libinput, button_map);
     if make_result(client, resource, result_id, status) {
@@ -420,6 +434,10 @@ unsafe extern "C" fn device_set_drag(
     let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
     if obj.is_null() { return; }
     let dev = (*obj).device;
+    if dev.is_null() {
+        make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+        return;
+    }
 
     let status = ffi::libinput_device_config_tap_set_drag_enabled((*dev).libinput, state);
     if make_result(client, resource, result_id, status) {
@@ -437,6 +455,10 @@ unsafe extern "C" fn device_set_drag_lock(
     let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
     if obj.is_null() { return; }
     let dev = (*obj).device;
+    if dev.is_null() {
+        make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+        return;
+    }
 
     let status = ffi::libinput_device_config_tap_set_drag_lock_enabled((*dev).libinput, state);
     if make_result(client, resource, result_id, status) {
@@ -454,6 +476,10 @@ unsafe extern "C" fn device_set_three_finger_drag(
     let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
     if obj.is_null() { return; }
     let dev = (*obj).device;
+    if dev.is_null() {
+        make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+        return;
+    }
 
     let status = ffi::libinput_device_config_3fg_drag_set_enabled((*dev).libinput, state);
     if make_result(client, resource, result_id, status) {
@@ -471,6 +497,10 @@ unsafe extern "C" fn device_set_calibration_matrix(
     let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
     if obj.is_null() { return; }
     let dev = (*obj).device;
+    if dev.is_null() {
+        make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+        return;
+    }
 
     if (*matrix_arr).size != std::mem::size_of::<[f32; 6]>() {
         ffi::wl_resource_post_error(
@@ -515,6 +545,10 @@ unsafe extern "C" fn device_set_accel_profile(
     let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
     if obj.is_null() { return; }
     let dev = (*obj).device;
+    if dev.is_null() {
+        make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+        return;
+    }
 
     let status = ffi::libinput_device_config_accel_set_profile((*dev).libinput, profile);
     if make_result(client, resource, result_id, status) {
@@ -532,6 +566,10 @@ unsafe extern "C" fn device_set_accel_speed(
     let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
     if obj.is_null() { return; }
     let dev = (*obj).device;
+    if dev.is_null() {
+        make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+        return;
+    }
 
     if (*speed_arr).size != std::mem::size_of::<f64>() {
         ffi::wl_resource_post_error(
@@ -576,13 +614,6 @@ unsafe extern "C" fn device_apply_accel_config(
     if obj.is_null() { return; }
     let dev = (*obj).device;
 
-    let accel_config = ffi::wl_resource_get_user_data(config_res) as *mut crate::libinput_accel_config::LibinputAccelConfig;
-    let config = if !accel_config.is_null() {
-        (*accel_config).libinput
-    } else {
-        std::ptr::null_mut()
-    };
-
     let result_res = ffi::wl_resource_create(
         client,
         &ffi::river_libinput_result_v1_interface,
@@ -594,6 +625,19 @@ unsafe extern "C" fn device_apply_accel_config(
         return;
     }
 
+    if dev.is_null() {
+        ffi::wl_resource_post_event(result_res, 1); // unsupported
+        ffi::wl_resource_destroy(result_res);
+        return;
+    }
+
+    let accel_config = ffi::wl_resource_get_user_data(config_res) as *mut crate::libinput_accel_config::LibinputAccelConfig;
+    let config = if !accel_config.is_null() {
+        (*accel_config).libinput
+    } else {
+        std::ptr::null_mut()
+    };
+
     if config.is_null() {
         ffi::wl_resource_post_event(result_res, 2); // invalid
         ffi::wl_resource_destroy(result_res);
@@ -634,6 +678,10 @@ unsafe extern "C" fn device_set_natural_scroll(
     let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
     if obj.is_null() { return; }
     let dev = (*obj).device;
+    if dev.is_null() {
+        make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+        return;
+    }
 
     let status = ffi::libinput_device_config_scroll_set_natural_scroll_enabled((*dev).libinput, state as i32);
     if make_result(client, resource, result_id, status) {
@@ -651,6 +699,10 @@ unsafe extern "C" fn device_set_left_handed(
     let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
     if obj.is_null() { return; }
     let dev = (*obj).device;
+    if dev.is_null() {
+        make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+        return;
+    }
 
     let status = ffi::libinput_device_config_left_handed_set((*dev).libinput, state as i32);
     if make_result(client, resource, result_id, status) {
@@ -668,6 +720,10 @@ unsafe extern "C" fn device_set_click_method(
     let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
     if obj.is_null() { return; }
     let dev = (*obj).device;
+    if dev.is_null() {
+        make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+        return;
+    }
 
     let status = ffi::libinput_device_config_click_set_method((*dev).libinput, method);
     if make_result(client, resource, result_id, status) {
@@ -685,6 +741,10 @@ unsafe extern "C" fn device_set_clickfinger_button_map(
     let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
     if obj.is_null() { return; }
     let dev = (*obj).device;
+    if dev.is_null() {
+        make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+        return;
+    }
 
     let status = ffi::libinput_device_config_click_set_clickfinger_button_map((*dev).libinput, button_map);
     if make_result(client, resource, result_id, status) {
@@ -702,6 +762,10 @@ unsafe extern "C" fn device_set_middle_emulation(
     let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
     if obj.is_null() { return; }
     let dev = (*obj).device;
+    if dev.is_null() {
+        make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+        return;
+    }
 
     let status = ffi::libinput_device_config_middle_emulation_set_enabled((*dev).libinput, state);
     if make_result(client, resource, result_id, status) {
@@ -719,6 +783,10 @@ unsafe extern "C" fn device_set_scroll_method(
     let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
     if obj.is_null() { return; }
     let dev = (*obj).device;
+    if dev.is_null() {
+        make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+        return;
+    }
 
     let status = ffi::libinput_device_config_scroll_set_method((*dev).libinput, method);
     if make_result(client, resource, result_id, status) {
@@ -736,6 +804,10 @@ unsafe extern "C" fn device_set_scroll_button(
     let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
     if obj.is_null() { return; }
     let dev = (*obj).device;
+    if dev.is_null() {
+        make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+        return;
+    }
 
     let status = ffi::libinput_device_config_scroll_set_button((*dev).libinput, button);
     if make_result(client, resource, result_id, status) {
@@ -753,6 +825,10 @@ unsafe extern "C" fn device_set_scroll_button_lock(
     let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
     if obj.is_null() { return; }
     let dev = (*obj).device;
+    if dev.is_null() {
+        make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+        return;
+    }
 
     let status = ffi::libinput_device_config_scroll_set_button_lock((*dev).libinput, state);
     if make_result(client, resource, result_id, status) {
@@ -770,6 +846,10 @@ unsafe extern "C" fn device_set_dwt(
     let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
     if obj.is_null() { return; }
     let dev = (*obj).device;
+    if dev.is_null() {
+        make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+        return;
+    }
 
     let status = ffi::libinput_device_config_dwt_set_enabled((*dev).libinput, state);
     if make_result(client, resource, result_id, status) {
@@ -787,6 +867,10 @@ unsafe extern "C" fn device_set_dwtp(
     let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
     if obj.is_null() { return; }
     let dev = (*obj).device;
+    if dev.is_null() {
+        make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+        return;
+    }
 
     let status = ffi::libinput_device_config_dwtp_set_enabled((*dev).libinput, state);
     if make_result(client, resource, result_id, status) {
@@ -804,6 +888,10 @@ unsafe extern "C" fn device_set_rotation(
     let obj = ffi::wl_resource_get_user_data(resource) as *mut LibinputDeviceObject;
     if obj.is_null() { return; }
     let dev = (*obj).device;
+    if dev.is_null() {
+        make_result(client, resource, result_id, ffi::libinput_config_status_LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
+        return;
+    }
 
     let status = ffi::libinput_device_config_rotation_set_angle((*dev).libinput, angle);
     if make_result(client, resource, result_id, status) {
diff --git a/cce-server/src/lock_manager.rs b/src/server/lock_manager.rs
similarity index 100%
rename from cce-server/src/lock_manager.rs
rename to src/server/lock_manager.rs
diff --git a/cce-server/src/output.rs b/src/server/output.rs
similarity index 100%
rename from cce-server/src/output.rs
rename to src/server/output.rs
diff --git a/cce-server/src/output_manager.rs b/src/server/output_manager.rs
similarity index 100%
rename from cce-server/src/output_manager.rs
rename to src/server/output_manager.rs
diff --git a/cce-server/src/pointer_binding.rs b/src/server/pointer_binding.rs
similarity index 100%
rename from cce-server/src/pointer_binding.rs
rename to src/server/pointer_binding.rs
diff --git a/cce-server/src/pointer_constraint.rs b/src/server/pointer_constraint.rs
similarity index 100%
rename from cce-server/src/pointer_constraint.rs
rename to src/server/pointer_constraint.rs
diff --git a/cce-server/src/process.rs b/src/server/process.rs
similarity index 100%
rename from cce-server/src/process.rs
rename to src/server/process.rs
diff --git a/cce-server/src/main.rs b/src/server/run_server.rs
similarity index 89%
rename from cce-server/src/main.rs
rename to src/server/run_server.rs
index 557b7fe..8961413 100644
--- a/cce-server/src/main.rs
+++ b/src/server/run_server.rs
@@ -1,53 +1,13 @@
 // SPDX-FileCopyrightText: © 2020 The River Developers
 // SPDX-License-Identifier: GPL-3.0-only
 
-mod ffi;
-mod server;
-mod process;
-mod util;
-mod slotmap;
-mod window_manager;
-mod xkb_bindings;
-mod layer_shell;
-mod scene;
-mod scene_node_data;
-mod output;
-mod output_manager;
-mod input_manager;
-mod libinput_config;
-pub mod libinput_device;
-pub mod libinput_accel_config;
-pub mod xkb_keyboard;
-mod xkb_config;
-mod idle_inhibit_manager;
-mod lock_manager;
-mod input_device;
-mod pointer_constraint;
-mod keyboard;
-mod cursor;
-mod seat;
-pub mod tablet;
-pub mod tablet_tool;
-pub mod window;
-pub mod xdg_toplevel;
-pub mod xdg_popup;
-pub mod shell_surface;
-pub mod wm_node;
-pub mod xwayland_window;
-pub mod xwayland_override_redirect;
-pub mod text_input;
-pub mod input_relay;
-pub mod input_popup;
-pub mod drag_icon;
-pub mod pointer_binding;
-pub mod keyboard_group;
-pub mod inspector;
-
-
 use clap::Parser;
 use std::ffi::{CStr, CString};
 use std::os::raw::c_char;
 
+use crate::ffi;
+use crate::server;
+use crate::process;
 
 const USAGE: &str = "\
 usage: river [options]
@@ -98,9 +58,9 @@ pub unsafe extern "C" fn river_wlroots_log_callback(
 
 fn default_init_path() -> Option<String> {
     let path = if let Ok(xdg_config_home) = std::env::var("XDG_CONFIG_HOME") {
-        format!("{}/river/init", xdg_config_home)
+        format!("{}/cce/init", xdg_config_home)
     } else if let Ok(home) = std::env::var("HOME") {
-        format!("{}/.config/river/init", home)
+        format!("{}/.config/cce/init", home)
     } else {
         return None;
     };
@@ -126,7 +86,7 @@ fn detect_classic(path: &str) {
     }
 }
 
-fn main() {
+pub fn run_server() {
     let args = match Args::try_parse() {
         Ok(a) => a,
         Err(_) => {
@@ -198,6 +158,15 @@ fn main() {
     let socket_str = unsafe { CStr::from_ptr(socket_ptr).to_string_lossy().into_owned() };
     log::info!("running server on display socket: {}", socket_str);
 
+    std::env::set_var("WAYLAND_DISPLAY", &socket_str);
+    log::info!("spawning in-process cce-client thread...");
+    std::thread::Builder::new()
+        .name("cce-client".to_string())
+        .spawn(|| {
+            crate::run_client();
+        })
+        .expect("Failed to spawn cce-client thread");
+
     let started = unsafe { ffi::wlr_backend_start(server.backend) };
     if !started {
         log::error!("failed to start wlr_backend");
diff --git a/cce-server/src/scene.rs b/src/server/scene.rs
similarity index 100%
rename from cce-server/src/scene.rs
rename to src/server/scene.rs
diff --git a/cce-server/src/scene_node_data.rs b/src/server/scene_node_data.rs
similarity index 100%
rename from cce-server/src/scene_node_data.rs
rename to src/server/scene_node_data.rs
diff --git a/cce-server/src/seat.rs b/src/server/seat.rs
similarity index 100%
rename from cce-server/src/seat.rs
rename to src/server/seat.rs
diff --git a/cce-server/src/server.rs b/src/server/server.rs
similarity index 100%
rename from cce-server/src/server.rs
rename to src/server/server.rs
diff --git a/cce-server/src/shell_surface.rs b/src/server/shell_surface.rs
similarity index 100%
rename from cce-server/src/shell_surface.rs
rename to src/server/shell_surface.rs
diff --git a/cce-server/src/slotmap.rs b/src/server/slotmap.rs
similarity index 100%
rename from cce-server/src/slotmap.rs
rename to src/server/slotmap.rs
diff --git a/cce-server/src/tablet.rs b/src/server/tablet.rs
similarity index 100%
rename from cce-server/src/tablet.rs
rename to src/server/tablet.rs
diff --git a/cce-server/src/tablet_tool.rs b/src/server/tablet_tool.rs
similarity index 100%
rename from cce-server/src/tablet_tool.rs
rename to src/server/tablet_tool.rs
diff --git a/cce-server/src/text_input.rs b/src/server/text_input.rs
similarity index 100%
rename from cce-server/src/text_input.rs
rename to src/server/text_input.rs
diff --git a/cce-server/src/util.rs b/src/server/util.rs
similarity index 100%
rename from cce-server/src/util.rs
rename to src/server/util.rs
diff --git a/cce-server/src/window.rs b/src/server/window.rs
similarity index 99%
rename from cce-server/src/window.rs
rename to src/server/window.rs
index 0233dab..e22a587 100644
--- a/cce-server/src/window.rs
+++ b/src/server/window.rs
@@ -427,11 +427,19 @@ impl Window {
         }
     }
 
+    pub unsafe fn is_antigravity(&self) -> bool {
+        let app_id_ptr = self.get_app_id();
+        if app_id_ptr.is_null() { return false; }
+        let app_id = std::ffi::CStr::from_ptr(app_id_ptr).to_str().unwrap_or("").to_lowercase();
+        app_id.contains("antigravity")
+    }
+
     pub unsafe fn is_chromium_electron(&self) -> bool {
+        if self.is_antigravity() { return true; }
         let app_id_ptr = self.get_app_id();
         if app_id_ptr.is_null() { return false; }
         let app_id = std::ffi::CStr::from_ptr(app_id_ptr).to_str().unwrap_or("").to_lowercase();
-        app_id.contains("antigravity") || app_id.contains("chromium") || app_id.contains("chrome") || app_id.contains("electron") || app_id.contains("code") || app_id.contains("discord") || app_id.contains("slack")
+        app_id.contains("chromium") || app_id.contains("chrome") || app_id.contains("electron") || app_id.contains("code") || app_id.contains("discord") || app_id.contains("slack")
     }
 
     pub unsafe fn get_parent(&self) -> *mut Window {
@@ -995,8 +1003,9 @@ impl Window {
         if self.wm_requested.ssd && self.is_chromium_electron() && self.wm_requested.fullscreen.is_null() {
             let margin_x = if self.margin_x > 0 { self.margin_x } else { 10 };
             let margin_y = if self.margin_y > 0 { self.margin_y } else { 10 };
-            let (left_margin, right_margin) = if margin_x == 10 { (10, 34) } else { (margin_x, margin_x) };
-            let (top_margin, bottom_margin) = if margin_y == 10 { (10, 34) } else { (margin_y, margin_y) };
+            let is_antigravity = self.is_antigravity();
+            let (left_margin, right_margin) = if margin_x == 10 && !is_antigravity { (10, 34) } else { (margin_x, margin_x) };
+            let (top_margin, bottom_margin) = if margin_y == 10 && !is_antigravity { (10, 34) } else { (margin_y, margin_y) };
             if let Some(w) = width {
                 width = Some(w + left_margin as u32 + right_margin as u32);
             }
@@ -1300,7 +1309,7 @@ impl Window {
 
     pub unsafe fn draw_borders(&mut self) {
         let requested = &self.rendering_requested;
-        if requested.circular || requested.border.width <= 0 {
+        if requested.circular || requested.border.width == 0 {
             ffi::wlr_scene_node_set_enabled(self.border.left as *mut ffi::wlr_scene_node, false);
             ffi::wlr_scene_node_set_enabled(self.border.right as *mut ffi::wlr_scene_node, false);
             ffi::wlr_scene_node_set_enabled(self.border.top as *mut ffi::wlr_scene_node, false);
diff --git a/cce-server/src/window_manager.rs b/src/server/window_manager.rs
similarity index 100%
rename from cce-server/src/window_manager.rs
rename to src/server/window_manager.rs
diff --git a/cce-server/src/wlroots_log_wrapper.c b/src/server/wlroots_log_wrapper.c
similarity index 100%
rename from cce-server/src/wlroots_log_wrapper.c
rename to src/server/wlroots_log_wrapper.c
diff --git a/cce-server/src/wm_node.rs b/src/server/wm_node.rs
similarity index 100%
rename from cce-server/src/wm_node.rs
rename to src/server/wm_node.rs
diff --git a/cce-server/src/xdg_popup.rs b/src/server/xdg_popup.rs
similarity index 100%
rename from cce-server/src/xdg_popup.rs
rename to src/server/xdg_popup.rs
diff --git a/cce-server/src/xdg_toplevel.rs b/src/server/xdg_toplevel.rs
similarity index 98%
rename from cce-server/src/xdg_toplevel.rs
rename to src/server/xdg_toplevel.rs
index 6d56250..0843c46 100644
--- a/cce-server/src/xdg_toplevel.rs
+++ b/src/server/xdg_toplevel.rs
@@ -682,8 +682,9 @@ pub unsafe fn override_geometry_if_needed(window: *mut crate::window::Window, ge
         let margin_x = if (*window).margin_x > 0 { (*window).margin_x } else { 10 };
         let margin_y = if (*window).margin_y > 0 { (*window).margin_y } else { 10 };
         
-        let (left_margin, right_margin) = if margin_x == 10 { (10, 34) } else { (margin_x, margin_x) };
-        let (top_margin, bottom_margin) = if margin_y == 10 { (10, 34) } else { (margin_y, margin_y) };
+        let is_antigravity = (*window).is_antigravity();
+        let (left_margin, right_margin) = if margin_x == 10 && !is_antigravity { (10, 34) } else { (margin_x, margin_x) };
+        let (top_margin, bottom_margin) = if margin_y == 10 && !is_antigravity { (10, 34) } else { (margin_y, margin_y) };
         
         geom.x = left_margin;
         geom.y = top_margin;
diff --git a/cce-server/src/xkb_bindings.rs b/src/server/xkb_bindings.rs
similarity index 100%
rename from cce-server/src/xkb_bindings.rs
rename to src/server/xkb_bindings.rs
diff --git a/cce-server/src/xkb_config.rs b/src/server/xkb_config.rs
similarity index 100%
rename from cce-server/src/xkb_config.rs
rename to src/server/xkb_config.rs
diff --git a/cce-server/src/xkb_keyboard.rs b/src/server/xkb_keyboard.rs
similarity index 100%
rename from cce-server/src/xkb_keyboard.rs
rename to src/server/xkb_keyboard.rs
diff --git a/cce-server/src/xwayland_override_redirect.rs b/src/server/xwayland_override_redirect.rs
similarity index 100%
rename from cce-server/src/xwayland_override_redirect.rs
rename to src/server/xwayland_override_redirect.rs
diff --git a/cce-server/src/xwayland_window.rs b/src/server/xwayland_window.rs
similarity index 100%
rename from cce-server/src/xwayland_window.rs
rename to src/server/xwayland_window.rs
diff --git a/cce-client/start-river.sh b/start-river.sh
similarity index 90%
rename from cce-client/start-river.sh
rename to start-river.sh
index 2a008fa..56989ca 100755
--- a/cce-client/start-river.sh
+++ b/start-river.sh
@@ -20,12 +20,12 @@ export XCURSOR_PATH="/home/lsgalante/.local/share/icons:/home/lsgalante/.icons:/
 if [ "$LOGGING" = true ]; then
     cat > /tmp/cce-client-launch-river.sh << 'LAUNCH_EOF'
 #!/bin/sh
-exec /home/lsgalante/.local/bin/cce-client 2>/tmp/cce-client-${WAYLAND_DISPLAY}.log
+exec /home/lsgalante/.local/bin/cce client 2>/tmp/cce-client-${WAYLAND_DISPLAY}.log
 LAUNCH_EOF
 else
     cat > /tmp/cce-client-launch-river.sh << 'LAUNCH_EOF'
 #!/bin/sh
-exec /home/lsgalante/.local/bin/cce-client
+exec /home/lsgalante/.local/bin/cce client
 LAUNCH_EOF
 fi
 chmod +x /tmp/cce-client-launch-river.sh
diff --git a/cce-server/start-rust.sh b/start-rust.sh
similarity index 53%
rename from cce-server/start-rust.sh
rename to start-rust.sh
index 84bff38..b16d6fc 100755
--- a/cce-server/start-rust.sh
+++ b/start-rust.sh
@@ -1,5 +1,5 @@
 #!/bin/sh
-# Launch cce-server with cce-client
+# Launch cce-server with in-process cce-client
 # Usage: ./start-rust.sh [--logging] [--debug]
  
 LOGGING=false
@@ -17,18 +17,6 @@ export XCURSOR_SIZE=24
 export XCURSOR_PATH="/home/lsgalante/.local/share/icons:/home/lsgalante/.icons:/usr/share/icons"
 export WLR_NO_HARDWARE_CURSORS=1
  
-# Create the cce-client launch script
-DEBUG_FLAG=""
-if [ "$DEBUG" = true ]; then
-    DEBUG_FLAG="WAYLAND_DEBUG=1 "
-fi
- 
-cat > /tmp/cce-client-launch-rust.sh << LAUNCH_EOF
-#!/bin/sh
-${DEBUG_FLAG}exec /home/lsgalante/.local/bin/cce-client 2>/tmp/cce-client-\${WAYLAND_DISPLAY}.log
-LAUNCH_EOF
-chmod +x /tmp/cce-client-launch-rust.sh
- 
 # Resolve script directory to reference target/release/cce-server reliably
 SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
 if [ "$DEBUG" = true ]; then
@@ -36,7 +24,7 @@ if [ "$DEBUG" = true ]; then
 fi
 
 if [ "$LOGGING" = true ] || [ "$DEBUG" = true ]; then
-    exec "$SCRIPT_DIR/target/release/cce-server" -c /tmp/cce-client-launch-rust.sh 2>/tmp/river-cce-client.log
+    exec "$SCRIPT_DIR/target/release/cce" 2>/tmp/river-cce-client.log
 else
-    exec "$SCRIPT_DIR/target/release/cce-server" --log-level error -c /tmp/cce-client-launch-rust.sh 2>/dev/null
+    exec "$SCRIPT_DIR/target/release/cce" --log-level error 2>/dev/null
 fi
diff --git a/cce-server/wrapper.h b/wrapper.h
similarity index 100%
rename from cce-server/wrapper.h
rename to wrapper.h