hedgewars/uInputHandler.pas
branchios-develop
changeset 13413 ba39a1d396c0
parent 13412 236cc4cf2448
child 13465 08a3851aaf67
--- a/hedgewars/uInputHandler.pas	Sun Jun 10 18:56:51 2018 +0200
+++ b/hedgewars/uInputHandler.pas	Sun Jun 10 19:12:26 2018 +0200
@@ -27,6 +27,9 @@
 
 function  KeyNameToCode(name: shortstring): LongInt; inline;
 function  KeyNameToCode(name: shortstring; Modifier: shortstring): LongInt;
+
+function  KeyBindToCode(bind: shortstring): LongInt;
+function  KeyBindToName(bind: shortstring): shortstring;
 //procedure MaskModifier(var code: LongInt; modifier: LongWord);
 procedure MaskModifier(Modifier: shortstring; var code: LongInt);
 procedure ProcessMouse(event: TSDL_MouseButtonEvent; ButtonDown: boolean);
@@ -91,6 +94,54 @@
     MaskModifier(Modifier, code);
     KeyNameToCode:= code;
 end;
+
+// Takes a control name (e.g. 'quit') and returns the corresponding key code,
+// if it has been bound.
+// Returns -1 if the control has not been bound.
+function KeyBindToCode(bind: shortstring): LongInt;
+var code, index: LongInt;
+begin
+    index:= 0;
+    while (index <= High(CurrentBinds.binds)) and (CurrentBinds.binds[index] <> bind) do inc(index);
+    if index > High(CurrentBinds.binds) then
+        // Return error
+        KeyBindToCode:= -1
+    else begin
+        code:= 0;
+        while (code <= High(CurrentBinds.indices)) and (CurrentBinds.indices[code] <> index) do inc(code);
+        checkFails(code <= High(CurrentBinds.indices), 'binds registry inconsistency', True);
+        KeyBindToCode:= code;
+    end;
+end;
+
+// Takes a control name (e.g. 'quit') and returns the corresponding
+// human-readable key name from SDL.
+// FIXME: Does not work 100% for all keys yet, but at least it no
+//        longer hardcodes any key name.
+// TODO: Localize
+function KeyBindToName(bind: shortstring): shortstring;
+var code: LongInt;
+    name: shortstring;
+begin
+    code:= KeyBindToCode(bind);
+    if code = -1 then
+        KeyBindToName:= trmsg[sidUnknownKey]
+    else
+        begin
+        name:= SDL_GetKeyName(SDL_GetKeyFromScancode(code));
+        if (name = 'Escape') then
+            // Let's shorten the name “Escape” for the quit menu
+            KeyBindToName:= 'Esc'
+        else if (length(name) <> 0) then
+            KeyBindToName:= name
+        else
+            begin
+            WriteLnToConsole('Error: KeyBindToName('+bind+') failed to find SDL key name!');
+            KeyBindToName:= trmsg[sidUnknownKey];
+            end;
+        end;
+end;
+
 (*
 procedure MaskModifier(var code: LongInt; Modifier: LongWord);
 begin
@@ -162,18 +213,23 @@
 {$ELSE}
     // on other systems use this shortcut only if the keys are not bound to any command
     if tkbd[KeyNameToCode('left_ctrl')] or tkbd[KeyNameToCode('right_ctrl')] then
-        if ((CurrentBinds[KeyNameToCode('left_ctrl')] = '') or
-            (CurrentBinds[KeyNameToCode('right_ctrl')] = '')) and
-            (CurrentBinds[SDLK_w] = '') then
+        if ((CurrentBinds.indices[KeyNameToCode('left_ctrl')] = 0) or
+            (CurrentBinds.indices[KeyNameToCode('right_ctrl')] = 0)) and
+            (CurrentBinds.indices[SDLK_w] = 0) then
 {$ENDIF}
         ParseCommand('forcequit', true);
     end;
 
-if CurrentBinds[code][0] <> #0 then
+if CurrentBinds.indices[code] > 0 then
     begin
     if (code < cKeyMaxIndex - 2) // means not mouse buttons
         and KeyDown
-        and (not ((CurrentBinds[code] = 'put') or (CurrentBinds[code] = 'ammomenu') or (CurrentBinds[code] = '+cur_u') or (CurrentBinds[code] = '+cur_d') or (CurrentBinds[code] = '+cur_l') or (CurrentBinds[code] = '+cur_r')))
+        and (not ((CurrentBinds.binds[CurrentBinds.indices[code]] = 'put') 
+                  or (CurrentBinds.binds[CurrentBinds.indices[code]] = 'ammomenu') 
+                  or (CurrentBinds.binds[CurrentBinds.indices[code]] = '+cur_u') 
+                  or (CurrentBinds.binds[CurrentBinds.indices[code]] = '+cur_d') 
+                  or (CurrentBinds.binds[CurrentBinds.indices[code]] = '+cur_l') 
+                  or (CurrentBinds.binds[CurrentBinds.indices[code]] = '+cur_r')))
         and (CurrentTeam <> nil) 
         and (not CurrentTeam^.ExtDriven) 
         then bShowAmmoMenu:= false;
@@ -182,20 +238,20 @@
         begin
         Trusted:= Trusted and (not isPaused); //releasing keys during pause should be allowed on the other hand
 
-        if CurrentBinds[code] = 'switch' then
+        if CurrentBinds.binds[CurrentBinds.indices[code]] = 'switch' then
             LocalMessage:= LocalMessage or gmSwitch
-        else if CurrentBinds[code] = '+precise' then
+        else if CurrentBinds.binds[CurrentBinds.indices[code]] = '+precise' then
             LocalMessage:= LocalMessage or gmPrecise;
 
-        ParseCommand(CurrentBinds[code], Trusted);
+        ParseCommand(CurrentBinds.binds[CurrentBinds.indices[code]], Trusted);
         if (CurrentTeam <> nil) and (not CurrentTeam^.ExtDriven) and (ReadyTimeLeft > 1) then
             ParseCommand('gencmd R', true)
         end
-    else if (CurrentBinds[code][1] = '+') then
+    else if (CurrentBinds.binds[CurrentBinds.indices[code]][1] = '+') then
         begin
-        if CurrentBinds[code] = '+precise' then
+        if CurrentBinds.binds[CurrentBinds.indices[code]] = '+precise' then
             LocalMessage:= LocalMessage and (not gmPrecise);
-        s:= CurrentBinds[code];
+        s:= CurrentBinds.binds[CurrentBinds.indices[code]];
         s[1]:= '-';
         ParseCommand(s, Trusted);
         if (CurrentTeam <> nil) and (not CurrentTeam^.ExtDriven) and (ReadyTimeLeft > 1) then
@@ -203,7 +259,7 @@
         end
     else
         begin
-        if CurrentBinds[code] = 'switch' then
+        if CurrentBinds.binds[CurrentBinds.indices[code]] = 'switch' then
             LocalMessage:= LocalMessage and (not gmSwitch)
         end
     end
@@ -274,55 +330,75 @@
         ProcessKey(t, False);
 end;
 
+procedure RegisterBind(var binds: TBinds; key, value: shortstring);
+var code: LongInt;
+begin
+    checkFails(binds.lastIndex < 255, 'too many binds', true);
+
+    code:= KeyNameToCode(key);
+
+    checkFails(code >= 0, 'unknown key', true);
+
+    if binds.indices[code] > 0 then
+    begin
+        binds.binds[binds.indices[code]]:= value
+    end
+    else begin
+        inc(binds.lastIndex);
+        binds.indices[code]:= binds.lastIndex;
+        binds.binds[binds.indices[code]]:= value
+    end;
+end;
 
 procedure InitDefaultBinds;
 var i: Longword;
 begin
-    DefaultBinds[KeyNameToCode('escape')]:= 'quit';
-    DefaultBinds[KeyNameToCode(_S'`')]:= 'history';
-    DefaultBinds[KeyNameToCode('delete')]:= 'rotmask';
+    RegisterBind(DefaultBinds, 'escape', 'quit');
+    RegisterBind(DefaultBinds, _S'`', 'history');
+    RegisterBind(DefaultBinds, 'delete', 'rotmask');
+    RegisterBind(DefaultBinds, 'home', 'rottags');
 
     //numpad
     //DefaultBinds[265]:= '+volup';
     //DefaultBinds[256]:= '+voldown';
 
-    DefaultBinds[KeyNameToCode(_S'0')]:= '+volup';
-    DefaultBinds[KeyNameToCode(_S'9')]:= '+voldown';
-    DefaultBinds[KeyNameToCode(_S'8')]:= 'mute';
-    DefaultBinds[KeyNameToCode(_S'c')]:= 'capture';
-    DefaultBinds[KeyNameToCode(_S'r')]:= 'record';
-    DefaultBinds[KeyNameToCode(_S'h')]:= 'findhh';
-    DefaultBinds[KeyNameToCode(_S'p')]:= 'pause';
-    DefaultBinds[KeyNameToCode(_S's')]:= '+speedup';
-    DefaultBinds[KeyNameToCode(_S't')]:= 'chat';
-    DefaultBinds[KeyNameToCode(_S'y')]:= 'confirm';
+    RegisterBind(DefaultBinds, _S'0', '+volup');
+    RegisterBind(DefaultBinds, _S'9', '+voldown');
+    RegisterBind(DefaultBinds, _S'8', 'mute');
+    RegisterBind(DefaultBinds, _S'c', 'capture');
+    RegisterBind(DefaultBinds, _S'r', 'record');
+    RegisterBind(DefaultBinds, _S'h', 'findhh');
+    RegisterBind(DefaultBinds, _S'p', 'pause');
+    RegisterBind(DefaultBinds, _S's', '+speedup');
+    RegisterBind(DefaultBinds, _S't', 'chat');
+    RegisterBind(DefaultBinds, _S'y', 'confirm');
 
-    DefaultBinds[KeyNameToCode('mousem')]:= 'zoomreset';
-    DefaultBinds[KeyNameToCode('wheelup')]:= 'zoomin';
-    DefaultBinds[KeyNameToCode('wheeldown')]:= 'zoomout';
+    RegisterBind(DefaultBinds, 'mousem', 'zoomreset');
+    RegisterBind(DefaultBinds, 'wheelup', 'zoomin');
+    RegisterBind(DefaultBinds, 'wheeldown', 'zoomout');
 
-    DefaultBinds[KeyNameToCode('f12')]:= 'fullscr';
+    RegisterBind(DefaultBinds, 'f12', 'fullscr');
 
 
-    DefaultBinds[KeyNameToCode('mousel')]:= '/put';
-    DefaultBinds[KeyNameToCode('mouser')]:= 'ammomenu';
-    DefaultBinds[KeyNameToCode('backspace')]:= 'hjump';
-    DefaultBinds[KeyNameToCode('tab')]:= 'switch';
-    DefaultBinds[KeyNameToCode('return')]:= 'ljump';
-    DefaultBinds[KeyNameToCode('space')]:= '+attack';
-    DefaultBinds[KeyNameToCode('up')]:= '+up';
-    DefaultBinds[KeyNameToCode('down')]:= '+down';
-    DefaultBinds[KeyNameToCode('left')]:= '+left';
-    DefaultBinds[KeyNameToCode('right')]:= '+right';
-    DefaultBinds[KeyNameToCode('left_shift')]:= '+precise';
+    RegisterBind(DefaultBinds, 'mousel', '/put');
+    RegisterBind(DefaultBinds, 'mouser', 'ammomenu');
+    RegisterBind(DefaultBinds, 'backspace', 'hjump');
+    RegisterBind(DefaultBinds, 'tab', 'switch');
+    RegisterBind(DefaultBinds, 'return', 'ljump');
+    RegisterBind(DefaultBinds, 'space', '+attack');
+    RegisterBind(DefaultBinds, 'up', '+up');
+    RegisterBind(DefaultBinds, 'down', '+down');
+    RegisterBind(DefaultBinds, 'left', '+left');
+    RegisterBind(DefaultBinds, 'right', '+right');
+    RegisterBind(DefaultBinds, 'left_shift', '+precise');
 
 
-    DefaultBinds[KeyNameToCode('j0a0u')]:= '+left';
-    DefaultBinds[KeyNameToCode('j0a0d')]:= '+right';
-    DefaultBinds[KeyNameToCode('j0a1u')]:= '+up';
-    DefaultBinds[KeyNameToCode('j0a1d')]:= '+down';
-    for i:= 1 to 10 do DefaultBinds[KeyNameToCode('f'+IntToStr(i))]:= 'slot '+char(48+i);
-    for i:= 1 to 5  do DefaultBinds[KeyNameToCode(IntToStr(i))]:= 'timer '+IntToStr(i);
+    RegisterBind(DefaultBinds, 'j0a0u', '+left');
+    RegisterBind(DefaultBinds, 'j0a0d', '+right');
+    RegisterBind(DefaultBinds, 'j0a1u', '+up');
+    RegisterBind(DefaultBinds, 'j0a1d', '+down');
+    for i:= 1 to 10 do RegisterBind(DefaultBinds, 'f'+IntToStr(i), 'slot '+char(48+i));
+    for i:= 1 to 5  do RegisterBind(DefaultBinds, IntToStr(i), 'timer '+IntToStr(i));
 
     loadBinds('dbind', cPathz[ptConfig] + '/settings.ini');
 end;
@@ -389,7 +465,7 @@
     t: LongInt;
 begin
     for t:= 0 to cKbdMaxIndex do
-        if (CurrentBinds[t] <> binds[t]) and tkbd[t] then
+        if (CurrentBinds.binds[CurrentBinds.indices[t]] <> binds.binds[binds.indices[t]]) and tkbd[t] then
             ProcessKey(t, False);
 
     CurrentBinds:= binds;
@@ -579,38 +655,59 @@
 
 procedure addBind(var binds: TBinds; var id: shortstring);
 var KeyName, Modifier, tmp: shortstring;
-    i, b: LongInt;
+    i, newCode, code, b: LongInt;
 begin
-KeyName:= '';
-Modifier:= '';
+    KeyName:= '';
+    Modifier:= '';
 
-if(Pos('mod:', id) <> 0)then
-    begin
-    tmp:= '';
-    SplitBySpace(id, tmp);
-    Modifier:= id;
-    id:= tmp;
-    end;
+    if(Pos('mod:', id) <> 0)then
+        begin
+        tmp:= '';
+        SplitBySpace(id, tmp);
+        Modifier:= id;
+        id:= tmp;
+        end;
 
-SplitBySpace(id, KeyName);
-if KeyName[1]='"' then
-    Delete(KeyName, 1, 1);
-if KeyName[byte(KeyName[0])]='"' then
-    Delete(KeyName, byte(KeyName[0]), 1);
-b:= KeyNameToCode(id, Modifier);
-if b = 0 then
-    OutError(errmsgUnknownVariable + ' "' + id + '"', false)
-else
+    SplitBySpace(id, KeyName);
+    if KeyName[1]='"' then
+        Delete(KeyName, 1, 1);
+    if KeyName[byte(KeyName[0])]='"' then
+        Delete(KeyName, byte(KeyName[0]), 1);
+    b:= KeyNameToCode(id, Modifier);
+    if b = 0 then
+        OutError(errmsgUnknownVariable + ' "' + id + '"', false)
+    else
     begin
-    // add bind: first check if this cmd is already bound, and remove old bind
-    i:= cKbdMaxIndex;
-    repeat
-        dec(i)
-    until (i < 0) or (binds[i] = KeyName);
-    if (i >= 0) then
-        binds[i]:= '';
+        // add bind: first check if this cmd is already bound, and remove old bind
+        i:= Low(binds.binds);
+        while (i <= High(binds.binds)) and (binds.binds[i] <> KeyName) do
+            inc(i);
+
+        if (i <= High(binds.binds)) then
+        begin
+            code:= Low(binds.indices);
+            while (code <= High(binds.indices)) and (binds.indices[code] <> i) do
+                inc(code);
+
+            checkFails(code <= High(binds.indices), 'binds registry inconsistency', true);
 
-    binds[b]:= KeyName;
+            binds.indices[code]:= 0;
+            binds.binds[i]:= ''
+        end;
+
+        if binds.indices[b] > 0 then
+            newCode:= binds.indices[b]
+        else if i >= High(binds.binds) then
+            begin
+                inc(binds.lastIndex);
+                checkFails(binds.lastIndex < High(binds.binds), 'too many binds', true);
+                newCode:= binds.lastIndex
+            end else
+                newCode:= i;
+
+
+    binds.indices[b]:= newCode;
+    binds.binds[binds.indices[b]]:= KeyName
     end
 end;