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