From f8e392a520caaefc860eb8a05d474a52a1e8279b Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 26 Jan 2017 02:55:48 +0300 Subject: [PATCH] Updated to the recent code base, implemented threading, resize and invalidate handling --- Avalonia.sln | 3 + .../ControlCatalog.Desktop.csproj | 5 +- src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj | 3 +- src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs | 21 +++++- src/Gtk/Avalonia.Gtk3/Interop/GlibTimeout.cs | 64 +++++++++++++++++++ src/Gtk/Avalonia.Gtk3/Interop/Native.cs | 55 +++++++++++++++- src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs | 3 +- src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs | 62 +++++++++++++----- src/Gtk/Avalonia.Gtk3/WindowImpl.cs | 16 +++-- 9 files changed, 203 insertions(+), 29 deletions(-) create mode 100644 src/Gtk/Avalonia.Gtk3/Interop/GlibTimeout.cs diff --git a/Avalonia.sln b/Avalonia.sln index 0eda7e5fc7..46dafa9c76 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -130,6 +130,9 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog", "samples\ControlCatalog\ControlCatalog.csproj", "{D0A739B9-3C68-4BA6-A328-41606954B6BD}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.Desktop", "samples\ControlCatalog.Desktop\ControlCatalog.Desktop.csproj", "{2B888490-D14A-4BCA-AB4B-48676FA93C9B}" + ProjectSection(ProjectDependencies) = postProject + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658} = {BB1F7BB5-6AD4-4776-94D9-C09D0A972658} + EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.iOS", "samples\ControlCatalog.iOS\ControlCatalog.iOS.csproj", "{57E0455D-D565-44BB-B069-EE1AA20F8337}" EndProject diff --git a/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj b/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj index 6fec660e55..fb2eba3c2f 100644 --- a/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj +++ b/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj @@ -5,7 +5,7 @@ Debug AnyCPU {2B888490-D14A-4BCA-AB4B-48676FA93C9B} - WinExe + Exe Properties ControlCatalog.Desktop ControlCatalog.Desktop @@ -33,6 +33,9 @@ prompt 4 + + + ..\..\packages\Serilog.1.5.14\lib\net45\Serilog.dll diff --git a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj index 5e645fb542..df5a712c18 100644 --- a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj +++ b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj @@ -41,6 +41,7 @@ + @@ -64,7 +65,7 @@ {62024B2D-53EB-4638-B26B-85EEAA54866E} Avalonia.Input - + {EB582467-6ABB-43A1-B052-E981BA910E3A} Avalonia.SceneGraph diff --git a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs index 164e987802..faf1983250 100644 --- a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs +++ b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs @@ -10,6 +10,7 @@ using Avalonia.Gtk3.Interop; using Avalonia.Input; using Avalonia.Input.Platform; using Avalonia.Platform; +using Avalonia.Rendering; namespace Avalonia.Gtk3 { @@ -35,6 +36,7 @@ namespace Avalonia.Gtk3 .Bind().ToConstant(Instance) .Bind().ToConstant(Instance) .Bind().ToSingleton() + .Bind().ToConstant(new DefaultRenderLoop(60)) .Bind().ToConstant(new PlatformIconLoaderStub()); } @@ -67,11 +69,28 @@ namespace Avalonia.Gtk3 public IDisposable StartTimer(TimeSpan interval, Action tick) { - return null; + return GlibTimeout.StarTimer((uint) interval.TotalMilliseconds, tick); } + private bool _signaled = false; + object _lock = new object(); + public void Signal() { + lock(_lock) + if (!_signaled) + { + _signaled = true; + GlibTimeout.Add(0, () => + { + lock (_lock) + { + _signaled = false; + } + Signaled?.Invoke(); + return false; + }); + } } public event Action Signaled; diff --git a/src/Gtk/Avalonia.Gtk3/Interop/GlibTimeout.cs b/src/Gtk/Avalonia.Gtk3/Interop/GlibTimeout.cs new file mode 100644 index 0000000000..9971d8881d --- /dev/null +++ b/src/Gtk/Avalonia.Gtk3/Interop/GlibTimeout.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Avalonia.Gtk3.Interop +{ + static class GlibTimeout + { + static bool Handler(IntPtr data) + { + var handle = GCHandle.FromIntPtr(data); + var cb = (Func) handle.Target; + if (!cb()) + { + handle.Free(); + return false; + } + return true; + } + + private static readonly GCHandle PinnedHandle; + private static readonly Native.D.timeout_callback PinnedHandler; + static GlibTimeout() + { + PinnedHandler = Handler; + + } + + + public static void Add(uint interval, Func callback) + { + var handle = GCHandle.Alloc(callback); + Native.GTimeoutAdd(interval, PinnedHandler, GCHandle.ToIntPtr(handle)); + } + + class Timer : IDisposable + { + public bool Stopped; + public void Dispose() + { + + Stopped = true; + } + } + + public static IDisposable StarTimer(uint interval, Action tick) + { + var timer = new Timer (); + GlibTimeout.Add(interval, + () => + { + if (timer.Stopped) + return false; + tick(); + return !timer.Stopped; + }); + + return timer; + } + } +} diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs index a07d84c710..2a5abfe603 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs @@ -36,7 +36,19 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate IntPtr gtk_widget_get_window(IntPtr gtkWidget); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate IntPtr gtk_widget_get_screen(IntPtr gtkWidget); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate IntPtr gtk_widget_set_double_buffered(IntPtr gtkWidget, bool value); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate IntPtr gtk_widget_set_events(IntPtr gtkWidget, uint flags); + + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] + public delegate int gdk_screen_get_height(IntPtr screen); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] + public delegate int gdk_screen_get_width(IntPtr screen); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_widget_realize(IntPtr gtkWidget); @@ -50,12 +62,29 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_window_get_size(IntPtr gtkWindow, out int width, out int height); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate void gtk_window_resize(IntPtr gtkWindow, int width, int height); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate void gtk_widget_queue_draw_area(IntPtr gtkWindow, int x, int y, int width, int height); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] + public delegate void gdk_window_invalidate_rect(IntPtr window, ref GdkRectangle rect, bool invalidate_children); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)] public delegate ulong g_signal_connect_object(IntPtr instance, [MarshalAs(UnmanagedType.AnsiBStr)]string signal, IntPtr handler, IntPtr userData, int flags); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)] public delegate ulong g_signal_handler_disconnect(IntPtr instance, ulong connectionId); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Glib)] + public delegate ulong g_timeout_add(uint interval, timeout_callback callback, IntPtr data); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate bool signal_widget_draw(IntPtr gtkWidget, IntPtr cairoContext, IntPtr userData); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate bool signal_onevent(IntPtr gtkWidget, IntPtr ev, IntPtr userData); + + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate bool timeout_callback(IntPtr data); } @@ -69,12 +98,19 @@ namespace Avalonia.Gtk3.Interop public static D.gtk_widget_hide GtkWidgetHide; public static D.gdk_get_native_handle GetNativeGdkWindowHandle; public static D.gtk_widget_get_window GtkWidgetGetWindow; + public static D.gtk_widget_get_screen GtkWidgetGetScreen; public static D.gtk_widget_realize GtkWidgetRealize; public static D.gtk_window_get_size GtkWindowGetSize; + public static D.gtk_window_resize GtkWindowResize; public static D.g_signal_connect_object GSignalConnectObject; public static D.g_signal_handler_disconnect GSignalHandlerDisconnect; + public static D.g_timeout_add GTimeoutAdd; public static D.gtk_widget_set_double_buffered GtkWidgetSetDoubleBuffered; - + public static D.gtk_widget_set_events GtkWidgetSetEvents; + public static D.gdk_window_invalidate_rect GdkWindowInvalidateRect; + public static D.gtk_widget_queue_draw_area GtkWidgetQueueDrawArea; + public static D.gdk_screen_get_height GdkScreenGetHeight; + public static D.gdk_screen_get_width GdkScreenGetWidth; } @@ -84,4 +120,21 @@ namespace Avalonia.Gtk3.Interop TopLevel, Popup } + + [StructLayout(LayoutKind.Sequential)] + public struct GdkRectangle + { + public int X, Y, Width, Height; + + public static GdkRectangle FromRect(Rect rect) + { + return new GdkRectangle + { + X = (int) rect.X, + Y = (int) rect.Y, + Width = (int) rect.Width, + Height = (int) rect.Height + }; + } + } } diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs b/src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs index 2410af7591..15a2f90ade 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs @@ -84,13 +84,14 @@ namespace Avalonia.Gtk3.Interop fieldInfo.SetValue(null, Marshal.GetDelegateForFunctionPointer(funcPtr, fieldInfo.FieldType)); } - var nativeHandleNames = new[] {"gdk_x11_window_get_xid", "gdk_win32_window_get_handle"}; + var nativeHandleNames = new[] { "gdk_win32_window_get_handle", "gdk_x11_window_get_xid", "gdk_quartz_window_get_nswindow" }; foreach (var name in nativeHandleNames) { try { Native.GetNativeGdkWindowHandle = (Native.D.gdk_get_native_handle)Marshal .GetDelegateForFunctionPointer(loader.GetProcAddress(gdk, name), typeof(Native.D.gdk_get_native_handle)); + break; } catch { } } diff --git a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs index 2f6181c893..672e98fe18 100644 --- a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Diagnostics; using Avalonia.Controls; using Avalonia.Gtk3.Interop; using Avalonia.Input; @@ -9,44 +11,69 @@ namespace Avalonia.Gtk3 { abstract class TopLevelImpl : ITopLevelImpl, IPlatformHandle { - protected readonly IntPtr _gtkWidget; + protected readonly IntPtr GtkWidget; private IInputRoot _inputRoot; + protected readonly List _disposables = new List(); public TopLevelImpl(IntPtr gtkWidget) { - _gtkWidget = gtkWidget; + GtkWidget = gtkWidget; + Native.GtkWidgetSetEvents(gtkWidget, uint.MaxValue); Native.GtkWidgetRealize(gtkWidget); - Native.GtkWidgetSetDoubleBuffered(gtkWidget, false); - Signal.Connect(_gtkWidget, "draw", OnDraw); + Connect("draw", OnDraw); + Connect("configure-event", OnConfigured); } + private bool OnConfigured(IntPtr gtkwidget, IntPtr ev, IntPtr userdata) + { + Debug.WriteLine("Configured"); + Resized?.Invoke(ClientSize); + return false; + } + + + void Connect(string name, T handler) => _disposables.Add(Signal.Connect(GtkWidget, name, handler)); + private bool OnDraw(IntPtr gtkwidget, IntPtr cairocontext, IntPtr userdata) { + Debug.WriteLine("Draw"); Paint?.Invoke(new Rect(ClientSize)); return true; } public void Dispose() { - //STUB + foreach(var d in _disposables) + d.Dispose(); + _disposables.Clear(); + //TODO } public abstract Size ClientSize { get; set; } - public Size MaxClientSize => new Size(1024, 768); //STUB + public Size MaxClientSize + { + get + { + var s = Native.GtkWidgetGetScreen(GtkWidget); + return new Size(Native.GdkScreenGetWidth(s), Native.GdkScreenGetHeight(s)); + } + } + + public double Scaling => 1; //TODO: Implement scaling public IPlatformHandle Handle => this; string IPlatformHandle.HandleDescriptor => "HWND"; - public Action Activated { get; set; } - public Action Closed { get; set; } - public Action Deactivated { get; set; } - public Action Input { get; set; } + public Action Activated { get; set; } //TODO + public Action Closed { get; set; } //TODO + public Action Deactivated { get; set; } //TODO + public Action Input { get; set; } //TODO public Action Paint { get; set; } - public Action Resized { get; set; } - public Action ScalingChanged { get; set; } - public Action PositionChanged { get; set; } + public Action Resized { get; set; } //TODO + public Action ScalingChanged { get; set; } //TODO + public Action PositionChanged { get; set; } //TODO public void Activate() { throw new NotImplementedException(); @@ -54,7 +81,7 @@ namespace Avalonia.Gtk3 public void Invalidate(Rect rect) { - throw new NotImplementedException(); + Native.GtkWidgetQueueDrawArea(GtkWidget, (int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height); } public void SetInputRoot(IInputRoot inputRoot) => _inputRoot = inputRoot; @@ -74,9 +101,9 @@ namespace Avalonia.Gtk3 //STUB } - public void Show() => Native.GtkWindowPresent(_gtkWidget); + public void Show() => Native.GtkWindowPresent(GtkWidget); - public void Hide() => Native.GtkWidgetHide(_gtkWidget); + public void Hide() => Native.GtkWidgetHide(GtkWidget); public void BeginMoveDrag() { @@ -90,6 +117,7 @@ namespace Avalonia.Gtk3 public Point Position { get; set; } - IntPtr IPlatformHandle.Handle => Native.GetNativeGdkWindowHandle(Native.GtkWidgetGetWindow(_gtkWidget)); + IntPtr IPlatformHandle.Handle => Native.GetNativeGdkWindowHandle(Native.GtkWidgetGetWindow(GtkWidget)); + public IEnumerable Surfaces => new object[] {Handle}; } } diff --git a/src/Gtk/Avalonia.Gtk3/WindowImpl.cs b/src/Gtk/Avalonia.Gtk3/WindowImpl.cs index e3fd52f971..2cd1c5fd6a 100644 --- a/src/Gtk/Avalonia.Gtk3/WindowImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/WindowImpl.cs @@ -11,7 +11,7 @@ namespace Avalonia.Gtk3 public void SetTitle(string title) { using (var t = new Utf8Buffer(title)) - Native.GtkWindowSetTitle(_gtkWidget, t); + Native.GtkWindowSetTitle(GtkWidget, t); } public IDisposable ShowDialog() @@ -20,7 +20,7 @@ namespace Avalonia.Gtk3 //STUB } - public void SetSystemDecorations(bool enabled) => Native.GtkWindowSetDecorated(_gtkWidget, enabled); + public void SetSystemDecorations(bool enabled) => Native.GtkWindowSetDecorated(GtkWidget, enabled); public void SetIcon(IWindowIconImpl icon) { @@ -36,13 +36,15 @@ namespace Avalonia.Gtk3 get { int w, h; - Native.GtkWindowGetSize(_gtkWidget, out w, out h); + Native.GtkWindowGetSize(GtkWidget, out w, out h); return new Size(w, h); } - set - { - //STUB - } + set { Native.GtkWindowResize(GtkWidget, (int) value.Width, (int) value.Height); } + } + + public void SetCoverTaskbarWhenMaximized(bool enable) + { + //Why do we even have that? } } }