32 #include <type_traits>
36 #include <QApplication>
38 #include <QDesktopWidget>
39 #include <QAbstractEventDispatcher>
43 #include <X11/Xutil.h>
44 #include <X11/Xatom.h>
57 bool EvFilter (
void *msg)
65 , AppWin_ (QX11Info::appRootWindow ())
66 , PrevFilter_ (QAbstractEventDispatcher::instance ()->setEventFilter (EvFilter))
70 PropertyChangeMask | StructureNotifyMask | SubstructureNotifyMask);
79 Display* XWrapper::GetDisplay ()
const
84 Window XWrapper::GetRootWindow ()
const
91 if (ev->type == PropertyNotify)
92 HandlePropNotify (&ev->xproperty);
94 return PrevFilter_ ? PrevFilter_ (ev) :
false;
100 struct IsDoublePtr : std::false_type {};
103 struct IsDoublePtr<T**> : std::true_type {};
121 T** Get (
bool clear =
true)
129 U GetAs (
bool clear =
true)
133 return IsDoublePtr<U>::value ?
134 reinterpret_cast<U
> (&
Data_) :
135 reinterpret_cast<U> (Data_);
138 T operator[] (
size_t idx)
const
143 T& operator[] (
size_t idx)
148 operator bool ()
const
150 return Data_ !=
nullptr;
153 bool operator! ()
const
160 void XWrapper::Sync ()
169 Guarded<Window> data;
172 if (GetRootWinProp (GetAtom (
"_NET_CLIENT_LIST"), &length, data.GetAs<uchar**> ()))
173 for (ulong i = 0; i < length; ++i)
178 QString XWrapper::GetWindowTitle (
Window wid)
185 auto utf8Str = GetAtom (
"UTF8_STRING");
187 if (GetWinProp (wid, GetAtom (
"_NET_WM_VISIBLE_NAME"), &length, data.Get (), utf8Str))
188 name = QString::fromUtf8 (data.GetAs<
char*> (
false));
191 if (GetWinProp (wid, GetAtom (
"_NET_WM_NAME"), &length, data.Get (), utf8Str))
192 name = QString::fromUtf8 (data.GetAs<
char*> (
false));
195 if (GetWinProp (wid, GetAtom (
"XA_WM_NAME"), &length, data.Get (), XA_STRING))
196 name = QString::fromUtf8 (data.GetAs<
char*> (
false));
200 XFetchName (
Display_, wid, data.GetAs<
char**> ());
201 name = QString (data.GetAs<
char*> (
false));
207 if (XGetWMName (
Display_, wid, &prop))
209 name = QString::fromUtf8 (reinterpret_cast<char*> (prop.value));
217 QIcon XWrapper::GetWindowIcon (
Window wid)
220 ulong type, count, extra;
223 XGetWindowProperty (
Display_, wid, GetAtom (
"_NET_WM_ICON"),
224 0, std::numeric_limits<long>::max (), False, AnyPropertyType,
225 &type, &fmt, &count, &extra,
226 data.GetAs<uchar**> ());
233 auto cur = *data.Get (
false);
234 auto end = cur + count;
237 QImage img (cur [0], cur [1], QImage::Format_ARGB32);
239 for (
int i = 0; i < img.byteCount () / 4; ++i, ++cur)
240 reinterpret_cast<uint*> (img.bits ()) [i] = *cur;
242 icon.addPixmap (QPixmap::fromImage (img));
248 WinStateFlags XWrapper::GetWindowState (
Window wid)
250 WinStateFlags result;
254 if (!GetWinProp (wid, GetAtom (
"_NET_WM_STATE"),
255 &length, reinterpret_cast<uchar**> (&data), XA_ATOM))
258 for (ulong i = 0; i < length; ++i)
260 const auto curAtom = data [i];
262 auto set = [
this, &curAtom, &result] (
const QString& atom,
WinStateFlag flag)
264 if (curAtom == GetAtom (
"_NET_WM_STATE_" + atom))
287 AllowedActionFlags XWrapper::GetWindowActions (
Window wid)
289 AllowedActionFlags result;
293 if (!GetWinProp (wid, GetAtom (
"_NET_WM_ALLOWED_ACTIONS"),
294 &length, reinterpret_cast<uchar**> (&data), XA_ATOM))
297 for (ulong i = 0; i < length; ++i)
299 const auto curAtom = data [i];
301 auto set = [
this, &curAtom, &result] (
const QString& atom,
AllowedActionFlag flag)
303 if (curAtom == GetAtom (
"_NET_WM_ACTION_" + atom))
328 auto win = GetActiveWindow ();
333 if (!ShouldShow (win) && XGetTransientForHint (
Display_, win, &
transient))
343 if (GetWinProp (wid, GetAtom (
"WM_CLASS"), &length, data.Get ()) &&
344 QString (data.GetAs<
char*> (
false)) ==
"leechcraft")
354 GetAtom (
"_NET_WM_WINDOW_TYPE_DESKTOP"),
355 GetAtom (
"_NET_WM_WINDOW_TYPE_DOCK"),
356 GetAtom (
"_NET_WM_WINDOW_TYPE_TOOLBAR"),
357 GetAtom (
"_NET_WM_WINDOW_TYPE_UTILITY"),
358 GetAtom (
"_NET_WM_WINDOW_TYPE_MENU"),
359 GetAtom (
"_NET_WM_WINDOW_TYPE_SPLASH"),
360 GetAtom (
"_NET_WM_WINDOW_TYPE_POPUP_MENU")
363 for (
const auto& type : GetWindowType (wid))
364 if (ignoreAtoms.contains (type))
371 if (!XGetTransientForHint (
Display_, wid, &
transient))
374 if (
transient == 0 ||
transient == wid ||
transient == AppWin_)
377 return !GetWindowType (
transient).contains (GetAtom (
"_NET_WM_WINDOW_TYPE_NORMAL"));
382 if (IsLCWindow (wid))
385 XSelectInput (
Display_, wid, PropertyChangeMask);
388 void XWrapper::SetStrut (QWidget *widget, Qt::ToolBarArea area)
390 const auto wid = widget->effectiveWinId ();
392 const auto& winGeom = widget->geometry ();
396 case Qt::BottomToolBarArea:
398 0, 0, 0, winGeom.height (),
402 winGeom.left (), winGeom.right ());
404 case Qt::TopToolBarArea:
406 0, 0, winGeom.height (), 0,
409 winGeom.left (), winGeom.right (),
412 case Qt::LeftToolBarArea:
414 winGeom.width (), 0, 0, 0,
415 winGeom.top (), winGeom.bottom (),
420 case Qt::RightToolBarArea:
422 0, winGeom.width (), 0, 0,
424 winGeom.top (), winGeom.bottom (),
429 qWarning () << Q_FUNC_INFO
430 <<
"incorrect area passed"
436 void XWrapper::ClearStrut (QWidget *w)
438 const auto wid = w->effectiveWinId ();
439 XDeleteProperty (
Display_, wid, GetAtom (
"_NET_WM_STRUT"));
440 XDeleteProperty (
Display_, wid, GetAtom (
"_NET_WM_STRUT_PARTIAL"));
444 int left,
int right,
int top,
int bottom,
445 int leftStartY,
int leftEndY,
446 int rightStartY,
int rightEndY,
447 int topStartX,
int topEndX,
448 int bottomStartX,
int bottomEndX)
450 ulong struts[12] = { 0 };
457 struts [4] = leftStartY;
458 struts [5] = leftEndY;
459 struts [6] = rightStartY;
460 struts [7] = rightEndY;
461 struts [8] = topStartX;
462 struts [9] = topEndX;
463 struts [10] = bottomStartX;
464 struts [11] = bottomEndX;
466 XChangeProperty (
Display_, wid, GetAtom (
"_NET_WM_STRUT_PARTIAL"),
467 XA_CARDINAL, 32, PropModeReplace, reinterpret_cast<uchar*> (struts), 12);
469 XChangeProperty (
Display_, wid, GetAtom (
"_NET_WM_STRUT"),
470 XA_CARDINAL, 32, PropModeReplace, reinterpret_cast<uchar*> (struts), 4);
475 SendMessage (wid, GetAtom (
"_NET_ACTIVE_WINDOW"), SourcePager);
478 void XWrapper::MinimizeWindow (
Window wid)
480 SendMessage (wid, GetAtom (
"WM_CHANGE_STATE"), IconicState);
483 void XWrapper::MaximizeWindow (
Window wid)
485 SendMessage (wid, GetAtom (
"_NET_WM_STATE"), StateAdd,
486 GetAtom (
"_NET_WM_STATE_MAXIMIZED_VERT"),
487 GetAtom (
"_NET_WM_STATE_MAXIMIZED_HORZ"),
491 void XWrapper::UnmaximizeWindow (
Window wid)
493 SendMessage (wid, GetAtom (
"_NET_WM_STATE"), StateRemove,
494 GetAtom (
"_NET_WM_STATE_MAXIMIZED_VERT"),
495 GetAtom (
"_NET_WM_STATE_MAXIMIZED_HORZ"),
499 void XWrapper::ResizeWindow (
Window wid,
int width,
int height)
501 XResizeWindow (
Display_, wid, width, height);
506 SendMessage (wid, GetAtom (
"_NET_WM_STATE"),
507 StateAdd, GetAtom (
"_NET_WM_STATE_SHADED"), 0, SourcePager);
510 void XWrapper::UnshadeWindow (
Window wid)
512 SendMessage (wid, GetAtom (
"_NET_WM_STATE"),
513 StateRemove, GetAtom (
"_NET_WM_STATE_SHADED"), 0, SourcePager);
518 const auto top = layer == Layer::Top ? StateAdd :
StateRemove;
519 const auto bottom = layer == Layer::Bottom ? StateAdd :
StateRemove;
521 SendMessage (wid, GetAtom (
"_NET_WM_STATE"), top,
522 GetAtom (
"_NET_WM_STATE_ABOVE"), 0, SourcePager);
524 SendMessage (wid, GetAtom (
"_NET_WM_STATE"), bottom,
525 GetAtom (
"_NET_WM_STATE_BELOW"), 0, SourcePager);
530 SendMessage (wid, GetAtom (
"_NET_CLOSE_WINDOW"), 0, SourcePager);
534 void XWrapper::HandlePropNotify (T ev)
536 if (ev->state == PropertyDelete)
539 const auto wid = ev->window;
543 if (ev->atom == GetAtom (
"_NET_CLIENT_LIST"))
544 emit windowListChanged ();
545 else if (ev->atom == GetAtom (
"_NET_ACTIVE_WINDOW"))
546 emit activeWindowChanged ();
547 else if (ev->atom == GetAtom (
"_NET_CURRENT_DESKTOP"))
548 emit desktopChanged ();
552 if (ev->atom == GetAtom (
"_NET_WM_VISIBLE_NAME") ||
553 ev->atom == GetAtom (
"WM_NAME"))
554 emit windowNameChanged (wid);
555 else if (ev->atom == GetAtom (
"_NET_WM_ICON"))
556 emit windowIconChanged (wid);
557 else if (ev->atom == GetAtom (
"_NET_WM_DESKTOP"))
558 emit windowDesktopChanged (wid);
559 else if (ev->atom == GetAtom (
"_NET_WM_STATE"))
560 emit windowStateChanged (wid);
561 else if (ev->atom == GetAtom (
"_NET_WM_ALLOWED_ACTIONS"))
562 emit windowActionsChanged (wid);
566 Window XWrapper::GetActiveWindow ()
571 if (!GetRootWinProp (GetAtom (
"_NET_ACTIVE_WINDOW"), &length, data.GetAs<uchar**> (), XA_WINDOW))
580 int XWrapper::GetDesktopCount ()
585 if (GetRootWinProp (GetAtom (
"_NET_NUMBER_OF_DESKTOPS"), &length, data.GetAs<uchar**> (), XA_CARDINAL))
586 return length > 0 ? data [0] : -1;
591 int XWrapper::GetCurrentDesktop ()
596 if (GetRootWinProp (GetAtom (
"_NET_CURRENT_DESKTOP"), &length, data.GetAs<uchar**> (), XA_CARDINAL))
597 return length > 0 ? data [0] : -1;
602 void XWrapper::SetCurrentDesktop (
int desktop)
604 SendMessage (AppWin_, GetAtom (
"_NET_CURRENT_DESKTOP"), desktop);
607 QStringList XWrapper::GetDesktopNames ()
612 if (!GetRootWinProp (GetAtom (
"_NET_DESKTOP_NAMES"),
613 &length, data.GetAs<uchar**> (), GetAtom (
"UTF8_STRING")))
620 for (
char *pos = data.GetAs<
char*> (
false), *end = data.GetAs<
char*> (
false) + length; pos < end; )
622 const auto& str = QString::fromUtf8 (pos);
624 pos += str.toUtf8 ().size () + 1;
629 QString XWrapper::GetDesktopName (
int desktop,
const QString& def)
631 return GetDesktopNames ().value (desktop, def);
634 int XWrapper::GetWindowDesktop (
Window wid)
638 if (GetWinProp (wid, GetAtom (
"_NET_WM_DESKTOP"), &length, data.GetAs<uchar**> (), XA_CARDINAL) && length)
641 if (GetWinProp (wid, GetAtom (
"_WIN_WORKSPACE"), &length, data.GetAs<uchar**> (), XA_CARDINAL) && length)
647 void XWrapper::MoveWindowToDesktop (
Window wid,
int num)
649 SendMessage (wid, GetAtom (
"_NET_WM_DESKTOP"), num);
652 QRect XWrapper::GetAvailableGeometry (
int screen)
654 auto dw = QApplication::desktop ();
656 if (screen < 0 || screen >= dw->screenCount ())
657 screen = dw->primaryScreen ();
659 if (dw->isVirtualDesktop ())
662 auto available = dw->screenGeometry (screen);
663 const auto deskGeom = dw->rect ();
665 for (
const auto wid : GetWindows ())
668 Guarded<ulong> struts;
669 const auto status = GetWinProp (wid, GetAtom (
"_NET_WM_STRUT_PARTIAL"),
670 &length, struts.GetAs<uchar**> (), XA_CARDINAL);
671 if (!status || length != 12)
676 static_cast<int> (deskGeom.x ()),
677 static_cast<int> (deskGeom.y () + struts [4]),
678 static_cast<int> (struts [0]),
679 static_cast<int> (struts [5] - struts [4])
681 if (available.intersects (left))
682 available.setX (left.width ());
686 static_cast<int> (deskGeom.x () + deskGeom.width () - struts [1]),
687 static_cast<int> (deskGeom.y () + struts [6]),
688 static_cast<int> (struts [1]),
689 static_cast<int> (struts [7] - struts [6])
691 if (available.intersects (right))
692 available.setWidth (right.x () - available.x ());
696 static_cast<int> (deskGeom.x () + struts [8]),
697 static_cast<int> (deskGeom.y ()),
698 static_cast<int> (struts [9] - struts [8]),
699 static_cast<int> (struts [2])
701 if (available.intersects (top))
702 available.setY (top.height ());
706 static_cast<int> (deskGeom.x () + struts [10]),
707 static_cast<int> (deskGeom.y () + deskGeom.height () - struts [3]),
708 static_cast<int> (struts [11] - struts [10]),
709 static_cast<int> (struts [3])
711 if (available.intersects (bottom))
712 available.setHeight (bottom.y () - available.y ());
718 QRect XWrapper::GetAvailableGeometry (QWidget *widget)
720 return GetAvailableGeometry (QApplication::desktop ()->screenNumber (widget));
723 Atom XWrapper::GetAtom (
const QString& name)
725 if (Atoms_.contains (name))
726 return Atoms_ [name];
728 auto atom = XInternAtom (
Display_, name.toLocal8Bit (),
false);
729 Atoms_ [name] = atom;
733 bool XWrapper::GetWinProp (
Window win, Atom property,
734 ulong *length,
unsigned char **result, Atom req)
const
737 ulong type = 0, rest = 0;
738 return XGetWindowProperty (
Display_, win,
739 property, 0, 1024,
false, req, &type,
740 &fmt, length, &rest, result) == Success;
743 bool XWrapper::GetRootWinProp (Atom property,
744 ulong *length, uchar **result, Atom req)
const
746 return GetWinProp (AppWin_, property, length, result, req);
754 ulong *data =
nullptr;
756 if (!GetWinProp (wid, GetAtom (
"_NET_WM_WINDOW_TYPE"),
757 &length, reinterpret_cast<uchar**> (&data)))
760 for (ulong i = 0; i < length; ++i)
767 bool XWrapper::SendMessage (
Window wid, Atom atom, ulong d0, ulong d1, ulong d2, ulong d3, ulong d4)
769 XClientMessageEvent msg;
771 msg.type = ClientMessage;
772 msg.message_type = atom;
773 msg.send_event =
true;
782 return XSendEvent (
Display_, AppWin_, FALSE, SubstructureRedirectMask | SubstructureNotifyMask,
783 reinterpret_cast<XEvent*> (&msg)) == Success;
786 void XWrapper::initialize ()
static XWrapper & Instance()