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