Make Super_L chordable and implement hyprland-global-shortcuts-v1
This commit is contained in:
parent
8ff17aca1e
commit
6d3bff952e
16 changed files with 363 additions and 16 deletions
|
|
@ -868,6 +868,10 @@ impl ConfigClient {
|
||||||
self.send(&ClientMessage::Quit)
|
self.send(&ClientMessage::Quit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn trigger_global_shortcut(&self, app_id: &str, id: &str) {
|
||||||
|
self.send(&ClientMessage::TriggerGlobalShortcut { app_id, id })
|
||||||
|
}
|
||||||
|
|
||||||
pub fn switch_to_vt(&self, vtnr: u32) {
|
pub fn switch_to_vt(&self, vtnr: u32) {
|
||||||
self.send(&ClientMessage::SwitchTo { vtnr })
|
self.send(&ClientMessage::SwitchTo { vtnr })
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -912,6 +912,10 @@ pub enum ClientMessage<'a> {
|
||||||
seat: Seat,
|
seat: Seat,
|
||||||
right: bool,
|
right: bool,
|
||||||
},
|
},
|
||||||
|
TriggerGlobalShortcut {
|
||||||
|
app_id: &'a str,
|
||||||
|
id: &'a str,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,14 @@ pub fn quit() {
|
||||||
get!().quit()
|
get!().quit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sends a `pressed` event to the client that has registered a Hyprland global
|
||||||
|
/// shortcut with the given `app_id` and `id`.
|
||||||
|
///
|
||||||
|
/// Has no effect if no client is currently registered for that combination.
|
||||||
|
pub fn trigger_global_shortcut(app_id: &str, id: &str) {
|
||||||
|
get!().trigger_global_shortcut(app_id, id)
|
||||||
|
}
|
||||||
|
|
||||||
/// Switches to a different VT.
|
/// Switches to a different VT.
|
||||||
pub fn switch_to_vt(n: u32) {
|
pub fn switch_to_vt(n: u32) {
|
||||||
get!().switch_to_vt(n)
|
get!().switch_to_vt(n)
|
||||||
|
|
|
||||||
|
|
@ -396,6 +396,7 @@ fn start_compositor2(
|
||||||
bo_drop_queue: Rc::new(ObjectDropQueue::new(&ring)),
|
bo_drop_queue: Rc::new(ObjectDropQueue::new(&ring)),
|
||||||
virtual_outputs: Default::default(),
|
virtual_outputs: Default::default(),
|
||||||
clean_logs_older_than: Default::default(),
|
clean_logs_older_than: Default::default(),
|
||||||
|
hyprland_global_shortcuts: Default::default(),
|
||||||
});
|
});
|
||||||
state.tracker.register(ClientId::from_raw(0));
|
state.tracker.register(ClientId::from_raw(0));
|
||||||
create_dummy_output(&state);
|
create_dummy_output(&state);
|
||||||
|
|
|
||||||
|
|
@ -3528,10 +3528,24 @@ impl ConfigProxyHandler {
|
||||||
ClientMessage::SeatMoveTab { seat, right } => self
|
ClientMessage::SeatMoveTab { seat, right } => self
|
||||||
.handle_seat_move_tab(seat, right)
|
.handle_seat_move_tab(seat, right)
|
||||||
.wrn("seat_move_tab")?,
|
.wrn("seat_move_tab")?,
|
||||||
|
ClientMessage::TriggerGlobalShortcut { app_id, id } => {
|
||||||
|
self.handle_trigger_global_shortcut(app_id, id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_trigger_global_shortcut(&self, app_id: &str, id: &str) {
|
||||||
|
let key = (app_id.to_string(), id.to_string());
|
||||||
|
let Some(shortcut) = self.state.hyprland_global_shortcuts.get(&key) else {
|
||||||
|
log::debug!(
|
||||||
|
"no client has registered hyprland global shortcut {app_id:?}:{id:?}"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
shortcut.send_pressed_now();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn auto_focus(&self, data: &ToplevelData) -> bool {
|
pub fn auto_focus(&self, data: &ToplevelData) -> bool {
|
||||||
for matcher in self.window_matcher_no_auto_focus.lock().values() {
|
for matcher in self.window_matcher_no_auto_focus.lock().values() {
|
||||||
if matcher.node.pull(data) {
|
if matcher.node.pull(data) {
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ use {
|
||||||
ext_session_lock_manager_v1::ExtSessionLockManagerV1Global,
|
ext_session_lock_manager_v1::ExtSessionLockManagerV1Global,
|
||||||
head_management::jay_head_manager_v1::JayHeadManagerV1Global,
|
head_management::jay_head_manager_v1::JayHeadManagerV1Global,
|
||||||
hyprland_focus_grab_manager_v1::HyprlandFocusGrabManagerV1Global,
|
hyprland_focus_grab_manager_v1::HyprlandFocusGrabManagerV1Global,
|
||||||
|
hyprland_global_shortcuts_manager_v1::HyprlandGlobalShortcutsManagerV1Global,
|
||||||
ipc::{
|
ipc::{
|
||||||
data_control::{
|
data_control::{
|
||||||
ext_data_control_manager_v1::ExtDataControlManagerV1Global,
|
ext_data_control_manager_v1::ExtDataControlManagerV1Global,
|
||||||
|
|
@ -208,6 +209,7 @@ singletons! {
|
||||||
ZwpRelativePointerManagerV1,
|
ZwpRelativePointerManagerV1,
|
||||||
ExtSessionLockManagerV1,
|
ExtSessionLockManagerV1,
|
||||||
HyprlandFocusGrabManagerV1,
|
HyprlandFocusGrabManagerV1,
|
||||||
|
HyprlandGlobalShortcutsManagerV1,
|
||||||
WpViewporter,
|
WpViewporter,
|
||||||
WpFractionalScaleManagerV1,
|
WpFractionalScaleManagerV1,
|
||||||
ZwpPointerConstraintsV1,
|
ZwpPointerConstraintsV1,
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@ pub mod ext_session_lock_v1;
|
||||||
pub mod head_management;
|
pub mod head_management;
|
||||||
pub mod hyprland_focus_grab_manager_v1;
|
pub mod hyprland_focus_grab_manager_v1;
|
||||||
pub mod hyprland_focus_grab_v1;
|
pub mod hyprland_focus_grab_v1;
|
||||||
|
pub mod hyprland_global_shortcut_v1;
|
||||||
|
pub mod hyprland_global_shortcuts_manager_v1;
|
||||||
pub mod ipc;
|
pub mod ipc;
|
||||||
pub mod jay_acceptor_request;
|
pub mod jay_acceptor_request;
|
||||||
pub mod jay_client_query;
|
pub mod jay_client_query;
|
||||||
|
|
|
||||||
97
src/ifs/hyprland_global_shortcut_v1.rs
Normal file
97
src/ifs/hyprland_global_shortcut_v1.rs
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
client::{Client, ClientError},
|
||||||
|
leaks::Tracker,
|
||||||
|
object::{Object, Version},
|
||||||
|
wire::{HyprlandGlobalShortcutV1Id, hyprland_global_shortcut_v1::*},
|
||||||
|
},
|
||||||
|
std::{rc::Rc, time::SystemTime},
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct HyprlandGlobalShortcutV1 {
|
||||||
|
pub id: HyprlandGlobalShortcutV1Id,
|
||||||
|
pub client: Rc<Client>,
|
||||||
|
pub tracker: Tracker<Self>,
|
||||||
|
pub version: Version,
|
||||||
|
pub shortcut_id: String,
|
||||||
|
pub app_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HyprlandGlobalShortcutV1 {
|
||||||
|
pub fn new(
|
||||||
|
id: HyprlandGlobalShortcutV1Id,
|
||||||
|
client: &Rc<Client>,
|
||||||
|
version: Version,
|
||||||
|
shortcut_id: String,
|
||||||
|
app_id: String,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
client: client.clone(),
|
||||||
|
tracker: Default::default(),
|
||||||
|
version,
|
||||||
|
shortcut_id,
|
||||||
|
app_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_pressed_now(&self) {
|
||||||
|
let (sec_hi, sec_lo, nsec) = now_split();
|
||||||
|
self.client.event(Pressed {
|
||||||
|
self_id: self.id,
|
||||||
|
tv_sec_hi: sec_hi,
|
||||||
|
tv_sec_lo: sec_lo,
|
||||||
|
tv_nsec: nsec,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn now_split() -> (u32, u32, u32) {
|
||||||
|
let dur = SystemTime::now()
|
||||||
|
.duration_since(SystemTime::UNIX_EPOCH)
|
||||||
|
.unwrap_or_default();
|
||||||
|
let secs = dur.as_secs();
|
||||||
|
((secs >> 32) as u32, secs as u32, dur.subsec_nanos())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HyprlandGlobalShortcutV1RequestHandler for HyprlandGlobalShortcutV1 {
|
||||||
|
type Error = HyprlandGlobalShortcutV1Error;
|
||||||
|
|
||||||
|
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
let key = (self.app_id.clone(), self.shortcut_id.clone());
|
||||||
|
if let Some(existing) = self.client.state.hyprland_global_shortcuts.get(&key)
|
||||||
|
&& Rc::as_ptr(&existing) as *const _ == self as *const _
|
||||||
|
{
|
||||||
|
self.client.state.hyprland_global_shortcuts.remove(&key);
|
||||||
|
}
|
||||||
|
self.client.remove_obj(self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object_base! {
|
||||||
|
self = HyprlandGlobalShortcutV1;
|
||||||
|
version = self.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Object for HyprlandGlobalShortcutV1 {
|
||||||
|
fn break_loops(&self) {
|
||||||
|
let key = (self.app_id.clone(), self.shortcut_id.clone());
|
||||||
|
if let Some(existing) = self.client.state.hyprland_global_shortcuts.get(&key)
|
||||||
|
&& Rc::as_ptr(&existing) as *const _ == self as *const _
|
||||||
|
{
|
||||||
|
self.client.state.hyprland_global_shortcuts.remove(&key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
simple_add_obj!(HyprlandGlobalShortcutV1);
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum HyprlandGlobalShortcutV1Error {
|
||||||
|
#[error(transparent)]
|
||||||
|
ClientError(Box<ClientError>),
|
||||||
|
}
|
||||||
|
|
||||||
|
efrom!(HyprlandGlobalShortcutV1Error, ClientError);
|
||||||
122
src/ifs/hyprland_global_shortcuts_manager_v1.rs
Normal file
122
src/ifs/hyprland_global_shortcuts_manager_v1.rs
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
client::{Client, ClientError},
|
||||||
|
globals::{Global, GlobalName},
|
||||||
|
ifs::hyprland_global_shortcut_v1::HyprlandGlobalShortcutV1,
|
||||||
|
leaks::Tracker,
|
||||||
|
object::{Object, Version},
|
||||||
|
wire::{HyprlandGlobalShortcutsManagerV1Id, hyprland_global_shortcuts_manager_v1::*},
|
||||||
|
},
|
||||||
|
std::rc::Rc,
|
||||||
|
thiserror::Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
const ALREADY_TAKEN: u32 = 0;
|
||||||
|
|
||||||
|
pub struct HyprlandGlobalShortcutsManagerV1Global {
|
||||||
|
pub name: GlobalName,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HyprlandGlobalShortcutsManagerV1Global {
|
||||||
|
pub fn new(name: GlobalName) -> Self {
|
||||||
|
Self { name }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bind_(
|
||||||
|
self: Rc<Self>,
|
||||||
|
id: HyprlandGlobalShortcutsManagerV1Id,
|
||||||
|
client: &Rc<Client>,
|
||||||
|
version: Version,
|
||||||
|
) -> Result<(), HyprlandGlobalShortcutsManagerV1Error> {
|
||||||
|
let obj = Rc::new(HyprlandGlobalShortcutsManagerV1 {
|
||||||
|
id,
|
||||||
|
client: client.clone(),
|
||||||
|
tracker: Default::default(),
|
||||||
|
version,
|
||||||
|
});
|
||||||
|
track!(client, obj);
|
||||||
|
client.add_client_obj(&obj)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct HyprlandGlobalShortcutsManagerV1 {
|
||||||
|
pub id: HyprlandGlobalShortcutsManagerV1Id,
|
||||||
|
pub client: Rc<Client>,
|
||||||
|
pub tracker: Tracker<Self>,
|
||||||
|
pub version: Version,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HyprlandGlobalShortcutsManagerV1RequestHandler for HyprlandGlobalShortcutsManagerV1 {
|
||||||
|
type Error = HyprlandGlobalShortcutsManagerV1Error;
|
||||||
|
|
||||||
|
fn destroy(&self, _req: Destroy, _slf: &Rc<Self>) -> Result<(), Self::Error> {
|
||||||
|
self.client.remove_obj(self)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_shortcut(
|
||||||
|
&self,
|
||||||
|
req: RegisterShortcut,
|
||||||
|
_slf: &Rc<Self>,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
let shortcut_id = req.id.to_string();
|
||||||
|
let app_id = req.app_id.to_string();
|
||||||
|
let key = (app_id.clone(), shortcut_id.clone());
|
||||||
|
let registry = &self.client.state.hyprland_global_shortcuts;
|
||||||
|
if registry.get(&key).is_some() {
|
||||||
|
self.client.protocol_error(
|
||||||
|
self,
|
||||||
|
ALREADY_TAKEN,
|
||||||
|
&format!(
|
||||||
|
"global shortcut with app_id={app_id:?} id={shortcut_id:?} is already registered"
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return Err(HyprlandGlobalShortcutsManagerV1Error::AlreadyTaken);
|
||||||
|
}
|
||||||
|
let shortcut = Rc::new(HyprlandGlobalShortcutV1::new(
|
||||||
|
req.shortcut,
|
||||||
|
&self.client,
|
||||||
|
self.version,
|
||||||
|
shortcut_id,
|
||||||
|
app_id,
|
||||||
|
));
|
||||||
|
track!(self.client, shortcut);
|
||||||
|
self.client.add_client_obj(&shortcut)?;
|
||||||
|
registry.set(key, shortcut);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
global_base!(
|
||||||
|
HyprlandGlobalShortcutsManagerV1Global,
|
||||||
|
HyprlandGlobalShortcutsManagerV1,
|
||||||
|
HyprlandGlobalShortcutsManagerV1Error
|
||||||
|
);
|
||||||
|
|
||||||
|
impl Global for HyprlandGlobalShortcutsManagerV1Global {
|
||||||
|
fn version(&self) -> u32 {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
simple_add_global!(HyprlandGlobalShortcutsManagerV1Global);
|
||||||
|
|
||||||
|
object_base! {
|
||||||
|
self = HyprlandGlobalShortcutsManagerV1;
|
||||||
|
version = self.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Object for HyprlandGlobalShortcutsManagerV1 {}
|
||||||
|
|
||||||
|
simple_add_obj!(HyprlandGlobalShortcutsManagerV1);
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum HyprlandGlobalShortcutsManagerV1Error {
|
||||||
|
#[error(transparent)]
|
||||||
|
ClientError(Box<ClientError>),
|
||||||
|
#[error("the app_id + id combination has already been registered")]
|
||||||
|
AlreadyTaken,
|
||||||
|
}
|
||||||
|
|
||||||
|
efrom!(HyprlandGlobalShortcutsManagerV1Error, ClientError);
|
||||||
|
|
@ -51,6 +51,7 @@ use {
|
||||||
HeadManagers, HeadNames,
|
HeadManagers, HeadNames,
|
||||||
jay_head_manager_session_v1::{HeadManagerEvent, JayHeadManagerSessionV1},
|
jay_head_manager_session_v1::{HeadManagerEvent, JayHeadManagerSessionV1},
|
||||||
},
|
},
|
||||||
|
hyprland_global_shortcut_v1::HyprlandGlobalShortcutV1,
|
||||||
ipc::{
|
ipc::{
|
||||||
DataOfferIds, DataSourceIds, data_control::DataControlDeviceIds,
|
DataOfferIds, DataSourceIds, data_control::DataControlDeviceIds,
|
||||||
x_data_device::XIpcDeviceIds,
|
x_data_device::XIpcDeviceIds,
|
||||||
|
|
@ -302,6 +303,8 @@ pub struct State {
|
||||||
pub bo_drop_queue: Rc<ObjectDropQueue<Rc<dyn BufferObject>>>,
|
pub bo_drop_queue: Rc<ObjectDropQueue<Rc<dyn BufferObject>>>,
|
||||||
pub virtual_outputs: VirtualOutputs,
|
pub virtual_outputs: VirtualOutputs,
|
||||||
pub clean_logs_older_than: Cell<Option<SystemTime>>,
|
pub clean_logs_older_than: Cell<Option<SystemTime>>,
|
||||||
|
pub hyprland_global_shortcuts:
|
||||||
|
CopyHashMap<(String, String), Rc<HyprlandGlobalShortcutV1>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl Drop for State {
|
// impl Drop for State {
|
||||||
|
|
|
||||||
|
|
@ -204,6 +204,10 @@ pub enum Action {
|
||||||
dx2: i32,
|
dx2: i32,
|
||||||
dy2: i32,
|
dy2: i32,
|
||||||
},
|
},
|
||||||
|
TriggerGlobalShortcut {
|
||||||
|
app_id: String,
|
||||||
|
id: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,10 @@ pub enum ActionParserError {
|
||||||
UnknownDirection(String),
|
UnknownDirection(String),
|
||||||
#[error("Exactly one of `output` or `direction` must be specified")]
|
#[error("Exactly one of `output` or `direction` must be specified")]
|
||||||
OutputAndDirectionMutuallyExclusive,
|
OutputAndDirectionMutuallyExclusive,
|
||||||
|
#[error("Specify either `name = \"app_id:id\"` or both `app_id` and `id`")]
|
||||||
|
GlobalShortcutNeedsName,
|
||||||
|
#[error("Global shortcut `name` must be of the form `app_id:id`, got `{0}`")]
|
||||||
|
GlobalShortcutBadName(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ActionParser<'a>(pub &'a Context<'a>);
|
pub struct ActionParser<'a>(pub &'a Context<'a>);
|
||||||
|
|
@ -516,6 +520,36 @@ impl ActionParser<'_> {
|
||||||
dy2: dy2.despan().unwrap_or(0),
|
dy2: dy2.despan().unwrap_or(0),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_global_shortcut(
|
||||||
|
&mut self,
|
||||||
|
span: Span,
|
||||||
|
ext: &mut Extractor<'_>,
|
||||||
|
) -> ParseResult<Self> {
|
||||||
|
let (name_opt, app_id_opt, id_opt) = ext.extract((
|
||||||
|
opt(str("name")),
|
||||||
|
opt(str("app_id")),
|
||||||
|
opt(str("id")),
|
||||||
|
))?;
|
||||||
|
let (app_id, id) = match (app_id_opt, id_opt, name_opt) {
|
||||||
|
(Some(a), Some(i), _) => (a.value.to_string(), i.value.to_string()),
|
||||||
|
(None, None, Some(n)) => match n.value.split_once(':') {
|
||||||
|
Some((a, i)) if !a.is_empty() && !i.is_empty() => {
|
||||||
|
(a.to_string(), i.to_string())
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(
|
||||||
|
ActionParserError::GlobalShortcutBadName(n.value.to_string())
|
||||||
|
.spanned(n.span),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
return Err(ActionParserError::GlobalShortcutNeedsName.spanned(span));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(Action::TriggerGlobalShortcut { app_id, id })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parser for ActionParser<'_> {
|
impl Parser for ActionParser<'_> {
|
||||||
|
|
@ -578,6 +612,7 @@ impl Parser for ActionParser<'_> {
|
||||||
"create-virtual-output" => self.parse_create_virtual_output(&mut ext),
|
"create-virtual-output" => self.parse_create_virtual_output(&mut ext),
|
||||||
"remove-virtual-output" => self.parse_remove_virtual_output(&mut ext),
|
"remove-virtual-output" => self.parse_remove_virtual_output(&mut ext),
|
||||||
"resize" => self.parse_resize(&mut ext),
|
"resize" => self.parse_resize(&mut ext),
|
||||||
|
"global-shortcut" => self.parse_global_shortcut(span, &mut ext),
|
||||||
v => {
|
v => {
|
||||||
ext.ignore_unused();
|
ext.ignore_unused();
|
||||||
return Err(ActionParserError::UnknownType(v.to_string()).spanned(ty.span));
|
return Err(ActionParserError::UnknownType(v.to_string()).spanned(ty.span));
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,10 @@ use {
|
||||||
ALT, CAPS, CTRL, LOCK, LOGO, MOD1, MOD2, MOD3, MOD4, MOD5, Modifiers, NUM, RELEASE,
|
ALT, CAPS, CTRL, LOCK, LOGO, MOD1, MOD2, MOD3, MOD4, MOD5, Modifiers, NUM, RELEASE,
|
||||||
SHIFT,
|
SHIFT,
|
||||||
},
|
},
|
||||||
syms::KeySym,
|
syms::{
|
||||||
|
KeySym, SYM_Alt_L, SYM_Alt_R, SYM_Control_L, SYM_Control_R, SYM_Hyper_L, SYM_Hyper_R,
|
||||||
|
SYM_Meta_L, SYM_Meta_R, SYM_Shift_L, SYM_Shift_R, SYM_Super_L, SYM_Super_R,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
kbvm::Keysym,
|
kbvm::Keysym,
|
||||||
thiserror::Error,
|
thiserror::Error,
|
||||||
|
|
@ -38,23 +41,28 @@ impl Parser for ModifiedKeysymParser {
|
||||||
|
|
||||||
fn parse_string(&mut self, span: Span, string: &str) -> ParseResult<Self> {
|
fn parse_string(&mut self, span: Span, string: &str) -> ParseResult<Self> {
|
||||||
let mut modifiers = Modifiers(0);
|
let mut modifiers = Modifiers(0);
|
||||||
let mut sym = None;
|
let mut sym: Option<KeySym> = None;
|
||||||
for part in string.split("-") {
|
for part in string.split("-") {
|
||||||
let modifier = match parse_mod(part) {
|
if let Some(m) = parse_mod(part) {
|
||||||
Some(m) => m,
|
modifiers |= m;
|
||||||
_ => match Keysym::from_str(part) {
|
continue;
|
||||||
Some(new) if sym.is_none() => {
|
}
|
||||||
sym = Some(KeySym(new.0));
|
let Some(new) = Keysym::from_str(part) else {
|
||||||
continue;
|
return Err(
|
||||||
}
|
ModifiedKeysymParserError::UnknownKeysym(part.to_string()).spanned(span),
|
||||||
Some(_) => return Err(ModifiedKeysymParserError::MoreThanOneSym.spanned(span)),
|
);
|
||||||
_ => {
|
|
||||||
return Err(ModifiedKeysymParserError::UnknownKeysym(part.to_string())
|
|
||||||
.spanned(span));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
modifiers |= modifier;
|
let new = KeySym(new.0);
|
||||||
|
match sym {
|
||||||
|
None => sym = Some(new),
|
||||||
|
Some(prev) => {
|
||||||
|
let Some(m) = modifier_key_to_mod(prev) else {
|
||||||
|
return Err(ModifiedKeysymParserError::MoreThanOneSym.spanned(span));
|
||||||
|
};
|
||||||
|
modifiers |= m;
|
||||||
|
sym = Some(new);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
match sym {
|
match sym {
|
||||||
Some(s) => Ok(modifiers | s),
|
Some(s) => Ok(modifiers | s),
|
||||||
|
|
@ -63,6 +71,20 @@ impl Parser for ModifiedKeysymParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
fn modifier_key_to_mod(sym: KeySym) -> Option<Modifiers> {
|
||||||
|
let m = match sym {
|
||||||
|
SYM_Super_L | SYM_Super_R => LOGO,
|
||||||
|
SYM_Alt_L | SYM_Alt_R => ALT,
|
||||||
|
SYM_Control_L | SYM_Control_R => CTRL,
|
||||||
|
SYM_Shift_L | SYM_Shift_R => SHIFT,
|
||||||
|
SYM_Meta_L | SYM_Meta_R => MOD1,
|
||||||
|
SYM_Hyper_L | SYM_Hyper_R => MOD4,
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
Some(m)
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ModifiersParser;
|
pub struct ModifiersParser;
|
||||||
|
|
||||||
impl Parser for ModifiersParser {
|
impl Parser for ModifiersParser {
|
||||||
|
|
|
||||||
|
|
@ -508,6 +508,11 @@ impl Action {
|
||||||
Action::Resize { dx1, dy1, dx2, dy2 } => {
|
Action::Resize { dx1, dy1, dx2, dy2 } => {
|
||||||
window_or_seat!(s, s.resize(dx1, dy1, dx2, dy2))
|
window_or_seat!(s, s.resize(dx1, dy1, dx2, dy2))
|
||||||
}
|
}
|
||||||
|
Action::TriggerGlobalShortcut { app_id, id } => {
|
||||||
|
b.new(move || {
|
||||||
|
jay_config::trigger_global_shortcut(&app_id, &id);
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
14
wire/hyprland_global_shortcut_v1.txt
Normal file
14
wire/hyprland_global_shortcut_v1.txt
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
request destroy (destructor) {
|
||||||
|
}
|
||||||
|
|
||||||
|
event pressed {
|
||||||
|
tv_sec_hi: u32,
|
||||||
|
tv_sec_lo: u32,
|
||||||
|
tv_nsec: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
event released {
|
||||||
|
tv_sec_hi: u32,
|
||||||
|
tv_sec_lo: u32,
|
||||||
|
tv_nsec: u32,
|
||||||
|
}
|
||||||
10
wire/hyprland_global_shortcuts_manager_v1.txt
Normal file
10
wire/hyprland_global_shortcuts_manager_v1.txt
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
request register_shortcut {
|
||||||
|
shortcut: id(hyprland_global_shortcut_v1) (new),
|
||||||
|
id: str,
|
||||||
|
app_id: str,
|
||||||
|
description: str,
|
||||||
|
trigger_description: str,
|
||||||
|
}
|
||||||
|
|
||||||
|
request destroy (destructor) {
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue