1 /*
2  * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
3  * Copyright (C) 2004-2021 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 "config.h"
25 
26 #include <X11/Xlib.h>
27 
28 #include "E.h"
29 #include "aclass.h"
30 #include "borders.h"
31 #include "buttons.h"
32 #include "cursors.h"
33 #include "desktops.h"
34 #include "emodule.h"
35 #include "eobj.h"
36 #include "ewins.h"
37 #include "grabs.h"
38 #include "list.h"
39 #include "slide.h"
40 #include "xwin.h"
41 
42 #define SLIDEOUT_EVENT_MASK \
43   (KeyPressMask | KeyReleaseMask | \
44    ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | \
45    PointerMotionMask)
46 
47 typedef struct {
48    dlist_t             list;
49    EObj                o;
50    char               *name;
51    char                direction;
52    int                 num_objs;
53    EObj              **objs;
54    unsigned int        ref_count;
55    EWin               *context_ewin;
56 } Slideout;
57 
58 #define DIR_LEFT        0
59 #define DIR_RIGHT       1
60 #define DIR_UP          2
61 #define DIR_DOWN        3
62 
63 static              LIST_HEAD(slideout_list);
64 
65 static struct {
66    Slideout           *active;
67 } Mode_slideouts = {
68    NULL
69 };
70 
71 static void         SlideoutHandleEvent(Win win, XEvent * ev, void *prm);
72 
73 static Slideout    *
SlideoutCreate(const char * name,char dir)74 SlideoutCreate(const char *name, char dir)
75 {
76    Slideout           *s;
77 
78    s = ECALLOC(Slideout, 1);
79    if (!s)
80       return NULL;
81 
82    s->name = Estrdup(name);
83    s->direction = dir;
84 
85    EoInit(s, EOBJ_TYPE_MISC, NoXID, -10, -10, 1, 1, 1, name);
86    EoSetShadow(s, 1);
87    ESelectInput(EoGetWin(s), SLIDEOUT_EVENT_MASK);
88    EventCallbackRegister(EoGetWin(s), SlideoutHandleEvent, s);
89 
90    return s;
91 }
92 
93 static void
SlideoutCalcSize(Slideout * s)94 SlideoutCalcSize(Slideout * s)
95 {
96    int                 i;
97    int                 sw, sh, bw, bh;
98 
99    sw = sh = 0;
100    for (i = 0; i < s->num_objs; i++)
101      {
102 	bw = EobjGetW(s->objs[i]);
103 	bh = EobjGetH(s->objs[i]);
104 
105 	switch (s->direction)
106 	  {
107 	  case DIR_UP:
108 	  case DIR_DOWN:
109 	     if (bw > sw)
110 		sw = bw;
111 	     sh += bh;
112 	     break;
113 	  case DIR_LEFT:
114 	  case DIR_RIGHT:
115 	     if (bh > sh)
116 		sh = bh;
117 	     sw += bw;
118 	     break;
119 	  default:
120 	     break;
121 	  }
122      }
123 
124    EoResize(s, sw, sh);
125 }
126 
127 static void
SlideoutArrange(Slideout * s,int dir)128 SlideoutArrange(Slideout * s, int dir)
129 {
130    int                 i, x, y;
131    int                 sw, sh, bw, bh;
132 
133    x = y = 0;
134    sw = EoGetW(s);
135    sh = EoGetH(s);
136 
137    for (i = 0; i < s->num_objs; i++)
138      {
139 	bw = EobjGetW(s->objs[i]);
140 	bh = EobjGetH(s->objs[i]);
141 
142 	switch (dir)
143 	  {
144 	  case DIR_UP:
145 	     y += bh;
146 	     EMoveWindow(EobjGetWin(s->objs[i]), (sw - bw) >> 1, sh - y);
147 	     break;
148 	  case DIR_DOWN:
149 	     EMoveWindow(EobjGetWin(s->objs[i]), (sw - bw) >> 1, y);
150 	     y += bh;
151 	     break;
152 	  case DIR_LEFT:
153 	     x += bw;
154 	     EMoveWindow(EobjGetWin(s->objs[i]), sw - x, (sh - bh) >> 1);
155 	     break;
156 	  case DIR_RIGHT:
157 	     EMoveWindow(EobjGetWin(s->objs[i]), x, (sh - bh) >> 1);
158 	     x += bw;
159 	     break;
160 	  default:
161 	     break;
162 	  }
163      }
164    EShapePropagate(EoGetWin(s));
165 }
166 
167 static void
SlideoutShow(Slideout * s,EWin * ewin,Win win)168 SlideoutShow(Slideout * s, EWin * ewin, Win win)
169 {
170    int                 x, y, i, xx, yy, sw, sh;
171    int                 dir;
172    XSetWindowAttributes att;
173    int                 w, h;
174    Desk               *dsk;
175 
176    /* Don't ever show more than one slideout */
177    if (Mode_slideouts.active)
178       return;
179 
180    SlideoutCalcSize(s);
181    ETranslateCoordinates(win, VROOT, 0, 0, &x, &y, NULL);
182 
183    w = WinGetW(win);
184    h = WinGetH(win);
185    sw = EoGetW(s);
186    sh = EoGetH(s);
187    xx = 0;
188    yy = 0;
189 
190    dir = s->direction;
191    switch (dir)
192      {
193      case DIR_UP:
194 	xx = x + ((w - sw) >> 1);
195 	yy = y - sh;
196 	if (yy < 0 && WinGetH(VROOT) - (y + h) > y)
197 	  {
198 	     dir = DIR_DOWN;
199 	     yy = y + h;
200 	  }
201 	break;
202      case DIR_DOWN:
203 	xx = x + ((w - sw) >> 1);
204 	yy = y + h;
205 	if (yy + sh > WinGetH(VROOT) && WinGetH(VROOT) - (y + h) < y)
206 	  {
207 	     dir = DIR_UP;
208 	     yy = y - sh;
209 	  }
210 	break;
211      case DIR_LEFT:
212 	xx = x - sw;
213 	yy = y + ((h - sh) >> 1);
214 	if (xx < 0 && WinGetW(VROOT) - (x + w) > x)
215 	  {
216 	     dir = DIR_RIGHT;
217 	     xx = x + w;
218 	  }
219 	break;
220      case DIR_RIGHT:
221 	xx = x + w;
222 	yy = y + ((h - sh) >> 1);
223 	if (xx + sw > WinGetW(VROOT) && WinGetW(VROOT) - (x + w) < x)
224 	  {
225 	     dir = DIR_LEFT;
226 	     xx = x - sw;
227 	  }
228 	break;
229      default:
230 	break;
231      }
232 
233    SlideoutArrange(s, dir);
234 
235    if (ewin)
236      {
237 	/* If the slideout is associated with an ewin,
238 	 * put it on the same virtual desktop. */
239 	dsk = EoGetDesk(ewin);
240 	if (BorderWinpartIndex(ewin, win) >= 0 &&
241 	    !EoIsFloating(ewin) /* && !ewin->sticky */ )
242 	  {
243 	     xx -= EoGetX(dsk);
244 	     yy -= EoGetY(dsk);
245 	  }
246 	EoSetLayer(s, EoGetLayer(ewin));
247      }
248    else
249      {
250 	dsk = DeskGet(0);
251 	EoSetLayer(s, 10);
252 	EoSetFloating(s, 1);
253      }
254    EoReparent(s, EoObj(dsk), xx, yy);
255 
256    switch (dir)
257      {
258      case DIR_LEFT:
259 	att.win_gravity = SouthEastGravity;
260 	EChangeWindowAttributes(EoGetWin(s), CWWinGravity, &att);
261 	att.win_gravity = NorthWestGravity;
262 	for (i = 0; i < s->num_objs; i++)
263 	   EChangeWindowAttributes(EobjGetWin(s->objs[i]), CWWinGravity, &att);
264 	EoMoveResize(s, xx, yy, 1, 1);
265 	ESync(ESYNC_SLIDEOUT);
266 	EoMap(s, 2);
267 	EobjSlideSizeTo(EoObj(s), xx + sw, yy, xx, yy, 1, sh, sw, sh,
268 			Conf.shading.speed);
269 	break;
270      case DIR_RIGHT:
271 	att.win_gravity = NorthWestGravity;
272 	EChangeWindowAttributes(EoGetWin(s), CWWinGravity, &att);
273 	att.win_gravity = SouthEastGravity;
274 	for (i = 0; i < s->num_objs; i++)
275 	   EChangeWindowAttributes(EobjGetWin(s->objs[i]), CWWinGravity, &att);
276 	EoMoveResize(s, xx, yy, 1, 1);
277 	ESync(ESYNC_SLIDEOUT);
278 	EoMap(s, 2);
279 	EobjSlideSizeTo(EoObj(s), xx, yy, xx, yy, 1, sh, sw, sh,
280 			Conf.shading.speed);
281 	break;
282      case DIR_UP:
283 	att.win_gravity = SouthEastGravity;
284 	EChangeWindowAttributes(EoGetWin(s), CWWinGravity, &att);
285 	att.win_gravity = NorthWestGravity;
286 	for (i = 0; i < s->num_objs; i++)
287 	   EChangeWindowAttributes(EobjGetWin(s->objs[i]), CWWinGravity, &att);
288 	EoMoveResize(s, xx, yy, 1, 1);
289 	ESync(ESYNC_SLIDEOUT);
290 	EoMap(s, 2);
291 	EobjSlideSizeTo(EoObj(s), xx, yy + sh, xx, yy, sw, 1, sw, sh,
292 			Conf.shading.speed);
293 	break;
294      case DIR_DOWN:
295 	att.win_gravity = NorthWestGravity;
296 	EChangeWindowAttributes(EoGetWin(s), CWWinGravity, &att);
297 	att.win_gravity = SouthEastGravity;
298 	for (i = 0; i < s->num_objs; i++)
299 	   EChangeWindowAttributes(EobjGetWin(s->objs[i]), CWWinGravity, &att);
300 	EoMoveResize(s, xx, yy, 1, 1);
301 	ESync(ESYNC_SLIDEOUT);
302 	EoMap(s, 2);
303 	EobjSlideSizeTo(EoObj(s), xx, yy, xx, yy, sw, 1, sw, sh,
304 			Conf.shading.speed);
305 	break;
306      default:
307 	break;
308      }
309    s->ref_count++;
310    s->context_ewin = ewin;
311 
312    GrabPointerSet(EoGetWin(s), ECSR_ROOT, 0);
313 
314    Mode_slideouts.active = s;
315 }
316 
317 static void
SlideoutHide(Slideout * s)318 SlideoutHide(Slideout * s)
319 {
320    if (!s)
321       return;
322 
323    GrabPointerRelease();
324    EoUnmap(s);
325    s->context_ewin = NULL;
326    s->ref_count--;
327    Mode_slideouts.active = NULL;
328 }
329 
330 static void
SlideoutButtonCallback(void * prm,XEvent * ev,ActionClass * ac)331 SlideoutButtonCallback(void *prm, XEvent * ev, ActionClass * ac)
332 {
333    Slideout           *s = (Slideout *) prm;
334    EWin               *ewin = s->context_ewin;
335 
336    if (ev->type == ButtonRelease)
337       SlideoutHide(s);
338 
339    if (ac)
340       ActionclassEvent(ac, ev, ewin);
341 }
342 
343 static void
SlideoutAddButton(Slideout * s,const char * bname)344 SlideoutAddButton(Slideout * s, const char *bname)
345 {
346    Button             *b;
347 
348    if (!s)
349       return;
350 
351    b = ButtonFind(bname);
352    if (!b)
353       return;
354 
355    s->num_objs++;
356    s->objs = EREALLOC(EObj *, s->objs, s->num_objs);
357    s->objs[s->num_objs - 1] = ButtonSwallowInto(b, EoObj(s));
358    ButtonSetCallback(b, SlideoutButtonCallback, s);
359 }
360 
361 #if 0
362 static void
363 SlideoutRemoveButton(Slideout * s, Button * b)
364 {
365    s = NULL;
366    b = NULL;
367 }
368 #endif
369 
370 static void
SlideoutHandleEvent(Win win __UNUSED__,XEvent * ev,void * prm)371 SlideoutHandleEvent(Win win __UNUSED__, XEvent * ev, void *prm)
372 {
373    Slideout           *s = (Slideout *) prm;
374 
375    switch (ev->type)
376      {
377      case KeyPress:
378      case KeyRelease:
379 	SlideoutHide(s);
380 	break;
381      case ButtonPress:
382 	break;
383      case ButtonRelease:
384 	SlideoutHide(s);
385 	break;
386      case EnterNotify:
387 	if (ev->xcrossing.mode != NotifyGrab)
388 	   GrabPointerRelease();
389 	break;
390      case LeaveNotify:
391 	if (ev->xcrossing.mode != NotifyUngrab)
392 	   GrabPointerSet(EoGetWin(s), ECSR_ROOT, 0);
393 	break;
394      }
395 }
396 
397 static void
SlideoutsHide(void)398 SlideoutsHide(void)
399 {
400    if (Mode_slideouts.active)
401       SlideoutHide(Mode_slideouts.active);
402 }
403 
404 /*
405  * Configuration load/save
406  */
407 #include "conf.h"
408 
409 int
SlideoutsConfigLoad(FILE * fs)410 SlideoutsConfigLoad(FILE * fs)
411 {
412    int                 err = 0;
413    Slideout           *slideout = 0;
414    int                 i1;
415    char                s[FILEPATH_LEN_MAX];
416    char                s2[FILEPATH_LEN_MAX];
417    char                name[FILEPATH_LEN_MAX];
418 
419    name[0] = '\0';
420 
421    while (GetLine(s, sizeof(s), fs))
422      {
423 	i1 = ConfigParseline1(s, s2, NULL, NULL);
424 	switch (i1)
425 	  {
426 	  case CONFIG_CLOSE:
427 	     if (slideout)
428 		LIST_PREPEND(Slideout, &slideout_list, slideout);
429 	     goto done;
430 	  case CONFIG_CLASSNAME:
431 	     strcpy(name, s2);
432 	     break;
433 	  case SLIDEOUT_DIRECTION:
434 	     slideout = SlideoutCreate(name, (char)atoi(s2));
435 	     break;
436 	  case CONFIG_BUTTON:
437 	     SlideoutAddButton(slideout, s2);
438 	     break;
439 	  default:
440 	     ConfigParseError("Slideout", s);
441 	     break;
442 	  }
443      }
444    err = -1;
445 
446  done:
447    return err;
448 }
449 
450 /*
451  * Slideouts Module
452  */
453 
454 static void
SlideoutsSighan(int sig,void * prm)455 SlideoutsSighan(int sig, void *prm)
456 {
457    switch (sig)
458      {
459      case ESIGNAL_AREA_SWITCH_START:
460      case ESIGNAL_DESK_SWITCH_START:
461 	SlideoutsHide();
462 	break;
463 
464      case ESIGNAL_EWIN_UNMAP:
465 	if (Mode_slideouts.active
466 	    && Mode_slideouts.active->context_ewin == (EWin *) prm)
467 	   SlideoutsHide();
468 	break;
469      }
470 }
471 
472 static int
_SlideoutMatchName(const void * data,const void * match)473 _SlideoutMatchName(const void *data, const void *match)
474 {
475    return strcmp(((const Slideout *)data)->name, (const char *)match);
476 }
477 
478 static Slideout    *
SlideoutFind(const char * name)479 SlideoutFind(const char *name)
480 {
481    return LIST_FIND(Slideout, &slideout_list, _SlideoutMatchName, name);
482 }
483 
484 static void
IPC_Slideout(const char * params)485 IPC_Slideout(const char *params)
486 {
487    Slideout           *s;
488 
489    if (!params)
490       return;
491 
492    s = SlideoutFind(params);
493    if (!s)
494       return;
495 
496    SoundPlay(SOUND_SLIDEOUT_SHOW);
497    SlideoutShow(s, GetContextEwin(), Mode.context_win);
498 }
499 
500 static const IpcItem SlideoutsIpcArray[] = {
501    {
502     IPC_Slideout, "slideout", NULL, "Show slideout", NULL},
503 };
504 
505 /*
506  * Module descriptor
507  */
508 extern const EModule ModSlideouts;
509 
510 const EModule       ModSlideouts = {
511    "slideouts", "slideout",
512    SlideoutsSighan,
513    MOD_ITEMS(SlideoutsIpcArray),
514    {0, NULL}
515 };
516