1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 obt/prop.c for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2007 Dana Jansens
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 See the COPYING file for a copy of the GNU General Public License.
21 #include "obt/display.h"
23 #include <X11/Xatom.h>
28 Atom prop_atoms
[OBT_PROP_NUM_ATOMS
];
29 gboolean prop_started
= FALSE
;
31 #define CREATE_NAME(var, name) (prop_atoms[OBT_PROP_##var] = \
32 XInternAtom((obt_display), (name), FALSE))
33 #define CREATE(var) CREATE_NAME(var, #var)
34 #define CREATE_(var) CREATE_NAME(var, "_" #var)
36 void obt_prop_startup(void)
38 if (prop_started
) return;
41 g_assert(obt_display
);
48 CREATE(COMPOUND_TEXT
);
53 CREATE(WM_COLORMAP_WINDOWS
);
56 CREATE(WM_CHANGE_STATE
);
57 CREATE(WM_DELETE_WINDOW
);
58 CREATE(WM_TAKE_FOCUS
);
62 CREATE(WM_WINDOW_ROLE
);
63 CREATE(WM_CLIENT_MACHINE
);
65 CREATE(WM_CLIENT_LEADER
);
66 CREATE(WM_TRANSIENT_FOR
);
67 CREATE_(MOTIF_WM_HINTS
);
68 CREATE_(MOTIF_WM_INFO
);
72 CREATE_(NET_WM_FULL_PLACEMENT
);
74 CREATE_(NET_SUPPORTED
);
75 CREATE_(NET_CLIENT_LIST
);
76 CREATE_(NET_CLIENT_LIST_STACKING
);
77 CREATE_(NET_NUMBER_OF_DESKTOPS
);
78 CREATE_(NET_DESKTOP_GEOMETRY
);
79 CREATE_(NET_DESKTOP_VIEWPORT
);
80 CREATE_(NET_CURRENT_DESKTOP
);
81 CREATE_(NET_DESKTOP_NAMES
);
82 CREATE_(NET_ACTIVE_WINDOW
);
83 /* CREATE_(NET_RESTACK_WINDOW);*/
84 CREATE_(NET_WORKAREA
);
85 CREATE_(NET_SUPPORTING_WM_CHECK
);
86 CREATE_(NET_DESKTOP_LAYOUT
);
87 CREATE_(NET_SHOWING_DESKTOP
);
89 CREATE_(NET_CLOSE_WINDOW
);
90 CREATE_(NET_WM_MOVERESIZE
);
91 CREATE_(NET_MOVERESIZE_WINDOW
);
92 CREATE_(NET_REQUEST_FRAME_EXTENTS
);
93 CREATE_(NET_RESTACK_WINDOW
);
95 CREATE_(NET_STARTUP_ID
);
98 CREATE_(NET_WM_VISIBLE_NAME
);
99 CREATE_(NET_WM_ICON_NAME
);
100 CREATE_(NET_WM_VISIBLE_ICON_NAME
);
101 CREATE_(NET_WM_DESKTOP
);
102 CREATE_(NET_WM_WINDOW_TYPE
);
103 CREATE_(NET_WM_STATE
);
104 CREATE_(NET_WM_STRUT
);
105 CREATE_(NET_WM_STRUT_PARTIAL
);
106 CREATE_(NET_WM_ICON
);
107 CREATE_(NET_WM_ICON_GEOMETRY
);
109 CREATE_(NET_WM_ALLOWED_ACTIONS
);
110 CREATE_(NET_WM_WINDOW_OPACITY
);
111 CREATE_(NET_WM_USER_TIME
);
112 /* CREATE_(NET_WM_USER_TIME_WINDOW); */
113 CREATE_(KDE_NET_WM_FRAME_STRUT
);
114 CREATE_(NET_FRAME_EXTENTS
);
116 CREATE_(NET_WM_PING
);
118 CREATE_(NET_WM_SYNC_REQUEST
);
119 CREATE_(NET_WM_SYNC_REQUEST_COUNTER
);
122 CREATE_(NET_WM_WINDOW_TYPE_DESKTOP
);
123 CREATE_(NET_WM_WINDOW_TYPE_DOCK
);
124 CREATE_(NET_WM_WINDOW_TYPE_TOOLBAR
);
125 CREATE_(NET_WM_WINDOW_TYPE_MENU
);
126 CREATE_(NET_WM_WINDOW_TYPE_UTILITY
);
127 CREATE_(NET_WM_WINDOW_TYPE_SPLASH
);
128 CREATE_(NET_WM_WINDOW_TYPE_DIALOG
);
129 CREATE_(NET_WM_WINDOW_TYPE_NORMAL
);
130 CREATE_(NET_WM_WINDOW_TYPE_POPUP_MENU
);
132 prop_atoms
[OBT_PROP_NET_WM_MOVERESIZE_SIZE_TOPLEFT
] = 0;
133 prop_atoms
[OBT_PROP_NET_WM_MOVERESIZE_SIZE_TOP
] = 1;
134 prop_atoms
[OBT_PROP_NET_WM_MOVERESIZE_SIZE_TOPRIGHT
] = 2;
135 prop_atoms
[OBT_PROP_NET_WM_MOVERESIZE_SIZE_RIGHT
] = 3;
136 prop_atoms
[OBT_PROP_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT
] = 4;
137 prop_atoms
[OBT_PROP_NET_WM_MOVERESIZE_SIZE_BOTTOM
] = 5;
138 prop_atoms
[OBT_PROP_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT
] = 6;
139 prop_atoms
[OBT_PROP_NET_WM_MOVERESIZE_SIZE_LEFT
] = 7;
140 prop_atoms
[OBT_PROP_NET_WM_MOVERESIZE_MOVE
] = 8;
141 prop_atoms
[OBT_PROP_NET_WM_MOVERESIZE_SIZE_KEYBOARD
] = 9;
142 prop_atoms
[OBT_PROP_NET_WM_MOVERESIZE_MOVE_KEYBOARD
] = 10;
143 prop_atoms
[OBT_PROP_NET_WM_MOVERESIZE_CANCEL
] = 11;
145 CREATE_(NET_WM_ACTION_MOVE
);
146 CREATE_(NET_WM_ACTION_RESIZE
);
147 CREATE_(NET_WM_ACTION_MINIMIZE
);
148 CREATE_(NET_WM_ACTION_SHADE
);
149 CREATE_(NET_WM_ACTION_MAXIMIZE_HORZ
);
150 CREATE_(NET_WM_ACTION_MAXIMIZE_VERT
);
151 CREATE_(NET_WM_ACTION_FULLSCREEN
);
152 CREATE_(NET_WM_ACTION_CHANGE_DESKTOP
);
153 CREATE_(NET_WM_ACTION_CLOSE
);
154 CREATE_(NET_WM_ACTION_ABOVE
);
155 CREATE_(NET_WM_ACTION_BELOW
);
157 CREATE_(NET_WM_STATE_MODAL
);
158 /* CREATE_(NET_WM_STATE_STICKY);*/
159 CREATE_(NET_WM_STATE_MAXIMIZED_VERT
);
160 CREATE_(NET_WM_STATE_MAXIMIZED_HORZ
);
161 CREATE_(NET_WM_STATE_SHADED
);
162 CREATE_(NET_WM_STATE_SKIP_TASKBAR
);
163 CREATE_(NET_WM_STATE_SKIP_PAGER
);
164 CREATE_(NET_WM_STATE_HIDDEN
);
165 CREATE_(NET_WM_STATE_FULLSCREEN
);
166 CREATE_(NET_WM_STATE_ABOVE
);
167 CREATE_(NET_WM_STATE_BELOW
);
168 CREATE_(NET_WM_STATE_DEMANDS_ATTENTION
);
170 prop_atoms
[OBT_PROP_NET_WM_STATE_ADD
] = 1;
171 prop_atoms
[OBT_PROP_NET_WM_STATE_REMOVE
] = 0;
172 prop_atoms
[OBT_PROP_NET_WM_STATE_TOGGLE
] = 2;
174 prop_atoms
[OBT_PROP_NET_WM_ORIENTATION_HORZ
] = 0;
175 prop_atoms
[OBT_PROP_NET_WM_ORIENTATION_VERT
] = 1;
176 prop_atoms
[OBT_PROP_NET_WM_TOPLEFT
] = 0;
177 prop_atoms
[OBT_PROP_NET_WM_TOPRIGHT
] = 1;
178 prop_atoms
[OBT_PROP_NET_WM_BOTTOMRIGHT
] = 2;
179 prop_atoms
[OBT_PROP_NET_WM_BOTTOMLEFT
] = 3;
181 CREATE_(KDE_WM_CHANGE_STATE
);
182 CREATE_(KDE_NET_WM_WINDOW_TYPE_OVERRIDE
);
185 CREATE_NAME(ROOTPMAPId, "_XROOTPMAP_ID");
186 CREATE_NAME(ESETROOTId, "ESETROOT_PMAP_ID");
189 CREATE_(OPENBOX_PID
);
191 CREATE_(OB_CONFIG_FILE
);
192 CREATE_(OB_WM_ACTION_UNDECORATE
);
193 CREATE_(OB_WM_STATE_UNDECORATED
);
196 CREATE_(OB_APP_ROLE
);
197 CREATE_(OB_APP_TITLE
);
198 CREATE_(OB_APP_NAME
);
199 CREATE_(OB_APP_CLASS
);
200 CREATE_(OB_APP_TYPE
);
203 Atom
obt_prop_atom(ObtPropAtom a
)
205 g_assert(prop_started
);
206 g_assert(a
< OBT_PROP_NUM_ATOMS
);
207 return prop_atoms
[a
];
210 static gboolean
get_prealloc(Window win
, Atom prop
, Atom type
, gint size
,
211 guchar
*data
, gulong num
)
213 gboolean ret
= FALSE
;
215 guchar
*xdata
= NULL
;
218 gulong ret_items
, bytes_left
;
219 glong num32
= 32 / size
* num
; /* num in 32-bit elements */
221 res
= XGetWindowProperty(obt_display
, win
, prop
, 0l, num32
,
222 FALSE
, type
, &ret_type
, &ret_size
,
223 &ret_items
, &bytes_left
, &xdata
);
224 if (res
== Success
&& ret_items
&& xdata
) {
225 if (ret_size
== size
&& ret_items
>= num
) {
227 for (i
= 0; i
< num
; ++i
)
233 ((guint16
*)data
)[i
] = ((gushort
*)xdata
)[i
];
236 ((guint32
*)data
)[i
] = ((gulong
*)xdata
)[i
];
239 g_assert_not_reached(); /* unhandled size */
248 static gboolean
get_all(Window win
, Atom prop
, Atom type
, gint size
,
249 guchar
**data
, guint
*num
)
251 gboolean ret
= FALSE
;
253 guchar
*xdata
= NULL
;
256 gulong ret_items
, bytes_left
;
258 res
= XGetWindowProperty(obt_display
, win
, prop
, 0l, G_MAXLONG
,
259 FALSE
, type
, &ret_type
, &ret_size
,
260 &ret_items
, &bytes_left
, &xdata
);
261 if (res
== Success
) {
262 if (ret_size
== size
&& ret_items
> 0) {
265 *data
= g_malloc(ret_items
* (size
/ 8));
266 for (i
= 0; i
< ret_items
; ++i
)
269 (*data
)[i
] = xdata
[i
];
272 ((guint16
*)*data
)[i
] = ((gushort
*)xdata
)[i
];
275 ((guint32
*)*data
)[i
] = ((gulong
*)xdata
)[i
];
278 g_assert_not_reached(); /* unhandled size */
288 /*! Get a text property from a window, and fill out the XTextProperty with it.
289 @param win The window to read the property from.
290 @param prop The atom of the property to read off the window.
291 @param tprop The XTextProperty to fill out.
292 @param type 0 to get text of any type, or a value from
293 ObtPropTextType to restrict the value to a specific type.
294 @return TRUE if the text was read and validated against the @type, and FALSE
297 static gboolean
get_text_property(Window win
, Atom prop
,
298 XTextProperty
*tprop
, ObtPropTextType type
)
300 if (!(XGetTextProperty(obt_display
, win
, tprop
, prop
) && tprop
->nitems
))
303 return TRUE
; /* no type checking */
305 case OBT_PROP_TEXT_STRING
:
306 case OBT_PROP_TEXT_STRING_XPCS
:
307 case OBT_PROP_TEXT_STRING_NO_CC
:
308 return tprop
->encoding
== OBT_PROP_ATOM(STRING
);
309 case OBT_PROP_TEXT_COMPOUND_TEXT
:
310 return tprop
->encoding
== OBT_PROP_ATOM(COMPOUND_TEXT
);
311 case OBT_PROP_TEXT_UTF8_STRING
:
312 return tprop
->encoding
== OBT_PROP_ATOM(UTF8_STRING
);
314 g_assert_not_reached();
318 /*! Returns one or more UTF-8 encoded strings from the text property.
319 @param tprop The XTextProperty to convert into UTF-8 string(s).
320 @param type The type which specifies the format that the text must meet, or
321 0 to allow any valid characters that can be converted to UTF-8 through.
322 @param max The maximum number of strings to return. -1 to return them all.
323 @return If max is 1, then this returns a gchar* with the single string.
324 Otherwise, this returns a gchar** of no more than max strings (or all
325 strings read, if max is negative). If an error occurs, NULL is returned.
327 static void* convert_text_property(XTextProperty
*tprop
,
328 ObtPropTextType type
, gint max
)
335 const gboolean return_single
= (max
== 1);
337 gchar
**strlist
= NULL
;
338 gchar
*single
[1] = { NULL
};
339 gchar
**retlist
= single
; /* single is used when max == 1 */
342 /* Read each string in the text property and store a pointer to it in
343 retlist. These pointers point into the X data structures directly.
345 Then we will convert them to UTF-8, and replace the retlist pointer with
348 if (tprop
->encoding
== OBT_PROP_ATOM(COMPOUND_TEXT
))
351 ok
= (XmbTextPropertyToTextList(
352 obt_display
, tprop
, &strlist
, &n_strs
) == Success
);
355 n_strs
= MIN(max
, n_strs
);
357 retlist
= g_new0(gchar
*, n_strs
+1);
359 for (i
= 0; i
< n_strs
; ++i
)
360 retlist
[i
] = strlist
[i
];
363 else if (tprop
->encoding
== OBT_PROP_ATOM(UTF8_STRING
) ||
364 tprop
->encoding
== OBT_PROP_ATOM(STRING
))
366 gchar
*p
; /* iterator */
368 if (tprop
->encoding
== OBT_PROP_ATOM(STRING
))
374 /* First, count the number of strings. Then make a structure for them
375 and copy pointers to them into it. */
376 p
= (gchar
*)tprop
->value
;
378 while (p
< (gchar
*)tprop
->value
+ tprop
->nitems
) {
379 p
+= strlen(p
) + 1; /* next string */
384 n_strs
= MIN(max
, n_strs
);
386 retlist
= g_new0(gchar
*, n_strs
+1);
388 p
= (gchar
*)tprop
->value
;
389 for (i
= 0; i
< n_strs
; ++i
) {
391 p
+= strlen(p
) + 1; /* next string */
396 if (!(ok
&& retlist
)) {
397 if (strlist
) XFreeStringList(strlist
);
401 /* convert each element in retlist to UTF-8, and replace it. */
402 for (i
= 0; i
< n_strs
; ++i
) {
403 if (encoding
== UTF8
) {
404 const gchar
*end
; /* the first byte past the valid data */
406 g_utf8_validate(retlist
[i
], -1, &end
);
407 retlist
[i
] = g_strndup(retlist
[i
], end
-retlist
[i
]);
409 else if (encoding
== LOCALE
) {
410 gsize nvalid
; /* the number of valid bytes at the front of the
412 gchar
*utf
; /* the string converted into utf8 */
414 utf
= g_locale_to_utf8(retlist
[i
], -1, &nvalid
, NULL
, NULL
);
416 utf
= g_locale_to_utf8(retlist
[i
], nvalid
, NULL
, NULL
, NULL
);
420 else { /* encoding == LATIN1 */
421 gsize nvalid
; /* the number of valid bytes at the front of the
423 gchar
*utf
; /* the string converted into utf8 */
424 gchar
*p
; /* iterator */
426 /* look for invalid characters */
427 for (p
= retlist
[i
], nvalid
= 0; *p
; ++p
, ++nvalid
) {
428 /* The only valid control characters are TAB(HT)=9 and
430 This is defined in ICCCM section 2:
431 http://tronche.com/gui/x/icccm/sec-2.html.
432 See a definition of the latin1 codepage here:
433 http://en.wikipedia.org/wiki/ISO/IEC_8859-1.
434 The above page includes control characters in the table,
435 which we must explicitly exclude, as the g_convert function
436 will happily take them.
438 const register guchar c
= (guchar
)*p
; /* unsigned value at p */
439 if ((c
< 32 && c
!= 9 && c
!= 10) || (c
>= 127 && c
<= 160))
440 break; /* found a control character that isn't allowed */
442 if (type
== OBT_PROP_TEXT_STRING_NO_CC
&& c
< 32)
443 break; /* absolutely no control characters are allowed */
445 if (type
== OBT_PROP_TEXT_STRING_XPCS
) {
446 const gboolean valid
= (
447 (c
>= 32 && c
< 128) || c
== 9 || c
== 10);
449 break; /* strict whitelisting for XPCS */
452 /* look for invalid latin1 characters */
453 utf
= g_convert(retlist
[i
], nvalid
, "utf-8", "iso-8859-1",
454 &nvalid
, NULL
, NULL
);
456 utf
= g_convert(retlist
[i
], nvalid
, "utf-8", "iso-8859-1",
463 if (strlist
) XFreeStringList(strlist
);
470 gboolean
obt_prop_get32(Window win
, Atom prop
, Atom type
, guint32
*ret
)
472 return get_prealloc(win
, prop
, type
, 32, (guchar
*)ret
, 1);
475 gboolean
obt_prop_get_array32(Window win
, Atom prop
, Atom type
, guint32
**ret
,
478 return get_all(win
, prop
, type
, 32, (guchar
**)ret
, nret
);
481 gboolean
obt_prop_get_text(Window win
, Atom prop
, ObtPropTextType type
,
486 gboolean ret
= FALSE
;
488 if (get_text_property(win
, prop
, &tprop
, type
)) {
489 str
= (gchar
*)convert_text_property(&tprop
, type
, 1);
500 gboolean
obt_prop_get_array_text(Window win
, Atom prop
,
501 ObtPropTextType type
,
502 gchar
***ret_strings
)
506 gboolean ret
= FALSE
;
508 if (get_text_property(win
, prop
, &tprop
, type
)) {
509 strs
= (gchar
**)convert_text_property(&tprop
, type
, -1);
520 void obt_prop_set32(Window win
, Atom prop
, Atom type
, gulong val
)
522 XChangeProperty(obt_display
, win
, prop
, type
, 32, PropModeReplace
,
526 void obt_prop_set_array32(Window win
, Atom prop
, Atom type
, gulong
*val
,
529 XChangeProperty(obt_display
, win
, prop
, type
, 32, PropModeReplace
,
533 void obt_prop_set_text(Window win
, Atom prop
, const gchar
*val
)
535 XChangeProperty(obt_display
, win
, prop
, OBT_PROP_ATOM(UTF8_STRING
), 8,
536 PropModeReplace
, (const guchar
*)val
, strlen(val
));
539 void obt_prop_set_array_text(Window win
, Atom prop
, const gchar
*const *strs
)
542 gchar
const *const *s
;
544 str
= g_string_sized_new(0);
545 for (s
= strs
; *s
; ++s
) {
546 str
= g_string_append(str
, *s
);
547 str
= g_string_append_c(str
, '\0');
549 XChangeProperty(obt_display
, win
, prop
, OBT_PROP_ATOM(UTF8_STRING
), 8,
550 PropModeReplace
, (guchar
*)str
->str
, str
->len
);
551 g_string_free(str
, TRUE
);
554 void obt_prop_erase(Window win
, Atom prop
)
556 XDeleteProperty(obt_display
, win
, prop
);
559 void obt_prop_message(gint screen
, Window about
, Atom messagetype
,
560 glong data0
, glong data1
, glong data2
, glong data3
,
561 glong data4
, glong mask
)
563 obt_prop_message_to(obt_root(screen
), about
, messagetype
,
564 data0
, data1
, data2
, data3
, data4
, mask
);
567 void obt_prop_message_to(Window to
, Window about
,
569 glong data0
, glong data1
, glong data2
, glong data3
,
570 glong data4
, glong mask
)
573 ce
.xclient
.type
= ClientMessage
;
574 ce
.xclient
.message_type
= messagetype
;
575 ce
.xclient
.display
= obt_display
;
576 ce
.xclient
.window
= about
;
577 ce
.xclient
.format
= 32;
578 ce
.xclient
.data
.l
[0] = data0
;
579 ce
.xclient
.data
.l
[1] = data1
;
580 ce
.xclient
.data
.l
[2] = data2
;
581 ce
.xclient
.data
.l
[3] = data3
;
582 ce
.xclient
.data
.l
[4] = data4
;
583 XSendEvent(obt_display
, to
, FALSE
, mask
, &ce
);