Skip to content

Commit bba8cb8

Browse files
committed
hyprland/global_shortcuts: add GlobalShortcut
1 parent 87a884c commit bba8cb8

14 files changed

+569
-6
lines changed

Diff for: CMakeLists.txt

+6
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ option(WAYLAND "Enable wayland support" ON)
1515
option(WAYLAND_WLR_LAYERSHELL "Support the zwlr_layer_shell_v1 wayland protocol" ON)
1616
option(WAYLAND_SESSION_LOCK "Support the ext_session_lock_v1 wayland protocol" ON)
1717
option(HYPRLAND "Support hyprland specific features" ON)
18+
option(HYPRLAND_GLOBAL_SHORTCUTS "Hyprland Global Shortcuts" ON)
19+
option(HYPRLAND_FOCUS_GRAB "Hyprland Focus Grabbing" ON)
1820
option(SERVICE_STATUS_NOTIFIER "StatusNotifierItem service" ON)
1921

2022
message(STATUS "Quickshell configuration")
@@ -29,6 +31,10 @@ endif ()
2931
message(STATUS " Services")
3032
message(STATUS " StatusNotifier: ${SERVICE_STATUS_NOTIFIER}")
3133
message(STATUS " Hyprland: ${HYPRLAND}")
34+
if (HYPRLAND)
35+
message(STATUS " Focus Grabbing: ${HYPRLAND_FOCUS_GRAB}")
36+
message(STATUS " Global Shortcuts: ${HYPRLAND_GLOBAL_SHORTCUTS}")
37+
endif()
3238

3339
if (NOT DEFINED GIT_REVISION)
3440
execute_process(

Diff for: src/services/CMakeLists.txt

+3-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1-
add_subdirectory(status_notifier)
1+
if (SERVICE_STATUS_NOTIFIER)
2+
add_subdirectory(status_notifier)
3+
endif()

Diff for: src/wayland/hyprland/CMakeLists.txt

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
qt_add_library(quickshell-hyprland STATIC)
22
qt_add_qml_module(quickshell-hyprland URI Quickshell.Hyprland VERSION 0.1)
33

4-
add_subdirectory(focus_grab)
4+
if (HYPRLAND_FOCUS_GRAB)
5+
add_subdirectory(focus_grab)
6+
endif()
7+
8+
if (HYPRLAND_GLOBAL_SHORTCUTS)
9+
add_subdirectory(global_shortcuts)
10+
endif()
511

612
target_link_libraries(quickshell-hyprland PRIVATE ${QT_DEPS})
713

Diff for: src/wayland/hyprland/focus_grab/CMakeLists.txt

+13-3
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,26 @@ qt_add_library(quickshell-hyprland-focus-grab STATIC
44
qml.cpp
55
)
66

7-
qt_add_qml_module(quickshell-hyprland-focus-grab URI Quickshell.Hyprland._FocusGrab VERSION 0.1)
7+
qt_add_qml_module(quickshell-hyprland-focus-grab
8+
URI Quickshell.Hyprland._FocusGrab
9+
VERSION 0.1
10+
)
811

912
add_library(quickshell-hyprland-focus-grab-init OBJECT init.cpp)
1013

11-
wl_proto(quickshell-hyprland-focus-grab hyprland-focus-grab-v1 "${CMAKE_CURRENT_SOURCE_DIR}/hyprland-focus-grab-v1.xml")
14+
wl_proto(quickshell-hyprland-focus-grab
15+
hyprland-focus-grab-v1
16+
"${CMAKE_CURRENT_SOURCE_DIR}/hyprland-focus-grab-v1.xml"
17+
)
18+
1219
target_link_libraries(quickshell-hyprland-focus-grab PRIVATE ${QT_DEPS} wayland-client)
1320
target_link_libraries(quickshell-hyprland-focus-grab-init PRIVATE ${QT_DEPS})
1421

1522
qs_pch(quickshell-hyprland-focus-grab)
1623
qs_pch(quickshell-hyprland-focus-grabplugin)
1724
qs_pch(quickshell-hyprland-focus-grab-init)
1825

19-
target_link_libraries(quickshell PRIVATE quickshell-hyprland-focus-grabplugin quickshell-hyprland-focus-grab-init)
26+
target_link_libraries(quickshell PRIVATE
27+
quickshell-hyprland-focus-grabplugin
28+
quickshell-hyprland-focus-grab-init
29+
)

Diff for: src/wayland/hyprland/global_shortcuts/CMakeLists.txt

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
qt_add_library(quickshell-hyprland-global-shortcuts STATIC
2+
qml.cpp
3+
manager.cpp
4+
shortcut.cpp
5+
)
6+
7+
qt_add_qml_module(quickshell-hyprland-global-shortcuts
8+
URI Quickshell.Hyprland._GlobalShortcuts
9+
VERSION 0.1
10+
)
11+
12+
add_library(quickshell-hyprland-global-shortcuts-init OBJECT init.cpp)
13+
14+
wl_proto(quickshell-hyprland-global-shortcuts
15+
hyprland-global-shortcuts-v1
16+
"${CMAKE_CURRENT_SOURCE_DIR}/hyprland-global-shortcuts-v1.xml"
17+
)
18+
19+
target_link_libraries(quickshell-hyprland-global-shortcuts PRIVATE ${QT_DEPS} wayland-client)
20+
target_link_libraries(quickshell-hyprland-global-shortcuts-init PRIVATE ${QT_DEPS})
21+
22+
qs_pch(quickshell-hyprland-global-shortcuts)
23+
qs_pch(quickshell-hyprland-global-shortcutsplugin)
24+
qs_pch(quickshell-hyprland-global-shortcuts-init)
25+
26+
target_link_libraries(quickshell PRIVATE
27+
quickshell-hyprland-global-shortcutsplugin
28+
quickshell-hyprland-global-shortcuts-init
29+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<protocol name="hyprland_global_shortcuts_v1">
3+
<copyright>
4+
Copyright © 2022 Vaxry
5+
All rights reserved.
6+
7+
Redistribution and use in source and binary forms, with or without
8+
modification, are permitted provided that the following conditions are met:
9+
10+
1. Redistributions of source code must retain the above copyright notice, this
11+
list of conditions and the following disclaimer.
12+
13+
2. Redistributions in binary form must reproduce the above copyright notice,
14+
this list of conditions and the following disclaimer in the documentation
15+
and/or other materials provided with the distribution.
16+
17+
3. Neither the name of the copyright holder nor the names of its
18+
contributors may be used to endorse or promote products derived from
19+
this software without specific prior written permission.
20+
21+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31+
</copyright>
32+
33+
<description summary="registering global shortcuts">
34+
This protocol allows a client to register triggerable actions,
35+
meant to be global shortcuts.
36+
</description>
37+
38+
<interface name="hyprland_global_shortcuts_manager_v1" version="1">
39+
<description summary="manager to register global shortcuts">
40+
This object is a manager which offers requests to create global shortcuts.
41+
</description>
42+
43+
<request name="register_shortcut">
44+
<description summary="register a shortcut">
45+
Register a new global shortcut.
46+
47+
A global shortcut is anonymous, meaning the app does not know what key(s) trigger it.
48+
49+
The shortcut's keybinding shall be dealt with by the compositor.
50+
51+
In the case of a duplicate app_id + id combination, the already_taken protocol error is raised.
52+
</description>
53+
<arg name="shortcut" type="new_id" interface="hyprland_global_shortcut_v1"/>
54+
<arg name="id" type="string" summary="a unique id for the shortcut"/>
55+
<arg name="app_id" type="string" summary="the app_id of the application requesting the shortcut"/>
56+
<arg name="description" type="string" summary="user-readable text describing what the shortcut does."/>
57+
<arg name="trigger_description" type="string" summary="user-readable text describing how to trigger the shortcut for the client to render."/>
58+
</request>
59+
60+
<request name="destroy" type="destructor">
61+
<description summary="destroy the manager">
62+
All objects created by the manager will still remain valid, until their
63+
appropriate destroy request has been called.
64+
</description>
65+
</request>
66+
67+
<enum name="error">
68+
<entry name="already_taken" value="0"
69+
summary="the app_id + id combination has already been registered."/>
70+
</enum>
71+
</interface>
72+
73+
<interface name="hyprland_global_shortcut_v1" version="1">
74+
<description summary="a shortcut">
75+
This object represents a single shortcut.
76+
</description>
77+
78+
<event name="pressed">
79+
<description summary="keystroke pressed">
80+
The keystroke was pressed.
81+
82+
tv_ values hold the timestamp of the occurrence.
83+
</description>
84+
<arg name="tv_sec_hi" type="uint"
85+
summary="high 32 bits of the seconds part of the timestamp"/>
86+
<arg name="tv_sec_lo" type="uint"
87+
summary="low 32 bits of the seconds part of the timestamp"/>
88+
<arg name="tv_nsec" type="uint"
89+
summary="nanoseconds part of the timestamp"/>
90+
</event>
91+
92+
<event name="released">
93+
<description summary="keystroke released">
94+
The keystroke was released.
95+
96+
tv_ values hold the timestamp of the occurrence.
97+
</description>
98+
<arg name="tv_sec_hi" type="uint"
99+
summary="high 32 bits of the seconds part of the timestamp"/>
100+
<arg name="tv_sec_lo" type="uint"
101+
summary="low 32 bits of the seconds part of the timestamp"/>
102+
<arg name="tv_nsec" type="uint"
103+
summary="nanoseconds part of the timestamp"/>
104+
</event>
105+
106+
<request name="destroy" type="destructor">
107+
<description summary="delete this object, used or not">
108+
Destroys the shortcut. Can be sent at any time by the client.
109+
</description>
110+
</request>
111+
</interface>
112+
</protocol>

Diff for: src/wayland/hyprland/global_shortcuts/init.cpp

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#include <qqml.h>
2+
3+
#include "../../../core/plugin.hpp"
4+
5+
namespace {
6+
7+
class HyprlandFocusGrabPlugin: public QuickshellPlugin {
8+
void registerTypes() override {
9+
qmlRegisterModuleImport(
10+
"Quickshell.Hyprland",
11+
QQmlModuleImportModuleAny,
12+
"Quickshell.Hyprland._GlobalShortcuts",
13+
QQmlModuleImportLatest
14+
);
15+
}
16+
};
17+
18+
QS_REGISTER_PLUGIN(HyprlandFocusGrabPlugin);
19+
20+
} // namespace

Diff for: src/wayland/hyprland/global_shortcuts/manager.cpp

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#include "manager.hpp"
2+
3+
#include <qstring.h>
4+
#include <qwaylandclientextension.h>
5+
6+
#include "shortcut.hpp"
7+
8+
namespace qs::hyprland::global_shortcuts::impl {
9+
10+
GlobalShortcutManager::GlobalShortcutManager()
11+
: QWaylandClientExtensionTemplate<GlobalShortcutManager>(1) {
12+
this->initialize();
13+
}
14+
15+
GlobalShortcut* GlobalShortcutManager::registerShortcut(
16+
const QString& appid,
17+
const QString& name,
18+
const QString& description,
19+
const QString& triggerDescription
20+
) {
21+
auto shortcut = this->shortcuts.value({appid, name});
22+
23+
if (shortcut.second != nullptr) {
24+
this->shortcuts.insert({appid, name}, {shortcut.first + 1, shortcut.second});
25+
return shortcut.second;
26+
} else {
27+
auto* shortcutObj = this->register_shortcut(name, appid, description, triggerDescription);
28+
auto* managedObj = new GlobalShortcut(shortcutObj);
29+
this->shortcuts.insert({appid, name}, {1, managedObj});
30+
return managedObj;
31+
}
32+
}
33+
34+
void GlobalShortcutManager::unregisterShortcut(const QString& appid, const QString& name) {
35+
auto shortcut = this->shortcuts.value({appid, name});
36+
37+
if (shortcut.first > 1) {
38+
this->shortcuts.insert({appid, name}, {shortcut.first - 1, shortcut.second});
39+
} else {
40+
delete shortcut.second;
41+
this->shortcuts.remove({appid, name});
42+
}
43+
}
44+
45+
GlobalShortcutManager* GlobalShortcutManager::instance() {
46+
static GlobalShortcutManager* instance = nullptr; // NOLINT
47+
48+
if (instance == nullptr) {
49+
instance = new GlobalShortcutManager();
50+
}
51+
52+
return instance;
53+
}
54+
55+
} // namespace qs::hyprland::global_shortcuts::impl

Diff for: src/wayland/hyprland/global_shortcuts/manager.hpp

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#pragma once
2+
3+
#include <qcontainerfwd.h>
4+
#include <qhash.h>
5+
#include <qstring.h>
6+
#include <qwayland-hyprland-global-shortcuts-v1.h>
7+
#include <qwaylandclientextension.h>
8+
9+
#include "shortcut.hpp"
10+
11+
namespace qs::hyprland::global_shortcuts::impl {
12+
13+
class GlobalShortcutManager
14+
: public QWaylandClientExtensionTemplate<GlobalShortcutManager>
15+
, public QtWayland::hyprland_global_shortcuts_manager_v1 {
16+
public:
17+
explicit GlobalShortcutManager();
18+
19+
GlobalShortcut* registerShortcut(
20+
const QString& appid,
21+
const QString& name,
22+
const QString& description,
23+
const QString& triggerDescription
24+
);
25+
26+
void unregisterShortcut(const QString& appid, const QString& name);
27+
28+
static GlobalShortcutManager* instance();
29+
30+
private:
31+
QHash<QPair<QString, QString>, QPair<qint32, GlobalShortcut*>> shortcuts;
32+
};
33+
34+
} // namespace qs::hyprland::global_shortcuts::impl

0 commit comments

Comments
 (0)