No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

x11platform.cpp 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727
  1. /* The MIT License
  2. *
  3. * Copyright (c) 2010 OTClient, https://github.com/edubart/otclient
  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 deal
  7. * in the Software without restriction, including without limitation the rights
  8. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. * 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 FROM,
  20. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. * THE SOFTWARE.
  22. */
  23. #include "platform.h"
  24. #include "engine.h"
  25. #include "input.h"
  26. #include "logger.h"
  27. #include <sys/time.h>
  28. #include <sys/stat.h>
  29. #include <errno.h>
  30. #include <X11/Xlib.h>
  31. #include <X11/Xatom.h>
  32. #include <GL/glx.h>
  33. #include <physfs.h>
  34. struct X11PlatformPrivate {
  35. Display *display;
  36. XVisualInfo *visual;
  37. GLXContext glxContext;
  38. XIM xim;
  39. XIC xic;
  40. Colormap colormap;
  41. Window window;
  42. Cursor cursor;
  43. Atom atomDeleteWindow;
  44. Atom atomClipboard;
  45. Atom atomTargets;
  46. Atom atomText;
  47. Atom atomCompoundText;
  48. Atom atomUTF8String;
  49. bool visible;
  50. bool focused;
  51. int width;
  52. int height;
  53. std::string clipboardText;
  54. std::map<int, unsigned char> keyMap;
  55. } x11;
  56. void Platform::init()
  57. {
  58. x11.display = NULL;
  59. x11.visual = NULL;
  60. x11.glxContext = NULL;
  61. x11.xim = NULL;
  62. x11.xic = NULL;
  63. x11.colormap = None;
  64. x11.window = None;
  65. x11.cursor = None;
  66. x11.visible = false;
  67. x11.focused = false;
  68. x11.width = 0;
  69. x11.height = 0;
  70. // setup keymap
  71. x11.keyMap[XK_1] = KC_1;
  72. x11.keyMap[XK_2] = KC_2;
  73. x11.keyMap[XK_3] = KC_3;
  74. x11.keyMap[XK_4] = KC_4;
  75. x11.keyMap[XK_5] = KC_5;
  76. x11.keyMap[XK_6] = KC_6;
  77. x11.keyMap[XK_7] = KC_7;
  78. x11.keyMap[XK_8] = KC_8;
  79. x11.keyMap[XK_9] = KC_9;
  80. x11.keyMap[XK_0] = KC_0;
  81. x11.keyMap[XK_BackSpace] = KC_BACK;
  82. x11.keyMap[XK_minus] = KC_MINUS;
  83. x11.keyMap[XK_equal] = KC_EQUALS;
  84. x11.keyMap[XK_space] = KC_SPACE;
  85. x11.keyMap[XK_comma] = KC_COMMA;
  86. x11.keyMap[XK_period] = KC_PERIOD;
  87. x11.keyMap[XK_backslash] = KC_BACKSLASH;
  88. x11.keyMap[XK_slash] = KC_SLASH;
  89. x11.keyMap[XK_bracketleft] = KC_LBRACKET;
  90. x11.keyMap[XK_bracketright] = KC_RBRACKET;
  91. x11.keyMap[XK_Escape] = KC_ESCAPE;
  92. x11.keyMap[XK_Caps_Lock] = KC_CAPITAL;
  93. x11.keyMap[XK_Tab] = KC_TAB;
  94. x11.keyMap[XK_Return] = KC_RETURN;
  95. x11.keyMap[XK_Control_L] = KC_LCONTROL;
  96. x11.keyMap[XK_Control_R] = KC_RCONTROL;
  97. x11.keyMap[XK_colon] = KC_COLON;
  98. x11.keyMap[XK_semicolon] = KC_SEMICOLON;
  99. x11.keyMap[XK_apostrophe] = KC_APOSTROPHE;
  100. x11.keyMap[XK_grave] = KC_GRAVE;
  101. x11.keyMap[XK_b] = KC_B;
  102. x11.keyMap[XK_a] = KC_A;
  103. x11.keyMap[XK_c] = KC_C;
  104. x11.keyMap[XK_d] = KC_D;
  105. x11.keyMap[XK_e] = KC_E;
  106. x11.keyMap[XK_f] = KC_F;
  107. x11.keyMap[XK_g] = KC_G;
  108. x11.keyMap[XK_h] = KC_H;
  109. x11.keyMap[XK_i] = KC_I;
  110. x11.keyMap[XK_j] = KC_J;
  111. x11.keyMap[XK_k] = KC_K;
  112. x11.keyMap[XK_l] = KC_L;
  113. x11.keyMap[XK_m] = KC_M;
  114. x11.keyMap[XK_n] = KC_N;
  115. x11.keyMap[XK_o] = KC_O;
  116. x11.keyMap[XK_p] = KC_P;
  117. x11.keyMap[XK_q] = KC_Q;
  118. x11.keyMap[XK_r] = KC_R;
  119. x11.keyMap[XK_s] = KC_S;
  120. x11.keyMap[XK_t] = KC_T;
  121. x11.keyMap[XK_u] = KC_U;
  122. x11.keyMap[XK_v] = KC_V;
  123. x11.keyMap[XK_w] = KC_W;
  124. x11.keyMap[XK_x] = KC_X;
  125. x11.keyMap[XK_y] = KC_Y;
  126. x11.keyMap[XK_z] = KC_Z;
  127. x11.keyMap[XK_F1] = KC_F1;
  128. x11.keyMap[XK_F2] = KC_F2;
  129. x11.keyMap[XK_F3] = KC_F3;
  130. x11.keyMap[XK_F4] = KC_F4;
  131. x11.keyMap[XK_F5] = KC_F5;
  132. x11.keyMap[XK_F6] = KC_F6;
  133. x11.keyMap[XK_F7] = KC_F7;
  134. x11.keyMap[XK_F8] = KC_F8;
  135. x11.keyMap[XK_F9] = KC_F9;
  136. x11.keyMap[XK_F10] = KC_F10;
  137. x11.keyMap[XK_F11] = KC_F11;
  138. x11.keyMap[XK_F12] = KC_F12;
  139. x11.keyMap[XK_F13] = KC_F13;
  140. x11.keyMap[XK_F14] = KC_F14;
  141. x11.keyMap[XK_F15] = KC_F15;
  142. // keypad
  143. x11.keyMap[XK_KP_0] = KC_NUMPAD0;
  144. x11.keyMap[XK_KP_1] = KC_NUMPAD1;
  145. x11.keyMap[XK_KP_2] = KC_NUMPAD2;
  146. x11.keyMap[XK_KP_3] = KC_NUMPAD3;
  147. x11.keyMap[XK_KP_4] = KC_NUMPAD4;
  148. x11.keyMap[XK_KP_5] = KC_NUMPAD5;
  149. x11.keyMap[XK_KP_6] = KC_NUMPAD6;
  150. x11.keyMap[XK_KP_7] = KC_NUMPAD7;
  151. x11.keyMap[XK_KP_8] = KC_NUMPAD8;
  152. x11.keyMap[XK_KP_9] = KC_NUMPAD9;
  153. x11.keyMap[XK_KP_Add] = KC_ADD;
  154. x11.keyMap[XK_KP_Subtract] = KC_SUBTRACT;
  155. x11.keyMap[XK_KP_Decimal] = KC_DECIMAL;
  156. x11.keyMap[XK_KP_Equal] = KC_NUMPADEQUALS;
  157. x11.keyMap[XK_KP_Divide] = KC_DIVIDE;
  158. x11.keyMap[XK_KP_Multiply] = KC_MULTIPLY;
  159. x11.keyMap[XK_KP_Enter] = KC_NUMPADENTER;
  160. // keypad with numlock off
  161. x11.keyMap[XK_KP_Home] = KC_NUMPAD7;
  162. x11.keyMap[XK_KP_Up] = KC_NUMPAD8;
  163. x11.keyMap[XK_KP_Page_Up] = KC_NUMPAD9;
  164. x11.keyMap[XK_KP_Left] = KC_NUMPAD4;
  165. x11.keyMap[XK_KP_Begin] = KC_NUMPAD5;
  166. x11.keyMap[XK_KP_Right] = KC_NUMPAD6;
  167. x11.keyMap[XK_KP_End] = KC_NUMPAD1;
  168. x11.keyMap[XK_KP_Down] = KC_NUMPAD2;
  169. x11.keyMap[XK_KP_Page_Down] = KC_NUMPAD3;
  170. x11.keyMap[XK_KP_Insert] = KC_NUMPAD0;
  171. x11.keyMap[XK_KP_Delete] = KC_DECIMAL;
  172. x11.keyMap[XK_Up] = KC_UP;
  173. x11.keyMap[XK_Down] = KC_DOWN;
  174. x11.keyMap[XK_Left] = KC_LEFT;
  175. x11.keyMap[XK_Right] = KC_RIGHT;
  176. x11.keyMap[XK_Page_Up] = KC_PGUP;
  177. x11.keyMap[XK_Page_Down] = KC_PGDOWN;
  178. x11.keyMap[XK_Home] = KC_HOME;
  179. x11.keyMap[XK_End] = KC_END;
  180. x11.keyMap[XK_Num_Lock] = KC_NUMLOCK;
  181. x11.keyMap[XK_Print] = KC_SYSRQ;
  182. x11.keyMap[XK_Scroll_Lock] = KC_SCROLL;
  183. x11.keyMap[XK_Pause] = KC_PAUSE;
  184. x11.keyMap[XK_Shift_R] = KC_RSHIFT;
  185. x11.keyMap[XK_Shift_L] = KC_LSHIFT;
  186. x11.keyMap[XK_Alt_R] = KC_RALT;
  187. x11.keyMap[XK_Alt_L] = KC_LALT;
  188. x11.keyMap[XK_Insert] = KC_INSERT;
  189. x11.keyMap[XK_Delete] = KC_DELETE;
  190. x11.keyMap[XK_Super_L] = KC_LWIN;
  191. x11.keyMap[XK_Super_R] = KC_RWIN;
  192. x11.keyMap[XK_Menu] = KC_APPS;
  193. // try to set a latin1 locales otherwise fallback to standard C locale
  194. static char locales[][32] = { "en_US.iso88591", "iso88591", "en_US", "C" };
  195. for(int i=0;i<4;++i) {
  196. if(setlocale(LC_ALL, locales[i]))
  197. break;
  198. }
  199. // open display
  200. x11.display = XOpenDisplay(0);
  201. if(!x11.display)
  202. fatal("Failed to open X display");
  203. // check if GLX is supported on this display
  204. if(!glXQueryExtension(x11.display, 0, 0))
  205. fatal("GLX not supported");
  206. // retrieve GLX version
  207. int glxMajor;
  208. int glxMinor;
  209. if(!glXQueryVersion(x11.display, &glxMajor, &glxMinor))
  210. fatal("Unable to query GLX version");
  211. notice("GLX version %d.%d", glxMajor, glxMinor);
  212. // clipboard related atoms
  213. x11.atomClipboard = XInternAtom(x11.display, "CLIPBOARD", False);
  214. x11.atomTargets = XInternAtom(x11.display, "TARGETS", False);
  215. x11.atomUTF8String = XInternAtom(x11.display, "UTF8_STRING", False);
  216. x11.atomText = XInternAtom(x11.display, "TEXT", False);
  217. x11.atomCompoundText = XInternAtom(x11.display, "COMPOUND_TEXT", False);
  218. }
  219. void Platform::terminate()
  220. {
  221. if(x11.window) {
  222. destroyWindow();
  223. x11.window = None;
  224. }
  225. // close display
  226. if(x11.display) {
  227. XCloseDisplay(x11.display);
  228. x11.display = NULL;
  229. }
  230. }
  231. void Platform::poll()
  232. {
  233. XEvent event, peekevent;
  234. static InputEvent inputEvent;
  235. while(XPending(x11.display) > 0) {
  236. XNextEvent(x11.display, &event);
  237. // call filter because xim will discard KeyPress events when keys still composing
  238. if(XFilterEvent(&event, x11.window))
  239. continue;
  240. // discard events of repeated key releases
  241. if(event.type == KeyRelease && XPending(x11.display)) {
  242. XPeekEvent(x11.display, &peekevent);
  243. if((peekevent.type == KeyPress) &&
  244. (peekevent.xkey.keycode == event.xkey.keycode) &&
  245. ((peekevent.xkey.time-event.xkey.time) < 2))
  246. continue;
  247. }
  248. switch(event.type) {
  249. case ConfigureNotify:
  250. // window resize
  251. if(x11.width != event.xconfigure.width || x11.height != event.xconfigure.height) {
  252. x11.width = event.xconfigure.width;
  253. x11.height = event.xconfigure.height;
  254. g_engine.onResize(x11.width, x11.height);
  255. }
  256. break;
  257. case KeyPress:
  258. case KeyRelease: {
  259. KeySym keysym;
  260. char buf[32];
  261. int len;
  262. inputEvent.key.ctrl = (event.xkey.state & ControlMask);
  263. inputEvent.key.shift = (event.xkey.state & ShiftMask);
  264. inputEvent.key.alt = (event.xkey.state & Mod1Mask);
  265. // fire enter text event
  266. if(event.type == KeyPress && !inputEvent.key.ctrl && !inputEvent.key.alt) {
  267. if(x11.xic) { // with xim we can get latin1 input correctly
  268. Status status;
  269. len = XmbLookupString(x11.xic, &event.xkey, buf, sizeof(buf), &keysym, &status);
  270. } else { // otherwise use XLookupString, but it doesn't work right with dead keys often
  271. static XComposeStatus compose = {NULL, 0};
  272. len = XLookupString(&event.xkey, buf, sizeof(buf), &keysym, &compose);
  273. }
  274. if(len > 0 &&
  275. // for some reason these keys produces characters and we don't want that
  276. keysym != XK_BackSpace &&
  277. keysym != XK_Return &&
  278. keysym != XK_Delete &&
  279. keysym != XK_Escape
  280. ) {
  281. //debug("char: %c code: %d", buf[0], (unsigned char)buf[0]);
  282. inputEvent.type = EV_TEXT_ENTER;
  283. inputEvent.key.keychar = buf[0];
  284. inputEvent.key.keycode = KC_UNKNOWN;
  285. g_engine.onInputEvent(&inputEvent);
  286. }
  287. }
  288. // unmask Shift/Lock to get expected results
  289. event.xkey.state &= ~(ShiftMask | LockMask);
  290. len = XLookupString(&event.xkey, buf, sizeof(buf), &keysym, 0);
  291. // fire key up/down event
  292. if(x11.keyMap.find(keysym) != x11.keyMap.end()) {
  293. inputEvent.key.keycode = x11.keyMap[keysym];
  294. inputEvent.type = (event.type == KeyPress) ? EV_KEY_DOWN : EV_KEY_UP;
  295. inputEvent.key.keychar = (len > 0) ? buf[0] : 0;
  296. g_engine.onInputEvent(&inputEvent);
  297. }
  298. break;
  299. }
  300. case ButtonPress:
  301. case ButtonRelease:
  302. switch(event.xbutton.button) {
  303. case Button1:
  304. inputEvent.type = (event.type == ButtonPress) ? EV_MOUSE_LDOWN : EV_MOUSE_LUP;
  305. break;
  306. case Button3:
  307. inputEvent.type = (event.type == ButtonPress) ? EV_MOUSE_RDOWN : EV_MOUSE_RUP;
  308. break;
  309. case Button2:
  310. inputEvent.type = (event.type == ButtonPress) ? EV_MOUSE_MDOWN : EV_MOUSE_MUP;
  311. break;
  312. case Button4:
  313. inputEvent.type = EV_MOUSE_WHEEL_UP;
  314. break;
  315. case Button5:
  316. inputEvent.type = EV_MOUSE_WHEEL_DOWN;
  317. break;
  318. }
  319. g_engine.onInputEvent(&inputEvent);
  320. break;
  321. case MotionNotify:
  322. inputEvent.type = EV_MOUSE_MOVE;
  323. inputEvent.mouse.x = event.xbutton.x;
  324. inputEvent.mouse.y = event.xbutton.y;
  325. g_engine.onInputEvent(&inputEvent);
  326. break;
  327. case MapNotify:
  328. x11.visible = true;
  329. break;
  330. case UnmapNotify:
  331. x11.visible = false;
  332. break;
  333. case FocusIn:
  334. x11.focused = true;
  335. break;
  336. case FocusOut:
  337. x11.focused = false;
  338. break;
  339. // clipboard data request
  340. case SelectionRequest:
  341. {
  342. XEvent respond;
  343. XSelectionRequestEvent *req = &(event.xselectionrequest);
  344. if(req->target == x11.atomTargets ) {
  345. Atom typeList[] = {x11.atomText, x11.atomCompoundText, x11.atomUTF8String, XA_STRING};
  346. XChangeProperty(x11.display, req->requestor,
  347. req->property, req->target,
  348. 8, PropModeReplace,
  349. (unsigned char *) &typeList,
  350. sizeof(typeList));
  351. respond.xselection.property = req->property;
  352. } else {
  353. XChangeProperty(x11.display,
  354. req->requestor,
  355. req->property, req->target,
  356. 8,
  357. PropModeReplace,
  358. (unsigned char*) x11.clipboardText.c_str(),
  359. x11.clipboardText.size());
  360. respond.xselection.property = req->property;
  361. }
  362. respond.xselection.type = SelectionNotify;
  363. respond.xselection.display = req->display;
  364. respond.xselection.requestor = req->requestor;
  365. respond.xselection.selection = req->selection;
  366. respond.xselection.target = req->target;
  367. respond.xselection.time = req->time;
  368. XSendEvent(x11.display, req->requestor, 0, 0, &respond);
  369. XFlush(x11.display);
  370. break;
  371. }
  372. case ClientMessage:
  373. {
  374. if((Atom)event.xclient.data.l[0] == x11.atomDeleteWindow)
  375. g_engine.onClose();
  376. break;
  377. }
  378. }
  379. }
  380. }
  381. unsigned long Platform::getTicks()
  382. {
  383. static timeval tv;
  384. static unsigned long firstTick = 0;
  385. gettimeofday(&tv, 0);
  386. if(!firstTick)
  387. firstTick = tv.tv_sec;
  388. return ((tv.tv_sec - firstTick) * 1000) + (tv.tv_usec / 1000);
  389. }
  390. void Platform::sleep(unsigned long miliseconds)
  391. {
  392. timespec tv;
  393. tv.tv_sec = miliseconds / 1000;
  394. tv.tv_nsec = (miliseconds % 1000) * 1000000;
  395. nanosleep(&tv, NULL);
  396. }
  397. bool Platform::createWindow(int x, int y, int width, int height, int minWidth, int minHeight, bool maximized)
  398. {
  399. static int attrList[] = {
  400. GLX_USE_GL,
  401. GLX_RGBA,
  402. GLX_DOUBLEBUFFER,
  403. None
  404. };
  405. // choose OpenGL, RGBA, double buffered, visual
  406. x11.visual = glXChooseVisual(x11.display, DefaultScreen(x11.display), attrList);
  407. if(!x11.visual)
  408. fatal("RGBA/Double buffered visual not supported");
  409. // create GLX context
  410. x11.glxContext = glXCreateContext(x11.display, x11.visual, 0, GL_TRUE);
  411. if(!x11.glxContext)
  412. fatal("Unable to create GLX context");
  413. // color map
  414. x11.colormap = XCreateColormap(x11.display,
  415. RootWindow(x11.display, x11.visual->screen),
  416. x11.visual->visual,
  417. AllocNone);
  418. // setup window attributes
  419. XSetWindowAttributes wa;
  420. wa.colormap = x11.colormap;
  421. wa.border_pixel = 0;
  422. wa.event_mask = KeyPressMask | KeyReleaseMask |
  423. ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
  424. ExposureMask | VisibilityChangeMask |
  425. StructureNotifyMask | FocusChangeMask;
  426. // calculate center position
  427. //x = (XDisplayWidth(x11.display, DefaultScreen(x11.display)) - width) / 2;
  428. //y = (XDisplayHeight(x11.display, DefaultScreen(x11.display)) - height) / 2;
  429. // create the window
  430. x11.window = XCreateWindow(x11.display,
  431. RootWindow(x11.display, x11.visual->screen),
  432. x, y,
  433. width, height,
  434. 0,
  435. x11.visual->depth,
  436. InputOutput,
  437. x11.visual->visual,
  438. CWBorderPixel | CWColormap | CWEventMask,
  439. &wa);
  440. if(!x11.window)
  441. fatal("Unable to create X window");
  442. // create input context (to have better key input handling)
  443. if(XSupportsLocale()) {
  444. XSetLocaleModifiers("");
  445. x11.xim = XOpenIM(x11.display, NULL, NULL, NULL);
  446. if(x11.xim) {
  447. x11.xic = XCreateIC(x11.xim,
  448. XNInputStyle,
  449. XIMPreeditNothing | XIMStatusNothing,
  450. XNClientWindow, x11.window, NULL);
  451. if(!x11.xic)
  452. error("Unable to create the input context");
  453. } else
  454. error("Failed to open an input method");
  455. } else
  456. error("X11 does not support the current locale");
  457. if(!x11.xic)
  458. warning("Input of special keys maybe messed up because we couldn't create an input context");
  459. // set window minimum size
  460. XSizeHints xsizehints;
  461. xsizehints.flags = PMinSize;
  462. xsizehints.min_width = minWidth;
  463. xsizehints.min_height= minHeight;
  464. XSetWMSizeHints(x11.display, x11.window, &xsizehints, XA_WM_NORMAL_HINTS);
  465. // handle delete window event
  466. x11.atomDeleteWindow = XInternAtom(x11.display, "WM_DELETE_WINDOW", True);
  467. XSetWMProtocols(x11.display, x11.window, &x11.atomDeleteWindow , 1);
  468. // connect the GLX-context to the window
  469. glXMakeCurrent(x11.display, x11.window, x11.glxContext);
  470. x11.width = width;
  471. x11.height = height;
  472. return true;
  473. }
  474. void Platform::destroyWindow()
  475. {
  476. if(x11.glxContext) {
  477. glXMakeCurrent(x11.display, None, NULL);
  478. glXDestroyContext(x11.display, x11.glxContext);
  479. x11.glxContext = NULL;
  480. }
  481. if(x11.visual) {
  482. XFree(x11.visual);
  483. x11.visual = NULL;
  484. }
  485. if(x11.colormap != None) {
  486. XFreeColormap(x11.display, x11.colormap);
  487. x11.colormap = 0;
  488. }
  489. if(x11.window != None) {
  490. XUnmapWindow(x11.display, x11.window);
  491. XDestroyWindow(x11.display, x11.window);
  492. x11.window = None;
  493. }
  494. if(x11.xic) {
  495. XDestroyIC(x11.xic);
  496. x11.xic = NULL;
  497. }
  498. if(x11.xim) {
  499. XCloseIM(x11.xim);
  500. x11.xim = NULL;
  501. }
  502. }
  503. void Platform::showWindow()
  504. {
  505. XMapWindow(x11.display, x11.window);
  506. }
  507. void Platform::setWindowTitle(const char *title)
  508. {
  509. XStoreName(x11.display, x11.window, title);
  510. XSetIconName(x11.display, x11.window, title);
  511. }
  512. void *Platform::getExtensionProcAddress(const char *ext)
  513. {
  514. return (void*)glXGetProcAddressARB((const GLubyte*)ext);
  515. }
  516. bool Platform::isExtensionSupported(const char *ext)
  517. {
  518. const char *exts = glXQueryExtensionsString(x11.display, DefaultScreen(x11.display));
  519. if(strstr(exts, ext))
  520. return true;
  521. return true;
  522. }
  523. const char *Platform::getTextFromClipboard()
  524. {
  525. Window ownerWindow = XGetSelectionOwner(x11.display, x11.atomClipboard);
  526. if(ownerWindow == x11.window)
  527. return x11.clipboardText.c_str();
  528. std::string clipboard = "";
  529. if(ownerWindow != None) {
  530. XConvertSelection(x11.display, x11.atomClipboard, XA_STRING, 0, ownerWindow, CurrentTime);
  531. XFlush(x11.display);
  532. // hack to wait SelectioNotify event, otherwise we will get wrong clipboard pastes
  533. sleep(100);
  534. // check for data
  535. Atom type;
  536. int format;
  537. unsigned long numItems, bytesLeft, dummy;
  538. unsigned char *data;
  539. XGetWindowProperty(x11.display, ownerWindow,
  540. XA_STRING,
  541. 0, 0, 0,
  542. AnyPropertyType,
  543. &type,
  544. &format,
  545. &numItems,
  546. &bytesLeft,
  547. &data);
  548. if(bytesLeft > 0) {
  549. // get the data get
  550. int result = XGetWindowProperty(x11.display, ownerWindow,
  551. XA_STRING,
  552. 0,
  553. bytesLeft,
  554. 0,
  555. AnyPropertyType,
  556. &type,
  557. &format,
  558. &numItems,
  559. &dummy,
  560. &data);
  561. if(result == Success)
  562. clipboard = (const char*)data;
  563. XFree(data);
  564. }
  565. }
  566. return clipboard.c_str();
  567. }
  568. void Platform::copyToClipboard(const char *text)
  569. {
  570. x11.clipboardText = text;
  571. XSetSelectionOwner(x11.display, x11.atomClipboard, x11.window, CurrentTime);
  572. XFlush(x11.display);
  573. }
  574. void Platform::hideMouseCursor()
  575. {
  576. if(x11.cursor == None) {
  577. char bm[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
  578. Pixmap pix = XCreateBitmapFromData(x11.display, x11.window, bm, 8, 8);
  579. XColor black;
  580. memset(&black, 0, sizeof(XColor));
  581. black.flags = DoRed | DoGreen | DoBlue;
  582. x11.cursor = XCreatePixmapCursor(x11.display, pix, pix, &black, &black, 0, 0);
  583. XFreePixmap(x11.display, pix);
  584. }
  585. XDefineCursor(x11.display, x11.window, x11.cursor);
  586. }
  587. void Platform::showMouseCursor()
  588. {
  589. XUndefineCursor(x11.display, x11.window);
  590. if(x11.cursor != None) {
  591. XFreeCursor(x11.display, x11.cursor);
  592. x11.cursor = None;
  593. }
  594. }
  595. void Platform::setVsync(bool enable)
  596. {
  597. typedef GLint (*glSwapIntervalProc)(GLint);
  598. glSwapIntervalProc glSwapInterval = NULL;
  599. if(isExtensionSupported("GLX_MESA_swap_control"))
  600. glSwapInterval = (glSwapIntervalProc)getExtensionProcAddress("glXSwapIntervalMESA");
  601. else if(isExtensionSupported("GLX_SGI_swap_control"))
  602. glSwapInterval = (glSwapIntervalProc)getExtensionProcAddress("glXSwapIntervalSGI");
  603. if(glSwapInterval)
  604. glSwapInterval(enable ? 1 : 0);
  605. }
  606. void Platform::swapBuffers()
  607. {
  608. glXSwapBuffers(x11.display, x11.window);
  609. }
  610. bool Platform::isWindowFocused()
  611. {
  612. return x11.focused;
  613. }
  614. bool Platform::isWindowVisible()
  615. {
  616. return x11.visible;
  617. }
  618. int Platform::getWindowWidth()
  619. {
  620. return x11.width;
  621. }
  622. int Platform::getWindowHeight()
  623. {
  624. return x11.height;
  625. }
  626. const char *Platform::getAppUserDir()
  627. {
  628. std::stringstream sdir;
  629. sdir << PHYSFS_getUserDir() << "/." << APP_NAME << "/";
  630. if((mkdir(sdir.str().c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) && (errno != EEXIST))
  631. error("Couldn't create directory for saving configuration file. (%s)", sdir.str().c_str());
  632. return sdir.str().c_str();
  633. }