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

commit44de7be64fb18b21f79f253322a7df26d2ca3ced
parentcfb9b26957
authorLucas Galante <[email protected]>
date2026-06-29 13:07
Add gestures config to input section, toggle trackpad gesture handlers, and support window focus raising

 scripts/cce-desktop-menu            | 14 ++++++++++++--
 src/server/cce_window_management.rs |  5 +++++
 src/server/config.rs                |  7 +++++++
 src/server/cursor.rs                | 30 ++++++++++++++++++++++++++++++
 src/server/seat.rs                  |  8 ++++++++
 src/server/window.rs                | 10 +++++-----
 src/server/window_manager.rs        | 14 +++++++-------
 7 files changed, 74 insertions(+), 14 deletions(-)

diff --git a/scripts/cce-desktop-menu b/scripts/cce-desktop-menu
index e7732cf..ec5ab11 100644
--- a/scripts/cce-desktop-menu
+++ b/scripts/cce-desktop-menu
@@ -32,11 +32,13 @@ json_layout='{
       "justify": "left",
       "widgets": [
         { "type": "button", "text": "Terminal", "id": "terminal" },
+        { "type": "button", "text": "Files", "id": "files" },
+        { "type": "button", "text": "Data Editor", "id": "data_editor" },
         { "type": "button", "text": "Applications", "id": "apps" },
-        { "type": "button", "text": "System Settings", "id": "settings" },
+        { "type": "button", "text": "Settings", "id": "settings" },
         { "type": "button", "text": "Expose Windows", "id": "expose" },
         { "type": "button", "text": "Reload Config", "id": "reload" },
-        { "type": "button", "text": "Exit CCE", "id": "exit" }
+        { "type": "button", "text": "Logout", "id": "exit" }
       ]
     }
   ]
@@ -55,6 +57,14 @@ case "$btn" in
         # Run terminal
         foot &
         ;;
+    "files")
+        # Run files
+        /home/lsgalante/.local/bin/cce-files &
+        ;;
+    "data_editor")
+        # Run data editor
+        /home/lsgalante/.local/bin/cce-data-editor &
+        ;;
     "apps")
         # Run applications search/list
         $CLEAR_CLOUD --mode apps &
diff --git a/src/server/cce_window_management.rs b/src/server/cce_window_management.rs
index 881e716..e737729 100644
--- a/src/server/cce_window_management.rs
+++ b/src/server/cce_window_management.rs
@@ -118,6 +118,11 @@ unsafe extern "C" fn toplevel_set_floating(
     if let Some(window) = resolve_window(server, window_key) {
         (*window).tiling_mode = crate::tiling::TilingMode::Floating;
         (*window).mode_locked = true;
+        if let Some(seat) = (*server).wm.first_seat() {
+            if (*seat).focused == crate::seat::Focus::Window(window) {
+                (*server).wm.raise_window(window);
+            }
+        }
         (*server).wm.dirty_windowing();
 
         // Send floating_state(1)
diff --git a/src/server/config.rs b/src/server/config.rs
index c45ad6d..5251176 100644
--- a/src/server/config.rs
+++ b/src/server/config.rs
@@ -213,6 +213,12 @@ pub struct InputDeviceConfigRule {
     pub scroll_factor: Option<f64>,
 }
 
+#[derive(Debug, Deserialize, Clone, Default, PartialEq, Eq)]
+pub struct GesturesConfig {
+    pub swipe: Option<bool>,
+    pub pinch: Option<bool>,
+}
+
 #[derive(Debug, Deserialize, Clone, Default)]
 pub struct InputConfig {
     pub tap_to_click: Option<bool>,
@@ -223,6 +229,7 @@ pub struct InputConfig {
     pub dwtp: Option<bool>,
     pub trackpoint_accel_speed: Option<f64>,
     pub trackpoint_accel_profile: Option<String>,
+    pub gestures: Option<GesturesConfig>,
 }
 
 #[derive(Debug, Deserialize, Clone, Default)]
diff --git a/src/server/cursor.rs b/src/server/cursor.rs
index abb3549..3e8dfef 100644
--- a/src/server/cursor.rs
+++ b/src/server/cursor.rs
@@ -1534,6 +1534,11 @@ unsafe extern "C" fn handle_swipe_begin(listener: *mut ffi::wl_listener, data: *
     let event = data as *mut ffi::wlr_pointer_swipe_begin_event;
 
     let seat = &mut *cursor.seat;
+    let wm = &(*seat.server).wm;
+    let swipe_enabled = wm.input_config.gestures.as_ref().and_then(|g| g.swipe).unwrap_or(true);
+    if !swipe_enabled {
+        return;
+    }
     seat.handle_activity();
 
     cursor.gesture_dx = 0.0;
@@ -1559,6 +1564,11 @@ unsafe extern "C" fn handle_swipe_update(listener: *mut ffi::wl_listener, data:
     let event = data as *mut ffi::wlr_pointer_swipe_update_event;
 
     let seat = &mut *cursor.seat;
+    let wm = &(*seat.server).wm;
+    let swipe_enabled = wm.input_config.gestures.as_ref().and_then(|g| g.swipe).unwrap_or(true);
+    if !swipe_enabled {
+        return;
+    }
     seat.handle_activity();
 
     if (*event).fingers == 3 {
@@ -1698,6 +1708,11 @@ unsafe extern "C" fn handle_swipe_end(listener: *mut ffi::wl_listener, data: *mu
     let event = data as *mut ffi::wlr_pointer_swipe_end_event;
 
     let seat = &mut *cursor.seat;
+    let wm = &(*seat.server).wm;
+    let swipe_enabled = wm.input_config.gestures.as_ref().and_then(|g| g.swipe).unwrap_or(true);
+    if !swipe_enabled {
+        return;
+    }
     seat.handle_activity();
 
     log::info!("handle_swipe_end: cancelled={}", (*event).cancelled);
@@ -1724,6 +1739,11 @@ unsafe extern "C" fn handle_pinch_begin(listener: *mut ffi::wl_listener, data: *
     let event = data as *mut ffi::wlr_pointer_pinch_begin_event;
 
     let seat = &mut *cursor.seat;
+    let wm = &(*seat.server).wm;
+    let pinch_enabled = wm.input_config.gestures.as_ref().and_then(|g| g.pinch).unwrap_or(true);
+    if !pinch_enabled {
+        return;
+    }
     seat.handle_activity();
 
     cursor.gesture_scale = 1.0;
@@ -1746,6 +1766,11 @@ unsafe extern "C" fn handle_pinch_update(listener: *mut ffi::wl_listener, data:
     let event = data as *mut ffi::wlr_pointer_pinch_update_event;
 
     let seat = &mut *cursor.seat;
+    let wm = &(*seat.server).wm;
+    let pinch_enabled = wm.input_config.gestures.as_ref().and_then(|g| g.pinch).unwrap_or(true);
+    if !pinch_enabled {
+        return;
+    }
     seat.handle_activity();
 
     if cursor.gesture_triggered {
@@ -1815,6 +1840,11 @@ unsafe extern "C" fn handle_pinch_end(listener: *mut ffi::wl_listener, data: *mu
     let event = data as *mut ffi::wlr_pointer_pinch_end_event;
 
     let seat = &mut *cursor.seat;
+    let wm = &(*seat.server).wm;
+    let pinch_enabled = wm.input_config.gestures.as_ref().and_then(|g| g.pinch).unwrap_or(true);
+    if !pinch_enabled {
+        return;
+    }
     seat.handle_activity();
 
     if cursor.gesture_triggered {
diff --git a/src/server/seat.rs b/src/server/seat.rs
index a4458b4..dd00fd9 100644
--- a/src/server/seat.rs
+++ b/src/server/seat.rs
@@ -310,6 +310,13 @@ impl Seat {
             }
         }
 
+        if let Focus::Window(window) = new_focus {
+            if !window.is_null() && (*window).tiling_mode == crate::tiling::TilingMode::Floating {
+                (*self.server).wm.raise_window(window);
+                (*self.server).wm.dirty_windowing();
+            }
+        }
+
         if self.focused == new_focus {
             return;
         }
@@ -950,6 +957,7 @@ impl Seat {
                 {
                     (*win).tiling_mode = crate::tiling::TilingMode::Floating;
                     (*win).mode_locked = true;
+                    (*self.server).wm.raise_window(win);
                 }
                 
                 match op.op_type {
diff --git a/src/server/window.rs b/src/server/window.rs
index 04fe070..2a57993 100644
--- a/src/server/window.rs
+++ b/src/server/window.rs
@@ -270,7 +270,7 @@ impl Window {
     }
 
     pub unsafe fn is_status_bar(&self) -> bool {
-        self.get_app_id_string().as_deref() == Some("cce-status")
+        self.get_app_id_string().as_deref().map_or(false, |id| id.starts_with("cce-status"))
     }
 
 
@@ -630,7 +630,7 @@ impl Window {
             return;
         }
         let app_id_str = self.get_app_id_string().unwrap_or_default();
-        if app_id_str.is_empty() || app_id_str == "cce-status" {
+        if app_id_str.is_empty() || app_id_str.starts_with("cce-status") {
             return;
         }
         let title_str = self.get_title_string().unwrap_or_default();
@@ -697,7 +697,7 @@ impl Window {
         let app_id_ptr = self.get_app_id();
         let is_status_bar = if !app_id_ptr.is_null() {
             let app_id = std::ffi::CStr::from_ptr(app_id_ptr).to_string_lossy();
-            app_id == "cce-status"
+            app_id.starts_with("cce-status")
         } else {
             false
         };
@@ -1483,7 +1483,7 @@ impl Window {
 
     pub unsafe fn notify_app_id(&mut self) {
         self.wm_scheduled.dirty_app_id = true;
-        if self.get_app_id_string().as_deref() == Some("cce-status") {
+        if self.get_app_id_string().as_deref().map_or(false, |id| id.starts_with("cce-status")) {
             self.tiling_mode = crate::tiling::TilingMode::Status;
         }
         self.try_restore();
@@ -1668,7 +1668,7 @@ impl Window {
             let app_id_ptr = self.get_app_id();
             let is_status_bar = if !app_id_ptr.is_null() {
                 let app_id = std::ffi::CStr::from_ptr(app_id_ptr).to_string_lossy();
-                app_id == "cce-status"
+                app_id.starts_with("cce-status")
             } else {
                 false
             };
diff --git a/src/server/window_manager.rs b/src/server/window_manager.rs
index ddfd702..93345e9 100644
--- a/src/server/window_manager.rs
+++ b/src/server/window_manager.rs
@@ -827,7 +827,7 @@ impl WindowManager {
             return crate::tiling::TilingMode::Status;
         }
         let app_id = (*win).get_app_id_string();
-        if app_id.as_deref() == Some("cce-notification-daemon") || app_id.as_deref() == Some("clear-notification-daemon") {
+        if app_id.as_deref() == Some("cce-notifier") || app_id.as_deref() == Some("cce-notification-daemon") || app_id.as_deref() == Some("clear-notification-daemon") {
             return crate::tiling::TilingMode::Popup;
         }
         if app_id.as_deref() == Some("cce-cloud") {
@@ -997,7 +997,7 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
                 }
 
                 let app_id = (*win_ptr).get_app_id_string();
-                let is_status_bar = app_id.as_deref() == Some("cce-status");
+                let is_status_bar = app_id.as_deref().map_or(false, |id| id.starts_with("cce-status"));
                 
                 if is_status_bar {
                     (*win_ptr).tiling_mode = crate::tiling::TilingMode::Status;
@@ -1442,7 +1442,7 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
         for &w in self.focus_history.iter() {
             if !w.is_null() && !(*w).closed && !(*w).minimized && matches!((*w).state, crate::window::WindowState::Mapped) {
                 let app_id = (*w).get_app_id_string();
-                let is_status_bar = app_id.as_deref() == Some("cce-status");
+                let is_status_bar = app_id.as_deref().map_or(false, |id| id.starts_with("cce-status"));
                 if !is_status_bar {
                     next_focus = w;
                     break;
@@ -1453,7 +1453,7 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
             for &w in self.windows.iter() {
                 if !w.is_null() && !(*w).closed && !(*w).minimized && matches!((*w).state, crate::window::WindowState::Mapped) {
                     let app_id = (*w).get_app_id_string();
-                    let is_status_bar = app_id.as_deref() == Some("cce-status");
+                    let is_status_bar = app_id.as_deref().map_or(false, |id| id.starts_with("cce-status"));
                     if !is_status_bar {
                         next_focus = w;
                     }
@@ -1477,7 +1477,7 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
                 continue;
             }
             if let Some(app_id) = (*win_ptr).get_app_id_string() {
-                if app_id == "cce-status" {
+                if app_id.starts_with("cce-status") {
                     status_bar_windows.push(win_ptr);
                 }
             }
@@ -1629,7 +1629,7 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
                         if let crate::wm_node::WmNodeType::Window(window) = (*node).get() {
                             if !window.is_null() && !(*window).closed && !(*window).minimized {
                                 let is_status_bar = (*window).get_app_id_string()
-                                    .map_or(false, |aid| aid == "cce-status");
+                                    .map_or(false, |aid| aid.starts_with("cce-status"));
                                 if !is_status_bar {
                                     visible_windows.push(window);
                                 }
@@ -1899,7 +1899,7 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
                         }
 
                         let app_id = (*win_ptr).get_app_id_string();
-                        let is_status_bar = app_id.as_deref() == Some("cce-status");
+                        let is_status_bar = app_id.as_deref().map_or(false, |id| id.starts_with("cce-status"));
                         if is_status_bar {
                             continue;
                         }