feat: add modern shell top bar
This commit is contained in:
1 parent
e511c0c31d
commit
4a6d91980c
9 files changed
+782
-20
No files matched your search
+50
-11
@@ -17,7 +17,13 @@ mod state;
|
||||
mod window_rules;
|
||||
mod winit;
|
||||
|
||||
use std::{error::Error, io, process::Command};
|
||||
use std::{
|
||||
error::Error,
|
||||
io,
|
||||
process::{Child, Command},
|
||||
thread,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use calloop::signals::{Signal, Signals};
|
||||
use smithay::reexports::{calloop::EventLoop, wayland_server::Display};
|
||||
@@ -70,9 +76,14 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
unsafe { std::env::set_var("WAYLAND_DISPLAY", &state.socket_name) };
|
||||
|
||||
info!(socket = ?state.socket_name, backend = ?launch.backend, "Ferese is accepting Wayland clients");
|
||||
spawn_client(&mut state, launch.client, launch.client_capabilities);
|
||||
event_loop.run(None, &mut state, |_| {})?;
|
||||
Ok(())
|
||||
let mut child = spawn_client(&mut state, launch.client, launch.client_capabilities);
|
||||
let result = event_loop.run(None, &mut state, |_| {});
|
||||
|
||||
if let Some(child) = &mut child {
|
||||
terminate_child(child);
|
||||
}
|
||||
|
||||
result.map_err(Into::into)
|
||||
}
|
||||
|
||||
fn init_logging() {
|
||||
@@ -85,10 +96,10 @@ fn spawn_client(
|
||||
state: &mut Ferese,
|
||||
mut args: Vec<std::ffi::OsString>,
|
||||
capabilities: private_client::ClientCapabilities,
|
||||
) {
|
||||
) -> Option<Child> {
|
||||
if args.is_empty() {
|
||||
info!("no client requested; pass one after `--`, for example `-- foot`");
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
let program = args.remove(0);
|
||||
|
||||
@@ -101,14 +112,42 @@ fn spawn_client(
|
||||
Ok(connection) => Some(connection),
|
||||
Err(error) => {
|
||||
warn!(program = ?program, %error, "failed to create private Wayland connection");
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match command.spawn() {
|
||||
Ok(child) => info!(program = ?program, pid = child.id(), "spawned Wayland client"),
|
||||
Err(error) => warn!(program = ?program, %error, "failed to spawn Wayland client"),
|
||||
}
|
||||
let child = match command.spawn() {
|
||||
Ok(child) => {
|
||||
info!(program = ?program, pid = child.id(), "spawned Wayland client");
|
||||
Some(child)
|
||||
}
|
||||
Err(error) => {
|
||||
warn!(program = ?program, %error, "failed to spawn Wayland client");
|
||||
None
|
||||
}
|
||||
};
|
||||
drop(private_connection);
|
||||
child
|
||||
}
|
||||
|
||||
fn terminate_child(child: &mut Child) {
|
||||
if child.try_wait().ok().flatten().is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
// SAFETY: the process ID comes from the live Child owned by Ferese.
|
||||
unsafe {
|
||||
libc::kill(child.id() as i32, libc::SIGTERM);
|
||||
}
|
||||
|
||||
for _ in 0..20 {
|
||||
if child.try_wait().ok().flatten().is_some() {
|
||||
return;
|
||||
}
|
||||
thread::sleep(Duration::from_millis(10));
|
||||
}
|
||||
|
||||
let _ = child.kill();
|
||||
let _ = child.wait();
|
||||
}
|
||||
+68
-8
@@ -1371,13 +1371,15 @@ fn framebuffer_clip_rect(
|
||||
output_size: smithay::utils::Size<i32, Physical>,
|
||||
transform: Transform,
|
||||
) -> [f32; 4] {
|
||||
let transformed = transform.transform_rect_in(geometry, &output_size);
|
||||
let transformed_output_size = transform.transform_size(output_size);
|
||||
let bottom = transformed_output_size.h - transformed.loc.y - transformed.size.h;
|
||||
// Match GlesFrame's projection: Smithay already accounts for GL's Y axis.
|
||||
// An extra bottom-left conversion here mirrors shader clips independently
|
||||
// of the surface geometry (notably with the nested Flipped180 output).
|
||||
let element_size = transform.transform_size(output_size);
|
||||
let transformed = transform.transform_rect_in(geometry, &element_size);
|
||||
|
||||
[
|
||||
transformed.loc.x as f32,
|
||||
bottom as f32,
|
||||
transformed.loc.y as f32,
|
||||
transformed.size.w as f32,
|
||||
transformed.size.h as f32,
|
||||
]
|
||||
@@ -1861,12 +1863,12 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn clip_rect_uses_opengl_bottom_left_origin() {
|
||||
fn clip_rect_matches_smithay_gles_projection() {
|
||||
let geometry = Rectangle::<i32, Physical>::new((10, 5).into(), (30, 40).into());
|
||||
|
||||
assert_eq!(
|
||||
framebuffer_clip_rect(geometry, (100, 80).into(), Transform::Normal),
|
||||
[10.0, 35.0, 30.0, 40.0]
|
||||
[10.0, 5.0, 30.0, 40.0]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1876,14 +1878,72 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
framebuffer_clip_rect(geometry, (100, 80).into(), Transform::Flipped180),
|
||||
[10.0, 5.0, 30.0, 40.0]
|
||||
[10.0, 35.0, 30.0, 40.0]
|
||||
);
|
||||
assert_eq!(
|
||||
framebuffer_clip_rect(geometry, (100, 80).into(), Transform::_180),
|
||||
[60.0, 5.0, 30.0, 40.0]
|
||||
[60.0, 35.0, 30.0, 40.0]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn panel_clearance_clip_matches_gles_projection_at_fractional_scales() {
|
||||
for scale in [1.0, 1.25, 1.75, 2.0] {
|
||||
for transform in [
|
||||
Transform::Normal,
|
||||
Transform::_90,
|
||||
Transform::_180,
|
||||
Transform::_270,
|
||||
Transform::Flipped,
|
||||
Transform::Flipped90,
|
||||
Transform::Flipped180,
|
||||
Transform::Flipped270,
|
||||
] {
|
||||
let output_size = smithay::utils::Size::<i32, Physical>::from((
|
||||
(1280.0 * scale) as i32,
|
||||
(800.0 * scale) as i32,
|
||||
));
|
||||
let element_size = transform.transform_size(output_size);
|
||||
let logical_height = f64::from(element_size.h) / scale;
|
||||
// Bar and exclusion end at 40; the 10px outer gap ends at 50.
|
||||
let geometry = Rectangle::<f64, Logical>::new(
|
||||
(10.0, 50.0).into(),
|
||||
(400.0, logical_height - 60.0).into(),
|
||||
)
|
||||
.to_physical_precise_round(scale);
|
||||
let clip = framebuffer_clip_rect(geometry, output_size, transform);
|
||||
// Independently reproduce GlesFrame's orthographic projection,
|
||||
// transform matrix, GL flip, and viewport mapping for each corner.
|
||||
let matrix = transform.matrix();
|
||||
let mut xs = Vec::new();
|
||||
let mut ys = Vec::new();
|
||||
for x in [geometry.loc.x, geometry.loc.x + geometry.size.w] {
|
||||
for y in [geometry.loc.y, geometry.loc.y + geometry.size.h] {
|
||||
let nx = 2.0 * x as f32 / element_size.w as f32 - 1.0;
|
||||
let ny = 1.0 - 2.0 * y as f32 / element_size.h as f32;
|
||||
let tx = matrix[0][0] * nx + matrix[1][0] * ny;
|
||||
let ty = matrix[0][1] * nx + matrix[1][1] * ny;
|
||||
xs.push((tx + 1.0) * output_size.w as f32 / 2.0);
|
||||
ys.push((1.0 - ty) * output_size.h as f32 / 2.0);
|
||||
}
|
||||
}
|
||||
let min_x = xs.iter().copied().fold(f32::INFINITY, f32::min);
|
||||
let max_x = xs.iter().copied().fold(f32::NEG_INFINITY, f32::max);
|
||||
let min_y = ys.iter().copied().fold(f32::INFINITY, f32::min);
|
||||
let max_y = ys.iter().copied().fold(f32::NEG_INFINITY, f32::max);
|
||||
for (actual, expected) in
|
||||
clip.into_iter()
|
||||
.zip([min_x, min_y, max_x - min_x, max_y - min_y])
|
||||
{
|
||||
assert!(
|
||||
(actual - expected).abs() < 0.001,
|
||||
"{transform:?}, scale={scale}: {actual} != {expected}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blur_sampling_expands_around_the_visible_surface() {
|
||||
let visible = Rectangle::<i32, Logical>::new((100, 80).into(), (400, 300).into());
|
||||
|
||||
Reference in new issue
Block a user