-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,118 +1,76 @@ | ||
using System; | ||
using System.Diagnostics; | ||
using System.Runtime.InteropServices; | ||
using System.Threading.Tasks; | ||
using Avalonia; | ||
using Avalonia; | ||
using Avalonia.Controls; | ||
using Avalonia.Interactivity; | ||
using Avalonia.Markup.Xaml; | ||
|
||
using Avalonia.Xaml.Interactivity; | ||
using System; | ||
|
||
#if OSX | ||
using WonderLab.Platform.MacOS; | ||
#elif LINUX | ||
using WonderLab.Platform.Linux; | ||
#endif | ||
|
||
namespace WonderLab.Controls.Media.Behaviors; | ||
|
||
public sealed class WindowTitleBarBehavior : Behavior<Window> { | ||
protected override void OnAttached() { | ||
base.OnAttached(); | ||
|
||
AssociatedObject.Opened+= OnOpened; | ||
AssociatedObject.Opened += OnOpened; | ||
AssociatedObject.PropertyChanged += OnPropertyChanged; | ||
} | ||
|
||
private bool IsWayland() { | ||
string sessionType = Environment.GetEnvironmentVariable("XDG_SESSION_TYPE").ToLower(); | ||
return sessionType is "wayland"; | ||
} | ||
|
||
private void OnPropertyChanged(object sender, AvaloniaPropertyChangedEventArgs e) { | ||
#if OSX | ||
if (e.Property == Window.WindowStateProperty || e.Property == Window.BoundsProperty) { | ||
var platform = AssociatedObject.TryGetPlatformHandle(); | ||
if (platform is not null) { | ||
var nsWindow = platform.Handle; | ||
if (nsWindow != IntPtr.Zero) { | ||
try { | ||
AdjustTitleBarButtonPosition(nsWindow); | ||
HideZoomButton(nsWindow); | ||
} | ||
catch (Exception exception) { | ||
WindowHandler.RefreshTitleBarButtonPosition(nsWindow); | ||
WindowHandler.HideZoomButton(nsWindow); | ||
} catch (Exception exception) { | ||
Console.WriteLine(exception); | ||
throw; | ||
} | ||
} | ||
} | ||
} | ||
#elif Unix | ||
|
||
|
||
#endif | ||
} | ||
|
||
private async void OnOpened(object sender, EventArgs e) { | ||
private void OnOpened(object sender, EventArgs e) { | ||
#if OSX | ||
var platform = AssociatedObject.TryGetPlatformHandle(); | ||
if (platform is not null) { | ||
var nsWindow = platform.Handle; | ||
if (nsWindow != IntPtr.Zero) { | ||
try { | ||
AdjustTitleBarButtonPosition(nsWindow); | ||
HideZoomButton(nsWindow); | ||
WindowHandler.RefreshTitleBarButtonPosition(nsWindow); | ||
WindowHandler.HideZoomButton(nsWindow); | ||
} | ||
catch (Exception exception) { | ||
Console.WriteLine(exception); | ||
throw; | ||
} | ||
} | ||
} | ||
} | ||
|
||
private void AdjustTitleBarButtonPosition(IntPtr nsWindow) { | ||
var selStandardWindowButton = sel_registerName("standardWindowButton:"); | ||
var selSetFrameOrigin = sel_registerName("setFrameOrigin:"); | ||
|
||
if (selStandardWindowButton == IntPtr.Zero || selSetFrameOrigin == IntPtr.Zero) { | ||
throw new NullReferenceException(); | ||
} | ||
|
||
var closeButton = objc_msgSend_IntPtr_IntPtr(nsWindow, selStandardWindowButton, (IntPtr)0); // NSWindowButtonClose | ||
var minimizeButton = objc_msgSend_IntPtr_IntPtr(nsWindow, selStandardWindowButton, (IntPtr)1); // NSWindowButtonMiniaturize | ||
var zoomButton = objc_msgSend_IntPtr_IntPtr(nsWindow, selStandardWindowButton, (IntPtr)2); // NSWindowButtonZoom | ||
|
||
var newCloseButtonPosition = new CGPoint(30, -2); | ||
var newMinimizeButtonPosition = new CGPoint(50, -2); | ||
var newZoomButtonPosition = new CGPoint(0, 200); | ||
|
||
objc_msgSend_CGPoint(closeButton, selSetFrameOrigin, newCloseButtonPosition); | ||
objc_msgSend_CGPoint(minimizeButton, selSetFrameOrigin, newMinimizeButtonPosition); | ||
objc_msgSend_CGPoint(zoomButton, selSetFrameOrigin, newZoomButtonPosition); | ||
|
||
} | ||
|
||
private void HideZoomButton(IntPtr nsWindow) { | ||
var selStandardWindowButton = sel_registerName("standardWindowButton:"); | ||
var selSetHidden = sel_registerName("setHidden:"); | ||
|
||
var zoomButton = objc_msgSend_IntPtr_IntPtr(nsWindow, selStandardWindowButton, (IntPtr)3); // NSWindowButtonZoom | ||
|
||
objc_msgSend_Bool(zoomButton,selSetHidden, true); | ||
} | ||
|
||
[StructLayout(LayoutKind.Sequential)] | ||
public struct CGPoint { | ||
public double X; | ||
public double Y; | ||
|
||
public CGPoint(double x, double y) { | ||
X = x; | ||
Y = y; | ||
#elif LINUX | ||
var platform = AssociatedObject.TryGetPlatformHandle(); | ||
if (platform is not null) { | ||
if (IsWayland()) { | ||
} else { | ||
Platform.Linux.X11.WindowHandler.HideTitleBar(platform.Handle); | ||
Check warning on line 71 in WonderLab/Controls/Media/Behaviors/WindowTitleBarBehavior.cs GitHub Actions / test_build
Check warning on line 71 in WonderLab/Controls/Media/Behaviors/WindowTitleBarBehavior.cs GitHub Actions / test_build
Check warning on line 71 in WonderLab/Controls/Media/Behaviors/WindowTitleBarBehavior.cs GitHub Actions / build_MacOS
Check warning on line 71 in WonderLab/Controls/Media/Behaviors/WindowTitleBarBehavior.cs GitHub Actions / build_Linux (x64)
Check warning on line 71 in WonderLab/Controls/Media/Behaviors/WindowTitleBarBehavior.cs GitHub Actions / build_Linux (arm)
Check warning on line 71 in WonderLab/Controls/Media/Behaviors/WindowTitleBarBehavior.cs GitHub Actions / build_Linux (arm64)
|
||
} | ||
} | ||
#endif | ||
} | ||
|
||
[DllImport("libobjc.dylib", EntryPoint = "objc_msgSend")] | ||
private static extern IntPtr objc_msgSend_IntPtr_IntPtr(IntPtr receiver, IntPtr selector, IntPtr arg1); | ||
|
||
[DllImport("libobjc.dylib", EntryPoint = "objc_msgSend")] | ||
private static extern void objc_msgSend_CGPoint(IntPtr receiver, IntPtr selector, CGPoint arg1); | ||
|
||
[DllImport("libobjc.dylib", EntryPoint = "objc_msgSend")] | ||
private static extern IntPtr objc_msgSend_Bool(IntPtr receiver, IntPtr selector, bool arg1); | ||
|
||
[DllImport("libobjc.dylib", EntryPoint = "sel_registerName")] | ||
private static extern IntPtr sel_registerName(string name); | ||
} | ||
|
||
public static class Dlfcn { | ||
[DllImport("libSystem.B.dylib", EntryPoint = "dlopen")] | ||
public static extern IntPtr dlopen(string path, int mode); | ||
|
||
[DllImport("libSystem.B.dylib", EntryPoint = "dlsym")] | ||
public static extern IntPtr dlsym(IntPtr handle, string symbol); } | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
using System.Runtime.InteropServices; | ||
using System; | ||
using System.Runtime.Versioning; | ||
|
||
namespace WonderLab.Platform.Linux.X11; | ||
|
||
[SupportedOSPlatform("Linux")] | ||
public static class WindowHandler { | ||
public const string X11Library = "libX11.so"; | ||
|
||
public static void HideTitleBar(IntPtr windowHandle) { | ||
IntPtr display = XOpenDisplay(IntPtr.Zero); | ||
if (display == IntPtr.Zero) { | ||
throw new ArgumentNullException(); | ||
} | ||
|
||
//get window atom | ||
IntPtr mwmHintsProperty = XInternAtom(display, "_MOTIF_WM_HINTS", false); | ||
if (mwmHintsProperty == IntPtr.Zero) { | ||
throw new ArgumentNullException(); | ||
} | ||
|
||
MotifWmHints hints = new MotifWmHints { | ||
Flags = (IntPtr)(HintsFlags.Functions | HintsFlags.Decorations), | ||
Functions = IntPtr.Zero, | ||
Decorations = IntPtr.Zero, | ||
InputMode = IntPtr.Zero, | ||
Status = IntPtr.Zero, | ||
}; | ||
|
||
IntPtr hintsPtr = Marshal.AllocHGlobal(Marshal.SizeOf(hints)); | ||
Marshal.StructureToPtr(hints, hintsPtr, false); | ||
_ = XChangeProperty(display, windowHandle, mwmHintsProperty, mwmHintsProperty, 32, PropertyMode.Replace, hintsPtr, 5); | ||
Marshal.FreeHGlobal(hintsPtr); | ||
|
||
XFlush(display); | ||
XCloseDisplay(display); | ||
} | ||
|
||
[DllImport(X11Library)] | ||
public static extern IntPtr XOpenDisplay(IntPtr display); | ||
|
||
[DllImport(X11Library)] | ||
public static extern int XCloseDisplay(IntPtr display); | ||
|
||
[DllImport(X11Library)] | ||
public static extern IntPtr XInternAtom(IntPtr display, string atom_name, bool only_if_exists); | ||
|
||
[DllImport(X11Library)] | ||
public static extern int XChangeProperty(IntPtr display, IntPtr w, IntPtr property, IntPtr type, int format, PropertyMode mode, IntPtr data, int nelements); | ||
|
||
[DllImport(X11Library)] | ||
public static extern int XFlush(IntPtr display); | ||
} | ||
|
||
[StructLayout(LayoutKind.Sequential)] | ||
public struct MotifWmHints { | ||
public IntPtr Flags; | ||
public IntPtr Functions; | ||
public IntPtr Decorations; | ||
public IntPtr InputMode; | ||
public IntPtr Status; | ||
} | ||
|
||
[Flags] | ||
public enum HintsFlags : long { | ||
Functions = 1L << 0, | ||
Decorations = 1L << 1, | ||
InputMode = 1L << 2, | ||
Status = 1L << 3 | ||
} | ||
|
||
public enum PropertyMode { | ||
Replace = 0, | ||
Prepend = 1, | ||
Append = 2 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
using static WonderLab.Controls.Media.Behaviors.WindowTitleBarBehavior; | ||
using System.Runtime.InteropServices; | ||
using System; | ||
using System.Runtime.Versioning; | ||
|
||
namespace WonderLab.Platform.MacOS; | ||
|
||
[SupportedOSPlatform("MacOS")] | ||
public static class WindowHandler { | ||
public static void HideZoomButton(IntPtr nsWindow) { | ||
var selStandardWindowButton = sel_registerName("standardWindowButton:"); | ||
var selSetHidden = sel_registerName("setHidden:"); | ||
|
||
var zoomButton = objc_msgSend_IntPtr_IntPtr(nsWindow, selStandardWindowButton, (IntPtr)2); | ||
objc_msgSend_Bool(zoomButton, selSetHidden, true); | ||
} | ||
|
||
public static void RefreshTitleBarButtonPosition(IntPtr nsWindow) { | ||
var selStandardWindowButton = sel_registerName("standardWindowButton:"); | ||
var selSetFrameOrigin = sel_registerName("setFrameOrigin:"); | ||
|
||
if (selStandardWindowButton == IntPtr.Zero || selSetFrameOrigin == IntPtr.Zero) { | ||
throw new NullReferenceException(); | ||
} | ||
|
||
var closeButton = objc_msgSend_IntPtr_IntPtr(nsWindow, selStandardWindowButton, (IntPtr)0); | ||
var minimizeButton = objc_msgSend_IntPtr_IntPtr(nsWindow, selStandardWindowButton, (IntPtr)1); | ||
var zoomButton = objc_msgSend_IntPtr_IntPtr(nsWindow, selStandardWindowButton, (IntPtr)2); | ||
|
||
var newCloseButtonPosition = new CGPoint(30, -2); | ||
var newMinimizeButtonPosition = new CGPoint(50, -2); | ||
var newZoomButtonPosition = new CGPoint(0, 200); | ||
|
||
objc_msgSend_CGPoint(closeButton, selSetFrameOrigin, newCloseButtonPosition); | ||
objc_msgSend_CGPoint(minimizeButton, selSetFrameOrigin, newMinimizeButtonPosition); | ||
objc_msgSend_CGPoint(zoomButton, selSetFrameOrigin, newZoomButtonPosition); | ||
|
||
} | ||
|
||
[DllImport("libobjc.dylib", EntryPoint = "objc_msgSend")] | ||
public static extern IntPtr objc_msgSend_IntPtr_IntPtr(IntPtr receiver, IntPtr selector, IntPtr arg1); | ||
|
||
[DllImport("libobjc.dylib", EntryPoint = "objc_msgSend")] | ||
public static extern void objc_msgSend_CGPoint(IntPtr receiver, IntPtr selector, CGPoint arg1); | ||
|
||
[DllImport("libobjc.dylib", EntryPoint = "objc_msgSend")] | ||
public static extern IntPtr objc_msgSend_Bool(IntPtr receiver, IntPtr selector, bool arg1); | ||
|
||
[DllImport("libobjc.dylib", EntryPoint = "sel_registerName")] | ||
public static extern IntPtr sel_registerName(string name); | ||
} | ||
|
||
[StructLayout(LayoutKind.Sequential)] | ||
public struct CGPoint { | ||
public double X; | ||
public double Y; | ||
|
||
public CGPoint(double x, double y) { | ||
X = x; | ||
Y = y; | ||
} | ||
} |