]> git.lizzy.rs Git - irrlicht.git/blob - source/Irrlicht/CIrrDeviceLinux.cpp
91d1e4700b0602dabd222a1596574cc47712cc5b
[irrlicht.git] / source / Irrlicht / CIrrDeviceLinux.cpp
1 // Copyright (C) 2002-2012 Nikolaus Gebhardt\r
2 // This file is part of the "Irrlicht Engine".\r
3 // For conditions of distribution and use, see copyright notice in irrlicht.h\r
4 \r
5 #include "CIrrDeviceLinux.h"\r
6 \r
7 #ifdef _IRR_COMPILE_WITH_X11_DEVICE_\r
8 \r
9 #include <stdio.h>\r
10 #include <stdlib.h>\r
11 #include <sys/utsname.h>\r
12 #include <time.h>\r
13 #include <locale.h>\r
14 #include "IEventReceiver.h"\r
15 #include "ISceneManager.h"\r
16 #include "IGUIElement.h"\r
17 #include "IGUIEnvironment.h"\r
18 #include "os.h"\r
19 #include "CTimer.h"\r
20 #include "irrString.h"\r
21 #include "Keycodes.h"\r
22 #include "COSOperator.h"\r
23 #include "CColorConverter.h"\r
24 #include "SIrrCreationParameters.h"\r
25 #include "SExposedVideoData.h"\r
26 #include "IGUISpriteBank.h"\r
27 #include <X11/XKBlib.h>\r
28 #include <X11/Xatom.h>\r
29 \r
30 #if defined(_IRR_LINUX_X11_XINPUT2_)\r
31 #include <X11/extensions/XInput2.h>\r
32 #endif\r
33 \r
34 #if defined(_IRR_COMPILE_WITH_OGLES1_) || defined(_IRR_COMPILE_WITH_OGLES2_)\r
35 #include "CEGLManager.h"\r
36 #endif\r
37 \r
38 #if defined(_IRR_COMPILE_WITH_OPENGL_)\r
39 #include "CGLXManager.h"\r
40 #endif\r
41 \r
42 #ifdef _IRR_LINUX_XCURSOR_\r
43 #include <X11/Xcursor/Xcursor.h>\r
44 #endif\r
45 \r
46 #if defined _IRR_COMPILE_WITH_JOYSTICK_EVENTS_\r
47 #include <fcntl.h>\r
48 #include <unistd.h>\r
49 \r
50 #ifdef __FreeBSD__\r
51 #include <sys/joystick.h>\r
52 #else\r
53 \r
54 // linux/joystick.h includes linux/input.h, which #defines values for various KEY_FOO keys.\r
55 // These override the irr::KEY_FOO equivalents, which stops key handling from working.\r
56 // As a workaround, defining _INPUT_H stops linux/input.h from being included; it\r
57 // doesn't actually seem to be necessary except to pull in sys/ioctl.h.\r
58 #define _INPUT_H\r
59 #include <sys/ioctl.h> // Would normally be included in linux/input.h\r
60 #include <linux/joystick.h>\r
61 #undef _INPUT_H\r
62 #endif\r
63 \r
64 #endif // _IRR_COMPILE_WITH_JOYSTICK_EVENTS_\r
65 \r
66 namespace irr\r
67 {\r
68         namespace video\r
69         {\r
70 #ifdef _IRR_COMPILE_WITH_OPENGL_\r
71                 IVideoDriver* createOpenGLDriver(const irr::SIrrlichtCreationParameters& params, io::IFileSystem* io, IContextManager* contextManager);\r
72 #endif\r
73 \r
74 #ifdef _IRR_COMPILE_WITH_OGLES1_\r
75         IVideoDriver* createOGLES1Driver(const irr::SIrrlichtCreationParameters& params, io::IFileSystem* io, IContextManager* contextManager);\r
76 #endif\r
77 \r
78 #ifdef _IRR_COMPILE_WITH_OGLES2_\r
79         IVideoDriver* createOGLES2Driver(const irr::SIrrlichtCreationParameters& params, io::IFileSystem* io, IContextManager* contextManager);\r
80 #endif\r
81 \r
82 #ifdef _IRR_COMPILE_WITH_WEBGL1_\r
83                 IVideoDriver* createWebGL1Driver(const irr::SIrrlichtCreationParameters& params, io::IFileSystem* io, IContextManager* contextManager);\r
84 #endif\r
85         }\r
86 } // end namespace irr\r
87 \r
88 namespace\r
89 {\r
90         Atom X_ATOM_CLIPBOARD;\r
91         Atom X_ATOM_TARGETS;\r
92         Atom X_ATOM_UTF8_STRING;\r
93         Atom X_ATOM_UTF8_MIME_TYPE;\r
94         Atom X_ATOM_TEXT;\r
95         Atom X_ATOM_NETWM_MAXIMIZE_VERT;\r
96         Atom X_ATOM_NETWM_MAXIMIZE_HORZ;\r
97         Atom X_ATOM_NETWM_STATE;\r
98         Atom X_ATOM_NETWM_STATE_FULLSCREEN;\r
99 \r
100         Atom X_ATOM_WM_DELETE_WINDOW;\r
101 \r
102 #if defined(_IRR_LINUX_X11_XINPUT2_)\r
103         int XI_EXTENSIONS_OPCODE;\r
104 #endif\r
105 }\r
106 \r
107 namespace irr\r
108 {\r
109 //! constructor\r
110 CIrrDeviceLinux::CIrrDeviceLinux(const SIrrlichtCreationParameters& param)\r
111         : CIrrDeviceStub(param),\r
112 #ifdef _IRR_COMPILE_WITH_X11_\r
113         XDisplay(0), VisualInfo(0), Screennr(0), XWindow(0), StdHints(0),\r
114         XInputMethod(0), XInputContext(0),\r
115         HasNetWM(false),\r
116 #endif\r
117 #if defined(_IRR_LINUX_X11_XINPUT2_)\r
118         currentTouchedCount(0),\r
119 #endif\r
120         Width(param.WindowSize.Width), Height(param.WindowSize.Height),\r
121         WindowHasFocus(false), WindowMinimized(false),\r
122         ExternalWindow(false), AutorepeatSupport(0)\r
123 {\r
124         #ifdef _DEBUG\r
125         setDebugName("CIrrDeviceLinux");\r
126         #endif\r
127 \r
128         // print version, distribution etc.\r
129         // thx to LynxLuna for pointing me to the uname function\r
130         core::stringc linuxversion;\r
131         struct utsname LinuxInfo;\r
132         uname(&LinuxInfo);\r
133 \r
134         linuxversion += LinuxInfo.sysname;\r
135         linuxversion += " ";\r
136         linuxversion += LinuxInfo.release;\r
137         linuxversion += " ";\r
138         linuxversion += LinuxInfo.version;\r
139         linuxversion += " ";\r
140         linuxversion += LinuxInfo.machine;\r
141 \r
142         Operator = new COSOperator(linuxversion, this);\r
143         os::Printer::log(linuxversion.c_str(), ELL_INFORMATION);\r
144 \r
145         // create keymap\r
146         createKeyMap();\r
147 \r
148         // create window\r
149         if (CreationParams.DriverType != video::EDT_NULL)\r
150         {\r
151                 // create the window, only if we do not use the null device\r
152                 if (!createWindow())\r
153                         return;\r
154                 if (param.WindowResizable < 2 )\r
155                         setResizable(param.WindowResizable == 1 ? true : false);\r
156 #ifdef _IRR_COMPILE_WITH_X11_\r
157                 createInputContext();\r
158 #endif\r
159         }\r
160 \r
161         // create cursor control\r
162         CursorControl = new CCursorControl(this, CreationParams.DriverType == video::EDT_NULL);\r
163 \r
164         // create driver\r
165         createDriver();\r
166 \r
167         if (!VideoDriver)\r
168                 return;\r
169 \r
170         createGUIAndScene();\r
171 }\r
172 \r
173 \r
174 //! destructor\r
175 CIrrDeviceLinux::~CIrrDeviceLinux()\r
176 {\r
177 #ifdef _IRR_COMPILE_WITH_X11_\r
178         if (StdHints)\r
179                 XFree(StdHints);\r
180         // Disable cursor (it is drop'ed in stub)\r
181         if (CursorControl)\r
182         {\r
183                 CursorControl->setVisible(false);\r
184                 static_cast<CCursorControl*>(CursorControl)->clearCursors();\r
185         }\r
186 \r
187         // Must free OpenGL textures etc before destroying context, so can't wait for stub destructor\r
188         if ( GUIEnvironment )\r
189         {\r
190                 GUIEnvironment->drop();\r
191                 GUIEnvironment = NULL;\r
192         }\r
193         if ( SceneManager )\r
194         {\r
195                 SceneManager->drop();\r
196                 SceneManager = NULL;\r
197         }\r
198         if ( VideoDriver )\r
199         {\r
200                 VideoDriver->drop();\r
201                 VideoDriver = NULL;\r
202         }\r
203 \r
204         destroyInputContext();\r
205 \r
206         if (XDisplay)\r
207         {\r
208                 if (ContextManager)\r
209                 {\r
210                         ContextManager->destroyContext();\r
211                         ContextManager->destroySurface();\r
212                 }\r
213 \r
214                 if (!ExternalWindow)\r
215                 {\r
216                         XDestroyWindow(XDisplay,XWindow);\r
217                         XCloseDisplay(XDisplay);\r
218                 }\r
219         }\r
220         if (VisualInfo)\r
221                 XFree(VisualInfo);\r
222 \r
223 #endif // #ifdef _IRR_COMPILE_WITH_X11_\r
224 \r
225 #if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_)\r
226         for (u32 joystick = 0; joystick < ActiveJoysticks.size(); ++joystick)\r
227         {\r
228                 if (ActiveJoysticks[joystick].fd >= 0)\r
229                 {\r
230                         close(ActiveJoysticks[joystick].fd);\r
231                 }\r
232         }\r
233 #endif\r
234 }\r
235 \r
236 \r
237 #if defined(_IRR_COMPILE_WITH_X11_) && defined(_DEBUG)\r
238 int IrrPrintXError(Display *display, XErrorEvent *event)\r
239 {\r
240         char msg[256];\r
241         char msg2[256];\r
242 \r
243         snprintf_irr(msg, 256, "%d", event->request_code);\r
244         XGetErrorDatabaseText(display, "XRequest", msg, "unknown", msg2, 256);\r
245         XGetErrorText(display, event->error_code, msg, 256);\r
246         os::Printer::log("X Error", msg, ELL_WARNING);\r
247         os::Printer::log("From call ", msg2, ELL_WARNING);\r
248         return 0;\r
249 }\r
250 #endif\r
251 \r
252 \r
253 bool CIrrDeviceLinux::switchToFullscreen()\r
254 {\r
255         if (!CreationParams.Fullscreen)\r
256                 return true;\r
257 \r
258         if (!HasNetWM)\r
259         {\r
260                 os::Printer::log("NetWM support is required to allow Irrlicht to switch "\r
261                 "to fullscreen mode. Running in windowed mode instead.", ELL_WARNING);\r
262                 CreationParams.Fullscreen = false;\r
263                 return false;\r
264         }\r
265 \r
266         XEvent ev = {0};\r
267 \r
268         ev.type = ClientMessage;\r
269         ev.xclient.window = XWindow;\r
270         ev.xclient.message_type = X_ATOM_NETWM_STATE;\r
271         ev.xclient.format = 32;\r
272         ev.xclient.data.l[0] = 1; // _NET_WM_STATE_ADD\r
273         ev.xclient.data.l[1] = X_ATOM_NETWM_STATE_FULLSCREEN;\r
274 \r
275         XSendEvent(XDisplay, DefaultRootWindow(XDisplay), false,\r
276                         SubstructureNotifyMask | SubstructureRedirectMask, &ev);\r
277 \r
278         return true;\r
279 }\r
280 \r
281 \r
282 #if defined(_IRR_COMPILE_WITH_X11_)\r
283 void IrrPrintXGrabError(int grabResult, const c8 * grabCommand )\r
284 {\r
285         if ( grabResult == GrabSuccess )\r
286         {\r
287 //              os::Printer::log(grabCommand, ": GrabSuccess", ELL_INFORMATION);\r
288                 return;\r
289         }\r
290 \r
291         switch ( grabResult )\r
292         {\r
293                 case AlreadyGrabbed:\r
294                         os::Printer::log(grabCommand, ": AlreadyGrabbed", ELL_WARNING);\r
295                         break;\r
296                 case GrabNotViewable:\r
297                         os::Printer::log(grabCommand, ": GrabNotViewable", ELL_WARNING);\r
298                         break;\r
299                 case GrabFrozen:\r
300                         os::Printer::log(grabCommand, ": GrabFrozen", ELL_WARNING);\r
301                         break;\r
302                 case GrabInvalidTime:\r
303                         os::Printer::log(grabCommand, ": GrabInvalidTime", ELL_WARNING);\r
304                         break;\r
305                 default:\r
306                         os::Printer::log(grabCommand, ": grab failed with unknown problem", ELL_WARNING);\r
307                         break;\r
308         }\r
309 }\r
310 #endif\r
311 \r
312 \r
313 bool CIrrDeviceLinux::createWindow()\r
314 {\r
315 #ifdef _IRR_COMPILE_WITH_X11_\r
316 #ifdef _DEBUG\r
317         os::Printer::log("Creating X window...", ELL_INFORMATION);\r
318         XSetErrorHandler(IrrPrintXError);\r
319 #endif\r
320 \r
321         XDisplay = XOpenDisplay(0);\r
322         if (!XDisplay)\r
323         {\r
324                 os::Printer::log("Error: Need running XServer to start Irrlicht Engine.", ELL_ERROR);\r
325                 if (XDisplayName(0)[0])\r
326                         os::Printer::log("Could not open display", XDisplayName(0), ELL_ERROR);\r
327                 else\r
328                         os::Printer::log("Could not open display, set DISPLAY variable", ELL_ERROR);\r
329                 return false;\r
330         }\r
331 \r
332         Screennr = DefaultScreen(XDisplay);\r
333 \r
334         initXAtoms();\r
335 \r
336         // check netwm support\r
337         Atom WMCheck = XInternAtom(XDisplay, "_NET_SUPPORTING_WM_CHECK", True);\r
338         if (WMCheck != None)\r
339                 HasNetWM = true;\r
340 \r
341 #if defined(_IRR_COMPILE_WITH_OPENGL_)\r
342         // don't use the XVisual with OpenGL, because it ignores all requested\r
343         // properties of the CreationParams\r
344         if (CreationParams.DriverType == video::EDT_OPENGL)\r
345         {\r
346                 video::SExposedVideoData data;\r
347                 data.OpenGLLinux.X11Display = XDisplay;\r
348                 ContextManager = new video::CGLXManager(CreationParams, data, Screennr);\r
349                 VisualInfo = ((video::CGLXManager*)ContextManager)->getVisual();\r
350         }\r
351 #endif\r
352 \r
353         if (!VisualInfo)\r
354         {\r
355                 // create visual with standard X methods\r
356                 os::Printer::log("Using plain X visual");\r
357                 XVisualInfo visTempl; //Template to hold requested values\r
358                 int visNumber; // Return value of available visuals\r
359 \r
360                 visTempl.screen = Screennr;\r
361                 // ARGB visuals should be avoided for usual applications\r
362                 visTempl.depth = CreationParams.WithAlphaChannel?32:24;\r
363                 while ((!VisualInfo) && (visTempl.depth>=16))\r
364                 {\r
365                         VisualInfo = XGetVisualInfo(XDisplay, VisualScreenMask|VisualDepthMask,\r
366                                 &visTempl, &visNumber);\r
367                         visTempl.depth -= 8;\r
368                 }\r
369         }\r
370 \r
371         if (!VisualInfo)\r
372         {\r
373                 os::Printer::log("Fatal error, could not get visual.", ELL_ERROR);\r
374                 XCloseDisplay(XDisplay);\r
375                 XDisplay=0;\r
376                 return false;\r
377         }\r
378 #ifdef _DEBUG\r
379         else\r
380                 os::Printer::log("Visual chosen: ", core::stringc(static_cast<u32>(VisualInfo->visualid)).c_str(), ELL_DEBUG);\r
381 #endif\r
382 \r
383         // create color map\r
384         Colormap colormap;\r
385         colormap = XCreateColormap(XDisplay,\r
386                         RootWindow(XDisplay, VisualInfo->screen),\r
387                         VisualInfo->visual, AllocNone);\r
388 \r
389         WndAttributes.colormap = colormap;\r
390         WndAttributes.border_pixel = 0;\r
391         WndAttributes.event_mask = StructureNotifyMask | FocusChangeMask | ExposureMask;\r
392         if (!CreationParams.IgnoreInput)\r
393                 WndAttributes.event_mask |= PointerMotionMask |\r
394                                 ButtonPressMask | KeyPressMask |\r
395                                 ButtonReleaseMask | KeyReleaseMask;\r
396 \r
397         if (!CreationParams.WindowId)\r
398         {\r
399                 int x = 0;\r
400                 int y = 0;\r
401 \r
402                 if (!CreationParams.Fullscreen)\r
403                 {\r
404                         if (CreationParams.WindowPosition.X > 0)\r
405                                 x = CreationParams.WindowPosition.X;\r
406                         if (CreationParams.WindowPosition.Y > 0)\r
407                                 y = CreationParams.WindowPosition.Y;\r
408                 }\r
409 \r
410                 // create new Window\r
411                 // Remove window manager decoration in fullscreen\r
412                 XWindow = XCreateWindow(XDisplay,\r
413                                 RootWindow(XDisplay, VisualInfo->screen),\r
414                                 x, y, Width, Height, 0, VisualInfo->depth,\r
415                                 InputOutput, VisualInfo->visual,\r
416                                 CWBorderPixel | CWColormap | CWEventMask,\r
417                                 &WndAttributes);\r
418 \r
419                 XMapRaised(XDisplay, XWindow);\r
420                 CreationParams.WindowId = (void*)XWindow;\r
421                 X_ATOM_WM_DELETE_WINDOW = XInternAtom(XDisplay, "WM_DELETE_WINDOW", True);\r
422                 XSetWMProtocols(XDisplay, XWindow, &X_ATOM_WM_DELETE_WINDOW, 1);\r
423 \r
424                 if (CreationParams.Fullscreen)\r
425                 {\r
426                         // Don't try to set window position\r
427                 }\r
428                 else if (CreationParams.WindowPosition.X >= 0 || CreationParams.WindowPosition.Y >= 0)  // default is -1, -1\r
429                 {\r
430                         // Window managers are free to ignore positions above, so give it another shot\r
431                         XMoveWindow(XDisplay,XWindow,x,y);\r
432                 }\r
433         }\r
434         else\r
435         {\r
436                 // attach external window\r
437                 XWindow = (Window)CreationParams.WindowId;\r
438                 if (!CreationParams.IgnoreInput)\r
439                 {\r
440                         // Note: This might be further improved by using a InputOnly window instead of InputOutput.\r
441                         // I think then it should be possible to render into the given parent window instead of\r
442                         // creating a child-window.\r
443                         // That could also be a third option for IgnoreInput in the CreationParams.\r
444                         // But we need another window variable then and have to split input/output in\r
445                         // the rest of the device code.\r
446                         // Also... this does possibly leak.\r
447                         Window child_window = XCreateWindow(XDisplay,\r
448                                         XWindow,\r
449                                         0, 0, Width, Height, 0, VisualInfo->depth,\r
450                                         InputOutput, VisualInfo->visual,\r
451                                         CWBorderPixel | CWColormap | CWEventMask,\r
452                                         &WndAttributes);\r
453 \r
454                         // do not forget to map new window\r
455                         XMapWindow(XDisplay, child_window);\r
456 \r
457                         // overwrite device window id\r
458                         XWindow = child_window;\r
459                 }\r
460                 XWindowAttributes wa;\r
461                 XGetWindowAttributes(XDisplay, XWindow, &wa);\r
462                 CreationParams.WindowSize.Width = wa.width;\r
463                 CreationParams.WindowSize.Height = wa.height;\r
464                 CreationParams.Fullscreen = false;\r
465                 ExternalWindow = true;\r
466         }\r
467 \r
468         switchToFullscreen();\r
469 \r
470         WindowMinimized=false;\r
471         XkbSetDetectableAutoRepeat(XDisplay, True, &AutorepeatSupport);\r
472 \r
473         Window tmp;\r
474         u32 borderWidth;\r
475         int x,y;\r
476         unsigned int bits;\r
477 \r
478         XGetGeometry(XDisplay, XWindow, &tmp, &x, &y, &Width, &Height, &borderWidth, &bits);\r
479         CreationParams.Bits = bits;\r
480         CreationParams.WindowSize.Width = Width;\r
481         CreationParams.WindowSize.Height = Height;\r
482 \r
483         StdHints = XAllocSizeHints();\r
484         long num;\r
485         XGetWMNormalHints(XDisplay, XWindow, StdHints, &num);\r
486 \r
487         initXInput2();\r
488 \r
489 #endif // #ifdef _IRR_COMPILE_WITH_X11_\r
490         return true;\r
491 }\r
492 \r
493 \r
494 //! create the driver\r
495 void CIrrDeviceLinux::createDriver()\r
496 {\r
497         switch(CreationParams.DriverType)\r
498         {\r
499 #ifdef _IRR_COMPILE_WITH_X11_\r
500         case video::EDT_OPENGL:\r
501 #ifdef _IRR_COMPILE_WITH_OPENGL_\r
502                 {\r
503                         video::SExposedVideoData data;\r
504                         data.OpenGLLinux.X11Window = XWindow;\r
505                         data.OpenGLLinux.X11Display = XDisplay;\r
506 \r
507                         ContextManager->initialize(CreationParams, data);\r
508 \r
509                         VideoDriver = video::createOpenGLDriver(CreationParams, FileSystem, ContextManager);\r
510                 }\r
511 #else\r
512                 os::Printer::log("No OpenGL support compiled in.", ELL_ERROR);\r
513 #endif\r
514                 break;\r
515         case video::EDT_OGLES1:\r
516 #ifdef _IRR_COMPILE_WITH_OGLES1_\r
517                 {\r
518                         video::SExposedVideoData data;\r
519                         data.OpenGLLinux.X11Window = XWindow;\r
520                         data.OpenGLLinux.X11Display = XDisplay;\r
521 \r
522                         ContextManager = new video::CEGLManager();\r
523                         ContextManager->initialize(CreationParams, data);\r
524 \r
525                         VideoDriver = video::createOGLES1Driver(CreationParams, FileSystem, ContextManager);\r
526                 }\r
527 #else\r
528                 os::Printer::log("No OpenGL-ES1 support compiled in.", ELL_ERROR);\r
529 #endif\r
530                 break;\r
531         case video::EDT_OGLES2:\r
532 #ifdef _IRR_COMPILE_WITH_OGLES2_\r
533                 {\r
534                         video::SExposedVideoData data;\r
535                         data.OpenGLLinux.X11Window = XWindow;\r
536                         data.OpenGLLinux.X11Display = XDisplay;\r
537 \r
538                         ContextManager = new video::CEGLManager();\r
539                         ContextManager->initialize(CreationParams, data);\r
540 \r
541                         VideoDriver = video::createOGLES2Driver(CreationParams, FileSystem, ContextManager);\r
542                 }\r
543 #else\r
544                 os::Printer::log("No OpenGL-ES2 support compiled in.", ELL_ERROR);\r
545 #endif\r
546                 break;\r
547         case video::EDT_WEBGL1:\r
548 #ifdef _IRR_COMPILE_WITH_WEBGL1_\r
549                 {\r
550                         video::SExposedVideoData data;\r
551                         data.OpenGLLinux.X11Window = XWindow;\r
552                         data.OpenGLLinux.X11Display = XDisplay;\r
553 \r
554                         ContextManager = new video::CEGLManager();\r
555                         ContextManager->initialize(CreationParams, data);\r
556 \r
557                         VideoDriver = video::createWebGL1Driver(CreationParams, FileSystem, ContextManager);\r
558                 }\r
559 #else\r
560                 os::Printer::log("No WebGL1 support compiled in.", ELL_ERROR);\r
561 #endif\r
562                 break;\r
563         case video::DEPRECATED_EDT_DIRECT3D8_NO_LONGER_EXISTS:\r
564         case video::EDT_DIRECT3D9:\r
565                 os::Printer::log("This driver is not available in Linux. Try OpenGL or Software renderer.",\r
566                         ELL_ERROR);\r
567                 break;\r
568         case video::EDT_NULL:\r
569                 VideoDriver = video::createNullDriver(FileSystem, CreationParams.WindowSize);\r
570                 break;\r
571         default:\r
572                 os::Printer::log("Unable to create video driver of unknown type.", ELL_ERROR);\r
573                 break;\r
574 #else\r
575         case video::EDT_NULL:\r
576                 VideoDriver = video::createNullDriver(FileSystem, CreationParams.WindowSize);\r
577                 break;\r
578         default:\r
579                 os::Printer::log("No X11 support compiled in. Only Null driver available.", ELL_ERROR);\r
580                 break;\r
581 #endif\r
582         }\r
583 }\r
584 \r
585 #ifdef _IRR_COMPILE_WITH_X11_\r
586 bool CIrrDeviceLinux::createInputContext()\r
587 {\r
588         // One one side it would be nicer to let users do that - on the other hand\r
589         // not setting the environment locale will not work when using i18n X11 functions.\r
590         // So users would have to call it always or their input is broken badly.\r
591         // We can restore immediately - so won't mess with anything in users apps.\r
592         core::stringc oldLocale(setlocale(LC_CTYPE, NULL));\r
593         setlocale(LC_CTYPE, "");        // use environment locale\r
594 \r
595         if ( !XSupportsLocale() )\r
596         {\r
597                 os::Printer::log("Locale not supported. Falling back to non-i18n input.", ELL_WARNING);\r
598                 setlocale(LC_CTYPE, oldLocale.c_str());\r
599                 return false;\r
600         }\r
601 \r
602         // Load XMODIFIERS (e.g. for IMEs)\r
603         if (!XSetLocaleModifiers(""))\r
604         {\r
605                 setlocale(LC_CTYPE, oldLocale.c_str());\r
606                 os::Printer::log("XSetLocaleModifiers failed. Falling back to non-i18n input.", ELL_WARNING);\r
607                 return false;\r
608         }\r
609 \r
610         XInputMethod = XOpenIM(XDisplay, NULL, NULL, NULL);\r
611         if ( !XInputMethod )\r
612         {\r
613                 setlocale(LC_CTYPE, oldLocale.c_str());\r
614                 os::Printer::log("XOpenIM failed to create an input method. Falling back to non-i18n input.", ELL_WARNING);\r
615                 return false;\r
616         }\r
617 \r
618         XIMStyles *im_supported_styles;\r
619         XGetIMValues(XInputMethod, XNQueryInputStyle, &im_supported_styles, (char*)NULL);\r
620         XIMStyle bestStyle = 0;\r
621         XIMStyle supportedStyle = XIMPreeditNothing | XIMStatusNothing;\r
622     for(int i=0; i < im_supported_styles->count_styles; ++i)\r
623         {\r
624         XIMStyle style = im_supported_styles->supported_styles[i];\r
625         if ((style & supportedStyle) == style) /* if we can handle it */\r
626                 {\r
627             bestStyle = style;\r
628                         break;\r
629                 }\r
630     }\r
631         XFree(im_supported_styles);\r
632 \r
633         if ( !bestStyle )\r
634         {\r
635                 XDestroyIC(XInputContext);\r
636                 XInputContext = 0;\r
637 \r
638                 os::Printer::log("XInputMethod has no input style we can use. Falling back to non-i18n input.", ELL_WARNING);\r
639                 setlocale(LC_CTYPE, oldLocale.c_str());\r
640                 return false;\r
641         }\r
642 \r
643         XInputContext = XCreateIC(XInputMethod,\r
644                                                         XNInputStyle, bestStyle,\r
645                                                         XNClientWindow, XWindow,\r
646                                                         (char*)NULL);\r
647         if (!XInputContext )\r
648         {\r
649                 os::Printer::log("XInputContext failed to create an input context. Falling back to non-i18n input.", ELL_WARNING);\r
650                 setlocale(LC_CTYPE, oldLocale.c_str());\r
651                 return false;\r
652         }\r
653         XSetICFocus(XInputContext);\r
654         setlocale(LC_CTYPE, oldLocale.c_str());\r
655         return true;\r
656 }\r
657 \r
658 void CIrrDeviceLinux::destroyInputContext()\r
659 {\r
660         if ( XInputContext )\r
661         {\r
662                 XUnsetICFocus(XInputContext);\r
663                 XDestroyIC(XInputContext);\r
664                 XInputContext = 0;\r
665         }\r
666         if ( XInputMethod )\r
667         {\r
668                 XCloseIM(XInputMethod);\r
669                 XInputMethod = 0;\r
670         }\r
671 }\r
672 \r
673 EKEY_CODE CIrrDeviceLinux::getKeyCode(XEvent &event)\r
674 {\r
675         EKEY_CODE keyCode = (EKEY_CODE)0;\r
676 \r
677         SKeyMap mp;\r
678         mp.X11Key = XkbKeycodeToKeysym(XDisplay, event.xkey.keycode, 0, 0);\r
679         const s32 idx = KeyMap.binary_search(mp);\r
680         if (idx != -1)\r
681         {\r
682                 keyCode = (EKEY_CODE)KeyMap[idx].Win32Key;\r
683         }\r
684         if (keyCode == 0)\r
685         {\r
686                 keyCode = KEY_UNKNOWN;\r
687                 if ( !mp.X11Key )\r
688                 {\r
689                         os::Printer::log("No such X11Key, event keycode", core::stringc(event.xkey.keycode).c_str(), ELL_INFORMATION);\r
690                 }\r
691                 else if (idx == -1)\r
692                 {\r
693                         os::Printer::log("EKEY_CODE not found, X11 keycode", core::stringc(mp.X11Key).c_str(), ELL_INFORMATION);\r
694                 }\r
695                 else\r
696                 {\r
697                         os::Printer::log("EKEY_CODE is 0, X11 keycode", core::stringc(mp.X11Key).c_str(), ELL_INFORMATION);\r
698                 }\r
699         }\r
700         return keyCode;\r
701 }\r
702 #endif\r
703 \r
704 //! runs the device. Returns false if device wants to be deleted\r
705 bool CIrrDeviceLinux::run()\r
706 {\r
707         os::Timer::tick();\r
708 \r
709 #ifdef _IRR_COMPILE_WITH_X11_\r
710 \r
711         if ( CursorControl )\r
712                 static_cast<CCursorControl*>(CursorControl)->update();\r
713 \r
714         if ((CreationParams.DriverType != video::EDT_NULL) && XDisplay)\r
715         {\r
716                 SEvent irrevent;\r
717                 irrevent.MouseInput.ButtonStates = 0xffffffff;\r
718 \r
719                 while (XPending(XDisplay) > 0 && !Close)\r
720                 {\r
721                         XEvent event;\r
722                         XNextEvent(XDisplay, &event);\r
723                         if (XFilterEvent(&event, None))\r
724                                 continue;\r
725 \r
726                         switch (event.type)\r
727                         {\r
728                         case ConfigureNotify:\r
729                                 // check for changed window size\r
730                                 if ((event.xconfigure.width != (int) Width) ||\r
731                                         (event.xconfigure.height != (int) Height))\r
732                                 {\r
733                                         Width = event.xconfigure.width;\r
734                                         Height = event.xconfigure.height;\r
735 \r
736                                         if (VideoDriver)\r
737                                                 VideoDriver->OnResize(core::dimension2d<u32>(Width, Height));\r
738                                 }\r
739                                 break;\r
740 \r
741                         case MapNotify:\r
742                                 WindowMinimized=false;\r
743                                 break;\r
744 \r
745                         case UnmapNotify:\r
746                                 WindowMinimized=true;\r
747                                 break;\r
748 \r
749                         case FocusIn:\r
750                                 WindowHasFocus=true;\r
751                                 break;\r
752 \r
753                         case FocusOut:\r
754                                 WindowHasFocus=false;\r
755                                 break;\r
756 \r
757                         case MotionNotify:\r
758                                 irrevent.EventType = irr::EET_MOUSE_INPUT_EVENT;\r
759                                 irrevent.MouseInput.Event = irr::EMIE_MOUSE_MOVED;\r
760                                 irrevent.MouseInput.X = event.xbutton.x;\r
761                                 irrevent.MouseInput.Y = event.xbutton.y;\r
762                                 irrevent.MouseInput.Control = (event.xkey.state & ControlMask) != 0;\r
763                                 irrevent.MouseInput.Shift = (event.xkey.state & ShiftMask) != 0;\r
764 \r
765                                 // mouse button states\r
766                                 irrevent.MouseInput.ButtonStates = (event.xbutton.state & Button1Mask) ? irr::EMBSM_LEFT : 0;\r
767                                 irrevent.MouseInput.ButtonStates |= (event.xbutton.state & Button3Mask) ? irr::EMBSM_RIGHT : 0;\r
768                                 irrevent.MouseInput.ButtonStates |= (event.xbutton.state & Button2Mask) ? irr::EMBSM_MIDDLE : 0;\r
769 \r
770                                 postEventFromUser(irrevent);\r
771                                 break;\r
772 \r
773                         case ButtonPress:\r
774                         case ButtonRelease:\r
775 \r
776                                 irrevent.EventType = irr::EET_MOUSE_INPUT_EVENT;\r
777                                 irrevent.MouseInput.X = event.xbutton.x;\r
778                                 irrevent.MouseInput.Y = event.xbutton.y;\r
779                                 irrevent.MouseInput.Control = (event.xkey.state & ControlMask) != 0;\r
780                                 irrevent.MouseInput.Shift = (event.xkey.state & ShiftMask) != 0;\r
781 \r
782                                 // mouse button states\r
783                                 // This sets the state which the buttons had _prior_ to the event.\r
784                                 // So unlike on Windows the button which just got changed has still the old state here.\r
785                                 // We handle that below by flipping the corresponding bit later.\r
786                                 irrevent.MouseInput.ButtonStates = (event.xbutton.state & Button1Mask) ? irr::EMBSM_LEFT : 0;\r
787                                 irrevent.MouseInput.ButtonStates |= (event.xbutton.state & Button3Mask) ? irr::EMBSM_RIGHT : 0;\r
788                                 irrevent.MouseInput.ButtonStates |= (event.xbutton.state & Button2Mask) ? irr::EMBSM_MIDDLE : 0;\r
789 \r
790                                 irrevent.MouseInput.Event = irr::EMIE_COUNT;\r
791 \r
792                                 switch(event.xbutton.button)\r
793                                 {\r
794                                 case  Button1:\r
795                                         irrevent.MouseInput.Event =\r
796                                                 (event.type == ButtonPress) ? irr::EMIE_LMOUSE_PRESSED_DOWN : irr::EMIE_LMOUSE_LEFT_UP;\r
797                                         irrevent.MouseInput.ButtonStates ^= irr::EMBSM_LEFT;\r
798                                         break;\r
799 \r
800                                 case  Button3:\r
801                                         irrevent.MouseInput.Event =\r
802                                                 (event.type == ButtonPress) ? irr::EMIE_RMOUSE_PRESSED_DOWN : irr::EMIE_RMOUSE_LEFT_UP;\r
803                                         irrevent.MouseInput.ButtonStates ^= irr::EMBSM_RIGHT;\r
804                                         break;\r
805 \r
806                                 case  Button2:\r
807                                         irrevent.MouseInput.Event =\r
808                                                 (event.type == ButtonPress) ? irr::EMIE_MMOUSE_PRESSED_DOWN : irr::EMIE_MMOUSE_LEFT_UP;\r
809                                         irrevent.MouseInput.ButtonStates ^= irr::EMBSM_MIDDLE;\r
810                                         break;\r
811 \r
812                                 case  Button4:\r
813                                         if (event.type == ButtonPress)\r
814                                         {\r
815                                                 irrevent.MouseInput.Event = EMIE_MOUSE_WHEEL;\r
816                                                 irrevent.MouseInput.Wheel = 1.0f;\r
817                                         }\r
818                                         break;\r
819 \r
820                                 case  Button5:\r
821                                         if (event.type == ButtonPress)\r
822                                         {\r
823                                                 irrevent.MouseInput.Event = EMIE_MOUSE_WHEEL;\r
824                                                 irrevent.MouseInput.Wheel = -1.0f;\r
825                                         }\r
826                                         break;\r
827                                 }\r
828 \r
829                                 if (irrevent.MouseInput.Event != irr::EMIE_COUNT)\r
830                                 {\r
831                                         postEventFromUser(irrevent);\r
832 \r
833                                         if ( irrevent.MouseInput.Event >= EMIE_LMOUSE_PRESSED_DOWN && irrevent.MouseInput.Event <= EMIE_MMOUSE_PRESSED_DOWN )\r
834                                         {\r
835                                                 u32 clicks = checkSuccessiveClicks(irrevent.MouseInput.X, irrevent.MouseInput.Y, irrevent.MouseInput.Event);\r
836                                                 if ( clicks == 2 )\r
837                                                 {\r
838                                                         irrevent.MouseInput.Event = (EMOUSE_INPUT_EVENT)(EMIE_LMOUSE_DOUBLE_CLICK + irrevent.MouseInput.Event-EMIE_LMOUSE_PRESSED_DOWN);\r
839                                                         postEventFromUser(irrevent);\r
840                                                 }\r
841                                                 else if ( clicks == 3 )\r
842                                                 {\r
843                                                         irrevent.MouseInput.Event = (EMOUSE_INPUT_EVENT)(EMIE_LMOUSE_TRIPLE_CLICK + irrevent.MouseInput.Event-EMIE_LMOUSE_PRESSED_DOWN);\r
844                                                         postEventFromUser(irrevent);\r
845                                                 }\r
846                                         }\r
847                                 }\r
848                                 break;\r
849 \r
850                         case MappingNotify:\r
851                                 XRefreshKeyboardMapping (&event.xmapping) ;\r
852                                 break;\r
853 \r
854                         case KeyRelease:\r
855                                 if (0 == AutorepeatSupport && (XPending( XDisplay ) > 0) )\r
856                                 {\r
857                                         // check for Autorepeat manually\r
858                                         // We'll do the same as Windows does: Only send KeyPressed\r
859                                         // So every KeyRelease is a real release\r
860                                         XEvent next_event;\r
861                                         XPeekEvent (event.xkey.display, &next_event);\r
862                                         if ((next_event.type == KeyPress) &&\r
863                                                 (next_event.xkey.keycode == event.xkey.keycode) &&\r
864                                                 (next_event.xkey.time - event.xkey.time) < 2)   // usually same time, but on some systems a difference of 1 is possible\r
865                                         {\r
866                                                 // Ignore the key release event\r
867                                                 break;\r
868                                         }\r
869                                 }\r
870 \r
871                                 irrevent.EventType = irr::EET_KEY_INPUT_EVENT;\r
872                                 irrevent.KeyInput.PressedDown = false;\r
873                                 irrevent.KeyInput.Char = 0;     // on release that's undefined\r
874                                 irrevent.KeyInput.Control = (event.xkey.state & ControlMask) != 0;\r
875                                 irrevent.KeyInput.Shift = (event.xkey.state & ShiftMask) != 0;\r
876                                 irrevent.KeyInput.Key = getKeyCode(event);\r
877 \r
878                                 postEventFromUser(irrevent);\r
879                                 break;\r
880 \r
881                         case KeyPress:\r
882                                 {\r
883                                         SKeyMap mp;\r
884                                         if ( XInputContext )\r
885                                         {\r
886                                                 wchar_t buf[64]={0};\r
887                                                 Status status;\r
888                                                 int strLen = XwcLookupString(XInputContext, &event.xkey, buf, sizeof(buf)/sizeof(wchar_t)-1, &mp.X11Key, &status);\r
889                                                 if ( status == XBufferOverflow )\r
890                                                 {\r
891                                                         os::Printer::log("XwcLookupString needs a larger buffer", ELL_INFORMATION);\r
892                                                 }\r
893                                                 if ( strLen > 0 && (status == XLookupChars || status == XLookupBoth) )\r
894                                                 {\r
895                                                         if (strLen > 1)\r
896                                                         {\r
897                                                                 // Multiple characters: send string event\r
898                                                                 irrevent.EventType = irr::EET_STRING_INPUT_EVENT;\r
899                                                                 irrevent.StringInput.Str = new core::stringw(buf);\r
900                                                                 postEventFromUser(irrevent);\r
901                                                                 delete irrevent.StringInput.Str;\r
902                                                                 irrevent.StringInput.Str = NULL;\r
903                                                                 break;\r
904                                                         }\r
905                                                         else\r
906                                                         {\r
907                                                                 irrevent.KeyInput.Char = buf[0];\r
908                                                         }\r
909                                                 }\r
910                                                 else\r
911                                                 {\r
912 #if 0 // Most of those are fine - but useful to have the info when debugging Irrlicht itself.\r
913                                                         if ( status == XLookupNone )\r
914                                                                 os::Printer::log("XLookupNone", ELL_INFORMATION);\r
915                                                         else if ( status ==  XLookupKeySym )\r
916                                                                 // Getting this also when user did not set setlocale(LC_ALL, ""); and using an unknown locale\r
917                                                                 // XSupportsLocale doesn't seeem to catch that unfortunately - any other ideas to catch it are welcome.\r
918                                                                 os::Printer::log("XLookupKeySym", ELL_INFORMATION);\r
919                                                         else if ( status ==  XBufferOverflow )\r
920                                                                 os::Printer::log("XBufferOverflow", ELL_INFORMATION);\r
921                                                         else if ( strLen == 0 )\r
922                                                                 os::Printer::log("no string", ELL_INFORMATION);\r
923 #endif\r
924                                                         irrevent.KeyInput.Char = 0;\r
925                                                 }\r
926                                         }\r
927                                         else    // Old version without InputContext. Does not support i18n, but good to have as fallback.\r
928                                         {\r
929                                                 union\r
930                                                 {\r
931                                                         char buf[8];\r
932                                                         wchar_t wbuf[2];\r
933                                                 } tmp = {{0}};\r
934                                                 XLookupString(&event.xkey, tmp.buf, sizeof(tmp.buf), &mp.X11Key, NULL);\r
935                                                 irrevent.KeyInput.Char = tmp.wbuf[0];\r
936                                         }\r
937 \r
938                                         irrevent.EventType = irr::EET_KEY_INPUT_EVENT;\r
939                                         irrevent.KeyInput.PressedDown = true;\r
940                                         irrevent.KeyInput.Control = (event.xkey.state & ControlMask) != 0;\r
941                                         irrevent.KeyInput.Shift = (event.xkey.state & ShiftMask) != 0;\r
942                                         irrevent.KeyInput.Key = getKeyCode(event);\r
943                                         postEventFromUser(irrevent);\r
944                                 }\r
945                                 break;\r
946 \r
947                         case ClientMessage:\r
948                                 {\r
949                                         if (static_cast<Atom>(event.xclient.data.l[0]) == X_ATOM_WM_DELETE_WINDOW && X_ATOM_WM_DELETE_WINDOW != None)\r
950                                         {\r
951                                                 os::Printer::log("Quit message received.", ELL_INFORMATION);\r
952                                                 Close = true;\r
953                                         }\r
954                                         else\r
955                                         {\r
956                                                 // we assume it's a user message\r
957                                                 irrevent.EventType = irr::EET_USER_EVENT;\r
958                                                 irrevent.UserEvent.UserData1 = static_cast<size_t>(event.xclient.data.l[0]);\r
959                                                 irrevent.UserEvent.UserData2 = static_cast<size_t>(event.xclient.data.l[1]);\r
960                                                 postEventFromUser(irrevent);\r
961                                         }\r
962                                 }\r
963                                 break;\r
964 \r
965                         case SelectionRequest:\r
966                                 {\r
967                                         XSelectionRequestEvent *req = &(event.xselectionrequest);\r
968 \r
969                                         auto send_response = [this, req](Atom property) {\r
970                                                 XEvent response;\r
971                                                 response.xselection.type = SelectionNotify;\r
972                                                 response.xselection.display = req->display;\r
973                                                 response.xselection.requestor = req->requestor;\r
974                                                 response.xselection.selection = req->selection;\r
975                                                 response.xselection.target = req->target;\r
976                                                 response.xselection.property = property;\r
977                                                 response.xselection.time = req->time;\r
978                                                 XSendEvent (XDisplay, req->requestor, 0, 0, &response);\r
979                                                 XFlush (XDisplay);\r
980                                         };\r
981                                         auto send_response_refuse = [&send_response] {\r
982                                                 send_response(None);\r
983                                         };\r
984 \r
985                                         // sets the required property to data of type type and\r
986                                         // sends the according response\r
987                                         auto set_property_and_notify = [this, req, &send_response]\r
988                                                         (Atom type, int format, const void *data, u32 data_size) {\r
989                                                 XChangeProperty(XDisplay,\r
990                                                                 req->requestor,\r
991                                                                 req->property,\r
992                                                                 type,\r
993                                                                 format,\r
994                                                                 PropModeReplace,\r
995                                                                 (const unsigned char *)data,\r
996                                                                 data_size);\r
997                                                 send_response(req->property);\r
998                                         };\r
999 \r
1000                                         if (req->selection != X_ATOM_CLIPBOARD ||\r
1001                                                         req->owner != XWindow) {\r
1002                                                 // we are not the owner, refuse request\r
1003                                                 send_response_refuse();\r
1004                                                 break;\r
1005                                         }\r
1006 \r
1007                                         // for debugging:\r
1008                                         //~ {\r
1009                                                 //~ char *target_name = XGetAtomName(XDisplay, req->target);\r
1010                                                 //~ fprintf(stderr, "CIrrDeviceLinux::run: target: %s (=%ld)\n",\r
1011                                                                 //~ target_name, req->target);\r
1012                                                 //~ XFree(target_name);\r
1013                                         //~ }\r
1014 \r
1015                                         if (req->property == None) {\r
1016                                                 // req is from obsolete client, use target as property name\r
1017                                                 // and X_ATOM_UTF8_STRING as type\r
1018                                                 // Note: this was not tested and might be incorrect\r
1019                                                 os::Printer::log("CIrrDeviceLinux::run: SelectionRequest from obsolete client",\r
1020                                                                 ELL_WARNING);\r
1021                                                 XChangeProperty(XDisplay,\r
1022                                                                 req->requestor,\r
1023                                                                 req->target, X_ATOM_UTF8_STRING,\r
1024                                                                 8, // format = 8-bit\r
1025                                                                 PropModeReplace,\r
1026                                                                 (unsigned char *)Clipboard.c_str(),\r
1027                                                                 Clipboard.size());\r
1028                                                 send_response(req->target);\r
1029                                                 break;\r
1030                                         }\r
1031 \r
1032                                         if (req->target == X_ATOM_TARGETS) {\r
1033                                                 Atom data[] = {\r
1034                                                         X_ATOM_TARGETS,\r
1035                                                         X_ATOM_TEXT,\r
1036                                                         X_ATOM_UTF8_STRING,\r
1037                                                         X_ATOM_UTF8_MIME_TYPE\r
1038                                                 };\r
1039                                                 set_property_and_notify(\r
1040                                                                 XA_ATOM,\r
1041                                                                 32, // Atom is long, we need to set 32 for longs\r
1042                                                                 &data,\r
1043                                                                 sizeof(data) / sizeof(*data)\r
1044                                                         );\r
1045 \r
1046                                         } else if (req->target == X_ATOM_TEXT ||\r
1047                                                         req->target == X_ATOM_UTF8_STRING ||\r
1048                                                         req->target == X_ATOM_UTF8_MIME_TYPE) {\r
1049                                                 set_property_and_notify(\r
1050                                                                 X_ATOM_UTF8_STRING,\r
1051                                                                 8,\r
1052                                                                 Clipboard.c_str(),\r
1053                                                                 Clipboard.size()\r
1054                                                         );\r
1055 \r
1056                                         } else {\r
1057                                                 // refuse the request\r
1058                                                 send_response_refuse();\r
1059                                         }\r
1060                                 }\r
1061                                 break;\r
1062 \r
1063 #if defined(_IRR_LINUX_X11_XINPUT2_)\r
1064                         case GenericEvent:\r
1065                                 {\r
1066                                         XGenericEventCookie *cookie = &event.xcookie;\r
1067                                         if (XGetEventData(XDisplay, cookie) && cookie->extension == XI_EXTENSIONS_OPCODE && XI_EXTENSIONS_OPCODE\r
1068                                         && (cookie->evtype == XI_TouchUpdate || cookie->evtype == XI_TouchBegin || cookie->evtype == XI_TouchEnd))\r
1069                                         {\r
1070                                                 XIDeviceEvent *de = (XIDeviceEvent *) cookie->data;\r
1071 \r
1072                                                 irrevent.EventType = EET_TOUCH_INPUT_EVENT;\r
1073 \r
1074                                                 irrevent.TouchInput.Event = cookie->evtype == XI_TouchUpdate ? ETIE_MOVED : (cookie->evtype == XI_TouchBegin ? ETIE_PRESSED_DOWN : ETIE_LEFT_UP);\r
1075 \r
1076                                                 irrevent.TouchInput.ID = de->detail;\r
1077                                                 irrevent.TouchInput.X = de->event_x;\r
1078                                                 irrevent.TouchInput.Y = de->event_y;\r
1079 \r
1080                                                 if (irrevent.TouchInput.Event == ETIE_PRESSED_DOWN) {\r
1081                                                         currentTouchedCount++;\r
1082                                                 }\r
1083                                                 irrevent.TouchInput.touchedCount = currentTouchedCount;\r
1084                                                 if (currentTouchedCount > 0 && irrevent.TouchInput.Event == ETIE_LEFT_UP) {\r
1085                                                         currentTouchedCount--;\r
1086                                                 }\r
1087 \r
1088                                                 postEventFromUser(irrevent);\r
1089                                         }\r
1090                                 }\r
1091                                 break;\r
1092 #endif\r
1093 \r
1094                         default:\r
1095                                 break;\r
1096                         } // end switch\r
1097 \r
1098                         // Update IME information\r
1099                         if (XInputContext && GUIEnvironment)\r
1100                         {\r
1101                                 gui::IGUIElement *elem = GUIEnvironment->getFocus();\r
1102                                 if (elem && elem->acceptsIME())\r
1103                                 {\r
1104                                         core::rect<s32> r = elem->getAbsolutePosition();\r
1105                                         XPoint p;\r
1106                                         p.x = (short)r.UpperLeftCorner.X;\r
1107                                         p.y = (short)r.LowerRightCorner.Y;\r
1108                                         XSetICFocus(XInputContext);\r
1109                                         XVaNestedList l = XVaCreateNestedList(0, XNSpotLocation, &p, NULL);\r
1110                                         XSetICValues(XInputContext, XNPreeditAttributes, l, NULL);\r
1111                                         XFree(l);\r
1112                                 } else {\r
1113                                         XUnsetICFocus(XInputContext);\r
1114                                 }\r
1115                         }\r
1116 \r
1117                 } // end while\r
1118 \r
1119         }\r
1120 #endif //_IRR_COMPILE_WITH_X11_\r
1121 \r
1122         if (!Close)\r
1123                 pollJoysticks();\r
1124 \r
1125         return !Close;\r
1126 }\r
1127 \r
1128 \r
1129 //! Pause the current process for the minimum time allowed only to allow other processes to execute\r
1130 void CIrrDeviceLinux::yield()\r
1131 {\r
1132         struct timespec ts = {0,1};\r
1133         nanosleep(&ts, NULL);\r
1134 }\r
1135 \r
1136 \r
1137 //! Pause execution and let other processes to run for a specified amount of time.\r
1138 void CIrrDeviceLinux::sleep(u32 timeMs, bool pauseTimer=false)\r
1139 {\r
1140         const bool wasStopped = Timer ? Timer->isStopped() : true;\r
1141 \r
1142         struct timespec ts;\r
1143         ts.tv_sec = (time_t) (timeMs / 1000);\r
1144         ts.tv_nsec = (long) (timeMs % 1000) * 1000000;\r
1145 \r
1146         if (pauseTimer && !wasStopped)\r
1147                 Timer->stop();\r
1148 \r
1149         nanosleep(&ts, NULL);\r
1150 \r
1151         if (pauseTimer && !wasStopped)\r
1152                 Timer->start();\r
1153 }\r
1154 \r
1155 \r
1156 //! sets the caption of the window\r
1157 void CIrrDeviceLinux::setWindowCaption(const wchar_t* text)\r
1158 {\r
1159 #ifdef _IRR_COMPILE_WITH_X11_\r
1160         if (CreationParams.DriverType == video::EDT_NULL)\r
1161                 return;\r
1162 \r
1163         XTextProperty txt;\r
1164         if (Success==XwcTextListToTextProperty(XDisplay, const_cast<wchar_t**>(&text),\r
1165                                 1, XStdICCTextStyle, &txt))\r
1166         {\r
1167                 XSetWMName(XDisplay, XWindow, &txt);\r
1168                 XSetWMIconName(XDisplay, XWindow, &txt);\r
1169                 XFree(txt.value);\r
1170         }\r
1171 #endif\r
1172 }\r
1173 \r
1174 \r
1175 //! notifies the device that it should close itself\r
1176 void CIrrDeviceLinux::closeDevice()\r
1177 {\r
1178         Close = true;\r
1179 }\r
1180 \r
1181 \r
1182 //! returns if window is active. if not, nothing need to be drawn\r
1183 bool CIrrDeviceLinux::isWindowActive() const\r
1184 {\r
1185         return (WindowHasFocus && !WindowMinimized);\r
1186 }\r
1187 \r
1188 \r
1189 //! returns if window has focus.\r
1190 bool CIrrDeviceLinux::isWindowFocused() const\r
1191 {\r
1192         return WindowHasFocus;\r
1193 }\r
1194 \r
1195 \r
1196 //! returns if window is minimized.\r
1197 bool CIrrDeviceLinux::isWindowMinimized() const\r
1198 {\r
1199         return WindowMinimized;\r
1200 }\r
1201 \r
1202 \r
1203 //! returns color format of the window.\r
1204 video::ECOLOR_FORMAT CIrrDeviceLinux::getColorFormat() const\r
1205 {\r
1206 #ifdef _IRR_COMPILE_WITH_X11_\r
1207         if (VisualInfo && (VisualInfo->depth != 16))\r
1208                 return video::ECF_R8G8B8;\r
1209         else\r
1210 #endif\r
1211                 return video::ECF_R5G6B5;\r
1212 }\r
1213 \r
1214 \r
1215 //! Sets if the window should be resizable in windowed mode.\r
1216 void CIrrDeviceLinux::setResizable(bool resize)\r
1217 {\r
1218 #ifdef _IRR_COMPILE_WITH_X11_\r
1219         if (CreationParams.DriverType == video::EDT_NULL || CreationParams.Fullscreen )\r
1220                 return;\r
1221 \r
1222         if ( !resize )\r
1223         {\r
1224                 // Must be heap memory because data size depends on X Server\r
1225                 XSizeHints *hints = XAllocSizeHints();\r
1226                 hints->flags=PSize|PMinSize|PMaxSize;\r
1227                 hints->min_width=hints->max_width=hints->base_width=Width;\r
1228                 hints->min_height=hints->max_height=hints->base_height=Height;\r
1229                 XSetWMNormalHints(XDisplay, XWindow, hints);\r
1230                 XFree(hints);\r
1231         }\r
1232         else\r
1233         {\r
1234                 XSetWMNormalHints(XDisplay, XWindow, StdHints);\r
1235         }\r
1236         XFlush(XDisplay);\r
1237 #endif // #ifdef _IRR_COMPILE_WITH_X11_\r
1238 }\r
1239 \r
1240 //! Resize the render window.\r
1241 void CIrrDeviceLinux::setWindowSize(const irr::core::dimension2d<u32>& size)\r
1242 {\r
1243 #ifdef _IRR_COMPILE_WITH_X11_\r
1244         if (CreationParams.DriverType == video::EDT_NULL || CreationParams.Fullscreen )\r
1245                 return;\r
1246 \r
1247         XWindowChanges values;\r
1248         values.width = size.Width;\r
1249         values.height = size.Height;\r
1250         XConfigureWindow(XDisplay, XWindow, CWWidth | CWHeight, &values);\r
1251         XFlush(XDisplay);\r
1252 #endif // #ifdef _IRR_COMPILE_WITH_X11_\r
1253 }\r
1254 \r
1255 \r
1256 //! Minimize window\r
1257 void CIrrDeviceLinux::minimizeWindow()\r
1258 {\r
1259 #ifdef _IRR_COMPILE_WITH_X11_\r
1260         XIconifyWindow(XDisplay, XWindow, Screennr);\r
1261 #endif\r
1262 }\r
1263 \r
1264 \r
1265 //! Maximize window\r
1266 void CIrrDeviceLinux::maximizeWindow()\r
1267 {\r
1268 #ifdef _IRR_COMPILE_WITH_X11_\r
1269         // Maximize is not implemented in bare X, it's a WM construct.\r
1270         if (HasNetWM)\r
1271         {\r
1272                 XEvent ev = {0};\r
1273 \r
1274                 ev.type = ClientMessage;\r
1275                 ev.xclient.window = XWindow;\r
1276                 ev.xclient.message_type = X_ATOM_NETWM_STATE;\r
1277                 ev.xclient.format = 32;\r
1278                 ev.xclient.data.l[0] = 1; // _NET_WM_STATE_ADD\r
1279                 ev.xclient.data.l[1] = X_ATOM_NETWM_MAXIMIZE_VERT;\r
1280                 ev.xclient.data.l[2] = X_ATOM_NETWM_MAXIMIZE_HORZ;\r
1281 \r
1282                 XSendEvent(XDisplay, DefaultRootWindow(XDisplay), false,\r
1283                                 SubstructureNotifyMask|SubstructureRedirectMask, &ev);\r
1284         }\r
1285 \r
1286         XMapWindow(XDisplay, XWindow);\r
1287 #endif\r
1288 }\r
1289 \r
1290 \r
1291 //! Restore original window size\r
1292 void CIrrDeviceLinux::restoreWindow()\r
1293 {\r
1294 #ifdef _IRR_COMPILE_WITH_X11_\r
1295         // Maximize is not implemented in bare X, it's a WM construct.\r
1296         if (HasNetWM)\r
1297         {\r
1298                 XEvent ev = {0};\r
1299 \r
1300                 ev.type = ClientMessage;\r
1301                 ev.xclient.window = XWindow;\r
1302                 ev.xclient.message_type = X_ATOM_NETWM_STATE;\r
1303                 ev.xclient.format = 32;\r
1304                 ev.xclient.data.l[0] = 0; // _NET_WM_STATE_REMOVE\r
1305                 ev.xclient.data.l[1] = X_ATOM_NETWM_MAXIMIZE_VERT;\r
1306                 ev.xclient.data.l[2] = X_ATOM_NETWM_MAXIMIZE_HORZ;\r
1307 \r
1308                 XSendEvent(XDisplay, DefaultRootWindow(XDisplay), false,\r
1309                                 SubstructureNotifyMask|SubstructureRedirectMask, &ev);\r
1310         }\r
1311 \r
1312         XMapWindow(XDisplay, XWindow);\r
1313 #endif\r
1314 }\r
1315 \r
1316 core::position2di CIrrDeviceLinux::getWindowPosition()\r
1317 {\r
1318         int wx = 0, wy = 0;\r
1319 #ifdef _IRR_COMPILE_WITH_X11_\r
1320         Window child;\r
1321         XTranslateCoordinates(XDisplay, XWindow, DefaultRootWindow(XDisplay), 0, 0, &wx, &wy, &child);\r
1322 #endif\r
1323         return core::position2di(wx, wy);\r
1324 }\r
1325 \r
1326 void CIrrDeviceLinux::createKeyMap()\r
1327 {\r
1328         // I don't know if this is the best method  to create\r
1329         // the lookuptable, but I'll leave it like that until\r
1330         // I find a better version.\r
1331         // Search for missing numbers in keysymdef.h\r
1332 \r
1333 #ifdef _IRR_COMPILE_WITH_X11_\r
1334         KeyMap.reallocate(190);\r
1335         KeyMap.push_back(SKeyMap(XK_BackSpace, KEY_BACK));\r
1336         KeyMap.push_back(SKeyMap(XK_Tab, KEY_TAB));\r
1337         KeyMap.push_back(SKeyMap(XK_ISO_Left_Tab, KEY_TAB));\r
1338         KeyMap.push_back(SKeyMap(XK_Linefeed, 0)); // ???\r
1339         KeyMap.push_back(SKeyMap(XK_Clear, KEY_CLEAR));\r
1340         KeyMap.push_back(SKeyMap(XK_Return, KEY_RETURN));\r
1341         KeyMap.push_back(SKeyMap(XK_Pause, KEY_PAUSE));\r
1342         KeyMap.push_back(SKeyMap(XK_Scroll_Lock, KEY_SCROLL));\r
1343         KeyMap.push_back(SKeyMap(XK_Sys_Req, 0)); // ???\r
1344         KeyMap.push_back(SKeyMap(XK_Escape, KEY_ESCAPE));\r
1345         KeyMap.push_back(SKeyMap(XK_Insert, KEY_INSERT));\r
1346         KeyMap.push_back(SKeyMap(XK_Delete, KEY_DELETE));\r
1347         KeyMap.push_back(SKeyMap(XK_Home, KEY_HOME));\r
1348         KeyMap.push_back(SKeyMap(XK_Left, KEY_LEFT));\r
1349         KeyMap.push_back(SKeyMap(XK_Up, KEY_UP));\r
1350         KeyMap.push_back(SKeyMap(XK_Right, KEY_RIGHT));\r
1351         KeyMap.push_back(SKeyMap(XK_Down, KEY_DOWN));\r
1352         KeyMap.push_back(SKeyMap(XK_Prior, KEY_PRIOR));\r
1353         KeyMap.push_back(SKeyMap(XK_Page_Up, KEY_PRIOR));\r
1354         KeyMap.push_back(SKeyMap(XK_Next, KEY_NEXT));\r
1355         KeyMap.push_back(SKeyMap(XK_Page_Down, KEY_NEXT));\r
1356         KeyMap.push_back(SKeyMap(XK_End, KEY_END));\r
1357         KeyMap.push_back(SKeyMap(XK_Begin, KEY_HOME));\r
1358         KeyMap.push_back(SKeyMap(XK_Num_Lock, KEY_NUMLOCK));\r
1359         KeyMap.push_back(SKeyMap(XK_KP_Space, KEY_SPACE));\r
1360         KeyMap.push_back(SKeyMap(XK_KP_Tab, KEY_TAB));\r
1361         KeyMap.push_back(SKeyMap(XK_KP_Enter, KEY_RETURN));\r
1362         KeyMap.push_back(SKeyMap(XK_KP_F1, KEY_F1));\r
1363         KeyMap.push_back(SKeyMap(XK_KP_F2, KEY_F2));\r
1364         KeyMap.push_back(SKeyMap(XK_KP_F3, KEY_F3));\r
1365         KeyMap.push_back(SKeyMap(XK_KP_F4, KEY_F4));\r
1366         KeyMap.push_back(SKeyMap(XK_KP_Home, KEY_HOME));\r
1367         KeyMap.push_back(SKeyMap(XK_KP_Left, KEY_LEFT));\r
1368         KeyMap.push_back(SKeyMap(XK_KP_Up, KEY_UP));\r
1369         KeyMap.push_back(SKeyMap(XK_KP_Right, KEY_RIGHT));\r
1370         KeyMap.push_back(SKeyMap(XK_KP_Down, KEY_DOWN));\r
1371         KeyMap.push_back(SKeyMap(XK_Print, KEY_PRINT));\r
1372         KeyMap.push_back(SKeyMap(XK_KP_Prior, KEY_PRIOR));\r
1373         KeyMap.push_back(SKeyMap(XK_KP_Page_Up, KEY_PRIOR));\r
1374         KeyMap.push_back(SKeyMap(XK_KP_Next, KEY_NEXT));\r
1375         KeyMap.push_back(SKeyMap(XK_KP_Page_Down, KEY_NEXT));\r
1376         KeyMap.push_back(SKeyMap(XK_KP_End, KEY_END));\r
1377         KeyMap.push_back(SKeyMap(XK_KP_Begin, KEY_HOME));\r
1378         KeyMap.push_back(SKeyMap(XK_KP_Insert, KEY_INSERT));\r
1379         KeyMap.push_back(SKeyMap(XK_KP_Delete, KEY_DELETE));\r
1380         KeyMap.push_back(SKeyMap(XK_KP_Equal, 0)); // ???\r
1381         KeyMap.push_back(SKeyMap(XK_KP_Multiply, KEY_MULTIPLY));\r
1382         KeyMap.push_back(SKeyMap(XK_KP_Add, KEY_ADD));\r
1383         KeyMap.push_back(SKeyMap(XK_KP_Separator, KEY_SEPARATOR));\r
1384         KeyMap.push_back(SKeyMap(XK_KP_Subtract, KEY_SUBTRACT));\r
1385         KeyMap.push_back(SKeyMap(XK_KP_Decimal, KEY_DECIMAL));\r
1386         KeyMap.push_back(SKeyMap(XK_KP_Divide, KEY_DIVIDE));\r
1387         KeyMap.push_back(SKeyMap(XK_KP_0, KEY_NUMPAD0));\r
1388         KeyMap.push_back(SKeyMap(XK_KP_1, KEY_NUMPAD1));\r
1389         KeyMap.push_back(SKeyMap(XK_KP_2, KEY_NUMPAD2));\r
1390         KeyMap.push_back(SKeyMap(XK_KP_3, KEY_NUMPAD3));\r
1391         KeyMap.push_back(SKeyMap(XK_KP_4, KEY_NUMPAD4));\r
1392         KeyMap.push_back(SKeyMap(XK_KP_5, KEY_NUMPAD5));\r
1393         KeyMap.push_back(SKeyMap(XK_KP_6, KEY_NUMPAD6));\r
1394         KeyMap.push_back(SKeyMap(XK_KP_7, KEY_NUMPAD7));\r
1395         KeyMap.push_back(SKeyMap(XK_KP_8, KEY_NUMPAD8));\r
1396         KeyMap.push_back(SKeyMap(XK_KP_9, KEY_NUMPAD9));\r
1397         KeyMap.push_back(SKeyMap(XK_F1, KEY_F1));\r
1398         KeyMap.push_back(SKeyMap(XK_F2, KEY_F2));\r
1399         KeyMap.push_back(SKeyMap(XK_F3, KEY_F3));\r
1400         KeyMap.push_back(SKeyMap(XK_F4, KEY_F4));\r
1401         KeyMap.push_back(SKeyMap(XK_F5, KEY_F5));\r
1402         KeyMap.push_back(SKeyMap(XK_F6, KEY_F6));\r
1403         KeyMap.push_back(SKeyMap(XK_F7, KEY_F7));\r
1404         KeyMap.push_back(SKeyMap(XK_F8, KEY_F8));\r
1405         KeyMap.push_back(SKeyMap(XK_F9, KEY_F9));\r
1406         KeyMap.push_back(SKeyMap(XK_F10, KEY_F10));\r
1407         KeyMap.push_back(SKeyMap(XK_F11, KEY_F11));\r
1408         KeyMap.push_back(SKeyMap(XK_F12, KEY_F12));\r
1409         KeyMap.push_back(SKeyMap(XK_Shift_L, KEY_LSHIFT));\r
1410         KeyMap.push_back(SKeyMap(XK_Shift_R, KEY_RSHIFT));\r
1411         KeyMap.push_back(SKeyMap(XK_Control_L, KEY_LCONTROL));\r
1412         KeyMap.push_back(SKeyMap(XK_Control_R, KEY_RCONTROL));\r
1413         KeyMap.push_back(SKeyMap(XK_Caps_Lock, KEY_CAPITAL));\r
1414         KeyMap.push_back(SKeyMap(XK_Shift_Lock, KEY_CAPITAL));\r
1415         KeyMap.push_back(SKeyMap(XK_Meta_L, KEY_LWIN));\r
1416         KeyMap.push_back(SKeyMap(XK_Meta_R, KEY_RWIN));\r
1417         KeyMap.push_back(SKeyMap(XK_Alt_L, KEY_LMENU));\r
1418         KeyMap.push_back(SKeyMap(XK_Alt_R, KEY_RMENU));\r
1419         KeyMap.push_back(SKeyMap(XK_ISO_Level3_Shift, KEY_RMENU));\r
1420         KeyMap.push_back(SKeyMap(XK_Menu, KEY_MENU));\r
1421         KeyMap.push_back(SKeyMap(XK_space, KEY_SPACE));\r
1422         KeyMap.push_back(SKeyMap(XK_exclam, 0)); //?\r
1423         KeyMap.push_back(SKeyMap(XK_quotedbl, 0)); //?\r
1424         KeyMap.push_back(SKeyMap(XK_section, 0)); //?\r
1425         KeyMap.push_back(SKeyMap(XK_numbersign, KEY_OEM_2));\r
1426         KeyMap.push_back(SKeyMap(XK_dollar, 0)); //?\r
1427         KeyMap.push_back(SKeyMap(XK_percent, 0)); //?\r
1428         KeyMap.push_back(SKeyMap(XK_ampersand, 0)); //?\r
1429         KeyMap.push_back(SKeyMap(XK_apostrophe, KEY_OEM_7));\r
1430         KeyMap.push_back(SKeyMap(XK_parenleft, 0)); //?\r
1431         KeyMap.push_back(SKeyMap(XK_parenright, 0)); //?\r
1432         KeyMap.push_back(SKeyMap(XK_asterisk, 0)); //?\r
1433         KeyMap.push_back(SKeyMap(XK_plus, KEY_PLUS)); //?\r
1434         KeyMap.push_back(SKeyMap(XK_comma, KEY_COMMA)); //?\r
1435         KeyMap.push_back(SKeyMap(XK_minus, KEY_MINUS)); //?\r
1436         KeyMap.push_back(SKeyMap(XK_period, KEY_PERIOD)); //?\r
1437         KeyMap.push_back(SKeyMap(XK_slash, KEY_OEM_2)); //?\r
1438         KeyMap.push_back(SKeyMap(XK_0, KEY_KEY_0));\r
1439         KeyMap.push_back(SKeyMap(XK_1, KEY_KEY_1));\r
1440         KeyMap.push_back(SKeyMap(XK_2, KEY_KEY_2));\r
1441         KeyMap.push_back(SKeyMap(XK_3, KEY_KEY_3));\r
1442         KeyMap.push_back(SKeyMap(XK_4, KEY_KEY_4));\r
1443         KeyMap.push_back(SKeyMap(XK_5, KEY_KEY_5));\r
1444         KeyMap.push_back(SKeyMap(XK_6, KEY_KEY_6));\r
1445         KeyMap.push_back(SKeyMap(XK_7, KEY_KEY_7));\r
1446         KeyMap.push_back(SKeyMap(XK_8, KEY_KEY_8));\r
1447         KeyMap.push_back(SKeyMap(XK_9, KEY_KEY_9));\r
1448         KeyMap.push_back(SKeyMap(XK_colon, 0)); //?\r
1449         KeyMap.push_back(SKeyMap(XK_semicolon, KEY_OEM_1));\r
1450         KeyMap.push_back(SKeyMap(XK_less, KEY_OEM_102));\r
1451         KeyMap.push_back(SKeyMap(XK_equal, KEY_PLUS));\r
1452         KeyMap.push_back(SKeyMap(XK_greater, 0)); //?\r
1453         KeyMap.push_back(SKeyMap(XK_question, 0)); //?\r
1454         KeyMap.push_back(SKeyMap(XK_at, KEY_KEY_2)); //?\r
1455         KeyMap.push_back(SKeyMap(XK_mu, 0)); //?\r
1456         KeyMap.push_back(SKeyMap(XK_EuroSign, 0)); //?\r
1457         KeyMap.push_back(SKeyMap(XK_A, KEY_KEY_A));\r
1458         KeyMap.push_back(SKeyMap(XK_B, KEY_KEY_B));\r
1459         KeyMap.push_back(SKeyMap(XK_C, KEY_KEY_C));\r
1460         KeyMap.push_back(SKeyMap(XK_D, KEY_KEY_D));\r
1461         KeyMap.push_back(SKeyMap(XK_E, KEY_KEY_E));\r
1462         KeyMap.push_back(SKeyMap(XK_F, KEY_KEY_F));\r
1463         KeyMap.push_back(SKeyMap(XK_G, KEY_KEY_G));\r
1464         KeyMap.push_back(SKeyMap(XK_H, KEY_KEY_H));\r
1465         KeyMap.push_back(SKeyMap(XK_I, KEY_KEY_I));\r
1466         KeyMap.push_back(SKeyMap(XK_J, KEY_KEY_J));\r
1467         KeyMap.push_back(SKeyMap(XK_K, KEY_KEY_K));\r
1468         KeyMap.push_back(SKeyMap(XK_L, KEY_KEY_L));\r
1469         KeyMap.push_back(SKeyMap(XK_M, KEY_KEY_M));\r
1470         KeyMap.push_back(SKeyMap(XK_N, KEY_KEY_N));\r
1471         KeyMap.push_back(SKeyMap(XK_O, KEY_KEY_O));\r
1472         KeyMap.push_back(SKeyMap(XK_P, KEY_KEY_P));\r
1473         KeyMap.push_back(SKeyMap(XK_Q, KEY_KEY_Q));\r
1474         KeyMap.push_back(SKeyMap(XK_R, KEY_KEY_R));\r
1475         KeyMap.push_back(SKeyMap(XK_S, KEY_KEY_S));\r
1476         KeyMap.push_back(SKeyMap(XK_T, KEY_KEY_T));\r
1477         KeyMap.push_back(SKeyMap(XK_U, KEY_KEY_U));\r
1478         KeyMap.push_back(SKeyMap(XK_V, KEY_KEY_V));\r
1479         KeyMap.push_back(SKeyMap(XK_W, KEY_KEY_W));\r
1480         KeyMap.push_back(SKeyMap(XK_X, KEY_KEY_X));\r
1481         KeyMap.push_back(SKeyMap(XK_Y, KEY_KEY_Y));\r
1482         KeyMap.push_back(SKeyMap(XK_Z, KEY_KEY_Z));\r
1483         KeyMap.push_back(SKeyMap(XK_bracketleft, KEY_OEM_4));\r
1484         KeyMap.push_back(SKeyMap(XK_backslash, KEY_OEM_5));\r
1485         KeyMap.push_back(SKeyMap(XK_bracketright, KEY_OEM_6));\r
1486         KeyMap.push_back(SKeyMap(XK_asciicircum, KEY_OEM_5));\r
1487         KeyMap.push_back(SKeyMap(XK_dead_circumflex, KEY_OEM_5));\r
1488         KeyMap.push_back(SKeyMap(XK_degree, 0)); //?\r
1489         KeyMap.push_back(SKeyMap(XK_underscore, KEY_MINUS)); //?\r
1490         KeyMap.push_back(SKeyMap(XK_grave, KEY_OEM_3));\r
1491         KeyMap.push_back(SKeyMap(XK_dead_grave, KEY_OEM_3));\r
1492         KeyMap.push_back(SKeyMap(XK_acute, KEY_OEM_6));\r
1493         KeyMap.push_back(SKeyMap(XK_dead_acute, KEY_OEM_6));\r
1494         KeyMap.push_back(SKeyMap(XK_a, KEY_KEY_A));\r
1495         KeyMap.push_back(SKeyMap(XK_b, KEY_KEY_B));\r
1496         KeyMap.push_back(SKeyMap(XK_c, KEY_KEY_C));\r
1497         KeyMap.push_back(SKeyMap(XK_d, KEY_KEY_D));\r
1498         KeyMap.push_back(SKeyMap(XK_e, KEY_KEY_E));\r
1499         KeyMap.push_back(SKeyMap(XK_f, KEY_KEY_F));\r
1500         KeyMap.push_back(SKeyMap(XK_g, KEY_KEY_G));\r
1501         KeyMap.push_back(SKeyMap(XK_h, KEY_KEY_H));\r
1502         KeyMap.push_back(SKeyMap(XK_i, KEY_KEY_I));\r
1503         KeyMap.push_back(SKeyMap(XK_j, KEY_KEY_J));\r
1504         KeyMap.push_back(SKeyMap(XK_k, KEY_KEY_K));\r
1505         KeyMap.push_back(SKeyMap(XK_l, KEY_KEY_L));\r
1506         KeyMap.push_back(SKeyMap(XK_m, KEY_KEY_M));\r
1507         KeyMap.push_back(SKeyMap(XK_n, KEY_KEY_N));\r
1508         KeyMap.push_back(SKeyMap(XK_o, KEY_KEY_O));\r
1509         KeyMap.push_back(SKeyMap(XK_p, KEY_KEY_P));\r
1510         KeyMap.push_back(SKeyMap(XK_q, KEY_KEY_Q));\r
1511         KeyMap.push_back(SKeyMap(XK_r, KEY_KEY_R));\r
1512         KeyMap.push_back(SKeyMap(XK_s, KEY_KEY_S));\r
1513         KeyMap.push_back(SKeyMap(XK_t, KEY_KEY_T));\r
1514         KeyMap.push_back(SKeyMap(XK_u, KEY_KEY_U));\r
1515         KeyMap.push_back(SKeyMap(XK_v, KEY_KEY_V));\r
1516         KeyMap.push_back(SKeyMap(XK_w, KEY_KEY_W));\r
1517         KeyMap.push_back(SKeyMap(XK_x, KEY_KEY_X));\r
1518         KeyMap.push_back(SKeyMap(XK_y, KEY_KEY_Y));\r
1519         KeyMap.push_back(SKeyMap(XK_z, KEY_KEY_Z));\r
1520         KeyMap.push_back(SKeyMap(XK_ssharp, KEY_OEM_4));\r
1521         KeyMap.push_back(SKeyMap(XK_adiaeresis, KEY_OEM_7));\r
1522         KeyMap.push_back(SKeyMap(XK_odiaeresis, KEY_OEM_3));\r
1523         KeyMap.push_back(SKeyMap(XK_udiaeresis, KEY_OEM_1));\r
1524         KeyMap.push_back(SKeyMap(XK_Super_L, KEY_LWIN));\r
1525         KeyMap.push_back(SKeyMap(XK_Super_R, KEY_RWIN));\r
1526 \r
1527         KeyMap.sort();\r
1528 #endif\r
1529 }\r
1530 \r
1531 bool CIrrDeviceLinux::activateJoysticks(core::array<SJoystickInfo> & joystickInfo)\r
1532 {\r
1533 #if defined (_IRR_COMPILE_WITH_JOYSTICK_EVENTS_)\r
1534 \r
1535         joystickInfo.clear();\r
1536 \r
1537         u32 joystick;\r
1538         for (joystick = 0; joystick < 32; ++joystick)\r
1539         {\r
1540                 // The joystick device could be here...\r
1541                 core::stringc devName = "/dev/js";\r
1542                 devName += joystick;\r
1543 \r
1544                 SJoystickInfo returnInfo;\r
1545                 JoystickInfo info;\r
1546 \r
1547                 info.fd = open(devName.c_str(), O_RDONLY);\r
1548                 if (-1 == info.fd)\r
1549                 {\r
1550                         // ...but Ubuntu and possibly other distros\r
1551                         // create the devices in /dev/input\r
1552                         devName = "/dev/input/js";\r
1553                         devName += joystick;\r
1554                         info.fd = open(devName.c_str(), O_RDONLY);\r
1555                         if (-1 == info.fd)\r
1556                         {\r
1557                                 // and BSD here\r
1558                                 devName = "/dev/joy";\r
1559                                 devName += joystick;\r
1560                                 info.fd = open(devName.c_str(), O_RDONLY);\r
1561                         }\r
1562                 }\r
1563 \r
1564                 if (-1 == info.fd)\r
1565                         continue;\r
1566 \r
1567 #ifdef __FreeBSD__\r
1568                 info.axes=2;\r
1569                 info.buttons=2;\r
1570 #else\r
1571                 ioctl( info.fd, JSIOCGAXES, &(info.axes) );\r
1572                 ioctl( info.fd, JSIOCGBUTTONS, &(info.buttons) );\r
1573                 fcntl( info.fd, F_SETFL, O_NONBLOCK );\r
1574 #endif\r
1575 \r
1576                 (void)memset(&info.persistentData, 0, sizeof(info.persistentData));\r
1577                 info.persistentData.EventType = irr::EET_JOYSTICK_INPUT_EVENT;\r
1578                 info.persistentData.JoystickEvent.Joystick = ActiveJoysticks.size();\r
1579 \r
1580                 // There's no obvious way to determine which (if any) axes represent a POV\r
1581                 // hat, so we'll just set it to "not used" and forget about it.\r
1582                 info.persistentData.JoystickEvent.POV = 65535;\r
1583 \r
1584                 ActiveJoysticks.push_back(info);\r
1585 \r
1586                 returnInfo.Joystick = joystick;\r
1587                 returnInfo.PovHat = SJoystickInfo::POV_HAT_UNKNOWN;\r
1588                 returnInfo.Axes = info.axes;\r
1589                 returnInfo.Buttons = info.buttons;\r
1590 \r
1591 #ifndef __FreeBSD__\r
1592                 char name[80];\r
1593                 ioctl( info.fd, JSIOCGNAME(80), name);\r
1594                 returnInfo.Name = name;\r
1595 #endif\r
1596 \r
1597                 joystickInfo.push_back(returnInfo);\r
1598         }\r
1599 \r
1600         for (joystick = 0; joystick < joystickInfo.size(); ++joystick)\r
1601         {\r
1602                 char logString[256];\r
1603                 snprintf_irr(logString, sizeof(logString), "Found joystick %d, %d axes, %d buttons '%s'",\r
1604                         joystick, joystickInfo[joystick].Axes,\r
1605                         joystickInfo[joystick].Buttons, joystickInfo[joystick].Name.c_str());\r
1606                 os::Printer::log(logString, ELL_INFORMATION);\r
1607         }\r
1608 \r
1609         return true;\r
1610 #else\r
1611         return false;\r
1612 #endif // _IRR_COMPILE_WITH_JOYSTICK_EVENTS_\r
1613 }\r
1614 \r
1615 \r
1616 void CIrrDeviceLinux::pollJoysticks()\r
1617 {\r
1618 #if defined (_IRR_COMPILE_WITH_JOYSTICK_EVENTS_)\r
1619         if (0 == ActiveJoysticks.size())\r
1620                 return;\r
1621 \r
1622         for (u32 j= 0; j< ActiveJoysticks.size(); ++j)\r
1623         {\r
1624                 JoystickInfo & info =  ActiveJoysticks[j];\r
1625 \r
1626 #ifdef __FreeBSD__\r
1627                 struct joystick js;\r
1628                 if (read(info.fd, &js, sizeof(js)) == sizeof(js))\r
1629                 {\r
1630                         info.persistentData.JoystickEvent.ButtonStates = js.b1 | (js.b2 << 1); /* should be a two-bit field */\r
1631                         info.persistentData.JoystickEvent.Axis[0] = js.x; /* X axis */\r
1632                         info.persistentData.JoystickEvent.Axis[1] = js.y; /* Y axis */\r
1633                 }\r
1634 #else\r
1635                 struct js_event event;\r
1636                 while (sizeof(event) == read(info.fd, &event, sizeof(event)))\r
1637                 {\r
1638                         switch(event.type & ~JS_EVENT_INIT)\r
1639                         {\r
1640                         case JS_EVENT_BUTTON:\r
1641                                 if (event.value)\r
1642                                                 info.persistentData.JoystickEvent.ButtonStates |= (1 << event.number);\r
1643                                 else\r
1644                                                 info.persistentData.JoystickEvent.ButtonStates &= ~(1 << event.number);\r
1645                                 break;\r
1646 \r
1647                         case JS_EVENT_AXIS:\r
1648                                 if (event.number < SEvent::SJoystickEvent::NUMBER_OF_AXES)\r
1649                                         info.persistentData.JoystickEvent.Axis[event.number] = event.value;\r
1650                                 break;\r
1651 \r
1652                         default:\r
1653                                 break;\r
1654                         }\r
1655                 }\r
1656 #endif\r
1657 \r
1658                 // Send an irrlicht joystick event once per ::run() even if no new data were received.\r
1659                 (void)postEventFromUser(info.persistentData);\r
1660         }\r
1661 #endif // _IRR_COMPILE_WITH_JOYSTICK_EVENTS_\r
1662 }\r
1663 \r
1664 \r
1665 //! gets text from the clipboard\r
1666 //! \return Returns 0 if no string is in there, otherwise utf-8 text.\r
1667 const c8 *CIrrDeviceLinux::getTextFromClipboard() const\r
1668 {\r
1669 #if defined(_IRR_COMPILE_WITH_X11_)\r
1670         Window ownerWindow = XGetSelectionOwner(XDisplay, X_ATOM_CLIPBOARD);\r
1671         if (ownerWindow == XWindow) {\r
1672                 return Clipboard.c_str();\r
1673         }\r
1674 \r
1675         Clipboard = "";\r
1676 \r
1677         if (ownerWindow == None) {\r
1678                 return Clipboard.c_str();\r
1679         }\r
1680 \r
1681         // delete the property to be set beforehand\r
1682         XDeleteProperty(XDisplay, XWindow, XA_PRIMARY);\r
1683 \r
1684         XConvertSelection(XDisplay, X_ATOM_CLIPBOARD, X_ATOM_UTF8_STRING, XA_PRIMARY,\r
1685                         XWindow, CurrentTime);\r
1686         XFlush(XDisplay);\r
1687 \r
1688         // wait for event via a blocking call\r
1689         XEvent event_ret;\r
1690         XIfEvent(XDisplay, &event_ret, [](Display *_display, XEvent *event, XPointer arg) {\r
1691                 return (Bool) (event->type == SelectionNotify &&\r
1692                                 event->xselection.requestor == *(Window *)arg &&\r
1693                                 event->xselection.selection == X_ATOM_CLIPBOARD &&\r
1694                                 event->xselection.target == X_ATOM_UTF8_STRING);\r
1695         }, (XPointer)&XWindow);\r
1696 \r
1697         _IRR_DEBUG_BREAK_IF(!(event_ret.type == SelectionNotify &&\r
1698                         event_ret.xselection.requestor == XWindow &&\r
1699                         event_ret.xselection.selection == X_ATOM_CLIPBOARD &&\r
1700                         event_ret.xselection.target == X_ATOM_UTF8_STRING));\r
1701 \r
1702         Atom property_set = event_ret.xselection.property;\r
1703         if (event_ret.xselection.property == None) {\r
1704                 // request failed => empty string\r
1705                 return Clipboard.c_str();\r
1706         }\r
1707 \r
1708         // check for data\r
1709         Atom type;\r
1710         int format;\r
1711         unsigned long numItems, bytesLeft, dummy;\r
1712         unsigned char *data = nullptr;\r
1713         XGetWindowProperty (XDisplay, XWindow,\r
1714                         property_set, // property name\r
1715                         0, // offset\r
1716                         0, // length (we only check for data, so 0)\r
1717                         0, // Delete 0==false\r
1718                         AnyPropertyType, // AnyPropertyType or property identifier\r
1719                         &type, // return type\r
1720                         &format, // return format\r
1721                         &numItems, // number items\r
1722                         &bytesLeft, // remaining bytes for partial reads\r
1723                         &data); // data\r
1724         if (data) {\r
1725                 XFree(data);\r
1726                 data = nullptr;\r
1727         }\r
1728 \r
1729         // for debugging:\r
1730         //~ {\r
1731                 //~ char *type_name = XGetAtomName(XDisplay, type);\r
1732                 //~ fprintf(stderr, "CIrrDeviceLinux::getTextFromClipboard: actual type: %s (=%ld)\n",\r
1733                                 //~ type_name, type);\r
1734                 //~ XFree(type_name);\r
1735         //~ }\r
1736 \r
1737         if (type != X_ATOM_UTF8_STRING && type != X_ATOM_UTF8_MIME_TYPE) {\r
1738                 os::Printer::log("CIrrDeviceLinux::getTextFromClipboard: did not get utf-8 string",\r
1739                                 ELL_WARNING);\r
1740                 return Clipboard.c_str();\r
1741         }\r
1742 \r
1743         if (bytesLeft > 0) {\r
1744                 // there is some data to get\r
1745                 int result = XGetWindowProperty (XDisplay, XWindow, property_set, 0,\r
1746                                                                         bytesLeft, 0, AnyPropertyType, &type, &format,\r
1747                                                                         &numItems, &dummy, &data);\r
1748                 if (result == Success)\r
1749                         Clipboard = (irr::c8 *)data;\r
1750                 XFree (data);\r
1751         }\r
1752 \r
1753         // delete the property again, to inform the owner about the successful transfer\r
1754         XDeleteProperty(XDisplay, XWindow, property_set);\r
1755 \r
1756         return Clipboard.c_str();\r
1757 \r
1758 #else\r
1759         return nullptr;\r
1760 #endif\r
1761 }\r
1762 \r
1763 //! copies text to the clipboard\r
1764 void CIrrDeviceLinux::copyToClipboard(const c8 *text) const\r
1765 {\r
1766 #if defined(_IRR_COMPILE_WITH_X11_)\r
1767         // Actually there is no clipboard on X but applications just say they own the clipboard and return text when asked.\r
1768         // Which btw. also means that on X you lose clipboard content when closing applications.\r
1769         Clipboard = text;\r
1770         XSetSelectionOwner (XDisplay, X_ATOM_CLIPBOARD, XWindow, CurrentTime);\r
1771         XFlush (XDisplay);\r
1772         Window owner = XGetSelectionOwner(XDisplay, X_ATOM_CLIPBOARD);\r
1773         if (owner != XWindow) {\r
1774                 os::Printer::log("CIrrDeviceLinux::copyToClipboard: failed to set owner", ELL_WARNING);\r
1775         }\r
1776 #endif\r
1777 }\r
1778 \r
1779 #ifdef _IRR_COMPILE_WITH_X11_\r
1780 // return true if the passed event has the type passed in parameter arg\r
1781 Bool PredicateIsEventType(Display *display, XEvent *event, XPointer arg)\r
1782 {\r
1783         if ( event && event->type == *(int*)arg )\r
1784         {\r
1785 //              os::Printer::log("remove event:", core::stringc((int)arg).c_str(), ELL_INFORMATION);\r
1786                 return True;\r
1787         }\r
1788         return False;\r
1789 }\r
1790 #endif //_IRR_COMPILE_WITH_X11_\r
1791 \r
1792 //! Remove all messages pending in the system message loop\r
1793 void CIrrDeviceLinux::clearSystemMessages()\r
1794 {\r
1795 #ifdef _IRR_COMPILE_WITH_X11_\r
1796         if (CreationParams.DriverType != video::EDT_NULL)\r
1797         {\r
1798                 XEvent event;\r
1799                 int usrArg = ButtonPress;\r
1800                 while ( XCheckIfEvent(XDisplay, &event, PredicateIsEventType, XPointer(&usrArg)) == True ) {}\r
1801                 usrArg = ButtonRelease;\r
1802                 while ( XCheckIfEvent(XDisplay, &event, PredicateIsEventType, XPointer(&usrArg)) == True ) {}\r
1803                 usrArg = MotionNotify;\r
1804                 while ( XCheckIfEvent(XDisplay, &event, PredicateIsEventType, XPointer(&usrArg)) == True ) {}\r
1805                 usrArg = KeyRelease;\r
1806                 while ( XCheckIfEvent(XDisplay, &event, PredicateIsEventType, XPointer(&usrArg)) == True ) {}\r
1807                 usrArg = KeyPress;\r
1808                 while ( XCheckIfEvent(XDisplay, &event, PredicateIsEventType, XPointer(&usrArg)) == True ) {}\r
1809         }\r
1810 #endif //_IRR_COMPILE_WITH_X11_\r
1811 }\r
1812 \r
1813 void CIrrDeviceLinux::initXAtoms()\r
1814 {\r
1815 #ifdef _IRR_COMPILE_WITH_X11_\r
1816         X_ATOM_CLIPBOARD = XInternAtom(XDisplay, "CLIPBOARD", False);\r
1817         X_ATOM_TARGETS = XInternAtom(XDisplay, "TARGETS", False);\r
1818         X_ATOM_UTF8_STRING = XInternAtom(XDisplay, "UTF8_STRING", False);\r
1819         X_ATOM_UTF8_MIME_TYPE = XInternAtom(XDisplay, "text/plain;charset=utf-8", False);\r
1820         X_ATOM_TEXT = XInternAtom(XDisplay, "TEXT", False);\r
1821         X_ATOM_NETWM_MAXIMIZE_VERT = XInternAtom(XDisplay, "_NET_WM_STATE_MAXIMIZED_VERT", true);\r
1822         X_ATOM_NETWM_MAXIMIZE_HORZ = XInternAtom(XDisplay, "_NET_WM_STATE_MAXIMIZED_HORZ", true);\r
1823         X_ATOM_NETWM_STATE = XInternAtom(XDisplay, "_NET_WM_STATE", true);\r
1824         X_ATOM_NETWM_STATE_FULLSCREEN = XInternAtom(XDisplay, "_NET_WM_STATE_FULLSCREEN", True);\r
1825 #endif\r
1826 }\r
1827 \r
1828 void CIrrDeviceLinux::initXInput2()\r
1829 {\r
1830 #if defined(_IRR_LINUX_X11_XINPUT2_)\r
1831         int ev=0;\r
1832         int err=0;\r
1833         if (!XQueryExtension(XDisplay, "XInputExtension", &XI_EXTENSIONS_OPCODE, &ev, &err))\r
1834         {\r
1835                 os::Printer::log("X Input extension not available.", ELL_WARNING);\r
1836                 return;\r
1837         }\r
1838 \r
1839         int major = 2;\r
1840         int minor = 3;\r
1841         int rc = XIQueryVersion(XDisplay, &major, &minor);\r
1842         if ( rc != Success )\r
1843         {\r
1844                 os::Printer::log("No XI2 support.", ELL_WARNING);\r
1845                 return;\r
1846         }\r
1847 \r
1848         // So far we only use XInput2 for touch events.\r
1849         // So we enable those and disable all other events for now.\r
1850         XIEventMask eventMask;\r
1851         unsigned char mask[XIMaskLen(XI_TouchEnd)];\r
1852         memset(mask, 0, sizeof(mask));\r
1853         eventMask.deviceid = XIAllMasterDevices;\r
1854         eventMask.mask_len = sizeof(mask);\r
1855         eventMask.mask = mask;\r
1856         XISetMask(eventMask.mask, XI_TouchBegin);\r
1857         XISetMask(eventMask.mask, XI_TouchUpdate);\r
1858         XISetMask(eventMask.mask, XI_TouchEnd);\r
1859 \r
1860         XISelectEvents(XDisplay, XWindow, &eventMask, 1);\r
1861 #endif\r
1862 }\r
1863 \r
1864 \r
1865 #ifdef _IRR_COMPILE_WITH_X11_\r
1866 \r
1867 Cursor CIrrDeviceLinux::TextureToMonochromeCursor(irr::video::ITexture * tex, const core::rect<s32>& sourceRect, const core::position2d<s32> &hotspot)\r
1868 {\r
1869         XImage * sourceImage = XCreateImage(XDisplay, VisualInfo->visual,\r
1870                                                                                 1, // depth,\r
1871                                                                                 ZPixmap,        // XYBitmap (depth=1), ZPixmap(depth=x)\r
1872                                                                                 0, 0, sourceRect.getWidth(), sourceRect.getHeight(),\r
1873                                                                                 32, // bitmap_pad,\r
1874                                                                                 0// bytes_per_line (0 means continuos in memory)\r
1875                                                                                 );\r
1876         sourceImage->data = new char[sourceImage->height * sourceImage->bytes_per_line];\r
1877         XImage * maskImage = XCreateImage(XDisplay, VisualInfo->visual,\r
1878                                                                                 1, // depth,\r
1879                                                                                 ZPixmap,\r
1880                                                                                 0, 0, sourceRect.getWidth(), sourceRect.getHeight(),\r
1881                                                                                 32, // bitmap_pad,\r
1882                                                                                 0 // bytes_per_line\r
1883                                                                                 );\r
1884         maskImage->data = new char[maskImage->height * maskImage->bytes_per_line];\r
1885 \r
1886         // write texture into XImage\r
1887         video::ECOLOR_FORMAT format = tex->getColorFormat();\r
1888         u32 bytesPerPixel = video::IImage::getBitsPerPixelFromFormat(format) / 8;\r
1889         u32 bytesLeftGap = sourceRect.UpperLeftCorner.X * bytesPerPixel;\r
1890         u32 bytesRightGap = tex->getPitch() - sourceRect.LowerRightCorner.X * bytesPerPixel;\r
1891         const u8* data = (const u8*)tex->lock(video::ETLM_READ_ONLY, 0);\r
1892         data += sourceRect.UpperLeftCorner.Y*tex->getPitch();\r
1893         for ( s32 y = 0; y < sourceRect.getHeight(); ++y )\r
1894         {\r
1895                 data += bytesLeftGap;\r
1896                 for ( s32 x = 0; x < sourceRect.getWidth(); ++x )\r
1897                 {\r
1898                         video::SColor pixelCol;\r
1899                         pixelCol.setData((const void*)data, format);\r
1900                         data += bytesPerPixel;\r
1901 \r
1902                         if ( pixelCol.getAlpha() == 0 ) // transparent\r
1903                         {\r
1904                                 XPutPixel(maskImage, x, y, 0);\r
1905                                 XPutPixel(sourceImage, x, y, 0);\r
1906                         }\r
1907                         else    // color\r
1908                         {\r
1909                                 if ( pixelCol.getAverage() >= 127 )\r
1910                                         XPutPixel(sourceImage, x, y, 1);\r
1911                                 else\r
1912                                         XPutPixel(sourceImage, x, y, 0);\r
1913                                 XPutPixel(maskImage, x, y, 1);\r
1914                         }\r
1915                 }\r
1916                 data += bytesRightGap;\r
1917         }\r
1918         tex->unlock();\r
1919 \r
1920         Pixmap sourcePixmap = XCreatePixmap(XDisplay, XWindow, sourceImage->width, sourceImage->height, sourceImage->depth);\r
1921         Pixmap maskPixmap = XCreatePixmap(XDisplay, XWindow, maskImage->width, maskImage->height, maskImage->depth);\r
1922 \r
1923         XGCValues values;\r
1924         values.foreground = 1;\r
1925         values.background = 1;\r
1926         GC gc = XCreateGC( XDisplay, sourcePixmap, GCForeground | GCBackground, &values );\r
1927 \r
1928         XPutImage(XDisplay, sourcePixmap, gc, sourceImage, 0, 0, 0, 0, sourceImage->width, sourceImage->height);\r
1929         XPutImage(XDisplay, maskPixmap, gc, maskImage, 0, 0, 0, 0, maskImage->width, maskImage->height);\r
1930 \r
1931         XFreeGC(XDisplay, gc);\r
1932         XDestroyImage(sourceImage);\r
1933         XDestroyImage(maskImage);\r
1934 \r
1935         Cursor cursorResult = 0;\r
1936         XColor foreground, background;\r
1937         foreground.red = 65535;\r
1938         foreground.green = 65535;\r
1939         foreground.blue = 65535;\r
1940         foreground.flags = DoRed | DoGreen | DoBlue;\r
1941         background.red = 0;\r
1942         background.green = 0;\r
1943         background.blue = 0;\r
1944         background.flags = DoRed | DoGreen | DoBlue;\r
1945 \r
1946         cursorResult = XCreatePixmapCursor(XDisplay, sourcePixmap, maskPixmap, &foreground, &background, hotspot.X, hotspot.Y);\r
1947 \r
1948         XFreePixmap(XDisplay, sourcePixmap);\r
1949         XFreePixmap(XDisplay, maskPixmap);\r
1950 \r
1951         return cursorResult;\r
1952 }\r
1953 \r
1954 #ifdef _IRR_LINUX_XCURSOR_\r
1955 Cursor CIrrDeviceLinux::TextureToARGBCursor(irr::video::ITexture * tex, const core::rect<s32>& sourceRect, const core::position2d<s32> &hotspot)\r
1956 {\r
1957         XcursorImage * image = XcursorImageCreate (sourceRect.getWidth(), sourceRect.getHeight());\r
1958         image->xhot = hotspot.X;\r
1959         image->yhot = hotspot.Y;\r
1960 \r
1961         // write texture into XcursorImage\r
1962         video::ECOLOR_FORMAT format = tex->getColorFormat();\r
1963         u32 bytesPerPixel = video::IImage::getBitsPerPixelFromFormat(format) / 8;\r
1964         u32 bytesLeftGap = sourceRect.UpperLeftCorner.X * bytesPerPixel;\r
1965         u32 bytesRightGap = tex->getPitch() - sourceRect.LowerRightCorner.X * bytesPerPixel;\r
1966         XcursorPixel* target = image->pixels;\r
1967         const u8* data = (const u8*)tex->lock(video::ETLM_READ_ONLY, 0);\r
1968         data += sourceRect.UpperLeftCorner.Y*tex->getPitch();\r
1969         for ( s32 y = 0; y < sourceRect.getHeight(); ++y )\r
1970         {\r
1971                 data += bytesLeftGap;\r
1972                 for ( s32 x = 0; x < sourceRect.getWidth(); ++x )\r
1973                 {\r
1974                         video::SColor pixelCol;\r
1975                         pixelCol.setData((const void*)data, format);\r
1976                         data += bytesPerPixel;\r
1977 \r
1978                         *target = (XcursorPixel)pixelCol.color;\r
1979                         ++target;\r
1980                 }\r
1981                 data += bytesRightGap;\r
1982         }\r
1983         tex->unlock();\r
1984 \r
1985         Cursor cursorResult=XcursorImageLoadCursor(XDisplay, image);\r
1986 \r
1987         XcursorImageDestroy(image);\r
1988 \r
1989 \r
1990         return cursorResult;\r
1991 }\r
1992 #endif // #ifdef _IRR_LINUX_XCURSOR_\r
1993 \r
1994 Cursor CIrrDeviceLinux::TextureToCursor(irr::video::ITexture * tex, const core::rect<s32>& sourceRect, const core::position2d<s32> &hotspot)\r
1995 {\r
1996 #ifdef _IRR_LINUX_XCURSOR_\r
1997         return TextureToARGBCursor( tex, sourceRect, hotspot );\r
1998 #else\r
1999         return TextureToMonochromeCursor( tex, sourceRect, hotspot );\r
2000 #endif\r
2001 }\r
2002 #endif  // _IRR_COMPILE_WITH_X11_\r
2003 \r
2004 \r
2005 CIrrDeviceLinux::CCursorControl::CCursorControl(CIrrDeviceLinux* dev, bool null)\r
2006         : Device(dev)\r
2007 #ifdef _IRR_COMPILE_WITH_X11_\r
2008         , PlatformBehavior(gui::ECPB_NONE), LastQuery(0)\r
2009 #ifdef _IRR_LINUX_X11_XINPUT2_\r
2010         , DeviceId(0)\r
2011 #endif\r
2012 #endif\r
2013         , IsVisible(true), Null(null), UseReferenceRect(false)\r
2014         , ActiveIcon(gui::ECI_NORMAL), ActiveIconStartTime(0)\r
2015 {\r
2016 #ifdef _IRR_COMPILE_WITH_X11_\r
2017         if (!Null)\r
2018         {\r
2019 #ifdef _IRR_LINUX_X11_XINPUT2_\r
2020                 // XIWarpPointer is entirely broken on multi-head setups (see also [1]),\r
2021                 // but behaves better in other cases so we can't just disable it outright.\r
2022                 // [1] https://developer.blender.org/rB165caafb99c6846e53d11c4e966990aaffc06cea\r
2023                 if (XScreenCount(Device->XDisplay) > 1)\r
2024                 {\r
2025                         os::Printer::log("Detected classic multi-head setup, not using XIWarpPointer");\r
2026                 }\r
2027                 else\r
2028                 {\r
2029                         XIGetClientPointer(Device->XDisplay, Device->XWindow, &DeviceId);\r
2030                 }\r
2031 #endif\r
2032 \r
2033                 XGCValues values;\r
2034                 unsigned long valuemask = 0;\r
2035 \r
2036                 XColor fg, bg;\r
2037 \r
2038                 // this code, for making the cursor invisible was sent in by\r
2039                 // Sirshane, thank your very much!\r
2040 \r
2041 \r
2042                 Pixmap invisBitmap = XCreatePixmap(Device->XDisplay, Device->XWindow, 32, 32, 1);\r
2043                 Pixmap maskBitmap = XCreatePixmap(Device->XDisplay, Device->XWindow, 32, 32, 1);\r
2044                 Colormap screen_colormap = DefaultColormap( Device->XDisplay, DefaultScreen( Device->XDisplay ) );\r
2045                 XAllocNamedColor( Device->XDisplay, screen_colormap, "black", &fg, &fg );\r
2046                 XAllocNamedColor( Device->XDisplay, screen_colormap, "white", &bg, &bg );\r
2047 \r
2048                 GC gc = XCreateGC( Device->XDisplay, invisBitmap, valuemask, &values );\r
2049 \r
2050                 XSetForeground( Device->XDisplay, gc, BlackPixel( Device->XDisplay, DefaultScreen( Device->XDisplay ) ) );\r
2051                 XFillRectangle( Device->XDisplay, invisBitmap, gc, 0, 0, 32, 32 );\r
2052                 XFillRectangle( Device->XDisplay, maskBitmap, gc, 0, 0, 32, 32 );\r
2053 \r
2054                 InvisCursor = XCreatePixmapCursor( Device->XDisplay, invisBitmap, maskBitmap, &fg, &bg, 1, 1 );\r
2055                 XFreeGC(Device->XDisplay, gc);\r
2056                 XFreePixmap(Device->XDisplay, invisBitmap);\r
2057                 XFreePixmap(Device->XDisplay, maskBitmap);\r
2058 \r
2059                 initCursors();\r
2060         }\r
2061 #endif\r
2062 }\r
2063 \r
2064 CIrrDeviceLinux::CCursorControl::~CCursorControl()\r
2065 {\r
2066         // Do not clearCursors here as the display is already closed\r
2067         // TODO (cutealien): droping cursorcontrol earlier might work, not sure about reason why that's done in stub currently.\r
2068 }\r
2069 \r
2070 #ifdef _IRR_COMPILE_WITH_X11_\r
2071 void CIrrDeviceLinux::CCursorControl::clearCursors()\r
2072 {\r
2073         if (!Null)\r
2074                 XFreeCursor(Device->XDisplay, InvisCursor);\r
2075         for ( u32 i=0; i < Cursors.size(); ++i )\r
2076         {\r
2077                 for ( u32 f=0; f < Cursors[i].Frames.size(); ++f )\r
2078                 {\r
2079                         XFreeCursor(Device->XDisplay, Cursors[i].Frames[f].IconHW);\r
2080                 }\r
2081         }\r
2082 }\r
2083 \r
2084 void CIrrDeviceLinux::CCursorControl::initCursors()\r
2085 {\r
2086         Cursors.push_back( CursorX11(XCreateFontCursor(Device->XDisplay, XC_top_left_arrow)) ); //  (or XC_arrow?)\r
2087         Cursors.push_back( CursorX11(XCreateFontCursor(Device->XDisplay, XC_crosshair)) );\r
2088         Cursors.push_back( CursorX11(XCreateFontCursor(Device->XDisplay, XC_hand2)) ); // (or XC_hand1? )\r
2089         Cursors.push_back( CursorX11(XCreateFontCursor(Device->XDisplay, XC_question_arrow)) );\r
2090         Cursors.push_back( CursorX11(XCreateFontCursor(Device->XDisplay, XC_xterm)) );\r
2091         Cursors.push_back( CursorX11(XCreateFontCursor(Device->XDisplay, XC_X_cursor)) );       //  (or XC_pirate?)\r
2092         Cursors.push_back( CursorX11(XCreateFontCursor(Device->XDisplay, XC_watch)) );  // (or XC_clock?)\r
2093         Cursors.push_back( CursorX11(XCreateFontCursor(Device->XDisplay, XC_fleur)) );\r
2094         Cursors.push_back( CursorX11(XCreateFontCursor(Device->XDisplay, XC_top_right_corner)) );       // NESW not available in X11\r
2095         Cursors.push_back( CursorX11(XCreateFontCursor(Device->XDisplay, XC_top_left_corner)) );        // NWSE not available in X11\r
2096         Cursors.push_back( CursorX11(XCreateFontCursor(Device->XDisplay, XC_sb_v_double_arrow)) );\r
2097         Cursors.push_back( CursorX11(XCreateFontCursor(Device->XDisplay, XC_sb_h_double_arrow)) );\r
2098         Cursors.push_back( CursorX11(XCreateFontCursor(Device->XDisplay, XC_sb_up_arrow)) );    // (or XC_center_ptr?)\r
2099 }\r
2100 \r
2101 void CIrrDeviceLinux::CCursorControl::update()\r
2102 {\r
2103         if ( (u32)ActiveIcon < Cursors.size() && !Cursors[ActiveIcon].Frames.empty() && Cursors[ActiveIcon].FrameTime )\r
2104         {\r
2105                 // update animated cursors. This could also be done by X11 in case someone wants to figure that out (this way was just easier to implement)\r
2106                 u32 now = Device->getTimer()->getRealTime();\r
2107                 u32 frame = ((now - ActiveIconStartTime) / Cursors[ActiveIcon].FrameTime) % Cursors[ActiveIcon].Frames.size();\r
2108                 XDefineCursor(Device->XDisplay, Device->XWindow, Cursors[ActiveIcon].Frames[frame].IconHW);\r
2109         }\r
2110 }\r
2111 #endif\r
2112 \r
2113 //! Sets the active cursor icon\r
2114 void CIrrDeviceLinux::CCursorControl::setActiveIcon(gui::ECURSOR_ICON iconId)\r
2115 {\r
2116 #ifdef _IRR_COMPILE_WITH_X11_\r
2117         if ( iconId >= (s32)Cursors.size() )\r
2118                 return;\r
2119 \r
2120         if ( Cursors[iconId].Frames.size() )\r
2121                 XDefineCursor(Device->XDisplay, Device->XWindow, Cursors[iconId].Frames[0].IconHW);\r
2122 \r
2123         ActiveIconStartTime = Device->getTimer()->getRealTime();\r
2124         ActiveIcon = iconId;\r
2125 #endif\r
2126 }\r
2127 \r
2128 \r
2129 //! Add a custom sprite as cursor icon.\r
2130 gui::ECURSOR_ICON CIrrDeviceLinux::CCursorControl::addIcon(const gui::SCursorSprite& icon)\r
2131 {\r
2132 #ifdef _IRR_COMPILE_WITH_X11_\r
2133         if ( icon.SpriteId >= 0 )\r
2134         {\r
2135                 CursorX11 cX11;\r
2136                 cX11.FrameTime = icon.SpriteBank->getSprites()[icon.SpriteId].frameTime;\r
2137                 for ( u32 i=0; i < icon.SpriteBank->getSprites()[icon.SpriteId].Frames.size(); ++i )\r
2138                 {\r
2139                         irr::u32 texId = icon.SpriteBank->getSprites()[icon.SpriteId].Frames[i].textureNumber;\r
2140                         irr::u32 rectId = icon.SpriteBank->getSprites()[icon.SpriteId].Frames[i].rectNumber;\r
2141                         irr::core::rect<s32> rectIcon = icon.SpriteBank->getPositions()[rectId];\r
2142                         Cursor cursor = Device->TextureToCursor(icon.SpriteBank->getTexture(texId), rectIcon, icon.HotSpot);\r
2143                         cX11.Frames.push_back( CursorFrameX11(cursor) );\r
2144                 }\r
2145 \r
2146                 Cursors.push_back( cX11 );\r
2147 \r
2148                 return (gui::ECURSOR_ICON)(Cursors.size() - 1);\r
2149         }\r
2150 #endif\r
2151         return gui::ECI_NORMAL;\r
2152 }\r
2153 \r
2154 //! replace the given cursor icon.\r
2155 void CIrrDeviceLinux::CCursorControl::changeIcon(gui::ECURSOR_ICON iconId, const gui::SCursorSprite& icon)\r
2156 {\r
2157 #ifdef _IRR_COMPILE_WITH_X11_\r
2158         if ( iconId >= (s32)Cursors.size() )\r
2159                 return;\r
2160 \r
2161         for ( u32 i=0; i < Cursors[iconId].Frames.size(); ++i )\r
2162                 XFreeCursor(Device->XDisplay, Cursors[iconId].Frames[i].IconHW);\r
2163 \r
2164         if ( icon.SpriteId >= 0 )\r
2165         {\r
2166                 CursorX11 cX11;\r
2167                 cX11.FrameTime = icon.SpriteBank->getSprites()[icon.SpriteId].frameTime;\r
2168                 for ( u32 i=0; i < icon.SpriteBank->getSprites()[icon.SpriteId].Frames.size(); ++i )\r
2169                 {\r
2170                         irr::u32 texId = icon.SpriteBank->getSprites()[icon.SpriteId].Frames[i].textureNumber;\r
2171                         irr::u32 rectId = icon.SpriteBank->getSprites()[icon.SpriteId].Frames[i].rectNumber;\r
2172                         irr::core::rect<s32> rectIcon = icon.SpriteBank->getPositions()[rectId];\r
2173                         Cursor cursor = Device->TextureToCursor(icon.SpriteBank->getTexture(texId), rectIcon, icon.HotSpot);\r
2174                         cX11.Frames.push_back( CursorFrameX11(cursor) );\r
2175                 }\r
2176 \r
2177                 Cursors[iconId] = cX11;\r
2178         }\r
2179 #endif\r
2180 }\r
2181 \r
2182 irr::core::dimension2di CIrrDeviceLinux::CCursorControl::getSupportedIconSize() const\r
2183 {\r
2184         // this returns the closest match that is smaller or same size, so we just pass a value which should be large enough for cursors\r
2185         unsigned int width=0, height=0;\r
2186 #ifdef _IRR_COMPILE_WITH_X11_\r
2187         XQueryBestCursor(Device->XDisplay, Device->XWindow, 64, 64, &width, &height);\r
2188 #endif\r
2189         return core::dimension2di(width, height);\r
2190 }\r
2191 \r
2192 } // end namespace\r
2193 \r
2194 #endif // _IRR_COMPILE_WITH_X11_DEVICE_\r
2195 \r