BsLinuxPlatform.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "Image/BsPixelData.h"
  4. #include "Image/BsPixelUtil.h"
  5. #include "String/BsUnicode.h"
  6. #include "Linux/BsLinuxPlatform.h"
  7. #include "Linux/BsLinuxWindow.h"
  8. #include "RenderAPI/BsRenderWindow.h"
  9. #include "BsLinuxDragAndDrop.h"
  10. #include <X11/Xatom.h>
  11. #include <X11/Xcursor/Xcursor.h>
  12. namespace bs
  13. {
  14. Event<void(const Vector2I&, const OSPointerButtonStates&)> Platform::onCursorMoved;
  15. Event<void(const Vector2I&, OSMouseButton button, const OSPointerButtonStates&)> Platform::onCursorButtonPressed;
  16. Event<void(const Vector2I&, OSMouseButton button, const OSPointerButtonStates&)> Platform::onCursorButtonReleased;
  17. Event<void(const Vector2I&, const OSPointerButtonStates&)> Platform::onCursorDoubleClick;
  18. Event<void(InputCommandType)> Platform::onInputCommand;
  19. Event<void(float)> Platform::onMouseWheelScrolled;
  20. Event<void(UINT32)> Platform::onCharInput;
  21. Event<void(ct::RenderWindow*)> Platform::onMouseLeftWindow;
  22. Event<void()> Platform::onMouseCaptureChanged;
  23. enum class X11CursorType
  24. {
  25. Arrow,
  26. ArrowDrag,
  27. ArrowLeftRight,
  28. Wait,
  29. IBeam,
  30. SizeTopLeft,
  31. SizeTopRight,
  32. SizeBotLeft,
  33. SizeBotRight,
  34. SizeLeft,
  35. SizeRight,
  36. SizeTop,
  37. SizeBottom,
  38. Deny,
  39. Count
  40. };;
  41. struct Platform::Pimpl
  42. {
  43. ::Display* xDisplay = nullptr;
  44. ::Window mainXWindow = 0;
  45. ::Window fullscreenXWindow = 0;
  46. std::unordered_map<::Window, LinuxWindow*> windowMap;
  47. Mutex lock;
  48. XIM IM;
  49. XIC IC;
  50. Time lastButtonPressTime;
  51. Atom atomDeleteWindow;
  52. // Clipboard
  53. WString clipboardData;
  54. // Cursor
  55. ::Cursor currentCursor = None;
  56. ::Cursor emptyCursor = None;
  57. bool isCursorHidden = false;
  58. Rect2I cursorClipRect;
  59. LinuxWindow* cursorClipWindow = nullptr;
  60. bool cursorClipEnabled = false;
  61. };
  62. static const UINT32 DOUBLE_CLICK_MS = 500;
  63. Vector2I _getCursorPosition(Platform::Pimpl* data)
  64. {
  65. Vector2I pos;
  66. UINT32 screenCount = (UINT32)XScreenCount(data->xDisplay);
  67. for (UINT32 i = 0; i < screenCount; ++i)
  68. {
  69. ::Window outRoot, outChild;
  70. INT32 childX, childY;
  71. UINT32 mask;
  72. if(XQueryPointer(data->xDisplay, XRootWindow(data->xDisplay, i), &outRoot, &outChild, &pos.x,
  73. &pos.y, &childX, &childY, &mask))
  74. break;
  75. }
  76. return pos;
  77. }
  78. void _setCursorPosition(Platform::Pimpl* data, const Vector2I& screenPos)
  79. {
  80. UINT32 screenCount = (UINT32)XScreenCount(data->xDisplay);
  81. // Note assuming screens are laid out horizontally left to right
  82. INT32 screenX = 0;
  83. for(UINT32 i = 0; i < screenCount; ++i)
  84. {
  85. ::Window root = XRootWindow(data->xDisplay, i);
  86. INT32 screenXEnd = screenX + XDisplayWidth(data->xDisplay, i);
  87. if(screenPos.x >= screenX && screenPos.x < screenXEnd)
  88. {
  89. XWarpPointer(data->xDisplay, None, root, 0, 0, 0, 0, screenPos.x, screenPos.y);
  90. XFlush(data->xDisplay);
  91. return;
  92. }
  93. screenX = screenXEnd;
  94. }
  95. }
  96. void applyCurrentCursor(Platform::Pimpl* data, ::Window window)
  97. {
  98. if(data->isCursorHidden)
  99. XDefineCursor(data->xDisplay, window, data->emptyCursor);
  100. else
  101. {
  102. if (data->currentCursor != None)
  103. XDefineCursor(data->xDisplay, window, data->currentCursor);
  104. else
  105. XUndefineCursor(data->xDisplay, window);
  106. }
  107. }
  108. void updateClipBounds(Platform::Pimpl* data, LinuxWindow* window)
  109. {
  110. if(!data->cursorClipEnabled || data->cursorClipWindow != window)
  111. return;
  112. data->cursorClipRect.x = window->getLeft();
  113. data->cursorClipRect.y = window->getTop();
  114. data->cursorClipRect.width = window->getWidth();
  115. data->cursorClipRect.height = window->getHeight();
  116. }
  117. bool clipCursor(Platform::Pimpl* data, Vector2I& pos)
  118. {
  119. if(!data->cursorClipEnabled)
  120. return false;
  121. int32_t clippedX = pos.x - data->cursorClipRect.x;
  122. int32_t clippedY = pos.y - data->cursorClipRect.y;
  123. if(clippedX < 0)
  124. clippedX = data->cursorClipRect.x + data->cursorClipRect.width + clippedX;
  125. else if(clippedX >= data->cursorClipRect.width)
  126. clippedX = data->cursorClipRect.x + (clippedX - data->cursorClipRect.width);
  127. else
  128. clippedX = data->cursorClipRect.x + clippedX;
  129. if(clippedY < 0)
  130. clippedY = data->cursorClipRect.y + data->cursorClipRect.height + clippedY;
  131. else if(clippedY >= data->cursorClipRect.height)
  132. clippedY = data->cursorClipRect.y + (clippedY - data->cursorClipRect.height);
  133. else
  134. clippedY = data->cursorClipRect.y + clippedY;
  135. if(clippedX != pos.x || clippedY != pos.y)
  136. {
  137. pos.x = clippedX;
  138. pos.y = clippedY;
  139. return true;
  140. }
  141. return false;
  142. }
  143. void setCurrentCursor(Platform::Pimpl* data, ::Cursor cursor)
  144. {
  145. if(data->currentCursor)
  146. XFreeCursor(data->xDisplay, data->currentCursor);
  147. data->currentCursor = cursor;
  148. for(auto& entry : data->windowMap)
  149. applyCurrentCursor(data, entry.first);
  150. }
  151. Platform::Pimpl* Platform::mData = bs_new<Platform::Pimpl>();
  152. Platform::~Platform()
  153. { }
  154. Vector2I Platform::getCursorPosition()
  155. {
  156. Lock lock(mData->lock);
  157. return _getCursorPosition(mData);
  158. }
  159. void Platform::setCursorPosition(const Vector2I& screenPos)
  160. {
  161. Lock lock(mData->lock);
  162. _setCursorPosition(mData, screenPos);
  163. }
  164. void Platform::captureMouse(const RenderWindow& window)
  165. {
  166. Lock lock(mData->lock);
  167. LinuxWindow* linuxWindow;
  168. window.getCustomAttribute("WINDOW", &linuxWindow);
  169. uint32_t mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask | FocusChangeMask;
  170. XGrabPointer(mData->xDisplay, linuxWindow->_getXWindow(), False, mask, GrabModeAsync,
  171. GrabModeAsync, None, None, CurrentTime);
  172. XSync(mData->xDisplay, False);
  173. }
  174. void Platform::releaseMouseCapture()
  175. {
  176. Lock lock(mData->lock);
  177. XUngrabPointer(mData->xDisplay, CurrentTime);
  178. XSync(mData->xDisplay, False);
  179. }
  180. bool Platform::isPointOverWindow(const RenderWindow& window, const Vector2I& screenPos)
  181. {
  182. Lock lock(mData->lock);
  183. LinuxWindow* linuxWindow;
  184. window.getCustomAttribute("WINDOW", &linuxWindow);
  185. ::Window xWindow = linuxWindow->_getXWindow();
  186. Vector2I pos;
  187. UINT32 screenCount = (UINT32)XScreenCount(mData->xDisplay);
  188. for (UINT32 i = 0; i < screenCount; ++i)
  189. {
  190. ::Window outRoot, outChild;
  191. INT32 childX, childY;
  192. UINT32 mask;
  193. if(XQueryPointer(mData->xDisplay, XRootWindow(mData->xDisplay, i), &outRoot, &outChild, &pos.x,
  194. &pos.y, &childX, &childY, &mask))
  195. {
  196. return outChild == xWindow;
  197. }
  198. }
  199. return false;
  200. }
  201. void Platform::hideCursor()
  202. {
  203. Lock lock(mData->lock);
  204. mData->isCursorHidden = true;
  205. for(auto& entry : mData->windowMap)
  206. applyCurrentCursor(mData, entry.first);
  207. }
  208. void Platform::showCursor()
  209. {
  210. Lock lock(mData->lock);
  211. mData->isCursorHidden = false;
  212. for(auto& entry : mData->windowMap)
  213. applyCurrentCursor(mData, entry.first);
  214. }
  215. bool Platform::isCursorHidden()
  216. {
  217. Lock lock(mData->lock);
  218. return mData->isCursorHidden;
  219. }
  220. void Platform::clipCursorToWindow(const RenderWindow& window)
  221. {
  222. Lock lock(mData->lock);
  223. LinuxWindow* linuxWindow;
  224. window.getCustomAttribute("WINDOW", &linuxWindow);
  225. mData->cursorClipEnabled = true;
  226. mData->cursorClipWindow = linuxWindow;
  227. updateClipBounds(mData, linuxWindow);
  228. Vector2I pos = _getCursorPosition(mData);
  229. if(clipCursor(mData, pos))
  230. _setCursorPosition(mData, pos);
  231. }
  232. void Platform::clipCursorToRect(const Rect2I& screenRect)
  233. {
  234. Lock lock(mData->lock);
  235. mData->cursorClipEnabled = true;
  236. mData->cursorClipRect = screenRect;
  237. mData->cursorClipWindow = nullptr;
  238. Vector2I pos = _getCursorPosition(mData);
  239. if(clipCursor(mData, pos))
  240. _setCursorPosition(mData, pos);
  241. }
  242. void Platform::clipCursorDisable()
  243. {
  244. Lock lock(mData->lock);
  245. mData->cursorClipEnabled = false;
  246. mData->cursorClipWindow = None;
  247. }
  248. void Platform::setCursor(PixelData& pixelData, const Vector2I& hotSpot)
  249. {
  250. SPtr<PixelData> bgraData = PixelData::create(pixelData.getWidth(), pixelData.getHeight(), 1, PF_BGRA8);
  251. PixelUtil::bulkPixelConversion(pixelData, *bgraData);
  252. Lock lock(mData->lock);
  253. XcursorImage* image = XcursorImageCreate((int)bgraData->getWidth(), (int)bgraData->getHeight());
  254. image->xhot = hotSpot.x;
  255. image->yhot = hotSpot.y;
  256. image->delay = 0;
  257. memcpy(image->pixels, bgraData->getData(), bgraData->getSize());
  258. ::Cursor cursor = XcursorImageLoadCursor(mData->xDisplay, image);
  259. XcursorImageDestroy(image);
  260. setCurrentCursor(mData, cursor);
  261. }
  262. void Platform::setIcon(const PixelData& pixelData)
  263. {
  264. SPtr<PixelData> resizedData = PixelData::create(32, 32, 1, PF_RGBA8);
  265. PixelUtil::scale(pixelData, *resizedData);
  266. if(!mData->mainXWindow)
  267. return;
  268. auto iterFind = mData->windowMap.find(mData->mainXWindow);
  269. if(iterFind == mData->windowMap.end())
  270. return;
  271. LinuxWindow* mainLinuxWindow = iterFind->second;
  272. Lock lock(mData->lock);
  273. mainLinuxWindow->setIcon(pixelData);
  274. }
  275. void Platform::setCaptionNonClientAreas(const ct::RenderWindow& window, const Vector<Rect2I>& nonClientAreas)
  276. {
  277. if(nonClientAreas.size() == 0)
  278. return;
  279. Lock lock(mData->lock);
  280. LinuxWindow* linuxWindow;
  281. window.getCustomAttribute("WINDOW", &linuxWindow);
  282. // Note: Only supporting a single area
  283. linuxWindow->_setDragZone(nonClientAreas[0]);
  284. }
  285. void Platform::setResizeNonClientAreas(const ct::RenderWindow& window, const Vector<NonClientResizeArea>& nonClientAreas)
  286. {
  287. // Do nothing, resize areas not supported on Linux (but they are provided even on undecorated windows by the WM)
  288. }
  289. void Platform::resetNonClientAreas(const ct::RenderWindow& window)
  290. {
  291. // Do nothing, resize areas not supported on Linux (but they are provided even on undecorated windows by the WM)
  292. }
  293. void Platform::sleep(UINT32 duration)
  294. {
  295. usleep(duration * 1000);
  296. }
  297. OSDropTarget& Platform::createDropTarget(const RenderWindow* window, INT32 x, INT32 y, UINT32 width, UINT32 height)
  298. {
  299. return LinuxDragAndDrop::createDropTarget(window, x, y, width, height);
  300. }
  301. void Platform::destroyDropTarget(OSDropTarget& target)
  302. {
  303. LinuxDragAndDrop::destroyDropTarget(target);
  304. }
  305. void Platform::copyToClipboard(const WString& string)
  306. {
  307. Lock lock(mData->lock);
  308. mData->clipboardData = string;
  309. Atom clipboardAtom = XInternAtom(mData->xDisplay, "CLIPBOARD", 0);
  310. XSetSelectionOwner(mData->xDisplay, clipboardAtom, mData->mainXWindow, CurrentTime);
  311. }
  312. WString Platform::copyFromClipboard()
  313. {
  314. Lock lock(mData->lock);
  315. Atom clipboardAtom = XInternAtom(mData->xDisplay, "CLIPBOARD", 0);
  316. ::Window selOwner = XGetSelectionOwner(mData->xDisplay, clipboardAtom);
  317. if(selOwner == None)
  318. return L"";
  319. if(selOwner == mData->mainXWindow)
  320. return mData->clipboardData;
  321. XConvertSelection(mData->xDisplay, clipboardAtom, XA_STRING, clipboardAtom, mData->mainXWindow,
  322. CurrentTime);
  323. XFlush(mData->xDisplay);
  324. // Note: This might discard events if there are any in between the one we need. Ideally we let the
  325. // processEvents() handle them
  326. while(true)
  327. {
  328. XEvent event;
  329. XNextEvent(mData->xDisplay, &event);
  330. if(event.type == SelectionNotify && event.xselection.requestor == mData->mainXWindow)
  331. break;
  332. }
  333. Atom actualType;
  334. INT32 actualFormat;
  335. unsigned long length;
  336. unsigned long bytesRemaining;
  337. UINT8* data;
  338. XGetWindowProperty(mData->xDisplay, mData->mainXWindow, clipboardAtom,
  339. 0, 0, False, AnyPropertyType, &actualType, &actualFormat, &length, &bytesRemaining, &data);
  340. if(bytesRemaining > 0)
  341. {
  342. unsigned long unused;
  343. INT32 result = XGetWindowProperty(mData->xDisplay, mData->mainXWindow, clipboardAtom,
  344. 0, bytesRemaining, False, AnyPropertyType, &actualType, &actualFormat, &length,
  345. &unused, &data);
  346. if(result == Success)
  347. return UTF8::toWide(String((const char*)data));
  348. XFree(data);
  349. }
  350. return L"";
  351. }
  352. WString Platform::keyCodeToUnicode(UINT32 keyCode)
  353. {
  354. Lock lock(mData->lock);
  355. // TODOPORT
  356. return L"";
  357. }
  358. void Platform::_messagePump()
  359. {
  360. while(true)
  361. {
  362. Lock lock(mData->lock);
  363. if(XPending(mData->xDisplay) <= 0)
  364. break;
  365. XEvent event;
  366. XNextEvent(mData->xDisplay, &event);
  367. switch (event.type)
  368. {
  369. case ClientMessage:
  370. {
  371. if(LinuxDragAndDrop::handleClientMessage(event.xclient))
  372. break;
  373. if(event.xclient.data.l[0] == mData->atomDeleteWindow)
  374. XDestroyWindow(mData->xDisplay, event.xclient.window);
  375. }
  376. break;
  377. case DestroyNotify:
  378. {
  379. auto iterFind = mData->windowMap.find(event.xdestroywindow.window);
  380. if (iterFind == mData->windowMap.end())
  381. break;
  382. LinuxWindow* window = iterFind->second;
  383. window->_cleanUp();
  384. if(mData->mainXWindow == 0)
  385. return;
  386. }
  387. break;
  388. case KeyPress:
  389. {
  390. // Process text input
  391. //// Check if input manager wants this event. If not, we process it.
  392. if(!XFilterEvent(&event, None))
  393. {
  394. Status status;
  395. char buffer[16];
  396. INT32 length = Xutf8LookupString(mData->IC, &event.xkey, buffer, sizeof(buffer), nullptr,
  397. &status);
  398. if(length > 0)
  399. {
  400. buffer[length] = '\0';
  401. U32String utfStr = UTF8::toUTF32(String(buffer));
  402. if(utfStr.length() > 0)
  403. onCharInput((UINT32)utfStr[0]);
  404. }
  405. }
  406. // Process normal key press
  407. {
  408. static XComposeStatus keyboard;
  409. uint8_t buffer[16];
  410. KeySym symbol;
  411. XLookupString(&event.xkey, (char*)buffer, sizeof(buffer), &symbol, &keyboard);
  412. bool alt = event.xkey.state & Mod1Mask;
  413. bool control = event.xkey.state & ControlMask;
  414. bool shift = event.xkey.state & ShiftMask;
  415. // TODOPORT - Report key press
  416. }
  417. }
  418. break;
  419. case KeyRelease:
  420. {
  421. uint8_t buffer[16];
  422. KeySym symbol;
  423. XLookupString(&event.xkey, (char *) buffer, sizeof(buffer), &symbol, nullptr);
  424. bool alt = (event.xkey.state & Mod1Mask) != 0;
  425. bool control = (event.xkey.state & ControlMask) != 0;
  426. bool shift = (event.xkey.state & ShiftMask) != 0;
  427. // TODOPORT - Report key release
  428. }
  429. break;
  430. case ButtonPress:
  431. {
  432. UINT32 button = event.xbutton.button;
  433. OSMouseButton mouseButton;
  434. bool validPress = false;
  435. switch(button)
  436. {
  437. case Button1:
  438. mouseButton = OSMouseButton::Left;
  439. validPress = true;
  440. break;
  441. case Button2:
  442. mouseButton = OSMouseButton::Middle;
  443. validPress = true;
  444. break;
  445. case Button3:
  446. mouseButton = OSMouseButton::Right;
  447. validPress = true;
  448. break;
  449. default:
  450. break;
  451. }
  452. if(validPress)
  453. {
  454. // Send event
  455. Vector2I pos;
  456. pos.x = event.xbutton.x_root;
  457. pos.y = event.xbutton.y_root;
  458. OSPointerButtonStates btnStates;
  459. btnStates.ctrl = (event.xbutton.state & ControlMask) != 0;
  460. btnStates.shift = (event.xbutton.state & ShiftMask) != 0;
  461. btnStates.mouseButtons[0] = (event.xbutton.state & Button1Mask) != 0;
  462. btnStates.mouseButtons[1] = (event.xbutton.state & Button2Mask) != 0;
  463. btnStates.mouseButtons[2] = (event.xbutton.state & Button3Mask) != 0;
  464. onCursorButtonPressed(pos, mouseButton, btnStates);
  465. // Handle double-click
  466. if(button == Button1)
  467. {
  468. if (event.xbutton.time < (mData->lastButtonPressTime + DOUBLE_CLICK_MS))
  469. {
  470. onCursorDoubleClick(pos, btnStates);
  471. mData->lastButtonPressTime = 0;
  472. }
  473. else
  474. mData->lastButtonPressTime = event.xbutton.time;
  475. }
  476. }
  477. // Handle window dragging for windows without a title bar
  478. if(button == Button1)
  479. {
  480. auto iterFind = mData->windowMap.find(event.xbutton.window);
  481. if (iterFind != mData->windowMap.end())
  482. {
  483. LinuxWindow* window = iterFind->second;
  484. window->_dragStart(event.xbutton.x, event.xbutton.y);
  485. }
  486. }
  487. break;
  488. }
  489. case ButtonRelease:
  490. {
  491. UINT32 button = event.xbutton.button;
  492. Vector2I pos;
  493. pos.x = event.xbutton.x_root;
  494. pos.y = event.xbutton.y_root;
  495. OSPointerButtonStates btnStates;
  496. btnStates.ctrl = (event.xbutton.state & ControlMask) != 0;
  497. btnStates.shift = (event.xbutton.state & ShiftMask) != 0;
  498. btnStates.mouseButtons[0] = (event.xbutton.state & Button1Mask) != 0;
  499. btnStates.mouseButtons[1] = (event.xbutton.state & Button2Mask) != 0;
  500. btnStates.mouseButtons[2] = (event.xbutton.state & Button3Mask) != 0;
  501. switch(button)
  502. {
  503. case Button1:
  504. onCursorButtonReleased(pos, OSMouseButton::Left, btnStates);
  505. break;
  506. case Button2:
  507. onCursorButtonReleased(pos, OSMouseButton::Middle, btnStates);
  508. break;
  509. case Button3:
  510. onCursorButtonReleased(pos, OSMouseButton::Right, btnStates);
  511. break;
  512. case Button4: // Vertical mouse wheel
  513. case Button5:
  514. {
  515. INT32 delta = button == Button4 ? 1 : -1;
  516. onMouseWheelScrolled((float)delta);
  517. }
  518. break;
  519. default:
  520. break;
  521. }
  522. // Handle window dragging for windows without a title bar
  523. if(button == Button1)
  524. {
  525. auto iterFind = mData->windowMap.find(event.xbutton.window);
  526. if (iterFind != mData->windowMap.end())
  527. {
  528. LinuxWindow* window = iterFind->second;
  529. window->_dragEnd();
  530. }
  531. }
  532. break;
  533. }
  534. case MotionNotify:
  535. {
  536. Vector2I pos;
  537. pos.x = event.xmotion.x_root;
  538. pos.y = event.xmotion.y_root;
  539. // Handle clipping if enabled
  540. if(clipCursor(mData, pos))
  541. _setCursorPosition(mData, pos);
  542. // Send event
  543. OSPointerButtonStates btnStates;
  544. btnStates.ctrl = (event.xmotion.state & ControlMask) != 0;
  545. btnStates.shift = (event.xmotion.state & ShiftMask) != 0;
  546. btnStates.mouseButtons[0] = (event.xmotion.state & Button1Mask) != 0;
  547. btnStates.mouseButtons[1] = (event.xmotion.state & Button2Mask) != 0;
  548. btnStates.mouseButtons[2] = (event.xmotion.state & Button3Mask) != 0;
  549. onCursorMoved(pos, btnStates);
  550. // Handle window dragging for windows without a title bar
  551. auto iterFind = mData->windowMap.find(event.xmotion.window);
  552. if (iterFind != mData->windowMap.end())
  553. {
  554. LinuxWindow* window = iterFind->second;
  555. window->_dragUpdate(event.xmotion.x, event.xmotion.y);
  556. }
  557. }
  558. break;
  559. case EnterNotify:
  560. if(event.xcrossing.mode == NotifyNormal)
  561. {
  562. // TODOPORT - Send mouse enter event
  563. }
  564. break;
  565. case LeaveNotify:
  566. if(event.xcrossing.mode == NotifyNormal)
  567. {
  568. Vector2I pos;
  569. pos.x = event.xcrossing.x_root;
  570. pos.y = event.xcrossing.y_root;
  571. if(clipCursor(mData, pos))
  572. _setCursorPosition(mData, pos);
  573. // TODOPORT - Send mouse leave event
  574. }
  575. break;
  576. case ConfigureNotify:
  577. {
  578. const XConfigureEvent &xce = event.xconfigure;
  579. auto iterFind = mData->windowMap.find(event.xdestroywindow.window);
  580. if (iterFind == mData->windowMap.end())
  581. break;
  582. LinuxWindow* window = iterFind->second;
  583. updateClipBounds(mData, window);
  584. // TODOPORT - Send move/resize event
  585. }
  586. break;
  587. case FocusIn:
  588. // Update input context focus
  589. XSetICFocus(mData->IC);
  590. break;
  591. case FocusOut:
  592. // Update input context focus
  593. XUnsetICFocus(mData->IC);
  594. break;
  595. case SelectionNotify:
  596. LinuxDragAndDrop::handleSelectionNotify(event.xselection);
  597. break;
  598. case SelectionRequest:
  599. {
  600. // Send the data saved by the last clipboard copy operation
  601. Atom compoundTextAtom = XInternAtom(mData->xDisplay, "COMPOUND_TEXT", 0);
  602. Atom utf8StringAtom = XInternAtom(mData->xDisplay, "UTF8_STRING", 0);
  603. Atom targetsAtom = XInternAtom(mData->xDisplay, "TARGETS", 0);
  604. XSelectionRequestEvent& selReq = event.xselectionrequest;
  605. XEvent response;
  606. if(selReq.target == XA_STRING || selReq.target == compoundTextAtom || selReq.target == utf8StringAtom)
  607. {
  608. String utf8data = UTF8::fromWide(mData->clipboardData);
  609. const UINT8* data = (const UINT8*)utf8data.c_str();
  610. INT32 dataLength = (INT32)utf8data.length();
  611. XChangeProperty(mData->xDisplay, selReq.requestor, selReq.property,
  612. selReq.target, 8, PropModeReplace, data, dataLength);
  613. response.xselection.property = selReq.property;
  614. }
  615. else if(selReq.target == targetsAtom)
  616. {
  617. Atom data[2];
  618. data[0] = utf8StringAtom;
  619. data[1] = XA_STRING;
  620. XChangeProperty (mData->xDisplay, selReq.requestor, selReq.property, selReq.target,
  621. 8, PropModeReplace, (unsigned char*)&data, sizeof (data));
  622. response.xselection.property = selReq.property;
  623. }
  624. else
  625. {
  626. response.xselection.property = None;
  627. }
  628. response.xselection.type = SelectionNotify;
  629. response.xselection.display = selReq.display;
  630. response.xselection.requestor = selReq.requestor;
  631. response.xselection.selection = selReq.selection;
  632. response.xselection.target = selReq.target;
  633. response.xselection.time = selReq.time;
  634. XSendEvent (mData->xDisplay, selReq.requestor, 0, 0, &response);
  635. XFlush (mData->xDisplay);
  636. }
  637. break;
  638. default:
  639. break;
  640. }
  641. }
  642. }
  643. void Platform::_startUp()
  644. {
  645. Lock lock(mData->lock);
  646. mData->xDisplay = XOpenDisplay(nullptr);
  647. if(XSupportsLocale())
  648. {
  649. XSetLocaleModifiers("");
  650. mData->IM = XOpenIM(mData->xDisplay, nullptr, nullptr, nullptr);
  651. // Note: Currently our windows don't support pre-edit and status areas, which are used for more complex types
  652. // of character input. Later on it might be beneficial to support them.
  653. mData->IC = XCreateIC(mData->IM, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, nullptr);
  654. }
  655. mData->atomDeleteWindow = XInternAtom(mData->xDisplay, "WM_DELETE_WINDOW", False);
  656. // Drag and drop
  657. LinuxDragAndDrop::startUp(mData->xDisplay);
  658. // Create empty cursor
  659. char data[1];
  660. memset(data, 0, sizeof(data));
  661. Pixmap pixmap = XCreateBitmapFromData(mData->xDisplay, DefaultRootWindow(mData->xDisplay), data, 1, 1);
  662. XColor color;
  663. color.red = color.green = color.blue = 0;
  664. mData->emptyCursor = XCreatePixmapCursor(mData->xDisplay, pixmap, pixmap, &color, &color, 0, 0);
  665. XFreePixmap(mData->xDisplay, pixmap);
  666. }
  667. void Platform::_update()
  668. {
  669. LinuxDragAndDrop::update();
  670. }
  671. void Platform::_coreUpdate()
  672. {
  673. _messagePump();
  674. }
  675. void Platform::_shutDown()
  676. {
  677. Lock lock(mData->lock);
  678. // Free empty cursor
  679. XFreeCursor(mData->xDisplay, mData->emptyCursor);
  680. mData->emptyCursor = None;
  681. // Shutdown drag and drop
  682. LinuxDragAndDrop::shutDown();
  683. if(mData->IC)
  684. {
  685. XDestroyIC(mData->IC);
  686. mData->IC = 0;
  687. }
  688. if(mData->IM)
  689. {
  690. XCloseIM(mData->IM);
  691. mData->IM = 0;
  692. }
  693. XCloseDisplay(mData->xDisplay);
  694. mData->xDisplay = nullptr;
  695. bs_delete(mData);
  696. mData = nullptr;
  697. }
  698. ::Display* LinuxPlatform::getXDisplay()
  699. {
  700. return mData->xDisplay;
  701. }
  702. ::Window LinuxPlatform::getMainXWindow()
  703. {
  704. return mData->mainXWindow;
  705. }
  706. void LinuxPlatform::lockX()
  707. {
  708. mData->lock.lock();
  709. }
  710. void LinuxPlatform::unlockX()
  711. {
  712. mData->lock.unlock();
  713. }
  714. void LinuxPlatform::_registerWindow(::Window xWindow, LinuxWindow* window)
  715. {
  716. // First window is assumed to be the main
  717. if(mData->mainXWindow == 0)
  718. {
  719. mData->mainXWindow = xWindow;
  720. // Input context client window must be set before use
  721. XSetICValues(mData->IC,
  722. XNClientWindow, xWindow,
  723. XNFocusWindow, xWindow,
  724. nullptr);
  725. }
  726. mData->windowMap[xWindow] = window;
  727. applyCurrentCursor(mData, xWindow);
  728. }
  729. void LinuxPlatform::_unregisterWindow(::Window xWindow)
  730. {
  731. auto iterFind = mData->windowMap.find(xWindow);
  732. if(iterFind != mData->windowMap.end())
  733. {
  734. if(mData->cursorClipEnabled && mData->cursorClipWindow == iterFind->second)
  735. clipCursorDisable();
  736. mData->windowMap.erase(iterFind);
  737. }
  738. if(mData->mainXWindow == xWindow)
  739. mData->mainXWindow = 0;
  740. }
  741. Pixmap LinuxPlatform::createPixmap(const PixelData& data)
  742. {
  743. SPtr<PixelData> bgraData = PixelData::create(data.getWidth(), data.getHeight(), 1, PF_BGRA8);
  744. PixelUtil::bulkPixelConversion(data, *bgraData);
  745. UINT32 depth = (UINT32)XDefaultDepth(mData->xDisplay, 0);
  746. XImage* image = XCreateImage(mData->xDisplay, XDefaultVisual(mData->xDisplay, 0), depth, ZPixmap, 0,
  747. (char*)bgraData->getData(), data.getWidth(), data.getHeight(), 32, 0);
  748. Pixmap pixmap = XCreatePixmap(mData->xDisplay, XDefaultRootWindow(mData->xDisplay),
  749. data.getWidth(), data.getHeight(), depth);
  750. XGCValues gcValues;
  751. GC gc = XCreateGC(mData->xDisplay, pixmap, 0, &gcValues);
  752. XPutImage(mData->xDisplay, pixmap, gc, image, 0, 0, 0, 0, data.getWidth(), data.getHeight());
  753. XFreeGC(mData->xDisplay, gc);
  754. // Make sure XDestroyImage doesn't free the data pointed to by 'data.bytes'
  755. image->data = nullptr;
  756. XDestroyImage(image);
  757. return pixmap;
  758. }
  759. }