1 #include <X11/Xlib.h>
2 #include <X11/Xutil.h>
3 #include <X11/Xatom.h>
4
5 #include "allegro5/allegro.h"
6 #include "allegro5/allegro_x.h"
7 #include "allegro5/internal/aintern_x.h"
8 #include "allegro5/internal/aintern_xdisplay.h"
9 #include "allegro5/internal/aintern_xsystem.h"
10 #include "allegro5/internal/aintern_xwindow.h"
11
12 #ifdef ALLEGRO_RASPBERRYPI
13 #include "allegro5/internal/aintern_raspberrypi.h"
14 #define ALLEGRO_SYSTEM_XGLX ALLEGRO_SYSTEM_RASPBERRYPI
15 #define ALLEGRO_DISPLAY_XGLX ALLEGRO_DISPLAY_RASPBERRYPI
16 #endif
17
18 ALLEGRO_DEBUG_CHANNEL("xwindow")
19
20 #define X11_ATOM(x) XInternAtom(x11, #x, False);
21
_al_xwin_set_size_hints(ALLEGRO_DISPLAY * d,int x_off,int y_off)22 void _al_xwin_set_size_hints(ALLEGRO_DISPLAY *d, int x_off, int y_off)
23 {
24 ALLEGRO_SYSTEM_XGLX *system = (void *)al_get_system_driver();
25 ALLEGRO_DISPLAY_XGLX *glx = (void *)d;
26 XSizeHints *sizehints;
27 XWMHints *wmhints;
28 XClassHint *classhints;
29 int w, h;
30
31 sizehints = XAllocSizeHints();
32 sizehints->flags = 0;
33
34 #ifdef ALLEGRO_RASPBERRYPI
35 int x, y;
36 _al_raspberrypi_get_screen_info(&x, &y, &w, &h);
37 #else
38 w = d->w;
39 h = d->h;
40 #endif
41
42 /* Do not force the size of the window on resizeable or fullscreen windows */
43 /* on fullscreen windows, it confuses most X Window Managers */
44 if (!(d->flags & ALLEGRO_RESIZABLE) && !(d->flags & ALLEGRO_FULLSCREEN)) {
45 sizehints->flags |= PMinSize | PMaxSize | PBaseSize;
46 sizehints->min_width = sizehints->max_width = sizehints->base_width = w;
47 sizehints->min_height = sizehints->max_height = sizehints->base_height = h;
48 }
49
50 /* Constrain the window if needed. */
51 if (d->use_constraints && (d->flags & ALLEGRO_RESIZABLE) &&
52 (d->min_w > 0 || d->min_h > 0 || d->max_w > 0 || d->max_h > 0))
53 {
54 sizehints->flags |= PMinSize | PMaxSize | PBaseSize;
55 sizehints->min_width = (d->min_w > 0) ? d->min_w : 0;
56 sizehints->min_height = (d->min_h > 0) ? d->min_h : 0;
57 sizehints->max_width = (d->max_w > 0) ? d->max_w : INT_MAX;
58 sizehints->max_height = (d->max_h > 0) ? d->max_h : INT_MAX;
59 sizehints->base_width = w;
60 sizehints->base_height = h;
61 }
62
63 // Tell WMs to respect our chosen position, otherwise the x_off/y_off
64 // positions passed to XCreateWindow will be ignored by most WMs.
65 if (x_off != INT_MAX && y_off != INT_MAX) {
66 ALLEGRO_DEBUG("Force window position to %d, %d.\n", x_off, y_off);
67 sizehints->flags |= PPosition;
68 sizehints->x = x_off;
69 sizehints->y = y_off;
70 }
71
72 if (d->flags & ALLEGRO_FULLSCREEN) {
73 /* kwin will improperly layer a panel over our window on a second display without this.
74 * some other Size flags may cause glitches with various WMs, but this seems to be ok
75 * with metacity and kwin. As noted in xdpy_create_display, compiz is just broken.
76 */
77 sizehints->flags |= PBaseSize;
78 sizehints->base_width = w;
79 sizehints->base_height = h;
80 }
81
82 /* Setup the input hints so we get keyboard input */
83 wmhints = XAllocWMHints();
84 wmhints->input = True;
85 wmhints->flags = InputHint;
86
87 ALLEGRO_PATH *exepath = al_get_standard_path(ALLEGRO_EXENAME_PATH);
88
89 /* Setup the class hints so we can get an icon (AfterStep)
90 * We must use the executable filename here.
91 */
92 classhints = XAllocClassHint();
93 classhints->res_name = strdup(al_get_path_basename(exepath));
94 classhints->res_class = strdup(al_get_path_basename(exepath));
95
96 /* Set the size, input and class hints, and define WM_CLIENT_MACHINE and WM_LOCALE_NAME */
97 XSetWMProperties(system->x11display, glx->window, NULL, NULL, NULL, 0,
98 sizehints, wmhints, classhints);
99
100 free(classhints->res_name);
101 free(classhints->res_class);
102 XFree(sizehints);
103 XFree(wmhints);
104 XFree(classhints);
105
106 al_destroy_path(exepath);
107 }
108
109
_al_xwin_reset_size_hints(ALLEGRO_DISPLAY * d)110 void _al_xwin_reset_size_hints(ALLEGRO_DISPLAY *d)
111 {
112 ALLEGRO_SYSTEM_XGLX *system = (void *)al_get_system_driver();
113 ALLEGRO_DISPLAY_XGLX *glx = (void *)d;
114 XSizeHints *hints;
115
116 hints = XAllocSizeHints();
117 hints->flags = PMinSize | PMaxSize;
118 hints->min_width = 0;
119 hints->min_height = 0;
120 // FIXME: Is there a way to remove/reset max dimensions?
121 hints->max_width = 32768;
122 hints->max_height = 32768;
123 XSetWMNormalHints(system->x11display, glx->window, hints);
124
125 XFree(hints);
126 }
127
128
129 /* Note: The system mutex must be locked (exactly once) before
130 * calling this as we call _al_display_xglx_await_resize.
131 */
_al_xwin_set_fullscreen_window(ALLEGRO_DISPLAY * display,int value)132 void _al_xwin_set_fullscreen_window(ALLEGRO_DISPLAY *display, int value)
133 {
134 ALLEGRO_SYSTEM_XGLX *system = (void *)al_get_system_driver();
135 ALLEGRO_DISPLAY_XGLX *glx = (ALLEGRO_DISPLAY_XGLX *)display;
136 Display *x11 = system->x11display;
137 #ifndef ALLEGRO_RASPBERRYPI
138 int old_resize_count = glx->resize_count;
139 #endif
140
141 ALLEGRO_DEBUG("Toggling _NET_WM_STATE_FULLSCREEN hint: %d\n", value);
142
143 XEvent xev;
144 xev.xclient.type = ClientMessage;
145 xev.xclient.serial = 0;
146 xev.xclient.send_event = True;
147 xev.xclient.message_type = X11_ATOM(_NET_WM_STATE);
148 xev.xclient.window = glx->window;
149 xev.xclient.format = 32;
150
151 // Note: It seems 0 is not reliable except when mapping a window -
152 // 2 is all we need though.
153 xev.xclient.data.l[0] = value; /* 0 = off, 1 = on, 2 = toggle */
154
155 xev.xclient.data.l[1] = X11_ATOM(_NET_WM_STATE_FULLSCREEN);
156 xev.xclient.data.l[2] = 0;
157 xev.xclient.data.l[3] = 0;
158 xev.xclient.data.l[4] = 1;
159
160 XSendEvent(
161 x11,
162 #if !defined ALLEGRO_RASPBERRYPI
163 RootWindowOfScreen(ScreenOfDisplay(x11, glx->xscreen)),
164 #else
165 RootWindowOfScreen(ScreenOfDisplay(x11, DefaultScreen(x11))),
166 #endif
167 False,
168 SubstructureRedirectMask | SubstructureNotifyMask, &xev);
169
170 #if !defined ALLEGRO_RASPBERRYPI
171 if (value == 2) {
172 /* Only wait for a resize if toggling. */
173 _al_display_xglx_await_resize(display, old_resize_count, true);
174 }
175 #endif
176 }
177
178
_al_xwin_set_above(ALLEGRO_DISPLAY * display,int value)179 void _al_xwin_set_above(ALLEGRO_DISPLAY *display, int value)
180 {
181 ALLEGRO_SYSTEM_XGLX *system = (void *)al_get_system_driver();
182 ALLEGRO_DISPLAY_XGLX *glx = (ALLEGRO_DISPLAY_XGLX *)display;
183 Display *x11 = system->x11display;
184
185 ALLEGRO_DEBUG("Toggling _NET_WM_STATE_ABOVE hint: %d\n", value);
186
187 XEvent xev;
188 xev.xclient.type = ClientMessage;
189 xev.xclient.serial = 0;
190 xev.xclient.send_event = True;
191 xev.xclient.message_type = X11_ATOM(_NET_WM_STATE);
192 xev.xclient.window = glx->window;
193 xev.xclient.format = 32;
194
195 // Note: It seems 0 is not reliable except when mapping a window -
196 // 2 is all we need though.
197 xev.xclient.data.l[0] = value; /* 0 = off, 1 = on, 2 = toggle */
198
199 xev.xclient.data.l[1] = X11_ATOM(_NET_WM_STATE_ABOVE);
200 xev.xclient.data.l[2] = 0;
201 xev.xclient.data.l[3] = 0;
202 xev.xclient.data.l[4] = 1;
203
204 XSendEvent(x11, DefaultRootWindow(x11), False,
205 SubstructureRedirectMask | SubstructureNotifyMask, &xev);
206 }
207
208
_al_xwin_set_frame(ALLEGRO_DISPLAY * display,bool frame_on)209 void _al_xwin_set_frame(ALLEGRO_DISPLAY *display, bool frame_on)
210 {
211 ALLEGRO_SYSTEM_XGLX *system = (void *)al_get_system_driver();
212 ALLEGRO_DISPLAY_XGLX *glx = (ALLEGRO_DISPLAY_XGLX *)display;
213 Display *x11 = system->x11display;
214 Atom hints;
215
216 _al_mutex_lock(&system->lock);
217
218 #if 1
219 /* This code is taken from the GDK sources. So it works perfectly in Gnome,
220 * no idea if it will work anywhere else. X11 documentation itself only
221 * describes a way how to make the window completely unmanaged, but that
222 * would also require special care in the event handler.
223 */
224 hints = XInternAtom(x11, "_MOTIF_WM_HINTS", True);
225 if (hints) {
226 struct {
227 unsigned long flags;
228 unsigned long functions;
229 unsigned long decorations;
230 long input_mode;
231 unsigned long status;
232 } motif = {2, 0, frame_on, 0, 0};
233 XChangeProperty(x11, glx->window, hints, hints, 32, PropModeReplace,
234 (void *)&motif, sizeof motif / 4);
235
236 if (frame_on)
237 display->flags &= ~ALLEGRO_FRAMELESS;
238 else
239 display->flags |= ALLEGRO_FRAMELESS;
240 }
241 #endif
242
243 _al_mutex_unlock(&system->lock);
244 }
245
246
247 /* Helper to set a window icon. We use the _NET_WM_ICON property which is
248 * supported by modern window managers.
249 *
250 * The old method is XSetWMHints but the (antiquated) ICCCM talks about 1-bit
251 * pixmaps. For colour icons, perhaps you're supposed use the icon_window,
252 * and draw the window yourself?
253 */
xdpy_set_icon_inner(Display * x11display,Window window,ALLEGRO_BITMAP * bitmap,int prop_mode)254 static bool xdpy_set_icon_inner(Display *x11display, Window window,
255 ALLEGRO_BITMAP *bitmap, int prop_mode)
256 {
257 int w, h;
258 int data_size;
259 unsigned long *data; /* Yes, unsigned long, even on 64-bit platforms! */
260 ALLEGRO_LOCKED_REGION *lr;
261 bool ret;
262
263 w = al_get_bitmap_width(bitmap);
264 h = al_get_bitmap_height(bitmap);
265 data_size = 2 + w * h;
266 data = al_malloc(data_size * sizeof(data[0]));
267 if (!data)
268 return false;
269
270 lr = al_lock_bitmap(bitmap, ALLEGRO_PIXEL_FORMAT_ANY_WITH_ALPHA,
271 ALLEGRO_LOCK_READONLY);
272 if (lr) {
273 int x, y;
274 ALLEGRO_COLOR c;
275 unsigned char r, g, b, a;
276 Atom _NET_WM_ICON;
277
278 data[0] = w;
279 data[1] = h;
280 for (y = 0; y < h; y++) {
281 for (x = 0; x < w; x++) {
282 c = al_get_pixel(bitmap, x, y);
283 al_unmap_rgba(c, &r, &g, &b, &a);
284 data[2 + y*w + x] = ((unsigned long)a << 24) | ((unsigned long)r << 16) |
285 ((unsigned long)g << 8) | (unsigned long)b;
286 }
287 }
288
289 _NET_WM_ICON = XInternAtom(x11display, "_NET_WM_ICON", False);
290 XChangeProperty(x11display, window, _NET_WM_ICON, XA_CARDINAL, 32,
291 prop_mode, (unsigned char *)data, data_size);
292
293 al_unlock_bitmap(bitmap);
294 ret = true;
295 }
296 else {
297 ret = false;
298 }
299
300 al_free(data);
301
302 return ret;
303 }
304
305
_al_xwin_set_icons(ALLEGRO_DISPLAY * d,int num_icons,ALLEGRO_BITMAP * bitmaps[])306 void _al_xwin_set_icons(ALLEGRO_DISPLAY *d,
307 int num_icons, ALLEGRO_BITMAP *bitmaps[])
308 {
309 ALLEGRO_SYSTEM_XGLX *system = (ALLEGRO_SYSTEM_XGLX *)al_get_system_driver();
310 ALLEGRO_DISPLAY_XGLX *glx = (ALLEGRO_DISPLAY_XGLX *)d;
311 int prop_mode = PropModeReplace;
312 int i;
313
314 _al_mutex_lock(&system->lock);
315
316 for (i = 0; i < num_icons; i++) {
317 if (xdpy_set_icon_inner(system->x11display, glx->window, bitmaps[i],
318 prop_mode)) {
319 prop_mode = PropModeAppend;
320 }
321 }
322
323 _al_mutex_unlock(&system->lock);
324 }
325
326
_al_xwin_maximize(ALLEGRO_DISPLAY * display,bool maximized)327 void _al_xwin_maximize(ALLEGRO_DISPLAY *display, bool maximized)
328 {
329 #ifndef ALLEGRO_RASPBERRYPI
330 if (!!(display->flags & ALLEGRO_MAXIMIZED) == maximized)
331 return;
332 ALLEGRO_SYSTEM_XGLX *system = (void *)al_get_system_driver();
333 ALLEGRO_DISPLAY_XGLX *glx = (ALLEGRO_DISPLAY_XGLX *)display;
334 Display *x11 = system->x11display;
335 int old_resize_count = glx->resize_count;
336
337 XEvent xev;
338 xev.xclient.type = ClientMessage;
339 xev.xclient.serial = 0;
340 xev.xclient.send_event = True;
341 xev.xclient.message_type = X11_ATOM(_NET_WM_STATE);
342 xev.xclient.window = glx->window;
343 xev.xclient.format = 32;
344
345 xev.xclient.data.l[0] = maximized ? 1 : 0;
346 xev.xclient.data.l[1] = X11_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ);
347 xev.xclient.data.l[2] = X11_ATOM(_NET_WM_STATE_MAXIMIZED_VERT);
348 xev.xclient.data.l[3] = 0;
349
350 XSendEvent(
351 x11,
352 RootWindowOfScreen(ScreenOfDisplay(x11, glx->xscreen)),
353 False,
354 SubstructureRedirectMask | SubstructureNotifyMask, &xev);
355
356 _al_display_xglx_await_resize(display, old_resize_count, true);
357 #endif
358 }
359
360
_al_xwin_check_maximized(ALLEGRO_DISPLAY * display)361 void _al_xwin_check_maximized(ALLEGRO_DISPLAY *display)
362 {
363 #ifndef ALLEGRO_RASPBERRYPI
364 ALLEGRO_SYSTEM_XGLX *system = (void *)al_get_system_driver();
365 ALLEGRO_DISPLAY_XGLX *glx = (ALLEGRO_DISPLAY_XGLX *)display;
366 Display *x11 = system->x11display;
367 Atom type;
368 Atom horz = X11_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ);
369 Atom vert = X11_ATOM(_NET_WM_STATE_MAXIMIZED_VERT);
370 Atom property = X11_ATOM(_NET_WM_STATE);
371 int format;
372 int maximized = 0;
373 unsigned long n, remaining, i, *p32;
374 unsigned char *p8 = NULL;
375 if (XGetWindowProperty(x11, glx->window, property, 0, INT_MAX,
376 False, AnyPropertyType, &type, &format, &n, &remaining, &p8)
377 != Success) {
378 return;
379 }
380 p32 = (unsigned long *)p8;
381 for (i = 0; i < n; i++) {
382 if (p32[i] == horz)
383 maximized |= 1;
384 if (p32[i] == vert)
385 maximized |= 2;
386 }
387 XFree(p8);
388 display->flags &= ~ALLEGRO_MAXIMIZED;
389 if (maximized == 3)
390 display->flags |= ALLEGRO_MAXIMIZED;
391 #endif
392 }
393
394
395 /* Function: al_get_x_window_id
396 */
al_get_x_window_id(ALLEGRO_DISPLAY * display)397 XID al_get_x_window_id(ALLEGRO_DISPLAY *display)
398 {
399 ASSERT(display != NULL);
400 return ((ALLEGRO_DISPLAY_XGLX*)display)->window;
401 }
402
403 /* vim: set sts=3 sw=3 et: */
404