32 #include <type_traits>
36 #include <QApplication>
38 #include <QDesktopWidget>
39 #include <QAbstractEventDispatcher>
43 #include <X11/Xutil.h>
44 #include <X11/Xatom.h>
58 : Display_ (QX11Info::display ())
59 , AppWin_ (QX11Info::appRootWindow ())
61 QAbstractEventDispatcher::instance ()->installNativeEventFilter (
this);
63 const uint32_t rootEvents [] =
65 XCB_EVENT_MASK_STRUCTURE_NOTIFY |
66 XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
67 XCB_EVENT_MASK_PROPERTY_CHANGE
69 xcb_change_window_attributes (QX11Info::connection (),
70 AppWin_, XCB_CW_EVENT_MASK, rootEvents);
73 XWrapper& XWrapper::Instance ()
79 Display* XWrapper::GetDisplay ()
const
84 Window XWrapper::GetRootWindow ()
const
89 bool XWrapper::nativeEventFilter (
const QByteArray& eventType,
void *msg,
long int*)
91 if (eventType !=
"xcb_generic_event_t")
94 const auto ev = static_cast<xcb_generic_event_t*> (msg);
95 if ((ev->response_type & ~0x80) == XCB_PROPERTY_NOTIFY)
96 HandlePropNotify (static_cast<xcb_property_notify_event_t*> (msg));
104 struct IsDoublePtr : std::false_type {};
107 struct IsDoublePtr<T**> : std::true_type {};
125 T** Get (
bool clear =
true)
133 U GetAs (
bool clear =
true)
137 return IsDoublePtr<U>::value ?
138 reinterpret_cast<U> (&Data_) :
139 reinterpret_cast<U> (Data_);
142 T operator[] (
size_t idx)
const
147 T& operator[] (
size_t idx)
152 operator bool ()
const
154 return Data_ !=
nullptr;
157 bool operator! ()
const
164 void XWrapper::Sync ()
167 XSync (Display_, False);
173 Guarded<Window> data;
176 if (GetRootWinProp (GetAtom (
"_NET_CLIENT_LIST"), &length, data.GetAs<uchar**> ()))
177 for (ulong i = 0; i < length; ++i)
182 QString XWrapper::GetWindowTitle (
Window wid)
189 auto utf8Str = GetAtom (
"UTF8_STRING");
191 if (GetWinProp (wid, GetAtom (
"_NET_WM_VISIBLE_NAME"), &length, data.Get (), utf8Str))
192 name = QString::fromUtf8 (data.GetAs<
char*> (
false));
195 if (GetWinProp (wid, GetAtom (
"_NET_WM_NAME"), &length, data.Get (), utf8Str))
196 name = QString::fromUtf8 (data.GetAs<
char*> (
false));
199 if (GetWinProp (wid, GetAtom (
"XA_WM_NAME"), &length, data.Get (), XA_STRING))
200 name = QString::fromUtf8 (data.GetAs<
char*> (
false));
204 XFetchName (Display_, wid, data.GetAs<
char**> ());
205 name = QString (data.GetAs<
char*> (
false));
211 if (XGetWMName (Display_, wid, &prop))
213 name = QString::fromUtf8 (reinterpret_cast<char*> (prop.value));
221 QIcon XWrapper::GetWindowIcon (
Window wid)
224 ulong type,
count, extra;
227 XGetWindowProperty (Display_, wid, GetAtom (
"_NET_WM_ICON"),
229 &type, &fmt, &
count, &extra,
230 data.GetAs<uchar**> ());
237 auto cur = *data.Get (
false);
238 auto end = cur +
count;
241 QImage img (cur [0], cur [1], QImage::Format_ARGB32);
244 for (
int i = 0; i < bytesCount / 4; ++i, ++cur)
245 reinterpret_cast<uint*> (img.bits ()) [i] = *cur;
247 icon.addPixmap (QPixmap::fromImage (img));
253 WinStateFlags XWrapper::GetWindowState (
Window wid)
255 WinStateFlags result;
259 if (!GetWinProp (wid, GetAtom (
"_NET_WM_STATE"),
260 &length, reinterpret_cast<uchar**> (&data), XA_ATOM))
263 for (ulong i = 0; i < length; ++i)
265 const auto curAtom = data [i];
267 auto set = [
this, &curAtom, &result] (
const QString& atom,
WinStateFlag flag)
269 if (curAtom == GetAtom (
"_NET_WM_STATE_" + atom))
292 AllowedActionFlags XWrapper::GetWindowActions (
Window wid)
294 AllowedActionFlags result;
298 if (!GetWinProp (wid, GetAtom (
"_NET_WM_ALLOWED_ACTIONS"),
299 &length, reinterpret_cast<uchar**> (&data), XA_ATOM))
302 for (ulong i = 0; i < length; ++i)
304 const auto curAtom = data [i];
306 auto set = [
this, &curAtom, &result] (
const QString& atom,
AllowedActionFlag flag)
308 if (curAtom == GetAtom (
"_NET_WM_ACTION_" + atom))
331 Window XWrapper::GetActiveApp ()
333 auto win = GetActiveWindow ();
338 if (!ShouldShow (win) && XGetTransientForHint (Display_, win, &
transient))
348 if (GetWinProp (wid, GetAtom (
"WM_CLASS"), &length, data.Get ()) &&
349 QString (data.GetAs<
char*> (
false)).startsWith (
"leechcraft"))
355 bool XWrapper::ShouldShow (
Window wid)
359 GetAtom (
"_NET_WM_WINDOW_TYPE_DESKTOP"),
360 GetAtom (
"_NET_WM_WINDOW_TYPE_DOCK"),
361 GetAtom (
"_NET_WM_WINDOW_TYPE_TOOLBAR"),
362 GetAtom (
"_NET_WM_WINDOW_TYPE_UTILITY"),
363 GetAtom (
"_NET_WM_WINDOW_TYPE_MENU"),
364 GetAtom (
"_NET_WM_WINDOW_TYPE_SPLASH"),
365 GetAtom (
"_NET_WM_WINDOW_TYPE_POPUP_MENU")
368 for (
const auto& type : GetWindowType (wid))
369 if (ignoreAtoms.contains (type))
376 if (!XGetTransientForHint (Display_, wid, &
transient))
379 if (
transient == 0 ||
transient == wid ||
transient == AppWin_)
382 return !GetWindowType (
transient).contains (GetAtom (
"_NET_WM_WINDOW_TYPE_NORMAL"));
385 void XWrapper::Subscribe (
Window wid)
387 if (IsLCWindow (wid))
390 XSelectInput (Display_, wid, PropertyChangeMask);
393 void XWrapper::SetStrut (QWidget *widget, Qt::ToolBarArea area)
395 const auto wid = widget->effectiveWinId ();
397 const auto& winGeom = widget->geometry ();
401 case Qt::BottomToolBarArea:
403 0, 0, 0, winGeom.height (),
407 winGeom.left (), winGeom.right ());
409 case Qt::TopToolBarArea:
411 0, 0, winGeom.height (), 0,
414 winGeom.left (), winGeom.right (),
417 case Qt::LeftToolBarArea:
419 winGeom.width (), 0, 0, 0,
420 winGeom.top (), winGeom.bottom (),
425 case Qt::RightToolBarArea:
427 0, winGeom.width (), 0, 0,
429 winGeom.top (), winGeom.bottom (),
434 qWarning () << Q_FUNC_INFO
435 <<
"incorrect area passed"
441 void XWrapper::ClearStrut (QWidget *w)
443 const auto wid = w->effectiveWinId ();
444 XDeleteProperty (Display_, wid, GetAtom (
"_NET_WM_STRUT"));
445 XDeleteProperty (Display_, wid, GetAtom (
"_NET_WM_STRUT_PARTIAL"));
448 void XWrapper::SetStrut (
Window wid,
449 int left,
int right,
int top,
int bottom,
450 int leftStartY,
int leftEndY,
451 int rightStartY,
int rightEndY,
452 int topStartX,
int topEndX,
453 int bottomStartX,
int bottomEndX)
455 ulong struts[12] = { 0 };
462 struts [4] = leftStartY;
463 struts [5] = leftEndY;
464 struts [6] = rightStartY;
465 struts [7] = rightEndY;
466 struts [8] = topStartX;
467 struts [9] = topEndX;
468 struts [10] = bottomStartX;
469 struts [11] = bottomEndX;
471 XChangeProperty (Display_, wid, GetAtom (
"_NET_WM_STRUT_PARTIAL"),
472 XA_CARDINAL, 32, PropModeReplace, reinterpret_cast<uchar*> (struts), 12);
474 XChangeProperty (Display_, wid, GetAtom (
"_NET_WM_STRUT"),
475 XA_CARDINAL, 32, PropModeReplace, reinterpret_cast<uchar*> (struts), 4);
478 void XWrapper::RaiseWindow (
Window wid)
480 SendMessage (wid, GetAtom (
"_NET_ACTIVE_WINDOW"),
SourcePager);
483 void XWrapper::MinimizeWindow (
Window wid)
485 SendMessage (wid, GetAtom (
"WM_CHANGE_STATE"), IconicState);
488 void XWrapper::MaximizeWindow (
Window wid)
490 SendMessage (wid, GetAtom (
"_NET_WM_STATE"),
StateAdd,
491 GetAtom (
"_NET_WM_STATE_MAXIMIZED_VERT"),
492 GetAtom (
"_NET_WM_STATE_MAXIMIZED_HORZ"),
496 void XWrapper::UnmaximizeWindow (
Window wid)
498 SendMessage (wid, GetAtom (
"_NET_WM_STATE"),
StateRemove,
499 GetAtom (
"_NET_WM_STATE_MAXIMIZED_VERT"),
500 GetAtom (
"_NET_WM_STATE_MAXIMIZED_HORZ"),
504 void XWrapper::ResizeWindow (
Window wid,
int width,
int height)
506 XResizeWindow (Display_, wid, width, height);
509 void XWrapper::ShadeWindow (
Window wid)
511 SendMessage (wid, GetAtom (
"_NET_WM_STATE"),
515 void XWrapper::UnshadeWindow (
Window wid)
517 SendMessage (wid, GetAtom (
"_NET_WM_STATE"),
521 void XWrapper::MoveWindowTo (
Window wid, Layer layer)
526 SendMessage (wid, GetAtom (
"_NET_WM_STATE"), top,
529 SendMessage (wid, GetAtom (
"_NET_WM_STATE"), bottom,
533 void XWrapper::CloseWindow (
Window wid)
539 void XWrapper::HandlePropNotify (T ev)
541 if (ev->state == XCB_PROPERTY_DELETE)
544 const auto wid = ev->window;
548 if (ev->atom == GetAtom (
"_NET_CLIENT_LIST"))
549 emit windowListChanged ();
550 else if (ev->atom == GetAtom (
"_NET_ACTIVE_WINDOW"))
551 emit activeWindowChanged ();
552 else if (ev->atom == GetAtom (
"_NET_CURRENT_DESKTOP"))
553 emit desktopChanged ();
557 if (ev->atom == GetAtom (
"_NET_WM_VISIBLE_NAME") ||
558 ev->atom == GetAtom (
"WM_NAME"))
559 emit windowNameChanged (wid);
560 else if (ev->atom == GetAtom (
"_NET_WM_ICON"))
561 emit windowIconChanged (wid);
562 else if (ev->atom == GetAtom (
"_NET_WM_DESKTOP"))
563 emit windowDesktopChanged (wid);
564 else if (ev->atom == GetAtom (
"_NET_WM_STATE"))
565 emit windowStateChanged (wid);
566 else if (ev->atom == GetAtom (
"_NET_WM_ALLOWED_ACTIONS"))
567 emit windowActionsChanged (wid);
571 Window XWrapper::GetActiveWindow ()
576 if (!GetRootWinProp (GetAtom (
"_NET_ACTIVE_WINDOW"), &length, data.GetAs<uchar**> (), XA_WINDOW))
585 int XWrapper::GetDesktopCount ()
590 if (GetRootWinProp (GetAtom (
"_NET_NUMBER_OF_DESKTOPS"), &length, data.GetAs<uchar**> (), XA_CARDINAL))
591 return length > 0 ? data [0] : -1;
596 int XWrapper::GetCurrentDesktop ()
601 if (GetRootWinProp (GetAtom (
"_NET_CURRENT_DESKTOP"), &length, data.GetAs<uchar**> (), XA_CARDINAL))
602 return length > 0 ? data [0] : -1;
607 void XWrapper::SetCurrentDesktop (
int desktop)
609 SendMessage (AppWin_, GetAtom (
"_NET_CURRENT_DESKTOP"), desktop);
612 QStringList XWrapper::GetDesktopNames ()
617 if (!GetRootWinProp (GetAtom (
"_NET_DESKTOP_NAMES"),
618 &length, data.GetAs<uchar**> (), GetAtom (
"UTF8_STRING")))
625 for (
char *pos = data.GetAs<
char*> (
false), *end = data.GetAs<
char*> (
false) + length; pos < end; )
627 const auto& str = QString::fromUtf8 (pos);
629 pos += str.toUtf8 ().size () + 1;
634 QString XWrapper::GetDesktopName (
int desktop,
const QString& def)
636 return GetDesktopNames ().value (desktop, def);
639 int XWrapper::GetWindowDesktop (
Window wid)
643 if (GetWinProp (wid, GetAtom (
"_NET_WM_DESKTOP"), &length, data.GetAs<uchar**> (), XA_CARDINAL) && length)
646 if (GetWinProp (wid, GetAtom (
"_WIN_WORKSPACE"), &length, data.GetAs<uchar**> (), XA_CARDINAL) && length)
652 void XWrapper::MoveWindowToDesktop (
Window wid,
int num)
654 unsigned long data = num;
655 XChangeProperty (QX11Info::display (),
657 GetAtom (
"_NET_WM_DESKTOP"),
661 reinterpret_cast<unsigned char*> (&data),
665 QRect XWrapper::GetAvailableGeometry (
int screenIdx)
667 auto dw = QApplication::desktop ();
669 const auto& screens = QGuiApplication::screens ();
670 auto screen = screens.value (screenIdx, QGuiApplication::primaryScreen ());
672 auto available = screen->geometry ();
673 const auto deskGeom = dw->rect ();
675 for (
const auto wid : GetWindows ())
678 Guarded<ulong> struts;
679 const auto status = GetWinProp (wid, GetAtom (
"_NET_WM_STRUT_PARTIAL"),
680 &length, struts.GetAs<uchar**> (), XA_CARDINAL);
681 if (!status || length != 12)
686 static_cast<int> (deskGeom.x ()),
687 static_cast<int> (deskGeom.y () + struts [4]),
688 static_cast<int> (struts [0]),
689 static_cast<int> (struts [5] - struts [4])
691 if (available.intersects (left))
692 available.setX (left.width ());
696 static_cast<int> (deskGeom.x () + deskGeom.width () - struts [1]),
697 static_cast<int> (deskGeom.y () + struts [6]),
698 static_cast<int> (struts [1]),
699 static_cast<int> (struts [7] - struts [6])
701 if (available.intersects (right))
702 available.setWidth (right.x () - available.x ());
706 static_cast<int> (deskGeom.x () + struts [8]),
707 static_cast<int> (deskGeom.y ()),
708 static_cast<int> (struts [9] - struts [8]),
709 static_cast<int> (struts [2])
711 if (available.intersects (top))
712 available.setY (top.height ());
716 static_cast<int> (deskGeom.x () + struts [10]),
717 static_cast<int> (deskGeom.y () + deskGeom.height () - struts [3]),
718 static_cast<int> (struts [11] - struts [10]),
719 static_cast<int> (struts [3])
721 if (available.intersects (bottom))
722 available.setHeight (bottom.y () - available.y ());
728 QRect XWrapper::GetAvailableGeometry (QWidget *widget)
730 return GetAvailableGeometry (QApplication::desktop ()->screenNumber (widget));
733 Atom XWrapper::GetAtom (
const QString& name)
735 if (Atoms_.contains (name))
736 return Atoms_ [name];
738 auto atom = XInternAtom (Display_, name.toLocal8Bit (),
false);
739 Atoms_ [name] = atom;
743 bool XWrapper::GetWinProp (
Window win, Atom property,
744 ulong *length,
unsigned char **result, Atom req)
const
747 ulong type = 0, rest = 0;
748 return XGetWindowProperty (Display_, win,
749 property, 0, 1024,
false, req, &type,
750 &fmt, length, &rest, result) == Success;
753 bool XWrapper::GetRootWinProp (Atom property,
754 ulong *length, uchar **result, Atom req)
const
756 return GetWinProp (AppWin_, property, length, result, req);
764 ulong *data =
nullptr;
766 if (!GetWinProp (wid, GetAtom (
"_NET_WM_WINDOW_TYPE"),
767 &length, reinterpret_cast<uchar**> (&data)))
770 for (ulong i = 0; i < length; ++i)
777 bool XWrapper::SendMessage (
Window wid, Atom atom, ulong d0, ulong d1, ulong d2, ulong d3, ulong d4)
780 msg.xclient.window = wid;
781 msg.xclient.type = ClientMessage;
782 msg.xclient.message_type = atom;
783 msg.xclient.send_event =
true;
784 msg.xclient.display = Display_;
785 msg.xclient.format = 32;
786 msg.xclient.data.l [0] = d0;
787 msg.xclient.data.l [1] = d1;
788 msg.xclient.data.l [2] = d2;
789 msg.xclient.data.l [3] = d3;
790 msg.xclient.data.l [4] = d4;
792 auto flags = SubstructureRedirectMask | SubstructureNotifyMask;
793 return XSendEvent (Display_, AppWin_,
false, flags, &msg) == Success;
796 void XWrapper::initialize ()