17#include <QApplication>
19#include <QAbstractEventDispatcher>
37 , Display_ { display }
38 , AppWin_ { DefaultRootWindow (display) }
40 QAbstractEventDispatcher::instance ()->installNativeEventFilter (
this);
42 const uint32_t rootEvents [] =
44 XCB_EVENT_MASK_STRUCTURE_NOTIFY |
45 XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
46 XCB_EVENT_MASK_PROPERTY_CHANGE
48 xcb_change_window_attributes (Conn_,
49 AppWin_, XCB_CW_EVENT_MASK, rootEvents);
54 static const auto wrapper = [] () -> std::unique_ptr<XWrapper>
56 const auto x11app = qGuiApp->nativeInterface<QNativeInterface::QX11Application> ();
57 if (!x11app || !x11app->display ())
60 return std::make_unique<XWrapper> (x11app->connection (), x11app->display (), PrivateCtor {});
84 if (eventType !=
"xcb_generic_event_t")
87 const auto ev =
static_cast<xcb_generic_event_t*
> (msg);
88 if ((ev->response_type & ~0x80) == XCB_PROPERTY_NOTIFY)
89 HandlePropNotify (
static_cast<xcb_property_notify_event_t*
> (msg));
97 struct IsDoublePtr : std::false_type {};
100 struct IsDoublePtr<T**> : std::true_type {};
107 Guarded () =
default;
109 Guarded (
const Guarded&) =
delete;
110 Guarded& operator= (
const Guarded&) =
delete;
112 Guarded (Guarded&& other) noexcept
113 : Data_ { other.Data_ }
115 other.Data_ =
nullptr;
118 Guarded& operator= (Guarded&& other)
noexcept
120 std::swap (Data_, other.Data_);
130 T**
Get (
bool clear =
true) noexcept
138 U GetAs (
bool clear =
true) noexcept
142 return IsDoublePtr<U>::value ?
143 reinterpret_cast<U
> (&Data_) :
144 reinterpret_cast<U> (Data_);
147 T operator[] (
size_t idx)
const noexcept
152 T& operator[] (
size_t idx)
noexcept
157 explicit operator bool () const noexcept
159 return Data_ !=
nullptr;
162 bool operator! () const noexcept
172 XSync (Display_, False);
178 Guarded<Window> data;
181 if (GetRootWinProp (
GetAtom (
"_NET_CLIENT_LIST"), &length, data.GetAs<uchar**> ()))
182 for (ulong i = 0; i < length; ++i)
194 auto utf8Str =
GetAtom (
"UTF8_STRING");
196 if (GetWinProp (wid,
GetAtom (
"_NET_WM_VISIBLE_NAME"), &length, data.Get (), utf8Str))
197 name = QString::fromUtf8 (data.GetAs<
char*> (
false));
199 if (name.isEmpty () && GetWinProp (wid,
GetAtom (
"_NET_WM_NAME"), &length, data.Get (), utf8Str))
200 name = QString::fromUtf8 (data.GetAs<
char*> (
false));
202 if (name.isEmpty () && GetWinProp (wid,
GetAtom (
"XA_WM_NAME"), &length, data.Get (), XA_STRING))
203 name = QString::fromUtf8 (data.GetAs<
char*> (
false));
207 XFetchName (Display_, wid, data.GetAs<
char**> ());
208 name = QString (data.GetAs<
char*> (
false));
214 if (XGetWMName (Display_, wid, &prop))
216 name = QString::fromUtf8 (
reinterpret_cast<char*
> (prop.value));
227 ulong type, count, extra;
230 XGetWindowProperty (Display_, wid,
GetAtom (
"_NET_WM_ICON"),
231 0, std::numeric_limits<long>::max (), False, AnyPropertyType,
232 &type, &fmt, &count, &extra,
233 data.GetAs<uchar**> ());
240 auto cur = *data.Get (
false);
241 auto end = cur + count;
244 QImage img (cur [0], cur [1], QImage::Format_ARGB32);
246 const auto bytesCount = img.sizeInBytes ();
247 for (
int i = 0; i < bytesCount / 4; ++i, ++cur)
248 reinterpret_cast<uint*
> (img.bits ()) [i] = *cur;
250 icon.addPixmap (QPixmap::fromImage (img));
256 template<
typename Flag>
257 QFlags<Flag> XWrapper::GetFlagsList (
Window wid, Atom property,
const QHash<Atom, Flag>& atom2flag)
const
263 if (!GetWinProp (wid, property, &length,
reinterpret_cast<uchar**
> (&data), XA_ATOM))
266 for (ulong i = 0; i < length; ++i)
267 result |= atom2flag.value (data [i],
static_cast<Flag
> (0));
276 auto get = [
this] (
const QByteArray& atom) {
return GetAtom (
AsStringView (
"_NET_WM_STATE_" + atom)); };
277 static const QHash<Atom, WinStateFlag> atom2flag
293 return GetFlagsList (wid,
GetAtom (
"_NET_WM_STATE"), atom2flag);
298 auto get = [
this] (
const QByteArray& atom) {
return GetAtom (
AsStringView (
"_NET_WM_ACTION_" + atom)); };
299 static const QHash<Atom, AllowedActionFlag> atom2flag
315 return GetFlagsList (wid,
GetAtom (
"_NET_WM_ALLOWED_ACTIONS"), atom2flag);
320 auto win = GetActiveWindow ();
325 if (!
ShouldShow (win) && XGetTransientForHint (Display_, win, &transient))
335 return GetWinProp (wid,
GetAtom (
"WM_CLASS"), &length, data.Get ()) &&
336 QString (data.GetAs<
char*> (
false)).startsWith (
"leechcraft"_ql);
343 GetAtom (
"_NET_WM_WINDOW_TYPE_DESKTOP"),
344 GetAtom (
"_NET_WM_WINDOW_TYPE_DOCK"),
345 GetAtom (
"_NET_WM_WINDOW_TYPE_TOOLBAR"),
346 GetAtom (
"_NET_WM_WINDOW_TYPE_UTILITY"),
347 GetAtom (
"_NET_WM_WINDOW_TYPE_MENU"),
348 GetAtom (
"_NET_WM_WINDOW_TYPE_SPLASH"),
349 GetAtom (
"_NET_WM_WINDOW_TYPE_POPUP_MENU")
352 for (
const auto& type : GetWindowType (wid))
353 if (ignoreAtoms.contains (type))
360 if (!XGetTransientForHint (Display_, wid, &transient))
363 if (transient == 0 || transient == wid || transient == AppWin_)
366 return !GetWindowType (transient).contains (
GetAtom (
"_NET_WM_WINDOW_TYPE_NORMAL"));
374 XSelectInput (Display_, wid, PropertyChangeMask);
379 const auto wid = widget->effectiveWinId ();
381 const auto& winGeom = widget->geometry ();
385 case Qt::BottomToolBarArea:
387 0, 0, 0, winGeom.height (),
391 winGeom.left (), winGeom.right ());
393 case Qt::TopToolBarArea:
395 0, 0, winGeom.height (), 0,
398 winGeom.left (), winGeom.right (),
401 case Qt::LeftToolBarArea:
403 winGeom.width (), 0, 0, 0,
404 winGeom.top (), winGeom.bottom (),
409 case Qt::RightToolBarArea:
411 0, winGeom.width (), 0, 0,
413 winGeom.top (), winGeom.bottom (),
418 qWarning () << Q_FUNC_INFO
419 <<
"incorrect area passed"
427 const auto wid = w->effectiveWinId ();
428 XDeleteProperty (Display_, wid,
GetAtom (
"_NET_WM_STRUT"));
429 XDeleteProperty (Display_, wid,
GetAtom (
"_NET_WM_STRUT_PARTIAL"));
433 ulong left, ulong right, ulong top, ulong bottom,
434 ulong leftStartY, ulong leftEndY,
435 ulong rightStartY, ulong rightEndY,
436 ulong topStartX, ulong topEndX,
437 ulong bottomStartX, ulong bottomEndX)
457 XChangeProperty (Display_, wid,
GetAtom (
"_NET_WM_STRUT_PARTIAL"),
458 XA_CARDINAL, 32, PropModeReplace,
reinterpret_cast<uchar*
> (struts), 12);
460 XChangeProperty (Display_, wid,
GetAtom (
"_NET_WM_STRUT"),
461 XA_CARDINAL, 32, PropModeReplace,
reinterpret_cast<uchar*
> (struts), 4);
471 SendMessage (wid,
GetAtom (
"WM_CHANGE_STATE"), IconicState);
477 GetAtom (
"_NET_WM_STATE_MAXIMIZED_VERT"),
478 GetAtom (
"_NET_WM_STATE_MAXIMIZED_HORZ"),
485 GetAtom (
"_NET_WM_STATE_MAXIMIZED_VERT"),
486 GetAtom (
"_NET_WM_STATE_MAXIMIZED_HORZ"),
492 XResizeWindow (Display_, wid, width, height);
497 SendMessage (wid,
GetAtom (
"_NET_WM_STATE"),
503 SendMessage (wid,
GetAtom (
"_NET_WM_STATE"),
512 SendMessage (wid,
GetAtom (
"_NET_WM_STATE"), top,
515 SendMessage (wid,
GetAtom (
"_NET_WM_STATE"), bottom,
525 void XWrapper::HandlePropNotify (T ev)
527 if (ev->state == XCB_PROPERTY_DELETE)
530 const auto wid = ev->window;
534 if (ev->atom ==
GetAtom (
"_NET_CLIENT_LIST"))
536 else if (ev->atom ==
GetAtom (
"_NET_ACTIVE_WINDOW"))
538 else if (ev->atom ==
GetAtom (
"_NET_CURRENT_DESKTOP"))
543 if (ev->atom ==
GetAtom (
"_NET_WM_VISIBLE_NAME") ||
544 ev->atom ==
GetAtom (
"WM_NAME"))
546 else if (ev->atom ==
GetAtom (
"_NET_WM_ICON"))
548 else if (ev->atom ==
GetAtom (
"_NET_WM_DESKTOP"))
550 else if (ev->atom ==
GetAtom (
"_NET_WM_STATE"))
552 else if (ev->atom ==
GetAtom (
"_NET_WM_ALLOWED_ACTIONS"))
557 Window XWrapper::GetActiveWindow ()
562 if (!GetRootWinProp (
GetAtom (
"_NET_ACTIVE_WINDOW"), &length, data.GetAs<uchar**> (), XA_WINDOW))
576 if (GetRootWinProp (
GetAtom (
"_NET_NUMBER_OF_DESKTOPS"), &length, data.GetAs<uchar**> (), XA_CARDINAL))
577 return length > 0 ? data [0] : -1;
587 if (GetRootWinProp (
GetAtom (
"_NET_CURRENT_DESKTOP"), &length, data.GetAs<uchar**> (), XA_CARDINAL))
588 return length > 0 ? data [0] : -1;
595 SendMessage (AppWin_,
GetAtom (
"_NET_CURRENT_DESKTOP"), desktop);
603 if (!GetRootWinProp (
GetAtom (
"_NET_DESKTOP_NAMES"),
604 &length, data.GetAs<uchar**> (),
GetAtom (
"UTF8_STRING")))
611 for (
char *pos = data.GetAs<
char*> (
false), *end = data.GetAs<
char*> (
false) + length; pos < end; )
613 const auto& str = QString::fromUtf8 (pos);
615 pos += str.toUtf8 ().size () + 1;
629 if (GetWinProp (wid,
GetAtom (
"_NET_WM_DESKTOP"), &length, data.GetAs<uchar**> (), XA_CARDINAL) && length)
632 if (GetWinProp (wid,
GetAtom (
"_WIN_WORKSPACE"), &length, data.GetAs<uchar**> (), XA_CARDINAL) && length)
640 unsigned long data = num;
641 XChangeProperty (Display_,
647 reinterpret_cast<unsigned char*
> (&data),
653 auto available = screen.geometry ();
654 const auto deskGeom = screen.virtualGeometry ();
659 Guarded<ulong> struts;
660 const auto status = GetWinProp (wid,
GetAtom (
"_NET_WM_STRUT_PARTIAL"),
661 &length, struts.GetAs<uchar**> (), XA_CARDINAL);
662 if (!status || length != 12)
667 static_cast<int> (deskGeom.x ()),
668 static_cast<int> (deskGeom.y () + struts [4]),
669 static_cast<int> (struts [0]),
670 static_cast<int> (struts [5] - struts [4])
672 if (available.intersects (left))
673 available.setX (left.width ());
677 static_cast<int> (deskGeom.x () + deskGeom.width () - struts [1]),
678 static_cast<int> (deskGeom.y () + struts [6]),
679 static_cast<int> (struts [1]),
680 static_cast<int> (struts [7] - struts [6])
682 if (available.intersects (right))
683 available.setWidth (right.x () - available.x ());
687 static_cast<int> (deskGeom.x () + struts [8]),
688 static_cast<int> (deskGeom.y ()),
689 static_cast<int> (struts [9] - struts [8]),
690 static_cast<int> (struts [2])
692 if (available.intersects (top))
693 available.setY (top.height ());
697 static_cast<int> (deskGeom.x () + struts [10]),
698 static_cast<int> (deskGeom.y () + deskGeom.height () - struts [3]),
699 static_cast<int> (struts [11] - struts [10]),
700 static_cast<int> (struts [3])
702 if (available.intersects (bottom))
703 available.setHeight (bottom.y () - available.y ());
717 if (pos != Atoms_.end ())
720 auto atom = XInternAtom (Display_, name.data (),
false);
725 bool XWrapper::GetWinProp (
Window win, Atom property,
726 ulong *length,
unsigned char **result, Atom req)
const
729 ulong type = 0, rest = 0;
730 return XGetWindowProperty (Display_, win,
731 property, 0, 1024,
false, req, &type,
732 &fmt, length, &rest, result) == Success;
735 bool XWrapper::GetRootWinProp (Atom property,
736 ulong *length, uchar **result, Atom req)
const
738 return GetWinProp (AppWin_, property, length, result, req);
746 ulong *data =
nullptr;
748 if (!GetWinProp (wid,
GetAtom (
"_NET_WM_WINDOW_TYPE"),
749 &length,
reinterpret_cast<uchar**
> (&data)))
752 for (ulong i = 0; i < length; ++i)
759 bool XWrapper::SendMessage (
Window wid, Atom atom, ulong d0, ulong d1, ulong d2, ulong d3, ulong d4)
762 msg.xclient.window = wid;
763 msg.xclient.type = ClientMessage;
764 msg.xclient.message_type = atom;
765 msg.xclient.send_event =
true;
766 msg.xclient.display = Display_;
767 msg.xclient.format = 32;
768 msg.xclient.data.l [0] = d0;
769 msg.xclient.data.l [1] = d1;
770 msg.xclient.data.l [2] = d2;
771 msg.xclient.data.l [3] = d3;
772 msg.xclient.data.l [4] = d4;
774 auto flags = SubstructureRedirectMask | SubstructureNotifyMask;
775 return XSendEvent (Display_, AppWin_,
false, flags, &msg) == Success;
778 void XWrapper::initialize ()
static std::optional< XWrapper & > Instance()
QList< Window > GetWindows()
QRect GetAvailableGeometry(QScreen &)
void activeWindowChanged()
xcb_connection_t * GetConnection() const
QStringList GetDesktopNames()
void UnshadeWindow(Window)
void windowIconChanged(ulong)
Window GetRootWindow() const
void MinimizeWindow(Window)
XWrapper(xcb_connection_t *, Display *, PrivateCtor)
QString GetDesktopName(int, const QString &=QString())
void windowNameChanged(ulong)
void UnmaximizeWindow(Window)
QIcon GetWindowIcon(Window)
void windowDesktopChanged(ulong)
void ResizeWindow(Window, int, int)
void windowActionsChanged(ulong)
void MaximizeWindow(Window)
bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override
AllowedActionFlags GetWindowActions(Window)
int GetWindowDesktop(Window)
void MoveWindowTo(Window, Layer)
void SetStrut(QWidget *, Qt::ToolBarArea)
void windowStateChanged(ulong)
void SetCurrentDesktop(int)
WinStateFlags GetWindowState(Window)
void ClearStrut(QWidget *)
Atom GetAtom(std::string_view str)
Returns the atom denoting the given string.
QString GetWindowTitle(Window)
Display * GetDisplay() const
void MoveWindowToDesktop(Window, int)
constexpr decltype(auto) Get(const Seq &seq)
std::string_view AsStringView(const QByteArray &arr) noexcept
Create a std::string_view referring the data within a QByteArray.
QByteArray ToByteArray() noexcept
QByteArray AsByteArray(std::string_view view) noexcept
Convert the view into a QByteArray without copying.