mirror of
https://github.com/pinnacle-comp/pinnacle.git
synced 2025-01-19 10:26:36 +01:00
Add keybind descriptions
This commit is contained in:
parent
d70a0abda5
commit
5b241a626b
9 changed files with 456 additions and 122 deletions
|
@ -34,12 +34,18 @@ require("pinnacle").setup(function(Pinnacle)
|
|||
-- mod_key + alt + q = Quit Pinnacle
|
||||
Input.keybind({ mod_key, "alt" }, "q", function()
|
||||
Pinnacle.quit()
|
||||
end)
|
||||
end, {
|
||||
group = "Compositor",
|
||||
description = "Quit Pinnacle",
|
||||
})
|
||||
|
||||
-- mod_key + alt + r = Reload config
|
||||
Input.keybind({ mod_key, "alt" }, "r", function()
|
||||
Pinnacle.reload_config()
|
||||
end)
|
||||
end, {
|
||||
group = "Compositor",
|
||||
description = "Reload the config",
|
||||
})
|
||||
|
||||
-- mod_key + alt + c = Close window
|
||||
Input.keybind({ mod_key, "alt" }, "c", function()
|
||||
|
@ -47,12 +53,18 @@ require("pinnacle").setup(function(Pinnacle)
|
|||
if focused then
|
||||
focused:close()
|
||||
end
|
||||
end)
|
||||
end, {
|
||||
group = "Window",
|
||||
description = "Close the focused window",
|
||||
})
|
||||
|
||||
-- mod_key + alt + Return = Spawn `terminal`
|
||||
Input.keybind({ mod_key }, key.Return, function()
|
||||
Process.spawn(terminal)
|
||||
end)
|
||||
end, {
|
||||
group = "Process",
|
||||
description = "Spawn `alacritty`",
|
||||
})
|
||||
|
||||
-- mod_key + alt + space = Toggle floating
|
||||
Input.keybind({ mod_key, "alt" }, key.space, function()
|
||||
|
@ -61,7 +73,10 @@ require("pinnacle").setup(function(Pinnacle)
|
|||
focused:toggle_floating()
|
||||
focused:raise()
|
||||
end
|
||||
end)
|
||||
end, {
|
||||
group = "Window",
|
||||
description = "Toggle floating on the focused window",
|
||||
})
|
||||
|
||||
-- mod_key + f = Toggle fullscreen
|
||||
Input.keybind({ mod_key }, "f", function()
|
||||
|
@ -70,7 +85,10 @@ require("pinnacle").setup(function(Pinnacle)
|
|||
focused:toggle_fullscreen()
|
||||
focused:raise()
|
||||
end
|
||||
end)
|
||||
end, {
|
||||
group = "Window",
|
||||
description = "Toggle fullscreen on the focused window",
|
||||
})
|
||||
|
||||
-- mod_key + m = Toggle maximized
|
||||
Input.keybind({ mod_key }, "m", function()
|
||||
|
@ -79,7 +97,10 @@ require("pinnacle").setup(function(Pinnacle)
|
|||
focused:toggle_maximized()
|
||||
focused:raise()
|
||||
end
|
||||
end)
|
||||
end, {
|
||||
group = "Window",
|
||||
description = "Toggle maximized on the focused window",
|
||||
})
|
||||
|
||||
----------------------
|
||||
-- Tags and Outputs --
|
||||
|
@ -109,12 +130,18 @@ require("pinnacle").setup(function(Pinnacle)
|
|||
-- mod_key + 1-5 = Switch to tags 1-5
|
||||
Input.keybind({ mod_key }, tag_name, function()
|
||||
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
|
||||
Input.keybind({ mod_key, "shift" }, tag_name, function()
|
||||
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
|
||||
Input.keybind({ mod_key, "alt" }, tag_name, function()
|
||||
|
@ -122,7 +149,10 @@ require("pinnacle").setup(function(Pinnacle)
|
|||
if focused then
|
||||
focused:move_to_tag(Tag.get(tag_name) --[[@as TagHandle]])
|
||||
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
|
||||
Input.keybind({ mod_key, "shift", "alt" }, tag_name, function()
|
||||
|
@ -130,7 +160,10 @@ require("pinnacle").setup(function(Pinnacle)
|
|||
if focused then
|
||||
focused:toggle_tag(Tag.get(tag_name) --[[@as TagHandle]])
|
||||
end
|
||||
end)
|
||||
end, {
|
||||
group = "Tag",
|
||||
description = "Toggle tag " .. tag_name .. " on the focused window",
|
||||
})
|
||||
end
|
||||
|
||||
--------------------
|
||||
|
@ -226,7 +259,10 @@ require("pinnacle").setup(function(Pinnacle)
|
|||
Layout.request_layout(focused_op)
|
||||
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
|
||||
Input.keybind({ mod_key, "shift" }, key.space, function()
|
||||
|
@ -257,7 +293,10 @@ require("pinnacle").setup(function(Pinnacle)
|
|||
Layout.request_layout(focused_op)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end, {
|
||||
group = "Layout",
|
||||
description = "Cycle the layout backward on the first active tag",
|
||||
})
|
||||
|
||||
Input.set_libinput_settings({
|
||||
tap = true,
|
||||
|
|
|
@ -258,6 +258,8 @@ local pinnacle_input_v0alpha1_Modifier = {
|
|||
---@field modifiers pinnacle.input.v0alpha1.Modifier[]?
|
||||
---@field raw_code integer?
|
||||
---@field xkb_name string?
|
||||
---@field group string?
|
||||
---@field description string?
|
||||
|
||||
---@class pinnacle.input.v0alpha1.SetKeybindResponse
|
||||
|
||||
|
@ -274,6 +276,18 @@ local pinnacle_input_v0alpha1_SetMousebindRequest_MouseEdge = {
|
|||
|
||||
---@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
|
||||
---@field rules string?
|
||||
---@field variant string?
|
||||
|
@ -729,6 +743,13 @@ defs.pinnacle = {
|
|||
response = "pinnacle.input.v0alpha1.SetMousebindResponse",
|
||||
},
|
||||
---@type GrpcRequestArgs
|
||||
KeybindDescriptions = {
|
||||
service = "pinnacle.input.v0alpha1.InputService",
|
||||
method = "KeybindDescriptions",
|
||||
request = "pinnacle.input.v0alpha1.KeybindDescriptionsRequest",
|
||||
response = "pinnacle.input.v0alpha1.KeybindDescriptionsResponse",
|
||||
},
|
||||
---@type GrpcRequestArgs
|
||||
SetXkbConfig = {
|
||||
service = "pinnacle.input.v0alpha1.InputService",
|
||||
method = "SetXkbConfig",
|
||||
|
|
|
@ -74,6 +74,10 @@ local input = {
|
|||
}
|
||||
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.
|
||||
---
|
||||
---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 key Key | string The key used to trigger the bind
|
||||
---@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 xkb_name = nil
|
||||
|
||||
|
@ -130,6 +135,8 @@ function input.keybind(mods, key, action)
|
|||
modifiers = mod_values,
|
||||
raw_code = raw_code,
|
||||
xkb_name = xkb_name,
|
||||
group = keybind_info and keybind_info.group,
|
||||
description = keybind_info and keybind_info.description,
|
||||
}, action)
|
||||
end
|
||||
|
||||
|
@ -165,6 +172,31 @@ function input.mousebind(mods, button, edge, action)
|
|||
}, action)
|
||||
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
|
||||
---@field rules string?
|
||||
---@field model string?
|
||||
|
|
|
@ -18,9 +18,25 @@ message SetKeybindRequest {
|
|||
uint32 raw_code = 2;
|
||||
string xkb_name = 3;
|
||||
}
|
||||
optional string group = 4;
|
||||
optional string description = 5;
|
||||
}
|
||||
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 {
|
||||
repeated Modifier modifiers = 1;
|
||||
// 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 SetMousebind(SetMousebindRequest) returns (stream SetMousebindResponse);
|
||||
|
||||
rpc KeybindDescriptions(KeybindDescriptionsRequest) returns (KeybindDescriptionsResponse);
|
||||
|
||||
rpc SetXkbConfig(SetXkbConfigRequest) returns (google.protobuf.Empty);
|
||||
rpc SetRepeatRate(SetRepeatRateRequest) returns (google.protobuf.Empty);
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use pinnacle_api::input::libinput::LibinputSetting;
|
||||
use pinnacle_api::input::KeybindInfo;
|
||||
use pinnacle_api::layout::{
|
||||
CornerLayout, CornerLocation, CyclingLayoutManager, DwindleLayout, FairLayout, MasterSide,
|
||||
MasterStackLayout, SpiralLayout,
|
||||
|
@ -51,6 +52,15 @@ async fn main() {
|
|||
|
||||
let terminal = "alacritty";
|
||||
|
||||
input.keybind(
|
||||
[mod_key],
|
||||
'i',
|
||||
|| {
|
||||
dbg!(input.keybind_descriptions().collect::<Vec<_>>());
|
||||
},
|
||||
None,
|
||||
);
|
||||
|
||||
//------------------------
|
||||
// Mousebinds |
|
||||
//------------------------
|
||||
|
@ -70,54 +80,110 @@ async fn main() {
|
|||
//------------------------
|
||||
|
||||
// `mod_key + alt + q` quits Pinnacle
|
||||
input.keybind([mod_key, Mod::Alt], 'q', || {
|
||||
#[cfg(feature = "snowcap")]
|
||||
snowcap.integration.quit_prompt().show();
|
||||
input.keybind(
|
||||
[mod_key, Mod::Alt],
|
||||
'q',
|
||||
|| {
|
||||
#[cfg(feature = "snowcap")]
|
||||
snowcap.integration.quit_prompt().show();
|
||||
|
||||
#[cfg(not(feature = "snowcap"))]
|
||||
pinnacle.quit();
|
||||
});
|
||||
#[cfg(not(feature = "snowcap"))]
|
||||
pinnacle.quit();
|
||||
},
|
||||
KeybindInfo {
|
||||
group: Some("Compositor".into()),
|
||||
description: Some("Quit Pinnacle".into()),
|
||||
},
|
||||
);
|
||||
|
||||
// `mod_key + alt + r` reloads the config
|
||||
input.keybind([mod_key, Mod::Alt], 'r', || {
|
||||
pinnacle.reload_config();
|
||||
});
|
||||
input.keybind(
|
||||
[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
|
||||
input.keybind([mod_key, Mod::Alt], 'c', || {
|
||||
if let Some(window) = window.get_focused() {
|
||||
window.close();
|
||||
}
|
||||
});
|
||||
input.keybind(
|
||||
[mod_key, Mod::Alt],
|
||||
'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
|
||||
input.keybind([mod_key], Keysym::Return, move || {
|
||||
process.spawn([terminal]);
|
||||
});
|
||||
input.keybind(
|
||||
[mod_key],
|
||||
Keysym::Return,
|
||||
move || {
|
||||
process.spawn([terminal]);
|
||||
},
|
||||
KeybindInfo {
|
||||
group: Some("Process".into()),
|
||||
description: Some("Spawn `alacritty`".into()),
|
||||
},
|
||||
);
|
||||
|
||||
// `mod_key + alt + space` toggles floating
|
||||
input.keybind([mod_key, Mod::Alt], Keysym::space, || {
|
||||
if let Some(window) = window.get_focused() {
|
||||
window.toggle_floating();
|
||||
window.raise();
|
||||
}
|
||||
});
|
||||
input.keybind(
|
||||
[mod_key, Mod::Alt],
|
||||
Keysym::space,
|
||||
|| {
|
||||
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
|
||||
input.keybind([mod_key], 'f', || {
|
||||
if let Some(window) = window.get_focused() {
|
||||
window.toggle_fullscreen();
|
||||
window.raise();
|
||||
}
|
||||
});
|
||||
input.keybind(
|
||||
[mod_key],
|
||||
'f',
|
||||
|| {
|
||||
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
|
||||
input.keybind([mod_key], 'm', || {
|
||||
if let Some(window) = window.get_focused() {
|
||||
window.toggle_maximized();
|
||||
window.raise();
|
||||
}
|
||||
});
|
||||
input.keybind(
|
||||
[mod_key],
|
||||
'm',
|
||||
|| {
|
||||
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 |
|
||||
|
@ -200,32 +266,48 @@ async fn main() {
|
|||
let mut layout_requester_clone = layout_requester.clone();
|
||||
|
||||
// `mod_key + space` cycles to the next layout
|
||||
input.keybind([mod_key], Keysym::space, move || {
|
||||
let Some(focused_op) = output.get_focused() else { return };
|
||||
let Some(first_active_tag) = focused_op.tags().batch_find(
|
||||
|tg| Box::pin(tg.active_async()),
|
||||
|active| active == &Some(true),
|
||||
) else {
|
||||
return;
|
||||
};
|
||||
input.keybind(
|
||||
[mod_key],
|
||||
Keysym::space,
|
||||
move || {
|
||||
let Some(focused_op) = output.get_focused() else { return };
|
||||
let Some(first_active_tag) = focused_op.tags().batch_find(
|
||||
|tg| Box::pin(tg.active_async()),
|
||||
|active| active == &Some(true),
|
||||
) else {
|
||||
return;
|
||||
};
|
||||
|
||||
layout_requester.cycle_layout_forward(&first_active_tag);
|
||||
layout_requester.request_layout_on_output(&focused_op);
|
||||
});
|
||||
layout_requester.cycle_layout_forward(&first_active_tag);
|
||||
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
|
||||
input.keybind([mod_key, Mod::Shift], Keysym::space, move || {
|
||||
let Some(focused_op) = output.get_focused() else { return };
|
||||
let Some(first_active_tag) = focused_op.tags().batch_find(
|
||||
|tg| Box::pin(tg.active_async()),
|
||||
|active| active == &Some(true),
|
||||
) else {
|
||||
return;
|
||||
};
|
||||
input.keybind(
|
||||
[mod_key, Mod::Shift],
|
||||
Keysym::space,
|
||||
move || {
|
||||
let Some(focused_op) = output.get_focused() else { return };
|
||||
let Some(first_active_tag) = focused_op.tags().batch_find(
|
||||
|tg| Box::pin(tg.active_async()),
|
||||
|active| active == &Some(true),
|
||||
) else {
|
||||
return;
|
||||
};
|
||||
|
||||
layout_requester_clone.cycle_layout_backward(&first_active_tag);
|
||||
layout_requester_clone.request_layout_on_output(&focused_op);
|
||||
});
|
||||
layout_requester_clone.cycle_layout_backward(&first_active_tag);
|
||||
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 |
|
||||
|
@ -238,36 +320,68 @@ async fn main() {
|
|||
|
||||
for tag_name in tag_names {
|
||||
// `mod_key + 1-5` switches to tag "1" to "5"
|
||||
input.keybind([mod_key], tag_name, move || {
|
||||
if let Some(tg) = tag.get(tag_name) {
|
||||
tg.switch_to();
|
||||
}
|
||||
});
|
||||
input.keybind(
|
||||
[mod_key],
|
||||
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"
|
||||
input.keybind([mod_key, Mod::Shift], tag_name, move || {
|
||||
if let Some(tg) = tag.get(tag_name) {
|
||||
tg.toggle_active();
|
||||
}
|
||||
});
|
||||
input.keybind(
|
||||
[mod_key, Mod::Shift],
|
||||
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"
|
||||
input.keybind([mod_key, Mod::Alt], tag_name, move || {
|
||||
if let Some(tg) = tag.get(tag_name) {
|
||||
if let Some(win) = window.get_focused() {
|
||||
win.move_to_tag(&tg);
|
||||
input.keybind(
|
||||
[mod_key, Mod::Alt],
|
||||
tag_name,
|
||||
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
|
||||
input.keybind([mod_key, Mod::Shift, Mod::Alt], tag_name, move || {
|
||||
if let Some(tg) = tag.get(tag_name) {
|
||||
if let Some(win) = window.get_focused() {
|
||||
win.toggle_tag(&tg);
|
||||
input.keybind(
|
||||
[mod_key, Mod::Shift, Mod::Alt],
|
||||
tag_name,
|
||||
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));
|
||||
|
|
|
@ -15,8 +15,8 @@ use pinnacle_api_defs::pinnacle::input::{
|
|||
v0alpha1::{
|
||||
input_service_client::InputServiceClient,
|
||||
set_libinput_setting_request::{CalibrationMatrix, Setting},
|
||||
SetKeybindRequest, SetLibinputSettingRequest, SetMousebindRequest, SetRepeatRateRequest,
|
||||
SetXkbConfigRequest,
|
||||
KeybindDescriptionsRequest, SetKeybindRequest, SetLibinputSettingRequest,
|
||||
SetMousebindRequest, SetRepeatRateRequest, SetXkbConfigRequest,
|
||||
},
|
||||
};
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
|
@ -99,6 +99,32 @@ pub struct Input {
|
|||
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 {
|
||||
pub(crate) fn new(
|
||||
channel: Channel,
|
||||
|
@ -157,25 +183,28 @@ impl Input {
|
|||
mods: impl IntoIterator<Item = Mod>,
|
||||
key: impl Key + Send + 'static,
|
||||
mut action: impl FnMut() + Send + 'static,
|
||||
keybind_info: impl Into<Option<KeybindInfo>>,
|
||||
) {
|
||||
let mut client = self.create_input_client();
|
||||
|
||||
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
|
||||
.send(
|
||||
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 {
|
||||
action();
|
||||
tokio::task::yield_now().await;
|
||||
|
@ -218,20 +247,17 @@ impl Input {
|
|||
let mut client = self.create_input_client();
|
||||
|
||||
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
|
||||
.send(
|
||||
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 {
|
||||
action();
|
||||
tokio::task::yield_now().await;
|
||||
|
@ -242,6 +268,31 @@ impl Input {
|
|||
.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.
|
||||
///
|
||||
/// This allows you to set several xkeyboard options like `layout` and `rules`.
|
||||
|
|
|
@ -237,6 +237,7 @@ pub async fn connect() -> Result<(ApiModules, Receivers), Box<dyn std::error::Er
|
|||
tag.finish_init(modules.clone());
|
||||
layout.finish_init(modules.clone());
|
||||
signal.read().await.finish_init(modules.clone());
|
||||
#[cfg(feature = "snowcap")]
|
||||
modules.snowcap.finish_init(modules.clone());
|
||||
|
||||
#[cfg(feature = "snowcap")]
|
||||
|
|
55
src/api.rs
55
src/api.rs
|
@ -9,6 +9,7 @@ use pinnacle_api_defs::pinnacle::{
|
|||
input_service_server,
|
||||
set_libinput_setting_request::{AccelProfile, ClickMethod, ScrollMethod, TapButtonMap},
|
||||
set_mousebind_request::MouseEdge,
|
||||
KeybindDescription, KeybindDescriptionsRequest, KeybindDescriptionsResponse, Modifier,
|
||||
SetKeybindRequest, SetKeybindResponse, SetLibinputSettingRequest, SetMousebindRequest,
|
||||
SetMousebindResponse, SetRepeatRateRequest, SetXkbConfigRequest,
|
||||
},
|
||||
|
@ -55,7 +56,7 @@ use tracing::{debug, error, info, trace, warn};
|
|||
use crate::{
|
||||
backend::{udev::drm_mode_from_api_modeline, BackendData},
|
||||
config::ConnectorSavedState,
|
||||
input::ModifierMask,
|
||||
input::{KeybindData, ModifierMask},
|
||||
output::{OutputMode, OutputName},
|
||||
render::util::snapshot::capture_snapshots_on_output,
|
||||
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| {
|
||||
let keybind_data = KeybindData {
|
||||
sender,
|
||||
group,
|
||||
description,
|
||||
};
|
||||
|
||||
state
|
||||
.pinnacle
|
||||
.input_state
|
||||
.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(
|
||||
&self,
|
||||
request: Request<SetXkbConfigRequest>,
|
||||
|
|
15
src/input.rs
15
src/input.rs
|
@ -13,6 +13,7 @@ use crate::{
|
|||
state::{Pinnacle, WithState},
|
||||
window::WindowElement,
|
||||
};
|
||||
use indexmap::IndexMap;
|
||||
use pinnacle_api_defs::pinnacle::input::v0alpha1::{
|
||||
set_libinput_setting_request::Setting, set_mousebind_request, SetKeybindResponse,
|
||||
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)]
|
||||
pub struct InputState {
|
||||
// TODO: move all of these to config
|
||||
pub reload_keybind: Option<(ModifierMask, Keysym)>,
|
||||
pub kill_keybind: Option<(ModifierMask, Keysym)>,
|
||||
|
||||
pub keybinds:
|
||||
HashMap<(ModifierMask, Keysym), UnboundedSender<Result<SetKeybindResponse, tonic::Status>>>,
|
||||
pub keybinds: IndexMap<(ModifierMask, Keysym), KeybindData>,
|
||||
pub mousebinds: HashMap<
|
||||
(ModifierMask, u32, set_mousebind_request::MouseEdge),
|
||||
UnboundedSender<Result<SetMousebindResponse, tonic::Status>>,
|
||||
|
@ -540,7 +547,7 @@ impl State {
|
|||
let raw_sym = keysym.raw_syms().iter().next();
|
||||
let mod_sym = keysym.modified_sym();
|
||||
|
||||
if let Some(sender) = state
|
||||
if let Some(keybind_data) = state
|
||||
.pinnacle
|
||||
.input_state
|
||||
.keybinds
|
||||
|
@ -557,7 +564,7 @@ impl State {
|
|||
{
|
||||
if state.pinnacle.lock_state.is_unlocked() {
|
||||
return FilterResult::Intercept(KeyAction::CallCallback(
|
||||
sender.clone(),
|
||||
keybind_data.sender.clone(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue