Announcement

Collapse
No announcement yet.

Capturing mouse and keyboard messages from a graphic Window in PBCC 4.04

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Capturing mouse and keyboard messages from a graphic Window in PBCC 4.04

    In PBCC 4.03 and previous versions of PBCC 4, you could subclass the graphic window and capture these messages, there was a change in PBCC 4.04, where you must subclass the static control on the graphic window to capture these messages. A graphic window is a dialog box with one static control on it, the static control is what you actually do the drawing on. All you need to do is get the handle of this static control with hStatic = GetWindow(hGraphicWindow, %GW_CHILD) and subclass hStatic instead of hGraphicWindow. I posted an example of how to do this at http://www.powerbasic.com/support/pb...ad.php?t=35336.
    Sincerely,

    Steve Rossell
    PowerBASIC Staff


  • #2
    A simplified version of the example program is given below. The keyboard gets detected only after you click the window. If the first thing you do is press keys they will not be detected.

    This morning I asked PB support about this. Here’s their reply to the first – I assume it’s OK to quote support email:
    The static control on the graphic window must have the keyboard focus before it can receive a key board message. The easiest solution would be to subclass the dialog of the graphic window and catch a WM_SETFOCUS message and set the focus to the static control, this way the dialog will never receive the keyboard focus, only the static control will.
    Code:
    %WM_NCHITTEST = &H084
    %GW_CHILD = 5
    %GWL_WNDPROC = -4
    %WM_MOUSEMOVE = &H200
    %WM_LBUTTONDOWN = &H201
    %WM_RBUTTONDOWN = &H204
    %WM_KEYDOWN = &H100
    
    Type POINTAPI
      x As Long
      y As Long
    End Type
    
    Declare Function GetWindow Lib "USER32.DLL" Alias "GetWindow" (ByVal hWnd As Dword, ByVal wCmd As Dword) As Long
    Declare Function SetWindowLong Lib "USER32.DLL" Alias "SetWindowLongA" (ByVal hWnd As Dword, ByVal nIndex As Long, ByVal lNewLong As Long) As Long
    Declare Function CallWindowProc Lib "USER32.DLL" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Dword, ByVal hWnd As Dword, ByVal uMsg As Dword, ByVal wParam As Dword, ByVal lParam As Long) As Long
    '================================================================
    
    Global GraphicWindowChildOldProc As Long
    Global finished As Long
    '----------------------------------------------------------------
    
    ' Called whenever keyboard or mouse activity is detected in the graphics window.
    Function GraphicWindowChildNewProc(ByVal hWnd As Dword, ByVal wMsg As Dword, ByVal wParam As Dword, ByVal lParam As Long) As Long
      Local p As POINTAPI
    
      Select Case wMsg
         Case %WM_NCHITTEST
         Case %WM_MOUSEMOVE
           p.x = Lo(Word, lParam)  ' Current mouse X Position in the graphic window.
           p.y = Hi(Word, lParam)  ' Current mouse Y Position in the graphic window.
           Print "Mouse at " + Format$(Lo(Word, lParam)) + "," + Format$(Hi(Word, lParam))
         Case %WM_LBUTTONDOWN
           Print "Left -click at " + Format$(Lo(Word,lParam)) + "," + Format$(Hi(Word,lParam))
         Case %WM_RBUTTONDOWN
           Print "Right-click at " + Format$(Lo(Word,lParam)) + "," + Format$(Hi(Word,lParam))
         Case %WM_KEYDOWN
           If Chr$(WPARAM) = $Esc Then
             finished = 1
           Else
             Print Chr$(wparam)
           End If
      End Select
    
      Function = CallWindowProc(GraphicWindowChildOldProc, hWnd, wMsg, wParam, lParam)
    End Function
    '----------------------------------------------------------------
    
    Function PBMain
      Local hGraphicWindow As Dword
      Local hStatic As Dword
      Local w As Long
    
      Graphic Window "Graphic Window SubClassing Example", 0, 0, 320, 240 To hGraphicWindow
    
      ' Retrieve handle to static control (the graphic) on the graphic window (dialog box).
      hStatic = GetWindow(hGraphicWindow, %GW_CHILD)
    
      ' Subclass the graphic window; any keyboard or mouse activity in the window
      ' will cause the GraphicWindowChildNewProc function to be called.
      GraphicWindowChildOldProc = SetWindowLong(hStatic, %GWL_WNDPROC, CodePtr(GraphicWindowChildNewProc))
    
      Console Set Loc 320,0
    
      Graphic Attach hGraphicWindow, 0, ReDraw
      Graphic Font "Times New Roman", 14, 0
      Graphic Print "Move mouse, click, type."
      Graphic Print "Press Esc to quit"
      Graphic ReDraw
    
      ' Loop until Esc is pressed on the graphic window.
      While finished = 0
        Incr w
        ' Every third iteration give back one time slice
        ' to Windows to prevent 100% CPU utilization.
        If w Mod 3 = 0 Then
          Sleep 0
        End If
      Wend
    End Function
    Last edited by Mark Hunter; 7 Nov 2007, 11:28 AM. Reason: corrected garbled text
    Algorithms - interesting mathematical techniques with program code included.

    Comment


    • #3
      Mark--

      For a particular window (any window) to receive keyboard input, it must have the focus. That window can receive the focus programatically, or by physically "clicking" on it. That choice is entirely up to you, but it is a prerequisite. This isn't likely to change in the future.

      Regards,

      Bob Zale
      PowerBASIC Inc.

      Comment


      • #4
        1. I do not believe the question was about anticipated changes to Windows; rather, it was a question if there are any anticipated changes to the compiler to not require the use of subclassing to capture mouse/keyboard notifications.

        Of course this would get into "vaporware" so no answer should be anticipated.

        2. Since I don't have CC "dot=oh-four" I can't test Steve's examples, but the SDK doc says pretty clearly...
        . Although static controls are child windows, they cannot be selected. Therefore, they cannot receive the keyboard focus and cannot have a keyboard interface..
        I'm just wondering what the application requirements are..

        2. (Speculation, but good speculation ) ....if you DO need a 'real' keyboard interface on a graphic window, I can't see anything to stop you from adding a child window to that graphic window of any class... 'edit' comes to mind to take in keystrokes and get input from the user. CreateWindowEx works just fine with PB/CC.

        I don't see a "window procedure interface" in the GRAPHIC WINDOW but if subclassing works for the static control no reason it shouldn't work for an explicitly-added edit control.

        Then again, when you start gettng into controls and such, the PB/Windows compiler might be a better choice of tool. The GRAPHICS commands appear identical, so that part of the program would be pretty much the same.



        MCM
        Michael Mattias
        Tal Systems (retired)
        Port Washington WI USA
        [email protected]
        http://www.talsystems.com

        Comment


        • #5
          [beginning text deleted]

          If you kept the console I suppose you could use some sort of set focus command.

          Note that you can’t use Graphic Print within the "callback" function, you need to set a variable to what you want to print, then print it later (say in PBMain), within a loop that checks that variable.
          Last edited by Mark Hunter; 8 Nov 2007, 06:07 PM. Reason: correction
          Algorithms - interesting mathematical techniques with program code included.

          Comment


          • #6
            More on the 4.03/4.04 difference ...

            Say you have several graphic windows on the screen at once. And each window has “buttons” on it – really just areas that are checked for mouse-click’s – and each button has a shortcut key.

            Naturally you want all these keys to work in all the windows, regardless which has the focus.

            In both 4.03 and 4.04 if you process the mouse and keyboard in the a subclassing procedure for a window, the window must have the focus (from clicking) before the keys will work.

            However, in 4.03 you could subclass a window twice, once for the mouse and once for the keyboard, in different ways. In what follows win1 and win2 are PB handles for two graphic windows.
            GW1 = GetWindow(win1, %GW_CHILD)
            GWold1 = SetWindowLong(GW1, %GWL_WNDPROC, CodePtr(GWnew1))
            GW2 = GetWindow(win2, %GW_CHILD)
            GWold2 = SetWindowLong(GW2, %GWL_WNDPROC, CodePtr(GWnew2))

            GWold1k = SetWindowLong(win1, %GWL_WNDPROC, CodePtr(GWnew_k))
            GWold2k = SetWindowLong(win2, %GWL_WNDPROC, CodePtr(GWnew_k))

            where GWnew1 and GWnew2 process mouse-clicks, GWnew_k processes the keyboard.

            This works in 4.03 (when it probably shouldn't) and not in 4.04.
            Last edited by Mark Hunter; 8 Nov 2007, 06:13 PM.
            Algorithms - interesting mathematical techniques with program code included.

            Comment


            • #7
              The following code displays two graphic windows side by side, sans console. When you click on either window you will see printed where, and which mouse button. When you type letters they will be displayed in that window.

              It works as it ought to, in 4.04.

              It doesn’t work in the earlier version, 4.03, though something like it can be made to work with some extra code.
              Code:
              #Console Off
              '----------------------------------------------------------------
              
              ' From WinAPI32 ...
              %WM_NCHITTEST = &H084
              %HTTRANSPARENT = -1
              %HTCLIENT = 1
              %GW_CHILD = 5
              %GWL_WNDPROC = -4
              %WM_LBUTTONDOWN = &H201
              %WM_RBUTTONDOWN = &H204
              %WM_KEYDOWN = &H100
              Declare Function GetWindow Lib "USER32.DLL" Alias "GetWindow" (ByVal hWnd As Dword, ByVal wCmd As Dword) As Long
              Declare Function SetWindowLong Lib "USER32.DLL" Alias "SetWindowLongA" (ByVal hWnd As Dword, ByVal nIndex As Long, ByVal lNewLong As Long) As Long
              Declare Function CallWindowProc Lib "USER32.DLL" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Dword, ByVal hWnd As Dword, ByVal uMsg As Dword, ByVal wParam As Dword, ByVal lParam As Long) As Long
              '=================================
              			===========================
              
              Global GraphicWindowChildOldProc1 As Dword
              Global GraphicWindowChildOldProc2 As Dword
              Global GWold1k As Dword
              Global GWold2k As Dword
              
              Global whichWindow As Long     'in window 1 or 2
              Global ky As Long              'keycode
              Global whichButton As Long     'button left or right (1 or 2)
              Global xmouse, ymouse As Long  'mouse-click position
              
              ' Zero/Non-zero  ky  and  whichButton  are also used as flags for
              ' keyboard and mouse activity, respectively.
              '----------------------------------------------------------------
              
              ' Called whenever keyboard or mouse activity is detected in the FIRST window.
              Function GraphicWindowChildNewProc1(ByVal hWnd As Dword, ByVal wMsg As Dword, ByVal wParam As Dword, ByVal lParam As Long) As Long
                Local result As Dword
              
                result = CallWindowProc(GraphicWindowChildOldProc1, hWnd, wMsg, wParam, lParam)
                Select Case wMsg
                   Case %WM_NCHITTEST
                     If result = %HTTRANSPARENT Then result = %HTCLIENT
                   Case %WM_LBUTTONDOWN
                     xmouse = Lo(Word, lParam) :ymouse = Hi(Word, lParam)
                     whichWindow = 1
                     whichButton = 1
                   Case %WM_RBUTTONDOWN
                     xmouse = Lo(Word, lParam) :ymouse = Hi(Word, lParam)
                     whichWindow = 1
                     whichButton = 2
                   Case %WM_KEYDOWN
                     ky = wParam
                     whichWindow = 1
                End Select
              
                Function = result
              End Function
              '----------------------------------------------------------------
              
              ' Called whenever keyboard or mouse activity is detected in the SECOND window.
              Function GraphicWindowChildNewProc2(ByVal hWnd As Dword, ByVal wMsg As Dword, ByVal wParam As Dword, ByVal lParam As Long) As Long
                Local result As Dword
              
                result = CallWindowProc(GraphicWindowChildOldProc2, hWnd, wMsg, wParam, lParam)
              
                Select Case wMsg
                   Case %WM_NCHITTEST
                     If result = %HTTRANSPARENT Then result = %HTCLIENT
                   Case %WM_LBUTTONDOWN
                     xmouse = Lo(Word, lParam) :ymouse = Hi(Word, lParam)
                     whichWindow = 2
                     whichButton = 1
                   Case %WM_RBUTTONDOWN
                     xmouse = Lo(Word, lParam) :ymouse = Hi(Word, lParam)
                     whichWindow = 2
                     whichButton = 2
                   Case %WM_KEYDOWN
                     ky = wParam
                     whichWindow = 2
                End Select
              
                Function = result
              End Function
              '----------------------------------------------------------------
              
              Function PBMain
                Local hGraphicWindow1 As Dword
                Local hGraphicWindow2 As Dword
                Local hStatic As Dword
                Local w As Long
              
                ' Create two graphic windows side by side
                Graphic Window "",   0, 0, 398, 400 To hGraphicWindow1
                Graphic Window "", 400, 0, 398, 400 To hGraphicWindow2
              
                ' Subclass
                hStatic = GetWindow(hGraphicWindow1, %GW_CHILD)
                GraphicWindowChildOldProc1 = SetWindowLong(hStatic, %GWL_WNDPROC, CodePtr(GraphicWindowChildNewProc1))
                hStatic = GetWindow(hGraphicWindow2, %GW_CHILD)
                GraphicWindowChildOldProc2 = SetWindowLong(hStatic, %GWL_WNDPROC, CodePtr(GraphicWindowChildNewProc2))
              
                ' Print instructions on the windows
                Graphic Attach hGraphicWindow1, 0, ReDraw
                Graphic Font "Times New Roman", 12, 0
                Graphic Print " Click or type,  press Esc to quit."
                Graphic ReDraw
                Graphic Attach hGraphicWindow2, 0, ReDraw
                Graphic Font "Times New Roman", 12, 0
                Graphic Print " Click or type,  press Esc to quit."
                Graphic ReDraw
              
               ' Start with the second window having the focus
                Graphic Set Focus
              
                ' Continually check  ky  for keyboard activity and
                ' whichwindow  for mouse activity.  Print on the
                ' window that was last clicked.  (Or
                Do
                  Incr w
                  If w Mod 3 = 0 Then
                    Sleep 0                          ' give time slice to Windows
                  End If
              
                  If ky Then
                    If whichWindow = 1 Then
                     Graphic Attach hGraphicWindow1, 0, ReDraw
                    Else
                     Graphic Attach hGraphicWindow2, 0, ReDraw
                    End If
              
                    If     ky = Asc($Esc) Then
                     Exit Do
                    ElseIf ky = Asc($Cr) Then
                     Graphic Print "[CR]"
                    ElseIf ky = Asc($Spc) Then
                     Graphic Print "[space]";
                    Else
                     Graphic Print Chr$(ky);
                    End If
                    Graphic ReDraw
                    ky = 0                            'reset for next time
                  End If
              
                  If whichButton Then
                    If whichWindow = 1 Then
                     Graphic Attach hGraphicWindow1, 0, ReDraw
                    Else
                     Graphic Attach hGraphicWindow2, 0, ReDraw
                    End If
              
                    Graphic Print " - Button"; Str$(whichbutton);
                    Graphic Print "  at "; Str$(xmouse); ","; Str$(ymouse)
                    Graphic ReDraw
                    whichButton = 0                   'reset for next time
                  End If
                Loop
              End Function
              Algorithms - interesting mathematical techniques with program code included.

              Comment

              Working...
              X