1 /* -*- c-basic-offset: 8 -*-
2 rdesktop: A Remote Desktop Protocol client.
4 Support functions for Extended Window Manager Hints,
5 http://www.freedesktop.org/wiki/Standards_2fwm_2dspec
7 Copyright (C) Peter Astrand <astrand@cendio.se> 2005
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.
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.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include <X11/Xatom.h>
26 #include <X11/Xutil.h>
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 */
34 Get window property value (32 bit format)
35 Returns zero on success, -1 on error
38 get_property_value(RDPCLIENT
* This
, Window wnd
, char *propname
, long max_length
,
39 unsigned long *nitems_return
, unsigned char **prop_return
, int nowarn
)
43 Atom actual_type_return
;
44 int actual_format_return
;
45 unsigned long bytes_after_return
;
47 property
= XInternAtom(This
->display
, propname
, True
);
50 fprintf(stderr
, "Atom %s does not exist\n", propname
);
54 result
= XGetWindowProperty(This
->display
, wnd
, property
, 0, /* long_offset */
55 max_length
, /* long_length */
57 AnyPropertyType
, /* req_type */
59 &actual_format_return
,
60 nitems_return
, &bytes_after_return
, prop_return
);
62 if (result
!= Success
)
64 fprintf(stderr
, "XGetWindowProperty failed\n");
68 if (actual_type_return
== None
|| actual_format_return
== 0)
71 fprintf(stderr
, "Window is missing property %s\n", propname
);
75 if (bytes_after_return
)
77 fprintf(stderr
, "%s is too big for me\n", propname
);
81 if (actual_format_return
!= 32)
83 fprintf(stderr
, "%s has bad format\n", propname
);
91 Get current desktop number
95 get_current_desktop(RDPCLIENT
* This
)
97 unsigned long nitems_return
;
98 unsigned char *prop_return
;
101 if (get_property_value
102 (This
, DefaultRootWindow(This
->display
), "_NET_CURRENT_DESKTOP", 1, &nitems_return
,
103 &prop_return
, 0) < 0)
106 if (nitems_return
!= 1)
108 fprintf(stderr
, "_NET_CURRENT_DESKTOP has bad length\n");
112 current_desktop
= *prop_return
;
114 return current_desktop
;
118 Get workarea geometry
119 Returns zero on success, -1 on error
123 get_current_workarea(RDPCLIENT
* This
, uint32
* x
, uint32
* y
, uint32
* width
, uint32
* height
)
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 */
135 if (get_property_value
136 (This
, DefaultRootWindow(This
->display
), "_NET_WORKAREA", max_prop_length
, &nitems_return
,
137 &prop_return
, 0) < 0)
140 if (nitems_return
% 4)
142 fprintf(stderr
, "_NET_WORKAREA has odd length\n");
146 current_desktop
= get_current_desktop(This
);
148 if (current_desktop
< 0)
151 return_words
= (uint32
*) prop_return
;
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
];
167 ewmh_init(RDPCLIENT
* This
)
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
);
187 Get the window state: normal/minimized/maximized.
191 ewmh_get_window_state(RDPCLIENT
* This
, Window w
)
193 unsigned long nitems_return
;
194 unsigned char *prop_return
;
195 uint32
*return_words
;
197 BOOL maximized_vert
, maximized_horz
, hidden
;
199 maximized_vert
= maximized_horz
= hidden
= False
;
201 if (get_property_value(This
, w
, "_NET_WM_STATE", 64, &nitems_return
, &prop_return
, 0) < 0)
202 return SEAMLESSRDP_NORMAL
;
204 return_words
= (uint32
*) prop_return
;
206 for (item
= 0; item
< nitems_return
; item
++)
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
)
218 if (maximized_vert
&& maximized_horz
)
219 return SEAMLESSRDP_MAXIMIZED
;
221 return SEAMLESSRDP_MINIMIZED
;
223 return SEAMLESSRDP_NORMAL
;
227 ewmh_modify_state(RDPCLIENT
* This
, Window wnd
, int add
, Atom atom1
, Atom atom2
)
233 unsigned long nitems
;
234 unsigned char *props
;
235 uint32 state
= WithdrawnState
;
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
)
244 state
= *(uint32
*) props
;
248 if (state
== WithdrawnState
)
262 XChangeProperty(This
->display
, wnd
, This
->net_wm_state_atom
, XA_ATOM
,
263 32, PropModeAppend
, (unsigned char *) atoms
, nitems
);
270 if (get_property_value(This
, wnd
, "_NET_WM_STATE", 64, &nitems
, &props
, 1) < 0)
273 atoms
= (Atom
*) props
;
275 for (i
= 0; i
< nitems
; i
++)
277 if ((atoms
[i
] == atom1
) || (atom2
&& (atoms
[i
] == atom2
)))
279 if (i
!= (nitems
- 1))
280 memmove(&atoms
[i
], &atoms
[i
+ 1],
281 sizeof(Atom
) * (nitems
- i
- 1));
287 XChangeProperty(This
->display
, wnd
, This
->net_wm_state_atom
, XA_ATOM
,
288 32, PropModeReplace
, (unsigned char *) atoms
, nitems
);
296 xevent
.type
= ClientMessage
;
297 xevent
.xclient
.window
= wnd
;
298 xevent
.xclient
.message_type
= This
->net_wm_state_atom
;
299 xevent
.xclient
.format
= 32;
301 xevent
.xclient
.data
.l
[0] = _NET_WM_STATE_ADD
;
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
);
317 Set the window state: normal/minimized/maximized.
318 Returns -1 on failure.
321 ewmh_change_state(RDPCLIENT
* This
, Window wnd
, int state
)
324 * Deal with the max atoms
326 if (state
== SEAMLESSRDP_MAXIMIZED
)
328 if (ewmh_modify_state
329 (This
, wnd
, 1, This
->ewmhints
.state_maximized_vert_atom
,
330 This
->ewmhints
.state_maximized_horz_atom
) < 0)
335 if (ewmh_modify_state
336 (This
, wnd
, 0, This
->ewmhints
.state_maximized_vert_atom
,
337 This
->ewmhints
.state_maximized_horz_atom
) < 0)
346 ewmh_get_window_desktop(RDPCLIENT
* This
, Window wnd
)
348 unsigned long nitems_return
;
349 unsigned char *prop_return
;
352 if (get_property_value(This
, wnd
, "_NET_WM_DESKTOP", 1, &nitems_return
, &prop_return
, 0) < 0)
355 if (nitems_return
!= 1)
357 fprintf(stderr
, "_NET_WM_DESKTOP has bad length\n");
361 desktop
= *prop_return
;
368 ewmh_move_to_desktop(RDPCLIENT
* This
, Window wnd
, unsigned int desktop
)
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
);
391 ewmh_set_wm_name(RDPCLIENT
* This
, Window wnd
, const char *title
)
396 XChangeProperty(This
->display
, wnd
, This
->ewmhints
.name_atom
, This
->ewmhints
.utf8_string_atom
,
397 8, PropModeReplace
, (unsigned char *) title
, len
);
402 ewmh_set_window_popup(RDPCLIENT
* This
, Window wnd
)
404 if (ewmh_modify_state
405 (This
, wnd
, 1, This
->ewmhints
.state_skip_taskbar_atom
, This
->ewmhints
.state_skip_pager_atom
) < 0)
411 ewmh_set_window_modal(RDPCLIENT
* This
, Window wnd
)
413 if (ewmh_modify_state(This
, wnd
, 1, This
->ewmhints
.state_modal_atom
, 0) < 0)
418 #endif /* MAKE_PROTO */
423 /* FIXME: _NET_MOVERESIZE_WINDOW is for pagers, not for
424 applications. We should implement _NET_WM_MOVERESIZE instead */
427 ewmh_net_moveresize_window(RDPCLIENT
* This
, Window wnd
, int x
, int y
, int width
, int height
)
433 moveresize
= XInternAtom(This
->display
, "_NET_MOVERESIZE_WINDOW", False
);
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
;
449 status
= XSendEvent(This
->display
, DefaultRootWindow(This
->display
), False
,
450 SubstructureNotifyMask
| SubstructureRedirectMask
, &xevent
);