forked from quickshell/quickshell
		
	hyprland/global_shortcuts: add GlobalShortcut
This commit is contained in:
		
							parent
							
								
									87a884ca36
								
							
						
					
					
						commit
						bba8cb8a7d
					
				
					 14 changed files with 569 additions and 6 deletions
				
			
		| 
						 | 
				
			
			@ -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_SESSION_LOCK "Support the ext_session_lock_v1 wayland protocol" 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)
 | 
			
		||||
 | 
			
		||||
message(STATUS "Quickshell configuration")
 | 
			
		||||
| 
						 | 
				
			
			@ -29,6 +31,10 @@ endif ()
 | 
			
		|||
message(STATUS "  Services")
 | 
			
		||||
message(STATUS "    StatusNotifier: ${SERVICE_STATUS_NOTIFIER}")
 | 
			
		||||
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)
 | 
			
		||||
	execute_process(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1 +1,3 @@
 | 
			
		|||
add_subdirectory(status_notifier)
 | 
			
		||||
if (SERVICE_STATUS_NOTIFIER)
 | 
			
		||||
	add_subdirectory(status_notifier)
 | 
			
		||||
endif()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,13 @@
 | 
			
		|||
qt_add_library(quickshell-hyprland STATIC)
 | 
			
		||||
qt_add_qml_module(quickshell-hyprland URI Quickshell.Hyprland VERSION 0.1)
 | 
			
		||||
 | 
			
		||||
add_subdirectory(focus_grab)
 | 
			
		||||
if (HYPRLAND_FOCUS_GRAB)
 | 
			
		||||
	add_subdirectory(focus_grab)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if (HYPRLAND_GLOBAL_SHORTCUTS)
 | 
			
		||||
	add_subdirectory(global_shortcuts)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
target_link_libraries(quickshell-hyprland PRIVATE ${QT_DEPS})
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,11 +4,18 @@ qt_add_library(quickshell-hyprland-focus-grab STATIC
 | 
			
		|||
	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)
 | 
			
		||||
 | 
			
		||||
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-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-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"
 | 
			
		||||
description = "Hyprland specific Quickshell types"
 | 
			
		||||
headers = [
 | 
			
		||||
	"focus_grab/qml.hpp"
 | 
			
		||||
	"focus_grab/qml.hpp",
 | 
			
		||||
	"global_shortcuts/qml.hpp",
 | 
			
		||||
]
 | 
			
		||||
-----
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue