diff --git a/Cargo.lock b/Cargo.lock index 54ee283..982acb7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -590,6 +590,17 @@ dependencies = [ "iced_runtime", ] +[[package]] +name = "counter_virtual_keyboard" +version = "0.3.0-beta1" +dependencies = [ + "iced", + "iced_layershell", + "iced_runtime", + "tempfile", + "xkbcommon 0.6.0", +] + [[package]] name = "crc32fast" version = "1.4.2" @@ -1765,6 +1776,15 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memmap2" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49388d20533534cd19360ad3d6a7dadc885944aa802ba3995040c5ec11288c6" +dependencies = [ + "libc", +] + [[package]] name = "memmap2" version = "0.8.0" @@ -4146,6 +4166,17 @@ version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" +[[package]] +name = "xkbcommon" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c286371c44b3572d19b09196c129a8fff47d7704d6494daefb44fec10f0278ab" +dependencies = [ + "libc", + "memmap2 0.7.1", + "xkeysym", +] + [[package]] name = "xkbcommon" version = "0.7.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..7cad336 --- /dev/null +++ b/iced_examples/counter_virtual_keyboard/src/main.rs @@ -0,0 +1,202 @@ +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::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 }.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..fb056ab 100644 --- a/iced_layershell/src/actions.rs +++ b/iced_layershell/src/actions.rs @@ -18,6 +18,7 @@ pub enum LayershellCustomActions { AnchorChange(Anchor), LayerChange(Layer), SizeChange((u32, u32)), + VirtualKeyboardPressed { time: u32, key: u32 }, } #[derive(Debug, Clone, Copy)] diff --git a/iced_layershell/src/application.rs b/iced_layershell/src/application.rs index 622d507..437d222 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, time::Duration}; use crate::{ actions::{LayerShellActions, LayershellCustomActions}, clipboard::LayerShellClipboard, conversion, error::Error, + settings::VirtualKeyboardSettings, }; use iced_graphics::Compositor; @@ -20,7 +21,11 @@ use iced_style::application::StyleSheet; use iced_futures::{Executor, Runtime, Subscription}; -use layershellev::{LayerEvent, ReturnData, WindowState, WindowWrapper}; +use layershellev::{ + calloop::timer::{TimeoutAction, Timer}, + reexport::zwp_virtual_keyboard_v1, + LayerEvent, ReturnData, WindowState, WindowWrapper, +}; use futures::{channel::mpsc, SinkExt, StreamExt}; @@ -189,10 +194,32 @@ where let _ = ev.running_with_proxy(message_receiver, move |event, ev, _| { use layershellev::DispatchMessage; + let mut def_returndata = ReturnData::None; 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); + ev.set_virtual_keyboard(virtual_keyboard_in); + } LayerEvent::RequestMessages(message) => { if let DispatchMessage::MouseEnter { serial, .. } = message { pointer_serial = *serial; @@ -216,7 +243,7 @@ where } let poll = instance.as_mut().poll(&mut context); match poll { - task::Poll::Pending => 'peddingBlock: { + task::Poll::Pending => { if let Ok(Some(flow)) = control_receiver.try_next() { for flow in flow { match flow { @@ -232,31 +259,51 @@ where LayershellCustomActions::SizeChange((width, height)) => { ev.main_window().set_size((width, height)); } + LayershellCustomActions::VirtualKeyboardPressed { + time, + key, + } => { + use layershellev::reexport::wayland_client::KeyState; + let ky = ev.get_virtual_keyboard().unwrap(); + ky.key(time, key, KeyState::Pressed.into()); + + let eh = ev.get_loop_handler().unwrap(); + eh.insert_source( + Timer::from_duration(Duration::from_micros(100)), + move |_, _, state| { + let ky = state.get_virtual_keyboard().unwrap(); + + ky.key(time, key, KeyState::Released.into()); + TimeoutAction::Drop + }, + ) + .ok(); + } } } } LayerShellActions::Mouse(mouse) => { let Some(pointer) = ev.get_pointer() else { - break 'peddingBlock ReturnData::None; + return ReturnData::None; }; - break 'peddingBlock ReturnData::RequestSetCursorShape(( + return ReturnData::RequestSetCursorShape(( conversion::mouse_interaction(mouse), pointer.clone(), pointer_serial, )); } LayerShellActions::RedrawAll => { - break 'peddingBlock ReturnData::RedrawAllRequest; + return ReturnData::RedrawAllRequest; } LayerShellActions::RedrawWindow(index) => { - break 'peddingBlock ReturnData::RedrawIndexRequest(index); + return ReturnData::RedrawIndexRequest(index); } _ => {} } } } - ReturnData::None + def_returndata } task::Poll::Ready(_) => ReturnData::RequestExist, } diff --git a/iced_layershell/src/lib.rs b/iced_layershell/src/lib.rs index 9583757..aed390a 100644 --- a/iced_layershell/src/lib.rs +++ b/iced_layershell/src/lib.rs @@ -12,6 +12,7 @@ mod sandbox; pub mod settings; pub mod reexport { + pub use layershellev::reexport::wayland_client::wl_keyboard; pub use layershellev::reexport::Anchor; pub use layershellev::reexport::KeyboardInteractivity; pub use layershellev::reexport::Layer; diff --git a/iced_layershell/src/multi_window.rs b/iced_layershell/src/multi_window.rs index 307e13c..1b21025 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, time::Duration}; use crate::{ actions::{LayerShellActions, LayershellCustomActions}, @@ -22,7 +23,11 @@ use iced_style::application::StyleSheet; use iced_futures::{Executor, Runtime, Subscription}; -use layershellev::{LayerEvent, ReturnData, WindowState}; +use layershellev::{ + calloop::timer::{TimeoutAction, Timer}, + reexport::zwp_virtual_keyboard_v1, + LayerEvent, ReturnData, WindowState, +}; use futures::{channel::mpsc, SinkExt, StreamExt}; @@ -198,11 +203,33 @@ where 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); + ev.set_virtual_keyboard(virtual_keyboard_in); + } LayerEvent::RequestMessages(message) => 'outside: { match message { DispatchMessage::RequestRefresh { width, height } => { @@ -265,9 +292,30 @@ where LayershellCustomActions::SizeChange((width, height)) => { window.set_size((width, height)); } + LayershellCustomActions::VirtualKeyboardPressed { + time, + key, + } => { + use layershellev::reexport::wayland_client::KeyState; + let ky = ev.get_virtual_keyboard().unwrap(); + ky.key(time, key, KeyState::Pressed.into()); + + let eh = ev.get_loop_handler().unwrap(); + eh.insert_source( + Timer::from_duration(Duration::from_micros(100)), + move |_, _, state| { + let ky = state.get_virtual_keyboard().unwrap(); + + ky.key(time, key, KeyState::Released.into()); + TimeoutAction::Drop + }, + ) + .ok(); + } } } } + LayerShellActions::Mouse(mouse) => { let Some(pointer) = ev.get_pointer() else { break 'peddingBlock ReturnData::None; @@ -289,7 +337,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, } } } diff --git a/layershellev/src/lib.rs b/layershellev/src/lib.rs index 4f30843..ee1df8f 100644 --- a/layershellev/src/lib.rs +++ b/layershellev/src/lib.rs @@ -111,9 +111,12 @@ //! } //! ``` //! +use sctk::reexports::calloop::LoopHandle; pub use waycrate_xkbkeycode::keyboard; pub use waycrate_xkbkeycode::xkb_keyboard; +pub use sctk::reexports::calloop; + mod events; mod strtoshape; @@ -459,6 +462,7 @@ pub struct WindowState { pointer: Option, touch: Option, + virtual_keyboard: Option, // states namespace: String, @@ -471,6 +475,7 @@ pub struct WindowState { // settings use_display_handle: bool, + loop_handler: Option>, } impl WindowState { @@ -657,6 +662,7 @@ impl Default for WindowState { xdg_output_manager: None, globals: None, fractional_scale_manager: None, + virtual_keyboard: None, seat: None, keyboard_state: None, @@ -672,11 +678,27 @@ impl Default for WindowState { margin: None, use_display_handle: false, + loop_handler: None, } } } impl WindowState { + /// You can save the virtual_keyboard here + pub fn set_virtual_keyboard(&mut self, keyboard: ZwpVirtualKeyboardV1) { + self.virtual_keyboard = Some(keyboard); + } + + /// get the saved virtual_keyboard + pub fn get_virtual_keyboard(&self) -> Option<&ZwpVirtualKeyboardV1> { + self.virtual_keyboard.as_ref() + } + + /// with loop_handler you can do more thing + pub fn get_loop_handler(&self) -> Option<&LoopHandle<'static, Self>> { + self.loop_handler.as_ref() + } + /// get the unit with the index returned by eventloop pub fn get_unit(&mut self, index: usize) -> &mut WindowStateUnit { &mut self.units[index] @@ -1377,6 +1399,8 @@ impl WindowState { .insert(event_loop.handle()) .expect("Failed to init wayland source"); + self.loop_handler = Some(event_loop.handle()); + 'out: loop { event_loop.dispatch(Duration::from_millis(1), &mut self)?;