1/*
2 *
3Copyright 1989, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21Except as contained in this notice, the name of The Open Group shall not be
22used in advertising or otherwise to promote the sale, use or other dealings
23in this Software without prior written authorization from The Open Group.
24 * */
25
26/**********************************************************************
27 *
28 * Icon related routines
29 *
30 * 10-Apr-89 Tom LaStrange        Initial Version.
31 *
32 **********************************************************************/
33
34#include <stdio.h>
35#include "twm.h"
36#include "screen.h"
37#include "icons.h"
38#include "parse.h"
39#include "util.h"
40
41#define iconWidth(w)    (Scr->IconBorderWidth * 2 + w->icon_w_width)
42#define iconHeight(w)   (Scr->IconBorderWidth * 2 + w->icon_w_height)
43
44static void splitEntry(IconEntry *ie, int grav1, int grav2, int w, int h);
45static IconEntry *FindIconEntry(TwmWindow *tmp_win, IconRegion **irp);
46static IconEntry *prevIconEntry(IconEntry *ie, IconRegion *ir);
47static void mergeEntries(IconEntry *old, IconEntry *ie);
48
49static void
50splitEntry(IconEntry *ie, int grav1, int grav2, int w, int h)
51{
52    IconEntry *entry;
53
54    switch (grav1) {
55    case D_NORTH:
56    case D_SOUTH:
57        if (w != ie->w)
58            splitEntry(ie, grav2, grav1, w, ie->h);
59        if (h != ie->h) {
60            entry = (IconEntry *) malloc(sizeof(IconEntry));
61            entry->twm_win = NULL;
62            entry->used = 0;
63            entry->next = ie->next;
64            ie->next = entry;
65            entry->x = ie->x;
66            entry->h = (ie->h - h);
67            entry->w = ie->w;
68            ie->h = h;
69            if (grav1 == D_SOUTH) {
70                entry->y = ie->y;
71                ie->y = entry->y + entry->h;
72            }
73            else
74                entry->y = ie->y + ie->h;
75        }
76        break;
77    case D_EAST:
78    case D_WEST:
79        if (h != ie->h)
80            splitEntry(ie, grav2, grav1, ie->w, h);
81        if (w != ie->w) {
82            entry = (IconEntry *) malloc(sizeof(IconEntry));
83            entry->twm_win = NULL;
84            entry->used = 0;
85            entry->next = ie->next;
86            ie->next = entry;
87            entry->y = ie->y;
88            entry->w = (ie->w - w);
89            entry->h = ie->h;
90            ie->w = w;
91            if (grav1 == D_EAST) {
92                entry->x = ie->x;
93                ie->x = entry->x + entry->w;
94            }
95            else
96                entry->x = ie->x + ie->w;
97        }
98        break;
99    }
100}
101
102static inline int
103roundUp(int v, int multiple)
104{
105    return ((v + multiple - 1) / multiple) * multiple;
106}
107
108static void
109PlaceIcon(TwmWindow *tmp_win, int def_x, int def_y, int *final_x, int *final_y)
110{
111    IconRegion *ir;
112    IconEntry *ie;
113    int w = 0, h = 0;
114
115    ie = NULL;
116    for (ir = Scr->FirstRegion; ir; ir = ir->next) {
117        w = roundUp(iconWidth(tmp_win), ir->stepx);
118        h = roundUp(iconHeight(tmp_win), ir->stepy);
119        for (ie = ir->entries; ie; ie = ie->next) {
120            if (ie->used)
121                continue;
122            if (ie->w >= w && ie->h >= h)
123                break;
124        }
125        if (ie)
126            break;
127    }
128    if (ie) {
129        splitEntry(ie, ir->grav1, ir->grav2, w, h);
130        ie->used = 1;
131        ie->twm_win = tmp_win;
132        *final_x = ie->x + (ie->w - iconWidth(tmp_win)) / 2;
133        *final_y = ie->y + (ie->h - iconHeight(tmp_win)) / 2;
134    }
135    else {
136        *final_x = def_x;
137        *final_y = def_y;
138    }
139    return;
140}
141
142static IconEntry *
143FindIconEntry(TwmWindow *tmp_win, IconRegion **irp)
144{
145    IconRegion *ir;
146    IconEntry *ie;
147
148    for (ir = Scr->FirstRegion; ir; ir = ir->next) {
149        for (ie = ir->entries; ie; ie = ie->next)
150            if (ie->twm_win == tmp_win) {
151                if (irp)
152                    *irp = ir;
153                return ie;
154            }
155    }
156    return NULL;
157}
158
159void
160IconUp(TwmWindow *tmp_win)
161{
162    int x, y;
163    int defx, defy;
164    struct IconRegion *ir;
165    unsigned udummy = 0;
166    Window wdummy = None;
167
168    /*
169     * If the client specified a particular location, let's use it (this might
170     * want to be an option at some point).  Otherwise, try to fit within the
171     * icon region.
172     */
173    if (tmp_win->wmhints && (tmp_win->wmhints->flags & IconPositionHint))
174        return;
175
176    if (tmp_win->icon_moved) {
177        unsigned width = 0;
178        unsigned height = 0;
179
180        if (!XGetGeometry(dpy, tmp_win->icon_w, &wdummy, &defx, &defy,
181                          &width, &height, &udummy, &udummy))
182            return;
183
184        x = defx + ((int) width) / 2;
185        y = defy + ((int) height) / 2;
186
187        for (ir = Scr->FirstRegion; ir; ir = ir->next) {
188            if (x >= ir->x && x < (ir->x + ir->w) &&
189                y >= ir->y && y < (ir->y + ir->h))
190                break;
191        }
192        if (!ir)
193            return;             /* outside icon regions, leave alone */
194    }
195
196    defx = -100;
197    defy = -100;
198    PlaceIcon(tmp_win, defx, defy, &x, &y);
199    if (x != defx || y != defy) {
200        XMoveWindow(dpy, tmp_win->icon_w, x, y);
201        tmp_win->icon_moved = FALSE;    /* since we've restored it */
202    }
203}
204
205static IconEntry *
206prevIconEntry(IconEntry *ie, IconRegion *ir)
207{
208    IconEntry *ip;
209
210    if (ie == ir->entries)
211        return NULL;
212    for (ip = ir->entries; ip->next != ie; ip = ip->next);
213    return ip;
214}
215
216/**
217 * old is being freed; and is adjacent to ie.  Merge
218 * regions together
219 */
220static void
221mergeEntries(IconEntry *old, IconEntry *ie)
222{
223    if (old->y == ie->y) {
224        ie->w = old->w + ie->w;
225        if (old->x < ie->x)
226            ie->x = old->x;
227    }
228    else {
229        ie->h = old->h + ie->h;
230        if (old->y < ie->y)
231            ie->y = old->y;
232    }
233}
234
235void
236IconDown(TwmWindow *tmp_win)
237{
238    IconEntry *ie, *ip, *in;
239    IconRegion *ir;
240
241    ie = FindIconEntry(tmp_win, &ir);
242    if (ie) {
243        ie->twm_win = NULL;
244        ie->used = 0;
245        ip = prevIconEntry(ie, ir);
246        in = ie->next;
247        for (;;) {
248            if (ip && ip->used == 0 &&
249                ((ip->x == ie->x && ip->w == ie->w) ||
250                 (ip->y == ie->y && ip->h == ie->h))) {
251                ip->next = ie->next;
252                mergeEntries(ie, ip);
253                free(ie);
254                ie = ip;
255                ip = prevIconEntry(ip, ir);
256            }
257            else if (in && in->used == 0 &&
258                     ((in->x == ie->x && in->w == ie->w) ||
259                      (in->y == ie->y && in->h == ie->h))) {
260                ie->next = in->next;
261                mergeEntries(in, ie);
262                free(in);
263                in = ie->next;
264            }
265            else
266                break;
267        }
268    }
269}
270
271void
272AddIconRegion(char *geom, int grav1, int grav2, int stepx, int stepy)
273{
274    IconRegion *ir;
275    int mask;
276
277    ir = (IconRegion *) malloc(sizeof(IconRegion));
278    ir->next = NULL;
279    if (Scr->LastRegion)
280        Scr->LastRegion->next = ir;
281    Scr->LastRegion = ir;
282    if (!Scr->FirstRegion)
283        Scr->FirstRegion = ir;
284
285    ir->entries = NULL;
286    ir->grav1 = grav1;
287    ir->grav2 = grav2;
288    if (stepx <= 0)
289        stepx = 1;
290    if (stepy <= 0)
291        stepy = 1;
292    ir->stepx = stepx;
293    ir->stepy = stepy;
294    ir->x = ir->y = ir->w = ir->h = 0;
295
296    mask =
297        XParseGeometry(geom, &ir->x, &ir->y, (unsigned int *) &ir->w,
298                       (unsigned int *) &ir->h);
299
300    if (mask & XNegative)
301        ir->x += Scr->MyDisplayWidth - ir->w;
302
303    if (mask & YNegative)
304        ir->y += Scr->MyDisplayHeight - ir->h;
305    ir->entries = (IconEntry *) malloc(sizeof(IconEntry));
306    ir->entries->next = NULL;
307    ir->entries->x = ir->x;
308    ir->entries->y = ir->y;
309    ir->entries->w = ir->w;
310    ir->entries->h = ir->h;
311    ir->entries->twm_win = NULL;
312    ir->entries->used = 0;
313}
314
315void
316CreateIconWindow(TwmWindow *tmp_win, int def_x, int def_y)
317{
318    unsigned long event_mask;
319    unsigned long valuemask;    /* mask for create windows */
320    XSetWindowAttributes attributes;    /* attributes for create windows */
321    Pixmap pm = None;           /* tmp pixmap variable */
322    int final_x, final_y;
323    int dummy = 0;
324    unsigned udummy = 0;
325    Window wdummy = None;
326
327    FB(tmp_win->iconc.fore, tmp_win->iconc.back);
328
329    tmp_win->forced = FALSE;
330    tmp_win->icon_not_ours = FALSE;
331
332    /* now go through the steps to get an icon window,  if ForceIcon is
333     * set, then no matter what else is defined, the bitmap from the
334     * .twmrc file is used
335     */
336    if (Scr->ForceIcon) {
337        char *icon_name;
338        Pixmap bm;
339
340        icon_name = LookInNameList(Scr->IconNames, tmp_win->full_name);
341        if (icon_name == NULL)
342            icon_name = LookInList(Scr->IconNames, tmp_win->full_name,
343                                   &tmp_win->xclass);
344
345        bm = None;
346        if (icon_name != NULL) {
347            if ((bm =
348                 (Pixmap) (void *) LookInNameList(Scr->Icons,
349                                                  icon_name)) == None) {
350                if ((bm = GetBitmap(icon_name)) != None)
351                    AddToList(&Scr->Icons, icon_name, (char *) bm);
352            }
353        }
354
355        if (bm != None) {
356            XGetGeometry(dpy, bm, &wdummy, &dummy, &dummy,
357                         (unsigned int *) &tmp_win->icon_width,
358                         (unsigned int *) &tmp_win->icon_height, &udummy,
359                         &udummy);
360
361            pm = XCreatePixmap(dpy, Scr->Root, (unsigned) tmp_win->icon_width,
362                               (unsigned) tmp_win->icon_height,
363                               (unsigned) Scr->d_depth);
364
365            /* the copy plane works on color ! */
366            XCopyPlane(dpy, bm, pm, Scr->NormalGC,
367                       0, 0, (unsigned) tmp_win->icon_width,
368                       (unsigned) tmp_win->icon_height, 0, 0, 1);
369
370            tmp_win->forced = TRUE;
371        }
372    }
373
374    /* if the pixmap is still NULL, we didn't get one from the above code,
375     * that could mean that ForceIcon was not set, or that the window
376     * was not in the Icons list, now check the WM hints for an icon
377     */
378    if (pm == None && tmp_win->wmhints &&
379        tmp_win->wmhints->flags & IconPixmapHint) {
380
381        XGetGeometry(dpy, tmp_win->wmhints->icon_pixmap,
382                     &wdummy, &dummy, &dummy,
383                     (unsigned int *) &tmp_win->icon_width,
384                     (unsigned int *) &tmp_win->icon_height, &udummy,
385                     &udummy);
386
387        pm = XCreatePixmap(dpy, Scr->Root,
388                           (unsigned) tmp_win->icon_width,
389                           (unsigned) tmp_win->icon_height,
390                           (unsigned) Scr->d_depth);
391
392        XCopyPlane(dpy, tmp_win->wmhints->icon_pixmap, pm, Scr->NormalGC,
393                   0, 0, (unsigned) tmp_win->icon_width,
394                   (unsigned) tmp_win->icon_height, 0, 0, 1);
395    }
396
397    /* if we still haven't got an icon, let's look in the Icon list
398     * if ForceIcon is not set
399     */
400    if (pm == None && !Scr->ForceIcon) {
401        char *icon_name;
402        Pixmap bm;
403
404        icon_name = LookInNameList(Scr->IconNames, tmp_win->full_name);
405        if (icon_name == NULL)
406            icon_name = LookInList(Scr->IconNames, tmp_win->full_name,
407                                   &tmp_win->xclass);
408
409        bm = None;
410        if (icon_name != NULL) {
411            if ((bm =
412                 (Pixmap) (void *) LookInNameList(Scr->Icons,
413                                                  icon_name)) == None) {
414                if ((bm = GetBitmap(icon_name)) != None)
415                    AddToList(&Scr->Icons, icon_name, (char *) bm);
416            }
417        }
418
419        if (bm != None) {
420            XGetGeometry(dpy, bm, &wdummy, &dummy, &dummy,
421                         (unsigned int *) &tmp_win->icon_width,
422                         (unsigned int *) &tmp_win->icon_height, &udummy,
423                         &udummy);
424
425            pm = XCreatePixmap(dpy, Scr->Root, (unsigned) tmp_win->icon_width,
426                               (unsigned) tmp_win->icon_height,
427                               (unsigned) Scr->d_depth);
428
429            /* the copy plane works on color ! */
430            XCopyPlane(dpy, bm, pm, Scr->NormalGC,
431                       0, 0, (unsigned) tmp_win->icon_width,
432                       (unsigned) tmp_win->icon_height, 0, 0, 1);
433        }
434    }
435
436    /* if we still don't have an icon, assign the UnknownIcon */
437
438    if (pm == None && Scr->UnknownPm != None) {
439        tmp_win->icon_width = Scr->UnknownWidth;
440        tmp_win->icon_height = Scr->UnknownHeight;
441
442        pm = XCreatePixmap(dpy, Scr->Root, (unsigned) tmp_win->icon_width,
443                           (unsigned) tmp_win->icon_height,
444                           (unsigned) Scr->d_depth);
445
446        /* the copy plane works on color ! */
447        XCopyPlane(dpy, Scr->UnknownPm, pm, Scr->NormalGC,
448                   0, 0, (unsigned) tmp_win->icon_width,
449                   (unsigned) tmp_win->icon_height, 0, 0, 1);
450    }
451
452    if (pm == None) {
453        tmp_win->icon_height = 0;
454        tmp_win->icon_width = 0;
455        valuemask = 0;
456    }
457    else {
458        valuemask = CWBackPixmap;
459        attributes.background_pixmap = pm;
460    }
461
462    tmp_win->icon_w_width = MyFont_TextWidth(&Scr->IconFont,
463                                             tmp_win->icon_name,
464                                             (int) strlen(tmp_win->icon_name));
465
466    tmp_win->icon_w_width += 6;
467    if (tmp_win->icon_w_width < tmp_win->icon_width) {
468        tmp_win->icon_x = (tmp_win->icon_width - tmp_win->icon_w_width) / 2;
469        tmp_win->icon_x += 3;
470        tmp_win->icon_w_width = tmp_win->icon_width;
471    }
472    else {
473        tmp_win->icon_x = 3;
474    }
475    tmp_win->icon_y = tmp_win->icon_height + Scr->IconFont.height;
476    tmp_win->icon_w_height = tmp_win->icon_height + Scr->IconFont.height + 4;
477
478    event_mask = 0;
479    if (tmp_win->wmhints && tmp_win->wmhints->flags & IconWindowHint) {
480        tmp_win->icon_w = tmp_win->wmhints->icon_window;
481        if (tmp_win->forced ||
482            XGetGeometry(dpy, tmp_win->icon_w, &wdummy, &dummy, &dummy,
483                         (unsigned int *) &tmp_win->icon_w_width,
484                         (unsigned int *) &tmp_win->icon_w_height, &udummy,
485                         &udummy) == 0) {
486            tmp_win->icon_w = None;
487            tmp_win->wmhints->flags &= ~IconWindowHint;
488        }
489        else {
490            tmp_win->icon_not_ours = TRUE;
491            event_mask = EnterWindowMask | LeaveWindowMask;
492        }
493    }
494    else {
495        tmp_win->icon_w = None;
496    }
497
498    if (tmp_win->icon_w == None) {
499        tmp_win->icon_w = XCreateSimpleWindow(dpy, Scr->Root,
500                                              0, 0,
501                                              (unsigned) tmp_win->icon_w_width,
502                                              (unsigned) tmp_win->icon_w_height,
503                                              (unsigned) Scr->IconBorderWidth,
504                                              tmp_win->icon_border,
505                                              tmp_win->iconc.back);
506        event_mask = ExposureMask;
507    }
508
509    XSelectInput(dpy, tmp_win->icon_w,
510                 (long) (KeyPressMask | ButtonPressMask | ButtonReleaseMask |
511                         event_mask));
512
513    tmp_win->icon_bm_w = None;
514    if (pm != None &&
515        (!(tmp_win->wmhints && tmp_win->wmhints->flags & IconWindowHint))) {
516        int y;
517        int x;
518
519        y = 0;
520        if (tmp_win->icon_w_width == tmp_win->icon_width)
521            x = 0;
522        else
523            x = (tmp_win->icon_w_width - tmp_win->icon_width) / 2;
524
525        tmp_win->icon_bm_w = XCreateWindow(dpy, tmp_win->icon_w, x, y,
526                                           (unsigned int) tmp_win->icon_width,
527                                           (unsigned int) tmp_win->icon_height,
528                                           (unsigned int) 0, Scr->d_depth,
529                                           (unsigned int) CopyFromParent,
530                                           Scr->d_visual, valuemask,
531                                           &attributes);
532    }
533
534    /* I need to figure out where to put the icon window now, because
535     * getting here means that I am going to make the icon visible
536     */
537    if (tmp_win->wmhints && tmp_win->wmhints->flags & IconPositionHint) {
538        final_x = tmp_win->wmhints->icon_x;
539        final_y = tmp_win->wmhints->icon_y;
540    }
541    else {
542        PlaceIcon(tmp_win, def_x, def_y, &final_x, &final_y);
543    }
544
545    if (final_x > Scr->MyDisplayWidth)
546        final_x = Scr->MyDisplayWidth - tmp_win->icon_w_width -
547            (2 * Scr->IconBorderWidth);
548
549    if (final_y > Scr->MyDisplayHeight)
550        final_y = Scr->MyDisplayHeight - tmp_win->icon_height -
551            Scr->IconFont.height - 4 - (2 * Scr->IconBorderWidth);
552
553    XMoveWindow(dpy, tmp_win->icon_w, final_x, final_y);
554    tmp_win->iconified = TRUE;
555
556    XMapSubwindows(dpy, tmp_win->icon_w);
557    XSaveContext(dpy, tmp_win->icon_w, TwmContext, (XPointer) tmp_win);
558    XSaveContext(dpy, tmp_win->icon_w, ScreenContext, (XPointer) Scr);
559    XDefineCursor(dpy, tmp_win->icon_w, Scr->IconCursor);
560    if (pm)
561        XFreePixmap(dpy, pm);
562    return;
563}
564