Search code examples
mpv

MPV: Get window position and size? Or window-moved/resized event?


Is there a way to get the current position and size of the MPV media player's window (or an event that fires when they are changed)?


I'm trying to make a script that automatically saves the last position of the window, and then loads it when the player starts. Settings the position on startup can be done with geometry, but reading can't.

In the log it is logged when the windows is moved:

[  34.308][d][vo/gpu/win32] move window: 1953:48

And resized:

[  37.990][v][vo/gpu] Resize: 1810x1004
[  37.990][v][vo/gpu] Window size: 1810x1004

Is there a way to get these values or callbacks in a (javascript) script? I couldn't find the event in the documentation sadly, or did I just miss it?

Only thing I could find were dwidth and dheight, but these represent only the size of the video, not the entire window nor the position of it.

Thanks!

Note: I also asked this on mpv's github, but no reply yet. I'll update the other when I get a reply in either.


Solution

  • I figured out a way using mp.utils.subprocess to run some powershell script, as mpv doesn't have any API's to get the position directly. It's a bit slow, but it does work:

    (The ps1 script:)

    Add-Type @"
    using System;
    using System.Runtime.InteropServices;
    public class Window {
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
    }
    public struct RECT
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
    }
    "@
    
    $Handle = (Get-Process -Id $Args[0]).MainWindowHandle
    $WindowRect = New-Object RECT
    $GotWindowRect = [Window]::GetWindowRect($Handle, [ref]$WindowRect)
    ConvertTo-Json($WindowRect)
    

    This then gives you a json object that has the position and size of the window. You can then use SetWindowRect in a similar way to set the position again. Note that this rect doesn't correspond to the thing you would set with geometry in mpv itself, as this rect also includes the title bar and such.

    Edit:

    I made a better version.

    The powershell script now gets the client-rect which can be used with geometry, and thus opening mpv is now much smoother.

    So the new powershell script:

    Add-Type @"
    using System;
    using System.Runtime.InteropServices;
    public class Window {
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool ClientToScreen(IntPtr hWnd, ref POINT lpPoint);
    }
    public struct RECT
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
    }
    public struct POINT
    {
        public int x;
        public int y;
    }
    "@
    
    $Handle = (Get-Process -Id $Args[0]).MainWindowHandle
    $WindowRect = New-Object RECT
    $ClientRect = New-Object RECT
    $TopLeft = New-Object POINT
    $BottomRight = New-Object POINT
    
    [Window]::GetClientRect($Handle, [ref]$ClientRect) | out-null
    $TopLeft.x = $ClientRect.Left
    $TopLeft.y = $ClientRect.Top
    $BottomRight.x = $ClientRect.Right
    $BottomRight.y = $ClientRect.Bottom
    
    [Window]::ClientToScreen($Handle, [ref]$TopLeft) | out-null
    [Window]::ClientToScreen($Handle, [ref]$BottomRight) | out-null
    
    $WindowRect.Left = $TopLeft.x
    $WindowRect.Top = $TopLeft.y
    $WindowRect.Right = $BottomRight.x
    $WindowRect.Bottom = $BottomRight.y
    
    ConvertTo-Json($WindowRect)
    

    Then I have a javascript plugin that calls this ps1 in a simple javascript plugin:

    // Some setup used by both reading and writing
    var dir = mp.utils.split_path(mp.get_script_file())[0]
    var rect_path = mp.utils.join_path(dir, "last_window_rect.txt")
    
    // Read last window rect if present
    try {
        var rect_json = mp.utils.read_file(rect_path)
        var rect = JSON.parse(rect_json)
    
        var width = rect.Right - rect.Left
        var height = rect.Bottom - rect.Top
        mp.set_property("screen", 0)
        mp.set_property("geometry", width + "x" + height + "+" + rect.Left + "+" + rect.Top)
    }
    catch (e) {
        dump(e)
    }
    
    // Save the rect at shutdown
    function save_rect() {
        var ps1_script = mp.utils.join_path(dir, "Get-Client-Rect.ps1")
        var rect_json = mp.utils.subprocess({ args: ["powershell", "&(\"" + ps1_script + "\")", mp.utils.getpid()], cancellable: false }).stdout
        mp.utils.write_file("file://" + rect_path, rect_json)
    }
    mp.register_event("shutdown", save_rect)
    

    You can also find these scripts on my github: https://github.com/TheOddler/mpv-config/tree/master/scripts but I can't promise these will stay the same forever.