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_USER_TIME
);
111 /* CREATE_(NET_WM_USER_TIME_WINDOW); */
112 CREATE_(KDE_NET_WM_FRAME_STRUT
);
113 CREATE_(NET_FRAME_EXTENTS
);
115 CREATE_(NET_WM_PING
);
117 CREATE_(NET_WM_SYNC_REQUEST
);
118 CREATE_(NET_WM_SYNC_REQUEST_COUNTER
);
121 CREATE_(NET_WM_WINDOW_TYPE_DESKTOP
);
122 CREATE_(NET_WM_WINDOW_TYPE_DOCK
);
123 CREATE_(NET_WM_WINDOW_TYPE_TOOLBAR
);
124 CREATE_(NET_WM_WINDOW_TYPE_MENU
);
125 CREATE_(NET_WM_WINDOW_TYPE_UTILITY
);
126 CREATE_(NET_WM_WINDOW_TYPE_SPLASH
);
127 CREATE_(NET_WM_WINDOW_TYPE_DIALOG
);
128 CREATE_(NET_WM_WINDOW_TYPE_NORMAL
);
129 CREATE_(NET_WM_WINDOW_TYPE_POPUP_MENU
);
131 prop_atoms
[OBT_PROP_NET_WM_MOVERESIZE_SIZE_TOPLEFT
] = 0;
132 prop_atoms
[OBT_PROP_NET_WM_MOVERESIZE_SIZE_TOP
] = 1;
133 prop_atoms
[OBT_PROP_NET_WM_MOVERESIZE_SIZE_TOPRIGHT
] = 2;
134 prop_atoms
[OBT_PROP_NET_WM_MOVERESIZE_SIZE_RIGHT
] = 3;
135 prop_atoms
[OBT_PROP_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT
] = 4;
136 prop_atoms
[OBT_PROP_NET_WM_MOVERESIZE_SIZE_BOTTOM
] = 5;
137 prop_atoms
[OBT_PROP_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT
] = 6;
138 prop_atoms
[OBT_PROP_NET_WM_MOVERESIZE_SIZE_LEFT
] = 7;
139 prop_atoms
[OBT_PROP_NET_WM_MOVERESIZE_MOVE
] = 8;
140 prop_atoms
[OBT_PROP_NET_WM_MOVERESIZE_SIZE_KEYBOARD
] = 9;
141 prop_atoms
[OBT_PROP_NET_WM_MOVERESIZE_MOVE_KEYBOARD
] = 10;
142 prop_atoms
[OBT_PROP_NET_WM_MOVERESIZE_CANCEL
] = 11;
144 CREATE_(NET_WM_ACTION_MOVE
);
145 CREATE_(NET_WM_ACTION_RESIZE
);
146 CREATE_(NET_WM_ACTION_MINIMIZE
);
147 CREATE_(NET_WM_ACTION_SHADE
);
148 CREATE_(NET_WM_ACTION_MAXIMIZE_HORZ
);
149 CREATE_(NET_WM_ACTION_MAXIMIZE_VERT
);
150 CREATE_(NET_WM_ACTION_FULLSCREEN
);
151 CREATE_(NET_WM_ACTION_CHANGE_DESKTOP
);
152 CREATE_(NET_WM_ACTION_CLOSE
);
153 CREATE_(NET_WM_ACTION_ABOVE
);
154 CREATE_(NET_WM_ACTION_BELOW
);
156 CREATE_(NET_WM_STATE_MODAL
);
157 /* CREATE_(NET_WM_STATE_STICKY);*/
158 CREATE_(NET_WM_STATE_MAXIMIZED_VERT
);
159 CREATE_(NET_WM_STATE_MAXIMIZED_HORZ
);
160 CREATE_(NET_WM_STATE_SHADED
);
161 CREATE_(NET_WM_STATE_SKIP_TASKBAR
);
162 CREATE_(NET_WM_STATE_SKIP_PAGER
);
163 CREATE_(NET_WM_STATE_HIDDEN
);
164 CREATE_(NET_WM_STATE_FULLSCREEN
);
165 CREATE_(NET_WM_STATE_ABOVE
);
166 CREATE_(NET_WM_STATE_BELOW
);
167 CREATE_(NET_WM_STATE_DEMANDS_ATTENTION
);
169 prop_atoms
[OBT_PROP_NET_WM_STATE_ADD
] = 1;
170 prop_atoms
[OBT_PROP_NET_WM_STATE_REMOVE
] = 0;
171 prop_atoms
[OBT_PROP_NET_WM_STATE_TOGGLE
] = 2;
173 prop_atoms
[OBT_PROP_NET_WM_ORIENTATION_HORZ
] = 0;
174 prop_atoms
[OBT_PROP_NET_WM_ORIENTATION_VERT
] = 1;
175 prop_atoms
[OBT_PROP_NET_WM_TOPLEFT
] = 0;
176 prop_atoms
[OBT_PROP_NET_WM_TOPRIGHT
] = 1;
177 prop_atoms
[OBT_PROP_NET_WM_BOTTOMRIGHT
] = 2;
178 prop_atoms
[OBT_PROP_NET_WM_BOTTOMLEFT
] = 3;
180 CREATE_(KDE_WM_CHANGE_STATE
);
181 CREATE_(KDE_NET_WM_WINDOW_TYPE_OVERRIDE
);
184 CREATE_NAME(ROOTPMAPId, "_XROOTPMAP_ID");
185 CREATE_NAME(ESETROOTId, "ESETROOT_PMAP_ID");
188 CREATE_(OPENBOX_PID
);
190 CREATE_(OB_CONFIG_FILE
);
191 CREATE_(OB_WM_ACTION_UNDECORATE
);
192 CREATE_(OB_WM_STATE_UNDECORATED
);
195 CREATE_(OB_APP_ROLE
);
196 CREATE_(OB_APP_TITLE
);
197 CREATE_(OB_APP_NAME
);
198 CREATE_(OB_APP_CLASS
);
199 CREATE_(OB_APP_TYPE
);
202 Atom
obt_prop_atom(ObtPropAtom a
)
204 g_assert(prop_started
);
205 g_assert(a
< OBT_PROP_NUM_ATOMS
);
206 return prop_atoms
[a
];
209 static gboolean
get_prealloc(Window win
, Atom prop
, Atom type
, gint size
,
210 guchar
*data
, gulong num
)
212 gboolean ret
= FALSE
;
214 guchar
*xdata
= NULL
;
217 gulong ret_items
, bytes_left
;
218 glong num32
= 32 / size
* num
; /* num in 32-bit elements */
220 res
= XGetWindowProperty(obt_display
, win
, prop
, 0l, num32
,
221 FALSE
, type
, &ret_type
, &ret_size
,
222 &ret_items
, &bytes_left
, &xdata
);
223 if (res
== Success
&& ret_items
&& xdata
) {
224 if (ret_size
== size
&& ret_items
>= num
) {
226 for (i
= 0; i
< num
; ++i
)
232 ((guint16
*)data
)[i
] = ((gushort
*)xdata
)[i
];
235 ((guint32
*)data
)[i
] = ((gulong
*)xdata
)[i
];
238 g_assert_not_reached(); /* unhandled size */
247 static gboolean
get_all(Window win
, Atom prop
, Atom type
, gint size
,
248 guchar
**data
, guint
*num
)
250 gboolean ret
= FALSE
;
252 guchar
*xdata
= NULL
;
255 gulong ret_items
, bytes_left
;
257 res
= XGetWindowProperty(obt_display
, win
, prop
, 0l, G_MAXLONG
,
258 FALSE
, type
, &ret_type
, &ret_size
,
259 &ret_items
, &bytes_left
, &xdata
);
260 if (res
== Success
) {
261 if (ret_size
== size
&& ret_items
> 0) {
264 *data
= g_malloc(ret_items
* (size
/ 8));
265 for (i
= 0; i
< ret_items
; ++i
)
268 (*data
)[i
] = xdata
[i
];
271 ((guint16
*)*data
)[i
] = ((gushort
*)xdata
)[i
];
274 ((guint32
*)*data
)[i
] = ((gulong
*)xdata
)[i
];
277 g_assert_not_reached(); /* unhandled size */
287 /*! Get a text property from a window, and fill out the XTextProperty with it.
288 @param win The window to read the property from.
289 @param prop The atom of the property to read off the window.
290 @param tprop The XTextProperty to fill out.
291 @param type 0 to get text of any type, or a value from
292 ObtPropTextType to restrict the value to a specific type.
293 @return TRUE if the text was read and validated against the @type, and FALSE
296 static gboolean
get_text_property(Window win
, Atom prop
,
297 XTextProperty
*tprop
, ObtPropTextType type
)
299 if (!(XGetTextProperty(obt_display
, win
, tprop
, prop
) && tprop
->nitems
))
302 return TRUE
; /* no type checking */
304 case OBT_PROP_TEXT_STRING
:
305 case OBT_PROP_TEXT_STRING_XPCS
:
306 case OBT_PROP_TEXT_STRING_NO_CC
:
307 return tprop
->encoding
== OBT_PROP_ATOM(STRING
);
308 case OBT_PROP_TEXT_COMPOUND_TEXT
:
309 return tprop
->encoding
== OBT_PROP_ATOM(COMPOUND_TEXT
);
310 case OBT_PROP_TEXT_UTF8_STRING
:
311 return tprop
->encoding
== OBT_PROP_ATOM(UTF8_STRING
);
313 g_assert_not_reached();
317 /*! Returns one or more UTF-8 encoded strings from the text property.
318 @param tprop The XTextProperty to convert into UTF-8 string(s).
319 @param type The type which specifies the format that the text must meet, or
320 0 to allow any valid characters that can be converted to UTF-8 through.
321 @param max The maximum number of strings to return. -1 to return them all.
322 @return If max is 1, then this returns a gchar* with the single string.
323 Otherwise, this returns a gchar** of no more than max strings (or all
324 strings read, if max is negative). If an error occurs, NULL is returned.
326 static void* convert_text_property(XTextProperty
*tprop
,
327 ObtPropTextType type
, gint max
)
334 const gboolean return_single
= (max
== 1);
336 gchar
**strlist
= NULL
;
337 gchar
*single
[1] = { NULL
};
338 gchar
**retlist
= single
; /* single is used when max == 1 */
341 /* Read each string in the text property and store a pointer to it in
342 retlist. These pointers point into the X data structures directly.
344 Then we will convert them to UTF-8, and replace the retlist pointer with
347 if (tprop
->encoding
== OBT_PROP_ATOM(COMPOUND_TEXT
))
350 ok
= (XmbTextPropertyToTextList(
351 obt_display
, tprop
, &strlist
, &n_strs
) == Success
);
354 n_strs
= MIN(max
, n_strs
);
356 retlist
= g_new0(gchar
*, n_strs
+1);
358 for (i
= 0; i
< n_strs
; ++i
)
359 retlist
[i
] = strlist
[i
];
362 else if (tprop
->encoding
== OBT_PROP_ATOM(UTF8_STRING
) ||
363 tprop
->encoding
== OBT_PROP_ATOM(STRING
))
365 gchar
*p
; /* iterator */
367 if (tprop
->encoding
== OBT_PROP_ATOM(STRING
))
373 /* First, count the number of strings. Then make a structure for them
374 and copy pointers to them into it. */
375 p
= (gchar
*)tprop
->value
;
377 while (p
< (gchar
*)tprop
->value
+ tprop
->nitems
) {
378 p
+= strlen(p
) + 1; /* next string */
383 n_strs
= MIN(max
, n_strs
);
385 retlist
= g_new0(gchar
*, n_strs
+1);
387 p
= (gchar
*)tprop
->value
;
388 for (i
= 0; i
< n_strs
; ++i
) {
390 p
+= strlen(p
) + 1; /* next string */
395 if (!(ok
&& retlist
)) {
396 if (strlist
) XFreeStringList(strlist
);
400 /* convert each element in retlist to UTF-8, and replace it. */
401 for (i
= 0; i
< n_strs
; ++i
) {
402 if (encoding
== UTF8
) {
403 const gchar
*end
; /* the first byte past the valid data */
405 g_utf8_validate(retlist
[i
], -1, &end
);
406 retlist
[i
] = g_strndup(retlist
[i
], end
-retlist
[i
]);
408 else if (encoding
== LOCALE
) {
409 gsize nvalid
; /* the number of valid bytes at the front of the
411 gchar
*utf
; /* the string converted into utf8 */
413 utf
= g_locale_to_utf8(retlist
[i
], -1, &nvalid
, NULL
, NULL
);
415 utf
= g_locale_to_utf8(retlist
[i
], nvalid
, NULL
, NULL
, NULL
);
419 else { /* encoding == LATIN1 */
420 gsize nvalid
; /* the number of valid bytes at the front of the
422 gchar
*utf
; /* the string converted into utf8 */
423 gchar
*p
; /* iterator */
425 /* look for invalid characters */
426 for (p
= retlist
[i
], nvalid
= 0; *p
; ++p
, ++nvalid
) {
427 /* The only valid control characters are TAB(HT)=9 and
429 This is defined in ICCCM section 2:
430 http://tronche.com/gui/x/icccm/sec-2.html.
431 See a definition of the latin1 codepage here:
432 http://en.wikipedia.org/wiki/ISO/IEC_8859-1.
433 The above page includes control characters in the table,
434 which we must explicitly exclude, as the g_convert function
435 will happily take them.
437 const register guchar c
= (guchar
)*p
; /* unsigned value at p */
438 if ((c
< 32 && c
!= 9 && c
!= 10) || (c
>= 127 && c
<= 160))
439 break; /* found a control character that isn't allowed */
441 if (type
== OBT_PROP_TEXT_STRING_NO_CC
&& c
< 32)
442 break; /* absolutely no control characters are allowed */
444 if (type
== OBT_PROP_TEXT_STRING_XPCS
) {
445 const gboolean valid
= (
446 (c
>= 32 && c
< 128) || c
== 9 || c
== 10);
448 break; /* strict whitelisting for XPCS */
451 /* look for invalid latin1 characters */
452 utf
= g_convert(retlist
[i
], nvalid
, "utf-8", "iso-8859-1",
453 &nvalid
, NULL
, NULL
);
455 utf
= g_convert(retlist
[i
], nvalid
, "utf-8", "iso-8859-1",
462 if (strlist
) XFreeStringList(strlist
);
469 gboolean
obt_prop_get32(Window win
, Atom prop
, Atom type
, guint32
*ret
)
471 return get_prealloc(win
, prop
, type
, 32, (guchar
*)ret
, 1);
474 gboolean
obt_prop_get_array32(Window win
, Atom prop
, Atom type
, guint32
**ret
,
477 return get_all(win
, prop
, type
, 32, (guchar
**)ret
, nret
);
480 gboolean
obt_prop_get_text(Window win
, Atom prop
, ObtPropTextType type
,
485 gboolean ret
= FALSE
;
487 if (get_text_property(win
, prop
, &tprop
, type
)) {
488 str
= (gchar
*)convert_text_property(&tprop
, type
, 1);
499 gboolean
obt_prop_get_array_text(Window win
, Atom prop
,
500 ObtPropTextType type
,
501 gchar
***ret_strings
)
505 gboolean ret
= FALSE
;
507 if (get_text_property(win
, prop
, &tprop
, type
)) {
508 strs
= (gchar
**)convert_text_property(&tprop
, type
, -1);
519 void obt_prop_set32(Window win
, Atom prop
, Atom type
, gulong val
)
521 XChangeProperty(obt_display
, win
, prop
, type
, 32, PropModeReplace
,
525 void obt_prop_set_array32(Window win
, Atom prop
, Atom type
, gulong
*val
,
528 XChangeProperty(obt_display
, win
, prop
, type
, 32, PropModeReplace
,
532 void obt_prop_set_text(Window win
, Atom prop
, const gchar
*val
)
534 XChangeProperty(obt_display
, win
, prop
, OBT_PROP_ATOM(UTF8_STRING
), 8,
535 PropModeReplace
, (const guchar
*)val
, strlen(val
));
538 void obt_prop_set_array_text(Window win
, Atom prop
, const gchar
*const *strs
)
541 gchar
const *const *s
;
543 str
= g_string_sized_new(0);
544 for (s
= strs
; *s
; ++s
) {
545 str
= g_string_append(str
, *s
);
546 str
= g_string_append_c(str
, '\0');
548 XChangeProperty(obt_display
, win
, prop
, OBT_PROP_ATOM(UTF8_STRING
), 8,
549 PropModeReplace
, (guchar
*)str
->str
, str
->len
);
550 g_string_free(str
, TRUE
);
553 void obt_prop_erase(Window win
, Atom prop
)
555 XDeleteProperty(obt_display
, win
, prop
);
558 void obt_prop_message(gint screen
, Window about
, Atom messagetype
,
559 glong data0
, glong data1
, glong data2
, glong data3
,
560 glong data4
, glong mask
)
562 obt_prop_message_to(obt_root(screen
), about
, messagetype
,
563 data0
, data1
, data2
, data3
, data4
, mask
);
566 void obt_prop_message_to(Window to
, Window about
,
568 glong data0
, glong data1
, glong data2
, glong data3
,
569 glong data4
, glong mask
)
572 ce
.xclient
.type
= ClientMessage
;
573 ce
.xclient
.message_type
= messagetype
;
574 ce
.xclient
.display
= obt_display
;
575 ce
.xclient
.window
= about
;
576 ce
.xclient
.format
= 32;
577 ce
.xclient
.data
.l
[0] = data0
;
578 ce
.xclient
.data
.l
[1] = data1
;
579 ce
.xclient
.data
.l
[2] = data2
;
580 ce
.xclient
.data
.l
[3] = data3
;
581 ce
.xclient
.data
.l
[4] = data4
;
582 XSendEvent(obt_display
, to
, FALSE
, mask
, &ce
);