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