FreeRDP
Loading...
Searching...
No Matches
sdl_x11.cpp
1
19#include "sdl_x11.hpp"
20
21#include <utility>
22
23#include <winpr/platform.h>
24#include "sdl_utils.hpp"
25
26#include <cstdio>
27
28#if defined(WITH_SDL_X11_NATIVE)
29
30#include <X11/Xatom.h>
31#include <X11/Xlib.h>
32
33bool sdl_x11_has_compositor()
34{
35 if (!sdl::utils::isX11Driver())
36 return true;
37 /* Short-lived connection: caps are detected once, and no SDL window may exist yet. */
38 Display* dpy = XOpenDisplay(nullptr);
39 if (!dpy)
40 return true; /* Default to composited if undetected. */
41 char sel[32];
42 (void)snprintf(sel, sizeof(sel), "_NET_WM_CM_S%d", DefaultScreen(dpy));
43 const Atom atom = XInternAtom(dpy, sel, False);
44 const bool composited = (XGetSelectionOwner(dpy, atom) != None);
45 XCloseDisplay(dpy);
46 return composited;
47}
48
49/* The window's X11 Display + Window, or {nullptr, 0} off the X11 driver. */
50static std::pair<Display*, Window> sdl_x11_handles(SDL_Window* window)
51{
52 if (!window || !sdl::utils::isX11Driver())
53 return { nullptr, 0 };
54 auto props = SDL_GetWindowProperties(window);
55 auto dpy = static_cast<Display*>(
56 SDL_GetPointerProperty(props, SDL_PROP_WINDOW_X11_DISPLAY_POINTER, nullptr));
57 const auto xwin =
58 static_cast<Window>(SDL_GetNumberProperty(props, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0));
59 return { dpy, xwin };
60}
61
62bool sdl_x11_begin_move_size(SDL_Window* window, int netDirection)
63{
64 const auto [dpy, xwin] = sdl_x11_handles(window);
65 if (!dpy || (xwin == 0))
66 return false;
67
68 /* One display per process; resolve the atom once instead of a roundtrip per move. */
69 static Atom s_moveResize = None;
70 if (s_moveResize == None)
71 s_moveResize = XInternAtom(dpy, "_NET_WM_MOVERESIZE", False);
72
73 float gx = 0;
74 float gy = 0;
75 SDL_GetGlobalMouseState(&gx, &gy);
76
77 XClientMessageEvent xev = {};
78 xev.type = ClientMessage;
79 xev.window = xwin;
80 xev.message_type = s_moveResize;
81 xev.format = 32;
82 // NOLINTBEGIN(cppcoreguidelines-pro-type-union-access,google-runtime-int)
83 xev.data.l[0] = static_cast<long>(gx);
84 xev.data.l[1] = static_cast<long>(gy);
85 xev.data.l[2] = netDirection;
86 xev.data.l[3] = Button1;
87 xev.data.l[4] = 1; /* source: normal application */
88 // NOLINTEND(cppcoreguidelines-pro-type-union-access,google-runtime-int)
89
90 /* Release the implicit pointer grab of the pressed button, or the WM cannot take over. */
91 XUngrabPointer(dpy, CurrentTime);
92 XSendEvent(dpy, DefaultRootWindow(dpy), False,
93 SubstructureRedirectMask | SubstructureNotifyMask, reinterpret_cast<XEvent*>(&xev));
94 XFlush(dpy);
95 return true;
96}
97
98bool sdl_x11_set_frame_extents(SDL_Window* window, int left, int right, int top, int bottom)
99{
100 const auto [dpy, xwin] = sdl_x11_handles(window);
101 if (!dpy || (xwin == 0))
102 return false;
103
104 static Atom s_extents = None;
105 if (s_extents == None)
106 s_extents = XInternAtom(dpy, "_GTK_FRAME_EXTENTS", False);
107
108 /* Set _GTK_FRAME_EXTENTS for invisible resize bands. */
109 const long data[4] = { left, right, top, bottom }; // NOLINT(google-runtime-int)
110 XChangeProperty(dpy, xwin, s_extents, XA_CARDINAL, 32, PropModeReplace,
111 reinterpret_cast<const unsigned char*>(data), 4);
112 XFlush(dpy);
113 return true;
114}
115
116bool sdl_x11_set_bit_gravity(SDL_Window* window, int gravity)
117{
118 const auto [dpy, xwin] = sdl_x11_handles(window);
119 if (!dpy || (xwin == 0))
120 return false;
121
122 /* Set bit gravity to prevent frame jumping during async window resize. */
123 XSetWindowAttributes attrs{};
124 attrs.bit_gravity = gravity;
125 XChangeWindowAttributes(dpy, xwin, CWBitGravity, &attrs);
126 XFlush(dpy);
127 return true;
128}
129
130bool sdl_x11_send_left_button_release(SDL_Window* window)
131{
132 const auto [dpy, xwin] = sdl_x11_handles(window);
133 if (!dpy || (xwin == 0))
134 return false;
135
136 /* Empty mask: delivered to this client only, never to the WM. */
137 XButtonEvent ev = {};
138 ev.type = ButtonRelease;
139 ev.display = dpy;
140 ev.window = xwin;
141 ev.root = DefaultRootWindow(dpy);
142 ev.time = CurrentTime; /* SDL ignores X event time. */
143 ev.button = Button1;
144 ev.same_screen = True;
145 if (!XSendEvent(dpy, xwin, False, NoEventMask, reinterpret_cast<XEvent*>(&ev)))
146 return false;
147 XFlush(dpy);
148 return true;
149}
150
151static Window sdl_x11_xwindow(SDL_Window* window)
152{
153 return sdl_x11_handles(window).second;
154}
155
156bool sdl_x11_restack_windows(const std::vector<SDL_Window*>& topToBottom)
157{
158 if (!sdl::utils::isX11Driver() || (topToBottom.size() < 2))
159 return false;
160
161 auto props = SDL_GetWindowProperties(topToBottom.front());
162 auto dpy = static_cast<Display*>(
163 SDL_GetPointerProperty(props, SDL_PROP_WINDOW_X11_DISPLAY_POINTER, nullptr));
164 if (!dpy)
165 return false;
166
167 static Atom s_restack = None;
168 if (s_restack == None)
169 s_restack = XInternAtom(dpy, "_NET_RESTACK_WINDOW", False);
170
171 const Window root = DefaultRootWindow(dpy);
172 /* Restack each window below its predecessor to realize the server z-order. */
173 for (size_t i = 1; i < topToBottom.size(); i++)
174 {
175 const Window win = sdl_x11_xwindow(topToBottom.at(i));
176 const Window sibling = sdl_x11_xwindow(topToBottom.at(i - 1));
177 if ((win == 0) || (sibling == 0))
178 continue;
179
180 XClientMessageEvent xev = {};
181 xev.type = ClientMessage;
182 xev.window = win;
183 xev.message_type = s_restack;
184 xev.format = 32;
185 // NOLINTBEGIN(cppcoreguidelines-pro-type-union-access,google-runtime-int)
186 xev.data.l[0] = 2; /* source indication: pager/direct (authoritative) */
187 xev.data.l[1] = static_cast<long>(sibling);
188 xev.data.l[2] = Below;
189 // NOLINTEND(cppcoreguidelines-pro-type-union-access,google-runtime-int)
190 XSendEvent(dpy, root, False, SubstructureRedirectMask | SubstructureNotifyMask,
191 reinterpret_cast<XEvent*>(&xev));
192 }
193 XFlush(dpy);
194 return true;
195}
196
197#else /* !WITH_SDL_X11_NATIVE */
198
199bool sdl_x11_has_compositor()
200{
201 return true;
202}
203
204bool sdl_x11_begin_move_size(WINPR_ATTR_UNUSED SDL_Window* window,
205 WINPR_ATTR_UNUSED int netDirection)
206{
207 return false;
208}
209
210bool sdl_x11_set_frame_extents(WINPR_ATTR_UNUSED SDL_Window* window, WINPR_ATTR_UNUSED int left,
211 WINPR_ATTR_UNUSED int right, WINPR_ATTR_UNUSED int top,
212 WINPR_ATTR_UNUSED int bottom)
213{
214 return false;
215}
216
217bool sdl_x11_set_bit_gravity(WINPR_ATTR_UNUSED SDL_Window* window, WINPR_ATTR_UNUSED int gravity)
218{
219 return false;
220}
221
222bool sdl_x11_restack_windows(WINPR_ATTR_UNUSED const std::vector<SDL_Window*>& topToBottom)
223{
224 return false;
225}
226
227bool sdl_x11_send_left_button_release(WINPR_ATTR_UNUSED SDL_Window* window)
228{
229 return false;
230}
231
232#endif