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();
321 /*! Returns one or more UTF-8 encoded strings from the text property.
322 @param tprop The XTextProperty to convert into UTF-8 string(s).
323 @param type The type which specifies the format that the text must meet, or
324 0 to allow any valid characters that can be converted to UTF-8 through.
325 @param max The maximum number of strings to return. -1 to return them all.
326 @return If max is 1, then this returns a gchar* with the single string.
327 Otherwise, this returns a gchar** of no more than max strings (or all
328 strings read, if max is negative). If an error occurs, NULL is returned.
330 static void* convert_text_property(XTextProperty
*tprop
,
331 ObtPropTextType type
, gint max
)
338 const gboolean return_single
= (max
== 1);
340 gchar
**strlist
= NULL
;
341 gchar
*single
[1] = { NULL
};
342 gchar
**retlist
= single
; /* single is used when max == 1 */
345 /* Read each string in the text property and store a pointer to it in
346 retlist. These pointers point into the X data structures directly.
348 Then we will convert them to UTF-8, and replace the retlist pointer with
351 if (tprop
->encoding
== OBT_PROP_ATOM(COMPOUND_TEXT
))
354 ok
= (XmbTextPropertyToTextList(
355 obt_display
, tprop
, &strlist
, &n_strs
) == Success
);
358 n_strs
= MIN(max
, n_strs
);
360 retlist
= g_new0(gchar
*, n_strs
+1);
362 for (i
= 0; i
< n_strs
; ++i
)
363 retlist
[i
] = strlist
[i
];
366 else if (tprop
->encoding
== OBT_PROP_ATOM(UTF8_STRING
) ||
367 tprop
->encoding
== OBT_PROP_ATOM(STRING
))
369 gchar
*p
; /* iterator */
371 if (tprop
->encoding
== OBT_PROP_ATOM(STRING
))
377 /* First, count the number of strings. Then make a structure for them
378 and copy pointers to them into it. */
379 p
= (gchar
*)tprop
->value
;
381 while (p
< (gchar
*)tprop
->value
+ tprop
->nitems
) {
382 p
+= strlen(p
) + 1; /* next string */
387 n_strs
= MIN(max
, n_strs
);
389 retlist
= g_new0(gchar
*, n_strs
+1);
391 p
= (gchar
*)tprop
->value
;
392 for (i
= 0; i
< n_strs
; ++i
) {
394 p
+= strlen(p
) + 1; /* next string */
399 if (!(ok
&& retlist
)) {
400 if (strlist
) XFreeStringList(strlist
);
404 /* convert each element in retlist to UTF-8, and replace it. */
405 for (i
= 0; i
< n_strs
; ++i
) {
406 if (encoding
== UTF8
) {
407 const gchar
*end
; /* the first byte past the valid data */
409 g_utf8_validate(retlist
[i
], -1, &end
);
410 retlist
[i
] = g_strndup(retlist
[i
], end
-retlist
[i
]);
412 else if (encoding
== LOCALE
) {
413 gsize nvalid
; /* the number of valid bytes at the front of the
415 gchar
*utf
; /* the string converted into utf8 */
417 utf
= g_locale_to_utf8(retlist
[i
], -1, &nvalid
, NULL
, NULL
);
419 utf
= g_locale_to_utf8(retlist
[i
], nvalid
, NULL
, NULL
, NULL
);
423 else { /* encoding == LATIN1 */
424 gsize nvalid
; /* the number of valid bytes at the front of the
426 gchar
*utf
; /* the string converted into utf8 */
427 gchar
*p
; /* iterator */
429 /* look for invalid characters */
430 for (p
= retlist
[i
], nvalid
= 0; *p
; ++p
, ++nvalid
) {
431 /* The only valid control characters are TAB(HT)=9 and
433 This is defined in ICCCM section 2:
434 http://tronche.com/gui/x/icccm/sec-2.html.
435 See a definition of the latin1 codepage here:
436 http://en.wikipedia.org/wiki/ISO/IEC_8859-1.
437 The above page includes control characters in the table,
438 which we must explicitly exclude, as the g_convert function
439 will happily take them.
441 const register guchar c
= (guchar
)*p
; /* unsigned value at p */
442 if ((c
< 32 && c
!= 9 && c
!= 10) || (c
>= 127 && c
<= 160))
443 break; /* found a control character that isn't allowed */
445 if (type
== OBT_PROP_TEXT_STRING_NO_CC
&& c
< 32)
446 break; /* absolutely no control characters are allowed */
448 if (type
== OBT_PROP_TEXT_STRING_XPCS
) {
449 const gboolean valid
= (
450 (c
>= 32 && c
< 128) || c
== 9 || c
== 10);
452 break; /* strict whitelisting for XPCS */
455 /* look for invalid latin1 characters */
456 utf
= g_convert(retlist
[i
], nvalid
, "utf-8", "iso-8859-1",
457 &nvalid
, NULL
, NULL
);
459 utf
= g_convert(retlist
[i
], nvalid
, "utf-8", "iso-8859-1",
466 if (strlist
) XFreeStringList(strlist
);
473 gboolean
obt_prop_get32(Window win
, Atom prop
, Atom type
, guint32
*ret
)
475 return get_prealloc(win
, prop
, type
, 32, (guchar
*)ret
, 1);
478 gboolean
obt_prop_get_array32(Window win
, Atom prop
, Atom type
, guint32
**ret
,
481 return get_all(win
, prop
, type
, 32, (guchar
**)ret
, nret
);
484 gboolean
obt_prop_get_text(Window win
, Atom prop
, ObtPropTextType type
,
489 gboolean ret
= FALSE
;
491 if (get_text_property(win
, prop
, &tprop
, type
)) {
492 str
= (gchar
*)convert_text_property(&tprop
, type
, 1);
503 gboolean
obt_prop_get_array_text(Window win
, Atom prop
,
504 ObtPropTextType type
,
505 gchar
***ret_strings
)
509 gboolean ret
= FALSE
;
511 if (get_text_property(win
, prop
, &tprop
, type
)) {
512 strs
= (gchar
**)convert_text_property(&tprop
, type
, -1);
523 void obt_prop_set32(Window win
, Atom prop
, Atom type
, gulong val
)
525 XChangeProperty(obt_display
, win
, prop
, type
, 32, PropModeReplace
,
529 void obt_prop_set_array32(Window win
, Atom prop
, Atom type
, gulong
*val
,
532 XChangeProperty(obt_display
, win
, prop
, type
, 32, PropModeReplace
,
536 void obt_prop_set_text(Window win
, Atom prop
, const gchar
*val
)
538 XChangeProperty(obt_display
, win
, prop
, OBT_PROP_ATOM(UTF8_STRING
), 8,
539 PropModeReplace
, (const guchar
*)val
, strlen(val
));
542 void obt_prop_set_array_text(Window win
, Atom prop
, const gchar
*const *strs
)
545 gchar
const *const *s
;
547 str
= g_string_sized_new(0);
548 for (s
= strs
; *s
; ++s
) {
549 str
= g_string_append(str
, *s
);
550 str
= g_string_append_c(str
, '\0');
552 XChangeProperty(obt_display
, win
, prop
, OBT_PROP_ATOM(UTF8_STRING
), 8,
553 PropModeReplace
, (guchar
*)str
->str
, str
->len
);
554 g_string_free(str
, TRUE
);
557 void obt_prop_erase(Window win
, Atom prop
)
559 XDeleteProperty(obt_display
, win
, prop
);
562 void obt_prop_message(gint screen
, Window about
, Atom messagetype
,
563 glong data0
, glong data1
, glong data2
, glong data3
,
564 glong data4
, glong mask
)
566 obt_prop_message_to(obt_root(screen
), about
, messagetype
,
567 data0
, data1
, data2
, data3
, data4
, mask
);
570 void obt_prop_message_to(Window to
, Window about
,
572 glong data0
, glong data1
, glong data2
, glong data3
,
573 glong data4
, glong mask
)
576 ce
.xclient
.type
= ClientMessage
;
577 ce
.xclient
.message_type
= messagetype
;
578 ce
.xclient
.display
= obt_display
;
579 ce
.xclient
.window
= about
;
580 ce
.xclient
.format
= 32;
581 ce
.xclient
.data
.l
[0] = data0
;
582 ce
.xclient
.data
.l
[1] = data1
;
583 ce
.xclient
.data
.l
[2] = data2
;
584 ce
.xclient
.data
.l
[3] = data3
;
585 ce
.xclient
.data
.l
[4] = data4
;
586 XSendEvent(obt_display
, to
, FALSE
, mask
, &ce
);