From 5ab1b1dd66a54c795b35ba5e8ebe0c8bf246ee4d Mon Sep 17 00:00:00 2001 From: YangSpring <99802662+YangSpring114@users.noreply.github.com> Date: Mon, 18 Nov 2024 22:03:20 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20Linux=20X11=20=E7=8E=AF?= =?UTF-8?q?=E5=A2=83=E4=B8=8B=E7=9A=84=E7=AA=97=E5=8F=A3=E5=A4=84=E7=90=86?= =?UTF-8?q?=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Media/Behaviors/WindowTitleBarBehavior.cs | 112 ++++++------------ WonderLab/Platform/Linux/X11/WindowHandler.cs | 77 ++++++++++++ WonderLab/Platform/MacOS/WindowHandler.cs | 62 ++++++++++ WonderLab/WonderLab.csproj | 99 +++++++++------- 4 files changed, 230 insertions(+), 120 deletions(-) create mode 100644 WonderLab/Platform/Linux/X11/WindowHandler.cs create mode 100644 WonderLab/Platform/MacOS/WindowHandler.cs diff --git a/WonderLab/Controls/Media/Behaviors/WindowTitleBarBehavior.cs b/WonderLab/Controls/Media/Behaviors/WindowTitleBarBehavior.cs index 2cd4136a..8038d143 100644 --- a/WonderLab/Controls/Media/Behaviors/WindowTitleBarBehavior.cs +++ b/WonderLab/Controls/Media/Behaviors/WindowTitleBarBehavior.cs @@ -1,13 +1,13 @@ -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; @@ -15,37 +15,47 @@ public sealed class WindowTitleBarBehavior : Behavior { 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); @@ -53,66 +63,14 @@ private async void OnOpened(object sender, EventArgs e) { } } } - } - - 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); + } } +#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); } \ No newline at end of file +} \ No newline at end of file diff --git a/WonderLab/Platform/Linux/X11/WindowHandler.cs b/WonderLab/Platform/Linux/X11/WindowHandler.cs new file mode 100644 index 00000000..fa98e304 --- /dev/null +++ b/WonderLab/Platform/Linux/X11/WindowHandler.cs @@ -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 +} \ No newline at end of file diff --git a/WonderLab/Platform/MacOS/WindowHandler.cs b/WonderLab/Platform/MacOS/WindowHandler.cs new file mode 100644 index 00000000..ba7d95ff --- /dev/null +++ b/WonderLab/Platform/MacOS/WindowHandler.cs @@ -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; + } +} \ No newline at end of file diff --git a/WonderLab/WonderLab.csproj b/WonderLab/WonderLab.csproj index 40591840..333a446d 100644 --- a/WonderLab/WonderLab.csproj +++ b/WonderLab/WonderLab.csproj @@ -5,6 +5,17 @@ latest net8.0 false + true + + + + WINDOWS + + + LINUX + + + OSX @@ -12,43 +23,44 @@ - - - - - - - - - - - + + + + + + + + + + + - - - - - - - + + + + + + + - - - - - - - + + + + + + + + @@ -63,33 +75,34 @@ + - + - - True - True - Languages.resx - - - ResXFileCodeGenerator - Languages.Designer.cs - + + True + True + Languages.resx + + + ResXFileCodeGenerator + Languages.Designer.cs + - - True - True - Languages.resx - - - SettingNavigationPage.axaml - + + True + True + Languages.resx + + + SettingNavigationPage.axaml +