1 /**
2  * @file clientlist.c
3  * @author Joe Wingbermuehle
4  * @date 2007
5  *
6  * @brief Functions to manage lists of clients.
7  *
8  */
9 
10 #include "jwm.h"
11 #include "clientlist.h"
12 #include "client.h"
13 #include "key.h"
14 #include "event.h"
15 #include "tray.h"
16 #include "settings.h"
17 
18 ClientNode *nodes[LAYER_COUNT];
19 ClientNode *nodeTail[LAYER_COUNT];
20 
21 static Window *windowStack = NULL;  /**< Image of the window stack. */
22 static int windowStackSize = 0;     /**< Size of the image. */
23 static int windowStackCurrent = 0;  /**< Current location in the image. */
24 static char walkingWindows = 0;     /**< Are we walking windows? */
25 static char wasMinimized = 0;       /**< Was the current window minimized? */
26 
27 /** Determine if a client is allowed focus. */
ShouldFocus(const ClientNode * np,char current)28 char ShouldFocus(const ClientNode *np, char current)
29 {
30 
31    /* Only display clients on the current desktop or clients that are sticky. */
32    if(!settings.listAllTasks || current) {
33       if(!IsClientOnCurrentDesktop(np)) {
34          return 0;
35       }
36    }
37 
38    /* Don't display a client if it doesn't want to be displayed. */
39    if(np->state.status & STAT_NOLIST) {
40       return 0;
41    }
42 
43    /* Don't display a client on the tray if it has an owner. */
44    if(np->owner != None) {
45       return 0;
46    }
47 
48    if(!(np->state.status & (STAT_MAPPED | STAT_MINIMIZED | STAT_SHADED))) {
49       return 0;
50    }
51 
52    return 1;
53 
54 }
55 
56 /** Start walking windows in client list order. */
StartWindowWalk(void)57 void StartWindowWalk(void)
58 {
59    JXGrabKeyboard(display, rootWindow, False, GrabModeAsync,
60                   GrabModeAsync, CurrentTime);
61    RaiseTrays();
62    walkingWindows = 1;
63 }
64 
65 /** Start walking the window stack. */
StartWindowStackWalk(void)66 void StartWindowStackWalk(void)
67 {
68 
69    /* Get an image of the window stack.
70     * Here we get the Window IDs rather than client pointers so
71     * clients can be added/removed without disrupting the stack walk.
72     */
73 
74    ClientNode *np;
75    int layer;
76    int count;
77 
78    /* If we are already walking the stack, just return. */
79    if(windowStack != NULL) {
80       return;
81    }
82 
83    /* First determine how much space to allocate for windows. */
84    count = 0;
85    for(layer = LAST_LAYER; layer >= FIRST_LAYER; layer--) {
86       for(np = nodes[layer]; np; np = np->next) {
87          if(ShouldFocus(np, 1)) {
88             ++count;
89          }
90       }
91    }
92 
93    /* If there were no windows to walk, don't even start. */
94    if(count == 0) {
95       return;
96    }
97 
98    /* Allocate space for the windows. */
99    windowStack = Allocate(sizeof(Window) * count);
100 
101    /* Copy windows into the array. */
102    windowStackSize = 0;
103    for(layer = LAST_LAYER; layer >= FIRST_LAYER; layer--) {
104       for(np = nodes[layer]; np; np = np->next) {
105          if(ShouldFocus(np, 1)) {
106             windowStack[windowStackSize++] = np->window;
107          }
108       }
109    }
110 
111    Assert(windowStackSize == count);
112 
113    windowStackCurrent = 0;
114 
115    JXGrabKeyboard(display, rootWindow, False, GrabModeAsync,
116                   GrabModeAsync, CurrentTime);
117 
118    RaiseTrays();
119 
120    walkingWindows = 1;
121    wasMinimized = 0;
122 
123 }
124 
125 /** Move to the next window in the window stack. */
WalkWindowStack(char forward)126 void WalkWindowStack(char forward)
127 {
128 
129    ClientNode *np;
130 
131    if(windowStack != NULL) {
132       int x;
133 
134       if(wasMinimized) {
135          np = FindClientByWindow(windowStack[windowStackCurrent]);
136          if(np) {
137             MinimizeClient(np, 1);
138          }
139       }
140 
141       /* Loop until we either raise a window or go through them all. */
142       for(x = 0; x < windowStackSize; x++) {
143 
144          /* Move to the next/previous window (wrap if needed). */
145          if(forward) {
146              windowStackCurrent = (windowStackCurrent + 1) % windowStackSize;
147          } else {
148              if(windowStackCurrent == 0) {
149                  windowStackCurrent = windowStackSize;
150              }
151              windowStackCurrent -= 1;
152          }
153 
154          /* Look up the window. */
155          np = FindClientByWindow(windowStack[windowStackCurrent]);
156 
157          /* Skip this window if it no longer exists or is currently in
158           * a state that doesn't allow focus.
159           */
160          if(np == NULL || !ShouldFocus(np, 1)
161             || (np->state.status & STAT_ACTIVE)) {
162             continue;
163          }
164 
165          /* Show the window.
166           * Only when the walk completes do we update the stacking order. */
167          RestackClients();
168          if(np->state.status & STAT_MINIMIZED) {
169             RestoreClient(np, 1);
170             wasMinimized = 1;
171          } else {
172             wasMinimized = 0;
173          }
174          JXRaiseWindow(display, np->parent ? np->parent : np->window);
175          FocusClient(np);
176          break;
177 
178       }
179 
180    }
181 
182 }
183 
184 /** Stop walking the window stack or client list. */
StopWindowWalk(void)185 void StopWindowWalk(void)
186 {
187 
188    ClientNode *np;
189 
190    /* Raise the selected window and free the window array. */
191    if(windowStack != NULL) {
192 
193       /* Look up the current window. */
194       np = FindClientByWindow(windowStack[windowStackCurrent]);
195       if(np) {
196          if(np->state.status & STAT_MINIMIZED) {
197             RestoreClient(np, 1);
198          } else {
199             RaiseClient(np);
200          }
201       }
202 
203       Release(windowStack);
204       windowStack = NULL;
205 
206       windowStackSize = 0;
207       windowStackCurrent = 0;
208 
209    }
210 
211    if(walkingWindows) {
212       JXUngrabKeyboard(display, CurrentTime);
213       LowerTrays();
214       walkingWindows = 0;
215    }
216 
217 }
218 
219 /** Focus the next client in the stacking order. */
FocusNextStacked(ClientNode * np)220 void FocusNextStacked(ClientNode *np)
221 {
222 
223    int x;
224    ClientNode *tp;
225 
226    Assert(np);
227 
228    for(tp = np->next; tp; tp = tp->next) {
229       if((tp->state.status & (STAT_MAPPED | STAT_SHADED))
230          && !(tp->state.status & STAT_HIDDEN)) {
231          FocusClient(tp);
232          return;
233       }
234    }
235    for(x = np->state.layer - 1; x >= FIRST_LAYER; x--) {
236       for(tp = nodes[x]; tp; tp = tp->next) {
237          if((tp->state.status & (STAT_MAPPED | STAT_SHADED))
238             && !(tp->state.status & STAT_HIDDEN)) {
239             FocusClient(tp);
240             return;
241          }
242       }
243    }
244 
245    /* No client to focus. */
246    JXSetInputFocus(display, rootWindow, RevertToParent, eventTime);
247 
248 }
249 
250