Skip to content
Snippets Groups Projects
Commit 7a759dab authored by Capucine Berthet's avatar Capucine Berthet Committed by Sébastien Blin
Browse files

Push-To-Talk : add global and local ptt

It works with a Pimpl which the right pttlistener.cpp depending
on the platform you are on (macOs, windows or X11). It is a
global PTT which listen to key events during calls. If the
global PTT is not supported, a local PTT is set.

jami-project#1402

Change-Id: I8399800966c737bb8e8a656ecbb6af7ac7cdde8c
parent d2e87150
No related branches found
Tags nightly/20231030.0
No related merge requests found
Showing
with 2253 additions and 25 deletions
......@@ -245,7 +245,8 @@ set(COMMON_SOURCES
${APP_SRC_DIR}/previewengine.cpp
${APP_SRC_DIR}/imagedownloader.cpp
${APP_SRC_DIR}/pluginversionmanager.cpp
${APP_SRC_DIR}/connectioninfolistmodel.cpp)
${APP_SRC_DIR}/connectioninfolistmodel.cpp
${APP_SRC_DIR}/pluginversionmanager.cpp)
set(COMMON_HEADERS
${APP_SRC_DIR}/avatarimageprovider.h
......@@ -312,7 +313,8 @@ set(COMMON_HEADERS
${APP_SRC_DIR}/htmlparser.h
${APP_SRC_DIR}/imagedownloader.h
${APP_SRC_DIR}/pluginversionmanager.h
${APP_SRC_DIR}/connectioninfolistmodel.h)
${APP_SRC_DIR}/connectioninfolistmodel.h
${APP_SRC_DIR}/pttlistener.h)
# For libavutil/avframe.
set(LIBJAMI_CONTRIB_DIR "${DAEMON_DIR}/contrib")
......@@ -494,6 +496,29 @@ else()
OPTIONAL_COMPONENTS LinguistTools)
endif()
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
if (DEFINED ENV{XDG_SESSION_TYPE})
if ($ENV{XDG_SESSION_TYPE} STREQUAL "x11")
set(PTT_PLATFORM "x11")
list(APPEND COMMON_HEADER ${APP_SRC_DIR}/platform/X11/xcbkeyboard.H)
# TODO: add Wayland support
endif ()
endif ()
elseif (CMAKE_SYSTEM_NAME STREQUAL "Windows")
set(PTT_PLATFORM "windows")
elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
set(PTT_PLATFORM "macos")
endif ()
if (NOT ${PTT_PLATFORM} STREQUAL "")
message(STATUS "Platform: ${PTT_PLATFORM}")
add_definitions(-DHAVE_GLOBAL_PTT)
list(APPEND COMMON_SOURCES ${APP_SRC_DIR}/platform/${PTT_PLATFORM}/pttlistener.cpp)
else ()
message(WARNING "Global push-to-talk not supported.")
list(APPEND COMMON_SOURCES ${APP_SRC_DIR}/platform/local/pttlistener.cpp)
endif ()
# common includes
include_directories(
${PROJECT_SOURCE_DIR}
......@@ -594,7 +619,6 @@ elseif (NOT APPLE)
${GLIB_LIBRARIES}
${GIO_LIBRARIES})
# Installation rules
install(
TARGETS ${PROJECT_NAME}
RUNTIME DESTINATION bin)
......@@ -725,7 +749,7 @@ else()
list(APPEND CLIENT_LIBS
"-framework AVFoundation"
"-framework CoreAudio -framework CoreMedia -framework CoreVideo"
"-framework VideoToolbox -framework AudioUnit"
"-framework VideoToolbox -framework AudioUnit -framework Carbon"
"-framework Security"
compression
resolv
......
......@@ -36,5 +36,7 @@
<string>Jami requires to access your microphone to make calls and record audio</string>
<key>ITSAppUsesNonExemptEncryption</key>
<true/>
<key>NSAppleEventsUsageDescription</key>
<string>Jami requires to monitor global key events for push-to-talk functionality.</string>
</dict>
</plist>
......@@ -66,8 +66,9 @@ extern const QString defaultDownloadPath;
X(ShowSendOption, false) \
X(DonationVisibleDate, "2023-11-01 05:00") \
X(IsDonationVisible, true) \
X(DonationEndDate, "2024-01-01 00:00")
X(DonationEndDate, "2024-01-01 00:00") \
X(EnablePtt, false) \
X(pttKey, 36)
/*
* A class to expose settings keys in both c++ and QML.
* Note: this is using a non-constructable class instead of a
......
......@@ -7,6 +7,7 @@
* Author: Isa Nanic <isa.nanic@savoirfairelinux.com>
* Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
* Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
* Author: Capucine Berthet <capucine.berthet@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -47,7 +48,7 @@ CallAdapter::CallAdapter(SystemTray* systemTray, LRCInstance* instance, QObject*
timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &CallAdapter::updateAdvancedInformation);
overlayModel_.reset(new CallOverlayModel(lrcInstance_, this));
overlayModel_.reset(new CallOverlayModel(lrcInstance_, listener_, this));
QML_REGISTERSINGLETONTYPE_POBJECT(NS_MODELS, overlayModel_.get(), "CallOverlayModel");
accountId_ = lrcInstance_->get_currentAccountId();
......@@ -97,6 +98,65 @@ CallAdapter::CallAdapter(SystemTray* systemTray, LRCInstance* instance, QObject*
&LRCInstance::selectedConvUidChanged,
this,
&CallAdapter::saveConferenceSubcalls);
#ifdef HAVE_GLOBAL_PTT
connectPtt();
#endif
}
CallAdapter::~CallAdapter()
{
#ifdef HAVE_GLOBAL_PTT
disconnectPtt();
#endif
}
void
CallAdapter::connectPtt()
{
#ifdef HAVE_GLOBAL_PTT
if (listener_->getPttState()) {
QObject::connect(
listener_,
&PTTListener::pttKeyPressed,
this,
[this]() {
const auto callId
= lrcInstance_->getCallIdForConversationUid(lrcInstance_->get_selectedConvUid(),
accountId_);
try {
isMicrophoneMuted_ = isMuted(callId);
if (isMicrophoneMuted_)
muteAudioToggle();
} catch (const std::exception& e) {
qWarning() << e.what();
}
},
Qt::QueuedConnection);
QObject::connect(
listener_,
&PTTListener::pttKeyReleased,
this,
[this]() {
if (isMicrophoneMuted_) {
muteAudioToggle();
}
},
Qt::QueuedConnection);
}
#endif
}
void
CallAdapter::disconnectPtt()
{
#ifdef HAVE_GLOBAL_PTT
if (listener_->getPttState()) {
QObject::disconnect(listener_, &PTTListener::pttKeyPressed, this, nullptr);
QObject::disconnect(listener_, &PTTListener::pttKeyReleased, this, nullptr);
}
#endif
}
void
......@@ -172,6 +232,12 @@ CallAdapter::onCallStarted(const QString& callId)
// update call Information list by adding the new information related to the callId
callInformationListModel_->addElement(
qMakePair(callId, callModel->advancedInformationForCallId(callId)));
if (listener_->getPttState()){
#ifdef HAVE_GLOBAL_PTT
listener_->startListening();
toMute += callId;
#endif
}
}
void
......@@ -181,6 +247,10 @@ CallAdapter::onCallEnded(const QString& callId)
return;
// update call Information list by removing information related to the callId
callInformationListModel_->removeElement(callId);
#ifdef HAVE_GLOBAL_PTT
if (listener_->getPttState() && !hasCall_)
listener_->stopListening();
#endif
}
void
......@@ -271,6 +341,15 @@ CallAdapter::onCallStatusChanged(const QString& callId, int code)
}
}
void
CallAdapter::onCallInfosChanged(const QString& accountId, const QString& callId)
{
Q_UNUSED(accountId)
auto mute = toMute.remove(callId);
if (mute && listener_->getPttState())
muteAudioToggle();
}
void
CallAdapter::onCallAddedToConference(const QString& callId, const QString& confId)
{
......@@ -494,6 +573,12 @@ CallAdapter::connectCallModel(const QString& accountId)
QOverload<const QString&, int>::of(&CallAdapter::onCallStatusChanged),
Qt::UniqueConnection);
connect(accInfo.callModel.get(),
&CallModel::callInfosChanged,
this,
&CallAdapter::onCallInfosChanged,
Qt::UniqueConnection);
connect(accInfo.callModel.get(),
&CallModel::callAddedToConference,
this,
......@@ -816,6 +901,23 @@ CallAdapter::holdThisCallToggle()
}
}
bool
CallAdapter::isMuted(const QString& callId)
{
if (!(callId.isEmpty() || !lrcInstance_->getCurrentCallModel()->hasCall(callId))) {
auto* callModel = lrcInstance_->getCurrentCallModel();
if (callModel->hasCall(callId)) {
const auto callInfo = lrcInstance_->getCurrentCallModel()->getCall(callId);
auto mute = false;
for (const auto& m : callInfo.mediaList)
if (m[libjami::Media::MediaAttributeKey::LABEL] == "audio_0")
mute = m[libjami::Media::MediaAttributeKey::MUTED] == TRUE_STR;
return mute;
}
}
throw std::runtime_error("CallAdapter::isMuted: callId is empty or call does not exist");
}
void
CallAdapter::muteAudioToggle()
{
......@@ -825,13 +927,10 @@ CallAdapter::muteAudioToggle()
return;
}
auto* callModel = lrcInstance_->getCurrentCallModel();
if (callModel->hasCall(callId)) {
const auto callInfo = lrcInstance_->getCurrentCallModel()->getCall(callId);
auto mute = false;
for (const auto& m : callInfo.mediaList)
if (m[libjami::Media::MediaAttributeKey::LABEL] == "audio_0")
mute = m[libjami::Media::MediaAttributeKey::MUTED] == FALSE_STR;
callModel->muteMedia(callId, "audio_0", mute);
try {
callModel->muteMedia(callId, "audio_0", !isMuted(callId));
} catch (const std::exception& e) {
qWarning() << e.what();
}
}
......
......@@ -25,6 +25,10 @@
#include "screensaver.h"
#include "calloverlaymodel.h"
#ifdef HAVE_GLOBAL_PTT
#include "pttlistener.h"
#endif
#include <QObject>
#include <QString>
#include <QVariant>
......@@ -46,7 +50,7 @@ public:
Q_ENUM(MuteStates)
explicit CallAdapter(SystemTray* systemTray, LRCInstance* instance, QObject* parent = nullptr);
~CallAdapter() = default;
~CallAdapter();
public:
Q_INVOKABLE void startTimerInformation();
......@@ -76,6 +80,9 @@ public:
Q_INVOKABLE void holdThisCallToggle();
Q_INVOKABLE void recordThisCallToggle();
Q_INVOKABLE void muteAudioToggle();
Q_INVOKABLE bool isMuted(const QString& callId);
Q_INVOKABLE void connectPtt();
Q_INVOKABLE void disconnectPtt();
Q_INVOKABLE void muteCameraToggle();
Q_INVOKABLE bool isRecordingThisCall();
Q_INVOKABLE void muteParticipant(const QString& accountUri,
......@@ -109,6 +116,7 @@ public Q_SLOTS:
void onCallAddedToConference(const QString& callId, const QString& confId);
void onCallStarted(const QString& callId);
void onCallEnded(const QString& callId);
void onCallInfosChanged(const QString& accountId, const QString& callId);
private:
void showNotification(const QString& accountId, const QString& convUid);
......@@ -121,6 +129,9 @@ private:
SystemTray* systemTray_;
QScopedPointer<CallOverlayModel> overlayModel_;
VectorString currentConfSubcalls_;
std::unique_ptr<CallInformationListModel> callInformationListModel_;
PTTListener* listener_ = new PTTListener(systemTray_->getSettingsManager());
bool isMicrophoneMuted_ = true;
QSet<QString> toMute;
};
......@@ -22,6 +22,7 @@
#include <QEvent>
#include <QMouseEvent>
#include <QQuickWindow>
#include <QKeyEvent>
IndexRangeFilterProxyModel::IndexRangeFilterProxyModel(QAbstractListModel* parent)
: QSortFilterProxyModel(parent)
......@@ -74,10 +75,10 @@ PendingConferenceesListModel::data(const QModelIndex& index, int role) const
using namespace PendingConferences;
// WARNING: not swarm ready
lrc::api::call::Status callStatus;
QString pendingConferenceeCallId;
QString pendingConferenceeContactUri;
ContactModel* contactModel {nullptr};
lrc::api::call::Status callStatus;
try {
auto callModel = lrcInstance_->getCurrentCallModel();
auto currentPendingConferenceeInfo = callModel->getPendingConferencees().at(index.row());
......@@ -268,7 +269,7 @@ CallControlListModel::clearData()
data_.clear();
}
CallOverlayModel::CallOverlayModel(LRCInstance* instance, QObject* parent)
CallOverlayModel::CallOverlayModel(LRCInstance* instance, PTTListener* listener, QObject* parent)
: QObject(parent)
, lrcInstance_(instance)
, primaryModel_(new CallControlListModel(this))
......@@ -283,6 +284,10 @@ CallOverlayModel::CallOverlayModel(LRCInstance* instance, QObject* parent)
this,
&CallOverlayModel::setControlRanges);
overflowVisibleModel_->setFilterRole(CallControl::Role::UrgentCount);
#ifndef HAVE_GLOBAL_PTT
listener_ = listener;
#endif
}
void
......@@ -386,6 +391,19 @@ CallOverlayModel::eventFilter(QObject* object, QEvent* event)
}
}
}
#ifndef HAVE_GLOBAL_PTT
else if (event->type() == QEvent::KeyPress && listener_->getPttState()) {
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent->key() == listener_->getCurrentKey() && !keyEvent->isAutoRepeat()) {
Q_EMIT pttKeyPressed();
}
} else if (event->type() == QEvent::KeyRelease && listener_->getPttState()) {
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
if (keyEvent->key() == listener_->getCurrentKey() && !keyEvent->isAutoRepeat()) {
Q_EMIT pttKeyReleased();
}
}
#endif
return QObject::eventFilter(object, event);
}
......
......@@ -21,6 +21,9 @@
#include "lrcinstance.h"
#include "qtutils.h"
#include "mainapplication.h"
#include "pttlistener.h"
#include <QAbstractListModel>
#include <QObject>
......@@ -36,7 +39,7 @@
namespace CallControl {
Q_NAMESPACE
enum Role { ItemAction = Qt::UserRole + 1, UrgentCount, Enabled};
enum Role { ItemAction = Qt::UserRole + 1, UrgentCount, Enabled };
Q_ENUM_NS(Role)
struct Item
......@@ -121,7 +124,7 @@ class CallOverlayModel : public QObject
QML_PROPERTY(int, overflowIndex)
public:
CallOverlayModel(LRCInstance* instance, QObject* parent = nullptr);
CallOverlayModel(LRCInstance* instance, PTTListener* listener, QObject* parent = nullptr);
Q_INVOKABLE void addPrimaryControl(const QVariant& action, bool enabled);
Q_INVOKABLE void addSecondaryControl(const QVariant& action, bool enabled);
......@@ -142,6 +145,8 @@ public:
Q_SIGNALS:
void mouseMoved(QQuickItem* item);
void pttKeyPressed();
void pttKeyReleased();
private Q_SLOTS:
void setControlRanges();
......@@ -157,4 +162,8 @@ private:
PendingConferenceesListModel* pendingConferenceesModel_;
QList<QQuickItem*> watchedItems_;
#ifndef HAVE_GLOBAL_PTT
PTTListener* listener_ {nullptr};
#endif
};
/*
* Copyright (C) 2023 Savoir-faire Linux Inc.
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import net.jami.Models 1.1
import net.jami.Adapters 1.1
import net.jami.Constants 1.1
BaseModalDialog {
id: pttPage
property string bestName: ""
property string accountId: ""
property int pressedKey: Qt.Key_unknown
signal accepted
signal choiceMade(int chosenKey)
title: JamiStrings.changeShortcut
popupContent: ColumnLayout {
id: deleteAccountContentColumnLayout
anchors.centerIn: parent
spacing: JamiTheme.preferredMarginSize
Component.onCompleted: keyItem.forceActiveFocus()
Label {
id: instructionLabel
Layout.alignment: Qt.AlignCenter
Layout.preferredWidth: JamiTheme.preferredDialogWidth - 4*JamiTheme.preferredMarginSize
color: JamiTheme.textColor
text: JamiStrings.assignmentIndication
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.pointSize: JamiTheme.textFontSize
font.kerning: true
wrapMode: Text.Wrap
}
Label {
id: keyLabel
Layout.alignment: Qt.AlignCenter
color: JamiTheme.blackColor
wrapMode: Text.WordWrap
text: ""
font.pointSize: JamiTheme.settingsFontSize
font.kerning: true
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
background: Rectangle {
id: backgroundRect
anchors.centerIn: parent
width: keyLabel.width + 2 * JamiTheme.preferredMarginSize
height: keyLabel.height + JamiTheme.preferredMarginSize
color: JamiTheme.lightGrey_
border.color: JamiTheme.darkGreyColor
radius: 4
}
}
MaterialButton {
id: btnAssign
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: JamiTheme.preferredMarginSize
preferredWidth: JamiTheme.preferredFieldWidth / 2 - 8
buttontextHeightMargin: JamiTheme.buttontextHeightMargin
color: JamiTheme.buttonTintedBlack
hoveredColor: JamiTheme.buttonTintedBlackHovered
pressedColor: JamiTheme.buttonTintedBlackPressed
secondary: true
text: JamiStrings.assign
autoAccelerator: true
onClicked: {
if (!(pressedKey === Qt.Key_unknown)){
pttListener.setPttKey(pressedKey);
choiceMade(pressedKey);
}
close();
}
}
Item {
id: keyItem
Keys.onPressed: (event)=>{
keyLabel.text = pttListener.keyToString(event.key);
pressedKey = event.key;
}
}
}
}
......@@ -83,6 +83,15 @@ Item {
property string selectNewRingtone: qsTr("Select a new ringtone")
property string certificateFile: qsTr("Certificate File (*.crt)")
property string audioFile: qsTr("Audio File (*.wav *.ogg *.opus *.mp3 *.aiff *.wma)")
property string pushToTalk: qsTr("Push-to-talk")
property string enablePTT: qsTr("Enable push-to-talk")
property string keyboardShortcut: qsTr("Keyboard shortcut")
property string changeKeyboardShortcut: qsTr("Change keyboard shortcut")
// ChangePttKeyPopup
property string changeShortcut: qsTr("Change shortcut")
property string assignmentIndication: qsTr("Press the key to be assigned to push-to-talk shortcut")
property string assign: qsTr("Assign")
// AdvancedChatSettings
property string enableReadReceipts: qsTr("Enable read receipts")
......
......@@ -20,6 +20,7 @@
*/
#include "mainapplication.h"
#include "pttlistener.h"
#include "qmlregister.h"
#include "appsettingsmanager.h"
......@@ -132,6 +133,7 @@ MainApplication::init()
connectivityMonitor_.reset(new ConnectivityMonitor(this));
settingsManager_.reset(new AppSettingsManager(this));
systemTray_.reset(new SystemTray(settingsManager_.get(), this));
listener_ = new PTTListener(settingsManager_.get(), this);
QObject::connect(settingsManager_.get(),
&AppSettingsManager::retranslate,
......@@ -350,6 +352,7 @@ MainApplication::initQmlLayer()
auto videoProvider = new VideoProvider(lrcInstance_->avModel(), this);
engine_->rootContext()->setContextProperty("videoProvider", videoProvider);
engine_->rootContext()->setContextProperty("pttListener", listener_);
engine_->load(QUrl(QStringLiteral("qrc:/MainApplicationWindow.qml")));
qWarning().noquote() << "Main window loaded using" << getRenderInterfaceString();
......@@ -409,10 +412,9 @@ MainApplication::cleanup()
}
}
#ifdef Q_OS_MACOS
void
MainApplication::setEventFilter()
{
installEventFilter(this);
}
#endif
......@@ -23,6 +23,7 @@
#include "imagedownloader.h"
#include "lrcinstance.h"
#include "qtutils.h"
#include "pttlistener.h"
#include <QFile>
#include <QApplication>
......@@ -82,17 +83,20 @@ public:
return runOptions_[opt];
};
#ifdef Q_OS_MACOS
Q_INVOKABLE void setEventFilter();
bool eventFilter(QObject* object, QEvent* event)
{
#ifdef Q_OS_MACOS
if (event->type() == QEvent::ApplicationActivate) {
restoreApp();
}
#endif // Q_OS_MACOS
return QApplication::eventFilter(object, event);
}
#endif // Q_OS_MACOS
Q_SIGNALS:
void closeRequested();
......@@ -120,6 +124,8 @@ private:
QScopedPointer<SystemTray> systemTray_;
QScopedPointer<ImageDownloader> imageDownloader_;
PTTListener* listener_;
ScreenInfo screenInfo_;
bool isCleanupped;
......
......@@ -25,7 +25,6 @@ import "../../commoncomponents"
Item {
id: root
property alias chatViewContainer: ongoingCallPage.chatViewContainer
property alias contentView: callStackMainView
......@@ -49,6 +48,16 @@ Item {
}
}
Connections {
target: CallOverlayModel
function onPttKeyPressed() {
CallAdapter.muteAudioToggle();
}
function onPttKeyReleased() {
CallAdapter.muteAudioToggle();
}
}
// TODO: this should all be done by listening to
// parent visibility change or parent `Component.onDestruction`
function needToCloseInCallConversationAndPotentialWindow() {
......
/*
* Copyright (C) 2023 Savoir-faire Linux Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "pttlistener.h"
#include <QCoreApplication>
#include <QVariant>
class PTTListener::Impl : public QObject
{
Q_OBJECT
public:
Impl(PTTListener* parent)
: QObject(parent)
{}
~Impl() = default;
};
PTTListener::PTTListener(AppSettingsManager* settingsManager, QObject* parent)
: settingsManager_(settingsManager)
, QObject(parent)
, pimpl_(std::make_unique<Impl>(this))
{}
PTTListener::~PTTListener() = default;
#include "pttlistener.moc"
/*
* Copyright (C) 2023 Savoir-faire Linux Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <ApplicationServices/ApplicationServices.h>
#include <Carbon/Carbon.h>
#include "pttlistener.h"
#include <QCoreApplication>
#include <QVariant>
class PTTListener::Impl : public QObject
{
Q_OBJECT
public:
Impl(PTTListener* parent)
: QObject(parent)
{
qApp->setProperty("PTTListener", QVariant::fromValue(parent));
}
~Impl()
{
stopListening();
};
void startListening()
{
CGEventMask eventMask = (1 << kCGEventKeyDown) | (1 << kCGEventKeyUp);
CFMachPortRef eventTap = CGEventTapCreate(kCGHIDEventTap,
kCGHeadInsertEventTap,
kCGEventTapOptionDefault,
eventMask,
CGEventCallback,
this);
if (eventTap) {
CFRunLoopSourceRef runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault,
eventTap,
0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode);
CFRelease(runLoopSource);
CGEventTapEnable(eventTap, true);
} else {
qDebug() << "Impossible to create the keyboard tap.";
}
}
void stopListening()
{
if (eventTap) {
CGEventTapEnable(eventTap, false);
CFRelease(eventTap);
}
}
static CGEventRef CGEventCallback(CGEventTapProxy proxy,
CGEventType type,
CGEventRef event,
void* refcon)
{
auto* pThis = qApp->property("PTTListener").value<PTTListener*>();
CGKeyCode keycode = (CGKeyCode) CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);
if (pThis == nullptr) {
qWarning() << "PTTListener not found";
return {};
}
CGKeyCode pttKey = (CGKeyCode) pThis->pimpl_->qtKeyTokVKey(pThis->getCurrentKey());
static bool isKeyDown = false;
if (keycode == pttKey) {
if (type == kCGEventKeyDown && !isKeyDown) {
Q_EMIT pThis->pttKeyPressed();
isKeyDown = true;
} else if (type == kCGEventKeyUp && isKeyDown) {
Q_EMIT pThis->pttKeyReleased();
isKeyDown = false;
}
}
return event;
}
quint32 qtKeyTokVKey(Qt::Key key);
private:
CFMachPortRef eventTap;
};
PTTListener::PTTListener(AppSettingsManager* settingsManager, QObject* parent)
: settingsManager_(settingsManager)
, QObject(parent)
, pimpl_(std::make_unique<Impl>(this))
{}
PTTListener::~PTTListener() = default;
void
PTTListener::startListening()
{
pimpl_->startListening();
}
void
PTTListener::stopListening()
{
pimpl_->stopListening();
}
quint32
PTTListener::Impl::qtKeyTokVKey(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)
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<UCKeyStateRecordsIndex*>(
data + table[i].keyStateRecordsIndexOffset);
if (stateRec->keyStateRecordsIndexFormat != kUCKeyStateRecordsIndexFormat)
stateRec = 0;
}
UCKeyToCharTableIndex* charTable = reinterpret_cast<UCKeyToCharTableIndex*>(
data + table[i].keyToCharTableIndexOffset);
if (charTable->keyToCharTableIndexFormat != kUCKeyToCharTableIndexFormat)
continue;
for (quint32 j = 0; j < charTable->keyToCharTableCount; j++) {
UCKeyOutput* keyToChar = reinterpret_cast<UCKeyOutput*>(
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<UCKeyStateRecord*>(
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;
}
#include "pttlistener.moc"
/*
* Copyright (C) 2023 Savoir-faire Linux Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "pttlistener.h"
#include <QCoreApplication>
#include <QVariant>
#include <Windows.h>
class PTTListener::Impl : public QObject
{
Q_OBJECT
public:
Impl(PTTListener* parent)
: QObject(nullptr)
{
qApp->setProperty("PTTListener", QVariant::fromValue(parent));
}
~Impl()
{
stopListening();
};
void startListening()
{
keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, GlobalKeyboardProc, NULL, 0);
}
void stopListening()
{
UnhookWindowsHookEx(keyboardHook);
}
static LRESULT CALLBACK GlobalKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
auto* pThis = qApp->property("PTTListener").value<PTTListener*>();
if (pThis == nullptr) {
qWarning() << "PTTListener not found";
return {};
}
auto* keyboardHook = pThis->pimpl_->keyboardHook;
quint32 key = qtKeyToVKey(pThis->getCurrentKey());
static bool isKeyDown = false;
if (nCode == HC_ACTION) {
if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) {
KBDLLHOOKSTRUCT* keyInfo = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
if (keyInfo->vkCode == key && !isKeyDown) {
Q_EMIT pThis->pttKeyPressed();
isKeyDown = true;
}
} else if (wParam == WM_KEYUP || wParam == WM_SYSKEYUP) {
KBDLLHOOKSTRUCT* keyInfo = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
if (keyInfo->vkCode == key) {
Q_EMIT pThis->pttKeyReleased();
isKeyDown = false;
}
}
}
return CallNextHookEx(keyboardHook, nCode, wParam, lParam);
}
HHOOK keyboardHook;
static quint32 qtKeyToVKey(Qt::Key key);
};
PTTListener::PTTListener(AppSettingsManager* settingsManager, QObject* parent)
: settingsManager_(settingsManager)
, QObject(parent)
, pimpl_(std::make_unique<Impl>(this))
{}
PTTListener::~PTTListener() = default;
#ifdef HAVE_GLOBAL_PTT
void
PTTListener::startListening()
{
pimpl_->startListening();
}
void
PTTListener::stopListening()
{
pimpl_->stopListening();
}
#endif
quint32
PTTListener::Impl::qtKeyToVKey(Qt::Key key)
{
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;
case Qt::Key_0:
return VK_NUMPAD0;
case Qt::Key_1:
return VK_NUMPAD1;
case Qt::Key_2:
return VK_NUMPAD2;
case Qt::Key_3:
return VK_NUMPAD3;
case Qt::Key_4:
return VK_NUMPAD4;
case Qt::Key_5:
return VK_NUMPAD5;
case Qt::Key_6:
return VK_NUMPAD6;
case Qt::Key_7:
return VK_NUMPAD7;
case Qt::Key_8:
return VK_NUMPAD8;
case Qt::Key_9:
return VK_NUMPAD9;
case Qt::Key_A:
return 'A';
case Qt::Key_B:
return 'B';
case Qt::Key_C:
return 'C';
case Qt::Key_D:
return 'D';
case Qt::Key_E:
return 'E';
case Qt::Key_F:
return 'F';
case Qt::Key_G:
return 'G';
case Qt::Key_H:
return 'H';
case Qt::Key_I:
return 'I';
case Qt::Key_J:
return 'J';
case Qt::Key_K:
return 'K';
case Qt::Key_L:
return 'L';
case Qt::Key_M:
return 'M';
case Qt::Key_N:
return 'N';
case Qt::Key_O:
return 'O';
case Qt::Key_P:
return 'P';
case Qt::Key_Q:
return 'Q';
case Qt::Key_R:
return 'R';
case Qt::Key_S:
return 'S';
case Qt::Key_T:
return 'T';
case Qt::Key_U:
return 'U';
case Qt::Key_V:
return 'V';
case Qt::Key_W:
return 'W';
case Qt::Key_X:
return 'X';
case Qt::Key_Y:
return 'Y';
case Qt::Key_Z:
return 'Z';
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;
}
}
#include "pttlistener.moc"
\ No newline at end of file
/*
* Copyright (C) 2023 Savoir-faire Linux Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "pttlistener.h"
#include "appsettingsmanager.h"
#include "xcbkeyboard.h"
#include <QCoreApplication>
#include <QVariant>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <thread>
class PTTListener::Impl : public QObject
{
Q_OBJECT
public:
Impl(PTTListener* parent)
: QObject(nullptr)
, parent_(*parent)
, display_(XOpenDisplay(NULL))
, root_(DefaultRootWindow(display_))
{
thread_.reset(new QThread());
moveToThread(thread_.get());
}
~Impl()
{
stopListening();
XCloseDisplay(display_);
};
void startListening()
{
stop_.store(false);
connect(thread_.get(), &QThread::started, this, &Impl::processEvents);
thread_->start();
}
void stopListening()
{
stop_.store(true);
thread_->quit();
thread_->wait();
}
static const unsigned int* keyTbl_;
KeySym getKeySymFromQtKey(Qt::Key qtKey);
QString keySymToQString(KeySym ks);
KeySym qtKeyToXKeySym(Qt::Key key);
private Q_SLOTS:
void processEvents()
{
Window curFocus;
char buf[17];
KeySym ks;
XComposeStatus comp;
int len;
int revert;
static auto flags = KeyPressMask | KeyReleaseMask | FocusChangeMask;
XGetInputFocus(display_, &curFocus, &revert);
XSelectInput(display_, curFocus, flags);
bool pressed = false;
KeySym key = qtKeyToXKeySym(parent_.getCurrentKey());
while (!stop_.load()) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
while (XPending(display_)) {
XEvent ev;
XNextEvent(display_, &ev);
XLookupString(&ev.xkey, buf, 16, &ks, &comp);
switch (ev.type) {
case FocusOut:
if (curFocus != root_)
XSelectInput(display_, curFocus, 0);
XGetInputFocus(display_, &curFocus, &revert);
if (curFocus == PointerRoot)
curFocus = root_;
XSelectInput(display_, curFocus, flags);
break;
case KeyPress: {
if (!(pressed) && ks == key) {
Q_EMIT parent_.pttKeyPressed();
pressed = true;
}
break;
}
case KeyRelease:
bool is_retriggered = false;
if (XEventsQueued(display_, QueuedAfterReading)) {
XEvent nev;
XPeekEvent(display_, &nev);
if (nev.type == KeyPress && nev.xkey.time == ev.xkey.time
&& nev.xkey.keycode == ev.xkey.keycode) {
is_retriggered = true;
}
}
if (!is_retriggered && ks == key) {
Q_EMIT parent_.pttKeyReleased();
pressed = false;
}
break;
}
}
}
}
private:
PTTListener& parent_;
Display* display_;
Window root_;
QScopedPointer<QThread> thread_;
std::atomic_bool stop_ {false};
};
QString
PTTListener::Impl::keySymToQString(KeySym ks)
{
return QString::fromUtf8(XKeysymToString(ks));
}
KeySym
PTTListener::Impl::qtKeyToXKeySym(Qt::Key key)
{
const auto keySym = getKeySymFromQtKey(key);
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<ushort>(key);
}
PTTListener::PTTListener(AppSettingsManager* settingsManager, QObject* parent)
: settingsManager_(settingsManager)
, QObject(parent)
, pimpl_(std::make_unique<Impl>(this))
{}
PTTListener::~PTTListener() = default;
void
PTTListener::startListening()
{
pimpl_->startListening();
}
void
PTTListener::stopListening()
{
pimpl_->stopListening();
}
KeySym
PTTListener::Impl::getKeySymFromQtKey(Qt::Key qtKey)
{
QString keyString = QKeySequence(qtKey).toString().toLower();
KeySym keySym = XStringToKeysym(keyString.toUtf8().data());
return keySym;
}
const unsigned int* PTTListener::Impl::keyTbl_ = keyTable;
#include "pttlistener.moc"
#include "pttlistener.h"
/****************************************************************************
**
** 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 <Qt>
#include <X11/keysym.h>
#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 keyTable[] = {
// 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_F2,
Qt::Key_F2,
XK_F3,
Qt::Key_F3,
XK_F4,
Qt::Key_F4,
XK_F5,
Qt::Key_F5,
XK_F6,
Qt::Key_F6,
XK_F7,
Qt::Key_F7,
XK_F8,
Qt::Key_F8,
XK_F9,
Qt::Key_F9,
XK_F10,
Qt::Key_F10,
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};
#pragma once
#include "systemtray.h"
#include "appsettingsmanager.h"
#include <QObject>
#include <QThread>
#include <QKeyEvent>
class PTTListener : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE Qt::Key getCurrentKey()
{
int keyInt = settingsManager_->getValue(Settings::Key::pttKey).toInt();
Qt::Key key = static_cast<Qt::Key>(keyInt);
return key;
}
Q_INVOKABLE QString keyToString(Qt::Key key)
{
return QKeySequence(key).toString();
}
Q_INVOKABLE void setPttKey(Qt::Key key)
{
settingsManager_->setValue(Settings::Key::pttKey, key);
}
Q_INVOKABLE bool getPttState()
{
return settingsManager_->getValue(Settings::Key::EnablePtt).toBool();
}
PTTListener(AppSettingsManager* settingsManager, QObject* parent = nullptr);
~PTTListener();
Q_SIGNALS:
void pttKeyPressed();
void pttKeyReleased();
#ifdef HAVE_GLOBAL_PTT
public Q_SLOTS:
void startListening();
void stopListening();
#endif
private:
class Impl;
std::unique_ptr<Impl> pimpl_;
AppSettingsManager* settingsManager_;
};
......@@ -28,11 +28,13 @@ import "../../commoncomponents"
import "../../mainview/components"
import "../../mainview/js/contactpickercreation.js" as ContactPickerCreation
SettingsPageBase {
id: root
property bool isSIP: CurrentAccount.type === Profile.Type.SIP
property int itemWidth: 132
property string key: pttListener.keyToString(pttListener.getCurrentKey())
title: JamiStrings.callSettingsTitle
function updateAndShowModeratorsSlot() {
......@@ -374,5 +376,72 @@ SettingsPageBase {
}
}
}
ColumnLayout{
width: parent.width
spacing: 9
Text {
text: JamiStrings.pushToTalk
color: JamiTheme.textColor
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
wrapMode: Text.WordWrap
font.pixelSize: JamiTheme.settingsTitlePixelSize
font.kerning: true
}
ToggleSwitch {
id: pttToggle
labelText: JamiStrings.enablePTT
checked: UtilsAdapter.getAppValue(Settings.EnablePtt)
onSwitchToggled: {
UtilsAdapter.setAppValue(Settings.Key.EnablePtt, checked)
}
}
RowLayout {
visible: pttToggle.checked
Layout.preferredWidth: parent.width
Label {
color: JamiTheme.textColor
wrapMode: Text.WordWrap
text: JamiStrings.keyboardShortcut
font.pointSize: JamiTheme.settingsFontSize
font.kerning: true
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
}
Label {
id: keyLabel
color: JamiTheme.blackColor
wrapMode: Text.WordWrap
text: key
font.pointSize: JamiTheme.settingsFontSize
font.kerning: true
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
background: Rectangle {
id: backgroundRect
anchors.centerIn: parent
width: keyLabel.width + 2 * JamiTheme.preferredMarginSize
height: keyLabel.height + JamiTheme.preferredMarginSize
color: JamiTheme.lightGrey_
border.color: JamiTheme.darkGreyColor
radius: 4
}
}
MaterialButton {
Layout.alignment: Qt.AlignRight
buttontextHeightMargin: JamiTheme.buttontextHeightMargin
primary: true
toolTipText: JamiStrings.changeKeyboardShortcut
text: JamiStrings.change
onClicked: {
var dlg = viewCoordinator.presentDialog(appWindow, "commoncomponents/ChangePttKeyPopup.qml");
dlg.choiceMade.connect(function (chosenKey) {
keyLabel.text = pttListener.keyToString(chosenKey);
});
}
}
}
}
}
}
......@@ -37,6 +37,11 @@ public:
explicit SystemTray(AppSettingsManager* settingsManager, QObject* parent = nullptr);
~SystemTray();
AppSettingsManager* getSettingsManager()
{
return settingsManager_;
}
void onNotificationCountChanged(int count);
#ifdef Q_OS_LINUX
bool hideNotification(const QString& id);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment