diff --git a/src/app.rs b/src/app.rs index 10ebb80..de9ef48 100644 --- a/src/app.rs +++ b/src/app.rs @@ -840,6 +840,9 @@ pub trait App: 'static /// focused; a new `serial` renews the request, e.g. on a press on the field. fn external_text_input( &self ) -> Option { None } + /// What the field held by [`Self::external_text_input`] is for; `Terminal` gives the on-screen keyboard its row of terminal keys. + fn external_text_purpose( &self ) -> crate::osk::Purpose { crate::osk::Purpose::Normal } + /// Text committed by the input method while [`Self::external_text_input`] holds it. fn on_external_text_commit( &mut self, _text: &str ) {} @@ -880,7 +883,7 @@ pub trait App: 'static fn take_clipboard_request( &mut self ) -> Option { None } /// Called on every pointer motion (mouse move, button press, button - /// release). `x`, `y` are in physical pixels of the main surface, + /// release) and on every primary-finger touch down and motion. `x`, `y` are in physical pixels of the main surface, /// the same coordinate space as widget rects. Default: no-op. /// /// Useful for embedding components that need to know the live @@ -965,8 +968,8 @@ pub trait App: 'static /// release position in logical pixels. fn on_drop_received( &mut self, _x: f32, _y: f32, _mime: &str, _text: &str ) {} - /// Called on every left-button mouse press / release before the - /// regular gesture machine fires. `pressed = true` for press, + /// Called on every left-button mouse press / release, and on every + /// primary-finger touch down / up, before the regular gesture machine fires. `pressed = true` for press, /// `false` for release. `(x, y)` are in physical pixels (same /// coordinate space as [`Self::on_pointer_move`]). Default: no-op. /// @@ -998,6 +1001,15 @@ pub trait App: 'static /// input for the embedded system, and goes through [`Self::on_back`]. fn on_raw_key( &mut self, _keysym: Keysym, _keycode: u32, _pressed: bool, _ctrl: bool, _shift: bool ) {} + /// [`Self::on_raw_key`] with the Alt state too; the runtime calls this one, which forwards to `on_raw_key` unless overridden. Override it instead when Alt matters, as in a terminal. + fn on_raw_key_with_alt( &mut self, keysym: Keysym, keycode: u32, pressed: bool, ctrl: bool, shift: bool, _alt: bool ) + { + self.on_raw_key( keysym, keycode, pressed, ctrl, shift ); + } + + /// `true` while every key belongs to [`Self::on_raw_key`], as in a terminal: ltk then neither dispatches keys to its widgets, so Enter and Space do not press the focused or hovered one, nor repeats them. `XF86_Back` still goes through [`Self::on_back`]. + fn claims_keyboard( &self ) -> bool { false } + /// Called when the surface is (re-)configured with a new size. /// /// `width` and `height` are in **physical pixels** (the Wayland surface @@ -1186,8 +1198,10 @@ pub trait App: 'static /// When [`Self::window_size_hint`] is set, ltk pins /// `min_size == max_size` before the first commit so the compositor's /// first configure honours the requested size, and then releases - /// `max_size` from the first configure so the surface becomes - /// user-resizable. Returning `false` skips that release: the pin stays + /// both from the first configure so the surface becomes + /// user-resizable; a minimum left in place would make a mobile + /// compositor shrink the window's scale to honour it. Returning + /// `false` skips that release: the pin stays /// in place for the lifetime of the toplevel, declaring a fixed-size /// window that compositors must not resize or restore a remembered /// geometry over. diff --git a/src/event_loop/app_data.rs b/src/event_loop/app_data.rs index a70be9a..1a5571c 100644 --- a/src/event_loop/app_data.rs +++ b/src/event_loop/app_data.rs @@ -163,6 +163,7 @@ pub struct AppData smithay_client_toolkit::foreign_toplevel_list::ForeignToplevelList, pub shift_pressed: bool, pub ctrl_pressed: bool, + pub alt_pressed: bool, /// Calloop handle for inserting timers / channels. Used by the /// key-repeat machinery and any future feature that needs to /// schedule work on the run loop without going through the message diff --git a/src/event_loop/run.rs b/src/event_loop/run.rs index 50fb414..00a6bd5 100644 --- a/src/event_loop/run.rs +++ b/src/event_loop/run.rs @@ -133,10 +133,14 @@ pub( crate ) fn try_run( mut app: A ) -> Result<(), RunError> // Skipped when going fullscreen: Mutter rejects the fullscreen // request if a min_size constrains it. + // Fitted before the first commit, a hint too big for the screen never reaches a compositor that would rescale the window to honour it. + let prefitted_size_hint = app.window_size_hint() + .filter( |_| !app.start_fullscreen() ) + .and_then( |hint| super::output_probe::output_logical_size( &conn, &globals ).map( |output| super::window_fit::fit_within( hint, super::window_fit::output_share( output ) ) ) ); let apply_size_hint = |window: &smithay_client_toolkit::shell::xdg::window::Window| { if app.start_fullscreen() { return; } - match app.window_size_hint() + match prefitted_size_hint.or( app.window_size_hint() ) { Some( ( w, h ) ) => { @@ -288,7 +292,7 @@ pub( crate ) fn try_run( mut app: A ) -> Result<(), RunError> let pending_fullscreen = app.start_fullscreen(); let pending_size_hint_unpin = !app.start_fullscreen() && app.window_size_hint().is_some() && app.window_resizable(); - let pending_size_hint_fit = !app.start_fullscreen() && app.window_size_hint().is_some(); + let pending_size_hint_fit = !app.start_fullscreen() && app.window_size_hint().is_some() && prefitted_size_hint.is_none(); let mut data = AppData { @@ -334,6 +338,7 @@ pub( crate ) fn try_run( mut app: A ) -> Result<(), RunError> foreign_toplevel_list, shift_pressed: false, ctrl_pressed: false, + alt_pressed: false, loop_handle: event_loop.handle(), compositor_repeat_rate: 0, compositor_repeat_delay: 0, @@ -355,7 +360,7 @@ pub( crate ) fn try_run( mut app: A ) -> Result<(), RunError> applied_fullscreen: Some( pending_fullscreen ), pending_size_hint_unpin, pending_size_hint_fit, - fitted_size_hint: None, + fitted_size_hint: prefitted_size_hint, main: SurfaceState::::new( surface_kind, titlebar_height, titlebar_title ), overlays: std::collections::HashMap::new(), secondaries: Vec::new(), diff --git a/src/event_loop/text_editing/ime.rs b/src/event_loop/text_editing/ime.rs index e6a6897..0da2dd6 100644 --- a/src/event_loop/text_editing/ime.rs +++ b/src/event_loop/text_editing/ime.rs @@ -71,9 +71,14 @@ impl AppData ( Some( _ ), _ ) if self.text_input.is_none() => { let qh = self.qh.clone(); - self.activate_text_input( &qh, false, crate::osk::Purpose::Normal ); + let purpose = self.app.external_text_purpose(); + self.activate_text_input( &qh, false, purpose ); + } + ( Some( new ), Some( old ) ) if new != old => + { + self.text_input_purpose = self.app.external_text_purpose(); + self.reenable_text_input(); } - ( Some( new ), Some( old ) ) if new != old => self.reenable_text_input(), ( None, Some( _ ) ) => self.deactivate_text_input(), _ => {} } diff --git a/src/input/keyboard/mod.rs b/src/input/keyboard/mod.rs index 8c03b82..9bad86f 100644 --- a/src/input/keyboard/mod.rs +++ b/src/input/keyboard/mod.rs @@ -6,7 +6,7 @@ //! Translates `wl_keyboard` events into focus / text-insertion / //! widget-submit actions. Does not share state with the gesture state //! machine — keyboard tracks its own focus (`AppData::keyboard_focus`) -//! and modifier flags (`shift_pressed`, `ctrl_pressed`). +//! and modifier flags (`shift_pressed`, `ctrl_pressed`, `alt_pressed`). //! //! The only cross-cutting state it reads is `SurfaceState::focused_idx` //! (set by pointer / touch focus updates) so keyboard navigation can @@ -81,13 +81,14 @@ impl KeyboardHandler for AppData // request, not input for the embedded system. if event.keysym != Keysym::XF86_Back { - self.app.on_raw_key( event.keysym, event.raw_code, true, self.ctrl_pressed, self.shift_pressed ); + self.app.on_raw_key_with_alt( event.keysym, event.raw_code, true, self.ctrl_pressed, self.shift_pressed, self.alt_pressed ); } // A new press always cancels any in-flight repeat — the user // has either released the previous key or is pressing a // different one. Either way, the prior timer's keysym should // not keep firing. self.stop_key_repeat(); + if event.keysym != Keysym::XF86_Back && self.app.claims_keyboard() { return; } self.dispatch_key( focus, event.clone() ); self.start_key_repeat( focus, event ); } @@ -103,7 +104,7 @@ impl KeyboardHandler for AppData { if event.keysym != Keysym::XF86_Back { - self.app.on_raw_key( event.keysym, event.raw_code, false, self.ctrl_pressed, self.shift_pressed ); + self.app.on_raw_key_with_alt( event.keysym, event.raw_code, false, self.ctrl_pressed, self.shift_pressed, self.alt_pressed ); } // Only stop if the released key matches the one currently // repeating; releasing a non-repeating key (e.g. a shift that @@ -131,6 +132,7 @@ impl KeyboardHandler for AppData { self.shift_pressed = modifiers.shift; self.ctrl_pressed = modifiers.ctrl; + self.alt_pressed = modifiers.alt; } fn update_repeat_info(