1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "Image/BsColor.h"
4#include "Image/BsPixelData.h"
5#include "Image/BsPixelUtil.h"
6#include "Private/Linux/BsLinuxWindow.h"
7#include "Private/Linux/BsLinuxPlatform.h"
8#include "Private/Linux/BsLinuxDropTarget.h"
9
10#include <X11/Xatom.h>
11#include <X11/extensions/Xrandr.h>
12#include <X11/Xutil.h>
13#include <X11/Xlib.h>
14
15#define _NET_WM_STATE_REMOVE 0
16#define _NET_WM_STATE_ADD 1
17#define _NET_WM_STATE_TOGGLE 2
18
19#define _NET_WM_MOVERESIZE_MOVE 8
20#define _NET_WM_MOVERESIZE_CANCEL 11
21
22#define WM_NormalState 1
23#define WM_IconicState 3
24
25namespace bs
26{
27 enum class WindowState
28 {
29 Minimized,
30 Maximized,
31 Normal
32 };
33
34 struct LinuxWindow::Pimpl
35 {
36 ::Window xWindow = 0;
37
38 INT32 x, y;
39 UINT32 width, height;
40 bool hasTitleBar = true;
41 bool dragInProgress = false;
42 bool resizeDisabled = false;
43 bool isExternal = false;
44 WindowState state = WindowState::Normal;
45
46 Vector<Rect2I> dragZones;
47
48 void* userData = nullptr;
49 };
50
51 LinuxWindow::LinuxWindow(const WINDOW_DESC &desc)
52 {
53 m = bs_new<Pimpl>();
54
55 if (desc.external)
56 {
57 m->x = desc.x;
58 m->y = desc.y;
59 m->width = desc.width;
60 m->height = desc.height;
61 m->xWindow = desc.external;
62 m->isExternal = true;
63 }
64 else
65 {
66 ::Display* display = LinuxPlatform::getXDisplay();
67
68 // Find the screen of the chosen monitor, as well as its current dimensions
69 INT32 screen = XDefaultScreen(display);
70 UINT32 outputIdx = 0;
71
72 RROutput primaryOutput = XRRGetOutputPrimary(display, RootWindow(display, screen));
73 INT32 monitorX = 0;
74 INT32 monitorY = 0;
75 UINT32 monitorWidth = 0;
76 UINT32 monitorHeight = 0;
77
78 INT32 screenCount = XScreenCount(display);
79 for(INT32 i = 0; i < screenCount; i++)
80 {
81 XRRScreenResources* screenRes = XRRGetScreenResources(display, RootWindow(display, i));
82
83 bool foundMonitor = false;
84 for (INT32 j = 0; j < screenRes->noutput; j++)
85 {
86 XRROutputInfo* outputInfo = XRRGetOutputInfo(display, screenRes, screenRes->outputs[j]);
87 if (outputInfo == nullptr || outputInfo->crtc == 0 || outputInfo->connection == RR_Disconnected)
88 {
89 XRRFreeOutputInfo(outputInfo);
90
91 continue;
92 }
93
94 XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(display, screenRes, outputInfo->crtc);
95 if (crtcInfo == nullptr)
96 {
97 XRRFreeCrtcInfo(crtcInfo);
98 XRRFreeOutputInfo(outputInfo);
99
100 continue;
101 }
102
103 if(desc.screen == (UINT32)-1)
104 {
105 if(screenRes->outputs[j] == primaryOutput)
106 foundMonitor = true;
107 }
108 else
109 foundMonitor = outputIdx == desc.screen;
110
111 if(foundMonitor)
112 {
113 screen = i;
114
115 monitorX = crtcInfo->x;
116 monitorY = crtcInfo->y;
117 monitorWidth = crtcInfo->width;
118 monitorHeight = crtcInfo->height;
119
120 foundMonitor = true;
121 break;
122 }
123 }
124
125 if(foundMonitor)
126 break;
127 }
128
129 XSetWindowAttributes attributes;
130 attributes.background_pixel = XWhitePixel(display, screen);
131 attributes.border_pixel = XBlackPixel(display, screen);
132 attributes.background_pixmap = 0;
133
134 attributes.colormap = XCreateColormap(display,
135 XRootWindow(display, screen),
136 desc.visualInfo.visual,
137 AllocNone);
138
139 // If no position specified, center on the requested monitor
140 if (desc.x == -1)
141 m->x = monitorX + (monitorWidth - desc.width) / 2;
142 else if (desc.screen != (UINT32)-1)
143 m->x = monitorX + desc.x;
144 else
145 m->x = desc.x;
146
147 if (desc.y == -1)
148 m->y = monitorY + (monitorHeight - desc.height) / 2;
149 else if (desc.screen != (UINT32)-1)
150 m->y = monitorY + desc.y;
151 else
152 m->y = desc.y;
153
154 m->width = desc.width;
155 m->height = desc.height;
156
157 m->xWindow = XCreateWindow(display,
158 XRootWindow(display, screen),
159 m->x, m->y,
160 m->width, m->height,
161 0, desc.visualInfo.depth,
162 InputOutput, desc.visualInfo.visual,
163 CWBackPixel | CWBorderPixel | CWColormap | CWBackPixmap, &attributes);
164
165 XStoreName(display, m->xWindow, desc.title.c_str());
166
167 // Position/size might have (and usually will) get overridden by the WM, so re-apply them
168 XSizeHints hints;
169 hints.flags = PPosition | PSize;
170 hints.x = m->x;
171 hints.y = m->y;
172 hints.width = m->width;
173 hints.height = m->height;
174
175 if(!desc.allowResize)
176 {
177 hints.flags |= PMinSize | PMaxSize;
178
179 hints.min_height = desc.height;
180 hints.max_height = desc.height;
181
182 hints.min_width = desc.width;
183 hints.max_width = desc.width;
184 }
185
186 XSetNormalHints(display, m->xWindow, &hints);
187
188 setShowDecorations(desc.showDecorations);
189 setIsModal(desc.modal);
190
191 XClassHint* classHint = XAllocClassHint();
192
193 classHint->res_class = (char*)"banshee3d";
194 classHint->res_name = (char*)desc.title.c_str();
195
196 XSetClassHint(display, m->xWindow, classHint);
197 XFree(classHint);
198
199 // Ensures the child window is always on top of the parent window
200 if(desc.parent)
201 XSetTransientForHint(display, m->xWindow, desc.parent);
202
203 long eventMask =
204 ExposureMask | FocusChangeMask |
205 KeyPressMask | KeyReleaseMask |
206 ButtonPressMask | ButtonReleaseMask |
207 EnterWindowMask | LeaveWindowMask |
208 PointerMotionMask | ButtonMotionMask |
209 StructureNotifyMask | PropertyChangeMask;
210
211 if(!desc.parent)
212 eventMask |= SubstructureNotifyMask | SubstructureRedirectMask;
213
214 XSelectInput(display, m->xWindow, eventMask);
215
216 // Make sure we get the window delete message from WM, so we can clean up ourselves
217 Atom atomDeleteWindow = XInternAtom(display, "WM_DELETE_WINDOW", False);
218 XSetWMProtocols(display, m->xWindow, &atomDeleteWindow, 1);
219
220 // Enable drag and drop
221 LinuxDragAndDrop::makeDNDAware(m->xWindow);
222
223 // Set background image if assigned
224 if(desc.background)
225 {
226 Pixmap pixmap = LinuxPlatform::createPixmap(*desc.background, (UINT32)desc.visualInfo.depth);
227
228 XSetWindowBackgroundPixmap(display, m->xWindow, pixmap);
229 XFreePixmap(display, pixmap);
230 XSync(display, 0);
231 }
232
233 // Show the window (needs to happen after setting the background pixmap)
234 if(!desc.hidden)
235 XMapWindow(display, m->xWindow);
236
237 if(!desc.showOnTaskBar)
238 showOnTaskbar(false);
239 }
240
241 m->hasTitleBar = desc.showDecorations;
242 m->resizeDisabled = !desc.allowResize;
243
244 LinuxPlatform::_registerWindow(m->xWindow, this);
245 }
246
247 LinuxWindow::~LinuxWindow()
248 {
249 if(m->xWindow != 0)
250 _destroy();
251
252 bs_delete(m);
253 }
254
255 void LinuxWindow::move(INT32 x, INT32 y)
256 {
257 m->x = x;
258 m->y = y;
259
260 XMoveWindow(LinuxPlatform::getXDisplay(), m->xWindow, x, y);
261 }
262
263 void LinuxWindow::resize(UINT32 width, UINT32 height)
264 {
265 // If resize is disabled on WM level, we need to force it
266 if(m->resizeDisabled)
267 {
268 XSizeHints hints;
269 hints.flags = PMinSize | PMaxSize;
270
271 hints.min_height = height;
272 hints.max_height = height;
273
274 hints.min_width = width;
275 hints.max_width = width;
276
277 XSetNormalHints(LinuxPlatform::getXDisplay(), m->xWindow, &hints);
278 }
279
280 m->width = width;
281 m->height = height;
282
283 XResizeWindow(LinuxPlatform::getXDisplay(), m->xWindow, width, height);
284 }
285
286 void LinuxWindow::hide()
287 {
288 XUnmapWindow(LinuxPlatform::getXDisplay(), m->xWindow);
289 }
290
291 void LinuxWindow::show()
292 {
293 XMapWindow(LinuxPlatform::getXDisplay(), m->xWindow);
294 XMoveResizeWindow(LinuxPlatform::getXDisplay(), m->xWindow, m->x, m->y, m->width, m->height);
295 }
296
297 void LinuxWindow::maximize()
298 {
299 maximize(true);
300 }
301
302 void LinuxWindow::minimize()
303 {
304 minimize(true);
305 }
306
307 void LinuxWindow::restore()
308 {
309 if(isMaximized())
310 maximize(false);
311 else if(isMinimized())
312 minimize(false);
313 }
314
315 INT32 LinuxWindow::getLeft() const
316 {
317 INT32 x, y;
318 ::Window child;
319 XTranslateCoordinates(LinuxPlatform::getXDisplay(), m->xWindow, DefaultRootWindow(LinuxPlatform::getXDisplay()),
320 0, 0, &x, &y, &child);
321
322 return x;
323 }
324
325 INT32 LinuxWindow::getTop() const
326 {
327 INT32 x, y;
328 ::Window child;
329 XTranslateCoordinates(LinuxPlatform::getXDisplay(), m->xWindow, DefaultRootWindow(LinuxPlatform::getXDisplay()),
330 0, 0, &x, &y, &child);
331
332 return y;
333 }
334
335 UINT32 LinuxWindow::getWidth() const
336 {
337 XWindowAttributes xwa;
338 XGetWindowAttributes(LinuxPlatform::getXDisplay(), m->xWindow, &xwa);
339
340 return (UINT32)xwa.width;
341 }
342
343 UINT32 LinuxWindow::getHeight() const
344 {
345 XWindowAttributes xwa;
346 XGetWindowAttributes(LinuxPlatform::getXDisplay(), m->xWindow, &xwa);
347
348 return (UINT32)xwa.height;
349 }
350
351 Vector2I LinuxWindow::windowToScreenPos(const Vector2I& windowPos) const
352 {
353 Vector2I screenPos;
354
355 ::Window child;
356 XTranslateCoordinates(LinuxPlatform::getXDisplay(), m->xWindow, DefaultRootWindow(LinuxPlatform::getXDisplay()),
357 windowPos.x, windowPos.y, &screenPos.x, &screenPos.y, &child);
358
359 return screenPos;
360 }
361
362 Vector2I LinuxWindow::screenToWindowPos(const Vector2I& screenPos) const
363 {
364 Vector2I windowPos;
365
366 ::Window child;
367 XTranslateCoordinates(LinuxPlatform::getXDisplay(), DefaultRootWindow(LinuxPlatform::getXDisplay()), m->xWindow,
368 screenPos.x, screenPos.y, &windowPos.x, &windowPos.y, &child);
369
370 return windowPos;
371 }
372
373 void LinuxWindow::setIcon(const PixelData& data)
374 {
375 constexpr UINT32 WIDTH = 128;
376 constexpr UINT32 HEIGHT = 128;
377
378 PixelData resizedData(WIDTH, HEIGHT, 1, PF_RGBA8);
379 resizedData.allocateInternalBuffer();
380
381 PixelUtil::scale(data, resizedData);
382
383 ::Display* display = LinuxPlatform::getXDisplay();
384
385 // Set icon the old way using IconPixmapHint.
386 Pixmap iconPixmap = LinuxPlatform::createPixmap(resizedData, (UINT32)XDefaultDepth(display,
387 XDefaultScreen(display)));
388
389 XWMHints* hints = XAllocWMHints();
390 hints->flags = IconPixmapHint;
391 hints->icon_pixmap = iconPixmap;
392
393 XSetWMHints(display, m->xWindow, hints);
394
395 XFree(hints);
396 XFreePixmap(display, iconPixmap);
397
398 // Also try to set _NET_WM_ICON for modern window managers.
399 // Using long because the spec for XChangeProperty states that format size of 32 = long (this means for 64-bit it
400 // is padded in upper 4 bytes)
401 Vector<long> wmIconData(2 + WIDTH * HEIGHT, 0);
402 wmIconData[0] = WIDTH;
403 wmIconData[1] = HEIGHT;
404 for (UINT32 y = 0; y < HEIGHT; y++)
405 for (UINT32 x = 0; x < WIDTH; x++)
406 wmIconData[y * WIDTH + x + 2] = resizedData.getColorAt(x, y).getAsBGRA();
407
408 Atom iconAtom = XInternAtom(display, "_NET_WM_ICON", False);
409 Atom cardinalAtom = XInternAtom(display, "CARDINAL", False);
410 XChangeProperty(display, m->xWindow, iconAtom, cardinalAtom, 32, PropModeReplace,
411 (const unsigned char*) wmIconData.data(), wmIconData.size());
412
413 XFlush(display);
414 }
415
416 void LinuxWindow::_destroy()
417 {
418 if (!m->isExternal)
419 {
420 XUnmapWindow(LinuxPlatform::getXDisplay(), m->xWindow);
421 XSync(LinuxPlatform::getXDisplay(), 0);
422
423 XDestroyWindow(LinuxPlatform::getXDisplay(), m->xWindow);
424 XSync(LinuxPlatform::getXDisplay(), 0);
425 }
426
427 LinuxPlatform::_unregisterWindow(m->xWindow);
428 m->xWindow = 0;
429 }
430
431 void LinuxWindow::_setDragZones(const Vector<Rect2I>& rects)
432 {
433 m->dragZones = rects;
434 }
435
436 void LinuxWindow::_dragStart(const XButtonEvent& event)
437 {
438 // Make sure to reset the flag since WM could have (and probably has) handled the drag end event, so we never
439 // received _dragEnd() call.
440 m->dragInProgress = false;
441
442 // If window has a titlebar, custom drag zones aren't used
443 if(m->hasTitleBar)
444 return;
445
446 for(auto& entry : m->dragZones)
447 {
448 if (entry.width == 0 || entry.height == 0)
449 continue;
450
451 if(entry.contains(Vector2I(event.x, event.y)))
452 {
453 XUngrabPointer(LinuxPlatform::getXDisplay(), 0L);
454 XFlush(LinuxPlatform::getXDisplay());
455
456 Atom wmMoveResize = XInternAtom(LinuxPlatform::getXDisplay(), "_NET_WM_MOVERESIZE", False);
457
458 XEvent xev;
459 memset(&xev, 0, sizeof(xev));
460 xev.type = ClientMessage;
461 xev.xclient.window = m->xWindow;
462 xev.xclient.message_type = wmMoveResize;
463 xev.xclient.format = 32;
464 xev.xclient.data.l[0] = event.x_root;
465 xev.xclient.data.l[1] = event.y_root;
466 xev.xclient.data.l[2] = _NET_WM_MOVERESIZE_MOVE;
467 xev.xclient.data.l[3] = Button1;
468 xev.xclient.data.l[4] = 0;
469
470 XSendEvent(LinuxPlatform::getXDisplay(), DefaultRootWindow(LinuxPlatform::getXDisplay()), False,
471 SubstructureRedirectMask | SubstructureNotifyMask, &xev);
472 XSync(LinuxPlatform::getXDisplay(), 0);
473
474 m->dragInProgress = true;
475 return;
476 }
477 }
478
479 return;
480 }
481
482 void LinuxWindow::_dragEnd()
483 {
484 // WM failed to end the drag, send the cancel drag event
485 if(m->dragInProgress)
486 {
487 Atom wmMoveResize = XInternAtom(LinuxPlatform::getXDisplay(), "_NET_WM_MOVERESIZE", False);
488
489 XEvent xev;
490 memset(&xev, 0, sizeof(xev));
491 xev.type = ClientMessage;
492 xev.xclient.window = m->xWindow;
493 xev.xclient.message_type = wmMoveResize;
494 xev.xclient.format = 32;
495 xev.xclient.data.l[0] = 0;
496 xev.xclient.data.l[1] = 0;
497 xev.xclient.data.l[2] = _NET_WM_MOVERESIZE_CANCEL;
498 xev.xclient.data.l[3] = Button1;
499 xev.xclient.data.l[4] = 0;
500
501 XSendEvent(LinuxPlatform::getXDisplay(), DefaultRootWindow(LinuxPlatform::getXDisplay()), False,
502 SubstructureRedirectMask | SubstructureNotifyMask, &xev);
503
504 m->dragInProgress = false;
505 }
506 }
507
508 ::Window LinuxWindow::_getXWindow() const
509 {
510 return m->xWindow;
511 }
512
513 void LinuxWindow::_setUserData(void* data)
514 {
515 m->userData = data;
516 }
517
518 void* LinuxWindow::_getUserData() const
519 {
520 return m->userData;
521 }
522
523 bool LinuxWindow::isMaximized() const
524 {
525 Atom wmState = XInternAtom(LinuxPlatform::getXDisplay(), "_NET_WM_STATE", False);
526 Atom type;
527 INT32 format;
528 uint64_t length;
529 uint64_t remaining;
530 uint8_t* data = nullptr;
531
532 INT32 result = XGetWindowProperty(LinuxPlatform::getXDisplay(), m->xWindow, wmState,
533 0, 1024, False, XA_ATOM, &type, &format,
534 &length, &remaining, &data);
535
536 if (result == Success)
537 {
538 Atom* atoms = (Atom*)data;
539 Atom wmMaxHorz = XInternAtom(LinuxPlatform::getXDisplay(), "_NET_WM_STATE_MAXIMIZED_HORZ", False);
540 Atom wmMaxVert = XInternAtom(LinuxPlatform::getXDisplay(), "_NET_WM_STATE_MAXIMIZED_VERT", False);
541
542 bool foundHorz = false;
543 bool foundVert = false;
544 for (UINT32 i = 0; i < length; i++)
545 {
546 if (atoms[i] == wmMaxHorz)
547 foundHorz = true;
548 if (atoms[i] == wmMaxVert)
549 foundVert = true;
550
551 if (foundVert && foundHorz)
552 return true;
553 }
554
555 XFree(atoms);
556 }
557
558 return false;
559 }
560
561 bool LinuxWindow::isMinimized()
562 {
563 Atom wmState = XInternAtom(LinuxPlatform::getXDisplay(), "WM_STATE", True);
564 Atom type;
565 INT32 format;
566 uint64_t length;
567 uint64_t remaining;
568 uint8_t* data = nullptr;
569
570 INT32 result = XGetWindowProperty(LinuxPlatform::getXDisplay(), m->xWindow, wmState,
571 0, 1024, False, AnyPropertyType, &type, &format,
572 &length, &remaining, &data);
573
574 if(result == Success)
575 {
576 long* state = (long*) data;
577 if(state[0] == WM_IconicState)
578 return true;
579 }
580
581 return false;
582 }
583
584 void LinuxWindow::maximize(bool enable)
585 {
586 Atom wmState = XInternAtom(LinuxPlatform::getXDisplay(), "_NET_WM_STATE", False);
587 Atom wmMaxHorz = XInternAtom(LinuxPlatform::getXDisplay(), "_NET_WM_STATE_MAXIMIZED_HORZ", False);
588 Atom wmMaxVert = XInternAtom(LinuxPlatform::getXDisplay(), "_NET_WM_STATE_MAXIMIZED_VERT", False);
589
590 XEvent xev;
591 memset(&xev, 0, sizeof(xev));
592 xev.type = ClientMessage;
593 xev.xclient.window = m->xWindow;
594 xev.xclient.message_type = wmState;
595 xev.xclient.format = 32;
596 xev.xclient.data.l[0] = enable ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
597 xev.xclient.data.l[1] = wmMaxHorz;
598 xev.xclient.data.l[2] = wmMaxVert;
599
600 XSendEvent(LinuxPlatform::getXDisplay(), DefaultRootWindow(LinuxPlatform::getXDisplay()), False,
601 SubstructureRedirectMask | SubstructureNotifyMask, &xev);
602 }
603
604 void LinuxWindow::minimize(bool enable)
605 {
606 XEvent xev;
607 Atom wmChange = XInternAtom(LinuxPlatform::getXDisplay(), "WM_CHANGE_STATE", False);
608
609 memset(&xev, 0, sizeof(xev));
610 xev.type = ClientMessage;
611 xev.xclient.window = m->xWindow;
612 xev.xclient.message_type = wmChange;
613 xev.xclient.format = 32;
614 xev.xclient.data.l[0] = enable ? WM_IconicState : WM_NormalState;
615
616 XSendEvent(LinuxPlatform::getXDisplay(), DefaultRootWindow(LinuxPlatform::getXDisplay()), False,
617 SubstructureRedirectMask | SubstructureNotifyMask, &xev);
618 }
619
620 void LinuxWindow::showOnTaskbar(bool enable)
621 {
622 Atom wmState = XInternAtom(LinuxPlatform::getXDisplay(), "_NET_WM_STATE", False);
623 Atom wmSkipTaskbar = XInternAtom(LinuxPlatform::getXDisplay(), "_NET_WM_STATE_SKIP_TASKBAR", False);
624 Atom wmSkipPager = XInternAtom(LinuxPlatform::getXDisplay(), "_NET_WM_STATE_SKIP_PAGER", False);
625
626 XEvent xev;
627 memset(&xev, 0, sizeof(xev));
628 xev.type = ClientMessage;
629 xev.xclient.window = m->xWindow;
630 xev.xclient.message_type = wmState;
631 xev.xclient.format = 32;
632 xev.xclient.data.l[0] = enable ? _NET_WM_STATE_REMOVE : _NET_WM_STATE_ADD;
633 xev.xclient.data.l[1] = wmSkipTaskbar;
634
635 XSendEvent(LinuxPlatform::getXDisplay(), DefaultRootWindow(LinuxPlatform::getXDisplay()), False,
636 SubstructureRedirectMask | SubstructureNotifyMask, &xev);
637
638 xev.xclient.data.l[1] = wmSkipPager;
639 XSendEvent(LinuxPlatform::getXDisplay(), DefaultRootWindow(LinuxPlatform::getXDisplay()), False,
640 SubstructureRedirectMask | SubstructureNotifyMask, &xev);
641
642 XSync(LinuxPlatform::getXDisplay(), 0);
643 }
644
645 void LinuxWindow::_setFullscreen(bool fullscreen)
646 {
647 // Attempt to bypass compositor if switching to fullscreen
648 if(fullscreen)
649 {
650 Atom wmBypassCompositor = XInternAtom(LinuxPlatform::getXDisplay(), "_NET_WM_BYPASS_COMPOSITOR", False);
651 if (wmBypassCompositor)
652 {
653 static constexpr UINT32 enabled = 1;
654
655 XChangeProperty(LinuxPlatform::getXDisplay(), m->xWindow, wmBypassCompositor,
656 XA_CARDINAL, 32, PropModeReplace, (unsigned char*) &enabled, 1);
657 }
658 }
659
660 // Make the switch to fullscreen
661 XEvent xev;
662 Atom wmState = XInternAtom(LinuxPlatform::getXDisplay(), "_NET_WM_STATE", False);
663 Atom wmFullscreen = XInternAtom(LinuxPlatform::getXDisplay(), "_NET_WM_STATE_FULLSCREEN", False);
664
665 memset(&xev, 0, sizeof(xev));
666 xev.type = ClientMessage;
667 xev.xclient.window = m->xWindow;
668 xev.xclient.message_type = wmState;
669 xev.xclient.format = 32;
670 xev.xclient.data.l[0] = fullscreen ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
671 xev.xclient.data.l[1] = wmFullscreen;
672 xev.xclient.data.l[2] = 0;
673
674 XSendEvent(LinuxPlatform::getXDisplay(), DefaultRootWindow(LinuxPlatform::getXDisplay()), False,
675 SubstructureRedirectMask | SubstructureNotifyMask, &xev);
676 }
677
678 void LinuxWindow::setShowDecorations(bool show)
679 {
680 static constexpr UINT32 MWM_HINTS_DECORATIONS = (1 << 1);
681
682 struct MotifHints
683 {
684 UINT32 flags;
685 UINT32 functions;
686 UINT32 decorations;
687 INT32 inputMode;
688 UINT32 status;
689 };
690
691 if(show)
692 return;
693
694 MotifHints motifHints;
695 motifHints.flags = MWM_HINTS_DECORATIONS;
696 motifHints.decorations = 0;
697 motifHints.functions = 0;
698 motifHints.inputMode = 0;
699 motifHints.status = 0;
700
701 Atom wmHintsAtom = XInternAtom(LinuxPlatform::getXDisplay(), "_MOTIF_WM_HINTS", False);
702 XChangeProperty(LinuxPlatform::getXDisplay(), m->xWindow,
703 wmHintsAtom, wmHintsAtom,
704 32,
705 PropModeReplace,
706 (unsigned char *)&motifHints,
707 5);
708 }
709
710 void LinuxWindow::setIsModal(bool modal)
711 {
712 if(modal)
713 {
714 Atom wmState = XInternAtom(LinuxPlatform::getXDisplay(), "_NET_WM_STATE", False);
715 Atom wmValue = XInternAtom(LinuxPlatform::getXDisplay(), "_NET_WM_STATE_MODAL", False);
716
717 XEvent xev;
718 memset(&xev, 0, sizeof(xev));
719 xev.type = ClientMessage;
720 xev.xclient.window = m->xWindow;
721 xev.xclient.message_type = wmState;
722 xev.xclient.format = 32;
723 xev.xclient.data.l[0] = _NET_WM_STATE_ADD;
724 xev.xclient.data.l[1] = wmValue;
725 xev.xclient.data.l[2] = 0;
726 xev.xclient.data.l[3] = 1;
727
728 XSendEvent(LinuxPlatform::getXDisplay(), DefaultRootWindow(LinuxPlatform::getXDisplay()), False,
729 SubstructureRedirectMask | SubstructureNotifyMask, &xev);
730 }
731 }
732}
733
734