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