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