From 7873dacb822b744f195ad97a8980d5bcbdda3900 Mon Sep 17 00:00:00 2001 From: ShootingStarDragons Date: Tue, 2 Apr 2024 20:28:55 +0800 Subject: [PATCH] feat: support ext-virtual-keyboard --- Cargo.lock | 11 + .../counter_virtual_keyboard/Cargo.toml | 19 ++ .../counter_virtual_keyboard/src/main.rs | 207 ++++++++++++++++++ iced_layershell/src/actions.rs | 7 +- iced_layershell/src/application.rs | 48 +++- iced_layershell/src/lib.rs | 1 + iced_layershell/src/multi_window.rs | 50 ++++- iced_layershell/src/settings.rs | 16 +- 8 files changed, 345 insertions(+), 14 deletions(-) create mode 100644 iced_examples/counter_virtual_keyboard/Cargo.toml create mode 100644 iced_examples/counter_virtual_keyboard/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 1ff7400..3331300 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -523,6 +523,17 @@ dependencies = [ "iced_runtime", ] +[[package]] +name = "counter_virtual_keyboard" +version = "0.2.3" +dependencies = [ + "iced", + "iced_layershell", + "iced_runtime", + "tempfile", + "xkbcommon", +] + [[package]] name = "crc32fast" version = "1.4.0" diff --git a/iced_examples/counter_virtual_keyboard/Cargo.toml b/iced_examples/counter_virtual_keyboard/Cargo.toml new file mode 100644 index 0000000..41959f9 --- /dev/null +++ b/iced_examples/counter_virtual_keyboard/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "counter_virtual_keyboard" +authors.workspace = true +edition.workspace = true +version.workspace = true +license.workspace = true +repository.workspace = true +description.workspace = true +keywords.workspace = true +readme.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +iced.workspace = true +iced_runtime.workspace = true +iced_layershell.workspace = true +xkbcommon = "0.6.0" +tempfile.workspace = true diff --git a/iced_examples/counter_virtual_keyboard/src/main.rs b/iced_examples/counter_virtual_keyboard/src/main.rs new file mode 100644 index 0000000..2d35bc3 --- /dev/null +++ b/iced_examples/counter_virtual_keyboard/src/main.rs @@ -0,0 +1,207 @@ +use std::ffi::CString; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +use iced::widget::{button, column, row, text, text_input}; +use iced::{Alignment, Command, Element, Length, Theme}; +use iced_layershell::actions::LayershellCustomActions; +use iced_layershell::reexport::{Anchor, KeyboardInteractivity}; +use iced_layershell::settings::{LayerShellSettings, Settings, VirtualKeyboardSettings}; +use iced_layershell::Application; + +use iced_layershell::reexport::wl_keyboard::{KeyState, KeymapFormat}; +use xkbcommon::xkb; + +pub fn get_keymap_as_file() -> (File, u32) { + let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS); + + let keymap = xkb::Keymap::new_from_names( + &context, + "", + "", + "us", // if no , it is norwegian + "", + None, + xkb::KEYMAP_COMPILE_NO_FLAGS, + ) + .expect("xkbcommon keymap panicked!"); + let xkb_state = xkb::State::new(&keymap); + let keymap = xkb_state + .get_keymap() + .get_as_string(xkb::KEYMAP_FORMAT_TEXT_V1); + let keymap = CString::new(keymap).expect("Keymap should not contain interior nul bytes"); + let keymap = keymap.as_bytes_with_nul(); + let dir = std::env::var_os("XDG_RUNTIME_DIR") + .map(PathBuf::from) + .unwrap_or_else(std::env::temp_dir); + let mut file = tempfile::tempfile_in(dir).expect("File could not be created!"); + file.write_all(keymap).unwrap(); + file.flush().unwrap(); + (file, keymap.len() as u32) +} + +pub fn main() -> Result<(), iced_layershell::Error> { + let (file, keymap_size) = get_keymap_as_file(); + Counter::run(Settings { + layer_settings: LayerShellSettings { + size: Some((0, 400)), + exclusize_zone: 400, + anchor: Anchor::Bottom | Anchor::Left | Anchor::Right, + keyboard_interactivity: KeyboardInteractivity::None, + ..Default::default() + }, + virtual_keyboard_support: Some(VirtualKeyboardSettings { + file, + keymap_size, + keymap_format: KeymapFormat::XkbV1, + }), + ..Default::default() + }) +} + +struct Counter { + value: i32, + text: String, +} + +#[derive(Debug, Clone, Copy)] +enum WindowDirection { + Top, + Left, + Right, + Bottom, +} + +#[derive(Debug, Clone)] +enum Message { + IncrementPressed, + DecrementPressed, + TextInput(String), + Direction(WindowDirection), + InputTest, +} + +impl Application for Counter { + type Message = Message; + type Flags = (); + type Theme = Theme; + type Executor = iced::executor::Default; + + fn new(_flags: ()) -> (Self, Command) { + ( + Self { + value: 0, + text: "eee".to_string(), + }, + Command::none(), + ) + } + + fn namespace(&self) -> String { + String::from("Counter - Iced") + } + + fn update(&mut self, message: Message) -> Command { + match message { + Message::IncrementPressed => { + self.value += 1; + Command::none() + } + Message::DecrementPressed => { + self.value -= 1; + Command::none() + } + Message::TextInput(text) => { + self.text = text; + Command::none() + } + Message::InputTest => Command::single( + LayershellCustomActions::VirtualKeyboardPressed { + time: 100, + key: 16, + keystate: KeyState::Pressed, + } + .into(), + ), + Message::Direction(direction) => match direction { + WindowDirection::Left => Command::batch(vec![ + Command::single( + LayershellCustomActions::AnchorChange( + Anchor::Left | Anchor::Top | Anchor::Bottom, + ) + .into(), + ), + Command::single(LayershellCustomActions::SizeChange((400, 0)).into()), + ]), + WindowDirection::Right => Command::batch(vec![ + Command::single( + LayershellCustomActions::AnchorChange( + Anchor::Right | Anchor::Top | Anchor::Bottom, + ) + .into(), + ), + Command::single(LayershellCustomActions::SizeChange((400, 0)).into()), + ]), + WindowDirection::Bottom => Command::batch(vec![ + Command::single( + LayershellCustomActions::AnchorChange( + Anchor::Bottom | Anchor::Left | Anchor::Right, + ) + .into(), + ), + Command::single(LayershellCustomActions::SizeChange((0, 400)).into()), + ]), + WindowDirection::Top => Command::batch(vec![ + Command::single( + LayershellCustomActions::AnchorChange( + Anchor::Top | Anchor::Left | Anchor::Right, + ) + .into(), + ), + Command::single(LayershellCustomActions::SizeChange((0, 400)).into()), + ]), + }, + } + } + + fn view(&self) -> Element { + let center = column![ + button("Increment").on_press(Message::IncrementPressed), + text(self.value).size(50), + button("test_q").on_press(Message::InputTest), + button("Decrement").on_press(Message::DecrementPressed) + ] + .padding(20) + .align_items(Alignment::Center) + .width(Length::Fill) + .height(Length::Fill); + row![ + button("left") + .on_press(Message::Direction(WindowDirection::Left)) + .height(Length::Fill), + column![ + button("top") + .on_press(Message::Direction(WindowDirection::Top)) + .width(Length::Fill), + center, + text_input("hello", &self.text) + .on_input(Message::TextInput) + .padding(10), + button("bottom") + .on_press(Message::Direction(WindowDirection::Bottom)) + .width(Length::Fill), + ] + .width(Length::Fill), + button("right") + .on_press(Message::Direction(WindowDirection::Right)) + .height(Length::Fill), + ] + .padding(20) + .spacing(10) + .align_items(Alignment::Center) + .width(Length::Fill) + .height(Length::Fill) + .into() + } +} diff --git a/iced_layershell/src/actions.rs b/iced_layershell/src/actions.rs index cfa7db7..cc8fce4 100644 --- a/iced_layershell/src/actions.rs +++ b/iced_layershell/src/actions.rs @@ -2,7 +2,7 @@ use crate::reexport::{Anchor, Layer}; use iced::window::Id as IcedId; use iced_core::mouse::Interaction; use iced_runtime::command::Action; -use layershellev::id::Id as LayerId; +use layershellev::{id::Id as LayerId, reexport::wayland_client::KeyState}; #[allow(unused)] #[derive(Debug, Clone)] pub(crate) enum LayerShellActions { @@ -18,6 +18,11 @@ pub enum LayershellCustomActions { AnchorChange(Anchor), LayerChange(Layer), SizeChange((u32, u32)), + VirtualKeyboardPressed { + time: u32, + key: u32, + keystate: KeyState, + }, } #[derive(Debug, Clone, Copy)] diff --git a/iced_layershell/src/application.rs b/iced_layershell/src/application.rs index 196a013..f7ab84b 100644 --- a/iced_layershell/src/application.rs +++ b/iced_layershell/src/application.rs @@ -1,12 +1,13 @@ mod state; -use std::{mem::ManuallyDrop, sync::Arc}; +use std::{mem::ManuallyDrop, os::fd::AsFd, sync::Arc}; use crate::{ actions::{LayerShellActions, LayershellCustomActions}, clipboard::LayerShellClipboard, conversion, error::Error, + settings::VirtualKeyboardSettings, }; use iced_graphics::Compositor; @@ -21,7 +22,10 @@ use iced_style::application::StyleSheet; use iced_futures::{Executor, Runtime, Subscription}; use layershellev::{ - reexport::wayland_client::{KeyState, WEnum}, + reexport::{ + wayland_client::{KeyState, WEnum}, + zwp_virtual_keyboard_v1, + }, LayerEvent, ReturnData, WindowState, WindowWrapper, }; @@ -192,12 +196,36 @@ where let mut key_event: Option> = None; let mut key_ping_count: u32 = 400; + let mut virtuan_keyboard = None; let _ = ev.running_with_proxy(message_receiver, move |event, ev, _| { use layershellev::DispatchMessage; + let mut def_returndata = ReturnData::None; match event { - LayerEvent::InitRequest => {} + LayerEvent::InitRequest => { + if settings.virtual_keyboard_support.is_some() { + def_returndata = ReturnData::RequestBind; + } + } // TODO: maybe use it later - LayerEvent::BindProvide(_, _) => {} + LayerEvent::BindProvide(globals, qh) => { + let virtual_keyboard_manager = globals + .bind::( + qh, + 1..=1, + (), + ) + .expect("no support virtual_keyboard"); + let VirtualKeyboardSettings { + file, + keymap_size, + keymap_format, + } = settings.virtual_keyboard_support.as_ref().unwrap(); + let seat = ev.get_seat(); + let virtual_keyboard_in = + virtual_keyboard_manager.create_virtual_keyboard(seat, qh, ()); + virtual_keyboard_in.keymap((*keymap_format).into(), file.as_fd(), *keymap_size); + virtuan_keyboard = Some(virtual_keyboard_in); + } LayerEvent::RequestMessages(message) => { match message { DispatchMessage::MouseEnter { serial, .. } => { @@ -268,6 +296,16 @@ where LayershellCustomActions::SizeChange((width, height)) => { ev.main_window().set_size((width, height)); } + LayershellCustomActions::VirtualKeyboardPressed { + time, + key, + keystate, + } => { + virtuan_keyboard + .as_ref() + .unwrap() + .key(time, key, keystate.into()); + } } } } @@ -292,7 +330,7 @@ where } } } - ReturnData::None + def_returndata } task::Poll::Ready(_) => ReturnData::RequestExist, } diff --git a/iced_layershell/src/lib.rs b/iced_layershell/src/lib.rs index e9a553e..fcbf9b9 100644 --- a/iced_layershell/src/lib.rs +++ b/iced_layershell/src/lib.rs @@ -15,6 +15,7 @@ pub mod reexport { pub use layershellev::reexport::Anchor; pub use layershellev::reexport::KeyboardInteractivity; pub use layershellev::reexport::Layer; + pub use layershellev::reexport::wayland_client::wl_keyboard; } use settings::Settings; diff --git a/iced_layershell/src/multi_window.rs b/iced_layershell/src/multi_window.rs index 4ab864d..86e64f6 100644 --- a/iced_layershell/src/multi_window.rs +++ b/iced_layershell/src/multi_window.rs @@ -2,8 +2,9 @@ mod state; use crate::{ actions::{LayershellCustomActionsWithId, LayershellCustomActionsWithIdInner}, multi_window::window_manager::WindowManager, + settings::VirtualKeyboardSettings, }; -use std::{collections::HashMap, f64, mem::ManuallyDrop, sync::Arc}; +use std::{collections::HashMap, f64, mem::ManuallyDrop, os::fd::AsFd, sync::Arc}; use crate::{ actions::{LayerShellActions, LayershellCustomActions}, @@ -23,7 +24,10 @@ use iced_style::application::StyleSheet; use iced_futures::{Executor, Runtime, Subscription}; use layershellev::{ - reexport::wayland_client::{KeyState, WEnum}, + reexport::{ + wayland_client::{KeyState, WEnum}, + zwp_virtual_keyboard_v1, + }, LayerEvent, ReturnData, WindowState, }; @@ -201,13 +205,36 @@ where let mut key_event: Option> = None; let mut key_ping_count: u32 = 400; + let mut virtuan_keyboard = None; let _ = ev.running_with_proxy(message_receiver, move |event, ev, index| { use layershellev::DispatchMessage; + let mut def_returndata = ReturnData::None; let id = index.map(|index| ev.get_unit(index).id()); match event { - LayerEvent::InitRequest => {} - // TODO: maybe use it later - LayerEvent::BindProvide(_, _) => {} + LayerEvent::InitRequest => { + if settings.virtual_keyboard_support.is_some() { + def_returndata = ReturnData::RequestBind; + } + } + LayerEvent::BindProvide(globals, qh) => { + let virtual_keyboard_manager = globals + .bind::( + qh, + 1..=1, + (), + ) + .expect("no support virtual_keyboard"); + let VirtualKeyboardSettings { + file, + keymap_size, + keymap_format, + } = settings.virtual_keyboard_support.as_ref().unwrap(); + let seat = ev.get_seat(); + let virtual_keyboard_in = + virtual_keyboard_manager.create_virtual_keyboard(seat, qh, ()); + virtual_keyboard_in.keymap((*keymap_format).into(), file.as_fd(), *keymap_size); + virtuan_keyboard = Some(virtual_keyboard_in); + } LayerEvent::RequestMessages(message) => 'outside: { match message { DispatchMessage::RequestRefresh { width, height } => { @@ -302,6 +329,17 @@ where LayershellCustomActions::SizeChange((width, height)) => { window.set_size((width, height)); } + LayershellCustomActions::VirtualKeyboardPressed { + time, + key, + keystate, + } => { + virtuan_keyboard.as_ref().unwrap().key( + time, + key, + keystate.into(), + ); + } } } } @@ -326,7 +364,7 @@ where } } } - ReturnData::None + def_returndata } task::Poll::Ready(_) => ReturnData::RequestExist, } diff --git a/iced_layershell/src/settings.rs b/iced_layershell/src/settings.rs index 1233d68..79f91aa 100644 --- a/iced_layershell/src/settings.rs +++ b/iced_layershell/src/settings.rs @@ -1,10 +1,19 @@ -use std::borrow::Cow; +use std::{borrow::Cow, fs::File}; use iced::{Font, Pixels}; use crate::reexport::{Anchor, KeyboardInteractivity, Layer}; -#[derive(Debug, Clone)] +use layershellev::reexport::wayland_client::wl_keyboard::KeymapFormat; + +#[derive(Debug)] +pub struct VirtualKeyboardSettings { + pub file: File, + pub keymap_size: u32, + pub keymap_format: KeymapFormat, +} + +#[derive(Debug)] pub struct Settings { /// The identifier of the application. /// @@ -42,6 +51,8 @@ pub struct Settings { /// /// [`Canvas`]: crate::widget::Canvas pub antialiasing: bool, + + pub virtual_keyboard_support: Option, } impl Default for Settings @@ -57,6 +68,7 @@ where default_font: Font::default(), default_text_size: Pixels(16.0), antialiasing: false, + virtual_keyboard_support: None, } } }