[comp.sources.amiga] placewindow.c

doc@s.cc.purdue.edu (Craig Norborg) (06/22/87)

    Here is the source to placewindow as promised by Mike Meyer.  If you
wish for this to compile using AZTEC, you must use the -DAZTEC switch
while compiling.  I did the AZTEC port, and do not totally guarantee it,
but it seems to work just fine.  Without the -DAZTEC, your running virgin
mwm code.
    -Craig Norborg

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:	Shell Archiver
#	Run the following text with /bin/sh to create:
#	placewindow.c
# This archive created: Mon Jun 22 01:57:53 1987
# By:	Craig Norborg (Purdue University Computing Center)
cat << \SHAR_EOF > placewindow.c
/*
 * placewindow - starts a program, and make the window look like you want it
 * to, not like the person who wrote the program wants it to.
 *
 * Copyright (c) 1987, Mike Meyer
 * This program can be redistributed freely, under two conditions:
 *	1) The source must be part of the distribution.
 *	2) This copyright notice must stay attached to the source.
 *
 * usage: placewindow windowname geometry [ command ]
 *
 *	windowname is a proper prefix of the title for the window
 *		that we're going to change.
 *
 *	geometry describes where the window should be, and how it should
 *		look. This is an enhanced version of the con: syntax for
 *		specifying where a window should go. The full spec is
 *		X/Y/W/H/e. Postive X (Y) specifies the offset from the
 *		left (top) edge of the screen to the left (top) edge of
 *		the window. Negative X (Y) specifes the offset from the
 *		right (bottom) edge of the screen to the right (bottom)
 *		edge of the window. W and H are the width and height of
 *		the window, respectively. Zero values for those means to
 *		make the window as long as possible in that direction.
 *		Obviously, you can't specify -X and 0 W, or -Y and 0 H.
 *		The "e", if present, means that the maximum sizes for the
 *		window should be set to -1 (any value you can mouse to).
 *		Nearly any piece of this can be left off. If the value is
 *		not specified (i.e., you didn't put in enough geometry to
 *		reach it) then it is left unmodified. If you actually put
 *		in the gemoetry specification, but don't put in a number
 *		(i.e. "////") then the missing values are treated as zero.
 *
 *		Simple examples: "////", "////e" and "-////" all specify
 *		max-sized windows. The "e" makes the max-sized window the
 *		screen size, and makes the window that big. "-" positions the
 *		window against the right edge of the screen if it's smaller
 *		than the screen. '10////', '10////', '10////e' and '10////e'
 *		all specify windows that are max-height, and stretch as far
 *		as they can from 10 pixels to the right of the left edge of
 *		the screen. The two with the "e" make the max-size window
 *		the screen size. The first two are synonyms, as are the
 *		last two. '10//100e' will be a window that start 10 pixels
 *		to the right of the left edge of the screen, is 100 pixels
 *		wide, and stretches from the top to the bottom of the
 *		screen. '10/10//100e' will be a window with it's upper left
 *		corner at 10, 10, is 100 pixels high, and stretches from
 *		the left to the right edge of the screen. '10/10/100/100e'
 *		and '10/10/100/100/e' both describe a 100 by 100 window with
 *		it's upper left corner at 10, 10 that has had it's maximum
 *		window sizes changed to the screen sizes. For leaving things
 *		off, '3/10' and '3/10e' would be a window the same size as
 *		the default, at location 3, 10. Note that '///' and '///e' are
 *		windows at 0, 0 with max-size X, and Y left alone, but '////'
 *		and '////e' are full-screen windows. Finally, a lone "e" will
 *		leave the window alone, but change it's maximum size but
 *		otherwise leave it alone.
 *
 *	command consists of all following arguments, and is a command to be
 *		executed before we try resizing the window. If command is
 *		given, we'll try to find the window at 1-second intervals
 *		one minute (roughly), so that command can create the window.
 *		If command isn't specified, we just look for the window
 *		once.
 *
 *	Warning: Negative W and H values in the geometry aren't supported,
 *		and I ain't going to say what happens if you use them.
 *		Sanity checks are done on the size/position. Rather than
 *		abort and have you try again, it trys to do something
 *		sane. Improvements on this code are welcome.
 */

#include <stdio.h>
#ifndef AZTEC
#include <string.h>
#include <dos.h>
#else
#include <ctype.h>
#endif
#include <libraries/dos.h>
#include <exec/types.h>
#include <intuition/intuitionbase.h>

/*
 * Declare names for the error returns.
 */
#define	OK		0
#define ARG_ERROR	100
#define NO_INTUITION	200
#define NO_MEMORY	300
#define COMMAND_ERROR	400
#define	NO_WINDOW	500
#define	BAD_ARGS	600

struct IntuitionBase *IntuitionBase;

void
main(argc, argv) char **argv; {
	register short		counter, length, width, height, x, y ;
	register char		*out, *in, *geometry ;
	register struct Window	*w ;
	register struct Screen	*s ;
	long			lock ;
	short			xminusp, yminusp ;
	char			*command ;

/*
 * Error processing - we need at least two args + our name
 */
	if (argc < 3) {
		fprintf(stderr,
			"usage: %s windowname geometry [ command ... ]\n",
			argv[0]) ;
		exit(ARG_ERROR) ;
		}
/*
 * If only have two arguments, then it's a window name and a geomtry.
 * We're only going to try once, so set the counter for one try.
 * Otherwise, we need to run the command, and retry 60 times (for up
 * to a minute.
 */
	if (argc == 3) counter = 1 ;
	else {
/*
 * We want to run the command first, so that it has as much time as possible
 * to get the window open before we go looking for it. To do that, we need to
 * turn the args list into a string for execution. Step one is to count
 * the total number of characters, with one extra for pad.
 */
		for (length = 0, counter = 3; counter < argc; counter++)
			length += strlen(argv[counter]) + 1 ;
/*
 * Step two is to get space for the command to reside in.
 */
		if ((command = (char *) malloc(length + 4)) == NULL) {
			fprintf(stderr,
				"%s: Couldn't get %d bytes", argv[0], length) ;
			exit(NO_MEMORY) ;
			}
/*
 * Step three is to put the "run " into the buffer.
 */
		(void) strcpy(command, "run ") ;
/*
 * And step four is to copy the strings into it. 
 */
		for (out = &command[4], counter = 3; counter < argc; counter++){
			for (in = argv[counter]; *in;)
				*out++ = *in++ ;
			*out++ = ' ' ;
			}
		*out = '\0' ;
/*
 * Now, just execute the rest of the args as a command.
 */
		if (!Execute(command, 0, 0)) {
			fprintf(stderr, "%s: %s failed\n", argv[0], argv[3]) ;
			exit(COMMAND_ERROR) ;
			}
		counter = 60 ;
		}
/*
 * Parsing the geometry turned out to be the nasty part of this program,
 * probably because I tried to do it ad hoc. It took almost no time after
 * I sat down and wrote out the bnf. Which is:
 *
 *	geometry ::= spec flag
 *	flag ::= 'e' | '/' 'e' | nil
 *	spec ::=  pos_spec
 *		| pos_spec '/' pos_spec
 *		| pos_spec '/' pos_spec '/' dim_spec
 *		| pos_spec '/' pos_spec '/' dim_spec '/' dim_spec
 *	pos_spec ::= number | '-' | '-' number | nil
 *	dim_spec ::= number | nil
 *	number ::= a string of digits, of course.
 *	nil ::= an empty string
 *
 * Following that, parsing is easy. First, set up the defaults values
 * if things are missing. Then, for the two pos_spec pieces, check
 * for a minus sign. If it's there, bump the pointer and note that this
 * is a negative value. For all the pieces, you then check to see
 * if you've come to the flags part (end of string or a 'e'), and
 * drop to finished parsing if so. Otherwise, you translate the string
 * of digits to a number (an empty string is 0), and check to see if
 * there's a trailing '/' that needs to be skipped. Then on to the
 * next piece. At the end of all of this, the pointer is left pointing
 * to any 'e' that may be there.
 */
	geometry = argv[2] ;
	x = y = width = height = -1 ;
	xminusp = yminusp = FALSE ;

/* X */	if (*geometry == '-') {
		geometry += 1 ;
		xminusp = TRUE ;
		}
	if (*geometry == '\0' || *geometry == 'e' || *geometry == 'E')
		goto parse_over ;
	x = atoi(geometry) ;
#ifndef AZTEC
	geometry += strspn(geometry, "0123456789") ;
#else
	while (isdigit(*geometry))
		++geometry;
#endif
	if (*geometry == '/') geometry += 1 ;

/* Y */	if (*geometry == '-') {
		geometry += 1 ;
		yminusp = TRUE ;
		}
	if (*geometry == '\0' || *geometry == 'e' || *geometry == 'E')
		goto parse_over ;
	y = atoi(geometry) ;
#ifndef AZTEC
	geometry += strspn(geometry, "0123456789") ;
#else
	while (isdigit(*geometry))
		++geometry;
#endif
	if (*geometry == '/') geometry += 1 ;

/* W */	if (*geometry == '\0' || *geometry == 'e' || *geometry == 'E')
		goto parse_over ;
	width = atoi(geometry) ;
#ifndef AZTEC
	geometry += strspn(geometry, "0123456789") ;
#else
	while (isdigit(*geometry))
		++geometry;
#endif
	if (*geometry == '/') geometry += 1 ;

/* H */	if (*geometry == '\0' || *geometry == 'e' || *geometry == 'E')
		goto parse_over ;
	height = atoi(geometry) ;
#ifndef AZTEC
	geometry += strspn(geometry, "0123456789") ;
#else
	while (isdigit(*geometry))
		++geometry;
#endif
	if (*geometry == '/') geometry += 1 ;

parse_over:
/*
 * Fix the case on the flag.
 */
	if (*geometry == 'E') *geometry = 'e' ;
/*
 * Now, verify that we didn't get both negative offsets & unspecified
 * dimensions.
 */
	if ((xminusp && width == 0) || (yminusp && height == 0)) {
		fprintf(stderr, "%s: Bad geometry %s, to many defaults!\n",
			argv[0], argv[2]) ;
		exit(BAD_ARGS) ;
		}
/*
 * Now, get IntuitionBase so we can find the window we need.
 */
	if ((IntuitionBase = (struct IntuitionBase *)
	    OpenLibrary("intuition.library", 0)) == NULL) {
		fprintf(stderr, "%s: Can't open intuition\n", argv[0]) ;
		exit(NO_INTUITION) ;
		}
/*
 * We need to know how much of the window title to check.
 */
	length = strlen(argv[1]) ;
/*
 * Now, until we find the window, or have tried for over a minute,
 * keep trying to find the window we're looking for.
 */
	for (;;) {
		lock = LockIBase(0L) ;
		for (s = IntuitionBase->FirstScreen; s; s = s->NextScreen)
			for (w = s->FirstWindow; w; w = w->NextWindow)
				if (strncmp(w->Title, argv[1], length) ==  0)
					goto found_it ;
		UnlockIBase(lock) ;
		if (--counter) Delay(TICKS_PER_SECOND) ;
		else break ;
		} ;
/*
 * We never found the window. Clean up, and go tell the user.
 * go.
 */
	fprintf(stderr, "%s: Couldn't find window %s\n", argv[0], argv[1]) ;
	CloseLibrary(IntuitionBase) ;
	exit(NO_WINDOW) ;
/*
 * Find it. The rest of this block deals with fixing the window sizing
 * to be reasonable, and actually doing the sizing.
 */
found_it:
	if (*geometry == 'e') WindowLimits(w, 0, 0, -1, -1) ;
/*
 * Get absolute width & height values.
 */
	if (width == 0) width = s->Width - x ;
	else if (width == -1) width = w->Width ;
	if (height == 0) height = s->Height - y ;
	else if (height == -1) height = w->Height ;
/*
 * and sanity check them.
 */
	if (width > w->MaxWidth) width = w->MaxWidth ;
	if (width > s->Width) width = s->Width ;
	if (width < w->MinWidth) width = w->MinWidth ;
	if (height > w->MaxHeight) height = w->MaxHeight ;
	if (height > s->Height) height = s->Height ;
	if (height < w->MinHeight) height = w->MinHeight ;
/*
 * find absolute x & y values.
 */
	if (xminusp) x = s->Width - width - x ;
	else if (x == -1) x = w->LeftEdge ;
	if (yminusp) y = s->Height - height - y ;
	else if (y == -1) y = w->TopEdge ;
/*
 * now sanity check them.
 */
	if (x >= s->Width || x < 0) x = 0 ;
	if (y >= s->Height || y < 0) y = 0 ;
/*
 * Now sanity check the whole
 * package.
 */
	if (x + width > s->Width) x = s->Width - width ;
	if (y + height > s->Height) y = s->Height - height ;
/*
 * Now we can set the sizes.
 */
	SizeWindow(w, width - w->Width, height - w->Height) ;
	MoveWindow(w, x - w->LeftEdge, y - w->TopEdge) ;
/*
 * And clean up and exit.
 */
	UnlockIBase(lock) ;
	CloseLibrary(IntuitionBase) ;
	exit(OK) ;
	}
SHAR_EOF
#	End of shell archive
exit 0