Jump to content

Multiple monitors: how to modify script to keep app on current monitor instead of defaulting back to first monitor?


Recommended Posts

Posted

I'm new to this and have no experience, so please be kind. I have been using a script to run Foobar fullscreen, but now that I have a 3-monitor setup I've run into an issue: no matter what screen the application is on, when executed with the hotkey the au3 will throw Foobar back to monitor 1 and make it fullscreen there. When I do the hotkey again to pull it out of fullscreen mode it will return the original monitor it was on. I would like the au3 script to make Foobar fullscreen on whatever monitor it is on.

 

What part of the script needs to be modified and what does it need to be replaced with?

 

I did try changing a few things, but the script either fails to execute or it executes with the same behavior.

 

Help!


 

#AutoIt3Wrapper_Run_Obfuscator=y
#Obfuscator_Parameters=/striponly

#include <WinAPI.au3>
#include <WinAPIEx.au3>

#include <Constants.au3>
#include <WindowsConstants.au3>

; ============================================================================ ;

Global $wndFoobar2000 = 0
Global $wndSavedPosition

Global Const $FullscreenRect[2] = [ _
    _WinAPI_GetSystemMetrics($SM_CXSCREEN), _
    _WinAPI_GetSystemMetrics($SM_CYSCREEN) _
]

HotKeySet("^!+g","ToggleFullscreen")

Func ToggleFullscreen()
    Global $wndFoobar2000, $wndSavedPosition
    Local Const $stylesToRemove = BitOR($WS_CAPTION,$WS_BORDER,$WS_SIZEBOX)

    If Not WinExists($wndFoobar2000) Then Return

    Local $wndStyle = _WinAPI_GetWindowLong($wndFoobar2000, $GWL_STYLE)
    If @error Then Return

    If BitAND($wndStyle, $stylesToRemove) <> 0 Then
        $wndSavedPosition = WinGetPos($wndFoobar2000)

        $wndStyle = BitAND($wndStyle, BitNOT($stylesToRemove))
        _WinAPI_SetWindowLong($wndFoobar2000, $GWL_STYLE, $wndStyle)

        _WinAPI_SetWindowPos($wndFoobar2000, $HWND_TOPMOST, _
                             0, 0, _
                             0, 0, _
                             BitOR($SWP_NOCOPYBITS, $SWP_FRAMECHANGED, $SWP_NOMOVE, $SWP_NOSIZE))

        _WinAPI_SetWindowPos($wndFoobar2000, 0, _
                             0, 0, _
                             $FullscreenRect[0], $FullscreenRect[1], _
                             BitOR($SWP_NOCOPYBITS, $SWP_FRAMECHANGED, $SWP_NOZORDER))
    Else
        $wndStyle = BitOR($wndStyle, $stylesToRemove)
        _WinAPI_SetWindowLong($wndFoobar2000, $GWL_STYLE, $wndStyle)

        _WinAPI_SetWindowPos($wndFoobar2000, $HWND_NOTOPMOST, _
                             0, 0, _
                             0, 0, _
                             BitOR($SWP_NOCOPYBITS, $SWP_FRAMECHANGED, $SWP_NOMOVE, $SWP_NOSIZE))

        If UBound($wndSavedPosition, 1) >= 4 Then
            _WinAPI_SetWindowPos($wndFoobar2000, 0, _
                                 $wndSavedPosition[0], $wndSavedPosition[1], _
                                 $wndSavedPosition[2], $wndSavedPosition[3], _
                                 BitOR($SWP_NOCOPYBITS, $SWP_FRAMECHANGED, $SWP_NOZORDER))
        EndIf
    EndIf
EndFunc

Func GetFoobar2000Window()
    Local $wndTemp

    $wndTemp = WinGetHandle("[CLASS:{Ea7076D1C-A7BF-4f39-B771-BCBE88F2A2A8}]","")
    If $wndTemp Then Return $wndTemp

    Local $i = 1
    While True
        $wndTemp = WinGetHandle("[REGEXPTITLE:foobar2000; INSTANCE:" & $i & "]","")
        If Not $wndTemp Then ExitLoop

        If StringRegExp(_WinAPI_GetWindowFileName($wndTemp),"foobar2000\.exe$") Then
            Return $wndTemp
        EndIf

        $i += 1
    WEnd

    Return 0
EndFunc

While True
    If Not WinExists($wndFoobar2000) Then
        $wndFoobar2000 =  GetFoobar2000Window()
        If Not $wndFoobar2000 Then
            Sleep(500)
        EndIf
    EndIf

    Sleep(200)
WEnd

 

Posted

I would expect this to work, but cannot test it on multiple monitors at the moment.

#include <Constants.au3>

HotKeySet("^!+g","ToggleFullscreen")

Func ToggleFullscreen($windowHandle)
   If BitAND(WinGetState($windowHandle), $WIN_STATE_MAXIMIZED) Then
      WinSetState($windowHandle, "", @SW_RESTORE)
   Else
      WinSetState($windowHandle, "", @SW_MAXIMIZE)
   EndIf
EndFunc

Local $windowHandle
While True
   $windowHandle = WinWait("[CLASS:Notepad;]", "", 0)
   While WinExists($windowHandle)
      Sleep(500)
   WEnd
WEnd

 

Posted

Hey vdude84,

If you can not get a better solution, maybe we can approach this like:

1. Get initial position of the foobar window, and find out which monitor it is on

2. move the foobar window to top,left-most position of that monitor and adjust window size to fit the available space (if there is a taskbar, need to adjust for that)

Now, this wouldn't exactly be fullscreen I guess (just a theory atm). And I don't know if anything special happens on the foobar window when going fullscreen.

Also, can possibly add a function to move it to the next/previous monitor perhaps that you can use with a shortcut key?

If you would be interested, post your monitor setup, and I will try to work something out.

Need for each monitor:

Monitor No: 1/2/3

1. Resolution

2. Scaling

3. Position Info: i.e, 2,1,3 - 1 is primary (means top,left coord = 0,0)

Posted

Take a look on this two topics:

 

Signature beginning:
Please remember: "AutoIt"..... *  Wondering who uses AutoIt and what it can be used for ? * Forum Rules *
ADO.au3 UDF * POP3.au3 UDF * XML.au3 UDF * IE on Windows 11 * How to ask ChatGPT for AutoIt Codefor other useful stuff click the following button:

  Reveal hidden contents

Signature last update: 2023-04-24

Posted
  On 11/20/2020 at 3:18 AM, boom221 said:

I would expect this to work, but cannot test it on multiple monitors at the moment.

#include <Constants.au3>

HotKeySet("^!+g","ToggleFullscreen")

Func ToggleFullscreen($windowHandle)
   If BitAND(WinGetState($windowHandle), $WIN_STATE_MAXIMIZED) Then
      WinSetState($windowHandle, "", @SW_RESTORE)
   Else
      WinSetState($windowHandle, "", @SW_MAXIMIZE)
   EndIf
EndFunc

Local $windowHandle
While True
   $windowHandle = WinWait("[CLASS:Notepad;]", "", 0)
   While WinExists($windowHandle)
      Sleep(500)
   WEnd
WEnd

 

Expand  

I can confirm that this works on multiple monitors, so I feel like we're moving in the right direction. Now, how to incorporate your script that respects the window/monitor relationship with foobar specifically? Your script maximizes the window, whereas the script that I have makes it fullscreen.

 

A little background - Foobar doesn't have the ability to actually go into a fullscreen mode. The script that I am using will kill the bar at the top and position it over the taskbar (giving it the same functionality and appearance of what hitting F11 in your browser would do).

 

Keeping with the browser analogy, imagine moving your browser to monitor 3 and hitting F11 and then it throws the browser back to monitor 1 fullscreen, and when you do the hotkey combination again it returns it to monitor 3 in the maximized state. Since it gets rid of the taskbar, it makes it pretty frustrating because every program/file explorer window will want to open on the main desktop and it's completely obscured. My end goal is to have Foobar fullscreened on my third monitor all the time where it isn't bothering anything or in the way of any other program/window that needs to launch.

 

Posted

I realize that I mispoke and I've discovered we have another route we could take -

Let's say you have your browser in fullscreen (F11) mode on monitor 1 - if you open a program or launch a file explorer window, it will go to the main monitor (#1), but it will overlay the fullscreened browser window, so you can drag it to another window. For some reason the script I am using to make Foobar fullscreen gives it an "always on top" feature, so there will be things launching behind it, and you'd have to get out of fullscreen mode to be able to see them.

 

Solution 1 - incorporate something into foobar script that makes it respect monitor that it's on, instead of defaulting to the monitor where the main desktop resides (ideal for me)

Solution 2 - lose the "always on top" aspect of the script, so I don't lose functionality when a window launches on the main desktop, instead of being hidden behind foobar (acceptable and still an improvement over the existing script behavior)

Posted

 

@vdude84

Have you looked at the solutions I indicated?
Were they helpful or did I misunderstand your idea?

btw.

oh.. I forget to say...

 :welcome: to logo_autoit_210x72.svg forum.

 

Signature beginning:
Please remember: "AutoIt"..... *  Wondering who uses AutoIt and what it can be used for ? * Forum Rules *
ADO.au3 UDF * POP3.au3 UDF * XML.au3 UDF * IE on Windows 11 * How to ask ChatGPT for AutoIt Codefor other useful stuff click the following button:

  Reveal hidden contents

Signature last update: 2023-04-24

Posted
  On 11/20/2020 at 7:05 PM, mLipok said:

 

@vdude84

Have you looked at the solutions I indicated?
Were they helpful or did I misunderstand your idea?

btw.

oh.. I forget to say...

 :welcome: to logo_autoit_210x72.svg forum.

 

Expand  

Thanks for the warm welcome and for the resources. Sadly, I don't have the knowledge base to understand what I'm looking at. Both you and @GokAy seem to be suggesting incorporating current monitor resolution information and monitor layout into the script, which (correct me if I'm wrong) seems to be leading down a path where we could get it to work for my current layout, but if I were to ever change things up it seems like it would require the script to be updated.

Does this mean that it's not possible to keep the "global" nature of the script now that multiple monitors are involved and we have to customize it to my specific set up? I was really hoping for a way to just make it respect the monitor it's on instead of hopping back to the main display each time it's executed.

If you say it's not possible to make it global, then yes - I'll go down the route to make it work for my layout, I just want to make sure I'm understanding fully.

Posted

Hey again,

Yes, in it's current nature what I am suggesting requires manual input of those info, however, there are some methods to get some/all of those. I just don't know if we can get a reliable way of capturing everything exactly, and have no time to research and incorporate them atm. What I can do is help create the script with those as variables, and then you or someone else (mLipok and couple others have good posts about this) can work on getting them programmatically.

Posted (edited)

Hey vdude84. I did misunderstood your requirement and am sorry for that. I came up with the following code.

It works on a multiple monitor setup as well as on a single monitor setup with a Notepad window (I do not have FooBar).

#include <Array.au3>
#include <WinAPI.au3>
#include <WindowsConstants.au3>

AutoItSetOption("MustDeclareVars", True)

Local Const $WINDOW_CLASS_NAME = "Notepad"

HotKeySet("^!+f", "ExitProgram")
HotKeySet("^!+g", "ToggleFullscreen")

Local $windowHandle = 0
While True
   $windowHandle = WinWait("[CLASS:" & $WINDOW_CLASS_NAME & ";]", "")
   While WinExists($windowHandle)
      Sleep(500)
   WEnd
   $windowHandle = 0
WEnd

Func ToggleFullScreen($windowHandle)
   Local Static $savedWindowWidth = 800
   Local Static $savedWindowHeight = 600
   Local Static $savedWindowLeft = 0
   Local Static $savedWindowTop = 0
   If $windowHandle == 0 Then
      ConsoleWrite("There is no window to control." & @CRLF)
      Return
   EndIf

   Local $displayMonitors = _WinAPI_EnumDisplayMonitors()
   If @error Then
      ConsoleWrite("Failed to retrieve display monitors.")
      Return
   EndIf

   If Not WinActive($windowHandle) Then WinActivate($windowHandle)

   Local $windowStyle = _WinAPI_GetWindowLong($windowHandle, $GWL_STYLE)
   Local $windowExtendedStyle = _WinAPI_GetWindowLong($windowHandle, $GWL_EXSTYLE)
   Local $styleBitMask = BitOR($WS_CAPTION, $WS_BORDER, $WS_SIZEBOX)
   If BitAND($windowStyle, BitNOT($styleBitMask)) == $windowStyle Then
      ConsoleWrite("Window is in full screen mode." & @CRLF)
      _WinAPI_SetWindowLong($windowHandle, $GWL_STYLE, BitOR($windowStyle, $styleBitMask))
      _WinAPI_SetWindowLong($windowHandle, $GWL_EXSTYLE, BitAND($windowExtendedStyle, BitNOT($WS_EX_TOPMOST)))
      If WinMove($windowHandle, "", $savedWindowLeft, $savedWindowTop, $savedWindowWidth, $savedWindowHeight) == 0 Then
         ConsoleWrite("Failed to reset window to regular mode." & @CRLF)
      Else
         ConsoleWrite("Moved window with handle " & $windowHandle & " to saved coordinates (" & $savedWindowLeft & ", " & $savedWindowTop & ") with size (" & $savedWindowWidth & ", " & $savedWindowHeight & ") and reset it to regular mode." & @CRLF)
      EndIf
   Else
      ConsoleWrite("Window is in regular mode." & @CRLF)

      _WinAPI_SetWindowLong($windowHandle, $GWL_STYLE, BitAND($windowStyle, BitNOT($styleBitMask)))
      _WinAPI_SetWindowLong($windowHandle, $GWL_EXSTYLE, BitOR($windowExtendedStyle, $WS_EX_TOPMOST))

      Local $windowBoundingRectangle = WinGetPos($windowHandle)
      $savedWindowLeft = $windowBoundingRectangle[0]
      $savedWindowTop = $windowBoundingRectangle[1]
      $savedWindowWidth = $windowBoundingRectangle[2]
      $savedWindowHeight = $windowBoundingRectangle[3]
      ConsoleWrite("Saved coordinates and size of window with handle " & $windowHandle & " to coordinates (" & $savedWindowLeft & ", " & $savedWindowTop & ") with size (" & $savedWindowWidth & ", " & $savedWindowHeight & ")." & @CRLF)
      Local $windowCenterX = $windowBoundingRectangle[0] + ($windowBoundingRectangle[2] - 1) / 2
      Local $windowCenterY = $windowBoundingRectangle[1] + ($windowBoundingRectangle[3] - 1) / 2

      Local $i = 1
      While $i <= UBound($displayMonitors, $UBOUND_ROWS) - 1

         Local $displayMonitorBoundingRectangle = _WinAPI_GetMonitorInfo($displayMonitors[$i][0])[1]
         Local $displayLeft = DllStructGetData($displayMonitorBoundingRectangle, 1)
         Local $displayTop = DllStructGetData($displayMonitorBoundingRectangle, 2)
         Local $displayWidth = DllStructGetData($displayMonitorBoundingRectangle, 3)
         Local $displayHeight = DllStructGetData($displayMonitorBoundingRectangle, 4)
         Local $displayRight = $displayLeft + $displayWidth - 1
         Local $displayBottom = $displayTop + $displayHeight - 1

         If $windowCenterX >= $displayLeft And $windowCenterX <= $displayRight And $windowCenterY >= $displayTop And $windowCenterY <= $displayBottom Then
            ;The window is on this monitor.
            If Not WinActive($windowHandle) Then WinActivate($windowHandle)

            If WinMove($windowHandle, "", $displayLeft, $displayTop, $displayWidth, $displayHeight) == 0 Then
               ConsoleWrite("Failed to move window." & @CRLF)
            Else
               ConsoleWrite("Moved window with handle " & $windowHandle & " to coordinates (" & $displayLeft & ", " & $displayTop & ") with size (" & $displayWidth & ", " & $displayHeight & ")." & @CRLF)
            EndIf

            ExitLoop;
         EndIf

         $i += 1
      WEnd
   EndIf
EndFunc

Func ExitProgram()
   Exit 0
Endfunc

 

Edited by boom221
Posted (edited)

I retrieve the coordinates of the monitors and determine which monitor the window is on. I then used pretty much the same code as the OP to make the app full screen. Both my monitors are identical, so I can't really confirm that it does take scaling into account, but it did work on two different computers so I would expect so.

Edited by boom221
Posted

In my case, as long as $WS_CAPTION and $WS_SIZEBOX are unset upon going full screen, it works no matter what I set for the resolution and/or scale. If I fail to do that, then I start getting gaps and very weird bugs.

Posted

Can I just say that I appreciate all of your help so much? (Both of you). I don't know why you've taken an interest to my post, but I'm glad you have.

Sometimes a picture/video is worth a thousand words. In the video I show you all 3 monitors with the behavior of Foobar using the original script, and then showing you notepad's reaction to boom221's script on my set up. (For context, the original script is already launched and I'm showing you Foobar's behavior on all 3 windows from left to right. I alter between maximized and a base-sized window while executing the script. Then I kill that script, load boom221's and launch notepad and rinse and repeat moving from monitor to monitor.)

https://www.youtube.com/watch?v=vWWUUu3zbRE

gokay - I know I never answered your question about the specs of my monitors, and the reason is because I don't think I fully understand what you need. Here's my attempt:

My monitor layout according to Windows from left to right is 3-1-2, with 3 being the main display. Based on what you wrote it sounds like you just want me to order them in an easy way to demonstrate which is primary, so in that case would be 1-2-3. Each monitor is 1920x1080, so the top left-most pixel position is 0,0 and the bottom right-most position is 1920,1080. We're talking fullscreen (no titlebar, no taskbar), so the specs should be what you're looking for. All are scaled at 100%. Again, if I'm misunderstanding you please bear with me and I'll give it another go.

Posted

@vdude84 Yeah, the info you provided should be enough. No need to re-order anything. I just need the order and primary to calculate coords. In your case, since left-most monitor is primary there are no -x coords.

In short it ill be like (if all are 100%), if windowpos is between 0 and 1919, then it is on display 3, if between 1920 and 3839 then it is on display 1, if between 3840 and 5760 then on display 2. And can also add a scaling calculation perhaps in case it might be needed. We may need a few tests though, I will try to prepare a script when I have the time.

Posted (edited)

Hey @vdude84, I did make some really dumb mistake in my code which caused the problems you showed in your video. I fixed the code and tested it on 3 screens, and it seems fine:

#include <Array.au3>
#include <WinAPI.au3>
#include <WindowsConstants.au3>

AutoItSetOption("MustDeclareVars", True)

;Local Const $WINDOW_CLASS_NAME = "Notepad"
;Local Const $WINDOW_CLASS_NAME = "Chrome_WidgetWin_1"
;Local Const $WINDOW_CLASS_NAME = "CabinetWClass"
Local Const $WINDOW_CLASS_NAME = "{97E27FAA-C0B3-4b8e-A693-ED7881E99FC1}"

HotKeySet("^!+f", "ExitProgram")
HotKeySet("^!+g", "ToggleFullscreen")

Local $windowHandle = 0
While True
   $windowHandle = WinWait("[CLASS:" & $WINDOW_CLASS_NAME & ";]", "")
   While WinExists($windowHandle)
      Sleep(500)
   WEnd
   $windowHandle = 0
WEnd

Func ToggleFullScreen($windowHandle)
   Local Static $savedWindowWidth = 800
   Local Static $savedWindowHeight = 600
   Local Static $savedWindowLeft = 0
   Local Static $savedWindowTop = 0
   Local Static $inProgress

   If $inProgress Then
      ConsoleWrite("Function already running." & @CRLF)
      Return SetError(1, 0, Null)
   EndIf
   $inProgress = True

   If $windowHandle == 0 Then
      ConsoleWrite("There is no window to control." & @CRLF)
   Else
      If Not WinActive($windowHandle) Then WinActivate($windowHandle)

      Local $windowStyle = _WinAPI_GetWindowLong($windowHandle, $GWL_STYLE)
      Local $windowExtendedStyle = _WinAPI_GetWindowLong($windowHandle, $GWL_EXSTYLE)
      Local $styleBitMask = BitOR($WS_CAPTION, $WS_BORDER)
      If BitAND($windowStyle, BitNOT($styleBitMask)) == $windowStyle Then
         _WinAPI_SetWindowLong($windowHandle, $GWL_STYLE, BitOR($windowStyle, $styleBitMask))
         _WinAPI_SetWindowLong($windowHandle, $GWL_EXSTYLE, BitAND($windowExtendedStyle, BitNOT($WS_EX_TOPMOST)))
         WinMove($windowHandle, "", $savedWindowLeft, $savedWindowTop, $savedWindowWidth, $savedWindowHeight)
         ConsoleWrite("Window is back to normal." & @CRLF)
      Else
         Local $windowBoundingRectangle = WinGetPos($windowHandle)
         $savedWindowLeft = $windowBoundingRectangle[0]
         $savedWindowTop = $windowBoundingRectangle[1]
         $savedWindowWidth = $windowBoundingRectangle[2]
         $savedWindowHeight = $windowBoundingRectangle[3]
         ConsoleWrite("Saved coordinates and size of window with handle " & $windowHandle & " to coordinates (" & $savedWindowLeft & ", " & $savedWindowTop & ") with size (" & $savedWindowWidth & ", " & $savedWindowHeight & ")." & @CRLF)

         WinSetState($windowHandle, "", @SW_MAXIMIZE)
         $windowBoundingRectangle =  WinGetPos($windowHandle)
         _WinAPI_SetWindowLong($windowHandle, $GWL_STYLE, BitAND($windowStyle, BitNOT($styleBitMask)))
         _WinAPI_SetWindowLong($windowHandle, $GWL_EXSTYLE, BitOR($windowExtendedStyle, $WS_EX_TOPMOST))
         ConsoleWrite("Window is full screen." & @CRLF)
      EndIf
   EndIf

   $inProgress = False
EndFunc

Func ExitProgram()
   Exit 0
Endfunc

 

Edited by boom221
Now works fine with foobar2000
Posted

@boom221

I haven't had a chance until today to test, but this script isn't doing anything for me with any program. According to the script it's still the same hotkey, so I'm not sure what's going on. I tested the original Foobar script to make sure I didn't have some other problem going on, but it's still working. Not sure why it would work for you and not for me.

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
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...