Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
fix: enable blur effect on window backplate and sync corner radius
scenefx/render/fx_renderer/fx_pass.c | 15 +-
.../render/fx_renderer/shaders/corner_alpha.frag | 6 +-
scenefx/render/fx_renderer/shaders/quad_round.frag | 55 ++++----
scenefx/types/scene/wlr_scene.c | 50 ++++++-
src/server/config.rs | 48 ++++++-
src/server/cursor.rs | 33 +++--
src/server/output.rs | 115 ++++++++++++----
src/server/output_manager.rs | 3 +
src/server/seat.rs | 4 +-
src/server/window.rs | 52 ++++---
src/server/window_manager.rs | 153 +++++++++++++++++++--
src/server/wlroots_log_wrapper.c | 83 +++++++----
src/server/xdg_toplevel.rs | 26 +++-
13 files changed, 484 insertions(+), 159 deletions(-)
diff --git a/scenefx/render/fx_renderer/fx_pass.c b/scenefx/render/fx_renderer/fx_pass.c
index e0a7774..d7d0f4c 100644
--- a/scenefx/render/fx_renderer/fx_pass.c
+++ b/scenefx/render/fx_renderer/fx_pass.c
@@ -690,7 +690,7 @@ void fx_render_pass_add_rounded_rect(struct fx_gles_render_pass *pass,
struct fx_corner_fradii corners = fx_options->corners;
uniform_corner_radii_set(&shader.radius, &corners);
- glUniform1f(shader.fade_inset, fx_options->fade_inset);
+ glUniform1f(shader.fade_inset, (float)fx_options->fade_inset / 1000.0f);
render(&box, &clip_region, renderer->shaders.quad_round.pos_attrib);
pixman_region32_fini(&clip_region);
@@ -805,7 +805,7 @@ void fx_render_pass_add_box_shadow(struct fx_gles_render_pass *pass,
// blending will practically always be needed (unless we have a madman
// who uses opaque shadows with zero sigma), so just enable it
setup_blending(WLR_RENDER_BLEND_MODE_PREMULTIPLIED);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
glUseProgram(renderer->shaders.box_shadow.program);
@@ -825,7 +825,7 @@ void fx_render_pass_add_box_shadow(struct fx_gles_render_pass *pass,
render(&box, &clip_region, renderer->shaders.box_shadow.pos_attrib);
pixman_region32_fini(&clip_region);
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
pop_fx_debug(renderer);
TRACY_BOTH_ZONES_END;
@@ -1260,6 +1260,11 @@ void fx_render_pass_read_to_buffer(struct fx_gles_render_pass *pass,
goto done;
}
+ float saved_projection_matrix[9];
+ memcpy(saved_projection_matrix, pass->projection_matrix, sizeof(saved_projection_matrix));
+ matrix_projection(pass->projection_matrix, dst_buffer->buffer->width, dst_buffer->buffer->height,
+ WL_OUTPUT_TRANSFORM_NORMAL);
+
// Draw onto the dst_buffer
fx_framebuffer_bind(dst_buffer);
wlr_render_pass_add_texture(&pass->base, &(struct wlr_render_texture_options) {
@@ -1282,6 +1287,8 @@ void fx_render_pass_read_to_buffer(struct fx_gles_render_pass *pass,
});
wlr_texture_destroy(src_tex);
+ memcpy(pass->projection_matrix, saved_projection_matrix, sizeof(saved_projection_matrix));
+
// Bind back to the main WLR buffer
fx_framebuffer_bind(pass->buffer);
@@ -1350,7 +1357,7 @@ struct fx_gles_render_pass *fx_begin_buffer_pass(struct fx_framebuffer *buffer,
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glViewport(0, 0, wlr_buffer->width, wlr_buffer->height);
- glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
+ glBlendFuncSeparate(GL_ONE, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
glDisable(GL_SCISSOR_TEST);
pop_fx_debug(renderer);
diff --git a/scenefx/render/fx_renderer/shaders/corner_alpha.frag b/scenefx/render/fx_renderer/shaders/corner_alpha.frag
index 42c1413..c3f78fa 100644
--- a/scenefx/render/fx_renderer/shaders/corner_alpha.frag
+++ b/scenefx/render/fx_renderer/shaders/corner_alpha.frag
@@ -14,9 +14,10 @@ float corner_alpha(vec2 size, vec2 position, bool is_cutout,
}
vec2 relative_pos = (gl_FragCoord.xy - position);
+ relative_pos.y = size.y - relative_pos.y;
- if (relative_pos.x < 0.0 || relative_pos.y < 0.0
- || relative_pos.x > size.x || relative_pos.y > size.y) {
+ if (relative_pos.x < -0.5 || relative_pos.y < -0.5
+ || relative_pos.x > size.x + 0.5 || relative_pos.y > size.y + 0.5) {
if (is_cutout) {
return 1.0;
}
@@ -59,6 +60,7 @@ float corner_alpha(vec2 size, vec2 position, bool is_cutout,
float corner_dist(vec2 size, vec2 position,
float radius_tl, float radius_tr, float radius_bl, float radius_br) {
vec2 relative_pos = (gl_FragCoord.xy - position);
+ relative_pos.y = size.y - relative_pos.y;
vec2 top_left = abs(relative_pos - size) - size + radius_tl;
vec2 top_right = abs(relative_pos - vec2(0, size.y)) - size + radius_tr;
diff --git a/scenefx/render/fx_renderer/shaders/quad_round.frag b/scenefx/render/fx_renderer/shaders/quad_round.frag
index 3e80e26..fb41842 100644
--- a/scenefx/render/fx_renderer/shaders/quad_round.frag
+++ b/scenefx/render/fx_renderer/shaders/quad_round.frag
@@ -1,8 +1,4 @@
-#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
-#else
-precision mediump float;
-#endif
varying vec4 v_color;
varying vec2 v_texcoord;
@@ -22,22 +18,39 @@ uniform float clip_radius_top_right;
uniform float clip_radius_bottom_left;
uniform float clip_radius_bottom_right;
+float get_dist(vec2 q, float radius);
float corner_alpha(vec2 size, vec2 position, bool is_cutout,
float radius_tl, float radius_tr, float radius_bl, float radius_br);
-float corner_dist(vec2 size, vec2 position,
- float radius_tl, float radius_tr, float radius_bl, float radius_br);
void main() {
- float quad_corner_alpha = corner_alpha(
- size - 1.0,
- position + 0.5,
- false,
- radius_top_left,
- radius_top_right,
- radius_bottom_left,
- radius_bottom_right
+ vec2 relative_pos = (gl_FragCoord.xy - (position + 0.5));
+ relative_pos.y = size.y - relative_pos.y;
+
+ // Bounding box check
+ /* if (relative_pos.x < -0.5 || relative_pos.y < -0.5
+ || relative_pos.x > size.x - 0.5 || relative_pos.y > size.y - 0.5) {
+ discard;
+ } */
+
+ float r_tl = radius_top_left > 0.0 ? radius_top_left + fade_inset : 0.0;
+ float r_tr = radius_top_right > 0.0 ? radius_top_right + fade_inset : 0.0;
+ float r_bl = radius_bottom_left > 0.0 ? radius_bottom_left + fade_inset : 0.0;
+ float r_br = radius_bottom_right > 0.0 ? radius_bottom_right + fade_inset : 0.0;
+
+ // Calculate corner distance
+ vec2 top_left = abs(relative_pos - (size - 1.0)) - (size - 1.0) + r_tl;
+ vec2 top_right = abs(relative_pos - vec2(0.0, size.y - 1.0)) - (size - 1.0) + r_tr;
+ vec2 bottom_left = abs(relative_pos - vec2(size.x - 1.0, 0.0)) - (size - 1.0) + r_bl;
+ vec2 bottom_right = abs(relative_pos) - (size - 1.0) + r_br;
+
+ float dist = max(
+ max(get_dist(top_left, r_tl), get_dist(top_right, r_tr)),
+ max(get_dist(bottom_left, r_bl), get_dist(bottom_right, r_br))
);
+ float result = smoothstep(-0.5, 0.5, dist);
+ float quad_corner_alpha = 1.0 - result;
+
// Clipping
float clip_corner_alpha = corner_alpha(
clip_size - 1.0,
@@ -51,19 +64,7 @@ void main() {
vec4 final_color = v_color;
if (fade_inset > 0.0) {
- float r_tl = radius_top_left > 0.0 ? radius_top_left + fade_inset : 0.0;
- float r_tr = radius_top_right > 0.0 ? radius_top_right + fade_inset : 0.0;
- float r_bl = radius_bottom_left > 0.0 ? radius_bottom_left + fade_inset : 0.0;
- float r_br = radius_bottom_right > 0.0 ? radius_bottom_right + fade_inset : 0.0;
- float d = corner_dist(
- size - 1.0,
- position + 0.5,
- r_tl,
- r_tr,
- r_bl,
- r_br
- );
- float inside_dist = -d;
+ float inside_dist = 0.5 - dist;
float fade_factor = clamp(inside_dist / fade_inset, 0.0, 1.0);
final_color *= fade_factor;
}
diff --git a/scenefx/types/scene/wlr_scene.c b/scenefx/types/scene/wlr_scene.c
index 6ceefd5..aa3e1cd 100644
--- a/scenefx/types/scene/wlr_scene.c
+++ b/scenefx/types/scene/wlr_scene.c
@@ -40,6 +40,13 @@
#define DMABUF_FEEDBACK_DEBOUNCE_FRAMES 30
#define HIGHLIGHT_DAMAGE_FADEOUT_TIME 250
+#define CURSOR_HISTORY_SIZE 16
+static struct {
+ int x, y, w, h;
+ bool valid;
+} g_cursor_history[CURSOR_HISTORY_SIZE];
+static int g_cursor_history_index = 0;
+
struct wlr_scene_tree *wlr_scene_tree_from_node(struct wlr_scene_node *node) {
assert(node->type == WLR_SCENE_NODE_TREE);
struct wlr_scene_tree *tree = wl_container_of(node, tree, node);
@@ -2028,7 +2035,7 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren
.a = scene_rect->color[3],
},
.clip = &render_region,
- .blend_mode = scene_rect->color[3] == 1.0f ?
+ .blend_mode = (scene_rect->color[3] == 1.0f && fx_corner_radii_is_empty(&rect_corners) && scene_rect->fade_inset == 0) ?
WLR_RENDER_BLEND_MODE_NONE : WLR_RENDER_BLEND_MODE_PREMULTIPLIED,
},
.clipped_region = {
@@ -2175,8 +2182,7 @@ static void scene_entry_render(struct render_list_entry *entry, const struct ren
struct wlr_scene_optimized_blur *scene_blur = wlr_scene_optimized_blur_from_node(node);
// Re-render the optimized blur buffer when needed. Retry rendering
// until there's a visible blur_node.
- if (fx_pass->has_blur && is_scene_blur_enabled(&scene->blur_data)
- && scene_blur->dirty) {
+ if (fx_pass->has_blur && is_scene_blur_enabled(&scene->blur_data)) {
const float opacity = 1.0f;
enum wl_output_transform transform =
wlr_output_transform_invert(data->transform);
@@ -3350,7 +3356,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
if (apply_blur_region(node, &blur_data, &render_data, state,
&original_damage, &blur_padding_region)) {
- should_compensate_blur = true;
+ should_compensate_blur = false;
fx_pass->has_blur = true;
}
}
@@ -3370,6 +3376,42 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output,
pixman_region32_intersect_rect(&render_data.damage, &render_data.damage,
0, 0, output->width, output->height);
+ // Add current software cursors to the history
+ struct wlr_output_cursor *cursor;
+ wl_list_for_each(cursor, &output->cursors, link) {
+ if (!cursor->enabled || !cursor->visible) {
+ continue;
+ }
+ double scale = output->scale;
+ int cx = (cursor->x - cursor->hotspot_x) * scale;
+ int cy = (cursor->y - cursor->hotspot_y) * scale;
+ int cw = cursor->width;
+ int ch = cursor->height;
+
+ g_cursor_history[g_cursor_history_index].x = cx;
+ g_cursor_history[g_cursor_history_index].y = cy;
+ g_cursor_history[g_cursor_history_index].w = cw;
+ g_cursor_history[g_cursor_history_index].h = ch;
+ g_cursor_history[g_cursor_history_index].valid = true;
+ g_cursor_history_index = (g_cursor_history_index + 1) % CURSOR_HISTORY_SIZE;
+ }
+
+ // Subtract all historical cursor regions from the blur padding region
+ for (int i = 0; i < CURSOR_HISTORY_SIZE; i++) {
+ if (!g_cursor_history[i].valid) {
+ continue;
+ }
+ pixman_region32_t cursor_reg;
+ pixman_region32_init_rect(&cursor_reg,
+ g_cursor_history[i].x - 16,
+ g_cursor_history[i].y - 16,
+ g_cursor_history[i].w + 32,
+ g_cursor_history[i].h + 32);
+ pixman_region32_subtract(&fx_pass->blur_padding_region,
+ &fx_pass->blur_padding_region, &cursor_reg);
+ pixman_region32_fini(&cursor_reg);
+ }
+
// Copy the surrounding content where the blur would display artifacts
// and draw it above the artifacts. Otherwise The old rendered
// content would be included into the new blur. This means that
diff --git a/src/server/config.rs b/src/server/config.rs
index 27e9977..481260b 100644
--- a/src/server/config.rs
+++ b/src/server/config.rs
@@ -47,6 +47,8 @@ pub struct Layout {
pub desktop_gap_width: i32,
pub desktop_cell_corner_radius: i32,
pub desktop_cell_fade_inset: i64,
+ pub desktop_enable_solid_color: bool,
+ pub desktop_solid_color: [f32; 4],
pub scenefx_optimized_blur: bool,
pub status_backdrop_blur_ignore_transparent: bool,
pub window_backdrop_blur_ignore_transparent: bool,
@@ -90,11 +92,13 @@ impl Default for Layout {
desktop_gap_color: "#000000".to_string(),
transparency_opacity: 0.9,
window_opacity: true,
- desktop_cell_color: [1.0, 1.0, 1.0, 0.05],
+ desktop_cell_color: [0.05, 0.05, 0.05, 0.05],
desktop_grid_scale: 100.0,
desktop_gap_width: 1,
desktop_cell_corner_radius: 0,
desktop_cell_fade_inset: 0,
+ desktop_enable_solid_color: false,
+ desktop_solid_color: [0.0, 0.0, 0.0, 1.0],
scenefx_optimized_blur: true,
status_backdrop_blur_ignore_transparent: true,
window_backdrop_blur_ignore_transparent: true,
@@ -281,6 +285,10 @@ pub struct SurfacesConfig {
pub desktop_cell_corner_radius: i64,
#[serde(default = "default_desktop_cell_fade_inset")]
pub desktop_cell_fade_inset: i64,
+ #[serde(default = "default_desktop_enable_solid_color")]
+ pub desktop_enable_solid_color: bool,
+ #[serde(default = "default_desktop_solid_color")]
+ pub desktop_solid_color: String,
#[serde(default = "default_backplate_color")]
pub backplate_color: String,
#[serde(default = "default_backplate_blur")]
@@ -298,11 +306,13 @@ impl Default for SurfacesConfig {
desktop_gap_width: default_desktop_gap_width(),
desktop_cell_corner_radius: default_desktop_cell_corner_radius(),
desktop_cell_fade_inset: default_desktop_cell_fade_inset(),
+ desktop_enable_solid_color: default_desktop_enable_solid_color(),
+ desktop_solid_color: default_desktop_solid_color(),
backplate_color: default_backplate_color(),
backplate_blur: default_backplate_blur(),
backplate_corner_radius: default_backplate_corner_radius(),
}
- }
+ }
}
fn default_desktop_gap_color() -> String {
@@ -329,6 +339,14 @@ fn default_desktop_cell_fade_inset() -> i64 {
0
}
+fn default_desktop_enable_solid_color() -> bool {
+ false
+}
+
+fn default_desktop_solid_color() -> String {
+ "#000000".to_string()
+}
+
fn default_backplate_color() -> String {
"#151520e6".to_string()
}
@@ -502,11 +520,12 @@ pub fn parse_hex_color_rgba(hex_str: &str) -> [f32; 4] {
u8::from_str_radix(&hex[4..6], 16),
u8::from_str_radix(&hex[6..8], 16),
) {
+ let alpha = a as f32 / 255.0;
return [
- r as f32 / 255.0,
- g as f32 / 255.0,
- b as f32 / 255.0,
- a as f32 / 255.0,
+ (r as f32 / 255.0) * alpha,
+ (g as f32 / 255.0) * alpha,
+ (b as f32 / 255.0) * alpha,
+ alpha,
];
}
} else if hex.len() == 6 {
@@ -523,7 +542,7 @@ pub fn parse_hex_color_rgba(hex_str: &str) -> [f32; 4] {
];
}
}
- [1.0, 1.0, 1.0, 0.05] // fallback default (5% white)
+ [0.05, 0.05, 0.05, 0.05] // fallback default (5% white)
}
pub fn parse_tiling_mode(s: &str) -> TilingMode {
@@ -1212,6 +1231,16 @@ fn parse_kdl_config(content: &str) -> Result<Config, String> {
surfaces.desktop_cell_fade_inset = val;
}
}
+ "enable_solid_color" => {
+ if let Some(val) = entry.value().as_bool() {
+ surfaces.desktop_enable_solid_color = val;
+ }
+ }
+ "solid_color" => {
+ if let Some(val) = entry.value().as_string() {
+ surfaces.desktop_solid_color = val.to_string();
+ }
+ }
_ => {}
}
}
@@ -1254,6 +1283,8 @@ fn parse_kdl_config(content: &str) -> Result<Config, String> {
surfaces.desktop_gap_width = get_child_arg_i64(node, "desktop_gap_width", default_desktop_gap_width());
surfaces.desktop_cell_corner_radius = get_child_arg_i64(node, "desktop_cell_corner_radius", default_desktop_cell_corner_radius());
surfaces.desktop_cell_fade_inset = get_child_arg_i64(node, "desktop_cell_fade_inset", default_desktop_cell_fade_inset());
+ surfaces.desktop_enable_solid_color = get_child_arg_bool(node, "desktop_enable_solid_color", default_desktop_enable_solid_color());
+ surfaces.desktop_solid_color = get_child_arg_string(node, "desktop_solid_color", &default_desktop_solid_color());
surfaces.backplate_color = get_child_arg_string(node, "backplate_color", &default_backplate_color());
surfaces.backplate_blur = get_child_arg_f64(node, "backplate_blur", default_backplate_blur());
surfaces.backplate_corner_radius = get_child_arg_i64(node, "backplate_corner_radius", default_backplate_corner_radius());
@@ -1321,6 +1352,8 @@ pub fn parse_config(path: &str, state: &mut crate::window_manager::WindowManager
state.layout.desktop_gap_width = config.surfaces.desktop_gap_width as i32;
state.layout.desktop_cell_corner_radius = config.surfaces.desktop_cell_corner_radius as i32;
state.layout.desktop_cell_fade_inset = config.surfaces.desktop_cell_fade_inset;
+ state.layout.desktop_enable_solid_color = config.surfaces.desktop_enable_solid_color;
+ state.layout.desktop_solid_color = parse_hex_color_rgba(&config.surfaces.desktop_solid_color);
state.layout.border_font_size = 11;
state.layout.transition_duration = config.layout.transition_duration as i32;
@@ -1555,6 +1588,7 @@ mod tests {
assert_eq!(server.wm.layout.desktop_gap_color, "#a5cfc2");
assert_eq!(server.wm.layout.desktop_gap_width, 24);
assert_eq!(server.wm.layout.desktop_cell_corner_radius, 8);
+ assert_eq!(server.wm.layout.desktop_enable_solid_color, true);
println!("TEST_WM_STARTUP: {:?}", server.wm.startup);
println!("TEST_WM_PATH: {:?}", std::env::var("PATH"));
}
diff --git a/src/server/cursor.rs b/src/server/cursor.rs
index 933c980..f9b8d5f 100644
--- a/src/server/cursor.rs
+++ b/src/server/cursor.rs
@@ -468,10 +468,12 @@ impl Cursor {
let mut is_window = false;
match result.data {
SceneNodeDataVal::Window(window) => {
- if !(*window).is_status_bar() {
+ if !(*window).is_status_bar() && !(*window).is_wallpaper() {
is_window = true;
}
- if (*window).tiling_mode != crate::tiling::TilingMode::Popup
+ if !(*window).is_status_bar()
+ && !(*window).is_wallpaper()
+ && (*window).tiling_mode != crate::tiling::TilingMode::Popup
&& (*window).tiling_mode != crate::tiling::TilingMode::Fullscreen
&& (*window).tiling_mode != crate::tiling::TilingMode::Status
&& (*server).wm.mode == crate::window_manager::WindowManagerMode::Normal
@@ -587,7 +589,7 @@ unsafe extern "C" fn handle_button(listener: *mut ffi::wl_listener, data: *mut s
if let Some(result) = (*server).scene.at(lx, ly) {
match result.data {
SceneNodeDataVal::Window(window) => {
- if !(*window).is_status_bar() {
+ if !(*window).is_status_bar() && !(*window).is_wallpaper() {
is_app_surface = true;
if (*window).tiling_mode == crate::tiling::TilingMode::Overlay {
is_overlay_window = true;
@@ -629,7 +631,7 @@ unsafe extern "C" fn handle_button(listener: *mut ffi::wl_listener, data: *mut s
}
}
- if !clicked_win.is_null() && !(*clicked_win).is_status_bar() {
+ if !clicked_win.is_null() && !(*clicked_win).is_status_bar() && !(*clicked_win).is_wallpaper() {
(*server).wm.stop_panning_animation();
let cursor_x = (*cursor.wlr_cursor).x;
let cursor_y = (*cursor.wlr_cursor).y;
@@ -670,7 +672,12 @@ unsafe extern "C" fn handle_button(listener: *mut ffi::wl_listener, data: *mut s
let mut clicked_interactive = false;
if let Some(result) = (*server).scene.at(lx, ly) {
match result.data {
- SceneNodeDataVal::Window(_) | SceneNodeDataVal::LayerSurface(_) | SceneNodeDataVal::ShellSurface(_) | SceneNodeDataVal::LockSurface(_) | SceneNodeDataVal::OverrideRedirect(_) => {
+ SceneNodeDataVal::Window(window) => {
+ if !(*window).is_wallpaper() {
+ clicked_interactive = true;
+ }
+ }
+ SceneNodeDataVal::LayerSurface(_) | SceneNodeDataVal::ShellSurface(_) | SceneNodeDataVal::LockSurface(_) | SceneNodeDataVal::OverrideRedirect(_) => {
clicked_interactive = true;
}
}
@@ -709,7 +716,7 @@ unsafe extern "C" fn handle_button(listener: *mut ffi::wl_listener, data: *mut s
}
}
- if !target_win.is_null() && !(*target_win).is_status_bar() {
+ if !target_win.is_null() && !(*target_win).is_status_bar() && !(*target_win).is_wallpaper() {
if (*target_win).tiling_mode != crate::tiling::TilingMode::Floating
&& (*target_win).tiling_mode != crate::tiling::TilingMode::Popup
&& (*target_win).tiling_mode != crate::tiling::TilingMode::Fullscreen
@@ -795,7 +802,7 @@ unsafe extern "C" fn handle_button(listener: *mut ffi::wl_listener, data: *mut s
}
}
- if !border_target_win.is_null() && !(*border_target_win).is_status_bar() && (
+ if !border_target_win.is_null() && !(*border_target_win).is_status_bar() && !(*border_target_win).is_wallpaper() && (
(*border_target_win).tiling_mode != crate::tiling::TilingMode::Popup
&& (*border_target_win).tiling_mode != crate::tiling::TilingMode::Fullscreen
) {
@@ -978,7 +985,7 @@ unsafe extern "C" fn handle_button(listener: *mut ffi::wl_listener, data: *mut s
match result.data {
SceneNodeDataVal::Window(window) => {
clicked_something = true;
- if !(*window).is_status_bar() {
+ if !(*window).is_status_bar() && !(*window).is_wallpaper() {
if !seat.object.is_null() {
if !(*window).object.is_null() {
ffi::wl_resource_post_event(seat.object, 4, (*window).object);
@@ -1020,7 +1027,7 @@ unsafe extern "C" fn handle_button(listener: *mut ffi::wl_listener, data: *mut s
(*win_ptr).mode_locked = op.start_mode_locked;
let server = seat.server;
- if !(*win_ptr).closed && !(*win_ptr).is_status_bar() {
+ if !(*win_ptr).closed && !(*win_ptr).is_status_bar() && !(*win_ptr).is_wallpaper() {
let mut viewport_w = 1920.0;
let mut viewport_h = 1080.0;
let outputs_list = &mut (*server).om.outputs as *mut ffi::wl_list as *mut WlList;
@@ -1360,7 +1367,7 @@ unsafe extern "C" fn handle_touch_down(listener: *mut ffi::wl_listener, data: *m
let mut is_overlay_window = false;
match result.data {
SceneNodeDataVal::Window(window) => {
- if !(*window).is_status_bar() {
+ if !(*window).is_status_bar() && !(*window).is_wallpaper() {
is_app_surface = true;
if (*window).tiling_mode == crate::tiling::TilingMode::Overlay {
is_overlay_window = true;
@@ -1429,7 +1436,7 @@ unsafe extern "C" fn handle_touch_motion(listener: *mut ffi::wl_listener, data:
let mut is_overlay_window = false;
match result.data {
SceneNodeDataVal::Window(window) => {
- if !(*window).is_status_bar() {
+ if !(*window).is_status_bar() && !(*window).is_wallpaper() {
is_app_surface = true;
if (*window).tiling_mode == crate::tiling::TilingMode::Overlay {
is_overlay_window = true;
@@ -1482,7 +1489,7 @@ unsafe extern "C" fn handle_touch_up(listener: *mut ffi::wl_listener, data: *mut
if let Some(result) = (*server).scene.at(lx, ly) {
match result.data {
SceneNodeDataVal::Window(window) => {
- if !(*window).is_status_bar() {
+ if !(*window).is_status_bar() && !(*window).is_wallpaper() {
is_app_surface = true;
if (*window).tiling_mode == crate::tiling::TilingMode::Overlay {
is_overlay_window = true;
@@ -1682,7 +1689,7 @@ unsafe extern "C" fn handle_swipe_update(listener: *mut ffi::wl_listener, data:
hovered_win = window;
}
}
- if !hovered_win.is_null() && !(*hovered_win).is_status_bar() {
+ if !hovered_win.is_null() && !(*hovered_win).is_status_bar() && !(*hovered_win).is_wallpaper() {
seat.focus(Focus::Window(hovered_win));
if !seat.object.is_null() && !(*hovered_win).object.is_null() {
ffi::wl_resource_post_event(seat.object, 4, (*hovered_win).object);
diff --git a/src/server/output.rs b/src/server/output.rs
index 5f6039b..d3d844f 100644
--- a/src/server/output.rs
+++ b/src/server/output.rs
@@ -167,7 +167,9 @@ pub struct Output {
pub last_grid_cell_fade_inset: i64,
pub last_grid_gap_color: String,
pub grid_is_low_res: bool,
+ pub last_grid_enable_solid_color: bool,
pub grid_rect_pool: Vec<*mut ffi::wlr_scene_rect>,
+ pub grid_force_redraw_frames: u8,
pub destroy: ffi::wl_listener,
pub request_state: ffi::wl_listener,
@@ -418,7 +420,9 @@ impl Output {
last_grid_cell_fade_inset: 0,
last_grid_gap_color: String::new(),
grid_is_low_res: false,
+ last_grid_enable_solid_color: false,
grid_rect_pool: Vec::new(),
+ grid_force_redraw_frames: 0,
destroy: std::mem::zeroed(),
request_state: std::mem::zeroed(),
frame: std::mem::zeroed(),
@@ -551,6 +555,24 @@ impl Output {
}
let wm = &(*self.server).wm;
+ if wm.layout.desktop_enable_solid_color {
+ ffi::wlr_scene_node_set_enabled(self.grid_tree as *mut ffi::wlr_scene_node, false);
+ for &rect in &self.grid_rect_pool {
+ ffi::wlr_scene_node_set_enabled(rect as *mut ffi::wlr_scene_node, false);
+ }
+ if !self.last_grid_enable_solid_color {
+ self.last_grid_enable_solid_color = true;
+ }
+ return;
+ }
+
+ ffi::wlr_scene_node_set_enabled(self.grid_tree as *mut ffi::wlr_scene_node, true);
+
+ if self.last_grid_enable_solid_color {
+ self.last_grid_enable_solid_color = false;
+ self.grid_force_redraw_frames = 3;
+ }
+
let (viewport_w, viewport_h) = self.current.dimensions();
let zoom = wm.desk_zoom;
@@ -570,22 +592,63 @@ impl Output {
let cell_fade_inset = wm.layout.desktop_cell_fade_inset;
let gap_color = wm.layout.desktop_gap_color.clone();
- let structure_changed = self.last_grid_viewport_w != viewport_w
- || self.last_grid_viewport_h != viewport_h
- || self.last_grid_zoom != zoom
- || self.last_grid_spacing != cell_size
- || self.last_grid_gap_width != gap_size as i32
- || self.last_grid_cell_color != cell_color
- || self.last_grid_cell_corner_radius != cell_corner_radius
- || self.last_grid_cell_fade_inset != cell_fade_inset
- || self.last_grid_gap_color != gap_color;
+ if self.last_grid_zoom == 0.0 {
+ self.last_grid_zoom = zoom;
+ }
+
+ let c_w = self.last_grid_viewport_w != viewport_w;
+ let c_h = self.last_grid_viewport_h != viewport_h;
+ let c_z = self.last_grid_zoom != zoom;
+ let c_s = self.last_grid_spacing != cell_size;
+ let c_g = self.last_grid_gap_width != gap_size as i32;
+ let c_cc = self.last_grid_cell_color != cell_color;
+ let c_cr = self.last_grid_cell_corner_radius != cell_corner_radius;
+ let c_fi = self.last_grid_cell_fade_inset != cell_fade_inset;
+ let c_gc = self.last_grid_gap_color != gap_color;
+
+ let structure_changed = c_w || c_h || c_z || c_s || c_g || c_cc || c_cr || c_fi || c_gc;
+ if structure_changed {
+ self.grid_force_redraw_frames = 3;
+ }
- let force_high_res = !structure_changed && self.grid_is_low_res;
+ let force_redraw = self.grid_force_redraw_frames > 0;
let period_pixels = period * zoom;
- if structure_changed || force_high_res {
+ // Apply modulo translation to the grid tree based on panning
+ let shift_x = -((wm.desk_pan_x * zoom).rem_euclid(period_pixels));
+ let shift_y = -((wm.desk_pan_y * zoom).rem_euclid(period_pixels));
+
+ let dest_x = self.sent.x + shift_x.round() as i32;
+ let dest_y = self.sent.y + shift_y.round() as i32;
+
+ log::debug!(
+ "draw_grid: structure_changed={}, force_redraw={}, force_redraw_frames={}, grid_is_low_res={}, last_zoom={}, zoom={}, dest=({}, {}), shift=({}, {}), pan=({}, {}), spacing={}, cell_size={}, fade_inset={}, cell_fade_inset={}",
+ structure_changed,
+ force_redraw,
+ self.grid_force_redraw_frames,
+ self.grid_is_low_res,
+ self.last_grid_zoom,
+ zoom,
+ dest_x,
+ dest_y,
+ shift_x,
+ shift_y,
+ wm.desk_pan_x,
+ wm.desk_pan_y,
+ self.last_grid_spacing,
+ cell_size,
+ self.last_grid_cell_fade_inset,
+ cell_fade_inset
+ );
+
+ if force_redraw {
+ if self.grid_force_redraw_frames > 0 {
+ self.grid_force_redraw_frames -= 1;
+ }
+
let draw_low_res = self.last_grid_zoom != zoom;
+ log::debug!("Entering redraw block: draw_low_res={}, force_redraw_frames_left={}", draw_low_res, self.grid_force_redraw_frames);
// Cache current parameters (excluding pan coordinates)
self.last_grid_viewport_w = viewport_w;
@@ -622,6 +685,7 @@ impl Output {
if !rect.is_null() {
ffi::wlr_scene_node_set_position(rect as *mut ffi::wlr_scene_node, x, y);
ffi::river_scene_rect_set_corner_radius(rect, r);
+ ffi::wlr_scene_rect_set_fade_inset(rect, 0);
}
pool_idx += 1;
rect
@@ -629,8 +693,8 @@ impl Output {
// Draw the base level background rect (gap color)
// Sized larger to allow shifting by up to one period
- let bg_w = viewport_w + period_pixels.ceil() as i32;
- let bg_h = viewport_h + period_pixels.ceil() as i32;
+ let bg_w = viewport_w + period_pixels.ceil().round() as i32;
+ let bg_h = viewport_h + period_pixels.ceil().round() as i32;
let gap_color_arr = crate::config::parse_hex_color_rgba(&self.last_grid_gap_color);
let _bg_rect = get_rect(bg_w, bg_h, gap_color_arr.as_ptr(), 0, 0, 0);
@@ -639,27 +703,22 @@ impl Output {
let rows = (viewport_h as f64 / period_pixels).ceil() as i32 + 1;
for col in 0..=cols {
- let rel_x = (col as f64 * period_pixels) as i32;
- let rw = (cell_size * zoom) as i32;
+ let rel_x = (col as f64 * period_pixels).round() as i32;
+ let rw = (cell_size * zoom).round() as i32;
if rw <= 0 {
continue;
}
for row in 0..=rows {
- let rel_y = (row as f64 * period_pixels) as i32;
- let rh = (cell_size * zoom) as i32;
+ let rel_y = (row as f64 * period_pixels).round() as i32;
+ let rh = (cell_size * zoom).round() as i32;
if rh <= 0 {
continue;
}
- let draw_fade = cell_fade_inset > 0 && !draw_low_res;
- let scaled_corner_radius = (cell_corner_radius as f64 * zoom) as i32;
+ let scaled_corner_radius = (cell_corner_radius as f64 * zoom).round() as i32;
let cell_rect = get_rect(rw, rh, cell_color.as_ptr(), rel_x, rel_y, scaled_corner_radius);
if !cell_rect.is_null() {
- let inset_scaled = if draw_fade {
- (cell_fade_inset as f64 * zoom) as i32
- } else {
- 0
- };
+ let inset_scaled = (cell_fade_inset as f64 * zoom * 1000.0).round() as i32;
ffi::wlr_scene_rect_set_fade_inset(cell_rect, inset_scaled);
}
}
@@ -671,14 +730,10 @@ impl Output {
}
}
- // Apply modulo translation to the grid tree based on panning
- let shift_x = -((wm.desk_pan_x * zoom).rem_euclid(period_pixels));
- let shift_y = -((wm.desk_pan_y * zoom).rem_euclid(period_pixels));
-
ffi::river_scene_node_set_position_if_changed(
self.grid_tree as *mut ffi::wlr_scene_node,
- shift_x as i32,
- shift_y as i32,
+ dest_x,
+ dest_y,
);
self.last_grid_pan_x = wm.desk_pan_x;
diff --git a/src/server/output_manager.rs b/src/server/output_manager.rs
index 4a1bd13..b43e0da 100644
--- a/src/server/output_manager.rs
+++ b/src/server/output_manager.rs
@@ -234,6 +234,9 @@ impl OutputManager {
output.sent.x,
output.sent.y,
);
+ if (*server).wm.layout.desktop_enable_solid_color {
+ ffi::wlr_scene_node_set_enabled(output.grid_tree as *mut ffi::wlr_scene_node, false);
+ }
}
}
OutputStateValue::DisabledHard => {
diff --git a/src/server/seat.rs b/src/server/seat.rs
index dd00fd9..cda9ebd 100644
--- a/src/server/seat.rs
+++ b/src/server/seat.rs
@@ -304,8 +304,8 @@ impl Seat {
pub unsafe fn focus(&mut self, new_focus: Focus) {
if let Focus::Window(window) = new_focus {
- if !window.is_null() && (*window).is_status_bar() {
- log::info!("[FocusDebug] Seat::focus blocking focus to status bar window");
+ if !window.is_null() && ((*window).is_status_bar() || (*window).is_wallpaper()) {
+ log::info!("[FocusDebug] Seat::focus blocking focus to status bar/wallpaper window");
return;
}
}
diff --git a/src/server/window.rs b/src/server/window.rs
index 425faa8..9bc1d1f 100644
--- a/src/server/window.rs
+++ b/src/server/window.rs
@@ -274,6 +274,10 @@ impl Window {
self.get_app_id_string().as_deref().map_or(false, |id| id.starts_with("cce-status"))
}
+ pub unsafe fn is_wallpaper(&self) -> bool {
+ self.get_app_id_string().as_deref() == Some("cce-wallpaper")
+ }
+
pub unsafe fn create(impl_type: WindowImpl, server: *mut Server) -> Result<*mut Self, &'static str> {
let hidden_tree = (*server).scene.hidden_tree;
@@ -632,7 +636,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.starts_with("cce-status") {
+ if app_id_str.is_empty() || app_id_str.starts_with("cce-status") || app_id_str == "cce-wallpaper" {
return;
}
let title_str = self.get_title_string().unwrap_or_default();
@@ -697,14 +701,14 @@ impl Window {
}
let app_id_ptr = self.get_app_id();
- let is_status_bar = if !app_id_ptr.is_null() {
+ let (is_status_bar, is_wallpaper) = if !app_id_ptr.is_null() {
let app_id = std::ffi::CStr::from_ptr(app_id_ptr).to_string_lossy();
- app_id.starts_with("cce-status")
+ (app_id.starts_with("cce-status"), app_id.as_ref() == "cce-wallpaper")
} else {
- false
+ (false, false)
};
- if is_status_bar {
+ if is_status_bar || is_wallpaper {
self.tiling_mode = crate::tiling::TilingMode::Status;
} else {
let mut should_focus = true;
@@ -1490,7 +1494,8 @@ 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().map_or(false, |id| id.starts_with("cce-status")) {
+ let app_id_str = self.get_app_id_string();
+ if app_id_str.as_deref().map_or(false, |id| id.starts_with("cce-status") || id == "cce-wallpaper") {
self.tiling_mode = crate::tiling::TilingMode::Status;
}
self.try_restore();
@@ -1539,24 +1544,23 @@ impl Window {
if app_id.starts_with("cce-status") {
ignore_transparent = (*self.server).wm.layout.status_backdrop_blur_ignore_transparent;
}
- let scale = self.scale;
- let geom_x = (self.rendering_requested.x as f64 * scale) as i32;
- let geom_y = (self.rendering_requested.y as f64 * scale) as i32;
- let geom_w = (self.rendering_sent.width as f64 * scale) as i32;
- let geom_h = (self.rendering_sent.height as f64 * scale) as i32;
+ let width = (self.rendering_sent.width as f64 * self.scale) as i32;
+ let height = (self.rendering_sent.height as f64 * self.scale) as i32;
ffi::river_scene_node_enable_blur(
- self.surfaces.tree as *mut ffi::wlr_scene_node,
+ self.tree as *mut ffi::wlr_scene_node,
blur_enabled,
(*self.server).wm.layout.scenefx_optimized_blur,
ignore_transparent,
- geom_x,
- geom_y,
- geom_w,
- geom_h,
+ 0,
+ 0,
+ width,
+ height,
);
ffi::river_scene_node_set_opacity(self.tree as *mut ffi::wlr_scene_node, requested.opacity);
- let radius = if requested.circular {
+ let radius = if self.is_fullscreen() {
+ 0
+ } else if requested.circular {
let w = self.rendering_sent.width as i32;
let h = self.rendering_sent.height as i32;
w.min(h) / 2
@@ -1570,6 +1574,10 @@ impl Window {
self.surfaces.tree as *mut ffi::wlr_scene_node,
radius,
);
+ ffi::river_scene_rect_set_corner_radius(
+ self.window_background,
+ radius,
+ );
struct ScaleData {
scale: f64,
@@ -1674,14 +1682,14 @@ impl Window {
self.box_geom.y = (*output).sent.y;
let app_id_ptr = self.get_app_id();
- let is_status_bar = if !app_id_ptr.is_null() {
+ let (is_status_bar, is_wallpaper) = if !app_id_ptr.is_null() {
let app_id = std::ffi::CStr::from_ptr(app_id_ptr).to_string_lossy();
- app_id.starts_with("cce-status")
+ (app_id.starts_with("cce-status"), app_id.as_ref() == "cce-wallpaper")
} else {
- false
+ (false, false)
};
- ffi::wlr_scene_node_set_enabled(self.fullscreen_background as *mut ffi::wlr_scene_node, !is_status_bar);
+ ffi::wlr_scene_node_set_enabled(self.fullscreen_background as *mut ffi::wlr_scene_node, !is_status_bar && !is_wallpaper);
let (width, height) = (*output).sent.dimensions();
ffi::wlr_scene_rect_set_size(self.fullscreen_background, width as i32, height as i32);
clip = ffi::wlr_box { x: 0, y: 0, width: width as i32, height: height as i32 };
@@ -1861,7 +1869,7 @@ impl Window {
ffi::river_scene_node_set_position_if_changed(self.popup_tree as *mut ffi::wlr_scene_node, self.box_geom.x, self.box_geom.y);
// Disable backdrop blur during active viewport zoom/pan for maximum performance
- ffi::river_scene_node_enable_blur(self.surfaces.tree as *mut ffi::wlr_scene_node, false, (*self.server).wm.layout.scenefx_optimized_blur, true, 0, 0, 0, 0);
+ ffi::river_scene_node_enable_blur(self.tree as *mut ffi::wlr_scene_node, false, (*self.server).wm.layout.scenefx_optimized_blur, true, 0, 0, 0, 0);
self.scale_only_render_finish();
self.draw_borders();
diff --git a/src/server/window_manager.rs b/src/server/window_manager.rs
index 9905a7e..b987752 100644
--- a/src/server/window_manager.rs
+++ b/src/server/window_manager.rs
@@ -265,7 +265,7 @@ impl WindowManager {
if w.is_null() || (*w).closed || matches!((*w).state, crate::window::WindowState::Closing | crate::window::WindowState::Init) {
continue;
}
- if (*w).is_status_bar() {
+ if (*w).is_status_bar() || (*w).is_wallpaper() {
continue;
}
@@ -738,7 +738,10 @@ impl WindowManager {
ffi::wlr_scene_node_reparent((*window).popup_tree as *mut _, (*self.server).scene.hidden_tree);
} else {
ffi::wlr_scene_node_reparent((*window).popup_tree as *mut _, (*self.server).scene.layers.popups);
- if rendered_fullscreen(window) {
+ if (*window).get_app_id_string().as_deref() == Some("cce-wallpaper") {
+ ffi::wlr_scene_node_reparent((*window).tree as *mut _, (*self.server).scene.layers.background);
+ ffi::wlr_scene_node_lower_to_bottom((*window).tree as *mut _);
+ } else if rendered_fullscreen(window) {
ffi::wlr_scene_node_reparent((*window).tree as *mut _, (*self.server).scene.layers.fullscreen);
ffi::wlr_scene_node_raise_to_top((*window).tree as *mut _);
found_fullscreen = true;
@@ -953,6 +956,13 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
return;
}
+ let has_wallpaper = self.windows.iter().any(|&w| !w.is_null() && !(*w).closed && (*w).get_app_id_string().as_deref() == Some("cce-wallpaper"));
+ for &output in &active_outputs {
+ if !(*output).background_rect.is_null() {
+ ffi::wlr_scene_node_set_enabled((*output).background_rect as *mut ffi::wlr_scene_node, !has_wallpaper);
+ }
+ }
+
let mut focused_window: *mut Window = std::ptr::null_mut();
let seats_list = &mut (*self.server).input_manager.seats as *mut ffi::wl_list as *mut WlList;
let mut curr_seat = (*seats_list).next;
@@ -1002,20 +1012,31 @@ 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().map_or(false, |id| id.starts_with("cce-status"));
+ let is_wallpaper = app_id.as_deref() == Some("cce-wallpaper");
- if is_status_bar {
+ if is_wallpaper {
(*win_ptr).tiling_mode = crate::tiling::TilingMode::Status;
- 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.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;
+ (*win_ptr).rendering_requested.blur = false;
+ (*win_ptr).rendering_requested.x = phys_x;
+ (*win_ptr).rendering_requested.y = phys_y;
(*win_ptr).wm_requested.dimensions = Some(crate::window::Dimensions {
- width: wlr_box.width as u32,
- height: bar_h,
+ width: phys_w as u32,
+ height: phys_h as u32,
});
(*win_ptr).wm_requested.bounds = crate::window::Dimensions {
- width: wlr_box.width as u32,
- height: bar_h,
+ width: phys_w as u32,
+ height: phys_h as u32,
};
+ continue;
+ }
+
+ if is_status_bar {
+ (*win_ptr).tiling_mode = crate::tiling::TilingMode::Status;
(*win_ptr).wm_requested.tiled = 0;
(*win_ptr).wm_requested.ssd = false;
(*win_ptr).scale = 1.0;
@@ -1361,6 +1382,101 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
if !self.layout.window_opacity { 1.0f32 } else { 0.90f32 }
};
}
+
+ // Position status bar windows on this output
+ let mut left_status = Vec::new();
+ let mut right_status = Vec::new();
+ let mut full_status = Vec::new();
+
+ for &win_ptr in self.windows.iter() {
+ if win_ptr.is_null() || (*win_ptr).closed {
+ continue;
+ }
+ if matches!((*win_ptr).state, crate::window::WindowState::Closing | crate::window::WindowState::Init) {
+ continue;
+ }
+ if let Some(app_id) = (*win_ptr).get_app_id_string() {
+ if app_id.starts_with("cce-status-left-") {
+ left_status.push(win_ptr);
+ } else if app_id.starts_with("cce-status-right-") {
+ right_status.push(win_ptr);
+ } else if app_id.starts_with("cce-status") {
+ full_status.push(win_ptr);
+ }
+ }
+ }
+
+ const LEFT_ORDER: &[&str] = &["viewport", "window"];
+ const RIGHT_ORDER: &[&str] = &["tray", "cpu", "memory", "brightness", "volume", "battery", "clock"];
+
+ left_status.sort_by_key(|&w| unsafe {
+ let app_id = (*w).get_app_id_string().unwrap_or_default();
+ let name = app_id.trim_start_matches("cce-status-left-");
+ LEFT_ORDER.iter().position(|&m| m == name).unwrap_or(99)
+ });
+
+ right_status.sort_by_key(|&w| unsafe {
+ let app_id = (*w).get_app_id_string().unwrap_or_default();
+ let name = app_id.trim_start_matches("cce-status-right-");
+ RIGHT_ORDER.iter().position(|&m| m == name).unwrap_or(99)
+ });
+
+ let bar_h = self.layout.bar_height as u32;
+ let spacing = 12;
+ let margin = 12;
+
+ // Layout Left status windows
+ let mut cur_left_x = wlr_box.x + margin;
+ for win_ptr in left_status {
+ let w = if (*win_ptr).box_geom.width > 0 { (*win_ptr).box_geom.width as u32 } else { 100 };
+ let app_id = (*win_ptr).get_app_id_string().unwrap_or_default();
+ log::info!("[ArrangeStatus] Left module app_id={} x={} y={} w={}", app_id, cur_left_x, wlr_box.y, w);
+ (*win_ptr).rendering_requested.x = cur_left_x;
+ (*win_ptr).rendering_requested.y = wlr_box.y;
+ (*win_ptr).wm_requested.dimensions = Some(crate::window::Dimensions {
+ width: w,
+ height: bar_h,
+ });
+ (*win_ptr).wm_requested.bounds = crate::window::Dimensions {
+ width: w,
+ height: bar_h,
+ };
+ cur_left_x += w as i32 + spacing;
+ }
+
+ // Layout Right status windows (right-to-left)
+ let mut cur_right_x = wlr_box.x + wlr_box.width - margin;
+ for win_ptr in right_status.into_iter().rev() {
+ let w = if (*win_ptr).box_geom.width > 0 { (*win_ptr).box_geom.width as u32 } else { 100 };
+ let x = cur_right_x - w as i32;
+ let app_id = (*win_ptr).get_app_id_string().unwrap_or_default();
+ log::info!("[ArrangeStatus] Right module app_id={} x={} y={} w={}", app_id, x, wlr_box.y, w);
+ (*win_ptr).rendering_requested.x = x;
+ (*win_ptr).rendering_requested.y = wlr_box.y;
+ (*win_ptr).wm_requested.dimensions = Some(crate::window::Dimensions {
+ width: w,
+ height: bar_h,
+ });
+ (*win_ptr).wm_requested.bounds = crate::window::Dimensions {
+ width: w,
+ height: bar_h,
+ };
+ cur_right_x = x - spacing;
+ }
+
+ // Layout Full/Legacy status windows
+ for win_ptr in full_status {
+ (*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,
+ };
+ }
}
// If the focused window is no longer visible, refocus
let seats_list = &mut (*self.server).input_manager.seats as *mut ffi::wl_list as *mut WlList;
@@ -1526,7 +1642,8 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
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().map_or(false, |id| id.starts_with("cce-status"));
- if !is_status_bar {
+ let is_wallpaper = app_id.as_deref() == Some("cce-wallpaper");
+ if !is_status_bar && !is_wallpaper {
next_focus = w;
break;
}
@@ -1537,7 +1654,8 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
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().map_or(false, |id| id.starts_with("cce-status"));
- if !is_status_bar {
+ let is_wallpaper = app_id.as_deref() == Some("cce-wallpaper");
+ if !is_status_bar && !is_wallpaper {
next_focus = w;
}
}
@@ -1995,7 +2113,8 @@ 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().map_or(false, |id| id.starts_with("cce-status"));
- if is_status_bar {
+ let is_wallpaper = app_id.as_deref() == Some("cce-wallpaper");
+ if is_status_bar || is_wallpaper {
continue;
}
@@ -2415,6 +2534,14 @@ fn get_closest_tag(x: f64, y: f64) -> i32 {
self.layout.desktop_cell_fade_inset = v;
}
}
+ "desktop_enable_solid_color" => {
+ if let Ok(v) = val.parse::<bool>() {
+ self.layout.desktop_enable_solid_color = v;
+ }
+ }
+ "desktop_solid_color" => {
+ self.layout.desktop_solid_color = crate::config::parse_hex_color_rgba(val);
+ }
"gap" => { if let Ok(v) = val.parse::<i32>() { self.layout.gap = v; } }
"gap_top" => { if let Ok(v) = val.parse::<i32>() { self.layout.gap_top = v; } }
"gap_left" => { if let Ok(v) = val.parse::<i32>() { self.layout.gap_left = v; } }
diff --git a/src/server/wlroots_log_wrapper.c b/src/server/wlroots_log_wrapper.c
index 90e5734..9ea316d 100644
--- a/src/server/wlroots_log_wrapper.c
+++ b/src/server/wlroots_log_wrapper.c
@@ -655,7 +655,17 @@ void river_wlr_keyboard_init(struct wlr_keyboard *keyboard,
static struct wlr_scene_node *find_blur_node(struct wlr_scene_tree *tree) {
struct wlr_scene_node *child;
wl_list_for_each(child, &tree->children, link) {
- if (child->type == WLR_SCENE_NODE_BLUR || child->type == WLR_SCENE_NODE_OPTIMIZED_BLUR) {
+ if (child->type == WLR_SCENE_NODE_BLUR) {
+ return child;
+ }
+ }
+ return NULL;
+}
+
+static struct wlr_scene_node *find_optimized_blur_node(struct wlr_scene_tree *tree) {
+ struct wlr_scene_node *child;
+ wl_list_for_each(child, &tree->children, link) {
+ if (child->type == WLR_SCENE_NODE_OPTIMIZED_BLUR) {
return child;
}
}
@@ -676,11 +686,15 @@ void river_scene_node_enable_blur(struct wlr_scene_node *node, bool enabled, boo
return;
}
struct wlr_scene_tree *tree = wlr_scene_tree_from_node(node);
- struct wlr_scene_node *blur_node = find_blur_node(tree);
+ struct wlr_scene_node *opt_blur_node = find_optimized_blur_node(tree);
+ struct wlr_scene_node *std_blur_node = find_blur_node(tree);
if (!enabled) {
- if (blur_node) {
- wlr_scene_node_destroy(blur_node);
+ if (opt_blur_node) {
+ wlr_scene_node_destroy(opt_blur_node);
+ }
+ if (std_blur_node) {
+ wlr_scene_node_destroy(std_blur_node);
}
return;
}
@@ -695,42 +709,55 @@ void river_scene_node_enable_blur(struct wlr_scene_node *node, bool enabled, boo
}
if (width <= 0 || height <= 0) {
- width = 1920; // Fallback defaults
- height = 1080;
- }
-
- enum wlr_scene_node_type expected_type = optimized ? WLR_SCENE_NODE_OPTIMIZED_BLUR : WLR_SCENE_NODE_BLUR;
- if (blur_node && blur_node->type != expected_type) {
- wlr_scene_node_destroy(blur_node);
- blur_node = NULL;
+ if (opt_blur_node) {
+ wlr_scene_node_destroy(opt_blur_node);
+ }
+ if (std_blur_node) {
+ wlr_scene_node_destroy(std_blur_node);
+ }
+ return;
}
- if (!blur_node) {
- if (optimized) {
+ if (optimized) {
+ if (!opt_blur_node) {
struct wlr_scene_optimized_blur *opt_blur = wlr_scene_optimized_blur_create(tree, width, height);
if (opt_blur) {
- blur_node = &opt_blur->node;
+ opt_blur_node = &opt_blur->node;
}
} else {
- struct wlr_scene_blur *std_blur = wlr_scene_blur_create(tree, width, height);
- if (std_blur) {
- wlr_scene_blur_set_should_only_blur_bottom_layer(std_blur, true);
- blur_node = &std_blur->node;
- }
+ wlr_scene_optimized_blur_set_size((struct wlr_scene_optimized_blur *)opt_blur_node, width, height);
}
- if (blur_node) {
- wlr_scene_node_lower_to_bottom(blur_node);
+ if (opt_blur_node) {
+ wlr_scene_node_set_position(opt_blur_node, x, y);
}
} else {
- if (optimized) {
- wlr_scene_optimized_blur_set_size((struct wlr_scene_optimized_blur *)blur_node, width, height);
- } else {
- wlr_scene_blur_set_size((struct wlr_scene_blur *)blur_node, width, height);
+ if (opt_blur_node) {
+ wlr_scene_node_destroy(opt_blur_node);
+ opt_blur_node = NULL;
}
}
- if (blur_node) {
- wlr_scene_node_set_position(blur_node, x, y);
+ if (!std_blur_node) {
+ struct wlr_scene_blur *std_blur = wlr_scene_blur_create(tree, width, height);
+ if (std_blur) {
+ std_blur_node = &std_blur->node;
+ wlr_scene_blur_set_should_only_blur_bottom_layer(std_blur, optimized);
+ }
+ } else {
+ wlr_scene_blur_set_should_only_blur_bottom_layer((struct wlr_scene_blur *)std_blur_node, optimized);
+ wlr_scene_blur_set_size((struct wlr_scene_blur *)std_blur_node, width, height);
+ }
+
+ if (std_blur_node) {
+ wlr_scene_node_set_position(std_blur_node, x, y);
+ }
+
+ // Ensure correct stack order (from back to front): opt_blur_node -> std_blur_node -> window content
+ if (std_blur_node) {
+ wlr_scene_node_lower_to_bottom(std_blur_node);
+ }
+ if (opt_blur_node) {
+ wlr_scene_node_lower_to_bottom(opt_blur_node);
}
}
diff --git a/src/server/xdg_toplevel.rs b/src/server/xdg_toplevel.rs
index 43bbe9b..3fda19f 100644
--- a/src/server/xdg_toplevel.rs
+++ b/src/server/xdg_toplevel.rs
@@ -414,17 +414,15 @@ unsafe extern "C" fn handle_commit(listener: *mut ffi::wl_listener, _data: *mut
ignore_transparent = (*(*window).server).wm.layout.status_backdrop_blur_ignore_transparent;
}
let scale = (*window).scale;
- let geom_x = ((*window).rendering_requested.x as f64 * scale) as i32;
- let geom_y = ((*window).rendering_requested.y as f64 * scale) as i32;
let geom_w = ((*window).rendering_sent.width as f64 * scale) as i32;
let geom_h = ((*window).rendering_sent.height as f64 * scale) as i32;
ffi::river_scene_node_enable_blur(
- (*window).surfaces.tree as *mut ffi::wlr_scene_node,
+ (*window).tree as *mut ffi::wlr_scene_node,
(*window).rendering_requested.blur,
(*(*window).server).wm.layout.scenefx_optimized_blur,
ignore_transparent,
- geom_x,
- geom_y,
+ 0,
+ 0,
geom_w,
geom_h,
);
@@ -454,7 +452,7 @@ unsafe extern "C" fn handle_commit(listener: *mut ffi::wl_listener, _data: *mut
return;
}
- if (*window).state != crate::window::WindowState::Mapped {
+ if (*window).state != crate::window::WindowState::Mapped && (*window).state != crate::window::WindowState::Ready {
return;
}
@@ -472,10 +470,15 @@ unsafe extern "C" fn handle_commit(listener: *mut ffi::wl_listener, _data: *mut
"client initiated size change: {}x{} -> {}x{}",
old_geometry.width, old_geometry.height, new_geometry.width, new_geometry.height
);
- if matches!((*window).tiling_mode, crate::tiling::TilingMode::Floating | crate::tiling::TilingMode::Popup) {
+ let is_status = (*window).tiling_mode == crate::tiling::TilingMode::Status ||
+ (*window).get_app_id_string().map_or(false, |id| id.starts_with("cce-status"));
+ if matches!((*window).tiling_mode, crate::tiling::TilingMode::Floating | crate::tiling::TilingMode::Popup) || is_status {
(*window).set_dimensions(new_geometry.width as u32, new_geometry.height as u32);
(*window).configure_sent.width = Some(new_geometry.width as u32);
(*window).configure_sent.height = Some(new_geometry.height as u32);
+ if is_status {
+ (*(*window).server).wm.dirty_windowing();
+ }
} else {
(*window).render_finish();
}
@@ -494,6 +497,15 @@ unsafe extern "C" fn handle_commit(listener: *mut ffi::wl_listener, _data: *mut
(*window).rendering_scheduled.width = new_geometry.width as u32;
(*window).rendering_scheduled.height = new_geometry.height as u32;
+ let is_status = (*window).tiling_mode == crate::tiling::TilingMode::Status ||
+ (*window).get_app_id_string().map_or(false, |id| id.starts_with("cce-status"));
+ if matches!((*window).tiling_mode, crate::tiling::TilingMode::Floating | crate::tiling::TilingMode::Popup) || is_status {
+ (*window).set_dimensions(new_geometry.width as u32, new_geometry.height as u32);
+ if is_status {
+ (*(*window).server).wm.dirty_windowing();
+ }
+ }
+
let (dec_w, dec_h) = (*window).get_decorations_size();
if dec_w != (*window).last_decor_w || dec_h != (*window).last_decor_h {
(*window).last_decor_w = dec_w;