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