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

commit12cc5b08e54a1802983916ab0b7f86e3a3f041f8
parenteec487b58a
authorLucas Galante <[email protected]>
date2026-05-29 17:37
Update system configuration and interface modules

 src/config.rs        |   8 +
 src/decorations.rs   | 197 +++++++++++++++-
 src/ipc.rs           |  19 ++
 src/ipc_server.rs    |   7 +-
 src/main.rs          |   4 +
 src/status_server.rs |  10 +-
 src/types.rs         |   6 +
 src/wayland.rs       | 622 ++++++++++++++++++++++++++++++++++++++++++++++++---
 8 files changed, 831 insertions(+), 42 deletions(-)

diff --git a/src/config.rs b/src/config.rs
index ccf4f5b..78a8ba8 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -183,6 +183,8 @@ pub struct InputConfig {
     pub dwtp: Option<bool>,
     pub trackpoint_accel_speed: Option<f64>,
     pub trackpoint_accel_profile: Option<String>,
+    pub cursor_theme: Option<String>,
+    pub cursor_size: Option<u32>,
 }
 
 #[derive(Debug, Deserialize, Clone)]
@@ -460,6 +462,12 @@ pub fn parse_config(path: &str, cold_start: bool, state: &mut WindowManager) ->
     state.trackpoint_accel_profile = config.input.trackpoint_accel_profile.clone();
     state.tap_config_applied = false;
 
+    if state.cursor_theme != config.input.cursor_theme || state.cursor_size != config.input.cursor_size {
+        state.cursor_theme = config.input.cursor_theme.clone();
+        state.cursor_size = config.input.cursor_size;
+        state.cursor_theme_applied = false;
+    }
+
     // Send InertialConfig and tap_to_click state to input subsystem daemon
     let inertial_cfg = config.inertial.clone().unwrap_or_else(|| {
         InertialConfig {
diff --git a/src/decorations.rs b/src/decorations.rs
index 0112809..5219988 100644
--- a/src/decorations.rs
+++ b/src/decorations.rs
@@ -32,8 +32,8 @@ fn resolve_window_border_font_path() -> Option<String> {
 
 /// Struct tracking the Wayland decoration resources for a window.
 pub struct WindowDecoration {
-    pub surface: wl_surface::WlSurface,
     pub decoration: RiverDecorationV1,
+    pub surface: wl_surface::WlSurface,
     pub buffer: Option<wl_buffer::WlBuffer>,
     pub pool: Option<wl_shm_pool::WlShmPool>,
     pub width: i32,
@@ -200,6 +200,116 @@ fn create_memfd(size: usize) -> Option<RawFd> {
     Some(fd)
 }
 
+fn update_transparent_decoration(
+    _wid: u64,
+    dec_opt: &mut Option<WindowDecoration>,
+    compositor: &wayland_client::protocol::wl_compositor::WlCompositor,
+    shm: &wayland_client::protocol::wl_shm::WlShm,
+    river_window: &crate::protocol::river_window_management::client::river_window_v1::RiverWindowV1,
+    qhandle: &QueueHandle<AppState>,
+    offset_x: i32,
+    offset_y: i32,
+    logical_width: i32,
+    logical_height: i32,
+    scale: i32,
+) {
+    let dec_width = logical_width * scale;
+    let dec_height = logical_height * scale;
+
+    let needs_new_buffer = match dec_opt {
+        Some(dec) => dec.width != dec_width || dec.height != dec_height,
+        None => true,
+    };
+
+    if dec_opt.is_none() {
+        let surface: wl_surface::WlSurface = compositor.create_surface(qhandle, ());
+        let decoration: RiverDecorationV1 = river_window.get_decoration_above(&surface, qhandle, ());
+
+        *dec_opt = Some(WindowDecoration {
+            decoration,
+            surface,
+            buffer: None,
+            pool: None,
+            width: 0,
+            height: 0,
+            mapped_data: ptr::null_mut(),
+            mapped_size: 0,
+        });
+    }
+
+    let dec = dec_opt.as_mut().unwrap();
+    dec.decoration.set_offset(offset_x, offset_y);
+
+    if needs_new_buffer {
+        if !dec.mapped_data.is_null() {
+            unsafe {
+                libc::munmap(dec.mapped_data as *mut libc::c_void, dec.mapped_size);
+            }
+            dec.mapped_data = ptr::null_mut();
+        }
+
+        let stride = dec_width * 4;
+        let size = (stride * dec_height) as usize;
+
+        if let Some(fd) = create_memfd(size) {
+            let mapped_data = unsafe {
+                libc::mmap(
+                    ptr::null_mut(),
+                    size,
+                    libc::PROT_READ | libc::PROT_WRITE,
+                    libc::MAP_SHARED,
+                    fd,
+                    0,
+                ) as *mut u32
+            };
+
+            if mapped_data != libc::MAP_FAILED as *mut u32 {
+                let borrowed_fd = unsafe { std::os::fd::BorrowedFd::borrow_raw(fd) };
+                let pool = shm.create_pool(borrowed_fd, size as i32, qhandle, ());
+                let buffer = pool.create_buffer(
+                    0,
+                    dec_width,
+                    dec_height,
+                    stride,
+                    wl_shm::Format::Argb8888,
+                    qhandle,
+                    (),
+                );
+
+                // Clear to fully transparent
+                let buffer_slice = unsafe {
+                    std::slice::from_raw_parts_mut(mapped_data, (dec_width * dec_height) as usize)
+                };
+                for pixel in buffer_slice.iter_mut() {
+                    *pixel = 0x00000000;
+                }
+
+                dec.pool = Some(pool);
+                dec.buffer = Some(buffer);
+                dec.width = dec_width;
+                dec.height = dec_height;
+                dec.mapped_data = mapped_data;
+                dec.mapped_size = size;
+                dec.surface.set_buffer_scale(scale);
+            } else {
+                eprintln!("[decorations] failed to mmap transparent decoration buffer");
+                unsafe { libc::close(fd); }
+                return;
+            }
+            unsafe { libc::close(fd); }
+        } else {
+            eprintln!("[decorations] failed to create memfd for transparent decoration");
+            return;
+        }
+    }
+
+    if let Some(ref buffer) = dec.buffer {
+        dec.surface.attach(Some(buffer), 0, 0);
+        dec.surface.damage(0, 0, dec_width, dec_height);
+        dec.surface.commit();
+    }
+}
+
 /// Main entry point to create or update decoration surfaces during rendering.
 pub fn update_decorations(state: &mut AppState, qhandle: &QueueHandle<AppState>) {
     // Check if we need to load or reload the font
@@ -224,15 +334,34 @@ pub fn update_decorations(state: &mut AppState, qhandle: &QueueHandle<AppState>)
         _ => return,
     };
 
-    // Clean up decorations for windows that should not be decorated
+    // Clean up decorations for windows that should not be decorated,
+    // or side borders for windows that do not have them (Popup, Fullscreen).
     for (wid, wp) in &mut state.window_proxies {
         if let Some(w) = state.wm.windows.iter().find(|win| win.id == *wid) {
-            if w.closed || w.app_id.as_deref() == Some("clear-status-interface") || w.tiling_mode == crate::types::TilingMode::Popup {
+            let should_not_decorate = w.closed || w.app_id.as_deref() == Some("clear-status-interface") || w.tiling_mode == crate::types::TilingMode::Popup || w.tiling_mode == crate::types::TilingMode::Fullscreen;
+            let needs_sides = !should_not_decorate;
+
+            if should_not_decorate {
                 if let Some(dec) = wp.decoration.take() {
                     dec.decoration.destroy();
                     dec.surface.destroy();
                 }
             }
+
+            if !needs_sides {
+                if let Some(dec) = wp.dec_left.take() {
+                    dec.decoration.destroy();
+                    dec.surface.destroy();
+                }
+                if let Some(dec) = wp.dec_right.take() {
+                    dec.decoration.destroy();
+                    dec.surface.destroy();
+                }
+                if let Some(dec) = wp.dec_bottom.take() {
+                    dec.decoration.destroy();
+                    dec.surface.destroy();
+                }
+            }
         }
     }
 
@@ -241,12 +370,12 @@ pub fn update_decorations(state: &mut AppState, qhandle: &QueueHandle<AppState>)
     let text_color = 0xFFE0E0E0u32;
 
     // Collect window IDs to modify so we don't violate the borrow checker
-    let windows_to_decorate: Vec<(u64, i32, i32, String, u32)> = state
+    let windows_to_decorate: Vec<(u64, i32, i32, i32, String, u32, crate::types::TilingMode)> = state
         .wm
         .windows
         .iter()
         .enumerate()
-        .filter(|(_, w)| !w.closed && w.app_id.as_deref() != Some("clear-status-interface") && w.tiling_mode != crate::types::TilingMode::Popup)
+        .filter(|(_, w)| !w.closed && w.app_id.as_deref() != Some("clear-status-interface") && w.tiling_mode != crate::types::TilingMode::Popup && w.tiling_mode != crate::types::TilingMode::Fullscreen)
         .filter(|(_, w)| (w.tags & active_tags) != 0)
         .map(|(idx, w)| {
             let title = w.title.clone().unwrap_or_else(|| {
@@ -312,11 +441,11 @@ pub fn update_decorations(state: &mut AppState, qhandle: &QueueHandle<AppState>)
                     crate::types::TilingMode::Popup => 0,
                 }
             };
-            (w.id, w.width, border_w, title_with_idx, bg_color)
+            (w.id, w.width, w.height, border_w, title_with_idx, bg_color, w.tiling_mode)
         })
         .collect();
 
-    for (wid, win_width, border_width, title, bg_color) in windows_to_decorate {
+    for (wid, win_width, win_height, border_width, title, bg_color, tiling_mode) in windows_to_decorate {
         // Find or create decoration proxy
         let wp_idx = match state.window_proxies.iter().position(|(id, _)| *id == wid) {
             Some(idx) => idx,
@@ -327,6 +456,56 @@ pub fn update_decorations(state: &mut AppState, qhandle: &QueueHandle<AppState>)
 
         let scale = (state.wm.output_scale.round() as i32).max(1);
 
+        if tiling_mode != crate::types::TilingMode::Popup && tiling_mode != crate::types::TilingMode::Fullscreen {
+            let grab_w = if tiling_mode == crate::types::TilingMode::Floating {
+                border_width.max(10)
+            } else {
+                border_width.max(1)
+            };
+
+            update_transparent_decoration(
+                wid,
+                &mut wp.dec_left,
+                compositor,
+                shm,
+                &wp.river_window,
+                qhandle,
+                -grab_w,
+                0,
+                grab_w,
+                win_height,
+                scale,
+            );
+
+            update_transparent_decoration(
+                wid,
+                &mut wp.dec_right,
+                compositor,
+                shm,
+                &wp.river_window,
+                qhandle,
+                win_width,
+                0,
+                grab_w,
+                win_height,
+                scale,
+            );
+
+            update_transparent_decoration(
+                wid,
+                &mut wp.dec_bottom,
+                compositor,
+                shm,
+                &wp.river_window,
+                qhandle,
+                -grab_w,
+                win_height,
+                win_width + 2 * grab_w,
+                grab_w,
+                scale,
+            );
+        }
+
         // Determine titlebar dimensions:
         // Height equals border_width (or 16 if border_width is too small to display font)
         let logical_height = std::cmp::max(border_width, 16);
@@ -345,8 +524,8 @@ pub fn update_decorations(state: &mut AppState, qhandle: &QueueHandle<AppState>)
             let decoration: RiverDecorationV1 = wp.river_window.get_decoration_above(&surface, qhandle, ());
 
             wp.decoration = Some(WindowDecoration {
-                surface,
                 decoration,
+                surface,
                 buffer: None,
                 pool: None,
                 width: 0,
@@ -359,7 +538,7 @@ pub fn update_decorations(state: &mut AppState, qhandle: &QueueHandle<AppState>)
         let dec = wp.decoration.as_mut().unwrap();
 
         // Position decoration on top of the window top border
-        dec.decoration.set_offset(-border_width, -border_width);
+        dec.decoration.set_offset(-border_width, -logical_height);
 
         if needs_new_buffer {
             eprintln!(
diff --git a/src/ipc.rs b/src/ipc.rs
index 5d4d681..7326bdc 100644
--- a/src/ipc.rs
+++ b/src/ipc.rs
@@ -774,6 +774,25 @@ fn handle_input_command(rest: &str, state: &mut WindowManager) {
                 }
             }
         }
+        "cursor-theme" | "cursor_theme" => {
+            let val = value_str.trim().to_string();
+            if !val.is_empty() {
+                state.cursor_theme = Some(val.clone());
+                state.cursor_theme_applied = false;
+                if state.notifications_enable {
+                    crate::config::show_notification("clearwm", &format!("Cursor theme set to {}", val));
+                }
+            }
+        }
+        "cursor-size" | "cursor_size" => {
+            if let Ok(val) = value_str.parse::<u32>() {
+                state.cursor_size = Some(val);
+                state.cursor_theme_applied = false;
+                if state.notifications_enable {
+                    crate::config::show_notification("clearwm", &format!("Cursor size set to {}", val));
+                }
+            }
+        }
         _ => {}
     }
 }
diff --git a/src/ipc_server.rs b/src/ipc_server.rs
index 86ef998..2ad5b6d 100644
--- a/src/ipc_server.rs
+++ b/src/ipc_server.rs
@@ -45,6 +45,7 @@ fn ipc_server_main(tx: mpsc::Sender<String>, pipe_write: libc::c_int) {
     let mut streams: Vec<UnixStream> = Vec::new();
 
     loop {
+        let mut activity = false;
         for _ in 0..5 {
             match listener.accept() {
                 Ok((stream, _addr)) => {
@@ -54,6 +55,7 @@ fn ipc_server_main(tx: mpsc::Sender<String>, pipe_write: libc::c_int) {
                     }
                     eprintln!("[ipc] new connection");
                     streams.push(stream);
+                    activity = true;
                 }
                 Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => break,
                 Err(e) => {
@@ -87,6 +89,7 @@ fn ipc_server_main(tx: mpsc::Sender<String>, pipe_write: libc::c_int) {
                             libc::write(pipe_write, &1u8 as *const u8 as *const libc::c_void, 1);
                         }
                     }
+                    activity = true;
                 }
                 Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {}
                 Err(e) => {
@@ -100,6 +103,8 @@ fn ipc_server_main(tx: mpsc::Sender<String>, pipe_write: libc::c_int) {
             streams.remove(i);
         }
 
-        std::thread::sleep(std::time::Duration::from_millis(50));
+        if !activity {
+            std::thread::sleep(std::time::Duration::from_millis(20));
+        }
     }
 }
diff --git a/src/main.rs b/src/main.rs
index df2a918..83c813d 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -253,6 +253,10 @@ fn main() {
             if let Some(ref wm) = state.window_manager {
                 wm.manage_dirty();
             }
+            if !state.wm.tap_config_applied && !state.libinput_devices.is_empty() {
+                let qh = event_queue.handle();
+                clearwm::wayland::apply_input_config(&mut state, &qh);
+            }
         }
 
         if state.exit_requested || state.wm.exit_requested {
diff --git a/src/status_server.rs b/src/status_server.rs
index 2db92f9..d734455 100644
--- a/src/status_server.rs
+++ b/src/status_server.rs
@@ -106,6 +106,8 @@ fn status_server_main(rx: mpsc::Receiver<StatusUpdate>) {
     let mut latest: Option<StatusUpdate> = None;
 
     loop {
+        let mut activity = false;
+
         // Accept new connections (non-blocking)
         for _ in 0..5 {
             match listener.accept() {
@@ -133,6 +135,7 @@ fn status_server_main(rx: mpsc::Receiver<StatusUpdate>) {
                         subscription,
                         stream,
                     });
+                    activity = true;
                 }
                 Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {
                     break; // No more pending connections
@@ -150,6 +153,7 @@ fn status_server_main(rx: mpsc::Receiver<StatusUpdate>) {
             match rx.try_recv() {
                 Ok(update) => {
                     latest = Some(update);
+                    activity = true;
                 }
                 Err(mpsc::TryRecvError::Empty) => break,
                 Err(mpsc::TryRecvError::Disconnected) => {
@@ -198,8 +202,10 @@ fn status_server_main(rx: mpsc::Receiver<StatusUpdate>) {
             }
         }
 
-        // Small sleep to avoid busy-looping when nothing is happening
-        std::thread::sleep(std::time::Duration::from_millis(50));
+        if !activity {
+            // Small sleep to avoid busy-looping when nothing is happening
+            std::thread::sleep(std::time::Duration::from_millis(20));
+        }
     }
 }
 
diff --git a/src/types.rs b/src/types.rs
index bb3fe78..7abc041 100644
--- a/src/types.rs
+++ b/src/types.rs
@@ -340,6 +340,9 @@ pub struct WindowManager {
     pub trackpoint_accel_profile: Option<String>,
     /// Whether tap-to-click config has been applied to libinput devices yet
     pub tap_config_applied: bool,
+    pub cursor_theme: Option<String>,
+    pub cursor_size: Option<u32>,
+    pub cursor_theme_applied: bool,
     /// Whether system notifications are enabled
     pub notifications_enable: bool,
     /// Reload commands to execute on configuration reload
@@ -387,6 +390,9 @@ impl Default for WindowManager {
             trackpoint_accel_speed: None,
             trackpoint_accel_profile: None,
             tap_config_applied: false,
+            cursor_theme: None,
+            cursor_size: None,
+            cursor_theme_applied: false,
             notifications_enable: true,
             reload_commands: Vec::new(),
             input_controller: None,
diff --git a/src/wayland.rs b/src/wayland.rs
index 725fa8c..fa60f62 100644
--- a/src/wayland.rs
+++ b/src/wayland.rs
@@ -58,11 +58,49 @@ const IFACE_INPUT_MANAGER: &str = "river_input_manager_v1";
 const IFACE_LIBINPUT_CONFIG: &str = "river_libinput_config_v1";
 const IFACE_WLR_OUTPUT_MANAGER: &str = "zwlr_output_manager_v1";
 
+const CORNER_THRESHOLD: f64 = 16.0;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum PointerOpType {
+    Move,
+    Resize,
+    ResizeLeft,
+    ResizeRight,
+    ResizeBottom,
+    ResizeTop,
+    ResizeBottomLeft,
+    ResizeBottomRight,
+    ResizeTopLeft,
+    ResizeTopRight,
+}
+
+#[derive(Debug, Clone)]
+pub struct PointerOp {
+    pub window_id: u64,
+    pub op_type: PointerOpType,
+    pub start_x: i32,
+    pub start_y: i32,
+    pub start_width: i32,
+    pub start_height: i32,
+}
+
+#[derive(Debug, Clone)]
+pub struct PendingBorderDrag {
+    pub window_id: u64,
+    pub seat_id: u64,
+    pub start_surface_x: f64,
+    pub start_surface_y: f64,
+    pub op_type: PointerOpType,
+}
+
 /// Wayland proxy objects stored alongside each Window, so we can
 /// call protocol methods (set_position, propose_dimensions, etc.) on it.
 pub struct WindowProxy {
     pub river_window: RiverWindowV1,
     pub decoration: Option<crate::decorations::WindowDecoration>,
+    pub dec_left: Option<crate::decorations::WindowDecoration>,
+    pub dec_right: Option<crate::decorations::WindowDecoration>,
+    pub dec_bottom: Option<crate::decorations::WindowDecoration>,
 }
 
 /// Wayland proxy objects stored alongside each Seat.
@@ -72,6 +110,7 @@ pub struct SeatProxy {
     pub wl_seat: Option<wl_seat::WlSeat>,
     pub wl_pointer: Option<wl_pointer::WlPointer>,
     pub cursor_shape_device: Option<WpCursorShapeDeviceV1>,
+    pub last_pointer_enter_serial: u32,
 }
 
 /// Wayland proxy objects stored alongside each Output.
@@ -152,6 +191,12 @@ pub struct AppState {
     pub libinput_devices: Vec<LibinputDeviceInfo>,
     /// The surface the pointer is currently hovering over
     pub pointer_hovered_surface: Option<wl_surface::WlSurface>,
+    pub active_pointer_op: Option<PointerOp>,
+    pub pointer_op_release_pending: bool,
+    pub pending_border_drag: Option<PendingBorderDrag>,
+    pub pending_pointer_op_type: Option<PointerOpType>,
+    pub last_pointer_surface_x: f64,
+    pub last_pointer_surface_y: f64,
     pub input_device_names: std::collections::HashMap<u32, String>,
     pub border_font: Option<fontdue::Font>,
     pub border_font_path: Option<String>,
@@ -215,6 +260,12 @@ impl AppState {
             libinput_config: None,
             libinput_devices: Vec::new(),
             pointer_hovered_surface: None,
+            active_pointer_op: None,
+            pointer_op_release_pending: false,
+            pending_border_drag: None,
+            pending_pointer_op_type: None,
+            last_pointer_surface_x: 0.0,
+            last_pointer_surface_y: 0.0,
             input_device_names: std::collections::HashMap::new(),
             border_font: None,
             border_font_path: None,
@@ -265,6 +316,123 @@ impl AppState {
             .find(|(_, sp)| sp.river_seat.id().protocol_id() == pid)
             .map(|(id, _)| *id)
     }
+
+    fn update_cursor_shape_for_surface(state: &mut Self, pointer: &wl_pointer::WlPointer) {
+        let seat_info = state.seat_proxies.iter().find_map(|(_, sp)| {
+            if sp.wl_pointer.as_ref() == Some(pointer) {
+                Some((sp.cursor_shape_device.clone(), sp.last_pointer_enter_serial))
+            } else {
+                None
+            }
+        });
+
+        let (device, serial) = match seat_info {
+            Some((Some(dev), ser)) => (dev, ser),
+            _ => return,
+        };
+
+        let mut shape = Shape::Default;
+
+        if let Some(ref current_surface) = state.pointer_hovered_surface {
+            let matched_window = state.window_proxies.iter().find_map(|(id, proxy)| {
+                if let Some(dec) = &proxy.decoration {
+                    if &dec.surface == current_surface {
+                        return Some((*id, "top"));
+                    }
+                }
+                if let Some(dec) = &proxy.dec_left {
+                    if &dec.surface == current_surface {
+                        return Some((*id, "left"));
+                    }
+                }
+                if let Some(dec) = &proxy.dec_right {
+                    if &dec.surface == current_surface {
+                        return Some((*id, "right"));
+                    }
+                }
+                if let Some(dec) = &proxy.dec_bottom {
+                    if &dec.surface == current_surface {
+                        return Some((*id, "bottom"));
+                    }
+                }
+                None
+            });
+
+            if let Some((wid, surface_type)) = matched_window {
+                if let Some(w) = state.wm.windows.iter().find(|win| win.id == wid) {
+                    let border_w = if state.wm.expose_active && w.tiling_mode != crate::types::TilingMode::Popup {
+                        state.wm.layout.grid_border_width
+                    } else {
+                        match w.tiling_mode {
+                            crate::types::TilingMode::Cascade => state.wm.layout.cascade_border_width,
+                            crate::types::TilingMode::Fullscreen => state.wm.layout.fullscreen_border_width,
+                            crate::types::TilingMode::Grid => state.wm.layout.grid_border_width,
+                            crate::types::TilingMode::Vsplit => state.wm.layout.vsplit_border_width,
+                            crate::types::TilingMode::Hsplit => state.wm.layout.hsplit_border_width,
+                            crate::types::TilingMode::Floating => state.wm.layout.floating_border_width,
+                            crate::types::TilingMode::Popup => 0,
+                        }
+                    };
+                    let grab_w = border_w.max(10);
+
+                    shape = match surface_type {
+                        "top" => {
+                            let total_width = w.width + 2 * border_w;
+                            let edge_thresh = (border_w as f64).max(8.0);
+                            if state.last_pointer_surface_y < edge_thresh {
+                                if state.last_pointer_surface_x < CORNER_THRESHOLD {
+                                    Shape::NwseResize
+                                } else if state.last_pointer_surface_x > (total_width as f64 - CORNER_THRESHOLD) {
+                                    Shape::NeswResize
+                                } else {
+                                    Shape::NsResize
+                                }
+                            } else {
+                                if state.last_pointer_surface_x < CORNER_THRESHOLD {
+                                    Shape::EwResize
+                                } else if state.last_pointer_surface_x > (total_width as f64 - CORNER_THRESHOLD) {
+                                    Shape::EwResize
+                                } else {
+                                    Shape::Default
+                                }
+                            }
+                        }
+                        "left" => {
+                            if state.last_pointer_surface_y < CORNER_THRESHOLD {
+                                Shape::NwseResize
+                            } else if state.last_pointer_surface_y > (w.height as f64 - CORNER_THRESHOLD) {
+                                Shape::NeswResize
+                            } else {
+                                Shape::EwResize
+                            }
+                        }
+                        "right" => {
+                            if state.last_pointer_surface_y < CORNER_THRESHOLD {
+                                Shape::NeswResize
+                            } else if state.last_pointer_surface_y > (w.height as f64 - CORNER_THRESHOLD) {
+                                Shape::NwseResize
+                            } else {
+                                Shape::EwResize
+                            }
+                        }
+                        "bottom" => {
+                            let total_width = w.width + 2 * grab_w;
+                            if state.last_pointer_surface_x < CORNER_THRESHOLD {
+                                Shape::NeswResize
+                            } else if state.last_pointer_surface_x > (total_width as f64 - CORNER_THRESHOLD) {
+                                Shape::NwseResize
+                            } else {
+                                Shape::NsResize
+                            }
+                        }
+                        _ => Shape::Default,
+                    };
+                }
+            }
+        }
+
+        device.set_shape(serial, shape);
+    }
 }
 
 // --- Dispatch implementations ---
@@ -434,9 +602,21 @@ impl Dispatch<RiverWindowManagerV1, ()> for AppState {
                 // tinyrwm's remove_windows() pattern which cleans up seat ops
                 // referencing closed window proxies.
                 for (closed_id, closed_app_id) in &closed_ids {
-                    state.window_proxies.retain(|(id, wp)| {
+                    state.window_proxies.retain_mut(|(id, wp)| {
                         if id == closed_id {
-                            if let Some(dec) = wp.decoration.as_ref() {
+                            if let Some(dec) = wp.decoration.take() {
+                                dec.decoration.destroy();
+                                dec.surface.destroy();
+                            }
+                            if let Some(dec) = wp.dec_left.take() {
+                                dec.decoration.destroy();
+                                dec.surface.destroy();
+                            }
+                            if let Some(dec) = wp.dec_right.take() {
+                                dec.decoration.destroy();
+                                dec.surface.destroy();
+                            }
+                            if let Some(dec) = wp.dec_bottom.take() {
                                 dec.decoration.destroy();
                                 dec.surface.destroy();
                             }
@@ -755,12 +935,24 @@ impl Dispatch<RiverWindowManagerV1, ()> for AppState {
                     state.wm.needs_focus = false;
                 }
 
+                if state.pointer_op_release_pending {
+                    state.pointer_op_release_pending = false;
+                    state.active_pointer_op = None;
+                    for (_sid, sp) in &state.seat_proxies {
+                        sp.river_seat.op_end();
+                    }
+                    for seat in &mut state.wm.seats {
+                        seat.interacted_window_id = None;
+                    }
+                    state.wm.needs_render = true;
+                }
+
                 // Execute pending actions from key/pointer bindings (tinyrwm pattern).
                 // Like tinyrwm, we defer action execution to ManageStart so all
                 // state mutations happen during the manage sequence.
                 // Collect pending actions first to avoid borrow checker issues
                 // (execute_action borrows state mutably).
-                let pending: Vec<(Action, Option<String>)> = state
+                let pending: Vec<(u64, Action, Option<String>)> = state
                     .wm
                     .seats
                     .iter_mut()
@@ -769,14 +961,14 @@ impl Dispatch<RiverWindowManagerV1, ()> for AppState {
                             let action = seat.pending_action;
                             let command = seat.pending_command.take();
                             seat.pending_action = Action::None;
-                            Some((action, command))
+                            Some((seat.id, action, command))
                         } else {
                             None
                         }
                     })
                     .collect();
-                for (action, command) in pending {
-                    execute_action(state, &action, command.as_deref());
+                for (seat_id, action, command) in pending {
+                    execute_action(state, seat_id, &action, command.as_deref());
                 }
 
                 wm_proxy.manage_finish();
@@ -929,6 +1121,15 @@ impl Dispatch<RiverWindowManagerV1, ()> for AppState {
                 if !state.wm.tap_config_applied && !state.libinput_devices.is_empty() {
                     crate::wayland::apply_input_config(state, qhandle);
                 }
+
+                if !state.wm.cursor_theme_applied {
+                    let theme = state.wm.cursor_theme.clone().unwrap_or_else(|| "default".to_string());
+                    let size = state.wm.cursor_size.unwrap_or(24);
+                    for (_, sp) in &state.seat_proxies {
+                        sp.river_seat.set_xcursor_theme(theme.clone(), size);
+                    }
+                    state.wm.cursor_theme_applied = true;
+                }
             }
 
             // Window event: field is `id` (the new RiverWindowV1 proxy)
@@ -950,7 +1151,13 @@ impl Dispatch<RiverWindowManagerV1, ()> for AppState {
                 state.wm.windows.push(window);
                 state
                     .window_proxies
-                    .push((id, WindowProxy { river_window, decoration: None }));
+                    .push((id, WindowProxy {
+                        river_window,
+                        decoration: None,
+                        dec_left: None,
+                        dec_right: None,
+                        dec_bottom: None,
+                    }));
                 eprintln!("[window] new window id={} (app_id pending)", id);
             }
 
@@ -999,6 +1206,10 @@ impl Dispatch<RiverWindowManagerV1, ()> for AppState {
                 seat.id = id;
                 seat.is_new = true;
 
+                let cursor_theme = state.wm.cursor_theme.clone().unwrap_or_else(|| "default".to_string());
+                let cursor_size = state.wm.cursor_size.unwrap_or(24);
+                river_seat.set_xcursor_theme(cursor_theme, cursor_size);
+
                 state.wm.seats.push(seat);
                 state.seat_proxies.push((
                     id,
@@ -1008,6 +1219,7 @@ impl Dispatch<RiverWindowManagerV1, ()> for AppState {
                         wl_seat: None,
                         wl_pointer: None,
                         cursor_shape_device: None,
+                        last_pointer_enter_serial: 0,
                     },
                 ));
                 eprintln!("wm_handle_seat: new seat id={}", id);
@@ -1337,8 +1549,76 @@ impl Dispatch<RiverSeatV1, ()> for AppState {
                 }
             }
 
-            river_seat_v1::Event::OpDelta { .. } => {}
-            river_seat_v1::Event::OpRelease => {}
+            river_seat_v1::Event::OpDelta { dx, dy } => {
+                if let Some(ref op) = state.active_pointer_op {
+                    let wid = op.window_id;
+                    if let Some(win) = state.wm.get_window_mut(wid) {
+                        match op.op_type {
+                            PointerOpType::Move => {
+                                win.x = op.start_x + dx;
+                                win.y = op.start_y + dy;
+                            }
+                            PointerOpType::Resize | PointerOpType::ResizeRight => {
+                                win.width = std::cmp::max(50, op.start_width + dx);
+                            }
+                            PointerOpType::ResizeLeft => {
+                                let target_width = std::cmp::max(50, op.start_width - dx);
+                                let actual_dx = op.start_width - target_width;
+                                win.x = op.start_x + actual_dx;
+                                win.width = target_width;
+                            }
+                            PointerOpType::ResizeBottom => {
+                                win.height = std::cmp::max(50, op.start_height + dy);
+                            }
+                            PointerOpType::ResizeTop => {
+                                let target_height = std::cmp::max(50, op.start_height - dy);
+                                let actual_dy = op.start_height - target_height;
+                                win.y = op.start_y + actual_dy;
+                                win.height = target_height;
+                            }
+                            PointerOpType::ResizeBottomRight => {
+                                win.width = std::cmp::max(50, op.start_width + dx);
+                                win.height = std::cmp::max(50, op.start_height + dy);
+                            }
+                            PointerOpType::ResizeBottomLeft => {
+                                let target_width = std::cmp::max(50, op.start_width - dx);
+                                let actual_dx = op.start_width - target_width;
+                                win.x = op.start_x + actual_dx;
+                                win.width = target_width;
+                                win.height = std::cmp::max(50, op.start_height + dy);
+                            }
+                            PointerOpType::ResizeTopLeft => {
+                                let target_width = std::cmp::max(50, op.start_width - dx);
+                                let actual_dx = op.start_width - target_width;
+                                win.x = op.start_x + actual_dx;
+                                win.width = target_width;
+
+                                let target_height = std::cmp::max(50, op.start_height - dy);
+                                let actual_dy = op.start_height - target_height;
+                                win.y = op.start_y + actual_dy;
+                                win.height = target_height;
+                            }
+                            PointerOpType::ResizeTopRight => {
+                                win.width = std::cmp::max(50, op.start_width + dx);
+
+                                let target_height = std::cmp::max(50, op.start_height - dy);
+                                let actual_dy = op.start_height - target_height;
+                                win.y = op.start_y + actual_dy;
+                                win.height = target_height;
+                            }
+                        }
+                    }
+                    if let Some(ref wm) = state.window_manager {
+                        wm.manage_dirty();
+                    }
+                }
+            }
+            river_seat_v1::Event::OpRelease => {
+                state.pointer_op_release_pending = true;
+                if let Some(ref wm) = state.window_manager {
+                    wm.manage_dirty();
+                }
+            }
 
             _ => {}
         }
@@ -1707,7 +1987,7 @@ impl Dispatch<RiverNodeV1, ()> for AppState {
 // --- Helper functions ---
 
 /// Execute an action triggered by a keybinding or pointer binding.
-fn execute_action(state: &mut AppState, action: &crate::types::Action, command: Option<&str>) {
+fn execute_action(state: &mut AppState, seat_id: u64, action: &crate::types::Action, command: Option<&str>) {
     use crate::types::Action;
     match action {
         Action::None => {}
@@ -1820,11 +2100,54 @@ fn execute_action(state: &mut AppState, action: &crate::types::Action, command:
                 }
             }
         }
-        Action::Move => {
-            // TODO: pointer move
-        }
-        Action::Resize => {
-            // TODO: pointer resize
+        Action::Move | Action::Resize => {
+            let op_type = if *action == Action::Move {
+                PointerOpType::Move
+            } else {
+                state.pending_pointer_op_type.take().unwrap_or(PointerOpType::Resize)
+            };
+
+            let target_wid = if let Some(seat) = state.wm.seats.iter().find(|s| s.id == seat_id) {
+                seat.interacted_window_id.or(seat.focused_window_id)
+            } else {
+                None
+            };
+
+            if let Some(wid) = target_wid {
+                let mut window_found = false;
+                let mut needs_render = false;
+                if let Some(win) = state.wm.get_window_mut(wid) {
+                    if win.tiling_mode != TilingMode::Fullscreen && win.tiling_mode != TilingMode::Popup {
+                        if win.tiling_mode != TilingMode::Floating {
+                            win.tiling_mode = TilingMode::Floating;
+                            needs_render = true;
+                        }
+                        win.mode_locked = true;
+                        window_found = true;
+                    }
+                }
+
+                if window_found {
+                    if needs_render {
+                        state.wm.needs_render = true;
+                        state.wm.needs_focus = true;
+                        state.wm.needs_status_update = true;
+                    }
+                    if let Some((_, sp)) = state.seat_proxies.iter().find(|(sid, _)| *sid == seat_id) {
+                        sp.river_seat.op_start_pointer();
+                    }
+                    if let Some(win) = state.wm.get_window(wid) {
+                        state.active_pointer_op = Some(PointerOp {
+                            window_id: wid,
+                            op_type,
+                            start_x: win.x,
+                            start_y: win.y,
+                            start_width: win.width,
+                            start_height: win.height,
+                        });
+                    }
+                }
+            }
         }
         Action::Exit => {
             state.wm.exit_requested = true;
@@ -2749,14 +3072,18 @@ pub fn apply_input_config(state: &mut AppState, qhandle: &QueueHandle<AppState>)
         return;
     }
 
-    // Wait until ALL discovered devices have received their name and support events.
+    // Wait until ALL discovered devices have received their name and support events (if they have a corresponding input_device).
     let all_info_received = state.libinput_devices.iter().all(|d| {
-        d.name_received &&
-        d.tap_finger_count >= 0 &&
-        d.accel_profiles_support.is_some() &&
-        d.natural_scroll_supported.is_some() &&
-        d.dwt_supported.is_some() &&
-        d.dwtp_supported.is_some()
+        if d.input_device.is_none() {
+            true
+        } else {
+            d.name_received &&
+            d.tap_finger_count >= 0 &&
+            d.accel_profiles_support.is_some() &&
+            d.natural_scroll_supported.is_some() &&
+            d.dwt_supported.is_some() &&
+            d.dwtp_supported.is_some()
+        }
     });
     if !all_info_received {
         return;
@@ -2772,6 +3099,11 @@ pub fn apply_input_config(state: &mut AppState, qhandle: &QueueHandle<AppState>)
             continue;
         }
 
+        if dev_info.input_device.is_none() {
+            dev_info.config_applied = true;
+            continue;
+        }
+
         let is_trackpoint = dev_info.name.to_lowercase().contains("trackpoint");
 
         // 1. Tap to click
@@ -2914,7 +3246,7 @@ impl Dispatch<wl_seat::WlSeat, ()> for AppState {
                         
                         if let Some(ref csm) = state.cursor_shape_manager {
                             let device = csm.get_pointer(&wl_pointer, qhandle, ());
-                            device.set_shape(0, Shape::Crosshair);
+                            device.set_shape(0, Shape::Default);
                             seat_proxy.cursor_shape_device = Some(device);
                         }
                         
@@ -2937,33 +3269,263 @@ impl Dispatch<wl_seat::WlSeat, ()> for AppState {
 impl Dispatch<wl_pointer::WlPointer, ()> for AppState {
     fn event(
         state: &mut Self,
-        _proxy: &wl_pointer::WlPointer,
+        proxy: &wl_pointer::WlPointer,
         event: wl_pointer::Event,
         _data: &(),
         _conn: &Connection,
         _qhandle: &QueueHandle<Self>,
     ) {
         match event {
-            wl_pointer::Event::Enter { surface, .. } => {
+            wl_pointer::Event::Enter { serial, surface, surface_x, surface_y } => {
+                eprintln!("[pointer] enter surface={:?} x={} y={}", surface, surface_x, surface_y);
                 state.pointer_hovered_surface = Some(surface);
+                state.last_pointer_surface_x = surface_x;
+                state.last_pointer_surface_y = surface_y;
+
+                if let Some((_, seat_proxy)) = state.seat_proxies.iter_mut().find(|(_, sp)| {
+                    sp.wl_pointer.as_ref() == Some(proxy)
+                }) {
+                    seat_proxy.last_pointer_enter_serial = serial;
+                }
+
+                Self::update_cursor_shape_for_surface(state, proxy);
             }
             wl_pointer::Event::Leave { .. } => {
+                eprintln!("[pointer] leave");
                 state.pointer_hovered_surface = None;
+                state.pending_border_drag = None;
+                Self::update_cursor_shape_for_surface(state, proxy);
+            }
+            wl_pointer::Event::Motion { surface_x, surface_y, .. } => {
+                state.last_pointer_surface_x = surface_x;
+                state.last_pointer_surface_y = surface_y;
+
+                Self::update_cursor_shape_for_surface(state, proxy);
+
+                if let Some(pending) = state.pending_border_drag.clone() {
+                    let dx = surface_x - pending.start_surface_x;
+                    let dy = surface_y - pending.start_surface_y;
+                    let dist = (dx * dx + dy * dy).sqrt();
+                    // Threshold of 12.0 logical pixels to distinguish click vs drag
+                    if dist > 12.0 {
+                        state.pending_border_drag = None;
+
+                        // Start the drag! Set the window to Floating and lock it.
+                        let mut needs_render = false;
+                        if let Some(win) = state.wm.get_window_mut(pending.window_id) {
+                            if win.tiling_mode != TilingMode::Fullscreen && win.tiling_mode != TilingMode::Popup {
+                                if win.tiling_mode != TilingMode::Floating {
+                                    win.tiling_mode = TilingMode::Floating;
+                                    needs_render = true;
+                                }
+                                win.mode_locked = true;
+                            }
+                        }
+
+                        if needs_render {
+                            state.wm.needs_render = true;
+                            state.wm.needs_focus = true;
+                            state.wm.needs_status_update = true;
+                        }
+
+                        // Store the pending op type in AppState so execute_action can retrieve it
+                        state.pending_pointer_op_type = Some(pending.op_type);
+
+                        // Tell River to start pointer move/resize grab
+                        if let Some(seat) = state.wm.seats.iter_mut().find(|s| s.id == pending.seat_id) {
+                            seat.interacted_window_id = Some(pending.window_id);
+                            seat.pending_action = if pending.op_type == PointerOpType::Move {
+                                crate::types::Action::Move
+                            } else {
+                                crate::types::Action::Resize
+                            };
+                            if let Some(ref wm) = state.window_manager {
+                                wm.manage_dirty();
+                            }
+                        }
+                    }
+                }
             }
             wl_pointer::Event::Button { button, state: btn_state, .. } => {
+                // Left click on border
+                if button == 0x110 {
+                    if btn_state == wayland_client::WEnum::Value(wl_pointer::ButtonState::Pressed) {
+                        eprintln!("[pointer] left button pressed, hovered={:?}", state.pointer_hovered_surface);
+                        if let Some(ref current_surface) = state.pointer_hovered_surface {
+                            let matched_window = state.window_proxies.iter().find_map(|(id, proxy)| {
+                                if let Some(dec) = &proxy.decoration {
+                                    if &dec.surface == current_surface {
+                                        return Some((*id, "top"));
+                                    }
+                                }
+                                if let Some(dec) = &proxy.dec_left {
+                                    if &dec.surface == current_surface {
+                                        return Some((*id, "left"));
+                                    }
+                                }
+                                if let Some(dec) = &proxy.dec_right {
+                                    if &dec.surface == current_surface {
+                                        return Some((*id, "right"));
+                                    }
+                                }
+                                if let Some(dec) = &proxy.dec_bottom {
+                                    if &dec.surface == current_surface {
+                                        return Some((*id, "bottom"));
+                                    }
+                                }
+                                None
+                            });
+                            eprintln!("[pointer] matched_window={:?}", matched_window);
+
+                            if let Some((wid, surface_type)) = matched_window {
+                                let window_tiling_mode = state.wm.windows.iter().find(|w| w.id == wid).map(|w| w.tiling_mode);
+                                if let Some(mode) = window_tiling_mode {
+                                    if mode != TilingMode::Fullscreen {
+                                        // 1. Focus the window immediately
+                                        let seat_id = state.seat_proxies.iter().find_map(|(sid, sp)| {
+                                            if sp.wl_pointer.as_ref() == Some(proxy) {
+                                                Some(*sid)
+                                            } else {
+                                                None
+                                            }
+                                        });
+
+                                        let mut focus_changed = false;
+                                        if let Some(sid) = seat_id {
+                                            if let Some(seat) = state.wm.seats.iter_mut().find(|s| s.id == sid) {
+                                                focus_changed = seat.focused_window_id != Some(wid);
+                                                seat.focused_window_id = Some(wid);
+                                                state.wm.move_window_to_end(wid);
+                                                if state.wm.expose_active {
+                                                    state.wm.expose_active = false;
+                                                }
+                                                state.wm.needs_focus = true;
+                                                state.wm.needs_render = true;
+                                                state.wm.needs_status_update = true;
+                                                if let Some(ref wm) = state.window_manager {
+                                                    wm.manage_dirty();
+                                                }
+                                            }
+
+                                            if !focus_changed {
+                                                // Determine the specific PointerOpType based on coordinates on the matched surface
+                                                let op_type = if let Some(w) = state.wm.windows.iter().find(|win| win.id == wid) {
+                                                    let border_w = if state.wm.expose_active && w.tiling_mode != crate::types::TilingMode::Popup {
+                                                        state.wm.layout.grid_border_width
+                                                    } else {
+                                                        match w.tiling_mode {
+                                                            crate::types::TilingMode::Cascade => state.wm.layout.cascade_border_width,
+                                                            crate::types::TilingMode::Fullscreen => state.wm.layout.fullscreen_border_width,
+                                                            crate::types::TilingMode::Grid => state.wm.layout.grid_border_width,
+                                                            crate::types::TilingMode::Vsplit => state.wm.layout.vsplit_border_width,
+                                                            crate::types::TilingMode::Hsplit => state.wm.layout.hsplit_border_width,
+                                                            crate::types::TilingMode::Floating => state.wm.layout.floating_border_width,
+                                                            crate::types::TilingMode::Popup => 0,
+                                                        }
+                                                    };
+                                                    let grab_w = border_w.max(10);
+
+                                                    match surface_type {
+                                                        "top" => {
+                                                            let total_width = w.width + 2 * border_w;
+                                                            let edge_thresh = (border_w as f64).max(8.0);
+                                                            if state.last_pointer_surface_y < edge_thresh {
+                                                                if state.last_pointer_surface_x < CORNER_THRESHOLD {
+                                                                    PointerOpType::ResizeTopLeft
+                                                                } else if state.last_pointer_surface_x > (total_width as f64 - CORNER_THRESHOLD) {
+                                                                    PointerOpType::ResizeTopRight
+                                                                } else {
+                                                                    PointerOpType::ResizeTop
+                                                                }
+                                                            } else {
+                                                                if state.last_pointer_surface_x < CORNER_THRESHOLD {
+                                                                    PointerOpType::ResizeLeft
+                                                                } else if state.last_pointer_surface_x > (total_width as f64 - CORNER_THRESHOLD) {
+                                                                    PointerOpType::ResizeRight
+                                                                } else {
+                                                                    PointerOpType::Move
+                                                                }
+                                                            }
+                                                        }
+                                                        "left" => {
+                                                            if state.last_pointer_surface_y < CORNER_THRESHOLD {
+                                                                PointerOpType::ResizeTopLeft
+                                                            } else if state.last_pointer_surface_y > (w.height as f64 - CORNER_THRESHOLD) {
+                                                                PointerOpType::ResizeBottomLeft
+                                                            } else {
+                                                                PointerOpType::ResizeLeft
+                                                            }
+                                                        }
+                                                        "right" => {
+                                                            if state.last_pointer_surface_y < CORNER_THRESHOLD {
+                                                                PointerOpType::ResizeTopRight
+                                                            } else if state.last_pointer_surface_y > (w.height as f64 - CORNER_THRESHOLD) {
+                                                                PointerOpType::ResizeBottomRight
+                                                            } else {
+                                                                PointerOpType::ResizeRight
+                                                            }
+                                                        }
+                                                        "bottom" => {
+                                                            let total_width = w.width + 2 * grab_w;
+                                                            if state.last_pointer_surface_x < CORNER_THRESHOLD {
+                                                                PointerOpType::ResizeBottomLeft
+                                                            } else if state.last_pointer_surface_x > (total_width as f64 - CORNER_THRESHOLD) {
+                                                                PointerOpType::ResizeBottomRight
+                                                            } else {
+                                                                PointerOpType::ResizeBottom
+                                                            }
+                                                        }
+                                                        _ => PointerOpType::Move,
+                                                    }
+                                                } else {
+                                                    PointerOpType::Move
+                                                };
+
+                                                // 2. Store the pending drag info (threshold check is in Event::Motion)
+                                                state.pending_border_drag = Some(PendingBorderDrag {
+                                                    window_id: wid,
+                                                    seat_id: sid,
+                                                    start_surface_x: state.last_pointer_surface_x,
+                                                    start_surface_y: state.last_pointer_surface_y,
+                                                    op_type,
+                                                });
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    } else if btn_state == wayland_client::WEnum::Value(wl_pointer::ButtonState::Released) {
+                        eprintln!("[pointer] left button released");
+                        state.pending_border_drag = None;
+                    }
+                }
+
                 // Middle click is button 0x112 (BTN_MIDDLE)
                 if button == 0x112 && btn_state == wayland_client::WEnum::Value(wl_pointer::ButtonState::Pressed) {
                     if let Some(ref current_surface) = state.pointer_hovered_surface {
                         let matched_window_id = state.window_proxies.iter().find_map(|(id, proxy)| {
                             if let Some(dec) = &proxy.decoration {
                                 if &dec.surface == current_surface {
-                                    Some(*id)
-                                } else {
-                                    None
+                                    return Some(*id);
                                 }
-                            } else {
-                                None
                             }
+                            if let Some(dec) = &proxy.dec_left {
+                                if &dec.surface == current_surface {
+                                    return Some(*id);
+                                }
+                            }
+                            if let Some(dec) = &proxy.dec_right {
+                                if &dec.surface == current_surface {
+                                    return Some(*id);
+                                }
+                            }
+                            if let Some(dec) = &proxy.dec_bottom {
+                                if &dec.surface == current_surface {
+                                    return Some(*id);
+                                }
+                            }
+                            None
                         });
 
                         if let Some(wid) = matched_window_id {