18 changed files with 895 additions and 1 deletions
@ -0,0 +1 @@ |
|||
project.lock.json |
|||
@ -0,0 +1,80 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
|||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> |
|||
<PropertyGroup> |
|||
<MinimumVisualStudioVersion>14.0</MinimumVisualStudioVersion> |
|||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> |
|||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> |
|||
<ProjectGuid>{BB1F7BB5-6AD4-4776-94D9-C09D0A972658}</ProjectGuid> |
|||
<OutputType>Library</OutputType> |
|||
<AppDesignerFolder>Properties</AppDesignerFolder> |
|||
<RootNamespace>Avalonia.Gtk3</RootNamespace> |
|||
<AssemblyName>Avalonia.Gtk3</AssemblyName> |
|||
<DefaultLanguage>en-US</DefaultLanguage> |
|||
<FileAlignment>512</FileAlignment> |
|||
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> |
|||
<TargetFrameworkProfile> |
|||
</TargetFrameworkProfile> |
|||
<TargetFrameworkVersion>v5.0</TargetFrameworkVersion> |
|||
</PropertyGroup> |
|||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> |
|||
<DebugSymbols>true</DebugSymbols> |
|||
<DebugType>full</DebugType> |
|||
<Optimize>false</Optimize> |
|||
<OutputPath>bin\Debug\</OutputPath> |
|||
<DefineConstants>DEBUG;TRACE</DefineConstants> |
|||
<ErrorReport>prompt</ErrorReport> |
|||
<WarningLevel>4</WarningLevel> |
|||
</PropertyGroup> |
|||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> |
|||
<DebugType>pdbonly</DebugType> |
|||
<Optimize>true</Optimize> |
|||
<OutputPath>bin\Release\</OutputPath> |
|||
<DefineConstants>TRACE</DefineConstants> |
|||
<ErrorReport>prompt</ErrorReport> |
|||
<WarningLevel>4</WarningLevel> |
|||
</PropertyGroup> |
|||
<ItemGroup> |
|||
<!-- A reference to the entire .NET Framework is automatically included --> |
|||
<None Include="project.json" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<Compile Include="Gtk3Platform.cs" /> |
|||
<Compile Include="Interop\DynLoader.cs" /> |
|||
<Compile Include="Interop\Native.cs" /> |
|||
<Compile Include="Interop\NativeException.cs" /> |
|||
<Compile Include="Interop\Resolver.cs" /> |
|||
<Compile Include="Interop\Signal.cs" /> |
|||
<Compile Include="Properties\AssemblyInfo.cs" /> |
|||
<Compile Include="Stubs.cs" /> |
|||
<Compile Include="TopLevelImpl.cs" /> |
|||
<Compile Include="Interop\Utf8Buffer.cs" /> |
|||
<Compile Include="WindowImpl.cs" /> |
|||
</ItemGroup> |
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj"> |
|||
<Project>{B09B78D8-9B26-48B0-9149-D64A2F120F3F}</Project> |
|||
<Name>Avalonia.Base</Name> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\..\Avalonia.Controls\Avalonia.Controls.csproj"> |
|||
<Project>{D2221C82-4A25-4583-9B43-D791E3F6820C}</Project> |
|||
<Name>Avalonia.Controls</Name> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\..\Avalonia.Input\Avalonia.Input.csproj"> |
|||
<Project>{62024B2D-53EB-4638-B26B-85EEAA54866E}</Project> |
|||
<Name>Avalonia.Input</Name> |
|||
</ProjectReference> |
|||
<ProjectReference Include="..\..\Avalonia.SceneGraph\Avalonia.SceneGraph.csproj"> |
|||
<Project>{EB582467-6ABB-43A1-B052-E981BA910E3A}</Project> |
|||
<Name>Avalonia.SceneGraph</Name> |
|||
</ProjectReference> |
|||
</ItemGroup> |
|||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" /> |
|||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. |
|||
Other similar extension points exist, see Microsoft.Common.targets. |
|||
<Target Name="BeforeBuild"> |
|||
</Target> |
|||
<Target Name="AfterBuild"> |
|||
</Target> |
|||
--> |
|||
</Project> |
|||
@ -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<IWindowingPlatform>().ToConstant(Instance) |
|||
.Bind<IClipboard>().ToSingleton<ClipboardStub>() |
|||
.Bind<IStandardCursorFactory>().ToConstant(new CursorFactoryStub()) |
|||
.Bind<IKeyboardDevice>().ToConstant(Keyboard) |
|||
.Bind<IMouseDevice>().ToConstant(Mouse) |
|||
.Bind<IPlatformSettings>().ToConstant(Instance) |
|||
.Bind<IPlatformThreadingInterface>().ToConstant(Instance) |
|||
.Bind<ISystemDialogImpl>().ToSingleton<SystemDialogStub>() |
|||
.Bind<IPlatformIconLoader>().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<T>(this AppBuilderBase<T> builder) where T : AppBuilderBase<T>, new() |
|||
=> builder.UseWindowingSubsystem(Gtk3Platform.Initialize, "GTK3"); |
|||
} |
|||
} |
|||
@ -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<string, int, IntPtr> DlOpen; |
|||
private static Func<IntPtr, string, IntPtr> DlSym; |
|||
private static Func<IntPtr> 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; |
|||
} |
|||
} |
|||
} |
|||
@ -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 |
|||
} |
|||
} |
|||
@ -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) |
|||
{ |
|||
} |
|||
|
|||
} |
|||
} |
|||
@ -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<GtkImportAttribute>().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"); |
|||
|
|||
} |
|||
|
|||
|
|||
} |
|||
} |
|||
@ -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<T>(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); |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
@ -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")] |
|||
@ -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. |
|||
@ -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<string> 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<string[]> ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent) => Task.FromResult(new string[0]); |
|||
|
|||
public Task<string> 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; |
|||
} |
|||
} |
|||
@ -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<Native.D.signal_widget_draw>(_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<RawInputEventArgs> Input { get; set; } |
|||
public Action<Rect> Paint { get; set; } |
|||
public Action<Size> Resized { get; set; } |
|||
public Action<double> ScalingChanged { get; set; } |
|||
public Action<Point> 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)); |
|||
} |
|||
} |
|||
@ -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
|
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
{ |
|||
"supports": {}, |
|||
"dependencies": { |
|||
"Microsoft.NETCore.Portable.Compatibility": "1.0.1", |
|||
"NETStandard.Library": "1.6.0" |
|||
}, |
|||
"frameworks": { |
|||
"netstandard1.1": {} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue