16#include <QApplication>
18#include <QDesktopWidget>
19#include <QAbstractEventDispatcher>
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);
57 Display* XWrapper::GetDisplay ()
const
62 Window XWrapper::GetRootWindow ()
const
67 bool XWrapper::nativeEventFilter (
const QByteArray& eventType,
void *msg,
long int*)
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
105 std::swap (Data_, other.Data_);
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
154 void XWrapper::Sync ()
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)
172 QString XWrapper::GetWindowTitle (
Window wid)
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));
209 QIcon XWrapper::GetWindowIcon (
Window wid)
212 ulong type, count, extra;
215 XGetWindowProperty (Display_, wid, GetAtom (
"_NET_WM_ICON"),
216 0, std::numeric_limits<long>::max (), False, AnyPropertyType,
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));
259 WinStateFlags XWrapper::GetWindowState (
Window wid)
261 auto get = [
this] (
const QByteArray& atom) {
return GetAtom (
AsStringView (
"_NET_WM_STATE_" + atom)); };
262 static const QHash<Atom, WinStateFlag> atom2flag
264 { get (
"MODAL"), WinStateFlag::Modal },
265 { get (
"STICKY"), WinStateFlag::Sticky },
266 { get (
"MAXIMIZED_VERT"), WinStateFlag::MaximizedVert },
267 { get (
"MAXIMIZED_HORZ"), WinStateFlag::MaximizedHorz },
268 { get (
"SHADED"), WinStateFlag::Shaded },
269 { get (
"SKIP_TASKBAR"), WinStateFlag::SkipTaskbar },
270 { get (
"SKIP_PAGER"), WinStateFlag::SkipPager },
271 { get (
"HIDDEN"), WinStateFlag::Hidden },
272 { get (
"FULLSCREEN"), WinStateFlag::Fullscreen },
273 { get (
"ABOVE"), WinStateFlag::OnTop },
274 { get (
"BELOW"), WinStateFlag::OnBottom },
275 { get (
"DEMANDS_ATTENTION"), WinStateFlag::Attention },
278 return GetFlagsList (wid, GetAtom (
"_NET_WM_STATE"), atom2flag);
281 AllowedActionFlags XWrapper::GetWindowActions (
Window wid)
283 auto get = [
this] (
const QByteArray& atom) {
return GetAtom (
AsStringView (
"_NET_WM_ACTION_" + atom)); };
284 static const QHash<Atom, AllowedActionFlag> atom2flag
286 { get (
"MOVE"), AllowedActionFlag::Move },
287 { get (
"RESIZE"), AllowedActionFlag::Resize },
288 { get (
"MINIMIZE"), AllowedActionFlag::Minimize },
289 { get (
"SHADE"), AllowedActionFlag::Shade },
290 { get (
"STICK"), AllowedActionFlag::Stick },
291 { get (
"MAXIMIZE_HORZ"), AllowedActionFlag::MaximizeHorz },
292 { get (
"MAXIMIZE_VERT"), AllowedActionFlag::MaximizeVert },
293 { get (
"FULLSCREEN"), AllowedActionFlag::ShowFullscreen },
294 { get (
"CHANGE_DESKTOP"), AllowedActionFlag::ChangeDesktop },
295 { get (
"CLOSE"), AllowedActionFlag::Close },
296 { get (
"ABOVE"), AllowedActionFlag::MoveToTop },
297 { get (
"BELOW"), AllowedActionFlag::MoveToBottom },
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"),
330 GetAtom (
"_NET_WM_WINDOW_TYPE_TOOLBAR"),
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))
341 if (GetWindowState (wid) & WinStateFlag::SkipTaskbar)
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"));
356 if (IsLCWindow (wid))
359 XSelectInput (Display_, wid, PropertyChangeMask);
362 void XWrapper::SetStrut (QWidget *widget, Qt::ToolBarArea area)
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"
410 void XWrapper::ClearStrut (QWidget *w)
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);
451 SendMessage (wid, GetAtom (
"_NET_ACTIVE_WINDOW"),
SourcePager);
454 void XWrapper::MinimizeWindow (
Window wid)
456 SendMessage (wid, GetAtom (
"WM_CHANGE_STATE"), IconicState);
459 void XWrapper::MaximizeWindow (
Window wid)
461 SendMessage (wid, GetAtom (
"_NET_WM_STATE"),
StateAdd,
462 GetAtom (
"_NET_WM_STATE_MAXIMIZED_VERT"),
463 GetAtom (
"_NET_WM_STATE_MAXIMIZED_HORZ"),
467 void XWrapper::UnmaximizeWindow (
Window wid)
469 SendMessage (wid, GetAtom (
"_NET_WM_STATE"),
StateRemove,
470 GetAtom (
"_NET_WM_STATE_MAXIMIZED_VERT"),
471 GetAtom (
"_NET_WM_STATE_MAXIMIZED_HORZ"),
475 void XWrapper::ResizeWindow (
Window wid,
int width,
int height)
477 XResizeWindow (Display_, wid, width, height);
482 SendMessage (wid, GetAtom (
"_NET_WM_STATE"),
486 void XWrapper::UnshadeWindow (
Window wid)
488 SendMessage (wid, GetAtom (
"_NET_WM_STATE"),
497 SendMessage (wid, GetAtom (
"_NET_WM_STATE"), top,
500 SendMessage (wid, GetAtom (
"_NET_WM_STATE"), bottom,
506 SendMessage (wid, GetAtom (
"_NET_CLOSE_WINDOW"), 0,
SourcePager);
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"))
520 emit windowListChanged ();
521 else if (ev->atom == GetAtom (
"_NET_ACTIVE_WINDOW"))
522 emit activeWindowChanged ();
523 else if (ev->atom == GetAtom (
"_NET_CURRENT_DESKTOP"))
524 emit desktopChanged ();
528 if (ev->atom == GetAtom (
"_NET_WM_VISIBLE_NAME") ||
529 ev->atom == GetAtom (
"WM_NAME"))
530 emit windowNameChanged (wid);
531 else if (ev->atom == GetAtom (
"_NET_WM_ICON"))
532 emit windowIconChanged (wid);
533 else if (ev->atom == GetAtom (
"_NET_WM_DESKTOP"))
534 emit windowDesktopChanged (wid);
535 else if (ev->atom == GetAtom (
"_NET_WM_STATE"))
536 emit windowStateChanged (wid);
537 else if (ev->atom == GetAtom (
"_NET_WM_ALLOWED_ACTIONS"))
538 emit windowActionsChanged (wid);
542 Window XWrapper::GetActiveWindow ()
547 if (!GetRootWinProp (GetAtom (
"_NET_ACTIVE_WINDOW"), &length, data.GetAs<uchar**> (), XA_WINDOW))
556 int XWrapper::GetDesktopCount ()
561 if (GetRootWinProp (GetAtom (
"_NET_NUMBER_OF_DESKTOPS"), &length, data.GetAs<uchar**> (), XA_CARDINAL))
562 return length > 0 ? data [0] : -1;
567 int XWrapper::GetCurrentDesktop ()
572 if (GetRootWinProp (GetAtom (
"_NET_CURRENT_DESKTOP"), &length, data.GetAs<uchar**> (), XA_CARDINAL))
573 return length > 0 ? data [0] : -1;
578 void XWrapper::SetCurrentDesktop (
int desktop)
580 SendMessage (AppWin_, GetAtom (
"_NET_CURRENT_DESKTOP"), desktop);
583 QStringList XWrapper::GetDesktopNames ()
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;
605 QString XWrapper::GetDesktopName (
int desktop,
const QString& def)
607 return GetDesktopNames ().value (desktop, def);
610 int XWrapper::GetWindowDesktop (
Window wid)
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)
623 void XWrapper::MoveWindowToDesktop (
Window wid,
int num)
625 unsigned long data = num;
626 XChangeProperty (QX11Info::display (),
628 GetAtom (
"_NET_WM_DESKTOP"),
632 reinterpret_cast<unsigned char*
> (&data),
636 QRect XWrapper::GetAvailableGeometry (
int screenIdx)
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 ();
646 for (
const auto wid : GetWindows ())
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 ());
699 QRect XWrapper::GetAvailableGeometry (QWidget *widget)
701 return GetAvailableGeometry (QApplication::desktop ()->screenNumber (widget));
704 Atom XWrapper::GetAtom (std::string_view name)
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 ()
QByteArray AsByteArray(std::string_view view)
Convert the view into a QByteArray without copying.
QByteArray ToByteArray(std::string_view view)
Create a QByteArray with the data referenced by the view.
std::string_view AsStringView(const QByteArray &arr)
Create a std::string_view referring the data within a QByteArray.