mirror of https://github.com/cutefishos/core
feat(core): migrate services to Wayland
parent
62b998573b
commit
0926373b14
@ -1,26 +0,0 @@
|
||||
find_package(Qt6 COMPONENTS Core Widgets DBus Gui REQUIRED)
|
||||
find_package(XCB MODULE REQUIRED COMPONENTS XCB KEYSYMS)
|
||||
find_package(X11)
|
||||
|
||||
set(PROJECT_SOURCES
|
||||
main.cpp
|
||||
application.cpp
|
||||
hotkeys.cpp
|
||||
)
|
||||
|
||||
add_executable(chotkeys
|
||||
${PROJECT_SOURCES}
|
||||
)
|
||||
|
||||
target_link_libraries(chotkeys
|
||||
PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::Widgets
|
||||
Qt6::DBus
|
||||
Qt6::Gui
|
||||
${XCB_LIBS}
|
||||
${X11_LIBRARIES}
|
||||
XCB::KEYSYMS
|
||||
)
|
||||
|
||||
install(TARGETS chotkeys RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2021 CutefishOS Team.
|
||||
*
|
||||
* Author: Reion Wong <aj@cutefishos.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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* 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 "application.h"
|
||||
#include "hotkeys.h"
|
||||
|
||||
#include <QProcess>
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusInterface>
|
||||
#include <QDebug>
|
||||
|
||||
Application::Application(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_hotKeys(new Hotkeys)
|
||||
{
|
||||
setupShortcuts();
|
||||
|
||||
connect(m_hotKeys, &Hotkeys::pressed, this, &Application::onPressed);
|
||||
connect(m_hotKeys, &Hotkeys::released, this, &Application::onReleased);
|
||||
}
|
||||
|
||||
void Application::setupShortcuts()
|
||||
{
|
||||
m_hotKeys->registerKey(QKeySequence(QKeyCombination(Qt::CTRL | Qt::ALT, Qt::Key_Delete)));
|
||||
m_hotKeys->registerKey(QKeySequence(QKeyCombination(Qt::CTRL | Qt::ALT, Qt::Key_A)));
|
||||
m_hotKeys->registerKey(QKeySequence(QKeyCombination(Qt::META, Qt::Key_L)));
|
||||
//m_hotKeys->registerKey(QKeySequence(Qt::META + Qt::Key_6));
|
||||
m_hotKeys->registerKey(647);
|
||||
}
|
||||
|
||||
void Application::onPressed(QKeySequence keySeq)
|
||||
{
|
||||
|
||||
if (keySeq.toString() == "Ctrl+Alt+Del") {
|
||||
QProcess::startDetached("cutefish-shutdown", QStringList());
|
||||
}
|
||||
|
||||
if (keySeq.toString() == "Meta+L") {
|
||||
QProcess::startDetached("cutefish-screenlocker", QStringList());
|
||||
}
|
||||
|
||||
if (keySeq.toString() == "Ctrl+Alt+A") {
|
||||
QProcess::startDetached("cutefish-screenshot", QStringList());
|
||||
}
|
||||
|
||||
if (keySeq.toString() == "Ʇ") {
|
||||
// The launcher is a window of cutefish-shell, not a program to run.
|
||||
QDBusInterface("com.cutefish.Launcher", "/Launcher",
|
||||
"com.cutefish.Launcher",
|
||||
QDBusConnection::sessionBus()).asyncCall("toggle");
|
||||
}
|
||||
}
|
||||
|
||||
void Application::onReleased(QKeySequence keySeq)
|
||||
{
|
||||
Q_UNUSED(keySeq);
|
||||
}
|
||||
@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2021 CutefishOS Team.
|
||||
*
|
||||
* Author: Reion Wong <aj@cutefishos.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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef APPLICATION_H
|
||||
#define APPLICATION_H
|
||||
|
||||
#include <QObject>
|
||||
#include "hotkeys.h"
|
||||
|
||||
class Application : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit Application(QObject *parent = nullptr);
|
||||
|
||||
private:
|
||||
void setupShortcuts();
|
||||
|
||||
private slots:
|
||||
void onPressed(QKeySequence keySeq);
|
||||
void onReleased(QKeySequence keySeq);
|
||||
|
||||
private:
|
||||
Hotkeys *m_hotKeys;
|
||||
};
|
||||
|
||||
#endif // APPLICATION_H
|
||||
@ -1,296 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2021 CutefishOS Team.
|
||||
*
|
||||
* Author: Reion Wong <aj@cutefishos.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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* 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 "hotkeys.h"
|
||||
#include "x11utils.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QKeySequence>
|
||||
#include <QTimer>
|
||||
#include <QDebug>
|
||||
|
||||
// #include <KKeyServer>
|
||||
// #include <NETWM>
|
||||
|
||||
// XCB & X11
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/keysym.h>
|
||||
#include <xcb/xcb_keysyms.h>
|
||||
|
||||
#include <X11/XKBlib.h>
|
||||
|
||||
Hotkeys::Hotkeys(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
qApp->installNativeEventFilter(this);
|
||||
}
|
||||
|
||||
Hotkeys::~Hotkeys()
|
||||
{
|
||||
qApp->removeNativeEventFilter(this);
|
||||
}
|
||||
|
||||
bool Hotkeys::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result)
|
||||
{
|
||||
Q_UNUSED(result);
|
||||
|
||||
if (eventType != "xcb_generic_event_t") {
|
||||
return false;
|
||||
}
|
||||
|
||||
xcb_generic_event_t *e = static_cast<xcb_generic_event_t *>(message);
|
||||
|
||||
if (e->response_type == XCB_KEY_PRESS) {
|
||||
xcb_key_press_event_t *keyEvent = static_cast<xcb_key_press_event_t *>(message);
|
||||
quint32 keycode = keyEvent->detail;
|
||||
quint32 mods = keyEvent->state; // & (ShiftMask | ControlMask | Mod1Mask | Mod3Mask);
|
||||
quint32 id = keycode | mods;
|
||||
|
||||
// int keyQt;
|
||||
// KKeyServer::xcbKeyPressEventToQt(keyEvent, &keyQt);
|
||||
|
||||
// bool found = false;
|
||||
// for (QKeySequence &seq : m_shortcuts.values()) {
|
||||
// if (seq == QKeySequence(keyQt)) {
|
||||
// found = true;
|
||||
// // emit pressed(seq);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (!found && m_shortcuts.contains(id)) {
|
||||
// // emit pressed(m_shortcuts[id]);
|
||||
// }
|
||||
|
||||
// // Keyboard needs to be ungrabed after XGrabKey() activates the grab,
|
||||
// // otherwise it becomes frozen.
|
||||
// xcb_void_cookie_t cookie = xcb_ungrab_keyboard_checked(c, XCB_TIME_CURRENT_TIME);
|
||||
// xcb_flush(c);
|
||||
|
||||
// // xcb_flush() only makes sure that the ungrab keyboard request has been
|
||||
// // sent, but is not enough to make sure that request has been fulfilled. Use
|
||||
// // xcb_request_check() to make sure that the request has been processed.
|
||||
// xcb_request_check(c, cookie);
|
||||
|
||||
// int keyQt;
|
||||
// if (!KKeyServer::xcbKeyPressEventToQt(keyEvent, &keyQt)) {
|
||||
// qDebug() << "KKeyServer::xcbKeyPressEventToQt failed";
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// // All that work for this hey... argh...
|
||||
// bool found = false;
|
||||
// for (QKeySequence &seq : m_shortcuts.values()) {
|
||||
// if (seq == QKeySequence(keyQt)) {
|
||||
// found = true;
|
||||
// emit pressed(seq);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
if (m_shortcuts.contains(id)) {
|
||||
emit pressed(m_shortcuts[id]);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} else if (e->response_type == XCB_KEY_RELEASE) {
|
||||
xcb_key_release_event_t *keyEvent = static_cast<xcb_key_release_event_t *>(message);
|
||||
quint32 keycode = keyEvent->detail;
|
||||
quint32 mods = keyEvent->state; // & (ShiftMask | ControlMask | Mod1Mask | Mod3Mask);
|
||||
quint32 id = keycode | mods;
|
||||
|
||||
// META
|
||||
if (id == 197) {
|
||||
id = 133;
|
||||
}
|
||||
|
||||
if (m_shortcuts.contains(id)) {
|
||||
emit released(m_shortcuts[id]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Hotkeys::registerKey(QKeySequence keySequence)
|
||||
{
|
||||
if (keySequence.isEmpty())
|
||||
return;
|
||||
|
||||
quint32 keycode = nativeKeycode(getKey(keySequence));
|
||||
quint32 mods = nativeModifiers(getMods(keySequence));
|
||||
quint32 keyId = keycode | mods;
|
||||
|
||||
// META
|
||||
if (keycode == 204 && mods == 0) {
|
||||
keycode = 133;
|
||||
keyId = keycode | mods;
|
||||
}
|
||||
|
||||
if (!m_shortcuts.contains(keyId)) {
|
||||
registerKey(keycode, mods);
|
||||
m_shortcuts.insert(keyId, keySequence);
|
||||
}
|
||||
}
|
||||
|
||||
void Hotkeys::registerKey(quint32 keycode)
|
||||
{
|
||||
if (!m_shortcuts.contains(keycode)) {
|
||||
registerKey(keycode, 0);
|
||||
m_shortcuts.insert(keycode, QKeySequence(keycode | 0));
|
||||
}
|
||||
}
|
||||
|
||||
void Hotkeys::registerKey(quint32 key, quint32 mods)
|
||||
{
|
||||
xcb_grab_key(qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->connection(),
|
||||
1,
|
||||
Cutefish::X11::rootWindow(),
|
||||
mods,
|
||||
key,
|
||||
XCB_GRAB_MODE_ASYNC,
|
||||
XCB_GRAB_MODE_ASYNC);
|
||||
|
||||
xcb_grab_key(qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->connection(),
|
||||
1,
|
||||
Cutefish::X11::rootWindow(),
|
||||
mods | XCB_MOD_MASK_2,
|
||||
key,
|
||||
XCB_GRAB_MODE_ASYNC,
|
||||
XCB_GRAB_MODE_ASYNC);
|
||||
}
|
||||
|
||||
void Hotkeys::unregisterKey(quint32 key, quint32 mods)
|
||||
{
|
||||
xcb_ungrab_key(qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->connection(), key, Cutefish::X11::rootWindow(), mods);
|
||||
}
|
||||
|
||||
quint32 Hotkeys::nativeKeycode(Qt::Key k)
|
||||
{
|
||||
/* keysymdef.h */
|
||||
quint32 key = 0;
|
||||
if (k >= Qt::Key_F1 && k <= Qt::Key_F35) {
|
||||
key = XK_F1 + (k - Qt::Key_F1);
|
||||
} else if (k >= Qt::Key_Space && k <= Qt::Key_QuoteLeft) {
|
||||
key = k;
|
||||
} else if (k >= Qt::Key_BraceLeft && k <= Qt::Key_AsciiTilde) {
|
||||
key = k;
|
||||
} else if (k >= Qt::Key_nobreakspace && k <= Qt::Key_ydiaeresis) {
|
||||
key = k;
|
||||
} else {
|
||||
switch (k) {
|
||||
case Qt::Key_Escape:
|
||||
key = XK_Escape;
|
||||
break;
|
||||
case Qt::Key_Tab:
|
||||
case Qt::Key_Backtab:
|
||||
key = XK_Tab;
|
||||
break;
|
||||
case Qt::Key_Backspace:
|
||||
key = XK_BackSpace;
|
||||
break;
|
||||
case Qt::Key_Return:
|
||||
case Qt::Key_Enter:
|
||||
key = XK_Return;
|
||||
break;
|
||||
case Qt::Key_Insert:
|
||||
key = XK_Insert;
|
||||
break;
|
||||
case Qt::Key_Delete:
|
||||
key = XK_Delete;
|
||||
break;
|
||||
case Qt::Key_Pause:
|
||||
key = XK_Pause;
|
||||
break;
|
||||
case Qt::Key_Print:
|
||||
key = XK_Print;
|
||||
break;
|
||||
case Qt::Key_SysReq:
|
||||
key = XK_Sys_Req;
|
||||
break;
|
||||
case Qt::Key_Clear:
|
||||
key = XK_Clear;
|
||||
break;
|
||||
case Qt::Key_Home:
|
||||
key = XK_Home;
|
||||
break;
|
||||
case Qt::Key_End:
|
||||
key = XK_End;
|
||||
break;
|
||||
case Qt::Key_Left:
|
||||
key = XK_Left;
|
||||
break;
|
||||
case Qt::Key_Up:
|
||||
key = XK_Up;
|
||||
break;
|
||||
case Qt::Key_Right:
|
||||
key = XK_Right;
|
||||
break;
|
||||
case Qt::Key_Down:
|
||||
key = XK_Down;
|
||||
break;
|
||||
case Qt::Key_PageUp:
|
||||
key = XK_Page_Up;
|
||||
break;
|
||||
case Qt::Key_PageDown:
|
||||
key = XK_Page_Down;
|
||||
break;
|
||||
default:
|
||||
key = 0;
|
||||
}
|
||||
}
|
||||
return XKeysymToKeycode(qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->display(), key);
|
||||
}
|
||||
|
||||
quint32 Hotkeys::nativeModifiers(Qt::KeyboardModifiers m)
|
||||
{
|
||||
quint32 mods = Qt::NoModifier;
|
||||
|
||||
if (m & Qt::ShiftModifier)
|
||||
mods |= ShiftMask;
|
||||
if (m & Qt::ControlModifier)
|
||||
mods |= ControlMask;
|
||||
if (m & Qt::AltModifier)
|
||||
mods |= Mod1Mask;
|
||||
if (m & Qt::MetaModifier)
|
||||
mods |= Mod4Mask;
|
||||
|
||||
return mods;
|
||||
}
|
||||
|
||||
Qt::Key Hotkeys::getKey(const QKeySequence &keyseq)
|
||||
{
|
||||
if (keyseq.isEmpty()) {
|
||||
return Qt::Key(0);
|
||||
}
|
||||
|
||||
return Qt::Key(keyseq[0] & ~Qt::KeyboardModifierMask);
|
||||
}
|
||||
|
||||
Qt::KeyboardModifiers Hotkeys::getMods(const QKeySequence &keyseq)
|
||||
{
|
||||
if (keyseq.isEmpty()) {
|
||||
return Qt::KeyboardModifiers();
|
||||
}
|
||||
|
||||
return Qt::KeyboardModifiers(keyseq[0] & Qt::KeyboardModifierMask);
|
||||
}
|
||||
@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2021 CutefishOS Team.
|
||||
*
|
||||
* Author: Reion Wong <aj@cutefishos.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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef HOTKEYS_H
|
||||
#define HOTKEYS_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QKeySequence>
|
||||
#include <QAbstractNativeEventFilter>
|
||||
#include <QHash>
|
||||
#include <QTimer>
|
||||
#include <QSet>
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
class Hotkeys : public QObject, public QAbstractNativeEventFilter
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit Hotkeys(QObject *parent = nullptr);
|
||||
~Hotkeys();
|
||||
|
||||
bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override;
|
||||
|
||||
void registerKey(QKeySequence keySequence);
|
||||
void registerKey(quint32 keycode);
|
||||
|
||||
void registerKey(quint32 key, quint32 mods);
|
||||
void unregisterKey(quint32 key, quint32 mods);
|
||||
|
||||
|
||||
signals:
|
||||
void pressed(QKeySequence keySeq);
|
||||
void released(QKeySequence keySeq);
|
||||
|
||||
private:
|
||||
quint32 nativeKeycode(Qt::Key k);
|
||||
quint32 nativeModifiers(Qt::KeyboardModifiers m);
|
||||
Qt::Key getKey(const QKeySequence& keyseq);
|
||||
Qt::KeyboardModifiers getMods(const QKeySequence& keyseq);
|
||||
|
||||
private:
|
||||
QHash<quint32, QKeySequence> m_shortcuts;
|
||||
};
|
||||
|
||||
#endif // HOTKEYS_H
|
||||
@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2021 CutefishOS Team.
|
||||
*
|
||||
* Author: Reion Wong <aj@cutefishos.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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* 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 <QApplication>
|
||||
#include <QDBusConnection>
|
||||
#include "application.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
a.setQuitOnLastWindowClosed(true);
|
||||
|
||||
if (!QDBusConnection::sessionBus().registerService("com.cutefish.Chotkeys")) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!QDBusConnection::sessionBus().registerObject("/Chotkeys", &a)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Application app;
|
||||
return a.exec();
|
||||
}
|
||||
@ -1,297 +0,0 @@
|
||||
#.rst:
|
||||
# ECMFindModuleHelpers
|
||||
# --------------------
|
||||
#
|
||||
# Helper macros for find modules: ecm_find_package_version_check(),
|
||||
# ecm_find_package_parse_components() and
|
||||
# ecm_find_package_handle_library_components().
|
||||
#
|
||||
# ::
|
||||
#
|
||||
# ecm_find_package_version_check(<name>)
|
||||
#
|
||||
# Prints warnings if the CMake version or the project's required CMake version
|
||||
# is older than that required by extra-cmake-modules.
|
||||
#
|
||||
# ::
|
||||
#
|
||||
# ecm_find_package_parse_components(<name>
|
||||
# RESULT_VAR <variable>
|
||||
# KNOWN_COMPONENTS <component1> [<component2> [...]]
|
||||
# [SKIP_DEPENDENCY_HANDLING])
|
||||
#
|
||||
# This macro will populate <variable> with a list of components found in
|
||||
# <name>_FIND_COMPONENTS, after checking that all those components are in the
|
||||
# list of KNOWN_COMPONENTS; if there are any unknown components, it will print
|
||||
# an error or warning (depending on the value of <name>_FIND_REQUIRED) and call
|
||||
# return().
|
||||
#
|
||||
# The order of components in <variable> is guaranteed to match the order they
|
||||
# are listed in the KNOWN_COMPONENTS argument.
|
||||
#
|
||||
# If SKIP_DEPENDENCY_HANDLING is not set, for each component the variable
|
||||
# <name>_<component>_component_deps will be checked for dependent components.
|
||||
# If <component> is listed in <name>_FIND_COMPONENTS, then all its (transitive)
|
||||
# dependencies will also be added to <variable>.
|
||||
#
|
||||
# ::
|
||||
#
|
||||
# ecm_find_package_handle_library_components(<name>
|
||||
# COMPONENTS <component> [<component> [...]]
|
||||
# [SKIP_DEPENDENCY_HANDLING])
|
||||
# [SKIP_PKG_CONFIG])
|
||||
#
|
||||
# Creates an imported library target for each component. The operation of this
|
||||
# macro depends on the presence of a number of CMake variables.
|
||||
#
|
||||
# The <name>_<component>_lib variable should contain the name of this library,
|
||||
# and <name>_<component>_header variable should contain the name of a header
|
||||
# file associated with it (whatever relative path is normally passed to
|
||||
# '#include'). <name>_<component>_header_subdir variable can be used to specify
|
||||
# which subdirectory of the include path the headers will be found in.
|
||||
# ecm_find_package_components() will then search for the library
|
||||
# and include directory (creating appropriate cache variables) and create an
|
||||
# imported library target named <name>::<component>.
|
||||
#
|
||||
# Additional variables can be used to provide additional information:
|
||||
#
|
||||
# If SKIP_PKG_CONFIG, the <name>_<component>_pkg_config variable is set, and
|
||||
# pkg-config is found, the pkg-config module given by
|
||||
# <name>_<component>_pkg_config will be searched for and used to help locate the
|
||||
# library and header file. It will also be used to set
|
||||
# <name>_<component>_VERSION.
|
||||
#
|
||||
# Note that if version information is found via pkg-config,
|
||||
# <name>_<component>_FIND_VERSION can be set to require a particular version
|
||||
# for each component.
|
||||
#
|
||||
# If SKIP_DEPENDENCY_HANDLING is not set, the INTERFACE_LINK_LIBRARIES property
|
||||
# of the imported target for <component> will be set to contain the imported
|
||||
# targets for the components listed in <name>_<component>_component_deps.
|
||||
# <component>_FOUND will also be set to false if any of the compoments in
|
||||
# <name>_<component>_component_deps are not found. This requires the components
|
||||
# in <name>_<component>_component_deps to be listed before <component> in the
|
||||
# COMPONENTS argument.
|
||||
#
|
||||
# The following variables will be set:
|
||||
#
|
||||
# ``<name>_TARGETS``
|
||||
# the imported targets
|
||||
# ``<name>_LIBRARIES``
|
||||
# the found libraries
|
||||
# ``<name>_INCLUDE_DIRS``
|
||||
# the combined required include directories for the components
|
||||
# ``<name>_DEFINITIONS``
|
||||
# the "other" CFLAGS provided by pkg-config, if any
|
||||
# ``<name>_VERSION``
|
||||
# the value of ``<name>_<component>_VERSION`` for the first component that
|
||||
# has this variable set (note that components are searched for in the order
|
||||
# they are passed to the macro), although if it is already set, it will not
|
||||
# be altered
|
||||
#
|
||||
# Note that these variables are never cleared, so if
|
||||
# ecm_find_package_handle_library_components() is called multiple times with
|
||||
# different components (typically because of multiple find_package() calls) then
|
||||
# ``<name>_TARGETS``, for example, will contain all the targets found in any
|
||||
# call (although no duplicates).
|
||||
#
|
||||
# Since pre-1.0.0.
|
||||
|
||||
#=============================================================================
|
||||
# Copyright 2014 Alex Merry <alex.merry@kde.org>
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. The name of the author may not be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
# IN NO EVENT SHALL THE AUTHOR 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(CMakeParseArguments)
|
||||
|
||||
macro(ecm_find_package_version_check module_name)
|
||||
if(CMAKE_VERSION VERSION_LESS 2.8.12)
|
||||
message(FATAL_ERROR "CMake 2.8.12 is required by Find${module_name}.cmake")
|
||||
endif()
|
||||
if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.12)
|
||||
message(AUTHOR_WARNING "Your project should require at least CMake 2.8.12 to use Find${module_name}.cmake")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
macro(ecm_find_package_parse_components module_name)
|
||||
set(ecm_fppc_options SKIP_DEPENDENCY_HANDLING)
|
||||
set(ecm_fppc_oneValueArgs RESULT_VAR)
|
||||
set(ecm_fppc_multiValueArgs KNOWN_COMPONENTS DEFAULT_COMPONENTS)
|
||||
cmake_parse_arguments(ECM_FPPC "${ecm_fppc_options}" "${ecm_fppc_oneValueArgs}" "${ecm_fppc_multiValueArgs}" ${ARGN})
|
||||
|
||||
if(ECM_FPPC_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unexpected arguments to ecm_find_package_parse_components: ${ECM_FPPC_UNPARSED_ARGUMENTS}")
|
||||
endif()
|
||||
if(NOT ECM_FPPC_RESULT_VAR)
|
||||
message(FATAL_ERROR "Missing RESULT_VAR argument to ecm_find_package_parse_components")
|
||||
endif()
|
||||
if(NOT ECM_FPPC_KNOWN_COMPONENTS)
|
||||
message(FATAL_ERROR "Missing KNOWN_COMPONENTS argument to ecm_find_package_parse_components")
|
||||
endif()
|
||||
if(NOT ECM_FPPC_DEFAULT_COMPONENTS)
|
||||
set(ECM_FPPC_DEFAULT_COMPONENTS ${ECM_FPPC_KNOWN_COMPONENTS})
|
||||
endif()
|
||||
|
||||
if(${module_name}_FIND_COMPONENTS)
|
||||
set(ecm_fppc_requestedComps ${${module_name}_FIND_COMPONENTS})
|
||||
|
||||
if(NOT ECM_FPPC_SKIP_DEPENDENCY_HANDLING)
|
||||
# Make sure deps are included
|
||||
foreach(ecm_fppc_comp ${ecm_fppc_requestedComps})
|
||||
foreach(ecm_fppc_dep_comp ${${module_name}_${ecm_fppc_comp}_component_deps})
|
||||
list(FIND ecm_fppc_requestedComps "${ecm_fppc_dep_comp}" ecm_fppc_index)
|
||||
if("${ecm_fppc_index}" STREQUAL "-1")
|
||||
if(NOT ${module_name}_FIND_QUIETLY)
|
||||
message(STATUS "${module_name}: ${ecm_fppc_comp} requires ${${module_name}_${ecm_fppc_comp}_component_deps}")
|
||||
endif()
|
||||
list(APPEND ecm_fppc_requestedComps "${ecm_fppc_dep_comp}")
|
||||
endif()
|
||||
endforeach()
|
||||
endforeach()
|
||||
else()
|
||||
message(STATUS "Skipping dependency handling for ${module_name}")
|
||||
endif()
|
||||
list(REMOVE_DUPLICATES ecm_fppc_requestedComps)
|
||||
|
||||
# This makes sure components are listed in the same order as
|
||||
# KNOWN_COMPONENTS (potentially important for inter-dependencies)
|
||||
set(${ECM_FPPC_RESULT_VAR})
|
||||
foreach(ecm_fppc_comp ${ECM_FPPC_KNOWN_COMPONENTS})
|
||||
list(FIND ecm_fppc_requestedComps "${ecm_fppc_comp}" ecm_fppc_index)
|
||||
if(NOT "${ecm_fppc_index}" STREQUAL "-1")
|
||||
list(APPEND ${ECM_FPPC_RESULT_VAR} "${ecm_fppc_comp}")
|
||||
list(REMOVE_AT ecm_fppc_requestedComps ${ecm_fppc_index})
|
||||
endif()
|
||||
endforeach()
|
||||
# if there are any left, they are unknown components
|
||||
if(ecm_fppc_requestedComps)
|
||||
set(ecm_fppc_msgType STATUS)
|
||||
if(${module_name}_FIND_REQUIRED)
|
||||
set(ecm_fppc_msgType FATAL_ERROR)
|
||||
endif()
|
||||
if(NOT ${module_name}_FIND_QUIETLY)
|
||||
message(${ecm_fppc_msgType} "${module_name}: requested unknown components ${ecm_fppc_requestedComps}")
|
||||
endif()
|
||||
return()
|
||||
endif()
|
||||
else()
|
||||
set(${ECM_FPPC_RESULT_VAR} ${ECM_FPPC_DEFAULT_COMPONENTS})
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
macro(ecm_find_package_handle_library_components module_name)
|
||||
set(ecm_fpwc_options SKIP_PKG_CONFIG SKIP_DEPENDENCY_HANDLING)
|
||||
set(ecm_fpwc_oneValueArgs)
|
||||
set(ecm_fpwc_multiValueArgs COMPONENTS)
|
||||
cmake_parse_arguments(ECM_FPWC "${ecm_fpwc_options}" "${ecm_fpwc_oneValueArgs}" "${ecm_fpwc_multiValueArgs}" ${ARGN})
|
||||
|
||||
if(ECM_FPWC_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unexpected arguments to ecm_find_package_handle_components: ${ECM_FPWC_UNPARSED_ARGUMENTS}")
|
||||
endif()
|
||||
if(NOT ECM_FPWC_COMPONENTS)
|
||||
message(FATAL_ERROR "Missing COMPONENTS argument to ecm_find_package_handle_components")
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package(PkgConfig)
|
||||
foreach(ecm_fpwc_comp ${ECM_FPWC_COMPONENTS})
|
||||
set(ecm_fpwc_dep_vars)
|
||||
set(ecm_fpwc_dep_targets)
|
||||
if(NOT SKIP_DEPENDENCY_HANDLING)
|
||||
foreach(ecm_fpwc_dep ${${module_name}_${ecm_fpwc_comp}_component_deps})
|
||||
list(APPEND ecm_fpwc_dep_vars "${module_name}_${ecm_fpwc_dep}_FOUND")
|
||||
list(APPEND ecm_fpwc_dep_targets "${module_name}::${ecm_fpwc_dep}")
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(NOT ECM_FPWC_SKIP_PKG_CONFIG AND ${module_name}_${ecm_fpwc_comp}_pkg_config)
|
||||
pkg_check_modules(PKG_${module_name}_${ecm_fpwc_comp} QUIET
|
||||
${${module_name}_${ecm_fpwc_comp}_pkg_config})
|
||||
endif()
|
||||
|
||||
find_path(${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
|
||||
NAMES ${${module_name}_${ecm_fpwc_comp}_header}
|
||||
HINTS ${PKG_${module_name}_${ecm_fpwc_comp}_INCLUDE_DIRS}
|
||||
PATH_SUFFIXES ${${module_name}_${ecm_fpwc_comp}_header_subdir}
|
||||
)
|
||||
find_library(${module_name}_${ecm_fpwc_comp}_LIBRARY
|
||||
NAMES ${${module_name}_${ecm_fpwc_comp}_lib}
|
||||
HINTS ${PKG_${module_name}_${ecm_fpwc_comp}_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
set(${module_name}_${ecm_fpwc_comp}_VERSION "${PKG_${module_name}_${ecm_fpwc_comp}_VERSION}")
|
||||
if(NOT ${module_name}_VERSION)
|
||||
set(${module_name}_VERSION ${${module_name}_${ecm_fpwc_comp}_VERSION})
|
||||
endif()
|
||||
|
||||
find_package_handle_standard_args(${module_name}_${ecm_fpwc_comp}
|
||||
FOUND_VAR
|
||||
${module_name}_${ecm_fpwc_comp}_FOUND
|
||||
REQUIRED_VARS
|
||||
${module_name}_${ecm_fpwc_comp}_LIBRARY
|
||||
${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
|
||||
${ecm_fpwc_dep_vars}
|
||||
VERSION_VAR
|
||||
${module_name}_${ecm_fpwc_comp}_VERSION
|
||||
)
|
||||
|
||||
mark_as_advanced(
|
||||
${module_name}_${ecm_fpwc_comp}_LIBRARY
|
||||
${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR
|
||||
)
|
||||
|
||||
if(${module_name}_${ecm_fpwc_comp}_FOUND)
|
||||
list(APPEND ${module_name}_LIBRARIES
|
||||
"${${module_name}_${ecm_fpwc_comp}_LIBRARY}")
|
||||
list(APPEND ${module_name}_INCLUDE_DIRS
|
||||
"${${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR}")
|
||||
set(${module_name}_DEFINITIONS
|
||||
${${module_name}_DEFINITIONS}
|
||||
${PKG_${module_name}_${ecm_fpwc_comp}_DEFINITIONS})
|
||||
if(NOT TARGET ${module_name}::${ecm_fpwc_comp})
|
||||
add_library(${module_name}::${ecm_fpwc_comp} UNKNOWN IMPORTED)
|
||||
set_target_properties(${module_name}::${ecm_fpwc_comp} PROPERTIES
|
||||
IMPORTED_LOCATION "${${module_name}_${ecm_fpwc_comp}_LIBRARY}"
|
||||
INTERFACE_COMPILE_OPTIONS "${PKG_${module_name}_${ecm_fpwc_comp}_DEFINITIONS}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR}"
|
||||
INTERFACE_LINK_LIBRARIES "${ecm_fpwc_dep_targets}"
|
||||
)
|
||||
endif()
|
||||
list(APPEND ${module_name}_TARGETS
|
||||
"${module_name}::${ecm_fpwc_comp}")
|
||||
endif()
|
||||
endforeach()
|
||||
if(${module_name}_LIBRARIES)
|
||||
list(REMOVE_DUPLICATES ${module_name}_LIBRARIES)
|
||||
endif()
|
||||
if(${module_name}_INCLUDE_DIRS)
|
||||
list(REMOVE_DUPLICATES ${module_name}_INCLUDE_DIRS)
|
||||
endif()
|
||||
if(${module_name}_DEFINITIONS)
|
||||
list(REMOVE_DUPLICATES ${module_name}_DEFINITIONS)
|
||||
endif()
|
||||
if(${module_name}_TARGETS)
|
||||
list(REMOVE_DUPLICATES ${module_name}_TARGETS)
|
||||
endif()
|
||||
endmacro()
|
||||
@ -1,92 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kde.org>
|
||||
# SPDX-FileCopyrightText: 2011 Fredrik Höglund <fredrik@kde.org>
|
||||
# SPDX-FileCopyrightText: 2008 Helio Chissini de Castro <helio@kde.org>
|
||||
# SPDX-FileCopyrightText: 2007 Matthias Kretz <kretz@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
FindX11_XCB
|
||||
-----------
|
||||
|
||||
Try to find the X11 XCB compatibility library.
|
||||
|
||||
This will define the following variables:
|
||||
|
||||
``X11_XCB_FOUND``
|
||||
True if (the requested version of) libX11-xcb is available
|
||||
``X11_XCB_VERSION``
|
||||
The version of libX11-xcb (this is not guaranteed to be set even when
|
||||
X11_XCB_FOUND is true)
|
||||
``X11_XCB_LIBRARIES``
|
||||
This can be passed to target_link_libraries() instead of the ``EGL::EGL``
|
||||
target
|
||||
``X11_XCB_INCLUDE_DIR``
|
||||
This should be passed to target_include_directories() if the target is not
|
||||
used for linking
|
||||
``X11_XCB_DEFINITIONS``
|
||||
This should be passed to target_compile_options() if the target is not
|
||||
used for linking
|
||||
|
||||
If ``X11_XCB_FOUND`` is TRUE, it will also define the following imported
|
||||
target:
|
||||
|
||||
``X11::XCB``
|
||||
The X11 XCB compatibility library
|
||||
|
||||
In general we recommend using the imported target, as it is easier to use.
|
||||
Bear in mind, however, that if the target is in the link interface of an
|
||||
exported library, it must be made available by the package config file.
|
||||
|
||||
Since pre-1.0.0.
|
||||
#]=======================================================================]
|
||||
|
||||
# use pkg-config to get the directories and then use these values
|
||||
# in the FIND_PATH() and FIND_LIBRARY() calls
|
||||
find_package(PkgConfig QUIET)
|
||||
pkg_check_modules(PKG_X11_XCB QUIET x11-xcb)
|
||||
|
||||
set(X11_XCB_DEFINITIONS ${PKG_X11_XCB_CFLAGS_OTHER})
|
||||
set(X11_XCB_VERSION ${PKG_X11_XCB_VERSION})
|
||||
|
||||
find_path(X11_XCB_INCLUDE_DIR
|
||||
NAMES X11/Xlib-xcb.h
|
||||
HINTS ${PKG_X11_XCB_INCLUDE_DIRS}
|
||||
)
|
||||
find_library(X11_XCB_LIBRARY
|
||||
NAMES X11-xcb
|
||||
HINTS ${PKG_X11_XCB_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(X11_XCB
|
||||
FOUND_VAR
|
||||
X11_XCB_FOUND
|
||||
REQUIRED_VARS
|
||||
X11_XCB_LIBRARY
|
||||
X11_XCB_INCLUDE_DIR
|
||||
VERSION_VAR
|
||||
X11_XCB_VERSION
|
||||
)
|
||||
|
||||
if(X11_XCB_FOUND AND NOT TARGET X11::XCB)
|
||||
add_library(X11::XCB UNKNOWN IMPORTED)
|
||||
set_target_properties(X11::XCB PROPERTIES
|
||||
IMPORTED_LOCATION "${X11_XCB_LIBRARY}"
|
||||
INTERFACE_COMPILE_OPTIONS "${X11_XCB_DEFINITIONS}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${X11_XCB_INCLUDE_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(X11_XCB_INCLUDE_DIR X11_XCB_LIBRARY)
|
||||
|
||||
# compatibility variables
|
||||
set(X11_XCB_LIBRARIES ${X11_XCB_LIBRARY})
|
||||
set(X11_XCB_INCLUDE_DIRS ${X11_XCB_INCLUDE_DIR})
|
||||
set(X11_XCB_VERSION_STRING ${X11_XCB_VERSION})
|
||||
|
||||
include(FeatureSummary)
|
||||
set_package_properties(X11_XCB PROPERTIES
|
||||
URL "https://xorg.freedesktop.org/"
|
||||
DESCRIPTION "A compatibility library for code that translates Xlib API calls into XCB calls"
|
||||
)
|
||||
@ -1,177 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2011 Fredrik Höglund <fredrik@kde.org>
|
||||
# SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
|
||||
# SPDX-FileCopyrightText: 2014-2015 Alex Merry <alex.merry@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#[=======================================================================[.rst:
|
||||
FindXCB
|
||||
-------
|
||||
|
||||
Try to find XCB.
|
||||
|
||||
This is a component-based find module, which makes use of the COMPONENTS and
|
||||
OPTIONAL_COMPONENTS arguments to find_module. The following components are
|
||||
available::
|
||||
|
||||
XCB
|
||||
ATOM AUX COMPOSITE CURSOR DAMAGE
|
||||
DPMS DRI2 DRI3 EVENT EWMH
|
||||
GLX ICCCM IMAGE KEYSYMS PRESENT
|
||||
RANDR RECORD RENDER RENDERUTIL RES
|
||||
SCREENSAVER SHAPE SHM SYNC UTIL
|
||||
XEVIE XF86DRI XFIXES XINERAMA XINPUT
|
||||
XKB XPRINT XTEST XV XVMC
|
||||
|
||||
If no components are specified, this module will act as though all components
|
||||
except XINPUT (which is considered unstable) were passed to
|
||||
OPTIONAL_COMPONENTS.
|
||||
|
||||
This module will define the following variables, independently of the
|
||||
components searched for or found:
|
||||
|
||||
``XCB_FOUND``
|
||||
True if (the requestion version of) xcb is available
|
||||
``XCB_VERSION``
|
||||
Found xcb version
|
||||
``XCB_TARGETS``
|
||||
A list of all targets imported by this module (note that there may be more
|
||||
than the components that were requested)
|
||||
``XCB_LIBRARIES``
|
||||
This can be passed to target_link_libraries() instead of the imported
|
||||
targets
|
||||
``XCB_INCLUDE_DIRS``
|
||||
This should be passed to target_include_directories() if the targets are
|
||||
not used for linking
|
||||
``XCB_DEFINITIONS``
|
||||
This should be passed to target_compile_options() if the targets are not
|
||||
used for linking
|
||||
|
||||
For each searched-for components, ``XCB_<component>_FOUND`` will be set to
|
||||
true if the corresponding xcb library was found, and false otherwise. If
|
||||
``XCB_<component>_FOUND`` is true, the imported target ``XCB::<component>``
|
||||
will be defined. This module will also attempt to determine
|
||||
``XCB_*_VERSION`` variables for each imported target, although
|
||||
``XCB_VERSION`` should normally be sufficient.
|
||||
|
||||
In general we recommend using the imported targets, as they are easier to use
|
||||
and provide more control. Bear in mind, however, that if any target is in the
|
||||
link interface of an exported library, it must be made available by the
|
||||
package config file.
|
||||
|
||||
Since pre-1.0.0.
|
||||
#]=======================================================================]
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/ECMFindModuleHelpers.cmake)
|
||||
|
||||
# Note that this list needs to be ordered such that any component
|
||||
# appears after its dependencies
|
||||
set(XCB_known_components
|
||||
XCB
|
||||
RENDER
|
||||
SHAPE
|
||||
XFIXES
|
||||
SHM
|
||||
ATOM
|
||||
AUX
|
||||
COMPOSITE
|
||||
CURSOR
|
||||
DAMAGE
|
||||
DPMS
|
||||
DRI2
|
||||
DRI3
|
||||
EVENT
|
||||
EWMH
|
||||
GLX
|
||||
ICCCM
|
||||
IMAGE
|
||||
KEYSYMS
|
||||
PRESENT
|
||||
RANDR
|
||||
RECORD
|
||||
RENDERUTIL
|
||||
RES
|
||||
SCREENSAVER
|
||||
SYNC
|
||||
UTIL
|
||||
XEVIE
|
||||
XF86DRI
|
||||
XINERAMA
|
||||
XINPUT
|
||||
XKB
|
||||
XPRINT
|
||||
XTEST
|
||||
XV
|
||||
XVMC
|
||||
)
|
||||
|
||||
# XINPUT is unstable; do not include it by default
|
||||
set(XCB_default_components ${XCB_known_components})
|
||||
list(REMOVE_ITEM XCB_default_components "XINPUT")
|
||||
|
||||
# default component info: xcb components have fairly predictable
|
||||
# header files, library names and pkg-config names
|
||||
foreach(_comp ${XCB_known_components})
|
||||
string(TOLOWER "${_comp}" _lc_comp)
|
||||
set(XCB_${_comp}_component_deps XCB)
|
||||
set(XCB_${_comp}_pkg_config "xcb-${_lc_comp}")
|
||||
set(XCB_${_comp}_lib "xcb-${_lc_comp}")
|
||||
set(XCB_${_comp}_header "xcb/${_lc_comp}.h")
|
||||
endforeach()
|
||||
# exceptions
|
||||
set(XCB_XCB_component_deps)
|
||||
set(XCB_COMPOSITE_component_deps XCB XFIXES)
|
||||
set(XCB_DAMAGE_component_deps XCB XFIXES)
|
||||
set(XCB_IMAGE_component_deps XCB SHM)
|
||||
set(XCB_RENDERUTIL_component_deps XCB RENDER)
|
||||
set(XCB_XFIXES_component_deps XCB RENDER SHAPE)
|
||||
set(XCB_XVMC_component_deps XCB XV)
|
||||
set(XCB_XV_component_deps XCB SHM)
|
||||
set(XCB_XCB_pkg_config "xcb")
|
||||
set(XCB_XCB_lib "xcb")
|
||||
set(XCB_ATOM_header "xcb/xcb_atom.h")
|
||||
set(XCB_ATOM_lib "xcb-util")
|
||||
set(XCB_AUX_header "xcb/xcb_aux.h")
|
||||
set(XCB_AUX_lib "xcb-util")
|
||||
set(XCB_CURSOR_header "xcb/xcb_cursor.h")
|
||||
set(XCB_EVENT_header "xcb/xcb_event.h")
|
||||
set(XCB_EVENT_lib "xcb-util")
|
||||
set(XCB_EWMH_header "xcb/xcb_ewmh.h")
|
||||
set(XCB_ICCCM_header "xcb/xcb_icccm.h")
|
||||
set(XCB_IMAGE_header "xcb/xcb_image.h")
|
||||
set(XCB_KEYSYMS_header "xcb/xcb_keysyms.h")
|
||||
set(XCB_PIXEL_header "xcb/xcb_pixel.h")
|
||||
set(XCB_RENDERUTIL_header "xcb/xcb_renderutil.h")
|
||||
set(XCB_RENDERUTIL_lib "xcb-render-util")
|
||||
set(XCB_UTIL_header "xcb/xcb_util.h")
|
||||
|
||||
ecm_find_package_parse_components(XCB
|
||||
RESULT_VAR XCB_components
|
||||
KNOWN_COMPONENTS ${XCB_known_components}
|
||||
DEFAULT_COMPONENTS ${XCB_default_components}
|
||||
)
|
||||
|
||||
list(FIND XCB_components "XINPUT" _XCB_XINPUT_index)
|
||||
if (NOT _XCB_XINPUT_index EQUAL -1)
|
||||
message(AUTHOR_WARNING "XINPUT from XCB was requested: this is EXPERIMENTAL and is likely to unavailable on many systems!")
|
||||
endif()
|
||||
|
||||
ecm_find_package_handle_library_components(XCB
|
||||
COMPONENTS ${XCB_components}
|
||||
)
|
||||
|
||||
find_package_handle_standard_args(XCB
|
||||
FOUND_VAR
|
||||
XCB_FOUND
|
||||
REQUIRED_VARS
|
||||
XCB_LIBRARIES
|
||||
VERSION_VAR
|
||||
XCB_VERSION
|
||||
HANDLE_COMPONENTS
|
||||
)
|
||||
|
||||
include(FeatureSummary)
|
||||
set_package_properties(XCB PROPERTIES
|
||||
URL "https://xcb.freedesktop.org/"
|
||||
DESCRIPTION "X protocol C-language Binding"
|
||||
)
|
||||
@ -1,16 +0,0 @@
|
||||
find_package(Qt6 COMPONENTS Core Gui REQUIRED)
|
||||
find_package(X11)
|
||||
|
||||
add_executable(cupdatecursor
|
||||
main.cpp
|
||||
)
|
||||
target_link_libraries(cupdatecursor
|
||||
Qt6::Core
|
||||
Qt6::Gui
|
||||
${X11_LIBRARIES}
|
||||
X11::X11
|
||||
X11::Xi
|
||||
X11::Xcursor
|
||||
)
|
||||
|
||||
install(TARGETS cupdatecursor DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
@ -1,54 +0,0 @@
|
||||
#include <QGuiApplication>
|
||||
#include <QFile>
|
||||
#include <QDebug>
|
||||
#include <QSettings>
|
||||
#include <QStandardPaths>
|
||||
|
||||
#include <QtGui/qguiapplication_platform.h>
|
||||
|
||||
#include <X11/X.h>
|
||||
#include <X11/Xcursor/Xcursor.h>
|
||||
|
||||
inline void applyTheme(const QString &theme, int size)
|
||||
{
|
||||
Display *display = qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->display();
|
||||
|
||||
if (!theme.isEmpty())
|
||||
XcursorSetTheme(display, QFile::encodeName(theme));
|
||||
|
||||
if (size > 0)
|
||||
XcursorSetDefaultSize(display, size);
|
||||
|
||||
Cursor handle = XcursorLibraryLoadCursor(display, "left_ptr");
|
||||
XDefineCursor(display, DefaultRootWindow(display), handle);
|
||||
XFreeCursor(display, handle);
|
||||
XFlush(display);
|
||||
|
||||
// For KWin
|
||||
QSettings settings(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + "/kcminputrc",
|
||||
QSettings::IniFormat);
|
||||
settings.beginGroup("Mouse");
|
||||
settings.setValue("cursorTheme", theme);
|
||||
settings.setValue("cursorSize", size);
|
||||
settings.endGroup();
|
||||
settings.sync();
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QGuiApplication::setDesktopSettingsAware(false);
|
||||
QGuiApplication a(argc, argv);
|
||||
|
||||
if (argc != 3)
|
||||
return 1;
|
||||
|
||||
if (!qGuiApp->nativeInterface<QNativeInterface::QX11Application>())
|
||||
return 2;
|
||||
|
||||
QString theme = QFile::decodeName(argv[1]);
|
||||
QString size = QFile::decodeName(argv[2]);
|
||||
|
||||
applyTheme(theme, size.toInt());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1,53 +0,0 @@
|
||||
find_package(AppMenuGtkModule)
|
||||
find_package(KF6WindowSystem)
|
||||
find_package(KF6CoreAddons)
|
||||
set_package_properties(AppMenuGtkModule PROPERTIES TYPE RUNTIME)
|
||||
|
||||
add_definitions(-DQT_NO_CAST_TO_ASCII
|
||||
-DQT_NO_CAST_FROM_ASCII
|
||||
-DQT_NO_CAST_FROM_BYTEARRAY)
|
||||
|
||||
find_package(XCB
|
||||
REQUIRED COMPONENTS
|
||||
XCB
|
||||
)
|
||||
|
||||
set(GMENU_DBUSMENU_PROXY_SRCS
|
||||
extend/dbusmenutypes_p.cpp
|
||||
# extend/dbusmenushortcut_p.cpp
|
||||
|
||||
main.cpp
|
||||
menuproxy.cpp
|
||||
window.cpp
|
||||
menu.cpp
|
||||
actions.cpp
|
||||
dbusmenuadaptor.cpp
|
||||
gdbusmenutypes_p.cpp
|
||||
icons.cpp
|
||||
utils.cpp
|
||||
)
|
||||
|
||||
# qt_add_dbus_adaptor(GMENU_DBUSMENU_PROXY_SRCS ./com.canonical.dbusmenu.xml window.h Window)
|
||||
|
||||
add_executable(cutefish-gmenuproxy ${GMENU_DBUSMENU_PROXY_SRCS})
|
||||
|
||||
set_package_properties(XCB PROPERTIES TYPE REQUIRED)
|
||||
|
||||
target_link_libraries(cutefish-gmenuproxy
|
||||
Qt6::Core
|
||||
Qt6::Gui
|
||||
Qt6::DBus
|
||||
Qt6::Widgets
|
||||
KF6::CoreAddons
|
||||
KF6::WindowSystem
|
||||
XCB::XCB
|
||||
)
|
||||
|
||||
configure_file(
|
||||
cutefish-gmenuproxy.service.in
|
||||
cutefish-gmenuproxy.service
|
||||
@ONLY
|
||||
)
|
||||
|
||||
install(TARGETS cutefish-gmenuproxy DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/cutefish-gmenuproxy.service DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/systemd/user/)
|
||||
@ -1,196 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include "actions.h"
|
||||
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusMessage>
|
||||
#include <QDBusPendingCallWatcher>
|
||||
#include <QDBusPendingReply>
|
||||
#include <QDebug>
|
||||
#include <QStringList>
|
||||
#include <QVariantList>
|
||||
|
||||
static const QString s_orgGtkActions = QStringLiteral("org.gtk.Actions");
|
||||
|
||||
Actions::Actions(const QString &serviceName, const QString &objectPath, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_serviceName(serviceName)
|
||||
, m_objectPath(objectPath)
|
||||
{
|
||||
Q_ASSERT(!serviceName.isEmpty());
|
||||
Q_ASSERT(!m_objectPath.isEmpty());
|
||||
|
||||
if (!QDBusConnection::sessionBus().connect(serviceName,
|
||||
objectPath,
|
||||
s_orgGtkActions,
|
||||
QStringLiteral("Changed"),
|
||||
this,
|
||||
SLOT(onActionsChanged(QStringList, StringBoolMap, QVariantMap, GMenuActionMap)))) {
|
||||
qDebug() << "Failed to subscribe to action changes for" << parent << "on" << serviceName << "at" << objectPath;
|
||||
}
|
||||
}
|
||||
|
||||
Actions::~Actions() = default;
|
||||
|
||||
void Actions::load()
|
||||
{
|
||||
QDBusMessage msg = QDBusMessage::createMethodCall(m_serviceName, m_objectPath, s_orgGtkActions, QStringLiteral("DescribeAll"));
|
||||
|
||||
QDBusPendingReply<GMenuActionMap> reply = QDBusConnection::sessionBus().asyncCall(msg);
|
||||
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this);
|
||||
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) {
|
||||
QDBusPendingReply<GMenuActionMap> reply = *watcher;
|
||||
if (reply.isError()) {
|
||||
qDebug() << "Failed to get actions from" << m_serviceName << "at" << m_objectPath << reply.error();
|
||||
emit failedToLoad();
|
||||
} else {
|
||||
m_actions = reply.value();
|
||||
emit loaded();
|
||||
}
|
||||
watcher->deleteLater();
|
||||
});
|
||||
}
|
||||
|
||||
bool Actions::get(const QString &name, GMenuAction &action) const
|
||||
{
|
||||
auto it = m_actions.find(name);
|
||||
if (it == m_actions.constEnd()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
action = *it;
|
||||
return true;
|
||||
}
|
||||
|
||||
GMenuActionMap Actions::getAll() const
|
||||
{
|
||||
return m_actions;
|
||||
}
|
||||
|
||||
void Actions::trigger(const QString &name, const QVariant &target, uint timestamp)
|
||||
{
|
||||
if (!m_actions.contains(name)) {
|
||||
qDebug() << "Cannot invoke action" << name << "which doesn't exist";
|
||||
return;
|
||||
}
|
||||
|
||||
QDBusMessage msg = QDBusMessage::createMethodCall(m_serviceName, m_objectPath, s_orgGtkActions, QStringLiteral("Activate"));
|
||||
msg << name;
|
||||
|
||||
QVariantList args;
|
||||
if (target.isValid()) {
|
||||
args << target;
|
||||
}
|
||||
msg << QVariant::fromValue(args);
|
||||
|
||||
QVariantMap platformData;
|
||||
|
||||
if (timestamp) {
|
||||
// From documentation:
|
||||
// If the startup notification id is not available, this can be just "_TIMEtime", where
|
||||
// time is the time stamp from the event triggering the call.
|
||||
// see also gtkwindow.c extract_time_from_startup_id and startup_id_is_fake
|
||||
platformData.insert(QStringLiteral("desktop-startup-id"), QStringLiteral("_TIME") + QString::number(timestamp));
|
||||
}
|
||||
|
||||
msg << platformData;
|
||||
|
||||
QDBusPendingReply<void> reply = QDBusConnection::sessionBus().asyncCall(msg);
|
||||
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this);
|
||||
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, name](QDBusPendingCallWatcher *watcher) {
|
||||
QDBusPendingReply<void> reply = *watcher;
|
||||
if (reply.isError()) {
|
||||
qDebug() << "Failed to invoke action" << name << "on" << m_serviceName << "at" << m_objectPath << reply.error();
|
||||
}
|
||||
watcher->deleteLater();
|
||||
});
|
||||
}
|
||||
|
||||
bool Actions::isValid() const
|
||||
{
|
||||
return !m_actions.isEmpty();
|
||||
}
|
||||
|
||||
void Actions::onActionsChanged(const QStringList &removed, const StringBoolMap &enabledChanges, const QVariantMap &stateChanges, const GMenuActionMap &added)
|
||||
{
|
||||
// Collect the actions that we removed, altered, or added, so we can eventually signal changes for all menus that contain one of those actions
|
||||
QStringList dirtyActions;
|
||||
|
||||
// TODO I bet for most of the loops below we could use a nice short std algorithm
|
||||
|
||||
for (const QString &removedAction : removed) {
|
||||
if (m_actions.remove(removedAction)) {
|
||||
dirtyActions.append(removedAction);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = enabledChanges.constBegin(), end = enabledChanges.constEnd(); it != end; ++it) {
|
||||
const QString &actionName = it.key();
|
||||
const bool enabled = it.value();
|
||||
|
||||
auto actionIt = m_actions.find(actionName);
|
||||
if (actionIt == m_actions.end()) {
|
||||
qDebug() << "Got enabled changed for action" << actionName << "which we don't know";
|
||||
continue;
|
||||
}
|
||||
|
||||
GMenuAction &action = *actionIt;
|
||||
if (action.enabled != enabled) {
|
||||
action.enabled = enabled;
|
||||
dirtyActions.append(actionName);
|
||||
} else {
|
||||
qDebug() << "Got enabled change for action" << actionName << "which didn't change it";
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = stateChanges.constBegin(), end = stateChanges.constEnd(); it != end; ++it) {
|
||||
const QString &actionName = it.key();
|
||||
const QVariant &state = it.value();
|
||||
|
||||
auto actionIt = m_actions.find(actionName);
|
||||
if (actionIt == m_actions.end()) {
|
||||
qDebug() << "Got state changed for action" << actionName << "which we don't know";
|
||||
continue;
|
||||
}
|
||||
|
||||
GMenuAction &action = *actionIt;
|
||||
|
||||
if (action.state.isEmpty()) {
|
||||
qDebug() << "Got new state for action" << actionName << "that didn't have any state before";
|
||||
action.state.append(state);
|
||||
dirtyActions.append(actionName);
|
||||
} else {
|
||||
// Action state is a list but the state change only sends us a single variant, so just overwrite the first one
|
||||
QVariant &firstState = action.state.first();
|
||||
if (firstState != state) {
|
||||
firstState = state;
|
||||
dirtyActions.append(actionName);
|
||||
} else {
|
||||
qDebug() << "Got state change for action" << actionName << "which didn't change it";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// unite() will result in keys being present multiple times, do it manually and overwrite existing ones
|
||||
for (auto it = added.constBegin(), end = added.constEnd(); it != end; ++it) {
|
||||
const QString &actionName = it.key();
|
||||
|
||||
// if ((DBUSMENUPROXY).isInfoEnabled()) {
|
||||
// if (m_actions.contains(actionName)) {
|
||||
// qDebug() << "Got new action" << actionName << "that we already have, overwriting existing one";
|
||||
// }
|
||||
// }
|
||||
|
||||
m_actions.insert(actionName, it.value());
|
||||
|
||||
dirtyActions.append(actionName);
|
||||
}
|
||||
|
||||
if (!dirtyActions.isEmpty()) {
|
||||
emit actionsChanged(dirtyActions);
|
||||
}
|
||||
}
|
||||
@ -1,44 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
#include "gdbusmenutypes_p.h"
|
||||
|
||||
class Actions : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Actions(const QString &serviceName, const QString &objectPath, QObject *parent = nullptr);
|
||||
~Actions() override;
|
||||
|
||||
void load();
|
||||
|
||||
bool get(const QString &name, GMenuAction &action) const;
|
||||
GMenuActionMap getAll() const;
|
||||
void trigger(const QString &name, const QVariant &target, uint timestamp = 0);
|
||||
|
||||
bool isValid() const; // basically "has actions"
|
||||
|
||||
Q_SIGNALS:
|
||||
void loaded();
|
||||
void failedToLoad(); // expose error?
|
||||
void actionsChanged(const QStringList &dirtyActions);
|
||||
|
||||
private slots:
|
||||
void onActionsChanged(const QStringList &removed, const StringBoolMap &enabledChanges, const QVariantMap &stateChanges, const GMenuActionMap &added);
|
||||
|
||||
private:
|
||||
GMenuActionMap m_actions;
|
||||
|
||||
QString m_serviceName;
|
||||
QString m_objectPath;
|
||||
};
|
||||
@ -1,49 +0,0 @@
|
||||
<interface name="com.canonical.dbusmenu">
|
||||
<property name="Version" type="u" access="read"/>
|
||||
<property name="Status" type="s" access="read"/>
|
||||
<signal name="ItemsPropertiesUpdated">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="DBusMenuItemList"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="DBusMenuItemKeysList"/>
|
||||
<arg type="a(ia{sv})" direction="out"/>
|
||||
<arg type="a(ias)" direction="out"/>
|
||||
</signal>
|
||||
<signal name="LayoutUpdated">
|
||||
<arg name="revision" type="u" direction="out"/>
|
||||
<arg name="parentId" type="i" direction="out"/>
|
||||
</signal>
|
||||
<signal name="ItemActivationRequested">
|
||||
<arg name="id" type="i" direction="out"/>
|
||||
<arg name="timeStamp" type="u" direction="out"/>
|
||||
</signal>
|
||||
<method name="Event">
|
||||
<arg name="id" type="i" direction="in"/>
|
||||
<arg name="eventId" type="s" direction="in"/>
|
||||
<arg name="data" type="v" direction="in"/>
|
||||
<arg name="timestamp" type="u" direction="in"/>
|
||||
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/>
|
||||
</method>
|
||||
<method name="GetProperty">
|
||||
<arg type="v" direction="out"/>
|
||||
<arg name="id" type="i" direction="in"/>
|
||||
<arg name="property" type="s" direction="in"/>
|
||||
</method>
|
||||
<method name="GetLayout">
|
||||
<arg type="u" direction="out"/>
|
||||
<arg name="parentId" type="i" direction="in"/>
|
||||
<arg name="recursionDepth" type="i" direction="in"/>
|
||||
<arg name="propertyNames" type="as" direction="in"/>
|
||||
<arg name="item" type="(ia{sv}av)" direction="out"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="DBusMenuLayoutItem"/>
|
||||
</method>
|
||||
<method name="GetGroupProperties">
|
||||
<arg type="a(ia{sv})" direction="out"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="DBusMenuItemList"/>
|
||||
<arg name="ids" type="ai" direction="in"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QList<int>"/>
|
||||
<arg name="propertyNames" type="as" direction="in"/>
|
||||
</method>
|
||||
<method name="AboutToShow">
|
||||
<arg type="b" direction="out"/>
|
||||
<arg name="id" type="i" direction="in"/>
|
||||
</method>
|
||||
</interface>
|
||||
@ -1,10 +0,0 @@
|
||||
[Unit]
|
||||
Description=Proxies GTK DBus menus to a Cutefish readable format
|
||||
PartOf=graphical-session.target
|
||||
|
||||
[Service]
|
||||
ExecStart=@CMAKE_INSTALL_FULL_BINDIR@/cutefish-gmenuproxy
|
||||
Restart=on-failure
|
||||
Type=simple
|
||||
Slice=background.slice
|
||||
TimeoutSec=5sec
|
||||
@ -1,79 +0,0 @@
|
||||
/*
|
||||
* This file was generated by qdbusxml2cpp version 0.8
|
||||
* Command line was: qdbusxml2cpp -m -a dbusmenuadaptor -i window.h -l Window /home/reion/Cutefish/core/gmenuproxy/com.canonical.dbusmenu.xml
|
||||
*
|
||||
* qdbusxml2cpp is Copyright (C) 2020 The Qt Company Ltd.
|
||||
*
|
||||
* This is an auto-generated file.
|
||||
* Do not edit! All changes made to it will be lost.
|
||||
*/
|
||||
|
||||
#include "dbusmenuadaptor.h"
|
||||
#include <QtCore/QMetaObject>
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QMap>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QVariant>
|
||||
|
||||
/*
|
||||
* Implementation of adaptor class DbusmenuAdaptor
|
||||
*/
|
||||
|
||||
DbusmenuAdaptor::DbusmenuAdaptor(Window *parent)
|
||||
: QDBusAbstractAdaptor(parent)
|
||||
{
|
||||
// constructor
|
||||
setAutoRelaySignals(true);
|
||||
}
|
||||
|
||||
DbusmenuAdaptor::~DbusmenuAdaptor()
|
||||
{
|
||||
// destructor
|
||||
}
|
||||
|
||||
QString DbusmenuAdaptor::status() const
|
||||
{
|
||||
// get the value of property Status
|
||||
return qvariant_cast< QString >(parent()->property("Status"));
|
||||
}
|
||||
|
||||
uint DbusmenuAdaptor::version() const
|
||||
{
|
||||
// get the value of property Version
|
||||
return qvariant_cast< uint >(parent()->property("Version"));
|
||||
}
|
||||
|
||||
bool DbusmenuAdaptor::AboutToShow(int id)
|
||||
{
|
||||
// handle method call com.canonical.dbusmenu.AboutToShow
|
||||
return parent()->AboutToShow(id);
|
||||
}
|
||||
|
||||
void DbusmenuAdaptor::Event(int id, const QString &eventId, const QDBusVariant &data, uint timestamp)
|
||||
{
|
||||
// handle method call com.canonical.dbusmenu.Event
|
||||
parent()->Event(id, eventId, data, timestamp);
|
||||
}
|
||||
|
||||
DBusMenuItemList DbusmenuAdaptor::GetGroupProperties(const QList<int> &ids, const QStringList &propertyNames)
|
||||
{
|
||||
// handle method call com.canonical.dbusmenu.GetGroupProperties
|
||||
return parent()->GetGroupProperties(ids, propertyNames);
|
||||
}
|
||||
|
||||
uint DbusmenuAdaptor::GetLayout(int parentId, int recursionDepth, const QStringList &propertyNames, DBusMenuLayoutItem &item)
|
||||
{
|
||||
// handle method call com.canonical.dbusmenu.GetLayout
|
||||
return parent()->GetLayout(parentId, recursionDepth, propertyNames, item);
|
||||
}
|
||||
|
||||
QDBusVariant DbusmenuAdaptor::GetProperty(int id, const QString &property)
|
||||
{
|
||||
// handle method call com.canonical.dbusmenu.GetProperty
|
||||
return parent()->GetProperty(id, property);
|
||||
}
|
||||
|
||||
|
||||
#include "dbusmenuadaptor.moc"
|
||||
@ -1,111 +0,0 @@
|
||||
/*
|
||||
* This file was generated by qdbusxml2cpp version 0.8
|
||||
* Command line was: qdbusxml2cpp -m -a dbusmenuadaptor -i window.h -l Window /home/reion/Cutefish/core/gmenuproxy/com.canonical.dbusmenu.xml
|
||||
*
|
||||
* qdbusxml2cpp is Copyright (C) 2020 The Qt Company Ltd.
|
||||
*
|
||||
* This is an auto-generated file.
|
||||
* This file may have been hand-edited. Look for HAND-EDIT comments
|
||||
* before re-generating it.
|
||||
*/
|
||||
|
||||
#ifndef DBUSMENUADAPTOR_H
|
||||
#define DBUSMENUADAPTOR_H
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QStringList>
|
||||
#include <QtDBus/QtDBus>
|
||||
#include "window.h"
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QByteArray;
|
||||
template<class T> class QList;
|
||||
template<class Key, class Value> class QMap;
|
||||
class QString;
|
||||
class QVariant;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
/*
|
||||
* Adaptor class for interface com.canonical.dbusmenu
|
||||
*/
|
||||
class DbusmenuAdaptor: public QDBusAbstractAdaptor
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_CLASSINFO("D-Bus Interface", "com.canonical.dbusmenu")
|
||||
Q_CLASSINFO("D-Bus Introspection", ""
|
||||
" <interface name=\"com.canonical.dbusmenu\">\n"
|
||||
" <property access=\"read\" type=\"u\" name=\"Version\"/>\n"
|
||||
" <property access=\"read\" type=\"s\" name=\"Status\"/>\n"
|
||||
" <signal name=\"ItemsPropertiesUpdated\">\n"
|
||||
" <annotation value=\"DBusMenuItemList\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
|
||||
" <annotation value=\"DBusMenuItemKeysList\" name=\"org.qtproject.QtDBus.QtTypeName.Out1\"/>\n"
|
||||
" <arg direction=\"out\" type=\"a(ia{sv})\"/>\n"
|
||||
" <arg direction=\"out\" type=\"a(ias)\"/>\n"
|
||||
" </signal>\n"
|
||||
" <signal name=\"LayoutUpdated\">\n"
|
||||
" <arg direction=\"out\" type=\"u\" name=\"revision\"/>\n"
|
||||
" <arg direction=\"out\" type=\"i\" name=\"parentId\"/>\n"
|
||||
" </signal>\n"
|
||||
" <signal name=\"ItemActivationRequested\">\n"
|
||||
" <arg direction=\"out\" type=\"i\" name=\"id\"/>\n"
|
||||
" <arg direction=\"out\" type=\"u\" name=\"timeStamp\"/>\n"
|
||||
" </signal>\n"
|
||||
" <method name=\"Event\">\n"
|
||||
" <arg direction=\"in\" type=\"i\" name=\"id\"/>\n"
|
||||
" <arg direction=\"in\" type=\"s\" name=\"eventId\"/>\n"
|
||||
" <arg direction=\"in\" type=\"v\" name=\"data\"/>\n"
|
||||
" <arg direction=\"in\" type=\"u\" name=\"timestamp\"/>\n"
|
||||
" <annotation value=\"true\" name=\"org.freedesktop.DBus.Method.NoReply\"/>\n"
|
||||
" </method>\n"
|
||||
" <method name=\"GetProperty\">\n"
|
||||
" <arg direction=\"out\" type=\"v\"/>\n"
|
||||
" <arg direction=\"in\" type=\"i\" name=\"id\"/>\n"
|
||||
" <arg direction=\"in\" type=\"s\" name=\"property\"/>\n"
|
||||
" </method>\n"
|
||||
" <method name=\"GetLayout\">\n"
|
||||
" <arg direction=\"out\" type=\"u\"/>\n"
|
||||
" <arg direction=\"in\" type=\"i\" name=\"parentId\"/>\n"
|
||||
" <arg direction=\"in\" type=\"i\" name=\"recursionDepth\"/>\n"
|
||||
" <arg direction=\"in\" type=\"as\" name=\"propertyNames\"/>\n"
|
||||
" <arg direction=\"out\" type=\"(ia{sv}av)\" name=\"item\"/>\n"
|
||||
" <annotation value=\"DBusMenuLayoutItem\" name=\"org.qtproject.QtDBus.QtTypeName.Out1\"/>\n"
|
||||
" </method>\n"
|
||||
" <method name=\"GetGroupProperties\">\n"
|
||||
" <arg direction=\"out\" type=\"a(ia{sv})\"/>\n"
|
||||
" <annotation value=\"DBusMenuItemList\" name=\"org.qtproject.QtDBus.QtTypeName.Out0\"/>\n"
|
||||
" <arg direction=\"in\" type=\"ai\" name=\"ids\"/>\n"
|
||||
" <annotation value=\"QList<int>\" name=\"org.qtproject.QtDBus.QtTypeName.In0\"/>\n"
|
||||
" <arg direction=\"in\" type=\"as\" name=\"propertyNames\"/>\n"
|
||||
" </method>\n"
|
||||
" <method name=\"AboutToShow\">\n"
|
||||
" <arg direction=\"out\" type=\"b\"/>\n"
|
||||
" <arg direction=\"in\" type=\"i\" name=\"id\"/>\n"
|
||||
" </method>\n"
|
||||
" </interface>\n"
|
||||
"")
|
||||
public:
|
||||
DbusmenuAdaptor(Window *parent);
|
||||
virtual ~DbusmenuAdaptor();
|
||||
|
||||
inline Window *parent() const
|
||||
{ return static_cast<Window *>(QObject::parent()); }
|
||||
|
||||
public: // PROPERTIES
|
||||
Q_PROPERTY(QString Status READ status)
|
||||
QString status() const;
|
||||
|
||||
Q_PROPERTY(uint Version READ version)
|
||||
uint version() const;
|
||||
|
||||
public Q_SLOTS: // METHODS
|
||||
bool AboutToShow(int id);
|
||||
Q_NOREPLY void Event(int id, const QString &eventId, const QDBusVariant &data, uint timestamp);
|
||||
DBusMenuItemList GetGroupProperties(const QList<int> &ids, const QStringList &propertyNames);
|
||||
uint GetLayout(int parentId, int recursionDepth, const QStringList &propertyNames, DBusMenuLayoutItem &item);
|
||||
QDBusVariant GetProperty(int id, const QString &property);
|
||||
Q_SIGNALS: // SIGNALS
|
||||
void ItemActivationRequested(int id, uint timeStamp);
|
||||
void ItemsPropertiesUpdated(DBusMenuItemList in0, DBusMenuItemKeysList in1);
|
||||
void LayoutUpdated(uint revision, int parentId);
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,69 +0,0 @@
|
||||
/* This file is part of the dbusmenu-qt library
|
||||
SPDX-FileCopyrightText: 2009 Canonical
|
||||
SPDX-FileContributor: Aurelien Gateau <aurelien.gateau@canonical.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
#include "dbusmenushortcut_p.h"
|
||||
|
||||
// Qt
|
||||
#include <QKeySequence>
|
||||
|
||||
static const int QT_COLUMN = 0;
|
||||
static const int DM_COLUMN = 1;
|
||||
|
||||
static void processKeyTokens(QStringList *tokens, int srcCol, int dstCol)
|
||||
{
|
||||
struct Row {
|
||||
const char *zero;
|
||||
const char *one;
|
||||
const char *operator[](int col) const
|
||||
{
|
||||
return col == 0 ? zero : one;
|
||||
}
|
||||
};
|
||||
static const Row table[] = {{"Meta", "Super"},
|
||||
{"Ctrl", "Control"},
|
||||
// Special cases for compatibility with libdbusmenu-glib which uses
|
||||
// "plus" for "+" and "minus" for "-".
|
||||
// cf https://bugs.launchpad.net/libdbusmenu-qt/+bug/712565
|
||||
{"+", "plus"},
|
||||
{"-", "minus"},
|
||||
{nullptr, nullptr}};
|
||||
|
||||
const Row *ptr = table;
|
||||
for (; ptr->zero != nullptr; ++ptr) {
|
||||
const char *from = (*ptr)[srcCol];
|
||||
const char *to = (*ptr)[dstCol];
|
||||
tokens->replaceInStrings(from, to);
|
||||
}
|
||||
}
|
||||
|
||||
DBusMenuShortcut DBusMenuShortcut::fromKeySequence(const QKeySequence &sequence)
|
||||
{
|
||||
QString string = sequence.toString();
|
||||
DBusMenuShortcut shortcut;
|
||||
QStringList tokens = string.split(QStringLiteral(", "));
|
||||
Q_FOREACH (QString token, tokens) {
|
||||
// Hack: Qt::CTRL | Qt::Key_Plus is turned into the string "Ctrl++",
|
||||
// but we don't want the call to token.split() to consider the
|
||||
// second '+' as a separator so we replace it with its final value.
|
||||
token.replace(QLatin1String("++"), QLatin1String("+plus"));
|
||||
QStringList keyTokens = token.split('+');
|
||||
processKeyTokens(&keyTokens, QT_COLUMN, DM_COLUMN);
|
||||
shortcut << keyTokens;
|
||||
}
|
||||
return shortcut;
|
||||
}
|
||||
|
||||
QKeySequence DBusMenuShortcut::toKeySequence() const
|
||||
{
|
||||
QStringList tmp;
|
||||
Q_FOREACH (const QStringList &keyTokens_, *this) {
|
||||
QStringList keyTokens = keyTokens_;
|
||||
processKeyTokens(&keyTokens, DM_COLUMN, QT_COLUMN);
|
||||
tmp << keyTokens.join(QLatin1String("+"));
|
||||
}
|
||||
QString string = tmp.join(QLatin1String(", "));
|
||||
return QKeySequence::fromString(string);
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
/* This file is part of the dbusmenu-qt library
|
||||
SPDX-FileCopyrightText: 2009 Canonical
|
||||
SPDX-FileContributor: Aurelien Gateau <aurelien.gateau@canonical.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// Qt
|
||||
#include <QMetaType>
|
||||
#include <QStringList>
|
||||
|
||||
class QKeySequence;
|
||||
|
||||
class DBusMenuShortcut : public QList<QStringList>
|
||||
{
|
||||
public:
|
||||
QKeySequence toKeySequence() const;
|
||||
static DBusMenuShortcut fromKeySequence(const QKeySequence &);
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(DBusMenuShortcut)
|
||||
@ -1,122 +0,0 @@
|
||||
/* This file is part of the dbusmenu-qt library
|
||||
SPDX-FileCopyrightText: 2009 Canonical
|
||||
SPDX-FileContributor: Aurelien Gateau <aurelien.gateau@canonical.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
#include "dbusmenutypes_p.h"
|
||||
|
||||
// Local
|
||||
#include "dbusmenushortcut_p.h"
|
||||
|
||||
// Qt
|
||||
#include <QDBusArgument>
|
||||
#include <QDBusMetaType>
|
||||
|
||||
//// DBusMenuItem
|
||||
QDBusArgument &operator<<(QDBusArgument &argument, const DBusMenuItem &obj)
|
||||
{
|
||||
argument.beginStructure();
|
||||
argument << obj.id << obj.properties;
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
const QDBusArgument &operator>>(const QDBusArgument &argument, DBusMenuItem &obj)
|
||||
{
|
||||
argument.beginStructure();
|
||||
argument >> obj.id >> obj.properties;
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
//// DBusMenuItemKeys
|
||||
QDBusArgument &operator<<(QDBusArgument &argument, const DBusMenuItemKeys &obj)
|
||||
{
|
||||
argument.beginStructure();
|
||||
argument << obj.id << obj.properties;
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
const QDBusArgument &operator>>(const QDBusArgument &argument, DBusMenuItemKeys &obj)
|
||||
{
|
||||
argument.beginStructure();
|
||||
argument >> obj.id >> obj.properties;
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
//// DBusMenuLayoutItem
|
||||
QDBusArgument &operator<<(QDBusArgument &argument, const DBusMenuLayoutItem &obj)
|
||||
{
|
||||
argument.beginStructure();
|
||||
argument << obj.id << obj.properties;
|
||||
argument.beginArray(qMetaTypeId<QDBusVariant>());
|
||||
Q_FOREACH (const DBusMenuLayoutItem &child, obj.children) {
|
||||
argument << QDBusVariant(QVariant::fromValue<DBusMenuLayoutItem>(child));
|
||||
}
|
||||
argument.endArray();
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
const QDBusArgument &operator>>(const QDBusArgument &argument, DBusMenuLayoutItem &obj)
|
||||
{
|
||||
argument.beginStructure();
|
||||
argument >> obj.id >> obj.properties;
|
||||
argument.beginArray();
|
||||
while (!argument.atEnd()) {
|
||||
QDBusVariant dbusVariant;
|
||||
argument >> dbusVariant;
|
||||
QDBusArgument childArgument = dbusVariant.variant().value<QDBusArgument>();
|
||||
|
||||
DBusMenuLayoutItem child;
|
||||
childArgument >> child;
|
||||
obj.children.append(child);
|
||||
}
|
||||
argument.endArray();
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
//// DBusMenuShortcut
|
||||
QDBusArgument &operator<<(QDBusArgument &argument, const DBusMenuShortcut &obj)
|
||||
{
|
||||
argument.beginArray(qMetaTypeId<QStringList>());
|
||||
typename QList<QStringList>::ConstIterator it = obj.constBegin();
|
||||
typename QList<QStringList>::ConstIterator end = obj.constEnd();
|
||||
for (; it != end; ++it)
|
||||
argument << *it;
|
||||
argument.endArray();
|
||||
return argument;
|
||||
}
|
||||
|
||||
const QDBusArgument &operator>>(const QDBusArgument &argument, DBusMenuShortcut &obj)
|
||||
{
|
||||
argument.beginArray();
|
||||
obj.clear();
|
||||
while (!argument.atEnd()) {
|
||||
QStringList item;
|
||||
argument >> item;
|
||||
obj.push_back(item);
|
||||
}
|
||||
argument.endArray();
|
||||
return argument;
|
||||
}
|
||||
|
||||
void DBusMenuTypes_register()
|
||||
{
|
||||
static bool registered = false;
|
||||
if (registered) {
|
||||
return;
|
||||
}
|
||||
qDBusRegisterMetaType<DBusMenuItem>();
|
||||
qDBusRegisterMetaType<DBusMenuItemList>();
|
||||
qDBusRegisterMetaType<DBusMenuItemKeys>();
|
||||
qDBusRegisterMetaType<DBusMenuItemKeysList>();
|
||||
qDBusRegisterMetaType<DBusMenuLayoutItem>();
|
||||
qDBusRegisterMetaType<DBusMenuLayoutItemList>();
|
||||
qDBusRegisterMetaType<DBusMenuShortcut>();
|
||||
registered = true;
|
||||
}
|
||||
@ -1,80 +0,0 @@
|
||||
/* This file is part of the dbusmenu-qt library
|
||||
SPDX-FileCopyrightText: 2009 Canonical
|
||||
SPDX-FileContributor: Aurelien Gateau <aurelien.gateau@canonical.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// Qt
|
||||
#include <QList>
|
||||
#include <QStringList>
|
||||
#include <QVariant>
|
||||
|
||||
class QDBusArgument;
|
||||
|
||||
//// DBusMenuItem
|
||||
/**
|
||||
* Internal struct used to communicate on DBus
|
||||
*/
|
||||
struct DBusMenuItem {
|
||||
int id;
|
||||
QVariantMap properties;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(DBusMenuItem)
|
||||
|
||||
QDBusArgument &operator<<(QDBusArgument &argument, const DBusMenuItem &item);
|
||||
const QDBusArgument &operator>>(const QDBusArgument &argument, DBusMenuItem &item);
|
||||
|
||||
typedef QList<DBusMenuItem> DBusMenuItemList;
|
||||
|
||||
Q_DECLARE_METATYPE(DBusMenuItemList)
|
||||
|
||||
//// DBusMenuItemKeys
|
||||
/**
|
||||
* Represents a list of keys for a menu item
|
||||
*/
|
||||
struct DBusMenuItemKeys {
|
||||
int id;
|
||||
QStringList properties;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(DBusMenuItemKeys)
|
||||
|
||||
QDBusArgument &operator<<(QDBusArgument &argument, const DBusMenuItemKeys &);
|
||||
const QDBusArgument &operator>>(const QDBusArgument &argument, DBusMenuItemKeys &);
|
||||
|
||||
typedef QList<DBusMenuItemKeys> DBusMenuItemKeysList;
|
||||
|
||||
Q_DECLARE_METATYPE(DBusMenuItemKeysList)
|
||||
|
||||
//// DBusMenuLayoutItem
|
||||
/**
|
||||
* Represents an item with its children. GetLayout() returns a
|
||||
* DBusMenuLayoutItemList.
|
||||
*/
|
||||
struct DBusMenuLayoutItem;
|
||||
struct DBusMenuLayoutItem {
|
||||
int id;
|
||||
QVariantMap properties;
|
||||
QList<DBusMenuLayoutItem> children;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(DBusMenuLayoutItem)
|
||||
|
||||
QDBusArgument &operator<<(QDBusArgument &argument, const DBusMenuLayoutItem &);
|
||||
const QDBusArgument &operator>>(const QDBusArgument &argument, DBusMenuLayoutItem &);
|
||||
|
||||
typedef QList<DBusMenuLayoutItem> DBusMenuLayoutItemList;
|
||||
|
||||
Q_DECLARE_METATYPE(DBusMenuLayoutItemList)
|
||||
|
||||
//// DBusMenuShortcut
|
||||
|
||||
class DBusMenuShortcut;
|
||||
|
||||
QDBusArgument &operator<<(QDBusArgument &argument, const DBusMenuShortcut &);
|
||||
const QDBusArgument &operator>>(const QDBusArgument &argument, DBusMenuShortcut &);
|
||||
|
||||
void DBusMenuTypes_register();
|
||||
@ -1,119 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include "gdbusmenutypes_p.h"
|
||||
|
||||
#include <QDBusArgument>
|
||||
#include <QDBusMetaType>
|
||||
|
||||
// GMenuItem
|
||||
QDBusArgument &operator<<(QDBusArgument &argument, const GMenuItem &item)
|
||||
{
|
||||
argument.beginStructure();
|
||||
argument << item.id << item.section << item.items;
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
const QDBusArgument &operator>>(const QDBusArgument &argument, GMenuItem &item)
|
||||
{
|
||||
argument.beginStructure();
|
||||
argument >> item.id >> item.section >> item.items;
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
// GMenuSection
|
||||
QDBusArgument &operator<<(QDBusArgument &argument, const GMenuSection &item)
|
||||
{
|
||||
argument.beginStructure();
|
||||
argument << item.subscription << item.menu;
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
const QDBusArgument &operator>>(const QDBusArgument &argument, GMenuSection &item)
|
||||
{
|
||||
argument.beginStructure();
|
||||
argument >> item.subscription >> item.menu;
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
// GMenuChange
|
||||
QDBusArgument &operator<<(QDBusArgument &argument, const GMenuChange &item)
|
||||
{
|
||||
argument.beginStructure();
|
||||
argument << item.subscription << item.menu << item.changePosition << item.itemsToRemoveCount << item.itemsToInsert;
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
const QDBusArgument &operator>>(const QDBusArgument &argument, GMenuChange &item)
|
||||
{
|
||||
argument.beginStructure();
|
||||
argument >> item.subscription >> item.menu >> item.changePosition >> item.itemsToRemoveCount >> item.itemsToInsert;
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
// GMenuActionProperty
|
||||
QDBusArgument &operator<<(QDBusArgument &argument, const GMenuAction &item)
|
||||
{
|
||||
argument.beginStructure();
|
||||
argument << item.enabled << item.signature << item.state;
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
const QDBusArgument &operator>>(const QDBusArgument &argument, GMenuAction &item)
|
||||
{
|
||||
argument.beginStructure();
|
||||
argument >> item.enabled >> item.signature >> item.state;
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
// GMenuActionsChange
|
||||
QDBusArgument &operator<<(QDBusArgument &argument, const GMenuActionsChange &item)
|
||||
{
|
||||
argument.beginStructure();
|
||||
argument << item.removed << item.enabledChanged << item.stateChanged << item.added;
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
const QDBusArgument &operator>>(const QDBusArgument &argument, GMenuActionsChange &item)
|
||||
{
|
||||
argument.beginStructure();
|
||||
argument >> item.removed >> item.enabledChanged >> item.stateChanged >> item.added;
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
void GDBusMenuTypes_register()
|
||||
{
|
||||
static bool registered = false;
|
||||
if (registered) {
|
||||
return;
|
||||
}
|
||||
|
||||
qDBusRegisterMetaType<GMenuItem>();
|
||||
qDBusRegisterMetaType<GMenuItemList>();
|
||||
|
||||
qDBusRegisterMetaType<GMenuSection>();
|
||||
|
||||
qDBusRegisterMetaType<GMenuChange>();
|
||||
qDBusRegisterMetaType<GMenuChangeList>();
|
||||
|
||||
qDBusRegisterMetaType<GMenuAction>();
|
||||
qDBusRegisterMetaType<GMenuActionMap>();
|
||||
|
||||
qDBusRegisterMetaType<GMenuActionsChange>();
|
||||
qDBusRegisterMetaType<StringBoolMap>();
|
||||
|
||||
registered = true;
|
||||
}
|
||||
@ -1,89 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QDBusSignature>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QVariant>
|
||||
|
||||
class QDBusArgument;
|
||||
|
||||
// Various
|
||||
using VariantMapList = QList<QVariantMap>;
|
||||
Q_DECLARE_METATYPE(VariantMapList);
|
||||
|
||||
using StringBoolMap = QMap<QString, bool>;
|
||||
Q_DECLARE_METATYPE(StringBoolMap);
|
||||
|
||||
// Menu item itself (Start method)
|
||||
struct GMenuItem {
|
||||
uint id;
|
||||
uint section;
|
||||
VariantMapList items;
|
||||
};
|
||||
Q_DECLARE_METATYPE(GMenuItem);
|
||||
|
||||
QDBusArgument &operator<<(QDBusArgument &argument, const GMenuItem &item);
|
||||
const QDBusArgument &operator>>(const QDBusArgument &argument, GMenuItem &item);
|
||||
|
||||
using GMenuItemList = QList<GMenuItem>;
|
||||
Q_DECLARE_METATYPE(GMenuItemList);
|
||||
|
||||
// Information about what section or submenu to use for a particular entry
|
||||
struct GMenuSection {
|
||||
uint subscription;
|
||||
uint menu;
|
||||
};
|
||||
Q_DECLARE_METATYPE(GMenuSection);
|
||||
|
||||
QDBusArgument &operator<<(QDBusArgument &argument, const GMenuSection &item);
|
||||
const QDBusArgument &operator>>(const QDBusArgument &argument, GMenuSection &item);
|
||||
|
||||
// Changes of a menu item (Changed signal)
|
||||
struct GMenuChange {
|
||||
uint subscription;
|
||||
uint menu;
|
||||
|
||||
uint changePosition;
|
||||
uint itemsToRemoveCount;
|
||||
VariantMapList itemsToInsert;
|
||||
};
|
||||
Q_DECLARE_METATYPE(GMenuChange);
|
||||
|
||||
QDBusArgument &operator<<(QDBusArgument &argument, const GMenuChange &item);
|
||||
const QDBusArgument &operator>>(const QDBusArgument &argument, GMenuChange &item);
|
||||
|
||||
using GMenuChangeList = QList<GMenuChange>;
|
||||
Q_DECLARE_METATYPE(GMenuChangeList);
|
||||
|
||||
// An application action
|
||||
struct GMenuAction {
|
||||
bool enabled;
|
||||
QDBusSignature signature;
|
||||
QVariantList state;
|
||||
};
|
||||
Q_DECLARE_METATYPE(GMenuAction);
|
||||
|
||||
QDBusArgument &operator<<(QDBusArgument &argument, const GMenuAction &item);
|
||||
const QDBusArgument &operator>>(const QDBusArgument &argument, GMenuAction &item);
|
||||
|
||||
using GMenuActionMap = QMap<QString, GMenuAction>;
|
||||
Q_DECLARE_METATYPE(GMenuActionMap);
|
||||
|
||||
struct GMenuActionsChange {
|
||||
QStringList removed;
|
||||
QMap<QString, bool> enabledChanged;
|
||||
QVariantMap stateChanged;
|
||||
GMenuActionMap added;
|
||||
};
|
||||
Q_DECLARE_METATYPE(GMenuActionsChange);
|
||||
|
||||
QDBusArgument &operator<<(QDBusArgument &argument, const GMenuActionsChange &item);
|
||||
const QDBusArgument &operator>>(const QDBusArgument &argument, GMenuActionsChange &item);
|
||||
|
||||
void GDBusMenuTypes_register();
|
||||
@ -1,50 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Exec=gmenudbusmenuproxy
|
||||
Name=GMenuDBusMenuProxy
|
||||
Name[ar]=وكيل قائمة D-Bus لـ GMenu.
|
||||
Name[ast]=GMenuDBusMenuProxy
|
||||
Name[az]=GMenuDBusMenuProxy
|
||||
Name[ca]=GMenuDBusMenuProxy
|
||||
Name[ca@valencia]=GMenuDBusMenuProxy
|
||||
Name[da]=GMenuDBusMenuProxy
|
||||
Name[de]=GMenuDBusMenuProxy
|
||||
Name[el]=GMenuDBusMenuProxy
|
||||
Name[en_GB]=GMenuDBusMenuProxy
|
||||
Name[es]=GMenuDBusMenuProxy
|
||||
Name[et]=GMenuDBusMenuProxy
|
||||
Name[eu]=GMenuDBusMenuProxy
|
||||
Name[fi]=GMenuDBusMenuProxy
|
||||
Name[fr]=GMenuDBusMenuProxy
|
||||
Name[gl]=Proxy de menú por D-Bus para GMenu.
|
||||
Name[hi]=जीमेन्यूडीबसमेन्यूप्रॉक्सी
|
||||
Name[hu]=GMenuDBusMenuProxy
|
||||
Name[ia]=GMenuDBusMenuProxy
|
||||
Name[id]=GMenuDBusMenuProxy
|
||||
Name[it]=GMenuDBusMenuProxy
|
||||
Name[ko]=GMenuDBusMenuProxy
|
||||
Name[lt]=GMenuDBusMenuProxy
|
||||
Name[ml]=ജിമെനുഡിബസ്മെനുപ്രോക്സി
|
||||
Name[nl]=GMenuDBusMenuProxy
|
||||
Name[nn]=GMenuDBusMenuProxy
|
||||
Name[pa]=GMenuDBusMenuProxy
|
||||
Name[pl]=GMenuDBusMenuProxy
|
||||
Name[pt]=GMenuDBusMenuProxy
|
||||
Name[pt_BR]=GMenuDBusMenuProxy
|
||||
Name[ro]=GMenuDBusMenuProxy
|
||||
Name[ru]=GMenuDBusMenuProxy
|
||||
Name[sk]=GMenuDBusMenuProxy
|
||||
Name[sl]=GMenuDBusMenuProxy
|
||||
Name[sv]=GMenuDBusMenuProxy
|
||||
Name[ta]=GMenuDBusMenuProxy
|
||||
Name[tr]=GMenuDBusMenuProxy
|
||||
Name[uk]=Проксі-меню GMenu D-Bus
|
||||
Name[vi]=GMenuDBusMenuProxy
|
||||
Name[x-test]=xxGMenuDBusMenuProxyxx
|
||||
Name[zh_CN]=GMenuDBusMenuProxy
|
||||
Name[zh_TW]=GMenuDBusMenuProxy
|
||||
Type=Application
|
||||
X-KDE-StartupNotify=false
|
||||
NoDisplay=true
|
||||
OnlyShowIn=KDE;
|
||||
X-KDE-autostart-phase=1
|
||||
X-systemd-skip=true
|
||||
@ -1,308 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include "icons.h"
|
||||
|
||||
#include <QHash>
|
||||
#include <QRegularExpression>
|
||||
|
||||
QString Icons::actionIcon(const QString &actionName)
|
||||
{
|
||||
QString icon;
|
||||
|
||||
QString action = actionName;
|
||||
|
||||
if (action.isEmpty()) {
|
||||
return icon;
|
||||
}
|
||||
|
||||
static const QHash<QString, QString> s_icons{
|
||||
{QStringLiteral("new"), QStringLiteral("document-new")}, // appmenu-gtk-module "New"
|
||||
{QStringLiteral("image-new"), QStringLiteral("document-new")}, // Gimp "New" item
|
||||
{QStringLiteral("adddirect"), QStringLiteral("document-new")}, // LibreOffice "New" item
|
||||
{QStringLiteral("filenew"), QStringLiteral("document-new")}, // Pluma "New" item
|
||||
{QStringLiteral("new-window"), QStringLiteral("window-new")},
|
||||
{QStringLiteral("newwindow"), QStringLiteral("window-new")},
|
||||
{QStringLiteral("yelp-window-new"), QStringLiteral("window-new")}, // Gnome help
|
||||
{QStringLiteral("new-tab"), QStringLiteral("tab-new")},
|
||||
{QStringLiteral("open"), QStringLiteral("document-open")},
|
||||
{QStringLiteral("open-location"), QStringLiteral("document-open-remote")},
|
||||
{QStringLiteral("openremote"), QStringLiteral("document-open-remote")},
|
||||
{QStringLiteral("save"), QStringLiteral("document-save")},
|
||||
{QStringLiteral("save-as"), QStringLiteral("document-save-as")},
|
||||
{QStringLiteral("saveas"), QStringLiteral("document-save-as")},
|
||||
{QStringLiteral("save-all"), QStringLiteral("document-save-all")},
|
||||
{QStringLiteral("saveall"), QStringLiteral("document-save-all")},
|
||||
{QStringLiteral("import"), QStringLiteral("document-import")},
|
||||
{QStringLiteral("export"), QStringLiteral("document-export")},
|
||||
{QStringLiteral("exportto"), QStringLiteral("document-export")}, // LibreOffice
|
||||
{QStringLiteral("exporttopdf"), QStringLiteral("viewpdf")}, // LibreOffice, the icon it uses but the name is quite random
|
||||
{QStringLiteral("webhtml"), QStringLiteral("text-html")}, // LibreOffice
|
||||
{QStringLiteral("printpreview"), QStringLiteral("document-print-preview")},
|
||||
{QStringLiteral("print-preview"), QStringLiteral("document-print-preview")},
|
||||
{QStringLiteral("print"), QStringLiteral("document-print")},
|
||||
{QStringLiteral("print-gtk"), QStringLiteral("document-print")}, // Gimp
|
||||
{QStringLiteral("mail-image"), QStringLiteral("mail-message-new")}, // Gimp
|
||||
{QStringLiteral("sendmail"), QStringLiteral("mail-message-new")}, // LibreOffice
|
||||
{QStringLiteral("sendviabluetooth"), QStringLiteral("preferences-system-bluetooth")}, // LibreOffice
|
||||
{QStringLiteral("sendviabluetooth"), QStringLiteral("preferences-system-bluetooth")}, // LibreOffice
|
||||
{QStringLiteral("document-properties"), QStringLiteral("document-properties")},
|
||||
{QStringLiteral("close"), QStringLiteral("document-close")}, // appmenu-gtk-module "Close"
|
||||
{QStringLiteral("closedoc"), QStringLiteral("document-close")},
|
||||
{QStringLiteral("close-all"), QStringLiteral("document-close")},
|
||||
{QStringLiteral("closeall"), QStringLiteral("document-close")},
|
||||
{QStringLiteral("closewin"), QStringLiteral("window-close")}, // LibreOffice
|
||||
{QStringLiteral("quit"), QStringLiteral("application-exit")},
|
||||
|
||||
{QStringLiteral("undo"), QStringLiteral("edit-undo")},
|
||||
{QStringLiteral("redo"), QStringLiteral("edit-redo")},
|
||||
{QStringLiteral("revert"), QStringLiteral("document-revert")},
|
||||
{QStringLiteral("cut"), QStringLiteral("edit-cut")},
|
||||
{QStringLiteral("copy"), QStringLiteral("edit-copy")},
|
||||
{QStringLiteral("paste"), QStringLiteral("edit-paste")},
|
||||
{QStringLiteral("duplicate"), QStringLiteral("edit-duplicate")},
|
||||
|
||||
{QStringLiteral("preferences"), QStringLiteral("settings-configure")},
|
||||
{QStringLiteral("optionstreedialog"), QStringLiteral("settings-configure")}, // LibreOffice
|
||||
{QStringLiteral("keyboard-shortcuts"), QStringLiteral("configure-shortcuts")},
|
||||
|
||||
{QStringLiteral("fullscreen"), QStringLiteral("view-fullscreen")},
|
||||
|
||||
{QStringLiteral("find"), QStringLiteral("edit-find")},
|
||||
{QStringLiteral("searchfind"), QStringLiteral("edit-find")},
|
||||
{QStringLiteral("replace"), QStringLiteral("edit-find-replace")},
|
||||
{QStringLiteral("searchreplace"), QStringLiteral("edit-find-replace")}, // LibreOffice
|
||||
{QStringLiteral("searchdialog"), QStringLiteral("edit-find-replace")}, // LibreOffice
|
||||
{QStringLiteral("find-replace"), QStringLiteral("edit-find-replace")}, // Inkscape
|
||||
{QStringLiteral("select-all"), QStringLiteral("edit-select-all")},
|
||||
{QStringLiteral("selectall"), QStringLiteral("edit-select-all")},
|
||||
{QStringLiteral("select-none"), QStringLiteral("edit-select-invert")},
|
||||
{QStringLiteral("select-invert"), QStringLiteral("edit-select-invert")},
|
||||
{QStringLiteral("invert-selection"), QStringLiteral("edit-select-invert")}, // Inkscape
|
||||
{QStringLiteral("check-spelling"), QStringLiteral("tools-check-spelling")},
|
||||
{QStringLiteral("set-language"), QStringLiteral("set-language")},
|
||||
|
||||
{QStringLiteral("increasesize"), QStringLiteral("zoom-in")},
|
||||
{QStringLiteral("decreasesize"), QStringLiteral("zoom-out")},
|
||||
{QStringLiteral("zoom-in"), QStringLiteral("zoom-in")},
|
||||
{QStringLiteral("zoom-out"), QStringLiteral("zoom-out")},
|
||||
{QStringLiteral("zoomfit"), QStringLiteral("zoom-fit-best")},
|
||||
{QStringLiteral("zoom-fit-in"), QStringLiteral("zoom-fit-best")},
|
||||
{QStringLiteral("show-guides"), QStringLiteral("show-guides")},
|
||||
{QStringLiteral("show-grid"), QStringLiteral("show-grid")},
|
||||
|
||||
{QStringLiteral("rotateclockwise"), QStringLiteral("object-rotate-right")},
|
||||
{QStringLiteral("rotatecounterclockwise"), QStringLiteral("object-rotate-left")},
|
||||
{QStringLiteral("fliphorizontally"), QStringLiteral("object-flip-horizontal")},
|
||||
{QStringLiteral("image-flip-horizontal"), QStringLiteral("object-flip-horizontal")},
|
||||
{QStringLiteral("flipvertically"), QStringLiteral("object-flip-vertical")},
|
||||
{QStringLiteral("image-flip-vertical"), QStringLiteral("object-flip-vertical")},
|
||||
{QStringLiteral("image-scale"), QStringLiteral("transform-scale")},
|
||||
|
||||
{QStringLiteral("bold"), QStringLiteral("format-text-bold")},
|
||||
{QStringLiteral("italic"), QStringLiteral("format-text-italic")},
|
||||
{QStringLiteral("underline"), QStringLiteral("format-text-underline")},
|
||||
{QStringLiteral("strikeout"), QStringLiteral("format-text-strikethrough")},
|
||||
{QStringLiteral("superscript"), QStringLiteral("format-text-superscript")},
|
||||
{QStringLiteral("subscript"), QStringLiteral("format-text-subscript")},
|
||||
// "grow" is a bit unspecific to always set it to "grow font", so use the exact ID here
|
||||
{QStringLiteral(".uno:Grow"), QStringLiteral("format-font-size-more")}, // LibreOffice
|
||||
{QStringLiteral(".uno:Shrink"), QStringLiteral("format-font-size-less")}, // LibreOffice
|
||||
// also a bit unspecific?
|
||||
{QStringLiteral("alignleft"), QStringLiteral("format-justify-left")},
|
||||
{QStringLiteral("alignhorizontalcenter"), QStringLiteral("format-justify-center")},
|
||||
{QStringLiteral("alignright"), QStringLiteral("format-justify-right")},
|
||||
{QStringLiteral("alignjustified"), QStringLiteral("format-justify-fill")},
|
||||
{QStringLiteral("incrementindent"), QStringLiteral("format-indent-more")},
|
||||
{QStringLiteral("decrementindent"), QStringLiteral("format-indent-less")},
|
||||
{QStringLiteral("defaultbullet"), QStringLiteral("format-list-unordered")}, // LibreOffice
|
||||
{QStringLiteral("defaultnumbering"), QStringLiteral("format-list-ordered")}, // LibreOffice
|
||||
|
||||
{QStringLiteral("sortascending"), QStringLiteral("view-sort-ascending")},
|
||||
{QStringLiteral("sortdescending"), QStringLiteral("view-sort-descending")},
|
||||
|
||||
{QStringLiteral("autopilotmenu"), QStringLiteral("tools-wizard")}, // LibreOffice
|
||||
|
||||
{QStringLiteral("layers-new"), QStringLiteral("layer-new")},
|
||||
{QStringLiteral("layers-duplicate"), QStringLiteral("layer-duplicate")},
|
||||
{QStringLiteral("layers-delete"), QStringLiteral("layer-delete")},
|
||||
{QStringLiteral("layers-anchor"), QStringLiteral("anchor")},
|
||||
|
||||
{QStringLiteral("slideshow"), QStringLiteral("media-playback-start")}, // Gwenview uses this icon for that
|
||||
{QStringLiteral("playvideo"), QStringLiteral("media-playback-start")},
|
||||
|
||||
{QStringLiteral("addtags"), QStringLiteral("tag-new")},
|
||||
{QStringLiteral("newevent"), QStringLiteral("appointment-new")},
|
||||
|
||||
{QStringLiteral("previous-document"), QStringLiteral("go-previous")},
|
||||
{QStringLiteral("prevphoto"), QStringLiteral("go-previous")},
|
||||
{QStringLiteral("next-document"), QStringLiteral("go-next")},
|
||||
{QStringLiteral("nextphoto"), QStringLiteral("go-next")},
|
||||
|
||||
{QStringLiteral("redeye"), QStringLiteral("redeyes")},
|
||||
{QStringLiteral("crop"), QStringLiteral("transform-crop")},
|
||||
{QStringLiteral("move"), QStringLiteral("transform-move")},
|
||||
{QStringLiteral("rotate"), QStringLiteral("transform-rotate")},
|
||||
{QStringLiteral("scale"), QStringLiteral("transform-scale")},
|
||||
{QStringLiteral("shear"), QStringLiteral("transform-shear")},
|
||||
{QStringLiteral("flip"), QStringLiteral("object-flip-horizontal")},
|
||||
{QStringLiteral("flag"), QStringLiteral("flag-red")}, // is there a "mark" or "important" icon that isn't email?
|
||||
|
||||
{QStringLiteral("tools-measure"), QStringLiteral("measure")},
|
||||
{QStringLiteral("tools-text"), QStringLiteral("draw-text")},
|
||||
{QStringLiteral("tools-color-picker"), QStringLiteral("color-picker")},
|
||||
{QStringLiteral("tools-paintbrush"), QStringLiteral("draw-brush")},
|
||||
{QStringLiteral("tools-eraser"), QStringLiteral("draw-eraser")},
|
||||
{QStringLiteral("tools-paintbrush"), QStringLiteral("draw-brush")},
|
||||
|
||||
{QStringLiteral("help"), QStringLiteral("help-contents")},
|
||||
{QStringLiteral("helpindex"), QStringLiteral("help-contents")},
|
||||
{QStringLiteral("contents"), QStringLiteral("help-contents")},
|
||||
{QStringLiteral("helpcontents"), QStringLiteral("help-contents")},
|
||||
{QStringLiteral("context-help"), QStringLiteral("help-whatsthis")},
|
||||
{QStringLiteral("extendedhelp"), QStringLiteral("help-whatsthis")}, // LibreOffice
|
||||
{QStringLiteral("helpreportproblem"), QStringLiteral("tools-report-bug")},
|
||||
{QStringLiteral("sendfeedback"), QStringLiteral("tools-report-bug")}, // LibreOffice
|
||||
{QStringLiteral("about"), QStringLiteral("help-about")},
|
||||
|
||||
{QStringLiteral("emptytrash"), QStringLiteral("trash-empty")},
|
||||
{QStringLiteral("movetotrash"), QStringLiteral("user-trash-symbolic")},
|
||||
|
||||
// Gnome help
|
||||
{QStringLiteral("yelp-application-larger-text"), QStringLiteral("format-font-size-more")},
|
||||
{QStringLiteral("yelp-application-smaller-text"), QStringLiteral("format-font-size-less")}, // LibreOffice
|
||||
|
||||
// LibreOffice documents in its New menu
|
||||
{QStringLiteral("private:factory/swriter"), QStringLiteral("application-vnd.oasis.opendocument.text")},
|
||||
{QStringLiteral("private:factory/scalc"), QStringLiteral("application-vnd.oasis.opendocument.spreadsheet")},
|
||||
{QStringLiteral("private:factory/simpress"), QStringLiteral("application-vnd.oasis.opendocument.presentation")},
|
||||
{QStringLiteral("private:factory/sdraw"), QStringLiteral("application-vnd.oasis.opendocument.graphics")},
|
||||
{QStringLiteral("private:factory/swriter/web"), QStringLiteral("text-html")},
|
||||
{QStringLiteral("private:factory/smath"), QStringLiteral("application-vnd.oasis.opendocument.formula")},
|
||||
};
|
||||
|
||||
// Sometimes we get additional arguments (?slot=123) we don't care about
|
||||
const int questionMarkIndex = action.indexOf(QLatin1Char('?'));
|
||||
if (questionMarkIndex > -1) {
|
||||
action.truncate(questionMarkIndex);
|
||||
}
|
||||
|
||||
icon = s_icons.value(action);
|
||||
|
||||
if (icon.isEmpty()) {
|
||||
const int dotIndex = action.indexOf(QLatin1Char('.')); // app., win., or unity. prefix
|
||||
|
||||
QString prefix;
|
||||
if (dotIndex > -1) {
|
||||
prefix = action.left(dotIndex);
|
||||
|
||||
action = action.mid(dotIndex + 1);
|
||||
}
|
||||
|
||||
// appmenu-gtk-module
|
||||
if (prefix == QLatin1String("unity")) {
|
||||
// Remove superfluous hyphens added by appmenu-gtk-module
|
||||
// First remove multiple subsequent ones
|
||||
static QRegularExpression subsequentHyphenRegExp(QStringLiteral("-{2,}"));
|
||||
action.replace(subsequentHyphenRegExp, QStringLiteral("-"));
|
||||
|
||||
// now we can be sure we only have a single hyphen at the start or end, remove it if needed
|
||||
if (action.startsWith(QLatin1Char('-'))) {
|
||||
action.remove(0, 1);
|
||||
}
|
||||
if (action.endsWith(QLatin1Char('-'))) {
|
||||
action.chop(1);
|
||||
}
|
||||
|
||||
// It also turns accelerators (&) into hyphens, so remove any hyphen that comes before
|
||||
// a lower-case letter ("mid sentence"), e.g. "P-references"
|
||||
static QRegularExpression strayHyphenRegExp(QStringLiteral("-(?=[a-z]+)"));
|
||||
action.remove(strayHyphenRegExp);
|
||||
}
|
||||
|
||||
icon = s_icons.value(action);
|
||||
}
|
||||
|
||||
if (icon.isEmpty()) {
|
||||
static const auto s_dup1Prefix = QStringLiteral("dup:1:"); // can it be dup2 also?
|
||||
if (action.startsWith(s_dup1Prefix)) {
|
||||
action = action.mid(s_dup1Prefix.length());
|
||||
}
|
||||
|
||||
static const auto s_unoPrefix = QStringLiteral(".uno:"); // LibreOffice with appmenu-gtk
|
||||
if (action.startsWith(s_unoPrefix)) {
|
||||
action = action.mid(s_unoPrefix.length());
|
||||
}
|
||||
|
||||
// LibreOffice's "Open" entry is always "OpenFromAppname" so we just chop that off
|
||||
if (action.startsWith(QLatin1String("OpenFrom"))) {
|
||||
action.truncate(4); // basically "Open"
|
||||
}
|
||||
|
||||
icon = s_icons.value(action);
|
||||
}
|
||||
|
||||
if (icon.isEmpty()) {
|
||||
static const auto s_commonPrefix = QStringLiteral("Common");
|
||||
if (action.startsWith(s_commonPrefix)) {
|
||||
action = action.mid(s_commonPrefix.length());
|
||||
}
|
||||
|
||||
icon = s_icons.value(action);
|
||||
}
|
||||
|
||||
if (icon.isEmpty()) {
|
||||
static const auto s_prefixes = QStringList{
|
||||
// Gimp with appmenu-gtk
|
||||
QStringLiteral("file-"),
|
||||
QStringLiteral("edit-"),
|
||||
QStringLiteral("view-"),
|
||||
QStringLiteral("image-"),
|
||||
QStringLiteral("layers-"),
|
||||
QStringLiteral("colors-"),
|
||||
QStringLiteral("tools-"),
|
||||
QStringLiteral("plug-in-"),
|
||||
QStringLiteral("windows-"),
|
||||
QStringLiteral("dialogs-"),
|
||||
QStringLiteral("help-"),
|
||||
};
|
||||
|
||||
for (const QString &prefix : s_prefixes) {
|
||||
if (action.startsWith(prefix)) {
|
||||
action = action.mid(prefix.length());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
icon = s_icons.value(action);
|
||||
}
|
||||
|
||||
if (icon.isEmpty()) {
|
||||
action = action.toLower();
|
||||
icon = s_icons.value(action);
|
||||
}
|
||||
|
||||
if (icon.isEmpty()) {
|
||||
static const auto s_prefixes = QStringList{
|
||||
// Pluma with appmenu-gtk
|
||||
QStringLiteral("file"),
|
||||
QStringLiteral("edit"),
|
||||
QStringLiteral("view"),
|
||||
QStringLiteral("help"),
|
||||
};
|
||||
|
||||
for (const QString &prefix : s_prefixes) {
|
||||
if (action.startsWith(prefix)) {
|
||||
action = action.mid(prefix.length());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
icon = s_icons.value(action);
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace Icons
|
||||
{
|
||||
QString actionIcon(const QString &actionName);
|
||||
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QSessionManager>
|
||||
|
||||
#include <KWindowSystem>
|
||||
|
||||
#include "menuproxy.h"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
qputenv("QT_QPA_PLATFORM", "xcb");
|
||||
|
||||
QGuiApplication::setDesktopSettingsAware(false);
|
||||
|
||||
QGuiApplication app(argc, argv);
|
||||
|
||||
if (!KWindowSystem::isPlatformX11()) {
|
||||
qFatal("qdbusmenuproxy is only useful XCB. Aborting");
|
||||
}
|
||||
|
||||
auto disableSessionManagement = [](QSessionManager &sm) {
|
||||
sm.setRestartHint(QSessionManager::RestartNever);
|
||||
};
|
||||
QObject::connect(&app, &QGuiApplication::commitDataRequest, disableSessionManagement);
|
||||
QObject::connect(&app, &QGuiApplication::saveStateRequest, disableSessionManagement);
|
||||
|
||||
app.setQuitOnLastWindowClosed(false);
|
||||
|
||||
MenuProxy proxy;
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
@ -1,328 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include "menu.h"
|
||||
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusMessage>
|
||||
#include <QDBusPendingCallWatcher>
|
||||
#include <QDBusPendingReply>
|
||||
#include <QDebug>
|
||||
#include <QVariantList>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
static const QString s_orgGtkMenus = QStringLiteral("org.gtk.Menus");
|
||||
|
||||
Menu::Menu(const QString &serviceName, const QString &objectPath, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_serviceName(serviceName)
|
||||
, m_objectPath(objectPath)
|
||||
{
|
||||
Q_ASSERT(!serviceName.isEmpty());
|
||||
Q_ASSERT(!m_objectPath.isEmpty());
|
||||
|
||||
if (!QDBusConnection::sessionBus()
|
||||
.connect(m_serviceName, m_objectPath, s_orgGtkMenus, QStringLiteral("Changed"), this, SLOT(onMenuChanged(GMenuChangeList)))) {
|
||||
qDebug() << "Failed to subscribe to menu changes for" << parent << "on" << serviceName << "at" << objectPath;
|
||||
}
|
||||
}
|
||||
|
||||
Menu::~Menu() = default;
|
||||
|
||||
void Menu::cleanup()
|
||||
{
|
||||
stop(m_subscriptions);
|
||||
}
|
||||
|
||||
void Menu::start(uint id)
|
||||
{
|
||||
if (m_subscriptions.contains(id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO watch service disappearing?
|
||||
|
||||
// dbus-send --print-reply --session --dest=:1.103 /org/libreoffice/window/104857641/menus/menubar org.gtk.Menus.Start array:uint32:0
|
||||
|
||||
QDBusMessage msg = QDBusMessage::createMethodCall(m_serviceName, m_objectPath, s_orgGtkMenus, QStringLiteral("Start"));
|
||||
msg.setArguments({QVariant::fromValue(QList<uint>{id})});
|
||||
|
||||
QDBusPendingReply<GMenuItemList> reply = QDBusConnection::sessionBus().asyncCall(msg);
|
||||
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this);
|
||||
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, id](QDBusPendingCallWatcher *watcher) {
|
||||
QScopedPointer<QDBusPendingCallWatcher, QScopedPointerDeleteLater> watcherPtr(watcher);
|
||||
|
||||
QDBusPendingReply<GMenuItemList> reply = *watcherPtr;
|
||||
if (reply.isError()) {
|
||||
qDebug() << "Failed to start subscription to" << id << "on" << m_serviceName << "at" << m_objectPath << reply.error();
|
||||
emit failedToSubscribe(id);
|
||||
} else {
|
||||
const bool hadMenu = !m_menus.isEmpty();
|
||||
|
||||
const auto menus = reply.value();
|
||||
for (const auto &menu : menus) {
|
||||
m_menus[menu.id].append(menus);
|
||||
}
|
||||
|
||||
// LibreOffice on startup fails to give us some menus right away, we'll also subscribe in onMenuChanged() if necessary
|
||||
if (menus.isEmpty()) {
|
||||
qDebug() << "Got an empty menu for" << id << "on" << m_serviceName << "at" << m_objectPath;
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO are we subscribed to all it returns or just to the ones we requested?
|
||||
m_subscriptions.append(id);
|
||||
|
||||
// do we have a menu now? let's tell everyone
|
||||
if (!hadMenu && !m_menus.isEmpty()) {
|
||||
emit menuAppeared();
|
||||
}
|
||||
|
||||
emit subscribed(id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Menu::stop(const QList<uint> &ids)
|
||||
{
|
||||
QDBusMessage msg = QDBusMessage::createMethodCall(m_serviceName, m_objectPath, s_orgGtkMenus, QStringLiteral("End"));
|
||||
msg.setArguments({
|
||||
QVariant::fromValue(ids) // don't let it unwrap it, hence in a variant
|
||||
});
|
||||
|
||||
QDBusPendingReply<void> reply = QDBusConnection::sessionBus().asyncCall(msg);
|
||||
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this);
|
||||
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, ids](QDBusPendingCallWatcher *watcher) {
|
||||
QDBusPendingReply<void> reply = *watcher;
|
||||
if (reply.isError()) {
|
||||
qDebug() << "Failed to stop subscription to" << ids << "on" << m_serviceName << "at" << m_objectPath << reply.error();
|
||||
} else {
|
||||
// remove all subscriptions that we unsubscribed from
|
||||
// TODO is there a nicer algorithm for that?
|
||||
// TODO remove all m_menus also?
|
||||
m_subscriptions.erase(
|
||||
std::remove_if(m_subscriptions.begin(), m_subscriptions.end(), [ids](uint subscription) {
|
||||
return ids.contains(subscription);
|
||||
}),
|
||||
m_subscriptions.end());
|
||||
|
||||
if (m_subscriptions.isEmpty()) {
|
||||
emit menuDisappeared();
|
||||
}
|
||||
}
|
||||
watcher->deleteLater();
|
||||
});
|
||||
}
|
||||
|
||||
bool Menu::hasMenu() const
|
||||
{
|
||||
return !m_menus.isEmpty();
|
||||
}
|
||||
|
||||
bool Menu::hasSubscription(uint subscription) const
|
||||
{
|
||||
return m_subscriptions.contains(subscription);
|
||||
}
|
||||
|
||||
GMenuItem Menu::getSection(int id, bool *ok) const
|
||||
{
|
||||
int subscription;
|
||||
int section;
|
||||
int index;
|
||||
Utils::intToTreeStructure(id, subscription, section, index);
|
||||
return getSection(subscription, section, ok);
|
||||
}
|
||||
|
||||
GMenuItem Menu::getSection(int subscription, int section, bool *ok) const
|
||||
{
|
||||
const auto menu = m_menus.value(subscription);
|
||||
|
||||
auto it = std::find_if(menu.begin(), menu.end(), [section](const GMenuItem &item) {
|
||||
return item.section == section;
|
||||
});
|
||||
|
||||
if (it == menu.end()) {
|
||||
if (ok) {
|
||||
*ok = false;
|
||||
}
|
||||
return GMenuItem();
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
*ok = true;
|
||||
}
|
||||
return *it;
|
||||
}
|
||||
|
||||
QVariantMap Menu::getItem(int id) const
|
||||
{
|
||||
int subscription;
|
||||
int section;
|
||||
int index;
|
||||
Utils::intToTreeStructure(id, subscription, section, index);
|
||||
return getItem(subscription, section, index);
|
||||
}
|
||||
|
||||
QVariantMap Menu::getItem(int subscription, int sectionId, int index) const
|
||||
{
|
||||
bool ok;
|
||||
const GMenuItem section = getSection(subscription, sectionId, &ok);
|
||||
|
||||
if (!ok) {
|
||||
return QVariantMap();
|
||||
}
|
||||
|
||||
const auto items = section.items;
|
||||
|
||||
if (items.count() < index) {
|
||||
qDebug() << "Cannot get action" << subscription << sectionId << index << "which is out of bounds";
|
||||
return QVariantMap();
|
||||
}
|
||||
|
||||
// 0 is the menu itself, items start at 1
|
||||
return items.at(index - 1);
|
||||
}
|
||||
|
||||
void Menu::onMenuChanged(const GMenuChangeList &changes)
|
||||
{
|
||||
const bool hadMenu = !m_menus.isEmpty();
|
||||
|
||||
QVector<uint> dirtyMenus;
|
||||
QVector<uint> dirtyItems;
|
||||
|
||||
for (const auto &change : changes) {
|
||||
auto updateSection = [&](GMenuItem §ion) {
|
||||
// Check if the amount of inserted items is identical to the items to be removed,
|
||||
// just update the existing items and signal a change for that.
|
||||
// LibreOffice tends to do that e.g. to update its Undo menu entry
|
||||
if (change.itemsToRemoveCount == change.itemsToInsert.count()) {
|
||||
for (int i = 0; i < change.itemsToInsert.count(); ++i) {
|
||||
const auto &newItem = change.itemsToInsert.at(i);
|
||||
|
||||
section.items[change.changePosition + i] = newItem;
|
||||
|
||||
// 0 is the menu itself, items start at 1
|
||||
dirtyItems.append(Utils::treeStructureToInt(change.subscription, change.menu, change.changePosition + i + 1));
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < change.itemsToRemoveCount; ++i) {
|
||||
section.items.removeAt(change.changePosition); // TODO bounds check
|
||||
}
|
||||
|
||||
for (int i = 0; i < change.itemsToInsert.count(); ++i) {
|
||||
section.items.insert(change.changePosition + i, change.itemsToInsert.at(i));
|
||||
}
|
||||
|
||||
dirtyMenus.append(Utils::treeStructureToInt(change.subscription, change.menu, 0));
|
||||
}
|
||||
};
|
||||
|
||||
// shouldn't happen, it says only Start() subscribes to changes
|
||||
if (!m_subscriptions.contains(change.subscription)) {
|
||||
qDebug() << "Got menu change for menu" << change.subscription << "that we are not subscribed to, subscribing now";
|
||||
// LibreOffice doesn't give us a menu right away but takes a while and then signals us a change
|
||||
start(change.subscription);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto &menu = m_menus[change.subscription];
|
||||
|
||||
bool sectionFound = false;
|
||||
// TODO findSectionRef
|
||||
for (GMenuItem §ion : menu) {
|
||||
if (section.section != change.menu) {
|
||||
continue;
|
||||
}
|
||||
|
||||
qDebug() << "Updating existing section" << change.menu << "in subscription" << change.subscription;
|
||||
|
||||
sectionFound = true;
|
||||
updateSection(section);
|
||||
break;
|
||||
}
|
||||
|
||||
// Insert new section
|
||||
if (!sectionFound) {
|
||||
qDebug() << "Creating new section" << change.menu << "in subscription" << change.subscription;
|
||||
|
||||
if (change.itemsToRemoveCount > 0) {
|
||||
qDebug() << "Menu change requested to remove items from a new (and as such empty) section";
|
||||
}
|
||||
|
||||
GMenuItem newSection;
|
||||
newSection.id = change.subscription;
|
||||
newSection.section = change.menu;
|
||||
updateSection(newSection);
|
||||
menu.append(newSection);
|
||||
}
|
||||
}
|
||||
|
||||
// do we have a menu now? let's tell everyone
|
||||
if (!hadMenu && !m_menus.isEmpty()) {
|
||||
emit menuAppeared();
|
||||
} else if (hadMenu && m_menus.isEmpty()) {
|
||||
emit menuDisappeared();
|
||||
}
|
||||
|
||||
if (!dirtyItems.isEmpty()) {
|
||||
emit itemsChanged(dirtyItems);
|
||||
}
|
||||
|
||||
emit menusChanged(dirtyMenus);
|
||||
}
|
||||
|
||||
void Menu::actionsChanged(const QStringList &dirtyActions, const QString &prefix)
|
||||
{
|
||||
auto forEachMenuItem = [this](const std::function<bool(int subscription, int section, int index, const QVariantMap &item)> &cb) {
|
||||
for (auto it = m_menus.constBegin(), end = m_menus.constEnd(); it != end; ++it) {
|
||||
const int subscription = it.key();
|
||||
|
||||
for (const auto &menu : it.value()) {
|
||||
const int section = menu.section;
|
||||
|
||||
int count = 0;
|
||||
|
||||
const auto items = menu.items;
|
||||
for (const auto &item : items) {
|
||||
++count; // 0 is a menu, entries start at 1
|
||||
|
||||
if (!cb(subscription, section, count, item)) {
|
||||
goto loopExit; // hell yeah
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loopExit: // loop exit
|
||||
return;
|
||||
};
|
||||
|
||||
// now find in which menus these actions are and emit a change accordingly
|
||||
QVector<uint> dirtyItems;
|
||||
|
||||
for (const QString &action : dirtyActions) {
|
||||
const QString prefixedAction = prefix + action;
|
||||
|
||||
forEachMenuItem([&prefixedAction, &dirtyItems](int subscription, int section, int index, const QVariantMap &item) {
|
||||
const QString actionName = Utils::itemActionName(item);
|
||||
|
||||
if (actionName == prefixedAction) {
|
||||
dirtyItems.append(Utils::treeStructureToInt(subscription, section, index));
|
||||
return false; // break
|
||||
}
|
||||
|
||||
return true; // continue
|
||||
});
|
||||
}
|
||||
|
||||
if (!dirtyItems.isEmpty()) {
|
||||
emit itemsChanged(dirtyItems);
|
||||
}
|
||||
}
|
||||
@ -1,67 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
|
||||
#include "./extend/dbusmenutypes_p.h"
|
||||
#include "gdbusmenutypes_p.h"
|
||||
|
||||
class Menu : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Menu(const QString &serviceName, const QString &objectPath, QObject *parent = nullptr);
|
||||
~Menu() override;
|
||||
|
||||
void init();
|
||||
void cleanup();
|
||||
|
||||
void start(uint id);
|
||||
void stop(const QList<uint> &ids);
|
||||
|
||||
bool hasMenu() const;
|
||||
bool hasSubscription(uint subscription) const;
|
||||
|
||||
GMenuItem getSection(int id, bool *ok = nullptr) const;
|
||||
GMenuItem getSection(int subscription, int sectionId, bool *ok = nullptr) const;
|
||||
|
||||
QVariantMap getItem(int id) const; // bool ok argument?
|
||||
QVariantMap getItem(int subscription, int sectionId, int id) const;
|
||||
|
||||
public slots:
|
||||
void actionsChanged(const QStringList &dirtyActions, const QString &prefix);
|
||||
|
||||
Q_SIGNALS:
|
||||
void menuAppeared(); // emitted the first time a menu was successfully loaded
|
||||
void menuDisappeared();
|
||||
|
||||
void subscribed(uint id);
|
||||
void failedToSubscribe(uint id);
|
||||
|
||||
void itemsChanged(const QVector<uint> &itemIds);
|
||||
void menusChanged(const QVector<uint> &menuIds);
|
||||
|
||||
private slots:
|
||||
void onMenuChanged(const GMenuChangeList &changes);
|
||||
|
||||
private:
|
||||
void initMenu();
|
||||
|
||||
void menuChanged(const GMenuChangeList &changes);
|
||||
|
||||
// QSet?
|
||||
QList<uint> m_subscriptions; // keeps track of which menu trees we're subscribed to
|
||||
|
||||
QHash<uint, GMenuItemList> m_menus;
|
||||
|
||||
QString m_serviceName;
|
||||
QString m_objectPath;
|
||||
};
|
||||
@ -1,388 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include "menuproxy.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusConnectionInterface>
|
||||
#include <QDBusServiceWatcher>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QStandardPaths>
|
||||
#include <QTimer>
|
||||
#include <QSettings>
|
||||
#include <QDebug>
|
||||
#include <QtGui/qguiapplication_platform.h>
|
||||
|
||||
// #include <KConfigGroup>
|
||||
#include <KDirWatch>
|
||||
// #include <KSharedConfig>
|
||||
#include <KX11Extras>
|
||||
#include <KWindowInfo>
|
||||
#include <netwm.h>
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
#include "window.h"
|
||||
|
||||
static const QString s_ourServiceName = QStringLiteral("org.kde.plasma.gmenu_dbusmenu_proxy");
|
||||
|
||||
static const QString s_dbusMenuRegistrar = QStringLiteral("com.canonical.AppMenu.Registrar");
|
||||
|
||||
static const QByteArray s_gtkUniqueBusName = QByteArrayLiteral("_GTK_UNIQUE_BUS_NAME");
|
||||
|
||||
static const QByteArray s_gtkApplicationObjectPath = QByteArrayLiteral("_GTK_APPLICATION_OBJECT_PATH");
|
||||
static const QByteArray s_unityObjectPath = QByteArrayLiteral("_UNITY_OBJECT_PATH");
|
||||
static const QByteArray s_gtkWindowObjectPath = QByteArrayLiteral("_GTK_WINDOW_OBJECT_PATH");
|
||||
static const QByteArray s_gtkMenuBarObjectPath = QByteArrayLiteral("_GTK_MENUBAR_OBJECT_PATH");
|
||||
// that's the generic app menu with Help and Options and will be used if window doesn't have a fully-blown menu bar
|
||||
static const QByteArray s_gtkAppMenuObjectPath = QByteArrayLiteral("_GTK_APP_MENU_OBJECT_PATH");
|
||||
|
||||
static const QByteArray s_kdeNetWmAppMenuServiceName = QByteArrayLiteral("_KDE_NET_WM_APPMENU_SERVICE_NAME");
|
||||
static const QByteArray s_kdeNetWmAppMenuObjectPath = QByteArrayLiteral("_KDE_NET_WM_APPMENU_OBJECT_PATH");
|
||||
|
||||
static const QString s_gtkModules = QStringLiteral("gtk-modules");
|
||||
static const QString s_appMenuGtkModule = QStringLiteral("appmenu-gtk-module");
|
||||
|
||||
MenuProxy::MenuProxy()
|
||||
: QObject()
|
||||
, m_xConnection(qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->connection())
|
||||
, m_serviceWatcher(new QDBusServiceWatcher(this))
|
||||
, m_gtk2RcWatch(new KDirWatch(this))
|
||||
, m_writeGtk2SettingsTimer(new QTimer(this))
|
||||
{
|
||||
m_serviceWatcher->setConnection(QDBusConnection::sessionBus());
|
||||
m_serviceWatcher->setWatchMode(QDBusServiceWatcher::WatchForUnregistration | QDBusServiceWatcher::WatchForRegistration);
|
||||
m_serviceWatcher->addWatchedService(s_dbusMenuRegistrar);
|
||||
|
||||
connect(m_serviceWatcher, &QDBusServiceWatcher::serviceRegistered, this, [this](const QString &service) {
|
||||
Q_UNUSED(service);
|
||||
qDebug() << "Global menu service became available, starting";
|
||||
init();
|
||||
});
|
||||
connect(m_serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, [this](const QString &service) {
|
||||
Q_UNUSED(service);
|
||||
qDebug() << "Global menu service disappeared, cleaning up";
|
||||
teardown();
|
||||
});
|
||||
|
||||
// It's fine to do a blocking call here as we're a separate binary with no UI
|
||||
if (QDBusConnection::sessionBus().interface()->isServiceRegistered(s_dbusMenuRegistrar)) {
|
||||
qDebug() << "Global menu service is running, starting right away";
|
||||
init();
|
||||
} else {
|
||||
qDebug() << "No global menu service available, waiting for it to start before doing anything";
|
||||
|
||||
// be sure when started to restore gtk menus when there's no dbus menu around in case we crashed
|
||||
enableGtkSettings(false);
|
||||
}
|
||||
|
||||
// kde-gtk-config just deletes and re-creates the gtkrc-2.0, watch this and add our config to it again
|
||||
m_writeGtk2SettingsTimer->setSingleShot(true);
|
||||
m_writeGtk2SettingsTimer->setInterval(1000);
|
||||
connect(m_writeGtk2SettingsTimer, &QTimer::timeout, this, &MenuProxy::writeGtk2Settings);
|
||||
|
||||
auto startGtk2SettingsTimer = [this] {
|
||||
if (!m_writeGtk2SettingsTimer->isActive()) {
|
||||
m_writeGtk2SettingsTimer->start();
|
||||
}
|
||||
};
|
||||
|
||||
connect(m_gtk2RcWatch, &KDirWatch::created, this, startGtk2SettingsTimer);
|
||||
connect(m_gtk2RcWatch, &KDirWatch::dirty, this, startGtk2SettingsTimer);
|
||||
m_gtk2RcWatch->addFile(gtkRc2Path());
|
||||
}
|
||||
|
||||
MenuProxy::~MenuProxy()
|
||||
{
|
||||
teardown();
|
||||
}
|
||||
|
||||
bool MenuProxy::init()
|
||||
{
|
||||
if (!QDBusConnection::sessionBus().registerService(s_ourServiceName)) {
|
||||
qDebug() << "Failed to register DBus service" << s_ourServiceName;
|
||||
return false;
|
||||
}
|
||||
|
||||
enableGtkSettings(true);
|
||||
|
||||
connect(KX11Extras::self(), &KX11Extras::windowAdded, this, &MenuProxy::onWindowAdded);
|
||||
connect(KX11Extras::self(), &KX11Extras::windowRemoved, this, &MenuProxy::onWindowRemoved);
|
||||
|
||||
const auto windows = KX11Extras::windows();
|
||||
for (WId id : windows) {
|
||||
onWindowAdded(id);
|
||||
}
|
||||
|
||||
if (m_windows.isEmpty()) {
|
||||
qDebug() << "Up and running but no windows with menus in sight";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MenuProxy::teardown()
|
||||
{
|
||||
enableGtkSettings(false);
|
||||
|
||||
QDBusConnection::sessionBus().unregisterService(s_ourServiceName);
|
||||
|
||||
disconnect(KX11Extras::self(), &KX11Extras::windowAdded, this, &MenuProxy::onWindowAdded);
|
||||
disconnect(KX11Extras::self(), &KX11Extras::windowRemoved, this, &MenuProxy::onWindowRemoved);
|
||||
|
||||
qDeleteAll(m_windows);
|
||||
m_windows.clear();
|
||||
}
|
||||
|
||||
void MenuProxy::enableGtkSettings(bool enable)
|
||||
{
|
||||
m_enabled = enable;
|
||||
|
||||
writeGtk2Settings();
|
||||
writeGtk3Settings();
|
||||
|
||||
// TODO use gconf/dconf directly or at least signal a change somehow?
|
||||
}
|
||||
|
||||
QString MenuProxy::gtkRc2Path()
|
||||
{
|
||||
return QDir::homePath() + QLatin1String("/.gtkrc-2.0");
|
||||
}
|
||||
|
||||
QString MenuProxy::gtk3SettingsIniPath()
|
||||
{
|
||||
return QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1String("/gtk-3.0/settings.ini");
|
||||
}
|
||||
|
||||
void MenuProxy::writeGtk2Settings()
|
||||
{
|
||||
QFile rcFile(gtkRc2Path());
|
||||
if (!rcFile.exists()) {
|
||||
// Don't create it here, that would break writing default GTK-2.0 settings on first login,
|
||||
// as the gtkbreeze kconf_update script only does so if it does not exist
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << "Writing gtkrc-2.0 to" << (m_enabled ? "enable" : "disable") << "global menu support";
|
||||
|
||||
if (!rcFile.open(QIODevice::ReadWrite | QIODevice::Text)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray content;
|
||||
|
||||
QStringList gtkModules;
|
||||
|
||||
while (!rcFile.atEnd()) {
|
||||
const QByteArray rawLine = rcFile.readLine();
|
||||
|
||||
const QString line = QString::fromUtf8(rawLine.trimmed());
|
||||
|
||||
if (!line.startsWith(s_gtkModules)) {
|
||||
// keep line as-is
|
||||
content += rawLine;
|
||||
continue;
|
||||
}
|
||||
|
||||
const int equalSignIdx = line.indexOf(QLatin1Char('='));
|
||||
if (equalSignIdx < 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
|
||||
gtkModules = line.mid(equalSignIdx + 1).split(QLatin1Char(':'), QString::SkipEmptyParts);
|
||||
#else
|
||||
gtkModules = line.mid(equalSignIdx + 1).split(QLatin1Char(':'), Qt::SkipEmptyParts);
|
||||
#endif
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
addOrRemoveAppMenuGtkModule(gtkModules);
|
||||
|
||||
if (!gtkModules.isEmpty()) {
|
||||
content += QStringLiteral("%1=%2").arg(s_gtkModules, gtkModules.join(QLatin1Char(':'))).toUtf8();
|
||||
}
|
||||
|
||||
qDebug() << " gtk-modules:" << gtkModules;
|
||||
|
||||
m_gtk2RcWatch->stopScan();
|
||||
|
||||
// now write the new contents of the file
|
||||
rcFile.resize(0);
|
||||
rcFile.write(content);
|
||||
rcFile.close();
|
||||
|
||||
m_gtk2RcWatch->startScan();
|
||||
}
|
||||
|
||||
void MenuProxy::writeGtk3Settings()
|
||||
{
|
||||
qDebug() << "Writing gtk-3.0/settings.ini" << (m_enabled ? "enable" : "disable") << "global menu support";
|
||||
|
||||
// mostly taken from kde-gtk-config
|
||||
QSettings cfg(gtk3SettingsIniPath(), QSettings::IniFormat);
|
||||
cfg.beginGroup(QStringLiteral("Settings"));
|
||||
|
||||
QStringList gtkModules = cfg.value(QStringLiteral("gtk-modules")).toString().split(QLatin1Char(':'));
|
||||
addOrRemoveAppMenuGtkModule(gtkModules);
|
||||
|
||||
if (!gtkModules.isEmpty()) {
|
||||
cfg.setValue(QStringLiteral("gtk-modules"), gtkModules.join(QLatin1Char(':')));
|
||||
} else {
|
||||
cfg.remove(QStringLiteral("gtk-modules"));
|
||||
}
|
||||
|
||||
qDebug() << " gtk-modules:" << gtkModules;
|
||||
|
||||
if (m_enabled) {
|
||||
cfg.setValue(QStringLiteral("gtk-shell-shows-menubar"), 1);
|
||||
} else {
|
||||
cfg.remove(QStringLiteral("gtk-shell-shows-menubar"));
|
||||
}
|
||||
|
||||
qDebug() << " gtk-shell-shows-menubar:" << (m_enabled ? 1 : 0);
|
||||
|
||||
cfg.sync();
|
||||
}
|
||||
|
||||
void MenuProxy::addOrRemoveAppMenuGtkModule(QStringList &list)
|
||||
{
|
||||
if (m_enabled && !list.contains(s_appMenuGtkModule)) {
|
||||
list.append(s_appMenuGtkModule);
|
||||
} else if (!m_enabled) {
|
||||
list.removeAll(s_appMenuGtkModule);
|
||||
}
|
||||
}
|
||||
|
||||
void MenuProxy::onWindowAdded(WId id)
|
||||
{
|
||||
if (m_windows.contains(id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
KWindowInfo info(id, NET::WMWindowType);
|
||||
|
||||
NET::WindowType wType = info.windowType(NET::NormalMask | NET::DesktopMask | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask
|
||||
| NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask);
|
||||
|
||||
// Only top level windows typically have a menu bar, dialogs, such as settings don't
|
||||
if (wType != NET::Normal) {
|
||||
qDebug() << "Ignoring window" << id << "of type" << wType;
|
||||
return;
|
||||
}
|
||||
|
||||
const QString serviceName = QString::fromUtf8(getWindowPropertyString(id, s_gtkUniqueBusName));
|
||||
|
||||
if (serviceName.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QString applicationObjectPath = QString::fromUtf8(getWindowPropertyString(id, s_gtkApplicationObjectPath));
|
||||
const QString unityObjectPath = QString::fromUtf8(getWindowPropertyString(id, s_unityObjectPath));
|
||||
const QString windowObjectPath = QString::fromUtf8(getWindowPropertyString(id, s_gtkWindowObjectPath));
|
||||
|
||||
const QString applicationMenuObjectPath = QString::fromUtf8(getWindowPropertyString(id, s_gtkAppMenuObjectPath));
|
||||
const QString menuBarObjectPath = QString::fromUtf8(getWindowPropertyString(id, s_gtkMenuBarObjectPath));
|
||||
|
||||
if (applicationMenuObjectPath.isEmpty() && menuBarObjectPath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Window *window = new Window(serviceName);
|
||||
window->setWinId(id);
|
||||
window->setApplicationObjectPath(applicationObjectPath);
|
||||
window->setUnityObjectPath(unityObjectPath);
|
||||
window->setWindowObjectPath(windowObjectPath);
|
||||
window->setApplicationMenuObjectPath(applicationMenuObjectPath);
|
||||
window->setMenuBarObjectPath(menuBarObjectPath);
|
||||
m_windows.insert(id, window);
|
||||
|
||||
connect(window, &Window::requestWriteWindowProperties, this, [this, window] {
|
||||
Q_ASSERT(!window->proxyObjectPath().isEmpty());
|
||||
|
||||
writeWindowProperty(window->winId(), s_kdeNetWmAppMenuServiceName, s_ourServiceName.toUtf8());
|
||||
writeWindowProperty(window->winId(), s_kdeNetWmAppMenuObjectPath, window->proxyObjectPath().toUtf8());
|
||||
});
|
||||
connect(window, &Window::requestRemoveWindowProperties, this, [this, window] {
|
||||
writeWindowProperty(window->winId(), s_kdeNetWmAppMenuServiceName, QByteArray());
|
||||
writeWindowProperty(window->winId(), s_kdeNetWmAppMenuObjectPath, QByteArray());
|
||||
});
|
||||
|
||||
window->init();
|
||||
}
|
||||
|
||||
void MenuProxy::onWindowRemoved(WId id)
|
||||
{
|
||||
// no need to cleanup() (which removes window properties) when the window is gone, delete right away
|
||||
delete m_windows.take(id);
|
||||
}
|
||||
|
||||
QByteArray MenuProxy::getWindowPropertyString(WId id, const QByteArray &name)
|
||||
{
|
||||
QByteArray value;
|
||||
|
||||
auto atom = getAtom(name);
|
||||
if (atom == XCB_ATOM_NONE) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// GTK properties aren't XCB_ATOM_STRING but a custom one
|
||||
auto utf8StringAtom = getAtom(QByteArrayLiteral("UTF8_STRING"));
|
||||
|
||||
static const long MAX_PROP_SIZE = 10000;
|
||||
auto propertyCookie = xcb_get_property(m_xConnection, false, id, atom, utf8StringAtom, 0, MAX_PROP_SIZE);
|
||||
QScopedPointer<xcb_get_property_reply_t, QScopedPointerPodDeleter> propertyReply(xcb_get_property_reply(m_xConnection, propertyCookie, nullptr));
|
||||
if (propertyReply.isNull()) {
|
||||
qDebug() << "XCB property reply for atom" << name << "on" << id << "was null";
|
||||
return value;
|
||||
}
|
||||
|
||||
if (propertyReply->type == utf8StringAtom && propertyReply->format == 8 && propertyReply->value_len > 0) {
|
||||
const char *data = (const char *)xcb_get_property_value(propertyReply.data());
|
||||
int len = propertyReply->value_len;
|
||||
if (data) {
|
||||
value = QByteArray(data, data[len - 1] ? len : len - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void MenuProxy::writeWindowProperty(WId id, const QByteArray &name, const QByteArray &value)
|
||||
{
|
||||
auto atom = getAtom(name);
|
||||
if (atom == XCB_ATOM_NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (value.isEmpty()) {
|
||||
xcb_delete_property(m_xConnection, id, atom);
|
||||
} else {
|
||||
xcb_change_property(m_xConnection, XCB_PROP_MODE_REPLACE, id, atom, XCB_ATOM_STRING, 8, value.length(), value.constData());
|
||||
}
|
||||
}
|
||||
|
||||
xcb_atom_t MenuProxy::getAtom(const QByteArray &name)
|
||||
{
|
||||
static QHash<QByteArray, xcb_atom_t> s_atoms;
|
||||
|
||||
auto atom = s_atoms.value(name, XCB_ATOM_NONE);
|
||||
if (atom == XCB_ATOM_NONE) {
|
||||
const xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom(m_xConnection, false, name.length(), name.constData());
|
||||
QScopedPointer<xcb_intern_atom_reply_t, QScopedPointerPodDeleter> atomReply(xcb_intern_atom_reply(m_xConnection, atomCookie, nullptr));
|
||||
if (!atomReply.isNull()) {
|
||||
atom = atomReply->atom;
|
||||
if (atom != XCB_ATOM_NONE) {
|
||||
s_atoms.insert(name, atom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return atom;
|
||||
}
|
||||
@ -1,63 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
#include <QWindow> // for WId
|
||||
|
||||
#include <xcb/xcb_atom.h>
|
||||
|
||||
class QDBusServiceWatcher;
|
||||
class QTimer;
|
||||
|
||||
class KDirWatch;
|
||||
|
||||
class Window;
|
||||
|
||||
class MenuProxy : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MenuProxy();
|
||||
~MenuProxy() override;
|
||||
|
||||
private Q_SLOTS:
|
||||
void onWindowAdded(WId id);
|
||||
void onWindowRemoved(WId id);
|
||||
|
||||
private:
|
||||
bool init();
|
||||
void teardown();
|
||||
|
||||
static QString gtkRc2Path();
|
||||
static QString gtk3SettingsIniPath();
|
||||
|
||||
void enableGtkSettings(bool enabled);
|
||||
|
||||
void writeGtk2Settings();
|
||||
void writeGtk3Settings();
|
||||
|
||||
void addOrRemoveAppMenuGtkModule(QStringList &list);
|
||||
|
||||
xcb_connection_t *m_xConnection;
|
||||
|
||||
QByteArray getWindowPropertyString(WId id, const QByteArray &name);
|
||||
void writeWindowProperty(WId id, const QByteArray &name, const QByteArray &value);
|
||||
xcb_atom_t getAtom(const QByteArray &name);
|
||||
|
||||
QHash<WId, Window *> m_windows;
|
||||
|
||||
QDBusServiceWatcher *m_serviceWatcher;
|
||||
|
||||
KDirWatch *m_gtk2RcWatch;
|
||||
QTimer *m_writeGtk2SettingsTimer;
|
||||
|
||||
bool m_enabled = false;
|
||||
};
|
||||
@ -1,29 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
int Utils::treeStructureToInt(int subscription, int section, int index)
|
||||
{
|
||||
return subscription * 1000000 + section * 1000 + index;
|
||||
}
|
||||
|
||||
void Utils::intToTreeStructure(int source, int &subscription, int §ion, int &index)
|
||||
{
|
||||
// TODO some better math :) or bit shifting or something
|
||||
index = source % 1000;
|
||||
section = (source / 1000) % 1000;
|
||||
subscription = source / 1000000;
|
||||
}
|
||||
|
||||
QString Utils::itemActionName(const QVariantMap &item)
|
||||
{
|
||||
QString actionName = item.value(QStringLiteral("action")).toString();
|
||||
if (actionName.isEmpty()) {
|
||||
actionName = item.value(QStringLiteral("submenu-action")).toString();
|
||||
}
|
||||
return actionName;
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QVariantMap>
|
||||
|
||||
namespace Utils
|
||||
{
|
||||
int treeStructureToInt(int subscription, int section, int index);
|
||||
void intToTreeStructure(int source, int &subscription, int §ion, int &index);
|
||||
|
||||
QString itemActionName(const QVariantMap &item);
|
||||
|
||||
}
|
||||
@ -1,653 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include "window.h"
|
||||
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusMessage>
|
||||
#include <QDBusPendingCallWatcher>
|
||||
#include <QDBusPendingReply>
|
||||
#include <QDebug>
|
||||
#include <QList>
|
||||
#include <QMutableListIterator>
|
||||
#include <QVariantList>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "actions.h"
|
||||
#include "dbusmenuadaptor.h"
|
||||
#include "icons.h"
|
||||
#include "menu.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "./extend/dbusmenushortcut_p.h"
|
||||
|
||||
static const QString s_orgGtkActions = QStringLiteral("org.gtk.Actions");
|
||||
static const QString s_orgGtkMenus = QStringLiteral("org.gtk.Menus");
|
||||
|
||||
static const QString s_applicationActionsPrefix = QStringLiteral("app.");
|
||||
static const QString s_unityActionsPrefix = QStringLiteral("unity.");
|
||||
static const QString s_windowActionsPrefix = QStringLiteral("win.");
|
||||
|
||||
Window::Window(const QString &serviceName)
|
||||
: QObject()
|
||||
, m_serviceName(serviceName)
|
||||
{
|
||||
qDebug() << "Created menu on" << serviceName;
|
||||
|
||||
Q_ASSERT(!serviceName.isEmpty());
|
||||
|
||||
GDBusMenuTypes_register();
|
||||
DBusMenuTypes_register();
|
||||
}
|
||||
|
||||
Window::~Window() = default;
|
||||
|
||||
void Window::init()
|
||||
{
|
||||
qDebug() << "Inited window with menu for" << m_winId << "on" << m_serviceName << "at app" << m_applicationObjectPath << "win"
|
||||
<< m_windowObjectPath << "unity" << m_unityObjectPath;
|
||||
|
||||
if (!m_applicationMenuObjectPath.isEmpty()) {
|
||||
m_applicationMenu = new Menu(m_serviceName, m_applicationMenuObjectPath, this);
|
||||
connect(m_applicationMenu, &Menu::menuAppeared, this, &Window::updateWindowProperties);
|
||||
connect(m_applicationMenu, &Menu::menuDisappeared, this, &Window::updateWindowProperties);
|
||||
connect(m_applicationMenu, &Menu::subscribed, this, &Window::onMenuSubscribed);
|
||||
// basically so it replies on DBus no matter what
|
||||
connect(m_applicationMenu, &Menu::failedToSubscribe, this, &Window::onMenuSubscribed);
|
||||
connect(m_applicationMenu, &Menu::itemsChanged, this, &Window::menuItemsChanged);
|
||||
connect(m_applicationMenu, &Menu::menusChanged, this, &Window::menuChanged);
|
||||
}
|
||||
|
||||
if (!m_menuBarObjectPath.isEmpty()) {
|
||||
m_menuBar = new Menu(m_serviceName, m_menuBarObjectPath, this);
|
||||
connect(m_menuBar, &Menu::menuAppeared, this, &Window::updateWindowProperties);
|
||||
connect(m_menuBar, &Menu::menuDisappeared, this, &Window::updateWindowProperties);
|
||||
connect(m_menuBar, &Menu::subscribed, this, &Window::onMenuSubscribed);
|
||||
connect(m_menuBar, &Menu::failedToSubscribe, this, &Window::onMenuSubscribed);
|
||||
connect(m_menuBar, &Menu::itemsChanged, this, &Window::menuItemsChanged);
|
||||
connect(m_menuBar, &Menu::menusChanged, this, &Window::menuChanged);
|
||||
}
|
||||
|
||||
if (!m_applicationObjectPath.isEmpty()) {
|
||||
m_applicationActions = new Actions(m_serviceName, m_applicationObjectPath, this);
|
||||
connect(m_applicationActions, &Actions::actionsChanged, this, [this](const QStringList &dirtyActions) {
|
||||
onActionsChanged(dirtyActions, s_applicationActionsPrefix);
|
||||
});
|
||||
connect(m_applicationActions, &Actions::loaded, this, [this] {
|
||||
if (m_menuInited) {
|
||||
onActionsChanged(m_applicationActions->getAll().keys(), s_applicationActionsPrefix);
|
||||
} else {
|
||||
initMenu();
|
||||
}
|
||||
});
|
||||
m_applicationActions->load();
|
||||
}
|
||||
|
||||
if (!m_unityObjectPath.isEmpty()) {
|
||||
m_unityActions = new Actions(m_serviceName, m_unityObjectPath, this);
|
||||
connect(m_unityActions, &Actions::actionsChanged, this, [this](const QStringList &dirtyActions) {
|
||||
onActionsChanged(dirtyActions, s_unityActionsPrefix);
|
||||
});
|
||||
connect(m_unityActions, &Actions::loaded, this, [this] {
|
||||
if (m_menuInited) {
|
||||
onActionsChanged(m_unityActions->getAll().keys(), s_unityActionsPrefix);
|
||||
} else {
|
||||
initMenu();
|
||||
}
|
||||
});
|
||||
m_unityActions->load();
|
||||
}
|
||||
|
||||
if (!m_windowObjectPath.isEmpty()) {
|
||||
m_windowActions = new Actions(m_serviceName, m_windowObjectPath, this);
|
||||
connect(m_windowActions, &Actions::actionsChanged, this, [this](const QStringList &dirtyActions) {
|
||||
onActionsChanged(dirtyActions, s_windowActionsPrefix);
|
||||
});
|
||||
connect(m_windowActions, &Actions::loaded, this, [this] {
|
||||
if (m_menuInited) {
|
||||
onActionsChanged(m_windowActions->getAll().keys(), s_windowActionsPrefix);
|
||||
} else {
|
||||
initMenu();
|
||||
}
|
||||
});
|
||||
m_windowActions->load();
|
||||
}
|
||||
}
|
||||
|
||||
WId Window::winId() const
|
||||
{
|
||||
return m_winId;
|
||||
}
|
||||
|
||||
void Window::setWinId(WId winId)
|
||||
{
|
||||
m_winId = winId;
|
||||
}
|
||||
|
||||
QString Window::serviceName() const
|
||||
{
|
||||
return m_serviceName;
|
||||
}
|
||||
|
||||
QString Window::applicationObjectPath() const
|
||||
{
|
||||
return m_applicationObjectPath;
|
||||
}
|
||||
|
||||
void Window::setApplicationObjectPath(const QString &applicationObjectPath)
|
||||
{
|
||||
m_applicationObjectPath = applicationObjectPath;
|
||||
}
|
||||
|
||||
QString Window::unityObjectPath() const
|
||||
{
|
||||
return m_unityObjectPath;
|
||||
}
|
||||
|
||||
void Window::setUnityObjectPath(const QString &unityObjectPath)
|
||||
{
|
||||
m_unityObjectPath = unityObjectPath;
|
||||
}
|
||||
|
||||
QString Window::applicationMenuObjectPath() const
|
||||
{
|
||||
return m_applicationMenuObjectPath;
|
||||
}
|
||||
|
||||
void Window::setApplicationMenuObjectPath(const QString &applicationMenuObjectPath)
|
||||
{
|
||||
m_applicationMenuObjectPath = applicationMenuObjectPath;
|
||||
}
|
||||
|
||||
QString Window::menuBarObjectPath() const
|
||||
{
|
||||
return m_menuBarObjectPath;
|
||||
}
|
||||
|
||||
void Window::setMenuBarObjectPath(const QString &menuBarObjectPath)
|
||||
{
|
||||
m_menuBarObjectPath = menuBarObjectPath;
|
||||
}
|
||||
|
||||
QString Window::windowObjectPath() const
|
||||
{
|
||||
return m_windowObjectPath;
|
||||
}
|
||||
|
||||
void Window::setWindowObjectPath(const QString &windowObjectPath)
|
||||
{
|
||||
m_windowObjectPath = windowObjectPath;
|
||||
}
|
||||
|
||||
QString Window::currentMenuObjectPath() const
|
||||
{
|
||||
return m_currentMenuObjectPath;
|
||||
}
|
||||
|
||||
QString Window::proxyObjectPath() const
|
||||
{
|
||||
return m_proxyObjectPath;
|
||||
}
|
||||
|
||||
void Window::initMenu()
|
||||
{
|
||||
if (m_menuInited) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!registerDBusObject()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// appmenu-gtk-module always announces a menu bar on every GTK window even if there is none
|
||||
// so we subscribe to the menu bar as soon as it shows up so we can figure out
|
||||
// if we have a menu bar, an app menu, or just nothing
|
||||
if (m_applicationMenu) {
|
||||
m_applicationMenu->start(0);
|
||||
}
|
||||
|
||||
if (m_menuBar) {
|
||||
m_menuBar->start(0);
|
||||
}
|
||||
|
||||
m_menuInited = true;
|
||||
}
|
||||
|
||||
void Window::menuItemsChanged(const QVector<uint> &itemIds)
|
||||
{
|
||||
if (qobject_cast<Menu *>(sender()) != m_currentMenu) {
|
||||
return;
|
||||
}
|
||||
|
||||
DBusMenuItemList items;
|
||||
|
||||
for (uint id : itemIds) {
|
||||
const auto newItem = m_currentMenu->getItem(id);
|
||||
|
||||
DBusMenuItem dBusItem{// 0 is menu, items start at 1
|
||||
static_cast<int>(id),
|
||||
gMenuToDBusMenuProperties(newItem)};
|
||||
items.append(dBusItem);
|
||||
}
|
||||
|
||||
emit ItemsPropertiesUpdated(items, {});
|
||||
}
|
||||
|
||||
void Window::menuChanged(const QVector<uint> &menuIds)
|
||||
{
|
||||
if (qobject_cast<Menu *>(sender()) != m_currentMenu) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint menu : menuIds) {
|
||||
emit LayoutUpdated(3 /*revision*/, menu);
|
||||
}
|
||||
}
|
||||
|
||||
void Window::onMenuSubscribed(uint id)
|
||||
{
|
||||
// When it was a delayed GetLayout request, send the reply now
|
||||
const auto pendingReplies = m_pendingGetLayouts.values(id);
|
||||
if (!pendingReplies.isEmpty()) {
|
||||
for (const auto &pendingReply : pendingReplies) {
|
||||
if (pendingReply.type() != QDBusMessage::InvalidMessage) {
|
||||
auto reply = pendingReply.createReply();
|
||||
|
||||
DBusMenuLayoutItem item;
|
||||
uint revision = GetLayout(Utils::treeStructureToInt(id, 0, 0), 0, {}, item);
|
||||
|
||||
reply << revision << QVariant::fromValue(item);
|
||||
|
||||
QDBusConnection::sessionBus().send(reply);
|
||||
}
|
||||
}
|
||||
m_pendingGetLayouts.remove(id);
|
||||
} else {
|
||||
emit LayoutUpdated(2 /*revision*/, id);
|
||||
}
|
||||
}
|
||||
|
||||
bool Window::getAction(const QString &name, GMenuAction &action) const
|
||||
{
|
||||
QString lookupName;
|
||||
Actions *actions = getActionsForAction(name, lookupName);
|
||||
|
||||
if (!actions) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return actions->get(lookupName, action);
|
||||
}
|
||||
|
||||
void Window::triggerAction(const QString &name, const QVariant &target, uint timestamp)
|
||||
{
|
||||
QString lookupName;
|
||||
Actions *actions = getActionsForAction(name, lookupName);
|
||||
if (!actions) {
|
||||
return;
|
||||
}
|
||||
|
||||
actions->trigger(lookupName, target, timestamp);
|
||||
}
|
||||
|
||||
Actions *Window::getActionsForAction(const QString &name, QString &lookupName) const
|
||||
{
|
||||
if (name.startsWith(QLatin1String("app."))) {
|
||||
lookupName = name.mid(4);
|
||||
return m_applicationActions;
|
||||
} else if (name.startsWith(QLatin1String("unity."))) {
|
||||
lookupName = name.mid(6);
|
||||
return m_unityActions;
|
||||
} else if (name.startsWith(QLatin1String("win."))) {
|
||||
lookupName = name.mid(4);
|
||||
return m_windowActions;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Window::onActionsChanged(const QStringList &dirty, const QString &prefix)
|
||||
{
|
||||
if (m_applicationMenu) {
|
||||
m_applicationMenu->actionsChanged(dirty, prefix);
|
||||
}
|
||||
if (m_menuBar) {
|
||||
m_menuBar->actionsChanged(dirty, prefix);
|
||||
}
|
||||
}
|
||||
|
||||
bool Window::registerDBusObject()
|
||||
{
|
||||
Q_ASSERT(m_proxyObjectPath.isEmpty());
|
||||
|
||||
static int menus = 0;
|
||||
++menus;
|
||||
|
||||
new DbusmenuAdaptor(this);
|
||||
|
||||
const QString objectPath = QStringLiteral("/MenuBar/%1").arg(QString::number(menus));
|
||||
qDebug() << "Registering DBus object path" << objectPath;
|
||||
|
||||
if (!QDBusConnection::sessionBus().registerObject(objectPath, this)) {
|
||||
qDebug() << "Failed to register object";
|
||||
return false;
|
||||
}
|
||||
|
||||
m_proxyObjectPath = objectPath;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Window::updateWindowProperties()
|
||||
{
|
||||
const bool hasMenu = ((m_applicationMenu && m_applicationMenu->hasMenu()) || (m_menuBar && m_menuBar->hasMenu()));
|
||||
|
||||
if (!hasMenu) {
|
||||
emit requestRemoveWindowProperties();
|
||||
return;
|
||||
}
|
||||
|
||||
Menu *oldMenu = m_currentMenu;
|
||||
Menu *newMenu = qobject_cast<Menu *>(sender());
|
||||
// set current menu as needed
|
||||
if (!m_currentMenu) {
|
||||
m_currentMenu = newMenu;
|
||||
// Menu Bar takes precedence over application menu
|
||||
} else if (m_currentMenu == m_applicationMenu && newMenu == m_menuBar) {
|
||||
qDebug() << "Switching from application menu to menu bar";
|
||||
m_currentMenu = newMenu;
|
||||
// TODO update layout
|
||||
}
|
||||
|
||||
if (m_currentMenu != oldMenu) {
|
||||
// update entire menu now
|
||||
emit LayoutUpdated(4 /*revision*/, 0);
|
||||
}
|
||||
|
||||
emit requestWriteWindowProperties();
|
||||
}
|
||||
|
||||
// DBus
|
||||
bool Window::AboutToShow(int id)
|
||||
{
|
||||
// We always request the first time GetLayout is called and keep up-to-date internally
|
||||
// No need to have us prepare anything here
|
||||
Q_UNUSED(id);
|
||||
return false;
|
||||
}
|
||||
|
||||
void Window::Event(int id, const QString &eventId, const QDBusVariant &data, uint timestamp)
|
||||
{
|
||||
Q_UNUSED(data);
|
||||
|
||||
if (!m_currentMenu) {
|
||||
return;
|
||||
}
|
||||
|
||||
// GMenu dbus doesn't have any "opened" or "closed" signals, we'll only handle "clicked"
|
||||
|
||||
if (eventId == QLatin1String("clicked")) {
|
||||
const QVariantMap item = m_currentMenu->getItem(id);
|
||||
const QString action = item.value(QStringLiteral("action")).toString();
|
||||
const QVariant target = item.value(QStringLiteral("target"));
|
||||
if (!action.isEmpty()) {
|
||||
triggerAction(action, target, timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DBusMenuItemList Window::GetGroupProperties(const QList<int> &ids, const QStringList &propertyNames)
|
||||
{
|
||||
Q_UNUSED(ids);
|
||||
Q_UNUSED(propertyNames);
|
||||
return DBusMenuItemList();
|
||||
}
|
||||
|
||||
uint Window::GetLayout(int parentId, int recursionDepth, const QStringList &propertyNames, DBusMenuLayoutItem &dbusItem)
|
||||
{
|
||||
Q_UNUSED(recursionDepth); // TODO
|
||||
Q_UNUSED(propertyNames);
|
||||
|
||||
int subscription;
|
||||
int sectionId;
|
||||
int index;
|
||||
|
||||
Utils::intToTreeStructure(parentId, subscription, sectionId, index);
|
||||
|
||||
if (!m_currentMenu) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!m_currentMenu->hasSubscription(subscription)) {
|
||||
// let's serve multiple similar requests in one go once we've processed them
|
||||
m_pendingGetLayouts.insert(subscription, message());
|
||||
setDelayedReply(true);
|
||||
|
||||
m_currentMenu->start(subscription);
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool ok;
|
||||
const GMenuItem section = m_currentMenu->getSection(subscription, sectionId, &ok);
|
||||
|
||||
if (!ok) {
|
||||
qDebug() << "There is no section on" << subscription << "at" << sectionId << "with" << parentId;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// If a particular entry is requested, see what it is and resolve as necessary
|
||||
// for example the "File" entry on root is 0,0,1 but is a menu reference to e.g. 1,0,0
|
||||
// so resolve that and return the correct menu
|
||||
if (index > 0) {
|
||||
// non-zero index indicates item within a menu but the index in the list still starts at zero
|
||||
if (section.items.count() < index) {
|
||||
qDebug() << "Requested index" << index << "on" << subscription << "at" << sectionId << "with" << parentId << "is out of bounds";
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto &requestedItem = section.items.at(index - 1);
|
||||
|
||||
auto it = requestedItem.constFind(QStringLiteral(":submenu"));
|
||||
if (it != requestedItem.constEnd()) {
|
||||
const GMenuSection gmenuSection = qdbus_cast<GMenuSection>(it->value<QDBusArgument>());
|
||||
return GetLayout(Utils::treeStructureToInt(gmenuSection.subscription, gmenuSection.menu, 0), recursionDepth, propertyNames, dbusItem);
|
||||
} else {
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
dbusItem.id = parentId; // TODO
|
||||
dbusItem.properties = {{QStringLiteral("children-display"), QStringLiteral("submenu")}};
|
||||
|
||||
int count = 0;
|
||||
|
||||
const auto itemsToBeAdded = section.items;
|
||||
for (const auto &item : itemsToBeAdded) {
|
||||
DBusMenuLayoutItem child{
|
||||
Utils::treeStructureToInt(section.id, sectionId, ++count),
|
||||
gMenuToDBusMenuProperties(item),
|
||||
{} // children
|
||||
};
|
||||
dbusItem.children.append(child);
|
||||
|
||||
// Now resolve section aliases
|
||||
auto it = item.constFind(QStringLiteral(":section"));
|
||||
if (it != item.constEnd()) {
|
||||
// references another place, add it instead
|
||||
GMenuSection gmenuSection = qdbus_cast<GMenuSection>(it->value<QDBusArgument>());
|
||||
|
||||
// remember where the item came from and give it an appropriate ID
|
||||
// so updates signalled by the app will map to the right place
|
||||
int originalSubscription = gmenuSection.subscription;
|
||||
int originalMenu = gmenuSection.menu;
|
||||
|
||||
// TODO start subscription if we don't have it
|
||||
auto items = m_currentMenu->getSection(gmenuSection.subscription, gmenuSection.menu).items;
|
||||
|
||||
// Check whether it's an alias to an alias
|
||||
// FIXME make generic/recursive
|
||||
if (items.count() == 1) {
|
||||
const auto &aliasedItem = items.constFirst();
|
||||
auto findIt = aliasedItem.constFind(QStringLiteral(":section"));
|
||||
if (findIt != aliasedItem.constEnd()) {
|
||||
GMenuSection gmenuSection2 = qdbus_cast<GMenuSection>(findIt->value<QDBusArgument>());
|
||||
items = m_currentMenu->getSection(gmenuSection2.subscription, gmenuSection2.menu).items;
|
||||
|
||||
originalSubscription = gmenuSection2.subscription;
|
||||
originalMenu = gmenuSection2.menu;
|
||||
}
|
||||
}
|
||||
|
||||
int aliasedCount = 0;
|
||||
for (const auto &aliasedItem : qAsConst(items)) {
|
||||
DBusMenuLayoutItem aliasedChild{
|
||||
Utils::treeStructureToInt(originalSubscription, originalMenu, ++aliasedCount),
|
||||
gMenuToDBusMenuProperties(aliasedItem),
|
||||
{} // children
|
||||
};
|
||||
dbusItem.children.append(aliasedChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// revision, unused in libdbusmenuqt
|
||||
return 1;
|
||||
}
|
||||
|
||||
QDBusVariant Window::GetProperty(int id, const QString &property)
|
||||
{
|
||||
Q_UNUSED(id);
|
||||
Q_UNUSED(property);
|
||||
QDBusVariant value;
|
||||
return value;
|
||||
}
|
||||
|
||||
QString Window::status() const
|
||||
{
|
||||
return QStringLiteral("normal");
|
||||
}
|
||||
|
||||
uint Window::version() const
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
|
||||
QVariantMap Window::gMenuToDBusMenuProperties(const QVariantMap &source) const
|
||||
{
|
||||
QVariantMap result;
|
||||
|
||||
result.insert(QStringLiteral("label"), source.value(QStringLiteral("label")).toString());
|
||||
|
||||
if (source.contains(QLatin1String(":section"))) {
|
||||
result.insert(QStringLiteral("type"), QStringLiteral("separator"));
|
||||
}
|
||||
|
||||
const bool isMenu = source.contains(QLatin1String(":submenu"));
|
||||
if (isMenu) {
|
||||
result.insert(QStringLiteral("children-display"), QStringLiteral("submenu"));
|
||||
}
|
||||
|
||||
QString accel = source.value(QStringLiteral("accel")).toString();
|
||||
if (!accel.isEmpty()) {
|
||||
QStringList shortcut;
|
||||
|
||||
// TODO use regexp or something
|
||||
if (accel.contains(QLatin1String("<Primary>")) || accel.contains(QLatin1String("<Control>"))) {
|
||||
shortcut.append(QStringLiteral("Control"));
|
||||
accel.remove(QLatin1String("<Primary>"));
|
||||
accel.remove(QLatin1String("<Control>"));
|
||||
}
|
||||
|
||||
if (accel.contains(QLatin1String("<Shift>"))) {
|
||||
shortcut.append(QStringLiteral("Shift"));
|
||||
accel.remove(QLatin1String("<Shift>"));
|
||||
}
|
||||
|
||||
if (accel.contains(QLatin1String("<Alt>"))) {
|
||||
shortcut.append(QStringLiteral("Alt"));
|
||||
accel.remove(QLatin1String("<Alt>"));
|
||||
}
|
||||
|
||||
if (accel.contains(QLatin1String("<Super>"))) {
|
||||
shortcut.append(QStringLiteral("Super"));
|
||||
accel.remove(QLatin1String("<Super>"));
|
||||
}
|
||||
|
||||
if (!accel.isEmpty()) {
|
||||
// TODO replace "+" by "plus" and "-" by "minus"
|
||||
shortcut.append(accel);
|
||||
|
||||
// TODO does gmenu support multiple?
|
||||
DBusMenuShortcut dbusShortcut;
|
||||
dbusShortcut.append(shortcut); // don't let it unwrap the list we append
|
||||
|
||||
result.insert(QStringLiteral("shortcut"), QVariant::fromValue(dbusShortcut));
|
||||
}
|
||||
}
|
||||
|
||||
bool enabled = true;
|
||||
|
||||
const QString actionName = Utils::itemActionName(source);
|
||||
|
||||
GMenuAction action;
|
||||
// if no action is specified this is fine but if there is an action we don't have
|
||||
// disable the menu entry
|
||||
bool actionOk = true;
|
||||
if (!actionName.isEmpty()) {
|
||||
actionOk = getAction(actionName, action);
|
||||
enabled = actionOk && action.enabled;
|
||||
}
|
||||
|
||||
// we used to only send this if not enabled but then dbusmenuimporter does not
|
||||
// update the enabled state when it changes from disabled to enabled
|
||||
result.insert(QStringLiteral("enabled"), enabled);
|
||||
|
||||
bool visible = true;
|
||||
const QString hiddenWhen = source.value(QStringLiteral("hidden-when")).toString();
|
||||
if (hiddenWhen == QLatin1String("action-disabled") && (!actionOk || !enabled)) {
|
||||
visible = false;
|
||||
} else if (hiddenWhen == QLatin1String("action-missing") && !actionOk) {
|
||||
visible = false;
|
||||
// While we have Global Menu we don't have macOS menu (where Quit, Help, etc is separate)
|
||||
} else if (hiddenWhen == QLatin1String("macos-menubar")) {
|
||||
visible = true;
|
||||
}
|
||||
|
||||
result.insert(QStringLiteral("visible"), visible);
|
||||
|
||||
QString icon = source.value(QStringLiteral("icon")).toString();
|
||||
if (icon.isEmpty()) {
|
||||
icon = source.value(QStringLiteral("verb-icon")).toString();
|
||||
}
|
||||
|
||||
icon = Icons::actionIcon(actionName);
|
||||
if (!icon.isEmpty()) {
|
||||
result.insert(QStringLiteral("icon-name"), icon);
|
||||
}
|
||||
|
||||
const QVariant target = source.value(QStringLiteral("target"));
|
||||
|
||||
if (actionOk) {
|
||||
const auto actionStates = action.state;
|
||||
if (actionStates.count() == 1) {
|
||||
const auto &actionState = actionStates.first();
|
||||
// assume this is a checkbox
|
||||
if (!isMenu) {
|
||||
if (actionState.type() == QVariant::Bool) {
|
||||
result.insert(QStringLiteral("toggle-type"), QStringLiteral("checkbox"));
|
||||
result.insert(QStringLiteral("toggle-state"), actionState.toBool() ? 1 : 0);
|
||||
} else if (actionState.type() == QVariant::String) {
|
||||
result.insert(QStringLiteral("toggle-type"), QStringLiteral("radio"));
|
||||
result.insert(QStringLiteral("toggle-state"), actionState == target ? 1 : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -1,127 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Kai Uwe Broulik <kde@privat.broulik.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QDBusContext>
|
||||
#include <QMultiHash>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QVector>
|
||||
#include <QWindow> // for WId
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "./extend/dbusmenutypes_p.h"
|
||||
#include "gdbusmenutypes_p.h"
|
||||
|
||||
class QDBusVariant;
|
||||
|
||||
class Actions;
|
||||
class Menu;
|
||||
|
||||
class Window : public QObject, protected QDBusContext
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
// DBus
|
||||
Q_PROPERTY(QString Status READ status)
|
||||
Q_PROPERTY(uint Version READ version)
|
||||
|
||||
public:
|
||||
Window(const QString &serviceName);
|
||||
~Window() override;
|
||||
|
||||
void init();
|
||||
|
||||
WId winId() const;
|
||||
void setWinId(WId winId);
|
||||
|
||||
QString serviceName() const;
|
||||
|
||||
QString applicationObjectPath() const;
|
||||
void setApplicationObjectPath(const QString &applicationObjectPath);
|
||||
|
||||
QString unityObjectPath() const;
|
||||
void setUnityObjectPath(const QString &unityObjectPath);
|
||||
|
||||
QString windowObjectPath() const;
|
||||
void setWindowObjectPath(const QString &windowObjectPath);
|
||||
|
||||
QString applicationMenuObjectPath() const;
|
||||
void setApplicationMenuObjectPath(const QString &applicationMenuObjectPath);
|
||||
|
||||
QString menuBarObjectPath() const;
|
||||
void setMenuBarObjectPath(const QString &menuBarObjectPath);
|
||||
|
||||
QString currentMenuObjectPath() const;
|
||||
|
||||
QString proxyObjectPath() const;
|
||||
|
||||
// DBus
|
||||
bool AboutToShow(int id);
|
||||
void Event(int id, const QString &eventId, const QDBusVariant &data, uint timestamp);
|
||||
DBusMenuItemList GetGroupProperties(const QList<int> &ids, const QStringList &propertyNames);
|
||||
uint GetLayout(int parentId, int recursionDepth, const QStringList &propertyNames, DBusMenuLayoutItem &dbusItem);
|
||||
QDBusVariant GetProperty(int id, const QString &property);
|
||||
|
||||
QString status() const;
|
||||
uint version() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
// don't want to pollute X stuff into Menu, let all of that be in MenuProxy
|
||||
void requestWriteWindowProperties();
|
||||
void requestRemoveWindowProperties();
|
||||
|
||||
// DBus
|
||||
void ItemActivationRequested(int id, uint timestamp);
|
||||
void ItemsPropertiesUpdated(const DBusMenuItemList &updatedProps, const DBusMenuItemKeysList &removedProps);
|
||||
void LayoutUpdated(uint revision, int parent);
|
||||
|
||||
private:
|
||||
void initMenu();
|
||||
|
||||
bool registerDBusObject();
|
||||
void updateWindowProperties();
|
||||
|
||||
bool getAction(const QString &name, GMenuAction &action) const;
|
||||
void triggerAction(const QString &name, const QVariant &target, uint timestamp = 0);
|
||||
Actions *getActionsForAction(const QString &name, QString &lookupName) const;
|
||||
|
||||
void menuChanged(const QVector<uint> &menuIds);
|
||||
void menuItemsChanged(const QVector<uint> &itemIds);
|
||||
|
||||
void onActionsChanged(const QStringList &dirty, const QString &prefix);
|
||||
void onMenuSubscribed(uint id);
|
||||
|
||||
QVariantMap gMenuToDBusMenuProperties(const QVariantMap &source) const;
|
||||
|
||||
WId m_winId = 0;
|
||||
QString m_serviceName; // original GMenu service (the gtk app)
|
||||
|
||||
QString m_applicationObjectPath;
|
||||
QString m_unityObjectPath;
|
||||
QString m_windowObjectPath;
|
||||
QString m_applicationMenuObjectPath;
|
||||
QString m_menuBarObjectPath;
|
||||
|
||||
QString m_currentMenuObjectPath;
|
||||
|
||||
QString m_proxyObjectPath; // our object path on this proxy app
|
||||
|
||||
QMultiHash<int, QDBusMessage> m_pendingGetLayouts;
|
||||
|
||||
Menu *m_applicationMenu = nullptr;
|
||||
Menu *m_menuBar = nullptr;
|
||||
|
||||
Menu *m_currentMenu = nullptr;
|
||||
|
||||
Actions *m_applicationActions = nullptr;
|
||||
Actions *m_unityActions = nullptr;
|
||||
Actions *m_windowActions = nullptr;
|
||||
|
||||
bool m_menuInited = false;
|
||||
};
|
||||
@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
exec /usr/bin/cutefish-session --wayland --no-wm "$@"
|
||||
exec /usr/bin/cutefish-session "$@"
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Exec=cutefish-session
|
||||
TryExec=cutefish-session
|
||||
Name=Cutefish Desktop
|
||||
Keywords=session
|
||||
Comment=session
|
||||
@ -0,0 +1,167 @@
|
||||
#include "kwininputbackend.h"
|
||||
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusMessage>
|
||||
#include <QDBusReply>
|
||||
#include <QDBusServiceWatcher>
|
||||
#include <QDBusVariant>
|
||||
#include <QTimer>
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr auto s_service = "org.kde.KWin";
|
||||
constexpr auto s_managerPath = "/org/kde/KWin/InputDevice";
|
||||
constexpr auto s_managerInterface = "org.kde.KWin.InputDeviceManager";
|
||||
constexpr auto s_deviceInterface = "org.kde.KWin.InputDevice";
|
||||
constexpr auto s_propertiesInterface = "org.freedesktop.DBus.Properties";
|
||||
}
|
||||
|
||||
KWinInputBackend::KWinInputBackend(DeviceType type, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_type(type)
|
||||
, m_serviceWatcher(new QDBusServiceWatcher(QString::fromLatin1(s_service),
|
||||
QDBusConnection::sessionBus(),
|
||||
QDBusServiceWatcher::WatchForRegistration
|
||||
| QDBusServiceWatcher::WatchForUnregistration,
|
||||
this))
|
||||
{
|
||||
connect(m_serviceWatcher, &QDBusServiceWatcher::serviceRegistered,
|
||||
this, &KWinInputBackend::refreshDevices);
|
||||
connect(m_serviceWatcher, &QDBusServiceWatcher::serviceUnregistered,
|
||||
this, &KWinInputBackend::refreshDevices);
|
||||
|
||||
QDBusConnection::sessionBus().connect(QString::fromLatin1(s_service),
|
||||
QString::fromLatin1(s_managerPath),
|
||||
QString::fromLatin1(s_managerInterface),
|
||||
QStringLiteral("deviceAdded"),
|
||||
this,
|
||||
SLOT(handleDeviceAdded(QString)));
|
||||
QDBusConnection::sessionBus().connect(QString::fromLatin1(s_service),
|
||||
QString::fromLatin1(s_managerPath),
|
||||
QString::fromLatin1(s_managerInterface),
|
||||
QStringLiteral("deviceRemoved"),
|
||||
this,
|
||||
SLOT(handleDeviceRemoved(QString)));
|
||||
|
||||
QTimer::singleShot(0, this, &KWinInputBackend::refreshDevices);
|
||||
}
|
||||
|
||||
bool KWinInputBackend::available() const
|
||||
{
|
||||
return !matchingDevicePaths().isEmpty();
|
||||
}
|
||||
|
||||
bool KWinInputBackend::booleanProperty(const QString &name, bool fallback) const
|
||||
{
|
||||
const QStringList paths = matchingDevicePaths();
|
||||
if (paths.isEmpty())
|
||||
return fallback;
|
||||
|
||||
const QVariant value = readProperty(paths.constFirst(),
|
||||
QString::fromLatin1(s_deviceInterface), name);
|
||||
return value.isValid() ? value.toBool() : fallback;
|
||||
}
|
||||
|
||||
qreal KWinInputBackend::realProperty(const QString &name, qreal fallback) const
|
||||
{
|
||||
const QStringList paths = matchingDevicePaths();
|
||||
if (paths.isEmpty())
|
||||
return fallback;
|
||||
|
||||
const QVariant value = readProperty(paths.constFirst(),
|
||||
QString::fromLatin1(s_deviceInterface), name);
|
||||
return value.isValid() ? value.toReal() : fallback;
|
||||
}
|
||||
|
||||
void KWinInputBackend::setBooleanProperty(const QString &name, bool value)
|
||||
{
|
||||
for (const QString &path : matchingDevicePaths())
|
||||
writeProperty(path, name, value);
|
||||
}
|
||||
|
||||
void KWinInputBackend::setRealProperty(const QString &name, qreal value)
|
||||
{
|
||||
value = qBound<qreal>(-1.0, value, 1.0);
|
||||
for (const QString &path : matchingDevicePaths())
|
||||
writeProperty(path, name, value);
|
||||
}
|
||||
|
||||
void KWinInputBackend::refreshDevices()
|
||||
{
|
||||
const QVariant value = readProperty(QString::fromLatin1(s_managerPath),
|
||||
QString::fromLatin1(s_managerInterface),
|
||||
QStringLiteral("devicesSysNames"));
|
||||
const QStringList names = value.isValid() ? value.toStringList() : QStringList();
|
||||
if (names == m_deviceNames)
|
||||
return;
|
||||
|
||||
m_deviceNames = names;
|
||||
emit devicesChanged();
|
||||
}
|
||||
|
||||
void KWinInputBackend::handleDeviceAdded(const QString &sysName)
|
||||
{
|
||||
if (!m_deviceNames.contains(sysName)) {
|
||||
m_deviceNames.append(sysName);
|
||||
emit devicesChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void KWinInputBackend::handleDeviceRemoved(const QString &sysName)
|
||||
{
|
||||
if (m_deviceNames.removeAll(sysName) > 0)
|
||||
emit devicesChanged();
|
||||
}
|
||||
|
||||
QStringList KWinInputBackend::matchingDevicePaths() const
|
||||
{
|
||||
QStringList paths;
|
||||
for (const QString &name : m_deviceNames) {
|
||||
const QString path = devicePath(name);
|
||||
if (matches(path))
|
||||
paths.append(path);
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
bool KWinInputBackend::matches(const QString &path) const
|
||||
{
|
||||
const bool touchpad = readProperty(path, QString::fromLatin1(s_deviceInterface),
|
||||
QStringLiteral("touchpad")).toBool();
|
||||
if (m_type == DeviceType::Touchpad)
|
||||
return touchpad;
|
||||
|
||||
const bool pointer = readProperty(path, QString::fromLatin1(s_deviceInterface),
|
||||
QStringLiteral("pointer")).toBool();
|
||||
return pointer && !touchpad;
|
||||
}
|
||||
|
||||
QVariant KWinInputBackend::readProperty(const QString &path, const QString &interface,
|
||||
const QString &name) const
|
||||
{
|
||||
QDBusMessage message = QDBusMessage::createMethodCall(QString::fromLatin1(s_service),
|
||||
path,
|
||||
QString::fromLatin1(s_propertiesInterface),
|
||||
QStringLiteral("Get"));
|
||||
message << interface << name;
|
||||
const QDBusReply<QDBusVariant> reply = QDBusConnection::sessionBus().call(message);
|
||||
return reply.isValid() ? reply.value().variant() : QVariant();
|
||||
}
|
||||
|
||||
bool KWinInputBackend::writeProperty(const QString &path, const QString &name,
|
||||
const QVariant &value) const
|
||||
{
|
||||
QDBusMessage message = QDBusMessage::createMethodCall(QString::fromLatin1(s_service),
|
||||
path,
|
||||
QString::fromLatin1(s_propertiesInterface),
|
||||
QStringLiteral("Set"));
|
||||
message << QString::fromLatin1(s_deviceInterface) << name
|
||||
<< QVariant::fromValue(QDBusVariant(value));
|
||||
const QDBusMessage reply = QDBusConnection::sessionBus().call(message);
|
||||
return reply.type() != QDBusMessage::ErrorMessage;
|
||||
}
|
||||
|
||||
QString KWinInputBackend::devicePath(const QString &sysName)
|
||||
{
|
||||
return QString::fromLatin1(s_managerPath) + QLatin1Char('/') + sysName;
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
#ifndef KWININPUTBACKEND_H
|
||||
#define KWININPUTBACKEND_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
#include <QVariant>
|
||||
|
||||
class QDBusServiceWatcher;
|
||||
|
||||
class KWinInputBackend : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum class DeviceType {
|
||||
Pointer,
|
||||
Touchpad,
|
||||
};
|
||||
|
||||
explicit KWinInputBackend(DeviceType type, QObject *parent = nullptr);
|
||||
|
||||
bool available() const;
|
||||
bool booleanProperty(const QString &name, bool fallback = false) const;
|
||||
qreal realProperty(const QString &name, qreal fallback = 0.0) const;
|
||||
|
||||
void setBooleanProperty(const QString &name, bool value);
|
||||
void setRealProperty(const QString &name, qreal value);
|
||||
|
||||
signals:
|
||||
void devicesChanged();
|
||||
|
||||
private slots:
|
||||
void refreshDevices();
|
||||
void handleDeviceAdded(const QString &sysName);
|
||||
void handleDeviceRemoved(const QString &sysName);
|
||||
|
||||
private:
|
||||
QStringList matchingDevicePaths() const;
|
||||
bool matches(const QString &path) const;
|
||||
QVariant readProperty(const QString &path, const QString &interface,
|
||||
const QString &name) const;
|
||||
bool writeProperty(const QString &path, const QString &name,
|
||||
const QVariant &value) const;
|
||||
static QString devicePath(const QString &sysName);
|
||||
|
||||
DeviceType m_type;
|
||||
QStringList m_deviceNames;
|
||||
QDBusServiceWatcher *m_serviceWatcher;
|
||||
};
|
||||
|
||||
#endif // KWININPUTBACKEND_H
|
||||
@ -1,39 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#include "libinputsettings.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QSettings>
|
||||
|
||||
template<>
|
||||
bool LibinputSettings::load(QString key, bool defVal)
|
||||
{
|
||||
QSettings settings("cutefishos", "mouse");
|
||||
return settings.value(key, defVal).toBool();
|
||||
}
|
||||
|
||||
template<>
|
||||
qreal LibinputSettings::load(QString key, qreal defVal)
|
||||
{
|
||||
QSettings settings("cutefishos", "mouse");
|
||||
return settings.value(key, defVal).toReal();
|
||||
}
|
||||
|
||||
template<>
|
||||
void LibinputSettings::save(QString key, bool val)
|
||||
{
|
||||
QSettings settings("cutefishos", "mouse");
|
||||
settings.setValue(key, val);
|
||||
settings.sync();
|
||||
}
|
||||
|
||||
template<>
|
||||
void LibinputSettings::save(QString key, qreal val)
|
||||
{
|
||||
QSettings settings("cutefishos", "mouse");
|
||||
settings.setValue(key, val);
|
||||
settings.sync();
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef LIBINPUTSETTINGS_H
|
||||
#define LIBINPUTSETTINGS_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
struct LibinputSettings {
|
||||
template<class T>
|
||||
T load(QString key, T defVal);
|
||||
|
||||
template<class T>
|
||||
void save(QString key, T val);
|
||||
};
|
||||
|
||||
#endif // LIBINPUTSETTINGS_H
|
||||
@ -1,86 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2021 CutefishOS Team.
|
||||
*
|
||||
* Author: Reion Wong <reionwong@gmail.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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* 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 "mousemanager.h"
|
||||
#include "mouseadaptor.h"
|
||||
|
||||
#include <QtGui/qguiapplication_platform.h>
|
||||
#include "input/kwininputbackend.h"
|
||||
|
||||
#include <QDBusConnection>
|
||||
|
||||
Mouse::Mouse(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_inputDummydevice(new X11LibinputDummyDevice(this, qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->display()))
|
||||
, m_backend(new KWinInputBackend(KWinInputBackend::DeviceType::Pointer, this))
|
||||
{
|
||||
// init dbus
|
||||
new MouseAdaptor(this);
|
||||
QDBusConnection::sessionBus().registerObject(QStringLiteral("/Mouse"), this);
|
||||
|
||||
connect(m_inputDummydevice, &X11LibinputDummyDevice::leftHandedChanged, this, &Mouse::leftHandedChanged);
|
||||
connect(m_inputDummydevice, &X11LibinputDummyDevice::pointerAccelerationProfileChanged, this, &Mouse::accelerationChanged);
|
||||
connect(m_inputDummydevice, &X11LibinputDummyDevice::naturalScrollChanged, this, &Mouse::naturalScrollChanged);
|
||||
connect(m_inputDummydevice, &X11LibinputDummyDevice::pointerAccelerationChanged, this, &Mouse::pointerAccelerationChanged);
|
||||
connect(m_backend, &KWinInputBackend::devicesChanged, this, [this] {
|
||||
emit availableChanged();
|
||||
emit leftHandedChanged();
|
||||
emit accelerationChanged();
|
||||
emit naturalScrollChanged();
|
||||
emit pointerAccelerationChanged();
|
||||
});
|
||||
}
|
||||
|
||||
Mouse::~Mouse()
|
||||
Mouse::~Mouse() = default;
|
||||
|
||||
bool Mouse::available() const
|
||||
{
|
||||
delete m_inputDummydevice;
|
||||
return m_backend->available();
|
||||
}
|
||||
|
||||
bool Mouse::leftHanded() const
|
||||
{
|
||||
return m_inputDummydevice->isLeftHanded();
|
||||
return m_backend->booleanProperty(QStringLiteral("leftHanded"));
|
||||
}
|
||||
|
||||
void Mouse::setLeftHanded(bool enabled)
|
||||
{
|
||||
m_inputDummydevice->setLeftHanded(enabled);
|
||||
m_inputDummydevice->applyConfig();
|
||||
if (leftHanded() == enabled)
|
||||
return;
|
||||
m_backend->setBooleanProperty(QStringLiteral("leftHanded"), enabled);
|
||||
emit leftHandedChanged();
|
||||
}
|
||||
|
||||
bool Mouse::acceleration() const
|
||||
{
|
||||
return m_inputDummydevice->pointerAccelerationProfileFlat();
|
||||
return m_backend->booleanProperty(QStringLiteral("pointerAccelerationProfileFlat"));
|
||||
}
|
||||
|
||||
void Mouse::setAcceleration(bool enabled)
|
||||
{
|
||||
m_inputDummydevice->setPointerAccelerationProfileFlat(enabled);
|
||||
m_inputDummydevice->applyConfig();
|
||||
if (acceleration() == enabled)
|
||||
return;
|
||||
m_backend->setBooleanProperty(QStringLiteral("pointerAccelerationProfileFlat"), enabled);
|
||||
m_backend->setBooleanProperty(QStringLiteral("pointerAccelerationProfileAdaptive"), !enabled);
|
||||
emit accelerationChanged();
|
||||
}
|
||||
|
||||
bool Mouse::naturalScroll() const
|
||||
{
|
||||
return m_inputDummydevice->isNaturalScroll();
|
||||
return m_backend->booleanProperty(QStringLiteral("naturalScroll"));
|
||||
}
|
||||
|
||||
void Mouse::setNaturalScroll(bool enabled)
|
||||
{
|
||||
m_inputDummydevice->setNaturalScroll(enabled);
|
||||
m_inputDummydevice->applyConfig();
|
||||
if (naturalScroll() == enabled)
|
||||
return;
|
||||
m_backend->setBooleanProperty(QStringLiteral("naturalScroll"), enabled);
|
||||
emit naturalScrollChanged();
|
||||
}
|
||||
|
||||
qreal Mouse::pointerAcceleration() const
|
||||
{
|
||||
return m_inputDummydevice->pointerAcceleration();
|
||||
return m_backend->realProperty(QStringLiteral("pointerAcceleration"));
|
||||
}
|
||||
|
||||
void Mouse::setPointerAcceleration(qreal value)
|
||||
{
|
||||
m_inputDummydevice->setPointerAcceleration(value);
|
||||
m_inputDummydevice->applyConfig();
|
||||
value = qBound<qreal>(-1.0, value, 1.0);
|
||||
if (qFuzzyCompare(1.0 + pointerAcceleration(), 1.0 + value))
|
||||
return;
|
||||
m_backend->setRealProperty(QStringLiteral("pointerAcceleration"), value);
|
||||
emit pointerAccelerationChanged();
|
||||
}
|
||||
|
||||
@ -1,242 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#include "x11libinputdummydevice.h"
|
||||
#include "libinputsettings.h"
|
||||
#include <QDebug>
|
||||
#include <libinput-properties.h>
|
||||
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/extensions/XInput.h>
|
||||
#include <X11/extensions/XInput2.h>
|
||||
|
||||
static Atom s_touchpadAtom;
|
||||
|
||||
template<typename Callback>
|
||||
static void XIForallPointerDevices(Display *dpy, const Callback &callback)
|
||||
{
|
||||
int ndevices_return;
|
||||
XDeviceInfo *info = XListInputDevices(dpy, &ndevices_return);
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < ndevices_return; ++i) {
|
||||
XDeviceInfo *dev = info + i;
|
||||
if ((dev->use == IsXPointer || dev->use == IsXExtensionPointer) && dev->type != s_touchpadAtom) {
|
||||
callback(dev);
|
||||
}
|
||||
}
|
||||
XFreeDeviceList(info);
|
||||
}
|
||||
|
||||
struct ScopedXDeleter {
|
||||
static inline void cleanup(void *pointer)
|
||||
{
|
||||
if (pointer) {
|
||||
XFree(pointer);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
template<typename T>
|
||||
void valueWriterPart(T val, Atom valAtom, Display *dpy)
|
||||
{
|
||||
Q_UNUSED(val);
|
||||
Q_UNUSED(valAtom);
|
||||
Q_UNUSED(dpy);
|
||||
}
|
||||
|
||||
template<>
|
||||
void valueWriterPart<bool>(bool val, Atom valAtom, Display *dpy)
|
||||
{
|
||||
XIForallPointerDevices(dpy, [&](XDeviceInfo *info) {
|
||||
int deviceid = info->id;
|
||||
Status status;
|
||||
Atom type_return;
|
||||
int format_return;
|
||||
unsigned long num_items_return;
|
||||
unsigned long bytes_after_return;
|
||||
|
||||
unsigned char *_data = nullptr;
|
||||
// data returned is an 1 byte boolean
|
||||
status = XIGetProperty(dpy, deviceid, valAtom, 0, 1, False, XA_INTEGER, &type_return, &format_return, &num_items_return, &bytes_after_return, &_data);
|
||||
if (status != Success) {
|
||||
return;
|
||||
}
|
||||
|
||||
QScopedArrayPointer<unsigned char, ScopedXDeleter> data(_data);
|
||||
_data = nullptr;
|
||||
|
||||
if (type_return != XA_INTEGER || !data || format_return != 8) {
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned char sendVal[2] = {0};
|
||||
if (num_items_return == 1) {
|
||||
sendVal[0] = val;
|
||||
} else {
|
||||
// Special case for acceleration profile.
|
||||
const Atom accel = XInternAtom(dpy, LIBINPUT_PROP_ACCEL_PROFILE_ENABLED, True);
|
||||
if (num_items_return != 2 || valAtom != accel) {
|
||||
return;
|
||||
}
|
||||
sendVal[val] = 1;
|
||||
}
|
||||
|
||||
XIChangeProperty(dpy, deviceid, valAtom, XA_INTEGER, 8, XIPropModeReplace, sendVal, num_items_return);
|
||||
});
|
||||
}
|
||||
|
||||
template<>
|
||||
void valueWriterPart<qreal>(qreal val, Atom valAtom, Display *dpy)
|
||||
{
|
||||
XIForallPointerDevices(dpy, [&](XDeviceInfo *info) {
|
||||
int deviceid = info->id;
|
||||
Status status;
|
||||
Atom float_type = XInternAtom(dpy, "FLOAT", False);
|
||||
Atom type_return;
|
||||
int format_return;
|
||||
unsigned long num_items_return;
|
||||
unsigned long bytes_after_return;
|
||||
|
||||
unsigned char *_data = nullptr;
|
||||
// data returned is an 1 byte boolean
|
||||
status = XIGetProperty(dpy, deviceid, valAtom, 0, 1, False, float_type, &type_return, &format_return, &num_items_return, &bytes_after_return, &_data);
|
||||
if (status != Success) {
|
||||
return;
|
||||
}
|
||||
|
||||
QScopedArrayPointer<unsigned char, ScopedXDeleter> data(_data);
|
||||
_data = nullptr;
|
||||
|
||||
if (type_return != float_type || !data || format_return != 32 || num_items_return != 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned char buffer[4096];
|
||||
float *sendPtr = (float *)buffer;
|
||||
*sendPtr = val;
|
||||
|
||||
XIChangeProperty(dpy, deviceid, valAtom, float_type, format_return, XIPropModeReplace, buffer, 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
X11LibinputDummyDevice::X11LibinputDummyDevice(QObject *parent, Display *dpy)
|
||||
: QObject(parent)
|
||||
, m_settings(new LibinputSettings())
|
||||
, m_dpy(dpy)
|
||||
{
|
||||
m_leftHanded.atom = XInternAtom(dpy, LIBINPUT_PROP_LEFT_HANDED, True);
|
||||
m_middleEmulation.atom = XInternAtom(dpy, LIBINPUT_PROP_MIDDLE_EMULATION_ENABLED, True);
|
||||
m_naturalScroll.atom = XInternAtom(dpy, LIBINPUT_PROP_NATURAL_SCROLL, True);
|
||||
m_pointerAcceleration.atom = XInternAtom(dpy, LIBINPUT_PROP_ACCEL, True);
|
||||
m_pointerAccelerationProfileFlat.atom = XInternAtom(dpy, LIBINPUT_PROP_ACCEL_PROFILE_ENABLED, True);
|
||||
|
||||
m_supportsDisableEvents.val = false;
|
||||
m_enabled.val = true;
|
||||
m_supportedButtons.val = Qt::LeftButton | Qt::MiddleButton | Qt::RightButton;
|
||||
m_supportsLeftHanded.val = true;
|
||||
m_supportsMiddleEmulation.val = true;
|
||||
m_middleEmulationEnabledByDefault.val = false;
|
||||
|
||||
m_supportsPointerAcceleration.val = true;
|
||||
m_defaultPointerAcceleration.val = 0;
|
||||
|
||||
m_supportsPointerAccelerationProfileAdaptive.val = true;
|
||||
m_supportsPointerAccelerationProfileFlat.val = true;
|
||||
|
||||
m_defaultPointerAccelerationProfileAdaptive.val = true;
|
||||
m_defaultPointerAccelerationProfileFlat.val = false;
|
||||
|
||||
m_supportsNaturalScroll.val = true;
|
||||
m_naturalScrollEnabledByDefault.val = false;
|
||||
|
||||
s_touchpadAtom = XInternAtom(m_dpy, XI_TOUCHPAD, True);
|
||||
|
||||
// Init
|
||||
getConfig();
|
||||
applyConfig();
|
||||
}
|
||||
|
||||
X11LibinputDummyDevice::~X11LibinputDummyDevice()
|
||||
{
|
||||
delete m_settings;
|
||||
}
|
||||
|
||||
bool X11LibinputDummyDevice::getConfig()
|
||||
{
|
||||
auto reset = [this](Prop<bool> &prop, bool defVal) {
|
||||
prop.reset(m_settings->load(prop.cfgName, defVal));
|
||||
};
|
||||
|
||||
reset(m_leftHanded, false);
|
||||
|
||||
reset(m_middleEmulation, false);
|
||||
reset(m_naturalScroll, false);
|
||||
reset(m_pointerAccelerationProfileFlat, false);
|
||||
|
||||
m_pointerAccelerationProfileAdaptive.reset(!m_settings->load(m_pointerAccelerationProfileFlat.cfgName, false));
|
||||
m_pointerAcceleration.reset(m_settings->load(m_pointerAcceleration.cfgName, 0.));
|
||||
|
||||
emit leftHandedChanged();
|
||||
emit naturalScrollChanged();
|
||||
emit pointerAccelerationProfileChanged();
|
||||
emit pointerAccelerationChanged();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool X11LibinputDummyDevice::getDefaultConfig()
|
||||
{
|
||||
m_leftHanded.set(false);
|
||||
|
||||
m_pointerAcceleration.set(m_defaultPointerAcceleration);
|
||||
m_pointerAccelerationProfileFlat.set(m_defaultPointerAccelerationProfileFlat);
|
||||
m_pointerAccelerationProfileAdaptive.set(m_defaultPointerAccelerationProfileAdaptive);
|
||||
|
||||
m_middleEmulation.set(m_middleEmulationEnabledByDefault);
|
||||
m_naturalScroll.set(m_naturalScrollEnabledByDefault);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool X11LibinputDummyDevice::applyConfig()
|
||||
{
|
||||
valueWriter(m_leftHanded);
|
||||
valueWriter(m_middleEmulation);
|
||||
valueWriter(m_naturalScroll);
|
||||
valueWriter(m_pointerAcceleration);
|
||||
valueWriter(m_pointerAccelerationProfileFlat);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool X11LibinputDummyDevice::valueWriter(Prop<T> &prop)
|
||||
{
|
||||
// Check atom availability first.
|
||||
if (prop.atom == None) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (prop.val != prop.old) {
|
||||
m_settings->save(prop.cfgName, prop.val);
|
||||
}
|
||||
|
||||
valueWriterPart(prop.val, prop.atom, m_dpy);
|
||||
|
||||
prop.old = prop.val;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool X11LibinputDummyDevice::isChangedConfig() const
|
||||
{
|
||||
return m_leftHanded.changed() || m_pointerAcceleration.changed() || m_pointerAccelerationProfileFlat.changed()
|
||||
|| m_pointerAccelerationProfileAdaptive.changed() || m_middleEmulation.changed() || m_naturalScroll.changed();
|
||||
}
|
||||
@ -1,297 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2018 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef X11LIBINPUTDUMMYDEVICE_H
|
||||
#define X11LIBINPUTDUMMYDEVICE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
#include <X11/Xdefs.h>
|
||||
#include <QtGui/qguiapplication_platform.h>
|
||||
|
||||
struct LibinputSettings;
|
||||
|
||||
class X11LibinputDummyDevice : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
//
|
||||
// general
|
||||
Q_PROPERTY(QString name READ name CONSTANT)
|
||||
Q_PROPERTY(bool supportsDisableEvents READ supportsDisableEvents CONSTANT)
|
||||
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged)
|
||||
|
||||
//
|
||||
// advanced
|
||||
Q_PROPERTY(Qt::MouseButtons supportedButtons READ supportedButtons CONSTANT)
|
||||
|
||||
Q_PROPERTY(bool supportsLeftHanded READ supportsLeftHanded CONSTANT)
|
||||
Q_PROPERTY(bool leftHandedEnabledByDefault READ leftHandedEnabledByDefault CONSTANT)
|
||||
Q_PROPERTY(bool leftHanded READ isLeftHanded WRITE setLeftHanded NOTIFY leftHandedChanged)
|
||||
|
||||
Q_PROPERTY(bool supportsMiddleEmulation READ supportsMiddleEmulation CONSTANT)
|
||||
Q_PROPERTY(bool middleEmulationEnabledByDefault READ middleEmulationEnabledByDefault CONSTANT)
|
||||
Q_PROPERTY(bool middleEmulation READ isMiddleEmulation WRITE setMiddleEmulation NOTIFY middleEmulationChanged)
|
||||
|
||||
//
|
||||
// acceleration speed and profile
|
||||
Q_PROPERTY(bool supportsPointerAcceleration READ supportsPointerAcceleration CONSTANT)
|
||||
Q_PROPERTY(qreal pointerAcceleration READ pointerAcceleration WRITE setPointerAcceleration NOTIFY pointerAccelerationChanged)
|
||||
|
||||
Q_PROPERTY(bool supportsPointerAccelerationProfileFlat READ supportsPointerAccelerationProfileFlat CONSTANT)
|
||||
Q_PROPERTY(bool defaultPointerAccelerationProfileFlat READ defaultPointerAccelerationProfileFlat CONSTANT)
|
||||
Q_PROPERTY(bool pointerAccelerationProfileFlat READ pointerAccelerationProfileFlat WRITE setPointerAccelerationProfileFlat NOTIFY
|
||||
pointerAccelerationProfileChanged)
|
||||
|
||||
Q_PROPERTY(bool supportsPointerAccelerationProfileAdaptive READ supportsPointerAccelerationProfileAdaptive CONSTANT)
|
||||
Q_PROPERTY(bool defaultPointerAccelerationProfileAdaptive READ defaultPointerAccelerationProfileAdaptive CONSTANT)
|
||||
Q_PROPERTY(bool pointerAccelerationProfileAdaptive READ pointerAccelerationProfileAdaptive WRITE setPointerAccelerationProfileAdaptive NOTIFY
|
||||
pointerAccelerationProfileChanged)
|
||||
|
||||
//
|
||||
// scrolling
|
||||
Q_PROPERTY(bool supportsNaturalScroll READ supportsNaturalScroll CONSTANT)
|
||||
Q_PROPERTY(bool naturalScrollEnabledByDefault READ naturalScrollEnabledByDefault CONSTANT)
|
||||
Q_PROPERTY(bool naturalScroll READ isNaturalScroll WRITE setNaturalScroll NOTIFY naturalScrollChanged)
|
||||
|
||||
public:
|
||||
X11LibinputDummyDevice(QObject *parent, Display *dpy);
|
||||
~X11LibinputDummyDevice() override;
|
||||
|
||||
bool getConfig();
|
||||
bool getDefaultConfig();
|
||||
bool applyConfig();
|
||||
bool isChangedConfig() const;
|
||||
|
||||
//
|
||||
// general
|
||||
QString name() const
|
||||
{
|
||||
return m_name.val;
|
||||
}
|
||||
QString sysName() const
|
||||
{
|
||||
return m_sysName.val;
|
||||
}
|
||||
bool supportsDisableEvents() const
|
||||
{
|
||||
return m_supportsDisableEvents.val;
|
||||
}
|
||||
void setEnabled(bool enabled)
|
||||
{
|
||||
m_enabled.set(enabled);
|
||||
}
|
||||
bool isEnabled() const
|
||||
{
|
||||
return m_enabled.val;
|
||||
}
|
||||
Qt::MouseButtons supportedButtons() const
|
||||
{
|
||||
return m_supportedButtons.val;
|
||||
}
|
||||
|
||||
//
|
||||
// advanced
|
||||
bool supportsLeftHanded() const
|
||||
{
|
||||
return m_supportsLeftHanded.val;
|
||||
}
|
||||
bool leftHandedEnabledByDefault() const
|
||||
{
|
||||
return m_leftHandedEnabledByDefault.val;
|
||||
}
|
||||
bool isLeftHanded() const
|
||||
{
|
||||
return m_leftHanded.val;
|
||||
}
|
||||
void setLeftHanded(bool set)
|
||||
{
|
||||
m_leftHanded.set(set);
|
||||
}
|
||||
|
||||
bool supportsMiddleEmulation() const
|
||||
{
|
||||
return m_supportsMiddleEmulation.val;
|
||||
}
|
||||
bool middleEmulationEnabledByDefault() const
|
||||
{
|
||||
return m_middleEmulationEnabledByDefault.val;
|
||||
}
|
||||
bool isMiddleEmulation() const
|
||||
{
|
||||
return m_middleEmulation.val;
|
||||
}
|
||||
void setMiddleEmulation(bool set)
|
||||
{
|
||||
m_middleEmulation.set(set);
|
||||
}
|
||||
|
||||
//
|
||||
// acceleration speed and profile
|
||||
bool supportsPointerAcceleration() const
|
||||
{
|
||||
return m_supportsPointerAcceleration.val;
|
||||
}
|
||||
qreal pointerAcceleration() const
|
||||
{
|
||||
return m_pointerAcceleration.val;
|
||||
}
|
||||
void setPointerAcceleration(qreal acceleration)
|
||||
{
|
||||
m_pointerAcceleration.set(acceleration);
|
||||
}
|
||||
|
||||
bool supportsPointerAccelerationProfileFlat() const
|
||||
{
|
||||
return m_supportsPointerAccelerationProfileFlat.val;
|
||||
}
|
||||
bool defaultPointerAccelerationProfileFlat() const
|
||||
{
|
||||
return m_defaultPointerAccelerationProfileFlat.val;
|
||||
}
|
||||
bool pointerAccelerationProfileFlat() const
|
||||
{
|
||||
return m_pointerAccelerationProfileFlat.val;
|
||||
}
|
||||
void setPointerAccelerationProfileFlat(bool set)
|
||||
{
|
||||
m_pointerAccelerationProfileFlat.set(set);
|
||||
}
|
||||
|
||||
bool supportsPointerAccelerationProfileAdaptive() const
|
||||
{
|
||||
return m_supportsPointerAccelerationProfileAdaptive.val;
|
||||
}
|
||||
bool defaultPointerAccelerationProfileAdaptive() const
|
||||
{
|
||||
return m_defaultPointerAccelerationProfileAdaptive.val;
|
||||
}
|
||||
bool pointerAccelerationProfileAdaptive() const
|
||||
{
|
||||
return m_pointerAccelerationProfileAdaptive.val;
|
||||
}
|
||||
void setPointerAccelerationProfileAdaptive(bool set)
|
||||
{
|
||||
m_pointerAccelerationProfileAdaptive.set(set);
|
||||
}
|
||||
|
||||
//
|
||||
// scrolling
|
||||
bool supportsNaturalScroll() const
|
||||
{
|
||||
return m_supportsNaturalScroll.val;
|
||||
}
|
||||
bool naturalScrollEnabledByDefault() const
|
||||
{
|
||||
return m_naturalScrollEnabledByDefault.val;
|
||||
}
|
||||
bool isNaturalScroll() const
|
||||
{
|
||||
return m_naturalScroll.val;
|
||||
}
|
||||
void setNaturalScroll(bool set)
|
||||
{
|
||||
m_naturalScroll.set(set);
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
void leftHandedChanged();
|
||||
void pointerAccelerationChanged();
|
||||
void pointerAccelerationProfileChanged();
|
||||
void enabledChanged();
|
||||
void middleEmulationChanged();
|
||||
void naturalScrollChanged();
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
struct Prop {
|
||||
explicit Prop(const QString &_name, const QString &_cfgName = "")
|
||||
: name(_name)
|
||||
, cfgName(_cfgName)
|
||||
{
|
||||
}
|
||||
|
||||
void set(T newVal)
|
||||
{
|
||||
if (avail && val != newVal) {
|
||||
val = newVal;
|
||||
}
|
||||
}
|
||||
void set(const Prop<T> &p)
|
||||
{
|
||||
if (avail && val != p.val) {
|
||||
val = p.val;
|
||||
}
|
||||
}
|
||||
bool changed() const
|
||||
{
|
||||
return avail && (old != val);
|
||||
}
|
||||
|
||||
void reset(T newVal)
|
||||
{
|
||||
val = newVal;
|
||||
old = newVal;
|
||||
}
|
||||
|
||||
QString name;
|
||||
QString cfgName;
|
||||
|
||||
bool avail = true;
|
||||
T old;
|
||||
T val;
|
||||
|
||||
Atom atom;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
bool valueWriter(Prop<T> &prop);
|
||||
|
||||
//
|
||||
// general
|
||||
Prop<QString> m_name = Prop<QString>("name");
|
||||
Prop<QString> m_sysName = Prop<QString>("sysName");
|
||||
Prop<bool> m_supportsDisableEvents = Prop<bool>("supportsDisableEvents");
|
||||
Prop<bool> m_enabled = Prop<bool>("enabled");
|
||||
|
||||
//
|
||||
// advanced
|
||||
Prop<Qt::MouseButtons> m_supportedButtons = Prop<Qt::MouseButtons>("supportedButtons");
|
||||
|
||||
Prop<bool> m_supportsLeftHanded = Prop<bool>("supportsLeftHanded");
|
||||
Prop<bool> m_leftHandedEnabledByDefault = Prop<bool>("leftHandedEnabledByDefault");
|
||||
Prop<bool> m_leftHanded = Prop<bool>("leftHanded", "XLbInptLeftHanded");
|
||||
|
||||
Prop<bool> m_supportsMiddleEmulation = Prop<bool>("supportsMiddleEmulation");
|
||||
Prop<bool> m_middleEmulationEnabledByDefault = Prop<bool>("middleEmulationEnabledByDefault");
|
||||
Prop<bool> m_middleEmulation = Prop<bool>("middleEmulation", "XLbInptMiddleEmulation");
|
||||
|
||||
//
|
||||
// acceleration speed and profile
|
||||
Prop<bool> m_supportsPointerAcceleration = Prop<bool>("supportsPointerAcceleration");
|
||||
Prop<qreal> m_defaultPointerAcceleration = Prop<qreal>("defaultPointerAcceleration");
|
||||
Prop<qreal> m_pointerAcceleration = Prop<qreal>("pointerAcceleration", "XLbInptPointerAcceleration");
|
||||
|
||||
Prop<bool> m_supportsPointerAccelerationProfileFlat = Prop<bool>("supportsPointerAccelerationProfileFlat");
|
||||
Prop<bool> m_defaultPointerAccelerationProfileFlat = Prop<bool>("defaultPointerAccelerationProfileFlat");
|
||||
Prop<bool> m_pointerAccelerationProfileFlat = Prop<bool>("pointerAccelerationProfileFlat", "XLbInptAccelProfileFlat");
|
||||
|
||||
Prop<bool> m_supportsPointerAccelerationProfileAdaptive = Prop<bool>("supportsPointerAccelerationProfileAdaptive");
|
||||
Prop<bool> m_defaultPointerAccelerationProfileAdaptive = Prop<bool>("defaultPointerAccelerationProfileAdaptive");
|
||||
Prop<bool> m_pointerAccelerationProfileAdaptive = Prop<bool>("pointerAccelerationProfileAdaptive");
|
||||
|
||||
//
|
||||
// scrolling
|
||||
Prop<bool> m_supportsNaturalScroll = Prop<bool>("supportsNaturalScroll");
|
||||
Prop<bool> m_naturalScrollEnabledByDefault = Prop<bool>("naturalScrollEnabledByDefault");
|
||||
Prop<bool> m_naturalScroll = Prop<bool>("naturalScroll", "XLbInptNaturalScroll");
|
||||
|
||||
LibinputSettings *m_settings;
|
||||
Display *m_dpy = nullptr;
|
||||
};
|
||||
|
||||
#endif // X11LIBINPUTDUMMYDEVICE_H
|
||||
@ -1,9 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2019 Atul Bisht <atulbisht26@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "libinputcommon.h"
|
||||
|
||||
#include "moc_libinputcommon.cpp"
|
||||
@ -1,566 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2017 Roman Gilg <subdiff@gmail.com>
|
||||
SPDX-FileCopyrightText: 2019 Atul Bisht <atulbisht26@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef LIBINPUTCOMMON_H
|
||||
#define LIBINPUTCOMMON_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariant>
|
||||
|
||||
namespace
|
||||
{
|
||||
template<typename T>
|
||||
inline T valueLoaderPart(QVariant const &reply)
|
||||
{
|
||||
Q_UNUSED(reply);
|
||||
return T();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool valueLoaderPart(QVariant const &reply)
|
||||
{
|
||||
return reply.toBool();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline int valueLoaderPart(QVariant const &reply)
|
||||
{
|
||||
return reply.toInt();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline quint32 valueLoaderPart(QVariant const &reply)
|
||||
{
|
||||
return reply.toInt();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline qreal valueLoaderPart(QVariant const &reply)
|
||||
{
|
||||
return reply.toReal();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline QString valueLoaderPart(QVariant const &reply)
|
||||
{
|
||||
return reply.toString();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline Qt::MouseButtons valueLoaderPart(QVariant const &reply)
|
||||
{
|
||||
return static_cast<Qt::MouseButtons>(reply.toInt());
|
||||
}
|
||||
}
|
||||
|
||||
class LibinputCommon : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
//
|
||||
// general
|
||||
Q_PROPERTY(QString name READ name CONSTANT)
|
||||
Q_PROPERTY(bool supportsDisableEvents READ supportsDisableEvents CONSTANT)
|
||||
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged)
|
||||
|
||||
//
|
||||
// advanced
|
||||
Q_PROPERTY(Qt::MouseButtons supportedButtons READ supportedButtons CONSTANT)
|
||||
|
||||
Q_PROPERTY(bool supportsLeftHanded READ supportsLeftHanded CONSTANT)
|
||||
Q_PROPERTY(bool leftHandedEnabledByDefault READ leftHandedEnabledByDefault CONSTANT)
|
||||
Q_PROPERTY(bool leftHanded READ isLeftHanded WRITE setLeftHanded NOTIFY leftHandedChanged)
|
||||
|
||||
Q_PROPERTY(bool supportsDisableEventsOnExternalMouse READ supportsDisableEventsOnExternalMouse CONSTANT)
|
||||
|
||||
Q_PROPERTY(bool supportsDisableWhileTyping READ supportsDisableWhileTyping CONSTANT)
|
||||
Q_PROPERTY(bool disableWhileTypingEnabledByDefault READ disableWhileTypingEnabledByDefault CONSTANT)
|
||||
Q_PROPERTY(bool disableWhileTyping READ isDisableWhileTyping WRITE setDisableWhileTyping NOTIFY disableWhileTypingChanged)
|
||||
|
||||
Q_PROPERTY(bool supportsMiddleEmulation READ supportsMiddleEmulation CONSTANT)
|
||||
Q_PROPERTY(bool middleEmulationEnabledByDefault READ middleEmulationEnabledByDefault CONSTANT)
|
||||
Q_PROPERTY(bool middleEmulation READ isMiddleEmulation WRITE setMiddleEmulation NOTIFY middleEmulationChanged)
|
||||
|
||||
//
|
||||
// acceleration speed and profile
|
||||
Q_PROPERTY(bool supportsPointerAcceleration READ supportsPointerAcceleration CONSTANT)
|
||||
Q_PROPERTY(qreal pointerAcceleration READ pointerAcceleration WRITE setPointerAcceleration NOTIFY pointerAccelerationChanged)
|
||||
|
||||
Q_PROPERTY(bool supportsPointerAccelerationProfileFlat READ supportsPointerAccelerationProfileFlat CONSTANT)
|
||||
Q_PROPERTY(bool defaultPointerAccelerationProfileFlat READ defaultPointerAccelerationProfileFlat CONSTANT)
|
||||
Q_PROPERTY(bool pointerAccelerationProfileFlat READ pointerAccelerationProfileFlat WRITE setPointerAccelerationProfileFlat NOTIFY
|
||||
pointerAccelerationProfileChanged)
|
||||
|
||||
Q_PROPERTY(bool supportsPointerAccelerationProfileAdaptive READ supportsPointerAccelerationProfileAdaptive CONSTANT)
|
||||
Q_PROPERTY(bool defaultPointerAccelerationProfileAdaptive READ defaultPointerAccelerationProfileAdaptive CONSTANT)
|
||||
Q_PROPERTY(bool pointerAccelerationProfileAdaptive READ pointerAccelerationProfileAdaptive WRITE setPointerAccelerationProfileAdaptive NOTIFY
|
||||
pointerAccelerationProfileChanged)
|
||||
|
||||
//
|
||||
// tapping
|
||||
Q_PROPERTY(int tapFingerCount READ tapFingerCount CONSTANT)
|
||||
Q_PROPERTY(bool tapToClickEnabledByDefault READ tapToClickEnabledByDefault CONSTANT)
|
||||
Q_PROPERTY(bool tapToClick READ isTapToClick WRITE setTapToClick NOTIFY tapToClickChanged)
|
||||
|
||||
Q_PROPERTY(bool supportsLmrTapButtonMap READ supportsLmrTapButtonMap CONSTANT)
|
||||
Q_PROPERTY(bool lmrTapButtonMapEnabledByDefault READ lmrTapButtonMapEnabledByDefault CONSTANT)
|
||||
Q_PROPERTY(bool lmrTapButtonMap READ lmrTapButtonMap WRITE setLmrTapButtonMap NOTIFY lmrTapButtonMapChanged)
|
||||
|
||||
Q_PROPERTY(bool tapAndDragEnabledByDefault READ tapAndDragEnabledByDefault CONSTANT)
|
||||
Q_PROPERTY(bool tapAndDrag READ isTapAndDrag WRITE setTapAndDrag NOTIFY tapAndDragChanged)
|
||||
|
||||
Q_PROPERTY(bool tapDragLockEnabledByDefault READ tapDragLockEnabledByDefault CONSTANT)
|
||||
Q_PROPERTY(bool tapDragLock READ isTapDragLock WRITE setTapDragLock NOTIFY tapDragLockChanged)
|
||||
|
||||
//
|
||||
// scrolling
|
||||
Q_PROPERTY(bool supportsNaturalScroll READ supportsNaturalScroll CONSTANT)
|
||||
Q_PROPERTY(bool naturalScrollEnabledByDefault READ naturalScrollEnabledByDefault CONSTANT)
|
||||
Q_PROPERTY(bool naturalScroll READ isNaturalScroll WRITE setNaturalScroll NOTIFY naturalScrollChanged)
|
||||
|
||||
Q_PROPERTY(bool supportsHorizontalScrolling READ supportsHorizontalScrolling CONSTANT)
|
||||
Q_PROPERTY(bool horizontalScrollingByDefault READ horizontalScrollingByDefault CONSTANT)
|
||||
Q_PROPERTY(bool horizontalScrolling READ horizontalScrolling WRITE setHorizontalScrolling NOTIFY horizontalScrollingChanged)
|
||||
|
||||
Q_PROPERTY(bool supportsScrollTwoFinger READ supportsScrollTwoFinger CONSTANT)
|
||||
Q_PROPERTY(bool scrollTwoFingerEnabledByDefault READ scrollTwoFingerEnabledByDefault CONSTANT)
|
||||
Q_PROPERTY(bool scrollTwoFinger READ isScrollTwoFinger WRITE setScrollTwoFinger NOTIFY scrollMethodChanged)
|
||||
|
||||
Q_PROPERTY(bool supportsScrollEdge READ supportsScrollEdge CONSTANT)
|
||||
Q_PROPERTY(bool scrollEdgeEnabledByDefault READ scrollEdgeEnabledByDefault CONSTANT)
|
||||
Q_PROPERTY(bool scrollEdge READ isScrollEdge WRITE setScrollEdge NOTIFY scrollMethodChanged)
|
||||
|
||||
Q_PROPERTY(bool supportsScrollOnButtonDown READ supportsScrollOnButtonDown CONSTANT)
|
||||
Q_PROPERTY(bool scrollOnButtonDownEnabledByDefault READ scrollOnButtonDownEnabledByDefault CONSTANT)
|
||||
Q_PROPERTY(bool scrollOnButtonDown READ isScrollOnButtonDown WRITE setScrollOnButtonDown NOTIFY scrollMethodChanged)
|
||||
|
||||
Q_PROPERTY(quint32 defaultScrollButton READ defaultScrollButton CONSTANT)
|
||||
Q_PROPERTY(quint32 scrollButton READ scrollButton WRITE setScrollButton NOTIFY scrollButtonChanged)
|
||||
|
||||
// Click Methods
|
||||
Q_PROPERTY(bool supportsClickMethodAreas READ supportsClickMethodAreas CONSTANT)
|
||||
Q_PROPERTY(bool defaultClickMethodAreas READ defaultClickMethodAreas CONSTANT)
|
||||
Q_PROPERTY(bool clickMethodAreas READ isClickMethodAreas WRITE setClickMethodAreas NOTIFY clickMethodChanged)
|
||||
|
||||
Q_PROPERTY(bool supportsClickMethodClickfinger READ supportsClickMethodClickfinger CONSTANT)
|
||||
Q_PROPERTY(bool defaultClickMethodClickfinger READ defaultClickMethodClickfinger CONSTANT)
|
||||
Q_PROPERTY(bool clickMethodClickfinger READ isClickMethodClickfinger WRITE setClickMethodClickfinger NOTIFY clickMethodChanged)
|
||||
|
||||
Q_PROPERTY(bool supportsScrollFactor READ supportsScrollFactor CONSTANT)
|
||||
public:
|
||||
LibinputCommon()
|
||||
{
|
||||
}
|
||||
virtual ~LibinputCommon()
|
||||
{
|
||||
}
|
||||
|
||||
virtual QString name() const = 0;
|
||||
virtual bool supportsDisableEvents() const = 0;
|
||||
virtual bool isEnabled() const = 0;
|
||||
virtual void setEnabled(bool set) = 0;
|
||||
|
||||
//
|
||||
// advanced
|
||||
Qt::MouseButtons supportedButtons() const
|
||||
{
|
||||
return m_supportedButtons.val;
|
||||
}
|
||||
|
||||
virtual bool supportsLeftHanded() const = 0;
|
||||
bool leftHandedEnabledByDefault() const
|
||||
{
|
||||
return m_leftHandedEnabledByDefault.val;
|
||||
}
|
||||
bool isLeftHanded() const
|
||||
{
|
||||
return m_leftHanded.val;
|
||||
}
|
||||
void setLeftHanded(bool set)
|
||||
{
|
||||
m_leftHanded.set(set);
|
||||
}
|
||||
|
||||
virtual bool supportsDisableEventsOnExternalMouse() const = 0;
|
||||
|
||||
virtual bool supportsDisableWhileTyping() const = 0;
|
||||
bool disableWhileTypingEnabledByDefault() const
|
||||
{
|
||||
return m_disableWhileTypingEnabledByDefault.val;
|
||||
}
|
||||
bool isDisableWhileTyping() const
|
||||
{
|
||||
return m_disableWhileTyping.val;
|
||||
}
|
||||
void setDisableWhileTyping(bool set)
|
||||
{
|
||||
m_disableWhileTyping.set(set);
|
||||
}
|
||||
|
||||
virtual bool supportsMiddleEmulation() const = 0;
|
||||
bool middleEmulationEnabledByDefault() const
|
||||
{
|
||||
return m_middleEmulationEnabledByDefault.val;
|
||||
}
|
||||
bool isMiddleEmulation() const
|
||||
{
|
||||
return m_middleEmulation.val;
|
||||
}
|
||||
void setMiddleEmulation(bool set)
|
||||
{
|
||||
m_middleEmulation.set(set);
|
||||
}
|
||||
|
||||
virtual bool supportsPointerAcceleration() const = 0;
|
||||
qreal pointerAcceleration() const
|
||||
{
|
||||
return m_pointerAcceleration.val;
|
||||
}
|
||||
void setPointerAcceleration(qreal acceleration)
|
||||
{
|
||||
m_pointerAcceleration.set(acceleration);
|
||||
}
|
||||
|
||||
virtual bool supportsPointerAccelerationProfileFlat() const = 0;
|
||||
bool defaultPointerAccelerationProfileFlat() const
|
||||
{
|
||||
return m_defaultPointerAccelerationProfileFlat.val;
|
||||
}
|
||||
bool pointerAccelerationProfileFlat() const
|
||||
{
|
||||
return m_pointerAccelerationProfileFlat.val;
|
||||
}
|
||||
void setPointerAccelerationProfileFlat(bool set)
|
||||
{
|
||||
m_pointerAccelerationProfileFlat.set(set);
|
||||
}
|
||||
|
||||
virtual bool supportsPointerAccelerationProfileAdaptive() const = 0;
|
||||
bool defaultPointerAccelerationProfileAdaptive() const
|
||||
{
|
||||
return m_defaultPointerAccelerationProfileAdaptive.val;
|
||||
}
|
||||
bool pointerAccelerationProfileAdaptive() const
|
||||
{
|
||||
return m_pointerAccelerationProfileAdaptive.val;
|
||||
}
|
||||
void setPointerAccelerationProfileAdaptive(bool set)
|
||||
{
|
||||
m_pointerAccelerationProfileAdaptive.set(set);
|
||||
}
|
||||
|
||||
//
|
||||
// scrolling
|
||||
virtual bool supportsNaturalScroll() const = 0;
|
||||
bool naturalScrollEnabledByDefault() const
|
||||
{
|
||||
return m_naturalScrollEnabledByDefault.val;
|
||||
}
|
||||
bool isNaturalScroll() const
|
||||
{
|
||||
return m_naturalScroll.val;
|
||||
}
|
||||
void setNaturalScroll(bool set)
|
||||
{
|
||||
m_naturalScroll.set(set);
|
||||
}
|
||||
|
||||
virtual bool supportsHorizontalScrolling() const = 0;
|
||||
bool horizontalScrollingByDefault() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool horizontalScrolling() const
|
||||
{
|
||||
return m_horizontalScrolling.val;
|
||||
}
|
||||
void setHorizontalScrolling(bool set)
|
||||
{
|
||||
m_horizontalScrolling.set(set);
|
||||
}
|
||||
|
||||
virtual bool supportsScrollTwoFinger() const = 0;
|
||||
bool scrollTwoFingerEnabledByDefault() const
|
||||
{
|
||||
return m_scrollTwoFingerEnabledByDefault.val;
|
||||
}
|
||||
bool isScrollTwoFinger() const
|
||||
{
|
||||
return m_isScrollTwoFinger.val;
|
||||
}
|
||||
void setScrollTwoFinger(bool set)
|
||||
{
|
||||
m_isScrollTwoFinger.set(set);
|
||||
}
|
||||
|
||||
virtual bool supportsScrollEdge() const = 0;
|
||||
bool scrollEdgeEnabledByDefault() const
|
||||
{
|
||||
return m_scrollEdgeEnabledByDefault.val;
|
||||
}
|
||||
bool isScrollEdge() const
|
||||
{
|
||||
return m_isScrollEdge.val;
|
||||
}
|
||||
void setScrollEdge(bool set)
|
||||
{
|
||||
m_isScrollEdge.set(set);
|
||||
}
|
||||
|
||||
virtual bool supportsScrollOnButtonDown() const = 0;
|
||||
bool scrollOnButtonDownEnabledByDefault() const
|
||||
{
|
||||
return m_scrollOnButtonDownEnabledByDefault.val;
|
||||
}
|
||||
bool isScrollOnButtonDown() const
|
||||
{
|
||||
return m_isScrollOnButtonDown.val;
|
||||
}
|
||||
void setScrollOnButtonDown(bool set)
|
||||
{
|
||||
m_isScrollOnButtonDown.set(set);
|
||||
}
|
||||
|
||||
quint32 defaultScrollButton() const
|
||||
{
|
||||
return m_defaultScrollButton.val;
|
||||
}
|
||||
quint32 scrollButton() const
|
||||
{
|
||||
return m_scrollButton.val;
|
||||
}
|
||||
void setScrollButton(quint32 button)
|
||||
{
|
||||
m_scrollButton.set(button);
|
||||
}
|
||||
|
||||
//
|
||||
// tapping
|
||||
int tapFingerCount() const
|
||||
{
|
||||
return m_tapFingerCount.val;
|
||||
}
|
||||
bool tapToClickEnabledByDefault() const
|
||||
{
|
||||
return m_tapToClickEnabledByDefault.val;
|
||||
}
|
||||
bool isTapToClick() const
|
||||
{
|
||||
return m_tapToClick.val;
|
||||
}
|
||||
void setTapToClick(bool set)
|
||||
{
|
||||
m_tapToClick.set(set);
|
||||
}
|
||||
|
||||
bool supportsLmrTapButtonMap() const
|
||||
{
|
||||
return m_tapFingerCount.val > 1;
|
||||
}
|
||||
bool lmrTapButtonMapEnabledByDefault() const
|
||||
{
|
||||
return m_lmrTapButtonMapEnabledByDefault.val;
|
||||
}
|
||||
bool lmrTapButtonMap() const
|
||||
{
|
||||
return m_lmrTapButtonMap.val;
|
||||
}
|
||||
virtual void setLmrTapButtonMap(bool set) = 0;
|
||||
|
||||
bool tapAndDragEnabledByDefault() const
|
||||
{
|
||||
return m_tapAndDragEnabledByDefault.val;
|
||||
}
|
||||
bool isTapAndDrag() const
|
||||
{
|
||||
return m_tapAndDrag.val;
|
||||
}
|
||||
void setTapAndDrag(bool set)
|
||||
{
|
||||
m_tapAndDrag.set(set);
|
||||
}
|
||||
|
||||
bool tapDragLockEnabledByDefault() const
|
||||
{
|
||||
return m_tapDragLockEnabledByDefault.val;
|
||||
}
|
||||
bool isTapDragLock() const
|
||||
{
|
||||
return m_tapDragLock.val;
|
||||
}
|
||||
void setTapDragLock(bool set)
|
||||
{
|
||||
m_tapDragLock.set(set);
|
||||
}
|
||||
|
||||
//
|
||||
// click method
|
||||
virtual bool supportsClickMethodAreas() const = 0;
|
||||
bool defaultClickMethodAreas() const
|
||||
{
|
||||
return m_defaultClickMethodAreas.val;
|
||||
}
|
||||
bool isClickMethodAreas() const
|
||||
{
|
||||
return m_clickMethodAreas.val;
|
||||
}
|
||||
void setClickMethodAreas(bool set)
|
||||
{
|
||||
m_clickMethodAreas.set(set);
|
||||
}
|
||||
|
||||
virtual bool supportsClickMethodClickfinger() const = 0;
|
||||
bool defaultClickMethodClickfinger() const
|
||||
{
|
||||
return m_defaultClickMethodClickfinger.val;
|
||||
}
|
||||
bool isClickMethodClickfinger() const
|
||||
{
|
||||
return m_clickMethodClickfinger.val;
|
||||
}
|
||||
void setClickMethodClickfinger(bool set)
|
||||
{
|
||||
m_clickMethodClickfinger.set(set);
|
||||
}
|
||||
|
||||
virtual bool supportsScrollFactor() const = 0;
|
||||
|
||||
Q_SIGNALS:
|
||||
void enabledChanged();
|
||||
// Tapping
|
||||
void tapToClickChanged();
|
||||
void lmrTapButtonMapChanged();
|
||||
void tapAndDragChanged();
|
||||
void tapDragLockChanged();
|
||||
// Advanced
|
||||
void leftHandedChanged();
|
||||
void disableWhileTypingChanged();
|
||||
void middleEmulationChanged();
|
||||
// acceleration speed and profile
|
||||
void pointerAccelerationChanged();
|
||||
void pointerAccelerationProfileChanged();
|
||||
// scrolling
|
||||
void naturalScrollChanged();
|
||||
void horizontalScrollingChanged();
|
||||
void scrollMethodChanged();
|
||||
void scrollButtonChanged();
|
||||
// click methods
|
||||
void clickMethodChanged();
|
||||
|
||||
protected:
|
||||
template<typename T>
|
||||
struct Prop {
|
||||
explicit Prop(const QByteArray &name)
|
||||
: name(name)
|
||||
{
|
||||
}
|
||||
|
||||
void set(T newVal)
|
||||
{
|
||||
if (avail && val != newVal) {
|
||||
val = newVal;
|
||||
}
|
||||
}
|
||||
void set(const Prop<T> &p)
|
||||
{
|
||||
if (avail && val != p.val) {
|
||||
val = p.val;
|
||||
}
|
||||
}
|
||||
bool changed() const
|
||||
{
|
||||
return avail && (old != val);
|
||||
}
|
||||
|
||||
// In wayland, name will be dbus name
|
||||
QByteArray name;
|
||||
bool avail;
|
||||
T old;
|
||||
T val;
|
||||
};
|
||||
|
||||
//
|
||||
// general
|
||||
Prop<bool> m_supportsDisableEvents = Prop<bool>("supportsDisableEvents");
|
||||
Prop<bool> m_enabledDefault = Prop<bool>("enabledDefault");
|
||||
Prop<bool> m_enabled = Prop<bool>("enabled");
|
||||
|
||||
//
|
||||
// advanced
|
||||
Prop<Qt::MouseButtons> m_supportedButtons = Prop<Qt::MouseButtons>("supportedButtons");
|
||||
|
||||
Prop<bool> m_leftHandedEnabledByDefault = Prop<bool>("leftHandedEnabledByDefault");
|
||||
Prop<bool> m_leftHanded = Prop<bool>("leftHanded");
|
||||
|
||||
Prop<bool> m_supportsDisableEventsOnExternalMouse = Prop<bool>("supportsDisableEventsOnExternalMouse");
|
||||
|
||||
Prop<bool> m_disableWhileTypingEnabledByDefault = Prop<bool>("disableWhileTypingEnabledByDefault");
|
||||
Prop<bool> m_disableWhileTyping = Prop<bool>("disableWhileTyping");
|
||||
|
||||
Prop<bool> m_middleEmulationEnabledByDefault = Prop<bool>("middleEmulationEnabledByDefault");
|
||||
Prop<bool> m_middleEmulation = Prop<bool>("middleEmulation");
|
||||
|
||||
//
|
||||
// acceleration speed and profile
|
||||
Prop<qreal> m_defaultPointerAcceleration = Prop<qreal>("defaultPointerAcceleration");
|
||||
Prop<qreal> m_pointerAcceleration = Prop<qreal>("pointerAcceleration");
|
||||
|
||||
Prop<bool> m_supportsPointerAccelerationProfileFlat = Prop<bool>("supportsPointerAccelerationProfileFlat");
|
||||
Prop<bool> m_defaultPointerAccelerationProfileFlat = Prop<bool>("defaultPointerAccelerationProfileFlat");
|
||||
Prop<bool> m_pointerAccelerationProfileFlat = Prop<bool>("pointerAccelerationProfileFlat");
|
||||
|
||||
Prop<bool> m_supportsPointerAccelerationProfileAdaptive = Prop<bool>("supportsPointerAccelerationProfileAdaptive");
|
||||
Prop<bool> m_defaultPointerAccelerationProfileAdaptive = Prop<bool>("defaultPointerAccelerationProfileAdaptive");
|
||||
Prop<bool> m_pointerAccelerationProfileAdaptive = Prop<bool>("pointerAccelerationProfileAdaptive");
|
||||
|
||||
//
|
||||
// tapping
|
||||
Prop<int> m_tapFingerCount = Prop<int>("tapFingerCount");
|
||||
Prop<bool> m_tapToClickEnabledByDefault = Prop<bool>("tapToClickEnabledByDefault");
|
||||
Prop<bool> m_tapToClick = Prop<bool>("tapToClick");
|
||||
|
||||
Prop<bool> m_lmrTapButtonMapEnabledByDefault = Prop<bool>("lmrTapButtonMapEnabledByDefault");
|
||||
Prop<bool> m_lmrTapButtonMap = Prop<bool>("lmrTapButtonMap");
|
||||
|
||||
Prop<bool> m_tapAndDragEnabledByDefault = Prop<bool>("tapAndDragEnabledByDefault");
|
||||
Prop<bool> m_tapAndDrag = Prop<bool>("tapAndDrag");
|
||||
Prop<bool> m_tapDragLockEnabledByDefault = Prop<bool>("tapDragLockEnabledByDefault");
|
||||
Prop<bool> m_tapDragLock = Prop<bool>("tapDragLock");
|
||||
|
||||
//
|
||||
// scrolling
|
||||
Prop<bool> m_naturalScrollEnabledByDefault = Prop<bool>("naturalScrollEnabledByDefault");
|
||||
Prop<bool> m_naturalScroll = Prop<bool>("naturalScroll");
|
||||
|
||||
Prop<bool> m_horizontalScrolling = Prop<bool>("horizontalScrolling");
|
||||
|
||||
Prop<bool> m_supportsScrollTwoFinger = Prop<bool>("supportsScrollTwoFinger");
|
||||
Prop<bool> m_scrollTwoFingerEnabledByDefault = Prop<bool>("scrollTwoFingerEnabledByDefault");
|
||||
Prop<bool> m_isScrollTwoFinger = Prop<bool>("scrollTwoFinger");
|
||||
|
||||
Prop<bool> m_supportsScrollEdge = Prop<bool>("supportsScrollEdge");
|
||||
Prop<bool> m_scrollEdgeEnabledByDefault = Prop<bool>("scrollEdgeEnabledByDefault");
|
||||
Prop<bool> m_isScrollEdge = Prop<bool>("scrollEdge");
|
||||
|
||||
Prop<bool> m_supportsScrollOnButtonDown = Prop<bool>("supportsScrollOnButtonDown");
|
||||
Prop<bool> m_scrollOnButtonDownEnabledByDefault = Prop<bool>("scrollOnButtonDownEnabledByDefault");
|
||||
Prop<bool> m_isScrollOnButtonDown = Prop<bool>("scrollOnButtonDown");
|
||||
|
||||
Prop<quint32> m_defaultScrollButton = Prop<quint32>("defaultScrollButton");
|
||||
Prop<quint32> m_scrollButton = Prop<quint32>("scrollButton");
|
||||
|
||||
// Click Method
|
||||
Prop<bool> m_supportsClickMethodAreas = Prop<bool>("supportsClickMethodAreas");
|
||||
Prop<bool> m_defaultClickMethodAreas = Prop<bool>("defaultClickMethodAreas");
|
||||
Prop<bool> m_clickMethodAreas = Prop<bool>("clickMethodAreas");
|
||||
|
||||
Prop<bool> m_supportsClickMethodClickfinger = Prop<bool>("supportsClickMethodClickfinger");
|
||||
Prop<bool> m_defaultClickMethodClickfinger = Prop<bool>("defaultClickMethodClickfinger");
|
||||
Prop<bool> m_clickMethodClickfinger = Prop<bool>("clickMethodClickfinger");
|
||||
};
|
||||
|
||||
#endif // LIBINPUTCOMMON_H
|
||||
@ -1,66 +1,80 @@
|
||||
#include "touchpadmanager.h"
|
||||
#include "touchpadadaptor.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include "input/kwininputbackend.h"
|
||||
|
||||
#include <QDBusConnection>
|
||||
|
||||
TouchpadManager::TouchpadManager(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_backend(XlibBackend::initialize())
|
||||
, m_backend(new KWinInputBackend(KWinInputBackend::DeviceType::Touchpad, this))
|
||||
{
|
||||
// init dbus
|
||||
new TouchpadAdaptor(this);
|
||||
QDBusConnection::sessionBus().registerObject(QStringLiteral("/Touchpad"), this);
|
||||
|
||||
m_backend->getConfig();
|
||||
m_backend->applyConfig();
|
||||
connect(m_backend, &KWinInputBackend::devicesChanged, this, [this] {
|
||||
emit availableChanged();
|
||||
emit enabledChanged();
|
||||
emit tapToClickChanged();
|
||||
emit naturalScrollChanged();
|
||||
emit pointerAccelerationChanged();
|
||||
});
|
||||
}
|
||||
|
||||
bool TouchpadManager::available() const
|
||||
{
|
||||
return m_backend->isTouchpadAvailable();
|
||||
return m_backend->available();
|
||||
}
|
||||
|
||||
bool TouchpadManager::enabled() const
|
||||
{
|
||||
return m_backend->isTouchpadEnabled();
|
||||
return m_backend->booleanProperty(QStringLiteral("enabled"), true);
|
||||
}
|
||||
|
||||
void TouchpadManager::setEnabled(bool enabled)
|
||||
{
|
||||
m_backend->setTouchpadEnabled(enabled);
|
||||
m_backend->applyConfig();
|
||||
if (this->enabled() == enabled)
|
||||
return;
|
||||
m_backend->setBooleanProperty(QStringLiteral("enabled"), enabled);
|
||||
emit enabledChanged();
|
||||
}
|
||||
|
||||
bool TouchpadManager::tapToClick() const
|
||||
{
|
||||
return m_backend->tapToClick();
|
||||
return m_backend->booleanProperty(QStringLiteral("tapToClick"));
|
||||
}
|
||||
|
||||
void TouchpadManager::setTapToClick(bool value)
|
||||
{
|
||||
m_backend->setTapToClick(value);
|
||||
m_backend->applyConfig();
|
||||
if (tapToClick() == value)
|
||||
return;
|
||||
m_backend->setBooleanProperty(QStringLiteral("tapToClick"), value);
|
||||
emit tapToClickChanged();
|
||||
}
|
||||
|
||||
bool TouchpadManager::naturalScroll() const
|
||||
{
|
||||
return m_backend->naturalScroll();
|
||||
return m_backend->booleanProperty(QStringLiteral("naturalScroll"));
|
||||
}
|
||||
|
||||
void TouchpadManager::setNaturalScroll(bool naturalScroll)
|
||||
{
|
||||
m_backend->setNaturalScroll(naturalScroll);
|
||||
m_backend->applyConfig();
|
||||
if (this->naturalScroll() == naturalScroll)
|
||||
return;
|
||||
m_backend->setBooleanProperty(QStringLiteral("naturalScroll"), naturalScroll);
|
||||
emit naturalScrollChanged();
|
||||
}
|
||||
|
||||
qreal TouchpadManager::pointerAcceleration() const
|
||||
{
|
||||
return m_backend->pointerAcceleration();
|
||||
return m_backend->realProperty(QStringLiteral("pointerAcceleration"));
|
||||
}
|
||||
|
||||
void TouchpadManager::setPointerAcceleration(qreal value)
|
||||
{
|
||||
qDebug() << value;
|
||||
m_backend->setPointerAcceleration(value);
|
||||
m_backend->applyConfig();
|
||||
value = qBound<qreal>(-1.0, value, 1.0);
|
||||
if (qFuzzyCompare(1.0 + pointerAcceleration(), 1.0 + value))
|
||||
return;
|
||||
m_backend->setRealProperty(QStringLiteral("pointerAcceleration"), value);
|
||||
emit pointerAccelerationChanged();
|
||||
}
|
||||
|
||||
@ -1,401 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2019 Atul Bisht <atulbisht26@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "libinputtouchpad.h"
|
||||
|
||||
#include <QSet>
|
||||
#include <QDebug>
|
||||
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <libinput-properties.h>
|
||||
#include <xserver-properties.h>
|
||||
|
||||
#include <X11/extensions/XInput2.h>
|
||||
|
||||
const Parameter libinputProperties[] = {
|
||||
|
||||
/* libinput disable supports property */
|
||||
{"supportsDisableEvents", PT_INT, 0, 1, LIBINPUT_PROP_SENDEVENTS_AVAILABLE, 8, 0},
|
||||
{"enabled", PT_INT, 0, 1, LIBINPUT_PROP_SENDEVENTS_ENABLED, 8, 0},
|
||||
{"enabledDefault", PT_INT, 0, 1, LIBINPUT_PROP_SENDEVENTS_ENABLED_DEFAULT, 8, 0},
|
||||
|
||||
/* LeftHandSupport */
|
||||
{"leftHandedEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_LEFT_HANDED_DEFAULT, 8, 0},
|
||||
{"leftHanded", PT_INT, 0, 1, LIBINPUT_PROP_LEFT_HANDED, 8, 0},
|
||||
|
||||
/* Disable on external mouse */
|
||||
{"supportsDisableEventsOnExternalMouse", PT_INT, 0, 1, LIBINPUT_PROP_SENDEVENTS_AVAILABLE, 8, 1},
|
||||
{"disableEventsOnExternalMouse", PT_INT, 0, 1, LIBINPUT_PROP_SENDEVENTS_ENABLED, 8, 1},
|
||||
{"disableEventsOnExternalMouseDefault", PT_INT, 0, 1, LIBINPUT_PROP_SENDEVENTS_ENABLED_DEFAULT, 8, 1},
|
||||
|
||||
/* Disable while typing */
|
||||
{"disableWhileTypingEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_DISABLE_WHILE_TYPING_DEFAULT, 8, 0},
|
||||
{"disableWhileTyping", PT_INT, 0, 1, LIBINPUT_PROP_DISABLE_WHILE_TYPING, 8, 0},
|
||||
|
||||
/* Middle Emulation */
|
||||
{"middleEmulationEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_MIDDLE_EMULATION_ENABLED_DEFAULT, 8, 0},
|
||||
{"middleEmulation", PT_INT, 0, 1, LIBINPUT_PROP_MIDDLE_EMULATION_ENABLED, 8, 0},
|
||||
|
||||
/* This is a boolean for all three fingers, no per-finger config */
|
||||
{"tapToClick", PT_INT, 0, 1, LIBINPUT_PROP_TAP, 8, 0},
|
||||
{"tapToClickEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_TAP_DEFAULT, 8, 0},
|
||||
|
||||
/* LMR */
|
||||
{"lrmTapButtonMapEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_TAP_BUTTONMAP_DEFAULT, 8, 0},
|
||||
{"lrmTapButtonMap", PT_INT, 0, 1, LIBINPUT_PROP_TAP_BUTTONMAP, 8, 0},
|
||||
{"lmrTapButtonMapEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_TAP_BUTTONMAP_DEFAULT, 8, 1},
|
||||
{"lmrTapButtonMap", PT_INT, 0, 1, LIBINPUT_PROP_TAP_BUTTONMAP, 8, 1},
|
||||
|
||||
/* Tap and Drag Enabled */
|
||||
{"tapAndDragEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_TAP_DRAG_DEFAULT, 8, 0},
|
||||
{"tapAndDrag", PT_INT, 0, 1, LIBINPUT_PROP_TAP_DRAG, 8, 0},
|
||||
|
||||
/* Tap and Drag Lock Enabled */
|
||||
{"tapDragLockEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_TAP_DRAG_LOCK_DEFAULT, 8, 0},
|
||||
{"tapDragLock", PT_INT, 0, 1, LIBINPUT_PROP_TAP_DRAG_LOCK, 8, 0},
|
||||
|
||||
/* libinput normalizes the accel to -1/1 */
|
||||
{"defaultPointerAcceleration", PT_DOUBLE, -1.0, 1.0, LIBINPUT_PROP_ACCEL_DEFAULT, 0 /*float */, 0},
|
||||
{"pointerAcceleration", PT_DOUBLE, -1.0, 1.0, LIBINPUT_PROP_ACCEL, 0 /*float */, 0},
|
||||
|
||||
/* Libinput Accel Profile */
|
||||
{"supportsPointerAccelerationProfileAdaptive", PT_BOOL, 0, 1, LIBINPUT_PROP_ACCEL_PROFILES_AVAILABLE, 8, 0},
|
||||
{"defaultPointerAccelerationProfileAdaptive", PT_BOOL, 0, 1, LIBINPUT_PROP_ACCEL_PROFILE_ENABLED_DEFAULT, 8, 0},
|
||||
{"pointerAccelerationProfileAdaptive", PT_BOOL, 0, 1, LIBINPUT_PROP_ACCEL_PROFILE_ENABLED, 8, 0},
|
||||
{"supportsPointerAccelerationProfileFlat", PT_BOOL, 0, 1, LIBINPUT_PROP_ACCEL_PROFILES_AVAILABLE, 8, 1},
|
||||
{"defaultPointerAccelerationProfileFlat", PT_BOOL, 0, 1, LIBINPUT_PROP_ACCEL_PROFILE_ENABLED_DEFAULT, 8, 1},
|
||||
{"pointerAccelerationProfileFlat", PT_BOOL, 0, 1, LIBINPUT_PROP_ACCEL_PROFILE_ENABLED, 8, 1},
|
||||
|
||||
/* Natural Scrolling */
|
||||
{"naturalScrollEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_NATURAL_SCROLL_DEFAULT, 8, 0},
|
||||
{"naturalScroll", PT_INT, 0, 1, LIBINPUT_PROP_NATURAL_SCROLL, 8, 0},
|
||||
|
||||
/* Horizontal scrolling */
|
||||
{"horizontalScrolling", PT_INT, 0, 1, LIBINPUT_PROP_HORIZ_SCROLL_ENABLED, 8, 0},
|
||||
|
||||
/* Two-Finger Scrolling */
|
||||
{"supportsScrollTwoFinger", PT_INT, 0, 1, LIBINPUT_PROP_SCROLL_METHODS_AVAILABLE, 8, 0},
|
||||
{"scrollTwoFingerEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_SCROLL_METHOD_ENABLED_DEFAULT, 8, 0},
|
||||
{"scrollTwoFinger", PT_INT, 0, 1, LIBINPUT_PROP_SCROLL_METHOD_ENABLED, 8, 0},
|
||||
|
||||
/* Edge Scrolling */
|
||||
{"supportsScrollEdge", PT_INT, 0, 1, LIBINPUT_PROP_SCROLL_METHODS_AVAILABLE, 8, 1},
|
||||
{"scrollEdgeEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_SCROLL_METHOD_ENABLED_DEFAULT, 8, 1},
|
||||
{"scrollEdge", PT_INT, 0, 1, LIBINPUT_PROP_SCROLL_METHOD_ENABLED, 8, 1},
|
||||
|
||||
/* scroll on button */
|
||||
{"supportsScrollOnButtonDown", PT_INT, 0, 1, LIBINPUT_PROP_SCROLL_METHODS_AVAILABLE, 8, 2},
|
||||
{"scrollOnButtonDownEnabledByDefault", PT_INT, 0, 1, LIBINPUT_PROP_SCROLL_METHOD_ENABLED_DEFAULT, 8, 2},
|
||||
{"scrollOnButtonDown", PT_INT, 0, 1, LIBINPUT_PROP_SCROLL_METHOD_ENABLED, 8, 2},
|
||||
|
||||
/* Scroll Button for scroll on button Down */
|
||||
{"defaultScrollButton", PT_INT, 0, INT_MAX, LIBINPUT_PROP_SCROLL_BUTTON_DEFAULT, 32, 0},
|
||||
{"scrollButton", PT_INT, 0, INT_MAX, LIBINPUT_PROP_SCROLL_BUTTON, 32, 0},
|
||||
|
||||
/* Click Methods */
|
||||
{"supportsClickMethodAreas", PT_INT, 0, 1, LIBINPUT_PROP_CLICK_METHODS_AVAILABLE, 8, 0},
|
||||
{"defaultClickMethodAreas", PT_INT, 0, 1, LIBINPUT_PROP_CLICK_METHOD_ENABLED_DEFAULT, 8, 0},
|
||||
{"clickMethodAreas", PT_INT, 0, 1, LIBINPUT_PROP_CLICK_METHOD_ENABLED, 8, 0},
|
||||
|
||||
{"supportsClickMethodClickfinger", PT_INT, 0, 1, LIBINPUT_PROP_CLICK_METHODS_AVAILABLE, 8, 1},
|
||||
{"defaultClickMethodClickfinger", PT_INT, 0, 1, LIBINPUT_PROP_CLICK_METHOD_ENABLED_DEFAULT, 8, 1},
|
||||
{"clickMethodClickfinger", PT_INT, 0, 1, LIBINPUT_PROP_CLICK_METHOD_ENABLED, 8, 1},
|
||||
|
||||
/* libinput doesn't have a separate toggle for horiz scrolling */
|
||||
{nullptr, PT_INT, 0, 0, nullptr, 0, 0}};
|
||||
|
||||
Qt::MouseButtons maskBtns(Display *display, XIButtonClassInfo *buttonInfo)
|
||||
{
|
||||
Qt::MouseButtons buttons = Qt::NoButton;
|
||||
for (int i = 0; i < buttonInfo->num_buttons; ++i) {
|
||||
QByteArray reply = XGetAtomName(display, buttonInfo->labels[i]);
|
||||
|
||||
if (reply == BTN_LABEL_PROP_BTN_LEFT) {
|
||||
buttons |= Qt::LeftButton;
|
||||
}
|
||||
if (reply == BTN_LABEL_PROP_BTN_RIGHT) {
|
||||
buttons |= Qt::RightButton;
|
||||
}
|
||||
if (reply == BTN_LABEL_PROP_BTN_MIDDLE) {
|
||||
buttons |= Qt::MiddleButton;
|
||||
}
|
||||
if (reply == BTN_LABEL_PROP_BTN_SIDE) {
|
||||
buttons |= Qt::ExtraButton1;
|
||||
}
|
||||
if (reply == BTN_LABEL_PROP_BTN_EXTRA) {
|
||||
buttons |= Qt::ExtraButton2;
|
||||
}
|
||||
if (reply == BTN_LABEL_PROP_BTN_FORWARD) {
|
||||
buttons |= Qt::ForwardButton;
|
||||
}
|
||||
if (reply == BTN_LABEL_PROP_BTN_BACK) {
|
||||
buttons |= Qt::BackButton;
|
||||
}
|
||||
if (reply == BTN_LABEL_PROP_BTN_TASK) {
|
||||
buttons |= Qt::TaskButton;
|
||||
}
|
||||
}
|
||||
return buttons;
|
||||
}
|
||||
|
||||
LibinputTouchpad::LibinputTouchpad(Display *display, int deviceId)
|
||||
: LibinputCommon()
|
||||
, XlibTouchpad(display, deviceId)
|
||||
, m_config("cutefishos", "touchpadxlibinputrc")
|
||||
{
|
||||
loadSupportedProperties(libinputProperties);
|
||||
|
||||
int nDevices = 0;
|
||||
XIDeviceInfo *deviceInfo = XIQueryDevice(m_display, m_deviceId, &nDevices);
|
||||
m_name = deviceInfo->name;
|
||||
|
||||
for (int i = 0; i < deviceInfo->num_classes; ++i) {
|
||||
XIAnyClassInfo *classInfo = deviceInfo->classes[i];
|
||||
|
||||
if (classInfo->type == XIButtonClass) {
|
||||
XIButtonClassInfo *btnInfo = (XIButtonClassInfo *)classInfo;
|
||||
m_supportedButtons.avail = true;
|
||||
m_supportedButtons.set(maskBtns(m_display, btnInfo));
|
||||
}
|
||||
if (classInfo->type == XITouchClass) {
|
||||
XITouchClassInfo *touchInfo = (XITouchClassInfo *)classInfo;
|
||||
m_tapFingerCount.avail = true;
|
||||
m_tapFingerCount.set(touchInfo->num_touches);
|
||||
}
|
||||
}
|
||||
XIFreeDeviceInfo(deviceInfo);
|
||||
|
||||
/* FingerCount cannot be zero */
|
||||
if (!m_tapFingerCount.val) {
|
||||
m_tapFingerCount.avail = true;
|
||||
m_tapFingerCount.set(1);
|
||||
}
|
||||
}
|
||||
|
||||
bool LibinputTouchpad::getConfig()
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
success &= valueLoader(m_supportsDisableEvents);
|
||||
success &= valueLoader(m_enabled);
|
||||
success &= valueLoader(m_enabledDefault);
|
||||
|
||||
success &= valueLoader(m_tapToClickEnabledByDefault);
|
||||
success &= valueLoader(m_tapToClick);
|
||||
success &= valueLoader(m_lrmTapButtonMapEnabledByDefault);
|
||||
success &= valueLoader(m_lrmTapButtonMap);
|
||||
success &= valueLoader(m_lmrTapButtonMapEnabledByDefault);
|
||||
success &= valueLoader(m_lmrTapButtonMap);
|
||||
success &= valueLoader(m_tapAndDragEnabledByDefault);
|
||||
success &= valueLoader(m_tapAndDrag);
|
||||
success &= valueLoader(m_tapDragLockEnabledByDefault);
|
||||
success &= valueLoader(m_tapDragLock);
|
||||
|
||||
success &= valueLoader(m_leftHandedEnabledByDefault);
|
||||
success &= valueLoader(m_leftHanded);
|
||||
|
||||
success &= valueLoader(m_supportsDisableEventsOnExternalMouse);
|
||||
success &= valueLoader(m_disableEventsOnExternalMouse);
|
||||
success &= valueLoader(m_disableEventsOnExternalMouseDefault);
|
||||
|
||||
success &= valueLoader(m_disableWhileTypingEnabledByDefault);
|
||||
success &= valueLoader(m_disableWhileTyping);
|
||||
|
||||
success &= valueLoader(m_middleEmulationEnabledByDefault);
|
||||
success &= valueLoader(m_middleEmulation);
|
||||
|
||||
success &= valueLoader(m_defaultPointerAcceleration);
|
||||
success &= valueLoader(m_pointerAcceleration);
|
||||
|
||||
success &= valueLoader(m_supportsPointerAccelerationProfileFlat);
|
||||
success &= valueLoader(m_defaultPointerAccelerationProfileFlat);
|
||||
success &= valueLoader(m_pointerAccelerationProfileFlat);
|
||||
success &= valueLoader(m_supportsPointerAccelerationProfileAdaptive);
|
||||
success &= valueLoader(m_defaultPointerAccelerationProfileAdaptive);
|
||||
success &= valueLoader(m_pointerAccelerationProfileAdaptive);
|
||||
|
||||
success &= valueLoader(m_naturalScrollEnabledByDefault);
|
||||
success &= valueLoader(m_naturalScroll);
|
||||
|
||||
success &= valueLoader(m_horizontalScrolling);
|
||||
|
||||
success &= valueLoader(m_supportsScrollTwoFinger);
|
||||
success &= valueLoader(m_scrollTwoFingerEnabledByDefault);
|
||||
success &= valueLoader(m_isScrollTwoFinger);
|
||||
|
||||
success &= valueLoader(m_supportsScrollEdge);
|
||||
success &= valueLoader(m_scrollEdgeEnabledByDefault);
|
||||
success &= valueLoader(m_isScrollEdge);
|
||||
|
||||
success &= valueLoader(m_supportsScrollOnButtonDown);
|
||||
success &= valueLoader(m_scrollOnButtonDownEnabledByDefault);
|
||||
success &= valueLoader(m_isScrollOnButtonDown);
|
||||
|
||||
success &= valueLoader(m_defaultScrollButton);
|
||||
success &= valueLoader(m_scrollButton);
|
||||
|
||||
// click methods
|
||||
success &= valueLoader(m_supportsClickMethodAreas);
|
||||
success &= valueLoader(m_supportsClickMethodClickfinger);
|
||||
success &= valueLoader(m_defaultClickMethodAreas);
|
||||
success &= valueLoader(m_defaultClickMethodClickfinger);
|
||||
success &= valueLoader(m_clickMethodAreas);
|
||||
success &= valueLoader(m_clickMethodClickfinger);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool LibinputTouchpad::applyConfig()
|
||||
{
|
||||
QVector<QString> msgs;
|
||||
|
||||
msgs << valueWriter(m_enabled) << valueWriter(m_tapToClick) << valueWriter(m_lrmTapButtonMap) << valueWriter(m_lmrTapButtonMap) << valueWriter(m_tapAndDrag)
|
||||
<< valueWriter(m_tapDragLock) << valueWriter(m_leftHanded) << valueWriter(m_disableWhileTyping) << valueWriter(m_middleEmulation)
|
||||
<< valueWriter(m_pointerAcceleration) << valueWriter(m_pointerAccelerationProfileFlat) << valueWriter(m_pointerAccelerationProfileAdaptive)
|
||||
<< valueWriter(m_naturalScroll) << valueWriter(m_horizontalScrolling) << valueWriter(m_isScrollTwoFinger) << valueWriter(m_isScrollEdge)
|
||||
<< valueWriter(m_isScrollOnButtonDown) << valueWriter(m_scrollButton) << valueWriter(m_clickMethodAreas) << valueWriter(m_clickMethodClickfinger);
|
||||
|
||||
bool success = true;
|
||||
QString error_msg;
|
||||
|
||||
for (QString m : msgs) {
|
||||
if (!m.isNull()) {
|
||||
// qCCritical(KCM_TOUCHPAD) << "in error:" << m;
|
||||
if (!success) {
|
||||
error_msg.append("\n");
|
||||
}
|
||||
error_msg.append(m);
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
// qCCritical(KCM_TOUCHPAD) << error_msg;
|
||||
}
|
||||
|
||||
flush();
|
||||
return success;
|
||||
}
|
||||
|
||||
bool LibinputTouchpad::getDefaultConfig()
|
||||
{
|
||||
m_enabled.set(m_enabledDefault);
|
||||
m_tapToClick.set(m_tapToClickEnabledByDefault);
|
||||
m_lrmTapButtonMap.set(m_lrmTapButtonMap);
|
||||
m_lmrTapButtonMap.set(m_lmrTapButtonMapEnabledByDefault);
|
||||
m_tapAndDrag.set(m_tapAndDragEnabledByDefault);
|
||||
m_tapDragLock.set(m_tapDragLockEnabledByDefault);
|
||||
m_leftHanded.set(m_leftHandedEnabledByDefault);
|
||||
m_disableEventsOnExternalMouse.set(m_disableEventsOnExternalMouseDefault);
|
||||
m_disableWhileTyping.set(m_disableWhileTypingEnabledByDefault);
|
||||
m_middleEmulation.set(m_middleEmulationEnabledByDefault);
|
||||
m_pointerAcceleration.set(m_defaultPointerAcceleration);
|
||||
m_pointerAccelerationProfileFlat.set(m_defaultPointerAccelerationProfileFlat);
|
||||
m_pointerAccelerationProfileAdaptive.set(m_defaultPointerAccelerationProfileAdaptive);
|
||||
m_naturalScroll.set(m_naturalScrollEnabledByDefault);
|
||||
m_horizontalScrolling.set(true);
|
||||
m_isScrollTwoFinger.set(m_scrollTwoFingerEnabledByDefault);
|
||||
m_isScrollEdge.set(m_scrollEdgeEnabledByDefault);
|
||||
m_isScrollOnButtonDown.set(m_scrollOnButtonDownEnabledByDefault);
|
||||
m_scrollButton.set(m_defaultScrollButton);
|
||||
m_clickMethodAreas.set(m_defaultClickMethodAreas);
|
||||
m_clickMethodClickfinger.set(m_defaultClickMethodClickfinger);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LibinputTouchpad::isChangedConfig()
|
||||
{
|
||||
// clang-format off
|
||||
bool changed = m_enabled.changed() ||
|
||||
m_tapToClick.changed() ||
|
||||
m_lrmTapButtonMap.changed() ||
|
||||
m_lmrTapButtonMap.changed() ||
|
||||
m_tapAndDrag.changed() ||
|
||||
m_tapDragLock.changed() ||
|
||||
m_leftHanded.changed() ||
|
||||
m_disableEventsOnExternalMouse.changed() ||
|
||||
m_disableWhileTyping.changed() ||
|
||||
m_middleEmulation.changed() ||
|
||||
m_pointerAcceleration.changed() ||
|
||||
m_pointerAccelerationProfileFlat.changed() ||
|
||||
m_pointerAccelerationProfileAdaptive.changed() ||
|
||||
m_naturalScroll.changed() ||
|
||||
m_horizontalScrolling.changed() ||
|
||||
m_isScrollTwoFinger.changed() ||
|
||||
m_isScrollEdge.changed() ||
|
||||
m_isScrollOnButtonDown.changed() ||
|
||||
m_scrollButton.changed() ||
|
||||
m_clickMethodAreas.changed() ||
|
||||
m_clickMethodClickfinger.changed();
|
||||
// clang-format on
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
int LibinputTouchpad::touchpadOff()
|
||||
{
|
||||
return m_enabled.val;
|
||||
}
|
||||
|
||||
XcbAtom &LibinputTouchpad::touchpadOffAtom()
|
||||
{
|
||||
return *m_atoms[QLatin1String(LIBINPUT_PROP_SENDEVENTS_ENABLED)].data();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool LibinputTouchpad::valueLoader(Prop<T> &prop)
|
||||
{
|
||||
const Parameter *p = findParameter(QString::fromLatin1(prop.name));
|
||||
|
||||
if (!p) {
|
||||
// qCCritical(KCM_TOUCHPAD) << "Error on read of " << QString::fromLatin1(prop.name);
|
||||
}
|
||||
|
||||
QVariant reply = getParameter(p);
|
||||
if (!reply.isValid()) {
|
||||
prop.avail = false;
|
||||
return true;
|
||||
}
|
||||
prop.avail = true;
|
||||
|
||||
m_config.beginGroup(m_name);
|
||||
|
||||
const T replyValue = valueLoaderPart<T>(reply);
|
||||
const T loadedValue = m_config.value(prop.name, replyValue).toBool();
|
||||
prop.old = replyValue;
|
||||
prop.val = loadedValue;
|
||||
|
||||
m_config.endGroup();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
QString LibinputTouchpad::valueWriter(const Prop<T> &prop)
|
||||
{
|
||||
const Parameter *p = findParameter(QString::fromLatin1(prop.name));
|
||||
|
||||
// Reion
|
||||
if (!p /*|| !prop.changed()*/) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
bool error = !setParameter(p, prop.val);
|
||||
if (error) {
|
||||
// qCCritical(KCM_TOUCHPAD) << "Cannot set property " + QString::fromLatin1(prop.name);
|
||||
return QStringLiteral("Cannot set property ") + QString::fromLatin1(prop.name);
|
||||
}
|
||||
|
||||
m_config.beginGroup(m_name);
|
||||
m_config.setValue(prop.name, prop.val);
|
||||
m_config.endGroup();
|
||||
m_config.sync();
|
||||
|
||||
return QString();
|
||||
}
|
||||
@ -1,148 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2019 Atul Bisht <atulbisht26@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef LIBINPUTTOUCHPAD_H
|
||||
#define LIBINPUTTOUCHPAD_H
|
||||
|
||||
#include "../libinputcommon.h"
|
||||
#include "xlibtouchpad.h"
|
||||
|
||||
#include <QSettings>
|
||||
|
||||
class LibinputTouchpad : public LibinputCommon, public XlibTouchpad
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
LibinputTouchpad(Display *display, int deviceId);
|
||||
~LibinputTouchpad() override
|
||||
{
|
||||
}
|
||||
|
||||
bool getConfig() override;
|
||||
bool applyConfig() override;
|
||||
bool getDefaultConfig() override;
|
||||
bool isChangedConfig() override;
|
||||
|
||||
int touchpadOff() override;
|
||||
XcbAtom &touchpadOffAtom() override;
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
bool valueLoader(Prop<T> &prop);
|
||||
|
||||
template<typename T>
|
||||
QString valueWriter(const Prop<T> &prop);
|
||||
|
||||
QSettings m_config;
|
||||
|
||||
//
|
||||
// general
|
||||
QString name() const override
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
bool supportsDisableEvents() const override
|
||||
{
|
||||
return m_supportsDisableEvents.avail && m_supportsDisableEvents.val;
|
||||
}
|
||||
bool isEnabled() const override
|
||||
{
|
||||
return !m_enabled.val;
|
||||
}
|
||||
void setEnabled(bool set) override
|
||||
{
|
||||
m_enabled.set(!set);
|
||||
}
|
||||
//
|
||||
// Tapping
|
||||
void setLmrTapButtonMap(bool set) override
|
||||
{
|
||||
m_lrmTapButtonMap.set(!set);
|
||||
m_lmrTapButtonMap.set(set);
|
||||
}
|
||||
//
|
||||
// advanced
|
||||
bool supportsLeftHanded() const override
|
||||
{
|
||||
return m_leftHanded.avail;
|
||||
}
|
||||
bool supportsDisableEventsOnExternalMouse() const override
|
||||
{
|
||||
return m_supportsDisableEventsOnExternalMouse.avail && m_supportsDisableEventsOnExternalMouse.val;
|
||||
}
|
||||
bool supportsDisableWhileTyping() const override
|
||||
{
|
||||
return m_disableWhileTyping.avail;
|
||||
}
|
||||
bool supportsMiddleEmulation() const override
|
||||
{
|
||||
return m_middleEmulation.avail;
|
||||
}
|
||||
//
|
||||
// acceleration speed and profile
|
||||
bool supportsPointerAcceleration() const override
|
||||
{
|
||||
return m_pointerAcceleration.avail;
|
||||
}
|
||||
bool supportsPointerAccelerationProfileFlat() const override
|
||||
{
|
||||
return m_supportsPointerAccelerationProfileFlat.avail && m_supportsPointerAccelerationProfileFlat.val;
|
||||
}
|
||||
bool supportsPointerAccelerationProfileAdaptive() const override
|
||||
{
|
||||
return m_supportsPointerAccelerationProfileAdaptive.avail && m_supportsPointerAccelerationProfileAdaptive.val;
|
||||
}
|
||||
//
|
||||
// scrolling
|
||||
bool supportsNaturalScroll() const override
|
||||
{
|
||||
return m_naturalScroll.avail;
|
||||
}
|
||||
bool supportsHorizontalScrolling() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool supportsScrollTwoFinger() const override
|
||||
{
|
||||
return m_supportsScrollTwoFinger.avail && m_supportsScrollTwoFinger.val;
|
||||
}
|
||||
bool supportsScrollEdge() const override
|
||||
{
|
||||
return m_supportsScrollEdge.avail && m_supportsScrollEdge.val;
|
||||
}
|
||||
bool supportsScrollOnButtonDown() const override
|
||||
{
|
||||
return m_supportsScrollOnButtonDown.avail && m_supportsScrollOnButtonDown.val;
|
||||
}
|
||||
//
|
||||
// click method
|
||||
bool supportsClickMethodAreas() const override
|
||||
{
|
||||
return m_supportsClickMethodAreas.avail && m_supportsClickMethodAreas.val;
|
||||
}
|
||||
bool supportsClickMethodClickfinger() const override
|
||||
{
|
||||
return m_supportsClickMethodClickfinger.avail && m_supportsClickMethodClickfinger.val;
|
||||
}
|
||||
|
||||
bool supportsScrollFactor() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Tapping
|
||||
Prop<bool> m_lrmTapButtonMapEnabledByDefault = Prop<bool>("lrmTapButtonMapEnabledByDefault");
|
||||
Prop<bool> m_lrmTapButtonMap = Prop<bool>("lrmTapButtonMap");
|
||||
//
|
||||
// advanced
|
||||
Prop<bool> m_disableEventsOnExternalMouse = Prop<bool>("disableEventsOnExternalMouse");
|
||||
Prop<bool> m_disableEventsOnExternalMouseDefault = Prop<bool>("disableEventsOnExternalMouseDefault");
|
||||
|
||||
QString m_name;
|
||||
};
|
||||
|
||||
#endif // LIBINPUTTOUCHPAD_H
|
||||
@ -1,86 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Alexander Mezin <mezin.alexander@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "propertyinfo.h"
|
||||
|
||||
#include <QVariant>
|
||||
#include <QDebug>
|
||||
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/extensions/XInput2.h>
|
||||
|
||||
void XDeleter(void *p)
|
||||
{
|
||||
if (p) {
|
||||
XFree(p);
|
||||
}
|
||||
}
|
||||
|
||||
PropertyInfo::PropertyInfo()
|
||||
: type(0)
|
||||
, format(0)
|
||||
, nitems(0)
|
||||
, f(nullptr)
|
||||
, i(nullptr)
|
||||
, b(nullptr)
|
||||
, display(nullptr)
|
||||
, device(0)
|
||||
, prop(0)
|
||||
{
|
||||
}
|
||||
|
||||
PropertyInfo::PropertyInfo(Display *display, int device, Atom prop, Atom floatType)
|
||||
: type(0)
|
||||
, format(0)
|
||||
, nitems(0)
|
||||
, f(nullptr)
|
||||
, i(nullptr)
|
||||
, b(nullptr)
|
||||
, display(display)
|
||||
, device(device)
|
||||
, prop(prop)
|
||||
{
|
||||
unsigned char *dataPtr = nullptr;
|
||||
unsigned long bytes_after;
|
||||
XIGetProperty(display, device, prop, 0, 1000, False, AnyPropertyType, &type, &format, &nitems, &bytes_after, &dataPtr);
|
||||
data = QSharedPointer<unsigned char>(dataPtr, XDeleter);
|
||||
|
||||
if (format == CHAR_BIT && type == XA_INTEGER) {
|
||||
b = reinterpret_cast<char *>(dataPtr);
|
||||
}
|
||||
if (format == sizeof(int) * CHAR_BIT && (type == XA_INTEGER || type == XA_CARDINAL)) {
|
||||
i = reinterpret_cast<int *>(dataPtr);
|
||||
}
|
||||
if (format == sizeof(float) * CHAR_BIT && floatType && type == floatType) {
|
||||
f = reinterpret_cast<float *>(dataPtr);
|
||||
}
|
||||
}
|
||||
|
||||
QVariant PropertyInfo::value(unsigned offset) const
|
||||
{
|
||||
QVariant v;
|
||||
if (offset >= nitems) {
|
||||
return v;
|
||||
}
|
||||
|
||||
if (b) {
|
||||
v = QVariant(static_cast<int>(b[offset]));
|
||||
}
|
||||
if (i) {
|
||||
v = QVariant(i[offset]);
|
||||
}
|
||||
if (f) {
|
||||
v = QVariant(f[offset]);
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
void PropertyInfo::set()
|
||||
{
|
||||
XIChangeProperty(display, device, prop, type, format, XIPropModeReplace, data.data(), nitems);
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Alexander Mezin <mezin.alexander@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef PROPERTYINFO_H
|
||||
#define PROPERTYINFO_H
|
||||
|
||||
#include <QSharedPointer>
|
||||
#include <QtGui/qguiapplication_platform.h>
|
||||
#include <X11/Xdefs.h>
|
||||
|
||||
void XDeleter(void *p);
|
||||
|
||||
struct PropertyInfo {
|
||||
Atom type;
|
||||
int format;
|
||||
QSharedPointer<unsigned char> data;
|
||||
unsigned long nitems;
|
||||
|
||||
float *f;
|
||||
int *i;
|
||||
char *b;
|
||||
|
||||
Display *display;
|
||||
int device;
|
||||
Atom prop;
|
||||
|
||||
PropertyInfo();
|
||||
PropertyInfo(Display *display, int device, Atom prop, Atom floatType);
|
||||
QVariant value(unsigned offset) const;
|
||||
|
||||
void set();
|
||||
};
|
||||
|
||||
#endif // PROPERTYINFO_H
|
||||
@ -1,225 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Alexander Mezin <mezin.alexander@gmail.com>
|
||||
SPDX-FileContributor: 2002-2005, 2007 Peter Osterlund <petero2@telia.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later AND LicenseRef-synaptics
|
||||
*/
|
||||
|
||||
#include <QDebug>
|
||||
#include <cmath>
|
||||
|
||||
#include "synapticstouchpad.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <synaptics-properties.h>
|
||||
|
||||
#define SYN_MAX_BUTTONS 12
|
||||
|
||||
const struct Parameter synapticsProperties[] = {
|
||||
{"LeftEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_EDGES, 32, 0},
|
||||
{"RightEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_EDGES, 32, 1},
|
||||
{"TopEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_EDGES, 32, 2},
|
||||
{"BottomEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_EDGES, 32, 3},
|
||||
{"FingerLow", PT_INT, 0, 255, SYNAPTICS_PROP_FINGER, 32, 0},
|
||||
{"FingerHigh", PT_INT, 0, 255, SYNAPTICS_PROP_FINGER, 32, 1},
|
||||
{"MaxTapTime", PT_INT, 0, 1000, SYNAPTICS_PROP_TAP_TIME, 32, 0},
|
||||
{"MaxTapMove", PT_INT, 0, 2000, SYNAPTICS_PROP_TAP_MOVE, 32, 0},
|
||||
{"MaxDoubleTapTime", PT_INT, 0, 1000, SYNAPTICS_PROP_TAP_DURATIONS, 32, 1},
|
||||
{"SingleTapTimeout", PT_INT, 0, 1000, SYNAPTICS_PROP_TAP_DURATIONS, 32, 0},
|
||||
{"ClickTime", PT_INT, 0, 1000, SYNAPTICS_PROP_TAP_DURATIONS, 32, 2},
|
||||
{"FastTaps", PT_BOOL, 0, 1, SYNAPTICS_PROP_TAP_FAST, 8, 0},
|
||||
{"EmulateMidButtonTime", PT_INT, 0, 1000, SYNAPTICS_PROP_MIDDLE_TIMEOUT, 32, 0},
|
||||
{"EmulateTwoFingerMinZ", PT_INT, 0, 1000, SYNAPTICS_PROP_TWOFINGER_PRESSURE, 32, 0},
|
||||
{"EmulateTwoFingerMinW", PT_INT, 0, 15, SYNAPTICS_PROP_TWOFINGER_WIDTH, 32, 0},
|
||||
{"VertScrollDelta", PT_INT, -1000, 1000, SYNAPTICS_PROP_SCROLL_DISTANCE, 32, 0},
|
||||
{"HorizScrollDelta", PT_INT, -1000, 1000, SYNAPTICS_PROP_SCROLL_DISTANCE, 32, 1},
|
||||
{"VertEdgeScroll", PT_BOOL, 0, 1, SYNAPTICS_PROP_SCROLL_EDGE, 8, 0},
|
||||
{"HorizEdgeScroll", PT_BOOL, 0, 1, SYNAPTICS_PROP_SCROLL_EDGE, 8, 1},
|
||||
{"CornerCoasting", PT_BOOL, 0, 1, SYNAPTICS_PROP_SCROLL_EDGE, 8, 2},
|
||||
{"VertTwoFingerScroll", PT_BOOL, 0, 1, SYNAPTICS_PROP_SCROLL_TWOFINGER, 8, 0},
|
||||
{"HorizTwoFingerScroll", PT_BOOL, 0, 1, SYNAPTICS_PROP_SCROLL_TWOFINGER, 8, 1},
|
||||
{"MinSpeed", PT_DOUBLE, 0, 255.0, SYNAPTICS_PROP_SPEED, 0, /*float */ 0},
|
||||
{"MaxSpeed", PT_DOUBLE, 0, 255.0, SYNAPTICS_PROP_SPEED, 0, /*float */ 1},
|
||||
{"AccelFactor", PT_DOUBLE, 0, 1.0, SYNAPTICS_PROP_SPEED, 0, /*float */ 2},
|
||||
/*{"TouchpadOff", PT_INT, 0, 2, SYNAPTICS_PROP_OFF, 8, 0},*/
|
||||
{"LockedDrags", PT_BOOL, 0, 1, SYNAPTICS_PROP_LOCKED_DRAGS, 8, 0},
|
||||
{"LockedDragTimeout", PT_INT, 0, 30000, SYNAPTICS_PROP_LOCKED_DRAGS_TIMEOUT, 32, 0},
|
||||
{"RTCornerButton", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_TAP_ACTION, 8, 0},
|
||||
{"RBCornerButton", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_TAP_ACTION, 8, 1},
|
||||
{"LTCornerButton", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_TAP_ACTION, 8, 2},
|
||||
{"LBCornerButton", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_TAP_ACTION, 8, 3},
|
||||
{"OneFingerTapButton", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_TAP_ACTION, 8, 4},
|
||||
{"TwoFingerTapButton", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_TAP_ACTION, 8, 5},
|
||||
{"ThreeFingerTapButton", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_TAP_ACTION, 8, 6},
|
||||
{"ClickFinger1", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_CLICK_ACTION, 8, 0},
|
||||
{"ClickFinger2", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_CLICK_ACTION, 8, 1},
|
||||
{"ClickFinger3", PT_INT, 0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_CLICK_ACTION, 8, 2},
|
||||
{"CircularScrolling", PT_BOOL, 0, 1, SYNAPTICS_PROP_CIRCULAR_SCROLLING, 8, 0},
|
||||
{"CircScrollDelta", PT_DOUBLE, .01, 3, SYNAPTICS_PROP_CIRCULAR_SCROLLING_DIST, 0 /* float */, 0},
|
||||
{"CircScrollTrigger", PT_INT, 0, 8, SYNAPTICS_PROP_CIRCULAR_SCROLLING_TRIGGER, 8, 0},
|
||||
{"PalmDetect", PT_BOOL, 0, 1, SYNAPTICS_PROP_PALM_DETECT, 8, 0},
|
||||
{"PalmMinWidth", PT_INT, 0, 15, SYNAPTICS_PROP_PALM_DIMENSIONS, 32, 0},
|
||||
{"PalmMinZ", PT_INT, 0, 255, SYNAPTICS_PROP_PALM_DIMENSIONS, 32, 1},
|
||||
{"CoastingSpeed", PT_DOUBLE, 0, 255, SYNAPTICS_PROP_COASTING_SPEED, 0 /* float*/, 0},
|
||||
{"CoastingFriction", PT_DOUBLE, 0, 255, SYNAPTICS_PROP_COASTING_SPEED, 0 /* float*/, 1},
|
||||
{"PressureMotionMinZ", PT_INT, 1, 255, SYNAPTICS_PROP_PRESSURE_MOTION, 32, 0},
|
||||
{"PressureMotionMaxZ", PT_INT, 1, 255, SYNAPTICS_PROP_PRESSURE_MOTION, 32, 1},
|
||||
{"PressureMotionMinFactor", PT_DOUBLE, 0, 10.0, SYNAPTICS_PROP_PRESSURE_MOTION_FACTOR, 0 /*float*/, 0},
|
||||
{"PressureMotionMaxFactor", PT_DOUBLE, 0, 10.0, SYNAPTICS_PROP_PRESSURE_MOTION_FACTOR, 0 /*float*/, 1},
|
||||
{"GrabEventDevice", PT_BOOL, 0, 1, SYNAPTICS_PROP_GRAB, 8, 0},
|
||||
{"TapAndDragGesture", PT_BOOL, 0, 1, SYNAPTICS_PROP_GESTURES, 8, 0},
|
||||
{"AreaLeftEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_AREA, 32, 0},
|
||||
{"AreaRightEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_AREA, 32, 1},
|
||||
{"AreaTopEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_AREA, 32, 2},
|
||||
{"AreaBottomEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_AREA, 32, 3},
|
||||
{"HorizHysteresis", PT_INT, 0, 10000, SYNAPTICS_PROP_NOISE_CANCELLATION, 32, 0},
|
||||
{"VertHysteresis", PT_INT, 0, 10000, SYNAPTICS_PROP_NOISE_CANCELLATION, 32, 1},
|
||||
{"ClickPad", PT_BOOL, 0, 1, SYNAPTICS_PROP_CLICKPAD, 8, 0},
|
||||
{"RightButtonAreaLeft", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 0},
|
||||
{"RightButtonAreaRight", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 1},
|
||||
{"RightButtonAreaTop", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 2},
|
||||
{"RightButtonAreaBottom", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 3},
|
||||
{"MiddleButtonAreaLeft", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 4},
|
||||
{"MiddleButtonAreaRight", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 5},
|
||||
{"MiddleButtonAreaTop", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 6},
|
||||
{"MiddleButtonAreaBottom", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 7},
|
||||
{NULL, PT_INT, 0, 0, nullptr, 0, 0},
|
||||
};
|
||||
|
||||
SynapticsTouchpad::SynapticsTouchpad(Display *display, int deviceId)
|
||||
: XlibTouchpad(display, deviceId)
|
||||
, m_resX(1)
|
||||
, m_resY(1)
|
||||
{
|
||||
m_capsAtom.intern(m_connection, SYNAPTICS_PROP_CAPABILITIES);
|
||||
m_touchpadOffAtom.intern(m_connection, SYNAPTICS_PROP_OFF);
|
||||
XcbAtom resolutionAtom(m_connection, SYNAPTICS_PROP_RESOLUTION);
|
||||
XcbAtom edgesAtom(m_connection, SYNAPTICS_PROP_EDGES);
|
||||
|
||||
loadSupportedProperties(synapticsProperties);
|
||||
|
||||
m_toRadians.append("CircScrollDelta");
|
||||
|
||||
PropertyInfo edges(m_display, m_deviceId, edgesAtom, 0);
|
||||
if (edges.i && edges.nitems == 4) {
|
||||
int w = qAbs(edges.i[1] - edges.i[0]);
|
||||
int h = qAbs(edges.i[3] - edges.i[2]);
|
||||
m_resX = w / 90;
|
||||
m_resY = h / 50;
|
||||
qDebug() << "Width: " << w << " height: " << h;
|
||||
qDebug() << "Approx. resX: " << m_resX << " resY: " << m_resY;
|
||||
}
|
||||
|
||||
PropertyInfo resolution(m_display, m_deviceId, resolutionAtom, 0);
|
||||
if (resolution.i && resolution.nitems == 2 && resolution.i[0] > 1 && resolution.i[1] > 1) {
|
||||
m_resY = qMin(static_cast<unsigned long>(resolution.i[0]), static_cast<unsigned long>(INT_MAX));
|
||||
m_resX = qMin(static_cast<unsigned long>(resolution.i[1]), static_cast<unsigned long>(INT_MAX));
|
||||
qDebug() << "Touchpad resolution: x: " << m_resX << " y: " << m_resY;
|
||||
}
|
||||
|
||||
m_scaleByResX.append("HorizScrollDelta");
|
||||
m_scaleByResY.append("VertScrollDelta");
|
||||
m_scaleByResX.append("MaxTapMove");
|
||||
m_scaleByResY.append("MaxTapMove");
|
||||
|
||||
m_resX = qMax(10, m_resX);
|
||||
m_resY = qMax(10, m_resY);
|
||||
qDebug() << "Final resolution x:" << m_resX << " y:" << m_resY;
|
||||
m_negate["HorizScrollDelta"] = "InvertHorizScroll";
|
||||
m_negate["VertScrollDelta"] = "InvertVertScroll";
|
||||
m_supported.append(m_negate.values());
|
||||
m_supported.append("Coasting");
|
||||
|
||||
PropertyInfo caps(m_display, m_deviceId, m_capsAtom.atom(), 0);
|
||||
if (!caps.b) {
|
||||
return;
|
||||
}
|
||||
|
||||
enum TouchpadCapabilitiy {
|
||||
TouchpadHasLeftButton,
|
||||
TouchpadHasMiddleButton,
|
||||
TouchpadHasRightButton,
|
||||
TouchpadTwoFingerDetect,
|
||||
TouchpadThreeFingerDetect,
|
||||
TouchpadPressureDetect,
|
||||
TouchpadPalmDetect,
|
||||
TouchpadCapsCount,
|
||||
};
|
||||
|
||||
QVector<bool> cap(TouchpadCapsCount, false);
|
||||
std::copy(caps.b, caps.b + qMin(cap.size(), static_cast<int>(caps.nitems)), cap.begin());
|
||||
|
||||
if (!cap[TouchpadTwoFingerDetect]) {
|
||||
m_supported.removeAll("HorizTwoFingerScroll");
|
||||
m_supported.removeAll("VertTwoFingerScroll");
|
||||
m_supported.removeAll("TwoFingerTapButton");
|
||||
}
|
||||
|
||||
if (!cap[TouchpadThreeFingerDetect]) {
|
||||
m_supported.removeAll("ThreeFingerTapButton");
|
||||
}
|
||||
|
||||
if (!cap[TouchpadPressureDetect]) {
|
||||
m_supported.removeAll("FingerHigh");
|
||||
m_supported.removeAll("FingerLow");
|
||||
|
||||
m_supported.removeAll("PalmMinZ");
|
||||
m_supported.removeAll("PressureMotionMinZ");
|
||||
m_supported.removeAll("PressureMotionMinFactor");
|
||||
m_supported.removeAll("PressureMotionMaxZ");
|
||||
m_supported.removeAll("PressureMotionMaxFactor");
|
||||
m_supported.removeAll("EmulateTwoFingerMinZ");
|
||||
}
|
||||
|
||||
if (!cap[TouchpadPalmDetect]) {
|
||||
m_supported.removeAll("PalmDetect");
|
||||
m_supported.removeAll("PalmMinWidth");
|
||||
m_supported.removeAll("PalmMinZ");
|
||||
m_supported.removeAll("EmulateTwoFingerMinW");
|
||||
}
|
||||
|
||||
for (QMap<QString, QString>::Iterator i = m_negate.begin(); i != m_negate.end(); ++i) {
|
||||
if (!m_supported.contains(i.key())) {
|
||||
m_supported.removeAll(i.value());
|
||||
}
|
||||
}
|
||||
|
||||
m_paramList = synapticsProperties;
|
||||
}
|
||||
|
||||
void SynapticsTouchpad::setTouchpadOff(int touchpadOff)
|
||||
{
|
||||
PropertyInfo off(m_display, m_deviceId, m_touchpadOffAtom.atom(), 0);
|
||||
if (off.b && *(off.b) != touchpadOff) {
|
||||
*(off.b) = touchpadOff;
|
||||
off.set();
|
||||
}
|
||||
|
||||
flush();
|
||||
}
|
||||
|
||||
int SynapticsTouchpad::touchpadOff()
|
||||
{
|
||||
PropertyInfo off(m_display, m_deviceId, m_touchpadOffAtom.atom(), 0);
|
||||
return off.value(0).toInt();
|
||||
}
|
||||
|
||||
XcbAtom &SynapticsTouchpad::touchpadOffAtom()
|
||||
{
|
||||
return m_touchpadOffAtom;
|
||||
}
|
||||
|
||||
double SynapticsTouchpad::getPropertyScale(const QString &name) const
|
||||
{
|
||||
if (m_scaleByResX.contains(name) && m_scaleByResY.contains(name)) {
|
||||
return std::sqrt(static_cast<double>(m_resX) * m_resX + static_cast<double>(m_resY) * m_resY);
|
||||
} else if (m_scaleByResX.contains(name)) {
|
||||
return m_resX;
|
||||
} else if (m_scaleByResY.contains(name)) {
|
||||
return m_resY;
|
||||
} else if (m_toRadians.contains(name)) {
|
||||
return M_PI_4 / 45.0;
|
||||
}
|
||||
return 1.0;
|
||||
}
|
||||
@ -1,34 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Weng Xuetian <wengxt@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef SYNAPTICSTOUCHPAD_H
|
||||
#define SYNAPTICSTOUCHPAD_H
|
||||
|
||||
#include "xcbatom.h"
|
||||
#include "xlibtouchpad.h"
|
||||
|
||||
class SynapticsTouchpad : public QObject, public XlibTouchpad
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SynapticsTouchpad(Display *display, int deviceId);
|
||||
|
||||
void setTouchpadOff(int touchpadOff) override;
|
||||
int touchpadOff() override;
|
||||
|
||||
XcbAtom &touchpadOffAtom() override;
|
||||
|
||||
protected:
|
||||
double getPropertyScale(const QString &name) const override;
|
||||
|
||||
private:
|
||||
XcbAtom m_capsAtom, m_touchpadOffAtom;
|
||||
int m_resX, m_resY;
|
||||
QStringList m_scaleByResX, m_scaleByResY, m_toRadians;
|
||||
};
|
||||
|
||||
#endif // SYNAPTICSTOUCHPAD_H
|
||||
@ -1,48 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Alexander Mezin <mezin.alexander@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "xcbatom.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
XcbAtom::XcbAtom()
|
||||
: m_connection(nullptr)
|
||||
, m_reply(nullptr)
|
||||
, m_fetched(false)
|
||||
{
|
||||
}
|
||||
|
||||
XcbAtom::XcbAtom(xcb_connection_t *c, const char *name, bool onlyIfExists)
|
||||
: m_reply(nullptr)
|
||||
, m_fetched(false)
|
||||
{
|
||||
intern(c, name, onlyIfExists);
|
||||
}
|
||||
|
||||
void XcbAtom::intern(xcb_connection_t *c, const char *name, bool onlyIfExists)
|
||||
{
|
||||
m_connection = c;
|
||||
m_cookie = xcb_intern_atom(c, onlyIfExists, std::strlen(name), name);
|
||||
}
|
||||
|
||||
XcbAtom::~XcbAtom()
|
||||
{
|
||||
std::free(m_reply);
|
||||
}
|
||||
|
||||
xcb_atom_t XcbAtom::atom()
|
||||
{
|
||||
if (!m_fetched) {
|
||||
m_fetched = true;
|
||||
m_reply = xcb_intern_atom_reply(m_connection, m_cookie, nullptr);
|
||||
}
|
||||
if (m_reply) {
|
||||
return m_reply->atom;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Alexander Mezin <mezin.alexander@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef XCBATOM_H
|
||||
#define XCBATOM_H
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
class XcbAtom
|
||||
{
|
||||
public:
|
||||
XcbAtom();
|
||||
XcbAtom(xcb_connection_t *, const char *name, bool onlyIfExists = true);
|
||||
~XcbAtom();
|
||||
|
||||
void intern(xcb_connection_t *, const char *name, bool onlyIfExists = true);
|
||||
xcb_atom_t atom();
|
||||
operator xcb_atom_t()
|
||||
{
|
||||
return atom();
|
||||
}
|
||||
|
||||
private:
|
||||
XcbAtom(const XcbAtom &);
|
||||
XcbAtom &operator=(const XcbAtom &);
|
||||
|
||||
xcb_connection_t *m_connection;
|
||||
xcb_intern_atom_cookie_t m_cookie;
|
||||
xcb_intern_atom_reply_t *m_reply;
|
||||
bool m_fetched;
|
||||
};
|
||||
|
||||
#endif // XCBATOM_H
|
||||
@ -1,425 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Alexander Mezin <mezin.alexander@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
#include <QtAlgorithms>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
// Includes are ordered this way because of #defines in Xorg's headers
|
||||
#include "xlibbackend.h" // krazy:exclude=includes
|
||||
#include "xlibnotifications.h" // krazy:exclude=includes
|
||||
#include "xrecordkeyboardmonitor.h" // krazy:exclude=includes
|
||||
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xlib-xcb.h>
|
||||
#include <X11/extensions/XInput.h>
|
||||
#include <X11/extensions/XInput2.h>
|
||||
|
||||
#include <synaptics-properties.h>
|
||||
#include <xserver-properties.h>
|
||||
|
||||
struct DeviceListDeleter {
|
||||
static void cleanup(XDeviceInfo *p)
|
||||
{
|
||||
if (p) {
|
||||
XFreeDeviceList(p);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void XlibBackend::XDisplayCleanup::cleanup(Display *p)
|
||||
{
|
||||
if (p) {
|
||||
XCloseDisplay(p);
|
||||
}
|
||||
}
|
||||
|
||||
XlibBackend *XlibBackend::initialize(QObject *parent)
|
||||
{
|
||||
XlibBackend *backend = new XlibBackend(parent);
|
||||
if (!backend->m_display) {
|
||||
delete backend;
|
||||
return nullptr;
|
||||
}
|
||||
return backend;
|
||||
}
|
||||
|
||||
XlibBackend::~XlibBackend()
|
||||
{
|
||||
}
|
||||
|
||||
XlibBackend::XlibBackend(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_display(XOpenDisplay(nullptr))
|
||||
, m_connection(nullptr)
|
||||
{
|
||||
if (m_display) {
|
||||
m_connection = XGetXCBConnection(m_display.data());
|
||||
}
|
||||
|
||||
if (!m_connection) {
|
||||
m_errorString = "Cannot connect to X server";
|
||||
return;
|
||||
}
|
||||
|
||||
m_mouseAtom.intern(m_connection, XI_MOUSE);
|
||||
m_keyboardAtom.intern(m_connection, XI_KEYBOARD);
|
||||
m_touchpadAtom.intern(m_connection, XI_TOUCHPAD);
|
||||
m_enabledAtom.intern(m_connection, XI_PROP_ENABLED);
|
||||
|
||||
m_synapticsIdentifierAtom.intern(m_connection, SYNAPTICS_PROP_CAPABILITIES);
|
||||
m_libinputIdentifierAtom.intern(m_connection, "libinput Send Events Modes Available");
|
||||
|
||||
m_device.reset(findTouchpad());
|
||||
if (!m_device) {
|
||||
m_errorString = "No touchpad found";
|
||||
}
|
||||
}
|
||||
|
||||
XlibTouchpad *XlibBackend::findTouchpad()
|
||||
{
|
||||
int nDevices = 0;
|
||||
QScopedPointer<XDeviceInfo, DeviceListDeleter> deviceInfo(XListInputDevices(m_display.data(), &nDevices));
|
||||
|
||||
for (XDeviceInfo *info = deviceInfo.data(); info < deviceInfo.data() + nDevices; info++) {
|
||||
// Make sure device is touchpad
|
||||
if (info->type != m_touchpadAtom.atom()) {
|
||||
continue;
|
||||
}
|
||||
int nProperties = 0;
|
||||
QSharedPointer<Atom> properties(XIListProperties(m_display.data(), info->id, &nProperties), XDeleter);
|
||||
|
||||
Atom *atom = properties.data(), *atomEnd = properties.data() + nProperties;
|
||||
for (; atom != atomEnd; atom++) {
|
||||
|
||||
if (*atom == m_libinputIdentifierAtom.atom()) {
|
||||
// setMode(TouchpadInputBackendMode::XLibinput);
|
||||
return new LibinputTouchpad(m_display.data(), info->id);
|
||||
}
|
||||
|
||||
if (*atom == m_synapticsIdentifierAtom.atom()) {
|
||||
// setMode(TouchpadInputBackendMode::XSynaptics);
|
||||
return new SynapticsTouchpad(m_display.data(), info->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool XlibBackend::applyConfig(const QVariantHash &p)
|
||||
{
|
||||
if (!m_device) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = m_device->applyConfig(p);
|
||||
if (!success) {
|
||||
m_errorString = "Cannot apply touchpad configuration";
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool XlibBackend::applyConfig()
|
||||
{
|
||||
if (!m_device) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = m_device->applyConfig();
|
||||
if (!success) {
|
||||
m_errorString = "Cannot apply touchpad configuration";
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool XlibBackend::getConfig(QVariantHash &p)
|
||||
{
|
||||
if (!m_device) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = m_device->getConfig(p);
|
||||
if (!success) {
|
||||
m_errorString = "Cannot read touchpad configuration";
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool XlibBackend::getConfig()
|
||||
{
|
||||
if (!m_device) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = m_device->getConfig();
|
||||
if (!success) {
|
||||
m_errorString = "Cannot read touchpad configuration";
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool XlibBackend::getDefaultConfig()
|
||||
{
|
||||
if (!m_device) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = m_device->getDefaultConfig();
|
||||
if (!success) {
|
||||
m_errorString = "Cannot read default touchpad configuration";
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool XlibBackend::isChangedConfig() const
|
||||
{
|
||||
if (!m_device) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_device->isChangedConfig();
|
||||
}
|
||||
|
||||
void XlibBackend::setTouchpadEnabled(bool enable)
|
||||
{
|
||||
if (!m_device) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_device->setEnabled(enable);
|
||||
|
||||
// FIXME? This should not be needed, m_notifications should trigger
|
||||
// a propertyChanged signal when we enable/disable the touchpad,
|
||||
// that will Q_EMIT touchpadStateChanged, but for some reason
|
||||
// XlibNotifications is not getting the property change events
|
||||
// so we just Q_EMIT touchpadStateChanged from here as a workaround
|
||||
Q_EMIT touchpadStateChanged();
|
||||
}
|
||||
|
||||
bool XlibBackend::tapToClick()
|
||||
{
|
||||
LibinputTouchpad *object = dynamic_cast<LibinputTouchpad *>(m_device.data());
|
||||
|
||||
if (!object)
|
||||
return false;
|
||||
|
||||
return object->isTapToClick();
|
||||
}
|
||||
|
||||
void XlibBackend::setTapToClick(bool enabled)
|
||||
{
|
||||
LibinputTouchpad *object = dynamic_cast<LibinputTouchpad *>(m_device.data());
|
||||
|
||||
if (!object)
|
||||
return;
|
||||
|
||||
object->setTapToClick(enabled);
|
||||
}
|
||||
|
||||
bool XlibBackend::naturalScroll()
|
||||
{
|
||||
LibinputTouchpad *object = dynamic_cast<LibinputTouchpad *>(m_device.data());
|
||||
|
||||
if (!object)
|
||||
return false;
|
||||
|
||||
return object->isNaturalScroll();
|
||||
}
|
||||
|
||||
void XlibBackend::setNaturalScroll(bool value)
|
||||
{
|
||||
LibinputTouchpad *object = dynamic_cast<LibinputTouchpad *>(m_device.data());
|
||||
|
||||
if (!object)
|
||||
return;
|
||||
|
||||
object->setNaturalScroll(value);
|
||||
}
|
||||
|
||||
qreal XlibBackend::pointerAcceleration()
|
||||
{
|
||||
LibinputTouchpad *object = dynamic_cast<LibinputTouchpad *>(m_device.data());
|
||||
|
||||
if (!object)
|
||||
return 1;
|
||||
|
||||
return object->pointerAcceleration();
|
||||
}
|
||||
|
||||
void XlibBackend::setPointerAcceleration(qreal value)
|
||||
{
|
||||
LibinputTouchpad *object = dynamic_cast<LibinputTouchpad *>(m_device.data());
|
||||
|
||||
if (!object)
|
||||
return;
|
||||
|
||||
object->setPointerAcceleration(value);
|
||||
}
|
||||
|
||||
void XlibBackend::setTouchpadOff(XlibBackend::TouchpadOffState state)
|
||||
{
|
||||
if (!m_device) {
|
||||
return;
|
||||
}
|
||||
|
||||
int touchpadOff = 0;
|
||||
switch (state) {
|
||||
case TouchpadEnabled:
|
||||
touchpadOff = 0;
|
||||
break;
|
||||
case TouchpadFullyDisabled:
|
||||
touchpadOff = 1;
|
||||
break;
|
||||
case TouchpadTapAndScrollDisabled:
|
||||
touchpadOff = 2;
|
||||
break;
|
||||
default:
|
||||
qCritical() << "Unknown TouchpadOffState" << state;
|
||||
return;
|
||||
}
|
||||
|
||||
m_device->setTouchpadOff(touchpadOff);
|
||||
}
|
||||
|
||||
bool XlibBackend::isTouchpadAvailable()
|
||||
{
|
||||
return !m_device.isNull();
|
||||
}
|
||||
|
||||
bool XlibBackend::isTouchpadEnabled()
|
||||
{
|
||||
if (!m_device) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_device->enabled();
|
||||
}
|
||||
|
||||
XlibBackend::TouchpadOffState XlibBackend::getTouchpadOff()
|
||||
{
|
||||
if (!m_device) {
|
||||
return TouchpadFullyDisabled;
|
||||
}
|
||||
int value = m_device->touchpadOff();
|
||||
switch (value) {
|
||||
case 0:
|
||||
return TouchpadEnabled;
|
||||
case 1:
|
||||
return TouchpadFullyDisabled;
|
||||
case 2:
|
||||
return TouchpadTapAndScrollDisabled;
|
||||
default:
|
||||
qCritical() << "Unknown TouchpadOff value" << value;
|
||||
return TouchpadFullyDisabled;
|
||||
}
|
||||
}
|
||||
|
||||
void XlibBackend::touchpadDetached()
|
||||
{
|
||||
qWarning() << "Touchpad detached";
|
||||
m_device.reset();
|
||||
Q_EMIT touchpadReset();
|
||||
}
|
||||
|
||||
void XlibBackend::devicePlugged(int device)
|
||||
{
|
||||
if (!m_device) {
|
||||
m_device.reset(findTouchpad());
|
||||
if (m_device) {
|
||||
qWarning() << "Touchpad reset";
|
||||
m_notifications.reset();
|
||||
watchForEvents(!m_keyboard.isNull());
|
||||
Q_EMIT touchpadReset();
|
||||
}
|
||||
}
|
||||
if (!m_device || device != m_device->deviceId()) {
|
||||
Q_EMIT mousesChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void XlibBackend::propertyChanged(xcb_atom_t prop)
|
||||
{
|
||||
if ((m_device && prop == m_device->touchpadOffAtom().atom()) || prop == m_enabledAtom.atom()) {
|
||||
Q_EMIT touchpadStateChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QStringList XlibBackend::listMouses(const QStringList &blacklist)
|
||||
{
|
||||
int nDevices = 0;
|
||||
QScopedPointer<XDeviceInfo, DeviceListDeleter> info(XListInputDevices(m_display.data(), &nDevices));
|
||||
QStringList list;
|
||||
for (XDeviceInfo *i = info.data(); i != info.data() + nDevices; i++) {
|
||||
if (m_device && i->id == static_cast<XID>(m_device->deviceId())) {
|
||||
continue;
|
||||
}
|
||||
if (i->use != IsXExtensionPointer && i->use != IsXPointer) {
|
||||
continue;
|
||||
}
|
||||
// type = KEYBOARD && use = Pointer means usb receiver for both keyboard
|
||||
// and mouse
|
||||
if (i->type != m_mouseAtom.atom() && i->type != m_keyboardAtom.atom()) {
|
||||
continue;
|
||||
}
|
||||
QString name(i->name);
|
||||
if (blacklist.contains(name, Qt::CaseInsensitive)) {
|
||||
continue;
|
||||
}
|
||||
PropertyInfo enabled(m_display.data(), i->id, m_enabledAtom.atom(), 0);
|
||||
if (enabled.value(0) == false) {
|
||||
continue;
|
||||
}
|
||||
list.append(name);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
QVector<QObject *> XlibBackend::getDevices() const
|
||||
{
|
||||
QVector<QObject *> touchpads;
|
||||
|
||||
LibinputTouchpad *libinputtouchpad = dynamic_cast<LibinputTouchpad *>(m_device.data());
|
||||
if (libinputtouchpad) {
|
||||
touchpads.push_back(libinputtouchpad);
|
||||
}
|
||||
|
||||
SynapticsTouchpad *synaptics = dynamic_cast<SynapticsTouchpad *>(m_device.data());
|
||||
if (synaptics) {
|
||||
touchpads.push_back(synaptics);
|
||||
}
|
||||
|
||||
return touchpads;
|
||||
}
|
||||
|
||||
void XlibBackend::watchForEvents(bool keyboard)
|
||||
{
|
||||
if (!m_notifications) {
|
||||
m_notifications.reset(new XlibNotifications(m_display.data(), m_device ? m_device->deviceId() : XIAllDevices));
|
||||
connect(m_notifications.data(), SIGNAL(devicePlugged(int)), SLOT(devicePlugged(int)));
|
||||
connect(m_notifications.data(), SIGNAL(touchpadDetached()), SLOT(touchpadDetached()));
|
||||
connect(m_notifications.data(), SIGNAL(propertyChanged(xcb_atom_t)), SLOT(propertyChanged(xcb_atom_t)));
|
||||
}
|
||||
|
||||
if (keyboard == !m_keyboard.isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!keyboard) {
|
||||
m_keyboard.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
m_keyboard.reset(new XRecordKeyboardMonitor(m_display.data()));
|
||||
connect(m_keyboard.data(), SIGNAL(keyboardActivityStarted()), SIGNAL(keyboardActivityStarted()));
|
||||
connect(m_keyboard.data(), SIGNAL(keyboardActivityFinished()), SIGNAL(keyboardActivityFinished()));
|
||||
}
|
||||
@ -1,123 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Alexander Mezin <mezin.alexander@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef XLIBBACKEND_H
|
||||
#define XLIBBACKEND_H
|
||||
|
||||
#include <QLatin1String>
|
||||
#include <QMap>
|
||||
#include <QScopedPointer>
|
||||
#include <QSet>
|
||||
#include <QSharedPointer>
|
||||
#include <QStringList>
|
||||
|
||||
#include "libinputtouchpad.h"
|
||||
#include "synapticstouchpad.h"
|
||||
#include "xlibtouchpad.h"
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
#include "propertyinfo.h"
|
||||
#include "xcbatom.h"
|
||||
|
||||
class XlibTouchpad;
|
||||
class XlibNotifications;
|
||||
class XRecordKeyboardMonitor;
|
||||
|
||||
class XlibBackend : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(int touchpadCount READ touchpadCount CONSTANT)
|
||||
|
||||
public:
|
||||
enum TouchpadOffState {
|
||||
TouchpadEnabled,
|
||||
TouchpadTapAndScrollDisabled,
|
||||
TouchpadFullyDisabled,
|
||||
};
|
||||
|
||||
static XlibBackend *initialize(QObject *parent = nullptr);
|
||||
~XlibBackend();
|
||||
|
||||
bool applyConfig(const QVariantHash &);
|
||||
bool applyConfig();
|
||||
bool getConfig(QVariantHash &);
|
||||
bool getConfig();
|
||||
bool getDefaultConfig();
|
||||
bool isChangedConfig() const;
|
||||
QStringList supportedParameters() const
|
||||
{
|
||||
return m_device ? m_device->supportedParameters() : QStringList();
|
||||
}
|
||||
QString errorString() const
|
||||
{
|
||||
return m_errorString;
|
||||
}
|
||||
int touchpadCount() const
|
||||
{
|
||||
return m_device ? 1 : 0;
|
||||
}
|
||||
|
||||
void setTouchpadOff(TouchpadOffState);
|
||||
TouchpadOffState getTouchpadOff();
|
||||
|
||||
bool isTouchpadAvailable();
|
||||
bool isTouchpadEnabled();
|
||||
void setTouchpadEnabled(bool);
|
||||
|
||||
bool tapToClick();
|
||||
void setTapToClick(bool enabled);
|
||||
|
||||
bool naturalScroll();
|
||||
void setNaturalScroll(bool value);
|
||||
|
||||
qreal pointerAcceleration();
|
||||
void setPointerAcceleration(qreal value);
|
||||
|
||||
void watchForEvents(bool keyboard);
|
||||
|
||||
QStringList listMouses(const QStringList &blacklist);
|
||||
QVector<QObject *> getDevices() const;
|
||||
|
||||
signals:
|
||||
void touchpadStateChanged();
|
||||
void mousesChanged();
|
||||
void touchpadReset();
|
||||
void keyboardActivityStarted();
|
||||
void keyboardActivityFinished();
|
||||
|
||||
void touchpadAdded(bool success);
|
||||
void touchpadRemoved(int index);
|
||||
|
||||
private Q_SLOTS:
|
||||
void propertyChanged(xcb_atom_t);
|
||||
void touchpadDetached();
|
||||
void devicePlugged(int);
|
||||
|
||||
protected:
|
||||
explicit XlibBackend(QObject *parent);
|
||||
|
||||
struct XDisplayCleanup {
|
||||
static void cleanup(Display *);
|
||||
};
|
||||
|
||||
QScopedPointer<Display, XDisplayCleanup> m_display;
|
||||
xcb_connection_t *m_connection;
|
||||
|
||||
XcbAtom m_enabledAtom, m_mouseAtom, m_keyboardAtom, m_touchpadAtom;
|
||||
XcbAtom m_synapticsIdentifierAtom;
|
||||
XcbAtom m_libinputIdentifierAtom;
|
||||
|
||||
XlibTouchpad *findTouchpad();
|
||||
QScopedPointer<XlibTouchpad> m_device;
|
||||
|
||||
QString m_errorString;
|
||||
QScopedPointer<XlibNotifications> m_notifications;
|
||||
QScopedPointer<XRecordKeyboardMonitor> m_keyboard;
|
||||
};
|
||||
|
||||
#endif // XLIBBACKEND_H
|
||||
@ -1,135 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Alexander Mezin <mezin.alexander@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "xlibnotifications.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <X11/Xlib-xcb.h>
|
||||
#include <X11/extensions/XI.h>
|
||||
#include <X11/extensions/XI2proto.h>
|
||||
#include <X11/extensions/XInput2.h>
|
||||
|
||||
XlibNotifications::XlibNotifications(Display *display, int device)
|
||||
: m_display(display)
|
||||
, m_device(device)
|
||||
{
|
||||
m_connection = XGetXCBConnection(display);
|
||||
|
||||
m_notifier = new QSocketNotifier(xcb_get_file_descriptor(m_connection), QSocketNotifier::Read, this);
|
||||
|
||||
xcb_query_extension_cookie_t inputExtCookie = xcb_query_extension(m_connection, std::strlen(INAME), INAME);
|
||||
QScopedPointer<xcb_query_extension_reply_t, QScopedPointerPodDeleter> inputExt(xcb_query_extension_reply(m_connection, inputExtCookie, nullptr));
|
||||
if (!inputExt) {
|
||||
return;
|
||||
}
|
||||
m_inputOpcode = inputExt->major_opcode;
|
||||
|
||||
const xcb_setup_t *setup = xcb_get_setup(m_connection);
|
||||
xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup);
|
||||
xcb_screen_t *screen = iter.data;
|
||||
|
||||
m_inputWindow = xcb_generate_id(m_connection);
|
||||
xcb_create_window(m_connection, 0, m_inputWindow, screen->root, 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY, 0, 0, nullptr);
|
||||
xcb_flush(m_connection);
|
||||
|
||||
XIEventMask masks[2];
|
||||
|
||||
unsigned char touchpadMask[] = {0, 0, 0, 0};
|
||||
masks[0].deviceid = device;
|
||||
masks[0].mask = touchpadMask;
|
||||
masks[0].mask_len = sizeof(touchpadMask);
|
||||
XISetMask(touchpadMask, XI_PropertyEvent);
|
||||
|
||||
unsigned char allMask[] = {0, 0, 0, 0};
|
||||
masks[1].deviceid = XIAllDevices;
|
||||
masks[1].mask = allMask;
|
||||
masks[1].mask_len = sizeof(allMask);
|
||||
XISetMask(allMask, XI_HierarchyChanged);
|
||||
|
||||
XISelectEvents(display, XDefaultRootWindow(display), masks, sizeof(masks) / sizeof(XIEventMask));
|
||||
XFlush(display);
|
||||
|
||||
connect(m_notifier, SIGNAL(activated(int)), SLOT(processEvents()));
|
||||
m_notifier->setEnabled(true);
|
||||
}
|
||||
|
||||
void XlibNotifications::processEvents()
|
||||
{
|
||||
while (XPending(m_display)) {
|
||||
XEvent event;
|
||||
XNextEvent(m_display, &event);
|
||||
processEvent(&event);
|
||||
}
|
||||
}
|
||||
|
||||
struct XEventDataDeleter {
|
||||
XEventDataDeleter(Display *display, XGenericEventCookie *cookie)
|
||||
: m_display(display)
|
||||
, m_cookie(cookie)
|
||||
{
|
||||
XGetEventData(m_display, m_cookie);
|
||||
}
|
||||
|
||||
~XEventDataDeleter()
|
||||
{
|
||||
if (m_cookie->data) {
|
||||
XFreeEventData(m_display, m_cookie);
|
||||
}
|
||||
}
|
||||
|
||||
Display *m_display;
|
||||
XGenericEventCookie *m_cookie;
|
||||
};
|
||||
|
||||
void XlibNotifications::processEvent(XEvent *event)
|
||||
{
|
||||
if (event->xcookie.type != GenericEvent) {
|
||||
return;
|
||||
}
|
||||
if (event->xcookie.extension != m_inputOpcode) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->xcookie.evtype == XI_PropertyEvent) {
|
||||
XEventDataDeleter helper(m_display, &event->xcookie);
|
||||
if (!event->xcookie.data) {
|
||||
return;
|
||||
}
|
||||
|
||||
XIPropertyEvent *propEvent = reinterpret_cast<XIPropertyEvent *>(event->xcookie.data);
|
||||
Q_EMIT propertyChanged(propEvent->property);
|
||||
} else if (event->xcookie.evtype == XI_HierarchyChanged) {
|
||||
XEventDataDeleter helper(m_display, &event->xcookie);
|
||||
if (!event->xcookie.data) {
|
||||
return;
|
||||
}
|
||||
|
||||
XIHierarchyEvent *hierarchyEvent = reinterpret_cast<XIHierarchyEvent *>(event->xcookie.data);
|
||||
for (uint16_t i = 0; i < hierarchyEvent->num_info; i++) {
|
||||
if (hierarchyEvent->info[i].deviceid == m_device) {
|
||||
if (hierarchyEvent->info[i].flags & XISlaveRemoved) {
|
||||
Q_EMIT touchpadDetached();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (hierarchyEvent->info[i].use != XISlavePointer) {
|
||||
continue;
|
||||
}
|
||||
if (hierarchyEvent->info[i].flags & (XIDeviceEnabled | XIDeviceDisabled)) {
|
||||
Q_EMIT devicePlugged(hierarchyEvent->info[i].deviceid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
XlibNotifications::~XlibNotifications()
|
||||
{
|
||||
xcb_destroy_window(m_connection, m_inputWindow);
|
||||
xcb_flush(m_connection);
|
||||
}
|
||||
|
||||
#include "moc_xlibnotifications.cpp"
|
||||
@ -1,41 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Alexander Mezin <mezin.alexander@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef XLIBNOTIFICATIONS_H
|
||||
#define XLIBNOTIFICATIONS_H
|
||||
|
||||
#include <QSocketNotifier>
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
class XlibNotifications : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
XlibNotifications(Display *display, int device);
|
||||
~XlibNotifications();
|
||||
|
||||
Q_SIGNALS:
|
||||
void propertyChanged(xcb_atom_t);
|
||||
void devicePlugged(int);
|
||||
void touchpadDetached();
|
||||
|
||||
private Q_SLOTS:
|
||||
void processEvents();
|
||||
|
||||
private:
|
||||
void processEvent(XEvent *);
|
||||
|
||||
Display *m_display;
|
||||
xcb_connection_t *m_connection;
|
||||
QSocketNotifier *m_notifier;
|
||||
xcb_window_t m_inputWindow;
|
||||
uint8_t m_inputOpcode;
|
||||
int m_device;
|
||||
};
|
||||
|
||||
#endif // XLIBNOTIFICATIONS_H
|
||||
@ -1,257 +0,0 @@
|
||||
#include <cmath>
|
||||
|
||||
#include "xlibtouchpad.h"
|
||||
#include <X11/Xlib-xcb.h>
|
||||
#include <X11/extensions/XInput.h>
|
||||
#include <X11/extensions/XInput2.h>
|
||||
#include <xserver-properties.h>
|
||||
|
||||
static QVariant negateVariant(const QVariant &value)
|
||||
{
|
||||
if (value.type() == QVariant::Double) {
|
||||
return QVariant(-value.toDouble());
|
||||
} else if (value.type() == QVariant::Int) {
|
||||
return QVariant(-value.toInt());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
XlibTouchpad::XlibTouchpad(Display *display, int deviceId)
|
||||
: m_display(display)
|
||||
, m_connection(XGetXCBConnection(display))
|
||||
, m_deviceId(deviceId)
|
||||
{
|
||||
m_floatType.intern(m_connection, "FLOAT");
|
||||
m_enabledAtom.intern(m_connection, XI_PROP_ENABLED);
|
||||
}
|
||||
|
||||
bool XlibTouchpad::applyConfig(const QVariantHash &p)
|
||||
{
|
||||
m_props.clear();
|
||||
|
||||
bool error = false;
|
||||
for (const QString &name : qAsConst(m_supported)) {
|
||||
QVariantHash::ConstIterator i = p.find(name);
|
||||
if (i == p.end()) {
|
||||
continue;
|
||||
}
|
||||
const Parameter *par = findParameter(name);
|
||||
if (par) {
|
||||
QVariant value(i.value());
|
||||
|
||||
double k = getPropertyScale(name);
|
||||
if (k != 1.0) {
|
||||
bool ok = false;
|
||||
value = QVariant(value.toDouble(&ok) * k);
|
||||
if (!ok) {
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_negate.contains(name)) {
|
||||
QVariantHash::ConstIterator i = p.find(m_negate[name]);
|
||||
if (i != p.end() && i.value().toBool()) {
|
||||
value = negateVariant(value);
|
||||
}
|
||||
}
|
||||
|
||||
if (name == "CoastingSpeed") {
|
||||
QVariantHash::ConstIterator coastingEnabled = p.find("Coasting");
|
||||
if (coastingEnabled != p.end() && !coastingEnabled.value().toBool()) {
|
||||
value = QVariant(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (!setParameter(par, value)) {
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
flush();
|
||||
|
||||
return !error;
|
||||
}
|
||||
|
||||
bool XlibTouchpad::getConfig(QVariantHash &p)
|
||||
{
|
||||
if (m_supported.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_props.clear();
|
||||
|
||||
bool error = false;
|
||||
for (const QString &name : qAsConst(m_supported)) {
|
||||
const Parameter *par = findParameter(name);
|
||||
if (!par) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QVariant value(getParameter(par));
|
||||
if (!value.isValid()) {
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
double k = getPropertyScale(name);
|
||||
if (k != 1.0) {
|
||||
bool ok = false;
|
||||
value = QVariant(value.toDouble(&ok) / k);
|
||||
if (!ok) {
|
||||
error = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_negate.contains(name)) {
|
||||
bool negative = value.toDouble() < 0.0;
|
||||
p[m_negate[name]] = QVariant(negative);
|
||||
if (negative) {
|
||||
value = negateVariant(value);
|
||||
}
|
||||
}
|
||||
|
||||
if (name == "CoastingSpeed") {
|
||||
bool coasting = value.toDouble() != 0.0;
|
||||
p["Coasting"] = QVariant(coasting);
|
||||
if (!coasting) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
p[name] = value;
|
||||
}
|
||||
|
||||
return !error;
|
||||
}
|
||||
|
||||
void XlibTouchpad::loadSupportedProperties(const Parameter *props)
|
||||
{
|
||||
m_paramList = props;
|
||||
for (const Parameter *param = props; param->name; param++) {
|
||||
QLatin1String name(param->prop_name);
|
||||
|
||||
if (!m_atoms.contains(name)) {
|
||||
m_atoms.insert(name, QSharedPointer<XcbAtom>(new XcbAtom(m_connection, param->prop_name)));
|
||||
}
|
||||
}
|
||||
|
||||
for (const Parameter *p = props; p->name; p++) {
|
||||
if (getParameter(p).isValid()) {
|
||||
m_supported.append(p->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QVariant XlibTouchpad::getParameter(const Parameter *par)
|
||||
{
|
||||
PropertyInfo *p = getDevProperty(QLatin1String(par->prop_name));
|
||||
if (!p || par->prop_offset >= p->nitems) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
return p->value(par->prop_offset);
|
||||
}
|
||||
|
||||
void XlibTouchpad::flush()
|
||||
{
|
||||
for (const QLatin1String &name : qAsConst(m_changed)) {
|
||||
m_props[name].set();
|
||||
}
|
||||
m_changed.clear();
|
||||
|
||||
XFlush(m_display);
|
||||
}
|
||||
|
||||
double XlibTouchpad::getPropertyScale(const QString &name) const
|
||||
{
|
||||
Q_UNUSED(name);
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
PropertyInfo *XlibTouchpad::getDevProperty(const QLatin1String &propName)
|
||||
{
|
||||
if (m_props.contains(propName)) {
|
||||
return &m_props[propName];
|
||||
}
|
||||
|
||||
if (!m_atoms.contains(propName) || !m_atoms[propName]) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
xcb_atom_t prop = m_atoms[propName]->atom();
|
||||
if (!prop) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PropertyInfo p(m_display, m_deviceId, prop, m_floatType.atom());
|
||||
if (!p.b && !p.f && !p.i) {
|
||||
return nullptr;
|
||||
}
|
||||
return &m_props.insert(propName, p).value();
|
||||
}
|
||||
|
||||
bool XlibTouchpad::setParameter(const Parameter *par, const QVariant &value)
|
||||
{
|
||||
QLatin1String propName(par->prop_name);
|
||||
PropertyInfo *p = getDevProperty(propName);
|
||||
if (!p || par->prop_offset >= p->nitems) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariant converted(value);
|
||||
QVariant::Type convType = QVariant::Int;
|
||||
if (p->f) {
|
||||
convType = QVariant::Double;
|
||||
} else if (value.type() == QVariant::Double) {
|
||||
converted = QVariant(qRound(static_cast<qreal>(value.toDouble())));
|
||||
}
|
||||
|
||||
if (!converted.convert(convType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (converted == p->value(par->prop_offset)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (p->b) {
|
||||
p->b[par->prop_offset] = static_cast<char>(converted.toInt());
|
||||
} else if (p->i) {
|
||||
p->i[par->prop_offset] = converted.toInt();
|
||||
} else if (p->f) {
|
||||
p->f[par->prop_offset] = converted.toDouble();
|
||||
}
|
||||
|
||||
m_changed.insert(propName);
|
||||
return true;
|
||||
}
|
||||
|
||||
void XlibTouchpad::setEnabled(bool enable)
|
||||
{
|
||||
PropertyInfo enabled(m_display, m_deviceId, m_enabledAtom.atom(), 0);
|
||||
if (enabled.b && *(enabled.b) != enable) {
|
||||
*(enabled.b) = enable;
|
||||
enabled.set();
|
||||
}
|
||||
|
||||
flush();
|
||||
}
|
||||
|
||||
bool XlibTouchpad::enabled()
|
||||
{
|
||||
PropertyInfo enabled(m_display, m_deviceId, m_enabledAtom.atom(), 0);
|
||||
return enabled.value(0).toBool();
|
||||
}
|
||||
|
||||
const Parameter *XlibTouchpad::findParameter(const QString &name)
|
||||
{
|
||||
for (const Parameter *par = m_paramList; par->name; par++) {
|
||||
if (name == par->name) {
|
||||
return par;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@ -1,101 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2015 Weng Xuetian <wengxt@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef XLIBTOUCHPAD_H
|
||||
#define XLIBTOUCHPAD_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QSet>
|
||||
#include <QVariantHash>
|
||||
|
||||
#include "propertyinfo.h"
|
||||
#include "xcbatom.h"
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
enum ParaType {
|
||||
PT_INT,
|
||||
PT_BOOL,
|
||||
PT_DOUBLE,
|
||||
};
|
||||
|
||||
struct Parameter {
|
||||
const char *name; /* Name of parameter */
|
||||
enum ParaType type; /* Type of parameter */
|
||||
double min_val; /* Minimum allowed value */
|
||||
double max_val; /* Maximum allowed value */
|
||||
const char *prop_name; /* Property name */
|
||||
int prop_format; /* Property format (0 for floats) */
|
||||
unsigned prop_offset; /* Offset inside property */
|
||||
};
|
||||
|
||||
class XlibTouchpad
|
||||
{
|
||||
public:
|
||||
XlibTouchpad(Display *display, int deviceId);
|
||||
virtual ~XlibTouchpad()
|
||||
{
|
||||
}
|
||||
|
||||
int deviceId()
|
||||
{
|
||||
return m_deviceId;
|
||||
}
|
||||
const QStringList &supportedParameters() const
|
||||
{
|
||||
return m_supported;
|
||||
}
|
||||
bool applyConfig(const QVariantHash &p);
|
||||
bool getConfig(QVariantHash &p);
|
||||
virtual bool getConfig()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual bool applyConfig()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual bool getDefaultConfig()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual bool isChangedConfig()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
void setEnabled(bool enable);
|
||||
bool enabled();
|
||||
virtual void setTouchpadOff(int /*touchpadOff*/)
|
||||
{
|
||||
}
|
||||
virtual int touchpadOff() = 0;
|
||||
|
||||
virtual XcbAtom &touchpadOffAtom() = 0;
|
||||
|
||||
protected:
|
||||
void loadSupportedProperties(const Parameter *props);
|
||||
bool setParameter(const struct Parameter *, const QVariant &);
|
||||
QVariant getParameter(const struct Parameter *);
|
||||
struct PropertyInfo *getDevProperty(const QLatin1String &propName);
|
||||
void flush();
|
||||
virtual double getPropertyScale(const QString &name) const;
|
||||
const Parameter *findParameter(const QString &name);
|
||||
|
||||
Display *m_display;
|
||||
xcb_connection_t *m_connection;
|
||||
int m_deviceId;
|
||||
|
||||
XcbAtom m_floatType, m_enabledAtom;
|
||||
|
||||
QMap<QLatin1String, QSharedPointer<XcbAtom>> m_atoms;
|
||||
|
||||
QMap<QString, QString> m_negate;
|
||||
QMap<QLatin1String, struct PropertyInfo> m_props;
|
||||
QSet<QLatin1String> m_changed;
|
||||
QStringList m_supported;
|
||||
const struct Parameter *m_paramList;
|
||||
};
|
||||
|
||||
#endif
|
||||
@ -1,140 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Alexander Mezin <mezin.alexander@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "xrecordkeyboardmonitor.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <limits>
|
||||
|
||||
#include <QScopedPointer>
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <xcb/xcbext.h>
|
||||
|
||||
XRecordKeyboardMonitor::XRecordKeyboardMonitor(Display *display)
|
||||
: m_connection(xcb_connect(XDisplayString(display), nullptr))
|
||||
, m_modifiersPressed(0)
|
||||
, m_keysPressed(0)
|
||||
{
|
||||
if (!m_connection) {
|
||||
return;
|
||||
}
|
||||
|
||||
xcb_get_modifier_mapping_cookie_t modmapCookie = xcb_get_modifier_mapping(m_connection);
|
||||
|
||||
m_context = xcb_generate_id(m_connection);
|
||||
xcb_record_range_t range;
|
||||
memset(&range, 0, sizeof(range));
|
||||
range.device_events.first = XCB_KEY_PRESS;
|
||||
range.device_events.last = XCB_KEY_RELEASE;
|
||||
xcb_record_client_spec_t cs = XCB_RECORD_CS_ALL_CLIENTS;
|
||||
xcb_record_create_context(m_connection, m_context, 0, 1, 1, &cs, &range);
|
||||
xcb_flush(m_connection);
|
||||
|
||||
QScopedPointer<xcb_get_modifier_mapping_reply_t, QScopedPointerPodDeleter> modmap(xcb_get_modifier_mapping_reply(m_connection, modmapCookie, nullptr));
|
||||
if (!modmap) {
|
||||
return;
|
||||
}
|
||||
|
||||
int nModifiers = xcb_get_modifier_mapping_keycodes_length(modmap.data());
|
||||
xcb_keycode_t *modifiers = xcb_get_modifier_mapping_keycodes(modmap.data());
|
||||
m_modifier.fill(false, std::numeric_limits<xcb_keycode_t>::max() + 1);
|
||||
for (xcb_keycode_t *i = modifiers; i < modifiers + nModifiers; i++) {
|
||||
m_modifier[*i] = true;
|
||||
}
|
||||
m_ignore.fill(false, std::numeric_limits<xcb_keycode_t>::max() + 1);
|
||||
for (xcb_keycode_t *i = modifiers; i < modifiers + modmap->keycodes_per_modifier; i++) {
|
||||
m_ignore[*i] = true;
|
||||
}
|
||||
m_pressed.fill(false, std::numeric_limits<xcb_keycode_t>::max() + 1);
|
||||
|
||||
m_cookie = xcb_record_enable_context(m_connection, m_context);
|
||||
xcb_flush(m_connection);
|
||||
|
||||
m_notifier = new QSocketNotifier(xcb_get_file_descriptor(m_connection), QSocketNotifier::Read, this);
|
||||
connect(m_notifier, &QSocketNotifier::activated, this, &XRecordKeyboardMonitor::processNextReply);
|
||||
m_notifier->setEnabled(true);
|
||||
}
|
||||
|
||||
XRecordKeyboardMonitor::~XRecordKeyboardMonitor()
|
||||
{
|
||||
if (!m_connection) {
|
||||
return;
|
||||
}
|
||||
|
||||
xcb_record_disable_context(m_connection, m_context);
|
||||
xcb_record_free_context(m_connection, m_context);
|
||||
xcb_disconnect(m_connection);
|
||||
}
|
||||
|
||||
void XRecordKeyboardMonitor::processNextReply()
|
||||
{
|
||||
xcb_generic_event_t *event;
|
||||
while ((event = xcb_poll_for_event(m_connection))) {
|
||||
std::free(event);
|
||||
}
|
||||
|
||||
void *reply = nullptr;
|
||||
xcb_generic_error_t *error = nullptr;
|
||||
while (m_cookie.sequence && xcb_poll_for_reply(m_connection, m_cookie.sequence, &reply, &error)) {
|
||||
// xcb_poll_for_reply may set both reply and error to null if connection has error.
|
||||
// break if xcb_connection has error, no point to continue anyway.
|
||||
if (xcb_connection_has_error(m_connection)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
std::free(error);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!reply) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QScopedPointer<xcb_record_enable_context_reply_t, QScopedPointerPodDeleter> data(reinterpret_cast<xcb_record_enable_context_reply_t *>(reply));
|
||||
process(data.data());
|
||||
}
|
||||
}
|
||||
|
||||
void XRecordKeyboardMonitor::process(xcb_record_enable_context_reply_t *reply)
|
||||
{
|
||||
bool prevActivity = activity();
|
||||
|
||||
xcb_key_press_event_t *events = reinterpret_cast<xcb_key_press_event_t *>(xcb_record_enable_context_data(reply));
|
||||
int nEvents = xcb_record_enable_context_data_length(reply) / sizeof(xcb_key_press_event_t);
|
||||
bool wasActivity = prevActivity;
|
||||
for (xcb_key_press_event_t *e = events; e < events + nEvents; e++) {
|
||||
if (e->response_type != XCB_KEY_PRESS && e->response_type != XCB_KEY_RELEASE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_ignore[e->detail]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool pressed = (e->response_type == XCB_KEY_PRESS);
|
||||
if (m_pressed[e->detail] == pressed) {
|
||||
continue;
|
||||
}
|
||||
m_pressed[e->detail] = pressed;
|
||||
|
||||
int &counter = m_modifier[e->detail] ? m_modifiersPressed : m_keysPressed;
|
||||
if (pressed) {
|
||||
counter++;
|
||||
} else {
|
||||
counter--;
|
||||
}
|
||||
|
||||
wasActivity = wasActivity || activity();
|
||||
}
|
||||
|
||||
if (!prevActivity && activity()) {
|
||||
Q_EMIT keyboardActivityStarted();
|
||||
} else if (!activity() && wasActivity) {
|
||||
Q_EMIT keyboardActivityFinished();
|
||||
}
|
||||
}
|
||||
@ -1,49 +0,0 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 Alexander Mezin <mezin.alexander@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef XRECORDKEYBOARDMONITOR_H
|
||||
#define XRECORDKEYBOARDMONITOR_H
|
||||
|
||||
#include <QSocketNotifier>
|
||||
#include <QVector>
|
||||
|
||||
#include <QtGui/qguiapplication_platform.h>
|
||||
|
||||
#include <xcb/record.h>
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
class XRecordKeyboardMonitor : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
XRecordKeyboardMonitor(Display *display);
|
||||
~XRecordKeyboardMonitor();
|
||||
|
||||
Q_SIGNALS:
|
||||
void keyboardActivityStarted();
|
||||
void keyboardActivityFinished();
|
||||
|
||||
private Q_SLOTS:
|
||||
void processNextReply();
|
||||
|
||||
private:
|
||||
void process(xcb_record_enable_context_reply_t *reply);
|
||||
bool activity() const
|
||||
{
|
||||
return m_keysPressed && !m_modifiersPressed;
|
||||
}
|
||||
|
||||
QSocketNotifier *m_notifier;
|
||||
xcb_connection_t *m_connection;
|
||||
xcb_record_context_t m_context;
|
||||
xcb_record_enable_context_cookie_t m_cookie;
|
||||
|
||||
QVector<bool> m_modifier, m_ignore, m_pressed;
|
||||
int m_modifiersPressed, m_keysPressed;
|
||||
};
|
||||
|
||||
#endif // XRECORDKEYBOARDMONITOR_H
|
||||
@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QtGui/qguiapplication_platform.h>
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
namespace Cutefish::X11
|
||||
{
|
||||
inline xcb_window_t rootWindow()
|
||||
{
|
||||
const auto native = qGuiApp ? qGuiApp->nativeInterface<QNativeInterface::QX11Application>() : nullptr;
|
||||
const auto c = native ? native->connection() : nullptr;
|
||||
if (!c)
|
||||
return XCB_WINDOW_NONE;
|
||||
|
||||
const auto *setup = xcb_get_setup(c);
|
||||
const auto iterator = xcb_setup_roots_iterator(setup);
|
||||
return iterator.data ? iterator.data->root : XCB_WINDOW_NONE;
|
||||
}
|
||||
|
||||
inline int screenNumber()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -1,80 +0,0 @@
|
||||
project(cutefish-xembedsniproxy)
|
||||
|
||||
add_definitions(-DQT_NO_CAST_TO_ASCII
|
||||
-DQT_NO_CAST_FROM_ASCII
|
||||
-DQT_NO_URL_CAST_FROM_STRING
|
||||
-DQT_NO_CAST_FROM_BYTEARRAY)
|
||||
|
||||
find_package(X11)
|
||||
set_package_properties(X11 PROPERTIES DESCRIPTION "X11 libraries"
|
||||
URL "http://www.x.org"
|
||||
TYPE OPTIONAL
|
||||
PURPOSE "Required for building the X11 based workspace")
|
||||
|
||||
if(X11_FOUND)
|
||||
find_package(XCB MODULE REQUIRED COMPONENTS XCB RANDR)
|
||||
set_package_properties(XCB PROPERTIES TYPE REQUIRED)
|
||||
if(NOT X11_SM_FOUND)
|
||||
message(FATAL_ERROR "\nThe X11 Session Management (SM) development package could not be found.\nPlease install libSM.\n")
|
||||
endif(NOT X11_SM_FOUND)
|
||||
|
||||
find_package(Qt6 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Gui)
|
||||
endif()
|
||||
|
||||
if(X11_FOUND AND XCB_XCB_FOUND)
|
||||
set(HAVE_X11 1)
|
||||
endif()
|
||||
|
||||
find_package(XCB
|
||||
REQUIRED COMPONENTS
|
||||
XCB
|
||||
XFIXES
|
||||
DAMAGE
|
||||
COMPOSITE
|
||||
RANDR
|
||||
SHM
|
||||
UTIL
|
||||
IMAGE
|
||||
)
|
||||
|
||||
find_package(KF6WindowSystem)
|
||||
|
||||
set(XCB_LIBS
|
||||
XCB::XCB
|
||||
XCB::XFIXES
|
||||
XCB::DAMAGE
|
||||
XCB::COMPOSITE
|
||||
XCB::RANDR
|
||||
XCB::SHM
|
||||
XCB::UTIL
|
||||
XCB::IMAGE
|
||||
)
|
||||
|
||||
set(XEMBED_SNI_PROXY_SOURCES
|
||||
main.cpp
|
||||
fdoselectionmanager.cpp
|
||||
snidbus.cpp
|
||||
sniproxy.cpp
|
||||
debug.cpp
|
||||
xtestsender.cpp
|
||||
)
|
||||
|
||||
qt6_add_dbus_adaptor(DBUS_SOURCES org.kde.StatusNotifierItem.xml
|
||||
sniproxy.h SNIProxy)
|
||||
set(statusnotifierwatcher_xml org.kde.StatusNotifierWatcher.xml)
|
||||
qt6_add_dbus_interface(DBUS_SOURCES ${statusnotifierwatcher_xml} statusnotifierwatcher_interface)
|
||||
|
||||
set_source_files_properties(${DBUS_SOURCES} PROPERTIES SKIP_AUTOGEN ON)
|
||||
|
||||
add_executable(cutefish-xembedsniproxy ${XEMBED_SNI_PROXY_SOURCES} ${DBUS_SOURCES})
|
||||
set_package_properties(XCB PROPERTIES TYPE REQUIRED)
|
||||
target_link_libraries(cutefish-xembedsniproxy
|
||||
Qt6::Core
|
||||
Qt6::Gui
|
||||
Qt6::DBus
|
||||
KF6::WindowSystem
|
||||
${XCB_LIBS}
|
||||
${X11_XTest_LIB}
|
||||
)
|
||||
|
||||
install(TARGETS cutefish-xembedsniproxy DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
@ -1,8 +0,0 @@
|
||||
/* This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2015 Bhushan Shah <bshah@kde.org>
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(SNIPROXY, "com.cutefish.sniproxy", QtWarningMsg)
|
||||
@ -1,12 +0,0 @@
|
||||
/* This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2015 Bhushan Shah <bshah@kde.org>
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef DEBUG_H
|
||||
#define DEBUG_H
|
||||
|
||||
#include <QLoggingCategory>
|
||||
Q_DECLARE_LOGGING_CATEGORY(SNIPROXY)
|
||||
|
||||
#endif
|
||||
@ -1,234 +0,0 @@
|
||||
/*
|
||||
Registers as a embed container
|
||||
SPDX-FileCopyrightText: 2015 David Edmundson <davidedmundson@kde.org>
|
||||
SPDX-FileCopyrightText: 2019 Konrad Materka <materka@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
#include "fdoselectionmanager.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QTimer>
|
||||
|
||||
#include <KSelectionOwner>
|
||||
|
||||
#include <xcb/composite.h>
|
||||
#include <xcb/damage.h>
|
||||
#include <xcb/xcb_atom.h>
|
||||
#include <xcb/xcb_event.h>
|
||||
|
||||
#include "sniproxy.h"
|
||||
#include "xcbutils.h"
|
||||
|
||||
#define SYSTEM_TRAY_REQUEST_DOCK 0
|
||||
#define SYSTEM_TRAY_BEGIN_MESSAGE 1
|
||||
#define SYSTEM_TRAY_CANCEL_MESSAGE 2
|
||||
|
||||
FdoSelectionManager::FdoSelectionManager()
|
||||
: QObject()
|
||||
, m_selectionOwner(new KSelectionOwner(Xcb::atoms->selectionAtom, -1, this))
|
||||
{
|
||||
qCDebug(SNIPROXY) << "starting";
|
||||
|
||||
// we may end up calling QCoreApplication::quit() in this method, at which point we need the event loop running
|
||||
QTimer::singleShot(0, this, &FdoSelectionManager::init);
|
||||
}
|
||||
|
||||
FdoSelectionManager::~FdoSelectionManager()
|
||||
{
|
||||
qCDebug(SNIPROXY) << "closing";
|
||||
m_selectionOwner->release();
|
||||
}
|
||||
|
||||
void FdoSelectionManager::init()
|
||||
{
|
||||
// load damage extension
|
||||
xcb_connection_t *c = qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->connection();
|
||||
xcb_prefetch_extension_data(c, &xcb_damage_id);
|
||||
const auto *reply = xcb_get_extension_data(c, &xcb_damage_id);
|
||||
if (reply && reply->present) {
|
||||
m_damageEventBase = reply->first_event;
|
||||
xcb_damage_query_version_unchecked(c, XCB_DAMAGE_MAJOR_VERSION, XCB_DAMAGE_MINOR_VERSION);
|
||||
} else {
|
||||
// no XDamage means
|
||||
qCCritical(SNIPROXY) << "could not load damage extension. Quitting";
|
||||
qApp->exit(-1);
|
||||
}
|
||||
|
||||
qApp->installNativeEventFilter(this);
|
||||
|
||||
connect(m_selectionOwner, &KSelectionOwner::claimedOwnership, this, &FdoSelectionManager::onClaimedOwnership);
|
||||
connect(m_selectionOwner, &KSelectionOwner::failedToClaimOwnership, this, &FdoSelectionManager::onFailedToClaimOwnership);
|
||||
connect(m_selectionOwner, &KSelectionOwner::lostOwnership, this, &FdoSelectionManager::onLostOwnership);
|
||||
m_selectionOwner->claim(false);
|
||||
}
|
||||
|
||||
bool FdoSelectionManager::addDamageWatch(xcb_window_t client)
|
||||
{
|
||||
qCDebug(SNIPROXY) << "adding damage watch for " << client;
|
||||
|
||||
xcb_connection_t *c = qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->connection();
|
||||
const auto attribsCookie = xcb_get_window_attributes_unchecked(c, client);
|
||||
|
||||
const auto damageId = xcb_generate_id(c);
|
||||
m_damageWatches[client] = damageId;
|
||||
xcb_damage_create(c, damageId, client, XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY);
|
||||
|
||||
xcb_generic_error_t *error = nullptr;
|
||||
QScopedPointer<xcb_get_window_attributes_reply_t, QScopedPointerPodDeleter> attr(xcb_get_window_attributes_reply(c, attribsCookie, &error));
|
||||
QScopedPointer<xcb_generic_error_t, QScopedPointerPodDeleter> getAttrError(error);
|
||||
uint32_t events = XCB_EVENT_MASK_STRUCTURE_NOTIFY;
|
||||
if (!attr.isNull()) {
|
||||
events = events | attr->your_event_mask;
|
||||
}
|
||||
// if window is already gone, there is no need to handle it.
|
||||
if (getAttrError && getAttrError->error_code == XCB_WINDOW) {
|
||||
return false;
|
||||
}
|
||||
// the event mask will not be removed again. We cannot track whether another component also needs STRUCTURE_NOTIFY (e.g. KWindowSystem).
|
||||
// if we would remove the event mask again, other areas will break.
|
||||
const auto changeAttrCookie = xcb_change_window_attributes_checked(c, client, XCB_CW_EVENT_MASK, &events);
|
||||
QScopedPointer<xcb_generic_error_t, QScopedPointerPodDeleter> changeAttrError(xcb_request_check(c, changeAttrCookie));
|
||||
// if window is gone by this point, it will be caught by eventFilter, so no need to check later errors.
|
||||
if (changeAttrError && changeAttrError->error_code == XCB_WINDOW) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FdoSelectionManager::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result)
|
||||
{
|
||||
Q_UNUSED(result)
|
||||
|
||||
if (eventType != "xcb_generic_event_t") {
|
||||
return false;
|
||||
}
|
||||
|
||||
xcb_generic_event_t *ev = static_cast<xcb_generic_event_t *>(message);
|
||||
|
||||
const auto responseType = XCB_EVENT_RESPONSE_TYPE(ev);
|
||||
if (responseType == XCB_CLIENT_MESSAGE) {
|
||||
const auto ce = reinterpret_cast<xcb_client_message_event_t *>(ev);
|
||||
if (ce->type == Xcb::atoms->opcodeAtom) {
|
||||
switch (ce->data.data32[1]) {
|
||||
case SYSTEM_TRAY_REQUEST_DOCK:
|
||||
dock(ce->data.data32[2]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else if (responseType == XCB_UNMAP_NOTIFY) {
|
||||
const auto unmappedWId = reinterpret_cast<xcb_unmap_notify_event_t *>(ev)->window;
|
||||
if (m_proxies.contains(unmappedWId)) {
|
||||
undock(unmappedWId);
|
||||
}
|
||||
} else if (responseType == XCB_DESTROY_NOTIFY) {
|
||||
const auto destroyedWId = reinterpret_cast<xcb_destroy_notify_event_t *>(ev)->window;
|
||||
if (m_proxies.contains(destroyedWId)) {
|
||||
undock(destroyedWId);
|
||||
}
|
||||
} else if (responseType == m_damageEventBase + XCB_DAMAGE_NOTIFY) {
|
||||
const auto damagedWId = reinterpret_cast<xcb_damage_notify_event_t *>(ev)->drawable;
|
||||
const auto sniProxy = m_proxies.value(damagedWId);
|
||||
if (sniProxy) {
|
||||
sniProxy->update();
|
||||
xcb_damage_subtract(qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->connection(), m_damageWatches[damagedWId], XCB_NONE, XCB_NONE);
|
||||
}
|
||||
} else if (responseType == XCB_CONFIGURE_REQUEST) {
|
||||
const auto event = reinterpret_cast<xcb_configure_request_event_t *>(ev);
|
||||
const auto sniProxy = m_proxies.value(event->window);
|
||||
if (sniProxy) {
|
||||
// The embedded window tries to move or resize. Ignore move, handle resize only.
|
||||
if ((event->value_mask & XCB_CONFIG_WINDOW_WIDTH) || (event->value_mask & XCB_CONFIG_WINDOW_HEIGHT)) {
|
||||
sniProxy->resizeWindow(event->width, event->height);
|
||||
}
|
||||
}
|
||||
} else if (responseType == XCB_VISIBILITY_NOTIFY) {
|
||||
const auto event = reinterpret_cast<xcb_visibility_notify_event_t *>(ev);
|
||||
// it's possible that something showed our container window, we have to hide it
|
||||
// workaround for BUG 357443: when KWin is restarted, container window is shown on top
|
||||
if (event->state == XCB_VISIBILITY_UNOBSCURED) {
|
||||
for (auto sniProxy : m_proxies.values()) {
|
||||
sniProxy->hideContainerWindow(event->window);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void FdoSelectionManager::dock(xcb_window_t winId)
|
||||
{
|
||||
qCDebug(SNIPROXY) << "trying to dock window " << winId;
|
||||
|
||||
if (m_proxies.contains(winId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (addDamageWatch(winId)) {
|
||||
m_proxies[winId] = new SNIProxy(winId, this);
|
||||
}
|
||||
}
|
||||
|
||||
void FdoSelectionManager::undock(xcb_window_t winId)
|
||||
{
|
||||
qCDebug(SNIPROXY) << "trying to undock window " << winId;
|
||||
|
||||
if (!m_proxies.contains(winId)) {
|
||||
return;
|
||||
}
|
||||
m_proxies[winId]->deleteLater();
|
||||
m_proxies.remove(winId);
|
||||
}
|
||||
|
||||
void FdoSelectionManager::onClaimedOwnership()
|
||||
{
|
||||
qCDebug(SNIPROXY) << "Manager selection claimed";
|
||||
|
||||
setSystemTrayVisual();
|
||||
}
|
||||
|
||||
void FdoSelectionManager::onFailedToClaimOwnership()
|
||||
{
|
||||
qCWarning(SNIPROXY) << "failed to claim ownership of Systray Manager";
|
||||
qApp->exit(-1);
|
||||
}
|
||||
|
||||
void FdoSelectionManager::onLostOwnership()
|
||||
{
|
||||
qCWarning(SNIPROXY) << "lost ownership of Systray Manager";
|
||||
qApp->exit(-1);
|
||||
}
|
||||
|
||||
void FdoSelectionManager::setSystemTrayVisual()
|
||||
{
|
||||
xcb_connection_t *c = qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->connection();
|
||||
auto screen = xcb_setup_roots_iterator(xcb_get_setup(c)).data;
|
||||
auto trayVisual = screen->root_visual;
|
||||
xcb_depth_iterator_t depth_iterator = xcb_screen_allowed_depths_iterator(screen);
|
||||
xcb_depth_t *depth = nullptr;
|
||||
|
||||
while (depth_iterator.rem) {
|
||||
if (depth_iterator.data->depth == 32) {
|
||||
depth = depth_iterator.data;
|
||||
break;
|
||||
}
|
||||
xcb_depth_next(&depth_iterator);
|
||||
}
|
||||
|
||||
if (depth) {
|
||||
xcb_visualtype_iterator_t visualtype_iterator = xcb_depth_visuals_iterator(depth);
|
||||
while (visualtype_iterator.rem) {
|
||||
xcb_visualtype_t *visualtype = visualtype_iterator.data;
|
||||
if (visualtype->_class == XCB_VISUAL_CLASS_TRUE_COLOR) {
|
||||
trayVisual = visualtype->visual_id;
|
||||
break;
|
||||
}
|
||||
xcb_visualtype_next(&visualtype_iterator);
|
||||
}
|
||||
}
|
||||
|
||||
xcb_change_property(c, XCB_PROP_MODE_REPLACE, m_selectionOwner->ownerWindow(), Xcb::atoms->visualAtom, XCB_ATOM_VISUALID, 32, 1, &trayVisual);
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
/*
|
||||
Registers as a embed container
|
||||
SPDX-FileCopyrightText: 2015 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractNativeEventFilter>
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
class KSelectionOwner;
|
||||
class SNIProxy;
|
||||
|
||||
class FdoSelectionManager : public QObject, public QAbstractNativeEventFilter
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FdoSelectionManager();
|
||||
~FdoSelectionManager() override;
|
||||
|
||||
protected:
|
||||
bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override;
|
||||
|
||||
private Q_SLOTS:
|
||||
void onClaimedOwnership();
|
||||
void onFailedToClaimOwnership();
|
||||
void onLostOwnership();
|
||||
|
||||
private:
|
||||
void init();
|
||||
bool addDamageWatch(xcb_window_t client);
|
||||
void dock(xcb_window_t embed_win);
|
||||
void undock(xcb_window_t client);
|
||||
void setSystemTrayVisual();
|
||||
|
||||
uint8_t m_damageEventBase;
|
||||
|
||||
QHash<xcb_window_t, u_int32_t> m_damageWatches;
|
||||
QHash<xcb_window_t, SNIProxy *> m_proxies;
|
||||
KSelectionOwner *m_selectionOwner;
|
||||
};
|
||||
@ -1,60 +0,0 @@
|
||||
/*
|
||||
Main
|
||||
SPDX-FileCopyrightText: 2015 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*/
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QSessionManager>
|
||||
|
||||
#include "fdoselectionmanager.h"
|
||||
|
||||
#include "debug.h"
|
||||
#include "snidbus.h"
|
||||
#include "xcbutils.h"
|
||||
|
||||
#include <QDBusMetaType>
|
||||
|
||||
#include <KWindowSystem>
|
||||
|
||||
namespace Xcb
|
||||
{
|
||||
Xcb::Atoms *atoms;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// the whole point of this is to interact with X, if we are in any other session, force trying to connect to X
|
||||
// if the QPA can't load xcb, this app is useless anyway.
|
||||
qputenv("QT_QPA_PLATFORM", "xcb");
|
||||
|
||||
QGuiApplication::setDesktopSettingsAware(false);
|
||||
|
||||
QGuiApplication app(argc, argv);
|
||||
|
||||
if (!KWindowSystem::isPlatformX11()) {
|
||||
qFatal("xembed-sni-proxy is only useful XCB. Aborting");
|
||||
}
|
||||
|
||||
auto disableSessionManagement = [](QSessionManager &sm) {
|
||||
sm.setRestartHint(QSessionManager::RestartNever);
|
||||
};
|
||||
QObject::connect(&app, &QGuiApplication::commitDataRequest, disableSessionManagement);
|
||||
QObject::connect(&app, &QGuiApplication::saveStateRequest, disableSessionManagement);
|
||||
|
||||
app.setQuitOnLastWindowClosed(false);
|
||||
|
||||
qDBusRegisterMetaType<KDbusImageStruct>();
|
||||
qDBusRegisterMetaType<KDbusImageVector>();
|
||||
qDBusRegisterMetaType<KDbusToolTipStruct>();
|
||||
|
||||
Xcb::atoms = new Xcb::Atoms();
|
||||
|
||||
FdoSelectionManager manager;
|
||||
|
||||
auto rc = app.exec();
|
||||
|
||||
delete Xcb::atoms;
|
||||
return rc;
|
||||
}
|
||||
@ -1,63 +0,0 @@
|
||||
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||
<node>
|
||||
<!-- This is a minimally cut down version of the interface only implementing the
|
||||
methods and properties used by xembedsniproxy -->
|
||||
<interface name="org.kde.StatusNotifierItem">
|
||||
|
||||
<property name="Category" type="s" access="read"/>
|
||||
<property name="Id" type="s" access="read"/>
|
||||
<property name="Title" type="s" access="read"/>
|
||||
<property name="Status" type="s" access="read"/>
|
||||
<property name="WindowId" type="i" access="read"/>
|
||||
|
||||
<property name="ItemIsMenu" type="b" access="read"/>
|
||||
|
||||
|
||||
<property name="IconPixmap" type="(iiay)" access="read">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName" value="KDbusImageVector"/>
|
||||
</property>
|
||||
|
||||
<!-- interaction: the systemtray wants the application to do something -->
|
||||
<method name="ContextMenu">
|
||||
<!-- we're passing the coordinates of the icon, so the app knows where to put the popup window -->
|
||||
<arg name="x" type="i" direction="in"/>
|
||||
<arg name="y" type="i" direction="in"/>
|
||||
</method>
|
||||
|
||||
<method name="Activate">
|
||||
<arg name="x" type="i" direction="in"/>
|
||||
<arg name="y" type="i" direction="in"/>
|
||||
</method>
|
||||
|
||||
<method name="SecondaryActivate">
|
||||
<arg name="x" type="i" direction="in"/>
|
||||
<arg name="y" type="i" direction="in"/>
|
||||
</method>
|
||||
|
||||
<method name="Scroll">
|
||||
<arg name="delta" type="i" direction="in"/>
|
||||
<arg name="orientation" type="s" direction="in"/>
|
||||
</method>
|
||||
|
||||
<!-- Signals: the client wants to change something in the status-->
|
||||
<signal name="NewTitle">
|
||||
</signal>
|
||||
|
||||
<signal name="NewIcon">
|
||||
</signal>
|
||||
|
||||
<signal name="NewAttentionIcon">
|
||||
</signal>
|
||||
|
||||
<signal name="NewOverlayIcon">
|
||||
</signal>
|
||||
|
||||
<signal name="NewToolTip">
|
||||
</signal>
|
||||
|
||||
<signal name="NewStatus">
|
||||
<arg name="status" type="s"/>
|
||||
</signal>
|
||||
|
||||
</interface>
|
||||
</node>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue