127 lines
6.2 KiB
C#
127 lines
6.2 KiB
C#
using System;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Runtime.InteropServices;
|
|
using System.Xml.Linq;
|
|
|
|
namespace BSO
|
|
{
|
|
/// <summary>
|
|
/// Vollbild im Fenster: macht das Spielfenster randlos und so gross wie den Bildschirm. Das exklusive DirectX-9-Vollbild
|
|
/// des Spiels laeuft unter neuen Windows-Versionen schlecht (Alt-Tab, Abstuerze) - so geht Alt-Tab sofort. Nur wenn
|
|
/// das Spiel im Fenstermodus laeuft (Settings.xml: Fullscreen="FALSE"). F11 schaltet zwischen Vollbild und Fenster.
|
|
/// Am schaerfsten ist es, wenn im Spiel die Aufloesung des Bildschirms eingestellt ist (das Bild wird sonst skaliert).
|
|
/// </summary>
|
|
public static class BorderlessWindow
|
|
{
|
|
[StructLayout(LayoutKind.Sequential)] struct Rect { public int Left, Top, Right, Bottom; }
|
|
[StructLayout(LayoutKind.Sequential)] struct MonitorInfo { public int Size; public Rect Monitor, Work; public uint Flags; }
|
|
|
|
[DllImport("user32.dll")] static extern nint GetWindowLongPtrW(nint window, int index);
|
|
[DllImport("user32.dll")] static extern nint SetWindowLongPtrW(nint window, int index, nint value);
|
|
[DllImport("user32.dll")] static extern bool GetWindowRect(nint window, out Rect rect);
|
|
[DllImport("user32.dll")] static extern bool GetClientRect(nint window, out Rect rect);
|
|
[DllImport("user32.dll")] static extern bool SetWindowPos(nint window, nint after, int x, int y, int w, int h, uint flags);
|
|
[DllImport("user32.dll")] static extern nint MonitorFromWindow(nint window, uint flags);
|
|
[DllImport("user32.dll")] static extern bool GetMonitorInfoW(nint monitor, ref MonitorInfo info);
|
|
|
|
const int GwlStyle = -16, GwlExStyle = -20;
|
|
const long Frame = 0x00C00000 | 0x00040000 | 0x00020000 | 0x00010000; // Titelleiste, Rahmen, Min/Max
|
|
const long ExFrame = 0x1 | 0x100 | 0x200 | 0x20000; // Rahmen-Kanten
|
|
const uint MonitorNearest = 2;
|
|
const uint SwpFrameChanged = 0x20, SwpNoZOrder = 0x4, SwpNoOwnerZOrder = 0x200, SwpShowWindow = 0x40;
|
|
|
|
static bool checkedSettings, allowed, enabled = true, isBorderless;
|
|
static nint window;
|
|
static long savedStyle, savedExStyle;
|
|
static Rect savedRect;
|
|
static readonly Stopwatch checkClock = new();
|
|
|
|
/// <summary>Einmal pro Bild im Hauptthread (dem Thread des Fensters).</summary>
|
|
public static void Update()
|
|
{
|
|
if (!checkedSettings)
|
|
{
|
|
checkedSettings = true;
|
|
allowed = !GameFullscreen();
|
|
if (!allowed) Bootstrap.Log("Spiel laeuft im eigenen Vollbild - BSO-Vollbild bleibt aus");
|
|
}
|
|
if (!allowed) return;
|
|
|
|
if (Input.Pressed(Input.F11))
|
|
{
|
|
enabled = !enabled;
|
|
Bootstrap.Log(enabled ? "Vollbild an" : "Vollbild aus (Fenster)");
|
|
if (!enabled && isBorderless) Restore();
|
|
}
|
|
|
|
// Jede Sekunde pruefen - das Spiel stellt sein Fenster z.B. nach Aenderungen in den Optionen neu ein
|
|
if (checkClock.IsRunning && checkClock.ElapsedMilliseconds < 1000 && !Input.Pressed(Input.F11)) return;
|
|
checkClock.Restart();
|
|
if (window == 0) window = Process.GetCurrentProcess().MainWindowHandle;
|
|
if (window == 0 || !enabled) return;
|
|
|
|
Rect monitor = MonitorRect();
|
|
GetWindowRect(window, out Rect now);
|
|
bool hasFrame = (GetWindowLongPtrW(window, GwlStyle) & Frame) != 0;
|
|
if (isBorderless && !hasFrame && Same(now, monitor)) return;
|
|
MakeBorderless(monitor);
|
|
}
|
|
|
|
static void MakeBorderless(Rect monitor)
|
|
{
|
|
long style = GetWindowLongPtrW(window, GwlStyle), exStyle = GetWindowLongPtrW(window, GwlExStyle);
|
|
if ((style & Frame) != 0) // nur das echte Fenster merken, nicht unser randloses
|
|
{
|
|
savedStyle = style;
|
|
savedExStyle = exStyle;
|
|
GetWindowRect(window, out savedRect);
|
|
}
|
|
SetWindowLongPtrW(window, GwlStyle, (nint)(style & ~Frame));
|
|
SetWindowLongPtrW(window, GwlExStyle, (nint)(exStyle & ~ExFrame));
|
|
SetWindowPos(window, 0, monitor.Left, monitor.Top, monitor.Right - monitor.Left, monitor.Bottom - monitor.Top,
|
|
SwpFrameChanged | SwpNoZOrder | SwpNoOwnerZOrder | SwpShowWindow);
|
|
isBorderless = true;
|
|
|
|
GetClientRect(window, out Rect client);
|
|
Bootstrap.Log($"Vollbild im Fenster: {client.Right}x{client.Bottom} (Bildschirm {monitor.Right - monitor.Left}x{monitor.Bottom - monitor.Top})");
|
|
}
|
|
|
|
static void Restore()
|
|
{
|
|
isBorderless = false;
|
|
if (window == 0 || savedStyle == 0) return;
|
|
SetWindowLongPtrW(window, GwlStyle, (nint)savedStyle);
|
|
SetWindowLongPtrW(window, GwlExStyle, (nint)savedExStyle);
|
|
SetWindowPos(window, 0, savedRect.Left, savedRect.Top, savedRect.Right - savedRect.Left, savedRect.Bottom - savedRect.Top,
|
|
SwpFrameChanged | SwpNoZOrder | SwpNoOwnerZOrder | SwpShowWindow);
|
|
}
|
|
|
|
static Rect MonitorRect()
|
|
{
|
|
var info = new MonitorInfo { Size = Marshal.SizeOf<MonitorInfo>() };
|
|
GetMonitorInfoW(MonitorFromWindow(window, MonitorNearest), ref info);
|
|
return info.Monitor;
|
|
}
|
|
|
|
static bool Same(Rect a, Rect b) => a.Left == b.Left && a.Top == b.Top && a.Right == b.Right && a.Bottom == b.Bottom;
|
|
|
|
/// <summary>Fullscreen-Einstellung des Spiels (%LOCALAPPDATA%\European Bus Simulator 2012\Settings.xml).</summary>
|
|
static bool GameFullscreen()
|
|
{
|
|
try
|
|
{
|
|
string file = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
|
"European Bus Simulator 2012", "Settings.xml");
|
|
string value = (string)XDocument.Load(file).Root?.Element("Graphics")?.Attribute("Fullscreen");
|
|
return string.Equals(value, "TRUE", StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Bootstrap.Log("Settings.xml nicht lesbar: " + e.Message);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|