freebsd-dev/games/x11/xroach/xroach.c

603 lines
12 KiB
C
Raw Normal View History

/*
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;
}