Sign in to follow this  
Followers 0
Christopher Blue

Setting Mouse Boundries and Window Borders

31 posts in this topic

#1 ·  Posted (edited)

I am trying to run Warcraft 3 in Windowed mode. However it presented two problems:

Problem 1. The game window has the standard Windows XP borders and also, it doesn't set itself to my desktop resolution (1024x768). Since the taskbar is larger than 12 pixels, I could not simply increase its size and then move it a little off-screen to compensate although I tried. However, I was told of WinCheat which allowed me to manually remove the Window borders at runtime. While it *does* present a solution to my problem, I am curious is AutoIt could either (a)talk to WinCheat and therefore automate it, or (b)remove the borders itself thus eliminating the need for WinCheat. I was unsuccessful at finding anything about border removal when using the stable AutoIt version but I am curious: Is there a DLL I might call (thanks to the new DLLCall) that would allow me to do what WinCheat does?

Problem 2. I have dual monitors and anyone who has played Warcraft 3 knows that it involves alot of mouse scrolling. However, in Windowed mode Warcraft 3 doesn't trap the mouse within it's borders and thus the mouse can fly right outside the game window which is extremely disorienting and basically makes War3 unplayable. I have tried making my own mouse trapping script but it is slow and doesn't work well:

#cs

Algorithm:
Get Active Window
Find Window 
Get Window Dimensions
Check Mouse Location against window dimensions
Move Mouse to closest valid coordinate

#ce

Opt("WinTitleMatchMode", 3)
Opt("MouseCoordMode", 0)

Global $window_name = "Warcraft III"
Global $shrink = 7
Global $titlebar = 26
Global $rest = 5
Global $hotkey1 = "{F1}"
Global $hotkey2 = "{F2}"
Global $Paused = 0
Global $window_info = WinGetPos($window_name)

HotKeySet($hotkey1, "TogglePause")
HotKeySet($hotkey2, "RecalculateWindow")


while (1)
    CheckBoundries()
    Sleep($rest)
WEnd

Func CheckBoundries()
    If (WinActive($window_name)) Then
        $mouse_info = MouseGetPos()

        ; Left
        If ($mouse_info[0] < $shrink) Then
            MouseMove($shrink + 1, $mouse_info[1], 0)
        EndIf        

        ; Left-Up
        If (($mouse_info[0] < $shrink) AND ($mouse_info[1] < $shrink + $titlebar)) Then
            MouseMove($shrink + 1, $shrink + $titlebar + 1, 0)
        EndIf

        ; Up
        If ($mouse_info[1] < $shrink + $titlebar) Then
            MouseMove($mouse_info[0], $shrink + $titlebar + 1, 0)
        EndIf

        ; Right-Up
        If (($mouse_info[0] > $window_info[2] - $shrink) AND ($mouse_info[1] < $shrink + $titlebar)) Then
            MouseMove($window_info[2] - $shrink - 1, $shrink + $titlebar + 1, 0)
        EndIf

        ; Right
        If ($mouse_info[0] > $window_info[2] - $shrink) Then
            MouseMove($window_info[2] - $shrink - 1, $mouse_info[1], 0)
        EndIf

        ; Right-Down
        If (($mouse_info[0] > $window_info[2] - $shrink) AND ($mouse_info[1] > $window_info[3] - $shrink)) Then
            MouseMove($window_info[2] - $shrink - 1, $window_info[3] - $shrink - 1, 0)    
        EndIf

        ; Down
        If ($mouse_info[1] > $window_info[3] - $shrink) Then
            MouseMove($mouse_info[0], $window_info[3] - $shrink - 1, 0)
        EndIf

        ; Left-Down
        If (($mouse_info[0] < $shrink) AND ($mouse_info[1] > $window_info[3] - $shrink)) Then
            MouseMove($shrink + 1, $window_info[3] - $shrink - 1, 0)    
        EndIf

    EndIf
EndFunc

Func TogglePause()
    $Paused = NOT $Paused
    While $Paused
        Sleep(100)
    WEnd
EndFunc

Func RecalculateWindow()
    $window_info = WinGetPos($window_name)
EndFunc

The mouse is trapped, but it jumps around alot and isn't fully responsive near the screen edges. Is there a way to either improve my script or perhaps, redefine the mouse boundries at a very low-level so Window's traps the mouse instead of my crappy script?

Edited by Christopher Blue

Share this post


Link to post
Share on other sites



Hm, WinCheat allows you to install it's source code files and it has some DLL's as well! I shall investigate them but it will take me awhile. I am still at a dead-end in regards to the mouse-boundries issue.

Share this post


Link to post
Share on other sites

I've just finishing changing the mouse/pixel commands so that in addition to the usual "screen" and "window" modes that are used for coordinate there is a "client" mode that uses coords relative to the window's client area (the bit inside the window frame). Will this make this problem go away?

Share this post


Link to post
Share on other sites

to DLLCalls

GetWindowLong

SetWindowLong

I can't remember how to remove styles from the result of GetWindowLong and apply it to SetWindowLong... I will research and try to answer before the more brainy (Valik, Jon) people get to it...

Lar.

<{POST_SNAPBACK}>

Use either BitNOT to remove it, or use BitAND to test to see if it exists and then if so, use BitXOR to remove it. In C, it looks like:

style = GetWindowLong();
if (style & StyleIWantToRemove)
    SetWindowLong(style ^ StyleIWantToRemove)

(It should go without saying that the code isn't valid, that its only concept code)

Share this post


Link to post
Share on other sites

Larry:

I will start reading about the Windows Classes Reference.

Jon:

That will help me simplify my code since I don't have to take the Window borders into consideration, but I suspect my code will still be as slow and jumpy as before and thus I am curious as to if there is a low-level solution to setting Mouse Boundries. I mean, if Windows can tell the mouse that 768 is the max Y and 1024 (2048 in my dual monitor situation) is the max X, perhaps there is a way to change those values and thus make the mouse run in a box of the programmer's choosing. It doesn't even have to be that specific for I am merely trying to get it to stay within the area of a client Window (Warcraft III's game screen).

Share this post


Link to post
Share on other sites

I am currently simplfying my mouse bounding script for when the window is bounded on three sides by the screen edges which is the case once I use WinCheat to take away the borders and then reposition the window in the upper left corner of my left-monitor. Since the borders are gone, I can easily resize War3 to 1024x768 using AutoIt.

I shall post the simplified mouse trap soon but I still believe it is just a bandaid until I find out something lower level.

Share this post


Link to post
Share on other sites

Sorry Larry, I used WinCheat since I wanted to quickly try my mouse script. I will go try your script right now and let you know what happens.

Share this post


Link to post
Share on other sites

Larry, your script took away WS_BORDER but still left WS_DLGFRAME and WS_THICKFRAME. All 3 must be removed in order to have a truly borderless window, at least according to WinCheat. I am not sure what variables correspond to those in AutoIt 3.

Share this post


Link to post
Share on other sites

#9 ·  Posted (edited)

example I wrote...

@Valik: My implementation of Bit commands work... is there a simpler syntax?

#include <GUIConstants.au3>

Global $GWL_STYLE = -16

$hWnd = WinGetHandle("AutoIt Forums")

$OldStyle = DLLCall( "user32.dll", "long", "GetWindowLong", "hwnd", $hWnd, "int", $GWL_STYLE )
If @error Then Exit
$OldStyle = $OldStyle[0]

$NewStyle = BitAND($OldStyle,BitNOT($WS_CAPTION) )

DLLCall( "user32.dll", "long", "SetWindowLong", "hwnd", $hWnd, "int", $GWL_STYLE, "long", $NewStyle )

Sleep(2000)

DLLCall( "user32.dll", "long", "SetWindowLong", "hwnd", $hWnd, "int", $GWL_STYLE, "long", $OldStyle )
WinSetState($hWnd,"",@SW_MINIMIZE)
WinSetState($hWnd,"",@SW_RESTORE)

<{POST_SNAPBACK}>

I prefer wrapping DllCall() functions in functions to make them more "natural" (and reusable). As far as the bitwise stuff goes, Shouldn't that be BitOR and not BitAND?

Edit: Never mind, BitAND is correct, my brain is frazzled; I've been looking at COM way too much this morning. Is what you have the functional equivalent to:

If BitAND($Style, $WS_CAPTION) Then $Style = BitXOR($Style, $WS_CAPTION)

Edit2: Ignore me, my brain is taking a nap for a little while. Playing with vectors of pointers and COM does that to me.

Edited by Valik

Share this post


Link to post
Share on other sites

Absolutely incredible Larry! Your code works perfectly. I have implemented your suggestions and rolled the resulting script into my hide/unhide script:

#include <GUIConstants.au3>

Global $window_name = "Warcraft III"
Global $hidden = 0
Global $GWL_STYLE = -16

HotKeySet("^q", "ToggleWindowHide")

;Remove the window borders
$hWnd = WinGetHandle($window_name)

$OldStyle = DLLCall( "user32.dll", "long", "GetWindowLong", "hwnd", $hWnd, "int", $GWL_STYLE )
If @error Then Exit
$OldStyle = $OldStyle[0]

$NewStyle = BitAND($OldStyle,BitNOT($WS_DLGFRAME) )
$NewStyle = BitAND($NewStyle,BitNOT($WS_THICKFRAME) )
$NewStyle = BitAND($NewStyle,BitNOT($WS_BORDER) )

DLLCall( "user32.dll", "long", "SetWindowLong", "hwnd", $hWnd, "int", $GWL_STYLE, "long", $NewStyle )

; Move the Window into postion and resize it
WinMove($window_name, "", 0, 0, 1024, 768)

; Wait for hotkey input
while (1)
    Sleep (2147483647)
WEnd

; Hides/Unhides window
Func ToggleWindowHide()
    If ($hidden) Then
        WinSetState ($window_name, "", @SW_SHOW)
    ;WinMove($window_name, "", 0, 0, 1024, 768)
    ;MouseClick("left")
    Else    
        WinSetState ($window_name, "", @SW_HIDE)
    ;WinMove($window_name, "", -5000, -5000)
    ;MouseClick("left")
    EndIf
    $hidden = NOT $hidden
EndFun

Now all that is left to tackle is how to keep the mouse from flying out of the right side of the screen unless I want it to...

Share this post


Link to post
Share on other sites

I have refined my mouse trap script: it now only covers the right-hand side. It works but it is imperfect for the mouse, when moved quickly, will escape onto the second monitor for a split-second before it gets lassoed back in.

#cs

Algorithm:
Get Active Window
Find Window 
Get Window Dimensions
Check Mouse Location against window dimensions
Move Mouse to closest valid coordinate

#ce

Opt("WinTitleMatchMode", 3)
Opt("MouseCoordMode", 0)

Global $window_name = "Warcraft III"
Global $rest = 1
Global $hotkey = "{F1}"
Global $Paused = 0
Global $window_info = WinGetPos($window_name)

HotKeySet($hotkey, "TogglePause")

while (1)
    CheckBoundries()
    Sleep($rest)
WEnd

Func CheckBoundries()
    If (WinActive($window_name)) Then
        $mouse_info = MouseGetPos()

        ; Right
        If ($mouse_info[0] >= ($window_info[2] - 2)) Then
            MouseMove($window_info[2] - 2, $mouse_info[1], 0)
        EndIf

    EndIf
EndFunc

Func TogglePause()
    $Paused = NOT $Paused
    While $Paused
        Sleep(100)
    WEnd
EndFunc

Share this post


Link to post
Share on other sites

Sounds great Larry. I will read up on ClipCursor as MSDN in the meantime.

Share this post


Link to post
Share on other sites

I am going to try writing a small C++ program that traps the cursor and then I will roll that into my AutoIt script. It will be a large undertaking for my newb-skills so it might be awhile until I post any code.

Share this post


Link to post
Share on other sites

Wait, someone has shown me a wrapper DLL that might allow me to make API calls from inside my script. I am going to try that out in a bit (EMch homework is calling me =\ )

Share this post


Link to post
Share on other sites

#15 ·  Posted (edited)

Hm, I'm not sure what dynacall can do that DLLCall cannot. I guess I just got confused. =\ So the problem is the need to pass a RECT structure to ClipCursor?

Edited by Christopher Blue

Share this post


Link to post
Share on other sites

From what I get from the page you provided, I would say this won't work with AutoIt. It requires a COM interface, which AutoIt does not yet support (maybe sometime in the near future?).

*** Matt @ MPCS

Share this post


Link to post
Share on other sites

I have been able to write a tiny C++ program that uses ClipCursor. Now all I have to do is refine it and then incorporate it into an AutoIt script.

Share this post


Link to post
Share on other sites

Ah darn I guess I spoke too soon: the cursor's clipping area keeps getting reset. I will try to fix that somehow in my program.

In the meantime, how can I use AutoIt to kill a process that doesn't have a window? (I start my MouseTrap.exe in hidden window mode).

Share this post


Link to post
Share on other sites

#19 ·  Posted (edited)

It appears that ClipCursor is reset as soon as another app gains focus. :) I will see if it sticks if I reapply it after the app has gained focus.

edit: Ah it appears that it does stick it reapplied after the app has gained focus. In light of that, I can probably work around it then.

Edited by Christopher Blue

Share this post


Link to post
Share on other sites

Side note, any application you drag with the titlebar will reset the clipcursor. You will need to call clipcursor multiple times to keep it active.


Who else would I be?

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