x86UNIXInput.client.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 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 "platform/gameInterface.h"
  27. #include "console/console.h"
  28. #include "platformX86UNIX/x86UNIXState.h"
  29. #include "platformX86UNIX/x86UNIXInputManager.h"
  30. #include <X11/Xlib.h>
  31. #include <X11/Xatom.h>
  32. #include <X11/keysym.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;
  66. // smActive is not maintained under unix. Use Input::isActive()
  67. // instead
  68. bool Input::smActive = false;
  69. // unix platform state
  70. extern x86UNIXPlatformState * x86UNIXState;
  71. extern AsciiData AsciiTable[NUM_KEYS];
  72. static XClipboard xclipboard;
  73. #ifdef LOG_INPUT
  74. S32 gInputLog = -1;
  75. #endif
  76. //------------------------------------------------------------------------------
  77. void Input::init()
  78. {
  79. Con::printf( "Input Init:" );
  80. destroy();
  81. #ifdef LOG_INPUT
  82. struct tm* newTime;
  83. time_t aclock;
  84. time( &aclock );
  85. newTime = localtime( &aclock );
  86. asctime( newTime );
  87. gInputLog = x86UNIXOpen("input.log", O_WRONLY | O_CREAT);
  88. log("Input log opened at %s\n", asctime( newTime ) );
  89. log("Operating System:\n" );
  90. log(" %s", UUtils->getOSName());
  91. log("\n");
  92. #endif
  93. smActive = false;
  94. smManager = NULL;
  95. UInputManager *uInputManager = new UInputManager;
  96. if ( !uInputManager->enable() )
  97. {
  98. Con::errorf( " Failed to enable Input Manager." );
  99. delete uInputManager;
  100. return;
  101. }
  102. uInputManager->init();
  103. smManager = uInputManager;
  104. Con::printf(" Input initialized");
  105. Con::printf(" ");
  106. }
  107. //------------------------------------------------------------------------------
  108. ConsoleFunction( isJoystickDetected, bool, 1, 1, "isJoystickDetected()" )
  109. {
  110. argc; argv;
  111. UInputManager* manager = dynamic_cast<UInputManager*>(Input::getManager());
  112. if (manager)
  113. return manager->joystickDetected();
  114. else
  115. return false;
  116. }
  117. //------------------------------------------------------------------------------
  118. ConsoleFunction( getJoystickAxes, const char*, 2, 2, "getJoystickAxes( instance )" )
  119. {
  120. argc; argv;
  121. UInputManager* manager = dynamic_cast<UInputManager*>(Input::getManager());
  122. if (manager)
  123. return manager->getJoystickAxesString(dAtoi(argv[1]));
  124. else
  125. return "";
  126. }
  127. //------------------------------------------------------------------------------
  128. U16 Input::getKeyCode( U16 asciiCode )
  129. {
  130. U16 keyCode = 0;
  131. U16 i;
  132. // This is done three times so the lowerkey will always
  133. // be found first. Some foreign keyboards have duplicate
  134. // chars on some keys.
  135. for ( i = KEY_FIRST; i < NUM_KEYS && !keyCode; i++ )
  136. {
  137. if ( AsciiTable[i].lower.ascii == asciiCode )
  138. {
  139. keyCode = i;
  140. break;
  141. };
  142. }
  143. for ( i = KEY_FIRST; i < NUM_KEYS && !keyCode; i++ )
  144. {
  145. if ( AsciiTable[i].upper.ascii == asciiCode )
  146. {
  147. keyCode = i;
  148. break;
  149. };
  150. }
  151. for ( i = KEY_FIRST; i < NUM_KEYS && !keyCode; i++ )
  152. {
  153. if ( AsciiTable[i].goofy.ascii == asciiCode )
  154. {
  155. keyCode = i;
  156. break;
  157. };
  158. }
  159. return( keyCode );
  160. }
  161. //-----------------------------------------------------------------------------
  162. //
  163. // This function gets the standard ASCII code corresponding to our key code
  164. // and the existing modifier key state.
  165. //
  166. //-----------------------------------------------------------------------------
  167. U16 Input::getAscii( U16 keyCode, KEY_STATE keyState )
  168. {
  169. if ( keyCode >= NUM_KEYS )
  170. return 0;
  171. switch ( keyState )
  172. {
  173. case STATE_LOWER:
  174. return AsciiTable[keyCode].lower.ascii;
  175. case STATE_UPPER:
  176. return AsciiTable[keyCode].upper.ascii;
  177. case STATE_GOOFY:
  178. return AsciiTable[keyCode].goofy.ascii;
  179. default:
  180. return(0);
  181. }
  182. }
  183. //------------------------------------------------------------------------------
  184. void Input::destroy()
  185. {
  186. #ifdef LOG_INPUT
  187. if ( gInputLog != -1 )
  188. {
  189. log( "*** CLOSING LOG ***\n" );
  190. x86UNIXClose(gInputLog);
  191. gInputLog = -1;
  192. }
  193. #endif
  194. if ( smManager && smManager->isEnabled() )
  195. {
  196. smManager->disable();
  197. delete smManager;
  198. smManager = NULL;
  199. }
  200. }
  201. //------------------------------------------------------------------------------
  202. bool Input::enable()
  203. {
  204. if ( smManager && !smManager->isEnabled() )
  205. return( smManager->enable() );
  206. return( false );
  207. }
  208. //------------------------------------------------------------------------------
  209. void Input::disable()
  210. {
  211. if ( smManager && smManager->isEnabled() )
  212. smManager->disable();
  213. }
  214. //------------------------------------------------------------------------------
  215. void Input::activate()
  216. {
  217. if ( smManager && smManager->isEnabled() && !isActive())
  218. {
  219. #ifdef LOG_INPUT
  220. Input::log( "Activating Input...\n" );
  221. #endif
  222. UInputManager* uInputManager = dynamic_cast<UInputManager*>( smManager );
  223. if ( uInputManager )
  224. uInputManager->activate();
  225. }
  226. }
  227. //------------------------------------------------------------------------------
  228. void Input::deactivate()
  229. {
  230. if ( smManager && smManager->isEnabled() && isActive() )
  231. {
  232. #ifdef LOG_INPUT
  233. Input::log( "Deactivating Input...\n" );
  234. #endif
  235. UInputManager* uInputManager = dynamic_cast<UInputManager*>( smManager );
  236. if ( uInputManager )
  237. uInputManager->deactivate();
  238. }
  239. }
  240. //------------------------------------------------------------------------------
  241. void Input::reactivate()
  242. {
  243. Input::deactivate();
  244. Input::activate();
  245. }
  246. //------------------------------------------------------------------------------
  247. bool Input::isEnabled()
  248. {
  249. if ( smManager )
  250. return smManager->isEnabled();
  251. return false;
  252. }
  253. //------------------------------------------------------------------------------
  254. bool Input::isActive()
  255. {
  256. UInputManager* uInputManager = dynamic_cast<UInputManager*>( smManager );
  257. if ( uInputManager )
  258. return uInputManager->isActive();
  259. return false;
  260. }
  261. //------------------------------------------------------------------------------
  262. void Input::process()
  263. {
  264. if ( smManager )
  265. smManager->process();
  266. }
  267. //------------------------------------------------------------------------------
  268. InputManager* Input::getManager()
  269. {
  270. return smManager;
  271. }
  272. #ifdef LOG_INPUT
  273. //------------------------------------------------------------------------------
  274. void Input::log( const char* format, ... )
  275. {
  276. if ( gInputLog == -1)
  277. return;
  278. va_list argptr;
  279. va_start( argptr, format );
  280. const int BufSize = 4096;
  281. char buffer[BufSize];
  282. dVsprintf( buffer, BufSize, format, argptr );
  283. x86UNIXWrite(gInputLog, buffer, dStrlen( buffer ));
  284. va_end( argptr );
  285. }
  286. ConsoleFunction( inputLog, void, 2, 2, "inputLog( string )" )
  287. {
  288. argc;
  289. Input::log( "%s\n", (const char*)argv[1] );
  290. }
  291. #endif // LOG_INPUT
  292. //------------------------------------------------------------------------------
  293. void NotifySelectionEvent(XEvent& event)
  294. {
  295. // somebody sent us a select event
  296. if (event.type == SelectionRequest)
  297. xclipboard.handleSelectionRequest(event.xselectionrequest);
  298. }
  299. //------------------------------------------------------------------------------
  300. const char* Platform::getClipboard()
  301. {
  302. return xclipboard.getClipboard();
  303. }
  304. //------------------------------------------------------------------------------
  305. bool Platform::setClipboard(const char *text)
  306. {
  307. return xclipboard.setClipboard(text);
  308. }
  309. //-----------------------------------------------------------------------------
  310. // XClipboard members
  311. XClipboard::XClipboard()
  312. {
  313. mInitialized = false;
  314. }
  315. //------------------------------------------------------------------------------
  316. XClipboard::~XClipboard()
  317. {
  318. freeXData();
  319. freeTData();
  320. }
  321. //------------------------------------------------------------------------------
  322. void XClipboard::init()
  323. {
  324. DisplayPtrManager xdisplay;
  325. Display* display = xdisplay.getDisplayPointer();
  326. mClipboardProperty = XInternAtom(display,
  327. "TORQUE_CLIPBOARD_ATOM", False);
  328. mClipboard = XInternAtom(display, "CLIPBOARD",
  329. False);
  330. mPrimary = XA_PRIMARY; //XInternAtom(display, "PRIMARY", False);
  331. mXData = NULL;
  332. mTData = NULL;
  333. mTDataSize = 0;
  334. mInitialized = true;
  335. }
  336. //------------------------------------------------------------------------------
  337. inline void XClipboard::freeXData()
  338. {
  339. if (mXData != NULL)
  340. {
  341. XFree(mXData);
  342. mXData = NULL;
  343. }
  344. }
  345. //------------------------------------------------------------------------------
  346. inline void XClipboard::freeTData()
  347. {
  348. if (mTData != NULL)
  349. {
  350. dRealFree(mTData);
  351. mTData = NULL;
  352. mTDataSize = 0;
  353. }
  354. }
  355. //
  356. // JMQ: As you might expect, X clipboard usage is bizarre. I
  357. // found this document to be useful.
  358. //
  359. // http://www.freedesktop.org/standards/clipboards.txt
  360. //
  361. // JMQ: later note: programming the X clipboard is not just
  362. // bizarre, it SUCKS. No wonder so many apps have
  363. // clipboard problems.
  364. //
  365. //------------------------------------------------------------------------------
  366. const char* XClipboard::getClipboard()
  367. {
  368. DisplayPtrManager xdisplay;
  369. Display* display = xdisplay.getDisplayPointer();
  370. if (!mInitialized)
  371. init();
  372. // find the owner of the clipboard
  373. Atom targetSelection = mClipboard;
  374. Window clipOwner = XGetSelectionOwner(display,
  375. targetSelection);
  376. if (clipOwner == None)
  377. {
  378. // It seems like KDE/QT reads the clipboard but doesn't set it.
  379. // This is a bug, that supposedly will be fixed in QT3.
  380. // I tried working around this by using
  381. // PRIMARY instead of CLIPBOARD, but this has some nonintuitive
  382. // side effects. So, no pasting from KDE apps for now.
  383. //targetSelection = mPrimary;
  384. //clipOwner = XGetSelectionOwner(display, targetSelection);
  385. }
  386. if (clipOwner == None)
  387. // oh well
  388. return "";
  389. // request that the owner convert the selection to a string
  390. XConvertSelection(display, targetSelection,
  391. XA_STRING, mClipboardProperty, x86UNIXState->getWindow(), CurrentTime);
  392. // flush the output buffer to make sure the selection request event gets
  393. // sent now
  394. XFlush(display);
  395. XEvent xevent;
  396. // if our window is the current owner, (e.g. copy from one part of
  397. // torque and paste to another), then we just sent an event to our
  398. // window that won't get processed until we get back to the event
  399. // loop in x86Unixwindow. So look for selection request events in
  400. // the event queue immediately and handle them.
  401. while (XCheckTypedWindowEvent(display,
  402. x86UNIXState->getWindow(), SelectionRequest, &xevent))
  403. handleSelectionRequest(xevent.xselectionrequest);
  404. // poll for the SelectionNotify event for 5 seconds. in most cases
  405. // we should get the event very quickly
  406. U32 startTime = Platform::getRealMilliseconds();
  407. bool timeOut = false;
  408. while (!XCheckTypedWindowEvent(display,
  409. x86UNIXState->getWindow(), SelectionNotify, &xevent) &&
  410. !timeOut)
  411. {
  412. // we'll be spinning here, but who cares
  413. if ((Platform::getRealMilliseconds() - startTime) > 5000)
  414. timeOut = true;
  415. }
  416. if (timeOut)
  417. {
  418. Con::warnf(ConsoleLogEntry::General,
  419. "XClipboard: waited too long for owner to convert selection");
  420. return "";
  421. }
  422. if (xevent.xselection.property == None)
  423. return "";
  424. // free the X data from a previous get
  425. freeXData();
  426. // grab the string data from the property
  427. Atom actual_type;
  428. int actual_format;
  429. unsigned long bytes_after;
  430. unsigned long nitems;
  431. // query the property length the 250000 is "the length in 32-bit
  432. // multiples of the data to be retrieved". so we support up to a
  433. // million bytes of returned data.
  434. int numToRetrieve = 250000;
  435. int status = XGetWindowProperty(display,
  436. x86UNIXState->getWindow(),
  437. mClipboardProperty, 0, numToRetrieve, True, XA_STRING,
  438. &actual_type, &actual_format, &nitems, &bytes_after, &mXData);
  439. // we should have returned OK, with string type, 8bit data,
  440. // and > 0 items.
  441. if ((status != Success) || (actual_type != XA_STRING) ||
  442. (actual_format != 8) || (nitems == 0))
  443. return "";
  444. // if there is data left in the clipboard, warn about it
  445. if (bytes_after > 0)
  446. Con::warnf(ConsoleLogEntry::General,
  447. "XClipboard: some data was not retrieved");
  448. return reinterpret_cast<const char *>(mXData);
  449. }
  450. //------------------------------------------------------------------------------
  451. void XClipboard::checkTDataSize(S32 requestedSize)
  452. {
  453. if (mTDataSize < requestedSize)
  454. {
  455. freeTData();
  456. mTData = static_cast<char*>(dRealMalloc(sizeof(char) * requestedSize));
  457. AssertFatal(mTData, "unable to allocate clipboard buffer data!");
  458. mTDataSize = requestedSize;
  459. }
  460. }
  461. //------------------------------------------------------------------------------
  462. bool XClipboard::setClipboard(const char *text)
  463. {
  464. DisplayPtrManager xdisplay;
  465. Display* display = xdisplay.getDisplayPointer();
  466. if (!mInitialized)
  467. init();
  468. // get the length of the text
  469. S32 len = dStrlen(text) + 1;
  470. // reallocate the storage buffer if necessary
  471. checkTDataSize(len);
  472. // copy the data into the storage buffer
  473. dStrcpy(mTData, text);
  474. // tell X that we own the clipboard. (we'll get events
  475. // if an app tries to paste)
  476. XSetSelectionOwner(display, mClipboard,
  477. x86UNIXState->getWindow(), CurrentTime);
  478. return true;
  479. }
  480. //------------------------------------------------------------------------------
  481. void XClipboard::handleSelectionRequest(XSelectionRequestEvent& request)
  482. {
  483. DisplayPtrManager xdisplay;
  484. Display* display = xdisplay.getDisplayPointer();
  485. // init our response
  486. XSelectionEvent notify;
  487. notify.type = SelectionNotify;
  488. notify.display = display;
  489. notify.requestor = request.requestor;
  490. notify.selection = request.selection;
  491. notify.target = XA_STRING;
  492. notify.property = None;
  493. notify.time = CurrentTime;
  494. // make sure the owner is our window, and that the
  495. // requestor wants the clipboard
  496. if (request.owner == x86UNIXState->getWindow() &&
  497. request.selection == mClipboard)
  498. {
  499. notify.property = request.property;
  500. // check to see if they did not set the property
  501. if (notify.property == None)
  502. notify.property = mClipboardProperty;
  503. // get the length of the data in the clipboard
  504. S32 length = dStrlen(mTData);
  505. // set the property on the requestor window
  506. XChangeProperty(display, request.requestor,
  507. notify.property, XA_STRING,
  508. 8, PropModeReplace, reinterpret_cast<const unsigned char*>(mTData),
  509. length);
  510. }
  511. XSendEvent(display, notify.requestor, False, 0,
  512. reinterpret_cast<XEvent*>(&notify));
  513. // flush the output buffer to send the event now
  514. XFlush(display);
  515. }