BsLinuxPlatform.cpp 23 KB

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