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