The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4022 lines
155KB

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