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 "E.h"
25 #include "eimage.h"
26 #include "ewins.h"
27 #include "iclass.h"
28 #include "icons.h"
29 #include "timers.h"
30 #include "windowmatch.h"
31 #include "xwin.h"
32 
33 #define EWIN_ICON_TYPE_NONE     0
34 #define EWIN_ICON_TYPE_APP      1
35 #define EWIN_ICON_TYPE_IMG      2
36 #define EWIN_ICON_TYPE_SNAP     3
37 #define EWIN_ICON_TYPE_FB       4
38 
39 static int
NetwmIconFindBestSize(unsigned int * val,unsigned int len,int size)40 NetwmIconFindBestSize(unsigned int *val, unsigned int len, int size)
41 {
42    unsigned int        i, j, sj, sbest, sz;
43    int                 k = -1;
44 
45    sz = (unsigned int)size;
46    sbest = 0;
47    for (i = 0; i < len;)
48      {
49 	j = i;
50 	i += 2 + val[i] * val[i + 1];
51 	if (i > len)
52 	   break;
53 	/* Valid */
54 	sj = val[j];
55 	if (sj == sz)
56 	  {
57 	     k = j;
58 	     break;		/* First exact match */
59 	  }
60 	if (sj > sz)
61 	  {
62 	     if (sbest > sz && sj >= sbest)
63 		continue;
64 	  }
65 	else
66 	  {
67 	     if (sj <= sbest)
68 		continue;
69 	  }
70 	k = j;
71 	sbest = sj;
72      }
73 
74    return k;
75 }
76 
77 static void
IB_IconGetSize(int ww,int hh,int size,int scale,int * pw,int * ph)78 IB_IconGetSize(int ww, int hh, int size, int scale, int *pw, int *ph)
79 {
80    int                 w, h;
81 
82    w = h = size;
83    w *= scale;
84    h *= scale;
85 
86    if (ww > hh)
87       h = (w * hh) / ww;
88    else
89       w = (h * ww) / hh;
90    if (w < 4)
91       w = 4;
92    if (h < 4)
93       h = 4;
94    if (w > ww || h > hh)
95      {
96 	w = ww;
97 	h = hh;
98      }
99 
100    *pw = w;
101    *ph = h;
102 }
103 
104 static EImage      *
IB_SnapEWin(EWin * ewin,int size)105 IB_SnapEWin(EWin * ewin, int size)
106 {
107    /* Make snapshot of window */
108    int                 w, h, ww, hh;
109    int                 was_shaded;
110    EImage             *im;
111    EX_Drawable         draw;
112 
113    if (!EoIsShown(ewin))
114       return NULL;
115 
116    was_shaded = ewin->state.shaded;
117 
118    if (ewin->state.shaded)
119       EwinInstantUnShade(ewin);
120    EwinRaise(ewin);
121    IdlersRun();
122 
123    ww = EoGetW(ewin);
124    hh = EoGetH(ewin);
125    if (ww <= 0 || hh <= 0)
126       return NULL;
127 
128    /* Oversample for nicer snapshots */
129    IB_IconGetSize(ww, hh, size, 4, &w, &h);
130 
131    draw = EoGetPixmap(ewin);
132    if (draw != NoXID)
133      {
134 	EX_Pixmap           mask;
135 
136 	mask = EWindowGetShapePixmap(EoGetWin(ewin));
137 	im = EImageGrabDrawableScaled(EoGetWin(ewin), draw, mask, 0, 0, ww, hh,
138 				      w, h, !EServerIsGrabbed(), 0);
139 	if (mask)
140 	   EFreePixmap(mask);
141      }
142    else
143      {
144 	draw = EoGetXwin(ewin);
145 	im = EImageGrabDrawableScaled(EoGetWin(ewin), draw, NoXID, 0, 0, ww, hh,
146 				      w, h, !EServerIsGrabbed(), 1);
147      }
148    EImageSetHasAlpha(im, 1);
149 
150    if (was_shaded != ewin->state.shaded)
151       EwinInstantShade(ewin, 0);
152 
153    return im;
154 }
155 
156 static EImage      *
IB_GetAppIcon(EWin * ewin,int size)157 IB_GetAppIcon(EWin * ewin, int size)
158 {
159    /* Get the applications icon pixmap/mask */
160    int                 w, h;
161    EImage             *im;
162 
163    if (ewin->ewmh.wm_icon)
164      {
165 	int                 x;
166 
167 	x = NetwmIconFindBestSize(ewin->ewmh.wm_icon, ewin->ewmh.wm_icon_len,
168 				  size);
169 	if (x >= 0)
170 	  {
171 	     im = EImageCreateFromData(ewin->ewmh.wm_icon[x],
172 				       ewin->ewmh.wm_icon[x + 1],
173 				       ewin->ewmh.wm_icon + x + 2);
174 	     EImageSetHasAlpha(im, 1);
175 	     return im;
176 	  }
177      }
178 
179    if (!ewin->icccm.icon_pmap)
180       return NULL;
181 
182    EXGetSize(ewin->icccm.icon_pmap, &w, &h);
183    if (w <= 0 || h <= 0)
184       return NULL;
185 
186    im = EImageGrabDrawable(ewin->icccm.icon_pmap, ewin->icccm.icon_mask,
187 			   0, 0, w, h, !EServerIsGrabbed());
188    EImageSetHasAlpha(im, 1);
189 
190    return im;
191 }
192 
193 static EImage      *
IB_GetEIcon(EWin * ewin)194 IB_GetEIcon(EWin * ewin)
195 {
196    /* get the icon defined for this window in E's iconf match file */
197    const char         *file;
198    EImage             *im;
199 
200    file = WindowMatchEwinIcon(ewin);
201    if (!file)
202       return NULL;
203 
204    im = ThemeImageLoad(file);
205 
206    return im;
207 }
208 
209 static EImage      *
IB_GetFallbackIcon(EWin * ewin,int size)210 IB_GetFallbackIcon(EWin * ewin, int size)
211 {
212    int                 w, h, ww, hh;
213    ImageClass         *ic;
214    EImage             *im;
215 
216    im = ThemeImageLoad("icons/default.png");
217    if (im)
218       return im;
219 
220    ww = EoGetW(ewin);
221    hh = EoGetH(ewin);
222    if (ww <= 0)
223       ww = 1;
224    if (hh <= 0)
225       hh = 1;
226 
227    IB_IconGetSize(ww, hh, size, 1, &w, &h);
228 
229    ic = ImageclassFind("ICONBOX_HORIZONTAL", 1);
230    im = ImageclassGetImageBlended(ic, EoGetWin(ewin), w, h, 0, 0, STATE_NORMAL);
231 
232    return im;
233 }
234 
235 #define N_MODES 5
236 #define N_TYPES 3
237 static const char   ewin_icon_modes[N_MODES][N_TYPES] = {
238    {EWIN_ICON_TYPE_SNAP, EWIN_ICON_TYPE_FB, EWIN_ICON_TYPE_NONE},
239    {EWIN_ICON_TYPE_APP, EWIN_ICON_TYPE_IMG, EWIN_ICON_TYPE_SNAP},
240    {EWIN_ICON_TYPE_IMG, EWIN_ICON_TYPE_APP, EWIN_ICON_TYPE_SNAP},
241    {EWIN_ICON_TYPE_APP, EWIN_ICON_TYPE_IMG, EWIN_ICON_TYPE_FB},
242    {EWIN_ICON_TYPE_IMG, EWIN_ICON_TYPE_APP, EWIN_ICON_TYPE_FB},
243 };
244 
245 EImage             *
EwinIconImageGet(EWin * ewin,int size,int mode)246 EwinIconImageGet(EWin * ewin, int size, int mode)
247 {
248    EImage             *im = NULL;
249    int                 i, type;
250 
251    if (mode < 0 || mode >= N_MODES)
252       mode = 1;
253 
254    for (i = 0; i < N_TYPES; i++)
255      {
256 	type = ewin_icon_modes[mode][i];
257 
258 	switch (type)
259 	  {
260 	  default:
261 	     goto done;
262 
263 	  case EWIN_ICON_TYPE_SNAP:
264 	     im = IB_SnapEWin(ewin, size);
265 	     break;
266 
267 	  case EWIN_ICON_TYPE_APP:
268 	     im = IB_GetAppIcon(ewin, size);
269 	     break;
270 
271 	  case EWIN_ICON_TYPE_IMG:
272 	     im = IB_GetEIcon(ewin);
273 	     break;
274 
275 	  case EWIN_ICON_TYPE_FB:
276 	     im = IB_GetFallbackIcon(ewin, size);
277 	     break;
278 	  }
279 	if (im)
280 	   goto done;
281      }
282 
283  done:
284    return im;
285 }
286