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