KWin
Loading...
Searching...
No Matches
xdgshell.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6
7#include "xdgshell.h"
8#include "xdgshell_p.h"
9
10#include "display.h"
11#include "output.h"
12#include "seat.h"
13#include "utils/common.h"
14#include "utils/resource.h"
15
16#include <QTimer>
17
18namespace KWin
19{
20static const int s_version = 6;
21
26
27XdgShellInterfacePrivate::Resource *XdgShellInterfacePrivate::resourceForXdgSurface(XdgSurfaceInterface *surface) const
28{
29 return xdgSurfaces.value(surface);
30}
31
33{
34 xdgSurfaces.remove(surface);
35}
36
42{
43 QTimer *timer = new QTimer(q);
44 // we'll run the timer twice.
45 timer->setInterval(pingTimeout / 2);
46 QObject::connect(timer, &QTimer::timeout, q, [this, serial, attempt = 0]() mutable {
47 ++attempt;
48 if (attempt == 1) {
49 Q_EMIT q->pingDelayed(serial);
50 return;
51 }
52 Q_EMIT q->pingTimeout(serial);
53 delete pings.take(serial);
54 });
55 pings.insert(serial, timer);
56 timer->start();
57}
58
63
65{
66 const QList<XdgSurfaceInterface *> surfaces = xdgSurfaces.keys(resource);
67 qDeleteAll(surfaces);
68}
69
71{
72 if (xdgSurfaces.key(resource)) {
73 wl_resource_post_error(resource->handle, error_defunct_surfaces, "xdg_wm_base was destroyed before children");
74 return;
75 }
76 wl_resource_destroy(resource->handle);
77}
78
80{
81 wl_resource *positionerResource = wl_resource_create(resource->client(), &xdg_positioner_interface, resource->version(), id);
82 new XdgPositionerPrivate(positionerResource);
83}
84
85void XdgShellInterfacePrivate::xdg_wm_base_get_xdg_surface(Resource *resource, uint32_t id, ::wl_resource *surfaceResource)
86{
87 SurfaceInterface *surface = SurfaceInterface::get(surfaceResource);
88
89 if (surface->buffer()) {
90 wl_resource_post_error(resource->handle, XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER, "xdg_surface must not have a buffer at creation");
91 return;
92 }
93
94 wl_resource *xdgSurfaceResource = wl_resource_create(resource->client(), &xdg_surface_interface, resource->version(), id);
95
96 XdgSurfaceInterface *xdgSurface = new XdgSurfaceInterface(q, surface, xdgSurfaceResource);
97 xdgSurfaces.insert(xdgSurface, resource);
98}
99
100void XdgShellInterfacePrivate::xdg_wm_base_pong(Resource *resource, uint32_t serial)
101{
102 if (QTimer *timer = pings.take(serial)) {
103 delete timer;
104 }
105 Q_EMIT q->pongReceived(serial);
106}
107
109 : QObject(parent)
110 , d(new XdgShellInterfacePrivate(this))
111{
112 d->display = display;
113 d->init(*display, s_version);
114}
115
119
121{
122 return d->display;
123}
124
126{
127 XdgShellInterfacePrivate::Resource *clientResource = d->resourceForXdgSurface(surface);
128 if (!clientResource)
129 return 0;
130
131 quint32 serial = d->display->nextSerial();
132 d->send_ping(clientResource->handle, serial);
133 d->registerPing(serial);
134
135 return serial;
136}
137
138std::chrono::milliseconds XdgShellInterface::pingTimeoutInterval() const
139{
140 return d->pingTimeout;
141}
142
143void XdgShellInterface::setPingTimeoutInterval(std::chrono::milliseconds pingTimeout)
144{
145 d->pingTimeout = pingTimeout;
146}
147
152
154{
155 if (surface->buffer()) {
156 firstBufferAttached = true;
157 }
158
159 if (commit->acknowledgedConfigure.has_value()) {
160 Q_EMIT q->configureAcknowledged(commit->acknowledgedConfigure.value());
161 }
162
163 if (commit->windowGeometry.has_value()) {
164 windowGeometry = commit->windowGeometry.value();
166 }
167}
168
170{
171 firstBufferAttached = false;
172 isConfigured = false;
173 isInitialized = false;
174 windowGeometry = QRect();
175 Q_EMIT q->resetOccurred();
176}
177
182
184{
185 delete q;
186}
187
189{
190 if (toplevel || popup) {
191 qWarning() << "Tried to destroy xdg_surface before its role object";
192 }
193 wl_resource_destroy(resource->handle);
194}
195
196void XdgSurfaceInterfacePrivate::xdg_surface_get_toplevel(Resource *resource, uint32_t id)
197{
198 if (const SurfaceRole *role = surface->role()) {
199 if (role != XdgToplevelInterface::role()) {
200 wl_resource_post_error(resource->handle, error_already_constructed, "the surface already has a role assigned %s", role->name().constData());
201 return;
202 }
203 } else {
205 }
206
207 wl_resource *toplevelResource = wl_resource_create(resource->client(), &xdg_toplevel_interface, resource->version(), id);
208
209 auto toplevel = new XdgToplevelInterface(q, toplevelResource);
211}
212
213void XdgSurfaceInterfacePrivate::xdg_surface_get_popup(Resource *resource, uint32_t id, ::wl_resource *parentResource, ::wl_resource *positionerResource)
214{
215 if (const SurfaceRole *role = surface->role()) {
216 if (role != XdgPopupInterface::role()) {
217 wl_resource_post_error(resource->handle, error_already_constructed, "the surface already has a role assigned %s", role->name().constData());
218 return;
219 }
220 } else {
222 }
223
224 XdgPositioner positioner = XdgPositioner::get(positionerResource);
225 if (!positioner.isComplete()) {
226 auto shellPrivate = XdgShellInterfacePrivate::get(shell);
227 wl_resource_post_error(shellPrivate->resourceForXdgSurface(q)->handle,
228 QtWaylandServer::xdg_wm_base::error_invalid_positioner,
229 "xdg_positioner is incomplete");
230 return;
231 }
232
233 XdgSurfaceInterface *parentXdgSurface = XdgSurfaceInterface::get(parentResource);
234 SurfaceInterface *parentSurface = nullptr;
235 if (parentXdgSurface) {
236 if (!parentXdgSurface->surface()->role()) {
237 auto shellPrivate = XdgShellInterfacePrivate::get(shell);
238 wl_resource_post_error(shellPrivate->resourceForXdgSurface(q)->handle,
239 QtWaylandServer::xdg_wm_base::error_invalid_popup_parent,
240 "parent surface has no surface role");
241 return;
242 }
243 parentSurface = parentXdgSurface->surface();
244 }
245
246 wl_resource *popupResource = wl_resource_create(resource->client(), &xdg_popup_interface, resource->version(), id);
247
248 auto popup = new XdgPopupInterface(q, parentSurface, positioner, popupResource);
249 Q_EMIT shell->popupCreated(popup);
250}
251
252void XdgSurfaceInterfacePrivate::xdg_surface_set_window_geometry(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height)
253{
254 if (Q_UNLIKELY(!pending)) {
255 wl_resource_post_error(resource->handle, error_not_constructed, "xdg_surface must have a role");
256 return;
257 }
258
259 if (width < 1 || height < 1) {
260 wl_resource_post_error(resource->handle, -1, "invalid window geometry size (%dx%d)", width, height);
261 return;
262 }
263
264 pending->windowGeometry = QRect(x, y, width, height);
265}
266
267void XdgSurfaceInterfacePrivate::xdg_surface_ack_configure(Resource *resource, uint32_t serial)
268{
269 if (Q_UNLIKELY(!pending)) {
270 wl_resource_post_error(resource->handle, error_not_constructed, "xdg_surface must have a role");
271 return;
272 }
273
275}
276
278 : d(new XdgSurfaceInterfacePrivate(this))
279{
280 d->shell = shell;
281 d->surface = surface;
282 d->init(resource);
283}
284
286{
287 delete d->toplevel;
288 delete d->popup;
289
290 Q_EMIT aboutToBeDestroyed();
292}
293
295{
296 return d->toplevel;
297}
298
300{
301 return d->popup;
302}
303
305{
306 return d->shell;
307}
308
310{
311 return d->surface;
312}
313
315{
316 return d->isConfigured;
317}
318
320{
321 return d->windowGeometry;
322}
323
325{
326 if (auto surfacePrivate = resource_cast<XdgSurfaceInterfacePrivate *>(resource)) {
327 return surfacePrivate->q;
328 }
329 return nullptr;
330}
331
333 : SurfaceExtension(xdgSurface->surface())
334 , q(toplevel)
335 , xdgSurface(xdgSurface)
336{
337}
338
340{
341 auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface);
342 if (xdgSurfacePrivate->firstBufferAttached && !xdgSurfacePrivate->surface->buffer()) {
343 reset();
344 return;
345 }
346
347 xdgSurfacePrivate->apply(commit);
348
349 const auto minSize = commit->minimumSize.value_or(minimumSize);
350 const auto maxSize = commit->maximumSize.value_or(maximumSize);
351 if (!minSize.isEmpty() && !maxSize.isEmpty() && (minSize.width() > maxSize.width() || minSize.height() > maxSize.height())) {
352 wl_resource_post_error(resource()->handle, error_invalid_size, "minimum size can't be bigger than the maximum");
353 return;
354 }
355
356 if (commit->minimumSize && commit->minimumSize != minimumSize) {
357 minimumSize = commit->minimumSize.value();
359 }
360 if (commit->maximumSize && commit->maximumSize != maximumSize) {
361 maximumSize = commit->maximumSize.value();
363 }
364
365 if (!xdgSurfacePrivate->isInitialized) {
366 Q_EMIT q->initializeRequested();
367 xdgSurfacePrivate->isInitialized = true;
368 }
369}
370
372{
373 auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface);
374 xdgSurfacePrivate->reset();
375
376 windowTitle = QString();
377 windowClass = QString();
378 minimumSize = QSize();
379 maximumSize = QSize();
381 stashed.clear();
382
383 Q_EMIT q->resetOccurred();
384}
385
387{
388 delete q;
389}
390
392{
393 wl_resource_destroy(resource->handle);
394}
395
396void XdgToplevelInterfacePrivate::xdg_toplevel_set_parent(Resource *resource, ::wl_resource *parentResource)
397{
398 XdgToplevelInterface *parent = XdgToplevelInterface::get(parentResource);
399 if (parentXdgToplevel == parent) {
400 return;
401 }
402 parentXdgToplevel = parent;
403 Q_EMIT q->parentXdgToplevelChanged();
404}
405
406void XdgToplevelInterfacePrivate::xdg_toplevel_set_title(Resource *resource, const QString &title)
407{
408 if (windowTitle == title) {
409 return;
410 }
411 windowTitle = title;
412 Q_EMIT q->windowTitleChanged(title);
413}
414
415void XdgToplevelInterfacePrivate::xdg_toplevel_set_app_id(Resource *resource, const QString &app_id)
416{
417 if (windowClass == app_id) {
418 return;
419 }
420 windowClass = app_id;
421 Q_EMIT q->windowClassChanged(app_id);
422}
423
424void XdgToplevelInterfacePrivate::xdg_toplevel_show_window_menu(Resource *resource, ::wl_resource *seatResource, uint32_t serial, int32_t x, int32_t y)
425{
426 auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface);
427
428 if (!xdgSurfacePrivate->isConfigured) {
429 wl_resource_post_error(resource->handle, QtWaylandServer::xdg_surface::error_not_constructed, "surface has not been configured yet");
430 return;
431 }
432
433 SeatInterface *seat = SeatInterface::get(seatResource);
434 Q_EMIT q->windowMenuRequested(seat, QPoint(x, y), serial);
435}
436
437void XdgToplevelInterfacePrivate::xdg_toplevel_move(Resource *resource, ::wl_resource *seatResource, uint32_t serial)
438{
439 auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface);
440
441 if (!xdgSurfacePrivate->isConfigured) {
442 wl_resource_post_error(resource->handle, QtWaylandServer::xdg_surface::error_not_constructed, "surface has not been configured yet");
443 return;
444 }
445
446 SeatInterface *seat = SeatInterface::get(seatResource);
447 Q_EMIT q->moveRequested(seat, serial);
448}
449
450void XdgToplevelInterfacePrivate::xdg_toplevel_resize(Resource *resource, ::wl_resource *seatResource, uint32_t serial, uint32_t xdgEdges)
451{
452 auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface);
453
454 if (!xdgSurfacePrivate->isConfigured) {
455 wl_resource_post_error(resource->handle, QtWaylandServer::xdg_surface::error_not_constructed, "surface has not been configured yet");
456 return;
457 }
458
459 SeatInterface *seat = SeatInterface::get(seatResource);
460 Q_EMIT q->resizeRequested(seat, XdgToplevelInterface::ResizeAnchor(xdgEdges), serial);
461}
462
463void XdgToplevelInterfacePrivate::xdg_toplevel_set_max_size(Resource *resource, int32_t width, int32_t height)
464{
465 if (width < 0 || height < 0) {
466 wl_resource_post_error(resource->handle, error_invalid_size, "width and height must be positive or zero");
467 return;
468 }
469 pending.maximumSize = QSize(width, height);
470}
471
472void XdgToplevelInterfacePrivate::xdg_toplevel_set_min_size(Resource *resource, int32_t width, int32_t height)
473{
474 if (width < 0 || height < 0) {
475 wl_resource_post_error(resource->handle, error_invalid_size, "width and height must be positive or zero");
476 return;
477 }
478 pending.minimumSize = QSize(width, height);
479}
480
482{
483 Q_EMIT q->maximizeRequested();
484}
485
490
491void XdgToplevelInterfacePrivate::xdg_toplevel_set_fullscreen(Resource *resource, ::wl_resource *outputResource)
492{
493 OutputInterface *output = OutputInterface::get(outputResource);
494 Q_EMIT q->fullscreenRequested(output);
495}
496
501
503{
504 Q_EMIT q->minimizeRequested();
505}
506
511
513{
514 return resource_cast<XdgToplevelInterfacePrivate *>(resource);
515}
516
518 : d(new XdgToplevelInterfacePrivate(this, xdgSurface))
519{
521 surfacePrivate->toplevel = this;
522 surfacePrivate->pending = &d->pending;
523
524 d->init(resource);
525}
526
528{
529 Q_EMIT aboutToBeDestroyed();
530
531 XdgSurfaceInterfacePrivate *surfacePrivate = XdgSurfaceInterfacePrivate::get(d->xdgSurface);
532 surfacePrivate->toplevel = nullptr;
533 surfacePrivate->pending = nullptr;
534}
535
537{
538 static SurfaceRole role(QByteArrayLiteral("xdg_toplevel"));
539 return &role;
540}
541
543{
544 return d->xdgSurface->shell();
545}
546
548{
549 return d->xdgSurface;
550}
551
553{
554 return d->xdgSurface->surface();
555}
556
558{
559 return d->xdgSurface->isConfigured();
560}
561
563{
564 return d->parentXdgToplevel;
565}
566
568{
569 return d->windowTitle;
570}
571
573{
574 return d->windowClass;
575}
576
578{
579 return d->minimumSize.isEmpty() ? QSize(0, 0) : d->minimumSize;
580}
581
583{
584 return d->maximumSize.isEmpty() ? QSize(INT_MAX, INT_MAX) : d->maximumSize;
585}
586
587quint32 XdgToplevelInterface::sendConfigure(const QSize &size, const States &states)
588{
589 // Note that the states listed in the configure event must be an array of uint32_t.
590
591 uint32_t statesData[9] = {0};
592 int i = 0;
593
594 if (states & State::MaximizedHorizontal && states & State::MaximizedVertical) {
595 statesData[i++] = QtWaylandServer::xdg_toplevel::state_maximized;
596 }
597 if (states & State::FullScreen) {
598 statesData[i++] = QtWaylandServer::xdg_toplevel::state_fullscreen;
599 }
600 if (states & State::Resizing) {
601 statesData[i++] = QtWaylandServer::xdg_toplevel::state_resizing;
602 }
603 if (states & State::Activated) {
604 statesData[i++] = QtWaylandServer::xdg_toplevel::state_activated;
605 }
606
607 if (d->resource()->version() >= XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION) {
608 if (states & State::TiledLeft) {
609 statesData[i++] = QtWaylandServer::xdg_toplevel::state_tiled_left;
610 }
611 if (states & State::TiledTop) {
612 statesData[i++] = QtWaylandServer::xdg_toplevel::state_tiled_top;
613 }
614 if (states & State::TiledRight) {
615 statesData[i++] = QtWaylandServer::xdg_toplevel::state_tiled_right;
616 }
617 if (states & State::TiledBottom) {
618 statesData[i++] = QtWaylandServer::xdg_toplevel::state_tiled_bottom;
619 }
620 }
621
622 if (d->resource()->version() >= XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION) {
623 if (states & State::Suspended) {
624 statesData[i++] = QtWaylandServer::xdg_toplevel::state_suspended;
625 }
626 }
627
628 const QByteArray xdgStates = QByteArray::fromRawData(reinterpret_cast<char *>(statesData), sizeof(uint32_t) * i);
629 const quint32 serial = xdgSurface()->shell()->display()->nextSerial();
630
631 d->send_configure(size.width(), size.height(), xdgStates);
632
633 auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface());
634 xdgSurfacePrivate->send_configure(serial);
635 xdgSurfacePrivate->isConfigured = true;
636
637 return serial;
638}
639
641{
642 d->send_close();
643}
644
646{
647 if (d->resource()->version() >= XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION) {
648 d->send_configure_bounds(size.width(), size.height());
649 }
650}
651
652void XdgToplevelInterface::sendWmCapabilities(Capabilities capabilities)
653{
654 if (d->resource()->version() < XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION) {
655 return;
656 }
657 // Note that the capabilities listed in the event must be an array of uint32_t.
658
659 uint32_t capabilitiesData[4] = {0};
660 int i = 0;
661
662 if (capabilities & Capability::WindowMenu) {
663 capabilitiesData[i++] = QtWaylandServer::xdg_toplevel::wm_capabilities_window_menu;
664 }
665 if (capabilities & Capability::Maximize) {
666 capabilitiesData[i++] = QtWaylandServer::xdg_toplevel::wm_capabilities_maximize;
667 }
668 if (capabilities & Capability::FullScreen) {
669 capabilitiesData[i++] = QtWaylandServer::xdg_toplevel::wm_capabilities_fullscreen;
670 }
671 if (capabilities & Capability::Minimize) {
672 capabilitiesData[i++] = QtWaylandServer::xdg_toplevel::wm_capabilities_minimize;
673 }
674
675 d->send_wm_capabilities(QByteArray::fromRawData(reinterpret_cast<char *>(capabilitiesData), sizeof(uint32_t) * i));
676}
677
679{
680 if (auto toplevelPrivate = resource_cast<XdgToplevelInterfacePrivate *>(resource)) {
681 return toplevelPrivate->q;
682 }
683 return nullptr;
684}
685
690
692 : SurfaceExtension(xdgSurface->surface())
693 , q(popup)
694 , xdgSurface(xdgSurface)
695{
696}
697
699{
700 if (!parentSurface) {
701 auto shellPrivate = XdgShellInterfacePrivate::get(xdgSurface->shell());
702 wl_resource_post_error(shellPrivate->resourceForXdgSurface(xdgSurface)->handle,
703 QtWaylandServer::xdg_wm_base::error_invalid_popup_parent,
704 "no xdg_popup parent surface has been specified");
705 return;
706 }
707
708 auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface);
709 if (xdgSurfacePrivate->firstBufferAttached && !xdgSurfacePrivate->surface->buffer()) {
710 reset();
711 return;
712 }
713
714 xdgSurfacePrivate->apply(commit);
715
716 if (!xdgSurfacePrivate->isInitialized) {
717 Q_EMIT q->initializeRequested();
718 xdgSurfacePrivate->isInitialized = true;
719 }
720}
721
723{
724 auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface);
726 stashed.clear();
727 xdgSurfacePrivate->reset();
728}
729
731{
732 delete q;
733}
734
736{
737 // TODO: We need to post an error with the code XDG_WM_BASE_ERROR_NOT_THE_TOPMOST_POPUP if
738 // this popup is not the topmost grabbing popup. We most likely need a grab abstraction or
739 // something to determine whether the given popup has an explicit grab.
740 wl_resource_destroy(resource->handle);
741}
742
743void XdgPopupInterfacePrivate::xdg_popup_grab(Resource *resource, ::wl_resource *seatHandle, uint32_t serial)
744{
745 if (xdgSurface->surface()->buffer()) {
746 wl_resource_post_error(resource->handle, error_invalid_grab, "xdg_surface is already mapped");
747 return;
748 }
749 SeatInterface *seat = SeatInterface::get(seatHandle);
750 Q_EMIT q->grabRequested(seat, serial);
751}
752
753void XdgPopupInterfacePrivate::xdg_popup_reposition(Resource *resource, ::wl_resource *positionerResource, uint32_t token)
754{
755 positioner = XdgPositioner::get(positionerResource);
756 Q_EMIT q->repositionRequested(token);
757}
758
759XdgPopupInterface::XdgPopupInterface(XdgSurfaceInterface *xdgSurface, SurfaceInterface *parentSurface, const XdgPositioner &positioner, ::wl_resource *resource)
760 : d(new XdgPopupInterfacePrivate(this, xdgSurface))
761{
763 surfacePrivate->popup = this;
764 surfacePrivate->pending = &d->pending;
765
766 d->parentSurface = parentSurface;
767 d->positioner = positioner;
768 d->init(resource);
769}
770
772{
773 Q_EMIT aboutToBeDestroyed();
774
775 XdgSurfaceInterfacePrivate *surfacePrivate = XdgSurfaceInterfacePrivate::get(d->xdgSurface);
776 surfacePrivate->popup = nullptr;
777 surfacePrivate->pending = nullptr;
778}
779
781{
782 static SurfaceRole role(QByteArrayLiteral("xdg_popup"));
783 return &role;
784}
785
787{
788 return d->parentSurface;
789}
790
792{
793 return d->xdgSurface;
794}
795
797{
798 return d->xdgSurface->surface();
799}
800
802{
803 return d->xdgSurface->isConfigured();
804}
805
807{
808 return d->positioner;
809}
810
811quint32 XdgPopupInterface::sendConfigure(const QRect &rect)
812{
813 const quint32 serial = xdgSurface()->shell()->display()->nextSerial();
814
815 d->send_configure(rect.x(), rect.y(), rect.width(), rect.height());
816
817 auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface());
818 xdgSurfacePrivate->send_configure(serial);
819 xdgSurfacePrivate->isConfigured = true;
820
821 return serial;
822}
823
825{
826 d->send_popup_done();
827}
828
830{
831 Q_ASSERT(d->resource()->version() >= XDG_POPUP_REPOSITIONED_SINCE_VERSION);
832 d->send_repositioned(token);
833}
834
836{
837 if (auto popupPrivate = resource_cast<XdgPopupInterfacePrivate *>(resource)) {
838 return popupPrivate->q;
839 }
840 return nullptr;
841}
842
844 : data(new XdgPositionerData)
845{
846 init(resource);
847}
848
850{
851 return resource_cast<XdgPositionerPrivate *>(resource);
852}
853
855{
856 delete this;
857}
858
860{
861 wl_resource_destroy(resource->handle);
862}
863
864void XdgPositionerPrivate::xdg_positioner_set_size(Resource *resource, int32_t width, int32_t height)
865{
866 if (width < 1 || height < 1) {
867 wl_resource_post_error(resource->handle, error_invalid_input, "width and height must be positive and non-zero");
868 return;
869 }
870 data->size = QSize(width, height);
871}
872
873void XdgPositionerPrivate::xdg_positioner_set_anchor_rect(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height)
874{
875 if (width < 1 || height < 1) {
876 wl_resource_post_error(resource->handle, error_invalid_input, "width and height must be positive and non-zero");
877 return;
878 }
879 data->anchorRect = QRect(x, y, width, height);
880}
881
882void XdgPositionerPrivate::xdg_positioner_set_anchor(Resource *resource, uint32_t anchor)
883{
884 if (anchor > anchor_bottom_right) {
885 wl_resource_post_error(resource->handle, error_invalid_input, "unknown anchor point");
886 return;
887 }
888
889 switch (anchor) {
890 case anchor_top:
891 data->anchorEdges = Qt::TopEdge;
892 break;
893 case anchor_top_right:
894 data->anchorEdges = Qt::TopEdge | Qt::RightEdge;
895 break;
896 case anchor_right:
897 data->anchorEdges = Qt::RightEdge;
898 break;
899 case anchor_bottom_right:
900 data->anchorEdges = Qt::BottomEdge | Qt::RightEdge;
901 break;
902 case anchor_bottom:
903 data->anchorEdges = Qt::BottomEdge;
904 break;
905 case anchor_bottom_left:
906 data->anchorEdges = Qt::BottomEdge | Qt::LeftEdge;
907 break;
908 case anchor_left:
909 data->anchorEdges = Qt::LeftEdge;
910 break;
911 case anchor_top_left:
912 data->anchorEdges = Qt::TopEdge | Qt::LeftEdge;
913 break;
914 default:
915 data->anchorEdges = Qt::Edges();
916 break;
917 }
918}
919
920void XdgPositionerPrivate::xdg_positioner_set_parent_size(Resource *resource, int32_t width, int32_t height)
921{
922 data->parentSize = QSize(width, height);
923}
924
926{
927 data->isReactive = true;
928}
929
930void XdgPositionerPrivate::xdg_positioner_set_parent_configure(Resource *resource, uint32_t serial)
931{
932 data->parentConfigure = serial;
933}
934
935void XdgPositionerPrivate::xdg_positioner_set_gravity(Resource *resource, uint32_t gravity)
936{
937 if (gravity > gravity_bottom_right) {
938 wl_resource_post_error(resource->handle, error_invalid_input, "unknown gravity direction");
939 return;
940 }
941
942 switch (gravity) {
943 case gravity_top:
944 data->gravityEdges = Qt::TopEdge;
945 break;
946 case gravity_top_right:
947 data->gravityEdges = Qt::TopEdge | Qt::RightEdge;
948 break;
949 case gravity_right:
950 data->gravityEdges = Qt::RightEdge;
951 break;
952 case gravity_bottom_right:
953 data->gravityEdges = Qt::BottomEdge | Qt::RightEdge;
954 break;
955 case gravity_bottom:
956 data->gravityEdges = Qt::BottomEdge;
957 break;
958 case gravity_bottom_left:
959 data->gravityEdges = Qt::BottomEdge | Qt::LeftEdge;
960 break;
961 case gravity_left:
962 data->gravityEdges = Qt::LeftEdge;
963 break;
964 case gravity_top_left:
965 data->gravityEdges = Qt::TopEdge | Qt::LeftEdge;
966 break;
967 default:
968 data->gravityEdges = Qt::Edges();
969 break;
970 }
971}
972
973void XdgPositionerPrivate::xdg_positioner_set_constraint_adjustment(Resource *resource, uint32_t constraint_adjustment)
974{
975 if (constraint_adjustment & constraint_adjustment_flip_x) {
976 data->flipConstraintAdjustments |= Qt::Horizontal;
977 } else {
978 data->flipConstraintAdjustments &= ~Qt::Horizontal;
979 }
980
981 if (constraint_adjustment & constraint_adjustment_flip_y) {
982 data->flipConstraintAdjustments |= Qt::Vertical;
983 } else {
984 data->flipConstraintAdjustments &= ~Qt::Vertical;
985 }
986
987 if (constraint_adjustment & constraint_adjustment_slide_x) {
988 data->slideConstraintAdjustments |= Qt::Horizontal;
989 } else {
990 data->slideConstraintAdjustments &= ~Qt::Horizontal;
991 }
992
993 if (constraint_adjustment & constraint_adjustment_slide_y) {
994 data->slideConstraintAdjustments |= Qt::Vertical;
995 } else {
996 data->slideConstraintAdjustments &= ~Qt::Vertical;
997 }
998
999 if (constraint_adjustment & constraint_adjustment_resize_x) {
1000 data->resizeConstraintAdjustments |= Qt::Horizontal;
1001 } else {
1002 data->resizeConstraintAdjustments &= ~Qt::Horizontal;
1003 }
1004
1005 if (constraint_adjustment & constraint_adjustment_resize_y) {
1006 data->resizeConstraintAdjustments |= Qt::Vertical;
1007 } else {
1008 data->resizeConstraintAdjustments &= ~Qt::Vertical;
1009 }
1010}
1011
1012void XdgPositionerPrivate::xdg_positioner_set_offset(Resource *resource, int32_t x, int32_t y)
1013{
1014 data->offset = QPoint(x, y);
1015}
1016
1021
1023 : d(other.d)
1024{
1025}
1026
1030
1032{
1033 d = other.d;
1034 return *this;
1035}
1036
1038{
1039 return d->size.isValid() && d->anchorRect.isValid();
1040}
1041
1043{
1044 return d->size;
1045}
1046
1048{
1049 return d->isReactive;
1050}
1051
1052QRectF XdgPositioner::placement(const QRectF &bounds) const
1053{
1054 // returns if a target is within the supplied bounds, optional edges argument states which side to check
1055 auto inBounds = [bounds](const QRectF &target, Qt::Edges edges = Qt::LeftEdge | Qt::RightEdge | Qt::TopEdge | Qt::BottomEdge) -> bool {
1056 if (edges & Qt::LeftEdge && target.left() < bounds.left()) {
1057 return false;
1058 }
1059 if (edges & Qt::TopEdge && target.top() < bounds.top()) {
1060 return false;
1061 }
1062 if (edges & Qt::RightEdge && target.right() > bounds.right()) {
1063 // normal QRect::right issue cancels out
1064 return false;
1065 }
1066 if (edges & Qt::BottomEdge && target.bottom() > bounds.bottom()) {
1067 return false;
1068 }
1069 return true;
1070 };
1071
1072 QRectF popupRect(popupOffset(d->anchorRect, d->anchorEdges, d->gravityEdges, d->size) + d->offset, d->size);
1073
1074 // if that fits, we don't need to do anything
1075 if (inBounds(popupRect)) {
1076 return popupRect;
1077 }
1078 // otherwise apply constraint adjustment per axis in order XDG Shell Popup states
1079
1080 if (d->flipConstraintAdjustments & Qt::Horizontal) {
1081 if (!inBounds(popupRect, Qt::LeftEdge | Qt::RightEdge)) {
1082 // flip both edges (if either bit is set, XOR both)
1083 auto flippedAnchorEdge = d->anchorEdges;
1084 if (flippedAnchorEdge & (Qt::LeftEdge | Qt::RightEdge)) {
1085 flippedAnchorEdge ^= (Qt::LeftEdge | Qt::RightEdge);
1086 }
1087 auto flippedGravity = d->gravityEdges;
1088 if (flippedGravity & (Qt::LeftEdge | Qt::RightEdge)) {
1089 flippedGravity ^= (Qt::LeftEdge | Qt::RightEdge);
1090 }
1091 auto flippedPopupRect = QRectF(popupOffset(d->anchorRect, flippedAnchorEdge, flippedGravity, d->size) + d->offset, d->size);
1092
1093 // if it still doesn't fit we should continue with the unflipped version
1094 if (inBounds(flippedPopupRect, Qt::LeftEdge | Qt::RightEdge)) {
1095 popupRect.moveLeft(flippedPopupRect.left());
1096 }
1097 }
1098 }
1099 if (d->slideConstraintAdjustments & Qt::Horizontal) {
1100 if (!inBounds(popupRect, Qt::LeftEdge)) {
1101 popupRect.moveLeft(bounds.left());
1102 }
1103 if (!inBounds(popupRect, Qt::RightEdge)) {
1104 popupRect.moveRight(bounds.right());
1105 }
1106 }
1107 if (d->resizeConstraintAdjustments & Qt::Horizontal) {
1108 QRectF unconstrainedRect = popupRect;
1109
1110 if (!inBounds(unconstrainedRect, Qt::LeftEdge)) {
1111 unconstrainedRect.setLeft(bounds.left());
1112 }
1113 if (!inBounds(unconstrainedRect, Qt::RightEdge)) {
1114 unconstrainedRect.setRight(bounds.right());
1115 }
1116
1117 if (unconstrainedRect.isValid()) {
1118 popupRect = unconstrainedRect;
1119 }
1120 }
1121
1122 if (d->flipConstraintAdjustments & Qt::Vertical) {
1123 if (!inBounds(popupRect, Qt::TopEdge | Qt::BottomEdge)) {
1124 // flip both edges (if either bit is set, XOR both)
1125 auto flippedAnchorEdge = d->anchorEdges;
1126 if (flippedAnchorEdge & (Qt::TopEdge | Qt::BottomEdge)) {
1127 flippedAnchorEdge ^= (Qt::TopEdge | Qt::BottomEdge);
1128 }
1129 auto flippedGravity = d->gravityEdges;
1130 if (flippedGravity & (Qt::TopEdge | Qt::BottomEdge)) {
1131 flippedGravity ^= (Qt::TopEdge | Qt::BottomEdge);
1132 }
1133 auto flippedPopupRect = QRectF(popupOffset(d->anchorRect, flippedAnchorEdge, flippedGravity, d->size) + d->offset, d->size);
1134
1135 // if it still doesn't fit we should continue with the unflipped version
1136 if (inBounds(flippedPopupRect, Qt::TopEdge | Qt::BottomEdge)) {
1137 popupRect.moveTop(flippedPopupRect.top());
1138 }
1139 }
1140 }
1141 if (d->slideConstraintAdjustments & Qt::Vertical) {
1142 if (!inBounds(popupRect, Qt::TopEdge)) {
1143 popupRect.moveTop(bounds.top());
1144 }
1145 if (!inBounds(popupRect, Qt::BottomEdge)) {
1146 popupRect.moveBottom(bounds.bottom());
1147 }
1148 }
1149 if (d->resizeConstraintAdjustments & Qt::Vertical) {
1150 QRectF unconstrainedRect = popupRect;
1151
1152 if (!inBounds(unconstrainedRect, Qt::TopEdge)) {
1153 unconstrainedRect.setTop(bounds.top());
1154 }
1155 if (!inBounds(unconstrainedRect, Qt::BottomEdge)) {
1156 unconstrainedRect.setBottom(bounds.bottom());
1157 }
1158
1159 if (unconstrainedRect.isValid()) {
1160 popupRect = unconstrainedRect;
1161 }
1162 }
1163
1164 return popupRect;
1165}
1166
1167XdgPositioner XdgPositioner::get(::wl_resource *resource)
1168{
1169 XdgPositionerPrivate *xdgPositionerPrivate = XdgPositionerPrivate::get(resource);
1170 if (xdgPositionerPrivate)
1171 return XdgPositioner(xdgPositionerPrivate->data);
1172 return XdgPositioner();
1173}
1174
1175XdgPositioner::XdgPositioner(const QSharedDataPointer<XdgPositionerData> &data)
1176 : d(data)
1177{
1178}
1179
1180} // namespace KWin
1181
1182#include "moc_xdgshell.cpp"
Class holding the Wayland server display loop.
Definition display.h:34
quint32 nextSerial()
Definition display.cpp:134
static OutputInterface * get(wl_resource *native)
Definition output.cpp:320
Represents a Seat on the Wayland Display.
Definition seat.h:134
static SeatInterface * get(wl_resource *native)
Definition seat.cpp:429
QMap< quint32, XdgToplevelCommit > stashed
Definition surface.h:472
Resource representing a wl_surface.
Definition surface.h:80
static SurfaceInterface * get(wl_resource *native)
Definition surface.cpp:819
GraphicsBuffer * buffer() const
Definition surface.cpp:809
SurfaceRole * role() const
Definition surface.cpp:429
static SurfaceRole * role()
Definition xdgshell.cpp:780
void repositionRequested(quint32 token)
~XdgPopupInterface() override
Definition xdgshell.cpp:771
void grabRequested(SeatInterface *seat, quint32 serial)
void sendRepositioned(quint32 token)
Definition xdgshell.cpp:829
static XdgPopupInterface * get(::wl_resource *resource)
Definition xdgshell.cpp:835
SurfaceInterface * parentSurface() const
Definition xdgshell.cpp:786
XdgSurfaceInterface * xdgSurface() const
Definition xdgshell.cpp:791
XdgPopupInterface(XdgSurfaceInterface *surface, SurfaceInterface *parentSurface, const XdgPositioner &positioner, ::wl_resource *resource)
Definition xdgshell.cpp:759
SurfaceInterface * surface() const
Definition xdgshell.cpp:796
quint32 sendConfigure(const QRect &rect)
Definition xdgshell.cpp:811
bool isConfigured() const
Definition xdgshell.cpp:801
XdgPositioner positioner() const
Definition xdgshell.cpp:806
XdgSurfaceInterface * xdgSurface
Definition xdgshell_p.h:184
static XdgPopupInterfacePrivate * get(XdgPopupInterface *popup)
Definition xdgshell.cpp:686
SurfaceInterface * parentSurface
Definition xdgshell_p.h:183
void xdg_popup_grab(Resource *resource, ::wl_resource *seat, uint32_t serial) override
Definition xdgshell.cpp:743
void xdg_popup_destroy_resource(Resource *resource) override
Definition xdgshell.cpp:730
XdgPopupInterfacePrivate(XdgPopupInterface *popup, XdgSurfaceInterface *surface)
Definition xdgshell.cpp:691
void xdg_popup_reposition(Resource *resource, struct ::wl_resource *positioner, uint32_t token) override
Definition xdgshell.cpp:753
void apply(XdgPopupCommit *commit) override
Definition xdgshell.cpp:698
void xdg_popup_destroy(Resource *resource) override
Definition xdgshell.cpp:735
QRectF placement(const QRectF &bounds) const
bool isReactive() const
static XdgPositioner get(::wl_resource *resource)
bool isComplete() const
QSize size() const
XdgPositioner & operator=(const XdgPositioner &other)
void xdg_positioner_set_reactive(Resource *resource) override
Definition xdgshell.cpp:925
void xdg_positioner_destroy(Resource *resource) override
Definition xdgshell.cpp:859
void xdg_positioner_set_size(Resource *resource, int32_t width, int32_t height) override
Definition xdgshell.cpp:864
void xdg_positioner_set_offset(Resource *resource, int32_t x, int32_t y) override
void xdg_positioner_destroy_resource(Resource *resource) override
Definition xdgshell.cpp:854
static XdgPositionerPrivate * get(::wl_resource *resource)
Definition xdgshell.cpp:849
void xdg_positioner_set_constraint_adjustment(Resource *resource, uint32_t constraint_adjustment) override
Definition xdgshell.cpp:973
void xdg_positioner_set_gravity(Resource *resource, uint32_t gravity) override
Definition xdgshell.cpp:935
void xdg_positioner_set_parent_size(Resource *resource, int32_t width, int32_t height) override
Definition xdgshell.cpp:920
void xdg_positioner_set_anchor_rect(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) override
Definition xdgshell.cpp:873
void xdg_positioner_set_anchor(Resource *resource, uint32_t anchor) override
Definition xdgshell.cpp:882
QSharedDataPointer< XdgPositionerData > data
Definition xdgshell_p.h:68
XdgPositionerPrivate(::wl_resource *resource)
Definition xdgshell.cpp:843
void xdg_positioner_set_parent_configure(Resource *resource, uint32_t serial) override
Definition xdgshell.cpp:930
XdgShellInterface(Display *display, QObject *parent=nullptr)
Definition xdgshell.cpp:108
void setPingTimeoutInterval(std::chrono::milliseconds pingTimeout)
Definition xdgshell.cpp:143
void toplevelCreated(XdgToplevelInterface *toplevel)
void popupCreated(XdgPopupInterface *popup)
quint32 ping(XdgSurfaceInterface *surface)
Definition xdgshell.cpp:125
std::chrono::milliseconds pingTimeoutInterval() const
Definition xdgshell.cpp:138
void pingTimeout(quint32 serial)
~XdgShellInterface() override
Definition xdgshell.cpp:116
void pingDelayed(quint32 serial)
void pongReceived(quint32 serial)
Display * display() const
Definition xdgshell.cpp:120
void xdg_wm_base_create_positioner(Resource *resource, uint32_t id) override
Definition xdgshell.cpp:79
void registerPing(quint32 serial)
Definition xdgshell.cpp:41
void xdg_wm_base_pong(Resource *resource, uint32_t serial) override
Definition xdgshell.cpp:100
XdgShellInterface * q
Definition xdgshell_p.h:31
std::chrono::milliseconds pingTimeout
Definition xdgshell_p.h:34
void xdg_wm_base_destroy(Resource *resource) override
Definition xdgshell.cpp:70
Resource * resourceForXdgSurface(XdgSurfaceInterface *surface) const
Definition xdgshell.cpp:27
void unregisterXdgSurface(XdgSurfaceInterface *surface)
Definition xdgshell.cpp:32
QMap< quint32, QTimer * > pings
Definition xdgshell_p.h:33
void xdg_wm_base_get_xdg_surface(Resource *resource, uint32_t id, ::wl_resource *surface) override
Definition xdgshell.cpp:85
void xdg_wm_base_destroy_resource(Resource *resource) override
Definition xdgshell.cpp:64
XdgShellInterfacePrivate(XdgShellInterface *shell)
Definition xdgshell.cpp:22
static XdgShellInterfacePrivate * get(XdgShellInterface *shell)
Definition xdgshell.cpp:59
~XdgSurfaceInterface() override
Definition xdgshell.cpp:285
static XdgSurfaceInterface * get(::wl_resource *resource)
Definition xdgshell.cpp:324
XdgPopupInterface * popup() const
Definition xdgshell.cpp:299
QRect windowGeometry() const
Definition xdgshell.cpp:319
void windowGeometryChanged(const QRect &rect)
XdgSurfaceInterface(XdgShellInterface *shell, SurfaceInterface *surface, ::wl_resource *resource)
Definition xdgshell.cpp:277
SurfaceInterface * surface() const
Definition xdgshell.cpp:309
XdgShellInterface * shell() const
Definition xdgshell.cpp:304
void configureAcknowledged(quint32 serial)
XdgToplevelInterface * toplevel() const
Definition xdgshell.cpp:294
QPointer< XdgPopupInterface > popup
Definition xdgshell_p.h:116
void xdg_surface_get_popup(Resource *resource, uint32_t id, ::wl_resource *parent, ::wl_resource *positioner) override
Definition xdgshell.cpp:213
void xdg_surface_get_toplevel(Resource *resource, uint32_t id) override
Definition xdgshell.cpp:196
void xdg_surface_ack_configure(Resource *resource, uint32_t serial) override
Definition xdgshell.cpp:267
void apply(XdgSurfaceCommit *commit)
Definition xdgshell.cpp:153
XdgSurfaceInterfacePrivate(XdgSurfaceInterface *xdgSurface)
Definition xdgshell.cpp:148
QPointer< XdgToplevelInterface > toplevel
Definition xdgshell_p.h:115
void xdg_surface_set_window_geometry(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) override
Definition xdgshell.cpp:252
void xdg_surface_destroy(Resource *resource) override
Definition xdgshell.cpp:188
void xdg_surface_destroy_resource(Resource *resource) override
Definition xdgshell.cpp:183
QPointer< SurfaceInterface > surface
Definition xdgshell_p.h:117
static XdgSurfaceInterfacePrivate * get(XdgSurfaceInterface *surface)
Definition xdgshell.cpp:178
XdgSurfaceInterface * q
Definition xdgshell_p.h:113
void windowClassChanged(const QString &windowClass)
void windowMenuRequested(KWin::SeatInterface *seat, const QPoint &pos, quint32 serial)
XdgShellInterface * shell() const
Definition xdgshell.cpp:542
void minimumSizeChanged(const QSize &size)
quint32 sendConfigure(const QSize &size, const States &states)
Definition xdgshell.cpp:587
void windowTitleChanged(const QString &windowTitle)
XdgSurfaceInterface * xdgSurface() const
Definition xdgshell.cpp:547
static XdgToplevelInterface * get(::wl_resource *resource)
Definition xdgshell.cpp:678
void fullscreenRequested(KWin::OutputInterface *output)
void maximumSizeChanged(const QSize &size)
void moveRequested(KWin::SeatInterface *seat, quint32 serial)
QString windowTitle() const
Definition xdgshell.cpp:567
static SurfaceRole * role()
Definition xdgshell.cpp:536
SurfaceInterface * surface() const
Definition xdgshell.cpp:552
void sendConfigureBounds(const QSize &size)
Definition xdgshell.cpp:645
XdgToplevelInterface(XdgSurfaceInterface *surface, ::wl_resource *resource)
Definition xdgshell.cpp:517
QString windowClass() const
Definition xdgshell.cpp:572
void sendWmCapabilities(Capabilities capabilities)
Definition xdgshell.cpp:652
XdgToplevelInterface * parentXdgToplevel() const
Definition xdgshell.cpp:562
void resizeRequested(KWin::SeatInterface *seat, KWin::XdgToplevelInterface::ResizeAnchor anchor, quint32 serial)
void apply(XdgToplevelCommit *commit) override
Definition xdgshell.cpp:339
void xdg_toplevel_set_max_size(Resource *resource, int32_t width, int32_t height) override
Definition xdgshell.cpp:463
void xdg_toplevel_move(Resource *resource, ::wl_resource *seat, uint32_t serial) override
Definition xdgshell.cpp:437
static XdgToplevelInterfacePrivate * get(XdgToplevelInterface *toplevel)
Definition xdgshell.cpp:507
void xdg_toplevel_set_fullscreen(Resource *resource, ::wl_resource *output) override
Definition xdgshell.cpp:491
void xdg_toplevel_destroy_resource(Resource *resource) override
Definition xdgshell.cpp:386
XdgToplevelInterface * q
Definition xdgshell_p.h:145
void xdg_toplevel_set_min_size(Resource *resource, int32_t width, int32_t height) override
Definition xdgshell.cpp:472
QPointer< XdgToplevelInterface > parentXdgToplevel
Definition xdgshell_p.h:146
void xdg_toplevel_set_maximized(Resource *resource) override
Definition xdgshell.cpp:481
void xdg_toplevel_set_title(Resource *resource, const QString &title) override
Definition xdgshell.cpp:406
void xdg_toplevel_destroy(Resource *resource) override
Definition xdgshell.cpp:391
void xdg_toplevel_show_window_menu(Resource *resource, ::wl_resource *seat, uint32_t serial, int32_t x, int32_t y) override
Definition xdgshell.cpp:424
void xdg_toplevel_unset_maximized(Resource *resource) override
Definition xdgshell.cpp:486
XdgSurfaceInterface * xdgSurface
Definition xdgshell_p.h:148
void xdg_toplevel_set_minimized(Resource *resource) override
Definition xdgshell.cpp:502
void xdg_toplevel_set_parent(Resource *resource, ::wl_resource *parent) override
Definition xdgshell.cpp:396
void xdg_toplevel_unset_fullscreen(Resource *resource) override
Definition xdgshell.cpp:497
void xdg_toplevel_resize(Resource *resource, ::wl_resource *seat, uint32_t serial, uint32_t edges) override
Definition xdgshell.cpp:450
XdgToplevelInterfacePrivate(XdgToplevelInterface *toplevel, XdgSurfaceInterface *surface)
Definition xdgshell.cpp:332
void xdg_toplevel_set_app_id(Resource *resource, const QString &app_id) override
Definition xdgshell.cpp:415
QPointF popupOffset(const QRectF &anchorRect, const Qt::Edges anchorEdge, const Qt::Edges gravity, const QSizeF popupSize)
Definition common.cpp:194
std::optional< quint32 > acknowledgedConfigure
Definition xdgshell_p.h:89
std::optional< QRect > windowGeometry
Definition xdgshell_p.h:88
std::optional< QSize > maximumSize
Definition xdgshell_p.h:95
std::optional< QSize > minimumSize
Definition xdgshell_p.h:94