Swap keyboard key functions

Just starting out? Need help? Post your questions and find answers here.
User avatar
Michael Vogel
Addict
Addict
Posts: 2797
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Swap keyboard key functions

Post by Michael Vogel »

I need to work with two different notebooks which have different keyboard layouts which makes me crazy :evil:

I thought to do the trick by swaping key functions but this leads to an endless loop...
My code below just changes the END and INSERT key for the moment, much more has to be done later on. Pressing the END key does to the trick with the code below, but... when removing the semicolon from the line ;Debug RegisterHotKey_(Win,#HotkeyLineEnd,#MOD_NULL,#VK_INSERT) the drama starts...

One idea would be to unregister the hotkeys while sending the right keys and then registering them again, but maybe there's a more elegant solution?

Code: Select all

; Define

	EnableExplicit

	#Delet=	Chr(5)
	#Insert=	Chr(6)
	#Back=		Chr(8)
	#Tabstop=	Chr(9)
	#Enter=	Chr(13)
	#Shift=		Chr(16)
	#Control=	Chr(17)
	#Alt=		Chr(18)
	#Win=		Chr(19)
	#ShiftUp=	Chr(20)
	#ControlUp=Chr(21)
	#AltUp=	Chr(22)
	#WinUp=	Chr(23)
	#Home=	Chr(24)
	#End=		Chr(25)
	#Escape=	Chr(27)

	#Shift_=	16
	#WinUp_=	23
	#Delet_=	5
	#Insert_=	6
	#Home_=	24
	#End_=	25


	Enumeration
		#HotkeyInsert;								Insert
		#HotkeyPaste;				Shift+			Insert
		#HotkeyXXX;				Shift+Control+	Insert
		#HotkeyCopy;				Control+		Insert
		#HotkeyLineEnd;							End
		#HotkeySelectToLineEnd;	Shift+			End
		#HotkeySelectToDocumentEnd;	Shift+Control+	End
		#HotkeyDocumentEnd;	Control+		End
		#HotkeyCopyScreen;						PrintScr
		#HotkeyCopyWindow;		Alt+			PrintScr
		#HotkeyCopyArea;			Control+		PrintScr
		#HotkeyDefineArea;			Control+Shift+	PrintScr
	EndEnumeration
	
	#MOD_NULL=0
	
; EndDefine

Procedure SendKeys(keys.s)

	Protected.i n,vk,up
	Protected.i keyshift,layout
	Protected.s s,t

	BlockInput_(#True)

	keybd_event_(#VK_MENU,0,#KEYEVENTF_KEYUP,0)
	keybd_event_(#VK_CONTROL,0,#KEYEVENTF_KEYUP,0)
	keybd_event_(#VK_SHIFT,0,#KEYEVENTF_KEYUP,0)
	keybd_event_(#VK_LWIN,0,#KEYEVENTF_KEYUP,0)

	GetKeyboardLayout_(GetWindowThreadProcessId_(GetForegroundWindow_(),0))

	For n=1 To Len(keys)
		vk=Asc(Mid(keys,n,1))
		up=#Null

		Select vk

		Case #Shift_ To #WinUp_
			If vk>19 : up=#KEYEVENTF_KEYUP : vk-4 : EndIf
			If vk=19 : vk=91 : EndIf
			keybd_event_(vk,0,up,0)

		Case #Delet_
			vk=#VK_DELETE
			keybd_event_(vk,0,0,0) : keybd_event_(vk,0,#KEYEVENTF_KEYUP,0)
		Case #Insert_
			vk=#VK_INSERT
			keybd_event_(vk,0,0,0) : keybd_event_(vk,0,#KEYEVENTF_KEYUP,0)
		Case #Home_
			vk=#VK_HOME
			keybd_event_(vk,0,0,0) : keybd_event_(vk,0,#KEYEVENTF_KEYUP,0)
		Case #End_
			vk=#VK_END
			keybd_event_(vk,0,0,0) : keybd_event_(vk,0,#KEYEVENTF_KEYUP,0)

		Default
			vk=VkKeyScanEx_(vk,layout)
			up=vk >> 8
			vk & $ff
			If up&1
				keybd_event_(#VK_SHIFT,0,0,0)
			EndIf
			If up&2
				keybd_event_(#VK_CONTROL,0,0,0)
			EndIf
			If up&4
				keybd_event_(#VK_MENU,0,0,0)
			EndIf

			keybd_event_(vk,0,0,0) : keybd_event_(vk,0,#KEYEVENTF_KEYUP,0)

			If up&1
				keybd_event_(#VK_SHIFT,0,#KEYEVENTF_KEYUP,0)
			EndIf
			If up&2
				keybd_event_(#VK_CONTROL,0,#KEYEVENTF_KEYUP,0)
			EndIf
			If up&4
				keybd_event_(#VK_MENU,0,#KEYEVENTF_KEYUP,0)
			EndIf

		EndSelect
	Next n

	keybd_event_(#VK_MENU,0,#KEYEVENTF_KEYUP,0)
	keybd_event_(#VK_CONTROL,0,#KEYEVENTF_KEYUP,0)
	keybd_event_(#VK_SHIFT,0,#KEYEVENTF_KEYUP,0)
	keybd_event_(#VK_LWIN,0,#KEYEVENTF_KEYUP,0)

	BlockInput_(#False)

	ProcedureReturn #True

EndProcedure


#Window=1

Global win

Win=OpenWindow(#Window,300,250,400,200,"Swap End / Insert",#PB_Window_SystemMenu)

Debug RegisterHotKey_(Win,1001,#MOD_NULL,		#VK_SNAPSHOT)
Debug RegisterHotKey_(Win,1001,#MOD_SHIFT,	#VK_SNAPSHOT)
Debug RegisterHotKey_(Win,1002,#MOD_ALT,		#VK_SNAPSHOT)
Debug RegisterHotKey_(Win,1003,#MOD_CONTROL,#VK_SNAPSHOT)
Debug RegisterHotKey_(Win,1004,#MOD_SHIFT, 	#VK_SNAPSHOT)

;Debug RegisterHotKey_(Win,#HotkeyLineEnd,	#MOD_NULL,  	#VK_INSERT)

Debug RegisterHotKey_(Win,2002,				#MOD_SHIFT,	#VK_INSERT)
Debug RegisterHotKey_(Win,#HotkeyInsert		,#MOD_NULL,	#VK_END)

Repeat
	Select WaitWindowEvent()
	Case #PB_Event_CloseWindow
		Break
	Case #WM_HOTKEY
		Select EventwParam()
		Case #HotkeyLineEnd
			Debug "END"
			SendKeys(#End)

		Case #HotkeyInsert
			Debug "INS"
			SendKeys(#Insert)

		EndSelect

		Debug EventwParam()
	EndSelect

ForEver
SMaag
Enthusiast
Enthusiast
Posts: 302
Joined: Sat Jan 14, 2023 6:55 pm
Location: Bavaria/Germany

Re: Swap keyboard key functions

Post by SMaag »

with a keyboard hook you can exchange keys.

Code: Select all

; PseudoCode

; Keybord Hook Structure is predefined in PB

; Structure KBDLLHOOKSTRUCT   
;   vkCode.l
;   scanCode.l
;   flags.l
;   time.l
;   dwExtraInfo.l
; EndStructure

Procedure.l myKeyboardHook(nCode, wParam, *p.KBDLLHOOKSTRUCT)

  If nCode = #HC_ACTION
    If wParam = #WM_KEYDOWN  Or wParam = #WM_SYSKEYDOWN ; Or wParam = #WM_KEYUP Or wParam = #WM_SYSKEYUP
	
	Select *p\vkcode
	
		case #VK_F1
			*p\vkcode =  #VK_F11
		case #VK_F2
			*p\vkcode =  #VK_F12"
	EndSelect
		
    endif
  endif

  ProcedureReturn CallNextHookEx_(0, nCode, wParam, *p)

EndProcedure


Procedure Keyboard_Hook()
  ; Win NT
  #WH_KEYBOARD_LL = 13
  hook = SetWindowsHookEx_(#WH_KEYBOARD_LL,@myKeyboardHook(),GetModuleHandle_(0),0)
  If hook = 0
    End
  EndIf
    
EndProcedure

Procedure Keyboard_Unhook()
  UnhookWindowsHookEx_(hook) 
EndProcedure
User avatar
Michael Vogel
Addict
Addict
Posts: 2797
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: Swap keyboard key functions

Post by Michael Vogel »

Sounds interesting, thank you.

BTW the unregister idea I had works fine but does result in quite a lot of code...

Anyhow I need to investigate it a little bit more, a quick test did not work here (tried to swap 'y' and 'z') and there is no information in the structure about the state of the shift, control, alt and window keys.

Code: Select all

Global Hook

Procedure.l myKeyboardHook(nCode, wParam, *p.KBDLLHOOKSTRUCT)

	If nCode = #HC_ACTION
		If wParam = #WM_KEYDOWN  Or wParam = #WM_SYSKEYDOWN  Or wParam = #WM_KEYUP Or wParam = #WM_SYSKEYUP

			Select *p\vkcode
			Case #VK_Y
				Debug *p\scanCode
				;Debug *p\dwExtraInfo\l;  Pointer=0
				*p\vkcode =  #VK_Z
			Case #VK_Z
				*p\vkcode =  #VK_Y
			EndSelect

		EndIf
	EndIf

	ProcedureReturn CallNextHookEx_(0, nCode, wParam, *p)

EndProcedure
Procedure KeyboardHook()

	#WH_KEYBOARD_LL = 13

	If hook=0
		hook = SetWindowsHookEx_(#WH_KEYBOARD_LL,@myKeyboardHook(),GetModuleHandle_(0),0)
		If hook = 0
			Debug ":("
		EndIf

	Else
		UnhookWindowsHookEx_(hook)
		hook=0

	EndIf

	SetGadgetText(1,StringField("START.STOP",Bool(hook)+1,"."))

EndProcedure

OpenWindow(0,0,0,500,300,"Hoooook")
StringGadget(0,10,10,480,130,"")
ButtonGadget(1,10,150,480,30,"")

SetActiveGadget(0)

KeyboardHook()

Repeat
	Select WaitWindowEvent()
	Case #PB_Event_Gadget
		If EventGadget()=1
			KeyboardHook()
			SetActiveGadget(0)
		EndIf
	Case #PB_Event_CloseWindow
		quit=1
	EndSelect
Until quit
SMaag
Enthusiast
Enthusiast
Posts: 302
Joined: Sat Jan 14, 2023 6:55 pm
Location: Bavaria/Germany

Re: Swap keyboard key functions

Post by SMaag »

here a C-Code for exchanging keys

Code: Select all

LRESULT CALLBACK keyboardHook(int nCode, WPARAM wParam, LPARAM lParam) {

    KBDLLHOOKSTRUCT *p = (KBDLLHOOKSTRUCT *) lParam;

    DWORD newVkCode;
    INPUT inputs[1];
    UINT ret;

    char wParamStr[11];
    char vkStr    [10] = "";

    If (nCode < 0) {
     //
     // nCode: A code the hook Procedure uses To determine how To process the message.
     // If nCode is less than zero, the hook Procedure must pass the message To
     // the CallNextHookEx function without further processing And should Return
     // the value returned by CallNextHookEx.
     //
        Return CallNextHookEx(hook, nCode, wParam, lParam);
    }

#ifdef SHOW_INFO

    If      (wParam == WM_KEYDOWN)    strcpy(wParamStr, "KEYDOWN");
    Else If (wParam == WM_KEYUP)      strcpy(wParamStr, "KEYUP");
    Else If (wParam == WM_SYSKEYDOWN) strcpy(wParamStr, "SYSKEYDOWN");
    Else If (wParam == WM_SYSKEYUP)   strcpy(wParamStr, "SYSKEYUP");
    Else                              strcpy(wParamStr, "UNKNOWN");

    If      (p->vkCode ==   9) strcpy(vkStr, "<TAB>"    );
    Else If (p->vkCode ==  10) strcpy(vkStr, "<LF>"     );
    Else If (p->vkCode ==  13) strcpy(vkStr, "<CR>"     );
    Else If (p->vkCode ==  20) strcpy(vkStr, "<CAPSLCK>");
    Else If (p->vkCode ==  27) strcpy(vkStr, "<ESC>"    );
    Else If (p->vkCode == 161) strcpy(vkStr, "<L-SHIFT>");
    Else If (p->vkCode == 162) strcpy(vkStr, "<L-CTRL>" );
    Else If (p->vkCode == 163) strcpy(vkStr, "<R-CTRL>" );
    Else If (p->vkCode == 164) strcpy(vkStr, "<L-ALT>"  );
    Else If (p->vkCode == 165) strcpy(vkStr, "<R-ALT>"  );
    Else If (p->vkCode == 226) strcpy(vkStr, "<R-SHIFT>");
    Else vkStr[0] = p->vkCode;

    char wndTitle[256];
    HWND hwnd=GetForegroundWindow();
    GetWindowText(hwnd, wndTitle, SizeOf(wndTitle));

    WINDOWPLACEMENT wp;
    wp.length = SizeOf(wp);

    GetWindowPlacement(hwnd, &wp);
    char* showCmd;
    If (wp.showCmd == SW_MAXIMIZE) { showCmd = "    SW_MAXIMIZE"; }
    Else                           { showCmd = "not SW_MAXIMIZE"; }

    printf(" %-10s - %3lu | %9s | %3d - %lu | %-30s %s\n", wParamStr, p->vkCode, vkStr, p->scanCode, p->time, wndTitle, showCmd);

#endif

    inputs[0].type           = INPUT_KEYBOARD;
    inputs[0].ki.wScan       = 0;
    inputs[0].ki.dwFlags     = 0;
    inputs[0].ki.time        = 0;
    inputs[0].ki.dwExtraInfo = 0;

    If (wParam == WM_KEYUP || wParam == WM_SYSKEYUP) {
        inputs[0].ki.dwFlags = KEYEVENTF_KEYUP;
    }

    If ((p->flags & LLKHF_INJECTED) == 0) {
     //
     // If the LLKHF_INJECTED (= 0x10) bit is set, the event was injected.
     //
     // With the previous test, we skip over injected events And only
     // change «original» events.
     //
        If (p->vkCode == VK_ESCAPE) {                    // ESC (27) -> CAPS LOCK (20)
            inputs[0].ki.wVk = VK_CAPITAL;
            ret = SendInput(1, inputs, SizeOf (INPUT));
            Return 1;
        }
        If (p->vkCode == VK_CAPITAL) {                   // CAPS LOCK (20) -> ESC (27)
            inputs[0].ki.wVk = VK_ESCAPE;
            ret = SendInput(1, inputs, SizeOf (INPUT));
            Return 1;
        }
        If (p->vkCode == VK_RMENU) {                     // Right Alt (165) -> Right Ctrl (163)
            inputs[0].ki.wVk = VK_RCONTROL;
            ret = SendInput(1, inputs, SizeOf (INPUT));
            Return 1;
        }
    }


    Return CallNextHookEx(hook, nCode, wParam, lParam);
}

User avatar
Michael Vogel
Addict
Addict
Posts: 2797
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: Swap keyboard key functions

Post by Michael Vogel »

Thanks once more, seems to work in a first test, will take a while until I'm ready with all details.

Code: Select all

Global Hook

Global Dim Inputs.INPUT(0)


Procedure.l myKeyboardHook(nCode, wParam, *p.KBDLLHOOKSTRUCT)

	If ncode<0
		ProcedureReturn CallNextHookEx_(0, nCode, wParam, *p)
	EndIf
	;	ElseIf nCode = #HC_ACTION

	With Inputs(0)
		\type=			#INPUT_KEYBOARD
		\ki\wScan=		0
		\ki\time=			0
		\ki\dwFlags=		#KEYEVENTF_KEYUP * Bool(wParam=#WM_KEYUP Or wParam=#WM_SYSKEYUP)
		\ki\dwExtraInfo=	0
	EndWith

	If *p\flags & #LLKHF_INJECTED=0
		Select *p\vkcode
		Case #VK_Y
			Inputs(0)\ki\wVk=#VK_Z
			SendInput_(1,inputs(),SizeOf(INPUT))
			ProcedureReturn 1
		Case #VK_Z
			Inputs(0)\ki\wVk=#VK_Y
			SendInput_(1,inputs(),SizeOf(INPUT))
			ProcedureReturn 1
		EndSelect
	EndIf

	ProcedureReturn CallNextHookEx_(0, nCode, wParam, *p)

EndProcedure
Procedure KeyboardHook()

	#WH_KEYBOARD_LL = 13

	If hook=0
		hook = SetWindowsHookEx_(#WH_KEYBOARD_LL,@myKeyboardHook(),GetModuleHandle_(0),0)
		If hook = 0
			Debug ":("
		EndIf

	Else
		UnhookWindowsHookEx_(hook)
		hook=0

	EndIf

	SetGadgetText(1,StringField("START.STOP",Bool(hook)+1,"."))

EndProcedure

OpenWindow(0,0,0,500,300,"Hoooook")
StringGadget(0,10,10,480,130,"")
ButtonGadget(1,10,150,480,30,"")

SetActiveGadget(0)

KeyboardHook()

Repeat
	Select WaitWindowEvent()
	Case #PB_Event_Gadget
		If EventGadget()=1
			KeyboardHook()
			SetActiveGadget(0)
		EndIf
	Case #PB_Event_CloseWindow
		quit=1
	EndSelect
Until quit
BarryG
Addict
Addict
Posts: 4123
Joined: Thu Apr 18, 2019 8:17 am

Re: Swap keyboard key functions

Post by BarryG »

[Deleted; not a keyboard hook problem]
Last edited by BarryG on Mon Sep 25, 2023 8:49 am, edited 1 time in total.
User avatar
Mijikai
Addict
Addict
Posts: 1517
Joined: Sun Sep 11, 2016 2:17 pm

Re: Swap keyboard key functions

Post by Mijikai »

I only had a quick look but mby try to use the hhk handle in CallNextHookEx_() despite what msdn recommends.
BarryG
Addict
Addict
Posts: 4123
Joined: Thu Apr 18, 2019 8:17 am

Re: Swap keyboard key functions

Post by BarryG »

[Deleted; not a keyboard hook problem]
Last edited by BarryG on Mon Sep 25, 2023 8:49 am, edited 1 time in total.
BarryG
Addict
Addict
Posts: 4123
Joined: Thu Apr 18, 2019 8:17 am

Re: Swap keyboard key functions

Post by BarryG »

[Deleted; not a keyboard hook problem]
Last edited by BarryG on Mon Sep 25, 2023 8:49 am, edited 1 time in total.
User avatar
Michael Vogel
Addict
Addict
Posts: 2797
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: Swap keyboard key functions

Post by Michael Vogel »

Hi, I have the routine running for quite a while with no problems (Windows 11), but my program has already around 3000 code lines...

...so it took me a while to remove most of them to get "byte sized pieces" :wink: - here we go, would give it a try if it works on your computer.

_____
PS: it also includes an ugly workaround for the problem to have a handle for the latest active window (viewtopic.php?p=605365).

Code: Select all

; Define

	EnableExplicit

	#GetApiTextWidth=	1
	#DebugPowerGraph=	0
	#UseTaskbarColor=	0

	Enumeration
		#Win
		#WinMessage
		#SysTray
	EndEnumeration

	Enumeration
		#ImageIcon;	für Systray und Message
		#Pop
		#PopRefreshWallpaper
		#PopPrintScreen
		#PopKeyActive
		#PopQuit
		#PopClose
		#PopSaveSettings
		#PopReloadSettings
		#PopMessages
		#EventResizeWindow
		#EventAbortSelection
	EndEnumeration


	Structure FrameType
		Mode.i;			aktueller Modus
	EndStructure

	Structure WinType
		HandleScreen.i;	Main-Window
		HandleActive.i;	aktives Fenster für Screenshot (damit beim Auswählen der Screenshot-Funktion aus dem Menü nicht wieder ein anderes Fenster aktiv wird)
		HandleTaskbar.i;	Taskbar
		DpiMode.i
		DpiScale.i;	Skalierung (4096:n)
		Sx.i;		Bildschirmbreite
		Sy.i;		Bildschirmhöhe
		Sx_.i;		Bildschirmbreite für skalierte Dialoge/Fenster
		Sy_.i;		Bildschirmhöhe  für skalierte Dialoge/Fenster
		Semaphore.i;	ResizeWindow-Konflikt auflösen
		Thread.i;		Fader
	EndStructure


	#PB_DpiBits=		16
	#PB_DpiScale=		1<<#PB_DpiBits
	#PB_Shortcut_SrodFlag=1<<16

	#Undefined=			-1

	Global Frame.FrameType

	Structure KeyType
		KeyActive.i
		KeyHook.i
	EndStructure

	Global Dim Inputs.INPUT(0)
	Global Key.KeyType
	Global Win.WinType

	Macro ScaleUp(value)

		(((value)*Win\DpiScale)>>#PB_DpiBits)

	EndMacro
	Macro ScaleDown(value)
		(((value)<<#PB_DpiBits)/Win\DpiScale)
	EndMacro

; EndDefine

Procedure InitPopupMenu()

	#Q=			#DOUBLEQUOTE$

	CreatePopupMenu(#Pop)
	MenuItem(#PopPrintScreen,"Copy Screen"+#TAB$+"PrtScr")
	MenuBar()
	MenuItem(#PopQuit,"Quit Program")
	MenuItem(#PopClose,"Close Menu")
	CloseSubMenu()

	Protected i

EndProcedure
Procedure.l KeyboardHook(nCode,wParam,*p.KBDLLHOOKSTRUCT)

	Protected o

	If ncode<0
		ProcedureReturn CallNextHookEx_(0,nCode,wParam,*p)
	EndIf

	With Inputs(0)
		\type=			#INPUT_KEYBOARD
		\ki\wScan=		0
		\ki\time=			0
		\ki\dwFlags=		#KEYEVENTF_KEYUP * Bool(wParam=#WM_KEYUP Or wParam=#WM_SYSKEYUP)
		\ki\dwExtraInfo=	0
	EndWith

	If *p\flags & #LLKHF_INJECTED=0
		Select *p\vkcode

		Case #VK_SNAPSHOT;			Alt+PrtScr und Alt+Shift+PrtScr funktionieren nicht
			; Debug "Print"
			; KeyboardState()
			If wParam=#WM_KEYDOWN
				Debug "Printkey: "+Str(((GetAsyncKeyState_(#VK_SHIFT)>>15)&1) | ((GetAsyncKeyState_(#VK_CONTROL)>>14)&2) | ((GetAsyncKeyState_(#VK_MENU)>>13)&4) | ((GetAsyncKeyState_(#VK_LWIN)>>12)&8))
				Select ((GetAsyncKeyState_(#VK_SHIFT)>>15)&1) | ((GetAsyncKeyState_(#VK_CONTROL)>>14)&2) | ((GetAsyncKeyState_(#VK_MENU)>>13)&4) | ((GetAsyncKeyState_(#VK_LWIN)>>12)&8)
				Case 0
					PostMessage_(Win\HandleScreen,#WM_COMMAND,#PB_Shortcut_SrodFlag|#PopPrintScreen,0)
					; Debug "Screen Snapshot"
				Case 8
					If Key\KeyActive
						Inputs(0)\ki\wVk=#VK_LWIN;				Win up
						Inputs(0)\ki\dwFlags=#KEYEVENTF_KEYUP
						;SendInput_(1,inputs(),SizeOf(INPUT))
						Inputs(0)\ki\wVk=#VK_SHIFT;				Shift down
						Inputs(0)\ki\dwFlags=#Null
						SendInput_(1,inputs(),SizeOf(INPUT))
						Inputs(0)\ki\wVk=#VK_INSERT;				Shift+Insert
						Inputs(0)\ki\dwFlags=#Null
						SendInput_(1,inputs(),SizeOf(INPUT))
						Inputs(0)\ki\wVk=#VK_LWIN;				Win down
						Inputs(0)\ki\dwFlags=#Null
						SendInput_(1,inputs(),SizeOf(INPUT))
						Inputs(0)\ki\wVk=#VK_SHIFT;				Shift up
						Inputs(0)\ki\dwFlags=#KEYEVENTF_KEYUP
						SendInput_(1,inputs(),SizeOf(INPUT))
						ProcedureReturn 1
					EndIf
				EndSelect
			EndIf
			ProcedureReturn 1

		EndSelect
	EndIf

	ProcedureReturn CallNextHookEx_(0,nCode,wParam,*p)

EndProcedure
Procedure ToggleKeyboardHook()

	Protected result

	If Key\KeyHook=0
		Key\KeyHook=SetWindowsHookEx_(#WH_KEYBOARD_LL,@KeyboardHook(),GetModuleHandle_(0),0)
		result=#Undefined+Bool(Key\KeyHook)<<1; -1: Hook konnte nicht erzeugt werden, +1: Hook aktiv
	Else
		UnhookWindowsHookEx_(Key\KeyHook)
		Key\KeyHook=0
		result=#Null;						0: Hook deaktiviert
	EndIf

	ProcedureReturn result

EndProcedure

Procedure Main()

	;Protected cursor.Point
	Protected r.rect
	Protected handle
	Protected *LibFunction
	Protected n
	Protected tick
	Protected BoardNow
	Protected quit
	Protected s.s

	Protected mutti=CreateMutex_(0,0,@"+v+")
	If GetLastError_()=#ERROR_ALREADY_EXISTS
		ReleaseMutex_(mutti)

	Else
		With Win
			\DpiScale=GetDeviceCaps_(GetDC_(0),#LOGPIXELSX)<<#PB_DpiBits/96
			\Sx=	GetSystemMetrics_(#SM_CXSCREEN)
			\Sy=	GetSystemMetrics_(#SM_CYSCREEN)
			\Sx_=ScaleDown(\Sx)
			\Sy_=ScaleDown(\Sy)

			\HandleTaskbar=FindWindowEx_(#Null,#Null,"Shell_TrayWnd",#Null)
			\HandleScreen=OpenWindow(#Win,0,0,\SX_,\SY_,"[Board]",#PB_Window_BorderLess|#PB_Window_Invisible|#PB_Window_NoActivate)
			SetWindowLong_(\HandleScreen,#GWL_EXSTYLE,#WS_EX_TOOLWINDOW); Hide from Taskbar

			AddWindowTimer(#Win,0,125); 			Blend = 1/4 Timer für CPU-Load
			CreateImage(#ImageIcon,20,20,32,#Blue)
			AddSysTrayIcon(0,\HandleScreen,ImageID(#ImageIcon))
			SysTrayIconToolTip(0,"Boarder Tool by Michael Vogel")

			SmartWindowRefresh(#Win,1);			flickers when doing HideWindow(...,0)
			StickyWindow(#Win,1)
			;HideWindow(#Win,0)
			;AddWindowTimer(#Win,0,250)

		EndWith


		ToggleKeyboardHook()

		InitPopupMenu()
		Repeat

			Select WaitWindowEvent()

			Case #PB_Event_Menu
				n=EventMenu()
				Select n
				Case #PopPrintScreen
					Debug "OKAY"

				Case #PopKeyActive
					Key\KeyActive!1

				Case #PopQuit
					quit=1

				EndSelect

			Case #PB_Event_Timer
				Tick+1

			Case #PB_Event_SysTray;
				Select EventType()
				Case #PB_EventType_LeftClick
					DisplayPopupMenu(#Pop,Win\HandleScreen)

				Case #PB_EventType_RightClick

				EndSelect

			Case #PB_Event_CloseWindow
				quit=1
			EndSelect

			With Win
				n=GetForegroundWindow_()
				#WS_EX_NOREDIRECTIONBITMAP=$200000
				If GetWindowLong_(n,#GWL_EXSTYLE)&#WS_EX_NOREDIRECTIONBITMAP=#Null
					: CompilerIf #PB_Compiler_Debugger :
					If n<>\HandleActive And n<>\HandleTaskbar And n<>\HandleScreen; And n<>\HandleOverflow
						: CompilerElse :
						: If n<>\HandleTaskbar And n<>\HandleScreen; And n<>\HandleOverflow :
						: CompilerEndIf :
						\HandleActive=n
						; Debug "New Active Window = "+n
					EndIf
				EndIf
			EndWith

		Until quit

	EndIf

EndProcedure

Main()

benubi
Enthusiast
Enthusiast
Posts: 215
Joined: Tue Mar 29, 2005 4:01 pm

Re: Swap keyboard key functions

Post by benubi »

@BarryG

I never tested or used it but I have suspicions.

After a while being away from keyboard your session will be logged off or sent into the background, or maybe it's the screen blanker. This should nullify your hooks to prevent I/O conflicts (with login screen etc). Perhaps the peripherals are getting switched off to save current; and once they are reconnected the hooks may be set again, because there are new handles and the old are invalid. Something like that.

Does this lead to somewhere?
BarryG
Addict
Addict
Posts: 4123
Joined: Thu Apr 18, 2019 8:17 am

Re: Swap keyboard key functions

Post by BarryG »

[Deleted; not a keyboard hook problem]
Last edited by BarryG on Mon Sep 25, 2023 8:49 am, edited 1 time in total.
benubi
Enthusiast
Enthusiast
Posts: 215
Joined: Tue Mar 29, 2005 4:01 pm

Re: Swap keyboard key functions

Post by benubi »

Hmmm, no,

I'm no WinAPI expert but I suppose these GetMessage_() and TranslateMessage_() API functions are called by the WaitWindowEvent() procedure.

Except that instead of GetMessage_() it may use a WaitMessage_() kind of API function to lower CPU consumption, before doing PB internal things, and returning a #PB_Event_* translated window event number.

Perhaps there's a bug or glitch in the WaitWindowEvent() procedure, but I've often seen Sendkeys kind of programs done in PB so this would have been noticed in other places.

One thing I imagined could be buffer full problem, but that's improbable if you type by hand, or that the message queue is full. Does it completely stop or does it work sparsely? How come you don't know if a screen blanker opened in between? I might be sending you the wrong way anyway. :?
User avatar
Mijikai
Addict
Addict
Posts: 1517
Joined: Sun Sep 11, 2016 2:17 pm

Re: Swap keyboard key functions

Post by Mijikai »

I came up with this, give it a try.
Im not entirely sure on the ThreadMessage stuff but mby it works.

KMAP Library:

Code: Select all

EnableExplicit

;KMAP - Library (installs a keyboard hook to swap keys)
;Version: dev.1
;Author: Mijikai
;Platform: Windows

Structure KEY_STRUCT
  old.a
  new.a
EndStructure

Global hook.i
Global hevent.i
Global hthread.i
Global thread_id.i
Global lock.CRITICAL_SECTION
Global NewList key.KEY_STRUCT()
Global inp.INPUT

Procedure.i kmapProc(Code.i,*Parameter,*Info.KBDLLHOOKSTRUCT)
  If Code = #HC_ACTION
    If Not *Info\flags & #LLKHF_INJECTED
      EnterCriticalSection_(@lock)
      ForEach key()
        If key()\old = *Info\vkCode
          inp\ki\wVk = key()\new
          SendInput_(1,@inp,SizeOf(INPUT))
          LeaveCriticalSection_(@lock)
          ProcedureReturn 1
        EndIf
      Next
      LeaveCriticalSection_(@lock)
    EndIf
  EndIf
  ProcedureReturn CallNextHookEx_(hook,Code,*Parameter,*Info)
EndProcedure

Procedure.i kmapThread(*Parameter)
  Protected task.MSG
  hook = SetWindowsHookEx_(#WH_KEYBOARD_LL,@kmapProc(),GetModuleHandle_(#Null),#Null)
  SetEvent_(hevent)
  If hook
    While GetMessage_(@task,#Null,#Null,#Null)
      If task\message = #WM_USER
        Break
      EndIf
      TranslateMessage_(@task)
      DispatchMessage_(@task)
    Wend
    UnhookWindowsHookEx_(hook)
  EndIf
  ProcedureReturn #Null
EndProcedure

ProcedureDLL.i kmapAdd(KeyOld.i,KeyNew.i)
  EnterCriticalSection_(@lock)
  If AddElement(key())
    key()\old = KeyOld
    key()\new = KeyNew
    LeaveCriticalSection_(@lock)
    ProcedureReturn #True  
  EndIf
  LeaveCriticalSection_(@lock)
  ProcedureReturn #False
EndProcedure

ProcedureDLL.i kmapReset()
  EnterCriticalSection_(@lock)
  ClearList(key())
  LeaveCriticalSection_(@lock)
  ProcedureReturn #Null
EndProcedure

ProcedureDLL.i kmapInstall()
  If hook = #Null
    inp\type = #INPUT_KEYBOARD
    hevent = CreateEvent_(#Null,#False,#False,#Null)
    If hevent
      InitializeCriticalSection_(@lock)
      hthread = CreateThread_(#Null,#Null,@kmapThread(),#Null,#Null,@thread_id)
      If hthread
        WaitForSingleObject_(hevent,#INFINITE)
        If hook
          ProcedureReturn #True
        EndIf
        WaitForSingleObject_(hthread,#INFINITE)
        CloseHandle_(hthread)
      EndIf
      CloseHandle_(hevent)
      DeleteCriticalSection_(@lock)
    EndIf
  EndIf
  ProcedureReturn #False
EndProcedure

ProcedureDLL.i kmapUninstall()
  If hook
    PostThreadMessage_(thread_id,#WM_USER,#Null,#Null)
    WaitForSingleObject_(hthread,#INFINITE)
    DeleteCriticalSection_(@lock)
    CloseHandle_(hthread)
    CloseHandle_(hevent)
    ClearList(key())
    hook = #Null
  EndIf
  ProcedureReturn #Null
EndProcedure

ProcedureDLL.i kmapVersion()
  ProcedureReturn $0001
EndProcedure
Example:

Code: Select all

EnableExplicit

;KMAP - Library

;Example

Import "kmap.lib"
  kmapInstall()
  kmapAdd.i(KeyOld.i,KeyNew.i)
  kmapReset.i()
  kmapUninstall.i()
  kmapVersion.i()
EndImport

If OpenConsole("")
  If kmapInstall()
    kmapAdd(#VK_Z,#VK_Y)
    Repeat
      PrintN(Input())
    ForEver
    kmapUninstall()
  EndIf
  CloseConsole()
EndIf

End
Post Reply