Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions snowcap/api/lua/snowcap/grpc/defs.lua
Original file line number Diff line number Diff line change
Expand Up @@ -921,6 +921,8 @@ local snowcap_layer_v1_Layer = {
---@field key integer?
---@field modifiers snowcap.input.v1.Modifiers?
---@field pressed boolean?
---@field captured boolean?
---@field text string?

---@class snowcap.input.v1.PointerButtonRequest
---@field id integer?
Expand Down
8 changes: 8 additions & 0 deletions snowcap/api/lua/snowcap/input.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,12 @@ local input = {
---@field alt boolean
---@field super boolean

---A Key event.
---@class snowcap.input.KeyEvent
---@field key snowcap.Key Key Symbol.
---@field mods snowcap.input.Modifiers Currently active modifiers.
---@field pressed boolean True if the key is currently pressed, false on release.
---@field captured boolean True if the event was flagged as Captured by a widget.
---@field text? string Text produced by the event, if any.

return input
36 changes: 35 additions & 1 deletion snowcap/api/lua/snowcap/layer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -169,14 +169,48 @@ function layer.new_widget(args)
end)
end

---Do something when a key event is received.
---@param on_event fun(handle: snowcap.layer.LayerHandle, event: snowcap.input.KeyEvent)
function LayerHandle:on_key_event(on_event)
local err = client:snowcap_input_v1_InputService_KeyboardKey(
{ id = self.id },
function(response)
---@cast response snowcap.input.v1.KeyboardKeyResponse

local mods = response.modifiers or {}
mods.shift = mods.shift or false
mods.ctrl = mods.ctrl or false
mods.alt = mods.alt or false
mods.super = mods.super or false

---@cast mods snowcap.input.Modifiers

---@type snowcap.input.KeyEvent
local event = {
key = response.key or 0,
mods = mods,
pressed = response.pressed,
captured = response.captured,
text = response.text,
}

on_event(self, event)
end
)

if err then
log.error(err)
end
end

---@param on_press fun(mods: snowcap.input.Modifiers, key: snowcap.Key)
function LayerHandle:on_key_press(on_press)
local err = client:snowcap_input_v1_InputService_KeyboardKey(
{ id = self.id },
function(response)
---@cast response snowcap.input.v1.KeyboardKeyResponse

if not response.pressed then
if not response.pressed or response.captured then
return
end

Expand Down
2 changes: 2 additions & 0 deletions snowcap/api/protobuf/snowcap/input/v1/input.proto
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ message KeyboardKeyResponse {
uint32 key = 1;
Modifiers modifiers = 2;
bool pressed = 3;
bool captured = 4;
optional string text = 5;
}

message PointerButtonRequest {
Expand Down
27 changes: 27 additions & 0 deletions snowcap/api/rust/src/input.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Input types.

use snowcap_api_defs::snowcap::input;
use xkbcommon::xkb::Keysym;

/// Keyboard modifiers.
#[allow(missing_docs)]
Expand All @@ -22,3 +23,29 @@ impl From<input::v1::Modifiers> for Modifiers {
}
}
}

/// A Key event.
pub struct KeyEvent {
/// Key Symbol.
pub key: Keysym,
/// Currently active modifiers.
pub mods: Modifiers,
/// True if the key is currently pressed, false on release.
pub pressed: bool,
/// True if the event was flagged as Captured by a widget.
pub captured: bool,
/// Text produced by the event, if any.
pub text: Option<String>,
}

impl From<input::v1::KeyboardKeyResponse> for KeyEvent {
fn from(value: input::v1::KeyboardKeyResponse) -> Self {
Self {
key: Keysym::new(value.key),
mods: Modifiers::from(value.modifiers.unwrap_or_default()),
pressed: value.pressed,
captured: value.captured,
text: value.text,
}
}
}
33 changes: 31 additions & 2 deletions snowcap/api/rust/src/layer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use xkbcommon::xkb::Keysym;
use crate::{
BlockOnTokio,
client::Client,
input::Modifiers,
input::{KeyEvent, Modifiers},
widget::{Program, Widget, WidgetId},
};

Expand Down Expand Up @@ -264,6 +264,35 @@ where
let _ = self.msg_sender.send(message);
}

/// Do something when a key event is received
pub fn on_key_event(
&self,
mut on_event: impl FnMut(LayerHandle<Msg>, KeyEvent) + Send + 'static,
) {
let mut stream = match Client::input()
.keyboard_key(KeyboardKeyRequest {
id: self.id.to_inner(),
})
.block_on_tokio()
{
Ok(stream) => stream.into_inner(),
Err(status) => {
error!("Failed to set `on_key_event` handler: {status}");
return;
}
};

let handle = self.clone();

tokio::spawn(async move {
while let Some(Ok(response)) = stream.next().await {
let event = KeyEvent::from(response);

on_event(handle.clone(), event);
}
});
}

/// Do something on key press.
pub fn on_key_press(
&self,
Expand All @@ -286,7 +315,7 @@ where

tokio::spawn(async move {
while let Some(Ok(response)) = stream.next().await {
if !response.pressed {
if !response.pressed || response.captured {
continue;
}

Expand Down
2 changes: 2 additions & 0 deletions snowcap/src/api/input/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ impl input_service_server::InputService for super::InputService {
key: item.key.raw(),
modifiers: Some(api_modifiers),
pressed: item.pressed,
captured: item.captured,
text: item.text,
})
},
)
Expand Down
12 changes: 11 additions & 1 deletion snowcap/src/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,17 @@ impl SeatHandler for State {
capability: Capability,
) {
if capability == Capability::Keyboard && self.keyboard.is_none() {
let keyboard = self.seat_state.get_keyboard(qh, &seat, None).unwrap();
let keyboard = self
.seat_state
.get_keyboard_with_repeat(
qh,
&seat,
None,
self.loop_handle.clone(),
Box::new(State::on_key_press),
)
.unwrap();

self.keyboard = Some(keyboard);
}

Expand Down
95 changes: 66 additions & 29 deletions snowcap/src/handlers/keyboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,53 @@ use smithay_client_toolkit::{

use crate::{input::keyboard::keysym_to_iced_key_and_loc, state::State};

#[derive(Clone, Copy, Debug)]
#[derive(Clone, Debug)]
pub struct KeyboardKey {
pub key: Keysym,
pub modifiers: Modifiers,
pub pressed: bool,
pub captured: bool,
pub text: Option<String>,
}

impl State {
pub(crate) fn on_key_press(&mut self, _keyboard: &WlKeyboard, event: KeyEvent) {
let Some(KeyboardFocus::Layer(layer)) = self.keyboard_focus.as_ref() else {
return;
};

let Some(snowcap_layer) = self.layers.iter_mut().find(|sn_l| &sn_l.layer == layer) else {
return;
};

let (key, location) = keysym_to_iced_key_and_loc(event.keysym);

let mut modifiers = iced::keyboard::Modifiers::empty();
if self.keyboard_modifiers.ctrl {
modifiers |= iced::keyboard::Modifiers::CTRL;
}
if self.keyboard_modifiers.alt {
modifiers |= iced::keyboard::Modifiers::ALT;
}
if self.keyboard_modifiers.shift {
modifiers |= iced::keyboard::Modifiers::SHIFT;
}
if self.keyboard_modifiers.logo {
modifiers |= iced::keyboard::Modifiers::LOGO;
}

snowcap_layer
.surface
.widgets
.queue_event(iced::Event::Keyboard(iced::keyboard::Event::KeyPressed {
key: key.clone(),
location,
modifiers,
text: event.utf8.map(Into::into),
modified_key: key, // TODO:
physical_key: Physical::Unidentified(NativeCode::Xkb(event.keysym.raw())),
}));
}
}

impl KeyboardHandler for State {
Expand Down Expand Up @@ -60,6 +102,17 @@ impl KeyboardHandler for State {
_keyboard: &WlKeyboard,
_serial: u32,
event: KeyEvent,
) {
self.on_key_press(_keyboard, event)
}

fn release_key(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_keyboard: &WlKeyboard,
_serial: u32,
event: KeyEvent,
) {
let Some(KeyboardFocus::Layer(layer)) = self.keyboard_focus.as_ref() else {
return;
Expand Down Expand Up @@ -88,24 +141,28 @@ impl KeyboardHandler for State {
snowcap_layer
.surface
.widgets
.queue_event(iced::Event::Keyboard(iced::keyboard::Event::KeyPressed {
.queue_event(iced::Event::Keyboard(iced::keyboard::Event::KeyReleased {
key: key.clone(),
location,
modifiers,
text: None,
modified_key: key, // TODO:
// TODO:
modified_key: key,
physical_key: Physical::Unidentified(NativeCode::Xkb(event.keysym.raw())),
}));
}

fn release_key(
fn update_modifiers(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_keyboard: &WlKeyboard,
_serial: u32,
event: KeyEvent,
modifiers: Modifiers,
_layout: u32,
) {
self.keyboard_modifiers = modifiers;

// TODO: Should we check if the modifiers actually changed ?
let Some(KeyboardFocus::Layer(layer)) = self.keyboard_focus.as_ref() else {
return;
};
Expand All @@ -114,8 +171,6 @@ impl KeyboardHandler for State {
return;
};

let (key, location) = keysym_to_iced_key_and_loc(event.keysym);

let mut modifiers = iced::keyboard::Modifiers::empty();
if self.keyboard_modifiers.ctrl {
modifiers |= iced::keyboard::Modifiers::CTRL;
Expand All @@ -133,27 +188,9 @@ impl KeyboardHandler for State {
snowcap_layer
.surface
.widgets
.queue_event(iced::Event::Keyboard(iced::keyboard::Event::KeyReleased {
key: key.clone(),
location,
modifiers,
// TODO:
modified_key: key,
physical_key: Physical::Unidentified(NativeCode::Xkb(event.keysym.raw())),
}));
}

fn update_modifiers(
&mut self,
_conn: &Connection,
_qh: &QueueHandle<Self>,
_keyboard: &WlKeyboard,
_serial: u32,
modifiers: Modifiers,
_layout: u32,
) {
// TODO: per wl_keyboard
self.keyboard_modifiers = modifiers;
.queue_event(iced::Event::Keyboard(
iced::keyboard::Event::ModifiersChanged(modifiers),
));
}
}
delegate_keyboard!(State);
Expand Down
10 changes: 6 additions & 4 deletions snowcap/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,14 +149,12 @@ impl State {

runtime.track(iced_futures::subscription::into_recipes(
iced::event::listen_with(|event, status, id| {
if status == iced::event::Status::Captured {
return None;
}

let captured = status == iced::event::Status::Captured;
match event {
iced::Event::Keyboard(iced::keyboard::Event::KeyPressed {
modifiers,
physical_key: Physical::Unidentified(NativeCode::Xkb(raw)),
text,
..
}) => Some((
id,
Expand All @@ -171,6 +169,8 @@ impl State {
num_lock: false,
},
pressed: true,
captured,
text: text.map(|s| s.into()),
}),
)),
iced::Event::Keyboard(iced::keyboard::Event::KeyReleased {
Expand All @@ -190,6 +190,8 @@ impl State {
num_lock: false,
},
pressed: false,
captured,
text: None,
}),
)),
_ => None,
Expand Down
Loading