using ExamineKeyboard() then using a hook might be what you are looking for.
I know an easy way is by using the #WM_CHAR event in the event loop but we are
often reminded not to use non-PureBasic events in our event loops.
I decided to take a dive into the deep water and look for another solution.
I investigated the use of subclassing but results were not satisfactory.
The use of a hook seemed to be the best bet so after much research I have
produced the following bit of code.
Note: this is for windows only.
You will see that any keypress is detected and a custom event is fired.
Any gadgets that use keyboard input will still behave normally.
The state of shift, ctrl, and alt can be determined as well with each event.
The virtual key code is provided but sorry, not the ascii code.
If you want ascii code you will have to convert it yourself.
Notice in the example that the return key can be detected and by
using GetActiveGadget() you can easily have your code respond
as desired. This is useful for gadgets that use string input such as
the StringGadget, SpinGadget, or EditorGadget or others.
Code: Select all
; Keyboard Hook example
; screen and ExamineKeyboard() is not required
; a custom event is generated when any key is pressed
; virtual key code can be obtained along with shift, ctrl, alt states
; by BasicallyPure
; date: 10/13/2014
; OS: windows
; compiler: PB 5.30
; forum topic: http://www.purebasic.fr/english/viewtopic.php?f=12&t=60775
EnableExplicit
Define vKeyCode, scanCode, shiftState, altKeyState, ctrlKeyState, Quit = #False
Define hHookProc ; <--- must have this for hook procedure
#KeyBoard_Event = #PB_Event_FirstCustomValue ; hook generates this event on any key press
#WinMain = 1
; here is the hook callback procedure
Procedure MyKeyboardHook(nCode, wParam, lParam)
; posts a custom event (#KeyBoard_Event) when a keyboard key is pressed
; information about the key press can be obtained with the following:
; EventData() returns wParam (the virtual key code).
; EventType() returns #True if shift key was down when key was pressed.
; EventGadget() returns lParam (repeat count (bits 0-15), scan code (bits 16-23),
; extended-key flag (bit 24), reserved (bits 25-28), context code (bit 29),
; previous key-state flag (bit 30), And transition-state flag (bit 31).
; see windows 'KeyboardProc callback function' for more detailed information.
; http://msdn.microsoft.com/en-us/library/windows/desktop/ms644984(v=vs.85).aspx
Shared hHookProc
Static shift, ctrl, alt
Protected vShift, vlParam
If Not (nCode < 0)
If Not lParam & $80000000 ; key down
If wParam = #VK_CONTROL : ctrl + 1 : EndIf
If wParam = #VK_SHIFT : shift + 1 : EndIf
If wParam = #VK_MENU : alt + 1 : EndIf
Select wParam
Case #VK_A To #VK_Z
vShift = Bool(shift > 0) ! (GetKeyState_(#VK_CAPITAL) & 1)
Default
vShift = Bool(shift > 0)
EndSelect
; use bit 25 of vlParam for 'CTRL' key state
vlParam = (lParam & $FDFFFFFF) | (Bool(ctrl > 0) << 25)
Select wParam
Case #VK_SHIFT
If shift < 2 ; prevent auto repeat with shift
PostEvent(#KeyBoard_Event,#WinMain,vlParam,vShift,wParam)
EndIf
Case #VK_CONTROL
If ctrl < 2 ; prevent auto repeat with ctrl
PostEvent(#KeyBoard_Event,#WinMain,vlParam,vShift,wParam)
EndIf
Case #VK_MENU
If alt < 2 ; prevent auto repeat with alt
PostEvent(#KeyBoard_Event,#WinMain,vlParam,vShift,wParam)
EndIf
Default
PostEvent(#KeyBoard_Event,#WinMain,vlParam,vShift,wParam)
EndSelect
Else ; key up
Select wParam
Case #VK_SHIFT : shift = 0
Case #VK_CONTROL : ctrl = 0
Case #VK_MENU : alt = 0
EndSelect
EndIf
EndIf
ProcedureReturn CallNextHookEx_(hHookProc,nCode,wParam,lParam)
EndProcedure
If OpenWindow(#WinMain,0,0,400,200,"keyboard hook",#PB_Window_ScreenCentered | #PB_Window_SystemMenu)
; create the hook
hHookProc = SetWindowsHookEx_(#WH_KEYBOARD,@MyKeyboardHook(),#Null,GetCurrentThreadId_())
StringGadget(0,10,10,100,25,"")
StringGadget(1,10,45,100,25,"")
SpinGadget(2,10,90,100,25,0,100,#PB_Spin_Numeric)
ButtonGadget(3,150,10,50,25,"clear")
EditorGadget(4,210,10,150,150)
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
Quit = #True
Case #PB_Event_Gadget
Select EventGadget()
Case 0 :
Case 1 :
Case 2 :
Case 3 : SetGadgetText(4,"") : SetActiveGadget(4)
Case 4 :
EndSelect
Case #KeyBoard_Event ; with this custom event
; EventData() gives virtual key code
; EventGadget() bits 16-23 gives scan code
; EventType() gives shift state
; EventGadget() bit 29 gives alt key state
; EventGadget() bit 25 gives ctrl key state
vKeyCode = EventData() ; obtain vKey code
scanCode = (EventGadget() & $FF0000) >> 16 ; obtain scan code
shiftState = EventType() ; obtain shift state
altKeyState = (EventGadget() & $20000000) >> 29 ; obtain alt key state
ctrlKeyState = (EventGadget() & $2000000) >> 25 ; obtain ctrl key state
Debug "virtual key code = " + vKeyCode +
" | scan code = " + scanCode +
" | shift = " + shiftState +
" | Alt = " + altKeyState +
" | ctrl = " + ctrlKeyState
; perform an action if 'return' key was pressed while
; the gadget of your choice had focus
If vKeyCode = #VK_RETURN
Select GetActiveGadget()
Case 0 : SetActiveGadget(1)
Case 1 : SetActiveGadget(0)
Case 2 : SetGadgetText(2,Str(GetGadgetState(2))) ; spin gadget
EndSelect
EndIf
EndSelect
Until Quit = #True
; remove hook
UnhookWindowsHookEx_(hHookProc)
EndIf
After taking another look at this I have decided it may be simpler to just use global variables to retrieve the keyboard data.
Here is the modified code.
Code: Select all
; Keyboard Hook example
; screen and ExamineKeyboard() are not required
; a custom event is generated when any key is pressed
; virtual key code can be obtained along with shift, ctrl, alt states
; by BasicallyPure
; date: 7/30/2015
; OS: windows
; compiler: PB 5.31
EnableExplicit
Define Quit = #False
Global hHookProc, vKeyCode, altKeyState, ctrlKeyState, shiftState, scanCode
#KeyBoard_Event = #PB_Event_FirstCustomValue ; hook generates this event on any key press
#WinMain = 1
; here is the hook callback procedure
Procedure KeyboardHook(nCode, wParam, lParam)
; posts this custom event: '#KeyBoard_Event' :when a keyboard key is pressed
; information about the key press can be obtained with the following:
; vKeyCode : the virtual key code '#VK_nnn'
; scanCode : the keyboard scan code
; shiftState : 1 = shifted, 0 = unshifted
; ctrlKeyState : 1 = pressed, 0 = not pressed
; altKeyState : 1 = pressed, 0 = not pressed
; see windows 'KeyboardProc callback function' for more detailed information.
; http://msdn.microsoft.com/en-us/library/windows/desktop/ms644984(v=vs.85).aspx
Static shift, ctrl, alt
If Not (nCode < 0)
If Not lParam & $80000000 ; key down
If wParam = #VK_CONTROL : ctrl + 1 : EndIf
If wParam = #VK_SHIFT : shift + 1 : EndIf
If wParam = #VK_MENU : alt + 1 : EndIf
shiftState = Bool(shift > 0)
If wParam >= #VK_A And wParam <= #VK_Z ; for CapsLock
shiftState ! (GetKeyState_(#VK_CAPITAL) & 1)
EndIf
vKeyCode = wParam
scanCode = (lParam & $FF0000) >> 16
altKeyState = Bool(alt > 0)
ctrlKeyState = Bool(ctrl > 0)
Select wParam
Case #VK_SHIFT
If shift < 2 ; prevent auto repeat with shift
PostEvent(#KeyBoard_Event)
EndIf
Case #VK_CONTROL
If ctrl < 2 ; prevent auto repeat with ctrl
PostEvent(#KeyBoard_Event)
EndIf
Case #VK_MENU
If alt < 2 ; prevent auto repeat with alt
PostEvent(#KeyBoard_Event)
EndIf
Default
PostEvent(#KeyBoard_Event)
EndSelect
Else ; key up
Select wParam
Case #VK_SHIFT : shift = 0
Case #VK_CONTROL : ctrl = 0
Case #VK_MENU : alt = 0
EndSelect
EndIf
EndIf
ProcedureReturn CallNextHookEx_(hHookProc,nCode,wParam,lParam)
EndProcedure
If OpenWindow(#WinMain,0,0,400,200,"keyboard hook",#PB_Window_ScreenCentered | #PB_Window_SystemMenu)
; create the hook
hHookProc = SetWindowsHookEx_(#WH_KEYBOARD,@KeyboardHook(),#Null,GetCurrentThreadId_())
TextGadget(#PB_Any,10,5,100,20,"string_1",#PB_String_ReadOnly)
StringGadget(0,10,25,100,25,"")
TextGadget(#PB_Any,10,60,100,20,"string_2",#PB_String_ReadOnly)
StringGadget(1,10,80,100,25,"")
ButtonGadget(2,10,140,100,30,"clear")
TextGadget(#PB_Any,210,5,100,20,"editor",#PB_String_ReadOnly)
EditorGadget(3,210,25,150,150)
SetActiveGadget(0)
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
Quit = #True
Case #PB_Event_Gadget
Select EventGadget()
Case 0 :
Case 1 :
Case 2 ;clear
SetGadgetText(0,"")
SetGadgetText(1,"")
SetGadgetText(3,"") : SetActiveGadget(0)
Case 3 :
EndSelect
Case #KeyBoard_Event ; custom event
Debug "virtual key code = " + vKeyCode +
" | scan code = " + scanCode +
" | shift = " + shiftState +
" | Alt = " + altKeyState +
" | ctrl = " + ctrlKeyState
; perform an action if 'return' key was pressed while
; the gadget of your choice had focus
If vKeyCode = #VK_RETURN
Select GetActiveGadget()
Case 0 : SetActiveGadget(1)
Case 1 : SetActiveGadget(3)
Case 3 : If shiftState : SetActiveGadget(0) : EndIf
EndSelect
ElseIf vKeyCode = #VK_TAB
If GetActiveGadget() = 2 :SetActiveGadget(3) : EndIf
EndIf
EndSelect
Until Quit = #True
; remove hook
UnhookWindowsHookEx_(hHookProc)
EndIf