]> git.lizzy.rs Git - irrlicht.git/blob - source/Irrlicht/CIrrDeviceLinux.cpp
Implement X11 primary selection
[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->selection != XA_PRIMARY) ||\r
1005                                                         req->owner != XWindow) {\r
1006                                                 // we are not the owner, refuse request\r
1007                                                 send_response_refuse();\r
1008                                                 break;\r
1009                                         }\r
1010                                         const core::stringc &text_buffer = req->selection == X_ATOM_CLIPBOARD ?\r
1011                                                         Clipboard : PrimarySelection;\r
1012 \r
1013                                         // for debugging:\r
1014                                         //~ {\r
1015                                                 //~ char *target_name = XGetAtomName(XDisplay, req->target);\r
1016                                                 //~ fprintf(stderr, "CIrrDeviceLinux::run: target: %s (=%ld)\n",\r
1017                                                                 //~ target_name, req->target);\r
1018                                                 //~ XFree(target_name);\r
1019                                         //~ }\r
1020 \r
1021                                         if (req->property == None) {\r
1022                                                 // req is from obsolete client, use target as property name\r
1023                                                 // and X_ATOM_UTF8_STRING as type\r
1024                                                 // Note: this was not tested and might be incorrect\r
1025                                                 os::Printer::log("CIrrDeviceLinux::run: SelectionRequest from obsolete client",\r
1026                                                                 ELL_WARNING);\r
1027                                                 XChangeProperty(XDisplay,\r
1028                                                                 req->requestor,\r
1029                                                                 req->target, X_ATOM_UTF8_STRING,\r
1030                                                                 8, // format = 8-bit\r
1031                                                                 PropModeReplace,\r
1032                                                                 (unsigned char *)text_buffer.c_str(),\r
1033                                                                 text_buffer.size());\r
1034                                                 send_response(req->target);\r
1035                                                 break;\r
1036                                         }\r
1037 \r
1038                                         if (req->target == X_ATOM_TARGETS) {\r
1039                                                 Atom data[] = {\r
1040                                                         X_ATOM_TARGETS,\r
1041                                                         X_ATOM_TEXT,\r
1042                                                         X_ATOM_UTF8_STRING,\r
1043                                                         X_ATOM_UTF8_MIME_TYPE\r
1044                                                 };\r
1045                                                 set_property_and_notify(\r
1046                                                                 XA_ATOM,\r
1047                                                                 32, // Atom is long, we need to set 32 for longs\r
1048                                                                 &data,\r
1049                                                                 sizeof(data) / sizeof(*data)\r
1050                                                         );\r
1051 \r
1052                                         } else if (req->target == X_ATOM_TEXT ||\r
1053                                                         req->target == X_ATOM_UTF8_STRING ||\r
1054                                                         req->target == X_ATOM_UTF8_MIME_TYPE) {\r
1055                                                 set_property_and_notify(\r
1056                                                                 X_ATOM_UTF8_STRING,\r
1057                                                                 8,\r
1058                                                                 text_buffer.c_str(),\r
1059                                                                 text_buffer.size()\r
1060                                                         );\r
1061 \r
1062                                         } else {\r
1063                                                 // refuse the request\r
1064                                                 send_response_refuse();\r
1065                                         }\r
1066                                 }\r
1067                                 break;\r
1068 \r
1069 #if defined(_IRR_LINUX_X11_XINPUT2_)\r
1070                         case GenericEvent:\r
1071                                 {\r
1072                                         XGenericEventCookie *cookie = &event.xcookie;\r
1073                                         if (XGetEventData(XDisplay, cookie) && cookie->extension == XI_EXTENSIONS_OPCODE && XI_EXTENSIONS_OPCODE\r
1074                                         && (cookie->evtype == XI_TouchUpdate || cookie->evtype == XI_TouchBegin || cookie->evtype == XI_TouchEnd))\r
1075                                         {\r
1076                                                 XIDeviceEvent *de = (XIDeviceEvent *) cookie->data;\r
1077 \r
1078                                                 irrevent.EventType = EET_TOUCH_INPUT_EVENT;\r
1079 \r
1080                                                 irrevent.TouchInput.Event = cookie->evtype == XI_TouchUpdate ? ETIE_MOVED : (cookie->evtype == XI_TouchBegin ? ETIE_PRESSED_DOWN : ETIE_LEFT_UP);\r
1081 \r
1082                                                 irrevent.TouchInput.ID = de->detail;\r
1083                                                 irrevent.TouchInput.X = de->event_x;\r
1084                                                 irrevent.TouchInput.Y = de->event_y;\r
1085 \r
1086                                                 if (irrevent.TouchInput.Event == ETIE_PRESSED_DOWN) {\r
1087                                                         currentTouchedCount++;\r
1088                                                 }\r
1089                                                 irrevent.TouchInput.touchedCount = currentTouchedCount;\r
1090                                                 if (currentTouchedCount > 0 && irrevent.TouchInput.Event == ETIE_LEFT_UP) {\r
1091                                                         currentTouchedCount--;\r
1092                                                 }\r
1093 \r
1094                                                 postEventFromUser(irrevent);\r
1095                                         }\r
1096                                 }\r
1097                                 break;\r
1098 #endif\r
1099 \r
1100                         default:\r
1101                                 break;\r
1102                         } // end switch\r
1103 \r
1104                         // Update IME information\r
1105                         if (XInputContext && GUIEnvironment)\r
1106                         {\r
1107                                 gui::IGUIElement *elem = GUIEnvironment->getFocus();\r
1108                                 if (elem && elem->acceptsIME())\r
1109                                 {\r
1110                                         core::rect<s32> r = elem->getAbsolutePosition();\r
1111                                         XPoint p;\r
1112                                         p.x = (short)r.UpperLeftCorner.X;\r
1113                                         p.y = (short)r.LowerRightCorner.Y;\r
1114                                         XSetICFocus(XInputContext);\r
1115                                         XVaNestedList l = XVaCreateNestedList(0, XNSpotLocation, &p, NULL);\r
1116                                         XSetICValues(XInputContext, XNPreeditAttributes, l, NULL);\r
1117                                         XFree(l);\r
1118                                 } else {\r
1119                                         XUnsetICFocus(XInputContext);\r
1120                                 }\r
1121                         }\r
1122 \r
1123                 } // end while\r
1124 \r
1125         }\r
1126 #endif //_IRR_COMPILE_WITH_X11_\r
1127 \r
1128         if (!Close)\r
1129                 pollJoysticks();\r
1130 \r
1131         return !Close;\r
1132 }\r
1133 \r
1134 \r
1135 //! Pause the current process for the minimum time allowed only to allow other processes to execute\r
1136 void CIrrDeviceLinux::yield()\r
1137 {\r
1138         struct timespec ts = {0,1};\r
1139         nanosleep(&ts, NULL);\r
1140 }\r
1141 \r
1142 \r
1143 //! Pause execution and let other processes to run for a specified amount of time.\r
1144 void CIrrDeviceLinux::sleep(u32 timeMs, bool pauseTimer=false)\r
1145 {\r
1146         const bool wasStopped = Timer ? Timer->isStopped() : true;\r
1147 \r
1148         struct timespec ts;\r
1149         ts.tv_sec = (time_t) (timeMs / 1000);\r
1150         ts.tv_nsec = (long) (timeMs % 1000) * 1000000;\r
1151 \r
1152         if (pauseTimer && !wasStopped)\r
1153                 Timer->stop();\r
1154 \r
1155         nanosleep(&ts, NULL);\r
1156 \r
1157         if (pauseTimer && !wasStopped)\r
1158                 Timer->start();\r
1159 }\r
1160 \r
1161 \r
1162 //! sets the caption of the window\r
1163 void CIrrDeviceLinux::setWindowCaption(const wchar_t* text)\r
1164 {\r
1165 #ifdef _IRR_COMPILE_WITH_X11_\r
1166         if (CreationParams.DriverType == video::EDT_NULL)\r
1167                 return;\r
1168 \r
1169         XTextProperty txt;\r
1170         if (Success==XwcTextListToTextProperty(XDisplay, const_cast<wchar_t**>(&text),\r
1171                                 1, XStdICCTextStyle, &txt))\r
1172         {\r
1173                 XSetWMName(XDisplay, XWindow, &txt);\r
1174                 XSetWMIconName(XDisplay, XWindow, &txt);\r
1175                 XFree(txt.value);\r
1176         }\r
1177 #endif\r
1178 }\r
1179 \r
1180 \r
1181 //! notifies the device that it should close itself\r
1182 void CIrrDeviceLinux::closeDevice()\r
1183 {\r
1184         Close = true;\r
1185 }\r
1186 \r
1187 \r
1188 //! returns if window is active. if not, nothing need to be drawn\r
1189 bool CIrrDeviceLinux::isWindowActive() const\r
1190 {\r
1191         return (WindowHasFocus && !WindowMinimized);\r
1192 }\r
1193 \r
1194 \r
1195 //! returns if window has focus.\r
1196 bool CIrrDeviceLinux::isWindowFocused() const\r
1197 {\r
1198         return WindowHasFocus;\r
1199 }\r
1200 \r
1201 \r
1202 //! returns if window is minimized.\r
1203 bool CIrrDeviceLinux::isWindowMinimized() const\r
1204 {\r
1205         return WindowMinimized;\r
1206 }\r
1207 \r
1208 \r
1209 //! returns last state from maximizeWindow() and restoreWindow()\r
1210 bool CIrrDeviceLinux::isWindowMaximized() const\r
1211 {\r
1212         return WindowMaximized;\r
1213 }\r
1214 \r
1215 \r
1216 //! returns color format of the window.\r
1217 video::ECOLOR_FORMAT CIrrDeviceLinux::getColorFormat() const\r
1218 {\r
1219 #ifdef _IRR_COMPILE_WITH_X11_\r
1220         if (VisualInfo && (VisualInfo->depth != 16))\r
1221                 return video::ECF_R8G8B8;\r
1222         else\r
1223 #endif\r
1224                 return video::ECF_R5G6B5;\r
1225 }\r
1226 \r
1227 \r
1228 //! Sets if the window should be resizable in windowed mode.\r
1229 void CIrrDeviceLinux::setResizable(bool resize)\r
1230 {\r
1231 #ifdef _IRR_COMPILE_WITH_X11_\r
1232         if (CreationParams.DriverType == video::EDT_NULL || CreationParams.Fullscreen )\r
1233                 return;\r
1234 \r
1235         if ( !resize )\r
1236         {\r
1237                 // Must be heap memory because data size depends on X Server\r
1238                 XSizeHints *hints = XAllocSizeHints();\r
1239                 hints->flags=PSize|PMinSize|PMaxSize;\r
1240                 hints->min_width=hints->max_width=hints->base_width=Width;\r
1241                 hints->min_height=hints->max_height=hints->base_height=Height;\r
1242                 XSetWMNormalHints(XDisplay, XWindow, hints);\r
1243                 XFree(hints);\r
1244         }\r
1245         else\r
1246         {\r
1247                 XSetWMNormalHints(XDisplay, XWindow, StdHints);\r
1248         }\r
1249         XFlush(XDisplay);\r
1250 #endif // #ifdef _IRR_COMPILE_WITH_X11_\r
1251 }\r
1252 \r
1253 //! Resize the render window.\r
1254 void CIrrDeviceLinux::setWindowSize(const irr::core::dimension2d<u32>& size)\r
1255 {\r
1256 #ifdef _IRR_COMPILE_WITH_X11_\r
1257         if (CreationParams.DriverType == video::EDT_NULL || CreationParams.Fullscreen )\r
1258                 return;\r
1259 \r
1260         XWindowChanges values;\r
1261         values.width = size.Width;\r
1262         values.height = size.Height;\r
1263         XConfigureWindow(XDisplay, XWindow, CWWidth | CWHeight, &values);\r
1264         XFlush(XDisplay);\r
1265 #endif // #ifdef _IRR_COMPILE_WITH_X11_\r
1266 }\r
1267 \r
1268 \r
1269 //! Minimize window\r
1270 void CIrrDeviceLinux::minimizeWindow()\r
1271 {\r
1272 #ifdef _IRR_COMPILE_WITH_X11_\r
1273         XIconifyWindow(XDisplay, XWindow, Screennr);\r
1274 #endif\r
1275 }\r
1276 \r
1277 \r
1278 //! Maximize window\r
1279 void CIrrDeviceLinux::maximizeWindow()\r
1280 {\r
1281 #ifdef _IRR_COMPILE_WITH_X11_\r
1282         // Maximize is not implemented in bare X, it's a WM construct.\r
1283         if (HasNetWM)\r
1284         {\r
1285                 XEvent ev = {0};\r
1286 \r
1287                 ev.type = ClientMessage;\r
1288                 ev.xclient.window = XWindow;\r
1289                 ev.xclient.message_type = X_ATOM_NETWM_STATE;\r
1290                 ev.xclient.format = 32;\r
1291                 ev.xclient.data.l[0] = 1; // _NET_WM_STATE_ADD\r
1292                 ev.xclient.data.l[1] = X_ATOM_NETWM_MAXIMIZE_VERT;\r
1293                 ev.xclient.data.l[2] = X_ATOM_NETWM_MAXIMIZE_HORZ;\r
1294 \r
1295                 XSendEvent(XDisplay, DefaultRootWindow(XDisplay), false,\r
1296                                 SubstructureNotifyMask|SubstructureRedirectMask, &ev);\r
1297         }\r
1298 \r
1299         XMapWindow(XDisplay, XWindow);\r
1300 \r
1301         WindowMaximized = true;\r
1302 #endif\r
1303 }\r
1304 \r
1305 \r
1306 //! Restore original window size\r
1307 void CIrrDeviceLinux::restoreWindow()\r
1308 {\r
1309 #ifdef _IRR_COMPILE_WITH_X11_\r
1310         // Maximize is not implemented in bare X, it's a WM construct.\r
1311         if (HasNetWM)\r
1312         {\r
1313                 XEvent ev = {0};\r
1314 \r
1315                 ev.type = ClientMessage;\r
1316                 ev.xclient.window = XWindow;\r
1317                 ev.xclient.message_type = X_ATOM_NETWM_STATE;\r
1318                 ev.xclient.format = 32;\r
1319                 ev.xclient.data.l[0] = 0; // _NET_WM_STATE_REMOVE\r
1320                 ev.xclient.data.l[1] = X_ATOM_NETWM_MAXIMIZE_VERT;\r
1321                 ev.xclient.data.l[2] = X_ATOM_NETWM_MAXIMIZE_HORZ;\r
1322 \r
1323                 XSendEvent(XDisplay, DefaultRootWindow(XDisplay), false,\r
1324                                 SubstructureNotifyMask|SubstructureRedirectMask, &ev);\r
1325         }\r
1326 \r
1327         XMapWindow(XDisplay, XWindow);\r
1328 \r
1329         WindowMaximized = false;\r
1330 #endif\r
1331 }\r
1332 \r
1333 core::position2di CIrrDeviceLinux::getWindowPosition()\r
1334 {\r
1335         int wx = 0, wy = 0;\r
1336 #ifdef _IRR_COMPILE_WITH_X11_\r
1337         Window child;\r
1338         XTranslateCoordinates(XDisplay, XWindow, DefaultRootWindow(XDisplay), 0, 0, &wx, &wy, &child);\r
1339 #endif\r
1340         return core::position2di(wx, wy);\r
1341 }\r
1342 \r
1343 void CIrrDeviceLinux::createKeyMap()\r
1344 {\r
1345         // I don't know if this is the best method  to create\r
1346         // the lookuptable, but I'll leave it like that until\r
1347         // I find a better version.\r
1348         // Search for missing numbers in keysymdef.h\r
1349 \r
1350 #ifdef _IRR_COMPILE_WITH_X11_\r
1351         KeyMap.reallocate(190);\r
1352         KeyMap.push_back(SKeyMap(XK_BackSpace, KEY_BACK));\r
1353         KeyMap.push_back(SKeyMap(XK_Tab, KEY_TAB));\r
1354         KeyMap.push_back(SKeyMap(XK_ISO_Left_Tab, KEY_TAB));\r
1355         KeyMap.push_back(SKeyMap(XK_Linefeed, 0)); // ???\r
1356         KeyMap.push_back(SKeyMap(XK_Clear, KEY_CLEAR));\r
1357         KeyMap.push_back(SKeyMap(XK_Return, KEY_RETURN));\r
1358         KeyMap.push_back(SKeyMap(XK_Pause, KEY_PAUSE));\r
1359         KeyMap.push_back(SKeyMap(XK_Scroll_Lock, KEY_SCROLL));\r
1360         KeyMap.push_back(SKeyMap(XK_Sys_Req, 0)); // ???\r
1361         KeyMap.push_back(SKeyMap(XK_Escape, KEY_ESCAPE));\r
1362         KeyMap.push_back(SKeyMap(XK_Insert, KEY_INSERT));\r
1363         KeyMap.push_back(SKeyMap(XK_Delete, KEY_DELETE));\r
1364         KeyMap.push_back(SKeyMap(XK_Home, KEY_HOME));\r
1365         KeyMap.push_back(SKeyMap(XK_Left, KEY_LEFT));\r
1366         KeyMap.push_back(SKeyMap(XK_Up, KEY_UP));\r
1367         KeyMap.push_back(SKeyMap(XK_Right, KEY_RIGHT));\r
1368         KeyMap.push_back(SKeyMap(XK_Down, KEY_DOWN));\r
1369         KeyMap.push_back(SKeyMap(XK_Prior, KEY_PRIOR));\r
1370         KeyMap.push_back(SKeyMap(XK_Page_Up, KEY_PRIOR));\r
1371         KeyMap.push_back(SKeyMap(XK_Next, KEY_NEXT));\r
1372         KeyMap.push_back(SKeyMap(XK_Page_Down, KEY_NEXT));\r
1373         KeyMap.push_back(SKeyMap(XK_End, KEY_END));\r
1374         KeyMap.push_back(SKeyMap(XK_Begin, KEY_HOME));\r
1375         KeyMap.push_back(SKeyMap(XK_Num_Lock, KEY_NUMLOCK));\r
1376         KeyMap.push_back(SKeyMap(XK_KP_Space, KEY_SPACE));\r
1377         KeyMap.push_back(SKeyMap(XK_KP_Tab, KEY_TAB));\r
1378         KeyMap.push_back(SKeyMap(XK_KP_Enter, KEY_RETURN));\r
1379         KeyMap.push_back(SKeyMap(XK_KP_F1, KEY_F1));\r
1380         KeyMap.push_back(SKeyMap(XK_KP_F2, KEY_F2));\r
1381         KeyMap.push_back(SKeyMap(XK_KP_F3, KEY_F3));\r
1382         KeyMap.push_back(SKeyMap(XK_KP_F4, KEY_F4));\r
1383         KeyMap.push_back(SKeyMap(XK_KP_Home, KEY_HOME));\r
1384         KeyMap.push_back(SKeyMap(XK_KP_Left, KEY_LEFT));\r
1385         KeyMap.push_back(SKeyMap(XK_KP_Up, KEY_UP));\r
1386         KeyMap.push_back(SKeyMap(XK_KP_Right, KEY_RIGHT));\r
1387         KeyMap.push_back(SKeyMap(XK_KP_Down, KEY_DOWN));\r
1388         KeyMap.push_back(SKeyMap(XK_Print, KEY_PRINT));\r
1389         KeyMap.push_back(SKeyMap(XK_KP_Prior, KEY_PRIOR));\r
1390         KeyMap.push_back(SKeyMap(XK_KP_Page_Up, KEY_PRIOR));\r
1391         KeyMap.push_back(SKeyMap(XK_KP_Next, KEY_NEXT));\r
1392         KeyMap.push_back(SKeyMap(XK_KP_Page_Down, KEY_NEXT));\r
1393         KeyMap.push_back(SKeyMap(XK_KP_End, KEY_END));\r
1394         KeyMap.push_back(SKeyMap(XK_KP_Begin, KEY_HOME));\r
1395         KeyMap.push_back(SKeyMap(XK_KP_Insert, KEY_INSERT));\r
1396         KeyMap.push_back(SKeyMap(XK_KP_Delete, KEY_DELETE));\r
1397         KeyMap.push_back(SKeyMap(XK_KP_Equal, 0)); // ???\r
1398         KeyMap.push_back(SKeyMap(XK_KP_Multiply, KEY_MULTIPLY));\r
1399         KeyMap.push_back(SKeyMap(XK_KP_Add, KEY_ADD));\r
1400         KeyMap.push_back(SKeyMap(XK_KP_Separator, KEY_SEPARATOR));\r
1401         KeyMap.push_back(SKeyMap(XK_KP_Subtract, KEY_SUBTRACT));\r
1402         KeyMap.push_back(SKeyMap(XK_KP_Decimal, KEY_DECIMAL));\r
1403         KeyMap.push_back(SKeyMap(XK_KP_Divide, KEY_DIVIDE));\r
1404         KeyMap.push_back(SKeyMap(XK_KP_0, KEY_NUMPAD0));\r
1405         KeyMap.push_back(SKeyMap(XK_KP_1, KEY_NUMPAD1));\r
1406         KeyMap.push_back(SKeyMap(XK_KP_2, KEY_NUMPAD2));\r
1407         KeyMap.push_back(SKeyMap(XK_KP_3, KEY_NUMPAD3));\r
1408         KeyMap.push_back(SKeyMap(XK_KP_4, KEY_NUMPAD4));\r
1409         KeyMap.push_back(SKeyMap(XK_KP_5, KEY_NUMPAD5));\r
1410         KeyMap.push_back(SKeyMap(XK_KP_6, KEY_NUMPAD6));\r
1411         KeyMap.push_back(SKeyMap(XK_KP_7, KEY_NUMPAD7));\r
1412         KeyMap.push_back(SKeyMap(XK_KP_8, KEY_NUMPAD8));\r
1413         KeyMap.push_back(SKeyMap(XK_KP_9, KEY_NUMPAD9));\r
1414         KeyMap.push_back(SKeyMap(XK_F1, KEY_F1));\r
1415         KeyMap.push_back(SKeyMap(XK_F2, KEY_F2));\r
1416         KeyMap.push_back(SKeyMap(XK_F3, KEY_F3));\r
1417         KeyMap.push_back(SKeyMap(XK_F4, KEY_F4));\r
1418         KeyMap.push_back(SKeyMap(XK_F5, KEY_F5));\r
1419         KeyMap.push_back(SKeyMap(XK_F6, KEY_F6));\r
1420         KeyMap.push_back(SKeyMap(XK_F7, KEY_F7));\r
1421         KeyMap.push_back(SKeyMap(XK_F8, KEY_F8));\r
1422         KeyMap.push_back(SKeyMap(XK_F9, KEY_F9));\r
1423         KeyMap.push_back(SKeyMap(XK_F10, KEY_F10));\r
1424         KeyMap.push_back(SKeyMap(XK_F11, KEY_F11));\r
1425         KeyMap.push_back(SKeyMap(XK_F12, KEY_F12));\r
1426         KeyMap.push_back(SKeyMap(XK_Shift_L, KEY_LSHIFT));\r
1427         KeyMap.push_back(SKeyMap(XK_Shift_R, KEY_RSHIFT));\r
1428         KeyMap.push_back(SKeyMap(XK_Control_L, KEY_LCONTROL));\r
1429         KeyMap.push_back(SKeyMap(XK_Control_R, KEY_RCONTROL));\r
1430         KeyMap.push_back(SKeyMap(XK_Caps_Lock, KEY_CAPITAL));\r
1431         KeyMap.push_back(SKeyMap(XK_Shift_Lock, KEY_CAPITAL));\r
1432         KeyMap.push_back(SKeyMap(XK_Meta_L, KEY_LWIN));\r
1433         KeyMap.push_back(SKeyMap(XK_Meta_R, KEY_RWIN));\r
1434         KeyMap.push_back(SKeyMap(XK_Alt_L, KEY_LMENU));\r
1435         KeyMap.push_back(SKeyMap(XK_Alt_R, KEY_RMENU));\r
1436         KeyMap.push_back(SKeyMap(XK_ISO_Level3_Shift, KEY_RMENU));\r
1437         KeyMap.push_back(SKeyMap(XK_Menu, KEY_MENU));\r
1438         KeyMap.push_back(SKeyMap(XK_space, KEY_SPACE));\r
1439         KeyMap.push_back(SKeyMap(XK_exclam, 0)); //?\r
1440         KeyMap.push_back(SKeyMap(XK_quotedbl, 0)); //?\r
1441         KeyMap.push_back(SKeyMap(XK_section, 0)); //?\r
1442         KeyMap.push_back(SKeyMap(XK_numbersign, KEY_OEM_2));\r
1443         KeyMap.push_back(SKeyMap(XK_dollar, 0)); //?\r
1444         KeyMap.push_back(SKeyMap(XK_percent, 0)); //?\r
1445         KeyMap.push_back(SKeyMap(XK_ampersand, 0)); //?\r
1446         KeyMap.push_back(SKeyMap(XK_apostrophe, KEY_OEM_7));\r
1447         KeyMap.push_back(SKeyMap(XK_parenleft, 0)); //?\r
1448         KeyMap.push_back(SKeyMap(XK_parenright, 0)); //?\r
1449         KeyMap.push_back(SKeyMap(XK_asterisk, 0)); //?\r
1450         KeyMap.push_back(SKeyMap(XK_plus, KEY_PLUS)); //?\r
1451         KeyMap.push_back(SKeyMap(XK_comma, KEY_COMMA)); //?\r
1452         KeyMap.push_back(SKeyMap(XK_minus, KEY_MINUS)); //?\r
1453         KeyMap.push_back(SKeyMap(XK_period, KEY_PERIOD)); //?\r
1454         KeyMap.push_back(SKeyMap(XK_slash, KEY_OEM_2)); //?\r
1455         KeyMap.push_back(SKeyMap(XK_0, KEY_KEY_0));\r
1456         KeyMap.push_back(SKeyMap(XK_1, KEY_KEY_1));\r
1457         KeyMap.push_back(SKeyMap(XK_2, KEY_KEY_2));\r
1458         KeyMap.push_back(SKeyMap(XK_3, KEY_KEY_3));\r
1459         KeyMap.push_back(SKeyMap(XK_4, KEY_KEY_4));\r
1460         KeyMap.push_back(SKeyMap(XK_5, KEY_KEY_5));\r
1461         KeyMap.push_back(SKeyMap(XK_6, KEY_KEY_6));\r
1462         KeyMap.push_back(SKeyMap(XK_7, KEY_KEY_7));\r
1463         KeyMap.push_back(SKeyMap(XK_8, KEY_KEY_8));\r
1464         KeyMap.push_back(SKeyMap(XK_9, KEY_KEY_9));\r
1465         KeyMap.push_back(SKeyMap(XK_colon, 0)); //?\r
1466         KeyMap.push_back(SKeyMap(XK_semicolon, KEY_OEM_1));\r
1467         KeyMap.push_back(SKeyMap(XK_less, KEY_OEM_102));\r
1468         KeyMap.push_back(SKeyMap(XK_equal, KEY_PLUS));\r
1469         KeyMap.push_back(SKeyMap(XK_greater, 0)); //?\r
1470         KeyMap.push_back(SKeyMap(XK_question, 0)); //?\r
1471         KeyMap.push_back(SKeyMap(XK_at, KEY_KEY_2)); //?\r
1472         KeyMap.push_back(SKeyMap(XK_mu, 0)); //?\r
1473         KeyMap.push_back(SKeyMap(XK_EuroSign, 0)); //?\r
1474         KeyMap.push_back(SKeyMap(XK_A, KEY_KEY_A));\r
1475         KeyMap.push_back(SKeyMap(XK_B, KEY_KEY_B));\r
1476         KeyMap.push_back(SKeyMap(XK_C, KEY_KEY_C));\r
1477         KeyMap.push_back(SKeyMap(XK_D, KEY_KEY_D));\r
1478         KeyMap.push_back(SKeyMap(XK_E, KEY_KEY_E));\r
1479         KeyMap.push_back(SKeyMap(XK_F, KEY_KEY_F));\r
1480         KeyMap.push_back(SKeyMap(XK_G, KEY_KEY_G));\r
1481         KeyMap.push_back(SKeyMap(XK_H, KEY_KEY_H));\r
1482         KeyMap.push_back(SKeyMap(XK_I, KEY_KEY_I));\r
1483         KeyMap.push_back(SKeyMap(XK_J, KEY_KEY_J));\r
1484         KeyMap.push_back(SKeyMap(XK_K, KEY_KEY_K));\r
1485         KeyMap.push_back(SKeyMap(XK_L, KEY_KEY_L));\r
1486         KeyMap.push_back(SKeyMap(XK_M, KEY_KEY_M));\r
1487         KeyMap.push_back(SKeyMap(XK_N, KEY_KEY_N));\r
1488         KeyMap.push_back(SKeyMap(XK_O, KEY_KEY_O));\r
1489         KeyMap.push_back(SKeyMap(XK_P, KEY_KEY_P));\r
1490         KeyMap.push_back(SKeyMap(XK_Q, KEY_KEY_Q));\r
1491         KeyMap.push_back(SKeyMap(XK_R, KEY_KEY_R));\r
1492         KeyMap.push_back(SKeyMap(XK_S, KEY_KEY_S));\r
1493         KeyMap.push_back(SKeyMap(XK_T, KEY_KEY_T));\r
1494         KeyMap.push_back(SKeyMap(XK_U, KEY_KEY_U));\r
1495         KeyMap.push_back(SKeyMap(XK_V, KEY_KEY_V));\r
1496         KeyMap.push_back(SKeyMap(XK_W, KEY_KEY_W));\r
1497         KeyMap.push_back(SKeyMap(XK_X, KEY_KEY_X));\r
1498         KeyMap.push_back(SKeyMap(XK_Y, KEY_KEY_Y));\r
1499         KeyMap.push_back(SKeyMap(XK_Z, KEY_KEY_Z));\r
1500         KeyMap.push_back(SKeyMap(XK_bracketleft, KEY_OEM_4));\r
1501         KeyMap.push_back(SKeyMap(XK_backslash, KEY_OEM_5));\r
1502         KeyMap.push_back(SKeyMap(XK_bracketright, KEY_OEM_6));\r
1503         KeyMap.push_back(SKeyMap(XK_asciicircum, KEY_OEM_5));\r
1504         KeyMap.push_back(SKeyMap(XK_dead_circumflex, KEY_OEM_5));\r
1505         KeyMap.push_back(SKeyMap(XK_degree, 0)); //?\r
1506         KeyMap.push_back(SKeyMap(XK_underscore, KEY_MINUS)); //?\r
1507         KeyMap.push_back(SKeyMap(XK_grave, KEY_OEM_3));\r
1508         KeyMap.push_back(SKeyMap(XK_dead_grave, KEY_OEM_3));\r
1509         KeyMap.push_back(SKeyMap(XK_acute, KEY_OEM_6));\r
1510         KeyMap.push_back(SKeyMap(XK_dead_acute, KEY_OEM_6));\r
1511         KeyMap.push_back(SKeyMap(XK_a, KEY_KEY_A));\r
1512         KeyMap.push_back(SKeyMap(XK_b, KEY_KEY_B));\r
1513         KeyMap.push_back(SKeyMap(XK_c, KEY_KEY_C));\r
1514         KeyMap.push_back(SKeyMap(XK_d, KEY_KEY_D));\r
1515         KeyMap.push_back(SKeyMap(XK_e, KEY_KEY_E));\r
1516         KeyMap.push_back(SKeyMap(XK_f, KEY_KEY_F));\r
1517         KeyMap.push_back(SKeyMap(XK_g, KEY_KEY_G));\r
1518         KeyMap.push_back(SKeyMap(XK_h, KEY_KEY_H));\r
1519         KeyMap.push_back(SKeyMap(XK_i, KEY_KEY_I));\r
1520         KeyMap.push_back(SKeyMap(XK_j, KEY_KEY_J));\r
1521         KeyMap.push_back(SKeyMap(XK_k, KEY_KEY_K));\r
1522         KeyMap.push_back(SKeyMap(XK_l, KEY_KEY_L));\r
1523         KeyMap.push_back(SKeyMap(XK_m, KEY_KEY_M));\r
1524         KeyMap.push_back(SKeyMap(XK_n, KEY_KEY_N));\r
1525         KeyMap.push_back(SKeyMap(XK_o, KEY_KEY_O));\r
1526         KeyMap.push_back(SKeyMap(XK_p, KEY_KEY_P));\r
1527         KeyMap.push_back(SKeyMap(XK_q, KEY_KEY_Q));\r
1528         KeyMap.push_back(SKeyMap(XK_r, KEY_KEY_R));\r
1529         KeyMap.push_back(SKeyMap(XK_s, KEY_KEY_S));\r
1530         KeyMap.push_back(SKeyMap(XK_t, KEY_KEY_T));\r
1531         KeyMap.push_back(SKeyMap(XK_u, KEY_KEY_U));\r
1532         KeyMap.push_back(SKeyMap(XK_v, KEY_KEY_V));\r
1533         KeyMap.push_back(SKeyMap(XK_w, KEY_KEY_W));\r
1534         KeyMap.push_back(SKeyMap(XK_x, KEY_KEY_X));\r
1535         KeyMap.push_back(SKeyMap(XK_y, KEY_KEY_Y));\r
1536         KeyMap.push_back(SKeyMap(XK_z, KEY_KEY_Z));\r
1537         KeyMap.push_back(SKeyMap(XK_ssharp, KEY_OEM_4));\r
1538         KeyMap.push_back(SKeyMap(XK_adiaeresis, KEY_OEM_7));\r
1539         KeyMap.push_back(SKeyMap(XK_odiaeresis, KEY_OEM_3));\r
1540         KeyMap.push_back(SKeyMap(XK_udiaeresis, KEY_OEM_1));\r
1541         KeyMap.push_back(SKeyMap(XK_Super_L, KEY_LWIN));\r
1542         KeyMap.push_back(SKeyMap(XK_Super_R, KEY_RWIN));\r
1543 \r
1544         KeyMap.sort();\r
1545 #endif\r
1546 }\r
1547 \r
1548 bool CIrrDeviceLinux::activateJoysticks(core::array<SJoystickInfo> & joystickInfo)\r
1549 {\r
1550 #if defined (_IRR_COMPILE_WITH_JOYSTICK_EVENTS_)\r
1551 \r
1552         joystickInfo.clear();\r
1553 \r
1554         u32 joystick;\r
1555         for (joystick = 0; joystick < 32; ++joystick)\r
1556         {\r
1557                 // The joystick device could be here...\r
1558                 core::stringc devName = "/dev/js";\r
1559                 devName += joystick;\r
1560 \r
1561                 SJoystickInfo returnInfo;\r
1562                 JoystickInfo info;\r
1563 \r
1564                 info.fd = open(devName.c_str(), O_RDONLY);\r
1565                 if (-1 == info.fd)\r
1566                 {\r
1567                         // ...but Ubuntu and possibly other distros\r
1568                         // create the devices in /dev/input\r
1569                         devName = "/dev/input/js";\r
1570                         devName += joystick;\r
1571                         info.fd = open(devName.c_str(), O_RDONLY);\r
1572                         if (-1 == info.fd)\r
1573                         {\r
1574                                 // and BSD here\r
1575                                 devName = "/dev/joy";\r
1576                                 devName += joystick;\r
1577                                 info.fd = open(devName.c_str(), O_RDONLY);\r
1578                         }\r
1579                 }\r
1580 \r
1581                 if (-1 == info.fd)\r
1582                         continue;\r
1583 \r
1584 #ifdef __FreeBSD__\r
1585                 info.axes=2;\r
1586                 info.buttons=2;\r
1587 #else\r
1588                 ioctl( info.fd, JSIOCGAXES, &(info.axes) );\r
1589                 ioctl( info.fd, JSIOCGBUTTONS, &(info.buttons) );\r
1590                 fcntl( info.fd, F_SETFL, O_NONBLOCK );\r
1591 #endif\r
1592 \r
1593                 (void)memset(&info.persistentData, 0, sizeof(info.persistentData));\r
1594                 info.persistentData.EventType = irr::EET_JOYSTICK_INPUT_EVENT;\r
1595                 info.persistentData.JoystickEvent.Joystick = ActiveJoysticks.size();\r
1596 \r
1597                 // There's no obvious way to determine which (if any) axes represent a POV\r
1598                 // hat, so we'll just set it to "not used" and forget about it.\r
1599                 info.persistentData.JoystickEvent.POV = 65535;\r
1600 \r
1601                 ActiveJoysticks.push_back(info);\r
1602 \r
1603                 returnInfo.Joystick = joystick;\r
1604                 returnInfo.PovHat = SJoystickInfo::POV_HAT_UNKNOWN;\r
1605                 returnInfo.Axes = info.axes;\r
1606                 returnInfo.Buttons = info.buttons;\r
1607 \r
1608 #ifndef __FreeBSD__\r
1609                 char name[80];\r
1610                 ioctl( info.fd, JSIOCGNAME(80), name);\r
1611                 returnInfo.Name = name;\r
1612 #endif\r
1613 \r
1614                 joystickInfo.push_back(returnInfo);\r
1615         }\r
1616 \r
1617         for (joystick = 0; joystick < joystickInfo.size(); ++joystick)\r
1618         {\r
1619                 char logString[256];\r
1620                 snprintf_irr(logString, sizeof(logString), "Found joystick %d, %d axes, %d buttons '%s'",\r
1621                         joystick, joystickInfo[joystick].Axes,\r
1622                         joystickInfo[joystick].Buttons, joystickInfo[joystick].Name.c_str());\r
1623                 os::Printer::log(logString, ELL_INFORMATION);\r
1624         }\r
1625 \r
1626         return true;\r
1627 #else\r
1628         return false;\r
1629 #endif // _IRR_COMPILE_WITH_JOYSTICK_EVENTS_\r
1630 }\r
1631 \r
1632 \r
1633 void CIrrDeviceLinux::pollJoysticks()\r
1634 {\r
1635 #if defined (_IRR_COMPILE_WITH_JOYSTICK_EVENTS_)\r
1636         if (0 == ActiveJoysticks.size())\r
1637                 return;\r
1638 \r
1639         for (u32 j= 0; j< ActiveJoysticks.size(); ++j)\r
1640         {\r
1641                 JoystickInfo & info =  ActiveJoysticks[j];\r
1642 \r
1643 #ifdef __FreeBSD__\r
1644                 struct joystick js;\r
1645                 if (read(info.fd, &js, sizeof(js)) == sizeof(js))\r
1646                 {\r
1647                         info.persistentData.JoystickEvent.ButtonStates = js.b1 | (js.b2 << 1); /* should be a two-bit field */\r
1648                         info.persistentData.JoystickEvent.Axis[0] = js.x; /* X axis */\r
1649                         info.persistentData.JoystickEvent.Axis[1] = js.y; /* Y axis */\r
1650                 }\r
1651 #else\r
1652                 struct js_event event;\r
1653                 while (sizeof(event) == read(info.fd, &event, sizeof(event)))\r
1654                 {\r
1655                         switch(event.type & ~JS_EVENT_INIT)\r
1656                         {\r
1657                         case JS_EVENT_BUTTON:\r
1658                                 if (event.value)\r
1659                                                 info.persistentData.JoystickEvent.ButtonStates |= (1 << event.number);\r
1660                                 else\r
1661                                                 info.persistentData.JoystickEvent.ButtonStates &= ~(1 << event.number);\r
1662                                 break;\r
1663 \r
1664                         case JS_EVENT_AXIS:\r
1665                                 if (event.number < SEvent::SJoystickEvent::NUMBER_OF_AXES)\r
1666                                         info.persistentData.JoystickEvent.Axis[event.number] = event.value;\r
1667                                 break;\r
1668 \r
1669                         default:\r
1670                                 break;\r
1671                         }\r
1672                 }\r
1673 #endif\r
1674 \r
1675                 // Send an irrlicht joystick event once per ::run() even if no new data were received.\r
1676                 (void)postEventFromUser(info.persistentData);\r
1677         }\r
1678 #endif // _IRR_COMPILE_WITH_JOYSTICK_EVENTS_\r
1679 }\r
1680 \r
1681 \r
1682 #if defined(_IRR_COMPILE_WITH_X11_)\r
1683 //! gets text from the clipboard\r
1684 //! \return Returns 0 if no string is in there, otherwise utf-8 text.\r
1685 const c8 *CIrrDeviceLinux::getTextFromSelection(Atom selection, core::stringc &text_buffer) const\r
1686 {\r
1687         Window ownerWindow = XGetSelectionOwner(XDisplay, selection);\r
1688         if (ownerWindow == XWindow) {\r
1689                 return text_buffer.c_str();\r
1690         }\r
1691 \r
1692         text_buffer = "";\r
1693 \r
1694         if (ownerWindow == None) {\r
1695                 return text_buffer.c_str();\r
1696         }\r
1697 \r
1698         // delete the property to be set beforehand\r
1699         XDeleteProperty(XDisplay, XWindow, XA_PRIMARY);\r
1700 \r
1701         XConvertSelection(XDisplay, selection, X_ATOM_UTF8_STRING, XA_PRIMARY,\r
1702                         XWindow, CurrentTime);\r
1703         XFlush(XDisplay);\r
1704 \r
1705         // wait for event via a blocking call\r
1706         XEvent event_ret;\r
1707         std::pair<Window, Atom> args(XWindow, selection);\r
1708         XIfEvent(XDisplay, &event_ret, [](Display *_display, XEvent *event, XPointer arg) {\r
1709                 auto p = reinterpret_cast<std::pair<Window, Atom> *>(arg);\r
1710                 return (Bool) (event->type == SelectionNotify &&\r
1711                                 event->xselection.requestor == p->first &&\r
1712                                 event->xselection.selection == p->second &&\r
1713                                 event->xselection.target == X_ATOM_UTF8_STRING);\r
1714         }, (XPointer)&args);\r
1715 \r
1716         _IRR_DEBUG_BREAK_IF(!(event_ret.type == SelectionNotify &&\r
1717                         event_ret.xselection.requestor == XWindow &&\r
1718                         event_ret.xselection.selection == selection &&\r
1719                         event_ret.xselection.target == X_ATOM_UTF8_STRING));\r
1720 \r
1721         Atom property_set = event_ret.xselection.property;\r
1722         if (event_ret.xselection.property == None) {\r
1723                 // request failed => empty string\r
1724                 return text_buffer.c_str();\r
1725         }\r
1726 \r
1727         // check for data\r
1728         Atom type;\r
1729         int format;\r
1730         unsigned long numItems, bytesLeft, dummy;\r
1731         unsigned char *data = nullptr;\r
1732         XGetWindowProperty (XDisplay, XWindow,\r
1733                         property_set, // property name\r
1734                         0, // offset\r
1735                         0, // length (we only check for data, so 0)\r
1736                         0, // Delete 0==false\r
1737                         AnyPropertyType, // AnyPropertyType or property identifier\r
1738                         &type, // return type\r
1739                         &format, // return format\r
1740                         &numItems, // number items\r
1741                         &bytesLeft, // remaining bytes for partial reads\r
1742                         &data); // data\r
1743         if (data) {\r
1744                 XFree(data);\r
1745                 data = nullptr;\r
1746         }\r
1747 \r
1748         // for debugging:\r
1749         //~ {\r
1750                 //~ char *type_name = XGetAtomName(XDisplay, type);\r
1751                 //~ fprintf(stderr, "CIrrDeviceLinux::getTextFromSelection: actual type: %s (=%ld)\n",\r
1752                                 //~ type_name, type);\r
1753                 //~ XFree(type_name);\r
1754         //~ }\r
1755 \r
1756         if (type != X_ATOM_UTF8_STRING && type != X_ATOM_UTF8_MIME_TYPE) {\r
1757                 os::Printer::log("CIrrDeviceLinux::getTextFromSelection: did not get utf-8 string",\r
1758                                 ELL_WARNING);\r
1759                 return text_buffer.c_str();\r
1760         }\r
1761 \r
1762         if (bytesLeft > 0) {\r
1763                 // there is some data to get\r
1764                 int result = XGetWindowProperty (XDisplay, XWindow, property_set, 0,\r
1765                                                                         bytesLeft, 0, AnyPropertyType, &type, &format,\r
1766                                                                         &numItems, &dummy, &data);\r
1767                 if (result == Success)\r
1768                         text_buffer = (irr::c8 *)data;\r
1769                 XFree (data);\r
1770         }\r
1771 \r
1772         // delete the property again, to inform the owner about the successful transfer\r
1773         XDeleteProperty(XDisplay, XWindow, property_set);\r
1774 \r
1775         return text_buffer.c_str();\r
1776 }\r
1777 #endif\r
1778 \r
1779 //! gets text from the clipboard\r
1780 //! \return Returns 0 if no string is in there, otherwise utf-8 text.\r
1781 const c8 *CIrrDeviceLinux::getTextFromClipboard() const\r
1782 {\r
1783 #if defined(_IRR_COMPILE_WITH_X11_)\r
1784         return getTextFromSelection(X_ATOM_CLIPBOARD, Clipboard);\r
1785 #else\r
1786         return nullptr;\r
1787 #endif\r
1788 }\r
1789 \r
1790 //! gets text from the primary selection\r
1791 //! \return Returns 0 if no string is in there, otherwise utf-8 text.\r
1792 const c8 *CIrrDeviceLinux::getTextFromPrimarySelection() const\r
1793 {\r
1794 #if defined(_IRR_COMPILE_WITH_X11_)\r
1795         return getTextFromSelection(XA_PRIMARY, PrimarySelection);\r
1796 #else\r
1797         return nullptr;\r
1798 #endif\r
1799 }\r
1800 \r
1801 #if defined(_IRR_COMPILE_WITH_X11_)\r
1802 bool CIrrDeviceLinux::becomeSelectionOwner(Atom selection) const\r
1803 {\r
1804         XSetSelectionOwner (XDisplay, selection, XWindow, CurrentTime);\r
1805         XFlush (XDisplay);\r
1806         Window owner = XGetSelectionOwner(XDisplay, selection);\r
1807         return owner == XWindow;\r
1808 }\r
1809 #endif\r
1810 \r
1811 //! copies text to the clipboard\r
1812 void CIrrDeviceLinux::copyToClipboard(const c8 *text) const\r
1813 {\r
1814 #if defined(_IRR_COMPILE_WITH_X11_)\r
1815         // Actually there is no clipboard on X but applications just say they own the clipboard and return text when asked.\r
1816         // Which btw. also means that on X you lose clipboard content when closing applications.\r
1817         Clipboard = text;\r
1818         if (!becomeSelectionOwner(X_ATOM_CLIPBOARD)) {\r
1819                 os::Printer::log("CIrrDeviceLinux::copyToClipboard: failed to set owner", ELL_WARNING);\r
1820         }\r
1821 #endif\r
1822 }\r
1823 \r
1824 //! copies text to the primary selection\r
1825 void CIrrDeviceLinux::copyToPrimarySelection(const c8 *text) const\r
1826 {\r
1827 #if defined(_IRR_COMPILE_WITH_X11_)\r
1828         PrimarySelection = text;\r
1829         if (!becomeSelectionOwner(XA_PRIMARY)) {\r
1830                 os::Printer::log("CIrrDeviceLinux::copyToPrimarySelection: failed to set owner", ELL_WARNING);\r
1831         }\r
1832 #endif\r
1833 }\r
1834 \r
1835 #ifdef _IRR_COMPILE_WITH_X11_\r
1836 // return true if the passed event has the type passed in parameter arg\r
1837 Bool PredicateIsEventType(Display *display, XEvent *event, XPointer arg)\r
1838 {\r
1839         if ( event && event->type == *(int*)arg )\r
1840         {\r
1841 //              os::Printer::log("remove event", core::stringc((int)arg).c_str(), ELL_INFORMATION);\r
1842                 return True;\r
1843         }\r
1844         return False;\r
1845 }\r
1846 #endif //_IRR_COMPILE_WITH_X11_\r
1847 \r
1848 //! Remove all messages pending in the system message loop\r
1849 void CIrrDeviceLinux::clearSystemMessages()\r
1850 {\r
1851 #ifdef _IRR_COMPILE_WITH_X11_\r
1852         if (CreationParams.DriverType != video::EDT_NULL)\r
1853         {\r
1854                 XEvent event;\r
1855                 int usrArg = ButtonPress;\r
1856                 while ( XCheckIfEvent(XDisplay, &event, PredicateIsEventType, XPointer(&usrArg)) == True ) {}\r
1857                 usrArg = ButtonRelease;\r
1858                 while ( XCheckIfEvent(XDisplay, &event, PredicateIsEventType, XPointer(&usrArg)) == True ) {}\r
1859                 usrArg = MotionNotify;\r
1860                 while ( XCheckIfEvent(XDisplay, &event, PredicateIsEventType, XPointer(&usrArg)) == True ) {}\r
1861                 usrArg = KeyRelease;\r
1862                 while ( XCheckIfEvent(XDisplay, &event, PredicateIsEventType, XPointer(&usrArg)) == True ) {}\r
1863                 usrArg = KeyPress;\r
1864                 while ( XCheckIfEvent(XDisplay, &event, PredicateIsEventType, XPointer(&usrArg)) == True ) {}\r
1865         }\r
1866 #endif //_IRR_COMPILE_WITH_X11_\r
1867 }\r
1868 \r
1869 void CIrrDeviceLinux::initXAtoms()\r
1870 {\r
1871 #ifdef _IRR_COMPILE_WITH_X11_\r
1872         X_ATOM_CLIPBOARD = XInternAtom(XDisplay, "CLIPBOARD", False);\r
1873         X_ATOM_TARGETS = XInternAtom(XDisplay, "TARGETS", False);\r
1874         X_ATOM_UTF8_STRING = XInternAtom(XDisplay, "UTF8_STRING", False);\r
1875         X_ATOM_UTF8_MIME_TYPE = XInternAtom(XDisplay, "text/plain;charset=utf-8", False);\r
1876         X_ATOM_TEXT = XInternAtom(XDisplay, "TEXT", False);\r
1877         X_ATOM_NETWM_MAXIMIZE_VERT = XInternAtom(XDisplay, "_NET_WM_STATE_MAXIMIZED_VERT", true);\r
1878         X_ATOM_NETWM_MAXIMIZE_HORZ = XInternAtom(XDisplay, "_NET_WM_STATE_MAXIMIZED_HORZ", true);\r
1879         X_ATOM_NETWM_STATE = XInternAtom(XDisplay, "_NET_WM_STATE", true);\r
1880         X_ATOM_NETWM_STATE_FULLSCREEN = XInternAtom(XDisplay, "_NET_WM_STATE_FULLSCREEN", True);\r
1881 #endif\r
1882 }\r
1883 \r
1884 void CIrrDeviceLinux::initXInput2()\r
1885 {\r
1886 #if defined(_IRR_LINUX_X11_XINPUT2_)\r
1887         int ev=0;\r
1888         int err=0;\r
1889         if (!XQueryExtension(XDisplay, "XInputExtension", &XI_EXTENSIONS_OPCODE, &ev, &err))\r
1890         {\r
1891                 os::Printer::log("X Input extension not available.", ELL_WARNING);\r
1892                 return;\r
1893         }\r
1894 \r
1895         int major = 2;\r
1896         int minor = 3;\r
1897         int rc = XIQueryVersion(XDisplay, &major, &minor);\r
1898         if ( rc != Success )\r
1899         {\r
1900                 os::Printer::log("No XI2 support.", ELL_WARNING);\r
1901                 return;\r
1902         }\r
1903 \r
1904         // So far we only use XInput2 for touch events.\r
1905         // So we enable those and disable all other events for now.\r
1906         XIEventMask eventMask;\r
1907         unsigned char mask[XIMaskLen(XI_TouchEnd)];\r
1908         memset(mask, 0, sizeof(mask));\r
1909         eventMask.deviceid = XIAllMasterDevices;\r
1910         eventMask.mask_len = sizeof(mask);\r
1911         eventMask.mask = mask;\r
1912         XISetMask(eventMask.mask, XI_TouchBegin);\r
1913         XISetMask(eventMask.mask, XI_TouchUpdate);\r
1914         XISetMask(eventMask.mask, XI_TouchEnd);\r
1915 \r
1916         XISelectEvents(XDisplay, XWindow, &eventMask, 1);\r
1917 #endif\r
1918 }\r
1919 \r
1920 \r
1921 #ifdef _IRR_COMPILE_WITH_X11_\r
1922 \r
1923 Cursor CIrrDeviceLinux::TextureToMonochromeCursor(irr::video::ITexture * tex, const core::rect<s32>& sourceRect, const core::position2d<s32> &hotspot)\r
1924 {\r
1925         XImage * sourceImage = XCreateImage(XDisplay, VisualInfo->visual,\r
1926                                                                                 1, // depth,\r
1927                                                                                 ZPixmap,        // XYBitmap (depth=1), ZPixmap(depth=x)\r
1928                                                                                 0, 0, sourceRect.getWidth(), sourceRect.getHeight(),\r
1929                                                                                 32, // bitmap_pad,\r
1930                                                                                 0// bytes_per_line (0 means continuos in memory)\r
1931                                                                                 );\r
1932         sourceImage->data = new char[sourceImage->height * sourceImage->bytes_per_line];\r
1933         XImage * maskImage = XCreateImage(XDisplay, VisualInfo->visual,\r
1934                                                                                 1, // depth,\r
1935                                                                                 ZPixmap,\r
1936                                                                                 0, 0, sourceRect.getWidth(), sourceRect.getHeight(),\r
1937                                                                                 32, // bitmap_pad,\r
1938                                                                                 0 // bytes_per_line\r
1939                                                                                 );\r
1940         maskImage->data = new char[maskImage->height * maskImage->bytes_per_line];\r
1941 \r
1942         // write texture into XImage\r
1943         video::ECOLOR_FORMAT format = tex->getColorFormat();\r
1944         u32 bytesPerPixel = video::IImage::getBitsPerPixelFromFormat(format) / 8;\r
1945         u32 bytesLeftGap = sourceRect.UpperLeftCorner.X * bytesPerPixel;\r
1946         u32 bytesRightGap = tex->getPitch() - sourceRect.LowerRightCorner.X * bytesPerPixel;\r
1947         const u8* data = (const u8*)tex->lock(video::ETLM_READ_ONLY, 0);\r
1948         data += sourceRect.UpperLeftCorner.Y*tex->getPitch();\r
1949         for ( s32 y = 0; y < sourceRect.getHeight(); ++y )\r
1950         {\r
1951                 data += bytesLeftGap;\r
1952                 for ( s32 x = 0; x < sourceRect.getWidth(); ++x )\r
1953                 {\r
1954                         video::SColor pixelCol;\r
1955                         pixelCol.setData((const void*)data, format);\r
1956                         data += bytesPerPixel;\r
1957 \r
1958                         if ( pixelCol.getAlpha() == 0 ) // transparent\r
1959                         {\r
1960                                 XPutPixel(maskImage, x, y, 0);\r
1961                                 XPutPixel(sourceImage, x, y, 0);\r
1962                         }\r
1963                         else    // color\r
1964                         {\r
1965                                 if ( pixelCol.getAverage() >= 127 )\r
1966                                         XPutPixel(sourceImage, x, y, 1);\r
1967                                 else\r
1968                                         XPutPixel(sourceImage, x, y, 0);\r
1969                                 XPutPixel(maskImage, x, y, 1);\r
1970                         }\r
1971                 }\r
1972                 data += bytesRightGap;\r
1973         }\r
1974         tex->unlock();\r
1975 \r
1976         Pixmap sourcePixmap = XCreatePixmap(XDisplay, XWindow, sourceImage->width, sourceImage->height, sourceImage->depth);\r
1977         Pixmap maskPixmap = XCreatePixmap(XDisplay, XWindow, maskImage->width, maskImage->height, maskImage->depth);\r
1978 \r
1979         XGCValues values;\r
1980         values.foreground = 1;\r
1981         values.background = 1;\r
1982         GC gc = XCreateGC( XDisplay, sourcePixmap, GCForeground | GCBackground, &values );\r
1983 \r
1984         XPutImage(XDisplay, sourcePixmap, gc, sourceImage, 0, 0, 0, 0, sourceImage->width, sourceImage->height);\r
1985         XPutImage(XDisplay, maskPixmap, gc, maskImage, 0, 0, 0, 0, maskImage->width, maskImage->height);\r
1986 \r
1987         XFreeGC(XDisplay, gc);\r
1988         XDestroyImage(sourceImage);\r
1989         XDestroyImage(maskImage);\r
1990 \r
1991         Cursor cursorResult = 0;\r
1992         XColor foreground, background;\r
1993         foreground.red = 65535;\r
1994         foreground.green = 65535;\r
1995         foreground.blue = 65535;\r
1996         foreground.flags = DoRed | DoGreen | DoBlue;\r
1997         background.red = 0;\r
1998         background.green = 0;\r
1999         background.blue = 0;\r
2000         background.flags = DoRed | DoGreen | DoBlue;\r
2001 \r
2002         cursorResult = XCreatePixmapCursor(XDisplay, sourcePixmap, maskPixmap, &foreground, &background, hotspot.X, hotspot.Y);\r
2003 \r
2004         XFreePixmap(XDisplay, sourcePixmap);\r
2005         XFreePixmap(XDisplay, maskPixmap);\r
2006 \r
2007         return cursorResult;\r
2008 }\r
2009 \r
2010 #ifdef _IRR_LINUX_XCURSOR_\r
2011 Cursor CIrrDeviceLinux::TextureToARGBCursor(irr::video::ITexture * tex, const core::rect<s32>& sourceRect, const core::position2d<s32> &hotspot)\r
2012 {\r
2013         XcursorImage * image = XcursorImageCreate (sourceRect.getWidth(), sourceRect.getHeight());\r
2014         image->xhot = hotspot.X;\r
2015         image->yhot = hotspot.Y;\r
2016 \r
2017         // write texture into XcursorImage\r
2018         video::ECOLOR_FORMAT format = tex->getColorFormat();\r
2019         u32 bytesPerPixel = video::IImage::getBitsPerPixelFromFormat(format) / 8;\r
2020         u32 bytesLeftGap = sourceRect.UpperLeftCorner.X * bytesPerPixel;\r
2021         u32 bytesRightGap = tex->getPitch() - sourceRect.LowerRightCorner.X * bytesPerPixel;\r
2022         XcursorPixel* target = image->pixels;\r
2023         const u8* data = (const u8*)tex->lock(video::ETLM_READ_ONLY, 0);\r
2024         data += sourceRect.UpperLeftCorner.Y*tex->getPitch();\r
2025         for ( s32 y = 0; y < sourceRect.getHeight(); ++y )\r
2026         {\r
2027                 data += bytesLeftGap;\r
2028                 for ( s32 x = 0; x < sourceRect.getWidth(); ++x )\r
2029                 {\r
2030                         video::SColor pixelCol;\r
2031                         pixelCol.setData((const void*)data, format);\r
2032                         data += bytesPerPixel;\r
2033 \r
2034                         *target = (XcursorPixel)pixelCol.color;\r
2035                         ++target;\r
2036                 }\r
2037                 data += bytesRightGap;\r
2038         }\r
2039         tex->unlock();\r
2040 \r
2041         Cursor cursorResult=XcursorImageLoadCursor(XDisplay, image);\r
2042 \r
2043         XcursorImageDestroy(image);\r
2044 \r
2045 \r
2046         return cursorResult;\r
2047 }\r
2048 #endif // #ifdef _IRR_LINUX_XCURSOR_\r
2049 \r
2050 Cursor CIrrDeviceLinux::TextureToCursor(irr::video::ITexture * tex, const core::rect<s32>& sourceRect, const core::position2d<s32> &hotspot)\r
2051 {\r
2052 #ifdef _IRR_LINUX_XCURSOR_\r
2053         return TextureToARGBCursor( tex, sourceRect, hotspot );\r
2054 #else\r
2055         return TextureToMonochromeCursor( tex, sourceRect, hotspot );\r
2056 #endif\r
2057 }\r
2058 #endif  // _IRR_COMPILE_WITH_X11_\r
2059 \r
2060 \r
2061 CIrrDeviceLinux::CCursorControl::CCursorControl(CIrrDeviceLinux* dev, bool null)\r
2062         : Device(dev)\r
2063 #ifdef _IRR_COMPILE_WITH_X11_\r
2064         , PlatformBehavior(gui::ECPB_NONE), LastQuery(0)\r
2065 #ifdef _IRR_LINUX_X11_XINPUT2_\r
2066         , DeviceId(0)\r
2067 #endif\r
2068 #endif\r
2069         , IsVisible(true), Null(null), UseReferenceRect(false)\r
2070         , ActiveIcon(gui::ECI_NORMAL), ActiveIconStartTime(0)\r
2071 {\r
2072 #ifdef _IRR_COMPILE_WITH_X11_\r
2073         if (!Null)\r
2074         {\r
2075 #ifdef _IRR_LINUX_X11_XINPUT2_\r
2076                 // XIWarpPointer is entirely broken on multi-head setups (see also [1]),\r
2077                 // but behaves better in other cases so we can't just disable it outright.\r
2078                 // [1] https://developer.blender.org/rB165caafb99c6846e53d11c4e966990aaffc06cea\r
2079                 if (XScreenCount(Device->XDisplay) > 1)\r
2080                 {\r
2081                         os::Printer::log("Detected classic multi-head setup, not using XIWarpPointer");\r
2082                 }\r
2083                 else\r
2084                 {\r
2085                         XIGetClientPointer(Device->XDisplay, Device->XWindow, &DeviceId);\r
2086                 }\r
2087 #endif\r
2088 \r
2089                 XGCValues values;\r
2090                 unsigned long valuemask = 0;\r
2091 \r
2092                 XColor fg, bg;\r
2093 \r
2094                 // this code, for making the cursor invisible was sent in by\r
2095                 // Sirshane, thank your very much!\r
2096 \r
2097 \r
2098                 Pixmap invisBitmap = XCreatePixmap(Device->XDisplay, Device->XWindow, 32, 32, 1);\r
2099                 Pixmap maskBitmap = XCreatePixmap(Device->XDisplay, Device->XWindow, 32, 32, 1);\r
2100                 Colormap screen_colormap = DefaultColormap( Device->XDisplay, DefaultScreen( Device->XDisplay ) );\r
2101                 XAllocNamedColor( Device->XDisplay, screen_colormap, "black", &fg, &fg );\r
2102                 XAllocNamedColor( Device->XDisplay, screen_colormap, "white", &bg, &bg );\r
2103 \r
2104                 GC gc = XCreateGC( Device->XDisplay, invisBitmap, valuemask, &values );\r
2105 \r
2106                 XSetForeground( Device->XDisplay, gc, BlackPixel( Device->XDisplay, DefaultScreen( Device->XDisplay ) ) );\r
2107                 XFillRectangle( Device->XDisplay, invisBitmap, gc, 0, 0, 32, 32 );\r
2108                 XFillRectangle( Device->XDisplay, maskBitmap, gc, 0, 0, 32, 32 );\r
2109 \r
2110                 InvisCursor = XCreatePixmapCursor( Device->XDisplay, invisBitmap, maskBitmap, &fg, &bg, 1, 1 );\r
2111                 XFreeGC(Device->XDisplay, gc);\r
2112                 XFreePixmap(Device->XDisplay, invisBitmap);\r
2113                 XFreePixmap(Device->XDisplay, maskBitmap);\r
2114 \r
2115                 initCursors();\r
2116         }\r
2117 #endif\r
2118 }\r
2119 \r
2120 CIrrDeviceLinux::CCursorControl::~CCursorControl()\r
2121 {\r
2122         // Do not clearCursors here as the display is already closed\r
2123         // TODO (cutealien): droping cursorcontrol earlier might work, not sure about reason why that's done in stub currently.\r
2124 }\r
2125 \r
2126 #ifdef _IRR_COMPILE_WITH_X11_\r
2127 void CIrrDeviceLinux::CCursorControl::clearCursors()\r
2128 {\r
2129         if (!Null)\r
2130                 XFreeCursor(Device->XDisplay, InvisCursor);\r
2131         for ( u32 i=0; i < Cursors.size(); ++i )\r
2132         {\r
2133                 for ( u32 f=0; f < Cursors[i].Frames.size(); ++f )\r
2134                 {\r
2135                         XFreeCursor(Device->XDisplay, Cursors[i].Frames[f].IconHW);\r
2136                 }\r
2137         }\r
2138 }\r
2139 \r
2140 void CIrrDeviceLinux::CCursorControl::initCursors()\r
2141 {\r
2142         Cursors.push_back( CursorX11(XCreateFontCursor(Device->XDisplay, XC_top_left_arrow)) ); //  (or XC_arrow?)\r
2143         Cursors.push_back( CursorX11(XCreateFontCursor(Device->XDisplay, XC_crosshair)) );\r
2144         Cursors.push_back( CursorX11(XCreateFontCursor(Device->XDisplay, XC_hand2)) ); // (or XC_hand1? )\r
2145         Cursors.push_back( CursorX11(XCreateFontCursor(Device->XDisplay, XC_question_arrow)) );\r
2146         Cursors.push_back( CursorX11(XCreateFontCursor(Device->XDisplay, XC_xterm)) );\r
2147         Cursors.push_back( CursorX11(XCreateFontCursor(Device->XDisplay, XC_X_cursor)) );       //  (or XC_pirate?)\r
2148         Cursors.push_back( CursorX11(XCreateFontCursor(Device->XDisplay, XC_watch)) );  // (or XC_clock?)\r
2149         Cursors.push_back( CursorX11(XCreateFontCursor(Device->XDisplay, XC_fleur)) );\r
2150         Cursors.push_back( CursorX11(XCreateFontCursor(Device->XDisplay, XC_top_right_corner)) );       // NESW not available in X11\r
2151         Cursors.push_back( CursorX11(XCreateFontCursor(Device->XDisplay, XC_top_left_corner)) );        // NWSE not available in X11\r
2152         Cursors.push_back( CursorX11(XCreateFontCursor(Device->XDisplay, XC_sb_v_double_arrow)) );\r
2153         Cursors.push_back( CursorX11(XCreateFontCursor(Device->XDisplay, XC_sb_h_double_arrow)) );\r
2154         Cursors.push_back( CursorX11(XCreateFontCursor(Device->XDisplay, XC_sb_up_arrow)) );    // (or XC_center_ptr?)\r
2155 }\r
2156 \r
2157 void CIrrDeviceLinux::CCursorControl::update()\r
2158 {\r
2159         if ( (u32)ActiveIcon < Cursors.size() && !Cursors[ActiveIcon].Frames.empty() && Cursors[ActiveIcon].FrameTime )\r
2160         {\r
2161                 // 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
2162                 u32 now = Device->getTimer()->getRealTime();\r
2163                 u32 frame = ((now - ActiveIconStartTime) / Cursors[ActiveIcon].FrameTime) % Cursors[ActiveIcon].Frames.size();\r
2164                 XDefineCursor(Device->XDisplay, Device->XWindow, Cursors[ActiveIcon].Frames[frame].IconHW);\r
2165         }\r
2166 }\r
2167 #endif\r
2168 \r
2169 //! Sets the active cursor icon\r
2170 void CIrrDeviceLinux::CCursorControl::setActiveIcon(gui::ECURSOR_ICON iconId)\r
2171 {\r
2172 #ifdef _IRR_COMPILE_WITH_X11_\r
2173         if ( iconId >= (s32)Cursors.size() )\r
2174                 return;\r
2175 \r
2176         if ( Cursors[iconId].Frames.size() )\r
2177                 XDefineCursor(Device->XDisplay, Device->XWindow, Cursors[iconId].Frames[0].IconHW);\r
2178 \r
2179         ActiveIconStartTime = Device->getTimer()->getRealTime();\r
2180         ActiveIcon = iconId;\r
2181 #endif\r
2182 }\r
2183 \r
2184 \r
2185 //! Add a custom sprite as cursor icon.\r
2186 gui::ECURSOR_ICON CIrrDeviceLinux::CCursorControl::addIcon(const gui::SCursorSprite& icon)\r
2187 {\r
2188 #ifdef _IRR_COMPILE_WITH_X11_\r
2189         if ( icon.SpriteId >= 0 )\r
2190         {\r
2191                 CursorX11 cX11;\r
2192                 cX11.FrameTime = icon.SpriteBank->getSprites()[icon.SpriteId].frameTime;\r
2193                 for ( u32 i=0; i < icon.SpriteBank->getSprites()[icon.SpriteId].Frames.size(); ++i )\r
2194                 {\r
2195                         irr::u32 texId = icon.SpriteBank->getSprites()[icon.SpriteId].Frames[i].textureNumber;\r
2196                         irr::u32 rectId = icon.SpriteBank->getSprites()[icon.SpriteId].Frames[i].rectNumber;\r
2197                         irr::core::rect<s32> rectIcon = icon.SpriteBank->getPositions()[rectId];\r
2198                         Cursor cursor = Device->TextureToCursor(icon.SpriteBank->getTexture(texId), rectIcon, icon.HotSpot);\r
2199                         cX11.Frames.push_back( CursorFrameX11(cursor) );\r
2200                 }\r
2201 \r
2202                 Cursors.push_back( cX11 );\r
2203 \r
2204                 return (gui::ECURSOR_ICON)(Cursors.size() - 1);\r
2205         }\r
2206 #endif\r
2207         return gui::ECI_NORMAL;\r
2208 }\r
2209 \r
2210 //! replace the given cursor icon.\r
2211 void CIrrDeviceLinux::CCursorControl::changeIcon(gui::ECURSOR_ICON iconId, const gui::SCursorSprite& icon)\r
2212 {\r
2213 #ifdef _IRR_COMPILE_WITH_X11_\r
2214         if ( iconId >= (s32)Cursors.size() )\r
2215                 return;\r
2216 \r
2217         for ( u32 i=0; i < Cursors[iconId].Frames.size(); ++i )\r
2218                 XFreeCursor(Device->XDisplay, Cursors[iconId].Frames[i].IconHW);\r
2219 \r
2220         if ( icon.SpriteId >= 0 )\r
2221         {\r
2222                 CursorX11 cX11;\r
2223                 cX11.FrameTime = icon.SpriteBank->getSprites()[icon.SpriteId].frameTime;\r
2224                 for ( u32 i=0; i < icon.SpriteBank->getSprites()[icon.SpriteId].Frames.size(); ++i )\r
2225                 {\r
2226                         irr::u32 texId = icon.SpriteBank->getSprites()[icon.SpriteId].Frames[i].textureNumber;\r
2227                         irr::u32 rectId = icon.SpriteBank->getSprites()[icon.SpriteId].Frames[i].rectNumber;\r
2228                         irr::core::rect<s32> rectIcon = icon.SpriteBank->getPositions()[rectId];\r
2229                         Cursor cursor = Device->TextureToCursor(icon.SpriteBank->getTexture(texId), rectIcon, icon.HotSpot);\r
2230                         cX11.Frames.push_back( CursorFrameX11(cursor) );\r
2231                 }\r
2232 \r
2233                 Cursors[iconId] = cX11;\r
2234         }\r
2235 #endif\r
2236 }\r
2237 \r
2238 irr::core::dimension2di CIrrDeviceLinux::CCursorControl::getSupportedIconSize() const\r
2239 {\r
2240         // 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
2241         unsigned int width=0, height=0;\r
2242 #ifdef _IRR_COMPILE_WITH_X11_\r
2243         XQueryBestCursor(Device->XDisplay, Device->XWindow, 64, 64, &width, &height);\r
2244 #endif\r
2245         return core::dimension2di(width, height);\r
2246 }\r
2247 \r
2248 } // end namespace\r
2249 \r
2250 #endif // _IRR_COMPILE_WITH_X11_DEVICE_\r
2251 \r