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