x86UNIXInput.client.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  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. }
  316. //------------------------------------------------------------------------------
  317. XClipboard::~XClipboard()
  318. {
  319. freeXData();
  320. freeTData();
  321. }
  322. //------------------------------------------------------------------------------
  323. void XClipboard::init()
  324. {
  325. DisplayPtrManager xdisplay;
  326. Display* display = xdisplay.getDisplayPointer();
  327. mClipboardProperty = XInternAtom(display,
  328. "TORQUE_CLIPBOARD_ATOM", False);
  329. mClipboard = XInternAtom(display, "CLIPBOARD",
  330. False);
  331. mPrimary = XA_PRIMARY; //XInternAtom(display, "PRIMARY", False);
  332. mXData = NULL;
  333. mTData = NULL;
  334. mTDataSize = 0;
  335. mInitialized = true;
  336. }
  337. //------------------------------------------------------------------------------
  338. inline void XClipboard::freeXData()
  339. {
  340. if (mXData != NULL)
  341. {
  342. XFree(mXData);
  343. mXData = NULL;
  344. }
  345. }
  346. //------------------------------------------------------------------------------
  347. inline void XClipboard::freeTData()
  348. {
  349. if (mTData != NULL)
  350. {
  351. dRealFree(mTData);
  352. mTData = NULL;
  353. mTDataSize = 0;
  354. }
  355. }
  356. //
  357. // JMQ: As you might expect, X clipboard usage is bizarre. I
  358. // found this document to be useful.
  359. //
  360. // http://www.freedesktop.org/standards/clipboards.txt
  361. //
  362. // JMQ: later note: programming the X clipboard is not just
  363. // bizarre, it SUCKS. No wonder so many apps have
  364. // clipboard problems.
  365. //
  366. //------------------------------------------------------------------------------
  367. const char* XClipboard::getClipboard()
  368. {
  369. DisplayPtrManager xdisplay;
  370. Display* display = xdisplay.getDisplayPointer();
  371. if (!mInitialized)
  372. init();
  373. // find the owner of the clipboard
  374. Atom targetSelection = mClipboard;
  375. Window clipOwner = XGetSelectionOwner(display,
  376. targetSelection);
  377. if (clipOwner == None)
  378. {
  379. // It seems like KDE/QT reads the clipboard but doesn't set it.
  380. // This is a bug, that supposedly will be fixed in QT3.
  381. // I tried working around this by using
  382. // PRIMARY instead of CLIPBOARD, but this has some nonintuitive
  383. // side effects. So, no pasting from KDE apps for now.
  384. //targetSelection = mPrimary;
  385. //clipOwner = XGetSelectionOwner(display, targetSelection);
  386. }
  387. if (clipOwner == None)
  388. // oh well
  389. return "";
  390. // request that the owner convert the selection to a string
  391. XConvertSelection(display, targetSelection,
  392. XA_STRING, mClipboardProperty, x86UNIXState->getWindow(), CurrentTime);
  393. // flush the output buffer to make sure the selection request event gets
  394. // sent now
  395. XFlush(display);
  396. XEvent xevent;
  397. // if our window is the current owner, (e.g. copy from one part of
  398. // torque and paste to another), then we just sent an event to our
  399. // window that won't get processed until we get back to the event
  400. // loop in x86Unixwindow. So look for selection request events in
  401. // the event queue immediately and handle them.
  402. while (XCheckTypedWindowEvent(display,
  403. x86UNIXState->getWindow(), SelectionRequest, &xevent))
  404. handleSelectionRequest(xevent.xselectionrequest);
  405. // poll for the SelectionNotify event for 5 seconds. in most cases
  406. // we should get the event very quickly
  407. U32 startTime = Platform::getRealMilliseconds();
  408. bool timeOut = false;
  409. while (!XCheckTypedWindowEvent(display,
  410. x86UNIXState->getWindow(), SelectionNotify, &xevent) &&
  411. !timeOut)
  412. {
  413. // we'll be spinning here, but who cares
  414. if ((Platform::getRealMilliseconds() - startTime) > 5000)
  415. timeOut = true;
  416. }
  417. if (timeOut)
  418. {
  419. Con::warnf(ConsoleLogEntry::General,
  420. "XClipboard: waited too long for owner to convert selection");
  421. return "";
  422. }
  423. if (xevent.xselection.property == None)
  424. return "";
  425. // free the X data from a previous get
  426. freeXData();
  427. // grab the string data from the property
  428. Atom actual_type;
  429. int actual_format;
  430. unsigned long bytes_after;
  431. unsigned long nitems;
  432. // query the property length the 250000 is "the length in 32-bit
  433. // multiples of the data to be retrieved". so we support up to a
  434. // million bytes of returned data.
  435. int numToRetrieve = 250000;
  436. int status = XGetWindowProperty(display,
  437. x86UNIXState->getWindow(),
  438. mClipboardProperty, 0, numToRetrieve, True, XA_STRING,
  439. &actual_type, &actual_format, &nitems, &bytes_after, &mXData);
  440. // we should have returned OK, with string type, 8bit data,
  441. // and > 0 items.
  442. if ((status != Success) || (actual_type != XA_STRING) ||
  443. (actual_format != 8) || (nitems == 0))
  444. return "";
  445. // if there is data left in the clipboard, warn about it
  446. if (bytes_after > 0)
  447. Con::warnf(ConsoleLogEntry::General,
  448. "XClipboard: some data was not retrieved");
  449. return reinterpret_cast<const char *>(mXData);
  450. }
  451. //------------------------------------------------------------------------------
  452. void XClipboard::checkTDataSize(S32 requestedSize)
  453. {
  454. if (mTDataSize < requestedSize)
  455. {
  456. freeTData();
  457. mTData = static_cast<char*>(dRealMalloc(sizeof(char) * requestedSize));
  458. AssertFatal(mTData, "unable to allocate clipboard buffer data!");
  459. mTDataSize = requestedSize;
  460. }
  461. }
  462. //------------------------------------------------------------------------------
  463. bool XClipboard::setClipboard(const char *text)
  464. {
  465. DisplayPtrManager xdisplay;
  466. Display* display = xdisplay.getDisplayPointer();
  467. if (!mInitialized)
  468. init();
  469. // get the length of the text
  470. S32 len = dStrlen(text) + 1;
  471. // reallocate the storage buffer if necessary
  472. checkTDataSize(len);
  473. // copy the data into the storage buffer
  474. dStrcpy(mTData, text);
  475. // tell X that we own the clipboard. (we'll get events
  476. // if an app tries to paste)
  477. XSetSelectionOwner(display, mClipboard,
  478. x86UNIXState->getWindow(), CurrentTime);
  479. return true;
  480. }
  481. //------------------------------------------------------------------------------
  482. void XClipboard::handleSelectionRequest(XSelectionRequestEvent& request)
  483. {
  484. DisplayPtrManager xdisplay;
  485. Display* display = xdisplay.getDisplayPointer();
  486. // init our response
  487. XSelectionEvent notify;
  488. notify.type = SelectionNotify;
  489. notify.display = display;
  490. notify.requestor = request.requestor;
  491. notify.selection = request.selection;
  492. notify.target = XA_STRING;
  493. notify.property = None;
  494. notify.time = CurrentTime;
  495. // make sure the owner is our window, and that the
  496. // requestor wants the clipboard
  497. if (request.owner == x86UNIXState->getWindow() &&
  498. request.selection == mClipboard)
  499. {
  500. notify.property = request.property;
  501. // check to see if they did not set the property
  502. if (notify.property == None)
  503. notify.property = mClipboardProperty;
  504. // get the length of the data in the clipboard
  505. S32 length = dStrlen(mTData);
  506. // set the property on the requestor window
  507. XChangeProperty(display, request.requestor,
  508. notify.property, XA_STRING,
  509. 8, PropModeReplace, reinterpret_cast<const unsigned char*>(mTData),
  510. length);
  511. }
  512. XSendEvent(display, notify.requestor, False, 0,
  513. reinterpret_cast<XEvent*>(&notify));
  514. // flush the output buffer to send the event now
  515. XFlush(display);
  516. }
  517. #endif