clone
ssh://git@git.notashelf.dev:33/notashelf/beer.gitCommit e172d4fbb3fc
tarballunverified4 files changed+107-0
@@ -39,6 +39,11 @@ channel). built-in set (whitespace and common punctuation, keeping _.-/:~\__ inside words). +*idle-inhibit* = _bool_+ While the window is focused, ask the compositor not to blank the screen or+ start the screensaver (idle-inhibit-v1). A backgrounded window stops+ inhibiting. Default _false_.+ # [colors] *foreground* = _color_, *background* = _color_@@ -154,6 +154,9 @@ pub struct Main { /// Characters that break a word for double-click selection. Empty/unset /// keeps the built-in default. pub word_delimiters: Option<String>,+ /// Hold an idle inhibitor while the window is focused, so the compositor+ /// does not blank the screen or start the screensaver. Default off.+ pub idle_inhibit: bool, } impl Default for Main {@@ -167,6 +170,7 @@ impl Default for Main { pad_x: 2, pad_y: 2, word_delimiters: None,+ idle_inhibit: false, } } }@@ -70,6 +70,7 @@ impl WindowHandler for App { } } self.focused = configure.is_activated();+ self.sync_idle_inhibit(); if self.session.is_none() { self.spawn_session(); } else {@@ -188,6 +189,7 @@ impl KeyboardHandler for App { self.activate_keyboard(keyboard); self.serial = serial; self.focused = true;+ self.sync_idle_inhibit(); self.report_focus(true); self.needs_draw = true; }@@ -201,6 +203,7 @@ impl KeyboardHandler for App { _: u32, ) { self.focused = false;+ self.sync_idle_inhibit(); // Drop held-key state so a key released while unfocused can't leak a // stale kitty release event later. self.keys_down.clear();@@ -634,6 +637,56 @@ impl Dispatch<WpViewport, ()> for App { } } +// idle-inhibit and content-type are likewise raw protocol objects; none of them+// emit events we act on.+impl Dispatch<ZwpIdleInhibitManagerV1, ()> for App {+ fn event(+ _: &mut Self,+ _: &ZwpIdleInhibitManagerV1,+ _: <ZwpIdleInhibitManagerV1 as Proxy>::Event,+ _: &(),+ _: &Connection,+ _: &QueueHandle<Self>,+ ) {+ }+}++impl Dispatch<ZwpIdleInhibitorV1, ()> for App {+ fn event(+ _: &mut Self,+ _: &ZwpIdleInhibitorV1,+ _: zwp_idle_inhibitor_v1::Event,+ _: &(),+ _: &Connection,+ _: &QueueHandle<Self>,+ ) {+ }+}++impl Dispatch<WpContentTypeManagerV1, ()> for App {+ fn event(+ _: &mut Self,+ _: &WpContentTypeManagerV1,+ _: <WpContentTypeManagerV1 as Proxy>::Event,+ _: &(),+ _: &Connection,+ _: &QueueHandle<Self>,+ ) {+ }+}++impl Dispatch<WpContentTypeV1, ()> for App {+ fn event(+ _: &mut Self,+ _: &WpContentTypeV1,+ _: wp_content_type_v1::Event,+ _: &(),+ _: &Connection,+ _: &QueueHandle<Self>,+ ) {+ }+}+ impl ActivationHandler for App { type RequestData = RequestData; @@ -84,10 +84,18 @@ use wayland_client::{ wl_surface, }, };+use wayland_protocols::wp::content_type::v1::client::{+ wp_content_type_manager_v1::WpContentTypeManagerV1,+ wp_content_type_v1::{self, WpContentTypeV1},+}; use wayland_protocols::wp::fractional_scale::v1::client::{ wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1, wp_fractional_scale_v1::{self, WpFractionalScaleV1}, };+use wayland_protocols::wp::idle_inhibit::zv1::client::{+ zwp_idle_inhibit_manager_v1::ZwpIdleInhibitManagerV1,+ zwp_idle_inhibitor_v1::{self, ZwpIdleInhibitorV1},+}; use wayland_protocols::wp::text_input::zv3::client::{ zwp_text_input_manager_v3::ZwpTextInputManagerV3, zwp_text_input_v3::{self, ContentHint, ContentPurpose, ZwpTextInputV3},@@ -181,6 +189,14 @@ pub fn run(config: Config, config_path: Option<std::path::PathBuf>) -> anyhow::R }); let text_input_manager = bind_global::<ZwpTextInputManagerV3>(&globals, &qh); let activation = ActivationState::bind(&globals, &qh).ok();+ let idle_inhibit_manager = bind_global::<ZwpIdleInhibitManagerV1>(&globals, &qh);+ // Tag the surface as plain content (a terminal is none of photo/video/game)+ // so the compositor applies no media-specific treatment. Applies on commit.+ let content_type = bind_global::<WpContentTypeManagerV1>(&globals, &qh)+ .map(|mgr| mgr.get_surface_content_type(window.wl_surface(), &qh, ()));+ if let Some(ct) = &content_type {+ ct.set_content_type(wp_content_type_v1::Type::None);+ } // First commit with no buffer kicks off the initial configure. window.commit();@@ -219,6 +235,9 @@ pub fn run(config: Config, config_path: Option<std::path::PathBuf>) -> anyhow::R cursor_shape_manager, text_input_manager, activation,+ idle_inhibit_manager,+ idle_inhibitor: None,+ content_type, preedit: String::new(), ime_preedit_pending: String::new(), ime_commit_pending: String::new(),@@ -397,6 +416,17 @@ struct App { text_input_manager: Option<ZwpTextInputManagerV3>, /// xdg-activation, used to request attention on an urgent bell. activation: Option<ActivationState>,+ /// idle-inhibit-v1 manager; an inhibitor is held while focused when the+ /// `[main] idle-inhibit` config is on, so the screen does not blank.+ idle_inhibit_manager: Option<ZwpIdleInhibitManagerV1>,+ idle_inhibitor: Option<ZwpIdleInhibitorV1>,+ /// content-type-v1 hint object. Set once at startup; held only so the+ /// object (and thus the hint) outlives construction.+ #[allow(+ dead_code,+ reason = "kept alive to preserve the surface content-type hint"+ )]+ content_type: Option<WpContentTypeV1>, /// Committed IME preedit string shown inline at the cursor while composing. preedit: String, /// Preedit/commit accumulated since the last text-input `done`.@@ -1409,6 +1439,21 @@ impl App { } } + /// Create or drop the idle inhibitor to match `[main] idle-inhibit` and the+ /// current focus: inhibit only while focused, so a backgrounded terminal+ /// still lets the screen blank. Idempotent; called on every focus change.+ fn sync_idle_inhibit(&mut self) {+ let want = self.config.main.idle_inhibit && self.focused;+ if want && self.idle_inhibitor.is_none() {+ if let Some(mgr) = &self.idle_inhibit_manager {+ self.idle_inhibitor =+ Some(mgr.create_inhibitor(self.window.wl_surface(), &self.qh, ()));+ }+ } else if !want && let Some(inhibitor) = self.idle_inhibitor.take() {+ inhibitor.destroy();+ }+ }+ /// Write bytes to the PTY master, logging on failure. fn write_to_pty(&mut self, bytes: &[u8]) { if let Some(session) = self.session.as_mut()