Skip to content

Commit

Permalink
Add manual override for input-method state
Browse files Browse the repository at this point in the history
When tapping the gesture handle while no surface is focused, Catacomb
now allows switching between three different input-method states.

The "default" state is indicated by a grey gesture handle notch and
behaves as usual.

The "blocked" state is indicated by a red gesture handle notch and
forces input-method to stay deactivated, never showing the virtual
keyboard.

The "locked" state is indicated by a green gesture handle notch and
forces input-method to stay activated, always showing the virtual
keyboard.

Closes #143.
  • Loading branch information
chrisduerr committed Feb 2, 2024
1 parent f088c9c commit cf6940d
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 27 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
3 changes: 2 additions & 1 deletion catacomb_ipc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
20 changes: 19 additions & 1 deletion src/catacomb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -142,6 +142,7 @@ pub struct Catacomb {
last_focus: Option<WlSurface>,
locker: Option<SessionLocker>,
_power_inhibitor: Option<OwnedFd>,
ime_override: Option<bool>,

// Indicates if rendering was intentionally stalled.
//
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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 {
Expand Down
47 changes: 34 additions & 13 deletions src/drawing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -310,10 +319,13 @@ impl RenderElement<GlesRenderer> for CatacombElement {
/// Grahpics texture cache.
#[derive(Debug)]
pub struct Graphics {
pub gesture_handle: Option<RenderTexture>,
pub active_drop_target: RenderTexture,
pub urgency_icon: RenderTexture,
pub drop_target: RenderTexture,

gesture_handle_default: Option<RenderTexture>,
gesture_handle_blocked: Option<RenderTexture>,
gesture_handle_locked: Option<RenderTexture>,
}

impl Graphics {
Expand All @@ -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,
}
}

Expand All @@ -337,41 +351,48 @@ impl Graphics {
&mut self,
renderer: &mut GlesRenderer,
canvas: &Canvas,
ime_override: Option<bool>,
) -> 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() }
}
}

Expand Down
13 changes: 11 additions & 2 deletions src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
3 changes: 1 addition & 2 deletions src/udev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
24 changes: 18 additions & 6 deletions src/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ pub struct Windows {
unlocked_orientation: Orientation,
orientation_locked: bool,

/// IME force enable/disable state.
ime_override: Option<bool>,

/// Client-independent damage.
dirty: bool,
}
Expand All @@ -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(),
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -890,14 +894,13 @@ impl Windows {
}

/// Hand quick touch input.
pub fn on_tap(&mut self, point: Point<f64, Logical>) {
pub fn on_tap(&mut self, point: Point<f64, Logical>, 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,
Expand Down Expand Up @@ -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<bool>) {
self.ime_override = ime_override;
self.dirty = true;
}
}

/// Atomic changes to [`Windows`].
Expand Down

0 comments on commit cf6940d

Please sign in to comment.