Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
Implement infinite pannable and zoomable coordinate desktop
src/server/config.rs | 93 +++++-
src/server/cursor.rs | 6 +
src/server/scene.rs | 4 +-
src/server/seat.rs | 94 ++++--
src/server/status_server.rs | 44 +--
src/server/window.rs | 6 +-
src/server/window_manager.rs | 712 +++++++++++++++++--------------------------
src/server/xdg_toplevel.rs | 4 +
8 files changed, 465 insertions(+), 498 deletions(-)
diff --git a/src/server/config.rs b/src/server/config.rs
index 00f79d8..2a5525a 100644
--- a/src/server/config.rs
+++ b/src/server/config.rs
@@ -127,6 +127,13 @@ pub enum Action {
Minimize,
SidePanelLeft,
SidePanelRight,
+ ZoomIn,
+ ZoomOut,
+ ZoomReset,
+ PanLeft,
+ PanRight,
+ PanUp,
+ PanDown,
}
#[derive(Debug, Deserialize, Clone)]
@@ -549,6 +556,20 @@ pub fn parse_action(s: &str) -> Action {
Action::SidePanelLeft
} else if s == "side-panel-right" {
Action::SidePanelRight
+ } else if s == "zoom-in" {
+ Action::ZoomIn
+ } else if s == "zoom-out" {
+ Action::ZoomOut
+ } else if s == "zoom-reset" {
+ Action::ZoomReset
+ } else if s == "pan-left" {
+ Action::PanLeft
+ } else if s == "pan-right" {
+ Action::PanRight
+ } else if s == "pan-up" {
+ Action::PanUp
+ } else if s == "pan-down" {
+ Action::PanDown
} else {
Action::None
}
@@ -738,6 +759,70 @@ pub fn parse_config(path: &str, state: &mut crate::window_manager::WindowManager
});
}
+ let super_ctrl_mod = parse_modifiers("super+ctrl");
+ let up_sym = parse_keysym("Up");
+ let down_sym = parse_keysym("Down");
+ let equal_sym = parse_keysym("equal");
+ let minus_sym = parse_keysym("minus");
+ let zero_sym = parse_keysym("0");
+
+ if !state.keybinds.iter().any(|b| b.mods == super_ctrl_mod && b.keysym == up_sym) {
+ state.keybinds.push(Keybind {
+ mods: super_ctrl_mod,
+ keysym: up_sym,
+ action: Action::PanUp,
+ command: None,
+ });
+ }
+ if !state.keybinds.iter().any(|b| b.mods == super_ctrl_mod && b.keysym == down_sym) {
+ state.keybinds.push(Keybind {
+ mods: super_ctrl_mod,
+ keysym: down_sym,
+ action: Action::PanDown,
+ command: None,
+ });
+ }
+ if !state.keybinds.iter().any(|b| b.mods == super_ctrl_mod && b.keysym == left_sym) {
+ state.keybinds.push(Keybind {
+ mods: super_ctrl_mod,
+ keysym: left_sym,
+ action: Action::PanLeft,
+ command: None,
+ });
+ }
+ if !state.keybinds.iter().any(|b| b.mods == super_ctrl_mod && b.keysym == right_sym) {
+ state.keybinds.push(Keybind {
+ mods: super_ctrl_mod,
+ keysym: right_sym,
+ action: Action::PanRight,
+ command: None,
+ });
+ }
+ if !state.keybinds.iter().any(|b| b.mods == super_mod && b.keysym == equal_sym) {
+ state.keybinds.push(Keybind {
+ mods: super_mod,
+ keysym: equal_sym,
+ action: Action::ZoomIn,
+ command: None,
+ });
+ }
+ if !state.keybinds.iter().any(|b| b.mods == super_mod && b.keysym == minus_sym) {
+ state.keybinds.push(Keybind {
+ mods: super_mod,
+ keysym: minus_sym,
+ action: Action::ZoomOut,
+ command: None,
+ });
+ }
+ if !state.keybinds.iter().any(|b| b.mods == super_mod && b.keysym == zero_sym) {
+ state.keybinds.push(Keybind {
+ mods: super_mod,
+ keysym: zero_sym,
+ action: Action::ZoomReset,
+ command: None,
+ });
+ }
+
let super_shift_mod = parse_modifiers("super+shift");
let r_sym = parse_keysym("r");
if !state.keybinds.iter().any(|b| b.mods == super_shift_mod && b.keysym == r_sym) {
@@ -793,12 +878,8 @@ pub fn parse_config(path: &str, state: &mut crate::window_manager::WindowManager
});
}
- for tag_layout in config.tag_layout {
- let idx = tag_layout.tag as usize - 1;
- if idx < 4 {
- state.tag_layouts[idx] = parse_tiling_mode(&tag_layout.mode);
- state.has_tag_layout[idx] = true;
- }
+ for _tag_layout in config.tag_layout {
+ // Tag layouts are ignored in the pannable coordinate system.
}
state.startup.clear();
diff --git a/src/server/cursor.rs b/src/server/cursor.rs
index 2c013fc..2d3181e 100644
--- a/src/server/cursor.rs
+++ b/src/server/cursor.rs
@@ -673,6 +673,8 @@ unsafe extern "C" fn handle_button(listener: *mut ffi::wl_listener, data: *mut s
start_win_y: (*target_win).box_geom.y,
start_win_w: (*target_win).box_geom.width as u32,
start_win_h: (*target_win).box_geom.height as u32,
+ start_win_virtual_x: (*target_win).virtual_x,
+ start_win_virtual_y: (*target_win).virtual_y,
});
cursor.op_start_pointer();
cursor.pressed.insert((*event).button, None);
@@ -730,6 +732,8 @@ unsafe extern "C" fn handle_button(listener: *mut ffi::wl_listener, data: *mut s
start_win_y: (*border_target_win).box_geom.y,
start_win_w: (*border_target_win).box_geom.width as u32,
start_win_h: (*border_target_win).box_geom.height as u32,
+ start_win_virtual_x: (*border_target_win).virtual_x,
+ start_win_virtual_y: (*border_target_win).virtual_y,
});
cursor.op_start_pointer();
cursor.pressed.insert((*event).button, None);
@@ -762,6 +766,8 @@ unsafe extern "C" fn handle_button(listener: *mut ffi::wl_listener, data: *mut s
start_win_y: (*border_target_win).box_geom.y,
start_win_w: (*border_target_win).box_geom.width as u32,
start_win_h: (*border_target_win).box_geom.height as u32,
+ start_win_virtual_x: (*border_target_win).virtual_x,
+ start_win_virtual_y: (*border_target_win).virtual_y,
});
cursor.op_start_pointer();
cursor.pressed.insert((*event).button, None);
diff --git a/src/server/scene.rs b/src/server/scene.rs
index c9c9b8f..1f7872d 100644
--- a/src/server/scene.rs
+++ b/src/server/scene.rs
@@ -139,8 +139,8 @@ impl Scene {
if let SceneNodeDataVal::Window(window) = scene_node_data.data {
if (*window).rendering_requested.circular {
// Check if outside the circle
- let w = (*window).box_geom.width as f64;
- let h = (*window).box_geom.height as f64;
+ let w = (*window).box_geom.width as f64 * (*window).scale;
+ let h = (*window).box_geom.height as f64 * (*window).scale;
let cx = (*window).box_geom.x as f64 + w / 2.0;
let cy = (*window).box_geom.y as f64 + h / 2.0;
let r = w.min(h) / 2.0;
diff --git a/src/server/seat.rs b/src/server/seat.rs
index a0b37b2..9e8e3a1 100644
--- a/src/server/seat.rs
+++ b/src/server/seat.rs
@@ -1,5 +1,5 @@
use crate::ffi;
-use crate::server::{Server, WlListener, wl_listener_remove, wl_signal_add};
+use crate::server::{Server, WlList, WlListener, wl_listener_remove, wl_signal_add};
use crate::cursor::Cursor;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -27,6 +27,8 @@ pub struct SeatOp {
pub start_win_y: i32,
pub start_win_w: u32,
pub start_win_h: u32,
+ pub start_win_virtual_x: f64,
+ pub start_win_virtual_y: f64,
}
#[derive(Clone, Copy, PartialEq)]
@@ -823,39 +825,95 @@ impl Seat {
match op.op_type {
PointerOpType::Move => {
- (*win).rendering_requested.x = op.start_win_x + dx;
- (*win).rendering_requested.y = op.start_win_y + dy;
- (*win).box_geom.x = op.start_win_x + dx;
- (*win).box_geom.y = op.start_win_y + dy;
+ let scale = (*(*self.server).wm.server).wm.desk_zoom;
+ let pan_x = (*(*self.server).wm.server).wm.desk_pan_x;
+ let pan_y = (*(*self.server).wm.server).wm.desk_pan_y;
+ let virtual_dx = dx as f64 / scale;
+ let virtual_dy = dy as f64 / scale;
+
+ let vx = op.start_win_virtual_x + virtual_dx;
+ let vy = op.start_win_virtual_y + virtual_dy;
+ (*win).virtual_x = vx;
+ (*win).virtual_y = vy;
+
+ let mut out_x = 0;
+ let mut out_y = 0;
+ let outputs_list = &mut (*(*self.server).wm.server).om.outputs as *mut ffi::wl_list as *mut WlList;
+ let mut curr_out = (*outputs_list).next;
+ while curr_out != outputs_list {
+ let output = crate::container_of!(curr_out, crate::output::Output, link);
+ if (*output).sent.state == crate::output::OutputStateValue::Enabled {
+ let wlr_box = (*output).sent.box_layout();
+ out_x = wlr_box.x;
+ out_y = wlr_box.y;
+ break;
+ }
+ curr_out = (*curr_out).next;
+ }
+
+ let final_x = out_x + ((vx - pan_x) * scale) as i32;
+ let final_y = out_y + ((vy - pan_y) * scale) as i32;
+ (*win).rendering_requested.x = final_x;
+ (*win).rendering_requested.y = final_y;
+ (*win).box_geom.x = final_x;
+ (*win).box_geom.y = final_y;
}
PointerOpType::Resize { edges } => {
+ let scale = (*(*self.server).wm.server).wm.desk_zoom;
+ let pan_x = (*(*self.server).wm.server).wm.desk_pan_x;
+ let pan_y = (*(*self.server).wm.server).wm.desk_pan_y;
+ let virtual_dx = dx as f64 / scale;
+ let virtual_dy = dy as f64 / scale;
+
let mut new_w = op.start_win_w;
let mut new_h = op.start_win_h;
- let mut new_x = op.start_win_x;
- let mut new_y = op.start_win_y;
+
+ let mut vx = op.start_win_virtual_x;
+ let mut vy = op.start_win_virtual_y;
if edges.left {
- let w = std::cmp::max(50, op.start_win_w as i32 - dx) as u32;
+ let w = std::cmp::max(50, (op.start_win_w as f64 - virtual_dx) as i32) as u32;
let dw = w as i32 - op.start_win_w as i32;
new_w = w;
- new_x = op.start_win_x - dw;
+ vx = op.start_win_virtual_x - dw as f64;
} else if edges.right {
- new_w = std::cmp::max(50, op.start_win_w as i32 + dx) as u32;
+ new_w = std::cmp::max(50, (op.start_win_w as f64 + virtual_dx) as i32) as u32;
}
if edges.top {
- let h = std::cmp::max(50, op.start_win_h as i32 - dy) as u32;
+ let h = std::cmp::max(50, (op.start_win_h as f64 - virtual_dy) as i32) as u32;
let dh = h as i32 - op.start_win_h as i32;
new_h = h;
- new_y = op.start_win_y - dh;
+ vy = op.start_win_virtual_y - dh as f64;
} else if edges.bottom {
- new_h = std::cmp::max(50, op.start_win_h as i32 + dy) as u32;
+ new_h = std::cmp::max(50, (op.start_win_h as f64 + virtual_dy) as i32) as u32;
}
- (*win).rendering_requested.x = new_x;
- (*win).rendering_requested.y = new_y;
- (*win).box_geom.x = new_x;
- (*win).box_geom.y = new_y;
+ (*win).virtual_x = vx;
+ (*win).virtual_y = vy;
+
+ let mut out_x = 0;
+ let mut out_y = 0;
+ let outputs_list = &mut (*(*self.server).wm.server).om.outputs as *mut ffi::wl_list as *mut WlList;
+ let mut curr_out = (*outputs_list).next;
+ while curr_out != outputs_list {
+ let output = crate::container_of!(curr_out, crate::output::Output, link);
+ if (*output).sent.state == crate::output::OutputStateValue::Enabled {
+ let wlr_box = (*output).sent.box_layout();
+ out_x = wlr_box.x;
+ out_y = wlr_box.y;
+ break;
+ }
+ curr_out = (*curr_out).next;
+ }
+
+ let final_x = out_x + ((vx - pan_x) * scale) as i32;
+ let final_y = out_y + ((vy - pan_y) * scale) as i32;
+
+ (*win).rendering_requested.x = final_x;
+ (*win).rendering_requested.y = final_y;
+ (*win).box_geom.x = final_x;
+ (*win).box_geom.y = final_y;
(*win).wm_requested.resizing = true;
(*win).wm_requested.dimensions = Some(crate::window::Dimensions {
@@ -1115,6 +1173,8 @@ unsafe extern "C" fn seat_op_start_pointer(
start_win_y: 0,
start_win_w: 0,
start_win_h: 0,
+ start_win_virtual_x: 0.0,
+ start_win_virtual_y: 0.0,
});
(*seat).cursor.op_start_pointer();
}
diff --git a/src/server/status_server.rs b/src/server/status_server.rs
index 20ba548..a554836 100644
--- a/src/server/status_server.rs
+++ b/src/server/status_server.rs
@@ -233,25 +233,15 @@ fn format_for_subscription(sub: Subscription, update: &StatusUpdate) -> String {
pub unsafe fn build_status_update(wm: &crate::window_manager::WindowManager) -> StatusUpdate {
let focused_window = wm.focused_window();
- let focused_tags = if !focused_window.is_null() {
- (*focused_window).tags
- } else {
- 0
- };
- let tags_json = render_tags_json(
- wm.active_tags,
- focused_tags,
- 4,
- &wm.layout.status_normal_color,
- );
+ let text = format!("Zoom: {:.2} | Pan: ({:.0}, {:.0})", wm.desk_zoom, wm.desk_pan_x, wm.desk_pan_y);
+ let escaped = text.replace('\\', "\\\\").replace('"', "\\\"");
+ let tags_json = format!("{{\"text\": \"{}\", \"tooltip\": \"Camera State\"}}", escaped);
let layout_text = if wm.expose_active {
"Expose".to_string()
- } else if !focused_window.is_null() {
- (*focused_window).tiling_mode.as_str().to_string()
} else {
- wm.global_layout.as_str().to_string()
+ "Pannable".to_string()
};
let title_text = if !focused_window.is_null() {
@@ -281,29 +271,3 @@ pub unsafe fn build_status_update(wm: &crate::window_manager::WindowManager) ->
title_text,
}
}
-
-fn render_tags_json(active: u32, focused: u32, num_tags: u32, normal_color: &str) -> String {
- let mut text = String::new();
- for i in 0..num_tags {
- let bit = 1u32 << i;
- let label = i + 1;
-
- let is_active = (active & bit) != 0;
- let is_focused = (focused & bit) != 0;
-
- let color = if is_focused && is_active {
- normal_color
- } else if is_focused {
- "#666666"
- } else if is_active {
- "#888888"
- } else {
- "#444444"
- };
-
- text.push_str(&format!("<span color='{}'>{}</span>", color, label));
- }
-
- let escaped = text.replace('\\', "\\\\").replace('"', "\\\"");
- format!("{{\"text\": \"{}\", \"tooltip\": \"Tags\"}}", escaped)
-}
diff --git a/src/server/window.rs b/src/server/window.rs
index 1e1552a..619cfb3 100644
--- a/src/server/window.rs
+++ b/src/server/window.rs
@@ -221,6 +221,8 @@ pub struct Window {
pub circular: bool,
pub blur: bool,
pub scale: f64,
+ pub virtual_x: f64,
+ pub virtual_y: f64,
pub commit: ffi::wl_listener,
pub wm_scheduled: WmScheduledState,
@@ -360,7 +362,7 @@ impl Window {
popup_tree,
capture_scene,
capture_source: std::ptr::null_mut(),
- tags: (*server).wm.active_tags,
+ tags: 1,
tiling_mode: crate::tiling::TilingMode::Floating,
mode_locked: false,
is_new: true,
@@ -375,6 +377,8 @@ impl Window {
circular: false,
blur: false,
scale: 1.0,
+ virtual_x: unsafe { (*server).wm.desk_pan_x + 100.0 },
+ virtual_y: unsafe { (*server).wm.desk_pan_y + 100.0 },
commit: std::mem::zeroed(),
wm_scheduled: WmScheduledState {
dimensions_hint: DimensionsHint { min_width: 0, min_height: 0, max_width: 0, max_height: 0 },
diff --git a/src/server/window_manager.rs b/src/server/window_manager.rs
index debb681..e43f79f 100644
--- a/src/server/window_manager.rs
+++ b/src/server/window_manager.rs
@@ -54,10 +54,10 @@ pub struct WindowManager {
pub rendering_requested: WindowManagerRenderingRequested,
pub dirty_idle: *mut ffi::wl_event_source,
pub timeout: *mut ffi::wl_event_source,
- pub active_tags: u32,
+ pub desk_pan_x: f64,
+ pub desk_pan_y: f64,
+ pub desk_zoom: f64,
pub global_layout: crate::tiling::TilingMode,
- pub tag_layouts: [crate::tiling::TilingMode; 4],
- pub has_tag_layout: [bool; 4],
pub layout: crate::config::Layout,
pub mode_rules: Vec<crate::config::ModeRule>,
pub keybinds: Vec<crate::config::Keybind>,
@@ -114,10 +114,10 @@ impl WindowManager {
order_hash: 0,
};
self.dirty_idle = std::ptr::null_mut();
- self.active_tags = 1;
+ self.desk_pan_x = 0.0;
+ self.desk_pan_y = 0.0;
+ self.desk_zoom = 1.0;
self.global_layout = crate::tiling::TilingMode::Cascade;
- self.tag_layouts = [crate::tiling::TilingMode::Cascade; 4];
- self.has_tag_layout = [false; 4];
self.layout = crate::config::Layout::default();
self.output_scale = 1.0;
self.mode_rules = Vec::new();
@@ -600,15 +600,7 @@ impl WindowManager {
return rule.mode;
}
- // Check tag layouts
- for tag_bit in 0..4 {
- let tag_mask = 1u32 << tag_bit;
- if ((*win).tags & tag_mask) != 0 && self.has_tag_layout[tag_bit] {
- return self.tag_layouts[tag_bit];
- }
- }
-
- self.global_layout
+ crate::tiling::TilingMode::Floating
}
pub unsafe fn get_active_resize_dimensions(&self, win_ptr: *mut Window) -> Option<(u32, u32)> {
@@ -653,14 +645,6 @@ impl WindowManager {
log::info!(" window #{}: title={:?}, app_id={:?}, state={:?}, closed={}", idx, title, aid, (*win_ptr).state, (*win_ptr).closed);
}
- if !self.expose_active {
- for &win_ptr in self.windows.iter() {
- if !win_ptr.is_null() {
- (*win_ptr).scale = 1.0;
- }
- }
- }
-
let outputs_list = &mut (*self.server).om.outputs as *mut ffi::wl_list as *mut WlList;
let mut curr_out = (*outputs_list).next;
@@ -691,18 +675,6 @@ impl WindowManager {
curr_seat = next_seat;
}
- let mut stack_order: Vec<*mut Window> = Vec::new();
- let render_list = &mut self.rendering_requested.list as *mut ffi::wl_list as *mut WlList;
- let mut curr = (*render_list).next;
- while curr != render_list {
- let next = (*curr).next;
- let node = crate::container_of!(curr, crate::wm_node::WmNode, link);
- if let crate::wm_node::WmNodeType::Window(window) = (*node).get() {
- stack_order.push(window);
- }
- curr = next;
- }
-
for &output in &active_outputs {
let wlr_box = (*output).sent.box_layout();
let phys_x = wlr_box.x;
@@ -723,9 +695,8 @@ impl WindowManager {
usable_h = non_ex.height;
}
- let mut tiled_windows: Vec<*mut Window> = Vec::new();
- let mut floating_windows: Vec<*mut Window> = Vec::new();
let mut side_panel_windows: Vec<*mut Window> = Vec::new();
+ let mut normal_windows: Vec<*mut Window> = Vec::new();
for &win_ptr in self.windows.iter() {
if (*win_ptr).closed {
@@ -734,7 +705,28 @@ impl WindowManager {
let app_id = (*win_ptr).get_app_id_string();
let is_status_bar = app_id.as_deref() == Some("cce-status-interface");
- let visible = (is_status_bar || ((*win_ptr).tags & self.active_tags) != 0)
+
+ if is_status_bar {
+ let bar_h = self.layout.bar_height as u32;
+ (*win_ptr).rendering_requested.x = wlr_box.x;
+ (*win_ptr).rendering_requested.y = wlr_box.y;
+ (*win_ptr).wm_requested.dimensions = Some(crate::window::Dimensions {
+ width: wlr_box.width as u32,
+ height: bar_h,
+ });
+ (*win_ptr).wm_requested.bounds = crate::window::Dimensions {
+ width: wlr_box.width as u32,
+ height: bar_h,
+ };
+ (*win_ptr).wm_requested.tiled = 0;
+ (*win_ptr).wm_requested.ssd = false;
+ (*win_ptr).scale = 1.0;
+ ffi::wlr_scene_node_set_enabled((*win_ptr).tree as *mut ffi::wlr_scene_node, true);
+ (*win_ptr).rendering_requested.hidden = false;
+ continue;
+ }
+
+ let visible = !(*win_ptr).minimized
&& !matches!((*win_ptr).state, crate::window::WindowState::Closing | crate::window::WindowState::Init);
if !visible {
ffi::wlr_scene_node_set_enabled((*win_ptr).tree as *mut ffi::wlr_scene_node, false);
@@ -746,11 +738,8 @@ impl WindowManager {
(*win_ptr).rendering_requested.hidden = false;
let mode = self.get_mode_for_window(win_ptr);
- if !self.expose_active {
- (*win_ptr).tiling_mode = mode;
- }
+ (*win_ptr).tiling_mode = mode;
- // Apply ModeRule SSD configuration if defined and not locked
if !(*win_ptr).mode_locked {
if let Some(rule) = self.get_rule_for_window(win_ptr) {
if let Some(rule_ssd) = rule.ssd {
@@ -759,38 +748,17 @@ impl WindowManager {
}
}
- let app_id = (*win_ptr).get_app_id_string();
-
- if app_id.as_deref() == Some("cce-status-interface") {
- let wlr_box = (*output).sent.box_layout();
- let bar_h = self.layout.bar_height as u32;
- (*win_ptr).rendering_requested.x = wlr_box.x;
- (*win_ptr).rendering_requested.y = wlr_box.y;
- (*win_ptr).wm_requested.dimensions = Some(crate::window::Dimensions {
- width: wlr_box.width as u32,
- height: bar_h,
- });
- (*win_ptr).wm_requested.bounds = crate::window::Dimensions {
- width: wlr_box.width as u32,
- height: bar_h,
- };
- (*win_ptr).wm_requested.tiled = 0;
- (*win_ptr).wm_requested.ssd = false;
- continue;
- }
-
- if mode == crate::tiling::TilingMode::Floating || mode == crate::tiling::TilingMode::Popup || mode == crate::tiling::TilingMode::Status {
- floating_windows.push(win_ptr);
- } else if mode == crate::tiling::TilingMode::SidePanel {
+ if mode == crate::tiling::TilingMode::SidePanel {
side_panel_windows.push(win_ptr);
} else {
- tiled_windows.push(win_ptr);
+ normal_windows.push(win_ptr);
}
}
+ // Arrange side panel windows (fixed on screen, scale = 1.0)
let side_panel_win = side_panel_windows.first().copied();
let mut side_panel_w = 0;
- let mut shift_x = 0;
+ let bw = self.layout.border_width;
if let Some(sp_win) = side_panel_win {
let hint_min_w = (*sp_win).wm_scheduled.dimensions_hint.min_width as i32;
side_panel_w = if hint_min_w > 32 {
@@ -798,216 +766,6 @@ impl WindowManager {
} else {
self.layout.side_panel_width
};
- if self.layout.side_panel_behavior != "above" {
- shift_x = side_panel_w + self.layout.side_panel_border_gap;
- }
- }
-
- let tiled_usable_w = (usable_w - shift_x).max(1);
- let tiled_usable_x = if self.layout.side_panel_position == "right" || self.layout.side_panel_behavior == "above" {
- usable_x
- } else {
- usable_x + shift_x
- };
-
- let n_tiled = tiled_windows.len() as i32;
- let current_layout = if n_tiled > 0 {
- self.get_mode_for_window(tiled_windows[0])
- } else {
- self.global_layout
- };
-
- log::info!("arrange_views: n_tiled = {}, current_layout = {:?}, expose_active = {}", n_tiled, current_layout, self.expose_active);
-
- if current_layout == crate::tiling::TilingMode::Cascade {
- tiled_windows.sort_by_key(|&w| stack_order.iter().position(|&x| x == w).unwrap_or(usize::MAX));
- }
-
- let gap = self.layout.gap;
- let gap_top = self.layout.gap_top;
- let gap_left = self.layout.gap_left;
- let gap_right = self.layout.gap_right;
- let gap_bottom = self.layout.gap_bottom;
- let bw = match current_layout {
- crate::tiling::TilingMode::Cascade => self.layout.cascade_border_width,
- crate::tiling::TilingMode::Grid | crate::tiling::TilingMode::Expose => self.layout.grid_border_width,
- crate::tiling::TilingMode::Fullscreen => self.layout.fullscreen_border_width,
- _ => self.layout.border_width,
- };
- let cascade_offset = self.layout.cascade_offset;
- let bar_height = self.layout.bar_height;
-
- for (idx, &win_ptr) in tiled_windows.iter().enumerate() {
- if !self.expose_active {
- (*win_ptr).tiling_mode = current_layout;
- }
- let win_bw = if !(*win_ptr).wm_requested.ssd {
- 0
- } else {
- bw
- };
- let win_dec_h = if !(*win_ptr).wm_requested.ssd {
- 0
- } else {
- std::cmp::max(win_bw, 16)
- };
-
- let (x, y, w, h) = match current_layout {
- crate::tiling::TilingMode::Cascade => {
- crate::tiling::tile_cascade(
- tiled_usable_w, usable_h, gap, gap_top, gap_left, gap_right, gap_bottom,
- win_bw, win_dec_h, cascade_offset, bar_height, n_tiled, idx as i32
- )
- }
- crate::tiling::TilingMode::Grid => {
- crate::tiling::tile_grid(
- tiled_usable_w, usable_h, gap, gap_top, gap_left, gap_right, gap_bottom,
- win_bw, win_dec_h, bar_height, n_tiled, idx as i32
- )
- }
- crate::tiling::TilingMode::Expose => {
- crate::tiling::tile_expose(
- tiled_usable_w, usable_h, gap, gap_top, gap_left, gap_right, gap_bottom,
- win_bw, win_dec_h, bar_height, n_tiled, idx as i32
- )
- }
- crate::tiling::TilingMode::Fullscreen => {
- crate::tiling::tile_fullscreen(
- phys_w, phys_h, 0, 0, 0, 0,
- 0, 0
- )
- }
- _ => {
- ((*win_ptr).box_geom.x, (*win_ptr).box_geom.y, (*win_ptr).box_geom.width as i32, (*win_ptr).box_geom.height as i32)
- }
- };
-
- let mut final_x = if current_layout == crate::tiling::TilingMode::Fullscreen {
- phys_x + x
- } else {
- tiled_usable_x + x
- };
- let mut final_y = if current_layout == crate::tiling::TilingMode::Fullscreen {
- phys_y + y
- } else {
- usable_y + y
- };
-
- if self.expose_active {
- let orig_w = (*win_ptr).box_geom.width;
- let orig_h = (*win_ptr).box_geom.height;
- let scale = if orig_w > 0 && orig_h > 0 {
- let scale_x = w as f64 / orig_w as f64;
- let scale_y = h as f64 / orig_h as f64;
- scale_x.min(scale_y).min(1.0)
- } else {
- 1.0
- };
- (*win_ptr).scale = scale;
-
- let visual_w = orig_w as f64 * scale;
- let visual_h = orig_h as f64 * scale;
- let offset_x = (w as f64 - visual_w) / 2.0;
- let offset_y = (h as f64 - visual_h) / 2.0;
-
- final_x += offset_x as i32;
- final_y += offset_y as i32;
- } else {
- (*win_ptr).scale = 1.0;
- }
-
- log::info!("arrange_views: tiled window index {}, title = {:?}, app_id = {:?}, geom_box = (x={}, y={}, w={}, h={}), target_box = (x={}, y={}, w={}, h={}), scale = {}",
- idx,
- (*win_ptr).get_title_string(),
- (*win_ptr).get_app_id_string(),
- (*win_ptr).box_geom.x, (*win_ptr).box_geom.y, (*win_ptr).box_geom.width, (*win_ptr).box_geom.height,
- final_x, final_y, w, h, (*win_ptr).scale
- );
-
- (*win_ptr).rendering_requested.x = final_x;
- (*win_ptr).rendering_requested.y = final_y;
-
- if !self.expose_active {
- let mut target_w = w;
- let mut target_h = h;
- if !(*win_ptr).wm_requested.ssd {
- let (dec_w, dec_h) = (*win_ptr).get_decorations_size();
- let is_tiled = current_layout != crate::tiling::TilingMode::Floating &&
- current_layout != crate::tiling::TilingMode::Popup &&
- current_layout != crate::tiling::TilingMode::Status;
- if (*win_ptr).csd_buffer_size_bug {
- target_w = w + dec_w;
- target_h = h + dec_h;
- } else if !is_tiled {
- target_w = (w - dec_w).max(1);
- target_h = (h - dec_h).max(1);
- }
- }
- (*win_ptr).wm_requested.dimensions = Some(crate::window::Dimensions {
- width: target_w as u32,
- height: target_h as u32,
- });
- (*win_ptr).wm_requested.bounds = crate::window::Dimensions {
- width: target_w as u32,
- height: target_h as u32,
- };
- let mode = self.get_mode_for_window(win_ptr);
- let is_maximized_layout = mode == crate::tiling::TilingMode::Cascade || mode == crate::tiling::TilingMode::Grid;
- (*win_ptr).wm_requested.tiled = if is_maximized_layout { 0 } else { 1 | 2 | 4 | 8 };
- }
-
- let is_focused = win_ptr == focused_window;
- let (win_bw, r, g_val, b, a) = if self.expose_active {
- if self.window_is_valid(self.expose_hovered_window) && win_ptr == self.expose_hovered_window {
- (
- bw as u32,
- self.layout.border_r,
- self.layout.border_g,
- self.layout.border_b,
- self.layout.border_a,
- )
- } else {
- (0, 0, 0, 0, 0)
- }
- } else {
- let (r, g_val, b, a) = if is_focused {
- (
- self.layout.border_r,
- self.layout.border_g,
- self.layout.border_b,
- self.layout.border_a,
- )
- } else {
- let mut tiled_stack = tiled_windows.clone();
- tiled_stack.sort_by_key(|&w| stack_order.iter().position(|&x| x == w).unwrap_or(usize::MAX));
- let pos = tiled_stack.iter().position(|&w| w == win_ptr).unwrap_or(0);
- let depth = n_tiled - 1 - pos as i32;
- let mut factor = 1.0_f64;
- for _ in 0..depth {
- factor *= 0.70; // UNFOCUSED_DEPTH_FACTOR
- }
- let r = blend_channel(self.layout.background_r, self.layout.border_r, factor);
- let g = blend_channel(self.layout.background_g, self.layout.border_g, factor);
- let b = blend_channel(self.layout.background_b, self.layout.border_b, factor);
- let a = blend_channel(self.layout.background_a, self.layout.border_a, factor);
- (r, g, b, a)
- };
- (bw as u32, r, g_val, b, a)
- };
-
- (*win_ptr).rendering_requested.border = crate::window::Border {
- edges: crate::window::Edges { top: true, bottom: true, left: true, right: true },
- width: win_bw,
- r,
- g: g_val,
- b,
- a,
- };
-
- (*win_ptr).rendering_requested.blur = self.layout.window_blur;
- (*win_ptr).rendering_requested.opacity = if is_focused { 1.0f32 } else {
- if !self.layout.window_opacity { 1.0f32 } else { 0.85f32 }
- };
}
let g = self.layout.side_panel_border_gap;
@@ -1019,12 +777,14 @@ impl WindowManager {
} else {
usable_x + g + bw
};
- let sp_y = bar_height + usable_y + dec_h + g;
- let sp_h = (usable_h - bar_height - (dec_h + bw) - 2 * g).max(1);
+ let sp_y = usable_y + dec_h + g;
+ let sp_h = (usable_h - (dec_h + bw) - 2 * g).max(1);
let sp_w = (side_panel_w - bw * 2).max(1);
(*win_ptr).rendering_requested.x = sp_x;
(*win_ptr).rendering_requested.y = sp_y;
+ (*win_ptr).scale = 1.0;
+
let mut sp_target_w = sp_w;
let mut sp_target_h = sp_h;
if !(*win_ptr).wm_requested.ssd {
@@ -1062,32 +822,16 @@ impl WindowManager {
if !self.layout.window_opacity { 1.0f32 } else { 0.85f32 * opacity_factor }
};
} else {
- floating_windows.push(win_ptr);
+ normal_windows.push(win_ptr);
}
}
- let mut idx_floating = 0;
- for &win_ptr in &floating_windows {
+ // Arrange normal windows on the virtual surface
+ for &win_ptr in &normal_windows {
+ let mode = (*win_ptr).tiling_mode;
let is_focused = win_ptr == focused_window;
- let r = self.layout.border_r;
- let g_val = self.layout.border_g;
- let b = self.layout.border_b;
- let a = self.layout.border_a;
-
- (*win_ptr).rendering_requested.border = crate::window::Border {
- edges: crate::window::Edges { top: true, bottom: true, left: true, right: true },
- width: bw as u32,
- r,
- g: g_val,
- b,
- a,
- };
- (*win_ptr).rendering_requested.blur = self.layout.window_blur;
- (*win_ptr).rendering_requested.opacity = if is_focused { 1.0f32 } else {
- if !self.layout.window_opacity { 1.0f32 } else { 0.90f32 }
- };
- if (*win_ptr).tiling_mode == crate::tiling::TilingMode::Popup {
+ if mode == crate::tiling::TilingMode::Popup {
let hint_min_w = (*win_ptr).wm_scheduled.dimensions_hint.min_width as i32;
let hint_min_h = (*win_ptr).wm_scheduled.dimensions_hint.min_height as i32;
let fw = if (*win_ptr).box_geom.width > 0 {
@@ -1105,10 +849,11 @@ impl WindowManager {
100
};
let fx = usable_x + usable_w - fw - self.layout.gap_right;
- let fy = usable_y + bar_height + self.layout.gap_top;
+ let fy = usable_y + self.layout.gap_top;
(*win_ptr).rendering_requested.x = fx;
(*win_ptr).rendering_requested.y = fy;
+ (*win_ptr).scale = 1.0;
(*win_ptr).wm_requested.dimensions = Some(crate::window::Dimensions {
width: fw as u32,
height: fh as u32,
@@ -1117,54 +862,47 @@ impl WindowManager {
width: fw as u32,
height: fh as u32,
};
+ } else if mode == crate::tiling::TilingMode::Fullscreen {
+ (*win_ptr).rendering_requested.x = phys_x;
+ (*win_ptr).rendering_requested.y = phys_y;
+ (*win_ptr).scale = 1.0;
+ (*win_ptr).wm_requested.dimensions = Some(crate::window::Dimensions {
+ width: phys_w as u32,
+ height: phys_h as u32,
+ });
+ (*win_ptr).wm_requested.bounds = crate::window::Dimensions {
+ width: phys_w as u32,
+ height: phys_h as u32,
+ };
+ (*win_ptr).wm_requested.tiled = 1 | 2 | 4 | 8;
} else {
- let fbw = self.layout.floating_border_width;
- let mut fw = if let Some(resize_size) = self.get_active_resize_dimensions(win_ptr) {
+ // Regular pannable window on the virtual surface
+ let fw = if let Some(resize_size) = self.get_active_resize_dimensions(win_ptr) {
resize_size.0 as i32
} else if (*win_ptr).box_geom.width > 0 {
(*win_ptr).box_geom.width as i32
} else if (*win_ptr).wm_scheduled.dimensions_hint.min_width > 32 {
(*win_ptr).wm_scheduled.dimensions_hint.min_width as i32
} else {
- usable_w * 2 / 3
+ 800
};
- let mut fh = if let Some(resize_size) = self.get_active_resize_dimensions(win_ptr) {
+ let fh = if let Some(resize_size) = self.get_active_resize_dimensions(win_ptr) {
resize_size.1 as i32
} else if (*win_ptr).box_geom.height > 0 {
(*win_ptr).box_geom.height as i32
} else if (*win_ptr).wm_scheduled.dimensions_hint.min_height > 32 {
(*win_ptr).wm_scheduled.dimensions_hint.min_height as i32
} else {
- usable_h * 2 / 3
- };
-
- let max_w = usable_w - gap_left - gap_right;
- let max_h = usable_h - bar_height - gap_top - gap_bottom;
- fw = fw.clamp(1, max_w.max(1));
- fh = fh.clamp(1, max_h.max(1));
-
- let mut fx = if (*win_ptr).rendering_requested.x != 0 || (*win_ptr).rendering_requested.y != 0 {
- (*win_ptr).rendering_requested.x
- } else {
- usable_x + gap_left + fbw + cascade_offset * idx_floating
- };
- let mut fy = if (*win_ptr).rendering_requested.x != 0 || (*win_ptr).rendering_requested.y != 0 {
- (*win_ptr).rendering_requested.y
- } else {
- usable_y + gap_left + fbw + bar_height + gap_top + cascade_offset * idx_floating
+ 600
};
- let min_visible = 64;
- let min_x = usable_x + gap_left - fw + fw.min(min_visible);
- let max_x = usable_x + usable_w - gap_right - fw.min(min_visible);
- let min_y = usable_y + bar_height + gap_top - fh + fh.min(min_visible);
- let max_y = usable_y + usable_h - gap_bottom - fh.min(min_visible);
+ let final_x = phys_x + (((*win_ptr).virtual_x - self.desk_pan_x) * self.desk_zoom) as i32;
+ let final_y = phys_y + (((*win_ptr).virtual_y - self.desk_pan_y) * self.desk_zoom) as i32;
- fx = fx.clamp(min_x, max_x.max(min_x));
- fy = fy.clamp(min_y, max_y.max(min_y));
+ (*win_ptr).rendering_requested.x = final_x;
+ (*win_ptr).rendering_requested.y = final_y;
+ (*win_ptr).scale = self.desk_zoom;
- (*win_ptr).rendering_requested.x = fx;
- (*win_ptr).rendering_requested.y = fy;
(*win_ptr).wm_requested.dimensions = Some(crate::window::Dimensions {
width: fw as u32,
height: fh as u32,
@@ -1173,9 +911,26 @@ impl WindowManager {
width: fw as u32,
height: fh as u32,
};
-
- idx_floating += 1;
}
+
+ // Apply borders, opacity, and blur to normal windows
+ let r = self.layout.border_r;
+ let g_val = self.layout.border_g;
+ let b = self.layout.border_b;
+ let a = self.layout.border_a;
+
+ (*win_ptr).rendering_requested.border = crate::window::Border {
+ edges: crate::window::Edges { top: true, bottom: true, left: true, right: true },
+ width: bw as u32,
+ r,
+ g: g_val,
+ b,
+ a,
+ };
+ (*win_ptr).rendering_requested.blur = self.layout.window_blur;
+ (*win_ptr).rendering_requested.opacity = if is_focused { 1.0f32 } else {
+ if !self.layout.window_opacity { 1.0f32 } else { 0.90f32 }
+ };
}
}
// If the focused window is no longer visible on the active tags, refocus
@@ -1187,7 +942,7 @@ impl WindowManager {
let mut focused_visible = false;
match (*seat).focused {
crate::seat::Focus::Window(w) => {
- if !w.is_null() && !(*w).closed && !(*w).minimized && ((*w).tags & self.active_tags) != 0 {
+ if !w.is_null() && !(*w).closed && !(*w).minimized {
focused_visible = true;
}
}
@@ -1266,7 +1021,7 @@ impl WindowManager {
pub unsafe fn focus_next_visible_window(&mut self, seat: *mut crate::seat::Seat) {
let mut next_focus: *mut Window = std::ptr::null_mut();
for &w in self.windows.iter() {
- if !w.is_null() && !(*w).closed && !(*w).minimized && matches!((*w).state, crate::window::WindowState::Mapped) && ((*w).tags & self.active_tags) != 0 {
+ 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-interface");
if !is_status_bar {
@@ -1430,7 +1185,7 @@ impl WindowManager {
let next = (*curr).next;
let node = crate::container_of!(curr, crate::wm_node::WmNode, link);
if let crate::wm_node::WmNodeType::Window(window) = (*node).get() {
- if !window.is_null() && !(*window).closed && !(*window).minimized && ((*window).tags & self.active_tags) != 0 {
+ if !window.is_null() && !(*window).closed && !(*window).minimized {
let is_status_bar = (*window).get_app_id_string()
.map_or(false, |aid| aid == "cce-status-interface");
if !is_status_bar {
@@ -1518,45 +1273,11 @@ impl WindowManager {
}
}
}
- Action::LayoutNext => {
- let cycle = [
- crate::tiling::TilingMode::Cascade,
- crate::tiling::TilingMode::Grid,
- crate::tiling::TilingMode::Fullscreen,
- ];
- let active_tags = self.active_tags;
- let first_tag_bit = (0..4).find(|b| (active_tags & (1u32 << b)) != 0);
- let current = if let Some(bit) = first_tag_bit {
- if self.has_tag_layout[bit] {
- self.tag_layouts[bit]
- } else {
- self.global_layout
- }
- } else {
- self.global_layout
- };
-
- let next = cycle
- .iter()
- .position(|m| *m == current)
- .map(|i| cycle[(i + 1) % cycle.len()])
- .unwrap_or(crate::tiling::TilingMode::Cascade);
-
- for tag_bit in 0..4 {
- if (active_tags & (1u32 << tag_bit)) != 0 {
- self.tag_layouts[tag_bit] = next;
- self.has_tag_layout[tag_bit] = true;
- }
- }
- log::info!("layout-next: cycled layout to {:?}", next);
- self.dirty_windowing();
- }
+ Action::LayoutNext => {}
Action::ModeNext => {
let cycle = [
- crate::tiling::TilingMode::Cascade,
- crate::tiling::TilingMode::Grid,
- crate::tiling::TilingMode::Fullscreen,
crate::tiling::TilingMode::Floating,
+ crate::tiling::TilingMode::Fullscreen,
];
if let Some(seat) = self.first_seat() {
if let crate::seat::Focus::Window(fw) = (*seat).focused {
@@ -1565,7 +1286,7 @@ impl WindowManager {
.iter()
.position(|m| *m == current_mode)
.map(|i| cycle[(i + 1) % cycle.len()])
- .unwrap_or(crate::tiling::TilingMode::Cascade);
+ .unwrap_or(crate::tiling::TilingMode::Floating);
(*fw).tiling_mode = next;
(*fw).mode_locked = true;
self.dirty_windowing();
@@ -1574,10 +1295,8 @@ impl WindowManager {
}
Action::ModeNextShared => {
let cycle = [
- crate::tiling::TilingMode::Cascade,
- crate::tiling::TilingMode::Grid,
- crate::tiling::TilingMode::Fullscreen,
crate::tiling::TilingMode::Floating,
+ crate::tiling::TilingMode::Fullscreen,
];
if let Some(seat) = self.first_seat() {
if let crate::seat::Focus::Window(fw) = (*seat).focused {
@@ -1586,11 +1305,10 @@ impl WindowManager {
.iter()
.position(|m| *m == current_mode)
.map(|i| cycle[(i + 1) % cycle.len()])
- .unwrap_or(crate::tiling::TilingMode::Cascade);
+ .unwrap_or(crate::tiling::TilingMode::Floating);
- let active_tags = self.active_tags;
for &w in self.windows.iter() {
- if !w.is_null() && !(*w).closed && !matches!((*w).state, crate::window::WindowState::Closing | crate::window::WindowState::Init) && ((*w).tags & active_tags) != 0 && (*w).tiling_mode == current_mode {
+ if !w.is_null() && !(*w).closed && !matches!((*w).state, crate::window::WindowState::Closing | crate::window::WindowState::Init) && (*w).tiling_mode == current_mode {
(*w).tiling_mode = next;
(*w).mode_locked = true;
}
@@ -1599,49 +1317,93 @@ impl WindowManager {
}
}
}
- Action::View1 | Action::View2 | Action::View3 | Action::View4 => {
- let tag = match action {
- Action::View1 => 1,
- Action::View2 => 2,
- Action::View3 => 3,
- Action::View4 => 4,
- _ => 1,
- };
- self.active_tags = 1 << (tag - 1);
- self.dirty_windowing();
- }
- Action::Toggle1 | Action::Toggle2 | Action::Toggle3 | Action::Toggle4 => {
- let tag = match action {
- Action::Toggle1 => 1,
- Action::Toggle2 => 2,
- Action::Toggle3 => 3,
- Action::Toggle4 => 4,
- _ => 1,
+ Action::View1 | Action::View2 | Action::View3 | Action::View4 => {
+ let (target_x, target_y) = match action {
+ Action::View1 => (0.0, 0.0),
+ Action::View2 => (2000.0, 0.0),
+ Action::View3 => (0.0, 2000.0),
+ Action::View4 => (2000.0, 2000.0),
+ _ => (0.0, 0.0),
};
- self.active_tags ^= 1 << (tag - 1);
- if self.active_tags == 0 {
- self.active_tags = 1;
+
+ let (mut viewport_w, mut viewport_h) = (1920.0, 1080.0);
+ let outputs_list = &mut (*self.server).om.outputs as *mut ffi::wl_list as *mut WlList;
+ let mut curr_out = (*outputs_list).next;
+ while curr_out != outputs_list {
+ let output = crate::container_of!(curr_out, crate::output::Output, link);
+ if (*output).sent.state == crate::output::OutputStateValue::Enabled {
+ let wlr_box = (*output).sent.box_layout();
+ viewport_w = wlr_box.width as f64;
+ viewport_h = wlr_box.height as f64;
+ break;
+ }
+ curr_out = (*curr_out).next;
}
+
+ self.desk_pan_x = target_x - (viewport_w / 2.0) / self.desk_zoom;
+ self.desk_pan_y = target_y - (viewport_h / 2.0) / self.desk_zoom;
self.dirty_windowing();
}
+ Action::Toggle1 | Action::Toggle2 | Action::Toggle3 | Action::Toggle4 => {}
Action::SetTag1 | Action::SetTag2 | Action::SetTag3 | Action::SetTag4 => {
- let tag = match action {
- Action::SetTag1 => 1,
- Action::SetTag2 => 2,
- Action::SetTag3 => 3,
- Action::SetTag4 => 4,
- _ => 1,
+ let (target_x, target_y) = match action {
+ Action::SetTag1 => (0.0, 0.0),
+ Action::SetTag2 => (2000.0, 0.0),
+ Action::SetTag3 => (0.0, 2000.0),
+ Action::SetTag4 => (2000.0, 2000.0),
+ _ => (0.0, 0.0),
};
if let Some(seat) = self.first_seat() {
if let crate::seat::Focus::Window(fw) = (*seat).focused {
- (*fw).tags = 1 << (tag - 1);
- if ((*fw).tags & self.active_tags) == 0 {
- self.focus_next_visible_window(seat);
- }
+ let w = if (*fw).box_geom.width > 0 { (*fw).box_geom.width as f64 } else { 800.0 };
+ let h = if (*fw).box_geom.height > 0 { (*fw).box_geom.height as f64 } else { 600.0 };
+ (*fw).virtual_x = target_x - w / 2.0;
+ (*fw).virtual_y = target_y - h / 2.0;
self.dirty_windowing();
}
}
}
+ Action::ZoomIn | Action::ZoomOut | Action::ZoomReset => {
+ let (mut viewport_w, mut viewport_h) = (1920.0, 1080.0);
+ let outputs_list = &mut (*self.server).om.outputs as *mut ffi::wl_list as *mut WlList;
+ let mut curr_out = (*outputs_list).next;
+ while curr_out != outputs_list {
+ let output = crate::container_of!(curr_out, crate::output::Output, link);
+ if (*output).sent.state == crate::output::OutputStateValue::Enabled {
+ let wlr_box = (*output).sent.box_layout();
+ viewport_w = wlr_box.width as f64;
+ viewport_h = wlr_box.height as f64;
+ break;
+ }
+ curr_out = (*curr_out).next;
+ }
+
+ let cx = self.desk_pan_x + (viewport_w / 2.0) / self.desk_zoom;
+ let cy = self.desk_pan_y + (viewport_h / 2.0) / self.desk_zoom;
+
+ let new_zoom = match action {
+ Action::ZoomIn => (self.desk_zoom * 1.1).min(10.0),
+ Action::ZoomOut => (self.desk_zoom / 1.1).max(0.1),
+ Action::ZoomReset => 1.0,
+ _ => self.desk_zoom,
+ };
+
+ self.desk_pan_x = cx - (viewport_w / 2.0) / new_zoom;
+ self.desk_pan_y = cy - (viewport_h / 2.0) / new_zoom;
+ self.desk_zoom = new_zoom;
+ self.dirty_windowing();
+ }
+ Action::PanLeft | Action::PanRight | Action::PanUp | Action::PanDown => {
+ let step = 100.0 / self.desk_zoom;
+ match action {
+ Action::PanLeft => self.desk_pan_x -= step,
+ Action::PanRight => self.desk_pan_x += step,
+ Action::PanUp => self.desk_pan_y -= step,
+ Action::PanDown => self.desk_pan_y += step,
+ _ => {}
+ }
+ self.dirty_windowing();
+ }
Action::SidePanelLeft => {
self.layout.side_panel_position = "left".to_string();
self.dirty_windowing();
@@ -1692,7 +1454,14 @@ impl WindowManager {
if parts.len() < 2 { return "error: missing tag\n".to_string(); }
if let Ok(tag) = parts[1].parse::<i32>() {
if tag >= 1 && tag <= 4 {
- self.execute_action(&crate::config::Action::View1, Some(parts[1]));
+ let act = match tag {
+ 1 => crate::config::Action::View1,
+ 2 => crate::config::Action::View2,
+ 3 => crate::config::Action::View3,
+ 4 => crate::config::Action::View4,
+ _ => crate::config::Action::None,
+ };
+ self.execute_action(&act, None);
return "ok\n".to_string();
}
}
@@ -1732,6 +1501,101 @@ impl WindowManager {
}
"error: invalid tag\n".to_string()
}
+ "pan-by" => {
+ if parts.len() < 3 { return "error: missing dx or dy\n".to_string(); }
+ if let (Ok(dx), Ok(dy)) = (parts[1].parse::<f64>(), parts[2].parse::<f64>()) {
+ self.desk_pan_x += dx;
+ self.desk_pan_y += dy;
+ self.dirty_windowing();
+ return "ok\n".to_string();
+ }
+ "error: invalid dx or dy\n".to_string()
+ }
+ "pan-to" => {
+ if parts.len() < 3 { return "error: missing x or y\n".to_string(); }
+ if let (Ok(x), Ok(y)) = (parts[1].parse::<f64>(), parts[2].parse::<f64>()) {
+ self.desk_pan_x = x;
+ self.desk_pan_y = y;
+ self.dirty_windowing();
+ return "ok\n".to_string();
+ }
+ "error: invalid x or y\n".to_string()
+ }
+ "zoom-in" => {
+ self.execute_action(&crate::config::Action::ZoomIn, None);
+ "ok\n".to_string()
+ }
+ "zoom-out" => {
+ self.execute_action(&crate::config::Action::ZoomOut, None);
+ "ok\n".to_string()
+ }
+ "set-zoom" => {
+ if parts.len() < 2 { return "error: missing zoom factor\n".to_string(); }
+ if let Ok(factor) = parts[1].parse::<f64>() {
+ let new_zoom = factor.clamp(0.1, 10.0);
+ let (mut viewport_w, mut viewport_h) = (1920.0, 1080.0);
+ let outputs_list = &mut (*self.server).om.outputs as *mut ffi::wl_list as *mut WlList;
+ let mut curr_out = (*outputs_list).next;
+ while curr_out != outputs_list {
+ let output = crate::container_of!(curr_out, crate::output::Output, link);
+ if (*output).sent.state == crate::output::OutputStateValue::Enabled {
+ let wlr_box = (*output).sent.box_layout();
+ viewport_w = wlr_box.width as f64;
+ viewport_h = wlr_box.height as f64;
+ break;
+ }
+ curr_out = (*curr_out).next;
+ }
+ let cx = self.desk_pan_x + (viewport_w / 2.0) / self.desk_zoom;
+ let cy = self.desk_pan_y + (viewport_h / 2.0) / self.desk_zoom;
+ self.desk_pan_x = cx - (viewport_w / 2.0) / new_zoom;
+ self.desk_pan_y = cy - (viewport_h / 2.0) / new_zoom;
+ self.desk_zoom = new_zoom;
+ self.dirty_windowing();
+ return "ok\n".to_string();
+ }
+ "error: invalid zoom factor\n".to_string()
+ }
+ "set-coords" => {
+ if parts.len() < 3 { return "error: missing x or y\n".to_string(); }
+ if let (Ok(x), Ok(y)) = (parts[1].parse::<f64>(), parts[2].parse::<f64>()) {
+ if let Some(seat) = self.first_seat() {
+ if let crate::seat::Focus::Window(fw) = (*seat).focused {
+ (*fw).virtual_x = x;
+ (*fw).virtual_y = y;
+ self.dirty_windowing();
+ return "ok\n".to_string();
+ }
+ }
+ return "error: no focused window\n".to_string();
+ }
+ "error: invalid x or y\n".to_string()
+ }
+ "set-coords-of" => {
+ if parts.len() < 4 { return "error: missing app_id, x, or y\n".to_string(); }
+ let app_id_query = parts[1];
+ if let (Ok(x), Ok(y)) = (parts[2].parse::<f64>(), parts[3].parse::<f64>()) {
+ let mut found = false;
+ for &w in self.windows.iter() {
+ if !w.is_null() && !(*w).closed && !(*w).minimized && matches!((*w).state, crate::window::WindowState::Mapped) {
+ if let Some(aid) = (*w).get_app_id_string() {
+ if aid.to_lowercase() == app_id_query.to_lowercase() {
+ (*w).virtual_x = x;
+ (*w).virtual_y = y;
+ found = true;
+ }
+ }
+ }
+ }
+ if found {
+ self.dirty_windowing();
+ return "ok\n".to_string();
+ } else {
+ return "error: window not found\n".to_string();
+ }
+ }
+ "error: invalid x or y\n".to_string()
+ }
"close" => {
self.execute_action(&crate::config::Action::Close, None);
"ok\n".to_string()
@@ -1754,7 +1618,7 @@ impl WindowManager {
if let Some(seat) = self.first_seat() {
let mut target: *mut Window = std::ptr::null_mut();
for &w in self.windows.iter() {
- if !w.is_null() && !(*w).closed && !(*w).minimized && matches!((*w).state, crate::window::WindowState::Mapped) && ((*w).tags & self.active_tags) != 0 {
+ if !w.is_null() && !(*w).closed && !(*w).minimized && matches!((*w).state, crate::window::WindowState::Mapped) {
let aid = (*w).get_app_id_string();
let title = (*w).get_title_string();
@@ -1811,7 +1675,7 @@ impl WindowManager {
let app_id = (*w).get_app_id_string().unwrap_or_default();
let title = (*w).get_title_string().unwrap_or_default();
out.push_str(&format!(
- "window id={} app_id={} title=\"{}\" mode={} x={} y={} w={} h={} tags={} minimized={} has_parent={}\n",
+ "window id={} app_id={} title=\"{}\" mode={} x={} y={} w={} h={} vx={:.1} vy={:.1} minimized={} has_parent={}\n",
(*w).ref_key.index,
app_id,
title,
@@ -1820,7 +1684,8 @@ impl WindowManager {
(*w).box_geom.y,
(*w).box_geom.width,
(*w).box_geom.height,
- (*w).tags,
+ (*w).virtual_x,
+ (*w).virtual_y,
(*w).minimized,
(*w).has_parent,
));
@@ -1887,17 +1752,7 @@ impl WindowManager {
"ok\n".to_string()
}
"tag-layout" => {
- if parts.len() < 3 { return "error: missing tag or layout mode\n".to_string(); }
- if let Ok(tag) = parts[1].parse::<usize>() {
- if tag >= 1 && tag <= 4 {
- let mode = crate::config::parse_tiling_mode(parts[2]);
- self.tag_layouts[tag - 1] = mode;
- self.has_tag_layout[tag - 1] = true;
- self.dirty_windowing();
- return "ok\n".to_string();
- }
- }
- "error: invalid tag\n".to_string()
+ "ok\n".to_string()
}
"mode" => {
if parts.len() < 3 { return "error: missing mode or app_id\n".to_string(); }
@@ -2019,13 +1874,6 @@ unsafe extern "C" fn handle_ipc_timer(data: *mut std::ffi::c_void) -> std::os::r
0
}
-fn blend_channel(bg_channel: u32, fg_channel: u32, factor: f64) -> u32 {
- let bg = (bg_channel & 0xFF) as u8 as f64;
- let fg = (fg_channel & 0xFF) as u8 as f64;
- let val = (bg + (fg - bg) * factor) as u8;
- val as u32 * 0x01010101
-}
-
unsafe fn rendered_fullscreen(window: *mut Window) -> bool {
(*window).is_fullscreen() && !(*window).rendering_requested.hidden
}
diff --git a/src/server/xdg_toplevel.rs b/src/server/xdg_toplevel.rs
index 429eea2..272f290 100644
--- a/src/server/xdg_toplevel.rs
+++ b/src/server/xdg_toplevel.rs
@@ -610,6 +610,8 @@ unsafe extern "C" fn handle_request_move(
start_win_y: (*window).box_geom.y,
start_win_w: (*window).box_geom.width as u32,
start_win_h: (*window).box_geom.height as u32,
+ start_win_virtual_x: (*window).virtual_x,
+ start_win_virtual_y: (*window).virtual_y,
});
cursor.op_start_pointer();
@@ -657,6 +659,8 @@ unsafe extern "C" fn handle_request_resize(
start_win_y: (*window).box_geom.y,
start_win_w: (*window).box_geom.width as u32,
start_win_h: (*window).box_geom.height as u32,
+ start_win_virtual_x: (*window).virtual_x,
+ start_win_virtual_y: (*window).virtual_y,
});
cursor.op_start_pointer();