603 lines
12 KiB
C
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;
|
||
|
}
|
||
|
|