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
|
||||
/// connection, the calloop event loop, or one of the protocol bindings
|
||||
/// the runtime cannot operate without (`wl_compositor`, `wl_shm`,
|
||||
/// `xdg_wm_base`). Once init succeeds, runtime errors during the dispatch
|
||||
/// loop still panic — they are non-recoverable from the caller's point
|
||||
/// of view since the surface is already on screen.
|
||||
/// `xdg_wm_base`). Once init succeeds, the compositor disconnecting
|
||||
/// (`BrokenPipe` / `ConnectionReset`) is treated as a clean exit; any
|
||||
/// 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
|
||||
/// 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
|
||||
// fires on time.
|
||||
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`
|
||||
// emits its stored message and flips into drag mode for the rest
|
||||
// of the gesture.
|
||||
|
||||
@@ -162,6 +162,8 @@ pub struct LauncherSpec
|
||||
#[ derive( Debug, Clone, Copy ) ]
|
||||
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.
|
||||
pub icon: Color,
|
||||
/// Button background while hovered.
|
||||
@@ -972,6 +974,7 @@ pub ( super ) fn default_window_controls( palette: Palette ) -> WindowControlsSp
|
||||
{
|
||||
WindowControlsSpec
|
||||
{
|
||||
bar_bg: Color { a: 1.0, ..palette.surface },
|
||||
icon: palette.icon,
|
||||
hover_bg: palette.surface_alt,
|
||||
pressed_bg: palette.divider,
|
||||
|
||||
@@ -740,6 +740,7 @@ struct RawLauncher
|
||||
#[ serde( deny_unknown_fields ) ]
|
||||
struct RawWindowControls
|
||||
{
|
||||
#[ serde( default ) ] bar_bg: Option<String>,
|
||||
#[ serde( default ) ] icon: Option<String>,
|
||||
#[ serde( default ) ] hover_bg: Option<String>,
|
||||
#[ serde( default ) ] pressed_bg: Option<String>,
|
||||
@@ -814,6 +815,7 @@ fn window_controls_from_raw
|
||||
Some( p ) => default_window_controls( *p ),
|
||||
None => WindowControlsSpec
|
||||
{
|
||||
bar_bg: Color::WHITE,
|
||||
icon: Color::BLACK,
|
||||
hover_bg: Color::rgba( 0.0, 0.0, 0.0, 0.08 ),
|
||||
pressed_bg: Color::rgba( 0.0, 0.0, 0.0, 0.12 ),
|
||||
@@ -824,6 +826,7 @@ fn window_controls_from_raw
|
||||
};
|
||||
Ok( WindowControlsSpec
|
||||
{
|
||||
bar_bg: parse_opt( raw.bar_bg.as_deref(), fallback.bar_bg )?,
|
||||
icon: parse_opt( raw.icon.as_deref(), fallback.icon )?,
|
||||
hover_bg: parse_opt( raw.hover_bg.as_deref(), fallback.hover_bg )?,
|
||||
pressed_bg: parse_opt( raw.pressed_bg.as_deref(), fallback.pressed_bg )?,
|
||||
|
||||
Reference in New Issue
Block a user