util.c revision df1c27a6
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
146
147/*
148 * Some color utils
149 */
150/**
151 * Get info from the server about a given color.
152 */
153void
154GetColor(int kind, Pixel *what, const char *name)
155{
156	XColor color;
157	Colormap cmap = Scr->RootColormaps.cwins[0]->colormap->c;
158
159	// If we have no valid X connection (generally means a --cfgchk or
160	// similar run; wont' happen in normal operations), just stub out.
161	if(dpy == NULL) {
162		*what = 0;
163		return;
164	}
165
166#ifndef TOM
167	if(!Scr->FirstTime) {
168		return;
169	}
170#endif
171
172	if(Scr->Monochrome != kind) {
173		return;
174	}
175
176	if(! XParseColor(dpy, cmap, name, &color)) {
177		fprintf(stderr, "%s:  invalid color name \"%s\"\n", ProgramName, name);
178		return;
179	}
180	if(! XAllocColor(dpy, cmap, &color)) {
181		/* if we could not allocate the color, let's see if this is a
182		 * standard colormap
183		 */
184		XStandardColormap *stdcmap = NULL;
185
186		if(! XParseColor(dpy, cmap, name, &color)) {
187			fprintf(stderr, "%s:  invalid color name \"%s\"\n", ProgramName, name);
188			return;
189		}
190
191		/*
192		 * look through the list of standard colormaps (check cache first)
193		 */
194		if(Scr->StdCmapInfo.mru && Scr->StdCmapInfo.mru->maps &&
195		                (Scr->StdCmapInfo.mru->maps[Scr->StdCmapInfo.mruindex].colormap ==
196		                 cmap)) {
197			stdcmap = &(Scr->StdCmapInfo.mru->maps[Scr->StdCmapInfo.mruindex]);
198		}
199		else {
200			StdCmap *sc;
201
202			for(sc = Scr->StdCmapInfo.head; sc; sc = sc->next) {
203				int i;
204
205				for(i = 0; i < sc->nmaps; i++) {
206					if(sc->maps[i].colormap == cmap) {
207						Scr->StdCmapInfo.mru = sc;
208						Scr->StdCmapInfo.mruindex = i;
209						stdcmap = &(sc->maps[i]);
210						goto gotit;
211					}
212				}
213			}
214		}
215
216gotit:
217		if(stdcmap) {
218			color.pixel = (stdcmap->base_pixel +
219			               ((Pixel)(((float)color.red / 65535.0) *
220			                        stdcmap->red_max + 0.5) *
221			                stdcmap->red_mult) +
222			               ((Pixel)(((float)color.green / 65535.0) *
223			                        stdcmap->green_max + 0.5) *
224			                stdcmap->green_mult) +
225			               ((Pixel)(((float)color.blue  / 65535.0) *
226			                        stdcmap->blue_max + 0.5) *
227			                stdcmap->blue_mult));
228		}
229		else {
230			fprintf(stderr, "%s:  unable to allocate color \"%s\"\n",
231			        ProgramName, name);
232			return;
233		}
234	}
235
236	*what = color.pixel;
237	return;
238}
239
240
241/**
242 * Try and create a 'shaded' version of a color for prettier UI.
243 */
244void
245GetShadeColors(ColorPair *cp)
246{
247	XColor      xcol;
248	Colormap    cmap = Scr->RootColormaps.cwins[0]->colormap->c;
249	bool        save;
250	float       clearfactor;
251	float       darkfactor;
252	char        clearcol [32], darkcol [32];
253
254	// If we have no valid X connection (generally means a --cfgchk or
255	// similar run; wont' happen in normal operations), just stub out.
256	if(dpy == NULL) {
257		cp->shadc = 0;
258		cp->shadd = 0;
259		return;
260	}
261
262	clearfactor = (float) Scr->ClearShadowContrast / 100.0;
263	darkfactor  = (100.0 - (float) Scr->DarkShadowContrast)  / 100.0;
264	xcol.pixel = cp->back;
265	XQueryColor(dpy, cmap, &xcol);
266
267	sprintf(clearcol, "#%04x%04x%04x",
268	        xcol.red   + (unsigned short)((65535 -   xcol.red) * clearfactor),
269	        xcol.green + (unsigned short)((65535 - xcol.green) * clearfactor),
270	        xcol.blue  + (unsigned short)((65535 -  xcol.blue) * clearfactor));
271	sprintf(darkcol,  "#%04x%04x%04x",
272	        (unsigned short)(xcol.red   * darkfactor),
273	        (unsigned short)(xcol.green * darkfactor),
274	        (unsigned short)(xcol.blue  * darkfactor));
275
276	save = Scr->FirstTime;
277	Scr->FirstTime = true;
278	GetColor(Scr->Monochrome, &cp->shadc, clearcol);
279	GetColor(Scr->Monochrome, &cp->shadd,  darkcol);
280	Scr->FirstTime = save;
281}
282
283
284
285/*
286 * Various font utils
287 */
288/**
289 * Try adjusting a font's height.  Used in drawing the icon manager.
290 */
291bool
292UpdateFont(MyFont *font, int height)
293{
294	int prev = font->avg_height;
295	font->avg_fheight = (font->avg_fheight * font->avg_count + height)
296	                    / (font->avg_count + 1);
297	font->avg_count++;
298	/* Arbitrary limit.  */
299	if(font->avg_count >= 256) {
300		font->avg_count = 256;
301	}
302	font->avg_height = (int)(font->avg_fheight + 0.5);
303	/* fprintf (stderr, "Updating avg with %d(%d) + %d -> %d(%f)\n",
304	 *       prev, font->avg_count, height,
305	 *       font->avg_height, font->avg_fheight); */
306	return (prev != font->avg_height);
307}
308
309
310/**
311 * Load up fontsets from the X server.  Only used by CreateFonts() below.
312 */
313static void
314GetFont(MyFont *font)
315{
316	char *deffontname = "fixed,*";
317	char **missing_charset_list_return;
318	int missing_charset_count_return;
319	char *def_string_return;
320	XFontSetExtents *font_extents;
321	XFontStruct **xfonts;
322	char **font_names;
323	int i;
324	int ascent;
325	int descent;
326	int fnum;
327	char *basename2;
328
329	// In special cases where we have no dpy, I don't think we're going
330	// to need details here, so just leave things untouched.  We may need
331	// to stub in some magic values; deal with that when we run into the
332	// case.
333	if(dpy == NULL) {
334		return;
335	}
336
337	if(font->font_set != NULL) {
338		XFreeFontSet(dpy, font->font_set);
339	}
340
341	asprintf(&basename2, "%s,*", font->basename);
342	if((font->font_set = XCreateFontSet(dpy, basename2,
343	                                    &missing_charset_list_return,
344	                                    &missing_charset_count_return,
345	                                    &def_string_return)) == NULL) {
346		fprintf(stderr, "Failed to get fontset %s\n", basename2);
347		if(Scr->DefaultFont.basename) {
348			deffontname = Scr->DefaultFont.basename;
349		}
350		if((font->font_set = XCreateFontSet(dpy, deffontname,
351		                                    &missing_charset_list_return,
352		                                    &missing_charset_count_return,
353		                                    &def_string_return)) == NULL) {
354			fprintf(stderr, "%s:  unable to open fonts \"%s\" or \"%s\"\n",
355			        ProgramName, font->basename, deffontname);
356			exit(1);
357		}
358	}
359	free(basename2);
360	font_extents = XExtentsOfFontSet(font->font_set);
361
362	fnum = XFontsOfFontSet(font->font_set, &xfonts, &font_names);
363	for(i = 0, ascent = 0, descent = 0; i < fnum; i++) {
364		ascent = MaxSize(ascent, (*xfonts)->ascent);
365		descent = MaxSize(descent, (*xfonts)->descent);
366		xfonts++;
367	}
368
369	font->height = font_extents->max_logical_extent.height;
370	font->y = ascent;
371	font->ascent = ascent;
372	font->descent = descent;
373	font->avg_height = 0;
374	font->avg_fheight = 0.0;
375	font->avg_count = 0;
376}
377
378
379/**
380 * Load up our various defined fonts
381 */
382void
383CreateFonts(ScreenInfo *scr)
384{
385#define LOADFONT(fld) (GetFont(&scr->fld##Font))
386	LOADFONT(TitleBar);
387	LOADFONT(Menu);
388	LOADFONT(Icon);
389	LOADFONT(Size);
390	LOADFONT(IconManager);
391	LOADFONT(Default);
392	LOADFONT(workSpaceMgr.window);
393#undef LOADFONT
394
395	scr->HaveFonts = true;
396}
397
398
399
400#if 0
401static void move_to_head(TwmWindow *t)
402{
403	if(t == NULL) {
404		return;
405	}
406	if(Scr->FirstWindow == t) {
407		return;
408	}
409
410	/* Unlink t from current position */
411	if(t->prev) {
412		t->prev->next = t->next;
413	}
414	if(t->next) {
415		t->next->prev = t->prev;
416	}
417
418	/* Re-link t at head */
419	t->next = Scr->FirstWindow;
420	if(Scr->FirstWindow != NULL) {
421		Scr->FirstWindow->prev = t;
422	}
423	t->prev = NULL;
424	Scr->FirstWindow = t;
425}
426
427/*
428 * Moves window 't' after window 'after'.
429 *
430 * If 'after' == NULL, puts it at the head.
431 * If 't' == NULL, does nothing.
432 * If the 't' is already after 'after', does nothing.
433 */
434
435void move_to_after(TwmWindow *t, TwmWindow *after)
436{
437	if(after == NULL) {
438		move_to_head(t);
439		return;
440	}
441	if(t == NULL) {
442		return;
443	}
444	if(after->next == t) {
445		return;
446	}
447
448	/* Unlink t from current position */
449	if(t->prev) {
450		t->prev->next = t->next;
451	}
452	if(t->next) {
453		t->next->prev = t->prev;
454	}
455
456	/* Re-link t after 'after' */
457	t->next = after->next;
458	if(after->next) {
459		after->next->prev = t;
460	}
461	t->prev = after;
462	after->next = t;
463}
464#endif
465
466
467
468/**
469 * Backend for f.rescuewindows
470 */
471void RescueWindows(void)
472{
473	TwmWindow *twm_win = Scr->FirstWindow;
474
475	while(twm_win) {
476		VirtualScreen *vs = twm_win->vs;
477		if(vs) {
478			/*
479			 * Check if this window seems completely out of sight.
480			 */
481			int x = twm_win->frame_x;
482			int y = twm_win->frame_y;
483			int w = twm_win->frame_width;
484			int h = twm_win->frame_height;
485			int bw = twm_win->frame_bw;
486			int fullw = w + 2 * bw;
487			int fullh = h + 2 * bw;
488			int old_x = x, old_y = y;
489			struct Icon *i;
490
491#define MARGIN  20
492
493			if(x >= vs->w - MARGIN) {
494				x = vs->w - fullw;
495			}
496			if(y >= vs->h - MARGIN) {
497				y = vs->h - fullh;
498			}
499			if((x + fullw <= MARGIN)) {
500				x = 0;
501			}
502			if((y + fullh <= MARGIN)) {
503				y = 0;
504			}
505
506			if(x != old_x || y != old_y) {
507				SetupWindow(twm_win, x, y, w, h, -1);
508			}
509
510			/*
511			 * If there is an icon, check it too.
512			 */
513			i = twm_win->icon;
514			if(i != NULL) {
515				x = i->w_x;
516				y = i->w_y;
517				w = i->w_width;
518				h = i->w_height;
519				old_x = x;
520				old_y = y;
521
522				if(x >= vs->w - MARGIN) {
523					x = vs->w - w;
524				}
525				if(y >= vs->h - MARGIN) {
526					y = vs->h - h;
527				}
528				if((x + w <= MARGIN)) {
529					x = 0;
530				}
531				if((y + h <= MARGIN)) {
532					y = 0;
533				}
534
535				if(x != old_x || y != old_y) {
536					XMoveWindow(dpy, i->w, x, y);
537					i->w_x = x;
538					i->w_y = y;
539				}
540			}
541#undef MARGIN
542		}
543		twm_win = twm_win->next;
544	}
545}
546
547
548
549/**
550 * Backend for f.trace
551 */
552void
553DebugTrace(char *file)
554{
555	if(!file) {
556		return;
557	}
558	if(tracefile) {
559		fprintf(stderr, "stop logging events\n");
560		if(tracefile != stderr) {
561			fclose(tracefile);
562		}
563		tracefile = NULL;
564	}
565	else {
566		if(strcmp(file, "stderr")) {
567			tracefile = fopen(file, "w");
568		}
569		else {
570			tracefile = stderr;
571		}
572		fprintf(stderr, "logging events to : %s\n", file);
573	}
574}
575
576
577
578/*
579 * A safe strncpy(), which always ensures NUL-termination.
580 *
581 * XXX This is really just a slightly pessimized implementation of
582 * strlcpy().  Maybe we should use that instead, with a local
583 * implementation for systems like glibc-users that lack it?
584 */
585void
586safe_strncpy(char *dest, const char *src, size_t size)
587{
588	strncpy(dest, src, size - 1);
589	dest[size - 1] = '\0';
590}
591