diff --git a/packages/Avalonia/AvaloniaBuildTasks.targets b/packages/Avalonia/AvaloniaBuildTasks.targets index 45a7f1aa44..de3830ffea 100644 --- a/packages/Avalonia/AvaloniaBuildTasks.targets +++ b/packages/Avalonia/AvaloniaBuildTasks.targets @@ -42,12 +42,24 @@ - $(BuildAvaloniaResourcesDependsOn);AddAvaloniaResources;ResolveReferences + $(BuildAvaloniaResourcesDependsOn);AddAvaloniaResources;ResolveReferences;_GenerateAvaloniaResourcesDependencyCache + + + + + + + + + + + + diff --git a/src/Avalonia.Controls/Design.cs b/src/Avalonia.Controls/Design.cs index 0d05e19e53..07d2918a88 100644 --- a/src/Avalonia.Controls/Design.cs +++ b/src/Avalonia.Controls/Design.cs @@ -60,6 +60,19 @@ namespace Avalonia.Controls return target.GetValue(PreviewWithProperty); } + public static readonly AttachedProperty DesignStyleProperty = AvaloniaProperty + .RegisterAttached("DesignStyle", typeof(Design)); + + public static void SetDesignStyle(Control control, IStyle value) + { + control.SetValue(DesignStyleProperty, value); + } + + public static IStyle GetDesignStyle(Control control) + { + return control.GetValue(DesignStyleProperty); + } + public static void ApplyDesignModeProperties(Control target, Control source) { if (source.IsSet(WidthProperty)) @@ -68,6 +81,8 @@ namespace Avalonia.Controls target.Height = source.GetValue(HeightProperty); if (source.IsSet(DataContextProperty)) target.DataContext = source.GetValue(DataContextProperty); + if (source.IsSet(DesignStyleProperty)) + target.Styles.Add(source.GetValue(DesignStyleProperty)); } } } diff --git a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs index 209feb351c..726d2c596c 100644 --- a/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs +++ b/src/Avalonia.Controls/Platform/DefaultMenuInteractionHandler.cs @@ -303,7 +303,8 @@ namespace Avalonia.Controls.Platform { item.Parent.SelectedItem.Close(); SelectItemAndAncestors(item); - Open(item, false); + if (item.HasSubMenu) + Open(item, false); } else { diff --git a/src/Avalonia.Themes.Fluent/Controls/NotificationCard.xaml b/src/Avalonia.Themes.Fluent/Controls/NotificationCard.xaml index c06b501f82..924d977eb5 100644 --- a/src/Avalonia.Themes.Fluent/Controls/NotificationCard.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/NotificationCard.xaml @@ -17,7 +17,7 @@ - + - + - - + + diff --git a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs index 3bd8e05ee2..6c84cfd55c 100644 --- a/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs +++ b/src/Avalonia.Visuals/Rendering/DeferredRenderer.cs @@ -35,6 +35,8 @@ namespace Avalonia.Rendering private IRef _currentDraw; private readonly IDeferredRendererLock _lock; private readonly object _sceneLock = new object(); + private readonly object _startStopLock = new object(); + private readonly object _renderLoopIsRenderingLock = new object(); private readonly Action _updateSceneIfNeededDelegate; /// @@ -139,6 +141,8 @@ namespace Avalonia.Rendering } Stop(); + // Wait for any in-progress rendering to complete + lock(_renderLoopIsRenderingLock){} DisposeRenderTarget(); } @@ -233,20 +237,26 @@ namespace Avalonia.Rendering /// public void Start() { - if (!_running && _renderLoop != null) + lock (_startStopLock) { - _renderLoop.Add(this); - _running = true; + if (!_running && _renderLoop != null) + { + _renderLoop.Add(this); + _running = true; + } } } /// public void Stop() { - if (_running && _renderLoop != null) + lock (_startStopLock) { - _renderLoop.Remove(this); - _running = false; + if (_running && _renderLoop != null) + { + _renderLoop.Remove(this); + _running = false; + } } } @@ -255,7 +265,16 @@ namespace Avalonia.Rendering void IRenderLoopTask.Update(TimeSpan time) => UpdateScene(); - void IRenderLoopTask.Render() => Render(false); + void IRenderLoopTask.Render() + { + lock (_renderLoopIsRenderingLock) + { + lock(_startStopLock) + if(!_running) + return; + Render(false); + } + } /// Size IVisualBrushRenderer.GetRenderTargetSize(IVisualBrush brush) diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 149d7fb9b2..f0d2d5ca8a 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -805,13 +805,14 @@ namespace Avalonia.X11 if (_handle != IntPtr.Zero) { - XDestroyWindow(_x11.Display, _handle); _platform.Windows.Remove(_handle); _platform.XI2?.OnWindowDestroyed(_handle); + var handle = _handle; _handle = IntPtr.Zero; Closed?.Invoke(); _mouse.Dispose(); _touch.Dispose(); + XDestroyWindow(_x11.Display, handle); } if (_useRenderWindow && _renderHandle != IntPtr.Zero) diff --git a/src/Linux/Avalonia.LinuxFramebuffer/LockedFramebuffer.cs b/src/Linux/Avalonia.LinuxFramebuffer/LockedFramebuffer.cs deleted file mode 100644 index 87c7b64c26..0000000000 --- a/src/Linux/Avalonia.LinuxFramebuffer/LockedFramebuffer.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using Avalonia.Platform; - -namespace Avalonia.LinuxFramebuffer -{ - unsafe class LockedFramebuffer : ILockedFramebuffer - { - private readonly int _fb; - private readonly fb_fix_screeninfo _fixedInfo; - private fb_var_screeninfo _varInfo; - private readonly IntPtr _address; - - public LockedFramebuffer(int fb, fb_fix_screeninfo fixedInfo, fb_var_screeninfo varInfo, IntPtr address, Vector dpi) - { - _fb = fb; - _fixedInfo = fixedInfo; - _varInfo = varInfo; - _address = address; - Dpi = dpi; - //Use double buffering to avoid flicker - Address = Marshal.AllocHGlobal(RowBytes * Size.Height); - } - - - void VSync() - { - NativeUnsafeMethods.ioctl(_fb, FbIoCtl.FBIO_WAITFORVSYNC, null); - } - - public void Dispose() - { - VSync(); - NativeUnsafeMethods.memcpy(_address, Address, new IntPtr(RowBytes * Size.Height)); - - Marshal.FreeHGlobal(Address); - Address = IntPtr.Zero; - } - - public IntPtr Address { get; private set; } - public PixelSize Size => new PixelSize((int)_varInfo.xres, (int) _varInfo.yres); - public int RowBytes => (int) _fixedInfo.line_length; - public Vector Dpi { get; } - public PixelFormat Format => _varInfo.bits_per_pixel == 16 ? PixelFormat.Rgb565 : _varInfo.blue.offset == 16 ? PixelFormat.Rgba8888 : PixelFormat.Bgra8888; - } -} diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Output/FbDevBackBuffer.cs b/src/Linux/Avalonia.LinuxFramebuffer/Output/FbDevBackBuffer.cs new file mode 100644 index 0000000000..7afad13bb6 --- /dev/null +++ b/src/Linux/Avalonia.LinuxFramebuffer/Output/FbDevBackBuffer.cs @@ -0,0 +1,70 @@ +using System; +using System.Runtime.InteropServices; +using System.Threading; +using Avalonia.Platform; + +namespace Avalonia.LinuxFramebuffer.Output +{ + internal unsafe class FbDevBackBuffer : IDisposable + { + private readonly int _fb; + private readonly fb_fix_screeninfo _fixedInfo; + private readonly fb_var_screeninfo _varInfo; + private readonly IntPtr _targetAddress; + private readonly object _lock = new object(); + + public FbDevBackBuffer(int fb, fb_fix_screeninfo fixedInfo, fb_var_screeninfo varInfo, IntPtr targetAddress) + { + _fb = fb; + _fixedInfo = fixedInfo; + _varInfo = varInfo; + _targetAddress = targetAddress; + Address = Marshal.AllocHGlobal(RowBytes * Size.Height); + } + + + public void Dispose() + { + if (Address != IntPtr.Zero) + { + Marshal.FreeHGlobal(Address); + Address = IntPtr.Zero; + } + } + + public ILockedFramebuffer Lock(Vector dpi) + { + Monitor.Enter(_lock); + try + { + return new LockedFramebuffer(Address, + new PixelSize((int)_varInfo.xres, (int)_varInfo.yres), + (int)_fixedInfo.line_length, dpi, + _varInfo.bits_per_pixel == 16 ? PixelFormat.Rgb565 + : _varInfo.blue.offset == 16 ? PixelFormat.Rgba8888 + : PixelFormat.Bgra8888, + () => + { + try + { + NativeUnsafeMethods.ioctl(_fb, FbIoCtl.FBIO_WAITFORVSYNC, null); + NativeUnsafeMethods.memcpy(_targetAddress, Address, new IntPtr(RowBytes * Size.Height)); + } + finally + { + Monitor.Exit(_lock); + } + }); + } + catch + { + Monitor.Exit(_lock); + throw; + } + } + + public IntPtr Address { get; private set; } + public PixelSize Size => new PixelSize((int)_varInfo.xres, (int) _varInfo.yres); + public int RowBytes => (int) _fixedInfo.line_length; + } +} diff --git a/src/Linux/Avalonia.LinuxFramebuffer/Output/FbdevOutput.cs b/src/Linux/Avalonia.LinuxFramebuffer/Output/FbdevOutput.cs index 61f00b2795..f3f9a12ac8 100644 --- a/src/Linux/Avalonia.LinuxFramebuffer/Output/FbdevOutput.cs +++ b/src/Linux/Avalonia.LinuxFramebuffer/Output/FbdevOutput.cs @@ -14,6 +14,7 @@ namespace Avalonia.LinuxFramebuffer private fb_var_screeninfo _varInfo; private IntPtr _mappedLength; private IntPtr _mappedAddress; + private FbDevBackBuffer _backBuffer; public double Scaling { get; set; } /// @@ -146,7 +147,9 @@ namespace Avalonia.LinuxFramebuffer { if (_fd <= 0) throw new ObjectDisposedException("LinuxFramebuffer"); - return new LockedFramebuffer(_fd, _fixedInfo, _varInfo, _mappedAddress, new Vector(96, 96) * Scaling); + return (_backBuffer ??= + new FbDevBackBuffer(_fd, _fixedInfo, _varInfo, _mappedAddress)) + .Lock(new Vector(96, 96) * Scaling); } @@ -165,6 +168,8 @@ namespace Avalonia.LinuxFramebuffer public void Dispose() { + _backBuffer?.Dispose(); + _backBuffer = null; ReleaseUnmanagedResources(); GC.SuppressFinalize(this); } diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index ad409810b8..6a6bcc5715 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -808,6 +808,31 @@ namespace Avalonia.Win32.Interop MNC_SELECT = 3 } + public enum SysCommands + { + SC_SIZE = 0xF000, + SC_MOVE = 0xF010, + SC_MINIMIZE = 0xF020, + SC_MAXIMIZE = 0xF030, + SC_NEXTWINDOW = 0xF040, + SC_PREVWINDOW = 0xF050, + SC_CLOSE = 0xF060, + SC_VSCROLL = 0xF070, + SC_HSCROLL = 0xF080, + SC_MOUSEMENU = 0xF090, + SC_KEYMENU = 0xF100, + SC_ARRANGE = 0xF110, + SC_RESTORE = 0xF120, + SC_TASKLIST = 0xF130, + SC_SCREENSAVE = 0xF140, + SC_HOTKEY = 0xF150, + SC_DEFAULT = 0xF160, + SC_MONITORPOWER = 0xF170, + SC_CONTEXTHELP = 0xF180, + SC_SEPARATOR = 0xF00F, + SCF_ISSECURE = 0x00000001, + } + [StructLayout(LayoutKind.Sequential)] public struct RGBQUAD { diff --git a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs index d163b3d068..f7ed2f215f 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs @@ -123,6 +123,12 @@ namespace Avalonia.Win32 break; } + case WindowsMessage.WM_SYSCOMMAND: + // Disable system handling of Alt/F10 menu keys. + if ((SysCommands)wParam == SysCommands.SC_KEYMENU && HighWord(ToInt32(lParam)) <= 0) + return IntPtr.Zero; + break; + case WindowsMessage.WM_MENUCHAR: { // mute the system beep