Sign in to follow this  
Followers 0
johnnyzero

AutoPopKeyboard - exclude windows by class

1 post in this topic

#1 ·  Posted (edited)

I came across this existing AutoIT code that works pretty well for what I need: automatically pop up the oncreen keyboard as needed for text input when using "desktop mode" programs on a Windows tablet.

Pretty clever: it detects when the cursor changes to an i-beam and assumes you've clicked on a text input field.

;#RequireAdmin
#include <WinAPI.au3>
#include <GUIConstants.au3>
#include <Constants.au3>
#Include <WinAPIEx.au3>
;#include <SendMessage.au3>
#include <TrayConstants.au3>

Opt("TrayOnEventMode", 1) ; Use event trapping for tray menu
Opt("TrayMenuMode", 3) ; Default tray menu items will not be shown.

$iTrayIBeam = TrayCreateItem("Toggle Cursor I-Beam")
TrayItemSetOnEvent(-1, "ToggleIBeam")

$iTrayDebug = TrayCreateItem("Debug Tooltips")
TrayItemSetOnEvent(-1, "DebugMode")

TrayCreateItem("") ; Create a separator line.

TrayCreateItem("Exit")
TrayItemSetOnEvent(-1, "OnTrayMenuExit")

TraySetState()
TraySetClick(16)

; Set left click
TraySetOnEvent($TRAY_EVENT_PRIMARYUP, "OnTrayIconClick")
TraySetIcon("Resources\Enabled.ico")


Global $title,$lastTitle,$cursorBlinking, $editDetected, $visible, $PID, $debugMode, $Address, $useIBeam
Global $triggerMethod, $lastTriggerMethod, $debugMsg, $lastDebugMsg

Global $enabled = True

Global $myError = ObjEvent("AutoIt.Error","ErrorTrapper")    ; Initialize COM error handler - used for the Excel hack

Global Const $TRIGGER_EXCEL = "XLMAIN"
Global const $TRIGGER_BROWSER = "BROWSER"
Global Const $TRIGGER_CURSORBLINKING = "CB"
Global Const $TRIGGER_IBEAM = "IBEAM"

While 1
    ;
    Sleep(100)


    If  RegRead('HKLM\SYSTEM\CurrentControlSet\Control\PriorityControl', 'ConvertibleSlateMode') = 0 And $enabled Then
        $title = WinGetTitle('[ACTIVE]','')
        $hwnd=WinGetHandle('[ACTIVE]','')
        $className = _WinAPI_GetClassName($hwnd)

        $cursorBlinking=False
        $editDetected=False

        ;Check if we're using a browser as these are sandboxed.... Also Excel doesn't seem to be coming through
        local $browser=""
        Switch $className
            Case "Chrome_WidgetWin_1"
                $browser="Chrome"
            Case "MozillaWindowClass"
                $browser="FireFox"
            Case "IEFrame"
                $browser="IE"
            Case "XLMain"
                ;Kludge...!!! Seems an error is thrown if in edit mode, so let's use that to our advantage! Could stop working at any time...
                local $excelObj = ObjGet('','Excel.Application')
                if @error  and $visible Then ;bit flaky so had to do this to prevent keyboard bouncing up and down
                    $editDetected =True
                Else

                    If IsObj($excelObj) Then
                        local $excelActiveCell = $excelObj.ActiveCell.Address
                        if @error then $editDetected = True
                        if DebugMode then ShowDebugMessage("Excel - " & $title & ": " & $excelActiveCell,True)
                        $triggerMethod = $TRIGGER_EXCEL
                    Elseif $debugMode Then
                        ShowDebugMessage("Could not get Excel object")
                    EndIf
                EndIf
        EndSwitch

        ;If we're not already in Edit Mode, check if we've got anything in the browser title to indicate edit mode
        if $browser<>"" and not $editDetected Then
            $className=$browser
            if StringInStr($title," [input]") then
                $editDetected=True
                $triggerMethod= $TRIGGER_BROWSER
            EndIf
        EndIf

        ;If we still haven't got anything, look for a 'blinking cursor'
        if not $editDetected Then
            $Info = _WinAPI_GetGUIThreadInfo(_WinAPI_GetWindowThreadProcessId($hwnd, $PID))
            If Not @error Then $editDetected = ($info[0] = 1)
            $cursorBlinking=$editDetected
            if $editDetected then $triggerMethod = $TRIGGER_CURSORBLINKING
        EndIf

        ;If all else fails, IBeam cursor...
        if not $editDetected and $useIBeam Then
            $cursor = MouseGetCursor()
            $editDetected = ($cursor =5) ; 5 is for an I-Beam cursor
        EndIf

        if $debugMode then
            $debugMsg="Variables - Keyboard: " & $visible & ", Trigger: " & $triggerMethod & ", Last Trigger: " & $lastTriggerMethod & ", EditDetected: " & $editDetected
            if $debugMsg <> $lastDebugMsg then ShowDebugMessage ($debugMsg)
            $lastDebugMsg=$debugMsg
        EndIf

        If $editDetected and (not $visible or $title <> $lastTitle) Then
            $keyboard = ShellExecute('"C:\Program Files\Common Files\microsoft shared\ink\TabTip.exe"') ;Can we replace this with a PostMessage???
            $visible = True

        ElseIf not $editDetected and $visible Then
            HideKeyboard()
            $visible = False
        EndIf
        $lastTitle = $title
        $lastTriggerMethod = $triggerMethod
    EndIf

WEnd


Func OnTrayMenuExit()
    Exit
EndFunc

;Toggle Enabled/Disabled mode
Func OnTrayIconClick()
    if $enabled Then
        TraySetIcon("Resources\Disabled.ico")
        $enabled=False
    Else
        TraySetIcon("Resources\Enabled.ico")
        $enabled=True
    EndIf
    ;ConsoleWrite("Single" & @CRLF)
EndFunc


Func ErrorTrapper()
    Local $err = $myError.number
    If $err = 0 Then $err = -1
    SetError($err)  ; to check for after this function returns
    if $debugMode then ShowDebugMessage ("[ErrorTrapper] Error Code: " & $err)
Endfunc

;Tray Menu - Show us what's happening...
Func DebugMode()
    $debugMode = not $debugMode
    SetTrayMenuCheck($iTrayDebug, $debugMode)
EndFunc

;Tray Menu -Toggle use of I-Beam as a fallback edit mode check method
Func ToggleIBeam()
    $useIBeam = not $useIBeam
    SetTrayMenuCheck($iTrayIBeam, $useIBeam)
EndFunc

Func SetTrayMenuCheck($controlID, $checked)
    Local $state
    If $checked then
        $state = $TRAY_CHECKED
    Else
        $state = $TRAY_UNCHECKED
    EndIf
    TrayItemSetState($controlID, $state)
EndFunc

Func HideKeyboard()
    Local Const $WM_MESSAGE = 0x112
    Local Const $WM_WPARAM  = 0xF060
    ;Local Const $AUTOIT_CLASS = "IPTip_Main_Window"
    Local $hClass = _WinAPI_FindWindow("IPTip_Main_Window","") ;WinGetHandle("[CLASS:IPTip_Main_Window]")
    if @error then
        If $debugMode Then ShowDebugMessage("[HideKeyboard] Error occured in getting keyboard handle");$hClass = GetKBHandle()
    Else
        If $debugMode Then ShowDebugMessage("[HideKeyboard] Handle " & $hClass)
    EndIf
    if not @error Then
        if $debugMode then ShowDebugMessage ("[HideKeyboard] Hidekeyboard called")
        Local $dll = DllOpen("User32.dll")
        Local $res=DllCall($dll, "bool", "PostMessage", "HWND", $hClass,"UINT",$WM_MESSAGE, "WPARAM",$WM_WPARAM,"LPARAM","0")
        if @error and $debugMode then ShowDebugMessage("[HideKeyboard] Error occured in PostMessage")
    Else
        if $debugMode then ToolTip("[HideKeyboard] error occured in getting handle")
    EndIf
EndFunc


Func ShowDebugMessage($msg, $toolTipOnly = False)
    ToolTip ($msg,MouseGetPos(0)+20, MouseGetPos(1)-50)
    if not $toolTipOnly then ConsoleWrite($msg & @CRLF)
EndFunc

For the most part, it works perfectly. However, if you click on a text field in a Metro app (or the Search field on the Start screen), the keyboard pops up as it should, but it gets stuck and then you can't get rid of it. Metro already has the ability to automatically popup/hide the onscreen keyboard as needed, and I assume my script is conflicting with that.

So I think I need to use WinActive or some other function to exclude the following window classes:

Windows.UI.Core.CoreWindow                (Metro apps)

ImmersiveLauncher                                 (the Start screen)

 

I'm not a programmer (just a bit of experience with AutoIT and Autohotkey), so any help or advice will be much appreciated.

Please note: by design, this script will only pop up the keyboard if it detects that you're in "tablet mode" by checking for a 0 dword value of the following registry key:

HKLMSYSTEMCurrentControlSetControlPriorityControlConvertibleSlateMode

So if you want to test the script on a non-tablet Windows 8 device, you'll need to temporarily set that value in your registry. Or, you could remove that part of the code.

Thanks!

Edited by johnnyzero

Share this post


Link to post
Share on other sites



Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0