chiark / gitweb /
Imported Debian patch 1.0.0-5
[e16] / src / desktops.c
1 /*
2  * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3  * Copyright (C) 2004-2009 Kim Woelders
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy
6  * of this software and associated documentation files (the "Software"), to
7  * deal in the Software without restriction, including without limitation the
8  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9  * sell copies of the Software, and to permit persons to whom the Software is
10  * furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies of the Software, its documentation and marketing & publicity
14  * materials, and acknowledgment shall be given in the documentation, materials
15  * and software packages that this Software was used.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  */
24 #include "E.h"
25 #include "aclass.h"
26 #include "backgrounds.h"
27 #include "buttons.h"
28 #include "desktops.h"
29 #include "dialog.h"
30 #include "ecompmgr.h"
31 #include "emodule.h"
32 #include "eobj.h"
33 #include "events.h"
34 #include "ewins.h"
35 #include "focus.h"
36 #include "grabs.h"
37 #include "hints.h"
38 #include "iclass.h"
39 #include "screen.h"
40 #include "settings.h"
41 #include "timers.h"
42 #include "tooltips.h"
43 #include "xwin.h"
44 #include <time.h>
45 #if USE_XRANDR
46 #include <X11/extensions/Xrandr.h>
47 #endif
48
49 #define EDESK_EVENT_MASK \
50   (ButtonPressMask | ButtonReleaseMask | \
51    EnterWindowMask | LeaveWindowMask /* | PointerMotionMask | ButtonMotionMask */ | \
52    SubstructureNotifyMask | SubstructureRedirectMask | PropertyChangeMask)
53
54 #define ENLIGHTENMENT_CONF_NUM_DESKTOPS 32
55
56 typedef struct {
57    Desk               *current;
58    Desk               *previous;
59    Desk               *desk[ENLIGHTENMENT_CONF_NUM_DESKTOPS];
60    unsigned int        order[ENLIGHTENMENT_CONF_NUM_DESKTOPS];
61    int                 drag_x0, drag_y0;
62 } Desktops;
63
64 static void         DeskRaise(unsigned int num);
65 static void         DeskLower(unsigned int num);
66 static void         DeskHandleEvents(Win win, XEvent * ev, void *prm);
67 static void         DeskButtonCallback(EObj * eo, XEvent * ev,
68                                        ActionClass * ac);
69
70 /* The desktops */
71 static Desktops     desks;
72
73 #define _DeskGet(d) (desks.desk[d])
74
75 static void
76 DeskControlsCreate(Desk * dsk)
77 {
78    char                s[512];
79    const char         *ic1, *ic2, *ic3;
80
81 #if ENABLE_DESKRAY
82    const char         *ic4;
83 #endif
84    char                ac1[64], ac2[64], ac3[64];
85    Button             *b;
86    ActionClass        *ac;
87    Action             *a;
88    int                 x[3], y[3], w[3], h[3], m, n, o;
89    const char         *t;
90
91    if (Conf.desks.dragdir < 0 || Conf.desks.dragdir > 3)
92       Conf.desks.dragdir = 2;
93    if (Conf.desks.dragbar_ordering < 0 || Conf.desks.dragbar_ordering > 5)
94       Conf.desks.dragbar_ordering = 1;
95    if (Conf.desks.dragbar_width < 0)
96       Conf.desks.dragbar_width = 0;
97    else if (Conf.desks.dragbar_width > 64)
98       Conf.desks.dragbar_width = 64;
99    if (Conf.desks.dragbar_length < 0)
100       Conf.desks.dragbar_length = 0;
101    else if (Conf.desks.dragbar_length > WinGetW(VROOT))
102       Conf.desks.dragbar_length = WinGetW(VROOT);
103
104    Esnprintf(ac1, sizeof(ac1), "DRAGBAR_DESKTOP_%i", dsk->num);
105    if (!ActionclassFind(ac1))
106      {
107         ac = ActionclassCreate(ac1, 0);
108         a = ActionCreate(EVENT_MOUSE_DOWN, 0, 0, 0, 1, 0, NULL, NULL);
109         ActionclassAddAction(ac, a);
110
111         Esnprintf(s, sizeof(s), "desk drag %i", dsk->num);
112         ActionAddTo(a, s);
113
114         a = ActionCreate(EVENT_MOUSE_DOWN, 0, 0, 0, 3, 0, NULL, NULL);
115         ActionclassAddAction(ac, a);
116         ActionAddTo(a, "menus show deskmenu");
117
118         a = ActionCreate(EVENT_MOUSE_DOWN, 0, 0, 0, 2, 0, NULL, NULL);
119         ActionclassAddAction(ac, a);
120         ActionAddTo(a, "menus show windowlist");
121
122         if (dsk->num > 0)
123           {
124              t = _("Hold down the mouse button and drag\n"
125                    "the mouse to be able to drag the desktop\n"
126                    "back and forth.\n"
127                    "Click right mouse button for a list of all\n"
128                    "Desktops and their applications.\n"
129                    "Click middle mouse button for a list of all\n"
130                    "applications currently running.\n");
131              ActionclassSetTooltipString(ac, t);
132           }
133         else
134           {
135              t = _("This is the Root desktop.\n"
136                    "You cannot drag the root desktop around.\n"
137                    "Click right mouse button for a list of all\n"
138                    "Desktops and their applications.\n"
139                    "Click middle mouse button for a list of all\n"
140                    "applications currently running.\n");
141              ActionclassSetTooltipString(ac, t);
142           }
143      }
144
145    Esnprintf(ac2, sizeof(ac2), "RAISEBUTTON_DESKTOP_%i", dsk->num);
146    if (!ActionclassFind(ac2))
147      {
148         ac = ActionclassCreate(ac2, 0);
149         a = ActionCreate(EVENT_MOUSE_UP, 1, 0, 1, 0, 0, NULL, NULL);
150         ActionclassAddAction(ac, a);
151
152         ActionAddTo(a, "desk next");
153         t = _("Click here to raise this desktop\nto the top.\n");
154         ActionclassSetTooltipString(ac, t);
155      }
156
157    Esnprintf(ac3, sizeof(ac3), "LOWERBUTTON_DESKTOP_%i", dsk->num);
158    if (!ActionclassFind(ac3))
159      {
160         ac = ActionclassCreate(ac3, 0);
161         a = ActionCreate(EVENT_MOUSE_UP, 1, 0, 1, 0, 0, NULL, NULL);
162         ActionclassAddAction(ac, a);
163
164         ActionAddTo(a, "desk prev");
165         t = _("Click here to lower this desktop\nto the bottom.\n");
166         ActionclassSetTooltipString(ac, t);
167      }
168
169    if (Conf.desks.dragdir < 2)
170      {
171         ic1 = "DESKTOP_DRAGBUTTON_VERT";
172         ic2 = "DESKTOP_RAISEBUTTON_VERT";
173         ic3 = "DESKTOP_LOWERBUTTON_VERT";
174 #if ENABLE_DESKRAY
175         ic4 = "DESKTOP_DESKRAY_VERT";
176 #endif
177      }
178    else
179      {
180         ic1 = "DESKTOP_DRAGBUTTON_HORIZ";
181         ic2 = "DESKTOP_RAISEBUTTON_HORIZ";
182         ic3 = "DESKTOP_LOWERBUTTON_HORIZ";
183 #if ENABLE_DESKRAY
184         ic4 = "DESKTOP_DESKRAY_HORIZ";
185 #endif
186      }
187
188    switch (Conf.desks.dragbar_ordering)
189      {
190      case 0:
191         m = 0;
192         n = 1;
193         o = 2;
194         break;
195      case 1:
196         m = 0;
197         n = 2;
198         o = 1;
199         break;
200      case 2:
201         m = 2;
202         n = 0;
203         o = 1;
204         break;
205      case 3:
206         m = 1;
207         n = 0;
208         o = 2;
209         break;
210      case 4:
211         m = 1;
212         n = 2;
213         o = 0;
214         break;
215      case 5:
216         m = 2;
217         n = 1;
218         o = 0;
219         break;
220      default:
221         m = 0;
222         n = 1;
223         o = 2;
224         break;
225      }
226
227    switch (Conf.desks.dragdir)
228      {
229      default:
230      case 0:
231         w[0] = w[1] = w[2] = h[0] = h[1] = Conf.desks.dragbar_width;
232         if (Conf.desks.dragbar_length == 0)
233            h[2] = WinGetH(VROOT) - (Conf.desks.dragbar_width * 2);
234         else
235            h[2] = Conf.desks.dragbar_length;
236         x[0] = x[1] = x[2] = 0;
237         y[m] = 0;
238         y[n] = y[m] + h[m];
239         y[o] = y[n] + h[n];
240         break;
241      case 1:
242         w[0] = w[1] = w[2] = h[0] = h[1] = Conf.desks.dragbar_width;
243         if (Conf.desks.dragbar_length == 0)
244            h[2] = WinGetH(VROOT) - (Conf.desks.dragbar_width * 2);
245         else
246            h[2] = Conf.desks.dragbar_length;
247         x[0] = x[1] = x[2] = WinGetW(VROOT) - Conf.desks.dragbar_width;
248         y[m] = 0;
249         y[n] = y[m] + h[m];
250         y[o] = y[n] + h[n];
251         break;
252      case 2:
253         h[0] = h[1] = h[2] = w[0] = w[1] = Conf.desks.dragbar_width;
254         if (Conf.desks.dragbar_length == 0)
255            w[2] = WinGetW(VROOT) - (Conf.desks.dragbar_width * 2);
256         else
257            w[2] = Conf.desks.dragbar_length;
258         y[0] = y[1] = y[2] = 0;
259         x[m] = 0;
260         x[n] = x[m] + w[m];
261         x[o] = x[n] + w[n];
262         break;
263      case 3:
264         h[0] = h[1] = h[2] = w[0] = w[1] = Conf.desks.dragbar_width;
265         if (Conf.desks.dragbar_length == 0)
266            w[2] = WinGetW(VROOT) - (Conf.desks.dragbar_width * 2);
267         else
268            w[2] = Conf.desks.dragbar_length;
269         y[0] = y[1] = y[2] = WinGetH(VROOT) - Conf.desks.dragbar_width;
270         x[m] = 0;
271         x[n] = x[m] + w[m];
272         x[o] = x[n] + w[n];
273         break;
274      }
275
276    b = NULL;
277
278    if (Conf.desks.dragbar_width > 0)
279      {
280         b = ButtonCreate("_DESKTOP_DRAG_CONTROL", 1, ic2, ac2, NULL, NULL,
281                          -1, FLAG_FIXED, 1, 99999, 1, 99999, 0, 0, x[0], 0,
282                          y[0], 0, 0, w[0], 0, h[0], 0, dsk->num, 0);
283         b = ButtonCreate("_DESKTOP_DRAG_CONTROL", 1, ic3, ac3, NULL, NULL,
284                          -1, FLAG_FIXED, 1, 99999, 1, 99999, 0, 0, x[1], 0,
285                          y[1], 0, 0, w[1], 0, h[1], 0, dsk->num, 0);
286         b = ButtonCreate("_DESKTOP_DRAG_CONTROL", 1, ic1, ac1, NULL, NULL,
287                          -1, FLAG_FIXED, 1, 99999, 1, 99999, 0, 0, x[2], 0,
288                          y[2], 0, 0, w[2], 0, h[2], 0, dsk->num, 0);
289         ButtonSetCallback(b, DeskButtonCallback, EoObj(dsk));
290      }
291
292 #if ENABLE_DESKRAY
293    if (dsk->num > 0)
294      {
295         if (Conf.desks.dragdir == 0)
296           {
297              b = ButtonCreate("_DESKTOP_DESKRAY_DRAG_CONTROL", 2, ic4, ac1,
298                               NULL, NULL, 1, FLAG_FIXED_VERT, 1, 99999, 1,
299                               99999, 0, 0, EoGetX(dsk), 0, EoGetY(dsk),
300                               0, 0, 0, 0, 0, 1, 0, 1);
301           }
302         else if (Conf.desks.dragdir == 1)
303           {
304              b = ButtonCreate("_DESKTOP_DESKRAY_DRAG_CONTROL", 2, ic4, ac1,
305                               NULL, NULL, 1, FLAG_FIXED_VERT, 1, 99999, 1,
306                               99999, 0, 0,
307                               EoGetX(dsk) + WinGetW(VROOT) -
308                               Conf.desks.dragbar_width, 0, EoGetY(dsk),
309                               0, 0, 0, 0, 0, 1, 0, 1);
310           }
311         else if (Conf.desks.dragdir == 2)
312           {
313              b = ButtonCreate("_DESKTOP_DESKRAY_DRAG_CONTROL", 2, ic4, ac1,
314                               NULL, NULL, 1, FLAG_FIXED_HORIZ, 1, 99999, 1,
315                               99999, 0, 0, EoGetX(dsk), 0, EoGetY(dsk),
316                               0, 0, 0, 0, 0, 1, 0, 1);
317           }
318         else
319           {
320              b = ButtonCreate("_DESKTOP_DESKRAY_DRAG_CONTROL", 2, ic4, ac1,
321                               NULL, NULL, 1, FLAG_FIXED_HORIZ, 1, 99999, 1,
322                               99999, 0, 0, EoGetX(dsk), 0,
323                               EoGetY(dsk) + WinGetH(VROOT) -
324                               Conf.desks.dragbar_width, 0, 0, 0, 0, 0, 1, 0, 1);
325           }
326      }
327 #endif
328
329    dsk->tag = b;
330 }
331
332 static void
333 DeskControlsDestroy(Desk * dsk, int id)
334 {
335    ButtonsForeach(id, dsk, ButtonDestroy);
336 }
337
338 static void
339 DeskControlsShow(Desk * dsk, int id)
340 {
341    ButtonsForeach(id, dsk, ButtonShow);
342 }
343
344 static void
345 DeskConfigure(Desk * dsk)
346 {
347    Background         *bg;
348
349    DeskControlsCreate(dsk);
350    DeskControlsShow(dsk, 1);
351
352    bg = BackgroundGetForDesk(dsk->num);
353    DeskBackgroundSet(dsk, bg);
354
355    if (dsk->num > 0)
356      {
357         EoMove(dsk, WinGetW(VROOT), 0);
358         EoMap(dsk, 0);
359      }
360
361    ModulesSignal(ESIGNAL_DESK_ADDED, dsk);
362 }
363
364 static Desk        *
365 DeskCreate(int desk, int configure)
366 {
367 #if USE_COMPOSITE
368    EObj               *eo;
369 #endif
370    Desk               *dsk;
371    Win                 win;
372    char                buf[64];
373
374    if (desk < 0 || desk >= ENLIGHTENMENT_CONF_NUM_DESKTOPS)
375       return NULL;
376
377    dsk = ECALLOC(Desk, 1);
378
379    desks.desk[desk] = dsk;
380    dsk->num = desk;
381    desks.order[desk] = desk;
382
383    win = (desk == 0) ? VROOT : NULL;
384
385    Esnprintf(buf, sizeof(buf), "Desk-%d", desk);
386    EoSetNoRedirect(dsk, 1);
387    EoInit(dsk, EOBJ_TYPE_DESK, win,
388           0, 0, WinGetW(VROOT), WinGetH(VROOT), 0, buf);
389    EventCallbackRegister(EoGetWin(dsk), 0, DeskHandleEvents, dsk);
390    dsk->bg.o = EoObj(dsk);
391    if (desk == 0)
392      {
393         desks.current = dsk;
394 #if !USE_BG_WIN_ON_ALL_DESKS    /* TBD - Use per virtual root bg window? */
395 #if USE_COMPOSITE
396         /* Add background window */
397         eo = EobjWindowCreate(EOBJ_TYPE_ROOT_BG,
398                               0, 0, WinGetW(VROOT), WinGetH(VROOT),
399                               0, "Root-bg");
400         eo->floating = 0;
401         EobjSetLayer(eo, 0);
402         EventCallbackRegister(EobjGetWin(eo), 0, DeskHandleEvents, dsk);
403         dsk->bg.o_bg = eo;
404         ESelectInput(EobjGetWin(eo), EnterWindowMask);
405 #endif
406 #endif
407      }
408    else
409      {
410         EoSetFloating(dsk, 1);
411         EoSetLayer(dsk, 0);
412         /* Set the _XROOT... atoms so apps will find them even before the bg is set */
413         HintsSetRootInfo(EoGetWin(dsk), None, 0);
414      }
415
416 #if USE_BG_WIN_ON_ALL_DESKS     /* TBD - Use per virtual root bg window? */
417 #if USE_COMPOSITE
418    /* Add background window */
419    Esnprintf(buf, sizeof(buf), "Desk-bg-%d", desk);
420    eo = EobjWindowCreate(EOBJ_TYPE_MISC,
421                          0, 0, WinGetW(VROOT), WinGetH(VROOT), 0, buf);
422    eo->floating = 0;
423    EobjReparent(eo, EoObj(dsk), 0, 0);
424    EobjSetLayer(eo, 0);
425    dsk->bg.o_bg = eo;
426    EventCallbackRegister(EobjGetWin(eo), 0, DeskHandleEvents, dsk);
427 #endif
428 #endif
429
430    HintsSetRootHints(EoGetWin(dsk));
431
432    if (configure)
433       DeskConfigure(dsk);
434
435    if (desk == 0)
436       ESelectInputChange(EoGetWin(dsk), EDESK_EVENT_MASK, 0);
437    else
438       ESelectInput(EoGetWin(dsk), EDESK_EVENT_MASK);
439
440    return dsk;
441 }
442
443 static void
444 DeskDestroy(Desk * dsk)
445 {
446    ModulesSignal(ESIGNAL_DESK_REMOVED, dsk);
447
448 #if USE_COMPOSITE
449    if (dsk->bg.o_bg)
450      {
451         EventCallbackUnregister(EobjGetWin(dsk->bg.o_bg), 0, DeskHandleEvents,
452                                 dsk);
453         EobjWindowDestroy(dsk->bg.o_bg);
454      }
455 #endif
456    EventCallbackUnregister(EoGetWin(dsk), 0, DeskHandleEvents, dsk);
457
458    DeskControlsDestroy(dsk, 1);
459    DeskControlsDestroy(dsk, 2);
460
461    if (dsk->bg.bg)
462       BackgroundDecRefcount(dsk->bg.bg);
463
464    EoFini(dsk);
465
466    desks.desk[dsk->num] = NULL;
467    Efree(dsk);
468 }
469
470 EObj               *
471 DeskGetBackgroundObj(const Desk * dsk)
472 {
473    return (dsk) ? dsk->bg.o : NULL;
474 }
475
476 Pixmap
477 DeskGetBackgroundPixmap(const Desk * dsk)
478 {
479    if (!dsk)
480       return None;
481    return dsk->bg.pmap;
482 }
483
484 Background         *
485 DeskBackgroundGet(const Desk * dsk)
486 {
487    return (dsk) ? dsk->bg.bg : NULL;
488 }
489
490 static void
491 DeskBackgroundConfigure(Desk * dsk)
492 {
493    Win                 win;
494    Pixmap              pmap = dsk->bg.pmap;
495    unsigned long       pixel = dsk->bg.pixel;
496
497    if (EDebug(EDBUG_TYPE_DESKS))
498       Eprintf
499          ("DeskBackgroundConfigure %d v=%d %#lx/%#lx: ext=%d pmap=%#lx/%#lx pixel=%#lx/%#lx\n",
500           dsk->num, dsk->viewable, EoGetXwin(dsk), EobjGetXwin(dsk->bg.o),
501           BackgroundIsNone(dsk->bg.bg), pmap, dsk->bg.pmap_set, pixel,
502           dsk->bg.pixel);
503
504 #if USE_COMPOSITE
505    if (dsk->bg.o_bg)
506      {
507         if (ECompMgrIsActive())
508           {
509              dsk->bg.o = dsk->bg.o_bg;
510              EobjMap(dsk->bg.o_bg, 0);
511           }
512         else
513           {
514              dsk->bg.o = EoObj(dsk);
515              EobjUnmap(dsk->bg.o_bg);
516           }
517      }
518 #endif
519
520    win = EobjGetWin(dsk->bg.o);
521
522    if (dsk->viewable || !dsk->bg.bg)
523      {
524 #if !USE_BG_WIN_ON_ALL_DESKS
525         if (ECompMgrDeskConfigure(dsk))
526           {
527              ESetWindowBackgroundPixmap(win, None);
528           }
529         else
530 #endif
531           {
532              if (pmap != None)
533                {
534                   ESetWindowBackgroundPixmap(win, pmap);
535                   if (dsk->num == 0 && win != VROOT)
536                      ESetWindowBackgroundPixmap(VROOT, pmap);
537                }
538              else
539                {
540                   ESetWindowBackground(win, pixel);
541                   if (dsk->num == 0 && win != VROOT)
542                      ESetWindowBackground(VROOT, pixel);
543                }
544              EClearWindow(win);
545           }
546
547         HintsSetRootInfo(EoGetWin(dsk), pmap, pixel);
548      }
549    else if (dsk->bg.bg)
550      {
551         if (!Conf.hints.set_xroot_info_on_root_window)
552            HintsSetRootInfo(EoGetWin(dsk), None, 0);
553
554         ESetWindowBackgroundPixmap(win, None);
555         if (dsk->num == 0 && win != VROOT)
556            ESetWindowBackgroundPixmap(VROOT, None);
557      }
558 }
559
560 static void
561 DeskBackgroundRefresh(Desk * dsk, int why)
562 {
563    Background         *bg = dsk->bg.bg;
564    Pixmap              pmap = dsk->bg.pmap;
565    unsigned long       pixel = dsk->bg.pixel;
566    int                 changed = 0;
567    int                 reconfigure = 0;
568
569    if (EDebug(EDBUG_TYPE_DESKS))
570       Eprintf("DeskBackgroundRefresh %d v=%d why=%d pmap=%#lx pixel=%#lx\n",
571               dsk->num, dsk->viewable, why, pmap, pixel);
572
573    switch (why)
574      {
575      case DESK_BG_REFRESH:
576         if (bg && dsk->viewable)
577            BackgroundTouch(bg);
578         break;
579
580      case DESK_BG_RECONFIGURE_ALL:
581         reconfigure = 1;
582         break;
583
584      case DESK_BG_TIMEOUT:
585         if (bg && dsk->viewable)
586            BackgroundTouch(bg);
587         return;
588
589      case DESK_BG_FREE:
590         if (!bg || dsk->viewable)
591            return;
592         break;
593      }
594
595    if (bg)
596      {
597         if (dsk->viewable)
598           {
599              if (BackgroundGetSeqNo(bg) == dsk->bg.seq_no)
600                 goto done;
601
602              pmap = BackgroundGetPixmap(bg);
603              pixel = 0;
604
605              if (pmap == None)
606                 BackgroundRealize(bg, EoGetWin(dsk), None,
607                                   EoGetW(dsk), EoGetH(dsk), 1, &pmap, &pixel);
608              if (pmap != None)
609                 BackgroundPixmapSet(bg, pmap);
610
611              dsk->bg.seq_no = BackgroundGetSeqNo(bg);
612              changed = 1;
613           }
614         else
615           {
616              if (dsk->bg.pmap == None)
617                 return;
618
619              pmap = None;
620              pixel = 0;
621              dsk->bg.seq_no = 0;
622           }
623      }
624    else
625      {
626         pmap = (Mode.root.ext_pmap_valid) ? Mode.root.ext_pmap : None;
627         pixel = 0;
628         changed = pmap != dsk->bg.pmap_set;
629      }
630
631  done:
632    dsk->bg.pmap = pmap;
633    if (reconfigure || pmap != dsk->bg.pmap_set || pixel != dsk->bg.pixel)
634      {
635         dsk->bg.pixel = pixel;
636         DeskBackgroundConfigure(dsk);
637         dsk->bg.pmap_set = pmap;
638      }
639
640    if (bg && dsk->viewable)
641       if (Conf.hints.set_xroot_info_on_root_window && dsk->num > 0)
642          HintsSetRootInfo(VROOT, pmap, pixel);
643
644    if (changed)
645       ModulesSignal(ESIGNAL_BACKGROUND_CHANGE, dsk);
646 }
647
648 void
649 DeskBackgroundSet(Desk * dsk, Background * bg)
650 {
651    if (!dsk)
652       return;
653
654    BackgroundSetForDesk(bg, dsk->num);
655    if (bg && BackgroundIsNone(bg))
656       bg = NULL;
657
658    if (dsk->bg.bg != bg)
659      {
660         if (dsk->bg.bg)
661            BackgroundDecRefcount(dsk->bg.bg);
662         if (bg)
663            BackgroundIncRefcount(bg);
664      }
665
666    dsk->bg.bg = bg;
667
668    DeskBackgroundRefresh(dsk, DESK_BG_REFRESH);
669 }
670
671 void
672 DesksBackgroundRefresh(Background * bg, int why)
673 {
674    Desk               *dsk;
675    unsigned int        i;
676
677    for (i = 0; i < Conf.desks.num; i++)
678      {
679         dsk = _DeskGet(i);
680         if (!dsk)               /* May happen during init */
681            continue;
682         if (bg && dsk->bg.bg != bg)
683            continue;
684         DeskBackgroundRefresh(dsk, why);
685      }
686 }
687
688 static void
689 DeskResize(int desk, int w, int h)
690 {
691    Desk               *dsk;
692    int                 x;
693
694    dsk = _DeskGet(desk);
695
696    if (dsk->num != 0)
697      {
698         x = (dsk->viewable) ? EoGetX(dsk) : WinGetW(VROOT);
699         EoMoveResize(dsk, x, 0, w, h);
700      }
701 #if USE_COMPOSITE
702    if (dsk->bg.o_bg)
703       EobjMoveResize(dsk->bg.o_bg, 0, 0, w, h);
704 #endif
705    DeskBackgroundRefresh(dsk, DESK_BG_REFRESH);
706    DeskControlsDestroy(dsk, 1);
707    DeskControlsCreate(dsk);
708    DeskControlsShow(dsk, 1);
709 }
710
711 Desk               *
712 DeskGet(unsigned int desk)
713 {
714    if (desk >= Conf.desks.num)
715       return NULL;
716
717    return _DeskGet(desk);
718 }
719
720 Desk               *
721 DeskGetRelative(Desk * dsk, int inc)
722 {
723    unsigned int        desk;
724
725    desk = (dsk) ? dsk->num : 0;
726    desk += inc;
727    desk %= Conf.desks.num;
728
729    return _DeskGet(desk);
730 }
731
732 void
733 DeskGetArea(const Desk * dsk, int *ax, int *ay)
734 {
735    if (!dsk)
736      {
737         *ax = *ay = 0;
738         return;
739      }
740    *ax = dsk->current_area_x;
741    *ay = dsk->current_area_y;
742 }
743
744 void
745 DeskSetArea(Desk * dsk, int ax, int ay)
746 {
747    if (!dsk)
748       return;
749
750    dsk->current_area_x = ax;
751    dsk->current_area_y = ay;
752 }
753
754 void
755 DeskSetDirtyStack(Desk * dsk, EObj * eo)
756 {
757    dsk->stack.dirty++;
758    dsk->stack.latest = eo;
759    if (EobjGetType(eo) == EOBJ_TYPE_EWIN)
760       dsk->stack.update_client_list = 1;
761    if (EDebug(EDBUG_TYPE_STACKING))
762       Eprintf("DeskSetDirtyStack %d (%d): %s\n", dsk->num, dsk->stack.dirty,
763               EobjGetName(eo));
764 }
765
766 void
767 DeskCurrentGetArea(int *ax, int *ay)
768 {
769    DeskGetArea(desks.current, ax, ay);
770 }
771
772 static void
773 DeskCurrentSetArea(int ax, int ay)
774 {
775    DeskSetArea(desks.current, ax, ay);
776 }
777
778 unsigned int
779 DesksGetNumber(void)
780 {
781    return Conf.desks.num;
782 }
783
784 Desk               *
785 DesksGetCurrent(void)
786 {
787    return desks.current;
788 }
789
790 unsigned int
791 DesksGetCurrentNum(void)
792 {
793    return desks.current->num;
794 }
795
796 void
797 DesksSetCurrent(Desk * dsk)
798 {
799    if (!dsk)
800       return;
801    desks.current = dsk;
802 }
803
804 static void
805 DesksResize(int w, int h)
806 {
807    unsigned int        i;
808
809    BackgroundsInvalidate(0);
810
811    for (i = 0; i < Conf.desks.num; i++)
812       DeskResize(i, w, h);
813
814    EdgeWindowsShow();
815
816    ModulesSignal(ESIGNAL_DESK_RESIZE, NULL);
817 }
818
819 static void
820 ChangeNumberOfDesktops(unsigned int quantity)
821 {
822    unsigned int        i;
823    int                 j, num;
824    EWin               *const *lst;
825
826    if (quantity >= ENLIGHTENMENT_CONF_NUM_DESKTOPS)
827       quantity = ENLIGHTENMENT_CONF_NUM_DESKTOPS;
828
829    if (quantity <= 0 || quantity == Conf.desks.num)
830       return;
831
832    for (i = quantity; i < Conf.desks.num; i++)
833       DeskLower(i);
834
835    if (quantity > Conf.desks.num)
836      {
837         while (Conf.desks.num < quantity)
838           {
839              Conf.desks.num++;
840              DeskCreate(Conf.desks.num - 1, 1);
841           }
842      }
843    else if (quantity < Conf.desks.num)
844      {
845         lst = EwinListGetAll(&num);
846         for (j = 0; j < num; j++)
847           {
848              if (EoGetDeskNum(lst[j]) >= quantity)
849                 EwinMoveToDesktop(lst[j], _DeskGet(quantity - 1));
850           }
851
852         while (Conf.desks.num > quantity)
853           {
854              DeskDestroy(_DeskGet(Conf.desks.num - 1));
855              Conf.desks.num--;
856           }
857      }
858
859    if (DesksGetCurrentNum() >= Conf.desks.num)
860       DeskGotoNum(Conf.desks.num - 1);
861
862    HintsSetDesktopConfig();
863
864    autosave();
865 }
866
867 static void
868 DesksControlsCreate(void)
869 {
870    unsigned int        i;
871
872    for (i = 0; i < Conf.desks.num; i++)
873       DeskControlsCreate(_DeskGet(i));
874 }
875
876 static void
877 DesksControlsDestroy(void)
878 {
879    unsigned int        i;
880
881    for (i = 0; i < Conf.desks.num; i++)
882       DeskControlsDestroy(_DeskGet(i), 1);
883 }
884
885 static void
886 DesksControlsShow(void)
887 {
888    unsigned int        i;
889
890    for (i = 0; i < Conf.desks.num; i++)
891       DeskControlsShow(_DeskGet(i), 1);
892 }
893
894 static void
895 DesksControlsRefresh(void)
896 {
897    DesksControlsDestroy();
898    DesksControlsCreate();
899    DesksControlsShow();
900
901    autosave();
902 }
903
904 #if 0                           /* Unused */
905 static void
906 DeskShowTabs(void)
907 {
908    ButtonsForeach(2, NULL, ButtonShow);
909 }
910
911 static void
912 DeskHideTabs(void)
913 {
914    ButtonsForeach(2, NULL, ButtonHide);
915 }
916 #endif
917
918 static void
919 DeskButtonShowDefault(Button * b)
920 {
921    if (!ButtonDoShowDefault(b))
922       return;
923    ButtonShow(b);
924 }
925
926 static void
927 DeskShowButtons(void)
928 {
929    ButtonsForeach(0, NULL, DeskButtonShowDefault);
930 }
931
932 static void
933 MoveToDeskTop(Desk * dsk)
934 {
935    int                 i, j;
936
937    EoRaise(dsk);
938
939    j = -1;
940    i = 0;
941    while (j < 0 && i < (int)Conf.desks.num)
942      {
943         if (desks.order[i] == dsk->num)
944            j = i;
945         i++;
946      }
947    if (j < 0)
948       return;
949    if (j > 0)
950      {
951         for (i = j - 1; i >= 0; i--)
952            desks.order[i + 1] = desks.order[i];
953         desks.order[0] = dsk->num;
954      }
955 }
956
957 static void
958 MoveToDeskBottom(Desk * dsk)
959 {
960    int                 i, j;
961
962    EoLower(dsk);
963
964    j = -1;
965    i = 0;
966    while (j < 0 && i < (int)Conf.desks.num)
967      {
968         if (desks.order[i] == dsk->num)
969            j = i;
970         i++;
971      }
972    if (j < 0)
973       return;
974    if (j < (int)Conf.desks.num - 1)
975      {
976         for (i = j; i < (int)Conf.desks.num - 1; i++)
977            desks.order[i] = desks.order[i + 1];
978         desks.order[Conf.desks.num - 1] = dsk->num;
979      }
980 }
981
982 Desk               *
983 DesktopAt(int x, int y)
984 {
985    Desk               *dsk;
986    unsigned int        i;
987
988    for (i = 0; i < Conf.desks.num; i++)
989      {
990         dsk = _DeskGet(desks.order[i]);
991         if (x >= EoGetX(dsk) && x < (EoGetX(dsk) + WinGetW(VROOT)) &&
992             y >= EoGetY(dsk) && y < (EoGetY(dsk) + WinGetH(VROOT)))
993            return _DeskGet(desks.order[i]);
994      }
995    return _DeskGet(0);
996 }
997
998 static void
999 DesksStackingCheck(void)
1000 {
1001    Desk               *dsk;
1002    unsigned int        i;
1003
1004    for (i = 0; i < Conf.desks.num; i++)
1005      {
1006         dsk = _DeskGet(i);
1007         if (i && !dsk->viewable)
1008            continue;
1009         if (!dsk->stack.dirty)
1010            continue;
1011         DeskRestack(dsk);
1012      }
1013 }
1014
1015 static void
1016 _DesksIdler(void *data __UNUSED__)
1017 {
1018    DesksStackingCheck();
1019 }
1020
1021 static void
1022 DeskMove(Desk * dsk, int x, int y)
1023 {
1024    Desk               *dd;
1025    unsigned int        i;
1026    int                 n, v, dx, dy;
1027
1028    if (dsk->num <= 0)
1029       return;
1030
1031    n = -1;
1032    i = 0;
1033    while (n < 0 && i < Conf.desks.num)
1034      {
1035         if (desks.order[i] == dsk->num)
1036            n = i;
1037         i++;
1038      }
1039    if (n < 0)                   /* Should not be possible */
1040       return;
1041
1042    dx = x - EoGetX(dsk);
1043    dy = y - EoGetY(dsk);
1044
1045    if (x == 0 && y == 0)
1046      {
1047         /* Desks below are obscured - touch and set unviewable */
1048         for (i = n + 1; i < Conf.desks.num; i++)
1049           {
1050              dd = _DeskGet(desks.order[i]);
1051              if (dd->viewable)
1052                 BackgroundTouch(dd->bg.bg);
1053              dd->viewable = 0;
1054           }
1055      }
1056    else
1057      {
1058         v = dsk->viewable;
1059
1060         for (i = n + 1; i < Conf.desks.num; i++)
1061           {
1062              dd = _DeskGet(desks.order[i]);
1063              if (!dd->viewable && v)
1064                {
1065                   dd->viewable = 1;
1066                   DeskBackgroundRefresh(_DeskGet(desks.order[i]),
1067                                         DESK_BG_REFRESH);
1068                }
1069              else if (dd->viewable && !v)
1070                {
1071                   BackgroundTouch(dd->bg.bg);
1072                   dd->viewable = 0;
1073                }
1074
1075              if (EoGetX(dd) == 0 && EoGetY(dd) == 0)
1076                 v = 0;
1077           }
1078      }
1079
1080    EoMove(dsk, x, y);
1081
1082    if (dsk->tag)
1083       ButtonMoveRelative(dsk->tag, dx, dy);
1084
1085 #if 0                           /* FIXME - Remove? */
1086    EWin               *const *lst;
1087
1088    lst = EwinListGetAll(&n);
1089    for (i = 0; i < (unsigned int)n; i++)
1090       if (EoGetDesk(lst[i]) == dsk)
1091          ICCCM_Configure(lst[i]);
1092 #endif
1093 }
1094
1095 static void
1096 DeskHide(unsigned int desk)
1097 {
1098    Desk               *dsk;
1099
1100    if (desk <= 0 || desk >= Conf.desks.num)
1101       return;
1102
1103    dsk = _DeskGet(desk);
1104
1105    if (dsk->viewable)
1106       BackgroundTouch(dsk->bg.bg);
1107    dsk->viewable = 0;
1108    EoMove(dsk, WinGetW(VROOT), 0);
1109 }
1110
1111 static void
1112 DeskEnter(Desk * dsk)
1113 {
1114    int                 i;
1115
1116    EGrabServer();
1117
1118    dsk->viewable = 1;
1119    DeskBackgroundRefresh(dsk, DESK_BG_REFRESH);
1120    MoveToDeskTop(dsk);
1121
1122    desks.previous = desks.current = dsk;
1123
1124    if (dsk->num == 0)
1125      {
1126         for (i = Conf.desks.num - 1; i > 0; i--)
1127            DeskHide(desks.order[i]);
1128      }
1129
1130    EwinsMoveStickyToDesk(dsk);
1131    ButtonsMoveStickyToDesk(dsk);
1132    DesksStackingCheck();
1133    HintsSetCurrentDesktop();
1134    EdgeWindowsShow();
1135
1136    EUngrabServer();
1137 }
1138
1139 void
1140 DeskGotoNum(unsigned int desk)
1141 {
1142    Desk               *dsk;
1143
1144    if (Conf.desks.desks_wraparound)
1145       desk %= Conf.desks.num;
1146    if (desk >= Conf.desks.num || desk == desks.current->num)
1147       return;
1148
1149    dsk = _DeskGet(desk);
1150    DeskGoto(dsk);
1151 }
1152
1153 static void
1154 DeskSwitchStart(void)
1155 {
1156    FocusNewDeskBegin();
1157 }
1158
1159 static void
1160 DeskSwitchDone(void)
1161 {
1162    FocusNewDesk();
1163 }
1164
1165 void
1166 DeskGoto(Desk * dsk)
1167 {
1168    if (!dsk || dsk == desks.previous)
1169       return;
1170
1171    if (EDebug(EDBUG_TYPE_DESKS))
1172       Eprintf("DeskGoto %d\n", dsk->num);
1173
1174    ModulesSignal(ESIGNAL_DESK_SWITCH_START, NULL);
1175
1176    ActionsSuspend();
1177    DeskSwitchStart();
1178
1179    if (dsk->num > 0)
1180      {
1181         if (Conf.desks.slidein)
1182           {
1183              if (!dsk->viewable)
1184                {
1185                   int                 x, y;
1186
1187                   switch (Conf.desks.dragdir)
1188                     {
1189                     default:
1190                     case 0:
1191                        x = WinGetW(VROOT);
1192                        y = 0;
1193                        break;
1194                     case 1:
1195                        x = -WinGetW(VROOT);
1196                        y = 0;
1197                        break;
1198                     case 2:
1199                        x = 0;
1200                        y = WinGetH(VROOT);
1201                        break;
1202                     case 3:
1203                        x = 0;
1204                        y = -WinGetH(VROOT);
1205                        break;
1206                     }
1207                   DeskMove(dsk, x, y);
1208                   DeskEnter(dsk);
1209                   EobjSlideTo(&dsk->o, x, y, 0, 0, Conf.desks.slidespeed);
1210                }
1211              else
1212                {
1213                   EobjSlideTo(&dsk->o, EoGetX(dsk), EoGetY(dsk), 0, 0,
1214                               Conf.desks.slidespeed);
1215                   DeskEnter(dsk);
1216                }
1217           }
1218         else
1219           {
1220              DeskEnter(dsk);
1221           }
1222         DeskMove(dsk, 0, 0);
1223      }
1224    else
1225      {
1226         DeskEnter(dsk);
1227      }
1228
1229    DeskSwitchDone();
1230    ActionsResume();
1231
1232    ModulesSignal(ESIGNAL_DESK_SWITCH_DONE, NULL);
1233
1234    if (EDebug(EDBUG_TYPE_DESKS))
1235       Eprintf("DeskGoto %d done\n", dsk->num);
1236 }
1237
1238 static void
1239 UncoverDesktop(unsigned int desk)
1240 {
1241    Desk               *dsk;
1242
1243    if (desk >= Conf.desks.num)
1244       return;
1245
1246    dsk = _DeskGet(desk);
1247
1248    dsk->viewable = 1;
1249    DeskBackgroundRefresh(dsk, DESK_BG_REFRESH);
1250 }
1251
1252 static void
1253 DeskRaise(unsigned int desk)
1254 {
1255    Desk               *dsk;
1256
1257    if (desk >= Conf.desks.num)
1258       return;
1259
1260    dsk = _DeskGet(desk);
1261
1262    if (EDebug(EDBUG_TYPE_DESKS))
1263       Eprintf("DeskRaise(%d) current=%d\n", desk, desks.current->num);
1264
1265    DeskSwitchStart();
1266    DeskEnter(dsk);
1267    DeskSwitchDone();
1268
1269    ModulesSignal(ESIGNAL_DESK_SWITCH_DONE, NULL);
1270
1271    ESync(ESYNC_DESKS);
1272 }
1273
1274 static void
1275 DeskLower(unsigned int desk)
1276 {
1277    Desk               *dsk;
1278
1279    if ((desk <= 0) || (desk >= Conf.desks.num))
1280       return;
1281
1282    dsk = _DeskGet(desk);
1283
1284    DeskSwitchStart();
1285    MoveToDeskBottom(dsk);
1286
1287    if (EDebug(EDBUG_TYPE_DESKS))
1288       Eprintf("DeskLower(%d) %d -> %d\n", desk, desks.current->num,
1289               desks.order[0]);
1290
1291    desks.previous = desks.current = DeskGet(desks.order[0]);
1292
1293    EGrabServer();
1294
1295    UncoverDesktop(desks.order[0]);
1296    DeskHide(desk);
1297
1298    EwinsMoveStickyToDesk(desks.current);
1299    ButtonsMoveStickyToDesk(desks.current);
1300    DesksStackingCheck();
1301    DeskSwitchDone();
1302    if (Mode.mode == MODE_NONE)
1303       ModulesSignal(ESIGNAL_DESK_SWITCH_DONE, NULL);
1304    HintsSetCurrentDesktop();
1305
1306    EUngrabServer();
1307    ESync(ESYNC_DESKS);
1308 }
1309
1310 #if 0                           /* Unused */
1311 void
1312 DeskShow(int desk)
1313 {
1314    Desk               *dsk;
1315    int                 i;
1316
1317    if (desk < 0 || desk >= Conf.desks.num)
1318       return;
1319
1320    dsk = _DeskGet(desk);
1321
1322    dsk->viewable = 1;
1323    DeskBackgroundRefresh(dsk, DESK_BG_REFRESH);
1324    MoveToDeskTop(desk);
1325
1326    if (desk == 0)
1327      {
1328         for (i = Conf.desks.num - 1; i > 0; i--)
1329            DeskHide(desks.order[i]);
1330      }
1331 }
1332 #endif
1333
1334 static void
1335 DeskRestackSimple(Desk * dsk)
1336 {
1337    EObj               *const *lst, *eo;
1338    int                 i, num;
1339    XWindowChanges      xwc;
1340    unsigned int        value_mask;
1341
1342    eo = dsk->stack.latest;
1343    eo->stacked = 1;
1344
1345    if (EDebug(EDBUG_TYPE_STACKING))
1346       Eprintf("DeskRestackSimple %#lx %s\n", EobjGetXwin(eo), EobjGetName(eo));
1347
1348    lst = EobjListStackGetForDesk(&num, dsk);
1349    if (num < 2)
1350       return;
1351
1352    for (i = 0; i < num; i++)
1353       if (lst[i] == eo)
1354          break;
1355    if (i >= num)
1356       return;
1357
1358    if (i < num - 1)
1359      {
1360         xwc.stack_mode = Above;
1361         xwc.sibling = EobjGetXwin(lst[i + 1]);
1362      }
1363    else
1364      {
1365         xwc.stack_mode = Below;
1366         xwc.sibling = EobjGetXwin(lst[i - 1]);
1367      }
1368    value_mask = CWSibling | CWStackMode;
1369    if (EDebug(EDBUG_TYPE_STACKING))
1370       Eprintf("DeskRestackSimple %#10lx %s %#10lx\n", EobjGetXwin(eo),
1371               (xwc.stack_mode == Above) ? "Above" : "Below", xwc.sibling);
1372    XConfigureWindow(disp, EobjGetXwin(eo), value_mask, &xwc);
1373 }
1374
1375 #define _APPEND_TO_WIN_LIST(win) \
1376   { \
1377      wl = EREALLOC(Window, wl, ++tot); \
1378      wl[tot - 1] = win; \
1379   }
1380 void
1381 DeskRestack(Desk * dsk)
1382 {
1383    Window             *wl;
1384    int                 i, num, tot;
1385    EObj               *const *lst, *eo;
1386
1387    if (!dsk->stack.dirty)
1388       return;
1389
1390    /* Special case if only one window needs restacking */
1391    if (dsk->stack.dirty == 1)
1392      {
1393         DeskRestackSimple(dsk);
1394         goto done;
1395      }
1396
1397    /* Build the window stack, top to bottom */
1398    tot = 0;
1399    wl = NULL;
1400    lst = EobjListStackGetForDesk(&num, dsk);
1401
1402    /* Normal objects */
1403    for (i = 0; i < num; i++)
1404      {
1405         eo = lst[i];
1406         _APPEND_TO_WIN_LIST(EobjGetXwin(eo));
1407         eo->stacked = 1;
1408      }
1409
1410    if (EDebug(EDBUG_TYPE_STACKING))
1411      {
1412         Eprintf("DeskRestack %d (%d):\n", dsk->num, dsk->stack.dirty);
1413         for (i = 0; i < tot; i++)
1414            Eprintf(" win=%#10lx parent=%#10lx\n", wl[i],
1415                    EXWindowGetParent(wl[i]));
1416      }
1417
1418    EXRestackWindows(wl, tot);
1419
1420    Efree(wl);
1421
1422  done:
1423    if (dsk->stack.update_client_list)
1424      {
1425         dsk->stack.update_client_list = 0;
1426         HintsSetClientStacking();
1427         ClickGrabsUpdate();
1428      }
1429    dsk->stack.dirty = 0;
1430    dsk->stack.latest = NULL;
1431 }
1432
1433 void
1434 DeskGotoByEwin(EWin * ewin)
1435 {
1436    if (EoIsSticky(ewin) || EoIsFloating(ewin))
1437       return;
1438
1439    DeskGoto(EoGetDesk(ewin));
1440    DeskCurrentGotoArea(ewin->area_x, ewin->area_y);
1441 }
1442
1443 /*
1444  * Areas
1445  */
1446 static int          area_w = 3;
1447 static int          area_h = 3;
1448
1449 void
1450 DesksFixArea(int *ax, int *ay)
1451 {
1452    if (*ax < 0)
1453      {
1454         if (Conf.desks.areas_wraparound)
1455            *ax = area_w - 1;
1456         else
1457            *ax = 0;
1458      }
1459    else if (*ax >= area_w)
1460      {
1461         if (Conf.desks.areas_wraparound)
1462            *ax = 0;
1463         else
1464            *ax = area_w - 1;
1465      }
1466
1467    if (*ay < 0)
1468      {
1469         if (Conf.desks.areas_wraparound)
1470            *ay = area_h - 1;
1471         else
1472            *ay = 0;
1473      }
1474    else if (*ay >= area_h)
1475      {
1476         if (Conf.desks.areas_wraparound)
1477            *ay = 0;
1478         else
1479            *ay = area_h - 1;
1480      }
1481 }
1482
1483 static int
1484 AreaXYToLinear(int ax, int ay)
1485 {
1486    DesksFixArea(&ax, &ay);
1487    return (ay * area_w) + ax;
1488 }
1489
1490 static void
1491 AreaLinearToXY(int a, int *ax, int *ay)
1492 {
1493    if (a < 0)
1494       a = 0;
1495    else if (a >= (area_w * area_h))
1496       a = (area_w * area_h) - 1;
1497    *ay = a / area_w;
1498    *ax = a - (*ay * area_w);
1499 }
1500
1501 static void
1502 SetAreaSize(int aw, int ah)
1503 {
1504    if (aw < 1)
1505       aw = 1;
1506    if (ah < 1)
1507       ah = 1;
1508    Conf.desks.areas_nx = area_w = aw;
1509    Conf.desks.areas_ny = area_h = ah;
1510    HintsSetViewportConfig();
1511    EdgeWindowsShow();
1512    ModulesSignal(ESIGNAL_AREA_CONFIGURED, NULL);
1513 }
1514
1515 void
1516 DesksGetAreaSize(int *aw, int *ah)
1517 {
1518    *aw = area_w;
1519    *ah = area_h;
1520 }
1521
1522 static void
1523 SetNewAreaSize(int ax, int ay)
1524 {
1525
1526    int                 a, b, i, num;
1527    EWin               *const *lst;
1528
1529    if (ax <= 0)
1530       return;
1531    if (ay <= 0)
1532       return;
1533
1534    DesksGetAreaSize(&a, &b);
1535    if ((a == ax) && (b == ay))
1536       return;
1537
1538    SetAreaSize(ax, ay);
1539
1540    lst = EwinListGetAll(&num);
1541    for (i = 0; i < num; i++)
1542      {
1543         if (!EoIsSticky(lst[i]))
1544           {
1545              if (lst[i]->area_x >= ax)
1546                 EwinMoveToArea(lst[i], ax - 1, lst[i]->area_x);
1547              if (lst[i]->area_y >= ay)
1548                 EwinMoveToArea(lst[i], lst[i]->area_x, ay - 1);
1549           }
1550      }
1551
1552    DeskCurrentGetArea(&a, &b);
1553    if (a >= ax)
1554      {
1555         DeskCurrentGotoArea(ax - 1, b);
1556         DeskCurrentGetArea(&a, &b);
1557      }
1558    if (b >= ay)
1559       DeskCurrentGotoArea(a, ay - 1);
1560
1561    autosave();
1562 }
1563
1564 static void
1565 SetCurrentLinearArea(int a)
1566 {
1567    int                 ax, ay;
1568
1569    AreaLinearToXY(a, &ax, &ay);
1570    DeskCurrentGotoArea(ax, ay);
1571 }
1572
1573 static int
1574 GetCurrentLinearArea(void)
1575 {
1576    int                 ax, ay;
1577
1578    DeskCurrentGetArea(&ax, &ay);
1579
1580    return AreaXYToLinear(ax, ay);
1581 }
1582
1583 static void
1584 MoveCurrentLinearAreaBy(int a)
1585 {
1586    SetCurrentLinearArea(GetCurrentLinearArea() + a);
1587 }
1588
1589 void
1590 DeskCurrentGotoArea(int ax, int ay)
1591 {
1592    EWin               *const *lst, *ewin;
1593    int                 i, num, dx, dy, pax, pay;
1594
1595    if ((Mode.mode == MODE_RESIZE) || (Mode.mode == MODE_RESIZE_H)
1596        || (Mode.mode == MODE_RESIZE_V))
1597       return;
1598
1599    DesksFixArea(&ax, &ay);
1600    DeskCurrentGetArea(&pax, &pay);
1601
1602    if (ax == pax && ay == pay)
1603       return;
1604
1605    if (EDebug(EDBUG_TYPE_DESKS))
1606       Eprintf("DeskCurrentGotoArea %d,%d\n", ax, ay);
1607
1608    ModulesSignal(ESIGNAL_AREA_SWITCH_START, NULL);
1609
1610    dx = WinGetW(VROOT) * (ax - pax);
1611    dy = WinGetH(VROOT) * (ay - pay);
1612
1613    if (dx < 0)
1614       SoundPlay(SOUND_MOVE_AREA_LEFT);
1615    else if (dx > 0)
1616       SoundPlay(SOUND_MOVE_AREA_RIGHT);
1617    else if (dy < 0)
1618       SoundPlay(SOUND_MOVE_AREA_UP);
1619    else if (dy > 0)
1620       SoundPlay(SOUND_MOVE_AREA_DOWN);
1621
1622    ActionsSuspend();
1623
1624    /* remove lots of event masks from windows.. we dont want to bother */
1625    /* handling events as a result of our playing wiht windows */
1626    DeskSwitchStart();
1627
1628    /* set the current area up in out data structs */
1629    DeskCurrentSetArea(ax, ay);
1630
1631    /* move all the windows around */
1632    lst = EwinListGetAll(&num);
1633    if (Conf.desks.slidein)
1634      {
1635         int                 wnum = 0;
1636         EObj              **wl = NULL;
1637
1638         /* create the list of windwos to move */
1639         for (i = 0; i < num; i++)
1640           {
1641              ewin = lst[i];
1642              if (EoIsSticky(ewin) || ewin->state.iconified)
1643                 continue;
1644              if (EoGetDesk(ewin) != DesksGetCurrent() && !EoIsFloating(ewin))
1645                 continue;
1646
1647              if (EoIsFloating(ewin) && Conf.movres.mode_move == 0)
1648                 continue;
1649
1650              wnum++;
1651              wl = EREALLOC(EObj *, wl, wnum);
1652              wl[wnum - 1] = &ewin->o;
1653           }
1654
1655         /* slide them */
1656         if (wl)
1657           {
1658              EobjsSlideBy(wl, wnum, -dx, -dy, Conf.desks.slidespeed);
1659              Efree(wl);
1660              EobjsRepaint();
1661           }
1662      }
1663
1664    /* move all windows to their final positions */
1665    Mode.move.check = 0;
1666    for (i = 0; i < num; i++)
1667      {
1668         ewin = lst[i];
1669         if (EwinIsTransientChild(ewin))
1670            continue;
1671         if (EoGetDesk(ewin) != DesksGetCurrent() && !EoIsFloating(ewin))
1672            continue;
1673
1674         if (EoIsSticky(ewin) ||
1675             (EoIsFloating(ewin) && Conf.movres.mode_move == 0) ||
1676             (!ewin->state.iconified && Conf.desks.slidein))
1677            EwinMove(ewin, EoGetX(ewin), EoGetY(ewin));
1678         else
1679            EwinMove(ewin, EoGetX(ewin) - dx, EoGetY(ewin) - dy);
1680      }
1681    Mode.move.check = 1;
1682
1683    if (!Conf.desks.slidein)
1684       EobjsRepaint();
1685
1686    /* set hints up for it */
1687    HintsSetDesktopViewport();
1688
1689    ActionsResume();
1690
1691    /* re-focus on a new ewin on that new desktop area */
1692    DeskSwitchDone();
1693
1694    ModulesSignal(ESIGNAL_AREA_SWITCH_DONE, DesksGetCurrent());
1695
1696    /* update which "edge flip resistance" detector windows are visible */
1697    EdgeWindowsShow();
1698 }
1699
1700 void
1701 DeskCurrentMoveAreaBy(int dx, int dy)
1702 {
1703    int                 ax, ay;
1704
1705    DeskCurrentGetArea(&ax, &ay);
1706    DeskCurrentGotoArea(ax + dx, ay + dy);
1707 }
1708
1709 /*
1710  * Actions, events
1711  */
1712 static char         sentpress = 0;
1713
1714 static void
1715 ButtonProxySendEvent(XEvent * ev)
1716 {
1717    if (Mode.button_proxy_win)
1718       EXSendEvent(Mode.button_proxy_win, SubstructureNotifyMask, ev);
1719 }
1720
1721 static void
1722 DeskDragStart(int desk)
1723 {
1724    Desk               *dsk;
1725
1726    if (!(dsk = DeskGet(desk)))
1727       return;
1728
1729    desks.drag_x0 = Mode.events.cx - EoGetX(dsk);
1730    desks.drag_y0 = Mode.events.cy - EoGetY(dsk);
1731
1732    Mode.mode = MODE_DESKDRAG;
1733 }
1734
1735 static void
1736 DeskDragEnd(Desk * dsk __UNUSED__)
1737 {
1738    Mode.mode = MODE_NONE;
1739 }
1740
1741 static void
1742 DeskDragMotion(Desk * dsk)
1743 {
1744    int                 x, y;
1745
1746    x = Mode.events.mx - desks.drag_x0;
1747    y = Mode.events.my - desks.drag_y0;
1748
1749    switch (Conf.desks.dragdir)
1750      {
1751      case 0:
1752         if (x < 0)
1753            x = 0;
1754         y = 0;
1755         break;
1756      case 1:
1757         if (x > 0)
1758            x = 0;
1759         y = 0;
1760         break;
1761      case 2:
1762         x = 0;
1763         if (y < 0)
1764            y = 0;
1765         break;
1766      case 3:
1767         x = 0;
1768         if (y > 0)
1769            y = 0;
1770         break;
1771      default:
1772         break;
1773      }
1774    DeskMove(dsk, x, y);
1775 }
1776
1777 static void
1778 DeskButtonCallback(EObj * eo, XEvent * ev, ActionClass * ac)
1779 {
1780    Desk               *dsk;
1781
1782    if (Mode.mode != MODE_DESKDRAG)
1783      {
1784         if (ac)
1785            ActionclassEvent(ac, ev, NULL);
1786         return;
1787      }
1788
1789    dsk = (Desk *) eo;
1790    switch (ev->type)
1791      {
1792      case ButtonRelease:
1793         DeskDragEnd(dsk);
1794         break;
1795      case MotionNotify:
1796         DeskDragMotion(dsk);
1797         break;
1798      }
1799 }
1800
1801 static int
1802 DeskCheckAction(Desk * dsk __UNUSED__, XEvent * ev)
1803 {
1804    ActionClass        *ac;
1805
1806    ac = ActionclassFind("DESKBINDINGS");
1807    if (!ac)
1808       return 0;
1809
1810    return ActionclassEvent(ac, ev, NULL);
1811 }
1812
1813 static void
1814 DeskEventButtonPress(Desk * dsk, XEvent * ev)
1815 {
1816    /* Don't handle desk bindings while doing stuff */
1817    if (Mode.mode)
1818       return;
1819
1820    GrabPointerRelease();
1821
1822    if (!DeskCheckAction(dsk, ev))
1823       ButtonProxySendEvent(ev);
1824 }
1825
1826 static void
1827 DeskEventButtonRelease(Desk * dsk, XEvent * ev)
1828 {
1829    /* Don't handle desk bindings while doing stuff */
1830    if (Mode.mode)
1831       return;
1832
1833    if (sentpress)
1834      {
1835         /* We never get here? */
1836         sentpress = 0;
1837         ButtonProxySendEvent(ev);
1838      }
1839
1840    DeskCheckAction(dsk, ev);
1841 }
1842
1843 static void
1844 DeskRootResize(int root, int w, int h)
1845 {
1846    if (EDebug(EDBUG_TYPE_DESKS))
1847       Eprintf("DeskRootResize %d %dx%d\n", root, w, h);
1848
1849    if (root && (VROOT != RROOT))
1850      {
1851         WinGetW(RROOT) = w;
1852         WinGetH(RROOT) = h;
1853      }
1854
1855    /* Quit if no change */
1856    if (w == WinGetW(VROOT) && h == WinGetH(VROOT))
1857       return;
1858
1859    EWindowSync(VROOT);
1860
1861    /* Quit if size is not final */
1862    if (w != WinGetW(VROOT) || h != WinGetH(VROOT))
1863       return;
1864
1865    ScreenInit();
1866    DesksResize(w, h);
1867
1868    Mode.screen.w_old = WinGetW(VROOT);
1869    Mode.screen.h_old = WinGetH(VROOT);
1870 }
1871
1872 static ActionClass *
1873 DeskGetAclass(void *data __UNUSED__)
1874 {
1875    return ActionclassFind("DESKBINDINGS");
1876 }
1877
1878 static void
1879 DeskPropertyChange(Desk * dsk, XEvent * ev)
1880 {
1881    Pixmap              pmap;
1882
1883    if (ev->xproperty.atom == E_XROOTPMAP_ID)
1884      {
1885         /* Possible race here? */
1886         pmap = HintsGetRootPixmap(EoGetWin(dsk));
1887         if (EDebug(EDBUG_TYPE_DESKS))
1888            Eprintf("DeskPropertyChange win=%#lx _XROOTPMAP_ID=%#lx\n",
1889                    ev->xany.window, pmap);
1890         if (ev->xany.window != WinGetXwin(VROOT))
1891            return;
1892         if (pmap == dsk->bg.pmap)
1893            return;
1894         if (pmap == Mode.root.ext_pmap)
1895            return;
1896         Mode.root.ext_pmap = pmap;
1897         Mode.root.ext_pmap_valid = EDrawableCheck(pmap, 0);
1898         DesksBackgroundRefresh(NULL, DESK_BG_REFRESH);
1899      }
1900    else if (ev->xproperty.atom == E_XROOTCOLOR_PIXEL)
1901      {
1902         if (EDebug(EDBUG_TYPE_DESKS))
1903            Eprintf("DeskPropertyChange win=%#lx _XROOTCOLOR_PIXEL\n",
1904                    ev->xany.window);
1905         if (ev->xany.window != WinGetXwin(VROOT))
1906            return;
1907      }
1908 }
1909
1910 static void
1911 DeskHandleEvents(Win win __UNUSED__, XEvent * ev, void *prm)
1912 {
1913    Desk               *dsk = (Desk *) prm;
1914
1915    switch (ev->type)
1916      {
1917      case ButtonPress:
1918         DeskEventButtonPress(dsk, ev);
1919         break;
1920      case ButtonRelease:
1921         DeskEventButtonRelease(dsk, ev);
1922         break;
1923
1924      case EnterNotify:
1925         FocusHandleEnter(NULL, ev);
1926         break;
1927      case LeaveNotify:
1928         FocusHandleLeave(NULL, ev);
1929         break;
1930
1931      case MotionNotify:
1932         /* Motion over desk buttons doesn't go here - We probably don't care much. */
1933         DesksSetCurrent(DesktopAt(Mode.events.mx, Mode.events.my));
1934         TooltipsSetPending(1, DeskGetAclass, dsk);
1935         break;
1936
1937      case ConfigureNotify:
1938         if (ev->xconfigure.window == WinGetXwin(VROOT))
1939            DeskRootResize(0, ev->xconfigure.width, ev->xconfigure.height);
1940         break;
1941
1942      case PropertyNotify:
1943         if (ev->xany.window == WinGetXwin(VROOT))
1944            DeskPropertyChange(dsk, ev);
1945         break;
1946
1947 #if USE_XRANDR
1948      case EX_EVENT_SCREEN_CHANGE_NOTIFY:
1949         {
1950            XRRScreenChangeNotifyEvent *rrev = (XRRScreenChangeNotifyEvent *) ev;
1951
1952            DeskRootResize(1, rrev->width, rrev->height);
1953         }
1954         break;
1955 #endif
1956      }
1957 }
1958
1959 /* Settings */
1960
1961 static void
1962 DeskDragdirSet(const char *params)
1963 {
1964    Desk               *dsk;
1965    unsigned int        i;
1966    int                 pd;
1967
1968    pd = Conf.desks.dragdir;
1969
1970    if (params && params[0])
1971       Conf.desks.dragdir = atoi(params);
1972    else
1973      {
1974         Conf.desks.dragdir++;
1975         if (Conf.desks.dragdir > 3)
1976            Conf.desks.dragdir = 0;
1977      }
1978
1979    if (pd == Conf.desks.dragdir)
1980       return;
1981
1982    for (i = 1; i < Conf.desks.num; i++)
1983      {
1984         dsk = _DeskGet(i);
1985         EoMove(dsk, (dsk->viewable) ? 0 : WinGetW(VROOT), 0);
1986      }
1987    DesksControlsRefresh();
1988 }
1989
1990 static void
1991 DeskDragbarOrderSet(const char *params)
1992 {
1993    int                 pd;
1994
1995    pd = Conf.desks.dragbar_ordering;
1996
1997    if (params && params[0])
1998       Conf.desks.dragbar_ordering = atoi(params);
1999    else
2000      {
2001         Conf.desks.dragbar_ordering++;
2002         if (Conf.desks.dragbar_ordering > 5)
2003            Conf.desks.dragbar_ordering = 0;
2004      }
2005
2006    if (pd == Conf.desks.dragbar_ordering)
2007       return;
2008
2009    DesksControlsRefresh();
2010 }
2011
2012 #if 0                           /* FIXME */
2013
2014 static int
2015 doDragbarWidthSet(EWin * edummy, const char *params)
2016 {
2017    int                 pd;
2018    Button             *b;
2019
2020    pd = Conf.desks.dragbar_width;
2021    if (params)
2022       Conf.desks.dragbar_width = atoi(params);
2023
2024    if (pd != Conf.desks.dragbar_width)
2025      {
2026         DesksControlsRefresh();
2027      }
2028    return 0;
2029 }
2030
2031 static int
2032 doDragbarLengthSet(EWin * edummy, const char *params)
2033 {
2034    int                 pd;
2035    Button             *b;
2036
2037    pd = Conf.desks.dragbar_length;
2038    if (params)
2039       Conf.desks.dragbar_length = atoi(params);
2040
2041    if (pd != Conf.desks.dragbar_length)
2042      {
2043         DesksControlsRefresh();
2044      }
2045    return 0;
2046 }
2047 #endif
2048
2049 #if ENABLE_DESKRAY
2050 static int
2051 doDeskray(EWin * edummy, const char *params)
2052 {
2053    if (params)
2054      {
2055         if (!atoi(params))
2056           {
2057              DeskHideTabs();
2058              Conf.deskmode = MODE_NONE;
2059           }
2060         else
2061           {
2062              Conf.deskmode = MODE_DESKRAY;
2063              DeskShowTabs();
2064           }
2065      }
2066    else
2067      {
2068         if (Conf.deskmode == MODE_DESKRAY)
2069           {
2070              DeskHideTabs();
2071              Conf.deskmode = MODE_NONE;
2072           }
2073         else
2074           {
2075              Conf.deskmode = MODE_DESKRAY;
2076              DeskShowTabs();
2077           }
2078      }
2079    return 0;
2080 }
2081 #endif /* ENABLE_DESKRAY */
2082
2083 static void
2084 DesksInit(void)
2085 {
2086    unsigned int        i;
2087
2088    memset(&desks, 0, sizeof(desks));
2089
2090    Mode.screen.w_old = WinGetW(VROOT);
2091    Mode.screen.h_old = WinGetH(VROOT);
2092
2093    /* Backward compatibility hack */
2094    if (Conf.desks.edge_flip_resistance <= 0)
2095       Conf.desks.edge_flip_mode = EDGE_FLIP_OFF;
2096
2097    desks.previous = NULL;
2098
2099    for (i = 0; i < Conf.desks.num; i++)
2100       DeskCreate(i, 0);
2101
2102    SetAreaSize(Conf.desks.areas_nx, Conf.desks.areas_ny);
2103
2104    /* Retreive stuff from last time we were loaded if we're restarting */
2105    EHintsGetDeskInfo();
2106
2107    HintsSetDesktopConfig();
2108 }
2109
2110 static void
2111 DesksConfigure(void)
2112 {
2113    unsigned int        i;
2114
2115    for (i = 0; i < Conf.desks.num; i++)
2116       DeskConfigure(_DeskGet(i));
2117
2118    UncoverDesktop(0);
2119 }
2120
2121 /*
2122  * Desktops Module
2123  */
2124
2125 static void
2126 DesksSighan(int sig, void *prm __UNUSED__)
2127 {
2128    switch (sig)
2129      {
2130      case ESIGNAL_INIT:
2131         DesksInit();
2132         break;
2133
2134      case ESIGNAL_CONFIGURE:
2135         DesksConfigure();
2136         break;
2137
2138      case ESIGNAL_START:
2139         /* Draw all the buttons that belong on the desktop */
2140         DeskShowButtons();
2141         IdlerAdd(_DesksIdler, NULL);
2142         break;
2143      }
2144 }
2145
2146 #if ENABLE_DIALOGS
2147 /*
2148  * Dialogs
2149  */
2150 static int          tmp_desktops;
2151 static DItem       *tmp_desk_text;
2152 static char         tmp_desktop_slide;
2153 static int          tmp_desktop_slide_speed;
2154 static char         tmp_desktop_wraparound;
2155 static char         tmp_dragbar;
2156 static int          tmp_dragdir;
2157
2158 static void
2159 CB_ConfigureDesktops(Dialog * d __UNUSED__, int val, void *data __UNUSED__)
2160 {
2161    if (val >= 2)
2162       return;
2163
2164    ChangeNumberOfDesktops(tmp_desktops);
2165    Conf.desks.slidein = tmp_desktop_slide;
2166    Conf.desks.slidespeed = tmp_desktop_slide_speed;
2167    Conf.desks.desks_wraparound = tmp_desktop_wraparound;
2168
2169    if ((Conf.desks.dragdir != tmp_dragdir) ||
2170        ((tmp_dragbar) && (Conf.desks.dragbar_width < 1)) ||
2171        ((!tmp_dragbar) && (Conf.desks.dragbar_width > 0)))
2172      {
2173         if (tmp_dragbar)
2174            Conf.desks.dragbar_width = 16;
2175         else
2176            Conf.desks.dragbar_width = 0;
2177         Conf.desks.dragdir = tmp_dragdir;
2178         DesksControlsRefresh();
2179      }
2180 }
2181
2182 static void
2183 CB_DesktopDisplayRedraw(Dialog * d, int val, void *data)
2184 {
2185    static char         called = 0;
2186    static int          prev_desktops = -1;
2187    static Win          wins[ENLIGHTENMENT_CONF_NUM_DESKTOPS];
2188    DItem              *di;
2189    int                 i;
2190    int                 w, h;
2191    Win                 win;
2192    char                s[64];
2193    ImageClass         *ic;
2194
2195    if (val == 1)
2196       called = 0;
2197
2198    if ((val != 1) && (prev_desktops == tmp_desktops))
2199       return;
2200
2201    prev_desktops = tmp_desktops;
2202    di = (DItem *) data;
2203    win = DialogItemAreaGetWindow(di);
2204    DialogItemAreaGetSize(di, &w, &h);
2205
2206    if (!called)
2207      {
2208         ic = ImageclassFind("SETTINGS_DESKTOP_AREA", 1);
2209         ImageclassApply(ic, win, 0, 0, STATE_NORMAL, ST_SOLID);
2210         for (i = 0; i < ENLIGHTENMENT_CONF_NUM_DESKTOPS; i++)
2211            wins[i] = 0;
2212         called = 1;
2213      }
2214
2215    for (i = 0; i < tmp_desktops; i++)
2216      {
2217         if (!wins[i])
2218           {
2219              Background         *bg;
2220
2221              wins[i] = ECreateWindow(win, 0, 0, 64, 48, 0);
2222              ESetWindowBorderWidth(wins[i], 1);
2223
2224              bg = DeskBackgroundGet(DeskGet(i));
2225              if (bg)
2226                {
2227                   Pixmap              pmap;
2228
2229                   pmap = EGetWindowBackgroundPixmap(wins[i]);
2230                   BackgroundApplyPmap(bg, wins[i], pmap, 64, 48);
2231                }
2232              else
2233                {
2234                   ic = ImageclassFind("SETTINGS_DESKTOP_AREA", 1);
2235                   ImageclassApply(ic, wins[i], 0, 0, STATE_NORMAL, ST_SOLID);
2236                }
2237           }
2238      }
2239
2240    for (i = tmp_desktops - 1; i >= 0; i--)
2241      {
2242         int                 num;
2243
2244         num = tmp_desktops - 1;
2245         if (num < 1)
2246            num = 1;
2247         EMoveWindow(wins[i], (i * (w - 64 - 2)) / num,
2248                     (i * (h - 48 - 2)) / num);
2249         ERaiseWindow(wins[i]);
2250         EMapWindow(wins[i]);
2251      }
2252
2253    for (i = tmp_desktops; i < ENLIGHTENMENT_CONF_NUM_DESKTOPS; i++)
2254      {
2255         if (!wins[i])
2256            continue;
2257         EUnmapWindow(wins[i]);
2258      }
2259
2260    Esnprintf(s, sizeof(s), "%i", tmp_desktops);
2261    DialogItemSetText(tmp_desk_text, s);
2262    DialogDrawItems(d, tmp_desk_text, 0, 0, 99999, 99999);
2263 }
2264
2265 static void
2266 CB_DesktopDisplayAreaRedraw(DItem * di, int val __UNUSED__,
2267                             void *data __UNUSED__)
2268 {
2269    CB_DesktopDisplayRedraw(DialogItemGetDialog(di), 1, di);
2270 }
2271
2272 static void
2273 _DlgFillDesks(Dialog * d __UNUSED__, DItem * table, void *data __UNUSED__)
2274 {
2275    DItem              *di, *slider, *radio;
2276
2277    tmp_desktops = Conf.desks.num;
2278    tmp_desktop_slide = Conf.desks.slidein;
2279    tmp_desktop_slide_speed = Conf.desks.slidespeed;
2280    tmp_desktop_wraparound = Conf.desks.desks_wraparound;
2281    if (Conf.desks.dragbar_width < 1)
2282       tmp_dragbar = 0;
2283    else
2284       tmp_dragbar = 1;
2285    tmp_dragdir = Conf.desks.dragdir;
2286
2287    DialogItemTableSetOptions(table, 2, 0, 0, 0);
2288
2289    di = DialogAddItem(table, DITEM_TEXT);
2290    DialogItemSetColSpan(di, 2);
2291    DialogItemSetText(di, _("Number of virtual desktops:\n"));
2292
2293    di = tmp_desk_text = DialogAddItem(table, DITEM_TEXT);
2294    DialogItemSetColSpan(di, 2);
2295    DialogItemSetText(di, "X");
2296
2297    di = slider = DialogAddItem(table, DITEM_SLIDER);
2298    DialogItemSliderSetBounds(di, 1, 32);
2299    DialogItemSliderSetUnits(di, 1);
2300    DialogItemSliderSetJump(di, 1);
2301    DialogItemSetColSpan(di, 2);
2302    DialogItemSliderSetValPtr(di, &tmp_desktops);
2303
2304    di = DialogAddItem(table, DITEM_AREA);
2305    DialogItemSetColSpan(di, 2);
2306    DialogItemAreaSetSize(di, 128, 96);
2307    DialogItemAreaSetInitFunc(di, CB_DesktopDisplayAreaRedraw);
2308
2309    DialogItemSetCallback(slider, CB_DesktopDisplayRedraw, 0, di);
2310
2311    di = DialogAddItem(table, DITEM_SEPARATOR);
2312    DialogItemSetColSpan(di, 2);
2313
2314    di = DialogAddItem(table, DITEM_CHECKBUTTON);
2315    DialogItemSetColSpan(di, 2);
2316    DialogItemSetText(di, _("Slide desktops around when changing"));
2317    DialogItemCheckButtonSetPtr(di, &tmp_desktop_slide);
2318
2319    di = DialogAddItem(table, DITEM_TEXT);
2320    DialogItemSetColSpan(di, 2);
2321    DialogItemSetAlign(di, 1024, 512);
2322    DialogItemSetText(di, _("Desktop Slide speed:\n"));
2323
2324    di = DialogAddItem(table, DITEM_SLIDER);
2325    DialogItemSetColSpan(di, 2);
2326    DialogItemSliderSetBounds(di, 0, 20000);
2327    DialogItemSliderSetUnits(di, 500);
2328    DialogItemSliderSetJump(di, 1000);
2329    DialogItemSliderSetValPtr(di, &tmp_desktop_slide_speed);
2330
2331    di = DialogAddItem(table, DITEM_SEPARATOR);
2332    DialogItemSetColSpan(di, 2);
2333
2334    di = DialogAddItem(table, DITEM_CHECKBUTTON);
2335    DialogItemSetColSpan(di, 2);
2336    DialogItemSetText(di, _("Wrap desktops around"));
2337    DialogItemCheckButtonSetPtr(di, &tmp_desktop_wraparound);
2338
2339    di = DialogAddItem(table, DITEM_SEPARATOR);
2340    DialogItemSetColSpan(di, 2);
2341
2342    di = DialogAddItem(table, DITEM_CHECKBUTTON);
2343    DialogItemSetColSpan(di, 2);
2344    DialogItemSetText(di, _("Display desktop dragbar"));
2345    DialogItemCheckButtonSetPtr(di, &tmp_dragbar);
2346
2347    di = DialogAddItem(table, DITEM_TEXT);
2348    DialogItemSetColSpan(di, 2);
2349    DialogItemSetAlign(di, 0, 512);
2350    DialogItemSetText(di, _("Drag bar position:"));
2351
2352    radio = di = DialogAddItem(table, DITEM_RADIOBUTTON);
2353    DialogItemSetColSpan(di, 2);
2354    DialogItemSetText(di, _("Top"));
2355    DialogItemRadioButtonSetFirst(di, radio);
2356    DialogItemRadioButtonGroupSetVal(di, 2);
2357
2358    di = DialogAddItem(table, DITEM_RADIOBUTTON);
2359    DialogItemSetColSpan(di, 2);
2360    DialogItemSetText(di, _("Bottom"));
2361    DialogItemRadioButtonSetFirst(di, radio);
2362    DialogItemRadioButtonGroupSetVal(di, 3);
2363
2364    di = DialogAddItem(table, DITEM_RADIOBUTTON);
2365    DialogItemSetColSpan(di, 2);
2366    DialogItemSetText(di, _("Left"));
2367    DialogItemRadioButtonSetFirst(di, radio);
2368    DialogItemRadioButtonGroupSetVal(di, 0);
2369
2370    di = DialogAddItem(table, DITEM_RADIOBUTTON);
2371    DialogItemSetColSpan(di, 2);
2372    DialogItemSetText(di, _("Right"));
2373    DialogItemRadioButtonSetFirst(di, radio);
2374    DialogItemRadioButtonGroupSetVal(di, 1);
2375    DialogItemRadioButtonGroupSetValPtr(radio, &tmp_dragdir);
2376 }
2377
2378 const DialogDef     DlgDesks = {
2379    "CONFIGURE_DESKTOPS",
2380    N_("Desks"),
2381    N_("Multiple Desktop Settings"),
2382    SOUND_SETTINGS_DESKTOPS,
2383    "pix/desktops.png",
2384    N_("Enlightenment Multiple Desktop\n" "Settings Dialog\n"),
2385    _DlgFillDesks,
2386    DLG_OAC, CB_ConfigureDesktops,
2387 };
2388
2389 static int          tmp_area_x;
2390 static int          tmp_area_y;
2391 static int          tmp_edge_flip;
2392 static int          tmp_edge_resist;
2393 static DItem       *tmp_area_text;
2394 static char         tmp_area_wraparound;
2395
2396 static void
2397 CB_ConfigureAreas(Dialog * d __UNUSED__, int val, void *data __UNUSED__)
2398 {
2399    if (val >= 2)
2400       return;
2401
2402    SetNewAreaSize(tmp_area_x, tmp_area_y);
2403    Conf.desks.areas_wraparound = tmp_area_wraparound;
2404    Conf.desks.edge_flip_mode = tmp_edge_flip;
2405    if (tmp_edge_resist < 1)
2406       tmp_edge_resist = 1;
2407    Conf.desks.edge_flip_resistance = tmp_edge_resist;
2408 }
2409
2410 static void
2411 CB_AreaDisplayRedraw(Dialog * d __UNUSED__, int val, void *data)
2412 {
2413    static int          prev_ax = 0, prev_ay = 0;
2414    static Win          awin;
2415    char                s[64];
2416    DItem              *di;
2417    Win                 win;
2418    int                 w, h, ww, hh;
2419
2420    if ((val != 1) && (prev_ax == tmp_area_x) && (prev_ay == tmp_area_y))
2421       return;
2422
2423    prev_ax = tmp_area_x;
2424    prev_ay = tmp_area_y;
2425
2426    di = (DItem *) data;
2427    win = DialogItemAreaGetWindow(di);
2428    DialogItemAreaGetSize(di, &w, &h);
2429
2430    if (val == 1)
2431      {
2432         ImageClass         *ic;
2433         Pixmap              pmap;
2434
2435         ic = ImageclassFind("SETTINGS_AREA_AREA", 1);
2436         ImageclassApply(ic, win, 0, 0, STATE_NORMAL, ST_SOLID);
2437
2438         /* Note: awin is destroyed when the dialog is destroyed */
2439         awin = ECreateWindow(win, 0, 0, 18, 14, 0);
2440         ic = ImageclassFind("SETTINGS_AREADESK_AREA", 1);
2441         pmap = EGetWindowBackgroundPixmap(awin);
2442         ImageclassApplySimple(ic, awin, pmap, STATE_NORMAL, 0, 0, 18, 14);
2443      }
2444    ww = 18 * prev_ax;
2445    hh = 14 * prev_ay;
2446    EMoveResizeWindow(awin, (w - ww) / 2, (h - hh) / 2, ww, hh);
2447    EMapWindow(awin);
2448
2449    Esnprintf(s, sizeof(s), "%i x %i", prev_ax, prev_ay);
2450    DialogItemSetText(tmp_area_text, s);
2451    DialogDrawItems(d, tmp_area_text, 0, 0, 99999, 99999);
2452 }
2453
2454 static void
2455 CB_AreaDisplayAreaRedraw(DItem * di, int val __UNUSED__, void *data __UNUSED__)
2456 {
2457    CB_AreaDisplayRedraw(DialogItemGetDialog(di), 1, di);
2458 }
2459
2460 static void
2461 _DlgFillAreas(Dialog * d __UNUSED__, DItem * table, void *data __UNUSED__)
2462 {
2463    DItem              *di, *slider, *slider2, *table2, *radio;
2464
2465    tmp_area_wraparound = Conf.desks.areas_wraparound;
2466
2467    tmp_edge_flip = Conf.desks.edge_flip_mode;
2468    tmp_edge_resist = Conf.desks.edge_flip_resistance;
2469
2470    DesksGetAreaSize(&tmp_area_x, &tmp_area_y);
2471
2472    DialogItemTableSetOptions(table, 1, 0, 0, 0);
2473
2474    di = DialogAddItem(table, DITEM_TEXT);
2475    DialogItemSetText(di, _("Virtual Desktop size:\n"));
2476
2477    di = tmp_area_text = DialogAddItem(table, DITEM_TEXT);
2478    DialogItemSetText(di, "X");
2479
2480    table2 = DialogAddItem(table, DITEM_TABLE);
2481    DialogItemTableSetOptions(table2, 2, 0, 0, 0);
2482
2483    di = DialogAddItem(table2, DITEM_NONE);
2484
2485    di = slider = DialogAddItem(table2, DITEM_SLIDER);
2486    DialogItemSliderSetMinLength(di, 10);
2487    DialogItemSliderSetBounds(di, 1, 8);
2488    DialogItemSliderSetUnits(di, 1);
2489    DialogItemSliderSetJump(di, 1);
2490    DialogItemSliderSetValPtr(di, &tmp_area_x);
2491
2492    di = slider2 = DialogAddItem(table2, DITEM_SLIDER);
2493    DialogItemSliderSetMinLength(di, 10);
2494    DialogItemSliderSetOrientation(di, 0);
2495    DialogItemSetFill(di, 0, 1);
2496    DialogItemSliderSetBounds(di, 1, 8);
2497    DialogItemSliderSetUnits(di, 1);
2498    DialogItemSliderSetJump(di, 1);
2499    DialogItemSliderSetValPtr(di, &tmp_area_y);
2500
2501    di = DialogAddItem(table2, DITEM_AREA);
2502    DialogItemAreaSetSize(di, 160, 120);
2503    DialogItemAreaSetInitFunc(di, CB_AreaDisplayAreaRedraw);
2504
2505    DialogItemSetCallback(slider, CB_AreaDisplayRedraw, 0, di);
2506    DialogItemSetCallback(slider2, CB_AreaDisplayRedraw, 0, di);
2507
2508    di = DialogAddItem(table, DITEM_SEPARATOR);
2509
2510    di = DialogAddItem(table, DITEM_CHECKBUTTON);
2511    DialogItemSetText(di, _("Wrap virtual desktops around"));
2512    DialogItemCheckButtonSetPtr(di, &tmp_area_wraparound);
2513
2514    di = DialogAddItem(table, DITEM_SEPARATOR);
2515
2516    di = DialogAddItem(table, DITEM_TEXT);
2517    DialogItemSetAlign(di, 0, 512);
2518    DialogItemSetText(di, _("Edge Flip Mode:"));
2519
2520    radio = di = DialogAddItem(table, DITEM_RADIOBUTTON);
2521    DialogItemSetText(di, _("Off"));
2522    DialogItemRadioButtonSetFirst(di, radio);
2523    DialogItemRadioButtonGroupSetVal(di, EDGE_FLIP_OFF);
2524
2525    di = DialogAddItem(table, DITEM_RADIOBUTTON);
2526    DialogItemSetText(di, _("On"));
2527    DialogItemRadioButtonSetFirst(di, radio);
2528    DialogItemRadioButtonGroupSetVal(di, EDGE_FLIP_ON);
2529
2530    di = DialogAddItem(table, DITEM_RADIOBUTTON);
2531    DialogItemSetText(di, _("Only when moving window"));
2532    DialogItemRadioButtonSetFirst(di, radio);
2533    DialogItemRadioButtonGroupSetVal(di, EDGE_FLIP_MOVE);
2534    DialogItemRadioButtonGroupSetValPtr(radio, &tmp_edge_flip);
2535
2536    di = DialogAddItem(table, DITEM_TEXT);
2537    DialogItemSetText(di, _("Resistance at edge of screen:\n"));
2538
2539    di = DialogAddItem(table, DITEM_SLIDER);
2540    DialogItemSliderSetMinLength(di, 10);
2541    DialogItemSliderSetBounds(di, 1, 100);
2542    DialogItemSliderSetUnits(di, 1);
2543    DialogItemSliderSetJump(di, 10);
2544    DialogItemSliderSetValPtr(di, &tmp_edge_resist);
2545 }
2546
2547 const DialogDef     DlgAreas = {
2548    "CONFIGURE_AREA",
2549    N_("Areas"),
2550    N_("Virtual Desktop Settings"),
2551    SOUND_SETTINGS_AREA,
2552    "pix/areas.png",
2553    N_("Enlightenment Virtual Desktop\n" "Settings Dialog\n"),
2554    _DlgFillAreas,
2555    DLG_OAC, CB_ConfigureAreas,
2556 };
2557 #endif /* ENABLE_DIALOGS */
2558
2559 /*
2560  * IPC functions
2561  */
2562
2563 static void
2564 DeskOpGoto(unsigned int desk)
2565 {
2566    Desk               *dsk;
2567    Desk               *pd = DesksGetCurrent();
2568
2569    if (desk >= Conf.desks.num)
2570       return;
2571
2572    dsk = _DeskGet(desk);
2573
2574    DeskGoto(dsk);
2575
2576    if (DesksGetCurrent() != pd)
2577       SoundPlay(SOUND_DESKTOP_SHUT);
2578 }
2579
2580 static void
2581 DeskOpGotoRel(int drel)
2582 {
2583    int                 desk;
2584
2585    desk = (int)DesksGetCurrentNum() + drel;
2586    if (Conf.desks.desks_wraparound)
2587       desk = (desk + Conf.desks.num) % Conf.desks.num;
2588
2589    DeskOpGoto((unsigned int)desk);
2590 }
2591
2592 static void
2593 DeskOpDrag(int desk)
2594 {
2595    DeskDragStart(desk);
2596 }
2597
2598 static void
2599 DesksIpcDesk(const char *params)
2600 {
2601    const char         *p;
2602    char                cmd[128], prm[128];
2603    int                 len;
2604    unsigned int        desk;
2605
2606    cmd[0] = prm[0] = '\0';
2607    p = params;
2608    if (p)
2609      {
2610         len = 0;
2611         sscanf(p, "%100s %100s %n", cmd, prm, &len);
2612         p += len;
2613      }
2614
2615    desk = DesksGetCurrentNum();
2616
2617    if (!p || cmd[0] == '?')
2618      {
2619         IpcPrintf("Current Desktop: %d/%d\n", desk, Conf.desks.num);
2620      }
2621    else if (!strncmp(cmd, "set", 3))
2622      {
2623         sscanf(prm, "%i", &desk);
2624         ChangeNumberOfDesktops(desk);
2625      }
2626    else if (!strncmp(cmd, "list", 2))
2627      {
2628         Desk               *dsk;
2629
2630         for (desk = 0; desk < Conf.desks.num; desk++)
2631           {
2632              dsk = _DeskGet(desk);
2633              IpcPrintf
2634                 ("Desk %d: viewable=%d order=%d  x,y=%4d,%4d wxh=%4dx%4d  area x,y=%d,%d  pmap=%#lx\n",
2635                  desk, dsk->viewable, desks.order[desk],
2636                  EoGetX(dsk), EoGetY(dsk), EoGetW(dsk), EoGetH(dsk),
2637                  dsk->current_area_x, dsk->current_area_y, dsk->bg.pmap);
2638           }
2639      }
2640    else if (!strncmp(cmd, "goto", 2))
2641      {
2642         sscanf(prm, "%i", &desk);
2643         DeskOpGoto(desk);
2644      }
2645    else if (!strncmp(cmd, "next", 2))
2646      {
2647         DeskOpGotoRel(1);
2648      }
2649    else if (!strncmp(cmd, "prev", 2))
2650      {
2651         DeskOpGotoRel(-1);
2652      }
2653    else if (!strncmp(cmd, "this", 2))
2654      {
2655         DeskOpGotoRel(0);
2656      }
2657    else if (!strncmp(cmd, "raise", 2))
2658      {
2659         sscanf(prm, "%i", &desk);
2660         SoundPlay(SOUND_DESKTOP_RAISE);
2661         DeskRaise(desk);
2662      }
2663    else if (!strncmp(cmd, "lower", 2))
2664      {
2665         sscanf(prm, "%i", &desk);
2666         SoundPlay(SOUND_DESKTOP_LOWER);
2667         DeskLower(desk);
2668      }
2669    else if (!strcmp(cmd, "drag"))
2670      {
2671         if (prm[0])
2672            desk = atoi(prm);
2673         DeskOpDrag(desk);
2674      }
2675    else if (!strcmp(cmd, "clear"))
2676      {
2677         EwinsShowDesktop(!Mode.showing_desktop);
2678      }
2679    else if (!strncmp(cmd, "arrange", 3))
2680      {
2681         ArrangeEwins(prm);
2682      }
2683 }
2684
2685 static void
2686 DesksIpcArea(const char *params)
2687 {
2688    const char         *p;
2689    char                cmd[128], prm[128];
2690    int                 len;
2691    int                 ax, ay, dx, dy;
2692
2693    cmd[0] = prm[0] = '\0';
2694    p = params;
2695    if (p)
2696      {
2697         len = 0;
2698         sscanf(p, "%100s %100s %n", cmd, prm, &len);
2699         p += len;
2700      }
2701
2702    DeskCurrentGetArea(&ax, &ay);
2703
2704    if (!p || cmd[0] == '?')
2705      {
2706         IpcPrintf("Current Area: %d %d\n", ax, ay);
2707      }
2708    else if (!strncmp(cmd, "set", 3))
2709      {
2710         sscanf(params, "%*s %i %i", &ax, &ay);
2711         SetNewAreaSize(ax, ay);
2712      }
2713    else if (!strncmp(cmd, "goto", 2))
2714      {
2715         sscanf(params, "%*s %i %i", &ax, &ay);
2716         DeskCurrentGotoArea(ax, ay);
2717      }
2718    else if (!strncmp(cmd, "move", 2))
2719      {
2720         dx = dy = 0;
2721         sscanf(params, "%*s %i %i", &dx, &dy);
2722         DeskCurrentMoveAreaBy(dx, dy);
2723      }
2724    else if (!strncmp(cmd, "lgoto", 2))
2725      {
2726         sscanf(params, "%*s %i", &ax);
2727         SetCurrentLinearArea(ax);
2728      }
2729    else if (!strncmp(cmd, "lmove", 2))
2730      {
2731         dx = 0;
2732         sscanf(params, "%*s %i", &dx);
2733         MoveCurrentLinearAreaBy(dx);
2734      }
2735 }
2736
2737 static const IpcItem DesksIpcArray[] = {
2738    {
2739     DesksIpcDesk,
2740     "desk", NULL,
2741     "Desktop functions",
2742     "  desk ?               Desktop info\n"
2743     "  desk drag            Start deskdrag\n"
2744     "  desk set <nd>        Set number of desktops\n"
2745     "  desk goto <d>        Goto specified desktop\n"
2746     "  desk list            Show desk info\n"
2747     "  desk next            Goto next desktop\n"
2748     "  desk prev            Goto previous desktop\n"
2749     "  desk this            Goto this desktop\n"
2750     "  desk lower <d>       Lower desktop\n"
2751     "  desk raise <d>       Raise desktop\n"
2752     "  desk arrange         Arrange windows on desktop\"\n"
2753     "  desk clear           \"Show Desktop\"\n"}
2754    ,
2755    {
2756     DesksIpcArea,
2757     "area", NULL,
2758     "Area functions",
2759     "  area ?               Area info\n"
2760     "  area set <nx> <ny>   Set area size\n"
2761     "  area goto <ax> <ay>  Goto specified area\n"
2762     "  area move <dx> <dy>  Move relative to current area\n"
2763     "  area lgoto <al>      Goto specified linear area\n"
2764     "  area lmove <dl>      Move relative to current linear area\n"}
2765    ,
2766 };
2767 #define N_IPC_FUNCS (sizeof(DesksIpcArray)/sizeof(IpcItem))
2768
2769 static void
2770 DesksCfgFuncCount(void *item __UNUSED__, const char *value)
2771 {
2772    ChangeNumberOfDesktops(atoi(value));
2773 }
2774
2775 static void
2776 DesksCfgFuncDragdir(void *item __UNUSED__, const char *value)
2777 {
2778    DeskDragdirSet(value);
2779 }
2780
2781 static void
2782 DesksCfgFuncDragdbarOrder(void *item __UNUSED__, const char *value)
2783 {
2784    DeskDragbarOrderSet(value);
2785 }
2786
2787 static void
2788 AreasCfgFuncSizeX(void *item __UNUSED__, const char *value)
2789 {
2790    int                 ax, ay;
2791
2792    DesksGetAreaSize(&ax, &ay);
2793    SetNewAreaSize(atoi(value), ay);
2794 }
2795
2796 static void
2797 AreasCfgFuncSizeY(void *item __UNUSED__, const char *value)
2798 {
2799    int                 ax, ay;
2800
2801    DesksGetAreaSize(&ax, &ay);
2802    SetNewAreaSize(ax, atoi(value));
2803 }
2804
2805 static const CfgItem DesksCfgItems[] = {
2806    CFG_FUNC_INT(Conf.desks, num, 2, DesksCfgFuncCount),
2807    CFG_FUNC_INT(Conf.desks, dragdir, 2, DesksCfgFuncDragdir),
2808    CFG_ITEM_INT(Conf.desks, dragbar_width, 16),
2809    CFG_ITEM_INT(Conf.desks, dragbar_length, 0),
2810    CFG_FUNC_INT(Conf.desks, dragbar_ordering, 1, DesksCfgFuncDragdbarOrder),
2811    CFG_ITEM_BOOL(Conf.desks, desks_wraparound, 0),
2812    CFG_ITEM_BOOL(Conf.desks, slidein, 1),
2813    CFG_ITEM_INT(Conf.desks, slidespeed, 6000),
2814
2815    CFG_FUNC_INT(Conf.desks, areas_nx, 2, AreasCfgFuncSizeX),
2816    CFG_FUNC_INT(Conf.desks, areas_ny, 1, AreasCfgFuncSizeY),
2817    CFG_ITEM_BOOL(Conf.desks, areas_wraparound, 0),
2818
2819    CFG_ITEM_INT(Conf.desks, edge_flip_mode, EDGE_FLIP_ON),
2820    CFG_ITEM_INT(Conf.desks, edge_flip_resistance, 25),
2821 };
2822 #define N_CFG_ITEMS (sizeof(DesksCfgItems)/sizeof(CfgItem))
2823
2824 /*
2825  * Module descriptor
2826  */
2827 extern const EModule ModDesktops;
2828 const EModule       ModDesktops = {
2829    "desktops", "desk",
2830    DesksSighan,
2831    {N_IPC_FUNCS, DesksIpcArray},
2832    {N_CFG_ITEMS, DesksCfgItems}
2833 };