Skip to content

Commit 560998d

Browse files
committed
Add action preregistering api for xdg-desktop-portal
1 parent 29cb8d0 commit 560998d

9 files changed

+284
-155
lines changed

Cargo.lock

+199-121
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ napi-derive = { version = "2", optional = true }
1616
[target.'cfg(target_os = "linux")'.dependencies]
1717
xcb = { version = "1", features = ["x11", "xkb", "as-raw-xcb-connection"] }
1818
xkbcommon = { version = "0.8", features = ["x11"] }
19-
ashpd = { version = "0.10", features = ["wayland"] }
19+
ashpd = { version = "0.10", features = ["wayland", "async-std"], default-features = false }
2020

2121
[features]
2222
default = ["node"]

devenv.nix

+6-3
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,19 @@
22

33
{
44
packages = with pkgs; [
5+
cmake
6+
libclang
7+
pkg-config
8+
59
xorg.libX11
610
xorg.libXi
711
xorg.libXtst
812
xorg.libxcb
913
libxkbcommon
1014
xorg.libxkbfile
11-
cmake
12-
libclang
13-
pkg-config
15+
1416
wayland
17+
1518
ninja
1619
llvmPackages_latest.llvm
1720
cargo-xwin

lib/venbind.d.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
export class Venbind {
2-
startKeybinds(callback: (err: null | Error, id: number) => void): void
3-
registerKeybind(keybind: string, keybindId: number): void;
4-
unregisterKeybind(keybindId: number): void;
2+
startKeybinds(callback: (err: null | Error, id: number) => void): void;
3+
registerKeybind(keybind: string, keybindId: number): void;
4+
unregisterKeybind(keybindId: number): void;
5+
preregisterKeybinds(actions: PreRegisterAction[]): void;
6+
}
7+
export interface PreRegisterAction {
8+
id: number
9+
name: string
510
}

src/js.rs

+14
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ use napi_derive::napi;
88

99
use crate::structs::{KeybindId, KeybindTrigger};
1010

11+
pub use crate::structs::PreRegisterAction;
12+
1113
#[napi(ts_args_type = "callback: (err: null | Error, id: number) => void")]
1214
pub fn start_keybinds(callback: JsFunction) -> Result<()> {
1315
let (tx, rx) = channel::<KeybindTrigger>();
@@ -43,3 +45,15 @@ pub fn register_keybind(keybind: String, #[napi(ts_arg_type = "number")] id: Key
4345
pub fn unregister_keybind(#[napi(ts_arg_type = "number")] id: KeybindId) {
4446
crate::unregister_keybind(id);
4547
}
48+
49+
#[napi]
50+
pub fn preregister_keybinds(#[napi(ts_arg_type = "PreRegisterAction[]")] actions: Vec<PreRegisterAction>) {
51+
#[cfg(target_os = "linux")]
52+
{
53+
crate::platform::xdg_preregister_keybinds(actions).unwrap();
54+
}
55+
#[cfg(not(target_os = "linux"))]
56+
{
57+
panic!("Can't preregister keybinds on non-linux!");
58+
}
59+
}

src/lib.rs

+25-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
mod errors;
2-
#[cfg(all(feature = "node", not(test)))]
3-
pub mod js; // BREAKS TESTS
2+
#[cfg(feature = "node")]
3+
pub mod js;
44
mod structs;
55

66
#[cfg_attr(target_os = "linux", path = "linux.rs")]
@@ -27,16 +27,36 @@ pub fn unregister_keybind(id: KeybindId) {
2727
mod tests {
2828
use std::{sync::mpsc::channel, thread};
2929

30-
use crate::{register_keybind, start_keybinds, structs::KeybindTrigger};
30+
use crate::{register_keybind, start_keybinds, structs::{KeybindTrigger, PreRegisterAction}};
3131
#[test]
3232
fn demo() {
3333
let (tx, rx) = channel::<KeybindTrigger>();
3434
thread::spawn(|| {
3535
start_keybinds(tx);
3636
});
3737
thread::sleep(std::time::Duration::from_secs(2));
38-
register_keybind("shift+alt+m".to_string(), 1);
39-
// register_keybind("SHIFT+CTRL+a".to_string(), 2);
38+
#[cfg(target_os = "linux")]
39+
if crate::is_wayland() || crate::use_xdg_on_x11() {
40+
crate::xdg_preregister_keybinds(vec![
41+
PreRegisterAction {
42+
id: 1,
43+
name: "Does a thing!".to_owned(),
44+
},
45+
PreRegisterAction {
46+
id: 2,
47+
name: "Does another thing!".to_owned(),
48+
}
49+
]).unwrap();
50+
} else {
51+
register_keybind("shift+alt+m".to_string(), 1);
52+
register_keybind("SHIFT+CTRL+a".to_string(), 2);
53+
}
54+
#[cfg(not(target_os = "linux"))]
55+
{
56+
register_keybind("shift+alt+m".to_string(), 1);
57+
register_keybind("SHIFT+CTRL+a".to_string(), 2);
58+
}
59+
4060
loop {
4161
match rx.recv() {
4262
Err(err) => {

src/linux.rs

+22-20
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use ashpd::{
55
use std::{
66
cell::RefCell,
77
env,
8-
sync::{mpsc::Sender, LazyLock, Mutex, OnceLock},
8+
sync::{mpsc::Sender, LazyLock, Mutex, OnceLock}
99
};
1010
use uiohook_sys::{
1111
_event_type_EVENT_KEY_PRESSED, _uiohook_event, hook_run, hook_set_dispatch_proc,
@@ -14,8 +14,11 @@ use uiohook_sys::{
1414
use xcb::Extension;
1515
use xkbcommon::xkb::{self, State};
1616

17-
use crate::errors::{Result, VenbindError};
1817
use crate::structs::{Keybind, KeybindId, KeybindTrigger, Keybinds};
18+
use crate::{
19+
errors::{Result, VenbindError},
20+
js::PreRegisterAction,
21+
};
1922

2023
static KEYBINDS: LazyLock<Mutex<Keybinds>> = LazyLock::new(|| Mutex::new(Keybinds::default()));
2124
static TX: OnceLock<Sender<KeybindTrigger>> = OnceLock::new();
@@ -42,10 +45,9 @@ pub(crate) fn start_keybinds_internal(tx: Sender<KeybindTrigger>) -> Result<()>
4245

4346
pub(crate) fn register_keybind_internal(keybind: String, id: KeybindId) -> Result<()> {
4447
if is_wayland() || use_xdg_on_x11() {
45-
futures::executor::block_on(xdg_register_keybind(keybind, id))
46-
} else {
47-
uiohook_register_keybind(keybind, id)
48+
panic!("Keybinds should be preregistered on wayland!");
4849
}
50+
uiohook_register_keybind(keybind, id)
4951
}
5052

5153
pub(crate) fn unregister_keybind_internal(id: KeybindId) -> Result<()> {
@@ -97,20 +99,19 @@ async fn xdg_input_thread() {
9799
}
98100
}
99101

100-
async fn xdg_register_keybind(keybind: String, id: KeybindId) -> Result<()> {
101-
let new_keybind = Keybind::from_string(keybind);
102-
let mut keybinds = KEYBINDS.lock().unwrap();
103-
keybinds.register_keybind(new_keybind.clone(), id);
104-
105-
let shortcut = NewShortcut::new(format!("{}", id), id.to_string())
106-
.preferred_trigger(Some(new_keybind.to_string().as_str()));
107-
let lock = XDG_STATE.lock().unwrap();
108-
let state = lock.as_ref().unwrap();
109-
110-
state
111-
.portal
112-
.bind_shortcuts(&state.session, &[shortcut], None)
113-
.await?;
102+
pub(crate) fn xdg_preregister_keybinds(actions: Vec<PreRegisterAction>) -> Result<()> {
103+
futures::executor::block_on(async move {
104+
let shortcuts: Vec<NewShortcut> = actions
105+
.iter()
106+
.map(|x| NewShortcut::new(format!("{}", x.id), x.name.clone()))
107+
.collect();
108+
let lock = XDG_STATE.lock().unwrap();
109+
let state = lock.as_ref().unwrap();
110+
state
111+
.portal
112+
.bind_shortcuts(&state.session, &shortcuts, None)
113+
.await
114+
})?;
114115
Ok(())
115116
}
116117

@@ -186,7 +187,8 @@ fn uiohook_register_keybind(keybind: String, id: KeybindId) -> Result<()> {
186187

187188
#[inline]
188189
pub(crate) fn is_wayland() -> bool {
189-
env::var("WAYLAND_DISPLAY").is_ok()
190+
env::var("XDG_SESSION_TYPE").is_ok_and(|x| x == "wayland".to_owned())
191+
|| env::var("WAYLAND_DISPLAY").is_ok()
190192
}
191193

192194
#[inline]

src/structs.rs

+9
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@ use std::collections::HashMap;
22

33
pub type KeybindId = u32;
44

5+
#[cfg(feature = "node")]
6+
use napi_derive::napi;
7+
8+
#[cfg_attr(feature = "node", napi(object))]
9+
pub struct PreRegisterAction {
10+
pub id: i32,
11+
pub name: String,
12+
}
13+
514
#[derive(Default)]
615
pub struct Keybinds {
716
keybinds: HashMap<Keybind, KeybindId>,

src/windows.rs

-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ static TX: OnceLock<Sender<KeybindTrigger>> = OnceLock::new();
1717
pub(crate) fn start_keybinds_internal(tx: Sender<KeybindTrigger>) -> Result<()> {
1818
TX.set(tx).unwrap();
1919

20-
// don't make a state with an xcb connection (state_new_from_device) so it only chooses the first layout
21-
// TODO: if someone's first selected layout is not a latin based layout horrible things happen
2220
unsafe {
2321
hook_set_dispatch_proc(Some(dispatch_proc));
2422
if hook_run() != UIOHOOK_SUCCESS as i32 {

0 commit comments

Comments
 (0)