Krita Source Code Documentation
Loading...
Searching...
No Matches
KisWaylandSurfaceColorManager.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2025 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
8
9#include <QWindow>
10#include <QPromise>
11#include <QFuture>
12#include <QPlatformSurfaceEvent>
13
14#include <qpa/qplatformnativeinterface.h>
15#include <qpa/qplatformwindow_p.h>
16
21
22#include <kis_debug.h>
23
24namespace {
25template <typename T>
26QFuture<std::decay_t<T>> makeReadyQFuture(T &&value) {
27 QPromise<std::decay_t<T>> promise;
28 promise.start();
29 promise.addResult(std::forward<T>(value));
30 promise.finish();
31 return promise.future();
32}
33}
34
35Q_GLOBAL_STATIC(std::weak_ptr<KisWaylandAPIColorManager>, s_waylandManager)
36
37std::shared_ptr<KisWaylandAPIColorManager> KisWaylandSurfaceColorManager::getOrCreateGlobalWaylandManager()
38{
39 std::shared_ptr<KisWaylandAPIColorManager> result;
40
41 if (s_waylandManager.exists()) {
42 result = s_waylandManager->lock();
43 }
44
49 if (!result) {
50 result.reset(new KisWaylandAPIColorManager());
51 *s_waylandManager = result;
52 }
53
54 return result;
55}
56
71
75
83
85{
86 Q_OBJECT
87public:
88 PlatformWindowDetectionEventFilter(QObject *watched, QObject *parent = nullptr)
89 : QObject(parent)
90 , m_watched(watched)
91 {
92 }
93
94Q_SIGNALS:
97
98private:
99 bool eventFilter(QObject *watched, QEvent *event) override {
100 if (watched != m_watched) return false;
101
102 if (event->type() == QEvent::PlatformSurface) {
103 QPlatformSurfaceEvent *pevent = static_cast<QPlatformSurfaceEvent*>(event);
104 if (pevent->surfaceEventType() == QPlatformSurfaceEvent::SurfaceCreated) {
106 } else if (pevent->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) {
108 }
109 }
110
111 return false;
112 }
113
114 QObject *m_watched = nullptr;
115};
116
117
119{
120 if (!m_waylandManager->isReady()) {
121 auto newState = tryDeinitialize(std::nullopt);
123 setReadyImpl(false);
124 return;
125 }
126
128 qWarning() << "WARNING: KisWaylandSurfaceColorManager::reinitialize(): received unbalanced connectionActive(true) signal!";
129 qWarning() << " " << ppVar(m_currentState);
130 m_currentState = tryDeinitialize(std::nullopt);
132 }
133
136}
137
139{
140 return m_isReady;
141}
142
144{
145 using feature = QtWayland::wp_color_manager_v1::feature;
146 using namespace KisColorimetryUtils;
147
148 if (!m_waylandManager->isFeatureSupported(feature::feature_parametric)) {
149 qWarning() << "KisWaylandSurfaceColorManager: feature_parametric is not supported";
150 return false;
151 }
152
153 if (!m_waylandManager->isFeatureSupported(feature::feature_set_primaries) &&
154 std::holds_alternative<Colorimetry>(desc.colorSpace.primaries)) {
155
156 qWarning() << "KisWaylandSurfaceColorManager: feature_set_primaries is not supported, even though requested";
157 return false;
158 }
159
160 if (!m_waylandManager->isFeatureSupported(feature::feature_set_tf_power) &&
161 std::holds_alternative<uint32_t>(desc.colorSpace.transferFunction)) {
162
163 qWarning() << "KisWaylandSurfaceColorManager: feature_set_tf_power is not supported, even though requested";
164 return false;
165 }
166
167 if (!m_waylandManager->isFeatureSupported(feature::feature_set_luminances) &&
168 desc.colorSpace.luminance) {
169
170 qWarning() << "KisWaylandSurfaceColorManager: feature_set_luminances is not supported, even though requested";
171 return false;
172 }
173
174 if (!m_waylandManager->isFeatureSupported(feature::feature_set_mastering_display_primaries) &&
175 desc.masteringInfo) {
176
177 qWarning() << "KisWaylandSurfaceColorManager: feature_set_mastering_display_primaries is not supported, even though requested";
178 return false;
179 }
180
181 if (!m_waylandManager->isFeatureSupported(feature::feature_extended_target_volume) &&
182 desc.masteringInfo && desc.colorSpace.luminance) {
183 if (desc.masteringInfo->luminance.minLuminance < desc.colorSpace.luminance->minLuminance
184 || desc.masteringInfo->luminance.maxLuminance > desc.colorSpace.luminance->maxLuminance) {
185
186 qWarning() << "KisWaylandSurfaceColorManager: feature_set_mastering_display_primaries is not supported, even though requested";
187 return false;
188 }
189 }
190
191 if (std::holds_alternative<KisSurfaceColorimetry::NamedPrimaries>(desc.colorSpace.primaries)) {
192 auto waylandPrimaries = primariesKritaToWayland(std::get<KisSurfaceColorimetry::NamedPrimaries>(desc.colorSpace.primaries));
193
194 if (!m_waylandManager->isPrimariesNamedSupported(waylandPrimaries))
195 return false;
196 }
197
198 if (std::holds_alternative<KisSurfaceColorimetry::NamedTransferFunction>(desc.colorSpace.transferFunction)) {
199 auto waylandTransferFunction = transferFunctionKritaToWayland(std::get<KisSurfaceColorimetry::NamedTransferFunction>(desc.colorSpace.transferFunction));
200
208 if (waylandTransferFunction == QtWayland::wp_color_manager_v1::transfer_function_srgb ||
209 waylandTransferFunction == QtWayland::wp_color_manager_v1::transfer_function_ext_srgb) {
210
211 return false;
212 }
213
214 if (!m_waylandManager->isTransferFunctionNamedSupported(waylandTransferFunction))
215 return false;
216 }
217
218 return true;
219}
220
222{
223 auto waylandIntent = renderIntentKritaToWayland(intent);
224 return m_waylandManager->isIntentSupported(waylandIntent);
225}
226
227#define USE_KWIN_BUG_WORKAROUND
228
229#ifdef USE_KWIN_BUG_WORKAROUND
230#include "wayland-wayland-client-protocol.h"
231#endif
232
234{
235 if (!m_isReady) {
236 qWarning() << "ERROR: KisWaylandSurfaceColorManager::setSurfaceDescription: the manager is not ready";
237 return makeReadyQFuture(false);
238 }
239
241
242 auto waylandDescription = WaylandSurfaceDescription::fromSurfaceDescription(desc);
243 auto waylandIntent = renderIntentKritaToWayland(intent);
244
245 if (!supportsSurfaceDescription(desc)) {
246 qWarning() << "ERROR: KisWaylandSurfaceColorManager::setSurfaceDescription: unsupported surface description";
247 return makeReadyQFuture(false);
248 }
249
250 if (!m_waylandManager->isIntentSupported(waylandIntent)) {
251 qWarning() << "ERROR: KisWaylandSurfaceColorManager::setSurfaceDescription: unsupported rendering intent";
252 return makeReadyQFuture(false);
253 }
254
256 std::shared_ptr<KisWaylandAPIImageDescriptionNoInfo> descriptionObject =
257 creator.createImageDescription(waylandDescription);
258
259 QPromise<bool> imageDescriptionPromise;
260 auto future = imageDescriptionPromise.future();
261
262 connect(descriptionObject.get(), &KisWaylandAPIImageDescription::sigDescriptionConstructed,
263 this,
264 [promise = std::move(imageDescriptionPromise), descriptionObject] (bool success) mutable {
265 promise.start();
266 promise.addResult(success);
267 promise.finish();
268 });
269
270 QFuture<bool> result =
271 future.then([this, desc, descriptionObject, waylandIntent, intent] (QFuture<bool> future) {
272 if (!future.result()) return false;
273
274 if (!m_surface) {
275 qWarning() << "WARNING: the surface has been destroyed while its format was being set!";
276 return false;
277 }
278
279#ifdef USE_KWIN_BUG_WORKAROUND
280 if (qEnvironmentVariableIsSet("KRITA_ENABLE_KWIN_INTENT_WORKAROUND")) {
281 // WARNING: KWin <= 6.4.4 doesn't handle intent changes properly
284
285 m_surface->unset_image_description();
286 auto waylandWindow = m_window->nativeInterface<QNativeInterface::Private::QWaylandWindow>();
287 ::wl_surface_commit(waylandWindow->surface());
288 }
289 }
290#endif
291
292 m_surface->set_image_description(descriptionObject->object(), waylandIntent);
293 m_window->requestUpdate();
295 m_renderingIntent = intent;
296 return true;
297 });
298
299 return result;
300}
301
303{
304 if (!m_isReady) return;
305
306 m_surface->unset_image_description();
307 m_currentDescription = std::nullopt;
308 m_renderingIntent = std::nullopt;
309}
310
311std::optional<KisSurfaceColorimetry::SurfaceDescription> KisWaylandSurfaceColorManager::surfaceDescription() const
312{
314}
315
316std::optional<KisSurfaceColorimetry::RenderIntent> KisWaylandSurfaceColorManager::renderingIntent() const
317{
318 return m_renderingIntent;
319}
320
321std::optional<KisSurfaceColorimetry::SurfaceDescription> KisWaylandSurfaceColorManager::preferredSurfaceDescription() const
322{
324}
325
341
343{
345 qWarning() << "WARNING: KisWaylandSurfaceColorManager::slotPlatformWindowCreated(): received unbalanced window created signal!";
346 qWarning() << " " << ppVar(m_currentState);
349 setReadyImpl(false);
350 }
351
352 auto newState = tryInitilize();
353
355 qWarning() << "WARNING: KisWaylandSurfaceColorManager::slotPlatformWindowCreated(): failed to reach WaylandWindowCreated state";
356 }
357
358 m_currentState = newState;
359}
360
368
370{
372 qWarning() << "WARNING: KisWaylandSurfaceColorManager::slotWaylandSurfaceCreated(): received unbalanced surface created signal!";
373 qWarning() << " " << ppVar(m_currentState);
376 setReadyImpl(false);
377 }
378
379 auto newState = tryInitilize();
380
382 qWarning() << "WARNING: KisWaylandSurfaceColorManager::slotWaylandSurfaceCreated(): failed to reach WaylandSurfaceCreated state";
383 }
384 m_currentState = newState;
385}
386
394
397{
405
406 if (m_waylandManager->isReady()) {
407 currentState = WaylandSurfaceState::Connected;
408 }
409
410 if (currentState == WaylandSurfaceState::Connected) {
412 auto *filter = new PlatformWindowDetectionEventFilter(m_window, this);
413 connect(filter,
415 this,
417 connect(filter,
419 this,
421 m_window->installEventFilter(filter);
423 }
424
425 auto waylandWindow = m_window->nativeInterface<QNativeInterface::Private::QWaylandWindow>();
426 if (waylandWindow) {
428 }
429 }
430
431 if (currentState == WaylandSurfaceState::WaylandWindowCreated) {
432 auto waylandWindow = m_window->nativeInterface<QNativeInterface::Private::QWaylandWindow>();
433
435 m_surfaceCreatedConnection = connect(waylandWindow,
436 &QNativeInterface::Private::QWaylandWindow::surfaceCreated,
437 this,
439 }
440
442 m_surfaceDestroyedConnection = connect(waylandWindow,
443 &QNativeInterface::Private::QWaylandWindow::surfaceDestroyed,
444 this,
446 }
447
448 if (waylandWindow->surface()) {
450 }
451 }
452
453 if (currentState == WaylandSurfaceState::WaylandSurfaceCreated) {
454 auto waylandWindow = m_window->nativeInterface<QNativeInterface::Private::QWaylandWindow>();
455
456 if (!m_surface) {
457 auto feedback = std::make_unique<KisWaylandAPISurfaceFeedback>(
458 m_waylandManager->get_surface_feedback(waylandWindow->surface()));
459 connect(feedback.get(),
461 this,
463 m_surface = std::make_unique<KisWaylandAPISurface>(m_waylandManager->get_surface(waylandWindow->surface()),
464 std::move(feedback));
465 }
466
468 }
469
470 if (currentState == WaylandSurfaceState::APIFeedbackCreated) {
471 if (m_surface->m_feedback->m_preferred->info.isReady()) {
472 auto waylandDesc = m_surface->m_feedback->m_preferred->info.m_data;
473 m_preferredDescription = waylandDesc.toSurfaceDescription();
474
476 }
477 }
478
479 return currentState;
480}
481
483KisWaylandSurfaceColorManager::tryDeinitialize(std::optional<KisWaylandSurfaceColorManager::WaylandSurfaceState> targetState)
484{
486
487 if (!targetState || *targetState < currentState) {
488 m_currentDescription = std::nullopt;
489 m_renderingIntent = std::nullopt;
490 m_preferredDescription = std::nullopt;
491
493 }
494
495 auto waylandWindow = m_window->nativeInterface<QNativeInterface::Private::QWaylandWindow>();
496
497 if (!waylandWindow || !waylandWindow->surface() ||
498 (targetState && *targetState < currentState)) {
499
500 m_surface.reset();
502 }
503
504 if (!waylandWindow || !waylandWindow->surface() ||
505 (targetState && *targetState < currentState)) {
506
508 }
509
510 if (!waylandWindow || (targetState && *targetState < currentState)) {
512 disconnect(m_surfaceCreatedConnection);
513 }
516 }
517 currentState = WaylandSurfaceState::Connected;
518 }
519
520 if (!m_waylandManager->isReady() || (targetState && *targetState < currentState)) {
521 m_window->removeEventFilter(m_platformWindowStateDetector);
522 m_platformWindowStateDetector->deleteLater();
524
526 }
527
528 return currentState;
529}
530
531#include <moc_KisWaylandSurfaceColorManager.cpp>
532#include <KisWaylandSurfaceColorManager.moc>
float value(const T *src, size_t ch)
Q_GLOBAL_STATIC(KisStoragePluginRegistry, s_instance)
void sigReadyChanged(bool value)
void sigPreferredSurfaceDescriptionChanged(const KisSurfaceColorimetry::SurfaceDescription &desc)
void sigReadyChanged(bool value)
std::unique_ptr< KisWaylandAPIImageDescriptionNoInfo > createImageDescription(const KisSurfaceColorimetry::WaylandSurfaceDescription &data)
Q_SIGNAL void sigDescriptionConstructed(bool success)
Q_SIGNAL void preferredChanged()
bool supportsSurfaceDescription(const KisSurfaceColorimetry::SurfaceDescription &desc) override
static std::shared_ptr< KisWaylandAPIColorManager > getOrCreateGlobalWaylandManager()
QFuture< bool > setSurfaceDescription(const KisSurfaceColorimetry::SurfaceDescription &desc, KisSurfaceColorimetry::RenderIntent intent) override
std::optional< KisSurfaceColorimetry::RenderIntent > m_renderingIntent
QMetaObject::Connection m_surfaceCreatedConnection
std::unique_ptr< KisWaylandAPISurface > m_surface
std::optional< KisSurfaceColorimetry::SurfaceDescription > preferredSurfaceDescription() const override
WaylandSurfaceState tryDeinitialize(std::optional< KisWaylandSurfaceColorManager::WaylandSurfaceState > targetState)
std::optional< KisSurfaceColorimetry::SurfaceDescription > surfaceDescription() const override
bool supportsRenderIntent(const KisSurfaceColorimetry::RenderIntent &intent) override
std::optional< KisSurfaceColorimetry::SurfaceDescription > m_currentDescription
std::shared_ptr< KisWaylandAPIColorManager > m_waylandManager
std::optional< KisSurfaceColorimetry::RenderIntent > renderingIntent() const override
std::optional< KisSurfaceColorimetry::SurfaceDescription > m_preferredDescription
KisWaylandSurfaceColorManager(QWindow *window, QObject *parent=nullptr)
QMetaObject::Connection m_surfaceDestroyedConnection
bool eventFilter(QObject *watched, QEvent *event) override
PlatformWindowDetectionEventFilter(QObject *watched, QObject *parent=nullptr)
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define ppVar(var)
Definition kis_debug.h:155
std::optional< Luminance > luminance
std::variant< NamedPrimaries, Colorimetry > primaries
std::variant< NamedTransferFunction, uint32_t > transferFunction