gardesk/gar / 8db8991

Browse files

Add workspace switch/move keybinds (Alt+1-9, Alt+Shift+1-9)

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
8db8991ab70e0a0ee10a8cf5eb62e24c419c2819
Parents
89073c5
Tree
587a275

1 changed file

StatusFile+-
M gar/src/x11/events.rs 136 7
gar/src/x11/events.rsmodified
@@ -18,6 +18,16 @@ const XK_LEFT: u32 = 0xff51;
1818
 const XK_UP: u32 = 0xff52;
1919
 const XK_RIGHT: u32 = 0xff53;
2020
 const XK_DOWN: u32 = 0xff54;
21
+const XK_1: u32 = 0x31;
22
+const XK_2: u32 = 0x32;
23
+const XK_3: u32 = 0x33;
24
+const XK_4: u32 = 0x34;
25
+const XK_5: u32 = 0x35;
26
+const XK_6: u32 = 0x36;
27
+const XK_7: u32 = 0x37;
28
+const XK_8: u32 = 0x38;
29
+const XK_9: u32 = 0x39;
30
+const XK_0: u32 = 0x30;
2131
 
2232
 /// Keybind action types
2333
 #[derive(Debug, Clone)]
@@ -28,6 +38,8 @@ enum Action {
2838
     Swap(Direction),
2939
     Resize(Direction),
3040
     Equalize,
41
+    SwitchWorkspace(usize),
42
+    MoveToWorkspace(usize),
3143
 }
3244
 
3345
 struct Keybind {
@@ -122,6 +134,28 @@ impl WindowManager {
122134
                 keysym: XK_DOWN,
123135
                 action: Action::Resize(Direction::Down),
124136
             },
137
+            // Alt+1-9,0: switch workspace
138
+            Keybind { modifiers: ModMask::M1, keysym: XK_1, action: Action::SwitchWorkspace(0) },
139
+            Keybind { modifiers: ModMask::M1, keysym: XK_2, action: Action::SwitchWorkspace(1) },
140
+            Keybind { modifiers: ModMask::M1, keysym: XK_3, action: Action::SwitchWorkspace(2) },
141
+            Keybind { modifiers: ModMask::M1, keysym: XK_4, action: Action::SwitchWorkspace(3) },
142
+            Keybind { modifiers: ModMask::M1, keysym: XK_5, action: Action::SwitchWorkspace(4) },
143
+            Keybind { modifiers: ModMask::M1, keysym: XK_6, action: Action::SwitchWorkspace(5) },
144
+            Keybind { modifiers: ModMask::M1, keysym: XK_7, action: Action::SwitchWorkspace(6) },
145
+            Keybind { modifiers: ModMask::M1, keysym: XK_8, action: Action::SwitchWorkspace(7) },
146
+            Keybind { modifiers: ModMask::M1, keysym: XK_9, action: Action::SwitchWorkspace(8) },
147
+            Keybind { modifiers: ModMask::M1, keysym: XK_0, action: Action::SwitchWorkspace(9) },
148
+            // Alt+Shift+1-9,0: move window to workspace
149
+            Keybind { modifiers: ModMask::M1 | ModMask::SHIFT, keysym: XK_1, action: Action::MoveToWorkspace(0) },
150
+            Keybind { modifiers: ModMask::M1 | ModMask::SHIFT, keysym: XK_2, action: Action::MoveToWorkspace(1) },
151
+            Keybind { modifiers: ModMask::M1 | ModMask::SHIFT, keysym: XK_3, action: Action::MoveToWorkspace(2) },
152
+            Keybind { modifiers: ModMask::M1 | ModMask::SHIFT, keysym: XK_4, action: Action::MoveToWorkspace(3) },
153
+            Keybind { modifiers: ModMask::M1 | ModMask::SHIFT, keysym: XK_5, action: Action::MoveToWorkspace(4) },
154
+            Keybind { modifiers: ModMask::M1 | ModMask::SHIFT, keysym: XK_6, action: Action::MoveToWorkspace(5) },
155
+            Keybind { modifiers: ModMask::M1 | ModMask::SHIFT, keysym: XK_7, action: Action::MoveToWorkspace(6) },
156
+            Keybind { modifiers: ModMask::M1 | ModMask::SHIFT, keysym: XK_8, action: Action::MoveToWorkspace(7) },
157
+            Keybind { modifiers: ModMask::M1 | ModMask::SHIFT, keysym: XK_9, action: Action::MoveToWorkspace(8) },
158
+            Keybind { modifiers: ModMask::M1 | ModMask::SHIFT, keysym: XK_0, action: Action::MoveToWorkspace(9) },
125159
         ]
126160
     }
127161
 
@@ -218,15 +252,25 @@ impl WindowManager {
218252
     fn handle_unmap_notify(&mut self, event: UnmapNotifyEvent) -> Result<()> {
219253
         tracing::debug!("UnmapNotify for window {}", event.window);
220254
 
221
-        // Remove from management
222
-        self.unmanage_window(event.window);
255
+        // Only unmanage if window is on current workspace
256
+        // (windows on other workspaces are unmapped due to workspace switching)
257
+        let on_current = self
258
+            .windows
259
+            .get(&event.window)
260
+            .map(|w| w.workspace == self.focused_workspace)
261
+            .unwrap_or(false);
223262
 
224
-        // Re-apply layout
225
-        self.apply_layout()?;
263
+        if on_current {
264
+            // Remove from management
265
+            self.unmanage_window(event.window);
226266
 
227
-        // Update focus
228
-        if let Some(window) = self.focused_window {
229
-            self.set_focus(window)?;
267
+            // Re-apply layout
268
+            self.apply_layout()?;
269
+
270
+            // Update focus
271
+            if let Some(window) = self.focused_window {
272
+                self.set_focus(window)?;
273
+            }
230274
         }
231275
 
232276
         Ok(())
@@ -325,6 +369,12 @@ impl WindowManager {
325369
             Action::Equalize => {
326370
                 self.equalize()?;
327371
             }
372
+            Action::SwitchWorkspace(idx) => {
373
+                self.switch_workspace(idx)?;
374
+            }
375
+            Action::MoveToWorkspace(idx) => {
376
+                self.move_to_workspace(idx)?;
377
+            }
328378
         }
329379
         Ok(())
330380
     }
@@ -429,6 +479,85 @@ impl WindowManager {
429479
         Ok(())
430480
     }
431481
 
482
+    fn switch_workspace(&mut self, idx: usize) -> Result<()> {
483
+        if idx >= self.workspaces.len() || idx == self.focused_workspace {
484
+            return Ok(());
485
+        }
486
+
487
+        tracing::info!("Switching to workspace {}", idx + 1);
488
+
489
+        // Hide windows on current workspace
490
+        for window in self.current_workspace().tree.windows() {
491
+            self.conn.unmap_window(window)?;
492
+        }
493
+
494
+        // Switch workspace
495
+        self.focused_workspace = idx;
496
+
497
+        // Show windows on new workspace
498
+        for window in self.current_workspace().tree.windows() {
499
+            self.conn.map_window(window)?;
500
+        }
501
+
502
+        // Apply layout and update focus
503
+        self.apply_layout()?;
504
+
505
+        // Focus the workspace's focused window or first window
506
+        if let Some(window) = self.current_workspace().focused.or_else(|| self.current_workspace().tree.first_window()) {
507
+            self.set_focus(window)?;
508
+            self.conn.ungrab_button(window)?;
509
+        } else {
510
+            self.focused_window = None;
511
+        }
512
+
513
+        self.conn.flush()?;
514
+        Ok(())
515
+    }
516
+
517
+    fn move_to_workspace(&mut self, idx: usize) -> Result<()> {
518
+        if idx >= self.workspaces.len() || idx == self.focused_workspace {
519
+            return Ok(());
520
+        }
521
+
522
+        let Some(window) = self.focused_window else {
523
+            return Ok(());
524
+        };
525
+
526
+        tracing::info!("Moving window {} to workspace {}", window, idx + 1);
527
+
528
+        // Remove from current workspace tree
529
+        self.current_workspace_mut().tree.remove(window);
530
+
531
+        // Update focus on current workspace
532
+        self.focused_window = self.current_workspace().tree.first_window();
533
+        self.current_workspace_mut().focused = self.focused_window;
534
+
535
+        // Update window's workspace tracking
536
+        if let Some(win) = self.windows.get_mut(&window) {
537
+            win.workspace = idx;
538
+        }
539
+
540
+        // Hide the window (it's moving to another workspace)
541
+        self.conn.unmap_window(window)?;
542
+
543
+        // Insert into target workspace
544
+        let target_focused = self.workspaces[idx].focused;
545
+        let screen = self.screen_rect();
546
+        self.workspaces[idx].tree.insert_with_rect(window, target_focused, screen);
547
+
548
+        // Re-apply layout on current workspace
549
+        self.apply_layout()?;
550
+
551
+        // Update focus
552
+        if let Some(new_focus) = self.focused_window {
553
+            self.set_focus(new_focus)?;
554
+            self.conn.ungrab_button(new_focus)?;
555
+        }
556
+
557
+        self.conn.flush()?;
558
+        Ok(())
559
+    }
560
+
432561
     pub fn run(&mut self) -> Result<()> {
433562
         tracing::info!("Starting event loop");
434563