Home | History | Annotate | Line # | Download | only in dist
      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 */
     59 FILE *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  */
     67 char *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  */
    130 char *
    131 ExpandFilename(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  */
    153 void
    154 GetColor(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 
    216 gotit:
    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  */
    244 void
    245 GetShadeColors(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  */
    291 bool
    292 UpdateFont(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  */
    313 static void
    314 GetFont(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  */
    382 void
    383 CreateFonts(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
    401 static 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 
    435 void 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  */
    471 void 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  */
    552 void
    553 DebugTrace(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  */
    585 void
    586 safe_strncpy(char *dest, const char *src, size_t size)
    587 {
    588 	strncpy(dest, src, size - 1);
    589 	dest[size - 1] = '\0';
    590 }
    591