1/*****************************************************************************/
2/*
3
4Copyright 1989, 1998  The Open Group
5
6Permission to use, copy, modify, distribute, and sell this software and its
7documentation for any purpose is hereby granted without fee, provided that
8the above copyright notice appear in all copies and that both that
9copyright notice and this permission notice appear in supporting
10documentation.
11
12The above copyright notice and this permission notice shall be included in
13all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of The Open Group shall not be
23used in advertising or otherwise to promote the sale, use or other dealings
24in this Software without prior written authorization from The Open Group.
25
26*/
27/**       Copyright 1988 by Evans & Sutherland Computer Corporation,        **/
28/**                          Salt Lake City, Utah                           **/
29/**                        Cambridge, Massachusetts                         **/
30/**                                                                         **/
31/**                           All Rights Reserved                           **/
32/**                                                                         **/
33/**    Permission to use, copy, modify, and distribute this software and    **/
34/**    its documentation  for  any  purpose  and  without  fee is hereby    **/
35/**    granted, provided that the above copyright notice appear  in  all    **/
36/**    copies and that both  that  copyright  notice  and  this  permis-    **/
37/**    sion  notice appear in supporting  documentation,  and  that  the    **/
38/**    name of Evans & Sutherland not be used in advertising    **/
39/**    in publicity pertaining to distribution of the  software  without    **/
40/**    specific, written prior permission.                                  **/
41/**                                                                         **/
42/**    EVANS & SUTHERLAND DISCLAIMs ALL WARRANTIES WITH REGARD    **/
43/**    TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES  OF  MERCHANT-    **/
44/**    ABILITY  AND  FITNESS,  IN  NO  EVENT SHALL EVANS & SUTHERLAND    **/
45/**    BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL  DAM-    **/
46/**    AGES OR  ANY DAMAGES WHATSOEVER  RESULTING FROM LOSS OF USE, DATA    **/
47/**    OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER    **/
48/**    TORTIOUS ACTION, ARISING OUT OF OR IN  CONNECTION  WITH  THE  USE    **/
49/**    OR PERFORMANCE OF THIS SOFTWARE.                                     **/
50/*****************************************************************************/
51
52/***********************************************************************
53 *
54 * utility routines for twm
55 *
56 * 28-Oct-87 Thomas E. LaStrange        File created
57 *
58 ***********************************************************************/
59
60#include "twm.h"
61#include "util.h"
62#include "parse.h"
63#include "screen.h"
64#include <X11/Xos.h>
65#include <X11/Xatom.h>
66#include <stdio.h>
67#include <X11/Xmu/Drawing.h>
68#include <X11/Xmu/CharSet.h>
69
70static Pixmap CreateXLogoPixmap(unsigned int *widthp, unsigned int *heightp);
71static Pixmap CreateResizePixmap(unsigned int *widthp, unsigned int *heightp);
72static Pixmap CreateDotPixmap(unsigned int *widthp, unsigned int *heightp);
73static Pixmap CreateQuestionPixmap(unsigned int *widthp, unsigned int *heightp);
74static Pixmap CreateMenuPixmap(unsigned int *widthp, unsigned int *heightp);
75
76int HotX, HotY;
77
78/**
79 * move a window outline
80 *
81 *  \param root         window we are outlining
82 *  \param x,y          upper left coordinate
83 *  \param width,height size of the rectangle
84 *  \param bw           border width of the frame
85 *  \param th           title height
86 */
87void
88MoveOutline(Window root, int x, int y, int width, int height, int bw, int th)
89{
90    static int lastx = 0;
91    static int lasty = 0;
92    static int lastWidth = 0;
93    static int lastHeight = 0;
94    static int lastBW = 0;
95    static int lastTH = 0;
96    int xl, xr, yt, yb, xinnerl, xinnerr, yinnert, yinnerb;
97    int xthird, ythird;
98    XSegment outline[18];
99    XSegment *r;
100
101    if (x == lastx && y == lasty && width == lastWidth && height == lastHeight
102        && lastBW == bw && th == lastTH)
103        return;
104
105    r = outline;
106
107#define DRAWIT() \
108    if (lastWidth || lastHeight)                        \
109    {                                                   \
110        xl = lastx;                                     \
111        xr = lastx + lastWidth - 1;                     \
112        yt = lasty;                                     \
113        yb = lasty + lastHeight - 1;                    \
114        xinnerl = xl + lastBW;                          \
115        xinnerr = xr - lastBW;                          \
116        yinnert = yt + lastTH + lastBW;                 \
117        yinnerb = yb - lastBW;                          \
118        xthird = (xinnerr - xinnerl) / 3;               \
119        ythird = (yinnerb - yinnert) / 3;               \
120                                                        \
121        r->x1 = (short)(xl);                            \
122        r->y1 = (short)(yt);                            \
123        r->x2 = (short)(xr);                            \
124        r->y2 = (short)(yt);                            \
125        r++;                                            \
126                                                        \
127        r->x1 = (short)(xl);                            \
128        r->y1 = (short)(yb);                            \
129        r->x2 = (short)(xr);                            \
130        r->y2 = (short)(yb);                            \
131        r++;                                            \
132                                                        \
133        r->x1 = (short)(xl);                            \
134        r->y1 = (short)(yt);                            \
135        r->x2 = (short)(xl);                            \
136        r->y2 = (short)(yb);                            \
137        r++;                                            \
138                                                        \
139        r->x1 = (short)(xr);                            \
140        r->y1 = (short)(yt);                            \
141        r->x2 = (short)(xr);                            \
142        r->y2 = (short)(yb);                            \
143        r++;                                            \
144                                                        \
145        r->x1 = (short)(xinnerl + xthird);              \
146        r->y1 = (short)(yinnert);                       \
147        r->x2 = (short)(r->x1);                         \
148        r->y2 = (short)(yinnerb);                       \
149        r++;                                            \
150                                                        \
151        r->x1 = (short)(xinnerl + (2 * xthird));        \
152        r->y1 = (short)(yinnert);                       \
153        r->x2 = (short)(r->x1);                         \
154        r->y2 = (short)(yinnerb);                       \
155        r++;                                            \
156                                                        \
157        r->x1 = (short)(xinnerl);                       \
158        r->y1 = (short)(yinnert + ythird);              \
159        r->x2 = (short)(xinnerr);                       \
160        r->y2 = (short)(r->y1);                         \
161        r++;                                            \
162                                                        \
163        r->x1 = (short)(xinnerl);                       \
164        r->y1 = (short)(yinnert + (2 * ythird));        \
165        r->x2 = (short)(xinnerr);                       \
166        r->y2 = (short)(r->y1);                         \
167        r++;                                            \
168                                                        \
169        if (lastTH != 0) {                              \
170            r->x1 = (short)(xl);                        \
171            r->y1 = (short)(yt + lastTH);               \
172            r->x2 = (short)(xr);                        \
173            r->y2 = (short)(r->y1);                     \
174            r++;                                        \
175        }                                               \
176    }
177
178    /* undraw the old one, if any */
179    DRAWIT();
180
181    lastx = x;
182    lasty = y;
183    lastWidth = width;
184    lastHeight = height;
185    lastBW = bw;
186    lastTH = th;
187
188    /* draw the new one, if any */
189    DRAWIT();
190
191#undef DRAWIT
192
193    if (r != outline) {
194        XDrawSegments(dpy, root, Scr->DrawGC, outline, (int) (r - outline));
195    }
196}
197
198/**
199 * zoom in or out of an icon
200 *
201 *  \param wf window to zoom from
202 *  \param wt window to zoom to
203 */
204void
205Zoom(Window wf, Window wt)
206{
207    int fx, fy, tx, ty;         /* from, to */
208    unsigned int fw, fh, tw, th;        /* from, to */
209    long dx, dy, dw, dh;
210    long z;
211    int j;
212    unsigned udummy = 0;
213    Window wdummy = None;
214
215    if (!Scr->DoZoom || Scr->ZoomCount < 1)
216        return;
217
218    if (wf == None || wt == None)
219        return;
220
221    XGetGeometry(dpy, wf, &wdummy, &fx, &fy, &fw, &fh, &udummy, &udummy);
222    XGetGeometry(dpy, wt, &wdummy, &tx, &ty, &tw, &th, &udummy, &udummy);
223
224    dx = ((long) (tx - fx));    /* going from -> to */
225    dy = ((long) (ty - fy));    /* going from -> to */
226    dw = ((long) (tw - fw));    /* going from -> to */
227    dh = ((long) (th - fh));    /* going from -> to */
228    z = (long) (Scr->ZoomCount + 1);
229
230    for (j = 0; j < 2; j++) {
231        long i;
232
233        XDrawRectangle(dpy, Scr->Root, Scr->DrawGC, fx, fy, fw, fh);
234        for (i = 1; i < z; i++) {
235            int x = fx + (int) ((dx * i) / z);
236            int y = fy + (int) ((dy * i) / z);
237            unsigned width = (unsigned) (((long) fw) + (dw * i) / z);
238            unsigned height = (unsigned) (((long) fh) + (dh * i) / z);
239
240            XDrawRectangle(dpy, Scr->Root, Scr->DrawGC, x, y, width, height);
241        }
242        XDrawRectangle(dpy, Scr->Root, Scr->DrawGC, tx, ty, tw, th);
243    }
244}
245
246/**
247 * expand the tilde character to HOME if it is the first
248 * character of the filename
249 *
250 *      \return a pointer to the new name
251 *
252 *  \param name  the filename to expand
253 */
254char *
255ExpandFilename(const char *name)
256{
257    char *newname;
258
259    if (name[0] != '~')
260        return strdup(name);
261
262    newname = (char *) malloc((size_t) HomeLen + strlen(name) + 2);
263    if (!newname) {
264        twmWarning("unable to allocate %lu bytes to expand filename %s/%s",
265                   (unsigned long) HomeLen + (unsigned long) strlen(name) + 2,
266                   Home, &name[1]);
267    }
268    else {
269        (void) sprintf(newname, "%s/%s", Home, &name[1]);
270    }
271
272    return newname;
273}
274
275/**
276 * read in the bitmap file for the unknown icon
277 *
278 * \param name  the filename to read
279 */
280void
281GetUnknownIcon(const char *name)
282{
283    int dummy = 0;
284    unsigned udummy = 0;
285    Window wdummy = None;
286
287    if ((Scr->UnknownPm = GetBitmap(name)) != None) {
288        XGetGeometry(dpy, Scr->UnknownPm, &wdummy, &dummy, &dummy,
289                     (unsigned int *) &Scr->UnknownWidth,
290                     (unsigned int *) &Scr->UnknownHeight, &udummy, &udummy);
291    }
292}
293
294/**
295 *      FindBitmap - read in a bitmap file and return size
296 *
297 *  \return pixmap associated with bitmap
298 *
299 *  \param name          filename to read
300 *  \param[out] widthp   pointer to width of bitmap
301 *  \param[out] heightp  pointer to height of bitmap
302 */
303Pixmap
304FindBitmap(const char *name, unsigned *widthp, unsigned *heightp)
305{
306    char *bigname;
307    Pixmap pm;
308
309    if (!name)
310        return None;
311
312    /*
313     * Names of the form :name refer to hardcoded images that are scaled to
314     * look nice in title buttons.  Eventually, it would be nice to put in a
315     * menu symbol as well....
316     */
317    if (name[0] == ':') {
318        int i;
319	/* *INDENT-OFF* */
320        static struct {
321            const char *name;
322            Pixmap (*proc)(unsigned int *, unsigned int *);
323        } pmtab[] = {
324            { TBPM_DOT,         CreateDotPixmap },
325            { TBPM_ICONIFY,     CreateDotPixmap },
326            { TBPM_RESIZE,      CreateResizePixmap },
327            { TBPM_XLOGO,       CreateXLogoPixmap },
328            { TBPM_DELETE,      CreateXLogoPixmap },
329            { TBPM_MENU,        CreateMenuPixmap },
330            { TBPM_QUESTION,    CreateQuestionPixmap },
331        };
332	/* *INDENT-ON* */
333
334        for (i = 0; (size_t) i < (sizeof pmtab) / (sizeof pmtab[0]); i++) {
335            if (XmuCompareISOLatin1(pmtab[i].name, name) == 0)
336                return (*pmtab[i].proc) (widthp, heightp);
337        }
338        twmWarning("no such built-in bitmap \"%s\"", name);
339        return None;
340    }
341
342    /*
343     * Generate a full pathname if any special prefix characters (such as ~)
344     * are used.  If the bigname is different from name, bigname will need to
345     * be freed.
346     */
347    bigname = ExpandFilename(name);
348    if (!bigname)
349        return None;
350
351    /*
352     * look along bitmapFilePath resource same as toolkit clients
353     */
354    pm = XmuLocateBitmapFile(ScreenOfDisplay(dpy, Scr->screen), bigname, NULL,
355                             0, (int *) widthp, (int *) heightp, &HotX, &HotY);
356    if (pm == None && Scr->IconDirectory && bigname[0] != '/') {
357        free(bigname);
358        /*
359         * Attempt to find icon in old IconDirectory (now obsolete)
360         */
361        bigname = (char *)
362            malloc(strlen(name) + strlen(Scr->IconDirectory) + 2);
363        if (!bigname) {
364            twmWarning("unable to allocate memory for \"%s/%s\"",
365                       Scr->IconDirectory, name);
366            return None;
367        }
368        (void) sprintf(bigname, "%s/%s", Scr->IconDirectory, name);
369        if (XReadBitmapFile(dpy, Scr->Root, bigname, widthp, heightp, &pm,
370                            &HotX, &HotY) != BitmapSuccess) {
371            pm = None;
372        }
373    }
374    free(bigname);
375    if (pm == None) {
376        twmWarning("unable to find bitmap \"%s\"", name);
377    }
378
379    return pm;
380}
381
382Pixmap
383GetBitmap(const char *name)
384{
385    unsigned udummy = 0;
386
387    return FindBitmap(name, &udummy, &udummy);
388}
389
390void
391InsertRGBColormap(Atom a, XStandardColormap *maps, int nmaps, Bool replace)
392{
393    StdCmap *sc = NULL;
394
395    if (replace) {              /* locate existing entry */
396        for (sc = Scr->StdCmapInfo.head; sc; sc = sc->next) {
397            if (sc->atom == a)
398                break;
399        }
400    }
401
402    if (!sc) {                  /* no existing, allocate new */
403        sc = (StdCmap *) malloc(sizeof(StdCmap));
404        if (!sc) {
405            twmWarning("unable to allocate %lu bytes for StdCmap",
406                       (unsigned long) sizeof(StdCmap));
407            return;
408        }
409        replace = False;
410    }
411
412    if (replace) {              /* just update contents */
413        if (sc->maps)
414            XFree(sc->maps);
415        if (sc == Scr->StdCmapInfo.mru)
416            Scr->StdCmapInfo.mru = NULL;
417    }
418    else {                      /* else appending */
419        sc->next = NULL;
420        sc->atom = a;
421        if (Scr->StdCmapInfo.tail) {
422            Scr->StdCmapInfo.tail->next = sc;
423        }
424        else {
425            Scr->StdCmapInfo.head = sc;
426        }
427        Scr->StdCmapInfo.tail = sc;
428    }
429    sc->nmaps = nmaps;
430    sc->maps = maps;
431
432    return;
433}
434
435void
436RemoveRGBColormap(Atom a)
437{
438    StdCmap *sc, *prev;
439
440    prev = NULL;
441    for (sc = Scr->StdCmapInfo.head; sc; sc = sc->next) {
442        if (sc->atom == a)
443            break;
444        prev = sc;
445    }
446    if (sc) {                   /* found one */
447        if (sc->maps)
448            XFree(sc->maps);
449        if (prev)
450            prev->next = sc->next;
451        if (Scr->StdCmapInfo.head == sc)
452            Scr->StdCmapInfo.head = sc->next;
453        if (Scr->StdCmapInfo.tail == sc)
454            Scr->StdCmapInfo.tail = prev;
455        if (Scr->StdCmapInfo.mru == sc)
456            Scr->StdCmapInfo.mru = NULL;
457    }
458    return;
459}
460
461void
462LocateStandardColormaps(void)
463{
464    Atom *atoms;
465    int natoms;
466    int i;
467
468    atoms = XListProperties(dpy, Scr->Root, &natoms);
469    for (i = 0; i < natoms; i++) {
470        XStandardColormap *maps = NULL;
471        int nmaps;
472
473        if (XGetRGBColormaps(dpy, Scr->Root, &maps, &nmaps, atoms[i])) {
474            /* if got one, then append to current list */
475            InsertRGBColormap(atoms[i], maps, nmaps, False);
476        }
477    }
478    if (atoms)
479        XFree(atoms);
480    return;
481}
482
483void
484GetColor(int kind, Pixel *what, const char *name)
485{
486    XColor color, junkcolor;
487    Status stat = 0;
488    Colormap cmap = Scr->TwmRoot.cmaps.cwins[0]->colormap->c;
489
490    if (!Scr->FirstTime)
491        return;
492
493    if (Scr->Monochrome != kind)
494        return;
495
496    if (!XAllocNamedColor(dpy, cmap, name, &color, &junkcolor)) {
497        /* if we could not allocate the color, let's see if this is a
498         * standard colormap
499         */
500        XStandardColormap *stdcmap = NULL;
501
502        /* parse the named color */
503        if (name[0] != '#')
504            stat = XParseColor(dpy, cmap, name, &color);
505        if (!stat) {
506            twmWarning("invalid color name \"%s\"", name);
507            return;
508        }
509
510        /*
511         * look through the list of standard colormaps (check cache first)
512         */
513        if (Scr->StdCmapInfo.mru && Scr->StdCmapInfo.mru->maps &&
514            (Scr->StdCmapInfo.mru->maps[Scr->StdCmapInfo.mruindex].colormap ==
515             cmap)) {
516            stdcmap = &(Scr->StdCmapInfo.mru->maps[Scr->StdCmapInfo.mruindex]);
517        }
518        else {
519            StdCmap *sc;
520
521            for (sc = Scr->StdCmapInfo.head; sc; sc = sc->next) {
522                int i;
523
524                for (i = 0; i < sc->nmaps; i++) {
525                    if (sc->maps[i].colormap == cmap) {
526                        Scr->StdCmapInfo.mru = sc;
527                        Scr->StdCmapInfo.mruindex = i;
528                        stdcmap = &(sc->maps[i]);
529                        goto gotit;
530                    }
531                }
532            }
533        }
534
535 gotit:
536        if (stdcmap) {
537            color.pixel = (stdcmap->base_pixel +
538                           ((Pixel) (((float) color.red / 65535.0) *
539                                     (double) stdcmap->red_max + 0.5) *
540                            stdcmap->red_mult) +
541                           ((Pixel) (((float) color.green / 65535.0) *
542                                     (double) stdcmap->green_max + 0.5) *
543                            stdcmap->green_mult) +
544                           ((Pixel) (((float) color.blue / 65535.0) *
545                                     (double) stdcmap->blue_max + 0.5) *
546                            stdcmap->blue_mult));
547        }
548        else {
549            twmWarning("unable to allocate color \"%s\"", name);
550            return;
551        }
552    }
553
554    *what = color.pixel;
555}
556
557void
558GetColorValue(int kind, XColor *what, const char *name)
559{
560    XColor junkcolor;
561    Colormap cmap = Scr->TwmRoot.cmaps.cwins[0]->colormap->c;
562
563    if (!Scr->FirstTime)
564        return;
565
566    if (Scr->Monochrome != kind)
567        return;
568
569    if (!XLookupColor(dpy, cmap, name, what, &junkcolor)) {
570        twmWarning("invalid color name \"%s\"", name);
571    }
572    else {
573        what->pixel = AllPlanes;
574    }
575}
576
577static Boolean
578FindFontSet(MyFont *font, const char *fontname)
579{
580    char **missing_charset_list_return;
581    int missing_charset_count_return;
582    char *def_string_return;
583    XFontSetExtents *font_extents;
584    XFontStruct **xfonts;
585    char **font_names;
586
587    if (use_fontset) {
588        int ascent;
589        int descent;
590        int fnum;
591        int i;
592
593        if (font->fontset != NULL) {
594            XFreeFontSet(dpy, font->fontset);
595        }
596
597        if ((font->fontset = XCreateFontSet(dpy, fontname,
598                                            &missing_charset_list_return,
599                                            &missing_charset_count_return,
600                                            &def_string_return)) == NULL) {
601            return False;
602        }
603        if (missing_charset_count_return) {
604            twmVerbose("%d fonts are missing from fontset",
605                       missing_charset_count_return);
606            for (i = 0; i < missing_charset_count_return; i++) {
607                twmVerbose("font for charset %s is lacking.",
608                           missing_charset_list_return[i]);
609            }
610            XFreeStringList(missing_charset_list_return);
611        }
612
613        font_extents = XExtentsOfFontSet(font->fontset);
614        fnum = XFontsOfFontSet(font->fontset, &xfonts, &font_names);
615        for (i = 0, ascent = 0, descent = 0; i < fnum; i++) {
616            if (ascent < (*xfonts)->ascent)
617                ascent = (*xfonts)->ascent;
618            if (descent < (*xfonts)->descent)
619                descent = (*xfonts)->descent;
620            xfonts++;
621        }
622        font->height = font_extents->max_logical_extent.height;
623        font->y = ascent;
624        font->ascent = ascent;
625        font->descent = descent;
626        twmMessage("created fontset with %d fonts (%d missing) for \"%s\"",
627                   fnum, missing_charset_count_return,
628                   fontname ? fontname : "NULL");
629        return True;
630    }
631
632    if (font->font != NULL)
633        XFreeFont(dpy, font->font);
634
635    if ((font->font = XLoadQueryFont(dpy, fontname)) == NULL) {
636        return False;
637    }
638    font->height = font->font->ascent + font->font->descent;
639    font->y = font->font->ascent;
640    font->ascent = font->font->ascent;
641    font->descent = font->font->descent;
642    return True;
643}
644
645/*
646 * The following functions are sensible to 'use_fontset'.
647 * When 'use_fontset' is True,
648 *  - XFontSet-related internationalized functions are used
649 *     so as multibyte languages can be displayed.
650 * When 'use_fontset' is False,
651 *  - XFontStruct-related conventional functions are used
652 *     so as 8-bit characters can be displayed even when
653 *     locale is not set properly.
654 */
655void
656GetFont(MyFont *font)
657{
658
659    if (!FindFontSet(font, font->name)) {
660        const char *what = "fonts";
661        const char *deffontname = "fixed";
662
663        if (use_fontset) {
664            what = "fontsets";
665        }
666        else if (Scr->DefaultFont.name) {
667            deffontname = Scr->DefaultFont.name;
668        }
669        if (!FindFontSet(font, deffontname)) {
670            twmError("unable to open %s \"%s\" or \"%s\"",
671                     what, font->name, deffontname);
672        }
673    }
674}
675
676void
677DestroyFont(MyFont *font)
678{
679    if (!font) {
680        return;
681    }
682
683    if (font->fontset) {
684        XFreeFontSet(dpy, font->fontset);
685        font->fontset = NULL;
686    }
687
688    if (font->font) {
689        XFreeFont(dpy, font->font);
690        font->font = NULL;
691    }
692}
693
694int
695MyFont_TextWidth(MyFont *font, const char *string, int len)
696{
697    XRectangle ink_rect;
698    XRectangle logical_rect;
699
700    if (use_fontset) {
701        XmbTextExtents(font->fontset, string, len, &ink_rect, &logical_rect);
702        return logical_rect.width;
703    }
704    return XTextWidth(font->font, string, len);
705}
706
707void
708MyFont_DrawImageString(Display *dpy2, Drawable d, MyFont *font, GC gc,
709                       int x, int y, const char *string, int len)
710{
711    if (use_fontset) {
712        XmbDrawImageString(dpy2, d, font->fontset, gc, x, y, string, len);
713        return;
714    }
715    XDrawImageString(dpy2, d, gc, x, y, string, len);
716}
717
718void
719MyFont_DrawString(Display *dpy2, Drawable d, MyFont *font, GC gc,
720                  int x, int y, const char *string, int len)
721{
722    if (use_fontset) {
723        XmbDrawString(dpy2, d, font->fontset, gc, x, y, string, len);
724        return;
725    }
726    XDrawString(dpy2, d, gc, x, y, string, len);
727}
728
729void
730MyFont_ChangeGC(unsigned long fix_fore, unsigned long fix_back,
731                MyFont *fix_font)
732{
733    Gcv.foreground = fix_fore;
734    Gcv.background = fix_back;
735    if (use_fontset) {
736        XChangeGC(dpy, Scr->NormalGC, GCForeground | GCBackground, &Gcv);
737        return;
738    }
739    Gcv.font = fix_font->font->fid;
740    XChangeGC(dpy, Scr->NormalGC, GCFont | GCForeground | GCBackground, &Gcv);
741}
742
743/*
744 * The following functions are internationalized substitutions
745 * for XFetchName and XGetIconName using XGetWMName and
746 * XGetWMIconName.
747 *
748 * Please note that the third arguments have to be freed using free(),
749 * not XFree().
750 */
751Status
752I18N_FetchName(Display *dpy2, Window w, char **winname)
753{
754    int status;
755    XTextProperty text_prop;
756    int rc = 0;
757
758    *winname = NULL;
759
760    status = XGetWMName(dpy2, w, &text_prop);
761    if (status && text_prop.value && text_prop.nitems) {
762        char **list = NULL;
763        int num;
764
765        status = XmbTextPropertyToTextList(dpy2, &text_prop, &list, &num);
766        if (status >= Success && num && list && *list) {
767            XFree(text_prop.value);
768            *winname = strdup(*list);
769            XFreeStringList(list);
770            rc = 1;
771        }
772        else {
773            char *value = NULL;
774
775            /*
776             * If the system's locale support is broken (e.g., missing useful
777             * parts), the preceding Xmb call may fail.
778             */
779            if (XFetchName(dpy2, w, &value) && value != NULL) {
780                *winname = strdup(value);
781                XFree(value);
782                rc = 1;
783            }
784        }
785    }
786
787    return rc;
788}
789
790Status
791I18N_GetIconName(Display *dpy2, Window w, char **iconname)
792{
793    int status;
794    XTextProperty text_prop;
795    char **list;
796    int num;
797
798    status = XGetWMIconName(dpy2, w, &text_prop);
799    if (!status || !text_prop.value || !text_prop.nitems)
800        return 0;
801    status = XmbTextPropertyToTextList(dpy2, &text_prop, &list, &num);
802    if (status < Success || !num || !*list)
803        return 0;
804    XFree(text_prop.value);
805    *iconname = (char *) strdup(*list);
806    XFreeStringList(list);
807    return 1;
808}
809
810/**
811 * separate routine to set focus to make things more understandable
812 * and easier to debug
813 */
814void
815SetFocus(TwmWindow *tmp_win, Time time)
816{
817    Window w = (tmp_win ? tmp_win->w : PointerRoot);
818
819#ifdef TRACE
820    if (tmp_win) {
821        twmMessage("Focusing on window \"%s\"", tmp_win->full_name);
822    }
823    else {
824        twmMessage("Unfocusing; Scr->Focus was \"%s\"",
825                   Scr->Focus ? Scr->Focus->full_name : "(nil)");
826    }
827#endif
828
829    XSetInputFocus(dpy, w, RevertToPointerRoot, time);
830}
831
832static Pixmap
833CreateXLogoPixmap(unsigned *widthp, unsigned *heightp)
834{
835    int h = Scr->TBInfo.width - Scr->TBInfo.border * 2;
836
837    if (h < 0)
838        h = 0;
839
840    *widthp = *heightp = (unsigned int) h;
841    if (Scr->tbpm.xlogo == None) {
842        GC gc, gcBack;
843
844        Scr->tbpm.xlogo =
845            XCreatePixmap(dpy, Scr->Root, (unsigned) h, (unsigned) h, 1);
846        gc = XCreateGC(dpy, Scr->tbpm.xlogo, 0L, NULL);
847        XSetForeground(dpy, gc, 0);
848        XFillRectangle(dpy, Scr->tbpm.xlogo, gc, 0, 0, (unsigned) h,
849                       (unsigned) h);
850        XSetForeground(dpy, gc, 1);
851        gcBack = XCreateGC(dpy, Scr->tbpm.xlogo, 0L, NULL);
852        XSetForeground(dpy, gcBack, 0);
853
854        /*
855         * draw the logo large so that it gets as dense as possible; then white
856         * out the edges so that they look crisp
857         */
858        XmuDrawLogo(dpy, Scr->tbpm.xlogo, gc, gcBack, -1, -1,
859                    (unsigned) (h + 2), (unsigned) (h + 2));
860        XDrawRectangle(dpy, Scr->tbpm.xlogo, gcBack, 0, 0, (unsigned) (h - 1),
861                       (unsigned) (h - 1));
862
863        /*
864         * done drawing
865         */
866        XFreeGC(dpy, gc);
867        XFreeGC(dpy, gcBack);
868    }
869    return Scr->tbpm.xlogo;
870}
871
872static Pixmap
873CreateResizePixmap(unsigned *widthp, unsigned *heightp)
874{
875    int h = Scr->TBInfo.width - Scr->TBInfo.border * 2;
876
877    if (h < 1)
878        h = 1;
879
880    *widthp = *heightp = (unsigned int) h;
881    if (Scr->tbpm.resize == None) {
882        XPoint points[3];
883        GC gc;
884        int w;
885        int lw;
886
887        /*
888         * create the pixmap
889         */
890        Scr->tbpm.resize =
891            XCreatePixmap(dpy, Scr->Root, (unsigned) h, (unsigned) h, 1);
892        gc = XCreateGC(dpy, Scr->tbpm.resize, 0L, NULL);
893        XSetForeground(dpy, gc, 0);
894        XFillRectangle(dpy, Scr->tbpm.resize, gc, 0, 0, (unsigned) h,
895                       (unsigned) h);
896        XSetForeground(dpy, gc, 1);
897        lw = h / 16;
898        if (lw == 1)
899            lw = 0;
900        XSetLineAttributes(dpy, gc, (unsigned) lw, LineSolid, CapButt,
901                           JoinMiter);
902
903        /*
904         * draw the resize button,
905         */
906        w = (h * 2) / 3;
907        points[0].x = (short) w;
908        points[0].y = 0;
909        points[1].x = (short) w;
910        points[1].y = (short) w;
911        points[2].x = 0;
912        points[2].y = (short) w;
913        XDrawLines(dpy, Scr->tbpm.resize, gc, points, 3, CoordModeOrigin);
914        w = w / 2;
915        points[0].x = (short) w;
916        points[0].y = 0;
917        points[1].x = (short) w;
918        points[1].y = (short) w;
919        points[2].x = 0;
920        points[2].y = (short) w;
921        XDrawLines(dpy, Scr->tbpm.resize, gc, points, 3, CoordModeOrigin);
922
923        /*
924         * done drawing
925         */
926        XFreeGC(dpy, gc);
927    }
928    return Scr->tbpm.resize;
929}
930
931static Pixmap
932CreateDotPixmap(unsigned *widthp, unsigned *heightp)
933{
934    int h = Scr->TBInfo.width - Scr->TBInfo.border * 2;
935
936    h = h * 3 / 4;
937    if (h < 1)
938        h = 1;
939    if (!(h & 1))
940        h--;
941    *widthp = *heightp = (unsigned int) h;
942    if (Scr->tbpm.remove == None) {
943        GC gc;
944        Pixmap pix;
945
946        pix = Scr->tbpm.remove =
947            XCreatePixmap(dpy, Scr->Root, (unsigned) h, (unsigned) h, 1);
948        gc = XCreateGC(dpy, pix, 0L, NULL);
949        XSetLineAttributes(dpy, gc, (unsigned) h, LineSolid, CapRound,
950                           JoinRound);
951        XSetForeground(dpy, gc, 0L);
952        XFillRectangle(dpy, pix, gc, 0, 0, (unsigned) h, (unsigned) h);
953        XSetForeground(dpy, gc, 1L);
954        XDrawLine(dpy, pix, gc, h / 2, h / 2, h / 2, h / 2);
955        XFreeGC(dpy, gc);
956    }
957    return Scr->tbpm.remove;
958}
959
960#define questionmark_width 8
961#define questionmark_height 8
962static char questionmark_bits[] = {
963    0x38, 0x7c, 0x64, 0x30, 0x18, 0x00, 0x18, 0x18
964};
965
966static Pixmap
967CreateQuestionPixmap(unsigned *widthp, unsigned *heightp)
968{
969    *widthp = questionmark_width;
970    *heightp = questionmark_height;
971    if (Scr->tbpm.question == None) {
972        Scr->tbpm.question = XCreateBitmapFromData(dpy, Scr->Root,
973                                                   questionmark_bits,
974                                                   questionmark_width,
975                                                   questionmark_height);
976    }
977    /*
978     * this must succeed or else we are in deep trouble elsewhere
979     */
980    return Scr->tbpm.question;
981}
982
983static Pixmap
984CreateMenuPixmap(unsigned *widthp, unsigned *heightp)
985{
986    return CreateMenuIcon(Scr->TBInfo.width - Scr->TBInfo.border * 2,
987                          widthp, heightp);
988}
989
990Pixmap
991CreateMenuIcon(int height, unsigned *widthp, unsigned *heightp)
992{
993    int h, w;
994
995    h = height;
996    w = h * 7 / 8;
997    if (h < 1)
998        h = 1;
999    if (w < 1)
1000        w = 1;
1001    *widthp = (unsigned) w;
1002    *heightp = (unsigned) h;
1003    if (Scr->tbpm.menu == None) {
1004        Pixmap pix;
1005        GC gc;
1006        int ih, iw;
1007        int ix, iy;
1008        int mh, mw;
1009        int tw, th;
1010        int lw, lh;
1011        int lx, ly;
1012        int lines, dly;
1013        int off;
1014        int bw;
1015
1016        pix = Scr->tbpm.menu =
1017            XCreatePixmap(dpy, Scr->Root, (unsigned) w, (unsigned) h, 1);
1018        gc = XCreateGC(dpy, pix, 0L, NULL);
1019        XSetForeground(dpy, gc, 0L);
1020        XFillRectangle(dpy, pix, gc, 0, 0, (unsigned) w, (unsigned) h);
1021        XSetForeground(dpy, gc, 1L);
1022        ix = 1;
1023        iy = 1;
1024        ih = h - iy * 2;
1025        iw = w - ix * 2;
1026        off = ih / 8;
1027        mh = ih - off;
1028        mw = iw - off;
1029        bw = mh / 16;
1030        if (bw == 0 && mw > 2)
1031            bw = 1;
1032        tw = mw - bw * 2;
1033        th = mh - bw * 2;
1034        XFillRectangle(dpy, pix, gc, ix, iy, (unsigned) mw, (unsigned) mh);
1035        XFillRectangle(dpy, pix, gc, ix + iw - mw, iy + ih - mh, (unsigned) mw,
1036                       (unsigned) mh);
1037        XSetForeground(dpy, gc, 0L);
1038        XFillRectangle(dpy, pix, gc, ix + bw, iy + bw, (unsigned) tw,
1039                       (unsigned) th);
1040        XSetForeground(dpy, gc, 1L);
1041        lw = tw / 2;
1042        if ((tw & 1) ^ (lw & 1))
1043            lw++;
1044        lx = ix + bw + (tw - lw) / 2;
1045
1046        lh = th / 2 - bw;
1047        if ((lh & 1) ^ ((th - bw) & 1))
1048            lh++;
1049        ly = iy + bw + (th - bw - lh) / 2;
1050
1051        lines = 3;
1052        if ((lh & 1) && lh < 6) {
1053            lines--;
1054        }
1055        dly = lh / (lines - 1);
1056        while (lines--) {
1057            XFillRectangle(dpy, pix, gc, lx, ly, (unsigned) lw, (unsigned) bw);
1058            ly += dly;
1059        }
1060        XFreeGC(dpy, gc);
1061    }
1062    return Scr->tbpm.menu;
1063}
1064
1065void
1066Bell(int type _X_UNUSED, int percent, Window win _X_UNUSED)
1067{
1068#ifdef XKB
1069    XkbStdBell(dpy, win, percent, type);
1070#else
1071    XBell(dpy, percent);
1072#endif
1073    return;
1074}
1075