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