[Win] Enable / Disable hotkeys on the fly?

Started by Grisu, March 28, 2020, 13:15:00

Previous topic - Next topic

Grisu

Hi everyone,

I'm using the following old code for setting up hotkeys in my app.
It uses a global var "ini_dis_hotkeys" to set the usage of hotkeys at the start of the app,"0" or "1".
In the example óode the key "ALT+X" is registered. But I have more than one to deal with.


' Hotkey example code
Import maxgui.drivers

Global ini_dis_hotkeys:Int=0 '<- disable all hotkeys?

?Win32 ' HOTKEY STUFF -------------------------------------------------------------------------------------
Extern "win32"
Function GetLastError:Int ()
End Extern

Const MOD_ALT:Int = $0001
'Const MOD_CONTROL:Int = $0002
'Const MOD_NOREPEAT:Int = $4000
'Const MOD_SHIFT:Int = $0004
'Const MOD_WIN:Int = $0008
'Const VK_LWIN: Int

Global mainwindow:TGadget = CreateWindow ("Hotkey Example Code", 320, 200, 320, 200)

Global OldWindowFunc:Int = SetWindowFunc( MainWindow, WindowFunc)

If ini_dis_hotkeys  = 0 Then

Local RegisterHotkey:Int (hWnd:Int, id:Int, fsModifiers:Int, vk:Int) "win32"
Local user32:Int = LoadLibraryA ("user32.dll")

If user32 Then
RegisterHotkey = GetProcAddress (user32, "RegisterHotKey")
EndIf

' Hotkeylist: http://msdn.microsoft.com/en-us/library/windows/Desktop/dd375731(v=VS.85).aspx
' https://docs.microsoft.com/en-us/windows/desktop/inputdev/virtual-key-codes

Print ""
Print ""
Print ""
Print "Registering ALT+X as Hotkey: "+ RegisterHotkey (QueryGadget (MainWindow , QUERY_HWND) , 1 , MOD_ALT , $58) ' = "X"
Print ""
Print "PRESS ALT+X TO EXIT APP!"
EndIf ' disablehotkeys  = 0
?

' VKEY CODES:
' <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=VS.85).aspx" target="_blank">http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=VS.85).aspx</a>

' MODIFIER CONSTANTS HERE:
' <a href="http://msdn.microsoft.com/en-us/library/windows/Desktop/ms646309(v=VS.85).aspx" target="_blank">http://msdn.microsoft.com/en-us/library/windows/Desktop/ms646309(v=VS.85).aspx</a>

' HOTKEY STUFF --------------------------------------------------------------------------------------------------------

Repeat
Select WaitEvent ()
Case EVENT_WINDOWCLOSE
Exit
End Select
Forever

End ' of world

' HOTKEY FUNCS -----------------------------------------------------------------------------------------
Function WindowFunc:Int( hwnd:Int, msg:Int, wparam:Int, lparam:Int) "Win32"
Local hdr:Int Ptr = Int Ptr(lparam)

Select msg

Case WM_HOTKEY

      If ini_dis_hotkeys=0 Then ' Disable Hotkeys?

        If Int(lparam) = "5767169" Then PostEvent(CreateEvent(EVENT_WINDOWCLOSE, MainWindow))
        Print "ALT + X pressed"

Notify "OK "+lparam ' HANDLE HOTKEY HERE!
  Print Int(lparam)

      EndIf

Case WM_DESTROY
SetWindowLong( hwnd , GWL_WNDPROC , OldWindowFunc)

End Select


Return CallWindowProc( OldWindowFunc, hwnd, msg, wparam, Int(lparam))
EndFunction

' setting window function
Const GWL_WNDPROC:Int = -4

Extern "Win32"
Function SetWindowLong:Int(HWND:Int,nIndex:Int,dwNewLong:Int) = "SetWindowLongA@12"
Function GetWindowLong:Int(HWND:Int,nIndex:Int) = "GetWindowLongA@8"
Function CallWindowProc:Int( prevwndproc:Int, hwnd:Int, msg:Int, wparam:Int, lparam:Int) = "CallWindowProcA@20"
EndExtern

Function SetWindowFunc:Int( window:TGadget, newfunc:Int( hwnd:Int, msg:Int, wparam:Int, lparam:Int))
Local hwnd:Int = QueryGadget( window, QUERY_HWND)
Local oldfunc:Int = GetWindowLong( hwnd, GWL_WNDPROC)
SetWindowLong( hwnd, GWL_WNDPROC, Int(Byte Ptr newfunc))
Return oldfunc
EndFunction
?
'------------------------------------------------------------------------------------------------------


My issue is, that I want to unregister and register the hotkeys during runtime. That means without the need of an app restart.
Currently, I just "set ini_dis_hotkeys" to 1 it will just leave out some code, but the keys themself will be still registered by Windows.

So far I can't move the register commands to an extra function, it won't let me compile.
Also I don't have an unregister function for the keys.

Does anyone have a working code for this?
Or a way to get around this problem?

Cheers
Grisu
Pocket Radio Player     Cardwar

wombats

Can you not use SetHotKeyEvent and RemoveHotKey?

Grisu

Does it also support the "media keys" of a keyboard (volume up/down, Play, Stop, Mute...)? From the magui docs (BRL.KeyCodes), it doesn't look like it.

If so, is there a small example of how to handle/detect the key events properly?
Pocket Radio Player     Cardwar

Henri

Hi Grisu,

I modified your example a bit to add some hotkey handling functions (see at the bottom):

Code (blitzmax) Select

Strict

' Hotkey example code
Import maxgui.drivers

'GUI
Global mainwindow:TGadget = CreateWindow ("Hotkey Example Code", 320, 200, 320, 200)
Global OldWindowFunc:Int = SetWindowFunc( MainWindow, WindowFunc)
Global button:TGadget = CreateButton("DisableHotkeys", 95, 50, 100, 40, mainwindow)

'RegisterHotkey on startup
AddHotkey (QueryGadget (MainWindow, QUERY_HWND), 1, MOD_ALT, $58)

Print "~n~n~n"
Print "Registering ALT + X as Hotkey:~n"
Print "PRESS ALT + X TO SEE A POPUP!"


' HOTKEY STUFF --------------------------------------------------------------------------------------------------------
Repeat
Select WaitEvent ()
Case EVENT_WINDOWCLOSE
Exit

Case EVENT_GADGETACTION
Select EventSource()
Case button

Print "Button pressed"

Select GadgetText(button).ToUpper()
Case "DISABLEHOTKEYS"

DebugLog "DisableHotkeys"

SetGadgetText(button, "EnableHotkeys")
DisableHotkeys()
Case "ENABLEHOTKEYS"

DebugLog "EnableHotkeys"

SetGadgetText(button, "DisableHotkeys")
EnableHotkeys()
EndSelect
EndSelect
End Select
Forever

End ' of world

' HOTKEY FUNCS -----------------------------------------------------------------------------------------

' setting window function
Const GWL_WNDPROC:Int = -4
Const MOD_ALT:Int = $0001

Function WindowFunc:Int( hwnd:Int, msg:Int, wparam:Int, lparam:Int) "Win32"
Local hdr:Int Ptr = Int Ptr(lparam)

Select msg

Case WM_HOTKEY

Local h:THotkey = GetHotKeyFromID(wparam)
If h Then
Notify "Hotkey pressed (id = " + h.id + ")! Key = " + h.key + " | modifier = " + h.modifier
EndIf

Case WM_DESTROY
SetWindowLong( hwnd , GWL_WNDPROC , OldWindowFunc)

End Select


Return CallWindowProc( OldWindowFunc, hwnd, msg, wparam, Int(lparam))
EndFunction

Extern "Win32"
Function RegisterHotkey:Int (hWnd:Int, id:Int, fsModifiers:Int, vk:Int) = "RegisterHotKey@16"
Function UnRegisterHotkey:Int (hWnd:Int, id:Int) = "UnregisterHotKey@8"
Function SetWindowLong:Int(HWND:Int,nIndex:Int,dwNewLong:Int) = "SetWindowLongA@12"
Function GetWindowLong:Int(HWND:Int,nIndex:Int) = "GetWindowLongA@8"
Function CallWindowProc:Int( prevwndproc:Int, hwnd:Int, msg:Int, wparam:Int, lparam:Int) = "CallWindowProcA@20"
EndExtern

Function SetWindowFunc:Int( window:TGadget, newfunc:Int( hwnd:Int, msg:Int, wparam:Int, lparam:Int))
Local hwnd:Int = QueryGadget( window, QUERY_HWND)
Local oldfunc:Int = GetWindowLong( hwnd, GWL_WNDPROC)
SetWindowLong( hwnd, GWL_WNDPROC, Int(Byte Ptr newfunc))
Return oldfunc
EndFunction
?
'------------------------------------------------------------------------------------------------------

'New stuff
'---------
Type THotkey

Global hotkeys:TList = New TList

Field hwnd:Int
Field id:Int
Field key:Int
Field modifier:Int

Function Create(hwnd:Int, id:Int, modifier:Int, key:Int)

Local h:THotkey, found:Int

'Check if already exists and overwrite
For h = EachIn hotkeys
If h.id = id Then
h.hwnd = hwnd
h.id = id
h.key = key
h.modifier = modifier

found = True; Exit
EndIf
Next

If Not found Then
h = New THotkey
h.hwnd = hwnd
h.id = id
h.key = key
h.modifier = modifier

hotkeys.addlast(h)
EndIf

DebugLog "Registering hot key: " + RegisterHotkey(hwnd, id, modifier, key)
EndFunction

EndType

Function AddHotkey(hwnd:Int, id:Int, modifier:Int, key:Int)
THotkey.Create(hwnd, id, modifier, key)
EndFunction

Function ClearHotkeys()
For Local h:THotkey = EachIn THotkey.hotkeys
If h.id Then UnRegisterHotkey(h.hwnd, h.id)
Next
THotkey.hotkeys.Clear()
EndFunction

Function DisableHotkeys()
For Local h:THotkey = EachIn THotkey.hotkeys
If h.id Then UnRegisterHotkey(h.hwnd, h.id)
Next
EndFunction

Function EnableHotkeys()
For Local h:THotkey = EachIn THotkey.hotkeys
If h.id Then RegisterHotkey(h.hwnd, h.id, h.modifier, h.key)
Next
EndFunction

Function GetHotKeyFromID:THotkey(id:Int)
For Local h:THotkey = EachIn THotkey.hotkeys
If h.id = id Then Return h
Next

Return Null
EndFunction



-Henri
- Got 01100011 problems, but the bit ain't 00000001

Grisu

Good evening Henri,

you are a lifesaver in coding terms!  ^-^

I spent the afternoon fiddling around with your code. Somehow, it still didn't detect the hotkeys in my über-long code.
In the end, I moved all AddHotkey() commands to your EnableHotkeys() function.
Not so elegant, but this way, it works fine on my Windows 10 PC.  8)

I have put up a test build, if you want to give it a try. I want to make sure, it didn't break anything else.
Full distro: http://www.mediafire.com/file/shmdcqkkhycm0p4/prp_win32.zip/file
+ fixed exe: http://www.mediafire.com/file/u2y91rbi3eayp8k/prp_nightly.zip/file

Grisu

P.S. Is your e-mail (*.inet.fi) still valid?
Pocket Radio Player     Cardwar

Henri

Evening -> Night here :-)

Disable hotkeys-option seems to work on the fly at least, so that's progress. I noticed in the example that because events are processed in their own Windowproc function for the main window, that proc function needs to be registered before AddHotkey is called.

Yep, email is still the same.

-Henri
- Got 01100011 problems, but the bit ain't 00000001