16#include <QApplication>
18#include <QAbstractEventDispatcher>
36 const auto x11app = qGuiApp->nativeInterface<QNativeInterface::QX11Application> ();
38 throw std::runtime_error {
"XWrapper used on non-X11" };
40 Conn_ = x11app->connection ();
41 Display_ = x11app->display ();
42 AppWin_ = DefaultRootWindow (Display_);
45 throw std::runtime_error {
"No X11 display" };
47 QAbstractEventDispatcher::instance ()->installNativeEventFilter (
this);
49 const uint32_t rootEvents [] =
51 XCB_EVENT_MASK_STRUCTURE_NOTIFY |
52 XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
53 XCB_EVENT_MASK_PROPERTY_CHANGE
55 xcb_change_window_attributes (Conn_,
56 AppWin_, XCB_CW_EVENT_MASK, rootEvents);
82 if (eventType !=
"xcb_generic_event_t")
85 const auto ev =
static_cast<xcb_generic_event_t*
> (msg);
86 if ((ev->response_type & ~0x80) == XCB_PROPERTY_NOTIFY)
87 HandlePropNotify (
static_cast<xcb_property_notify_event_t*
> (msg));
95 struct IsDoublePtr : std::false_type {};
98 struct IsDoublePtr<T**> : std::true_type {};
105 Guarded () =
default;
107 Guarded (
const Guarded&) =
delete;
108 Guarded& operator= (
const Guarded&) =
delete;
110 Guarded (Guarded&& other) noexcept
111 : Data_ { other.Data_ }
113 other.Data_ =
nullptr;
116 Guarded& operator= (Guarded&& other)
noexcept
118 std::swap (Data_, other.Data_);
128 T**
Get (
bool clear =
true) noexcept
136 U GetAs (
bool clear =
true) noexcept
140 return IsDoublePtr<U>::value ?
141 reinterpret_cast<U
> (&Data_) :
142 reinterpret_cast<U> (Data_);
145 T operator[] (
size_t idx)
const noexcept
150 T& operator[] (
size_t idx)
noexcept
155 explicit operator bool () const noexcept
157 return Data_ !=
nullptr;
160 bool operator! () const noexcept
170 XSync (Display_, False);
176 Guarded<Window> data;
179 if (GetRootWinProp (
GetAtom (
"_NET_CLIENT_LIST"), &length, data.GetAs<uchar**> ()))
180 for (ulong i = 0; i < length; ++i)
192 auto utf8Str =
GetAtom (
"UTF8_STRING");
194 if (GetWinProp (wid,
GetAtom (
"_NET_WM_VISIBLE_NAME"), &length, data.Get (), utf8Str))
195 name = QString::fromUtf8 (data.GetAs<
char*> (
false));
197 if (name.isEmpty () && GetWinProp (wid,
GetAtom (
"_NET_WM_NAME"), &length, data.Get (), utf8Str))
198 name = QString::fromUtf8 (data.GetAs<
char*> (
false));
200 if (name.isEmpty () && GetWinProp (wid,
GetAtom (
"XA_WM_NAME"), &length, data.Get (), XA_STRING))
201 name = QString::fromUtf8 (data.GetAs<
char*> (
false));
205 XFetchName (Display_, wid, data.GetAs<
char**> ());
206 name = QString (data.GetAs<
char*> (
false));
212 if (XGetWMName (Display_, wid, &prop))
214 name = QString::fromUtf8 (
reinterpret_cast<char*
> (prop.value));
225 ulong type, count, extra;
228 XGetWindowProperty (Display_, wid,
GetAtom (
"_NET_WM_ICON"),
229 0, std::numeric_limits<long>::max (), False, AnyPropertyType,
230 &type, &fmt, &count, &extra,
231 data.GetAs<uchar**> ());
238 auto cur = *data.Get (
false);
239 auto end = cur + count;
242 QImage img (cur [0], cur [1], QImage::Format_ARGB32);
244 const auto bytesCount = img.sizeInBytes ();
245 for (
int i = 0; i < bytesCount / 4; ++i, ++cur)
246 reinterpret_cast<uint*
> (img.bits ()) [i] = *cur;
248 icon.addPixmap (QPixmap::fromImage (img));
254 template<
typename Flag>
255 QFlags<Flag> XWrapper::GetFlagsList (
Window wid, Atom property,
const QHash<Atom, Flag>& atom2flag)
const
261 if (!GetWinProp (wid, property, &length,
reinterpret_cast<uchar**
> (&data), XA_ATOM))
264 for (ulong i = 0; i < length; ++i)
265 result |= atom2flag.value (data [i],
static_cast<Flag
> (0));
274 auto get = [
this] (
const QByteArray& atom) {
return GetAtom (
AsStringView (
"_NET_WM_STATE_" + atom)); };
275 static const QHash<Atom, WinStateFlag> atom2flag
291 return GetFlagsList (wid,
GetAtom (
"_NET_WM_STATE"), atom2flag);
296 auto get = [
this] (
const QByteArray& atom) {
return GetAtom (
AsStringView (
"_NET_WM_ACTION_" + atom)); };
297 static const QHash<Atom, AllowedActionFlag> atom2flag
313 return GetFlagsList (wid,
GetAtom (
"_NET_WM_ALLOWED_ACTIONS"), atom2flag);
318 auto win = GetActiveWindow ();
323 if (!
ShouldShow (win) && XGetTransientForHint (Display_, win, &transient))
333 return GetWinProp (wid,
GetAtom (
"WM_CLASS"), &length, data.Get ()) &&
334 QString (data.GetAs<
char*> (
false)).startsWith (
"leechcraft"_ql);
341 GetAtom (
"_NET_WM_WINDOW_TYPE_DESKTOP"),
342 GetAtom (
"_NET_WM_WINDOW_TYPE_DOCK"),
343 GetAtom (
"_NET_WM_WINDOW_TYPE_TOOLBAR"),
344 GetAtom (
"_NET_WM_WINDOW_TYPE_UTILITY"),
345 GetAtom (
"_NET_WM_WINDOW_TYPE_MENU"),
346 GetAtom (
"_NET_WM_WINDOW_TYPE_SPLASH"),
347 GetAtom (
"_NET_WM_WINDOW_TYPE_POPUP_MENU")
350 for (
const auto& type : GetWindowType (wid))
351 if (ignoreAtoms.contains (type))
358 if (!XGetTransientForHint (Display_, wid, &transient))
361 if (transient == 0 || transient == wid || transient == AppWin_)
364 return !GetWindowType (transient).contains (
GetAtom (
"_NET_WM_WINDOW_TYPE_NORMAL"));
372 XSelectInput (Display_, wid, PropertyChangeMask);
377 const auto wid = widget->effectiveWinId ();
379 const auto& winGeom = widget->geometry ();
383 case Qt::BottomToolBarArea:
385 0, 0, 0, winGeom.height (),
389 winGeom.left (), winGeom.right ());
391 case Qt::TopToolBarArea:
393 0, 0, winGeom.height (), 0,
396 winGeom.left (), winGeom.right (),
399 case Qt::LeftToolBarArea:
401 winGeom.width (), 0, 0, 0,
402 winGeom.top (), winGeom.bottom (),
407 case Qt::RightToolBarArea:
409 0, winGeom.width (), 0, 0,
411 winGeom.top (), winGeom.bottom (),
416 qWarning () << Q_FUNC_INFO
417 <<
"incorrect area passed"
425 const auto wid = w->effectiveWinId ();
426 XDeleteProperty (Display_, wid,
GetAtom (
"_NET_WM_STRUT"));
427 XDeleteProperty (Display_, wid,
GetAtom (
"_NET_WM_STRUT_PARTIAL"));
431 ulong left, ulong right, ulong top, ulong bottom,
432 ulong leftStartY, ulong leftEndY,
433 ulong rightStartY, ulong rightEndY,
434 ulong topStartX, ulong topEndX,
435 ulong bottomStartX, ulong bottomEndX)
455 XChangeProperty (Display_, wid,
GetAtom (
"_NET_WM_STRUT_PARTIAL"),
456 XA_CARDINAL, 32, PropModeReplace,
reinterpret_cast<uchar*
> (struts), 12);
458 XChangeProperty (Display_, wid,
GetAtom (
"_NET_WM_STRUT"),
459 XA_CARDINAL, 32, PropModeReplace,
reinterpret_cast<uchar*
> (struts), 4);
469 SendMessage (wid,
GetAtom (
"WM_CHANGE_STATE"), IconicState);
475 GetAtom (
"_NET_WM_STATE_MAXIMIZED_VERT"),
476 GetAtom (
"_NET_WM_STATE_MAXIMIZED_HORZ"),
483 GetAtom (
"_NET_WM_STATE_MAXIMIZED_VERT"),
484 GetAtom (
"_NET_WM_STATE_MAXIMIZED_HORZ"),
490 XResizeWindow (Display_, wid, width, height);
495 SendMessage (wid,
GetAtom (
"_NET_WM_STATE"),
501 SendMessage (wid,
GetAtom (
"_NET_WM_STATE"),
510 SendMessage (wid,
GetAtom (
"_NET_WM_STATE"), top,
513 SendMessage (wid,
GetAtom (
"_NET_WM_STATE"), bottom,
523 void XWrapper::HandlePropNotify (T ev)
525 if (ev->state == XCB_PROPERTY_DELETE)
528 const auto wid = ev->window;
532 if (ev->atom ==
GetAtom (
"_NET_CLIENT_LIST"))
534 else if (ev->atom ==
GetAtom (
"_NET_ACTIVE_WINDOW"))
536 else if (ev->atom ==
GetAtom (
"_NET_CURRENT_DESKTOP"))
541 if (ev->atom ==
GetAtom (
"_NET_WM_VISIBLE_NAME") ||
542 ev->atom ==
GetAtom (
"WM_NAME"))
544 else if (ev->atom ==
GetAtom (
"_NET_WM_ICON"))
546 else if (ev->atom ==
GetAtom (
"_NET_WM_DESKTOP"))
548 else if (ev->atom ==
GetAtom (
"_NET_WM_STATE"))
550 else if (ev->atom ==
GetAtom (
"_NET_WM_ALLOWED_ACTIONS"))
555 Window XWrapper::GetActiveWindow ()
560 if (!GetRootWinProp (
GetAtom (
"_NET_ACTIVE_WINDOW"), &length, data.GetAs<uchar**> (), XA_WINDOW))
574 if (GetRootWinProp (
GetAtom (
"_NET_NUMBER_OF_DESKTOPS"), &length, data.GetAs<uchar**> (), XA_CARDINAL))
575 return length > 0 ? data [0] : -1;
585 if (GetRootWinProp (
GetAtom (
"_NET_CURRENT_DESKTOP"), &length, data.GetAs<uchar**> (), XA_CARDINAL))
586 return length > 0 ? data [0] : -1;
593 SendMessage (AppWin_,
GetAtom (
"_NET_CURRENT_DESKTOP"), desktop);
601 if (!GetRootWinProp (
GetAtom (
"_NET_DESKTOP_NAMES"),
602 &length, data.GetAs<uchar**> (),
GetAtom (
"UTF8_STRING")))
609 for (
char *pos = data.GetAs<
char*> (
false), *end = data.GetAs<
char*> (
false) + length; pos < end; )
611 const auto& str = QString::fromUtf8 (pos);
613 pos += str.toUtf8 ().size () + 1;
627 if (GetWinProp (wid,
GetAtom (
"_NET_WM_DESKTOP"), &length, data.GetAs<uchar**> (), XA_CARDINAL) && length)
630 if (GetWinProp (wid,
GetAtom (
"_WIN_WORKSPACE"), &length, data.GetAs<uchar**> (), XA_CARDINAL) && length)
638 unsigned long data = num;
639 XChangeProperty (Display_,
645 reinterpret_cast<unsigned char*
> (&data),
651 auto available = screen.geometry ();
652 const auto deskGeom = screen.virtualGeometry ();
657 Guarded<ulong> struts;
658 const auto status = GetWinProp (wid,
GetAtom (
"_NET_WM_STRUT_PARTIAL"),
659 &length, struts.GetAs<uchar**> (), XA_CARDINAL);
660 if (!status || length != 12)
665 static_cast<int> (deskGeom.x ()),
666 static_cast<int> (deskGeom.y () + struts [4]),
667 static_cast<int> (struts [0]),
668 static_cast<int> (struts [5] - struts [4])
670 if (available.intersects (left))
671 available.setX (left.width ());
675 static_cast<int> (deskGeom.x () + deskGeom.width () - struts [1]),
676 static_cast<int> (deskGeom.y () + struts [6]),
677 static_cast<int> (struts [1]),
678 static_cast<int> (struts [7] - struts [6])
680 if (available.intersects (right))
681 available.setWidth (right.x () - available.x ());
685 static_cast<int> (deskGeom.x () + struts [8]),
686 static_cast<int> (deskGeom.y ()),
687 static_cast<int> (struts [9] - struts [8]),
688 static_cast<int> (struts [2])
690 if (available.intersects (top))
691 available.setY (top.height ());
695 static_cast<int> (deskGeom.x () + struts [10]),
696 static_cast<int> (deskGeom.y () + deskGeom.height () - struts [3]),
697 static_cast<int> (struts [11] - struts [10]),
698 static_cast<int> (struts [3])
700 if (available.intersects (bottom))
701 available.setHeight (bottom.y () - available.y ());
715 if (pos != Atoms_.end ())
718 auto atom = XInternAtom (Display_, name.data (),
false);
723 bool XWrapper::GetWinProp (
Window win, Atom property,
724 ulong *length,
unsigned char **result, Atom req)
const
727 ulong type = 0, rest = 0;
728 return XGetWindowProperty (Display_, win,
729 property, 0, 1024,
false, req, &type,
730 &fmt, length, &rest, result) == Success;
733 bool XWrapper::GetRootWinProp (Atom property,
734 ulong *length, uchar **result, Atom req)
const
736 return GetWinProp (AppWin_, property, length, result, req);
744 ulong *data =
nullptr;
746 if (!GetWinProp (wid,
GetAtom (
"_NET_WM_WINDOW_TYPE"),
747 &length,
reinterpret_cast<uchar**
> (&data)))
750 for (ulong i = 0; i < length; ++i)
757 bool XWrapper::SendMessage (
Window wid, Atom atom, ulong d0, ulong d1, ulong d2, ulong d3, ulong d4)
760 msg.xclient.window = wid;
761 msg.xclient.type = ClientMessage;
762 msg.xclient.message_type = atom;
763 msg.xclient.send_event =
true;
764 msg.xclient.display = Display_;
765 msg.xclient.format = 32;
766 msg.xclient.data.l [0] = d0;
767 msg.xclient.data.l [1] = d1;
768 msg.xclient.data.l [2] = d2;
769 msg.xclient.data.l [3] = d3;
770 msg.xclient.data.l [4] = d4;
772 auto flags = SubstructureRedirectMask | SubstructureNotifyMask;
773 return XSendEvent (Display_, AppWin_,
false, flags, &msg) == Success;
776 void XWrapper::initialize ()
static 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)
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 AsByteArray(std::string_view view) noexcept
Convert the view into a QByteArray without copying.