x86UNIXInput.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "platformX86UNIX/platformX86UNIX.h"
  23. #include "platform/platformInput.h"
  24. #include "platform/platformVideo.h"
  25. #include "platform/event.h"
  26. #include "game/gameInterface.h"
  27. #include "console/console.h"
  28. #include "platformX86UNIX/x86UNIXState.h"
  29. #include "platformX86UNIX/x86UNIXInputManager.h"
  30. #include "platformX86UNIX/x86UNIXInput_ScriptBinding.h"
  31. #include <X11/Xlib.h>
  32. #include <X11/Xatom.h>
  33. #include <SDL/SDL.h>
  34. #ifdef LOG_INPUT
  35. #include <time.h>
  36. #include <stdarg.h>
  37. #include <fcntl.h>
  38. #include <platformX86UNIX/x86UNIXUtils.h>
  39. extern int x86UNIXOpen(const char *path, int oflag);
  40. extern int x86UNIXClose(int fd);
  41. extern ssize_t x86UNIXWrite(int fd, const void *buf, size_t nbytes);
  42. #endif
  43. class XClipboard
  44. {
  45. private:
  46. Atom mClipboardProperty;
  47. Atom mClipboard;
  48. Atom mPrimary;
  49. bool mInitialized;
  50. U8 *mXData;
  51. char *mTData;
  52. S32 mTDataSize;
  53. void init();
  54. void freeXData();
  55. void freeTData();
  56. void checkTDataSize(S32 requestedSize);
  57. public:
  58. XClipboard();
  59. ~XClipboard();
  60. bool setClipboard(const char *text);
  61. const char* getClipboard();
  62. void handleSelectionRequest(XSelectionRequestEvent& request);
  63. };
  64. // Static class variables:
  65. InputManager* Input::smManager; //= 0;
  66. CursorManager* Input::smCursorManager = 0;
  67. // smActive is not maintained under unix. Use Input::isActive()
  68. // instead
  69. bool Input::smActive = false;
  70. // unix platform state
  71. extern x86UNIXPlatformState * x86UNIXState;
  72. extern AsciiData AsciiTable[NUM_KEYS];
  73. static XClipboard xclipboard;
  74. #ifdef LOG_INPUT
  75. S32 gInputLog = -1;
  76. #endif
  77. //------------------------------------------------------------------------------
  78. void Input::init()
  79. {
  80. Con::printf( "Input Init:" );
  81. destroy();
  82. #ifdef LOG_INPUT
  83. struct tm* newTime;
  84. time_t aclock;
  85. time( &aclock );
  86. newTime = localtime( &aclock );
  87. asctime( newTime );
  88. gInputLog = x86UNIXOpen("input.log", O_WRONLY | O_CREAT);
  89. log("Input log opened at %s\n", asctime( newTime ) );
  90. log("Operating System:\n" );
  91. log(" %s", UUtils->getOSName());
  92. log("\n");
  93. #endif
  94. smActive = false;
  95. smManager = NULL;
  96. UInputManager *uInputManager = new UInputManager;
  97. if ( !uInputManager->enable() )
  98. {
  99. Con::errorf( " Failed to enable Input Manager." );
  100. delete uInputManager;
  101. return;
  102. }
  103. uInputManager->init();
  104. smManager = uInputManager;
  105. Con::printf(" Input initialized");
  106. Con::printf(" ");
  107. }
  108. //------------------------------------------------------------------------------
  109. U16 Input::getKeyCode( U16 asciiCode )
  110. {
  111. U16 keyCode = 0;
  112. U16 i;
  113. // This is done three times so the lowerkey will always
  114. // be found first. Some foreign keyboards have duplicate
  115. // chars on some keys.
  116. for ( i = KEY_FIRST; i < NUM_KEYS && !keyCode; i++ )
  117. {
  118. if ( AsciiTable[i].lower.ascii == asciiCode )
  119. {
  120. keyCode = i;
  121. break;
  122. };
  123. }
  124. for ( i = KEY_FIRST; i < NUM_KEYS && !keyCode; i++ )
  125. {
  126. if ( AsciiTable[i].upper.ascii == asciiCode )
  127. {
  128. keyCode = i;
  129. break;
  130. };
  131. }
  132. for ( i = KEY_FIRST; i < NUM_KEYS && !keyCode; i++ )
  133. {
  134. if ( AsciiTable[i].goofy.ascii == asciiCode )
  135. {
  136. keyCode = i;
  137. break;
  138. };
  139. }
  140. return( keyCode );
  141. }
  142. //-----------------------------------------------------------------------------
  143. //
  144. // This function gets the standard ASCII code corresponding to our key code
  145. // and the existing modifier key state.
  146. //
  147. //-----------------------------------------------------------------------------
  148. U16 Input::getAscii( U16 keyCode, KEY_STATE keyState )
  149. {
  150. if ( keyCode >= NUM_KEYS )
  151. return 0;
  152. switch ( keyState )
  153. {
  154. case STATE_LOWER:
  155. return AsciiTable[keyCode].lower.ascii;
  156. case STATE_UPPER:
  157. return AsciiTable[keyCode].upper.ascii;
  158. case STATE_GOOFY:
  159. return AsciiTable[keyCode].goofy.ascii;
  160. default:
  161. return(0);
  162. }
  163. }
  164. //------------------------------------------------------------------------------
  165. void Input::destroy()
  166. {
  167. #ifdef LOG_INPUT
  168. if ( gInputLog != -1 )
  169. {
  170. log( "*** CLOSING LOG ***\n" );
  171. x86UNIXClose(gInputLog);
  172. gInputLog = -1;
  173. }
  174. #endif
  175. if ( smManager && smManager->isEnabled() )
  176. {
  177. smManager->disable();
  178. delete smManager;
  179. smManager = NULL;
  180. }
  181. }
  182. //------------------------------------------------------------------------------
  183. bool Input::enable()
  184. {
  185. if ( smManager && !smManager->isEnabled() )
  186. return( smManager->enable() );
  187. return( false );
  188. }
  189. //------------------------------------------------------------------------------
  190. void Input::disable()
  191. {
  192. if ( smManager && smManager->isEnabled() )
  193. smManager->disable();
  194. }
  195. //------------------------------------------------------------------------------
  196. void Input::activate()
  197. {
  198. if ( smManager && smManager->isEnabled() && !isActive())
  199. {
  200. #ifdef LOG_INPUT
  201. Input::log( "Activating Input...\n" );
  202. #endif
  203. UInputManager* uInputManager = dynamic_cast<UInputManager*>( smManager );
  204. if ( uInputManager )
  205. uInputManager->activate();
  206. }
  207. }
  208. //------------------------------------------------------------------------------
  209. void Input::deactivate()
  210. {
  211. if ( smManager && smManager->isEnabled() && isActive() )
  212. {
  213. #ifdef LOG_INPUT
  214. Input::log( "Deactivating Input...\n" );
  215. #endif
  216. UInputManager* uInputManager = dynamic_cast<UInputManager*>( smManager );
  217. if ( uInputManager )
  218. uInputManager->deactivate();
  219. }
  220. }
  221. //------------------------------------------------------------------------------
  222. void Input::reactivate()
  223. {
  224. Input::deactivate();
  225. Input::activate();
  226. }
  227. //------------------------------------------------------------------------------
  228. bool Input::isEnabled()
  229. {
  230. if ( smManager )
  231. return smManager->isEnabled();
  232. return false;
  233. }
  234. //------------------------------------------------------------------------------
  235. bool Input::isActive()
  236. {
  237. UInputManager* uInputManager = dynamic_cast<UInputManager*>( smManager );
  238. if ( uInputManager )
  239. return uInputManager->isActive();
  240. return false;
  241. }
  242. //------------------------------------------------------------------------------
  243. void Input::process()
  244. {
  245. if ( smManager )
  246. smManager->process();
  247. }
  248. //------------------------------------------------------------------------------
  249. InputManager* Input::getManager()
  250. {
  251. return smManager;
  252. }
  253. //------------------------------------------------------------------------------
  254. void NotifySelectionEvent(XEvent& event)
  255. {
  256. // somebody sent us a select event
  257. if (event.type == SelectionRequest)
  258. xclipboard.handleSelectionRequest(event.xselectionrequest);
  259. }
  260. //------------------------------------------------------------------------------
  261. const char* Platform::getClipboard()
  262. {
  263. return xclipboard.getClipboard();
  264. }
  265. //------------------------------------------------------------------------------
  266. bool Platform::setClipboard(const char *text)
  267. {
  268. return xclipboard.setClipboard(text);
  269. }
  270. //-----------------------------------------------------------------------------
  271. // XClipboard members
  272. XClipboard::XClipboard()
  273. {
  274. mInitialized = false;
  275. }
  276. //------------------------------------------------------------------------------
  277. XClipboard::~XClipboard()
  278. {
  279. freeXData();
  280. freeTData();
  281. }
  282. //------------------------------------------------------------------------------
  283. void XClipboard::init()
  284. {
  285. DisplayPtrManager xdisplay;
  286. Display* display = xdisplay.getDisplayPointer();
  287. mClipboardProperty = XInternAtom(display,
  288. "TORQUE_CLIPBOARD_ATOM", False);
  289. mClipboard = XInternAtom(display, "CLIPBOARD",
  290. False);
  291. mPrimary = XA_PRIMARY; //XInternAtom(display, "PRIMARY", False);
  292. mXData = NULL;
  293. mTData = NULL;
  294. mTDataSize = 0;
  295. mInitialized = true;
  296. }
  297. //------------------------------------------------------------------------------
  298. inline void XClipboard::freeXData()
  299. {
  300. if (mXData != NULL)
  301. {
  302. XFree(mXData);
  303. mXData = NULL;
  304. }
  305. }
  306. //------------------------------------------------------------------------------
  307. inline void XClipboard::freeTData()
  308. {
  309. if (mTData != NULL)
  310. {
  311. dRealFree(mTData);
  312. mTData = NULL;
  313. mTDataSize = 0;
  314. }
  315. }
  316. //
  317. // JMQ: As you might expect, X clipboard usage is bizarre. I
  318. // found this document to be useful.
  319. //
  320. // http://www.freedesktop.org/standards/clipboards.txt
  321. //
  322. // JMQ: later note: programming the X clipboard is not just
  323. // bizarre, it SUCKS. No wonder so many apps have
  324. // clipboard problems.
  325. //
  326. //------------------------------------------------------------------------------
  327. const char* XClipboard::getClipboard()
  328. {
  329. DisplayPtrManager xdisplay;
  330. Display* display = xdisplay.getDisplayPointer();
  331. if (!mInitialized)
  332. init();
  333. // find the owner of the clipboard
  334. Atom targetSelection = mClipboard;
  335. Window clipOwner = XGetSelectionOwner(display,
  336. targetSelection);
  337. if (clipOwner == None)
  338. {
  339. // It seems like KDE/QT reads the clipboard but doesn't set it.
  340. // This is a bug, that supposedly will be fixed in QT3.
  341. // I tried working around this by using
  342. // PRIMARY instead of CLIPBOARD, but this has some nonintuitive
  343. // side effects. So, no pasting from KDE apps for now.
  344. //targetSelection = mPrimary;
  345. //clipOwner = XGetSelectionOwner(display, targetSelection);
  346. }
  347. if (clipOwner == None)
  348. // oh well
  349. return "";
  350. // request that the owner convert the selection to a string
  351. XConvertSelection(display, targetSelection,
  352. XA_STRING, mClipboardProperty, x86UNIXState->getWindow(), CurrentTime);
  353. // flush the output buffer to make sure the selection request event gets
  354. // sent now
  355. XFlush(display);
  356. XEvent xevent;
  357. // if our window is the current owner, (e.g. copy from one part of
  358. // torque and paste to another), then we just sent an event to our
  359. // window that won't get processed until we get back to the event
  360. // loop in x86Unixwindow. So look for selection request events in
  361. // the event queue immediately and handle them.
  362. while (XCheckTypedWindowEvent(display,
  363. x86UNIXState->getWindow(), SelectionRequest, &xevent))
  364. handleSelectionRequest(xevent.xselectionrequest);
  365. // poll for the SelectionNotify event for 5 seconds. in most cases
  366. // we should get the event very quickly
  367. U32 startTime = Platform::getRealMilliseconds();
  368. bool timeOut = false;
  369. while (!XCheckTypedWindowEvent(display,
  370. x86UNIXState->getWindow(), SelectionNotify, &xevent) &&
  371. !timeOut)
  372. {
  373. // we'll be spinning here, but who cares
  374. if ((Platform::getRealMilliseconds() - startTime) > 5000)
  375. timeOut = true;
  376. }
  377. if (timeOut)
  378. {
  379. Con::warnf(ConsoleLogEntry::General,
  380. "XClipboard: waited too long for owner to convert selection");
  381. return "";
  382. }
  383. if (xevent.xselection.property == None)
  384. return "";
  385. // free the X data from a previous get
  386. freeXData();
  387. // grab the string data from the property
  388. Atom actual_type;
  389. int actual_format;
  390. unsigned long bytes_after;
  391. unsigned long nitems;
  392. // query the property length the 250000 is "the length in 32-bit
  393. // multiples of the data to be retrieved". so we support up to a
  394. // million bytes of returned data.
  395. int numToRetrieve = 250000;
  396. int status = XGetWindowProperty(display,
  397. x86UNIXState->getWindow(),
  398. mClipboardProperty, 0, numToRetrieve, True, XA_STRING,
  399. &actual_type, &actual_format, &nitems, &bytes_after, &mXData);
  400. // we should have returned OK, with string type, 8bit data,
  401. // and > 0 items.
  402. if ((status != Success) || (actual_type != XA_STRING) ||
  403. (actual_format != 8) || (nitems == 0))
  404. return "";
  405. // if there is data left in the clipboard, warn about it
  406. if (bytes_after > 0)
  407. Con::warnf(ConsoleLogEntry::General,
  408. "XClipboard: some data was not retrieved");
  409. return reinterpret_cast<const char *>(mXData);
  410. }
  411. //------------------------------------------------------------------------------
  412. void XClipboard::checkTDataSize(S32 requestedSize)
  413. {
  414. if (mTDataSize < requestedSize)
  415. {
  416. freeTData();
  417. mTData = static_cast<char*>(dRealMalloc(sizeof(char) * requestedSize));
  418. AssertFatal(mTData, "unable to allocate clipboard buffer data!");
  419. mTDataSize = requestedSize;
  420. }
  421. }
  422. //------------------------------------------------------------------------------
  423. bool XClipboard::setClipboard(const char *text)
  424. {
  425. DisplayPtrManager xdisplay;
  426. Display* display = xdisplay.getDisplayPointer();
  427. if (!mInitialized)
  428. init();
  429. // get the length of the text
  430. S32 len = dStrlen(text) + 1;
  431. // reallocate the storage buffer if necessary
  432. checkTDataSize(len);
  433. // copy the data into the storage buffer
  434. dStrcpy(mTData, text);
  435. // tell X that we own the clipboard. (we'll get events
  436. // if an app tries to paste)
  437. XSetSelectionOwner(display, mClipboard,
  438. x86UNIXState->getWindow(), CurrentTime);
  439. return true;
  440. }
  441. //------------------------------------------------------------------------------
  442. void XClipboard::handleSelectionRequest(XSelectionRequestEvent& request)
  443. {
  444. DisplayPtrManager xdisplay;
  445. Display* display = xdisplay.getDisplayPointer();
  446. // init our response
  447. XSelectionEvent notify;
  448. notify.type = SelectionNotify;
  449. notify.display = display;
  450. notify.requestor = request.requestor;
  451. notify.selection = request.selection;
  452. notify.target = XA_STRING;
  453. notify.property = None;
  454. notify.time = CurrentTime;
  455. // make sure the owner is our window, and that the
  456. // requestor wants the clipboard
  457. if (request.owner == x86UNIXState->getWindow() &&
  458. request.selection == mClipboard)
  459. {
  460. notify.property = request.property;
  461. // check to see if they did not set the property
  462. if (notify.property == None)
  463. notify.property = mClipboardProperty;
  464. // get the length of the data in the clipboard
  465. S32 length = dStrlen(mTData);
  466. // set the property on the requestor window
  467. XChangeProperty(display, request.requestor,
  468. notify.property, XA_STRING,
  469. 8, PropModeReplace, reinterpret_cast<const unsigned char*>(mTData),
  470. length);
  471. }
  472. XSendEvent(display, notify.requestor, False, 0,
  473. reinterpret_cast<XEvent*>(&notify));
  474. // flush the output buffer to send the event now
  475. XFlush(display);
  476. }
  477. //------------------------------------------------------------------------------
  478. void Input::setCursorPos(S32 x, S32 y)
  479. {
  480. SDL_WarpMouse((S16)x, (S16)y);
  481. }
  482. void Input::pushCursor(S32 cursorID)
  483. {
  484. //CursorManager* cm = getCursorManager();
  485. //if (cm)
  486. // cm->pushCursor(cursorID);
  487. }
  488. void Input::popCursor()
  489. {
  490. //CursorManager* cm = getCursorManager();
  491. //if (cm)
  492. // cm->popCursor();
  493. }
  494. void Input::refreshCursor()
  495. {
  496. //CursorManager* cm = getCursorManager();
  497. //if (cm)
  498. // cm->refreshCursor();
  499. }
  500. void Input::setCursorState(bool on)
  501. {
  502. //SDL_ShowCursor(on ? SDL_ENABLE : SDL_DISABLE);
  503. }
  504. void Input::setCursorShape(U32 cursorID)
  505. {
  506. }
  507. U32 Input::getDoubleClickTime()
  508. {
  509. return 1000;
  510. }
  511. S32 Input::getDoubleClickWidth()
  512. {
  513. // Arbitrary value
  514. return 10;
  515. }
  516. S32 Input::getDoubleClickHeight()
  517. {
  518. return Input::getDoubleClickWidth();
  519. }