Page 1 of 2

Key presses

Posted: Sun Apr 22, 2007 8:00 pm
by Matt
I need a way to get which key is pressed even when a window is not in focus (not using a screen though). I need this because I am designing an application for windows that allows you to press keys to execute certain commands, which you will be able to do when other windows are in focus. (for example, pressing F10 to hide all windows, and then again to show them)

Thanks,
Matt

Posted: Sun Apr 22, 2007 8:43 pm
by ts-soft
You can use the RegisterHotKey_ API

Posted: Mon Apr 23, 2007 12:04 am
by Kaeru Gaman
also GetAsyncKeystate_ or GetKeyboardState_ may help....

Posted: Mon Apr 23, 2007 12:24 am
by Matt
How do I use the GetKeyboardState function

Posted: Mon Apr 23, 2007 1:00 am
by Kaeru Gaman
it returns the complete keyboardstate to a 256byte buffer you pass byRef.

never did it before, but it should work this way:

Code: Select all

Dim KeyBuf.b(255)
GetKeyboardState_(@KeyBuf())
...or without the @.. not sure... the pointer to the array anyways...

the buffer then holds the state of every key,
the index should be the virtual keycode,
eventually it's the same as the #PB_Key_ Constants...

Posted: Mon Apr 23, 2007 1:02 am
by akj
Matt,

The program below uses RegisterHotKey_() and is similar to the code you need.

It is not exactly what you want, as it hides/unhides only windows visible at the moment of the program starting, whereas you really need to update the list of windows every time the F11 key is pressed in case some new windows have been created or destroyed in the meantime. This is actually harder to do than it seems, as there is a danger of losing track of windows currently hidden by the program but which were originally visible.

Code: Select all

; Hide Windows  AKJ  23-Apr-07
; Enable F10 key to hide/unhide visible windows

#program$ = "Hide Windows"
#version$ = "1.0"

EnableExplicit

Declare   AlreadyRunning()
Declare   BuildWindowList(winmain)
Declare.s WindowTitle(h)
Declare   FlipWindows()

Enumeration
  #winMain
  #lstWindows
  #butFlip
  #butExit
EndEnumeration

Structure windowdata
  handle.l
  title$
EndStructure

Global NewList window.windowdata() ; Window handles and titles
Global gHidden = #False ; True iff visible windows are hidden

AlreadyRunning()

; GUI interface
Define.l gap, butw, buth, lstw, lsth, winw, winh, flags
gap = 20
butw = 120: buth = 30
lstw = butw*2+gap: lsth=200
winw = butw*2+gap*3: winh = lsth+buth+gap*3
flags = #PB_Window_SystemMenu | #PB_Window_MinimizeGadget | #PB_Window_ScreenCentered
OpenWindow(#winMain, 0, 0, winw, winh, #Program$+"   v"+#Version$, flags)
CreateGadgetList(WindowID(#winMain))
flags = #PB_ListIcon_GridLines | #PB_ListIcon_FullRowSelect
ListIconGadget(#lstWindows, gap, gap, lstw, lsth, "Handle", 56, flags)
AddGadgetColumn(#lstWindows, 1, "Visible Window Title", lstw-56-4) ; The -4 is a kludge
ButtonGadget(#butFlip, gap, lsth+gap*2, butw, buth, "F l i p    (F10 key)")
ButtonGadget(#butExit, butw+gap*2, lsth+gap*2, butw, buth, "E x i t")

; Initialise
StickyWindow(#winMain, #True)
BuildWindowList(#winMain)
ForEach window()
  With window()
    AddGadgetItem(#lstWindows, -1, "$"+Hex(\handle)+Chr(10)+\title$)
  EndWith
Next window()
RegisterHotKey_(#Null, 0, 0, #VK_F10) ; Register F10 as a hot key

; Event loop  15-Feb-06
Define.l done=#False, ev, reply
Repeat
  ev = WaitWindowEvent()
  If ev=#PB_Event_Menu: ev=#PB_Event_Gadget: EndIf ; To map shortcut keys to gadgets
  Select ev
  Case #WM_HOTKEY ; = $312 = 786.
    Debug "Hotkey pressed"
    FlipWindows()
    StickyWindow(#winMain, #True)
  Case #PB_Event_Gadget
    Select EventGadget()
    Case #butFlip
      FlipWindows()
      StickyWindow(#winMain, #True)
    Case #butExit
      done=#True
    EndSelect
  Case #PB_Event_CloseWindow
    done=#True
  EndSelect
Until done

; Windup
If gHidden: FlipWindows(): EndIf ; Ensure windows are visible
UnregisterHotKey_(#Null, 0)
CloseWindow(#winMain)
End

Procedure AlreadyRunning() ; AKJ  30-Jan-07
; Ensure the current program is not already running
; Terminate this process if it is
; Uses #Program$
Protected app, msg$
app=CreateSemaphore_(0,0,1,"AKJ "+#Program$)
If app<>0 And GetLastError_()=#ERROR_ALREADY_EXISTS
  CloseHandle_(app) ; This line can be omitted
  msg$="The "+#Program$+" program is already running."+#CRLF$+#CRLF$
  msg$+"This process will terminate."
  MessageRequester(#Program$+" Error", msg$, #MB_ICONERROR)
  End
EndIf 
EndProcedure

Procedure BuildWindowList(winmain) ; AKJ  22-Apr-07
; Build list of handles of visible top-level [owned] windows
; Exclude the window given in the parameter
Protected hWin, h, cn$, title$
ClearList(window())
hWin = WindowID(winmain)
; GetDesktopWindow_() handle differs from the window entitled "Program Manager"
h = GetWindow_(GetDesktopWindow_(), #GW_CHILD) ; Handle
While h
  If h<>hWin ; If not window for this program
    If IsWindowVisible_(h)=#True
      If IsIconic_(h)=0 ; If not minimised
        cn$ = Space(33) : GetClassName_(h, cn$, 32) ; Window class name
        If Left(cn$,8)<>"CursorXP" And FindString(LCase(cn$),"tooltips_class",1)=0 ; Ignore mouse cursor and tooltips
          If cn$<>"Shell_TrayWnd" ; Ignore system tray and taskbar
            title$ = WindowTitle(h)
            If title$<>"Program Manager" ; Ignore Desktop
              ; Remember the window
              AddElement(window())
              window()\handle = h
              window()\title$ = title$
            EndIf ; Desktop
          EndIf ; System tray
        EndIf ; Mouse cursor, tooltips
      EndIf ; Minimised
    EndIf ; Visible
  EndIf ; winMain
  h = GetWindow_(h, #GW_HWNDNEXT) ; Handle
Wend
EndProcedure

Procedure.s WindowTitle(h) ; AKJ  07-Mar-06
; Return the title (caption) for the window with handle h
Protected lth, title$
If h=0: ProcedureReturn "": EndIf
lth=GetWindowTextLength_(h)+1
title$=Space(lth)
GetWindowText_(h, title$, lth)
ProcedureReturn title$
EndProcedure

Procedure FlipWindows() ; AKJ  22-Apr-07
; Toggle the hidden state for all originally visible windows
gHidden = ~gHidden ; Toggle state
ForEach window()
  If gHidden ; If to be hidden
    ShowWindow_(window()\handle, #SW_HIDE)
  Else
    ShowWindow_(window()\handle, #SW_SHOW)
  EndIf
Next window()
EndProcedure

Posted: Mon Apr 23, 2007 1:15 am
by Matt
Thanks guys, really what I am looking for is something like this:
If (currentKeyDown() == theF10Key)
<do this>
endif
so something will return whatever key is down... if this makes anysense, because i have the user select their own key to use and using ini files read what key it was and then compare it.

Posted: Mon Apr 23, 2007 1:19 am
by Kaeru Gaman
sure you could test

Code: Select all

If GetAsyncKeyState_(#VK_F10)
you could also setup a table first and let the user chose the key, as you can replace the constant by a variable.
for that, getting the whole table maybe oversized....

PS:
note that if you just test it for #True, it will return #True repetively as long the key is pressed.
you should stop testing while execution or work with a flag,
or find out wich bit in the returnvalue is for "first press"...

Posted: Mon Apr 23, 2007 1:22 am
by Matt
Yeah, thats why I wasn't sure of using that, but I guess I'll go with that.
Is there like a reverse of that function that would return #VK_F10

Posted: Mon Apr 23, 2007 1:24 am
by akj
Matt,

Continually polling all key presses as you are suggesting will add a lot of CPU overhead to your system. If you think about it carefully you should discover that hot keys really will do what you want and they can be selected based on the contents of your .INI file.

Posted: Mon Apr 23, 2007 1:26 am
by Matt
alright, I'll look into those
thanks

Posted: Mon Apr 23, 2007 1:36 am
by Kaeru Gaman
Continually polling all key presses as you are suggesting will add a lot of CPU overhead to your system.
I think Matt is able to implement a Delay the correct way, isn't he?

give that a test and monitor the CPU usage:

Code: Select all

Repeat
  Delay(200)
Until GetAsyncKeyState_(#VK_F10)
sure, the hotkey thing will also work, the test will be performed by the OS.
CPU will also be used for that, it's only more difficult to monitor... ;)

Posted: Mon Apr 23, 2007 1:45 am
by Matt
I'll be using the getasynckeystate i guess than

thanks guys

Posted: Mon Apr 23, 2007 2:17 am
by Matt
How do I check if the key was released, instead of being held down?

Posted: Mon Apr 23, 2007 2:39 am
by Kaeru Gaman
well, this is when it is #False again, so for that you need a flag to test #True before.

btw: firstpress is -32767, hold is -32768

if you really just want to wait for a key, perhaps an unusual wating loop is quite a good suggestion:

Code: Select all

Repeat
  Repeat
    Delay(200)
  Until GetAsyncKeyState_(#VK_F10)
  Debug "pressed"
  While GetAsyncKeyState_(#VK_F10)
    Delay(100)
  Wend
  Debug "released"
Until GetAsyncKeyState_(#VK_F11)
I realized that the debug-window seems not to be updated immediately...
strange...

PS:
of course, that is nothing one would do in a normal eventloop,
but when you're really only wating for one special key, it is ok.