]> git.tdb.fi Git - libs/gui.git/blob - source/graphics/x11/window.cpp
Update .gitignore to include build products on Windows
[libs/gui.git] / source / graphics / x11 / window.cpp
1 #include "window.h"
2 #include "window_private.h"
3 #include <vector>
4 #include <X11/Xatom.h>
5 #include <X11/Xutil.h>
6 #include <msp/core/systemerror.h>
7 #include "display_private.h"
8
9 using namespace std;
10
11 namespace {
12
13 Bool match_event_type(Display *, XEvent *event, XPointer arg)
14 {
15         return event->type==*reinterpret_cast<int *>(arg);
16 }
17
18 }
19
20 namespace Msp {
21 namespace Graphics {
22
23 void Window::platform_init()
24 {
25         DisplayHandle dpy = display.get_private().display;
26
27         priv->wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", true);
28
29         XSetWindowAttributes attr;
30         attr.override_redirect = options.fullscreen;
31         attr.event_mask = ButtonPressMask|ButtonReleaseMask|PointerMotionMask|KeyPressMask|KeyReleaseMask|StructureNotifyMask|EnterWindowMask|ExposureMask|FocusChangeMask;
32
33         // User position is set when the window is mapped
34         int x = 0;
35         int y = 0;
36         if(options.fullscreen && !options.fullscreen_exclusive)
37         {
38                 const Monitor::Settings &ms = options.fullscreen_monitor->current_settings;
39                 x = ms.x;
40                 y = ms.y;
41         }
42
43         priv->window = XCreateWindow(dpy,
44                 display.get_private().root_window,
45                 x, y,
46                 options.width, options.height,
47                 0,
48                 CopyFromParent,
49                 InputOutput,
50                 CopyFromParent,
51                 CWOverrideRedirect|CWEventMask, &attr);
52
53         XSetWMProtocols(dpy, priv->window, &priv->wm_delete_window, 1);
54
55         if(!options.resizable)
56         {
57                 XSizeHints hints;
58                 hints.flags = PMinSize|PMaxSize;
59                 hints.min_width = hints.max_width = options.width;
60                 hints.min_height = hints.max_height = options.height;
61                 XSetWMNormalHints(dpy, priv->window, &hints);
62         }
63 }
64
65 void Window::platform_cleanup()
66 {
67         if(priv->window)
68                 XDestroyWindow(display.get_private().display, priv->window);
69
70         if(priv->invisible_cursor)
71                 XFreeCursor(display.get_private().display, priv->invisible_cursor);
72 }
73
74 void Window::set_title(const string &title)
75 {
76         vector<unsigned char> buf(title.begin(), title.end());
77         XTextProperty prop;
78         prop.value = &buf[0];
79         prop.encoding = XA_STRING;
80         prop.format = 8;
81         prop.nitems = title.size();
82         XSetWMName(display.get_private().display, priv->window, &prop);
83         display.check_error();
84 }
85
86 void Window::platform_reconfigure(bool fullscreen_changed)
87 {
88         DisplayHandle dpy = display.get_private().display;
89
90         bool was_visible = visible;
91         if(fullscreen_changed)
92         {
93                 if(was_visible)
94                 {
95                         hide();
96
97                         // Wait for the window to be unmapped.  This makes window managers happy.
98                         XEvent ev;
99                         int ev_type = UnmapNotify;
100                         XPeekIfEvent(dpy, &ev, match_event_type, reinterpret_cast<char *>(&ev_type));
101                 }
102
103                 XSetWindowAttributes attr;
104                 attr.override_redirect = options.fullscreen;
105                 XChangeWindowAttributes(dpy, priv->window, CWOverrideRedirect, &attr);
106         }
107
108         XSizeHints hints;
109         if(options.resizable)
110                 hints.flags = 0;
111         else
112         {
113                 hints.flags = PMinSize|PMaxSize;
114                 hints.min_width = hints.max_width = options.width;
115                 hints.min_height = hints.max_height = options.height;
116         }
117         XSetWMNormalHints(dpy, priv->window, &hints);
118
119         if(options.fullscreen)
120         {
121                 if(options.fullscreen_exclusive)
122                         XMoveResizeWindow(dpy, priv->window, 0, 0, options.width, options.height);
123                 else
124                 {
125                         const Monitor::Settings &ms = options.fullscreen_monitor->current_settings;
126                         XMoveResizeWindow(dpy, priv->window, ms.x, ms.y, options.width, options.height);
127                 }
128         }
129         else if(options.user_position)
130                 XMoveResizeWindow(dpy, priv->window, options.x, options.y, options.width, options.height);
131         else
132                 XResizeWindow(dpy, priv->window, options.width, options.height);
133
134         if(fullscreen_changed && was_visible)
135                 show();
136 }
137
138 void Window::show_cursor(bool s)
139 {
140         DisplayHandle dpy = display.get_private().display;
141
142         if(s)
143                 XUndefineCursor(dpy, priv->window);
144         else
145         {
146                 if(!priv->invisible_cursor)
147                 {
148                         int screen = DefaultScreen(dpy);
149
150                         Pixmap pm = XCreatePixmap(dpy, priv->window, 1, 1, 1);
151                         GC gc = XCreateGC(dpy, pm, 0, nullptr);
152                         XSetFunction(dpy, gc, GXclear);
153                         XDrawPoint(dpy, pm, gc, 0, 0);
154                         XFreeGC(dpy, gc);
155
156                         XColor black;
157                         black.pixel = BlackPixel(dpy, screen);
158                         XQueryColor(dpy, DefaultColormap(dpy, screen), &black);
159
160                         priv->invisible_cursor = XCreatePixmapCursor(dpy, pm, pm, &black, &black, 0, 0);
161
162                         XFreePixmap(dpy, pm);
163                 }
164                 XDefineCursor(dpy, priv->window, priv->invisible_cursor);
165         }
166 }
167
168 void Window::warp_pointer(int x, int y)
169 {
170         XWarpPointer(display.get_private().display, None, priv->window, 0, 0, 0, 0, x, y);
171 }
172
173 void Window::platform_set_touch_input()
174 {
175 }
176
177 void Window::platform_show()
178 {
179         DisplayHandle dpy = display.get_private().display;
180         XMapRaised(dpy, priv->window);
181         if(options.user_position && !options.fullscreen)
182                 XMoveWindow(dpy, priv->window, options.x, options.y);
183 }
184
185 void Window::platform_hide()
186 {
187         XUnmapWindow(display.get_private().display, priv->window);
188 }
189
190 bool Window::event(const Event &evnt)
191 {
192         const XEvent &ev = evnt.xevent;
193         switch(ev.type)
194         {
195         case ButtonPress:
196         case ButtonRelease:
197         case MotionNotify:
198         case KeyPress:
199         case KeyRelease:
200                 signal_input_event.emit(evnt);
201                 break;
202         case ConfigureNotify:
203                 if((ev.xconfigure.width==static_cast<int>(options.width) && ev.xconfigure.height==static_cast<int>(options.height)) == resizing)
204                 {
205                         options.width = ev.xconfigure.width;
206                         options.height = ev.xconfigure.height;
207                         resizing = false;
208                         signal_resize.emit(options.width, options.height);
209                 }
210
211                 {
212                         int x = ev.xconfigure.x;
213                         int y = ev.xconfigure.y;
214
215                         if(priv->reparented)
216                         {
217                                 if(!ev.xconfigure.send_event)
218                                 {
219                                         /* If the window manager reparented us, the coordinates of a
220                                         real event are in the parent window's space. */
221                                         priv->rel_x = ev.xconfigure.x;
222                                         priv->rel_y = ev.xconfigure.y;
223
224                                         const Display::Private &dpy_priv = display.get_private();
225                                         WindowHandle dummy;
226                                         XTranslateCoordinates(dpy_priv.display, priv->window, dpy_priv.root_window, 0, 0, &x, &y, &dummy);
227                                 }
228
229                                 // Use the coordinates of the window manager frame
230                                 x -= priv->rel_x;
231                                 y -= priv->rel_y;
232                         }
233
234                         if((x==options.x && y==options.y) == moving)
235                         {
236                                 options.x = x;
237                                 options.y = y;
238                                 moving = false;
239                                 signal_move.emit(options.x, options.y);
240                         }
241                 }
242
243                 break;
244         case ReparentNotify:
245                 priv->reparented = (ev.xreparent.parent!=display.get_private().root_window);
246                 if(!priv->reparented)
247                 {
248                         priv->rel_x = 0;
249                         priv->rel_y = 0;
250                 }
251                 break;
252         case ClientMessage:
253                 if(ev.xclient.data.l[0]==static_cast<long>(priv->wm_delete_window))
254                         signal_close.emit();
255                 break;
256         case EnterNotify:
257                 if(options.fullscreen)
258                         XSetInputFocus(display.get_private().display, priv->window, RevertToParent, CurrentTime);
259                 break;
260         case MapNotify:
261                 if(options.fullscreen && options.fullscreen_exclusive)
262                         XGrabPointer(display.get_private().display, priv->window, true, None, GrabModeAsync, GrabModeAsync, priv->window, None, CurrentTime);
263                 break;
264         case Expose:
265                 signal_expose.emit(ev.xexpose.x, ev.xexpose.y, ev.xexpose.width, ev.xexpose.height, evnt);
266                 break;
267         case FocusIn:
268                 signal_got_focus.emit();
269                 break;
270         case FocusOut:
271                 signal_lost_focus.emit();
272                 break;
273         default:
274                 return false;
275         }
276
277         return true;
278 }
279
280 } // namespace Graphics
281 } // namespace Msp