Martin Ettl <ettl DOT martin AT gmx DOT de>: Fix various resource and memory leaks
[reactos.git] / reactos / base / applications / tsclient / rdesktop / ewmhints.c
1 /* -*- c-basic-offset: 8 -*-
2 rdesktop: A Remote Desktop Protocol client.
3
4 Support functions for Extended Window Manager Hints,
5 http://www.freedesktop.org/wiki/Standards_2fwm_2dspec
6
7 Copyright (C) Peter Astrand <astrand@cendio.se> 2005
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License along
20 with this program; if not, write to the Free Software Foundation, Inc.,
21 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 */
23
24 #include <X11/Xlib.h>
25 #include <X11/Xatom.h>
26 #include <X11/Xutil.h>
27 #include "rdesktop.h"
28
29 #define _NET_WM_STATE_REMOVE 0 /* remove/unset property */
30 #define _NET_WM_STATE_ADD 1 /* add/set property */
31 #define _NET_WM_STATE_TOGGLE 2 /* toggle property */
32
33 /*
34 Get window property value (32 bit format)
35 Returns zero on success, -1 on error
36 */
37 static int
38 get_property_value(RDPCLIENT * This, Window wnd, char *propname, long max_length,
39 unsigned long *nitems_return, unsigned char **prop_return, int nowarn)
40 {
41 int result;
42 Atom property;
43 Atom actual_type_return;
44 int actual_format_return;
45 unsigned long bytes_after_return;
46
47 property = XInternAtom(This->display, propname, True);
48 if (property == None)
49 {
50 fprintf(stderr, "Atom %s does not exist\n", propname);
51 return (-1);
52 }
53
54 result = XGetWindowProperty(This->display, wnd, property, 0, /* long_offset */
55 max_length, /* long_length */
56 False, /* delete */
57 AnyPropertyType, /* req_type */
58 &actual_type_return,
59 &actual_format_return,
60 nitems_return, &bytes_after_return, prop_return);
61
62 if (result != Success)
63 {
64 fprintf(stderr, "XGetWindowProperty failed\n");
65 return (-1);
66 }
67
68 if (actual_type_return == None || actual_format_return == 0)
69 {
70 if (!nowarn)
71 fprintf(stderr, "Window is missing property %s\n", propname);
72 return (-1);
73 }
74
75 if (bytes_after_return)
76 {
77 fprintf(stderr, "%s is too big for me\n", propname);
78 return (-1);
79 }
80
81 if (actual_format_return != 32)
82 {
83 fprintf(stderr, "%s has bad format\n", propname);
84 return (-1);
85 }
86
87 return (0);
88 }
89
90 /*
91 Get current desktop number
92 Returns -1 on error
93 */
94 static int
95 get_current_desktop(RDPCLIENT * This)
96 {
97 unsigned long nitems_return;
98 unsigned char *prop_return;
99 int current_desktop;
100
101 if (get_property_value
102 (This, DefaultRootWindow(This->display), "_NET_CURRENT_DESKTOP", 1, &nitems_return,
103 &prop_return, 0) < 0)
104 return (-1);
105
106 if (nitems_return != 1)
107 {
108 fprintf(stderr, "_NET_CURRENT_DESKTOP has bad length\n");
109 return (-1);
110 }
111
112 current_desktop = *prop_return;
113 XFree(prop_return);
114 return current_desktop;
115 }
116
117 /*
118 Get workarea geometry
119 Returns zero on success, -1 on error
120 */
121
122 int
123 get_current_workarea(RDPCLIENT * This, uint32 * x, uint32 * y, uint32 * width, uint32 * height)
124 {
125 int current_desktop;
126 unsigned long nitems_return;
127 unsigned char *prop_return;
128 uint32 *return_words;
129 const uint32 net_workarea_x_offset = 0;
130 const uint32 net_workarea_y_offset = 1;
131 const uint32 net_workarea_width_offset = 2;
132 const uint32 net_workarea_height_offset = 3;
133 const uint32 max_prop_length = 32 * 4; /* Max 32 desktops */
134
135 if (get_property_value
136 (This, DefaultRootWindow(This->display), "_NET_WORKAREA", max_prop_length, &nitems_return,
137 &prop_return, 0) < 0)
138 return (-1);
139
140 if (nitems_return % 4)
141 {
142 fprintf(stderr, "_NET_WORKAREA has odd length\n");
143 return (-1);
144 }
145
146 current_desktop = get_current_desktop(This);
147
148 if (current_desktop < 0)
149 return -1;
150
151 return_words = (uint32 *) prop_return;
152
153 *x = return_words[current_desktop * 4 + net_workarea_x_offset];
154 *y = return_words[current_desktop * 4 + net_workarea_y_offset];
155 *width = return_words[current_desktop * 4 + net_workarea_width_offset];
156 *height = return_words[current_desktop * 4 + net_workarea_height_offset];
157
158 XFree(prop_return);
159
160 return (0);
161
162 }
163
164
165
166 void
167 ewmh_init(RDPCLIENT * This)
168 {
169 /* FIXME: Use XInternAtoms */
170 This->ewmhints.state_maximized_vert_atom =
171 XInternAtom(This->display, "_NET_WM_STATE_MAXIMIZED_VERT", False);
172 This->ewmhints.state_maximized_horz_atom =
173 XInternAtom(This->display, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
174 This->ewmhints.state_hidden_atom = XInternAtom(This->display, "_NET_WM_STATE_HIDDEN", False);
175 This->ewmhints.state_skip_taskbar_atom =
176 XInternAtom(This->display, "_NET_WM_STATE_SKIP_TASKBAR", False);
177 This->ewmhints.state_skip_pager_atom = XInternAtom(This->display, "_NET_WM_STATE_SKIP_PAGER", False);
178 This->ewmhints.state_modal_atom = XInternAtom(This->display, "_NET_WM_STATE_MODAL", False);
179 This->net_wm_state_atom = XInternAtom(This->display, "_NET_WM_STATE", False);
180 This->net_wm_desktop_atom = XInternAtom(This->display, "_NET_WM_DESKTOP", False);
181 This->ewmhints.name_atom = XInternAtom(This->display, "_NET_WM_NAME", False);
182 This->ewmhints.utf8_string_atom = XInternAtom(This->display, "UTF8_STRING", False);
183 }
184
185
186 /*
187 Get the window state: normal/minimized/maximized.
188 */
189 #ifndef MAKE_PROTO
190 int
191 ewmh_get_window_state(RDPCLIENT * This, Window w)
192 {
193 unsigned long nitems_return;
194 unsigned char *prop_return;
195 uint32 *return_words;
196 unsigned long item;
197 BOOL maximized_vert, maximized_horz, hidden;
198
199 maximized_vert = maximized_horz = hidden = False;
200
201 if (get_property_value(This, w, "_NET_WM_STATE", 64, &nitems_return, &prop_return, 0) < 0)
202 return SEAMLESSRDP_NORMAL;
203
204 return_words = (uint32 *) prop_return;
205
206 for (item = 0; item < nitems_return; item++)
207 {
208 if (return_words[item] == This->ewmhints.state_maximized_vert_atom)
209 maximized_vert = True;
210 if (return_words[item] == This->ewmhints.state_maximized_horz_atom)
211 maximized_horz = True;
212 if (return_words[item] == This->ewmhints.state_hidden_atom)
213 hidden = True;
214 }
215
216 XFree(prop_return);
217
218 if (maximized_vert && maximized_horz)
219 return SEAMLESSRDP_MAXIMIZED;
220 else if (hidden)
221 return SEAMLESSRDP_MINIMIZED;
222 else
223 return SEAMLESSRDP_NORMAL;
224 }
225
226 static int
227 ewmh_modify_state(RDPCLIENT * This, Window wnd, int add, Atom atom1, Atom atom2)
228 {
229 Status status;
230 XEvent xevent;
231
232 int result;
233 unsigned long nitems;
234 unsigned char *props;
235 uint32 state = WithdrawnState;
236
237 /* The spec states that the window manager must respect any
238 _NET_WM_STATE attributes on a withdrawn window. In order words, we
239 modify the attributes directly for withdrawn windows and ask the WM
240 to do it for active windows. */
241 result = get_property_value(This, wnd, "WM_STATE", 64, &nitems, &props, 1);
242 if ((result >= 0) && nitems)
243 {
244 state = *(uint32 *) props;
245 XFree(props);
246 }
247
248 if (state == WithdrawnState)
249 {
250 if (add)
251 {
252 Atom atoms[2];
253
254 atoms[0] = atom1;
255 nitems = 1;
256 if (atom2)
257 {
258 atoms[1] = atom2;
259 nitems = 2;
260 }
261
262 XChangeProperty(This->display, wnd, This->net_wm_state_atom, XA_ATOM,
263 32, PropModeAppend, (unsigned char *) atoms, nitems);
264 }
265 else
266 {
267 Atom *atoms;
268 int i;
269
270 if (get_property_value(This, wnd, "_NET_WM_STATE", 64, &nitems, &props, 1) < 0)
271 return 0;
272
273 atoms = (Atom *) props;
274
275 for (i = 0; i < nitems; i++)
276 {
277 if ((atoms[i] == atom1) || (atom2 && (atoms[i] == atom2)))
278 {
279 if (i != (nitems - 1))
280 memmove(&atoms[i], &atoms[i + 1],
281 sizeof(Atom) * (nitems - i - 1));
282 nitems--;
283 i--;
284 }
285 }
286
287 XChangeProperty(This->display, wnd, This->net_wm_state_atom, XA_ATOM,
288 32, PropModeReplace, (unsigned char *) atoms, nitems);
289
290 XFree(props);
291 }
292
293 return 0;
294 }
295
296 xevent.type = ClientMessage;
297 xevent.xclient.window = wnd;
298 xevent.xclient.message_type = This->net_wm_state_atom;
299 xevent.xclient.format = 32;
300 if (add)
301 xevent.xclient.data.l[0] = _NET_WM_STATE_ADD;
302 else
303 xevent.xclient.data.l[0] = _NET_WM_STATE_REMOVE;
304 xevent.xclient.data.l[1] = atom1;
305 xevent.xclient.data.l[2] = atom2;
306 xevent.xclient.data.l[3] = 0;
307 xevent.xclient.data.l[4] = 0;
308 status = XSendEvent(This->display, DefaultRootWindow(This->display), False,
309 SubstructureNotifyMask | SubstructureRedirectMask, &xevent);
310 if (!status)
311 return -1;
312
313 return 0;
314 }
315
316 /*
317 Set the window state: normal/minimized/maximized.
318 Returns -1 on failure.
319 */
320 int
321 ewmh_change_state(RDPCLIENT * This, Window wnd, int state)
322 {
323 /*
324 * Deal with the max atoms
325 */
326 if (state == SEAMLESSRDP_MAXIMIZED)
327 {
328 if (ewmh_modify_state
329 (This, wnd, 1, This->ewmhints.state_maximized_vert_atom,
330 This->ewmhints.state_maximized_horz_atom) < 0)
331 return -1;
332 }
333 else
334 {
335 if (ewmh_modify_state
336 (This, wnd, 0, This->ewmhints.state_maximized_vert_atom,
337 This->ewmhints.state_maximized_horz_atom) < 0)
338 return -1;
339 }
340
341 return 0;
342 }
343
344
345 int
346 ewmh_get_window_desktop(RDPCLIENT * This, Window wnd)
347 {
348 unsigned long nitems_return;
349 unsigned char *prop_return;
350 int desktop;
351
352 if (get_property_value(This, wnd, "_NET_WM_DESKTOP", 1, &nitems_return, &prop_return, 0) < 0)
353 return (-1);
354
355 if (nitems_return != 1)
356 {
357 fprintf(stderr, "_NET_WM_DESKTOP has bad length\n");
358 return (-1);
359 }
360
361 desktop = *prop_return;
362 XFree(prop_return);
363 return desktop;
364 }
365
366
367 int
368 ewmh_move_to_desktop(RDPCLIENT * This, Window wnd, unsigned int desktop)
369 {
370 Status status;
371 XEvent xevent;
372
373 xevent.type = ClientMessage;
374 xevent.xclient.window = wnd;
375 xevent.xclient.message_type = This->net_wm_desktop_atom;
376 xevent.xclient.format = 32;
377 xevent.xclient.data.l[0] = desktop;
378 xevent.xclient.data.l[1] = 0;
379 xevent.xclient.data.l[2] = 0;
380 xevent.xclient.data.l[3] = 0;
381 xevent.xclient.data.l[4] = 0;
382 status = XSendEvent(This->display, DefaultRootWindow(This->display), False,
383 SubstructureNotifyMask | SubstructureRedirectMask, &xevent);
384 if (!status)
385 return -1;
386
387 return 0;
388 }
389
390 void
391 ewmh_set_wm_name(RDPCLIENT * This, Window wnd, const char *title)
392 {
393 int len;
394
395 len = strlen(title);
396 XChangeProperty(This->display, wnd, This->ewmhints.name_atom, This->ewmhints.utf8_string_atom,
397 8, PropModeReplace, (unsigned char *) title, len);
398 }
399
400
401 int
402 ewmh_set_window_popup(RDPCLIENT * This, Window wnd)
403 {
404 if (ewmh_modify_state
405 (This, wnd, 1, This->ewmhints.state_skip_taskbar_atom, This->ewmhints.state_skip_pager_atom) < 0)
406 return -1;
407 return 0;
408 }
409
410 int
411 ewmh_set_window_modal(RDPCLIENT * This, Window wnd)
412 {
413 if (ewmh_modify_state(This, wnd, 1, This->ewmhints.state_modal_atom, 0) < 0)
414 return -1;
415 return 0;
416 }
417
418 #endif /* MAKE_PROTO */
419
420
421 #if 0
422
423 /* FIXME: _NET_MOVERESIZE_WINDOW is for pagers, not for
424 applications. We should implement _NET_WM_MOVERESIZE instead */
425
426 int
427 ewmh_net_moveresize_window(RDPCLIENT * This, Window wnd, int x, int y, int width, int height)
428 {
429 Status status;
430 XEvent xevent;
431 Atom moveresize;
432
433 moveresize = XInternAtom(This->display, "_NET_MOVERESIZE_WINDOW", False);
434 if (!moveresize)
435 {
436 return -1;
437 }
438
439 xevent.type = ClientMessage;
440 xevent.xclient.window = wnd;
441 xevent.xclient.message_type = moveresize;
442 xevent.xclient.format = 32;
443 xevent.xclient.data.l[0] = StaticGravity | (1 << 8) | (1 << 9) | (1 << 10) | (1 << 11);
444 xevent.xclient.data.l[1] = x;
445 xevent.xclient.data.l[2] = y;
446 xevent.xclient.data.l[3] = width;
447 xevent.xclient.data.l[4] = height;
448
449 status = XSendEvent(This->display, DefaultRootWindow(This->display), False,
450 SubstructureNotifyMask | SubstructureRedirectMask, &xevent);
451 if (!status)
452 return -1;
453 return 0;
454 }
455
456 #endif