JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_XWindowSystem_linux.cpp
Go to the documentation of this file.
1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24*/
25
26namespace juce
27{
28
29#if JUCE_DEBUG && ! defined (JUCE_DEBUG_XERRORS)
30 #define JUCE_DEBUG_XERRORS 1
31
32 #if ! defined (JUCE_DEBUG_XERRORS_SYNCHRONOUSLY)
33 #define JUCE_DEBUG_XERRORS_SYNCHRONOUSLY 0
34 #endif
35#endif
36
37#if JUCE_MODULE_AVAILABLE_juce_gui_extra
38 #define JUCE_X11_SUPPORTS_XEMBED 1
39#else
40 #define JUCE_X11_SUPPORTS_XEMBED 0
41#endif
42
43namespace
44{
45 struct XFreeDeleter
46 {
47 void operator() (void* ptr) const
48 {
49 if (ptr != nullptr)
50 X11Symbols::getInstance()->xFree (ptr);
51 }
52 };
53
54 template <typename Data>
56
57 template <typename Data, typename Deleter>
59
60 template <typename XValueType>
61 struct XValueHolder
62 {
63 XValueHolder (XValueType&& xv, const std::function<void(XValueType&)>& cleanup)
64 : value (std::move (xv)), cleanupFunc (cleanup)
65 {}
66
68 {
69 cleanupFunc (value);
70 }
71
72 XValueType value;
73 std::function<void(XValueType&)> cleanupFunc;
74 };
75}
76
77//==============================================================================
78XWindowSystemUtilities::ScopedXLock::ScopedXLock()
79{
80 if (auto* xWindow = XWindowSystem::getInstanceWithoutCreating())
81 if (auto* d = xWindow->getDisplay())
82 X11Symbols::getInstance()->xLockDisplay (d);
83}
84
85XWindowSystemUtilities::ScopedXLock::~ScopedXLock()
86{
87 if (auto* xWindow = XWindowSystem::getInstanceWithoutCreating())
88 if (auto* d = xWindow->getDisplay())
89 X11Symbols::getInstance()->xUnlockDisplay (d);
90}
91
92//==============================================================================
93XWindowSystemUtilities::Atoms::Atoms (::Display* display)
94{
95 protocols = getIfExists (display, "WM_PROTOCOLS");
96 protocolList [TAKE_FOCUS] = getIfExists (display, "WM_TAKE_FOCUS");
97 protocolList [DELETE_WINDOW] = getIfExists (display, "WM_DELETE_WINDOW");
98 protocolList [PING] = getIfExists (display, "_NET_WM_PING");
99 changeState = getIfExists (display, "WM_CHANGE_STATE");
100 state = getIfExists (display, "WM_STATE");
101 userTime = getCreating (display, "_NET_WM_USER_TIME");
102 activeWin = getCreating (display, "_NET_ACTIVE_WINDOW");
103 pid = getCreating (display, "_NET_WM_PID");
104 windowType = getIfExists (display, "_NET_WM_WINDOW_TYPE");
105 windowState = getIfExists (display, "_NET_WM_STATE");
106 windowStateHidden = getIfExists (display, "_NET_WM_STATE_HIDDEN");
107
108 XdndAware = getCreating (display, "XdndAware");
109 XdndEnter = getCreating (display, "XdndEnter");
110 XdndLeave = getCreating (display, "XdndLeave");
111 XdndPosition = getCreating (display, "XdndPosition");
112 XdndStatus = getCreating (display, "XdndStatus");
113 XdndDrop = getCreating (display, "XdndDrop");
114 XdndFinished = getCreating (display, "XdndFinished");
115 XdndSelection = getCreating (display, "XdndSelection");
116
117 XdndTypeList = getCreating (display, "XdndTypeList");
118 XdndActionList = getCreating (display, "XdndActionList");
119 XdndActionCopy = getCreating (display, "XdndActionCopy");
120 XdndActionPrivate = getCreating (display, "XdndActionPrivate");
121 XdndActionDescription = getCreating (display, "XdndActionDescription");
122
123 XembedMsgType = getCreating (display, "_XEMBED");
124 XembedInfo = getCreating (display, "_XEMBED_INFO");
125
126 allowedMimeTypes[0] = getCreating (display, "UTF8_STRING");
127 allowedMimeTypes[1] = getCreating (display, "text/plain;charset=utf-8");
128 allowedMimeTypes[2] = getCreating (display, "text/plain");
129 allowedMimeTypes[3] = getCreating (display, "text/uri-list");
130
131 allowedActions[0] = getCreating (display, "XdndActionMove");
132 allowedActions[1] = XdndActionCopy;
133 allowedActions[2] = getCreating (display, "XdndActionLink");
134 allowedActions[3] = getCreating (display, "XdndActionAsk");
135 allowedActions[4] = XdndActionPrivate;
136
137 utf8String = getCreating (display, "UTF8_STRING");
138 clipboard = getCreating (display, "CLIPBOARD");
139 targets = getCreating (display, "TARGETS");
140}
141
142Atom XWindowSystemUtilities::Atoms::getIfExists (::Display* display, const char* name)
143{
144 return X11Symbols::getInstance()->xInternAtom (display, name, True);
145}
146
147Atom XWindowSystemUtilities::Atoms::getCreating (::Display* display, const char* name)
148{
149 return X11Symbols::getInstance()->xInternAtom (display, name, False);
150}
151
152String XWindowSystemUtilities::Atoms::getName (::Display* display, Atom atom)
153{
154 if (atom == None)
155 return "None";
156
157 return makeXFreePtr (X11Symbols::getInstance()->xGetAtomName (display, atom)).get();
158}
159
160bool XWindowSystemUtilities::Atoms::isMimeTypeFile (::Display* display, Atom atom)
161{
162 return getName (display, atom).equalsIgnoreCase ("text/uri-list");
163}
164
165//==============================================================================
166XWindowSystemUtilities::GetXProperty::GetXProperty (::Display* display, Window window, Atom atom,
167 long offset, long length, bool shouldDelete, Atom requestedType)
168{
169 success = (X11Symbols::getInstance()->xGetWindowProperty (display, window, atom, offset, length,
170 (Bool) shouldDelete, requestedType, &actualType,
171 &actualFormat, &numItems, &bytesLeft, &data) == Success)
172 && data != nullptr;
173}
174
175XWindowSystemUtilities::GetXProperty::~GetXProperty()
176{
177 if (data != nullptr)
178 X11Symbols::getInstance()->xFree (data);
179}
180
181//==============================================================================
182std::unique_ptr<XWindowSystemUtilities::XSettings> XWindowSystemUtilities::XSettings::createXSettings (::Display* d)
183{
184 const auto settingsAtom = Atoms::getCreating (d, "_XSETTINGS_SETTINGS");
185 const auto settingsWindow = X11Symbols::getInstance()->xGetSelectionOwner (d,
186 Atoms::getCreating (d, "_XSETTINGS_S0"));
187
188 if (settingsWindow == None)
189 return {};
190
191 return rawToUniquePtr (new XWindowSystemUtilities::XSettings (d, settingsWindow, settingsAtom));
192}
193
194XWindowSystemUtilities::XSettings::XSettings (::Display* d, ::Window settingsWindowIn, Atom settingsAtomIn)
195 : display (d), settingsWindow (settingsWindowIn), settingsAtom (settingsAtomIn)
196{
197 update();
198}
199
200XWindowSystemUtilities::XSetting XWindowSystemUtilities::XSettings::getSetting (const String& name) const
201{
202 const auto iter = settings.find (name);
203
204 if (iter != settings.end())
205 return iter->second;
206
207 return {};
208}
209
210void XWindowSystemUtilities::XSettings::update()
211{
212 const GetXProperty prop { display,
213 settingsWindow,
214 settingsAtom,
215 0L,
217 false,
218 settingsAtom };
219
220 if (prop.success
221 && prop.actualType == settingsAtom
222 && prop.actualFormat == 8
223 && prop.numItems > 0)
224 {
225 const auto bytes = (size_t) prop.numItems;
226 auto* data = prop.data;
227 size_t byteNum = 0;
228
229 const auto increment = [&] (size_t amount)
230 {
231 data += amount;
232 byteNum += amount;
233 };
234
235 struct Header
236 {
237 CARD8 byteOrder;
238 CARD8 padding[3];
239 CARD32 serial;
240 CARD32 nSettings;
241 };
242
243 const auto* header = unalignedPointerCast<const Header*> (data);
244 const auto headerSerial = (int) header->serial;
245 increment (sizeof (Header));
246
247 const auto readCARD16 = [&]() -> CARD16
248 {
249 if (byteNum + sizeof (CARD16) > bytes)
250 return {};
251
252 const auto value = header->byteOrder == MSBFirst ? ByteOrder::bigEndianShort (data)
253 : ByteOrder::littleEndianShort (data);
254 increment (sizeof (CARD16));
255 return value;
256 };
257
258 const auto readCARD32 = [&]() -> CARD32
259 {
260 if (byteNum + sizeof (CARD32) > bytes)
261 return {};
262
263 const auto value = header->byteOrder == MSBFirst ? ByteOrder::bigEndianInt (data)
264 : ByteOrder::littleEndianInt (data);
265 increment (sizeof (CARD32));
266 return value;
267 };
268
269 const auto readString = [&] (size_t nameLen) -> String
270 {
271 const auto padded = (nameLen + 3) & (~(size_t) 3);
272
273 if (byteNum + padded > bytes)
274 return {};
275
276 auto* ptr = reinterpret_cast<const char*> (data);
277 const String result (ptr, nameLen);
278 increment (padded);
279 return result;
280 };
281
282 CARD16 setting = 0;
283
284 while (byteNum < bytes && setting < header->nSettings)
285 {
286 const auto type = *reinterpret_cast<const char*> (data);
287 increment (2);
288
289 const auto name = readString (readCARD16());
290 const auto serial = (int) readCARD32();
291
292 enum { XSettingsTypeInteger, XSettingsTypeString, XSettingsTypeColor };
293
294 const auto parsedSetting = [&]() -> XSetting
295 {
296 switch (type)
297 {
298 case XSettingsTypeInteger:
299 return { name, (int) readCARD32() };
300
301 case XSettingsTypeString:
302 return { name, readString (readCARD32()) };
303
304 case XSettingsTypeColor:
305 // Order is important, these should be kept as separate statements!
306 const auto r = (uint8) readCARD16();
307 const auto g = (uint8) readCARD16();
308 const auto b = (uint8) readCARD16();
309 const auto a = (uint8) readCARD16();
310 return { name, Colour { r, g, b, a } };
311 }
312
313 return {};
314 }();
315
316 if (serial > lastUpdateSerial)
317 {
318 settings[parsedSetting.name] = parsedSetting;
319 listeners.call ([&parsedSetting] (Listener& l) { l.settingChanged (parsedSetting); });
320 }
321
322 setting += 1;
323 }
324
325 lastUpdateSerial = headerSerial;
326 }
327}
328
329//==============================================================================
330::Window juce_messageWindowHandle;
331XContext windowHandleXContext;
332
333#if JUCE_X11_SUPPORTS_XEMBED
334 bool juce_handleXEmbedEvent (ComponentPeer*, void*);
335 unsigned long juce_getCurrentFocusWindow (ComponentPeer*);
336#endif
337
339{
340 unsigned long flags = 0;
341 unsigned long functions = 0;
342 unsigned long decorations = 0;
343 long input_mode = 0;
344 unsigned long status = 0;
345};
346
347//=============================== X11 - Error Handling =========================
348namespace X11ErrorHandling
349{
350 static XErrorHandler oldErrorHandler = {};
351 static XIOErrorHandler oldIOErrorHandler = {};
352
353 // Usually happens when client-server connection is broken
354 static int ioErrorHandler (::Display*)
355 {
356 DBG ("ERROR: connection to X server broken.. terminating.");
357
358 if (JUCEApplicationBase::isStandaloneApp())
359 MessageManager::getInstance()->stopDispatchLoop();
360
361 return 0;
362 }
363
364 static int errorHandler ([[maybe_unused]] ::Display* display, [[maybe_unused]] XErrorEvent* event)
365 {
366 #if JUCE_DEBUG_XERRORS
367 char errorStr[64] = { 0 };
368 char requestStr[64] = { 0 };
369
370 X11Symbols::getInstance()->xGetErrorText (display, event->error_code, errorStr, 64);
371 X11Symbols::getInstance()->xGetErrorDatabaseText (display, "XRequest", String (event->request_code).toUTF8(), "Unknown", requestStr, 64);
372
373 DBG ("ERROR: X returned " << errorStr << " for operation " << requestStr);
374 #endif
375
376 return 0;
377 }
378
379 static void installXErrorHandlers()
380 {
381 oldIOErrorHandler = X11Symbols::getInstance()->xSetIOErrorHandler (ioErrorHandler);
382 oldErrorHandler = X11Symbols::getInstance()->xSetErrorHandler (errorHandler);
383 }
384
385 static void removeXErrorHandlers()
386 {
387 X11Symbols::getInstance()->xSetIOErrorHandler (oldIOErrorHandler);
388 oldIOErrorHandler = {};
389
390 X11Symbols::getInstance()->xSetErrorHandler (oldErrorHandler);
391 oldErrorHandler = {};
392 }
393}
394
395//=============================== X11 - Keys ===================================
396namespace Keys
397{
398 enum MouseButtons
399 {
400 NoButton = 0,
401 LeftButton = 1,
402 MiddleButton = 2,
403 RightButton = 3,
404 WheelUp = 4,
405 WheelDown = 5
406 };
407
408 static int AltMask = 0;
409 static int NumLockMask = 0;
410 static bool numLock = false;
411 static bool capsLock = false;
412 static char keyStates [32];
413 static constexpr int extendedKeyModifier = 0x10000000;
414 static bool modifierKeysAreStale = false;
415
416 static void refreshStaleModifierKeys()
417 {
418 if (modifierKeysAreStale)
419 {
420 XWindowSystem::getInstance()->getNativeRealtimeModifiers();
421 modifierKeysAreStale = false;
422 }
423 }
424
425 // Call this function when only the mouse keys need to be refreshed e.g. when the event
426 // parameter already has information about the keys.
427 static void refreshStaleMouseKeys()
428 {
429 if (modifierKeysAreStale)
430 {
431 const auto oldMods = ModifierKeys::currentModifiers;
432 XWindowSystem::getInstance()->getNativeRealtimeModifiers();
433 ModifierKeys::currentModifiers = oldMods.withoutMouseButtons()
434 .withFlags (ModifierKeys::currentModifiers.withOnlyMouseButtons()
435 .getRawFlags());
436 modifierKeysAreStale = false;
437 }
438 }
439}
440
441const int KeyPress::spaceKey = XK_space & 0xff;
442const int KeyPress::returnKey = XK_Return & 0xff;
443const int KeyPress::escapeKey = XK_Escape & 0xff;
444const int KeyPress::backspaceKey = XK_BackSpace & 0xff;
445const int KeyPress::leftKey = (XK_Left & 0xff) | Keys::extendedKeyModifier;
446const int KeyPress::rightKey = (XK_Right & 0xff) | Keys::extendedKeyModifier;
447const int KeyPress::upKey = (XK_Up & 0xff) | Keys::extendedKeyModifier;
448const int KeyPress::downKey = (XK_Down & 0xff) | Keys::extendedKeyModifier;
449const int KeyPress::pageUpKey = (XK_Page_Up & 0xff) | Keys::extendedKeyModifier;
450const int KeyPress::pageDownKey = (XK_Page_Down & 0xff) | Keys::extendedKeyModifier;
451const int KeyPress::endKey = (XK_End & 0xff) | Keys::extendedKeyModifier;
452const int KeyPress::homeKey = (XK_Home & 0xff) | Keys::extendedKeyModifier;
453const int KeyPress::insertKey = (XK_Insert & 0xff) | Keys::extendedKeyModifier;
454const int KeyPress::deleteKey = (XK_Delete & 0xff) | Keys::extendedKeyModifier;
455const int KeyPress::tabKey = XK_Tab & 0xff;
456const int KeyPress::F1Key = (XK_F1 & 0xff) | Keys::extendedKeyModifier;
457const int KeyPress::F2Key = (XK_F2 & 0xff) | Keys::extendedKeyModifier;
458const int KeyPress::F3Key = (XK_F3 & 0xff) | Keys::extendedKeyModifier;
459const int KeyPress::F4Key = (XK_F4 & 0xff) | Keys::extendedKeyModifier;
460const int KeyPress::F5Key = (XK_F5 & 0xff) | Keys::extendedKeyModifier;
461const int KeyPress::F6Key = (XK_F6 & 0xff) | Keys::extendedKeyModifier;
462const int KeyPress::F7Key = (XK_F7 & 0xff) | Keys::extendedKeyModifier;
463const int KeyPress::F8Key = (XK_F8 & 0xff) | Keys::extendedKeyModifier;
464const int KeyPress::F9Key = (XK_F9 & 0xff) | Keys::extendedKeyModifier;
465const int KeyPress::F10Key = (XK_F10 & 0xff) | Keys::extendedKeyModifier;
466const int KeyPress::F11Key = (XK_F11 & 0xff) | Keys::extendedKeyModifier;
467const int KeyPress::F12Key = (XK_F12 & 0xff) | Keys::extendedKeyModifier;
468const int KeyPress::F13Key = (XK_F13 & 0xff) | Keys::extendedKeyModifier;
469const int KeyPress::F14Key = (XK_F14 & 0xff) | Keys::extendedKeyModifier;
470const int KeyPress::F15Key = (XK_F15 & 0xff) | Keys::extendedKeyModifier;
471const int KeyPress::F16Key = (XK_F16 & 0xff) | Keys::extendedKeyModifier;
472const int KeyPress::F17Key = (XK_F17 & 0xff) | Keys::extendedKeyModifier;
473const int KeyPress::F18Key = (XK_F18 & 0xff) | Keys::extendedKeyModifier;
474const int KeyPress::F19Key = (XK_F19 & 0xff) | Keys::extendedKeyModifier;
475const int KeyPress::F20Key = (XK_F20 & 0xff) | Keys::extendedKeyModifier;
476const int KeyPress::F21Key = (XK_F21 & 0xff) | Keys::extendedKeyModifier;
477const int KeyPress::F22Key = (XK_F22 & 0xff) | Keys::extendedKeyModifier;
478const int KeyPress::F23Key = (XK_F23 & 0xff) | Keys::extendedKeyModifier;
479const int KeyPress::F24Key = (XK_F24 & 0xff) | Keys::extendedKeyModifier;
480const int KeyPress::F25Key = (XK_F25 & 0xff) | Keys::extendedKeyModifier;
481const int KeyPress::F26Key = (XK_F26 & 0xff) | Keys::extendedKeyModifier;
482const int KeyPress::F27Key = (XK_F27 & 0xff) | Keys::extendedKeyModifier;
483const int KeyPress::F28Key = (XK_F28 & 0xff) | Keys::extendedKeyModifier;
484const int KeyPress::F29Key = (XK_F29 & 0xff) | Keys::extendedKeyModifier;
485const int KeyPress::F30Key = (XK_F30 & 0xff) | Keys::extendedKeyModifier;
486const int KeyPress::F31Key = (XK_F31 & 0xff) | Keys::extendedKeyModifier;
487const int KeyPress::F32Key = (XK_F32 & 0xff) | Keys::extendedKeyModifier;
488const int KeyPress::F33Key = (XK_F33 & 0xff) | Keys::extendedKeyModifier;
489const int KeyPress::F34Key = (XK_F34 & 0xff) | Keys::extendedKeyModifier;
490const int KeyPress::F35Key = (XK_F35 & 0xff) | Keys::extendedKeyModifier;
491const int KeyPress::numberPad0 = (XK_KP_0 & 0xff) | Keys::extendedKeyModifier;
492const int KeyPress::numberPad1 = (XK_KP_1 & 0xff) | Keys::extendedKeyModifier;
493const int KeyPress::numberPad2 = (XK_KP_2 & 0xff) | Keys::extendedKeyModifier;
494const int KeyPress::numberPad3 = (XK_KP_3 & 0xff) | Keys::extendedKeyModifier;
495const int KeyPress::numberPad4 = (XK_KP_4 & 0xff) | Keys::extendedKeyModifier;
496const int KeyPress::numberPad5 = (XK_KP_5 & 0xff) | Keys::extendedKeyModifier;
497const int KeyPress::numberPad6 = (XK_KP_6 & 0xff) | Keys::extendedKeyModifier;
498const int KeyPress::numberPad7 = (XK_KP_7 & 0xff) | Keys::extendedKeyModifier;
499const int KeyPress::numberPad8 = (XK_KP_8 & 0xff) | Keys::extendedKeyModifier;
500const int KeyPress::numberPad9 = (XK_KP_9 & 0xff) | Keys::extendedKeyModifier;
501const int KeyPress::numberPadAdd = (XK_KP_Add & 0xff) | Keys::extendedKeyModifier;
502const int KeyPress::numberPadSubtract = (XK_KP_Subtract & 0xff) | Keys::extendedKeyModifier;
503const int KeyPress::numberPadMultiply = (XK_KP_Multiply & 0xff) | Keys::extendedKeyModifier;
504const int KeyPress::numberPadDivide = (XK_KP_Divide & 0xff) | Keys::extendedKeyModifier;
505const int KeyPress::numberPadSeparator = (XK_KP_Separator & 0xff) | Keys::extendedKeyModifier;
506const int KeyPress::numberPadDecimalPoint = (XK_KP_Decimal & 0xff) | Keys::extendedKeyModifier;
507const int KeyPress::numberPadEquals = (XK_KP_Equal & 0xff) | Keys::extendedKeyModifier;
508const int KeyPress::numberPadDelete = (XK_KP_Delete & 0xff) | Keys::extendedKeyModifier;
509const int KeyPress::playKey = ((int) 0xffeeff00) | Keys::extendedKeyModifier;
510const int KeyPress::stopKey = ((int) 0xffeeff01) | Keys::extendedKeyModifier;
511const int KeyPress::fastForwardKey = ((int) 0xffeeff02) | Keys::extendedKeyModifier;
512const int KeyPress::rewindKey = ((int) 0xffeeff03) | Keys::extendedKeyModifier;
513
514static void updateKeyStates (int keycode, bool press) noexcept
515{
516 auto keybyte = keycode >> 3;
517 auto keybit = (1 << (keycode & 7));
518
519 if (press)
520 Keys::keyStates [keybyte] |= keybit;
521 else
522 Keys::keyStates [keybyte] &= ~keybit;
523}
524
525static void updateKeyModifiers (int status) noexcept
526{
527 int keyMods = 0;
528
529 if ((status & ShiftMask) != 0) keyMods |= ModifierKeys::shiftModifier;
530 if ((status & ControlMask) != 0) keyMods |= ModifierKeys::ctrlModifier;
531 if ((status & Keys::AltMask) != 0) keyMods |= ModifierKeys::altModifier;
532
533 ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withOnlyMouseButtons().withFlags (keyMods);
534
535 Keys::numLock = ((status & Keys::NumLockMask) != 0);
536 Keys::capsLock = ((status & LockMask) != 0);
537}
538
539static bool updateKeyModifiersFromSym (KeySym sym, bool press) noexcept
540{
541 int modifier = 0;
542 bool isModifier = true;
543
544 switch (sym)
545 {
546 case XK_Shift_L:
547 case XK_Shift_R: modifier = ModifierKeys::shiftModifier; break;
548
549 case XK_Control_L:
550 case XK_Control_R: modifier = ModifierKeys::ctrlModifier; break;
551
552 case XK_Alt_L:
553 case XK_Alt_R: modifier = ModifierKeys::altModifier; break;
554
555 case XK_Num_Lock:
556 if (press)
557 Keys::numLock = ! Keys::numLock;
558
559 break;
560
561 case XK_Caps_Lock:
562 if (press)
563 Keys::capsLock = ! Keys::capsLock;
564
565 break;
566
567 case XK_Scroll_Lock:
568 break;
569
570 default:
571 isModifier = false;
572 break;
573 }
574
575 ModifierKeys::currentModifiers = press ? ModifierKeys::currentModifiers.withFlags (modifier)
576 : ModifierKeys::currentModifiers.withoutFlags (modifier);
577
578 return isModifier;
579}
580
581enum
582{
583 KeyPressEventType = 2
584};
585
586//================================== X11 - Shm =================================
587#if JUCE_USE_XSHM
588 namespace XSHMHelpers
589 {
590 static int trappedErrorCode = 0;
591
592 extern "C" int errorTrapHandler (Display*, XErrorEvent* err);
593 extern "C" int errorTrapHandler (Display*, XErrorEvent* err)
594 {
595 trappedErrorCode = err->error_code;
596 return 0;
597 }
598
599 static bool isShmAvailable (::Display* display)
600 {
601 static bool isChecked = false;
602 static bool isAvailable = false;
603
604 if (! isChecked)
605 {
606 isChecked = true;
607
608 if (display != nullptr)
609 {
610 int major, minor;
611 Bool pixmaps;
612
613 XWindowSystemUtilities::ScopedXLock xLock;
614
615 if (X11Symbols::getInstance()->xShmQueryVersion (display, &major, &minor, &pixmaps))
616 {
617 trappedErrorCode = 0;
618 auto oldHandler = X11Symbols::getInstance()->xSetErrorHandler (errorTrapHandler);
619
620 XShmSegmentInfo segmentInfo;
621 zerostruct (segmentInfo);
622
623 if (auto* xImage = X11Symbols::getInstance()->xShmCreateImage (display,
624 X11Symbols::getInstance()->xDefaultVisual (display, X11Symbols::getInstance()->xDefaultScreen (display)),
625 24, ZPixmap, nullptr, &segmentInfo, 50, 50))
626 {
627 if ((segmentInfo.shmid = shmget (IPC_PRIVATE,
628 (size_t) (xImage->bytes_per_line * xImage->height),
629 IPC_CREAT | 0777)) >= 0)
630 {
631 segmentInfo.shmaddr = (char*) shmat (segmentInfo.shmid, nullptr, 0);
632
633 if (segmentInfo.shmaddr != (void*) -1)
634 {
635 segmentInfo.readOnly = False;
636 xImage->data = segmentInfo.shmaddr;
637 X11Symbols::getInstance()->xSync (display, False);
638
639 if (X11Symbols::getInstance()->xShmAttach (display, &segmentInfo) != 0)
640 {
641 X11Symbols::getInstance()->xShmDetach (display, &segmentInfo);
642 X11Symbols::getInstance()->xSync (display, False);
643
644 isAvailable = true;
645 }
646 }
647
648 X11Symbols::getInstance()->xFlush (display);
649 X11Symbols::getInstance()->xDestroyImage (xImage);
650
651 shmdt (segmentInfo.shmaddr);
652 }
653
654 shmctl (segmentInfo.shmid, IPC_RMID, nullptr);
655
656 X11Symbols::getInstance()->xSetErrorHandler (oldHandler);
657
658 if (trappedErrorCode != 0)
659 isAvailable = false;
660 }
661 }
662 }
663 }
664
665 return isAvailable;
666 }
667 }
668#endif
669
670//=============================== X11 - Render =================================
671#if JUCE_USE_XRENDER
672 namespace XRender
673 {
674 static bool isAvailable (::Display* display)
675 {
676 int major, minor;
677 return X11Symbols::getInstance()->xRenderQueryVersion (display, &major, &minor);
678 }
679
680 static bool hasCompositingWindowManager (::Display* display)
681 {
682 return display != nullptr
683 && X11Symbols::getInstance()->xGetSelectionOwner (display,
684 XWindowSystemUtilities::Atoms::getCreating (display, "_NET_WM_CM_S0")) != 0;
685 }
686
687 static XRenderPictFormat* findPictureFormat (::Display* display)
688 {
689 XWindowSystemUtilities::ScopedXLock xLock;
690
691 if (isAvailable (display))
692 {
693 if (auto* pictFormat = X11Symbols::getInstance()->xRenderFindStandardFormat (display, PictStandardARGB32))
694 {
695 XRenderPictFormat desiredFormat;
696 desiredFormat.type = PictTypeDirect;
697 desiredFormat.depth = 32;
698
699 desiredFormat.direct.alphaMask = 0xff;
700 desiredFormat.direct.redMask = 0xff;
701 desiredFormat.direct.greenMask = 0xff;
702 desiredFormat.direct.blueMask = 0xff;
703
704 desiredFormat.direct.alpha = 24;
705 desiredFormat.direct.red = 16;
706 desiredFormat.direct.green = 8;
707 desiredFormat.direct.blue = 0;
708
709 pictFormat = X11Symbols::getInstance()->xRenderFindFormat (display,
710 PictFormatType | PictFormatDepth
711 | PictFormatRedMask | PictFormatRed
712 | PictFormatGreenMask | PictFormatGreen
713 | PictFormatBlueMask | PictFormatBlue
714 | PictFormatAlphaMask | PictFormatAlpha,
715 &desiredFormat,
716 0);
717
718 return pictFormat;
719 }
720 }
721
722 return nullptr;
723 }
724 }
725#endif
726
727//================================ X11 - Visuals ===============================
728namespace Visuals
729{
730 static Visual* findVisualWithDepth (::Display* display, int desiredDepth)
731 {
732 XWindowSystemUtilities::ScopedXLock xLock;
733
734 Visual* visual = nullptr;
735 int numVisuals = 0;
736 auto desiredMask = VisualNoMask;
737 XVisualInfo desiredVisual;
738
739 desiredVisual.screen = X11Symbols::getInstance()->xDefaultScreen (display);
740 desiredVisual.depth = desiredDepth;
741
742 desiredMask = VisualScreenMask | VisualDepthMask;
743
744 if (desiredDepth == 32)
745 {
746 desiredVisual.c_class = TrueColor;
747 desiredVisual.red_mask = 0x00FF0000;
748 desiredVisual.green_mask = 0x0000FF00;
749 desiredVisual.blue_mask = 0x000000FF;
750 desiredVisual.bits_per_rgb = 8;
751
752 desiredMask |= VisualClassMask;
753 desiredMask |= VisualRedMaskMask;
754 desiredMask |= VisualGreenMaskMask;
755 desiredMask |= VisualBlueMaskMask;
756 desiredMask |= VisualBitsPerRGBMask;
757 }
758
759 if (auto xvinfos = makeXFreePtr (X11Symbols::getInstance()->xGetVisualInfo (display, desiredMask, &desiredVisual, &numVisuals)))
760 {
761 for (int i = 0; i < numVisuals; i++)
762 {
763 if (xvinfos.get()[i].depth == desiredDepth)
764 {
765 visual = xvinfos.get()[i].visual;
766 break;
767 }
768 }
769 }
770
771 return visual;
772 }
773
774 static Visual* findVisualFormat (::Display* display, int desiredDepth, int& matchedDepth)
775 {
776 Visual* visual = nullptr;
777
778 if (desiredDepth == 32)
779 {
780 #if JUCE_USE_XSHM
781 if (XSHMHelpers::isShmAvailable (display))
782 {
783 #if JUCE_USE_XRENDER
784 if (XRender::isAvailable (display))
785 {
786 if (XRender::findPictureFormat (display) != nullptr)
787 {
788 int numVisuals = 0;
789 XVisualInfo desiredVisual;
790 desiredVisual.screen = X11Symbols::getInstance()->xDefaultScreen (display);
791 desiredVisual.depth = 32;
792 desiredVisual.bits_per_rgb = 8;
793
794 if (auto xvinfos = makeXFreePtr (X11Symbols::getInstance()->xGetVisualInfo (display,
795 VisualScreenMask | VisualDepthMask | VisualBitsPerRGBMask,
796 &desiredVisual, &numVisuals)))
797 {
798 for (int i = 0; i < numVisuals; ++i)
799 {
800 auto pictVisualFormat = X11Symbols::getInstance()->xRenderFindVisualFormat (display, xvinfos.get()[i].visual);
801
802 if (pictVisualFormat != nullptr
803 && pictVisualFormat->type == PictTypeDirect
804 && pictVisualFormat->direct.alphaMask)
805 {
806 visual = xvinfos.get()[i].visual;
807 matchedDepth = 32;
808 break;
809 }
810 }
811 }
812 }
813 }
814 #endif
815 if (visual == nullptr)
816 {
817 visual = findVisualWithDepth (display, 32);
818
819 if (visual != nullptr)
820 matchedDepth = 32;
821 }
822 }
823 #endif
824 }
825
826 if (visual == nullptr && desiredDepth >= 24)
827 {
828 visual = findVisualWithDepth (display, 24);
829
830 if (visual != nullptr)
831 matchedDepth = 24;
832 }
833
834 if (visual == nullptr && desiredDepth >= 16)
835 {
836 visual = findVisualWithDepth (display, 16);
837
838 if (visual != nullptr)
839 matchedDepth = 16;
840 }
841
842 return visual;
843 }
844}
845
846//================================= X11 - Bitmap ===============================
847class XBitmapImage final : public ImagePixelData
848{
849public:
850 explicit XBitmapImage (XImage* image)
851 : ImagePixelData (image->depth == 24 ? Image::RGB : Image::ARGB, image->width, image->height),
852 xImage (image),
853 imageDepth ((unsigned int) xImage->depth)
854 {
855 pixelStride = xImage->bits_per_pixel / 8;
856 lineStride = xImage->bytes_per_line;
857 imageData = reinterpret_cast<uint8*> (xImage->data);
858 }
859
860 XBitmapImage (Image::PixelFormat format, int w, int h,
861 bool clearImage, unsigned int imageDepth_, Visual* visual)
862 : ImagePixelData (format, w, h),
863 imageDepth (imageDepth_)
864 {
865 jassert (format == Image::RGB || format == Image::ARGB);
866
867 pixelStride = (format == Image::RGB) ? 3 : 4;
868 lineStride = ((w * pixelStride + 3) & ~3);
869
871
872 #if JUCE_USE_XSHM
873 usingXShm = false;
874
875 if ((imageDepth > 16) && XSHMHelpers::isShmAvailable (display))
876 {
877 zerostruct (segmentInfo);
878
879 segmentInfo.shmid = -1;
880 segmentInfo.shmaddr = (char *) -1;
881 segmentInfo.readOnly = False;
882
883 xImage.reset (X11Symbols::getInstance()->xShmCreateImage (display, visual, imageDepth, ZPixmap, nullptr,
884 &segmentInfo, (unsigned int) w, (unsigned int) h));
885
886 if (xImage != nullptr)
887 {
888 if ((segmentInfo.shmid = shmget (IPC_PRIVATE,
889 (size_t) (xImage->bytes_per_line * xImage->height),
890 IPC_CREAT | 0777)) >= 0)
891 {
892 if (segmentInfo.shmid != -1)
893 {
894 segmentInfo.shmaddr = (char*) shmat (segmentInfo.shmid, nullptr, 0);
895
896 if (segmentInfo.shmaddr != (void*) -1)
897 {
898 segmentInfo.readOnly = False;
899
900 xImage->data = segmentInfo.shmaddr;
901 imageData = (uint8*) segmentInfo.shmaddr;
902
903 if (X11Symbols::getInstance()->xShmAttach (display, &segmentInfo) != 0)
904 usingXShm = true;
905 else
907 }
908 else
909 {
910 shmctl (segmentInfo.shmid, IPC_RMID, nullptr);
911 }
912 }
913 }
914 }
915 }
916
917 if (! isUsingXShm())
918 #endif
919 {
920 imageDataAllocated.allocate ((size_t) (lineStride * h), format == Image::ARGB && clearImage);
921 imageData = imageDataAllocated;
922
923 xImage.reset ((XImage*) ::calloc (1, sizeof (XImage)));
924
925 xImage->width = w;
926 xImage->height = h;
927 xImage->xoffset = 0;
928 xImage->format = ZPixmap;
929 xImage->data = (char*) imageData;
930 xImage->byte_order = X11Symbols::getInstance()->xImageByteOrder (display);
931 xImage->bitmap_unit = X11Symbols::getInstance()->xBitmapUnit (display);
932 xImage->bitmap_bit_order = X11Symbols::getInstance()->xBitmapBitOrder (display);
933 xImage->bitmap_pad = 32;
934 xImage->depth = pixelStride * 8;
935 xImage->bytes_per_line = lineStride;
936 xImage->bits_per_pixel = pixelStride * 8;
937 xImage->red_mask = 0x00FF0000;
938 xImage->green_mask = 0x0000FF00;
939 xImage->blue_mask = 0x000000FF;
940
941 if (imageDepth == 16)
942 {
943 int pixStride = 2;
944 auto stride = ((w * pixStride + 3) & ~3);
945
946 imageData16Bit.malloc (stride * h);
947 xImage->data = imageData16Bit;
948 xImage->bitmap_pad = 16;
949 xImage->depth = pixStride * 8;
950 xImage->bytes_per_line = stride;
951 xImage->bits_per_pixel = pixStride * 8;
952 xImage->red_mask = visual->red_mask;
953 xImage->green_mask = visual->green_mask;
954 xImage->blue_mask = visual->blue_mask;
955 }
956
957 if (! X11Symbols::getInstance()->xInitImage (xImage.get()))
959 }
960 }
961
962 ~XBitmapImage() override
963 {
965
966 if (gc != None)
967 X11Symbols::getInstance()->xFreeGC (display, gc);
968
969 #if JUCE_USE_XSHM
970 if (isUsingXShm())
971 {
972 X11Symbols::getInstance()->xShmDetach (display, &segmentInfo);
973
974 X11Symbols::getInstance()->xFlush (display);
975
976 shmdt (segmentInfo.shmaddr);
977 shmctl (segmentInfo.shmid, IPC_RMID, nullptr);
978 }
979 else
980 #endif
981 {
982 xImage->data = nullptr;
983 }
984 }
985
987 {
988 sendDataChangeMessage();
989 return std::make_unique<LowLevelGraphicsSoftwareRenderer> (Image (this));
990 }
991
992 void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y,
993 Image::BitmapData::ReadWriteMode mode) override
994 {
995 const auto offset = (size_t) (x * pixelStride + y * lineStride);
996 bitmap.data = imageData + offset;
997 bitmap.size = (size_t) (lineStride * height) - offset;
998 bitmap.pixelFormat = pixelFormat;
999 bitmap.lineStride = lineStride;
1000 bitmap.pixelStride = pixelStride;
1001
1002 if (mode != Image::BitmapData::readOnly)
1003 sendDataChangeMessage();
1004 }
1005
1007 {
1009 return nullptr;
1010 }
1011
1012 std::unique_ptr<ImageType> createType() const override { return std::make_unique<NativeImageType>(); }
1013
1014 void blitToWindow (::Window window, int dx, int dy, unsigned int dw, unsigned int dh, int sx, int sy)
1015 {
1017
1018 #if JUCE_USE_XSHM
1019 if (isUsingXShm())
1020 XWindowSystem::getInstance()->addPendingPaintForWindow (window);
1021 #endif
1022
1023 if (gc == None)
1024 {
1025 XGCValues gcvalues;
1026 gcvalues.foreground = None;
1027 gcvalues.background = None;
1028 gcvalues.function = GXcopy;
1029 gcvalues.plane_mask = AllPlanes;
1030 gcvalues.clip_mask = None;
1031 gcvalues.graphics_exposures = False;
1032
1033 gc = X11Symbols::getInstance()->xCreateGC (display, window,
1034 GCBackground | GCForeground | GCFunction | GCPlaneMask | GCClipMask | GCGraphicsExposures,
1035 &gcvalues);
1036 }
1037
1038 if (imageDepth == 16)
1039 {
1040 auto rMask = (uint32) xImage->red_mask;
1041 auto gMask = (uint32) xImage->green_mask;
1042 auto bMask = (uint32) xImage->blue_mask;
1043 auto rShiftL = (uint32) jmax (0, getShiftNeeded (rMask));
1044 auto rShiftR = (uint32) jmax (0, -getShiftNeeded (rMask));
1045 auto gShiftL = (uint32) jmax (0, getShiftNeeded (gMask));
1046 auto gShiftR = (uint32) jmax (0, -getShiftNeeded (gMask));
1047 auto bShiftL = (uint32) jmax (0, getShiftNeeded (bMask));
1048 auto bShiftR = (uint32) jmax (0, -getShiftNeeded (bMask));
1049
1050 Image::BitmapData srcData (Image (this), Image::BitmapData::readOnly);
1051
1052 for (int y = sy; y < sy + (int) dh; ++y)
1053 {
1054 auto* p = srcData.getPixelPointer (sx, y);
1055
1056 for (int x = sx; x < sx + (int) dw; ++x)
1057 {
1058 auto* pixel = (PixelRGB*) p;
1059 p += srcData.pixelStride;
1060
1061 X11Symbols::getInstance()->xPutPixel (xImage.get(), x, y,
1062 (((((uint32) pixel->getRed()) << rShiftL) >> rShiftR) & rMask)
1063 | (((((uint32) pixel->getGreen()) << gShiftL) >> gShiftR) & gMask)
1064 | (((((uint32) pixel->getBlue()) << bShiftL) >> bShiftR) & bMask));
1065 }
1066 }
1067 }
1068
1069 // blit results to screen.
1070 #if JUCE_USE_XSHM
1071 if (isUsingXShm())
1072 X11Symbols::getInstance()->xShmPutImage (display, (::Drawable) window, gc, xImage.get(), sx, sy, dx, dy, dw, dh, True);
1073 else
1074 #endif
1075 X11Symbols::getInstance()->xPutImage (display, (::Drawable) window, gc, xImage.get(), sx, sy, dx, dy, dw, dh);
1076 }
1077
1078 #if JUCE_USE_XSHM
1079 bool isUsingXShm() const noexcept { return usingXShm; }
1080 #endif
1081
1082private:
1083 //==============================================================================
1084 struct Deleter
1085 {
1086 void operator() (XImage* img) const noexcept
1087 {
1088 X11Symbols::getInstance()->xDestroyImage (img);
1089 }
1090 };
1091
1093 const unsigned int imageDepth;
1094 HeapBlock<uint8> imageDataAllocated;
1095 HeapBlock<char> imageData16Bit;
1096 int pixelStride, lineStride;
1097 uint8* imageData = nullptr;
1098 GC gc = None;
1099 ::Display* display = XWindowSystem::getInstance()->getDisplay();
1100
1101 #if JUCE_USE_XSHM
1102 XShmSegmentInfo segmentInfo;
1103 bool usingXShm;
1104 #endif
1105
1106 static int getShiftNeeded (const uint32 mask) noexcept
1107 {
1108 for (int i = 32; --i >= 0;)
1109 if (((mask >> i) & 1) != 0)
1110 return i - 7;
1111
1113 return 0;
1114 }
1115
1117};
1118
1119//=============================== X11 - Displays ===============================
1120namespace DisplayHelpers
1121{
1122 static double getDisplayDPI (::Display* display, int index)
1123 {
1124 auto widthMM = X11Symbols::getInstance()->xDisplayWidthMM (display, index);
1125 auto heightMM = X11Symbols::getInstance()->xDisplayHeightMM (display, index);
1126
1127 if (widthMM > 0 && heightMM > 0)
1128 return (((X11Symbols::getInstance()->xDisplayWidth (display, index) * 25.4) / widthMM)
1129 + ((X11Symbols::getInstance()->xDisplayHeight (display, index) * 25.4) / heightMM)) / 2.0;
1130
1131 return 96.0;
1132 }
1133
1134 static double getDisplayScale (const String& name, double dpi)
1135 {
1136 if (auto* xSettings = XWindowSystem::getInstance()->getXSettings())
1137 {
1138 auto windowScalingFactorSetting = xSettings->getSetting (XWindowSystem::getWindowScalingFactorSettingName());
1139
1140 if (windowScalingFactorSetting.isValid()
1141 && windowScalingFactorSetting.integerValue > 0)
1142 {
1143 return (double) windowScalingFactorSetting.integerValue;
1144 }
1145 }
1146
1147 if (name.isNotEmpty())
1148 {
1149 // Ubuntu and derived distributions now save a per-display scale factor as a configuration
1150 // variable. This can be changed in the Monitor system settings panel.
1151 ChildProcess dconf;
1152
1153 if (File ("/usr/bin/dconf").existsAsFile()
1154 && dconf.start ("/usr/bin/dconf read /com/ubuntu/user-interface/scale-factor", ChildProcess::wantStdOut))
1155 {
1156 if (dconf.waitForProcessToFinish (200))
1157 {
1158 auto jsonOutput = dconf.readAllProcessOutput().replaceCharacter ('\'', '"');
1159
1160 if (dconf.getExitCode() == 0 && jsonOutput.isNotEmpty())
1161 {
1162 auto jsonVar = JSON::parse (jsonOutput);
1163
1164 if (auto* object = jsonVar.getDynamicObject())
1165 {
1166 auto scaleFactorVar = object->getProperty (name);
1167
1168 if (! scaleFactorVar.isVoid())
1169 {
1170 auto scaleFactor = ((double) scaleFactorVar) / 8.0;
1171
1172 if (scaleFactor > 0.0)
1173 return scaleFactor;
1174 }
1175 }
1176 }
1177 }
1178 }
1179 }
1180
1181 {
1182 // Other gnome based distros now use gsettings for a global scale factor
1183 ChildProcess gsettings;
1184
1185 if (File ("/usr/bin/gsettings").existsAsFile()
1186 && gsettings.start ("/usr/bin/gsettings get org.gnome.desktop.interface scaling-factor", ChildProcess::wantStdOut))
1187 {
1188 if (gsettings.waitForProcessToFinish (200))
1189 {
1190 auto gsettingsOutput = StringArray::fromTokens (gsettings.readAllProcessOutput(), true);
1191
1192 if (gsettingsOutput.size() >= 2 && gsettingsOutput[1].length() > 0)
1193 {
1194 auto scaleFactor = gsettingsOutput[1].getDoubleValue();
1195
1196 if (scaleFactor > 0.0)
1197 return scaleFactor;
1198
1199 return 1.0;
1200 }
1201 }
1202 }
1203 }
1204
1205 // If no scale factor is set by GNOME or Ubuntu then calculate from monitor dpi
1206 // We use the same approach as chromium which simply divides the dpi by 96
1207 // and then rounds the result
1208 return round (dpi / 96.0);
1209 }
1210
1211 #if JUCE_USE_XINERAMA
1212 static Array<XineramaScreenInfo> xineramaQueryDisplays (::Display* display)
1213 {
1214 int major_opcode, first_event, first_error;
1215
1216 if (X11Symbols::getInstance()->xQueryExtension (display, "XINERAMA", &major_opcode, &first_event, &first_error)
1217 && (X11Symbols::getInstance()->xineramaIsActive (display) != 0))
1218 {
1219 int numScreens;
1220
1221 if (auto xinfo = makeXFreePtr (X11Symbols::getInstance()->xineramaQueryScreens (display, &numScreens)))
1222 return { xinfo.get(), numScreens };
1223 }
1224
1225 return {};
1226 }
1227 #endif
1228}
1229
1230//=============================== X11 - Pixmap =================================
1231namespace PixmapHelpers
1232{
1233 static Pixmap createColourPixmapFromImage (::Display* display, const Image& image)
1234 {
1235 XWindowSystemUtilities::ScopedXLock xLock;
1236
1237 auto width = (unsigned int) image.getWidth();
1238 auto height = (unsigned int) image.getHeight();
1239 HeapBlock<uint32> colour (width * height);
1240 int index = 0;
1241
1242 for (int y = 0; y < (int) height; ++y)
1243 for (int x = 0; x < (int) width; ++x)
1244 colour[index++] = image.getPixelAt (x, y).getARGB();
1245
1246 auto ximage = makeXFreePtr (X11Symbols::getInstance()->xCreateImage (display, (Visual*) CopyFromParent, 24, ZPixmap,
1247 0, reinterpret_cast<const char*> (colour.getData()),
1248 width, height, 32, 0));
1249
1250 auto pixmap = X11Symbols::getInstance()->xCreatePixmap (display,
1251 X11Symbols::getInstance()->xDefaultRootWindow (display),
1252 width, height, 24);
1253
1254 XValueHolder<GC> gc (X11Symbols::getInstance()->xCreateGC (display, pixmap, 0, nullptr),
1255 [&display] (GC& g) { X11Symbols::getInstance()->xFreeGC (display, g); });
1256 X11Symbols::getInstance()->xPutImage (display, pixmap, gc.value, ximage.get(), 0, 0, 0, 0, width, height);
1257
1258 return pixmap;
1259 }
1260
1261 static Pixmap createMaskPixmapFromImage (::Display* display, const Image& image)
1262 {
1263 XWindowSystemUtilities::ScopedXLock xLock;
1264
1265 auto width = (unsigned int) image.getWidth();
1266 auto height = (unsigned int) image.getHeight();
1267 auto stride = (width + 7) >> 3;
1268 HeapBlock<char> mask;
1269 mask.calloc (stride * height);
1270
1271 auto msbfirst = (X11Symbols::getInstance()->xBitmapBitOrder (display) == MSBFirst);
1272
1273 for (unsigned int y = 0; y < height; ++y)
1274 {
1275 for (unsigned int x = 0; x < width; ++x)
1276 {
1277 auto bit = (char) (1 << (msbfirst ? (7 - (x & 7)) : (x & 7)));
1278 auto offset = y * stride + (x >> 3);
1279
1280 if (image.getPixelAt ((int) x, (int) y).getAlpha() >= 128)
1281 mask[offset] |= bit;
1282 }
1283 }
1284
1285 return X11Symbols::getInstance()->xCreatePixmapFromBitmapData (display, X11Symbols::getInstance()->xDefaultRootWindow (display),
1286 mask.getData(), width, height, 1, 0, 1);
1287 }
1288}
1289
1290//=============================== X11 - Clipboard ==============================
1291namespace ClipboardHelpers
1292{
1293 //==============================================================================
1294 // Read the content of a window property as either a locale-dependent string or an utf8 string
1295 // works only for strings shorter than 1000000 bytes
1296 static String readWindowProperty (::Display* display, Window window, Atom atom)
1297 {
1298 if (display != nullptr)
1299 {
1300 XWindowSystemUtilities::GetXProperty prop (display, window, atom, 0L, 100000, false, AnyPropertyType);
1301
1302 if (prop.success)
1303 {
1304 if (prop.actualType == XWindowSystem::getInstance()->getAtoms().utf8String && prop.actualFormat == 8)
1305 return String::fromUTF8 ((const char*) prop.data, (int) prop.numItems);
1306
1307 if (prop.actualType == XA_STRING && prop.actualFormat == 8)
1308 return String ((const char*) prop.data, prop.numItems);
1309 }
1310 }
1311
1312 return {};
1313 }
1314
1315 //==============================================================================
1316 // Send a SelectionRequest to the window owning the selection and waits for its answer (with a timeout) */
1317 static bool requestSelectionContent (::Display* display, String& selectionContent, Atom selection, Atom requestedFormat)
1318 {
1319 auto property_name = X11Symbols::getInstance()->xInternAtom (display, "JUCE_SEL", false);
1320
1321 // The selection owner will be asked to set the JUCE_SEL property on the
1322 // juce_messageWindowHandle with the selection content
1323 X11Symbols::getInstance()->xConvertSelection (display, selection, requestedFormat, property_name,
1324 juce_messageWindowHandle, CurrentTime);
1325
1326 int count = 50; // will wait at most for 200 ms
1327
1328 while (--count >= 0)
1329 {
1330 XEvent event;
1331
1332 if (X11Symbols::getInstance()->xCheckTypedWindowEvent (display, juce_messageWindowHandle, SelectionNotify, &event))
1333 {
1334 if (event.xselection.property == property_name)
1335 {
1336 jassert (event.xselection.requestor == juce_messageWindowHandle);
1337
1338 selectionContent = readWindowProperty (display, event.xselection.requestor, event.xselection.property);
1339 return true;
1340 }
1341
1342 return false; // the format we asked for was denied.. (event.xselection.property == None)
1343 }
1344
1345 // not very elegant.. we could do a select() or something like that...
1346 // however clipboard content requesting is inherently slow on x11, it
1347 // often takes 50ms or more so...
1348 Thread::sleep (4);
1349 }
1350
1351 return false;
1352 }
1353
1354 //==============================================================================
1355 // Called from the event loop in juce_Messaging_linux in response to SelectionRequest events
1356 static void handleSelection (XSelectionRequestEvent& evt)
1357 {
1358 // the selection content is sent to the target window as a window property
1359 XSelectionEvent reply;
1360 reply.type = SelectionNotify;
1361 reply.display = evt.display;
1362 reply.requestor = evt.requestor;
1363 reply.selection = evt.selection;
1364 reply.target = evt.target;
1365 reply.property = None; // == "fail"
1366 reply.time = evt.time;
1367
1368 HeapBlock<char> data;
1369 int propertyFormat = 0;
1370 size_t numDataItems = 0;
1371
1372 const auto& atoms = XWindowSystem::getInstance()->getAtoms();
1373
1374 if (evt.selection == XA_PRIMARY || evt.selection == atoms.clipboard)
1375 {
1376 if (evt.target == XA_STRING || evt.target == atoms.utf8String)
1377 {
1378 auto localContent = XWindowSystem::getInstance()->getLocalClipboardContent();
1379
1380 // Translate to utf8
1381 numDataItems = localContent.getNumBytesAsUTF8();
1382 auto numBytesRequiredToStore = numDataItems + 1;
1383 data.calloc (numBytesRequiredToStore);
1384 localContent.copyToUTF8 (data, numBytesRequiredToStore);
1385 propertyFormat = 8; // bits per item
1386 }
1387 else if (evt.target == atoms.targets)
1388 {
1389 // Another application wants to know what we are able to send
1390
1391 numDataItems = 2;
1392 data.calloc (numDataItems * sizeof (Atom));
1393
1394 // Atoms are flagged as 32-bit irrespective of sizeof (Atom)
1395 propertyFormat = 32;
1396
1397 auto* dataAtoms = unalignedPointerCast<Atom*> (data.getData());
1398
1399 dataAtoms[0] = atoms.utf8String;
1400 dataAtoms[1] = XA_STRING;
1401
1402 evt.target = XA_ATOM;
1403 }
1404 }
1405 else
1406 {
1407 DBG ("requested unsupported clipboard");
1408 }
1409
1410 if (data != nullptr)
1411 {
1412 const size_t maxReasonableSelectionSize = 1000000;
1413
1414 // for very big chunks of data, we should use the "INCR" protocol , which is a pain in the *ss
1415 if (evt.property != None && numDataItems < maxReasonableSelectionSize)
1416 {
1417 X11Symbols::getInstance()->xChangeProperty (evt.display, evt.requestor,
1418 evt.property, evt.target,
1419 propertyFormat, PropModeReplace,
1420 reinterpret_cast<const unsigned char*> (data.getData()), (int) numDataItems);
1421 reply.property = evt.property; // " == success"
1422 }
1423 }
1424
1425 X11Symbols::getInstance()->xSendEvent (evt.display, evt.requestor, 0, NoEventMask, (XEvent*) &reply);
1426 }
1427}
1428
1429//==============================================================================
1430ComponentPeer* getPeerFor (::Window windowH)
1431{
1432 if (windowH == 0)
1433 return nullptr;
1434
1435 if (auto* display = XWindowSystem::getInstance()->getDisplay())
1436 {
1437 XWindowSystemUtilities::ScopedXLock xLock;
1438
1439 if (XPointer peer = nullptr;
1440 X11Symbols::getInstance()->xFindContext (display,
1441 static_cast<XID> (windowH),
1442 windowHandleXContext,
1443 &peer) == 0)
1444 {
1445 return unalignedPointerCast<ComponentPeer*> (peer);
1446 }
1447 }
1448
1449 return nullptr;
1450}
1451
1452//==============================================================================
1454
1455XWindowSystem::XWindowSystem()
1456{
1457 xIsAvailable = X11Symbols::getInstance()->loadAllSymbols();
1458
1459 if (! xIsAvailable)
1460 return;
1461
1462 if (JUCEApplicationBase::isStandaloneApp())
1463 {
1464 // Initialise xlib for multiple thread support
1465 static bool initThreadCalled = false;
1466
1467 if (! initThreadCalled)
1468 {
1469 if (! X11Symbols::getInstance()->xInitThreads())
1470 {
1471 // This is fatal! Print error and closedown
1472 Logger::outputDebugString ("Failed to initialise xlib thread support.");
1473 Process::terminate();
1474
1475 return;
1476 }
1477
1478 initThreadCalled = true;
1479 }
1480
1481 X11ErrorHandling::installXErrorHandlers();
1482 }
1483
1484 if (! initialiseXDisplay())
1485 {
1486 if (JUCEApplicationBase::isStandaloneApp())
1487 X11ErrorHandling::removeXErrorHandlers();
1488
1489 X11Symbols::deleteInstance();
1490 xIsAvailable = false;
1491 }
1492}
1493
1494XWindowSystem::~XWindowSystem()
1495{
1496 if (xIsAvailable)
1497 {
1498 destroyXDisplay();
1499
1500 if (JUCEApplicationBase::isStandaloneApp())
1501 X11ErrorHandling::removeXErrorHandlers();
1502 }
1503
1504 X11Symbols::deleteInstance();
1505 clearSingletonInstance();
1506}
1507
1508//==============================================================================
1509static int getAllEventsMask (bool ignoresMouseClicks)
1510{
1511 return NoEventMask | KeyPressMask | KeyReleaseMask
1512 | EnterWindowMask | LeaveWindowMask | PointerMotionMask | KeymapStateMask
1513 | ExposureMask | StructureNotifyMask | FocusChangeMask | PropertyChangeMask
1514 | (ignoresMouseClicks ? 0 : (ButtonPressMask | ButtonReleaseMask));
1515}
1516
1517::Window XWindowSystem::createWindow (::Window parentToAddTo, LinuxComponentPeer* peer) const
1518{
1519 if (! xIsAvailable)
1520 {
1521 // can't open a window on a system that doesn't have X11 installed!
1523 return 0;
1524 }
1525
1526 auto styleFlags = peer->getStyleFlags();
1527
1528 XWindowSystemUtilities::ScopedXLock xLock;
1529
1530 auto root = X11Symbols::getInstance()->xRootWindow (display, X11Symbols::getInstance()->xDefaultScreen (display));
1531
1532 auto visualAndDepth = displayVisuals->getBestVisualForWindow ((styleFlags & ComponentPeer::windowIsSemiTransparent) != 0);
1533
1534 auto colormap = X11Symbols::getInstance()->xCreateColormap (display, root, visualAndDepth.visual, AllocNone);
1535 X11Symbols::getInstance()->xInstallColormap (display, colormap);
1536
1537 // Set up the window attributes
1538 XSetWindowAttributes swa;
1539 swa.border_pixel = 0;
1540 swa.background_pixmap = None;
1541 swa.colormap = colormap;
1542 swa.override_redirect = ((styleFlags & ComponentPeer::windowIsTemporary) != 0) ? True : False;
1543 swa.event_mask = getAllEventsMask (styleFlags & ComponentPeer::windowIgnoresMouseClicks);
1544
1545 auto windowH = X11Symbols::getInstance()->xCreateWindow (display, parentToAddTo != 0 ? parentToAddTo : root,
1546 0, 0, 1, 1,
1547 0, visualAndDepth.depth, InputOutput, visualAndDepth.visual,
1548 CWBorderPixel | CWColormap | CWBackPixmap | CWEventMask | CWOverrideRedirect,
1549 &swa);
1550
1551 // Set the window context to identify the window handle object
1552 if (! peer->setWindowAssociation (windowH))
1553 {
1554 // Failed
1556
1557 Logger::outputDebugString ("Failed to create context information for window.\n");
1558 X11Symbols::getInstance()->xDestroyWindow (display, windowH);
1559
1560 return 0;
1561 }
1562
1563 // Set window manager hints
1564 if (auto wmHints = makeXFreePtr (X11Symbols::getInstance()->xAllocWMHints()))
1565 {
1566 wmHints->flags = InputHint | StateHint;
1567 wmHints->input = True;
1568 wmHints->initial_state = NormalState;
1569 X11Symbols::getInstance()->xSetWMHints (display, windowH, wmHints.get());
1570 }
1571
1572 // Set class hint
1573 if (auto* app = JUCEApplicationBase::getInstance())
1574 {
1575 if (auto classHint = makeXFreePtr (X11Symbols::getInstance()->xAllocClassHint()))
1576 {
1577 auto appName = app->getApplicationName();
1578 classHint->res_name = (char*) appName.getCharPointer().getAddress();
1579 classHint->res_class = (char*) appName.getCharPointer().getAddress();
1580
1581 X11Symbols::getInstance()->xSetClassHint (display, windowH, classHint.get());
1582 }
1583 }
1584
1585 // Set the window type
1586 setWindowType (windowH, styleFlags);
1587
1588 // Define decoration
1589 if ((styleFlags & ComponentPeer::windowHasTitleBar) == 0)
1590 removeWindowDecorations (windowH);
1591 else
1592 addWindowButtons (windowH, styleFlags);
1593
1594 // Associate the PID, allowing to be shut down when something goes wrong
1595 auto pid = (unsigned long) getpid();
1596 xchangeProperty (windowH, atoms.pid, XA_CARDINAL, 32, &pid, 1);
1597
1598 // Set window manager protocols
1599 xchangeProperty (windowH, atoms.protocols, XA_ATOM, 32, atoms.protocolList, 2);
1600
1601 // Set drag and drop flags
1602 xchangeProperty (windowH, atoms.XdndTypeList, XA_ATOM, 32, atoms.allowedMimeTypes, numElementsInArray (atoms.allowedMimeTypes));
1603 xchangeProperty (windowH, atoms.XdndActionList, XA_ATOM, 32, atoms.allowedActions, numElementsInArray (atoms.allowedActions));
1604 xchangeProperty (windowH, atoms.XdndActionDescription, XA_STRING, 8, "", 0);
1605
1606 auto dndVersion = XWindowSystemUtilities::Atoms::DndVersion;
1607 xchangeProperty (windowH, atoms.XdndAware, XA_ATOM, 32, &dndVersion, 1);
1608
1609 unsigned long info[2] = { 0, 1 };
1610 xchangeProperty (windowH, atoms.XembedInfo, atoms.XembedInfo, 32, (unsigned char*) info, 2);
1611
1612 return windowH;
1613}
1614
1615void XWindowSystem::destroyWindow (::Window windowH)
1616{
1617 auto* peer = dynamic_cast<LinuxComponentPeer*> (getPeerFor (windowH));
1618
1619 if (peer == nullptr)
1620 {
1622 return;
1623 }
1624
1625 #if JUCE_X11_SUPPORTS_XEMBED
1626 juce_handleXEmbedEvent (peer, nullptr);
1627 #endif
1628
1629 deleteIconPixmaps (windowH);
1630 dragAndDropStateMap.erase (peer);
1631
1632 XWindowSystemUtilities::ScopedXLock xLock;
1633
1634 peer->clearWindowAssociation();
1635
1636 X11Symbols::getInstance()->xDestroyWindow (display, windowH);
1637
1638 // Wait for it to complete and then remove any events for this
1639 // window from the event queue.
1640 X11Symbols::getInstance()->xSync (display, false);
1641
1642 XEvent event;
1643 while (X11Symbols::getInstance()->xCheckWindowEvent (display, windowH,
1644 getAllEventsMask (peer->getStyleFlags() & ComponentPeer::windowIgnoresMouseClicks),
1645 &event) == True)
1646 {}
1647
1648 #if JUCE_USE_XSHM
1649 if (XSHMHelpers::isShmAvailable (display))
1650 shmPaintsPendingMap.erase (windowH);
1651 #endif
1652}
1653
1654//==============================================================================
1655void XWindowSystem::setTitle (::Window windowH, const String& title) const
1656{
1657 jassert (windowH != 0);
1658
1659 XTextProperty nameProperty{};
1660 char* strings[] = { const_cast<char*> (title.toRawUTF8()) };
1661
1662 XWindowSystemUtilities::ScopedXLock xLock;
1663
1664 if (X11Symbols::getInstance()->xutf8TextListToTextProperty (display,
1665 strings,
1666 numElementsInArray (strings),
1667 XUTF8StringStyle,
1668 &nameProperty) >= 0)
1669 {
1670 X11Symbols::getInstance()->xSetWMName (display, windowH, &nameProperty);
1671 X11Symbols::getInstance()->xSetWMIconName (display, windowH, &nameProperty);
1672
1673 X11Symbols::getInstance()->xFree (nameProperty.value);
1674 }
1675}
1676
1677
1678void XWindowSystem::setIcon (::Window windowH, const Image& newIcon) const
1679{
1680 jassert (windowH != 0);
1681
1682 auto dataSize = newIcon.getWidth() * newIcon.getHeight() + 2;
1683 HeapBlock<unsigned long> data (dataSize);
1684
1685 int index = 0;
1686 data[index++] = (unsigned long) newIcon.getWidth();
1687 data[index++] = (unsigned long) newIcon.getHeight();
1688
1689 for (int y = 0; y < newIcon.getHeight(); ++y)
1690 for (int x = 0; x < newIcon.getWidth(); ++x)
1691 data[index++] = (unsigned long) newIcon.getPixelAt (x, y).getARGB();
1692
1693 XWindowSystemUtilities::ScopedXLock xLock;
1694 xchangeProperty (windowH, XWindowSystemUtilities::Atoms::getCreating (display, "_NET_WM_ICON"),
1695 XA_CARDINAL, 32, data.getData(), dataSize);
1696
1697 deleteIconPixmaps (windowH);
1698
1699 auto wmHints = makeXFreePtr (X11Symbols::getInstance()->xGetWMHints (display, windowH));
1700
1701 if (wmHints == nullptr)
1702 wmHints = makeXFreePtr (X11Symbols::getInstance()->xAllocWMHints());
1703
1704 if (wmHints != nullptr)
1705 {
1706 wmHints->flags |= IconPixmapHint | IconMaskHint;
1707 wmHints->icon_pixmap = PixmapHelpers::createColourPixmapFromImage (display, newIcon);
1708 wmHints->icon_mask = PixmapHelpers::createMaskPixmapFromImage (display, newIcon);
1709
1710 X11Symbols::getInstance()->xSetWMHints (display, windowH, wmHints.get());
1711 }
1712
1713 X11Symbols::getInstance()->xSync (display, False);
1714}
1715
1716void XWindowSystem::setVisible (::Window windowH, bool shouldBeVisible) const
1717{
1718 jassert (windowH != 0);
1719
1720 XWindowSystemUtilities::ScopedXLock xLock;
1721
1722 if (shouldBeVisible)
1723 X11Symbols::getInstance()->xMapWindow (display, windowH);
1724 else
1725 X11Symbols::getInstance()->xUnmapWindow (display, windowH);
1726}
1727
1728void XWindowSystem::setBounds (::Window windowH, Rectangle<int> newBounds, bool isFullScreen) const
1729{
1730 jassert (windowH != 0);
1731
1732 if (auto* peer = getPeerFor (windowH))
1733 {
1734 if (peer->isFullScreen() && ! isFullScreen)
1735 {
1736 // When transitioning back from fullscreen, we might need to remove
1737 // the FULLSCREEN window property
1738 Atom fs = XWindowSystemUtilities::Atoms::getIfExists (display, "_NET_WM_STATE_FULLSCREEN");
1739
1740 if (fs != None)
1741 {
1742 auto root = X11Symbols::getInstance()->xRootWindow (display, X11Symbols::getInstance()->xDefaultScreen (display));
1743
1744 XClientMessageEvent clientMsg;
1745 clientMsg.display = display;
1746 clientMsg.window = windowH;
1747 clientMsg.type = ClientMessage;
1748 clientMsg.format = 32;
1749 clientMsg.message_type = atoms.windowState;
1750 clientMsg.data.l[0] = 0; // Remove
1751 clientMsg.data.l[1] = (long) fs;
1752 clientMsg.data.l[2] = 0;
1753 clientMsg.data.l[3] = 1; // Normal Source
1754
1755 XWindowSystemUtilities::ScopedXLock xLock;
1756 X11Symbols::getInstance()->xSendEvent (display, root, false,
1757 SubstructureRedirectMask | SubstructureNotifyMask,
1758 (XEvent*) &clientMsg);
1759 }
1760 }
1761
1762 updateConstraints (windowH, *peer);
1763
1764 XWindowSystemUtilities::ScopedXLock xLock;
1765
1766 if (auto hints = makeXFreePtr (X11Symbols::getInstance()->xAllocSizeHints()))
1767 {
1768 hints->flags = USSize | USPosition;
1769 hints->x = newBounds.getX();
1770 hints->y = newBounds.getY();
1771 hints->width = newBounds.getWidth();
1772 hints->height = newBounds.getHeight();
1773 X11Symbols::getInstance()->xSetWMNormalHints (display, windowH, hints.get());
1774 }
1775
1776 const auto nativeWindowBorder = [&]() -> BorderSize<int>
1777 {
1778 if (const auto& frameSize = peer->getFrameSizeIfPresent())
1779 return frameSize->multipliedBy (peer->getPlatformScaleFactor());
1780
1781 return {};
1782 }();
1783
1784 X11Symbols::getInstance()->xMoveResizeWindow (display, windowH,
1785 newBounds.getX() - nativeWindowBorder.getLeft(),
1786 newBounds.getY() - nativeWindowBorder.getTop(),
1787 (unsigned int) newBounds.getWidth(),
1788 (unsigned int) newBounds.getHeight());
1789 }
1790}
1791
1792void XWindowSystem::startHostManagedResize (::Window windowH,
1793 ResizableBorderComponent::Zone zone)
1794{
1795 const auto moveResize = XWindowSystemUtilities::Atoms::getIfExists (display, "_NET_WM_MOVERESIZE");
1796
1797 if (moveResize == None)
1798 return;
1799
1800 XWindowSystemUtilities::ScopedXLock xLock;
1801
1802 X11Symbols::getInstance()->xUngrabPointer (display, CurrentTime);
1803
1804 const auto root = X11Symbols::getInstance()->xRootWindow (display, X11Symbols::getInstance()->xDefaultScreen (display));
1805 const auto mouseDown = getCurrentMousePosition();
1806
1807 XClientMessageEvent clientMsg;
1808 clientMsg.display = display;
1809 clientMsg.window = windowH;
1810 clientMsg.type = ClientMessage;
1811 clientMsg.format = 32;
1812 clientMsg.message_type = moveResize;
1813 clientMsg.data.l[0] = (long) mouseDown.x;
1814 clientMsg.data.l[1] = (long) mouseDown.y;
1815 clientMsg.data.l[2] = [&]
1816 {
1817 // It's unclear which header is supposed to contain these
1818 static constexpr auto _NET_WM_MOVERESIZE_SIZE_TOPLEFT = 0;
1819 static constexpr auto _NET_WM_MOVERESIZE_SIZE_TOP = 1;
1820 static constexpr auto _NET_WM_MOVERESIZE_SIZE_TOPRIGHT = 2;
1821 static constexpr auto _NET_WM_MOVERESIZE_SIZE_RIGHT = 3;
1822 static constexpr auto _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT = 4;
1823 static constexpr auto _NET_WM_MOVERESIZE_SIZE_BOTTOM = 5;
1824 static constexpr auto _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT = 6;
1825 static constexpr auto _NET_WM_MOVERESIZE_SIZE_LEFT = 7;
1826 static constexpr auto _NET_WM_MOVERESIZE_MOVE = 8;
1827
1828 using F = ResizableBorderComponent::Zone::Zones;
1829
1830 switch (zone.getZoneFlags())
1831 {
1832 case F::top | F::left: return _NET_WM_MOVERESIZE_SIZE_TOPLEFT;
1833 case F::top: return _NET_WM_MOVERESIZE_SIZE_TOP;
1834 case F::top | F::right: return _NET_WM_MOVERESIZE_SIZE_TOPRIGHT;
1835 case F::right: return _NET_WM_MOVERESIZE_SIZE_RIGHT;
1836 case F::bottom | F::right: return _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT;
1837 case F::bottom: return _NET_WM_MOVERESIZE_SIZE_BOTTOM;
1838 case F::bottom | F::left: return _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT;
1839 case F::left: return _NET_WM_MOVERESIZE_SIZE_LEFT;
1840 }
1841
1842 return _NET_WM_MOVERESIZE_MOVE;
1843 }();
1844 clientMsg.data.l[3] = 0;
1845 clientMsg.data.l[4] = 1;
1846
1847 X11Symbols::getInstance()->xSendEvent (display,
1848 root,
1849 false,
1850 SubstructureRedirectMask | SubstructureNotifyMask,
1851 unalignedPointerCast<XEvent*> (&clientMsg));
1852}
1853
1854void XWindowSystem::updateConstraints (::Window windowH) const
1855{
1856 if (auto* peer = getPeerFor (windowH))
1857 updateConstraints (windowH, *peer);
1858}
1859
1860void XWindowSystem::updateConstraints (::Window windowH, ComponentPeer& peer) const
1861{
1862 XWindowSystemUtilities::ScopedXLock xLock;
1863
1864 if (auto hints = makeXFreePtr (X11Symbols::getInstance()->xAllocSizeHints()))
1865 {
1866 if ((peer.getStyleFlags() & ComponentPeer::windowIsResizable) == 0)
1867 {
1868 hints->min_width = hints->max_width = peer.getBounds().getWidth();
1869 hints->min_height = hints->max_height = peer.getBounds().getHeight();
1870 hints->flags = PMinSize | PMaxSize;
1871 }
1872 else if (auto* c = peer.getConstrainer())
1873 {
1874 const auto windowBorder = [&]() -> BorderSize<int>
1875 {
1876 if (const auto& frameSize = peer.getFrameSizeIfPresent())
1877 return *frameSize;
1878
1879 return {};
1880 }();
1881
1882 const auto factor = peer.getPlatformScaleFactor();
1883 const auto leftAndRight = windowBorder.getLeftAndRight();
1884 const auto topAndBottom = windowBorder.getTopAndBottom();
1885 hints->min_width = jmax (1, (int) (factor * c->getMinimumWidth()) - leftAndRight);
1886 hints->max_width = jmax (1, (int) (factor * c->getMaximumWidth()) - leftAndRight);
1887 hints->min_height = jmax (1, (int) (factor * c->getMinimumHeight()) - topAndBottom);
1888 hints->max_height = jmax (1, (int) (factor * c->getMaximumHeight()) - topAndBottom);
1889 hints->flags = PMinSize | PMaxSize;
1890 }
1891
1892 X11Symbols::getInstance()->xSetWMNormalHints (display, windowH, hints.get());
1893 }
1894}
1895
1896bool XWindowSystem::contains (::Window windowH, Point<int> localPos) const
1897{
1898 ::Window root, child;
1899 int wx, wy;
1900 unsigned int ww, wh, bw, bitDepth;
1901
1902 XWindowSystemUtilities::ScopedXLock xLock;
1903
1904 return X11Symbols::getInstance()->xGetGeometry (display, (::Drawable) windowH, &root, &wx, &wy, &ww, &wh, &bw, &bitDepth)
1905 && X11Symbols::getInstance()->xTranslateCoordinates (display, windowH, windowH, localPos.getX(), localPos.getY(), &wx, &wy, &child)
1906 && child == None;
1907}
1908
1909ComponentPeer::OptionalBorderSize XWindowSystem::getBorderSize (::Window windowH) const
1910{
1911 jassert (windowH != 0);
1912
1913 XWindowSystemUtilities::ScopedXLock xLock;
1914 auto hints = XWindowSystemUtilities::Atoms::getIfExists (display, "_NET_FRAME_EXTENTS");
1915
1916 if (hints != None)
1917 {
1918 XWindowSystemUtilities::GetXProperty prop (display, windowH, hints, 0, 4, false, XA_CARDINAL);
1919
1920 if (prop.success && prop.actualFormat == 32)
1921 {
1922 auto data = prop.data;
1924
1925 for (auto& size : sizes)
1926 {
1927 memcpy (&size, data, sizeof (unsigned long));
1928 data += sizeof (unsigned long);
1929 }
1930
1931 return ComponentPeer::OptionalBorderSize ({ (int) sizes[2], (int) sizes[0], (int) sizes[3], (int) sizes[1] });
1932 }
1933 }
1934
1935 return {};
1936}
1937
1938Rectangle<int> XWindowSystem::getWindowBounds (::Window windowH, ::Window parentWindow)
1939{
1940 jassert (windowH != 0);
1941
1942 Window root, child;
1943 int wx = 0, wy = 0;
1944 unsigned int ww = 0, wh = 0, bw, bitDepth;
1945
1946 XWindowSystemUtilities::ScopedXLock xLock;
1947
1948 if (X11Symbols::getInstance()->xGetGeometry (display, (::Drawable) windowH, &root, &wx, &wy, &ww, &wh, &bw, &bitDepth))
1949 {
1950 int rootX = 0, rootY = 0;
1951
1952 if (! X11Symbols::getInstance()->xTranslateCoordinates (display, windowH, root, 0, 0, &rootX, &rootY, &child))
1953 rootX = rootY = 0;
1954
1955 if (parentWindow == 0)
1956 {
1957 wx = rootX;
1958 wy = rootY;
1959 }
1960 else
1961 {
1962 // XGetGeometry returns wx and wy relative to the parent window's origin.
1963 // XTranslateCoordinates returns rootX and rootY relative to the root window.
1964 parentScreenPosition = Point<int> (rootX - wx, rootY - wy);
1965 }
1966 }
1967
1968 return { wx, wy, (int) ww, (int) wh };
1969}
1970
1971Point<int> XWindowSystem::getPhysicalParentScreenPosition() const
1972{
1973 return parentScreenPosition;
1974}
1975
1976void XWindowSystem::setMinimised (::Window windowH, bool shouldBeMinimised) const
1977{
1978 jassert (windowH != 0);
1979
1980 if (shouldBeMinimised)
1981 {
1982 auto root = X11Symbols::getInstance()->xRootWindow (display, X11Symbols::getInstance()->xDefaultScreen (display));
1983
1984 XClientMessageEvent clientMsg;
1985 clientMsg.display = display;
1986 clientMsg.window = windowH;
1987 clientMsg.type = ClientMessage;
1988 clientMsg.format = 32;
1989 clientMsg.message_type = atoms.changeState;
1990 clientMsg.data.l[0] = IconicState;
1991
1992 XWindowSystemUtilities::ScopedXLock xLock;
1993 X11Symbols::getInstance()->xSendEvent (display, root, false, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*) &clientMsg);
1994 }
1995}
1996
1997bool XWindowSystem::isMinimised (::Window w) const
1998{
1999 return isHidden (w);
2000}
2001
2002void XWindowSystem::setMaximised (::Window windowH, bool shouldBeMaximised) const
2003{
2004 const auto root = X11Symbols::getInstance()->xRootWindow (display, X11Symbols::getInstance()->xDefaultScreen (display));
2005
2006 XEvent ev;
2007 ev.xclient.window = windowH;
2008 ev.xclient.type = ClientMessage;
2009 ev.xclient.format = 32;
2010 ev.xclient.message_type = XWindowSystemUtilities::Atoms::getCreating (display, "_NET_WM_STATE");
2011 ev.xclient.data.l[0] = shouldBeMaximised;
2012 ev.xclient.data.l[1] = (long) XWindowSystemUtilities::Atoms::getCreating (display, "_NET_WM_STATE_MAXIMIZED_HORZ");
2013 ev.xclient.data.l[2] = (long) XWindowSystemUtilities::Atoms::getCreating (display, "_NET_WM_STATE_MAXIMIZED_VERT");
2014 ev.xclient.data.l[3] = 1;
2015 ev.xclient.data.l[4] = 0;
2016
2017 XWindowSystemUtilities::ScopedXLock xLock;
2018 X11Symbols::getInstance()->xSendEvent (display, root, false, SubstructureRedirectMask | SubstructureNotifyMask, &ev);
2019}
2020
2021void XWindowSystem::toFront (::Window windowH, bool) const
2022{
2023 jassert (windowH != 0);
2024
2025 XWindowSystemUtilities::ScopedXLock xLock;
2026 XEvent ev;
2027 ev.xclient.type = ClientMessage;
2028 ev.xclient.serial = 0;
2029 ev.xclient.send_event = True;
2030 ev.xclient.message_type = atoms.activeWin;
2031 ev.xclient.window = windowH;
2032 ev.xclient.format = 32;
2033 ev.xclient.data.l[0] = 2;
2034 ev.xclient.data.l[1] = getUserTime (windowH);
2035 ev.xclient.data.l[2] = 0;
2036 ev.xclient.data.l[3] = 0;
2037 ev.xclient.data.l[4] = 0;
2038
2039 X11Symbols::getInstance()->xSendEvent (display, X11Symbols::getInstance()->xRootWindow (display, X11Symbols::getInstance()->xDefaultScreen (display)),
2040 False, SubstructureRedirectMask | SubstructureNotifyMask, &ev);
2041
2042 X11Symbols::getInstance()->xSync (display, False);
2043}
2044
2045void XWindowSystem::toBehind (::Window windowH, ::Window otherWindow) const
2046{
2047 jassert (windowH != 0 && otherWindow != 0);
2048
2049 const auto topLevelA = findTopLevelWindowOf (windowH);
2050 const auto topLevelB = findTopLevelWindowOf (otherWindow);
2051
2052 Window newStack[] = { topLevelA, topLevelB };
2053
2054 XWindowSystemUtilities::ScopedXLock xLock;
2055 X11Symbols::getInstance()->xRestackWindows (display, newStack, numElementsInArray (newStack));
2056}
2057
2058bool XWindowSystem::isFocused (::Window windowH) const
2059{
2060 jassert (windowH != 0);
2061
2062 int revert = 0;
2063 Window focusedWindow = 0;
2064 XWindowSystemUtilities::ScopedXLock xLock;
2065 X11Symbols::getInstance()->xGetInputFocus (display, &focusedWindow, &revert);
2066
2067 if (focusedWindow == PointerRoot)
2068 return false;
2069
2070 return isParentWindowOf (windowH, focusedWindow);
2071}
2072
2073::Window XWindowSystem::getFocusWindow (::Window windowH) const
2074{
2075 jassert (windowH != 0);
2076
2077 #if JUCE_X11_SUPPORTS_XEMBED
2078 if (auto w = (::Window) juce_getCurrentFocusWindow (dynamic_cast<LinuxComponentPeer*> (getPeerFor (windowH))))
2079 return w;
2080 #endif
2081
2082 return windowH;
2083}
2084
2085bool XWindowSystem::grabFocus (::Window windowH) const
2086{
2087 jassert (windowH != 0);
2088
2089 XWindowAttributes atts;
2090 XWindowSystemUtilities::ScopedXLock xLock;
2091
2092 if (windowH != 0
2093 && X11Symbols::getInstance()->xGetWindowAttributes (display, windowH, &atts)
2094 && atts.map_state == IsViewable
2095 && ! isFocused (windowH))
2096 {
2097 X11Symbols::getInstance()->xSetInputFocus (display, getFocusWindow (windowH), RevertToParent, (::Time) getUserTime (windowH));
2098 return true;
2099 }
2100
2101 return false;
2102}
2103
2104bool XWindowSystem::canUseSemiTransparentWindows() const
2105{
2106 #if JUCE_USE_XRENDER
2107 if (XRender::hasCompositingWindowManager (display))
2108 {
2109 int matchedDepth = 0, desiredDepth = 32;
2110
2111 return Visuals::findVisualFormat (display, desiredDepth, matchedDepth) != nullptr
2112 && matchedDepth == desiredDepth;
2113 }
2114 #endif
2115
2116 return false;
2117}
2118
2119bool XWindowSystem::canUseARGBImages() const
2120{
2121 static bool canUseARGB = false;
2122
2123 #if JUCE_USE_XSHM
2124 static bool checked = false;
2125
2126 if (! checked)
2127 {
2128 if (XSHMHelpers::isShmAvailable (display))
2129 {
2130 XWindowSystemUtilities::ScopedXLock xLock;
2131 XShmSegmentInfo segmentinfo;
2132
2133 auto testImage = X11Symbols::getInstance()->xShmCreateImage (display,
2134 X11Symbols::getInstance()->xDefaultVisual (display, X11Symbols::getInstance()->xDefaultScreen (display)),
2135 24, ZPixmap, nullptr, &segmentinfo, 64, 64);
2136
2137 canUseARGB = testImage != nullptr && testImage->bits_per_pixel == 32;
2138 X11Symbols::getInstance()->xDestroyImage (testImage);
2139 }
2140 else
2141 {
2142 canUseARGB = false;
2143 }
2144
2145 checked = true;
2146 }
2147 #endif
2148
2149 return canUseARGB;
2150}
2151
2152bool XWindowSystem::isDarkModeActive() const
2153{
2154 const auto themeName = [this]() -> String
2155 {
2156 if (xSettings != nullptr)
2157 {
2158 const auto themeNameSetting = xSettings->getSetting (getThemeNameSettingName());
2159
2160 if (themeNameSetting.isValid()
2161 && themeNameSetting.stringValue.isNotEmpty())
2162 {
2163 return themeNameSetting.stringValue;
2164 }
2165 }
2166
2167 ChildProcess gsettings;
2168
2169 if (File ("/usr/bin/gsettings").existsAsFile()
2170 && gsettings.start ("/usr/bin/gsettings get org.gnome.desktop.interface gtk-theme", ChildProcess::wantStdOut))
2171 {
2172 if (gsettings.waitForProcessToFinish (200))
2173 return gsettings.readAllProcessOutput();
2174 }
2175
2176 return {};
2177 }();
2178
2179 return (themeName.isNotEmpty()
2180 && (themeName.containsIgnoreCase ("dark") || themeName.containsIgnoreCase ("black")));
2181}
2182
2183Image XWindowSystem::createImage (bool isSemiTransparent, int width, int height, bool argb) const
2184{
2185 auto visualAndDepth = displayVisuals->getBestVisualForWindow (isSemiTransparent);
2186
2187 #if JUCE_USE_XSHM
2188 return Image (new XBitmapImage (argb ? Image::ARGB : Image::RGB,
2189 #else
2190 return Image (new XBitmapImage (Image::RGB,
2191 #endif
2192 (width + 31) & ~31,
2193 (height + 31) & ~31,
2194 false, (unsigned int) visualAndDepth.depth, visualAndDepth.visual));
2195}
2196
2197void XWindowSystem::blitToWindow (::Window windowH, Image image, Rectangle<int> destinationRect, Rectangle<int> totalRect) const
2198{
2199 jassert (windowH != 0);
2200
2201 auto* xbitmap = static_cast<XBitmapImage*> (image.getPixelData());
2202
2203 xbitmap->blitToWindow (windowH,
2204 destinationRect.getX(), destinationRect.getY(),
2205 (unsigned int) destinationRect.getWidth(),
2206 (unsigned int) destinationRect.getHeight(),
2207 destinationRect.getX() - totalRect.getX(), destinationRect.getY() - totalRect.getY());
2208}
2209
2210void XWindowSystem::processPendingPaintsForWindow (::Window windowH)
2211{
2212 #if JUCE_USE_XSHM
2213 if (! XSHMHelpers::isShmAvailable (display))
2214 return;
2215
2216 if (getNumPaintsPendingForWindow (windowH) > 0)
2217 {
2218 XWindowSystemUtilities::ScopedXLock xLock;
2219
2220 XEvent evt;
2221 while (X11Symbols::getInstance()->xCheckTypedWindowEvent (display, windowH, shmCompletionEvent, &evt))
2222 removePendingPaintForWindow (windowH);
2223 }
2224 #endif
2225}
2226
2227int XWindowSystem::getNumPaintsPendingForWindow (::Window windowH)
2228{
2229 #if JUCE_USE_XSHM
2230 if (XSHMHelpers::isShmAvailable (display))
2231 return shmPaintsPendingMap[windowH];
2232 #endif
2233
2234 return 0;
2235}
2236
2237void XWindowSystem::addPendingPaintForWindow (::Window windowH)
2238{
2239 #if JUCE_USE_XSHM
2240 if (XSHMHelpers::isShmAvailable (display))
2241 ++shmPaintsPendingMap[windowH];
2242 #endif
2243}
2244
2245void XWindowSystem::removePendingPaintForWindow (::Window windowH)
2246{
2247 #if JUCE_USE_XSHM
2248 if (XSHMHelpers::isShmAvailable (display))
2249 --shmPaintsPendingMap[windowH];
2250 #endif
2251}
2252
2253void XWindowSystem::setScreenSaverEnabled (bool enabled) const
2254{
2255 using tXScreenSaverSuspend = void (*) (Display*, Bool);
2256 static tXScreenSaverSuspend xScreenSaverSuspend = nullptr;
2257
2258 if (xScreenSaverSuspend == nullptr)
2259 if (void* h = dlopen ("libXss.so.1", RTLD_GLOBAL | RTLD_NOW))
2260 xScreenSaverSuspend = (tXScreenSaverSuspend) dlsym (h, "XScreenSaverSuspend");
2261
2262 XWindowSystemUtilities::ScopedXLock xLock;
2263
2264 NullCheckedInvocation::invoke (xScreenSaverSuspend, display, ! enabled);
2265}
2266
2267Point<float> XWindowSystem::getCurrentMousePosition() const
2268{
2269 Window root, child;
2270 int x, y, winx, winy;
2271 unsigned int mask;
2272
2273 XWindowSystemUtilities::ScopedXLock xLock;
2274
2275 if (X11Symbols::getInstance()->xQueryPointer (display,
2276 X11Symbols::getInstance()->xRootWindow (display,
2277 X11Symbols::getInstance()->xDefaultScreen (display)),
2278 &root, &child,
2279 &x, &y, &winx, &winy, &mask) == False)
2280 {
2281 x = y = -1;
2282 }
2283
2284 return { (float) x, (float) y };
2285}
2286
2287void XWindowSystem::setMousePosition (Point<float> pos) const
2288{
2289 XWindowSystemUtilities::ScopedXLock xLock;
2290
2291 auto root = X11Symbols::getInstance()->xRootWindow (display,
2292 X11Symbols::getInstance()->xDefaultScreen (display));
2293
2294 X11Symbols::getInstance()->xWarpPointer (display, None, root, 0, 0, 0, 0,
2295 roundToInt (pos.getX()), roundToInt (pos.getY()));
2296}
2297
2298Cursor XWindowSystem::createCustomMouseCursorInfo (const Image& image, Point<int> hotspot) const
2299{
2300 if (display == nullptr)
2301 return {};
2302
2303 XWindowSystemUtilities::ScopedXLock xLock;
2304
2305 auto imageW = (unsigned int) image.getWidth();
2306 auto imageH = (unsigned int) image.getHeight();
2307 auto hotspotX = hotspot.x;
2308 auto hotspotY = hotspot.y;
2309
2310 #if JUCE_USE_XCURSOR
2311 if (auto xcImage = makeDeletedPtr (X11Symbols::getInstance()->xcursorImageCreate ((int) imageW, (int) imageH),
2312 [] (XcursorImage* i) { X11Symbols::getInstance()->xcursorImageDestroy (i); }))
2313 {
2314 xcImage->xhot = (XcursorDim) hotspotX;
2315 xcImage->yhot = (XcursorDim) hotspotY;
2316 auto* dest = xcImage->pixels;
2317
2318 for (int y = 0; y < (int) imageH; ++y)
2319 for (int x = 0; x < (int) imageW; ++x)
2320 *dest++ = image.getPixelAt (x, y).getARGB();
2321
2322 auto result = X11Symbols::getInstance()->xcursorImageLoadCursor (display, xcImage.get());
2323
2324 if (result != Cursor{})
2325 return result;
2326 }
2327 #endif
2328
2329 auto root = X11Symbols::getInstance()->xRootWindow (display,
2330 X11Symbols::getInstance()->xDefaultScreen (display));
2331
2332 unsigned int cursorW, cursorH;
2333 if (! X11Symbols::getInstance()->xQueryBestCursor (display, root, imageW, imageH, &cursorW, &cursorH))
2334 return {};
2335
2336 Image im (Image::ARGB, (int) cursorW, (int) cursorH, true);
2337
2338 {
2339 Graphics g (im);
2340
2341 if (imageW > cursorW || imageH > cursorH)
2342 {
2343 hotspotX = (hotspotX * (int) cursorW) / (int) imageW;
2344 hotspotY = (hotspotY * (int) cursorH) / (int) imageH;
2345
2346 g.drawImage (image, Rectangle<float> ((float) imageW, (float) imageH),
2347 RectanglePlacement::xLeft | RectanglePlacement::yTop | RectanglePlacement::onlyReduceInSize);
2348 }
2349 else
2350 {
2351 g.drawImageAt (image, 0, 0);
2352 }
2353 }
2354
2355 auto stride = (cursorW + 7) >> 3;
2356 HeapBlock<char> maskPlane, sourcePlane;
2357 maskPlane.calloc (stride * cursorH);
2358 sourcePlane.calloc (stride * cursorH);
2359
2360 auto msbfirst = (X11Symbols::getInstance()->xBitmapBitOrder (display) == MSBFirst);
2361
2362 for (auto y = (int) cursorH; --y >= 0;)
2363 {
2364 for (auto x = (int) cursorW; --x >= 0;)
2365 {
2366 auto mask = (char) (1 << (msbfirst ? (7 - (x & 7)) : (x & 7)));
2367 auto offset = (unsigned int) y * stride + ((unsigned int) x >> 3);
2368
2369 auto c = im.getPixelAt (x, y);
2370
2371 if (c.getAlpha() >= 128) maskPlane[offset] |= mask;
2372 if (c.getBrightness() >= 0.5f) sourcePlane[offset] |= mask;
2373 }
2374 }
2375
2376 auto xFreePixmap = [this] (Pixmap& p) { X11Symbols::getInstance()->xFreePixmap (display, p); };
2377 XValueHolder<Pixmap> sourcePixmap (X11Symbols::getInstance()->xCreatePixmapFromBitmapData (display, root, sourcePlane.getData(), cursorW, cursorH, 0xffff, 0, 1), xFreePixmap);
2378 XValueHolder<Pixmap> maskPixmap (X11Symbols::getInstance()->xCreatePixmapFromBitmapData (display, root, maskPlane.getData(), cursorW, cursorH, 0xffff, 0, 1), xFreePixmap);
2379
2380 XColor white, black;
2381 black.red = black.green = black.blue = 0;
2382 white.red = white.green = white.blue = 0xffff;
2383
2384 return X11Symbols::getInstance()->xCreatePixmapCursor (display, sourcePixmap.value, maskPixmap.value, &white, &black,
2385 (unsigned int) hotspotX, (unsigned int) hotspotY);
2386}
2387
2388void XWindowSystem::deleteMouseCursor (Cursor cursorHandle) const
2389{
2390 if (cursorHandle != Cursor{} && display != nullptr)
2391 {
2392 XWindowSystemUtilities::ScopedXLock xLock;
2393 X11Symbols::getInstance()->xFreeCursor (display, (Cursor) cursorHandle);
2394 }
2395}
2396
2397static Cursor createDraggingHandCursor()
2398{
2399 constexpr unsigned char dragHandData[] = {
2400 71,73,70,56,57,97,16,0,16,0,145,2,0,0,0,0,255,255,255,0,0,0,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0,16,0,
2401 0,2,52,148,47,0,200,185,16,130,90,12,74,139,107,84,123,39,132,117,151,116,132,146,248,60,209,138,98,22,203,
2402 114,34,236,37,52,77,217, 247,154,191,119,110,240,193,128,193,95,163,56,60,234,98,135,2,0,59
2403 };
2404
2405 auto image = ImageFileFormat::loadFrom (dragHandData, (size_t) numElementsInArray (dragHandData));
2406 return XWindowSystem::getInstance()->createCustomMouseCursorInfo (std::move (image), { 8, 7 });
2407}
2408
2409Cursor XWindowSystem::createStandardMouseCursor (MouseCursor::StandardCursorType type) const
2410{
2411 if (display == nullptr)
2412 return None;
2413
2414 unsigned int shape;
2415
2416 switch (type)
2417 {
2418 case MouseCursor::NormalCursor:
2419 case MouseCursor::ParentCursor: return None; // Use parent cursor
2420 case MouseCursor::NoCursor: return XWindowSystem::createCustomMouseCursorInfo (Image (Image::ARGB, 16, 16, true), {});
2421
2422 case MouseCursor::WaitCursor: shape = XC_watch; break;
2423 case MouseCursor::IBeamCursor: shape = XC_xterm; break;
2424 case MouseCursor::PointingHandCursor: shape = XC_hand2; break;
2425 case MouseCursor::LeftRightResizeCursor: shape = XC_sb_h_double_arrow; break;
2426 case MouseCursor::UpDownResizeCursor: shape = XC_sb_v_double_arrow; break;
2427 case MouseCursor::UpDownLeftRightResizeCursor: shape = XC_fleur; break;
2428 case MouseCursor::TopEdgeResizeCursor: shape = XC_top_side; break;
2429 case MouseCursor::BottomEdgeResizeCursor: shape = XC_bottom_side; break;
2430 case MouseCursor::LeftEdgeResizeCursor: shape = XC_left_side; break;
2431 case MouseCursor::RightEdgeResizeCursor: shape = XC_right_side; break;
2432 case MouseCursor::TopLeftCornerResizeCursor: shape = XC_top_left_corner; break;
2433 case MouseCursor::TopRightCornerResizeCursor: shape = XC_top_right_corner; break;
2434 case MouseCursor::BottomLeftCornerResizeCursor: shape = XC_bottom_left_corner; break;
2435 case MouseCursor::BottomRightCornerResizeCursor: shape = XC_bottom_right_corner; break;
2436 case MouseCursor::CrosshairCursor: shape = XC_crosshair; break;
2437 case MouseCursor::DraggingHandCursor: return createDraggingHandCursor();
2438
2439 case MouseCursor::CopyingCursor:
2440 {
2441 constexpr unsigned char copyCursorData[] = {
2442 71,73,70,56,57,97,21,0,21,0,145,0,0,0,0,0,255,255,255,0,128,128,255,255,255,33,249,4,1,0,0,3,0,44,0,0,0,0,
2443 21,0,21,0,0,2,72,4,134,169,171,16,199,98,11,79,90,71,161,93,56,111,78,133,218,215,137,31,82,154,100,200,
2444 86,91,202,142,12,108,212,87,235,174,15,54,214,126,237,226,37,96,59,141,16,37,18,201,142,157,230,204,51,112,
2445 252,114,147,74,83,5,50,68,147,208,217,16,71,149,252,124,5,0,59,0,0
2446 };
2447
2448 auto image = ImageFileFormat::loadFrom (copyCursorData, (size_t) numElementsInArray (copyCursorData));
2449 return createCustomMouseCursorInfo (std::move (image), { 1, 3 });
2450 }
2451
2452 case MouseCursor::NumStandardCursorTypes:
2453 default:
2454 {
2456 return None;
2457 }
2458 }
2459
2460 XWindowSystemUtilities::ScopedXLock xLock;
2461
2462 return X11Symbols::getInstance()->xCreateFontCursor (display, shape);
2463}
2464
2465void XWindowSystem::showCursor (::Window windowH, Cursor cursorHandle) const
2466{
2467 jassert (windowH != 0);
2468
2469 XWindowSystemUtilities::ScopedXLock xLock;
2470 X11Symbols::getInstance()->xDefineCursor (display, windowH, (Cursor) cursorHandle);
2471}
2472
2473bool XWindowSystem::isKeyCurrentlyDown (int keyCode) const
2474{
2475 int keysym;
2476
2477 if (keyCode & Keys::extendedKeyModifier)
2478 {
2479 keysym = 0xff00 | (keyCode & 0xff);
2480 }
2481 else
2482 {
2483 keysym = keyCode;
2484
2485 if (keysym == (XK_Tab & 0xff)
2486 || keysym == (XK_Return & 0xff)
2487 || keysym == (XK_Escape & 0xff)
2488 || keysym == (XK_BackSpace & 0xff))
2489 {
2490 keysym |= 0xff00;
2491 }
2492 }
2493
2494 XWindowSystemUtilities::ScopedXLock xLock;
2495
2496 auto keycode = X11Symbols::getInstance()->xKeysymToKeycode (display, (KeySym) keysym);
2497 auto keybyte = keycode >> 3;
2498 auto keybit = (1 << (keycode & 7));
2499
2500 return (Keys::keyStates [keybyte] & keybit) != 0;
2501}
2502
2503ModifierKeys XWindowSystem::getNativeRealtimeModifiers() const
2504{
2505 ::Window root, child;
2506 int x, y, winx, winy;
2507 unsigned int mask;
2508 int mouseMods = 0;
2509
2510 XWindowSystemUtilities::ScopedXLock xLock;
2511
2512 if (X11Symbols::getInstance()->xQueryPointer (display,
2513 X11Symbols::getInstance()->xRootWindow (display,
2514 X11Symbols::getInstance()->xDefaultScreen (display)),
2515 &root, &child, &x, &y, &winx, &winy, &mask) != False)
2516 {
2517 if ((mask & Button1Mask) != 0) mouseMods |= ModifierKeys::leftButtonModifier;
2518 if ((mask & Button2Mask) != 0) mouseMods |= ModifierKeys::middleButtonModifier;
2519 if ((mask & Button3Mask) != 0) mouseMods |= ModifierKeys::rightButtonModifier;
2520 }
2521
2522 ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutMouseButtons().withFlags (mouseMods);
2523
2524 // We are keeping track of the state of modifier keys and mouse buttons with the assumption that
2525 // for every mouse down we are going to receive a mouse up etc.
2526 //
2527 // This assumption is broken when getNativeRealtimeModifiers() is called. If for example we call
2528 // this function when the mouse cursor is in another application and the mouse button happens to
2529 // be down, then its represented state in currentModifiers may remain down indefinitely, since
2530 // we aren't going to receive an event when it's released.
2531 //
2532 // We mark this state in this variable, and we can restore synchronization when our window
2533 // receives an event again.
2534 Keys::modifierKeysAreStale = true;
2535
2536 return ModifierKeys::currentModifiers;
2537}
2538
2539static bool hasWorkAreaData (const XWindowSystemUtilities::GetXProperty& prop)
2540{
2541 return prop.success
2542 && prop.actualType == XA_CARDINAL
2543 && prop.actualFormat == 32
2544 && prop.numItems == 4
2545 && prop.data != nullptr;
2546}
2547
2548static Rectangle<int> getWorkArea (const XWindowSystemUtilities::GetXProperty& prop)
2549{
2550 if (hasWorkAreaData (prop))
2551 {
2552 auto* positionData = prop.data;
2553 std::array<long, 4> position;
2554
2555 for (auto& p : position)
2556 {
2557 memcpy (&p, positionData, sizeof (long));
2558 positionData += sizeof (long);
2559 }
2560
2561 return { (int) position[0], (int) position[1],
2562 (int) position[2], (int) position[3] };
2563 }
2564
2565 return {};
2566}
2567
2568Array<Displays::Display> XWindowSystem::findDisplays (float masterScale) const
2569{
2570 Array<Displays::Display> displays;
2571 auto workAreaHints = XWindowSystemUtilities::Atoms::getIfExists (display, "_NET_WORKAREA");
2572
2573 #if JUCE_USE_XRANDR
2574 if (workAreaHints != None)
2575 {
2576 int major_opcode, first_event, first_error;
2577
2578 if (X11Symbols::getInstance()->xQueryExtension (display, "RANDR", &major_opcode, &first_event, &first_error))
2579 {
2580 auto numMonitors = X11Symbols::getInstance()->xScreenCount (display);
2581 auto mainDisplay = X11Symbols::getInstance()->xRRGetOutputPrimary (display, X11Symbols::getInstance()->xRootWindow (display, 0));
2582
2583 for (int i = 0; i < numMonitors; ++i)
2584 {
2585 auto rootWindow = X11Symbols::getInstance()->xRootWindow (display, i);
2586 XWindowSystemUtilities::GetXProperty prop (display, rootWindow, workAreaHints, 0, 4, false, XA_CARDINAL);
2587
2588 if (! hasWorkAreaData (prop))
2589 continue;
2590
2591 if (auto screens = makeDeletedPtr (X11Symbols::getInstance()->xRRGetScreenResources (display, rootWindow),
2592 [] (XRRScreenResources* srs) { X11Symbols::getInstance()->xRRFreeScreenResources (srs); }))
2593 {
2594 for (int j = 0; j < screens->noutput; ++j)
2595 {
2596 if (screens->outputs[j])
2597 {
2598 // Xrandr on the raspberry pi fails to determine the main display (mainDisplay == 0)!
2599 // Detect this edge case and make the first found display the main display
2600 if (! mainDisplay)
2601 mainDisplay = screens->outputs[j];
2602
2603 if (auto output = makeDeletedPtr (X11Symbols::getInstance()->xRRGetOutputInfo (display, screens.get(), screens->outputs[j]),
2604 [] (XRROutputInfo* oi) { X11Symbols::getInstance()->xRRFreeOutputInfo (oi); }))
2605 {
2606 if (output->crtc)
2607 {
2608 if (auto crtc = makeDeletedPtr (X11Symbols::getInstance()->xRRGetCrtcInfo (display, screens.get(), output->crtc),
2609 [] (XRRCrtcInfo* ci) { X11Symbols::getInstance()->xRRFreeCrtcInfo (ci); }))
2610 {
2611 Displays::Display d;
2612 d.totalArea = { crtc->x, crtc->y, (int) crtc->width, (int) crtc->height };
2613 d.isMain = (mainDisplay == screens->outputs[j]) && (i == 0);
2614 d.dpi = DisplayHelpers::getDisplayDPI (display, 0);
2615
2616 d.verticalFrequencyHz = [&]() -> std::optional<double>
2617 {
2618 if (crtc->mode != None)
2619 {
2620 if (auto it = std::find_if (screens->modes,
2621 screens->modes + screens->nmode,
2622 [&crtc] (const auto& m) { return m.id == crtc->mode; });
2623 it != screens->modes + screens->nmode)
2624 {
2625 return (double) it->dotClock / ((double) it->hTotal * (double) it->vTotal);
2626 }
2627 }
2628
2629 return {};
2630 }();
2631
2632 // The raspberry pi returns a zero sized display, so we need to guard for divide-by-zero
2633 if (output->mm_width > 0 && output->mm_height > 0)
2634 d.dpi = ((static_cast<double> (crtc->width) * 25.4 * 0.5) / static_cast<double> (output->mm_width))
2635 + ((static_cast<double> (crtc->height) * 25.4 * 0.5) / static_cast<double> (output->mm_height));
2636
2637 auto scale = DisplayHelpers::getDisplayScale (output->name, d.dpi);
2638 scale = (scale <= 0.1 || ! JUCEApplicationBase::isStandaloneApp()) ? 1.0 : scale;
2639
2640 d.scale = masterScale * scale;
2641
2642 if (d.isMain)
2643 displays.insert (0, d);
2644 else
2645 displays.add (d);
2646 }
2647 }
2648 }
2649 }
2650 }
2651 }
2652 }
2653
2654 if (! displays.isEmpty() && ! displays.getReference (0).isMain)
2655 displays.getReference (0).isMain = true;
2656 }
2657 }
2658
2659 if (displays.isEmpty())
2660 #endif
2661 #if JUCE_USE_XINERAMA
2662 {
2663 auto screens = DisplayHelpers::xineramaQueryDisplays (display);
2664 auto numMonitors = screens.size();
2665
2666 for (int index = 0; index < numMonitors; ++index)
2667 {
2668 for (auto j = numMonitors; --j >= 0;)
2669 {
2670 if (screens[j].screen_number == index)
2671 {
2672 Displays::Display d;
2673 d.totalArea = { screens[j].x_org, screens[j].y_org,
2674 screens[j].width, screens[j].height };
2675 d.isMain = (index == 0);
2676 d.scale = masterScale;
2677 d.dpi = DisplayHelpers::getDisplayDPI (display, 0); // (all screens share the same DPI)
2678
2679 displays.add (d);
2680 }
2681 }
2682 }
2683 }
2684
2685 if (displays.isEmpty())
2686 #endif
2687 {
2688 if (workAreaHints != None)
2689 {
2690 auto numMonitors = X11Symbols::getInstance()->xScreenCount (display);
2691
2692 for (int i = 0; i < numMonitors; ++i)
2693 {
2694 XWindowSystemUtilities::GetXProperty prop (display,
2695 X11Symbols::getInstance()->xRootWindow (display, i),
2696 workAreaHints, 0, 4, false, XA_CARDINAL);
2697
2698 auto workArea = getWorkArea (prop);
2699
2700 if (! workArea.isEmpty())
2701 {
2702 Displays::Display d;
2703
2704 d.totalArea = workArea;
2705 d.isMain = displays.isEmpty();
2706 d.scale = masterScale;
2707 d.dpi = DisplayHelpers::getDisplayDPI (display, i);
2708
2709 displays.add (d);
2710 }
2711 }
2712 }
2713
2714 if (displays.isEmpty())
2715 {
2716 Displays::Display d;
2717 d.totalArea = { X11Symbols::getInstance()->xDisplayWidth (display, X11Symbols::getInstance()->xDefaultScreen (display)),
2718 X11Symbols::getInstance()->xDisplayHeight (display, X11Symbols::getInstance()->xDefaultScreen (display)) };
2719 d.isMain = true;
2720 d.scale = masterScale;
2721 d.dpi = DisplayHelpers::getDisplayDPI (display, 0);
2722
2723 displays.add (d);
2724 }
2725 }
2726
2727 for (auto& d : displays)
2728 d.userArea = d.totalArea; // JUCE currently does not support requesting the user area on Linux
2729
2730 return displays;
2731}
2732
2733::Window XWindowSystem::createKeyProxy (::Window windowH)
2734{
2735 jassert (windowH != 0);
2736
2737 XSetWindowAttributes swa;
2738 swa.event_mask = KeyPressMask | KeyReleaseMask | FocusChangeMask;
2739
2740 auto keyProxy = X11Symbols::getInstance()->xCreateWindow (display, windowH,
2741 -1, -1, 1, 1, 0, 0,
2742 InputOnly, CopyFromParent,
2743 CWEventMask,
2744 &swa);
2745
2746 X11Symbols::getInstance()->xMapWindow (display, keyProxy);
2747
2748 return keyProxy;
2749}
2750
2751void XWindowSystem::deleteKeyProxy (::Window keyProxy) const
2752{
2753 jassert (keyProxy != 0);
2754
2755 X11Symbols::getInstance()->xDestroyWindow (display, keyProxy);
2756 X11Symbols::getInstance()->xSync (display, false);
2757
2758 XEvent event;
2759 while (X11Symbols::getInstance()->xCheckWindowEvent (display, keyProxy, getAllEventsMask (false), &event) == True)
2760 {}
2761}
2762
2763bool XWindowSystem::externalDragFileInit (LinuxComponentPeer* peer, const StringArray& files, bool, std::function<void()>&& callback) const
2764{
2765 auto& dragState = dragAndDropStateMap[peer];
2766
2767 if (dragState.isDragging())
2768 return false;
2769
2770 StringArray uriList;
2771
2772 for (auto& f : files)
2773 {
2774 if (f.matchesWildcard ("?*://*", false))
2775 uriList.add (f);
2776 else
2777 uriList.add ("file://" + f);
2778 }
2779
2780 return dragState.externalDragInit ((::Window) peer->getNativeHandle(), false, uriList.joinIntoString ("\r\n"), std::move (callback));
2781}
2782
2783bool XWindowSystem::externalDragTextInit (LinuxComponentPeer* peer, const String& text, std::function<void()>&& callback) const
2784{
2785 auto& dragState = dragAndDropStateMap[peer];
2786
2787 if (dragState.isDragging())
2788 return false;
2789
2790 return dragState.externalDragInit ((::Window) peer->getNativeHandle(), true, text, std::move (callback));
2791}
2792
2793void XWindowSystem::copyTextToClipboard (const String& clipText)
2794{
2795 localClipboardContent = clipText;
2796
2797 X11Symbols::getInstance()->xSetSelectionOwner (display, XA_PRIMARY, juce_messageWindowHandle, CurrentTime);
2798 X11Symbols::getInstance()->xSetSelectionOwner (display, atoms.clipboard, juce_messageWindowHandle, CurrentTime);
2799}
2800
2801String XWindowSystem::getTextFromClipboard() const
2802{
2803 /* 1) try to read from the "CLIPBOARD" selection first (the "high
2804 level" clipboard that is supposed to be filled by ctrl-C
2805 etc). When a clipboard manager is running, the content of this
2806 selection is preserved even when the original selection owner
2807 exits.
2808
2809 2) and then try to read from "PRIMARY" selection (the "legacy" selection
2810 filled by good old x11 apps such as xterm)
2811 */
2812
2813 auto getContentForSelection = [this] (Atom selectionAtom) -> String
2814 {
2815 auto selectionOwner = X11Symbols::getInstance()->xGetSelectionOwner (display, selectionAtom);
2816
2817 if (selectionOwner == None)
2818 return {};
2819
2820 if (selectionOwner == juce_messageWindowHandle)
2821 return localClipboardContent;
2822
2823 String content;
2824
2825 if (! ClipboardHelpers::requestSelectionContent (display, content, selectionAtom, atoms.utf8String))
2826 ClipboardHelpers::requestSelectionContent (display, content, selectionAtom, XA_STRING);
2827
2828 return content;
2829 };
2830
2831 auto content = getContentForSelection (atoms.clipboard);
2832
2833 if (content.isEmpty())
2834 content = getContentForSelection (XA_PRIMARY);
2835
2836 return content;
2837}
2838
2839//==============================================================================
2840::Window XWindowSystem::findTopLevelWindowOf (::Window w) const
2841{
2842 if (w == 0)
2843 return 0;
2844
2845 Window* windowList = nullptr;
2846 uint32 windowListSize = 0;
2847 Window parent, root;
2848
2849 XWindowSystemUtilities::ScopedXLock xLock;
2850 const auto result = X11Symbols::getInstance()->xQueryTree (display, w, &root, &parent, &windowList, &windowListSize);
2851 const auto deleter = makeXFreePtr (windowList);
2852
2853 if (result == 0)
2854 return 0;
2855
2856 if (parent == root)
2857 return w;
2858
2859 return findTopLevelWindowOf (parent);
2860}
2861
2862bool XWindowSystem::isParentWindowOf (::Window windowH, ::Window possibleChild) const
2863{
2864 if (windowH == 0 || possibleChild == 0)
2865 return false;
2866
2867 if (possibleChild == windowH)
2868 return true;
2869
2870 Window* windowList = nullptr;
2871 uint32 windowListSize = 0;
2872 Window parent, root;
2873
2874 XWindowSystemUtilities::ScopedXLock xLock;
2875 const auto result = X11Symbols::getInstance()->xQueryTree (display, possibleChild, &root, &parent, &windowList, &windowListSize);
2876 const auto deleter = makeXFreePtr (windowList);
2877
2878 if (result == 0 || parent == root)
2879 return false;
2880
2881 return isParentWindowOf (windowH, parent);
2882}
2883
2884bool XWindowSystem::isFrontWindow (::Window windowH) const
2885{
2886 jassert (windowH != 0);
2887
2888 Window* windowList = nullptr;
2889 uint32 windowListSize = 0;
2890
2891 XWindowSystemUtilities::ScopedXLock xLock;
2892 Window parent;
2893 auto root = X11Symbols::getInstance()->xRootWindow (display, X11Symbols::getInstance()->xDefaultScreen (display));
2894
2895 const auto queryResult = X11Symbols::getInstance()->xQueryTree (display, root, &root, &parent, &windowList, &windowListSize);
2896 const auto deleter = makeXFreePtr (windowList);
2897
2898 if (queryResult == 0)
2899 return false;
2900
2901 for (int i = (int) windowListSize; --i >= 0;)
2902 {
2903 if (auto* peer = dynamic_cast<LinuxComponentPeer*> (getPeerFor (windowList[i])))
2904 return peer == dynamic_cast<LinuxComponentPeer*> (getPeerFor (windowH));
2905 }
2906
2907 return false;
2908}
2909
2910void XWindowSystem::xchangeProperty (::Window windowH, Atom property, Atom type, int format, const void* data, int numElements) const
2911{
2912 jassert (windowH != 0);
2913
2914 X11Symbols::getInstance()->xChangeProperty (display, windowH, property, type, format, PropModeReplace, (const unsigned char*) data, numElements);
2915}
2916
2917void XWindowSystem::removeWindowDecorations (::Window windowH) const
2918{
2919 jassert (windowH != 0);
2920
2921 auto hints = XWindowSystemUtilities::Atoms::getIfExists (display, "_MOTIF_WM_HINTS");
2922
2923 if (hints != None)
2924 {
2925 MotifWmHints motifHints;
2926 zerostruct (motifHints);
2927
2928 motifHints.flags = 2; /* MWM_HINTS_DECORATIONS */
2929 motifHints.decorations = 0;
2930
2931 XWindowSystemUtilities::ScopedXLock xLock;
2932 xchangeProperty (windowH, hints, hints, 32, &motifHints, 4);
2933 }
2934
2935 hints = XWindowSystemUtilities::Atoms::getIfExists (display, "_WIN_HINTS");
2936
2937 if (hints != None)
2938 {
2939 long gnomeHints = 0;
2940
2941 XWindowSystemUtilities::ScopedXLock xLock;
2942 xchangeProperty (windowH, hints, hints, 32, &gnomeHints, 1);
2943 }
2944
2945 hints = XWindowSystemUtilities::Atoms::getIfExists (display, "KWM_WIN_DECORATION");
2946
2947 if (hints != None)
2948 {
2949 long kwmHints = 2; /*KDE_tinyDecoration*/
2950
2951 XWindowSystemUtilities::ScopedXLock xLock;
2952 xchangeProperty (windowH, hints, hints, 32, &kwmHints, 1);
2953 }
2954
2955 hints = XWindowSystemUtilities::Atoms::getIfExists (display, "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE");
2956
2957 if (hints != None)
2958 {
2959 XWindowSystemUtilities::ScopedXLock xLock;
2960 xchangeProperty (windowH, atoms.windowType, XA_ATOM, 32, &hints, 1);
2961 }
2962}
2963
2964static void addAtomIfExists (bool condition, const char* key, ::Display* display, std::vector<Atom>& atoms)
2965{
2966 if (condition)
2967 {
2968 auto atom = XWindowSystemUtilities::Atoms::getIfExists (display, key);
2969
2970 if (atom != None)
2971 atoms.push_back (atom);
2972 }
2973}
2974
2975void XWindowSystem::addWindowButtons (::Window windowH, int styleFlags) const
2976{
2977 jassert (windowH != 0);
2978
2979 XWindowSystemUtilities::ScopedXLock xLock;
2980 auto motifAtom = XWindowSystemUtilities::Atoms::getIfExists (display, "_MOTIF_WM_HINTS");
2981
2982 if (motifAtom != None)
2983 {
2984 MotifWmHints motifHints;
2985 zerostruct (motifHints);
2986
2987 motifHints.flags = 1 | 2; /* MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS */
2988 motifHints.decorations = 2 /* MWM_DECOR_BORDER */ | 8 /* MWM_DECOR_TITLE */ | 16; /* MWM_DECOR_MENU */
2989
2990 motifHints.functions = 4 /* MWM_FUNC_MOVE */;
2991
2992 if ((styleFlags & ComponentPeer::windowHasCloseButton) != 0)
2993 motifHints.functions |= 32; /* MWM_FUNC_CLOSE */
2994
2995 if ((styleFlags & ComponentPeer::windowHasMinimiseButton) != 0)
2996 {
2997 motifHints.functions |= 8; /* MWM_FUNC_MINIMIZE */
2998 motifHints.decorations |= 0x20; /* MWM_DECOR_MINIMIZE */
2999 }
3000
3001 if ((styleFlags & ComponentPeer::windowHasMaximiseButton) != 0)
3002 {
3003 motifHints.functions |= 0x10; /* MWM_FUNC_MAXIMIZE */
3004 motifHints.decorations |= 0x40; /* MWM_DECOR_MAXIMIZE */
3005 }
3006
3007 if ((styleFlags & ComponentPeer::windowIsResizable) != 0)
3008 {
3009 motifHints.functions |= 2; /* MWM_FUNC_RESIZE */
3010 motifHints.decorations |= 0x4; /* MWM_DECOR_RESIZEH */
3011 }
3012
3013 xchangeProperty (windowH, motifAtom, motifAtom, 32, &motifHints, 5);
3014 }
3015
3016 auto actionsAtom = XWindowSystemUtilities::Atoms::getIfExists (display, "_NET_WM_ALLOWED_ACTIONS");
3017
3018 if (actionsAtom != None)
3019 {
3020 std::vector<Atom> netHints;
3021
3022 addAtomIfExists ((styleFlags & ComponentPeer::windowIsResizable) != 0, "_NET_WM_ACTION_RESIZE", display, netHints);
3023 addAtomIfExists ((styleFlags & ComponentPeer::windowHasMaximiseButton) != 0, "_NET_WM_ACTION_FULLSCREEN", display, netHints);
3024 addAtomIfExists ((styleFlags & ComponentPeer::windowHasMinimiseButton) != 0, "_NET_WM_ACTION_MINIMIZE", display, netHints);
3025 addAtomIfExists ((styleFlags & ComponentPeer::windowHasCloseButton) != 0, "_NET_WM_ACTION_CLOSE", display, netHints);
3026
3027 auto numHints = (int) netHints.size();
3028
3029 if (numHints > 0)
3030 xchangeProperty (windowH, actionsAtom, XA_ATOM, 32, netHints.data(), numHints);
3031 }
3032}
3033
3034void XWindowSystem::setWindowType (::Window windowH, int styleFlags) const
3035{
3036 jassert (windowH != 0);
3037
3038 if (atoms.windowType != None)
3039 {
3040 auto hint = (styleFlags & ComponentPeer::windowIsTemporary) != 0
3041 || ((styleFlags & ComponentPeer::windowHasDropShadow) == 0 && Desktop::canUseSemiTransparentWindows())
3042 ? XWindowSystemUtilities::Atoms::getIfExists (display, "_NET_WM_WINDOW_TYPE_COMBO")
3043 : XWindowSystemUtilities::Atoms::getIfExists (display, "_NET_WM_WINDOW_TYPE_NORMAL");
3044
3045 if (hint != None)
3046 xchangeProperty (windowH, atoms.windowType, XA_ATOM, 32, &hint, 1);
3047 }
3048
3049 if (atoms.windowState != None)
3050 {
3051 std::vector<Atom> netStateHints;
3052
3053 addAtomIfExists ((styleFlags & ComponentPeer::windowAppearsOnTaskbar) == 0, "_NET_WM_STATE_SKIP_TASKBAR", display, netStateHints);
3054 addAtomIfExists (getPeerFor (windowH)->getComponent().isAlwaysOnTop(), "_NET_WM_STATE_ABOVE", display, netStateHints);
3055
3056 auto numHints = (int) netStateHints.size();
3057
3058 if (numHints > 0)
3059 xchangeProperty (windowH, atoms.windowState, XA_ATOM, 32, netStateHints.data(), numHints);
3060 }
3061}
3062
3063void XWindowSystem::initialisePointerMap()
3064{
3065 auto numButtons = X11Symbols::getInstance()->xGetPointerMapping (display, nullptr, 0);
3066 pointerMap[2] = pointerMap[3] = pointerMap[4] = Keys::NoButton;
3067
3068 if (numButtons == 2)
3069 {
3070 pointerMap[0] = Keys::LeftButton;
3071 pointerMap[1] = Keys::RightButton;
3072 }
3073 else if (numButtons >= 3)
3074 {
3075 pointerMap[0] = Keys::LeftButton;
3076 pointerMap[1] = Keys::MiddleButton;
3077 pointerMap[2] = Keys::RightButton;
3078
3079 if (numButtons >= 5)
3080 {
3081 pointerMap[3] = Keys::WheelUp;
3082 pointerMap[4] = Keys::WheelDown;
3083 }
3084 }
3085}
3086
3087void XWindowSystem::deleteIconPixmaps (::Window windowH) const
3088{
3089 jassert (windowH != 0);
3090
3091 XWindowSystemUtilities::ScopedXLock xLock;
3092
3093 if (auto wmHints = makeXFreePtr (X11Symbols::getInstance()->xGetWMHints (display, windowH)))
3094 {
3095 if ((wmHints->flags & IconPixmapHint) != 0)
3096 {
3097 wmHints->flags &= ~IconPixmapHint;
3098 X11Symbols::getInstance()->xFreePixmap (display, wmHints->icon_pixmap);
3099 }
3100
3101 if ((wmHints->flags & IconMaskHint) != 0)
3102 {
3103 wmHints->flags &= ~IconMaskHint;
3104 X11Symbols::getInstance()->xFreePixmap (display, wmHints->icon_mask);
3105 }
3106
3107 X11Symbols::getInstance()->xSetWMHints (display, windowH, wmHints.get());
3108 }
3109}
3110
3111// Alt and Num lock are not defined by standard X modifier constants: check what they're mapped to
3112void XWindowSystem::updateModifierMappings() const
3113{
3114 XWindowSystemUtilities::ScopedXLock xLock;
3115 auto altLeftCode = X11Symbols::getInstance()->xKeysymToKeycode (display, XK_Alt_L);
3116 auto numLockCode = X11Symbols::getInstance()->xKeysymToKeycode (display, XK_Num_Lock);
3117
3118 Keys::AltMask = 0;
3119 Keys::NumLockMask = 0;
3120
3121 if (auto mapping = makeDeletedPtr (X11Symbols::getInstance()->xGetModifierMapping (display),
3122 [] (XModifierKeymap* mk) { X11Symbols::getInstance()->xFreeModifiermap (mk); }))
3123 {
3124 for (int modifierIdx = 0; modifierIdx < 8; ++modifierIdx)
3125 {
3126 for (int keyIndex = 0; keyIndex < mapping->max_keypermod; ++keyIndex)
3127 {
3128 auto key = mapping->modifiermap[(modifierIdx * mapping->max_keypermod) + keyIndex];
3129
3130 if (key == altLeftCode)
3131 Keys::AltMask = 1 << modifierIdx;
3132 else if (key == numLockCode)
3133 Keys::NumLockMask = 1 << modifierIdx;
3134 }
3135 }
3136 }
3137}
3138
3139long XWindowSystem::getUserTime (::Window windowH) const
3140{
3141 jassert (windowH != 0);
3142
3143 XWindowSystemUtilities::GetXProperty prop (display, windowH, atoms.userTime, 0, 65536, false, XA_CARDINAL);
3144
3145 if (! prop.success)
3146 return 0;
3147
3148 long result = 0;
3149 std::memcpy (&result, prop.data, sizeof (long));
3150
3151 return result;
3152}
3153
3154void XWindowSystem::initialiseXSettings()
3155{
3156 xSettings = XWindowSystemUtilities::XSettings::createXSettings (display);
3157
3158 if (xSettings != nullptr)
3159 X11Symbols::getInstance()->xSelectInput (display,
3160 xSettings->getSettingsWindow(),
3161 StructureNotifyMask | PropertyChangeMask);
3162}
3163
3164XWindowSystem::DisplayVisuals::DisplayVisuals (::Display* xDisplay)
3165{
3166 auto findVisualWithDepthOrNull = [&] (int desiredDepth) -> Visual*
3167 {
3168 int matchedDepth = 0;
3169 auto* visual = Visuals::findVisualFormat (xDisplay, desiredDepth, matchedDepth);
3170
3171 if (desiredDepth == matchedDepth)
3172 return visual;
3173
3174 return nullptr;
3175 };
3176
3177 visual16Bit = findVisualWithDepthOrNull (16);
3178 visual24Bit = findVisualWithDepthOrNull (24);
3179 visual32Bit = findVisualWithDepthOrNull (32);
3180}
3181
3182XWindowSystem::VisualAndDepth XWindowSystem::DisplayVisuals::getBestVisualForWindow (bool isSemiTransparent) const
3183{
3184 if (isSemiTransparent && visual32Bit != nullptr)
3185 return { visual32Bit, 32 };
3186
3187 if (visual24Bit != nullptr)
3188 return { visual24Bit, 24 };
3189
3190 if (visual32Bit != nullptr)
3191 return { visual32Bit, 32 };
3192
3193 // No visual available!
3194 jassert (visual16Bit != nullptr);
3195
3196 return { visual16Bit, 16 };
3197}
3198
3199bool XWindowSystem::DisplayVisuals::isValid() const noexcept
3200{
3201 return (visual32Bit != nullptr || visual24Bit != nullptr || visual16Bit != nullptr);
3202}
3203
3204//==============================================================================
3205bool XWindowSystem::initialiseXDisplay()
3206{
3207 jassert (display == nullptr);
3208
3209 String displayName (getenv ("DISPLAY"));
3210
3211 if (displayName.isEmpty())
3212 displayName = ":0.0";
3213
3214 // it seems that on some systems XOpenDisplay will occasionally
3215 // fail the first time, but succeed on a second attempt..
3216 for (int retries = 2; --retries >= 0;)
3217 {
3218 display = X11Symbols::getInstance()->xOpenDisplay (displayName.toUTF8());
3219
3220 if (display != nullptr)
3221 break;
3222 }
3223
3224 // No X Server running
3225 if (display == nullptr)
3226 return false;
3227
3228 #if JUCE_DEBUG_XERRORS_SYNCHRONOUSLY
3229 X11Symbols::getInstance()->xSynchronize (display, True);
3230 #endif
3231
3232 // Create a context to store user data associated with Windows we create
3233 windowHandleXContext = (XContext) X11Symbols::getInstance()->xrmUniqueQuark();
3234
3235 // Create our message window (this will never be mapped)
3236 auto screen = X11Symbols::getInstance()->xDefaultScreen (display);
3237 auto root = X11Symbols::getInstance()->xRootWindow (display, screen);
3238 X11Symbols::getInstance()->xSelectInput (display, root, SubstructureNotifyMask);
3239
3240 // We're only interested in client messages for this window, which are always sent
3241 XSetWindowAttributes swa;
3242 swa.event_mask = NoEventMask;
3243 juce_messageWindowHandle = X11Symbols::getInstance()->xCreateWindow (display, root,
3244 0, 0, 1, 1, 0, 0, InputOnly,
3245 X11Symbols::getInstance()->xDefaultVisual (display, screen),
3246 CWEventMask, &swa);
3247
3248 X11Symbols::getInstance()->xSync (display, False);
3249
3250 atoms = XWindowSystemUtilities::Atoms (display);
3251
3252 initialisePointerMap();
3253 updateModifierMappings();
3254 initialiseXSettings();
3255
3256 #if JUCE_USE_XSHM
3257 if (XSHMHelpers::isShmAvailable (display))
3258 shmCompletionEvent = X11Symbols::getInstance()->xShmGetEventBase (display) + ShmCompletion;
3259 #endif
3260
3261 displayVisuals = std::make_unique<DisplayVisuals> (display);
3262
3263 if (! displayVisuals->isValid())
3264 {
3265 Logger::outputDebugString ("ERROR: System doesn't support 32, 24 or 16 bit RGB display.\n");
3266 return false;
3267 }
3268
3269 // Setup input event handler
3270 LinuxEventLoop::registerFdCallback (X11Symbols::getInstance()->xConnectionNumber (display),
3271 [this] (int)
3272 {
3273 do
3274 {
3275 XEvent evt;
3276
3277 {
3278 XWindowSystemUtilities::ScopedXLock xLock;
3279
3280 if (! X11Symbols::getInstance()->xPending (display))
3281 return;
3282
3283 X11Symbols::getInstance()->xNextEvent (display, &evt);
3284 }
3285
3286 if (evt.type == SelectionRequest && evt.xany.window == juce_messageWindowHandle)
3287 {
3288 ClipboardHelpers::handleSelection (evt.xselectionrequest);
3289 }
3290 else if (evt.xany.window != juce_messageWindowHandle)
3291 {
3292 windowMessageReceive (evt);
3293 }
3294
3295 } while (display != nullptr);
3296 });
3297
3298 return true;
3299}
3300
3301void XWindowSystem::destroyXDisplay()
3302{
3303 if (xIsAvailable)
3304 {
3305 jassert (display != nullptr);
3306
3307 {
3308 XWindowSystemUtilities::ScopedXLock xLock;
3309
3310 X11Symbols::getInstance()->xDestroyWindow (display, juce_messageWindowHandle);
3311 juce_messageWindowHandle = 0;
3312 X11Symbols::getInstance()->xSync (display, True);
3313 }
3314
3315 LinuxEventLoop::unregisterFdCallback (X11Symbols::getInstance()->xConnectionNumber (display));
3316
3317 {
3318 XWindowSystemUtilities::ScopedXLock xLock;
3319 X11Symbols::getInstance()->xCloseDisplay (display);
3320 display = nullptr;
3321 displayVisuals = nullptr;
3322 }
3323 }
3324}
3325
3326//==============================================================================
3327::Window juce_createKeyProxyWindow (ComponentPeer* peer);
3328::Window juce_createKeyProxyWindow (ComponentPeer* peer)
3329{
3330 return XWindowSystem::getInstance()->createKeyProxy ((::Window) peer->getNativeHandle());
3331}
3332
3333void juce_deleteKeyProxyWindow (::Window keyProxy);
3334void juce_deleteKeyProxyWindow (::Window keyProxy)
3335{
3336 XWindowSystem::getInstance()->deleteKeyProxy (keyProxy);
3337}
3338
3339//==============================================================================
3340template <typename EventType>
3341static Point<float> getLogicalMousePos (const EventType& e, double scaleFactor) noexcept
3342{
3343 return Point<float> ((float) e.x, (float) e.y) / scaleFactor;
3344}
3345
3346static int64 getEventTime (::Time t)
3347{
3348 static int64 eventTimeOffset = 0x12345678;
3349 auto thisMessageTime = (int64) t;
3350
3351 if (eventTimeOffset == 0x12345678)
3352 eventTimeOffset = Time::currentTimeMillis() - thisMessageTime;
3353
3354 return eventTimeOffset + thisMessageTime;
3355}
3356
3357template <typename EventType>
3358static int64 getEventTime (const EventType& t)
3359{
3360 return getEventTime (t.time);
3361}
3362
3363void XWindowSystem::handleWindowMessage (LinuxComponentPeer* peer, XEvent& event) const
3364{
3365 switch (event.xany.type)
3366 {
3367 case KeyPressEventType: handleKeyPressEvent (peer, event.xkey); break;
3368 case KeyRelease: handleKeyReleaseEvent (peer, event.xkey); break;
3369 case ButtonPress: handleButtonPressEvent (peer, event.xbutton); break;
3370 case ButtonRelease: handleButtonReleaseEvent (peer, event.xbutton); break;
3371 case MotionNotify: handleMotionNotifyEvent (peer, event.xmotion); break;
3372 case EnterNotify: handleEnterNotifyEvent (peer, event.xcrossing); break;
3373 case LeaveNotify: handleLeaveNotifyEvent (peer, event.xcrossing); break;
3374 case FocusIn: handleFocusInEvent (peer); break;
3375 case FocusOut: handleFocusOutEvent (peer); break;
3376 case Expose: handleExposeEvent (peer, event.xexpose); break;
3377 case MappingNotify: handleMappingNotify (event.xmapping); break;
3378 case ClientMessage: handleClientMessageEvent (peer, event.xclient, event); break;
3379 case SelectionNotify: dragAndDropStateMap[peer].handleDragAndDropSelection (event); break;
3380 case ConfigureNotify: handleConfigureNotifyEvent (peer, event.xconfigure); break;
3381 case ReparentNotify:
3382 case GravityNotify: handleGravityNotify (peer); break;
3383 case SelectionClear: dragAndDropStateMap[peer].handleExternalSelectionClear(); break;
3384 case SelectionRequest: dragAndDropStateMap[peer].handleExternalSelectionRequest (event); break;
3385 case PropertyNotify: propertyNotifyEvent (peer, event.xproperty); break;
3386
3387 case CirculateNotify:
3388 case CreateNotify:
3389 case DestroyNotify:
3390 case UnmapNotify:
3391 break;
3392
3393 case MapNotify:
3394 peer->handleBroughtToFront();
3395 break;
3396
3397 default:
3398 #if JUCE_USE_XSHM
3399 if (XSHMHelpers::isShmAvailable (display))
3400 {
3401 XWindowSystemUtilities::ScopedXLock xLock;
3402
3403 if (event.xany.type == shmCompletionEvent)
3404 XWindowSystem::getInstance()->removePendingPaintForWindow ((::Window) peer->getNativeHandle());
3405 }
3406 #endif
3407 break;
3408 }
3409}
3410
3411void XWindowSystem::handleKeyPressEvent (LinuxComponentPeer* peer, XKeyEvent& keyEvent) const
3412{
3413 auto oldMods = ModifierKeys::currentModifiers;
3414 Keys::refreshStaleModifierKeys();
3415
3416 char utf8 [64] = { 0 };
3417 juce_wchar unicodeChar = 0;
3418 int keyCode = 0;
3419 bool keyDownChange = false;
3420 KeySym sym;
3421
3422 {
3423 XWindowSystemUtilities::ScopedXLock xLock;
3424 updateKeyStates ((int) keyEvent.keycode, true);
3425
3426 String oldLocale (::setlocale (LC_ALL, nullptr));
3427 ::setlocale (LC_ALL, "");
3428 X11Symbols::getInstance()->xLookupString (&keyEvent, utf8, sizeof (utf8), &sym, nullptr);
3429
3430 if (oldLocale.isNotEmpty())
3431 ::setlocale (LC_ALL, oldLocale.toRawUTF8());
3432
3433 unicodeChar = *CharPointer_UTF8 (utf8);
3434 keyCode = (int) unicodeChar;
3435
3436 if (keyCode < 0x20)
3437 keyCode = (int) X11Symbols::getInstance()->xkbKeycodeToKeysym (display, (::KeyCode) keyEvent.keycode, 0,
3438 ModifierKeys::currentModifiers.isShiftDown() ? 1 : 0);
3439
3440 keyDownChange = (sym != NoSymbol) && ! updateKeyModifiersFromSym (sym, true);
3441 }
3442
3443 bool keyPressed = false;
3444
3445 if ((sym & 0xff00) == 0xff00 || keyCode == XK_ISO_Left_Tab)
3446 {
3447 switch (sym) // Translate keypad
3448 {
3449 case XK_KP_Add: keyCode = XK_plus; break;
3450 case XK_KP_Subtract: keyCode = XK_hyphen; break;
3451 case XK_KP_Divide: keyCode = XK_slash; break;
3452 case XK_KP_Multiply: keyCode = XK_asterisk; break;
3453 case XK_KP_Enter: keyCode = XK_Return; break;
3454 case XK_KP_Insert: keyCode = XK_Insert; break;
3455 case XK_Delete:
3456 case XK_KP_Delete: keyCode = XK_Delete; break;
3457 case XK_KP_Left: keyCode = XK_Left; break;
3458 case XK_KP_Right: keyCode = XK_Right; break;
3459 case XK_KP_Up: keyCode = XK_Up; break;
3460 case XK_KP_Down: keyCode = XK_Down; break;
3461 case XK_KP_Home: keyCode = XK_Home; break;
3462 case XK_KP_End: keyCode = XK_End; break;
3463 case XK_KP_Page_Down: keyCode = XK_Page_Down; break;
3464 case XK_KP_Page_Up: keyCode = XK_Page_Up; break;
3465
3466 case XK_KP_0: keyCode = XK_0; break;
3467 case XK_KP_1: keyCode = XK_1; break;
3468 case XK_KP_2: keyCode = XK_2; break;
3469 case XK_KP_3: keyCode = XK_3; break;
3470 case XK_KP_4: keyCode = XK_4; break;
3471 case XK_KP_5: keyCode = XK_5; break;
3472 case XK_KP_6: keyCode = XK_6; break;
3473 case XK_KP_7: keyCode = XK_7; break;
3474 case XK_KP_8: keyCode = XK_8; break;
3475 case XK_KP_9: keyCode = XK_9; break;
3476
3477 default: break;
3478 }
3479
3480 switch (keyCode)
3481 {
3482 case XK_Left:
3483 case XK_Right:
3484 case XK_Up:
3485 case XK_Down:
3486 case XK_Page_Up:
3487 case XK_Page_Down:
3488 case XK_End:
3489 case XK_Home:
3490 case XK_Delete:
3491 case XK_Insert:
3492 keyPressed = true;
3493 keyCode = (keyCode & 0xff) | Keys::extendedKeyModifier;
3494 break;
3495
3496 case XK_Tab:
3497 case XK_Return:
3498 case XK_Escape:
3499 case XK_BackSpace:
3500 keyPressed = true;
3501 keyCode &= 0xff;
3502 break;
3503
3504 case XK_ISO_Left_Tab:
3505 keyPressed = true;
3506 keyCode = XK_Tab & 0xff;
3507 break;
3508
3509 default:
3510 if (sym >= XK_F1 && sym <= XK_F35)
3511 {
3512 keyPressed = true;
3513 keyCode = static_cast<int> ((sym & 0xff) | Keys::extendedKeyModifier);
3514 }
3515 break;
3516 }
3517 }
3518
3519 if (utf8[0] != 0 || ((sym & 0xff00) == 0 && sym >= 8))
3520 keyPressed = true;
3521
3522 if (oldMods != ModifierKeys::currentModifiers)
3523 peer->handleModifierKeysChange();
3524
3525 if (keyDownChange)
3526 peer->handleKeyUpOrDown (true);
3527
3528 if (keyPressed)
3529 peer->handleKeyPress (keyCode, unicodeChar);
3530}
3531
3532void XWindowSystem::handleKeyReleaseEvent (LinuxComponentPeer* peer, const XKeyEvent& keyEvent) const
3533{
3534 auto isKeyReleasePartOfAutoRepeat = [&]() -> bool
3535 {
3536 if (X11Symbols::getInstance()->xPending (display))
3537 {
3538 XEvent e;
3539 X11Symbols::getInstance()->xPeekEvent (display, &e);
3540
3541 // Look for a subsequent key-down event with the same timestamp and keycode
3542 return e.type == KeyPressEventType
3543 && e.xkey.keycode == keyEvent.keycode
3544 && e.xkey.time == keyEvent.time;
3545 }
3546
3547 return false;
3548 }();
3549
3550 if (! isKeyReleasePartOfAutoRepeat)
3551 {
3552 updateKeyStates ((int) keyEvent.keycode, false);
3553 KeySym sym;
3554
3555 {
3556 XWindowSystemUtilities::ScopedXLock xLock;
3557 sym = X11Symbols::getInstance()->xkbKeycodeToKeysym (display, (::KeyCode) keyEvent.keycode, 0, 0);
3558 }
3559
3560 auto oldMods = ModifierKeys::currentModifiers;
3561 auto keyDownChange = (sym != NoSymbol) && ! updateKeyModifiersFromSym (sym, false);
3562
3563 if (oldMods != ModifierKeys::currentModifiers)
3564 peer->handleModifierKeysChange();
3565
3566 if (keyDownChange)
3567 peer->handleKeyUpOrDown (false);
3568 }
3569}
3570
3571void XWindowSystem::handleWheelEvent (LinuxComponentPeer* peer, const XButtonPressedEvent& buttonPressEvent, float amount) const
3572{
3573 MouseWheelDetails wheel;
3574 wheel.deltaX = 0.0f;
3575 wheel.deltaY = amount;
3576 wheel.isReversed = false;
3577 wheel.isSmooth = false;
3578 wheel.isInertial = false;
3579
3580 peer->handleMouseWheel (MouseInputSource::InputSourceType::mouse, getLogicalMousePos (buttonPressEvent, peer->getPlatformScaleFactor()),
3581 getEventTime (buttonPressEvent), wheel);
3582}
3583
3584void XWindowSystem::handleButtonPressEvent (LinuxComponentPeer* peer, const XButtonPressedEvent& buttonPressEvent, int buttonModifierFlag) const
3585{
3586 ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withFlags (buttonModifierFlag);
3587 peer->toFront (true);
3588 peer->handleMouseEvent (MouseInputSource::InputSourceType::mouse, getLogicalMousePos (buttonPressEvent, peer->getPlatformScaleFactor()),
3589 ModifierKeys::currentModifiers, MouseInputSource::defaultPressure,
3590 MouseInputSource::defaultOrientation, getEventTime (buttonPressEvent), {});
3591}
3592
3593void XWindowSystem::handleButtonPressEvent (LinuxComponentPeer* peer, const XButtonPressedEvent& buttonPressEvent) const
3594{
3595 updateKeyModifiers ((int) buttonPressEvent.state);
3596
3597 auto mapIndex = (uint32) (buttonPressEvent.button - Button1);
3598
3599 if (mapIndex < (uint32) numElementsInArray (pointerMap))
3600 {
3601 switch (pointerMap[mapIndex])
3602 {
3603 case Keys::WheelUp: handleWheelEvent (peer, buttonPressEvent, 50.0f / 256.0f); break;
3604 case Keys::WheelDown: handleWheelEvent (peer, buttonPressEvent, -50.0f / 256.0f); break;
3605 case Keys::LeftButton: handleButtonPressEvent (peer, buttonPressEvent, ModifierKeys::leftButtonModifier); break;
3606 case Keys::RightButton: handleButtonPressEvent (peer, buttonPressEvent, ModifierKeys::rightButtonModifier); break;
3607 case Keys::MiddleButton: handleButtonPressEvent (peer, buttonPressEvent, ModifierKeys::middleButtonModifier); break;
3608 default: break;
3609 }
3610 }
3611}
3612
3613void XWindowSystem::handleButtonReleaseEvent (LinuxComponentPeer* peer, const XButtonReleasedEvent& buttonRelEvent) const
3614{
3615 updateKeyModifiers ((int) buttonRelEvent.state);
3616
3617 if (peer->getParentWindow() != 0)
3618 peer->updateWindowBounds();
3619
3620 auto mapIndex = (uint32) (buttonRelEvent.button - Button1);
3621
3622 if (mapIndex < (uint32) numElementsInArray (pointerMap))
3623 {
3624 switch (pointerMap[mapIndex])
3625 {
3626 case Keys::LeftButton: ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutFlags (ModifierKeys::leftButtonModifier); break;
3627 case Keys::RightButton: ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutFlags (ModifierKeys::rightButtonModifier); break;
3628 case Keys::MiddleButton: ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutFlags (ModifierKeys::middleButtonModifier); break;
3629 default: break;
3630 }
3631 }
3632
3633 auto& dragState = dragAndDropStateMap[peer];
3634
3635 if (dragState.isDragging())
3636 dragState.handleExternalDragButtonReleaseEvent();
3637
3638 peer->handleMouseEvent (MouseInputSource::InputSourceType::mouse, getLogicalMousePos (buttonRelEvent, peer->getPlatformScaleFactor()),
3639 ModifierKeys::currentModifiers, MouseInputSource::defaultPressure, MouseInputSource::defaultOrientation, getEventTime (buttonRelEvent));
3640}
3641
3642void XWindowSystem::handleMotionNotifyEvent (LinuxComponentPeer* peer, const XPointerMovedEvent& movedEvent) const
3643{
3644 updateKeyModifiers ((int) movedEvent.state);
3645 Keys::refreshStaleMouseKeys();
3646
3647 auto& dragState = dragAndDropStateMap[peer];
3648
3649 if (dragState.isDragging())
3650 dragState.handleExternalDragMotionNotify();
3651
3652 peer->handleMouseEvent (MouseInputSource::InputSourceType::mouse, getLogicalMousePos (movedEvent, peer->getPlatformScaleFactor()),
3653 ModifierKeys::currentModifiers, MouseInputSource::defaultPressure,
3654 MouseInputSource::defaultOrientation, getEventTime (movedEvent));
3655}
3656
3657void XWindowSystem::handleEnterNotifyEvent (LinuxComponentPeer* peer, const XEnterWindowEvent& enterEvent) const
3658{
3659 if (peer->getParentWindow() != 0)
3660 peer->updateWindowBounds();
3661
3662 if (! ModifierKeys::currentModifiers.isAnyMouseButtonDown())
3663 {
3664 updateKeyModifiers ((int) enterEvent.state);
3665 peer->handleMouseEvent (MouseInputSource::InputSourceType::mouse, getLogicalMousePos (enterEvent, peer->getPlatformScaleFactor()),
3666 ModifierKeys::currentModifiers, MouseInputSource::defaultPressure,
3667 MouseInputSource::defaultOrientation, getEventTime (enterEvent));
3668 }
3669}
3670
3671void XWindowSystem::handleLeaveNotifyEvent (LinuxComponentPeer* peer, const XLeaveWindowEvent& leaveEvent) const
3672{
3673 // Suppress the normal leave if we've got a pointer grab, or if
3674 // it's a bogus one caused by clicking a mouse button when running
3675 // in a Window manager
3676 if (((! ModifierKeys::currentModifiers.isAnyMouseButtonDown()) && leaveEvent.mode == NotifyNormal)
3677 || leaveEvent.mode == NotifyUngrab)
3678 {
3679 updateKeyModifiers ((int) leaveEvent.state);
3680 peer->handleMouseEvent (MouseInputSource::InputSourceType::mouse, getLogicalMousePos (leaveEvent, peer->getPlatformScaleFactor()),
3681 ModifierKeys::currentModifiers, MouseInputSource::defaultPressure,
3682 MouseInputSource::defaultOrientation, getEventTime (leaveEvent));
3683 }
3684}
3685
3686void XWindowSystem::handleFocusInEvent (LinuxComponentPeer* peer) const
3687{
3688 peer->isActiveApplication = true;
3689
3690 if (isFocused ((::Window) peer->getNativeHandle()) && ! peer->focused)
3691 {
3692 peer->focused = true;
3693 peer->handleFocusGain();
3694 }
3695}
3696
3697void XWindowSystem::handleFocusOutEvent (LinuxComponentPeer* peer) const
3698{
3699 if (! isFocused ((::Window) peer->getNativeHandle()) && peer->focused)
3700 {
3701 peer->focused = false;
3702 peer->isActiveApplication = false;
3703
3704 peer->handleFocusLoss();
3705 }
3706}
3707
3708void XWindowSystem::handleExposeEvent (LinuxComponentPeer* peer, XExposeEvent& exposeEvent) const
3709{
3710 // Batch together all pending expose events
3711 XEvent nextEvent;
3712 XWindowSystemUtilities::ScopedXLock xLock;
3713
3714 // if we have opengl contexts then just repaint them all
3715 // regardless if this is really necessary
3716 peer->repaintOpenGLContexts();
3717
3718 auto windowH = (::Window) peer->getNativeHandle();
3719
3720 if (exposeEvent.window != windowH)
3721 {
3722 Window child;
3723 X11Symbols::getInstance()->xTranslateCoordinates (display, exposeEvent.window, windowH,
3724 exposeEvent.x, exposeEvent.y, &exposeEvent.x, &exposeEvent.y,
3725 &child);
3726 }
3727
3728 // exposeEvent is in local window local coordinates so do not convert with
3729 // physicalToScaled, but rather use currentScaleFactor
3730 auto currentScaleFactor = peer->getPlatformScaleFactor();
3731
3732 peer->repaint (Rectangle<int> (exposeEvent.x, exposeEvent.y,
3733 exposeEvent.width, exposeEvent.height) / currentScaleFactor);
3734
3735 while (X11Symbols::getInstance()->xEventsQueued (display, QueuedAfterFlush) > 0)
3736 {
3737 X11Symbols::getInstance()->xPeekEvent (display, &nextEvent);
3738
3739 if (nextEvent.type != Expose || nextEvent.xany.window != exposeEvent.window)
3740 break;
3741
3742 X11Symbols::getInstance()->xNextEvent (display, &nextEvent);
3743 auto& nextExposeEvent = (XExposeEvent&) nextEvent.xexpose;
3744
3745 peer->repaint (Rectangle<int> (nextExposeEvent.x, nextExposeEvent.y,
3746 nextExposeEvent.width, nextExposeEvent.height) / currentScaleFactor);
3747 }
3748}
3749
3750void XWindowSystem::dismissBlockingModals (LinuxComponentPeer* peer) const
3751{
3752 if (peer->getComponent().isCurrentlyBlockedByAnotherModalComponent())
3753 if (auto* currentModalComp = Component::getCurrentlyModalComponent())
3754 if (auto* otherPeer = currentModalComp->getPeer())
3755 if ((otherPeer->getStyleFlags() & ComponentPeer::windowIsTemporary) != 0)
3756 currentModalComp->inputAttemptWhenModal();
3757}
3758
3759void XWindowSystem::handleConfigureNotifyEvent (LinuxComponentPeer* peer, XConfigureEvent& confEvent) const
3760{
3761 peer->updateWindowBounds();
3762 peer->updateBorderSize();
3763 peer->handleMovedOrResized();
3764
3765 // if the native title bar is dragged, need to tell any active menus, etc.
3766 if ((peer->getStyleFlags() & ComponentPeer::windowHasTitleBar) != 0)
3767 dismissBlockingModals (peer);
3768
3769 auto windowH = (::Window) peer->getNativeHandle();
3770
3771 if (confEvent.window == windowH && confEvent.above != 0 && isFrontWindow (windowH))
3772 peer->handleBroughtToFront();
3773}
3774
3775void XWindowSystem::handleGravityNotify (LinuxComponentPeer* peer) const
3776{
3777 peer->updateWindowBounds();
3778 peer->updateBorderSize();
3779 peer->handleMovedOrResized();
3780}
3781
3782bool XWindowSystem::isIconic (Window w) const
3783{
3784 jassert (w != 0);
3785
3786 XWindowSystemUtilities::ScopedXLock xLock;
3787 XWindowSystemUtilities::GetXProperty prop (display, w, atoms.state, 0, 64, false, atoms.state);
3788
3789 if (prop.success && prop.actualType == atoms.state
3790 && prop.actualFormat == 32 && prop.numItems > 0)
3791 {
3792 unsigned long state;
3793 memcpy (&state, prop.data, sizeof (unsigned long));
3794
3795 return state == IconicState;
3796 }
3797
3798 return false;
3799}
3800
3801bool XWindowSystem::isHidden (Window w) const
3802{
3803 XWindowSystemUtilities::ScopedXLock xLock;
3804 XWindowSystemUtilities::GetXProperty prop (display, w, atoms.windowState, 0, 128, false, XA_ATOM);
3805
3806 if (! (prop.success && prop.actualFormat == 32 && prop.actualType == XA_ATOM))
3807 return false;
3808
3809 const auto* data = unalignedPointerCast<const long*> (prop.data);
3810 const auto end = data + prop.numItems;
3811
3812 return std::find (data, end, atoms.windowStateHidden) != end;
3813}
3814
3815void XWindowSystem::propertyNotifyEvent (LinuxComponentPeer* peer, const XPropertyEvent& event) const
3816{
3817 if ((event.atom == atoms.state && isIconic (event.window))
3818 || (event.atom == atoms.windowState && isHidden (event.window)))
3819 {
3820 dismissBlockingModals (peer);
3821 }
3822
3823 if (event.atom == XWindowSystemUtilities::Atoms::getIfExists (display, "_NET_FRAME_EXTENTS"))
3824 peer->updateBorderSize();
3825}
3826
3827void XWindowSystem::handleMappingNotify (XMappingEvent& mappingEvent) const
3828{
3829 if (mappingEvent.request != MappingPointer)
3830 {
3831 // Deal with modifier/keyboard mapping
3832 XWindowSystemUtilities::ScopedXLock xLock;
3833 X11Symbols::getInstance()->xRefreshKeyboardMapping (&mappingEvent);
3834 updateModifierMappings();
3835 }
3836}
3837
3838void XWindowSystem::handleClientMessageEvent (LinuxComponentPeer* peer, XClientMessageEvent& clientMsg, XEvent& event) const
3839{
3840 if (clientMsg.message_type == atoms.protocols && clientMsg.format == 32)
3841 {
3842 auto atom = (Atom) clientMsg.data.l[0];
3843
3844 if (atom == atoms.protocolList [XWindowSystemUtilities::Atoms::PING])
3845 {
3846 auto root = X11Symbols::getInstance()->xRootWindow (display, X11Symbols::getInstance()->xDefaultScreen (display));
3847
3848 clientMsg.window = root;
3849
3850 X11Symbols::getInstance()->xSendEvent (display, root, False, NoEventMask, &event);
3851 X11Symbols::getInstance()->xFlush (display);
3852 }
3853 else if (atom == atoms.protocolList [XWindowSystemUtilities::Atoms::TAKE_FOCUS])
3854 {
3855 if ((peer->getStyleFlags() & ComponentPeer::windowIgnoresKeyPresses) == 0)
3856 {
3857 XWindowAttributes atts;
3858
3859 XWindowSystemUtilities::ScopedXLock xLock;
3860 if (clientMsg.window != 0
3861 && X11Symbols::getInstance()->xGetWindowAttributes (display, clientMsg.window, &atts))
3862 {
3863 if (atts.map_state == IsViewable)
3864 {
3865 auto windowH = (::Window) peer->getNativeHandle();
3866
3867 X11Symbols::getInstance()->xSetInputFocus (display, (clientMsg.window == windowH ? getFocusWindow (windowH)
3868 : clientMsg.window),
3869 RevertToParent, (::Time) clientMsg.data.l[1]);
3870 }
3871 }
3872 }
3873 }
3874 else if (atom == atoms.protocolList [XWindowSystemUtilities::Atoms::DELETE_WINDOW])
3875 {
3876 peer->handleUserClosingWindow();
3877 }
3878 }
3879 else if (clientMsg.message_type == atoms.XdndEnter)
3880 {
3881 dragAndDropStateMap[peer].handleDragAndDropEnter (clientMsg, peer);
3882 }
3883 else if (clientMsg.message_type == atoms.XdndLeave)
3884 {
3885 dragAndDropStateMap[peer].handleDragAndDropExit();
3886 }
3887 else if (clientMsg.message_type == atoms.XdndPosition)
3888 {
3889 dragAndDropStateMap[peer].handleDragAndDropPosition (clientMsg, peer);
3890 }
3891 else if (clientMsg.message_type == atoms.XdndDrop)
3892 {
3893 dragAndDropStateMap[peer].handleDragAndDropDrop (clientMsg, peer);
3894 }
3895 else if (clientMsg.message_type == atoms.XdndStatus)
3896 {
3897 dragAndDropStateMap[peer].handleExternalDragAndDropStatus (clientMsg);
3898 }
3899 else if (clientMsg.message_type == atoms.XdndFinished)
3900 {
3901 dragAndDropStateMap[peer].externalResetDragAndDrop();
3902 }
3903 else if (clientMsg.message_type == atoms.XembedMsgType && clientMsg.format == 32)
3904 {
3905 handleXEmbedMessage (peer, clientMsg);
3906 }
3907}
3908
3909void XWindowSystem::handleXEmbedMessage (LinuxComponentPeer* peer, XClientMessageEvent& clientMsg) const
3910{
3911 switch (clientMsg.data.l[1])
3912 {
3913 case 0: // XEMBED_EMBEDDED_NOTIFY
3914 peer->setParentWindow ((::Window) clientMsg.data.l[3]);
3915 peer->updateWindowBounds();
3916 peer->getComponent().setBounds (peer->getBounds());
3917 break;
3918 case 4: // XEMBED_FOCUS_IN
3919 handleFocusInEvent (peer);
3920 break;
3921 case 5: // XEMBED_FOCUS_OUT
3922 handleFocusOutEvent (peer);
3923 break;
3924
3925 default:
3926 break;
3927 }
3928}
3929
3930//==============================================================================
3931void XWindowSystem::dismissBlockingModals (LinuxComponentPeer* peer, const XConfigureEvent& configure) const
3932{
3933 if (peer == nullptr)
3934 return;
3935
3936 const auto peerHandle = peer->getWindowHandle();
3937
3938 if (configure.window != peerHandle && isParentWindowOf (configure.window, peerHandle))
3939 dismissBlockingModals (peer);
3940}
3941
3942void XWindowSystem::windowMessageReceive (XEvent& event)
3943{
3944 if (event.xany.window != None)
3945 {
3946 #if JUCE_X11_SUPPORTS_XEMBED
3947 if (! juce_handleXEmbedEvent (nullptr, &event))
3948 #endif
3949 {
3950 auto* instance = XWindowSystem::getInstance();
3951
3952 if (auto* xSettings = instance->getXSettings())
3953 {
3954 if (event.xany.window == xSettings->getSettingsWindow())
3955 {
3956 if (event.xany.type == PropertyNotify)
3957 xSettings->update();
3958 else if (event.xany.type == DestroyNotify)
3959 instance->initialiseXSettings();
3960
3961 return;
3962 }
3963 }
3964
3965 if (auto* peer = dynamic_cast<LinuxComponentPeer*> (getPeerFor (event.xany.window)))
3966 {
3967 XWindowSystem::getInstance()->handleWindowMessage (peer, event);
3968 return;
3969 }
3970
3971 if (event.type != ConfigureNotify)
3972 return;
3973
3974 for (auto i = ComponentPeer::getNumPeers(); --i >= 0;)
3975 instance->dismissBlockingModals (dynamic_cast<LinuxComponentPeer*> (ComponentPeer::getPeer (i)),
3976 event.xconfigure);
3977 }
3978 }
3979 else if (event.xany.type == KeymapNotify)
3980 {
3981 auto& keymapEvent = (const XKeymapEvent&) event.xkeymap;
3982 memcpy (Keys::keyStates, keymapEvent.key_vector, 32);
3983 }
3984}
3985
3986//==============================================================================
3987JUCE_IMPLEMENT_SINGLETON (XWindowSystem)
3988
3989Image createSnapshotOfNativeWindow (void* window)
3990{
3991 ::Window root;
3992 int wx, wy;
3993 unsigned int ww, wh, bw, bitDepth;
3994
3995 XWindowSystemUtilities::ScopedXLock xLock;
3996
3997 const auto display = XWindowSystem::getInstance()->getDisplay();
3998
3999 if (! X11Symbols::getInstance()->xGetGeometry (display, (::Drawable) window, &root, &wx, &wy, &ww, &wh, &bw, &bitDepth))
4000 return {};
4001
4002 const auto scale = []
4003 {
4004 if (auto* d = Desktop::getInstance().getDisplays().getPrimaryDisplay())
4005 return d->scale;
4006
4007 return 1.0;
4008 }();
4009
4010 auto image = Image { new XBitmapImage { X11Symbols::getInstance()->xGetImage (display,
4011 (::Drawable) window,
4012 0,
4013 0,
4014 ww,
4015 wh,
4016 AllPlanes,
4017 ZPixmap) } };
4018 return image.rescaled ((int) ((double) ww / scale), (int) ((double) wh / scale));
4019}
4020
4021} // namespace juce
This is a base class for holding image data in implementation-specific ways.
Definition juce_Image.h:446
Retrieves a section of an image as raw pixel data, so it can be read or written to.
Definition juce_Image.h:310
int pixelStride
The number of bytes between each pixel.
Definition juce_Image.h:355
int lineStride
The number of bytes between each line.
Definition juce_Image.h:354
PixelFormat pixelFormat
The format of the data.
Definition juce_Image.h:353
uint8 * data
The raw pixel data, packed according to the image's pixel format.
Definition juce_Image.h:351
size_t size
The number of valid/allocated bytes after data.
Definition juce_Image.h:352
Holds a fixed-size bitmap.
Definition juce_Image.h:58
A smart-pointer class which points to a reference-counted object.
std::unique_ptr< LowLevelGraphicsContext > createLowLevelContext() override
Creates a context that will draw into this image.
ImagePixelData::Ptr clone() override
Creates a copy of this image.
std::unique_ptr< ImageType > createType() const override
Creates an instance of the type of this image.
void initialiseBitmapData(Image::BitmapData &bitmap, int x, int y, Image::BitmapData::ReadWriteMode mode) override
Initialises a BitmapData object.
T count(T... args)
T data(T... args)
dlsym
T end(T... args)
T find_if(T... args)
getpid
#define jassert(expression)
Platform-independent assertion macro.
#define DBG(textToWrite)
Writes a string to the standard error stream.
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
This is a shorthand way of writing both a JUCE_DECLARE_NON_COPYABLE and JUCE_LEAK_DETECTOR macro for ...
#define jassertfalse
This will always cause an assertion failure.
#define JUCE_IMPLEMENT_SINGLETON(Classname)
This is a counterpart to the JUCE_DECLARE_SINGLETON macros.
typedef int
typedef double
T max(T... args)
memcpy
T move(T... args)
JUCE Namespace.
void zerostruct(Type &structure) noexcept
Overwrites a structure or object with zeros.
Definition juce_Memory.h:32
wchar_t juce_wchar
A platform-independent 32-bit unicode character type.
constexpr Type jmax(Type a, Type b)
Returns the larger of two values.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
Definition juce_Memory.h:88
@ press
Represents a "press" action.
unsigned char uint8
A platform-independent 8-bit unsigned integer type.
int roundToInt(const FloatType value) noexcept
Fast floating-point-to-integer conversion.
constexpr int numElementsInArray(Type(&)[N]) noexcept
Handy function for getting the number of elements in a simple const C array.
std::unique_ptr< T > rawToUniquePtr(T *ptr)
Converts an owning raw pointer into a unique_ptr, deriving the type of the unique_ptr automatically.
T push_back(T... args)
round
shmat
shmctl
shmdt
shmget
T size(T... args)
A handy struct that uses XLockDisplay and XUnlockDisplay to lock the X server using RAII.
typedef size_t