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