mirror of
https://github.com/cheat-engine/cheat-engine
synced 2026-08-15 02:26:08 -04:00
825 lines
24 KiB
ObjectPascal
Executable file
825 lines
24 KiB
ObjectPascal
Executable file
unit HotkeyHandler;
|
|
|
|
{$MODE Delphi}
|
|
|
|
interface
|
|
|
|
uses
|
|
{$ifdef darwin}
|
|
macport, machotkeys,
|
|
{$endif}
|
|
{$ifdef windows}
|
|
windows,
|
|
{$endif}
|
|
|
|
LCLType,LCLIntf,classes,sysutils, SyncObjs,CEFuncProc,messages,genericHotkey, math,
|
|
commonTypeDefs;
|
|
|
|
type thotkeyitem=record
|
|
{$ifdef darwin}
|
|
machk: pointer; //needed for unregister
|
|
{$endif}
|
|
keys: TKeyCombo;
|
|
windowtonotify: thandle;
|
|
id: integer;
|
|
memrechotkey: pointer; //if set this gets passed down to the hotkey handler in the message
|
|
genericHotkey: TGenericHotkey;
|
|
|
|
//extra
|
|
fuModifiers: word;
|
|
uVirtKey:word;
|
|
handler2:boolean;
|
|
lastactivate: dword; //determines when the last time this hotkey was activated
|
|
delayBetweenActivate: integer; //If not 0 this is a userdefined delay for a hotkey, so you have have fast responding and slow responding hotkeys at the same time
|
|
end;
|
|
|
|
type
|
|
PHotkeyItem=^THotkeyItem;
|
|
|
|
THotkeyThreadState=(htsActive=0, htsMemrecOnly=1, htsNoMemrec=2, htsDisabled=3);
|
|
|
|
|
|
Thotkeythread=class(tthread)
|
|
private
|
|
memrechk: pointer;
|
|
generichk: TGenericHotkey;
|
|
fstate: THotkeyThreadState;
|
|
|
|
mainformhotkey2command: integer;
|
|
|
|
procedure memrechotkey;
|
|
procedure memrechotkeydisable;
|
|
procedure handleGenericHotkey;
|
|
procedure mainformhotkey2;
|
|
public
|
|
suspended: boolean;
|
|
hotkeylist: array of thotkeyitem;
|
|
procedure execute; override;
|
|
constructor create(suspended: boolean);
|
|
published
|
|
property state: THotkeyThreadState read fState write fState;
|
|
end;
|
|
|
|
|
|
function RegisterHotKey(hWnd: HWND; id: Integer; fsModifiers, vk: UINT): BOOL; stdcall;
|
|
function RegisterHotKey2(hWnd: HWND; id: Integer; keys: TKeyCombo; memrechotkey: pointer=nil; genericHotkey: TGenericHotkey=nil): boolean;
|
|
function UnregisterHotKey(hWnd: HWND; id: Integer): BOOL; stdcall;
|
|
function UnregisterAddressHotkey(memrechotkey: pointer): boolean;
|
|
function UnregisterGenericHotkey(generichotkey: TGenericHotkey): boolean;
|
|
function GetGenericHotkeyKeyItem(generichotkey: TGenericHotkey): PHotkeyItem;
|
|
|
|
//function OldUnregisterHotKey(hWnd: HWND; id: Integer): BOOL; stdcall;
|
|
function GetKeyComboLength(keycombo: TKeyCombo): integer;
|
|
function CheckKeyCombo(keycombo: tkeycombo; nocache: boolean=false):boolean;
|
|
procedure ConvertOldHotkeyToKeyCombo(fsModifiers, vk: uint; var k: tkeycombo);
|
|
procedure ClearHotkeylist;
|
|
procedure SuspendHotkeyHandler;
|
|
procedure ResumeHotkeyHandler;
|
|
procedure hotkeyTargetWindowHandleChanged(oldhandle, newhandle: thandle);
|
|
|
|
function IsKeyPressed(key: integer; nocache: boolean=false):boolean;
|
|
|
|
|
|
procedure InitializeHotkeyHandler;
|
|
|
|
var hotkeythread: THotkeythread;
|
|
CSKeys: TCriticalSection;
|
|
|
|
|
|
hotkeyPollInterval: integer=100;
|
|
hotkeyIdletime: integer=100;
|
|
|
|
const WM_HOTKEY2=$8000;
|
|
|
|
|
|
implementation
|
|
|
|
uses MemoryRecordUnit, {$ifdef windows}xinput,winsapi,{$endif} MainUnit;
|
|
|
|
type tkeystate=(ks_undefined=0, ks_pressed=1, ks_notpressed=2);
|
|
|
|
var
|
|
keystate: array [0..255] of tkeystate; //0=undefined, 1=pressed, 2-not pressed
|
|
ksCS: TCriticalSection;
|
|
{$ifdef windows}
|
|
ControllerState: XINPUT_STATE;
|
|
{$endif}
|
|
|
|
|
|
|
|
function IsKeyPressed(key: integer; nocache: boolean=false):boolean;
|
|
var
|
|
ks: dword;
|
|
sks: short;
|
|
begin
|
|
result:=false;
|
|
if key>255 then //not inside the list... (doubt it's valid)
|
|
begin
|
|
//anyhow, check if it is currently pressed
|
|
|
|
{$ifdef windows}
|
|
if (key>=VK_PAD_A) and (key<=VK_PAD_RTHUMB_DOWNLEFT) then
|
|
begin
|
|
//controller key
|
|
if InitXinput then
|
|
begin
|
|
if ControllerState.dwPacketNumber=0 then //needs an update
|
|
if XInputGetState(0, ControllerState)<>0 then exit;
|
|
|
|
case key of
|
|
VK_PAD_A: exit((ControllerState.Gamepad.wButtons and XINPUT_GAMEPAD_A)>0);
|
|
VK_PAD_B: exit((ControllerState.Gamepad.wButtons and XINPUT_GAMEPAD_B)>0);
|
|
VK_PAD_X: exit((ControllerState.Gamepad.wButtons and XINPUT_GAMEPAD_X)>0);
|
|
VK_PAD_Y: exit((ControllerState.Gamepad.wButtons and XINPUT_GAMEPAD_Y)>0);
|
|
|
|
VK_PAD_RSHOULDER: exit((ControllerState.Gamepad.wButtons and XINPUT_GAMEPAD_RIGHT_SHOULDER)>0);
|
|
VK_PAD_LSHOULDER: exit((ControllerState.Gamepad.wButtons and XINPUT_GAMEPAD_LEFT_SHOULDER)>0);
|
|
|
|
VK_PAD_LTRIGGER: exit(ControllerState.Gamepad.bLeftTrigger>40);
|
|
VK_PAD_RTRIGGER: exit(ControllerState.Gamepad.bRightTrigger>40);
|
|
|
|
VK_PAD_DPAD_UP: exit((ControllerState.Gamepad.wButtons and XINPUT_GAMEPAD_DPAD_UP)>0);
|
|
VK_PAD_DPAD_DOWN: exit((ControllerState.Gamepad.wButtons and XINPUT_GAMEPAD_DPAD_DOWN)>0);
|
|
VK_PAD_DPAD_LEFT: exit((ControllerState.Gamepad.wButtons and XINPUT_GAMEPAD_DPAD_LEFT)>0);
|
|
VK_PAD_DPAD_RIGHT: exit((ControllerState.Gamepad.wButtons and XINPUT_GAMEPAD_DPAD_RIGHT)>0);
|
|
|
|
VK_PAD_START: exit((ControllerState.Gamepad.wButtons and XINPUT_GAMEPAD_START)>0);
|
|
VK_PAD_BACK: exit((ControllerState.Gamepad.wButtons and XINPUT_GAMEPAD_BACK)>0);
|
|
|
|
VK_PAD_LTHUMB_PRESS: exit((ControllerState.Gamepad.wButtons and XINPUT_GAMEPAD_LEFT_THUMB)>0);
|
|
VK_PAD_RTHUMB_PRESS: exit((ControllerState.Gamepad.wButtons and XINPUT_GAMEPAD_RIGHT_THUMB)>0);
|
|
|
|
VK_PAD_LTHUMB_UP: exit(ControllerState.Gamepad.sThumbLY>20000);
|
|
VK_PAD_LTHUMB_DOWN: exit(ControllerState.Gamepad.sThumbLY<-20000);
|
|
VK_PAD_LTHUMB_RIGHT: exit(ControllerState.Gamepad.sThumbLX>20000);
|
|
VK_PAD_LTHUMB_LEFT: exit(ControllerState.Gamepad.sThumbLX<-20000);
|
|
|
|
VK_PAD_LTHUMB_UPLEFT: exit((ControllerState.Gamepad.sThumbLY>20000) and (ControllerState.Gamepad.sThumbLX<-20000));
|
|
VK_PAD_LTHUMB_UPRIGHT: exit((ControllerState.Gamepad.sThumbLY>20000) and (ControllerState.Gamepad.sThumbLX>20000));
|
|
VK_PAD_LTHUMB_DOWNRIGHT: exit((ControllerState.Gamepad.sThumbLY<-20000) and (ControllerState.Gamepad.sThumbLX>20000));
|
|
VK_PAD_LTHUMB_DOWNLEFT: exit((ControllerState.Gamepad.sThumbLY<-20000) and (ControllerState.Gamepad.sThumbLX<-20000));
|
|
|
|
VK_PAD_RTHUMB_UP: exit(ControllerState.Gamepad.sThumbRY>20000);
|
|
VK_PAD_RTHUMB_DOWN: exit(ControllerState.Gamepad.sThumbRY<-20000);
|
|
VK_PAD_RTHUMB_RIGHT: exit(ControllerState.Gamepad.sThumbRX>20000);
|
|
VK_PAD_RTHUMB_LEFT: exit(ControllerState.Gamepad.sThumbRX<-20000);
|
|
|
|
VK_PAD_RTHUMB_UPLEFT: exit((ControllerState.Gamepad.sThumbRY>20000) and (ControllerState.Gamepad.sThumbRX<-20000));
|
|
VK_PAD_RTHUMB_UPRIGHT: exit((ControllerState.Gamepad.sThumbRY>20000) and (ControllerState.Gamepad.sThumbRX>20000));
|
|
VK_PAD_RTHUMB_DOWNRIGHT: exit((ControllerState.Gamepad.sThumbRY<-20000) and (ControllerState.Gamepad.sThumbRX>20000));
|
|
VK_PAD_RTHUMB_DOWNLEFT: exit((ControllerState.Gamepad.sThumbRY<-20000) and (ControllerState.Gamepad.sThumbRX<-20000));
|
|
|
|
end;
|
|
end;
|
|
end
|
|
else
|
|
{$endif}
|
|
result:=((word(getasynckeystate(key)) shr 15) and 1) = 1;
|
|
exit;
|
|
end;
|
|
|
|
//look up in the list
|
|
sks:=0;
|
|
ksCS.enter;
|
|
if keystate[key]=ks_undefined then
|
|
begin
|
|
|
|
sks:=getasynckeystate(longint(key));
|
|
|
|
|
|
|
|
if ((sks and 1)=1) then
|
|
begin
|
|
|
|
|
|
keystate[key]:=ks_pressed
|
|
end
|
|
else
|
|
begin
|
|
if ((sks shr 15) and 1)=1 then
|
|
begin
|
|
|
|
|
|
keystate[key]:=ks_pressed
|
|
end
|
|
else
|
|
begin
|
|
|
|
|
|
keystate[key]:=ks_notpressed; //not pressed at all
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
result:=keystate[key]=ks_pressed;
|
|
|
|
if nocache and (not result) then
|
|
begin
|
|
|
|
if sks<>0 then
|
|
result:=(sks and $8001)<>0;
|
|
|
|
if result then exit;
|
|
|
|
sks:=GetAsyncKeyState(longint(key));
|
|
result:=(sks and $8001)<>0;
|
|
|
|
if result=false then
|
|
begin
|
|
sks:=GetKeyState(key);
|
|
result:=(sks and $8001)<>0;
|
|
if not result then
|
|
begin
|
|
beep;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
ksCS.Leave;
|
|
end;
|
|
|
|
procedure clearkeystate;
|
|
var i: integer;
|
|
begin
|
|
ksCS.enter;
|
|
zeromemory(@keystate[0],256*sizeof(tkeystate));
|
|
for i:=0 to 255 do
|
|
getasynckeystate(i); //clears the last call flag
|
|
|
|
{$ifdef windows}
|
|
ControllerState.dwPacketNumber:=0;
|
|
{$endif}
|
|
ksCS.leave;
|
|
end;
|
|
|
|
procedure hotkeyTargetWindowHandleChanged(oldhandle, newhandle: thandle);
|
|
{
|
|
Called when the handle of a window is changed. This will update all associated hotkeys
|
|
}
|
|
var i: integer;
|
|
begin
|
|
cskeys.Enter;
|
|
try
|
|
for i:=0 to length(hotkeythread.hotkeylist)-1 do
|
|
if hotkeythread.hotkeylist[i].windowtonotify=oldhandle then
|
|
hotkeythread.hotkeylist[i].windowtonotify:=newhandle;
|
|
finally
|
|
cskeys.Leave;
|
|
end;
|
|
end;
|
|
|
|
var presuspendstate: THotkeyThreadState;
|
|
procedure SuspendHotkeyHandler;
|
|
begin
|
|
if hotkeythread<>nil then
|
|
begin
|
|
presuspendstate:=hotkeythread.state;
|
|
hotkeythread.state:=htsDisabled;
|
|
end;
|
|
end;
|
|
|
|
procedure ResumeHotkeyHandler;
|
|
begin
|
|
if hotkeythread<>nil then
|
|
hotkeythread.state:=presuspendstate;
|
|
end;
|
|
|
|
procedure ClearHotkeylist;
|
|
begin
|
|
CSKeys.Enter;
|
|
//it's now safe to clear the list
|
|
setlength(hotkeythread.hotkeylist,0);
|
|
|
|
CSKeys.Leave;
|
|
end;
|
|
|
|
//get all keystates and store them in a buffer
|
|
|
|
function GetKeyComboLength(keycombo: TKeyCombo): integer;
|
|
var i: integer;
|
|
begin
|
|
result:=0;
|
|
for i:=0 to length(keycombo)-1 do
|
|
if keycombo[i]<>0 then
|
|
inc(result);
|
|
end;
|
|
|
|
function CheckKeyCombo(keycombo: tkeycombo; nocache: boolean=false):boolean;
|
|
var i: integer;
|
|
begin
|
|
result:=false;
|
|
|
|
if keycombo[0]<>0 then
|
|
begin
|
|
result:=true;
|
|
|
|
for i:=0 to length(keycombo)-1 do
|
|
begin
|
|
if (keycombo[i]=0) then exit;
|
|
|
|
if not IsKeyPressed(keycombo[i], nocache) then
|
|
begin
|
|
//not pressed
|
|
result:=false;
|
|
exit;
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
end;
|
|
|
|
{$ifdef darwin}
|
|
procedure MacHotkeyCallBackOriginal(sender: TObject; param: pointer);
|
|
begin
|
|
if (hotkeythread<>nil) and (not (hotkeythread.state in [htsActive,htsNoMemrec])) then exit; //ignore
|
|
|
|
mainform.hotkey2(integer(param));
|
|
end;
|
|
{$endif}
|
|
|
|
{$ifdef darwin}
|
|
procedure MacHotkeyCallBackMemRecHotkey(sender: TObject; param: pointer);
|
|
begin
|
|
if (hotkeythread<>nil) and (not (hotkeythread.state in [htsActive,htsMemrecOnly])) then exit; //ignore
|
|
|
|
TMemoryRecordHotkey(param).DoHotkey;
|
|
end;
|
|
{$endif}
|
|
|
|
{$ifdef darwin}
|
|
procedure MacHotkeyCallBackGenericHotkey(sender: TObject; param: pointer);
|
|
begin
|
|
if (hotkeythread<>nil) and (not (hotkeythread.state in [htsActive,htsNoMemrec])) then exit; //ignore
|
|
|
|
if assigned(TGenericHotkey(param).onNotify) then
|
|
TGenericHotkey(param).onNotify(tobject(param));
|
|
end;
|
|
{$endif}
|
|
|
|
function RegisterHotKey(hWnd: HWND; id: Integer; fsModifiers, vk: UINT): BOOL; stdcall;
|
|
{$ifdef darwin}
|
|
var
|
|
keys: TKeyCombo;
|
|
hk: pointer;
|
|
{$endif}
|
|
begin
|
|
{$ifdef darwin}
|
|
outputdebugstring('RegisterHotKey Old. Modifiers='+inttohex(fsModifiers,2)+' vk='+inttohex(vk,2));
|
|
ConvertOldHotkeyToKeyCombo(fsModifiers, vk, keys);
|
|
hk:=RegisterHotkeyMac(keys,MacHotkeyCallBackOriginal,pointer(id));
|
|
if hk=nil then exit(false);
|
|
{$endif}
|
|
|
|
CSKeys.Enter;
|
|
try
|
|
setlength(hotkeythread.hotkeylist,length(hotkeythread.hotkeylist)+1);
|
|
hotkeythread.hotkeylist[length(hotkeythread.hotkeylist)-1].windowtonotify:=hWnd;
|
|
hotkeythread.hotkeylist[length(hotkeythread.hotkeylist)-1].id:=id;
|
|
hotkeythread.hotkeylist[length(hotkeythread.hotkeylist)-1].fuModifiers:=fsmodifiers;
|
|
hotkeythread.hotkeylist[length(hotkeythread.hotkeylist)-1].uVirtKey:=vk;
|
|
hotkeythread.hotkeylist[length(hotkeythread.hotkeylist)-1].handler2:=false;
|
|
hotkeythread.hotkeylist[length(hotkeythread.hotkeylist)-1].delayBetweenActivate:=0;
|
|
hotkeythread.hotkeylist[length(hotkeythread.hotkeylist)-1].memrechotkey:=nil;
|
|
hotkeythread.hotkeylist[length(hotkeythread.hotkeylist)-1].generichotkey:=nil;
|
|
|
|
ConvertOldHotkeyToKeyCombo(fsModifiers, vk, hotkeythread.hotkeylist[length(hotkeythread.hotkeylist)-1].keys);
|
|
|
|
{$ifdef darwin}
|
|
hotkeythread.hotkeylist[length(hotkeythread.hotkeylist)-1].machk:=hk;
|
|
{$endif}
|
|
|
|
result:=true;
|
|
finally
|
|
CSKeys.Leave;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
|
|
function RegisterHotKey2(hWnd: HWND; id: Integer; keys: TKeyCombo; memrechotkey: pointer=nil; genericHotkey: TGenericHotkey=nil): boolean;
|
|
{$ifdef darwin}
|
|
var
|
|
hk: pointer;
|
|
{$endif}
|
|
begin
|
|
|
|
|
|
{$ifdef darwin}
|
|
if keys[0]=0 then
|
|
outputdebugstring('RegisterHotKey2 called with empty hotkey');
|
|
|
|
outputdebugstring('RegisterHotKey2');
|
|
if (memrechotkey=nil) and (generichotkey=nil) then
|
|
begin
|
|
outputdebugstring('old hotkey');
|
|
hk:=RegisterHotkeyMac(keys,MacHotkeyCallBackOriginal,pointer(id))
|
|
end
|
|
else
|
|
begin
|
|
outputdebugstring('new hotkey');
|
|
if memrechotkey<>nil then
|
|
begin
|
|
outputdebugstring('memrechotkey');
|
|
hk:=RegisterHotkeyMac(keys,MacHotkeyCallBackMemRecHotkey,memrechotkey)
|
|
end
|
|
else
|
|
begin
|
|
outputdebugstring('generichotkey');
|
|
hk:=RegisterHotkeyMac(keys,MacHotkeyCallBackGenericHotkey,generichotkey);
|
|
end;
|
|
end;
|
|
{$endif}
|
|
|
|
|
|
CSKeys.Enter;
|
|
try
|
|
checkkeycombo(keys);
|
|
|
|
setlength(hotkeythread.hotkeylist,length(hotkeythread.hotkeylist)+1);
|
|
hotkeythread.hotkeylist[length(hotkeythread.hotkeylist)-1].windowtonotify:=hWnd;
|
|
hotkeythread.hotkeylist[length(hotkeythread.hotkeylist)-1].keys:=keys;
|
|
hotkeythread.hotkeylist[length(hotkeythread.hotkeylist)-1].id:=id;
|
|
hotkeythread.hotkeylist[length(hotkeythread.hotkeylist)-1].fuModifiers:=0;
|
|
hotkeythread.hotkeylist[length(hotkeythread.hotkeylist)-1].uVirtKey:=0;
|
|
hotkeythread.hotkeylist[length(hotkeythread.hotkeylist)-1].handler2:=true;
|
|
hotkeythread.hotkeylist[length(hotkeythread.hotkeylist)-1].delayBetweenActivate:=0;
|
|
hotkeythread.hotkeylist[length(hotkeythread.hotkeylist)-1].memrechotkey:=memrechotkey;
|
|
hotkeythread.hotkeylist[length(hotkeythread.hotkeylist)-1].generichotkey:=genericHotkey;
|
|
{$ifdef darwin}
|
|
hotkeythread.hotkeylist[length(hotkeythread.hotkeylist)-1].machk:=hk;
|
|
{$endif}
|
|
|
|
result:=true;
|
|
finally
|
|
CSKeys.Leave;
|
|
end;
|
|
end;
|
|
|
|
procedure ConvertOldHotkeyToKeyCombo(fsModifiers, vk: uint; var k: tkeycombo);
|
|
{
|
|
Function will take the fsmodifier and vk of the normal registerhotkey call and
|
|
fill it in into a TKeyCombo type array used by ce's hotkey handler
|
|
}
|
|
var i: integer;
|
|
begin
|
|
k[0]:=vk;
|
|
i:=1;
|
|
|
|
if (fsModifiers and MOD_CONTROL)>0 then
|
|
begin
|
|
k[i]:=vk_control;
|
|
inc(i);
|
|
end;
|
|
|
|
if (fsModifiers and MOD_ALT)>0 then
|
|
begin
|
|
k[i]:=vk_menu;
|
|
inc(i);
|
|
end;
|
|
|
|
if (fsModifiers and MOD_SHIFT)>0 then
|
|
begin
|
|
k[i]:=vk_shift;
|
|
inc(i);
|
|
end;
|
|
|
|
k[i]:=0;
|
|
end;
|
|
|
|
function getGenericHotkeyKeyItem(generichotkey: TGenericHotkey): PHotkeyItem;
|
|
//pre: ONLY call this when the CSKeys critical section has been acquired
|
|
var i: integer;
|
|
begin
|
|
result:=nil;
|
|
for i:=0 to length(hotkeythread.hotkeylist)-1 do
|
|
begin
|
|
if hotkeythread.hotkeylist[i].genericHotkey=genericHotkey then
|
|
begin
|
|
//found, so delete it
|
|
result:=@hotkeythread.hotkeylist[i];
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function UnregisterGenericHotkey(generichotkey: TGenericHotkey): boolean;
|
|
var i,j: integer;
|
|
begin
|
|
result:=false;
|
|
|
|
CSKeys.enter;
|
|
try
|
|
for i:=0 to length(hotkeythread.hotkeylist)-1 do
|
|
begin
|
|
if hotkeythread.hotkeylist[i].genericHotkey=genericHotkey then
|
|
begin
|
|
//found, so delete it
|
|
{$ifdef darwin}
|
|
UnregisterHotkeyMac(hotkeythread.hotkeylist[i].machk);
|
|
{$endif}
|
|
|
|
for j:=i to length(hotkeythread.hotkeylist)-2 do
|
|
hotkeythread.hotkeylist[j]:=hotkeythread.hotkeylist[j+1];
|
|
|
|
|
|
setlength(hotkeythread.hotkeylist,length(hotkeythread.hotkeylist)-1);
|
|
result:=true;
|
|
exit;
|
|
end;
|
|
end;
|
|
finally
|
|
CSKeys.Leave;
|
|
end;
|
|
end;
|
|
|
|
function UnregisterAddressHotkey(memrechotkey: pointer): boolean;
|
|
var i,j: integer;
|
|
begin
|
|
result:=false;
|
|
|
|
CSKeys.enter;
|
|
try
|
|
for i:=0 to length(hotkeythread.hotkeylist)-1 do
|
|
begin
|
|
if hotkeythread.hotkeylist[i].memrechotkey=memrechotkey then
|
|
begin
|
|
//found, so delete it
|
|
{$ifdef darwin}
|
|
UnregisterHotkeyMac(hotkeythread.hotkeylist[i].machk);
|
|
{$endif}
|
|
|
|
for j:=i to length(hotkeythread.hotkeylist)-2 do
|
|
hotkeythread.hotkeylist[j]:=hotkeythread.hotkeylist[j+1];
|
|
|
|
|
|
setlength(hotkeythread.hotkeylist,length(hotkeythread.hotkeylist)-1);
|
|
result:=true;
|
|
exit;
|
|
end;
|
|
end;
|
|
finally
|
|
CSKeys.Leave;
|
|
end;
|
|
end;
|
|
|
|
function UnregisterHotKey(hWnd: HWND; id: Integer): BOOL; stdcall;
|
|
var i,j: integer;
|
|
begin
|
|
result:=false;
|
|
|
|
CSKeys.Enter;
|
|
try
|
|
for i:=0 to length(hotkeythread.hotkeylist)-1 do
|
|
begin
|
|
if (hotkeythread.hotkeylist[i].windowtonotify=hwnd) and
|
|
(hotkeythread.hotkeylist[i].id=id) then
|
|
begin
|
|
{$ifdef darwin}
|
|
UnregisterHotkeyMac(hotkeythread.hotkeylist[i].machk);
|
|
{$endif}
|
|
|
|
for j:=i to length(hotkeythread.hotkeylist)-2 do
|
|
hotkeythread.hotkeylist[j]:=hotkeythread.hotkeylist[j+1];
|
|
|
|
setlength(hotkeythread.hotkeylist,length(hotkeythread.hotkeylist)-1);
|
|
result:=true;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
//still here so not found, try windows
|
|
{$ifdef windows}
|
|
result:=windows.UnregisterHotKey(hWnd,id)
|
|
{$endif}
|
|
|
|
|
|
|
|
finally
|
|
CSKeys.Leave;
|
|
end;
|
|
end;
|
|
|
|
constructor THotkeyThread.create(suspended: boolean);
|
|
begin
|
|
//init
|
|
inherited create(suspended);
|
|
end;
|
|
|
|
procedure Thotkeythread.memrechotkey;
|
|
begin
|
|
// sendmessage(mainform.handle,integer(cefuncproc.WM_HOTKEY2),0,ptrUint(memrechk));
|
|
|
|
//not 100% sure why sendmessage works here but not from within the thread...
|
|
//but since we're here anyhow:
|
|
TMemoryRecordHotkey(memrechk).DoHotkey;
|
|
end;
|
|
|
|
procedure Thotkeythread.memrechotkeydisable;
|
|
begin
|
|
// sendmessage(mainform.handle,integer(cefuncproc.WM_HOTKEY2),0,ptrUint(memrechk));
|
|
|
|
//not 100% sure why sendmessage works here but not from within the thread...
|
|
//but since we're here anyhow:
|
|
TMemoryRecordHotkey(memrechk).DoHotkeyDisable;
|
|
end;
|
|
|
|
procedure Thotkeythread.handleGenericHotkey;
|
|
begin
|
|
if assigned(generichk.onNotify) then
|
|
generichk.onNotify(generichk);
|
|
end;
|
|
|
|
procedure THotkeythread.mainformhotkey2;
|
|
begin
|
|
mainform.hotkey2(mainformhotkey2command);
|
|
end;
|
|
|
|
procedure THotkeyThread.execute;
|
|
type
|
|
TActiveHotkeyData=record //structure to hold hotkey and keycount
|
|
hotkeylistItem: PHotkeyItem;
|
|
keycount: integer;
|
|
end;
|
|
PActiveHotkeyData=^TActiveHotkeyData;
|
|
var i: integer;
|
|
a,b,c: dword;
|
|
|
|
activeHotkeyList: Tlist;
|
|
maxActiveKeyCount: integer;
|
|
|
|
tempHotkey: PActiveHotkeyData;
|
|
|
|
handeit: boolean;
|
|
begin
|
|
activeHotkeyList:=Tlist.create;
|
|
while not terminated do
|
|
begin
|
|
{$ifdef darwin}
|
|
//using mac hotkeys instead
|
|
if loadMacHotkeyFunctions then
|
|
begin
|
|
sleep(1000);
|
|
continue;
|
|
end;
|
|
|
|
//else use the getasynckeystate method which can only get function keys and modifiers
|
|
{$endif}
|
|
try
|
|
CSKeys.Enter;
|
|
try
|
|
//get a list of all the activated hotkeys
|
|
|
|
maxActiveKeyCount:=0;
|
|
for i:=0 to length(hotkeylist)-1 do
|
|
begin
|
|
if
|
|
((state in [htsActive,htsMemrecOnly]) and (hotkeylist[i].memrechotkey<>nil) and TMemoryrecordHotkey(hotkeylist[i].memrechotkey).Active and (checkkeycombo(TMemoryrecordHotkey(hotkeylist[i].memrechotkey).keys))) or
|
|
((state in [htsActive,htsNoMemrec]) and (hotkeylist[i].genericHotkey<>nil) and (checkkeycombo(TGenericHotkey(hotkeylist[i].generichotkey).keys))) or
|
|
((state in [htsActive,htsNoMemrec]) and ((hotkeylist[i].memrechotkey=nil) and (hotkeylist[i].generichotkey=nil)) and checkkeycombo(hotkeylist[i].keys))
|
|
|
|
then
|
|
begin
|
|
//OutputDebugString('Hotkey triggered');
|
|
|
|
//the hotkey got pressed
|
|
//6.3: Add it to a list of hotkeys
|
|
tempHotkey:=getmem(sizeof(tempHotkey));
|
|
|
|
tempHotkey.hotkeylistItem:=@hotkeylist[i];
|
|
if (hotkeylist[i].memrechotkey<>nil) then
|
|
tempHotkey.keycount:=GetKeyComboLength(TMemoryrecordHotkey(hotkeylist[i].memrechotkey).keys)
|
|
else
|
|
if (hotkeylist[i].genericHotkey<>nil) then
|
|
tempHotkey.keycount:=GetKeyComboLength(TGenericHotkey(hotkeylist[i].generichotkey).keys)
|
|
else
|
|
tempHotkey.keycount:=GetKeyComboLength(hotkeylist[i].keys);
|
|
|
|
maxActiveKeyCount:=max(maxActiveKeyCount, tempHotkey.keycount);
|
|
|
|
|
|
activeHotkeyList.Add(temphotkey);
|
|
|
|
end
|
|
else
|
|
begin
|
|
hotkeylist[i].lastactivate:=0; //not currently down. The user released the key so repeat can be skipped
|
|
if (hotkeylist[i].memrechotkey<>nil) and TMemoryrecordHotkey(hotkeylist[i].memrechotkey).OnlyWhileDown and TMemoryrecordHotkey(hotkeylist[i].memrechotkey).down then
|
|
begin
|
|
memrechk:=hotkeylist[i].memrechotkey;
|
|
synchronize(memrechotkeydisable);
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
|
|
//now go through the activeHotkeyList looking for a keycount of maxActiveKeyCount
|
|
i:=0;
|
|
while i<activeHotkeyList.count do
|
|
begin
|
|
//OutputDebugString('Handling hotkey');
|
|
temphotkey:=PActiveHotkeyData(activeHotkeyList[i]);
|
|
if temphotkey.keycount=maxActiveKeyCount then //it belongs to the max complex hotkey count
|
|
begin
|
|
//OutputDebugString('1');
|
|
if ((tempHotkey.hotkeylistItem.lastactivate+ifthen(tempHotkey.hotkeylistItem.delaybetweenActivate>0, tempHotkey.hotkeylistItem.delaybetweenActivate, hotkeyIdletime))<GetTickCount) then //check if it can be activated
|
|
begin
|
|
a:=tempHotkey.hotkeylistItem.windowtonotify;
|
|
b:=tempHotkey.hotkeylistItem.id;
|
|
c:=(tempHotkey.hotkeylistItem.uVirtKey shl 16)+tempHotkey.hotkeylistItem.fuModifiers;
|
|
// OutputDebugString('2');
|
|
|
|
tempHotkey.hotkeylistItem.lastactivate:=gettickcount;
|
|
if tempHotkey.hotkeylistItem.handler2 then
|
|
begin
|
|
//OutputDebugString('3');
|
|
if tempHotkey.hotkeylistItem.memrechotkey<>nil then
|
|
begin
|
|
//OutputDebugString('4');
|
|
memrechk:=tempHotkey.hotkeylistItem.memrechotkey;
|
|
|
|
CSKeys.leave;
|
|
//OutputDebugString('5');
|
|
Synchronize(memrechotkey);
|
|
//OutputDebugString('6');
|
|
cskeys.enter;
|
|
//OutputDebugString('7');
|
|
end
|
|
else
|
|
begin
|
|
//OutputDebugString('8');
|
|
if tempHotkey.hotkeylistItem.generichotkey<>nil then
|
|
begin
|
|
//OutputDebugString('9');
|
|
generichk:=tempHotkey.hotkeylistItem.genericHotkey;
|
|
synchronize(handlegenerichotkey);
|
|
|
|
//sendmessage(a,integer(WM_HOTKEY2),1,ptrUint(tempHotkey.hotkeylistItem.genericHotkey))
|
|
end
|
|
else
|
|
begin
|
|
|
|
// OutputDebugString('10');
|
|
|
|
mainformhotkey2command:=b;
|
|
synchronize(mainformhotkey2);
|
|
|
|
//sendmessage(a,integer(WM_HOTKEY2),b,0)
|
|
end
|
|
end;
|
|
OutputDebugString('11');
|
|
|
|
end
|
|
{$ifdef windows}
|
|
else
|
|
sendmessage(a,WM_HOTKEY,b,c)
|
|
{$endif};
|
|
|
|
OutputDebugString('12');
|
|
end;
|
|
end;
|
|
|
|
//cleanup the memory as well while we're at it
|
|
freememandnil(temphotkey);
|
|
inc(i);
|
|
end;
|
|
activeHotkeyList.clear;
|
|
|
|
finally
|
|
CSKeys.Leave;
|
|
end;
|
|
|
|
except
|
|
end;
|
|
|
|
clearkeystate; //keys have been handled
|
|
|
|
sleep(hotkeyPollInterval);
|
|
end;
|
|
end;
|
|
|
|
procedure InitializeHotkeyHandler;
|
|
begin
|
|
if hotkeythread=nil then
|
|
hotkeythread:=Thotkeythread.Create(false);
|
|
end;
|
|
|
|
initialization
|
|
ksCS:=TCriticalSection.Create;
|
|
clearkeystate;
|
|
|
|
CSKeys:=TCriticalSection.Create;
|
|
|
|
InitializeHotkeyHandler;
|
|
|
|
|
|
finalization
|
|
if hotkeythread<>nil then
|
|
hotkeythread.Terminate;
|
|
|
|
CSKeys.Free;
|
|
end.
|
|
|