diff -r 6e8b807bda4b -r ba39a1d396c0 hedgewars/uInputHandler.pas --- 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;