forked from quickshell/quickshell
hyprland/global_shortcuts: add GlobalShortcut
This commit is contained in:
parent
87a884ca36
commit
bba8cb8a7d
|
@ -15,6 +15,8 @@ option(WAYLAND "Enable wayland support" ON)
|
||||||
option(WAYLAND_WLR_LAYERSHELL "Support the zwlr_layer_shell_v1 wayland protocol" ON)
|
option(WAYLAND_WLR_LAYERSHELL "Support the zwlr_layer_shell_v1 wayland protocol" ON)
|
||||||
option(WAYLAND_SESSION_LOCK "Support the ext_session_lock_v1 wayland protocol" ON)
|
option(WAYLAND_SESSION_LOCK "Support the ext_session_lock_v1 wayland protocol" ON)
|
||||||
option(HYPRLAND "Support hyprland specific features" ON)
|
option(HYPRLAND "Support hyprland specific features" ON)
|
||||||
|
option(HYPRLAND_GLOBAL_SHORTCUTS "Hyprland Global Shortcuts" ON)
|
||||||
|
option(HYPRLAND_FOCUS_GRAB "Hyprland Focus Grabbing" ON)
|
||||||
option(SERVICE_STATUS_NOTIFIER "StatusNotifierItem service" ON)
|
option(SERVICE_STATUS_NOTIFIER "StatusNotifierItem service" ON)
|
||||||
|
|
||||||
message(STATUS "Quickshell configuration")
|
message(STATUS "Quickshell configuration")
|
||||||
|
@ -29,6 +31,10 @@ endif ()
|
||||||
message(STATUS " Services")
|
message(STATUS " Services")
|
||||||
message(STATUS " StatusNotifier: ${SERVICE_STATUS_NOTIFIER}")
|
message(STATUS " StatusNotifier: ${SERVICE_STATUS_NOTIFIER}")
|
||||||
message(STATUS " Hyprland: ${HYPRLAND}")
|
message(STATUS " Hyprland: ${HYPRLAND}")
|
||||||
|
if (HYPRLAND)
|
||||||
|
message(STATUS " Focus Grabbing: ${HYPRLAND_FOCUS_GRAB}")
|
||||||
|
message(STATUS " Global Shortcuts: ${HYPRLAND_GLOBAL_SHORTCUTS}")
|
||||||
|
endif()
|
||||||
|
|
||||||
if (NOT DEFINED GIT_REVISION)
|
if (NOT DEFINED GIT_REVISION)
|
||||||
execute_process(
|
execute_process(
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
|
if (SERVICE_STATUS_NOTIFIER)
|
||||||
add_subdirectory(status_notifier)
|
add_subdirectory(status_notifier)
|
||||||
|
endif()
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
qt_add_library(quickshell-hyprland STATIC)
|
qt_add_library(quickshell-hyprland STATIC)
|
||||||
qt_add_qml_module(quickshell-hyprland URI Quickshell.Hyprland VERSION 0.1)
|
qt_add_qml_module(quickshell-hyprland URI Quickshell.Hyprland VERSION 0.1)
|
||||||
|
|
||||||
|
if (HYPRLAND_FOCUS_GRAB)
|
||||||
add_subdirectory(focus_grab)
|
add_subdirectory(focus_grab)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (HYPRLAND_GLOBAL_SHORTCUTS)
|
||||||
|
add_subdirectory(global_shortcuts)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_link_libraries(quickshell-hyprland PRIVATE ${QT_DEPS})
|
target_link_libraries(quickshell-hyprland PRIVATE ${QT_DEPS})
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,18 @@ qt_add_library(quickshell-hyprland-focus-grab STATIC
|
||||||
qml.cpp
|
qml.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
qt_add_qml_module(quickshell-hyprland-focus-grab URI Quickshell.Hyprland._FocusGrab VERSION 0.1)
|
qt_add_qml_module(quickshell-hyprland-focus-grab
|
||||||
|
URI Quickshell.Hyprland._FocusGrab
|
||||||
|
VERSION 0.1
|
||||||
|
)
|
||||||
|
|
||||||
add_library(quickshell-hyprland-focus-grab-init OBJECT init.cpp)
|
add_library(quickshell-hyprland-focus-grab-init OBJECT init.cpp)
|
||||||
|
|
||||||
wl_proto(quickshell-hyprland-focus-grab hyprland-focus-grab-v1 "${CMAKE_CURRENT_SOURCE_DIR}/hyprland-focus-grab-v1.xml")
|
wl_proto(quickshell-hyprland-focus-grab
|
||||||
|
hyprland-focus-grab-v1
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/hyprland-focus-grab-v1.xml"
|
||||||
|
)
|
||||||
|
|
||||||
target_link_libraries(quickshell-hyprland-focus-grab PRIVATE ${QT_DEPS} wayland-client)
|
target_link_libraries(quickshell-hyprland-focus-grab PRIVATE ${QT_DEPS} wayland-client)
|
||||||
target_link_libraries(quickshell-hyprland-focus-grab-init PRIVATE ${QT_DEPS})
|
target_link_libraries(quickshell-hyprland-focus-grab-init PRIVATE ${QT_DEPS})
|
||||||
|
|
||||||
|
@ -16,4 +23,7 @@ qs_pch(quickshell-hyprland-focus-grab)
|
||||||
qs_pch(quickshell-hyprland-focus-grabplugin)
|
qs_pch(quickshell-hyprland-focus-grabplugin)
|
||||||
qs_pch(quickshell-hyprland-focus-grab-init)
|
qs_pch(quickshell-hyprland-focus-grab-init)
|
||||||
|
|
||||||
target_link_libraries(quickshell PRIVATE quickshell-hyprland-focus-grabplugin quickshell-hyprland-focus-grab-init)
|
target_link_libraries(quickshell PRIVATE
|
||||||
|
quickshell-hyprland-focus-grabplugin
|
||||||
|
quickshell-hyprland-focus-grab-init
|
||||||
|
)
|
||||||
|
|
29
src/wayland/hyprland/global_shortcuts/CMakeLists.txt
Normal file
29
src/wayland/hyprland/global_shortcuts/CMakeLists.txt
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
qt_add_library(quickshell-hyprland-global-shortcuts STATIC
|
||||||
|
qml.cpp
|
||||||
|
manager.cpp
|
||||||
|
shortcut.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
qt_add_qml_module(quickshell-hyprland-global-shortcuts
|
||||||
|
URI Quickshell.Hyprland._GlobalShortcuts
|
||||||
|
VERSION 0.1
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(quickshell-hyprland-global-shortcuts-init OBJECT init.cpp)
|
||||||
|
|
||||||
|
wl_proto(quickshell-hyprland-global-shortcuts
|
||||||
|
hyprland-global-shortcuts-v1
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/hyprland-global-shortcuts-v1.xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(quickshell-hyprland-global-shortcuts PRIVATE ${QT_DEPS} wayland-client)
|
||||||
|
target_link_libraries(quickshell-hyprland-global-shortcuts-init PRIVATE ${QT_DEPS})
|
||||||
|
|
||||||
|
qs_pch(quickshell-hyprland-global-shortcuts)
|
||||||
|
qs_pch(quickshell-hyprland-global-shortcutsplugin)
|
||||||
|
qs_pch(quickshell-hyprland-global-shortcuts-init)
|
||||||
|
|
||||||
|
target_link_libraries(quickshell PRIVATE
|
||||||
|
quickshell-hyprland-global-shortcutsplugin
|
||||||
|
quickshell-hyprland-global-shortcuts-init
|
||||||
|
)
|
|
@ -0,0 +1,112 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<protocol name="hyprland_global_shortcuts_v1">
|
||||||
|
<copyright>
|
||||||
|
Copyright © 2022 Vaxry
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
</copyright>
|
||||||
|
|
||||||
|
<description summary="registering global shortcuts">
|
||||||
|
This protocol allows a client to register triggerable actions,
|
||||||
|
meant to be global shortcuts.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<interface name="hyprland_global_shortcuts_manager_v1" version="1">
|
||||||
|
<description summary="manager to register global shortcuts">
|
||||||
|
This object is a manager which offers requests to create global shortcuts.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="register_shortcut">
|
||||||
|
<description summary="register a shortcut">
|
||||||
|
Register a new global shortcut.
|
||||||
|
|
||||||
|
A global shortcut is anonymous, meaning the app does not know what key(s) trigger it.
|
||||||
|
|
||||||
|
The shortcut's keybinding shall be dealt with by the compositor.
|
||||||
|
|
||||||
|
In the case of a duplicate app_id + id combination, the already_taken protocol error is raised.
|
||||||
|
</description>
|
||||||
|
<arg name="shortcut" type="new_id" interface="hyprland_global_shortcut_v1"/>
|
||||||
|
<arg name="id" type="string" summary="a unique id for the shortcut"/>
|
||||||
|
<arg name="app_id" type="string" summary="the app_id of the application requesting the shortcut"/>
|
||||||
|
<arg name="description" type="string" summary="user-readable text describing what the shortcut does."/>
|
||||||
|
<arg name="trigger_description" type="string" summary="user-readable text describing how to trigger the shortcut for the client to render."/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="destroy the manager">
|
||||||
|
All objects created by the manager will still remain valid, until their
|
||||||
|
appropriate destroy request has been called.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<enum name="error">
|
||||||
|
<entry name="already_taken" value="0"
|
||||||
|
summary="the app_id + id combination has already been registered."/>
|
||||||
|
</enum>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="hyprland_global_shortcut_v1" version="1">
|
||||||
|
<description summary="a shortcut">
|
||||||
|
This object represents a single shortcut.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<event name="pressed">
|
||||||
|
<description summary="keystroke pressed">
|
||||||
|
The keystroke was pressed.
|
||||||
|
|
||||||
|
tv_ values hold the timestamp of the occurrence.
|
||||||
|
</description>
|
||||||
|
<arg name="tv_sec_hi" type="uint"
|
||||||
|
summary="high 32 bits of the seconds part of the timestamp"/>
|
||||||
|
<arg name="tv_sec_lo" type="uint"
|
||||||
|
summary="low 32 bits of the seconds part of the timestamp"/>
|
||||||
|
<arg name="tv_nsec" type="uint"
|
||||||
|
summary="nanoseconds part of the timestamp"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="released">
|
||||||
|
<description summary="keystroke released">
|
||||||
|
The keystroke was released.
|
||||||
|
|
||||||
|
tv_ values hold the timestamp of the occurrence.
|
||||||
|
</description>
|
||||||
|
<arg name="tv_sec_hi" type="uint"
|
||||||
|
summary="high 32 bits of the seconds part of the timestamp"/>
|
||||||
|
<arg name="tv_sec_lo" type="uint"
|
||||||
|
summary="low 32 bits of the seconds part of the timestamp"/>
|
||||||
|
<arg name="tv_nsec" type="uint"
|
||||||
|
summary="nanoseconds part of the timestamp"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="delete this object, used or not">
|
||||||
|
Destroys the shortcut. Can be sent at any time by the client.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
</interface>
|
||||||
|
</protocol>
|
20
src/wayland/hyprland/global_shortcuts/init.cpp
Normal file
20
src/wayland/hyprland/global_shortcuts/init.cpp
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#include <qqml.h>
|
||||||
|
|
||||||
|
#include "../../../core/plugin.hpp"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class HyprlandFocusGrabPlugin: public QuickshellPlugin {
|
||||||
|
void registerTypes() override {
|
||||||
|
qmlRegisterModuleImport(
|
||||||
|
"Quickshell.Hyprland",
|
||||||
|
QQmlModuleImportModuleAny,
|
||||||
|
"Quickshell.Hyprland._GlobalShortcuts",
|
||||||
|
QQmlModuleImportLatest
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
QS_REGISTER_PLUGIN(HyprlandFocusGrabPlugin);
|
||||||
|
|
||||||
|
} // namespace
|
55
src/wayland/hyprland/global_shortcuts/manager.cpp
Normal file
55
src/wayland/hyprland/global_shortcuts/manager.cpp
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
#include "manager.hpp"
|
||||||
|
|
||||||
|
#include <qstring.h>
|
||||||
|
#include <qwaylandclientextension.h>
|
||||||
|
|
||||||
|
#include "shortcut.hpp"
|
||||||
|
|
||||||
|
namespace qs::hyprland::global_shortcuts::impl {
|
||||||
|
|
||||||
|
GlobalShortcutManager::GlobalShortcutManager()
|
||||||
|
: QWaylandClientExtensionTemplate<GlobalShortcutManager>(1) {
|
||||||
|
this->initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
GlobalShortcut* GlobalShortcutManager::registerShortcut(
|
||||||
|
const QString& appid,
|
||||||
|
const QString& name,
|
||||||
|
const QString& description,
|
||||||
|
const QString& triggerDescription
|
||||||
|
) {
|
||||||
|
auto shortcut = this->shortcuts.value({appid, name});
|
||||||
|
|
||||||
|
if (shortcut.second != nullptr) {
|
||||||
|
this->shortcuts.insert({appid, name}, {shortcut.first + 1, shortcut.second});
|
||||||
|
return shortcut.second;
|
||||||
|
} else {
|
||||||
|
auto* shortcutObj = this->register_shortcut(name, appid, description, triggerDescription);
|
||||||
|
auto* managedObj = new GlobalShortcut(shortcutObj);
|
||||||
|
this->shortcuts.insert({appid, name}, {1, managedObj});
|
||||||
|
return managedObj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalShortcutManager::unregisterShortcut(const QString& appid, const QString& name) {
|
||||||
|
auto shortcut = this->shortcuts.value({appid, name});
|
||||||
|
|
||||||
|
if (shortcut.first > 1) {
|
||||||
|
this->shortcuts.insert({appid, name}, {shortcut.first - 1, shortcut.second});
|
||||||
|
} else {
|
||||||
|
delete shortcut.second;
|
||||||
|
this->shortcuts.remove({appid, name});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GlobalShortcutManager* GlobalShortcutManager::instance() {
|
||||||
|
static GlobalShortcutManager* instance = nullptr; // NOLINT
|
||||||
|
|
||||||
|
if (instance == nullptr) {
|
||||||
|
instance = new GlobalShortcutManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace qs::hyprland::global_shortcuts::impl
|
34
src/wayland/hyprland/global_shortcuts/manager.hpp
Normal file
34
src/wayland/hyprland/global_shortcuts/manager.hpp
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <qcontainerfwd.h>
|
||||||
|
#include <qhash.h>
|
||||||
|
#include <qstring.h>
|
||||||
|
#include <qwayland-hyprland-global-shortcuts-v1.h>
|
||||||
|
#include <qwaylandclientextension.h>
|
||||||
|
|
||||||
|
#include "shortcut.hpp"
|
||||||
|
|
||||||
|
namespace qs::hyprland::global_shortcuts::impl {
|
||||||
|
|
||||||
|
class GlobalShortcutManager
|
||||||
|
: public QWaylandClientExtensionTemplate<GlobalShortcutManager>
|
||||||
|
, public QtWayland::hyprland_global_shortcuts_manager_v1 {
|
||||||
|
public:
|
||||||
|
explicit GlobalShortcutManager();
|
||||||
|
|
||||||
|
GlobalShortcut* registerShortcut(
|
||||||
|
const QString& appid,
|
||||||
|
const QString& name,
|
||||||
|
const QString& description,
|
||||||
|
const QString& triggerDescription
|
||||||
|
);
|
||||||
|
|
||||||
|
void unregisterShortcut(const QString& appid, const QString& name);
|
||||||
|
|
||||||
|
static GlobalShortcutManager* instance();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QHash<QPair<QString, QString>, QPair<qint32, GlobalShortcut*>> shortcuts;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace qs::hyprland::global_shortcuts::impl
|
115
src/wayland/hyprland/global_shortcuts/qml.cpp
Normal file
115
src/wayland/hyprland/global_shortcuts/qml.cpp
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
#include "qml.hpp"
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <qlogging.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
|
||||||
|
#include "manager.hpp"
|
||||||
|
#include "shortcut.hpp"
|
||||||
|
|
||||||
|
namespace qs::hyprland::global_shortcuts {
|
||||||
|
using impl::GlobalShortcutManager;
|
||||||
|
|
||||||
|
GlobalShortcut::~GlobalShortcut() {
|
||||||
|
auto* manager = GlobalShortcutManager::instance();
|
||||||
|
if (manager != nullptr) {
|
||||||
|
manager->unregisterShortcut(this->mAppid, this->mName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalShortcut::onPostReload() {
|
||||||
|
if (this->mName.isEmpty()) {
|
||||||
|
qWarning() << "Unable to create GlobalShortcut with empty name.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* manager = GlobalShortcutManager::instance();
|
||||||
|
if (manager == nullptr) {
|
||||||
|
qWarning() << "The active compositor does not support hyprland_global_shortcuts_v1.";
|
||||||
|
qWarning() << "GlobalShortcut will not work.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->shortcut = manager->registerShortcut(
|
||||||
|
this->mAppid,
|
||||||
|
this->mName,
|
||||||
|
this->mDescription,
|
||||||
|
this->mTriggerDescription
|
||||||
|
);
|
||||||
|
|
||||||
|
QObject::connect(this->shortcut, &ShortcutImpl::pressed, this, &GlobalShortcut::onPressed);
|
||||||
|
QObject::connect(this->shortcut, &ShortcutImpl::released, this, &GlobalShortcut::onReleased);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GlobalShortcut::isPressed() const { return this->mPressed; }
|
||||||
|
|
||||||
|
QString GlobalShortcut::appid() const { return this->mAppid; }
|
||||||
|
|
||||||
|
void GlobalShortcut::setAppid(QString appid) {
|
||||||
|
if (this->shortcut != nullptr) {
|
||||||
|
qWarning() << "GlobalShortcut cannot be modified after creation.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (appid == this->mAppid) return;
|
||||||
|
|
||||||
|
this->mAppid = std::move(appid);
|
||||||
|
emit this->appidChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString GlobalShortcut::name() const { return this->mName; }
|
||||||
|
|
||||||
|
void GlobalShortcut::setName(QString name) {
|
||||||
|
if (this->shortcut != nullptr) {
|
||||||
|
qWarning() << "GlobalShortcut cannot be modified after creation.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name == this->mName) return;
|
||||||
|
|
||||||
|
this->mName = std::move(name);
|
||||||
|
emit this->nameChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString GlobalShortcut::description() const { return this->mDescription; }
|
||||||
|
|
||||||
|
void GlobalShortcut::setDescription(QString description) {
|
||||||
|
if (this->shortcut != nullptr) {
|
||||||
|
qWarning() << "GlobalShortcut cannot be modified after creation.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (description == this->mDescription) return;
|
||||||
|
|
||||||
|
this->mDescription = std::move(description);
|
||||||
|
emit this->descriptionChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString GlobalShortcut::triggerDescription() const { return this->mTriggerDescription; }
|
||||||
|
|
||||||
|
void GlobalShortcut::setTriggerDescription(QString triggerDescription) {
|
||||||
|
if (this->shortcut != nullptr) {
|
||||||
|
qWarning() << "GlobalShortcut cannot be modified after creation.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (triggerDescription == this->mTriggerDescription) return;
|
||||||
|
|
||||||
|
this->mTriggerDescription = std::move(triggerDescription);
|
||||||
|
emit this->triggerDescriptionChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalShortcut::onPressed() {
|
||||||
|
this->mPressed = true;
|
||||||
|
emit this->pressed();
|
||||||
|
emit this->pressedChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalShortcut::onReleased() {
|
||||||
|
this->mPressed = false;
|
||||||
|
emit this->released();
|
||||||
|
emit this->pressedChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace qs::hyprland::global_shortcuts
|
108
src/wayland/hyprland/global_shortcuts/qml.hpp
Normal file
108
src/wayland/hyprland/global_shortcuts/qml.hpp
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qqmlintegration.h>
|
||||||
|
#include <qtclasshelpermacros.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
|
||||||
|
#include "../../../core/reload.hpp"
|
||||||
|
#include "shortcut.hpp"
|
||||||
|
|
||||||
|
namespace qs::hyprland::global_shortcuts {
|
||||||
|
|
||||||
|
///! Hyprland global shortcut.
|
||||||
|
/// Global shortcut implemented with [hyprland_global_shortcuts_v1].
|
||||||
|
///
|
||||||
|
/// You can use this within hyprland as a global shortcut:
|
||||||
|
/// ```
|
||||||
|
/// bind = <modifiers>, <key>, global, <appid>:<name>
|
||||||
|
/// ```
|
||||||
|
/// See [the wiki] for details.
|
||||||
|
///
|
||||||
|
/// > [!WARNING] The shortcuts protocol does not allow duplicate appid + name pairs.
|
||||||
|
/// > Within a single instance of quickshell this is handled internally, and both
|
||||||
|
/// > users will be notified, but multiple instances of quickshell or XDPH may collide.
|
||||||
|
/// >
|
||||||
|
/// > If that happens, whichever client that tries to register the shortcuts last will crash.
|
||||||
|
///
|
||||||
|
/// > [!INFO] This type does *not* use the xdg-desktop-portal global shortcuts protocol,
|
||||||
|
/// > as it is not fully functional without flatpak and would cause a considerably worse
|
||||||
|
/// > user experience from other limitations. It will only work with Hyprland.
|
||||||
|
/// > Note that, as this type bypasses xdg-desktop-portal, XDPH is not required.
|
||||||
|
///
|
||||||
|
/// [hyprland_global_shortcuts_v1]: https://github.com/hyprwm/hyprland-protocols/blob/main/protocols/hyprland-global-shortcuts-v1.xml
|
||||||
|
/// [the wiki]: https://wiki.hyprland.org/Configuring/Binds/#dbus-global-shortcuts
|
||||||
|
class GlobalShortcut
|
||||||
|
: public QObject
|
||||||
|
, public PostReloadHook {
|
||||||
|
using ShortcutImpl = impl::GlobalShortcut;
|
||||||
|
|
||||||
|
Q_OBJECT;
|
||||||
|
// clang-format off
|
||||||
|
/// If the keybind is currently pressed.
|
||||||
|
Q_PROPERTY(bool pressed READ isPressed NOTIFY pressedChanged);
|
||||||
|
/// The appid of the shortcut. Defaults to `quickshell`.
|
||||||
|
/// You cannot change this at runtime.
|
||||||
|
///
|
||||||
|
/// If you have more than one shortcut we recommend subclassing
|
||||||
|
/// GlobalShortcut to set this.
|
||||||
|
Q_PROPERTY(QString appid READ appid WRITE setAppid NOTIFY appidChanged);
|
||||||
|
/// The name of the shortcut.
|
||||||
|
/// You cannot change this at runtime.
|
||||||
|
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged);
|
||||||
|
/// The description of the shortcut that appears in `hyprctl globalshortcuts`.
|
||||||
|
/// You cannot change this at runtime.
|
||||||
|
Q_PROPERTY(QString description READ description WRITE setDescription NOTIFY descriptionChanged);
|
||||||
|
/// Have not seen this used ever, but included for completeness. Safe to ignore.
|
||||||
|
Q_PROPERTY(QString triggerDescription READ triggerDescription WRITE setTriggerDescription NOTIFY triggerDescriptionChanged);
|
||||||
|
// clang-format on
|
||||||
|
QML_ELEMENT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit GlobalShortcut(QObject* parent = nullptr): QObject(parent) {}
|
||||||
|
~GlobalShortcut() override;
|
||||||
|
Q_DISABLE_COPY_MOVE(GlobalShortcut);
|
||||||
|
|
||||||
|
void onPostReload() override;
|
||||||
|
|
||||||
|
[[nodiscard]] bool isPressed() const;
|
||||||
|
|
||||||
|
[[nodiscard]] QString appid() const;
|
||||||
|
void setAppid(QString appid);
|
||||||
|
|
||||||
|
[[nodiscard]] QString name() const;
|
||||||
|
void setName(QString name);
|
||||||
|
|
||||||
|
[[nodiscard]] QString description() const;
|
||||||
|
void setDescription(QString description);
|
||||||
|
|
||||||
|
[[nodiscard]] QString triggerDescription() const;
|
||||||
|
void setTriggerDescription(QString triggerDescription);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
/// Fired when the keybind is pressed.
|
||||||
|
void pressed();
|
||||||
|
/// Fired when the keybind is released.
|
||||||
|
void released();
|
||||||
|
|
||||||
|
void pressedChanged();
|
||||||
|
void appidChanged();
|
||||||
|
void nameChanged();
|
||||||
|
void descriptionChanged();
|
||||||
|
void triggerDescriptionChanged();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onPressed();
|
||||||
|
void onReleased();
|
||||||
|
|
||||||
|
private:
|
||||||
|
impl::GlobalShortcut* shortcut = nullptr;
|
||||||
|
|
||||||
|
bool mPressed = false;
|
||||||
|
QString mAppid = "quickshell";
|
||||||
|
QString mName;
|
||||||
|
QString mDescription;
|
||||||
|
QString mTriggerDescription;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace qs::hyprland::global_shortcuts
|
33
src/wayland/hyprland/global_shortcuts/shortcut.cpp
Normal file
33
src/wayland/hyprland/global_shortcuts/shortcut.cpp
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#include "shortcut.hpp"
|
||||||
|
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
#include <qtypes.h>
|
||||||
|
#include <wayland-hyprland-global-shortcuts-v1-client-protocol.h>
|
||||||
|
|
||||||
|
namespace qs::hyprland::global_shortcuts::impl {
|
||||||
|
|
||||||
|
GlobalShortcut::GlobalShortcut(::hyprland_global_shortcut_v1* shortcut) { this->init(shortcut); }
|
||||||
|
|
||||||
|
GlobalShortcut::~GlobalShortcut() {
|
||||||
|
if (this->isInitialized()) {
|
||||||
|
this->destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalShortcut::hyprland_global_shortcut_v1_pressed(
|
||||||
|
quint32 /*unused*/,
|
||||||
|
quint32 /*unused*/,
|
||||||
|
quint32 /*unused*/
|
||||||
|
) {
|
||||||
|
emit this->pressed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GlobalShortcut::hyprland_global_shortcut_v1_released(
|
||||||
|
quint32 /*unused*/,
|
||||||
|
quint32 /*unused*/,
|
||||||
|
quint32 /*unused*/
|
||||||
|
) {
|
||||||
|
emit this->released();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace qs::hyprland::global_shortcuts::impl
|
32
src/wayland/hyprland/global_shortcuts/shortcut.hpp
Normal file
32
src/wayland/hyprland/global_shortcuts/shortcut.hpp
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qtclasshelpermacros.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
#include <qtypes.h>
|
||||||
|
#include <qwayland-hyprland-global-shortcuts-v1.h>
|
||||||
|
|
||||||
|
namespace qs::hyprland::global_shortcuts::impl {
|
||||||
|
|
||||||
|
class GlobalShortcut
|
||||||
|
: public QObject
|
||||||
|
, public QtWayland::hyprland_global_shortcut_v1 {
|
||||||
|
Q_OBJECT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit GlobalShortcut(::hyprland_global_shortcut_v1* shortcut);
|
||||||
|
~GlobalShortcut() override;
|
||||||
|
Q_DISABLE_COPY_MOVE(GlobalShortcut);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void pressed();
|
||||||
|
void released();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// clang-format off
|
||||||
|
void hyprland_global_shortcut_v1_pressed(quint32 tvSecHi, quint32 tvSecLo, quint32 tvNsec) override;
|
||||||
|
void hyprland_global_shortcut_v1_released(quint32 tvSecHi, quint32 tvSecLo, quint32 tvNsec) override;
|
||||||
|
// clang-format on
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace qs::hyprland::global_shortcuts::impl
|
|
@ -1,6 +1,7 @@
|
||||||
name = "Quickshell.Hyprland"
|
name = "Quickshell.Hyprland"
|
||||||
description = "Hyprland specific Quickshell types"
|
description = "Hyprland specific Quickshell types"
|
||||||
headers = [
|
headers = [
|
||||||
"focus_grab/qml.hpp"
|
"focus_grab/qml.hpp",
|
||||||
|
"global_shortcuts/qml.hpp",
|
||||||
]
|
]
|
||||||
-----
|
-----
|
||||||
|
|
Loading…
Reference in a new issue