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_GROUP_NAME
);
201 CREATE_(OB_APP_GROUP_CLASS
);
202 CREATE_(OB_APP_TYPE
);
205 Atom
obt_prop_atom(ObtPropAtom a
)
207 g_assert(prop_started
);
208 g_assert(a
< OBT_PROP_NUM_ATOMS
);
209 return prop_atoms
[a
];
212 static gboolean
get_prealloc(Window win
, Atom prop
, Atom type
, gint size
,
213 guchar
*data
, gulong num
)
215 gboolean ret
= FALSE
;
217 guchar
*xdata
= NULL
;
220 gulong ret_items
, bytes_left
;
221 glong num32
= 32 / size
* num
; /* num in 32-bit elements */
223 res
= XGetWindowProperty(obt_display
, win
, prop
, 0l, num32
,
224 FALSE
, type
, &ret_type
, &ret_size
,
225 &ret_items
, &bytes_left
, &xdata
);
226 if (res
== Success
&& ret_items
&& xdata
) {
227 if (ret_size
== size
&& ret_items
>= num
) {
229 for (i
= 0; i
< num
; ++i
)
235 ((guint16
*)data
)[i
] = ((gushort
*)xdata
)[i
];
238 ((guint32
*)data
)[i
] = ((gulong
*)xdata
)[i
];
241 g_assert_not_reached(); /* unhandled size */
250 static gboolean
get_all(Window win
, Atom prop
, Atom type
, gint size
,
251 guchar
**data
, guint
*num
)
253 gboolean ret
= FALSE
;
255 guchar
*xdata
= NULL
;
258 gulong ret_items
, bytes_left
;
260 res
= XGetWindowProperty(obt_display
, win
, prop
, 0l, G_MAXLONG
,
261 FALSE
, type
, &ret_type
, &ret_size
,
262 &ret_items
, &bytes_left
, &xdata
);
263 if (res
== Success
) {
264 if (ret_size
== size
&& ret_items
> 0) {
267 *data
= g_malloc(ret_items
* (size
/ 8));
268 for (i
= 0; i
< ret_items
; ++i
)
271 (*data
)[i
] = xdata
[i
];
274 ((guint16
*)*data
)[i
] = ((gushort
*)xdata
)[i
];
277 ((guint32
*)*data
)[i
] = ((gulong
*)xdata
)[i
];
280 g_assert_not_reached(); /* unhandled size */
290 /*! Get a text property from a window, and fill out the XTextProperty with it.
291 @param win The window to read the property from.
292 @param prop The atom of the property to read off the window.
293 @param tprop The XTextProperty to fill out.
294 @param type 0 to get text of any type, or a value from
295 ObtPropTextType to restrict the value to a specific type.
296 @return TRUE if the text was read and validated against the @type, and FALSE
299 static gboolean
get_text_property(Window win
, Atom prop
,
300 XTextProperty
*tprop
, ObtPropTextType type
)
302 if (!(XGetTextProperty(obt_display
, win
, tprop
, prop
) && tprop
->nitems
))
305 return TRUE
; /* no type checking */
307 case OBT_PROP_TEXT_STRING
:
308 case OBT_PROP_TEXT_STRING_XPCS
:
309 case OBT_PROP_TEXT_STRING_NO_CC
:
310 return tprop
->encoding
== OBT_PROP_ATOM(STRING
);
311 case OBT_PROP_TEXT_COMPOUND_TEXT
:
312 return tprop
->encoding
== OBT_PROP_ATOM(COMPOUND_TEXT
);
313 case OBT_PROP_TEXT_UTF8_STRING
:
314 return tprop
->encoding
== OBT_PROP_ATOM(UTF8_STRING
);
316 g_assert_not_reached();
320 /*! Returns one or more UTF-8 encoded strings from the text property.
321 @param tprop The XTextProperty to convert into UTF-8 string(s).
322 @param type The type which specifies the format that the text must meet, or
323 0 to allow any valid characters that can be converted to UTF-8 through.
324 @param max The maximum number of strings to return. -1 to return them all.
325 @return If max is 1, then this returns a gchar* with the single string.
326 Otherwise, this returns a gchar** of no more than max strings (or all
327 strings read, if max is negative). If an error occurs, NULL is returned.
329 static void* convert_text_property(XTextProperty
*tprop
,
330 ObtPropTextType type
, gint max
)
337 const gboolean return_single
= (max
== 1);
339 gchar
**strlist
= NULL
;
340 gchar
*single
[1] = { NULL
};
341 gchar
**retlist
= single
; /* single is used when max == 1 */
344 /* Read each string in the text property and store a pointer to it in
345 retlist. These pointers point into the X data structures directly.
347 Then we will convert them to UTF-8, and replace the retlist pointer with
350 if (tprop
->encoding
== OBT_PROP_ATOM(COMPOUND_TEXT
))
353 ok
= (XmbTextPropertyToTextList(
354 obt_display
, tprop
, &strlist
, &n_strs
) == Success
);
357 n_strs
= MIN(max
, n_strs
);
359 retlist
= g_new0(gchar
*, n_strs
+1);
361 for (i
= 0; i
< n_strs
; ++i
)
362 retlist
[i
] = strlist
[i
];
365 else if (tprop
->encoding
== OBT_PROP_ATOM(UTF8_STRING
) ||
366 tprop
->encoding
== OBT_PROP_ATOM(STRING
))
368 gchar
*p
; /* iterator */
370 if (tprop
->encoding
== OBT_PROP_ATOM(STRING
))
376 /* First, count the number of strings. Then make a structure for them
377 and copy pointers to them into it. */
378 p
= (gchar
*)tprop
->value
;
380 while (p
< (gchar
*)tprop
->value
+ tprop
->nitems
) {
381 p
+= strlen(p
) + 1; /* next string */
386 n_strs
= MIN(max
, n_strs
);
388 retlist
= g_new0(gchar
*, n_strs
+1);
390 p
= (gchar
*)tprop
->value
;
391 for (i
= 0; i
< n_strs
; ++i
) {
393 p
+= strlen(p
) + 1; /* next string */
398 if (!(ok
&& retlist
)) {
399 if (strlist
) XFreeStringList(strlist
);
403 /* convert each element in retlist to UTF-8, and replace it. */
404 for (i
= 0; i
< n_strs
; ++i
) {
405 if (encoding
== UTF8
) {
406 const gchar
*end
; /* the first byte past the valid data */
408 g_utf8_validate(retlist
[i
], -1, &end
);
409 retlist
[i
] = g_strndup(retlist
[i
], end
-retlist
[i
]);
411 else if (encoding
== LOCALE
) {
412 gsize nvalid
; /* the number of valid bytes at the front of the
414 gchar
*utf
; /* the string converted into utf8 */
416 utf
= g_locale_to_utf8(retlist
[i
], -1, &nvalid
, NULL
, NULL
);
418 utf
= g_locale_to_utf8(retlist
[i
], nvalid
, NULL
, NULL
, NULL
);
422 else { /* encoding == LATIN1 */
423 gsize nvalid
; /* the number of valid bytes at the front of the
425 gchar
*utf
; /* the string converted into utf8 */
426 gchar
*p
; /* iterator */
428 /* look for invalid characters */
429 for (p
= retlist
[i
], nvalid
= 0; *p
; ++p
, ++nvalid
) {
430 /* The only valid control characters are TAB(HT)=9 and
432 This is defined in ICCCM section 2:
433 http://tronche.com/gui/x/icccm/sec-2.html.
434 See a definition of the latin1 codepage here:
435 http://en.wikipedia.org/wiki/ISO/IEC_8859-1.
436 The above page includes control characters in the table,
437 which we must explicitly exclude, as the g_convert function
438 will happily take them.
440 const register guchar c
= (guchar
)*p
; /* unsigned value at p */
441 if ((c
< 32 && c
!= 9 && c
!= 10) || (c
>= 127 && c
<= 160))
442 break; /* found a control character that isn't allowed */
444 if (type
== OBT_PROP_TEXT_STRING_NO_CC
&& c
< 32)
445 break; /* absolutely no control characters are allowed */
447 if (type
== OBT_PROP_TEXT_STRING_XPCS
) {
448 const gboolean valid
= (
449 (c
>= 32 && c
< 128) || c
== 9 || c
== 10);
451 break; /* strict whitelisting for XPCS */
454 /* look for invalid latin1 characters */
455 utf
= g_convert(retlist
[i
], nvalid
, "utf-8", "iso-8859-1",
456 &nvalid
, NULL
, NULL
);
458 utf
= g_convert(retlist
[i
], nvalid
, "utf-8", "iso-8859-1",
465 if (strlist
) XFreeStringList(strlist
);
472 gboolean
obt_prop_get32(Window win
, Atom prop
, Atom type
, guint32
*ret
)
474 return get_prealloc(win
, prop
, type
, 32, (guchar
*)ret
, 1);
477 gboolean
obt_prop_get_array32(Window win
, Atom prop
, Atom type
, guint32
**ret
,
480 return get_all(win
, prop
, type
, 32, (guchar
**)ret
, nret
);
483 gboolean
obt_prop_get_text(Window win
, Atom prop
, ObtPropTextType type
,
488 gboolean ret
= FALSE
;
490 if (get_text_property(win
, prop
, &tprop
, type
)) {
491 str
= (gchar
*)convert_text_property(&tprop
, type
, 1);
502 gboolean
obt_prop_get_array_text(Window win
, Atom prop
,
503 ObtPropTextType type
,
504 gchar
***ret_strings
)
508 gboolean ret
= FALSE
;
510 if (get_text_property(win
, prop
, &tprop
, type
)) {
511 strs
= (gchar
**)convert_text_property(&tprop
, type
, -1);
522 void obt_prop_set32(Window win
, Atom prop
, Atom type
, gulong val
)
524 XChangeProperty(obt_display
, win
, prop
, type
, 32, PropModeReplace
,
528 void obt_prop_set_array32(Window win
, Atom prop
, Atom type
, gulong
*val
,
531 XChangeProperty(obt_display
, win
, prop
, type
, 32, PropModeReplace
,
535 void obt_prop_set_text(Window win
, Atom prop
, const gchar
*val
)
537 XChangeProperty(obt_display
, win
, prop
, OBT_PROP_ATOM(UTF8_STRING
), 8,
538 PropModeReplace
, (const guchar
*)val
, strlen(val
));
541 void obt_prop_set_array_text(Window win
, Atom prop
, const gchar
*const *strs
)
544 gchar
const *const *s
;
546 str
= g_string_sized_new(0);
547 for (s
= strs
; *s
; ++s
) {
548 str
= g_string_append(str
, *s
);
549 str
= g_string_append_c(str
, '\0');
551 XChangeProperty(obt_display
, win
, prop
, OBT_PROP_ATOM(UTF8_STRING
), 8,
552 PropModeReplace
, (guchar
*)str
->str
, str
->len
);
553 g_string_free(str
, TRUE
);
556 void obt_prop_erase(Window win
, Atom prop
)
558 XDeleteProperty(obt_display
, win
, prop
);
561 void obt_prop_message(gint screen
, Window about
, Atom messagetype
,
562 glong data0
, glong data1
, glong data2
, glong data3
,
563 glong data4
, glong mask
)
565 obt_prop_message_to(obt_root(screen
), about
, messagetype
,
566 data0
, data1
, data2
, data3
, data4
, mask
);
569 void obt_prop_message_to(Window to
, Window about
,
571 glong data0
, glong data1
, glong data2
, glong data3
,
572 glong data4
, glong mask
)
575 ce
.xclient
.type
= ClientMessage
;
576 ce
.xclient
.message_type
= messagetype
;
577 ce
.xclient
.display
= obt_display
;
578 ce
.xclient
.window
= about
;
579 ce
.xclient
.format
= 32;
580 ce
.xclient
.data
.l
[0] = data0
;
581 ce
.xclient
.data
.l
[1] = data1
;
582 ce
.xclient
.data
.l
[2] = data2
;
583 ce
.xclient
.data
.l
[3] = data3
;
584 ce
.xclient
.data
.l
[4] = data4
;
585 XSendEvent(obt_display
, to
, FALSE
, mask
, &ce
);