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.

4173 lines
148KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 6 technical preview.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. You may use this code under the terms of the GPL v3
  6. (see www.gnu.org/licenses).
  7. For this technical preview, this file is not subject to commercial licensing.
  8. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  9. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  10. DISCLAIMED.
  11. ==============================================================================
  12. */
  13. namespace juce
  14. {
  15. #if JUCE_DEBUG && ! defined (JUCE_DEBUG_XERRORS)
  16. #define JUCE_DEBUG_XERRORS 1
  17. #endif
  18. #if JUCE_MODULE_AVAILABLE_juce_gui_extra
  19. #define JUCE_X11_SUPPORTS_XEMBED 1
  20. #else
  21. #define JUCE_X11_SUPPORTS_XEMBED 0
  22. #endif
  23. #if JUCE_X11_SUPPORTS_XEMBED
  24. bool juce_handleXEmbedEvent (ComponentPeer*, void*);
  25. unsigned long juce_getCurrentFocusWindow (ComponentPeer*);
  26. #endif
  27. extern WindowMessageReceiveCallback dispatchWindowMessage;
  28. extern XContext windowHandleXContext;
  29. //=============================== X11 - Keys ===================================
  30. namespace Keys
  31. {
  32. enum MouseButtons
  33. {
  34. NoButton = 0,
  35. LeftButton = 1,
  36. MiddleButton = 2,
  37. RightButton = 3,
  38. WheelUp = 4,
  39. WheelDown = 5
  40. };
  41. static int AltMask = 0;
  42. static int NumLockMask = 0;
  43. static bool numLock = false;
  44. static bool capsLock = false;
  45. static char keyStates [32];
  46. static const int extendedKeyModifier = 0x10000000;
  47. }
  48. bool KeyPress::isKeyCurrentlyDown (int keyCode)
  49. {
  50. ScopedXDisplay xDisplay;
  51. if (auto display = xDisplay.display)
  52. {
  53. int keysym;
  54. if (keyCode & Keys::extendedKeyModifier)
  55. {
  56. keysym = 0xff00 | (keyCode & 0xff);
  57. }
  58. else
  59. {
  60. keysym = keyCode;
  61. if (keysym == (XK_Tab & 0xff)
  62. || keysym == (XK_Return & 0xff)
  63. || keysym == (XK_Escape & 0xff)
  64. || keysym == (XK_BackSpace & 0xff))
  65. {
  66. keysym |= 0xff00;
  67. }
  68. }
  69. ScopedXLock xlock (display);
  70. const int keycode = XKeysymToKeycode (display, (KeySym) keysym);
  71. const int keybyte = keycode >> 3;
  72. const int keybit = (1 << (keycode & 7));
  73. return (Keys::keyStates [keybyte] & keybit) != 0;
  74. }
  75. return false;
  76. }
  77. //==============================================================================
  78. const int KeyPress::spaceKey = XK_space & 0xff;
  79. const int KeyPress::returnKey = XK_Return & 0xff;
  80. const int KeyPress::escapeKey = XK_Escape & 0xff;
  81. const int KeyPress::backspaceKey = XK_BackSpace & 0xff;
  82. const int KeyPress::leftKey = (XK_Left & 0xff) | Keys::extendedKeyModifier;
  83. const int KeyPress::rightKey = (XK_Right & 0xff) | Keys::extendedKeyModifier;
  84. const int KeyPress::upKey = (XK_Up & 0xff) | Keys::extendedKeyModifier;
  85. const int KeyPress::downKey = (XK_Down & 0xff) | Keys::extendedKeyModifier;
  86. const int KeyPress::pageUpKey = (XK_Page_Up & 0xff) | Keys::extendedKeyModifier;
  87. const int KeyPress::pageDownKey = (XK_Page_Down & 0xff) | Keys::extendedKeyModifier;
  88. const int KeyPress::endKey = (XK_End & 0xff) | Keys::extendedKeyModifier;
  89. const int KeyPress::homeKey = (XK_Home & 0xff) | Keys::extendedKeyModifier;
  90. const int KeyPress::insertKey = (XK_Insert & 0xff) | Keys::extendedKeyModifier;
  91. const int KeyPress::deleteKey = (XK_Delete & 0xff) | Keys::extendedKeyModifier;
  92. const int KeyPress::tabKey = XK_Tab & 0xff;
  93. const int KeyPress::F1Key = (XK_F1 & 0xff) | Keys::extendedKeyModifier;
  94. const int KeyPress::F2Key = (XK_F2 & 0xff) | Keys::extendedKeyModifier;
  95. const int KeyPress::F3Key = (XK_F3 & 0xff) | Keys::extendedKeyModifier;
  96. const int KeyPress::F4Key = (XK_F4 & 0xff) | Keys::extendedKeyModifier;
  97. const int KeyPress::F5Key = (XK_F5 & 0xff) | Keys::extendedKeyModifier;
  98. const int KeyPress::F6Key = (XK_F6 & 0xff) | Keys::extendedKeyModifier;
  99. const int KeyPress::F7Key = (XK_F7 & 0xff) | Keys::extendedKeyModifier;
  100. const int KeyPress::F8Key = (XK_F8 & 0xff) | Keys::extendedKeyModifier;
  101. const int KeyPress::F9Key = (XK_F9 & 0xff) | Keys::extendedKeyModifier;
  102. const int KeyPress::F10Key = (XK_F10 & 0xff) | Keys::extendedKeyModifier;
  103. const int KeyPress::F11Key = (XK_F11 & 0xff) | Keys::extendedKeyModifier;
  104. const int KeyPress::F12Key = (XK_F12 & 0xff) | Keys::extendedKeyModifier;
  105. const int KeyPress::F13Key = (XK_F13 & 0xff) | Keys::extendedKeyModifier;
  106. const int KeyPress::F14Key = (XK_F14 & 0xff) | Keys::extendedKeyModifier;
  107. const int KeyPress::F15Key = (XK_F15 & 0xff) | Keys::extendedKeyModifier;
  108. const int KeyPress::F16Key = (XK_F16 & 0xff) | Keys::extendedKeyModifier;
  109. const int KeyPress::F17Key = (XK_F17 & 0xff) | Keys::extendedKeyModifier;
  110. const int KeyPress::F18Key = (XK_F18 & 0xff) | Keys::extendedKeyModifier;
  111. const int KeyPress::F19Key = (XK_F19 & 0xff) | Keys::extendedKeyModifier;
  112. const int KeyPress::F20Key = (XK_F20 & 0xff) | Keys::extendedKeyModifier;
  113. const int KeyPress::F21Key = (XK_F21 & 0xff) | Keys::extendedKeyModifier;
  114. const int KeyPress::F22Key = (XK_F22 & 0xff) | Keys::extendedKeyModifier;
  115. const int KeyPress::F23Key = (XK_F23 & 0xff) | Keys::extendedKeyModifier;
  116. const int KeyPress::F24Key = (XK_F24 & 0xff) | Keys::extendedKeyModifier;
  117. const int KeyPress::F25Key = (XK_F25 & 0xff) | Keys::extendedKeyModifier;
  118. const int KeyPress::F26Key = (XK_F26 & 0xff) | Keys::extendedKeyModifier;
  119. const int KeyPress::F27Key = (XK_F27 & 0xff) | Keys::extendedKeyModifier;
  120. const int KeyPress::F28Key = (XK_F28 & 0xff) | Keys::extendedKeyModifier;
  121. const int KeyPress::F29Key = (XK_F29 & 0xff) | Keys::extendedKeyModifier;
  122. const int KeyPress::F30Key = (XK_F30 & 0xff) | Keys::extendedKeyModifier;
  123. const int KeyPress::F31Key = (XK_F31 & 0xff) | Keys::extendedKeyModifier;
  124. const int KeyPress::F32Key = (XK_F32 & 0xff) | Keys::extendedKeyModifier;
  125. const int KeyPress::F33Key = (XK_F33 & 0xff) | Keys::extendedKeyModifier;
  126. const int KeyPress::F34Key = (XK_F34 & 0xff) | Keys::extendedKeyModifier;
  127. const int KeyPress::F35Key = (XK_F35 & 0xff) | Keys::extendedKeyModifier;
  128. const int KeyPress::numberPad0 = (XK_KP_0 & 0xff) | Keys::extendedKeyModifier;
  129. const int KeyPress::numberPad1 = (XK_KP_1 & 0xff) | Keys::extendedKeyModifier;
  130. const int KeyPress::numberPad2 = (XK_KP_2 & 0xff) | Keys::extendedKeyModifier;
  131. const int KeyPress::numberPad3 = (XK_KP_3 & 0xff) | Keys::extendedKeyModifier;
  132. const int KeyPress::numberPad4 = (XK_KP_4 & 0xff) | Keys::extendedKeyModifier;
  133. const int KeyPress::numberPad5 = (XK_KP_5 & 0xff) | Keys::extendedKeyModifier;
  134. const int KeyPress::numberPad6 = (XK_KP_6 & 0xff) | Keys::extendedKeyModifier;
  135. const int KeyPress::numberPad7 = (XK_KP_7 & 0xff)| Keys::extendedKeyModifier;
  136. const int KeyPress::numberPad8 = (XK_KP_8 & 0xff)| Keys::extendedKeyModifier;
  137. const int KeyPress::numberPad9 = (XK_KP_9 & 0xff)| Keys::extendedKeyModifier;
  138. const int KeyPress::numberPadAdd = (XK_KP_Add & 0xff)| Keys::extendedKeyModifier;
  139. const int KeyPress::numberPadSubtract = (XK_KP_Subtract & 0xff)| Keys::extendedKeyModifier;
  140. const int KeyPress::numberPadMultiply = (XK_KP_Multiply & 0xff)| Keys::extendedKeyModifier;
  141. const int KeyPress::numberPadDivide = (XK_KP_Divide & 0xff)| Keys::extendedKeyModifier;
  142. const int KeyPress::numberPadSeparator = (XK_KP_Separator & 0xff)| Keys::extendedKeyModifier;
  143. const int KeyPress::numberPadDecimalPoint = (XK_KP_Decimal & 0xff)| Keys::extendedKeyModifier;
  144. const int KeyPress::numberPadEquals = (XK_KP_Equal & 0xff)| Keys::extendedKeyModifier;
  145. const int KeyPress::numberPadDelete = (XK_KP_Delete & 0xff)| Keys::extendedKeyModifier;
  146. const int KeyPress::playKey = ((int) 0xffeeff00) | Keys::extendedKeyModifier;
  147. const int KeyPress::stopKey = ((int) 0xffeeff01) | Keys::extendedKeyModifier;
  148. const int KeyPress::fastForwardKey = ((int) 0xffeeff02) | Keys::extendedKeyModifier;
  149. const int KeyPress::rewindKey = ((int) 0xffeeff03) | Keys::extendedKeyModifier;
  150. //================================== X11 - Shm =================================
  151. #if JUCE_USE_XSHM
  152. namespace XSHMHelpers
  153. {
  154. static int trappedErrorCode = 0;
  155. extern "C" int errorTrapHandler (Display*, XErrorEvent* err)
  156. {
  157. trappedErrorCode = err->error_code;
  158. return 0;
  159. }
  160. static bool isShmAvailable (::Display* display) noexcept
  161. {
  162. static bool isChecked = false;
  163. static bool isAvailable = false;
  164. if (! isChecked)
  165. {
  166. isChecked = true;
  167. if (display != nullptr)
  168. {
  169. int major, minor;
  170. Bool pixmaps;
  171. ScopedXLock xlock (display);
  172. if (XShmQueryVersion (display, &major, &minor, &pixmaps))
  173. {
  174. trappedErrorCode = 0;
  175. XErrorHandler oldHandler = XSetErrorHandler (errorTrapHandler);
  176. XShmSegmentInfo segmentInfo;
  177. zerostruct (segmentInfo);
  178. if (auto* xImage = XShmCreateImage (display, DefaultVisual (display, DefaultScreen (display)),
  179. 24, ZPixmap, nullptr, &segmentInfo, 50, 50))
  180. {
  181. if ((segmentInfo.shmid = shmget (IPC_PRIVATE,
  182. (size_t) (xImage->bytes_per_line * xImage->height),
  183. IPC_CREAT | 0777)) >= 0)
  184. {
  185. segmentInfo.shmaddr = (char*) shmat (segmentInfo.shmid, nullptr, 0);
  186. if (segmentInfo.shmaddr != (void*) -1)
  187. {
  188. segmentInfo.readOnly = False;
  189. xImage->data = segmentInfo.shmaddr;
  190. XSync (display, False);
  191. if (XShmAttach (display, &segmentInfo) != 0)
  192. {
  193. XSync (display, False);
  194. XShmDetach (display, &segmentInfo);
  195. isAvailable = true;
  196. }
  197. }
  198. XFlush (display);
  199. XDestroyImage (xImage);
  200. shmdt (segmentInfo.shmaddr);
  201. }
  202. shmctl (segmentInfo.shmid, IPC_RMID, nullptr);
  203. XSetErrorHandler (oldHandler);
  204. if (trappedErrorCode != 0)
  205. isAvailable = false;
  206. }
  207. }
  208. }
  209. }
  210. return isAvailable;
  211. }
  212. }
  213. #endif
  214. //=============================== X11 - Render =================================
  215. #if JUCE_USE_XRENDER
  216. namespace XRender
  217. {
  218. typedef Status (*tXRenderQueryVersion) (Display*, int*, int*);
  219. typedef XRenderPictFormat* (*tXRenderFindStandardFormat) (Display*, int);
  220. typedef XRenderPictFormat* (*tXRenderFindFormat) (Display*, unsigned long, XRenderPictFormat*, int);
  221. typedef XRenderPictFormat* (*tXRenderFindVisualFormat) (Display*, Visual*);
  222. static tXRenderQueryVersion xRenderQueryVersion = nullptr;
  223. static tXRenderFindStandardFormat xRenderFindStandardFormat = nullptr;
  224. static tXRenderFindFormat xRenderFindFormat = nullptr;
  225. static tXRenderFindVisualFormat xRenderFindVisualFormat = nullptr;
  226. static bool isAvailable (::Display* display)
  227. {
  228. static bool hasLoaded = false;
  229. if (! hasLoaded)
  230. {
  231. if (display != nullptr)
  232. {
  233. hasLoaded = true;
  234. ScopedXLock xlock (display);
  235. if (void* h = dlopen ("libXrender.so.1", RTLD_GLOBAL | RTLD_NOW))
  236. {
  237. xRenderQueryVersion = (tXRenderQueryVersion) dlsym (h, "XRenderQueryVersion");
  238. xRenderFindStandardFormat = (tXRenderFindStandardFormat) dlsym (h, "XRenderFindStandardFormat");
  239. xRenderFindFormat = (tXRenderFindFormat) dlsym (h, "XRenderFindFormat");
  240. xRenderFindVisualFormat = (tXRenderFindVisualFormat) dlsym (h, "XRenderFindVisualFormat");
  241. }
  242. if (xRenderQueryVersion != nullptr
  243. && xRenderFindStandardFormat != nullptr
  244. && xRenderFindFormat != nullptr
  245. && xRenderFindVisualFormat != nullptr)
  246. {
  247. int major, minor;
  248. if (xRenderQueryVersion (display, &major, &minor))
  249. return true;
  250. }
  251. }
  252. xRenderQueryVersion = nullptr;
  253. }
  254. return xRenderQueryVersion != nullptr;
  255. }
  256. static bool hasCompositingWindowManager (::Display* display) noexcept
  257. {
  258. return display != nullptr
  259. && XGetSelectionOwner (display, Atoms::getCreating (display, "_NET_WM_CM_S0")) != 0;
  260. }
  261. static XRenderPictFormat* findPictureFormat (::Display* display)
  262. {
  263. ScopedXLock xlock (display);
  264. XRenderPictFormat* pictFormat = nullptr;
  265. if (isAvailable (display))
  266. {
  267. pictFormat = xRenderFindStandardFormat (display, PictStandardARGB32);
  268. if (pictFormat == nullptr)
  269. {
  270. XRenderPictFormat desiredFormat;
  271. desiredFormat.type = PictTypeDirect;
  272. desiredFormat.depth = 32;
  273. desiredFormat.direct.alphaMask = 0xff;
  274. desiredFormat.direct.redMask = 0xff;
  275. desiredFormat.direct.greenMask = 0xff;
  276. desiredFormat.direct.blueMask = 0xff;
  277. desiredFormat.direct.alpha = 24;
  278. desiredFormat.direct.red = 16;
  279. desiredFormat.direct.green = 8;
  280. desiredFormat.direct.blue = 0;
  281. pictFormat = xRenderFindFormat (display,
  282. PictFormatType | PictFormatDepth
  283. | PictFormatRedMask | PictFormatRed
  284. | PictFormatGreenMask | PictFormatGreen
  285. | PictFormatBlueMask | PictFormatBlue
  286. | PictFormatAlphaMask | PictFormatAlpha,
  287. &desiredFormat,
  288. 0);
  289. }
  290. }
  291. return pictFormat;
  292. }
  293. }
  294. #endif
  295. //================================ X11 - Visuals ===============================
  296. namespace Visuals
  297. {
  298. static Visual* findVisualWithDepth (::Display* display, int desiredDepth) noexcept
  299. {
  300. ScopedXLock xlock (display);
  301. Visual* visual = nullptr;
  302. int numVisuals = 0;
  303. long desiredMask = VisualNoMask;
  304. XVisualInfo desiredVisual;
  305. desiredVisual.screen = DefaultScreen (display);
  306. desiredVisual.depth = desiredDepth;
  307. desiredMask = VisualScreenMask | VisualDepthMask;
  308. if (desiredDepth == 32)
  309. {
  310. desiredVisual.c_class = TrueColor;
  311. desiredVisual.red_mask = 0x00FF0000;
  312. desiredVisual.green_mask = 0x0000FF00;
  313. desiredVisual.blue_mask = 0x000000FF;
  314. desiredVisual.bits_per_rgb = 8;
  315. desiredMask |= VisualClassMask;
  316. desiredMask |= VisualRedMaskMask;
  317. desiredMask |= VisualGreenMaskMask;
  318. desiredMask |= VisualBlueMaskMask;
  319. desiredMask |= VisualBitsPerRGBMask;
  320. }
  321. if (auto* xvinfos = XGetVisualInfo (display, desiredMask, &desiredVisual, &numVisuals))
  322. {
  323. for (int i = 0; i < numVisuals; i++)
  324. {
  325. if (xvinfos[i].depth == desiredDepth)
  326. {
  327. visual = xvinfos[i].visual;
  328. break;
  329. }
  330. }
  331. XFree (xvinfos);
  332. }
  333. return visual;
  334. }
  335. static Visual* findVisualFormat (::Display* display, int desiredDepth, int& matchedDepth) noexcept
  336. {
  337. Visual* visual = nullptr;
  338. if (desiredDepth == 32)
  339. {
  340. #if JUCE_USE_XSHM
  341. if (XSHMHelpers::isShmAvailable (display))
  342. {
  343. #if JUCE_USE_XRENDER
  344. if (XRender::isAvailable (display))
  345. {
  346. if (auto pictFormat = XRender::findPictureFormat (display))
  347. {
  348. int numVisuals = 0;
  349. XVisualInfo desiredVisual;
  350. desiredVisual.screen = DefaultScreen (display);
  351. desiredVisual.depth = 32;
  352. desiredVisual.bits_per_rgb = 8;
  353. if (auto xvinfos = XGetVisualInfo (display,
  354. VisualScreenMask | VisualDepthMask | VisualBitsPerRGBMask,
  355. &desiredVisual, &numVisuals))
  356. {
  357. for (int i = 0; i < numVisuals; ++i)
  358. {
  359. auto pictVisualFormat = XRender::xRenderFindVisualFormat (display, xvinfos[i].visual);
  360. if (pictVisualFormat != nullptr
  361. && pictVisualFormat->type == PictTypeDirect
  362. && pictVisualFormat->direct.alphaMask)
  363. {
  364. visual = xvinfos[i].visual;
  365. matchedDepth = 32;
  366. break;
  367. }
  368. }
  369. XFree (xvinfos);
  370. }
  371. }
  372. }
  373. #endif
  374. if (visual == nullptr)
  375. {
  376. visual = findVisualWithDepth (display, 32);
  377. if (visual != nullptr)
  378. matchedDepth = 32;
  379. }
  380. }
  381. #endif
  382. }
  383. if (visual == nullptr && desiredDepth >= 24)
  384. {
  385. visual = findVisualWithDepth (display, 24);
  386. if (visual != nullptr)
  387. matchedDepth = 24;
  388. }
  389. if (visual == nullptr && desiredDepth >= 16)
  390. {
  391. visual = findVisualWithDepth (display, 16);
  392. if (visual != nullptr)
  393. matchedDepth = 16;
  394. }
  395. return visual;
  396. }
  397. }
  398. //================================= X11 - Bitmap ===============================
  399. class XBitmapImage : public ImagePixelData
  400. {
  401. public:
  402. XBitmapImage (::Display* d, Image::PixelFormat format, int w, int h,
  403. bool clearImage, unsigned int imageDepth_, Visual* visual)
  404. : ImagePixelData (format, w, h),
  405. imageDepth (imageDepth_),
  406. display (d)
  407. {
  408. jassert (format == Image::RGB || format == Image::ARGB);
  409. pixelStride = (format == Image::RGB) ? 3 : 4;
  410. lineStride = ((w * pixelStride + 3) & ~3);
  411. ScopedXLock xlock (display);
  412. #if JUCE_USE_XSHM
  413. usingXShm = false;
  414. if ((imageDepth > 16) && XSHMHelpers::isShmAvailable (display))
  415. {
  416. zerostruct (segmentInfo);
  417. segmentInfo.shmid = -1;
  418. segmentInfo.shmaddr = (char *) -1;
  419. segmentInfo.readOnly = False;
  420. xImage = XShmCreateImage (display, visual, imageDepth, ZPixmap, nullptr,
  421. &segmentInfo, (unsigned int) w, (unsigned int) h);
  422. if (xImage != nullptr)
  423. {
  424. if ((segmentInfo.shmid = shmget (IPC_PRIVATE,
  425. (size_t) (xImage->bytes_per_line * xImage->height),
  426. IPC_CREAT | 0777)) >= 0)
  427. {
  428. if (segmentInfo.shmid != -1)
  429. {
  430. segmentInfo.shmaddr = (char*) shmat (segmentInfo.shmid, nullptr, 0);
  431. if (segmentInfo.shmaddr != (void*) -1)
  432. {
  433. segmentInfo.readOnly = False;
  434. xImage->data = segmentInfo.shmaddr;
  435. imageData = (uint8*) segmentInfo.shmaddr;
  436. if (XShmAttach (display, &segmentInfo) != 0)
  437. usingXShm = true;
  438. else
  439. jassertfalse;
  440. }
  441. else
  442. {
  443. shmctl (segmentInfo.shmid, IPC_RMID, nullptr);
  444. }
  445. }
  446. }
  447. }
  448. }
  449. if (! isUsingXShm())
  450. #endif
  451. {
  452. imageDataAllocated.allocate ((size_t) (lineStride * h), format == Image::ARGB && clearImage);
  453. imageData = imageDataAllocated;
  454. xImage = (XImage*) ::calloc (1, sizeof (XImage));
  455. xImage->width = w;
  456. xImage->height = h;
  457. xImage->xoffset = 0;
  458. xImage->format = ZPixmap;
  459. xImage->data = (char*) imageData;
  460. xImage->byte_order = ImageByteOrder (display);
  461. xImage->bitmap_unit = BitmapUnit (display);
  462. xImage->bitmap_bit_order = BitmapBitOrder (display);
  463. xImage->bitmap_pad = 32;
  464. xImage->depth = pixelStride * 8;
  465. xImage->bytes_per_line = lineStride;
  466. xImage->bits_per_pixel = pixelStride * 8;
  467. xImage->red_mask = 0x00FF0000;
  468. xImage->green_mask = 0x0000FF00;
  469. xImage->blue_mask = 0x000000FF;
  470. if (imageDepth == 16)
  471. {
  472. const int pixStride = 2;
  473. const int stride = ((w * pixStride + 3) & ~3);
  474. imageData16Bit.malloc (stride * h);
  475. xImage->data = imageData16Bit;
  476. xImage->bitmap_pad = 16;
  477. xImage->depth = pixStride * 8;
  478. xImage->bytes_per_line = stride;
  479. xImage->bits_per_pixel = pixStride * 8;
  480. xImage->red_mask = visual->red_mask;
  481. xImage->green_mask = visual->green_mask;
  482. xImage->blue_mask = visual->blue_mask;
  483. }
  484. if (! XInitImage (xImage))
  485. jassertfalse;
  486. }
  487. }
  488. ~XBitmapImage() override
  489. {
  490. ScopedXLock xlock (display);
  491. if (gc != None)
  492. XFreeGC (display, gc);
  493. #if JUCE_USE_XSHM
  494. if (isUsingXShm())
  495. {
  496. XShmDetach (display, &segmentInfo);
  497. XFlush (display);
  498. XDestroyImage (xImage);
  499. shmdt (segmentInfo.shmaddr);
  500. shmctl (segmentInfo.shmid, IPC_RMID, nullptr);
  501. }
  502. else
  503. #endif
  504. {
  505. xImage->data = nullptr;
  506. XDestroyImage (xImage);
  507. }
  508. }
  509. std::unique_ptr<LowLevelGraphicsContext> createLowLevelContext() override
  510. {
  511. sendDataChangeMessage();
  512. return std::make_unique<LowLevelGraphicsSoftwareRenderer> (Image (this));
  513. }
  514. void initialiseBitmapData (Image::BitmapData& bitmap, int x, int y,
  515. Image::BitmapData::ReadWriteMode mode) override
  516. {
  517. bitmap.data = imageData + x * pixelStride + y * lineStride;
  518. bitmap.pixelFormat = pixelFormat;
  519. bitmap.lineStride = lineStride;
  520. bitmap.pixelStride = pixelStride;
  521. if (mode != Image::BitmapData::readOnly)
  522. sendDataChangeMessage();
  523. }
  524. ImagePixelData::Ptr clone() override
  525. {
  526. jassertfalse;
  527. return nullptr;
  528. }
  529. std::unique_ptr<ImageType> createType() const override { return std::make_unique<NativeImageType>(); }
  530. void blitToWindow (Window window, int dx, int dy,
  531. unsigned int dw, unsigned int dh, int sx, int sy)
  532. {
  533. ScopedXLock xlock (display);
  534. if (gc == None)
  535. {
  536. XGCValues gcvalues;
  537. gcvalues.foreground = None;
  538. gcvalues.background = None;
  539. gcvalues.function = GXcopy;
  540. gcvalues.plane_mask = AllPlanes;
  541. gcvalues.clip_mask = None;
  542. gcvalues.graphics_exposures = False;
  543. gc = XCreateGC (display, window,
  544. GCBackground | GCForeground | GCFunction | GCPlaneMask | GCClipMask | GCGraphicsExposures,
  545. &gcvalues);
  546. }
  547. if (imageDepth == 16)
  548. {
  549. auto rMask = (uint32) xImage->red_mask;
  550. auto gMask = (uint32) xImage->green_mask;
  551. auto bMask = (uint32) xImage->blue_mask;
  552. auto rShiftL = (uint32) jmax (0, getShiftNeeded (rMask));
  553. auto rShiftR = (uint32) jmax (0, -getShiftNeeded (rMask));
  554. auto gShiftL = (uint32) jmax (0, getShiftNeeded (gMask));
  555. auto gShiftR = (uint32) jmax (0, -getShiftNeeded (gMask));
  556. auto bShiftL = (uint32) jmax (0, getShiftNeeded (bMask));
  557. auto bShiftR = (uint32) jmax (0, -getShiftNeeded (bMask));
  558. const Image::BitmapData srcData (Image (this), Image::BitmapData::readOnly);
  559. for (int y = sy; y < sy + (int)dh; ++y)
  560. {
  561. const uint8* p = srcData.getPixelPointer (sx, y);
  562. for (int x = sx; x < sx + (int)dw; ++x)
  563. {
  564. auto* pixel = (const PixelRGB*) p;
  565. p += srcData.pixelStride;
  566. XPutPixel (xImage, x, y,
  567. (((((uint32) pixel->getRed()) << rShiftL) >> rShiftR) & rMask)
  568. | (((((uint32) pixel->getGreen()) << gShiftL) >> gShiftR) & gMask)
  569. | (((((uint32) pixel->getBlue()) << bShiftL) >> bShiftR) & bMask));
  570. }
  571. }
  572. }
  573. // blit results to screen.
  574. #if JUCE_USE_XSHM
  575. if (isUsingXShm())
  576. XShmPutImage (display, (::Drawable) window, gc, xImage, sx, sy, dx, dy, dw, dh, True);
  577. else
  578. #endif
  579. XPutImage (display, (::Drawable) window, gc, xImage, sx, sy, dx, dy, dw, dh);
  580. }
  581. #if JUCE_USE_XSHM
  582. bool isUsingXShm() const noexcept { return usingXShm; }
  583. #endif
  584. private:
  585. //==============================================================================
  586. XImage* xImage = {};
  587. const unsigned int imageDepth;
  588. HeapBlock<uint8> imageDataAllocated;
  589. HeapBlock<char> imageData16Bit;
  590. int pixelStride, lineStride;
  591. uint8* imageData = {};
  592. GC gc = None;
  593. ::Display* display = {};
  594. #if JUCE_USE_XSHM
  595. XShmSegmentInfo segmentInfo;
  596. bool usingXShm;
  597. #endif
  598. static int getShiftNeeded (const uint32 mask) noexcept
  599. {
  600. for (int i = 32; --i >= 0;)
  601. if (((mask >> i) & 1) != 0)
  602. return i - 7;
  603. jassertfalse;
  604. return 0;
  605. }
  606. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (XBitmapImage)
  607. };
  608. //==============================================================================
  609. #if JUCE_USE_XINERAMA
  610. static Array<XineramaScreenInfo> XineramaQueryDisplays (::Display* display)
  611. {
  612. typedef Bool (*tXineramaIsActive) (::Display*);
  613. typedef XineramaScreenInfo* (*tXineramaQueryScreens) (::Display*, int*);
  614. int major_opcode, first_event, first_error;
  615. if (XQueryExtension (display, "XINERAMA", &major_opcode, &first_event, &first_error))
  616. {
  617. static void* libXinerama = nullptr;
  618. static tXineramaIsActive isActiveFuncPtr = nullptr;
  619. static tXineramaQueryScreens xineramaQueryScreens = nullptr;
  620. if (libXinerama == nullptr)
  621. {
  622. libXinerama = dlopen ("libXinerama.so", RTLD_GLOBAL | RTLD_NOW);
  623. if (libXinerama == nullptr)
  624. libXinerama = dlopen ("libXinerama.so.1", RTLD_GLOBAL | RTLD_NOW);
  625. if (libXinerama != nullptr)
  626. {
  627. isActiveFuncPtr = (tXineramaIsActive) dlsym (libXinerama, "XineramaIsActive");
  628. xineramaQueryScreens = (tXineramaQueryScreens) dlsym (libXinerama, "XineramaQueryScreens");
  629. }
  630. }
  631. if (isActiveFuncPtr != nullptr && xineramaQueryScreens != nullptr && isActiveFuncPtr (display) != 0)
  632. {
  633. int numScreens;
  634. if (auto* xinfo = xineramaQueryScreens (display, &numScreens))
  635. {
  636. Array<XineramaScreenInfo> infos (xinfo, numScreens);
  637. XFree (xinfo);
  638. return infos;
  639. }
  640. }
  641. }
  642. return {};
  643. }
  644. #endif
  645. //==============================================================================
  646. #if JUCE_USE_XRANDR
  647. class XRandrWrapper
  648. {
  649. private:
  650. XRandrWrapper()
  651. {
  652. if (libXrandr == nullptr)
  653. {
  654. libXrandr = dlopen ("libXrandr.so", RTLD_GLOBAL | RTLD_NOW);
  655. if (libXrandr == nullptr)
  656. libXrandr = dlopen ("libXrandr.so.2", RTLD_GLOBAL | RTLD_NOW);
  657. if (libXrandr != nullptr)
  658. {
  659. getScreenResourcesPtr = (tXRRGetScreenResources) dlsym (libXrandr, "XRRGetScreenResources");
  660. freeScreenResourcesPtr = (tXRRFreeScreenResources) dlsym (libXrandr, "XRRFreeScreenResources");
  661. getOutputInfoPtr = (tXRRGetOutputInfo) dlsym (libXrandr, "XRRGetOutputInfo");
  662. freeOutputInfoPtr = (tXRRFreeOutputInfo) dlsym (libXrandr, "XRRFreeOutputInfo");
  663. getCrtcInfoPtr = (tXRRGetCrtcInfo) dlsym (libXrandr, "XRRGetCrtcInfo");
  664. freeCrtcInfoPtr = (tXRRFreeCrtcInfo) dlsym (libXrandr, "XRRFreeCrtcInfo");
  665. getOutputPrimaryPtr = (tXRRGetOutputPrimary) dlsym (libXrandr, "XRRGetOutputPrimary");
  666. }
  667. }
  668. }
  669. public:
  670. //==============================================================================
  671. static XRandrWrapper& getInstance()
  672. {
  673. static XRandrWrapper xrandr;
  674. return xrandr;
  675. }
  676. //==============================================================================
  677. XRRScreenResources* getScreenResources (::Display* display, ::Window window)
  678. {
  679. if (getScreenResourcesPtr != nullptr)
  680. return getScreenResourcesPtr (display, window);
  681. return nullptr;
  682. }
  683. XRROutputInfo* getOutputInfo (::Display* display, XRRScreenResources* resources, RROutput output)
  684. {
  685. if (getOutputInfoPtr != nullptr)
  686. return getOutputInfoPtr (display, resources, output);
  687. return nullptr;
  688. }
  689. XRRCrtcInfo* getCrtcInfo (::Display* display, XRRScreenResources* resources, RRCrtc crtc)
  690. {
  691. if (getCrtcInfoPtr != nullptr)
  692. return getCrtcInfoPtr (display, resources, crtc);
  693. return nullptr;
  694. }
  695. RROutput getOutputPrimary (::Display* display, ::Window window)
  696. {
  697. if (getOutputPrimaryPtr != nullptr)
  698. return getOutputPrimaryPtr (display, window);
  699. return 0;
  700. }
  701. //==============================================================================
  702. void freeScreenResources (XRRScreenResources* ptr)
  703. {
  704. if (freeScreenResourcesPtr != nullptr)
  705. freeScreenResourcesPtr (ptr);
  706. }
  707. void freeOutputInfo (XRROutputInfo* ptr)
  708. {
  709. if (freeOutputInfoPtr != nullptr)
  710. freeOutputInfoPtr (ptr);
  711. }
  712. void freeCrtcInfo (XRRCrtcInfo* ptr)
  713. {
  714. if (freeCrtcInfoPtr != nullptr)
  715. freeCrtcInfoPtr (ptr);
  716. }
  717. private:
  718. using tXRRGetScreenResources = XRRScreenResources* (*) (::Display*, ::Window);
  719. using tXRRFreeScreenResources = void (*) (XRRScreenResources*);
  720. using tXRRGetOutputInfo = XRROutputInfo* (*) (::Display*, XRRScreenResources*, RROutput);
  721. using tXRRFreeOutputInfo = void (*) (XRROutputInfo*);
  722. using tXRRGetCrtcInfo = XRRCrtcInfo* (*) (::Display*, XRRScreenResources*, RRCrtc);
  723. using tXRRFreeCrtcInfo = void (*) (XRRCrtcInfo*);
  724. using tXRRGetOutputPrimary = RROutput (*) (::Display*, ::Window);
  725. void* libXrandr = nullptr;
  726. tXRRGetScreenResources getScreenResourcesPtr = nullptr;
  727. tXRRFreeScreenResources freeScreenResourcesPtr = nullptr;
  728. tXRRGetOutputInfo getOutputInfoPtr = nullptr;
  729. tXRRFreeOutputInfo freeOutputInfoPtr = nullptr;
  730. tXRRGetCrtcInfo getCrtcInfoPtr = nullptr;
  731. tXRRFreeCrtcInfo freeCrtcInfoPtr = nullptr;
  732. tXRRGetOutputPrimary getOutputPrimaryPtr = nullptr;
  733. };
  734. #endif
  735. static double getDisplayDPI (::Display* display, int index)
  736. {
  737. auto widthMM = DisplayWidthMM (display, index);
  738. auto heightMM = DisplayHeightMM (display, index);
  739. if (widthMM > 0 && heightMM > 0)
  740. return (((DisplayWidth (display, index) * 25.4) / widthMM) + ((DisplayHeight (display, index) * 25.4) / heightMM)) / 2.0;
  741. return 96.0;
  742. }
  743. static double getScaleForDisplay (const String& name, double dpi)
  744. {
  745. if (name.isNotEmpty())
  746. {
  747. // Ubuntu and derived distributions now save a per-display scale factor as a configuration
  748. // variable. This can be changed in the Monitor system settings panel.
  749. ChildProcess dconf;
  750. if (File ("/usr/bin/dconf").existsAsFile()
  751. && dconf.start ("/usr/bin/dconf read /com/ubuntu/user-interface/scale-factor", ChildProcess::wantStdOut))
  752. {
  753. if (dconf.waitForProcessToFinish (200))
  754. {
  755. auto jsonOutput = dconf.readAllProcessOutput().replaceCharacter ('\'', '"');
  756. if (dconf.getExitCode() == 0 && jsonOutput.isNotEmpty())
  757. {
  758. auto jsonVar = JSON::parse (jsonOutput);
  759. if (auto* object = jsonVar.getDynamicObject())
  760. {
  761. auto scaleFactorVar = object->getProperty (name);
  762. if (! scaleFactorVar.isVoid())
  763. {
  764. auto scaleFactor = ((double) scaleFactorVar) / 8.0;
  765. if (scaleFactor > 0.0)
  766. return scaleFactor;
  767. }
  768. }
  769. }
  770. }
  771. }
  772. }
  773. {
  774. // Other gnome based distros now use gsettings for a global scale factor
  775. ChildProcess gsettings;
  776. if (File ("/usr/bin/gsettings").existsAsFile()
  777. && gsettings.start ("/usr/bin/gsettings get org.gnome.desktop.interface scaling-factor", ChildProcess::wantStdOut))
  778. {
  779. if (gsettings.waitForProcessToFinish (200))
  780. {
  781. auto gsettingsOutput = StringArray::fromTokens (gsettings.readAllProcessOutput(), true);
  782. if (gsettingsOutput.size() >= 2 && gsettingsOutput[1].length() > 0)
  783. {
  784. auto scaleFactor = gsettingsOutput[1].getDoubleValue();
  785. if (scaleFactor > 0.0)
  786. return scaleFactor;
  787. return 1.0;
  788. }
  789. }
  790. }
  791. }
  792. // If no scale factor is set by GNOME or Ubuntu then calculate from monitor dpi
  793. // We use the same approach as chromium which simply divides the dpi by 96
  794. // and then rounds the result
  795. return round (dpi / 96.0);
  796. }
  797. //=============================== X11 - Pixmap =================================
  798. namespace PixmapHelpers
  799. {
  800. Pixmap createColourPixmapFromImage (::Display* display, const Image& image)
  801. {
  802. ScopedXLock xlock (display);
  803. auto width = (unsigned int) image.getWidth();
  804. auto height = (unsigned int) image.getHeight();
  805. HeapBlock<uint32> colour (width * height);
  806. int index = 0;
  807. for (int y = 0; y < (int) height; ++y)
  808. for (int x = 0; x < (int) width; ++x)
  809. colour[index++] = image.getPixelAt (x, y).getARGB();
  810. XImage* ximage = XCreateImage (display, CopyFromParent, 24, ZPixmap,
  811. 0, reinterpret_cast<char*> (colour.getData()),
  812. width, height, 32, 0);
  813. Pixmap pixmap = XCreatePixmap (display, DefaultRootWindow (display),
  814. width, height, 24);
  815. GC gc = XCreateGC (display, pixmap, 0, nullptr);
  816. XPutImage (display, pixmap, gc, ximage, 0, 0, 0, 0, width, height);
  817. XFreeGC (display, gc);
  818. XFree (ximage);
  819. return pixmap;
  820. }
  821. Pixmap createMaskPixmapFromImage (::Display* display, const Image& image)
  822. {
  823. ScopedXLock xlock (display);
  824. auto width = (unsigned int) image.getWidth();
  825. auto height = (unsigned int) image.getHeight();
  826. auto stride = (width + 7) >> 3;
  827. HeapBlock<char> mask;
  828. mask.calloc (stride * height);
  829. const bool msbfirst = (BitmapBitOrder (display) == MSBFirst);
  830. for (unsigned int y = 0; y < height; ++y)
  831. {
  832. for (unsigned int x = 0; x < width; ++x)
  833. {
  834. auto bit = (char) (1 << (msbfirst ? (7 - (x & 7)) : (x & 7)));
  835. const unsigned int offset = y * stride + (x >> 3);
  836. if (image.getPixelAt ((int) x, (int) y).getAlpha() >= 128)
  837. mask[offset] |= bit;
  838. }
  839. }
  840. return XCreatePixmapFromBitmapData (display, DefaultRootWindow (display),
  841. mask.getData(), width, height, 1, 0, 1);
  842. }
  843. }
  844. static void* createDraggingHandCursor()
  845. {
  846. static unsigned char dragHandData[] = { 71,73,70,56,57,97,16,0,16,0,145,2,0,0,0,0,255,255,255,0,
  847. 0,0,0,0,0,33,249,4,1,0,0,2,0,44,0,0,0,0,16,0, 16,0,0,2,52,148,47,0,200,185,16,130,90,12,74,139,107,84,123,39,
  848. 132,117,151,116,132,146,248,60,209,138,98,22,203,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 };
  849. size_t dragHandDataSize = 99;
  850. return CustomMouseCursorInfo (ImageFileFormat::loadFrom (dragHandData, dragHandDataSize), { 8, 7 }).create();
  851. }
  852. //==============================================================================
  853. static int numAlwaysOnTopPeers = 0;
  854. bool juce_areThereAnyAlwaysOnTopWindows()
  855. {
  856. return numAlwaysOnTopPeers > 0;
  857. }
  858. //==============================================================================
  859. class LinuxComponentPeer : public ComponentPeer
  860. {
  861. public:
  862. LinuxComponentPeer (Component& comp, int windowStyleFlags, Window parentToAddTo)
  863. : ComponentPeer (comp, windowStyleFlags),
  864. isAlwaysOnTop (comp.isAlwaysOnTop())
  865. {
  866. // it's dangerous to create a window on a thread other than the message thread..
  867. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
  868. display = XWindowSystem::getInstance()->displayRef();
  869. atoms.reset (new Atoms (display));
  870. dragState.reset (new DragState (display));
  871. repainter.reset (new LinuxRepaintManager (*this, display));
  872. if (isAlwaysOnTop)
  873. ++numAlwaysOnTopPeers;
  874. createWindow (parentToAddTo);
  875. setTitle (component.getName());
  876. getNativeRealtimeModifiers = []
  877. {
  878. ScopedXDisplay xDisplay;
  879. if (auto d = xDisplay.display)
  880. {
  881. Window root, child;
  882. int x, y, winx, winy;
  883. unsigned int mask;
  884. int mouseMods = 0;
  885. ScopedXLock xlock (d);
  886. if (XQueryPointer (d, RootWindow (d, DefaultScreen (d)),
  887. &root, &child, &x, &y, &winx, &winy, &mask) != False)
  888. {
  889. if ((mask & Button1Mask) != 0) mouseMods |= ModifierKeys::leftButtonModifier;
  890. if ((mask & Button2Mask) != 0) mouseMods |= ModifierKeys::middleButtonModifier;
  891. if ((mask & Button3Mask) != 0) mouseMods |= ModifierKeys::rightButtonModifier;
  892. }
  893. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutMouseButtons().withFlags (mouseMods);
  894. }
  895. return ModifierKeys::currentModifiers;
  896. };
  897. }
  898. ~LinuxComponentPeer() override
  899. {
  900. // it's dangerous to delete a window on a thread other than the message thread..
  901. JUCE_ASSERT_MESSAGE_MANAGER_IS_LOCKED
  902. #if JUCE_X11_SUPPORTS_XEMBED
  903. juce_handleXEmbedEvent (this, nullptr);
  904. #endif
  905. deleteIconPixmaps();
  906. destroyWindow();
  907. windowH = 0;
  908. if (isAlwaysOnTop)
  909. --numAlwaysOnTopPeers;
  910. // delete before display
  911. repainter = nullptr;
  912. display = XWindowSystem::getInstance()->displayUnref();
  913. }
  914. //==============================================================================
  915. void* getNativeHandle() const override
  916. {
  917. return (void*) windowH;
  918. }
  919. static LinuxComponentPeer* getPeerFor (Window windowHandle) noexcept
  920. {
  921. XPointer peer = nullptr;
  922. if (display != nullptr)
  923. {
  924. ScopedXLock xlock (display);
  925. if (! XFindContext (display, (XID) windowHandle, windowHandleXContext, &peer))
  926. if (peer != nullptr && ! ComponentPeer::isValidPeer (reinterpret_cast<LinuxComponentPeer*> (peer)))
  927. peer = nullptr;
  928. }
  929. return reinterpret_cast<LinuxComponentPeer*> (peer);
  930. }
  931. void setVisible (bool shouldBeVisible) override
  932. {
  933. ScopedXLock xlock (display);
  934. if (shouldBeVisible)
  935. XMapWindow (display, windowH);
  936. else
  937. XUnmapWindow (display, windowH);
  938. }
  939. void setTitle (const String& title) override
  940. {
  941. XTextProperty nameProperty;
  942. char* strings[] = { const_cast<char*> (title.toRawUTF8()) };
  943. ScopedXLock xlock (display);
  944. if (XStringListToTextProperty (strings, 1, &nameProperty))
  945. {
  946. XSetWMName (display, windowH, &nameProperty);
  947. XSetWMIconName (display, windowH, &nameProperty);
  948. XFree (nameProperty.value);
  949. }
  950. }
  951. void updateScaleFactorFromNewBounds (const Rectangle<int>& newBounds, bool isPhysical)
  952. {
  953. Point<int> translation = (parentWindow != 0 ? getScreenPosition (isPhysical) : Point<int>());
  954. auto newScaleFactor = Desktop::getInstance().getDisplays().findDisplayForRect (newBounds.translated (translation.x, translation.y), isPhysical).scale
  955. / Desktop::getInstance().getGlobalScaleFactor();
  956. if (! approximatelyEqual (newScaleFactor, currentScaleFactor))
  957. {
  958. currentScaleFactor = newScaleFactor;
  959. scaleFactorListeners.call ([&] (ScaleFactorListener& l) { l.nativeScaleFactorChanged (currentScaleFactor); });
  960. }
  961. }
  962. void setBounds (const Rectangle<int>& newBounds, bool isNowFullScreen) override
  963. {
  964. if (fullScreen && ! isNowFullScreen)
  965. {
  966. // When transitioning back from fullscreen, we might need to remove
  967. // the FULLSCREEN window property
  968. Atom fs = Atoms::getIfExists (display, "_NET_WM_STATE_FULLSCREEN");
  969. if (fs != None)
  970. {
  971. Window root = RootWindow (display, DefaultScreen (display));
  972. XClientMessageEvent clientMsg;
  973. clientMsg.display = display;
  974. clientMsg.window = windowH;
  975. clientMsg.type = ClientMessage;
  976. clientMsg.format = 32;
  977. clientMsg.message_type = atoms->windowState;
  978. clientMsg.data.l[0] = 0; // Remove
  979. clientMsg.data.l[1] = (long) fs;
  980. clientMsg.data.l[2] = 0;
  981. clientMsg.data.l[3] = 1; // Normal Source
  982. ScopedXLock xlock (display);
  983. XSendEvent (display, root, false,
  984. SubstructureRedirectMask | SubstructureNotifyMask,
  985. (XEvent*) &clientMsg);
  986. }
  987. }
  988. fullScreen = isNowFullScreen;
  989. if (windowH != 0)
  990. {
  991. bounds = newBounds.withSize (jmax (1, newBounds.getWidth()),
  992. jmax (1, newBounds.getHeight()));
  993. updateScaleFactorFromNewBounds (bounds, false);
  994. auto physicalBounds = (parentWindow == 0 ? Desktop::getInstance().getDisplays().logicalToPhysical (bounds)
  995. : bounds * currentScaleFactor);
  996. WeakReference<Component> deletionChecker (&component);
  997. ScopedXLock xlock (display);
  998. auto* hints = XAllocSizeHints();
  999. hints->flags = USSize | USPosition;
  1000. hints->x = physicalBounds.getX();
  1001. hints->y = physicalBounds.getY();
  1002. hints->width = physicalBounds.getWidth();
  1003. hints->height = physicalBounds.getHeight();
  1004. if ((getStyleFlags() & windowIsResizable) == 0)
  1005. {
  1006. hints->min_width = hints->max_width = hints->width;
  1007. hints->min_height = hints->max_height = hints->height;
  1008. hints->flags |= PMinSize | PMaxSize;
  1009. }
  1010. XSetWMNormalHints (display, windowH, hints);
  1011. XFree (hints);
  1012. XMoveResizeWindow (display, windowH,
  1013. physicalBounds.getX() - windowBorder.getLeft(),
  1014. physicalBounds.getY() - windowBorder.getTop(),
  1015. (unsigned int) physicalBounds.getWidth(),
  1016. (unsigned int) physicalBounds.getHeight());
  1017. if (deletionChecker != nullptr)
  1018. {
  1019. updateBorderSize();
  1020. handleMovedOrResized();
  1021. }
  1022. }
  1023. }
  1024. Point<int> getScreenPosition (bool physical) const
  1025. {
  1026. auto screenBounds = (parentWindow == 0 ? bounds
  1027. : bounds.translated (parentScreenPosition.x, parentScreenPosition.y));
  1028. if (physical)
  1029. return Desktop::getInstance().getDisplays().logicalToPhysical (screenBounds.getTopLeft());
  1030. return screenBounds.getTopLeft();
  1031. }
  1032. Rectangle<int> getBounds() const override { return bounds; }
  1033. using ComponentPeer::localToGlobal;
  1034. Point<float> localToGlobal (Point<float> relativePosition) override { return relativePosition + getScreenPosition (false).toFloat(); }
  1035. using ComponentPeer::globalToLocal;
  1036. Point<float> globalToLocal (Point<float> screenPosition) override { return screenPosition - getScreenPosition (false).toFloat(); }
  1037. void setAlpha (float /* newAlpha */) override
  1038. {
  1039. //xxx todo!
  1040. }
  1041. StringArray getAvailableRenderingEngines() override
  1042. {
  1043. return StringArray ("Software Renderer");
  1044. }
  1045. void setMinimised (bool shouldBeMinimised) override
  1046. {
  1047. if (shouldBeMinimised)
  1048. {
  1049. Window root = RootWindow (display, DefaultScreen (display));
  1050. XClientMessageEvent clientMsg;
  1051. clientMsg.display = display;
  1052. clientMsg.window = windowH;
  1053. clientMsg.type = ClientMessage;
  1054. clientMsg.format = 32;
  1055. clientMsg.message_type = atoms->changeState;
  1056. clientMsg.data.l[0] = IconicState;
  1057. ScopedXLock xlock (display);
  1058. XSendEvent (display, root, false, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*) &clientMsg);
  1059. }
  1060. else
  1061. {
  1062. setVisible (true);
  1063. }
  1064. }
  1065. bool isMinimised() const override
  1066. {
  1067. ScopedXLock xlock (display);
  1068. GetXProperty prop (display, windowH, atoms->state, 0, 64, false, atoms->state);
  1069. if (prop.success && prop.actualType == atoms->state
  1070. && prop.actualFormat == 32 && prop.numItems > 0)
  1071. {
  1072. unsigned long state;
  1073. memcpy (&state, prop.data, sizeof (unsigned long));
  1074. return state == IconicState;
  1075. }
  1076. return false;
  1077. }
  1078. void setFullScreen (bool shouldBeFullScreen) override
  1079. {
  1080. auto r = lastNonFullscreenBounds; // (get a copy of this before de-minimising)
  1081. setMinimised (false);
  1082. if (fullScreen != shouldBeFullScreen)
  1083. {
  1084. if (shouldBeFullScreen)
  1085. r = Desktop::getInstance().getDisplays().getMainDisplay().userArea;
  1086. if (! r.isEmpty())
  1087. setBounds (ScalingHelpers::scaledScreenPosToUnscaled (component, r), shouldBeFullScreen);
  1088. component.repaint();
  1089. }
  1090. }
  1091. bool isFullScreen() const override
  1092. {
  1093. return fullScreen;
  1094. }
  1095. bool isChildWindowOf (Window possibleParent) const
  1096. {
  1097. Window* windowList = nullptr;
  1098. uint32 windowListSize = 0;
  1099. Window parent, root;
  1100. ScopedXLock xlock (display);
  1101. if (XQueryTree (display, windowH, &root, &parent, &windowList, &windowListSize) != 0)
  1102. {
  1103. if (windowList != nullptr)
  1104. XFree (windowList);
  1105. return parent == possibleParent;
  1106. }
  1107. return false;
  1108. }
  1109. bool isParentWindowOf (Window possibleChild) const
  1110. {
  1111. if (windowH != 0 && possibleChild != 0)
  1112. {
  1113. if (possibleChild == windowH)
  1114. return true;
  1115. Window* windowList = nullptr;
  1116. uint32 windowListSize = 0;
  1117. Window parent, root;
  1118. ScopedXLock xlock (display);
  1119. if (XQueryTree (display, possibleChild, &root, &parent, &windowList, &windowListSize) != 0)
  1120. {
  1121. if (windowList != nullptr)
  1122. XFree (windowList);
  1123. if (parent == root)
  1124. return false;
  1125. return isParentWindowOf (parent);
  1126. }
  1127. }
  1128. return false;
  1129. }
  1130. bool isFrontWindow() const
  1131. {
  1132. Window* windowList = nullptr;
  1133. uint32 windowListSize = 0;
  1134. bool result = false;
  1135. ScopedXLock xlock (display);
  1136. Window parent, root = RootWindow (display, DefaultScreen (display));
  1137. if (XQueryTree (display, root, &root, &parent, &windowList, &windowListSize) != 0)
  1138. {
  1139. for (int i = (int) windowListSize; --i >= 0;)
  1140. {
  1141. if (auto* peer = LinuxComponentPeer::getPeerFor (windowList[i]))
  1142. {
  1143. result = (peer == this);
  1144. break;
  1145. }
  1146. }
  1147. }
  1148. if (windowList != nullptr)
  1149. XFree (windowList);
  1150. return result;
  1151. }
  1152. bool contains (Point<int> localPos, bool trueIfInAChildWindow) const override
  1153. {
  1154. if (! bounds.withZeroOrigin().contains (localPos))
  1155. return false;
  1156. for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;)
  1157. {
  1158. auto* c = Desktop::getInstance().getComponent (i);
  1159. if (c == &component)
  1160. break;
  1161. if (! c->isVisible())
  1162. continue;
  1163. if (auto* peer = c->getPeer())
  1164. if (peer->contains (localPos + bounds.getPosition() - peer->getBounds().getPosition(), true))
  1165. return false;
  1166. }
  1167. if (trueIfInAChildWindow)
  1168. return true;
  1169. ::Window root, child;
  1170. int wx, wy;
  1171. unsigned int ww, wh, bw, bitDepth;
  1172. ScopedXLock xlock (display);
  1173. localPos *= currentScaleFactor;
  1174. return XGetGeometry (display, (::Drawable) windowH, &root, &wx, &wy, &ww, &wh, &bw, &bitDepth)
  1175. && XTranslateCoordinates (display, windowH, windowH, localPos.getX(), localPos.getY(), &wx, &wy, &child)
  1176. && child == None;
  1177. }
  1178. BorderSize<int> getFrameSize() const override
  1179. {
  1180. return {};
  1181. }
  1182. bool setAlwaysOnTop (bool /* alwaysOnTop */) override
  1183. {
  1184. return false;
  1185. }
  1186. void toFront (bool makeActive) override
  1187. {
  1188. if (makeActive)
  1189. {
  1190. setVisible (true);
  1191. grabFocus();
  1192. }
  1193. {
  1194. ScopedXLock xlock (display);
  1195. XEvent ev;
  1196. ev.xclient.type = ClientMessage;
  1197. ev.xclient.serial = 0;
  1198. ev.xclient.send_event = True;
  1199. ev.xclient.message_type = atoms->activeWin;
  1200. ev.xclient.window = windowH;
  1201. ev.xclient.format = 32;
  1202. ev.xclient.data.l[0] = 2;
  1203. ev.xclient.data.l[1] = getUserTime();
  1204. ev.xclient.data.l[2] = 0;
  1205. ev.xclient.data.l[3] = 0;
  1206. ev.xclient.data.l[4] = 0;
  1207. XSendEvent (display, RootWindow (display, DefaultScreen (display)),
  1208. False, SubstructureRedirectMask | SubstructureNotifyMask, &ev);
  1209. XSync (display, False);
  1210. }
  1211. handleBroughtToFront();
  1212. }
  1213. void toBehind (ComponentPeer* other) override
  1214. {
  1215. if (auto* otherPeer = dynamic_cast<LinuxComponentPeer*> (other))
  1216. {
  1217. if (otherPeer->styleFlags & windowIsTemporary)
  1218. return;
  1219. setMinimised (false);
  1220. Window newStack[] = { otherPeer->windowH, windowH };
  1221. ScopedXLock xlock (display);
  1222. XRestackWindows (display, newStack, 2);
  1223. }
  1224. else
  1225. jassertfalse; // wrong type of window?
  1226. }
  1227. bool isFocused() const override
  1228. {
  1229. int revert = 0;
  1230. Window focusedWindow = 0;
  1231. ScopedXLock xlock (display);
  1232. XGetInputFocus (display, &focusedWindow, &revert);
  1233. if (focusedWindow == PointerRoot)
  1234. return false;
  1235. return isParentWindowOf (focusedWindow);
  1236. }
  1237. Window getFocusWindow()
  1238. {
  1239. #if JUCE_X11_SUPPORTS_XEMBED
  1240. if (Window w = (Window) juce_getCurrentFocusWindow (this))
  1241. return w;
  1242. #endif
  1243. return windowH;
  1244. }
  1245. void grabFocus() override
  1246. {
  1247. XWindowAttributes atts;
  1248. ScopedXLock xlock (display);
  1249. if (windowH != 0
  1250. && XGetWindowAttributes (display, windowH, &atts)
  1251. && atts.map_state == IsViewable
  1252. && ! isFocused())
  1253. {
  1254. XSetInputFocus (display, getFocusWindow(), RevertToParent, (::Time) getUserTime());
  1255. isActiveApplication = true;
  1256. }
  1257. }
  1258. void textInputRequired (Point<int>, TextInputTarget&) override {}
  1259. void repaint (const Rectangle<int>& area) override
  1260. {
  1261. repainter->repaint (area.getIntersection (bounds.withZeroOrigin()));
  1262. }
  1263. void performAnyPendingRepaintsNow() override
  1264. {
  1265. repainter->performAnyPendingRepaintsNow();
  1266. }
  1267. void setIcon (const Image& newIcon) override
  1268. {
  1269. const int dataSize = newIcon.getWidth() * newIcon.getHeight() + 2;
  1270. HeapBlock<unsigned long> data (dataSize);
  1271. int index = 0;
  1272. data[index++] = (unsigned long) newIcon.getWidth();
  1273. data[index++] = (unsigned long) newIcon.getHeight();
  1274. for (int y = 0; y < newIcon.getHeight(); ++y)
  1275. for (int x = 0; x < newIcon.getWidth(); ++x)
  1276. data[index++] = (unsigned long) newIcon.getPixelAt (x, y).getARGB();
  1277. ScopedXLock xlock (display);
  1278. xchangeProperty (windowH, Atoms::getCreating (display, "_NET_WM_ICON"), XA_CARDINAL, 32, data.getData(), dataSize);
  1279. deleteIconPixmaps();
  1280. XWMHints* wmHints = XGetWMHints (display, windowH);
  1281. if (wmHints == nullptr)
  1282. wmHints = XAllocWMHints();
  1283. wmHints->flags |= IconPixmapHint | IconMaskHint;
  1284. wmHints->icon_pixmap = PixmapHelpers::createColourPixmapFromImage (display, newIcon);
  1285. wmHints->icon_mask = PixmapHelpers::createMaskPixmapFromImage (display, newIcon);
  1286. XSetWMHints (display, windowH, wmHints);
  1287. XFree (wmHints);
  1288. XSync (display, False);
  1289. }
  1290. void deleteIconPixmaps()
  1291. {
  1292. ScopedXLock xlock (display);
  1293. if (auto* wmHints = XGetWMHints (display, windowH))
  1294. {
  1295. if ((wmHints->flags & IconPixmapHint) != 0)
  1296. {
  1297. wmHints->flags &= ~IconPixmapHint;
  1298. XFreePixmap (display, wmHints->icon_pixmap);
  1299. }
  1300. if ((wmHints->flags & IconMaskHint) != 0)
  1301. {
  1302. wmHints->flags &= ~IconMaskHint;
  1303. XFreePixmap (display, wmHints->icon_mask);
  1304. }
  1305. XSetWMHints (display, windowH, wmHints);
  1306. XFree (wmHints);
  1307. }
  1308. }
  1309. //==============================================================================
  1310. void handleWindowMessage (XEvent& event)
  1311. {
  1312. switch (event.xany.type)
  1313. {
  1314. case KeyPressEventType: handleKeyPressEvent (event.xkey); break;
  1315. case KeyRelease: handleKeyReleaseEvent (event.xkey); break;
  1316. case ButtonPress: handleButtonPressEvent (event.xbutton); break;
  1317. case ButtonRelease: handleButtonReleaseEvent (event.xbutton); break;
  1318. case MotionNotify: handleMotionNotifyEvent (event.xmotion); break;
  1319. case EnterNotify: handleEnterNotifyEvent (event.xcrossing); break;
  1320. case LeaveNotify: handleLeaveNotifyEvent (event.xcrossing); break;
  1321. case FocusIn: handleFocusInEvent(); break;
  1322. case FocusOut: handleFocusOutEvent(); break;
  1323. case Expose: handleExposeEvent (event.xexpose); break;
  1324. case MappingNotify: handleMappingNotify (event.xmapping); break;
  1325. case ClientMessage: handleClientMessageEvent (event.xclient, event); break;
  1326. case SelectionNotify: handleDragAndDropSelection (event); break;
  1327. case ConfigureNotify: handleConfigureNotifyEvent (event.xconfigure); break;
  1328. case ReparentNotify:
  1329. case GravityNotify: handleGravityNotify(); break;
  1330. case SelectionClear: handleExternalSelectionClear(); break;
  1331. case SelectionRequest: handleExternalSelectionRequest (event); break;
  1332. case CirculateNotify:
  1333. case CreateNotify:
  1334. case DestroyNotify:
  1335. // Think we can ignore these
  1336. break;
  1337. case MapNotify:
  1338. mapped = true;
  1339. handleBroughtToFront();
  1340. break;
  1341. case UnmapNotify:
  1342. mapped = false;
  1343. break;
  1344. default:
  1345. #if JUCE_USE_XSHM
  1346. if (XSHMHelpers::isShmAvailable (display))
  1347. {
  1348. ScopedXLock xlock (display);
  1349. if (event.xany.type == shmCompletionEvent)
  1350. repainter->notifyPaintCompleted();
  1351. }
  1352. #endif
  1353. break;
  1354. }
  1355. }
  1356. void handleKeyPressEvent (XKeyEvent& keyEvent)
  1357. {
  1358. auto oldMods = ModifierKeys::currentModifiers;
  1359. char utf8 [64] = { 0 };
  1360. juce_wchar unicodeChar = 0;
  1361. int keyCode = 0;
  1362. bool keyDownChange = false;
  1363. KeySym sym;
  1364. {
  1365. ScopedXLock xlock (display);
  1366. updateKeyStates ((int) keyEvent.keycode, true);
  1367. String oldLocale (::setlocale (LC_ALL, nullptr));
  1368. ::setlocale (LC_ALL, "");
  1369. XLookupString (&keyEvent, utf8, sizeof (utf8), &sym, nullptr);
  1370. if (oldLocale.isNotEmpty())
  1371. ::setlocale (LC_ALL, oldLocale.toRawUTF8());
  1372. unicodeChar = *CharPointer_UTF8 (utf8);
  1373. keyCode = (int) unicodeChar;
  1374. if (keyCode < 0x20)
  1375. keyCode = (int) XkbKeycodeToKeysym (display, (::KeyCode) keyEvent.keycode, 0, ModifierKeys::currentModifiers.isShiftDown() ? 1 : 0);
  1376. keyDownChange = (sym != NoSymbol) && ! updateKeyModifiersFromSym (sym, true);
  1377. }
  1378. bool keyPressed = false;
  1379. if ((sym & 0xff00) == 0xff00 || keyCode == XK_ISO_Left_Tab)
  1380. {
  1381. switch (sym) // Translate keypad
  1382. {
  1383. case XK_KP_Add: keyCode = XK_plus; break;
  1384. case XK_KP_Subtract: keyCode = XK_hyphen; break;
  1385. case XK_KP_Divide: keyCode = XK_slash; break;
  1386. case XK_KP_Multiply: keyCode = XK_asterisk; break;
  1387. case XK_KP_Enter: keyCode = XK_Return; break;
  1388. case XK_KP_Insert: keyCode = XK_Insert; break;
  1389. case XK_Delete:
  1390. case XK_KP_Delete: keyCode = XK_Delete; break;
  1391. case XK_KP_Left: keyCode = XK_Left; break;
  1392. case XK_KP_Right: keyCode = XK_Right; break;
  1393. case XK_KP_Up: keyCode = XK_Up; break;
  1394. case XK_KP_Down: keyCode = XK_Down; break;
  1395. case XK_KP_Home: keyCode = XK_Home; break;
  1396. case XK_KP_End: keyCode = XK_End; break;
  1397. case XK_KP_Page_Down: keyCode = XK_Page_Down; break;
  1398. case XK_KP_Page_Up: keyCode = XK_Page_Up; break;
  1399. case XK_KP_0: keyCode = XK_0; break;
  1400. case XK_KP_1: keyCode = XK_1; break;
  1401. case XK_KP_2: keyCode = XK_2; break;
  1402. case XK_KP_3: keyCode = XK_3; break;
  1403. case XK_KP_4: keyCode = XK_4; break;
  1404. case XK_KP_5: keyCode = XK_5; break;
  1405. case XK_KP_6: keyCode = XK_6; break;
  1406. case XK_KP_7: keyCode = XK_7; break;
  1407. case XK_KP_8: keyCode = XK_8; break;
  1408. case XK_KP_9: keyCode = XK_9; break;
  1409. default: break;
  1410. }
  1411. switch (keyCode)
  1412. {
  1413. case XK_Left:
  1414. case XK_Right:
  1415. case XK_Up:
  1416. case XK_Down:
  1417. case XK_Page_Up:
  1418. case XK_Page_Down:
  1419. case XK_End:
  1420. case XK_Home:
  1421. case XK_Delete:
  1422. case XK_Insert:
  1423. keyPressed = true;
  1424. keyCode = static_cast<int> ((keyCode & 0xff) | Keys::extendedKeyModifier);
  1425. break;
  1426. case XK_Tab:
  1427. case XK_Return:
  1428. case XK_Escape:
  1429. case XK_BackSpace:
  1430. keyPressed = true;
  1431. keyCode &= 0xff;
  1432. break;
  1433. case XK_ISO_Left_Tab:
  1434. keyPressed = true;
  1435. keyCode = XK_Tab & 0xff;
  1436. break;
  1437. default:
  1438. if (sym >= XK_F1 && sym <= XK_F35)
  1439. {
  1440. keyPressed = true;
  1441. keyCode = static_cast<int> ((sym & 0xff) | Keys::extendedKeyModifier);
  1442. }
  1443. break;
  1444. }
  1445. }
  1446. if (utf8[0] != 0 || ((sym & 0xff00) == 0 && sym >= 8))
  1447. keyPressed = true;
  1448. if (oldMods != ModifierKeys::currentModifiers)
  1449. handleModifierKeysChange();
  1450. if (keyDownChange)
  1451. handleKeyUpOrDown (true);
  1452. if (keyPressed)
  1453. handleKeyPress (keyCode, unicodeChar);
  1454. }
  1455. static bool isKeyReleasePartOfAutoRepeat (const XKeyEvent& keyReleaseEvent)
  1456. {
  1457. if (XPending (display))
  1458. {
  1459. XEvent e;
  1460. XPeekEvent (display, &e);
  1461. // Look for a subsequent key-down event with the same timestamp and keycode
  1462. return e.type == KeyPressEventType
  1463. && e.xkey.keycode == keyReleaseEvent.keycode
  1464. && e.xkey.time == keyReleaseEvent.time;
  1465. }
  1466. return false;
  1467. }
  1468. void handleKeyReleaseEvent (const XKeyEvent& keyEvent)
  1469. {
  1470. if (! isKeyReleasePartOfAutoRepeat (keyEvent))
  1471. {
  1472. updateKeyStates ((int) keyEvent.keycode, false);
  1473. KeySym sym;
  1474. {
  1475. ScopedXLock xlock (display);
  1476. sym = XkbKeycodeToKeysym (display, (::KeyCode) keyEvent.keycode, 0, 0);
  1477. }
  1478. auto oldMods = ModifierKeys::currentModifiers;
  1479. const bool keyDownChange = (sym != NoSymbol) && ! updateKeyModifiersFromSym (sym, false);
  1480. if (oldMods != ModifierKeys::currentModifiers)
  1481. handleModifierKeysChange();
  1482. if (keyDownChange)
  1483. handleKeyUpOrDown (false);
  1484. }
  1485. }
  1486. template <typename EventType>
  1487. Point<float> getMousePos (const EventType& e) noexcept
  1488. {
  1489. return Point<float> ((float) e.x, (float) e.y) / currentScaleFactor;
  1490. }
  1491. void handleWheelEvent (const XButtonPressedEvent& buttonPressEvent, float amount)
  1492. {
  1493. MouseWheelDetails wheel;
  1494. wheel.deltaX = 0.0f;
  1495. wheel.deltaY = amount;
  1496. wheel.isReversed = false;
  1497. wheel.isSmooth = false;
  1498. wheel.isInertial = false;
  1499. handleMouseWheel (MouseInputSource::InputSourceType::mouse, getMousePos (buttonPressEvent),
  1500. getEventTime (buttonPressEvent), wheel);
  1501. }
  1502. void handleButtonPressEvent (const XButtonPressedEvent& buttonPressEvent, int buttonModifierFlag)
  1503. {
  1504. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withFlags (buttonModifierFlag);
  1505. toFront (true);
  1506. handleMouseEvent (MouseInputSource::InputSourceType::mouse, getMousePos (buttonPressEvent), ModifierKeys::currentModifiers,
  1507. MouseInputSource::invalidPressure, MouseInputSource::invalidOrientation, getEventTime (buttonPressEvent), {});
  1508. }
  1509. void handleButtonPressEvent (const XButtonPressedEvent& buttonPressEvent)
  1510. {
  1511. updateKeyModifiers ((int) buttonPressEvent.state);
  1512. auto mapIndex = (uint32) (buttonPressEvent.button - Button1);
  1513. if (mapIndex < (uint32) numElementsInArray (pointerMap))
  1514. {
  1515. switch (pointerMap[mapIndex])
  1516. {
  1517. case Keys::WheelUp: handleWheelEvent (buttonPressEvent, 50.0f / 256.0f); break;
  1518. case Keys::WheelDown: handleWheelEvent (buttonPressEvent, -50.0f / 256.0f); break;
  1519. case Keys::LeftButton: handleButtonPressEvent (buttonPressEvent, ModifierKeys::leftButtonModifier); break;
  1520. case Keys::RightButton: handleButtonPressEvent (buttonPressEvent, ModifierKeys::rightButtonModifier); break;
  1521. case Keys::MiddleButton: handleButtonPressEvent (buttonPressEvent, ModifierKeys::middleButtonModifier); break;
  1522. default: break;
  1523. }
  1524. }
  1525. clearLastMousePos();
  1526. }
  1527. void handleButtonReleaseEvent (const XButtonReleasedEvent& buttonRelEvent)
  1528. {
  1529. updateKeyModifiers ((int) buttonRelEvent.state);
  1530. if (parentWindow != 0)
  1531. updateWindowBounds();
  1532. auto mapIndex = (uint32) (buttonRelEvent.button - Button1);
  1533. if (mapIndex < (uint32) numElementsInArray (pointerMap))
  1534. {
  1535. switch (pointerMap[mapIndex])
  1536. {
  1537. case Keys::LeftButton: ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutFlags (ModifierKeys::leftButtonModifier); break;
  1538. case Keys::RightButton: ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutFlags (ModifierKeys::rightButtonModifier); break;
  1539. case Keys::MiddleButton: ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withoutFlags (ModifierKeys::middleButtonModifier); break;
  1540. default: break;
  1541. }
  1542. }
  1543. if (dragState->dragging)
  1544. handleExternalDragButtonReleaseEvent();
  1545. handleMouseEvent (MouseInputSource::InputSourceType::mouse, getMousePos (buttonRelEvent), ModifierKeys::currentModifiers,
  1546. MouseInputSource::invalidPressure, MouseInputSource::invalidOrientation, getEventTime (buttonRelEvent));
  1547. clearLastMousePos();
  1548. }
  1549. void handleMotionNotifyEvent (const XPointerMovedEvent& movedEvent)
  1550. {
  1551. updateKeyModifiers ((int) movedEvent.state);
  1552. lastMousePos = Point<int> (movedEvent.x_root, movedEvent.y_root);
  1553. if (dragState->dragging)
  1554. handleExternalDragMotionNotify();
  1555. handleMouseEvent (MouseInputSource::InputSourceType::mouse, getMousePos (movedEvent), ModifierKeys::currentModifiers,
  1556. MouseInputSource::invalidPressure, MouseInputSource::invalidOrientation, getEventTime (movedEvent));
  1557. }
  1558. void handleEnterNotifyEvent (const XEnterWindowEvent& enterEvent)
  1559. {
  1560. if (parentWindow != 0)
  1561. updateWindowBounds();
  1562. clearLastMousePos();
  1563. if (! ModifierKeys::currentModifiers.isAnyMouseButtonDown())
  1564. {
  1565. updateKeyModifiers ((int) enterEvent.state);
  1566. handleMouseEvent (MouseInputSource::InputSourceType::mouse, getMousePos (enterEvent), ModifierKeys::currentModifiers,
  1567. MouseInputSource::invalidPressure, MouseInputSource::invalidOrientation, getEventTime (enterEvent));
  1568. }
  1569. }
  1570. void handleLeaveNotifyEvent (const XLeaveWindowEvent& leaveEvent)
  1571. {
  1572. // Suppress the normal leave if we've got a pointer grab, or if
  1573. // it's a bogus one caused by clicking a mouse button when running
  1574. // in a Window manager
  1575. if (((! ModifierKeys::currentModifiers.isAnyMouseButtonDown()) && leaveEvent.mode == NotifyNormal)
  1576. || leaveEvent.mode == NotifyUngrab)
  1577. {
  1578. updateKeyModifiers ((int) leaveEvent.state);
  1579. handleMouseEvent (MouseInputSource::InputSourceType::mouse, getMousePos (leaveEvent), ModifierKeys::currentModifiers,
  1580. MouseInputSource::invalidPressure, MouseInputSource::invalidOrientation, getEventTime (leaveEvent));
  1581. }
  1582. }
  1583. void handleFocusInEvent()
  1584. {
  1585. isActiveApplication = true;
  1586. if (isFocused() && ! focused)
  1587. {
  1588. focused = true;
  1589. handleFocusGain();
  1590. }
  1591. }
  1592. void handleFocusOutEvent()
  1593. {
  1594. if (! isFocused() && focused)
  1595. {
  1596. focused = false;
  1597. isActiveApplication = false;
  1598. handleFocusLoss();
  1599. }
  1600. }
  1601. void handleExposeEvent (XExposeEvent& exposeEvent)
  1602. {
  1603. // Batch together all pending expose events
  1604. XEvent nextEvent;
  1605. ScopedXLock xlock (display);
  1606. // if we have opengl contexts then just repaint them all
  1607. // regardless if this is really necessary
  1608. repaintOpenGLContexts();
  1609. if (exposeEvent.window != windowH)
  1610. {
  1611. Window child;
  1612. XTranslateCoordinates (display, exposeEvent.window, windowH,
  1613. exposeEvent.x, exposeEvent.y, &exposeEvent.x, &exposeEvent.y,
  1614. &child);
  1615. }
  1616. // exposeEvent is in local window local coordinates so do not convert with
  1617. // physicalToScaled, but rather use currentScaleFactor
  1618. repaint (Rectangle<int> (exposeEvent.x, exposeEvent.y,
  1619. exposeEvent.width, exposeEvent.height) / currentScaleFactor);
  1620. while (XEventsQueued (display, QueuedAfterFlush) > 0)
  1621. {
  1622. XPeekEvent (display, &nextEvent);
  1623. if (nextEvent.type != Expose || nextEvent.xany.window != exposeEvent.window)
  1624. break;
  1625. XNextEvent (display, &nextEvent);
  1626. auto& nextExposeEvent = (const XExposeEvent&) nextEvent.xexpose;
  1627. repaint (Rectangle<int> (nextExposeEvent.x, nextExposeEvent.y,
  1628. nextExposeEvent.width, nextExposeEvent.height) / currentScaleFactor);
  1629. }
  1630. }
  1631. void handleConfigureNotifyEvent (XConfigureEvent& confEvent)
  1632. {
  1633. updateWindowBounds();
  1634. updateBorderSize();
  1635. handleMovedOrResized();
  1636. // if the native title bar is dragged, need to tell any active menus, etc.
  1637. if ((styleFlags & windowHasTitleBar) != 0
  1638. && component.isCurrentlyBlockedByAnotherModalComponent())
  1639. {
  1640. if (auto* currentModalComp = Component::getCurrentlyModalComponent())
  1641. currentModalComp->inputAttemptWhenModal();
  1642. }
  1643. if (confEvent.window == windowH && confEvent.above != 0 && isFrontWindow())
  1644. handleBroughtToFront();
  1645. }
  1646. void handleGravityNotify()
  1647. {
  1648. updateWindowBounds();
  1649. updateBorderSize();
  1650. handleMovedOrResized();
  1651. }
  1652. void handleMappingNotify (XMappingEvent& mappingEvent)
  1653. {
  1654. if (mappingEvent.request != MappingPointer)
  1655. {
  1656. // Deal with modifier/keyboard mapping
  1657. ScopedXLock xlock (display);
  1658. XRefreshKeyboardMapping (&mappingEvent);
  1659. updateModifierMappings();
  1660. }
  1661. }
  1662. void handleClientMessageEvent (XClientMessageEvent& clientMsg, XEvent& event)
  1663. {
  1664. if (clientMsg.message_type == atoms->protocols && clientMsg.format == 32)
  1665. {
  1666. auto atom = (Atom) clientMsg.data.l[0];
  1667. if (atom == atoms->protocolList [Atoms::PING])
  1668. {
  1669. Window root = RootWindow (display, DefaultScreen (display));
  1670. clientMsg.window = root;
  1671. XSendEvent (display, root, False, NoEventMask, &event);
  1672. XFlush (display);
  1673. }
  1674. else if (atom == atoms->protocolList [Atoms::TAKE_FOCUS])
  1675. {
  1676. if ((getStyleFlags() & juce::ComponentPeer::windowIgnoresKeyPresses) == 0)
  1677. {
  1678. XWindowAttributes atts;
  1679. ScopedXLock xlock (display);
  1680. if (clientMsg.window != 0
  1681. && XGetWindowAttributes (display, clientMsg.window, &atts))
  1682. {
  1683. if (atts.map_state == IsViewable)
  1684. XSetInputFocus (display,
  1685. (clientMsg.window == windowH ? getFocusWindow()
  1686. : clientMsg.window),
  1687. RevertToParent,
  1688. (::Time) clientMsg.data.l[1]);
  1689. }
  1690. }
  1691. }
  1692. else if (atom == atoms->protocolList [Atoms::DELETE_WINDOW])
  1693. {
  1694. handleUserClosingWindow();
  1695. }
  1696. }
  1697. else if (clientMsg.message_type == atoms->XdndEnter)
  1698. {
  1699. handleDragAndDropEnter (clientMsg);
  1700. }
  1701. else if (clientMsg.message_type == atoms->XdndLeave)
  1702. {
  1703. handleDragExit (dragInfo);
  1704. resetDragAndDrop();
  1705. }
  1706. else if (clientMsg.message_type == atoms->XdndPosition)
  1707. {
  1708. handleDragAndDropPosition (clientMsg);
  1709. }
  1710. else if (clientMsg.message_type == atoms->XdndDrop)
  1711. {
  1712. handleDragAndDropDrop (clientMsg);
  1713. }
  1714. else if (clientMsg.message_type == atoms->XdndStatus)
  1715. {
  1716. handleExternalDragAndDropStatus (clientMsg);
  1717. }
  1718. else if (clientMsg.message_type == atoms->XdndFinished)
  1719. {
  1720. externalResetDragAndDrop();
  1721. }
  1722. else if (clientMsg.message_type == atoms->XembedMsgType && clientMsg.format == 32)
  1723. {
  1724. handleXEmbedMessage (clientMsg);
  1725. }
  1726. }
  1727. bool externalDragTextInit (const String& text, std::function<void()> cb)
  1728. {
  1729. if (dragState->dragging)
  1730. return false;
  1731. return externalDragInit (true, text, cb);
  1732. }
  1733. bool externalDragFileInit (const StringArray& files, bool /*canMoveFiles*/, std::function<void()> cb)
  1734. {
  1735. if (dragState->dragging)
  1736. return false;
  1737. StringArray uriList;
  1738. for (auto& f : files)
  1739. {
  1740. if (f.matchesWildcard ("?*://*", false))
  1741. uriList.add (f);
  1742. else
  1743. uriList.add ("file://" + f);
  1744. }
  1745. return externalDragInit (false, uriList.joinIntoString ("\r\n"), cb);
  1746. }
  1747. void handleXEmbedMessage (XClientMessageEvent& clientMsg)
  1748. {
  1749. switch (clientMsg.data.l[1])
  1750. {
  1751. case XEMBED_EMBEDDED_NOTIFY:
  1752. parentWindow = (::Window) clientMsg.data.l[3];
  1753. updateWindowBounds();
  1754. component.setBounds (bounds);
  1755. break;
  1756. case XEMBED_FOCUS_IN:
  1757. handleFocusInEvent();
  1758. break;
  1759. case XEMBED_FOCUS_OUT:
  1760. handleFocusOutEvent();
  1761. break;
  1762. default:
  1763. break;
  1764. }
  1765. }
  1766. //==============================================================================
  1767. void showMouseCursor (Cursor cursor) noexcept
  1768. {
  1769. ScopedXLock xlock (display);
  1770. XDefineCursor (display, windowH, cursor);
  1771. }
  1772. //==============================================================================
  1773. double getPlatformScaleFactor() const noexcept override
  1774. {
  1775. return currentScaleFactor;
  1776. }
  1777. //==============================================================================
  1778. void addOpenGLRepaintListener (Component* dummy)
  1779. {
  1780. if (dummy != nullptr)
  1781. glRepaintListeners.addIfNotAlreadyThere (dummy);
  1782. }
  1783. void removeOpenGLRepaintListener (Component* dummy)
  1784. {
  1785. if (dummy != nullptr)
  1786. glRepaintListeners.removeAllInstancesOf (dummy);
  1787. }
  1788. void repaintOpenGLContexts()
  1789. {
  1790. for (int i = 0; i < glRepaintListeners.size(); ++i)
  1791. if (auto* c = glRepaintListeners [i])
  1792. c->handleCommandMessage (0);
  1793. }
  1794. //==============================================================================
  1795. unsigned long createKeyProxy()
  1796. {
  1797. jassert (keyProxy == 0 && windowH != 0);
  1798. if (keyProxy == 0 && windowH != 0)
  1799. {
  1800. XSetWindowAttributes swa;
  1801. swa.event_mask = KeyPressMask | KeyReleaseMask | FocusChangeMask;
  1802. keyProxy = XCreateWindow (display, windowH,
  1803. -1, -1, 1, 1, 0, 0,
  1804. InputOnly, CopyFromParent,
  1805. CWEventMask,
  1806. &swa);
  1807. XMapWindow (display, keyProxy);
  1808. XSaveContext (display, (XID) keyProxy, windowHandleXContext, (XPointer) this);
  1809. }
  1810. return keyProxy;
  1811. }
  1812. void deleteKeyProxy()
  1813. {
  1814. jassert (keyProxy != 0);
  1815. if (keyProxy != 0)
  1816. {
  1817. XPointer handlePointer;
  1818. if (! XFindContext (display, (XID) keyProxy, windowHandleXContext, &handlePointer))
  1819. XDeleteContext (display, (XID) keyProxy, windowHandleXContext);
  1820. XDestroyWindow (display, keyProxy);
  1821. XSync (display, false);
  1822. XEvent event;
  1823. while (XCheckWindowEvent (display, keyProxy, getAllEventsMask(), &event) == True)
  1824. {}
  1825. keyProxy = 0;
  1826. }
  1827. }
  1828. //==============================================================================
  1829. bool dontRepaint;
  1830. static bool isActiveApplication;
  1831. private:
  1832. //==============================================================================
  1833. class LinuxRepaintManager : public Timer
  1834. {
  1835. public:
  1836. LinuxRepaintManager (LinuxComponentPeer& p, ::Display* d)
  1837. : peer (p), display (d)
  1838. {
  1839. #if JUCE_USE_XSHM
  1840. useARGBImagesForRendering = XSHMHelpers::isShmAvailable (display);
  1841. if (useARGBImagesForRendering)
  1842. {
  1843. ScopedXLock xlock (display);
  1844. XShmSegmentInfo segmentinfo;
  1845. auto testImage = XShmCreateImage (display, DefaultVisual (display, DefaultScreen (display)),
  1846. 24, ZPixmap, nullptr, &segmentinfo, 64, 64);
  1847. useARGBImagesForRendering = (testImage->bits_per_pixel == 32);
  1848. XDestroyImage (testImage);
  1849. }
  1850. #endif
  1851. }
  1852. void timerCallback() override
  1853. {
  1854. #if JUCE_USE_XSHM
  1855. if (shmPaintsPending != 0)
  1856. {
  1857. ScopedXLock xlock (display);
  1858. XEvent evt;
  1859. while (XCheckTypedWindowEvent (display, peer.windowH, peer.shmCompletionEvent, &evt))
  1860. --shmPaintsPending;
  1861. }
  1862. if (shmPaintsPending != 0)
  1863. return;
  1864. #endif
  1865. if (! regionsNeedingRepaint.isEmpty())
  1866. {
  1867. stopTimer();
  1868. performAnyPendingRepaintsNow();
  1869. }
  1870. else if (Time::getApproximateMillisecondCounter() > lastTimeImageUsed + 3000)
  1871. {
  1872. stopTimer();
  1873. image = Image();
  1874. }
  1875. }
  1876. void repaint (Rectangle<int> area)
  1877. {
  1878. if (! isTimerRunning())
  1879. startTimer (repaintTimerPeriod);
  1880. regionsNeedingRepaint.add (area * peer.currentScaleFactor);
  1881. }
  1882. void performAnyPendingRepaintsNow()
  1883. {
  1884. #if JUCE_USE_XSHM
  1885. if (shmPaintsPending != 0)
  1886. {
  1887. startTimer (repaintTimerPeriod);
  1888. return;
  1889. }
  1890. #endif
  1891. auto originalRepaintRegion = regionsNeedingRepaint;
  1892. regionsNeedingRepaint.clear();
  1893. auto totalArea = originalRepaintRegion.getBounds();
  1894. if (! totalArea.isEmpty())
  1895. {
  1896. if (image.isNull() || image.getWidth() < totalArea.getWidth()
  1897. || image.getHeight() < totalArea.getHeight())
  1898. {
  1899. #if JUCE_USE_XSHM
  1900. image = Image (new XBitmapImage (display, useARGBImagesForRendering ? Image::ARGB
  1901. : Image::RGB,
  1902. #else
  1903. image = Image (new XBitmapImage (display, Image::RGB,
  1904. #endif
  1905. (totalArea.getWidth() + 31) & ~31,
  1906. (totalArea.getHeight() + 31) & ~31,
  1907. false, (unsigned int) peer.depth, peer.visual));
  1908. }
  1909. startTimer (repaintTimerPeriod);
  1910. RectangleList<int> adjustedList (originalRepaintRegion);
  1911. adjustedList.offsetAll (-totalArea.getX(), -totalArea.getY());
  1912. if (peer.depth == 32)
  1913. for (auto& i : originalRepaintRegion)
  1914. image.clear (i - totalArea.getPosition());
  1915. {
  1916. auto context = peer.getComponent().getLookAndFeel()
  1917. .createGraphicsContext (image, -totalArea.getPosition(), adjustedList);
  1918. context->addTransform (AffineTransform::scale ((float) peer.currentScaleFactor));
  1919. peer.handlePaint (*context);
  1920. }
  1921. for (auto& i : originalRepaintRegion)
  1922. {
  1923. auto* xbitmap = static_cast<XBitmapImage*> (image.getPixelData());
  1924. #if JUCE_USE_XSHM
  1925. if (xbitmap->isUsingXShm())
  1926. ++shmPaintsPending;
  1927. #endif
  1928. xbitmap->blitToWindow (peer.windowH,
  1929. i.getX(), i.getY(),
  1930. (unsigned int) i.getWidth(),
  1931. (unsigned int) i.getHeight(),
  1932. i.getX() - totalArea.getX(), i.getY() - totalArea.getY());
  1933. }
  1934. }
  1935. lastTimeImageUsed = Time::getApproximateMillisecondCounter();
  1936. startTimer (repaintTimerPeriod);
  1937. }
  1938. #if JUCE_USE_XSHM
  1939. void notifyPaintCompleted() noexcept { --shmPaintsPending; }
  1940. #endif
  1941. private:
  1942. enum { repaintTimerPeriod = 1000 / 100 };
  1943. LinuxComponentPeer& peer;
  1944. Image image;
  1945. uint32 lastTimeImageUsed = 0;
  1946. RectangleList<int> regionsNeedingRepaint;
  1947. ::Display* display;
  1948. #if JUCE_USE_XSHM
  1949. bool useARGBImagesForRendering;
  1950. int shmPaintsPending = 0;
  1951. #endif
  1952. JUCE_DECLARE_NON_COPYABLE (LinuxRepaintManager)
  1953. };
  1954. std::unique_ptr<Atoms> atoms;
  1955. std::unique_ptr<LinuxRepaintManager> repainter;
  1956. friend class LinuxRepaintManager;
  1957. Window windowH = {}, parentWindow = {}, keyProxy = {};
  1958. Rectangle<int> bounds;
  1959. Point<int> parentScreenPosition;
  1960. Image taskbarImage;
  1961. bool fullScreen = false, mapped = false, focused = false;
  1962. Visual* visual = {};
  1963. int depth = 0;
  1964. BorderSize<int> windowBorder;
  1965. bool isAlwaysOnTop;
  1966. double currentScaleFactor = 1.0;
  1967. Array<Component*> glRepaintListeners;
  1968. enum { KeyPressEventType = 2 };
  1969. static ::Display* display;
  1970. #if JUCE_USE_XSHM
  1971. int shmCompletionEvent = 0;
  1972. #endif
  1973. struct MotifWmHints
  1974. {
  1975. unsigned long flags;
  1976. unsigned long functions;
  1977. unsigned long decorations;
  1978. long input_mode;
  1979. unsigned long status;
  1980. };
  1981. static void updateKeyStates (int keycode, bool press) noexcept
  1982. {
  1983. const int keybyte = keycode >> 3;
  1984. const int keybit = (1 << (keycode & 7));
  1985. if (press)
  1986. Keys::keyStates[keybyte] = static_cast<char> (Keys::keyStates[keybyte] | keybit);
  1987. else
  1988. Keys::keyStates[keybyte] = static_cast<char> (Keys::keyStates[keybyte] & ~keybit);
  1989. }
  1990. static void updateKeyModifiers (int status) noexcept
  1991. {
  1992. int keyMods = 0;
  1993. if ((status & ShiftMask) != 0) keyMods |= ModifierKeys::shiftModifier;
  1994. if ((status & ControlMask) != 0) keyMods |= ModifierKeys::ctrlModifier;
  1995. if ((status & Keys::AltMask) != 0) keyMods |= ModifierKeys::altModifier;
  1996. ModifierKeys::currentModifiers = ModifierKeys::currentModifiers.withOnlyMouseButtons().withFlags (keyMods);
  1997. Keys::numLock = ((status & Keys::NumLockMask) != 0);
  1998. Keys::capsLock = ((status & LockMask) != 0);
  1999. }
  2000. static bool updateKeyModifiersFromSym (KeySym sym, bool press) noexcept
  2001. {
  2002. int modifier = 0;
  2003. bool isModifier = true;
  2004. switch (sym)
  2005. {
  2006. case XK_Shift_L:
  2007. case XK_Shift_R: modifier = ModifierKeys::shiftModifier; break;
  2008. case XK_Control_L:
  2009. case XK_Control_R: modifier = ModifierKeys::ctrlModifier; break;
  2010. case XK_Alt_L:
  2011. case XK_Alt_R: modifier = ModifierKeys::altModifier; break;
  2012. case XK_Num_Lock:
  2013. if (press)
  2014. Keys::numLock = ! Keys::numLock;
  2015. break;
  2016. case XK_Caps_Lock:
  2017. if (press)
  2018. Keys::capsLock = ! Keys::capsLock;
  2019. break;
  2020. case XK_Scroll_Lock:
  2021. break;
  2022. default:
  2023. isModifier = false;
  2024. break;
  2025. }
  2026. ModifierKeys::currentModifiers = press ? ModifierKeys::currentModifiers.withFlags (modifier)
  2027. : ModifierKeys::currentModifiers.withoutFlags (modifier);
  2028. return isModifier;
  2029. }
  2030. // Alt and Num lock are not defined by standard X
  2031. // modifier constants: check what they're mapped to
  2032. static void updateModifierMappings() noexcept
  2033. {
  2034. ScopedXLock xlock (display);
  2035. int altLeftCode = XKeysymToKeycode (display, XK_Alt_L);
  2036. int numLockCode = XKeysymToKeycode (display, XK_Num_Lock);
  2037. Keys::AltMask = 0;
  2038. Keys::NumLockMask = 0;
  2039. if (auto* mapping = XGetModifierMapping (display))
  2040. {
  2041. for (int modifierIdx = 0; modifierIdx < 8; ++modifierIdx)
  2042. {
  2043. for (int keyIndex = 0; keyIndex < mapping->max_keypermod; ++keyIndex)
  2044. {
  2045. auto key = mapping->modifiermap[(modifierIdx * mapping->max_keypermod) + keyIndex];
  2046. if (key == altLeftCode)
  2047. Keys::AltMask = 1 << modifierIdx;
  2048. else if (key == numLockCode)
  2049. Keys::NumLockMask = 1 << modifierIdx;
  2050. }
  2051. }
  2052. XFreeModifiermap (mapping);
  2053. }
  2054. }
  2055. //==============================================================================
  2056. static void xchangeProperty (Window wndH, Atom property, Atom type, int format, const void* data, int numElements)
  2057. {
  2058. XChangeProperty (display, wndH, property, type, format, PropModeReplace, (const unsigned char*) data, numElements);
  2059. }
  2060. void removeWindowDecorations (Window wndH)
  2061. {
  2062. Atom hints = Atoms::getIfExists (display, "_MOTIF_WM_HINTS");
  2063. if (hints != None)
  2064. {
  2065. MotifWmHints motifHints;
  2066. zerostruct (motifHints);
  2067. motifHints.flags = 2; /* MWM_HINTS_DECORATIONS */
  2068. motifHints.decorations = 0;
  2069. ScopedXLock xlock (display);
  2070. xchangeProperty (wndH, hints, hints, 32, &motifHints, 4);
  2071. }
  2072. hints = Atoms::getIfExists (display, "_WIN_HINTS");
  2073. if (hints != None)
  2074. {
  2075. long gnomeHints = 0;
  2076. ScopedXLock xlock (display);
  2077. xchangeProperty (wndH, hints, hints, 32, &gnomeHints, 1);
  2078. }
  2079. hints = Atoms::getIfExists (display, "KWM_WIN_DECORATION");
  2080. if (hints != None)
  2081. {
  2082. long kwmHints = 2; /*KDE_tinyDecoration*/
  2083. ScopedXLock xlock (display);
  2084. xchangeProperty (wndH, hints, hints, 32, &kwmHints, 1);
  2085. }
  2086. hints = Atoms::getIfExists (display, "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE");
  2087. if (hints != None)
  2088. {
  2089. ScopedXLock xlock (display);
  2090. xchangeProperty (wndH, atoms->windowType, XA_ATOM, 32, &hints, 1);
  2091. }
  2092. }
  2093. void addWindowButtons (Window wndH)
  2094. {
  2095. ScopedXLock xlock (display);
  2096. Atom hints = Atoms::getIfExists (display, "_MOTIF_WM_HINTS");
  2097. if (hints != None)
  2098. {
  2099. MotifWmHints motifHints;
  2100. zerostruct (motifHints);
  2101. motifHints.flags = 1 | 2; /* MWM_HINTS_FUNCTIONS | MWM_HINTS_DECORATIONS */
  2102. motifHints.decorations = 2 /* MWM_DECOR_BORDER */ | 8 /* MWM_DECOR_TITLE */ | 16; /* MWM_DECOR_MENU */
  2103. motifHints.functions = 4 /* MWM_FUNC_MOVE */;
  2104. if ((styleFlags & windowHasCloseButton) != 0)
  2105. motifHints.functions |= 32; /* MWM_FUNC_CLOSE */
  2106. if ((styleFlags & windowHasMinimiseButton) != 0)
  2107. {
  2108. motifHints.functions |= 8; /* MWM_FUNC_MINIMIZE */
  2109. motifHints.decorations |= 0x20; /* MWM_DECOR_MINIMIZE */
  2110. }
  2111. if ((styleFlags & windowHasMaximiseButton) != 0)
  2112. {
  2113. motifHints.functions |= 0x10; /* MWM_FUNC_MAXIMIZE */
  2114. motifHints.decorations |= 0x40; /* MWM_DECOR_MAXIMIZE */
  2115. }
  2116. if ((styleFlags & windowIsResizable) != 0)
  2117. {
  2118. motifHints.functions |= 2; /* MWM_FUNC_RESIZE */
  2119. motifHints.decorations |= 0x4; /* MWM_DECOR_RESIZEH */
  2120. }
  2121. xchangeProperty (wndH, hints, hints, 32, &motifHints, 5);
  2122. }
  2123. hints = Atoms::getIfExists (display, "_NET_WM_ALLOWED_ACTIONS");
  2124. if (hints != None)
  2125. {
  2126. Atom netHints [6];
  2127. int num = 0;
  2128. if ((styleFlags & windowIsResizable) != 0)
  2129. netHints [num++] = Atoms::getIfExists (display, "_NET_WM_ACTION_RESIZE");
  2130. if ((styleFlags & windowHasMaximiseButton) != 0)
  2131. netHints [num++] = Atoms::getIfExists (display, "_NET_WM_ACTION_FULLSCREEN");
  2132. if ((styleFlags & windowHasMinimiseButton) != 0)
  2133. netHints [num++] = Atoms::getIfExists (display, "_NET_WM_ACTION_MINIMIZE");
  2134. if ((styleFlags & windowHasCloseButton) != 0)
  2135. netHints [num++] = Atoms::getIfExists (display, "_NET_WM_ACTION_CLOSE");
  2136. xchangeProperty (wndH, hints, XA_ATOM, 32, &netHints, num);
  2137. }
  2138. }
  2139. void setWindowType()
  2140. {
  2141. Atom netHints [2];
  2142. if ((styleFlags & windowIsTemporary) != 0
  2143. || ((styleFlags & windowHasDropShadow) == 0 && Desktop::canUseSemiTransparentWindows()))
  2144. netHints [0] = Atoms::getIfExists (display, "_NET_WM_WINDOW_TYPE_COMBO");
  2145. else
  2146. netHints [0] = Atoms::getIfExists (display, "_NET_WM_WINDOW_TYPE_NORMAL");
  2147. xchangeProperty (windowH, atoms->windowType, XA_ATOM, 32, &netHints, 1);
  2148. int numHints = 0;
  2149. if ((styleFlags & windowAppearsOnTaskbar) == 0)
  2150. netHints [numHints++] = Atoms::getIfExists (display, "_NET_WM_STATE_SKIP_TASKBAR");
  2151. if (component.isAlwaysOnTop())
  2152. netHints [numHints++] = Atoms::getIfExists (display, "_NET_WM_STATE_ABOVE");
  2153. if (numHints > 0)
  2154. xchangeProperty (windowH, atoms->windowState, XA_ATOM, 32, &netHints, numHints);
  2155. }
  2156. void createWindow (Window parentToAddTo)
  2157. {
  2158. ScopedXLock xlock (display);
  2159. resetDragAndDrop();
  2160. // Get defaults for various properties
  2161. const int screen = DefaultScreen (display);
  2162. Window root = RootWindow (display, screen);
  2163. parentWindow = parentToAddTo;
  2164. // Try to obtain a 32-bit visual or fallback to 24 or 16
  2165. visual = Visuals::findVisualFormat (display, (styleFlags & windowIsSemiTransparent) ? 32 : 24, depth);
  2166. if (visual == nullptr)
  2167. {
  2168. Logger::outputDebugString ("ERROR: System doesn't support 32, 24 or 16 bit RGB display.\n");
  2169. Process::terminate();
  2170. }
  2171. // Create and install a colormap suitable fr our visual
  2172. Colormap colormap = XCreateColormap (display, root, visual, AllocNone);
  2173. XInstallColormap (display, colormap);
  2174. // Set up the window attributes
  2175. XSetWindowAttributes swa;
  2176. swa.border_pixel = 0;
  2177. swa.background_pixmap = None;
  2178. swa.colormap = colormap;
  2179. swa.override_redirect = ((styleFlags & windowIsTemporary) != 0) ? True : False;
  2180. swa.event_mask = getAllEventsMask();
  2181. windowH = XCreateWindow (display, parentToAddTo != 0 ? parentToAddTo : root,
  2182. 0, 0, 1, 1,
  2183. 0, depth, InputOutput, visual,
  2184. CWBorderPixel | CWColormap | CWBackPixmap | CWEventMask | CWOverrideRedirect,
  2185. &swa);
  2186. // Set the window context to identify the window handle object
  2187. if (XSaveContext (display, (XID) windowH, windowHandleXContext, (XPointer) this))
  2188. {
  2189. // Failed
  2190. jassertfalse;
  2191. Logger::outputDebugString ("Failed to create context information for window.\n");
  2192. XDestroyWindow (display, windowH);
  2193. windowH = 0;
  2194. return;
  2195. }
  2196. // Set window manager hints
  2197. XWMHints* wmHints = XAllocWMHints();
  2198. wmHints->flags = InputHint | StateHint;
  2199. wmHints->input = True; // Locally active input model
  2200. wmHints->initial_state = NormalState;
  2201. XSetWMHints (display, windowH, wmHints);
  2202. XFree (wmHints);
  2203. // Set the window type
  2204. setWindowType();
  2205. // Define decoration
  2206. if ((styleFlags & windowHasTitleBar) == 0)
  2207. removeWindowDecorations (windowH);
  2208. else
  2209. addWindowButtons (windowH);
  2210. setTitle (component.getName());
  2211. // Associate the PID, allowing to be shut down when something goes wrong
  2212. unsigned long pid = (unsigned long) getpid();
  2213. xchangeProperty (windowH, atoms->pid, XA_CARDINAL, 32, &pid, 1);
  2214. // Set window manager protocols
  2215. xchangeProperty (windowH, atoms->protocols, XA_ATOM, 32, atoms->protocolList, 2);
  2216. // Set drag and drop flags
  2217. xchangeProperty (windowH, atoms->XdndTypeList, XA_ATOM, 32, atoms->allowedMimeTypes, numElementsInArray (atoms->allowedMimeTypes));
  2218. xchangeProperty (windowH, atoms->XdndActionList, XA_ATOM, 32, atoms->allowedActions, numElementsInArray (atoms->allowedActions));
  2219. xchangeProperty (windowH, atoms->XdndActionDescription, XA_STRING, 8, "", 0);
  2220. xchangeProperty (windowH, atoms->XdndAware, XA_ATOM, 32, &atoms->DndVersion, 1);
  2221. unsigned long info[2] = { 0, 1 };
  2222. xchangeProperty (windowH, atoms->XembedInfo, atoms->XembedInfo, 32, (unsigned char*) info, 2);
  2223. initialisePointerMap();
  2224. updateModifierMappings();
  2225. #if JUCE_USE_XSHM
  2226. if (XSHMHelpers::isShmAvailable (display))
  2227. shmCompletionEvent = XShmGetEventBase (display) + ShmCompletion;
  2228. #endif
  2229. }
  2230. void destroyWindow()
  2231. {
  2232. ScopedXLock xlock (display);
  2233. XPointer handlePointer;
  2234. if (keyProxy != 0)
  2235. deleteKeyProxy();
  2236. if (! XFindContext (display, (XID) windowH, windowHandleXContext, &handlePointer))
  2237. XDeleteContext (display, (XID) windowH, windowHandleXContext);
  2238. XDestroyWindow (display, windowH);
  2239. // Wait for it to complete and then remove any events for this
  2240. // window from the event queue.
  2241. XSync (display, false);
  2242. XEvent event;
  2243. while (XCheckWindowEvent (display, windowH, getAllEventsMask(), &event) == True)
  2244. {}
  2245. }
  2246. int getAllEventsMask() const noexcept
  2247. {
  2248. return NoEventMask | KeyPressMask | KeyReleaseMask
  2249. | EnterWindowMask | LeaveWindowMask | PointerMotionMask | KeymapStateMask
  2250. | ExposureMask | StructureNotifyMask | FocusChangeMask
  2251. | ((styleFlags & windowIgnoresMouseClicks) != 0 ? 0 : (ButtonPressMask | ButtonReleaseMask));
  2252. }
  2253. template <typename EventType>
  2254. static int64 getEventTime (const EventType& t)
  2255. {
  2256. return getEventTime (t.time);
  2257. }
  2258. static int64 getEventTime (::Time t)
  2259. {
  2260. static int64 eventTimeOffset = 0x12345678;
  2261. auto thisMessageTime = (int64) t;
  2262. if (eventTimeOffset == 0x12345678)
  2263. eventTimeOffset = Time::currentTimeMillis() - thisMessageTime;
  2264. return eventTimeOffset + thisMessageTime;
  2265. }
  2266. long getUserTime() const
  2267. {
  2268. GetXProperty prop (display, windowH, atoms->userTime, 0, 65536, false, XA_CARDINAL);
  2269. if (! prop.success)
  2270. return 0;
  2271. long result;
  2272. memcpy (&result, prop.data, sizeof (long));
  2273. return result;
  2274. }
  2275. void updateBorderSize()
  2276. {
  2277. if ((styleFlags & windowHasTitleBar) == 0)
  2278. {
  2279. windowBorder = BorderSize<int> (0);
  2280. }
  2281. else if (windowBorder.getTopAndBottom() == 0 && windowBorder.getLeftAndRight() == 0)
  2282. {
  2283. ScopedXLock xlock (display);
  2284. Atom hints = Atoms::getIfExists (display, "_NET_FRAME_EXTENTS");
  2285. if (hints != None)
  2286. {
  2287. GetXProperty prop (display, windowH, hints, 0, 4, false, XA_CARDINAL);
  2288. if (prop.success && prop.actualFormat == 32)
  2289. {
  2290. auto data = prop.data;
  2291. std::array<unsigned long, 4> sizes;
  2292. for (auto& size : sizes)
  2293. {
  2294. memcpy (&size, data, sizeof (unsigned long));
  2295. data += sizeof (unsigned long);
  2296. }
  2297. windowBorder = BorderSize<int> ((int) sizes[2], (int) sizes[0],
  2298. (int) sizes[3], (int) sizes[1]);
  2299. }
  2300. }
  2301. }
  2302. }
  2303. void updateWindowBounds()
  2304. {
  2305. jassert (windowH != 0);
  2306. if (windowH != 0)
  2307. {
  2308. Window root, child;
  2309. int wx = 0, wy = 0;
  2310. unsigned int ww = 0, wh = 0, bw, bitDepth;
  2311. ScopedXLock xlock (display);
  2312. if (XGetGeometry (display, (::Drawable) windowH, &root, &wx, &wy, &ww, &wh, &bw, &bitDepth))
  2313. {
  2314. int rootX = 0, rootY = 0;
  2315. if (! XTranslateCoordinates (display, windowH, root, 0, 0, &rootX, &rootY, &child))
  2316. rootX = rootY = 0;
  2317. if (parentWindow == 0)
  2318. {
  2319. wx = rootX;
  2320. wy = rootY;
  2321. }
  2322. else
  2323. {
  2324. parentScreenPosition = Desktop::getInstance().getDisplays().physicalToLogical (Point<int> (rootX, rootY));
  2325. }
  2326. }
  2327. Rectangle<int> physicalBounds (wx, wy, (int) ww, (int) wh);
  2328. updateScaleFactorFromNewBounds (physicalBounds, true);
  2329. bounds = (parentWindow == 0 ? Desktop::getInstance().getDisplays().physicalToLogical (physicalBounds)
  2330. : physicalBounds / currentScaleFactor);
  2331. }
  2332. }
  2333. //==============================================================================
  2334. struct DragState
  2335. {
  2336. DragState (::Display* d)
  2337. {
  2338. if (isText)
  2339. allowedTypes.add (Atoms::getCreating (d, "text/plain"));
  2340. else
  2341. allowedTypes.add (Atoms::getCreating (d, "text/uri-list"));
  2342. }
  2343. bool isText = false;
  2344. bool dragging = false; // currently performing outgoing external dnd as Xdnd source, have grabbed mouse
  2345. bool expectingStatus = false; // XdndPosition sent, waiting for XdndStatus
  2346. bool canDrop = false; // target window signals it will accept the drop
  2347. Window targetWindow = None; // potential drop target
  2348. int xdndVersion = -1; // negotiated version with target
  2349. Rectangle<int> silentRect;
  2350. String textOrFiles;
  2351. Array<Atom> allowedTypes;
  2352. std::function<void()> completionCallback;
  2353. };
  2354. //==============================================================================
  2355. void resetDragAndDrop()
  2356. {
  2357. dragInfo.clear();
  2358. dragInfo.position = Point<int> (-1, -1);
  2359. dragAndDropCurrentMimeType = 0;
  2360. dragAndDropSourceWindow = 0;
  2361. srcMimeTypeAtomList.clear();
  2362. finishAfterDropDataReceived = false;
  2363. }
  2364. void resetExternalDragState()
  2365. {
  2366. dragState.reset (new DragState (display));
  2367. }
  2368. void sendDragAndDropMessage (XClientMessageEvent& msg)
  2369. {
  2370. msg.type = ClientMessage;
  2371. msg.display = display;
  2372. msg.window = dragAndDropSourceWindow;
  2373. msg.format = 32;
  2374. msg.data.l[0] = (long) windowH;
  2375. ScopedXLock xlock (display);
  2376. XSendEvent (display, dragAndDropSourceWindow, False, 0, (XEvent*) &msg);
  2377. }
  2378. bool sendExternalDragAndDropMessage (XClientMessageEvent& msg, Window targetWindow)
  2379. {
  2380. msg.type = ClientMessage;
  2381. msg.display = display;
  2382. msg.window = targetWindow;
  2383. msg.format = 32;
  2384. msg.data.l[0] = (long) windowH;
  2385. ScopedXLock xlock (display);
  2386. return XSendEvent (display, targetWindow, False, 0, (XEvent*) &msg) != 0;
  2387. }
  2388. void sendExternalDragAndDropDrop (Window targetWindow)
  2389. {
  2390. XClientMessageEvent msg;
  2391. zerostruct (msg);
  2392. msg.message_type = atoms->XdndDrop;
  2393. msg.data.l[2] = CurrentTime;
  2394. sendExternalDragAndDropMessage (msg, targetWindow);
  2395. }
  2396. void sendExternalDragAndDropEnter (Window targetWindow)
  2397. {
  2398. XClientMessageEvent msg;
  2399. zerostruct (msg);
  2400. msg.message_type = atoms->XdndEnter;
  2401. msg.data.l[1] = (dragState->xdndVersion << 24);
  2402. for (int i = 0; i < 3; ++i)
  2403. msg.data.l[i + 2] = (long) dragState->allowedTypes[i];
  2404. sendExternalDragAndDropMessage (msg, targetWindow);
  2405. }
  2406. void sendExternalDragAndDropPosition (Window targetWindow)
  2407. {
  2408. XClientMessageEvent msg;
  2409. zerostruct (msg);
  2410. msg.message_type = atoms->XdndPosition;
  2411. Point<int> mousePos (Desktop::getInstance().getMousePosition());
  2412. if (dragState->silentRect.contains (mousePos)) // we've been asked to keep silent
  2413. return;
  2414. auto& displays = Desktop::getInstance().getDisplays();
  2415. mousePos = displays.logicalToPhysical (mousePos);
  2416. msg.data.l[1] = 0;
  2417. msg.data.l[2] = (mousePos.x << 16) | mousePos.y;
  2418. msg.data.l[3] = CurrentTime;
  2419. msg.data.l[4] = (long) atoms->XdndActionCopy; // this is all JUCE currently supports
  2420. dragState->expectingStatus = sendExternalDragAndDropMessage (msg, targetWindow);
  2421. }
  2422. void sendDragAndDropStatus (bool acceptDrop, Atom dropAction)
  2423. {
  2424. XClientMessageEvent msg;
  2425. zerostruct (msg);
  2426. msg.message_type = atoms->XdndStatus;
  2427. msg.data.l[1] = (acceptDrop ? 1 : 0) | 2; // 2 indicates that we want to receive position messages
  2428. msg.data.l[4] = (long) dropAction;
  2429. sendDragAndDropMessage (msg);
  2430. }
  2431. void sendExternalDragAndDropLeave (Window targetWindow)
  2432. {
  2433. XClientMessageEvent msg;
  2434. zerostruct (msg);
  2435. msg.message_type = atoms->XdndLeave;
  2436. sendExternalDragAndDropMessage (msg, targetWindow);
  2437. }
  2438. void sendDragAndDropFinish()
  2439. {
  2440. XClientMessageEvent msg;
  2441. zerostruct (msg);
  2442. msg.message_type = atoms->XdndFinished;
  2443. sendDragAndDropMessage (msg);
  2444. }
  2445. void handleExternalSelectionClear()
  2446. {
  2447. if (dragState->dragging)
  2448. externalResetDragAndDrop();
  2449. }
  2450. void handleExternalSelectionRequest (const XEvent& evt)
  2451. {
  2452. Atom targetType = evt.xselectionrequest.target;
  2453. XEvent s;
  2454. s.xselection.type = SelectionNotify;
  2455. s.xselection.requestor = evt.xselectionrequest.requestor;
  2456. s.xselection.selection = evt.xselectionrequest.selection;
  2457. s.xselection.target = targetType;
  2458. s.xselection.property = None;
  2459. s.xselection.time = evt.xselectionrequest.time;
  2460. if (dragState->allowedTypes.contains (targetType))
  2461. {
  2462. s.xselection.property = evt.xselectionrequest.property;
  2463. xchangeProperty (evt.xselectionrequest.requestor,
  2464. evt.xselectionrequest.property,
  2465. targetType, 8,
  2466. dragState->textOrFiles.toRawUTF8(),
  2467. (int) dragState->textOrFiles.getNumBytesAsUTF8());
  2468. }
  2469. XSendEvent (display, evt.xselectionrequest.requestor, True, 0, &s);
  2470. }
  2471. void handleExternalDragAndDropStatus (const XClientMessageEvent& clientMsg)
  2472. {
  2473. if (dragState->expectingStatus)
  2474. {
  2475. dragState->expectingStatus = false;
  2476. dragState->canDrop = false;
  2477. dragState->silentRect = Rectangle<int>();
  2478. if ((clientMsg.data.l[1] & 1) != 0
  2479. && ((Atom) clientMsg.data.l[4] == atoms->XdndActionCopy
  2480. || (Atom) clientMsg.data.l[4] == atoms->XdndActionPrivate))
  2481. {
  2482. if ((clientMsg.data.l[1] & 2) == 0) // target requests silent rectangle
  2483. dragState->silentRect.setBounds ((int) clientMsg.data.l[2] >> 16,
  2484. (int) clientMsg.data.l[2] & 0xffff,
  2485. (int) clientMsg.data.l[3] >> 16,
  2486. (int) clientMsg.data.l[3] & 0xffff);
  2487. dragState->canDrop = true;
  2488. }
  2489. }
  2490. }
  2491. void handleExternalDragButtonReleaseEvent()
  2492. {
  2493. if (dragState->dragging)
  2494. XUngrabPointer (display, CurrentTime);
  2495. if (dragState->canDrop)
  2496. {
  2497. sendExternalDragAndDropDrop (dragState->targetWindow);
  2498. }
  2499. else
  2500. {
  2501. sendExternalDragAndDropLeave (dragState->targetWindow);
  2502. externalResetDragAndDrop();
  2503. }
  2504. }
  2505. void handleExternalDragMotionNotify()
  2506. {
  2507. Window targetWindow = externalFindDragTargetWindow (RootWindow (display, DefaultScreen (display)));
  2508. if (dragState->targetWindow != targetWindow)
  2509. {
  2510. if (dragState->targetWindow != None)
  2511. sendExternalDragAndDropLeave (dragState->targetWindow);
  2512. dragState->canDrop = false;
  2513. dragState->silentRect = Rectangle<int>();
  2514. if (targetWindow == None)
  2515. return;
  2516. dragState->xdndVersion = getDnDVersionForWindow (targetWindow);
  2517. if (dragState->xdndVersion == -1)
  2518. return;
  2519. sendExternalDragAndDropEnter (targetWindow);
  2520. dragState->targetWindow = targetWindow;
  2521. }
  2522. if (! dragState->expectingStatus)
  2523. sendExternalDragAndDropPosition (targetWindow);
  2524. }
  2525. void handleDragAndDropPosition (const XClientMessageEvent& clientMsg)
  2526. {
  2527. if (dragAndDropSourceWindow == 0)
  2528. return;
  2529. dragAndDropSourceWindow = (::Window) clientMsg.data.l[0];
  2530. Point<int> dropPos ((int) clientMsg.data.l[2] >> 16,
  2531. (int) clientMsg.data.l[2] & 0xffff);
  2532. dropPos = Desktop::getInstance().getDisplays().physicalToLogical (dropPos);
  2533. dropPos -= bounds.getPosition();
  2534. Atom targetAction = atoms->XdndActionCopy;
  2535. for (int i = numElementsInArray (atoms->allowedActions); --i >= 0;)
  2536. {
  2537. if ((Atom) clientMsg.data.l[4] == atoms->allowedActions[i])
  2538. {
  2539. targetAction = atoms->allowedActions[i];
  2540. break;
  2541. }
  2542. }
  2543. sendDragAndDropStatus (true, targetAction);
  2544. if (dragInfo.position != dropPos)
  2545. {
  2546. dragInfo.position = dropPos;
  2547. if (dragInfo.isEmpty())
  2548. updateDraggedFileList (clientMsg);
  2549. if (! dragInfo.isEmpty())
  2550. handleDragMove (dragInfo);
  2551. }
  2552. }
  2553. void handleDragAndDropDrop (const XClientMessageEvent& clientMsg)
  2554. {
  2555. if (dragInfo.isEmpty())
  2556. {
  2557. // no data, transaction finished in handleDragAndDropSelection()
  2558. finishAfterDropDataReceived = true;
  2559. updateDraggedFileList (clientMsg);
  2560. }
  2561. else
  2562. {
  2563. handleDragAndDropDataReceived(); // data was already received
  2564. }
  2565. }
  2566. void handleDragAndDropDataReceived()
  2567. {
  2568. DragInfo dragInfoCopy (dragInfo);
  2569. sendDragAndDropFinish();
  2570. resetDragAndDrop();
  2571. if (! dragInfoCopy.isEmpty())
  2572. handleDragDrop (dragInfoCopy);
  2573. }
  2574. void handleDragAndDropEnter (const XClientMessageEvent& clientMsg)
  2575. {
  2576. dragInfo.clear();
  2577. srcMimeTypeAtomList.clear();
  2578. dragAndDropCurrentMimeType = 0;
  2579. auto dndCurrentVersion = static_cast<unsigned long> (clientMsg.data.l[1] & 0xff000000) >> 24;
  2580. if (dndCurrentVersion < 3 || dndCurrentVersion > Atoms::DndVersion)
  2581. {
  2582. dragAndDropSourceWindow = 0;
  2583. return;
  2584. }
  2585. dragAndDropSourceWindow = (::Window) clientMsg.data.l[0];
  2586. if ((clientMsg.data.l[1] & 1) != 0)
  2587. {
  2588. ScopedXLock xlock (display);
  2589. GetXProperty prop (display, dragAndDropSourceWindow, atoms->XdndTypeList, 0, 0x8000000L, false, XA_ATOM);
  2590. if (prop.success
  2591. && prop.actualType == XA_ATOM
  2592. && prop.actualFormat == 32
  2593. && prop.numItems != 0)
  2594. {
  2595. auto* types = prop.data;
  2596. for (unsigned long i = 0; i < prop.numItems; ++i)
  2597. {
  2598. unsigned long type;
  2599. memcpy (&type, types, sizeof (unsigned long));
  2600. if (type != None)
  2601. srcMimeTypeAtomList.add (type);
  2602. types += sizeof (unsigned long);
  2603. }
  2604. }
  2605. }
  2606. if (srcMimeTypeAtomList.isEmpty())
  2607. {
  2608. for (int i = 2; i < 5; ++i)
  2609. if (clientMsg.data.l[i] != None)
  2610. srcMimeTypeAtomList.add ((unsigned long) clientMsg.data.l[i]);
  2611. if (srcMimeTypeAtomList.isEmpty())
  2612. {
  2613. dragAndDropSourceWindow = 0;
  2614. return;
  2615. }
  2616. }
  2617. for (int i = 0; i < srcMimeTypeAtomList.size() && dragAndDropCurrentMimeType == 0; ++i)
  2618. for (int j = 0; j < numElementsInArray (atoms->allowedMimeTypes); ++j)
  2619. if (srcMimeTypeAtomList[i] == atoms->allowedMimeTypes[j])
  2620. dragAndDropCurrentMimeType = atoms->allowedMimeTypes[j];
  2621. handleDragAndDropPosition (clientMsg);
  2622. }
  2623. void handleDragAndDropSelection (const XEvent& evt)
  2624. {
  2625. dragInfo.clear();
  2626. if (evt.xselection.property != None)
  2627. {
  2628. StringArray lines;
  2629. {
  2630. MemoryBlock dropData;
  2631. for (;;)
  2632. {
  2633. GetXProperty prop (display, evt.xany.window, evt.xselection.property,
  2634. (long) (dropData.getSize() / 4), 65536, false, AnyPropertyType);
  2635. if (! prop.success)
  2636. break;
  2637. dropData.append (prop.data, (size_t) (prop.actualFormat / 8) * prop.numItems);
  2638. if (prop.bytesLeft <= 0)
  2639. break;
  2640. }
  2641. lines.addLines (dropData.toString());
  2642. }
  2643. if (Atoms::isMimeTypeFile (display, dragAndDropCurrentMimeType))
  2644. {
  2645. for (int i = 0; i < lines.size(); ++i)
  2646. dragInfo.files.add (URL::removeEscapeChars (lines[i].replace ("file://", String(), true)));
  2647. dragInfo.files.trim();
  2648. dragInfo.files.removeEmptyStrings();
  2649. }
  2650. else
  2651. {
  2652. dragInfo.text = lines.joinIntoString ("\n");
  2653. }
  2654. if (finishAfterDropDataReceived)
  2655. handleDragAndDropDataReceived();
  2656. }
  2657. }
  2658. void updateDraggedFileList (const XClientMessageEvent& clientMsg)
  2659. {
  2660. jassert (dragInfo.isEmpty());
  2661. if (dragAndDropSourceWindow != None
  2662. && dragAndDropCurrentMimeType != None)
  2663. {
  2664. ScopedXLock xlock (display);
  2665. XConvertSelection (display,
  2666. atoms->XdndSelection,
  2667. dragAndDropCurrentMimeType,
  2668. Atoms::getCreating (display, "JXSelectionWindowProperty"),
  2669. windowH,
  2670. (::Time) clientMsg.data.l[2]);
  2671. }
  2672. }
  2673. bool isWindowDnDAware (Window w) const
  2674. {
  2675. int numProperties = 0;
  2676. auto* properties = XListProperties (display, w, &numProperties);
  2677. bool dndAwarePropFound = false;
  2678. for (int i = 0; i < numProperties; ++i)
  2679. if (properties[i] == atoms->XdndAware)
  2680. dndAwarePropFound = true;
  2681. if (properties != nullptr)
  2682. XFree (properties);
  2683. return dndAwarePropFound;
  2684. }
  2685. int getDnDVersionForWindow (Window targetWindow)
  2686. {
  2687. GetXProperty prop (display, targetWindow, atoms->XdndAware,
  2688. 0, 2, false, AnyPropertyType);
  2689. if (prop.success && prop.data != None && prop.actualFormat == 32 && prop.numItems == 1)
  2690. return jmin ((int) prop.data[0], (int) atoms->DndVersion);
  2691. return -1;
  2692. }
  2693. Window externalFindDragTargetWindow (Window targetWindow)
  2694. {
  2695. if (targetWindow == None)
  2696. return None;
  2697. if (isWindowDnDAware (targetWindow))
  2698. return targetWindow;
  2699. Window child, phonyWin;
  2700. int phony;
  2701. unsigned int uphony;
  2702. XQueryPointer (display, targetWindow, &phonyWin, &child,
  2703. &phony, &phony, &phony, &phony, &uphony);
  2704. return externalFindDragTargetWindow (child);
  2705. }
  2706. bool externalDragInit (bool isText, const String& textOrFiles, std::function<void()> cb)
  2707. {
  2708. ScopedXLock xlock (display);
  2709. resetExternalDragState();
  2710. dragState->isText = isText;
  2711. dragState->textOrFiles = textOrFiles;
  2712. dragState->targetWindow = windowH;
  2713. dragState->completionCallback = cb;
  2714. const int pointerGrabMask = Button1MotionMask | ButtonReleaseMask;
  2715. if (XGrabPointer (display, windowH, True, pointerGrabMask,
  2716. GrabModeAsync, GrabModeAsync, None, None, CurrentTime) == GrabSuccess)
  2717. {
  2718. // No other method of changing the pointer seems to work, this call is needed from this very context
  2719. XChangeActivePointerGrab (display, pointerGrabMask, (Cursor) createDraggingHandCursor(), CurrentTime);
  2720. XSetSelectionOwner (display, atoms->XdndSelection, windowH, CurrentTime);
  2721. // save the available types to XdndTypeList
  2722. xchangeProperty (windowH, atoms->XdndTypeList, XA_ATOM, 32,
  2723. dragState->allowedTypes.getRawDataPointer(),
  2724. dragState->allowedTypes.size());
  2725. dragState->dragging = true;
  2726. dragState->xdndVersion = getDnDVersionForWindow (dragState->targetWindow);
  2727. sendExternalDragAndDropEnter (dragState->targetWindow);
  2728. handleExternalDragMotionNotify();
  2729. return true;
  2730. }
  2731. return false;
  2732. }
  2733. void externalResetDragAndDrop()
  2734. {
  2735. if (dragState->dragging)
  2736. {
  2737. ScopedXLock xlock (display);
  2738. XUngrabPointer (display, CurrentTime);
  2739. }
  2740. if (dragState->completionCallback != nullptr)
  2741. dragState->completionCallback();
  2742. resetExternalDragState();
  2743. }
  2744. std::unique_ptr<DragState> dragState;
  2745. DragInfo dragInfo;
  2746. Atom dragAndDropCurrentMimeType;
  2747. Window dragAndDropSourceWindow;
  2748. bool finishAfterDropDataReceived;
  2749. Array<Atom> srcMimeTypeAtomList;
  2750. int pointerMap[5] = {};
  2751. void initialisePointerMap()
  2752. {
  2753. const int numButtons = XGetPointerMapping (display, nullptr, 0);
  2754. pointerMap[2] = pointerMap[3] = pointerMap[4] = Keys::NoButton;
  2755. if (numButtons == 2)
  2756. {
  2757. pointerMap[0] = Keys::LeftButton;
  2758. pointerMap[1] = Keys::RightButton;
  2759. }
  2760. else if (numButtons >= 3)
  2761. {
  2762. pointerMap[0] = Keys::LeftButton;
  2763. pointerMap[1] = Keys::MiddleButton;
  2764. pointerMap[2] = Keys::RightButton;
  2765. if (numButtons >= 5)
  2766. {
  2767. pointerMap[3] = Keys::WheelUp;
  2768. pointerMap[4] = Keys::WheelDown;
  2769. }
  2770. }
  2771. }
  2772. static Point<int> lastMousePos;
  2773. static void clearLastMousePos() noexcept
  2774. {
  2775. lastMousePos = Point<int> (0x100000, 0x100000);
  2776. }
  2777. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LinuxComponentPeer)
  2778. };
  2779. bool LinuxComponentPeer::isActiveApplication = false;
  2780. Point<int> LinuxComponentPeer::lastMousePos;
  2781. ::Display* LinuxComponentPeer::display = nullptr;
  2782. //==============================================================================
  2783. namespace WindowingHelpers
  2784. {
  2785. static void windowMessageReceive (XEvent& event)
  2786. {
  2787. if (event.xany.window != None)
  2788. {
  2789. #if JUCE_X11_SUPPORTS_XEMBED
  2790. if (! juce_handleXEmbedEvent (nullptr, &event))
  2791. #endif
  2792. {
  2793. if (auto* peer = LinuxComponentPeer::getPeerFor (event.xany.window))
  2794. peer->handleWindowMessage (event);
  2795. }
  2796. }
  2797. else if (event.xany.type == KeymapNotify)
  2798. {
  2799. auto& keymapEvent = (const XKeymapEvent&) event.xkeymap;
  2800. memcpy (Keys::keyStates, keymapEvent.key_vector, 32);
  2801. }
  2802. }
  2803. }
  2804. struct WindowingCallbackInitialiser
  2805. {
  2806. WindowingCallbackInitialiser()
  2807. {
  2808. dispatchWindowMessage = WindowingHelpers::windowMessageReceive;
  2809. }
  2810. };
  2811. static WindowingCallbackInitialiser windowingInitialiser;
  2812. //==============================================================================
  2813. JUCE_API bool JUCE_CALLTYPE Process::isForegroundProcess()
  2814. {
  2815. return LinuxComponentPeer::isActiveApplication;
  2816. }
  2817. // N/A on Linux as far as I know.
  2818. JUCE_API void JUCE_CALLTYPE Process::makeForegroundProcess() {}
  2819. JUCE_API void JUCE_CALLTYPE Process::hide() {}
  2820. //==============================================================================
  2821. void Desktop::setKioskComponent (Component* comp, bool enableOrDisable, bool /* allowMenusAndBars */)
  2822. {
  2823. if (enableOrDisable)
  2824. comp->setBounds (getDisplays().findDisplayForRect (comp->getScreenBounds()).totalArea);
  2825. }
  2826. void Desktop::allowedOrientationsChanged() {}
  2827. //==============================================================================
  2828. ComponentPeer* Component::createNewPeer (int styleFlags, void* nativeWindowToAttachTo)
  2829. {
  2830. return new LinuxComponentPeer (*this, styleFlags, (Window) nativeWindowToAttachTo);
  2831. }
  2832. //==============================================================================
  2833. void Displays::findDisplays (float masterScale)
  2834. {
  2835. ScopedXDisplay xDisplay;
  2836. if (auto display = xDisplay.display)
  2837. {
  2838. Atom hints = Atoms::getIfExists (display, "_NET_WORKAREA");
  2839. auto getWorkAreaPropertyData = [&] (int screenNum) -> unsigned char*
  2840. {
  2841. if (hints != None)
  2842. {
  2843. GetXProperty prop (display, RootWindow (display, screenNum), hints, 0, 4, false, XA_CARDINAL);
  2844. if (prop.success && prop.actualType == XA_CARDINAL && prop.actualFormat == 32 && prop.numItems == 4)
  2845. return prop.data;
  2846. }
  2847. return nullptr;
  2848. };
  2849. #if JUCE_USE_XRANDR
  2850. {
  2851. int major_opcode, first_event, first_error;
  2852. if (XQueryExtension (display, "RANDR", &major_opcode, &first_event, &first_error))
  2853. {
  2854. auto& xrandr = XRandrWrapper::getInstance();
  2855. auto numMonitors = ScreenCount (display);
  2856. auto mainDisplay = xrandr.getOutputPrimary (display, RootWindow (display, 0));
  2857. for (int i = 0; i < numMonitors; ++i)
  2858. {
  2859. if (getWorkAreaPropertyData (i) == nullptr)
  2860. continue;
  2861. if (auto* screens = xrandr.getScreenResources (display, RootWindow (display, i)))
  2862. {
  2863. for (int j = 0; j < screens->noutput; ++j)
  2864. {
  2865. if (screens->outputs[j])
  2866. {
  2867. // Xrandr on the raspberry pi fails to determine the main display (mainDisplay == 0)!
  2868. // Detect this edge case and make the first found display the main display
  2869. if (! mainDisplay)
  2870. mainDisplay = screens->outputs[j];
  2871. if (auto* output = xrandr.getOutputInfo (display, screens, screens->outputs[j]))
  2872. {
  2873. if (output->crtc)
  2874. {
  2875. if (auto* crtc = xrandr.getCrtcInfo (display, screens, output->crtc))
  2876. {
  2877. Display d;
  2878. d.totalArea = Rectangle<int> (crtc->x, crtc->y,
  2879. (int) crtc->width, (int) crtc->height);
  2880. d.isMain = (mainDisplay == screens->outputs[j]) && (i == 0);
  2881. d.dpi = getDisplayDPI (display, 0);
  2882. // The raspberry pi returns a zero sized display, so we need to guard for divide-by-zero
  2883. if (output->mm_width > 0 && output->mm_height > 0)
  2884. d.dpi = ((static_cast<double> (crtc->width) * 25.4 * 0.5) / static_cast<double> (output->mm_width))
  2885. + ((static_cast<double> (crtc->height) * 25.4 * 0.5) / static_cast<double> (output->mm_height));
  2886. double scale = getScaleForDisplay (output->name, d.dpi);
  2887. scale = (scale <= 0.1 ? 1.0 : scale);
  2888. d.scale = masterScale * scale;
  2889. if (d.isMain)
  2890. displays.insert (0, d);
  2891. else
  2892. displays.add (d);
  2893. xrandr.freeCrtcInfo (crtc);
  2894. }
  2895. }
  2896. xrandr.freeOutputInfo (output);
  2897. }
  2898. }
  2899. }
  2900. xrandr.freeScreenResources (screens);
  2901. }
  2902. }
  2903. if (! displays.isEmpty() && ! displays.getReference (0).isMain)
  2904. displays.getReference (0).isMain = true;
  2905. }
  2906. }
  2907. if (displays.isEmpty())
  2908. #endif
  2909. #if JUCE_USE_XINERAMA
  2910. {
  2911. auto screens = XineramaQueryDisplays (display);
  2912. int numMonitors = screens.size();
  2913. for (int index = 0; index < numMonitors; ++index)
  2914. {
  2915. for (int j = numMonitors; --j >= 0;)
  2916. {
  2917. if (screens[j].screen_number == index)
  2918. {
  2919. Display d;
  2920. d.totalArea = Rectangle<int> (screens[j].x_org,
  2921. screens[j].y_org,
  2922. screens[j].width,
  2923. screens[j].height);
  2924. d.isMain = (index == 0);
  2925. d.scale = masterScale;
  2926. d.dpi = getDisplayDPI (display, 0); // (all screens share the same DPI)
  2927. displays.add (d);
  2928. }
  2929. }
  2930. }
  2931. }
  2932. if (displays.isEmpty())
  2933. #endif
  2934. {
  2935. if (hints != None)
  2936. {
  2937. auto numMonitors = ScreenCount (display);
  2938. for (int i = 0; i < numMonitors; ++i)
  2939. {
  2940. if (auto* positionData = getWorkAreaPropertyData (i))
  2941. {
  2942. std::array<long, 4> position;
  2943. for (auto& p : position)
  2944. {
  2945. memcpy (&p, positionData, sizeof (long));
  2946. positionData += sizeof (long);
  2947. }
  2948. Display d;
  2949. d.totalArea = Rectangle<int> ((int) position[0], (int) position[1],
  2950. (int) position[2], (int) position[3]);
  2951. d.isMain = displays.isEmpty();
  2952. d.scale = masterScale;
  2953. d.dpi = getDisplayDPI (display, i);
  2954. displays.add (d);
  2955. }
  2956. }
  2957. }
  2958. if (displays.isEmpty())
  2959. {
  2960. Display d;
  2961. d.totalArea = Rectangle<int> (DisplayWidth (display, DefaultScreen (display)),
  2962. DisplayHeight (display, DefaultScreen (display)));
  2963. d.isMain = true;
  2964. d.scale = masterScale;
  2965. d.dpi = getDisplayDPI (display, 0);
  2966. displays.add (d);
  2967. }
  2968. }
  2969. for (auto& d : displays)
  2970. d.userArea = d.totalArea; // JUCE currently does not support requesting the user area on Linux
  2971. updateToLogical();
  2972. }
  2973. }
  2974. //==============================================================================
  2975. bool MouseInputSource::SourceList::addSource()
  2976. {
  2977. if (sources.isEmpty())
  2978. {
  2979. addSource (0, MouseInputSource::InputSourceType::mouse);
  2980. return true;
  2981. }
  2982. return false;
  2983. }
  2984. bool MouseInputSource::SourceList::canUseTouch()
  2985. {
  2986. return false;
  2987. }
  2988. bool Desktop::canUseSemiTransparentWindows() noexcept
  2989. {
  2990. #if JUCE_USE_XRENDER
  2991. auto display = XWindowSystem::getInstance()->displayRef();
  2992. if (XRender::hasCompositingWindowManager (display))
  2993. {
  2994. int matchedDepth = 0, desiredDepth = 32;
  2995. return Visuals::findVisualFormat (display, desiredDepth, matchedDepth) != 0
  2996. && matchedDepth == desiredDepth;
  2997. }
  2998. #endif
  2999. return false;
  3000. }
  3001. Point<float> MouseInputSource::getCurrentRawMousePosition()
  3002. {
  3003. ScopedXDisplay xDisplay;
  3004. auto display = xDisplay.display;
  3005. if (display == nullptr)
  3006. return {};
  3007. Window root, child;
  3008. int x, y, winx, winy;
  3009. unsigned int mask;
  3010. ScopedXLock xlock (display);
  3011. if (XQueryPointer (display,
  3012. RootWindow (display, DefaultScreen (display)),
  3013. &root, &child,
  3014. &x, &y, &winx, &winy, &mask) == False)
  3015. {
  3016. // Pointer not on the default screen
  3017. x = y = -1;
  3018. }
  3019. return Desktop::getInstance().getDisplays().physicalToLogical (Point<float> ((float) x, (float) y));
  3020. }
  3021. void MouseInputSource::setRawMousePosition (Point<float> newPosition)
  3022. {
  3023. ScopedXDisplay xDisplay;
  3024. if (auto display = xDisplay.display)
  3025. {
  3026. ScopedXLock xlock (display);
  3027. Window root = RootWindow (display, DefaultScreen (display));
  3028. newPosition = Desktop::getInstance().getDisplays().logicalToPhysical (newPosition);
  3029. XWarpPointer (display, None, root, 0, 0, 0, 0, roundToInt (newPosition.getX()), roundToInt (newPosition.getY()));
  3030. }
  3031. }
  3032. double Desktop::getDefaultMasterScale()
  3033. {
  3034. return 1.0;
  3035. }
  3036. Desktop::DisplayOrientation Desktop::getCurrentOrientation() const
  3037. {
  3038. return upright;
  3039. }
  3040. //==============================================================================
  3041. static bool screenSaverAllowed = true;
  3042. void Desktop::setScreenSaverEnabled (bool isEnabled)
  3043. {
  3044. if (screenSaverAllowed != isEnabled)
  3045. {
  3046. screenSaverAllowed = isEnabled;
  3047. ScopedXDisplay xDisplay;
  3048. if (auto display = xDisplay.display)
  3049. {
  3050. typedef void (*tXScreenSaverSuspend) (Display*, Bool);
  3051. static tXScreenSaverSuspend xScreenSaverSuspend = nullptr;
  3052. if (xScreenSaverSuspend == nullptr)
  3053. if (void* h = dlopen ("libXss.so.1", RTLD_GLOBAL | RTLD_NOW))
  3054. xScreenSaverSuspend = (tXScreenSaverSuspend) dlsym (h, "XScreenSaverSuspend");
  3055. ScopedXLock xlock (display);
  3056. if (xScreenSaverSuspend != nullptr)
  3057. xScreenSaverSuspend (display, ! isEnabled);
  3058. }
  3059. }
  3060. }
  3061. bool Desktop::isScreenSaverEnabled()
  3062. {
  3063. return screenSaverAllowed;
  3064. }
  3065. //==============================================================================
  3066. Image juce_createIconForFile (const File& /* file */)
  3067. {
  3068. return {};
  3069. }
  3070. //==============================================================================
  3071. void LookAndFeel::playAlertSound()
  3072. {
  3073. std::cout << "\a" << std::flush;
  3074. }
  3075. //==============================================================================
  3076. Rectangle<int> juce_LinuxScaledToPhysicalBounds (ComponentPeer* peer, Rectangle<int> bounds)
  3077. {
  3078. if (auto* linuxPeer = dynamic_cast<LinuxComponentPeer*> (peer))
  3079. bounds *= linuxPeer->getPlatformScaleFactor();
  3080. return bounds;
  3081. }
  3082. void juce_LinuxAddRepaintListener (ComponentPeer* peer, Component* dummy)
  3083. {
  3084. if (auto* linuxPeer = dynamic_cast<LinuxComponentPeer*> (peer))
  3085. linuxPeer->addOpenGLRepaintListener (dummy);
  3086. }
  3087. void juce_LinuxRemoveRepaintListener (ComponentPeer* peer, Component* dummy)
  3088. {
  3089. if (auto* linuxPeer = dynamic_cast<LinuxComponentPeer*> (peer))
  3090. linuxPeer->removeOpenGLRepaintListener (dummy);
  3091. }
  3092. unsigned long juce_createKeyProxyWindow (ComponentPeer* peer)
  3093. {
  3094. if (auto* linuxPeer = dynamic_cast<LinuxComponentPeer*> (peer))
  3095. return linuxPeer->createKeyProxy();
  3096. return 0;
  3097. }
  3098. void juce_deleteKeyProxyWindow (ComponentPeer* peer)
  3099. {
  3100. if (auto* linuxPeer = dynamic_cast<LinuxComponentPeer*> (peer))
  3101. linuxPeer->deleteKeyProxy();
  3102. }
  3103. //==============================================================================
  3104. #if JUCE_MODAL_LOOPS_PERMITTED
  3105. void JUCE_CALLTYPE NativeMessageBox::showMessageBox (AlertWindow::AlertIconType iconType,
  3106. const String& title, const String& message,
  3107. Component* /* associatedComponent */)
  3108. {
  3109. AlertWindow::showMessageBox (iconType, title, message);
  3110. }
  3111. #endif
  3112. void JUCE_CALLTYPE NativeMessageBox::showMessageBoxAsync (AlertWindow::AlertIconType iconType,
  3113. const String& title, const String& message,
  3114. Component* associatedComponent,
  3115. ModalComponentManager::Callback* callback)
  3116. {
  3117. AlertWindow::showMessageBoxAsync (iconType, title, message, String(), associatedComponent, callback);
  3118. }
  3119. bool JUCE_CALLTYPE NativeMessageBox::showOkCancelBox (AlertWindow::AlertIconType iconType,
  3120. const String& title, const String& message,
  3121. Component* associatedComponent,
  3122. ModalComponentManager::Callback* callback)
  3123. {
  3124. return AlertWindow::showOkCancelBox (iconType, title, message, String(), String(),
  3125. associatedComponent, callback);
  3126. }
  3127. int JUCE_CALLTYPE NativeMessageBox::showYesNoCancelBox (AlertWindow::AlertIconType iconType,
  3128. const String& title, const String& message,
  3129. Component* associatedComponent,
  3130. ModalComponentManager::Callback* callback)
  3131. {
  3132. return AlertWindow::showYesNoCancelBox (iconType, title, message,
  3133. String(), String(), String(),
  3134. associatedComponent, callback);
  3135. }
  3136. int JUCE_CALLTYPE NativeMessageBox::showYesNoBox (AlertWindow::AlertIconType iconType,
  3137. const String& title, const String& message,
  3138. Component* associatedComponent,
  3139. ModalComponentManager::Callback* callback)
  3140. {
  3141. return AlertWindow::showOkCancelBox (iconType, title, message, TRANS ("Yes"), TRANS ("No"),
  3142. associatedComponent, callback);
  3143. }
  3144. //============================== X11 - MouseCursor =============================
  3145. std::map<Cursor, Display*> cursorMap;
  3146. void* CustomMouseCursorInfo::create() const
  3147. {
  3148. ScopedXDisplay xDisplay;
  3149. auto display = xDisplay.display;
  3150. if (display == nullptr)
  3151. return nullptr;
  3152. ScopedXLock xlock (display);
  3153. auto imageW = (unsigned int) image.getWidth();
  3154. auto imageH = (unsigned int) image.getHeight();
  3155. int hotspotX = hotspot.x;
  3156. int hotspotY = hotspot.y;
  3157. #if JUCE_USE_XCURSOR
  3158. {
  3159. using tXcursorSupportsARGB = XcursorBool (*) (Display*);
  3160. using tXcursorImageCreate = XcursorImage* (*) (int, int);
  3161. using tXcursorImageDestroy = void (*) (XcursorImage*);
  3162. using tXcursorImageLoadCursor = Cursor (*) (Display*, const XcursorImage*);
  3163. static tXcursorSupportsARGB xcursorSupportsARGB = nullptr;
  3164. static tXcursorImageCreate xcursorImageCreate = nullptr;
  3165. static tXcursorImageDestroy xcursorImageDestroy = nullptr;
  3166. static tXcursorImageLoadCursor xcursorImageLoadCursor = nullptr;
  3167. static bool hasBeenLoaded = false;
  3168. if (! hasBeenLoaded)
  3169. {
  3170. hasBeenLoaded = true;
  3171. if (void* h = dlopen ("libXcursor.so.1", RTLD_GLOBAL | RTLD_NOW))
  3172. {
  3173. xcursorSupportsARGB = (tXcursorSupportsARGB) dlsym (h, "XcursorSupportsARGB");
  3174. xcursorImageCreate = (tXcursorImageCreate) dlsym (h, "XcursorImageCreate");
  3175. xcursorImageLoadCursor = (tXcursorImageLoadCursor) dlsym (h, "XcursorImageLoadCursor");
  3176. xcursorImageDestroy = (tXcursorImageDestroy) dlsym (h, "XcursorImageDestroy");
  3177. if (xcursorSupportsARGB == nullptr || xcursorImageCreate == nullptr
  3178. || xcursorImageLoadCursor == nullptr || xcursorImageDestroy == nullptr
  3179. || ! xcursorSupportsARGB (display))
  3180. xcursorSupportsARGB = nullptr;
  3181. }
  3182. }
  3183. if (xcursorSupportsARGB != nullptr)
  3184. {
  3185. if (XcursorImage* xcImage = xcursorImageCreate ((int) imageW, (int) imageH))
  3186. {
  3187. xcImage->xhot = (XcursorDim) hotspotX;
  3188. xcImage->yhot = (XcursorDim) hotspotY;
  3189. XcursorPixel* dest = xcImage->pixels;
  3190. for (int y = 0; y < (int) imageH; ++y)
  3191. for (int x = 0; x < (int) imageW; ++x)
  3192. *dest++ = image.getPixelAt (x, y).getARGB();
  3193. void* result = (void*) xcursorImageLoadCursor (display, xcImage);
  3194. xcursorImageDestroy (xcImage);
  3195. if (result != nullptr)
  3196. {
  3197. cursorMap[(Cursor) result] = display;
  3198. return result;
  3199. }
  3200. }
  3201. }
  3202. }
  3203. #endif
  3204. Window root = RootWindow (display, DefaultScreen (display));
  3205. unsigned int cursorW, cursorH;
  3206. if (! XQueryBestCursor (display, root, imageW, imageH, &cursorW, &cursorH))
  3207. return nullptr;
  3208. Image im (Image::ARGB, (int) cursorW, (int) cursorH, true);
  3209. {
  3210. Graphics g (im);
  3211. if (imageW > cursorW || imageH > cursorH)
  3212. {
  3213. hotspotX = (hotspotX * (int) cursorW) / (int) imageW;
  3214. hotspotY = (hotspotY * (int) cursorH) / (int) imageH;
  3215. g.drawImage (image, Rectangle<float> ((float) imageW, (float) imageH),
  3216. RectanglePlacement::xLeft | RectanglePlacement::yTop | RectanglePlacement::onlyReduceInSize);
  3217. }
  3218. else
  3219. {
  3220. g.drawImageAt (image, 0, 0);
  3221. }
  3222. }
  3223. const unsigned int stride = (cursorW + 7) >> 3;
  3224. HeapBlock<char> maskPlane, sourcePlane;
  3225. maskPlane.calloc (stride * cursorH);
  3226. sourcePlane.calloc (stride * cursorH);
  3227. const bool msbfirst = (BitmapBitOrder (display) == MSBFirst);
  3228. for (int y = (int) cursorH; --y >= 0;)
  3229. {
  3230. for (int x = (int) cursorW; --x >= 0;)
  3231. {
  3232. auto mask = (char) (1 << (msbfirst ? (7 - (x & 7)) : (x & 7)));
  3233. auto offset = (unsigned int) y * stride + ((unsigned int) x >> 3);
  3234. auto c = im.getPixelAt (x, y);
  3235. if (c.getAlpha() >= 128) maskPlane[offset] |= mask;
  3236. if (c.getBrightness() >= 0.5f) sourcePlane[offset] |= mask;
  3237. }
  3238. }
  3239. Pixmap sourcePixmap = XCreatePixmapFromBitmapData (display, root, sourcePlane.getData(), cursorW, cursorH, 0xffff, 0, 1);
  3240. Pixmap maskPixmap = XCreatePixmapFromBitmapData (display, root, maskPlane.getData(), cursorW, cursorH, 0xffff, 0, 1);
  3241. XColor white, black;
  3242. black.red = black.green = black.blue = 0;
  3243. white.red = white.green = white.blue = 0xffff;
  3244. void* result = (void*) XCreatePixmapCursor (display, sourcePixmap, maskPixmap, &white, &black,
  3245. (unsigned int) hotspotX, (unsigned int) hotspotY);
  3246. XFreePixmap (display, sourcePixmap);
  3247. XFreePixmap (display, maskPixmap);
  3248. cursorMap[(Cursor) result] = display;
  3249. return result;
  3250. }
  3251. void MouseCursor::deleteMouseCursor (void* cursorHandle, bool)
  3252. {
  3253. if (cursorHandle != nullptr)
  3254. {
  3255. ScopedXDisplay xDisplay;
  3256. if (auto display = xDisplay.display)
  3257. {
  3258. ScopedXLock xlock (display);
  3259. cursorMap.erase ((Cursor) cursorHandle);
  3260. XFreeCursor (display, (Cursor) cursorHandle);
  3261. }
  3262. }
  3263. }
  3264. void* MouseCursor::createStandardMouseCursor (MouseCursor::StandardCursorType type)
  3265. {
  3266. ScopedXDisplay xDisplay;
  3267. auto display = xDisplay.display;
  3268. if (display == nullptr)
  3269. return None;
  3270. unsigned int shape;
  3271. switch (type)
  3272. {
  3273. case NormalCursor:
  3274. case ParentCursor: return None; // Use parent cursor
  3275. case NoCursor: return CustomMouseCursorInfo (Image (Image::ARGB, 16, 16, true), {}).create();
  3276. case WaitCursor: shape = XC_watch; break;
  3277. case IBeamCursor: shape = XC_xterm; break;
  3278. case PointingHandCursor: shape = XC_hand2; break;
  3279. case LeftRightResizeCursor: shape = XC_sb_h_double_arrow; break;
  3280. case UpDownResizeCursor: shape = XC_sb_v_double_arrow; break;
  3281. case UpDownLeftRightResizeCursor: shape = XC_fleur; break;
  3282. case TopEdgeResizeCursor: shape = XC_top_side; break;
  3283. case BottomEdgeResizeCursor: shape = XC_bottom_side; break;
  3284. case LeftEdgeResizeCursor: shape = XC_left_side; break;
  3285. case RightEdgeResizeCursor: shape = XC_right_side; break;
  3286. case TopLeftCornerResizeCursor: shape = XC_top_left_corner; break;
  3287. case TopRightCornerResizeCursor: shape = XC_top_right_corner; break;
  3288. case BottomLeftCornerResizeCursor: shape = XC_bottom_left_corner; break;
  3289. case BottomRightCornerResizeCursor: shape = XC_bottom_right_corner; break;
  3290. case CrosshairCursor: shape = XC_crosshair; break;
  3291. case DraggingHandCursor: return createDraggingHandCursor();
  3292. case CopyingCursor:
  3293. {
  3294. static unsigned char copyCursorData[] = { 71,73,70,56,57,97,21,0,21,0,145,0,0,0,0,0,255,255,255,0,
  3295. 128,128,255,255,255,33,249,4,1,0,0,3,0,44,0,0,0,0,21,0, 21,0,0,2,72,4,134,169,171,16,199,98,11,79,90,71,161,93,56,111,
  3296. 78,133,218,215,137,31,82,154,100,200,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,
  3297. 252,114,147,74,83,5,50,68,147,208,217,16,71,149,252,124,5,0,59,0,0 };
  3298. const int copyCursorSize = 119;
  3299. return CustomMouseCursorInfo (ImageFileFormat::loadFrom (copyCursorData, copyCursorSize), { 1, 3 }).create();
  3300. }
  3301. case NumStandardCursorTypes:
  3302. default:
  3303. jassertfalse;
  3304. return None;
  3305. }
  3306. ScopedXLock xlock (display);
  3307. auto* result = (void*) XCreateFontCursor (display, shape);
  3308. cursorMap[(Cursor) result] = display;
  3309. return result;
  3310. }
  3311. void MouseCursor::showInWindow (ComponentPeer* peer) const
  3312. {
  3313. if (auto* lp = dynamic_cast<LinuxComponentPeer*> (peer))
  3314. {
  3315. ScopedXDisplay xDisplay;
  3316. auto cursor = (Cursor) getHandle();
  3317. auto cursorDisplay = cursorMap[cursor];
  3318. if (cursorHandle != nullptr && xDisplay.display != cursorDisplay)
  3319. {
  3320. cursorMap.erase (cursor);
  3321. XFreeCursor (cursorDisplay, cursor);
  3322. if (auto* customInfo = cursorHandle->getCustomInfo())
  3323. cursorHandle->setHandle (customInfo->create());
  3324. else
  3325. cursorHandle->setHandle (createStandardMouseCursor (cursorHandle->getType()));
  3326. }
  3327. lp->showMouseCursor ((Cursor) getHandle());
  3328. }
  3329. }
  3330. //=================================== X11 - DND ================================
  3331. static LinuxComponentPeer* getPeerForDragEvent (Component* sourceComp)
  3332. {
  3333. if (sourceComp == nullptr)
  3334. if (auto* draggingSource = Desktop::getInstance().getDraggingMouseSource(0))
  3335. sourceComp = draggingSource->getComponentUnderMouse();
  3336. if (sourceComp != nullptr)
  3337. if (auto* lp = dynamic_cast<LinuxComponentPeer*> (sourceComp->getPeer()))
  3338. return lp;
  3339. jassertfalse; // This method must be called in response to a component's mouseDown or mouseDrag event!
  3340. return nullptr;
  3341. }
  3342. bool DragAndDropContainer::performExternalDragDropOfFiles (const StringArray& files, bool canMoveFiles,
  3343. Component* sourceComp, std::function<void()> callback)
  3344. {
  3345. if (files.isEmpty())
  3346. return false;
  3347. if (auto* lp = getPeerForDragEvent (sourceComp))
  3348. return lp->externalDragFileInit (files, canMoveFiles, callback);
  3349. // This method must be called in response to a component's mouseDown or mouseDrag event!
  3350. jassertfalse;
  3351. return false;
  3352. }
  3353. bool DragAndDropContainer::performExternalDragDropOfText (const String& text, Component* sourceComp,
  3354. std::function<void()> callback)
  3355. {
  3356. if (text.isEmpty())
  3357. return false;
  3358. if (auto* lp = getPeerForDragEvent (sourceComp))
  3359. return lp->externalDragTextInit (text, callback);
  3360. // This method must be called in response to a component's mouseDown or mouseDrag event!
  3361. jassertfalse;
  3362. return false;
  3363. }
  3364. } // namespace juce