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
5 #include "CIrrDeviceLinux.h"
\r
7 #ifdef _IRR_COMPILE_WITH_X11_DEVICE_
\r
11 #include <sys/utsname.h>
\r
14 #include "IEventReceiver.h"
\r
15 #include "ISceneManager.h"
\r
16 #include "IGUIElement.h"
\r
17 #include "IGUIEnvironment.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
30 #if defined(_IRR_LINUX_X11_XINPUT2_)
\r
31 #include <X11/extensions/XInput2.h>
\r
34 #if defined(_IRR_COMPILE_WITH_OGLES1_) || defined(_IRR_COMPILE_WITH_OGLES2_)
\r
35 #include "CEGLManager.h"
\r
38 #if defined(_IRR_COMPILE_WITH_OPENGL_)
\r
39 #include "CGLXManager.h"
\r
42 #ifdef _IRR_LINUX_XCURSOR_
\r
43 #include <X11/Xcursor/Xcursor.h>
\r
46 #if defined _IRR_COMPILE_WITH_JOYSTICK_EVENTS_
\r
51 #include <sys/joystick.h>
\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
59 #include <sys/ioctl.h> // Would normally be included in linux/input.h
\r
60 #include <linux/joystick.h>
\r
64 #endif // _IRR_COMPILE_WITH_JOYSTICK_EVENTS_
\r
70 #ifdef _IRR_COMPILE_WITH_OPENGL_
\r
71 IVideoDriver* createOpenGLDriver(const irr::SIrrlichtCreationParameters& params, io::IFileSystem* io, IContextManager* contextManager);
\r
74 #ifdef _IRR_COMPILE_WITH_OGLES1_
\r
75 IVideoDriver* createOGLES1Driver(const irr::SIrrlichtCreationParameters& params, io::IFileSystem* io, IContextManager* contextManager);
\r
78 #ifdef _IRR_COMPILE_WITH_OGLES2_
\r
79 IVideoDriver* createOGLES2Driver(const irr::SIrrlichtCreationParameters& params, io::IFileSystem* io, IContextManager* contextManager);
\r
82 #ifdef _IRR_COMPILE_WITH_WEBGL1_
\r
83 IVideoDriver* createWebGL1Driver(const irr::SIrrlichtCreationParameters& params, io::IFileSystem* io, IContextManager* contextManager);
\r
86 } // end namespace irr
\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
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
100 Atom X_ATOM_WM_DELETE_WINDOW;
\r
102 #if defined(_IRR_LINUX_X11_XINPUT2_)
\r
103 int XI_EXTENSIONS_OPCODE;
\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
117 #if defined(_IRR_LINUX_X11_XINPUT2_)
\r
118 currentTouchedCount(0),
\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
125 setDebugName("CIrrDeviceLinux");
\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
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
142 Operator = new COSOperator(linuxversion, this);
\r
143 os::Printer::log(linuxversion.c_str(), ELL_INFORMATION);
\r
149 if (CreationParams.DriverType != video::EDT_NULL)
\r
151 // create the window, only if we do not use the null device
\r
152 if (!createWindow())
\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
161 // create cursor control
\r
162 CursorControl = new CCursorControl(this, CreationParams.DriverType == video::EDT_NULL);
\r
170 createGUIAndScene();
\r
172 if (param.WindowMaximized)
\r
178 CIrrDeviceLinux::~CIrrDeviceLinux()
\r
180 #ifdef _IRR_COMPILE_WITH_X11_
\r
183 // Disable cursor (it is drop'ed in stub)
\r
186 CursorControl->setVisible(false);
\r
187 static_cast<CCursorControl*>(CursorControl)->clearCursors();
\r
190 // Must free OpenGL textures etc before destroying context, so can't wait for stub destructor
\r
191 if ( GUIEnvironment )
\r
193 GUIEnvironment->drop();
\r
194 GUIEnvironment = NULL;
\r
196 if ( SceneManager )
\r
198 SceneManager->drop();
\r
199 SceneManager = NULL;
\r
203 VideoDriver->drop();
\r
204 VideoDriver = NULL;
\r
207 destroyInputContext();
\r
211 if (ContextManager)
\r
213 ContextManager->destroyContext();
\r
214 ContextManager->destroySurface();
\r
217 if (!ExternalWindow)
\r
219 XDestroyWindow(XDisplay,XWindow);
\r
220 XCloseDisplay(XDisplay);
\r
226 #endif // #ifdef _IRR_COMPILE_WITH_X11_
\r
228 #if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_)
\r
229 for (u32 joystick = 0; joystick < ActiveJoysticks.size(); ++joystick)
\r
231 if (ActiveJoysticks[joystick].fd >= 0)
\r
233 close(ActiveJoysticks[joystick].fd);
\r
240 #if defined(_IRR_COMPILE_WITH_X11_) && defined(_DEBUG)
\r
241 int IrrPrintXError(Display *display, XErrorEvent *event)
\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
256 bool CIrrDeviceLinux::switchToFullscreen()
\r
258 if (!CreationParams.Fullscreen)
\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
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
278 XSendEvent(XDisplay, DefaultRootWindow(XDisplay), false,
\r
279 SubstructureNotifyMask | SubstructureRedirectMask, &ev);
\r
285 #if defined(_IRR_COMPILE_WITH_X11_)
\r
286 void IrrPrintXGrabError(int grabResult, const c8 * grabCommand )
\r
288 if ( grabResult == GrabSuccess )
\r
290 // os::Printer::log(grabCommand, "GrabSuccess", ELL_INFORMATION);
\r
294 switch ( grabResult )
\r
296 case AlreadyGrabbed:
\r
297 os::Printer::log(grabCommand, "AlreadyGrabbed", ELL_WARNING);
\r
299 case GrabNotViewable:
\r
300 os::Printer::log(grabCommand, "GrabNotViewable", ELL_WARNING);
\r
303 os::Printer::log(grabCommand, "GrabFrozen", ELL_WARNING);
\r
305 case GrabInvalidTime:
\r
306 os::Printer::log(grabCommand, "GrabInvalidTime", ELL_WARNING);
\r
309 os::Printer::log(grabCommand, "grab failed with unknown problem", ELL_WARNING);
\r
316 bool CIrrDeviceLinux::createWindow()
\r
318 #ifdef _IRR_COMPILE_WITH_X11_
\r
320 os::Printer::log("Creating X window...", ELL_INFORMATION);
\r
321 XSetErrorHandler(IrrPrintXError);
\r
324 XDisplay = XOpenDisplay(0);
\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
331 os::Printer::log("Could not open display, set DISPLAY variable", ELL_ERROR);
\r
335 Screennr = DefaultScreen(XDisplay);
\r
339 // check netwm support
\r
340 Atom WMCheck = XInternAtom(XDisplay, "_NET_SUPPORTING_WM_CHECK", True);
\r
341 if (WMCheck != None)
\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
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
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
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
368 VisualInfo = XGetVisualInfo(XDisplay, VisualScreenMask|VisualDepthMask,
\r
369 &visTempl, &visNumber);
\r
370 visTempl.depth -= 8;
\r
376 os::Printer::log("Fatal error, could not get visual.", ELL_ERROR);
\r
377 XCloseDisplay(XDisplay);
\r
383 os::Printer::log("Visual chosen", core::stringc(static_cast<u32>(VisualInfo->visualid)).c_str(), ELL_DEBUG);
\r
386 // create color map
\r
388 colormap = XCreateColormap(XDisplay,
\r
389 RootWindow(XDisplay, VisualInfo->screen),
\r
390 VisualInfo->visual, AllocNone);
\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
400 if (!CreationParams.WindowId)
\r
405 if (!CreationParams.Fullscreen)
\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
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
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
427 if (CreationParams.Fullscreen)
\r
429 // Don't try to set window position
\r
431 else if (CreationParams.WindowPosition.X >= 0 || CreationParams.WindowPosition.Y >= 0) // default is -1, -1
\r
433 // Window managers are free to ignore positions above, so give it another shot
\r
434 XMoveWindow(XDisplay,XWindow,x,y);
\r
439 // attach external window
\r
440 XWindow = (Window)CreationParams.WindowId;
\r
441 if (!CreationParams.IgnoreInput)
\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
452 0, 0, Width, Height, 0, VisualInfo->depth,
\r
453 InputOutput, VisualInfo->visual,
\r
454 CWBorderPixel | CWColormap | CWEventMask,
\r
457 // do not forget to map new window
\r
458 XMapWindow(XDisplay, child_window);
\r
460 // overwrite device window id
\r
461 XWindow = child_window;
\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
471 switchToFullscreen();
\r
473 WindowMinimized=false;
\r
474 XkbSetDetectableAutoRepeat(XDisplay, True, &AutorepeatSupport);
\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
486 StdHints = XAllocSizeHints();
\r
488 XGetWMNormalHints(XDisplay, XWindow, StdHints, &num);
\r
492 #endif // #ifdef _IRR_COMPILE_WITH_X11_
\r
497 //! create the driver
\r
498 void CIrrDeviceLinux::createDriver()
\r
500 switch(CreationParams.DriverType)
\r
502 #ifdef _IRR_COMPILE_WITH_X11_
\r
503 case video::EDT_OPENGL:
\r
504 #ifdef _IRR_COMPILE_WITH_OPENGL_
\r
506 video::SExposedVideoData data;
\r
507 data.OpenGLLinux.X11Window = XWindow;
\r
508 data.OpenGLLinux.X11Display = XDisplay;
\r
510 ContextManager->initialize(CreationParams, data);
\r
512 VideoDriver = video::createOpenGLDriver(CreationParams, FileSystem, ContextManager);
\r
515 os::Printer::log("No OpenGL support compiled in.", ELL_ERROR);
\r
518 case video::EDT_OGLES1:
\r
519 #ifdef _IRR_COMPILE_WITH_OGLES1_
\r
521 video::SExposedVideoData data;
\r
522 data.OpenGLLinux.X11Window = XWindow;
\r
523 data.OpenGLLinux.X11Display = XDisplay;
\r
525 ContextManager = new video::CEGLManager();
\r
526 ContextManager->initialize(CreationParams, data);
\r
528 VideoDriver = video::createOGLES1Driver(CreationParams, FileSystem, ContextManager);
\r
531 os::Printer::log("No OpenGL-ES1 support compiled in.", ELL_ERROR);
\r
534 case video::EDT_OGLES2:
\r
535 #ifdef _IRR_COMPILE_WITH_OGLES2_
\r
537 video::SExposedVideoData data;
\r
538 data.OpenGLLinux.X11Window = XWindow;
\r
539 data.OpenGLLinux.X11Display = XDisplay;
\r
541 ContextManager = new video::CEGLManager();
\r
542 ContextManager->initialize(CreationParams, data);
\r
544 VideoDriver = video::createOGLES2Driver(CreationParams, FileSystem, ContextManager);
\r
547 os::Printer::log("No OpenGL-ES2 support compiled in.", ELL_ERROR);
\r
550 case video::EDT_WEBGL1:
\r
551 #ifdef _IRR_COMPILE_WITH_WEBGL1_
\r
553 video::SExposedVideoData data;
\r
554 data.OpenGLLinux.X11Window = XWindow;
\r
555 data.OpenGLLinux.X11Display = XDisplay;
\r
557 ContextManager = new video::CEGLManager();
\r
558 ContextManager->initialize(CreationParams, data);
\r
560 VideoDriver = video::createWebGL1Driver(CreationParams, FileSystem, ContextManager);
\r
563 os::Printer::log("No WebGL1 support compiled in.", ELL_ERROR);
\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
571 case video::EDT_NULL:
\r
572 VideoDriver = video::createNullDriver(FileSystem, CreationParams.WindowSize);
\r
575 os::Printer::log("Unable to create video driver of unknown type.", ELL_ERROR);
\r
578 case video::EDT_NULL:
\r
579 VideoDriver = video::createNullDriver(FileSystem, CreationParams.WindowSize);
\r
582 os::Printer::log("No X11 support compiled in. Only Null driver available.", ELL_ERROR);
\r
588 #ifdef _IRR_COMPILE_WITH_X11_
\r
589 bool CIrrDeviceLinux::createInputContext()
\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
598 if ( !XSupportsLocale() )
\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
605 // Load XMODIFIERS (e.g. for IMEs)
\r
606 if (!XSetLocaleModifiers(""))
\r
608 setlocale(LC_CTYPE, oldLocale.c_str());
\r
609 os::Printer::log("XSetLocaleModifiers failed. Falling back to non-i18n input.", ELL_WARNING);
\r
613 XInputMethod = XOpenIM(XDisplay, NULL, NULL, NULL);
\r
614 if ( !XInputMethod )
\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
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
627 XIMStyle style = im_supported_styles->supported_styles[i];
\r
628 if ((style & supportedStyle) == style) /* if we can handle it */
\r
634 XFree(im_supported_styles);
\r
638 XDestroyIC(XInputContext);
\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
646 XInputContext = XCreateIC(XInputMethod,
\r
647 XNInputStyle, bestStyle,
\r
648 XNClientWindow, XWindow,
\r
650 if (!XInputContext )
\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
656 XSetICFocus(XInputContext);
\r
657 setlocale(LC_CTYPE, oldLocale.c_str());
\r
661 void CIrrDeviceLinux::destroyInputContext()
\r
663 if ( XInputContext )
\r
665 XUnsetICFocus(XInputContext);
\r
666 XDestroyIC(XInputContext);
\r
669 if ( XInputMethod )
\r
671 XCloseIM(XInputMethod);
\r
676 EKEY_CODE CIrrDeviceLinux::getKeyCode(XEvent &event)
\r
678 EKEY_CODE keyCode = (EKEY_CODE)0;
\r
681 mp.X11Key = XkbKeycodeToKeysym(XDisplay, event.xkey.keycode, 0, 0);
\r
682 const s32 idx = KeyMap.binary_search(mp);
\r
685 keyCode = (EKEY_CODE)KeyMap[idx].Win32Key;
\r
689 keyCode = KEY_UNKNOWN;
\r
692 os::Printer::log("No such X11Key, event keycode", core::stringc(event.xkey.keycode).c_str(), ELL_INFORMATION);
\r
694 else if (idx == -1)
\r
696 os::Printer::log("EKEY_CODE not found, X11 keycode", core::stringc(mp.X11Key).c_str(), ELL_INFORMATION);
\r
700 os::Printer::log("EKEY_CODE is 0, X11 keycode", core::stringc(mp.X11Key).c_str(), ELL_INFORMATION);
\r
707 //! runs the device. Returns false if device wants to be deleted
\r
708 bool CIrrDeviceLinux::run()
\r
712 #ifdef _IRR_COMPILE_WITH_X11_
\r
714 if ( CursorControl )
\r
715 static_cast<CCursorControl*>(CursorControl)->update();
\r
717 if ((CreationParams.DriverType != video::EDT_NULL) && XDisplay)
\r
720 irrevent.MouseInput.ButtonStates = 0xffffffff;
\r
722 while (XPending(XDisplay) > 0 && !Close)
\r
725 XNextEvent(XDisplay, &event);
\r
726 if (XFilterEvent(&event, None))
\r
729 switch (event.type)
\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
736 Width = event.xconfigure.width;
\r
737 Height = event.xconfigure.height;
\r
740 VideoDriver->OnResize(core::dimension2d<u32>(Width, Height));
\r
745 WindowMinimized=false;
\r
749 WindowMinimized=true;
\r
753 WindowHasFocus=true;
\r
757 WindowHasFocus=false;
\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
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
773 postEventFromUser(irrevent);
\r
777 case ButtonRelease:
\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
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
793 irrevent.MouseInput.Event = irr::EMIE_COUNT;
\r
795 switch(event.xbutton.button)
\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
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
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
816 if (event.type == ButtonPress)
\r
818 irrevent.MouseInput.Event = EMIE_MOUSE_WHEEL;
\r
819 irrevent.MouseInput.Wheel = 1.0f;
\r
824 if (event.type == ButtonPress)
\r
826 irrevent.MouseInput.Event = EMIE_MOUSE_WHEEL;
\r
827 irrevent.MouseInput.Wheel = -1.0f;
\r
832 if (irrevent.MouseInput.Event != irr::EMIE_COUNT)
\r
834 postEventFromUser(irrevent);
\r
836 if ( irrevent.MouseInput.Event >= EMIE_LMOUSE_PRESSED_DOWN && irrevent.MouseInput.Event <= EMIE_MMOUSE_PRESSED_DOWN )
\r
838 u32 clicks = checkSuccessiveClicks(irrevent.MouseInput.X, irrevent.MouseInput.Y, irrevent.MouseInput.Event);
\r
841 irrevent.MouseInput.Event = (EMOUSE_INPUT_EVENT)(EMIE_LMOUSE_DOUBLE_CLICK + irrevent.MouseInput.Event-EMIE_LMOUSE_PRESSED_DOWN);
\r
842 postEventFromUser(irrevent);
\r
844 else if ( clicks == 3 )
\r
846 irrevent.MouseInput.Event = (EMOUSE_INPUT_EVENT)(EMIE_LMOUSE_TRIPLE_CLICK + irrevent.MouseInput.Event-EMIE_LMOUSE_PRESSED_DOWN);
\r
847 postEventFromUser(irrevent);
\r
853 case MappingNotify:
\r
854 XRefreshKeyboardMapping (&event.xmapping) ;
\r
858 if (0 == AutorepeatSupport && (XPending( XDisplay ) > 0) )
\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
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
869 // Ignore the key release event
\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
881 postEventFromUser(irrevent);
\r
887 if ( XInputContext )
\r
889 wchar_t buf[64]={0};
\r
891 int strLen = XwcLookupString(XInputContext, &event.xkey, buf, sizeof(buf)/sizeof(wchar_t)-1, &mp.X11Key, &status);
\r
892 if ( status == XBufferOverflow )
\r
894 os::Printer::log("XwcLookupString needs a larger buffer", ELL_INFORMATION);
\r
896 if ( strLen > 0 && (status == XLookupChars || status == XLookupBoth) )
\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
910 irrevent.KeyInput.Char = buf[0];
\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
927 irrevent.KeyInput.Char = 0;
\r
930 else // Old version without InputContext. Does not support i18n, but good to have as fallback.
\r
937 XLookupString(&event.xkey, tmp.buf, sizeof(tmp.buf), &mp.X11Key, NULL);
\r
938 irrevent.KeyInput.Char = tmp.wbuf[0];
\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
950 case ClientMessage:
\r
952 if (static_cast<Atom>(event.xclient.data.l[0]) == X_ATOM_WM_DELETE_WINDOW && X_ATOM_WM_DELETE_WINDOW != None)
\r
954 os::Printer::log("Quit message received.", ELL_INFORMATION);
\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
968 case SelectionRequest:
\r
970 XSelectionRequestEvent *req = &(event.xselectionrequest);
\r
972 auto send_response = [this, req](Atom property) {
\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
984 auto send_response_refuse = [&send_response] {
\r
985 send_response(None);
\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
998 (const unsigned char *)data,
\r
1000 send_response(req->property);
\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
1010 const core::stringc &text_buffer = req->selection == X_ATOM_CLIPBOARD ?
\r
1011 Clipboard : PrimarySelection;
\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
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
1027 XChangeProperty(XDisplay,
\r
1029 req->target, X_ATOM_UTF8_STRING,
\r
1030 8, // format = 8-bit
\r
1032 (unsigned char *)text_buffer.c_str(),
\r
1033 text_buffer.size());
\r
1034 send_response(req->target);
\r
1038 if (req->target == X_ATOM_TARGETS) {
\r
1042 X_ATOM_UTF8_STRING,
\r
1043 X_ATOM_UTF8_MIME_TYPE
\r
1045 set_property_and_notify(
\r
1047 32, // Atom is long, we need to set 32 for longs
\r
1049 sizeof(data) / sizeof(*data)
\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
1058 text_buffer.c_str(),
\r
1059 text_buffer.size()
\r
1063 // refuse the request
\r
1064 send_response_refuse();
\r
1069 #if defined(_IRR_LINUX_X11_XINPUT2_)
\r
1070 case GenericEvent:
\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
1076 XIDeviceEvent *de = (XIDeviceEvent *) cookie->data;
\r
1078 irrevent.EventType = EET_TOUCH_INPUT_EVENT;
\r
1080 irrevent.TouchInput.Event = cookie->evtype == XI_TouchUpdate ? ETIE_MOVED : (cookie->evtype == XI_TouchBegin ? ETIE_PRESSED_DOWN : ETIE_LEFT_UP);
\r
1082 irrevent.TouchInput.ID = de->detail;
\r
1083 irrevent.TouchInput.X = de->event_x;
\r
1084 irrevent.TouchInput.Y = de->event_y;
\r
1086 if (irrevent.TouchInput.Event == ETIE_PRESSED_DOWN) {
\r
1087 currentTouchedCount++;
\r
1089 irrevent.TouchInput.touchedCount = currentTouchedCount;
\r
1090 if (currentTouchedCount > 0 && irrevent.TouchInput.Event == ETIE_LEFT_UP) {
\r
1091 currentTouchedCount--;
\r
1094 postEventFromUser(irrevent);
\r
1104 // Update IME information
\r
1105 if (XInputContext && GUIEnvironment)
\r
1107 gui::IGUIElement *elem = GUIEnvironment->getFocus();
\r
1108 if (elem && elem->acceptsIME())
\r
1110 core::rect<s32> r = elem->getAbsolutePosition();
\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
1119 XUnsetICFocus(XInputContext);
\r
1126 #endif //_IRR_COMPILE_WITH_X11_
\r
1135 //! Pause the current process for the minimum time allowed only to allow other processes to execute
\r
1136 void CIrrDeviceLinux::yield()
\r
1138 struct timespec ts = {0,1};
\r
1139 nanosleep(&ts, NULL);
\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
1146 const bool wasStopped = Timer ? Timer->isStopped() : true;
\r
1148 struct timespec ts;
\r
1149 ts.tv_sec = (time_t) (timeMs / 1000);
\r
1150 ts.tv_nsec = (long) (timeMs % 1000) * 1000000;
\r
1152 if (pauseTimer && !wasStopped)
\r
1155 nanosleep(&ts, NULL);
\r
1157 if (pauseTimer && !wasStopped)
\r
1162 //! sets the caption of the window
\r
1163 void CIrrDeviceLinux::setWindowCaption(const wchar_t* text)
\r
1165 #ifdef _IRR_COMPILE_WITH_X11_
\r
1166 if (CreationParams.DriverType == video::EDT_NULL)
\r
1169 XTextProperty txt;
\r
1170 if (Success==XwcTextListToTextProperty(XDisplay, const_cast<wchar_t**>(&text),
\r
1171 1, XStdICCTextStyle, &txt))
\r
1173 XSetWMName(XDisplay, XWindow, &txt);
\r
1174 XSetWMIconName(XDisplay, XWindow, &txt);
\r
1181 //! notifies the device that it should close itself
\r
1182 void CIrrDeviceLinux::closeDevice()
\r
1188 //! returns if window is active. if not, nothing need to be drawn
\r
1189 bool CIrrDeviceLinux::isWindowActive() const
\r
1191 return (WindowHasFocus && !WindowMinimized);
\r
1195 //! returns if window has focus.
\r
1196 bool CIrrDeviceLinux::isWindowFocused() const
\r
1198 return WindowHasFocus;
\r
1202 //! returns if window is minimized.
\r
1203 bool CIrrDeviceLinux::isWindowMinimized() const
\r
1205 return WindowMinimized;
\r
1209 //! returns last state from maximizeWindow() and restoreWindow()
\r
1210 bool CIrrDeviceLinux::isWindowMaximized() const
\r
1212 return WindowMaximized;
\r
1216 //! returns color format of the window.
\r
1217 video::ECOLOR_FORMAT CIrrDeviceLinux::getColorFormat() const
\r
1219 #ifdef _IRR_COMPILE_WITH_X11_
\r
1220 if (VisualInfo && (VisualInfo->depth != 16))
\r
1221 return video::ECF_R8G8B8;
\r
1224 return video::ECF_R5G6B5;
\r
1228 //! Sets if the window should be resizable in windowed mode.
\r
1229 void CIrrDeviceLinux::setResizable(bool resize)
\r
1231 #ifdef _IRR_COMPILE_WITH_X11_
\r
1232 if (CreationParams.DriverType == video::EDT_NULL || CreationParams.Fullscreen )
\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
1247 XSetWMNormalHints(XDisplay, XWindow, StdHints);
\r
1250 #endif // #ifdef _IRR_COMPILE_WITH_X11_
\r
1253 //! Resize the render window.
\r
1254 void CIrrDeviceLinux::setWindowSize(const irr::core::dimension2d<u32>& size)
\r
1256 #ifdef _IRR_COMPILE_WITH_X11_
\r
1257 if (CreationParams.DriverType == video::EDT_NULL || CreationParams.Fullscreen )
\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
1265 #endif // #ifdef _IRR_COMPILE_WITH_X11_
\r
1269 //! Minimize window
\r
1270 void CIrrDeviceLinux::minimizeWindow()
\r
1272 #ifdef _IRR_COMPILE_WITH_X11_
\r
1273 XIconifyWindow(XDisplay, XWindow, Screennr);
\r
1278 //! Maximize window
\r
1279 void CIrrDeviceLinux::maximizeWindow()
\r
1281 #ifdef _IRR_COMPILE_WITH_X11_
\r
1282 // Maximize is not implemented in bare X, it's a WM construct.
\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
1295 XSendEvent(XDisplay, DefaultRootWindow(XDisplay), false,
\r
1296 SubstructureNotifyMask|SubstructureRedirectMask, &ev);
\r
1299 XMapWindow(XDisplay, XWindow);
\r
1301 WindowMaximized = true;
\r
1306 //! Restore original window size
\r
1307 void CIrrDeviceLinux::restoreWindow()
\r
1309 #ifdef _IRR_COMPILE_WITH_X11_
\r
1310 // Maximize is not implemented in bare X, it's a WM construct.
\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
1323 XSendEvent(XDisplay, DefaultRootWindow(XDisplay), false,
\r
1324 SubstructureNotifyMask|SubstructureRedirectMask, &ev);
\r
1327 XMapWindow(XDisplay, XWindow);
\r
1329 WindowMaximized = false;
\r
1333 core::position2di CIrrDeviceLinux::getWindowPosition()
\r
1335 int wx = 0, wy = 0;
\r
1336 #ifdef _IRR_COMPILE_WITH_X11_
\r
1338 XTranslateCoordinates(XDisplay, XWindow, DefaultRootWindow(XDisplay), 0, 0, &wx, &wy, &child);
\r
1340 return core::position2di(wx, wy);
\r
1343 void CIrrDeviceLinux::createKeyMap()
\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
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
1548 bool CIrrDeviceLinux::activateJoysticks(core::array<SJoystickInfo> & joystickInfo)
\r
1550 #if defined (_IRR_COMPILE_WITH_JOYSTICK_EVENTS_)
\r
1552 joystickInfo.clear();
\r
1555 for (joystick = 0; joystick < 32; ++joystick)
\r
1557 // The joystick device could be here...
\r
1558 core::stringc devName = "/dev/js";
\r
1559 devName += joystick;
\r
1561 SJoystickInfo returnInfo;
\r
1562 JoystickInfo info;
\r
1564 info.fd = open(devName.c_str(), O_RDONLY);
\r
1565 if (-1 == info.fd)
\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
1575 devName = "/dev/joy";
\r
1576 devName += joystick;
\r
1577 info.fd = open(devName.c_str(), O_RDONLY);
\r
1581 if (-1 == info.fd)
\r
1584 #ifdef __FreeBSD__
\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
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
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
1601 ActiveJoysticks.push_back(info);
\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
1608 #ifndef __FreeBSD__
\r
1610 ioctl( info.fd, JSIOCGNAME(80), name);
\r
1611 returnInfo.Name = name;
\r
1614 joystickInfo.push_back(returnInfo);
\r
1617 for (joystick = 0; joystick < joystickInfo.size(); ++joystick)
\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
1629 #endif // _IRR_COMPILE_WITH_JOYSTICK_EVENTS_
\r
1633 void CIrrDeviceLinux::pollJoysticks()
\r
1635 #if defined (_IRR_COMPILE_WITH_JOYSTICK_EVENTS_)
\r
1636 if (0 == ActiveJoysticks.size())
\r
1639 for (u32 j= 0; j< ActiveJoysticks.size(); ++j)
\r
1641 JoystickInfo & info = ActiveJoysticks[j];
\r
1643 #ifdef __FreeBSD__
\r
1644 struct joystick js;
\r
1645 if (read(info.fd, &js, sizeof(js)) == sizeof(js))
\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
1652 struct js_event event;
\r
1653 while (sizeof(event) == read(info.fd, &event, sizeof(event)))
\r
1655 switch(event.type & ~JS_EVENT_INIT)
\r
1657 case JS_EVENT_BUTTON:
\r
1659 info.persistentData.JoystickEvent.ButtonStates |= (1 << event.number);
\r
1661 info.persistentData.JoystickEvent.ButtonStates &= ~(1 << event.number);
\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
1675 // Send an irrlicht joystick event once per ::run() even if no new data were received.
\r
1676 (void)postEventFromUser(info.persistentData);
\r
1678 #endif // _IRR_COMPILE_WITH_JOYSTICK_EVENTS_
\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
1687 Window ownerWindow = XGetSelectionOwner(XDisplay, selection);
\r
1688 if (ownerWindow == XWindow) {
\r
1689 return text_buffer.c_str();
\r
1694 if (ownerWindow == None) {
\r
1695 return text_buffer.c_str();
\r
1698 // delete the property to be set beforehand
\r
1699 XDeleteProperty(XDisplay, XWindow, XA_PRIMARY);
\r
1701 XConvertSelection(XDisplay, selection, X_ATOM_UTF8_STRING, XA_PRIMARY,
\r
1702 XWindow, CurrentTime);
\r
1705 // wait for event via a blocking call
\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
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
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
1730 unsigned long numItems, bytesLeft, dummy;
\r
1731 unsigned char *data = nullptr;
\r
1732 XGetWindowProperty (XDisplay, XWindow,
\r
1733 property_set, // property name
\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
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
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
1759 return text_buffer.c_str();
\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
1772 // delete the property again, to inform the owner about the successful transfer
\r
1773 XDeleteProperty(XDisplay, XWindow, property_set);
\r
1775 return text_buffer.c_str();
\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
1783 #if defined(_IRR_COMPILE_WITH_X11_)
\r
1784 return getTextFromSelection(X_ATOM_CLIPBOARD, Clipboard);
\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
1794 #if defined(_IRR_COMPILE_WITH_X11_)
\r
1795 return getTextFromSelection(XA_PRIMARY, PrimarySelection);
\r
1801 #if defined(_IRR_COMPILE_WITH_X11_)
\r
1802 bool CIrrDeviceLinux::becomeSelectionOwner(Atom selection) const
\r
1804 XSetSelectionOwner (XDisplay, selection, XWindow, CurrentTime);
\r
1805 XFlush (XDisplay);
\r
1806 Window owner = XGetSelectionOwner(XDisplay, selection);
\r
1807 return owner == XWindow;
\r
1811 //! copies text to the clipboard
\r
1812 void CIrrDeviceLinux::copyToClipboard(const c8 *text) const
\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
1818 if (!becomeSelectionOwner(X_ATOM_CLIPBOARD)) {
\r
1819 os::Printer::log("CIrrDeviceLinux::copyToClipboard: failed to set owner", ELL_WARNING);
\r
1824 //! copies text to the primary selection
\r
1825 void CIrrDeviceLinux::copyToPrimarySelection(const c8 *text) const
\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
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
1839 if ( event && event->type == *(int*)arg )
\r
1841 // os::Printer::log("remove event", core::stringc((int)arg).c_str(), ELL_INFORMATION);
\r
1846 #endif //_IRR_COMPILE_WITH_X11_
\r
1848 //! Remove all messages pending in the system message loop
\r
1849 void CIrrDeviceLinux::clearSystemMessages()
\r
1851 #ifdef _IRR_COMPILE_WITH_X11_
\r
1852 if (CreationParams.DriverType != video::EDT_NULL)
\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
1866 #endif //_IRR_COMPILE_WITH_X11_
\r
1869 void CIrrDeviceLinux::initXAtoms()
\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
1884 void CIrrDeviceLinux::initXInput2()
\r
1886 #if defined(_IRR_LINUX_X11_XINPUT2_)
\r
1889 if (!XQueryExtension(XDisplay, "XInputExtension", &XI_EXTENSIONS_OPCODE, &ev, &err))
\r
1891 os::Printer::log("X Input extension not available.", ELL_WARNING);
\r
1897 int rc = XIQueryVersion(XDisplay, &major, &minor);
\r
1898 if ( rc != Success )
\r
1900 os::Printer::log("No XI2 support.", ELL_WARNING);
\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
1916 XISelectEvents(XDisplay, XWindow, &eventMask, 1);
\r
1921 #ifdef _IRR_COMPILE_WITH_X11_
\r
1923 Cursor CIrrDeviceLinux::TextureToMonochromeCursor(irr::video::ITexture * tex, const core::rect<s32>& sourceRect, const core::position2d<s32> &hotspot)
\r
1925 XImage * sourceImage = XCreateImage(XDisplay, VisualInfo->visual,
\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
1932 sourceImage->data = new char[sourceImage->height * sourceImage->bytes_per_line];
\r
1933 XImage * maskImage = XCreateImage(XDisplay, VisualInfo->visual,
\r
1936 0, 0, sourceRect.getWidth(), sourceRect.getHeight(),
\r
1937 32, // bitmap_pad,
\r
1938 0 // bytes_per_line
\r
1940 maskImage->data = new char[maskImage->height * maskImage->bytes_per_line];
\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
1951 data += bytesLeftGap;
\r
1952 for ( s32 x = 0; x < sourceRect.getWidth(); ++x )
\r
1954 video::SColor pixelCol;
\r
1955 pixelCol.setData((const void*)data, format);
\r
1956 data += bytesPerPixel;
\r
1958 if ( pixelCol.getAlpha() == 0 ) // transparent
\r
1960 XPutPixel(maskImage, x, y, 0);
\r
1961 XPutPixel(sourceImage, x, y, 0);
\r
1965 if ( pixelCol.getAverage() >= 127 )
\r
1966 XPutPixel(sourceImage, x, y, 1);
\r
1968 XPutPixel(sourceImage, x, y, 0);
\r
1969 XPutPixel(maskImage, x, y, 1);
\r
1972 data += bytesRightGap;
\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
1980 values.foreground = 1;
\r
1981 values.background = 1;
\r
1982 GC gc = XCreateGC( XDisplay, sourcePixmap, GCForeground | GCBackground, &values );
\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
1987 XFreeGC(XDisplay, gc);
\r
1988 XDestroyImage(sourceImage);
\r
1989 XDestroyImage(maskImage);
\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
2002 cursorResult = XCreatePixmapCursor(XDisplay, sourcePixmap, maskPixmap, &foreground, &background, hotspot.X, hotspot.Y);
\r
2004 XFreePixmap(XDisplay, sourcePixmap);
\r
2005 XFreePixmap(XDisplay, maskPixmap);
\r
2007 return cursorResult;
\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
2013 XcursorImage * image = XcursorImageCreate (sourceRect.getWidth(), sourceRect.getHeight());
\r
2014 image->xhot = hotspot.X;
\r
2015 image->yhot = hotspot.Y;
\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
2027 data += bytesLeftGap;
\r
2028 for ( s32 x = 0; x < sourceRect.getWidth(); ++x )
\r
2030 video::SColor pixelCol;
\r
2031 pixelCol.setData((const void*)data, format);
\r
2032 data += bytesPerPixel;
\r
2034 *target = (XcursorPixel)pixelCol.color;
\r
2037 data += bytesRightGap;
\r
2041 Cursor cursorResult=XcursorImageLoadCursor(XDisplay, image);
\r
2043 XcursorImageDestroy(image);
\r
2046 return cursorResult;
\r
2048 #endif // #ifdef _IRR_LINUX_XCURSOR_
\r
2050 Cursor CIrrDeviceLinux::TextureToCursor(irr::video::ITexture * tex, const core::rect<s32>& sourceRect, const core::position2d<s32> &hotspot)
\r
2052 #ifdef _IRR_LINUX_XCURSOR_
\r
2053 return TextureToARGBCursor( tex, sourceRect, hotspot );
\r
2055 return TextureToMonochromeCursor( tex, sourceRect, hotspot );
\r
2058 #endif // _IRR_COMPILE_WITH_X11_
\r
2061 CIrrDeviceLinux::CCursorControl::CCursorControl(CIrrDeviceLinux* dev, bool null)
\r
2063 #ifdef _IRR_COMPILE_WITH_X11_
\r
2064 , PlatformBehavior(gui::ECPB_NONE), LastQuery(0)
\r
2065 #ifdef _IRR_LINUX_X11_XINPUT2_
\r
2069 , IsVisible(true), Null(null), UseReferenceRect(false)
\r
2070 , ActiveIcon(gui::ECI_NORMAL), ActiveIconStartTime(0)
\r
2072 #ifdef _IRR_COMPILE_WITH_X11_
\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
2081 os::Printer::log("Detected classic multi-head setup, not using XIWarpPointer");
\r
2085 XIGetClientPointer(Device->XDisplay, Device->XWindow, &DeviceId);
\r
2090 unsigned long valuemask = 0;
\r
2094 // this code, for making the cursor invisible was sent in by
\r
2095 // Sirshane, thank your very much!
\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
2104 GC gc = XCreateGC( Device->XDisplay, invisBitmap, valuemask, &values );
\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
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
2120 CIrrDeviceLinux::CCursorControl::~CCursorControl()
\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
2126 #ifdef _IRR_COMPILE_WITH_X11_
\r
2127 void CIrrDeviceLinux::CCursorControl::clearCursors()
\r
2130 XFreeCursor(Device->XDisplay, InvisCursor);
\r
2131 for ( u32 i=0; i < Cursors.size(); ++i )
\r
2133 for ( u32 f=0; f < Cursors[i].Frames.size(); ++f )
\r
2135 XFreeCursor(Device->XDisplay, Cursors[i].Frames[f].IconHW);
\r
2140 void CIrrDeviceLinux::CCursorControl::initCursors()
\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
2157 void CIrrDeviceLinux::CCursorControl::update()
\r
2159 if ( (u32)ActiveIcon < Cursors.size() && !Cursors[ActiveIcon].Frames.empty() && Cursors[ActiveIcon].FrameTime )
\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
2169 //! Sets the active cursor icon
\r
2170 void CIrrDeviceLinux::CCursorControl::setActiveIcon(gui::ECURSOR_ICON iconId)
\r
2172 #ifdef _IRR_COMPILE_WITH_X11_
\r
2173 if ( iconId >= (s32)Cursors.size() )
\r
2176 if ( Cursors[iconId].Frames.size() )
\r
2177 XDefineCursor(Device->XDisplay, Device->XWindow, Cursors[iconId].Frames[0].IconHW);
\r
2179 ActiveIconStartTime = Device->getTimer()->getRealTime();
\r
2180 ActiveIcon = iconId;
\r
2185 //! Add a custom sprite as cursor icon.
\r
2186 gui::ECURSOR_ICON CIrrDeviceLinux::CCursorControl::addIcon(const gui::SCursorSprite& icon)
\r
2188 #ifdef _IRR_COMPILE_WITH_X11_
\r
2189 if ( icon.SpriteId >= 0 )
\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
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
2202 Cursors.push_back( cX11 );
\r
2204 return (gui::ECURSOR_ICON)(Cursors.size() - 1);
\r
2207 return gui::ECI_NORMAL;
\r
2210 //! replace the given cursor icon.
\r
2211 void CIrrDeviceLinux::CCursorControl::changeIcon(gui::ECURSOR_ICON iconId, const gui::SCursorSprite& icon)
\r
2213 #ifdef _IRR_COMPILE_WITH_X11_
\r
2214 if ( iconId >= (s32)Cursors.size() )
\r
2217 for ( u32 i=0; i < Cursors[iconId].Frames.size(); ++i )
\r
2218 XFreeCursor(Device->XDisplay, Cursors[iconId].Frames[i].IconHW);
\r
2220 if ( icon.SpriteId >= 0 )
\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
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
2233 Cursors[iconId] = cX11;
\r
2238 irr::core::dimension2di CIrrDeviceLinux::CCursorControl::getSupportedIconSize() const
\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
2245 return core::dimension2di(width, height);
\r
2248 } // end namespace
\r
2250 #endif // _IRR_COMPILE_WITH_X11_DEVICE_
\r