|
|
@@ -0,0 +1,190 @@
|
|
|
+using System;
|
|
|
+using Avalonia.Controls.Platform;
|
|
|
+using Avalonia.Platform;
|
|
|
+using Avalonia.VisualTree;
|
|
|
+using static Avalonia.X11.XLib;
|
|
|
+namespace Avalonia.X11
|
|
|
+{
|
|
|
+ // TODO: Actually implement XEmbed instead of simply using XReparentWindow
|
|
|
+ class X11NativeControlHost : INativeControlHostImpl
|
|
|
+ {
|
|
|
+ private readonly AvaloniaX11Platform _platform;
|
|
|
+ public X11Window Window { get; }
|
|
|
+
|
|
|
+ public X11NativeControlHost(AvaloniaX11Platform platform, X11Window window)
|
|
|
+ {
|
|
|
+ _platform = platform;
|
|
|
+ Window = window;
|
|
|
+ }
|
|
|
+
|
|
|
+ public INativeControlHostDestroyableControlHandle CreateDefaultChild(IPlatformHandle parent)
|
|
|
+ {
|
|
|
+ var ch = new DumbWindow(_platform.Info);
|
|
|
+ XSync(_platform.Display, false);
|
|
|
+ return ch;
|
|
|
+ }
|
|
|
+
|
|
|
+ public INativeControlHostControlTopLevelAttachment CreateNewAttachment(Func<IPlatformHandle, IPlatformHandle> create)
|
|
|
+ {
|
|
|
+ var holder = new DumbWindow(_platform.Info, Window.Handle.Handle);
|
|
|
+ Attachment attachment = null;
|
|
|
+ try
|
|
|
+ {
|
|
|
+ var child = create(holder);
|
|
|
+ // ReSharper disable once UseObjectOrCollectionInitializer
|
|
|
+ // It has to be assigned to the variable before property setter is called so we dispose it on exception
|
|
|
+ attachment = new Attachment(_platform.Display, holder, _platform.OrphanedWindow, child);
|
|
|
+ attachment.AttachedTo = this;
|
|
|
+ return attachment;
|
|
|
+ }
|
|
|
+ catch
|
|
|
+ {
|
|
|
+ attachment?.Dispose();
|
|
|
+ holder?.Destroy();
|
|
|
+ throw;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public INativeControlHostControlTopLevelAttachment CreateNewAttachment(IPlatformHandle handle)
|
|
|
+ {
|
|
|
+ if (!IsCompatibleWith(handle))
|
|
|
+ throw new ArgumentException(handle.HandleDescriptor + " is not compatible with the current window",
|
|
|
+ nameof(handle));
|
|
|
+ var attachment = new Attachment(_platform.Display, new DumbWindow(_platform.Info, Window.Handle.Handle),
|
|
|
+ _platform.OrphanedWindow, handle) { AttachedTo = this };
|
|
|
+ return attachment;
|
|
|
+ }
|
|
|
+
|
|
|
+ public bool IsCompatibleWith(IPlatformHandle handle) => handle.HandleDescriptor == "XID";
|
|
|
+
|
|
|
+ class DumbWindow : INativeControlHostDestroyableControlHandle
|
|
|
+ {
|
|
|
+ private readonly IntPtr _display;
|
|
|
+
|
|
|
+ public DumbWindow(X11Info x11, IntPtr? parent = null)
|
|
|
+ {
|
|
|
+ _display = x11.Display;
|
|
|
+ /*Handle = XCreateSimpleWindow(x11.Display, XLib.XDefaultRootWindow(_display),
|
|
|
+ 0, 0, 1, 1, 0, IntPtr.Zero, IntPtr.Zero);*/
|
|
|
+ var attr = new XSetWindowAttributes
|
|
|
+ {
|
|
|
+ backing_store = 1,
|
|
|
+ bit_gravity = Gravity.NorthWestGravity,
|
|
|
+ win_gravity = Gravity.NorthWestGravity,
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ parent = parent ?? XDefaultRootWindow(x11.Display);
|
|
|
+
|
|
|
+ Handle = XCreateWindow(_display, parent.Value, 0, 0,
|
|
|
+ 1,1, 0, 0,
|
|
|
+ (int)CreateWindowArgs.InputOutput,
|
|
|
+ IntPtr.Zero,
|
|
|
+ new UIntPtr((uint)(SetWindowValuemask.BorderPixel | SetWindowValuemask.BitGravity |
|
|
|
+ SetWindowValuemask.BackPixel |
|
|
|
+ SetWindowValuemask.WinGravity | SetWindowValuemask.BackingStore)), ref attr);
|
|
|
+ }
|
|
|
+
|
|
|
+ public IntPtr Handle { get; private set; }
|
|
|
+ public string HandleDescriptor => "XID";
|
|
|
+ public void Destroy()
|
|
|
+ {
|
|
|
+ if (Handle != IntPtr.Zero)
|
|
|
+ {
|
|
|
+ XDestroyWindow(_display, Handle);
|
|
|
+ Handle = IntPtr.Zero;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ class Attachment : INativeControlHostControlTopLevelAttachment
|
|
|
+ {
|
|
|
+ private readonly IntPtr _display;
|
|
|
+ private readonly IntPtr _orphanedWindow;
|
|
|
+ private DumbWindow _holder;
|
|
|
+ private IPlatformHandle _child;
|
|
|
+ private X11NativeControlHost _attachedTo;
|
|
|
+ private bool _mapped;
|
|
|
+
|
|
|
+ public Attachment(IntPtr display, DumbWindow holder, IntPtr orphanedWindow, IPlatformHandle child)
|
|
|
+ {
|
|
|
+ _display = display;
|
|
|
+ _orphanedWindow = orphanedWindow;
|
|
|
+ _holder = holder;
|
|
|
+ _child = child;
|
|
|
+ XReparentWindow(_display, child.Handle, holder.Handle, 0, 0);
|
|
|
+ XMapWindow(_display, child.Handle);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void Dispose()
|
|
|
+ {
|
|
|
+ if (_child != null)
|
|
|
+ {
|
|
|
+ XReparentWindow(_display, _child.Handle, _orphanedWindow, 0, 0);
|
|
|
+ _child = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ _holder?.Destroy();
|
|
|
+ _holder = null;
|
|
|
+ _attachedTo = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ void CheckDisposed()
|
|
|
+ {
|
|
|
+ if (_child == null)
|
|
|
+ throw new ObjectDisposedException("X11 INativeControlHostControlTopLevelAttachment");
|
|
|
+ }
|
|
|
+
|
|
|
+ public INativeControlHostImpl AttachedTo
|
|
|
+ {
|
|
|
+ get => _attachedTo;
|
|
|
+ set
|
|
|
+ {
|
|
|
+ CheckDisposed();
|
|
|
+ _attachedTo = (X11NativeControlHost)value;
|
|
|
+ if (value == null)
|
|
|
+ {
|
|
|
+ _mapped = false;
|
|
|
+ XUnmapWindow(_display, _holder.Handle);
|
|
|
+ XReparentWindow(_display, _holder.Handle, _orphanedWindow, 0, 0);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ XReparentWindow(_display, _holder.Handle, _attachedTo.Window.Handle.Handle, 0, 0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public bool IsCompatibleWith(INativeControlHostImpl host) => host is X11NativeControlHost;
|
|
|
+
|
|
|
+ public void Hide()
|
|
|
+ {
|
|
|
+ if(_attachedTo == null || _child == null)
|
|
|
+ return;
|
|
|
+ _mapped = false;
|
|
|
+ XUnmapWindow(_display, _holder.Handle);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void ShowInBounds(TransformedBounds transformedBounds)
|
|
|
+ {
|
|
|
+ CheckDisposed();
|
|
|
+ if (_attachedTo == null)
|
|
|
+ throw new InvalidOperationException("The control isn't currently attached to a toplevel");
|
|
|
+ var bounds = transformedBounds.Bounds.TransformToAABB(transformedBounds.Transform) *
|
|
|
+ new Vector(_attachedTo.Window.Scaling, _attachedTo.Window.Scaling);
|
|
|
+ var pixelRect = new PixelRect((int)bounds.X, (int)bounds.Y, Math.Max(1, (int)bounds.Width),
|
|
|
+ Math.Max(1, (int)bounds.Height));
|
|
|
+ XMoveResizeWindow(_display, _child.Handle, 0, 0, pixelRect.Width, pixelRect.Height);
|
|
|
+ XMoveResizeWindow(_display, _holder.Handle, pixelRect.X, pixelRect.Y, pixelRect.Width,
|
|
|
+ pixelRect.Height);
|
|
|
+ if (!_mapped)
|
|
|
+ {
|
|
|
+ XMapWindow(_display, _holder.Handle);
|
|
|
+ XRaiseWindow(_display, _holder.Handle);
|
|
|
+ _mapped = true;
|
|
|
+ }
|
|
|
+ Console.WriteLine($"Moved {_child.Handle} to {pixelRect}");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|