util.c revision 7d8a9cc2
1/*
2 *       Copyright 1988 by Evans & Sutherland Computer Corporation,
3 *                          Salt Lake City, Utah
4 *  Portions Copyright 1989 by the Massachusetts Institute of Technology
5 *                        Cambridge, Massachusetts
6 *
7 * Copyright 1992 Claude Lecommandeur.
8 */
9
10/***********************************************************************
11 *
12 * $XConsortium: util.c,v 1.47 91/07/14 13:40:37 rws Exp $
13 *
14 * utility routines for twm
15 *
16 * 28-Oct-87 Thomas E. LaStrange        File created
17 *
18 * Do the necessary modification to be integrated in ctwm.
19 * Can no longer be used for the standard twm.
20 *
21 * 22-April-92 Claude Lecommandeur.
22 *
23 * Changed behavior of DontMoveOff/MoveOffResistance to allow
24 * moving a window off screen less than #MoveOffResistance pixels.
25 * New code will no longer "snap" windows to #MoveOffResistance
26 * pixels off screen and instead movements will just be stopped and
27 * then resume once movement of #MoveOffResistance have been attempted.
28 *
29 * 15-December-02 Bjorn Knutsson
30 *
31 ***********************************************************************/
32
33#include "ctwm.h"
34
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38#include <strings.h>
39
40#include <signal.h>
41#include <sys/time.h>
42
43#include "animate.h"
44#include "add_window.h"
45#include "cursor.h"
46#include "drawing.h"
47#include "gram.tab.h"
48#include "iconmgr.h"
49#include "icons.h"
50#include "image.h"
51#include "screen.h"
52#include "util.h"
53#include "vscreen.h"
54#include "win_decorations.h"
55#include "win_resize.h"
56
57
58/* Handle for debug tracing */
59FILE *tracefile = NULL;
60
61
62/*
63 * Rewrite this, possibly in terms of replace_substr().  Alternately, the
64 * places it's being used might be better served by being preprocessed
65 * into arrays anyway.
66 */
67char *ExpandFilePath(char *path)
68{
69	char *ret, *colon, *p;
70	int  len;
71
72	len = 0;
73	p   = path;
74	while((colon = strchr(p, ':'))) {
75		len += colon - p + 1;
76		if(*p == '~') {
77			len += HomeLen - 1;
78		}
79		p = colon + 1;
80	}
81	if(*p == '~') {
82		len += HomeLen - 1;
83	}
84	len += strlen(p);
85	ret = malloc(len + 1);
86	*ret = 0;
87
88	p   = path;
89	while((colon = strchr(p, ':'))) {
90		*colon = '\0';
91		if(*p == '~') {
92			strcat(ret, Home);
93			strcat(ret, p + 1);
94		}
95		else {
96			strcat(ret, p);
97		}
98		*colon = ':';
99		strcat(ret, ":");
100		p = colon + 1;
101	}
102	if(*p == '~') {
103		strcat(ret, Home);
104		strcat(ret, p + 1);
105	}
106	else {
107		strcat(ret, p);
108	}
109	return ret;
110}
111
112/***********************************************************************
113 *
114 *  Procedure:
115 *      ExpandFilename - expand the tilde character to HOME
116 *              if it is the first character of the filename
117 *
118 *  Returned Value:
119 *      a pointer to the new name
120 *
121 *  Inputs:
122 *      name    - the filename to expand
123 *
124 ***********************************************************************
125 *
126 * Currently only used in one place in image_bitmap.c.  I've left this
127 * here instead of moving it into images at the moment on the assumption
128 * that there might be other places in the codebase where it's useful.
129 */
130char *
131ExpandFilename(const char *name)
132{
133	char *newname;
134
135	/* If it doesn't start with ~/ then it's not our concern */
136	if(name[0] != '~' || name[1] != '/') {
137		return strdup(name);
138	}
139
140	asprintf(&newname, "%s/%s", Home, &name[1]);
141
142	return newname;
143}
144
145
146void
147GetColor(int kind, Pixel *what, const char *name)
148{
149	XColor color;
150	Colormap cmap = Scr->RootColormaps.cwins[0]->colormap->c;
151
152#ifndef TOM
153	if(!Scr->FirstTime) {
154		return;
155	}
156#endif
157
158	if(Scr->Monochrome != kind) {
159		return;
160	}
161
162	if(! XParseColor(dpy, cmap, name, &color)) {
163		fprintf(stderr, "%s:  invalid color name \"%s\"\n", ProgramName, name);
164		return;
165	}
166	if(! XAllocColor(dpy, cmap, &color)) {
167		/* if we could not allocate the color, let's see if this is a
168		 * standard colormap
169		 */
170		XStandardColormap *stdcmap = NULL;
171
172		if(! XParseColor(dpy, cmap, name, &color)) {
173			fprintf(stderr, "%s:  invalid color name \"%s\"\n", ProgramName, name);
174			return;
175		}
176
177		/*
178		 * look through the list of standard colormaps (check cache first)
179		 */
180		if(Scr->StdCmapInfo.mru && Scr->StdCmapInfo.mru->maps &&
181		                (Scr->StdCmapInfo.mru->maps[Scr->StdCmapInfo.mruindex].colormap ==
182		                 cmap)) {
183			stdcmap = &(Scr->StdCmapInfo.mru->maps[Scr->StdCmapInfo.mruindex]);
184		}
185		else {
186			StdCmap *sc;
187
188			for(sc = Scr->StdCmapInfo.head; sc; sc = sc->next) {
189				int i;
190
191				for(i = 0; i < sc->nmaps; i++) {
192					if(sc->maps[i].colormap == cmap) {
193						Scr->StdCmapInfo.mru = sc;
194						Scr->StdCmapInfo.mruindex = i;
195						stdcmap = &(sc->maps[i]);
196						goto gotit;
197					}
198				}
199			}
200		}
201
202gotit:
203		if(stdcmap) {
204			color.pixel = (stdcmap->base_pixel +
205			               ((Pixel)(((float)color.red / 65535.0) *
206			                        stdcmap->red_max + 0.5) *
207			                stdcmap->red_mult) +
208			               ((Pixel)(((float)color.green / 65535.0) *
209			                        stdcmap->green_max + 0.5) *
210			                stdcmap->green_mult) +
211			               ((Pixel)(((float)color.blue  / 65535.0) *
212			                        stdcmap->blue_max + 0.5) *
213			                stdcmap->blue_mult));
214		}
215		else {
216			fprintf(stderr, "%s:  unable to allocate color \"%s\"\n",
217			        ProgramName, name);
218			return;
219		}
220	}
221
222	*what = color.pixel;
223	return;
224}
225
226void GetShadeColors(ColorPair *cp)
227{
228	XColor      xcol;
229	Colormap    cmap = Scr->RootColormaps.cwins[0]->colormap->c;
230	bool        save;
231	float       clearfactor;
232	float       darkfactor;
233	char        clearcol [32], darkcol [32];
234
235	clearfactor = (float) Scr->ClearShadowContrast / 100.0;
236	darkfactor  = (100.0 - (float) Scr->DarkShadowContrast)  / 100.0;
237	xcol.pixel = cp->back;
238	XQueryColor(dpy, cmap, &xcol);
239
240	sprintf(clearcol, "#%04x%04x%04x",
241	        xcol.red   + (unsigned short)((65535 -   xcol.red) * clearfactor),
242	        xcol.green + (unsigned short)((65535 - xcol.green) * clearfactor),
243	        xcol.blue  + (unsigned short)((65535 -  xcol.blue) * clearfactor));
244	sprintf(darkcol,  "#%04x%04x%04x",
245	        (unsigned short)(xcol.red   * darkfactor),
246	        (unsigned short)(xcol.green * darkfactor),
247	        (unsigned short)(xcol.blue  * darkfactor));
248
249	save = Scr->FirstTime;
250	Scr->FirstTime = true;
251	GetColor(Scr->Monochrome, &cp->shadc, clearcol);
252	GetColor(Scr->Monochrome, &cp->shadd,  darkcol);
253	Scr->FirstTime = save;
254}
255
256bool
257UpdateFont(MyFont *font, int height)
258{
259	int prev = font->avg_height;
260	font->avg_fheight = (font->avg_fheight * font->avg_count + height)
261	                    / (font->avg_count + 1);
262	font->avg_count++;
263	/* Arbitrary limit.  */
264	if(font->avg_count >= 256) {
265		font->avg_count = 256;
266	}
267	font->avg_height = (int)(font->avg_fheight + 0.5);
268	/* fprintf (stderr, "Updating avg with %d(%d) + %d -> %d(%f)\n",
269	 *       prev, font->avg_count, height,
270	 *       font->avg_height, font->avg_fheight); */
271	return (prev != font->avg_height);
272}
273
274void GetFont(MyFont *font)
275{
276	char *deffontname = "fixed,*";
277	char **missing_charset_list_return;
278	int missing_charset_count_return;
279	char *def_string_return;
280	XFontSetExtents *font_extents;
281	XFontStruct **xfonts;
282	char **font_names;
283	int i;
284	int ascent;
285	int descent;
286	int fnum;
287	char *basename2;
288
289	if(font->font_set != NULL) {
290		XFreeFontSet(dpy, font->font_set);
291	}
292
293	asprintf(&basename2, "%s,*", font->basename);
294	if((font->font_set = XCreateFontSet(dpy, basename2,
295	                                    &missing_charset_list_return,
296	                                    &missing_charset_count_return,
297	                                    &def_string_return)) == NULL) {
298		fprintf(stderr, "Failed to get fontset %s\n", basename2);
299		if(Scr->DefaultFont.basename) {
300			deffontname = Scr->DefaultFont.basename;
301		}
302		if((font->font_set = XCreateFontSet(dpy, deffontname,
303		                                    &missing_charset_list_return,
304		                                    &missing_charset_count_return,
305		                                    &def_string_return)) == NULL) {
306			fprintf(stderr, "%s:  unable to open fonts \"%s\" or \"%s\"\n",
307			        ProgramName, font->basename, deffontname);
308			exit(1);
309		}
310	}
311	free(basename2);
312	font_extents = XExtentsOfFontSet(font->font_set);
313
314	fnum = XFontsOfFontSet(font->font_set, &xfonts, &font_names);
315	for(i = 0, ascent = 0, descent = 0; i < fnum; i++) {
316		ascent = MaxSize(ascent, (*xfonts)->ascent);
317		descent = MaxSize(descent, (*xfonts)->descent);
318		xfonts++;
319	}
320
321	font->height = font_extents->max_logical_extent.height;
322	font->y = ascent;
323	font->ascent = ascent;
324	font->descent = descent;
325	font->avg_height = 0;
326	font->avg_fheight = 0.0;
327	font->avg_count = 0;
328}
329
330
331#if 0
332static void move_to_head(TwmWindow *t)
333{
334	if(t == NULL) {
335		return;
336	}
337	if(Scr->FirstWindow == t) {
338		return;
339	}
340
341	/* Unlink t from current position */
342	if(t->prev) {
343		t->prev->next = t->next;
344	}
345	if(t->next) {
346		t->next->prev = t->prev;
347	}
348
349	/* Re-link t at head */
350	t->next = Scr->FirstWindow;
351	if(Scr->FirstWindow != NULL) {
352		Scr->FirstWindow->prev = t;
353	}
354	t->prev = NULL;
355	Scr->FirstWindow = t;
356}
357
358/*
359 * Moves window 't' after window 'after'.
360 *
361 * If 'after' == NULL, puts it at the head.
362 * If 't' == NULL, does nothing.
363 * If the 't' is already after 'after', does nothing.
364 */
365
366void move_to_after(TwmWindow *t, TwmWindow *after)
367{
368	if(after == NULL) {
369		move_to_head(t);
370		return;
371	}
372	if(t == NULL) {
373		return;
374	}
375	if(after->next == t) {
376		return;
377	}
378
379	/* Unlink t from current position */
380	if(t->prev) {
381		t->prev->next = t->next;
382	}
383	if(t->next) {
384		t->next->prev = t->prev;
385	}
386
387	/* Re-link t after 'after' */
388	t->next = after->next;
389	if(after->next) {
390		after->next->prev = t;
391	}
392	t->prev = after;
393	after->next = t;
394}
395#endif
396
397
398void RescueWindows(void)
399{
400	TwmWindow *twm_win = Scr->FirstWindow;
401
402	while(twm_win) {
403		VirtualScreen *vs = twm_win->vs;
404		if(vs) {
405			/*
406			 * Check if this window seems completely out of sight.
407			 */
408			int x = twm_win->frame_x;
409			int y = twm_win->frame_y;
410			int w = twm_win->frame_width;
411			int h = twm_win->frame_height;
412			int bw = twm_win->frame_bw;
413			int fullw = w + 2 * bw;
414			int fullh = h + 2 * bw;
415			int old_x = x, old_y = y;
416			struct Icon *i;
417
418#define MARGIN  20
419
420			if(x >= vs->w - MARGIN) {
421				x = vs->w - fullw;
422			}
423			if(y >= vs->h - MARGIN) {
424				y = vs->h - fullh;
425			}
426			if((x + fullw <= MARGIN)) {
427				x = 0;
428			}
429			if((y + fullh <= MARGIN)) {
430				y = 0;
431			}
432
433			if(x != old_x || y != old_y) {
434				SetupWindow(twm_win, x, y, w, h, -1);
435			}
436
437			/*
438			 * If there is an icon, check it too.
439			 */
440			i = twm_win->icon;
441			if(i != NULL) {
442				x = i->w_x;
443				y = i->w_y;
444				w = i->w_width;
445				h = i->w_height;
446				old_x = x;
447				old_y = y;
448
449				if(x >= vs->w - MARGIN) {
450					x = vs->w - w;
451				}
452				if(y >= vs->h - MARGIN) {
453					y = vs->h - h;
454				}
455				if((x + w <= MARGIN)) {
456					x = 0;
457				}
458				if((y + h <= MARGIN)) {
459					y = 0;
460				}
461
462				if(x != old_x || y != old_y) {
463					XMoveWindow(dpy, i->w, x, y);
464					i->w_x = x;
465					i->w_y = y;
466				}
467			}
468#undef MARGIN
469		}
470		twm_win = twm_win->next;
471	}
472}
473
474void DebugTrace(char *file)
475{
476	if(!file) {
477		return;
478	}
479	if(tracefile) {
480		fprintf(stderr, "stop logging events\n");
481		if(tracefile != stderr) {
482			fclose(tracefile);
483		}
484		tracefile = NULL;
485	}
486	else {
487		if(strcmp(file, "stderr")) {
488			tracefile = fopen(file, "w");
489		}
490		else {
491			tracefile = stderr;
492		}
493		fprintf(stderr, "logging events to : %s\n", file);
494	}
495}
496
497
498/*
499 * A safe strncpy(), which always ensures NUL-termination.
500 *
501 * XXX This is really just a slightly pessimized implementation of
502 * strlcpy().  Maybe we should use that instead, with a local
503 * implementation for systems like glibc-users that lack it?
504 */
505void
506safe_strncpy(char *dest, const char *src, size_t size)
507{
508	strncpy(dest, src, size - 1);
509	dest[size - 1] = '\0';
510}
511