diff --git a/Cargo.lock b/Cargo.lock index b295328..15700fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1482,7 +1482,7 @@ checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" [[package]] name = "smithay" version = "0.3.0" -source = "git+https://github.com/smithay/smithay#3af1e3c6a5d8e890619f3f398ad02f5ce04b3ef7" +source = "git+https://github.com/chrisduerr/smithay?rev=48d7a8cff7c17758e5435ac94ce3f954a10cb39a#48d7a8cff7c17758e5435ac94ce3f954a10cb39a" dependencies = [ "appendlist", "bitflags 2.4.2", diff --git a/Cargo.toml b/Cargo.toml index 9becb8a..c5f75ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,8 @@ edition = "2021" members = ["catacomb_ipc"] [dependencies.smithay] -git = "https://github.com/smithay/smithay" +git = "https://github.com/chrisduerr/smithay" +rev = "48d7a8cff7c17758e5435ac94ce3f954a10cb39a" default-features = false features = [ "use_system_lib", diff --git a/catacomb_ipc/Cargo.toml b/catacomb_ipc/Cargo.toml index 5b97837..a3e476a 100644 --- a/catacomb_ipc/Cargo.toml +++ b/catacomb_ipc/Cargo.toml @@ -9,7 +9,8 @@ license = "GPL-3.0" edition = "2021" [dependencies.smithay] -git = "https://github.com/smithay/smithay" +git = "https://github.com/chrisduerr/smithay" +rev = "48d7a8cff7c17758e5435ac94ce3f954a10cb39a" default-features = false features = [ "use_system_lib", diff --git a/src/catacomb.rs b/src/catacomb.rs index a35740a..6c583dd 100644 --- a/src/catacomb.rs +++ b/src/catacomb.rs @@ -36,7 +36,7 @@ use smithay::wayland::fractional_scale::{ }; use smithay::wayland::idle_inhibit::{IdleInhibitHandler, IdleInhibitManagerState}; use smithay::wayland::input_method::{ - InputMethodHandler, InputMethodManagerState, PopupSurface as ImeSurface, + InputMethodHandler, InputMethodManagerState, InputMethodSeat, PopupSurface as ImeSurface, }; use smithay::wayland::keyboard_shortcuts_inhibit::{ KeyboardShortcutsInhibitHandler, KeyboardShortcutsInhibitState, KeyboardShortcutsInhibitor, @@ -142,6 +142,7 @@ pub struct Catacomb { last_focus: Option, locker: Option, _power_inhibitor: Option, + ime_override: Option, // Indicates if rendering was intentionally stalled. // @@ -333,6 +334,7 @@ impl Catacomb { idle_inhibitors: Default::default(), suspend_timer: Default::default(), key_bindings: Default::default(), + ime_override: Default::default(), last_focus: Default::default(), terminated: Default::default(), idle_timer: Default::default(), @@ -474,6 +476,22 @@ impl Catacomb { self.windows.unlock_orientation(); self.unstall(); } + + /// Toggle IME force enable/disable. + pub fn toggle_ime_override(&mut self) { + self.ime_override = match self.ime_override { + None => Some(false), + Some(false) => Some(true), + Some(true) => None, + }; + + // Ensure gesture handle is updated. + self.windows.set_ime_override(self.ime_override); + + // Update IME state. + let cseat = self.seat.clone(); + cseat.input_method().set_active(self, None, self.ime_override); + } } impl CompositorHandler for Catacomb { diff --git a/src/drawing.rs b/src/drawing.rs index 4e836e4..5c928c0 100644 --- a/src/drawing.rs +++ b/src/drawing.rs @@ -37,6 +37,15 @@ const DROP_TARGET_RGBA: [u8; 4] = [32, 32, 32, 64]; /// Relative size of gesture notch to the handle's whole width/height. const GESTURE_NOTCH_PERCENTAGE: f64 = 0.2; +/// Gesture handle color with automatic IME control. +const GESTURE_HANDLE_DEFAULT_RGBA: [u8; 3] = [117; 3]; + +/// Gesture handle color with IME force-enabled. +const GESTURE_HANDLE_LOCKED_RGBA: [u8; 3] = [42, 117, 42]; + +/// Gesture handle color with IME force-disabled. +const GESTURE_HANDLE_BLOCKED_RGBA: [u8; 3] = [117, 42, 42]; + /// Cached texture. /// /// Includes all information necessary to render a surface's texture even after @@ -310,10 +319,13 @@ impl RenderElement for CatacombElement { /// Grahpics texture cache. #[derive(Debug)] pub struct Graphics { - pub gesture_handle: Option, pub active_drop_target: RenderTexture, pub urgency_icon: RenderTexture, pub drop_target: RenderTexture, + + gesture_handle_default: Option, + gesture_handle_blocked: Option, + gesture_handle_locked: Option, } impl Graphics { @@ -328,7 +340,9 @@ impl Graphics { active_drop_target: RenderTexture::new(active_drop_target), urgency_icon: RenderTexture::new(urgency_icon), drop_target: RenderTexture::new(drop_target), - gesture_handle: None, + gesture_handle_default: None, + gesture_handle_blocked: None, + gesture_handle_locked: None, } } @@ -337,41 +351,48 @@ impl Graphics { &mut self, renderer: &mut GlesRenderer, canvas: &Canvas, + ime_override: Option, ) -> RenderTexture { + let (handle, color) = match ime_override { + None => (&mut self.gesture_handle_default, GESTURE_HANDLE_DEFAULT_RGBA), + Some(true) => (&mut self.gesture_handle_locked, GESTURE_HANDLE_LOCKED_RGBA), + Some(false) => (&mut self.gesture_handle_blocked, GESTURE_HANDLE_BLOCKED_RGBA), + }; + // Initialize texture or replace it after scale change. let scale = canvas.scale(); let width = canvas.physical_size().w; let height = (GESTURE_HANDLE_HEIGHT as f64 * scale).round() as i32; - if self.gesture_handle.as_ref().map_or(true, |handle| { + if handle.as_ref().map_or(true, |handle| { handle.texture.width() != width as u32 || handle.texture.height() != height as u32 }) { - // Initialize a white buffer with the correct size. - let mut buffer = vec![255; (height * width * 4) as usize]; + // Initialize a black buffer with the correct size. + let mut buffer = vec![0; (height * width * 4) as usize]; // Calculate notch size. let notch_height = (height as f64 * GESTURE_NOTCH_PERCENTAGE) as i32; let notch_width = (width as f64 * GESTURE_NOTCH_PERCENTAGE) as i32; - // Fill everything other than the handle's notch with black pixels. + // Fill handle's notch with the correct color. for x in 0..width { for y in 0..height { - if y < (height - notch_height) / 2 - || y >= (height + notch_height) / 2 - || x < (width - notch_width) / 2 - || x >= (width + notch_width) / 2 + if y >= (height - notch_height) / 2 + && y < (height + notch_height) / 2 + && x >= (width - notch_width) / 2 + && x < (width + notch_width) / 2 { let offset = (y * width + x) as usize * 4; - buffer[offset..offset + 3].copy_from_slice(&[0, 0, 0]); + buffer[offset..offset + 3].copy_from_slice(&color); } } } let texture = Texture::from_buffer(renderer, scale, &buffer, width, height, true); - self.gesture_handle = Some(RenderTexture(Rc::new(texture))); + *handle = Some(RenderTexture(Rc::new(texture))); } // SAFETY: The code above ensures the `Option` is `Some`. - unsafe { self.gesture_handle.clone().unwrap_unchecked() } + unsafe { handle.clone().unwrap_unchecked() } } } diff --git a/src/input.rs b/src/input.rs index a6820ee..d8be88d 100644 --- a/src/input.rs +++ b/src/input.rs @@ -621,13 +621,22 @@ impl Catacomb { /// Single-tap handler. fn on_single_tap(_: Instant, _: &mut (), catacomb: &mut Self) -> TimeoutAction { + let mut toggle_ime = false; + catacomb.windows.on_tap(catacomb.touch_state.position, &mut toggle_ime); + + // Update IME force enable/disable state. + if toggle_ime { + catacomb.toggle_ime_override(); + } + // Clear focus when tapping outside of any window. + // + // This must happen after `on_tap` so we can detect gesture handle taps without + // focused windows. if catacomb.touch_state.input_surface.is_none() { catacomb.windows.set_focus(None, None, None); } - catacomb.windows.on_tap(catacomb.touch_state.position); - // Ensure updates are rendered. catacomb.unstall(); diff --git a/src/udev.rs b/src/udev.rs index 246af92..9744eeb 100644 --- a/src/udev.rs +++ b/src/udev.rs @@ -184,8 +184,7 @@ impl Udev { // Reset DRM state. if let Some(output_device) = &mut catacomb.backend.output_device { // NOTE: Ideally we'd just reset the DRM+Compositor here, but this is - // currently not possible due to a bug in Smithay or - // the driver. + // currently not possible due to a bug in Smithay or the driver. let device_id = output_device.id; let result = catacomb.backend.change_device( &catacomb.display_handle, diff --git a/src/windows/mod.rs b/src/windows/mod.rs index 2b943d0..b068292 100644 --- a/src/windows/mod.rs +++ b/src/windows/mod.rs @@ -124,6 +124,9 @@ pub struct Windows { unlocked_orientation: Orientation, orientation_locked: bool, + /// IME force enable/disable state. + ime_override: Option, + /// Client-independent damage. dirty: bool, } @@ -143,6 +146,7 @@ impl Windows { unlocked_orientation: Default::default(), orphan_popups: Default::default(), window_scales: Default::default(), + ime_override: Default::default(), transaction: Default::default(), activated: Default::default(), textures: Default::default(), @@ -372,7 +376,7 @@ impl Windows { // Draw gesture handle when not in fullscreen/lock view. if !matches!(self.view, View::Fullscreen(_) | View::Lock(_)) { // Get texture for gesture handle. - let gesture_handle = graphics.gesture_handle(renderer, &self.canvas); + let gesture_handle = graphics.gesture_handle(renderer, &self.canvas, self.ime_override); // Calculate gesture handle bounds. let mut bounds = gesture_handle.geometry(scale.into()); @@ -890,14 +894,13 @@ impl Windows { } /// Hand quick touch input. - pub fn on_tap(&mut self, point: Point) { + pub fn on_tap(&mut self, point: Point, toggle_ime: &mut bool) { let overview = match &mut self.view { View::Overview(overview) => overview, + // Handle IME override toggle on gesture handle tap. View::Workspace => { - // Clear focus on gesture handle tap. - if point.y >= (self.canvas.size().h - GESTURE_HANDLE_HEIGHT) as f64 { - self.set_focus(None, None, None); - } + *toggle_ime = point.y >= (self.canvas.size().h - GESTURE_HANDLE_HEIGHT) as f64 + && self.focus().is_none(); return; }, View::DragAndDrop(_) | View::Fullscreen(_) | View::Lock(_) => return, @@ -1299,6 +1302,15 @@ impl Windows { // Default to full output size. Rectangle::from_loc_and_size((0, 0), self.output().size()) } + + /// IME force enable/disable status. + /// + /// This is only responsible for determining the gesture handle color, must + /// only be used through [`Catacomb::toggle_ime_override`]. + pub fn set_ime_override(&mut self, ime_override: Option) { + self.ime_override = ime_override; + self.dirty = true; + } } /// Atomic changes to [`Windows`].