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 } else {
44 result.reset(new KisWaylandAPIColorManager());
45 *s_waylandManager = result;
46 }
47
48 return result;
49}
50
65
69
77
79{
80 Q_OBJECT
81public:
82 PlatformWindowDetectionEventFilter(QObject *watched, QObject *parent = nullptr)
83 : QObject(parent)
84 , m_watched(watched)
85 {
86 }
87
88Q_SIGNALS:
91
92private:
93 bool eventFilter(QObject *watched, QEvent *event) override {
94 if (watched != m_watched) return false;
95
96 if (event->type() == QEvent::PlatformSurface) {
97 QPlatformSurfaceEvent *pevent = static_cast<QPlatformSurfaceEvent*>(event);
98 if (pevent->surfaceEventType() == QPlatformSurfaceEvent::SurfaceCreated) {
100 } else if (pevent->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) {
102 }
103 }
104
105 return false;
106 }
107
108 QObject *m_watched = nullptr;
109};
110
111
113{
114 if (!m_waylandManager->isReady()) {
115 auto newState = tryDeinitialize(std::nullopt);
117 setReadyImpl(false);
118 return;
119 }
120
122 qWarning() << "WARNING: KisWaylandSurfaceColorManager::reinitialize(): received unbalanced connectionActive(true) signal!";
123 qWarning() << " " << ppVar(m_currentState);
124 m_currentState = tryDeinitialize(std::nullopt);
126 }
127
130}
131
133{
134 return m_isReady;
135}
136
138{
139 using feature = QtWayland::wp_color_manager_v1::feature;
140 using namespace KisColorimetryUtils;
141
142 if (!m_waylandManager->isFeatureSupported(feature::feature_parametric)) {
143 qWarning() << "KisWaylandSurfaceColorManager: feature_parametric is not supported";
144 return false;
145 }
146
147 if (!m_waylandManager->isFeatureSupported(feature::feature_set_primaries) &&
148 std::holds_alternative<Colorimetry>(desc.colorSpace.primaries)) {
149
150 qWarning() << "KisWaylandSurfaceColorManager: feature_set_primaries is not supported, even though requested";
151 return false;
152 }
153
154 if (!m_waylandManager->isFeatureSupported(feature::feature_set_tf_power) &&
155 std::holds_alternative<uint32_t>(desc.colorSpace.transferFunction)) {
156
157 qWarning() << "KisWaylandSurfaceColorManager: feature_set_tf_power is not supported, even though requested";
158 return false;
159 }
160
161 if (!m_waylandManager->isFeatureSupported(feature::feature_set_luminances) &&
162 desc.colorSpace.luminance) {
163
164 qWarning() << "KisWaylandSurfaceColorManager: feature_set_luminances is not supported, even though requested";
165 return false;
166 }
167
168 if (!m_waylandManager->isFeatureSupported(feature::feature_set_mastering_display_primaries) &&
169 desc.masteringInfo) {
170
171 qWarning() << "KisWaylandSurfaceColorManager: feature_set_mastering_display_primaries is not supported, even though requested";
172 return false;
173 }
174
175 if (!m_waylandManager->isFeatureSupported(feature::feature_extended_target_volume) &&
176 desc.masteringInfo && desc.colorSpace.luminance) {
177 if (desc.masteringInfo->luminance.minLuminance < desc.colorSpace.luminance->minLuminance
178 || desc.masteringInfo->luminance.maxLuminance > desc.colorSpace.luminance->maxLuminance) {
179
180 qWarning() << "KisWaylandSurfaceColorManager: feature_set_mastering_display_primaries is not supported, even though requested";
181 return false;
182 }
183 }
184
185 if (std::holds_alternative<KisSurfaceColorimetry::NamedPrimaries>(desc.colorSpace.primaries)) {
186 auto waylandPrimaries = primariesKritaToWayland(std::get<KisSurfaceColorimetry::NamedPrimaries>(desc.colorSpace.primaries));
187
188 if (!m_waylandManager->isPrimariesNamedSupported(waylandPrimaries))
189 return false;
190 }
191
192 if (std::holds_alternative<KisSurfaceColorimetry::NamedTransferFunction>(desc.colorSpace.transferFunction)) {
193 auto waylandTransferFunction = transferFunctionKritaToWayland(std::get<KisSurfaceColorimetry::NamedTransferFunction>(desc.colorSpace.transferFunction));
194
202 if (waylandTransferFunction == QtWayland::wp_color_manager_v1::transfer_function_srgb ||
203 waylandTransferFunction == QtWayland::wp_color_manager_v1::transfer_function_ext_srgb) {
204
205 return false;
206 }
207
208 if (!m_waylandManager->isTransferFunctionNamedSupported(waylandTransferFunction))
209 return false;
210 }
211
212 return true;
213}
214
216{
217 auto waylandIntent = renderIntentKritaToWayland(intent);
218 return m_waylandManager->isIntentSupported(waylandIntent);
219}
220
221#define USE_KWIN_BUG_WORKAROUND
222
223#ifdef USE_KWIN_BUG_WORKAROUND
224#include "wayland-wayland-client-protocol.h"
225#endif
226
228{
229 if (!m_isReady) {
230 qWarning() << "ERROR: KisWaylandSurfaceColorManager::setSurfaceDescription: the manager is not ready";
231 return makeReadyQFuture(false);
232 }
233
235
236 auto waylandDescription = WaylandSurfaceDescription::fromSurfaceDescription(desc);
237 auto waylandIntent = renderIntentKritaToWayland(intent);
238
239 if (!supportsSurfaceDescription(desc)) {
240 qWarning() << "ERROR: KisWaylandSurfaceColorManager::setSurfaceDescription: unsupported surface description";
241 return makeReadyQFuture(false);
242 }
243
244 if (!m_waylandManager->isIntentSupported(waylandIntent)) {
245 qWarning() << "ERROR: KisWaylandSurfaceColorManager::setSurfaceDescription: unsupported rendering intent";
246 return makeReadyQFuture(false);
247 }
248
250 std::shared_ptr<KisWaylandAPIImageDescriptionNoInfo> descriptionObject =
251 creator.createImageDescription(waylandDescription);
252
253 QPromise<bool> imageDescriptionPromise;
254 auto future = imageDescriptionPromise.future();
255
257 this,
258 [promise = std::move(imageDescriptionPromise), descriptionObject] (bool success) mutable {
259 promise.start();
260 promise.addResult(success);
261 promise.finish();
262 });
263
264 QFuture<bool> result =
265 future.then([this, desc, descriptionObject, waylandIntent, intent] (QFuture<bool> future) {
266 if (!future.result()) return false;
267
268 if (!m_surface) {
269 qWarning() << "WARNING: the surface has been destroyed while its format was being set!";
270 return false;
271 }
272
273#ifdef USE_KWIN_BUG_WORKAROUND
274 if (qEnvironmentVariableIsSet("KRITA_ENABLE_KWIN_INTENT_WORKAROUND")) {
275 // WARNING: KWin <= 6.4.4 doesn't handle intent changes properly
278
279 m_surface->unset_image_description();
280 auto waylandWindow = m_window->nativeInterface<QNativeInterface::Private::QWaylandWindow>();
281 ::wl_surface_commit(waylandWindow->surface());
282 }
283 }
284#endif
285
286 m_surface->set_image_description(descriptionObject->object(), waylandIntent);
287 m_window->requestUpdate();
289 m_renderingIntent = intent;
290 return true;
291 });
292
293 return result;
294}
295
297{
298 if (!m_isReady) return;
299
300 m_surface->unset_image_description();
301 m_currentDescription = std::nullopt;
302 m_renderingIntent = std::nullopt;
303}
304
305std::optional<KisSurfaceColorimetry::SurfaceDescription> KisWaylandSurfaceColorManager::surfaceDescription() const
306{
308}
309
310std::optional<KisSurfaceColorimetry::RenderIntent> KisWaylandSurfaceColorManager::renderingIntent() const
311{
312 return m_renderingIntent;
313}
314
315std::optional<KisSurfaceColorimetry::SurfaceDescription> KisWaylandSurfaceColorManager::preferredSurfaceDescription() const
316{
318}
319
335
337{
339 qWarning() << "WARNING: KisWaylandSurfaceColorManager::slotPlatformWindowCreated(): received unbalanced window created signal!";
340 qWarning() << " " << ppVar(m_currentState);
343 setReadyImpl(false);
344 }
345
346 auto newState = tryInitilize();
347
349 qWarning() << "WARNING: KisWaylandSurfaceColorManager::slotPlatformWindowCreated(): failed to reach WaylandWindowCreated state";
350 }
351
352 m_currentState = newState;
353}
354
362
364{
366 qWarning() << "WARNING: KisWaylandSurfaceColorManager::slotWaylandSurfaceCreated(): received unbalanced surface created signal!";
367 qWarning() << " " << ppVar(m_currentState);
370 setReadyImpl(false);
371 }
372
373 auto newState = tryInitilize();
374
376 qWarning() << "WARNING: KisWaylandSurfaceColorManager::slotWaylandSurfaceCreated(): failed to reach WaylandSurfaceCreated state";
377 }
378 m_currentState = newState;
379}
380
388
391{
399
400 if (m_waylandManager->isReady()) {
401 currentState = WaylandSurfaceState::Connected;
402 }
403
404 if (currentState == WaylandSurfaceState::Connected) {
406 auto *filter = new PlatformWindowDetectionEventFilter(m_window, this);
407 connect(filter,
409 this,
411 connect(filter,
413 this,
415 m_window->installEventFilter(filter);
417 }
418
419 auto waylandWindow = m_window->nativeInterface<QNativeInterface::Private::QWaylandWindow>();
420 if (waylandWindow) {
422 }
423 }
424
425 if (currentState == WaylandSurfaceState::WaylandWindowCreated) {
426 auto waylandWindow = m_window->nativeInterface<QNativeInterface::Private::QWaylandWindow>();
427
429 m_surfaceCreatedConnection = connect(waylandWindow,
430 &QNativeInterface::Private::QWaylandWindow::surfaceCreated,
431 this,
433 }
434
437 &QNativeInterface::Private::QWaylandWindow::surfaceDestroyed,
438 this,
440 }
441
442 if (waylandWindow->surface()) {
444 }
445 }
446
447 if (currentState == WaylandSurfaceState::WaylandSurfaceCreated) {
448 auto waylandWindow = m_window->nativeInterface<QNativeInterface::Private::QWaylandWindow>();
449
450 if (!m_surface) {
451 auto feedback = std::make_unique<KisWaylandAPISurfaceFeedback>(
452 m_waylandManager->get_surface_feedback(waylandWindow->surface()));
453 connect(feedback.get(),
455 this,
457 m_surface = std::make_unique<KisWaylandAPISurface>(m_waylandManager->get_surface(waylandWindow->surface()),
458 std::move(feedback));
459 }
460
462 }
463
464 if (currentState == WaylandSurfaceState::APIFeedbackCreated) {
465 if (m_surface->m_feedback->m_preferred->info.isReady()) {
466 auto waylandDesc = m_surface->m_feedback->m_preferred->info.m_data;
467 m_preferredDescription = waylandDesc.toSurfaceDescription();
468
470 }
471 }
472
473 return currentState;
474}
475
477KisWaylandSurfaceColorManager::tryDeinitialize(std::optional<KisWaylandSurfaceColorManager::WaylandSurfaceState> targetState)
478{
480
481 if (!targetState || *targetState < currentState) {
482 m_currentDescription = std::nullopt;
483 m_renderingIntent = std::nullopt;
484 m_preferredDescription = std::nullopt;
485
487 }
488
489 auto waylandWindow = m_window->nativeInterface<QNativeInterface::Private::QWaylandWindow>();
490
491 if (!waylandWindow || !waylandWindow->surface() ||
492 (targetState && *targetState < currentState)) {
493
494 m_surface.reset();
496 }
497
498 if (!waylandWindow || !waylandWindow->surface() ||
499 (targetState && *targetState < currentState)) {
500
502 }
503
504 if (!waylandWindow || (targetState && *targetState < currentState)) {
506 disconnect(m_surfaceCreatedConnection);
507 }
510 }
511 currentState = WaylandSurfaceState::Connected;
512 }
513
514 if (!m_waylandManager->isReady() || (targetState && *targetState < currentState)) {
515 m_window->removeEventFilter(m_platformWindowStateDetector);
516 m_platformWindowStateDetector->deleteLater();
518
520 }
521
522 return currentState;
523}
524
525#include <moc_KisWaylandSurfaceColorManager.cpp>
526#include <KisWaylandSurfaceColorManager.moc>
float value(const T *src, size_t ch)
Q_GLOBAL_STATIC(KisStoragePluginRegistry, s_instance)
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
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