ADDED bindings.c Index: bindings.c ================================================================== --- /dev/null +++ bindings.c @@ -0,0 +1,159 @@ +#if 0 +gcc -s -O2 -c -Wno-unused-result bindings.c `sdl-config --cflags` +exit +#endif + +/* + This program is part of Free Hero Mesh and is public domain. +*/ + +#define HEROMESH_BINDINGS +#include +#include +#include +#include "SDL.h" +#include "sqlite3.h" +#include "smallxrm.h" +#include "quarks.h" +#include "heromesh.h" + +#define MOD_SHIFT 1 +#define MOD_CTRL 2 +#define MOD_ALT 4 +#define MOD_META 8 +#define MOD_NUMLOCK 14 +typedef struct { + UserCommand m[16]; +} KeyBinding; + +static KeyBinding*editor_bindings[SDLK_LAST]; +static KeyBinding*game_bindings[SDLK_LAST]; +static KeyBinding*editor_mouse_bindings[4]; +static KeyBinding*game_mouse_bindings[4]; +static int cur_modifiers,loose_modifiers; + +static void set_binding(KeyBinding**pkb,int mod,const char*txt) { + int i; + KeyBinding*kb=*pkb; + UserCommand*uc; + if(!*txt) return; + if(!kb) kb=*pkb=calloc(1,sizeof(KeyBinding)); + if(!kb) fatal("Allocation failed\n"); + uc=kb->m+mod; + if(uc->cmd) return; + switch(*txt) { + case '^': // Miscellaneous + uc->cmd='^'; + uc->n=txt[1]; + break; + case '=': case '-': case '+': // Restart, rewind, advance + uc->cmd=*txt; + uc->n=strtol(txt+1,0,0); + break; + case '\'': // Input move + uc->cmd='\''; + for(i=1;i<256;i++) if(heromesh_key_names[i] && !strcmp(txt+1,heromesh_key_names[i])) { + uc->n=i; + return; + } + fatal("Error in key binding: %s\nInvalid Hero Mesh key name\n",txt); + break; + case '!': // System command + uc->cmd='!'; + uc->txt=txt+1; + break; + case 'A' ... 'Z': case 'a' ... 'z': // Execute SQL statement + uc->cmd='s'; + if(i=sqlite3_prepare_v3(userdb,txt,-1,SQLITE_PREPARE_PERSISTENT,&uc->stmt,0)) fatal("Error in key binding: %s\n%s\n",txt,sqlite3_errmsg(userdb)); + break; + default: + fatal("Error in key binding: %s\nUnrecognized character\n",txt); + } +} + +#define BINDING_MODIFIER(a,b) \ + if(q==a && !(cur_modifiers&b)) { \ + cur_modifiers|=b; \ + xrm_enumerate(sub,cb_2,usr); \ + cur_modifiers&=~b; \ + if(loose) --loose_modifiers; \ + return 0; \ + } + +static void*cb_2(xrm_db*db,void*usr,int loose,xrm_quark q) { + KeyBinding**kb=usr; + const char*txt; + xrm_db*sub=xrm_sub(db,loose,q); + if(loose) ++loose_modifiers; + BINDING_MODIFIER(Q_shift,MOD_SHIFT); + BINDING_MODIFIER(Q_ctrl,MOD_CTRL); + BINDING_MODIFIER(Q_alt,MOD_ALT); + BINDING_MODIFIER(Q_meta,MOD_META); + BINDING_MODIFIER(Q_numLock,MOD_NUMLOCK); + if(txt=xrm_get(sub)) { + if(usr==editor_mouse_bindings || usr==game_mouse_bindings) { + switch(q) { + case Q_left: kb+=SDL_BUTTON_LEFT; break; + case Q_middle: kb+=SDL_BUTTON_MIDDLE; break; + case Q_right: kb+=SDL_BUTTON_RIGHT; break; + default: goto stop; + } + } else { + if(q>=FirstKeyQuark && q<=LastKeyQuark) kb+=quark_to_key[q-FirstKeyQuark]; else goto stop; + } + set_binding(kb,cur_modifiers,txt); + if(loose_modifiers) { + int i; + for(i=0;i<16;i++) if((i&cur_modifiers)==cur_modifiers && !kb[0]->m[i].cmd) kb[0]->m[i]=kb[0]->m[cur_modifiers]; + } + } +stop: + if(loose) --loose_modifiers; + return 0; +} + +static void*cb_1(xrm_db*db,void*usr) { + xrm_enumerate(db,cb_2,usr); + return 0; +} + +void load_key_bindings(void) { + fprintf(stderr,"Loading key bindings...\n"); + cur_modifiers=loose_modifiers=0; + optionquery[1]=Q_editKey; + xrm_search(resourcedb,optionquery,optionquery,2,cb_1,editor_bindings); + optionquery[1]=Q_gameKey; + xrm_search(resourcedb,optionquery,optionquery,2,cb_1,game_bindings); + optionquery[1]=Q_editClick; + xrm_search(resourcedb,optionquery,optionquery,2,cb_1,editor_mouse_bindings); + optionquery[1]=Q_gameClick; + xrm_search(resourcedb,optionquery,optionquery,2,cb_1,game_mouse_bindings); + fprintf(stderr,"Done\n"); +} + +const UserCommand*find_key_binding(SDL_Event*ev,int editing) { + KeyBinding*kb; + static const UserCommand nul={cmd:0}; + SDLMod m; + int i; + if(ev->type==SDL_KEYDOWN) { + m=ev->key.keysym.mod; + if(ev->key.keysym.sym>=SDLK_LAST) return &nul; + kb=(editing?editor_bindings:game_bindings)[ev->key.keysym.sym]; + } else if(ev->type==SDL_MOUSEBUTTONDOWN) { + m=SDL_GetModState(); + if(ev->button.button>3) return &nul; + kb=(editing?editor_mouse_bindings:game_mouse_bindings)[ev->button.button]; + } else { + return &nul; + } + if(!kb) return &nul; + i=0; + if(m&KMOD_CTRL) i|=MOD_CTRL; + if(m&KMOD_SHIFT) i|=MOD_SHIFT; + if(m&KMOD_ALT) i|=MOD_ALT; + if(m&KMOD_META) i|=MOD_META; + if((m&KMOD_NUM) && kb->m[i|MOD_NUMLOCK].cmd) i|=MOD_NUMLOCK; + return kb->m+i; +} + Index: heromesh.h ================================================================== --- heromesh.h +++ heromesh.h @@ -22,10 +22,14 @@ Uint32 u; }; Uint32 t; // Type 0-15, or a generation_number of an object } Value; +extern const char*const standard_message_names[]; +extern const char*const standard_sound_names[]; +extern const char*const heromesh_key_names[256]; + extern sqlite3*userdb; extern xrm_db*resourcedb; extern const char*basefilename; extern xrm_quark optionquery[16]; extern Uint32 generation_number; @@ -80,5 +84,19 @@ extern Class*classes[0x4000]; // 0 isn't used extern const char*messages[0x4000]; // index is 256 less than message number extern int max_animation; // max steps in animation queue extern Sint32 max_volume; // max total volume to allow moving diagonally (default 10000) +// == bindings == + +typedef struct { + char cmd; + union { + int n; + sqlite3_stmt*stmt; + const char*txt; + }; +} UserCommand; + +void load_key_bindings(void); +const UserCommand*find_key_binding(SDL_Event*ev,int editing); + Index: main.c ================================================================== --- main.c +++ main.c @@ -1,7 +1,7 @@ #if 0 -gcc -s -O2 -o ~/bin/heromesh main.c picture.o smallxrm.o sqlite3.o `sdl-config --cflags --libs` -ldl -lpthread +gcc -s -O2 -o ~/bin/heromesh main.c picture.o bindings.o smallxrm.o sqlite3.o `sdl-config --cflags --libs` -ldl -lpthread exit #endif /* This program is part of Free Hero Mesh and is public domain. @@ -19,28 +19,10 @@ #include "names.h" #include "quarks.h" #include "cursorshapes.h" #include "heromesh.h" -typedef struct { - char cmd; - union { - int n; - sqlite3_stmt*stmt; - const char*txt; - }; -} UserCommand; - -#define MOD_SHIFT 1 -#define MOD_CTRL 2 -#define MOD_ALT 4 -#define MOD_META 8 -#define MOD_NUMLOCK 14 -typedef struct { - UserCommand m[16]; -} KeyBinding; - static const char schema[]= "BEGIN;" "PRAGMA APPLICATION_ID(1296388936);" "PRAGMA RECURSIVE_TRIGGERS(1);" "CREATE TABLE IF NOT EXISTS `USERCACHEINDEX`(`ID` INTEGER PRIMARY KEY, `NAME` TEXT, `TIME` INT);" @@ -59,14 +41,10 @@ static FILE*levelfp; static FILE*solutionfp; static FILE*hamarc_fp; static long hamarc_pos; static char main_options[128]; -static KeyBinding*editor_bindings[SDLK_LAST]; -static KeyBinding*game_bindings[SDLK_LAST]; -static KeyBinding*editor_mouse_bindings[4]; -static KeyBinding*game_mouse_bindings[4]; static void hamarc_begin(FILE*fp,const char*name) { while(*name) fputc(*name++,fp); fwrite("\0\0\0\0",1,5,hamarc_fp=fp); hamarc_pos=ftell(fp); @@ -170,118 +148,10 @@ if(fscanf(fp," %255s",s)!=1) fatal("Unable to scan name of class set\n"); globalclassname=s; return !*s; } -static void set_key_binding(KeyBinding**pkb,int mod,const char*txt) { - int i; - KeyBinding*kb=*pkb; - UserCommand*uc; - if(!*txt) return; - if(!kb) kb=*pkb=calloc(1,sizeof(KeyBinding)); - uc=kb->m+mod; - switch(*txt) { - case '^': // Miscellaneous - uc->cmd='^'; - uc->n=txt[1]; - break; - case '=': case '-': case '+': // Restart, rewind, advance - uc->cmd=*txt; - uc->n=strtol(txt+1,0,0); - break; - case '\'': // Input move - uc->cmd='\''; - for(i=1;i<256;i++) if(heromesh_key_names[i] && !strcmp(txt+1,heromesh_key_names[i])) { - uc->n=i; - return; - } - fatal("Error in key binding: %s\nInvalid Hero Mesh key name\n",txt); - break; - case '!': // System command - uc->cmd='!'; - uc->txt=txt+1; - break; - case 'A' ... 'Z': case 'a' ... 'z': // Execute SQL statement - uc->cmd='s'; - if(i=sqlite3_prepare_v3(userdb,txt+1,-1,SQLITE_PREPARE_PERSISTENT,&uc->stmt,0)) fatal("Error in key binding: %s\n%s\n",txt,sqlite3_errmsg(userdb)); - break; - default: - fatal("Error in key binding: %s\nUnrecognized character\n",txt); - } -} - -#define SetKeyBinding(n,m) do { \ - optionquery[1]=Q_editKey; \ - if(s=xrm_get_resource(resourcedb,optionquery,optionquery,n)) set_key_binding(editor_bindings+quark_to_key[q-FirstKeyQuark],m,s); \ - optionquery[1]=Q_gameKey; \ - if(s=xrm_get_resource(resourcedb,optionquery,optionquery,n)) set_key_binding(game_bindings+quark_to_key[q-FirstKeyQuark],m,s); \ -} while(0) -#define SetMouseBinding(o,n,m) do { \ - optionquery[1]=Q_editClick; \ - if(s=xrm_get_resource(resourcedb,optionquery,optionquery,n)) set_key_binding(editor_mouse_bindings+o,m,s); \ - optionquery[1]=Q_gameClick; \ - if(s=xrm_get_resource(resourcedb,optionquery,optionquery,n)) set_key_binding(game_mouse_bindings+o,m,s); \ -} while(0) -static void load_key_bindings(void) { - xrm_quark q; - const char*s; - for(q=FirstKeyQuark;q<=LastKeyQuark;q++) { - optionquery[2]=optionquery[3]=optionquery[4]=q; - SetKeyBinding(3,0); - optionquery[2]=Q_shift; - SetKeyBinding(4,MOD_SHIFT); - optionquery[2]=Q_ctrl; - SetKeyBinding(4,MOD_CTRL); - optionquery[2]=Q_alt; - SetKeyBinding(4,MOD_ALT); - optionquery[2]=Q_meta; - SetKeyBinding(4,MOD_META); - optionquery[2]=Q_numLock; - SetKeyBinding(4,MOD_NUMLOCK); - optionquery[3]=Q_shift; - SetKeyBinding(5,MOD_NUMLOCK|MOD_SHIFT); -#if 0 - optionquery[2]=Q_alt; - SetKeyBinding(5,MOD_ALT|MOD_SHIFT); - optionquery[2]=Q_ctrl; - SetKeyBinding(5,MOD_CTRL|MOD_SHIFT); - optionquery[2]=Q_meta; - SetKeyBinding(5,MOD_META|MOD_SHIFT); -#endif - } - optionquery[2]=optionquery[3]=Q_left; - SetMouseBinding(SDL_BUTTON_LEFT,3,0); - optionquery[2]=Q_shift; - SetMouseBinding(SDL_BUTTON_LEFT,4,MOD_SHIFT); - optionquery[2]=Q_ctrl; - SetMouseBinding(SDL_BUTTON_LEFT,4,MOD_CTRL); - optionquery[2]=Q_alt; - SetMouseBinding(SDL_BUTTON_LEFT,4,MOD_ALT); - optionquery[2]=Q_meta; - SetMouseBinding(SDL_BUTTON_LEFT,4,MOD_META); - optionquery[2]=optionquery[3]=Q_middle; - SetMouseBinding(SDL_BUTTON_MIDDLE,3,0); - optionquery[2]=Q_shift; - SetMouseBinding(SDL_BUTTON_MIDDLE,4,MOD_SHIFT); - optionquery[2]=Q_ctrl; - SetMouseBinding(SDL_BUTTON_MIDDLE,4,MOD_CTRL); - optionquery[2]=Q_alt; - SetMouseBinding(SDL_BUTTON_MIDDLE,4,MOD_ALT); - optionquery[2]=Q_meta; - SetMouseBinding(SDL_BUTTON_MIDDLE,4,MOD_META); - optionquery[2]=optionquery[3]=Q_right; - SetMouseBinding(SDL_BUTTON_RIGHT,3,0); - optionquery[2]=Q_shift; - SetMouseBinding(SDL_BUTTON_RIGHT,4,MOD_SHIFT); - optionquery[2]=Q_ctrl; - SetMouseBinding(SDL_BUTTON_RIGHT,4,MOD_CTRL); - optionquery[2]=Q_alt; - SetMouseBinding(SDL_BUTTON_RIGHT,4,MOD_ALT); - optionquery[2]=Q_meta; - SetMouseBinding(SDL_BUTTON_RIGHT,4,MOD_META); -} - static int test_sql_callback(void*usr,int argc,char**argv,char**name) { int i; if(argc) printf("%s",*argv); for(i=1;icmd) { + case 0: printf("\n"); break; + case '^': printf(" %c\n",uc->n); break; + case '=': printf(" %d\n",uc->n); break; + case '-': printf(" %d\n",uc->n); break; + case '+': printf(" %d\n",uc->n); break; + case '\'': printf(" %s (%d)\n",heromesh_key_names[uc->n],uc->n); break; + case '!': printf(" %s",uc->txt); break; + case 's': printf(" %s",sqlite3_sql(uc->stmt)); break; + default: printf("\n"); + } + break; + case SDL_QUIT: + exit(0); + break; + } } int main(int argc,char**argv) { int optind=1; + fprintf(stderr,"FREE HERO MESH\n"); while(argc>optind && argv[optind][0]=='-') { int i; const char*s=argv[optind++]; if(s[1]=='-' && !s[2]) break; for(i=1;s[i];i++) main_options[s[i]&127]=1; @@ -359,14 +270,15 @@ } load_options(); if(argc>optind) read_options(argc-optind,argv+optind); *optionquery=xrm_make_quark(globalclassname,0)?:xrm_anyq; init_sql(); + load_key_bindings(); init_screen(); load_pictures(); if(main_options['T']) { test_mode(); return 0; } return 0; } Index: names.h ================================================================== --- names.h +++ names.h @@ -18,11 +18,11 @@ #define MSG_DESTROYED 16 #define MSG_CREATED 17 #define MSG_POSTINIT 18 #define MSG_END_TURN 19 #define MSG_CLEANUP 20 -static const char*const standard_message_names[]={ +const char*const standard_message_names[]={ "INIT", "CREATE", "DESTROY", "BEGIN_TURN", "ARRIVED", @@ -91,11 +91,11 @@ #define SND_FAROUT 45 #define SND_KEWEL 46 #define SND_WHACK 47 #define SND_STEAM 48 #define SND_HAWK 49 -static const char*const standard_sound_names[]={ +const char*const standard_sound_names[]={ "SPLASH", "POUR", "DOOR", "GLASS", "BANG", @@ -143,11 +143,11 @@ "KEWEL", "WHACK", "STEAM", "HAWK", }; -static const char*const heromesh_key_names[256]={ +const char*const heromesh_key_names[256]={ [8]="BACK", [9]="TAB", [12]="CENTER", [13]="ENTER", [16]="SHIFT", Index: names.js ================================================================== --- names.js +++ names.js @@ -135,15 +135,15 @@ [..."ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"].forEach(x=>{ heromesh_key_names[x.charCodeAt()]=x; }); console.log("// Auto-generated! Do not modify directly!"); standard_message_names.forEach(([a,b,c])=>console.log("#define MSG_"+c+" "+b)); -console.log("static const char*const standard_message_names[]={"); +console.log("const char*const standard_message_names[]={"); standard_message_names.forEach(([a,b,c])=>console.log(" \""+c+"\",")); console.log("};"); standard_sound_names.forEach((x,y)=>console.log("#define SND_"+x+" "+y)); -console.log("static const char*const standard_sound_names[]={"); +console.log("const char*const standard_sound_names[]={"); standard_sound_names.forEach(x=>console.log(" \""+x+"\",")); console.log("};"); -console.log("static const char*const heromesh_key_names[256]={"); +console.log("const char*const heromesh_key_names[256]={"); Object.keys(heromesh_key_names).forEach(x=>console.log(" ["+x+"]=\""+heromesh_key_names[x]+"\",")); console.log("};"); Index: picture.c ================================================================== --- picture.c +++ picture.c @@ -213,10 +213,11 @@ Uint16 havesize[256]; char*nam=sqlite3_mprintf("%s.xclass",basefilename); const char*v; int i,j,n; if(!nam) fatal("Allocation failed\n"); + fprintf(stderr,"Loading pictures...\n"); fp=fopen(nam,"r"); if(!fp) fatal("Failed to open xclass file (%m)\n"); sqlite3_free(nam); optionquery[1]=Q_altImage; altImage=strtol(xrm_get_resource(resourcedb,optionquery,optionquery,2)?:"0",0,10); @@ -295,10 +296,11 @@ } sqlite3_finalize(st); fclose(fp); sqlite3_exec(userdb,"COMMIT;",0,0,0); SDL_SetColorKey(picts,SDL_SRCCOLORKEY|SDL_RLEACCEL,0); + fprintf(stderr,"Done\n"); } void init_screen(void) { const char*v; int w,h,i; Index: quarks.h ================================================================== --- quarks.h +++ quarks.h @@ -346,11 +346,11 @@ "sqlSmallAllocations", "sqlCoveringIndexScan", "sqlPowerSafe", "level", 0}; -#ifdef HEROMESH_MAIN +#ifdef HEROMESH_BINDINGS static const SDLKey quark_to_key[Q_undo+1-Q_backspace]={ SDLK_BACKSPACE, SDLK_TAB, SDLK_CLEAR, SDLK_RETURN, Index: quarks.js ================================================================== --- quarks.js +++ quarks.js @@ -2,8 +2,8 @@ const fs=require("fs"); const quarks=fs.readFileSync("quarks","ascii").split("\n").map(x=>x.trim()).filter(x=>x&&x[0]!="!"); quarks.forEach((x,y)=>console.log("#define Q_"+x+" "+(y+2))); console.log("static const char*const global_quarks[]={"); quarks.forEach(x=>console.log(" \""+x+"\",")); -console.log("0};\n#ifdef HEROMESH_MAIN\nstatic const SDLKey quark_to_key[Q_undo+1-Q_backspace]={"); +console.log("0};\n#ifdef HEROMESH_BINDINGS\nstatic const SDLKey quark_to_key[Q_undo+1-Q_backspace]={"); quarks.slice(quarks.indexOf("backspace"),quarks.indexOf("undo")+1).forEach(x=>console.log("SDLK_"+x[x.length==1?"toLowerCase":"toUpperCase"]()+",")); console.log("};\n#define FirstKeyQuark Q_backspace\n#define LastKeyQuark Q_undo\n#endif"); Index: smallxrm.c ================================================================== --- smallxrm.c +++ smallxrm.c @@ -103,12 +103,12 @@ void*xrm_enumerate(xrm_db*db,void*(*cb)(xrm_db*,void*,int,xrm_quark),void*usr) { int i; void*r; if(!db) return 0; - for(i=0;il.n;i++) if(r=cb(db,usr,1,db->l.p[i].k)) return r; for(i=0;it.n;i++) if(r=cb(db,usr,0,db->t.p[i].k)) return r; + for(i=0;il.n;i++) if(r=cb(db,usr,1,db->l.p[i].k)) return r; return 0; } const char*xrm_get(xrm_db*db) { return db?db->v:0;