feat(core): migrate services to Wayland

main
Reion Wong 3 weeks ago
parent 62b998573b
commit 0926373b14

@ -26,12 +26,8 @@ add_subdirectory(screen-brightness)
add_subdirectory(session)
add_subdirectory(settings-daemon)
add_subdirectory(shutdown-ui)
add_subdirectory(xembed-sni-proxy)
add_subdirectory(powerman)
add_subdirectory(cpufreq)
add_subdirectory(chotkeys)
add_subdirectory(cupdatecursor)
add_subdirectory(gmenuproxy)
add_subdirectory(notificationd)
add_subdirectory(sddm-helper)
add_subdirectory(clipboard)

@ -4,34 +4,18 @@ System backend and start session and more.
## Compile dependencies
```shell
sudo pacman -S extra-cmake-modules pkgconf qt5-base qt5-quickcontrols2 qt5-x11extras qt5-tools\
kwindowsystem polkit polkit-qt5 xorg-server-devel xf86-input-libinput xf86-input-synaptics
```
For Ubuntu:
```shell
sudo apt install libpolkit-agent-1-dev libpolkit-qt5-1-dev libsm-dev libxtst-dev\
libxcb-randr0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxcb-composite0-dev libxcb-damage0-dev libxcb-image0-dev libxcb-util0-dev libkf5idletime-dev
```
(Yes it's annoying that so many xcb's packages here is needed to install. Isn't there a way to install one package and these `libxcb`s all get ready?)
For Debian:
```shell
sudo apt install extra-cmake-modules pkg-config xserver-xorg-input-libinput-dev libx11-xcb-dev libxcb1-dev libxcb-randr0-dev\
libxcb-keysyms1-dev libxcursor-dev libxcb-xfixes0-dev libxcb-damage0-dev libxcb-composite0-dev libxcb-shm0-dev libxcb-util-dev\
libxcb-image0-dev libxcb-dpms0-dev libxcb-dri2-0-dev libxcb-dri3-dev libxcb-ewmh-dev libxcb-glx0-dev libxcb-record0-dev xserver-xorg-dev\
xserver-xorg-input-synaptics-dev libxtst-dev libsm-dev libpolkit-qt5-1-dev libpolkit-agent-1-dev libkf5windowsystem-dev libkf5globalaccel-dev\
libkf5coreaddons-dev libkf5idletime-dev libqt5x11extras5-dev qtbase5-dev qtdeclarative5-dev qtquickcontrols2-5-dev qttools5-dev qttools5-dev-tools
```
Install Qt 6, KDE Frameworks 6, NetworkManager, Polkit, Fontconfig, FreeType
and the development tools provided by your distribution. The currently
retained settings daemon has its remaining input-backend dependencies
declared in `settings-daemon/CMakeLists.txt`.
## Runtime
For the Wayland session, install KWin Wayland and select `Cutefish (KWin
Wayland)` from the display manager. The session entry launches KWin with
XWayland enabled and starts `cutefish-session --wayland --no-wm` inside the
compositor. The Settings application and its X11-only settings daemon are not
started by this session yet.
Wayland enabled and starts `cutefish-session` inside the
compositor. The session starts the retained settings daemon, which in turn
starts the desktop components after its D-Bus service is ready.
## Build

@ -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;
}

24
debian/control vendored

@ -6,32 +6,8 @@ Build-Depends: cmake,
debhelper (>= 9),
extra-cmake-modules,
pkg-config,
xserver-xorg-input-libinput-dev,
libx11-xcb-dev,
libxcb1-dev,
libxcb-randr0-dev,
libxcb-keysyms1-dev,
libxcursor-dev,
libxcb-xfixes0-dev,
libxcb-damage0-dev,
libxcb-composite0-dev,
libxcb-shm0-dev,
libxcb-util-dev,
libxcb-image0-dev,
libxcb-dpms0-dev,
libxcb-dri2-0-dev,
libxcb-dri3-dev,
libxcb-ewmh-dev,
libxcb-glx0-dev,
libxcb-record0-dev,
xserver-xorg-dev,
xserver-xorg-input-synaptics-dev,
libxtst-dev,
libsm-dev,
libpolkit-qt6-1-dev,
libpolkit-agent-1-dev,
libkf6windowsystem-dev,
libkf6globalaccel-dev,
libkf6coreaddons-dev,
libkf6idletime-dev,
qt6-base-dev,

@ -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&lt;int&gt;"/>
<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&lt;int&gt;\" 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 &section) {
// 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 &section : 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 &section, 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 &section, 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;
};

@ -20,8 +20,6 @@ set(SRCS main.cpp
qt6_add_dbus_adaptor(DBUS_SOURCES com.cutefish.Notification.xml application.h Application)
set_source_files_properties(${DBUS_SOURCES} PROPERTIES SKIP_AUTOGEN ON)
find_package(KF6WindowSystem)
add_executable(cutefish-notificationd ${SRCS} ${DBUS_SOURCES})
target_link_libraries(cutefish-notificationd
@ -29,7 +27,6 @@ target_link_libraries(cutefish-notificationd
Qt6::DBus
Qt6::Quick
Qt6::Widgets
KF6::WindowSystem
)
install(TARGETS cutefish-notificationd

@ -21,10 +21,6 @@
#include <QQmlContext>
#include <KX11Extras>
#include <KWindowEffects>
#include <KWindowSystem>
#include <netwm.h>
NotificationPopup::NotificationPopup(QQuickView *parent)
: QQuickView(parent)
@ -37,10 +33,5 @@ NotificationPopup::NotificationPopup(QQuickView *parent)
bool NotificationPopup::eventFilter(QObject *object, QEvent *event)
{
if (event->type() == QEvent::Show) {
if (KWindowSystem::isPlatformX11())
KX11Extras::setState(winId(), NET::SkipTaskbar | NET::SkipPager | NET::SkipSwitcher);
}
return QObject::eventFilter(object, event);
}

@ -23,10 +23,6 @@
#include <QQmlContext>
#include <KX11Extras>
#include <KWindowEffects>
#include <KWindowSystem>
#include <netwm.h>
NotificationWindow::NotificationWindow(QQuickView *parent)
: QQuickView(parent)
@ -41,7 +37,6 @@ NotificationWindow::NotificationWindow(QQuickView *parent)
rootContext()->setContextProperty("notificationsModel", NotificationsModel::self());
rootContext()->setContextProperty("historyModel", HistoryModel::self());
// KWindowEffects::slideWindow(winId(), KWindowEffects::RightEdge);
setSource(QUrl("qrc:/qml/NotificationWindow.qml"));
setVisible(false);
}
@ -67,8 +62,6 @@ bool NotificationWindow::eventFilter(QObject *object, QEvent *event)
QQuickView::setVisible(false);
}
} else if (event->type() == QEvent::Show) {
if (KWindowSystem::isPlatformX11())
KX11Extras::setState(winId(), NET::SkipTaskbar | NET::SkipPager | NET::SkipSwitcher);
HistoryModel::self()->updateTime();
} else if (event->type() == QEvent::Hide) {
setMouseGrabEnabled(false);

@ -2,18 +2,6 @@ project(cutefish-powerman)
set(TARGET cutefish-powerman)
find_package(KF6IdleTime)
find_package(X11)
find_package(XCB
REQUIRED COMPONENTS
XCB
DPMS
)
set(XCB_LIBS
XCB::XCB
XCB::DPMS
)
set(SOURCES
main.cpp
application.cpp
@ -43,8 +31,6 @@ target_link_libraries(${TARGET}
Qt6::DBus
KF6::IdleTime
${XCB_LIBS}
${X11_LIBRARIES}
)
install(TARGETS ${TARGET} DESTINATION ${CMAKE_INSTALL_BINDIR})

@ -24,10 +24,6 @@
#include <QDBusPendingCall>
#include <QProcess>
#include <QDebug>
#include <QtGui/qguiapplication_platform.h>
#include <X11/Xlib.h>
#include <xcb/dpms.h>
DimDisplayAction::DimDisplayAction(QObject *parent)
: Action(parent)
@ -35,12 +31,6 @@ DimDisplayAction::DimDisplayAction(QObject *parent)
"/Brightness",
"com.cutefish.Brightness", QDBusConnection::sessionBus())
{
if (qGuiApp->nativeInterface<QNativeInterface::QX11Application>()) {
// Disable a default timeout, if any
xcb_dpms_set_timeouts(qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->connection(), 0, 0, 0);
XSetScreenSaver(qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->display(), 0, 0, 0, 0);
}
}
void DimDisplayAction::onWakeupFromIdle()

@ -3,8 +3,8 @@
"http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
<policyconfig>
<action id="com.cutefish.sddm.helper.pkexec">
<message>Authentication is required to SDDM DPI</message>
<icon_name>battery</icon_name>
<message>Authentication is required to change the SDDM scale factor</message>
<icon_name>preferences-desktop-display</icon_name>
<defaults>
<allow_any>no</allow_any>
<allow_inactive>no</allow_inactive>

@ -30,18 +30,17 @@ int main(int argc, char *argv[])
parser.addHelpOption();
parser.addVersionOption();
QCommandLineOption dpiOption(QStringLiteral("dpi"), QStringLiteral("Set DPI"));
parser.addOption(dpiOption);
parser.addPositionalArgument("value", "Value");
QCommandLineOption scaleOption(QStringLiteral("scale"), QStringLiteral("Set greeter scale factor"),
QStringLiteral("factor"));
parser.addOption(scaleOption);
parser.process(app);
if (parser.isSet(dpiOption)) {
int value = parser.positionalArguments().value(0).toInt();
int minValue = 96;
if (value < minValue)
value = minValue;
if (parser.isSet(scaleOption)) {
bool ok = false;
const qreal value = parser.value(scaleOption).toDouble(&ok);
if (!ok || value < 1.0 || value > 4.0)
return 1;
QDir dir("/etc/sddm.conf.d");
@ -49,9 +48,10 @@ int main(int argc, char *argv[])
dir.mkpath("/etc/sddm.conf.d");
}
QSettings settings("/etc/sddm.conf.d/dpi.conf", QSettings::IniFormat);
settings.beginGroup("X11");
settings.setValue("ServerArguments", QString("-nolisten tcp -dpi %1").arg(value));
QSettings settings("/etc/sddm.conf.d/scale.conf", QSettings::IniFormat);
settings.beginGroup("General");
settings.setValue("GreeterEnvironment",
QStringLiteral("QT_SCALE_FACTOR=%1").arg(value, 0, 'g', 3));
}
return 0;

@ -18,8 +18,6 @@ qt6_add_dbus_adaptor(DBUS_SOURCES
sessionadaptor SessionAdaptor)
set_source_files_properties(${DBUS_SOURCES} PROPERTIES SKIP_AUTOGEN ON)
find_package(KF6WindowSystem)
add_executable(${TARGET} ${SOURCES} ${DBUS_SOURCES})
target_link_libraries(${TARGET}
Qt6::Core
@ -27,11 +25,9 @@ target_link_libraries(${TARGET}
Qt6::Widgets
Qt6::Quick
Qt6::DBus
KF6::WindowSystem
)
install(TARGETS ${TARGET} DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES cutefish-xsession.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/xsessions/)
install(PROGRAMS cutefish-kwin-wayland-session cutefish-wayland-session DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES cutefish-applications.menu DESTINATION /etc/xdg/menus/)
install(FILES cutefish-wayland.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/wayland-sessions/)

@ -32,8 +32,6 @@
#include <QDBusConnectionInterface>
#include <QDBusServiceWatcher>
#include <KWindowSystem>
// STL
#include <optional>
@ -71,8 +69,7 @@ bool isShellVariable(const QByteArray &name)
bool isSessionVariable(const QByteArray &name)
{
// Check is variable is specific to session.
return name == "DISPLAY" || name == "XAUTHORITY" || //
name == "WAYLAND_DISPLAY" || name == "WAYLAND_SOCKET" || //
return name == "WAYLAND_DISPLAY" || name == "WAYLAND_SOCKET" || //
name.startsWith("XDG_");
}
@ -87,8 +84,6 @@ Application::Application(int &argc, char **argv)
: QApplication(argc, argv)
, m_processManager(new ProcessManager(this))
, m_networkProxyManager(new NetworkProxyManager)
, m_wayland(false)
, m_startWindowManager(true)
{
new SessionAdaptor(this);
@ -100,22 +95,11 @@ Application::Application(int &argc, char **argv)
parser.setApplicationDescription(QStringLiteral("Cutefish Session"));
parser.addHelpOption();
QCommandLineOption waylandOption(QStringList() << "w" << "wayland" << "Wayland Mode");
parser.addOption(waylandOption);
QCommandLineOption noWindowManagerOption(QStringList() << "no-wm" << "Do not start a window manager");
parser.addOption(noWindowManagerOption);
parser.process(*this);
m_wayland = parser.isSet(waylandOption) || QGuiApplication::platformName().contains(QStringLiteral("wayland"));
m_startWindowManager = !parser.isSet(noWindowManagerOption);
createConfigDirectory();
if (!m_wayland)
initKWinConfig();
initLanguage();
initScreenScaleFactors();
if (!m_wayland)
initXResource();
initEnvironments();
@ -131,26 +115,12 @@ Application::Application(int &argc, char **argv)
// ref plasma
importSystemdEnvrionment();
qunsetenv("XCURSOR_THEME");
qunsetenv("XCURSOR_SIZE");
qunsetenv("SESSION_MANAGER");
m_networkProxyManager->update();
QTimer::singleShot(50, this, &Application::updateUserDirs);
QTimer::singleShot(100, m_processManager, &ProcessManager::start);
}
bool Application::wayland() const
{
return m_wayland;
}
bool Application::startWindowManager() const
{
return m_startWindowManager;
}
void Application::launch(const QString &exec, const QStringList &args)
{
QProcess process;
@ -198,6 +168,7 @@ void Application::initEnvironments()
qputenv("XDG_SESSION_DESKTOP", "Cutefish");
// Qt
qputenv("QT_QPA_PLATFORM", "wayland");
qputenv("QT_QPA_PLATFORMTHEME", "cutefish");
qputenv("QT_PLATFORM_PLUGIN", "cutefish");
@ -277,76 +248,6 @@ void Application::initScreenScaleFactors()
}
}
void Application::initXResource()
{
QSettings settings(QSettings::UserScope, "cutefishos", "theme");
qreal scaleFactor = settings.value("PixelRatio", 1.0).toReal();
int fontDpi = 96 * scaleFactor;
QString cursorTheme = settings.value("CursorTheme", "default").toString();
int cursorSize = settings.value("CursorSize", 24).toInt() * scaleFactor;
int xftAntialias = settings.value("XftAntialias", 1).toBool();
QString xftHintStyle = settings.value("XftHintStyle", "hintslight").toString();
const QString datas = QString("Xft.dpi: %1\n"
"Xcursor.theme: %2\n"
"Xcursor.size: %3\n"
"Xft.antialias: %4\n"
"Xft.hintstyle: %5\n"
"Xft.rgba: rgb")
.arg(fontDpi)
.arg(cursorTheme)
.arg(cursorSize)
.arg(xftAntialias)
.arg(xftHintStyle);
QProcess p;
p.start(QStringLiteral("xrdb"), {QStringLiteral("-quiet"), QStringLiteral("-merge"), QStringLiteral("-nocpp")});
p.setProcessChannelMode(QProcess::ForwardedChannels);
p.write(datas.toLatin1());
p.closeWriteChannel();
p.waitForFinished(-1);
// For cutefish-wine
qputenv("CUTEFISH_FONT_DPI", QByteArray::number(fontDpi));
// Init cursor
runSync("cupdatecursor", {cursorTheme, QString::number(cursorSize)});
// qputenv("XCURSOR_THEME", cursorTheme.toLatin1());
// qputenv("XCURSOR_SIZE", QByteArray::number(cursorSize * scaleFactor));
}
void Application::initKWinConfig()
{
QSettings settings(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + "/kwinrc",
QSettings::IniFormat);
settings.beginGroup("Effect-Blur");
settings.setValue("BlurStrength", 10);
settings.setValue("NoiseStrength", 0);
settings.endGroup();
settings.beginGroup("Windows");
settings.setValue("FocusStealingPreventionLevel", 0);
settings.setValue("HideUtilityWindowsForInactive", false);
settings.setValue("BorderlessMaximizedWindows", false);
settings.setValue("Placement", "Centered");
settings.endGroup();
settings.beginGroup("org.kde.kdecoration2");
settings.setValue("BorderSize", "Normal");
settings.setValue("ButtonsOnLeft", "");
settings.setValue("ButtonsOnRight", "HIAX");
// The Cutefish decoration was written for the old KDecoration2 API and
// is not available on current KWin installations. Keeping this stale
// value makes KWin try to load a plugin that may no longer exist and can
// leave the session without a usable window manager. Let KWin select
// the system default decoration instead (normally Breeze).
if (settings.value("library").toString() == "org.cutefish.decoration")
settings.remove("library");
settings.setValue("theme", "");
settings.endGroup();
}
bool Application::syncDBusEnvironment()
{
int exitCode = 0;

@ -33,9 +33,6 @@ class Application : public QApplication
public:
explicit Application(int &argc, char **argv);
bool wayland() const;
bool startWindowManager() const;
public slots:
void logout() {
m_processManager->logout();
@ -70,8 +67,6 @@ private:
void initEnvironments();
void initLanguage();
void initScreenScaleFactors();
void initXResource();
void initKWinConfig();
bool syncDBusEnvironment();
void importSystemdEnvrionment();
void createConfigDirectory();
@ -83,8 +78,6 @@ private:
NetworkProxyManager *m_networkProxyManager;
Power m_power;
bool m_wayland;
bool m_startWindowManager;
};
#endif // APPLICATION_H

@ -1,3 +1,3 @@
#!/bin/sh
exec /usr/bin/cutefish-session --wayland --no-wm "$@"
exec /usr/bin/cutefish-session "$@"

@ -14,4 +14,4 @@ export XDG_CURRENT_DESKTOP=Cutefish
export XDG_SESSION_DESKTOP=Cutefish
export DESKTOP_SESSION=Cutefish
exec /usr/bin/kwin_wayland --xwayland --exit-with-session /usr/bin/cutefish-kwin-wayland-session "$@"
exec /usr/bin/kwin_wayland --exit-with-session /usr/bin/cutefish-kwin-wayland-session "$@"

@ -1,7 +0,0 @@
[Desktop Entry]
Type=Application
Exec=cutefish-session
TryExec=cutefish-session
Name=Cutefish Desktop
Keywords=session
Comment=session

@ -22,7 +22,6 @@
int main(int argc, char *argv[])
{
// putenv((char *)"SESSION_MANAGER=");
QQuickWindow::setDefaultAlphaBuffer(true);

@ -30,27 +30,17 @@
#include <QTimer>
#include <QThread>
#include <QDir>
#include <QtGui/qguiapplication_platform.h>
#include <QDBusInterface>
#include <QDBusPendingCall>
#include <KWindowSystem>
#include <netwm.h>
ProcessManager::ProcessManager(Application *app, QObject *parent)
: QObject(parent)
, m_app(app)
, m_wmStarted(false)
, m_waitLoop(nullptr)
{
qApp->installNativeEventFilter(this);
}
ProcessManager::~ProcessManager()
{
qApp->removeNativeEventFilter(this);
QMapIterator<QString, QProcess *> i(m_systemProcess);
while (i.hasNext()) {
i.next();
@ -62,29 +52,13 @@ ProcessManager::~ProcessManager()
void ProcessManager::start()
{
if (m_app->startWindowManager())
startWindowManager();
// The settings daemon owns the Cutefish settings D-Bus service and
// starts the desktop components after that service is ready. This is
// required for both X11 and Wayland sessions.
// starts the desktop components after that service is ready.
startDaemonProcess();
}
void ProcessManager::logout()
{
if (!m_app->wayland()) {
QDBusInterface kwinIface("org.kde.KWin",
"/Session",
"org.kde.KWin.Session",
QDBusConnection::sessionBus());
if (kwinIface.isValid()) {
kwinIface.call("aboutToSaveSession", "cutefish");
kwinIface.call("setState", uint(1)); // Quitting
}
}
// Close what we started ourselves, the window manager last since
// everything else is drawn on top of it.
stopProcesses(m_autoStartProcess);
@ -113,28 +87,6 @@ void ProcessManager::stopProcesses(QMap<QString, QProcess *> &processes)
}
}
void ProcessManager::startWindowManager()
{
if (m_app->wayland()) {
qInfo() << "Using the existing Wayland compositor.";
return;
}
QProcess *wmProcess = new QProcess;
m_systemProcess.insert("kwin", wmProcess);
wmProcess->start("kwin_x11", QStringList());
if (!m_app->wayland()) {
QEventLoop waitLoop;
m_waitLoop = &waitLoop;
// add a timeout to avoid infinite blocking if a WM fail to execute.
QTimer::singleShot(30 * 1000, &waitLoop, SLOT(quit()));
waitLoop.exec();
m_waitLoop = nullptr;
}
}
void ProcessManager::startDesktopProcess()
{
// When the cutefish-settings-daemon theme module is loaded, start the desktop.
@ -165,11 +117,9 @@ void ProcessManager::startDesktopProcess()
process->setProcessChannelMode(QProcess::ForwardedChannels);
process->setProgram(pair.first);
process->setArguments(pair.second);
if (m_app->wayland()) {
QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
environment.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("wayland"));
process->setProcessEnvironment(environment);
}
QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
environment.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("wayland"));
process->setProcessEnvironment(environment);
process->start();
process->waitForStarted();
@ -196,12 +146,6 @@ void ProcessManager::startDaemonProcess()
// the desktop components once the service is available.
list << qMakePair(QString("cutefish-settings-daemon"), QStringList());
if (!m_app->wayland()) {
list << qMakePair(QString("cutefish-xembedsniproxy"), QStringList());
list << qMakePair(QString("cutefish-gmenuproxy"), QStringList());
list << qMakePair(QString("chotkeys"), QStringList());
}
for (QPair<QString, QStringList> pair : list) {
QProcess *process = new QProcess;
process->setProcessChannelMode(QProcess::ForwardedChannels);
@ -242,19 +186,7 @@ void ProcessManager::loadAutoStartProcess()
const QString execValue = desktop.value("Exec").toString();
// 避免冲突
if (execValue.contains("gmenudbusmenuproxy"))
continue;
// These helpers are X11-only and are deliberately replaced by
// Wayland integration or omitted from the Wayland session. Do
// not let a legacy autostart entry bring them back after
// startDaemonProcess() skipped them.
if (m_app->wayland()
&& (execValue.contains("cutefish-settings-daemon")
|| execValue.contains("cutefish-xembedsniproxy")
|| execValue.contains("cutefish-gmenuproxy")
|| execValue.contains("chotkeys")))
if (execValue.contains("cutefish-settings-daemon"))
continue;
if (!execValue.isEmpty()) {
@ -266,11 +198,9 @@ void ProcessManager::loadAutoStartProcess()
for (const QString &exec : execList) {
QProcess *process = new QProcess;
process->setProgram(exec);
if (m_app->wayland()) {
QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
environment.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("wayland"));
process->setProcessEnvironment(environment);
}
QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
environment.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("wayland"));
process->setProcessEnvironment(environment);
process->start();
process->waitForStarted();
@ -281,27 +211,3 @@ void ProcessManager::loadAutoStartProcess()
}
}
}
bool ProcessManager::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result)
{
if (!KWindowSystem::isPlatformX11())
return false;
if (eventType != "xcb_generic_event_t") // We only want to handle XCB events
return false;
// ref: lxqt session
if (!m_wmStarted && m_waitLoop) {
// all window managers must set their name according to the spec
if (!QString::fromUtf8(NETRootInfo(qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->connection(), NET::SupportingWMCheck).wmName()).isEmpty()) {
qDebug() << "Window manager started";
m_wmStarted = true;
if (m_waitLoop && m_waitLoop->isRunning())
m_waitLoop->exit();
qApp->removeNativeEventFilter(this);
}
}
return false;
}

@ -20,14 +20,13 @@
#ifndef PROCESSMANAGER_H
#define PROCESSMANAGER_H
#include <QAbstractNativeEventFilter>
#include <QObject>
#include <QProcess>
#include <QEventLoop>
#include <QMap>
class Application;
class ProcessManager : public QObject, public QAbstractNativeEventFilter
class ProcessManager : public QObject
{
Q_OBJECT
@ -38,13 +37,10 @@ public:
void start();
void logout();
void startWindowManager();
void startDesktopProcess();
void startDaemonProcess();
void loadAutoStartProcess();
bool nativeEventFilter(const QByteArray & eventType, void * message, qintptr * result) override;
private:
void stopProcesses(QMap<QString, QProcess *> &processes);
@ -53,8 +49,6 @@ private:
QMap<QString, QProcess *> m_systemProcess;
QMap<QString, QProcess *> m_autoStartProcess;
bool m_wmStarted;
QEventLoop *m_waitLoop;
};
#endif // PROCESSMANAGER_H

@ -1,14 +1,6 @@
project(cutefish-settings-daemon)
set(TARGET cutefish-settings-daemon)
find_package(X11 REQUIRED)
find_package(X11_XCB REQUIRED)
find_package(XCB REQUIRED COMPONENTS XCB RECORD)
pkg_check_modules(XORGLIBINPUT xorg-libinput IMPORTED_TARGET)
pkg_check_modules(XORGSERVER xorg-server IMPORTED_TARGET)
pkg_check_modules(SYNAPTICS xorg-synaptics IMPORTED_TARGET GLOBAL)
file (GLOB_RECURSE SRCS
"*.cpp"
"*.h"
@ -27,8 +19,6 @@ file (GLOB_RECURSE SRCS
"mouse/*.cpp"
"touchpad/*.h"
"touchpad/*.cpp"
"touchpad/x11/*.h"
"touchpad/x11/*.cpp"
)
set(SOURCES ${SRCS})
@ -66,15 +56,6 @@ target_link_libraries(${TARGET}
Qt6::Widgets
Qt6::DBus
Qt6::Xml
${X11_LIBRARIES}
X11::X11
X11::Xi
X11::XCB
XCB::XCB
XCB::RECORD
PkgConfig::XORGLIBINPUT
PkgConfig::XORGSERVER
PkgConfig::SYNAPTICS
)
file(GLOB TS_FILES translations/*.ts)

@ -42,31 +42,16 @@ Application::Application(int &argc, char **argv)
, m_brightnessManager(new BrightnessManager(this))
, m_upowerManager(new UPowerManager(this))
, m_language(Language::self())
, m_mouse(nullptr)
, m_touchpad(nullptr)
, m_mouse(new Mouse(this))
, m_touchpad(new TouchpadManager(this))
, m_defaultApps(new DefaultApplications)
// , m_kwinTimer(new QTimer(this))
{
// Mouse and touchpad managers use the X11/XInput backend. Constructing
// them on a Wayland Qt platform dereferences a missing QX11 interface
// and makes the settings daemon exit before it can register its D-Bus
// service.
if (QGuiApplication::platformName() == QStringLiteral("xcb")) {
m_mouse = new Mouse(this);
m_touchpad = new TouchpadManager(this);
}
initTrash();
new DBusAdaptor(this);
// connect to D-Bus and register as an object:
QDBusConnection::sessionBus().registerService(QStringLiteral("com.cutefish.Settings"));
// m_kwinTimer->setSingleShot(false);
// m_kwinTimer->setInterval(50);
// connect(m_kwinTimer, &QTimer::timeout, this, &Application::initKWin);
// m_kwinTimer->start();
// Translations
QLocale locale;
QString qmFilePath = QString("%1/%2.qm").arg("/usr/share/cutefish-settings-daemon/translations/").arg(locale.name());
@ -121,28 +106,3 @@ void Application::initTrash()
::rmdir(trashDir);
}
}
//void Application::initKWin()
//{
// QDBusInterface effect("org.kde.KWin", "/Effects", "org.kde.kwin.Effects",
// QDBusConnection::sessionBus());
// if (effect.isValid() && !effect.lastError().isValid()) {
// // KWin
// effect.call("loadEffect", "kwin4_effect_dialogparent");
// effect.call("unloadEffect", "kwin4_effect_fadingpopups");
// effect.call("unloadEffect", "kwin4_effect_fade");
// effect.call("unloadEffect", "kwin4_effect_scale");
// effect.call("unloadEffect", "kwin4_effect_grayscale");
// effect.call("unloadEffect", "kwin4_effect_squash");
// effect.call("unloadEffect", "kwin4_effect_translucency");
// effect.call("unloadEffect", "magiclamp");
// effect.call("loadEffect", "cutefish_popups");
// effect.call("loadEffect", "cutefish_scale");
// effect.call("loadEffect", "cutefish_squash");
// m_kwinTimer->stop();
// }
//}

@ -41,8 +41,6 @@ public:
void invokeDesktopProcess();
void initTrash();
// void initKWin();
private:
ThemeManager *m_themeManager;
BrightnessManager *m_brightnessManager;
@ -51,8 +49,6 @@ private:
Mouse *m_mouse;
TouchpadManager *m_touchpad;
DefaultApplications *m_defaultApps;
// QTimer *m_kwinTimer;
};
#endif // APPLICATION_H

@ -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,6 +1,9 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="com.cutefish.Mouse">
<property name="available" type="b" access="read"/>
<signal name="availableChanged"></signal>
<method name="setLeftHanded"><arg name="value" type="b" direction="in"/></method>
<property name="leftHanded" type="b" access="read"/>
<signal name="leftHandedChanged"></signal>
@ -12,5 +15,9 @@
<method name="setNaturalScroll"><arg name="value" type="b" direction="in"/></method>
<property name="naturalScroll" type="b" access="read"/>
<signal name="naturalScrollChanged"></signal>
<method name="setAcceleration"><arg name="value" type="b" direction="in"/></method>
<property name="acceleration" type="b" access="read"/>
<signal name="accelerationChanged"></signal>
</interface>
</node>

@ -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();
}

@ -21,11 +21,13 @@
#define MOUSE_H
#include <QObject>
#include "x11libinputdummydevice.h"
class KWinInputBackend;
class Mouse : public QObject
{
Q_OBJECT
Q_PROPERTY(bool available READ available NOTIFY availableChanged)
Q_PROPERTY(bool leftHanded READ leftHanded WRITE setLeftHanded NOTIFY leftHandedChanged)
Q_PROPERTY(bool acceleration READ acceleration WRITE setAcceleration NOTIFY accelerationChanged)
Q_PROPERTY(bool naturalScroll READ naturalScroll WRITE setNaturalScroll NOTIFY naturalScrollChanged)
@ -33,8 +35,9 @@ class Mouse : public QObject
public:
explicit Mouse(QObject *parent = nullptr);
~Mouse();
~Mouse() override;
bool available() const;
bool leftHanded() const;
void setLeftHanded(bool enabled);
@ -48,13 +51,14 @@ public:
void setPointerAcceleration(qreal value);
signals:
void availableChanged();
void leftHandedChanged();
void accelerationChanged();
void naturalScrollChanged();
void pointerAccelerationChanged();
private:
X11LibinputDummyDevice *m_inputDummydevice;
KWinInputBackend *m_backend;
};
#endif // MOUSE_H

@ -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

@ -44,7 +44,7 @@
<arg name="type" type="s" direction="in"/>
</method>
<method name="applyXResources"></method>
<method name="applyFontSettings"></method>
<method name="updateFontConfig"></method>
<property name="isDarkMode" type="b" access="read"/>

@ -78,8 +78,8 @@ ThemeManager::ThemeManager(QObject *parent)
m_cursorSize = m_settings->value("CursorSize", 24).toInt();
m_iconTheme = m_settings->value("IconTheme", "Crule").toString();
// Start the DE and need to update the settings again.
updateGtk3Config();
// Synchronize toolkit and compositor cursor settings at session startup.
applyCursorSettings();
// Init fonts.
if (!m_settings->contains(s_systemFixedFontName)) {
@ -186,8 +186,7 @@ void ThemeManager::setCursorTheme(const QString &theme)
if (m_cursorTheme != theme) {
m_cursorTheme = theme;
m_settings->setValue("CursorTheme", m_cursorTheme);
applyXResources();
applyCursor();
applyCursorSettings();
emit cursorThemeChanged();
}
}
@ -202,8 +201,7 @@ void ThemeManager::setCursorSize(int size)
if (m_cursorSize != size) {
m_cursorSize = size;
m_settings->setValue("CursorSize", m_cursorSize);
applyXResources();
applyCursor();
applyCursorSettings();
emit cursorSizeChanged();
}
}
@ -258,17 +256,16 @@ qreal ThemeManager::devicePixelRatio()
void ThemeManager::setDevicePixelRatio(qreal ratio)
{
int fontDpi = qRound(ratio * 96.0);
ratio = qBound<qreal>(1.0, ratio, 4.0);
m_settings->setValue(s_devicePixelRatio, ratio);
m_settings->setValue("forceFontDPI", fontDpi);
m_settings->sync();
applyXResources();
updateGtk3Config();
applyCursorSettings();
// SDDM
QProcess p;
p.setProgram("pkexec");
p.setArguments(QStringList() << "cutefish-sddm-helper"
<< "--dpi" << QString::number(fontDpi));
<< "--scale" << QString::number(ratio));
p.start();
p.waitForFinished(-1);
@ -344,6 +341,8 @@ void ThemeManager::updateGtk3Config()
settings.setValue("gtk-application-prefer-dark-theme", isDarkMode());
// icon theme
settings.setValue("gtk-icon-theme-name", m_iconTheme);
settings.setValue("gtk-cursor-theme-name", m_cursorTheme);
settings.setValue("gtk-cursor-theme-size", qRound(m_cursorSize * devicePixelRatio()));
// other
settings.setValue("gtk-enable-animations", true);
// theme
@ -351,48 +350,40 @@ void ThemeManager::updateGtk3Config()
settings.sync();
}
void ThemeManager::applyXResources()
void ThemeManager::applyFontSettings()
{
m_settings->sync();
qreal scaleFactor = this->devicePixelRatio();
int fontDpi = 96 * scaleFactor;
int xftAntialias = m_settings->value("XftAntialias", 1).toBool();
QString xftHintStyle = m_settings->value("XftHintStyle", "hintfull").toString();
const QString datas = QString("Xft.dpi: %1\n"
"Xcursor.theme: %2\n"
"Xcursor.size: %3\n"
"Xft.antialias: %4\n"
"Xft.hintstyle: %5")
.arg(fontDpi)
.arg(m_cursorTheme)
.arg(m_cursorSize * scaleFactor)
.arg(xftAntialias)
.arg(xftHintStyle);
QProcess p;
p.start(QStringLiteral("xrdb"), {QStringLiteral("-quiet"), QStringLiteral("-merge"), QStringLiteral("-nocpp")});
p.setProcessChannelMode(QProcess::ForwardedChannels);
p.write(datas.toLatin1());
p.closeWriteChannel();
p.waitForFinished(-1);
updateFontConfig();
updateGtk3Config();
}
void ThemeManager::applyCursor()
void ThemeManager::applyCursorSettings()
{
QProcess p;
p.start("cupdatecursor", QStringList() << cursorTheme() << QString::number(cursorSize() * devicePixelRatio()));
p.waitForFinished(-1);
m_settings->sync();
QSettings inputSettings(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation)
+ QStringLiteral("/kcminputrc"),
QSettings::IniFormat);
inputSettings.beginGroup(QStringLiteral("Mouse"));
inputSettings.setValue(QStringLiteral("cursorTheme"), cursorTheme());
inputSettings.setValue(QStringLiteral("cursorSize"),
qRound(cursorSize() * devicePixelRatio()));
inputSettings.endGroup();
inputSettings.sync();
updateGtk3Config();
QDBusMessage message = QDBusMessage::createSignal("/KGlobalSettings",
"org.kde.KGlobalSettings",
"notifyChange");
// ChangeCursor
message << 5;
message << 0;
QDBusConnection::sessionBus().send(message);
QDBusInterface kwin(QStringLiteral("org.kde.KWin"), QStringLiteral("/KWin"),
QStringLiteral("org.kde.KWin"), QDBusConnection::sessionBus());
if (kwin.isValid())
kwin.asyncCall(QStringLiteral("reconfigure"));
}
void ThemeManager::updateFontConfig()
@ -412,8 +403,8 @@ void ThemeManager::updateFontConfig()
const QString familyFallback = familyFont;
QSettings settings(QSettings::UserScope, "cutefishos", "theme");
bool hinting = settings.value("XftAntialias", 1).toBool();
QString hintStyle = settings.value("XftHintStyle", "hintslight").toString();
bool hinting = settings.value("FontAntialias", true).toBool();
QString hintStyle = settings.value("FontHintStyle", "hintslight").toString();
QString content = QString("<?xml version=\"1.0\"?>"
"<!DOCTYPE fontconfig SYSTEM \"fonts.dtd\">"

@ -86,8 +86,8 @@ public:
void updateGtk2Config();
void updateGtk3Config();
void applyXResources();
void applyCursor();
void applyFontSettings();
void applyCursorSettings();
QString iconTheme() const;
void setIconTheme(const QString &iconTheme);

@ -7,6 +7,12 @@
<property name="naturalScroll" type="b" access="read"/>
<property name="pointerAcceleration" type="d" access="read"/>
<signal name="availableChanged"/>
<signal name="enabledChanged"/>
<signal name="tapToClickChanged"/>
<signal name="naturalScrollChanged"/>
<signal name="pointerAccelerationChanged"/>
<method name="setEnabled">
<arg name="enabled" type="b" direction="in"/>
</method>

@ -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();
}

@ -2,16 +2,17 @@
#define TOUCHPADMANAGER_H
#include <QObject>
#include "x11/xlibbackend.h"
class KWinInputBackend;
class TouchpadManager : public QObject
{
Q_OBJECT
Q_PROPERTY(bool available READ available CONSTANT)
Q_PROPERTY(bool enabled READ enabled WRITE setEnabled CONSTANT)
Q_PROPERTY(bool tapToClick READ tapToClick WRITE setTapToClick CONSTANT)
Q_PROPERTY(bool naturalScroll READ naturalScroll WRITE setNaturalScroll CONSTANT)
Q_PROPERTY(qreal pointerAcceleration READ pointerAcceleration WRITE setPointerAcceleration CONSTANT)
Q_PROPERTY(bool available READ available NOTIFY availableChanged)
Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
Q_PROPERTY(bool tapToClick READ tapToClick WRITE setTapToClick NOTIFY tapToClickChanged)
Q_PROPERTY(bool naturalScroll READ naturalScroll WRITE setNaturalScroll NOTIFY naturalScrollChanged)
Q_PROPERTY(qreal pointerAcceleration READ pointerAcceleration WRITE setPointerAcceleration NOTIFY pointerAccelerationChanged)
public:
explicit TouchpadManager(QObject *parent = nullptr);
@ -30,8 +31,15 @@ public:
qreal pointerAcceleration() const;
void setPointerAcceleration(qreal value);
signals:
void availableChanged();
void enabledChanged();
void tapToClickChanged();
void naturalScrollChanged();
void pointerAccelerationChanged();
private:
XlibBackend *m_backend;
KWinInputBackend *m_backend;
};
#endif // TOUCHPADMANAGER_H

@ -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

@ -32,7 +32,7 @@ ApplicationWindow {
height: Screen.height
visible: true
visibility: Window.FullScreen
flags: Qt.FramelessWindowHint | Qt.X11BypassWindowManagerHint
flags: Qt.FramelessWindowHint
id: root
function exit() {

@ -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…
Cancel
Save