1 /**
2  * @file desktop.h
3  * @author Joe Wingbermuehle
4  * @date 2004-2006
5  *
6  * @brief Header for the desktop management functions.
7  *
8  */
9 
10 #include "jwm.h"
11 #include "desktop.h"
12 #include "main.h"
13 #include "client.h"
14 #include "clientlist.h"
15 #include "taskbar.h"
16 #include "error.h"
17 #include "menu.h"
18 #include "misc.h"
19 #include "background.h"
20 #include "settings.h"
21 #include "grab.h"
22 #include "event.h"
23 #include "tray.h"
24 
25 static char **desktopNames = NULL;
26 static char *showingDesktop = NULL;
27 
28 /** Startup desktop support. */
StartupDesktops(void)29 void StartupDesktops(void)
30 {
31 
32    unsigned int x;
33 
34    if(desktopNames == NULL) {
35       desktopNames = Allocate(settings.desktopCount * sizeof(char*));
36       for(x = 0; x < settings.desktopCount; x++) {
37          desktopNames[x] = NULL;
38       }
39    }
40    for(x = 0; x < settings.desktopCount; x++) {
41       if(desktopNames[x] == NULL) {
42          desktopNames[x] = Allocate(4 * sizeof(char));
43          snprintf(desktopNames[x], 4, "%d", x + 1);
44       }
45    }
46    if(showingDesktop == NULL) {
47       showingDesktop = Allocate(settings.desktopCount * sizeof(char));
48       for(x = 0; x < settings.desktopCount; x++) {
49          showingDesktop[x] = 0;
50       }
51    }
52 }
53 
54 /** Release desktop data. */
DestroyDesktops(void)55 void DestroyDesktops(void)
56 {
57 
58    if(desktopNames) {
59       unsigned int x;
60       for(x = 0; x < settings.desktopCount; x++) {
61          Release(desktopNames[x]);
62       }
63       Release(desktopNames);
64       desktopNames = NULL;
65    }
66    if(showingDesktop) {
67       Release(showingDesktop);
68       showingDesktop = NULL;
69    }
70 
71 }
72 
73 /** Get the right desktop. */
GetRightDesktop(unsigned int desktop)74 unsigned int GetRightDesktop(unsigned int desktop)
75 {
76    const int y = desktop / settings.desktopWidth;
77    const int x = (desktop + 1) % settings.desktopWidth;
78    return y * settings.desktopWidth + x;
79 }
80 
81 /** Get the left desktop. */
GetLeftDesktop(unsigned int desktop)82 unsigned int GetLeftDesktop(unsigned int desktop)
83 {
84    const int y = currentDesktop / settings.desktopWidth;
85    int x = currentDesktop % settings.desktopWidth;
86    x = x > 0 ? x - 1 : settings.desktopWidth - 1;
87    return y * settings.desktopWidth + x;
88 }
89 
90 /** Get the above desktop. */
GetAboveDesktop(unsigned int desktop)91 unsigned int GetAboveDesktop(unsigned int desktop)
92 {
93    if(currentDesktop >= settings.desktopWidth) {
94       return currentDesktop - settings.desktopWidth;
95    }
96    return currentDesktop + (settings.desktopHeight - 1) * settings.desktopWidth;
97 }
98 
99 /** Get the below desktop. */
GetBelowDesktop(unsigned int desktop)100 unsigned int GetBelowDesktop(unsigned int desktop)
101 {
102    return (currentDesktop + settings.desktopWidth) % settings.desktopCount;
103 }
104 
105 /** Change to the desktop to the right. */
RightDesktop(void)106 char RightDesktop(void)
107 {
108    if(settings.desktopWidth > 1) {
109       const unsigned int desktop = GetRightDesktop(currentDesktop);
110       ChangeDesktop(desktop);
111       return 1;
112    } else {
113       return 0;
114    }
115 }
116 
117 /** Change to the desktop to the left. */
LeftDesktop(void)118 char LeftDesktop(void)
119 {
120    if(settings.desktopWidth > 1) {
121       const unsigned int desktop = GetLeftDesktop(currentDesktop);
122       ChangeDesktop(desktop);
123       return 1;
124    } else {
125       return 0;
126    }
127 }
128 
129 /** Change to the desktop above. */
AboveDesktop(void)130 char AboveDesktop(void)
131 {
132    if(settings.desktopHeight > 1) {
133       const int desktop = GetAboveDesktop(currentDesktop);
134       ChangeDesktop(desktop);
135       return 1;
136    } else {
137       return 0;
138    }
139 }
140 
141 /** Change to the desktop below. */
BelowDesktop(void)142 char BelowDesktop(void)
143 {
144    if(settings.desktopHeight > 1) {
145       const unsigned int desktop = GetBelowDesktop(currentDesktop);
146       ChangeDesktop(desktop);
147       return 1;
148    } else {
149       return 0;
150    }
151 }
152 
153 /** Change to the specified desktop. */
ChangeDesktop(unsigned int desktop)154 void ChangeDesktop(unsigned int desktop)
155 {
156 
157    ClientNode *np;
158    unsigned int x;
159 
160    if(JUNLIKELY(desktop >= settings.desktopCount)) {
161       return;
162    }
163 
164    if(currentDesktop == desktop) {
165       return;
166    }
167 
168    /* Hide clients from the old desktop.
169     * Note that we show clients in a separate loop to prevent an issue
170     * with clients losing focus.
171     */
172    for(x = 0; x < LAYER_COUNT; x++) {
173       for(np = nodes[x]; np; np = np->next) {
174          if(np->state.status & STAT_STICKY) {
175             continue;
176          }
177          if(np->state.desktop == currentDesktop) {
178             HideClient(np);
179          }
180       }
181    }
182 
183    /* Show clients on the new desktop. */
184    for(x = 0; x < LAYER_COUNT; x++) {
185       for(np = nodes[x]; np; np = np->next) {
186          if(np->state.status & STAT_STICKY) {
187             continue;
188          }
189          if(np->state.desktop == desktop) {
190             ShowClient(np);
191          }
192       }
193    }
194 
195    currentDesktop = desktop;
196 
197    SetCardinalAtom(rootWindow, ATOM_NET_CURRENT_DESKTOP, currentDesktop);
198    SetCardinalAtom(rootWindow, ATOM_NET_SHOWING_DESKTOP,
199                    showingDesktop[currentDesktop]);
200 
201    RequireRestack();
202    RequireTaskUpdate();
203 
204    LoadBackground(desktop);
205 
206 }
207 
208 /** Create a desktop menu. */
CreateDesktopMenu(unsigned int mask,void * context)209 Menu *CreateDesktopMenu(unsigned int mask, void *context)
210 {
211 
212    Menu *menu;
213    int x;
214 
215    menu = CreateMenu();
216    for(x = settings.desktopCount - 1; x >= 0; x--) {
217       const size_t len = strlen(desktopNames[x]);
218       MenuItem *item = CreateMenuItem(MENU_ITEM_NORMAL);
219       item->next = menu->items;
220       menu->items = item;
221 
222       item->action.type = MA_DESKTOP;
223       item->action.context = context;
224       item->action.value = x;
225 
226       item->name = Allocate(len + 3);
227       item->name[0] = (mask & (1 << x)) ? '[' : ' ';
228       memcpy(&item->name[1], desktopNames[x], len);
229       item->name[len + 1] = (mask & (1 << x)) ? ']' : ' ';
230       item->name[len + 2] = 0;
231    }
232 
233    return menu;
234 
235 }
236 
237 /** Create a sendto menu. */
CreateSendtoMenu(MenuActionType mask,void * context)238 Menu *CreateSendtoMenu(MenuActionType mask, void *context)
239 {
240 
241    Menu *menu;
242    int x;
243 
244    menu = CreateMenu();
245    for(x = settings.desktopCount - 1; x >= 0; x--) {
246       const size_t len = strlen(desktopNames[x]);
247       MenuItem *item = CreateMenuItem(MENU_ITEM_NORMAL);
248       item->next = menu->items;
249       menu->items = item;
250 
251       item->action.type = MA_SENDTO | mask;
252       item->action.context = context;
253       item->action.value = x;
254 
255       item->name = Allocate(len + 3);
256       item->name[0] = (x == currentDesktop) ? '[' : ' ';
257       memcpy(&item->name[1], desktopNames[x], len);
258       item->name[len + 1] = (x == currentDesktop) ? ']' : ' ';
259       item->name[len + 2] = 0;
260    }
261 
262    return menu;
263 }
264 
265 /** Toggle the "show desktop" state. */
ShowDesktop(void)266 void ShowDesktop(void)
267 {
268 
269    ClientNode *np;
270    int layer;
271 
272    GrabServer();
273    for(layer = 0; layer < LAYER_COUNT; layer++) {
274       for(np = nodes[layer]; np; np = np->next) {
275          if(np->state.status & STAT_NOLIST) {
276             continue;
277          }
278          if((np->state.desktop == currentDesktop) ||
279             (np->state.status & STAT_STICKY)) {
280             if(showingDesktop[currentDesktop]) {
281                if(np->state.status & STAT_SDESKTOP) {
282                   RestoreClient(np, 0);
283                }
284             } else {
285                if(np->state.status & STAT_ACTIVE) {
286                   JXSetInputFocus(display, rootWindow, RevertToParent,
287                                   CurrentTime);
288                }
289                if(np->state.status & (STAT_MAPPED | STAT_SHADED)) {
290                   MinimizeClient(np, 0);
291                   np->state.status |= STAT_SDESKTOP;
292                }
293             }
294          }
295       }
296    }
297    RequireRestack();
298    RequireTaskUpdate();
299    JXSync(display, True);
300 
301    if(showingDesktop[currentDesktop]) {
302       char first = 1;
303       JXSync(display, False);
304       for(layer = 0; layer < LAYER_COUNT; layer++) {
305          for(np = nodes[layer]; np; np = np->next) {
306             if(np->state.status & STAT_NOLIST) {
307                continue;
308             }
309             if((np->state.desktop == currentDesktop) ||
310                (np->state.status & STAT_STICKY)) {
311                if(first) {
312                   FocusClient(np);
313                   first = 0;
314                }
315                DrawBorder(np);
316             }
317          }
318       }
319       showingDesktop[currentDesktop] = 0;
320    } else {
321       showingDesktop[currentDesktop] = 1;
322    }
323    SetCardinalAtom(rootWindow, ATOM_NET_SHOWING_DESKTOP,
324                    showingDesktop[currentDesktop]);
325    UngrabServer();
326    DrawTray();
327 
328 }
329 
330 /** Set the name for a desktop. */
SetDesktopName(unsigned int desktop,const char * str)331 void SetDesktopName(unsigned int desktop, const char *str)
332 {
333 
334 
335    if(JUNLIKELY(!str)) {
336       Warning(_("empty Desktops Name tag"));
337       return;
338    }
339 
340    Assert(desktop < settings.desktopWidth * settings.desktopHeight);
341 
342    if(!desktopNames) {
343       unsigned int x;
344       desktopNames = Allocate(settings.desktopCount * sizeof(char*));
345       for(x = 0; x < settings.desktopCount; x++) {
346          desktopNames[x] = NULL;
347       }
348    }
349 
350    Assert(desktopNames[desktop] == NULL);
351 
352    desktopNames[desktop] = CopyString(str);
353 
354 }
355 
356 /** Get the name of a desktop. */
GetDesktopName(unsigned int desktop)357 const char *GetDesktopName(unsigned int desktop)
358 {
359    Assert(desktop < settings.desktopCount);
360    if(desktopNames && desktopNames[desktop]) {
361       return desktopNames[desktop];
362    } else {
363       return "";
364    }
365 }
366 
367