From e35677081ab38b0988c052df765ee3022c4aa587 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sat, 15 Oct 2016 15:05:42 +0300 Subject: [PATCH 01/24] Initial commit of GTK3 backend that shows something on screen --- Avalonia.sln | 43 ++++++ .../ControlCatalog.Desktop.csproj | 4 + samples/ControlCatalog.Desktop/Program.cs | 4 +- src/Gtk/Avalonia.Gtk3/.gitignore | 1 + src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj | 80 ++++++++++ src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs | 91 +++++++++++ src/Gtk/Avalonia.Gtk3/Interop/DynLoader.cs | 142 ++++++++++++++++++ src/Gtk/Avalonia.Gtk3/Interop/Native.cs | 87 +++++++++++ .../Avalonia.Gtk3/Interop/NativeException.cs | 24 +++ src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs | 104 +++++++++++++ src/Gtk/Avalonia.Gtk3/Interop/Signal.cs | 45 ++++++ src/Gtk/Avalonia.Gtk3/Interop/Utf8Buffer.cs | 32 ++++ .../Avalonia.Gtk3/Properties/AssemblyInfo.cs | 30 ++++ src/Gtk/Avalonia.Gtk3/README.md | 9 ++ src/Gtk/Avalonia.Gtk3/Stubs.cs | 47 ++++++ src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs | 95 ++++++++++++ src/Gtk/Avalonia.Gtk3/WindowImpl.cs | 48 ++++++ src/Gtk/Avalonia.Gtk3/project.json | 10 ++ 18 files changed, 895 insertions(+), 1 deletion(-) create mode 100644 src/Gtk/Avalonia.Gtk3/.gitignore create mode 100644 src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj create mode 100644 src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs create mode 100644 src/Gtk/Avalonia.Gtk3/Interop/DynLoader.cs create mode 100644 src/Gtk/Avalonia.Gtk3/Interop/Native.cs create mode 100644 src/Gtk/Avalonia.Gtk3/Interop/NativeException.cs create mode 100644 src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs create mode 100644 src/Gtk/Avalonia.Gtk3/Interop/Signal.cs create mode 100644 src/Gtk/Avalonia.Gtk3/Interop/Utf8Buffer.cs create mode 100644 src/Gtk/Avalonia.Gtk3/Properties/AssemblyInfo.cs create mode 100644 src/Gtk/Avalonia.Gtk3/README.md create mode 100644 src/Gtk/Avalonia.Gtk3/Stubs.cs create mode 100644 src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs create mode 100644 src/Gtk/Avalonia.Gtk3/WindowImpl.cs create mode 100644 src/Gtk/Avalonia.Gtk3/project.json diff --git a/Avalonia.sln b/Avalonia.sln index a6fdcbd421..0eda7e5fc7 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -155,6 +155,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RenderTest", "samples\Rende EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.Android", "samples\ControlCatalog.Android\ControlCatalog.Android.csproj", "{29132311-1848-4FD6-AE0C-4FF841151BD3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Gtk3", "src\Gtk\Avalonia.Gtk3\Avalonia.Gtk3.csproj", "{BB1F7BB5-6AD4-4776-94D9-C09D0A972658}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution src\Skia\Avalonia.Skia\Avalonia.Skia.projitems*{2f59f3d0-748d-4652-b01e-e0d954756308}*SharedItemsImports = 13 @@ -2329,6 +2331,46 @@ Global {29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|x86.ActiveCfg = Release|Any CPU {29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|x86.Build.0 = Release|Any CPU {29132311-1848-4FD6-AE0C-4FF841151BD3}.Release|x86.Deploy.0 = Release|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Ad-Hoc|Any CPU.ActiveCfg = Release|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Ad-Hoc|Any CPU.Build.0 = Release|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Ad-Hoc|iPhone.ActiveCfg = Release|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Ad-Hoc|iPhone.Build.0 = Release|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Release|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Ad-Hoc|iPhoneSimulator.Build.0 = Release|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Ad-Hoc|Mono.ActiveCfg = Release|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Ad-Hoc|Mono.Build.0 = Release|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Ad-Hoc|x86.ActiveCfg = Release|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Ad-Hoc|x86.Build.0 = Release|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.AppStore|Any CPU.ActiveCfg = Release|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.AppStore|Any CPU.Build.0 = Release|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.AppStore|iPhone.ActiveCfg = Release|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.AppStore|iPhone.Build.0 = Release|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.AppStore|iPhoneSimulator.ActiveCfg = Release|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.AppStore|iPhoneSimulator.Build.0 = Release|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.AppStore|Mono.ActiveCfg = Release|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.AppStore|Mono.Build.0 = Release|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.AppStore|x86.ActiveCfg = Release|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.AppStore|x86.Build.0 = Release|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Debug|iPhone.Build.0 = Debug|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Debug|Mono.ActiveCfg = Debug|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Debug|Mono.Build.0 = Debug|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Debug|x86.ActiveCfg = Debug|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Debug|x86.Build.0 = Debug|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Release|Any CPU.Build.0 = Release|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Release|iPhone.ActiveCfg = Release|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Release|iPhone.Build.0 = Release|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Release|Mono.ActiveCfg = Release|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Release|Mono.Build.0 = Release|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Release|x86.ActiveCfg = Release|Any CPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2382,5 +2424,6 @@ Global {BD7F352C-6DC1-4740-BAF2-2D34A038728C} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9} {F1FDC5B0-4654-416F-AE69-E3E9BBD87801} = {9B9E3891-2366-4253-A952-D08BCEB71098} {29132311-1848-4FD6-AE0C-4FF841151BD3} = {9B9E3891-2366-4253-A952-D08BCEB71098} + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658} = {B9894058-278A-46B5-B6ED-AD613FCC03B3} EndGlobalSection EndGlobal diff --git a/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj b/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj index 53cb277233..6fec660e55 100644 --- a/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj +++ b/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj @@ -72,6 +72,10 @@ {FB05AC90-89BA-4F2F-A924-F37875FB547C} Avalonia.Cairo + + {bb1f7bb5-6ad4-4776-94d9-c09d0a972658} + Avalonia.Gtk3 + {54F237D5-A70A-4752-9656-0C70B1A7B047} Avalonia.Gtk diff --git a/samples/ControlCatalog.Desktop/Program.cs b/samples/ControlCatalog.Desktop/Program.cs index b67c5ea51d..f2634865b9 100644 --- a/samples/ControlCatalog.Desktop/Program.cs +++ b/samples/ControlCatalog.Desktop/Program.cs @@ -2,6 +2,7 @@ using System; using System.Linq; using Avalonia; using Avalonia.Controls; +using Avalonia.Gtk3; using Avalonia.Logging.Serilog; using Avalonia.Platform; using Serilog; @@ -17,7 +18,8 @@ namespace ControlCatalog // TODO: Make this work with GTK/Skia/Cairo depending on command-line args // again. AppBuilder.Configure() - .UsePlatformDetect() + .UseDirect2D1() + .UseGtk3() .Start(); } diff --git a/src/Gtk/Avalonia.Gtk3/.gitignore b/src/Gtk/Avalonia.Gtk3/.gitignore new file mode 100644 index 0000000000..ddb0c6248b --- /dev/null +++ b/src/Gtk/Avalonia.Gtk3/.gitignore @@ -0,0 +1 @@ +project.lock.json \ No newline at end of file diff --git a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj new file mode 100644 index 0000000000..5e645fb542 --- /dev/null +++ b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj @@ -0,0 +1,80 @@ + + + + + 14.0 + Debug + AnyCPU + {BB1F7BB5-6AD4-4776-94D9-C09D0A972658} + Library + Properties + Avalonia.Gtk3 + Avalonia.Gtk3 + en-US + 512 + {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + v5.0 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + {B09B78D8-9B26-48B0-9149-D64A2F120F3F} + Avalonia.Base + + + {D2221C82-4A25-4583-9B43-D791E3F6820C} + Avalonia.Controls + + + {62024B2D-53EB-4638-B26B-85EEAA54866E} + Avalonia.Input + + + {EB582467-6ABB-43A1-B052-E981BA910E3A} + Avalonia.SceneGraph + + + + + \ No newline at end of file diff --git a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs new file mode 100644 index 0000000000..164e987802 --- /dev/null +++ b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Avalonia.Controls; +using Avalonia.Controls.Platform; +using Avalonia.Gtk3.Interop; +using Avalonia.Input; +using Avalonia.Input.Platform; +using Avalonia.Platform; + +namespace Avalonia.Gtk3 +{ + public class Gtk3Platform : IWindowingPlatform, IPlatformSettings, IPlatformThreadingInterface + { + internal static readonly Gtk3Platform Instance = new Gtk3Platform(); + internal static readonly MouseDevice Mouse = new MouseDevice(); + internal static readonly KeyboardDevice Keyboard = new KeyboardDevice(); + internal static IntPtr App { get; set; } + public static void Initialize() + { + Resolver.Resolve(); + Native.GtkInit(0, IntPtr.Zero); + App = Native.GtkApplicationNew("avalonia.app." + Guid.NewGuid(), 0); + //Mark current thread as UI thread + s_tlsMarker = true; + + AvaloniaLocator.CurrentMutable.Bind().ToConstant(Instance) + .Bind().ToSingleton() + .Bind().ToConstant(new CursorFactoryStub()) + .Bind().ToConstant(Keyboard) + .Bind().ToConstant(Mouse) + .Bind().ToConstant(Instance) + .Bind().ToConstant(Instance) + .Bind().ToSingleton() + .Bind().ToConstant(new PlatformIconLoaderStub()); + + } + + public IWindowImpl CreateWindow() => new WindowImpl(); + + public IEmbeddableWindowImpl CreateEmbeddableWindow() + { + throw new NotImplementedException(); + } + + public IPopupImpl CreatePopup() + { + throw new NotImplementedException(); + } + + + + public Size DoubleClickSize => new Size(4, 4); + + public TimeSpan DoubleClickTime => TimeSpan.FromMilliseconds(100); //STUB + public double RenderScalingFactor { get; } = 1; + public double LayoutScalingFactor { get; } = 1; + + public void RunLoop(CancellationToken cancellationToken) + { + while (!cancellationToken.IsCancellationRequested) + Native.GtkMainIteration(); + } + + public IDisposable StartTimer(TimeSpan interval, Action tick) + { + return null; + } + + public void Signal() + { + } + public event Action Signaled; + + + [ThreadStatic] + private static bool s_tlsMarker; + + public bool CurrentThreadIsLoopThread => s_tlsMarker; + + } + + public static class Gtk3AppBuilderExtensions + { + public static T UseGtk3(this AppBuilderBase builder) where T : AppBuilderBase, new() + => builder.UseWindowingSubsystem(Gtk3Platform.Initialize, "GTK3"); + } +} \ No newline at end of file diff --git a/src/Gtk/Avalonia.Gtk3/Interop/DynLoader.cs b/src/Gtk/Avalonia.Gtk3/Interop/DynLoader.cs new file mode 100644 index 0000000000..93ba7cc195 --- /dev/null +++ b/src/Gtk/Avalonia.Gtk3/Interop/DynLoader.cs @@ -0,0 +1,142 @@ +using System; +using System.Runtime.InteropServices; + +/* + * Source code imported from https://github.com/kekekeks/evhttp-sharp + * Source is provided under MIT license for Avalonia project and derived works + */ + + +namespace Avalonia.Gtk3.Interop +{ + internal interface IDynLoader + { + IntPtr LoadLibrary(string basePath, string dll); + IntPtr GetProcAddress(IntPtr dll, string proc); + + } + + class UnixLoader : IDynLoader + { + // ReSharper disable InconsistentNaming + static class LinuxImports + { + + [DllImport("libdl.so.2")] + private static extern IntPtr dlopen(string path, int flags); + + [DllImport("libdl.so.2")] + private static extern IntPtr dlsym(IntPtr handle, string symbol); + + [DllImport("libdl.so.2")] + private static extern IntPtr dlerror(); + + public static void Init() + { + DlOpen = dlopen; + DlSym = dlsym; + DlError = dlerror; + Postfix = ".so.0"; + } + } + static class OsXImports + { + + + [DllImport("/usr/lib/libSystem.dylib")] + private static extern IntPtr dlopen(string path, int flags); + + [DllImport("/usr/lib/libSystem.dylib")] + private static extern IntPtr dlsym(IntPtr handle, string symbol); + + [DllImport("/usr/lib/libSystem.dylib")] + private static extern IntPtr dlerror(); + + public static void Init() + { + DlOpen = dlopen; + DlSym = dlsym; + DlError = dlerror; + Postfix = ".dylib"; //TODO + } + + } + + + [DllImport("libc")] + static extern int uname(IntPtr buf); + + + + static UnixLoader() + { + var buffer = Marshal.AllocHGlobal(0x1000); + uname(buffer); + var unixName = Marshal.PtrToStringAnsi(buffer); + Marshal.FreeHGlobal(buffer); + if(unixName == "Darwin") + OsXImports.Init(); + else + LinuxImports.Init(); + } + + private static Func DlOpen; + private static Func DlSym; + private static Func DlError; + private static string Postfix; + // ReSharper restore InconsistentNaming + + static string DlErrorString() + { + + return Marshal.PtrToStringAnsi(DlError()); + } + + public IntPtr LoadLibrary(string basePath, string dll) + { + dll += Postfix; + if (basePath != null) + dll = System.IO.Path.Combine(basePath, dll); + var handle = DlOpen(dll, 1); + if (handle == IntPtr.Zero) + throw new NativeException(DlErrorString()); + return handle; + } + + public IntPtr GetProcAddress(IntPtr dll, string proc) + { + var ptr = DlSym(dll, proc); + if (ptr == IntPtr.Zero) + throw new NativeException(DlErrorString()); + return ptr; + } + } + + internal class Win32Loader : IDynLoader + { + [DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)] + private static extern IntPtr GetProcAddress(IntPtr hModule, string procName); + + [DllImport("kernel32", EntryPoint = "LoadLibraryW", SetLastError = true, CharSet = CharSet.Unicode)] + private static extern IntPtr LoadLibrary(string lpszLib); + + IntPtr IDynLoader.LoadLibrary(string basePath, string dll) + { + dll += "-0.dll"; + if (basePath != null) + dll = System.IO.Path.Combine(basePath, dll); + var handle = LoadLibrary(dll); + if (handle == IntPtr.Zero) + throw new NativeException("Error " + Marshal.GetLastWin32Error()); + return handle; + } + + IntPtr IDynLoader.GetProcAddress(IntPtr dll, string proc) + { + var ptr = GetProcAddress(dll, proc); + if (ptr == IntPtr.Zero) + throw new NativeException("Error " + Marshal.GetLastWin32Error()); + return ptr; + } + } +} diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs new file mode 100644 index 0000000000..a07d84c710 --- /dev/null +++ b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs @@ -0,0 +1,87 @@ +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 Native + { + public static class D + { + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate IntPtr gtk_application_new([MarshalAs(UnmanagedType.AnsiBStr)] string appId, int flags); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate void gtk_main_iteration(); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate IntPtr gtk_window_new(GtkWindowType windowType); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate IntPtr gtk_init(int argc, IntPtr argv); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate void gtk_window_present(IntPtr gtkWindow); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate void gtk_widget_hide(IntPtr gtkWidget); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] //No manual import + public delegate IntPtr gdk_get_native_handle(IntPtr gdkWindow); + + + [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_set_double_buffered(IntPtr gtkWidget, bool value); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate void gtk_widget_realize(IntPtr gtkWidget); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate void gtk_window_set_title(IntPtr gtkWindow, Utf8Buffer title); + + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate void gtk_window_set_decorated(IntPtr gtkWindow, bool decorated); + + [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.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)] + public delegate bool signal_widget_draw(IntPtr gtkWidget, IntPtr cairoContext, IntPtr userData); + } + + + public static D.gtk_window_set_decorated GtkWindowSetDecorated; + public static D.gtk_window_set_title GtkWindowSetTitle; + public static D.gtk_application_new GtkApplicationNew; + public static D.gtk_main_iteration GtkMainIteration; + public static D.gtk_window_new GtkWindowNew; + public static D.gtk_init GtkInit; + public static D.gtk_window_present GtkWindowPresent; + 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_realize GtkWidgetRealize; + public static D.gtk_window_get_size GtkWindowGetSize; + public static D.g_signal_connect_object GSignalConnectObject; + public static D.g_signal_handler_disconnect GSignalHandlerDisconnect; + public static D.gtk_widget_set_double_buffered GtkWidgetSetDoubleBuffered; + + + + } + + public enum GtkWindowType + { + TopLevel, + Popup + } +} diff --git a/src/Gtk/Avalonia.Gtk3/Interop/NativeException.cs b/src/Gtk/Avalonia.Gtk3/Interop/NativeException.cs new file mode 100644 index 0000000000..43659f8f83 --- /dev/null +++ b/src/Gtk/Avalonia.Gtk3/Interop/NativeException.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Avalonia.Gtk3.Interop +{ + public class NativeException : Exception + { + public NativeException() + { + } + + public NativeException(string message) : base(message) + { + } + + public NativeException(string message, Exception inner) : base(message, inner) + { + } + + } +} diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs b/src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs new file mode 100644 index 0000000000..2410af7591 --- /dev/null +++ b/src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Avalonia.Gtk3.Interop +{ + internal class GtkImportAttribute : Attribute + { + public GtkDll Dll { get; set; } + public string Name { get; set; } + public bool Optional { get; set; } + + public GtkImportAttribute(GtkDll dll, string name = null, bool optional = false) + { + Dll = dll; + Name = name; + Optional = optional; + } + } + + internal enum GtkDll + { + Gdk, + Gtk, + Glib, + Gio, + Gobject + } + + static class Resolver + { + [DllImport("kernel32.dll")] + static extern int GetVersion(); + + static bool IsWin32() + { + try + { + GetVersion(); + return true; + } + catch + { + return false; + } + } + + + + public static void Resolve(string basePath = null) + { + var loader = IsWin32() ? (IDynLoader)new Win32Loader() : new UnixLoader(); + + + var gdk = loader.LoadLibrary(basePath, "libgdk-3"); + var gtk = loader.LoadLibrary(basePath, "libgtk-3"); + var gio = loader.LoadLibrary(basePath, "libgio-2.0"); + var glib = loader.LoadLibrary(basePath, "libglib-2.0"); + var gobject = loader.LoadLibrary(basePath, "libgobject-2.0"); + foreach (var fieldInfo in typeof(Native).GetTypeInfo().DeclaredFields) + { + var import = fieldInfo.FieldType.GetTypeInfo().GetCustomAttributes(typeof(GtkImportAttribute), true).Cast().FirstOrDefault(); + if(import == null) + continue; + IntPtr lib; + if (import.Dll == GtkDll.Gtk) + lib = gtk; + else if (import.Dll == GtkDll.Gdk) + lib = gdk; + else if (import.Dll == GtkDll.Gio) + lib = gio; + else if (import.Dll == GtkDll.Glib) + lib = glib; + else if (import.Dll == GtkDll.Gobject) + lib = gobject; + else + throw new ArgumentException("Invalid GtkImportAttribute for " + fieldInfo.FieldType); + + var funcPtr = loader.GetProcAddress(lib, import.Name ?? fieldInfo.FieldType.Name); + fieldInfo.SetValue(null, Marshal.GetDelegateForFunctionPointer(funcPtr, fieldInfo.FieldType)); + } + + var nativeHandleNames = new[] {"gdk_x11_window_get_xid", "gdk_win32_window_get_handle"}; + 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)); + } + catch { } + } + if (Native.GetNativeGdkWindowHandle == null) + throw new Exception($"Unable to locate any of [{string.Join(", ", nativeHandleNames)}] in libgdk"); + + } + + + } +} diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Signal.cs b/src/Gtk/Avalonia.Gtk3/Interop/Signal.cs new file mode 100644 index 0000000000..5896c547f2 --- /dev/null +++ b/src/Gtk/Avalonia.Gtk3/Interop/Signal.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Avalonia.Gtk3.Interop +{ + public class Signal + { + class ConnectedSignal : IDisposable + { + private readonly IntPtr _instance; + private GCHandle _handle; + private readonly ulong _id; + + public ConnectedSignal(IntPtr instance, GCHandle handle, ulong id) + { + _instance = instance; + _handle = handle; + _id = id; + } + + public void Dispose() + { + if (_handle.IsAllocated) + { + Native.GSignalHandlerDisconnect(_instance, _id); + _handle.Free(); + } + } + } + + public static IDisposable Connect(IntPtr obj, string name, T handler) + { + var handle = GCHandle.Alloc(handler); + var ptr = Marshal.GetFunctionPointerForDelegate((Delegate)(object)handler); + var id = Native.GSignalConnectObject(obj, name, ptr, IntPtr.Zero, 0); + if (id == 0) + throw new ArgumentException("Unable to connect to signal " + name); + return new ConnectedSignal(obj, handle, id); + } + } +} diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Utf8Buffer.cs b/src/Gtk/Avalonia.Gtk3/Interop/Utf8Buffer.cs new file mode 100644 index 0000000000..7a9868857f --- /dev/null +++ b/src/Gtk/Avalonia.Gtk3/Interop/Utf8Buffer.cs @@ -0,0 +1,32 @@ +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace Avalonia.Gtk3.Interop +{ + class Utf8Buffer : SafeHandle + { + private GCHandle _gchandle; + private byte[] _data; + + public Utf8Buffer(string s) : base(IntPtr.Zero, true) + { + _data = Encoding.UTF8.GetBytes(s); + _gchandle = GCHandle.Alloc(_data, GCHandleType.Pinned); + handle = _gchandle.AddrOfPinnedObject(); + } + + protected override bool ReleaseHandle() + { + if (handle != IntPtr.Zero) + { + handle = IntPtr.Zero; + _data = null; + _gchandle.Free(); + } + return true; + } + + public override bool IsInvalid => handle == IntPtr.Zero; + } +} diff --git a/src/Gtk/Avalonia.Gtk3/Properties/AssemblyInfo.cs b/src/Gtk/Avalonia.Gtk3/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..72e6388531 --- /dev/null +++ b/src/Gtk/Avalonia.Gtk3/Properties/AssemblyInfo.cs @@ -0,0 +1,30 @@ +using System.Resources; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Avalonia.Gtk3")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Avalonia.Gtk3")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: NeutralResourcesLanguage("en")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Gtk/Avalonia.Gtk3/README.md b/src/Gtk/Avalonia.Gtk3/README.md new file mode 100644 index 0000000000..7f117d24b3 --- /dev/null +++ b/src/Gtk/Avalonia.Gtk3/README.md @@ -0,0 +1,9 @@ +P/Invoke based GTK3 backend +=========================== + +Code is EXPERIMENTAL at this point. It also needs Direct2D/Skia for rendering. + +Windows GTK3 binaries aren't included in the repo, you need to download them from http://www.tarnyko.net/repo/gtk3_build_system/gtk+-bundle_3.4.2-20130513_win32.zip +Then you need to extract them somewhere and add `bin` directory to PATH. Support for specifying exact path to binaries will be implemented later. + +On Linux it should work out of the box with system-provided GTK3. On OSX you should be able to wire GTK3 using DYLD_LIBRARY_PATH environment variable. \ No newline at end of file diff --git a/src/Gtk/Avalonia.Gtk3/Stubs.cs b/src/Gtk/Avalonia.Gtk3/Stubs.cs new file mode 100644 index 0000000000..76333aba60 --- /dev/null +++ b/src/Gtk/Avalonia.Gtk3/Stubs.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Avalonia.Controls; +using Avalonia.Controls.Platform; +using Avalonia.Input; +using Avalonia.Input.Platform; +using Avalonia.Platform; + +//TODO: This file should be empty once everything is implemented + +namespace Avalonia.Gtk3 +{ + class ClipboardStub : IClipboard + { + public Task GetTextAsync() => Task.FromResult(""); + + public Task SetTextAsync(string text) => Task.FromResult(0); + + public Task ClearAsync() => Task.FromResult(0); + } + + class CursorFactoryStub : IStandardCursorFactory + { + public IPlatformHandle GetCursor(StandardCursorType cursorType) => new PlatformHandle(IntPtr.Zero, "STUB"); + } + + class SystemDialogStub : ISystemDialogImpl + { + public Task ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent) => Task.FromResult(new string[0]); + + public Task ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent) + => Task.FromResult((string) null); + } + + class PlatformIconLoaderStub : IPlatformIconLoader + { + public IWindowIconImpl LoadIcon(string fileName) => null; + + public IWindowIconImpl LoadIcon(Stream stream) => null; + + public IWindowIconImpl LoadIcon(IBitmapImpl bitmap) => null; + } +} diff --git a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs new file mode 100644 index 0000000000..2f6181c893 --- /dev/null +++ b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs @@ -0,0 +1,95 @@ +using System; +using Avalonia.Controls; +using Avalonia.Gtk3.Interop; +using Avalonia.Input; +using Avalonia.Input.Raw; +using Avalonia.Platform; + +namespace Avalonia.Gtk3 +{ + abstract class TopLevelImpl : ITopLevelImpl, IPlatformHandle + { + protected readonly IntPtr _gtkWidget; + private IInputRoot _inputRoot; + + public TopLevelImpl(IntPtr gtkWidget) + { + _gtkWidget = gtkWidget; + Native.GtkWidgetRealize(gtkWidget); + Native.GtkWidgetSetDoubleBuffered(gtkWidget, false); + Signal.Connect(_gtkWidget, "draw", OnDraw); + } + + private bool OnDraw(IntPtr gtkwidget, IntPtr cairocontext, IntPtr userdata) + { + Paint?.Invoke(new Rect(ClientSize)); + return true; + } + + public void Dispose() + { + //STUB + } + + public abstract Size ClientSize { get; set; } + + public Size MaxClientSize => new Size(1024, 768); //STUB + 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 Paint { get; set; } + public Action Resized { get; set; } + public Action ScalingChanged { get; set; } + public Action PositionChanged { get; set; } + public void Activate() + { + throw new NotImplementedException(); + } + + public void Invalidate(Rect rect) + { + throw new NotImplementedException(); + } + + public void SetInputRoot(IInputRoot inputRoot) => _inputRoot = inputRoot; + + public Point PointToClient(Point point) + { + throw new NotImplementedException(); + } + + public Point PointToScreen(Point point) + { + throw new NotImplementedException(); + } + + public void SetCursor(IPlatformHandle cursor) + { + //STUB + } + + public void Show() => Native.GtkWindowPresent(_gtkWidget); + + public void Hide() => Native.GtkWidgetHide(_gtkWidget); + + public void BeginMoveDrag() + { + //STUB + } + + public void BeginResizeDrag(WindowEdge edge) + { + //STUB + } + + public Point Position { get; set; } + + IntPtr IPlatformHandle.Handle => Native.GetNativeGdkWindowHandle(Native.GtkWidgetGetWindow(_gtkWidget)); + } +} diff --git a/src/Gtk/Avalonia.Gtk3/WindowImpl.cs b/src/Gtk/Avalonia.Gtk3/WindowImpl.cs new file mode 100644 index 0000000000..e3fd52f971 --- /dev/null +++ b/src/Gtk/Avalonia.Gtk3/WindowImpl.cs @@ -0,0 +1,48 @@ +using System; +using Avalonia.Controls; +using Avalonia.Gtk3.Interop; +using Avalonia.Platform; + +namespace Avalonia.Gtk3 +{ + class WindowImpl : TopLevelImpl, IWindowImpl + { + public WindowState WindowState { get; set; } //STUB + public void SetTitle(string title) + { + using (var t = new Utf8Buffer(title)) + Native.GtkWindowSetTitle(_gtkWidget, t); + } + + public IDisposable ShowDialog() + { + return null; + //STUB + } + + public void SetSystemDecorations(bool enabled) => Native.GtkWindowSetDecorated(_gtkWidget, enabled); + + public void SetIcon(IWindowIconImpl icon) + { + //STUB + } + + public WindowImpl() : base(Native.GtkWindowNew(GtkWindowType.TopLevel)) + { + } + + public override Size ClientSize + { + get + { + int w, h; + Native.GtkWindowGetSize(_gtkWidget, out w, out h); + return new Size(w, h); + } + set + { + //STUB + } + } + } +} diff --git a/src/Gtk/Avalonia.Gtk3/project.json b/src/Gtk/Avalonia.Gtk3/project.json new file mode 100644 index 0000000000..7af946da1a --- /dev/null +++ b/src/Gtk/Avalonia.Gtk3/project.json @@ -0,0 +1,10 @@ +{ + "supports": {}, + "dependencies": { + "Microsoft.NETCore.Portable.Compatibility": "1.0.1", + "NETStandard.Library": "1.6.0" + }, + "frameworks": { + "netstandard1.1": {} + } +} \ No newline at end of file From f8e392a520caaefc860eb8a05d474a52a1e8279b Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 26 Jan 2017 02:55:48 +0300 Subject: [PATCH 02/24] 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? } } } From 04359041d7aef90f20ef87dc7f5a6acaa400ad9d Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 26 Jan 2017 03:42:54 +0300 Subject: [PATCH 03/24] Implemented click support --- src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj | 6 ++ src/Gtk/Avalonia.Gtk3/Interop/Native.cs | 96 +++++++++++++++++++++ src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs | 97 +++++++++++++++++++--- src/Gtk/Avalonia.Gtk3/WindowImpl.cs | 11 --- 4 files changed, 189 insertions(+), 21 deletions(-) diff --git a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj index df5a712c18..ab52c8f9c4 100644 --- a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj +++ b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj @@ -25,6 +25,7 @@ DEBUG;TRACE prompt 4 + true pdbonly @@ -33,6 +34,7 @@ TRACE prompt 4 + true @@ -65,6 +67,10 @@ {62024B2D-53EB-4638-B26B-85EEAA54866E} Avalonia.Input + + {6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b} + Avalonia.Interactivity + {EB582467-6ABB-43A1-B052-E981BA910E3A} Avalonia.SceneGraph diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs index 2a5abfe603..3ee8871b40 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs @@ -4,6 +4,12 @@ using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; +using gint8 = System.Byte; +using gint32 = System.Int32; +using gint = System.Int32; +using guint32 = System.UInt32; +using guint = System.UInt32; +using gdouble = System.Double; namespace Avalonia.Gtk3.Interop { @@ -48,6 +54,9 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] public delegate int gdk_screen_get_width(IntPtr screen); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] + public delegate int gdk_window_get_origin(IntPtr gdkWindow, out int x, out int y); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] @@ -64,6 +73,10 @@ namespace Avalonia.Gtk3.Interop 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_window_get_position(IntPtr gtkWindow, out int x, out int y); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate void gtk_window_move(IntPtr gtkWindow, int x, int y); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_widget_queue_draw_area(IntPtr gtkWindow, int x, int y, int width, int height); @@ -102,6 +115,8 @@ namespace Avalonia.Gtk3.Interop 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.gtk_window_get_position GtkWindowGetPosition; + public static D.gtk_window_move GtkWindowMove; public static D.g_signal_connect_object GSignalConnectObject; public static D.g_signal_handler_disconnect GSignalHandlerDisconnect; public static D.g_timeout_add GTimeoutAdd; @@ -111,6 +126,7 @@ namespace Avalonia.Gtk3.Interop 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; + public static D.gdk_window_get_origin GdkWindowGetOrigin; } @@ -137,4 +153,84 @@ namespace Avalonia.Gtk3.Interop }; } } + + enum GdkEventType + { + Nothing = -1, + Delete = 0, + Destroy = 1, + Expose = 2, + MotionNotify = 3, + ButtonPress = 4, + TwoButtonPress = 5, + ThreeButtonPress = 6, + ButtonRelease = 7, + KeyPress = 8, + KeyRelease = 9, + EnterNotify = 10, + LeaveNotify = 11, + FocusChange = 12, + Configure = 13, + Map = 14, + Unmap = 15, + PropertyNotify = 16, + SelectionClear = 17, + SelectionRequest = 18, + SelectionNotify = 19, + ProximityIn = 20, + ProximityOut = 21, + DragEnter = 22, + DragLeave = 23, + DragMotion = 24, + DragStatus = 25, + DropStart = 26, + DropFinished = 27, + ClientEvent = 28, + VisibilityNotify = 29, + NoExpose = 30, + Scroll = 31, + WindowState = 32, + Setting = 33, + OwnerChange = 34, + GrabBroken = 35, + } + + public enum GdkModifierType + { + ShiftMask = 1, + LockMask = 2, + ControlMask = 4, + Mod1Mask = 8, + Mod2Mask = 16, + Mod3Mask = 32, + Mod4Mask = 64, + Mod5Mask = 128, + Button1Mask = 256, + Button2Mask = 512, + Button3Mask = 1024, + Button4Mask = 2048, + Button5Mask = 4096, + SuperMask = 67108864, + HyperMask = 134217728, + MetaMask = 268435456, + ReleaseMask = 1073741824, + ModifierMask = ReleaseMask | Button5Mask | Button4Mask | Button3Mask | Button2Mask | Button1Mask | Mod5Mask | Mod4Mask | Mod3Mask | Mod2Mask | Mod1Mask | ControlMask | LockMask | ShiftMask, + None = 0, + } + + [StructLayout(LayoutKind.Sequential)] + unsafe struct GdkEventButton + { + public GdkEventType type; + public IntPtr window; + public gint8 send_event; + public guint32 time; + public gdouble x; + public gdouble y; + public gdouble* axes; + public GdkModifierType state; + public guint button; + public IntPtr device; + public gdouble x_root, y_root; + }; } diff --git a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs index 672e98fe18..c16415828f 100644 --- a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs @@ -22,21 +22,73 @@ namespace Avalonia.Gtk3 Native.GtkWidgetRealize(gtkWidget); Connect("draw", OnDraw); Connect("configure-event", OnConfigured); + Connect("button-press-event", OnButton); + Connect("button-release-event", OnButton); } + private Size _lastSize; + private Point _lastPosition; + private bool OnConfigured(IntPtr gtkwidget, IntPtr ev, IntPtr userdata) { - Debug.WriteLine("Configured"); - Resized?.Invoke(ClientSize); + var size = ClientSize; + if (_lastSize != size) + { + _lastSize = size; + Resized?.Invoke(size); + } + var pos = Position; + if (_lastPosition != pos) + { + _lastPosition = pos; + PositionChanged?.Invoke(pos); + } + return false; } + private static InputModifiers GetModifierKeys(GdkModifierType state) + { + var rv = InputModifiers.None; + if (state.HasFlag(GdkModifierType.ControlMask)) + rv |= InputModifiers.Control; + if (state.HasFlag(GdkModifierType.ShiftMask)) + rv |= InputModifiers.Shift; + if (state.HasFlag(GdkModifierType.Mod1Mask)) + rv |= InputModifiers.Control; + if (state.HasFlag(GdkModifierType.Button1Mask)) + rv |= InputModifiers.LeftMouseButton; + if (state.HasFlag(GdkModifierType.Button2Mask)) + rv |= InputModifiers.RightMouseButton; + if (state.HasFlag(GdkModifierType.Button3Mask)) + rv |= InputModifiers.MiddleMouseButton; + return rv; + } + + private unsafe bool OnButton(IntPtr w, IntPtr ev, IntPtr userdata) + { + var evnt = (GdkEventButton*)ev; + var e = new RawMouseEventArgs( + Gtk3Platform.Mouse, + evnt->time, + _inputRoot, + evnt->type == GdkEventType.ButtonRelease + ? evnt->button == 1 + ? RawMouseEventType.LeftButtonUp + : evnt->button == 3 ? RawMouseEventType.RightButtonUp : RawMouseEventType.MiddleButtonUp + : evnt->button == 1 + ? RawMouseEventType.LeftButtonDown + : evnt->button == 3 ? RawMouseEventType.RightButtonDown : RawMouseEventType.MiddleButtonDown, + new Point(evnt->x, evnt->y), GetModifierKeys(evnt->state)); + Input?.Invoke(e); + 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; } @@ -49,8 +101,6 @@ namespace Avalonia.Gtk3 //TODO } - public abstract Size ClientSize { get; set; } - public Size MaxClientSize { get @@ -71,9 +121,10 @@ namespace Avalonia.Gtk3 public Action Deactivated { get; set; } //TODO public Action Input { get; set; } //TODO public Action Paint { get; set; } - public Action Resized { get; set; } //TODO + public Action Resized { get; set; } public Action ScalingChanged { get; set; } //TODO - public Action PositionChanged { get; set; } //TODO + public Action PositionChanged { get; set; } + public void Activate() { throw new NotImplementedException(); @@ -88,12 +139,17 @@ namespace Avalonia.Gtk3 public Point PointToClient(Point point) { - throw new NotImplementedException(); + int x, y; + Native.GdkWindowGetOrigin(Native.GtkWidgetGetWindow(GtkWidget), out x, out y); + + return new Point(point.X - x, point.Y - y); } public Point PointToScreen(Point point) { - throw new NotImplementedException(); + int x, y; + Native.GdkWindowGetOrigin(Native.GtkWidgetGetWindow(GtkWidget), out x, out y); + return new Point(point.X + x, point.Y + y); } public void SetCursor(IPlatformHandle cursor) @@ -115,7 +171,28 @@ namespace Avalonia.Gtk3 //STUB } - public Point Position { get; set; } + + public Size ClientSize + { + get + { + int w, h; + Native.GtkWindowGetSize(GtkWidget, out w, out h); + return new Size(w, h); + } + set { Native.GtkWindowResize(GtkWidget, (int)value.Width, (int)value.Height); } + } + + public Point Position + { + get + { + int x, y; + Native.GtkWindowGetPosition(GtkWidget, out x, out y); + return new Point(x, y); + } + set { Native.GtkWindowMove(GtkWidget, (int)value.X, (int)value.Y); } + } 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 2cd1c5fd6a..b8c561c56c 100644 --- a/src/Gtk/Avalonia.Gtk3/WindowImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/WindowImpl.cs @@ -31,17 +31,6 @@ namespace Avalonia.Gtk3 { } - public override Size ClientSize - { - get - { - int w, h; - Native.GtkWindowGetSize(GtkWidget, out w, out h); - return new Size(w, h); - } - set { Native.GtkWindowResize(GtkWidget, (int) value.Width, (int) value.Height); } - } - public void SetCoverTaskbarWhenMaximized(bool enable) { //Why do we even have that? From f55a49729831db65472e26f31fbea0944ba0ae49 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 26 Jan 2017 04:01:38 +0300 Subject: [PATCH 04/24] Implemented motion and scroll events --- src/Gtk/Avalonia.Gtk3/Interop/Native.cs | 48 +++++++++++++++++++++++-- src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs | 39 ++++++++++++++++++++ 2 files changed, 85 insertions(+), 2 deletions(-) diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs index 3ee8871b40..d9cf9fff05 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs @@ -5,8 +5,10 @@ using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using gint8 = System.Byte; +using gint16 = System.Int16; using gint32 = System.Int32; using gint = System.Int32; +using guint16 = System.UInt16; using guint32 = System.UInt32; using guint = System.UInt32; using gdouble = System.Double; @@ -195,7 +197,7 @@ namespace Avalonia.Gtk3.Interop GrabBroken = 35, } - public enum GdkModifierType + enum GdkModifierType { ShiftMask = 1, LockMask = 2, @@ -218,6 +220,15 @@ namespace Avalonia.Gtk3.Interop None = 0, } + enum GdkScrollDirection + { + Up, + Down, + Left, + Right, + Smooth + } + [StructLayout(LayoutKind.Sequential)] unsafe struct GdkEventButton { @@ -232,5 +243,38 @@ namespace Avalonia.Gtk3.Interop public guint button; public IntPtr device; public gdouble x_root, y_root; - }; + } + + [StructLayout(LayoutKind.Sequential)] + unsafe struct GdkEventMotion + { + public GdkEventType type; + public IntPtr window; + public gint8 send_event; + public guint32 time; + public gdouble x; + public gdouble y; + public gdouble* axes; + public GdkModifierType state; + public gint16 is_hint; + public IntPtr device; + public gdouble x_root, y_root; + } + + [StructLayout(LayoutKind.Sequential)] + unsafe struct GdkEventScroll + { + public GdkEventType type; + public IntPtr window; + public gint8 send_event; + public guint32 time; + public gdouble x; + public gdouble y; + public GdkModifierType state; + public GdkScrollDirection direction; + public IntPtr device; + public gdouble x_root, y_root; + public gdouble delta_x; + public gdouble delta_y; + } } diff --git a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs index c16415828f..fc0893bdb8 100644 --- a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs @@ -24,6 +24,8 @@ namespace Avalonia.Gtk3 Connect("configure-event", OnConfigured); Connect("button-press-event", OnButton); Connect("button-release-event", OnButton); + Connect("motion-notify-event", OnMotion); + Connect("scroll-event", OnScroll); } private Size _lastSize; @@ -85,6 +87,43 @@ namespace Avalonia.Gtk3 return false; } + private unsafe bool OnMotion(IntPtr w, IntPtr ev, IntPtr userdata) + { + var evnt = (GdkEventMotion*)ev; + var position = new Point(evnt->x, evnt->y); + + + var e = new RawMouseEventArgs( + Gtk3Platform.Mouse, + evnt->time, + _inputRoot, + RawMouseEventType.Move, + position, GetModifierKeys(evnt->state)); + Input(e); + return false; + } + private unsafe bool OnScroll(IntPtr w, IntPtr ev, IntPtr userdata) + { + var evnt = (GdkEventScroll*)ev; + var delta = new Vector(); + var step = (double) 1; + if (evnt->direction == GdkScrollDirection.Down) + delta = new Vector(0, -step); + else if (evnt->direction == GdkScrollDirection.Up) + delta = new Vector(0, step); + else if (evnt->direction == GdkScrollDirection.Right) + delta = new Vector(-step, 0); + else if (evnt->direction == GdkScrollDirection.Left) + delta = new Vector(step, 0); + else if (evnt->direction == GdkScrollDirection.Smooth) + delta = new Vector(evnt->delta_x, evnt->delta_y); + + var e = new RawMouseWheelEventArgs(Gtk3Platform.Mouse, evnt->time, _inputRoot, + new Point(evnt->x, evnt->y), delta, GetModifierKeys(evnt->state)); + Input(e); + return false; + } + void Connect(string name, T handler) => _disposables.Add(Signal.Connect(GtkWidget, name, handler)); private bool OnDraw(IntPtr gtkwidget, IntPtr cairocontext, IntPtr userdata) From 5bb9e6913ed756fe91a3316a3479fe65d35a550d Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 26 Jan 2017 22:21:53 +0300 Subject: [PATCH 05/24] Implemented framebuffer and smarter dll search logic --- samples/ControlCatalog.Desktop/Program.cs | 2 +- src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj | 3 + src/Gtk/Avalonia.Gtk3/FramebufferManager.cs | 42 +++++++ src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs | 8 +- .../Avalonia.Gtk3/ImageSurfaceFramebuffer.cs | 71 +++++++++++ src/Gtk/Avalonia.Gtk3/Interop/DynLoader.cs | 33 ++--- .../ICustomGtk3NativeLibraryResolver.cs | 17 +++ src/Gtk/Avalonia.Gtk3/Interop/Native.cs | 36 +++++- src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs | 113 +++++++++++++----- src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs | 8 +- 10 files changed, 272 insertions(+), 61 deletions(-) create mode 100644 src/Gtk/Avalonia.Gtk3/FramebufferManager.cs create mode 100644 src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs create mode 100644 src/Gtk/Avalonia.Gtk3/Interop/ICustomGtk3NativeLibraryResolver.cs diff --git a/samples/ControlCatalog.Desktop/Program.cs b/samples/ControlCatalog.Desktop/Program.cs index f2634865b9..4abdce2657 100644 --- a/samples/ControlCatalog.Desktop/Program.cs +++ b/samples/ControlCatalog.Desktop/Program.cs @@ -18,7 +18,7 @@ namespace ControlCatalog // TODO: Make this work with GTK/Skia/Cairo depending on command-line args // again. AppBuilder.Configure() - .UseDirect2D1() + .UseSkia() .UseGtk3() .Start(); } diff --git a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj index ab52c8f9c4..06fc40458e 100644 --- a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj +++ b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj @@ -41,13 +41,16 @@ + + + diff --git a/src/Gtk/Avalonia.Gtk3/FramebufferManager.cs b/src/Gtk/Avalonia.Gtk3/FramebufferManager.cs new file mode 100644 index 0000000000..c2685e8791 --- /dev/null +++ b/src/Gtk/Avalonia.Gtk3/FramebufferManager.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Avalonia.Controls.Platform.Surfaces; + +namespace Avalonia.Gtk3 +{ + class FramebufferManager : IFramebufferPlatformSurface, IDisposable + { + private readonly TopLevelImpl _window; + private ImageSurfaceFramebuffer _fb; + public FramebufferManager(TopLevelImpl window) + { + _window = window; + } + + public void Dispose() + { + _fb?.Deallocate(); + } + + public ILockedFramebuffer Lock() + { + if(_window.CurrentCairoContext == IntPtr.Zero) + throw new InvalidOperationException("Window is not in drawing state"); + + var ctx = _window.CurrentCairoContext; + var width = (int) _window.ClientSize.Width; + var height = (int) _window.ClientSize.Height; + if (_fb == null || _fb.Width != width || + _fb.Height != height) + { + _fb?.Dispose(); + _fb = new ImageSurfaceFramebuffer(width, height); + } + _fb.Prepare(ctx); + return _fb; + } + } +} diff --git a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs index faf1983250..625cd7096a 100644 --- a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs +++ b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs @@ -104,7 +104,11 @@ namespace Avalonia.Gtk3 public static class Gtk3AppBuilderExtensions { - public static T UseGtk3(this AppBuilderBase builder) where T : AppBuilderBase, new() - => builder.UseWindowingSubsystem(Gtk3Platform.Initialize, "GTK3"); + public static T UseGtk3(this AppBuilderBase builder, ICustomGtk3NativeLibraryResolver resolver = null) + where T : AppBuilderBase, new() + { + Resolver.Custom = resolver; + return builder.UseWindowingSubsystem(Gtk3Platform.Initialize, "GTK3"); + } } } \ No newline at end of file diff --git a/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs b/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs new file mode 100644 index 0000000000..e955f82764 --- /dev/null +++ b/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Avalonia.Controls.Platform.Surfaces; +using Avalonia.Gtk3.Interop; +using Avalonia.Platform; + + +namespace Avalonia.Gtk3 +{ + class ImageSurfaceFramebuffer : ILockedFramebuffer + { + private IntPtr _context; + private IntPtr _surface; + + public ImageSurfaceFramebuffer(int width, int height) + { + _surface = Native.CairoImageSurfaceCreate(1, width, height); + Width = width; + Height = height; + Address = Native.CairoImageSurfaceGetData(_surface); + RowBytes = Native.CairoImageSurfaceGetStride(_surface); + Native.CairoSurfaceFlush(_surface); + } + + public void Prepare(IntPtr context) + { + _context = context; + } + + public void Deallocate() + { + Native.CairoSurfaceDestroy(_surface); + _surface = IntPtr.Zero; + } + + public void Dispose() + { + if(_context == IntPtr.Zero || _surface == IntPtr.Zero) + return; + Native.CairoSurfaceMarkDirty(_surface); + Native.CairoSetSourceSurface(_context, _surface, 0, 0); + Native.CairoPaint(_context); + _context = IntPtr.Zero; + + } + + public IntPtr Address { get; } + public int Width { get; } + public int Height { get; } + public int RowBytes { get; } + + //TODO: Proper DPI detect + public Size Dpi => new Size(96, 96); + public PixelFormat Format + { + get + { + if (AvaloniaLocator.Current.GetService().GetRuntimeInfo().OperatingSystem == + OperatingSystemType.WinNT) + return PixelFormat.Bgra8888; + return PixelFormat.Rgba8888; + } + } + } +} + + + diff --git a/src/Gtk/Avalonia.Gtk3/Interop/DynLoader.cs b/src/Gtk/Avalonia.Gtk3/Interop/DynLoader.cs index 93ba7cc195..afe526e2c3 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/DynLoader.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/DynLoader.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Runtime.InteropServices; /* @@ -11,7 +12,7 @@ namespace Avalonia.Gtk3.Interop { internal interface IDynLoader { - IntPtr LoadLibrary(string basePath, string dll); + IntPtr LoadLibrary(string dll); IntPtr GetProcAddress(IntPtr dll, string proc); } @@ -21,7 +22,6 @@ namespace Avalonia.Gtk3.Interop // ReSharper disable InconsistentNaming static class LinuxImports { - [DllImport("libdl.so.2")] private static extern IntPtr dlopen(string path, int flags); @@ -36,7 +36,6 @@ namespace Avalonia.Gtk3.Interop DlOpen = dlopen; DlSym = dlsym; DlError = dlerror; - Postfix = ".so.0"; } } static class OsXImports @@ -57,7 +56,6 @@ namespace Avalonia.Gtk3.Interop DlOpen = dlopen; DlSym = dlsym; DlError = dlerror; - Postfix = ".dylib"; //TODO } } @@ -66,8 +64,6 @@ namespace Avalonia.Gtk3.Interop [DllImport("libc")] static extern int uname(IntPtr buf); - - static UnixLoader() { var buffer = Marshal.AllocHGlobal(0x1000); @@ -83,20 +79,12 @@ namespace Avalonia.Gtk3.Interop private static Func DlOpen; private static Func DlSym; private static Func DlError; - private static string Postfix; // ReSharper restore InconsistentNaming - static string DlErrorString() - { - - return Marshal.PtrToStringAnsi(DlError()); - } + static string DlErrorString() => Marshal.PtrToStringAnsi(DlError()); - public IntPtr LoadLibrary(string basePath, string dll) + public IntPtr LoadLibrary(string dll) { - dll += Postfix; - if (basePath != null) - dll = System.IO.Path.Combine(basePath, dll); var handle = DlOpen(dll, 1); if (handle == IntPtr.Zero) throw new NativeException(DlErrorString()); @@ -120,15 +108,14 @@ namespace Avalonia.Gtk3.Interop [DllImport("kernel32", EntryPoint = "LoadLibraryW", SetLastError = true, CharSet = CharSet.Unicode)] private static extern IntPtr LoadLibrary(string lpszLib); - IntPtr IDynLoader.LoadLibrary(string basePath, string dll) + IntPtr IDynLoader.LoadLibrary(string dll) { - dll += "-0.dll"; - if (basePath != null) - dll = System.IO.Path.Combine(basePath, dll); var handle = LoadLibrary(dll); - if (handle == IntPtr.Zero) - throw new NativeException("Error " + Marshal.GetLastWin32Error()); - return handle; + if (handle != IntPtr.Zero) + return handle; + var err = Marshal.GetLastWin32Error(); + + throw new NativeException("Error loading " + dll + " error " + err); } IntPtr IDynLoader.GetProcAddress(IntPtr dll, string proc) diff --git a/src/Gtk/Avalonia.Gtk3/Interop/ICustomGtk3NativeLibraryResolver.cs b/src/Gtk/Avalonia.Gtk3/Interop/ICustomGtk3NativeLibraryResolver.cs new file mode 100644 index 0000000000..2f88b09896 --- /dev/null +++ b/src/Gtk/Avalonia.Gtk3/Interop/ICustomGtk3NativeLibraryResolver.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Avalonia.Gtk3.Interop; + +namespace Avalonia.Gtk3 +{ + public interface ICustomGtk3NativeLibraryResolver + { + string GetName(GtkDll dll); + string BasePath { get; } + bool TrySystemFirst { get; } + string Lookup(GtkDll dll); + } +} diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs index d9cf9fff05..4ed7474fb1 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs @@ -80,6 +80,33 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_window_move(IntPtr gtkWindow, int x, int y); + + + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] + public delegate IntPtr cairo_image_surface_create(int format, int width, int height); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] + public delegate IntPtr cairo_image_surface_get_data(IntPtr surface); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] + public delegate int cairo_image_surface_get_stride(IntPtr surface); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] + public delegate void cairo_surface_mark_dirty(IntPtr surface); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] + public delegate void cairo_surface_flush(IntPtr surface); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] + public delegate void cairo_surface_destroy(IntPtr surface); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] + public delegate void cairo_set_source_surface(IntPtr cr, IntPtr surface, double x, double y); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] + public delegate void cairo_paint(IntPtr context); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_widget_queue_draw_area(IntPtr gtkWindow, int x, int y, int width, int height); @@ -130,7 +157,14 @@ namespace Avalonia.Gtk3.Interop public static D.gdk_screen_get_width GdkScreenGetWidth; public static D.gdk_window_get_origin GdkWindowGetOrigin; - + public static D.cairo_image_surface_create CairoImageSurfaceCreate; + public static D.cairo_image_surface_get_data CairoImageSurfaceGetData; + public static D.cairo_image_surface_get_stride CairoImageSurfaceGetStride; + public static D.cairo_surface_mark_dirty CairoSurfaceMarkDirty; + public static D.cairo_surface_flush CairoSurfaceFlush; + public static D.cairo_surface_destroy CairoSurfaceDestroy; + public static D.cairo_set_source_surface CairoSetSourceSurface; + public static D.cairo_paint CairoPaint; } public enum GtkWindowType diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs b/src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs index 15a2f90ade..a965453841 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; +using Avalonia.Platform; namespace Avalonia.Gtk3.Interop { @@ -22,64 +24,108 @@ namespace Avalonia.Gtk3.Interop } } - internal enum GtkDll + public enum GtkDll { Gdk, Gtk, Glib, Gio, - Gobject + Gobject, + Cairo } static class Resolver { - [DllImport("kernel32.dll")] - static extern int GetVersion(); + private static Lazy Platform = + new Lazy( + () => AvaloniaLocator.Current.GetService().GetRuntimeInfo().OperatingSystem); - static bool IsWin32() + public static ICustomGtk3NativeLibraryResolver Custom { get; set; } + + + static string FormatName(string name, int version = 0) + { + if (Platform.Value == OperatingSystemType.WinNT) + return "lib" + name + "-" + version + ".dll"; + if (Platform.Value == OperatingSystemType.Linux) + return "lib" + name + ".so" + "." + version; + if (Platform.Value == OperatingSystemType.OSX) + return "lib" + name + "." + version + ".dylib"; + throw new Exception("Unknown platform, use custom name resolver"); + } + + + + static string GetDllName(GtkDll dll) { + var name = Custom?.GetName(dll); + if (name != null) + return name; + + switch (dll) + { + case GtkDll.Cairo: + return FormatName("cairo", 2); + case GtkDll.Gdk: + return FormatName("gdk-3"); + case GtkDll.Glib: + return FormatName("glib-2.0"); + case GtkDll.Gio: + return FormatName("gio-2.0"); + case GtkDll.Gtk: + return FormatName("gtk-3"); + case GtkDll.Gobject: + return FormatName("gobject-2.0"); + default: + throw new ArgumentException("Unknown lib: " + dll); + } + } + + static IntPtr LoadDll(IDynLoader loader, GtkDll dll) + { + + var exceptions = new List(); + + var name = GetDllName(dll); + if (Custom?.TrySystemFirst != false) + { + try + { + return loader.LoadLibrary(name); + } + catch (Exception e) + { + exceptions.Add(e); + } + } + var path = Custom?.Lookup(dll); + if (path == null && Custom?.BasePath != null) + path = Path.Combine(Custom.BasePath, name); + try { - GetVersion(); - return true; + return loader.LoadLibrary(path); } - catch + catch (Exception e) { - return false; + exceptions.Add(e); } + throw new AggregateException("Unable to load " + dll, exceptions); } - - public static void Resolve(string basePath = null) { - var loader = IsWin32() ? (IDynLoader)new Win32Loader() : new UnixLoader(); - + var loader = Platform.Value == OperatingSystemType.WinNT ? (IDynLoader)new Win32Loader() : new UnixLoader(); - var gdk = loader.LoadLibrary(basePath, "libgdk-3"); - var gtk = loader.LoadLibrary(basePath, "libgtk-3"); - var gio = loader.LoadLibrary(basePath, "libgio-2.0"); - var glib = loader.LoadLibrary(basePath, "libglib-2.0"); - var gobject = loader.LoadLibrary(basePath, "libgobject-2.0"); + var dlls = Enum.GetValues(typeof(GtkDll)).Cast().ToDictionary(x => x, x => LoadDll(loader, x)); + foreach (var fieldInfo in typeof(Native).GetTypeInfo().DeclaredFields) { var import = fieldInfo.FieldType.GetTypeInfo().GetCustomAttributes(typeof(GtkImportAttribute), true).Cast().FirstOrDefault(); if(import == null) continue; - IntPtr lib; - if (import.Dll == GtkDll.Gtk) - lib = gtk; - else if (import.Dll == GtkDll.Gdk) - lib = gdk; - else if (import.Dll == GtkDll.Gio) - lib = gio; - else if (import.Dll == GtkDll.Glib) - lib = glib; - else if (import.Dll == GtkDll.Gobject) - lib = gobject; - else - throw new ArgumentException("Invalid GtkImportAttribute for " + fieldInfo.FieldType); - + IntPtr lib = dlls[import.Dll]; + var funcPtr = loader.GetProcAddress(lib, import.Name ?? fieldInfo.FieldType.Name); fieldInfo.SetValue(null, Marshal.GetDelegateForFunctionPointer(funcPtr, fieldInfo.FieldType)); } @@ -90,7 +136,7 @@ namespace Avalonia.Gtk3.Interop try { Native.GetNativeGdkWindowHandle = (Native.D.gdk_get_native_handle)Marshal - .GetDelegateForFunctionPointer(loader.GetProcAddress(gdk, name), typeof(Native.D.gdk_get_native_handle)); + .GetDelegateForFunctionPointer(loader.GetProcAddress(dlls[GtkDll.Gdk], name), typeof(Native.D.gdk_get_native_handle)); break; } catch { } @@ -103,3 +149,4 @@ namespace Avalonia.Gtk3.Interop } } + diff --git a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs index fc0893bdb8..4d5509f4f7 100644 --- a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs @@ -13,11 +13,13 @@ namespace Avalonia.Gtk3 { protected readonly IntPtr GtkWidget; private IInputRoot _inputRoot; + private readonly FramebufferManager _framebuffer; protected readonly List _disposables = new List(); public TopLevelImpl(IntPtr gtkWidget) { GtkWidget = gtkWidget; + _framebuffer = new FramebufferManager(this); Native.GtkWidgetSetEvents(gtkWidget, uint.MaxValue); Native.GtkWidgetRealize(gtkWidget); Connect("draw", OnDraw); @@ -126,9 +128,13 @@ namespace Avalonia.Gtk3 void Connect(string name, T handler) => _disposables.Add(Signal.Connect(GtkWidget, name, handler)); + internal IntPtr CurrentCairoContext { get; private set; } + private bool OnDraw(IntPtr gtkwidget, IntPtr cairocontext, IntPtr userdata) { + CurrentCairoContext = cairocontext; Paint?.Invoke(new Rect(ClientSize)); + CurrentCairoContext = IntPtr.Zero; return true; } @@ -234,6 +240,6 @@ namespace Avalonia.Gtk3 } IntPtr IPlatformHandle.Handle => Native.GetNativeGdkWindowHandle(Native.GtkWidgetGetWindow(GtkWidget)); - public IEnumerable Surfaces => new object[] {Handle}; + public IEnumerable Surfaces => new object[] {Handle, _framebuffer}; } } From 480801864a1b5d422dbeed7c8a87a327a60d980a Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 26 Jan 2017 23:15:52 +0300 Subject: [PATCH 06/24] Mono support --- src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs | 3 ++- src/Gtk/Avalonia.Gtk3/Interop/Native.cs | 4 ++-- src/Gtk/Avalonia.Gtk3/Interop/Signal.cs | 11 +++++++---- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs index 625cd7096a..2aee41e617 100644 --- a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs +++ b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs @@ -24,7 +24,8 @@ namespace Avalonia.Gtk3 { Resolver.Resolve(); Native.GtkInit(0, IntPtr.Zero); - App = Native.GtkApplicationNew("avalonia.app." + Guid.NewGuid(), 0); + using (var utf = new Utf8Buffer("avalonia.app." + Guid.NewGuid())) + App = Native.GtkApplicationNew(utf, 0); //Mark current thread as UI thread s_tlsMarker = true; diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs index 4ed7474fb1..aa3e73f7c4 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs @@ -20,7 +20,7 @@ namespace Avalonia.Gtk3.Interop public static class D { [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate IntPtr gtk_application_new([MarshalAs(UnmanagedType.AnsiBStr)] string appId, int flags); + public delegate IntPtr gtk_application_new(Utf8Buffer appId, int flags); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_main_iteration(); @@ -113,7 +113,7 @@ namespace Avalonia.Gtk3.Interop [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); + public delegate ulong g_signal_connect_object(IntPtr instance, Utf8Buffer 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)] diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Signal.cs b/src/Gtk/Avalonia.Gtk3/Interop/Signal.cs index 5896c547f2..b2ccdcf408 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Signal.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Signal.cs @@ -36,10 +36,13 @@ namespace Avalonia.Gtk3.Interop { var handle = GCHandle.Alloc(handler); var ptr = Marshal.GetFunctionPointerForDelegate((Delegate)(object)handler); - var id = Native.GSignalConnectObject(obj, name, ptr, IntPtr.Zero, 0); - if (id == 0) - throw new ArgumentException("Unable to connect to signal " + name); - return new ConnectedSignal(obj, handle, id); + using (var utf = new Utf8Buffer(name)) + { + var id = Native.GSignalConnectObject(obj, utf, ptr, IntPtr.Zero, 0); + if (id == 0) + throw new ArgumentException("Unable to connect to signal " + name); + return new ConnectedSignal(obj, handle, id); + } } } } From 7ad30a564c1eefdc6caf2d988b356b0e91cc0e60 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sat, 28 Jan 2017 16:43:37 +0300 Subject: [PATCH 07/24] Cairo surface format seems to be always Rgba8888 for some reason --- src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs b/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs index e955f82764..a2b06d0d5e 100644 --- a/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs +++ b/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs @@ -54,16 +54,8 @@ namespace Avalonia.Gtk3 //TODO: Proper DPI detect public Size Dpi => new Size(96, 96); - public PixelFormat Format - { - get - { - if (AvaloniaLocator.Current.GetService().GetRuntimeInfo().OperatingSystem == - OperatingSystemType.WinNT) - return PixelFormat.Bgra8888; - return PixelFormat.Rgba8888; - } - } + + public PixelFormat Format => PixelFormat.Bgra8888; } } From 2be97bad853d9c70c9c67d23532cecb61c92bfe1 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Mon, 30 Jan 2017 20:38:12 +0000 Subject: [PATCH 08/24] fixes null reference exception on gtk backend. --- src/Gtk/Avalonia.Gtk/SystemDialogImpl.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Gtk/Avalonia.Gtk/SystemDialogImpl.cs b/src/Gtk/Avalonia.Gtk/SystemDialogImpl.cs index ed92a25873..962621856a 100644 --- a/src/Gtk/Avalonia.Gtk/SystemDialogImpl.cs +++ b/src/Gtk/Avalonia.Gtk/SystemDialogImpl.cs @@ -15,7 +15,7 @@ namespace Avalonia.Gtk public Task ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent) { var tcs = new TaskCompletionSource(); - var dlg = new global::Gtk.FileChooserDialog(dialog.Title, ((WindowImplBase)parent).Widget.Toplevel as Window, + var dlg = new global::Gtk.FileChooserDialog(dialog.Title, ((WindowImplBase)parent)?.Widget.Toplevel as Window, dialog is OpenFileDialog ? FileChooserAction.Open : FileChooserAction.Save, @@ -57,7 +57,7 @@ namespace Avalonia.Gtk public Task ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent) { var tcs = new TaskCompletionSource(); - var dlg = new global::Gtk.FileChooserDialog(dialog.Title, ((WindowImplBase)parent).Widget.Toplevel as Window, + var dlg = new global::Gtk.FileChooserDialog(dialog.Title, ((WindowImplBase)parent)?.Widget.Toplevel as Window, FileChooserAction.SelectFolder, "Cancel", ResponseType.Cancel, "Select Folder", ResponseType.Accept) From c277909e4376cddefcee0441d5063b804781e4de Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 31 Jan 2017 03:29:36 +0300 Subject: [PATCH 09/24] Implemented text input, activation/deactivation and window drags --- src/Gtk/Avalonia.Gtk/Avalonia.Gtk.csproj | 1 + .../Avalonia.Gtk/Input/GtkKeyboardDevice.cs | 209 +-- src/Gtk/Avalonia.Gtk/KeyTransform.cs | 224 +++ src/Gtk/Avalonia.Gtk/WindowImplBase.cs | 2 +- src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj | 8 +- src/Gtk/Avalonia.Gtk3/GdkKey.cs | 1347 +++++++++++++++++ src/Gtk/Avalonia.Gtk3/Interop/Native.cs | 81 + src/Gtk/Avalonia.Gtk3/README.md | 3 +- src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs | 110 +- 9 files changed, 1749 insertions(+), 236 deletions(-) create mode 100644 src/Gtk/Avalonia.Gtk/KeyTransform.cs create mode 100644 src/Gtk/Avalonia.Gtk3/GdkKey.cs diff --git a/src/Gtk/Avalonia.Gtk/Avalonia.Gtk.csproj b/src/Gtk/Avalonia.Gtk/Avalonia.Gtk.csproj index 540e70cee0..07a9ab56ed 100644 --- a/src/Gtk/Avalonia.Gtk/Avalonia.Gtk.csproj +++ b/src/Gtk/Avalonia.Gtk/Avalonia.Gtk.csproj @@ -52,6 +52,7 @@ + diff --git a/src/Gtk/Avalonia.Gtk/Input/GtkKeyboardDevice.cs b/src/Gtk/Avalonia.Gtk/Input/GtkKeyboardDevice.cs index 4e88dcda9a..a97a6190f1 100644 --- a/src/Gtk/Avalonia.Gtk/Input/GtkKeyboardDevice.cs +++ b/src/Gtk/Avalonia.Gtk/Input/GtkKeyboardDevice.cs @@ -8,218 +8,11 @@ using System.Reflection; using System.Text; using Avalonia.Input; + namespace Avalonia.Gtk { public class GtkKeyboardDevice : KeyboardDevice { - private static readonly Dictionary KeyDic = new Dictionary - { - { Gdk.Key.Cancel, Key.Cancel }, - { Gdk.Key.BackSpace, Key.Back }, - { Gdk.Key.Tab, Key.Tab }, - { Gdk.Key.Linefeed, Key.LineFeed }, - { Gdk.Key.Clear, Key.Clear }, - { Gdk.Key.Return, Key.Return }, - { Gdk.Key.Pause, Key.Pause }, - //{ Gdk.Key.?, Key.CapsLock } - //{ Gdk.Key.?, Key.HangulMode } - //{ Gdk.Key.?, Key.JunjaMode } - //{ Gdk.Key.?, Key.FinalMode } - //{ Gdk.Key.?, Key.KanjiMode } - { Gdk.Key.Escape, Key.Escape }, - //{ Gdk.Key.?, Key.ImeConvert } - //{ Gdk.Key.?, Key.ImeNonConvert } - //{ Gdk.Key.?, Key.ImeAccept } - //{ Gdk.Key.?, Key.ImeModeChange } - { Gdk.Key.space, Key.Space }, - { Gdk.Key.Prior, Key.Prior }, - //{ Gdk.Key.?, Key.PageDown } - { Gdk.Key.End, Key.End }, - { Gdk.Key.Home, Key.Home }, - { Gdk.Key.Left, Key.Left }, - { Gdk.Key.Up, Key.Up }, - { Gdk.Key.Right, Key.Right }, - { Gdk.Key.Down, Key.Down }, - { Gdk.Key.Select, Key.Select }, - { Gdk.Key.Print, Key.Print }, - { Gdk.Key.Execute, Key.Execute }, - //{ Gdk.Key.?, Key.Snapshot } - { Gdk.Key.Insert, Key.Insert }, - { Gdk.Key.Delete, Key.Delete }, - { Gdk.Key.Help, Key.Help }, - //{ Gdk.Key.?, Key.D0 } - //{ Gdk.Key.?, Key.D1 } - //{ Gdk.Key.?, Key.D2 } - //{ Gdk.Key.?, Key.D3 } - //{ Gdk.Key.?, Key.D4 } - //{ Gdk.Key.?, Key.D5 } - //{ Gdk.Key.?, Key.D6 } - //{ Gdk.Key.?, Key.D7 } - //{ Gdk.Key.?, Key.D8 } - //{ Gdk.Key.?, Key.D9 } - { Gdk.Key.A, Key.A }, - { Gdk.Key.B, Key.B }, - { Gdk.Key.C, Key.C }, - { Gdk.Key.D, Key.D }, - { Gdk.Key.E, Key.E }, - { Gdk.Key.F, Key.F }, - { Gdk.Key.G, Key.G }, - { Gdk.Key.H, Key.H }, - { Gdk.Key.I, Key.I }, - { Gdk.Key.J, Key.J }, - { Gdk.Key.K, Key.K }, - { Gdk.Key.L, Key.L }, - { Gdk.Key.M, Key.M }, - { Gdk.Key.N, Key.N }, - { Gdk.Key.O, Key.O }, - { Gdk.Key.P, Key.P }, - { Gdk.Key.Q, Key.Q }, - { Gdk.Key.R, Key.R }, - { Gdk.Key.S, Key.S }, - { Gdk.Key.T, Key.T }, - { Gdk.Key.U, Key.U }, - { Gdk.Key.V, Key.V }, - { Gdk.Key.W, Key.W }, - { Gdk.Key.X, Key.X }, - { Gdk.Key.Y, Key.Y }, - { Gdk.Key.Z, Key.Z }, - { Gdk.Key.a, Key.A }, - { Gdk.Key.b, Key.B }, - { Gdk.Key.c, Key.C }, - { Gdk.Key.d, Key.D }, - { Gdk.Key.e, Key.E }, - { Gdk.Key.f, Key.F }, - { Gdk.Key.g, Key.G }, - { Gdk.Key.h, Key.H }, - { Gdk.Key.i, Key.I }, - { Gdk.Key.j, Key.J }, - { Gdk.Key.k, Key.K }, - { Gdk.Key.l, Key.L }, - { Gdk.Key.m, Key.M }, - { Gdk.Key.n, Key.N }, - { Gdk.Key.o, Key.O }, - { Gdk.Key.p, Key.P }, - { Gdk.Key.q, Key.Q }, - { Gdk.Key.r, Key.R }, - { Gdk.Key.s, Key.S }, - { Gdk.Key.t, Key.T }, - { Gdk.Key.u, Key.U }, - { Gdk.Key.v, Key.V }, - { Gdk.Key.w, Key.W }, - { Gdk.Key.x, Key.X }, - { Gdk.Key.y, Key.Y }, - { Gdk.Key.z, Key.Z }, - //{ Gdk.Key.?, Key.LWin } - //{ Gdk.Key.?, Key.RWin } - //{ Gdk.Key.?, Key.Apps } - //{ Gdk.Key.?, Key.Sleep } - //{ Gdk.Key.?, Key.NumPad0 } - //{ Gdk.Key.?, Key.NumPad1 } - //{ Gdk.Key.?, Key.NumPad2 } - //{ Gdk.Key.?, Key.NumPad3 } - //{ Gdk.Key.?, Key.NumPad4 } - //{ Gdk.Key.?, Key.NumPad5 } - //{ Gdk.Key.?, Key.NumPad6 } - //{ Gdk.Key.?, Key.NumPad7 } - //{ Gdk.Key.?, Key.NumPad8 } - //{ Gdk.Key.?, Key.NumPad9 } - { Gdk.Key.multiply, Key.Multiply }, - //{ Gdk.Key.?, Key.Add } - //{ Gdk.Key.?, Key.Separator } - //{ Gdk.Key.?, Key.Subtract } - //{ Gdk.Key.?, Key.Decimal } - //{ Gdk.Key.?, Key.Divide } - { Gdk.Key.F1, Key.F1 }, - { Gdk.Key.F2, Key.F2 }, - { Gdk.Key.F3, Key.F3 }, - { Gdk.Key.F4, Key.F4 }, - { Gdk.Key.F5, Key.F5 }, - { Gdk.Key.F6, Key.F6 }, - { Gdk.Key.F7, Key.F7 }, - { Gdk.Key.F8, Key.F8 }, - { Gdk.Key.F9, Key.F9 }, - { Gdk.Key.F10, Key.F10 }, - { Gdk.Key.F11, Key.F11 }, - { Gdk.Key.F12, Key.F12 }, - { Gdk.Key.L3, Key.F13 }, - { Gdk.Key.F14, Key.F14 }, - { Gdk.Key.L5, Key.F15 }, - { Gdk.Key.F16, Key.F16 }, - { Gdk.Key.F17, Key.F17 }, - { Gdk.Key.L8, Key.F18 }, - { Gdk.Key.L9, Key.F19 }, - { Gdk.Key.L10, Key.F20 }, - { Gdk.Key.R1, Key.F21 }, - { Gdk.Key.R2, Key.F22 }, - { Gdk.Key.F23, Key.F23 }, - { Gdk.Key.R4, Key.F24 }, - //{ Gdk.Key.?, Key.NumLock } - //{ Gdk.Key.?, Key.Scroll } - //{ Gdk.Key.?, Key.LeftShift } - //{ Gdk.Key.?, Key.RightShift } - //{ Gdk.Key.?, Key.LeftCtrl } - //{ Gdk.Key.?, Key.RightCtrl } - //{ Gdk.Key.?, Key.LeftAlt } - //{ Gdk.Key.?, Key.RightAlt } - //{ Gdk.Key.?, Key.BrowserBack } - //{ Gdk.Key.?, Key.BrowserForward } - //{ Gdk.Key.?, Key.BrowserRefresh } - //{ Gdk.Key.?, Key.BrowserStop } - //{ Gdk.Key.?, Key.BrowserSearch } - //{ Gdk.Key.?, Key.BrowserFavorites } - //{ Gdk.Key.?, Key.BrowserHome } - //{ Gdk.Key.?, Key.VolumeMute } - //{ Gdk.Key.?, Key.VolumeDown } - //{ Gdk.Key.?, Key.VolumeUp } - //{ Gdk.Key.?, Key.MediaNextTrack } - //{ Gdk.Key.?, Key.MediaPreviousTrack } - //{ Gdk.Key.?, Key.MediaStop } - //{ Gdk.Key.?, Key.MediaPlayPause } - //{ Gdk.Key.?, Key.LaunchMail } - //{ Gdk.Key.?, Key.SelectMedia } - //{ Gdk.Key.?, Key.LaunchApplication1 } - //{ Gdk.Key.?, Key.LaunchApplication2 } - //{ Gdk.Key.?, Key.OemSemicolon } - //{ Gdk.Key.?, Key.OemPlus } - //{ Gdk.Key.?, Key.OemComma } - //{ Gdk.Key.?, Key.OemMinus } - //{ Gdk.Key.?, Key.OemPeriod } - //{ Gdk.Key.?, Key.Oem2 } - //{ Gdk.Key.?, Key.OemTilde } - //{ Gdk.Key.?, Key.AbntC1 } - //{ Gdk.Key.?, Key.AbntC2 } - //{ Gdk.Key.?, Key.Oem4 } - //{ Gdk.Key.?, Key.OemPipe } - //{ Gdk.Key.?, Key.OemCloseBrackets } - //{ Gdk.Key.?, Key.Oem7 } - //{ Gdk.Key.?, Key.Oem8 } - //{ Gdk.Key.?, Key.Oem102 } - //{ Gdk.Key.?, Key.ImeProcessed } - //{ Gdk.Key.?, Key.System } - //{ Gdk.Key.?, Key.OemAttn } - //{ Gdk.Key.?, Key.OemFinish } - //{ Gdk.Key.?, Key.DbeHiragana } - //{ Gdk.Key.?, Key.OemAuto } - //{ Gdk.Key.?, Key.DbeDbcsChar } - //{ Gdk.Key.?, Key.OemBackTab } - //{ Gdk.Key.?, Key.Attn } - //{ Gdk.Key.?, Key.DbeEnterWordRegisterMode } - //{ Gdk.Key.?, Key.DbeEnterImeConfigureMode } - //{ Gdk.Key.?, Key.EraseEof } - //{ Gdk.Key.?, Key.Play } - //{ Gdk.Key.?, Key.Zoom } - //{ Gdk.Key.?, Key.NoName } - //{ Gdk.Key.?, Key.DbeEnterDialogConversionMode } - //{ Gdk.Key.?, Key.OemClear } - //{ Gdk.Key.?, Key.DeadCharProcessed } - }; - public new static GtkKeyboardDevice Instance { get; } = new GtkKeyboardDevice(); - - public static Key ConvertKey(Gdk.Key key) - { - Key result; - return KeyDic.TryGetValue(key, out result) ? result : Key.None; - } } } \ No newline at end of file diff --git a/src/Gtk/Avalonia.Gtk/KeyTransform.cs b/src/Gtk/Avalonia.Gtk/KeyTransform.cs new file mode 100644 index 0000000000..77de8d18a3 --- /dev/null +++ b/src/Gtk/Avalonia.Gtk/KeyTransform.cs @@ -0,0 +1,224 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Avalonia.Input; +#if GTK3_PINVOKE +using Avalonia.Gtk3; +#else +using GdkKey = Gdk.Key; +#endif +namespace Avalonia.Gtk.Common +{ + static class KeyTransform + { + private static readonly Dictionary KeyDic = new Dictionary + { + { GdkKey.Cancel, Key.Cancel }, + { GdkKey.BackSpace, Key.Back }, + { GdkKey.Tab, Key.Tab }, + { GdkKey.Linefeed, Key.LineFeed }, + { GdkKey.Clear, Key.Clear }, + { GdkKey.Return, Key.Return }, + { GdkKey.Pause, Key.Pause }, + //{ GdkKey.?, Key.CapsLock } + //{ GdkKey.?, Key.HangulMode } + //{ GdkKey.?, Key.JunjaMode } + //{ GdkKey.?, Key.FinalMode } + //{ GdkKey.?, Key.KanjiMode } + { GdkKey.Escape, Key.Escape }, + //{ GdkKey.?, Key.ImeConvert } + //{ GdkKey.?, Key.ImeNonConvert } + //{ GdkKey.?, Key.ImeAccept } + //{ GdkKey.?, Key.ImeModeChange } + { GdkKey.space, Key.Space }, + { GdkKey.Prior, Key.Prior }, + //{ GdkKey.?, Key.PageDown } + { GdkKey.End, Key.End }, + { GdkKey.Home, Key.Home }, + { GdkKey.Left, Key.Left }, + { GdkKey.Up, Key.Up }, + { GdkKey.Right, Key.Right }, + { GdkKey.Down, Key.Down }, + { GdkKey.Select, Key.Select }, + { GdkKey.Print, Key.Print }, + { GdkKey.Execute, Key.Execute }, + //{ GdkKey.?, Key.Snapshot } + { GdkKey.Insert, Key.Insert }, + { GdkKey.Delete, Key.Delete }, + { GdkKey.Help, Key.Help }, + //{ GdkKey.?, Key.D0 } + //{ GdkKey.?, Key.D1 } + //{ GdkKey.?, Key.D2 } + //{ GdkKey.?, Key.D3 } + //{ GdkKey.?, Key.D4 } + //{ GdkKey.?, Key.D5 } + //{ GdkKey.?, Key.D6 } + //{ GdkKey.?, Key.D7 } + //{ GdkKey.?, Key.D8 } + //{ GdkKey.?, Key.D9 } + { GdkKey.A, Key.A }, + { GdkKey.B, Key.B }, + { GdkKey.C, Key.C }, + { GdkKey.D, Key.D }, + { GdkKey.E, Key.E }, + { GdkKey.F, Key.F }, + { GdkKey.G, Key.G }, + { GdkKey.H, Key.H }, + { GdkKey.I, Key.I }, + { GdkKey.J, Key.J }, + { GdkKey.K, Key.K }, + { GdkKey.L, Key.L }, + { GdkKey.M, Key.M }, + { GdkKey.N, Key.N }, + { GdkKey.O, Key.O }, + { GdkKey.P, Key.P }, + { GdkKey.Q, Key.Q }, + { GdkKey.R, Key.R }, + { GdkKey.S, Key.S }, + { GdkKey.T, Key.T }, + { GdkKey.U, Key.U }, + { GdkKey.V, Key.V }, + { GdkKey.W, Key.W }, + { GdkKey.X, Key.X }, + { GdkKey.Y, Key.Y }, + { GdkKey.Z, Key.Z }, + { GdkKey.a, Key.A }, + { GdkKey.b, Key.B }, + { GdkKey.c, Key.C }, + { GdkKey.d, Key.D }, + { GdkKey.e, Key.E }, + { GdkKey.f, Key.F }, + { GdkKey.g, Key.G }, + { GdkKey.h, Key.H }, + { GdkKey.i, Key.I }, + { GdkKey.j, Key.J }, + { GdkKey.k, Key.K }, + { GdkKey.l, Key.L }, + { GdkKey.m, Key.M }, + { GdkKey.n, Key.N }, + { GdkKey.o, Key.O }, + { GdkKey.p, Key.P }, + { GdkKey.q, Key.Q }, + { GdkKey.r, Key.R }, + { GdkKey.s, Key.S }, + { GdkKey.t, Key.T }, + { GdkKey.u, Key.U }, + { GdkKey.v, Key.V }, + { GdkKey.w, Key.W }, + { GdkKey.x, Key.X }, + { GdkKey.y, Key.Y }, + { GdkKey.z, Key.Z }, + //{ GdkKey.?, Key.LWin } + //{ GdkKey.?, Key.RWin } + //{ GdkKey.?, Key.Apps } + //{ GdkKey.?, Key.Sleep } + //{ GdkKey.?, Key.NumPad0 } + //{ GdkKey.?, Key.NumPad1 } + //{ GdkKey.?, Key.NumPad2 } + //{ GdkKey.?, Key.NumPad3 } + //{ GdkKey.?, Key.NumPad4 } + //{ GdkKey.?, Key.NumPad5 } + //{ GdkKey.?, Key.NumPad6 } + //{ GdkKey.?, Key.NumPad7 } + //{ GdkKey.?, Key.NumPad8 } + //{ GdkKey.?, Key.NumPad9 } + { GdkKey.multiply, Key.Multiply }, + //{ GdkKey.?, Key.Add } + //{ GdkKey.?, Key.Separator } + //{ GdkKey.?, Key.Subtract } + //{ GdkKey.?, Key.Decimal } + //{ GdkKey.?, Key.Divide } + { GdkKey.F1, Key.F1 }, + { GdkKey.F2, Key.F2 }, + { GdkKey.F3, Key.F3 }, + { GdkKey.F4, Key.F4 }, + { GdkKey.F5, Key.F5 }, + { GdkKey.F6, Key.F6 }, + { GdkKey.F7, Key.F7 }, + { GdkKey.F8, Key.F8 }, + { GdkKey.F9, Key.F9 }, + { GdkKey.F10, Key.F10 }, + { GdkKey.F11, Key.F11 }, + { GdkKey.F12, Key.F12 }, + { GdkKey.L3, Key.F13 }, + { GdkKey.F14, Key.F14 }, + { GdkKey.L5, Key.F15 }, + { GdkKey.F16, Key.F16 }, + { GdkKey.F17, Key.F17 }, + { GdkKey.L8, Key.F18 }, + { GdkKey.L9, Key.F19 }, + { GdkKey.L10, Key.F20 }, + { GdkKey.R1, Key.F21 }, + { GdkKey.R2, Key.F22 }, + { GdkKey.F23, Key.F23 }, + { GdkKey.R4, Key.F24 }, + //{ GdkKey.?, Key.NumLock } + //{ GdkKey.?, Key.Scroll } + //{ GdkKey.?, Key.LeftShift } + //{ GdkKey.?, Key.RightShift } + //{ GdkKey.?, Key.LeftCtrl } + //{ GdkKey.?, Key.RightCtrl } + //{ GdkKey.?, Key.LeftAlt } + //{ GdkKey.?, Key.RightAlt } + //{ GdkKey.?, Key.BrowserBack } + //{ GdkKey.?, Key.BrowserForward } + //{ GdkKey.?, Key.BrowserRefresh } + //{ GdkKey.?, Key.BrowserStop } + //{ GdkKey.?, Key.BrowserSearch } + //{ GdkKey.?, Key.BrowserFavorites } + //{ GdkKey.?, Key.BrowserHome } + //{ GdkKey.?, Key.VolumeMute } + //{ GdkKey.?, Key.VolumeDown } + //{ GdkKey.?, Key.VolumeUp } + //{ GdkKey.?, Key.MediaNextTrack } + //{ GdkKey.?, Key.MediaPreviousTrack } + //{ GdkKey.?, Key.MediaStop } + //{ GdkKey.?, Key.MediaPlayPause } + //{ GdkKey.?, Key.LaunchMail } + //{ GdkKey.?, Key.SelectMedia } + //{ GdkKey.?, Key.LaunchApplication1 } + //{ GdkKey.?, Key.LaunchApplication2 } + //{ GdkKey.?, Key.OemSemicolon } + //{ GdkKey.?, Key.OemPlus } + //{ GdkKey.?, Key.OemComma } + //{ GdkKey.?, Key.OemMinus } + //{ GdkKey.?, Key.OemPeriod } + //{ GdkKey.?, Key.Oem2 } + //{ GdkKey.?, Key.OemTilde } + //{ GdkKey.?, Key.AbntC1 } + //{ GdkKey.?, Key.AbntC2 } + //{ GdkKey.?, Key.Oem4 } + //{ GdkKey.?, Key.OemPipe } + //{ GdkKey.?, Key.OemCloseBrackets } + //{ GdkKey.?, Key.Oem7 } + //{ GdkKey.?, Key.Oem8 } + //{ GdkKey.?, Key.Oem102 } + //{ GdkKey.?, Key.ImeProcessed } + //{ GdkKey.?, Key.System } + //{ GdkKey.?, Key.OemAttn } + //{ GdkKey.?, Key.OemFinish } + //{ GdkKey.?, Key.DbeHiragana } + //{ GdkKey.?, Key.OemAuto } + //{ GdkKey.?, Key.DbeDbcsChar } + //{ GdkKey.?, Key.OemBackTab } + //{ GdkKey.?, Key.Attn } + //{ GdkKey.?, Key.DbeEnterWordRegisterMode } + //{ GdkKey.?, Key.DbeEnterImeConfigureMode } + //{ GdkKey.?, Key.EraseEof } + //{ GdkKey.?, Key.Play } + //{ GdkKey.?, Key.Zoom } + //{ GdkKey.?, Key.NoName } + //{ GdkKey.?, Key.DbeEnterDialogConversionMode } + //{ GdkKey.?, Key.OemClear } + //{ GdkKey.?, Key.DeadCharProcessed } + }; + + public static Key ConvertKey(GdkKey key) + { + Key result; + return KeyDic.TryGetValue(key, out result) ? result : Key.None; + } + } +} diff --git a/src/Gtk/Avalonia.Gtk/WindowImplBase.cs b/src/Gtk/Avalonia.Gtk/WindowImplBase.cs index db4b5e9dde..4d7552aa72 100644 --- a/src/Gtk/Avalonia.Gtk/WindowImplBase.cs +++ b/src/Gtk/Avalonia.Gtk/WindowImplBase.cs @@ -267,7 +267,7 @@ namespace Avalonia.Gtk GtkKeyboardDevice.Instance, evnt.Time, evnt.Type == EventType.KeyPress ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp, - GtkKeyboardDevice.ConvertKey(evnt.Key), GetModifierKeys(evnt.State)); + Common.KeyTransform.ConvertKey(evnt.Key), GetModifierKeys(evnt.State)); Input(e); } diff --git a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj index 06fc40458e..dd35532830 100644 --- a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj +++ b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj @@ -22,7 +22,7 @@ full false bin\Debug\ - DEBUG;TRACE + TRACE;DEBUG;GTK3_PINVOKE prompt 4 true @@ -31,7 +31,7 @@ pdbonly true bin\Release\ - TRACE + TRACE;GTK3_PINVOKE prompt 4 true @@ -41,7 +41,11 @@ + + KeyTransform.cs + + diff --git a/src/Gtk/Avalonia.Gtk3/GdkKey.cs b/src/Gtk/Avalonia.Gtk3/GdkKey.cs new file mode 100644 index 0000000000..b4a0f31b92 --- /dev/null +++ b/src/Gtk/Avalonia.Gtk3/GdkKey.cs @@ -0,0 +1,1347 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Avalonia.Gtk3 +{ + enum GdkKey + { + space = 32, + exclam = 33, + quotedbl = 34, + numbersign = 35, + dollar = 36, + percent = 37, + ampersand = 38, + apostrophe = 39, + quoteright = 39, + parenleft = 40, + parenright = 41, + asterisk = 42, + plus = 43, + comma = 44, + minus = 45, + period = 46, + slash = 47, + Key_0 = 48, + Key_1 = 49, + Key_2 = 50, + Key_3 = 51, + Key_4 = 52, + Key_5 = 53, + Key_6 = 54, + Key_7 = 55, + Key_8 = 56, + Key_9 = 57, + colon = 58, + semicolon = 59, + less = 60, + equal = 61, + greater = 62, + question = 63, + at = 64, + A = 65, + B = 66, + C = 67, + D = 68, + E = 69, + F = 70, + G = 71, + H = 72, + I = 73, + J = 74, + K = 75, + L = 76, + M = 77, + N = 78, + O = 79, + P = 80, + Q = 81, + R = 82, + S = 83, + T = 84, + U = 85, + V = 86, + W = 87, + X = 88, + Y = 89, + Z = 90, + bracketleft = 91, + backslash = 92, + bracketright = 93, + asciicircum = 94, + underscore = 95, + grave = 96, + quoteleft = 96, + a = 97, + b = 98, + c = 99, + d = 100, + e = 101, + f = 102, + g = 103, + h = 104, + i = 105, + j = 106, + k = 107, + l = 108, + m = 109, + n = 110, + o = 111, + p = 112, + q = 113, + r = 114, + s = 115, + t = 116, + u = 117, + v = 118, + w = 119, + x = 120, + y = 121, + z = 122, + braceleft = 123, + bar = 124, + braceright = 125, + asciitilde = 126, + nobreakspace = 160, + exclamdown = 161, + cent = 162, + sterling = 163, + currency = 164, + yen = 165, + brokenbar = 166, + section = 167, + diaeresis = 168, + copyright = 169, + ordfeminine = 170, + guillemotleft = 171, + notsign = 172, + hyphen = 173, + registered = 174, + macron = 175, + degree = 176, + plusminus = 177, + twosuperior = 178, + threesuperior = 179, + acute = 180, + mu = 181, + paragraph = 182, + periodcentered = 183, + cedilla = 184, + onesuperior = 185, + masculine = 186, + guillemotright = 187, + onequarter = 188, + onehalf = 189, + threequarters = 190, + questiondown = 191, + Agrave = 192, + Aacute = 193, + Acircumflex = 194, + Atilde = 195, + Adiaeresis = 196, + Aring = 197, + AE = 198, + Ccedilla = 199, + Egrave = 200, + Eacute = 201, + Ecircumflex = 202, + Ediaeresis = 203, + Igrave = 204, + Iacute = 205, + Icircumflex = 206, + Idiaeresis = 207, + ETH = 208, + Eth = 208, + Ntilde = 209, + Ograve = 210, + Oacute = 211, + Ocircumflex = 212, + Otilde = 213, + Odiaeresis = 214, + multiply = 215, + Ooblique = 216, + Ugrave = 217, + Uacute = 218, + Ucircumflex = 219, + Udiaeresis = 220, + Yacute = 221, + THORN = 222, + Thorn = 222, + ssharp = 223, + agrave = 224, + aacute = 225, + acircumflex = 226, + atilde = 227, + adiaeresis = 228, + aring = 229, + ae = 230, + ccedilla = 231, + egrave = 232, + eacute = 233, + ecircumflex = 234, + ediaeresis = 235, + igrave = 236, + iacute = 237, + icircumflex = 238, + idiaeresis = 239, + eth = 240, + ntilde = 241, + ograve = 242, + oacute = 243, + ocircumflex = 244, + otilde = 245, + odiaeresis = 246, + division = 247, + oslash = 248, + ugrave = 249, + uacute = 250, + ucircumflex = 251, + udiaeresis = 252, + yacute = 253, + thorn = 254, + ydiaeresis = 255, + Aogonek = 417, + breve = 418, + Lstroke = 419, + Lcaron = 421, + Sacute = 422, + Scaron = 425, + Scedilla = 426, + Tcaron = 427, + Zacute = 428, + Zcaron = 430, + Zabovedot = 431, + aogonek = 433, + ogonek = 434, + lstroke = 435, + lcaron = 437, + sacute = 438, + caron = 439, + scaron = 441, + scedilla = 442, + tcaron = 443, + zacute = 444, + doubleacute = 445, + zcaron = 446, + zabovedot = 447, + Racute = 448, + Abreve = 451, + Lacute = 453, + Cacute = 454, + Ccaron = 456, + Eogonek = 458, + Ecaron = 460, + Dcaron = 463, + Dstroke = 464, + Nacute = 465, + Ncaron = 466, + Odoubleacute = 469, + Rcaron = 472, + Uring = 473, + Udoubleacute = 475, + Tcedilla = 478, + racute = 480, + abreve = 483, + lacute = 485, + cacute = 486, + ccaron = 488, + eogonek = 490, + ecaron = 492, + dcaron = 495, + dstroke = 496, + nacute = 497, + ncaron = 498, + odoubleacute = 501, + rcaron = 504, + uring = 505, + udoubleacute = 507, + tcedilla = 510, + abovedot = 511, + Hstroke = 673, + Hcircumflex = 678, + Iabovedot = 681, + Gbreve = 683, + Jcircumflex = 684, + hstroke = 689, + hcircumflex = 694, + idotless = 697, + gbreve = 699, + jcircumflex = 700, + Cabovedot = 709, + Ccircumflex = 710, + Gabovedot = 725, + Gcircumflex = 728, + Ubreve = 733, + Scircumflex = 734, + cabovedot = 741, + ccircumflex = 742, + gabovedot = 757, + gcircumflex = 760, + ubreve = 765, + scircumflex = 766, + kappa = 930, + kra = 930, + Rcedilla = 931, + Itilde = 933, + Lcedilla = 934, + Emacron = 938, + Gcedilla = 939, + Tslash = 940, + rcedilla = 947, + itilde = 949, + lcedilla = 950, + emacron = 954, + gcedilla = 955, + tslash = 956, + ENG = 957, + eng = 959, + Amacron = 960, + Iogonek = 967, + Eabovedot = 972, + Imacron = 975, + Ncedilla = 977, + Omacron = 978, + Kcedilla = 979, + Uogonek = 985, + Utilde = 989, + Umacron = 990, + amacron = 992, + iogonek = 999, + eabovedot = 1004, + imacron = 1007, + ncedilla = 1009, + omacron = 1010, + kcedilla = 1011, + uogonek = 1017, + utilde = 1021, + umacron = 1022, + overline = 1150, + kana_fullstop = 1185, + kana_openingbracket = 1186, + kana_closingbracket = 1187, + kana_comma = 1188, + kana_conjunctive = 1189, + kana_middledot = 1189, + kana_WO = 1190, + kana_a = 1191, + kana_i = 1192, + kana_u = 1193, + kana_e = 1194, + kana_o = 1195, + kana_ya = 1196, + kana_yu = 1197, + kana_yo = 1198, + kana_tsu = 1199, + kana_tu = 1199, + prolongedsound = 1200, + kana_A = 1201, + kana_I = 1202, + kana_U = 1203, + kana_E = 1204, + kana_O = 1205, + kana_KA = 1206, + kana_KI = 1207, + kana_KU = 1208, + kana_KE = 1209, + kana_KO = 1210, + kana_SA = 1211, + kana_SHI = 1212, + kana_SU = 1213, + kana_SE = 1214, + kana_SO = 1215, + kana_TA = 1216, + kana_CHI = 1217, + kana_TI = 1217, + kana_TSU = 1218, + kana_TU = 1218, + kana_TE = 1219, + kana_TO = 1220, + kana_NA = 1221, + kana_NI = 1222, + kana_NU = 1223, + kana_NE = 1224, + kana_NO = 1225, + kana_HA = 1226, + kana_HI = 1227, + kana_FU = 1228, + kana_HU = 1228, + kana_HE = 1229, + kana_HO = 1230, + kana_MA = 1231, + kana_MI = 1232, + kana_MU = 1233, + kana_ME = 1234, + kana_MO = 1235, + kana_YA = 1236, + kana_YU = 1237, + kana_YO = 1238, + kana_RA = 1239, + kana_RI = 1240, + kana_RU = 1241, + kana_RE = 1242, + kana_RO = 1243, + kana_WA = 1244, + kana_N = 1245, + voicedsound = 1246, + semivoicedsound = 1247, + Arabic_comma = 1452, + Arabic_semicolon = 1467, + Arabic_question_mark = 1471, + Arabic_hamza = 1473, + Arabic_maddaonalef = 1474, + Arabic_hamzaonalef = 1475, + Arabic_hamzaonwaw = 1476, + Arabic_hamzaunderalef = 1477, + Arabic_hamzaonyeh = 1478, + Arabic_alef = 1479, + Arabic_beh = 1480, + Arabic_tehmarbuta = 1481, + Arabic_teh = 1482, + Arabic_theh = 1483, + Arabic_jeem = 1484, + Arabic_hah = 1485, + Arabic_khah = 1486, + Arabic_dal = 1487, + Arabic_thal = 1488, + Arabic_ra = 1489, + Arabic_zain = 1490, + Arabic_seen = 1491, + Arabic_sheen = 1492, + Arabic_sad = 1493, + Arabic_dad = 1494, + Arabic_tah = 1495, + Arabic_zah = 1496, + Arabic_ain = 1497, + Arabic_ghain = 1498, + Arabic_tatweel = 1504, + Arabic_feh = 1505, + Arabic_qaf = 1506, + Arabic_kaf = 1507, + Arabic_lam = 1508, + Arabic_meem = 1509, + Arabic_noon = 1510, + Arabic_ha = 1511, + Arabic_heh = 1511, + Arabic_waw = 1512, + Arabic_alefmaksura = 1513, + Arabic_yeh = 1514, + Arabic_fathatan = 1515, + Arabic_dammatan = 1516, + Arabic_kasratan = 1517, + Arabic_fatha = 1518, + Arabic_damma = 1519, + Arabic_kasra = 1520, + Arabic_shadda = 1521, + Arabic_sukun = 1522, + Serbian_dje = 1697, + Macedonia_gje = 1698, + Cyrillic_io = 1699, + Ukrainian_ie = 1700, + Ukranian_je = 1700, + Macedonia_dse = 1701, + Ukrainian_i = 1702, + Ukranian_i = 1702, + Ukrainian_yi = 1703, + Ukranian_yi = 1703, + Cyrillic_je = 1704, + Serbian_je = 1704, + Cyrillic_lje = 1705, + Serbian_lje = 1705, + Cyrillic_nje = 1706, + Serbian_nje = 1706, + Serbian_tshe = 1707, + Macedonia_kje = 1708, + Byelorussian_shortu = 1710, + Cyrillic_dzhe = 1711, + Serbian_dze = 1711, + numerosign = 1712, + Serbian_DJE = 1713, + Macedonia_GJE = 1714, + Cyrillic_IO = 1715, + Ukrainian_IE = 1716, + Ukranian_JE = 1716, + Macedonia_DSE = 1717, + Ukrainian_I = 1718, + Ukranian_I = 1718, + Ukrainian_YI = 1719, + Ukranian_YI = 1719, + Cyrillic_JE = 1720, + Serbian_JE = 1720, + Cyrillic_LJE = 1721, + Serbian_LJE = 1721, + Cyrillic_NJE = 1722, + Serbian_NJE = 1722, + Serbian_TSHE = 1723, + Macedonia_KJE = 1724, + Byelorussian_SHORTU = 1726, + Cyrillic_DZHE = 1727, + Serbian_DZE = 1727, + Cyrillic_yu = 1728, + Cyrillic_a = 1729, + Cyrillic_be = 1730, + Cyrillic_tse = 1731, + Cyrillic_de = 1732, + Cyrillic_ie = 1733, + Cyrillic_ef = 1734, + Cyrillic_ghe = 1735, + Cyrillic_ha = 1736, + Cyrillic_i = 1737, + Cyrillic_shorti = 1738, + Cyrillic_ka = 1739, + Cyrillic_el = 1740, + Cyrillic_em = 1741, + Cyrillic_en = 1742, + Cyrillic_o = 1743, + Cyrillic_pe = 1744, + Cyrillic_ya = 1745, + Cyrillic_er = 1746, + Cyrillic_es = 1747, + Cyrillic_te = 1748, + Cyrillic_u = 1749, + Cyrillic_zhe = 1750, + Cyrillic_ve = 1751, + Cyrillic_softsign = 1752, + Cyrillic_yeru = 1753, + Cyrillic_ze = 1754, + Cyrillic_sha = 1755, + Cyrillic_e = 1756, + Cyrillic_shcha = 1757, + Cyrillic_che = 1758, + Cyrillic_hardsign = 1759, + Cyrillic_YU = 1760, + Cyrillic_A = 1761, + Cyrillic_BE = 1762, + Cyrillic_TSE = 1763, + Cyrillic_DE = 1764, + Cyrillic_IE = 1765, + Cyrillic_EF = 1766, + Cyrillic_GHE = 1767, + Cyrillic_HA = 1768, + Cyrillic_I = 1769, + Cyrillic_SHORTI = 1770, + Cyrillic_KA = 1771, + Cyrillic_EL = 1772, + Cyrillic_EM = 1773, + Cyrillic_EN = 1774, + Cyrillic_O = 1775, + Cyrillic_PE = 1776, + Cyrillic_YA = 1777, + Cyrillic_ER = 1778, + Cyrillic_ES = 1779, + Cyrillic_TE = 1780, + Cyrillic_U = 1781, + Cyrillic_ZHE = 1782, + Cyrillic_VE = 1783, + Cyrillic_SOFTSIGN = 1784, + Cyrillic_YERU = 1785, + Cyrillic_ZE = 1786, + Cyrillic_SHA = 1787, + Cyrillic_E = 1788, + Cyrillic_SHCHA = 1789, + Cyrillic_CHE = 1790, + Cyrillic_HARDSIGN = 1791, + Greek_ALPHAaccent = 1953, + Greek_EPSILONaccent = 1954, + Greek_ETAaccent = 1955, + Greek_IOTAaccent = 1956, + Greek_IOTAdiaeresis = 1957, + Greek_OMICRONaccent = 1959, + Greek_UPSILONaccent = 1960, + Greek_UPSILONdieresis = 1961, + Greek_OMEGAaccent = 1963, + Greek_accentdieresis = 1966, + Greek_horizbar = 1967, + Greek_alphaaccent = 1969, + Greek_epsilonaccent = 1970, + Greek_etaaccent = 1971, + Greek_iotaaccent = 1972, + Greek_iotadieresis = 1973, + Greek_iotaaccentdieresis = 1974, + Greek_omicronaccent = 1975, + Greek_upsilonaccent = 1976, + Greek_upsilondieresis = 1977, + Greek_upsilonaccentdieresis = 1978, + Greek_omegaaccent = 1979, + Greek_ALPHA = 1985, + Greek_BETA = 1986, + Greek_GAMMA = 1987, + Greek_DELTA = 1988, + Greek_EPSILON = 1989, + Greek_ZETA = 1990, + Greek_ETA = 1991, + Greek_THETA = 1992, + Greek_IOTA = 1993, + Greek_KAPPA = 1994, + Greek_LAMBDA = 1995, + Greek_LAMDA = 1995, + Greek_MU = 1996, + Greek_NU = 1997, + Greek_XI = 1998, + Greek_OMICRON = 1999, + Greek_PI = 2000, + Greek_RHO = 2001, + Greek_SIGMA = 2002, + Greek_TAU = 2004, + Greek_UPSILON = 2005, + Greek_PHI = 2006, + Greek_CHI = 2007, + Greek_PSI = 2008, + Greek_OMEGA = 2009, + Greek_alpha = 2017, + Greek_beta = 2018, + Greek_gamma = 2019, + Greek_delta = 2020, + Greek_epsilon = 2021, + Greek_zeta = 2022, + Greek_eta = 2023, + Greek_theta = 2024, + Greek_iota = 2025, + Greek_kappa = 2026, + Greek_lambda = 2027, + Greek_lamda = 2027, + Greek_mu = 2028, + Greek_nu = 2029, + Greek_xi = 2030, + Greek_omicron = 2031, + Greek_pi = 2032, + Greek_rho = 2033, + Greek_sigma = 2034, + Greek_finalsmallsigma = 2035, + Greek_tau = 2036, + Greek_upsilon = 2037, + Greek_phi = 2038, + Greek_chi = 2039, + Greek_psi = 2040, + Greek_omega = 2041, + leftradical = 2209, + topleftradical = 2210, + horizconnector = 2211, + topintegral = 2212, + botintegral = 2213, + vertconnector = 2214, + topleftsqbracket = 2215, + botleftsqbracket = 2216, + toprightsqbracket = 2217, + botrightsqbracket = 2218, + topleftparens = 2219, + botleftparens = 2220, + toprightparens = 2221, + botrightparens = 2222, + leftmiddlecurlybrace = 2223, + rightmiddlecurlybrace = 2224, + topleftsummation = 2225, + botleftsummation = 2226, + topvertsummationconnector = 2227, + botvertsummationconnector = 2228, + toprightsummation = 2229, + botrightsummation = 2230, + rightmiddlesummation = 2231, + lessthanequal = 2236, + notequal = 2237, + greaterthanequal = 2238, + integral = 2239, + therefore = 2240, + variation = 2241, + infinity = 2242, + nabla = 2245, + approximate = 2248, + similarequal = 2249, + ifonlyif = 2253, + implies = 2254, + identical = 2255, + radical = 2262, + includedin = 2266, + includes = 2267, + intersection = 2268, + union = 2269, + logicaland = 2270, + logicalor = 2271, + partialderivative = 2287, + function = 2294, + leftarrow = 2299, + uparrow = 2300, + rightarrow = 2301, + downarrow = 2302, + blank = 2527, + soliddiamond = 2528, + checkerboard = 2529, + ht = 2530, + ff = 2531, + cr = 2532, + lf = 2533, + nl = 2536, + vt = 2537, + lowrightcorner = 2538, + uprightcorner = 2539, + upleftcorner = 2540, + lowleftcorner = 2541, + crossinglines = 2542, + horizlinescan1 = 2543, + horizlinescan3 = 2544, + horizlinescan5 = 2545, + horizlinescan7 = 2546, + horizlinescan9 = 2547, + leftt = 2548, + rightt = 2549, + bott = 2550, + topt = 2551, + vertbar = 2552, + emspace = 2721, + enspace = 2722, + em3space = 2723, + em4space = 2724, + digitspace = 2725, + punctspace = 2726, + thinspace = 2727, + hairspace = 2728, + emdash = 2729, + endash = 2730, + signifblank = 2732, + ellipsis = 2734, + doubbaselinedot = 2735, + onethird = 2736, + twothirds = 2737, + onefifth = 2738, + twofifths = 2739, + threefifths = 2740, + fourfifths = 2741, + onesixth = 2742, + fivesixths = 2743, + careof = 2744, + figdash = 2747, + leftanglebracket = 2748, + decimalpoint = 2749, + rightanglebracket = 2750, + marker = 2751, + oneeighth = 2755, + threeeighths = 2756, + fiveeighths = 2757, + seveneighths = 2758, + trademark = 2761, + signaturemark = 2762, + trademarkincircle = 2763, + leftopentriangle = 2764, + rightopentriangle = 2765, + emopencircle = 2766, + emopenrectangle = 2767, + leftsinglequotemark = 2768, + rightsinglequotemark = 2769, + leftdoublequotemark = 2770, + rightdoublequotemark = 2771, + prescription = 2772, + minutes = 2774, + seconds = 2775, + latincross = 2777, + hexagram = 2778, + filledrectbullet = 2779, + filledlefttribullet = 2780, + filledrighttribullet = 2781, + emfilledcircle = 2782, + emfilledrect = 2783, + enopencircbullet = 2784, + enopensquarebullet = 2785, + openrectbullet = 2786, + opentribulletup = 2787, + opentribulletdown = 2788, + openstar = 2789, + enfilledcircbullet = 2790, + enfilledsqbullet = 2791, + filledtribulletup = 2792, + filledtribulletdown = 2793, + leftpointer = 2794, + rightpointer = 2795, + club = 2796, + diamond = 2797, + heart = 2798, + maltesecross = 2800, + dagger = 2801, + doubledagger = 2802, + checkmark = 2803, + ballotcross = 2804, + musicalsharp = 2805, + musicalflat = 2806, + malesymbol = 2807, + femalesymbol = 2808, + telephone = 2809, + telephonerecorder = 2810, + phonographcopyright = 2811, + caret = 2812, + singlelowquotemark = 2813, + doublelowquotemark = 2814, + cursor = 2815, + leftcaret = 2979, + rightcaret = 2982, + downcaret = 2984, + upcaret = 2985, + overbar = 3008, + downtack = 3010, + upshoe = 3011, + downstile = 3012, + underbar = 3014, + jot = 3018, + quad = 3020, + uptack = 3022, + circle = 3023, + upstile = 3027, + downshoe = 3030, + rightshoe = 3032, + leftshoe = 3034, + lefttack = 3036, + righttack = 3068, + hebrew_doublelowline = 3295, + hebrew_aleph = 3296, + hebrew_bet = 3297, + hebrew_beth = 3297, + hebrew_gimel = 3298, + hebrew_gimmel = 3298, + hebrew_dalet = 3299, + hebrew_daleth = 3299, + hebrew_he = 3300, + hebrew_waw = 3301, + hebrew_zain = 3302, + hebrew_zayin = 3302, + hebrew_chet = 3303, + hebrew_het = 3303, + hebrew_tet = 3304, + hebrew_teth = 3304, + hebrew_yod = 3305, + hebrew_finalkaph = 3306, + hebrew_kaph = 3307, + hebrew_lamed = 3308, + hebrew_finalmem = 3309, + hebrew_mem = 3310, + hebrew_finalnun = 3311, + hebrew_nun = 3312, + hebrew_samech = 3313, + hebrew_samekh = 3313, + hebrew_ayin = 3314, + hebrew_finalpe = 3315, + hebrew_pe = 3316, + hebrew_finalzade = 3317, + hebrew_finalzadi = 3317, + hebrew_zade = 3318, + hebrew_zadi = 3318, + hebrew_kuf = 3319, + hebrew_qoph = 3319, + hebrew_resh = 3320, + hebrew_shin = 3321, + hebrew_taf = 3322, + hebrew_taw = 3322, + Thai_kokai = 3489, + Thai_khokhai = 3490, + Thai_khokhuat = 3491, + Thai_khokhwai = 3492, + Thai_khokhon = 3493, + Thai_khorakhang = 3494, + Thai_ngongu = 3495, + Thai_chochan = 3496, + Thai_choching = 3497, + Thai_chochang = 3498, + Thai_soso = 3499, + Thai_chochoe = 3500, + Thai_yoying = 3501, + Thai_dochada = 3502, + Thai_topatak = 3503, + Thai_thothan = 3504, + Thai_thonangmontho = 3505, + Thai_thophuthao = 3506, + Thai_nonen = 3507, + Thai_dodek = 3508, + Thai_totao = 3509, + Thai_thothung = 3510, + Thai_thothahan = 3511, + Thai_thothong = 3512, + Thai_nonu = 3513, + Thai_bobaimai = 3514, + Thai_popla = 3515, + Thai_phophung = 3516, + Thai_fofa = 3517, + Thai_phophan = 3518, + Thai_fofan = 3519, + Thai_phosamphao = 3520, + Thai_moma = 3521, + Thai_yoyak = 3522, + Thai_rorua = 3523, + Thai_ru = 3524, + Thai_loling = 3525, + Thai_lu = 3526, + Thai_wowaen = 3527, + Thai_sosala = 3528, + Thai_sorusi = 3529, + Thai_sosua = 3530, + Thai_hohip = 3531, + Thai_lochula = 3532, + Thai_oang = 3533, + Thai_honokhuk = 3534, + Thai_paiyannoi = 3535, + Thai_saraa = 3536, + Thai_maihanakat = 3537, + Thai_saraaa = 3538, + Thai_saraam = 3539, + Thai_sarai = 3540, + Thai_saraii = 3541, + Thai_saraue = 3542, + Thai_sarauee = 3543, + Thai_sarau = 3544, + Thai_sarauu = 3545, + Thai_phinthu = 3546, + Thai_maihanakat_maitho = 3550, + Thai_baht = 3551, + Thai_sarae = 3552, + Thai_saraae = 3553, + Thai_sarao = 3554, + Thai_saraaimaimuan = 3555, + Thai_saraaimaimalai = 3556, + Thai_lakkhangyao = 3557, + Thai_maiyamok = 3558, + Thai_maitaikhu = 3559, + Thai_maiek = 3560, + Thai_maitho = 3561, + Thai_maitri = 3562, + Thai_maichattawa = 3563, + Thai_thanthakhat = 3564, + Thai_nikhahit = 3565, + Thai_leksun = 3568, + Thai_leknung = 3569, + Thai_leksong = 3570, + Thai_leksam = 3571, + Thai_leksi = 3572, + Thai_lekha = 3573, + Thai_lekhok = 3574, + Thai_lekchet = 3575, + Thai_lekpaet = 3576, + Thai_lekkao = 3577, + Hangul_Kiyeog = 3745, + Hangul_SsangKiyeog = 3746, + Hangul_KiyeogSios = 3747, + Hangul_Nieun = 3748, + Hangul_NieunJieuj = 3749, + Hangul_NieunHieuh = 3750, + Hangul_Dikeud = 3751, + Hangul_SsangDikeud = 3752, + Hangul_Rieul = 3753, + Hangul_RieulKiyeog = 3754, + Hangul_RieulMieum = 3755, + Hangul_RieulPieub = 3756, + Hangul_RieulSios = 3757, + Hangul_RieulTieut = 3758, + Hangul_RieulPhieuf = 3759, + Hangul_RieulHieuh = 3760, + Hangul_Mieum = 3761, + Hangul_Pieub = 3762, + Hangul_SsangPieub = 3763, + Hangul_PieubSios = 3764, + Hangul_Sios = 3765, + Hangul_SsangSios = 3766, + Hangul_Ieung = 3767, + Hangul_Jieuj = 3768, + Hangul_SsangJieuj = 3769, + Hangul_Cieuc = 3770, + Hangul_Khieuq = 3771, + Hangul_Tieut = 3772, + Hangul_Phieuf = 3773, + Hangul_Hieuh = 3774, + Hangul_A = 3775, + Hangul_AE = 3776, + Hangul_YA = 3777, + Hangul_YAE = 3778, + Hangul_EO = 3779, + Hangul_E = 3780, + Hangul_YEO = 3781, + Hangul_YE = 3782, + Hangul_O = 3783, + Hangul_WA = 3784, + Hangul_WAE = 3785, + Hangul_OE = 3786, + Hangul_YO = 3787, + Hangul_U = 3788, + Hangul_WEO = 3789, + Hangul_WE = 3790, + Hangul_WI = 3791, + Hangul_YU = 3792, + Hangul_EU = 3793, + Hangul_YI = 3794, + Hangul_I = 3795, + Hangul_J_Kiyeog = 3796, + Hangul_J_SsangKiyeog = 3797, + Hangul_J_KiyeogSios = 3798, + Hangul_J_Nieun = 3799, + Hangul_J_NieunJieuj = 3800, + Hangul_J_NieunHieuh = 3801, + Hangul_J_Dikeud = 3802, + Hangul_J_Rieul = 3803, + Hangul_J_RieulKiyeog = 3804, + Hangul_J_RieulMieum = 3805, + Hangul_J_RieulPieub = 3806, + Hangul_J_RieulSios = 3807, + Hangul_J_RieulTieut = 3808, + Hangul_J_RieulPhieuf = 3809, + Hangul_J_RieulHieuh = 3810, + Hangul_J_Mieum = 3811, + Hangul_J_Pieub = 3812, + Hangul_J_PieubSios = 3813, + Hangul_J_Sios = 3814, + Hangul_J_SsangSios = 3815, + Hangul_J_Ieung = 3816, + Hangul_J_Jieuj = 3817, + Hangul_J_Cieuc = 3818, + Hangul_J_Khieuq = 3819, + Hangul_J_Tieut = 3820, + Hangul_J_Phieuf = 3821, + Hangul_J_Hieuh = 3822, + Hangul_RieulYeorinHieuh = 3823, + Hangul_SunkyeongeumMieum = 3824, + Hangul_SunkyeongeumPieub = 3825, + Hangul_PanSios = 3826, + Hangul_KkogjiDalrinIeung = 3827, + Hangul_SunkyeongeumPhieuf = 3828, + Hangul_YeorinHieuh = 3829, + Hangul_AraeA = 3830, + Hangul_AraeAE = 3831, + Hangul_J_PanSios = 3832, + Hangul_J_KkogjiDalrinIeung = 3833, + Hangul_J_YeorinHieuh = 3834, + Korean_Won = 3839, + OE = 5052, + oe = 5053, + Ydiaeresis = 5054, + EcuSign = 8352, + ColonSign = 8353, + CruzeiroSign = 8354, + FFrancSign = 8355, + LiraSign = 8356, + MillSign = 8357, + NairaSign = 8358, + PesetaSign = 8359, + RupeeSign = 8360, + WonSign = 8361, + NewSheqelSign = 8362, + DongSign = 8363, + EuroSign = 8364, + Key_3270_Duplicate = 64769, + Key_3270_FieldMark = 64770, + Key_3270_Right2 = 64771, + Key_3270_Left2 = 64772, + Key_3270_BackTab = 64773, + Key_3270_EraseEOF = 64774, + Key_3270_EraseInput = 64775, + Key_3270_Reset = 64776, + Key_3270_Quit = 64777, + Key_3270_PA1 = 64778, + Key_3270_PA2 = 64779, + Key_3270_PA3 = 64780, + Key_3270_Test = 64781, + Key_3270_Attn = 64782, + Key_3270_CursorBlink = 64783, + Key_3270_AltCursor = 64784, + Key_3270_KeyClick = 64785, + Key_3270_Jump = 64786, + Key_3270_Ident = 64787, + Key_3270_Rule = 64788, + Key_3270_Copy = 64789, + Key_3270_Play = 64790, + Key_3270_Setup = 64791, + Key_3270_Record = 64792, + Key_3270_ChangeScreen = 64793, + Key_3270_DeleteWord = 64794, + Key_3270_ExSelect = 64795, + Key_3270_CursorSelect = 64796, + Key_3270_PrintScreen = 64797, + Key_3270_Enter = 64798, + ISO_Lock = 65025, + ISO_Level2_Latch = 65026, + ISO_Level3_Shift = 65027, + ISO_Level3_Latch = 65028, + ISO_Level3_Lock = 65029, + ISO_Group_Latch = 65030, + ISO_Group_Lock = 65031, + ISO_Next_Group = 65032, + ISO_Next_Group_Lock = 65033, + ISO_Prev_Group = 65034, + ISO_Prev_Group_Lock = 65035, + ISO_First_Group = 65036, + ISO_First_Group_Lock = 65037, + ISO_Last_Group = 65038, + ISO_Last_Group_Lock = 65039, + ISO_Left_Tab = 65056, + ISO_Move_Line_Up = 65057, + ISO_Move_Line_Down = 65058, + ISO_Partial_Line_Up = 65059, + ISO_Partial_Line_Down = 65060, + ISO_Partial_Space_Left = 65061, + ISO_Partial_Space_Right = 65062, + ISO_Set_Margin_Left = 65063, + ISO_Set_Margin_Right = 65064, + ISO_Release_Margin_Left = 65065, + ISO_Release_Margin_Right = 65066, + ISO_Release_Both_Margins = 65067, + ISO_Fast_Cursor_Left = 65068, + ISO_Fast_Cursor_Right = 65069, + ISO_Fast_Cursor_Up = 65070, + ISO_Fast_Cursor_Down = 65071, + ISO_Continuous_Underline = 65072, + ISO_Discontinuous_Underline = 65073, + ISO_Emphasize = 65074, + ISO_Center_Object = 65075, + ISO_Enter = 65076, + dead_grave = 65104, + dead_acute = 65105, + dead_circumflex = 65106, + dead_tilde = 65107, + dead_macron = 65108, + dead_breve = 65109, + dead_abovedot = 65110, + dead_diaeresis = 65111, + dead_abovering = 65112, + dead_doubleacute = 65113, + dead_caron = 65114, + dead_cedilla = 65115, + dead_ogonek = 65116, + dead_iota = 65117, + dead_voiced_sound = 65118, + dead_semivoiced_sound = 65119, + dead_belowdot = 65120, + AccessX_Enable = 65136, + AccessX_Feedback_Enable = 65137, + RepeatKeys_Enable = 65138, + SlowKeys_Enable = 65139, + BounceKeys_Enable = 65140, + StickyKeys_Enable = 65141, + MouseKeys_Enable = 65142, + MouseKeys_Accel_Enable = 65143, + Overlay1_Enable = 65144, + Overlay2_Enable = 65145, + AudibleBell_Enable = 65146, + First_Virtual_Screen = 65232, + Prev_Virtual_Screen = 65233, + Next_Virtual_Screen = 65234, + Last_Virtual_Screen = 65236, + Terminate_Server = 65237, + Pointer_Left = 65248, + Pointer_Right = 65249, + Pointer_Up = 65250, + Pointer_Down = 65251, + Pointer_UpLeft = 65252, + Pointer_UpRight = 65253, + Pointer_DownLeft = 65254, + Pointer_DownRight = 65255, + Pointer_Button_Dflt = 65256, + Pointer_Button1 = 65257, + Pointer_Button2 = 65258, + Pointer_Button3 = 65259, + Pointer_Button4 = 65260, + Pointer_Button5 = 65261, + Pointer_DblClick_Dflt = 65262, + Pointer_DblClick1 = 65263, + Pointer_DblClick2 = 65264, + Pointer_DblClick3 = 65265, + Pointer_DblClick4 = 65266, + Pointer_DblClick5 = 65267, + Pointer_Drag_Dflt = 65268, + Pointer_Drag1 = 65269, + Pointer_Drag2 = 65270, + Pointer_Drag3 = 65271, + Pointer_Drag4 = 65272, + Pointer_EnableKeys = 65273, + Pointer_Accelerate = 65274, + Pointer_DfltBtnNext = 65275, + Pointer_DfltBtnPrev = 65276, + Pointer_Drag5 = 65277, + BackSpace = 65288, + Tab = 65289, + Linefeed = 65290, + Clear = 65291, + Return = 65293, + Pause = 65299, + Scroll_Lock = 65300, + Sys_Req = 65301, + Escape = 65307, + Multi_key = 65312, + Kanji = 65313, + Muhenkan = 65314, + Henkan = 65315, + Henkan_Mode = 65315, + Romaji = 65316, + Hiragana = 65317, + Katakana = 65318, + Hiragana_Katakana = 65319, + Zenkaku = 65320, + Hankaku = 65321, + Zenkaku_Hankaku = 65322, + Touroku = 65323, + Massyo = 65324, + Kana_Lock = 65325, + Kana_Shift = 65326, + Eisu_Shift = 65327, + Eisu_toggle = 65328, + Hangul = 65329, + Hangul_Start = 65330, + Hangul_End = 65331, + Hangul_Hanja = 65332, + Hangul_Jamo = 65333, + Hangul_Romaja = 65334, + Codeinput = 65335, + Hangul_Codeinput = 65335, + Kanji_Bangou = 65335, + Hangul_Jeonja = 65336, + Hangul_Banja = 65337, + Hangul_PreHanja = 65338, + Hangul_PostHanja = 65339, + Hangul_SingleCandidate = 65340, + SingleCandidate = 65340, + Hangul_MultipleCandidate = 65341, + MultipleCandidate = 65341, + Zen_Koho = 65341, + Hangul_PreviousCandidate = 65342, + Mae_Koho = 65342, + PreviousCandidate = 65342, + Hangul_Special = 65343, + Home = 65360, + Left = 65361, + Up = 65362, + Right = 65363, + Down = 65364, + Page_Up = 65365, + Prior = 65365, + Next = 65366, + Page_Down = 65366, + End = 65367, + Begin = 65368, + Select = 65376, + Print = 65377, + Execute = 65378, + Insert = 65379, + Undo = 65381, + Redo = 65382, + Menu = 65383, + Find = 65384, + Cancel = 65385, + Help = 65386, + Break = 65387, + Arabic_switch = 65406, + Greek_switch = 65406, + Hangul_switch = 65406, + Hebrew_switch = 65406, + ISO_Group_Shift = 65406, + Mode_switch = 65406, + kana_switch = 65406, + script_switch = 65406, + Num_Lock = 65407, + KP_Space = 65408, + KP_Tab = 65417, + KP_Enter = 65421, + KP_F1 = 65425, + KP_F2 = 65426, + KP_F3 = 65427, + KP_F4 = 65428, + KP_Home = 65429, + KP_Left = 65430, + KP_Up = 65431, + KP_Right = 65432, + KP_Down = 65433, + KP_Page_Up = 65434, + KP_Prior = 65434, + KP_Next = 65435, + KP_Page_Down = 65435, + KP_End = 65436, + KP_Begin = 65437, + KP_Insert = 65438, + KP_Delete = 65439, + KP_Multiply = 65450, + KP_Add = 65451, + KP_Separator = 65452, + KP_Subtract = 65453, + KP_Decimal = 65454, + KP_Divide = 65455, + KP_0 = 65456, + KP_1 = 65457, + KP_2 = 65458, + KP_3 = 65459, + KP_4 = 65460, + KP_5 = 65461, + KP_6 = 65462, + KP_7 = 65463, + KP_8 = 65464, + KP_9 = 65465, + KP_Equal = 65469, + F1 = 65470, + F2 = 65471, + F3 = 65472, + F4 = 65473, + F5 = 65474, + F6 = 65475, + F7 = 65476, + F8 = 65477, + F9 = 65478, + F10 = 65479, + F11 = 65480, + L1 = 65480, + F12 = 65481, + L2 = 65481, + F13 = 65482, + L3 = 65482, + F14 = 65483, + L4 = 65483, + F15 = 65484, + L5 = 65484, + F16 = 65485, + L6 = 65485, + F17 = 65486, + L7 = 65486, + F18 = 65487, + L8 = 65487, + F19 = 65488, + L9 = 65488, + F20 = 65489, + L10 = 65489, + F21 = 65490, + R1 = 65490, + F22 = 65491, + R2 = 65491, + F23 = 65492, + R3 = 65492, + F24 = 65493, + R4 = 65493, + F25 = 65494, + R5 = 65494, + F26 = 65495, + R6 = 65495, + F27 = 65496, + R7 = 65496, + F28 = 65497, + R8 = 65497, + F29 = 65498, + R9 = 65498, + F30 = 65499, + R10 = 65499, + F31 = 65500, + R11 = 65500, + F32 = 65501, + R12 = 65501, + F33 = 65502, + R13 = 65502, + F34 = 65503, + R14 = 65503, + F35 = 65504, + R15 = 65504, + Shift_L = 65505, + Shift_R = 65506, + Control_L = 65507, + Control_R = 65508, + Caps_Lock = 65509, + Shift_Lock = 65510, + Meta_L = 65511, + Meta_R = 65512, + Alt_L = 65513, + Alt_R = 65514, + Super_L = 65515, + Super_R = 65516, + Hyper_L = 65517, + Hyper_R = 65518, + Delete = 65535, + VoidSymbol = 16777215, + } +} diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs index aa3e73f7c4..0508dcadc1 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; +using Avalonia.Controls; using gint8 = System.Byte; using gint16 = System.Int16; using gint32 = System.Int32; @@ -110,8 +111,33 @@ namespace Avalonia.Gtk3.Interop [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.Gtk)] + public delegate IntPtr gtk_im_multicontext_new(); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate IntPtr gtk_im_context_set_client_window(IntPtr context, IntPtr window); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate bool gtk_im_context_filter_keypress(IntPtr context, IntPtr ev); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate void gtk_widget_activate(IntPtr widget); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] + public delegate IntPtr gdk_screen_get_root_window(IntPtr screen); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] + public delegate IntPtr gdk_window_get_pointer(IntPtr raw, out int x, out int y, out int mask); + [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.Gdk)] + public delegate void gdk_window_begin_move_drag(IntPtr window, gint button, gint root_x, gint root_y, guint32 timestamp); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] + public delegate void gdk_window_begin_resize_drag(IntPtr window, WindowEdge edge, gint button, gint root_x, gint root_y, guint32 timestamp); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)] public delegate ulong g_signal_connect_object(IntPtr instance, Utf8Buffer signal, IntPtr handler, IntPtr userData, int flags); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)] @@ -121,9 +147,14 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate bool signal_widget_draw(IntPtr gtkWidget, IntPtr cairoContext, IntPtr userData); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate bool signal_generic(IntPtr gtkWidget, IntPtr userData); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate bool signal_onevent(IntPtr gtkWidget, IntPtr ev, IntPtr userData); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate bool signal_commit(IntPtr gtkWidget, IntPtr utf8string, IntPtr userData); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate bool timeout_callback(IntPtr data); @@ -153,9 +184,20 @@ namespace Avalonia.Gtk3.Interop 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.gtk_widget_activate GtkWidgetActivate; + + + public static D.gtk_im_multicontext_new GtkImMulticontextNew; + public static D.gtk_im_context_filter_keypress GtkImContextFilterKeypress; + public static D.gtk_im_context_set_client_window GtkImContextSetClientWindow; + public static D.gdk_screen_get_height GdkScreenGetHeight; public static D.gdk_screen_get_width GdkScreenGetWidth; + public static D.gdk_screen_get_root_window GdkScreenGetRootWindow; public static D.gdk_window_get_origin GdkWindowGetOrigin; + public static D.gdk_window_get_pointer GdkWindowGetPointer; + public static D.gdk_window_begin_move_drag GdkWindowBeginMoveDrag; + public static D.gdk_window_begin_resize_drag GdkWindowBeginResizeDrag; public static D.cairo_image_surface_create CairoImageSurfaceCreate; public static D.cairo_image_surface_get_data CairoImageSurfaceGetData; @@ -311,4 +353,43 @@ namespace Avalonia.Gtk3.Interop public gdouble delta_x; public gdouble delta_y; } + + [StructLayout(LayoutKind.Sequential)] + unsafe struct GdkEventWindowState + { + public GdkEventType type; + public IntPtr window; + gint8 send_event; + public GdkWindowState changed_mask; + public GdkWindowState new_window_state; + } + + unsafe struct GdkEventKey + { + public GdkEventType type; + public IntPtr window; + public gint8 send_event; + public guint32 time; + public guint state; + public guint keyval; + public gint length; + public IntPtr pstring; + public guint16 hardware_keycode; + public byte group; + public guint is_modifier; + } + + [Flags] + public enum GdkWindowState + { + Withdrawn = 1, + Iconified = 2, + Maximized = 4, + Sticky = 8, + Fullscreen = 16, + Above = 32, + Below = 64, + Focused = 128, + Ttiled = 256 + } } diff --git a/src/Gtk/Avalonia.Gtk3/README.md b/src/Gtk/Avalonia.Gtk3/README.md index 7f117d24b3..ea853bde75 100644 --- a/src/Gtk/Avalonia.Gtk3/README.md +++ b/src/Gtk/Avalonia.Gtk3/README.md @@ -3,7 +3,6 @@ P/Invoke based GTK3 backend Code is EXPERIMENTAL at this point. It also needs Direct2D/Skia for rendering. -Windows GTK3 binaries aren't included in the repo, you need to download them from http://www.tarnyko.net/repo/gtk3_build_system/gtk+-bundle_3.4.2-20130513_win32.zip -Then you need to extract them somewhere and add `bin` directory to PATH. Support for specifying exact path to binaries will be implemented later. +Windows GTK3 binaries aren't included in the repo, you need to download them from https://sourceforge.net/projects/gtk3win/ On Linux it should work out of the box with system-provided GTK3. On OSX you should be able to wire GTK3 using DYLD_LIBRARY_PATH environment variable. \ No newline at end of file diff --git a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs index 4d5509f4f7..aa0044328e 100644 --- a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Text; using Avalonia.Controls; using Avalonia.Gtk3.Interop; using Avalonia.Input; @@ -13,26 +15,33 @@ namespace Avalonia.Gtk3 { protected readonly IntPtr GtkWidget; private IInputRoot _inputRoot; + private readonly IntPtr _imContext; private readonly FramebufferManager _framebuffer; - protected readonly List _disposables = new List(); + protected readonly List Disposables = new List(); + private Size _lastSize; + private Point _lastPosition; + private uint _lastKbdEvent; public TopLevelImpl(IntPtr gtkWidget) { GtkWidget = gtkWidget; _framebuffer = new FramebufferManager(this); + _imContext = Native.GtkImMulticontextNew(); Native.GtkWidgetSetEvents(gtkWidget, uint.MaxValue); - Native.GtkWidgetRealize(gtkWidget); + Disposables.Add(Signal.Connect(_imContext, "commit", OnCommit)); Connect("draw", OnDraw); - Connect("configure-event", OnConfigured); - Connect("button-press-event", OnButton); - Connect("button-release-event", OnButton); - Connect("motion-notify-event", OnMotion); - Connect("scroll-event", OnScroll); + Connect("realize", OnRealized); + ConnectEvent("configure-event", OnConfigured); + ConnectEvent("button-press-event", OnButton); + ConnectEvent("button-release-event", OnButton); + ConnectEvent("motion-notify-event", OnMotion); + ConnectEvent("scroll-event", OnScroll); + ConnectEvent("window-state-event", OnStateChanged); + ConnectEvent("key-press-event", OnKeyEvent); + ConnectEvent("key-release-event", OnKeyEvent); + Native.GtkWidgetRealize(gtkWidget); } - private Size _lastSize; - private Point _lastPosition; - private bool OnConfigured(IntPtr gtkwidget, IntPtr ev, IntPtr userdata) { var size = ClientSize; @@ -51,6 +60,11 @@ namespace Avalonia.Gtk3 return false; } + private bool OnRealized(IntPtr gtkwidget, IntPtr userdata) + { + Native.GtkImContextSetClientWindow(_imContext, Native.GtkWidgetGetWindow(GtkWidget)); + return false; + } private static InputModifiers GetModifierKeys(GdkModifierType state) { @@ -89,6 +103,19 @@ namespace Avalonia.Gtk3 return false; } + private unsafe bool OnStateChanged(IntPtr w, IntPtr pev, IntPtr userData) + { + var ev = (GdkEventWindowState*) pev; + if (ev->changed_mask.HasFlag(GdkWindowState.Focused)) + { + if(ev->new_window_state.HasFlag(GdkWindowState.Focused)) + Activated?.Invoke(); + else + Deactivated?.Invoke(); + } + return true; + } + private unsafe bool OnMotion(IntPtr w, IntPtr ev, IntPtr userdata) { var evnt = (GdkEventMotion*)ev; @@ -108,7 +135,7 @@ namespace Avalonia.Gtk3 { var evnt = (GdkEventScroll*)ev; var delta = new Vector(); - var step = (double) 1; + const double step = (double) 1; if (evnt->direction == GdkScrollDirection.Down) delta = new Vector(0, -step); else if (evnt->direction == GdkScrollDirection.Up) @@ -126,7 +153,36 @@ namespace Avalonia.Gtk3 return false; } - void Connect(string name, T handler) => _disposables.Add(Signal.Connect(GtkWidget, name, handler)); + private unsafe bool OnKeyEvent(IntPtr w, IntPtr pev, IntPtr userData) + { + var evnt = (GdkEventKey*) pev; + _lastKbdEvent = evnt->time; + if (Native.GtkImContextFilterKeypress(_imContext, pev)) + return true; + var e = new RawKeyEventArgs( + Gtk3Platform.Keyboard, + evnt->time, + evnt->type == GdkEventType.KeyPress ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp, + Avalonia.Gtk.Common.KeyTransform.ConvertKey((GdkKey)evnt->keyval), GetModifierKeys((GdkModifierType)evnt->state)); + Input(e); + return true; + } + + private unsafe bool OnCommit(IntPtr gtkwidget, IntPtr utf8string, IntPtr userdata) + { + var pstr = (byte*)utf8string; + int len; + for (len = 0; pstr[len] != 0; len++) ; + var bytes = new byte[len]; + Marshal.Copy(utf8string, bytes, 0, len); + + Input(new RawTextInputEventArgs(Gtk3Platform.Keyboard, _lastKbdEvent, Encoding.UTF8.GetString(bytes, 0, len))); + return true; + } + + void ConnectEvent(string name, Native.D.signal_onevent handler) + => Disposables.Add(Signal.Connect(GtkWidget, name, handler)); + void Connect(string name, T handler) => Disposables.Add(Signal.Connect(GtkWidget, name, handler)); internal IntPtr CurrentCairoContext { get; private set; } @@ -140,9 +196,9 @@ namespace Avalonia.Gtk3 public void Dispose() { - foreach(var d in _disposables) + foreach(var d in Disposables) d.Dispose(); - _disposables.Clear(); + Disposables.Clear(); //TODO } @@ -161,19 +217,16 @@ namespace Avalonia.Gtk3 string IPlatformHandle.HandleDescriptor => "HWND"; - public Action Activated { get; set; } //TODO + public Action Activated { get; set; } public Action Closed { get; set; } //TODO - public Action Deactivated { get; set; } //TODO - public Action Input { get; set; } //TODO + public Action Deactivated { get; set; } + public Action Input { get; set; } public Action Paint { get; set; } public Action Resized { get; set; } public Action ScalingChanged { get; set; } //TODO public Action PositionChanged { get; set; } - public void Activate() - { - throw new NotImplementedException(); - } + public void Activate() => Native.GtkWidgetActivate(GtkWidget); public void Invalidate(Rect rect) { @@ -206,14 +259,25 @@ namespace Avalonia.Gtk3 public void Hide() => Native.GtkWidgetHide(GtkWidget); + void GetGlobalPointer(out int x, out int y) + { + int mask; + Native.GdkWindowGetPointer(Native.GdkScreenGetRootWindow(Native.GtkWidgetGetScreen(GtkWidget)), + out x, out y, out mask); + } + public void BeginMoveDrag() { - //STUB + int x, y; + GetGlobalPointer(out x, out y); + Native.GdkWindowBeginMoveDrag(Native.GtkWidgetGetWindow(GtkWidget), 1, x, y, 0); } public void BeginResizeDrag(WindowEdge edge) { - //STUB + int x, y; + GetGlobalPointer(out x, out y); + Native.GdkWindowBeginResizeDrag(Native.GtkWidgetGetWindow(GtkWidget), edge, 1, x, y, 0); } From 88e2dac3bf9f5b49ea26468b3569ad87ef5f3977 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 31 Jan 2017 04:05:29 +0300 Subject: [PATCH 10/24] Added popup impl --- src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj | 1 + src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs | 5 +-- src/Gtk/Avalonia.Gtk3/Interop/Native.cs | 42 +++++++++++++++++++++- src/Gtk/Avalonia.Gtk3/PopupImpl.cs | 42 ++++++++++++++++++++++ src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs | 10 ++++-- 5 files changed, 93 insertions(+), 7 deletions(-) create mode 100644 src/Gtk/Avalonia.Gtk3/PopupImpl.cs diff --git a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj index dd35532830..e0e773aaa6 100644 --- a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj +++ b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj @@ -55,6 +55,7 @@ + diff --git a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs index 2aee41e617..81aac5e2dc 100644 --- a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs +++ b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs @@ -49,10 +49,7 @@ namespace Avalonia.Gtk3 throw new NotImplementedException(); } - public IPopupImpl CreatePopup() - { - throw new NotImplementedException(); - } + public IPopupImpl CreatePopup() => new PopupImpl(); diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs index 0508dcadc1..736ec7e191 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs @@ -59,7 +59,8 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] public delegate int gdk_window_get_origin(IntPtr gdkWindow, out int x, out int y); - + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] + public delegate void gdk_window_resize(IntPtr gtkWindow, int width, int height); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] @@ -77,6 +78,10 @@ namespace Avalonia.Gtk3.Interop [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_set_size_request(IntPtr gtkWindow, int width, int height); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate void gtk_window_set_default_size(IntPtr gtkWindow, int width, int height); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_window_get_position(IntPtr gtkWindow, out int x, out int y); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_window_move(IntPtr gtkWindow, int x, int y); @@ -128,6 +133,8 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] public delegate IntPtr gdk_window_get_pointer(IntPtr raw, out int x, out int y, out int mask); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] + public delegate void gtk_window_set_geometry_hints(IntPtr window, IntPtr geometry_widget, ref GdkGeometry geometry, GdkWindowHints geom_mask); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] public delegate void gdk_window_invalidate_rect(IntPtr window, ref GdkRectangle rect, bool invalidate_children); @@ -175,6 +182,9 @@ namespace Avalonia.Gtk3.Interop 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.gdk_window_resize GdkWindowResize; + public static D.gtk_widget_set_size_request GtkWindowSetSizeRequest; + public static D.gtk_window_set_default_size GtkWindowSetDefaultSize; public static D.gtk_window_get_position GtkWindowGetPosition; public static D.gtk_window_move GtkWindowMove; public static D.g_signal_connect_object GSignalConnectObject; @@ -364,6 +374,7 @@ namespace Avalonia.Gtk3.Interop public GdkWindowState new_window_state; } + [StructLayout(LayoutKind.Sequential)] unsafe struct GdkEventKey { public GdkEventType type; @@ -392,4 +403,33 @@ namespace Avalonia.Gtk3.Interop Focused = 128, Ttiled = 256 } + + [StructLayout(LayoutKind.Sequential)] + struct GdkGeometry + { + gint min_width; + gint min_height; + gint max_width; + gint max_height; + gint base_width; + gint base_height; + gint width_inc; + gint height_inc; + gdouble min_aspect; + gdouble max_aspect; + gint win_gravity; + } + + enum GdkWindowHints + { + GDK_HINT_POS = 1 << 0, + GDK_HINT_MIN_SIZE = 1 << 1, + GDK_HINT_MAX_SIZE = 1 << 2, + GDK_HINT_BASE_SIZE = 1 << 3, + GDK_HINT_ASPECT = 1 << 4, + GDK_HINT_RESIZE_INC = 1 << 5, + GDK_HINT_WIN_GRAVITY = 1 << 6, + GDK_HINT_USER_POS = 1 << 7, + GDK_HINT_USER_SIZE = 1 << 8 + } } diff --git a/src/Gtk/Avalonia.Gtk3/PopupImpl.cs b/src/Gtk/Avalonia.Gtk3/PopupImpl.cs new file mode 100644 index 0000000000..4cc01c625f --- /dev/null +++ b/src/Gtk/Avalonia.Gtk3/PopupImpl.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Avalonia.Gtk3.Interop; +using Avalonia.Platform; + +namespace Avalonia.Gtk3 +{ + class PopupImpl : TopLevelImpl, IPopupImpl + { + static IntPtr CreateWindow() + { + var window = Native.GtkWindowNew(GtkWindowType.Popup); + Native.GtkWindowSetSizeRequest(window, 1, 1); + Native.GtkWindowSetDefaultSize(window, 200, 200); + + return window; + } + + public PopupImpl() : base(CreateWindow()) + { + + + } + + public override Size ClientSize + { + get + { + return base.ClientSize; + } + set + { + Native.GtkWindowSetDefaultSize(GtkWidget, (int)value.Width, (int)value.Height); + base.ClientSize = value; + var size = ClientSize; + } + } + } +} diff --git a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs index aa0044328e..989a0584fa 100644 --- a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs @@ -281,7 +281,7 @@ namespace Avalonia.Gtk3 } - public Size ClientSize + public virtual Size ClientSize { get { @@ -289,7 +289,13 @@ namespace Avalonia.Gtk3 Native.GtkWindowGetSize(GtkWidget, out w, out h); return new Size(w, h); } - set { Native.GtkWindowResize(GtkWidget, (int)value.Width, (int)value.Height); } + set + { + Native.GtkWindowResize(GtkWidget, (int)value.Width, (int)value.Height); + if (Native.GtkWidgetGetWindow(GtkWidget) == IntPtr.Zero) + Native.GtkWidgetRealize(GtkWidget); + Native.GdkWindowResize(Native.GtkWidgetGetWindow(GtkWidget), (int)value.Width, (int)value.Height); + } } public Point Position From cc60c6e5dd382920f0dcdd82d47241e939fff040 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 31 Jan 2017 04:41:33 +0300 Subject: [PATCH 11/24] Implemented clipboard --- src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj | 1 + src/Gtk/Avalonia.Gtk3/ClipboardImpl.cs | 53 +++++++++++++++++++++ src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs | 2 +- src/Gtk/Avalonia.Gtk3/Interop/Native.cs | 23 +++++++++ src/Gtk/Avalonia.Gtk3/Interop/Utf8Buffer.cs | 13 +++++ src/Gtk/Avalonia.Gtk3/Stubs.cs | 9 ---- src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs | 8 +--- 7 files changed, 92 insertions(+), 17 deletions(-) create mode 100644 src/Gtk/Avalonia.Gtk3/ClipboardImpl.cs diff --git a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj index e0e773aaa6..910b0f0d52 100644 --- a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj +++ b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj @@ -44,6 +44,7 @@ KeyTransform.cs + diff --git a/src/Gtk/Avalonia.Gtk3/ClipboardImpl.cs b/src/Gtk/Avalonia.Gtk3/ClipboardImpl.cs new file mode 100644 index 0000000000..333391a064 --- /dev/null +++ b/src/Gtk/Avalonia.Gtk3/ClipboardImpl.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Avalonia.Gtk3.Interop; +using Avalonia.Input.Platform; + +namespace Avalonia.Gtk3 +{ + class ClipboardImpl : IClipboard + { + + IntPtr GetClipboard() => Native.GtkClipboardGetForDisplay(Native.GdkGetDefaultDisplay(), IntPtr.Zero); + + static void OnText(IntPtr clipboard, IntPtr utf8string, IntPtr userdata) + { + var handle = GCHandle.FromIntPtr(userdata); + + ((TaskCompletionSource) handle.Target) + .TrySetResult(Utf8Buffer.StringFromPtr(utf8string)); + handle.Free(); + } + + private static readonly Native.D.GtkClipboardTextReceivedFunc OnTextDelegate = OnText; + + static ClipboardImpl() + { + GCHandle.Alloc(OnTextDelegate); + } + + public Task GetTextAsync() + { + var tcs = new TaskCompletionSource(); + Native.GtkClipboardRequestText(GetClipboard(), OnTextDelegate, GCHandle.ToIntPtr(GCHandle.Alloc(tcs))); + return tcs.Task; + } + + public Task SetTextAsync(string text) + { + using (var buf = new Utf8Buffer(text)) + Native.GtkClipboardSetText(GetClipboard(), buf, buf.ByteLen); + return Task.FromResult(0); + } + + public Task ClearAsync() + { + Native.GtkClipboardRequestClear(GetClipboard()); + return Task.FromResult(0); + } + } +} diff --git a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs index 81aac5e2dc..296f302556 100644 --- a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs +++ b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs @@ -30,7 +30,7 @@ namespace Avalonia.Gtk3 s_tlsMarker = true; AvaloniaLocator.CurrentMutable.Bind().ToConstant(Instance) - .Bind().ToSingleton() + .Bind().ToSingleton() .Bind().ToConstant(new CursorFactoryStub()) .Bind().ToConstant(Keyboard) .Bind().ToConstant(Mouse) diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs index 736ec7e191..f3c1c847ca 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs @@ -57,6 +57,9 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] public delegate int gdk_screen_get_width(IntPtr screen); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] + public delegate IntPtr gdk_display_get_default(); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] public delegate int gdk_window_get_origin(IntPtr gdkWindow, out int x, out int y); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] @@ -145,6 +148,18 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] public delegate void gdk_window_begin_resize_drag(IntPtr window, WindowEdge edge, gint button, gint root_x, gint root_y, guint32 timestamp); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate IntPtr gtk_clipboard_get_for_display(IntPtr display, IntPtr atom); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate void gtk_clipboard_request_text(IntPtr clipboard, GtkClipboardTextReceivedFunc callback, IntPtr user_data); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate void gtk_clipboard_set_text(IntPtr clipboard, Utf8Buffer text, int len); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate void gtk_clipboard_clear(IntPtr clipboard); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)] public delegate ulong g_signal_connect_object(IntPtr instance, Utf8Buffer signal, IntPtr handler, IntPtr userData, int flags); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)] @@ -165,6 +180,9 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate bool timeout_callback(IntPtr data); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void GtkClipboardTextReceivedFunc(IntPtr clipboard, IntPtr utf8string, IntPtr userdata); } @@ -195,6 +213,10 @@ namespace Avalonia.Gtk3.Interop public static D.gdk_window_invalidate_rect GdkWindowInvalidateRect; public static D.gtk_widget_queue_draw_area GtkWidgetQueueDrawArea; public static D.gtk_widget_activate GtkWidgetActivate; + public static D.gtk_clipboard_get_for_display GtkClipboardGetForDisplay; + public static D.gtk_clipboard_request_text GtkClipboardRequestText; + public static D.gtk_clipboard_set_text GtkClipboardSetText; + public static D.gtk_clipboard_clear GtkClipboardRequestClear; public static D.gtk_im_multicontext_new GtkImMulticontextNew; @@ -202,6 +224,7 @@ namespace Avalonia.Gtk3.Interop public static D.gtk_im_context_set_client_window GtkImContextSetClientWindow; public static D.gdk_screen_get_height GdkScreenGetHeight; + public static D.gdk_display_get_default GdkGetDefaultDisplay; public static D.gdk_screen_get_width GdkScreenGetWidth; public static D.gdk_screen_get_root_window GdkScreenGetRootWindow; public static D.gdk_window_get_origin GdkWindowGetOrigin; diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Utf8Buffer.cs b/src/Gtk/Avalonia.Gtk3/Interop/Utf8Buffer.cs index 7a9868857f..f108c291b8 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Utf8Buffer.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Utf8Buffer.cs @@ -16,6 +16,8 @@ namespace Avalonia.Gtk3.Interop handle = _gchandle.AddrOfPinnedObject(); } + public int ByteLen => _data.Length; + protected override bool ReleaseHandle() { if (handle != IntPtr.Zero) @@ -28,5 +30,16 @@ namespace Avalonia.Gtk3.Interop } public override bool IsInvalid => handle == IntPtr.Zero; + + public static unsafe string StringFromPtr(IntPtr s) + { + var pstr = (byte*)s; + int len; + for (len = 0; pstr[len] != 0; len++) ; + var bytes = new byte[len]; + Marshal.Copy(s, bytes, 0, len); + + return Encoding.UTF8.GetString(bytes, 0, len); + } } } diff --git a/src/Gtk/Avalonia.Gtk3/Stubs.cs b/src/Gtk/Avalonia.Gtk3/Stubs.cs index 76333aba60..89d9251cdd 100644 --- a/src/Gtk/Avalonia.Gtk3/Stubs.cs +++ b/src/Gtk/Avalonia.Gtk3/Stubs.cs @@ -14,15 +14,6 @@ using Avalonia.Platform; namespace Avalonia.Gtk3 { - class ClipboardStub : IClipboard - { - public Task GetTextAsync() => Task.FromResult(""); - - public Task SetTextAsync(string text) => Task.FromResult(0); - - public Task ClearAsync() => Task.FromResult(0); - } - class CursorFactoryStub : IStandardCursorFactory { public IPlatformHandle GetCursor(StandardCursorType cursorType) => new PlatformHandle(IntPtr.Zero, "STUB"); diff --git a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs index 989a0584fa..e532a05915 100644 --- a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs @@ -170,13 +170,7 @@ namespace Avalonia.Gtk3 private unsafe bool OnCommit(IntPtr gtkwidget, IntPtr utf8string, IntPtr userdata) { - var pstr = (byte*)utf8string; - int len; - for (len = 0; pstr[len] != 0; len++) ; - var bytes = new byte[len]; - Marshal.Copy(utf8string, bytes, 0, len); - - Input(new RawTextInputEventArgs(Gtk3Platform.Keyboard, _lastKbdEvent, Encoding.UTF8.GetString(bytes, 0, len))); + Input(new RawTextInputEventArgs(Gtk3Platform.Keyboard, _lastKbdEvent, Utf8Buffer.StringFromPtr(utf8string))); return true; } From dd284882188b5ed7c31fbb5f3cdb03a072dc71d3 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 1 Feb 2017 03:50:57 +0300 Subject: [PATCH 12/24] Implemented icons --- src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj | 4 ++ src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs | 2 +- src/Gtk/Avalonia.Gtk3/Interop/GException.cs | 33 ++++++++++ src/Gtk/Avalonia.Gtk3/Interop/GObject.cs | 37 +++++++++++ src/Gtk/Avalonia.Gtk3/Interop/Native.cs | 30 +++++++++ src/Gtk/Avalonia.Gtk3/Interop/Pixbuf.cs | 68 +++++++++++++++++++++ src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs | 5 +- src/Gtk/Avalonia.Gtk3/PlatformIconLoader.cs | 20 ++++++ src/Gtk/Avalonia.Gtk3/Stubs.cs | 10 --- src/Gtk/Avalonia.Gtk3/WindowImpl.cs | 5 +- 10 files changed, 198 insertions(+), 16 deletions(-) create mode 100644 src/Gtk/Avalonia.Gtk3/Interop/GException.cs create mode 100644 src/Gtk/Avalonia.Gtk3/Interop/GObject.cs create mode 100644 src/Gtk/Avalonia.Gtk3/Interop/Pixbuf.cs create mode 100644 src/Gtk/Avalonia.Gtk3/PlatformIconLoader.cs diff --git a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj index 910b0f0d52..11d9b5d2e1 100644 --- a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj +++ b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj @@ -48,14 +48,18 @@ + + + + diff --git a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs index 296f302556..943d83b57f 100644 --- a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs +++ b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs @@ -38,7 +38,7 @@ namespace Avalonia.Gtk3 .Bind().ToConstant(Instance) .Bind().ToSingleton() .Bind().ToConstant(new DefaultRenderLoop(60)) - .Bind().ToConstant(new PlatformIconLoaderStub()); + .Bind().ToConstant(new PlatformIconLoader()); } diff --git a/src/Gtk/Avalonia.Gtk3/Interop/GException.cs b/src/Gtk/Avalonia.Gtk3/Interop/GException.cs new file mode 100644 index 0000000000..ab08df4ec3 --- /dev/null +++ b/src/Gtk/Avalonia.Gtk3/Interop/GException.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Avalonia.Gtk3.Interop +{ + public class GException : Exception + { + [StructLayout(LayoutKind.Sequential)] + struct GError + { + UInt32 domain; + int code; + public IntPtr message; + }; + + static unsafe string GetError(IntPtr error) + { + if (error == IntPtr.Zero) + return "Unknown error"; + return Utf8Buffer.StringFromPtr(((GError*) error)->message); + } + + public GException(IntPtr error) : base(GetError(error)) + { + + } + + } +} diff --git a/src/Gtk/Avalonia.Gtk3/Interop/GObject.cs b/src/Gtk/Avalonia.Gtk3/Interop/GObject.cs new file mode 100644 index 0000000000..9b5ee758b9 --- /dev/null +++ b/src/Gtk/Avalonia.Gtk3/Interop/GObject.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace Avalonia.Gtk3.Interop +{ + class GObject : SafeHandle + { + public GObject() : base(IntPtr.Zero, true) + { + } + + public GObject(IntPtr handle, bool owned = true) : base(IntPtr.Zero, owned) + { + this.handle = handle; + } + + protected override bool ReleaseHandle() + { + if (handle != IntPtr.Zero) + Native.GObjectUnref(handle); + handle = IntPtr.Zero; + return true; + } + + public override bool IsInvalid => handle == IntPtr.Zero; + } + + class GInputStream : GObject + { + + } +} + diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs index f3c1c847ca..b325b46285 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs @@ -38,6 +38,9 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_widget_hide(IntPtr gtkWidget); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate void gtk_window_set_icon(IntPtr window, Pixbuf pixbuf); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] //No manual import public delegate IntPtr gdk_get_native_handle(IntPtr gdkWindow); @@ -160,12 +163,31 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_clipboard_clear(IntPtr clipboard); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.GdkPixBuf)] + public delegate IntPtr gdk_pixbuf_new_from_file(Utf8Buffer filename, out IntPtr error); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.GdkPixBuf)] + public delegate IntPtr gdk_pixbuf_new_from_stream(GInputStream stream, IntPtr cancel, out IntPtr error); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.GdkPixBuf)] + public delegate bool gdk_pixbuf_save_to_bufferv(Pixbuf pixbuf, out IntPtr buffer, out IntPtr buffer_size, + Utf8Buffer type, IntPtr option_keys, IntPtr option_values, out IntPtr error); + + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)] + public delegate void g_object_unref(IntPtr instance); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)] public delegate ulong g_signal_connect_object(IntPtr instance, Utf8Buffer 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), GtkImport(GtkDll.Glib)] + public delegate ulong g_free(IntPtr data); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gio)] + public delegate GInputStream g_memory_input_stream_new_from_data(IntPtr ptr, IntPtr len, IntPtr destroyCallback); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate bool signal_widget_draw(IntPtr gtkWidget, IntPtr cairoContext, IntPtr userData); @@ -191,6 +213,7 @@ namespace Avalonia.Gtk3.Interop public static D.gtk_application_new GtkApplicationNew; public static D.gtk_main_iteration GtkMainIteration; public static D.gtk_window_new GtkWindowNew; + public static D.gtk_window_set_icon GtkWindowSetIcon; public static D.gtk_init GtkInit; public static D.gtk_window_present GtkWindowPresent; public static D.gtk_widget_hide GtkWidgetHide; @@ -205,9 +228,12 @@ namespace Avalonia.Gtk3.Interop public static D.gtk_window_set_default_size GtkWindowSetDefaultSize; public static D.gtk_window_get_position GtkWindowGetPosition; public static D.gtk_window_move GtkWindowMove; + public static D.g_object_unref GObjectUnref; 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.g_free GFree; + public static D.g_memory_input_stream_new_from_data GMemoryInputStreamNewFromData; 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; @@ -232,6 +258,10 @@ namespace Avalonia.Gtk3.Interop public static D.gdk_window_begin_move_drag GdkWindowBeginMoveDrag; public static D.gdk_window_begin_resize_drag GdkWindowBeginResizeDrag; + public static D.gdk_pixbuf_new_from_file GdkPixbufNewFromFile; + public static D.gdk_pixbuf_new_from_stream GdkPixbufNewFromStream; + public static D.gdk_pixbuf_save_to_bufferv GdkPixbufSaveToBufferv; + public static D.cairo_image_surface_create CairoImageSurfaceCreate; public static D.cairo_image_surface_get_data CairoImageSurfaceGetData; public static D.cairo_image_surface_get_stride CairoImageSurfaceGetStride; diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Pixbuf.cs b/src/Gtk/Avalonia.Gtk3/Interop/Pixbuf.cs new file mode 100644 index 0000000000..1a0a772522 --- /dev/null +++ b/src/Gtk/Avalonia.Gtk3/Interop/Pixbuf.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Avalonia.Platform; + +namespace Avalonia.Gtk3.Interop +{ + internal class Pixbuf : GObject, IWindowIconImpl + { + Pixbuf(IntPtr handle) : base(handle) + { + + } + + public static Pixbuf NewFromFile(string filename) + { + using (var ub = new Utf8Buffer(filename)) + { + IntPtr err; + var rv = Native.GdkPixbufNewFromFile(ub, out err); + if(rv != IntPtr.Zero) + return new Pixbuf(rv); + throw new GException(err); + } + } + + public static unsafe Pixbuf NewFromBytes(byte[] data) + { + fixed (void* bytes = data) + { + using (var stream = Native.GMemoryInputStreamNewFromData(new IntPtr(bytes), new IntPtr(data.Length), IntPtr.Zero)) + { + IntPtr err; + var rv = Native.GdkPixbufNewFromStream(stream, IntPtr.Zero, out err); + if (rv != IntPtr.Zero) + return new Pixbuf(rv); + throw new GException(err); + } + } + } + + public static Pixbuf NewFromStream(Stream s) + { + if (s is MemoryStream) + return NewFromBytes(((MemoryStream) s).ToArray()); + var ms = new MemoryStream(); + s.CopyTo(ms); + return NewFromBytes(ms.ToArray()); + } + + public void Save(Stream outputStream) + { + IntPtr buffer, bufferLen, error; + using (var png = new Utf8Buffer("png")) + if (!Native.GdkPixbufSaveToBufferv(this, out buffer, out bufferLen, png, + IntPtr.Zero, IntPtr.Zero, out error)) + throw new GException(error); + var data = new byte[bufferLen.ToInt32()]; + Marshal.Copy(buffer, data, 0, bufferLen.ToInt32()); + Native.GFree(buffer); + outputStream.Write(data, 0, data.Length); + } + } +} diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs b/src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs index a965453841..c6b6326f80 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs @@ -31,7 +31,8 @@ namespace Avalonia.Gtk3.Interop Glib, Gio, Gobject, - Cairo + Cairo, + GdkPixBuf } static class Resolver @@ -76,6 +77,8 @@ namespace Avalonia.Gtk3.Interop return FormatName("gtk-3"); case GtkDll.Gobject: return FormatName("gobject-2.0"); + case GtkDll.GdkPixBuf: + return FormatName("gdk_pixbuf-2.0"); default: throw new ArgumentException("Unknown lib: " + dll); } diff --git a/src/Gtk/Avalonia.Gtk3/PlatformIconLoader.cs b/src/Gtk/Avalonia.Gtk3/PlatformIconLoader.cs new file mode 100644 index 0000000000..b4e293cdff --- /dev/null +++ b/src/Gtk/Avalonia.Gtk3/PlatformIconLoader.cs @@ -0,0 +1,20 @@ +using System.IO; +using Avalonia.Gtk3.Interop; +using Avalonia.Platform; + +namespace Avalonia.Gtk3 +{ + class PlatformIconLoader : IPlatformIconLoader + { + public IWindowIconImpl LoadIcon(string fileName) => Pixbuf.NewFromFile(fileName); + + public IWindowIconImpl LoadIcon(Stream stream) => Pixbuf.NewFromStream(stream); + + public IWindowIconImpl LoadIcon(IBitmapImpl bitmap) + { + var ms = new MemoryStream(); + bitmap.Save(ms); + return Pixbuf.NewFromBytes(ms.ToArray()); + } + } +} \ No newline at end of file diff --git a/src/Gtk/Avalonia.Gtk3/Stubs.cs b/src/Gtk/Avalonia.Gtk3/Stubs.cs index 89d9251cdd..8ebc1e9c59 100644 --- a/src/Gtk/Avalonia.Gtk3/Stubs.cs +++ b/src/Gtk/Avalonia.Gtk3/Stubs.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -26,13 +25,4 @@ namespace Avalonia.Gtk3 public Task ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent) => Task.FromResult((string) null); } - - class PlatformIconLoaderStub : IPlatformIconLoader - { - public IWindowIconImpl LoadIcon(string fileName) => null; - - public IWindowIconImpl LoadIcon(Stream stream) => null; - - public IWindowIconImpl LoadIcon(IBitmapImpl bitmap) => null; - } } diff --git a/src/Gtk/Avalonia.Gtk3/WindowImpl.cs b/src/Gtk/Avalonia.Gtk3/WindowImpl.cs index b8c561c56c..25b0bd8dd3 100644 --- a/src/Gtk/Avalonia.Gtk3/WindowImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/WindowImpl.cs @@ -22,10 +22,7 @@ namespace Avalonia.Gtk3 public void SetSystemDecorations(bool enabled) => Native.GtkWindowSetDecorated(GtkWidget, enabled); - public void SetIcon(IWindowIconImpl icon) - { - //STUB - } + public void SetIcon(IWindowIconImpl icon) => Native.GtkWindowSetIcon(GtkWidget, (Pixbuf) icon); public WindowImpl() : base(Native.GtkWindowNew(GtkWindowType.TopLevel)) { From 2561901fd10c7814d5e685adcd41e2da5a87921e Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 1 Feb 2017 04:32:07 +0300 Subject: [PATCH 13/24] Proper closing/disposing handling --- src/Gtk/Avalonia.Gtk3/Interop/GObject.cs | 15 +++++++ src/Gtk/Avalonia.Gtk3/Interop/Native.cs | 54 +++++++++++++----------- src/Gtk/Avalonia.Gtk3/Interop/Signal.cs | 8 ++-- src/Gtk/Avalonia.Gtk3/PopupImpl.cs | 4 +- src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs | 29 ++++++++++--- src/Gtk/Avalonia.Gtk3/WindowImpl.cs | 13 +++++- 6 files changed, 85 insertions(+), 38 deletions(-) diff --git a/src/Gtk/Avalonia.Gtk3/Interop/GObject.cs b/src/Gtk/Avalonia.Gtk3/Interop/GObject.cs index 9b5ee758b9..9cd9c5cd8b 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/GObject.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/GObject.cs @@ -33,5 +33,20 @@ namespace Avalonia.Gtk3.Interop { } + + class GtkWidget : GObject + { + + } + + class GtkWindow : GtkWidget + { + + } + + class GtkImContext : GObject + { + + } } diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs index b325b46285..4112bf3f91 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs @@ -27,32 +27,35 @@ namespace Avalonia.Gtk3.Interop public delegate void gtk_main_iteration(); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate IntPtr gtk_window_new(GtkWindowType windowType); + public delegate GtkWindow gtk_window_new(GtkWindowType windowType); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate IntPtr gtk_init(int argc, IntPtr argv); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_window_present(IntPtr gtkWindow); + public delegate void gtk_window_present(GtkWindow gtkWindow); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_widget_hide(IntPtr gtkWidget); + public delegate void gtk_widget_hide(GtkWidget gtkWidget); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_window_set_icon(IntPtr window, Pixbuf pixbuf); + public delegate void gtk_window_set_icon(GtkWindow window, Pixbuf pixbuf); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate void gtk_window_set_modal(GtkWindow window, bool modal); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] //No manual import public delegate IntPtr gdk_get_native_handle(IntPtr gdkWindow); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate IntPtr gtk_widget_get_window(IntPtr gtkWidget); + public delegate IntPtr gtk_widget_get_window(GtkWidget gtkWidget); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate IntPtr gtk_widget_get_screen(IntPtr gtkWidget); + public delegate IntPtr gtk_widget_get_screen(GtkWidget gtkWidget); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate IntPtr gtk_widget_set_double_buffered(IntPtr gtkWidget, bool value); + public delegate IntPtr gtk_widget_set_double_buffered(GtkWidget gtkWidget, bool value); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate IntPtr gtk_widget_set_events(IntPtr gtkWidget, uint flags); + public delegate IntPtr gtk_widget_set_events(GtkWidget gtkWidget, uint flags); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] @@ -70,27 +73,27 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_widget_realize(IntPtr gtkWidget); + public delegate void gtk_widget_realize(GtkWidget gtkWidget); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_window_set_title(IntPtr gtkWindow, Utf8Buffer title); + public delegate void gtk_window_set_title(GtkWindow gtkWindow, Utf8Buffer title); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_window_set_decorated(IntPtr gtkWindow, bool decorated); + public delegate void gtk_window_set_decorated(GtkWindow gtkWindow, bool decorated); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_window_get_size(IntPtr gtkWindow, out int width, out int height); + public delegate void gtk_window_get_size(GtkWindow 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); + public delegate void gtk_window_resize(GtkWindow gtkWindow, int width, int height); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_widget_set_size_request(IntPtr gtkWindow, int width, int height); + public delegate void gtk_widget_set_size_request(GtkWidget widget, int width, int height); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_window_set_default_size(IntPtr gtkWindow, int width, int height); + public delegate void gtk_window_set_default_size(GtkWindow gtkWindow, int width, int height); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_window_get_position(IntPtr gtkWindow, out int x, out int y); + public delegate void gtk_window_get_position(GtkWindow gtkWindow, out int x, out int y); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_window_move(IntPtr gtkWindow, int x, int y); + public delegate void gtk_window_move(GtkWindow gtkWindow, int x, int y); @@ -120,19 +123,19 @@ namespace Avalonia.Gtk3.Interop public delegate void cairo_paint(IntPtr context); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_widget_queue_draw_area(IntPtr gtkWindow, int x, int y, int width, int height); + public delegate void gtk_widget_queue_draw_area(GtkWidget widget, int x, int y, int width, int height); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate IntPtr gtk_im_multicontext_new(); + public delegate GtkImContext gtk_im_multicontext_new(); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate IntPtr gtk_im_context_set_client_window(IntPtr context, IntPtr window); + public delegate IntPtr gtk_im_context_set_client_window(GtkImContext context, IntPtr window); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate bool gtk_im_context_filter_keypress(IntPtr context, IntPtr ev); + public delegate bool gtk_im_context_filter_keypress(GtkImContext context, IntPtr ev); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] - public delegate void gtk_widget_activate(IntPtr widget); + public delegate void gtk_widget_activate(GtkWidget widget); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] public delegate IntPtr gdk_screen_get_root_window(IntPtr screen); @@ -140,7 +143,7 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] public delegate IntPtr gdk_window_get_pointer(IntPtr raw, out int x, out int y, out int mask); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] - public delegate void gtk_window_set_geometry_hints(IntPtr window, IntPtr geometry_widget, ref GdkGeometry geometry, GdkWindowHints geom_mask); + public delegate void gtk_window_set_geometry_hints(GtkWindow window, IntPtr geometry_widget, ref GdkGeometry geometry, GdkWindowHints geom_mask); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] public delegate void gdk_window_invalidate_rect(IntPtr window, ref GdkRectangle rect, bool invalidate_children); @@ -178,9 +181,9 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)] public delegate void g_object_unref(IntPtr instance); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)] - public delegate ulong g_signal_connect_object(IntPtr instance, Utf8Buffer signal, IntPtr handler, IntPtr userData, int flags); + public delegate ulong g_signal_connect_object(GObject instance, Utf8Buffer signal, IntPtr handler, IntPtr userData, int flags); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)] - public delegate ulong g_signal_handler_disconnect(IntPtr instance, ulong connectionId); + public delegate ulong g_signal_handler_disconnect(GObject 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), GtkImport(GtkDll.Glib)] @@ -214,6 +217,7 @@ namespace Avalonia.Gtk3.Interop public static D.gtk_main_iteration GtkMainIteration; public static D.gtk_window_new GtkWindowNew; public static D.gtk_window_set_icon GtkWindowSetIcon; + public static D.gtk_window_set_modal GtkWindowSetModal; public static D.gtk_init GtkInit; public static D.gtk_window_present GtkWindowPresent; public static D.gtk_widget_hide GtkWidgetHide; diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Signal.cs b/src/Gtk/Avalonia.Gtk3/Interop/Signal.cs index b2ccdcf408..abbcab9e21 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Signal.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Signal.cs @@ -7,15 +7,15 @@ using System.Threading.Tasks; namespace Avalonia.Gtk3.Interop { - public class Signal + class Signal { class ConnectedSignal : IDisposable { - private readonly IntPtr _instance; + private readonly GObject _instance; private GCHandle _handle; private readonly ulong _id; - public ConnectedSignal(IntPtr instance, GCHandle handle, ulong id) + public ConnectedSignal(GObject instance, GCHandle handle, ulong id) { _instance = instance; _handle = handle; @@ -32,7 +32,7 @@ namespace Avalonia.Gtk3.Interop } } - public static IDisposable Connect(IntPtr obj, string name, T handler) + public static IDisposable Connect(GObject obj, string name, T handler) { var handle = GCHandle.Alloc(handler); var ptr = Marshal.GetFunctionPointerForDelegate((Delegate)(object)handler); diff --git a/src/Gtk/Avalonia.Gtk3/PopupImpl.cs b/src/Gtk/Avalonia.Gtk3/PopupImpl.cs index 4cc01c625f..3f76960e8f 100644 --- a/src/Gtk/Avalonia.Gtk3/PopupImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/PopupImpl.cs @@ -10,7 +10,7 @@ namespace Avalonia.Gtk3 { class PopupImpl : TopLevelImpl, IPopupImpl { - static IntPtr CreateWindow() + static GtkWindow CreateWindow() { var window = Native.GtkWindowNew(GtkWindowType.Popup); Native.GtkWindowSetSizeRequest(window, 1, 1); @@ -33,6 +33,8 @@ namespace Avalonia.Gtk3 } set { + if(GtkWidget.IsClosed) + return; Native.GtkWindowSetDefaultSize(GtkWidget, (int)value.Width, (int)value.Height); base.ClientSize = value; var size = ClientSize; diff --git a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs index e532a05915..0a8ae49906 100644 --- a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using System.Runtime.InteropServices; using System.Text; using Avalonia.Controls; @@ -13,20 +14,23 @@ namespace Avalonia.Gtk3 { abstract class TopLevelImpl : ITopLevelImpl, IPlatformHandle { - protected readonly IntPtr GtkWidget; + protected readonly GtkWindow GtkWidget; private IInputRoot _inputRoot; - private readonly IntPtr _imContext; + private readonly GtkImContext _imContext; private readonly FramebufferManager _framebuffer; protected readonly List Disposables = new List(); private Size _lastSize; private Point _lastPosition; private uint _lastKbdEvent; - public TopLevelImpl(IntPtr gtkWidget) + public TopLevelImpl(GtkWindow gtkWidget) { + GtkWidget = gtkWidget; + Disposables.Add(gtkWidget); _framebuffer = new FramebufferManager(this); _imContext = Native.GtkImMulticontextNew(); + Disposables.Add(_imContext); Native.GtkWidgetSetEvents(gtkWidget, uint.MaxValue); Disposables.Add(Signal.Connect(_imContext, "commit", OnCommit)); Connect("draw", OnDraw); @@ -39,6 +43,7 @@ namespace Avalonia.Gtk3 ConnectEvent("window-state-event", OnStateChanged); ConnectEvent("key-press-event", OnKeyEvent); ConnectEvent("key-release-event", OnKeyEvent); + Connect("destroy", OnDestroy); Native.GtkWidgetRealize(gtkWidget); } @@ -66,6 +71,12 @@ namespace Avalonia.Gtk3 return false; } + private bool OnDestroy(IntPtr gtkwidget, IntPtr userdata) + { + Closed?.Invoke(); + return false; + } + private static InputModifiers GetModifierKeys(GdkModifierType state) { var rv = InputModifiers.None; @@ -190,10 +201,10 @@ namespace Avalonia.Gtk3 public void Dispose() { - foreach(var d in Disposables) + Closed?.Invoke(); + foreach(var d in Disposables.AsEnumerable().Reverse()) d.Dispose(); Disposables.Clear(); - //TODO } public Size MaxClientSize @@ -212,7 +223,7 @@ namespace Avalonia.Gtk3 string IPlatformHandle.HandleDescriptor => "HWND"; public Action Activated { get; set; } - public Action Closed { get; set; } //TODO + public Action Closed { get; set; } public Action Deactivated { get; set; } public Action Input { get; set; } public Action Paint { get; set; } @@ -224,6 +235,8 @@ namespace Avalonia.Gtk3 public void Invalidate(Rect rect) { + if(GtkWidget.IsClosed) + return; Native.GtkWidgetQueueDrawArea(GtkWidget, (int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height); } @@ -279,12 +292,16 @@ namespace Avalonia.Gtk3 { get { + if (GtkWidget.IsClosed) + return new Size(); int w, h; Native.GtkWindowGetSize(GtkWidget, out w, out h); return new Size(w, h); } set { + if (GtkWidget.IsClosed) + return; Native.GtkWindowResize(GtkWidget, (int)value.Width, (int)value.Height); if (Native.GtkWidgetGetWindow(GtkWidget) == IntPtr.Zero) Native.GtkWidgetRealize(GtkWidget); diff --git a/src/Gtk/Avalonia.Gtk3/WindowImpl.cs b/src/Gtk/Avalonia.Gtk3/WindowImpl.cs index 25b0bd8dd3..d1a689b75f 100644 --- a/src/Gtk/Avalonia.Gtk3/WindowImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/WindowImpl.cs @@ -14,10 +14,19 @@ namespace Avalonia.Gtk3 Native.GtkWindowSetTitle(GtkWidget, t); } + class EmptyDisposable : IDisposable + { + public void Dispose() + { + + } + } + public IDisposable ShowDialog() { - return null; - //STUB + Native.GtkWindowSetModal(GtkWidget, true); + Show(); + return new EmptyDisposable(); } public void SetSystemDecorations(bool enabled) => Native.GtkWindowSetDecorated(GtkWidget, enabled); From bcd2fdfccf9b29f237e146ff56cf183103c5718c Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 1 Feb 2017 04:57:10 +0300 Subject: [PATCH 14/24] Implemented cursors --- src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj | 2 + src/Gtk/Avalonia.Gtk3/CursorFactory.cs | 79 +++++++++++++++++++ src/Gtk/Avalonia.Gtk3/GdkCursor.cs | 91 ++++++++++++++++++++++ src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs | 2 +- src/Gtk/Avalonia.Gtk3/Interop/Native.cs | 20 +++++ src/Gtk/Avalonia.Gtk3/Stubs.cs | 9 +-- src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs | 2 +- 7 files changed, 195 insertions(+), 10 deletions(-) create mode 100644 src/Gtk/Avalonia.Gtk3/CursorFactory.cs create mode 100644 src/Gtk/Avalonia.Gtk3/GdkCursor.cs diff --git a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj index 11d9b5d2e1..eb9d460959 100644 --- a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj +++ b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj @@ -45,7 +45,9 @@ KeyTransform.cs + + diff --git a/src/Gtk/Avalonia.Gtk3/CursorFactory.cs b/src/Gtk/Avalonia.Gtk3/CursorFactory.cs new file mode 100644 index 0000000000..ac547b8bc2 --- /dev/null +++ b/src/Gtk/Avalonia.Gtk3/CursorFactory.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using Avalonia.Gtk3.Interop; +using Avalonia.Input; +using Avalonia.Platform; +using CursorType = Avalonia.Gtk3.GdkCursorType; +namespace Avalonia.Gtk3 +{ + class CursorFactory : IStandardCursorFactory + { + private static readonly Dictionary CursorTypeMapping = new Dictionary + + { + {StandardCursorType.AppStarting, CursorType.Watch}, + {StandardCursorType.Arrow, CursorType.LeftPtr}, + {StandardCursorType.Cross, CursorType.Cross}, + {StandardCursorType.Hand, CursorType.Hand1}, + {StandardCursorType.Ibeam, CursorType.Xterm}, + {StandardCursorType.No, "gtk-cancel"}, + {StandardCursorType.SizeAll, CursorType.Sizing}, + //{ StandardCursorType.SizeNorthEastSouthWest, 32643 }, + {StandardCursorType.SizeNorthSouth, CursorType.SbVDoubleArrow}, + //{ StandardCursorType.SizeNorthWestSouthEast, 32642 }, + {StandardCursorType.SizeWestEast, CursorType.SbHDoubleArrow}, + {StandardCursorType.UpArrow, CursorType.BasedArrowUp}, + {StandardCursorType.Wait, CursorType.Watch}, + {StandardCursorType.Help, "gtk-help"}, + {StandardCursorType.TopSide, CursorType.TopSide}, + {StandardCursorType.BottomSize, CursorType.BottomSide}, + {StandardCursorType.LeftSide, CursorType.LeftSide}, + {StandardCursorType.RightSide, CursorType.RightSide}, + {StandardCursorType.TopLeftCorner, CursorType.TopLeftCorner}, + {StandardCursorType.TopRightCorner, CursorType.TopRightCorner}, + {StandardCursorType.BottomLeftCorner, CursorType.BottomLeftCorner}, + {StandardCursorType.BottomRightCorner, CursorType.BottomRightCorner} + }; + + private static readonly Dictionary Cache = + new Dictionary(); + + private IntPtr GetCursor(object desc) + { + IntPtr rv; + var name = desc as string; + if (name != null) + { + var theme = Native.GtkIconThemeGetDefault(); + IntPtr icon, error; + using (var u = new Utf8Buffer(name)) + icon = Native.GtkIconThemeLoadIcon(theme, u, 32, 0, out error); + rv = icon == IntPtr.Zero + ? Native.GdkCursorNew(GdkCursorType.XCursor) + : Native.GdkCursorNewFromPixbuf(Native.GdkGetDefaultDisplay(), icon, 0, 0); + } + else + { + rv = Native.GdkCursorNew((CursorType)desc); + } + + + return rv; + } + + public IPlatformHandle GetCursor(StandardCursorType cursorType) + { + IPlatformHandle rv; + if (!Cache.TryGetValue(cursorType, out rv)) + { + Cache[cursorType] = + rv = + new PlatformHandle( + GetCursor(CursorTypeMapping[cursorType]), + "GTKCURSOR"); + } + + return rv; + } + } +} \ No newline at end of file diff --git a/src/Gtk/Avalonia.Gtk3/GdkCursor.cs b/src/Gtk/Avalonia.Gtk3/GdkCursor.cs new file mode 100644 index 0000000000..48815e1273 --- /dev/null +++ b/src/Gtk/Avalonia.Gtk3/GdkCursor.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Avalonia.Gtk3 +{ + enum GdkCursorType + { + CursorIsPixmap = -1, + XCursor = 0, + Arrow = 2, + BasedArrowDown = 4, + BasedArrowUp = 6, + Boat = 8, + Bogosity = 10, + BottomLeftCorner = 12, + BottomRightCorner = 14, + BottomSide = 16, + BottomTee = 18, + BoxSpiral = 20, + CenterPtr = 22, + Circle = 24, + Clock = 26, + CoffeeMug = 28, + Cross = 30, + CrossReverse = 32, + Crosshair = 34, + DiamondCross = 36, + Dot = 38, + Dotbox = 40, + DoubleArrow = 42, + DraftLarge = 44, + DraftSmall = 46, + DrapedBox = 48, + Exchange = 50, + Fleur = 52, + Gobbler = 54, + Gumby = 56, + Hand1 = 58, + Hand2 = 60, + Heart = 62, + Icon = 64, + IronCross = 66, + LeftPtr = 68, + LeftSide = 70, + LeftTee = 72, + Leftbutton = 74, + LlAngle = 76, + LrAngle = 78, + Man = 80, + Middlebutton = 82, + Mouse = 84, + Pencil = 86, + Pirate = 88, + Plus = 90, + QuestionArrow = 92, + RightPtr = 94, + RightSide = 96, + RightTee = 98, + Rightbutton = 100, + RtlLogo = 102, + Sailboat = 104, + SbDownArrow = 106, + SbHDoubleArrow = 108, + SbLeftArrow = 110, + SbRightArrow = 112, + SbUpArrow = 114, + SbVDoubleArrow = 116, + Shuttle = 118, + Sizing = 120, + Spider = 122, + Spraycan = 124, + Star = 126, + Target = 128, + Tcross = 130, + TopLeftArrow = 132, + TopLeftCorner = 134, + TopRightCorner = 136, + TopSide = 138, + TopTee = 140, + Trek = 142, + UlAngle = 144, + Umbrella = 146, + UrAngle = 148, + Watch = 150, + Xterm = 152, + LastCursor = 153, + } +} \ No newline at end of file diff --git a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs index 943d83b57f..68583da480 100644 --- a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs +++ b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs @@ -31,7 +31,7 @@ namespace Avalonia.Gtk3 AvaloniaLocator.CurrentMutable.Bind().ToConstant(Instance) .Bind().ToSingleton() - .Bind().ToConstant(new CursorFactoryStub()) + .Bind().ToConstant(new CursorFactory()) .Bind().ToConstant(Keyboard) .Bind().ToConstant(Mouse) .Bind().ToConstant(Instance) diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs index 4112bf3f91..25c10fd32b 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs @@ -140,6 +140,9 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] public delegate IntPtr gdk_screen_get_root_window(IntPtr screen); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] + public delegate IntPtr gdk_cursor_new(GdkCursorType type); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] public delegate IntPtr gdk_window_get_pointer(IntPtr raw, out int x, out int y, out int mask); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] @@ -170,6 +173,18 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.GdkPixBuf)] public delegate IntPtr gdk_pixbuf_new_from_file(Utf8Buffer filename, out IntPtr error); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate IntPtr gtk_icon_theme_get_default(); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate IntPtr gtk_icon_theme_load_icon(IntPtr icon_theme, Utf8Buffer icon_name, gint size, int flags,out IntPtr error); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] + public delegate IntPtr gdk_cursor_new_from_pixbuf(IntPtr disp, IntPtr pixbuf, int x, int y); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] + public delegate IntPtr gdk_window_set_cursor(IntPtr window, IntPtr cursor); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.GdkPixBuf)] public delegate IntPtr gdk_pixbuf_new_from_stream(GInputStream stream, IntPtr cancel, out IntPtr error); @@ -257,12 +272,17 @@ namespace Avalonia.Gtk3.Interop public static D.gdk_display_get_default GdkGetDefaultDisplay; public static D.gdk_screen_get_width GdkScreenGetWidth; public static D.gdk_screen_get_root_window GdkScreenGetRootWindow; + public static D.gdk_cursor_new GdkCursorNew; public static D.gdk_window_get_origin GdkWindowGetOrigin; public static D.gdk_window_get_pointer GdkWindowGetPointer; public static D.gdk_window_begin_move_drag GdkWindowBeginMoveDrag; public static D.gdk_window_begin_resize_drag GdkWindowBeginResizeDrag; public static D.gdk_pixbuf_new_from_file GdkPixbufNewFromFile; + public static D.gtk_icon_theme_get_default GtkIconThemeGetDefault; + public static D.gtk_icon_theme_load_icon GtkIconThemeLoadIcon; + public static D.gdk_cursor_new_from_pixbuf GdkCursorNewFromPixbuf; + public static D.gdk_window_set_cursor GdkWindowSetCursor; public static D.gdk_pixbuf_new_from_stream GdkPixbufNewFromStream; public static D.gdk_pixbuf_save_to_bufferv GdkPixbufSaveToBufferv; diff --git a/src/Gtk/Avalonia.Gtk3/Stubs.cs b/src/Gtk/Avalonia.Gtk3/Stubs.cs index 8ebc1e9c59..6d89384838 100644 --- a/src/Gtk/Avalonia.Gtk3/Stubs.cs +++ b/src/Gtk/Avalonia.Gtk3/Stubs.cs @@ -1,11 +1,9 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Controls.Platform; -using Avalonia.Input; using Avalonia.Input.Platform; using Avalonia.Platform; @@ -13,11 +11,6 @@ using Avalonia.Platform; namespace Avalonia.Gtk3 { - class CursorFactoryStub : IStandardCursorFactory - { - public IPlatformHandle GetCursor(StandardCursorType cursorType) => new PlatformHandle(IntPtr.Zero, "STUB"); - } - class SystemDialogStub : ISystemDialogImpl { public Task ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent) => Task.FromResult(new string[0]); diff --git a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs index 0a8ae49906..e24e435c08 100644 --- a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs @@ -259,7 +259,7 @@ namespace Avalonia.Gtk3 public void SetCursor(IPlatformHandle cursor) { - //STUB + Native.GdkWindowSetCursor(Native.GtkWidgetGetWindow(GtkWidget), cursor?.Handle ?? IntPtr.Zero); } public void Show() => Native.GtkWindowPresent(GtkWidget); From cbc401b565cf59df369c24b8e44e72d1a524075e Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 1 Feb 2017 15:10:17 +0300 Subject: [PATCH 15/24] Implemented system dialogs --- src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj | 2 +- src/Gtk/Avalonia.Gtk3/Interop/GObject.cs | 10 +++ src/Gtk/Avalonia.Gtk3/Interop/Native.cs | 52 ++++++++++++ src/Gtk/Avalonia.Gtk3/Stubs.cs | 21 ----- src/Gtk/Avalonia.Gtk3/SystemDialogs.cs | 94 ++++++++++++++++++++++ src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs | 2 +- 6 files changed, 158 insertions(+), 23 deletions(-) delete mode 100644 src/Gtk/Avalonia.Gtk3/Stubs.cs create mode 100644 src/Gtk/Avalonia.Gtk3/SystemDialogs.cs diff --git a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj index eb9d460959..7908a0fcb3 100644 --- a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj +++ b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj @@ -64,7 +64,7 @@ - + diff --git a/src/Gtk/Avalonia.Gtk3/Interop/GObject.cs b/src/Gtk/Avalonia.Gtk3/Interop/GObject.cs index 9cd9c5cd8b..9ead1d2cb3 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/GObject.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/GObject.cs @@ -48,5 +48,15 @@ namespace Avalonia.Gtk3.Interop { } + + class GtkDialog : GtkWindow + { + + } + + class GtkFileChooser : GtkDialog + { + + } } diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs index 25c10fd32b..be7a4e5117 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs @@ -95,6 +95,17 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate void gtk_window_move(GtkWindow gtkWindow, int x, int y); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate GtkFileChooser gtk_file_chooser_dialog_new(Utf8Buffer title, GtkWindow parent, GtkFileChooserAction action, IntPtr ignore); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public unsafe delegate GSList* gtk_file_chooser_get_filenames(GtkFileChooser chooser); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate void gtk_file_chooser_set_select_multiple(GtkFileChooser chooser, bool allow); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate void gtk_file_chooser_set_filename(GtkFileChooser chooser, Utf8Buffer file); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] + public delegate void gtk_dialog_add_button(GtkDialog raw, Utf8Buffer button_text, GtkResponseType response_id); + @@ -203,6 +214,8 @@ namespace Avalonia.Gtk3.Interop public delegate ulong g_timeout_add(uint interval, timeout_callback callback, IntPtr data); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Glib)] public delegate ulong g_free(IntPtr data); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Glib)] + public unsafe delegate void g_slist_free(GSList* data); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gio)] public delegate GInputStream g_memory_input_stream_new_from_data(IntPtr ptr, IntPtr len, IntPtr destroyCallback); @@ -212,6 +225,9 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate bool signal_generic(IntPtr gtkWidget, IntPtr userData); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate bool signal_dialog_response(IntPtr gtkWidget, GtkResponseType response, IntPtr userData); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate bool signal_onevent(IntPtr gtkWidget, IntPtr ev, IntPtr userData); @@ -247,11 +263,17 @@ namespace Avalonia.Gtk3.Interop public static D.gtk_window_set_default_size GtkWindowSetDefaultSize; public static D.gtk_window_get_position GtkWindowGetPosition; public static D.gtk_window_move GtkWindowMove; + public static D.gtk_file_chooser_dialog_new GtkFileChooserDialogNew; + public static D.gtk_file_chooser_set_select_multiple GtkFileChooserSetSelectMultiple; + public static D.gtk_file_chooser_set_filename GtkFileChooserSetFilename; + public static D.gtk_file_chooser_get_filenames GtkFileChooserGetFilenames; + public static D.gtk_dialog_add_button GtkDialogAddButton; public static D.g_object_unref GObjectUnref; 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.g_free GFree; + public static D.g_slist_free GSlistFree; public static D.g_memory_input_stream_new_from_data GMemoryInputStreamNewFromData; public static D.gtk_widget_set_double_buffered GtkWidgetSetDoubleBuffered; public static D.gtk_widget_set_events GtkWidgetSetEvents; @@ -467,6 +489,13 @@ namespace Avalonia.Gtk3.Interop public guint is_modifier; } + [StructLayout(LayoutKind.Sequential)] + unsafe struct GSList + { + public IntPtr Data; + public GSList* Next; + } + [Flags] public enum GdkWindowState { @@ -481,6 +510,29 @@ namespace Avalonia.Gtk3.Interop Ttiled = 256 } + public enum GtkResponseType + { + Help = -11, + Apply = -10, + No = -9, + Yes = -8, + Close = -7, + Cancel = -6, + Ok = -5, + DeleteEvent = -4, + Accept = -3, + Reject = -2, + None = -1, + } + + public enum GtkFileChooserAction + { + Open, + Save, + SelectFolder, + CreateFolder, + } + [StructLayout(LayoutKind.Sequential)] struct GdkGeometry { diff --git a/src/Gtk/Avalonia.Gtk3/Stubs.cs b/src/Gtk/Avalonia.Gtk3/Stubs.cs deleted file mode 100644 index 6d89384838..0000000000 --- a/src/Gtk/Avalonia.Gtk3/Stubs.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Avalonia.Controls; -using Avalonia.Controls.Platform; -using Avalonia.Input.Platform; -using Avalonia.Platform; - -//TODO: This file should be empty once everything is implemented - -namespace Avalonia.Gtk3 -{ - class SystemDialogStub : ISystemDialogImpl - { - public Task ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent) => Task.FromResult(new string[0]); - - public Task ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent) - => Task.FromResult((string) null); - } -} diff --git a/src/Gtk/Avalonia.Gtk3/SystemDialogs.cs b/src/Gtk/Avalonia.Gtk3/SystemDialogs.cs new file mode 100644 index 0000000000..19d135492a --- /dev/null +++ b/src/Gtk/Avalonia.Gtk3/SystemDialogs.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Avalonia.Controls; +using Avalonia.Controls.Platform; +using Avalonia.Gtk3.Interop; +using Avalonia.Input.Platform; +using Avalonia.Platform; + +//TODO: This file should be empty once everything is implemented + +namespace Avalonia.Gtk3 +{ + class SystemDialogStub : ISystemDialogImpl + { + + unsafe static Task ShowDialog(string title, GtkWindow parent, GtkFileChooserAction action, + bool multiselect, string initialFileName) + { + GtkFileChooser dlg; + using (var name = title != null ? new Utf8Buffer(title) : null) + dlg = Native.GtkFileChooserDialogNew(name, parent, action, IntPtr.Zero); + if (multiselect) + Native.GtkFileChooserSetSelectMultiple(dlg, true); + + Native.GtkWindowSetModal(dlg, true); + var tcs = new TaskCompletionSource(); + List disposables = null; + Action dispose = () => + { + foreach (var d in disposables) + d.Dispose(); + disposables.Clear(); + }; + disposables = new List + { + Signal.Connect(dlg, "close", delegate + { + tcs.TrySetResult(null); + dispose(); + return false; + }), + Signal.Connect(dlg, "response", (_, resp, __)=> + { + string[] result = null; + if (resp == GtkResponseType.Accept) + { + var rlst = new List(); + var gs = Native.GtkFileChooserGetFilenames(dlg); + var cgs = gs; + while (cgs != null) + { + if (cgs->Data != IntPtr.Zero) + rlst.Add(Utf8Buffer.StringFromPtr(cgs->Data)); + cgs = cgs->Next; + } + Native.GSlistFree(gs); + result = rlst.ToArray(); + } + Native.GtkWidgetHide(dlg); + dispose(); + tcs.TrySetResult(result); + return false; + }), + dlg + }; + using (var open = new Utf8Buffer("Open")) + Native.GtkDialogAddButton(dlg, open, GtkResponseType.Accept); + using (var open = new Utf8Buffer("Cancel")) + Native.GtkDialogAddButton(dlg, open, GtkResponseType.Cancel); + if(initialFileName!=null) + using (var fn = new Utf8Buffer(initialFileName)) + Native.GtkFileChooserSetFilename(dlg, fn); + Native.GtkWindowPresent(dlg); + return tcs.Task; + } + + public Task ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent) + { + return ShowDialog(dialog.Title, ((TopLevelImpl) parent)?.GtkWidget, + dialog is OpenFileDialog ? GtkFileChooserAction.Open : GtkFileChooserAction.Save, + (dialog as OpenFileDialog)?.AllowMultiple ?? false, dialog.InitialFileName); + } + + public async Task ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent) + { + var res = await ShowDialog(dialog.Title, ((TopLevelImpl) parent)?.GtkWidget, + GtkFileChooserAction.SelectFolder, false, dialog.InitialDirectory); + return res?.FirstOrDefault(); + } + } +} diff --git a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs index e24e435c08..3884b19ae3 100644 --- a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs @@ -14,7 +14,7 @@ namespace Avalonia.Gtk3 { abstract class TopLevelImpl : ITopLevelImpl, IPlatformHandle { - protected readonly GtkWindow GtkWidget; + public readonly GtkWindow GtkWidget; private IInputRoot _inputRoot; private readonly GtkImContext _imContext; private readonly FramebufferManager _framebuffer; From f7ebaf4c8a30d62a364e6f9a8d9fe4872320de9a Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 1 Feb 2017 16:29:16 +0300 Subject: [PATCH 16/24] Various fixes --- src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs | 2 +- src/Gtk/Avalonia.Gtk3/Interop/Native.cs | 4 ++++ src/Gtk/Avalonia.Gtk3/PopupImpl.cs | 4 +++- src/Gtk/Avalonia.Gtk3/SystemDialogs.cs | 4 +--- src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs | 20 +++++++++----------- 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs index 68583da480..b5235c7eb5 100644 --- a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs +++ b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs @@ -36,7 +36,7 @@ namespace Avalonia.Gtk3 .Bind().ToConstant(Mouse) .Bind().ToConstant(Instance) .Bind().ToConstant(Instance) - .Bind().ToSingleton() + .Bind().ToSingleton() .Bind().ToConstant(new DefaultRenderLoop(60)) .Bind().ToConstant(new PlatformIconLoader()); diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs index be7a4e5117..34abe89f76 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs @@ -168,6 +168,9 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] public delegate void gdk_window_begin_resize_drag(IntPtr window, WindowEdge edge, gint button, gint root_x, gint root_y, guint32 timestamp); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] + public delegate void gdk_event_request_motions(IntPtr ev); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] public delegate IntPtr gtk_clipboard_get_for_display(IntPtr display, IntPtr atom); @@ -299,6 +302,7 @@ namespace Avalonia.Gtk3.Interop public static D.gdk_window_get_pointer GdkWindowGetPointer; public static D.gdk_window_begin_move_drag GdkWindowBeginMoveDrag; public static D.gdk_window_begin_resize_drag GdkWindowBeginResizeDrag; + public static D.gdk_event_request_motions GdkEventRequestMotions; public static D.gdk_pixbuf_new_from_file GdkPixbufNewFromFile; public static D.gtk_icon_theme_get_default GtkIconThemeGetDefault; diff --git a/src/Gtk/Avalonia.Gtk3/PopupImpl.cs b/src/Gtk/Avalonia.Gtk3/PopupImpl.cs index 3f76960e8f..e14403f25b 100644 --- a/src/Gtk/Avalonia.Gtk3/PopupImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/PopupImpl.cs @@ -37,7 +37,9 @@ namespace Avalonia.Gtk3 return; Native.GtkWindowSetDefaultSize(GtkWidget, (int)value.Width, (int)value.Height); base.ClientSize = value; - var size = ClientSize; + if (Native.GtkWidgetGetWindow(GtkWidget) == IntPtr.Zero) + Native.GtkWidgetRealize(GtkWidget); + Native.GdkWindowResize(Native.GtkWidgetGetWindow(GtkWidget), (int)value.Width, (int)value.Height); } } } diff --git a/src/Gtk/Avalonia.Gtk3/SystemDialogs.cs b/src/Gtk/Avalonia.Gtk3/SystemDialogs.cs index 19d135492a..04c224ff5b 100644 --- a/src/Gtk/Avalonia.Gtk3/SystemDialogs.cs +++ b/src/Gtk/Avalonia.Gtk3/SystemDialogs.cs @@ -9,11 +9,9 @@ using Avalonia.Gtk3.Interop; using Avalonia.Input.Platform; using Avalonia.Platform; -//TODO: This file should be empty once everything is implemented - namespace Avalonia.Gtk3 { - class SystemDialogStub : ISystemDialogImpl + class SystemDialog : ISystemDialogImpl { unsafe static Task ShowDialog(string title, GtkWindow parent, GtkFileChooserAction action, diff --git a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs index 3884b19ae3..52feafa99d 100644 --- a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs @@ -31,7 +31,7 @@ namespace Avalonia.Gtk3 _framebuffer = new FramebufferManager(this); _imContext = Native.GtkImMulticontextNew(); Disposables.Add(_imContext); - Native.GtkWidgetSetEvents(gtkWidget, uint.MaxValue); + Native.GtkWidgetSetEvents(gtkWidget, 0xFFFFFE); Disposables.Add(Signal.Connect(_imContext, "commit", OnCommit)); Connect("draw", OnDraw); Connect("realize", OnRealized); @@ -111,7 +111,7 @@ namespace Avalonia.Gtk3 : evnt->button == 3 ? RawMouseEventType.RightButtonDown : RawMouseEventType.MiddleButtonDown, new Point(evnt->x, evnt->y), GetModifierKeys(evnt->state)); Input?.Invoke(e); - return false; + return true; } private unsafe bool OnStateChanged(IntPtr w, IntPtr pev, IntPtr userData) @@ -131,8 +131,7 @@ namespace Avalonia.Gtk3 { var evnt = (GdkEventMotion*)ev; var position = new Point(evnt->x, evnt->y); - - + Native.GdkEventRequestMotions(ev); var e = new RawMouseEventArgs( Gtk3Platform.Mouse, evnt->time, @@ -140,7 +139,8 @@ namespace Avalonia.Gtk3 RawMouseEventType.Move, position, GetModifierKeys(evnt->state)); Input(e); - return false; + + return true; } private unsafe bool OnScroll(IntPtr w, IntPtr ev, IntPtr userdata) { @@ -156,12 +156,13 @@ namespace Avalonia.Gtk3 else if (evnt->direction == GdkScrollDirection.Left) delta = new Vector(step, 0); else if (evnt->direction == GdkScrollDirection.Smooth) - delta = new Vector(evnt->delta_x, evnt->delta_y); - + { + delta = new Vector(-evnt->delta_x, -evnt->delta_y); + } var e = new RawMouseWheelEventArgs(Gtk3Platform.Mouse, evnt->time, _inputRoot, new Point(evnt->x, evnt->y), delta, GetModifierKeys(evnt->state)); Input(e); - return false; + return true; } private unsafe bool OnKeyEvent(IntPtr w, IntPtr pev, IntPtr userData) @@ -303,9 +304,6 @@ namespace Avalonia.Gtk3 if (GtkWidget.IsClosed) return; Native.GtkWindowResize(GtkWidget, (int)value.Width, (int)value.Height); - if (Native.GtkWidgetGetWindow(GtkWidget) == IntPtr.Zero) - Native.GtkWidgetRealize(GtkWidget); - Native.GdkWindowResize(Native.GtkWidgetGetWindow(GtkWidget), (int)value.Width, (int)value.Height); } } From 3b3633ad2693d4c050e76fe666043180e51cd0b9 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 1 Feb 2017 18:35:16 +0300 Subject: [PATCH 17/24] Ignore duplicated scroll events --- src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs index 52feafa99d..a8dc884244 100644 --- a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs @@ -22,6 +22,7 @@ namespace Avalonia.Gtk3 private Size _lastSize; private Point _lastPosition; private uint _lastKbdEvent; + private uint _lastSmoothScrollEvent; public TopLevelImpl(GtkWindow gtkWidget) { @@ -145,6 +146,11 @@ namespace Avalonia.Gtk3 private unsafe bool OnScroll(IntPtr w, IntPtr ev, IntPtr userdata) { var evnt = (GdkEventScroll*)ev; + + //Ignore duplicates + if (evnt->time - _lastSmoothScrollEvent < 10 && evnt->direction != GdkScrollDirection.Smooth) + return true; + var delta = new Vector(); const double step = (double) 1; if (evnt->direction == GdkScrollDirection.Down) @@ -158,6 +164,7 @@ namespace Avalonia.Gtk3 else if (evnt->direction == GdkScrollDirection.Smooth) { delta = new Vector(-evnt->delta_x, -evnt->delta_y); + _lastSmoothScrollEvent = evnt->time; } var e = new RawMouseWheelEventArgs(Gtk3Platform.Mouse, evnt->time, _inputRoot, new Point(evnt->x, evnt->y), delta, GetModifierKeys(evnt->state)); From e158f848fbb5b85d46705ab594453b9bd0d5e97c Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 1 Feb 2017 22:07:21 +0300 Subject: [PATCH 18/24] Fixed namespace --- src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs index b5235c7eb5..11209825dc 100644 --- a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs +++ b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs @@ -11,6 +11,7 @@ using Avalonia.Input; using Avalonia.Input.Platform; using Avalonia.Platform; using Avalonia.Rendering; +using Avalonia.Gtk3; namespace Avalonia.Gtk3 { @@ -99,7 +100,10 @@ namespace Avalonia.Gtk3 public bool CurrentThreadIsLoopThread => s_tlsMarker; } +} +namespace Avalonia +{ public static class Gtk3AppBuilderExtensions { public static T UseGtk3(this AppBuilderBase builder, ICustomGtk3NativeLibraryResolver resolver = null) From 64f3a880b6361390cab0352a38b45202cf4dab9d Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 1 Feb 2017 22:56:24 +0300 Subject: [PATCH 19/24] Workaround for popups --- src/Gtk/Avalonia.Gtk3/PopupImpl.cs | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/Gtk/Avalonia.Gtk3/PopupImpl.cs b/src/Gtk/Avalonia.Gtk3/PopupImpl.cs index e14403f25b..7bd16cfabd 100644 --- a/src/Gtk/Avalonia.Gtk3/PopupImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/PopupImpl.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -13,9 +14,6 @@ namespace Avalonia.Gtk3 static GtkWindow CreateWindow() { var window = Native.GtkWindowNew(GtkWindowType.Popup); - Native.GtkWindowSetSizeRequest(window, 1, 1); - Native.GtkWindowSetDefaultSize(window, 200, 200); - return window; } @@ -25,21 +23,18 @@ namespace Avalonia.Gtk3 } + private Size _desiredSize = new Size(1, 1); public override Size ClientSize { - get - { - return base.ClientSize; - } + get { return _desiredSize; } set { - if(GtkWidget.IsClosed) + _desiredSize = value; + if (GtkWidget.IsClosed) + return; + if (base.ClientSize == value) return; - Native.GtkWindowSetDefaultSize(GtkWidget, (int)value.Width, (int)value.Height); - base.ClientSize = value; - if (Native.GtkWidgetGetWindow(GtkWidget) == IntPtr.Zero) - Native.GtkWidgetRealize(GtkWidget); - Native.GdkWindowResize(Native.GtkWidgetGetWindow(GtkWidget), (int)value.Width, (int)value.Height); + Native.GtkWindowResize(GtkWidget, (int) value.Width, (int) value.Height); } } } From a649d176a3812e2e6f72d2d5d4ea45abe0756437 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 2 Feb 2017 00:30:56 +0300 Subject: [PATCH 20/24] Fixes --- src/Gtk/Avalonia.Gtk3/PopupImpl.cs | 5 +++-- src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Gtk/Avalonia.Gtk3/PopupImpl.cs b/src/Gtk/Avalonia.Gtk3/PopupImpl.cs index 7bd16cfabd..7d0423fd7a 100644 --- a/src/Gtk/Avalonia.Gtk3/PopupImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/PopupImpl.cs @@ -32,9 +32,10 @@ namespace Avalonia.Gtk3 _desiredSize = value; if (GtkWidget.IsClosed) return; - if (base.ClientSize == value) - return; Native.GtkWindowResize(GtkWidget, (int) value.Width, (int) value.Height); + if (Native.GtkWidgetGetWindow(GtkWidget) == IntPtr.Zero) + Native.GtkWidgetRealize(GtkWidget); + Native.GdkWindowResize(Native.GtkWidgetGetWindow(GtkWidget), (int) value.Width, (int) value.Height); } } } diff --git a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs index a8dc884244..80eb94d442 100644 --- a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs @@ -267,6 +267,8 @@ namespace Avalonia.Gtk3 public void SetCursor(IPlatformHandle cursor) { + if (GtkWidget.IsClosed) + return; Native.GdkWindowSetCursor(Native.GtkWidgetGetWindow(GtkWidget), cursor?.Handle ?? IntPtr.Zero); } From 3bce0a9583ac4b271748c08c0072e70cbf47d15c Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 2 Feb 2017 01:16:26 +0300 Subject: [PATCH 21/24] Keep a reference to gobject while signal is attached --- src/Gtk/Avalonia.Gtk3/Interop/Native.cs | 3 +++ src/Gtk/Avalonia.Gtk3/Interop/Signal.cs | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs index 34abe89f76..bf66ef0cbb 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs @@ -210,6 +210,8 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)] public delegate void g_object_unref(IntPtr instance); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)] + public delegate void g_object_ref(GObject instance); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)] public delegate ulong g_signal_connect_object(GObject instance, Utf8Buffer signal, IntPtr handler, IntPtr userData, int flags); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)] public delegate ulong g_signal_handler_disconnect(GObject instance, ulong connectionId); @@ -272,6 +274,7 @@ namespace Avalonia.Gtk3.Interop public static D.gtk_file_chooser_get_filenames GtkFileChooserGetFilenames; public static D.gtk_dialog_add_button GtkDialogAddButton; public static D.g_object_unref GObjectUnref; + public static D.g_object_ref GObjectRef; public static D.g_signal_connect_object GSignalConnectObject; public static D.g_signal_handler_disconnect GSignalHandlerDisconnect; public static D.g_timeout_add GTimeoutAdd; diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Signal.cs b/src/Gtk/Avalonia.Gtk3/Interop/Signal.cs index abbcab9e21..5f173000a2 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Signal.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Signal.cs @@ -18,6 +18,7 @@ namespace Avalonia.Gtk3.Interop public ConnectedSignal(GObject instance, GCHandle handle, ulong id) { _instance = instance; + Native.GObjectRef(instance); _handle = handle; _id = id; } @@ -26,6 +27,7 @@ namespace Avalonia.Gtk3.Interop { if (_handle.IsAllocated) { + Native.GObjectUnref(_instance.DangerousGetHandle()); Native.GSignalHandlerDisconnect(_instance, _id); _handle.Free(); } From f64f9f1a28dbe2d3d2568510707fece2e79b26b1 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 2 Feb 2017 01:36:23 +0300 Subject: [PATCH 22/24] Removed magic --- src/Gtk/Avalonia.Gtk3/PopupImpl.cs | 18 ------------------ src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs | 9 +++++---- 2 files changed, 5 insertions(+), 22 deletions(-) diff --git a/src/Gtk/Avalonia.Gtk3/PopupImpl.cs b/src/Gtk/Avalonia.Gtk3/PopupImpl.cs index 7d0423fd7a..5d23148b76 100644 --- a/src/Gtk/Avalonia.Gtk3/PopupImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/PopupImpl.cs @@ -19,24 +19,6 @@ namespace Avalonia.Gtk3 public PopupImpl() : base(CreateWindow()) { - - - } - - private Size _desiredSize = new Size(1, 1); - public override Size ClientSize - { - get { return _desiredSize; } - set - { - _desiredSize = value; - if (GtkWidget.IsClosed) - return; - Native.GtkWindowResize(GtkWidget, (int) value.Width, (int) value.Height); - if (Native.GtkWidgetGetWindow(GtkWidget) == IntPtr.Zero) - Native.GtkWidgetRealize(GtkWidget); - Native.GdkWindowResize(Native.GtkWidgetGetWindow(GtkWidget), (int) value.Width, (int) value.Height); - } } } } diff --git a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs index 80eb94d442..10ff735476 100644 --- a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs @@ -46,6 +46,7 @@ namespace Avalonia.Gtk3 ConnectEvent("key-release-event", OnKeyEvent); Connect("destroy", OnDestroy); Native.GtkWidgetRealize(gtkWidget); + _lastSize = ClientSize; } private bool OnConfigured(IntPtr gtkwidget, IntPtr ev, IntPtr userdata) @@ -53,14 +54,14 @@ namespace Avalonia.Gtk3 var size = ClientSize; if (_lastSize != size) { - _lastSize = size; Resized?.Invoke(size); + _lastSize = size; } var pos = Position; if (_lastPosition != pos) { - _lastPosition = pos; PositionChanged?.Invoke(pos); + _lastPosition = pos; } return false; @@ -115,7 +116,7 @@ namespace Avalonia.Gtk3 return true; } - private unsafe bool OnStateChanged(IntPtr w, IntPtr pev, IntPtr userData) + protected virtual unsafe bool OnStateChanged(IntPtr w, IntPtr pev, IntPtr userData) { var ev = (GdkEventWindowState*) pev; if (ev->changed_mask.HasFlag(GdkWindowState.Focused)) @@ -298,7 +299,7 @@ namespace Avalonia.Gtk3 } - public virtual Size ClientSize + public Size ClientSize { get { From 7776627fdab611880c21cad42d605fa75819e72e Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 2 Feb 2017 20:48:03 +0300 Subject: [PATCH 23/24] Build-related --- src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj index 7908a0fcb3..fe0d10d184 100644 --- a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj +++ b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj @@ -17,7 +17,7 @@ v5.0 - + true full false @@ -27,7 +27,7 @@ 4 true - + pdbonly true bin\Release\ From 39f4f45d77ac93320b081baaa8c617802cdca2c8 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Thu, 2 Feb 2017 20:51:45 +0300 Subject: [PATCH 24/24] Rollback unrelated changes --- samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj | 2 +- samples/ControlCatalog.Desktop/Program.cs | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj b/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj index fb2eba3c2f..ca5743dd5c 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} - Exe + WinExe Properties ControlCatalog.Desktop ControlCatalog.Desktop diff --git a/samples/ControlCatalog.Desktop/Program.cs b/samples/ControlCatalog.Desktop/Program.cs index 4abdce2657..b67c5ea51d 100644 --- a/samples/ControlCatalog.Desktop/Program.cs +++ b/samples/ControlCatalog.Desktop/Program.cs @@ -2,7 +2,6 @@ using System; using System.Linq; using Avalonia; using Avalonia.Controls; -using Avalonia.Gtk3; using Avalonia.Logging.Serilog; using Avalonia.Platform; using Serilog; @@ -18,8 +17,7 @@ namespace ControlCatalog // TODO: Make this work with GTK/Skia/Cairo depending on command-line args // again. AppBuilder.Configure() - .UseSkia() - .UseGtk3() + .UsePlatformDetect() .Start(); }