theme, event_loop: window_controls.bar_bg slot + graceful exit on compositor disconnect
`WindowControlsSpec` gains `bar_bg`, the background fill of the SSD title-bar strip the close / maximize / minimize controls sit on. Forge used to paint that strip with `palette.surface` clamped to alpha 1.0 — a hack on a translucent panel token. With a dedicated slot the theme decides directly: `@off-white` in light and a new `@window-bar-dark` shade in dark. Schema gets the matching `Option<String>` field (defaulting to `palette.surface` to keep existing themes rendering the same) and the fallback `WindowControlsSpec` seeds `Color::WHITE` so a missing theme still draws something readable.
`try_run`'s dispatch loop previously `.expect("dispatch")`-ed every calloop error. When the compositor closes the wayland socket — `wl_display.error`, forge crashing, the user logging out — the loop saw a `BrokenPipe` / `ConnectionReset` and panicked, which polluted the user's stderr with a backtrace for what is just "the session ended". The match now treats those two `IoError` kinds as a clean shutdown: it prints `Connection::protocol_error()` (the typed `wl_display.error` if the server sent one), sets `exit_requested = true`, and lets the loop drain out normally. Any other dispatch error keeps panicking — those are still genuine bugs.
This commit is contained in:
@@ -48,9 +48,11 @@ use std::collections::HashSet;
|
|||||||
/// Every variant maps to a fatal failure during init: the Wayland
|
/// Every variant maps to a fatal failure during init: the Wayland
|
||||||
/// connection, the calloop event loop, or one of the protocol bindings
|
/// connection, the calloop event loop, or one of the protocol bindings
|
||||||
/// the runtime cannot operate without (`wl_compositor`, `wl_shm`,
|
/// the runtime cannot operate without (`wl_compositor`, `wl_shm`,
|
||||||
/// `xdg_wm_base`). Once init succeeds, runtime errors during the dispatch
|
/// `xdg_wm_base`). Once init succeeds, the compositor disconnecting
|
||||||
/// loop still panic — they are non-recoverable from the caller's point
|
/// (`BrokenPipe` / `ConnectionReset`) is treated as a clean exit; any
|
||||||
/// of view since the surface is already on screen.
|
/// other runtime error during the dispatch loop still panics, since the
|
||||||
|
/// surface is already on screen and the state machine cannot be unwound
|
||||||
|
/// from this entry point.
|
||||||
///
|
///
|
||||||
/// Embedders that want a software-rendered fallback or that need to
|
/// Embedders that want a software-rendered fallback or that need to
|
||||||
/// degrade gracefully should call [`crate::try_run`] and match on the
|
/// degrade gracefully should call [`crate::try_run`] and match on the
|
||||||
@@ -373,7 +375,31 @@ pub( crate ) fn try_run<A: App>( app: A ) -> Result<(), RunError>
|
|||||||
// cap the wait at its deadline so a perfectly still press still
|
// cap the wait at its deadline so a perfectly still press still
|
||||||
// fires on time.
|
// fires on time.
|
||||||
let timeout = data.next_long_press_wakeup();
|
let timeout = data.next_long_press_wakeup();
|
||||||
event_loop.dispatch( timeout, &mut data ).expect( "dispatch" );
|
match event_loop.dispatch( timeout, &mut data )
|
||||||
|
{
|
||||||
|
Ok( () ) => {},
|
||||||
|
Err( calloop::Error::IoError( ref e ) )
|
||||||
|
if matches!( e.kind(), std::io::ErrorKind::BrokenPipe | std::io::ErrorKind::ConnectionReset ) =>
|
||||||
|
{
|
||||||
|
// On a fatal `wl_display.error` the compositor closes the
|
||||||
|
// socket; the backend has the typed error stashed but the
|
||||||
|
// dispatch result only carries the resulting `BrokenPipe`.
|
||||||
|
if let Some( pe ) = conn.protocol_error()
|
||||||
|
{
|
||||||
|
eprintln!(
|
||||||
|
"ltk: wayland protocol error — interface={} object_id={} code={}: {}",
|
||||||
|
pe.object_interface, pe.object_id, pe.code, pe.message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eprintln!( "ltk: wayland connection lost ({e}); exiting" );
|
||||||
|
}
|
||||||
|
data.exit_requested = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Err( e ) => panic!( "dispatch: {e:?}" ),
|
||||||
|
}
|
||||||
// Any surface whose press has now crossed `long_press_duration`
|
// Any surface whose press has now crossed `long_press_duration`
|
||||||
// emits its stored message and flips into drag mode for the rest
|
// emits its stored message and flips into drag mode for the rest
|
||||||
// of the gesture.
|
// of the gesture.
|
||||||
|
|||||||
@@ -162,6 +162,8 @@ pub struct LauncherSpec
|
|||||||
#[ derive( Debug, Clone, Copy ) ]
|
#[ derive( Debug, Clone, Copy ) ]
|
||||||
pub struct WindowControlsSpec
|
pub struct WindowControlsSpec
|
||||||
{
|
{
|
||||||
|
/// Background fill of the SSD title bar strip the controls sit on.
|
||||||
|
pub bar_bg: Color,
|
||||||
/// Symbol colour for minimize / maximize / restore / close glyphs.
|
/// Symbol colour for minimize / maximize / restore / close glyphs.
|
||||||
pub icon: Color,
|
pub icon: Color,
|
||||||
/// Button background while hovered.
|
/// Button background while hovered.
|
||||||
@@ -972,6 +974,7 @@ pub ( super ) fn default_window_controls( palette: Palette ) -> WindowControlsSp
|
|||||||
{
|
{
|
||||||
WindowControlsSpec
|
WindowControlsSpec
|
||||||
{
|
{
|
||||||
|
bar_bg: Color { a: 1.0, ..palette.surface },
|
||||||
icon: palette.icon,
|
icon: palette.icon,
|
||||||
hover_bg: palette.surface_alt,
|
hover_bg: palette.surface_alt,
|
||||||
pressed_bg: palette.divider,
|
pressed_bg: palette.divider,
|
||||||
|
|||||||
@@ -740,6 +740,7 @@ struct RawLauncher
|
|||||||
#[ serde( deny_unknown_fields ) ]
|
#[ serde( deny_unknown_fields ) ]
|
||||||
struct RawWindowControls
|
struct RawWindowControls
|
||||||
{
|
{
|
||||||
|
#[ serde( default ) ] bar_bg: Option<String>,
|
||||||
#[ serde( default ) ] icon: Option<String>,
|
#[ serde( default ) ] icon: Option<String>,
|
||||||
#[ serde( default ) ] hover_bg: Option<String>,
|
#[ serde( default ) ] hover_bg: Option<String>,
|
||||||
#[ serde( default ) ] pressed_bg: Option<String>,
|
#[ serde( default ) ] pressed_bg: Option<String>,
|
||||||
@@ -814,6 +815,7 @@ fn window_controls_from_raw
|
|||||||
Some( p ) => default_window_controls( *p ),
|
Some( p ) => default_window_controls( *p ),
|
||||||
None => WindowControlsSpec
|
None => WindowControlsSpec
|
||||||
{
|
{
|
||||||
|
bar_bg: Color::WHITE,
|
||||||
icon: Color::BLACK,
|
icon: Color::BLACK,
|
||||||
hover_bg: Color::rgba( 0.0, 0.0, 0.0, 0.08 ),
|
hover_bg: Color::rgba( 0.0, 0.0, 0.0, 0.08 ),
|
||||||
pressed_bg: Color::rgba( 0.0, 0.0, 0.0, 0.12 ),
|
pressed_bg: Color::rgba( 0.0, 0.0, 0.0, 0.12 ),
|
||||||
@@ -824,6 +826,7 @@ fn window_controls_from_raw
|
|||||||
};
|
};
|
||||||
Ok( WindowControlsSpec
|
Ok( WindowControlsSpec
|
||||||
{
|
{
|
||||||
|
bar_bg: parse_opt( raw.bar_bg.as_deref(), fallback.bar_bg )?,
|
||||||
icon: parse_opt( raw.icon.as_deref(), fallback.icon )?,
|
icon: parse_opt( raw.icon.as_deref(), fallback.icon )?,
|
||||||
hover_bg: parse_opt( raw.hover_bg.as_deref(), fallback.hover_bg )?,
|
hover_bg: parse_opt( raw.hover_bg.as_deref(), fallback.hover_bg )?,
|
||||||
pressed_bg: parse_opt( raw.pressed_bg.as_deref(), fallback.pressed_bg )?,
|
pressed_bg: parse_opt( raw.pressed_bg.as_deref(), fallback.pressed_bg )?,
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
"navy": "#0A032E",
|
"navy": "#0A032E",
|
||||||
"indigo": "#221A5A",
|
"indigo": "#221A5A",
|
||||||
"midnight": "#181045",
|
"midnight": "#181045",
|
||||||
|
"window-bar-dark": "#2B2838",
|
||||||
"white": "#FFFFFF",
|
"white": "#FFFFFF",
|
||||||
"off-white": "#EAE9EB",
|
"off-white": "#EAE9EB",
|
||||||
"sky": "#2AA7F7",
|
"sky": "#2AA7F7",
|
||||||
@@ -93,6 +94,7 @@
|
|||||||
"light": {
|
"light": {
|
||||||
"launcher": { "background": "@white/E6", "border_radius": 24.0 },
|
"launcher": { "background": "@white/E6", "border_radius": 24.0 },
|
||||||
"window_controls": {
|
"window_controls": {
|
||||||
|
"bar_bg": "@off-white",
|
||||||
"icon": "@slate",
|
"icon": "@slate",
|
||||||
"hover_bg": "@navy/14",
|
"hover_bg": "@navy/14",
|
||||||
"pressed_bg": "@navy/24",
|
"pressed_bg": "@navy/24",
|
||||||
@@ -206,6 +208,7 @@
|
|||||||
"dark": {
|
"dark": {
|
||||||
"launcher": { "background": "@indigo/E6", "border_radius": 24.0 },
|
"launcher": { "background": "@indigo/E6", "border_radius": 24.0 },
|
||||||
"window_controls": {
|
"window_controls": {
|
||||||
|
"bar_bg": "@window-bar-dark",
|
||||||
"icon": "@silver",
|
"icon": "@silver",
|
||||||
"hover_bg": "@white/18",
|
"hover_bg": "@white/18",
|
||||||
"pressed_bg": "@white/26",
|
"pressed_bg": "@white/26",
|
||||||
|
|||||||
Reference in New Issue
Block a user