x86UNIXInput.client.cpp 17 KB

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