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.