commit 2ffce462453d33ee6205c04f8fae017a3bce4e4c Author: jessequ Date: Wed Aug 27 10:53:32 2025 +0800 first commit diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..e7db470 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,20 @@ +kind: pipeline +type: ssh +name: default + +server: + host: 192.168.46.100:2223 + user: jessequ + password: + from_secret: password + +workspace: + path: /home/tmp + +steps: +- name: build + commands: + - mkdir -p build/debug + - cd build/debug + - cmake -S ../.. -DCMAKE_BUILD_TYPE:STRING=Debug -DCMAKE_PREFIX_PATH:PATH=/home/jessequ/Qt/6.7.2/gcc_64 + - make diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d8b4fd9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,105 @@ +build/ +.vscode/ + +# ---> CMake +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +# ---> C++ +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# ---> C +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..d3e2979 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.0) +project(qxtglobalshortcut VERSION 0.0.1) + +# Library name +set(bin qxtglobalshortcut) + +# Public headers +set(${bin}_public_headers + src/qxtglobalshortcut.h + ) + +# Source files common for all platforms +set(${bin}_sources + src/qxtglobalshortcut.cpp + ${${bin}_public_headers} + ) + +# Files with Q_OBJECT macros to pass to moc utility +set(${bin}_moc + src/qxtglobalshortcut.h + ) + +# Required pkg-config packages +set(${bin}_pkg_config_requires) + +include(cmake/cxx11.cmake) +include(cmake/library.cmake) +include(cmake/platform.cmake) +include(cmake/qt.cmake) +include(cmake/pkg-config.cmake) diff --git a/README.md b/README.md new file mode 100644 index 0000000..782d88d --- /dev/null +++ b/README.md @@ -0,0 +1,76 @@ +Cross-platform library for handling system-wide shortcuts in Qt applications. + +[![Build Status](http://192.168.46.100:4080/api/badges/jessequ/qxtglobalshortcut/status.svg)](http://192.168.46.100:4080/jessequ/qxtglobalshortcut) + +[API Reference](https://codedocs.xyz/hluk/qxtglobalshortcut/classQxtGlobalShortcut.html) + +This code was originally part of [Qtx](https://bitbucket.org/libqxt/libqxt/wiki/Home) which is no longer maintained. + +# Supported Platforms + +- Windows +- OS X +- Linux/X11 + +# Requirements + +- Compiler with C++11 support +- Qt 6 or Qt 5 +- CMake 3.0.0 and later + +# Build + +To build the library without installation run: + +```bash +mkdir build +cd build +cmake -S .. -DCMAKE_PREFIX_PATH:PATH=~/Qt/6.9.0/gcc_64 +make +``` + +To build and install run: + +```bash +mkdir build +cd build +cmake -S .. -DCMAKE_PREFIX_PATH:PATH=~/Qt/6.9.0/gcc_64 -DCMAKE_INSTALL_PREFIX=/usr/local +make +sudo make install +``` + +To build static library run: + +```bash +mkdir build +cd build +cmake -S .. -DCMAKE_PREFIX_PATH:PATH=~/Qt/6.9.0/gcc_64 -DQXTGLOBALSHORTCUT_STATIC=ON +make + +``` + +# Use the Library + +Directory "example" contains simple CMake project which uses the installed library. + +To compile and run the example: + +```bash +mkdir build +cd build +mkdir example +cd example +cmake -S ../../example -DCMAKE_PREFIX_PATH:PATH=~/Qt/6.9.0/gcc_64 +make +./qxtglobalshortcut_test +``` + +If `cmake` prints error that it cannot find package for the library you have to specify +it's install path. E.g. + +```bash +qxtglobalshortcut_DIR=/usr/local/lib/cmake/qxtglobalshortcut cmake . +``` + +Alternatively you can add the path to `CMAKE_PREFIX_PATH` environment variable. + diff --git a/cmake/cxx11.cmake b/cmake/cxx11.cmake new file mode 100644 index 0000000..d0f8511 --- /dev/null +++ b/cmake/cxx11.cmake @@ -0,0 +1,5 @@ +if (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 3.1) + set(CMAKE_CXX_STANDARD 11) +else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") +endif() diff --git a/cmake/library.cmake b/cmake/library.cmake new file mode 100644 index 0000000..663aa85 --- /dev/null +++ b/cmake/library.cmake @@ -0,0 +1,86 @@ +set(INSTALL_LIB_DIR "lib${LIB_SUFFIX}" CACHE PATH + "Installation directory for libraries") +set(INSTALL_BIN_DIR "bin" CACHE PATH + "Installation directory for executables") +set(INSTALL_INCLUDE_DIR "include/${bin}" CACHE PATH + "Installation directory for header files") +set(INSTALL_CMAKE_DIR "lib${LIB_SUFFIX}/cmake/${bin}" CACHE PATH + "Installation directory for CMake files") + +# Shared or static library +option(CREATE_STATIC_LIBRARY "Create static library" OFF) +if (CREATE_STATIC_LIBRARY) + add_definitions( -DQXT_STATIC ) + set(libtype STATIC) +else() + set(libtype SHARED) +endif() + +add_library(${bin} ${libtype} ${${bin}_sources}) +set_target_properties(${bin} PROPERTIES + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR} + ) + +# Headers +set(exports_dir "${CMAKE_CURRENT_BINARY_DIR}/exports") +set(export_header "${exports_dir}/private/${bin}_export.h") +set_target_properties(${bin} PROPERTIES + PUBLIC_HEADER "${${bin}_public_headers}" + PRIVATE_HEADER "${export_header}") + +target_include_directories(${bin} INTERFACE + $ + $ + ) + +install( + TARGETS ${bin} EXPORT ${bin}Targets + RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin + LIBRARY DESTINATION "${INSTALL_LIB_DIR}" COMPONENT lib + ARCHIVE DESTINATION "${INSTALL_LIB_DIR}" COMPONENT lib + PUBLIC_HEADER DESTINATION "${INSTALL_INCLUDE_DIR}" COMPONENT dev + PRIVATE_HEADER DESTINATION "${INSTALL_INCLUDE_DIR}/private" COMPONENT dev + ) + +# Generate and install CMake files for the library so `find_package()` can be used with CMake. +# For more info: https://cmake.org/cmake/help/v3.0/manual/cmake-packages.7.html#creating-packages +include(GenerateExportHeader) +generate_export_header(${bin} EXPORT_FILE_NAME "${export_header}") +target_include_directories(${bin} PRIVATE "${exports_dir}") + +include(CMakePackageConfigHelpers) +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/${bin}ConfigVersion.cmake" + VERSION ${PROJECT_VERSION} + COMPATIBILITY AnyNewerVersion + ) + +export( + EXPORT ${bin}Targets + FILE "${CMAKE_CURRENT_BINARY_DIR}/${bin}Targets.cmake" + ) +configure_file( + cmake/${bin}Config.cmake + "${CMAKE_CURRENT_BINARY_DIR}/${bin}Config.cmake" + COPYONLY + ) + +install( + EXPORT ${bin}Targets + FILE + ${bin}Targets.cmake + DESTINATION + "${INSTALL_CMAKE_DIR}" + COMPONENT + dev + ) +install( + FILES + "${CMAKE_CURRENT_BINARY_DIR}/${bin}Config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/${bin}ConfigVersion.cmake" + DESTINATION + "${INSTALL_CMAKE_DIR}" + COMPONENT + dev + ) diff --git a/cmake/pkg-config.cmake b/cmake/pkg-config.cmake new file mode 100644 index 0000000..6342fc1 --- /dev/null +++ b/cmake/pkg-config.cmake @@ -0,0 +1,22 @@ +set(PKG_CONFIG_REQUIRES "${${bin}_pkg_config_requires}") +set(PKG_CONFIG_LIBDIR "\${prefix}/lib${LIB_SUFFIX}") +set(PKG_CONFIG_INCLUDEDIR "\${prefix}/include/${bin}") +set(PKG_CONFIG_LIBS "-L\${libdir} -l${bin}") +set(PKG_CONFIG_CFLAGS "-I\${includedir}") + +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/pkg-config.pc.in" + "${CMAKE_CURRENT_BINARY_DIR}/${bin}.pc" + ) + +set(INSTALL_PKG_CONFIG_DIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}/pkgconfig" + CACHE PATH "Installation directory for pkg-config files") + +install( + FILES + "${CMAKE_CURRENT_BINARY_DIR}/${bin}.pc" + DESTINATION + "${INSTALL_PKG_CONFIG_DIR}" + COMPONENT + dev + ) diff --git a/cmake/platform.cmake b/cmake/platform.cmake new file mode 100644 index 0000000..19f5342 --- /dev/null +++ b/cmake/platform.cmake @@ -0,0 +1,10 @@ +find_package(X11) +if (X11_FOUND) + include(${CMAKE_CURRENT_LIST_DIR}/platform_x11.cmake) +elseif (Q_WS_WIN OR WIN32) + include(${CMAKE_CURRENT_LIST_DIR}/platform_win.cmake) +elseif (Q_OS_MAC OR APPLE) + include(${CMAKE_CURRENT_LIST_DIR}/platform_mac.cmake) +else() + message(FATAL_ERROR "Unsupported platform! Supported are Windows, OS X, Linux/X11.") +endif() diff --git a/cmake/platform_mac.cmake b/cmake/platform_mac.cmake new file mode 100644 index 0000000..0e7ff53 --- /dev/null +++ b/cmake/platform_mac.cmake @@ -0,0 +1,33 @@ +target_sources(${bin} PRIVATE src/qxtglobalshortcut_mac.cpp) + +# On Macs we must ensure Carbon and Cocoa are linked +# +# We do this using a macro taken from: +# http://stackoverflow.com/questions/12547624/cant-link-macos-frameworks-with-cmake?rq=1 + +#macro(ADD_FRAMEWORK fwname appname) +# find_library(FRAMEWORK_${fwname} +# NAMES ${fwname} +# PATHS ${CMAKE_OSX_SYSROOT}/System/Library +# PATH_SUFFIXES Frameworks +# NO_DEFAULT_PATH) +# if( ${FRAMEWORK_${fwname}} STREQUAL FRAMEWORK_${fwname}-NOTFOUND) +# MESSAGE(ERROR ": Framework ${fwname} not found") +# else() +# TARGET_LINK_LIBRARIES(${appname} "${FRAMEWORK_${fwname}}/${fwname}") +# MESSAGE(STATUS "Framework ${fwname} found at ${FRAMEWORK_${fwname}}") +# endif() +#endmacro(ADD_FRAMEWORK) + +#ADD_FRAMEWORK(Carbon ${bin}) +#ADD_FRAMEWORK(Cocoa ${bin}) + +set(MAC_LIBS) + +FIND_LIBRARY( CARBON_LIBRARY Carbon ) +LIST( APPEND MAC_LIBS ${CARBON_LIBRARY} ) + +FIND_LIBRARY( COCOA_LIBRARY Cocoa ) +LIST( APPEND MAC_LIBS ${COCOA_LIBRARY} ) + +TARGET_LINK_LIBRARIES(${bin} ${MAC_LIBS}) diff --git a/cmake/platform_win.cmake b/cmake/platform_win.cmake new file mode 100644 index 0000000..b997c21 --- /dev/null +++ b/cmake/platform_win.cmake @@ -0,0 +1 @@ +target_sources(${bin} PRIVATE src/qxtglobalshortcut_win.cpp) diff --git a/cmake/platform_x11.cmake b/cmake/platform_x11.cmake new file mode 100644 index 0000000..18bff0c --- /dev/null +++ b/cmake/platform_x11.cmake @@ -0,0 +1,3 @@ +target_sources(${bin} PRIVATE src/qxtglobalshortcut_x11.cpp) +target_include_directories(${bin} SYSTEM PRIVATE ${X11_INCLUDE_DIR}) +target_link_libraries(${bin} ${X11_LIBRARIES}) diff --git a/cmake/qt.cmake b/cmake/qt.cmake new file mode 100644 index 0000000..d8d3f7d --- /dev/null +++ b/cmake/qt.cmake @@ -0,0 +1,7 @@ +option(WITH_QT6 "Use Qt 6 (disable to use Qt 5 instead)" ON) + +if (WITH_QT6) + include(${CMAKE_CURRENT_LIST_DIR}/qt6.cmake) +else() + include(${CMAKE_CURRENT_LIST_DIR}/qt5.cmake) +endif() diff --git a/cmake/qt5.cmake b/cmake/qt5.cmake new file mode 100644 index 0000000..372db9e --- /dev/null +++ b/cmake/qt5.cmake @@ -0,0 +1,13 @@ +find_package(Qt5Widgets) +if (NOT Qt5Widgets_FOUND) + message(FATAL_ERROR "Qt 5 is unavailable. To compile with Qt 4 use -DWITH_QT5=OFF.") +endif() +message(STATUS "Building with Qt 5.") + +include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS}) +target_link_libraries(${bin} Qt5::Widgets) + +qt5_wrap_cpp(${bin}_mocced ${${bin}_moc}) +target_sources(${bin} PRIVATE ${${bin}_mocced}) + +set(${bin}_pkg_config_requires ${${bin}_pkg_config_requires} Qt5Widgets) diff --git a/cmake/qt6.cmake b/cmake/qt6.cmake new file mode 100644 index 0000000..05c5c5a --- /dev/null +++ b/cmake/qt6.cmake @@ -0,0 +1,13 @@ +find_package(Qt6 REQUIRED COMPONENTS Widgets) +if (NOT Qt6_FOUND) + message(FATAL_ERROR "Qt 6 is unavailable. To compile with Qt 5 use -DWITH_QT6=OFF.") +endif() +message(STATUS "Building with Qt 6.") + +include_directories(${Qt6Gui_PRIVATE_INCLUDE_DIRS}) +target_link_libraries(${bin} Qt6::Widgets) + +qt6_wrap_cpp(${bin}_mocced ${${bin}_moc}) +target_sources(${bin} PRIVATE ${${bin}_mocced}) + +set(${bin}_pkg_config_requires ${${bin}_pkg_config_requires} Qt6) diff --git a/cmake/qxtglobalshortcutConfig.cmake b/cmake/qxtglobalshortcutConfig.cmake new file mode 100644 index 0000000..335d206 --- /dev/null +++ b/cmake/qxtglobalshortcutConfig.cmake @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/qxtglobalshortcutTargets.cmake") diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt new file mode 100644 index 0000000..710e05a --- /dev/null +++ b/example/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 2.8.8) +project(qxtglobalshortcut_test) + +find_package(qxtglobalshortcut REQUIRED) +find_package(Qt6 REQUIRED COMPONENTS Widgets) + +set(bin qxtglobalshortcut_test) +add_executable(${bin} src/main.cpp) + +target_link_libraries(${bin} qxtglobalshortcut) +target_link_libraries(${bin} Qt6::Widgets) diff --git a/example/src/main.cpp b/example/src/main.cpp new file mode 100644 index 0000000..1f310f3 --- /dev/null +++ b/example/src/main.cpp @@ -0,0 +1,33 @@ +#include + +#include + +#include + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + QTextStream out(stdout); + QTextStream err(stderr); + + const QKeySequence shortcut("Ctrl+Shift+T"); + const QxtGlobalShortcut globalShortcut(shortcut); + + if ( !globalShortcut.isValid() ) { + err << QString("Error: Failed to set shortcut %1") + .arg(shortcut.toString()) << Qt::endl; + return 1; + } + + out << QString("Press shortcut %1 (or CTRL+C to exit)").arg(shortcut.toString()) << Qt::endl; + + QObject::connect( + &globalShortcut, &QxtGlobalShortcut::activated, &globalShortcut, + [&]{ + out << QLatin1String("Shortcut pressed!") << Qt::endl; + QApplication::quit(); + }); + + return app.exec(); +} diff --git a/pkg-config.pc.in b/pkg-config.pc.in new file mode 100644 index 0000000..8e79b9c --- /dev/null +++ b/pkg-config.pc.in @@ -0,0 +1,11 @@ +Name: ${PROJECT_NAME} +Description: ${PROJECT_DESCRIPTION} +Version: ${PROJECT_VERSION} +Requires: ${PKG_CONFIG_REQUIRES} + +prefix=${CMAKE_INSTALL_PREFIX} +includedir=${PKG_CONFIG_INCLUDEDIR} +libdir=${PKG_CONFIG_LIBDIR} + +Libs: ${PKG_CONFIG_LIBS} +Cflags: ${PKG_CONFIG_CFLAGS} diff --git a/src/qxtglobalshortcut.cpp b/src/qxtglobalshortcut.cpp new file mode 100644 index 0000000..92040a4 --- /dev/null +++ b/src/qxtglobalshortcut.cpp @@ -0,0 +1,254 @@ +#include "qxtglobalshortcut.h" +/**************************************************************************** +** Copyright (c) 2006 - 2011, the LibQxt project. +** See the Qxt AUTHORS file for a list of authors and copyright holders. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * 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. +** * Neither the name of the LibQxt project 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 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. +** +** +*****************************************************************************/ + +#include "qxtglobalshortcut_p.h" + +#include + +#ifndef Q_OS_MAC +int QxtGlobalShortcutPrivate::ref = 0; +# if QT_VERSION < QT_VERSION_CHECK(5,0,0) +QAbstractEventDispatcher::EventFilter QxtGlobalShortcutPrivate::prevEventFilter = 0; +# endif +#endif // Q_OS_MAC +QHash, QxtGlobalShortcut*> QxtGlobalShortcutPrivate::shortcuts; + +QxtGlobalShortcutPrivate::QxtGlobalShortcutPrivate(QxtGlobalShortcut *q) + : enabled(true) + , key(Qt::Key(0)) + , mods(Qt::NoModifier) + , nativeKey(0) + , nativeMods(0) + , registered(false) + , q_ptr(q) +{ +#ifndef Q_OS_MAC + if (ref == 0) { +# if QT_VERSION < QT_VERSION_CHECK(5,0,0) + prevEventFilter = QAbstractEventDispatcher::instance()->setEventFilter(eventFilter); +# else + QAbstractEventDispatcher::instance()->installNativeEventFilter(this); +#endif + } + ++ref; +#endif // Q_OS_MAC +} + +QxtGlobalShortcutPrivate::~QxtGlobalShortcutPrivate() +{ + unsetShortcut(); + +#ifndef Q_OS_MAC + --ref; + if (ref == 0) { + QAbstractEventDispatcher *ed = QAbstractEventDispatcher::instance(); + if (ed != nullptr) { +# if QT_VERSION < QT_VERSION_CHECK(5,0,0) + ed->setEventFilter(prevEventFilter); +# else + ed->removeNativeEventFilter(this); +# endif + } + } +#endif // Q_OS_MAC +} + +bool QxtGlobalShortcutPrivate::setShortcut(const QKeySequence& shortcut) +{ + unsetShortcut(); + + const Qt::KeyboardModifiers allMods = + Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier; + const auto xkeyCode = static_cast( shortcut.isEmpty() ? 0 : shortcut[0] ); + // WORKAROUND: Qt has convert some keys to upper case which + // breaks some shortcuts on some keyboard layouts. + const uint keyCode = QChar::toLower(xkeyCode & ~allMods); + + key = Qt::Key(keyCode); + mods = Qt::KeyboardModifiers(xkeyCode & allMods); + + nativeKey = nativeKeycode(key); + nativeMods = nativeModifiers(mods); + + registered = registerShortcut(nativeKey, nativeMods); + if (registered) + shortcuts.insert(qMakePair(nativeKey, nativeMods), q_ptr); + + return registered; +} + +bool QxtGlobalShortcutPrivate::unsetShortcut() +{ + if (registered + && shortcuts.value(qMakePair(nativeKey, nativeMods)) == q_ptr + && unregisterShortcut(nativeKey, nativeMods)) + { + shortcuts.remove(qMakePair(nativeKey, nativeMods)); + registered = false; + return true; + } + + return false; +} + +void QxtGlobalShortcutPrivate::activateShortcut(quint32 nativeKey, quint32 nativeMods) +{ + QxtGlobalShortcut* shortcut = shortcuts.value(qMakePair(nativeKey, nativeMods)); + if (shortcut && shortcut->isEnabled()) + emit shortcut->activated(shortcut); +} + +/*! + \class QxtGlobalShortcut + \brief The QxtGlobalShortcut class provides a global shortcut aka "hotkey". + + A global shortcut triggers even if the application is not active. This + makes it easy to implement applications that react to certain shortcuts + still if some other application is active or if the application is for + example minimized to the system tray. + + Example usage: + \code + QxtGlobalShortcut* shortcut = new QxtGlobalShortcut(window); + connect(shortcut, SIGNAL(activated()), window, SLOT(toggleVisibility())); + shortcut->setShortcut(QKeySequence("Ctrl+Shift+F12")); + \endcode + */ + +/*! + \fn QxtGlobalShortcut::activated() + + This signal is emitted when the user types the shortcut's key sequence. + + \sa shortcut + */ + +/*! + Constructs a new QxtGlobalShortcut with \a parent. + */ +QxtGlobalShortcut::QxtGlobalShortcut(QObject* parent) + : QObject(parent) + , d_ptr(new QxtGlobalShortcutPrivate(this)) +{ +} + +/*! + Constructs a new QxtGlobalShortcut with \a shortcut and \a parent. + */ +QxtGlobalShortcut::QxtGlobalShortcut(const QKeySequence& shortcut, QObject* parent) + : QxtGlobalShortcut(parent) +{ + setShortcut(shortcut); +} + +/*! + Destructs the QxtGlobalShortcut. + */ +QxtGlobalShortcut::~QxtGlobalShortcut() +{ + delete d_ptr; +} + +/*! + \property QxtGlobalShortcut::shortcut + \brief the shortcut key sequence + + Notice that corresponding key press and release events are not + delivered for registered global shortcuts even if they are disabled. + Also, comma separated key sequences are not supported. + Only the first part is used: + + \code + qxtShortcut->setShortcut(QKeySequence("Ctrl+Alt+A,Ctrl+Alt+B")); + Q_ASSERT(qxtShortcut->shortcut() == QKeySequence("Ctrl+Alt+A")); + \endcode + + \sa setShortcut() + */ +QKeySequence QxtGlobalShortcut::shortcut() const +{ + return QKeySequence( static_cast(d_ptr->key | d_ptr->mods) ); +} + +/*! + \property QxtGlobalShortcut::shortcut + \brief sets the shortcut key sequence + + \sa shortcut() + */ +bool QxtGlobalShortcut::setShortcut(const QKeySequence& shortcut) +{ + return d_ptr->setShortcut(shortcut); +} + +/*! + \property QxtGlobalShortcut::enabled + \brief whether the shortcut is enabled + + A disabled shortcut does not get activated. + + The default value is \c true. + + \sa setEnabled(), setDisabled() + */ +bool QxtGlobalShortcut::isEnabled() const +{ + return d_ptr->enabled; +} + +/*! + \property QxtGlobalShortcut::valid + \brief whether the shortcut was successfully set up + */ +bool QxtGlobalShortcut::isValid() const +{ + return d_ptr->registered; +} + +/*! + Sets the shortcut \a enabled. + + \sa enabled + */ +void QxtGlobalShortcut::setEnabled(bool enabled) +{ + d_ptr->enabled = enabled; +} + +/*! + Sets the shortcut \a disabled. + + \sa enabled + */ +void QxtGlobalShortcut::setDisabled(bool disabled) +{ + d_ptr->enabled = !disabled; +} diff --git a/src/qxtglobalshortcut.h b/src/qxtglobalshortcut.h new file mode 100644 index 0000000..13f0785 --- /dev/null +++ b/src/qxtglobalshortcut.h @@ -0,0 +1,72 @@ +#ifndef QXTGLOBALSHORTCUT_H +/**************************************************************************** +** Copyright (c) 2006 - 2011, the LibQxt project. +** See the Qxt AUTHORS file for a list of authors and copyright holders. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * 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. +** * Neither the name of the LibQxt project 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 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. +** +** +*****************************************************************************/ + +#define QXTGLOBALSHORTCUT_H + +#include "private/qxtglobalshortcut_export.h" + +#include +#include + +class QxtGlobalShortcutPrivate; + +class QXTGLOBALSHORTCUT_EXPORT QxtGlobalShortcut : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled) + Q_PROPERTY(bool valid READ isValid) + Q_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut) + +public: + explicit QxtGlobalShortcut(QObject* parent = nullptr); + explicit QxtGlobalShortcut(const QKeySequence& shortcut, QObject* parent = nullptr); + ~QxtGlobalShortcut(); + + QKeySequence shortcut() const; + bool setShortcut(const QKeySequence& shortcut); + + bool isEnabled() const; + + bool isValid() const; + +public Q_SLOTS: + void setEnabled(bool enabled = true); + void setDisabled(bool disabled = true); + +Q_SIGNALS: + void activated(QxtGlobalShortcut *self); + +private: + friend class QxtGlobalShortcutPrivate; + QxtGlobalShortcutPrivate *d_ptr; +}; + +#endif // QXTGLOBALSHORTCUT_H diff --git a/src/qxtglobalshortcut_mac.cpp b/src/qxtglobalshortcut_mac.cpp new file mode 100644 index 0000000..945ade5 --- /dev/null +++ b/src/qxtglobalshortcut_mac.cpp @@ -0,0 +1,387 @@ +#include +/**************************************************************************** +** Copyright (c) 2006 - 2011, the LibQxt project. +** See the Qxt AUTHORS file for a list of authors and copyright holders. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * 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. +** * Neither the name of the LibQxt project 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 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. +** +** +*****************************************************************************/ + +#include "qxtglobalshortcut_p.h" +#include +#include +#include +#include + +using Identifier = QPair; +static QMap keyRefs; +static QHash keyIDs; +static quint32 hotKeySerial = 0; +static bool qxt_mac_handler_installed = false; + +OSStatus qxt_mac_handle_hot_key(EventHandlerCallRef nextHandler, EventRef event, void* data) +{ + Q_UNUSED(nextHandler); + Q_UNUSED(data); + if (GetEventClass(event) == kEventClassKeyboard && GetEventKind(event) == kEventHotKeyPressed) + { + EventHotKeyID keyID; + GetEventParameter(event, kEventParamDirectObject, typeEventHotKeyID, nullptr, sizeof(keyID), nullptr, &keyID); + Identifier id = keyIDs.key(keyID.id); + QxtGlobalShortcutPrivate::activateShortcut(id.second, id.first); + } + return noErr; +} + +quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifiers) +{ + quint32 native = 0; + if (modifiers & Qt::ShiftModifier) + native |= shiftKey; + if (modifiers & Qt::ControlModifier) + native |= cmdKey; + if (modifiers & Qt::AltModifier) + native |= optionKey; + if (modifiers & Qt::MetaModifier) + native |= controlKey; + if (modifiers & Qt::KeypadModifier) + native |= kEventKeyModifierNumLockMask; + return native; +} + +#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) +// This is only here to get it to compile on OSX +bool QxtGlobalShortcutPrivate::nativeEventFilter(const QByteArray & eventType, + void * message, qintptr * result) +{ + Q_UNUSED(result); + Q_UNUSED(message); + Q_UNUSED(eventType); + + return false; +} +#endif + + +quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key key) +{ + UTF16Char ch; + // Constants found in NSEvent.h from AppKit.framework + switch (key) + { + case Qt::Key_Return: + return kVK_Return; + case Qt::Key_Enter: + return kVK_ANSI_KeypadEnter; + case Qt::Key_Tab: + return kVK_Tab; + case Qt::Key_Space: + return kVK_Space; + case Qt::Key_Backspace: + return kVK_Delete; + case Qt::Key_Control: + return kVK_Command; + case Qt::Key_Shift: + return kVK_Shift; + case Qt::Key_CapsLock: + return kVK_CapsLock; + case Qt::Key_Option: + return kVK_Option; + case Qt::Key_Meta: + return kVK_Control; + case Qt::Key_F17: + return kVK_F17; + case Qt::Key_VolumeUp: + return kVK_VolumeUp; + case Qt::Key_VolumeDown: + return kVK_VolumeDown; + case Qt::Key_F18: + return kVK_F18; + case Qt::Key_F19: + return kVK_F19; + case Qt::Key_F20: + return kVK_F20; + case Qt::Key_F5: + return kVK_F5; + case Qt::Key_F6: + return kVK_F6; + case Qt::Key_F7: + return kVK_F7; + case Qt::Key_F3: + return kVK_F3; + case Qt::Key_F8: + return kVK_F8; + case Qt::Key_F9: + return kVK_F9; + case Qt::Key_F11: + return kVK_F11; + case Qt::Key_F13: + return kVK_F13; + case Qt::Key_F16: + return kVK_F16; + case Qt::Key_F14: + return kVK_F14; + case Qt::Key_F10: + return kVK_F10; + case Qt::Key_F12: + return kVK_F12; + case Qt::Key_F15: + return kVK_F15; + case Qt::Key_Help: + return kVK_Help; + case Qt::Key_Home: + return kVK_Home; + case Qt::Key_PageUp: + return kVK_PageUp; + case Qt::Key_Delete: + return kVK_ForwardDelete; + case Qt::Key_F4: + return kVK_F4; + case Qt::Key_End: + return kVK_End; + case Qt::Key_F2: + return kVK_F2; + case Qt::Key_PageDown: + return kVK_PageDown; + case Qt::Key_F1: + return kVK_F1; + case Qt::Key_Left: + return kVK_LeftArrow; + case Qt::Key_Right: + return kVK_RightArrow; + case Qt::Key_Down: + return kVK_DownArrow; + case Qt::Key_Up: + return kVK_UpArrow; + default: + ; + } + + if (key == Qt::Key_Escape) ch = 27; + else if (key == Qt::Key_Return) ch = 13; + else if (key == Qt::Key_Enter) ch = 3; + else if (key == Qt::Key_Tab) ch = 9; + else ch = key; + + CFDataRef currentLayoutData; + TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource(); + + if (currentKeyboard == nullptr) + return 0; + + currentLayoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData); + CFRelease(currentKeyboard); + if (currentLayoutData == nullptr){//Japanese or Chinese always return null + currentKeyboard = TISCopyCurrentKeyboardLayoutInputSource(); + currentLayoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData); + if(!currentLayoutData) + return 0; + } + + UCKeyboardLayout* header = (UCKeyboardLayout*)CFDataGetBytePtr(currentLayoutData); + UCKeyboardTypeHeader* table = header->keyboardTypeList; + + uint8_t *data = (uint8_t*)header; + // God, would a little documentation for this shit kill you... + for (quint32 i=0; i < header->keyboardTypeCount; i++) + { + UCKeyStateRecordsIndex* stateRec = 0; + if (table[i].keyStateRecordsIndexOffset != 0) + { + stateRec = reinterpret_cast(data + table[i].keyStateRecordsIndexOffset); + if (stateRec->keyStateRecordsIndexFormat != kUCKeyStateRecordsIndexFormat) stateRec = 0; + } + + UCKeyToCharTableIndex* charTable = reinterpret_cast(data + table[i].keyToCharTableIndexOffset); + if (charTable->keyToCharTableIndexFormat != kUCKeyToCharTableIndexFormat) continue; + + for (quint32 j=0; j < charTable->keyToCharTableCount; j++) + { + UCKeyOutput* keyToChar = reinterpret_cast(data + charTable->keyToCharTableOffsets[j]); + for (quint32 k=0; k < charTable->keyToCharTableSize; k++) + { + if (keyToChar[k] & kUCKeyOutputTestForIndexMask) + { + long idx = keyToChar[k] & kUCKeyOutputGetIndexMask; + if (stateRec && idx < stateRec->keyStateRecordCount) + { + UCKeyStateRecord* rec = reinterpret_cast(data + stateRec->keyStateRecordOffsets[idx]); + if (rec->stateZeroCharData == ch) return k; + } + } + else if (!(keyToChar[k] & kUCKeyOutputSequenceIndexMask) && keyToChar[k] < 0xFFFE) + { + if (keyToChar[k] == ch) return k; + } + } // for k + } // for j + } // for i + + // The code above fails to translate keys like semicolon with Qt 5.7.1. + // Last resort is to try mapping the rest of the keys directly. + switch (key) + { + case Qt::Key_A: + return kVK_ANSI_A; + case Qt::Key_S: + return kVK_ANSI_S; + case Qt::Key_D: + return kVK_ANSI_D; + case Qt::Key_F: + return kVK_ANSI_F; + case Qt::Key_H: + return kVK_ANSI_H; + case Qt::Key_G: + return kVK_ANSI_G; + case Qt::Key_Z: + return kVK_ANSI_Z; + case Qt::Key_X: + return kVK_ANSI_X; + case Qt::Key_C: + return kVK_ANSI_C; + case Qt::Key_V: + return kVK_ANSI_V; + case Qt::Key_B: + return kVK_ANSI_B; + case Qt::Key_Q: + return kVK_ANSI_Q; + case Qt::Key_W: + return kVK_ANSI_W; + case Qt::Key_E: + return kVK_ANSI_E; + case Qt::Key_R: + return kVK_ANSI_R; + case Qt::Key_Y: + return kVK_ANSI_Y; + case Qt::Key_T: + return kVK_ANSI_T; + case Qt::Key_1: + return kVK_ANSI_1; + case Qt::Key_2: + return kVK_ANSI_2; + case Qt::Key_3: + return kVK_ANSI_3; + case Qt::Key_4: + return kVK_ANSI_4; + case Qt::Key_6: + return kVK_ANSI_6; + case Qt::Key_5: + return kVK_ANSI_5; + case Qt::Key_Equal: + return kVK_ANSI_Equal; + case Qt::Key_9: + return kVK_ANSI_9; + case Qt::Key_7: + return kVK_ANSI_7; + case Qt::Key_Minus: + return kVK_ANSI_Minus; + case Qt::Key_8: + return kVK_ANSI_8; + case Qt::Key_0: + return kVK_ANSI_0; + case Qt::Key_BracketRight: + return kVK_ANSI_RightBracket; + case Qt::Key_O: + return kVK_ANSI_O; + case Qt::Key_U: + return kVK_ANSI_U; + case Qt::Key_BracketLeft: + return kVK_ANSI_LeftBracket; + case Qt::Key_I: + return kVK_ANSI_I; + case Qt::Key_P: + return kVK_ANSI_P; + case Qt::Key_L: + return kVK_ANSI_L; + case Qt::Key_J: + return kVK_ANSI_J; + case Qt::Key_QuoteDbl: + return kVK_ANSI_Quote; + case Qt::Key_K: + return kVK_ANSI_K; + case Qt::Key_Semicolon: + return kVK_ANSI_Semicolon; + case Qt::Key_Backslash: + return kVK_ANSI_Backslash; + case Qt::Key_Comma: + return kVK_ANSI_Comma; + case Qt::Key_Slash: + return kVK_ANSI_Slash; + case Qt::Key_N: + return kVK_ANSI_N; + case Qt::Key_M: + return kVK_ANSI_M; + case Qt::Key_Period: + return kVK_ANSI_Period; + case Qt::Key_Dead_Grave: + return kVK_ANSI_Grave; + case Qt::Key_Asterisk: + return kVK_ANSI_KeypadMultiply; + case Qt::Key_Plus: + return kVK_ANSI_KeypadPlus; + case Qt::Key_Clear: + return kVK_ANSI_KeypadClear; + case Qt::Key_Escape: + return kVK_Escape; + default: + ; + } + + return 0; +} + +bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, quint32 nativeMods) +{ + if (!qxt_mac_handler_installed) + { + EventTypeSpec t; + t.eventClass = kEventClassKeyboard; + t.eventKind = kEventHotKeyPressed; + InstallApplicationEventHandler(&qxt_mac_handle_hot_key, 1, &t, nullptr, nullptr); + } + + EventHotKeyID keyID; + keyID.signature = 'cute'; + keyID.id = ++hotKeySerial; + + EventHotKeyRef ref = 0; + bool rv = !RegisterEventHotKey(nativeKey, nativeMods, keyID, GetApplicationEventTarget(), 0, &ref); + if (rv) + { + keyIDs.insert(Identifier(nativeMods, nativeKey), keyID.id); + keyRefs.insert(keyID.id, ref); + } + return rv; +} + +bool QxtGlobalShortcutPrivate::unregisterShortcut(quint32 nativeKey, quint32 nativeMods) +{ + Identifier id(nativeMods, nativeKey); + if (!keyIDs.contains(id)) return false; + + EventHotKeyRef ref = keyRefs.take(keyIDs[id]); + keyIDs.remove(id); + return !UnregisterEventHotKey(ref); +} diff --git a/src/qxtglobalshortcut_p.h b/src/qxtglobalshortcut_p.h new file mode 100644 index 0000000..cb08320 --- /dev/null +++ b/src/qxtglobalshortcut_p.h @@ -0,0 +1,90 @@ +#ifndef QXTGLOBALSHORTCUT_P_H +/**************************************************************************** +** Copyright (c) 2006 - 2011, the LibQxt project. +** See the Qxt AUTHORS file for a list of authors and copyright holders. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * 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. +** * Neither the name of the LibQxt project 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 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. +** +** +*****************************************************************************/ + +#define QXTGLOBALSHORTCUT_P_H + +#include +#include + +class QKeySequence; +class QxtGlobalShortcut; + +#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) +#include +#endif + +class QxtGlobalShortcutPrivate +#if QT_VERSION >= QT_VERSION_CHECK(5,0,0) + : public QAbstractNativeEventFilter +#endif +{ +public: + explicit QxtGlobalShortcutPrivate(QxtGlobalShortcut *q); + ~QxtGlobalShortcutPrivate(); + + bool enabled; + Qt::Key key; + Qt::KeyboardModifiers mods; + quint32 nativeKey; + quint32 nativeMods; + bool registered; + + bool setShortcut(const QKeySequence& shortcut); + bool unsetShortcut(); + +# ifndef Q_OS_MAC + static int ref; +# endif // Q_OS_MAC + +# if QT_VERSION < QT_VERSION_CHECK(5,0,0) +# ifndef Q_OS_MAC + static QAbstractEventDispatcher::EventFilter prevEventFilter; + static bool eventFilter(void* message); +# endif // Q_OS_MAC +# else + bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override; +# endif // QT_VERSION < QT_VERSION_CHECK(5,0,0) + + static void activateShortcut(quint32 nativeKey, quint32 nativeMods); + +private: + QxtGlobalShortcut *q_ptr; + + static quint32 nativeKeycode(Qt::Key keycode); + static quint32 nativeModifiers(Qt::KeyboardModifiers modifiers); + + static bool registerShortcut(quint32 nativeKey, quint32 nativeMods); + static bool unregisterShortcut(quint32 nativeKey, quint32 nativeMods); + + static QHash, QxtGlobalShortcut*> shortcuts; +}; + +#endif // QXTGLOBALSHORTCUT_P_H diff --git a/src/qxtglobalshortcut_win.cpp b/src/qxtglobalshortcut_win.cpp new file mode 100644 index 0000000..b606fb5 --- /dev/null +++ b/src/qxtglobalshortcut_win.cpp @@ -0,0 +1,211 @@ +#include "qxtglobalshortcut_p.h" +/**************************************************************************** +** Copyright (c) 2006 - 2011, the LibQxt project. +** See the Qxt AUTHORS file for a list of authors and copyright holders. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * 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. +** * Neither the name of the LibQxt project 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 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. +** +** +*****************************************************************************/ + +#include + + +#if QT_VERSION < QT_VERSION_CHECK(5,0,0) +bool QxtGlobalShortcutPrivate::eventFilter(void* message) +{ +#else +bool QxtGlobalShortcutPrivate::nativeEventFilter(const QByteArray & eventType, + void * message, qintptr * result) +{ + Q_UNUSED(eventType); + Q_UNUSED(result); +#endif + MSG* msg = static_cast(message); + if (msg->message == WM_HOTKEY) + { + const quint32 keycode = HIWORD(msg->lParam); + const quint32 modifiers = LOWORD(msg->lParam); + activateShortcut(keycode, modifiers); + } + return false; +} + +quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifiers) +{ + // MOD_ALT, MOD_CONTROL, (MOD_KEYUP), MOD_SHIFT, MOD_WIN + quint32 native = 0; + if (modifiers & Qt::ShiftModifier) + native |= MOD_SHIFT; + if (modifiers & Qt::ControlModifier) + native |= MOD_CONTROL; + if (modifiers & Qt::AltModifier) + native |= MOD_ALT; + if (modifiers & Qt::MetaModifier) + native |= MOD_WIN; + // TODO: resolve these? + //if (modifiers & Qt::KeypadModifier) + //if (modifiers & Qt::GroupSwitchModifier) + return native; +} + +quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key key) +{ + // Here is list of keys that presumably work on most keyboard layouts. + // Default branch is for keys that can change with keyboard layout. + switch (key) + { + case Qt::Key_Escape: + return VK_ESCAPE; + case Qt::Key_Tab: + case Qt::Key_Backtab: + return VK_TAB; + case Qt::Key_Backspace: + return VK_BACK; + case Qt::Key_Return: + case Qt::Key_Enter: + return VK_RETURN; + case Qt::Key_Insert: + return VK_INSERT; + case Qt::Key_Delete: + return VK_DELETE; + case Qt::Key_Pause: + return VK_PAUSE; + case Qt::Key_Print: + return VK_PRINT; + case Qt::Key_Clear: + return VK_CLEAR; + case Qt::Key_Home: + return VK_HOME; + case Qt::Key_End: + return VK_END; + case Qt::Key_Left: + return VK_LEFT; + case Qt::Key_Up: + return VK_UP; + case Qt::Key_Right: + return VK_RIGHT; + case Qt::Key_Down: + return VK_DOWN; + case Qt::Key_PageUp: + return VK_PRIOR; + case Qt::Key_PageDown: + return VK_NEXT; + case Qt::Key_F1: + return VK_F1; + case Qt::Key_F2: + return VK_F2; + case Qt::Key_F3: + return VK_F3; + case Qt::Key_F4: + return VK_F4; + case Qt::Key_F5: + return VK_F5; + case Qt::Key_F6: + return VK_F6; + case Qt::Key_F7: + return VK_F7; + case Qt::Key_F8: + return VK_F8; + case Qt::Key_F9: + return VK_F9; + case Qt::Key_F10: + return VK_F10; + case Qt::Key_F11: + return VK_F11; + case Qt::Key_F12: + return VK_F12; + case Qt::Key_F13: + return VK_F13; + case Qt::Key_F14: + return VK_F14; + case Qt::Key_F15: + return VK_F15; + case Qt::Key_F16: + return VK_F16; + case Qt::Key_F17: + return VK_F17; + case Qt::Key_F18: + return VK_F18; + case Qt::Key_F19: + return VK_F19; + case Qt::Key_F20: + return VK_F20; + case Qt::Key_F21: + return VK_F21; + case Qt::Key_F22: + return VK_F22; + case Qt::Key_F23: + return VK_F23; + case Qt::Key_F24: + return VK_F24; + case Qt::Key_Space: + return VK_SPACE; + case Qt::Key_Asterisk: + return VK_MULTIPLY; + case Qt::Key_Plus: + return VK_ADD; + case Qt::Key_Minus: + return VK_SUBTRACT; + case Qt::Key_Slash: + return VK_DIVIDE; + case Qt::Key_MediaNext: + return VK_MEDIA_NEXT_TRACK; + case Qt::Key_MediaPrevious: + return VK_MEDIA_PREV_TRACK; + case Qt::Key_MediaPlay: + return VK_MEDIA_PLAY_PAUSE; + case Qt::Key_MediaStop: + return VK_MEDIA_STOP; + // couldn't find those in VK_* + //case Qt::Key_MediaLast: + //case Qt::Key_MediaRecord: + case Qt::Key_VolumeDown: + return VK_VOLUME_DOWN; + case Qt::Key_VolumeUp: + return VK_VOLUME_UP; + case Qt::Key_VolumeMute: + return VK_VOLUME_MUTE; + + default: + // Try to get virtual key from current keyboard layout or US. + const HKL layout = GetKeyboardLayout(0); + int vk = VkKeyScanEx(key, layout); + if (vk == -1) { + const HKL layoutUs = GetKeyboardLayout(0x409); + vk = VkKeyScanEx(key, layoutUs); + } + return vk == -1 ? 0 : vk; + } +} + +bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, quint32 nativeMods) +{ + return RegisterHotKey(0, nativeMods ^ nativeKey, nativeMods, nativeKey); +} + +bool QxtGlobalShortcutPrivate::unregisterShortcut(quint32 nativeKey, quint32 nativeMods) +{ + return UnregisterHotKey(0, nativeMods ^ nativeKey); +} diff --git a/src/qxtglobalshortcut_x11.cpp b/src/qxtglobalshortcut_x11.cpp new file mode 100644 index 0000000..93fd733 --- /dev/null +++ b/src/qxtglobalshortcut_x11.cpp @@ -0,0 +1,286 @@ +#include "qxtglobalshortcut_p.h" +/**************************************************************************** +** Copyright (c) 2006 - 2011, the LibQxt project. +** See the Qxt AUTHORS file for a list of authors and copyright holders. +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * 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. +** * Neither the name of the LibQxt project 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 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. +** +** +*****************************************************************************/ + +#if QT_VERSION < QT_VERSION_CHECK(5,0,0) +# include +#else +# include +# include +# include +#endif +#include +#include +#include +#include + +#include "xcbkeyboard.h" + +namespace { + +/** + * Creates first invisible application window + * so X11 key press events can be received. + * + * This is used for Cinnamon and KDE. + */ +void createFirstWindow() +{ + static QWidget *w = nullptr; + if (!w) { + // Try hard so the window is not visible. + + // Tool tips won't show in taskbar. + w = new QWidget(nullptr, Qt::ToolTip); + + // Move out of screen (if it's not possible to show the window minimized). + w->resize(1, 1); + w->move(-100000, -100000); + + // Show and hide quickly. + w->showMinimized(); + w->hide(); + } +} + +QVector maskModifiers() +{ + return QVector() << 0 << Mod2Mask << LockMask << (Mod2Mask | LockMask); +} + +using X11ErrorHandler = int (*)(Display* display, XErrorEvent* event); + +class QxtX11ErrorHandler { +public: + static bool error; + + static int qxtX11ErrorHandler(Display* display, XErrorEvent *event) + { + Q_UNUSED(display); + switch (event->error_code) + { + case BadAccess: + case BadValue: + case BadWindow: + if (event->request_code == 33 /* X_GrabKey */ || + event->request_code == 34 /* X_UngrabKey */) + { + error = true; + //TODO: + //char errstr[256]; + //XGetErrorText(dpy, err->error_code, errstr, 256); + } + } + return 0; + } + + QxtX11ErrorHandler() + : m_previousErrorHandler(nullptr) + { + error = false; + m_previousErrorHandler = XSetErrorHandler(qxtX11ErrorHandler); + } + + ~QxtX11ErrorHandler() + { + XSetErrorHandler(m_previousErrorHandler); + } + +private: + X11ErrorHandler m_previousErrorHandler; +}; + +bool QxtX11ErrorHandler::error = false; + +class QxtX11Data { +public: + QxtX11Data() + : m_display(nullptr) + { + createFirstWindow(); + +#if QT_VERSION < QT_VERSION_CHECK(5,0,0) + m_display = QX11Info::display(); +#else + QPlatformNativeInterface *native = qApp->platformNativeInterface(); + void *display = native->nativeResourceForScreen(QByteArray("display"), + QGuiApplication::primaryScreen()); + m_display = reinterpret_cast(display); +#endif + } + + bool isValid() const + { + return m_display != nullptr; + } + + Display *display() + { + Q_ASSERT(isValid()); + return m_display; + } + + Window rootWindow() + { + return DefaultRootWindow(display()); + } + + bool grabKey(quint32 keycode, quint32 modifiers, Window window) + { + QxtX11ErrorHandler errorHandler; + + for (const auto maskMods : maskModifiers()) { + XGrabKey(display(), static_cast(keycode), modifiers | maskMods, window, True, + GrabModeAsync, GrabModeAsync); + if (errorHandler.error) + break; + } + + if (errorHandler.error) { + ungrabKey(keycode, modifiers, window); + return false; + } + + return true; + } + + bool ungrabKey(quint32 keycode, quint32 modifiers, Window window) + { + QxtX11ErrorHandler errorHandler; + + for (const auto maskMods : maskModifiers()) { + XUngrabKey(display(), static_cast(keycode), modifiers | maskMods, window); + } + + return !errorHandler.error; + } + +private: + Display *m_display; +}; + +KeySym qtKeyToXKeySym(Qt::Key key) +{ + const auto keySym = XStringToKeysym(QKeySequence(key).toString().toLatin1().data()); + if (keySym != NoSymbol) + return keySym; + + for (int i = 0; KeyTbl[i] != 0; i += 2) { + if (KeyTbl[i + 1] == key) + return KeyTbl[i]; + } + + return static_cast(key); +} + +} // namespace + +#if QT_VERSION < QT_VERSION_CHECK(5,0,0) +bool QxtGlobalShortcutPrivate::eventFilter(void* message) +{ + XEvent *event = static_cast(message); + if (event->type == KeyPress) + { + XKeyEvent* key = reinterpret_cast(event); + unsigned int keycode = key->keycode; + unsigned int keystate = key->state; +#else +bool QxtGlobalShortcutPrivate::nativeEventFilter(const QByteArray & eventType, + void * message, qintptr * result) +{ + Q_UNUSED(result); + + xcb_key_press_event_t *kev = nullptr; + if (eventType == "xcb_generic_event_t") { + xcb_generic_event_t* ev = static_cast(message); + if ((ev->response_type & 127) == XCB_KEY_PRESS) + kev = static_cast(message); + } + + if (kev != nullptr) { + unsigned int keycode = kev->detail; + unsigned int keystate = 0; + if(kev->state & XCB_MOD_MASK_1) + keystate |= Mod1Mask; + if(kev->state & XCB_MOD_MASK_CONTROL) + keystate |= ControlMask; + if(kev->state & XCB_MOD_MASK_4) + keystate |= Mod4Mask; + if(kev->state & XCB_MOD_MASK_SHIFT) + keystate |= ShiftMask; +#endif + activateShortcut(keycode, + // Mod1Mask == Alt, Mod4Mask == Meta + keystate & (ShiftMask | ControlMask | Mod1Mask | Mod4Mask)); + } + return false; +} + +quint32 QxtGlobalShortcutPrivate::nativeModifiers(Qt::KeyboardModifiers modifiers) +{ + // ShiftMask, LockMask, ControlMask, Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, and Mod5Mask + quint32 native = 0; + if (modifiers & Qt::ShiftModifier) + native |= ShiftMask; + if (modifiers & Qt::ControlModifier) + native |= ControlMask; + if (modifiers & Qt::AltModifier) + native |= Mod1Mask; + if (modifiers & Qt::MetaModifier) + native |= Mod4Mask; + + // TODO: resolve these? + //if (modifiers & Qt::MetaModifier) + //if (modifiers & Qt::KeypadModifier) + //if (modifiers & Qt::GroupSwitchModifier) + return native; +} + +quint32 QxtGlobalShortcutPrivate::nativeKeycode(Qt::Key key) +{ + QxtX11Data x11; + if (!x11.isValid()) + return 0; + + const KeySym keysym = qtKeyToXKeySym(key); + return XKeysymToKeycode(x11.display(), keysym); +} + +bool QxtGlobalShortcutPrivate::registerShortcut(quint32 nativeKey, quint32 nativeMods) +{ + QxtX11Data x11; + return x11.isValid() && x11.grabKey(nativeKey, nativeMods, x11.rootWindow()); +} + +bool QxtGlobalShortcutPrivate::unregisterShortcut(quint32 nativeKey, quint32 nativeMods) +{ + QxtX11Data x11; + return x11.isValid() && x11.ungrabKey(nativeKey, nativeMods, x11.rootWindow()); +} diff --git a/src/xcbkeyboard.h b/src/xcbkeyboard.h new file mode 100644 index 0000000..7a0bf20 --- /dev/null +++ b/src/xcbkeyboard.h @@ -0,0 +1,571 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Following definitions and table are taken from +// "qt5/qtbase/src/plugins/platforms/xcb/qxcbkeyboard.cpp". + +#include +#include + +#ifndef XK_ISO_Left_Tab +#define XK_ISO_Left_Tab 0xFE20 +#endif + +#ifndef XK_dead_hook +#define XK_dead_hook 0xFE61 +#endif + +#ifndef XK_dead_horn +#define XK_dead_horn 0xFE62 +#endif + +#ifndef XK_Codeinput +#define XK_Codeinput 0xFF37 +#endif + +#ifndef XK_Kanji_Bangou +#define XK_Kanji_Bangou 0xFF37 /* same as codeinput */ +#endif + +// Fix old X libraries +#ifndef XK_KP_Home +#define XK_KP_Home 0xFF95 +#endif +#ifndef XK_KP_Left +#define XK_KP_Left 0xFF96 +#endif +#ifndef XK_KP_Up +#define XK_KP_Up 0xFF97 +#endif +#ifndef XK_KP_Right +#define XK_KP_Right 0xFF98 +#endif +#ifndef XK_KP_Down +#define XK_KP_Down 0xFF99 +#endif +#ifndef XK_KP_Prior +#define XK_KP_Prior 0xFF9A +#endif +#ifndef XK_KP_Next +#define XK_KP_Next 0xFF9B +#endif +#ifndef XK_KP_End +#define XK_KP_End 0xFF9C +#endif +#ifndef XK_KP_Insert +#define XK_KP_Insert 0xFF9E +#endif +#ifndef XK_KP_Delete +#define XK_KP_Delete 0xFF9F +#endif + +// the next lines are taken on 10/2009 from X.org (X11/XF86keysym.h), defining some special +// multimedia keys. They are included here as not every system has them. +#define XF86XK_MonBrightnessUp 0x1008FF02 +#define XF86XK_MonBrightnessDown 0x1008FF03 +#define XF86XK_KbdLightOnOff 0x1008FF04 +#define XF86XK_KbdBrightnessUp 0x1008FF05 +#define XF86XK_KbdBrightnessDown 0x1008FF06 +#define XF86XK_Standby 0x1008FF10 +#define XF86XK_AudioLowerVolume 0x1008FF11 +#define XF86XK_AudioMute 0x1008FF12 +#define XF86XK_AudioRaiseVolume 0x1008FF13 +#define XF86XK_AudioPlay 0x1008FF14 +#define XF86XK_AudioStop 0x1008FF15 +#define XF86XK_AudioPrev 0x1008FF16 +#define XF86XK_AudioNext 0x1008FF17 +#define XF86XK_HomePage 0x1008FF18 +#define XF86XK_Mail 0x1008FF19 +#define XF86XK_Start 0x1008FF1A +#define XF86XK_Search 0x1008FF1B +#define XF86XK_AudioRecord 0x1008FF1C +#define XF86XK_Calculator 0x1008FF1D +#define XF86XK_Memo 0x1008FF1E +#define XF86XK_ToDoList 0x1008FF1F +#define XF86XK_Calendar 0x1008FF20 +#define XF86XK_PowerDown 0x1008FF21 +#define XF86XK_ContrastAdjust 0x1008FF22 +#define XF86XK_Back 0x1008FF26 +#define XF86XK_Forward 0x1008FF27 +#define XF86XK_Stop 0x1008FF28 +#define XF86XK_Refresh 0x1008FF29 +#define XF86XK_PowerOff 0x1008FF2A +#define XF86XK_WakeUp 0x1008FF2B +#define XF86XK_Eject 0x1008FF2C +#define XF86XK_ScreenSaver 0x1008FF2D +#define XF86XK_WWW 0x1008FF2E +#define XF86XK_Sleep 0x1008FF2F +#define XF86XK_Favorites 0x1008FF30 +#define XF86XK_AudioPause 0x1008FF31 +#define XF86XK_AudioMedia 0x1008FF32 +#define XF86XK_MyComputer 0x1008FF33 +#define XF86XK_LightBulb 0x1008FF35 +#define XF86XK_Shop 0x1008FF36 +#define XF86XK_History 0x1008FF37 +#define XF86XK_OpenURL 0x1008FF38 +#define XF86XK_AddFavorite 0x1008FF39 +#define XF86XK_HotLinks 0x1008FF3A +#define XF86XK_BrightnessAdjust 0x1008FF3B +#define XF86XK_Finance 0x1008FF3C +#define XF86XK_Community 0x1008FF3D +#define XF86XK_AudioRewind 0x1008FF3E +#define XF86XK_BackForward 0x1008FF3F +#define XF86XK_Launch0 0x1008FF40 +#define XF86XK_Launch1 0x1008FF41 +#define XF86XK_Launch2 0x1008FF42 +#define XF86XK_Launch3 0x1008FF43 +#define XF86XK_Launch4 0x1008FF44 +#define XF86XK_Launch5 0x1008FF45 +#define XF86XK_Launch6 0x1008FF46 +#define XF86XK_Launch7 0x1008FF47 +#define XF86XK_Launch8 0x1008FF48 +#define XF86XK_Launch9 0x1008FF49 +#define XF86XK_LaunchA 0x1008FF4A +#define XF86XK_LaunchB 0x1008FF4B +#define XF86XK_LaunchC 0x1008FF4C +#define XF86XK_LaunchD 0x1008FF4D +#define XF86XK_LaunchE 0x1008FF4E +#define XF86XK_LaunchF 0x1008FF4F +#define XF86XK_ApplicationLeft 0x1008FF50 +#define XF86XK_ApplicationRight 0x1008FF51 +#define XF86XK_Book 0x1008FF52 +#define XF86XK_CD 0x1008FF53 +#define XF86XK_Calculater 0x1008FF54 +#define XF86XK_Clear 0x1008FF55 +#define XF86XK_ClearGrab 0x1008FE21 +#define XF86XK_Close 0x1008FF56 +#define XF86XK_Copy 0x1008FF57 +#define XF86XK_Cut 0x1008FF58 +#define XF86XK_Display 0x1008FF59 +#define XF86XK_DOS 0x1008FF5A +#define XF86XK_Documents 0x1008FF5B +#define XF86XK_Excel 0x1008FF5C +#define XF86XK_Explorer 0x1008FF5D +#define XF86XK_Game 0x1008FF5E +#define XF86XK_Go 0x1008FF5F +#define XF86XK_iTouch 0x1008FF60 +#define XF86XK_LogOff 0x1008FF61 +#define XF86XK_Market 0x1008FF62 +#define XF86XK_Meeting 0x1008FF63 +#define XF86XK_MenuKB 0x1008FF65 +#define XF86XK_MenuPB 0x1008FF66 +#define XF86XK_MySites 0x1008FF67 +#define XF86XK_New 0x1008FF68 +#define XF86XK_News 0x1008FF69 +#define XF86XK_OfficeHome 0x1008FF6A +#define XF86XK_Open 0x1008FF6B +#define XF86XK_Option 0x1008FF6C +#define XF86XK_Paste 0x1008FF6D +#define XF86XK_Phone 0x1008FF6E +#define XF86XK_Reply 0x1008FF72 +#define XF86XK_Reload 0x1008FF73 +#define XF86XK_RotateWindows 0x1008FF74 +#define XF86XK_RotationPB 0x1008FF75 +#define XF86XK_RotationKB 0x1008FF76 +#define XF86XK_Save 0x1008FF77 +#define XF86XK_Send 0x1008FF7B +#define XF86XK_Spell 0x1008FF7C +#define XF86XK_SplitScreen 0x1008FF7D +#define XF86XK_Support 0x1008FF7E +#define XF86XK_TaskPane 0x1008FF7F +#define XF86XK_Terminal 0x1008FF80 +#define XF86XK_Tools 0x1008FF81 +#define XF86XK_Travel 0x1008FF82 +#define XF86XK_Video 0x1008FF87 +#define XF86XK_Word 0x1008FF89 +#define XF86XK_Xfer 0x1008FF8A +#define XF86XK_ZoomIn 0x1008FF8B +#define XF86XK_ZoomOut 0x1008FF8C +#define XF86XK_Away 0x1008FF8D +#define XF86XK_Messenger 0x1008FF8E +#define XF86XK_WebCam 0x1008FF8F +#define XF86XK_MailForward 0x1008FF90 +#define XF86XK_Pictures 0x1008FF91 +#define XF86XK_Music 0x1008FF92 +#define XF86XK_Battery 0x1008FF93 +#define XF86XK_Bluetooth 0x1008FF94 +#define XF86XK_WLAN 0x1008FF95 +#define XF86XK_UWB 0x1008FF96 +#define XF86XK_AudioForward 0x1008FF97 +#define XF86XK_AudioRepeat 0x1008FF98 +#define XF86XK_AudioRandomPlay 0x1008FF99 +#define XF86XK_Subtitle 0x1008FF9A +#define XF86XK_AudioCycleTrack 0x1008FF9B +#define XF86XK_Time 0x1008FF9F +#define XF86XK_Select 0x1008FFA0 +#define XF86XK_View 0x1008FFA1 +#define XF86XK_TopMenu 0x1008FFA2 +#define XF86XK_Red 0x1008FFA3 +#define XF86XK_Green 0x1008FFA4 +#define XF86XK_Yellow 0x1008FFA5 +#define XF86XK_Blue 0x1008FFA6 +#define XF86XK_Suspend 0x1008FFA7 +#define XF86XK_Hibernate 0x1008FFA8 +#define XF86XK_TouchpadToggle 0x1008FFA9 +#define XF86XK_TouchpadOn 0x1008FFB0 +#define XF86XK_TouchpadOff 0x1008FFB1 +#define XF86XK_AudioMicMute 0x1008FFB2 + + +// end of XF86keysyms.h + +// keyboard mapping table +static const unsigned int KeyTbl[] = { + + // misc keys + + XK_Escape, Qt::Key_Escape, + XK_Tab, Qt::Key_Tab, + XK_ISO_Left_Tab, Qt::Key_Backtab, + XK_BackSpace, Qt::Key_Backspace, + XK_Return, Qt::Key_Return, + XK_Insert, Qt::Key_Insert, + XK_Delete, Qt::Key_Delete, + XK_Clear, Qt::Key_Delete, + XK_Pause, Qt::Key_Pause, + XK_Print, Qt::Key_Print, + 0x1005FF60, Qt::Key_SysReq, // hardcoded Sun SysReq + 0x1007ff00, Qt::Key_SysReq, // hardcoded X386 SysReq + + // cursor movement + + XK_Home, Qt::Key_Home, + XK_End, Qt::Key_End, + XK_Left, Qt::Key_Left, + XK_Up, Qt::Key_Up, + XK_Right, Qt::Key_Right, + XK_Down, Qt::Key_Down, + XK_Prior, Qt::Key_PageUp, + XK_Next, Qt::Key_PageDown, + + // modifiers + + XK_Shift_L, Qt::Key_Shift, + XK_Shift_R, Qt::Key_Shift, + XK_Shift_Lock, Qt::Key_Shift, + XK_Control_L, Qt::Key_Control, + XK_Control_R, Qt::Key_Control, + XK_Meta_L, Qt::Key_Meta, + XK_Meta_R, Qt::Key_Meta, + XK_Alt_L, Qt::Key_Alt, + XK_Alt_R, Qt::Key_Alt, + XK_Caps_Lock, Qt::Key_CapsLock, + XK_Num_Lock, Qt::Key_NumLock, + XK_Scroll_Lock, Qt::Key_ScrollLock, + XK_Super_L, Qt::Key_Super_L, + XK_Super_R, Qt::Key_Super_R, + XK_Menu, Qt::Key_Menu, + XK_Hyper_L, Qt::Key_Hyper_L, + XK_Hyper_R, Qt::Key_Hyper_R, + XK_Help, Qt::Key_Help, + 0x1000FF74, Qt::Key_Backtab, // hardcoded HP backtab + 0x1005FF10, Qt::Key_F11, // hardcoded Sun F36 (labeled F11) + 0x1005FF11, Qt::Key_F12, // hardcoded Sun F37 (labeled F12) + + // numeric and function keypad keys + + XK_KP_Space, Qt::Key_Space, + XK_KP_Tab, Qt::Key_Tab, + XK_KP_Enter, Qt::Key_Enter, + //XK_KP_F1, Qt::Key_F1, + //XK_KP_F2, Qt::Key_F2, + //XK_KP_F3, Qt::Key_F3, + //XK_KP_F4, Qt::Key_F4, + XK_KP_Home, Qt::Key_Home, + XK_KP_Left, Qt::Key_Left, + XK_KP_Up, Qt::Key_Up, + XK_KP_Right, Qt::Key_Right, + XK_KP_Down, Qt::Key_Down, + XK_KP_Prior, Qt::Key_PageUp, + XK_KP_Next, Qt::Key_PageDown, + XK_KP_End, Qt::Key_End, + XK_KP_Begin, Qt::Key_Clear, + XK_KP_Insert, Qt::Key_Insert, + XK_KP_Delete, Qt::Key_Delete, + XK_KP_Equal, Qt::Key_Equal, + XK_KP_Multiply, Qt::Key_Asterisk, + XK_KP_Add, Qt::Key_Plus, + XK_KP_Separator, Qt::Key_Comma, + XK_KP_Subtract, Qt::Key_Minus, + XK_KP_Decimal, Qt::Key_Period, + XK_KP_Divide, Qt::Key_Slash, + + // International input method support keys + + // International & multi-key character composition + XK_ISO_Level3_Shift, Qt::Key_AltGr, + XK_Multi_key, Qt::Key_Multi_key, + XK_Codeinput, Qt::Key_Codeinput, + XK_SingleCandidate, Qt::Key_SingleCandidate, + XK_MultipleCandidate, Qt::Key_MultipleCandidate, + XK_PreviousCandidate, Qt::Key_PreviousCandidate, + + // Misc Functions + XK_Mode_switch, Qt::Key_Mode_switch, + XK_script_switch, Qt::Key_Mode_switch, + + // Japanese keyboard support + XK_Kanji, Qt::Key_Kanji, + XK_Muhenkan, Qt::Key_Muhenkan, + //XK_Henkan_Mode, Qt::Key_Henkan_Mode, + XK_Henkan_Mode, Qt::Key_Henkan, + XK_Henkan, Qt::Key_Henkan, + XK_Romaji, Qt::Key_Romaji, + XK_Hiragana, Qt::Key_Hiragana, + XK_Katakana, Qt::Key_Katakana, + XK_Hiragana_Katakana, Qt::Key_Hiragana_Katakana, + XK_Zenkaku, Qt::Key_Zenkaku, + XK_Hankaku, Qt::Key_Hankaku, + XK_Zenkaku_Hankaku, Qt::Key_Zenkaku_Hankaku, + XK_Touroku, Qt::Key_Touroku, + XK_Massyo, Qt::Key_Massyo, + XK_Kana_Lock, Qt::Key_Kana_Lock, + XK_Kana_Shift, Qt::Key_Kana_Shift, + XK_Eisu_Shift, Qt::Key_Eisu_Shift, + XK_Eisu_toggle, Qt::Key_Eisu_toggle, + //XK_Kanji_Bangou, Qt::Key_Kanji_Bangou, + //XK_Zen_Koho, Qt::Key_Zen_Koho, + //XK_Mae_Koho, Qt::Key_Mae_Koho, + XK_Kanji_Bangou, Qt::Key_Codeinput, + XK_Zen_Koho, Qt::Key_MultipleCandidate, + XK_Mae_Koho, Qt::Key_PreviousCandidate, + +#ifdef XK_KOREAN + // Korean keyboard support + XK_Hangul, Qt::Key_Hangul, + XK_Hangul_Start, Qt::Key_Hangul_Start, + XK_Hangul_End, Qt::Key_Hangul_End, + XK_Hangul_Hanja, Qt::Key_Hangul_Hanja, + XK_Hangul_Jamo, Qt::Key_Hangul_Jamo, + XK_Hangul_Romaja, Qt::Key_Hangul_Romaja, + //XK_Hangul_Codeinput, Qt::Key_Hangul_Codeinput, + XK_Hangul_Codeinput, Qt::Key_Codeinput, + XK_Hangul_Jeonja, Qt::Key_Hangul_Jeonja, + XK_Hangul_Banja, Qt::Key_Hangul_Banja, + XK_Hangul_PreHanja, Qt::Key_Hangul_PreHanja, + XK_Hangul_PostHanja, Qt::Key_Hangul_PostHanja, + //XK_Hangul_SingleCandidate,Qt::Key_Hangul_SingleCandidate, + //XK_Hangul_MultipleCandidate,Qt::Key_Hangul_MultipleCandidate, + //XK_Hangul_PreviousCandidate,Qt::Key_Hangul_PreviousCandidate, + XK_Hangul_SingleCandidate, Qt::Key_SingleCandidate, + XK_Hangul_MultipleCandidate,Qt::Key_MultipleCandidate, + XK_Hangul_PreviousCandidate,Qt::Key_PreviousCandidate, + XK_Hangul_Special, Qt::Key_Hangul_Special, + //XK_Hangul_switch, Qt::Key_Hangul_switch, + XK_Hangul_switch, Qt::Key_Mode_switch, +#endif // XK_KOREAN + + // dead keys + XK_dead_grave, Qt::Key_Dead_Grave, + XK_dead_acute, Qt::Key_Dead_Acute, + XK_dead_circumflex, Qt::Key_Dead_Circumflex, + XK_dead_tilde, Qt::Key_Dead_Tilde, + XK_dead_macron, Qt::Key_Dead_Macron, + XK_dead_breve, Qt::Key_Dead_Breve, + XK_dead_abovedot, Qt::Key_Dead_Abovedot, + XK_dead_diaeresis, Qt::Key_Dead_Diaeresis, + XK_dead_abovering, Qt::Key_Dead_Abovering, + XK_dead_doubleacute, Qt::Key_Dead_Doubleacute, + XK_dead_caron, Qt::Key_Dead_Caron, + XK_dead_cedilla, Qt::Key_Dead_Cedilla, + XK_dead_ogonek, Qt::Key_Dead_Ogonek, + XK_dead_iota, Qt::Key_Dead_Iota, + XK_dead_voiced_sound, Qt::Key_Dead_Voiced_Sound, + XK_dead_semivoiced_sound, Qt::Key_Dead_Semivoiced_Sound, + XK_dead_belowdot, Qt::Key_Dead_Belowdot, + XK_dead_hook, Qt::Key_Dead_Hook, + XK_dead_horn, Qt::Key_Dead_Horn, + + // Special keys from X.org - This include multimedia keys, + // wireless/bluetooth/uwb keys, special launcher keys, etc. + XF86XK_Back, Qt::Key_Back, + XF86XK_Forward, Qt::Key_Forward, + XF86XK_Stop, Qt::Key_Stop, + XF86XK_Refresh, Qt::Key_Refresh, + XF86XK_Favorites, Qt::Key_Favorites, + XF86XK_AudioMedia, Qt::Key_LaunchMedia, + XF86XK_OpenURL, Qt::Key_OpenUrl, + XF86XK_HomePage, Qt::Key_HomePage, + XF86XK_Search, Qt::Key_Search, + XF86XK_AudioLowerVolume, Qt::Key_VolumeDown, + XF86XK_AudioMute, Qt::Key_VolumeMute, + XF86XK_AudioRaiseVolume, Qt::Key_VolumeUp, + XF86XK_AudioPlay, Qt::Key_MediaPlay, + XF86XK_AudioStop, Qt::Key_MediaStop, + XF86XK_AudioPrev, Qt::Key_MediaPrevious, + XF86XK_AudioNext, Qt::Key_MediaNext, + XF86XK_AudioRecord, Qt::Key_MediaRecord, + XF86XK_AudioPause, Qt::Key_MediaPause, + XF86XK_Mail, Qt::Key_LaunchMail, + XF86XK_MyComputer, Qt::Key_Launch0, // ### Qt 6: remap properly + XF86XK_Calculator, Qt::Key_Launch1, + XF86XK_Memo, Qt::Key_Memo, + XF86XK_ToDoList, Qt::Key_ToDoList, + XF86XK_Calendar, Qt::Key_Calendar, + XF86XK_PowerDown, Qt::Key_PowerDown, + XF86XK_ContrastAdjust, Qt::Key_ContrastAdjust, + XF86XK_Standby, Qt::Key_Standby, + XF86XK_MonBrightnessUp, Qt::Key_MonBrightnessUp, + XF86XK_MonBrightnessDown, Qt::Key_MonBrightnessDown, + XF86XK_KbdLightOnOff, Qt::Key_KeyboardLightOnOff, + XF86XK_KbdBrightnessUp, Qt::Key_KeyboardBrightnessUp, + XF86XK_KbdBrightnessDown, Qt::Key_KeyboardBrightnessDown, + XF86XK_PowerOff, Qt::Key_PowerOff, + XF86XK_WakeUp, Qt::Key_WakeUp, + XF86XK_Eject, Qt::Key_Eject, + XF86XK_ScreenSaver, Qt::Key_ScreenSaver, + XF86XK_WWW, Qt::Key_WWW, + XF86XK_Sleep, Qt::Key_Sleep, + XF86XK_LightBulb, Qt::Key_LightBulb, + XF86XK_Shop, Qt::Key_Shop, + XF86XK_History, Qt::Key_History, + XF86XK_AddFavorite, Qt::Key_AddFavorite, + XF86XK_HotLinks, Qt::Key_HotLinks, + XF86XK_BrightnessAdjust, Qt::Key_BrightnessAdjust, + XF86XK_Finance, Qt::Key_Finance, + XF86XK_Community, Qt::Key_Community, + XF86XK_AudioRewind, Qt::Key_AudioRewind, + XF86XK_BackForward, Qt::Key_BackForward, + XF86XK_ApplicationLeft, Qt::Key_ApplicationLeft, + XF86XK_ApplicationRight, Qt::Key_ApplicationRight, + XF86XK_Book, Qt::Key_Book, + XF86XK_CD, Qt::Key_CD, + XF86XK_Calculater, Qt::Key_Calculator, + XF86XK_Clear, Qt::Key_Clear, + XF86XK_ClearGrab, Qt::Key_ClearGrab, + XF86XK_Close, Qt::Key_Close, + XF86XK_Copy, Qt::Key_Copy, + XF86XK_Cut, Qt::Key_Cut, + XF86XK_Display, Qt::Key_Display, + XF86XK_DOS, Qt::Key_DOS, + XF86XK_Documents, Qt::Key_Documents, + XF86XK_Excel, Qt::Key_Excel, + XF86XK_Explorer, Qt::Key_Explorer, + XF86XK_Game, Qt::Key_Game, + XF86XK_Go, Qt::Key_Go, + XF86XK_iTouch, Qt::Key_iTouch, + XF86XK_LogOff, Qt::Key_LogOff, + XF86XK_Market, Qt::Key_Market, + XF86XK_Meeting, Qt::Key_Meeting, + XF86XK_MenuKB, Qt::Key_MenuKB, + XF86XK_MenuPB, Qt::Key_MenuPB, + XF86XK_MySites, Qt::Key_MySites, +#if QT_VERSION >= 0x050400 + XF86XK_New, Qt::Key_New, +#endif + XF86XK_News, Qt::Key_News, + XF86XK_OfficeHome, Qt::Key_OfficeHome, +#if QT_VERSION >= 0x050400 + XF86XK_Open, Qt::Key_Open, +#endif + XF86XK_Option, Qt::Key_Option, + XF86XK_Paste, Qt::Key_Paste, + XF86XK_Phone, Qt::Key_Phone, + XF86XK_Reply, Qt::Key_Reply, + XF86XK_Reload, Qt::Key_Reload, + XF86XK_RotateWindows, Qt::Key_RotateWindows, + XF86XK_RotationPB, Qt::Key_RotationPB, + XF86XK_RotationKB, Qt::Key_RotationKB, + XF86XK_Save, Qt::Key_Save, + XF86XK_Send, Qt::Key_Send, + XF86XK_Spell, Qt::Key_Spell, + XF86XK_SplitScreen, Qt::Key_SplitScreen, + XF86XK_Support, Qt::Key_Support, + XF86XK_TaskPane, Qt::Key_TaskPane, + XF86XK_Terminal, Qt::Key_Terminal, + XF86XK_Tools, Qt::Key_Tools, + XF86XK_Travel, Qt::Key_Travel, + XF86XK_Video, Qt::Key_Video, + XF86XK_Word, Qt::Key_Word, + XF86XK_Xfer, Qt::Key_Xfer, + XF86XK_ZoomIn, Qt::Key_ZoomIn, + XF86XK_ZoomOut, Qt::Key_ZoomOut, + XF86XK_Away, Qt::Key_Away, + XF86XK_Messenger, Qt::Key_Messenger, + XF86XK_WebCam, Qt::Key_WebCam, + XF86XK_MailForward, Qt::Key_MailForward, + XF86XK_Pictures, Qt::Key_Pictures, + XF86XK_Music, Qt::Key_Music, + XF86XK_Battery, Qt::Key_Battery, + XF86XK_Bluetooth, Qt::Key_Bluetooth, + XF86XK_WLAN, Qt::Key_WLAN, + XF86XK_UWB, Qt::Key_UWB, + XF86XK_AudioForward, Qt::Key_AudioForward, + XF86XK_AudioRepeat, Qt::Key_AudioRepeat, + XF86XK_AudioRandomPlay, Qt::Key_AudioRandomPlay, + XF86XK_Subtitle, Qt::Key_Subtitle, + XF86XK_AudioCycleTrack, Qt::Key_AudioCycleTrack, + XF86XK_Time, Qt::Key_Time, + XF86XK_Select, Qt::Key_Select, + XF86XK_View, Qt::Key_View, + XF86XK_TopMenu, Qt::Key_TopMenu, +#if QT_VERSION >= 0x050400 + XF86XK_Red, Qt::Key_Red, + XF86XK_Green, Qt::Key_Green, + XF86XK_Yellow, Qt::Key_Yellow, + XF86XK_Blue, Qt::Key_Blue, +#endif + XF86XK_Bluetooth, Qt::Key_Bluetooth, + XF86XK_Suspend, Qt::Key_Suspend, + XF86XK_Hibernate, Qt::Key_Hibernate, +#if QT_VERSION >= 0x050400 + XF86XK_TouchpadToggle, Qt::Key_TouchpadToggle, + XF86XK_TouchpadOn, Qt::Key_TouchpadOn, + XF86XK_TouchpadOff, Qt::Key_TouchpadOff, + XF86XK_AudioMicMute, Qt::Key_MicMute, +#endif + XF86XK_Launch0, Qt::Key_Launch2, // ### Qt 6: remap properly + XF86XK_Launch1, Qt::Key_Launch3, + XF86XK_Launch2, Qt::Key_Launch4, + XF86XK_Launch3, Qt::Key_Launch5, + XF86XK_Launch4, Qt::Key_Launch6, + XF86XK_Launch5, Qt::Key_Launch7, + XF86XK_Launch6, Qt::Key_Launch8, + XF86XK_Launch7, Qt::Key_Launch9, + XF86XK_Launch8, Qt::Key_LaunchA, + XF86XK_Launch9, Qt::Key_LaunchB, + XF86XK_LaunchA, Qt::Key_LaunchC, + XF86XK_LaunchB, Qt::Key_LaunchD, + XF86XK_LaunchC, Qt::Key_LaunchE, + XF86XK_LaunchD, Qt::Key_LaunchF, + XF86XK_LaunchE, Qt::Key_LaunchG, + XF86XK_LaunchF, Qt::Key_LaunchH, + + 0, 0 +};