freebsd-dev/games/x11/xroach/xroach.c
Jordan K. Hubbard 554eb505f8 Bring in the 4.4 Lite games directory, modulo man page changes and segregation
of the x11 based games.  I'm not going to tag the originals with bsd_44_lite
and do this in two stages since it's just not worth it for this collection,
and I've got directory renames to deal with that way.  Bleah.
Submitted by:	jkh
1994-09-04 04:03:31 +00:00

603 lines
12 KiB
C

/*
Xroach - A game of skill. Try to find the roaches under your windows.
Copyright 1991 by J.T. Anderson
jta@locus.com
This program may be freely distributed provided that all
copyright notices are retained.
To build:
cc -o xroach roach.c -lX11 [-lsocketorwhatever] [-lm] [-l...]
Dedicated to Greg McFarlane. (gregm@otc.otca.oz.au)
*/
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <stdio.h>
#include <math.h>
#include <signal.h>
#include <stdlib.h>
char Copyright[] = "Xroach\nCopyright 1991 J.T. Anderson";
#include "roachmap.h"
typedef unsigned long Pixel;
typedef int ErrorHandler();
#define SCAMPER_EVENT (LASTEvent + 1)
#if !defined(GRAB_SERVER)
#define GRAB_SERVER 0
#endif
Display *display;
int screen;
Window rootWin;
unsigned int display_width, display_height;
int center_x, center_y;
GC gc;
char *display_name = NULL;
Pixel black, white;
int done = 0;
int eventBlock = 0;
int errorVal = 0;
typedef struct Roach {
RoachMap *rp;
int index;
float x;
float y;
int intX;
int intY;
int hidden;
int turnLeft;
int steps;
} Roach;
Roach *roaches;
int maxRoaches = 10;
int curRoaches = 0;
float roachSpeed = 20.0;
Region rootVisible = NULL;
void Usage();
void SigHandler();
void AddRoach();
void MoveRoach();
void DrawRoaches();
void CoverRoot();
int CalcRootVisible();
int MarkHiddenRoaches();
Pixel AllocNamedColor();
void
main(ac, av)
int ac;
char *av[];
{
XGCValues xgcv;
int ax;
char *arg;
RoachMap *rp;
int rx;
float angle;
XEvent ev;
char *roachColor = "black";
int nVis;
int needCalc;
/*
Process command line options.
*/
for (ax=1; ax<ac; ax++) {
arg = av[ax];
if (strcmp(arg, "-display") == 0) {
display_name = av[++ax];
}
else if (strcmp(arg, "-rc") == 0) {
roachColor = av[++ax];
}
else if (strcmp(arg, "-speed") == 0) {
roachSpeed = atof(av[++ax]);
}
else if (strcmp(arg, "-roaches") == 0) {
maxRoaches = strtol(av[++ax], (char **)NULL, 0);
}
else {
Usage();
}
}
srand((int)time((long *)NULL));
/*
Catch some signals so we can erase any visible roaches.
*/
signal(SIGKILL, SigHandler);
signal(SIGINT, SigHandler);
signal(SIGTERM, SigHandler);
signal(SIGHUP, SigHandler);
display = XOpenDisplay(display_name);
if (display == NULL) {
if (display_name == NULL) display_name = getenv("DISPLAY");
(void) fprintf(stderr, "%s: cannot connect to X server %s\n", av[0],
display_name ? display_name : "(default)");
exit(1);
}
screen = DefaultScreen(display);
rootWin = RootWindow(display, screen);
black = BlackPixel(display, screen);
white = WhitePixel(display, screen);
display_width = DisplayWidth(display, screen);
display_height = DisplayHeight(display, screen);
center_x = display_width / 2;
center_y = display_height / 2;
/*
Create roach pixmaps at several orientations.
*/
for (ax=0; ax<360; ax+=ROACH_ANGLE) {
rx = ax / ROACH_ANGLE;
angle = rx * 0.261799387799;
rp = &roachPix[rx];
rp->pixmap = XCreateBitmapFromData(display, rootWin,
rp->roachBits, rp->width, rp->height);
rp->sine = sin(angle);
rp->cosine = cos(angle);
}
roaches = (Roach *)malloc(sizeof(Roach) * maxRoaches);
gc = XCreateGC(display, rootWin, 0L, &xgcv);
XSetForeground(display, gc, AllocNamedColor(roachColor, black));
XSetFillStyle(display, gc, FillStippled);
while (curRoaches < maxRoaches)
AddRoach();
XSelectInput(display, rootWin, ExposureMask | SubstructureNotifyMask);
needCalc = 1;
while (!done) {
if (XPending(display)) {
XNextEvent(display, &ev);
}
else {
if (needCalc) {
needCalc = CalcRootVisible();
}
nVis = MarkHiddenRoaches();
if (nVis) {
ev.type = SCAMPER_EVENT;
}
else {
DrawRoaches();
eventBlock = 1;
XNextEvent(display, &ev);
eventBlock = 0;
}
}
switch (ev.type) {
case SCAMPER_EVENT:
for (rx=0; rx<curRoaches; rx++) {
if (!roaches[rx].hidden)
MoveRoach(rx);
}
DrawRoaches();
XSync(display, False);
break;
case Expose:
case MapNotify:
case UnmapNotify:
case ConfigureNotify:
needCalc = 1;
break;
}
}
CoverRoot();
XCloseDisplay(display);
}
#define USEPRT(msg) fprintf(stderr, msg)
void
Usage()
{
USEPRT("Usage: xroach [options]\n\n");
USEPRT("Options:\n");
USEPRT(" -display displayname\n");
USEPRT(" -rc roachcolor\n");
USEPRT(" -roaches numroaches\n");
USEPRT(" -speed roachspeed\n");
exit(1);
}
void
SigHandler()
{
/*
If we are blocked, no roaches are visible and we can just bail
out. If we are not blocked, then let the main procedure clean
up the root window.
*/
if (eventBlock) {
XCloseDisplay(display);
exit(0);
}
else {
done = 1;
}
}
/*
Generate random integer between 0 and maxVal-1.
*/
int
RandInt(maxVal)
int maxVal;
{
return rand() % maxVal;
}
/*
Check for roach completely in specified rectangle.
*/
int
RoachInRect(roach, rx, ry, x, y, width, height)
Roach *roach;
int rx;
int ry;
int x;
int y;
unsigned int width;
unsigned int height;
{
if (rx < x) return 0;
if ((rx + roach->rp->width) > (x + width)) return 0;
if (ry < y) return 0;
if ((ry + roach->rp->height) > (y + height)) return 0;
return 1;
}
/*
Check for roach overlapping specified rectangle.
*/
int
RoachOverRect(roach, rx, ry, x, y, width, height)
Roach *roach;
int rx;
int ry;
int x;
int y;
unsigned int width;
unsigned int height;
{
if (rx >= (x + width)) return 0;
if ((rx + roach->rp->width) <= x) return 0;
if (ry >= (y + height)) return 0;
if ((ry + roach->rp->height) <= y) return 0;
return 1;
}
/*
Give birth to a roach.
*/
void
AddRoach()
{
Roach *r;
if (curRoaches < maxRoaches) {
r = &roaches[curRoaches++];
r->index = RandInt(ROACH_HEADINGS);
r->rp = &roachPix[r->index];
r->x = RandInt(display_width - r->rp->width);
r->y = RandInt(display_height - r->rp->height);
r->intX = -1;
r->intY = -1;
r->hidden = 0;
r->steps = RandInt(200);
r->turnLeft = RandInt(100) >= 50;
}
}
/*
Turn a roach.
*/
void
TurnRoach(roach)
Roach *roach;
{
if (roach->index != (roach->rp - roachPix)) return;
if (roach->turnLeft) {
roach->index += (RandInt(30) / 10) + 1;
if (roach->index >= ROACH_HEADINGS)
roach->index -= ROACH_HEADINGS;
}
else {
roach->index -= (RandInt(30) / 10) + 1;
if (roach->index < 0)
roach->index += ROACH_HEADINGS;
}
}
/*
Move a roach.
*/
void
MoveRoach(rx)
int rx;
{
Roach *roach;
Roach *r2;
float newX;
float newY;
int ii;
roach = &roaches[rx];
newX = roach->x + (roachSpeed * roach->rp->cosine);
newY = roach->y - (roachSpeed * roach->rp->sine);
if (RoachInRect(roach, (int)newX, (int)newY,
0, 0, display_width, display_height)) {
roach->x = newX;
roach->y = newY;
if (roach->steps-- <= 0) {
TurnRoach(roach);
roach->steps = RandInt(200);
}
for (ii=rx+1; ii<curRoaches; ii++) {
r2 = &roaches[ii];
if (RoachOverRect(roach, (int)newX, (int)newY,
r2->intX, r2->intY, r2->rp->width, r2->rp->height)) {
TurnRoach(roach);
}
}
}
else {
TurnRoach(roach);
}
}
/*
Draw all roaches.
*/
void
DrawRoaches()
{
Roach *roach;
int rx;
for (rx=0; rx<curRoaches; rx++) {
roach = &roaches[rx];
if (roach->intX >= 0) {
XClearArea(display, rootWin, roach->intX, roach->intY,
roach->rp->width, roach->rp->height, False);
}
}
for (rx=0; rx<curRoaches; rx++) {
roach = &roaches[rx];
if (!roach->hidden) {
roach->intX = roach->x;
roach->intY = roach->y;
roach->rp = &roachPix[roach->index];
XSetStipple(display, gc, roach->rp->pixmap);
XSetTSOrigin(display, gc, roach->intX, roach->intY);
XFillRectangle(display, rootWin, gc,
roach->intX, roach->intY, roach->rp->width, roach->rp->height);
}
else {
roach->intX = -1;
}
}
}
/*
Cover root window to erase roaches.
*/
void
CoverRoot()
{
XSetWindowAttributes xswa;
long wamask;
Window roachWin;
xswa.background_pixmap = ParentRelative;
xswa.override_redirect = True;
wamask = CWBackPixmap | CWOverrideRedirect;
roachWin = XCreateWindow(display, rootWin, 0, 0,
display_width, display_height, 0, CopyFromParent,
InputOutput, CopyFromParent, wamask, &xswa);
XLowerWindow(display, roachWin);
XMapWindow(display, roachWin);
XFlush(display);
}
#if !GRAB_SERVER
int
RoachErrors(dpy, err)
Display *dpy;
XErrorEvent *err;
{
errorVal = err->error_code;
return 0;
}
#endif /* GRAB_SERVER */
/*
Calculate Visible region of root window.
*/
int
CalcRootVisible()
{
Region covered;
Region visible;
Window *children;
int nChildren;
Window dummy;
XWindowAttributes wa;
int wx;
XRectangle rect;
int winX, winY;
unsigned int winHeight, winWidth;
unsigned int borderWidth;
unsigned int depth;
/*
If we don't grab the server, the XGetWindowAttribute or XGetGeometry
calls can abort us. On the other hand, the server grabs can make for
some annoying delays.
*/
#if GRAB_SERVER
XGrabServer(display);
#else
XSetErrorHandler(RoachErrors);
#endif
/*
Get children of root.
*/
XQueryTree(display, rootWin, &dummy, &dummy, &children, &nChildren);
/*
For each mapped child, add the window rectangle to the covered
region.
*/
covered = XCreateRegion();
for (wx=0; wx<nChildren; wx++) {
if (XEventsQueued(display, QueuedAlready)) return 1;
errorVal = 0;
XGetWindowAttributes(display, children[wx], &wa);
if (errorVal) continue;
if (wa.map_state == IsViewable) {
XGetGeometry(display, children[wx], &dummy, &winX, &winY,
&winWidth, &winHeight, &borderWidth, &depth);
if (errorVal) continue;
rect.x = winX;
rect.y = winY;
rect.width = winWidth + (borderWidth * 2);
rect.height = winHeight + (borderWidth * 2);
XUnionRectWithRegion(&rect, covered, covered);
}
}
XFree((char *)children);
#if GRAB_SERVER
XUngrabServer(display);
#else
XSetErrorHandler((ErrorHandler *)NULL);
#endif
/*
Subtract the covered region from the root window region.
*/
visible = XCreateRegion();
rect.x = 0;
rect.y = 0;
rect.width = display_width;
rect.height = display_height;
XUnionRectWithRegion(&rect, visible, visible);
XSubtractRegion(visible, covered, visible);
XDestroyRegion(covered);
/*
Save visible region globally.
*/
if (rootVisible)
XDestroyRegion(rootVisible);
rootVisible = visible;
/*
Mark all roaches visible.
*/
for (wx=0; wx<curRoaches; wx++)
roaches[wx].hidden = 0;
return 0;
}
/*
Mark hidden roaches.
*/
int
MarkHiddenRoaches()
{
int rx;
Roach *r;
int nVisible;
nVisible = 0;
for (rx=0; rx<curRoaches; rx++) {
r = &roaches[rx];
if (!r->hidden) {
if (r->intX > 0 && XRectInRegion(rootVisible, r->intX, r->intY,
r->rp->width, r->rp->height) == RectangleOut) {
r->hidden = 1;
}
else {
nVisible++;
}
}
}
return nVisible;
}
/*
Allocate a color by name.
*/
Pixel
AllocNamedColor(colorName, dfltPix)
char *colorName;
Pixel dfltPix;
{
Pixel pix;
XColor scrncolor;
XColor exactcolor;
if (XAllocNamedColor(display, DefaultColormap(display, screen),
colorName, &scrncolor, &exactcolor)) {
pix = scrncolor.pixel;
}
else {
pix = dfltPix;
}
return pix;
}