Crossfire Client, Branch
R11627
|
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 }