Skip to content

Commit

Permalink
添加 Linux X11 环境下的窗口处理器
Browse files Browse the repository at this point in the history
  • Loading branch information
YangSpring114 committed Nov 18, 2024
1 parent f6f5057 commit 5ab1b1d
Show file tree
Hide file tree
Showing 4 changed files with 230 additions and 120 deletions.
112 changes: 35 additions & 77 deletions WonderLab/Controls/Media/Behaviors/WindowTitleBarBehavior.cs
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

View workflow job for this annotation

GitHub Actions / test_build

This call site is reachable on all platforms. 'WindowHandler.HideTitleBar(nint)' is only supported on: 'Linux'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1416)

Check warning on line 71 in WonderLab/Controls/Media/Behaviors/WindowTitleBarBehavior.cs

View workflow job for this annotation

GitHub Actions / test_build

This call site is reachable on all platforms. 'WindowHandler.HideTitleBar(nint)' is only supported on: 'Linux'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1416)

Check warning on line 71 in WonderLab/Controls/Media/Behaviors/WindowTitleBarBehavior.cs

View workflow job for this annotation

GitHub Actions / build_MacOS

This call site is reachable on all platforms. 'WindowHandler.HideTitleBar(nint)' is only supported on: 'Linux'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1416)

Check warning on line 71 in WonderLab/Controls/Media/Behaviors/WindowTitleBarBehavior.cs

View workflow job for this annotation

GitHub Actions / build_Linux (x64)

This call site is reachable on all platforms. 'WindowHandler.HideTitleBar(nint)' is only supported on: 'Linux'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1416)

Check warning on line 71 in WonderLab/Controls/Media/Behaviors/WindowTitleBarBehavior.cs

View workflow job for this annotation

GitHub Actions / build_Linux (arm)

This call site is reachable on all platforms. 'WindowHandler.HideTitleBar(nint)' is only supported on: 'Linux'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1416)

Check warning on line 71 in WonderLab/Controls/Media/Behaviors/WindowTitleBarBehavior.cs

View workflow job for this annotation

GitHub Actions / build_Linux (arm64)

This call site is reachable on all platforms. 'WindowHandler.HideTitleBar(nint)' is only supported on: 'Linux'. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1416)
}
}
#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); }
}
77 changes: 77 additions & 0 deletions WonderLab/Platform/Linux/X11/WindowHandler.cs
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
}
62 changes: 62 additions & 0 deletions WonderLab/Platform/MacOS/WindowHandler.cs
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;
}
}
Loading

0 comments on commit 5ab1b1d

Please sign in to comment.