11 #include <type_traits>
16 #include <QApplication>
18 #include <QDesktopWidget>
19 #include <QAbstractEventDispatcher>
23 #include <X11/Xutil.h>
24 #include <X11/Xatom.h>
36 : Display_ (QX11Info::display ())
37 , AppWin_ (QX11Info::appRootWindow ())
39 QAbstractEventDispatcher::instance ()->installNativeEventFilter (
this);
41 const uint32_t rootEvents [] =
43 XCB_EVENT_MASK_STRUCTURE_NOTIFY |
44 XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
45 XCB_EVENT_MASK_PROPERTY_CHANGE
47 xcb_change_window_attributes (QX11Info::connection (),
48 AppWin_, XCB_CW_EVENT_MASK, rootEvents);
69 if (eventType !=
"xcb_generic_event_t")
72 const auto ev =
static_cast<xcb_generic_event_t*
> (msg);
73 if ((ev->response_type & ~0x80) == XCB_PROPERTY_NOTIFY)
74 HandlePropNotify (
static_cast<xcb_property_notify_event_t*
> (msg));
82 struct IsDoublePtr : std::false_type {};
85 struct IsDoublePtr<T**> : std::true_type {};
94 Guarded (
const Guarded&) =
delete;
95 Guarded& operator= (
const Guarded&) =
delete;
97 Guarded (Guarded&& other) noexcept
98 : Data_ { other.Data_ }
100 other.Data_ =
nullptr;
103 Guarded& operator= (Guarded&& other) noexcept
115 T** Get (
bool clear =
true) noexcept
123 U GetAs (
bool clear =
true) noexcept
127 return IsDoublePtr<U>::value ?
128 reinterpret_cast<U
> (&Data_) :
129 reinterpret_cast<U
> (Data_);
132 T operator[] (
size_t idx)
const noexcept
137 T& operator[] (
size_t idx) noexcept
142 explicit operator bool () const noexcept
144 return Data_ !=
nullptr;
147 bool operator! () const noexcept
157 XSync (Display_, False);
163 Guarded<Window> data;
166 if (GetRootWinProp (
GetAtom (
"_NET_CLIENT_LIST"), &length, data.GetAs<uchar**> ()))
167 for (ulong i = 0; i < length; ++i)
179 auto utf8Str =
GetAtom (
"UTF8_STRING");
181 if (GetWinProp (wid,
GetAtom (
"_NET_WM_VISIBLE_NAME"), &length, data.Get (), utf8Str))
182 name = QString::fromUtf8 (data.GetAs<
char*> (
false));
184 if (name.isEmpty () && GetWinProp (wid,
GetAtom (
"_NET_WM_NAME"), &length, data.Get (), utf8Str))
185 name = QString::fromUtf8 (data.GetAs<
char*> (
false));
187 if (name.isEmpty () && GetWinProp (wid,
GetAtom (
"XA_WM_NAME"), &length, data.Get (), XA_STRING))
188 name = QString::fromUtf8 (data.GetAs<
char*> (
false));
192 XFetchName (Display_, wid, data.GetAs<
char**> ());
193 name = QString (data.GetAs<
char*> (
false));
199 if (XGetWMName (Display_, wid, &prop))
201 name = QString::fromUtf8 (
reinterpret_cast<char*
> (prop.value));
212 ulong type,
count, extra;
215 XGetWindowProperty (Display_, wid,
GetAtom (
"_NET_WM_ICON"),
217 &type, &fmt, &
count, &extra,
218 data.GetAs<uchar**> ());
225 auto cur = *data.Get (
false);
226 auto end = cur +
count;
229 QImage img (cur [0], cur [1], QImage::Format_ARGB32);
231 const auto bytesCount = img.sizeInBytes ();
232 for (
int i = 0; i < bytesCount / 4; ++i, ++cur)
233 reinterpret_cast<uint*
> (img.bits ()) [i] = *cur;
235 icon.addPixmap (QPixmap::fromImage (img));
241 template<
typename Flag>
242 QFlags<Flag> XWrapper::GetFlagsList (
Window wid, Atom property,
const QHash<Atom, Flag>& atom2flag)
const
248 if (!GetWinProp (wid, property, &length,
reinterpret_cast<uchar**
> (&data), XA_ATOM))
251 for (ulong i = 0; i < length; ++i)
252 result |= atom2flag.value (data [i],
static_cast<Flag
> (0));
261 auto get = [
this] (
const QByteArray& atom) {
return GetAtom (
AsStringView (
"_NET_WM_STATE_" + atom)); };
262 static const QHash<Atom, WinStateFlag> atom2flag
278 return GetFlagsList (wid,
GetAtom (
"_NET_WM_STATE"), atom2flag);
283 auto get = [
this] (
const QByteArray& atom) {
return GetAtom (
AsStringView (
"_NET_WM_ACTION_" + atom)); };
284 static const QHash<Atom, AllowedActionFlag> atom2flag
300 return GetFlagsList (wid,
GetAtom (
"_NET_WM_ALLOWED_ACTIONS"), atom2flag);
305 auto win = GetActiveWindow ();
310 if (!
ShouldShow (win) && XGetTransientForHint (Display_, win, &
transient))
320 return GetWinProp (wid,
GetAtom (
"WM_CLASS"), &length, data.Get ()) &&
321 QString (data.GetAs<
char*> (
false)).startsWith (
"leechcraft"_ql);
328 GetAtom (
"_NET_WM_WINDOW_TYPE_DESKTOP"),
329 GetAtom (
"_NET_WM_WINDOW_TYPE_DOCK"),
331 GetAtom (
"_NET_WM_WINDOW_TYPE_UTILITY"),
332 GetAtom (
"_NET_WM_WINDOW_TYPE_MENU"),
333 GetAtom (
"_NET_WM_WINDOW_TYPE_SPLASH"),
334 GetAtom (
"_NET_WM_WINDOW_TYPE_POPUP_MENU")
337 for (
const auto& type : GetWindowType (wid))
338 if (ignoreAtoms.contains (type))
345 if (!XGetTransientForHint (Display_, wid, &
transient))
348 if (
transient == 0 ||
transient == wid ||
transient == AppWin_)
351 return !GetWindowType (
transient).contains (
GetAtom (
"_NET_WM_WINDOW_TYPE_NORMAL"));
359 XSelectInput (Display_, wid, PropertyChangeMask);
364 const auto wid = widget->effectiveWinId ();
366 const auto& winGeom = widget->geometry ();
370 case Qt::BottomToolBarArea:
372 0, 0, 0, winGeom.height (),
376 winGeom.left (), winGeom.right ());
378 case Qt::TopToolBarArea:
380 0, 0, winGeom.height (), 0,
383 winGeom.left (), winGeom.right (),
386 case Qt::LeftToolBarArea:
388 winGeom.width (), 0, 0, 0,
389 winGeom.top (), winGeom.bottom (),
394 case Qt::RightToolBarArea:
396 0, winGeom.width (), 0, 0,
398 winGeom.top (), winGeom.bottom (),
403 qWarning () << Q_FUNC_INFO
404 <<
"incorrect area passed"
412 const auto wid = w->effectiveWinId ();
413 XDeleteProperty (Display_, wid,
GetAtom (
"_NET_WM_STRUT"));
414 XDeleteProperty (Display_, wid,
GetAtom (
"_NET_WM_STRUT_PARTIAL"));
418 ulong left, ulong right, ulong top, ulong bottom,
419 ulong leftStartY, ulong leftEndY,
420 ulong rightStartY, ulong rightEndY,
421 ulong topStartX, ulong topEndX,
422 ulong bottomStartX, ulong bottomEndX)
442 XChangeProperty (Display_, wid,
GetAtom (
"_NET_WM_STRUT_PARTIAL"),
443 XA_CARDINAL, 32, PropModeReplace,
reinterpret_cast<uchar*
> (struts), 12);
445 XChangeProperty (Display_, wid,
GetAtom (
"_NET_WM_STRUT"),
446 XA_CARDINAL, 32, PropModeReplace,
reinterpret_cast<uchar*
> (struts), 4);
456 SendMessage (wid,
GetAtom (
"WM_CHANGE_STATE"), IconicState);
462 GetAtom (
"_NET_WM_STATE_MAXIMIZED_VERT"),
463 GetAtom (
"_NET_WM_STATE_MAXIMIZED_HORZ"),
470 GetAtom (
"_NET_WM_STATE_MAXIMIZED_VERT"),
471 GetAtom (
"_NET_WM_STATE_MAXIMIZED_HORZ"),
477 XResizeWindow (Display_, wid, width, height);
482 SendMessage (wid,
GetAtom (
"_NET_WM_STATE"),
488 SendMessage (wid,
GetAtom (
"_NET_WM_STATE"),
497 SendMessage (wid,
GetAtom (
"_NET_WM_STATE"), top,
500 SendMessage (wid,
GetAtom (
"_NET_WM_STATE"), bottom,
510 void XWrapper::HandlePropNotify (T ev)
512 if (ev->state == XCB_PROPERTY_DELETE)
515 const auto wid = ev->window;
519 if (ev->atom ==
GetAtom (
"_NET_CLIENT_LIST"))
521 else if (ev->atom ==
GetAtom (
"_NET_ACTIVE_WINDOW"))
523 else if (ev->atom ==
GetAtom (
"_NET_CURRENT_DESKTOP"))
528 if (ev->atom ==
GetAtom (
"_NET_WM_VISIBLE_NAME") ||
529 ev->atom ==
GetAtom (
"WM_NAME"))
531 else if (ev->atom ==
GetAtom (
"_NET_WM_ICON"))
533 else if (ev->atom ==
GetAtom (
"_NET_WM_DESKTOP"))
535 else if (ev->atom ==
GetAtom (
"_NET_WM_STATE"))
537 else if (ev->atom ==
GetAtom (
"_NET_WM_ALLOWED_ACTIONS"))
542 Window XWrapper::GetActiveWindow ()
547 if (!GetRootWinProp (
GetAtom (
"_NET_ACTIVE_WINDOW"), &length, data.GetAs<uchar**> (), XA_WINDOW))
561 if (GetRootWinProp (
GetAtom (
"_NET_NUMBER_OF_DESKTOPS"), &length, data.GetAs<uchar**> (), XA_CARDINAL))
562 return length > 0 ? data [0] : -1;
572 if (GetRootWinProp (
GetAtom (
"_NET_CURRENT_DESKTOP"), &length, data.GetAs<uchar**> (), XA_CARDINAL))
573 return length > 0 ? data [0] : -1;
580 SendMessage (AppWin_,
GetAtom (
"_NET_CURRENT_DESKTOP"), desktop);
588 if (!GetRootWinProp (
GetAtom (
"_NET_DESKTOP_NAMES"),
589 &length, data.GetAs<uchar**> (),
GetAtom (
"UTF8_STRING")))
596 for (
char *pos = data.GetAs<
char*> (
false), *end = data.GetAs<
char*> (
false) + length; pos < end; )
598 const auto& str = QString::fromUtf8 (pos);
600 pos += str.toUtf8 ().size () + 1;
614 if (GetWinProp (wid,
GetAtom (
"_NET_WM_DESKTOP"), &length, data.GetAs<uchar**> (), XA_CARDINAL) && length)
617 if (GetWinProp (wid,
GetAtom (
"_WIN_WORKSPACE"), &length, data.GetAs<uchar**> (), XA_CARDINAL) && length)
625 unsigned long data = num;
626 XChangeProperty (QX11Info::display (),
632 reinterpret_cast<unsigned char*
> (&data),
638 auto dw = QApplication::desktop ();
640 const auto& screens = QGuiApplication::screens ();
641 auto screen = screens.value (screenIdx, QGuiApplication::primaryScreen ());
643 auto available = screen->geometry ();
644 const auto deskGeom = dw->rect ();
649 Guarded<ulong> struts;
650 const auto status = GetWinProp (wid,
GetAtom (
"_NET_WM_STRUT_PARTIAL"),
651 &length, struts.GetAs<uchar**> (), XA_CARDINAL);
652 if (!status || length != 12)
657 static_cast<int> (deskGeom.x ()),
658 static_cast<int> (deskGeom.y () + struts [4]),
659 static_cast<int> (struts [0]),
660 static_cast<int> (struts [5] - struts [4])
662 if (available.intersects (left))
663 available.setX (left.width ());
667 static_cast<int> (deskGeom.x () + deskGeom.width () - struts [1]),
668 static_cast<int> (deskGeom.y () + struts [6]),
669 static_cast<int> (struts [1]),
670 static_cast<int> (struts [7] - struts [6])
672 if (available.intersects (right))
673 available.setWidth (right.x () - available.x ());
677 static_cast<int> (deskGeom.x () + struts [8]),
678 static_cast<int> (deskGeom.y ()),
679 static_cast<int> (struts [9] - struts [8]),
680 static_cast<int> (struts [2])
682 if (available.intersects (top))
683 available.setY (top.height ());
687 static_cast<int> (deskGeom.x () + struts [10]),
688 static_cast<int> (deskGeom.y () + deskGeom.height () - struts [3]),
689 static_cast<int> (struts [11] - struts [10]),
690 static_cast<int> (struts [3])
692 if (available.intersects (bottom))
693 available.setHeight (bottom.y () - available.y ());
707 if (pos != Atoms_.end ())
710 auto atom = XInternAtom (Display_, name.data (),
false);
715 bool XWrapper::GetWinProp (
Window win, Atom property,
716 ulong *length,
unsigned char **result, Atom req)
const
719 ulong type = 0, rest = 0;
720 return XGetWindowProperty (Display_, win,
721 property, 0, 1024,
false, req, &type,
722 &fmt, length, &rest, result) == Success;
725 bool XWrapper::GetRootWinProp (Atom property,
726 ulong *length, uchar **result, Atom req)
const
728 return GetWinProp (AppWin_, property, length, result, req);
736 ulong *data =
nullptr;
738 if (!GetWinProp (wid,
GetAtom (
"_NET_WM_WINDOW_TYPE"),
739 &length,
reinterpret_cast<uchar**
> (&data)))
742 for (ulong i = 0; i < length; ++i)
749 bool XWrapper::SendMessage (
Window wid, Atom atom, ulong d0, ulong d1, ulong d2, ulong d3, ulong d4)
752 msg.xclient.window = wid;
753 msg.xclient.type = ClientMessage;
754 msg.xclient.message_type = atom;
755 msg.xclient.send_event =
true;
756 msg.xclient.display = Display_;
757 msg.xclient.format = 32;
758 msg.xclient.data.l [0] = d0;
759 msg.xclient.data.l [1] = d1;
760 msg.xclient.data.l [2] = d2;
761 msg.xclient.data.l [3] = d3;
762 msg.xclient.data.l [4] = d4;
764 auto flags = SubstructureRedirectMask | SubstructureNotifyMask;
765 return XSendEvent (Display_, AppWin_,
false, flags, &msg) == Success;
768 void XWrapper::initialize ()