Add keybind descriptions

This commit is contained in:
Ottatop 2024-06-15 14:06:23 -05:00
parent d70a0abda5
commit 5b241a626b
9 changed files with 456 additions and 122 deletions

View file

@ -34,12 +34,18 @@ require("pinnacle").setup(function(Pinnacle)
-- mod_key + alt + q = Quit Pinnacle -- mod_key + alt + q = Quit Pinnacle
Input.keybind({ mod_key, "alt" }, "q", function() Input.keybind({ mod_key, "alt" }, "q", function()
Pinnacle.quit() Pinnacle.quit()
end) end, {
group = "Compositor",
description = "Quit Pinnacle",
})
-- mod_key + alt + r = Reload config -- mod_key + alt + r = Reload config
Input.keybind({ mod_key, "alt" }, "r", function() Input.keybind({ mod_key, "alt" }, "r", function()
Pinnacle.reload_config() Pinnacle.reload_config()
end) end, {
group = "Compositor",
description = "Reload the config",
})
-- mod_key + alt + c = Close window -- mod_key + alt + c = Close window
Input.keybind({ mod_key, "alt" }, "c", function() Input.keybind({ mod_key, "alt" }, "c", function()
@ -47,12 +53,18 @@ require("pinnacle").setup(function(Pinnacle)
if focused then if focused then
focused:close() focused:close()
end end
end) end, {
group = "Window",
description = "Close the focused window",
})
-- mod_key + alt + Return = Spawn `terminal` -- mod_key + alt + Return = Spawn `terminal`
Input.keybind({ mod_key }, key.Return, function() Input.keybind({ mod_key }, key.Return, function()
Process.spawn(terminal) Process.spawn(terminal)
end) end, {
group = "Process",
description = "Spawn `alacritty`",
})
-- mod_key + alt + space = Toggle floating -- mod_key + alt + space = Toggle floating
Input.keybind({ mod_key, "alt" }, key.space, function() Input.keybind({ mod_key, "alt" }, key.space, function()
@ -61,7 +73,10 @@ require("pinnacle").setup(function(Pinnacle)
focused:toggle_floating() focused:toggle_floating()
focused:raise() focused:raise()
end end
end) end, {
group = "Window",
description = "Toggle floating on the focused window",
})
-- mod_key + f = Toggle fullscreen -- mod_key + f = Toggle fullscreen
Input.keybind({ mod_key }, "f", function() Input.keybind({ mod_key }, "f", function()
@ -70,7 +85,10 @@ require("pinnacle").setup(function(Pinnacle)
focused:toggle_fullscreen() focused:toggle_fullscreen()
focused:raise() focused:raise()
end end
end) end, {
group = "Window",
description = "Toggle fullscreen on the focused window",
})
-- mod_key + m = Toggle maximized -- mod_key + m = Toggle maximized
Input.keybind({ mod_key }, "m", function() Input.keybind({ mod_key }, "m", function()
@ -79,7 +97,10 @@ require("pinnacle").setup(function(Pinnacle)
focused:toggle_maximized() focused:toggle_maximized()
focused:raise() focused:raise()
end end
end) end, {
group = "Window",
description = "Toggle maximized on the focused window",
})
---------------------- ----------------------
-- Tags and Outputs -- -- Tags and Outputs --
@ -109,12 +130,18 @@ require("pinnacle").setup(function(Pinnacle)
-- mod_key + 1-5 = Switch to tags 1-5 -- mod_key + 1-5 = Switch to tags 1-5
Input.keybind({ mod_key }, tag_name, function() Input.keybind({ mod_key }, tag_name, function()
Tag.get(tag_name):switch_to() Tag.get(tag_name):switch_to()
end) end, {
group = "Tag",
description = "Switch to tag " .. tag_name,
})
-- mod_key + shift + 1-5 = Toggle tags 1-5 -- mod_key + shift + 1-5 = Toggle tags 1-5
Input.keybind({ mod_key, "shift" }, tag_name, function() Input.keybind({ mod_key, "shift" }, tag_name, function()
Tag.get(tag_name):toggle_active() Tag.get(tag_name):toggle_active()
end) end, {
group = "Tag",
description = "Toggle tag " .. tag_name,
})
-- mod_key + alt + 1-5 = Move window to tags 1-5 -- mod_key + alt + 1-5 = Move window to tags 1-5
Input.keybind({ mod_key, "alt" }, tag_name, function() Input.keybind({ mod_key, "alt" }, tag_name, function()
@ -122,7 +149,10 @@ require("pinnacle").setup(function(Pinnacle)
if focused then if focused then
focused:move_to_tag(Tag.get(tag_name) --[[@as TagHandle]]) focused:move_to_tag(Tag.get(tag_name) --[[@as TagHandle]])
end end
end) end, {
group = "Tag",
description = "Move the focused window to tag " .. tag_name,
})
-- mod_key + shift + alt + 1-5 = Toggle tags 1-5 on window -- mod_key + shift + alt + 1-5 = Toggle tags 1-5 on window
Input.keybind({ mod_key, "shift", "alt" }, tag_name, function() Input.keybind({ mod_key, "shift", "alt" }, tag_name, function()
@ -130,7 +160,10 @@ require("pinnacle").setup(function(Pinnacle)
if focused then if focused then
focused:toggle_tag(Tag.get(tag_name) --[[@as TagHandle]]) focused:toggle_tag(Tag.get(tag_name) --[[@as TagHandle]])
end end
end) end, {
group = "Tag",
description = "Toggle tag " .. tag_name .. " on the focused window",
})
end end
-------------------- --------------------
@ -226,7 +259,10 @@ require("pinnacle").setup(function(Pinnacle)
Layout.request_layout(focused_op) Layout.request_layout(focused_op)
end end
end end
end) end, {
group = "Layout",
description = "Cycle the layout forward on the first active tag",
})
-- mod_key + shift + space = Cycle backward one layout on the focused output -- mod_key + shift + space = Cycle backward one layout on the focused output
Input.keybind({ mod_key, "shift" }, key.space, function() Input.keybind({ mod_key, "shift" }, key.space, function()
@ -257,7 +293,10 @@ require("pinnacle").setup(function(Pinnacle)
Layout.request_layout(focused_op) Layout.request_layout(focused_op)
end end
end end
end) end, {
group = "Layout",
description = "Cycle the layout backward on the first active tag",
})
Input.set_libinput_settings({ Input.set_libinput_settings({
tap = true, tap = true,

View file

@ -258,6 +258,8 @@ local pinnacle_input_v0alpha1_Modifier = {
---@field modifiers pinnacle.input.v0alpha1.Modifier[]? ---@field modifiers pinnacle.input.v0alpha1.Modifier[]?
---@field raw_code integer? ---@field raw_code integer?
---@field xkb_name string? ---@field xkb_name string?
---@field group string?
---@field description string?
---@class pinnacle.input.v0alpha1.SetKeybindResponse ---@class pinnacle.input.v0alpha1.SetKeybindResponse
@ -274,6 +276,18 @@ local pinnacle_input_v0alpha1_SetMousebindRequest_MouseEdge = {
---@class pinnacle.input.v0alpha1.SetMousebindResponse ---@class pinnacle.input.v0alpha1.SetMousebindResponse
---@class pinnacle.input.v0alpha1.KeybindDescriptionsRequest
---@class pinnacle.input.v0alpha1.KeybindDescriptionsResponse
---@field descriptions pinnacle.input.v0alpha1.KeybindDescription[]?
---@class pinnacle.input.v0alpha1.KeybindDescription
---@field modifiers pinnacle.input.v0alpha1.Modifier[]?
---@field raw_code integer?
---@field xkb_name string?
---@field group string?
---@field description string?
---@class SetXkbConfigRequest ---@class SetXkbConfigRequest
---@field rules string? ---@field rules string?
---@field variant string? ---@field variant string?
@ -729,6 +743,13 @@ defs.pinnacle = {
response = "pinnacle.input.v0alpha1.SetMousebindResponse", response = "pinnacle.input.v0alpha1.SetMousebindResponse",
}, },
---@type GrpcRequestArgs ---@type GrpcRequestArgs
KeybindDescriptions = {
service = "pinnacle.input.v0alpha1.InputService",
method = "KeybindDescriptions",
request = "pinnacle.input.v0alpha1.KeybindDescriptionsRequest",
response = "pinnacle.input.v0alpha1.KeybindDescriptionsResponse",
},
---@type GrpcRequestArgs
SetXkbConfig = { SetXkbConfig = {
service = "pinnacle.input.v0alpha1.InputService", service = "pinnacle.input.v0alpha1.InputService",
method = "SetXkbConfig", method = "SetXkbConfig",

View file

@ -74,6 +74,10 @@ local input = {
} }
input.mouse_button_values = mouse_button_values input.mouse_button_values = mouse_button_values
---@class KeybindInfo
---@field group string? The group to place this keybind in. Used for the keybind list.
---@field description string? The description of this keybind. Used for the keybind list.
---Set a keybind. If called with an already existing keybind, it gets replaced. ---Set a keybind. If called with an already existing keybind, it gets replaced.
--- ---
---You must provide three arguments: ---You must provide three arguments:
@ -111,7 +115,8 @@ input.mouse_button_values = mouse_button_values
---@param mods Modifier[] The modifiers that need to be held down for the bind to trigger ---@param mods Modifier[] The modifiers that need to be held down for the bind to trigger
---@param key Key | string The key used to trigger the bind ---@param key Key | string The key used to trigger the bind
---@param action fun() The function to run when the bind is triggered ---@param action fun() The function to run when the bind is triggered
function input.keybind(mods, key, action) ---@param keybind_info KeybindInfo?
function input.keybind(mods, key, action, keybind_info)
local raw_code = nil local raw_code = nil
local xkb_name = nil local xkb_name = nil
@ -130,6 +135,8 @@ function input.keybind(mods, key, action)
modifiers = mod_values, modifiers = mod_values,
raw_code = raw_code, raw_code = raw_code,
xkb_name = xkb_name, xkb_name = xkb_name,
group = keybind_info and keybind_info.group,
description = keybind_info and keybind_info.description,
}, action) }, action)
end end
@ -165,6 +172,31 @@ function input.mousebind(mods, button, edge, action)
}, action) }, action)
end end
---@class KeybindDescription
---@field modifiers Modifier[]
---@field raw_code integer
---@field xkb_name string
---@field group string?
---@field description string?
---Get all keybinds along with their descriptions
---
---@return KeybindDescription[]
function input.keybind_descriptions()
---@type pinnacle.input.v0alpha1.KeybindDescriptionsResponse
local descs = client.unary_request(input_service.KeybindDescriptions, {})
local descs = descs.descriptions or {}
local ret = {}
for _, desc in ipairs(descs) do
desc.modifiers = desc.modifiers or {}
table.insert(ret, desc)
end
return ret
end
---@class XkbConfig ---@class XkbConfig
---@field rules string? ---@field rules string?
---@field model string? ---@field model string?

View file

@ -18,9 +18,25 @@ message SetKeybindRequest {
uint32 raw_code = 2; uint32 raw_code = 2;
string xkb_name = 3; string xkb_name = 3;
} }
optional string group = 4;
optional string description = 5;
} }
message SetKeybindResponse {} message SetKeybindResponse {}
message KeybindDescriptionsRequest {}
message KeybindDescriptionsResponse {
repeated KeybindDescription descriptions = 1;
}
message KeybindDescription {
repeated Modifier modifiers = 1;
optional uint32 raw_code = 2;
optional string xkb_name = 3;
optional string group = 4;
optional string description = 5;
}
message SetMousebindRequest { message SetMousebindRequest {
repeated Modifier modifiers = 1; repeated Modifier modifiers = 1;
// A button code corresponding to one of the `BTN_` prefixed definitions in input-event-codes.h // A button code corresponding to one of the `BTN_` prefixed definitions in input-event-codes.h
@ -131,6 +147,8 @@ service InputService {
rpc SetKeybind(SetKeybindRequest) returns (stream SetKeybindResponse); rpc SetKeybind(SetKeybindRequest) returns (stream SetKeybindResponse);
rpc SetMousebind(SetMousebindRequest) returns (stream SetMousebindResponse); rpc SetMousebind(SetMousebindRequest) returns (stream SetMousebindResponse);
rpc KeybindDescriptions(KeybindDescriptionsRequest) returns (KeybindDescriptionsResponse);
rpc SetXkbConfig(SetXkbConfigRequest) returns (google.protobuf.Empty); rpc SetXkbConfig(SetXkbConfigRequest) returns (google.protobuf.Empty);
rpc SetRepeatRate(SetRepeatRateRequest) returns (google.protobuf.Empty); rpc SetRepeatRate(SetRepeatRateRequest) returns (google.protobuf.Empty);

View file

@ -1,4 +1,5 @@
use pinnacle_api::input::libinput::LibinputSetting; use pinnacle_api::input::libinput::LibinputSetting;
use pinnacle_api::input::KeybindInfo;
use pinnacle_api::layout::{ use pinnacle_api::layout::{
CornerLayout, CornerLocation, CyclingLayoutManager, DwindleLayout, FairLayout, MasterSide, CornerLayout, CornerLocation, CyclingLayoutManager, DwindleLayout, FairLayout, MasterSide,
MasterStackLayout, SpiralLayout, MasterStackLayout, SpiralLayout,
@ -51,6 +52,15 @@ async fn main() {
let terminal = "alacritty"; let terminal = "alacritty";
input.keybind(
[mod_key],
'i',
|| {
dbg!(input.keybind_descriptions().collect::<Vec<_>>());
},
None,
);
//------------------------ //------------------------
// Mousebinds | // Mousebinds |
//------------------------ //------------------------
@ -70,54 +80,110 @@ async fn main() {
//------------------------ //------------------------
// `mod_key + alt + q` quits Pinnacle // `mod_key + alt + q` quits Pinnacle
input.keybind([mod_key, Mod::Alt], 'q', || { input.keybind(
#[cfg(feature = "snowcap")] [mod_key, Mod::Alt],
snowcap.integration.quit_prompt().show(); 'q',
|| {
#[cfg(feature = "snowcap")]
snowcap.integration.quit_prompt().show();
#[cfg(not(feature = "snowcap"))] #[cfg(not(feature = "snowcap"))]
pinnacle.quit(); pinnacle.quit();
}); },
KeybindInfo {
group: Some("Compositor".into()),
description: Some("Quit Pinnacle".into()),
},
);
// `mod_key + alt + r` reloads the config // `mod_key + alt + r` reloads the config
input.keybind([mod_key, Mod::Alt], 'r', || { input.keybind(
pinnacle.reload_config(); [mod_key, Mod::Alt],
}); 'r',
|| {
pinnacle.reload_config();
},
KeybindInfo {
group: Some("Compositor".into()),
description: Some("Reload the config".into()),
},
);
// `mod_key + alt + c` closes the focused window // `mod_key + alt + c` closes the focused window
input.keybind([mod_key, Mod::Alt], 'c', || { input.keybind(
if let Some(window) = window.get_focused() { [mod_key, Mod::Alt],
window.close(); 'c',
} || {
}); if let Some(window) = window.get_focused() {
window.close();
}
},
KeybindInfo {
group: Some("Window".into()),
description: Some("Close the focused window".into()),
},
);
// `mod_key + Return` spawns a terminal // `mod_key + Return` spawns a terminal
input.keybind([mod_key], Keysym::Return, move || { input.keybind(
process.spawn([terminal]); [mod_key],
}); Keysym::Return,
move || {
process.spawn([terminal]);
},
KeybindInfo {
group: Some("Process".into()),
description: Some("Spawn `alacritty`".into()),
},
);
// `mod_key + alt + space` toggles floating // `mod_key + alt + space` toggles floating
input.keybind([mod_key, Mod::Alt], Keysym::space, || { input.keybind(
if let Some(window) = window.get_focused() { [mod_key, Mod::Alt],
window.toggle_floating(); Keysym::space,
window.raise(); || {
} if let Some(window) = window.get_focused() {
}); window.toggle_floating();
window.raise();
}
},
KeybindInfo {
group: Some("Window".into()),
description: Some("Toggle floating on the focused window".into()),
},
);
// `mod_key + f` toggles fullscreen // `mod_key + f` toggles fullscreen
input.keybind([mod_key], 'f', || { input.keybind(
if let Some(window) = window.get_focused() { [mod_key],
window.toggle_fullscreen(); 'f',
window.raise(); || {
} if let Some(window) = window.get_focused() {
}); window.toggle_fullscreen();
window.raise();
}
},
KeybindInfo {
group: Some("Window".into()),
description: Some("Toggle fullscreen on the focused window".into()),
},
);
// `mod_key + m` toggles maximized // `mod_key + m` toggles maximized
input.keybind([mod_key], 'm', || { input.keybind(
if let Some(window) = window.get_focused() { [mod_key],
window.toggle_maximized(); 'm',
window.raise(); || {
} if let Some(window) = window.get_focused() {
}); window.toggle_maximized();
window.raise();
}
},
KeybindInfo {
group: Some("Window".into()),
description: Some("Toggle maximized on the focused window".into()),
},
);
//------------------------ //------------------------
// Window rules | // Window rules |
@ -200,32 +266,48 @@ async fn main() {
let mut layout_requester_clone = layout_requester.clone(); let mut layout_requester_clone = layout_requester.clone();
// `mod_key + space` cycles to the next layout // `mod_key + space` cycles to the next layout
input.keybind([mod_key], Keysym::space, move || { input.keybind(
let Some(focused_op) = output.get_focused() else { return }; [mod_key],
let Some(first_active_tag) = focused_op.tags().batch_find( Keysym::space,
|tg| Box::pin(tg.active_async()), move || {
|active| active == &Some(true), let Some(focused_op) = output.get_focused() else { return };
) else { let Some(first_active_tag) = focused_op.tags().batch_find(
return; |tg| Box::pin(tg.active_async()),
}; |active| active == &Some(true),
) else {
return;
};
layout_requester.cycle_layout_forward(&first_active_tag); layout_requester.cycle_layout_forward(&first_active_tag);
layout_requester.request_layout_on_output(&focused_op); layout_requester.request_layout_on_output(&focused_op);
}); },
KeybindInfo {
group: Some("Layout".into()),
description: Some("Cycle the layout forward on the first active tag".into()),
},
);
// `mod_key + shift + space` cycles to the previous layout // `mod_key + shift + space` cycles to the previous layout
input.keybind([mod_key, Mod::Shift], Keysym::space, move || { input.keybind(
let Some(focused_op) = output.get_focused() else { return }; [mod_key, Mod::Shift],
let Some(first_active_tag) = focused_op.tags().batch_find( Keysym::space,
|tg| Box::pin(tg.active_async()), move || {
|active| active == &Some(true), let Some(focused_op) = output.get_focused() else { return };
) else { let Some(first_active_tag) = focused_op.tags().batch_find(
return; |tg| Box::pin(tg.active_async()),
}; |active| active == &Some(true),
) else {
return;
};
layout_requester_clone.cycle_layout_backward(&first_active_tag); layout_requester_clone.cycle_layout_backward(&first_active_tag);
layout_requester_clone.request_layout_on_output(&focused_op); layout_requester_clone.request_layout_on_output(&focused_op);
}); },
KeybindInfo {
group: Some("Layout".into()),
description: Some("Cycle the layout backward on the first active tag".into()),
},
);
//------------------------ //------------------------
// Tags | // Tags |
@ -238,36 +320,68 @@ async fn main() {
for tag_name in tag_names { for tag_name in tag_names {
// `mod_key + 1-5` switches to tag "1" to "5" // `mod_key + 1-5` switches to tag "1" to "5"
input.keybind([mod_key], tag_name, move || { input.keybind(
if let Some(tg) = tag.get(tag_name) { [mod_key],
tg.switch_to(); tag_name,
} move || {
}); if let Some(tg) = tag.get(tag_name) {
tg.switch_to();
}
},
KeybindInfo {
group: Some("Tag".into()),
description: Some(format!("Switch to tag {tag_name}")),
},
);
// `mod_key + shift + 1-5` toggles tag "1" to "5" // `mod_key + shift + 1-5` toggles tag "1" to "5"
input.keybind([mod_key, Mod::Shift], tag_name, move || { input.keybind(
if let Some(tg) = tag.get(tag_name) { [mod_key, Mod::Shift],
tg.toggle_active(); tag_name,
} move || {
}); if let Some(tg) = tag.get(tag_name) {
tg.toggle_active();
}
},
KeybindInfo {
group: Some("Tag".into()),
description: Some(format!("Toggle tag {tag_name}")),
},
);
// `mod_key + alt + 1-5` moves the focused window to tag "1" to "5" // `mod_key + alt + 1-5` moves the focused window to tag "1" to "5"
input.keybind([mod_key, Mod::Alt], tag_name, move || { input.keybind(
if let Some(tg) = tag.get(tag_name) { [mod_key, Mod::Alt],
if let Some(win) = window.get_focused() { tag_name,
win.move_to_tag(&tg); move || {
if let Some(tg) = tag.get(tag_name) {
if let Some(win) = window.get_focused() {
win.move_to_tag(&tg);
}
} }
} },
}); KeybindInfo {
group: Some("Tag".into()),
description: Some(format!("Move the focused window to tag {tag_name}")),
},
);
// `mod_key + shift + alt + 1-5` toggles tag "1" to "5" on the focused window // `mod_key + shift + alt + 1-5` toggles tag "1" to "5" on the focused window
input.keybind([mod_key, Mod::Shift, Mod::Alt], tag_name, move || { input.keybind(
if let Some(tg) = tag.get(tag_name) { [mod_key, Mod::Shift, Mod::Alt],
if let Some(win) = window.get_focused() { tag_name,
win.toggle_tag(&tg); move || {
if let Some(tg) = tag.get(tag_name) {
if let Some(win) = window.get_focused() {
win.toggle_tag(&tg);
}
} }
} },
}); KeybindInfo {
group: Some("Tag".into()),
description: Some(format!("Toggle tag {tag_name} on the focused window")),
},
);
} }
input.set_libinput_setting(LibinputSetting::Tap(true)); input.set_libinput_setting(LibinputSetting::Tap(true));

View file

@ -15,8 +15,8 @@ use pinnacle_api_defs::pinnacle::input::{
v0alpha1::{ v0alpha1::{
input_service_client::InputServiceClient, input_service_client::InputServiceClient,
set_libinput_setting_request::{CalibrationMatrix, Setting}, set_libinput_setting_request::{CalibrationMatrix, Setting},
SetKeybindRequest, SetLibinputSettingRequest, SetMousebindRequest, SetRepeatRateRequest, KeybindDescriptionsRequest, SetKeybindRequest, SetLibinputSettingRequest,
SetXkbConfigRequest, SetMousebindRequest, SetRepeatRateRequest, SetXkbConfigRequest,
}, },
}; };
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
@ -99,6 +99,32 @@ pub struct Input {
fut_sender: UnboundedSender<BoxFuture<'static, ()>>, fut_sender: UnboundedSender<BoxFuture<'static, ()>>,
} }
/// Keybind information.
///
/// Mainly used for the keybind list.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub struct KeybindInfo {
/// The group to place this keybind in.
pub group: Option<String>,
/// The description of this keybind.
pub description: Option<String>,
}
/// The description of a keybind.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct KeybindDescription {
/// The keybind's modifiers.
pub modifiers: Vec<Mod>,
/// The keysym code.
pub key_code: u32,
/// The name of the key.
pub xkb_name: String,
/// The group.
pub group: Option<String>,
/// The description of the keybind.
pub description: Option<String>,
}
impl Input { impl Input {
pub(crate) fn new( pub(crate) fn new(
channel: Channel, channel: Channel,
@ -157,25 +183,28 @@ impl Input {
mods: impl IntoIterator<Item = Mod>, mods: impl IntoIterator<Item = Mod>,
key: impl Key + Send + 'static, key: impl Key + Send + 'static,
mut action: impl FnMut() + Send + 'static, mut action: impl FnMut() + Send + 'static,
keybind_info: impl Into<Option<KeybindInfo>>,
) { ) {
let mut client = self.create_input_client(); let mut client = self.create_input_client();
let modifiers = mods.into_iter().map(|modif| modif as i32).collect(); let modifiers = mods.into_iter().map(|modif| modif as i32).collect();
let keybind_info: Option<KeybindInfo> = keybind_info.into();
let mut stream = block_on_tokio(client.set_keybind(SetKeybindRequest {
modifiers,
key: Some(input::v0alpha1::set_keybind_request::Key::RawCode(
key.into_keysym().raw(),
)),
group: keybind_info.clone().and_then(|info| info.group),
description: keybind_info.clone().and_then(|info| info.description),
}))
.unwrap()
.into_inner();
self.fut_sender self.fut_sender
.send( .send(
async move { async move {
let mut stream = client
.set_keybind(SetKeybindRequest {
modifiers,
key: Some(input::v0alpha1::set_keybind_request::Key::RawCode(
key.into_keysym().raw(),
)),
})
.await
.unwrap()
.into_inner();
while let Some(Ok(_response)) = stream.next().await { while let Some(Ok(_response)) = stream.next().await {
action(); action();
tokio::task::yield_now().await; tokio::task::yield_now().await;
@ -218,20 +247,17 @@ impl Input {
let mut client = self.create_input_client(); let mut client = self.create_input_client();
let modifiers = mods.into_iter().map(|modif| modif as i32).collect(); let modifiers = mods.into_iter().map(|modif| modif as i32).collect();
let mut stream = block_on_tokio(client.set_mousebind(SetMousebindRequest {
modifiers,
button: Some(button as u32),
edge: Some(edge as i32),
}))
.unwrap()
.into_inner();
self.fut_sender self.fut_sender
.send( .send(
async move { async move {
let mut stream = client
.set_mousebind(SetMousebindRequest {
modifiers,
button: Some(button as u32),
edge: Some(edge as i32),
})
.await
.unwrap()
.into_inner();
while let Some(Ok(_response)) = stream.next().await { while let Some(Ok(_response)) = stream.next().await {
action(); action();
tokio::task::yield_now().await; tokio::task::yield_now().await;
@ -242,6 +268,31 @@ impl Input {
.unwrap(); .unwrap();
} }
/// Get all keybinds and their information.
pub fn keybind_descriptions(&self) -> impl Iterator<Item = KeybindDescription> {
let mut client = self.create_input_client();
let descriptions =
block_on_tokio(client.keybind_descriptions(KeybindDescriptionsRequest {})).unwrap();
let descriptions = descriptions.into_inner();
descriptions.descriptions.into_iter().map(|desc| {
let mods = desc.modifiers().flat_map(|m| match m {
input::v0alpha1::Modifier::Unspecified => None,
input::v0alpha1::Modifier::Shift => Some(Mod::Shift),
input::v0alpha1::Modifier::Ctrl => Some(Mod::Ctrl),
input::v0alpha1::Modifier::Alt => Some(Mod::Alt),
input::v0alpha1::Modifier::Super => Some(Mod::Super),
});
KeybindDescription {
modifiers: mods.collect(),
key_code: desc.raw_code(),
xkb_name: desc.xkb_name().to_string(),
group: desc.group,
description: desc.description,
}
})
}
/// Set the xkeyboard config. /// Set the xkeyboard config.
/// ///
/// This allows you to set several xkeyboard options like `layout` and `rules`. /// This allows you to set several xkeyboard options like `layout` and `rules`.

View file

@ -237,6 +237,7 @@ pub async fn connect() -> Result<(ApiModules, Receivers), Box<dyn std::error::Er
tag.finish_init(modules.clone()); tag.finish_init(modules.clone());
layout.finish_init(modules.clone()); layout.finish_init(modules.clone());
signal.read().await.finish_init(modules.clone()); signal.read().await.finish_init(modules.clone());
#[cfg(feature = "snowcap")]
modules.snowcap.finish_init(modules.clone()); modules.snowcap.finish_init(modules.clone());
#[cfg(feature = "snowcap")] #[cfg(feature = "snowcap")]

View file

@ -9,6 +9,7 @@ use pinnacle_api_defs::pinnacle::{
input_service_server, input_service_server,
set_libinput_setting_request::{AccelProfile, ClickMethod, ScrollMethod, TapButtonMap}, set_libinput_setting_request::{AccelProfile, ClickMethod, ScrollMethod, TapButtonMap},
set_mousebind_request::MouseEdge, set_mousebind_request::MouseEdge,
KeybindDescription, KeybindDescriptionsRequest, KeybindDescriptionsResponse, Modifier,
SetKeybindRequest, SetKeybindResponse, SetLibinputSettingRequest, SetMousebindRequest, SetKeybindRequest, SetKeybindResponse, SetLibinputSettingRequest, SetMousebindRequest,
SetMousebindResponse, SetRepeatRateRequest, SetXkbConfigRequest, SetMousebindResponse, SetRepeatRateRequest, SetXkbConfigRequest,
}, },
@ -55,7 +56,7 @@ use tracing::{debug, error, info, trace, warn};
use crate::{ use crate::{
backend::{udev::drm_mode_from_api_modeline, BackendData}, backend::{udev::drm_mode_from_api_modeline, BackendData},
config::ConnectorSavedState, config::ConnectorSavedState,
input::ModifierMask, input::{KeybindData, ModifierMask},
output::{OutputMode, OutputName}, output::{OutputMode, OutputName},
render::util::snapshot::capture_snapshots_on_output, render::util::snapshot::capture_snapshots_on_output,
state::{State, WithState}, state::{State, WithState},
@ -293,12 +294,21 @@ impl input_service_server::InputService for InputService {
} }
}; };
let group = request.group;
let description = request.description;
run_server_streaming(&self.sender, move |state, sender| { run_server_streaming(&self.sender, move |state, sender| {
let keybind_data = KeybindData {
sender,
group,
description,
};
state state
.pinnacle .pinnacle
.input_state .input_state
.keybinds .keybinds
.insert((modifiers, keysym), sender); .insert((modifiers, keysym), keybind_data);
}) })
} }
@ -346,6 +356,47 @@ impl input_service_server::InputService for InputService {
}) })
} }
async fn keybind_descriptions(
&self,
_request: Request<KeybindDescriptionsRequest>,
) -> Result<Response<KeybindDescriptionsResponse>, Status> {
run_unary(&self.sender, |state| {
let descriptions =
state
.pinnacle
.input_state
.keybinds
.iter()
.map(|((mods, key), data)| {
let mut modifiers = Vec::<i32>::new();
if mods.contains(ModifierMask::CTRL) {
modifiers.push(Modifier::Ctrl as i32);
}
if mods.contains(ModifierMask::ALT) {
modifiers.push(Modifier::Alt as i32);
}
if mods.contains(ModifierMask::SUPER) {
modifiers.push(Modifier::Super as i32);
}
if mods.contains(ModifierMask::SHIFT) {
modifiers.push(Modifier::Shift as i32);
}
KeybindDescription {
modifiers,
raw_code: Some(key.raw()),
xkb_name: Some(xkbcommon::xkb::keysym_get_name(*key)),
group: data.group.clone(),
description: data.description.clone(),
}
});
KeybindDescriptionsResponse {
descriptions: descriptions.collect(),
}
})
.await
}
async fn set_xkb_config( async fn set_xkb_config(
&self, &self,
request: Request<SetXkbConfigRequest>, request: Request<SetXkbConfigRequest>,

View file

@ -13,6 +13,7 @@ use crate::{
state::{Pinnacle, WithState}, state::{Pinnacle, WithState},
window::WindowElement, window::WindowElement,
}; };
use indexmap::IndexMap;
use pinnacle_api_defs::pinnacle::input::v0alpha1::{ use pinnacle_api_defs::pinnacle::input::v0alpha1::{
set_libinput_setting_request::Setting, set_mousebind_request, SetKeybindResponse, set_libinput_setting_request::Setting, set_mousebind_request, SetKeybindResponse,
SetMousebindResponse, SetMousebindResponse,
@ -93,14 +94,20 @@ impl From<&ModifiersState> for ModifierMask {
} }
} }
#[derive(Debug)]
pub struct KeybindData {
pub sender: UnboundedSender<Result<SetKeybindResponse, tonic::Status>>,
pub group: Option<String>,
pub description: Option<String>,
}
#[derive(Default)] #[derive(Default)]
pub struct InputState { pub struct InputState {
// TODO: move all of these to config // TODO: move all of these to config
pub reload_keybind: Option<(ModifierMask, Keysym)>, pub reload_keybind: Option<(ModifierMask, Keysym)>,
pub kill_keybind: Option<(ModifierMask, Keysym)>, pub kill_keybind: Option<(ModifierMask, Keysym)>,
pub keybinds: pub keybinds: IndexMap<(ModifierMask, Keysym), KeybindData>,
HashMap<(ModifierMask, Keysym), UnboundedSender<Result<SetKeybindResponse, tonic::Status>>>,
pub mousebinds: HashMap< pub mousebinds: HashMap<
(ModifierMask, u32, set_mousebind_request::MouseEdge), (ModifierMask, u32, set_mousebind_request::MouseEdge),
UnboundedSender<Result<SetMousebindResponse, tonic::Status>>, UnboundedSender<Result<SetMousebindResponse, tonic::Status>>,
@ -540,7 +547,7 @@ impl State {
let raw_sym = keysym.raw_syms().iter().next(); let raw_sym = keysym.raw_syms().iter().next();
let mod_sym = keysym.modified_sym(); let mod_sym = keysym.modified_sym();
if let Some(sender) = state if let Some(keybind_data) = state
.pinnacle .pinnacle
.input_state .input_state
.keybinds .keybinds
@ -557,7 +564,7 @@ impl State {
{ {
if state.pinnacle.lock_state.is_unlocked() { if state.pinnacle.lock_state.is_unlocked() {
return FilterResult::Intercept(KeyAction::CallCallback( return FilterResult::Intercept(KeyAction::CallCallback(
sender.clone(), keybind_data.sender.clone(),
)); ));
} }
} }