JWM Source Documentation
client.c
Go to the documentation of this file.
1 
10 #include "jwm.h"
11 #include "client.h"
12 #include "clientlist.h"
13 #include "icon.h"
14 #include "group.h"
15 #include "tray.h"
16 #include "confirm.h"
17 #include "cursor.h"
18 #include "taskbar.h"
19 #include "screen.h"
20 #include "pager.h"
21 #include "color.h"
22 #include "place.h"
23 #include "event.h"
24 #include "settings.h"
25 #include "timing.h"
26 #include "grab.h"
27 #include "desktop.h"
28 
30 
31 unsigned int clientCount;
32 
33 static void LoadFocus(void);
34 static void RestackTransients(const ClientNode *np);
35 static void MinimizeTransients(ClientNode *np, char lower);
36 static void RestoreTransients(ClientNode *np, char raise);
37 static void KillClientHandler(ClientNode *np);
38 static void UnmapClient(ClientNode *np);
39 
41 void StartupClients(void)
42 {
43 
44  XWindowAttributes attr;
45  Window rootReturn, parentReturn, *childrenReturn;
46  unsigned int childrenCount;
47  unsigned int x;
48 
49  clientCount = 0;
50  activeClient = NULL;
51  currentDesktop = 0;
52 
53  /* Clear out the client lists. */
54  for(x = 0; x < LAYER_COUNT; x++) {
55  nodes[x] = NULL;
56  nodeTail[x] = NULL;
57  }
58 
59  /* Query client windows. */
60  JXQueryTree(display, rootWindow, &rootReturn, &parentReturn,
61  &childrenReturn, &childrenCount);
62 
63  /* Add each client. */
64  for(x = 0; x < childrenCount; x++) {
65  if(JXGetWindowAttributes(display, childrenReturn[x], &attr)) {
66  if(attr.override_redirect == False && attr.map_state == IsViewable) {
67  AddClientWindow(childrenReturn[x], 1, 1);
68  }
69  }
70  }
71 
72  JXFree(childrenReturn);
73 
74  LoadFocus();
75 
78 
79 }
80 
82 void ShutdownClients(void)
83 {
84 
85  int x;
86 
87  for(x = 0; x < LAYER_COUNT; x++) {
88  while(nodeTail[x]) {
90  }
91  }
92 
93 }
94 
96 void LoadFocus(void)
97 {
98 
99  ClientNode *np;
100  Window rootReturn, childReturn;
101  int rootx, rooty;
102  int winx, winy;
103  unsigned int mask;
104 
105  JXQueryPointer(display, rootWindow, &rootReturn, &childReturn,
106  &rootx, &rooty, &winx, &winy, &mask);
107 
108  np = FindClient(childReturn);
109  if(np) {
110  FocusClient(np);
111  }
112 
113 }
114 
116 ClientNode *AddClientWindow(Window w, char alreadyMapped, char notOwner)
117 {
118 
119  XWindowAttributes attr;
120  ClientNode *np;
121 
122  Assert(w != None);
123 
124  /* Get window attributes. */
125  if(JXGetWindowAttributes(display, w, &attr) == 0) {
126  return NULL;
127  }
128 
129  /* Determine if we should care about this window. */
130  if(attr.override_redirect == True) {
131  return NULL;
132  }
133  if(attr.class == InputOnly) {
134  return NULL;
135  }
136 
137  /* Prepare a client node for this window. */
138  np = Allocate(sizeof(ClientNode));
139  memset(np, 0, sizeof(ClientNode));
140 
141  np->window = w;
142  np->parent = None;
143  np->owner = None;
145 
146  np->x = attr.x;
147  np->y = attr.y;
148  np->width = attr.width;
149  np->height = attr.height;
150  np->cmap = attr.colormap;
151  np->state.status = STAT_NONE;
152  np->state.maxFlags = MAX_NONE;
153  np->state.layer = LAYER_NORMAL;
155 
157  np->borderAction = BA_NONE;
158 
159  ReadClientInfo(np, alreadyMapped);
160 
161  if(!notOwner) {
164  np->state.layer = LAYER_ABOVE;
166  }
167 
168  ApplyGroups(np);
169  if(np->icon == NULL) {
170  LoadIcon(np);
171  }
172 
173  /* We now know the layer, so insert */
174  np->prev = NULL;
175  np->next = nodes[np->state.layer];
176  if(np->next) {
177  np->next->prev = np;
178  } else {
179  nodeTail[np->state.layer] = np;
180  }
181  nodes[np->state.layer] = np;
182 
184 
185  if(notOwner) {
186  XSetWindowAttributes sattr;
188  sattr.event_mask
189  = EnterWindowMask
190  | ColormapChangeMask
191  | PropertyChangeMask
192  | KeyReleaseMask
193  | StructureNotifyMask;
194  sattr.do_not_propagate_mask = ButtonPressMask
195  | ButtonReleaseMask
196  | PointerMotionMask
197  | KeyPressMask
198  | KeyReleaseMask;
200  CWEventMask | CWDontPropagate, &sattr);
201  }
202  JXGrabButton(display, AnyButton, AnyModifier, np->window, True,
203  ButtonPressMask, GrabModeSync, GrabModeAsync, None, None);
204 
205  PlaceClient(np, alreadyMapped);
206  ReparentClient(np);
207  XSaveContext(display, np->window, clientContext, (void*)np);
208 
209  if(np->state.status & STAT_MAPPED) {
210  JXMapWindow(display, np->window);
211  }
212 
213  clientCount += 1;
214 
215  if(!alreadyMapped) {
216  RaiseClient(np);
217  }
218 
219  if(np->state.status & STAT_OPACITY) {
220  SetOpacity(np, np->state.opacity, 1);
221  } else {
223  }
224  if(np->state.status & STAT_STICKY) {
226  } else {
228  }
229 
230  /* Shade the client if requested. */
231  if(np->state.status & STAT_SHADED) {
232  np->state.status &= ~STAT_SHADED;
233  ShadeClient(np);
234  }
235 
236  /* Minimize the client if requested. */
237  if(np->state.status & STAT_MINIMIZED) {
238  np->state.status &= ~STAT_MINIMIZED;
239  MinimizeClient(np, 0);
240  }
241 
242  /* Maximize the client if requested. */
243  if(np->state.maxFlags) {
244  const MaxFlags flags = np->state.maxFlags;
245  np->state.maxFlags = MAX_NONE;
246  MaximizeClient(np, flags);
247  }
248 
249  if(np->state.status & STAT_URGENT) {
251  }
252 
253  /* Update task bars. */
254  AddClientToTaskBar(np);
255 
256  /* Make sure we're still in sync */
257  WriteState(np);
258  SendConfigureEvent(np);
259 
260  /* Hide the client if we're not on the right desktop. */
261  if(np->state.desktop != currentDesktop
262  && !(np->state.status & STAT_STICKY)) {
263  HideClient(np);
264  }
265 
266  ReadClientStrut(np);
267 
268  /* Focus transients if their parent has focus. */
269  if(np->owner != None) {
270  if(activeClient && np->owner == activeClient->window) {
271  FocusClient(np);
272  }
273  }
274 
275  /* Make the client fullscreen if requested. */
276  if(np->state.status & STAT_FULLSCREEN) {
277  np->state.status &= ~STAT_FULLSCREEN;
278  SetClientFullScreen(np, 1);
279  }
280  ResetBorder(np);
281 
282  return np;
283 
284 }
285 
287 void MinimizeClient(ClientNode *np, char lower)
288 {
289  Assert(np);
290  MinimizeTransients(np, lower);
291  RequireRestack();
293 }
294 
296 void MinimizeTransients(ClientNode *np, char lower)
297 {
298 
299  ClientNode *tp;
300  int x;
301 
302  Assert(np);
303 
304  /* Unmap the window and update its state. */
305  if(np->state.status & (STAT_MAPPED | STAT_SHADED)) {
306  UnmapClient(np);
307  if(np->parent != None) {
309  }
310  }
311  np->state.status |= STAT_MINIMIZED;
312 
313  /* Minimize transient windows. */
314  for(x = 0; x < LAYER_COUNT; x++) {
315  tp = nodes[x];
316  while(tp) {
317  ClientNode *next = tp->next;
318  if(tp->owner == np->window
319  && (tp->state.status & (STAT_MAPPED | STAT_SHADED))
320  && !(tp->state.status & STAT_MINIMIZED)) {
321  MinimizeTransients(tp, lower);
322  }
323  tp = next;
324  }
325  }
326 
327  /* Focus the next window. */
328  if(np->state.status & STAT_ACTIVE) {
329  FocusNextStacked(np);
330  }
331 
332  if(lower) {
333  /* Move this client to the end of the layer list. */
334  if(nodeTail[np->state.layer] != np) {
335  if(np->prev) {
336  np->prev->next = np->next;
337  } else {
338  nodes[np->state.layer] = np->next;
339  }
340  np->next->prev = np->prev;
341  tp = nodeTail[np->state.layer];
342  nodeTail[np->state.layer] = np;
343  tp->next = np;
344  np->prev = tp;
345  np->next = NULL;
346  }
347  }
348 
349  WriteState(np);
350 
351 }
352 
355 {
356 
357  Assert(np);
358 
359  if((np->state.status & (STAT_SHADED | STAT_FULLSCREEN)) ||
360  !(np->state.border & BORDER_SHADE)) {
361  return;
362  }
363 
364  UnmapClient(np);
365  np->state.status |= STAT_SHADED;
366 
367  WriteState(np);
368  ResetBorder(np);
370 
371 }
372 
375 {
376 
377  Assert(np);
378 
379  if(!(np->state.status & STAT_SHADED)) {
380  return;
381  }
382 
383  if(!(np->state.status & (STAT_MINIMIZED | STAT_SDESKTOP))) {
384  JXMapWindow(display, np->window);
385  np->state.status |= STAT_MAPPED;
386  }
387  np->state.status &= ~STAT_SHADED;
388 
389  WriteState(np);
390  ResetBorder(np);
391  RefocusClient();
393 
394 }
395 
398 {
399 
400  Assert(np);
401 
402  if(activeClient == np) {
403  activeClient = NULL;
404  np->state.status &= ~STAT_ACTIVE;
405  FocusNextStacked(np);
406  }
407 
408  if(np->state.status & STAT_MAPPED) {
409  UnmapClient(np);
410  if(np->parent != None) {
412  }
413  } else if(np->state.status & STAT_SHADED) {
414  if(!(np->state.status & STAT_MINIMIZED)) {
415  if(np->parent != None) {
417  }
418  }
419  }
420 
421  np->state.status &= ~STAT_SHADED;
422  np->state.status &= ~STAT_MINIMIZED;
423  np->state.status &= ~STAT_SDESKTOP;
424 
425  WriteState(np);
428 
429 }
430 
432 void RestoreTransients(ClientNode *np, char raise)
433 {
434 
435  ClientNode *tp;
436  int x;
437 
438  Assert(np);
439 
440  /* Make sure this window is on the current desktop. */
442 
443  /* Restore this window. */
444  if(!(np->state.status & STAT_MAPPED)) {
445  if(np->state.status & STAT_SHADED) {
446  if(np->parent != None) {
447  JXMapWindow(display, np->parent);
448  }
449  } else {
450  JXMapWindow(display, np->window);
451  if(np->parent != None) {
452  JXMapWindow(display, np->parent);
453  }
454  np->state.status |= STAT_MAPPED;
455  }
456  }
457  np->state.status &= ~STAT_MINIMIZED;
458  np->state.status &= ~STAT_SDESKTOP;
459 
460  /* Restore transient windows. */
461  for(x = 0; x < LAYER_COUNT; x++) {
462  for(tp = nodes[x]; tp; tp = tp->next) {
463  if(tp->owner == np->window && (tp->state.status & STAT_MINIMIZED)) {
464  RestoreTransients(tp, raise);
465  }
466  }
467  }
468 
469  if(raise) {
470  FocusClient(np);
471  RaiseClient(np);
472  }
473  WriteState(np);
474 
475 }
476 
478 void RestoreClient(ClientNode *np, char raise)
479 {
480  if((np->state.status & STAT_FIXED) && !(np->state.status & STAT_STICKY)) {
482  }
483  RestoreTransients(np, raise);
484  RequireRestack();
486 }
487 
489 void SetClientLayer(ClientNode *np, unsigned int layer)
490 {
491 
492  ClientNode *tp, *next;
493 
494  Assert(np);
495  Assert(layer <= LAST_LAYER);
496 
497  if(np->state.layer != layer) {
498  int x;
499 
500  /* Loop through all clients so we get transients. */
501  for(x = FIRST_LAYER; x <= LAST_LAYER; x++) {
502  tp = nodes[x];
503  while(tp) {
504  next = tp->next;
505  if(tp == np || tp->owner == np->window) {
506 
507  /* Remove from the old node list */
508  if(next) {
509  next->prev = tp->prev;
510  } else {
511  nodeTail[tp->state.layer] = tp->prev;
512  }
513  if(tp->prev) {
514  tp->prev->next = next;
515  } else {
516  nodes[tp->state.layer] = next;
517  }
518 
519  /* Insert into the new node list */
520  tp->prev = NULL;
521  tp->next = nodes[layer];
522  if(nodes[layer]) {
523  nodes[layer]->prev = tp;
524  } else {
525  nodeTail[layer] = tp;
526  }
527  nodes[layer] = tp;
528 
529  /* Set the new layer */
530  tp->state.layer = layer;
531  WriteState(tp);
532 
533  }
534  tp = next;
535  }
536  }
537 
538  RequireRestack();
539 
540  }
541 
542 }
543 
545 void SetClientSticky(ClientNode *np, char isSticky)
546 {
547 
548  ClientNode *tp;
549  int x;
550  char old;
551 
552  Assert(np);
553 
554  /* Get the old sticky status. */
555  if(np->state.status & STAT_STICKY) {
556  old = 1;
557  } else {
558  old = 0;
559  }
560 
561  if(isSticky && !old) {
562 
563  /* Change from non-sticky to sticky. */
564 
565  for(x = 0; x < LAYER_COUNT; x++) {
566  for(tp = nodes[x]; tp; tp = tp->next) {
567  if(tp == np || tp->owner == np->window) {
568  tp->state.status |= STAT_STICKY;
570  WriteState(tp);
571  }
572  }
573  }
574 
575  } else if(!isSticky && old) {
576 
577  /* Change from sticky to non-sticky. */
578 
579  for(x = 0; x < LAYER_COUNT; x++) {
580  for(tp = nodes[x]; tp; tp = tp->next) {
581  if(tp == np || tp->owner == np->window) {
582  tp->state.status &= ~STAT_STICKY;
583  WriteState(tp);
584  }
585  }
586  }
587 
588  /* Since this client is no longer sticky, we need to assign
589  * a desktop. Here we use the current desktop.
590  * Note that SetClientDesktop updates transients (which is good).
591  */
593 
594  }
595 
596 }
597 
599 void SetClientDesktop(ClientNode *np, unsigned int desktop)
600 {
601 
602  ClientNode *tp;
603 
604  Assert(np);
605 
606  if(JUNLIKELY(desktop >= settings.desktopCount)) {
607  return;
608  }
609 
610  if(!(np->state.status & STAT_STICKY)) {
611  int x;
612  for(x = 0; x < LAYER_COUNT; x++) {
613  for(tp = nodes[x]; tp; tp = tp->next) {
614  if(tp == np || tp->owner == np->window) {
615 
616  tp->state.desktop = desktop;
617 
618  if(desktop == currentDesktop) {
619  ShowClient(tp);
620  } else {
621  HideClient(tp);
622  }
623 
625  tp->state.desktop);
626  }
627  }
628  }
631  }
632 
633 }
634 
637 {
638  if(activeClient == np) {
639  activeClient = NULL;
640  }
641  np->state.status |= STAT_HIDDEN;
642  if(np->state.status & (STAT_MAPPED | STAT_SHADED)) {
643  if(np->parent != None) {
645  } else {
647  }
648  }
649 }
650 
653 {
654  if(np->state.status & STAT_HIDDEN) {
655  np->state.status &= ~STAT_HIDDEN;
656  if(np->state.status & (STAT_MAPPED | STAT_SHADED)) {
657  if(!(np->state.status & STAT_MINIMIZED)) {
658  if(np->parent != None) {
659  JXMapWindow(display, np->parent);
660  } else {
661  JXMapWindow(display, np->window);
662  }
663  if(np->state.status & STAT_ACTIVE) {
664  FocusClient(np);
665  }
666  }
667  }
668  }
669 }
670 
673 {
674 
675  /* Return if we don't have a client. */
676  if(np == NULL) {
677  return;
678  }
679 
680  /* Don't allow maximization of full-screen clients. */
681  if(np->state.status & STAT_FULLSCREEN) {
682  return;
683  }
684  if(!(np->state.border & BORDER_MAX)) {
685  return;
686  }
687 
688  if(np->state.status & STAT_SHADED) {
689  UnshadeClient(np);
690  }
691 
692  if(np->state.status & STAT_MINIMIZED) {
693  RestoreClient(np, 1);
694  }
695 
696  RaiseClient(np);
697  FocusClient(np);
698  if(np->state.maxFlags) {
699  /* Undo existing maximization. */
700  np->x = np->oldx;
701  np->y = np->oldy;
702  np->width = np->oldWidth;
703  np->height = np->oldHeight;
704  np->state.maxFlags = MAX_NONE;
705  }
706  if(flags != MAX_NONE) {
707  /* Maximize if requested. */
708  PlaceMaximizedClient(np, flags);
709  }
710 
711  WriteState(np);
712  ResetBorder(np);
713  DrawBorder(np);
714  SendConfigureEvent(np);
716 
717 }
718 
721 {
722 
723  MaxFlags flags = MAX_NONE;
724 
725  Assert(np);
726 
727  if(np->state.maxFlags == MAX_NONE) {
728  if(np->state.border & BORDER_MAX_H) {
729  flags |= MAX_HORIZ;
730  }
731  if(np->state.border & BORDER_MAX_V) {
732  flags |= MAX_VERT;
733  }
734  }
735 
736  MaximizeClient(np, flags);
737 
738 }
739 
741 void SetClientFullScreen(ClientNode *np, char fullScreen)
742 {
743 
744  XEvent event;
745  int north, south, east, west;
746  BoundingBox box;
747  const ScreenType *sp;
748 
749  Assert(np);
750 
751  /* Make sure there's something to do. */
752  if(!fullScreen == !(np->state.status & STAT_FULLSCREEN)) {
753  return;
754  }
755  if(!(np->state.border & BORDER_FULLSCREEN)) {
756  return;
757  }
758 
759  if(np->state.status & STAT_SHADED) {
760  UnshadeClient(np);
761  }
762 
763  if(fullScreen) {
764 
766 
767  if(!(np->state.maxFlags)) {
768  np->oldx = np->x;
769  np->oldy = np->y;
770  np->oldWidth = np->width;
771  np->oldHeight = np->height;
772  }
773 
774  sp = GetCurrentScreen(np->x, np->y);
775  GetScreenBounds(sp, &box);
776 
777  GetBorderSize(&np->state, &north, &south, &east, &west);
778  box.x += west;
779  box.y += north;
780  box.width -= east + west;
781  box.height -= north + south;
782 
783  np->x = box.x;
784  np->y = box.y;
785  np->width = box.width;
786  np->height = box.height;
787  ResetBorder(np);
788 
789  } else {
790 
791  np->state.status &= ~STAT_FULLSCREEN;
792 
793  np->x = np->oldx;
794  np->y = np->oldy;
795  np->width = np->oldWidth;
796  np->height = np->oldHeight;
797  ConstrainSize(np);
798  ConstrainPosition(np);
799 
800  if(np->state.maxFlags != MAX_NONE) {
802  }
803 
804  ResetBorder(np);
805 
806  event.type = MapRequest;
807  event.xmaprequest.send_event = True;
808  event.xmaprequest.display = display;
809  event.xmaprequest.parent = np->parent;
810  event.xmaprequest.window = np->window;
812  SubstructureRedirectMask, &event);
813 
814  }
815 
816  WriteState(np);
817  SendConfigureEvent(np);
818  RequireRestack();
819 
820 }
821 
824 {
825  if(np->state.status & STAT_HIDDEN) {
826  return;
827  }
828  if(!(np->state.status & (STAT_CANFOCUS | STAT_TAKEFOCUS))) {
829  return;
830  }
831 
832  if(activeClient != np || !(np->state.status & STAT_ACTIVE)) {
833  if(activeClient) {
834  activeClient->state.status &= ~STAT_ACTIVE;
835  if(!(activeClient->state.status & STAT_OPACITY)) {
836  SetOpacity(activeClient, settings.inactiveClientOpacity, 0);
837  }
838  DrawBorder(activeClient);
839  WriteNetState(activeClient);
840  }
841  np->state.status |= STAT_ACTIVE;
842  activeClient = np;
843  if(!(np->state.status & STAT_OPACITY)) {
845  }
846 
847  DrawBorder(np);
850  }
851 
852  if(np->state.status & STAT_MAPPED) {
855  WriteNetState(np);
856  if(np->state.status & STAT_CANFOCUS) {
857  JXSetInputFocus(display, np->window, RevertToParent, eventTime);
858  }
859  if(np->state.status & STAT_TAKEFOCUS) {
861  }
862  } else {
863  JXSetInputFocus(display, rootWindow, RevertToParent, eventTime);
864  }
865 
866 }
867 
868 
870 void RefocusClient(void)
871 {
872  if(activeClient) {
873  FocusClient(activeClient);
874  }
875 }
876 
879 {
880  Assert(np);
881  ReadWMProtocols(np->window, &np->state);
882  if(np->state.status & STAT_DELETE) {
884  } else {
885  KillClient(np);
886  }
887 }
888 
891 {
892  if(np == activeClient) {
893  FocusNextStacked(np);
894  }
895 
897 }
898 
901 {
902  Assert(np);
904  _("Kill this window?"),
905  _("This may cause data to be lost!"),
906  NULL);
907 }
908 
911 {
912  ClientNode *tp;
913  unsigned int layer;
914 
915  /* Place any transient windows on top of the owner */
916  for(layer = 0; layer < LAYER_COUNT; layer++) {
917  for(tp = nodes[layer]; tp; tp = tp->next) {
918  if(tp->owner == np->window && tp->prev) {
919 
920  ClientNode *next = tp->next;
921 
922  tp->prev->next = tp->next;
923  if(tp->next) {
924  tp->next->prev = tp->prev;
925  } else {
926  nodeTail[tp->state.layer] = tp->prev;
927  }
928  tp->next = nodes[tp->state.layer];
929  nodes[tp->state.layer]->prev = tp;
930  tp->prev = NULL;
931  nodes[tp->state.layer] = tp;
932 
933  tp = next;
934 
935  }
936 
937  /* tp will be tp->next if the above code is executed. */
938  /* Thus, if it is NULL, we are done with this layer. */
939  if(!tp) {
940  break;
941  }
942  }
943  }
944 }
945 
948 {
949 
950  Assert(np);
951 
952  if(nodes[np->state.layer] != np) {
953 
954  /* Raise the window */
955  Assert(np->prev);
956  np->prev->next = np->next;
957  if(np->next) {
958  np->next->prev = np->prev;
959  } else {
960  nodeTail[np->state.layer] = np->prev;
961  }
962  np->next = nodes[np->state.layer];
963  nodes[np->state.layer]->prev = np;
964  np->prev = NULL;
965  nodes[np->state.layer] = np;
966 
967  }
968 
969  RestackTransients(np);
970  RequireRestack();
971 
972 }
973 
975 void RestackClient(ClientNode *np, Window above, int detail)
976 {
977 
978  ClientNode *tp;
979  char inserted = 0;
980 
981  /* Remove from the window list. */
982  if(np->prev) {
983  np->prev->next = np->next;
984  } else {
985  nodes[np->state.layer] = np->next;
986  }
987  if(np->next) {
988  np->next->prev = np->prev;
989  } else {
990  nodeTail[np->state.layer] = np->prev;
991  }
992 
993  /* Insert back into the window list. */
994  if(above != None && above != np->window) {
995 
996  /* Insert relative to some other window. */
997  char found = 0;
998  for(tp = nodes[np->state.layer]; tp; tp = tp->next) {
999  if(tp == np) {
1000  found = 1;
1001  } else if(tp->window == above) {
1002  char insert_before = 0;
1003  inserted = 1;
1004  switch(detail) {
1005  case Above:
1006  case TopIf:
1007  insert_before = 1;
1008  break;
1009  case Below:
1010  case BottomIf:
1011  insert_before = 0;
1012  break;
1013  case Opposite:
1014  insert_before = !found;
1015  break;
1016  }
1017  if(insert_before) {
1018 
1019  /* Insert before this window. */
1020  np->prev = tp->prev;
1021  np->next = tp;
1022  if(tp->prev) {
1023  tp->prev->next = np;
1024  } else {
1025  nodes[np->state.layer] = np;
1026  }
1027  tp->prev = np;
1028 
1029  } else {
1030 
1031  /* Insert after this window. */
1032  np->prev = tp;
1033  np->next = tp->next;
1034  if(tp->next) {
1035  tp->next->prev = np;
1036  } else {
1037  nodeTail[np->state.layer] = np;
1038  }
1039  tp->next = np;
1040 
1041  }
1042  break;
1043  }
1044  }
1045  }
1046  if(!inserted) {
1047 
1048  /* Insert absolute for the layer. */
1049  if(detail == Below || detail == BottomIf) {
1050 
1051  /* Insert to the bottom of the stack. */
1052  np->next = NULL;
1053  np->prev = nodeTail[np->state.layer];
1054  if(nodeTail[np->state.layer]) {
1055  nodeTail[np->state.layer]->next = np;
1056  } else {
1057  nodes[np->state.layer] = np;
1058  }
1059  nodeTail[np->state.layer] = np;
1060 
1061  } else {
1062 
1063  /* Insert at the top of the stack. */
1064  np->next = nodes[np->state.layer];
1065  np->prev = NULL;
1066  if(nodes[np->state.layer]) {
1067  nodes[np->state.layer]->prev = np;
1068  } else {
1069  nodeTail[np->state.layer] = np;
1070  }
1071  nodes[np->state.layer] = np;
1072 
1073  }
1074  }
1075 
1076  RestackTransients(np);
1077  RequireRestack();
1078 
1079 }
1080 
1082 void RestackClients(void)
1083 {
1084 
1085  TrayType *tp;
1086  ClientNode *np;
1087  unsigned int layer, index;
1088  int trayCount;
1089  Window *stack;
1090  Window fw;
1091 
1092  if(JUNLIKELY(shouldExit)) {
1093  return;
1094  }
1095 
1096  /* Allocate memory for restacking. */
1097  trayCount = GetTrayCount();
1098  stack = AllocateStack((clientCount + trayCount) * sizeof(Window));
1099 
1100  /* Prepare the stacking array. */
1101  fw = None;
1102  index = 0;
1103  if(activeClient && (activeClient->state.status & STAT_FULLSCREEN)) {
1104  fw = activeClient->window;
1105  for(np = nodes[activeClient->state.layer]; np; np = np->next) {
1106  if(np->owner == fw) {
1107  if(np->parent != None) {
1108  stack[index] = np->parent;
1109  } else {
1110  stack[index] = np->window;
1111  }
1112  index += 1;
1113  }
1114  }
1115  if(activeClient->parent != None) {
1116  stack[index] = activeClient->parent;
1117  } else {
1118  stack[index] = activeClient->window;
1119  }
1120  index += 1;
1121  }
1122  layer = LAST_LAYER;
1123  for(;;) {
1124 
1125  for(np = nodes[layer]; np; np = np->next) {
1126  if( (np->state.status & (STAT_MAPPED | STAT_SHADED))
1127  && !(np->state.status & STAT_HIDDEN)) {
1128  if(fw != None && (np->window == fw || np->owner == fw)) {
1129  continue;
1130  }
1131  if(np->parent != None) {
1132  stack[index] = np->parent;
1133  } else {
1134  stack[index] = np->window;
1135  }
1136  index += 1;
1137  }
1138  }
1139 
1140  for(tp = GetTrays(); tp; tp = tp->next) {
1141  if(layer == tp->layer) {
1142  stack[index] = tp->window;
1143  index += 1;
1144  }
1145  }
1146 
1147  if(layer == FIRST_LAYER) {
1148  break;
1149  }
1150  layer -= 1;
1151 
1152  }
1153 
1154  JXRestackWindows(display, stack, index);
1155 
1156  ReleaseStack(stack);
1159 
1160 }
1161 
1163 void SendClientMessage(Window w, AtomType type, AtomType message)
1164 {
1165 
1166  XEvent event;
1167  int status;
1168 
1169  memset(&event, 0, sizeof(event));
1170  event.xclient.type = ClientMessage;
1171  event.xclient.window = w;
1172  event.xclient.message_type = atoms[type];
1173  event.xclient.format = 32;
1174  event.xclient.data.l[0] = atoms[message];
1175  event.xclient.data.l[1] = eventTime;
1176 
1177  status = JXSendEvent(display, w, False, 0, &event);
1178  if(JUNLIKELY(status == False)) {
1179  Debug("SendClientMessage failed");
1180  }
1181 
1182 }
1183 
1186 {
1187 
1188  ColormapNode *cp;
1189 
1190  Assert(np);
1191  Assert(np->window != None);
1192 
1193  /* Remove this client from the client list */
1194  if(np->next) {
1195  np->next->prev = np->prev;
1196  } else {
1197  nodeTail[np->state.layer] = np->prev;
1198  }
1199  if(np->prev) {
1200  np->prev->next = np->next;
1201  } else {
1202  nodes[np->state.layer] = np->next;
1203  }
1204  clientCount -= 1;
1205  XDeleteContext(display, np->window, clientContext);
1206  if(np->parent != None) {
1207  XDeleteContext(display, np->parent, frameContext);
1208  }
1209 
1210  if(np->state.status & STAT_URGENT) {
1212  }
1213 
1214  /* Make sure this client isn't active */
1215  if(activeClient == np && !shouldExit) {
1216  FocusNextStacked(np);
1217  }
1218  if(activeClient == np) {
1219 
1220  /* Must be the last client. */
1222  activeClient = NULL;
1223  JXSetInputFocus(display, rootWindow, RevertToParent, eventTime);
1224 
1225  }
1226 
1227  /* If the window manager is exiting (ie, not the client), then
1228  * reparent etc. */
1229  if(shouldExit && !(np->state.status & STAT_WMDIALOG)) {
1230  if(np->state.maxFlags) {
1231  np->x = np->oldx;
1232  np->y = np->oldy;
1233  np->width = np->oldWidth;
1234  np->height = np->oldHeight;
1236  np->x, np->y, np->width, np->height);
1237  }
1238  GravitateClient(np, 1);
1239  if(!(np->state.status & STAT_MAPPED)
1240  && (np->state.status & (STAT_MINIMIZED | STAT_SHADED))) {
1241  JXMapWindow(display, np->window);
1242  }
1243  JXUngrabButton(display, AnyButton, AnyModifier, np->window);
1244  JXReparentWindow(display, np->window, rootWindow, np->x, np->y);
1246  }
1247 
1248  /* Destroy the parent */
1249  if(np->parent) {
1251  }
1252 
1253  if(np->name) {
1254  Release(np->name);
1255  }
1256  if(np->instanceName) {
1257  JXFree(np->instanceName);
1258  }
1259  if(np->className) {
1260  JXFree(np->className);
1261  }
1262 
1264  RemoveClientStrut(np);
1265 
1266  while(np->colormaps) {
1267  cp = np->colormaps->next;
1268  Release(np->colormaps);
1269  np->colormaps = cp;
1270  }
1271 
1272  DestroyIcon(np->icon);
1273 
1274  Release(np);
1275 
1276  RequireRestack();
1277 
1278 }
1279 
1282 {
1283  return activeClient;
1284 }
1285 
1288 {
1289  ClientNode *np;
1290  np = FindClientByWindow(w);
1291  if(!np) {
1292  np = FindClientByParent(w);
1293  }
1294  return np;
1295 }
1296 
1299 {
1300  ClientNode *np;
1301  if(!XFindContext(display, w, clientContext, (void*)&np)) {
1302  return np;
1303  } else {
1304  return NULL;
1305  }
1306 }
1307 
1310 {
1311  ClientNode *np;
1312  if(!XFindContext(display, p, frameContext, (void*)&np)) {
1313  return np;
1314  } else {
1315  return NULL;
1316  }
1317 }
1318 
1321 {
1322  XSetWindowAttributes attr;
1323  XEvent event;
1324  int attrMask;
1325  int x, y, width, height;
1326  int north, south, east, west;
1327 
1328  if((np->state.border & (BORDER_TITLE | BORDER_OUTLINE)) == 0) {
1329 
1330  if(np->parent == None) {
1331  return;
1332  }
1333 
1334  JXReparentWindow(display, np->window, rootWindow, np->x, np->y);
1335  XDeleteContext(display, np->parent, frameContext);
1337  np->parent = None;
1338 
1339  } else {
1340 
1341  if(np->parent != None) {
1342  return;
1343  }
1344 
1345  attrMask = 0;
1346 
1347  /* We can't use PointerMotionHint mask here since the exact location
1348  * of the mouse on the frame is important. */
1349  attrMask |= CWEventMask;
1350  attr.event_mask
1351  = ButtonPressMask
1352  | ButtonReleaseMask
1353  | ExposureMask
1354  | PointerMotionMask
1355  | SubstructureRedirectMask
1356  | SubstructureNotifyMask
1357  | EnterWindowMask
1358  | LeaveWindowMask
1359  | KeyPressMask
1360  | KeyReleaseMask;
1361 
1362  attrMask |= CWDontPropagate;
1363  attr.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask;
1364 
1365  attrMask |= CWBackPixel;
1366  attr.background_pixel = colors[COLOR_TITLE_BG2];
1367 
1368  attrMask |= CWBorderPixel;
1369  attr.border_pixel = 0;
1370 
1371  x = np->x;
1372  y = np->y;
1373  width = np->width;
1374  height = np->height;
1375  GetBorderSize(&np->state, &north, &south, &east, &west);
1376  x -= west;
1377  y -= north;
1378  width += east + west;
1379  height += north + south;
1380 
1381  /* Create the frame window. */
1382  np->parent = JXCreateWindow(display, rootWindow, x, y, width, height,
1383  0, rootDepth, InputOutput,
1384  rootVisual, attrMask, &attr);
1385  XSaveContext(display, np->parent, frameContext, (void*)np);
1386 
1388 
1389  /* Reparent the client window. */
1390  JXReparentWindow(display, np->window, np->parent, west, north);
1391 
1392  if(np->state.status & STAT_MAPPED) {
1393  JXMapWindow(display, np->parent);
1394  }
1395  }
1396 
1397  JXSync(display, False);
1398  JXCheckTypedWindowEvent(display, np->window, UnmapNotify, &event);
1399 
1400 }
1401 
1404 {
1405 
1406  XConfigureEvent event;
1407  const ScreenType *sp;
1408 
1409  Assert(np);
1410 
1411  memset(&event, 0, sizeof(event));
1412  event.type = ConfigureNotify;
1413  event.event = np->window;
1414  event.window = np->window;
1415  if(np->state.status & STAT_FULLSCREEN) {
1416  sp = GetCurrentScreen(np->x, np->y);
1417  event.x = sp->x;
1418  event.y = sp->y;
1419  event.width = sp->width;
1420  event.height = sp->height;
1421  } else {
1422  event.x = np->x;
1423  event.y = np->y;
1424  event.width = np->width;
1425  event.height = np->height;
1426  }
1427 
1428  JXSendEvent(display, np->window, False, StructureNotifyMask,
1429  (XEvent*)&event);
1430 
1431 }
1432 
1439 {
1440 
1441  Assert(np);
1442 
1443  if(np == activeClient) {
1444 
1445  ColormapNode *cp = np->colormaps;
1446  char wasInstalled = 0;
1447  while(cp) {
1448  XWindowAttributes attr;
1449  if(JXGetWindowAttributes(display, cp->window, &attr)) {
1450  if(attr.colormap != None) {
1451  if(attr.colormap == np->cmap) {
1452  wasInstalled = 1;
1453  }
1454  JXInstallColormap(display, attr.colormap);
1455  }
1456  }
1457  cp = cp->next;
1458  }
1459 
1460  if(!wasInstalled && np->cmap != None) {
1462  }
1463 
1464  }
1465 
1466 }
1467 
1469 void SignalUrgent(const TimeType *now, int x, int y, Window w, void *data)
1470 {
1471 
1472  ClientNode *np = (ClientNode*)data;
1473 
1474  /* Redraw borders. */
1475  if(np->state.status & STAT_FLASH) {
1476  np->state.status &= ~STAT_FLASH;
1477  } else if(!(np->state.status & STAT_NOTURGENT)) {
1478  np->state.status |= STAT_FLASH;
1479  }
1480  DrawBorder(np);
1483 
1484 }
1485 
1488 {
1489  if(np->state.status & STAT_MAPPED) {
1490  np->state.status &= ~STAT_MAPPED;
1492  }
1493 }
1494 

joewing.net / Projects / JWM