diff --git a/Cargo.toml b/Cargo.toml index 55c60838..cfb23c6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,6 @@ panic = "abort" [profile.dev] panic = "abort" -codegen-units = 1 [dependencies] uapi = "0.2.7" diff --git a/default-config/src/lib.rs b/default-config/src/lib.rs index 8542ae1c..139134dc 100644 --- a/default-config/src/lib.rs +++ b/default-config/src/lib.rs @@ -1,9 +1,8 @@ use i4config::keyboard::mods::{Modifiers, ALT, CTRL, SHIFT}; -use i4config::keyboard::syms::{ - SYM_Super_L, SYM_h, SYM_j, SYM_k, SYM_l, SYM_r, SYM_t, SYM_x, SYM_y, -}; +use i4config::keyboard::syms::{SYM_h, SYM_j, SYM_k, SYM_l, SYM_r, SYM_t, SYM_x, SYM_y, SYM_b, SYM_Super_L}; use i4config::Direction::{Down, Left, Right, Up}; -use i4config::{config, create_seat, input_devices, on_new_input_device, Command, Seat}; +use i4config::{config, create_seat, input_devices, on_new_input_device, Command, Seat, InputDevice}; +use i4config::embedded::grab_keyboard; const MOD: Modifiers = ALT; @@ -14,18 +13,19 @@ fn configure_seat(s: Seat) { let (rate, delay) = s.repeat_rate(); let new_rate = rate - delta; let new_delay = delay + 10 * delta; + log::info!("Changing repeat rate to {}/{}", new_rate, new_delay); s.set_repeat_rate(new_rate, new_delay); }; s.bind(CTRL | SHIFT | SYM_l, move || change_rate(-1)); s.bind(CTRL | SHIFT | SYM_r, move || change_rate(1)); - s.bind(CTRL | SYM_h, move || s.focus(Left)); - s.bind(CTRL | SYM_j, move || s.focus(Down)); - s.bind(CTRL | SYM_k, move || s.focus(Up)); - s.bind(CTRL | SYM_l, move || s.focus(Right)); + s.bind(MOD | SYM_h, move || s.focus(Left)); + s.bind(MOD | SYM_j, move || s.focus(Down)); + s.bind(MOD | SYM_k, move || s.focus(Up)); + s.bind(MOD | SYM_l, move || s.focus(Right)); - s.bind(CTRL | SYM_t, move || { + s.bind(MOD | SYM_t, move || { s.set_split(s.split().other()); }); @@ -34,8 +34,24 @@ fn configure_seat(s: Seat) { s.bind(MOD | SHIFT | SYM_k, move || s.move_(Up)); s.bind(MOD | SHIFT | SYM_l, move || s.move_(Right)); - s.bind(SYM_x, || Command::new("alacritty").spawn()); - s.bind(SYM_y, || Command::new("sleep").arg("100").spawn()); + s.bind(SYM_Super_L, || Command::new("alacritty").spawn()); + + fn do_grab(s: Seat, grab: bool) { + for device in s.input_devices() { + if let InputDevice::Keyboard(kb) = device { + log::info!("{}rabbing keyboard {:?}", if grab { "G" } else { "Ung" }, kb.0); + grab_keyboard(kb, grab); + } + } + if grab { + s.unbind(SYM_y); + s.bind(MOD | SYM_b, move || do_grab(s, false)); + } else { + s.unbind(MOD | SYM_b); + s.bind(SYM_y, move || do_grab(s, true)); + } + } + do_grab(s, false); } pub fn configure() { diff --git a/i4config/src/_private/client.rs b/i4config/src/_private/client.rs index 5b964010..f78e50d1 100644 --- a/i4config/src/_private/client.rs +++ b/i4config/src/_private/client.rs @@ -1,7 +1,7 @@ use crate::_private::ipc::{ClientMessage, InitMessage, Response, ServerMessage}; use crate::_private::{bincode_ops, logging, Config, ConfigEntry, ConfigEntryGen, VERSION}; use crate::keyboard::keymap::Keymap; -use crate::{Axis, Command, Direction, InputDevice, LogLevel, ModifiedKeySym, Seat}; +use crate::{Axis, Command, Direction, InputDevice, Keyboard, LogLevel, ModifiedKeySym, Seat}; use std::cell::{Cell, RefCell}; use std::collections::hash_map::Entry; use std::collections::HashMap; @@ -140,6 +140,10 @@ impl Client { }); } + pub fn grab(&self, kb: Keyboard, grab: bool) { + self.send(&ClientMessage::GrabKb { kb, grab }); + } + pub fn focus(&self, seat: Seat, direction: Direction) { self.send(&ClientMessage::Focus { seat, direction }); } @@ -206,8 +210,8 @@ impl Client { } } - pub fn get_input_devices(&self) -> Vec { - let res = self.with_response(|| self.send(&ClientMessage::GetInputDevices)); + pub fn get_input_devices(&self, seat: Option) -> Vec { + let res = self.with_response(|| self.send(&ClientMessage::GetInputDevices { seat })); match res { Response::GetInputDevices { devices } => devices, _ => { diff --git a/i4config/src/_private/ipc.rs b/i4config/src/_private/ipc.rs index 82ed7f89..2fa98246 100644 --- a/i4config/src/_private/ipc.rs +++ b/i4config/src/_private/ipc.rs @@ -1,7 +1,7 @@ use crate::keyboard::keymap::Keymap; use crate::keyboard::mods::Modifiers; use crate::keyboard::syms::KeySym; -use crate::{Axis, Direction, InputDevice, LogLevel, Seat}; +use crate::{Axis, Direction, InputDevice, Keyboard, LogLevel, Seat}; use bincode::{BorrowDecode, Decode, Encode}; #[derive(Encode, BorrowDecode, Debug)] @@ -64,7 +64,9 @@ pub enum ClientMessage<'a> { seat: Seat, }, GetSeats, - GetInputDevices, + GetInputDevices { + seat: Option, + }, AddShortcut { seat: Seat, mods: Modifiers, @@ -88,6 +90,10 @@ pub enum ClientMessage<'a> { seat: Seat, direction: Direction, }, + GrabKb { + kb: Keyboard, + grab: bool, + }, } #[derive(Encode, Decode, Debug)] diff --git a/i4config/src/embedded.rs b/i4config/src/embedded.rs new file mode 100644 index 00000000..c64e5617 --- /dev/null +++ b/i4config/src/embedded.rs @@ -0,0 +1,5 @@ +use crate::Keyboard; + +pub fn grab_keyboard(kb: Keyboard, grab: bool) { + get!().grab(kb, grab); +} diff --git a/i4config/src/lib.rs b/i4config/src/lib.rs index a7204291..7daf632a 100644 --- a/i4config/src/lib.rs +++ b/i4config/src/lib.rs @@ -10,6 +10,7 @@ mod macros; #[doc(hidden)] pub mod _private; pub mod keyboard; +pub mod embedded; #[derive(Encode, Decode, Copy, Clone, Debug)] pub enum LogLevel { @@ -122,6 +123,12 @@ impl Seat { pub fn set_split(self, axis: Axis) { get!().set_split(self, axis) } + + pub fn input_devices(self) -> Vec { + let mut res = vec![]; + (|| res = get!().get_input_devices(Some(self)))(); + res + } } pub fn get_seats() -> Vec { @@ -132,7 +139,7 @@ pub fn get_seats() -> Vec { pub fn input_devices() -> Vec { let mut res = vec![]; - (|| res = get!().get_input_devices())(); + (|| res = get!().get_input_devices(None))(); res } diff --git a/src/backend.rs b/src/backend.rs index 2d2886f4..32d76201 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -6,6 +6,9 @@ linear_ids!(OutputIds, OutputId); linear_ids!(KeyboardIds, KeyboardId); linear_ids!(MouseIds, MouseId); +pub trait Backend { +} + pub trait Output { fn id(&self) -> OutputId; fn removed(&self) -> bool; @@ -19,6 +22,7 @@ pub trait Keyboard { fn removed(&self) -> bool; fn event(&self) -> Option; fn on_change(&self, cb: Rc); + fn grab(&self, grab: bool); } pub trait Mouse { diff --git a/src/backends/dummy.rs b/src/backends/dummy.rs new file mode 100644 index 00000000..e3a544e0 --- /dev/null +++ b/src/backends/dummy.rs @@ -0,0 +1,15 @@ +use std::rc::Rc; +use crate::backend::Backend; + +pub struct DummyBackend { + +} + +impl DummyBackend { + pub fn new() -> Rc { + Rc::new(Self { }) + } +} + +impl Backend for DummyBackend { +} diff --git a/src/backends/mod.rs b/src/backends/mod.rs index 68b939e6..807f1e10 100644 --- a/src/backends/mod.rs +++ b/src/backends/mod.rs @@ -1 +1,2 @@ pub mod xorg; +pub mod dummy; diff --git a/src/backends/xorg.rs b/src/backends/xorg.rs index f69cc701..3939d6b2 100644 --- a/src/backends/xorg.rs +++ b/src/backends/xorg.rs @@ -1,7 +1,4 @@ -use crate::backend::{ - BackendEvent, KeyState, Keyboard, KeyboardEvent, KeyboardId, Mouse, MouseEvent, MouseId, - Output, OutputId, ScrollAxis, -}; +use crate::backend::{BackendEvent, KeyState, Keyboard, KeyboardEvent, KeyboardId, Mouse, MouseEvent, MouseId, Output, OutputId, ScrollAxis, Backend}; use crate::drm::drm::{Drm, DrmError}; use crate::drm::gbm::{GbmDevice, GbmError, GBM_BO_USE_RENDERING}; use crate::drm::{ModifiedFormat, INVALID_MODIFIER}; @@ -14,7 +11,7 @@ use crate::utils::clonecell::CloneCell; use crate::utils::copyhashmap::CopyHashMap; use crate::utils::ptr_ext::PtrExt; use crate::wheel::{WheelDispatcher, WheelId}; -use crate::{EventLoopError, NumCell, State, WheelError}; +use crate::{ErrorFmt, EventLoopError, NumCell, State, WheelError}; use isnt::std_1::primitive::IsntConstPtrExt; use rand::Rng; use std::cell::{Cell, RefCell}; @@ -214,6 +211,9 @@ pub struct XorgBackend { b: Cell, } +impl Backend for XorgBackend { +} + fn get_drm(con: &XcbCon) -> Result { unsafe { let mut err = ptr::null_mut(); @@ -555,7 +555,7 @@ impl XorgBackend { kb_id: self.state.kb_ids.next(), mouse_id: self.state.mouse_ids.next(), backend: self.clone(), - _kb: info.deviceid, + kb: info.deviceid, mouse: info.attachment, removed: Cell::new(false), kb_cb: Default::default(), @@ -998,7 +998,7 @@ struct XorgSeat { kb_id: KeyboardId, mouse_id: MouseId, backend: Rc, - _kb: ffi::xcb_input_device_id_t, + kb: ffi::xcb_input_device_id_t, mouse: ffi::xcb_input_device_id_t, removed: Cell, kb_cb: CloneCell>>, @@ -1080,6 +1080,43 @@ impl Keyboard for XorgSeat { fn on_change(&self, cb: Rc) { self.kb_cb.set(Some(cb)); } + + fn grab(&self, grab: bool) { + unsafe { + let con = &self.backend.con; + let mut err = ptr::null_mut(); + if grab { + let res = con.input.xcb_input_xi_grab_device( + con.c, + con.screen.root, + 0, + 0, + self.kb, + ffi::XCB_GRAB_MODE_ASYNC as _, + ffi::XCB_GRAB_MODE_ASYNC as _, + 1, + 0, + ptr::null(), + ); + let res = con.input.xcb_input_xi_grab_device_reply(con.c, res, &mut err); + let res = match con.check(res, err) { + Ok(r) => r, + Err(e) => { + log::error!("Could not grab device {}: {}", self.kb, ErrorFmt(e)); + return; + } + }; + if res.status != ffi::XCB_GRAB_STATUS_SUCCESS as _ { + log::error!("Could not grab device {}: status = {}", self.kb, res.status); + } + } else { + let cookie = con.input.xcb_input_xi_ungrab_device_checked(con.c, 0, self.kb); + if let Err(e) = con.check_cookie(cookie) { + log::error!("Could not ungrab device {}: {}", self.kb, ErrorFmt(e)); + } + } + } + } } impl Mouse for XorgSeat { diff --git a/src/config/handler.rs b/src/config/handler.rs index 5ee1df17..944e366c 100644 --- a/src/config/handler.rs +++ b/src/config/handler.rs @@ -1,12 +1,12 @@ use crate::backend::{KeyboardId, MouseId}; -use crate::ifs::wl_seat::WlSeatGlobal; -use crate::state::DeviceHandlerData; +use crate::ifs::wl_seat::{SeatId, WlSeatGlobal}; +use crate::state::{DeviceHandlerData}; use crate::tree::ContainerSplit; use crate::utils::copyhashmap::CopyHashMap; use crate::utils::debug_fn::debug_fn; use crate::utils::stack::Stack; use crate::xkbcommon::XkbKeymap; -use crate::{ErrorFmt, NumCell, State}; +use crate::{backend, ErrorFmt, NumCell, State}; use bincode::error::DecodeError; use i4config::_private::bincode_ops; use i4config::_private::ipc::{ClientMessage, Response, ServerMessage}; @@ -181,6 +181,14 @@ impl ConfigProxyHandler { Err(CphError::SeatDoesNotExist(seat)) } + fn get_kb(&self, kb: Keyboard) -> Result, CphError> { + let kbs = self.state.kb_handlers.borrow_mut(); + match kbs.get(&(KeyboardId::from_raw(kb.0 as _))) { + None => Err(CphError::KeyboardDoesNotExist(kb)), + Some(kb) => Ok(kb.kb.clone()), + } + } + fn get_keymap(&self, keymap: Keymap) -> Result, CphError> { match self.keymaps.get(&keymap) { Some(k) => Ok(k), @@ -229,18 +237,44 @@ impl ConfigProxyHandler { Ok(()) } - fn handle_get_input_devices(&self) -> Result<(), GetInputDevicesError> { + fn handle_remove_shortcut( + &self, + seat: Seat, + mods: Modifiers, + sym: KeySym, + ) -> Result<(), AddShortcutError> { + let seat = self.get_seat(seat)?; + seat.remove_shortcut(mods, sym); + Ok(()) + } + + fn handle_get_input_devices(&self, seat: Option) -> Result<(), GetInputDevicesError> { + let id = seat.map(|s| SeatId::from_raw(s.0 as _)); + let matches = |dhd: &DeviceHandlerData| { + let id = match id { + Some(id) => id, + _ => return true, + }; + if let Some(seat) = dhd.seat.get() { + return seat.id() == id; + } + false + }; let mut res = vec![]; { let devs = self.state.kb_handlers.borrow_mut(); for dev in devs.values() { - res.push(InputDevice::Keyboard(Keyboard(dev.id.raw() as _))); + if matches(&dev.data) { + res.push(InputDevice::Keyboard(Keyboard(dev.id.raw() as _))); + } } } { let devs = self.state.mouse_handlers.borrow_mut(); for dev in devs.values() { - res.push(InputDevice::Mouse(Mouse(dev.id.raw() as _))); + if matches(&dev.data) { + res.push(InputDevice::Mouse(Mouse(dev.id.raw() as _))); + } } } self.send(&ServerMessage::Response { @@ -277,6 +311,16 @@ impl ConfigProxyHandler { Ok(()) } + fn handle_grab( + &self, + kb: Keyboard, + grab: bool, + ) -> Result<(), GrabError> { + let kb = self.get_kb(kb)?; + kb.grab(grab); + Ok(()) + } + pub fn handle_request(&self, msg: &[u8]) { if let Err(e) = self.handle_request_(msg) { log::error!("Could not handle client request: {}", ErrorFmt(e)); @@ -311,13 +355,16 @@ impl ConfigProxyHandler { ClientMessage::AddShortcut { seat, mods, sym } => { self.handle_add_shortcut(seat, mods, sym)? } - ClientMessage::RemoveShortcut { .. } => {} + ClientMessage::RemoveShortcut { seat, mods, sym } => { + self.handle_remove_shortcut(seat, mods, sym)? + } ClientMessage::Focus { seat, direction } => self.handle_focus(seat, direction)?, ClientMessage::Move { seat, direction } => {} - ClientMessage::GetInputDevices => self.handle_get_input_devices()?, + ClientMessage::GetInputDevices { seat } => self.handle_get_input_devices(seat)?, ClientMessage::GetSeats => self.handle_get_seats()?, ClientMessage::RemoveSeat { .. } => {} ClientMessage::Run { prog, args, env } => self.handle_run(prog, args, env)?, + ClientMessage::GrabKb { kb, grab } => self.handle_grab(kb, grab)?, } Ok(()) } @@ -335,6 +382,8 @@ enum CphError { SetSeatError(#[from] SetSeatError), #[error("Could not process a `add_shortcut` request")] AddShortcutError(#[from] AddShortcutError), + #[error("Could not process a `remove_shortcut` request")] + RemoveShortcutError(#[from] RemoveShortcutError), #[error("Could not process a `get_input_devices` request")] GetInputDevicesError(#[from] GetInputDevicesError), #[error("Could not process a `get_seats` request")] @@ -353,12 +402,16 @@ enum CphError { GetSplitError(#[from] GetSplitError), #[error("Could not process a `run` request")] RunError(#[from] RunError), + #[error("Could not process a `grab` request")] + GrabError(#[from] GrabError), #[error("Device {0:?} does not exist")] DeviceDoesNotExist(InputDevice), #[error("Device {0:?} does not exist")] KeymapDoesNotExist(Keymap), #[error("Seat {0:?} does not exist")] SeatDoesNotExist(Seat), + #[error("Keyboard {0:?} does not exist")] + KeyboardDoesNotExist(Keyboard), #[error("Could not parse the message")] ParsingFailed(#[source] DecodeError), } @@ -389,6 +442,13 @@ enum AddShortcutError { } efrom!(AddShortcutError, CphError); +#[derive(Debug, Error)] +enum RemoveShortcutError { + #[error(transparent)] + CphError(#[from] Box), +} +efrom!(RemoveShortcutError, CphError); + #[derive(Debug, Error)] enum GetInputDevicesError {} @@ -446,3 +506,10 @@ enum RunError { #[error("The ol' forker is not available")] NoForker, } + +#[derive(Debug, Error)] +enum GrabError { + #[error(transparent)] + CphError(#[from] Box), +} +efrom!(GrabError, CphError); diff --git a/src/forker.rs b/src/forker.rs index 4dff516d..0bdf08c1 100644 --- a/src/forker.rs +++ b/src/forker.rs @@ -189,6 +189,7 @@ impl Forker { env::set_var("XDG_SESSION_TYPE", "wayland"); env::remove_var("DISPLAY"); env::remove_var("WAYLAND_DISPLAY"); + setup_name("the ol' forker"); setup_deathsig(ppid); reset_signals(); let socket = Rc::new(setup_fds(socket)); @@ -349,3 +350,10 @@ fn setup_deathsig(ppid: c::pid_t) { } } } + +fn setup_name(name: &str) { + unsafe { + let name = name.into_ustr(); + c::prctl(c::PR_SET_NAME, name.as_ptr()); + } +} diff --git a/src/ifs/wl_seat/event_handling.rs b/src/ifs/wl_seat/event_handling.rs index 1eb2c7b8..660f2a40 100644 --- a/src/ifs/wl_seat/event_handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -371,6 +371,10 @@ impl WlSeatGlobal { self.shortcuts.set((mods.0, keysym.0), mods); } + pub fn remove_shortcut(&self, mods: Modifiers, keysym: KeySym) { + self.shortcuts.remove(&(mods.0, keysym.0)); + } + pub fn trigger_tree_changed(&self) { self.tree_changed.trigger(); } diff --git a/src/main.rs b/src/main.rs index bdb58f48..318793d1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -40,6 +40,8 @@ use std::ops::Deref; use std::rc::Rc; use thiserror::Error; use wheel::Wheel; +use crate::backends::dummy::DummyBackend; +use crate::utils::clonecell::CloneCell; #[macro_use] mod macros; @@ -125,6 +127,7 @@ fn main_() -> Result<(), MainError> { let state = Rc::new(State { xkb_ctx, forker: Default::default(), + backend: CloneCell::new(DummyBackend::new()), default_keymap: xkb_keymap, eng: engine.clone(), el: el.clone(), @@ -152,13 +155,14 @@ fn main_() -> Result<(), MainError> { kb_handlers: Default::default(), }); forker.install(&state); + let backend = XorgBackend::new(&state)?; + state.backend.set(backend); let config = config::ConfigProxy::default(&state); state.config.set(Some(Rc::new(config))); let _global_event_handler = engine.spawn(tasks::handle_backend_events(state.clone())); let _slow_client_handler = engine.spawn(tasks::handle_slow_clients(state.clone())); let socket_path = Acceptor::install(&state)?; forker.setenv(b"WAYLAND_DISPLAY", socket_path.as_bytes()); - let _backend = XorgBackend::new(&state)?; el.run()?; state.clients.clear(); for (_, seat) in state.globals.seats.lock().deref() { diff --git a/src/state.rs b/src/state.rs index 007f1676..bfa6b9e3 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,7 +1,5 @@ use crate::async_engine::{AsyncEngine, SpawnedFuture}; -use crate::backend::{ - BackendEvent, KeyboardId, KeyboardIds, MouseId, MouseIds, OutputId, OutputIds, -}; +use crate::backend::{Backend, BackendEvent, Keyboard, KeyboardId, KeyboardIds, MouseId, MouseIds, OutputId, OutputIds}; use crate::client::{Client, Clients}; use crate::config::ConfigProxy; use crate::cursor::ServerCursors; @@ -27,6 +25,7 @@ use std::rc::Rc; pub struct State { pub xkb_ctx: XkbContext, pub forker: CloneCell>>, + pub backend: CloneCell>, pub default_keymap: Rc, pub eng: Rc, pub el: Rc, @@ -63,6 +62,7 @@ pub struct MouseData { pub struct KeyboardData { pub handler: SpawnedFuture<()>, pub id: KeyboardId, + pub kb: Rc, pub data: Rc, } diff --git a/src/tasks/device.rs b/src/tasks/device.rs index a7656e76..7177a972 100644 --- a/src/tasks/device.rs +++ b/src/tasks/device.rs @@ -14,7 +14,7 @@ pub trait DeviceApi: 'static { fn announce(&self, config: &ConfigProxy); fn announce_del(&self, config: &ConfigProxy); fn removed(&self) -> bool; - fn add(&self, state: &State, handler: SpawnedFuture<()>, data: Rc); + fn add(self: &Rc, state: &State, handler: SpawnedFuture<()>, data: Rc); fn remove(&self, state: &State); fn event(&self) -> Option; fn send(seat: &Rc, event: Self::Event); @@ -39,12 +39,13 @@ impl DeviceApi for dyn Keyboard { self.removed() } - fn add(&self, state: &State, handler: SpawnedFuture<()>, data: Rc) { + fn add(self: &Rc, state: &State, handler: SpawnedFuture<()>, data: Rc) { state.kb_handlers.borrow_mut().insert( self.id(), KeyboardData { handler, id: self.id(), + kb: self.clone(), data, }, ); @@ -82,7 +83,7 @@ impl DeviceApi for dyn Mouse { self.removed() } - fn add(&self, state: &State, handler: SpawnedFuture<()>, data: Rc) { + fn add(self: &Rc, state: &State, handler: SpawnedFuture<()>, data: Rc) { state.mouse_handlers.borrow_mut().insert( self.id(), MouseData { diff --git a/todo.md b/todo.md index d8879a52..82ab124b 100644 --- a/todo.md +++ b/todo.md @@ -4,8 +4,6 @@ - Container moving (kb) - Float toggle - Toplevel splitting -- Shortcuts -- Config - Workspaces - Float moving - Highlighting active @@ -19,3 +17,5 @@ - clipboard - primary selection - dnd +- Shortcuts +- Config