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.

3413 lines
132KB

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