Compare commits

...

18 Commits
4.5 ... 4.7.1

Author SHA1 Message Date
Frank Skare
d27778775d few layout problems were fixed, autosize for instance did not work 2019-07-13 16:35:42 +02:00
Frank Skare
5c053bb5eb fix borderless window not minimizable from taskbar 2019-07-13 03:24:15 +02:00
Frank Skare
83658aa476 - 2019-07-12 23:53:43 +02:00
Frank Skare
e0111b6f12 - 2019-07-12 06:23:53 +02:00
Frank Skare
e308bbf928 - 2019-07-11 23:55:07 +02:00
Frank Skare
1817fbc0fd - 2019-07-09 09:17:35 +02:00
Frank Skare
3b149aea54 - 2019-07-08 18:18:55 +02:00
Frank Skare
7575a2d736 - 2019-07-06 23:51:28 +02:00
Frank Skare
17c198e711 - 2019-07-06 23:46:14 +02:00
Frank Skare
1f8635e9c4 - 2019-07-06 09:35:14 +02:00
Frank Skare
37e6c198a1 - 2019-07-06 03:46:36 +02:00
Frank Skare
751c8779f4 - 2019-07-06 02:39:25 +02:00
Frank Skare
9e2bf46636 - 2019-07-05 07:17:58 +02:00
Frank Skare
3529323d93 - 2019-07-05 06:36:15 +02:00
Frank Skare
ba52c1f8b5 - 2019-07-05 05:40:14 +02:00
Frank Skare
bf2070d758 - 2019-07-05 05:38:52 +02:00
Frank Skare
7a1de4cf0f - 2019-07-05 05:05:37 +02:00
Frank Skare
caf0bee161 - 2019-07-01 04:17:16 +02:00
21 changed files with 427 additions and 345 deletions

View File

@@ -1,3 +1,41 @@
### 4.7.1
- few layout problems were fixed, autosize for instance did not work
### 4.7
- remember-height was replaced with start-size, when start-size is set
to video the main video starts directly with the native video size,
before it was starting with the autofit size first and was only
afterwards resized to the native video size
- on exit the window location can be saved with remember-position
- in the learn window of the input editor underscores were stripped
because they have a special meaning in WPF labels
- fix for keys/input not working for MBTN_LEFT_DBL, MBTN_BACK, MBTN_FORWARD
- in the learn window of the input editor support was added for
mouse left, mouse left double, mouse mid, mouse forward, mouse back
- libmpv was updated to shinchiro 2019-07-07
- when border is none it wasn't possible to minimize the window from
the task bar because this is the WinForms default behavier. This
was fixed by calling Spy++ to the rescue and adding WS_MINIMIZEBOX
in CreateParams
### 4.6
- fix for middle mouse button not working
- fix of logo overlay using a huge amount of memory (thx for the [ghacks article](https://www.ghacks.net/2019/07/05/a-look-at-mpv-net-a-mpv-frontend-with-everything-integration/))
- fix config dialog showing a message about app restart without reason
- when multiple files are selected in Windows File Explorer and enter is
pressed, the files are opened as selected, the order is random though
because Explorer starts multiple mpv.net processes concurrently
- libmpv was updated to shinchiro 2019-06-30
- the [mpv.conf defaults](https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/mpvConf.txt) were changed to show a larger OSC
- in case a file is opened that has a aspect ratio smaller then 1.2 then
the window size will use a aspect ratio of 1.8
- new JavaScript script osc-visibility.js included in the distribution
under startup\scripts. It sets the OSC to be always on for audio files
and auto for non audio files
### 4.5 ### 4.5
- opening a URL manually no longer uses a input box but uses the clipboard directly - opening a URL manually no longer uses a input box but uses the clipboard directly

View File

@@ -102,9 +102,9 @@ mpv.net is meant to be a small single person project, it's designed to be mpv co
### Target Audience ### Target Audience
The target audience of mpv.net are Windows programmers and users that need something more advanced than common media players. The target audience of mpv.net are programmers, nerds and software enthusiasts that need something more advanced than typical media players.
Furthermore mpv.net is well suited for Windows users who are interested to learn about the Linux operating system and portable apps, even though mpv.net self is not portable. Furthermore mpv.net is well suited for users who are interested to learn mpv, Linux, portable apps and the command line.
## Requirements ## Requirements

View File

@@ -52,12 +52,13 @@ Table of contents
- Searchable command palette to quickly launch commands and look for keys ([Screenshot](#command-palette-screenshot)) - Searchable command palette to quickly launch commands and look for keys ([Screenshot](#command-palette-screenshot))
- Modern UI with dark mode ([Screenshot](#config-editor-screenshot)) - Modern UI with dark mode ([Screenshot](#config-editor-screenshot))
- Addon/extension API for .NET languages - Addon/extension API for .NET languages
- Scripting API for Python, C#, Lua, JavaScript and PowerShell ([wiki](https://github.com/stax76/mpv.net/wiki/Scripting)) - Scripting API for Python, C#, Lua, JavaScript and PowerShell ([Wiki](https://github.com/stax76/mpv.net/wiki/Scripting))
- mpv's OSC, IPC and conf files - mpv's OSC, IPC and conf files
- [Command Line Interface](https://mpv.io/manual/master/#options) - [Command Line Interface](https://mpv.io/manual/master/#options)
- DXVA2 video decoding acceleration - DXVA2 video decoding acceleration
- OpenGL based video output capable of features loved by videophiles, such as video scaling with popular high quality algorithms, color management, frame timing, interpolation, HDR, and more - OpenGL based video output capable of features loved by videophiles, such as video scaling with popular high quality algorithms, color management, frame timing, interpolation, HDR, and more
- Search feature powered by [Everything](https://www.voidtools.com) to find and play media ([Screenshot](#media-search-screenshot)) - Search feature powered by [Everything](https://www.voidtools.com) to find and play media ([Screenshot](#media-search-screenshot))
- Extension to start mpv.net from Google Chrome ([Manual](Manual.md#chrome-extension))
### Screenshots ### Screenshots
@@ -86,7 +87,7 @@ A searchable key and mouse binding editor.
#### Command Palette Screenshot #### Command Palette Screenshot
Forgot where a command in the menu is located or what shortcut key it has? Forgot where a command in the menu is located or what shortcut key it has?
Just press Ctrl+Shift+P and find it easily in the searchable command palette. Just press F1 and find it easily in the searchable command palette.
![Command Palette](https://raw.githubusercontent.com/stax76/mpv.net/master/img/CommandPalette.png) ![Command Palette](https://raw.githubusercontent.com/stax76/mpv.net/master/img/CommandPalette.png)
@@ -194,6 +195,10 @@ Third party components:
Please click on the star at the top of the page and like mpv.net at [alternativeto.net](https://alternativeto.net/software/mpv-net/). Please click on the star at the top of the page and like mpv.net at [alternativeto.net](https://alternativeto.net/software/mpv-net/).
If you like you can express your appreciation for my player by sending little beer money with paypal.
<https://www.paypal.me/stax76>
### Links ### Links
mpv manual: <https://mpv.io/manual/master/> mpv manual: <https://mpv.io/manual/master/>

View File

@@ -1752,7 +1752,6 @@ namespace Tommy
} }
else else
stringBuilder.Append(c); stringBuilder.Append(c);
break; break;
} }
} }

View File

@@ -1,9 +1,8 @@
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Windows;
using System.Windows.Forms; using System.Windows.Forms;
using System.Windows.Interop; using System.Windows.Interop;
@@ -13,42 +12,43 @@ namespace mpvnet
{ {
public class Command public class Command
{ {
public string Name { get; set; } public static void Execute(string id, string[] args)
public Action<string[]> Action { get; set; }
static List<Command> commands;
public static List<Command> Commands
{ {
get switch (id)
{ {
if (commands == null) case "manage-file-associations": ManageFileAssociations(); break;
{ case "cycle-audio": CycleAudio(); break;
commands = new List<Command>(); case "load-audio": LoadAudio(); break;
Type type = typeof(Command); case "load-sub": LoadSubtitle(); break;
MethodInfo[] methods = type.GetMethods(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public); case "open-url": OpenURL(); break;
case "execute-mpv-command": ExecuteMpvCommand(); break;
foreach (MethodInfo i in methods) case "show-history": ShowHistory(); break;
{ case "show-media-search": ShowDialog(typeof(EverythingWindow)); break;
ParameterInfo[] parameters = i.GetParameters(); case "show-command-palette": ShowDialog(typeof(CommandPaletteWindow)); break;
case "show-about": ShowDialog(typeof(AboutWindow)); break;
if (parameters == null || case "show-conf-editor": ShowDialog(typeof(ConfWindow)); break;
parameters.Length != 1 || case "show-input-editor": ShowDialog(typeof(InputWindow)); break;
parameters[0].ParameterType != typeof(string[])) case "open-conf-folder": Process.Start(mp.ConfFolder); break;
continue; case "open-files": OpenFiles(args); break;
case "shell-execute": Process.Start(args[0]); break;
Command cmd = new Command() { case "show-info": ShowInfo(); break;
Name = i.Name.Replace("_", "-"), case "add-files-to-playlist": OpenFiles("append"); break; // deprecated 2019
Action = (Action<string[]>)i.CreateDelegate(typeof(Action<string[]>)) }; default: Msg.ShowError($"No command '{id}' found."); break;
commands.Add(cmd);
}
}
return commands;
} }
} }
public static void open_files(string[] args) public static void InvokeOnMainThread(Action action) => MainForm.Instance.Invoke(action);
public static void ShowDialog(Type winType)
{
InvokeOnMainThread(new Action(() => {
Window win = Activator.CreateInstance(winType) as Window;
new WindowInteropHelper(win).Owner = MainForm.Instance.Handle;
win.ShowDialog();
}));
}
public static void OpenFiles(params string[] args)
{ {
bool append = Control.ModifierKeys.HasFlag(Keys.Control); bool append = Control.ModifierKeys.HasFlag(Keys.Control);
bool loadFolder = true; bool loadFolder = true;
@@ -59,75 +59,14 @@ namespace mpvnet
if (arg == "no-folder") loadFolder = false; if (arg == "no-folder") loadFolder = false;
} }
MainForm.Instance.Invoke(new Action(() => { InvokeOnMainThread(new Action(() => {
using (var d = new OpenFileDialog() { Multiselect = true }) using (var d = new OpenFileDialog() { Multiselect = true })
if (d.ShowDialog() == DialogResult.OK) if (d.ShowDialog() == DialogResult.OK)
mp.Load(d.FileNames, loadFolder, append); mp.Load(d.FileNames, loadFolder, append);
})); }));
} }
// deprecated in 2019 public static void ShowHistory()
public static void add_files_to_playlist(string[] args)
{
MainForm.Instance.Invoke(new Action(() => {
using (var d = new OpenFileDialog() { Multiselect = true })
if (d.ShowDialog() == DialogResult.OK)
foreach (string file in d.FileNames)
mp.commandv("loadfile", file, "append");
}));
}
public static void open_conf_folder(string[] args)
{
Process.Start(mp.ConfFolder);
}
public static void show_input_editor(string[] args)
{
MainForm.Instance.Invoke(new Action(() => {
InputWindow w = new InputWindow();
new WindowInteropHelper(w).Owner = MainForm.Instance.Handle;
w.ShowDialog();
}));
}
public static void show_conf_editor(string[] args)
{
MainForm.Instance.Invoke(new Action(() => {
ConfWindow w = new ConfWindow();
new WindowInteropHelper(w).Owner = MainForm.Instance.Handle;
w.ShowDialog();
}));
}
public static void show_about(string[] args)
{
MainForm.Instance.Invoke(new Action(() => {
AboutWindow w = new AboutWindow();
new WindowInteropHelper(w).Owner = MainForm.Instance.Handle;
w.ShowDialog();
}));
}
public static void show_command_palette(string[] args)
{
MainForm.Instance.Invoke(new Action(() => {
var w = new CommandPaletteWindow();
new WindowInteropHelper(w).Owner = MainForm.Instance.Handle;
w.ShowDialog();
}));
}
public static void show_media_search(string[] args)
{
MainForm.Instance.Invoke(new Action(() => {
var w = new EverythingWindow();
new WindowInteropHelper(w).Owner = MainForm.Instance.Handle;
w.ShowDialog();
}));
}
public static void show_history(string[] args)
{ {
var fp = mp.ConfFolder + "history.txt"; var fp = mp.ConfFolder + "history.txt";
@@ -139,9 +78,7 @@ namespace mpvnet
File.WriteAllText(fp, ""); File.WriteAllText(fp, "");
} }
public static void shell_execute(string[] args) => Process.Start(args[0]); public static void ShowInfo()
public static void show_info(string[] args)
{ {
try try
{ {
@@ -205,9 +142,9 @@ namespace mpvnet
} }
} }
public static void execute_mpv_command(string[] args) public static void ExecuteMpvCommand()
{ {
MainForm.Instance.Invoke(new Action(() => { InvokeOnMainThread(new Action(() => {
string command = VB.Interaction.InputBox("Enter a mpv command to be executed.", "Execute Command", RegHelp.GetString(App.RegPath, "RecentExecutedCommand")); string command = VB.Interaction.InputBox("Enter a mpv command to be executed.", "Execute Command", RegHelp.GetString(App.RegPath, "RecentExecutedCommand"));
if (string.IsNullOrEmpty(command)) return; if (string.IsNullOrEmpty(command)) return;
RegHelp.SetObject(App.RegPath, "RecentExecutedCommand", command); RegHelp.SetObject(App.RegPath, "RecentExecutedCommand", command);
@@ -215,22 +152,22 @@ namespace mpvnet
})); }));
} }
public static void open_url(string[] args) public static void OpenURL()
{ {
MainForm.Instance.Invoke(new Action(() => { InvokeOnMainThread(new Action(() => {
string clipboard = Clipboard.GetText(); string clipboard = System.Windows.Forms.Clipboard.GetText();
if (string.IsNullOrEmpty(clipboard) || (!clipboard.Contains("://") && !File.Exists(clipboard)) || clipboard.Contains("\n")) if (string.IsNullOrEmpty(clipboard) || (!clipboard.Contains("://") && !File.Exists(clipboard)) || clipboard.Contains("\n"))
{ {
Msg.ShowError("The clipboard does not contain a valid URL or file, URLs have to contain :// and is not allowed to contain a newline character."); Msg.ShowError("The clipboard does not contain a valid URL or file, URLs have to contain :// and are not allowed to contain a newline character.");
return; return;
} }
mp.Load(new [] { clipboard }, false, Control.ModifierKeys.HasFlag(Keys.Control)); mp.Load(new [] { clipboard }, false, Control.ModifierKeys.HasFlag(Keys.Control));
})); }));
} }
public static void load_sub(string[] args) public static void LoadSubtitle()
{ {
MainForm.Instance.Invoke(new Action(() => { InvokeOnMainThread(new Action(() => {
using (var d = new OpenFileDialog()) using (var d = new OpenFileDialog())
{ {
d.InitialDirectory = Path.GetDirectoryName(mp.get_property_string("path", false)); d.InitialDirectory = Path.GetDirectoryName(mp.get_property_string("path", false));
@@ -242,9 +179,9 @@ namespace mpvnet
})); }));
} }
public static void load_audio(string[] args) public static void LoadAudio()
{ {
MainForm.Instance.Invoke(new Action(() => { InvokeOnMainThread(new Action(() => {
using (var d = new OpenFileDialog()) using (var d = new OpenFileDialog())
{ {
d.InitialDirectory = Path.GetDirectoryName(mp.get_property_string("path", false)); d.InitialDirectory = Path.GetDirectoryName(mp.get_property_string("path", false));
@@ -257,7 +194,7 @@ namespace mpvnet
})); }));
} }
public static void cycle_audio(string[] args) public static void CycleAudio()
{ {
string filePath = mp.get_property_string("path", false); string filePath = mp.get_property_string("path", false);
if (!File.Exists(filePath)) return; if (!File.Exists(filePath)) return;
@@ -274,7 +211,7 @@ namespace mpvnet
} }
} }
public static void manage_file_associations(string[] args) public static void ManageFileAssociations()
{ {
using (var td = new TaskDialog<string>()) using (var td = new TaskDialog<string>())
{ {
@@ -283,7 +220,7 @@ namespace mpvnet
td.AddCommandLink("Register video file extensions", "video"); td.AddCommandLink("Register video file extensions", "video");
td.AddCommandLink("Register audio file extensions", "audio"); td.AddCommandLink("Register audio file extensions", "audio");
td.AddCommandLink("Unregister file extensions", "unreg"); td.AddCommandLink("Unregister file extensions", "unreg");
string result = td.Show(); string result = td.Show();
@@ -291,14 +228,10 @@ namespace mpvnet
{ {
using (var proc = new Process()) using (var proc = new Process())
{ {
proc.StartInfo.FileName = Application.ExecutablePath; proc.StartInfo.FileName = System.Windows.Forms.Application.ExecutablePath;
proc.StartInfo.Arguments = "--reg-file-assoc " + result; proc.StartInfo.Arguments = "--reg-file-assoc " + result;
proc.StartInfo.Verb = "runas"; proc.StartInfo.Verb = "runas";
try { try { proc.Start(); } catch { }
proc.Start();
}
catch (Exception)
{ }
} }
} }
} }

View File

@@ -28,6 +28,8 @@ namespace mpvnet
public static string[] SubtitleTypes { get; } = "srt ass idx sup ttxt ssa smi".Split(' '); public static string[] SubtitleTypes { get; } = "srt ass idx sup ttxt ssa smi".Split(' ');
public static string[] UrlWhitelist { get; set; } = { "tube", "vimeo", "ard", "zdf" }; public static string[] UrlWhitelist { get; set; } = { "tube", "vimeo", "ard", "zdf" };
public static bool RememberHeight { get; set; } = true;
public static bool RememberPosition { get; set; }
public static bool DebugMode { get; set; } = false; public static bool DebugMode { get; set; } = false;
public static bool IsDarkMode { public static bool IsDarkMode {
@@ -36,6 +38,9 @@ namespace mpvnet
public static void Init() public static void Init()
{ {
string dummy = mp.ConfFolder;
var dummy2 = mp.Conf;
foreach (var i in Conf) foreach (var i in Conf)
ProcessProperty(i.Key, i.Value); ProcessProperty(i.Key, i.Value);
@@ -83,6 +88,8 @@ namespace mpvnet
{ {
switch (name) switch (name)
{ {
case "remember-position": RememberPosition = value == "yes"; break;
case "start-size": RememberHeight = value == "previous"; break;
case "process-instance": ProcessInstance = value; break; case "process-instance": ProcessInstance = value; break;
case "dark-mode": DarkMode = value; break; case "dark-mode": DarkMode = value; break;
case "debug-mode": DebugMode = value == "yes"; break; case "debug-mode": DebugMode = value == "yes"; break;
@@ -109,15 +116,9 @@ namespace mpvnet
} }
else else
{ {
string switchName = i.Substring(2); string name = i.Substring(2);
mp.ProcessProperty(name, "yes");
switch (switchName) ProcessProperty(name, "yes");
{
case "fs":
case "fullscreen":
mp.Fullscreen = true;
break;
}
} }
} }
} }
@@ -228,6 +229,13 @@ namespace mpvnet
return val.ToString(); return val.ToString();
} }
public static int GetInt(string path, string name)
{
object val = GetObject(path, name);
if (val == null || !(val is int)) return 0;
return (int)val;
}
public static object GetObject(string path, string name) public static object GetObject(string path, string name)
{ {
using (RegistryKey rk = GetRootKey(path).OpenSubKey(path.Substring(5))) using (RegistryKey rk = GetRootKey(path).OpenSubKey(path.Substring(5)))
@@ -393,15 +401,4 @@ namespace mpvnet
Math.Abs(screenPos.Y - Control.MousePosition.Y) > 10; Math.Abs(screenPos.Y - Control.MousePosition.Y) > 10;
} }
} }
public class SingleProcess
{
public static int Message { get; } = RegisterWindowMessage("mpvnet_IPC");
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
static extern int RegisterWindowMessage(string id);
[DllImport("user32.dll")]
public static extern bool AllowSetForegroundWindow(int dwProcessId);
}
} }

View File

@@ -31,24 +31,31 @@ namespace mpvnet
if ((App.ProcessInstance == "single" || App.ProcessInstance == "queue") && !isFirst) if ((App.ProcessInstance == "single" || App.ProcessInstance == "queue") && !isFirst)
{ {
List<string> files = new List<string>(); List<string> files = new List<string>();
files.Add(App.ProcessInstance);
foreach (string arg in args) foreach (string arg in args)
if (!arg.StartsWith("--") && (arg == "-" || arg.Contains("://") || File.Exists(arg))) if (!arg.StartsWith("--") && (arg == "-" || arg.Contains("://") || File.Exists(arg)))
files.Add(arg); files.Add(arg);
if (files.Count > 0) Process[] procs = Process.GetProcessesByName("mpvnet");
RegHelp.SetObject(App.RegPath, "ShellFiles", files.ToArray());
RegHelp.SetObject(App.RegPath, "ProcessInstanceMode", App.ProcessInstance); for (int i = 0; i < 20; i++)
foreach(Process process in Process.GetProcessesByName("mpvnet"))
{ {
try { foreach (Process proc in procs)
SingleProcess.AllowSetForegroundWindow(process.Id); {
Native.SendMessage(process.MainWindowHandle, SingleProcess.Message, IntPtr.Zero, IntPtr.Zero); if (proc.MainWindowHandle != IntPtr.Zero)
} catch {} {
Native.AllowSetForegroundWindow(proc.Id);
var data = new Native.COPYDATASTRUCT();
data.lpData = string.Join("\n", files.ToArray());
data.cbData = data.lpData.Length * 2 + 1;
Native.SendMessage(proc.MainWindowHandle, 0x004A /*WM_COPYDATA*/, IntPtr.Zero, ref data);
mutex.Dispose();
return;
}
}
Thread.Sleep(50);
} }
mutex.Dispose(); mutex.Dispose();
return; return;
} }

View File

@@ -15,9 +15,15 @@ namespace mpvnet
[DllImport("user32.dll", CharSet = CharSet.Unicode)] [DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, ref COPYDATASTRUCT lParam);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr PostMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam); public static extern IntPtr PostMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern bool AllowSetForegroundWindow(int dwProcessId);
[DllImport("user32.dll")] [DllImport("user32.dll")]
public static extern void ReleaseCapture(); public static extern void ReleaseCapture();
@@ -33,7 +39,7 @@ namespace mpvnet
[DllImport("user32.dll", EntryPoint = "GetWindowLongPtr")] [DllImport("user32.dll", EntryPoint = "GetWindowLongPtr")]
private static extern IntPtr GetWindowLong64(IntPtr hWnd, int nIndex); private static extern IntPtr GetWindowLong64(IntPtr hWnd, int nIndex);
public static IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex) public static IntPtr GetWindowLong(IntPtr hWnd, int nIndex)
{ {
if (IntPtr.Size == 8) if (IntPtr.Size == 8)
return GetWindowLong64(hWnd, nIndex); return GetWindowLong64(hWnd, nIndex);
@@ -70,5 +76,14 @@ namespace mpvnet
public int Width => Right - Left; public int Width => Right - Left;
public int Height => Bottom - Top; public int Height => Bottom - Top;
} }
[StructLayout(LayoutKind.Sequential)]
public struct COPYDATASTRUCT
{
public IntPtr dwData;
public int cbData;
[MarshalAs(UnmanagedType.LPStr)]
public string lpData;
}
} }
} }

View File

@@ -32,7 +32,7 @@ namespace mpvnet
public static void AddWindowBorders(IntPtr hwnd, ref Native.RECT rc) public static void AddWindowBorders(IntPtr hwnd, ref Native.RECT rc)
{ {
Native.AdjustWindowRect(ref rc, (uint)Native.GetWindowLongPtr(hwnd, -16 /* GWL_STYLE */), false); Native.AdjustWindowRect(ref rc, (uint)Native.GetWindowLong(hwnd, -16 /* GWL_STYLE */), false);
} }
} }
} }

View File

@@ -6,7 +6,7 @@ using System.Runtime.InteropServices;
// set of attributes. Change these attribute values to modify the information // set of attributes. Change these attribute values to modify the information
// associated with an assembly. // associated with an assembly.
[assembly: AssemblyTitle("mpv.net")] [assembly: AssemblyTitle("mpv.net")]
[assembly: AssemblyDescription("A modern media player")] [assembly: AssemblyDescription("media player")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Frank Skare (stax76)")] [assembly: AssemblyCompany("Frank Skare (stax76)")]
[assembly: AssemblyProduct("mpv.net")] [assembly: AssemblyProduct("mpv.net")]
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("4.5.0.0")] [assembly: AssemblyVersion("4.7.1.0")]
[assembly: AssemblyFileVersion("4.5.0.0")] [assembly: AssemblyFileVersion("4.7.1.0")]

View File

@@ -24,6 +24,8 @@
# mpv input keys: https://github.com/stax76/mpv.net/wiki/mpv-input-keys # mpv input keys: https://github.com/stax76/mpv.net/wiki/mpv-input-keys
# run mpv.net in input test mode with: mpvnet --input-test
o script-message mpv.net open-files #menu: Open > Open Files... o script-message mpv.net open-files #menu: Open > Open Files...
u script-message mpv.net open-url #menu: Open > Open URL or file path from clipboard u script-message mpv.net open-url #menu: Open > Open URL or file path from clipboard
_ ignore #menu: Open > - _ ignore #menu: Open > -
@@ -182,4 +184,5 @@
Next playlist-next Next playlist-next
Ctrl+Wheel_Up no-osd seek 7 Ctrl+Wheel_Up no-osd seek 7
Ctrl+Wheel_Down no-osd seek -7 Ctrl+Wheel_Down no-osd seek -7
MBTN_LEFT_DBL cycle fullscreen MBTN_LEFT_DBL cycle fullscreen
KP_ENTER cycle fullscreen

View File

@@ -11,4 +11,5 @@ keep-open = yes
keep-open-pause = no keep-open-pause = no
osd-playing-msg = '${filename}' osd-playing-msg = '${filename}'
screenshot-directory = '~~desktop/' screenshot-directory = '~~desktop/'
input-default-bindings = no input-default-bindings = no
script-opts=osc-scalewindowed=1.5

View File

@@ -254,14 +254,6 @@ default = "50%"
filter = "Screen" filter = "Screen"
help = "Set the initial window size in percent. Please note that this setting is only partly implemented in mpv.net, accepted are only integer values with percent sign added. Default: 50%" help = "Set the initial window size in percent. Please note that this setting is only partly implemented in mpv.net, accepted are only integer values with percent sign added. Default: 50%"
[[settings]]
name = "remember-height"
default = "yes"
filter = "Screen"
help = "mpv.net specific setting to remember the window height, otherwise the video's native resolution is used."
options = [{ name = "yes" },
{ name = "no" }]
[[settings]] [[settings]]
name = "keep-open-pause" name = "keep-open-pause"
default = "yes" default = "yes"
@@ -282,7 +274,7 @@ options = [{ name = "yes", help = "Don't terminate if the current file is the
[[settings]] [[settings]]
name = "loop-file" name = "loop-file"
filter = "Playback" filter = "Playback"
help = "Loop a single file N times. inf means forever, no means normal playback.\n\nThe difference to loop-playlist is that this doesn't loop the playlist, just the file itself. If the playlist contains only a single file, the difference between the two option is that this option performs a seek on loop, instead of reloading the file. loop is an alias for this option." help = "<N|inf|no> Loop a single file N times. inf means forever, no means normal playback.\n\nThe difference to loop-playlist is that this doesn't loop the playlist, just the file itself. If the playlist contains only a single file, the difference between the two option is that this option performs a seek on loop, instead of reloading the file. loop is an alias for this option."
[[settings]] [[settings]]
name = "save-position-on-quit" name = "save-position-on-quit"

View File

@@ -27,5 +27,21 @@ name = "debug-mode"
default = "no" default = "no"
filter = "General" filter = "General"
help = "mpv.net specific setting that writes debug info to a file located on the desktop." help = "mpv.net specific setting that writes debug info to a file located on the desktop."
options = [{ name = "yes" },
{ name = "no" }]
[[settings]]
name = "start-size"
default = "previous"
filter = "Screen"
help = "mpv.net specific setting to remember the window height in the current session, otherwise the video's native resolution is used."
options = [{ name = "video", help = "Window size is set to native video resolution" },
{ name = "previous", help = "Window size is remembered but only from the current session" }]
[[settings]]
name = "remember-position"
default = "no"
filter = "Screen"
help = "mpv.net specific setting to save the window position on exit."
options = [{ name = "yes" }, options = [{ name = "yes" },
{ name = "no" }] { name = "no" }]

View File

@@ -59,7 +59,7 @@ namespace mpvnet
if (setting.Name == pair.Key) if (setting.Name == pair.Key)
{ {
setting.Value = pair.Value.Trim('\'', '"'); setting.Value = pair.Value.Trim('\'', '"');
setting.StartValue = pair.Value; setting.StartValue = pair.Value.Trim('\'', '"');
continue; continue;
} }
} }

View File

@@ -5,7 +5,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" mc:Ignorable="d"
Title="Learn Input" Height="200" Width="400" WindowStartupLocation="CenterOwner" Title="Learn Input" Height="200" Width="400" WindowStartupLocation="CenterOwner"
ResizeMode="NoResize" Loaded="Window_Loaded" Background="Black" MouseWheel="Window_MouseWheel"> ResizeMode="NoResize" Loaded="Window_Loaded" Background="Black" MouseWheel="Window_MouseWheel" MouseUp="Window_MouseUp" MouseDoubleClick="Window_MouseDoubleClick">
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="*" /> <RowDefinition Height="*" />
@@ -16,8 +16,8 @@
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Label x:Name="MenuLabel" Grid.ColumnSpan="2" Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="16"></Label> <TextBlock x:Name="MenuTextBlock" Grid.ColumnSpan="2" Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="16"></TextBlock>
<Label x:Name="KeyLabel" Grid.Row="1" Grid.ColumnSpan="2" Foreground="White" VerticalAlignment="Top" HorizontalAlignment="Center" FontSize="16"></Label> <TextBlock x:Name="KeyTextBlock" Grid.Row="1" Grid.ColumnSpan="2" Foreground="White" VerticalAlignment="Top" HorizontalAlignment="Center" FontSize="16"></TextBlock>
<Button x:Name="ConfirmButton" Grid.Row="2" Click="ConfirmButton_Click">Confirm</Button> <Button x:Name="ConfirmButton" Grid.Row="2" Click="ConfirmButton_Click">Confirm</Button>
<Button x:Name="ClearButton" Grid.Row="2" Click="ClearButton_Click" Grid.Column="1">Clear</Button> <Button x:Name="ClearButton" Grid.Row="2" Click="ClearButton_Click" Grid.Column="1">Clear</Button>
</Grid> </Grid>

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Interop; using System.Windows.Interop;
@@ -12,10 +13,7 @@ namespace mpvnet
public CommandItem InputItem { get; set; } public CommandItem InputItem { get; set; }
public string NewKey { get; set; } = ""; public string NewKey { get; set; } = "";
public LearnWindow() public LearnWindow() => InitializeComponent();
{
InitializeComponent();
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{ {
@@ -43,8 +41,7 @@ namespace mpvnet
else else
try { try {
text = Convert.ToChar(charValue).ToString().ToLower().Trim(); text = Convert.ToChar(charValue).ToString().ToLower().Trim();
} } catch {}
catch {}
for (int i = 0; i < 13; i++) for (int i = 0; i < 13; i++)
if ("D" + i.ToString() == text) if ("D" + i.ToString() == text)
@@ -144,8 +141,8 @@ namespace mpvnet
void SetKey(string key) void SetKey(string key)
{ {
NewKey = key; NewKey = key;
MenuLabel.Content = InputItem.Path; MenuTextBlock.Text = InputItem.Path;
KeyLabel.Content = key; KeyTextBlock.Text = key;
} }
[DllImport("user32.dll")] [DllImport("user32.dll")]
@@ -295,5 +292,38 @@ namespace mpvnet
else else
SetKey("WHEEL_DOWN"); SetKey("WHEEL_DOWN");
} }
private void Window_MouseUp(object sender, MouseButtonEventArgs e)
{
switch (e.ChangedButton)
{
case MouseButton.Left:
if (BlockMBTN_LEFT)
BlockMBTN_LEFT = false;
else
SetKey("MBTN_LEFT");
break;
case MouseButton.Middle:
SetKey("MBTN_MID");
break;
case MouseButton.XButton1:
SetKey("MBTN_BACK");
break;
case MouseButton.XButton2:
SetKey("MBTN_FORWARD");
break;
}
}
bool BlockMBTN_LEFT;
private void Window_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
{
SetKey("MBTN_LEFT_DBL");
BlockMBTN_LEFT = true;
}
}
} }
} }

View File

@@ -45,7 +45,7 @@
this.AutoScaleDimensions = new System.Drawing.SizeF(288F, 288F); this.AutoScaleDimensions = new System.Drawing.SizeF(288F, 288F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.BackColor = System.Drawing.Color.Black; this.BackColor = System.Drawing.Color.Black;
this.ClientSize = new System.Drawing.Size(606, 368); this.ClientSize = new System.Drawing.Size(348, 0);
this.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4); this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);

View File

@@ -8,6 +8,7 @@ using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Globalization; using System.Globalization;
using System.Threading.Tasks;
namespace mpvnet namespace mpvnet
{ {
@@ -36,7 +37,6 @@ namespace mpvnet
WPF.WPF.Init(); WPF.WPF.Init();
System.Windows.Application.Current.ShutdownMode = System.Windows.ShutdownMode.OnExplicitShutdown; System.Windows.Application.Current.ShutdownMode = System.Windows.ShutdownMode.OnExplicitShutdown;
Hwnd = Handle; Hwnd = Handle;
MinimumSize = new Size(FontHeight * 16, FontHeight * 9);
Text += " " + Application.ProductVersion; Text += " " + Application.ProductVersion;
object recent = RegHelp.GetObject(App.RegPath, "Recent"); object recent = RegHelp.GetObject(App.RegPath, "Recent");
@@ -46,11 +46,40 @@ namespace mpvnet
else else
RecentFiles = new List<string>(); RecentFiles = new List<string>();
var dummy = mp.Conf;
App.ProcessCommandLineEarly(); App.ProcessCommandLineEarly();
if (mp.Screen == -1) mp.Screen = Array.IndexOf(Screen.AllScreens, Screen.PrimaryScreen); if (mp.Screen == -1) mp.Screen = Array.IndexOf(Screen.AllScreens, Screen.PrimaryScreen);
SetScreen(mp.Screen); int targetIndex = mp.Screen;
CycleFullscreen(mp.Fullscreen); Screen[] screens = Screen.AllScreens;
if (targetIndex < 0) targetIndex = 0;
if (targetIndex > screens.Length - 1) targetIndex = screens.Length - 1;
Screen screen = screens[Array.IndexOf(screens, screens[targetIndex])];
Rectangle target = screen.Bounds;
Left = target.X + (target.Width - Width) / 2;
Top = target.Y + (target.Height - Height) / 2;
int posX = RegHelp.GetInt(App.RegPath, "PosX");
int posY = RegHelp.GetInt(App.RegPath, "PosY");
if (posX != 0 && posY != 0 && App.RememberPosition)
{
Left = posX - Width / 2;
Top = posY - Height / 2;
}
mp.Shutdown += Shutdown;
mp.VideoSizeChanged += VideoSizeChanged;
mp.FileLoaded += FileLoaded;
mp.Idle += Idle;
Task.Run(() => mp.Init());
mp.VideoSizeAutoResetEvent.WaitOne(1000);
mp.observe_property_bool("fullscreen", PropChangeFullscreen);
mp.observe_property_bool("ontop", PropChangeOnTop);
mp.observe_property_bool("border", PropChangeBorder);
mp.observe_property_string("sid", PropChangeSid);
mp.observe_property_string("aid", PropChangeAid);
mp.observe_property_string("vid", PropChangeVid);
mp.observe_property_int("edition", PropChangeEdition);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -60,7 +89,7 @@ namespace mpvnet
public MenuItem FindMenuItem(string text) => FindMenuItem(text, ContextMenu.Items); public MenuItem FindMenuItem(string text) => FindMenuItem(text, ContextMenu.Items);
void Idle() => BeginInvoke(new Action(() => { Text = "mpv.net " + Application.ProductVersion; })); void Idle() => BeginInvoke(new Action(() => Text = "mpv.net " + Application.ProductVersion));
void CM_Popup(object sender, EventArgs e) => CursorHelp.Show(); void CM_Popup(object sender, EventArgs e) => CursorHelp.Show();
@@ -192,53 +221,91 @@ namespace mpvnet
return null; return null;
} }
protected void SetScreen(int targetIndex) bool WasInitialSizeSet;
{
Screen[] screens = Screen.AllScreens;
if (targetIndex < 0) targetIndex = 0;
if (targetIndex > screens.Length - 1) targetIndex = screens.Length - 1;
SetScreen(screens[Array.IndexOf(screens, screens[targetIndex])]);
}
protected void SetScreen(Screen screen)
{
Rectangle target = screen.Bounds;
Left = target.X + Convert.ToInt32((target.Width - Width) / 2.0);
Top = target.Y + Convert.ToInt32((target.Height - Height) / 2.0);
SetStartFormPositionAndSize();
}
void SetStartFormPositionAndSize()
{
if (IsFullscreen || mp.VideoSize.Width == 0) return;
Screen screen = Screen.FromControl(this);
int height = Convert.ToInt32(screen.Bounds.Height * mp.Autofit);
int width = Convert.ToInt32(height * mp.VideoSize.Width / (double)mp.VideoSize.Height);
Point middlePos = new Point(Left + Width / 2, Top + Height / 2);
var rect = new Native.RECT(new Rectangle(screen.Bounds.X, screen.Bounds.Y, width, height));
NativeHelp.AddWindowBorders(Handle, ref rect);
int left = middlePos.X - rect.Width / 2;
int top = middlePos.Y - rect.Height / 2;
Native.SetWindowPos(Handle, IntPtr.Zero /* HWND_TOP */, left, top, rect.Width, rect.Height, 4 /* SWP_NOZORDER */);
}
void SetFormPosAndSize() void SetFormPosAndSize()
{ {
if (IsFullscreen || mp.VideoSize.Width == 0) return; if (mp.Fullscreen)
{
CycleFullscreen(true);
return;
}
Screen screen = Screen.FromControl(this); Screen screen = Screen.FromControl(this);
int height = mp.VideoSize.Height; int autoFitHeight = Convert.ToInt32(screen.Bounds.Height * mp.Autofit);
if (mp.RememberHeight) height = ClientSize.Height;
if (height > screen.Bounds.Height * 0.9) height = Convert.ToInt32(screen.Bounds.Height * mp.Autofit); if (mp.VideoSize.Height == 0 || mp.VideoSize.Width == 0 ||
int width = Convert.ToInt32(height * mp.VideoSize.Width / (double)mp.VideoSize.Height); mp.VideoSize.Width / (float)mp.VideoSize.Height < 1.3)
mp.VideoSize = new Size((int)(autoFitHeight * 1.7), autoFitHeight);
Size size = mp.VideoSize;
int height = size.Height;
if (App.RememberHeight)
{
if (WasInitialSizeSet)
height = ClientSize.Height;
else
{
height = autoFitHeight;
WasInitialSizeSet = true;
}
}
if (height > screen.Bounds.Height * 0.9)
height = autoFitHeight;
int width = Convert.ToInt32(height * size.Width / (double)size.Height);
Point middlePos = new Point(Left + Width / 2, Top + Height / 2); Point middlePos = new Point(Left + Width / 2, Top + Height / 2);
var rect = new Native.RECT(new Rectangle(screen.Bounds.X, screen.Bounds.Y, width, height)); var rect = new Native.RECT(new Rectangle(screen.Bounds.X, screen.Bounds.Y, width, height));
NativeHelp.AddWindowBorders(Handle, ref rect); NativeHelp.AddWindowBorders(Handle, ref rect);
int left = middlePos.X - rect.Width / 2; int left = middlePos.X - rect.Width / 2;
int top = middlePos.Y - rect.Height / 2; int top = middlePos.Y - rect.Height / 2;
Screen[] screens = Screen.AllScreens; Screen[] screens = Screen.AllScreens;
int minLeft = screens.Select(val => val.WorkingArea.X).Min();
int maxRight = screens.Select(val => val.WorkingArea.Right).Max();
int minTop = screens.Select(val => val.WorkingArea.Y).Min();
int maxBottom = screens.Select(val => val.WorkingArea.Bottom).Max();
if (left < minLeft) left = minLeft;
if (left + rect.Width > maxRight) left = maxRight - rect.Width;
if (top < minTop) top = minTop;
if (top + rect.Height > maxBottom) top = maxBottom - rect.Height;
Native.SetWindowPos(Handle, IntPtr.Zero /* HWND_TOP */, left, top, rect.Width, rect.Height, 4 /* SWP_NOZORDER */); Native.SetWindowPos(Handle, IntPtr.Zero /* HWND_TOP */, left, top, rect.Width, rect.Height, 4 /* SWP_NOZORDER */);
} }
public void CycleFullscreen(bool enabled)
{
mp.Fullscreen = enabled;
if (enabled)
{
if (WindowState != FormWindowState.Maximized)
{
FormBorderStyle = FormBorderStyle.None;
WindowState = FormWindowState.Maximized;
}
}
else
{
if (WindowState == FormWindowState.Maximized)
{
WindowState = FormWindowState.Normal;
if (mp.Border)
FormBorderStyle = FormBorderStyle.Sizable;
else
FormBorderStyle = FormBorderStyle.None;
SetFormPosAndSize();
}
}
}
public void BuildMenu() public void BuildMenu()
{ {
string content = File.ReadAllText(mp.InputConfPath); string content = File.ReadAllText(mp.InputConfPath);
@@ -292,27 +359,12 @@ namespace mpvnet
{ {
Msg.ShowError(e.ExceptionObject.ToString()); Msg.ShowError(e.ExceptionObject.ToString());
} }
public void CycleFullscreen(bool enabled)
{
if (enabled)
{
if (WindowState != FormWindowState.Maximized)
{
FormBorderStyle = FormBorderStyle.None;
WindowState = FormWindowState.Maximized;
}
}
else
{
WindowState = FormWindowState.Normal;
if (mp.Border) protected override CreateParams CreateParams {
FormBorderStyle = FormBorderStyle.Sizable; get {
else CreateParams cp = base.CreateParams;
FormBorderStyle = FormBorderStyle.None; cp.Style |= 0x00020000 /* WS_MINIMIZEBOX */;
return cp;
SetFormPosAndSize();
} }
} }
@@ -324,18 +376,24 @@ namespace mpvnet
{ {
case 0x0201: // WM_LBUTTONDOWN case 0x0201: // WM_LBUTTONDOWN
case 0x0202: // WM_LBUTTONUP case 0x0202: // WM_LBUTTONUP
case 0x0207: // WM_MBUTTONDOWN
case 0x0208: // WM_MBUTTONUP
case 0x020A: // WM_MOUSEWHEEL
case 0x020C: // WM_XBUTTONUP
case 0x020B: // WM_XBUTTONDOWN
case 0x0100: // WM_KEYDOWN case 0x0100: // WM_KEYDOWN
case 0x0101: // WM_KEYUP case 0x0101: // WM_KEYUP
case 0x0104: // WM_SYSKEYDOWN case 0x0104: // WM_SYSKEYDOWN
case 0x0105: // WM_SYSKEYUP case 0x0105: // WM_SYSKEYUP
case 0x020A: // WM_MOUSEWHEEL
if (mp.WindowHandle != IntPtr.Zero) if (mp.WindowHandle != IntPtr.Zero)
Native.SendMessage(mp.WindowHandle, m.Msg, m.WParam, m.LParam); Native.SendMessage(mp.WindowHandle, m.Msg, m.WParam, m.LParam);
break; break;
case 0x0200: // WM_MOUSEMOVE case 0x0200: // WM_MOUSEMOVE
Point pos = PointToClient(Cursor.Position); {
mp.command_string($"mouse {pos.X} {pos.Y}"); Point pos = PointToClient(Cursor.Position);
if (CursorHelp.IsPosDifferent(LastCursorPosChanged)) CursorHelp.Show(); mp.command_string($"mouse {pos.X} {pos.Y}");
if (CursorHelp.IsPosDifferent(LastCursorPosChanged)) CursorHelp.Show();
}
break; break;
case 0x2a3: // WM_MOUSELEAVE case 0x2a3: // WM_MOUSELEAVE
mp.command_string("mouse 1 1"); // osc won't always auto hide mp.command_string("mouse 1 1"); // osc won't always auto hide
@@ -345,7 +403,10 @@ namespace mpvnet
Native.PostMessage(mp.WindowHandle, m.Msg, m.WParam, m.LParam); Native.PostMessage(mp.WindowHandle, m.Msg, m.WParam, m.LParam);
break; break;
case 0x203: // Native.WM.LBUTTONDBLCLK case 0x203: // Native.WM.LBUTTONDBLCLK
if (!IsMouseInOSC()) mp.command_string("cycle fullscreen"); {
Point pos = PointToClient(Cursor.Position);
mp.command_string($"mouse {pos.X} {pos.Y} 0 double");
}
break; break;
case 0x02E0: // WM_DPICHANGED case 0x02E0: // WM_DPICHANGED
if (IgnoreDpiChanged) break; if (IgnoreDpiChanged) break;
@@ -363,22 +424,17 @@ namespace mpvnet
int[] d_corners = { d_w, d_h, -d_w, -d_h }; int[] d_corners = { d_w, d_h, -d_w, -d_h };
int[] corners = { rc.Left, rc.Top, rc.Right, rc.Bottom }; int[] corners = { rc.Left, rc.Top, rc.Right, rc.Bottom };
int corner = NativeHelp.GetResizeBorder(m.WParam.ToInt32()); int corner = NativeHelp.GetResizeBorder(m.WParam.ToInt32());
if (corner >= 0) corners[corner] -= d_corners[corner];
if (corner >= 0)
corners[corner] -= d_corners[corner];
Marshal.StructureToPtr<Native.RECT>(new Native.RECT(corners[0], corners[1], corners[2], corners[3]), m.LParam, false); Marshal.StructureToPtr<Native.RECT>(new Native.RECT(corners[0], corners[1], corners[2], corners[3]), m.LParam, false);
m.Result = new IntPtr(1); m.Result = new IntPtr(1);
return; return;
} case 0x004A: // WM_COPYDATA
var copyData = (Native.COPYDATASTRUCT)m.GetLParam(typeof(Native.COPYDATASTRUCT));
string[] files = copyData.lpData.Split('\n');
string mode = files[0];
files = files.Skip(1).ToArray();
if (m.Msg == SingleProcess.Message) switch (mode)
{
object filesObject = RegHelp.GetObject(App.RegPath, "ShellFiles");
if (filesObject is string[] files)
{
switch (RegHelp.GetString(App.RegPath, "ProcessInstanceMode"))
{ {
case "single": case "single":
mp.Load(files, true, Control.ModifierKeys.HasFlag(Keys.Control)); mp.Load(files, true, Control.ModifierKeys.HasFlag(Keys.Control));
@@ -388,11 +444,9 @@ namespace mpvnet
mp.commandv("loadfile", file, "append"); mp.commandv("loadfile", file, "append");
break; break;
} }
}
RegHelp.RemoveValue(App.RegPath, "ShellFiles"); Activate();
Activate(); return;
return;
} }
base.WndProc(ref m); base.WndProc(ref m);
@@ -452,23 +506,6 @@ namespace mpvnet
})); }));
} }
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
mp.Init();
mp.observe_property_bool("fullscreen", PropChangeFullscreen);
mp.observe_property_bool("ontop", PropChangeOnTop);
mp.observe_property_bool("border", PropChangeBorder);
mp.observe_property_string("sid", PropChangeSid);
mp.observe_property_string("aid", PropChangeAid);
mp.observe_property_string("vid", PropChangeVid);
mp.observe_property_int("edition", PropChangeEdition);
mp.Shutdown += Shutdown;
mp.VideoSizeChanged += VideoSizeChanged;
mp.FileLoaded += FileLoaded;
mp.Idle += Idle;
}
protected override void OnShown(EventArgs e) protected override void OnShown(EventArgs e)
{ {
base.OnShown(e); base.OnShown(e);
@@ -479,13 +516,13 @@ namespace mpvnet
BuildMenu(); BuildMenu();
ContextMenuStrip = ContextMenu; ContextMenuStrip = ContextMenu;
IgnoreDpiChanged = false; IgnoreDpiChanged = false;
CheckUrlInClipboard(); CheckClipboardForURL();
} }
protected override void OnActivated(EventArgs e) protected override void OnActivated(EventArgs e)
{ {
base.OnActivated(e); base.OnActivated(e);
CheckUrlInClipboard(); CheckClipboardForURL();
Message m = new Message() { Msg = 0x0202 }; // WM_LBUTTONUP Message m = new Message() { Msg = 0x0202 }; // WM_LBUTTONUP
Native.SendMessage(Handle, m.Msg, m.WParam, m.LParam); Native.SendMessage(Handle, m.Msg, m.WParam, m.LParam);
} }
@@ -496,13 +533,20 @@ namespace mpvnet
if (mp.IsLogoVisible) mp.ShowLogo(); if (mp.IsLogoVisible) mp.ShowLogo();
} }
protected override void OnFormClosed(FormClosedEventArgs e) protected override void OnFormClosing(FormClosingEventArgs e)
{ {
base.OnFormClosed(e); base.OnFormClosing(e);
if (WindowState == FormWindowState.Normal)
{
RegHelp.SetObject(App.RegPath, "PosX", Left + Width / 2);
RegHelp.SetObject(App.RegPath, "PosY", Top + Height / 2);
}
RegHelp.SetObject(App.RegPath, "Recent", RecentFiles.ToArray()); RegHelp.SetObject(App.RegPath, "Recent", RecentFiles.ToArray());
App.Exit(); App.Exit();
mp.commandv("quit"); mp.commandv("quit");
mp.AutoResetEvent.WaitOne(3000); mp.ShutdownAutoResetEvent.WaitOne(3000);
} }
protected override void OnMouseDown(MouseEventArgs e) protected override void OnMouseDown(MouseEventArgs e)
@@ -528,7 +572,7 @@ namespace mpvnet
CursorHelp.Show(); CursorHelp.Show();
} }
void CheckUrlInClipboard() void CheckClipboardForURL()
{ {
string clipboard = Clipboard.GetText(); string clipboard = Clipboard.GetText();

View File

@@ -144,7 +144,7 @@ namespace mpvnet
MPV_END_FILE_REASON_REDIRECT = 5 MPV_END_FILE_REASON_REDIRECT = 5
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct mpv_event_log_message public struct mpv_event_log_message
{ {
public string prefix; public string prefix;

View File

@@ -58,9 +58,10 @@ namespace mpvnet
public static List<KeyValuePair<string, Action<bool>>> BoolPropChangeActions { get; set; } = new List<KeyValuePair<string, Action<bool>>>(); public static List<KeyValuePair<string, Action<bool>>> BoolPropChangeActions { get; set; } = new List<KeyValuePair<string, Action<bool>>>();
public static List<KeyValuePair<string, Action<int>>> IntPropChangeActions { get; set; } = new List<KeyValuePair<string, Action<int>>>(); public static List<KeyValuePair<string, Action<int>>> IntPropChangeActions { get; set; } = new List<KeyValuePair<string, Action<int>>>();
public static List<KeyValuePair<string, Action<string>>> StringPropChangeActions { get; set; } = new List<KeyValuePair<string, Action<string>>>(); public static List<KeyValuePair<string, Action<string>>> StringPropChangeActions { get; set; } = new List<KeyValuePair<string, Action<string>>>();
public static Size VideoSize { get; set; } = new Size(1920, 1080); public static Size VideoSize { get; set; }
public static List<PythonScript> PythonScripts { get; set; } = new List<PythonScript>(); public static List<PythonScript> PythonScripts { get; set; } = new List<PythonScript>();
public static AutoResetEvent AutoResetEvent { get; set; } = new AutoResetEvent(false); public static AutoResetEvent ShutdownAutoResetEvent { get; set; } = new AutoResetEvent(false);
public static AutoResetEvent VideoSizeAutoResetEvent { get; set; } = new AutoResetEvent(false);
public static List<MediaTrack> MediaTracks { get; set; } = new List<MediaTrack>(); public static List<MediaTrack> MediaTracks { get; set; } = new List<MediaTrack>();
public static List<KeyValuePair<string, double>> Chapters { get; set; } = new List<KeyValuePair<string, double>>(); public static List<KeyValuePair<string, double>> Chapters { get; set; } = new List<KeyValuePair<string, double>>();
@@ -72,13 +73,31 @@ namespace mpvnet
public static bool Fullscreen { get; set; } public static bool Fullscreen { get; set; }
public static bool Border { get; set; } = true; public static bool Border { get; set; } = true;
public static bool RememberHeight { get; set; } = true;
public static int Screen { get; set; } = -1; public static int Screen { get; set; } = -1;
public static int Edition { get; set; } public static int Edition { get; set; }
public static float Autofit { get; set; } = 0.5f; public static float Autofit { get; set; } = 0.5f;
static string LastPlaybackRestartFile;
public static void Init()
{
LoadLibrary("mpv-1.dll");
Handle = mpv_create();
set_property_string("osc", "yes");
set_property_string("config", "yes");
set_property_string("wid", MainForm.Hwnd.ToString());
set_property_string("force-window", "yes");
set_property_string("input-media-keys", "yes");
mpv_initialize(Handle);
ShowLogo();
ProcessCommandLine();
Task.Run(() => { LoadScripts(); });
Task.Run(() => { Addon = new Addon(); });
Task.Run(() => { EventLoop(); });
}
public static void ProcessProperty(string name, string value) public static void ProcessProperty(string name, string value)
{ {
switch (name) switch (name)
@@ -92,7 +111,6 @@ namespace mpvnet
case "fullscreen": Fullscreen = value == "yes"; break; case "fullscreen": Fullscreen = value == "yes"; break;
case "border": Border = value == "yes"; break; case "border": Border = value == "yes"; break;
case "screen": Screen = Convert.ToInt32(value); break; case "screen": Screen = Convert.ToInt32(value); break;
case "remember-height": RememberHeight = value == "yes"; break;
} }
} }
@@ -161,24 +179,6 @@ namespace mpvnet
} }
} }
public static void Init()
{
string dummy = ConfFolder;
LoadLibrary("mpv-1.dll");
Handle = mpv_create();
set_property_string("osc", "yes");
set_property_string("config", "yes");
set_property_string("wid", MainForm.Hwnd.ToString());
set_property_string("force-window", "yes");
set_property_string("input-media-keys", "yes");
mpv_initialize(Handle);
ShowLogo();
ProcessCommandLine();
Task.Run(() => { LoadScripts(); });
Task.Run(() => { Addon = new Addon(); });
Task.Run(() => { EventLoop(); });
}
public static void LoadScripts() public static void LoadScripts()
{ {
string[] startupScripts = Directory.GetFiles(Application.StartupPath + "\\Scripts"); string[] startupScripts = Directory.GetFiles(Application.StartupPath + "\\Scripts");
@@ -213,7 +213,7 @@ namespace mpvnet
if (WindowHandle == IntPtr.Zero) if (WindowHandle == IntPtr.Zero)
WindowHandle = FindWindowEx(MainForm.Hwnd, IntPtr.Zero, "mpv", null); WindowHandle = FindWindowEx(MainForm.Hwnd, IntPtr.Zero, "mpv", null);
//System.Diagnostics.Debug.WriteLine(evt.event_id.ToString()); // System.Diagnostics.Debug.WriteLine(evt.event_id.ToString());
try try
{ {
@@ -222,7 +222,7 @@ namespace mpvnet
case mpv_event_id.MPV_EVENT_SHUTDOWN: case mpv_event_id.MPV_EVENT_SHUTDOWN:
Shutdown?.Invoke(); Shutdown?.Invoke();
WriteHistory(null); WriteHistory(null);
AutoResetEvent.Set(); ShutdownAutoResetEvent.Set();
return; return;
case mpv_event_id.MPV_EVENT_LOG_MESSAGE: case mpv_event_id.MPV_EVENT_LOG_MESSAGE:
LogMessage?.Invoke(); LogMessage?.Invoke();
@@ -274,27 +274,8 @@ namespace mpvnet
case mpv_event_id.MPV_EVENT_CLIENT_MESSAGE: case mpv_event_id.MPV_EVENT_CLIENT_MESSAGE:
var client_messageData = (mpv_event_client_message)Marshal.PtrToStructure(evt.data, typeof(mpv_event_client_message)); var client_messageData = (mpv_event_client_message)Marshal.PtrToStructure(evt.data, typeof(mpv_event_client_message));
string[] args = NativeUtf8StrArray2ManagedStrArray(client_messageData.args, client_messageData.num_args); string[] args = NativeUtf8StrArray2ManagedStrArray(client_messageData.args, client_messageData.num_args);
if (args.Length > 1 && args[0] == "mpv.net") if (args.Length > 1 && args[0] == "mpv.net")
{ Command.Execute(args[1], args.Skip(2).ToArray());
bool found = false;
foreach (Command i in Command.Commands)
{
if (args[1] == i.Name)
{
found = true;
i.Action.Invoke(args.Skip(2).ToArray());
}
}
if (!found)
{
List<string> names = mpvnet.Command.Commands.Select((item) => item.Name).ToList();
names.Sort();
Msg.ShowError($"No command '{args[1]}' found.", $"Available commands are:\n\n{string.Join("\n", names)}\n\nHow to bind these commands can be seen in the [default input bindings and menu definition](https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/inputConf.txt).");
}
}
else if (args.Length > 0) else if (args.Length > 0)
ClientMessage?.Invoke(args); ClientMessage?.Invoke(args);
break; break;
@@ -330,15 +311,21 @@ namespace mpvnet
break; break;
case mpv_event_id.MPV_EVENT_PLAYBACK_RESTART: case mpv_event_id.MPV_EVENT_PLAYBACK_RESTART:
PlaybackRestart?.Invoke(); PlaybackRestart?.Invoke();
Size vidSize = new Size(get_property_int("dwidth"), get_property_int("dheight")); string path = get_property_string("path");
if (LastPlaybackRestartFile != path)
if (VideoSize != vidSize && vidSize != Size.Empty)
{ {
VideoSize = vidSize; Size vidSize = new Size(get_property_int("dwidth"), get_property_int("dheight"));
VideoSizeChanged?.Invoke(); if (vidSize.Width == 0 || vidSize.Height == 0)
} vidSize = new Size(1, 1);
if (VideoSize != vidSize)
Task.Run(new Action(() => ReadMetaData())); {
VideoSize = vidSize;
VideoSizeChanged?.Invoke();
}
VideoSizeAutoResetEvent.Set();
Task.Run(new Action(() => ReadMetaData()));
LastPlaybackRestartFile = path;
}
break; break;
case mpv_event_id.MPV_EVENT_CHAPTER_CHANGE: case mpv_event_id.MPV_EVENT_CHAPTER_CHANGE:
ChapterChange?.Invoke(); ChapterChange?.Invoke();
@@ -535,6 +522,12 @@ namespace mpvnet
Load(files.ToArray(), App.ProcessInstance != "queue", Control.ModifierKeys.HasFlag(Keys.Control)); Load(files.ToArray(), App.ProcessInstance != "queue", Control.ModifierKeys.HasFlag(Keys.Control));
if (files.Count == 0)
{
VideoSizeAutoResetEvent.Set();
VideoSizeChanged?.Invoke();
}
foreach (string i in args) foreach (string i in args)
{ {
if (i.StartsWith("--")) if (i.StartsWith("--"))
@@ -558,11 +551,18 @@ namespace mpvnet
} }
} }
public static DateTime LastLoad;
public static void Load(string[] files, bool loadFolder, bool append) public static void Load(string[] files, bool loadFolder, bool append)
{ {
if (files is null || files.Length == 0) return; if (files is null || files.Length == 0) return;
HideLogo(); HideLogo();
if ((DateTime.Now - LastLoad).TotalMilliseconds < 500)
append = true;
LastLoad = DateTime.Now;
for (int i = 0; i < files.Length; i++) for (int i = 0; i < files.Length; i++)
if (App.SubtitleTypes.Contains(Path.GetExtension(files[i]).TrimStart('.').ToLower())) if (App.SubtitleTypes.Contains(Path.GetExtension(files[i]).TrimStart('.').ToLower()))
commandv("sub-add", files[i]); commandv("sub-add", files[i]);
@@ -579,7 +579,7 @@ namespace mpvnet
public static void LoadFolder() public static void LoadFolder()
{ {
Thread.Sleep(50); // user reported race condition Thread.Sleep(200); // user reported race condition
string path = get_property_string("path"); string path = get_property_string("path");
if (!File.Exists(path) || get_property_int("playlist-count") != 1) return; if (!File.Exists(path) || get_property_int("playlist-count") != 1) return;
List<string> files = Directory.GetFiles(Path.GetDirectoryName(path)).ToList(); List<string> files = Directory.GetFiles(Path.GetDirectoryName(path)).ToList();
@@ -660,18 +660,20 @@ namespace mpvnet
if (MainForm.Instance is null) return; if (MainForm.Instance is null) return;
Rectangle cr = MainForm.Instance.ClientRectangle; Rectangle cr = MainForm.Instance.ClientRectangle;
if (cr.Width == 0 || cr.Height == 0) return; if (cr.Width == 0 || cr.Height == 0) return;
int len = cr.Height / 5;
using (Bitmap b = new Bitmap(cr.Width, cr.Height)) using (Bitmap b = new Bitmap(len, len))
{ {
using (Graphics g = Graphics.FromImage(b)) using (Graphics g = Graphics.FromImage(b))
{ {
g.InterpolationMode = InterpolationMode.HighQualityBicubic; g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.Clear(Color.Black); g.Clear(Color.Black);
int iconWidth = cr.Height / 7; Rectangle rect = new Rectangle(0, 0, len, len);
Rectangle r = new Rectangle(cr.Width / 2 - iconWidth / 2, cr.Height / 2 - iconWidth / 2, iconWidth, iconWidth); g.DrawImage(Properties.Resources.mpvnet, rect);
g.DrawImage(Properties.Resources.mpvnet, r); BitmapData bd = b.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppPArgb);
BitmapData bd = b.LockBits(cr, ImageLockMode.ReadOnly, PixelFormat.Format32bppPArgb); int x = Convert.ToInt32((cr.Width - len) / 2.0);
commandv("overlay-add", "0", "0", "0", "&" + bd.Scan0.ToInt64().ToString(), "0", "bgra", bd.Width.ToString(), bd.Height.ToString(), bd.Stride.ToString()); int y = Convert.ToInt32(((cr.Height - len) / 2.0) * 0.9);
commandv("overlay-add", "0", $"{x}", $"{y}", "&" + bd.Scan0.ToInt64().ToString(), "0", "bgra", bd.Width.ToString(), bd.Height.ToString(), bd.Stride.ToString());
b.UnlockBits(bd); b.UnlockBits(bd);
IsLogoVisible = true; IsLogoVisible = true;
} }