From ec682e2e1de5b3004c3e629d9b2ac9caa96b55be Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Mon, 14 Jan 2019 13:23:16 +0300 Subject: [PATCH] [X11] Implemented icons --- src/Avalonia.X11/Stubs.cs | 49 ------------- src/Avalonia.X11/X11Framebuffer.cs | 6 +- src/Avalonia.X11/X11FramebufferSurface.cs | 2 +- src/Avalonia.X11/X11IconLoader.cs | 85 +++++++++++++++++++++++ src/Avalonia.X11/X11Platform.cs | 3 +- src/Avalonia.X11/X11Window.cs | 6 +- 6 files changed, 96 insertions(+), 55 deletions(-) create mode 100644 src/Avalonia.X11/X11IconLoader.cs diff --git a/src/Avalonia.X11/Stubs.cs b/src/Avalonia.X11/Stubs.cs index 91d788c576..ec694ba9a8 100644 --- a/src/Avalonia.X11/Stubs.cs +++ b/src/Avalonia.X11/Stubs.cs @@ -14,53 +14,4 @@ namespace Avalonia.X11 public Size DoubleClickSize { get; } = new Size(2, 2); public TimeSpan DoubleClickTime { get; } = TimeSpan.FromMilliseconds(500); } - - class SystemDialogsStub : ISystemDialogImpl - { - public Task ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent) - { - return Task.FromResult((string[])null); - } - - public Task ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent) - { - return Task.FromResult((string)null); - } - } - - class IconLoaderStub : IPlatformIconLoader - { - class FakeIcon : IWindowIconImpl - { - private readonly byte[] _data; - - public FakeIcon(byte[] data) - { - _data = data; - } - public void Save(Stream outputStream) - { - outputStream.Write(_data, 0, _data.Length); - } - } - - public IWindowIconImpl LoadIcon(string fileName) - { - return new FakeIcon(File.ReadAllBytes(fileName)); - } - - public IWindowIconImpl LoadIcon(Stream stream) - { - var ms = new MemoryStream(); - stream.CopyTo(ms); - return new FakeIcon(ms.ToArray()); - } - - public IWindowIconImpl LoadIcon(IBitmapImpl bitmap) - { - var ms = new MemoryStream(); - bitmap.Save(ms); - return new FakeIcon(ms.ToArray()); - } - } } diff --git a/src/Avalonia.X11/X11Framebuffer.cs b/src/Avalonia.X11/X11Framebuffer.cs index d99d7970b6..00288f300d 100644 --- a/src/Avalonia.X11/X11Framebuffer.cs +++ b/src/Avalonia.X11/X11Framebuffer.cs @@ -9,12 +9,14 @@ namespace Avalonia.X11 { private readonly IntPtr _display; private readonly IntPtr _xid; + private readonly int _depth; private IUnmanagedBlob _blob; - public X11Framebuffer(IntPtr display, IntPtr xid, int width, int height, double factor) + public X11Framebuffer(IntPtr display, IntPtr xid, int depth, int width, int height, double factor) { _display = display; _xid = xid; + _depth = depth; Size = new PixelSize(width, height); RowBytes = width * 4; Dpi = new Vector(96, 96) * factor; @@ -35,7 +37,7 @@ namespace Avalonia.X11 image.bitmap_unit = bitsPerPixel; image.bitmap_bit_order = 0;// LSBFirst; image.bitmap_pad = bitsPerPixel; - image.depth = 24; + image.depth = _depth; image.bytes_per_line = RowBytes; image.bits_per_pixel = bitsPerPixel; XLockDisplay(_display); diff --git a/src/Avalonia.X11/X11FramebufferSurface.cs b/src/Avalonia.X11/X11FramebufferSurface.cs index 6caec0a66f..70220cebcd 100644 --- a/src/Avalonia.X11/X11FramebufferSurface.cs +++ b/src/Avalonia.X11/X11FramebufferSurface.cs @@ -23,7 +23,7 @@ namespace Avalonia.X11 XGetGeometry(_display, _xid, out var root, out var x, out var y, out var width, out var height, out var bw, out var d); XUnlockDisplay(_display); - return new X11Framebuffer(_display, _xid, width, height, _scaling()); + return new X11Framebuffer(_display, _xid, 24,width, height, _scaling()); } } } diff --git a/src/Avalonia.X11/X11IconLoader.cs b/src/Avalonia.X11/X11IconLoader.cs new file mode 100644 index 0000000000..2c57dca157 --- /dev/null +++ b/src/Avalonia.X11/X11IconLoader.cs @@ -0,0 +1,85 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using Avalonia.Controls.Platform.Surfaces; +using Avalonia.Media; +using Avalonia.Media.Imaging; +using Avalonia.Platform; +using Avalonia.Utilities; +using Avalonia.Visuals.Media.Imaging; +using static Avalonia.X11.XLib; +namespace Avalonia.X11 +{ + class X11IconLoader : IPlatformIconLoader + { + private readonly X11Info _x11; + + public X11IconLoader(X11Info x11) + { + _x11 = x11; + } + + IWindowIconImpl LoadIcon(Bitmap bitmap) => new X11IconData(bitmap); + + public IWindowIconImpl LoadIcon(string fileName) => LoadIcon(new Bitmap(fileName)); + + public IWindowIconImpl LoadIcon(Stream stream) => LoadIcon(new Bitmap(stream)); + + public IWindowIconImpl LoadIcon(IBitmapImpl bitmap) + { + var ms = new MemoryStream(); + bitmap.Save(ms); + ms.Position = 0; + return LoadIcon(ms); + } + } + + unsafe class X11IconData : IWindowIconImpl, IFramebufferPlatformSurface + { + private readonly Bitmap _bitmap; + private int _width; + private int _height; + private uint[] _bdata; + public IntPtr[] Data { get; } + + public X11IconData(Bitmap bitmap) + { + _bitmap = bitmap; + _width = Math.Min(_bitmap.PixelSize.Width, 128); + _height = Math.Min(_bitmap.PixelSize.Height, 128); + _bdata = new uint[_width * _height]; + fixed (void* ptr = _bdata) + { + var iptr = (int*)ptr; + iptr[0] = _width; + iptr[1] = _height; + } + using(var rt = AvaloniaLocator.Current.GetService().CreateRenderTarget(new[]{this})) + using (var ctx = rt.CreateDrawingContext(null)) + ctx.DrawImage(bitmap.PlatformImpl, 1, new Rect(bitmap.Size), + new Rect(0, 0, _width, _height)); + Data = new IntPtr[_width * _height + 2]; + Data[0] = new IntPtr(_width); + Data[1] = new IntPtr(_height); + for (var y = 0; y < _height; y++) + { + var r = y * _width; + for (var x = 0; x < _width; x++) + Data[r + x] = new IntPtr(_bdata[r + x]); + } + } + + public void Save(Stream outputStream) + { + _bitmap.Save(outputStream); + } + + public ILockedFramebuffer Lock() + { + var h = GCHandle.Alloc(_bdata, GCHandleType.Pinned); + return new LockedFramebuffer(h.AddrOfPinnedObject(), new PixelSize(_width, _height), _width * 4, + new Vector(96, 96), PixelFormat.Bgra8888, + () => h.Free()); + } + } +} diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs index efda30b2fc..3b50dff5c8 100644 --- a/src/Avalonia.X11/X11Platform.cs +++ b/src/Avalonia.X11/X11Platform.cs @@ -43,8 +43,7 @@ namespace Avalonia.X11 .Bind().ToConstant(new X11CursorFactory(Display)) .Bind().ToConstant(new X11Clipboard(this)) .Bind().ToConstant(new PlatformSettingsStub()) - .Bind().ToConstant(new SystemDialogsStub()) - .Bind().ToConstant(new IconLoaderStub()) + .Bind().ToConstant(new X11IconLoader(Info)) .Bind().ToConstant(new Gtk3ForeignX11SystemDialog()); X11Screens = Avalonia.X11.X11Screens.Init(this); diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index b919424efe..85eade8be3 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -800,7 +800,11 @@ namespace Avalonia.X11 public void SetIcon(IWindowIconImpl icon) { - //TODO + var data = ((X11IconData)icon).Data; + fixed (void* pdata = data) + XChangeProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_ICON, + new IntPtr((int)Atom.XA_CARDINAL), 32, PropertyMode.Replace, + pdata, data.Length); } public void ShowTaskbarIcon(bool value)