util.c revision 329eaa64
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 "gram.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    register 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
213    if (!Scr->DoZoom || Scr->ZoomCount < 1)
214        return;
215
216    if (wf == None || wt == None)
217        return;
218
219    XGetGeometry(dpy, wf, &JunkRoot, &fx, &fy, &fw, &fh, &JunkBW, &JunkDepth);
220    XGetGeometry(dpy, wt, &JunkRoot, &tx, &ty, &tw, &th, &JunkBW, &JunkDepth);
221
222    dx = ((long) (tx - fx));    /* going from -> to */
223    dy = ((long) (ty - fy));    /* going from -> to */
224    dw = ((long) (tw - fw));    /* going from -> to */
225    dh = ((long) (th - fh));    /* going from -> to */
226    z = (long) (Scr->ZoomCount + 1);
227
228    for (j = 0; j < 2; j++) {
229        long i;
230
231        XDrawRectangle(dpy, Scr->Root, Scr->DrawGC, fx, fy, fw, fh);
232        for (i = 1; i < z; i++) {
233            int x = fx + (int) ((dx * i) / z);
234            int y = fy + (int) ((dy * i) / z);
235            unsigned width = (unsigned) (((long) fw) + (dw * i) / z);
236            unsigned height = (unsigned) (((long) fh) + (dh * i) / z);
237
238            XDrawRectangle(dpy, Scr->Root, Scr->DrawGC, x, y, width, height);
239        }
240        XDrawRectangle(dpy, Scr->Root, Scr->DrawGC, tx, ty, tw, th);
241    }
242}
243
244/**
245 * expand the tilde character to HOME if it is the first
246 * character of the filename
247 *
248 *      \return a pointer to the new name
249 *
250 *  \param name  the filename to expand
251 */
252char *
253ExpandFilename(const char *name)
254{
255    char *newname;
256
257    if (name[0] != '~')
258        return strdup(name);
259
260    newname = malloc((size_t) HomeLen + strlen(name) + 2);
261    if (!newname) {
262        twmWarning("unable to allocate %lu bytes to expand filename %s/%s",
263                   (unsigned long) HomeLen + (unsigned long) strlen(name) + 2,
264                   Home, &name[1]);
265    }
266    else {
267        (void) sprintf(newname, "%s/%s", Home, &name[1]);
268    }
269
270    return newname;
271}
272
273/**
274 * read in the bitmap file for the unknown icon
275 *
276 * \param name  the filename to read
277 */
278void
279GetUnknownIcon(const char *name)
280{
281    if ((Scr->UnknownPm = GetBitmap(name)) != None) {
282        XGetGeometry(dpy, Scr->UnknownPm, &JunkRoot, &JunkX, &JunkY,
283                     (unsigned int *) &Scr->UnknownWidth,
284                     (unsigned int *) &Scr->UnknownHeight, &JunkBW, &JunkDepth);
285    }
286}
287
288/**
289 *      FindBitmap - read in a bitmap file and return size
290 *
291 *  \return pixmap associated with bitmap
292 *
293 *  \param name          filename to read
294 *  \param[out] widthp   pointer to width of bitmap
295 *  \param[out] heightp  pointer to height of bitmap
296 */
297Pixmap
298FindBitmap(const char *name, unsigned *widthp, unsigned *heightp)
299{
300    char *bigname;
301    Pixmap pm;
302
303    if (!name)
304        return None;
305
306    /*
307     * Names of the form :name refer to hardcoded images that are scaled to
308     * look nice in title buttons.  Eventually, it would be nice to put in a
309     * menu symbol as well....
310     */
311    if (name[0] == ':') {
312        int i;
313	/* *INDENT-OFF* */
314        static struct {
315            const char *name;
316            Pixmap (*proc)(unsigned int *, unsigned int *);
317        } pmtab[] = {
318            { TBPM_DOT,         CreateDotPixmap },
319            { TBPM_ICONIFY,     CreateDotPixmap },
320            { TBPM_RESIZE,      CreateResizePixmap },
321            { TBPM_XLOGO,       CreateXLogoPixmap },
322            { TBPM_DELETE,      CreateXLogoPixmap },
323            { TBPM_MENU,        CreateMenuPixmap },
324            { TBPM_QUESTION,    CreateQuestionPixmap },
325        };
326	/* *INDENT-ON* */
327
328        for (i = 0; (size_t) i < (sizeof pmtab) / (sizeof pmtab[0]); i++) {
329            if (XmuCompareISOLatin1(pmtab[i].name, name) == 0)
330                return (*pmtab[i].proc) (widthp, heightp);
331        }
332        twmWarning("no such built-in bitmap \"%s\"", name);
333        return None;
334    }
335
336    /*
337     * Generate a full pathname if any special prefix characters (such as ~)
338     * are used.  If the bigname is different from name, bigname will need to
339     * be freed.
340     */
341    bigname = ExpandFilename(name);
342    if (!bigname)
343        return None;
344
345    /*
346     * look along bitmapFilePath resource same as toolkit clients
347     */
348    pm = XmuLocateBitmapFile(ScreenOfDisplay(dpy, Scr->screen), bigname, NULL,
349                             0, (int *) widthp, (int *) heightp, &HotX, &HotY);
350    if (pm == None && Scr->IconDirectory && bigname[0] != '/') {
351        free(bigname);
352        /*
353         * Attempt to find icon in old IconDirectory (now obsolete)
354         */
355        bigname = malloc(strlen(name) + strlen(Scr->IconDirectory) + 2);
356        if (!bigname) {
357            twmWarning("unable to allocate memory for \"%s/%s\"",
358                       Scr->IconDirectory, name);
359            return None;
360        }
361        (void) sprintf(bigname, "%s/%s", Scr->IconDirectory, name);
362        if (XReadBitmapFile(dpy, Scr->Root, bigname, widthp, heightp, &pm,
363                            &HotX, &HotY) != BitmapSuccess) {
364            pm = None;
365        }
366    }
367    free(bigname);
368    if (pm == None) {
369        twmWarning("unable to find bitmap \"%s\"", name);
370    }
371
372    return pm;
373}
374
375Pixmap
376GetBitmap(const char *name)
377{
378    return FindBitmap(name, &JunkWidth, &JunkHeight);
379}
380
381void
382InsertRGBColormap(Atom a, XStandardColormap *maps, int nmaps, Bool replace)
383{
384    StdCmap *sc = NULL;
385
386    if (replace) {              /* locate existing entry */
387        for (sc = Scr->StdCmapInfo.head; sc; sc = sc->next) {
388            if (sc->atom == a)
389                break;
390        }
391    }
392
393    if (!sc) {                  /* no existing, allocate new */
394        sc = malloc(sizeof(StdCmap));
395        if (!sc) {
396            twmWarning("unable to allocate %lu bytes for StdCmap",
397                       (unsigned long) sizeof(StdCmap));
398            return;
399        }
400        replace = False;
401    }
402
403    if (replace) {              /* just update contents */
404        if (sc->maps)
405            XFree(sc->maps);
406        if (sc == Scr->StdCmapInfo.mru)
407            Scr->StdCmapInfo.mru = NULL;
408    }
409    else {                      /* else appending */
410        sc->next = NULL;
411        sc->atom = a;
412        if (Scr->StdCmapInfo.tail) {
413            Scr->StdCmapInfo.tail->next = sc;
414        }
415        else {
416            Scr->StdCmapInfo.head = sc;
417        }
418        Scr->StdCmapInfo.tail = sc;
419    }
420    sc->nmaps = nmaps;
421    sc->maps = maps;
422
423    return;
424}
425
426void
427RemoveRGBColormap(Atom a)
428{
429    StdCmap *sc, *prev;
430
431    prev = NULL;
432    for (sc = Scr->StdCmapInfo.head; sc; sc = sc->next) {
433        if (sc->atom == a)
434            break;
435        prev = sc;
436    }
437    if (sc) {                   /* found one */
438        if (sc->maps)
439            XFree(sc->maps);
440        if (prev)
441            prev->next = sc->next;
442        if (Scr->StdCmapInfo.head == sc)
443            Scr->StdCmapInfo.head = sc->next;
444        if (Scr->StdCmapInfo.tail == sc)
445            Scr->StdCmapInfo.tail = prev;
446        if (Scr->StdCmapInfo.mru == sc)
447            Scr->StdCmapInfo.mru = NULL;
448    }
449    return;
450}
451
452void
453LocateStandardColormaps(void)
454{
455    Atom *atoms;
456    int natoms;
457    int i;
458
459    atoms = XListProperties(dpy, Scr->Root, &natoms);
460    for (i = 0; i < natoms; i++) {
461        XStandardColormap *maps = NULL;
462        int nmaps;
463
464        if (XGetRGBColormaps(dpy, Scr->Root, &maps, &nmaps, atoms[i])) {
465            /* if got one, then append to current list */
466            InsertRGBColormap(atoms[i], maps, nmaps, False);
467        }
468    }
469    if (atoms)
470        XFree(atoms);
471    return;
472}
473
474void
475GetColor(int kind, Pixel *what, const char *name)
476{
477    XColor color, junkcolor;
478    Status stat = 0;
479    Colormap cmap = Scr->TwmRoot.cmaps.cwins[0]->colormap->c;
480
481#ifndef TOM
482    if (!Scr->FirstTime)
483        return;
484#endif
485
486    if (Scr->Monochrome != kind)
487        return;
488
489    if (!XAllocNamedColor(dpy, cmap, name, &color, &junkcolor)) {
490        /* if we could not allocate the color, let's see if this is a
491         * standard colormap
492         */
493        XStandardColormap *stdcmap = NULL;
494
495        /* parse the named color */
496        if (name[0] != '#')
497            stat = XParseColor(dpy, cmap, name, &color);
498        if (!stat) {
499            twmWarning("invalid color name \"%s\"", name);
500            return;
501        }
502
503        /*
504         * look through the list of standard colormaps (check cache first)
505         */
506        if (Scr->StdCmapInfo.mru && Scr->StdCmapInfo.mru->maps &&
507            (Scr->StdCmapInfo.mru->maps[Scr->StdCmapInfo.mruindex].colormap ==
508             cmap)) {
509            stdcmap = &(Scr->StdCmapInfo.mru->maps[Scr->StdCmapInfo.mruindex]);
510        }
511        else {
512            StdCmap *sc;
513
514            for (sc = Scr->StdCmapInfo.head; sc; sc = sc->next) {
515                int i;
516
517                for (i = 0; i < sc->nmaps; i++) {
518                    if (sc->maps[i].colormap == cmap) {
519                        Scr->StdCmapInfo.mru = sc;
520                        Scr->StdCmapInfo.mruindex = i;
521                        stdcmap = &(sc->maps[i]);
522                        goto gotit;
523                    }
524                }
525            }
526        }
527
528 gotit:
529        if (stdcmap) {
530            color.pixel = (stdcmap->base_pixel +
531                           ((Pixel) (((float) color.red / 65535.0) *
532                                     (double) stdcmap->red_max + 0.5) *
533                            stdcmap->red_mult) +
534                           ((Pixel) (((float) color.green / 65535.0) *
535                                     (double) stdcmap->green_max + 0.5) *
536                            stdcmap->green_mult) +
537                           ((Pixel) (((float) color.blue / 65535.0) *
538                                     (double) stdcmap->blue_max + 0.5) *
539                            stdcmap->blue_mult));
540        }
541        else {
542            twmWarning("unable to allocate color \"%s\"", name);
543            return;
544        }
545    }
546
547    *what = color.pixel;
548}
549
550void
551GetColorValue(int kind, XColor *what, const char *name)
552{
553    XColor junkcolor;
554    Colormap cmap = Scr->TwmRoot.cmaps.cwins[0]->colormap->c;
555
556#ifndef TOM
557    if (!Scr->FirstTime)
558        return;
559#endif
560
561    if (Scr->Monochrome != kind)
562        return;
563
564    if (!XLookupColor(dpy, cmap, name, what, &junkcolor)) {
565        twmWarning("invalid color name \"%s\"", name);
566    }
567    else {
568        what->pixel = AllPlanes;
569    }
570}
571
572static Boolean
573FindFontSet(MyFont *font, const char *fontname)
574{
575    char **missing_charset_list_return;
576    int missing_charset_count_return;
577    char *def_string_return;
578    XFontSetExtents *font_extents;
579    XFontStruct **xfonts;
580    char **font_names;
581
582    if (use_fontset) {
583        int ascent;
584        int descent;
585        int fnum;
586        register int i;
587
588        if (font->fontset != NULL) {
589            XFreeFontSet(dpy, font->fontset);
590        }
591
592        if ((font->fontset = XCreateFontSet(dpy, fontname,
593                                            &missing_charset_list_return,
594                                            &missing_charset_count_return,
595                                            &def_string_return)) == NULL) {
596            return False;
597        }
598        if (missing_charset_count_return) {
599            twmVerbose("%d fonts are missing from fontset",
600                       missing_charset_count_return);
601            for (i = 0; i < missing_charset_count_return; i++) {
602                twmVerbose("font for charset %s is lacking.",
603                           missing_charset_list_return[i]);
604            }
605        }
606
607        font_extents = XExtentsOfFontSet(font->fontset);
608        fnum = XFontsOfFontSet(font->fontset, &xfonts, &font_names);
609        for (i = 0, ascent = 0, descent = 0; i < fnum; i++) {
610            if (ascent < (*xfonts)->ascent)
611                ascent = (*xfonts)->ascent;
612            if (descent < (*xfonts)->descent)
613                descent = (*xfonts)->descent;
614            xfonts++;
615        }
616        font->height = font_extents->max_logical_extent.height;
617        font->y = ascent;
618        font->ascent = ascent;
619        font->descent = descent;
620        twmMessage("created fontset with %d fonts (%d missing) for \"%s\"",
621                   fnum, missing_charset_count_return,
622                   fontname ? fontname : "NULL");
623        return True;
624    }
625
626    if (font->font != NULL)
627        XFreeFont(dpy, font->font);
628
629    if ((font->font = XLoadQueryFont(dpy, fontname)) == NULL) {
630        return False;
631    }
632    font->height = font->font->ascent + font->font->descent;
633    font->y = font->font->ascent;
634    font->ascent = font->font->ascent;
635    font->descent = font->font->descent;
636    return True;
637}
638
639/*
640 * The following functions are sensible to 'use_fontset'.
641 * When 'use_fontset' is True,
642 *  - XFontSet-related internationalized functions are used
643 *     so as multibyte languages can be displayed.
644 * When 'use_fontset' is False,
645 *  - XFontStruct-related conventional functions are used
646 *     so as 8-bit characters can be displayed even when
647 *     locale is not set properly.
648 */
649void
650GetFont(MyFont *font)
651{
652
653    if (!FindFontSet(font, font->name)) {
654        const char *what = "fonts";
655        const char *deffontname = "fixed";
656
657        if (use_fontset) {
658            what = "fontsets";
659        }
660        else if (Scr->DefaultFont.name) {
661            deffontname = Scr->DefaultFont.name;
662        }
663        if (!FindFontSet(font, deffontname)) {
664            twmError("unable to open %s \"%s\" or \"%s\"",
665                     what, font->name, deffontname);
666        }
667    }
668}
669
670int
671MyFont_TextWidth(MyFont *font, const char *string, int len)
672{
673    XRectangle ink_rect;
674    XRectangle logical_rect;
675
676    if (use_fontset) {
677        XmbTextExtents(font->fontset, string, len, &ink_rect, &logical_rect);
678        return logical_rect.width;
679    }
680    return XTextWidth(font->font, string, len);
681}
682
683void
684MyFont_DrawImageString(Display *dpy2, Drawable d, MyFont *font, GC gc,
685                       int x, int y, const char *string, int len)
686{
687    if (use_fontset) {
688        XmbDrawImageString(dpy2, d, font->fontset, gc, x, y, string, len);
689        return;
690    }
691    XDrawImageString(dpy2, d, gc, x, y, string, len);
692}
693
694void
695MyFont_DrawString(Display *dpy2, Drawable d, MyFont *font, GC gc,
696                  int x, int y, const char *string, int len)
697{
698    if (use_fontset) {
699        XmbDrawString(dpy2, d, font->fontset, gc, x, y, string, len);
700        return;
701    }
702    XDrawString(dpy2, d, gc, x, y, string, len);
703}
704
705void
706MyFont_ChangeGC(unsigned long fix_fore, unsigned long fix_back,
707                MyFont *fix_font)
708{
709    Gcv.foreground = fix_fore;
710    Gcv.background = fix_back;
711    if (use_fontset) {
712        XChangeGC(dpy, Scr->NormalGC, GCForeground | GCBackground, &Gcv);
713        return;
714    }
715    Gcv.font = fix_font->font->fid;
716    XChangeGC(dpy, Scr->NormalGC, GCFont | GCForeground | GCBackground, &Gcv);
717}
718
719/*
720 * The following functions are internationalized substitutions
721 * for XFetchName and XGetIconName using XGetWMName and
722 * XGetWMIconName.
723 *
724 * Please note that the third arguments have to be freed using free(),
725 * not XFree().
726 */
727Status
728I18N_FetchName(Display *dpy2, Window w, char **winname)
729{
730    int status;
731    XTextProperty text_prop;
732    int rc = 0;
733
734    *winname = NULL;
735
736    status = XGetWMName(dpy2, w, &text_prop);
737    if (status && text_prop.value && text_prop.nitems) {
738        char **list = NULL;
739        int num;
740
741        status = XmbTextPropertyToTextList(dpy2, &text_prop, &list, &num);
742        if (status >= Success && num && list && *list) {
743            XFree(text_prop.value);
744            *winname = strdup(*list);
745            XFreeStringList(list);
746            rc = 1;
747        }
748        else {
749            char *value = NULL;
750
751            /*
752             * If the system's locale support is broken (e.g., missing useful
753             * parts), the preceding Xmb call may fail.
754             */
755            if (XFetchName(dpy2, w, &value) && value != NULL) {
756                *winname = strdup(value);
757                XFree(value);
758                rc = 1;
759            }
760        }
761    }
762
763    return rc;
764}
765
766Status
767I18N_GetIconName(Display *dpy2, Window w, char **iconname)
768{
769    int status;
770    XTextProperty text_prop;
771    char **list;
772    int num;
773
774    status = XGetWMIconName(dpy2, w, &text_prop);
775    if (!status || !text_prop.value || !text_prop.nitems)
776        return 0;
777    status = XmbTextPropertyToTextList(dpy2, &text_prop, &list, &num);
778    if (status < Success || !num || !*list)
779        return 0;
780    XFree(text_prop.value);
781    *iconname = (char *) strdup(*list);
782    XFreeStringList(list);
783    return 1;
784}
785
786/**
787 * separate routine to set focus to make things more understandable
788 * and easier to debug
789 */
790void
791SetFocus(TwmWindow *tmp_win, Time time)
792{
793    Window w = (tmp_win ? tmp_win->w : PointerRoot);
794
795#ifdef TRACE
796    if (tmp_win) {
797        twmMessage("Focusing on window \"%s\"", tmp_win->full_name);
798    }
799    else {
800        twmMessage("Unfocusing; Scr->Focus was \"%s\"",
801                   Scr->Focus ? Scr->Focus->full_name : "(nil)");
802    }
803#endif
804
805    XSetInputFocus(dpy, w, RevertToPointerRoot, time);
806}
807
808static Pixmap
809CreateXLogoPixmap(unsigned *widthp, unsigned *heightp)
810{
811    int h = Scr->TBInfo.width - Scr->TBInfo.border * 2;
812
813    if (h < 0)
814        h = 0;
815
816    *widthp = *heightp = (unsigned int) h;
817    if (Scr->tbpm.xlogo == None) {
818        GC gc, gcBack;
819
820        Scr->tbpm.xlogo =
821            XCreatePixmap(dpy, Scr->Root, (unsigned) h, (unsigned) h, 1);
822        gc = XCreateGC(dpy, Scr->tbpm.xlogo, 0L, NULL);
823        XSetForeground(dpy, gc, 0);
824        XFillRectangle(dpy, Scr->tbpm.xlogo, gc, 0, 0, (unsigned) h,
825                       (unsigned) h);
826        XSetForeground(dpy, gc, 1);
827        gcBack = XCreateGC(dpy, Scr->tbpm.xlogo, 0L, NULL);
828        XSetForeground(dpy, gcBack, 0);
829
830        /*
831         * draw the logo large so that it gets as dense as possible; then white
832         * out the edges so that they look crisp
833         */
834        XmuDrawLogo(dpy, Scr->tbpm.xlogo, gc, gcBack, -1, -1,
835                    (unsigned) (h + 2), (unsigned) (h + 2));
836        XDrawRectangle(dpy, Scr->tbpm.xlogo, gcBack, 0, 0, (unsigned) (h - 1),
837                       (unsigned) (h - 1));
838
839        /*
840         * done drawing
841         */
842        XFreeGC(dpy, gc);
843        XFreeGC(dpy, gcBack);
844    }
845    return Scr->tbpm.xlogo;
846}
847
848static Pixmap
849CreateResizePixmap(unsigned *widthp, unsigned *heightp)
850{
851    int h = Scr->TBInfo.width - Scr->TBInfo.border * 2;
852
853    if (h < 1)
854        h = 1;
855
856    *widthp = *heightp = (unsigned int) h;
857    if (Scr->tbpm.resize == None) {
858        XPoint points[3];
859        GC gc;
860        int w;
861        int lw;
862
863        /*
864         * create the pixmap
865         */
866        Scr->tbpm.resize =
867            XCreatePixmap(dpy, Scr->Root, (unsigned) h, (unsigned) h, 1);
868        gc = XCreateGC(dpy, Scr->tbpm.resize, 0L, NULL);
869        XSetForeground(dpy, gc, 0);
870        XFillRectangle(dpy, Scr->tbpm.resize, gc, 0, 0, (unsigned) h,
871                       (unsigned) h);
872        XSetForeground(dpy, gc, 1);
873        lw = h / 16;
874        if (lw == 1)
875            lw = 0;
876        XSetLineAttributes(dpy, gc, (unsigned) lw, LineSolid, CapButt,
877                           JoinMiter);
878
879        /*
880         * draw the resize button,
881         */
882        w = (h * 2) / 3;
883        points[0].x = (short) w;
884        points[0].y = 0;
885        points[1].x = (short) w;
886        points[1].y = (short) w;
887        points[2].x = 0;
888        points[2].y = (short) w;
889        XDrawLines(dpy, Scr->tbpm.resize, gc, points, 3, CoordModeOrigin);
890        w = w / 2;
891        points[0].x = (short) w;
892        points[0].y = 0;
893        points[1].x = (short) w;
894        points[1].y = (short) w;
895        points[2].x = 0;
896        points[2].y = (short) w;
897        XDrawLines(dpy, Scr->tbpm.resize, gc, points, 3, CoordModeOrigin);
898
899        /*
900         * done drawing
901         */
902        XFreeGC(dpy, gc);
903    }
904    return Scr->tbpm.resize;
905}
906
907static Pixmap
908CreateDotPixmap(unsigned *widthp, unsigned *heightp)
909{
910    int h = Scr->TBInfo.width - Scr->TBInfo.border * 2;
911
912    h = h * 3 / 4;
913    if (h < 1)
914        h = 1;
915    if (!(h & 1))
916        h--;
917    *widthp = *heightp = (unsigned int) h;
918    if (Scr->tbpm.delete == None) {
919        GC gc;
920        Pixmap pix;
921
922        pix = Scr->tbpm.delete =
923            XCreatePixmap(dpy, Scr->Root, (unsigned) h, (unsigned) h, 1);
924        gc = XCreateGC(dpy, pix, 0L, NULL);
925        XSetLineAttributes(dpy, gc, (unsigned) h, LineSolid, CapRound,
926                           JoinRound);
927        XSetForeground(dpy, gc, 0L);
928        XFillRectangle(dpy, pix, gc, 0, 0, (unsigned) h, (unsigned) h);
929        XSetForeground(dpy, gc, 1L);
930        XDrawLine(dpy, pix, gc, h / 2, h / 2, h / 2, h / 2);
931        XFreeGC(dpy, gc);
932    }
933    return Scr->tbpm.delete;
934}
935
936#define questionmark_width 8
937#define questionmark_height 8
938static char questionmark_bits[] = {
939    0x38, 0x7c, 0x64, 0x30, 0x18, 0x00, 0x18, 0x18
940};
941
942static Pixmap
943CreateQuestionPixmap(unsigned *widthp, unsigned *heightp)
944{
945    *widthp = questionmark_width;
946    *heightp = questionmark_height;
947    if (Scr->tbpm.question == None) {
948        Scr->tbpm.question = XCreateBitmapFromData(dpy, Scr->Root,
949                                                   questionmark_bits,
950                                                   questionmark_width,
951                                                   questionmark_height);
952    }
953    /*
954     * this must succeed or else we are in deep trouble elsewhere
955     */
956    return Scr->tbpm.question;
957}
958
959static Pixmap
960CreateMenuPixmap(unsigned *widthp, unsigned *heightp)
961{
962    return CreateMenuIcon(Scr->TBInfo.width - Scr->TBInfo.border * 2,
963                          widthp, heightp);
964}
965
966Pixmap
967CreateMenuIcon(int height, unsigned *widthp, unsigned *heightp)
968{
969    int h, w;
970
971    h = height;
972    w = h * 7 / 8;
973    if (h < 1)
974        h = 1;
975    if (w < 1)
976        w = 1;
977    *widthp = (unsigned) w;
978    *heightp = (unsigned) h;
979    if (Scr->tbpm.menu == None) {
980        Pixmap pix;
981        GC gc;
982        int ih, iw;
983        int ix, iy;
984        int mh, mw;
985        int tw, th;
986        int lw, lh;
987        int lx, ly;
988        int lines, dly;
989        int off;
990        int bw;
991
992        pix = Scr->tbpm.menu =
993            XCreatePixmap(dpy, Scr->Root, (unsigned) w, (unsigned) h, 1);
994        gc = XCreateGC(dpy, pix, 0L, NULL);
995        XSetForeground(dpy, gc, 0L);
996        XFillRectangle(dpy, pix, gc, 0, 0, (unsigned) w, (unsigned) h);
997        XSetForeground(dpy, gc, 1L);
998        ix = 1;
999        iy = 1;
1000        ih = h - iy * 2;
1001        iw = w - ix * 2;
1002        off = ih / 8;
1003        mh = ih - off;
1004        mw = iw - off;
1005        bw = mh / 16;
1006        if (bw == 0 && mw > 2)
1007            bw = 1;
1008        tw = mw - bw * 2;
1009        th = mh - bw * 2;
1010        XFillRectangle(dpy, pix, gc, ix, iy, (unsigned) mw, (unsigned) mh);
1011        XFillRectangle(dpy, pix, gc, ix + iw - mw, iy + ih - mh, (unsigned) mw,
1012                       (unsigned) mh);
1013        XSetForeground(dpy, gc, 0L);
1014        XFillRectangle(dpy, pix, gc, ix + bw, iy + bw, (unsigned) tw,
1015                       (unsigned) th);
1016        XSetForeground(dpy, gc, 1L);
1017        lw = tw / 2;
1018        if ((tw & 1) ^ (lw & 1))
1019            lw++;
1020        lx = ix + bw + (tw - lw) / 2;
1021
1022        lh = th / 2 - bw;
1023        if ((lh & 1) ^ ((th - bw) & 1))
1024            lh++;
1025        ly = iy + bw + (th - bw - lh) / 2;
1026
1027        lines = 3;
1028        if ((lh & 1) && lh < 6) {
1029            lines--;
1030        }
1031        dly = lh / (lines - 1);
1032        while (lines--) {
1033            XFillRectangle(dpy, pix, gc, lx, ly, (unsigned) lw, (unsigned) bw);
1034            ly += dly;
1035        }
1036        XFreeGC(dpy, gc);
1037    }
1038    return Scr->tbpm.menu;
1039}
1040
1041void
1042Bell(int type _X_UNUSED, int percent, Window win _X_UNUSED)
1043{
1044#ifdef XKB
1045    XkbStdBell(dpy, win, percent, type);
1046#else
1047    XBell(dpy, percent);
1048#endif
1049    return;
1050}
1051