JWM Source Documentation
hint.c
Go to the documentation of this file.
1 
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 
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" },
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" },
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"},
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" },
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" },
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] },
169  "_JWM_WM_STATE_MAXIMIZED_TOP" },
171  "_JWM_WM_STATE_MAXIMIZED_BOTTOM" },
173  "_JWM_WM_STATE_MAXIMIZED_LEFT" },
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 
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  }
220  XA_ATOM, 32, PropModeReplace, (unsigned char*)supported,
222 
223  /* _NET_NUMBER_OF_DESKTOPS */
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  }
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;
243  XA_CARDINAL, 32, PropModeReplace,
244  (unsigned char*)array, 2);
245 
246  /* _NET_DESKTOP_VIEWPORT */
247  array[0] = 0;
248  array[1] = 0;
250  XA_CARDINAL, 32, PropModeReplace,
251  (unsigned char*)array, 2);
252 
253  /* _NET_WM_NAME */
254  win = supportingWindow;
256  atoms[ATOM_UTF8_STRING], 8, PropModeReplace,
257  (unsigned char*)"JWM", 3);
258 
259  /* _NET_WM_PID */
260  array[0] = getpid();
262  XA_CARDINAL, 32, PropModeReplace,
263  (unsigned char*)array, 1);
264 
265  /* _NET_SUPPORTING_WM_CHECK */
268 
269  ReleaseStack(data);
270 
271 }
272 
275 {
276  unsigned long temp;
277  currentDesktop = 0;
279  ChangeDesktop(temp);
280  } else {
282  }
283 }
284 
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) {
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 
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) {
346  } else {
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 
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) {
369  } else {
371  }
372 }
373 
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))) {
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 
449  XA_ATOM, 32, PropModeReplace,
450  (unsigned char*)values, index);
451 }
452 
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 
468  XA_CARDINAL, 32, PropModeReplace,
469  (unsigned char*)values, 4);
470 
471 }
472 
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 
519  XA_ATOM, 32, PropModeReplace,
520  (unsigned char*)values, index);
521 
522 }
523 
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 
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 */
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 
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,
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 
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 
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;
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 
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 {
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 }

joewing.net / Projects / JWM