1 /**
2  * @file hint.c
3  * @author Joe Wingbermuehle
4  * @date 2004-2006
5  *
6  * @brief Functions for reading and writing X properties.
7  *
8  */
9 
10 #include "jwm.h"
11 #include "hint.h"
12 #include "client.h"
13 #include "desktop.h"
14 #include "misc.h"
15 #include "font.h"
16 #include "settings.h"
17 
18 #include <X11/Xlibint.h>
19 
20 /* MWM Defines */
21 #define MWM_HINTS_FUNCTIONS   (1L << 0)
22 #define MWM_HINTS_DECORATIONS (1L << 1)
23 #define MWM_HINTS_INPUT_MODE  (1L << 2)
24 #define MWM_HINTS_STATUS      (1L << 3)
25 
26 #define MWM_FUNC_ALL      (1L << 0)
27 #define MWM_FUNC_RESIZE   (1L << 1)
28 #define MWM_FUNC_MOVE     (1L << 2)
29 #define MWM_FUNC_MINIMIZE (1L << 3)
30 #define MWM_FUNC_MAXIMIZE (1L << 4)
31 #define MWM_FUNC_CLOSE    (1L << 5)
32 
33 #define MWM_DECOR_ALL      (1L << 0)
34 #define MWM_DECOR_BORDER   (1L << 1)
35 #define MWM_DECOR_RESIZEH  (1L << 2)
36 #define MWM_DECOR_TITLE    (1L << 3)
37 #define MWM_DECOR_MENU     (1L << 4)
38 #define MWM_DECOR_MINIMIZE (1L << 5)
39 #define MWM_DECOR_MAXIMIZE (1L << 6)
40 
41 #define MWM_INPUT_MODELESS                  0
42 #define MWM_INPUT_PRIMARY_APPLICATION_MODAL 1
43 #define MWM_INPUT_SYSTEM_MODAL              2
44 #define MWM_INPUT_FULL_APPLICATION_MODAL    3
45 
46 #define MWM_TEAROFF_WINDOW (1L << 0)
47 
48 typedef struct {
49 
50    unsigned long flags;
51    unsigned long functions;
52    unsigned long decorations;
53    long          inputMode;
54    unsigned long status;
55 
56 } PropMwmHints;
57 
58 typedef struct {
59    Atom *atom;
60    const char *name;
61 } ProtocolNode;
62 
63 typedef struct {
64    Atom *atom;
65    const char *name;
66 } AtomNode;
67 
68 Atom atoms[ATOM_COUNT];
69 
70 const char jwmRestart[]       = "_JWM_RESTART";
71 const char jwmExit[]          = "_JWM_EXIT";
72 const char jwmReload[]        = "_JWM_RELOAD";
73 const char managerProperty[]  = "MANAGER";
74 
75 static const AtomNode atomList[] = {
76 
77    { &atoms[ATOM_COMPOUND_TEXT],             "COMPOUND_TEXT"               },
78    { &atoms[ATOM_UTF8_STRING],               "UTF8_STRING"                 },
79    { &atoms[ATOM_XROOTPMAP_ID],              "_XROOTPMAP_ID"               },
80    { &atoms[ATOM_MANAGER],                   &managerProperty[0]           },
81 
82    { &atoms[ATOM_WM_STATE],                  "WM_STATE"                    },
83    { &atoms[ATOM_WM_PROTOCOLS],              "WM_PROTOCOLS"                },
84    { &atoms[ATOM_WM_DELETE_WINDOW],          "WM_DELETE_WINDOW"            },
85    { &atoms[ATOM_WM_TAKE_FOCUS],             "WM_TAKE_FOCUS"               },
86    { &atoms[ATOM_WM_CHANGE_STATE],           "WM_CHANGE_STATE"             },
87    { &atoms[ATOM_WM_COLORMAP_WINDOWS],       "WM_COLORMAP_WINDOWS"         },
88 
89    { &atoms[ATOM_NET_SUPPORTED],             "_NET_SUPPORTED"              },
90    { &atoms[ATOM_NET_NUMBER_OF_DESKTOPS],    "_NET_NUMBER_OF_DESKTOPS"     },
91    { &atoms[ATOM_NET_DESKTOP_NAMES],         "_NET_DESKTOP_NAMES"          },
92    { &atoms[ATOM_NET_DESKTOP_GEOMETRY],      "_NET_DESKTOP_GEOMETRY"       },
93    { &atoms[ATOM_NET_DESKTOP_VIEWPORT],      "_NET_DESKTOP_VIEWPORT"       },
94    { &atoms[ATOM_NET_CURRENT_DESKTOP],       "_NET_CURRENT_DESKTOP"        },
95    { &atoms[ATOM_NET_ACTIVE_WINDOW],         "_NET_ACTIVE_WINDOW"          },
96    { &atoms[ATOM_NET_WORKAREA],              "_NET_WORKAREA"               },
97    { &atoms[ATOM_NET_SUPPORTING_WM_CHECK],   "_NET_SUPPORTING_WM_CHECK"    },
98    { &atoms[ATOM_NET_SHOWING_DESKTOP],       "_NET_SHOWING_DESKTOP"        },
99    { &atoms[ATOM_NET_FRAME_EXTENTS],         "_NET_FRAME_EXTENTS"          },
100    { &atoms[ATOM_NET_WM_DESKTOP],            "_NET_WM_DESKTOP"             },
101    { &atoms[ATOM_NET_WM_STATE],              "_NET_WM_STATE"               },
102    { &atoms[ATOM_NET_WM_STATE_STICKY],       "_NET_WM_STATE_STICKY"        },
103    { &atoms[ATOM_NET_WM_STATE_MAXIMIZED_VERT], "_NET_WM_STATE_MAXIMIZED_VERT"},
104    { &atoms[ATOM_NET_WM_STATE_MAXIMIZED_HORZ], "_NET_WM_STATE_MAXIMIZED_HORZ"},
105    { &atoms[ATOM_NET_WM_STATE_SHADED],       "_NET_WM_STATE_SHADED"        },
106    { &atoms[ATOM_NET_WM_STATE_FULLSCREEN],   "_NET_WM_STATE_FULLSCREEN"    },
107    { &atoms[ATOM_NET_WM_STATE_HIDDEN],       "_NET_WM_STATE_HIDDEN"        },
108    { &atoms[ATOM_NET_WM_STATE_SKIP_TASKBAR], "_NET_WM_STATE_SKIP_TASKBAR"  },
109    { &atoms[ATOM_NET_WM_STATE_SKIP_PAGER],   "_NET_WM_STATE_SKIP_PAGER"    },
110    { &atoms[ATOM_NET_WM_STATE_BELOW],        "_NET_WM_STATE_BELOW"         },
111    { &atoms[ATOM_NET_WM_STATE_ABOVE],        "_NET_WM_STATE_ABOVE"         },
112    { &atoms[ATOM_NET_WM_STATE_DEMANDS_ATTENTION],
113       "_NET_WM_STATE_DEMANDS_ATTENTION"},
114    { &atoms[ATOM_NET_WM_STATE_FOCUSED],      "_NET_WM_STATE_FOCUSED"       },
115    { &atoms[ATOM_NET_WM_ALLOWED_ACTIONS],    "_NET_WM_ALLOWED_ACTIONS"     },
116    { &atoms[ATOM_NET_WM_ACTION_MOVE],        "_NET_WM_ACTION_MOVE"         },
117    { &atoms[ATOM_NET_WM_ACTION_RESIZE],      "_NET_WM_ACTION_RESIZE"       },
118    { &atoms[ATOM_NET_WM_ACTION_MINIMIZE],    "_NET_WM_ACTION_MINIMIZE"     },
119    { &atoms[ATOM_NET_WM_ACTION_SHADE],       "_NET_WM_ACTION_SHADE"        },
120    { &atoms[ATOM_NET_WM_ACTION_STICK],       "_NET_WM_ACTION_STICK"        },
121    { &atoms[ATOM_NET_WM_ACTION_FULLSCREEN],  "_NET_WM_ACTION_FULLSCREEN"   },
122    { &atoms[ATOM_NET_WM_ACTION_MAXIMIZE_HORZ], "_NET_WM_ACTION_MAXIMIZE_HORZ"},
123    { &atoms[ATOM_NET_WM_ACTION_MAXIMIZE_VERT], "_NET_WM_ACTION_MAXIMIZE_VERT"},
124    { &atoms[ATOM_NET_WM_ACTION_CHANGE_DESKTOP],
125       "_NET_WM_ACTION_CHANGE_DESKTOP"},
126    { &atoms[ATOM_NET_WM_ACTION_CLOSE],       "_NET_WM_ACTION_CLOSE"        },
127    { &atoms[ATOM_NET_WM_ACTION_BELOW],       "_NET_WM_ACTION_BELOW"        },
128    { &atoms[ATOM_NET_WM_ACTION_ABOVE],       "_NET_WM_ACTION_ABOVE"        },
129    { &atoms[ATOM_NET_CLOSE_WINDOW],          "_NET_CLOSE_WINDOW"           },
130    { &atoms[ATOM_NET_MOVERESIZE_WINDOW],     "_NET_MOVERESIZE_WINDOW"      },
131    { &atoms[ATOM_NET_RESTACK_WINDOW],        "_NET_RESTACK_WINDOW"         },
132    { &atoms[ATOM_NET_REQUEST_FRAME_EXTENTS], "_NET_REQUEST_FRAME_EXTENTS"  },
133    { &atoms[ATOM_NET_WM_PID],                "_NET_WM_PID"                 },
134    { &atoms[ATOM_NET_WM_NAME],               "_NET_WM_NAME"                },
135    { &atoms[ATOM_NET_WM_VISIBLE_NAME],       "_NET_WM_VISIBLE_NAME"        },
136    { &atoms[ATOM_NET_WM_HANDLED_ICONS],      "_NET_WM_HANDLED_ICONS"       },
137    { &atoms[ATOM_NET_WM_ICON],               "_NET_WM_ICON"                },
138    { &atoms[ATOM_NET_WM_ICON_NAME],          "_NET_WM_ICON_NAME"           },
139    { &atoms[ATOM_NET_WM_USER_TIME],          "_NET_WM_USER_TIME"           },
140    { &atoms[ATOM_NET_WM_USER_TIME_WINDOW],   "_NET_WM_USER_TIME_WINDOW"    },
141    { &atoms[ATOM_NET_WM_VISIBLE_ICON_NAME],  "_NET_WM_VISIBLE_ICON_NAME"   },
142    { &atoms[ATOM_NET_WM_WINDOW_TYPE],        "_NET_WM_WINDOW_TYPE"         },
143    { &atoms[ATOM_NET_WM_WINDOW_TYPE_DESKTOP],"_NET_WM_WINDOW_TYPE_DESKTOP" },
144    { &atoms[ATOM_NET_WM_WINDOW_TYPE_DOCK],   "_NET_WM_WINDOW_TYPE_DOCK"    },
145    { &atoms[ATOM_NET_WM_WINDOW_TYPE_SPLASH], "_NET_WM_WINDOW_TYPE_SPLASH"  },
146    { &atoms[ATOM_NET_WM_WINDOW_TYPE_DIALOG], "_NET_WM_WINDOW_TYPE_DIALOG"  },
147    { &atoms[ATOM_NET_WM_WINDOW_TYPE_NORMAL], "_NET_WM_WINDOW_TYPE_NORMAL"  },
148    { &atoms[ATOM_NET_WM_WINDOW_TYPE_MENU],   "_NET_WM_WINDOW_TYPE_MENU"    },
149    { &atoms[ATOM_NET_WM_WINDOW_TYPE_NOTIFICATION],
150       "_NET_WM_WINDOW_TYPE_NOTIFICATION" },
151    { &atoms[ATOM_NET_WM_WINDOW_TYPE_TOOLBAR], "_NET_WM_WINDOW_TYPE_TOOLBAR"},
152    { &atoms[ATOM_NET_WM_WINDOW_TYPE_UTILITY], "_NET_WM_WINDOW_TYPE_UTILITY"},
153    { &atoms[ATOM_NET_CLIENT_LIST],           "_NET_CLIENT_LIST"            },
154    { &atoms[ATOM_NET_CLIENT_LIST_STACKING],  "_NET_CLIENT_LIST_STACKING"   },
155    { &atoms[ATOM_NET_WM_STRUT_PARTIAL],      "_NET_WM_STRUT_PARTIAL"       },
156    { &atoms[ATOM_NET_WM_STRUT],              "_NET_WM_STRUT"               },
157    { &atoms[ATOM_NET_WM_WINDOW_OPACITY],     "_NET_WM_WINDOW_OPACITY"      },
158    { &atoms[ATOM_NET_WM_MOVERESIZE],         "_NET_WM_MOVERESIZE"          },
159    { &atoms[ATOM_NET_SYSTEM_TRAY_OPCODE],    "_NET_SYSTEM_TRAY_OPCODE"     },
160    { &atoms[ATOM_NET_SYSTEM_TRAY_ORIENTATION],
161       "_NET_SYSTEM_TRAY_ORIENTATION" },
162 
163    { &atoms[ATOM_MOTIF_WM_HINTS],            "_MOTIF_WM_HINTS"             },
164 
165    { &atoms[ATOM_JWM_RESTART],               &jwmRestart[0]                },
166    { &atoms[ATOM_JWM_EXIT],                  &jwmExit[0]                   },
167    { &atoms[ATOM_JWM_RELOAD],                &jwmReload[0]                 },
168    { &atoms[ATOM_JWM_WM_STATE_MAXIMIZED_TOP],
169       "_JWM_WM_STATE_MAXIMIZED_TOP" },
170    { &atoms[ATOM_JWM_WM_STATE_MAXIMIZED_BOTTOM],
171       "_JWM_WM_STATE_MAXIMIZED_BOTTOM" },
172    { &atoms[ATOM_JWM_WM_STATE_MAXIMIZED_LEFT],
173       "_JWM_WM_STATE_MAXIMIZED_LEFT" },
174    { &atoms[ATOM_JWM_WM_STATE_MAXIMIZED_RIGHT],
175       "_JWM_WM_STATE_MAXIMIZED_RIGHT" }
176 
177 };
178 
179 static char CheckShape(Window win);
180 static void WriteNetAllowed(ClientNode *np);
181 static void ReadWMState(Window win, ClientState *state);
182 static void ReadMotifHints(Window win, ClientState *state);
183 
184 /** Set root hints and intern atoms. */
StartupHints(void)185 void StartupHints(void)
186 {
187 
188    unsigned long *array;
189    char *data;
190    Atom *supported;
191    Window win;
192    unsigned int x;
193    unsigned int count;
194 
195    /* Determine how much space we will need on the stack and allocate it. */
196    count = 0;
197    for(x = 0; x < settings.desktopCount; x++) {
198       count += strlen(GetDesktopName(x)) + 1;
199    }
200    if(count < 2 * sizeof(unsigned long)) {
201       count = 2 * sizeof(unsigned long);
202    }
203    if(count < ATOM_COUNT * sizeof(Atom)) {
204       count = ATOM_COUNT * sizeof(Atom);
205    }
206    data = AllocateStack(count);
207    array = (unsigned long*)data;
208    supported = (Atom*)data;
209 
210    /* Intern the atoms */
211    for(x = 0; x < ATOM_COUNT; x++) {
212       *atomList[x].atom = JXInternAtom(display, atomList[x].name, False);
213    }
214 
215    /* _NET_SUPPORTED */
216    for(x = FIRST_NET_ATOM; x <= LAST_NET_ATOM; x++) {
217       supported[x - FIRST_NET_ATOM] = atoms[x];
218    }
219    JXChangeProperty(display, rootWindow, atoms[ATOM_NET_SUPPORTED],
220                     XA_ATOM, 32, PropModeReplace, (unsigned char*)supported,
221                     LAST_NET_ATOM - FIRST_NET_ATOM + 1);
222 
223    /* _NET_NUMBER_OF_DESKTOPS */
224    SetCardinalAtom(rootWindow, ATOM_NET_NUMBER_OF_DESKTOPS,
225                    settings.desktopCount);
226 
227    /* _NET_DESKTOP_NAMES */
228    count = 0;
229    for(x = 0; x < settings.desktopCount; x++) {
230       const char *name = GetDesktopName(x);
231       const unsigned len = strlen(name);
232       memcpy(&data[count], name, len + 1);
233       count += len + 1;
234    }
235    JXChangeProperty(display, rootWindow, atoms[ATOM_NET_DESKTOP_NAMES],
236                     atoms[ATOM_UTF8_STRING], 8, PropModeReplace,
237                     (unsigned char*)data, count);
238 
239    /* _NET_DESKTOP_GEOMETRY */
240    array[0] = rootWidth;
241    array[1] = rootHeight;
242    JXChangeProperty(display, rootWindow, atoms[ATOM_NET_DESKTOP_GEOMETRY],
243                     XA_CARDINAL, 32, PropModeReplace,
244                     (unsigned char*)array, 2);
245 
246    /* _NET_DESKTOP_VIEWPORT */
247    array[0] = 0;
248    array[1] = 0;
249    JXChangeProperty(display, rootWindow, atoms[ATOM_NET_DESKTOP_VIEWPORT],
250                     XA_CARDINAL, 32, PropModeReplace,
251                     (unsigned char*)array, 2);
252 
253    /* _NET_WM_NAME */
254    win = supportingWindow;
255    JXChangeProperty(display, win, atoms[ATOM_NET_WM_NAME],
256                     atoms[ATOM_UTF8_STRING], 8, PropModeReplace,
257                     (unsigned char*)"JWM", 3);
258 
259    /* _NET_WM_PID */
260    array[0] = getpid();
261    JXChangeProperty(display, win, atoms[ATOM_NET_WM_PID],
262                     XA_CARDINAL, 32, PropModeReplace,
263                     (unsigned char*)array, 1);
264 
265    /* _NET_SUPPORTING_WM_CHECK */
266    SetWindowAtom(rootWindow, ATOM_NET_SUPPORTING_WM_CHECK, win);
267    SetWindowAtom(win, ATOM_NET_SUPPORTING_WM_CHECK, win);
268 
269    ReleaseStack(data);
270 
271 }
272 
273 /** Determine the current desktop. */
ReadCurrentDesktop(void)274 void ReadCurrentDesktop(void)
275 {
276    unsigned long temp;
277    currentDesktop = 0;
278    if(GetCardinalAtom(rootWindow, ATOM_NET_CURRENT_DESKTOP, &temp)) {
279       ChangeDesktop(temp);
280    } else {
281       SetCardinalAtom(rootWindow, ATOM_NET_CURRENT_DESKTOP, currentDesktop);
282    }
283 }
284 
285 /** Read client hints.
286  * This is called while the client is being added to management.
287  */
ReadClientInfo(ClientNode * np,char alreadyMapped)288 void ReadClientInfo(ClientNode *np, char alreadyMapped)
289 {
290 
291    Status status;
292    ClientNode *pp;
293 
294    Assert(np);
295 
296    ReadWMName(np);
297    ReadWMClass(np);
298    ReadWMNormalHints(np);
299    ReadWMColormaps(np);
300 
301    status = JXGetTransientForHint(display, np->window, &np->owner);
302    if(!status) {
303       np->owner = None;
304    }
305 
306    /* Read the window state. */
307    np->state = ReadWindowState(np->window, alreadyMapped);
308    if(np->minWidth == np->maxWidth && np->minHeight == np->maxHeight) {
309       np->state.border &= ~BORDER_RESIZE;
310       np->state.border &= ~BORDER_MAX;
311       if(np->minWidth * np->xinc >= rootWidth
312          && np->minHeight * np->yinc >= rootHeight) {
313          np->state.status |= STAT_FULLSCREEN;
314       }
315    }
316 
317    /* Make sure this client is on at least as high of a layer
318     * as its owner. */
319    if(np->owner != None) {
320       pp = FindClientByWindow(np->owner);
321       if(pp) {
322          np->state.layer = Max(pp->state.layer, np->state.layer);
323       }
324    }
325 
326 }
327 
328 /** Write the window state hint for a client. */
WriteState(ClientNode * np)329 void WriteState(ClientNode *np)
330 {
331    unsigned long data[2];
332 
333    if(np->state.status & STAT_MAPPED) {
334       data[0] = NormalState;
335    } else if(np->state.status & STAT_MINIMIZED) {
336       data[0] = IconicState;
337    } else if(np->state.status & STAT_SHADED) {
338       data[0] = NormalState;
339    } else {
340       data[0] = WithdrawnState;
341    }
342    data[1] = None;
343 
344    if(data[0] == WithdrawnState) {
345       JXDeleteProperty(display, np->window, atoms[ATOM_WM_STATE]);
346    } else {
347       JXChangeProperty(display, np->window, atoms[ATOM_WM_STATE],
348                        atoms[ATOM_WM_STATE], 32, PropModeReplace,
349                        (unsigned char*)data, 2);
350    }
351 
352    WriteNetState(np);
353    WriteFrameExtents(np->window, &np->state);
354    WriteNetAllowed(np);
355 }
356 
357 /** Set the opacity of a client. */
SetOpacity(ClientNode * np,unsigned int opacity,char force)358 void SetOpacity(ClientNode *np, unsigned int opacity, char force)
359 {
360    Window w;
361    if(np->state.opacity == opacity && !force) {
362       return;
363    }
364 
365    w = np->parent != None ? np->parent : np->window;
366    np->state.opacity = opacity;
367    if(opacity == 0xFFFFFFFF) {
368       JXDeleteProperty(display, w, atoms[ATOM_NET_WM_WINDOW_OPACITY]);
369    } else {
370       SetCardinalAtom(w, ATOM_NET_WM_WINDOW_OPACITY, opacity);
371    }
372 }
373 
374 /** Write the net state hint for a client. */
WriteNetState(ClientNode * np)375 void WriteNetState(ClientNode *np)
376 {
377    unsigned long values[16];
378    int index;
379 
380    Assert(np);
381 
382    /* We remove the _NET_WM_STATE and _NET_WM_DESKTOP for withdrawn windows. */
383    if(!(np->state.status & (STAT_MAPPED | STAT_MINIMIZED | STAT_SHADED))) {
384       JXDeleteProperty(display, np->window, atoms[ATOM_NET_WM_STATE]);
385       JXDeleteProperty(display, np->window, atoms[ATOM_NET_WM_DESKTOP]);
386       return;
387    }
388 
389    index = 0;
390    if(np->state.status & STAT_MINIMIZED) {
391       values[index++] = atoms[ATOM_NET_WM_STATE_HIDDEN];
392    }
393 
394    if(np->state.maxFlags & MAX_HORIZ) {
395       values[index++] = atoms[ATOM_NET_WM_STATE_MAXIMIZED_HORZ];
396    }
397    if(np->state.maxFlags & MAX_VERT) {
398       values[index++] = atoms[ATOM_NET_WM_STATE_MAXIMIZED_VERT];
399    }
400    if(np->state.maxFlags & MAX_TOP) {
401       values[index++] = atoms[ATOM_JWM_WM_STATE_MAXIMIZED_TOP];
402    }
403    if(np->state.maxFlags & MAX_BOTTOM) {
404       values[index++] = atoms[ATOM_JWM_WM_STATE_MAXIMIZED_BOTTOM];
405    }
406    if(np->state.maxFlags & MAX_LEFT) {
407       values[index++] = atoms[ATOM_JWM_WM_STATE_MAXIMIZED_LEFT];
408    }
409    if(np->state.maxFlags & MAX_RIGHT) {
410       values[index++] = atoms[ATOM_JWM_WM_STATE_MAXIMIZED_RIGHT];
411    }
412 
413    if(np->state.status & STAT_SHADED) {
414       values[index++] = atoms[ATOM_NET_WM_STATE_SHADED];
415    }
416 
417    if(np->state.status & STAT_STICKY) {
418       values[index++] = atoms[ATOM_NET_WM_STATE_STICKY];
419    }
420 
421    if(np->state.status & STAT_FULLSCREEN) {
422       values[index++] = atoms[ATOM_NET_WM_STATE_FULLSCREEN];
423    }
424 
425    if(np->state.status & STAT_NOLIST) {
426       values[index++] = atoms[ATOM_NET_WM_STATE_SKIP_TASKBAR];
427    }
428 
429    if(np->state.status & STAT_NOPAGER) {
430       values[index++] = atoms[ATOM_NET_WM_STATE_SKIP_PAGER];
431    }
432 
433    if(np->state.layer != np->state.defaultLayer) {
434       if(np->state.layer == LAYER_BELOW) {
435          values[index++] = atoms[ATOM_NET_WM_STATE_BELOW];
436       } else if(np->state.layer == LAYER_ABOVE) {
437          values[index++] = atoms[ATOM_NET_WM_STATE_ABOVE];
438       }
439    }
440 
441    if(np->state.status & STAT_URGENT) {
442       values[index++] = atoms[ATOM_NET_WM_STATE_DEMANDS_ATTENTION];
443    }
444    if(np->state.status & STAT_ACTIVE) {
445       values[index++] = atoms[ATOM_NET_WM_STATE_FOCUSED];
446    }
447 
448    JXChangeProperty(display, np->window, atoms[ATOM_NET_WM_STATE],
449                     XA_ATOM, 32, PropModeReplace,
450                     (unsigned char*)values, index);
451 }
452 
453 /** Set _NET_FRAME_EXTENTS. */
WriteFrameExtents(Window win,const ClientState * state)454 void WriteFrameExtents(Window win, const ClientState *state)
455 {
456    unsigned long values[4];
457    int north, south, east, west;
458 
459    GetBorderSize(state, &north, &south, &east, &west);
460 
461    /* left, right, top, bottom */
462    values[0] = west;
463    values[1] = east;
464    values[2] = north;
465    values[3] = south;
466 
467    JXChangeProperty(display, win, atoms[ATOM_NET_FRAME_EXTENTS],
468                     XA_CARDINAL, 32, PropModeReplace,
469                     (unsigned char*)values, 4);
470 
471 }
472 
473 /** Write the allowed action property. */
WriteNetAllowed(ClientNode * np)474 void WriteNetAllowed(ClientNode *np)
475 {
476 
477    unsigned long values[12];
478    unsigned int index;
479 
480    Assert(np);
481 
482    index = 0;
483 
484    if(np->state.border & BORDER_SHADE) {
485       values[index++] = atoms[ATOM_NET_WM_ACTION_SHADE];
486    }
487 
488    if(np->state.border & BORDER_MIN) {
489       values[index++] = atoms[ATOM_NET_WM_ACTION_MINIMIZE];
490    }
491 
492    if(np->state.border & BORDER_MAX) {
493       values[index++] = atoms[ATOM_NET_WM_ACTION_MAXIMIZE_HORZ];
494       values[index++] = atoms[ATOM_NET_WM_ACTION_MAXIMIZE_VERT];
495       values[index++] = atoms[ATOM_NET_WM_ACTION_FULLSCREEN];
496    }
497 
498    if(np->state.border & BORDER_CLOSE) {
499       values[index++] = atoms[ATOM_NET_WM_ACTION_CLOSE];
500    }
501 
502    if(np->state.border & BORDER_RESIZE) {
503       values[index++] = atoms[ATOM_NET_WM_ACTION_RESIZE];
504    }
505 
506    if(np->state.border & BORDER_MOVE) {
507       values[index++] = atoms[ATOM_NET_WM_ACTION_MOVE];
508    }
509 
510    if(!(np->state.status & STAT_STICKY)) {
511       values[index++] = atoms[ATOM_NET_WM_ACTION_CHANGE_DESKTOP];
512    }
513 
514    values[index++] = atoms[ATOM_NET_WM_ACTION_STICK];
515    values[index++] = atoms[ATOM_NET_WM_ACTION_BELOW];
516    values[index++] = atoms[ATOM_NET_WM_ACTION_ABOVE];
517 
518    JXChangeProperty(display, np->window, atoms[ATOM_NET_WM_ALLOWED_ACTIONS],
519                     XA_ATOM, 32, PropModeReplace,
520                     (unsigned char*)values, index);
521 
522 }
523 
524 /** Check if a window uses the shape extension. */
CheckShape(Window win)525 char CheckShape(Window win)
526 {
527 #ifdef USE_SHAPE
528    int shaped = 0;
529    int r1;
530    unsigned int r2;
531    if(haveShape) {
532       JXShapeSelectInput(display, win, ShapeNotifyMask);
533       XShapeQueryExtents(display, win, &shaped,
534                          &r1, &r1, &r2, &r2,
535                          &r1, &r1, &r1, &r2, &r2);
536       return shaped ? 1 : 0;
537    } else {
538       return 0;
539    }
540 #else
541    return 0;
542 #endif
543 }
544 
545 /** Read all hints needed to determine the current window state. */
ReadWindowState(Window win,char alreadyMapped)546 ClientState ReadWindowState(Window win, char alreadyMapped)
547 {
548 
549    ClientState result;
550    Status status;
551    unsigned long count, x;
552    unsigned long extra;
553    Atom realType;
554    int realFormat;
555    unsigned char *temp;
556    Atom *state;
557    unsigned long card;
558    Window utwin;
559 
560    Assert(win != None);
561 
562    result.status = STAT_MAPPED;
563    result.maxFlags = MAX_NONE;
564    result.border = BORDER_DEFAULT;
565    result.layer = LAYER_NORMAL;
566    result.defaultLayer = LAYER_NORMAL;
567    result.desktop = currentDesktop;
568    result.opacity = UINT_MAX;
569 
570    ReadWMProtocols(win, &result);
571    ReadWMHints(win, &result, alreadyMapped);
572    ReadWMState(win, &result);
573    ReadMotifHints(win, &result);
574    ReadWMOpacity(win, &result.opacity);
575 
576    /* _NET_WM_DESKTOP */
577    if(GetCardinalAtom(win, ATOM_NET_WM_DESKTOP, &card)) {
578       if(card == ~0UL) {
579          result.status |= STAT_STICKY;
580       } else if(card < settings.desktopCount) {
581          result.desktop = card;
582       } else {
583          result.desktop = settings.desktopCount - 1;
584       }
585    }
586 
587    /* _NET_WM_STATE */
588    status = JXGetWindowProperty(display, win, atoms[ATOM_NET_WM_STATE], 0, 32,
589                                 False, XA_ATOM, &realType, &realFormat,
590                                 &count, &extra, &temp);
591    if(status == Success && realFormat != 0) {
592       if(count > 0) {
593          state = (Atom*)temp;
594          for(x = 0; x < count; x++) {
595             if(state[x] == atoms[ATOM_NET_WM_STATE_STICKY]) {
596                result.status |= STAT_STICKY;
597             } else if(state[x] == atoms[ATOM_NET_WM_STATE_SHADED]) {
598                result.status |= STAT_SHADED;
599             } else if(state[x] == atoms[ATOM_NET_WM_STATE_MAXIMIZED_VERT]) {
600                result.maxFlags |= MAX_VERT;
601             } else if(state[x] == atoms[ATOM_NET_WM_STATE_MAXIMIZED_HORZ]) {
602                result.maxFlags |= MAX_HORIZ;
603             } else if(state[x] == atoms[ATOM_JWM_WM_STATE_MAXIMIZED_TOP]) {
604                result.maxFlags |= MAX_TOP;
605             } else if(state[x] == atoms[ATOM_JWM_WM_STATE_MAXIMIZED_BOTTOM]) {
606                result.maxFlags |= MAX_BOTTOM;
607             } else if(state[x] == atoms[ATOM_JWM_WM_STATE_MAXIMIZED_LEFT]) {
608                result.maxFlags |= MAX_LEFT;
609             } else if(state[x] == atoms[ATOM_JWM_WM_STATE_MAXIMIZED_RIGHT]) {
610                result.maxFlags |= MAX_RIGHT;
611             } else if(state[x] == atoms[ATOM_NET_WM_STATE_FULLSCREEN]) {
612                result.status |= STAT_FULLSCREEN;
613             } else if(state[x] == atoms[ATOM_NET_WM_STATE_HIDDEN]) {
614                result.status |= STAT_MINIMIZED;
615             } else if(state[x] == atoms[ATOM_NET_WM_STATE_SKIP_TASKBAR]) {
616                result.status |= STAT_NOLIST;
617             } else if(state[x] == atoms[ATOM_NET_WM_STATE_SKIP_PAGER]) {
618                result.status |= STAT_NOPAGER;
619             } else if(state[x] == atoms[ATOM_NET_WM_STATE_ABOVE]) {
620                result.layer = LAYER_ABOVE;
621             } else if(state[x] == atoms[ATOM_NET_WM_STATE_BELOW]) {
622                result.layer = LAYER_BELOW;
623             } else if(state[x] == atoms[ATOM_NET_WM_STATE_DEMANDS_ATTENTION]) {
624                result.status |= STAT_URGENT;
625             }
626          }
627       }
628       if(temp) {
629          JXFree(temp);
630       }
631    }
632 
633    /* _NET_WM_WINDOW_TYPE */
634    status = JXGetWindowProperty(display, win, atoms[ATOM_NET_WM_WINDOW_TYPE],
635                                 0, 32, False, XA_ATOM, &realType, &realFormat,
636                                 &count, &extra, &temp);
637    if(status == Success && realFormat != 0) {
638       /* Loop until we hit a window type we recognize. */
639       state = (Atom*)temp;
640       for(x = 0; x < count; x++) {
641          if(         state[x] == atoms[ATOM_NET_WM_WINDOW_TYPE_NORMAL]) {
642             break;
643          } else if(  state[x] == atoms[ATOM_NET_WM_WINDOW_TYPE_DESKTOP]) {
644             result.defaultLayer  = LAYER_DESKTOP;
645             result.border        = BORDER_NONE;
646             result.status       |= STAT_STICKY;
647             result.status       |= STAT_NOLIST;
648             result.status       |= STAT_NOFOCUS;
649             break;
650          } else if(  state[x] == atoms[ATOM_NET_WM_WINDOW_TYPE_DOCK]) {
651             result.border        = BORDER_NONE;
652             result.defaultLayer  = LAYER_ABOVE;
653             result.status       |= STAT_NOFOCUS;
654             break;
655          } else if(  state[x] == atoms[ATOM_NET_WM_WINDOW_TYPE_SPLASH]) {
656             result.border = BORDER_NONE;
657             break;
658          } else if(  state[x] == atoms[ATOM_NET_WM_WINDOW_TYPE_DIALOG]) {
659             result.border &= ~BORDER_MIN;
660             result.border &= ~BORDER_MAX;
661             break;
662          } else if(  state[x] == atoms[ATOM_NET_WM_WINDOW_TYPE_MENU]) {
663             result.border       &= ~BORDER_MAX;
664             result.status       |= STAT_NOLIST;
665          } else if(  state[x] == atoms[ATOM_NET_WM_WINDOW_TYPE_NOTIFICATION]) {
666             result.border        = BORDER_NONE;
667             result.status       |= STAT_NOLIST;
668             result.status       |= STAT_NOFOCUS;
669          } else if(  state[x] == atoms[ATOM_NET_WM_WINDOW_TYPE_TOOLBAR]) {
670             result.border       &= ~BORDER_MAX;
671             result.defaultLayer  = LAYER_ABOVE;
672             result.status       |= STAT_STICKY;
673             result.status       |= STAT_NOLIST;
674             result.status       |= STAT_NOFOCUS;
675          } else if(  state[x] == atoms[ATOM_NET_WM_WINDOW_TYPE_UTILITY]) {
676             result.border       &= ~BORDER_MAX;
677             result.status       |= STAT_NOFOCUS;
678          } else {
679             Debug("Unknown _NET_WM_WINDOW_TYPE: %lu", state[x]);
680          }
681       }
682       if(temp) {
683          JXFree(temp);
684       }
685    }
686 
687    /* _NET_WM_USER_TIME_WINDOW */
688    if(!GetWindowAtom(win, ATOM_NET_WM_USER_TIME_WINDOW, &utwin)) {
689       utwin = win;
690    }
691 
692    /* _NET_WM_USER_TIME */
693    if(GetCardinalAtom(utwin, ATOM_NET_WM_USER_TIME, &card)) {
694       if(card == 0) {
695          result.status |= STAT_NOFOCUS;
696       }
697    }
698 
699    /* Use the default layer if the layer wasn't set explicitly. */
700    if(result.layer == LAYER_NORMAL) {
701       result.layer = result.defaultLayer;
702    }
703 
704    /* Check if this window uses the shape extension. */
705    if(CheckShape(win)) {
706       result.status |= STAT_SHAPED;
707    }
708 
709    return result;
710 
711 }
712 
713 /** Determine the title to display for a client. */
ReadWMName(ClientNode * np)714 void ReadWMName(ClientNode *np)
715 {
716 
717    unsigned long count;
718    int status;
719    unsigned long extra;
720    Atom realType;
721    int realFormat;
722    unsigned char *name;
723 
724    if(np->name) {
725       Release(np->name);
726    }
727 
728    status = JXGetWindowProperty(display, np->window,
729                                 atoms[ATOM_NET_WM_NAME], 0, 1024, False,
730                                 atoms[ATOM_UTF8_STRING], &realType,
731                                 &realFormat, &count, &extra, &name);
732    if(status != Success || realFormat == 0) {
733       np->name = NULL;
734    } else {
735       np->name = Allocate(count + 1);
736       memcpy(np->name, name, count);
737       np->name[count] = 0;
738       JXFree(name);
739       np->name = ConvertFromUTF8(np->name);
740    }
741 
742 #ifdef USE_XUTF8
743    if(!np->name) {
744       status = JXGetWindowProperty(display, np->window,
745                                    XA_WM_NAME, 0, 1024, False,
746                                    atoms[ATOM_COMPOUND_TEXT],
747                                    &realType, &realFormat, &count,
748                                    &extra, &name);
749       if(status == Success && realFormat != 0) {
750          char **tlist;
751          XTextProperty tprop;
752          int tcount;
753          tprop.value = name;
754          tprop.encoding = atoms[ATOM_COMPOUND_TEXT];
755          tprop.format = realFormat;
756          tprop.nitems = count;
757          if(XmbTextPropertyToTextList(display, &tprop, &tlist, &tcount)
758             == Success && tcount > 0) {
759             const size_t len = strlen(tlist[0]) + 1;
760             np->name = Allocate(len);
761             memcpy(np->name, tlist[0], len);
762             XFreeStringList(tlist);
763          }
764          JXFree(name);
765       }
766    }
767 #endif
768 
769    if(!np->name) {
770       char *temp = NULL;
771       if(JXFetchName(display, np->window, &temp)) {
772          const size_t len = strlen(temp) + 1;
773          np->name = Allocate(len);
774          memcpy(np->name, temp, len);
775          JXFree(temp);
776       }
777    }
778 
779 }
780 
781 /** Read the window class for a client. */
ReadWMClass(ClientNode * np)782 void ReadWMClass(ClientNode *np)
783 {
784    XClassHint hint;
785    Assert(np);
786    if(JXGetClassHint(display, np->window, &hint)) {
787       np->instanceName = hint.res_name;
788       np->className = hint.res_class;
789    }
790 }
791 
792 /** Read the protocols hint for a window. */
ReadWMProtocols(Window w,ClientState * state)793 void ReadWMProtocols(Window w, ClientState *state)
794 {
795 
796    unsigned long count, x;
797    int status;
798    unsigned long extra;
799    Atom realType;
800    int realFormat;
801    unsigned char *temp;
802    Atom *p;
803 
804    Assert(w != None);
805 
806    state->status &= ~STAT_TAKEFOCUS;
807    state->status &= ~STAT_DELETE;
808    status = JXGetWindowProperty(display, w, atoms[ATOM_WM_PROTOCOLS],
809                                 0, 32, False, XA_ATOM, &realType, &realFormat,
810                                 &count, &extra, &temp);
811    p = (Atom*)temp;
812    if(status != Success || realFormat == 0 || !p) {
813       return;
814    }
815 
816    for(x = 0; x < count; x++) {
817       if(p[x] == atoms[ATOM_WM_DELETE_WINDOW]) {
818          state->status |= STAT_DELETE;
819       } else if(p[x] == atoms[ATOM_WM_TAKE_FOCUS]) {
820          state->status |= STAT_TAKEFOCUS;
821       }
822    }
823 
824    JXFree(p);
825 
826 }
827 
828 /** Read the "normal hints" for a client. */
ReadWMNormalHints(ClientNode * np)829 void ReadWMNormalHints(ClientNode *np)
830 {
831 
832    XSizeHints hints;
833    long temp;
834 
835    Assert(np);
836 
837    if(!JXGetWMNormalHints(display, np->window, &hints, &temp)) {
838       np->sizeFlags = 0;
839    } else {
840       np->sizeFlags = hints.flags;
841    }
842 
843    if(np->sizeFlags & PResizeInc) {
844       np->xinc = Max(1, hints.width_inc);
845       np->yinc = Max(1, hints.height_inc);
846    } else {
847       np->xinc = 1;
848       np->yinc = 1;
849    }
850 
851    if(np->sizeFlags & PMinSize) {
852       np->minWidth = Max(0, hints.min_width);
853       np->minHeight = Max(0, hints.min_height);
854    } else {
855       np->minWidth = 1;
856       np->minHeight = 1;
857    }
858 
859    if(np->sizeFlags & PMaxSize) {
860       np->maxWidth =  hints.max_width;
861       np->maxHeight = hints.max_height;
862       if(np->maxWidth <= 0) {
863          np->maxWidth = rootWidth;
864       }
865       if(np->maxHeight <= 0) {
866          np->maxHeight = rootHeight;
867       }
868    } else {
869       np->maxWidth = MAX_WINDOW_WIDTH;
870       np->maxHeight = MAX_WINDOW_HEIGHT;
871    }
872 
873    if(np->sizeFlags & PBaseSize) {
874       np->baseWidth = hints.base_width;
875       np->baseHeight = hints.base_height;
876    } else if(np->sizeFlags & PMinSize) {
877       np->baseWidth = np->minWidth;
878       np->baseHeight = np->minHeight;
879    } else {
880       np->baseWidth = 0;
881       np->baseHeight = 0;
882    }
883 
884    if(np->sizeFlags & PAspect) {
885       np->aspect.minx = hints.min_aspect.x;
886       np->aspect.miny = hints.min_aspect.y;
887       np->aspect.maxx = hints.max_aspect.x;
888       np->aspect.maxy = hints.max_aspect.y;
889    }
890 
891    if(np->sizeFlags & PWinGravity) {
892       np->gravity = hints.win_gravity;
893    } else {
894       np->gravity = 1;
895    }
896 
897 }
898 
899 /** Read colormap information for a client. */
ReadWMColormaps(ClientNode * np)900 void ReadWMColormaps(ClientNode *np)
901 {
902 
903    Window *windows;
904    ColormapNode *cp;
905    int count;
906 
907    Assert(np);
908 
909    if(JXGetWMColormapWindows(display, np->window, &windows, &count)) {
910       if(count > 0) {
911          int x;
912 
913          /* Free old colormaps. */
914          while(np->colormaps) {
915             cp = np->colormaps->next;
916             Release(np->colormaps);
917             np->colormaps = cp;
918          }
919 
920          /* Put the maps in the list in order so they will come out in
921           * reverse order. This way they will be installed with the
922           * most important last.
923           * Keep track of at most colormapCount colormaps for each
924           * window to avoid doing extra work. */
925          count = Min(colormapCount, count);
926          for(x = 0; x < count; x++) {
927             cp = Allocate(sizeof(ColormapNode));
928             cp->window = windows[x];
929             cp->next = np->colormaps;
930             np->colormaps = cp;
931          }
932 
933          JXFree(windows);
934 
935       }
936    }
937 
938 }
939 
940 /** Read the WM state for a window. */
ReadWMState(Window win,ClientState * state)941 void ReadWMState(Window win, ClientState *state)
942 {
943 
944    Status status;
945    unsigned long count;
946    unsigned long extra;
947    Atom realType;
948    int realFormat;
949    unsigned long *temp;
950 
951    count = 0;
952    status = JXGetWindowProperty(display, win, atoms[ATOM_WM_STATE], 0, 2,
953                                 False, atoms[ATOM_WM_STATE],
954                                 &realType, &realFormat,
955                                 &count, &extra, (unsigned char**)&temp);
956    if(JLIKELY(status == Success && realFormat != 0)) {
957       if(JLIKELY(count == 2)) {
958          switch(temp[0]) {
959          case IconicState:
960             state->status |= STAT_MINIMIZED;
961             break;
962          case WithdrawnState:
963             state->status &= ~STAT_MAPPED;
964             break;
965          default:
966             break;
967          }
968       }
969       JXFree(temp);
970    }
971 
972 }
973 
974 /** Read the WM hints for a window. */
ReadWMHints(Window win,ClientState * state,char alreadyMapped)975 void ReadWMHints(Window win, ClientState *state, char alreadyMapped)
976 {
977 
978    XWMHints *wmhints;
979 
980    Assert(win != None);
981    Assert(state);
982 
983    state->status |= STAT_CANFOCUS;
984    wmhints = JXGetWMHints(display, win);
985    if(wmhints) {
986       if(!alreadyMapped && (wmhints->flags & StateHint)) {
987          switch(wmhints->initial_state) {
988          case IconicState:
989             state->status |= STAT_MINIMIZED;
990             break;
991          default:
992             break;
993          }
994       }
995       if((wmhints->flags & InputHint) && wmhints->input == False) {
996          state->status &= ~STAT_CANFOCUS;
997       }
998       if(wmhints->flags & XUrgencyHint) {
999          state->status |= STAT_URGENT;
1000       } else {
1001          state->status &= ~(STAT_URGENT | STAT_FLASH);
1002       }
1003       JXFree(wmhints);
1004    }
1005 
1006 }
1007 
1008 /** Read _NET_WM_WINDOW_OPACITY. */
ReadWMOpacity(Window win,unsigned * opacity)1009 void ReadWMOpacity(Window win, unsigned *opacity)
1010 {
1011    unsigned long card;
1012    if(GetCardinalAtom(win, ATOM_NET_WM_WINDOW_OPACITY, &card)) {
1013       *opacity = card;
1014    } else {
1015       *opacity = UINT_MAX;
1016    }
1017 }
1018 
1019 /** Read _MOTIF_WM_HINTS */
ReadMotifHints(Window win,ClientState * state)1020 void ReadMotifHints(Window win, ClientState *state)
1021 {
1022 
1023    PropMwmHints *mhints;
1024    Atom type;
1025    unsigned long itemCount, bytesLeft;
1026    unsigned char *data;
1027    int format;
1028    int status;
1029 
1030    Assert(win != None);
1031    Assert(state);
1032 
1033    status = JXGetWindowProperty(display, win, atoms[ATOM_MOTIF_WM_HINTS],
1034                                 0L, 20L, False, atoms[ATOM_MOTIF_WM_HINTS],
1035                                 &type, &format, &itemCount, &bytesLeft, &data);
1036    if(status != Success || type == 0) {
1037       return;
1038    }
1039 
1040    mhints = (PropMwmHints*)data;
1041    if(JLIKELY(mhints)) {
1042 
1043       if((mhints->flags & MWM_HINTS_FUNCTIONS)
1044          && !(mhints->functions & MWM_FUNC_ALL)) {
1045 
1046          if(!(mhints->functions & MWM_FUNC_RESIZE)) {
1047             state->border &= ~BORDER_RESIZE;
1048          }
1049          if(!(mhints->functions & MWM_FUNC_MOVE)) {
1050             state->border &= ~BORDER_MOVE;
1051          }
1052          if(!(mhints->functions & MWM_FUNC_MINIMIZE)) {
1053             state->border &= ~BORDER_MIN;
1054          }
1055          if(!(mhints->functions & MWM_FUNC_MAXIMIZE)) {
1056             state->border &= ~BORDER_MAX;
1057          }
1058          if(!(mhints->functions & MWM_FUNC_CLOSE)) {
1059             state->border &= ~BORDER_CLOSE;
1060          }
1061       }
1062 
1063       if((mhints->flags & MWM_HINTS_DECORATIONS)
1064          && !(mhints->decorations & MWM_DECOR_ALL)) {
1065 
1066          if(!(mhints->decorations & MWM_DECOR_BORDER)) {
1067             state->border &= ~BORDER_OUTLINE;
1068          }
1069          if(!(mhints->decorations & MWM_DECOR_TITLE)) {
1070             state->border &= ~BORDER_TITLE;
1071          }
1072       }
1073 
1074       JXFree(mhints);
1075    }
1076 }
1077 
1078 /** Read a cardinal atom. */
GetCardinalAtom(Window window,AtomType atom,unsigned long * value)1079 char GetCardinalAtom(Window window, AtomType atom, unsigned long *value)
1080 {
1081 
1082    unsigned long count;
1083    int status;
1084    unsigned long extra;
1085    Atom realType;
1086    int realFormat;
1087    unsigned char *data;
1088    char ret;
1089 
1090    Assert(window != None);
1091    Assert(value);
1092 
1093    count = 0;
1094    status = JXGetWindowProperty(display, window, atoms[atom], 0, 1, False,
1095                                 XA_CARDINAL, &realType, &realFormat,
1096                                 &count, &extra, &data);
1097    ret = 0;
1098    if(status == Success && realFormat != 0 && data) {
1099       if(JLIKELY(count == 1)) {
1100          *value = *(unsigned long*)data;
1101          ret = 1;
1102       }
1103       JXFree(data);
1104    }
1105 
1106    return ret;
1107 
1108 }
1109 
1110 /** Set a cardinal atom. */
SetCardinalAtom(Window window,AtomType atom,unsigned long value)1111 void SetCardinalAtom(Window window, AtomType atom, unsigned long value)
1112 {
1113    Assert(window != None);
1114    JXChangeProperty(display, window, atoms[atom], XA_CARDINAL, 32,
1115                     PropModeReplace, (unsigned char*)&value, 1);
1116 }
1117 
1118 /** Read a window atom. */
GetWindowAtom(Window window,AtomType atom,Window * value)1119 char GetWindowAtom(Window window, AtomType atom, Window *value)
1120 {
1121 
1122    unsigned long count;
1123    int status;
1124    unsigned long extra;
1125    Atom realType;
1126    int realFormat;
1127    unsigned char *data;
1128    char ret;
1129 
1130    Assert(window != None);
1131    Assert(value);
1132 
1133    count = 0;
1134    status = JXGetWindowProperty(display, window, atoms[atom], 0, 1, False,
1135                                 XA_WINDOW, &realType, &realFormat,
1136                                 &count, &extra, &data);
1137    ret = 0;
1138    if(status == Success && realFormat != 0 && data) {
1139       if(JLIKELY(count == 1)) {
1140          *value = *(Window*)data;
1141          ret = 1;
1142       }
1143       JXFree(data);
1144    }
1145 
1146    return ret;
1147 
1148 }
1149 
1150 /** Set a window atom. */
SetWindowAtom(Window window,AtomType atom,unsigned long value)1151 void SetWindowAtom(Window window, AtomType atom, unsigned long value)
1152 {
1153    Assert(window != None);
1154    JXChangeProperty(display, window, atoms[atom], XA_WINDOW, 32,
1155                     PropModeReplace, (unsigned char*)&value, 1);
1156 }
1157 
1158 /** Set a pixmap atom. */
SetPixmapAtom(Window window,AtomType atom,Pixmap value)1159 void SetPixmapAtom(Window window, AtomType atom, Pixmap value)
1160 {
1161    Assert(window != None);
1162    JXChangeProperty(display, window, atoms[atom], XA_PIXMAP, 32,
1163                     PropModeReplace, (unsigned char*)&value, 1);
1164 }
1165 
1166 /** Set an atom atom. */
SetAtomAtom(Window window,AtomType atom,AtomType value)1167 void SetAtomAtom(Window window, AtomType atom, AtomType value)
1168 {
1169    Assert(window != None);
1170    JXChangeProperty(display, window, atoms[atom], XA_ATOM, 32,
1171                     PropModeReplace, (unsigned char*)&atoms[value], 1);
1172 }
1173