0f5f66c0b0704fdcee284af5b3414c47bf9e188e
[platform/core/uifw/dali-adaptor.git] / dali / internal / window-system / x11 / window-system-x.cpp
1 /*
2  * Copyright (c) 2023 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/window-system/x11/window-system-x.h>
20
21 // INTERNAL HEADERS
22 #include <dali/devel-api/adaptor-framework/keyboard.h>
23 #include <dali/integration-api/adaptor-framework/adaptor.h>
24 #include <dali/integration-api/adaptor-framework/scene-holder.h>
25 #include <dali/integration-api/debug.h>
26 #include <dali/internal/system/common/file-descriptor-monitor.h>
27 #include <dali/internal/system/common/system-factory.h>
28 #include <dali/internal/window-system/common/window-system.h>
29
30 // EXTERNAL_HEADERS
31 #include <X11/XKBlib.h>
32 #include <X11/Xatom.h>
33 #include <X11/Xlib.h>
34 #include <X11/Xutil.h>
35 #include <X11/extensions/XInput2.h>
36 #include <X11/extensions/Xrender.h>
37
38 #include <unistd.h>
39 #include <algorithm>
40 #include <cerrno>
41 #include <cstring>
42 #include <unordered_map>
43 #include <vector>
44
45 #include <fcntl.h>
46 #include <sys/stat.h>
47 #include <sys/types.h>
48
49 namespace Dali
50 {
51 namespace Internal
52 {
53 namespace Adaptor
54 {
55 namespace
56 {
57 const int MOUSE_SCROLL_WHEEL_UP{4};
58 const int MOUSE_SCROLL_WHEEL_DOWN{5};
59 const int MOUSE_SCROLL_WHEEL_LEFT{6};
60 const int MOUSE_SCROLL_WHEEL_RIGHT{7};
61
62 /**
63  * @brief Get an XWindow property
64  *
65  * @tparam T - The type of data to return
66  * @param[in] display The display containing the window
67  * @param[in] window The window to get the property for
68  * @param[in] property The property to acquire
69  * @param[in] type The property type
70  * @param[out] data The property data
71  *
72  * @return true if the property was successfully retrieved
73  */
74 template<typename T>
75 bool GetWindowProperty(::Display* display, ::Window window, ::Atom property, ::Atom type, std::vector<T>& data)
76 {
77   ::Atom         actualType;
78   int            actualFormat;
79   unsigned long  numberOfItems = 0, bytesRemaining = 0;
80   unsigned char* propertyData{nullptr};
81
82   if(!window)
83   {
84     window = DefaultRootWindow(display);
85   }
86
87   XSync(display, false);
88   XGetWindowProperty(display, window, property, 0, LONG_MAX, False, type, &actualType, &actualFormat, &numberOfItems, &bytesRemaining, &propertyData);
89
90   if(actualFormat == 0 || numberOfItems == 0 || actualType != type)
91   {
92     XFree(propertyData);
93     return false;
94   }
95
96   switch(actualFormat)
97   {
98     case 8:
99     {
100       if(sizeof(T) == sizeof(unsigned char))
101       {
102         data.resize(numberOfItems + 1); // Allow space for c-string terminator
103         for(int i = 0; i < numberOfItems + 1; ++i)
104         {
105           data[i] = static_cast<T>(propertyData[i]);
106         }
107       }
108       break;
109     }
110     case 16:
111     {
112       if(sizeof(T) == sizeof(unsigned short))
113       {
114         data.resize(numberOfItems);
115         for(int i = 0; i < numberOfItems; ++i)
116         {
117           data[i] = static_cast<T>(((unsigned short*)propertyData)[i]);
118         }
119       }
120       break;
121     }
122     case 32:
123     {
124       if(sizeof(T) == sizeof(unsigned long))
125       {
126         data.resize(numberOfItems);
127         for(unsigned long i = 0; i < numberOfItems; ++i)
128         {
129           // X returns native long type, regardless of whether it's actually 32 bits or not
130           data[i] = static_cast<T>(((unsigned long*)propertyData)[i]);
131         }
132       }
133       break;
134     }
135   }
136   XFree(propertyData);
137   return data.size() != 0;
138 }
139
140 }; // namespace
141
142 namespace WindowSystem
143 {
144 static WindowSystemX* gWindowSystem{nullptr};
145 static bool           gGeometryHittest = false;
146
147 /**
148  * Initialize the window system (currently run from the first window that gets created)
149  */
150 void Initialize()
151 {
152   if(nullptr == gWindowSystem)
153   {
154     gWindowSystem = new WindowSystemX();
155   }
156 }
157
158 /**
159  * Shutdown the window system (Currently run from the first window
160  */
161 void Shutdown()
162 {
163   if(nullptr != gWindowSystem)
164   {
165     delete gWindowSystem;
166     gWindowSystem = nullptr;
167   }
168 }
169
170 Atom WindowSystemX::ATOM_WM_PROTOCOLS{0};
171 Atom WindowSystemX::ATOM_WM_STATE{0};
172 Atom WindowSystemX::ATOM_WM_DELETE_WINDOW{0};
173 Atom WindowSystemX::ATOM_WM_TRANSIENT_FOR{0};
174 Atom WindowSystemX::ATOM_NET_ACTIVE_WINDOW{0};
175 Atom WindowSystemX::ATOM_NET_STARTUP_ID{0};
176 Atom WindowSystemX::ATOM_NET_WM_PID{0};
177 Atom WindowSystemX::ATOM_NET_WM_WINDOW_TYPE{0};
178 Atom WindowSystemX::ATOM_NET_WM_WINDOW_TYPE_NORMAL{0};
179 Atom WindowSystemX::ATOM_NET_WM_NAME{0};
180 Atom WindowSystemX::ATOM_UTF8_STRING{0};
181
182 struct AtomItem
183 {
184   std::string name;
185   Atom*       atom;
186 };
187
188 const AtomItem atomItems[] =
189   {
190     {"UTF8_STRING", &WindowSystemX::ATOM_UTF8_STRING},
191     {"WM_DELETE_WINDOW", &WindowSystemX::ATOM_WM_DELETE_WINDOW},
192     {"WM_PROTOCOLS", &WindowSystemX::ATOM_WM_PROTOCOLS},
193     {"WM_STATE", &WindowSystemX::ATOM_WM_STATE},
194     {"WM_TRANSIENT_FOR", &WindowSystemX::ATOM_WM_TRANSIENT_FOR},
195     {"_NET_ACTIVE_WINDOW", &WindowSystemX::ATOM_NET_ACTIVE_WINDOW},
196     {"_NET_STARTUP_ID", &WindowSystemX::ATOM_NET_STARTUP_ID},
197     {"_NET_WM_NAME", &WindowSystemX::ATOM_NET_WM_NAME},
198     {"_NET_WM_PID", &WindowSystemX::ATOM_NET_WM_PID},
199     {"_NET_WM_WINDOW_TYPE", &WindowSystemX::ATOM_NET_WM_WINDOW_TYPE},
200     {"_NET_WM_WINDOW_TYPE_NORMAL", &WindowSystemX::ATOM_NET_WM_WINDOW_TYPE_NORMAL},
201 };
202
203 const int NUMBER_OF_ATOMS = sizeof(atomItems) / sizeof(AtomItem);
204
205 struct DeleteWindowRequest
206 {
207   ::Window* window;
208 };
209
210 void ConfigureNotifyEventHandler(const XEvent* xevent)
211 {
212   WindowSystemX::X11ConfigureNotifyEvent configureNotify;
213   configureNotify.window = xevent->xconfigure.window;
214   configureNotify.event  = xevent;
215
216   configureNotify.x      = xevent->xconfigure.x;
217   configureNotify.y      = xevent->xconfigure.y;
218   configureNotify.width  = xevent->xconfigure.width;
219   configureNotify.height = xevent->xconfigure.height;
220
221   GetImplementation().TriggerEventHandler(WindowSystemBase::Event::CONFIGURE_NOTIFY, configureNotify);
222 }
223
224 void PropertyNotifyEventHandler(const XEvent* xevent)
225 {
226   WindowSystemX::X11PropertyNotifyEvent propertyNotifyEvent;
227   propertyNotifyEvent.window    = xevent->xproperty.window;
228   propertyNotifyEvent.event     = xevent;
229   propertyNotifyEvent.timestamp = xevent->xproperty.time;
230   propertyNotifyEvent.atom      = xevent->xproperty.atom;
231   propertyNotifyEvent.state     = xevent->xproperty.state;
232   GetImplementation().TriggerEventHandler(WindowSystemBase::Event::PROPERTY_NOTIFY, propertyNotifyEvent);
233 }
234
235 void ClientMessageEventHandler(const XEvent* xevent)
236 {
237   if((xevent->xclient.message_type == WindowSystemX::ATOM_WM_PROTOCOLS) &&
238      (xevent->xclient.format == 32) &&
239      (static_cast<Atom>(xevent->xclient.data.l[0]) == WindowSystemX::ATOM_WM_DELETE_WINDOW))
240   {
241     WindowSystemX::X11Event x11Event;
242     x11Event.window = xevent->xclient.window;
243     x11Event.event  = xevent;
244
245     GetImplementation().TriggerEventHandler(WindowSystemBase::Event::DELETE_REQUEST, x11Event);
246   }
247 }
248
249 void FocusInEventHandler(const XEvent* xevent)
250 {
251   WindowSystemX::X11Event x11Event;
252   x11Event.window = xevent->xclient.window;
253   x11Event.event  = xevent;
254   GetImplementation().TriggerEventHandler(WindowSystemBase::Event::FOCUS_IN, x11Event);
255 }
256
257 void FocusOutEventHandler(const XEvent* xevent)
258 {
259   WindowSystemX::X11Event x11Event;
260   x11Event.window = xevent->xclient.window;
261   x11Event.event  = xevent;
262   GetImplementation().TriggerEventHandler(WindowSystemBase::Event::FOCUS_OUT, x11Event);
263 }
264
265 void ExposeEventHandler(const XEvent* xevent)
266 {
267   WindowSystemX::X11ExposeEvent x11ExposeEvent;
268   x11ExposeEvent.window = xevent->xclient.window;
269   x11ExposeEvent.event  = xevent;
270   x11ExposeEvent.x      = xevent->xexpose.x;
271   x11ExposeEvent.y      = xevent->xexpose.y;
272   x11ExposeEvent.width  = xevent->xexpose.width;
273   x11ExposeEvent.height = xevent->xexpose.height;
274   GetImplementation().TriggerEventHandler(WindowSystemBase::Event::DAMAGE, x11ExposeEvent);
275 }
276
277 void HandlePointerMove(int x, int y, unsigned long timestamp, ::Window window)
278 {
279   WindowSystemX::X11MouseEvent mouseEvent;
280   mouseEvent.window         = window;
281   mouseEvent.timestamp      = timestamp;
282   mouseEvent.x              = x;
283   mouseEvent.y              = y;
284   mouseEvent.buttons        = 0;
285   mouseEvent.device         = 0;
286   mouseEvent.multi.pressure = 1.0f;
287   mouseEvent.multi.angle    = 0.0f;
288   mouseEvent.multi.radius   = 1;
289   mouseEvent.multi.radiusX  = 1;
290   mouseEvent.multi.radiusY  = 1;
291   GetImplementation().TriggerEventHandler(WindowSystemBase::Event::MOUSE_MOVE, mouseEvent);
292 }
293
294 void ConvertButtonEvent(const XEvent* xevent, WindowSystemX::X11MouseEvent& mouseEvent)
295 {
296   mouseEvent.window         = xevent->xbutton.subwindow ? xevent->xbutton.subwindow : xevent->xbutton.window;
297   mouseEvent.timestamp      = xevent->xbutton.time;
298   mouseEvent.x              = xevent->xbutton.x;
299   mouseEvent.y              = xevent->xbutton.y;
300   mouseEvent.buttons        = xevent->xbutton.button;
301   mouseEvent.device         = 0;
302   mouseEvent.multi.pressure = 1.0f;
303   mouseEvent.multi.angle    = 0.0f;
304   mouseEvent.multi.radius   = 1;
305   mouseEvent.multi.radiusX  = 1;
306   mouseEvent.multi.radiusY  = 1;
307 }
308
309 void ButtonPressEventHandler(const XEvent* xevent)
310 {
311   if(xevent->xbutton.button < MOUSE_SCROLL_WHEEL_UP || xevent->xbutton.button > MOUSE_SCROLL_WHEEL_RIGHT)
312   {
313     HandlePointerMove(xevent->xbutton.x, xevent->xbutton.y, xevent->xbutton.time, xevent->xbutton.subwindow ? xevent->xbutton.subwindow : xevent->xbutton.window);
314
315     WindowSystemX::X11MouseEvent mouseEvent;
316     ConvertButtonEvent(xevent, mouseEvent);
317     GetImplementation().TriggerEventHandler(WindowSystemBase::Event::MOUSE_BUTTON_DOWN, mouseEvent);
318   }
319   else // Otherwise, it's a mouse wheel event
320   {
321     WindowSystemX::X11MouseWheelEvent mouseWheelEvent;
322     mouseWheelEvent.x         = xevent->xbutton.x;
323     mouseWheelEvent.y         = xevent->xbutton.y;
324     mouseWheelEvent.window    = (xevent->xbutton.subwindow ? xevent->xbutton.subwindow : xevent->xbutton.window);
325     mouseWheelEvent.timestamp = xevent->xbutton.time;
326
327     switch(xevent->xbutton.button)
328     {
329       case MOUSE_SCROLL_WHEEL_UP:
330       {
331         mouseWheelEvent.direction = 0;
332         mouseWheelEvent.z         = -1;
333         break;
334       }
335       case MOUSE_SCROLL_WHEEL_DOWN:
336       {
337         mouseWheelEvent.direction = 0;
338         mouseWheelEvent.z         = 1;
339         break;
340       }
341       case MOUSE_SCROLL_WHEEL_LEFT:
342       {
343         mouseWheelEvent.direction = 1;
344         mouseWheelEvent.z         = -1;
345         break;
346       }
347       case MOUSE_SCROLL_WHEEL_RIGHT:
348       {
349         mouseWheelEvent.direction = 1;
350         mouseWheelEvent.z         = 1;
351         break;
352       }
353     }
354     GetImplementation().TriggerEventHandler(WindowSystemBase::Event::MOUSE_WHEEL, mouseWheelEvent);
355   }
356 }
357
358 void ButtonReleaseEventHandler(const XEvent* xevent)
359 {
360   // Check it's a normal button, not a mouse wheel button
361   if(xevent->xbutton.button < MOUSE_SCROLL_WHEEL_UP || xevent->xbutton.button > MOUSE_SCROLL_WHEEL_RIGHT)
362   {
363     HandlePointerMove(xevent->xbutton.x, xevent->xbutton.y, xevent->xbutton.time, xevent->xbutton.subwindow ? xevent->xbutton.subwindow : xevent->xbutton.window);
364
365     WindowSystemX::X11MouseEvent mouseEvent;
366     ConvertButtonEvent(xevent, mouseEvent);
367     GetImplementation().TriggerEventHandler(WindowSystemBase::Event::MOUSE_BUTTON_UP, mouseEvent);
368   }
369   // ignore wheel release events, they are sent immediately prior to another press event
370 }
371
372 void MotionNotifyEventHandler(const XEvent* xevent)
373 {
374   HandlePointerMove(xevent->xmotion.x, xevent->xmotion.y, xevent->xmotion.time, xevent->xmotion.subwindow ? xevent->xmotion.subwindow : xevent->xmotion.window);
375 }
376
377 void EnterNotifyEventHandler(const XEvent* xevent)
378 {
379   HandlePointerMove(xevent->xcrossing.x, xevent->xcrossing.y, xevent->xcrossing.time, xevent->xcrossing.subwindow ? xevent->xcrossing.subwindow : xevent->xcrossing.window);
380 }
381 void LeaveNotifyEventHandler(const XEvent* xevent)
382 {
383   HandlePointerMove(xevent->xcrossing.x, xevent->xcrossing.y, xevent->xcrossing.time, xevent->xcrossing.subwindow ? xevent->xcrossing.subwindow : xevent->xcrossing.window);
384 }
385
386 void ConvertKeyEvent(const XEvent* xEvent, WindowSystemX::X11KeyEvent& keyEvent)
387 {
388   const XKeyEvent* xKeyEvent = &(xEvent->xkey);
389
390   keyEvent.keyCode = xKeyEvent->keycode;
391
392   KeySym keySymbol = XkbKeycodeToKeysym(xKeyEvent->display, xKeyEvent->keycode, 0, 0);
393   char*  keyname   = XKeysymToString(keySymbol);
394   if(!keyname)
395   {
396     asprintf(&keyname, "Keycode-%i", xKeyEvent->keycode);
397     keyEvent.keyname = keyname;
398     free(keyname);
399   }
400   else
401   {
402     keyEvent.keyname = keyname;
403   }
404
405   keyEvent.timestamp = xKeyEvent->time;
406   keyEvent.modifiers = xKeyEvent->state; // @todo add own modifier list
407   keyEvent.window    = xKeyEvent->window;
408   keyEvent.event     = xEvent;
409
410   KeySym         keySymbol2;
411   XComposeStatus composeStatus;
412   const int      BUFFER_LENGTH{256};
413   char           buffer[BUFFER_LENGTH];
414   int            stringLength = XLookupString(const_cast<XKeyEvent*>(xKeyEvent), buffer, BUFFER_LENGTH, &keySymbol2, &composeStatus);
415
416   const char* key{nullptr};
417   if(keySymbol != keySymbol2)
418   {
419     key = XKeysymToString(keySymbol2);
420   }
421   if(!key)
422   {
423     key = keyEvent.keyname.c_str();
424   }
425   keyEvent.key = key;
426
427   buffer[std::max(0, std::min(BUFFER_LENGTH - 1, stringLength))] = '\0';
428
429   keyEvent.compose = stringLength ? buffer : "";
430 }
431
432 void KeyPressEventHandler(const XEvent* xevent)
433 {
434   WindowSystemX::X11KeyEvent x11KeyEvent;
435   ConvertKeyEvent(xevent, x11KeyEvent);
436   GetImplementation().TriggerEventHandler(WindowSystemBase::Event::KEY_DOWN, x11KeyEvent);
437 }
438
439 void KeyReleaseEventHandler(const XEvent* xevent)
440 {
441   WindowSystemX::X11KeyEvent x11KeyEvent;
442   ConvertKeyEvent(xevent, x11KeyEvent);
443   GetImplementation().TriggerEventHandler(WindowSystemBase::Event::KEY_UP, x11KeyEvent);
444 }
445
446 void SelectionClearEventHandler(const XEvent* xevent)
447 {
448   // NOT IMPLEMENTED
449 }
450
451 void SelectionNotifyEventHandler(const XEvent* xevent)
452 {
453   // NOT IMPLEMENTED
454 }
455
456 struct WindowSystemX::Impl
457 {
458   Impl()
459   {
460     XInitThreads();
461     mDisplay = XOpenDisplay(nullptr); // Note, DisplayConnection now reads this var.
462
463     mXEventMonitor = Dali::Internal::Adaptor::GetSystemFactory()->CreateFileDescriptorMonitor(
464       ConnectionNumber(mDisplay),
465       MakeCallback(this, &WindowSystemX::Impl::XPollCallback),
466       FileDescriptorMonitor::FD_READABLE);
467     // libuv hacks: add FD_WRITABLE.
468
469     InitializeAtoms();
470     SetupEventHandlers();
471     InitializeInput();
472   }
473
474   ~Impl()
475   {
476     // @todo Flush events, delete fd handlers, shutdown other X subsystems
477     XCloseDisplay(mDisplay);
478   }
479
480   ::Display* GetXDisplay()
481   {
482     return mDisplay;
483   }
484
485   void XPollCallback(FileDescriptorMonitor::EventType eventType)
486   {
487     if(eventType & (FileDescriptorMonitor::FD_READABLE | FileDescriptorMonitor::FD_WRITABLE))
488     {
489       while(XPending(mDisplay))
490       {
491         XEvent event;
492         XNextEvent(mDisplay, &event);
493         HandleXEvent(event);
494       }
495     }
496   }
497
498   void InitializeAtoms()
499   {
500     std::vector<Atom>        atoms;
501     std::vector<const char*> names;
502     atoms.resize(NUMBER_OF_ATOMS);
503     names.resize(NUMBER_OF_ATOMS);
504     for(unsigned int i = 0; i < NUMBER_OF_ATOMS; ++i)
505     {
506       names[i] = atomItems[i].name.c_str();
507     }
508     Status status = XInternAtoms(mDisplay, const_cast<char**>(&names[0]), NUMBER_OF_ATOMS, false, &atoms[0]);
509     if(status != 0)
510     {
511       for(unsigned int i = 0; i < NUMBER_OF_ATOMS; ++i)
512       {
513         *(atomItems[i].atom) = atoms[i];
514       }
515     }
516   }
517
518   void SetupEventHandlers()
519   {
520     mXEventHandlers[PropertyNotify]  = &PropertyNotifyEventHandler;
521     mXEventHandlers[ClientMessage]   = &ClientMessageEventHandler;
522     mXEventHandlers[FocusIn]         = &FocusInEventHandler;
523     mXEventHandlers[FocusOut]        = &FocusOutEventHandler;
524     mXEventHandlers[Expose]          = &ExposeEventHandler;
525     mXEventHandlers[ButtonPress]     = &ButtonPressEventHandler;
526     mXEventHandlers[ButtonRelease]   = &ButtonReleaseEventHandler;
527     mXEventHandlers[MotionNotify]    = &MotionNotifyEventHandler;
528     mXEventHandlers[EnterNotify]     = &EnterNotifyEventHandler;
529     mXEventHandlers[LeaveNotify]     = &LeaveNotifyEventHandler;
530     mXEventHandlers[KeyPress]        = &KeyPressEventHandler;
531     mXEventHandlers[KeyRelease]      = &KeyReleaseEventHandler;
532     mXEventHandlers[SelectionClear]  = &SelectionClearEventHandler;
533     mXEventHandlers[SelectionNotify] = &SelectionNotifyEventHandler;
534     mXEventHandlers[ConfigureNotify] = &ConfigureNotifyEventHandler;
535   }
536
537   void InitializeInput()
538   {
539     int event;
540     int error;
541     if(XQueryExtension(mDisplay, "XInputExtension", &mXi2OpCode, &event, &error))
542     {
543       // Extension is present
544       int majorVersion = XI_2_Major;
545       int minorVersion = XI_2_Minor;
546
547       Status status = XIQueryVersion(mDisplay, &majorVersion, &minorVersion);
548       if(status == Success)
549       {
550         // @todo May need to enable DeviceChanged, HierarchyChanged and PropertyEvent masks.
551         mXi2Devices = XIQueryDevice(mDisplay, XIAllDevices, &mXi2NumberOfDevices);
552       }
553     }
554   }
555
556   void ShutdownInput()
557   {
558     // @todo Clear any events set in InitializeInput()
559     if(mXi2Devices)
560     {
561       XIFreeDeviceInfo(mXi2Devices);
562       mXi2Devices = nullptr;
563     }
564     mXi2NumberOfDevices = 0;
565     mXi2OpCode          = -1;
566   }
567
568   void InputMultiSelect(::Window window)
569   {
570     // NOT IMPLEMENTED. See ecore_x_input_multi_select(mEcoreWindow);
571   }
572   void EnableDragAndDrop(::Window window, bool enable)
573   {
574     // NOT IMPLEMENTED. See ecore_x_dnd_aware_set(mEcoreWindow, enable);
575   }
576
577   // Call the internal X11 event handler. This will call TriggerEventHandler which
578   // will call each registered handler's callback.
579   void HandleXEvent(const XEvent& event)
580   {
581     auto iter = mXEventHandlers.find(event.type);
582     if(iter != mXEventHandlers.end())
583     {
584       iter->second(&event);
585     }
586   }
587
588   WindowSystemBase::EventHandler* AddEventHandler(WindowSystemBase::Event                event,
589                                                   WindowSystemBase::EventHandlerCallback callback,
590                                                   void*                                  data)
591   {
592     mHandlers.emplace_back(EventHandler{callback, data, event, ++mNextHandlerId});
593     return &mHandlers.back();
594   }
595
596   void DeleteEventHandler(WindowSystemBase::EventHandler* eventHandler)
597   {
598     int  id   = eventHandler->handlerId;
599     auto iter = std::find_if(mHandlers.begin(), mHandlers.end(), [id](const WindowSystemBase::EventHandler& eventHandler) { return eventHandler.handlerId == id; });
600     if(iter != mHandlers.end())
601     {
602       mHandlers.erase(iter);
603     }
604   }
605
606   void TriggerEventHandler(WindowSystemBase::Event eventType, WindowSystemX::X11Event& x11Event)
607   {
608     //@todo make this much more efficient!
609     for(auto& element : mHandlers)
610     {
611       if(element.event == eventType)
612       {
613         bool stop = element.callback(element.data, eventType, &x11Event);
614         if(stop)
615         {
616           break;
617         }
618       }
619     }
620   }
621
622   void Move(::Window window, int x, int y)
623   {
624     XMoveWindow(mDisplay, window, x, y);
625   }
626   void Resize(::Window window, int width, int height)
627   {
628     XResizeWindow(mDisplay, window, std::max(1, width), std::max(1, height));
629   }
630
631   void MoveResize(::Window window, int x, int y, int width, int height)
632   {
633     XMoveResizeWindow(mDisplay, window, x, y, std::max(1, width), std::max(1, height));
634   }
635
636   void SetStringProperty(::Window window, Atom atom, const std::string& string)
637   {
638     XChangeProperty(mDisplay, window, atom, WindowSystemX::ATOM_UTF8_STRING, 8, PropModeReplace, (unsigned char*)string.c_str(), string.length());
639   }
640
641   void SetClass(::Window window, const std::string& name, const std::string& className)
642   {
643     char*         list[] = {strdup(name.c_str())};
644     XTextProperty textProperty;
645     if(Xutf8TextListToTextProperty(mDisplay, list, 1, XUTF8StringStyle, &textProperty) >= Success)
646     {
647       XSetWMName(mDisplay, window, &textProperty);
648       if(textProperty.value)
649       {
650         XFree(textProperty.value);
651       }
652     }
653
654     SetStringProperty(window, WindowSystemX::ATOM_NET_WM_NAME, name);
655
656     XClassHint* classHint = XAllocClassHint();
657     classHint->res_name   = list[0];
658     classHint->res_class  = strdup(className.c_str());
659     XSetClassHint(mDisplay, window, classHint);
660     free(classHint->res_class);
661     XFree(classHint);
662     free(list[0]);
663   }
664
665   ::Display* mDisplay;
666   int        mNextHandlerId{0};
667   using EventHandlerFunctionPointer = void (*)(const XEvent*);
668   std::unordered_map<int, EventHandlerFunctionPointer> mXEventHandlers;
669   std::vector<WindowSystemBase::EventHandler>          mHandlers;
670   std::unique_ptr<FileDescriptorMonitor>               mXEventMonitor;
671   XIDeviceInfo*                                        mXi2Devices{nullptr};
672   int                                                  mXi2NumberOfDevices{0};
673   int                                                  mXi2OpCode{-1};
674 };
675
676 WindowSystemX::WindowSystemX()
677 {
678   mImpl = new Impl();
679 }
680
681 WindowSystemX::~WindowSystemX()
682 {
683   delete mImpl;
684 }
685
686 Dali::Any WindowSystemX::GetDisplay()
687 {
688   return Dali::Any(mImpl->mDisplay);
689 }
690
691 ::Display* WindowSystemX::GetXDisplay()
692 {
693   return mImpl->mDisplay;
694 }
695
696 void WindowSystemX::Sync()
697 {
698   XSync(mImpl->mDisplay, false);
699 }
700
701 void WindowSystemX::GetScreenSize(int& width, int& height)
702 {
703   ::Screen* screen = DefaultScreenOfDisplay(mImpl->mDisplay);
704   if(screen != nullptr)
705   {
706     width  = screen->width;
707     height = screen->height;
708   }
709 }
710
711 WindowSystemBase::EventHandler* WindowSystemX::AddEventHandler(Event event, EventHandlerCallback callback, void* data)
712 {
713   return mImpl->AddEventHandler(event, callback, data);
714 }
715
716 void WindowSystemX::DeleteEventHandler(WindowSystemBase::EventHandler* eventHandler)
717 {
718   mImpl->DeleteEventHandler(eventHandler);
719 }
720
721 ::Window WindowSystemX::CreateWindow(int depth, int x, int y, int width, int height)
722 {
723   ::Window window;
724   ::Window parent = DefaultRootWindow(mImpl->mDisplay);
725
726   XSetWindowAttributes attributes;
727   attributes.background_pixmap     = None;             /* background, None, or ParentRelative */
728   attributes.border_pixel          = 0;                /* border pixel value */
729   attributes.bit_gravity           = NorthWestGravity; /* one of bit gravity values */
730   attributes.win_gravity           = NorthWestGravity; /* one of the window gravity values */
731   attributes.backing_store         = NotUseful;        /* NotUseful, WhenMapped, Always */
732   attributes.save_under            = false;            /* should bits under be saved? (popups) */
733   attributes.event_mask            = (KeyPressMask |   /* set of events that should be saved */
734                            KeyReleaseMask |
735                            ButtonPressMask |
736                            ButtonReleaseMask |
737                            EnterWindowMask |
738                            LeaveWindowMask |
739                            PointerMotionMask |
740                            StructureNotifyMask |
741                            ExposureMask |
742                            VisibilityChangeMask |
743                            StructureNotifyMask |
744                            FocusChangeMask |
745                            PropertyChangeMask |
746                            ColormapChangeMask);
747   attributes.do_not_propagate_mask = NoEventMask; /* set of events that should not propagate */
748   attributes.override_redirect     = false;       /* boolean value for override_redirect */
749   attributes.cursor                = None;        /* cursor to be displayed (or None) */
750
751   if(depth == 4)
752   {
753     Visual* visual{nullptr};
754
755     XVisualInfo visualInfoRequest;
756     visualInfoRequest.screen  = DefaultScreen(mImpl->mDisplay);
757     visualInfoRequest.depth   = 32;
758     visualInfoRequest.c_class = TrueColor;
759     int          visualInfoCount;
760     XVisualInfo* visualInfoList = XGetVisualInfo(mImpl->mDisplay,
761                                                  (VisualScreenMask | VisualDepthMask | VisualClassMask),
762                                                  &visualInfoRequest,
763                                                  &visualInfoCount);
764     for(int i = 0; i < visualInfoCount; ++i)
765     {
766       XRenderPictFormat* pictFormat = XRenderFindVisualFormat(mImpl->mDisplay, visualInfoList[i].visual);
767       Sync();
768       if(pictFormat->type == PictTypeDirect && pictFormat->direct.alphaMask)
769       {
770         visual = visualInfoList[i].visual;
771         break;
772       }
773     }
774     XFree(visualInfoList);
775
776     attributes.colormap = XCreateColormap(mImpl->mDisplay, parent, visual, AllocNone); /* color map to be associated with window */
777
778     window = XCreateWindow(mImpl->mDisplay, parent, x, y, width, height, 0, 32, InputOutput, visual, (CWBackingStore | CWOverrideRedirect | CWColormap | CWBorderPixel | CWBackPixmap | CWSaveUnder | CWDontPropagate | CWEventMask | CWBitGravity | CWWinGravity), &attributes);
779   }
780   else
781   {
782     window = XCreateWindow(mImpl->mDisplay, parent, x, y, width, height, 0, CopyFromParent, InputOutput, CopyFromParent, (CWBackingStore | CWOverrideRedirect | CWBorderPixel | CWBackPixmap | CWSaveUnder | CWDontPropagate | CWEventMask | CWBitGravity | CWWinGravity), &attributes);
783   }
784
785   Sync();
786   SetWindowDefaults(window);
787   return window;
788 }
789
790 void WindowSystemX::SetWindowDefaults(::Window window)
791 {
792   char hostnameBuffer[HOST_NAME_MAX + 1];
793   gethostname(hostnameBuffer, HOST_NAME_MAX);
794   hostnameBuffer[HOST_NAME_MAX] = '\0';
795   char* hostname[1];
796   hostname[0] = hostnameBuffer;
797
798   XTextProperty xTextProperty;
799
800   if(XStringListToTextProperty(hostname, 1, &xTextProperty))
801   {
802     XSetWMClientMachine(mImpl->mDisplay, window, &xTextProperty);
803     XFree(xTextProperty.value);
804   }
805   Sync();
806   long pid = getpid();
807   XChangeProperty(mImpl->mDisplay, window, ATOM_NET_WM_PID, XA_CARDINAL, 32, PropModeReplace, (uint8_t*)&pid, 1);
808   Sync();
809
810   Atom atom = ATOM_NET_WM_WINDOW_TYPE_NORMAL;
811   XChangeProperty(mImpl->mDisplay, window, ATOM_NET_WM_WINDOW_TYPE, XA_ATOM, 32, PropModeReplace, (uint8_t*)&atom, 1);
812
813   //ecore_app_args_get(&argc, &argv);
814   //ecore_x_icccm_command_set(win, argc, argv);
815 }
816
817 void WindowSystemX::SetTransientForHint(::Window window, ::Window forWindow)
818 {
819   XSetTransientForHint(mImpl->mDisplay, window, forWindow);
820   Sync();
821 }
822
823 void WindowSystemX::UnsetTransientFor(::Window window)
824 {
825   XDeleteProperty(mImpl->mDisplay, window, WindowSystemX::ATOM_WM_TRANSIENT_FOR);
826   Sync();
827 }
828
829 void WindowSystemX::SetProtocol(::Window window, Atom protocol, bool value)
830 {
831   Atom* protocols{nullptr};
832   int   protocolsCount = 0;
833
834   Status status = XGetWMProtocols(mImpl->mDisplay, window, &protocols, &protocolsCount);
835
836   if(status > 0)
837   {
838     XSync(mImpl->mDisplay, false);
839
840     bool found = false;
841     int  index = 0;
842     for(; index < protocolsCount; ++index)
843     {
844       if(protocols[index] == protocol)
845       {
846         found = true;
847         break;
848       }
849     }
850     if(value && !found)
851     {
852       std::vector<Atom> newProtocols;
853       newProtocols.resize(protocolsCount + 1);
854
855       for(int i = 0; i < protocolsCount; ++i)
856       {
857         newProtocols[i] = protocols[i];
858       }
859       newProtocols.back() = protocol;
860
861       XSetWMProtocols(mImpl->mDisplay, window, &newProtocols[0], newProtocols.size());
862       Sync();
863     }
864     else if(!value && found)
865     {
866       // Remove the protocol
867       --protocolsCount;
868       if(protocolsCount > 0)
869       {
870         for(int i = index; i < protocolsCount; ++i)
871         {
872           protocols[i] = protocols[i + 1];
873         }
874         XSetWMProtocols(mImpl->mDisplay, window, protocols, protocolsCount);
875       }
876       else
877       {
878         XDeleteProperty(mImpl->mDisplay, window, WindowSystemX::ATOM_WM_PROTOCOLS);
879       }
880     }
881     XFree(protocols);
882   }
883 }
884
885 void WindowSystemX::SetWindowHints(::Window window, bool acceptsFocus)
886 {
887   XWMHints* hints = XAllocWMHints();
888   if(hints != nullptr)
889   {
890     hints->flags         = InputHint | StateHint;
891     hints->input         = acceptsFocus;
892     hints->initial_state = NormalState;
893     XSetWMHints(mImpl->mDisplay, window, hints);
894     XFree(hints);
895   }
896 }
897
898 WindowSystemX::WindowState WindowSystemX::GetWindowState(::Window window)
899 {
900   std::vector<unsigned long> hints;
901   if(GetWindowProperty<unsigned long>(mImpl->mDisplay, window, WindowSystemX::ATOM_WM_STATE, WindowSystemX::ATOM_WM_STATE, hints))
902   {
903     if(hints.size() == 2)
904     {
905       switch(hints[0])
906       {
907         case WithdrawnState:
908         {
909           return WindowSystemX::WindowState::WITHDRAWN;
910         }
911         case NormalState:
912         {
913           return WindowSystemX::WindowState::NORMAL;
914         }
915         case IconicState:
916         {
917           return WindowSystemX::WindowState::ICONIC;
918         }
919       }
920     }
921   }
922   return WindowSystemX::WindowState::NORMAL;
923 }
924
925 void WindowSystemX::Show(::Window window)
926 {
927   XMapWindow(mImpl->mDisplay, window);
928   Sync();
929 }
930
931 void WindowSystemX::Hide(::Window window)
932 {
933   ::Window     rootWindow = window;
934   int          x, y;
935   unsigned int width, height, border, depth;
936   if(ScreenCount(mImpl->mDisplay) == 1)
937   {
938     rootWindow = DefaultRootWindow(mImpl->mDisplay);
939   }
940   else
941   {
942     // Need to get the root window in a different way
943     XGetGeometry(mImpl->mDisplay, window, &rootWindow, &x, &y, &width, &height, &border, &depth);
944   }
945   XUnmapWindow(mImpl->mDisplay, window);
946   XEvent event;
947   event.xunmap.type           = UnmapNotify;
948   event.xunmap.serial         = 0;
949   event.xunmap.send_event     = True;
950   event.xunmap.display        = mImpl->mDisplay;
951   event.xunmap.window         = window;
952   event.xunmap.from_configure = False;
953
954   XSendEvent(mImpl->mDisplay, rootWindow, False, SubstructureRedirectMask | SubstructureNotifyMask, &event);
955   Sync();
956 }
957
958 void WindowSystemX::Activate(::Window window)
959 {
960   XWindowAttributes attributes;
961   Status            status = XGetWindowAttributes(mImpl->mDisplay, window, &attributes);
962   ::Window          root   = (status > 0) ? attributes.root : DefaultRootWindow(mImpl->mDisplay);
963
964   XEvent event;
965   event.xclient.type         = ClientMessage;
966   event.xclient.display      = mImpl->mDisplay;
967   event.xclient.window       = window;
968   event.xclient.message_type = WindowSystemX::ATOM_NET_ACTIVE_WINDOW;
969   event.xclient.format       = 32;
970   event.xclient.data.l[0]    = 1;
971   event.xclient.data.l[1]    = CurrentTime;
972   event.xclient.data.l[2]    = 0;
973   event.xclient.data.l[3]    = 0;
974   event.xclient.data.l[4]    = 0;
975   XSendEvent(mImpl->mDisplay, root, False, SubstructureRedirectMask | SubstructureNotifyMask, &event);
976 }
977
978 void WindowSystemX::Raise(::Window window)
979 {
980   XRaiseWindow(mImpl->mDisplay, window);
981   Sync();
982 }
983
984 void WindowSystemX::Lower(::Window window)
985 {
986   XLowerWindow(mImpl->mDisplay, window);
987   Sync();
988 }
989
990 void WindowSystemX::TriggerEventHandler(WindowSystemBase::Event eventType, X11Event& event)
991 {
992   mImpl->TriggerEventHandler(eventType, event);
993 }
994
995 void WindowSystemX::GetDPI(unsigned int& dpiHorizontal, unsigned int& dpiVertical)
996 {
997   Screen* screen = DefaultScreenOfDisplay(mImpl->mDisplay);
998   if(screen->mwidth <= 0)
999   {
1000     dpiHorizontal = dpiVertical = 75;
1001   }
1002   else
1003   {
1004     dpiHorizontal = dpiVertical = (((screen->width * 254) / screen->mwidth) + 5) / 10;
1005   }
1006 }
1007
1008 void WindowSystemX::Move(::Window window, int x, int y)
1009 {
1010   mImpl->Move(window, x, y);
1011   Sync();
1012 }
1013
1014 void WindowSystemX::Resize(::Window window, int width, int height)
1015 {
1016   mImpl->Resize(window, width, height);
1017   Sync();
1018 }
1019
1020 void WindowSystemX::MoveResize(::Window window, int x, int y, int width, int height)
1021 {
1022   mImpl->MoveResize(window, x, y, width, height);
1023   Sync();
1024 }
1025
1026 void WindowSystemX::SetStringProperty(::Window window, Atom atom, const std::string& string)
1027 {
1028   mImpl->SetStringProperty(window, atom, string);
1029   Sync();
1030 }
1031
1032 void WindowSystemX::SetClass(::Window window, const std::string& name, const std::string& className)
1033 {
1034   mImpl->SetClass(window, name, className);
1035   Sync();
1036 }
1037
1038 void WindowSystemX::InputMultiSelect(::Window window)
1039 {
1040   mImpl->InputMultiSelect(window);
1041   Sync();
1042 }
1043
1044 void WindowSystemX::EnableDragAndDrop(::Window window, bool enable)
1045 {
1046   mImpl->EnableDragAndDrop(window, enable);
1047   Sync();
1048 }
1049
1050 WindowSystemX& GetImplementation()
1051 {
1052   if(nullptr != gWindowSystem)
1053   {
1054     Initialize();
1055   }
1056   return *gWindowSystem;
1057 }
1058
1059 void GetScreenSize(int& width, int& height)
1060 {
1061   if(gWindowSystem != nullptr)
1062   {
1063     gWindowSystem->GetScreenSize(width, height);
1064   }
1065 }
1066
1067 void UpdateScreenSize()
1068 {
1069 }
1070
1071 bool SetKeyboardRepeatInfo(float rate, float delay)
1072 {
1073   return false;
1074 }
1075
1076 bool GetKeyboardRepeatInfo(float& rate, float& delay)
1077 {
1078   return false;
1079 }
1080
1081 bool SetKeyboardHorizontalRepeatInfo(float rate, float delay)
1082 {
1083   return false;
1084 }
1085
1086 bool GetKeyboardHorizontalRepeatInfo(float& rate, float& delay)
1087 {
1088   return false;
1089 }
1090
1091 bool SetKeyboardVerticalRepeatInfo(float rate, float delay)
1092 {
1093   return false;
1094 }
1095
1096 bool GetKeyboardVerticalRepeatInfo(float& rate, float& delay)
1097 {
1098   return false;
1099 }
1100
1101 void SetGeometryHittestEnabled(bool enable)
1102 {
1103   DALI_LOG_RELEASE_INFO("GeometryHittest : %d \n", enable);
1104   gGeometryHittest = enable;
1105   if(gGeometryHittest)
1106   {
1107     Dali::SceneHolderList sceneHolders = Dali::Adaptor::Get().GetSceneHolders();
1108     for(auto iter = sceneHolders.begin(); iter != sceneHolders.end(); ++iter)
1109     {
1110       if(*iter)
1111       {
1112         (*iter).SetGeometryHittestEnabled(enable);
1113       }
1114     }
1115   }
1116 }
1117
1118 bool IsGeometryHittestEnabled()
1119 {
1120   return gGeometryHittest;
1121 }
1122
1123 } // namespace WindowSystem
1124
1125 } // namespace Adaptor
1126
1127 } // namespace Internal
1128
1129 } // namespace Dali