mcgrew@aramis.rutgers.edu (Charles Mcgrew) (05/06/89)
Submitted-by: kent@wsl.dec.com Posting-number: Volume 1, Issue 2 Archive-name: mazewar/part05 #!/bin/sh sed 's/^X//' >./winSunView.c << 'xxFUNNYxx' X/* $Header$ */ X X/* X * window_sunview.c - Window-system specific routines X * X * Author: Bob Brown X * RIACS X * NASA Ames Research Center X * Date: Thu Dec 5 1986 X * Copyright (c) 1986 Robert L. Brown X * X * Revised by: Malcolm Slaney (@ Schlumberger Palo Alto Research) X * August 1987 and January 1988 X * X */ X X/* X * It is intended that this file presents a window system-independent X * set of routines to MazeWar; that is, if you want to run MW under X * another window system, this is the only file that needs to be X * changed. It isn't yet clear how well I've succeeded, but it's a start. X * X * Routines that are presented to the outside are capitalized; local X * routines are not. X * X * This file is for Sun 3.1 Sunview. It has also been tested under Sun 3.4. X */ X X/* X * $Log$ X */ X X#ifndef lint Xstatic char rcs_ident[] = "$Header$"; X#endif X X#include <sys/types.h> /* I hate programs that do this... */ X#include <sys/socket.h> /* ... but sometimes you just gotta */ X#include <netinet/in.h> X#include <suntool/sunview.h> X#include <suntool/canvas.h> X#include <stdio.h> X#include "mazewar.h" X X#include <errno.h> X X/* X** These constants define where in the main pixwin the subregions lie X*/ X#define VIEW_X_DIM 400 X#define VIEW_Y_DIM 400 X#define VIEW_X_ORIGIN 100 X#define VIEW_Y_ORIGIN 50 X#define MAZE_X_DIM (MAZEXMAX*16) X#define MAZE_Y_DIM (MAZEYMAX*16) X#define MAZE_X_ORIGIN 48 X#define MAZE_Y_ORIGIN 451 X#define SCORE_X_DIM 192 X#define SCORE_Y_DIM (mwFontHeight * MAXRATS) X#define SCORE_X_ORIGIN 208 X#define SCORE_Y_ORIGIN 708 X#define MIN_X_DIM 608 /* see comments for InitWindow() */ X#define MIN_Y_DIM SCORE_Y_ORIGIN /* see InitWindow() */ X#define MAX_X_DIM MIN_X_DIM X#define MAX_Y_DIM (MIN_Y_DIM + SCORE_Y_DIM) X Xshort mazeBits[MAZEXMAX*MAZEYMAX*16]; /* shorts better be 16 bits! */ XFrame BaseFrame; /* parent frame */ XCanvas BaseCanvas; /* Base canvas */ XPixwin *mwPixwin; /* parent pixel window */ XPixwin *viewPixwin; /* view subwindow */ XPixwin *mazePixwin; /* maze subwindow */ XPixwin *scorePixwin; /* score subwindow */ Xstruct pixrect *mazePixrect; /* Pixrect that has the maze */ Xstruct pixrect *ratsPixrect; /* Pixrect that has the rats */ Xstruct pixfont *mwFont; /* Font being used */ Xint mwFontHeight; /* Height of the font */ XBoolean inverted = FALSE; /* user warned of window exit */ Xint displayFD; /* fd of display for events */ Xint frameFD; /* for handling frame events */ Xint RastOp = PIX_SRC; Xint MaxOffset; /* max vertical font offset from X * baseline */ Xint flashIcon = FALSE; /* Should icon be flashing ? */ X Xstatic short grayBits[16] = { X 0xaaaa, 0x5555, 0xaaaa, 0x5555, X 0xaaaa, 0x5555, 0xaaaa, 0x5555, X 0xaaaa, 0x5555, 0xaaaa, 0x5555, X 0xaaaa, 0x5555, 0xaaaa, 0x5555 X}; X Xstatic short ratBits[] = { X 0x0000,0x0000,0x07C0,0x0FE8,0x0FFC,0x7FFA,0x8FFF,0x8842, X 0x4000,0x3F00,0x0080,0x0100,0x0200,0x0100,0x0000,0x0000 X}; Xmpr_static(ratPixrect, 16, 16, 1, ratBits); X Xstatic short deadRatBits[] = { X 0x0000, 0x2088, 0x1045, 0x0082, X 0x0000, 0x1000, 0x2000, 0x4100, X 0x8220, 0x87ff, 0x8ffa, 0x7ffc, X 0x0fe8, 0x07c0, 0x0000, 0x0000 X}; Xmpr_static(deadRatPixrect, 16, 16, 1, deadRatBits); X Xstatic short coffeeCupBits[] = { X 0x0080, 0x0700, 0x0800, 0x07c0, X 0x0020, 0x07f0, 0x3c48, 0x47f8, X 0x5808, 0x4008, 0x3c08, 0x0818, X 0x37f6, 0x2002, 0x17fc, 0x0000 X}; Xmpr_static(coffeeCupPixrect, 16, 16, 1, coffeeCupBits); X Xstatic short ToolIconBits[] = { X/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16 X */ X 0x8888,0x8888,0x8888,0x8888,0x8800,0x0000,0x0000,0x0008, X 0x2200,0x0000,0x0000,0x0022,0x2284,0x0000,0x0820,0x0022, X 0x8884,0x0000,0x0820,0x0008,0x88CC,0x71F1,0xC827,0x1608, X 0x22CC,0x8812,0x2928,0x9922,0x22B4,0x0822,0x2540,0x9022, X 0x88B4,0x7843,0xE547,0x9008,0x8884,0x8882,0x0548,0x9008, X 0x2284,0x8902,0x2288,0x9022,0x2284,0x79F1,0xC287,0x9022, X 0x8800,0x0000,0x0000,0x0008,0x8800,0x0000,0x0000,0x0008, X 0x2200,0x0000,0x0000,0x0022,0x2222,0x2222,0x2222,0x2222, X 0x8888,0x8888,0x8888,0x8888,0x9FFF,0xFFFF,0xFFFF,0xFFFC, X 0x3800,0x0000,0x0000,0x0006,0x3400,0x0000,0x0000,0x0006, X 0x9200,0x0000,0x0000,0x0004,0x9100,0x0000,0x0000,0x0004, X 0x3080,0x0000,0x0000,0x0006,0x3040,0x0000,0x0000,0x01FE, X 0x9020,0x0000,0x0000,0x0304,0x9010,0x0000,0x0000,0x0504, X 0x3008,0x0000,0x0000,0x0906,0x3004,0x0000,0x0000,0x1106, X 0x9002,0x0000,0x0000,0x2104,0x9001,0x0000,0x0000,0x4104, X 0x3000,0x8000,0x0000,0x8106,0x3000,0x4000,0x0001,0x0106, X 0x9000,0x3FFF,0xFFFE,0x0104,0x9000,0x2000,0x0002,0x0104, X 0x3000,0x2000,0x0002,0x0106,0x3000,0x2000,0x0002,0x0106, X 0x9000,0x2000,0x0002,0x0104,0x9000,0x2000,0x0002,0x0104, X 0x3000,0x2000,0x0002,0x0106,0x3000,0x2000,0x0002,0x0106, X 0x9000,0x2000,0x0002,0x0104,0x9000,0x2000,0x0002,0x0104, X 0x3000,0x2000,0x0002,0x0106,0x3000,0x3FFF,0xFFFE,0x0106, X 0x9000,0x4000,0x0001,0x0104,0x9000,0x8000,0x0000,0x8104, X 0x3001,0x0000,0x0000,0x4106,0x3002,0x0000,0x0000,0x2106, X 0x9004,0x0000,0x0000,0x1104,0x9008,0x0001,0xF000,0x0904, X 0x3010,0x000B,0xF800,0x0506,0x3020,0x001F,0xF800,0x0306, X 0x9040,0x002F,0xFF00,0x01FC,0x9080,0x007F,0xF880,0x0004, X 0x3100,0x0021,0x0880,0x0006,0x3200,0x0000,0x0100,0x0006, X 0x9400,0x0000,0x7E00,0x0004,0x9800,0x0000,0x8000,0x0004, X 0x3000,0x0000,0x4000,0x0006,0x3000,0x0000,0x2000,0x0006, X 0x9FFF,0xFFFF,0xFFFF,0xFFFC,0x8888,0x8888,0x8888,0x8888, X 0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222,0x2222 X}; XDEFINE_ICON_FROM_IMAGE(ToolIcon, ToolIconBits); X Xstatic Cursor ratCursor, deadRatCursor, hourGlassCursor; X Xextern char *getenv(); Xextern int errno; X X/* state between InitWindow() and StartWindow() */ Xstatic char *progName; X X/* X * ALL COORDINATES ASSUME ORIGIN (0,0) AT UPPER LEFT! X */ X X/* X * Initialize the window. The overall window must be at least 608x by X * 808y (the size of the Alto screen). There are three distinct X * subregions: X * A 400x by 400y area for the view, beginning at (100,50) X * A 512x by 256y area for the maze, beginning at (48, 451) X * A 192x by 96y area for the score lines, at (208, 706) X * X * Actually, the y dimension of both the whole window and the score X * subwindow must be enough to accommodate MAXRATS lines of the X * height of the scoreFont. X */ X X/* parse arguments and set up window state */ X XInitWindow(argc, argv) Xchar **argv; X{ X int i; X X progName = argv[0]; X X BaseFrame = window_create(NULL, FRAME, X FRAME_ARGC_PTR_ARGV, &argc, argv, X FRAME_LABEL, "Maze War", X FRAME_ICON, &ToolIcon, X 0); X mwFont = (struct pixfont *)window_get(BaseFrame, WIN_FONT); X mwFontHeight = mwFont->pf_defaultsize.y; X MaxOffset = 999; X for(i=0;i<256;i++) { X if(MaxOffset > mwFont->pf_char[i].pc_home.y) X MaxOffset = mwFont->pf_char[i].pc_home.y; X } X/* printf("maxoffset = %d\n", MaxOffset); */ X frameFD = (int)window_get(BaseFrame, WIN_FD); X} X X/* X * actually start the display up, after all the user interaction has X * been done. X */ X XStartWindow(ratBits, ratWidth, ratHeight) Xshort *ratBits; X{ X int repaintWindow(); X X initCursors(); X X HourGlassCursor(); X X initMaze(); X/* FlipBitmaps(); /* bit-reverse all the bitmaps */ X initRats(ratBits, ratWidth, ratHeight); X X BaseCanvas = window_create(BaseFrame, CANVAS, X WIN_X, 0, X WIN_Y, 0, X WIN_WIDTH, MAX_X_DIM, X WIN_HEIGHT, MAX_Y_DIM, X 0); X window_fit(BaseFrame); X window_set(BaseFrame, WIN_SHOW, TRUE, 0); X window_set(BaseCanvas, X WIN_CONSUME_KBD_EVENTS, WIN_NO_EVENTS, X WIN_ASCII_EVENTS, KBD_USE, KBD_DONE, 0, X WIN_CONSUME_PICK_EVENTS, WIN_NO_EVENTS, WIN_MOUSE_BUTTONS, X LOC_WINENTER, LOC_WINEXIT, 0, X 0); X mwPixwin = (Pixwin *)window_get(BaseCanvas, CANVAS_PIXWIN); X notify_dispatch(); X displayFD = (int)window_get(BaseCanvas, WIN_FD); X viewPixwin = (Pixwin *)pw_region(mwPixwin, X VIEW_X_ORIGIN, VIEW_Y_ORIGIN, VIEW_X_DIM, VIEW_Y_DIM); X mazePixwin = (Pixwin *)pw_region(mwPixwin, X MAZE_X_ORIGIN, MAZE_Y_ORIGIN, MAZE_X_DIM, MAZE_Y_DIM); X scorePixwin = (Pixwin *)pw_region(mwPixwin, X SCORE_X_ORIGIN, SCORE_Y_ORIGIN, SCORE_X_DIM, SCORE_Y_DIM); X window_set(BaseCanvas, X CANVAS_REPAINT_PROC, repaintWindow, X CANVAS_RESIZE_PROC, repaintWindow, X CANVAS_RETAINED, TRUE, X CANVAS_AUTO_CLEAR, TRUE, X 0); X repaintWindow(); X} X X/* X * Shut down the window system, resetting any terminal modes or what X * have you that may have been altered. No-op for Sun View X */ X XStopWindow() X{ X X} X X/* X * Clear out the view subwindow. If possible, delay the visible effect X * until all the lines have been drawn (since this is only called by X * ShowView(), which is about to draw a bunch of lines). X */ X XClearView() X{ X pw_writebackground(viewPixwin, 0, 0, VIEW_X_DIM, VIEW_Y_DIM, X RastOp); X} X X/* X * Flash the view window to indicate a hit on an opponent. X */ X XFlashTop() X{ X pw_rop(viewPixwin, 0, 0, VIEW_X_DIM, VIEW_Y_DIM, X PIX_NOT(PIX_SRC), viewPixwin, 0, 0); X X /* might want some delay here */ X X pw_rop(viewPixwin, 0, 0, VIEW_X_DIM, VIEW_Y_DIM, X PIX_NOT(PIX_SRC), viewPixwin, 0, 0); X} X X/* X * Flash the whole window to indicate we were hit. X */ X XFlashScreen() X{ X pw_rop(mwPixwin, 0, 0, window_get(BaseCanvas, WIN_WIDTH), X window_get(BaseCanvas, WIN_HEIGHT), PIX_NOT(PIX_SRC), X mwPixwin, 0, 0); X X /* might want some delay here */ X X pw_rop(mwPixwin, 0, 0, window_get(BaseCanvas, WIN_WIDTH), X window_get(BaseCanvas, WIN_HEIGHT), PIX_NOT(PIX_SRC), X mwPixwin, 0, 0); X} X X/* X * Draw a line in the view subwindow. Don't show it right away if possible. X */ X XDrawViewLine(x1, y1, x2, y2) X{ X pw_vector(viewPixwin, x1, y1, x2, y2, RastOp, 1); X} X X/* X * Event handling. Presents a uniform event interface to the game, and X * handles all other events in here. The game only sees the events X * that affect it directly, as defined in mazewar.h. They are: X * X * keypresses that affect the game X * mouse button clicks X * incoming network packets X * timeouts (there's a "heartbeat" to drive the shot clock) X * X * All other events should be swallowed by this routine. Also, since X * keyboard focus is dependent on the mouse being in the game window, X * try to let the user know that he's wandered outside the window X * by inverting the window (should make it obvious) and is now vulnerable X * and defenseless. X */ X Xvoid XNextEvent(event) XMWEvent *event; X{ X int fdmask; X Event sunEvent; X struct timeval timeout; X int ret; X static int LastWinEnterExit = 0; X X while (1) { X if (flashIcon == TRUE){ X if (window_get(BaseFrame, FRAME_CLOSED)){ X InvertIcon(); X } else { X flashIcon = FALSE; X window_set(BaseFrame, FRAME_ICON, &ToolIcon, 0); X } X } X X /* must do this every time, since M.theSocket may change! */ X fdmask = (1 << displayFD) | (1 << frameFD) | (1 << M.theSocket); X timeout.tv_sec = 0; X timeout.tv_usec = 250000; X while ((ret = select(32, &fdmask, NULL, NULL, &timeout)) == -1){ X /* EINTR implies interrupts X * which we don't care about. X * EBADF implies that things X * are probably being shut down X * so we should exit. X */ X if (errno == EBADF){ X event->eventType = EVENT_INT; X return; X } X if (errno != EINTR) X MWError("select error on events"); X } X if (ret == 0) { X event->eventType = EVENT_TIMEOUT; X (void) notify_dispatch(); X return; X } X if (fdmask & (1 <<displayFD)) { X window_read_event(BaseCanvas, &sunEvent); X event->eventType = 0; X switch (sunEvent.ie_code) { X case 'a': X event->eventType = EVENT_A; X return; X X case 's': X event->eventType = EVENT_S; X return; X X case 'd': X event->eventType = EVENT_D; X return; X X case 'f': X event->eventType = EVENT_F; X return; X X case ' ': X event->eventType = EVENT_BAR; X return; X X case 'i': X event->eventType = EVENT_I; X return; X X case 'k': X event->eventType = EVENT_K; X return; X X case 'o': X event->eventType = EVENT_O; X return; X X case 'l': X event->eventType = EVENT_L; X return; X X case 'q': X case 077: X /* should also deal with Interrupt char */ X event->eventType = EVENT_INT; X return; X X case MS_RIGHT: X event->eventType = event_is_down(&sunEvent) ? X EVENT_RIGHT_D : EVENT_RIGHT_U; X return; X X case MS_MIDDLE: X event->eventType = event_is_down(&sunEvent) ? X EVENT_MIDDLE_D : 0; X return; X X case MS_LEFT: X event->eventType = event_is_down(&sunEvent) ? X EVENT_LEFT_D : EVENT_LEFT_U; X return; X X case KBD_USE: X#ifdef DEBUG X printf("Have keyboard focus again.....\n"); X#endif DEBUG X if (RastOp != PIX_SRC) { X invertDisplay(); X } X RastOp = PIX_SRC; X break; X X case KBD_DONE: X#ifdef DEBUG X printf("Lost keyboard focus.....\n"); X#endif DEBUG X if (RastOp != PIX_NOT(PIX_SRC)) { X invertDisplay(); X } X RastOp = PIX_NOT(PIX_SRC); X break; X case WIN_REPAINT: X repaintWindow(); X break; X default: X/* printf("Window event %d\n", sunEvent.ie_code); */ X break; X } X continue; X } X if (fdmask & (1 << M.theSocket)) { X int fromLen = sizeof(event->eventSource); X int cc; X X event->eventType = EVENT_NETWORK; X cc = recvfrom(M.theSocket, event->eventDetail, X sizeof(RatPacket), 0, &event->eventSource, &fromLen); X ConvertIncoming(event->eventDetail); X if (cc <= 0) { X if (cc < 0 && errno != EINTR) X perror("event recvfrom"); X continue; X } X if (fromLen != sizeof(struct sockaddr_in)) X continue; X return; X } X if ( fdmask & (1 << frameFD)) { X notify_dispatch(); X } X } X} X X/* Please stand by ... */ X XHourGlassCursor() X{ X window_set(BaseCanvas, WIN_CURSOR, hourGlassCursor, 0); X} X X/* Let the games begin! */ XRatCursor() X{ X window_set(BaseCanvas, WIN_CURSOR, ratCursor, 0); X} X X/* He's dead, Jim ... or might be */ XDeadRatCursor() X{ X window_set(BaseCanvas, WIN_CURSOR, deadRatCursor, 0); X} X X/* update the displayed bitmap */ X Xmpr_static(Hack_pr, 16, 16, 1, NULL); X XHackMazeBitmap(x, y, newBits) XBitCell *newBits; X{ X Hack_pr_data.md_image = newBits->bits; X pw_rop(mazePixwin, x*16, y*16, 16, 16, RastOp, &Hack_pr, 0, 0); X} X X/* X * Display a rat. Width, height, srcX and srcY describe the subbitmap X * in the rats bitmap that is the desired view of the opponent. X */ X XDisplayRatBitmap(screenX, screenY, width, height, srcX, srcY) X{ X pw_rop(viewPixwin, screenX, screenY, width, height, PIX_SRC^PIX_DST, X ratsPixrect, srcX, srcY); X} X X/* X * Display the score line for the indicated player. Name is left X * justified, score is right justified within the score window. X */ X XWriteScoreString(rat) XRatId rat; X{ X char buf[64]; X int leftEdge; X struct pr_size size; X X pw_text(scorePixwin, 0, (rat+1)*mwFontHeight, RastOp, mwFont, X M.ratcb.rats[rat].name); X sprintf(buf, "%d", (unsigned int)M.ratcb.rats[rat].score); X size = pf_textwidth(strlen(buf), mwFont, buf); X leftEdge = SCORE_X_DIM - size.x; X pw_text(scorePixwin, leftEdge, (rat+1)*mwFontHeight, RastOp, mwFont, X buf); X} X X/* X * Clear out the score line for a player that's left the game. X */ X XClearScoreLine(rat) XRatId rat; X{ X pw_writebackground(scorePixwin, 0, (rat+1)*mwFontHeight+MaxOffset, X SCORE_X_DIM, mwFontHeight, RastOp); X} X X/* X * Pretty obvious, eh? Means the guy's in sight. X */ X XInvertScoreLine(rat) XRatId rat; X{ X pw_writebackground(scorePixwin, 0, (rat+1)*mwFontHeight+MaxOffset, X SCORE_X_DIM, mwFontHeight, PIX_NOT(PIX_DST)); X} X X/* X * END PUBLIC ROUTINES X */ X X/* set up needed bitmaps in the server */ X XinitCursors() X{ X ratCursor = cursor_create(CURSOR_IMAGE, &ratPixrect, X CURSOR_OP, PIX_SRC^PIX_DST, X 0); X deadRatCursor = cursor_create(CURSOR_IMAGE, &deadRatPixrect, X CURSOR_OP, PIX_SRC^PIX_DST, X 0); X hourGlassCursor = cursor_create(CURSOR_IMAGE, &coffeeCupPixrect, X CURSOR_OP, PIX_SRC^PIX_DST, X 0); X} X X/* X * construct a Pixmap of the maze, and store it in the server. X */ X XinitMaze() X{ X register int i, j, k, line, index; X X for (i = 0; i < MAZEYMAX; i++) { X line = i * MAZEXMAX * MAZEYMAX; X for (j = 0; j < MAZEXMAX; j++) { X index = line + j; X for (k = 0; k < 16; k++) { X if (M.maze[j].y[i]) X mazeBits[index] = 0177777; X else X mazeBits[index] = 0; X index += 32; X } X X } X } X mazePixrect = mem_point(MAZE_X_DIM, MAZE_Y_DIM, 1, mazeBits); X} X X/* X * actually put the maze Pixmap on the screen X */ X XdrawMaze() X{ X pw_rop(mazePixwin, 0, 0, MAZE_X_DIM, MAZE_Y_DIM, RastOp, X mazePixrect, 0, 0); X} X X/* X * Create a memory pixrect of the rats so we can do arbitrary X * bitblts on them later. X */ X XinitRats(bits, width, height) Xshort *bits; X{ X ratsPixrect = mem_point(width, height, 1, bits); X} X Xusage() X{ X fprintf(stderr,"usage: mazewar\n"); X exit(1); X X} X XrepaintWindow() X{ X#ifdef DEBUG X printf("repaint window called\n"); X if (RastOp == PIX_SRC) X printf("repaintWindow called with RastOp = PIX_SRC.\n"); X else X printf( X "repaintWindow called with RastOp = PIX_NOT(PIX_SRC).\n"); X#endif DEBUG X if (RastOp != PIX_SRC) X pw_rop(mwPixwin, 0, 0, (int)window_get(BaseCanvas, WIN_WIDTH), X (int)window_get(BaseCanvas, WIN_HEIGHT), X PIX_NOT(PIX_CLR), X mwPixwin, 0, 0); X drawMaze(); X ShowPosition(M.xloc, M.yloc, M.invincible, M.dir); X ShowView(M.xloc, M.yloc, M.dir); X ShowAllPositions(); X X NewScoreCard(); X} X/* X * Invert the whole display. Used to let the user know the mouse has X * strayed outside the window. X */ X XinvertDisplay() X{ X pw_rop(mwPixwin, 0, 0, (int)window_get(BaseCanvas, WIN_WIDTH), X (int)window_get(BaseCanvas, WIN_HEIGHT), PIX_NOT(PIX_SRC), X mwPixwin, 0, 0); X} X X/* X* Peek to see if there's a keyboard event waiting, in case the X* program wants to short-circuit some code. If your system won't let X* you implement this feature easily, just always return FALSE. X*/ X XBoolean XKBEventPending() X{ X return (FALSE); X} X X/* X* Let the user know that a new player joined. This really should only X* take effect if the window is iconic, so you can keep a game around X* all day, close it up when there's no activity, and know when X* someone else is also out to kill some time. X*/ X XNotifyPlayer() X{ X flashIcon = TRUE; X} X X XInvertIcon(){ X Icon icon; X Pixrect *pr; X X icon = (Icon) window_get(BaseFrame, FRAME_ICON); X pr = (Pixrect *) icon_get(icon, ICON_IMAGE); X X pr_rop(pr, 0, 0, 64, 64, PIX_NOT(PIX_DST), pr, 0, 0); X X icon_set(icon, ICON_IMAGE, pr, 0); X window_set(BaseFrame, FRAME_ICON, icon, 0); X} xxFUNNYxx sed 's/^X//' >./winX10.c << 'xxFUNNYxx' X/* $Header: winX10.c,v 1.14 89/02/24 16:59:12 kent Exp $ */ X X/* X * window_X10.c - Window-system specific routines X * X * Author: Christopher A. Kent X * Western Research Laboratory X * Digital Equipment Corporation X * Date: Thu Oct 2 1986 X */ X X/*********************************************************** XCopyright 1986 by Digital Equipment Corporation, Maynard, Massachusetts, X X All Rights Reserved X XPermission to use, copy, modify, and distribute this software and its Xdocumentation for any purpose and without fee is hereby granted, Xprovided that the above copyright notice appear in all copies and that Xboth that copyright notice and this permission notice appear in Xsupporting documentation, and that the names of Digital not be Xused in advertising or publicity pertaining to disstribution of the Xsoftware without specific, written prior permission. X XDIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING XALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL XDIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR XANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, XWHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, XARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS XSOFTWARE. X X******************************************************************/ X X/* X * It is intended that this file presents a window system-independent X * set of routines to MazeWar; that is, if you want to run MW under X * another window system, this is the only file that needs to be X * changed. It isn't yet clear how well I've succeeded, but it's a start. X * X * Routines that are presented to the outside are capitalized; local X * routines are not. X * X * This file is for X version 10. X */ X X/* X * $Log: winX10.c,v $ X * Revision 1.14 89/02/24 16:59:12 kent X * moved bitmaps into a subdirectory, because they're different than X * the X11 ones now. X * X * Revision 1.13 88/08/25 09:59:12 kent X * Copyright and filename changes for system V compatability. X * X * Revision 1.12 88/08/09 14:46:28 kent X * Moved the bitmaps. X * X * Revision 1.11 87/08/21 15:49:57 kent X * Fixed things for monochrome displays. Since the rat images are a pixmap, X * not a bitmap, XORing the foreground color does funny things. Now X * a little more careful in how the pixmap is set up, and all is copacetic. X * X * Revision 1.10 87/08/21 11:46:34 kent X * Changed to use cursor masks and GXcopy; too many displays don't X * support XOR cursors, so the cursor disappears into the background at times. X * X * Revision 1.9 87/03/31 14:42:38 kent X * Portability considerations, especially byteswapping to/from the net. X * X * Revision 1.8 86/12/04 17:46:58 kent X * Make sure colors are always right when creating the iconic version. X * X * Revision 1.7 86/12/04 14:36:56 kent X * Changes to try to notify the user when a new player joins the game. X * X * Revision 1.6 86/12/04 11:20:37 kent X * Added some error return checks, so the program doesn't die with X * a mysterious X runtime error when the server runs out of resources. X * X * Revision 1.5 86/12/03 13:30:57 kent X * X * X * Revision 1.4 86/12/03 10:00:58 kent X * Changes to allow multiple players per machine. X * X * Revision 1.3 86/12/01 23:45:19 kent X * Housecleaning and documentation pass. X * X * Revision 1.2 86/12/01 14:49:11 kent X * Changes for a realistic implementation of shooting. X * X * Revision 1.1 86/11/26 16:58:31 kent X * Initial revision X */ X X#ifndef lint Xstatic char rcs_ident[] = "$Header: winX10.c,v 1.14 89/02/24 16:59:12 kent Exp $"; X#endif X X#include <X/Xlib.h> X#include <X/Xkeyboard.h> X X#include <sys/types.h> X#include <sys/time.h> X X#include <netinet/in.h> X X#include <errno.h> X#include <stdio.h> X#include <strings.h> X X#include "mazewar.h" X#include "bitmaps/X10/icon.ic" X#include "bitmaps/X10/iconMask.ic" X X#define VIEW_X_DIM 400 X#define VIEW_Y_DIM 400 X#define VIEW_X_ORIGIN 100 X#define VIEW_Y_ORIGIN 50 X#define MAZE_X_DIM (MAZEXMAX*16) X#define MAZE_Y_DIM (MAZEYMAX*16) X#define MAZE_X_ORIGIN 48 X#define MAZE_Y_ORIGIN 451 X#define SCORE_X_DIM 192 X#define SCORE_Y_DIM (scoreFontInfo.height * MAXRATS) X#define SCORE_X_ORIGIN 208 X#define SCORE_Y_ORIGIN 708 X#define MIN_X_DIM 608 /* see comments for InitWindow() */ X#define MIN_Y_DIM SCORE_Y_ORIGIN /* see InitWindow() */ X X#define DEFAULT_BORDER_WIDTH 3 X#define DEFAULT_FONT "helv10b" X#define DEFAULT_POSITION "=%dx%d+0+0" /* upper left hand corner */ X X#define KC_A 0302 /* interesting letter keycodes */ X#define KC_D 0315 X#define KC_F 0322 X#define KC_I 0346 X#define KC_K 0347 X#define KC_S 0307 X#define KC_Q 0301 X#define KC_O 0353 X#define KC_L 0354 X#define KC_DEL 0274 X#define KC_BAR 0324 X Xshort mazeBits[MAZEXMAX*MAZEYMAX*16]; /* shorts better be 16 bits! */ XWindow mwWindow; /* parent window */ XOpaqueFrame mwWin; /* frame for the window */ XWindow viewWindow; /* view subwindow */ XWindow mazeWindow; /* maze subwindow */ XWindow scoreWindow; /* score subwindow */ XWindow iconWindow; /* icon window */ XPixmap mazePixmap; /* Pixmap that has the maze */ XPixmap ratsPixmap; /* Pixmap that has the rats */ XBitmap iconMask; /* mask for icon outline */ Xint background; /* color of background */ Xint foreground; /* color of lines and such */ Xint iconFore; /* icon foreground */ Xint iconBack; /* icon background */ Xint mouseground; /* mouse cursor color */ Xint highlight; /* flash colors */ XFont scoreFont; /* font for printing scores */ Xchar *fontName = DEFAULT_FONT; /* font for scores */ XFontInfo scoreFontInfo; XBoolean mapped = FALSE; /* should really display? */ XBoolean inverted = FALSE; /* user warned of window exit */ XBoolean flashIcon = FALSE; /* should icon be flashing? */ XBoolean iconMapped = FALSE; /* is icon on screen? */ Xint displayFD; /* fd of display for events */ X Xstatic short grayBits[16] = { X 0xaaaa, 0x5555, 0xaaaa, 0x5555, X 0xaaaa, 0x5555, 0xaaaa, 0x5555, X 0xaaaa, 0x5555, 0xaaaa, 0x5555, X 0xaaaa, 0x5555, 0xaaaa, 0x5555 X}; X X#include "bitmaps/X10/rat.cur" X#include "bitmaps/X10/ratMask.cur" X#include "bitmaps/X10/dRat.cur" X#include "bitmaps/X10/dRatMask.cur" X#include "bitmaps/X10/cup.cur" X Xstatic Cursor ratCursor, deadRatCursor, hourGlassCursor; Xstatic int planeMask, planePixels; X Xextern char *getenv(); Xextern int errno; X X/* state between InitWindow() and StartWindow() */ Xstatic char *geometry = NULL; Xstatic int borderPixmap; Xstatic int borderWidth = DEFAULT_BORDER_WIDTH; Xstatic char def[128]; Xstatic char *progName; X X/* X * ALL COORDINATES ASSUME ORIGIN (0,0) AT UPPER LEFT! X */ X X/* X * Initialize the window. The overall window must be at least 608x by X * 808y (the size of the Alto screen). There are three distinct X * subregions: X * A 400x by 400y area for the view, beginning at (100,50) X * A 512x by 256y area for the maze, beginning at (48, 451) X * A 192x by 96y area for the score lines, at (208, 706) X * X * Actually, the y dimension of both the whole window and the score X * subwindow must be enough to accommodate MAXRATS lines of the X * height of the scoreFont. X */ X X/* parse arguments and set up window state */ X XInitWindow(argc, argv) Xchar **argv; X{ X int i; X char display[256]; /* will contain vs host */ X int reverse = 0; X char *borderColor; X char *foreColor; X char *backColor; X char *highColor; X char *mouseColor; X Color cdef; X char *option; X X display[0] = '\0'; X progName = argv[0]; X X if ((option = XGetDefault(argv[0],"ReverseVideo")) != NULL ) X if (strcmp (option, "on") == 0) X reverse = 1; X if ((option = XGetDefault(argv[0],"BorderWidth")) != NULL) X borderWidth = atoi(option); X if ((option = XGetDefault(argv[0],"BodyFont")) != NULL) X fontName = option; X if ((borderColor = XGetDefault(argv[0],"Border")) == NULL) X borderColor = XGetDefault(argv[0],"BorderColor"); X backColor = XGetDefault(argv[0],"Background"); X foreColor = XGetDefault(argv[0],"Foreground"); X highColor = XGetDefault(argv[0],"Highlight"); X mouseColor = XGetDefault(argv[0],"Mouse"); X X for (i = 1; i < argc; i++) { /* Parse line */ X if (argv[i][0] == '=') { X geometry = argv[i]; X continue; X } X if (index(argv[i], ':') != NULL) { /* host:display */ X strncpy(display, argv[i], sizeof(display)); X continue; X } X if (strcmp(argv[i], "-rv") == 0 || X strcmp(argv[i], "-reverse") == 0) { /* white on black */ X reverse = 1; X continue; X } X if (strcmp(argv[i], "-fw") == 0 || X strcmp(argv[i], "-forward") == 0) { /* black on white */ X reverse = 0; X continue; X } X if (strcmp(argv[i], "-bw") == 0 || X strcmp(argv[i], "-border") == 0) { /* border width */ X if (++i >= argc) usage(); X borderWidth = atoi(argv[i]); X continue; X } X if (strcmp(argv[i], "-fn") == 0 || X strcmp(argv[i], "-font") == 0) { /* score font */ X if (++i >= argc) usage(); X fontName = argv[i]; X continue; X } X if (strcmp(argv[i], "-bd") == 0 || X strcmp(argv[i], "-color") == 0) { /* border color */ X if (++i >= argc) usage(); X borderColor = argv[i]; X continue; X } X if (strcmp(argv[i], "-fg") == 0 || X strcmp(argv[i], "-foreground") == 0) { /* foreground color */ X if (++i >= argc) usage(); X foreColor = argv[i]; X continue; X } X if (strcmp(argv[i], "-bg") == 0 || X strcmp(argv[i], "-background") == 0) { /* background color */ X if (++i >= argc) usage(); X backColor = argv[i]; X continue; X } X if (strcmp(argv[i], "-ms") == 0 || X strcmp(argv[i], "-mouse") == 0) { /* mouse color */ X if (++i >= argc) usage(); X mouseColor = argv[i]; X continue; X } X if (strcmp(argv[i], "-hl") == 0 || X strcmp(argv[i], "-highlight") == 0) { /* highlight color */ X if (++i >= argc) usage(); X highColor = argv[i]; X continue; X } X usage(); X } X X if (borderWidth < 0) borderWidth = DEFAULT_BORDER_WIDTH; X X /* Open display */ X if (!XOpenDisplay(display)) X MWError("cannot open display"); X X /* Need a font to print scores */ X scoreFont = XGetFont(fontName); X if (!scoreFont) X MWError("cannot open font"); X XQueryFont(scoreFont, &scoreFontInfo); /* or XOpenFont */ X X if (DisplayCells() > 2) /* get some color map to play with */ X XGetColorCells(0, 1, 1, &planeMask, &planePixels); X else X planeMask = AllPlanes; X X if (borderColor && DisplayCells() > 2 && X XParseColor(borderColor, &cdef) && XGetHardwareColor(&cdef)) X borderPixmap = XMakeTile(cdef.pixel); X else if (borderColor && strcmp(borderColor, "black") == 0) X borderPixmap = BlackPixmap; X else if (borderColor && strcmp(borderColor, "white") == 0) X borderPixmap = WhitePixmap; X else X borderPixmap = XMakePixmap ( X (Bitmap) XStoreBitmap (16, 16, grayBits), X BlackPixel, WhitePixel); X X if (backColor && strcmp(backColor, "white") == 0) { X background = WhitePixel; X reverse = 0; X } else if (backColor && strcmp(backColor, "black") == 0) { X background = BlackPixel; X reverse = 0; X } else X background = WhitePixel; X if (DisplayCells() > 2) { X if (backColor == NULL) X backColor = "white"; X if (XParseColor(backColor, &cdef)) { X if (reverse) X cdef.pixel = planePixels | planeMask; X else X cdef.pixel = planePixels; X XStoreColor(&cdef); X background = cdef.pixel; X } X } X X if (foreColor && strcmp(foreColor, "black") == 0) { X foreground = BlackPixel; X reverse = 0; X } else if (foreColor && strcmp(foreColor, "white") == 0) { X foreground = WhitePixel; X reverse = 0; X } else X foreground = BlackPixel; X if (DisplayCells() > 2) { X if (foreColor == NULL) X foreColor = "black"; X if (XParseColor(foreColor, &cdef)) { X if (reverse) X cdef.pixel = planePixels; X else X cdef.pixel = planePixels | planeMask; X XStoreColor(&cdef); X foreground = cdef.pixel; X } X } X X if (mouseColor && DisplayCells() > 2 && X XParseColor(mouseColor, &cdef) && XGetHardwareColor(&cdef)) { X mouseground = cdef.pixel; X } else if (mouseColor && strcmp(mouseColor, "black") == 0) { X mouseground = BlackPixel; X reverse = 0; X } else if (mouseColor && strcmp(mouseColor, "white") == 0) { X mouseground = WhitePixel; X reverse = 0; X } else X mouseground = BlackPixel; X X if (highColor && DisplayCells() > 2 && X XParseColor(highColor, &cdef) && XGetHardwareColor(&cdef)) X highlight = cdef.pixel; X else if (reverse) X highlight = background; X else X highlight = foreground; X X if (reverse) { X background = foreground; X foreground = highlight; X } X} X X/* X * actually start the display up, after all the user interaction has X * been done. X */ X XStartWindow(ratBits, ratWidth, ratHeight) Xshort *ratBits; X{ X WindowInfo mwInfo; X X mwWin.bdrwidth = borderWidth; X mwWin.border = borderPixmap; X mwWin.background = XMakeTile(background); X sprintf(def, DEFAULT_POSITION, MIN_X_DIM, X MIN_Y_DIM + (MAXRATS+1) * scoreFontInfo.height); X mwWindow = XCreate ("Maze War", progName, geometry, def, &mwWin, X MIN_X_DIM, MIN_Y_DIM + (MAXRATS+1) * scoreFontInfo.height); X X /* set up the subwindows */ X /* see above comment for magic numbers */ X viewWindow = XCreateTransparency(mwWindow, VIEW_X_ORIGIN, X VIEW_Y_ORIGIN, VIEW_X_DIM, VIEW_Y_DIM); X mazeWindow = XCreateTransparency(mwWindow, MAZE_X_ORIGIN, X MAZE_Y_ORIGIN, MAZE_X_DIM, MAZE_Y_DIM); X scoreWindow = XCreateTransparency(mwWindow, SCORE_X_ORIGIN, X SCORE_Y_ORIGIN, SCORE_X_DIM, SCORE_Y_DIM); X X /* set up the icon */ X XQueryWindow(mwWindow, &mwInfo); X iconWindow = XCreateWindow(RootWindow, X mwInfo.x + (mwInfo.width - icon_width) / 2, X mwInfo.y + (mwInfo.height - icon_height) / 2, X icon_width, icon_height, 0, 0, 0); X XTileRelative(iconWindow); X XSetIconWindow(mwWindow, iconWindow); X iconMask = XStoreBitmap(icon_mask_width, icon_mask_height, X icon_mask_bits); X XSelectInput(iconWindow, ExposeWindow|UnmapWindow); X iconFore = foreground; X iconBack = background; X X initCursors(); X X XSelectInput(mwWindow, X KeyPressed|ButtonPressed|ButtonReleased| X EnterWindow|LeaveWindow|ExposeWindow|UnmapWindow); X XMapWindow(viewWindow); X XMapWindow(mazeWindow); X XMapWindow(scoreWindow); X HourGlassCursor(); X XMapWindow(mwWindow); /* Map window to screen */ X X initMaze(); X FlipBitmaps(); /* bit-reverse all the bitmaps */ X initRats(ratBits, ratWidth, ratHeight); X X displayFD = dpyno(); X} X X/* X * Shut down the window system, resetting any terminal modes or what X * have you that may have been altered. No-op for X V10. X */ X XStopWindow() X{ X X} X X/* X * Clear out the view subwindow. If possible, delay the visible effect X * until all the lines have been drawn (since this is only called by X * ShowView(), which is about to draw a bunch of lines). X */ X XClearView() X{ X XClear(viewWindow); X} X X/* X * Flash the view window to indicate a hit on an opponent. X */ X XFlashTop() X{ X XCopyArea(viewWindow, 0, 0, 0, 0, VIEW_X_DIM, VIEW_Y_DIM, X GXinvert, planeMask); X XFlush(); X X /* might want some delay here */ X X XCopyArea(viewWindow, 0, 0, 0, 0, VIEW_X_DIM, VIEW_Y_DIM, X GXinvert, planeMask); X XFlush(); X} X X/* X * Flash the whole window to indicate we were hit. X */ X XFlashScreen() X{ X XCopyArea(mwWindow, 0, 0, 0, 0, mwWin.width, mwWin.height, X GXinvert, planeMask); X XFlush(); X X /* might want some delay here */ X X XCopyArea(mwWindow, 0, 0, 0, 0, mwWin.width, mwWin.height, X GXinvert, planeMask); X XFlush(); X} X X/* X * Draw a line in the view subwindow. Don't show it right away if possible. X */ X XDrawViewLine(x1, y1, x2, y2) X{ X XLine(viewWindow, x1, y1, x2, y2, 1, 1, X foreground, GXcopy, planeMask); X} X X/* X * Event handling. Presents a uniform event interface to the game, and X * handles all other events in here. The game only sees the events X * that affect it directly, as defined in mazewar.h. They are: X * X * keypresses that affect the game X * mouse button clicks X * incoming network packets X * timeouts (there's a "heartbeat" to drive the shot clock) X * X * All other events should be swallowed by this routine. Also, since X * keyboard focus is dependent on the mouse being in the game window, X * try to let the user know that he's wandered outside the window X * by inverting the window (should make it obvious) and is now vulnerable X * and defenseless. X */ X Xvoid XNextEvent(event) XMWEvent *event; X{ X int fdmask; X XEvent xEvent; X struct timeval timeout; X int ret; X X while (1) { X if (flashIcon) { /* invert the icon colors */ X int temp; X X temp = iconFore; X iconFore = iconBack; X iconBack = temp; X repaintIcon(); X } X X /* must do this every time, since M.theSocket may change! */ X fdmask = (1 << displayFD) | (1 << M.theSocket); X if (!XPending()) { X timeout.tv_sec = 0; X timeout.tv_usec = 250000; X while ((ret = select(32, &fdmask, NULL, NULL, &timeout)) == -1) X if (errno != EINTR) X MWError("select error on events"); X if (ret == 0) { X event->eventType = EVENT_TIMEOUT; X return; X } X } else X fdmask = 0; X if (XPending()) { X XNextEvent(&xEvent); X switch (xEvent.type) { X case KeyPressed: X event->eventType = 0; X switch(((XKeyEvent *) &xEvent)->detail & 0xff) { X case KC_A: X case KC_KEYPAD_4: X event->eventType = EVENT_A; X return; X X case KC_S: X case KC_KEYPAD_5: X event->eventType = EVENT_S; X return; X X case KC_D: X case KC_KEYPAD_6: X event->eventType = EVENT_D; X return; X X case KC_F: X case KC_KEYPAD_COMMA: X event->eventType = EVENT_F; X return; X X case KC_BAR: X case KC_CURSOR_RIGHT: X event->eventType = EVENT_BAR; X return; X X case KC_I: X event->eventType = EVENT_I; X return; X X case KC_K: X event->eventType = EVENT_K; X return; X X case KC_O: X event->eventType = EVENT_O; X return; X X case KC_L: X event->eventType = EVENT_L; X return; X X case KC_Q: X case KC_DEL: X /* should also deal with Interrupt char */ X event->eventType = EVENT_INT; X return; X } X break; X X#define RightButton 0 X#define MiddleButton 1 X#define LeftButton 2 X case ButtonPressed: X event->eventType = 0; X switch(((XButtonPressedEvent *) &xEvent)->detail&0xff) { X case RightButton: X event->eventType = EVENT_RIGHT_D; X return; X X case MiddleButton: X event->eventType = EVENT_MIDDLE_D; X return; X X case LeftButton: X event->eventType = EVENT_LEFT_D; X return; X } X break; X X case ButtonReleased: X event->eventType = 0; X switch(((XButtonReleasedEvent *) &xEvent)->detail&0xff) { X case RightButton: X event->eventType = EVENT_RIGHT_U; X return; X X case LeftButton: X event->eventType = EVENT_LEFT_U; X return; X } X break; X X case EnterWindow: X if (xEvent.subwindow != 0) X break; X if ((((XEnterWindowEvent *)&xEvent)->detail&0xff) == 1) X break; X if (inverted == TRUE) { X inverted = FALSE; X invertDisplay(); X } X break; X X case LeaveWindow: X if (xEvent.subwindow != 0) X break; X if ((((XLeaveWindowEvent *)&xEvent)->detail&0xff) == 1) X break; X if (inverted == FALSE) { X inverted = TRUE; X invertDisplay(); X } X break; X X case ExposeWindow: X if (((XExposeWindowEvent *)&xEvent)->window X == iconWindow) { X if (iconMapped == FALSE) { X flashIcon = FALSE; X iconMapped = TRUE; X iconFore = foreground; X iconBack = background; X } X repaintIcon(); X break; X } X mapped = TRUE; X if (inverted == TRUE) { X inverted = FALSE; X invertDisplay(); X } X repaintWindow(); X break; X X case UnmapWindow: X if (((XUnmapEvent *)&xEvent)->window X == iconWindow) { X flashIcon = FALSE; X iconMapped = FALSE; X break; X } X mapped = FALSE; X break; X } X } X X if (fdmask & (1 << M.theSocket)) { X int fromLen = sizeof(event->eventSource); X int cc; X X event->eventType = EVENT_NETWORK; X cc = recvfrom(M.theSocket, event->eventDetail, X sizeof(RatPacket), 0, &event->eventSource, &fromLen); X if (cc <= 0) { X if (cc < 0 && errno != EINTR) X perror("event recvfrom"); X continue; X } X if (fromLen != sizeof(struct sockaddr_in)) X continue; X ConvertIncoming(event->eventDetail); X return; X } X } X} X X/* X * Peek to see if there's a keyboard event waiting, in case the X * program wants to short-circuit some code. If your system won't let X * you implement this feature easily, just always return FALSE. X */ X XBoolean XKBEventPending() X{ X return (QLength() != 0); X} X X/* Please stand by ... */ X XHourGlassCursor() X{ X XDefineCursor(mwWindow, hourGlassCursor); X XFlush(); X} X X/* Let the games begin! */ XRatCursor() X{ X XDefineCursor(mwWindow, ratCursor); X XFlush(); X} X X/* He's dead, Jim ... or might be */ XDeadRatCursor() X{ X XDefineCursor(mwWindow, deadRatCursor); X XFlush(); X} X X/* update the displayed bitmap */ X XHackMazeBitmap(x, y, newBits) XBitCell *newBits; X{ X XBitmapBitsPut(mazeWindow, x*16, y*16, 16, 16, newBits, X foreground, background, 0, GXcopy, planeMask); X XFlush(); X} X X/* X * Display a rat. X is clever enough that the server does all the work for X * you; provides a generalized bitblt on Pixmaps. Hopefully other window X * systems do as well. Width, height, srcX and srcY describe the subbitmap X * in the rats bitmap that is the desired view of the opponent. X */ X XDisplayRatBitmap(screenX, screenY, width, height, srcX, srcY) X{ X XPixmapPut(viewWindow, srcX, srcY, screenX, screenY, X width, height, ratsPixmap, GXxor, planeMask); X} X X/* X * Display the score line for the indicated player. Name is left X * justified, score is right justified within the score window. X */ X XWriteScoreString(rat) XRatId rat; X{ X char buf[64]; X int leftEdge; X X sprintf(buf, "%d", (unsigned int) M.ratcb.rats[rat].score); X X XText(scoreWindow, 0, rat * scoreFontInfo.height, X M.ratcb.rats[rat].name, strlen(M.ratcb.rats[rat].name), X scoreFont, foreground, background); X /* or XQueryWidth for speed, must XOpenFont above */ X leftEdge = SCORE_X_DIM - XQueryWidth(buf, scoreFont, 0, 0); X XText(scoreWindow, leftEdge, rat * scoreFontInfo.height, X buf, strlen(buf), X scoreFont, foreground, background); X} X X/* X * Clear out the score line for a player that's left the game. X */ X XClearScoreLine(rat) XRatId rat; X{ X XPixSet(scoreWindow, X 0, rat * scoreFontInfo.height, X SCORE_X_DIM, scoreFontInfo.height, X background); X} X X/* X * Pretty obvious, eh? Means the guy's in sight. X */ X XInvertScoreLine(rat) XRatId rat; X{ X XCopyArea(scoreWindow, X 0, rat * scoreFontInfo.height, X 0, rat * scoreFontInfo.height, X SCORE_X_DIM, scoreFontInfo.height, X GXinvert, planeMask); X} X X/* X * Let the user know that a new player joined. This really should only X * take effect if the window is iconic, so you can keep a game around X * all day, close it up when there's no activity, and know when X * someone else is also out to kill some time. X */ X XNotifyPlayer() X{ X flashIcon = TRUE; X} X X/* X * END PUBLIC ROUTINES X */ X X/* set up needed bitmaps in the server */ X XinitCursors() X{ X ratCursor = XCreateCursor(16, 16, rat_bits, ratMask_bits, X 0, 0, mouseground, background, GXcopy); X deadRatCursor = XCreateCursor(16, 16, deadRat_bits, deadRatMask_bits, X 0, 0, mouseground, background, GXcopy); X hourGlassCursor = XCreateCursor(16, 16, coffeeCup_bits, coffeeCup_bits, X 0, 0, mouseground, background, GXcopy); X} X X/* X * construct a Pixmap of the maze, and store it in the server. X */ X XinitMaze() X{ X register int i, j, k, line, index; X Bitmap b; X X for (i = 0; i < MAZEYMAX; i++) { X line = i * MAZEXMAX * MAZEYMAX; X for (j = 0; j < MAZEXMAX; j++) { X index = line + j; X for (k = 0; k < 16; k++) { X if (M.maze[j].y[i]) X mazeBits[index] = 0177777; X else X mazeBits[index] = 0; X index += 32; X } X X } X } X X b = XStoreBitmap(MAZE_X_DIM, MAZE_Y_DIM, mazeBits); X mazePixmap = XMakePixmap(b, foreground, background); X if (mazePixmap == 0) X MWError("Can't create maze Pixmap"); X XFreeBitmap(b); X} X X/* X * actually put the maze Pixmap on the screen X */ X XdrawMaze() X{ X XPixmapPut(mazeWindow, 0, 0, 0, 0, MAZE_X_DIM, MAZE_Y_DIM, X mazePixmap, GXcopy, planeMask); X} X X/* X * send the rat image bitmap to the server so we can do arbitrary X * bitblts on them later. X10 doesn't allow you to do this on bitmaps, X * just pixmaps. Ga. X */ X XinitRats(bits, width, height) Xshort *bits; X{ X Bitmap b; X X b = XStoreBitmap(width, height, bits); X if (DisplayCells() > 2) X ratsPixmap = XMakePixmap(b, planeMask, 0); X else X ratsPixmap = XMakePixmap(b, 1, 0); X if (ratsPixmap == 0) X MWError("Can't create rats Pixmap"); X XFreeBitmap(b); X} X Xusage() X{ X fprintf(stderr, X"usage: mazewar [-fn {font}] [-update {seconds}] [-scale {integer}] [-rv]\n" X); X fprintf(stderr, X" [=[{width}][x{height}][{+-}{xoff}[{+-}{yoff}]]] [[{host}]:[{vs}]]\n" X); X fprintf(stderr, X" [-fg {color}] [-bg {color}] [-hl {color}] [-bd {color}] [-bw {pixels}]\n"); X exit(1); X X} X X/* X * Repaint the window for exposure and resize events. All drawing is X * done here, even though there are times during the initialization X * code when it would seem obvious to put some of the display up; this X * way portions of the display aren't shown twice then. X */ X XrepaintWindow() X{ X drawMaze(); X ShowPosition(M.xloc, M.yloc, M.invincible, M.dir); X ShowView(M.xloc, M.yloc, M.dir); X ShowAllPositions(); X X NewScoreCard(); X XFlush(); X} X X/* X * Repaint the icon for exposure events, or while flashing to indicate X * that there's a new player. X */ X XrepaintIcon() X{ X int temp; X X XBitmapBitsPut(iconWindow, 0, 0, icon_width, icon_height, icon_bits, X iconFore, iconBack, iconMask, GXcopy, AllPlanes); X} X X/* X * Invert the whole display. Used to let the user know the mouse has X * strayed outside the window. Doesn't currently work right. X */ X XinvertDisplay() X{ X#ifdef notdef X XCopyArea(mwWindow, 0, 0, 0, 0, mwWin.width, mwWin.height, X GXinvert, planeMask); X XFlush(); X#endif X} xxFUNNYxx