1 /*
2  * Copyright (C) 2004-2018 Kim Woelders
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to
6  * deal in the Software without restriction, including without limitation the
7  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8  * sell copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies of the Software, its documentation and marketing & publicity
13  * materials, and acknowledgment shall be given in the documentation, materials
14  * and software packages that this Software was used.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 #include "config.h"
24 
25 #include <X11/Xlib.h>
26 
27 #include "E.h"
28 #include "animation.h"
29 #include "desktops.h"
30 #include "ecompmgr.h"
31 #include "eobj.h"
32 #include "ewins.h"		/* FIXME - Should not be here */
33 #include "hints.h"
34 #include "xprop.h"
35 #include "xwin.h"
36 
37 int
OpacityFix(int op,int op_0)38 OpacityFix(int op, int op_0)
39 {
40    if (op <= 0)
41       op = op_0;
42    else if (op > 255)
43       op = 100;
44    else if (op > 100)		/* Hack to convert old 0-255 range */
45       op = (100 * op) / 255;
46    return op;
47 }
48 
49 unsigned int
OpacityFromPercent(int opx)50 OpacityFromPercent(int opx)
51 {
52    unsigned int        op = (unsigned int)opx;
53 
54    /* op is 0-100, extend to 32 bit */
55    /* op <= 0 and op > 100 is mapped to 100 (opaque) */
56    if (op >= 100)
57       return 0xffffffff;
58    return op * 42949672;
59 }
60 
61 int
OpacityToPercent(unsigned int opacity)62 OpacityToPercent(unsigned int opacity)
63 {
64    return (int)(opacity / 42949672);
65 }
66 
67 const char         *
EobjGetNameSafe(const EObj * eo)68 EobjGetNameSafe(const EObj * eo)
69 {
70    const char         *name = NULL;
71 
72    if (!eo)
73       goto done;
74 
75    name = EobjGetName(eo);
76 
77  done:
78    return name ? name : "None";
79 }
80 
81 void
EobjSetLayer(EObj * eo,int layer)82 EobjSetLayer(EObj * eo, int layer)
83 {
84    int                 ilayer = eo->ilayer;
85 
86    eo->layer = layer;
87    /*
88     * For usual EWin's the internal layer is the "old" E-layer * 10.
89     *
90     * Internal layers:
91     *   0: Root
92     *   3: Desktop type apps
93     *   5: Below buttons
94     *  10: Lowest ewins
95     *  15: Normal buttons
96     *  20: Normal below ewins
97     *  40: Normal ewins
98     *  60: Above ewins
99     *  75: Above buttons
100     *  80: Ontop ewins
101     * 100: E-Dialogs
102     * 512-: Floating windows
103     * + 0: Virtual desktops
104     * +30: E-Menus
105     * +40: Override redirects
106     * +40: E-Tooltips
107     */
108 
109    switch (eo->type)
110      {
111      case EOBJ_TYPE_EWIN:
112 	eo->ilayer = 10 * eo->layer;
113 	if (eo->ilayer == 0)
114 	   eo->ilayer = 3;
115 	break;
116 
117      case EOBJ_TYPE_BUTTON:
118 	if (eo->layer > 0)
119 	   eo->ilayer = 75;	/* Ontop */
120 	else if (eo->layer == 0)
121 	   eo->ilayer = 15;	/* Normal */
122 	else if (eo->layer < 0)
123 	   eo->ilayer = 5;	/* Below */
124 	if (eo->layer > 0 && eo->sticky)
125 	   eo->floating = 1;
126 	break;
127 
128      default:
129 	eo->ilayer = 10 * eo->layer;
130 	break;
131      }
132 
133    if (eo->floating)
134       eo->ilayer |= 512;
135    else
136       eo->ilayer &= ~512;
137    if (eo->ghost)
138       eo->ilayer |= 1024;
139 
140    if (eo->ilayer != ilayer)
141       EobjRaise(eo);
142 }
143 
144 void
EobjSetFloating(EObj * eo,int floating)145 EobjSetFloating(EObj * eo, int floating)
146 {
147    if (floating == eo->floating)
148       return;
149 
150 #if 0
151    switch (eo->type)
152      {
153      default:
154 	break;
155      case EOBJ_TYPE_EWIN:
156 	if (floating > 1)
157 	   eo->desk = 0;
158 	break;
159      }
160 #endif
161 
162    eo->floating = floating;
163    EobjSetLayer(eo, eo->layer);
164 }
165 
166 #if 1				/* FIXME - Remove */
167 int
EobjIsShaped(const EObj * eo)168 EobjIsShaped(const EObj * eo)
169 {
170    switch (eo->type)
171      {
172      default:
173 	return 0;		/* FIXME */
174      case EOBJ_TYPE_EWIN:
175 	return ((EWin *) eo)->state.shaped;
176      }
177 }
178 #endif
179 
180 #if USE_GLX
181 #define WINTYPE(t) ((t == EOBJ_TYPE_GLX) ? WIN_TYPE_GLX : WIN_TYPE_INTERNAL)
182 #else
183 #define WINTYPE(t) WIN_TYPE_INTERNAL
184 #endif
185 
186 void
EobjInit(EObj * eo,int type,Win win,int x,int y,int w,int h,int su,const char * name)187 EobjInit(EObj * eo, int type, Win win, int x, int y, int w, int h,
188 	 int su, const char *name)
189 {
190    if (!eo->desk)
191       eo->desk = DeskGet(0);
192 
193    if (!win)
194      {
195 	if (type == EOBJ_TYPE_EVENT)
196 	  {
197 	     win = ECreateEventWindow(VROOT, x, y, w, h);
198 	     eo->inputonly = 1;
199 	  }
200 	else
201 	  {
202 	     win = ECreateObjectWindow(EoGetWin(eo->desk), x, y, w, h, su,
203 				       WINTYPE(type), NULL);
204 	  }
205      }
206    eo->type = type;
207    eo->win = win;
208    eo->shaped = -1;
209    if (!win)
210       return;
211 
212    if (type == EOBJ_TYPE_EXT)
213      {
214 	eo->icccm.wm_name = ex_icccm_title_get(WinGetXwin(win));
215 	ex_icccm_name_class_get(WinGetXwin(win),
216 				&eo->icccm.wm_res_name,
217 				&eo->icccm.wm_res_class);
218      }
219    else if (name)
220       eo->icccm.wm_name = Estrdup(name);
221    if (!eo->icccm.wm_name)
222       eo->icccm.wm_name = Estrdup("-?-");
223 
224    if (type != EOBJ_TYPE_EWIN && type != EOBJ_TYPE_EXT)
225       HintsSetWindowName(EobjGetWin(eo), eo->icccm.wm_name);
226 
227 #if USE_COMPOSITE
228    switch (type)
229      {
230      case EOBJ_TYPE_EVENT:
231      case EOBJ_TYPE_MISC_NR:
232      case EOBJ_TYPE_ROOT_BG:
233 	eo->noredir = 1;
234 	break;
235      }
236    ECompMgrWinNew(eo);
237 #endif
238    if (EobjGetXwin(eo) != WinGetXwin(VROOT))
239       EobjListStackAdd(eo, 1);
240 
241    if (EDebug(EDBUG_TYPE_EWINS))
242       Eprintf("%s: %#x %s\n", __func__, EobjGetXwin(eo), EobjGetName(eo));
243 }
244 
245 void
EobjFini(EObj * eo)246 EobjFini(EObj * eo)
247 {
248    if (EDebug(EDBUG_TYPE_EWINS))
249       Eprintf("%s: %#x %s\n", __func__, EobjGetXwin(eo), EobjGetName(eo));
250 
251    EobjListStackDel(eo);
252 
253 #if USE_COMPOSITE
254    if (!eo->external)
255       eo->gone = 1;		/* Actually not yet (but soon) */
256 
257    if (eo->cmhook)
258       ECompMgrWinDel(eo);
259 #endif
260 
261    if (eo->external)
262       EUnregisterWindow(EobjGetWin(eo));
263    else
264       EDestroyWindow(EobjGetWin(eo));
265 
266    Efree(eo->icccm.wm_name);
267    Efree(eo->icccm.wm_res_name);
268    Efree(eo->icccm.wm_res_class);
269    AnimatorsFree(eo);
270 }
271 
272 void
EobjDestroy(EObj * eo)273 EobjDestroy(EObj * eo)
274 {
275    if (EDebug(EDBUG_TYPE_EWINS))
276       Eprintf("%s: %#x %s\n", __func__, EobjGetXwin(eo), EobjGetName(eo));
277 
278    EobjFini(eo);
279 
280    Efree(eo);
281 }
282 
283 EObj               *
EobjWindowCreate(int type,int x,int y,int w,int h,int su,const char * name)284 EobjWindowCreate(int type, int x, int y, int w, int h, int su, const char *name)
285 {
286    EObj               *eo;
287 
288    eo = ECALLOC(EObj, 1);
289 
290    eo->floating = 1;
291    EobjSetLayer(eo, 20);
292    EobjInit(eo, type, EobjGetWin(eo), x, y, w, h, su, name);
293    if (!eo->win)
294      {
295 	EFREE_NULL(eo);
296      }
297 
298    return eo;
299 }
300 
301 void
EobjWindowDestroy(EObj * eo)302 EobjWindowDestroy(EObj * eo)
303 {
304 #if USE_COMPOSITE
305    if (eo->shown && !eo->inputonly)
306       EobjUnmap(eo);
307 #endif
308    EobjDestroy(eo);
309 }
310 
311 EObj               *
EobjRegisterOR(EX_Window xwin __UNUSED__,XWindowAttributes * pxwa __UNUSED__,int mapped __UNUSED__)312 EobjRegisterOR(EX_Window xwin __UNUSED__, XWindowAttributes * pxwa __UNUSED__,
313 	       int mapped __UNUSED__)
314 {
315    EObj               *eo = NULL;
316 
317 #if USE_COMPOSITE
318    XWindowAttributes   attr;
319    Win                 win;
320 
321    if (!ECompMgrIsActive())
322       return NULL;
323 
324    eo = EobjListStackFind(xwin);
325    if (eo)
326      {
327 	if (eo->external)
328 	   goto done;
329 	else
330 	   return eo;
331      }
332 
333    if (!pxwa)
334      {
335 	pxwa = &attr;
336 	if (!EXGetWindowAttributes(xwin, &attr))
337 	   return NULL;
338      }
339    if (!pxwa->override_redirect)
340       return NULL;
341 #ifndef __cplusplus
342 #define c_class class
343 #endif
344    if (pxwa->c_class != InputOutput)
345       return NULL;
346 
347    win = ERegisterWindow(xwin, pxwa);
348    if (!win)
349       return NULL;
350 
351    eo = ECALLOC(EObj, 1);
352    if (!eo)
353       return eo;
354 
355    eo->external = 1;
356    eo->fade = 1;
357    eo->shadow = 1;
358 
359    EobjInit(eo, EOBJ_TYPE_EXT, win, pxwa->x, pxwa->y, pxwa->width, pxwa->height,
360 	    0, NULL);
361 
362    eo->shaped = 0;		/* FIXME - Assume unshaped for now */
363    if (win->argb)
364       eo->shadow = 0;
365    EobjSetFloating(eo, 1);
366    EobjSetLayer(eo, 4);
367 
368  done:
369    if (mapped)
370      {
371 	eo->shown = 1;
372 	EobjListStackRaise(eo, 0);
373 	ECompMgrWinMap(eo);
374      }
375 
376 #if 0
377    Eprintf("%s: %#x depth=%d argb=%d %s\n", __func__,
378 	   EobjGetXwin(eo), win->depth, win->argb, EobjGetName(eo));
379 #endif
380 
381 #endif /* USE_COMPOSITE */
382 
383    return eo;
384 }
385 
386 void
EobjUnregister(EObj * eo)387 EobjUnregister(EObj * eo)
388 {
389 #if 0
390    Eprintf("%s: %#x type=%d: %s\n", __func__, EobjGetXwin(eo), eo->type,
391 	   EobjGetName(eo));
392 #endif
393    EobjDestroy(eo);
394 }
395 
396 void
EobjMap(EObj * eo,int raise)397 EobjMap(EObj * eo, int raise)
398 {
399    if (eo->shown)
400       return;
401    eo->shown = 1;
402 
403    if (raise)
404       EobjListStackRaise(eo, 0);
405 
406    if (eo->stacked <= 0 || raise > 1)
407       DeskRestack(eo->desk);
408 
409    if (eo->shaped < 0)
410       EobjShapeUpdate(eo, 0);
411 
412    EMapWindow(EobjGetWin(eo));
413 #if USE_COMPOSITE
414    ECompMgrWinMap(eo);
415 #endif
416 }
417 
418 void
EobjUnmap(EObj * eo)419 EobjUnmap(EObj * eo)
420 {
421    if (!eo->shown)
422       return;
423 
424 #if USE_COMPOSITE
425    if (eo->cmhook)
426       ECompMgrWinUnmap(eo);
427 #endif
428    EUnmapWindow(EobjGetWin(eo));
429    eo->shown = 0;
430 }
431 
432 void
EobjMoveResize(EObj * eo,int x,int y,int w,int h)433 EobjMoveResize(EObj * eo, int x, int y, int w, int h)
434 {
435 #if USE_COMPOSITE
436    int                 move, resize;
437 
438    move = x != EobjGetX(eo) || y != EobjGetY(eo);
439    resize = w != EobjGetW(eo) || h != EobjGetH(eo);
440 #endif
441 
442    EMoveResizeWindow(EobjGetWin(eo), x, y, w, h);
443 
444 #if USE_COMPOSITE
445    if (eo->cmhook)
446       ECompMgrWinMoveResize(eo, move, resize, 0);
447 #endif
448 }
449 
450 void
EobjMove(EObj * eo,int x,int y)451 EobjMove(EObj * eo, int x, int y)
452 {
453    EobjMoveResize(eo, x, y, EobjGetW(eo), EobjGetH(eo));
454 }
455 
456 void
EobjResize(EObj * eo,int w,int h)457 EobjResize(EObj * eo, int w, int h)
458 {
459    EobjMoveResize(eo, EobjGetX(eo), EobjGetY(eo), w, h);
460 }
461 
462 #if USE_COMPOSITE
463 void
EobjDamage(EObj * eo)464 EobjDamage(EObj * eo)
465 {
466    if (eo->cmhook)
467       ECompMgrWinDamageArea(eo, 0, 0, 0, 0);
468 }
469 #else
470 void
EobjDamage(EObj * eo __UNUSED__)471 EobjDamage(EObj * eo __UNUSED__)
472 {
473 }
474 #endif
475 
476 void
EobjReparent(EObj * eo,EObj * dst,int x,int y)477 EobjReparent(EObj * eo, EObj * dst, int x, int y)
478 {
479 #if USE_COMPOSITE
480    int                 move;
481 
482    move = x != EobjGetX(eo) || y != EobjGetY(eo);
483 #endif
484 
485    EReparentWindow(EobjGetWin(eo), EobjGetWin(dst), x, y);
486    if (dst->type == EOBJ_TYPE_DESK)
487      {
488 	Desk               *dsk = (Desk *) dst;
489 
490 	if (eo->stacked < 0)
491 	  {
492 	     eo->desk = NULL;
493 	     eo->stacked = 0;
494 	  }
495 	if (eo->desk != dsk)
496 	   DeskSetDirtyStack(dsk, eo);
497 #if USE_COMPOSITE
498 	if (eo->cmhook)
499 	   ECompMgrWinReparent(eo, dsk, move);
500 #endif
501 	eo->desk = dsk;
502      }
503    else
504      {
505 	EobjListStackDel(eo);
506 #if USE_COMPOSITE
507 	if (eo->cmhook)
508 	   ECompMgrWinDel(eo);
509 #endif
510      }
511 }
512 
513 int
EobjRaise(EObj * eo)514 EobjRaise(EObj * eo)
515 {
516 #if USE_COMPOSITE
517    int                 num;
518 
519    num = EobjListStackRaise(eo, 1);
520    if (num == 0)
521       return num;
522 
523    if (num < 0)
524       num = EobjListStackRaise(eo, 0);
525    if (eo->shown && eo->cmhook)
526       ECompMgrWinRaiseLower(eo, num);
527    if (num > 0)
528       num = EobjListStackRaise(eo, 0);
529 
530    return num;
531 #else
532    return EobjListStackRaise(eo, 0);
533 #endif
534 }
535 
536 int
EobjLower(EObj * eo)537 EobjLower(EObj * eo)
538 {
539 #if USE_COMPOSITE
540    int                 num;
541 
542    num = EobjListStackLower(eo, 1);
543    if (num == 0)
544       return num;
545 
546    if (num < 0)
547       num = EobjListStackLower(eo, 0);
548    if (eo->shown && eo->cmhook)
549       ECompMgrWinRaiseLower(eo, num);
550    if (num > 0)
551       num = EobjListStackLower(eo, 0);
552 
553    return num;
554 #else
555    return EobjListStackLower(eo, 0);
556 #endif
557 }
558 
559 void
EobjShapeUpdate(EObj * eo,int propagate)560 EobjShapeUpdate(EObj * eo, int propagate)
561 {
562 #if USE_COMPOSITE
563    int                 was_shaped = eo->shaped;
564 #endif
565 
566    if (propagate)
567       eo->shaped = EShapePropagate(EobjGetWin(eo)) != 0;
568    else
569       eo->shaped = EShapeCheck(EobjGetWin(eo)) != 0;
570 
571 #if USE_COMPOSITE
572    if (was_shaped <= 0 && eo->shaped <= 0)
573       return;
574 
575    /* Shape may still be unchanged. Well ... */
576    if (eo->shown && eo->cmhook)
577       ECompMgrWinChangeShape(eo);
578 #endif
579 }
580 
581 #if USE_COMPOSITE
582 EX_Pixmap
EobjGetPixmap(const EObj * eo)583 EobjGetPixmap(const EObj * eo)
584 {
585    return ECompMgrWinGetPixmap(eo);
586 }
587 #else
588 EX_Pixmap
EobjGetPixmap(const EObj * eo __UNUSED__)589 EobjGetPixmap(const EObj * eo __UNUSED__)
590 {
591    return NoXID;
592 }
593 #endif
594 
595 #if USE_COMPOSITE
596 /* Update now */
597 void
EobjChangeOpacityNow(EObj * eo,unsigned int opacity)598 EobjChangeOpacityNow(EObj * eo, unsigned int opacity)
599 {
600    if (eo->opacity == opacity)
601       return;
602    eo->opacity = opacity;
603    ECompMgrWinSetOpacity(eo, opacity);
604 }
605 
606 /* Change through fade, if enabled */
607 void
EobjChangeOpacity(EObj * eo,unsigned int opacity)608 EobjChangeOpacity(EObj * eo, unsigned int opacity)
609 {
610    if (eo->opacity == opacity)
611       return;
612    eo->opacity = opacity;
613    ECompMgrWinChangeOpacity(eo, opacity);
614 }
615 
616 void
EobjChangeShadow(EObj * eo,int shadow)617 EobjChangeShadow(EObj * eo, int shadow)
618 {
619    ECompMgrWinChangeShadow(eo, shadow);
620 }
621 #else
622 void
EobjChangeOpacityNow(EObj * eo __UNUSED__,unsigned int opacity __UNUSED__)623 EobjChangeOpacityNow(EObj * eo __UNUSED__, unsigned int opacity __UNUSED__)
624 {
625 }
626 
627 void
EobjChangeOpacity(EObj * eo __UNUSED__,unsigned int opacity __UNUSED__)628 EobjChangeOpacity(EObj * eo __UNUSED__, unsigned int opacity __UNUSED__)
629 {
630 }
631 
632 void
EobjChangeShadow(EObj * eo __UNUSED__,int shadow __UNUSED__)633 EobjChangeShadow(EObj * eo __UNUSED__, int shadow __UNUSED__)
634 {
635 }
636 #endif
637 
638 void
EobjsRepaint(void)639 EobjsRepaint(void)
640 {
641 #if USE_COMPOSITE
642    if (ECompMgrIsActive())
643       ECompMgrRepaint();
644    else
645 #endif
646       ESync(0);
647 }
648 
649 #if USE_COMPOSITE
650 void
EobjsOpacityUpdate(int op_or)651 EobjsOpacityUpdate(int op_or)
652 {
653    EObj               *eo, *const *lst;
654    int                 i, num;
655 
656    lst = EobjListStackGet(&num);
657    for (i = 0; i < num; i++)
658      {
659 	eo = lst[i];
660 	switch (eo->type)
661 	  {
662 	  default:
663 	     break;
664 	  case EOBJ_TYPE_EWIN:
665 	     EwinUpdateOpacity((EWin *) eo);
666 	     break;
667 	  case EOBJ_TYPE_EXT:
668 	     EobjChangeOpacity(eo, OpacityFromPercent(op_or));
669 	     break;
670 	  }
671      }
672 }
673 #endif
674