]> icculus.org git repositories - dana/openbox.git/blob - otk/property.cc
restart and catch errors appropriately
[dana/openbox.git] / otk / property.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2
3 #include "config.h"
4
5 #include "property.hh"
6 #include "display.hh"
7
8 extern "C" {
9 #include <X11/Xatom.h>
10 }
11
12 #include <algorithm>
13 #include <cassert>
14
15 namespace otk {
16
17 Atoms Property::atoms;
18
19 static Atom create(char *name) { return XInternAtom(**display, name, false); }
20
21 void Property::initialize()
22 {
23   assert(display);
24
25   // make sure asserts fire if there is a problem
26   memset(&atoms, 0, sizeof(Atoms));
27
28   atoms.cardinal = XA_CARDINAL;
29   atoms.window = XA_WINDOW;
30   atoms.pixmap = XA_PIXMAP;
31   atoms.atom = XA_ATOM;
32   atoms.string = XA_STRING;
33   atoms.utf8 = create("UTF8_STRING");
34   
35   atoms.openbox_pid = create("_OPENBOX_PID");
36
37   atoms.wm_colormap_windows = create("WM_COLORMAP_WINDOWS");
38   atoms.wm_protocols = create("WM_PROTOCOLS");
39   atoms.wm_state = create("WM_STATE");
40   atoms.wm_change_state = create("WM_CHANGE_STATE");
41   atoms.wm_delete_window = create("WM_DELETE_WINDOW");
42   atoms.wm_take_focus = create("WM_TAKE_FOCUS");
43   atoms.wm_name = create("WM_NAME");
44   atoms.wm_icon_name = create("WM_ICON_NAME");
45   atoms.wm_class = create("WM_CLASS");
46   atoms.wm_window_role = create("WM_WINDOW_ROLE");
47   atoms.motif_wm_hints = create("_MOTIF_WM_HINTS");
48
49   atoms.openbox_show_root_menu = create("_OPENBOX_SHOW_ROOT_MENU");
50   atoms.openbox_show_workspace_menu = create("_OPENBOX_SHOW_WORKSPACE_MENU");
51
52   atoms.net_supported = create("_NET_SUPPORTED");
53   atoms.net_client_list = create("_NET_CLIENT_LIST");
54   atoms.net_client_list_stacking = create("_NET_CLIENT_LIST_STACKING");
55   atoms.net_number_of_desktops = create("_NET_NUMBER_OF_DESKTOPS");
56   atoms.net_desktop_geometry = create("_NET_DESKTOP_GEOMETRY");
57   atoms.net_desktop_viewport = create("_NET_DESKTOP_VIEWPORT");
58   atoms.net_current_desktop = create("_NET_CURRENT_DESKTOP");
59   atoms.net_desktop_names = create("_NET_DESKTOP_NAMES");
60   atoms.net_active_window = create("_NET_ACTIVE_WINDOW");
61   atoms.net_workarea = create("_NET_WORKAREA");
62   atoms.net_supporting_wm_check = create("_NET_SUPPORTING_WM_CHECK");
63 //  atoms.net_virtual_roots = create("_NET_VIRTUAL_ROOTS");
64   atoms.net_desktop_layout = create("_NET_DESKTOP_LAYOUT");
65   atoms.net_showing_desktop = create("_NET_SHOWING_DESKTOP");
66
67   atoms.net_close_window = create("_NET_CLOSE_WINDOW");
68   atoms.net_wm_moveresize = create("_NET_WM_MOVERESIZE");
69
70 //  atoms.net_properties = create("_NET_PROPERTIES");
71   atoms.net_wm_name = create("_NET_WM_NAME");
72   atoms.net_wm_visible_name = create("_NET_WM_VISIBLE_NAME");
73   atoms.net_wm_icon_name = create("_NET_WM_ICON_NAME");
74   atoms.net_wm_visible_icon_name = create("_NET_WM_VISIBLE_ICON_NAME");
75   atoms.net_wm_desktop = create("_NET_WM_DESKTOP");
76   atoms.net_wm_window_type = create("_NET_WM_WINDOW_TYPE");
77   atoms.net_wm_state = create("_NET_WM_STATE");
78   atoms.net_wm_strut = create("_NET_WM_STRUT");
79 //  atoms.net_wm_icon_geometry = create("_NET_WM_ICON_GEOMETRY");
80   atoms.net_wm_icon = create("_NET_WM_ICON");
81 //  atoms.net_wm_pid = create("_NET_WM_PID");
82 //  atoms.net_wm_handled_icons = create("_NET_WM_HANDLED_ICONS");
83   atoms.net_wm_allowed_actions = create("_NET_WM_ALLOWED_ACTIONS");
84
85 //  atoms.net_wm_ping = create("_NET_WM_PING");
86   
87   atoms.net_wm_window_type_desktop = create("_NET_WM_WINDOW_TYPE_DESKTOP");
88   atoms.net_wm_window_type_dock = create("_NET_WM_WINDOW_TYPE_DOCK");
89   atoms.net_wm_window_type_toolbar = create("_NET_WM_WINDOW_TYPE_TOOLBAR");
90   atoms.net_wm_window_type_menu = create("_NET_WM_WINDOW_TYPE_MENU");
91   atoms.net_wm_window_type_utility = create("_NET_WM_WINDOW_TYPE_UTILITY");
92   atoms.net_wm_window_type_splash = create("_NET_WM_WINDOW_TYPE_SPLASH");
93   atoms.net_wm_window_type_dialog = create("_NET_WM_WINDOW_TYPE_DIALOG");
94   atoms.net_wm_window_type_normal = create("_NET_WM_WINDOW_TYPE_NORMAL");
95
96   atoms.net_wm_moveresize_size_topleft =
97     create("_NET_WM_MOVERESIZE_SIZE_TOPLEFT");
98   atoms.net_wm_moveresize_size_topright =
99     create("_NET_WM_MOVERESIZE_SIZE_TOPRIGHT");
100   atoms.net_wm_moveresize_size_bottomleft =
101     create("_NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT");
102   atoms.net_wm_moveresize_size_bottomright =
103     create("_NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT");
104   atoms.net_wm_moveresize_move =
105     create("_NET_WM_MOVERESIZE_MOVE");
106  
107   atoms.net_wm_action_move = create("_NET_WM_ACTION_MOVE");
108   atoms.net_wm_action_resize = create("_NET_WM_ACTION_RESIZE");
109   atoms.net_wm_action_minimize = create("_NET_WM_ACTION_MINIMIZE");
110   atoms.net_wm_action_shade = create("_NET_WM_ACTION_SHADE");
111   atoms.net_wm_action_stick = create("_NET_WM_ACTION_STICK");
112   atoms.net_wm_action_maximize_horz = create("_NET_WM_ACTION_MAXIMIZE_HORZ");
113   atoms.net_wm_action_maximize_vert = create("_NET_WM_ACTION_MAXIMIZE_VERT");
114   atoms.net_wm_action_fullscreen = create("_NET_WM_ACTION_FULLSCREEN");
115   atoms.net_wm_action_change_desktop =
116     create("_NET_WM_ACTION_CHANGE_DESKTOP");
117   atoms.net_wm_action_close = create("_NET_WM_ACTION_CLOSE");
118     
119   atoms.net_wm_state_modal = create("_NET_WM_STATE_MODAL");
120   atoms.net_wm_state_sticky = create("_NET_WM_STATE_STICKY");
121   atoms.net_wm_state_maximized_vert = create("_NET_WM_STATE_MAXIMIZED_VERT");
122   atoms.net_wm_state_maximized_horz = create("_NET_WM_STATE_MAXIMIZED_HORZ");
123   atoms.net_wm_state_shaded = create("_NET_WM_STATE_SHADED");
124   atoms.net_wm_state_skip_taskbar = create("_NET_WM_STATE_SKIP_TASKBAR");
125   atoms.net_wm_state_skip_pager = create("_NET_WM_STATE_SKIP_PAGER");
126   atoms.net_wm_state_hidden = create("_NET_WM_STATE_HIDDEN");
127   atoms.net_wm_state_fullscreen = create("_NET_WM_STATE_FULLSCREEN");
128   atoms.net_wm_state_above = create("_NET_WM_STATE_ABOVE");
129   atoms.net_wm_state_below = create("_NET_WM_STATE_BELOW");
130   
131   atoms.net_wm_state_add = 1;
132   atoms.net_wm_state_remove = 0;
133   atoms.net_wm_state_toggle = 2;
134
135   atoms.kde_net_system_tray_windows = create("_KDE_NET_SYSTEM_TRAY_WINDOWS");
136   atoms.kde_net_wm_system_tray_window_for =
137     create("_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR");
138   atoms.kde_net_wm_window_type_override =
139     create("_KDE_NET_WM_WINDOW_TYPE_OVERRIDE");
140
141   atoms.kwm_win_icon = create("KWM_WIN_ICON");
142   
143   atoms.rootpmapid = create("_XROOTPMAP_ID");
144   atoms.esetrootid = create("ESETROOT_PMAP_ID");
145
146   atoms.openbox_premax = create("_OPENBOX_PREMAX");
147   atoms.openbox_active_window = create("_OPENBOX_ACTIVE_WINDOW");
148   atoms.openbox_restack_window = create("_OPENBOX_RESTACK_WINDOW");
149 }
150
151 void Property::set(Window win, Atom atom, Atom type, unsigned char* data,
152                    int size, int nelements, bool append)
153 {
154   assert(win != None); assert(atom != None); assert(type != None);
155   assert(nelements == 0 || (nelements > 0 && data != (unsigned char *) 0));
156   assert(size == 8 || size == 16 || size == 32);
157   XChangeProperty(**display, win, atom, type, size,
158                   (append ? PropModeAppend : PropModeReplace),
159                   data, nelements);
160 }
161
162 void Property::set(Window win, Atom atom, Atom type, unsigned long value)
163 {
164   set(win, atom, type, (unsigned char*) &value, 32, 1, false);
165 }
166
167 void Property::set(Window win, Atom atom, Atom type, unsigned long value[],
168                    int elements)
169 {
170   set(win, atom, type, (unsigned char*) value, 32, elements, false);
171 }
172
173 void Property::set(Window win, Atom atom, StringType type,
174                    const ustring &value)
175 {
176   Atom t;
177   switch (type) {
178   case ascii: t = atoms.string; assert(!value.utf8()); break;
179   case utf8:  t = atoms.utf8;   assert(value.utf8());  break;
180   default: assert(false); return; // unhandled StringType
181   }
182
183   // add 1 to the size to include the trailing null
184   set(win, atom, t, (unsigned char*) value.c_str(), 8, value.bytes() + 1,
185       false);
186 }
187
188 void Property::set(Window win, Atom atom, StringType type,
189                      const StringVect &strings)
190 {
191   Atom t;
192   bool u; // utf8 encoded?
193   switch (type) {
194   case ascii: t = atoms.string; u = false; break;
195   case utf8:  t = atoms.utf8;   u = true;  break;
196   default: assert(false); return; // unhandled StringType
197   }
198
199   ustring value(u);
200
201   StringVect::const_iterator it = strings.begin();
202   const StringVect::const_iterator end = strings.end();
203   for (; it != end; ++it) {
204     assert(it->utf8() == u); // the ustring is encoded correctly?
205     value += *it;
206     value += '\0';
207   }
208
209   // add 1 to the size to include the trailing null
210   set(win, atom, t, (unsigned char*)value.c_str(), 8,
211       value.bytes() + 1, false);
212 }
213
214 bool Property::get(Window win, Atom atom, Atom type, unsigned long *nelements,
215                    unsigned char **value, int size)
216 {
217   assert(win != None); assert(atom != None); assert(type != None);
218   assert(size == 8 || size == 16 || size == 32);
219   unsigned char *c_val = 0;        // value alloc'd in Xlib, must be XFree()d
220   Atom ret_type;
221   int ret_size;
222   unsigned long ret_bytes;
223   int result;
224   bool ret = false;
225
226   // try get the first element
227   result = XGetWindowProperty(**display, win, atom, 0l, 1l,
228                               false, AnyPropertyType, &ret_type, &ret_size,
229                               nelements, &ret_bytes, &c_val);
230   ret = (result == Success && ret_type == type && ret_size == size &&
231          *nelements > 0);
232   if (ret) {
233     if (ret_bytes == 0) {
234       // we got the whole property's value
235       *value = new unsigned char[*nelements * size/8 + 1];
236       memcpy(*value, c_val, *nelements * size/8 + 1);
237     } else {
238       // get the entire property since it is larger than one long
239       XFree(c_val);
240       // the number of longs that need to be retreived to get the property's
241       // entire value. The last + 1 is the first long that we retrieved above.
242       long remain = (ret_bytes - 1)/sizeof(long) + 1 + 1;
243       result = XGetWindowProperty(**display, win, atom, 0l,
244                                   remain, false, type, &ret_type, &ret_size,
245                                   nelements, &ret_bytes, &c_val);
246       ret = (result == Success && ret_type == type && ret_size == size);
247       /*
248         If the property has changed type/size, or has grown since our first
249         read of it, then stop here and try again. If it shrank, then this will
250         still work.
251       */
252       if (! ret) {
253         return get(win, atom, type, nelements, value, size);
254       }
255   
256       *value = new unsigned char[*nelements * size/8 + 1];
257       memcpy(*value, c_val, *nelements * size/8 + 1);
258     }    
259   }
260   if (c_val) XFree(c_val);
261   return ret;
262 }
263
264 bool Property::get(Window win, Atom atom, Atom type, unsigned long *nelements,
265                    unsigned long **value)
266 {
267   return get(win, atom, type, nelements, (unsigned char**) value, 32);
268 }
269
270 bool Property::get(Window win, Atom atom, Atom type, unsigned long *value)
271 {
272   unsigned long *temp;
273   unsigned long num;
274   if (! get(win, atom, type, &num, (unsigned char **) &temp, 32))
275     return false;
276   if (num >= 1) {
277     *value = temp[0];
278     delete [] temp;
279     return true;
280   }
281   return false;
282 }
283
284 bool Property::get(Window win, Atom atom, StringType type, ustring *value)
285 {
286   unsigned long n;
287   StringVect s;
288
289   if (get(win, atom, type, &n, &s) && n > 0) {
290     *value = s[0];
291     return true;
292   }
293   return false;
294 }
295
296 bool Property::get(Window win, Atom atom, StringType type,
297                    unsigned long *nelements, StringVect *strings)
298 {
299   Atom t;
300   bool u; // utf8 encoded?
301   switch (type) {
302   case ascii: t = atoms.string; u = false; break;
303   case utf8:  t = atoms.utf8;   u = true;  break;
304   default: assert(false); return false; // unhandled StringType
305   }
306   
307   unsigned char *value;
308   unsigned long elements;;
309   if (!get(win, atom, t, &elements, &value, 8) || elements < 1)
310     return false;
311
312   std::string s((char*)value, elements);
313   delete [] value;
314
315   std::string::const_iterator it = s.begin(), end = s.end();
316   unsigned long num = 0;
317   while(true) {
318     std::string::const_iterator tmp = it; // current string.begin()
319     it = std::find(tmp, end, '\0');       // look for null between tmp and end
320     strings->push_back(std::string(tmp, it));   // s[tmp:it)
321     strings->back().setUtf8(u);
322     ++num;
323     if (it == end) break;
324     ++it;
325     if (it == end) break;
326   }
327
328   *nelements = num;
329
330   return true;
331 }
332
333
334 /*
335  * Removes a property entirely from a window.
336  */
337 void Property::erase(Window win, Atom atom)
338 {
339   XDeleteProperty(**display, win, atom);
340 }
341
342 }