Skip to content

Commit 7ec8727

Browse files
committed
Qt on Wayland
1 parent 0ff5092 commit 7ec8727

31 files changed

+942
-164
lines changed

.github/workflows/ci.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,13 @@ jobs:
276276
enable: INDICATOR,GNOME45,GNOME_CLASSIC_PANEL,GSTREAMER,PULSE,TESTS
277277
disable: COVERAGE,TRACING
278278

279+
- image: ubuntu-plucky
280+
os: ubuntu-24.04
281+
compiler: gcc
282+
ui: Qt
283+
enable: INDICATOR,GNOME45,GNOME_CLASSIC_PANEL,GSTREAMER,PULSE,TESTS
284+
disable: COVERAGE,TRACING
285+
279286
# - image: mingw-fedora-rawhide
280287
# os: ubuntu-24.04
281288
# compiler: clang

CMakeLists.txt

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,8 @@ if ("${WITH_UI}" STREQUAL "Gtk+3")
200200
set (WITH_UI_GTK TRUE)
201201
endif()
202202

203+
option(WITH_WAYLAND "Enable Wayland support" ON)
204+
203205
#----------------------------------------------------------------------------------------------------
204206
# Locations
205207

@@ -305,6 +307,16 @@ endif()
305307
#----------------------------------------------------------------------------------------------------
306308
# GTK
307309

310+
pkg_check_modules(GTK3 gtk+-3.0>=3.24.0)
311+
if (GTK3_FOUND)
312+
set (HAVE_GTK3 ON)
313+
endif()
314+
315+
pkg_check_modules(GTK4 gtk4>=4.10.0)
316+
if (GTK4_FOUND)
317+
set (HAVE_GTK4 ON)
318+
endif()
319+
308320
if ("${WITH_UI}" STREQUAL "Gtk+3")
309321
message(STATUS "Checking for: Gtk 3")
310322

@@ -368,10 +380,22 @@ endif()
368380
# Qt
369381

370382
if ("${WITH_UI}" STREQUAL "Qt")
371-
message(STATUS "Checking for: Qt")
372383

384+
set(EXTRA_QT_COMPONENTS "")
385+
message(STATUS "Checking for Wayland ${WITH_WAYLAND}")
386+
if (WITH_WAYLAND)
387+
find_package(ECM 5.68.0 NO_MODULE)
388+
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH})
389+
390+
list(APPEND EXTRA_QT_COMPONENTS WaylandClient)
391+
message(STATUS "Checking for: Wayland Client")
392+
find_package(Wayland COMPONENTS Client)
393+
set(HAVE_WAYLAND ON)
394+
endif()
395+
396+
message(STATUS "Checking for: Qt")
373397
find_package(QT NAMES Qt6 COMPONENTS Core REQUIRED)
374-
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets Core Gui Svg LinguistTools Xml REQUIRED)
398+
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets Core Gui Svg LinguistTools Xml WaylandClient ${EXTRA_QT_COMPONENTS} REQUIRED)
375399
qt6_standard_project_setup()
376400
set(CMAKE_AUTORCC ON)
377401

@@ -539,10 +563,6 @@ if (UNIX AND NOT APPLE)
539563
option(WITH_INDICATOR "Enable Ayatana indicator support" ON)
540564
option(LOCALINSTALL "Install file locally instead of to system location" OFF)
541565

542-
pkg_check_modules(
543-
GTK3
544-
gtk+-3.0>=3.10.0)
545-
546566
if (WITH_INDICATOR AND HAVE_DBUSMENU)
547567
message(STATUS "Checking for: Ayatana Indicators")
548568

@@ -714,9 +734,7 @@ endif()
714734
#----------------------------------------------------------------------------------------------------
715735
# Wayland
716736

717-
option(WITH_WAYLAND "Enable Wayland support" ON)
718-
719-
if (UNIX AND NOT APPLE)
737+
if (UNIX AND (NOT APPLE) AND WITH_UI_GTK)
720738
if (WITH_WAYLAND)
721739
message(STATUS "Checking for: Wayland")
722740

libs/config/src/QtSettingsConfigurator.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ QtSettingsConfigurator::set_value(const std::string &key, const ConfigValue &val
115115
}
116116
else if constexpr (!std::is_same_v<std::monostate, T>)
117117
{
118-
QVariant qval = value;
118+
QVariant qval = QVariant::fromValue(value);
119119
settings.setValue(qkey, qval);
120120
}
121121
},

libs/input-monitor/src/CMakeLists.txt

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,26 @@ if (PLATFORM_OS_UNIX)
2020
endif()
2121

2222
if (HAVE_WAYLAND)
23-
add_custom_command(
24-
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/ext-idle-notify-v1.c ${CMAKE_CURRENT_BINARY_DIR}/ext-idle-notify-v1-client.h
25-
COMMAND ${WAYLAND_SCANNER} private-code ${CMAKE_CURRENT_SOURCE_DIR}/unix/protocols/ext-idle-notify-v1.xml ${CMAKE_CURRENT_BINARY_DIR}/ext-idle-notify-v1.c
26-
COMMAND ${WAYLAND_SCANNER} client-header ${CMAKE_CURRENT_SOURCE_DIR}/unix/protocols/ext-idle-notify-v1.xml ${CMAKE_CURRENT_BINARY_DIR}/ext-idle-notify-v1-client.h
27-
DEPENDS unix/protocols/ext-idle-notify-v1.xml
28-
VERBATIM
29-
)
30-
target_sources(workrave-libs-input-monitor PRIVATE
31-
unix/WaylandInputMonitor.cc
32-
${CMAKE_CURRENT_BINARY_DIR}/ext-idle-notify-v1.c
23+
if (HAVE_APP_GTK)
24+
add_custom_command(
25+
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/ext-idle-notify-v1.c ${CMAKE_CURRENT_BINARY_DIR}/ext-idle-notify-v1-client.h
26+
COMMAND ${WAYLAND_SCANNER} private-code ${CMAKE_CURRENT_SOURCE_DIR}/unix/protocols/ext-idle-notify-v1.xml ${CMAKE_CURRENT_BINARY_DIR}/ext-idle-notify-v1.c
27+
COMMAND ${WAYLAND_SCANNER} client-header ${CMAKE_CURRENT_SOURCE_DIR}/unix/protocols/ext-idle-notify-v1.xml ${CMAKE_CURRENT_BINARY_DIR}/ext-idle-notify-v1-client.h
28+
DEPENDS unix/protocols/ext-idle-notify-v1.xml
29+
VERBATIM
3330
)
34-
target_link_libraries(workrave-libs-input-monitor ${WAYLAND_CLIENT_LIBRARIES})
31+
target_sources(workrave-libs-input-monitor PRIVATE
32+
unix/WaylandInputMonitor.cc
33+
${CMAKE_CURRENT_BINARY_DIR}/ext-idle-notify-v1.c
34+
)
35+
target_link_libraries(workrave-libs-input-monitor ${WAYLAND_CLIENT_LIBRARIES})
36+
elseif(HAVE_APP_QT)
37+
qt_generate_wayland_protocol_client_sources(workrave-libs-input-monitor
38+
FILES ${CMAKE_CURRENT_SOURCE_DIR}/unix/protocols/ext-idle-notify-v1.xml
39+
PRIVATE_CODE)
40+
target_sources(workrave-libs-input-monitor PRIVATE unix/QtWaylandInputMonitor.cc)
41+
target_link_libraries(workrave-libs-input-monitor Qt::WaylandClient Wayland::Client)
42+
endif()
3543
endif()
3644

3745
target_include_directories(workrave-libs-input-monitor PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
// Copyright (C) 2024 Rob Caelers <[email protected]>
2+
// All rights reserved.
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU General Public License
15+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
//
17+
18+
#ifdef HAVE_CONFIG_H
19+
# include "config.h"
20+
#endif
21+
22+
#include "QtWaylandInputMonitor.hh"
23+
24+
#include <memory>
25+
#include <wayland-client-protocol.h>
26+
#include <QGuiApplication>
27+
28+
#include "wayland-ext-idle-notify-v1-client-protocol.h"
29+
#include "debug.hh"
30+
31+
static const struct wl_registry_listener registry_listener = {
32+
.global = QtWaylandInputMonitor::registry_global,
33+
.global_remove = QtWaylandInputMonitor::registry_global_remove,
34+
};
35+
36+
static const struct ext_idle_notification_v1_listener idle_notification_listener = {
37+
.idled = QtWaylandInputMonitor::notification_idled,
38+
.resumed = QtWaylandInputMonitor::notification_resumed,
39+
};
40+
41+
QtWaylandInputMonitor::~QtWaylandInputMonitor()
42+
{
43+
TRACE_ENTRY();
44+
if (monitor_thread)
45+
{
46+
monitor_thread->join();
47+
}
48+
}
49+
50+
bool
51+
QtWaylandInputMonitor::init()
52+
{
53+
TRACE_ENTRY();
54+
55+
auto *app = qGuiApp->nativeInterface<QNativeInterface::QWaylandApplication>();
56+
auto *wl_display = app->display();
57+
auto *wl_seat = app->seat();
58+
59+
wl_registry = wl_display_get_registry(wl_display);
60+
61+
wl_registry_add_listener(wl_registry, &registry_listener, this);
62+
wl_display_roundtrip(wl_display);
63+
64+
if (wl_notifier == nullptr)
65+
{
66+
TRACE_MSG("ext-idle-notify-v1 protocol unsupported");
67+
return false;
68+
}
69+
70+
auto *wl_notification = ext_idle_notifier_v1_get_idle_notification(wl_notifier, timeout, wl_seat);
71+
72+
ext_idle_notification_v1_add_listener(wl_notification, &idle_notification_listener, this);
73+
monitor_thread = std::make_shared<std::thread>([this] { run(); });
74+
75+
TRACE_MSG("ext-idle-notify-v1 protocol supported");
76+
return true;
77+
}
78+
79+
void
80+
QtWaylandInputMonitor::terminate()
81+
{
82+
TRACE_ENTRY();
83+
mutex.lock();
84+
abort = true;
85+
cond.notify_all();
86+
mutex.unlock();
87+
88+
if (monitor_thread)
89+
{
90+
monitor_thread->join();
91+
}
92+
93+
if (wl_notifier != nullptr)
94+
{
95+
ext_idle_notifier_v1_destroy(wl_notifier);
96+
}
97+
wl_registry_destroy(wl_registry);
98+
}
99+
100+
void
101+
QtWaylandInputMonitor::registry_global(void *data,
102+
struct wl_registry *registry,
103+
uint32_t id,
104+
const char *interface,
105+
uint32_t version)
106+
{
107+
TRACE_ENTRY();
108+
auto *self = static_cast<QtWaylandInputMonitor *>(data);
109+
if (strcmp(ext_idle_notifier_v1_interface.name, interface) == 0)
110+
{
111+
if (self->wl_notifier != nullptr)
112+
{
113+
ext_idle_notifier_v1_destroy(self->wl_notifier);
114+
}
115+
116+
self->wl_notifier = static_cast<ext_idle_notifier_v1 *>(
117+
wl_registry_bind(self->wl_registry,
118+
id,
119+
&ext_idle_notifier_v1_interface,
120+
std::min((uint32_t)ext_idle_notifier_v1_interface.version, version)));
121+
}
122+
}
123+
124+
void
125+
QtWaylandInputMonitor::registry_global_remove(void *data, struct wl_registry *registry, uint32_t id)
126+
{
127+
}
128+
129+
void
130+
QtWaylandInputMonitor::notification_idled(void *data, struct ext_idle_notification_v1 *notification)
131+
{
132+
auto *self = static_cast<QtWaylandInputMonitor *>(data);
133+
self->idle = true;
134+
}
135+
136+
void
137+
QtWaylandInputMonitor::notification_resumed(void *data, struct ext_idle_notification_v1 *notification)
138+
{
139+
auto *self = static_cast<QtWaylandInputMonitor *>(data);
140+
self->idle = false;
141+
}
142+
143+
void
144+
QtWaylandInputMonitor::run()
145+
{
146+
TRACE_ENTRY();
147+
{
148+
std::unique_lock lock(mutex);
149+
while (!abort)
150+
{
151+
if (!idle)
152+
{
153+
fire_action();
154+
}
155+
156+
cond.wait_for(lock, std::chrono::milliseconds(timeout));
157+
}
158+
}
159+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright (C) 2024 Rob Caelers <[email protected]>
2+
// All rights reserved.
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU General Public License
15+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
//
17+
18+
#ifndef QTWAYLANDINPUTMONITOR_HH
19+
#define QTWAYLANDINPUTMONITOR_HH
20+
21+
#include "InputMonitor.hh"
22+
23+
#include <atomic>
24+
#include <condition_variable>
25+
#include <thread>
26+
27+
class QtWaylandInputMonitor : public InputMonitor
28+
{
29+
public:
30+
QtWaylandInputMonitor() = default;
31+
~QtWaylandInputMonitor() override;
32+
33+
bool init() override;
34+
void terminate() override;
35+
void run();
36+
37+
public:
38+
static void registry_global(void *data, struct wl_registry *registry, uint32_t id, const char *interface, uint32_t version);
39+
static void registry_global_remove(void *data, struct wl_registry *registry, uint32_t id);
40+
static void notification_idled(void *data, struct ext_idle_notification_v1 *notification);
41+
static void notification_resumed(void *data, struct ext_idle_notification_v1 *notification);
42+
43+
private:
44+
static constexpr int timeout = 1000;
45+
46+
private:
47+
bool abort{false};
48+
std::shared_ptr<std::thread> monitor_thread;
49+
std::mutex mutex;
50+
std::condition_variable cond;
51+
std::atomic<bool> idle{false};
52+
struct wl_registry *wl_registry{};
53+
struct ext_idle_notifier_v1 *wl_notifier{};
54+
};
55+
56+
#endif // QTWAYLANDINPUTMONITOR_HH

0 commit comments

Comments
 (0)