Crossfire Client, Branch  R11627
keys.c
Go to the documentation of this file.
00001 const char * const rcsid_gtk_keys_c =
00002     "$Id: keys.c 10997 2008-12-17 03:36:53Z kbulgrien $";
00003 /*
00004     Crossfire client, a client program for the crossfire program.
00005 
00006     Copyright (C) 2001 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 
00032 #include <config.h>
00033 #include <stdlib.h>
00034 #include <sys/stat.h>
00035 #ifndef WIN32
00036 #include <unistd.h>
00037 #endif
00038 
00039 /*#include <X11/keysym.h>*/
00040 
00041 /* Pick up the gtk headers we need */
00042 #include <gtk/gtk.h>
00043 #ifndef WIN32
00044 #include <gdk/gdkx.h>
00045 #else
00046 #include <gdk/gdkwin32.h>
00047 #define NoSymbol 0L /* Special KeySym */
00048 typedef int KeyCode; /* Undefined type */
00049 #include <io.h> /* access( ) */
00050 #endif
00051 #include <gdk/gdkkeysyms.h>
00052 
00053 #include "client-types.h"
00054 #include "gx11.h"
00055 #include "client.h"
00056 #include "p_cmd.h"
00057 
00058 #include "def-keys.h"
00059 
00060 #include "gtkproto.h"
00061 /******************************************************************************
00062  *
00063  * Code related to face caching.
00064  *
00065  *****************************************************************************/
00066 
00067 typedef struct Keys {
00068     uint8       flags;
00069     sint8       direction;
00070     uint32      keysym;
00071     char        *command;
00072     struct Keys *next;
00073 } Key_Entry;
00074 
00075 /***********************************************************************
00076  *
00077  * Key board input translations are handled here.  We don't deal with
00078  * the events, but rather KeyCodes and KeySyms.
00079  *
00080  * It would be nice to deal with only KeySyms, but many keyboards
00081  * have keys that do not correspond to a KeySym, so we do need to
00082  * support KeyCodes.
00083  *
00084  ***********************************************************************/
00085 
00086 
00087 static uint32 firekeysym[2], runkeysym[2], commandkeysym,*bind_keysym,
00088     prevkeysym, nextkeysym, completekeysym, cancelkeysym;
00089 static int bind_flags=0;
00090 static char bind_buf[MAX_BUF];
00091 
00092 #define KEYF_NORMAL     0x01    /* Used in normal mode */
00093 #define KEYF_FIRE       0x02    /* Used in fire mode */
00094 #define KEYF_RUN        0x04    /* Used in run mode */
00095 #define KEYF_MODIFIERS  0x07    /* Mask for actual keyboard modifiers, */
00096                                 /* not action modifiers */
00097 #define KEYF_EDIT       0x08    /* Line editor */
00098 #define KEYF_STANDARD   0x10    /* For standard (built in) key definitions */
00099 
00100 extern const char * const directions[9];
00101 
00102 /* Platform independence defines that we can't use keycodes.
00103  * instead, make it a hash, and set KEYHASH to a prime number for
00104  * this purpose.
00105  */
00106 #define KEYHASH 257
00107 static Key_Entry *keys[KEYHASH];
00108 
00109 
00110 
00111 /* Updates the keys array with the keybinding that is passed.  All the
00112  * arguments are pretty self explanatory.  flags is the various state
00113  * that the keyboard is in.
00114  * This function is common to both gdk and x11 client
00115  */
00116 static void insert_key(uint32 keysym, int flags, const char *command)
00117 {
00118 
00119     Key_Entry *newkey;
00120     int i, direction=-1, slot;
00121 
00122     slot = keysym % KEYHASH;
00123 
00124     if (keys[slot]==NULL) {
00125         keys[slot]=malloc(sizeof(Key_Entry));
00126         keys[slot]->command=NULL;
00127         keys[slot]->next=NULL;
00128         newkey=keys[slot];
00129     } else {
00130         newkey=keys[slot];
00131         while (newkey->next!=NULL)
00132             newkey = newkey->next;
00133         newkey->next = calloc(1, sizeof(Key_Entry));
00134         newkey = newkey->next;
00135     }
00136 
00137     /* Try to find out if the command is a direction command.  If so, we
00138      * then want to keep track of this fact, so in fire or run mode,
00139      * things work correctly.
00140      */
00141     for (i=0; i<9; i++)
00142         if (!strcmp(command, directions[i])) {
00143                 direction=i;
00144                 break;
00145         }
00146 
00147     newkey->keysym = keysym;
00148     newkey->flags = flags;
00149     newkey->command = strdup_local(command);
00150     newkey->direction = direction;
00151 }
00152 
00153 
00154 /* This function is common to both gdk and x11 client */
00155 
00156 static void parse_keybind_line(char *buf, int line, int standard)
00157 {
00158     char *cp, *cpnext;
00159     uint32 keysym;
00160     int flags;
00161 
00162     cp = NULL; /* There may be a rare error case when cp is used uninitialized. So let's be safe */
00163 
00164     if (buf[0]=='#' || buf[0]=='\n') return;
00165     if ((cpnext = strchr(buf,' '))==NULL) {
00166         LOG(LOG_WARNING,"gtk::parse_keybind_line","Line %d (%s) corrupted in keybinding file.", line,buf);
00167         return;
00168     }
00169     /* Special keybinding line */
00170     if (buf[0] == '!') {
00171         char *cp1;
00172         while (*cpnext == ' ') ++cpnext;
00173         cp = strchr(cpnext, ' ');
00174         if (!cp) {
00175             LOG(LOG_WARNING,"gtk::parse_keybind_line","Line %d (%s) corrupted in keybinding file.", line,buf);
00176             return;
00177         }
00178         *cp++ = 0;  /* Null terminate it */
00179         cp1 = strchr(cp, ' ');
00180         if (!cp1) {
00181             LOG(LOG_WARNING,"gtk::parse_keybind_line","Line %d (%s) corrupted in keybinding file.", line,buf);
00182             return;
00183         }
00184         *cp1 ++ = 0;/* Null terminate it */
00185         keysym = gdk_keyval_from_name(cp);
00186         /* As of now, all these keys must have keysyms */
00187         if (keysym == 0) {
00188             LOG(LOG_WARNING,"gtk::parse_keybind_line","Could not convert %s into keysym", cp);
00189             return;
00190         }
00191         if (!strcmp(cpnext,"commandkey")) {
00192             commandkeysym = keysym;
00193             return;
00194         }
00195         if (!strcmp(cpnext,"firekey0")) {
00196             firekeysym[0] = keysym;
00197             return;
00198         }
00199         if (!strcmp(cpnext,"firekey1")) {
00200             firekeysym[1] = keysym;
00201             return;
00202         }
00203         if (!strcmp(cpnext,"runkey0")) {
00204             runkeysym[0] = keysym;
00205             return;
00206         }
00207         if (!strcmp(cpnext,"runkey1")) {
00208             runkeysym[1] = keysym;
00209             return;
00210         }
00211         if (!strcmp(cpnext,"completekey")) {
00212             completekeysym = keysym;
00213             return;
00214         }
00215         if (!strcmp(cpnext,"nextkey")) {
00216             nextkeysym = keysym;
00217             return;
00218         }
00219         if (!strcmp(cpnext,"prevkey")) {
00220             prevkeysym = keysym;
00221             return;
00222         }
00223     } else {
00224         if (standard) standard=KEYF_STANDARD;
00225         else standard=0;
00226 
00227         *cpnext++ = '\0';
00228         keysym = gdk_keyval_from_name(buf);
00229         if (!keysym) {
00230             LOG(LOG_WARNING,"gtk::parse_keybind_line","Unable to convert line %d (%s) into keysym", line, cp);
00231             return;
00232         }
00233         cp = cpnext;
00234         if ((cpnext = strchr(cp,' '))==NULL) {
00235             LOG(LOG_WARNING,"gtk::parse_keybind_line","Line %d (%s) corrupted in keybinding file.", line, cp);
00236             return;
00237         }
00238         *cpnext++ = '\0';
00239 
00240         cp = cpnext;
00241         if ((cpnext = strchr(cp,' '))==NULL) {
00242             LOG(LOG_WARNING,"gtk::parse_keybind_line","Line %d (%s) corrupted in keybinding file.", line, cp);
00243             return;
00244         }
00245         *cpnext++ = '\0';
00246         flags = 0;
00247         while (*cp!='\0') {
00248             switch (*cp) {
00249                 case 'A':
00250                     flags |= KEYF_NORMAL | KEYF_FIRE | KEYF_RUN;
00251                     break;
00252                 case 'N':
00253                     flags |= KEYF_NORMAL;
00254                     break;
00255                 case 'F':
00256                     flags |= KEYF_FIRE;
00257                     break;
00258                 case 'R':
00259                     flags |= KEYF_RUN;
00260                     break;
00261                 case 'E':
00262                     flags |= KEYF_EDIT;
00263                     break;
00264                 case 'S':
00265                     flags |= KEYF_STANDARD;
00266                     break;
00267                 default:
00268                     LOG(LOG_WARNING,"gtk::parse_keybind_line","Unknown flag (%c) line %d in key binding file",
00269                             *cp, line);
00270             }
00271             cp++;
00272         }
00273 
00274         /* Rest of the line is the actual command.  Lets kill the newline */
00275         cpnext[strlen(cpnext)-1]='\0';
00276     if (strlen(cpnext)>(sizeof(bind_buf)-1)){
00277         cpnext[sizeof(bind_buf)-1]='\0';
00278         LOG(LOG_WARNING,"gtk::parse_keybind_line","Had to truncate a too long command");
00279     }
00280         insert_key(keysym, flags | standard, cpnext);
00281     } /* else if not special binding line */
00282 }
00283 
00284 /* This code is common to both x11 and gdk client */
00285 static void init_default_keybindings(void)
00286 {
00287     char buf[MAX_BUF];
00288     int i;
00289 
00290     for(i=0;i< sizeof(def_keys)/sizeof(char *);i++) {
00291         strcpy(buf,def_keys[i]);
00292         parse_keybind_line(buf,i,1);
00293     }
00294 }
00295 
00296 
00297 /* This reads in the keybindings, and initializes any special values.
00298  * called by init_windows.
00299  */
00300 /* This function is common to both x11 and gdk client */
00301 
00302 void init_keys(void)
00303 {
00304     int i, line=0;
00305     FILE *fp;
00306     char buf[BIG_BUF];
00307     static int was_init = 0;
00308 
00309     commandkeysym = GDK_apostrophe;
00310     firekeysym[0] =GDK_Shift_L;
00311     firekeysym[1] =GDK_Shift_R;
00312     runkeysym[0]  =GDK_Control_L;
00313     runkeysym[1]  =GDK_Control_R;
00314 
00315     completekeysym = GDK_Tab;
00316     cancelkeysym = GDK_Escape;
00317 
00318     /* Don't set these to anything by default.  At least on sun
00319      * keyboards, the keysym for up on both the keypad and arrow
00320      * keys is the same, so player needs to rebind this so we get proper
00321      * keycode.  Very unfriendly to log in and not be able to move north/south.
00322      */
00323     nextkeysym = NoSymbol;
00324     prevkeysym = NoSymbol;
00325 
00326     for (i=0; i<KEYHASH; i++) {
00327         if ( was_init && keys[i] )
00328         {
00329             Key_Entry* next;
00330             Key_Entry* cur = keys[i];
00331             while ( cur )
00332             {
00333                 next = cur->next;
00334                 free(cur->command);
00335                 free(cur);
00336                 cur = next;
00337             }
00338         }
00339         keys[i] = NULL;
00340     }
00341     was_init = 1;
00342 
00343     /* We now try to load the keybindings.  First place to look is the
00344      * users home directory, "~/.crossfire/keys".  Using a directory
00345      * seems like a good idea, in the future, additional stuff may be
00346      * stored.
00347      *
00348      * The format is described in the def_keys file.  Note that this file
00349      * is the same as what it was in the server distribution.  To convert
00350      * bindings in character files to this format, all that needs to be done
00351      * is remove the 'key ' at the start of each line.
00352      *
00353      * We need at least one of these keybinding files to exist - this is
00354      * where the various commands are defined.  In theory, we actually
00355      * don't need to have any of these defined -- the player could just
00356      * bind everything.  Probably not a good idea, however.
00357      */
00358 
00359 #ifdef MULTKEYS
00360     /* For Windows, use player name if defined for key file */
00361     if ( strlen( cpl.name ) )
00362         {
00363         sprintf( buf, "%s/.crossfire/%s.keys", getenv( "HOME" ), cpl.name );
00364         if ( access( buf, 0 ) == -1 )
00365             {
00366             /* Client key file not found, reverting to default file */
00367             sprintf(buf,"%s/.crossfire/keys", getenv("HOME"));
00368             }
00369         }
00370     else
00371         sprintf(buf,"%s/.crossfire/keys", getenv("HOME"));
00372 #else
00373     sprintf(buf,"%s/.crossfire/keys", getenv("HOME"));
00374 #endif
00375     if ((fp=fopen(buf,"r"))==NULL) {
00376         LOG(LOG_INFO,"gtk::init_keys","Could not open ~/.crossfire/keys, trying to load global bindings");
00377         if (client_libdir==NULL) {
00378             init_default_keybindings();
00379             return;
00380         }
00381         sprintf(buf,"%s/def_keys", client_libdir);
00382         if ((fp=fopen(buf,"r"))==NULL) {
00383             init_default_keybindings();
00384             return;
00385         }
00386     }
00387     while (fgets(buf, BIG_BUF, fp)) {
00388         line++;
00389     buf[BIG_BUF-1]='\0';
00390         parse_keybind_line(buf,line,0);
00391     }
00392     fclose(fp);
00393 }
00394 
00395 /* The only things we actually care about is the run and fire keys.
00396  * Other key releases are not important.
00397  * If it is the release of a run or fire key, we tell the client
00398  * to stop firing or running.  In some cases, it is possible that we
00399  * actually are not running or firing, and in such cases, the server
00400  * will just ignore the command.
00401  */
00402 
00403 /* This code is used by gdk and x11 client, but has
00404  *  a fair number of #ifdefs to get the right
00405  * behavioiur
00406  */
00407 static void parse_key_release(uint32 ks) {
00408 
00409     /* Only send stop firing/running commands if we are in actual
00410      * play mode.  Something smart does need to be done when the character
00411      * enters a non play mode with fire or run mode already set, however.
00412      */
00413 
00414     if (ks==firekeysym[0] || ks==firekeysym[1]) {
00415         cpl.fire_on=0;
00416         clear_fire();
00417         gtk_label_set (GTK_LABEL(fire_label),"    ");
00418     }
00419     else if (ks==runkeysym[0] || ks==runkeysym[1]) {
00420         cpl.run_on=0;
00421         if (use_config[CONFIG_ECHO]) draw_info("stop run",NDI_BLACK);
00422         clear_run();
00423         gtk_label_set (GTK_LABEL(run_label),"   ");
00424     }
00425 
00426     /* Firing is handled on server side.  However, to keep more like the
00427      * old version, if you release the direction key, you want the firing
00428      * to stop.  This should do that.
00429      */
00430     else if (cpl.fire_on)
00431         clear_fire();
00432 }
00433 
00434 /* This parses a keypress.  It should only be called when in Playing
00435  * mode.
00436  */
00437 static void parse_key(char key, uint32 keysym)
00438 {
00439     Key_Entry *keyentry, *first_match=NULL;
00440     int present_flags=0;
00441     char buf[MAX_BUF];
00442 
00443     if (keysym==commandkeysym) {
00444         if (use_config[CONFIG_SPLITWIN]) {
00445             gtk_widget_grab_focus (GTK_WIDGET(gtkwin_info));
00446             gtk_widget_grab_focus (GTK_WIDGET(entrytext));
00447         } else {
00448             gtk_widget_grab_focus (GTK_WIDGET(entrytext));
00449         }
00450 
00451         gtk_entry_set_visibility(GTK_ENTRY(entrytext), 1);
00452         cpl.input_state = Command_Mode;
00453         cpl.no_echo=FALSE;
00454         return;
00455     }
00456     if (keysym==firekeysym[0] ||keysym==firekeysym[1]) {
00457         cpl.fire_on=1;
00458         gtk_label_set (GTK_LABEL(fire_label),"Fire");
00459         return;
00460     }
00461     if (keysym==runkeysym[0] || keysym==runkeysym[1]) {
00462         cpl.run_on=1;
00463         gtk_label_set (GTK_LABEL(run_label),"Run");
00464         return;
00465     }
00466 
00467     if (cpl.run_on) present_flags |= KEYF_RUN;
00468     if (cpl.fire_on) present_flags |= KEYF_FIRE;
00469     if (present_flags ==0) present_flags = KEYF_NORMAL;
00470 
00471     keyentry = keys[keysym % KEYHASH];
00472     while (keyentry!=NULL) {
00473         if ((keyentry->keysym!=0 && keyentry->keysym!=keysym) ||
00474             (!(keyentry->flags & present_flags))) {
00475                 keyentry=keyentry->next;
00476                 continue;
00477         }
00478         first_match = keyentry;
00479 
00480         /* Try to find a prefect match */
00481         if ((keyentry->flags & KEYF_MODIFIERS)!= present_flags) {
00482             keyentry=keyentry->next;
00483             continue;
00484         }
00485         else break;
00486     }
00487     if (first_match!=NULL) {
00488         if (first_match->flags & KEYF_EDIT) {
00489             strcpy(cpl.input_text, first_match->command);
00490             cpl.input_state = Command_Mode;
00491             gtk_entry_set_text(GTK_ENTRY(entrytext),cpl.input_text);
00492             gtk_widget_grab_focus (GTK_WIDGET(entrytext));
00493 #ifdef CFGTK2
00494         gtk_editable_select_region(GTK_EDITABLE(entrytext),strlen(cpl.input_text),-1);
00495 #endif
00496             return;
00497         }
00498 
00499         if (first_match->direction>=0) {
00500             if (cpl.fire_on) {
00501                 sprintf(buf,"fire %s", first_match->command);
00502                 /* Some spells (dimension door) need a valid count value */
00503                 cpl.count = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(counttext));
00504                 fire_dir(first_match->direction);
00505             }
00506             else if (cpl.run_on) {
00507                 run_dir(first_match->direction);
00508                 sprintf(buf,"run %s", first_match->command);
00509             }
00510             else {
00511                 extended_command(first_match->command);
00512             }
00513             if (use_config[CONFIG_ECHO]) draw_info(first_match->command,NDI_BLACK);
00514         }
00515         else {
00516             if (use_config[CONFIG_ECHO]) draw_info(first_match->command,NDI_BLACK);
00517             extended_command(first_match->command);
00518         }
00519         return;
00520     }
00521     if (key>='0' && key<='9') {
00522         cpl.count = cpl.count*10 + (key-'0');
00523         if (cpl.count>100000) cpl.count%=100000;
00524         gtk_spin_button_set_value (GTK_SPIN_BUTTON(counttext), (float) cpl.count );
00525         return;
00526     }
00527     sprintf(buf, "Key unused (%s%s%s)",
00528           (cpl.fire_on? "Fire&": ""),
00529           (cpl.run_on ? "Run&" : ""),
00530           keysym==NoSymbol? "unknown": gdk_keyval_name(keysym));
00531 #ifdef WIN32
00532        if ( ( 65513 != keysym ) && ( 65511 != keysym ) )
00533 #endif
00534     draw_info(buf,NDI_BLACK);
00535     cpl.count=0;
00536 }
00537 
00538 
00539 /* This returns a character string desribing the key. */
00540 /* If save_mode is true, it means that the format used for saving
00541  * the information is used, instead of the usual format for displaying
00542  * the information in a friendly manner.
00543  */
00544 static char * get_key_info(Key_Entry *key, int save_mode)
00545 {
00546     /* bind buf is the maximum space allowed for a
00547      * binded command. We will add additional datas to
00548      * it so we increase by MAX_BUF*/
00549     static char buf[MAX_BUF+sizeof(bind_buf)];
00550 
00551     char buff[MAX_BUF];
00552     int bi=0;
00553 
00554     if ((key->flags & KEYF_MODIFIERS) == KEYF_MODIFIERS)
00555         buff[bi++] ='A';
00556     else {
00557         if (key->flags & KEYF_NORMAL)
00558           buff[bi++] ='N';
00559         if (key->flags & KEYF_FIRE)
00560           buff[bi++] ='F';
00561         if (key->flags & KEYF_RUN)
00562           buff[bi++] ='R';
00563     }
00564     if (key->flags & KEYF_EDIT)
00565         buff[bi++] ='E';
00566     if (key->flags & KEYF_STANDARD)
00567         buff[bi++] ='S';
00568 
00569     buff[bi]='\0';
00570     if (save_mode) {
00571         if(key->keysym == NoSymbol) {
00572           sprintf(buf, "(null) %i %s %s",
00573                 0,buff, key->command);
00574         }
00575         else {
00576           sprintf(buf, "%s %i %s %s",
00577                     gdk_keyval_name(key->keysym), 0,
00578                     buff, key->command);
00579         }
00580     }
00581     else {
00582         if(key->keysym == NoSymbol) {
00583           sprintf(buf, "key (null) %s %s",
00584                 buff, key->command);
00585         }
00586         else {
00587           sprintf(buf, "key %s %s %s",
00588                     gdk_keyval_name(key->keysym),
00589                     buff, key->command);
00590         }
00591     }
00592     return buf;
00593 }
00594 
00595 /* Shows all the keybindings.  allbindings me we also show the standard
00596  * (default) keybindings.
00597  */
00598 
00599 static void show_keys(int allbindings)
00600 {
00601     int i, count=1;
00602     Key_Entry *key;
00603     char buf[MAX_BUF + sizeof( bind_buf )];
00604 
00605   sprintf(buf, "Commandkey %s",
00606           commandkeysym==NoSymbol?"unknown":gdk_keyval_name(commandkeysym));
00607   draw_info(buf,NDI_BLACK);
00608 
00609   sprintf(buf, "Firekeys 1: %s, 2: %s",
00610           firekeysym[0]==NoSymbol?"unknown":gdk_keyval_name(firekeysym[0]),
00611           firekeysym[1]==NoSymbol?"unknown":gdk_keyval_name(firekeysym[1]));
00612   draw_info(buf,NDI_BLACK);
00613 
00614   sprintf(buf, "Runkeys 1: %s, 2: %s",
00615           runkeysym[0]==NoSymbol?"unknown":gdk_keyval_name(runkeysym[0]),
00616           runkeysym[1]==NoSymbol?"unknown":gdk_keyval_name(runkeysym[1]));
00617   draw_info(buf,NDI_BLACK);
00618 
00619   sprintf(buf, "Command Completion Key %s",
00620           completekeysym==NoSymbol?"unknown":gdk_keyval_name(completekeysym));
00621   draw_info(buf,NDI_BLACK);
00622 
00623   sprintf(buf, "Next Command in History Key %s",
00624           nextkeysym==NoSymbol?"unknown":gdk_keyval_name(nextkeysym));
00625   draw_info(buf,NDI_BLACK);
00626 
00627   sprintf(buf, "Previous Command in History Key %s",
00628           prevkeysym==NoSymbol?"unknown":gdk_keyval_name(prevkeysym));
00629   draw_info(buf,NDI_BLACK);
00630 
00631 
00632   /* Perhaps we should start at 8, so that we only show 'active'
00633    * keybindings?
00634    */
00635   for (i=0; i<KEYHASH; i++) {
00636         for (key=keys[i]; key!=NULL; key =key->next) {
00637             if (key->flags & KEYF_STANDARD && !allbindings) continue;
00638 
00639             sprintf(buf,"%3d %s",count,  get_key_info(key,0));
00640             draw_info(buf,NDI_BLACK);
00641             count++;
00642         }
00643   }
00644 }
00645 
00646 
00647 
00648 void bind_key(const char *params)
00649 {
00650     /* Must have enough room for MAX_BUF and 'Push key to bind to ''.' */
00651     char buf[MAX_BUF + 20];
00652 
00653     if (!params) {
00654         draw_info("Usage: bind [-nfre] {<commandline>/commandkey/firekey{1/2}/runkey{1/2}/",NDI_BLACK);
00655         draw_info("           completekey/nextkey/prevkey}",NDI_BLACK);
00656         return;
00657     }
00658 
00659     /* Skip over any spaces we may have */
00660     while (*params==' ') params++;
00661 
00662     if (!strcmp(params, "commandkey")) {
00663         bind_keysym = &commandkeysym;
00664         draw_info("Push key to bind new commandkey.",NDI_BLACK);
00665         cpl.input_state = Configure_Keys;
00666         return;
00667     }
00668 
00669     if (!strcmp(params, "firekey1")) {
00670         bind_keysym = & firekeysym[0];
00671     draw_info("Push key to bind new firekey 1.",NDI_BLACK);
00672         cpl.input_state = Configure_Keys;
00673         return;
00674     }
00675     if (!strcmp(params, "firekey2")) {
00676         bind_keysym = & firekeysym[1];
00677         draw_info("Push key to bind new firekey 2.",NDI_BLACK);
00678         cpl.input_state = Configure_Keys;
00679         return;
00680     }
00681     if (!strcmp(params, "runkey1")) {
00682         bind_keysym = &runkeysym[0];
00683         draw_info("Push key to bind new runkey 1.",NDI_BLACK);
00684         cpl.input_state = Configure_Keys;
00685         return;
00686     }
00687 
00688     if (!strcmp(params, "runkey2")) {
00689         bind_keysym = &runkeysym[1];
00690         draw_info("Push key to bind new runkey 2.",NDI_BLACK);
00691         cpl.input_state = Configure_Keys;
00692         return;
00693     }
00694 
00695     if (!strcmp(params, "completekey")) {
00696         bind_keysym = &completekeysym;
00697         draw_info("Push key to bind new command completeion key",NDI_BLACK);
00698         cpl.input_state = Configure_Keys;
00699         return;
00700     }
00701 
00702     if (!strcmp(params, "prevkey")) {
00703         bind_keysym = &prevkeysym;
00704         draw_info("Push key to bind new previous command in history key.",NDI_BLACK);
00705         cpl.input_state = Configure_Keys;
00706         return;
00707     }
00708 
00709     if (!strcmp(params, "nextkey")) {
00710         bind_keysym = &nextkeysym;
00711         draw_info("Push key to bind new next command in history key.",NDI_BLACK);
00712         cpl.input_state = Configure_Keys;
00713         return;
00714     }
00715 
00716 
00717     if (params[0] != '-')
00718         bind_flags =KEYF_MODIFIERS;
00719     else {
00720         bind_flags =0;
00721         bind_keysym=NULL;
00722         for (params++; *params != ' '; params++)
00723         switch (*params) {
00724             case 'n':
00725                 bind_flags |= KEYF_NORMAL;
00726                 break;
00727             case 'f':
00728                 bind_flags |= KEYF_FIRE;
00729                 break;
00730             case 'r':
00731                 bind_flags |= KEYF_RUN;
00732                 break;
00733             case 'e':
00734                 bind_flags |= KEYF_EDIT;
00735                 break;
00736             case '\0':
00737                 draw_info("Try unbind to remove bindings..",NDI_BLACK);
00738                 return;
00739             default:
00740                 sprintf(buf, "Unknown flag to bind: '%c'", *params);
00741                 draw_info(buf,NDI_BLACK);
00742                 return;
00743         }
00744         params++;
00745     }
00746 
00747     if (!(bind_flags & KEYF_MODIFIERS))
00748         bind_flags |= KEYF_MODIFIERS;
00749 
00750     if (!params[0]) {
00751         draw_info("Try unbind to remove bindings..",NDI_BLACK);
00752         return;
00753     }
00754 
00755     /* params is read only, so we need to the truncation on
00756      * the buffer we will store it in, not params
00757      */
00758     strncpy(bind_buf, params, sizeof(bind_buf)-1);
00759     bind_buf[sizeof(bind_buf)-1] = 0;
00760 
00761     if (strlen(params) >= sizeof(bind_buf)) {
00762         draw_info("Keybinding too long! Truncated:",NDI_RED);
00763         draw_info(bind_buf,NDI_RED);
00764     }
00765 
00766     sprintf(buf, "Push key to bind '%s'.", bind_buf);
00767     draw_info(buf,NDI_BLACK);
00768 
00769     cpl.input_state = Configure_Keys;
00770     return;
00771 }
00772 
00773 
00774 /* This is a recursive function that saves all the entries for a particular
00775  * entry.  We save the first element first, and then go through
00776  * and save the rest of the elements.  In this way, the ordering of the key
00777  * entries in the
00778  * file remains the same.
00779  */
00780 
00781 static void save_individual_key(FILE *fp, Key_Entry *key, KeyCode kc)
00782 {
00783     if (key==NULL) return;
00784     fprintf(fp, "%s\n", get_key_info(key, 1));
00785     save_individual_key(fp, key->next, kc);
00786 }
00787 
00788 static void save_keys(void)
00789 {
00790     char buf[MAX_BUF], buf2[MAX_BUF];
00791     int i;
00792     FILE *fp;
00793 
00794 #ifdef MULTKEYS
00795     /* Use player's name if available */
00796     if ( strlen( cpl.name ) )
00797         sprintf( buf,"%s/.crossfire/%s.keys", getenv("HOME"), cpl.name );
00798     else
00799         sprintf( buf,"%s/.crossfire/keys", getenv("HOME") );
00800 #else
00801     sprintf(buf,"%s/.crossfire/keys", getenv("HOME"));
00802 #endif
00803 
00804     if (make_path_to_file(buf)==-1) {
00805         LOG(LOG_WARNING,"gtk::save_keys","Could not create %s", buf);
00806         return;
00807     }
00808     if ((fp=fopen(buf,"w"))==NULL) {
00809         sprintf(buf2,"Could not open %s, key bindings not saved\n", buf);
00810         draw_info(buf2,NDI_BLACK);
00811         return;
00812     }
00813     if (commandkeysym != GDK_apostrophe && commandkeysym != NoSymbol) {
00814         fprintf(fp, "! commandkey %s %d\n",
00815                 gdk_keyval_name(commandkeysym), 0);
00816     }
00817     if (firekeysym[0] != GDK_Shift_L && firekeysym[0] != NoSymbol) {
00818         fprintf(fp, "! firekey0 %s %d\n",
00819                 gdk_keyval_name(firekeysym[0]), 0);
00820     }
00821     if (firekeysym[1] != GDK_Shift_R && firekeysym[1] != NoSymbol) {
00822         fprintf(fp, "! firekey1 %s %d\n",
00823                 gdk_keyval_name(firekeysym[1]), 0);
00824     }
00825     if (runkeysym[0] != GDK_Control_L && runkeysym[0] != NoSymbol) {
00826         fprintf(fp, "! runkey0 %s %d\n",
00827                 gdk_keyval_name(runkeysym[0]), 0);
00828     }
00829     if (runkeysym[1] != GDK_Control_R && runkeysym[1] != NoSymbol) {
00830         fprintf(fp, "! runkey1 %s %d\n",
00831                 gdk_keyval_name(runkeysym[1]), 0);
00832     }
00833     if (completekeysym != GDK_Tab && completekeysym != NoSymbol) {
00834         fprintf(fp, "! completekey %s %d\n",
00835                 gdk_keyval_name(completekeysym), 0);
00836     }
00837     /* No defaults for these, so if it is set to anything, assume its valid */
00838     if (nextkeysym != NoSymbol) {
00839         fprintf(fp, "! nextkey %s %d\n",
00840                 gdk_keyval_name(nextkeysym), 0);
00841     }
00842     if (prevkeysym != NoSymbol) {
00843         fprintf(fp, "! prevkey %s %d\n",
00844                 gdk_keyval_name(prevkeysym), 0);
00845     }
00846 
00847     for (i=0; i<KEYHASH; i++) {
00848     save_individual_key(fp, keys[i], 0);
00849     }
00850     fclose(fp);
00851     /* Should probably check return value on all writes to be sure, but... */
00852     draw_info("key bindings successfully saved.",NDI_BLACK);
00853 }
00854 
00855 static void configure_keys(uint32 keysym)
00856 {
00857     char buf[MAX_BUF];
00858 
00859     /* I think that basically if we are not rebinding the special
00860      * control keys (in which case bind_kesym would be set to something)
00861      * we just want to handle these keypresses as normal events.
00862      */
00863     if (bind_keysym==NULL) {
00864         if(keysym == firekeysym[0] || keysym == firekeysym[1]) {
00865             cpl.fire_on =1;
00866             draw_message_window(0);
00867             return;
00868         }
00869         if(keysym == runkeysym[0] || keysym == runkeysym[1]) {
00870             cpl.run_on =1;
00871             draw_message_window(0);
00872             return;
00873         }
00874     }
00875     cpl.input_state = Playing;
00876     /* Try to be clever - take into account shift/control keys being
00877      * held down when binding keys - in this way, player does not have to use
00878      * -f and -r flags to bind for many simple binds.
00879      */
00880 
00881     if ((cpl.fire_on || cpl.run_on) && (bind_flags & KEYF_MODIFIERS)==KEYF_MODIFIERS) {
00882         bind_flags &= ~KEYF_MODIFIERS;
00883         if (cpl.fire_on) bind_flags |= KEYF_FIRE;
00884         if (cpl.run_on) bind_flags |= KEYF_RUN;
00885     }
00886 
00887     if (bind_keysym!=NULL) {
00888         *bind_keysym=keysym;
00889         bind_keysym=NULL;
00890     }
00891     else {
00892         insert_key(keysym, bind_flags, bind_buf);
00893     }
00894 
00895     sprintf(buf, "Binded to key '%s' (%i)",
00896           keysym==NoSymbol?"unknown":gdk_keyval_name(keysym), keysym);
00897     draw_info(buf,NDI_BLACK);
00898     cpl.fire_on=0;
00899     cpl.run_on=0;
00900     draw_message_window(0);
00901 
00902     /* Do this each time a new key is bound.  This way, we are never actually
00903      * storing any information that needs to be saved when the connection
00904      * dies or the player quits.
00905      */
00906     save_keys();
00907     return;
00908 }
00909 
00910 static void unbind_usage(void)
00911 {
00912     draw_info("Usage: unbind <entry_number> or",NDI_BLACK);
00913     draw_info("Usage: unbind [-a] [-g] to show existing bindings", NDI_BLACK);
00914     draw_info("    -a shows all (global) bindings", NDI_BLACK);
00915     draw_info("    -g unbinds a global binding", NDI_BLACK);
00916 }
00917 
00918 void unbind_key(const char *params)
00919 {
00920     int count=0, keyentry, onkey,global=0;
00921     Key_Entry *key, *tmp;
00922     /* the key macro itself can be bind_buf long, and we need some extra
00923      * space for the sprintf unbind message.
00924      */
00925     char buf[sizeof(bind_buf)+60];
00926 
00927     if (params==NULL || params[0]=='\0') {
00928         show_keys(0);
00929         return;
00930     }
00931 
00932     /* Skip over any spaces we may have */
00933     while (*params==' ') params++;
00934 
00935     if (!strcmp(params,"-a")) {
00936         show_keys(1);
00937         return;
00938     }
00939     if (!strncmp(params,"-g",2)) {
00940         global=1;
00941         if (!(params=strchr(params,' ')))  {
00942             unbind_usage();
00943             return;
00944         }
00945     }
00946     if ((keyentry=atoi(params))==0) {
00947         unbind_usage();
00948         return;
00949     }
00950 
00951     for (onkey=0; onkey<KEYHASH; onkey++) {
00952         for (key=keys[onkey]; key; key =key->next) {
00953             if (global || !(key->flags&KEYF_STANDARD)) count++;
00954             /* We found the key we want to unbind */
00955             if (keyentry==count) {
00956 
00957                 /* If it is the first entry, it is easy */
00958                 if (key == keys[onkey]) {
00959                     keys[onkey] = key->next;
00960                     goto unbinded;
00961                 }
00962                 /* Otherwise, we need to figure out where in the link list
00963                  * the entry is.
00964                  */
00965                 for (tmp=keys[onkey]; tmp->next!=NULL; tmp=tmp->next) {
00966                     if (tmp->next == key) {
00967                         tmp->next =key->next;
00968                         goto unbinded;
00969                     }
00970                 }
00971                 LOG(LOG_ERROR,"gtk::unbind_key","found number entry, but could not find actual key");
00972             }
00973         }
00974     }
00975     /* Makes things look better to draw the blank line */
00976     draw_info("",NDI_BLACK);
00977     draw_info("No such entry. Try 'unbind' with no options to find entry.",NDI_BLACK);
00978     return;
00979 
00980     /*
00981      * Found. Now remove it.
00982      */
00983 
00984 unbinded:
00985 
00986     sprintf(buf,"Removed binding: %3d %s", count, get_key_info(key,0));
00987 
00988     draw_info(buf,NDI_BLACK);
00989     free(key->command);
00990     free(key);
00991     save_keys();
00992 }
00993 
00994 void keyrelfunc(GtkWidget *widget, GdkEventKey *event, GtkWidget *window)
00995 {
00996 
00997     updatelock=0;
00998     if (event->keyval>0) {
00999         if (GTK_WIDGET_HAS_FOCUS (entrytext) /*|| GTK_WIDGET_HAS_FOCUS(counttext)*/ ) {
01000         } else {
01001             parse_key_release(event->keyval);
01002             gtk_signal_emit_stop_by_name (GTK_OBJECT(window), "key_release_event") ;
01003         }
01004     }
01005 }
01006 
01007 extern void disconnect(GtkWidget*);
01008 void keyfunc(GtkWidget *widget, GdkEventKey *event, GtkWidget *window) {
01009     char *text;
01010     updatelock=0;
01011 
01012     if (!use_config[CONFIG_POPUPS]) {
01013         if ( ((cpl.input_state == Reply_One) || (cpl.input_state == Reply_Many))
01014             && (event->keyval==cancelkeysym) ) {
01015             /*Player hit cancel button during input. Disconnect it (code from menubar)*/
01016                 disconnect(widget);
01017             return;
01018         }
01019         if  (cpl.input_state == Reply_One) {
01020             text=gdk_keyval_name(event->keyval);
01021             send_reply(text);
01022             cpl.input_state = Playing;
01023             gtk_signal_emit_stop_by_name(GTK_OBJECT(window), "key_press_event");
01024             return;
01025         }
01026         else if (cpl.input_state == Reply_Many) {
01027             if (GTK_WIDGET_HAS_FOCUS(entrytext)) {
01028                 gtk_widget_event(GTK_WIDGET(entrytext), (GdkEvent*)event);
01029                 gtk_signal_emit_stop_by_name(
01030                     GTK_OBJECT(window), "key_press_event");
01031             } else
01032                 gtk_widget_grab_focus(GTK_WIDGET(entrytext));
01033             return;
01034         }
01035     }
01036 
01037     /* Better check for really weirdo keys, X doesnt like keyval 0*/
01038     if (event->keyval<=0) return;
01039 
01040     if (GTK_WIDGET_HAS_FOCUS (entrytext) /*|| GTK_WIDGET_HAS_FOCUS(counttext)*/) {
01041         if (event->keyval == completekeysym) {
01042         gtk_complete_command();
01043         return;
01044     }
01045         if (event->keyval == prevkeysym || event->keyval == nextkeysym)
01046             gtk_command_history(event->keyval==nextkeysym?0:1);
01047 #ifdef CFGTK2
01048     else
01049         gtk_widget_event(GTK_WIDGET(entrytext), (GdkEvent*)event);
01050         gtk_signal_emit_stop_by_name(GTK_OBJECT(window), "key_press_event");
01051 #endif
01052     } else {
01053         switch(cpl.input_state) {
01054             case Playing:
01055                 /* Specials - do command history - many times, the player
01056                  * will want to go the previous command when nothing is entered
01057                  * in the command window.
01058                  */
01059                 if (event->keyval == prevkeysym || event->keyval == nextkeysym) {
01060                     gtk_command_history(event->keyval==nextkeysym?0:1);
01061                     return;
01062                 }
01063 
01064                 if (cpl.run_on) {
01065                     if (!(event->state & GDK_CONTROL_MASK)) {
01066                         /*printf ("Run is on while ctrl is not\n");*/
01067                         gtk_label_set (GTK_LABEL(run_label),"   ");
01068                         cpl.run_on=0;
01069                         stop_run();
01070                     }
01071                 }
01072                 if (cpl.fire_on) {
01073                     if (!(event->state & GDK_SHIFT_MASK)) {
01074                         /* printf ("Fire is on while shift is not\n");*/
01075                         gtk_label_set (GTK_LABEL(fire_label),"   ");
01076                         cpl.fire_on=0;
01077                         stop_fire();
01078                     }
01079                 }
01080 
01081                 if( (event->state & GDK_CONTROL_MASK) && (event->state & GDK_SHIFT_MASK) &&
01082                    (event->keyval == GDK_i || event->keyval == GDK_I) ) {
01083                     reset_map();
01084                 }
01085 
01086 
01087                 parse_key(event->string[0], event->keyval);
01088                 gtk_signal_emit_stop_by_name (GTK_OBJECT(window), "key_press_event") ;
01089                 break;
01090 
01091             case Configure_Keys:
01092                 configure_keys(event->keyval);
01093                 gtk_signal_emit_stop_by_name (GTK_OBJECT(window), "key_press_event") ;
01094                 break;
01095 
01096             case Command_Mode:
01097                 if (event->keyval == completekeysym) gtk_complete_command();
01098                 if (event->keyval == prevkeysym || event->keyval == nextkeysym)
01099                 gtk_command_history(event->keyval==nextkeysym?0:1);
01100                 else {
01101                     gtk_widget_grab_focus (GTK_WIDGET(entrytext));
01102                     /* When running in split windows mode, entrytext can't get focus because
01103                      * it is in a different window.  So we have to pass the event to it
01104                      * explicitly
01105                      */
01106                     if (GTK_WIDGET_HAS_FOCUS(entrytext)==0)
01107                         gtk_widget_event(GTK_WIDGET(entrytext), (GdkEvent*)event);
01108                 }
01109                 /*
01110                  * Don't pass signal along to default handlers - otherwise, we get
01111                  * get crashes in the clist area (gtk fault I believe)
01112                  */
01113                 gtk_signal_emit_stop_by_name (GTK_OBJECT(window), "key_press_event") ;
01114                 break;
01115 
01116             case Metaserver_Select:
01117                 gtk_widget_grab_focus (GTK_WIDGET(entrytext));
01118                 break;
01119 
01120             default:
01121                 LOG(LOG_ERROR,"gtk::keyfunc","Unknown input state: %d", cpl.input_state);
01122         }
01123 
01124     }
01125 }
01126 
01127 
01128 
01129 void draw_keybindings (GtkWidget *keylist) {
01130     int i, count=1;
01131     Key_Entry *key;
01132     int allbindings=0;
01133     char buff[MAX_BUF];
01134     int bi=0;
01135     char buffer[5][MAX_BUF];
01136     char *buffers[5];
01137     gint tmprow;
01138 
01139     gtk_clist_clear (GTK_CLIST(keylist));
01140     for (i=0; i<KEYHASH; i++) {
01141         for (key=keys[i]; key!=NULL; key =key->next) {
01142             if (key->flags & KEYF_STANDARD && !allbindings) continue;
01143 
01144             bi=0;
01145 
01146             if ((key->flags & KEYF_MODIFIERS) == KEYF_MODIFIERS)
01147                 buff[bi++] ='A';
01148             else {
01149                 if (key->flags & KEYF_NORMAL)
01150                     buff[bi++] ='N';
01151                 if (key->flags & KEYF_FIRE)
01152                     buff[bi++] ='F';
01153                 if (key->flags & KEYF_RUN)
01154                     buff[bi++] ='R';
01155             }
01156             if (key->flags & KEYF_EDIT)
01157                 buff[bi++] ='E';
01158             if (key->flags & KEYF_STANDARD)
01159                 buff[bi++] ='S';
01160 
01161             buff[bi]='\0';
01162 
01163             if(key->keysym != NoSymbol) {
01164                 sprintf(buffer[0], "%i",count);
01165                 sprintf(buffer[1], "%s", gdk_keyval_name(key->keysym));
01166                 sprintf(buffer[2], "%i",i);
01167                 sprintf(buffer[3], "%s",buff);
01168                 sprintf(buffer[4], "%s", key->command);
01169                 buffers[0] = buffer[0];
01170                 buffers[1] = buffer[1];
01171                 buffers[2] = buffer[2];
01172                 buffers[3] = buffer[3];
01173                 buffers[4] = buffer[4];
01174                 tmprow = gtk_clist_append (GTK_CLIST (keylist), buffers);
01175             }
01176             count++;
01177         }
01178     }
01179 }
01180 
01181 void bind_callback (GtkWidget *gtklist, GdkEventButton *event) {
01182     KeySym keysym;
01183     const gchar *entry_text;
01184     const gchar *cpnext;
01185     const gchar *mod="";
01186     char buf[MAX_BUF];
01187 
01188     bind_flags = KEYF_MODIFIERS;
01189 
01190     if ((bind_flags & KEYF_MODIFIERS)==KEYF_MODIFIERS) {
01191         bind_flags &= ~KEYF_MODIFIERS;
01192         mod=gtk_entry_get_text (GTK_ENTRY(cmodentrytext));
01193         if (!strcmp(mod, "F")) {
01194             bind_flags |= KEYF_FIRE;
01195         }
01196         else if (!strcmp(mod, "R")) {
01197             bind_flags |= KEYF_RUN;
01198         }
01199         else if (!strcmp(mod, "A")) {
01200             bind_flags |= KEYF_MODIFIERS;
01201         }
01202     }
01203     cpnext = gtk_entry_get_text (GTK_ENTRY(ckentrytext));
01204     entry_text = gtk_entry_get_text (GTK_ENTRY(ckeyentrytext));
01205 
01206     keysym = gdk_keyval_from_name(entry_text);
01207     insert_key(keysym, bind_flags, cpnext);
01208     save_keys();
01209     draw_keybindings (cclist);
01210     sprintf(buf, "Binded to key '%s' (%i)", gdk_keyval_name(keysym), 0);
01211     draw_info(buf,NDI_BLACK);
01212 }
01213 
01214 void ckeyunbind (GtkWidget *gtklist, GdkEventButton *event) {
01215     gchar *buf;
01216     GList *node;
01217     node =  GTK_CLIST(cclist)->selection;
01218 
01219     if (node) {
01220         /* this line generates an warning about mismatched pointer sizes.  Not sure
01221          * if there is any good fix for it.
01222          * In addition, this appears to be using unsupported logic -
01223          * proper programming logic should not be accessing the clist->selection
01224          * data directly.  Better approach would probably be to catch the selection
01225          * in the clist, then store away what as selected.
01226          */
01227         gtk_clist_get_text (GTK_CLIST(cclist), (gint)node->data, 0, &buf);
01228         unbind_key(buf);
01229         draw_keybindings (cclist);
01230     }
01231 }
01232 
01233 void ckeyentry_callback (GtkWidget *widget, GdkEventKey *event, GtkWidget *window) {
01234   gtk_entry_set_text (GTK_ENTRY(ckeyentrytext),  gdk_keyval_name(event->keyval));
01235 
01236   switch (event->state) {
01237   case GDK_CONTROL_MASK:
01238     gtk_entry_set_text (GTK_ENTRY(cmodentrytext),  "R");
01239     break;
01240   case GDK_SHIFT_MASK:
01241     gtk_entry_set_text (GTK_ENTRY(cmodentrytext),  "F");
01242     break;
01243   default:
01244     gtk_entry_set_text (GTK_ENTRY(cmodentrytext),  "A");
01245   }
01246   /*  gdk_keyval_name(event->keyval);*/
01247   gtk_signal_emit_stop_by_name (GTK_OBJECT(window), "key_press_event");
01248 }
01249 
01250 
01251 void ckeyclear (void) {
01252   gtk_label_set (GTK_LABEL(cnumentrytext), "0");
01253   gtk_entry_set_text (GTK_ENTRY(ckeyentrytext), "Press key to bind here");
01254   /*  gtk_entry_set_text (GTK_ENTRY(cknumentrytext), ""); */
01255   gtk_entry_set_text (GTK_ENTRY(cmodentrytext), "");
01256   gtk_entry_set_text (GTK_ENTRY(ckentrytext), "");
01257 }