Home | History | Annotate | Line # | Download | only in src
      1 /*****************************************************************************/
      2 /*
      3 
      4 Copyright 1989, 1998  The Open Group
      5 
      6 Permission to use, copy, modify, distribute, and sell this software and its
      7 documentation for any purpose is hereby granted without fee, provided that
      8 the above copyright notice appear in all copies and that both that
      9 copyright notice and this permission notice appear in supporting
     10 documentation.
     11 
     12 The above copyright notice and this permission notice shall be included in
     13 all copies or substantial portions of the Software.
     14 
     15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
     18 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
     19 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     20 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     21 
     22 Except as contained in this notice, the name of The Open Group shall not be
     23 used in advertising or otherwise to promote the sale, use or other dealings
     24 in 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 
     70 static Pixmap CreateXLogoPixmap(unsigned int *widthp, unsigned int *heightp);
     71 static Pixmap CreateResizePixmap(unsigned int *widthp, unsigned int *heightp);
     72 static Pixmap CreateDotPixmap(unsigned int *widthp, unsigned int *heightp);
     73 static Pixmap CreateQuestionPixmap(unsigned int *widthp, unsigned int *heightp);
     74 static Pixmap CreateMenuPixmap(unsigned int *widthp, unsigned int *heightp);
     75 
     76 int 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  */
     87 void
     88 MoveOutline(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  */
    204 void
    205 Zoom(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  */
    254 char *
    255 ExpandFilename(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  */
    280 void
    281 GetUnknownIcon(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  */
    303 Pixmap
    304 FindBitmap(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 
    382 Pixmap
    383 GetBitmap(const char *name)
    384 {
    385     unsigned udummy = 0;
    386 
    387     return FindBitmap(name, &udummy, &udummy);
    388 }
    389 
    390 void
    391 InsertRGBColormap(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 
    435 void
    436 RemoveRGBColormap(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 
    461 void
    462 LocateStandardColormaps(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 
    483 void
    484 GetColor(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 
    557 void
    558 GetColorValue(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 
    577 static Boolean
    578 FindFontSet(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  */
    655 void
    656 GetFont(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 
    676 void
    677 DestroyFont(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 
    694 int
    695 MyFont_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 
    707 void
    708 MyFont_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 
    718 void
    719 MyFont_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 
    729 void
    730 MyFont_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  */
    751 Status
    752 I18N_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 
    790 Status
    791 I18N_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  */
    814 void
    815 SetFocus(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 
    832 static Pixmap
    833 CreateXLogoPixmap(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 
    872 static Pixmap
    873 CreateResizePixmap(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 
    931 static Pixmap
    932 CreateDotPixmap(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
    962 static char questionmark_bits[] = {
    963     0x38, 0x7c, 0x64, 0x30, 0x18, 0x00, 0x18, 0x18
    964 };
    965 
    966 static Pixmap
    967 CreateQuestionPixmap(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 
    983 static Pixmap
    984 CreateMenuPixmap(unsigned *widthp, unsigned *heightp)
    985 {
    986     return CreateMenuIcon(Scr->TBInfo.width - Scr->TBInfo.border * 2,
    987                           widthp, heightp);
    988 }
    989 
    990 Pixmap
    991 CreateMenuIcon(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 
   1065 void
   1066 Bell(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