Crossfire Client, Branch  R11627
x11.c
Go to the documentation of this file.
00001 const char *rcsid_x11_x11_c =
00002     "$Id: x11.c 9201 2008-06-01 17:32:45Z anmaster $";
00003 /*
00004     Crossfire client, a client program for the crossfire program.
00005 
00006     Copyright (C) 2001-2003,2006 Mark Wedel & Crossfire Development Team
00007 
00008     This program is free software; you can redistribute it and/or modify
00009     it under the terms of the GNU General Public License as published by
00010     the Free Software Foundation; either version 2 of the License, or
00011     (at your option) any later version.
00012 
00013     This program is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016     GNU General Public License for more details.
00017 
00018     You should have received a copy of the GNU General Public License
00019     along with this program; if not, write to the Free Software
00020     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00021 
00022     The author can be reached via e-mail to crossfire-devel@real-time.com
00023 */
00024 
00025 /*
00026  * This file handles mose of the windowing stuff.  The idea is
00027  * that all of it is in one file, so to port to different systems
00028  * or toolkits, only this file needs to be updated.  All windowing
00029  * variables (display, gc's, windows, etc), should be stored in
00030  * this file as statics.
00031  *
00032  * This file is largely a combination of the common/xutil.c and server/xio.c
00033  * file.  While I don't think this was a particulary great interface, the code
00034  * was there, and I figured it was probably easier to re-use that
00035  * code instead of writing new code, plus the old code worked.
00036  *
00037  */
00038 
00039 #define X_PROG_NAME "cfclient"
00040 
00041 /* Most functions in this file are private.  Here is a list of
00042  * the global functions:
00043  *
00044  * draw_color_info(int color, char*buf) - draws text in specified color
00045  * draw_info - draw info in the info window
00046  * end_windows - used when exiting
00047  * init_windows - called when starting up
00048  * load_images - creates the bitmaps and pixmaps (if being used)
00049  * create_pixmap - creates a pixmap from given file and assigns to
00050  *              the given face
00051  * create_xpm - as create_pixmap, but does an XPM image
00052  *
00053  * draw_stats(int) - draws the stat window.  Pass 1 to redraw all
00054  *      stats, not only those that changed
00055  *
00056  * draw_message_window(int) - draws the message window.  Pass 1 to redraw
00057  *      all the bars, not only those that changed.
00058  *
00059  * draw_look, draw_inv:  Update the look and inventory windows.
00060  *
00061  * NOTE: create_pixmap and create_xpm can be empty functions if the
00062  * client will always use fonts - in that case, it should never
00063  * request Bitmap or Pixmap data, and thus not need the create
00064  * functions above
00065  *
00066  * Only functions in this file should be calling functions like
00067  * draw_stats and draw_message_window with redraw set - functions
00068  * in other files should always pass 0, because they will never have
00069  * the information of whether a redraw is needed.
00070  */
00071 
00072 
00073 #include <client.h>
00074 #include "clientbmap.h"
00075 #include <item.h>
00076 #include <config.h>
00077 #include <script.h>
00078 #include <p_cmd.h>
00079 
00080 #ifdef HAVE_LIBXPM
00081 #include <X11/xpm.h>
00082 #endif
00083 
00084 #include <X11/Xlib.h>
00085 #include <X11/Xutil.h>
00086 
00087 #include "mapdata.h"
00088 #include "x11proto.h"
00089 #include "x11.h"
00090 
00091 #include <errno.h>
00092 
00093 #if defined(__pyrsoft)
00094 #define _Xconst
00095 #endif
00096 
00097 /* All the following are static because these variables should
00098  * be local only to this file.  Since the idea is to have only
00099  * this file be replaced for different windowing systems, use of
00100  * any of these variables anyplace else would not be portable.
00101  */
00102 
00103 typedef enum inventory_show {
00104   show_all = 0, show_applied = 0x1, show_unapplied = 0x2, show_unpaid = 0x4,
00105   show_cursed = 0x8, show_magical = 0x10, show_nonmagical = 0x20,
00106   show_locked = 0x40, show_unlocked = 0x80,
00107   show_mask=0xff
00108 } inventory_show;
00109 
00110 /*
00111  *  This is similar obwin, but totally redone for client
00112  */
00113 typedef struct {
00114     item *env;            /* Environment shown in window */
00115     char title[MAX_BUF];  /* title of item list */
00116     char old_title[MAX_BUF];  /* previos title (avoid redrawns) */
00117 
00118     Window win;           /* for X-windows */
00119     GC gc_text;
00120     GC gc_icon;
00121     GC gc_status;
00122 
00123     uint8 show_icon:1;    /* show status icons */
00124     uint8 show_weight:1;  /* show item's weight */
00125 
00126     char format_nw[20];   /* sprintf-format for text (name and weight) */
00127     char format_nwl[20];  /* sprintf-format for text (name, weight, limit) */
00128     char format_n[20];    /* sprintf-format for text (only name) */
00129     sint16 text_len;      /* How wide the text-field is */
00130 
00131     sint16 width;         /* How wide the window is in pixels */
00132     sint16 height;        /* How height the window is in pixels */
00133 
00134     sint16 item_pos;      /* The sequence number of the first drawn item */
00135     sint16 item_used;     /* How many items actually drawn. (0 - size) */
00136 
00137     sint16 size;          /* How many items there is room to display */
00138     sint16 *faces;        /* [size] */
00139     sint8 *icon1;         /* status icon : locked */
00140     sint8 *icon2;         /* status icon : applied / unpaid */
00141     sint8 *icon3;         /* status icon : magic */
00142     sint8 *icon4;         /* status icon : damned / cursed */
00143     char **names;         /* [size][NAME_LEN] */
00144 
00145     /* The scrollbar */
00146     sint16 bar_length;    /* the length of scrollbar in pixels */
00147     sint16 bar_size;      /* the current size of scrollbar in pixels */
00148     sint16 bar_pos;       /* the starting position of scrollbar in pixels */
00149     inventory_show show_what;   /* What to show in inventory */
00150     uint32  weight_limit;   /* Weight limit for this list - used for title */
00151 } itemlist;
00152 
00153 int noautorepeat = FALSE;       /* turn off autorepeat detection */
00154 
00155 static char *font_name="8x13",  **gargv;
00156 
00157 #define SCROLLBAR_WIDTH 16      /* +2+2 for border on each side */
00158 #define INFOCHARS 50
00159 #define INFOLINES 36
00160 /* Perhaps decent defaults, but not quite right */
00161 static int  FONTWIDTH= 8;
00162 static int FONTHEIGHT= 13;
00163 #define MAX_INFO_WIDTH 80
00164 #define MAXNAMELENGTH 50
00165 
00166 /* What follows is various constants (or calculations) for various
00167  * window sizes.
00168  */
00169 
00170 /* Width (and height) of the game window */
00171 #define GAME_WIDTH  (image_size * use_config[CONFIG_MAPWIDTH] + 5)
00172 
00173 #define STAT_HEIGHT 140
00174 
00175 /* Width of the inventory and look window */
00176 #define INV_WIDTH   300
00177 /* spacing between windows */
00178 #define WINDOW_SPACING  3
00179 /* Height of the master (root) window */
00180 #define ROOT_HEIGHT     522
00181 
00182 static int gargc, old_mapx=11, old_mapy=11;
00183 
00184 Display *display;
00185 static Window def_root; /* default root window */
00186 static long def_screen; /* default screen number */
00187 static unsigned long foreground,background;
00188 Window win_stats,win_message;
00189 Window win_root,win_game;
00190 Colormap colormap;
00191 static XColor discolor[16];
00192 static XFontStruct *font;       /* Font loaded to display in the windows */
00193 static XEvent event;
00194 static XSizeHints messagehint, roothint;
00195 static Atom wm_delete_window;
00196 
00197 /* This struct contains the information to draw 1 line of data. */
00198 typedef struct {
00199     char        *info;          /* Actual character data for a line */
00200     uint8       color;          /* Color to draw that line */
00201 } InfoLine;
00202 
00203 /* This contains all other information for the info window */
00204 typedef struct {
00205     uint16      info_chars;     /* width in chars of info window */
00206     uint16      max_info_chars; /* Max value of info_chars */
00207     uint16      infopos;        /* Where in the info arry to put new data */
00208     uint16      infoline;       /* Where on the window to draw the line */
00209     uint16      scroll_info_window:1;  /* True if we should scroll the window */
00210     uint16      numlines;       /* How many have been stored the array */
00211     uint16      maxlines;       /* Maxlines (how large the array below is) */
00212     uint16      maxdisp;        /* How many lines can be displayed at once */
00213     uint8       lastcolor;      /* Last color text was drawn in */
00214     InfoLine    *data;          /* An array of lines */
00215     Window      win_info;       /* Actual info window */
00216     GC          gc_info;        /* GC for this window */
00217     /* The scrollbar */
00218     sint16 bar_length;    /* the max length of scrollbar in pixels */
00219     sint16 bar_size;      /* the current size (length) of scrollbar in pixels */
00220     sint16 bar_pos;       /* the starting position of scrollbar.  This is
00221                            * an offset, which is the number of lines from
00222                            * 0 for the text to end out.*/
00223     sint16 bar_y;         /* X starting position of scrollbar */
00224     uint16      has_scrollbar:1;/* True if there is a scrollbar in the window */
00225     sint16      width,height; /* Width and height of window */
00226 } InfoData;
00227 
00228 InfoData infodata = {0, 0, 0, 0, 1, 0, INFOLINES, INFOLINES, NDI_BLACK,
00229         NULL, 0, 0,0,0,0,0,0,0,0};
00230 
00231 uint8   image_size=24;
00232 
00233 
00234 
00235 static char stats_buff[7][600];
00236 struct PixmapInfo *pixmaps[MAXPIXMAPNUM];
00237 /* Off the 'free' space in the window, this floating number is the
00238  * portion that the info takes up.
00239  */
00240 static float info_ratio=0;
00241 
00242 #define XPMGCS 100
00243 
00244 enum {
00245     no_icon = 0, locked_icon, applied_icon, unpaid_icon,
00246     damned_icon, cursed_icon, magic_icon, close_icon,
00247     stipple1_icon, stipple2_icon, max_icons
00248 };
00249 static Pixmap icons[max_icons];
00250 
00251 static Pixmap icon,xpm_pixmap,xpm_masks[XPMGCS], dark1, dark2, dark3;
00252 static GC gc_root,gc_stats,gc_message,
00253         gc_floor,gc_xpm_object,gc_clear_xpm,gc_xpm[XPMGCS],
00254         gc_blank;
00255 GC gc_game;
00256 static GC gc_copy;              /* used for copying when scrolling map view */
00257 
00258 /*
00259  * These are used for inventory and look window
00260  */
00261 static itemlist look_list, inv_list;
00262 
00263 /* Used to know what stats has changed */
00264 static Stats last_stats = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
00265 
00266 /* info win */
00267 #define INFOCHARS 50
00268 
00269 
00270 /* This is the loop that the client goes through once all the
00271  * initialization is done.  Basically, it checks for input and
00272  * processes X events (calls function to do that.)
00273  * The time for command_loop is fairly arbitrary - it can be most
00274  * any value.  If it is very low, however, as it will be doing a lot
00275  * of checks to see if there is data instead of blocking on input.
00276  *
00277  * check_x_events takes all the events that are waiting.
00278  */
00279 
00280 extern int maxfd;
00281 
00282 /* Handle errors.  I really needed this when debugging
00283  * the crashes with the big image stuff - I need to know
00284  * what function is causing the crash.
00285  */
00286 int error_handler(Display *dp, XErrorEvent *xe)
00287 {
00288     char buf[MAX_BUF];
00289 
00290     XGetErrorText(dp, xe->error_code, buf, MAX_BUF-1);
00291     fprintf(stderr,buf);
00292     /* If you want to try to live through errors, comment out
00293      * the abort below.
00294      */
00295     abort();
00296 
00297     return 0;   /* just to prevent warnings */
00298 }
00299 
00300 void event_loop(void)
00301 {
00302     fd_set tmp_read;
00303     int pollret;
00304     struct timeval timeout;
00305 
00306 
00307     if (MAX_TIME==0) {
00308         timeout.tv_sec = 0;
00309         timeout.tv_usec = 0;
00310     }
00311     maxfd = csocket.fd + 1;
00312     while (1) {
00313         if (csocket.fd==-1) return;
00314 
00315         /* Do a quick check here for better performance */
00316         check_x_events();
00317 
00318         FD_ZERO(&tmp_read);
00319         FD_SET(csocket.fd, &tmp_read);
00320         script_fdset(&maxfd,&tmp_read);
00321         if (MAX_TIME!=0) {
00322             timeout.tv_sec = MAX_TIME / 1000000;
00323             timeout.tv_usec = MAX_TIME % 1000000;
00324         }
00325         pollret = select(maxfd, &tmp_read, NULL, NULL, &timeout);
00326         if (pollret==-1) {
00327             fprintf(stderr, "Got errno %d on select call.\n", errno);
00328         }
00329         else if (FD_ISSET(csocket.fd, &tmp_read)) {
00330             DoClient(&csocket);
00331         }
00332         else {
00333             script_process(&tmp_read);
00334         }
00335         animate_objects();  /* Do this before the x events, since they
00336                              * can redraw this for us.
00337                              */
00338         check_x_events();
00339     }
00340 }
00341 
00342 int misses=0,total=0,newimages=0;
00343 
00344 /* Draws 'face' onto 'where' at x,y.
00345  * sx and sy is the the offset to draw from.
00346  */
00347 
00348 static void gen_draw_face(Drawable where,int face,int x,int y, int sx, int sy)
00349 {
00350     if (face < 0 || face >= MAXPIXMAPNUM) {
00351         fprintf(stderr,"Invalid face number: %d @ %d, %d\n", face, x, y);
00352         return;
00353     }
00354     if (pixmaps[face]->mask == None) {
00355         XCopyArea(display, pixmaps[face]->pixmap,
00356             where,gc_floor,
00357             sx,sy,image_size,image_size,x,y);
00358 
00359     /* Xpm and png do exactly the same thing */
00360     } else {
00361         /* Basically, what it looks like all this does is to try and preserve
00362          * gc's with various clipmasks set. */
00363 
00364         int gcnum,i;
00365         Pixmap mask;
00366         GC gc;
00367         total++;
00368         mask = pixmaps[face]->mask;
00369         /* Lets see if we can find a stored mask with matching gc */
00370         for(gcnum=0;gcnum<XPMGCS;gcnum++) {
00371             if (xpm_masks[gcnum] == mask)
00372                 break;
00373         }
00374         /* Nope - we didn't. set one up, but only temporarily */
00375         if (gcnum == XPMGCS) {
00376             misses++;
00377             gcnum--;
00378             gc = gc_xpm[gcnum];
00379             XSetClipMask(display,gc,mask);
00380         }
00381         gc = gc_xpm[gcnum];
00382         /* Now, we move all the ones up one, and then place the one just used
00383          * at position 0.  Thus, the one in the last position was the least
00384          * used entry
00385          */
00386         for(i=gcnum-1;i>=0;i--) {
00387             xpm_masks[i+1] = xpm_masks[i];
00388             gc_xpm[i+1] = gc_xpm[i];
00389         }
00390         xpm_masks[0] = mask;
00391         gc_xpm[0] = gc;
00392         /* Hopefully, this isn't too costly - needed for the inventory and look
00393          * window drawing code.
00394          */
00395         XSetClipOrigin(display, gc_xpm[0], x - sx , y - sy);
00396         XCopyArea(display, pixmaps[face]->pixmap,
00397             where,gc_xpm[0],
00398             sx,sy,image_size,image_size,x,y);
00399     }
00400 }
00401 
00402 void end_windows(void)
00403 {
00404     XFreeGC(display, gc_root);
00405     XFreeGC(display, gc_game);
00406     XFreeGC(display, gc_copy);
00407     XFreeGC(display, gc_stats);
00408     XFreeGC(display, infodata.gc_info);
00409     XFreeGC(display, inv_list.gc_text);
00410     XFreeGC(display, inv_list.gc_icon);
00411     XFreeGC(display, inv_list.gc_status);
00412     XFreeGC(display, look_list.gc_text);
00413     XFreeGC(display, look_list.gc_icon);
00414     XFreeGC(display, look_list.gc_status);
00415     XFreeGC(display, gc_message);
00416     XFreeGC(display, gc_xpm_object);
00417     XDestroyWindow(display,win_game);
00418     XCloseDisplay(display);
00419 }
00420 
00421 
00422 
00423 
00424 
00425 /***********************************************************************
00426  *
00427  * Stats window functions follow
00428  *
00429  ***********************************************************************/
00430 
00431 static int get_game_display(void) {
00432     XSizeHints gamehint;
00433     int i;
00434 
00435     gamehint.x=INV_WIDTH + WINDOW_SPACING;
00436     gamehint.y=STAT_HEIGHT + WINDOW_SPACING;
00437 
00438     gamehint.width=GAME_WIDTH;
00439     gamehint.height=gamehint.width;
00440 
00441     gamehint.max_width=gamehint.min_width=gamehint.width;
00442     gamehint.max_height=gamehint.min_height=gamehint.height;
00443     gamehint.flags=PPosition | PSize;
00444     win_game=XCreateSimpleWindow(display,win_root,
00445         gamehint.x,gamehint.y,gamehint.width,gamehint.height,2,
00446         background,foreground);
00447     icon=XCreateBitmapFromData(display,win_game,
00448         (_Xconst char *) crossfire_bits,
00449         (unsigned int) crossfire_width, (unsigned int)crossfire_height);
00450     if (want_config[CONFIG_SPLITWIN]) {
00451         allocate_colors(display, win_root, def_screen,
00452             &colormap, discolor);
00453         foreground=discolor[0].pixel;
00454         background=discolor[9].pixel;
00455     } else
00456         XSetWindowColormap(display, win_game, colormap);
00457 
00458     XSetStandardProperties(display,win_game,X_PROG_NAME, X_PROG_NAME,
00459         icon,gargv,gargc, &(gamehint));
00460 
00461     gc_game=XCreateGC(display,win_game,0,0);
00462     XSetForeground(display,gc_game,discolor[0].pixel);
00463     XSetBackground(display,gc_game,discolor[9].pixel);
00464 
00465     XSetGraphicsExposures(display, gc_game, False);
00466     gc_copy=XCreateGC(display,win_game,0,0);
00467     XSetGraphicsExposures(display, gc_game, True);
00468     gc_floor = XCreateGC(display,win_game,0,0);
00469     XSetGraphicsExposures(display, gc_floor, False);
00470     gc_blank = XCreateGC(display,win_game,0,0);
00471     XSetForeground(display,gc_blank,discolor[0].pixel); /*set to black*/
00472     XSetGraphicsExposures(display,gc_blank,False);
00473 
00474     for (i=0; i<XPMGCS; i++) {
00475             gc_xpm[i] = XCreateGC(display, win_game, 0,0);
00476             XSetClipOrigin(display, gc_xpm[i], 0, 0);
00477             XSetGraphicsExposures(display, gc_xpm[i], False);
00478     }
00479     gc_xpm_object = XCreateGC(display,win_game,0,0);
00480     XSetGraphicsExposures(display, gc_xpm_object, False);
00481     XSetClipOrigin(display, gc_xpm_object,0, 0);
00482     xpm_pixmap = XCreatePixmap(display, def_root, image_size, image_size,
00483                 DefaultDepth(display, DefaultScreen(display)));
00484     gc_clear_xpm = XCreateGC(display,xpm_pixmap,0,0);
00485     XSetGraphicsExposures(display,gc_clear_xpm,False);
00486     XSetForeground(display,gc_clear_xpm,discolor[12].pixel); /* khaki */
00487 
00488     XSelectInput(display,win_game,
00489         ButtonPressMask|KeyPressMask|KeyReleaseMask|ExposureMask);
00490     XSetWMProtocols(display, win_game, &wm_delete_window, 1);
00491     XMapRaised(display,win_game);
00492     return 0;
00493 }
00494 
00495 
00496 /******************************************************************************
00497  *
00498  * The functions dealing with the info window follow
00499  *
00500  *****************************************************************************/
00501 
00502 static int get_info_display(void) {
00503     XSizeHints infohint;
00504     int i;
00505 
00506     /* The following could happen if bad values are given. */
00507     if (infodata.maxlines<INFOLINES) infodata.maxlines=INFOLINES;
00508     infohint.x=INV_WIDTH + GAME_WIDTH + WINDOW_SPACING*2;
00509     infohint.y=0;
00510     infohint.width=infodata.width=6+INFOCHARS*FONTWIDTH;
00511     infohint.height=infodata.height=roothint.height;
00512     infodata.maxdisp = roothint.height/FONTHEIGHT;
00513 
00514     infohint.min_width=100;
00515     infohint.min_height=30;
00516     infohint.flags=PPosition | PSize;
00517     infodata.win_info=XCreateSimpleWindow(display, win_root,
00518         infohint.x,infohint.y,infohint.width,infohint.height,2,
00519         foreground,background);
00520     XSetWindowColormap(display, infodata.win_info, colormap);
00521     icon=XCreateBitmapFromData(display,infodata.win_info,
00522         (_Xconst char *) crossfire_bits,
00523         (unsigned int) crossfire_width, (unsigned int)crossfire_height);
00524     XSetStandardProperties(display,infodata.win_info,"Crossfire - text",
00525         "Crosstext",icon,gargv,gargc,&(infohint));
00526     infodata.gc_info=XCreateGC(display,infodata.win_info,0,0);
00527     XSetForeground(display,infodata.gc_info,foreground);
00528     XSetBackground(display,infodata.gc_info,background);
00529 
00530     XSetFont(display,infodata.gc_info,font->fid);
00531 
00532     XSelectInput(display,infodata.win_info,
00533         ButtonPressMask|KeyPressMask|KeyReleaseMask|ExposureMask|
00534         StructureNotifyMask);
00535     XSetWMProtocols(display, infodata.win_info, &wm_delete_window, 1);
00536     XMapRaised(display,infodata.win_info);
00537     if (infodata.maxlines>infodata.maxdisp) infodata.has_scrollbar=1;
00538     infodata.info_chars = (infohint.width/FONTWIDTH)-1;
00539     if (infodata.has_scrollbar) infodata.info_chars -=3;
00540     infodata.max_info_chars=infodata.info_chars;
00541     infodata.data=(InfoLine *) malloc(sizeof(InfoLine) * infodata.maxlines);
00542     infodata.bar_length=infodata.height - 8;
00543     for (i=0; i<infodata.maxlines; i++) {
00544         infodata.data[i].info = malloc(sizeof(char)* (infodata.info_chars+1));
00545         infodata.data[i].info[0]='\0';
00546         infodata.data[i].color=0;
00547     }
00548 
00549     return 0;
00550 }
00551 
00552 static void delete_ch(void) {
00553     if(strlen(cpl.input_text)==0)
00554         return;
00555     cpl.input_text[strlen(cpl.input_text)-1] = '\0';
00556 
00557     /* If not on the first line but backspacing to the front, we need to
00558      * do some special handling.
00559      */
00560     if ((strlen(cpl.input_text)>3) &&
00561       strlen(infodata.data[infodata.infopos].info)<3) {
00562         int line=infodata.infopos-1;
00563         if (line<0) line=infodata.numlines;
00564         strcpy(infodata.data[infodata.infopos].info,
00565                infodata.data[line].info);
00566         infodata.data[infodata.infopos].info[
00567             strlen(infodata.data[infodata.infopos].info)-1]=0;
00568         XDrawImageString(display,infodata.win_info,infodata.gc_info,
00569                 FONTWIDTH, (infodata.infoline+1)*FONTHEIGHT,
00570                 infodata.data[infodata.infopos].info,
00571                 strlen(infodata.data[infodata.infopos].info));
00572     } else {
00573         infodata.data[infodata.infopos].info[
00574             strlen(infodata.data[infodata.infopos].info)-1]=0;
00575         XDrawImageString(display,infodata.win_info,infodata.gc_info,
00576                 (strlen(infodata.data[infodata.infopos].info)+1)*FONTWIDTH,
00577                 (infodata.infoline+1)*FONTHEIGHT," ",1);
00578     }
00579 }
00580 
00581 /* Writes one character to the screen.  Used when player is typing
00582  * stuff we that we want to appear, or used to give prompts.
00583  */
00584 
00585 void write_ch(char key)
00586 {
00587     char c2[2];
00588 
00589     /* Sort of a gross hack, but this gets it so that we actually put
00590      * the command into the buffer.
00591      */
00592     if (key==13) {
00593         /* We turn off command mode for the draw_info call, because
00594          * it has special handling for normal output during command
00595          * mode; but we do this manually now.
00596          */
00597         Input_State old_state = cpl.input_state;
00598         cpl.input_state = Playing;
00599         draw_info(infodata.data[infodata.infopos].info,NDI_BLACK);
00600         cpl.input_state = old_state;
00601         return;
00602     }
00603 
00604     if (infodata.lastcolor!=NDI_BLACK) {
00605         XSetForeground(display,infodata.gc_info,discolor[NDI_BLACK].pixel);
00606         infodata.lastcolor=NDI_BLACK;
00607     }
00608 
00609     if (key == 9) { /* Tab */
00610         /* check for command mode */
00611         if (infodata.data[infodata.infopos].info[0] == '>') {
00612             const char *str = complete_command(infodata.data[infodata.infopos].info+1);
00613 
00614             if (str != NULL) {
00615                 /* +1 so that we keep our > at start of line.  Don't
00616                  * recopy the data on top of ourself.
00617                  */
00618                 strcpy(infodata.data[infodata.infopos].info+1, str);
00619                 strcpy(cpl.input_text, str);
00620             }
00621         }
00622     } else {
00623 
00624         if ((key < 32 || (unsigned char) key > 127) && key != 8)
00625             return;
00626         c2[0] = key;
00627         c2[1] ='\0';
00628 
00629 
00630         if(key==8||key==127) {
00631             /* By backspacking enough, let them get out of command mode */
00632             if (cpl.input_text[0]=='\0' && cpl.input_state==Command_Mode) {
00633                 cpl.input_state=Playing;
00634                 /* Erase the prompt */
00635                 XDrawImageString(display,infodata.win_info,infodata.gc_info,
00636                                  FONTWIDTH,(infodata.infoline+1)*FONTHEIGHT," ",1);
00637             }
00638             delete_ch();
00639             return;
00640         }
00641         /* Give some leeway here */
00642         if(strlen(cpl.input_text)>=(MAX_BUF-15))
00643             return;
00644 
00645         strcat(cpl.input_text,c2);
00646     }
00647 
00648     if(strlen(infodata.data[infodata.infopos].info)>=(infodata.info_chars-2)) {
00649         /* Draw the currently line and scroll down one */
00650 
00651         /* We turn off command mode for the draw_info call, because
00652          * it has special handling for normal output during command
00653          * mode; but we do this manually now.
00654          */
00655         cpl.input_state = Playing;
00656         draw_info(infodata.data[infodata.infopos].info,NDI_BLACK);
00657         cpl.input_state = Command_Mode;
00658         infodata.data[infodata.infopos].info[0]=(((strlen(cpl.input_text)/
00659             infodata.info_chars))%10)+49;
00660         infodata.data[infodata.infopos].info[1]='>';
00661         infodata.data[infodata.infopos].info[2]=0;
00662         XDrawImageString(display,infodata.win_info,infodata.gc_info,
00663                 FONTWIDTH,(infodata.infoline+1)*FONTHEIGHT,
00664                 infodata.data[infodata.infopos].info,
00665                 strlen(infodata.data[infodata.infopos].info));
00666     }
00667 
00668     if (key != 9 ) strcat(infodata.data[infodata.infopos].info,(cpl.no_echo? "?": c2));
00669 
00670     XDrawImageString(display,infodata.win_info,infodata.gc_info,
00671         FONTWIDTH,(infodata.infoline+1)*FONTHEIGHT,
00672         infodata.data[infodata.infopos].info,
00673         strlen(infodata.data[infodata.infopos].info));
00674 }
00675 
00676 
00677 
00678 /* This is similar to draw_info below, but doesn't advance to a new
00679  * line.  Generally, queries use this function to draw the prompt for
00680  * the name, password, etc.
00681  * This also starts from character position 0.  Thus, only 1 call of this
00682  * per a given line is useful
00683  */
00684 
00685 void draw_prompt(const char *str)
00686 {
00687     if (infodata.lastcolor!=NDI_BLACK) {
00688         XSetForeground(display,infodata.gc_info,discolor[NDI_BLACK].pixel);
00689         infodata.lastcolor=NDI_BLACK;
00690     }
00691 
00692     strncpy(infodata.data[infodata.infopos].info,str,infodata.info_chars);
00693     infodata.data[infodata.infopos].info[infodata.info_chars] = '\0';
00694     infodata.data[infodata.infopos].color=NDI_BLACK;
00695     XDrawImageString(display,infodata.win_info,
00696         infodata.gc_info,FONTWIDTH,(infodata.infoline+1)*FONTHEIGHT,
00697         infodata.data[infodata.infopos].info,
00698         strlen(infodata.data[infodata.infopos].info));
00699 }
00700 
00701 /* If redarew is true, draw the scrollbar no matter what */
00702 static void draw_info_scrollbar(int redraw)
00703 {
00704     static int last_length=0, last_y=0;
00705 
00706     if (!infodata.has_scrollbar) return;
00707 
00708     if (infodata.numlines <infodata.maxdisp) {
00709         infodata.bar_size=infodata.bar_length;
00710         infodata.bar_y=0;
00711     }
00712     else {
00713         infodata.bar_size=infodata.bar_length*infodata.maxdisp/infodata.numlines;
00714         infodata.bar_y=infodata.bar_length*(infodata.bar_pos-infodata.maxdisp)/infodata.numlines;
00715         if (infodata.bar_y<0) infodata.bar_y=0;
00716     }
00717 /*
00718     if ((infodata.bar_size+infodata.bar_y)>infodata.bar_length) {
00719             infodata.bar_y=infodata.bar_length-infodata.bar_size;
00720     }
00721 */
00722     if (!redraw && last_length==infodata.bar_size && last_y==infodata.bar_y) return;
00723 
00724     last_y=infodata.bar_y;
00725     last_length=infodata.bar_size;
00726 
00727     /* Note - with the way this is set up, it wouldn't be too hard to make
00728      * the scrollbar color customizable
00729      */
00730     if (infodata.lastcolor!=NDI_BLACK) {
00731       XSetForeground(display,infodata.gc_info,discolor[NDI_BLACK].pixel);
00732       infodata.lastcolor=NDI_BLACK;
00733     }
00734 
00735     XDrawRectangle(display, infodata.win_info,
00736         infodata.gc_info, infodata.width-SCROLLBAR_WIDTH-6,
00737                    3, 20,
00738                    infodata.height -6);
00739     XClearArea(display, infodata.win_info,
00740                    infodata.width-SCROLLBAR_WIDTH-4, 4, 16,
00741                    infodata.bar_length, False);
00742 
00743     XFillRectangle(display, infodata.win_info, infodata.gc_info,
00744         infodata.width - SCROLLBAR_WIDTH-4,4+infodata.bar_y,
00745         16, infodata.bar_size);
00746 }
00747 
00748 /* draw_info adds a line to the info window. */
00749 
00750 void draw_info(const char *str, int color) {
00751   char *cp;
00752   uint16 new_infopos = (infodata.infopos+1)% infodata.maxlines ;
00753   size_t len;
00754 
00755   if(str == (char *) NULL) {
00756     draw_info("[NULL]",color);
00757     return;
00758   }
00759 
00760   if((cp=strchr(str,'\n'))!=NULL) {
00761     /* 4096 is probably way overkill, but 1024 could very well be too small.
00762      * And I don't see the need to malloc and then free this either -
00763      * this is a single user program.
00764      */
00765     char obuf[4096],*buf = obuf;
00766 
00767     strncpy(buf,str, 4095);
00768     do {
00769       if ((cp = strchr(buf, '\n'))) {
00770         *cp='\0';
00771         draw_info(buf,color);
00772         buf = cp +1;
00773       } else
00774         draw_info(buf,color);
00775     } while (cp!=NULL);
00776     return;
00777   }
00778 
00779   /* Lets do the word wrap for messages - MSW */
00780   if ((int)strlen(str) >= infodata.info_chars) {
00781         int i=infodata.info_chars-1;
00782 
00783         /* i=last space (or ')' for armor.  Wrap armor, because
00784         otherwise, the two sets of ()() can be about half the line */
00785         while ((str[--i]!=' ') && (str[i]!=')') && (i!=0)) ;
00786         /* if i==0, string has no space.  Just let it be truncated */
00787         if (i!=0) {
00788             char *buf = (char *)malloc(sizeof(char)*(i+2));
00789             int j;
00790 
00791             i++;        /* want to keep the ')'.  This also keeps
00792                         the space, but that really doesn't matter */
00793             strncpy(buf, str, i);
00794             buf[i]='\0';
00795             draw_info(buf,color);
00796             free(buf);
00797 
00798             for (j=i; j < (int)strlen(str); j++) /* if the wrap portion is */
00799                 if (str[j]!=' ') break;         /* only space, don't wrap it*/
00800             if ((((strlen(str)-i)!=1) || (str[i]!='.')) && (j!=strlen(str)))
00801                 draw_info((str+i),color);
00802             return;
00803         }
00804   }
00805 
00806   /* This is the real code here - stuff above is just formating and making
00807    * it look nice.  This stuff here is actually drawing the code
00808    */
00809 
00810   /* clear the new last line in window */
00811   memset(infodata.data[new_infopos].info, 32, infodata.info_chars-1);
00812   if(cpl.input_state == Command_Mode)
00813   {
00814       /* we copy the last command line to the new last line in window */
00815       strcpy(infodata.data[new_infopos].info, infodata.data[infodata.infopos].info);
00816   }
00817   infodata.data[new_infopos].info[infodata.info_chars] = '\0';
00818 
00819   len = MIN(strlen(str), infodata.info_chars);
00820   memmove(infodata.data[infodata.infopos].info, str, len);
00821   infodata.data[infodata.infopos].info[len] = '\0';
00822   infodata.data[infodata.infopos].color=color;
00823 
00824   /* This area is for scrollbar handling.  The first check is to see if
00825    * the scrollbar is at the very end, if it is, then we don't care about this.
00826    * IF not at the end, then see if it is at the end of the window.  If
00827    * so, increase the bar position so that that view area keeps up with what
00828    * is being drawn.  If we are not at the end of the buffer, then decrease
00829    * the bar position - in this way, we keep the same viewable area visible
00830    * for redraws.
00831    *
00832    * A couple notes:  If jump to end was desired on output, then this
00833    * code just needs to be replaced with a line like infodata.bar_pos=
00834    * infodata.numlines.
00835    * If it is desired for the window to scroll up as new output is printed
00836    * out, then the second case would need to be removed, and a draw_all_info
00837    * call added instead.
00838    */
00839   if (infodata.bar_pos<infodata.maxlines) {
00840         if (infodata.bar_pos==infodata.numlines) {
00841                 infodata.bar_pos++;
00842         }
00843         else if (infodata.numlines==infodata.maxlines) {
00844             infodata.bar_pos--;
00845             if (infodata.bar_pos<infodata.maxdisp)
00846                 infodata.bar_pos=infodata.maxdisp;
00847         }
00848   }
00849   if (infodata.numlines<infodata.maxlines) infodata.numlines++;
00850 
00851   /* Basically, if we don't have a scrollbar, or we are at the end of it,
00852    * then do the drawing stuff, otherwise don't.
00853    */
00854   if (!infodata.has_scrollbar || infodata.bar_pos>=infodata.numlines) {
00855     /*
00856      * The XDrawImageString draws the line.
00857      */
00858     if (infodata.lastcolor!=color) {
00859       XSetForeground(display,infodata.gc_info,discolor[color].pixel);
00860       infodata.lastcolor=color;
00861     }
00862 
00863     XDrawImageString(display,infodata.win_info,
00864       infodata.gc_info,FONTWIDTH,(infodata.infoline+1)*FONTHEIGHT,
00865       infodata.data[infodata.infopos].info,
00866       strlen(infodata.data[infodata.infopos].info));
00867 
00868     /* Now it gets potentially more complicated - now we have to handle
00869      * wrapping and stuff like that.
00870      */
00871 
00872     if(++(infodata.infoline)>=infodata.maxdisp){
00873       if (infodata.scroll_info_window) {
00874         XCopyArea(display,infodata.win_info,infodata.win_info,
00875             infodata.gc_info,0,FONTHEIGHT,infodata.info_chars*FONTWIDTH,
00876             infodata.maxdisp*FONTHEIGHT,0,0);
00877         infodata.infoline--;
00878       }
00879       else
00880         infodata.infoline=0;
00881     }
00882   }
00883 
00884   infodata.infopos = new_infopos;
00885 
00886   if (!infodata.has_scrollbar || infodata.bar_pos>=infodata.numlines) {
00887         if(cpl.input_state == Command_Mode)
00888         {
00889                 uint8 endpos = strlen(infodata.data[infodata.infopos].info);
00890 
00891                 infodata.data[infodata.infopos].info[endpos] = ' ';
00892                 XDrawImageString(display,infodata.win_info,
00893                 infodata.gc_info,FONTWIDTH,(infodata.infoline+1)*FONTHEIGHT,
00894                 infodata.data[infodata.infopos].info, infodata.info_chars-1);
00895                 infodata.data[infodata.infopos].info[endpos] = '\0';
00896         }
00897         else
00898         {
00899                 XDrawImageString(display,infodata.win_info,
00900                 infodata.gc_info,FONTWIDTH,(infodata.infoline+1)*FONTHEIGHT,
00901                 infodata.data[infodata.infopos].info, infodata.info_chars-1);
00902         }
00903   }
00904 
00905   /* If in a reply state, grab the input buffer and store it.
00906    */
00907   if (cpl.input_state==Reply_Many) {
00908     strncpy(infodata.data[infodata.infopos].info, cpl.input_text,
00909             infodata.info_chars);
00910     infodata.data[infodata.infopos].info[infodata.info_chars] = '\0';
00911 
00912     XDrawImageString(display,infodata.win_info,infodata.gc_info,
00913         FONTWIDTH,(infodata.infoline+1)*FONTHEIGHT,
00914         infodata.data[infodata.infopos].info,
00915         strlen(infodata.data[infodata.infopos].info));
00916   }
00917   /* We should have some intelligent checks so it is not drawn unnecessarily */
00918   draw_info_scrollbar(FALSE);
00919 }
00920 
00921 /* This is pretty much print_message, but the name is changed, and some
00922  * unnecessary code has been removed.
00923  */
00924 
00925 void draw_color_info(int colr, const char *buf){
00926     draw_info(buf,colr);
00927 }
00928 
00929 
00930 /*
00931  * draw_all_info is only needed for redraws, which includes scrollbar
00932  * movement
00933  */
00934 
00935 
00936 static void draw_all_info(void) {
00937   int i;
00938 
00939   XClearWindow(display,infodata.win_info);
00940   if (infodata.numlines>=infodata.maxdisp) {
00941      int startline=infodata.infopos-infodata.maxdisp-
00942         (infodata.numlines-infodata.bar_pos)+1,displine=0;
00943 
00944      if (startline<0) startline+=infodata.maxlines;
00945 
00946      /* If we are scrolling back (bar_pos<numlines), then we want to
00947       * start drawing from the top, and want to keep displine 0.
00948       */
00949      if (!infodata.scroll_info_window) {
00950         if (infodata.bar_pos>=infodata.numlines) {
00951                 displine=(infodata.infoline+1) % infodata.maxdisp;
00952                 /* So that when we now that we have scrolled back */
00953 
00954         }
00955         else infodata.infoline=infodata.maxdisp-1;
00956      }
00957 
00958      for (i=0; i<infodata.maxdisp; i++) {
00959         if (infodata.lastcolor!=infodata.data[startline].color) {
00960                 XSetForeground(display,infodata.gc_info,
00961                                discolor[infodata.data[startline].color].pixel);
00962                 infodata.lastcolor=infodata.data[startline].color;
00963         }
00964         XDrawImageString(display,infodata.win_info,
00965             infodata.gc_info,FONTWIDTH,(displine+1)*FONTHEIGHT,
00966             infodata.data[startline].info,
00967             strlen(infodata.data[startline].info));
00968         startline++;
00969         startline %= infodata.maxlines;
00970         displine++;
00971         displine %= infodata.maxdisp;
00972      }
00973   }
00974   else  {
00975       for(i=0;i<=infodata.numlines;i++) {
00976         if (infodata.lastcolor!=infodata.data[i].color)  {
00977                 XSetForeground(display,infodata.gc_info,
00978                                discolor[infodata.data[i].color].pixel);
00979                 infodata.lastcolor=infodata.data[i].color;
00980         }
00981         XDrawImageString(display,infodata.win_info,
00982             infodata.gc_info,FONTWIDTH,(i+1)*FONTHEIGHT,
00983             infodata.data[i].info,strlen(infodata.data[i].info));
00984       }
00985   }
00986   draw_info_scrollbar(TRUE);
00987 }
00988 
00989 static void resize_win_message(int width, int height) {
00990     messagehint.width = width;
00991     messagehint.height = height;
00992 }
00993 
00994 static void resize_win_info(int width, int height) {
00995     int chars=(width/FONTWIDTH)-1;
00996     int lines=(height/FONTHEIGHT)-1;
00997     int i;
00998     InfoLine    *newlines;
00999 
01000     if (infodata.width==width &&
01001         infodata.height==height) return;
01002 
01003     if(chars<3 || lines<3)
01004         return;
01005 
01006     infodata.width=width;
01007     infodata.height=height;
01008     infodata.bar_length=infodata.height - 8;
01009     if (infodata.has_scrollbar) chars-=3;
01010 
01011 
01012     /* We have a scrollback buffer.  All we need to change then is maxdisp */
01013     if (infodata.maxdisp != infodata.maxlines && lines<infodata.maxlines) {
01014         /* Move insert line to bottom of the screen if we are already there
01015          * or it would otherwise be off the screen.
01016          */
01017         if (((infodata.infoline+1) == infodata.maxdisp) ||
01018             (infodata.infoline >= lines)) infodata.infoline = lines - 1;
01019         infodata.maxdisp=lines;
01020     }
01021     /* The window has changed size, but the amount of data we can display
01022      * has not.  so just redraw the window and return.
01023      */
01024     if (chars == infodata.info_chars && lines == infodata.maxdisp) {
01025         draw_all_info();
01026         return;
01027     }
01028 
01029     /* Either we have a scrollbar (as above), or the window has not
01030      * changed in height, so we just need to change the size of the
01031      * buffers.
01032      */
01033     if (lines == infodata.maxdisp) {
01034         for (i=0; i<infodata.maxlines; i++) {
01035             if (chars>infodata.max_info_chars) {
01036                 infodata.data[i].info= realloc(infodata.data[i].info, sizeof(char) * (chars+1));
01037             }
01038             /* Terminate buffer in both cases */
01039             infodata.data[i].info[chars]='\0';
01040         }
01041         infodata.info_chars=chars;
01042         draw_all_info();
01043         return;
01044     }
01045     /* IF we get here, the window has grown or shrunk, and we don't have
01046      * a scrollbar.  This code is a lot simpler than what was here before,
01047      * but probably is not as efficient (But with the number of resize
01048      * events likely, this should not be a big deal).
01049      */
01050 
01051     /* First, allocate new storage */
01052     newlines = malloc(sizeof(InfoLine) * lines);
01053     for (i=0; i<lines; i++) {
01054         newlines[i].info = malloc(sizeof(char) * (chars +1));
01055         newlines[i].info[0]='\0';
01056         newlines[i].color=0;
01057     }
01058     /* First case - we can keep all the old data.  Note that the old
01059      * buffer could have been filled up, so we still need to do some
01060      * checking to find the start
01061      */
01062     if (infodata.numlines <= lines) {
01063         int start=0,k;
01064 
01065         /* Buffer was full, so the start could be someplace else */
01066         if (infodata.numlines == infodata.maxlines) {
01067             start = infodata.infopos+1;
01068         }
01069         for (i=0; i<infodata.numlines; i++) {
01070             k= (start+i) % infodata.maxlines;
01071             strncpy(newlines[i].info, infodata.data[k].info, chars);
01072             newlines[i].info[chars]=0;
01073             newlines[i].color = infodata.data[k].color;
01074         }
01075     }
01076     else {
01077         /* We have to lose data, so keep the most recent. */
01078 
01079         int start=infodata.infopos-lines,k;
01080 
01081         if (start<0) start += infodata.maxlines;
01082         for (i=0; i<lines; i++) {
01083             k= (start+i) % infodata.maxlines;
01084             strncpy(newlines[i].info, infodata.data[k].info, chars);
01085             newlines[i].info[chars]=0;
01086             newlines[i].color = infodata.data[k].color;
01087         }
01088         infodata.infopos = 0;
01089         newlines[0].info[0] = '\0';
01090         infodata.infoline = lines-1;
01091         infodata.numlines = lines;
01092         infodata.bar_pos = lines;
01093     }
01094     infodata.maxdisp = lines;
01095     for (i=0; i<infodata.maxlines; i++) {
01096         free(infodata.data[i].info);
01097     }
01098     free(infodata.data);
01099     infodata.data = newlines;
01100     infodata.maxlines = lines;
01101     infodata.info_chars=chars;
01102     draw_all_info();
01103 }
01104 
01105 
01106 
01107 
01108 /***********************************************************************
01109  *
01110  * Stats window functions follow
01111  *
01112  ***********************************************************************/
01113 
01114 static int get_stats_display(void) {
01115     XSizeHints stathint;
01116 
01117     stathint.x=INV_WIDTH + WINDOW_SPACING;
01118     stathint.y=0;
01119     stathint.width=GAME_WIDTH;
01120     stathint.height=STAT_HEIGHT;
01121     stathint.min_width=stathint.max_width=stathint.width;
01122     stathint.min_height=stathint.max_height=stathint.height;
01123     stathint.flags=PPosition | PSize;
01124     win_stats=XCreateSimpleWindow(display,win_root,
01125         stathint.x,stathint.y,stathint.width,stathint.height,2,
01126         foreground,background);
01127     XSetWindowColormap(display, win_stats, colormap);
01128     icon=XCreateBitmapFromData(display,win_stats,
01129         (_Xconst char *) crossfire_bits,
01130         (unsigned int) crossfire_width, (unsigned int)crossfire_height);
01131     XSetStandardProperties(display,win_stats,"Crossfire - status",
01132         "crosstatus",icon,gargv,gargc, &(stathint));
01133 
01134     gc_stats=XCreateGC(display,win_stats,0,0);
01135     XSetForeground(display,gc_stats,foreground);
01136     XSetBackground(display,gc_stats,background);
01137     XSetFont(display,gc_stats,font->fid);
01138     XSelectInput(display,win_stats,KeyPressMask|KeyReleaseMask|ExposureMask);
01139     XMapRaised(display,win_stats);
01140     XSetWMProtocols(display, win_stats, &wm_delete_window, 1);
01141    return 0;
01142 }
01143 
01144 /* This draws the stats window.  If redraw is true, it means
01145  * we need to redraw the entire thing, and not just do an
01146  * updated.
01147  */
01148 
01149 void draw_stats(int redraw) {
01150   char buff[MAX_BUF];
01151   static char last_name[MAX_BUF]="", last_range[MAX_BUF]="";
01152   int i;
01153   char *s;
01154 
01155     if (strcmp(cpl.title, last_name) || redraw) {
01156         strcpy(last_name,cpl.title);
01157         strcpy(buff,cpl.title);
01158         strcat(buff,"                     ");
01159         XDrawImageString(display,win_stats,
01160             gc_stats,10,10, buff,strlen(buff));
01161     }
01162 
01163     if(redraw || cpl.stats.exp!=last_stats.exp ||
01164       cpl.stats.level!=last_stats.level) {
01165         sprintf(buff,"Score: %5" FMT64 "  Level: %d",cpl.stats.exp,
01166             cpl.stats.level);
01167         strcat(buff,"                     ");
01168         XDrawImageString(display,win_stats,
01169             gc_stats,10,24, buff,strlen(buff));
01170 
01171         last_stats.exp = cpl.stats.exp;
01172         last_stats.level = cpl.stats.level;
01173       }
01174 
01175     if(redraw ||
01176        cpl.stats.hp!=last_stats.hp || cpl.stats.maxhp!=last_stats.maxhp ||
01177        cpl.stats.sp!=last_stats.sp || cpl.stats.maxsp!=last_stats.maxsp ||
01178        cpl.stats.grace!=last_stats.grace ||
01179        cpl.stats.maxgrace!=last_stats.maxgrace) {
01180 
01181         sprintf(buff,"Hp %d/%d  Sp %d/%d Gr %d/%d",
01182             cpl.stats.hp, cpl.stats.maxhp,
01183             cpl.stats.sp, cpl.stats.maxsp,
01184             cpl.stats.grace, cpl.stats.maxgrace);
01185 
01186         strcat(buff,"                     ");
01187         XDrawImageString(display,win_stats,
01188             gc_stats,10,38, buff,strlen(buff));
01189         last_stats.hp=cpl.stats.hp;
01190         last_stats.maxhp=cpl.stats.maxhp;
01191         last_stats.sp=cpl.stats.sp;
01192         last_stats.maxsp=cpl.stats.maxsp;
01193         last_stats.grace=cpl.stats.grace;
01194         last_stats.maxgrace=cpl.stats.maxgrace;
01195     }
01196 
01197     if(redraw || cpl.stats.Dex!=last_stats.Dex ||
01198       cpl.stats.Con!=last_stats.Con || cpl.stats.Str!=last_stats.Str ||
01199       cpl.stats.Int!=last_stats.Int || cpl.stats.Wis!=last_stats.Wis ||
01200       cpl.stats.Cha!=last_stats.Cha || cpl.stats.Pow!=last_stats.Pow) {
01201 
01202         sprintf(buff,"S%2d D%2d Co%2d I%2d W%2d P%2d Ch%2d",
01203             cpl.stats.Str,cpl.stats.Dex,cpl.stats.Con,
01204             cpl.stats.Int,cpl.stats.Wis,cpl.stats.Pow,
01205             cpl.stats.Cha);
01206 
01207         strcat(buff,"                     ");
01208         XDrawImageString(display,win_stats,
01209             gc_stats,10,52, buff,strlen(buff));
01210 
01211         last_stats.Str=cpl.stats.Str;
01212         last_stats.Con=cpl.stats.Con;
01213         last_stats.Dex=cpl.stats.Dex;
01214         last_stats.Int=cpl.stats.Int;
01215         last_stats.Wis=cpl.stats.Wis;
01216         last_stats.Cha=cpl.stats.Cha;
01217         last_stats.Pow=cpl.stats.Pow;
01218       }
01219 
01220     if(redraw || cpl.stats.wc!=last_stats.wc ||
01221       cpl.stats.ac!=last_stats.ac ||
01222       cpl.stats.resists[0]!=last_stats.resists[0] ||
01223       cpl.stats.dam!=last_stats.dam) {
01224 
01225         sprintf(buff,"Wc:%3d Dam:%3d Ac:%3d Arm:%3d",
01226             cpl.stats.wc,cpl.stats.dam,cpl.stats.ac,
01227             cpl.stats.resists[0]);
01228         strcat(buff,"                     ");
01229         XDrawImageString(display,win_stats,
01230             gc_stats,10,66, buff,strlen(buff));
01231 
01232         last_stats.wc=cpl.stats.wc;
01233         last_stats.ac=cpl.stats.ac;
01234         last_stats.dam=cpl.stats.dam;
01235         last_stats.resists[0] = cpl.stats.resists[0];
01236       }
01237 
01238   if(redraw || last_stats.speed!=cpl.stats.speed ||
01239      cpl.stats.food!=last_stats.food ||
01240      cpl.stats.weapon_sp != last_stats.weapon_sp) {
01241         /* since both speed and weapon speed have been multiplied by
01242          * the same value, to get proper weapon, we only need to divide
01243          * by speed - the multiplication/division factor on both factors
01244          * out.
01245          */
01246         double weap_sp;
01247 
01248         /* Seems that weapon_sp can be 0 in some cases which caused SIGFPE's */
01249         if (cpl.stats.weapon_sp ==0) weap_sp = 0;
01250         else weap_sp = (float) cpl.stats.speed/ ((float)cpl.stats.weapon_sp);
01251 
01252         /* The following is generating an FPE on alpha systems - changed
01253          * everything to be doubles to see if that might make some
01254          * difference.
01255          */
01256         if(cpl.stats.food<100 && (cpl.stats.food&4)) {
01257             sprintf(buff,"Speed: %3.2f (%1.2f) Food: *%d* HUNGRY!",
01258                 (double)cpl.stats.speed/FLOAT_MULTF,
01259                 weap_sp,cpl.stats.food);
01260             if (use_config[CONFIG_FOODBEEP] && (cpl.stats.food%4==3)) XBell(display, 0);
01261         } else {
01262             sprintf(buff,"Speed: %3.2f (%1.2f)  Food: %3d",
01263                 (float)cpl.stats.speed/FLOAT_MULTF,
01264                 weap_sp, cpl.stats.food);
01265             if (use_config[CONFIG_FOODBEEP] && cpl.stats.food<1) XBell(display,100);
01266         }
01267 
01268         strcat(buff,"                     ");
01269         XDrawImageString(display,win_stats,
01270             gc_stats,10,80, buff,strlen(buff));
01271 
01272         last_stats.food=cpl.stats.food;
01273         last_stats.speed = cpl.stats.speed;
01274         last_stats.weapon_sp = cpl.stats.weapon_sp;
01275      }
01276     if (redraw || strcmp(cpl.range, last_range)) {
01277         strcpy(last_range, cpl.range);
01278         strcpy(buff,cpl.range);
01279         strcat(buff,"                     ");
01280         XDrawImageString(display,win_stats,
01281             gc_stats,10,94, buff,strlen(buff));
01282     }
01283 
01284     const int skills_per_line = 2;
01285         int on_skill=1;
01286 
01287         *buff = '\0';
01288         s = buff;
01289         for(i = 0; last_used_skills[i] >= 0; ++i)
01290         {
01291            int skill_id = last_used_skills[i];
01292 
01293             if (!skill_names[skill_id] || !cpl.stats.skill_exp[skill_id]) continue;
01294 
01295             last_stats.skill_level[skill_id] = cpl.stats.skill_level[skill_id];
01296             last_stats.skill_exp[skill_id] = cpl.stats.skill_exp[skill_id];
01297             s += sprintf(s,"%.3s: %7" FMT64 " (%d) ", skill_names[skill_id], cpl.stats.skill_exp[skill_id],
01298                 cpl.stats.skill_level[skill_id]);
01299             if ((on_skill % skills_per_line) == 0) {
01300                 XDrawImageString(display,win_stats,gc_stats,10,
01301                                 108 + (14 * (on_skill / skills_per_line)), buff,strlen(buff));
01302                 *buff = '\0';
01303                 s = buff;
01304             }
01305             on_skill++;
01306         }
01307         if (*buff)
01308             XDrawImageString(display,win_stats,gc_stats,10,
01309                         108 + (14 * (on_skill / skills_per_line)), buff,strlen(buff));
01310     }
01311 
01312 /***********************************************************************
01313 *
01314 * Handles the message window
01315 *
01316 ***********************************************************************/
01317 
01318 static int get_message_display(void) {
01319 
01320     messagehint.x=INV_WIDTH + WINDOW_SPACING;
01321     /* Game window is square so we can use the width */
01322     messagehint.y=GAME_WIDTH + STAT_HEIGHT+WINDOW_SPACING*2;
01323     messagehint.width=GAME_WIDTH;
01324     messagehint.height=roothint.height - messagehint.y;
01325     messagehint.max_width=messagehint.min_width=messagehint.width;
01326     messagehint.max_height=STAT_HEIGHT;
01327     messagehint.min_height=messagehint.height;
01328     messagehint.flags=PPosition | PSize;
01329     win_message=XCreateSimpleWindow(display,win_root,
01330         messagehint.x,messagehint.y,messagehint.width,
01331         messagehint.height,2,foreground,background);
01332     XSetWindowColormap(display, win_message, colormap);
01333     icon=XCreateBitmapFromData(display,win_message,
01334         (_Xconst char *) crossfire_bits,
01335         (unsigned int) crossfire_width, (unsigned int)crossfire_height);
01336     XSetStandardProperties(display,win_message,"Crossfire - vitals",
01337         "crossvitals",icon, gargv,gargc,&(messagehint));
01338     gc_message=XCreateGC(display,win_message,0,0);
01339     XSetForeground(display,gc_message,foreground);
01340     XSetBackground(display,gc_message,background);
01341     XSetFont(display,gc_message,font->fid);
01342     XSelectInput(display,win_message,
01343                ButtonPressMask|KeyPressMask|KeyReleaseMask|ExposureMask);
01344     XSetWMProtocols(display, win_message, &wm_delete_window, 1);
01345     XMapRaised(display,win_message);
01346    return 0;
01347 }
01348 
01349 static void xwritedown(const char *txt, int x) {
01350   int y=13;
01351   for(;*txt!='\0';txt++,y+=13)
01352     XDrawImageString(display,win_message,
01353                      look_list.gc_text,x,y,txt,1);
01354 }
01355 
01356 #define MAX_BARS_MESSAGE 80
01357 
01358 static void draw_stat_bar(int bar_pos, int height, int is_alert)
01359 {
01360   if(height!=MAX_BARS_MESSAGE)  /* clear the top of the bar */
01361     XClearArea(display,win_message, bar_pos, 4,
01362         10, MAX_BARS_MESSAGE-height, 0);
01363 
01364   if(height==0)                 /* empty bar */
01365     return;
01366 
01367   if(is_alert) /* this should have its own gc */
01368         XSetForeground(display,look_list.gc_text,
01369                         discolor[3].pixel);
01370 
01371   XFillRectangle(display,win_message,
01372          look_list.gc_text, bar_pos, 4+MAX_BARS_MESSAGE-height, 10, height);
01373 
01374   if(is_alert)
01375          XSetForeground(display,look_list.gc_text,
01376                         foreground);
01377 }
01378 
01379 
01380 /* This updates the status bars.  If redraw, then redraw them
01381  * even if they have not changed
01382  */
01383 
01384 void draw_message_window(int redraw) {
01385     int bar,is_alert,flags;
01386     static uint16 oldflags=0;
01387     static uint16 scrollsize_hp=0, scrollsize_sp=0, scrollsize_food=0,
01388         scrollsize_grace=0;
01389     static uint8 scrollhp_alert=FALSE, scrollsp_alert=FALSE,
01390         scrollfood_alert=FALSE, scrollgrace_alert=FALSE;
01391 
01392     /* draw hp bar */
01393     if(cpl.stats.maxhp>0)
01394     {
01395         bar=(cpl.stats.hp*MAX_BARS_MESSAGE)/cpl.stats.maxhp;
01396         if(bar<0)
01397             bar=0;
01398         is_alert=(cpl.stats.hp <= cpl.stats.maxhp/4);
01399     }
01400     else
01401     {
01402         bar=0;
01403         is_alert=0;
01404     }
01405     if (redraw || scrollsize_hp!=bar || scrollhp_alert!=is_alert)
01406         draw_stat_bar(20, bar, is_alert);
01407     scrollsize_hp=bar;
01408     scrollhp_alert=is_alert;
01409 
01410     /* draw sp bar.  spellpoints can go above max
01411      * spellpoints via supercharging with the transferrance spell,
01412      * or taking off items that raise max spellpoints.
01413      */
01414     if (cpl.stats.sp>cpl.stats.maxsp)
01415         bar = MAX_BARS_MESSAGE;
01416     else
01417         bar=(cpl.stats.sp*MAX_BARS_MESSAGE)/cpl.stats.maxsp;
01418     if(bar<0)
01419         bar=0;
01420 
01421     is_alert=(cpl.stats.sp <= cpl.stats.maxsp/4);
01422 
01423     if (redraw || scrollsize_sp!=bar || scrollsp_alert!=is_alert)
01424         draw_stat_bar(60, bar, is_alert);
01425 
01426     scrollsize_sp=bar;
01427     scrollsp_alert=is_alert;
01428 
01429     /* draw sp bar. grace can go above max or below min */
01430     if (cpl.stats.grace>cpl.stats.maxgrace)
01431         bar = MAX_BARS_MESSAGE;
01432     else
01433         bar=(cpl.stats.grace*MAX_BARS_MESSAGE)/cpl.stats.maxgrace;
01434     if(bar<0)
01435         bar=0;
01436 
01437     is_alert=(cpl.stats.grace <= cpl.stats.maxgrace/4);
01438 
01439     if (redraw || scrollsize_grace!=bar || scrollgrace_alert!=is_alert)
01440         draw_stat_bar(100, bar, is_alert);
01441 
01442     scrollsize_grace=bar;
01443     scrollgrace_alert=is_alert;
01444 
01445     /* draw food bar */
01446     bar=(cpl.stats.food*MAX_BARS_MESSAGE)/999;
01447     if(bar<0)
01448         bar=0;
01449     is_alert=(cpl.stats.food <= 999/4);
01450 
01451     if (redraw || scrollsize_food!=bar || scrollfood_alert!=is_alert)
01452         draw_stat_bar(140, bar, is_alert);
01453 
01454     scrollsize_food=bar;
01455     scrollfood_alert=is_alert;
01456 
01457     flags = cpl.stats.flags;
01458 
01459     if (cpl.fire_on) flags |= SF_FIREON;
01460     if (cpl.run_on) flags |= SF_RUNON;
01461 
01462     if ((flags & SF_FIREON ) != (oldflags & SF_FIREON)) {
01463         if (flags & SF_FIREON)
01464             XDrawImageString(display, win_message,
01465                              look_list.gc_text, 180, 15, "Fire On", 7);
01466         else
01467             XClearArea(display, win_message,
01468                    180, 0, 60, 15, False);
01469     }
01470     if ((flags & SF_RUNON ) != (oldflags & SF_RUNON)) {
01471         if (flags & SF_RUNON)
01472             XDrawImageString(display, win_message,
01473                              look_list.gc_text, 180, 30, "Run On", 6);
01474         else
01475             XClearArea(display, win_message,
01476                    180, 15, 60, 15, False);
01477     }
01478     oldflags = flags;
01479     if (redraw || cpl.stats.resist_change) {
01480         int x=180, y=45,i;
01481         char buf[40];
01482 
01483         cpl.stats.resist_change=0;
01484         XClearArea(display, win_message, 180, 30, messagehint.width-180, messagehint.height-30, False);
01485         for (i=0; i<NUM_RESISTS; i++) {
01486             if (cpl.stats.resists[i]) {
01487                 XDrawImageString(display, win_message,
01488                          look_list.gc_text, x, y, resists_name[i],
01489                          strlen(resists_name[i]));
01490                 sprintf(buf,"%+4d", cpl.stats.resists[i]);
01491                 XDrawImageString(display, win_message,
01492                          look_list.gc_text, x+40, y, buf, strlen(buf));
01493                 y+=15;
01494                 /* Move to the next draw position.  If we run
01495                  * out of space, just break out of the function.
01496                  */
01497                 if (y>messagehint.height) break;
01498             } /* If we have a resistance with value */
01499         } /* For loop of resistances */
01500     } /* If we need to draw the resistances */
01501 }
01502 
01503 
01504 static void draw_all_message(void) {
01505 
01506   XClearWindow(display,win_message);
01507   xwritedown("HP",06);
01508   XDrawRectangle(display,win_message,
01509                  look_list.gc_text,18,2,14,MAX_BARS_MESSAGE+4);
01510   xwritedown("Mana",46);
01511   XDrawRectangle(display,win_message,
01512                  look_list.gc_text,58,2,14,MAX_BARS_MESSAGE+4);
01513   xwritedown("Grace",86);
01514   XDrawRectangle(display,win_message,
01515                  look_list.gc_text,98,2,14,MAX_BARS_MESSAGE+4);
01516   xwritedown("Food",126);
01517   XDrawRectangle(display,win_message,
01518                  look_list.gc_text,138,2,14,MAX_BARS_MESSAGE+4);
01519   draw_message_window(1);
01520 }
01521 
01522 /****************************************************************************
01523  *
01524  * Inventory window functions follow
01525  *
01526  ****************************************************************************/
01527 
01528 
01529 static void draw_status_icon(itemlist *l, int x, int y, int face)
01530 {
01531     XCopyPlane(display, icons[face], l->win, l->gc_status, 0,0, 24,6, x,y, 1);
01532 }
01533 
01534 
01535 /* compares the 'flags' against the item.  return 1 if we should draw
01536  * that object, 0 if it should not be drawn.
01537  */
01538 
01539 static int show_object(item *ip, inventory_show flags)
01540 {
01541     if (flags==show_all) return 1;
01542     if ((flags & show_applied) && (ip->applied)) return 1;
01543     if ((flags & show_unapplied) && (!ip->applied)) return 1;
01544     if ((flags & show_unpaid) && (ip->unpaid)) return 1;
01545     if ((flags & show_cursed) && (ip->cursed || ip->damned)) return 1;
01546     if ((flags & show_magical) && (ip->magical)) return 1;
01547     if ((flags & show_nonmagical) && (!ip->magical)) return 1;
01548     if ((flags & show_locked) && (ip->locked)) return 1;
01549     if ((flags & show_unlocked) && (!ip->locked)) return 1;
01550 
01551     /* Doesn't match - probalby don't want it then */
01552     return 0;
01553 }
01554 
01555 /* This will need to be changed to do the 'right thing' for different display
01556  * modes (use bitmap data if we are using bitmaps for the game.
01557  */
01558 
01559 static void create_status_icons(void)
01560 {
01561 #include "pixmaps/clear.xbm"
01562 #include "pixmaps/locked.xbm"
01563 #include "pixmaps/applied.xbm"
01564 #include "pixmaps/unpaid.xbm"
01565 #include "pixmaps/damned.xbm"
01566 #include "pixmaps/cursed.xbm"
01567 #include "pixmaps/magic.xbm"
01568 #include "pixmaps/close.xbm"
01569 #include "pixmaps/stipple.111"
01570 #include "pixmaps/stipple.112"
01571 
01572     static int hasinit=0;
01573     int x, y;
01574     GC tmpgc;
01575 
01576     if (hasinit) return;
01577     hasinit=1;
01578 
01579 #define CREATEPM(name,data) \
01580     (icons[name] = XCreateBitmapFromData(display, def_root,\
01581                             data##_bits, data##_width, data##_height))
01582 
01583     if (0
01584         || CREATEPM(no_icon, clear) == None
01585         || CREATEPM(locked_icon, locked) == None
01586         || CREATEPM(applied_icon, applied) == None
01587         || CREATEPM(unpaid_icon, unpaid) == None
01588         || CREATEPM(damned_icon, damned) == None
01589         || CREATEPM(cursed_icon, cursed) == None
01590         || CREATEPM(magic_icon, magic) == None
01591         || CREATEPM(close_icon, close) == None
01592         || CREATEPM(stipple1_icon, stipple) == None
01593         || CREATEPM(stipple2_icon, stipple1) == None
01594     )
01595     {
01596         fprintf(stderr, "Unable to create pixmaps.\n");
01597         exit (0);
01598     }
01599     dark1 = XCreatePixmap(display, def_root, image_size, image_size, 1);
01600     dark2 = XCreatePixmap(display, def_root, image_size, image_size, 1);
01601     dark3 = XCreatePixmap(display, def_root, image_size, image_size, 1);
01602     tmpgc = XCreateGC(display, dark1, 0, 0);
01603     XSetFillStyle(display, tmpgc, FillSolid);
01604     XSetForeground(display, tmpgc, 1);
01605     XFillRectangle(display, dark1, tmpgc, 0, 0, image_size, image_size);
01606     XFillRectangle(display, dark2, tmpgc, 0, 0, image_size, image_size);
01607     XFillRectangle(display, dark3, tmpgc, 0, 0, image_size, image_size);
01608     XSetForeground(display, tmpgc, 0);
01609     for (x=0; x<image_size; x++) {
01610         for (y=0; y<image_size; y++) {
01611             /* we just fill in points every X pixels - dark1 is the darkest, dark3 is the li!
01612              * dark1 has 50% of the pixels filled in, dark2 has 33%, dark3 has 25%
01613              * The formula's here are not perfect - dark2 will not match perfectly with an
01614              * adjacent dark2 image.  dark3 results in diagonal stripes.  OTOH, these will
01615              * change depending on the image size.
01616              */
01617             if ((x+y) % 2) {
01618                 XDrawPoint(display, dark1, tmpgc, x, y);
01619             }
01620             if ((x+y) %3) {
01621                 XDrawPoint(display, dark2, tmpgc, x, y);
01622             }
01623             if ((x+y) % 4) {
01624                 XDrawPoint(display, dark3, tmpgc, x, y);
01625             }
01626         }
01627     }
01628     XFreeGC(display, tmpgc);
01629 }
01630 
01631 /*
01632  *  draw_list redraws changed item to the window and updates a scrollbar
01633  */
01634 static void draw_list (itemlist *l)
01635 {
01636     int i, items, size, pos;
01637     item *tmp;
01638     char buf[MAX_BUF], buf2[MAX_BUF];
01639 
01640     /* draw title */
01641     strcpy(buf2, l->title);
01642     if (l->show_what & show_mask) {
01643         strcat(buf2," (");
01644         if (l->show_what & show_applied) strcat(buf2,"applied, ");
01645         if (l->show_what & show_unapplied) strcat(buf2,"unapplied, ");
01646         if (l->show_what & show_unpaid) strcat(buf2,"unpaid, ");
01647         if (l->show_what & show_cursed) strcat(buf2,"cursed, ");
01648         if (l->show_what & show_magical) strcat(buf2,"magical, ");
01649         if (l->show_what & show_nonmagical) strcat(buf2,"nonmagical, ");
01650         if (l->show_what & show_locked) strcat(buf2,"locked, ");
01651         if (l->show_what & show_unlocked) strcat(buf2,"unlocked, ");
01652         /* want to kill the comma we put in above.  Replace it with the paren */
01653         buf2[strlen(buf2)-2]=')';
01654         buf2[strlen(buf2)-1]='\0';
01655     }
01656     if(l->env->weight < 0 || l->show_weight == 0)
01657         sprintf (buf, l->format_n, buf2);
01658     else if (!l->weight_limit)
01659         sprintf (buf, l->format_nw, buf2, l->env->weight);
01660     else
01661         sprintf(buf, l->format_nwl, buf2, l->env->weight,
01662                 l->weight_limit/1000);
01663 
01664     if (strcmp (buf, l->old_title)) {
01665         XCopyPlane(display, icons[l->env->open ? close_icon : no_icon],
01666                             l->win, l->gc_status, 0,0, image_size,13, 2,2, 1);
01667         strcpy (l->old_title, buf);
01668         XDrawImageString(display, l->win, l->gc_text,
01669                          (l->show_icon ? image_size+24+4 : image_size+4),
01670                          13, buf, strlen(buf));
01671     }
01672 
01673     /* Find how many objects we should draw. */
01674 
01675     for(tmp = l->env->inv, items=0; tmp ;tmp=tmp->next)
01676         if (show_object(tmp, l->show_what)) items++;
01677 
01678     if(l->item_pos > items - l->size)
01679         l->item_pos = items - l->size;
01680     if(l->item_pos < 0)
01681         l->item_pos = 0;
01682 
01683     /* Fast forward to the appropriate item location */
01684 
01685     for(tmp = l->env->inv, i=l->item_pos; tmp && i; tmp=tmp->next)
01686         if (show_object(tmp, l->show_what)) i--;
01687 
01688     for(i=0; tmp && i < l->size; tmp=tmp->next) {
01689         if (!show_object(tmp, l->show_what)) continue;
01690         /* draw face */
01691         if(l->faces[i] != tmp->face) {
01692             l->faces[i] = tmp->face;
01693             XClearArea(display, l->win, 4, 16 + image_size * i,
01694                        image_size, image_size, False);
01695             gen_draw_face (l->win, tmp->face,4, 16 + image_size * i, 0, 0);
01696         }
01697         /* draw status icon */
01698         if (l->show_icon) {
01699             sint8 tmp_icon;
01700             tmp_icon = tmp->locked ? locked_icon : no_icon;
01701             if (l->icon1[i] != tmp_icon) {
01702                 l->icon1[i] = tmp_icon;
01703                 draw_status_icon (l, image_size+4, 16 + image_size * i, tmp_icon);
01704             }
01705             tmp_icon = tmp->applied ? applied_icon :
01706                        tmp->unpaid  ? unpaid_icon : no_icon;
01707             if (l->icon2[i] != tmp_icon) {
01708                 l->icon2[i] = tmp_icon;
01709                 draw_status_icon (l, image_size+4, 22 + image_size * i, tmp_icon);
01710             }
01711             tmp_icon = tmp->magical ? magic_icon : no_icon;
01712             if (l->icon3[i] != tmp_icon) {
01713                 l->icon3[i] = tmp_icon;
01714                 draw_status_icon (l, image_size+4, 28 + image_size * i, tmp_icon);
01715             }
01716             tmp_icon = tmp->damned ? damned_icon :
01717                        tmp->cursed ? cursed_icon : no_icon;
01718             if (l->icon4[i] != tmp_icon) {
01719                 l->icon4[i] = tmp_icon;
01720                 draw_status_icon (l, image_size+4, 34 + image_size * i, tmp_icon);
01721             }
01722         }
01723         /* draw name */
01724         strcpy (buf2, tmp->d_name);
01725         if (l->show_icon == 0)
01726             strcat (buf2, tmp->flags);
01727 
01728         if(tmp->weight < 0 || l->show_weight == 0)
01729             sprintf(buf, l->format_n, buf2);
01730         else
01731             sprintf(buf, l->format_nw, buf2, tmp->nrof * tmp->weight);
01732 
01733         if(!l->names[i] || strcmp(buf, l->names[i])) {
01734             copy_name (l->names[i], buf);
01735             XDrawImageString(display, l->win, l->gc_text,
01736                              (l->show_icon?image_size+24+4:image_size+4),
01737                              34 + image_size * i, buf, strlen(buf));
01738         }
01739         i++;
01740     }
01741 
01742     /* If there are not enough items to fill in the display area,
01743      * then set the unused ares to nothing.
01744      */
01745     if(items < l->item_used) {
01746         XClearArea(display, l->win, 0, 16 + image_size * i, l->width - 23,
01747                    image_size * (l->size - i) , False);
01748         while (i < l->item_used) {
01749             copy_name (l->names[i], "");
01750             l->faces[i] = 0;
01751             l->icon1[i] = l->icon2[i] = l->icon3[i] = l->icon4[i] = 0;
01752             i++;
01753         }
01754     }
01755     l->item_used = items > l->size ? l->size : items;
01756 
01757     /* draw the scrollbar now */
01758     if(items < l->size)
01759         items = l->size;
01760 
01761     size = (long) l->bar_length * l->size / items;
01762     pos = (long) l->bar_length * l->item_pos / items;
01763 
01764     if(l->bar_size != size || pos != l->bar_pos) {
01765 
01766         XClearArea(display, l->win, l->width-20, 17 + l->bar_pos, 16,
01767                    l->bar_size, False);
01768         l->bar_size = size;
01769         l->bar_pos  = pos;
01770 
01771         XFillRectangle(display, l->win, l->gc_text, l->width - 20,
01772                        17 + l->bar_pos, 16, l->bar_size);
01773     }
01774 }
01775 
01776 /*
01777  * draw_all_list clears a window and after that draws all objects
01778  * and a scrollbar
01779  */
01780 static void draw_all_list(itemlist *l)
01781 {
01782     int i;
01783 
01784     strcpy (l->old_title, "");
01785 
01786     for(i=0; i<l->size; i++) {
01787         copy_name(l->names[i], "");
01788         l->faces[i] = 0;
01789         l->icon1[i] = 0;
01790         l->icon2[i] = 0;
01791         l->icon3[i] = 0;
01792         l->icon4[i] = 0;
01793     }
01794 
01795     XClearWindow(display, l->win);
01796     XDrawRectangle(display, l->win, l->gc_text, l->width - 22, 15, 20,
01797                    l->bar_length + 4);
01798 
01799 #if 0
01800     /* Don't reset these - causes window position to reset too often */
01801     l->item_pos = 0;
01802     l->item_used = 0;
01803 #endif
01804     l->bar_size = 1;    /* so scroll bar is drawn */
01805     draw_list (l);
01806 }
01807 
01808 
01809 /* we have received new images.  update these only */
01810 static void update_icons_list(itemlist *l)
01811 {
01812     int i;
01813     for (i = 0; i < l->size; ++i)
01814         l->faces[i] = 0;
01815     draw_list(l);
01816 }
01817 
01818 
01819 void open_container (item *op)
01820 {
01821     look_list.env = op;
01822     sprintf (look_list.title, "%s:", op->d_name);
01823     draw_list (&look_list);
01824 }
01825 
01826 void close_container (item *op)
01827 {
01828     look_list.env = cpl.below;
01829     strcpy (look_list.title, "You see:");
01830     draw_list (&look_list);
01831 }
01832 
01833 static void resize_list_info(itemlist *l, int w, int h)
01834 {
01835     int i;
01836 
01837     free(l->faces);
01838     free(l->icon1);
01839     free(l->icon2);
01840     free(l->icon3);
01841     free(l->icon4);
01842     if (l->names) {
01843         for (i=0; i < l->size; i++)
01844             if (l->names[i])
01845                 free(l->names[i]);
01846         free(l->names);
01847     }
01848     l->width  = w;
01849     l->height = h;
01850     l->size = (l->height - FONTHEIGHT - 8) / image_size;
01851     l->text_len = (l->width - (l->show_icon ? 84 : 60)) / FONTWIDTH;
01852     l->bar_length = l->size * image_size;
01853     sprintf (l->format_nw, "%%-%d.%ds%%6.1f", l->text_len-6, l->text_len-6);
01854     sprintf (l->format_nwl, "%%-%d.%ds%%6.1f/%%4d", l->text_len-11, l->text_len-11);
01855     sprintf (l->format_n, "%%-%d.%ds", l->text_len, l->text_len);
01856 
01857     if ((l->faces = malloc (sizeof (*(l->faces)) * l->size )) == NULL) {
01858         printf ("Can't allocate memory.\n");
01859         exit (0);
01860     }
01861     if ((l->icon1 = malloc (sizeof (*(l->icon1)) * l->size )) == NULL) {
01862         printf ("Can't allocate memory.\n");
01863         exit (0);
01864     }
01865     if ((l->icon2 = malloc (sizeof (*(l->icon2)) * l->size )) == NULL) {
01866         printf ("Can't allocate memory.\n");
01867         exit (0);
01868     }
01869     if ((l->icon3 = malloc (sizeof (*(l->icon3)) * l->size )) == NULL) {
01870         printf ("Can't allocate memory.\n");
01871         exit (0);
01872     }
01873     if ((l->icon4 = malloc (sizeof (*(l->icon4)) * l->size )) == NULL) {
01874         printf ("Can't allocate memory.\n");
01875         exit (0);
01876     }
01877     if ((l->names = malloc (sizeof (char *) * l->size )) == NULL) {
01878         printf ("Can't allocate memory.\n");
01879         exit (0);
01880     }
01881     for (i=0; i < l->size; i++) {
01882         if ((l->names[i] = malloc (NAME_LEN)) == NULL) {
01883             printf ("Can't allocate memory.\n");
01884             exit (0);
01885         }
01886     }
01887     draw_all_list(l);   /* this also initializes above allocated tables */
01888 }
01889 
01890 static void get_list_display(itemlist *l, int x, int y, int w, int h,
01891                    const char *t, const char *s)
01892 {
01893     XSizeHints hint;
01894 
01895     l->faces=NULL;
01896     l->names=NULL;
01897     hint.x = x;
01898     hint.y = y;
01899     hint.width  = w;
01900     hint.height = h;
01901     hint.min_width  = 60 + 10 * FONTWIDTH;
01902     hint.min_height = FONTHEIGHT + 8 + image_size * 2;
01903     hint.flags = PPosition | PSize;
01904     l->win = XCreateSimpleWindow(display, win_root, hint.x, hint.y, hint.width,
01905                                hint.height, 2, foreground, background);
01906     XSetWindowColormap(display, l->win, colormap);
01907     icon = XCreateBitmapFromData(display, l->win,
01908                                (_Xconst char *) crossfire_bits,
01909                                (unsigned int) crossfire_width,
01910                                (unsigned int)crossfire_height);
01911     XSetStandardProperties(display, l->win, t, s, icon, gargv, gargc, &(hint));
01912     l->gc_text = XCreateGC (display, l->win, 0, 0);
01913     l->gc_icon = XCreateGC (display, l->win, 0, 0);
01914     l->gc_status = XCreateGC (display, l->win, 0, 0);
01915     XSetForeground (display, l->gc_text, foreground);
01916     XSetBackground (display, l->gc_text, background);
01917     XSetForeground (display, l->gc_icon, foreground);
01918     XSetBackground (display, l->gc_icon, background);
01919     XSetForeground (display, l->gc_status, foreground);
01920     XSetBackground (display, l->gc_status, background);
01921     XSetGraphicsExposures (display, l->gc_icon, False);
01922     XSetGraphicsExposures (display, l->gc_status, False);
01923     XSetFont (display, l->gc_text, font->fid);
01924     XSelectInput (display, l->win, ButtonPressMask|KeyPressMask|KeyReleaseMask|
01925                 ExposureMask|StructureNotifyMask);
01926     XSetWMProtocols(display, l->win, &wm_delete_window, 1);
01927     XMapRaised(display,l->win);
01928     create_status_icons();
01929     resize_list_info(l, w, h);
01930 }
01931 
01932 static int get_inv_display(void)
01933 {
01934     inv_list.env = cpl.ob;
01935     strcpy (inv_list.title, ""/*ET: too long: "Inventory:"*/);
01936     inv_list.show_weight = 1;
01937     inv_list.show_what = show_all;
01938     inv_list.weight_limit=0;
01939     get_list_display ( &inv_list, 0, 0, INV_WIDTH,
01940                       2*(roothint.height - WINDOW_SPACING) / 3,
01941                       "Crossfire - inventory",
01942                       "crossinventory");
01943     return 0;
01944 }
01945 
01946 static int get_look_display(void)
01947 {
01948     look_list.env = cpl.below;
01949     strcpy (look_list.title, "You see:");
01950     look_list.show_weight = 1;
01951     look_list.show_what = show_all;
01952     inv_list.weight_limit = 0;
01953     get_list_display ( &look_list, 0,
01954               (2*(roothint.height - WINDOW_SPACING) / 3) + WINDOW_SPACING,
01955                       INV_WIDTH,
01956                       (roothint.height - WINDOW_SPACING) / 3,
01957                       "Crossfire - look",
01958                     "crosslook");
01959     return 0;
01960 }
01961 
01962 /*
01963  *  draw_lists() redraws inventory and look windows when necessary
01964  */
01965 void draw_lists(void)
01966 {
01967     if (inv_list.env->inv_updated) {
01968         draw_list (&inv_list);
01969         inv_list.env->inv_updated = 0;
01970     }
01971     if (look_list.env->inv_updated) {
01972         draw_list (&look_list);
01973         look_list.env->inv_updated = 0;
01974     }
01975 }
01976 
01977 
01978 void set_show_icon (const char *s)
01979 {
01980     if (s == NULL || *s == 0 || strncmp ("inventory", s, strlen(s)) == 0) {
01981         inv_list.show_icon = ! inv_list.show_icon; /* toggle */
01982         resize_list_info(&inv_list, inv_list.width, inv_list.height);
01983     } else if (strncmp ("look", s, strlen(s)) == 0) {
01984         look_list.show_icon = ! look_list.show_icon; /* toggle */
01985         resize_list_info(&look_list, look_list.width, look_list.height);
01986     }
01987 }
01988 
01989 void set_show_weight (const char *s)
01990 {
01991     if (s == NULL || *s == 0 || strncmp ("inventory", s, strlen(s)) == 0) {
01992         inv_list.show_weight = ! inv_list.show_weight; /* toggle */
01993         draw_list (&inv_list);
01994     } else if (strncmp ("look", s, strlen(s)) == 0) {
01995         look_list.show_weight = ! look_list.show_weight; /* toggle */
01996         draw_list (&look_list);
01997     }
01998 }
01999 
02000 void set_weight_limit (uint32 wlim)
02001 {
02002     inv_list.weight_limit = wlim;
02003 }
02004 
02005 void set_scroll(const char *s)
02006 {
02007     if (!infodata.scroll_info_window) {
02008         infodata.scroll_info_window=1;
02009         if (infodata.numlines>=infodata.maxdisp) {
02010             infodata.infoline=infodata.maxdisp-1;
02011         }
02012         draw_all_info();
02013         draw_info("Scroll is enabled", NDI_BLACK);
02014     }
02015     else {
02016         draw_info("Scroll is disabled", NDI_BLACK);
02017         infodata.scroll_info_window=0;
02018     }
02019 }
02020 
02021 void set_autorepeat(const char *s)
02022 {
02023     noautorepeat = noautorepeat ? FALSE : TRUE;
02024     draw_info(noautorepeat ? "Autorepeat is disabled":"Autorepeat is enabled",
02025               NDI_BLACK);
02026 }
02027 
02028 int get_info_width(void)
02029 {
02030     return infodata.info_chars;
02031 }
02032 
02033 void menu_clear(void)
02034 {
02035     draw_info("clearinfo command not implemented for this client", NDI_BLACK);
02036     return;
02037 }
02038 
02039 /* TODO Figure out if/how to *use* these events here. */
02040 void item_event_item_deleting(item * it) {}
02041 void item_event_container_clearing(item * container) {}
02042 void item_event_item_changed(item * it) {}
02043 
02044 /******************************************************************************
02045  * Root Window code
02046  ****************************************************************************/
02047 
02048 /* get_root_display:
02049  * this sets up the root window (or none, if in split
02050  * windows mode, and also scans for any Xdefaults.  Right now, only
02051  * splitwindow and image are used.  image is the display
02052  * mechanism to use.  I thought having one type that is set
02053  * to font, xpm, or pixmap was better than using xpm and pixmap
02054  * resources with on/off values (which gets pretty weird
02055  * if one of this is set to off.
02056  */
02057 
02058 /* Error handlers removed.  Right now, there is nothing for
02059  * the client to do if it gets a fatal error - it doesn't have
02060  * any information to save.  And we might as well let the standard
02061  * X11 error handler handle non fatal errors.
02062  */
02063 int sync_display = 0;
02064 
02065 static int get_root_display(char *display_name) {
02066     char *cp;
02067 
02068     display=XOpenDisplay(display_name);
02069     if (!display) {
02070         fprintf(stderr, "Can't open display %s.\n", display_name);
02071         return 1;
02072     }
02073 
02074     wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", 0);
02075     /* This generates warnings, but looking at the documenation,
02076      * it seems like it _should_ be ok.
02077      */
02078     XSetErrorHandler(error_handler);
02079 
02080     if ((getenv("ERIC_SYNC")!= NULL) || sync_display)
02081         XSynchronize(display,True);
02082 
02083     def_root = DefaultRootWindow(display);
02084     def_screen = DefaultScreen(display);
02085 
02086     /* For both split_windows and display mode, check to make sure that
02087      * the command line has not set the value.  Command line settings
02088      * should always have precedence over settings in the Xdefaults file.
02089      *
02090      * Also, if you add new defaults, make sure that the same rules for
02091      * the command line defaults are used.  The client assumes that certain
02092      * values can only be set if they have meaning (save_data will not
02093      * be set unless the data can actually be saved, for example.)  If
02094      * this is not followed for then the client might very well crash.
02095      */
02096     if (!want_config[CONFIG_SPLITWIN] &&
02097       (cp=XGetDefault(display,X_PROG_NAME,"splitwindow")) != NULL) {
02098         if (!strcmp("on",cp) || !strcmp("yes",cp))
02099             want_config[CONFIG_SPLITWIN] = TRUE;
02100         else if (!strcmp("off",cp) || !strcmp("no",cp))
02101             want_config[CONFIG_SPLITWIN] = FALSE;
02102     }
02103     if (!use_config[CONFIG_ECHO] &&
02104       (cp=XGetDefault(display,X_PROG_NAME,"echo")) != NULL) {
02105         if (!strcmp("on",cp) || !strcmp("yes",cp))
02106             use_config[CONFIG_ECHO] = TRUE;
02107         else if (!strcmp("off",cp) || !strcmp("no",cp))
02108             use_config[CONFIG_ECHO] = FALSE;
02109     }
02110     if ((cp=XGetDefault(display,X_PROG_NAME, "scrollLines"))!=NULL) {
02111         infodata.maxlines=atoi(cp);
02112     }
02113     if ((cp=XGetDefault(display,X_PROG_NAME,"font")) != NULL) {
02114         font_name = strdup_local(cp);
02115     }
02116     /* Failure will result in an uncaught X11 error */
02117     font=XLoadQueryFont(display,font_name);
02118     if (!font) {
02119         fprintf(stderr,"Could not load font %s\n", font_name);
02120         exit(1);
02121     }
02122     FONTWIDTH=font->max_bounds.width;
02123     FONTHEIGHT=font->max_bounds.ascent + font->max_bounds.descent;
02124 
02125     background=WhitePixel(display,def_screen);
02126     foreground=BlackPixel(display,def_screen);
02127     roothint.x=0;
02128     roothint.y=0;
02129     roothint.width=582+6+INFOCHARS*FONTWIDTH;
02130     roothint.height=ROOT_HEIGHT;
02131     /* Make up for the extra size of the game window.  88 is
02132      * 11 tiles * 8 pixels/tile bigger size.
02133      */
02134     roothint.width += 88;
02135     roothint.height+= 88;
02136     init_pngx_loader(display);
02137 
02138     roothint.max_width=roothint.min_width=roothint.width;
02139     roothint.max_height=roothint.min_height=roothint.height;
02140     roothint.flags=PSize; /*ET: no PPosition. let window manager handle that. */
02141 
02142     if(!want_config[CONFIG_SPLITWIN]) {
02143         win_root=XCreateSimpleWindow(display,def_root,
02144             roothint.x,roothint.y,roothint.width,roothint.height,2,
02145             background,foreground);
02146 
02147         allocate_colors(display, win_root, def_screen, &colormap, discolor);
02148         foreground=discolor[0].pixel;
02149         background=discolor[9].pixel;
02150         icon=XCreateBitmapFromData(display,win_root,
02151             (_Xconst char *) crossfire_bits,
02152             (unsigned int) crossfire_width, (unsigned int)crossfire_height);
02153         XSetStandardProperties(display,win_root,X_PROG_NAME,X_PROG_NAME,
02154             icon,gargv,gargc,&(roothint));
02155         gc_root=XCreateGC(display,win_root,0,0);
02156         XSetForeground(display,gc_root,foreground);
02157         XSetBackground(display,gc_root,background);
02158 
02159         XSelectInput(display,win_root,KeyPressMask|
02160              KeyReleaseMask|ExposureMask|StructureNotifyMask);
02161         XMapRaised(display,win_root);
02162         XNextEvent(display,&event);     /*ET: this is bogus */
02163     XSetWMProtocols(display, win_root, &wm_delete_window, 1);
02164     }
02165     else
02166         win_root = def_root;
02167     return 0;
02168 }
02169 
02170 static void resize_win_root(XEvent *event) {
02171     int width, inv_width, info_width;
02172 
02173     if (want_config[CONFIG_SPLITWIN]) {
02174         fprintf(stderr,"Got a resize root window in split windows mode\n");
02175         return;
02176     }
02177 
02178     /* The middle 3 windows don't really benefit from the resize, so keep
02179      * them the same size, and use the leftover equally between the left
02180      * and right windows.
02181      */
02182     width = (event->xconfigure.width - GAME_WIDTH -WINDOW_SPACING*2);
02183     info_width = width * info_ratio;
02184     inv_width = width - info_width;
02185 
02186     /* With png (and 32x32 images), the message window can get scrunched,
02187      * so lets make it taller if we can - there is no reason not to, as otherwise
02188      * that space is lost anyways.
02189      */
02190     XMoveResizeWindow(display, win_message, inv_width + WINDOW_SPACING,
02191                 GAME_WIDTH + STAT_HEIGHT + WINDOW_SPACING*2, GAME_WIDTH,
02192                 event->xconfigure.height - GAME_WIDTH + STAT_HEIGHT + WINDOW_SPACING*2);
02193     messagehint.width=GAME_WIDTH;
02194     messagehint.height=event->xconfigure.height - GAME_WIDTH + STAT_HEIGHT + WINDOW_SPACING*2;
02195 
02196     /* These windows just need to be relocated.  The y constants are
02197      * hardcoded - those windows don't really benefit from being resized
02198      * (actually, no code in place to currently do it), so no reason
02199      * to get trick with those just now.
02200      */
02201 
02202     XMoveWindow(display, win_game, inv_width + WINDOW_SPACING, STAT_HEIGHT + WINDOW_SPACING);
02203     XMoveWindow(display, win_stats, inv_width + WINDOW_SPACING, 0);
02204 
02205     /* Resize the info window */
02206     XMoveResizeWindow(display, infodata.win_info,
02207         inv_width + GAME_WIDTH + WINDOW_SPACING * 2, 0,
02208         info_width, event->xconfigure.height);
02209 
02210 
02211     /* Resize the inventory, message window */
02212     XResizeWindow(display, inv_list.win, inv_width,
02213                   2 * (event->xconfigure.height - WINDOW_SPACING) / 3);
02214 
02215 
02216     XMoveResizeWindow(display, look_list.win, 0,
02217               (2*(event->xconfigure.height - WINDOW_SPACING) / 3) + WINDOW_SPACING,
02218                       inv_width,
02219                       (event->xconfigure.height - WINDOW_SPACING) / 3);
02220 
02221     /* The Resize requests will generate events.  As such, we don't call
02222      * the resize functions here - the event handler would get those anyways,
02223      * so there isn't a big gain in doing it here.
02224      */
02225 }
02226 
02227 
02228 /***********************************************************************
02229  *
02230  * Here is the start of event handling functions
02231  *
02232  ***********************************************************************/
02233 
02234 static void parse_game_button_press(int button, int x, int y)
02235 {
02236     int dx, dy, i, xmidl, xmidh, ymidl, ymidh;
02237 
02238     dx = (x-2)/image_size-use_config[CONFIG_MAPWIDTH]/2;
02239     dy= (y-2)/image_size-use_config[CONFIG_MAPHEIGHT]/2;
02240     xmidl=(use_config[CONFIG_MAPWIDTH]/2) * image_size;
02241     xmidh=(use_config[CONFIG_MAPWIDTH]/2 + 1) * image_size;
02242     ymidl=(use_config[CONFIG_MAPHEIGHT]/2) * image_size;
02243     ymidh=(use_config[CONFIG_MAPHEIGHT]/2 + 1) * image_size;
02244 
02245     switch (button) {
02246         case 1:
02247         {
02248             /* Its unlikely this will happen, but if the window is
02249              * resized, its possible to be out of bounds.
02250              */
02251             if(dx<(-use_config[CONFIG_MAPWIDTH]/2)||dx>(use_config[CONFIG_MAPWIDTH]/2)||dy<(-use_config[CONFIG_MAPHEIGHT]/2)||dy>(use_config[CONFIG_MAPHEIGHT]/2)) return;
02252             look_at(dx,dy);
02253         }
02254         break;
02255         case 2:
02256         case 3:
02257             if (x<xmidl)
02258                 i = 0;
02259             else if (x>xmidh)
02260                i = 6;
02261             else i =3;
02262 
02263             if (y>ymidh)
02264               i += 2;
02265             else if (y>ymidl)
02266               i++;
02267 
02268             if (button==2) {
02269                 switch (i) {
02270                       case 0: fire_dir (8);break;
02271                       case 1: fire_dir (7);break;
02272                       case 2: fire_dir (6);break;
02273                       case 3: fire_dir (1);break;
02274                       case 5: fire_dir (5);break;
02275                       case 6: fire_dir (2);break;
02276                       case 7: fire_dir (3);break;
02277                       case 8: fire_dir (4);break;
02278                 }
02279                 /* Only want to fire once */
02280                 clear_fire();
02281             }
02282             else switch (i) {
02283               case 0: move_player (8);break;
02284               case 1: move_player (7);break;
02285               case 2: move_player (6);break;
02286               case 3: move_player (1);break;
02287               case 5: move_player (5);break;
02288               case 6: move_player (2);break;
02289               case 7: move_player (3);break;
02290               case 8: move_player (4);break;
02291             }
02292     }
02293 }
02294 
02295 
02296 /* Handles key presses.  Note that the client really doesn't know
02297  * much about states.  But there are at least a couple that the
02298  * client needs to know about - game play, where it parses keystrokes,
02299  * applies any bindings, and sends the command to the server,
02300  * and reply states.  The only time reply's are used right now is for rolling
02301  * up a character - sending 'SAY' commands is not appropriate.
02302  *
02303  * After input is completed in either of the reply states, the program
02304  * sets the game back to Play state.  May not be appropriate, but let
02305  * the server ignore the commands if they are not proper - a hack
02306  * client could always send such commands anyways.
02307  * If the server expect additional reply's, it should send an query.
02308  * Note that these can not be stacked up, (ie, server can not send
02309  * 5 queries and expect 5 replies).   But in no place does the server
02310  * do this anyways, so it is not a problem.
02311  */
02312 
02313 static void do_key_press(int repeated)
02314 {
02315     KeySym gkey;
02316     char text[10];
02317 
02318     /* Turn off the magic map.  Perhaps this should be more selective? */
02319     if (cpl.showmagic) {
02320         cpl.showmagic=0;
02321         display_map_doneupdate(TRUE, FALSE);
02322     }
02323     if(!XLookupString(&event.xkey,text,10, &gkey,NULL)) {
02324 /*
02325  *      This happens quite a bit - most modifier keys (shift, control, etc)
02326  *      can not be mapped to a value.
02327  */
02328  /*
02329         fprintf(stderr,"XLookupString failed, xkey.code=%d, gkey=%ld\n",
02330                 event.xkey.keycode, gkey);
02331 */
02332         text[0]='\0';
02333     }
02334     switch(cpl.input_state) {
02335         case Playing:
02336             parse_key(text[0],event.xkey.keycode,gkey,repeated);
02337             break;
02338 
02339         case Reply_One:
02340         /* Don't send modifier keys as reply to query. Tries to prevent
02341          * from getting into the "...state is not ST_PLAYING" state. */
02342             if (text[0]) {
02343                 text[1]='\0';
02344                 send_reply(text);
02345                 cpl.input_state = Playing;
02346             }
02347             break;
02348 
02349         case Configure_Keys:
02350             configure_keys(event.xkey.keycode, gkey);
02351             break;
02352 
02353         case Reply_Many:
02354         case Command_Mode:
02355         case Metaserver_Select:
02356             if (text[0]==13) {
02357                 enum Input_State old_state=cpl.input_state;
02358                 if (cpl.input_state==Metaserver_Select) {
02359                     cpl.input_state=Playing;
02360                     return;
02361                 }
02362                 if (cpl.input_state==Reply_Many)
02363                     send_reply(cpl.input_text);
02364                 else {
02365                     write_ch(13);
02366                     extended_command(cpl.input_text);
02367                 }
02368                 /* Only set state to playing if the state has otherwise
02369                  * not changed - this check is needed because 'bind
02370                  * changes the state, and we don't want to change to playing
02371                  * again.
02372                  */
02373                 if (old_state==cpl.input_state)
02374                     cpl.input_state = Playing;
02375                 cpl.input_text[0]='\0';
02376                 cpl.no_echo=0;  /* By default, start echoing thing again */
02377             }
02378             else {
02379                 write_ch(text[0]);
02380             }
02381             break;
02382 
02383         default:
02384             fprintf(stderr,"Unknown input state: %d\n", cpl.input_state);
02385     }
02386 }
02387 
02388 
02389 static void buttonpress_in_info(XButtonEvent *xbutton)
02390 {
02391     int y = xbutton->y-16, x=xbutton->x, button = xbutton->button,dy,pos=0;
02392 
02393     if (!infodata.has_scrollbar)
02394         return;
02395 
02396     if (button < 4 && x <= infodata.width-SCROLLBAR_WIDTH-4)
02397         return;
02398 
02399     dy = y / FONTHEIGHT > 0 ? y / FONTHEIGHT : 1;
02400 
02401     switch(button) {
02402           case 1:
02403             pos = infodata.bar_pos - dy;
02404             break;
02405 
02406           case 2:
02407             pos = y * infodata.numlines / infodata.bar_length;
02408             break;
02409 
02410           case 3:
02411             pos = infodata.bar_pos + dy;
02412             break;
02413 
02414           case 4:
02415             pos = infodata.bar_pos - 1;
02416             break;
02417 
02418           case 5:
02419             pos = infodata.bar_pos + 1;
02420             break;
02421 
02422     }
02423     if (pos<infodata.maxdisp) {
02424         if (infodata.numlines<infodata.maxdisp)
02425             pos=infodata.numlines;
02426         else
02427             pos=infodata.maxdisp;
02428     }
02429     if (pos>infodata.numlines) pos=infodata.numlines;
02430     if (pos != infodata.bar_pos) {
02431         infodata.bar_pos = pos;
02432         draw_all_info();
02433     }
02434 }
02435 
02436 
02437 /*
02438  *  buttonpress_in_list handles mouse button event in list window.
02439  *  It updates scrollbar or calls right function for item
02440  *  (apply/examine/move). It's probably better move calling the
02441  *  functions to the upper level.
02442  */
02443 
02444 static int buttonpress_in_list (itemlist *l, XButtonEvent *xbutton)
02445 {
02446     item *tmp;
02447     int y = xbutton->y - 16, x=xbutton->x, button = xbutton->button;
02448     int items, pos=0, dy;
02449 
02450     if (y < 0 && l->env->open) /* close the sack */
02451         client_send_apply (l->env->tag);
02452 
02453     if (y < 0 || y > image_size * l->size)
02454         return 1;
02455 
02456     if (button == 4 || button == 5)
02457     {
02458         if (button == 4)
02459             l->item_pos--;
02460         else
02461             l->item_pos++;
02462         draw_list(l);
02463         return 1;
02464     }
02465 
02466     if (x > l->width-23) {    /* scrollbar */
02467 
02468         dy = y / image_size > 0 ? y / image_size : 1;
02469 
02470         switch(button) {
02471           case 1:
02472             pos = l->item_pos - dy;
02473             break;
02474 
02475           case 2:
02476             for(tmp=l->env->inv, items=0; tmp; tmp=tmp->next)
02477                 items++;
02478             pos = y * items / l->bar_length;
02479             break;
02480 
02481           case 3:
02482             pos = l->item_pos + dy;
02483             break;
02484 
02485         }
02486         if (pos != l->item_pos) {
02487             l->item_pos = pos;
02488             draw_list (l);
02489         }
02490         return 1;
02491     }
02492 
02493     pos = l->item_pos + y / image_size;
02494     for(tmp=l->env->inv, items=0; tmp; tmp=tmp->next) {
02495         if (show_object(tmp, l->show_what)) items++;
02496         if (items>pos) break;
02497     }
02498     if (tmp) {
02499         switch(button) {
02500           case 1:
02501             if (xbutton->state & ShiftMask)
02502                 toggle_locked(tmp);
02503             else
02504                 client_send_examine (tmp->tag);
02505             break;
02506           case 2:
02507             if (xbutton->state & ShiftMask)
02508                 send_mark_obj(tmp);
02509             else
02510                 client_send_apply (tmp->tag);
02511             break;
02512           case 3:
02513             if (tmp->locked) {
02514                 draw_info ("This item is locked. To drop it, first unlock by shift+leftclicking on it.",
02515                     NDI_BLACK);
02516             } else if (l == &inv_list)
02517                 client_send_move (look_list.env->tag, tmp->tag, cpl.count);
02518             else
02519                 client_send_move (inv_list.env->tag, tmp->tag, cpl.count);
02520             cpl.count=0;
02521             break;
02522         }
02523     }
02524     return 1;
02525 }
02526 
02527 
02528 /* get_metaserver returns a string for what the user has selected as
02529  * their metaserver.  It basically does a subset of check_x_events.
02530  * and keeps looping until the user finishes selecting the metaserver
02531  * (detected by change of state.
02532  */
02533 char *get_metaserver(void)
02534 {
02535     static char ret_buf[MAX_BUF];
02536 
02537     cpl.input_state = Metaserver_Select;
02538     draw_prompt(":");
02539     while (cpl.input_state == Metaserver_Select) {
02540         check_x_events();
02541         usleep(50000);  /* 1/20 sec */
02542     } /* while input state is metaserver select. */
02543 
02544     /* We need to clear out cpl.input_text - otherwise the next
02545      * long input (like player name) won't work right.
02546      * so copy it to a private buffer and return.
02547      */
02548     strncpy(ret_buf, cpl.input_text, MAX_BUF-1);
02549     ret_buf[MAX_BUF-1]=0;
02550     cpl.input_text[0]=0;
02551     return ret_buf;
02552 }
02553 
02554 
02555 /* This function handles the reading of the X Events and then
02556  * doing the appropriate action.  For most input events, it is calling
02557  * another function.
02558  *
02559  * It can also call display functions to make sure the information is
02560  * correct - in this way, updates will not be done so often (like
02561  * for every ITEM command received), but less frequently but still
02562  * update the display fully.  All the functions above are optimized to
02563  * only draw stuff that needs drawing.  So calling them a lot is not
02564  * going to draw stuff too much.
02565  */
02566 
02567 void check_x_events(void) {
02568     KeySym gkey=0;
02569     static int lastupdate=0;
02570     static XEvent prev_event;   /* to detect autorepeated keys */
02571 
02572     /* If not connected, the below area does not apply, so don't deal with it */
02573     if (cpl.input_state != Metaserver_Select) {
02574 
02575         /* We need to periodically redraw stuff if we are caching images - otherwise
02576          * the default images might be display indefinately.  This is tuned so
02577          * that we should only be redrawing stuff if we got new images from
02578          * the last draw and enough time has passed.  This should be a bit
02579          * more efficient than redrawing everything anytime we get a new image -
02580          * especially since we might get a bunch of images at the same time.
02581          */
02582         if (want_config[CONFIG_CACHE] && lastupdate>5 && newimages) {
02583             update_icons_list(&inv_list);
02584             update_icons_list(&look_list);
02585             if (!cpl.showmagic) display_map_doneupdate(TRUE, FALSE);
02586             newimages=0;
02587             lastupdate=0;
02588         }
02589         else {
02590             if (newimages)
02591                 lastupdate++;
02592             draw_lists();               /* testing if this can work this way */
02593         }
02594     }
02595 
02596 
02597     while (XPending(display)!=0) {
02598         prev_event = event;
02599         XNextEvent(display,&event);
02600         switch(event.type) {
02601 
02602             case ConfigureNotify:
02603                 if(event.xconfigure.window==infodata.win_info)
02604                     resize_win_info(event.xconfigure.width, event.xconfigure.height);
02605                 else if(event.xconfigure.window==inv_list.win)
02606                     resize_list_info(&inv_list, event.xconfigure.width,
02607                          event.xconfigure.height);
02608                 else if(event.xconfigure.window==look_list.win)
02609                     resize_list_info(&look_list, event.xconfigure.width,
02610                          event.xconfigure.height);
02611                 else if(event.xconfigure.window==win_root)
02612                     resize_win_root(&event);
02613                 else if(event.xconfigure.window==win_message)
02614                     resize_win_message(event.xconfigure.width, event.xconfigure.height);
02615                 break;
02616 
02617             case Expose:
02618             /* No point redrawing windows if there are more Expose's to
02619              * come.
02620              */
02621             if (event.xexpose.count!=0) continue;
02622             if(event.xexpose.window==win_stats) {
02623                 XClearWindow(display,win_stats);
02624                 draw_stats(1);
02625             } else if(event.xexpose.window==infodata.win_info)
02626                 draw_all_info();
02627             else if(event.xexpose.window==inv_list.win)
02628                 draw_all_list(&inv_list);
02629             else if(event.xexpose.window==look_list.win)
02630                 draw_all_list(&look_list);
02631             else if(event.xexpose.window==win_message)
02632                 draw_all_message();
02633             else if(event.xexpose.window==win_game) {
02634                 if (cpl.showmagic) draw_magic_map();
02635                 else display_map_doneupdate(TRUE, FALSE);
02636             } else if(want_config[CONFIG_SPLITWIN]==FALSE && event.xexpose.window==win_root) {
02637                 XClearWindow(display,win_root);
02638             }
02639             break;
02640 
02641             case GraphicsExpose:
02642                 /* No point redrawing windows if there are more GraphicExpose's
02643                  * to come.
02644                  */
02645                 if (event.xgraphicsexpose.count!=0) continue;
02646                 if(event.xgraphicsexpose.drawable==win_game) {
02647                     if (cpl.showmagic) draw_magic_map();
02648                     else display_map_doneupdate(TRUE, FALSE);
02649                 }
02650                 break;
02651 
02652             case MappingNotify:
02653                 XRefreshKeyboardMapping(&event.xmapping);
02654                 break;
02655 
02656 
02657             case ButtonPress:
02658                 /* Most of these will try to send requests to the server - since we
02659                  * are not connected, this will probably fail in some bad way.
02660                  */
02661                 if (cpl.input_state != Metaserver_Select) {
02662 
02663                     if(event.xbutton.window==win_game) {
02664                         parse_game_button_press(event.xbutton.button,event.xbutton.x,
02665                                                 event.xbutton.y);
02666                     } else if(event.xbutton.window==inv_list.win) {
02667                         buttonpress_in_list(&inv_list, &event.xbutton);
02668 
02669                     } else if(event.xbutton.window==look_list.win) {
02670                         buttonpress_in_list(&look_list, &event.xbutton);
02671                     }
02672                     else if (event.xbutton.window==infodata.win_info) {
02673                         buttonpress_in_info(&event.xbutton);
02674                     }
02675                     break;
02676                 }
02677 
02678             case KeyRelease:
02679                 parse_key_release(event.xkey.keycode, gkey);
02680                 break;
02681 
02682             case KeyPress:
02683                 if (noautorepeat
02684                     && prev_event.type == KeyRelease
02685                     && prev_event.xkey.keycode == event.xkey.keycode
02686                     && prev_event.xkey.state == event.xkey.state
02687                     && prev_event.xkey.time == event.xkey.time)
02688                     do_key_press(1);    /* auto-repeated key */
02689                 else
02690                     do_key_press(0);    /* regular key */
02691                 break;
02692         case ClientMessage:
02693             if(event.xclient.data.l[0] == wm_delete_window){
02694                 LOG(LOG_INFO,"x11::check_x_events","Window closed. Exiting.");
02695                 exit(0);
02696             }
02697         break;
02698         }
02699     }
02700     /* Below does not apply if we're not connected */
02701     if (cpl.input_state != Metaserver_Select) {
02702         /* Since we cycle through all the Xevents, there is no need to do
02703          * keyboard flushing.  All commands get sent to the server.  There really
02704          * does need to be some decent way to flush the commands we have sent,
02705          * but there is no real way to do that.  This is at least somewhat
02706          * true because the client and server will never be completely
02707          * synchronized.
02708          */
02709 
02710         /* Need to do this to show the players position */
02711         if (cpl.showmagic) magic_map_flash_pos();
02712         clear_fire_run();
02713     }
02714     /* a good place to check for childs */
02715     monitorChilds();
02716 }
02717 
02718 
02719 /***********************************************************************
02720  *
02721  * Here starts the X11 init functions.  It will call other
02722  * functions that were grouped previously by window
02723  *
02724  ***********************************************************************/
02725 
02726 /* Usage routine.  All clients should support server, port and
02727  * display options, with -pix and -xpm also suggested.  -split
02728  * does not need to be supported - it is in this copy because
02729  * the old code supported it.
02730  */
02731 
02732 static void usage(char *progname)
02733 {
02734     puts("Usage of cfclient:\n\n");
02735     puts("-server <name>   - Connect to <name> instead of localhost.");
02736     puts("-port <number>   - Use port <number> instead of the standard port number");
02737     puts("-display <name>  - Use <name> instead if DISPLAY environment variable.\n");
02738     puts("-split           - Use split windows.");
02739     puts("-nosplit         - Disable split windows (default action).");
02740     puts("-echo            - Echo the bound commands");
02741     puts("-pix             - Use bitmaps for display.");
02742 #ifdef HAVE_LIBXPM
02743     puts("-xpm             - Use color pixmaps (XPM) for display.");
02744 #endif
02745 #ifdef HAVE_LIBPNG
02746     puts("-png             - Use png images for display.");
02747 #endif
02748     puts("-showicon        - Print status icons in inventory window");
02749     puts("-scrolllines <number>    - number of lines for scrollback");
02750     puts("-sync            - Synchronize on display");
02751     puts("-help            - Display this message.");
02752     puts("-cache           - Cache images for future use.");
02753     puts("-nocache         - Do not cache images (default action).");
02754     puts("-mapscroll       - Enable mapscrolling by bitmap operations");
02755     puts("-nomapscroll     - Disable mapscrolling by bitmap operations");
02756     puts("-darkness        - Enables darkness code (default)");
02757     puts("-nodarkness      - Disables darkness code");
02758     puts("-nosound         - Disable sound output.");
02759     puts("-updatekeycodes  - Update the saved bindings for this keyboard.");
02760     puts("-keepcache       - Keep already cached images even if server has different ones.");
02761     puts("-font <name>     - Use <name> as font to display data.");
02762     puts("-pngfile <name>  - Use <name> for source of images");
02763     puts("-mapsize xXy     - Set the mapsize to be X by Y spaces.");
02764     puts("-noautorepeat    - Auto repeat on directional keys is ignored.");
02765     puts("-faceset <num>   - Select a specific facset to use.");
02766     exit(0);
02767 }
02768 
02769 /* init_windows:  This initiliazes all the windows - it is an
02770  * interface routine.  The command line arguments are passed to
02771  * this function to interpert.  Note that it is not in fact
02772  * required to parse anything, but doing at least -server and
02773  * -port would be a good idea.  This is a little messy, as non window
02774  * related options as well as those relating to windowing are parsed here,
02775  * but this file is meant for only the X11 stuff.
02776  *
02777  * This function returns 0 on success, nonzero on failure.
02778  */
02779 
02780 int init_windows(int argc, char **argv)
02781 {
02782     int on_arg=1;
02783     char *display_name="";
02784 
02785     strcpy(VERSION_INFO,"X11 Unix Client " FULL_VERSION);
02786 
02787     load_defaults();    /* Load these first, so they can get overwritten by
02788                          * command line options.
02789                          */
02790     want_skill_exp=1;
02791     for (on_arg=1; on_arg<argc; on_arg++) {
02792         if (!strcmp(argv[on_arg],"-display")) {
02793             if (++on_arg == argc) {
02794                 fprintf(stderr,"-display requires a display name\n");
02795                 return 1;
02796             }
02797             display_name = argv[on_arg];
02798             continue;
02799         }
02800         if (strcmp(argv[on_arg],"-sync")==0) {
02801             sync_display = 1;
02802             continue;
02803         }
02804         if (!strcmp(argv[on_arg],"-port")) {
02805             if (++on_arg == argc) {
02806                 fprintf(stderr,"-port requires a port number\n");
02807                 return 1;
02808             }
02809             use_config[CONFIG_PORT] = atoi(argv[on_arg]);
02810             continue;
02811         }
02812         if (!strcmp(argv[on_arg],"-mapsize")) {
02813             char *cp, x, y=0;
02814 
02815             if (++on_arg == argc) {
02816                 fprintf(stderr,"-mapsize requires a XxY value\n");
02817                 return 1;
02818             }
02819             x = atoi(argv[on_arg]);
02820             for (cp = argv[on_arg]; *cp!='\0'; cp++)
02821                 if (*cp == 'x' || *cp == 'X') break;
02822 
02823             if (*cp==0) {
02824                 fprintf(stderr,"-mapsize requires both and X and Y value (ie, XxY - note the\nx in between.\n");
02825             } else {
02826                 y = atoi(cp+1);
02827             }
02828             if (x<=9 || y<=9) {
02829                 fprintf(stderr,"map size must be positive values of at least 9\n");
02830             } if (x>MAP_MAX_SIZE || y>MAP_MAX_SIZE) {
02831                 fprintf(stderr,"Map size can not be larger than %d x %d \n", MAP_MAX_SIZE, MAP_MAX_SIZE);
02832             } else {
02833                 want_config[CONFIG_MAPWIDTH]=x;
02834                 want_config[CONFIG_MAPHEIGHT]=y;
02835             }
02836             continue;
02837         }
02838         if (!strcmp(argv[on_arg],"-server")) {
02839             if (++on_arg == argc) {
02840                 fprintf(stderr,"-server requires a host name\n");
02841                 return 1;
02842             }
02843             server = argv[on_arg];
02844             continue;
02845         }
02846         else if (!strcmp(argv[on_arg],"-nofasttcpsend")) {
02847             use_config[CONFIG_FASTTCP]=0;
02848             continue;
02849         }
02850         else if (!strcmp(argv[on_arg],"-cache")) {
02851             want_config[CONFIG_CACHE]= TRUE;
02852             continue;
02853         }
02854         else if (!strcmp(argv[on_arg],"-nocache")) {
02855             want_config[CONFIG_CACHE]= FALSE;
02856             continue;
02857         }
02858         else if (!strcmp(argv[on_arg],"-mapscroll")) {
02859             want_config[CONFIG_MAPSCROLL] = TRUE;
02860         }
02861         else if (!strcmp(argv[on_arg],"-nomapscroll")) {
02862             want_config[CONFIG_MAPSCROLL] = FALSE;
02863         }
02864         else if (!strcmp(argv[on_arg],"-darkness")) {
02865             use_config[CONFIG_DARKNESS]= TRUE;
02866             continue;
02867         }
02868         else if (!strcmp(argv[on_arg],"-nodarkness")) {
02869             use_config[CONFIG_DARKNESS]= FALSE;
02870             continue;
02871         }
02872         else if (!strcmp(argv[on_arg],"-fogofwar")) {
02873             use_config[CONFIG_FOGWAR]= TRUE;
02874             continue;
02875         }
02876         else if (!strcmp(argv[on_arg],"-nofogofwar")) {
02877             use_config[CONFIG_FOGWAR]= FALSE;
02878             continue;
02879         }
02880         else if (!strcmp(argv[on_arg],"-split")) {
02881             want_config[CONFIG_SPLITWIN]=TRUE;
02882             continue;
02883         }
02884         else if (!strcmp(argv[on_arg],"-nosplit")) {
02885             want_config[CONFIG_SPLITWIN]=FALSE;
02886             continue;
02887         }
02888         else if (!strcmp(argv[on_arg],"-showicon")) {
02889             inv_list.show_icon = TRUE;
02890             continue;
02891         }
02892         else if (!strcmp(argv[on_arg],"-download_all_faces")) {
02893             use_config[CONFIG_DOWNLOAD]=TRUE;
02894             continue;
02895         }
02896         else if (!strcmp(argv[on_arg],"-echo")) {
02897             use_config[CONFIG_ECHO]=TRUE;
02898             continue;
02899         }
02900         else if (!strcmp(argv[on_arg],"-faceset")) {
02901             if (++on_arg == argc) {
02902                 fprintf(stderr,"-faceset requires a faceset name/number\n");
02903                 return 1;
02904             }
02905             face_info.want_faceset = argv[on_arg];
02906             continue;
02907         }
02908         else if (!strcmp(argv[on_arg],"-scrolllines")) {
02909             if (++on_arg == argc) {
02910                 fprintf(stderr,"-scrolllines requires a number\n");
02911                 return 1;
02912             }
02913             infodata.maxlines = atoi(argv[on_arg]);
02914             continue;
02915         }
02916         else if (!strcmp(argv[on_arg],"-font")) {
02917             if (++on_arg == argc) {
02918                 fprintf(stderr,"-font requires a font name\n");
02919                 return 1;
02920             }
02921             font_name = argv[on_arg];
02922             continue;
02923         }
02924         else if (!strcmp(argv[on_arg],"-help")) {
02925             usage(argv[0]);
02926             continue;
02927         }
02928         else if (!strcmp(argv[on_arg],"-nosound")) {
02929             want_config[CONFIG_SOUND]=FALSE;
02930             continue;
02931         }
02932         else if (!strcmp(argv[on_arg],"-updatekeycodes")) {
02933             updatekeycodes=TRUE;
02934             continue;
02935         }
02936         else if (!strcmp(argv[on_arg],"-autorepeat")) {
02937             noautorepeat=TRUE;
02938             continue;
02939         }
02940         else {
02941             fprintf(stderr,"Do not understand option %s\n", argv[on_arg]);
02942             usage(argv[0]);
02943             return 1;
02944         }
02945     }
02946     /* Moving processing and setting of display attributes down here.
02947      * This is because a default display mode may also require special
02948      * handling.
02949      * This is not ideal, as if we don't have one of the optional modes,
02950      * we just fall back to pixmap mode.  But I don't really want to get into
02951      * a big nest of #ifdefs checking/setting modes.
02952      */
02953 #ifndef HAVE_LIBPNG
02954     fprintf(stderr,"Client not configured with Png display mode enabled\n");
02955     fprintf(stderr,"Install the png library and try recompiling.\n");
02956     exit(1);
02957 #else
02958     image_size=32;
02959 #endif
02960 
02961     mapdata_init();
02962 
02963     /* Finished parsing all the command line options.  Now start
02964      * working on the display.
02965      */
02966     gargc=argc;
02967     gargv=argv;
02968 
02969     if (get_root_display(display_name) ||
02970         get_game_display() ||
02971         get_stats_display() ||
02972         get_info_display() ||
02973         get_inv_display() ||
02974         get_look_display() ||
02975         get_message_display())
02976                 return 1;
02977 
02978     init_keys();
02979     init_cache_data();
02980     set_window_pos();
02981     info_ratio=(float) infodata.width/ (float) (infodata.width + INV_WIDTH);
02982     return 0;
02983 }
02984 
02985 
02986 void display_map_newmap(void)
02987 {
02988     reset_map();
02989 }
02990 
02991 /* This can also get called for png.  Really, anything that gets rendered
02992  * into a pixmap that does not need colors set or other specials done.
02993  */
02994 static void display_mapcell(int ax, int ay)
02995 {
02996     int layer, mx, my, got_one = 0;
02997 
02998     mx = pl_pos.x+ax;
02999     my = pl_pos.y+ay;
03000 
03001     if (!use_config[CONFIG_FOGWAR] && the_map.cells[mx][my].cleared) {
03002         XFillRectangle(display,win_game,gc_blank,image_size*ax,image_size*ay,image_size,image_size);
03003         return;
03004     }
03005 
03006     XFillRectangle(display,xpm_pixmap,gc_clear_xpm,0,0,image_size,image_size);
03007     for (layer=0; layer<MAXLAYERS; layer++) {
03008         int sx, sy;
03009 
03010         /* draw single-tile faces first */
03011         int face = mapdata_face(ax, ay, layer);
03012         if (face > 0) {
03013             int w = pixmaps[face]->width;
03014             int h = pixmaps[face]->height;
03015             gen_draw_face(xpm_pixmap, face, 0, 0, (w-1)*image_size, (h-1)*image_size);
03016             got_one = 1;
03017         }
03018 
03019         /* draw big faces last (should overlap other objects) */
03020         face = mapdata_bigface(ax, ay, layer, &sx, &sy);
03021         if (face > 0) {
03022             gen_draw_face(xpm_pixmap, face, 0, 0, sx*image_size, sy*image_size);
03023             got_one = 1;
03024         }
03025     }
03026     if (got_one) {
03027         if (the_map.cells[mx][my].cleared) {
03028             XSetClipOrigin(display, gc_xpm[XPMGCS-1], 0, 0);
03029             XSetForeground(display, gc_xpm[XPMGCS-1], discolor[0].pixel);
03030             XSetClipMask(display, gc_xpm[XPMGCS-1], dark2);
03031             XCopyPlane(display, dark2, xpm_pixmap, gc_xpm[XPMGCS-1], 0, 0, image_size, image_size, 0, 0, 1);
03032             xpm_masks[XPMGCS-1] = dark2;
03033         }
03034         else if (use_config[CONFIG_DARKNESS] && the_map.cells[mx][my].have_darkness) {
03035             int darkness;
03036             XSetClipOrigin(display, gc_xpm[XPMGCS-1], 0, 0);
03037             XSetForeground(display, gc_xpm[XPMGCS-1], discolor[0].pixel);
03038             darkness = the_map.cells[mx][my].darkness;
03039             if (darkness > 192) {
03040                 XSetClipMask(display, gc_xpm[XPMGCS-1], dark1);
03041                 XCopyPlane(display, dark1, xpm_pixmap, gc_xpm[XPMGCS-1], 0, 0, image_size, image_size, 0, 0, 1);
03042                 xpm_masks[XPMGCS-1] = dark1;
03043             }
03044             else if (darkness > 128) {
03045                 XSetClipMask(display, gc_xpm[XPMGCS-1], dark2);
03046                 XCopyPlane(display, dark2, xpm_pixmap, gc_xpm[XPMGCS-1], 0, 0, image_size, image_size, 0, 0, 1);
03047                 xpm_masks[XPMGCS-1] = dark2;
03048             }
03049             else if (darkness > 64) {
03050                 XSetClipMask(display, gc_xpm[XPMGCS-1], dark3);
03051                 XCopyPlane(display, dark3, xpm_pixmap, gc_xpm[XPMGCS-1], 0, 0, image_size, image_size, 0, 0, 1);
03052                 xpm_masks[XPMGCS-1] = dark3;
03053             }
03054         }
03055         XCopyArea(display, xpm_pixmap, win_game, gc_game, 0, 0, image_size, image_size, image_size*ax, image_size*ay);
03056     } else {
03057         XFillRectangle(display, win_game, gc_blank, image_size*ax, image_size*ay, image_size, image_size);
03058     }
03059 }
03060 
03061 void resize_map_window(int x, int y)
03062 {
03063     if (!want_config[CONFIG_SPLITWIN]) {
03064         XWindowAttributes attrib;
03065         int width=0, height=0;
03066 
03067         /* width and height are how much larger we need to make
03068          * the window to keep it displayable.  This isn't perfect,
03069          * but does a reasonable job.  Don't do shrinks
03070          */
03071 
03072         if (use_config[CONFIG_MAPWIDTH] > old_mapx) width = (use_config[CONFIG_MAPWIDTH] - old_mapx)* image_size;
03073 
03074         if (use_config[CONFIG_MAPHEIGHT] > old_mapy) height = (use_config[CONFIG_MAPHEIGHT] - old_mapy)* image_size;
03075 
03076         /* if somethign to do */
03077         if (width>0 || height > 0) {
03078             XGetWindowAttributes(display, win_root, &attrib);
03079             width += attrib.width;
03080             height += attrib.height;
03081             XResizeWindow(display, win_game, x*image_size, y*image_size);
03082             XResizeWindow(display, win_root, width, height);
03083         }
03084         old_mapx=use_config[CONFIG_MAPWIDTH];
03085         old_mapy=use_config[CONFIG_MAPHEIGHT];
03086     }
03087     else {
03088         XResizeWindow(display, win_game, x*image_size, y*image_size);
03089     }
03090 }
03091 
03092 /* we don't need to do anything, as we figure this out as needed */
03093 void x_set_echo(void) { }
03094 
03095 
03101 void display_map_doneupdate(int redraw, int notice)
03102 {
03103     int ax,ay, mx, my;
03104 
03105     if(notice) {
03106         return;
03107     }
03108 
03109     if (cpl.showmagic) {
03110         magic_map_flash_pos();
03111         return;
03112     }
03113 
03114     XSetClipMask(display,gc_floor,None);
03115     for(ax=0;ax<use_config[CONFIG_MAPWIDTH];ax++) {
03116         for(ay=0;ay<use_config[CONFIG_MAPHEIGHT];ay++) {
03117             mx = pl_pos.x+ax;
03118             my = pl_pos.y+ay;
03119             if (redraw || the_map.cells[mx][my].need_update)  {
03120                 display_mapcell(ax, ay);
03121                 the_map.cells[mx][my].need_update = 0;
03122             }
03123         }
03124     }
03125     XFlush(display);
03126 }
03127 
03128 int display_mapscroll(int dx, int dy)
03129 {
03130     int srcx, srcy;
03131     int dstx, dsty;
03132     int w, h;
03133 
03134     srcx = dx > 0 ? image_size*dx : 0;
03135     srcy = dy > 0 ? image_size*dy : 0;
03136     dstx = dx < 0 ? image_size*-dx : 0;
03137     dsty = dy < 0 ? image_size*-dy : 0;
03138 
03139     w = use_config[CONFIG_MAPWIDTH];
03140     w -= abs(dx);
03141     h = use_config[CONFIG_MAPHEIGHT];
03142     h -= abs(dy);
03143 
03144     XCopyArea(display, win_game, win_game, gc_copy,
03145         srcx, srcy,
03146         w*image_size, h*image_size,
03147         dstx, dsty);
03148 
03149     return 1;
03150 }
03151 
03152 /* This functions associates the image_data in the cache entry
03153  * with the specific pixmap number.  Returns 0 on success, -1
03154  * on failure.  Currently, there is no failure condition, but
03155  * there is the potential that in the future, we want to more
03156  * closely look at the data and if it isn't valid, return
03157  * the failure code.
03158  */
03159 int associate_cache_entry(Cache_Entry *ce, int pixnum)
03160 {
03161     pixmaps[pixnum] = ce->image_data;
03162     return 0;
03163 }
03164 
03165 void redisplay_stats(void)
03166 {
03167   int i;
03168   for(i=0;i<7;i++) {
03169     XDrawImageString(display,win_stats,
03170                    gc_stats,10,i*14+10, stats_buff[i],strlen(stats_buff[i]));
03171 }
03172   XFlush(display);
03173 }
03174 
03175 void display_map_startupdate(void)
03176 {
03177 }
03178 
03179 /* This function draws the magic map in the game window.  I guess if
03180  * we wanted to get clever, we could open up some other window or
03181  * something.
03182  *
03183  * A lot of this code was taken from server/xio.c  But being all
03184  * the map data has been figured, it tends to be much simpler.
03185  */
03186 void draw_magic_map(void)
03187 {
03188     XWindowAttributes win_info;
03189     int x, y;
03190 
03191     if (!cpl.magicmap) {
03192         draw_info ("You have yet to cast magic map.",NDI_BLACK);
03193         return;
03194     }
03195 
03196     /* Do this in case the window has been resized. */
03197     XGetWindowAttributes(display, win_game, &win_info);
03198 
03199     /* server used to set a grey background because grey was not used much.
03200      * however, with the magic map cleanup, that is not necessarily true,
03201      * so just set it to the default.
03202      */
03203     XClearWindow(display,win_game);
03204 
03205     cpl.mapxres = (win_info.width-4)/cpl.mmapx;
03206     cpl.mapyres = (win_info.height-4)/cpl.mmapy;
03207     if (cpl.mapxres < 1 || cpl.mapyres<1) {
03208         fprintf(stderr,"magic map resolution less than 1, map is %dx%d\n",
03209                 cpl.mmapx, cpl.mmapy);
03210         return;
03211     }
03212     /* In theory, cpl.mapxres and cpl.mapyres do not have to be the same.  However,
03213      * it probably makes sense to keep them the same value.
03214      * Need to take the smaller value.
03215      */
03216     if (cpl.mapxres>cpl.mapyres) cpl.mapxres=cpl.mapyres;
03217     else cpl.mapyres=cpl.mapxres;
03218 
03219     if (cpl.mapxres>24) {
03220         cpl.mapxres=24;
03221         cpl.mapyres=24;
03222     }
03223     /* this is keeping the same unpacking scheme that the server uses
03224      * to pack it up.
03225      */
03226     for (y = 0; y < cpl.mmapy; y++) {
03227         for (x = 0; x < cpl.mmapx; x++) {
03228             uint8 val = cpl.magicmap[y*cpl.mmapx + x];
03229             XSetForeground(display,gc_game,
03230                 discolor[val&FACE_COLOR_MASK].pixel);
03231             XFillRectangle(display,win_game,
03232                 gc_game, cpl.mapxres*x, cpl.mapyres*y, cpl.mapxres, cpl.mapyres);
03233         } /* Saw into this space */
03234     }
03235 }
03236 
03237 /* Basically, this just flashes the player position on the magic map */
03238 void magic_map_flash_pos(void)
03239 {
03240     if (!cpl.showmagic) return;
03241     cpl.showmagic ^=2;
03242     if (cpl.showmagic & 2) {
03243         XSetForeground(display, gc_game, foreground);
03244         XSetBackground(display, gc_game, background);
03245     } else {
03246         XSetForeground(display, gc_game, background);
03247         XSetBackground(display, gc_game, foreground);
03248     }
03249     XFillRectangle(display, win_game, gc_game, cpl.mapxres*cpl.pmapx,
03250                    cpl.mapyres*cpl.pmapy, cpl.mapxres, cpl.mapyres);
03251 }
03252 
03253 /* We can now connect to different servers, so we need to clear out
03254  * any old images.  We try to free the data also to prevent memory
03255  * leaks.
03256  * This could be more clever, ie, if we're caching images and go to
03257  * a new server and get a name, we should try to re-arrange our cache
03258  * or the like.
03259  */
03260 
03261 void reset_image_data(void)
03262 {
03263     int i;
03264 
03265     reset_image_cache_data();
03266 
03267     for (i=1; i<MAXPIXMAPNUM; i++) {
03268         if (!want_config[CONFIG_CACHE] && pixmaps[i] != pixmaps[0]) {
03269             XFreePixmap(display, pixmaps[i]->pixmap);
03270             if (pixmaps[i]->mask) {
03271                 XFreePixmap(display, pixmaps[i]->mask);
03272             }
03273             free(pixmaps[i]);
03274             pixmaps[i] = pixmaps[0];
03275         }
03276     }
03277     look_list.env=cpl.below;
03278 }
03279 
03280 
03281 /* Gets a specified windows coordinates.  This function is pretty much
03282  * an exact copy out of the server.
03283  */
03284 
03285 static void get_window_coord(Window win,
03286                  int *x,int *y,
03287                  int *wx,int *wy,
03288                  unsigned int *w,unsigned int *h)
03289 {
03290   Window root,child;
03291   unsigned int tmp;
03292 
03293   XGetGeometry(display,win,&root,x,y,w,h,&tmp,&tmp);
03294   XTranslateCoordinates(display,win,root,0,0,wx,wy, &child);
03295 }
03296 
03297 
03298 
03299 void save_winpos(void)
03300 {
03301     char savename[MAX_BUF],buf[MAX_BUF];
03302     FILE    *fp;
03303     int     x,y,wx,wy;
03304     unsigned int w,h;
03305 
03306     if (!want_config[CONFIG_SPLITWIN]) {
03307         draw_info("You can only save window positions in split windows mode", NDI_BLUE);
03308         return;
03309     }
03310     sprintf(savename,"%s/.crossfire/winpos", getenv("HOME"));
03311     if (!(fp=fopen(savename,"w"))) {
03312         sprintf(buf,"Unable to open %s, window positions not saved",savename);
03313         draw_info(buf,NDI_BLUE);
03314         return;
03315     }
03316     /* This is a bit simpler than what the server was doing - it has
03317      * some code to handle goofy window managers which I am not sure
03318      * is still needed.
03319      */
03320     get_window_coord(win_game, &x,&y, &wx,&wy,&w,&h);
03321     fprintf(fp,"win_game: %d %d %d %d\n", wx,wy, w, h);
03322     get_window_coord(win_stats, &x,&y, &wx,&wy,&w,&h);
03323     fprintf(fp,"win_stats: %d %d %d %d\n", wx,wy, w, h);
03324     get_window_coord(infodata.win_info, &x,&y, &wx,&wy,&w,&h);
03325     fprintf(fp,"win_info: %d %d %d %d\n", wx,wy, w, h);
03326     get_window_coord(inv_list.win, &x,&y, &wx,&wy,&w,&h);
03327     fprintf(fp,"win_inv: %d %d %d %d\n", wx,wy, w, h);
03328     get_window_coord(look_list.win, &x,&y, &wx,&wy,&w,&h);
03329     fprintf(fp,"win_look: %d %d %d %d\n", wx,wy, w, h);
03330     get_window_coord(win_message, &x,&y, &wx,&wy,&w,&h);
03331     fprintf(fp,"win_message: %d %d %d %d\n", wx,wy, w, h);
03332     fclose(fp);
03333     sprintf(buf,"Window positions saved to %s",savename);
03334     draw_info(buf,NDI_BLUE);
03335 }
03336 
03337 /* Reads in the winpos file created by the above function and sets the
03338  * the window positions appropriately.
03339  */
03340 void set_window_pos(void)
03341 {
03342     unsigned int xwc_mask = CWX|CWY|CWWidth|CWHeight;
03343     XWindowChanges xwc;
03344     char buf[MAX_BUF],*cp;
03345     FILE *fp;
03346 
03347     if (!want_config[CONFIG_SPLITWIN]) return;
03348 
03349     sprintf(buf,"%s/.crossfire/winpos", getenv("HOME"));
03350     if (!(fp=fopen(buf,"r"))) return;
03351 
03352     while(fgets(buf,MAX_BUF-1, fp)!=NULL) {
03353         buf[MAX_BUF-1]='\0';
03354         if (!(cp=strchr(buf,' '))) continue;
03355         *cp++='\0';
03356         if (sscanf(cp,"%d %d %d %d",&xwc.x,&xwc.y,&xwc.width,&xwc.height)!=4)
03357             continue;
03358         if (!strcmp(buf,"win_game:"))
03359             XConfigureWindow(display,win_game,xwc_mask, &xwc);
03360         if (!strcmp(buf,"win_stats:"))
03361             XConfigureWindow(display,win_stats,xwc_mask, &xwc);
03362         if (!strcmp(buf,"win_info:"))
03363             XConfigureWindow(display,infodata.win_info,xwc_mask, &xwc);
03364         if (!strcmp(buf,"win_inv:"))
03365             XConfigureWindow(display,inv_list.win,xwc_mask, &xwc);
03366         if (!strcmp(buf,"win_look:"))
03367             XConfigureWindow(display,look_list.win,xwc_mask, &xwc);
03368         if (!strcmp(buf,"win_message:"))
03369             XConfigureWindow(display,win_message,xwc_mask, &xwc);
03370 
03371     }
03372     fclose(fp);
03373 }
03374 
03375 
03376 void load_defaults(void)
03377 {
03378     char path[MAX_BUF],inbuf[MAX_BUF],*cp;
03379     FILE *fp;
03380 
03381     sprintf(path,"%s/.crossfire/defaults", getenv("HOME"));
03382     if ((fp=fopen(path,"r"))==NULL) return;
03383     while (fgets(inbuf, MAX_BUF-1, fp)) {
03384         inbuf[MAX_BUF-1]='\0';
03385         inbuf[strlen(inbuf)-1]='\0';    /* kill newline */
03386 
03387         if (inbuf[0]=='#') continue;
03388         /* IF no colon, then we certainly don't have a real value, so just skip */
03389         if (!(cp=strchr(inbuf,':'))) continue;
03390         *cp='\0';
03391         cp+=2;      /* colon, space, then value */
03392 
03393         if (!strcmp(inbuf, "port")) {
03394             use_config[CONFIG_PORT] = atoi(cp);
03395             continue;
03396         }
03397         if (!strcmp(inbuf, "server")) {
03398             server = strdup_local(cp);  /* memory leak ! */
03399             continue;
03400         }
03401         if (!strcmp(inbuf,"cacheimages")) {
03402             if (!strcmp(cp,"True")) want_config[CONFIG_CACHE]=TRUE;
03403             else want_config[CONFIG_CACHE]=FALSE;
03404             continue;
03405         }
03406         if (!strcmp(inbuf,"split")) {
03407             if (!strcmp(cp,"True")) want_config[CONFIG_SPLITWIN]=TRUE;
03408             else want_config[CONFIG_SPLITWIN]=FALSE;
03409             continue;
03410         }
03411         if (!strcmp(inbuf,"showicon")) {
03412             if (!strcmp(cp,"True")) inv_list.show_icon=TRUE;
03413             else inv_list.show_icon=FALSE;
03414             continue;
03415         }
03416         if (!strcmp(inbuf,"scrolllines")) {
03417             infodata.maxlines = atoi(cp);
03418             continue;
03419         }
03420         if (!strcmp(inbuf,"scrollinfo")) {
03421             if (!strcmp(cp,"True")) infodata.scroll_info_window=TRUE;
03422             else infodata.scroll_info_window=FALSE;
03423             continue;
03424         }
03425         if (!strcmp(inbuf,"sound")) {
03426             if (!strcmp(cp,"True")) want_config[CONFIG_SOUND]=TRUE;
03427             else want_config[CONFIG_SOUND]=FALSE;
03428             continue;
03429         }
03430         if (!strcmp(inbuf,"command_window")) {
03431             use_config[CONFIG_CWINDOW] = atoi(cp);
03432             if (use_config[CONFIG_CWINDOW]<1 || use_config[CONFIG_CWINDOW]>127)
03433                 use_config[CONFIG_CWINDOW]=COMMAND_WINDOW;
03434             continue;
03435         }
03436         if (!strcmp(inbuf,"foodbeep")) {
03437             if (!strcmp(cp,"True")) use_config[CONFIG_FOODBEEP]=TRUE;
03438             else use_config[CONFIG_FOODBEEP]=FALSE;
03439             continue;
03440         }
03441         if (!strcmp(inbuf,"noautorepeat")) {
03442             if (!strcmp(cp,"True")) noautorepeat=TRUE;
03443             else noautorepeat=FALSE;
03444             continue;
03445         }
03446         if (!strcmp(inbuf,"font")) {
03447             font_name = strdup_local(cp);
03448             continue;
03449         }
03450         fprintf(stderr,"Got line we did not understand: %s: %s\n", inbuf, cp);
03451     }
03452     fclose(fp);
03453 }
03454 
03455 void save_defaults(void)
03456 {
03457     char path[MAX_BUF],buf[MAX_BUF];
03458     FILE *fp;
03459 
03460     sprintf(path,"%s/.crossfire/defaults", getenv("HOME"));
03461     if (make_path_to_file(path)==-1) {
03462         fprintf(stderr,"Could not create %s\n", path);
03463         return;
03464     }
03465     if ((fp=fopen(path,"w"))==NULL) {
03466         fprintf(stderr,"Could not open %s\n", path);
03467         return;
03468     }
03469     fprintf(fp,"# This file is generated automatically by cfclient.\n");
03470     fprintf(fp,"# Manually editing is allowed, however cfclient may be a bit finicky about\n");
03471     fprintf(fp,"# some of the matching it does.  all comparissons are case sensitive.\n");
03472     fprintf(fp,"# 'True' and 'False' are the proper cases for those two values\n");
03473 
03474     fprintf(fp,"port: %d\n", use_config[CONFIG_PORT]);
03475     fprintf(fp,"server: %s\n", server);
03476     fprintf(fp,"font: %s\n", font_name);
03477     fprintf(fp,"cacheimages: %s\n", want_config[CONFIG_CACHE]?"True":"False");
03478     fprintf(fp,"split: %s\n", want_config[CONFIG_SPLITWIN]?"True":"False");
03479     fprintf(fp,"showicon: %s\n", inv_list.show_icon?"True":"False");
03480     fprintf(fp,"scrolllines: %d\n", infodata.maxlines);
03481     fprintf(fp,"scrollinfo: %s\n", infodata.scroll_info_window?"True":"False");
03482     fprintf(fp,"sound: %s\n", want_config[CONFIG_SOUND]?"True":"False");
03483     fprintf(fp,"command_window: %d\n", use_config[CONFIG_CWINDOW]);
03484     fprintf(fp,"foodbeep: %s\n", use_config[CONFIG_FOODBEEP]?"True":"False");
03485     fprintf(fp,"noautorepeat: %s\n", noautorepeat?"True":"False");
03486 
03487     fclose(fp);
03488     sprintf(buf,"Defaults saved to %s",path);
03489     draw_info(buf,NDI_BLUE);
03490 }
03491 
03492 /* determine what we show in the inventory window.  This is a slightly
03493  * more complicated version than the server side, since we use a bitmask
03494  * which means we could show things like magical and cursed, or unpaid
03495  * and magical, etc.  Current time, we don't really support setting it
03496  * all that well.
03497  *
03498  */
03499 
03500 void command_show (const char *params)
03501 {
03502     if(!params) {
03503         if (inv_list.show_what==show_all) inv_list.show_what = show_applied;
03504         else { /* rotate the bit.  If no valid bits are set, start over */
03505             inv_list.show_what = inv_list.show_what << 1;
03506             if (!(inv_list.show_what & show_mask))
03507                 inv_list.show_what = show_all;
03508         }
03509         inv_list.env->inv_updated =1;
03510         return;
03511     }
03512 
03513     if (!strncmp(params, "all", strlen(params)))
03514         inv_list.show_what = show_all;
03515     else if (!strncmp(params, "applied", strlen(params)))
03516         inv_list.show_what = show_applied;
03517     else if (!strncmp(params, "unapplied", strlen(params)))
03518         inv_list.show_what = show_unapplied;
03519     else if (!strncmp(params, "unpaid", strlen(params)))
03520         inv_list.show_what = show_unpaid;
03521     else if (!strncmp(params, "cursed", strlen(params)))
03522         inv_list.show_what = show_cursed;
03523     else if (!strncmp(params, "magical", strlen(params)))
03524         inv_list.show_what = show_magical;
03525     else if (!strncmp(params, "nonmagical", strlen(params)))
03526         inv_list.show_what = show_nonmagical;
03527     else if (!strncmp(params, "locked", strlen(params)))
03528         inv_list.show_what = show_locked;
03529     else if (!strncmp(params, "unlocked", strlen(params)))
03530         inv_list.show_what = show_unlocked;
03531 
03532     inv_list.env->inv_updated =1;
03533 }
03534 
03535 /* This doesn't need to do anything for x11 client */
03536 void cleanup_connection(void) { }
03537 
03538 
03539 int main(int argc, char *argv[])
03540 {
03541     int sound,got_one=0;
03542 
03543     /* This needs to be done first.  In addition to being quite quick,
03544      * it also sets up some paths (client_libdir) that are needed by
03545      * the other functions.
03546      */
03547 
03548     init_client_vars();
03549 
03550     /* Call this very early.  It should parse all command
03551      * line arguments and set the pertinent ones up in
03552      * globals.  Also call it early so that if it can't set up
03553      * the windowing system, we get an error before trying to
03554      * to connect to the server.  And command line options will
03555      * likely change on the server we connect to.
03556      */
03557     if (init_windows(argc, argv)) {     /* x11.c */
03558         fprintf(stderr,"Failure to init windows.\n");
03559         exit(1);
03560     }
03561     csocket.inbuf.buf=malloc(MAXSOCKBUF);
03562 
03563 #ifdef HAVE_SYSCONF
03564     maxfd = sysconf(_SC_OPEN_MAX);
03565 #else
03566     maxfd = getdtablesize();
03567 #endif
03568 
03569     sound = init_sounds();
03570 
03571     /* Loop to connect to server/metaserver and play the game */
03572     while (1) {
03573         reset_client_vars();
03574         csocket.inbuf.len=0;
03575         csocket.cs_version=0;
03576 
03577         /* Perhaps not the best assumption, but we are taking it that
03578          * if the player has not specified a server (ie, server
03579          * matches compiled in default), we use the meta server.
03580          * otherwise, use the server provided, bypassing metaserver.
03581          * Also, if the player has already played on a server once (defined
03582          * by got_one), go to the metaserver.  That gives them the oppurtunity
03583          * to quit the client or select another server.  We should really add
03584          * an entry for the last server there also.
03585          */
03586 
03587         display_map_doneupdate(FALSE, FALSE);
03588         if (!server || got_one) {
03589             char *ms;
03590             metaserver_get_info(meta_server, meta_port);
03591             metaserver_show(TRUE);
03592             do {
03593                 ms=get_metaserver();
03594             } while (metaserver_select(ms));
03595             negotiate_connection(sound);
03596         } else {
03597             csocket.fd=init_connection(server, use_config[CONFIG_PORT]);
03598             if (csocket.fd == -1) { /* specified server no longer valid */
03599                 server = NULL;
03600                 continue;
03601             }
03602             negotiate_connection(sound);
03603         }
03604 
03605         got_one=1;
03606         event_loop();
03607         /* if event_loop has exited, we most of lost our connection, so we
03608          * loop again to establish a new one.
03609          */
03610 
03611         mapdata_reset();
03612         /* Need to reset the images so they match up properly and prevent
03613          * memory leaks.
03614          */
03615         reset_image_data();
03616         remove_item_inventory(cpl.ob);
03617         /* We know the following is the private map structure in
03618          * item.c.  But we don't have direct access to it, so
03619          * we still use locate.
03620          */
03621         remove_item_inventory(locate_item(0));
03622         look_list.env=cpl.below;
03623     }
03624     exit(0);    /* never reached */
03625 }