Browse Source

Merge branch 'master' into issues/1395

pull/1962/head
Steven Kirk 8 years ago
committed by GitHub
parent
commit
6dc296dbd5
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .travis.yml
  2. 26
      Avalonia.sln
  3. 41
      build.cake
  4. 1
      dirs.proj
  5. 2
      samples/ControlCatalog/Pages/ButtonPage.xaml
  6. 1
      src/Android/Avalonia.Android/AndroidPlatform.cs
  7. 9
      src/Avalonia.Controls/Application.cs
  8. 26
      src/Avalonia.Controls/Remote/RemoteWidget.cs
  9. 161
      src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs
  10. 378
      src/Avalonia.Controls/TextBox.cs
  11. 2
      src/Avalonia.DesignerSupport/DesignWindowLoader.cs
  12. 3
      src/Avalonia.DesignerSupport/Remote/PreviewerWindowingPlatform.cs
  13. 10
      src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs
  14. 4
      src/Avalonia.DesignerSupport/Remote/Stubs.cs
  15. 18
      src/Avalonia.Input/Key.cs
  16. 11
      src/Avalonia.Input/KeyGesture.cs
  17. 98
      src/Avalonia.Input/Platform/PlatformHotkeyConfiguration.cs
  18. 4
      src/Avalonia.Native.OSX/.gitignore
  19. 328
      src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj
  20. 7
      src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  21. 91
      src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme
  22. 12
      src/Avalonia.Native.OSX/KeyTransform.h
  23. 241
      src/Avalonia.Native.OSX/KeyTransform.mm
  24. 51
      src/Avalonia.Native.OSX/Screens.mm
  25. 262
      src/Avalonia.Native.OSX/SystemDialogs.mm
  26. 47
      src/Avalonia.Native.OSX/clipboard.mm
  27. 34
      src/Avalonia.Native.OSX/common.h
  28. 29
      src/Avalonia.Native.OSX/cursor.h
  29. 73
      src/Avalonia.Native.OSX/cursor.mm
  30. 255
      src/Avalonia.Native.OSX/gl.mm
  31. 178
      src/Avalonia.Native.OSX/main.mm
  32. 190
      src/Avalonia.Native.OSX/platformthreading.mm
  33. 33
      src/Avalonia.Native.OSX/window.h
  34. 1228
      src/Avalonia.Native.OSX/window.mm
  35. 1
      src/Avalonia.Native/.gitignore
  36. 22
      src/Avalonia.Native/Avalonia.Native.csproj
  37. 9
      src/Avalonia.Native/Avalonia.Native.targets
  38. 132
      src/Avalonia.Native/AvaloniaNativePlatform.cs
  39. 29
      src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs
  40. 80
      src/Avalonia.Native/CallbackBase.cs
  41. 47
      src/Avalonia.Native/ClipboardImpl.cs
  42. 45
      src/Avalonia.Native/Cursor.cs
  43. 87
      src/Avalonia.Native/DeferredFramebuffer.cs
  44. 108
      src/Avalonia.Native/DeferredRendererProxy.cs
  45. 128
      src/Avalonia.Native/DynLoader.cs
  46. 134
      src/Avalonia.Native/GlPlatformFeature.cs
  47. 35
      src/Avalonia.Native/Helpers.cs
  48. 50
      src/Avalonia.Native/IconLoader.cs
  49. 27
      src/Avalonia.Native/Mappings.xml
  50. 101
      src/Avalonia.Native/PlatformThreadingInterface.cs
  51. 39
      src/Avalonia.Native/PopupImpl.cs
  52. 45
      src/Avalonia.Native/ScreenImpl.cs
  53. 90
      src/Avalonia.Native/SystemDialogs.cs
  54. 100
      src/Avalonia.Native/WindowImpl.cs
  55. 351
      src/Avalonia.Native/WindowImplBase.cs
  56. 5
      src/Avalonia.Native/headers/avalonia-native-guids.h
  57. 363
      src/Avalonia.Native/headers/avalonia-native.h
  58. 57
      src/Avalonia.Native/headers/com.h
  59. 183
      src/Avalonia.Native/headers/comimpl.h
  60. 1023
      src/Avalonia.Native/headers/key.h
  61. 2
      src/Avalonia.Native/regen.sh
  62. 34
      src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs
  63. 44
      src/Avalonia.ReactiveUI/ReactiveUserControl.cs
  64. 44
      src/Avalonia.ReactiveUI/ReactiveWindow.cs
  65. 6
      src/Avalonia.Remote.Protocol/InputMessages.cs
  66. 7
      src/Avalonia.Remote.Protocol/ViewportMessages.cs
  67. 2
      src/Avalonia.Styling/Styling/IStyle.cs
  68. 52
      src/Avalonia.Styling/Styling/Style.cs
  69. 8
      src/Avalonia.Styling/Styling/Styles.cs
  70. 9
      src/Avalonia.Styling/Styling/packages.config
  71. 28
      src/Avalonia.Themes.Default/Accents/BaseLight.xaml
  72. 4
      src/Avalonia.Themes.Default/Button.xaml
  73. 2
      src/Avalonia.Themes.Default/ButtonSpinner.xaml
  74. 2
      src/Avalonia.Themes.Default/Calendar.xaml
  75. 4
      src/Avalonia.Themes.Default/CalendarButton.xaml
  76. 4
      src/Avalonia.Themes.Default/CalendarDayButton.xaml
  77. 2
      src/Avalonia.Themes.Default/CalendarItem.xaml
  78. 2
      src/Avalonia.Themes.Default/CheckBox.xaml
  79. 4
      src/Avalonia.Themes.Default/ContextMenu.xaml
  80. 2
      src/Avalonia.Themes.Default/DataValidationErrors.xaml
  81. 12
      src/Avalonia.Themes.Default/DatePicker.xaml
  82. 2
      src/Avalonia.Themes.Default/DropDown.xaml
  83. 4
      src/Avalonia.Themes.Default/Expander.xaml
  84. 4
      src/Avalonia.Themes.Default/GridSplitter.xaml
  85. 2
      src/Avalonia.Themes.Default/NumericUpDown.xaml
  86. 2
      src/Avalonia.Themes.Default/RadioButton.xaml
  87. 4
      src/Avalonia.Themes.Default/RepeatButton.xaml
  88. 12
      src/Avalonia.Themes.Default/ScrollBar.xaml
  89. 4
      src/Avalonia.Themes.Default/Slider.xaml
  90. 2
      src/Avalonia.Themes.Default/TabStripItem.xaml
  91. 4
      src/Avalonia.Themes.Default/TextBox.xaml
  92. 6
      src/Avalonia.Themes.Default/ToggleButton.xaml
  93. 3
      src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs
  94. 36
      src/Gtk/Avalonia.Gtk3/KeyTransform.cs
  95. 2
      src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs
  96. 2
      src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs
  97. 10
      src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs
  98. 37
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlObjectWriter.cs
  99. 18
      src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs
  100. 10
      src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs

2
.travis.yml

@ -11,6 +11,8 @@ mono:
- 5.2.0
dotnet: 2.1.200
script:
- sudo apt-get update
- sudo apt-get install castxml
- ./build.sh --target "Travis" --configuration "Release"
notifications:
email: false

26
Avalonia.sln

@ -188,6 +188,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia.UnitTests", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.OpenGL", "src\Avalonia.OpenGL\Avalonia.OpenGL.csproj", "{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Native", "src\Avalonia.Native\Avalonia.Native.csproj", "{12A91A62-C064-42CA-9A8C-A1272F354388}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13
@ -1689,6 +1691,30 @@ Global
{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Release|iPhone.Build.0 = Release|Any CPU
{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{7CCAEFC4-135D-401D-BDDD-896B9B7D3569}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{12A91A62-C064-42CA-9A8C-A1272F354388}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU
{12A91A62-C064-42CA-9A8C-A1272F354388}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU
{12A91A62-C064-42CA-9A8C-A1272F354388}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU
{12A91A62-C064-42CA-9A8C-A1272F354388}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU
{12A91A62-C064-42CA-9A8C-A1272F354388}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{12A91A62-C064-42CA-9A8C-A1272F354388}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU
{12A91A62-C064-42CA-9A8C-A1272F354388}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU
{12A91A62-C064-42CA-9A8C-A1272F354388}.AppStore|Any CPU.Build.0 = Debug|Any CPU
{12A91A62-C064-42CA-9A8C-A1272F354388}.AppStore|iPhone.ActiveCfg = Debug|Any CPU
{12A91A62-C064-42CA-9A8C-A1272F354388}.AppStore|iPhone.Build.0 = Debug|Any CPU
{12A91A62-C064-42CA-9A8C-A1272F354388}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{12A91A62-C064-42CA-9A8C-A1272F354388}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU
{12A91A62-C064-42CA-9A8C-A1272F354388}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{12A91A62-C064-42CA-9A8C-A1272F354388}.Debug|Any CPU.Build.0 = Debug|Any CPU
{12A91A62-C064-42CA-9A8C-A1272F354388}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{12A91A62-C064-42CA-9A8C-A1272F354388}.Debug|iPhone.Build.0 = Debug|Any CPU
{12A91A62-C064-42CA-9A8C-A1272F354388}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{12A91A62-C064-42CA-9A8C-A1272F354388}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{12A91A62-C064-42CA-9A8C-A1272F354388}.Release|Any CPU.ActiveCfg = Release|Any CPU
{12A91A62-C064-42CA-9A8C-A1272F354388}.Release|Any CPU.Build.0 = Release|Any CPU
{12A91A62-C064-42CA-9A8C-A1272F354388}.Release|iPhone.ActiveCfg = Release|Any CPU
{12A91A62-C064-42CA-9A8C-A1272F354388}.Release|iPhone.Build.0 = Release|Any CPU
{12A91A62-C064-42CA-9A8C-A1272F354388}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{12A91A62-C064-42CA-9A8C-A1272F354388}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

41
build.cake

@ -3,8 +3,8 @@
///////////////////////////////////////////////////////////////////////////////
#addin "nuget:?package=NuGet.Core&version=2.14.0"
#tool "nuget:?package=NuGet.CommandLine&version=4.3.0"
#tool "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2017.1.20170613.162720"
#tool "nuget:?package=NuGet.CommandLine&version=4.7.1"
#tool "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2018.2.3"
///////////////////////////////////////////////////////////////////////////////
// TOOLS
@ -311,42 +311,6 @@ Task("Publish-NuGet-Impl")
Information("Publish-NuGet Task failed, but continuing with next Task...");
});
Task("Inspect-Impl")
.WithCriteria<AvaloniaBuildData>((context, data) => data.Parameters.IsRunningOnWindows)
.Does(() =>
{
var badIssues = new []{"PossibleNullReferenceException"};
var whitelist = new []{"tests", "src\\android", "src\\ios",
"src\\markup\\avalonia.markup.xaml\\portablexaml\\portable.xaml.github"};
Information("Running code inspections");
var exitCode = StartProcess(Context.Tools.Resolve("inspectcode.exe"),
new ProcessSettings
{
Arguments = "--output=artifacts\\inspectcode.xml --profile=Avalonia.sln.DotSettings Avalonia.sln",
RedirectStandardOutput = true
});
Information("Analyzing report");
var doc = XDocument.Parse(System.IO.File.ReadAllText("artifacts\\inspectcode.xml"));
var failBuild = false;
foreach(var xml in doc.Descendants("Issue"))
{
var typeId = xml.Attribute("TypeId").Value.ToString();
if(badIssues.Contains(typeId))
{
var file = xml.Attribute("File").Value.ToString().ToLower();
if(whitelist.Any(wh => file.StartsWith(wh)))
continue;
var line = xml.Attribute("Line").Value.ToString();
Error(typeId + " - " + file + " on line " + line);
failBuild = true;
}
}
if(failBuild)
throw new Exception("Issues found");
});
///////////////////////////////////////////////////////////////////////////////
// TARGETS
///////////////////////////////////////////////////////////////////////////////
@ -364,7 +328,6 @@ Task("Run-Tests")
Task("Package")
.IsDependentOn("Run-Tests")
.IsDependentOn("Inspect-Impl")
.IsDependentOn("Create-NuGet-Packages-Impl");
Task("AppVeyor")

1
dirs.proj

@ -5,6 +5,7 @@
<ProjectReference Include="tests/**/*.*proj" />
<ProjectReference Remove="**/*.shproj" />
<ProjectReference Remove="src/Markup/Avalonia.Markup.Xaml/PortableXaml/**/*.*proj" />
<ProjectReference Remove="**/*.pbxproj" />
</ItemGroup>
<ItemGroup Condition="!Exists('$(MSBuildExtensionsPath)\Xamarin\Android')">
<ProjectReference Remove="src/Android/**/*.*proj" />

2
samples/ControlCatalog/Pages/ButtonPage.xaml

@ -18,7 +18,7 @@
<Style>
<Style.Resources>
<SolidColorBrush x:Key="ThemeBorderMidBrush">Red</SolidColorBrush>
<SolidColorBrush x:Key="ThemeControlDarkBrush">DarkRed</SolidColorBrush>
<SolidColorBrush x:Key="ThemeControlHighBrush">DarkRed</SolidColorBrush>
</Style.Resources>
</Style>
</Button.Styles>

1
src/Android/Avalonia.Android/AndroidPlatform.cs

@ -54,6 +54,7 @@ namespace Avalonia.Android
.Bind<IPlatformIconLoader>().ToSingleton<PlatformIconLoader>()
.Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60))
.Bind<IRenderLoop>().ToConstant(new RenderLoop())
.Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>()
.Bind<IAssetLoader>().ToConstant(new AssetLoader(app.GetType().Assembly));
SkiaPlatform.Initialize();

9
src/Avalonia.Controls/Application.cs

@ -122,11 +122,11 @@ namespace Avalonia
if (_resources != null)
{
hadResources = _resources.Count > 0;
_resources.ResourcesChanged -= ResourcesChanged;
_resources.ResourcesChanged -= ThisResourcesChanged;
}
_resources = value;
_resources.ResourcesChanged += ResourcesChanged;
_resources.ResourcesChanged += ThisResourcesChanged;
if (hadResources || _resources.Count > 0)
{
@ -343,5 +343,10 @@ namespace Avalonia
.Bind<IGlobalClock>().ToConstant(clock)
.GetService<IRenderLoop>()?.Add(clock);
}
private void ThisResourcesChanged(object sender, ResourcesChangedEventArgs e)
{
ResourcesChanged?.Invoke(this, e);
}
}
}

26
src/Avalonia.Controls/Remote/RemoteWidget.cs

@ -11,11 +11,19 @@ namespace Avalonia.Controls.Remote
{
public class RemoteWidget : Control
{
public enum SizingMode
{
Local,
Remote
}
private readonly IAvaloniaRemoteTransportConnection _connection;
private FrameMessage _lastFrame;
private WriteableBitmap _bitmap;
public RemoteWidget(IAvaloniaRemoteTransportConnection connection)
{
Mode = SizingMode.Local;
_connection = connection;
_connection.OnMessage += (t, msg) => Dispatcher.UIThread.Post(() => OnMessage(msg));
_connection.Send(new ClientSupportedPixelFormatsMessage
@ -28,6 +36,8 @@ namespace Avalonia.Controls.Remote
});
}
public SizingMode Mode { get; set; }
private void OnMessage(object msg)
{
if (msg is FrameMessage frame)
@ -44,13 +54,17 @@ namespace Avalonia.Controls.Remote
protected override void ArrangeCore(Rect finalRect)
{
_connection.Send(new ClientViewportAllocatedMessage
if (Mode == SizingMode.Local)
{
Width = finalRect.Width,
Height = finalRect.Height,
DpiX = 96,
DpiY = 96 //TODO: Somehow detect the actual DPI
});
_connection.Send(new ClientViewportAllocatedMessage
{
Width = finalRect.Width,
Height = finalRect.Height,
DpiX = 10 * 96,
DpiY = 10 * 96 //TODO: Somehow detect the actual DPI
});
}
base.ArrangeCore(finalRect);
}

161
src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs

@ -4,11 +4,15 @@ using System.Runtime.InteropServices;
using Avalonia.Controls.Embedding.Offscreen;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Layout;
using Avalonia.Platform;
using Avalonia.Remote.Protocol;
using Avalonia.Remote.Protocol.Input;
using Avalonia.Remote.Protocol.Viewport;
using Avalonia.Threading;
using InputModifiers = Avalonia.Input.InputModifiers;
using Key = Avalonia.Input.Key;
using PixelFormat = Avalonia.Platform.PixelFormat;
using ProtocolPixelFormat = Avalonia.Remote.Protocol.Viewport.PixelFormat;
@ -31,6 +35,67 @@ namespace Avalonia.Controls.Remote.Server
{
_transport = transport;
_transport.OnMessage += OnMessage;
KeyboardDevice = AvaloniaLocator.Current.GetService<IKeyboardDevice>();
}
private static RawMouseEventType GetAvaloniaEventType (Avalonia.Remote.Protocol.Input.MouseButton button, bool pressed)
{
switch (button)
{
case Avalonia.Remote.Protocol.Input.MouseButton.Left:
return pressed ? RawMouseEventType.LeftButtonDown : RawMouseEventType.LeftButtonUp;
case Avalonia.Remote.Protocol.Input.MouseButton.Middle:
return pressed ? RawMouseEventType.MiddleButtonDown : RawMouseEventType.MiddleButtonUp;
case Avalonia.Remote.Protocol.Input.MouseButton.Right:
return pressed ? RawMouseEventType.RightButtonDown : RawMouseEventType.RightButtonUp;
default:
return RawMouseEventType.Move;
}
}
private static InputModifiers GetAvaloniaInputModifiers (Avalonia.Remote.Protocol.Input.InputModifiers[] modifiers)
{
var result = InputModifiers.None;
foreach(var modifier in modifiers)
{
switch (modifier)
{
case Avalonia.Remote.Protocol.Input.InputModifiers.Control:
result |= InputModifiers.Control;
break;
case Avalonia.Remote.Protocol.Input.InputModifiers.Alt:
result |= InputModifiers.Alt;
break;
case Avalonia.Remote.Protocol.Input.InputModifiers.Shift:
result |= InputModifiers.Shift;
break;
case Avalonia.Remote.Protocol.Input.InputModifiers.Windows:
result |= InputModifiers.Windows;
break;
case Avalonia.Remote.Protocol.Input.InputModifiers.LeftMouseButton:
result |= InputModifiers.LeftMouseButton;
break;
case Avalonia.Remote.Protocol.Input.InputModifiers.MiddleMouseButton:
result |= InputModifiers.MiddleMouseButton;
break;
case Avalonia.Remote.Protocol.Input.InputModifiers.RightMouseButton:
result |= InputModifiers.RightMouseButton;
break;
}
}
return result;
}
protected virtual void OnMessage(IAvaloniaRemoteTransportConnection transport, object obj)
@ -45,6 +110,16 @@ namespace Avalonia.Controls.Remote.Server
}
Dispatcher.UIThread.Post(RenderIfNeeded);
}
if(obj is ClientRenderInfoMessage renderInfo)
{
lock(_lock)
{
_dpi = new Vector(renderInfo.DpiX, renderInfo.DpiY);
_invalidated = true;
}
Dispatcher.UIThread.Post(RenderIfNeeded);
}
if (obj is ClientSupportedPixelFormatsMessage supportedFormats)
{
lock (_lock)
@ -82,6 +157,84 @@ namespace Avalonia.Controls.Remote.Server
_pendingAllocation = allocated;
}
}
if(obj is PointerMovedEventMessage pointer)
{
Dispatcher.UIThread.Post(() =>
{
Input?.Invoke(new RawMouseEventArgs(
MouseDevice,
0,
InputRoot,
RawMouseEventType.Move,
new Point(pointer.X, pointer.Y),
GetAvaloniaInputModifiers(pointer.Modifiers)));
}, DispatcherPriority.Input);
}
if(obj is PointerPressedEventMessage pressed)
{
Dispatcher.UIThread.Post(() =>
{
Input?.Invoke(new RawMouseEventArgs(
MouseDevice,
0,
InputRoot,
GetAvaloniaEventType(pressed.Button, true),
new Point(pressed.X, pressed.Y),
GetAvaloniaInputModifiers(pressed.Modifiers)));
}, DispatcherPriority.Input);
}
if (obj is PointerPressedEventMessage released)
{
Dispatcher.UIThread.Post(() =>
{
Input?.Invoke(new RawMouseEventArgs(
MouseDevice,
0,
InputRoot,
GetAvaloniaEventType(released.Button, false),
new Point(released.X, released.Y),
GetAvaloniaInputModifiers(released.Modifiers)));
}, DispatcherPriority.Input);
}
if(obj is ScrollEventMessage scroll)
{
Dispatcher.UIThread.Post(() =>
{
Input?.Invoke(new RawMouseWheelEventArgs(
MouseDevice,
0,
InputRoot,
new Point(scroll.X, scroll.Y),
new Vector(scroll.DeltaX, scroll.DeltaY),
GetAvaloniaInputModifiers(scroll.Modifiers)));
}, DispatcherPriority.Input);
}
if(obj is KeyEventMessage key)
{
Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
Dispatcher.UIThread.Post(() =>
{
Input?.Invoke(new RawKeyEventArgs(
KeyboardDevice,
0,
key.IsDown ? RawKeyEventType.KeyDown : RawKeyEventType.KeyUp,
(Key)key.Key,
GetAvaloniaInputModifiers(key.Modifiers)));
}, DispatcherPriority.Input);
}
if(obj is TextInputEventMessage text)
{
Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
Dispatcher.UIThread.Post(() =>
{
Input?.Invoke(new RawTextInputEventArgs(
KeyboardDevice,
0,
text.Text));
}, DispatcherPriority.Input);
}
}
}
@ -102,6 +255,12 @@ namespace Avalonia.Controls.Remote.Server
FrameMessage RenderFrame(int width, int height, ProtocolPixelFormat? format)
{
var scalingX = _dpi.X / 96.0;
var scalingY = _dpi.Y / 96.0;
width = (int)(width * scalingX);
height = (int)(height * scalingY);
var fmt = format ?? ProtocolPixelFormat.Rgba8888;
var bpp = fmt == ProtocolPixelFormat.Rgb565 ? 2 : 4;
var data = new byte[width * height * bpp];
@ -169,5 +328,7 @@ namespace Avalonia.Controls.Remote.Server
}
public override IMouseDevice MouseDevice { get; } = new MouseDevice();
public IKeyboardDevice KeyboardDevice { get; }
}
}

378
src/Avalonia.Controls/TextBox.cs

@ -368,186 +368,242 @@ namespace Avalonia.Controls
string text = Text ?? string.Empty;
int caretIndex = CaretIndex;
bool movement = false;
bool selection = false;
bool handled = false;
var modifiers = e.Modifiers;
switch (e.Key)
{
case Key.A:
if (modifiers == InputModifiers.Control)
{
SelectAll();
handled = true;
}
break;
case Key.C:
if (modifiers == InputModifiers.Control)
{
if (!IsPasswordBox)
{
Copy();
}
handled = true;
}
break;
var keymap = AvaloniaLocator.Current.GetService<PlatformHotkeyConfiguration>();
case Key.X:
if (modifiers == InputModifiers.Control)
{
if (!IsPasswordBox)
{
Copy();
DeleteSelection();
}
handled = true;
}
break;
bool Match(List<KeyGesture> gestures) => gestures.Any(g => g.Matches(e));
bool DetectSelection() => e.Modifiers.HasFlag(keymap.SelectionModifiers);
case Key.V:
if (modifiers == InputModifiers.Control)
{
Paste();
handled = true;
}
if (Match(keymap.SelectAll))
{
SelectAll();
handled = true;
}
else if (Match(keymap.Copy))
{
if (!IsPasswordBox)
{
Copy();
}
break;
handled = true;
}
else if (Match(keymap.Cut))
{
if (!IsPasswordBox)
{
Copy();
DeleteSelection();
}
case Key.Z:
if (modifiers == InputModifiers.Control)
{
try
{
_isUndoingRedoing = true;
_undoRedoHelper.Undo();
}
finally
{
_isUndoingRedoing = false;
}
handled = true;
}
break;
case Key.Y:
if (modifiers == InputModifiers.Control)
{
try
{
_isUndoingRedoing = true;
_undoRedoHelper.Redo();
}
finally
{
_isUndoingRedoing = false;
}
handled = true;
}
break;
case Key.Left:
MoveHorizontal(-1, modifiers);
movement = true;
break;
handled = true;
}
else if (Match(keymap.Paste))
{
case Key.Right:
MoveHorizontal(1, modifiers);
movement = true;
break;
Paste();
handled = true;
}
else if (Match(keymap.Undo))
{
case Key.Up:
movement = MoveVertical(-1, modifiers);
break;
try
{
_isUndoingRedoing = true;
_undoRedoHelper.Undo();
}
finally
{
_isUndoingRedoing = false;
}
case Key.Down:
movement = MoveVertical(1, modifiers);
break;
handled = true;
}
else if (Match(keymap.Redo))
{
try
{
_isUndoingRedoing = true;
_undoRedoHelper.Redo();
}
finally
{
_isUndoingRedoing = false;
}
case Key.Home:
MoveHome(modifiers);
movement = true;
break;
handled = true;
}
else if (Match(keymap.MoveCursorToTheStartOfDocument))
{
MoveHome(true);
movement = true;
selection = false;
handled = true;
}
else if (Match(keymap.MoveCursorToTheEndOfDocument))
{
MoveEnd(true);
movement = true;
selection = false;
handled = true;
}
else if (Match(keymap.MoveCursorToTheStartOfLine))
{
MoveHome(false);
movement = true;
selection = false;
handled = true;
}
else if (Match(keymap.MoveCursorToTheEndOfLine))
{
MoveEnd(false);
movement = true;
selection = false;
handled = true;
}
else if (Match(keymap.MoveCursorToTheStartOfDocumentWithSelection))
{
MoveHome(true);
movement = true;
selection = true;
handled = true;
}
else if (Match(keymap.MoveCursorToTheEndOfDocumentWithSelection))
{
MoveEnd(true);
movement = true;
selection = true;
handled = true;
}
else if (Match(keymap.MoveCursorToTheStartOfLineWithSelection))
{
MoveHome(false);
movement = true;
selection = true;
handled = true;
}
else if (Match(keymap.MoveCursorToTheEndOfLineWithSelection))
{
MoveEnd(false);
movement = true;
selection = true;
handled = true;
}
else
{
bool hasWholeWordModifiers = modifiers.HasFlag(keymap.WholeWordTextActionModifiers);
switch (e.Key)
{
case Key.Left:
MoveHorizontal(-1, hasWholeWordModifiers);
movement = true;
selection = DetectSelection();
break;
case Key.End:
MoveEnd(modifiers);
movement = true;
break;
case Key.Right:
MoveHorizontal(1, hasWholeWordModifiers);
movement = true;
selection = DetectSelection();
break;
case Key.Back:
if (modifiers == InputModifiers.Control && SelectionStart == SelectionEnd)
{
SetSelectionForControlBackspace(modifiers);
}
case Key.Up:
movement = MoveVertical(-1);
selection = DetectSelection();
break;
if (!DeleteSelection() && CaretIndex > 0)
{
var removedCharacters = 1;
// handle deleting /r/n
// you don't ever want to leave a dangling /r around. So, if deleting /n, check to see if
// a /r should also be deleted.
if (CaretIndex > 1 &&
text[CaretIndex - 1] == '\n' &&
text[CaretIndex - 2] == '\r')
case Key.Down:
movement = MoveVertical(1);
selection = DetectSelection();
break;
case Key.Back:
if (hasWholeWordModifiers && SelectionStart == SelectionEnd)
{
removedCharacters = 2;
SetSelectionForControlBackspace();
}
SetTextInternal(text.Substring(0, caretIndex - removedCharacters) + text.Substring(caretIndex));
CaretIndex -= removedCharacters;
SelectionStart = SelectionEnd = CaretIndex;
}
handled = true;
break;
if (!DeleteSelection() && CaretIndex > 0)
{
var removedCharacters = 1;
// handle deleting /r/n
// you don't ever want to leave a dangling /r around. So, if deleting /n, check to see if
// a /r should also be deleted.
if (CaretIndex > 1 &&
text[CaretIndex - 1] == '\n' &&
text[CaretIndex - 2] == '\r')
{
removedCharacters = 2;
}
case Key.Delete:
if (modifiers == InputModifiers.Control && SelectionStart == SelectionEnd)
{
SetSelectionForControlDelete(modifiers);
}
SetTextInternal(text.Substring(0, caretIndex - removedCharacters) +
text.Substring(caretIndex));
CaretIndex -= removedCharacters;
SelectionStart = SelectionEnd = CaretIndex;
}
if (!DeleteSelection() && caretIndex < text.Length)
{
var removedCharacters = 1;
// handle deleting /r/n
// you don't ever want to leave a dangling /r around. So, if deleting /n, check to see if
// a /r should also be deleted.
if (CaretIndex < text.Length - 1 &&
text[caretIndex + 1] == '\n' &&
text[caretIndex] == '\r')
handled = true;
break;
case Key.Delete:
if (hasWholeWordModifiers && SelectionStart == SelectionEnd)
{
removedCharacters = 2;
SetSelectionForControlDelete();
}
SetTextInternal(text.Substring(0, caretIndex) + text.Substring(caretIndex + removedCharacters));
}
handled = true;
break;
if (!DeleteSelection() && caretIndex < text.Length)
{
var removedCharacters = 1;
// handle deleting /r/n
// you don't ever want to leave a dangling /r around. So, if deleting /n, check to see if
// a /r should also be deleted.
if (CaretIndex < text.Length - 1 &&
text[caretIndex + 1] == '\n' &&
text[caretIndex] == '\r')
{
removedCharacters = 2;
}
SetTextInternal(text.Substring(0, caretIndex) +
text.Substring(caretIndex + removedCharacters));
}
case Key.Enter:
if (AcceptsReturn)
{
HandleTextInput(NewLine);
handled = true;
}
break;
break;
case Key.Enter:
if (AcceptsReturn)
{
HandleTextInput(NewLine);
handled = true;
}
case Key.Tab:
if (AcceptsTab)
{
HandleTextInput("\t");
handled = true;
}
else
{
base.OnKeyDown(e);
}
break;
break;
case Key.Tab:
if (AcceptsTab)
{
HandleTextInput("\t");
handled = true;
}
else
{
base.OnKeyDown(e);
}
default:
handled = false;
break;
break;
default:
handled = false;
break;
}
}
if (movement && ((modifiers & InputModifiers.Shift) != 0))
if (movement && selection)
{
SelectionEnd = CaretIndex;
}
@ -666,12 +722,12 @@ namespace Avalonia.Controls
return result;
}
private void MoveHorizontal(int direction, InputModifiers modifiers)
private void MoveHorizontal(int direction, bool wholeWord)
{
var text = Text ?? string.Empty;
var caretIndex = CaretIndex;
if ((modifiers & InputModifiers.Control) == 0)
if (!wholeWord)
{
var index = caretIndex + direction;
@ -709,7 +765,7 @@ namespace Avalonia.Controls
}
}
private bool MoveVertical(int count, InputModifiers modifiers)
private bool MoveVertical(int count)
{
var formattedText = _presenter.FormattedText;
var lines = formattedText.GetLines().ToList();
@ -732,12 +788,12 @@ namespace Avalonia.Controls
}
}
private void MoveHome(InputModifiers modifiers)
private void MoveHome(bool document)
{
var text = Text ?? string.Empty;
var caretIndex = CaretIndex;
if ((modifiers & InputModifiers.Control) != 0)
if (document)
{
caretIndex = 0;
}
@ -762,12 +818,12 @@ namespace Avalonia.Controls
CaretIndex = caretIndex;
}
private void MoveEnd(InputModifiers modifiers)
private void MoveEnd(bool document)
{
var text = Text ?? string.Empty;
var caretIndex = CaretIndex;
if ((modifiers & InputModifiers.Control) != 0)
if (document)
{
caretIndex = text.Length;
}
@ -882,17 +938,17 @@ namespace Avalonia.Controls
}
}
private void SetSelectionForControlBackspace(InputModifiers modifiers)
private void SetSelectionForControlBackspace()
{
SelectionStart = CaretIndex;
MoveHorizontal(-1, modifiers);
MoveHorizontal(-1, true);
SelectionEnd = CaretIndex;
}
private void SetSelectionForControlDelete(InputModifiers modifiers)
private void SetSelectionForControlDelete()
{
SelectionStart = CaretIndex;
MoveHorizontal(1, modifiers);
MoveHorizontal(1, true);
SelectionEnd = CaretIndex;
}

2
src/Avalonia.DesignerSupport/DesignWindowLoader.cs

@ -18,7 +18,7 @@ namespace Avalonia.DesignerSupport
Control control;
using (PlatformManager.DesignerMode())
{
var loader = new AvaloniaXamlLoader();
var loader = new AvaloniaXamlLoader() {IsDesignMode = true};
var stream = new MemoryStream(Encoding.UTF8.GetBytes(xaml));

3
src/Avalonia.DesignerSupport/Remote/PreviewerWindowingPlatform.cs

@ -57,7 +57,8 @@ namespace Avalonia.DesignerSupport.Remote
.Bind<IRenderTimer>().ToConstant(threading)
.Bind<ISystemDialogImpl>().ToSingleton<SystemDialogsStub>()
.Bind<IWindowingPlatform>().ToConstant(instance)
.Bind<IPlatformIconLoader>().ToSingleton<IconLoaderStub>();
.Bind<IPlatformIconLoader>().ToSingleton<IconLoaderStub>()
.Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>();
}

10
src/Avalonia.DesignerSupport/Remote/RemoteDesignerEntryPoint.cs

@ -15,6 +15,8 @@ namespace Avalonia.DesignerSupport.Remote
{
private static ClientSupportedPixelFormatsMessage s_supportedPixelFormats;
private static ClientViewportAllocatedMessage s_viewportAllocatedMessage;
private static ClientRenderInfoMessage s_renderInfoMessage;
private static IAvaloniaRemoteTransportConnection s_transport;
class CommandLineArgs
{
@ -161,7 +163,8 @@ namespace Avalonia.DesignerSupport.Remote
PreviewerWindowingPlatform.PreFlightMessages = new List<object>
{
s_supportedPixelFormats,
s_viewportAllocatedMessage
s_viewportAllocatedMessage,
s_renderInfoMessage
};
}
@ -173,6 +176,11 @@ namespace Avalonia.DesignerSupport.Remote
s_supportedPixelFormats = formats;
RebuildPreFlight();
}
if (obj is ClientRenderInfoMessage renderInfo)
{
s_renderInfoMessage = renderInfo;
RebuildPreFlight();
}
if (obj is ClientViewportAllocatedMessage viewport)
{
s_viewportAllocatedMessage = viewport;

4
src/Avalonia.DesignerSupport/Remote/Stubs.cs

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reactive.Disposables;
@ -20,7 +20,7 @@ namespace Avalonia.DesignerSupport.Remote
public IPlatformHandle Handle { get; }
public Size MaxClientSize { get; }
public Size ClientSize { get; }
public double Scaling { get; }
public double Scaling { get; } = 1.0;
public IEnumerable<object> Surfaces { get; }
public Action<RawInputEventArgs> Input { get; set; }
public Action<Rect> Paint { get; set; }

18
src/Avalonia.Input/Key.cs

@ -1020,5 +1020,23 @@ namespace Avalonia.Input
/// The key is used with another key to create a single combined character.
/// </summary>
DeadCharProcessed = 172,
/// <summary>
/// OSX Platform-specific Fn+Left key
/// </summary>
FnLeftArrow = 10001,
/// <summary>
/// OSX Platform-specific Fn+Right key
/// </summary>
FnRightArrow = 10002,
/// <summary>
/// OSX Platform-specific Fn+Up key
/// </summary>
FnUpArrow = 10003,
/// <summary>
/// OSX Platform-specific Fn+Down key
/// </summary>
FnDownArrow = 10004,
}
}

11
src/Avalonia.Input/KeyGesture.cs

@ -6,6 +6,17 @@ namespace Avalonia.Input
{
public sealed class KeyGesture : IEquatable<KeyGesture>
{
public KeyGesture()
{
}
public KeyGesture(Key key, InputModifiers modifiers = InputModifiers.None)
{
Key = key;
Modifiers = modifiers;
}
public bool Equals(KeyGesture other)
{
if (ReferenceEquals(null, other)) return false;

98
src/Avalonia.Input/Platform/PlatformHotkeyConfiguration.cs

@ -0,0 +1,98 @@
using System.Collections.Generic;
namespace Avalonia.Input.Platform
{
public class PlatformHotkeyConfiguration
{
public PlatformHotkeyConfiguration() : this(InputModifiers.Control)
{
}
public PlatformHotkeyConfiguration(InputModifiers commandModifiers,
InputModifiers selectionModifiers = InputModifiers.Shift,
InputModifiers wholeWordTextActionModifiers = InputModifiers.Control)
{
CommandModifiers = commandModifiers;
SelectionModifiers = selectionModifiers;
WholeWordTextActionModifiers = wholeWordTextActionModifiers;
Copy = new List<KeyGesture>
{
new KeyGesture(Key.C, commandModifiers)
};
Cut = new List<KeyGesture>
{
new KeyGesture(Key.X, commandModifiers)
};
Paste = new List<KeyGesture>
{
new KeyGesture(Key.V, commandModifiers)
};
Undo = new List<KeyGesture>
{
new KeyGesture(Key.Z, commandModifiers)
};
Redo = new List<KeyGesture>
{
new KeyGesture(Key.Y, commandModifiers),
new KeyGesture(Key.Z, commandModifiers | selectionModifiers)
};
SelectAll = new List<KeyGesture>
{
new KeyGesture(Key.A, commandModifiers)
};
MoveCursorToTheStartOfLine = new List<KeyGesture>
{
new KeyGesture(Key.Home)
};
MoveCursorToTheEndOfLine = new List<KeyGesture>
{
new KeyGesture(Key.End)
};
MoveCursorToTheStartOfDocument = new List<KeyGesture>
{
new KeyGesture(Key.Home, commandModifiers)
};
MoveCursorToTheEndOfDocument = new List<KeyGesture>
{
new KeyGesture(Key.End, commandModifiers)
};
MoveCursorToTheStartOfLineWithSelection = new List<KeyGesture>
{
new KeyGesture(Key.Home, selectionModifiers)
};
MoveCursorToTheEndOfLineWithSelection = new List<KeyGesture>
{
new KeyGesture(Key.End, selectionModifiers)
};
MoveCursorToTheStartOfDocumentWithSelection = new List<KeyGesture>
{
new KeyGesture(Key.Home, commandModifiers | selectionModifiers)
};
MoveCursorToTheEndOfDocumentWithSelection = new List<KeyGesture>
{
new KeyGesture(Key.End, commandModifiers | selectionModifiers)
};
}
public InputModifiers CommandModifiers { get; set; }
public InputModifiers WholeWordTextActionModifiers { get; set; }
public InputModifiers SelectionModifiers { get; set; }
public List<KeyGesture> Copy { get; set; }
public List<KeyGesture> Cut { get; set; }
public List<KeyGesture> Paste { get; set; }
public List<KeyGesture> Undo { get; set; }
public List<KeyGesture> Redo { get; set; }
public List<KeyGesture> SelectAll { get; set; }
public List<KeyGesture> MoveCursorToTheStartOfLine { get; set; }
public List<KeyGesture> MoveCursorToTheEndOfLine { get; set; }
public List<KeyGesture> MoveCursorToTheStartOfDocument { get; set; }
public List<KeyGesture> MoveCursorToTheEndOfDocument { get; set; }
public List<KeyGesture> MoveCursorToTheStartOfLineWithSelection { get; set; }
public List<KeyGesture> MoveCursorToTheEndOfLineWithSelection { get; set; }
public List<KeyGesture> MoveCursorToTheStartOfDocumentWithSelection { get; set; }
public List<KeyGesture> MoveCursorToTheEndOfDocumentWithSelection { get; set; }
}
}

4
src/Avalonia.Native.OSX/.gitignore

@ -0,0 +1,4 @@
build
Avalonia.Native.OSX.xcodeproj/xcuserdata
Avalonia.Native.OSX.xcodeproj/project.xcworkspace/xcuserdata

328
src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj

@ -0,0 +1,328 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
37A517B32159597E00FBA241 /* Screens.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37A517B22159597E00FBA241 /* Screens.mm */; };
37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C09D8721580FE4006A6758 /* SystemDialogs.mm */; };
37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37E2330E21583241000CB7E2 /* KeyTransform.mm */; };
5B21A982216530F500CEE36E /* cursor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B21A981216530F500CEE36E /* cursor.mm */; };
5B8BD94F215BFEA6005ED2A7 /* clipboard.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */; };
AB00E4F72147CA920032A60A /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB00E4F62147CA920032A60A /* main.mm */; };
AB1E522C217613570091CD71 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB1E522B217613570091CD71 /* OpenGL.framework */; };
AB573DC4217605E400D389A2 /* gl.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB573DC3217605E400D389A2 /* gl.mm */; };
AB661C1E2148230F00291242 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB661C1D2148230F00291242 /* AppKit.framework */; };
AB661C202148286E00291242 /* window.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB661C1F2148286E00291242 /* window.mm */; };
AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
379860FE214DA0C000CD0246 /* KeyTransform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyTransform.h; sourceTree = "<group>"; };
37A4E71A2178846A00EACBCD /* headers */ = {isa = PBXFileReference; lastKnownFileType = folder; name = headers; path = ../Avalonia.Native/headers; sourceTree = "<group>"; };
37A517B22159597E00FBA241 /* Screens.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = Screens.mm; sourceTree = "<group>"; };
37C09D8721580FE4006A6758 /* SystemDialogs.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SystemDialogs.mm; sourceTree = "<group>"; };
37C09D8A21581EF2006A6758 /* window.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = window.h; sourceTree = "<group>"; };
37E2330E21583241000CB7E2 /* KeyTransform.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = KeyTransform.mm; sourceTree = "<group>"; };
5B21A981216530F500CEE36E /* cursor.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = cursor.mm; sourceTree = "<group>"; };
5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = clipboard.mm; sourceTree = "<group>"; };
5BF943652167AD1D009CAE35 /* cursor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cursor.h; sourceTree = "<group>"; };
AB00E4F62147CA920032A60A /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = "<group>"; };
AB1E522B217613570091CD71 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; };
AB573DC3217605E400D389A2 /* gl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = gl.mm; sourceTree = "<group>"; };
AB661C1D2148230F00291242 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
AB661C1F2148286E00291242 /* window.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = window.mm; sourceTree = "<group>"; };
AB661C212148288600291242 /* common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = common.h; sourceTree = "<group>"; };
AB7A61EF2147C815003C5833 /* libAvalonia.Native.OSX.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libAvalonia.Native.OSX.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = platformthreading.mm; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
AB7A61EC2147C814003C5833 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
AB1E522C217613570091CD71 /* OpenGL.framework in Frameworks */,
AB661C1E2148230F00291242 /* AppKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
AB661C1C2148230E00291242 /* Frameworks */ = {
isa = PBXGroup;
children = (
AB1E522B217613570091CD71 /* OpenGL.framework */,
AB661C1D2148230F00291242 /* AppKit.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
AB7A61E62147C814003C5833 = {
isa = PBXGroup;
children = (
37A4E71A2178846A00EACBCD /* headers */,
AB573DC3217605E400D389A2 /* gl.mm */,
5BF943652167AD1D009CAE35 /* cursor.h */,
5B21A981216530F500CEE36E /* cursor.mm */,
5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */,
AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */,
AB661C212148288600291242 /* common.h */,
379860FE214DA0C000CD0246 /* KeyTransform.h */,
37E2330E21583241000CB7E2 /* KeyTransform.mm */,
AB661C1F2148286E00291242 /* window.mm */,
37C09D8A21581EF2006A6758 /* window.h */,
AB00E4F62147CA920032A60A /* main.mm */,
37A517B22159597E00FBA241 /* Screens.mm */,
37C09D8721580FE4006A6758 /* SystemDialogs.mm */,
AB7A61F02147C815003C5833 /* Products */,
AB661C1C2148230E00291242 /* Frameworks */,
);
sourceTree = "<group>";
};
AB7A61F02147C815003C5833 /* Products */ = {
isa = PBXGroup;
children = (
AB7A61EF2147C815003C5833 /* libAvalonia.Native.OSX.dylib */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
AB7A61ED2147C814003C5833 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
AB7A61EE2147C814003C5833 /* Avalonia.Native.OSX */ = {
isa = PBXNativeTarget;
buildConfigurationList = AB7A61F82147C815003C5833 /* Build configuration list for PBXNativeTarget "Avalonia.Native.OSX" */;
buildPhases = (
AB7A61EB2147C814003C5833 /* Sources */,
AB7A61EC2147C814003C5833 /* Frameworks */,
AB7A61ED2147C814003C5833 /* Headers */,
);
buildRules = (
);
dependencies = (
);
name = Avalonia.Native.OSX;
productName = Avalonia.Native.OSX;
productReference = AB7A61EF2147C815003C5833 /* libAvalonia.Native.OSX.dylib */;
productType = "com.apple.product-type.library.dynamic";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
AB7A61E72147C814003C5833 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1000;
ORGANIZATIONNAME = Avalonia;
TargetAttributes = {
AB7A61EE2147C814003C5833 = {
CreatedOnToolsVersion = 8.3.2;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = AB7A61EA2147C814003C5833 /* Build configuration list for PBXProject "Avalonia.Native.OSX" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = AB7A61E62147C814003C5833;
productRefGroup = AB7A61F02147C815003C5833 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
AB7A61EE2147C814003C5833 /* Avalonia.Native.OSX */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
AB7A61EB2147C814003C5833 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5B8BD94F215BFEA6005ED2A7 /* clipboard.mm in Sources */,
5B21A982216530F500CEE36E /* cursor.mm in Sources */,
AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */,
37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */,
37A517B32159597E00FBA241 /* Screens.mm in Sources */,
AB00E4F72147CA920032A60A /* main.mm in Sources */,
37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */,
AB573DC4217605E400D389A2 /* gl.mm in Sources */,
AB661C202148286E00291242 /* window.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
AB7A61F62147C815003C5833 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.12;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
};
name = Debug;
};
AB7A61F72147C815003C5833 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.12;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
};
name = Release;
};
AB7A61F92147C815003C5833 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
EXECUTABLE_PREFIX = lib;
HEADER_SEARCH_PATHS = ../Avalonia.Native/headers;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
AB7A61FA2147C815003C5833 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
EXECUTABLE_PREFIX = lib;
HEADER_SEARCH_PATHS = ../Avalonia.Native/headers;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
AB7A61EA2147C814003C5833 /* Build configuration list for PBXProject "Avalonia.Native.OSX" */ = {
isa = XCConfigurationList;
buildConfigurations = (
AB7A61F62147C815003C5833 /* Debug */,
AB7A61F72147C815003C5833 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
AB7A61F82147C815003C5833 /* Build configuration list for PBXNativeTarget "Avalonia.Native.OSX" */ = {
isa = XCConfigurationList;
buildConfigurations = (
AB7A61F92147C815003C5833 /* Debug */,
AB7A61FA2147C815003C5833 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = AB7A61E72147C814003C5833 /* Project object */;
}

7
src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/project.xcworkspace/contents.xcworkspacedata

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:Avalonia.Native.OSX.xcodeproj">
</FileRef>
</Workspace>

91
src/Avalonia.Native.OSX/Avalonia.Native.OSX.xcodeproj/xcshareddata/xcschemes/Avalonia.Native.OSX.xcscheme

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1000"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AB7A61EE2147C814003C5833"
BuildableName = "libAvalonia.Native.OSX.dylib"
BlueprintName = "Avalonia.Native.OSX"
ReferencedContainer = "container:Avalonia.Native.OSX.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "YES"
customWorkingDirectory = "$PROJECT_DIR/../../samples/ControlCatalog"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<PathRunnable
runnableDebuggingMode = "0"
FilePath = "/usr/local/share/dotnet/dotnet">
</PathRunnable>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AB7A61EE2147C814003C5833"
BuildableName = "libAvalonia.Native.OSX.dylib"
BlueprintName = "Avalonia.Native.OSX"
ReferencedContainer = "container:Avalonia.Native.OSX.xcodeproj">
</BuildableReference>
</MacroExpansion>
<CommandLineArguments>
<CommandLineArgument
argument = "bin/Debug/netcoreapp2.0/ControlCatalog.dll"
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "AB7A61EE2147C814003C5833"
BuildableName = "libAvalonia.Native.OSX.dylib"
BlueprintName = "Avalonia.Native.OSX"
ReferencedContainer = "container:Avalonia.Native.OSX.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

12
src/Avalonia.Native.OSX/KeyTransform.h

@ -0,0 +1,12 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
#ifndef keytransform_h
#define keytransform_h
#include "common.h"
#include "key.h"
#include <map>
extern std::map<int, AvnKey> s_KeyMap;
#endif

241
src/Avalonia.Native.OSX/KeyTransform.mm

@ -0,0 +1,241 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
#include "KeyTransform.h"
const int kVK_ANSI_A = 0x00;
const int kVK_ANSI_S = 0x01;
const int kVK_ANSI_D = 0x02;
const int kVK_ANSI_F = 0x03;
const int kVK_ANSI_H = 0x04;
const int kVK_ANSI_G = 0x05;
const int kVK_ANSI_Z = 0x06;
const int kVK_ANSI_X = 0x07;
const int kVK_ANSI_C = 0x08;
const int kVK_ANSI_V = 0x09;
const int kVK_ANSI_B = 0x0B;
const int kVK_ANSI_Q = 0x0C;
const int kVK_ANSI_W = 0x0D;
const int kVK_ANSI_E = 0x0E;
const int kVK_ANSI_R = 0x0F;
const int kVK_ANSI_Y = 0x10;
const int kVK_ANSI_T = 0x11;
const int kVK_ANSI_1 = 0x12;
const int kVK_ANSI_2 = 0x13;
const int kVK_ANSI_3 = 0x14;
const int kVK_ANSI_4 = 0x15;
const int kVK_ANSI_6 = 0x16;
const int kVK_ANSI_5 = 0x17;
//const int kVK_ANSI_Equal = 0x18;
const int kVK_ANSI_9 = 0x19;
const int kVK_ANSI_7 = 0x1A;
const int kVK_ANSI_Minus = 0x1B;
const int kVK_ANSI_8 = 0x1C;
const int kVK_ANSI_0 = 0x1D;
const int kVK_ANSI_RightBracket = 0x1E;
const int kVK_ANSI_O = 0x1F;
const int kVK_ANSI_U = 0x20;
const int kVK_ANSI_LeftBracket = 0x21;
const int kVK_ANSI_I = 0x22;
const int kVK_ANSI_P = 0x23;
const int kVK_ANSI_L = 0x25;
const int kVK_ANSI_J = 0x26;
const int kVK_ANSI_Quote = 0x27;
const int kVK_ANSI_K = 0x28;
const int kVK_ANSI_Semicolon = 0x29;
const int kVK_ANSI_Backslash = 0x2A;
const int kVK_ANSI_Comma = 0x2B;
//const int kVK_ANSI_Slash = 0x2C;
const int kVK_ANSI_N = 0x2D;
const int kVK_ANSI_M = 0x2E;
const int kVK_ANSI_Period = 0x2F;
//const int kVK_ANSI_Grave = 0x32;
const int kVK_ANSI_KeypadDecimal = 0x41;
const int kVK_ANSI_KeypadMultiply = 0x43;
const int kVK_ANSI_KeypadPlus = 0x45;
const int kVK_ANSI_KeypadClear = 0x47;
const int kVK_ANSI_KeypadDivide = 0x4B;
const int kVK_ANSI_KeypadEnter = 0x4C;
const int kVK_ANSI_KeypadMinus = 0x4E;
//const int kVK_ANSI_KeypadEquals = 0x51;
const int kVK_ANSI_Keypad0 = 0x52;
const int kVK_ANSI_Keypad1 = 0x53;
const int kVK_ANSI_Keypad2 = 0x54;
const int kVK_ANSI_Keypad3 = 0x55;
const int kVK_ANSI_Keypad4 = 0x56;
const int kVK_ANSI_Keypad5 = 0x57;
const int kVK_ANSI_Keypad6 = 0x58;
const int kVK_ANSI_Keypad7 = 0x59;
const int kVK_ANSI_Keypad8 = 0x5B;
const int kVK_ANSI_Keypad9 = 0x5C;
const int kVK_Return = 0x24;
const int kVK_Tab = 0x30;
const int kVK_Space = 0x31;
const int kVK_Delete = 0x33;
const int kVK_Escape = 0x35;
const int kVK_Command = 0x37;
const int kVK_Shift = 0x38;
const int kVK_CapsLock = 0x39;
const int kVK_Option = 0x3A;
const int kVK_Control = 0x3B;
const int kVK_RightCommand = 0x36;
const int kVK_RightShift = 0x3C;
const int kVK_RightOption = 0x3D;
const int kVK_RightControl = 0x3E;
//const int kVK_Function = 0x3F;
const int kVK_F17 = 0x40;
const int kVK_VolumeUp = 0x48;
const int kVK_VolumeDown = 0x49;
const int kVK_Mute = 0x4A;
const int kVK_F18 = 0x4F;
const int kVK_F19 = 0x50;
const int kVK_F20 = 0x5A;
const int kVK_F5 = 0x60;
const int kVK_F6 = 0x61;
const int kVK_F7 = 0x62;
const int kVK_F3 = 0x63;
const int kVK_F8 = 0x64;
const int kVK_F9 = 0x65;
const int kVK_F11 = 0x67;
const int kVK_F13 = 0x69;
const int kVK_F16 = 0x6A;
const int kVK_F14 = 0x6B;
const int kVK_F10 = 0x6D;
const int kVK_F12 = 0x6F;
const int kVK_F15 = 0x71;
const int kVK_Help = 0x72;
const int kVK_Home = 0x73;
const int kVK_PageUp = 0x74;
const int kVK_ForwardDelete = 0x75;
const int kVK_F4 = 0x76;
const int kVK_End = 0x77;
const int kVK_F2 = 0x78;
const int kVK_PageDown = 0x79;
const int kVK_F1 = 0x7A;
const int kVK_LeftArrow = 0x7B;
const int kVK_RightArrow = 0x7C;
const int kVK_DownArrow = 0x7D;
const int kVK_UpArrow = 0x7E;
//const int kVK_ISO_Section = 0x0A;
//const int kVK_JIS_Yen = 0x5D;
//const int kVK_JIS_Underscore = 0x5E;
//const int kVK_JIS_KeypadComma = 0x5F;
//const int kVK_JIS_Eisu = 0x66;
//const int kVK_JIS_Kana = 0x68;
std::map<int, AvnKey> s_KeyMap =
{
{kVK_ANSI_A, A},
{kVK_ANSI_S, S},
{kVK_ANSI_D, D},
{kVK_ANSI_F, F},
{kVK_ANSI_H, H},
{kVK_ANSI_G, G},
{kVK_ANSI_Z, Z},
{kVK_ANSI_X, X},
{kVK_ANSI_C, C},
{kVK_ANSI_V, V},
{kVK_ANSI_B, B},
{kVK_ANSI_Q, Q},
{kVK_ANSI_W, W},
{kVK_ANSI_E, E},
{kVK_ANSI_R, R},
{kVK_ANSI_Y, Y},
{kVK_ANSI_T, T},
{kVK_ANSI_1, D1},
{kVK_ANSI_2, D2},
{kVK_ANSI_3, D3},
{kVK_ANSI_4, D4},
{kVK_ANSI_6, D6},
{kVK_ANSI_5, D5},
//{kVK_ANSI_Equal, ?},
{kVK_ANSI_9, D9},
{kVK_ANSI_7, D7},
{kVK_ANSI_Minus, OemMinus},
{kVK_ANSI_8, D8},
{kVK_ANSI_0, D0},
{kVK_ANSI_RightBracket, OemCloseBrackets},
{kVK_ANSI_O, O},
{kVK_ANSI_U, U},
{kVK_ANSI_LeftBracket, OemOpenBrackets},
{kVK_ANSI_I, I},
{kVK_ANSI_P, P},
{kVK_ANSI_L, L},
{kVK_ANSI_J, J},
{kVK_ANSI_Quote, OemQuotes},
{kVK_ANSI_K, AvnKeyK},
{kVK_ANSI_Semicolon, OemSemicolon},
{kVK_ANSI_Backslash, OemBackslash},
{kVK_ANSI_Comma, OemComma},
//{kVK_ANSI_Slash, ?},
{kVK_ANSI_N, N},
{kVK_ANSI_M, M},
{kVK_ANSI_Period, OemPeriod},
//{kVK_ANSI_Grave, ?},
{kVK_ANSI_KeypadDecimal, Decimal},
{kVK_ANSI_KeypadMultiply, Multiply},
{kVK_ANSI_KeypadPlus, OemPlus},
{kVK_ANSI_KeypadClear, AvnKeyClear},
{kVK_ANSI_KeypadDivide, Divide},
{kVK_ANSI_KeypadEnter, AvnKeyEnter},
{kVK_ANSI_KeypadMinus, OemMinus},
//{kVK_ANSI_KeypadEquals, ?},
{kVK_ANSI_Keypad0, NumPad0},
{kVK_ANSI_Keypad1, NumPad1},
{kVK_ANSI_Keypad2, NumPad2},
{kVK_ANSI_Keypad3, NumPad3},
{kVK_ANSI_Keypad4, NumPad4},
{kVK_ANSI_Keypad5, NumPad5},
{kVK_ANSI_Keypad6, NumPad6},
{kVK_ANSI_Keypad7, NumPad7},
{kVK_ANSI_Keypad8, NumPad8},
{kVK_ANSI_Keypad9, NumPad9},
{kVK_Return, AvnKeyReturn},
{kVK_Tab, AvnKeyTab},
{kVK_Space, Space},
{kVK_Delete, AvnKeyBack},
{kVK_Escape, Escape},
{kVK_Command, LWin},
{kVK_Shift, LeftShift},
{kVK_CapsLock, AvnKeyCapsLock},
{kVK_Option, LeftAlt},
{kVK_Control, LeftCtrl},
{kVK_RightCommand, RWin},
{kVK_RightShift, RightShift},
{kVK_RightOption, RightAlt},
{kVK_RightControl, RightCtrl},
//{kVK_Function, ?},
{kVK_F17, F17},
{kVK_VolumeUp, VolumeUp},
{kVK_VolumeDown, VolumeDown},
{kVK_Mute, VolumeMute},
{kVK_F18, F18},
{kVK_F19, F19},
{kVK_F20, F20},
{kVK_F5, F5},
{kVK_F6, F6},
{kVK_F7, F7},
{kVK_F3, F3},
{kVK_F8, F8},
{kVK_F9, F9},
{kVK_F11, F11},
{kVK_F13, F13},
{kVK_F16, F16},
{kVK_F14, F14},
{kVK_F10, F10},
{kVK_F12, F12},
{kVK_F15, F15},
{kVK_Help, Help},
{kVK_Home, Home},
{kVK_PageUp, PageUp},
{kVK_ForwardDelete, Delete},
{kVK_F4, F4},
{kVK_End, End},
{kVK_F2, F2},
{kVK_PageDown, PageDown},
{kVK_F1, F1},
{kVK_LeftArrow, Left},
{kVK_RightArrow, Right},
{kVK_DownArrow, Down},
{kVK_UpArrow, Up}
};

51
src/Avalonia.Native.OSX/Screens.mm

@ -0,0 +1,51 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
#include "common.h"
class Screens : public ComSingleObject<IAvnScreens, &IID_IAvnScreens>
{
public:
FORWARD_IUNKNOWN()
virtual HRESULT GetScreenCount (int* ret)
{
@autoreleasepool
{
*ret = (int)[NSScreen screens].count;
return S_OK;
}
}
virtual HRESULT GetScreen (int index, AvnScreen* ret)
{
@autoreleasepool
{
if(index < 0 || index >= [NSScreen screens].count)
{
return E_INVALIDARG;
}
auto screen = [[NSScreen screens] objectAtIndex:index];
ret->Bounds.X = [screen frame].origin.x;
ret->Bounds.Y = [screen frame].origin.y;
ret->Bounds.Height = [screen frame].size.height;
ret->Bounds.Width = [screen frame].size.width;
ret->WorkingArea.X = [screen visibleFrame].origin.x;
ret->WorkingArea.Y = [screen visibleFrame].origin.y;
ret->WorkingArea.Height = [screen visibleFrame].size.height;
ret->WorkingArea.Width = [screen visibleFrame].size.width;
ret->Primary = index == 0;
return S_OK;
}
}
};
extern IAvnScreens* CreateScreens()
{
return new Screens();
}

262
src/Avalonia.Native.OSX/SystemDialogs.mm

@ -0,0 +1,262 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
#include "common.h"
#include "window.h"
class SystemDialogs : public ComSingleObject<IAvnSystemDialogs, &IID_IAvnSystemDialogs>
{
public:
FORWARD_IUNKNOWN()
virtual void SelectFolderDialog (IAvnWindow* parentWindowHandle,
IAvnSystemDialogEvents* events,
const char* title,
const char* initialDirectory)
{
@autoreleasepool
{
auto panel = [NSOpenPanel openPanel];
panel.canChooseDirectories = true;
panel.canCreateDirectories = true;
panel.canChooseFiles = false;
if(title != nullptr)
{
panel.title = [NSString stringWithUTF8String:title];
}
if(initialDirectory != nullptr)
{
auto directoryString = [NSString stringWithUTF8String:initialDirectory];
panel.directoryURL = [NSURL fileURLWithPath:directoryString];
}
auto handler = ^(NSModalResponse result) {
if(result == NSFileHandlingPanelOKButton)
{
auto urls = [panel URLs];
if(urls.count > 0)
{
void* strings[urls.count];
for(int i = 0; i < urls.count; i++)
{
auto url = [urls objectAtIndex:i];
auto string = [url absoluteString];
string = [string substringFromIndex:7];
strings[i] = (void*)[string UTF8String];
}
events->OnCompleted((int)urls.count, &strings[0]);
[panel orderOut:panel];
if(parentWindowHandle != nullptr)
{
auto windowHolder = dynamic_cast<INSWindowHolder*>(parentWindowHandle);
[windowHolder->GetNSWindow() makeKeyAndOrderFront:windowHolder->GetNSWindow()];
}
return;
}
}
events->OnCompleted(0, nullptr);
};
if(parentWindowHandle != nullptr)
{
auto windowBase = dynamic_cast<INSWindowHolder*>(parentWindowHandle);
[panel beginSheetModalForWindow:windowBase->GetNSWindow() completionHandler:handler];
}
else
{
[panel beginWithCompletionHandler: handler];
}
}
}
virtual void OpenFileDialog (IAvnWindow* parentWindowHandle,
IAvnSystemDialogEvents* events,
bool allowMultiple,
const char* title,
const char* initialDirectory,
const char* initialFile,
const char* filters)
{
@autoreleasepool
{
auto panel = [NSOpenPanel openPanel];
panel.allowsMultipleSelection = allowMultiple;
if(title != nullptr)
{
panel.title = [NSString stringWithUTF8String:title];
}
if(initialDirectory != nullptr)
{
auto directoryString = [NSString stringWithUTF8String:initialDirectory];
panel.directoryURL = [NSURL fileURLWithPath:directoryString];
}
if(initialFile != nullptr)
{
panel.nameFieldStringValue = [NSString stringWithUTF8String:initialFile];
}
if(filters != nullptr)
{
auto filtersString = [NSString stringWithUTF8String:filters];
if(filtersString.length > 0)
{
auto allowedTypes = [filtersString componentsSeparatedByString:@";"];
panel.allowedFileTypes = allowedTypes;
}
}
auto handler = ^(NSModalResponse result) {
if(result == NSFileHandlingPanelOKButton)
{
auto urls = [panel URLs];
if(urls.count > 0)
{
void* strings[urls.count];
for(int i = 0; i < urls.count; i++)
{
auto url = [urls objectAtIndex:i];
auto string = [url absoluteString];
string = [string substringFromIndex:7];
strings[i] = (void*)[string UTF8String];
}
events->OnCompleted((int)urls.count, &strings[0]);
[panel orderOut:panel];
if(parentWindowHandle != nullptr)
{
auto windowHolder = dynamic_cast<INSWindowHolder*>(parentWindowHandle);
[windowHolder->GetNSWindow() makeKeyAndOrderFront:windowHolder->GetNSWindow()];
}
return;
}
}
events->OnCompleted(0, nullptr);
};
if(parentWindowHandle != nullptr)
{
auto windowHolder = dynamic_cast<INSWindowHolder*>(parentWindowHandle);
[panel beginSheetModalForWindow:windowHolder->GetNSWindow() completionHandler:handler];
}
else
{
[panel beginWithCompletionHandler: handler];
}
}
}
virtual void SaveFileDialog (IAvnWindow* parentWindowHandle,
IAvnSystemDialogEvents* events,
const char* title,
const char* initialDirectory,
const char* initialFile,
const char* filters)
{
@autoreleasepool
{
auto panel = [NSSavePanel savePanel];
if(title != nullptr)
{
panel.title = [NSString stringWithUTF8String:title];
}
if(initialDirectory != nullptr)
{
auto directoryString = [NSString stringWithUTF8String:initialDirectory];
panel.directoryURL = [NSURL fileURLWithPath:directoryString];
}
if(initialFile != nullptr)
{
panel.nameFieldStringValue = [NSString stringWithUTF8String:initialFile];
}
if(filters != nullptr)
{
auto filtersString = [NSString stringWithUTF8String:filters];
if(filtersString.length > 0)
{
auto allowedTypes = [filtersString componentsSeparatedByString:@";"];
panel.allowedFileTypes = allowedTypes;
}
}
auto handler = ^(NSModalResponse result) {
if(result == NSFileHandlingPanelOKButton)
{
void* strings[1];
auto url = [panel URL];
auto string = [url absoluteString];
string = [string substringFromIndex:7];
strings[0] = (void*)[string UTF8String];
events->OnCompleted(1, &strings[0]);
[panel orderOut:panel];
if(parentWindowHandle != nullptr)
{
auto windowHolder = dynamic_cast<INSWindowHolder*>(parentWindowHandle);
[windowHolder->GetNSWindow() makeKeyAndOrderFront:windowHolder->GetNSWindow()];
}
return;
}
events->OnCompleted(0, nullptr);
};
if(parentWindowHandle != nullptr)
{
auto windowBase = dynamic_cast<INSWindowHolder*>(parentWindowHandle);
[panel beginSheetModalForWindow:windowBase->GetNSWindow() completionHandler:handler];
}
else
{
[panel beginWithCompletionHandler: handler];
}
}
}
};
extern IAvnSystemDialogs* CreateSystemDialogs()
{
return new SystemDialogs();
}

47
src/Avalonia.Native.OSX/clipboard.mm

@ -0,0 +1,47 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
#include "common.h"
class Clipboard : public ComSingleObject<IAvnClipboard, &IID_IAvnClipboard>
{
public:
FORWARD_IUNKNOWN()
virtual HRESULT GetText (void** retOut)
{
@autoreleasepool
{
NSString *str = [[NSPasteboard generalPasteboard] stringForType:NSPasteboardTypeString];
*retOut = (void *)str.UTF8String;
}
return S_OK;
}
virtual HRESULT SetText (char* text)
{
@autoreleasepool
{
NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
[pasteBoard clearContents];
[pasteBoard setString:@(text) forType:NSPasteboardTypeString];
}
return S_OK;
}
virtual HRESULT Clear()
{
@autoreleasepool
{
[[NSPasteboard generalPasteboard] clearContents];
}
return S_OK;
}
};
extern IAvnClipboard* CreateClipboard()
{
return new Clipboard();
}

34
src/Avalonia.Native.OSX/common.h

@ -0,0 +1,34 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
#ifndef common_h
#define common_h
#include "comimpl.h"
#include "avalonia-native.h"
#include <stdio.h>
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#include <pthread.h>
extern IAvnPlatformThreadingInterface* CreatePlatformThreading();
extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events);
extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events);
extern IAvnSystemDialogs* CreateSystemDialogs();
extern IAvnScreens* CreateScreens();
extern IAvnClipboard* CreateClipboard();
extern IAvnCursorFactory* CreateCursorFactory();
extern IAvnGlFeature* GetGlFeature();
extern IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(NSWindow* window, NSView* view);
extern NSPoint ToNSPoint (AvnPoint p);
extern AvnPoint ToAvnPoint (NSPoint p);
extern AvnPoint ConvertPointY (AvnPoint p);
extern NSSize ToNSSize (AvnSize s);
#ifdef DEBUG
#define NSDebugLog(...) NSLog(__VA_ARGS__)
#else
#define NSDebugLog(...) (void)0
#endif
#endif

29
src/Avalonia.Native.OSX/cursor.h

@ -0,0 +1,29 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
#ifndef cursor_h
#define cursor_h
#include "common.h"
#include <map>
class Cursor : public ComSingleObject<IAvnCursor, &IID_IAvnCursor>
{
private:
NSCursor * _native;
public:
FORWARD_IUNKNOWN()
Cursor(NSCursor * cursor)
{
_native = cursor;
}
NSCursor* GetNative()
{
return _native;
}
};
extern std::map<AvnStandardCursorType, Cursor*> s_cursorMap;
#endif /* cursor_h */

73
src/Avalonia.Native.OSX/cursor.mm

@ -0,0 +1,73 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
#include "common.h"
#include "cursor.h"
#include <map>
class CursorFactory : public ComSingleObject<IAvnCursorFactory, &IID_IAvnCursorFactory>
{
Cursor* arrowCursor = new Cursor([NSCursor arrowCursor]);
Cursor* crossCursor = new Cursor([NSCursor crosshairCursor]);
Cursor* resizeUpCursor = new Cursor([NSCursor resizeUpCursor]);
Cursor* resizeDownCursor = new Cursor([NSCursor resizeDownCursor]);
Cursor* resizeUpDownCursor = new Cursor([NSCursor resizeUpDownCursor]);
Cursor* dragCopyCursor = new Cursor([NSCursor dragCopyCursor]);
Cursor* dragLinkCursor = new Cursor([NSCursor dragLinkCursor]);
Cursor* pointingHandCursor = new Cursor([NSCursor pointingHandCursor]);
Cursor* contextualMenuCursor = new Cursor([NSCursor contextualMenuCursor]);
Cursor* IBeamCursor = new Cursor([NSCursor IBeamCursor]);
Cursor* resizeLeftCursor = new Cursor([NSCursor resizeLeftCursor]);
Cursor* resizeRightCursor = new Cursor([NSCursor resizeRightCursor]);
Cursor* resizeWestEastCursor = new Cursor([NSCursor resizeLeftRightCursor]);
Cursor* operationNotAllowedCursor = new Cursor([NSCursor operationNotAllowedCursor]);
std::map<AvnStandardCursorType, Cursor*> s_cursorMap =
{
{ CursorArrow, arrowCursor },
{ CursorAppStarting, arrowCursor },
{ CursorWait, arrowCursor },
{ CursorTopLeftCorner, crossCursor },
{ CursorTopRightCorner, crossCursor },
{ CursorBottomLeftCorner, crossCursor },
{ CursorBottomRightCorner, crossCursor },
{ CursorCross, crossCursor },
{ CursorSizeAll, crossCursor },
{ CursorSizeNorthSouth, resizeUpDownCursor},
{ CursorSizeWestEast, resizeWestEastCursor},
{ CursorTopSide, resizeUpCursor },
{ CursorUpArrow, resizeUpCursor },
{ CursorBottomSize, resizeDownCursor },
{ CursorDragCopy, dragCopyCursor },
{ CursorDragMove, dragCopyCursor },
{ CursorDragLink, dragLinkCursor },
{ CursorHand, pointingHandCursor },
{ CursorHelp, contextualMenuCursor },
{ CursorIbeam, IBeamCursor },
{ CursorLeftSide, resizeLeftCursor },
{ CursorRightSide, resizeRightCursor },
{ CursorNo, operationNotAllowedCursor }
};
public:
FORWARD_IUNKNOWN()
virtual HRESULT GetCursor (AvnStandardCursorType cursorType, IAvnCursor** retOut)
{
*retOut = s_cursorMap[cursorType];
if(*retOut != nullptr)
{
(*retOut)->AddRef();
}
return S_OK;
}
};
extern IAvnCursorFactory* CreateCursorFactory()
{
@autoreleasepool
{
return new CursorFactory();
}
}

255
src/Avalonia.Native.OSX/gl.mm

@ -0,0 +1,255 @@
#include "common.h"
#include <OpenGL/gl.h>
#include <dlfcn.h>
template <typename T, size_t N> char (&ArrayCounter(T (&a)[N]))[N];
#define ARRAY_COUNT(a) (sizeof(ArrayCounter(a)))
NSOpenGLPixelFormat* CreateFormat()
{
NSOpenGLPixelFormatAttribute attribs[] =
{
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAColorSize, 32,
NSOpenGLPFAStencilSize, 8,
NSOpenGLPFADepthSize, 8,
0
};
return [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
}
class AvnGlContext : public virtual ComSingleObject<IAvnGlContext, &IID_IAvnGlContext>
{
public:
FORWARD_IUNKNOWN()
NSOpenGLContext* GlContext;
GLuint Framebuffer, RenderBuffer, StencilBuffer;
AvnGlContext(NSOpenGLContext* gl, bool offscreen)
{
Framebuffer = 0;
RenderBuffer = 0;
StencilBuffer = 0;
GlContext = gl;
if(offscreen)
{
[GlContext makeCurrentContext];
glGenFramebuffersEXT(1, &Framebuffer);
glBindFramebufferEXT(GL_FRAMEBUFFER, Framebuffer);
glGenRenderbuffersEXT(1, &RenderBuffer);
glGenRenderbuffersEXT(1, &StencilBuffer);
glBindRenderbufferEXT(GL_RENDERBUFFER, StencilBuffer);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, StencilBuffer);
glBindRenderbufferEXT(GL_RENDERBUFFER, RenderBuffer);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, RenderBuffer);
}
}
virtual HRESULT MakeCurrent()
{
[GlContext makeCurrentContext];/*
glBindFramebufferEXT(GL_FRAMEBUFFER, Framebuffer);
glBindRenderbufferEXT(GL_RENDERBUFFER, RenderBuffer);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, RenderBuffer);
glBindRenderbufferEXT(GL_RENDERBUFFER, StencilBuffer);
glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, StencilBuffer);*/
return S_OK;
}
};
class AvnGlDisplay : public virtual ComSingleObject<IAvnGlDisplay, &IID_IAvnGlDisplay>
{
int _sampleCount, _stencilSize;
void* _libgl;
public:
FORWARD_IUNKNOWN()
AvnGlDisplay(int sampleCount, int stencilSize)
{
_sampleCount = sampleCount;
_stencilSize = stencilSize;
_libgl = dlopen("/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib", RTLD_LAZY);
}
virtual HRESULT GetSampleCount(int* ret)
{
*ret = _sampleCount;
return S_OK;
}
virtual HRESULT GetStencilSize(int* ret)
{
*ret = _stencilSize;
return S_OK;
}
virtual HRESULT ClearContext()
{
[NSOpenGLContext clearCurrentContext];
return S_OK;
}
virtual void* GetProcAddress(char* proc)
{
return dlsym(_libgl, proc);
}
};
class GlFeature : public virtual ComSingleObject<IAvnGlFeature, &IID_IAvnGlFeature>
{
IAvnGlDisplay* _display;
AvnGlContext *_immediate;
NSOpenGLContext* _shared;
public:
FORWARD_IUNKNOWN()
NSOpenGLPixelFormat* _format;
GlFeature(IAvnGlDisplay* display, AvnGlContext* immediate, NSOpenGLPixelFormat* format)
{
_display = display;
_immediate = immediate;
_format = format;
_shared = [[NSOpenGLContext alloc] initWithFormat:_format shareContext:_immediate->GlContext];
}
NSOpenGLContext* CreateContext()
{
return _shared;
//return [[NSOpenGLContext alloc] initWithFormat:_format shareContext:nil];
}
virtual HRESULT ObtainDisplay(IAvnGlDisplay**retOut)
{
*retOut = _display;
_display->AddRef();
return S_OK;
}
virtual HRESULT ObtainImmediateContext(IAvnGlContext**retOut)
{
*retOut = _immediate;
_immediate->AddRef();
return S_OK;
}
};
static GlFeature* Feature;
GlFeature* CreateGlFeature()
{
auto format = CreateFormat();
if(format == nil)
{
NSLog(@"Unable to choose pixel format");
return NULL;
}
auto immediateContext = [[NSOpenGLContext alloc] initWithFormat:format shareContext:nil];
if(immediateContext == nil)
{
NSLog(@"Unable to create NSOpenGLContext");
return NULL;
}
int stencilBits = 0, sampleCount = 0;
auto fmt = CGLGetPixelFormat([immediateContext CGLContextObj]);
CGLDescribePixelFormat(fmt, 0, kCGLPFASamples, &sampleCount);
CGLDescribePixelFormat(fmt, 0, kCGLPFAStencilSize, &stencilBits);
auto offscreen = new AvnGlContext(immediateContext, true);
auto display = new AvnGlDisplay(sampleCount, stencilBits);
return new GlFeature(display, offscreen, format);
}
static GlFeature* GetFeature()
{
if(Feature == nil)
Feature = CreateGlFeature();
return Feature;
}
extern IAvnGlFeature* GetGlFeature()
{
return GetFeature();
}
class AvnGlRenderingSession : public ComSingleObject<IAvnGlSurfaceRenderingSession, &IID_IAvnGlSurfaceRenderingSession>
{
NSView* _view;
NSWindow* _window;
NSOpenGLContext* _context;
public:
FORWARD_IUNKNOWN()
AvnGlRenderingSession(NSWindow*window, NSView* view, NSOpenGLContext* context)
{
_context = context;
_window = window;
_view = view;
}
virtual HRESULT GetPixelSize(AvnPixelSize* ret)
{
auto fsize = [_view convertSizeToBacking: [_view frame].size];
ret->Width = (int)fsize.width;
ret->Height = (int)fsize.height;
return S_OK;
}
virtual HRESULT GetScaling(double* ret)
{
*ret = [_window backingScaleFactor];
return S_OK;
}
virtual ~AvnGlRenderingSession()
{
glFlush();
[_context flushBuffer];
[_context setView:nil];
CGLUnlockContext([_context CGLContextObj]);
[_view unlockFocus];
}
};
class AvnGlRenderTarget : public ComSingleObject<IAvnGlSurfaceRenderTarget, &IID_IAvnGlSurfaceRenderTarget>
{
NSView* _view;
NSWindow* _window;
NSOpenGLContext* _context;
public:
FORWARD_IUNKNOWN()
AvnGlRenderTarget(NSWindow* window, NSView*view)
{
_window = window;
_view = view;
_context = GetFeature()->CreateContext();
}
virtual HRESULT BeginDrawing(IAvnGlSurfaceRenderingSession** ret)
{
auto f = GetFeature();
if(f == NULL)
return E_FAIL;
if(![_view lockFocusIfCanDraw])
return E_ABORT;
auto gl = _context;
CGLLockContext([_context CGLContextObj]);
[gl setView: _view];
[gl makeCurrentContext];
auto frame = [_view frame];
*ret = new AvnGlRenderingSession(_window, _view, gl);
return S_OK;
}
};
extern IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(NSWindow* window, NSView* view)
{
return new AvnGlRenderTarget(window, view);
}

178
src/Avalonia.Native.OSX/main.mm

@ -0,0 +1,178 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
//This file will contain actual IID structures
#define COM_GUIDS_MATERIALIZE
#include "common.h"
static BOOL ShowInDock = 1;
static void SetActivationPolicy()
{
[[NSApplication sharedApplication] setActivationPolicy: (ShowInDock ? NSApplicationActivationPolicyRegular : NSApplicationActivationPolicyAccessory)];
}
class MacOptions : public ComSingleObject<IAvnMacOptions, &IID_IAvnMacOptions>
{
public:
FORWARD_IUNKNOWN()
virtual HRESULT SetShowInDock(int show)
{
ShowInDock = show;
SetActivationPolicy();
return S_OK;
}
};
/// See "Using POSIX Threads in a Cocoa Application" section here:
/// https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html#//apple_ref/doc/uid/20000738-125024
@interface ThreadingInitializer : NSObject
- (void) do;
@end
@implementation ThreadingInitializer
pthread_mutex_t mutex;
pthread_cond_t cond;
- (void) runOnce
{
pthread_mutex_lock(&mutex);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
- (void) do
{
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
[[[NSThread alloc] initWithTarget:self selector:@selector(runOnce) object:nil] start];
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
pthread_mutex_unlock(&mutex);
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
}
@end
class AvaloniaNative : public ComSingleObject<IAvaloniaNativeFactory, &IID_IAvaloniaNativeFactory>
{
public:
FORWARD_IUNKNOWN()
virtual HRESULT Initialize()
{
@autoreleasepool{
[[ThreadingInitializer new] do];
return S_OK;
}
};
virtual IAvnMacOptions* GetMacOptions()
{
return (IAvnMacOptions*)new MacOptions();
}
virtual HRESULT CreateWindow(IAvnWindowEvents* cb, IAvnWindow** ppv)
{
if(cb == nullptr || ppv == nullptr)
return E_POINTER;
*ppv = CreateAvnWindow(cb);
return S_OK;
};
virtual HRESULT CreatePopup(IAvnWindowEvents* cb, IAvnPopup** ppv)
{
if(cb == nullptr || ppv == nullptr)
return E_POINTER;
*ppv = CreateAvnPopup(cb);
return S_OK;
}
virtual HRESULT CreatePlatformThreadingInterface(IAvnPlatformThreadingInterface** ppv)
{
*ppv = CreatePlatformThreading();
return S_OK;
}
virtual HRESULT CreateSystemDialogs(IAvnSystemDialogs** ppv)
{
*ppv = ::CreateSystemDialogs();
return S_OK;
}
virtual HRESULT CreateScreens (IAvnScreens** ppv)
{
*ppv = ::CreateScreens ();
return S_OK;
}
virtual HRESULT CreateClipboard(IAvnClipboard** ppv)
{
*ppv = ::CreateClipboard ();
return S_OK;
}
virtual HRESULT CreateCursorFactory(IAvnCursorFactory** ppv)
{
*ppv = ::CreateCursorFactory();
return S_OK;
}
virtual HRESULT ObtainGlFeature(IAvnGlFeature** ppv)
{
auto rv = ::GetGlFeature();
if(rv == NULL)
return E_FAIL;
rv->AddRef();
*ppv = rv;
return S_OK;
}
};
extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative()
{
return new AvaloniaNative();
};
NSSize ToNSSize (AvnSize s)
{
NSSize result;
result.width = s.Width;
result.height = s.Height;
return result;
}
NSPoint ToNSPoint (AvnPoint p)
{
NSPoint result;
result.x = p.X;
result.y = p.Y;
return result;
}
AvnPoint ToAvnPoint (NSPoint p)
{
AvnPoint result;
result.X = p.x;
result.Y = p.y;
return result;
}
AvnPoint ConvertPointY (AvnPoint p)
{
auto sw = [NSScreen.screens objectAtIndex:0].frame;
auto t = MAX(sw.origin.y, sw.origin.y + sw.size.height);
p.Y = t - p.Y;
return p;
}

190
src/Avalonia.Native.OSX/platformthreading.mm

@ -0,0 +1,190 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
#include "common.h"
class PlatformThreadingInterface;
@interface Signaler : NSObject
-(void) setParent: (PlatformThreadingInterface*)parent;
-(void) signal: (int) priority;
-(Signaler*) init;
@end
@interface ActionCallback : NSObject
- (ActionCallback*) initWithCallback: (IAvnActionCallback*) callback;
- (void) action;
@end
@implementation ActionCallback
{
ComPtr<IAvnActionCallback> _callback;
}
- (ActionCallback*) initWithCallback: (IAvnActionCallback*) callback
{
_callback = callback;
return self;
}
- (void) action
{
_callback->Run();
}
@end
class TimerWrapper : public ComUnknownObject
{
NSTimer* _timer;
public:
TimerWrapper(IAvnActionCallback* callback, int ms)
{
auto cb = [[ActionCallback alloc] initWithCallback:callback];
_timer = [NSTimer scheduledTimerWithTimeInterval:(NSTimeInterval)(double)ms/1000 target:cb selector:@selector(action) userInfo:nullptr repeats:true];
}
virtual ~TimerWrapper()
{
[_timer invalidate];
}
};
class PlatformThreadingInterface : public ComSingleObject<IAvnPlatformThreadingInterface, &IID_IAvnPlatformThreadingInterface>
{
private:
Signaler* _signaler;
class LoopCancellation : public ComSingleObject<IAvnLoopCancellation, &IID_IAvnLoopCancellation>
{
public:
FORWARD_IUNKNOWN()
bool Cancelled = 0;
virtual void Cancel()
{
Cancelled = 1;
}
};
public:
FORWARD_IUNKNOWN()
ComPtr<IAvnSignaledCallback> SignaledCallback;
PlatformThreadingInterface()
{
_signaler = [Signaler new];
[_signaler setParent:this];
}
~PlatformThreadingInterface()
{
if(_signaler)
[_signaler setParent: NULL];
_signaler = NULL;
}
virtual bool GetCurrentThreadIsLoopThread()
{
return [[NSThread currentThread] isMainThread];
}
virtual void SetSignaledCallback(IAvnSignaledCallback* cb)
{
SignaledCallback = cb;
}
virtual IAvnLoopCancellation* CreateLoopCancellation()
{
return new LoopCancellation();
}
virtual void RunLoop(IAvnLoopCancellation* cancel)
{
@autoreleasepool {
auto can = dynamic_cast<LoopCancellation*>(cancel);
[[NSApplication sharedApplication] activateIgnoringOtherApps:true];
while(true)
{
@autoreleasepool
{
if(can != NULL && can->Cancelled)
return;
NSEvent* ev = [[NSApplication sharedApplication]
nextEventMatchingMask:NSEventMaskAny
untilDate: [NSDate dateWithTimeIntervalSinceNow:1]
inMode:NSDefaultRunLoopMode
dequeue:true];
if(can != NULL && can->Cancelled)
return;
if(ev != NULL)
[[NSApplication sharedApplication] sendEvent:ev];
}
}
NSDebugLog(@"RunLoop exited");
}
}
virtual void Signal(int priority)
{
[_signaler signal:priority];
}
virtual IUnknown* StartTimer(int priority, int ms, IAvnActionCallback* callback)
{
@autoreleasepool {
return new TimerWrapper(callback, ms);
}
}
};
@implementation Signaler
PlatformThreadingInterface* _parent = 0;
bool _signaled = 0;
NSArray<NSString*>* _modes;
-(Signaler*) init
{
if(self = [super init])
{
_modes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEventTrackingRunLoopMode, NSModalPanelRunLoopMode, NSRunLoopCommonModes, NSConnectionReplyMode, nil];
}
return self;
}
-(void) perform
{
@synchronized (self) {
_signaled = false;
if(_parent != NULL && _parent->SignaledCallback != NULL)
_parent->SignaledCallback->Signaled(0, false);
}
}
-(void) setParent:(PlatformThreadingInterface *)parent
{
@synchronized (self) {
_parent = parent;
}
}
-(void) signal: (int) priority
{
@synchronized (self) {
if(_signaled)
return;
_signaled = true;
[self performSelector:@selector(perform) onThread:[NSThread mainThread] withObject:NULL waitUntilDone:false modes:_modes];
}
}
@end
extern IAvnPlatformThreadingInterface* CreatePlatformThreading()
{
return new PlatformThreadingInterface();
}

33
src/Avalonia.Native.OSX/window.h

@ -0,0 +1,33 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
#ifndef window_h
#define window_h
class WindowBaseImpl;
@interface AvnView : NSView<NSTextInputClient>
-(AvnView* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent;
-(NSEvent* _Nonnull) lastMouseDownEvent;
-(AvnPoint) translateLocalPoint:(AvnPoint)pt;
-(void) setSwRenderedFrame: (AvnFramebuffer* _Nonnull) fb dispose: (IUnknown* _Nonnull) dispose;
-(void) onClosed;
@end
@interface AvnWindow : NSWindow <NSWindowDelegate>
-(AvnWindow* _Nonnull) initWithParent: (WindowBaseImpl* _Nonnull) parent;
-(void) setCanBecomeKeyAndMain;
-(void) pollModalSession: (NSModalSession _Nonnull) session;
@end
struct INSWindowHolder
{
virtual AvnWindow* _Nonnull GetNSWindow () = 0;
};
struct IWindowStateChanged
{
virtual void WindowStateChanged () = 0;
};
#endif /* window_h */

1228
src/Avalonia.Native.OSX/window.mm

File diff suppressed because it is too large

1
src/Avalonia.Native/.gitignore

@ -0,0 +1 @@
Generated

22
src/Avalonia.Native/Avalonia.Native.csproj

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<CastXmlPath Condition="Exists('/usr/bin/castxml')">/usr/bin/castxml</CastXmlPath>
<CastXmlPath Condition="Exists('/usr/local/bin/castxml')">/usr/local/bin/castxml</CastXmlPath>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<SharpGenGeneratedCodeFolder>$(MSBuildThisFileDirectory)/Generated</SharpGenGeneratedCodeFolder>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SharpGenTools.Sdk" Version="1.1.2" PrivateAssets="all" />
<PackageReference Include="SharpGen.Runtime.Com" Version="1.1.0" />
<SharpGenMapping Include="Mappings.xml" />
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\Avalonia.Controls\Avalonia.Controls.csproj" />
<ProjectReference Include="..\Avalonia.Input\Avalonia.Input.csproj" />
<ProjectReference Include="..\Avalonia.Interactivity\Avalonia.Interactivity.csproj" />
<ProjectReference Include="..\Avalonia.OpenGL\Avalonia.OpenGL.csproj" />
<ProjectReference Include="..\Avalonia.Visuals\Avalonia.Visuals.csproj" />
</ItemGroup>
</Project>

9
src/Avalonia.Native/Avalonia.Native.targets

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)..\..\runtimes\osx-x64\native\libAvalonia.Native.OSX.dylib">
<Link>libAvalonia.Native.OSX.dylib</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

132
src/Avalonia.Native/AvaloniaNativePlatform.cs

@ -0,0 +1,132 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Diagnostics.Contracts;
using System.Runtime.InteropServices;
using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Native.Interop;
using Avalonia.OpenGL;
using Avalonia.Platform;
using Avalonia.Rendering;
namespace Avalonia.Native
{
class AvaloniaNativePlatform : IPlatformSettings, IWindowingPlatform
{
private readonly IAvaloniaNativeFactory _factory;
[DllImport("libAvaloniaNative")]
static extern IntPtr CreateAvaloniaNative();
internal static readonly MouseDevice MouseDevice = new MouseDevice();
internal static readonly KeyboardDevice KeyboardDevice = new KeyboardDevice();
public Size DoubleClickSize => new Size(4, 4);
public TimeSpan DoubleClickTime => TimeSpan.FromMilliseconds(500); //TODO
public static void Initialize(IntPtr factory, Action<AvaloniaNativeOptions> configure)
{
new AvaloniaNativePlatform(new IAvaloniaNativeFactory(factory))
.DoInitialize(configure);
}
delegate IntPtr CreateAvaloniaNativeDelegate();
public static void Initialize(string library, Action<AvaloniaNativeOptions> configure)
{
var loader = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? (IDynLoader)new Win32Loader() : new UnixLoader();
var lib = loader.LoadLibrary(library);
var proc = loader.GetProcAddress(lib, "CreateAvaloniaNative", false);
var d = Marshal.GetDelegateForFunctionPointer<CreateAvaloniaNativeDelegate>(proc);
Initialize(d(), configure);
}
public static void Initialize(Action<AvaloniaNativeOptions> configure)
{
Initialize(CreateAvaloniaNative(), configure);
}
private AvaloniaNativePlatform(IAvaloniaNativeFactory factory)
{
_factory = factory;
}
void DoInitialize(Action<AvaloniaNativeOptions> configure)
{
var opts = new AvaloniaNativeOptions(_factory);
configure?.Invoke(opts);
_factory.Initialize();
AvaloniaLocator.CurrentMutable
.Bind<IPlatformThreadingInterface>().ToConstant(new PlatformThreadingInterface(_factory.CreatePlatformThreadingInterface()))
.Bind<IStandardCursorFactory>().ToConstant(new CursorFactory(_factory.CreateCursorFactory()))
.Bind<IPlatformIconLoader>().ToSingleton<IconLoader>()
.Bind<IKeyboardDevice>().ToConstant(KeyboardDevice)
.Bind<IMouseDevice>().ToConstant(MouseDevice)
.Bind<IPlatformSettings>().ToConstant(this)
.Bind<IWindowingPlatform>().ToConstant(this)
.Bind<IClipboard>().ToConstant(new ClipboardImpl(_factory.CreateClipboard()))
.Bind<IRenderLoop>().ToConstant(new RenderLoop())
.Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60))
.Bind<ISystemDialogImpl>().ToConstant(new SystemDialogs(_factory.CreateSystemDialogs()))
.Bind<IWindowingPlatformGlFeature>().ToConstant(new GlPlatformFeature(_factory.ObtainGlFeature()))
.Bind<AvaloniaNativeOptions>().ToConstant(opts);
}
public IWindowImpl CreateWindow()
{
return new WindowImpl(_factory);
}
public IEmbeddableWindowImpl CreateEmbeddableWindow()
{
throw new NotImplementedException();
}
public IPopupImpl CreatePopup()
{
return new PopupImpl(_factory);
}
}
public class AvaloniaNativeMacOptions
{
private readonly IAvnMacOptions _opts;
private bool _showInDock;
internal AvaloniaNativeMacOptions(IAvnMacOptions opts)
{
_opts = opts;
ShowInDock = true;
}
public bool ShowInDock
{
get => _showInDock;
set
{
_showInDock = value;
_opts.ShowInDock = value ? 1 : 0;
}
}
}
public class AvaloniaNativeOptions
{
public AvaloniaNativeMacOptions MacOptions { get; set; }
public bool UseDeferredRendering { get; set; } = true;
public bool UseGpu { get; set; } = false;
internal AvaloniaNativeOptions(IAvaloniaNativeFactory factory)
{
var mac = factory.GetMacOptions();
if (mac != null)
MacOptions = new AvaloniaNativeMacOptions(mac);
}
}
}

29
src/Avalonia.Native/AvaloniaNativePlatformExtensions.cs

@ -0,0 +1,29 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Avalonia.Controls;
using Avalonia.Native;
namespace Avalonia
{
public static class AvaloniaNativePlatformExtensions
{
public static T UseAvaloniaNative<T>(this T builder,
string libraryPath = null,
Action<AvaloniaNativeOptions> configure = null)
where T : AppBuilderBase<T>, new()
{
if (libraryPath == null)
{
builder.UseWindowingSubsystem(() => AvaloniaNativePlatform.Initialize(configure));
}
else
{
builder.UseWindowingSubsystem(() => AvaloniaNativePlatform.Initialize(libraryPath, configure));
}
return builder;
}
}
}

80
src/Avalonia.Native/CallbackBase.cs

@ -0,0 +1,80 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using SharpGen.Runtime;
namespace Avalonia.Native
{
public class CallbackBase : SharpGen.Runtime.IUnknown
{
private uint _refCount;
private bool _disposed;
private readonly object _lock = new object();
private ShadowContainer _shadow;
public CallbackBase()
{
_refCount = 1;
}
public ShadowContainer Shadow
{
get => _shadow;
set
{
lock (_lock)
{
if (_disposed && value != null)
{
throw new ObjectDisposedException("CallbackBase");
}
_shadow = value;
}
}
}
public uint AddRef()
{
lock (_lock)
{
return ++_refCount;
}
}
public void Dispose()
{
lock (_lock)
{
if (!_disposed)
{
_disposed = true;
Release();
}
}
}
public uint Release()
{
lock (_lock)
{
_refCount--;
if (_refCount == 0)
{
Shadow?.Dispose();
Shadow = null;
Destroyed();
}
return _refCount;
}
}
protected virtual void Destroyed()
{
}
}
}

47
src/Avalonia.Native/ClipboardImpl.cs

@ -0,0 +1,47 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using Avalonia.Input.Platform;
using Avalonia.Native.Interop;
namespace Avalonia.Native
{
class ClipboardImpl : IClipboard
{
private IAvnClipboard _native;
public ClipboardImpl(IAvnClipboard native)
{
_native = native;
}
public Task ClearAsync()
{
_native.Clear();
return Task.CompletedTask;
}
public Task<string> GetTextAsync()
{
var outPtr = _native.GetText();
var text = Marshal.PtrToStringAnsi(outPtr);
return Task.FromResult(text);
}
public Task SetTextAsync(string text)
{
_native.Clear();
if (text != null)
{
_native.SetText(text);
}
return Task.CompletedTask;
}
}
}

45
src/Avalonia.Native/Cursor.cs

@ -0,0 +1,45 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Avalonia.Input;
using Avalonia.Platform;
using Avalonia.Native.Interop;
namespace Avalonia.Native
{
class AvaloniaNativeCursor : IPlatformHandle, IDisposable
{
public IAvnCursor Cursor { get; private set; }
public IntPtr Handle => IntPtr.Zero;
public string HandleDescriptor => "<none>";
public AvaloniaNativeCursor(IAvnCursor cursor)
{
Cursor = cursor;
}
public void Dispose()
{
Cursor.Dispose();
Cursor = null;
}
}
class CursorFactory : IStandardCursorFactory
{
IAvnCursorFactory _native;
public CursorFactory(IAvnCursorFactory native)
{
_native = native;
}
public IPlatformHandle GetCursor(StandardCursorType cursorType)
{
var cursor = _native.GetCursor((AvnStandardCursorType)cursorType);
return new AvaloniaNativeCursor( cursor );
}
}
}

87
src/Avalonia.Native/DeferredFramebuffer.cs

@ -0,0 +1,87 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Runtime.InteropServices;
using Avalonia.Native.Interop;
using Avalonia.Platform;
using SharpGen.Runtime;
namespace Avalonia.Native
{
public class DeferredFramebuffer : ILockedFramebuffer
{
private readonly Func<Action<IAvnWindowBase>, bool> _lockWindow;
public DeferredFramebuffer(Func<Action<IAvnWindowBase>, bool> lockWindow,
int width, int height, Vector dpi)
{
_lockWindow = lockWindow;
Address = Marshal.AllocHGlobal(width * height * 4);
Width = width;
Height = height;
RowBytes = width * 4;
Dpi = dpi;
Format = PixelFormat.Rgba8888;
}
public IntPtr Address { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public int RowBytes { get; set; }
public Vector Dpi { get; set; }
public PixelFormat Format { get; set; }
class Disposer : CallbackBase
{
private IntPtr _ptr;
public Disposer(IntPtr ptr)
{
_ptr = ptr;
}
protected override void Destroyed()
{
if(_ptr != IntPtr.Zero)
{
Marshal.FreeHGlobal(_ptr);
_ptr = IntPtr.Zero;
}
}
}
public void Dispose()
{
if (Address == IntPtr.Zero)
return;
if (!_lockWindow(win =>
{
var fb = new AvnFramebuffer
{
Data = Address,
Dpi = new AvnVector
{
X = Dpi.X,
Y = Dpi.Y
},
Width = Width,
Height = Height,
PixelFormat = (AvnPixelFormat)Format,
Stride = RowBytes
};
using (var d = new Disposer(Address))
{
win.ThreadSafeSetSwRenderedFrame(ref fb, d);
}
}))
{
Marshal.FreeHGlobal(Address);
}
Address = IntPtr.Zero;
}
}
}

108
src/Avalonia.Native/DeferredRendererProxy.cs

@ -0,0 +1,108 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Collections.Generic;
using Avalonia.Native.Interop;
using Avalonia.Rendering;
using Avalonia.VisualTree;
namespace Avalonia.Native
{
public class DeferredRendererProxy : IRenderer, IRenderLoopTask, IRenderLoop
{
public DeferredRendererProxy(IRenderRoot root, IAvnWindowBase window)
{
if (window != null)
{
_useLock = true;
window.AddRef();
_window = new IAvnWindowBase(window.NativePointer);
}
_renderer = new DeferredRenderer(root, this);
_rendererTask = (IRenderLoopTask)_renderer;
}
void IRenderLoop.Add(IRenderLoopTask i)
{
AvaloniaLocator.Current.GetService<IRenderLoop>().Add(this);
}
void IRenderLoop.Remove(IRenderLoopTask i)
{
AvaloniaLocator.Current.GetService<IRenderLoop>().Remove(this);
}
private DeferredRenderer _renderer;
private IRenderLoopTask _rendererTask;
private IAvnWindowBase _window;
private bool _useLock;
public bool DrawFps{
get => _renderer.DrawFps;
set => _renderer.DrawFps = value;
}
public bool DrawDirtyRects
{
get => _renderer.DrawDirtyRects;
set => _renderer.DrawDirtyRects = value;
}
public bool NeedsUpdate => _rendererTask.NeedsUpdate;
public void AddDirty(IVisual visual) => _renderer.AddDirty(visual);
public void Dispose()
{
_renderer.Dispose();
_window?.Dispose();
_window = null;
}
public IEnumerable<IVisual> HitTest(Point p, IVisual root, Func<IVisual, bool> filter)
{
return _renderer.HitTest(p, root, filter);
}
public void Paint(Rect rect)
{
if (NeedsUpdate)
{
Update(TimeSpan.FromMilliseconds(Environment.TickCount));
}
Render();
}
public void Resized(Size size) => _renderer.Resized(size);
public void Start() => _renderer.Start();
public void Stop() => _renderer.Stop();
public void Update(TimeSpan time)
{
_rendererTask.Update(time);
}
public void Render()
{
if(_useLock)
{
_rendererTask.Render();
return;
}
if (_window == null)
return;
if (!_window.TryLock())
return;
try
{
_rendererTask.Render();
}
finally
{
_window.Unlock();
}
}
}
}

128
src/Avalonia.Native/DynLoader.cs

@ -0,0 +1,128 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
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.Native
{
internal interface IDynLoader
{
IntPtr LoadLibrary(string dll);
IntPtr GetProcAddress(IntPtr dll, string proc, bool optional);
}
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;
}
}
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;
}
}
[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;
// ReSharper restore InconsistentNaming
static string DlErrorString() => Marshal.PtrToStringAnsi(DlError());
public IntPtr LoadLibrary(string dll)
{
var handle = DlOpen(dll, 1);
if (handle == IntPtr.Zero)
throw new Exception(DlErrorString());
return handle;
}
public IntPtr GetProcAddress(IntPtr dll, string proc, bool optional)
{
var ptr = DlSym(dll, proc);
if (ptr == IntPtr.Zero && !optional)
throw new Exception(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 dll)
{
var handle = LoadLibrary(dll);
if (handle != IntPtr.Zero)
return handle;
var err = Marshal.GetLastWin32Error();
throw new Exception("Error loading " + dll + " error " + err);
}
IntPtr IDynLoader.GetProcAddress(IntPtr dll, string proc, bool optional)
{
var ptr = GetProcAddress(dll, proc);
if (ptr == IntPtr.Zero && !optional)
throw new Exception("Error " + Marshal.GetLastWin32Error());
return ptr;
}
}
}

134
src/Avalonia.Native/GlPlatformFeature.cs

@ -0,0 +1,134 @@
using System;
using Avalonia.OpenGL;
using Avalonia.Native.Interop;
using System.Drawing;
using Avalonia.Threading;
namespace Avalonia.Native
{
class GlPlatformFeature : IWindowingPlatformGlFeature
{
public GlPlatformFeature(IAvnGlFeature feature)
{
Display = new GlDisplay(feature.ObtainDisplay());
ImmediateContext = new GlContext(Display, feature.ObtainImmediateContext());
}
public IGlContext ImmediateContext { get; }
public GlDisplay Display { get; }
}
class GlDisplay : IGlDisplay
{
private readonly IAvnGlDisplay _display;
public GlDisplay(IAvnGlDisplay display)
{
_display = display;
GlInterface = new GlInterface((name, optional) =>
{
var rv = _display.GetProcAddress(name);
if (rv == IntPtr.Zero && !optional)
throw new OpenGlException($"{name} not found in system OpenGL");
return rv;
});
}
public GlDisplayType Type => GlDisplayType.OpenGL2;
public GlInterface GlInterface { get; }
public int SampleCount => _display.GetSampleCount();
public int StencilSize => _display.GetStencilSize();
public void ClearContext() => _display.ClearContext();
}
class GlContext : IGlContext
{
public IAvnGlContext Context { get; }
public GlContext(GlDisplay display, IAvnGlContext context)
{
Display = display;
Context = context;
}
public IGlDisplay Display { get; }
public void MakeCurrent(IGlSurface surface)
{
if (surface != null)
throw new ArgumentException(nameof(surface));
Context.MakeCurrent();
}
}
class GlPlatformSurfaceRenderTarget : IGlPlatformSurfaceRenderTarget
{
private IAvnGlSurfaceRenderTarget _target;
public GlPlatformSurfaceRenderTarget(IAvnGlSurfaceRenderTarget target)
{
_target = target;
}
public IGlPlatformSurfaceRenderingSession BeginDraw()
{
var feature = (GlPlatformFeature)AvaloniaLocator.Current.GetService<IWindowingPlatformGlFeature>();
return new GlPlatformSurfaceRenderingSession(feature.Display, _target.BeginDrawing());
}
public void Dispose()
{
_target?.Dispose();
_target = null;
}
}
class GlPlatformSurfaceRenderingSession : IGlPlatformSurfaceRenderingSession
{
private IAvnGlSurfaceRenderingSession _session;
public GlPlatformSurfaceRenderingSession(GlDisplay display, IAvnGlSurfaceRenderingSession session)
{
Display = display;
_session = session;
}
public IGlDisplay Display { get; }
public System.Drawing.Size PixelSize
{
get
{
var s = _session.GetPixelSize();
return new System.Drawing.Size(s.Width, s.Height);
}
}
public double Scaling => _session.GetScaling();
public void Dispose()
{
_session?.Dispose();
_session = null;
}
}
class GlPlatformSurface : IGlPlatformSurface
{
private readonly IAvnWindowBase _window;
public GlPlatformSurface(IAvnWindowBase window)
{
_window = window;
}
public IGlPlatformSurfaceRenderTarget CreateGlRenderTarget()
{
return new GlPlatformSurfaceRenderTarget(_window.CreateGlRenderTarget());
}
}
}

35
src/Avalonia.Native/Helpers.cs

@ -0,0 +1,35 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia.Native.Interop;
namespace Avalonia.Native
{
public static class Helpers
{
public static Point ToAvaloniaPoint (this AvnPoint pt)
{
return new Point(pt.X, pt.Y);
}
public static AvnPoint ToAvnPoint (this Point pt)
{
return new AvnPoint { X = pt.X, Y = pt.Y };
}
public static AvnSize ToAvnSize (this Size size)
{
return new AvnSize { Height = size.Height, Width = size.Width };
}
public static Size ToAvaloniaSize (this AvnSize size)
{
return new Size(size.Width, size.Height);
}
public static Rect ToAvaloniaRect (this AvnRect rect)
{
return new Rect(rect.X, rect.Y, rect.Width, rect.Height);
}
}
}

50
src/Avalonia.Native/IconLoader.cs

@ -0,0 +1,50 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.IO;
using Avalonia.Platform;
namespace Avalonia.Native
{
// OSX doesn't have a concept of *window* icon.
// Icons in the title bar are only shown if there is
// an opened file (on disk) associated with the current window
// see http://stackoverflow.com/a/7038671/2231814
class IconLoader : IPlatformIconLoader
{
class IconStub : IWindowIconImpl
{
private readonly IBitmapImpl _bitmap;
public IconStub(IBitmapImpl bitmap)
{
_bitmap = bitmap;
}
public void Save(Stream outputStream)
{
_bitmap.Save(outputStream);
}
}
public IWindowIconImpl LoadIcon(string fileName)
{
return new IconStub(
AvaloniaLocator.Current.GetService<IPlatformRenderInterface>().LoadBitmap(fileName));
}
public IWindowIconImpl LoadIcon(Stream stream)
{
return new IconStub(
AvaloniaLocator.Current.GetService<IPlatformRenderInterface>().LoadBitmap(stream));
}
public IWindowIconImpl LoadIcon(IBitmapImpl bitmap)
{
var ms = new MemoryStream();
bitmap.Save(ms);
ms.Seek(0, SeekOrigin.Begin);
return LoadIcon(ms);
}
}
}

27
src/Avalonia.Native/Mappings.xml

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<config id="AvaloniaNative" xmlns="urn:SharpGen.Config">
<assembly>Avalonia.Native</assembly>
<namespace>Avalonia.Native.Interop</namespace>
<depends>SharpGen.Runtime.COM</depends>
<include-dir>$(THIS_CONFIG_PATH)/headers</include-dir>
<include file="avalonia-native.h"
namespace="Avalonia.Native.Interop"
attach="true"
pre="#define COM_GUIDS_MATERIALIZE\n"/>
<include file="com.h"
namespace="Avalonia.Native.Interop"
attach="true"
pre="#define COM_GUIDS_MATERIALIZE\n"/>
<bindings>
<bind from="HRESULT" to="SharpGen.Runtime.Result"/>
<bind from="HRESULT" to="SharpGen.Runtime.Result"/>
</bindings>
<mapping>
<remove function=".*" />
<map interface="*.Events" callback="true" autogen-shadow="true"/>
<map interface="*.Callback" callback="true" autogen-shadow="true"/>
<map param=".*::.*::ppv" return="true"/>
<map param=".*::.*::ret" return="true"/>
<map param=".*::.*::retOut" attribute="out" return="true"/>
</mapping>
</config>

101
src/Avalonia.Native/PlatformThreadingInterface.cs

@ -0,0 +1,101 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Threading;
using Avalonia.Native.Interop;
using Avalonia.Platform;
using Avalonia.Threading;
using SharpGen.Runtime;
namespace Avalonia.Native
{
public class PlatformThreadingInterface : IPlatformThreadingInterface
{
class TimerCallback : CallbackBase, IAvnActionCallback
{
readonly Action _tick;
public TimerCallback(Action tick)
{
_tick = tick;
}
public void Run()
{
_tick();
}
}
class SignaledCallback : CallbackBase, IAvnSignaledCallback
{
readonly PlatformThreadingInterface _parent;
public SignaledCallback(PlatformThreadingInterface parent)
{
_parent = parent;
}
public void Signaled(int priority, bool priorityContainsMeaningfulValue)
{
_parent.Signaled?.Invoke(priorityContainsMeaningfulValue ? (DispatcherPriority?)priority : null);
}
}
readonly IAvnPlatformThreadingInterface _native;
public PlatformThreadingInterface(IAvnPlatformThreadingInterface native)
{
_native = native;
using (var cb = new SignaledCallback(this))
_native.SignaledCallback = cb;
}
public bool CurrentThreadIsLoopThread => _native.CurrentThreadIsLoopThread;
public event Action<DispatcherPriority?> Signaled;
public void RunLoop(CancellationToken cancellationToken)
{
if (cancellationToken.CanBeCanceled == false)
_native.RunLoop(null);
else
{
var l = new object();
var cancellation = _native.CreateLoopCancellation();
cancellationToken.Register(() =>
{
lock (l)
{
cancellation?.Cancel();
cancellation?.Dispose();
cancellation = null;
}
});
try
{
_native.RunLoop(cancellation);
}
finally
{
lock(l)
{
cancellation?.Dispose();
cancellation = null;
}
}
}
}
public void Signal(DispatcherPriority priority)
{
_native.Signal((int)priority);
}
public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick)
{
using (var cb = new TimerCallback(tick))
return _native.StartTimer((int)priority, (int)interval.TotalMilliseconds, cb);
}
}
}

39
src/Avalonia.Native/PopupImpl.cs

@ -0,0 +1,39 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Avalonia.Native.Interop;
using Avalonia.Platform;
namespace Avalonia.Native
{
public class PopupImpl : WindowBaseImpl, IPopupImpl
{
public PopupImpl(IAvaloniaNativeFactory factory)
{
using (var e = new PopupEvents(this))
{
Init(factory.CreatePopup(e), factory.CreateScreens());
}
}
class PopupEvents : WindowBaseEvents, IAvnWindowEvents
{
readonly PopupImpl _parent;
public PopupEvents(PopupImpl parent) : base(parent)
{
_parent = parent;
}
bool IAvnWindowEvents.Closing()
{
return true;
}
void IAvnWindowEvents.WindowStateChanged(AvnWindowState state)
{
}
}
}
}

45
src/Avalonia.Native/ScreenImpl.cs

@ -0,0 +1,45 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Avalonia.Native.Interop;
using Avalonia.Platform;
namespace Avalonia.Native
{
class ScreenImpl : IScreenImpl, IDisposable
{
private IAvnScreens _native;
public ScreenImpl(IAvnScreens native)
{
_native = native;
}
public int ScreenCount => _native.GetScreenCount();
public Screen[] AllScreens
{
get
{
var count = ScreenCount;
var result = new Screen[count];
for(int i = 0; i < count; i++)
{
var screen = _native.GetScreen(i);
result[i] = new Screen(screen.Bounds.ToAvaloniaRect(), screen.WorkingArea.ToAvaloniaRect(), screen.Primary);
}
return result;
}
}
public void Dispose ()
{
_native.Dispose();
_native = null;
}
}
}

90
src/Avalonia.Native/SystemDialogs.cs

@ -0,0 +1,90 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Controls.Platform;
using Avalonia.Native.Interop;
using Avalonia.Platform;
namespace Avalonia.Native
{
public class SystemDialogs : ISystemDialogImpl
{
IAvnSystemDialogs _native;
public SystemDialogs(IAvnSystemDialogs native)
{
_native = native;
}
public Task<string[]> ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent)
{
var events = new SystemDialogEvents();
if (dialog is OpenFileDialog ofd)
{
_native.OpenFileDialog((parent as WindowImpl)?.Native,
events, ofd.AllowMultiple,
ofd.Title ?? "",
ofd.InitialDirectory ?? "",
ofd.InitialFileName ?? "",
string.Join(";", dialog.Filters.SelectMany(f => f.Extensions)));
}
else
{
_native.SaveFileDialog((parent as WindowImpl)?.Native,
events,
dialog.Title ?? "",
dialog.InitialDirectory ?? "",
dialog.InitialFileName ?? "",
string.Join(";", dialog.Filters.SelectMany(f => f.Extensions)));
}
return events.Task.ContinueWith(t => { events.Dispose(); return t.Result; });
}
public Task<string> ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent)
{
var events = new SystemDialogEvents();
_native.SelectFolderDialog((parent as WindowImpl)?.Native, events, dialog.Title ?? "", dialog.InitialDirectory ?? "");
return events.Task.ContinueWith(t => { events.Dispose(); return t.Result.FirstOrDefault(); });
}
}
public class SystemDialogEvents : CallbackBase, IAvnSystemDialogEvents
{
private TaskCompletionSource<string[]> _tcs;
public SystemDialogEvents()
{
_tcs = new TaskCompletionSource<string[]>();
}
public Task<string[]> Task => _tcs.Task;
public void OnCompleted(int numResults, IntPtr trFirstResultRef)
{
string[] results = new string[numResults];
unsafe
{
var ptr = (IntPtr*)trFirstResultRef.ToPointer();
for (int i = 0; i < numResults; i++)
{
results[i] = Marshal.PtrToStringAnsi(*ptr);
ptr++;
}
}
_tcs.SetResult(results);
}
}
}

100
src/Avalonia.Native/WindowImpl.cs

@ -0,0 +1,100 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using Avalonia.Controls;
using Avalonia.Native.Interop;
using Avalonia.Platform;
namespace Avalonia.Native
{
public class WindowImpl : WindowBaseImpl, IWindowImpl
{
IAvnWindow _native;
public WindowImpl(IAvaloniaNativeFactory factory)
{
using (var e = new WindowEvents(this))
{
Init(_native = factory.CreateWindow(e), factory.CreateScreens());
}
}
class WindowEvents : WindowBaseEvents, IAvnWindowEvents
{
readonly WindowImpl _parent;
public WindowEvents(WindowImpl parent) : base(parent)
{
_parent = parent;
}
bool IAvnWindowEvents.Closing()
{
if(_parent.Closing != null)
{
return _parent.Closing();
}
return true;
}
void IAvnWindowEvents.WindowStateChanged(AvnWindowState state)
{
_parent.WindowStateChanged?.Invoke((WindowState)state);
}
}
public IAvnWindow Native => _native;
public IDisposable ShowDialog()
{
return _native.ShowDialog();
}
public void CanResize(bool value)
{
_native.CanResize = value;
}
public void SetSystemDecorations(bool enabled)
{
_native.HasDecorations = enabled;
}
public void SetTitleBarColor (Avalonia.Media.Color color)
{
_native.SetTitleBarColor(new AvnColor { Alpha = color.A, Red = color.R, Green = color.G, Blue = color.B });
}
public void SetTitle(string title)
{
_native.SetTitle(title);
}
public WindowState WindowState
{
get
{
return (WindowState)_native.GetWindowState();
}
set
{
_native.SetWindowState((AvnWindowState)value);
}
}
public Action<WindowState> WindowStateChanged { get; set; }
public void ShowTaskbarIcon(bool value)
{
// NO OP On OSX
}
public void SetIcon(IWindowIconImpl icon)
{
// NO OP on OSX
}
public Func<bool> Closing { get; set; }
}
}

351
src/Avalonia.Native/WindowImplBase.cs

@ -0,0 +1,351 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Native.Interop;
using Avalonia.OpenGL;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Threading;
namespace Avalonia.Native
{
public class WindowBaseImpl : IWindowBaseImpl,
IFramebufferPlatformSurface
{
IInputRoot _inputRoot;
IAvnWindowBase _native;
private object _syncRoot = new object();
private bool _deferredRendering = false;
private bool _gpu = false;
private readonly IMouseDevice _mouse;
private readonly IKeyboardDevice _keyboard;
private readonly IStandardCursorFactory _cursorFactory;
private Size _savedLogicalSize;
private Size _lastRenderedLogicalSize;
private double _savedScaling;
private GlPlatformSurface _glSurface;
public WindowBaseImpl()
{
var opts = AvaloniaLocator.Current.GetService<AvaloniaNativeOptions>();
_gpu = opts.UseGpu;
_deferredRendering = opts.UseDeferredRendering;
_keyboard = AvaloniaLocator.Current.GetService<IKeyboardDevice>();
_mouse = AvaloniaLocator.Current.GetService<IMouseDevice>();
_cursorFactory = AvaloniaLocator.Current.GetService<IStandardCursorFactory>();
}
protected void Init(IAvnWindowBase window, IAvnScreens screens)
{
_native = window;
_glSurface = new GlPlatformSurface(window);
Screen = new ScreenImpl(screens);
_savedLogicalSize = ClientSize;
_savedScaling = Scaling;
}
public Size ClientSize
{
get
{
var s = _native.GetClientSize();
return new Size(s.Width, s.Height);
}
}
public IEnumerable<object> Surfaces => new[] {
(_gpu ? _glSurface : (object)null),
this
};
public ILockedFramebuffer Lock()
{
if(_deferredRendering)
{
var w = _savedLogicalSize.Width * _savedScaling;
var h = _savedLogicalSize.Height * _savedScaling;
var dpi = _savedScaling * 96;
return new DeferredFramebuffer(cb =>
{
lock (_syncRoot)
{
if (_native == null)
return false;
cb(_native);
_lastRenderedLogicalSize = _savedLogicalSize;
return true;
}
}, (int)w, (int)h, new Vector(dpi, dpi));
}
return new FramebufferWrapper(_native.GetSoftwareFramebuffer());
}
public Action<Rect> Paint { get; set; }
public Action<Size> Resized { get; set; }
public Action Closed { get; set; }
public IMouseDevice MouseDevice => AvaloniaNativePlatform.MouseDevice;
class FramebufferWrapper : ILockedFramebuffer
{
public FramebufferWrapper(AvnFramebuffer fb)
{
Address = fb.Data;
Width = fb.Width;
Height = fb.Height;
RowBytes = fb.Stride;
Dpi = new Vector(fb.Dpi.X, fb.Dpi.Y);
Format = (PixelFormat)fb.PixelFormat;
}
public IntPtr Address { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public int RowBytes {get;set;}
public Vector Dpi { get; set; }
public PixelFormat Format { get; }
public void Dispose()
{
// Do nothing
}
}
protected class WindowBaseEvents : CallbackBase, IAvnWindowBaseEvents
{
private readonly WindowBaseImpl _parent;
public WindowBaseEvents(WindowBaseImpl parent)
{
_parent = parent;
}
void IAvnWindowBaseEvents.Closed()
{
var n = _parent._native;
_parent._native = null;
try
{
_parent?.Closed?.Invoke();
}
finally
{
n?.Dispose();
}
}
void IAvnWindowBaseEvents.Activated() => _parent.Activated?.Invoke();
void IAvnWindowBaseEvents.Deactivated() => _parent.Deactivated?.Invoke();
void IAvnWindowBaseEvents.Paint()
{
Dispatcher.UIThread.RunJobs(DispatcherPriority.Render);
var s = _parent.ClientSize;
_parent.Paint?.Invoke(new Rect(0, 0, s.Width, s.Height));
}
void IAvnWindowBaseEvents.Resized(AvnSize size)
{
var s = new Size(size.Width, size.Height);
_parent._savedLogicalSize = s;
_parent.Resized?.Invoke(s);
}
void IAvnWindowBaseEvents.PositionChanged(AvnPoint position)
{
_parent.PositionChanged?.Invoke(position.ToAvaloniaPoint());
}
void IAvnWindowBaseEvents.RawMouseEvent(AvnRawMouseEventType type, uint timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta)
{
_parent.RawMouseEvent(type, timeStamp, modifiers, point, delta);
}
bool IAvnWindowBaseEvents.RawKeyEvent(AvnRawKeyEventType type, uint timeStamp, AvnInputModifiers modifiers, uint key)
{
return _parent.RawKeyEvent(type, timeStamp, modifiers, key);
}
bool IAvnWindowBaseEvents.RawTextInputEvent(uint timeStamp, string text)
{
return _parent.RawTextInputEvent(timeStamp, text);
}
void IAvnWindowBaseEvents.ScalingChanged(double scaling)
{
_parent._savedScaling = scaling;
_parent.ScalingChanged?.Invoke(scaling);
}
void IAvnWindowBaseEvents.RunRenderPriorityJobs()
{
if (_parent._deferredRendering
&& _parent._lastRenderedLogicalSize != _parent.ClientSize)
// Hack to trigger Paint event on the renderer
_parent.Paint?.Invoke(new Rect());
Dispatcher.UIThread.RunJobs(DispatcherPriority.Render);
}
}
public void Activate()
{
_native.Activate();
}
public bool RawTextInputEvent(uint timeStamp, string text)
{
Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
var args = new RawTextInputEventArgs(_keyboard, timeStamp, text);
Input?.Invoke(args);
return args.Handled;
}
public bool RawKeyEvent(AvnRawKeyEventType type, uint timeStamp, AvnInputModifiers modifiers, uint key)
{
Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
var args = new RawKeyEventArgs(_keyboard, timeStamp, (RawKeyEventType)type, (Key)key, (InputModifiers)modifiers);
Input?.Invoke(args);
return args.Handled;
}
public void RawMouseEvent(AvnRawMouseEventType type, uint timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta)
{
Dispatcher.UIThread.RunJobs(DispatcherPriority.Input + 1);
switch (type)
{
case AvnRawMouseEventType.Wheel:
Input?.Invoke(new RawMouseWheelEventArgs(_mouse, timeStamp, _inputRoot, point.ToAvaloniaPoint(), new Vector(delta.X, delta.Y), (InputModifiers)modifiers));
break;
default:
Input?.Invoke(new RawMouseEventArgs(_mouse, timeStamp, _inputRoot, (RawMouseEventType)type, point.ToAvaloniaPoint(), (InputModifiers)modifiers));
break;
}
}
public void Resize(Size clientSize)
{
_native.Resize(clientSize.Width, clientSize.Height);
}
public IRenderer CreateRenderer(IRenderRoot root)
{
if (_deferredRendering)
return new DeferredRendererProxy(root, _gpu ? _native : null);
return new ImmediateRenderer(root);
}
public virtual void Dispose()
{
_native?.Close();
_native?.Dispose();
_native = null;
(Screen as ScreenImpl)?.Dispose();
}
public void Invalidate(Rect rect)
{
if (!_deferredRendering && _native != null)
_native.Invalidate(new AvnRect { Height = rect.Height, Width = rect.Width, X = rect.X, Y = rect.Y });
}
public void SetInputRoot(IInputRoot inputRoot)
{
_inputRoot = inputRoot;
}
public void Show()
{
_native.Show();
}
public Point Position
{
get => _native.GetPosition().ToAvaloniaPoint();
set => _native.SetPosition(value.ToAvnPoint());
}
public Point PointToClient(Point point)
{
return _native.PointToClient(point.ToAvnPoint()).ToAvaloniaPoint();
}
public Point PointToScreen(Point point)
{
return _native.PointToScreen(point.ToAvnPoint()).ToAvaloniaPoint();
}
public void Hide()
{
_native.Hide();
}
public void BeginMoveDrag()
{
_native.BeginMoveDrag();
}
public Size MaxClientSize => _native.GetMaxClientSize().ToAvaloniaSize();
public void SetTopmost(bool value)
{
_native.SetTopMost(value);
}
public double Scaling => _native.GetScaling();
public Action Deactivated { get; set; }
public Action Activated { get; set; }
public void SetCursor(IPlatformHandle cursor)
{
var newCursor = cursor as AvaloniaNativeCursor;
newCursor = newCursor ?? (_cursorFactory.GetCursor(StandardCursorType.Arrow) as AvaloniaNativeCursor);
_native.Cursor = newCursor.Cursor;
}
public Action<Point> PositionChanged { get; set; }
public Action<RawInputEventArgs> Input { get; set; }
Action<double> ScalingChanged { get; set; }
Action<double> ITopLevelImpl.ScalingChanged { get; set; }
public IScreenImpl Screen { get; private set; }
// TODO
public void SetMinMaxSize(Size minSize, Size maxSize)
{
_native.SetMinMaxSize(minSize.ToAvnSize(), maxSize.ToAvnSize());
}
public void BeginResizeDrag(WindowEdge edge)
{
}
public IPlatformHandle Handle => new PlatformHandle(IntPtr.Zero, "NOT SUPPORTED");
}
}

5
src/Avalonia.Native/headers/avalonia-native-guids.h

@ -0,0 +1,5 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
#define COM_GUIDS_MATERIALIZE
#include "avalonia-native.h"

363
src/Avalonia.Native/headers/avalonia-native.h

@ -0,0 +1,363 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
#include "com.h"
#include "key.h"
#define AVNCOM(name, id) COMINTERFACE(name, 2e2cda0a, 9ae5, 4f1b, 8e, 20, 08, 1a, 04, 27, 9f, id)
struct IAvnWindowEvents;
struct IAvnWindow;
struct IAvnPopup;
struct IAvnMacOptions;
struct IAvnPlatformThreadingInterface;
struct IAvnSystemDialogEvents;
struct IAvnSystemDialogs;
struct IAvnScreens;
struct IAvnClipboard;
struct IAvnCursor;
struct IAvnCursorFactory;
struct IAvnGlFeature;
struct IAvnGlContext;
struct IAvnGlDisplay;
struct IAvnGlSurfaceRenderTarget;
struct IAvnGlSurfaceRenderingSession;
struct AvnSize
{
double Width, Height;
};
struct AvnPixelSize
{
int Width, Height;
};
struct AvnRect
{
double X, Y, Width, Height;
};
struct AvnVector
{
double X, Y;
};
struct AvnPoint
{
double X, Y;
};
struct AvnScreen
{
AvnRect Bounds;
AvnRect WorkingArea;
bool Primary;
};
enum AvnPixelFormat
{
kAvnRgb565,
kAvnRgba8888,
kAvnBgra8888
};
struct AvnFramebuffer
{
void* Data;
int Width;
int Height;
int Stride;
AvnVector Dpi;
AvnPixelFormat PixelFormat;
};
struct AvnColor
{
unsigned char Alpha;
unsigned char Red;
unsigned char Green;
unsigned char Blue;
};
enum AvnRawMouseEventType
{
LeaveWindow,
LeftButtonDown,
LeftButtonUp,
RightButtonDown,
RightButtonUp,
MiddleButtonDown,
MiddleButtonUp,
Move,
Wheel,
NonClientLeftButtonDown
};
enum AvnRawKeyEventType
{
KeyDown,
KeyUp
};
enum AvnInputModifiers
{
AvnInputModifiersNone = 0,
Alt = 1,
Control = 2,
Shift = 4,
Windows = 8,
LeftMouseButton = 16,
RightMouseButton = 32,
MiddleMouseButton = 64
};
enum AvnWindowState
{
Normal,
Minimized,
Maximized,
};
enum AvnStandardCursorType
{
CursorArrow,
CursorIbeam,
CursorWait,
CursorCross,
CursorUpArrow,
CursorSizeWestEast,
CursorSizeNorthSouth,
CursorSizeAll,
CursorNo,
CursorHand,
CursorAppStarting,
CursorHelp,
CursorTopSide,
CursorBottomSize,
CursorLeftSide,
CursorRightSide,
CursorTopLeftCorner,
CursorTopRightCorner,
CursorBottomLeftCorner,
CursorBottomRightCorner,
CursorDragMove,
CursorDragCopy,
CursorDragLink,
};
enum AvnWindowEdge
{
WindowEdgeNorthWest,
WindowEdgeNorth,
WindowEdgeNorthEast,
WindowEdgeWest,
WindowEdgeEast,
WindowEdgeSouthWest,
WindowEdgeSouth,
WindowEdgeSouthEast
};
AVNCOM(IAvaloniaNativeFactory, 01) : IUnknown
{
public:
virtual HRESULT Initialize() = 0;
virtual IAvnMacOptions* GetMacOptions() = 0;
virtual HRESULT CreateWindow(IAvnWindowEvents* cb, IAvnWindow** ppv) = 0;
virtual HRESULT CreatePopup (IAvnWindowEvents* cb, IAvnPopup** ppv) = 0;
virtual HRESULT CreatePlatformThreadingInterface(IAvnPlatformThreadingInterface** ppv) = 0;
virtual HRESULT CreateSystemDialogs (IAvnSystemDialogs** ppv) = 0;
virtual HRESULT CreateScreens (IAvnScreens** ppv) = 0;
virtual HRESULT CreateClipboard(IAvnClipboard** ppv) = 0;
virtual HRESULT CreateCursorFactory(IAvnCursorFactory** ppv) = 0;
virtual HRESULT ObtainGlFeature(IAvnGlFeature** ppv) = 0;
};
AVNCOM(IAvnWindowBase, 02) : IUnknown
{
virtual HRESULT Show() = 0;
virtual HRESULT Hide () = 0;
virtual HRESULT Close() = 0;
virtual HRESULT Activate () = 0;
virtual HRESULT GetClientSize(AvnSize*ret) = 0;
virtual HRESULT GetMaxClientSize(AvnSize* ret) = 0;
virtual HRESULT GetScaling(double*ret)=0;
virtual HRESULT SetMinMaxSize(AvnSize minSize, AvnSize maxSize) = 0;
virtual HRESULT Resize(double width, double height) = 0;
virtual HRESULT Invalidate (AvnRect rect) = 0;
virtual HRESULT BeginMoveDrag () = 0;
virtual HRESULT BeginResizeDrag (AvnWindowEdge edge) = 0;
virtual HRESULT GetPosition (AvnPoint*ret) = 0;
virtual HRESULT SetPosition (AvnPoint point) = 0;
virtual HRESULT PointToClient (AvnPoint point, AvnPoint*ret) = 0;
virtual HRESULT PointToScreen (AvnPoint point, AvnPoint*ret) = 0;
virtual HRESULT ThreadSafeSetSwRenderedFrame(AvnFramebuffer* fb, IUnknown* dispose) = 0;
virtual HRESULT SetTopMost (bool value) = 0;
virtual HRESULT SetCursor(IAvnCursor* cursor) = 0;
virtual HRESULT CreateGlRenderTarget(IAvnGlSurfaceRenderTarget** ret) = 0;
virtual HRESULT GetSoftwareFramebuffer(AvnFramebuffer*ret) = 0;
virtual bool TryLock() = 0;
virtual void Unlock() = 0;
};
AVNCOM(IAvnPopup, 03) : virtual IAvnWindowBase
{
};
AVNCOM(IAvnWindow, 04) : virtual IAvnWindowBase
{
virtual HRESULT ShowDialog (IUnknown**ppv) = 0;
virtual HRESULT SetCanResize(bool value) = 0;
virtual HRESULT SetHasDecorations(bool value) = 0;
virtual HRESULT SetTitle (const char* title) = 0;
virtual HRESULT SetTitleBarColor (AvnColor color) = 0;
virtual HRESULT SetWindowState(AvnWindowState state) = 0;
virtual HRESULT GetWindowState(AvnWindowState*ret) = 0;
};
AVNCOM(IAvnWindowBaseEvents, 05) : IUnknown
{
virtual HRESULT Paint() = 0;
virtual void Closed() = 0;
virtual void Activated() = 0;
virtual void Deactivated() = 0;
virtual void Resized(const AvnSize& size) = 0;
virtual void PositionChanged (AvnPoint position) = 0;
virtual void RawMouseEvent (AvnRawMouseEventType type,
unsigned int timeStamp,
AvnInputModifiers modifiers,
AvnPoint point,
AvnVector delta) = 0;
virtual bool RawKeyEvent (AvnRawKeyEventType type, unsigned int timeStamp, AvnInputModifiers modifiers, unsigned int key) = 0;
virtual bool RawTextInputEvent (unsigned int timeStamp, const char* text) = 0;
virtual void ScalingChanged(double scaling) = 0;
virtual void RunRenderPriorityJobs() = 0;
};
AVNCOM(IAvnWindowEvents, 06) : IAvnWindowBaseEvents
{
/**
* Closing Event
* Called when the user presses the OS window close button.
* return true to allow the close, return false to prevent close.
*/
virtual bool Closing () = 0;
virtual void WindowStateChanged (AvnWindowState state) = 0;
};
AVNCOM(IAvnMacOptions, 07) : IUnknown
{
virtual HRESULT SetShowInDock(int show) = 0;
};
AVNCOM(IAvnActionCallback, 08) : IUnknown
{
virtual void Run() = 0;
};
AVNCOM(IAvnSignaledCallback, 09) : IUnknown
{
virtual void Signaled(int priority, bool priorityContainsMeaningfulValue) = 0;
};
AVNCOM(IAvnLoopCancellation, 0a) : IUnknown
{
virtual void Cancel() = 0;
};
AVNCOM(IAvnPlatformThreadingInterface, 0b) : IUnknown
{
virtual bool GetCurrentThreadIsLoopThread() = 0;
virtual void SetSignaledCallback(IAvnSignaledCallback* cb) = 0;
virtual IAvnLoopCancellation* CreateLoopCancellation() = 0;
virtual void RunLoop(IAvnLoopCancellation* cancel) = 0;
// Can't pass int* to sharpgentools for some reason
virtual void Signal(int priority) = 0;
virtual IUnknown* StartTimer(int priority, int ms, IAvnActionCallback* callback) = 0;
};
AVNCOM(IAvnSystemDialogEvents, 0c) : IUnknown
{
virtual void OnCompleted (int numResults, void* ptrFirstResult) = 0;
};
AVNCOM(IAvnSystemDialogs, 0d) : IUnknown
{
virtual void SelectFolderDialog (IAvnWindow* parentWindowHandle,
IAvnSystemDialogEvents* events,
const char* title,
const char* initialPath) = 0;
virtual void OpenFileDialog (IAvnWindow* parentWindowHandle,
IAvnSystemDialogEvents* events,
bool allowMultiple,
const char* title,
const char* initialDirectory,
const char* initialFile,
const char* filters) = 0;
virtual void SaveFileDialog (IAvnWindow* parentWindowHandle,
IAvnSystemDialogEvents* events,
const char* title,
const char* initialDirectory,
const char* initialFile,
const char* filters) = 0;
};
AVNCOM(IAvnScreens, 0e) : IUnknown
{
virtual HRESULT GetScreenCount (int* ret) = 0;
virtual HRESULT GetScreen (int index, AvnScreen* ret) = 0;
};
AVNCOM(IAvnClipboard, 0f) : IUnknown
{
virtual HRESULT GetText (void** retOut) = 0;
virtual HRESULT SetText (char* text) = 0;
virtual HRESULT Clear() = 0;
};
AVNCOM(IAvnCursor, 10) : IUnknown
{
};
AVNCOM(IAvnCursorFactory, 11) : IUnknown
{
virtual HRESULT GetCursor (AvnStandardCursorType cursorType, IAvnCursor** retOut) = 0;
};
AVNCOM(IAvnGlFeature, 12) : IUnknown
{
virtual HRESULT ObtainDisplay(IAvnGlDisplay**retOut) = 0;
virtual HRESULT ObtainImmediateContext(IAvnGlContext**retOut) = 0;
};
AVNCOM(IAvnGlDisplay, 13) : IUnknown
{
virtual HRESULT GetSampleCount(int* ret) = 0;
virtual HRESULT GetStencilSize(int* ret) = 0;
virtual HRESULT ClearContext() = 0;
virtual void* GetProcAddress(char* proc) = 0;
};
AVNCOM(IAvnGlContext, 14) : IUnknown
{
virtual HRESULT MakeCurrent() = 0;
};
AVNCOM(IAvnGlSurfaceRenderTarget, 15) : IUnknown
{
virtual HRESULT BeginDrawing(IAvnGlSurfaceRenderingSession** ret) = 0;
};
AVNCOM(IAvnGlSurfaceRenderingSession, 16) : IUnknown
{
virtual HRESULT GetPixelSize(AvnPixelSize* ret) = 0;
virtual HRESULT GetScaling(double* ret) = 0;
};
extern "C" IAvaloniaNativeFactory* CreateAvaloniaNative();

57
src/Avalonia.Native/headers/com.h

@ -0,0 +1,57 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
#pragma clang diagnostic push
#pragma ide diagnostic ignored "OCUnusedGlobalDeclarationInspection"
#ifndef COM_H_INCLUDED
#define COM_H_INCLUDED
typedef struct _GUID {
unsigned int Data1;
unsigned short Data2;
unsigned short Data3;
unsigned char Data4[ 8 ];
} GUID;
typedef GUID IID;
typedef const IID* REFIID;
typedef unsigned int HRESULT;
typedef unsigned int DWORD;
typedef DWORD ULONG;
#define STDMETHODCALLTYPE
#define S_OK 0x0L
#define E_NOTIMPL 0x80004001L
#define E_NOINTERFACE 0x80004002L
#define E_POINTER 0x80004003L
#define E_ABORT 0x80004004L
#define E_FAIL 0x80004005L
#define E_UNEXPECTED 0x8000FFFFL
#define E_HANDLE 0x80070006L
#define E_INVALIDARG 0x80070057L
struct IUnknown
{
virtual HRESULT STDMETHODCALLTYPE QueryInterface(
REFIID riid,
void **ppvObject) = 0;
virtual ULONG STDMETHODCALLTYPE AddRef( void) = 0;
virtual ULONG STDMETHODCALLTYPE Release( void) = 0;
};
#ifdef COM_GUIDS_MATERIALIZE
#define __IID_DEF(name,d1,d2,d3, d41, d42, d43, d44, d45, d46, d47, d48) extern "C" const GUID IID_ ## name = {0x ## d1, 0x ## d2, 0x ## d3, \
{0x ## d41, 0x ## d42, 0x ## d42, 0x ## d42, 0x ## d42, 0x ## d42, 0x ## d42, 0x ## d42 } };
#else
#define __IID_DEF(name,d1,d2,d3, d41, d42, d43, d44, d45, d46, d47, d48) extern "C" const GUID IID_ ## name;
#endif
#define COMINTERFACE(name,d1,d2,d3, d41, d42, d43, d44, d45, d46, d47, d48) __IID_DEF(name,d1,d2,d3, d41, d42, d43, d44, d45, d46, d47, d48) \
struct __attribute__((annotate("uuid(" #d1 "-" #d2 "-" #d3 "-" #d41 #d42 "-" #d43 #d44 #d45 #d46 #d47 #d48 ")" ))) name
#endif // COM_H_INCLUDED
#pragma clang diagnostic pop

183
src/Avalonia.Native/headers/comimpl.h

@ -0,0 +1,183 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
#include "com.h"
#pragma clang diagnostic push
#pragma ide diagnostic ignored "OCUnusedGlobalDeclarationInspection"
#ifndef COMIMPL_H_INCLUDED
#define COMIMPL_H_INCLUDED
#include <cstring>
__IID_DEF(IUnknown, 0, 0, 0, C0, 00, 00, 00, 00, 00, 00, 46);
class ComObject : public virtual IUnknown
{
private:
unsigned int _refCount;
public:
virtual ULONG AddRef()
{
_refCount++;
return _refCount;
}
virtual ULONG Release()
{
_refCount--;
ULONG rv = _refCount;
if(_refCount == 0)
delete(this);
return rv;
}
ComObject()
{
_refCount = 1;
}
virtual ~ComObject()
{
}
virtual ::HRESULT STDMETHODCALLTYPE QueryInterfaceImpl(REFIID riid, void **ppvObject) = 0;
virtual ::HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid,
void **ppvObject)
{
if(0 == memcmp(riid, &IID_IUnknown, sizeof(GUID)))
*ppvObject = (IUnknown*)this;
else
{
auto rv = QueryInterfaceImpl(riid, ppvObject);
if(rv != S_OK)
return rv;
}
_refCount++;
return S_OK;
}
};
#define FORWARD_IUNKNOWN() \
virtual ULONG Release(){ \
return ComObject::Release(); \
} \
virtual ULONG AddRef() \
{ \
return ComObject::AddRef(); \
} \
virtual HRESULT QueryInterface(REFIID riid, void **ppvObject) \
{ \
return ComObject::QueryInterface(riid, ppvObject); \
}
#define BEGIN_INTERFACE_MAP() public: virtual HRESULT STDMETHODCALLTYPE QueryInterfaceImpl(REFIID riid, void **ppvObject){
#define INTERFACE_MAP_ENTRY(TInterface, IID) if(0 == memcmp(riid, &IID, sizeof(GUID))) { TInterface* casted = this; *ppvObject = casted; return S_OK; }
#define END_INTERFACE_MAP() return E_NOINTERFACE; }
#define INHERIT_INTERFACE_MAP(TBase) if(TBase::QueryInterfaceImpl(riid, ppvObject) == S_OK) return S_OK;
class ComUnknownObject : public ComObject
{
public:
FORWARD_IUNKNOWN()
virtual ::HRESULT STDMETHODCALLTYPE QueryInterfaceImpl(REFIID riid, void **ppvObject) override
{
return E_NOINTERFACE;
};
virtual ~ComUnknownObject(){}
};
template<class TInterface, GUID const* TIID> class ComSingleObject : public ComObject, public virtual TInterface
{
BEGIN_INTERFACE_MAP()
INTERFACE_MAP_ENTRY(TInterface, *TIID)
END_INTERFACE_MAP()
public:
virtual ~ComSingleObject(){}
};
template<class TInterface>
class ComPtr
{
private:
TInterface* _obj;
public:
ComPtr()
{
_obj = 0;
}
ComPtr(TInterface* pObj)
{
_obj = 0;
if (pObj)
{
_obj = pObj;
_obj->AddRef();
}
}
ComPtr(const ComPtr& ptr)
{
_obj = 0;
if (ptr._obj)
{
_obj = ptr._obj;
_obj->AddRef();
}
}
ComPtr& operator=(ComPtr other)
{
if(_obj != NULL)
_obj->Release();
_obj = other._obj;
if(_obj != NULL)
_obj->AddRef();
return *this;
}
~ComPtr()
{
if (_obj)
{
_obj->Release();
_obj = 0;
}
}
TInterface* getRaw()
{
return _obj;
}
operator TInterface*() const
{
return _obj;
}
TInterface& operator*() const
{
return *_obj;
}
TInterface** operator&()
{
return &_obj;
}
TInterface* operator->() const
{
return _obj;
}
};
#endif // COMIMPL_H_INCLUDED
#pragma clang diagnostic pop

1023
src/Avalonia.Native/headers/key.h

File diff suppressed because it is too large

2
src/Avalonia.Native/regen.sh

@ -0,0 +1,2 @@
#!/bin/sh
dotnet msbuild /t:Clean,GenerateSharpGenBindings

34
src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs

@ -6,6 +6,7 @@ using System.Reflection;
using System.Reactive.Linq;
using Avalonia;
using Avalonia.VisualTree;
using Avalonia.Controls;
using ReactiveUI;
namespace Avalonia
@ -20,18 +21,41 @@ namespace Avalonia
public IObservable<bool> GetActivationForView(IActivatable view)
{
if (!(view is IVisual visual)) return Observable.Return(false);
var viewLoaded = Observable
if (view is WindowBase window) return GetActivationForWindowBase(window);
return GetActivationForVisual(visual);
}
private IObservable<bool> GetActivationForWindowBase(WindowBase window)
{
var windowLoaded = Observable
.FromEventPattern(
x => window.Initialized += x,
x => window.Initialized -= x)
.Select(args => true);
var windowUnloaded = Observable
.FromEventPattern(
x => window.Closed += x,
x => window.Closed -= x)
.Select(args => false);
return windowLoaded
.Merge(windowUnloaded)
.DistinctUntilChanged();
}
private IObservable<bool> GetActivationForVisual(IVisual visual)
{
var visualLoaded = Observable
.FromEventPattern<VisualTreeAttachmentEventArgs>(
x => visual.AttachedToVisualTree += x,
x => visual.DetachedFromVisualTree -= x)
x => visual.AttachedToVisualTree -= x)
.Select(args => true);
var viewUnloaded = Observable
var visualUnloaded = Observable
.FromEventPattern<VisualTreeAttachmentEventArgs>(
x => visual.DetachedFromVisualTree += x,
x => visual.DetachedFromVisualTree -= x)
.Select(args => false);
return viewLoaded
.Merge(viewUnloaded)
return visualLoaded
.Merge(visualUnloaded)
.DistinctUntilChanged();
}
}

44
src/Avalonia.ReactiveUI/ReactiveUserControl.cs

@ -0,0 +1,44 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia;
using Avalonia.VisualTree;
using Avalonia.Controls;
using ReactiveUI;
namespace Avalonia
{
/// <summary>
/// A ReactiveUI UserControl that implements <see cref="IViewFor{TViewModel}"/>
/// and will activate your ViewModel automatically if it supports activation.
/// </summary>
/// <typeparam name="TViewModel">ViewModel type.</typeparam>
public class ReactiveUserControl<TViewModel> : UserControl, IViewFor<TViewModel> where TViewModel : class
{
public static readonly AvaloniaProperty<TViewModel> ViewModelProperty = AvaloniaProperty
.Register<ReactiveWindow<TViewModel>, TViewModel>(nameof(ViewModel));
/// <summary>
/// Initializes a new instance of the <see cref="ReactiveUserControl{TViewModel}"/> class.
/// </summary>
public ReactiveUserControl()
{
DataContextChanged += (sender, args) => ViewModel = DataContext as TViewModel;
}
/// <summary>
/// The ViewModel.
/// </summary>
public TViewModel ViewModel
{
get => GetValue(ViewModelProperty);
set => SetValue(ViewModelProperty, value);
}
object IViewFor.ViewModel
{
get => ViewModel;
set => ViewModel = (TViewModel)value;
}
}
}

44
src/Avalonia.ReactiveUI/ReactiveWindow.cs

@ -0,0 +1,44 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia;
using Avalonia.VisualTree;
using Avalonia.Controls;
using ReactiveUI;
namespace Avalonia
{
/// <summary>
/// A ReactiveUI Window that implements <see cref="IViewFor{TViewModel}"/>
/// and will activate your ViewModel automatically if it supports activation.
/// </summary>
/// <typeparam name="TViewModel">ViewModel type.</typeparam>
public class ReactiveWindow<TViewModel> : Window, IViewFor<TViewModel> where TViewModel : class
{
public static readonly AvaloniaProperty<TViewModel> ViewModelProperty = AvaloniaProperty
.Register<ReactiveWindow<TViewModel>, TViewModel>(nameof(ViewModel));
/// <summary>
/// Initializes a new instance of the <see cref="ReactiveWindow{TViewModel}"/> class.
/// </summary>
public ReactiveWindow()
{
DataContextChanged += (sender, args) => ViewModel = DataContext as TViewModel;
}
/// <summary>
/// The ViewModel.
/// </summary>
public TViewModel ViewModel
{
get => GetValue(ViewModelProperty);
set => SetValue(ViewModelProperty, value);
}
object IViewFor.ViewModel
{
get => ViewModel;
set => ViewModel = (TViewModel)value;
}
}
}

6
src/Avalonia.Remote.Protocol/InputMessages.cs

@ -75,4 +75,10 @@ namespace Avalonia.Remote.Protocol.Input
public Key Key { get; set; }
}
[AvaloniaRemoteMessageGuid("C174102E-7405-4594-916F-B10B8248A17D")]
public class TextInputEventMessage : InputEventMessageBase
{
public string Text { get; set; }
}
}

7
src/Avalonia.Remote.Protocol/ViewportMessages.cs

@ -37,6 +37,13 @@
public PixelFormat[] Formats { get; set; }
}
[AvaloniaRemoteMessageGuid("7A3c25d3-3652-438D-8EF1-86E942CC96C0")]
public class ClientRenderInfoMessage
{
public double DpiX { get; set; }
public double DpiY { get; set; }
}
[AvaloniaRemoteMessageGuid("68014F8A-289D-4851-8D34-5367EDA7F827")]
public class FrameReceivedMessage
{

2
src/Avalonia.Styling/Styling/IStyle.cs

@ -18,5 +18,7 @@ namespace Avalonia.Styling
/// The control that contains this style. May be null.
/// </param>
void Attach(IStyleable control, IStyleHost container);
void Detach();
}
}

52
src/Avalonia.Styling/Styling/Style.cs

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using Avalonia.Animation;
using Avalonia.Controls;
@ -15,9 +16,12 @@ namespace Avalonia.Styling
/// </summary>
public class Style : AvaloniaObject, IStyle, ISetStyleParent
{
private static Dictionary<IStyleable, List<IDisposable>> _applied =
new Dictionary<IStyleable, List<IDisposable>>();
private static Dictionary<IStyleable, CompositeDisposable> _applied =
new Dictionary<IStyleable, CompositeDisposable>();
private IResourceNode _parent;
private CompositeDisposable _subscriptions;
private IResourceDictionary _resources;
private IList<IAnimation> _animations;
@ -88,6 +92,14 @@ namespace Avalonia.Styling
}
}
private CompositeDisposable Subscriptions
{
get
{
return _subscriptions ?? (_subscriptions = new CompositeDisposable(2));
}
}
/// <inheritdoc/>
IResourceNode IResourceNode.ResourceParent => _parent;
@ -109,7 +121,9 @@ namespace Avalonia.Styling
if (match.ImmediateResult != false)
{
var subs = GetSubscriptions(control);
var controlSubscriptions = GetSubscriptions(control);
var subs = new CompositeDisposable(Setters.Count + Animations.Count);
if (control is Animatable animatable)
{
@ -132,17 +146,25 @@ namespace Avalonia.Styling
var sub = setter.Apply(this, control, match.ObservableResult);
subs.Add(sub);
}
controlSubscriptions.Add(subs);
Subscriptions.Add(subs);
}
}
else if (control == container)
{
var subs = GetSubscriptions(control);
var controlSubscriptions = GetSubscriptions(control);
var subs = new CompositeDisposable(Setters.Count);
foreach (var setter in Setters)
{
var sub = setter.Apply(this, control, null);
subs.Add(sub);
}
controlSubscriptions.Add(subs);
Subscriptions.Add(subs);
}
}
@ -183,16 +205,25 @@ namespace Avalonia.Styling
throw new InvalidOperationException("The Style already has a parent.");
}
if (parent == null)
{
Detach();
}
_parent = parent;
}
private static List<IDisposable> GetSubscriptions(IStyleable control)
public void Detach()
{
List<IDisposable> subscriptions;
_subscriptions?.Dispose();
_subscriptions = null;
}
if (!_applied.TryGetValue(control, out subscriptions))
private static CompositeDisposable GetSubscriptions(IStyleable control)
{
if (!_applied.TryGetValue(control, out var subscriptions))
{
subscriptions = new List<IDisposable>(2);
subscriptions = new CompositeDisposable(2);
subscriptions.Add(control.StyleDetach.Subscribe(ControlDetach));
_applied.Add(control, subscriptions);
}
@ -209,10 +240,7 @@ namespace Avalonia.Styling
{
var subscriptions = _applied[control];
foreach (var subscription in subscriptions)
{
subscription.Dispose();
}
subscriptions.Dispose();
_applied.Remove(control);
}

8
src/Avalonia.Styling/Styling/Styles.cs

@ -105,6 +105,14 @@ namespace Avalonia.Styling
}
}
public void Detach()
{
foreach (IStyle style in this)
{
style.Detach();
}
}
/// <inheritdoc/>
public bool TryGetResource(string key, out object value)
{

9
src/Avalonia.Styling/Styling/packages.config

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Rx-Core" version="2.2.5" targetFramework="portable-net45+win8" userInstalled="true" />
<package id="Rx-Interfaces" version="2.2.5" targetFramework="portable-net45+win8" userInstalled="true" />
<package id="Rx-Linq" version="2.2.5" targetFramework="portable-net45+win8" userInstalled="true" />
<package id="Rx-Main" version="2.2.5" targetFramework="portable-net45+win8" userInstalled="true" />
<package id="Rx-PlatformServices" version="2.2.5" targetFramework="portable-net45+win8" userInstalled="true" />
<package id="Splat" version="1.6.2" targetFramework="portable45-net45+win8" userInstalled="true" />
</packages>

28
src/Avalonia.Themes.Default/Accents/BaseLight.xaml

@ -9,34 +9,34 @@
<Color x:Key="ThemeAccentColor4">#33119EDA</Color>
<Color x:Key="ThemeBackgroundColor">#FFFFFFFF</Color>
<Color x:Key="ThemeBorderLightColor">#FFAAAAAA</Color>
<Color x:Key="ThemeBorderLowColor">#FFAAAAAA</Color>
<Color x:Key="ThemeBorderMidColor">#FF888888</Color>
<Color x:Key="ThemeBorderDarkColor">#FF333333</Color>
<Color x:Key="ThemeControlLightColor">#FFFFFFFF</Color>
<Color x:Key="ThemeBorderHighColor">#FF333333</Color>
<Color x:Key="ThemeControlLowColor">#FFFFFFFF</Color>
<Color x:Key="ThemeControlMidColor">#FFAAAAAA</Color>
<Color x:Key="ThemeControlDarkColor">#FF888888</Color>
<Color x:Key="ThemeControlHighColor">#FF888888</Color>
<Color x:Key="ThemeControlHighlightLowColor">#FFF0F0F0</Color>
<Color x:Key="ThemeControlHighlightMidColor">#FFD0D0D0</Color>
<Color x:Key="ThemeControlHighlightDarkColor">#FF808080</Color>
<Color x:Key="ThemeControlHighlightHighColor">#FF808080</Color>
<Color x:Key="ThemeForegroundColor">#FF000000</Color>
<Color x:Key="ThemeForegroundLightColor">#FF808080</Color>
<Color x:Key="ThemeForegroundLowColor">#FF808080</Color>
<Color x:Key="HighlightColor">#FF086F9E</Color>
<Color x:Key="ErrorColor">#FFFF0000</Color>
<Color x:Key="ErrorLightColor">#10FF0000</Color>
<Color x:Key="ErrorLowColor">#10FF0000</Color>
<SolidColorBrush x:Key="ThemeBackgroundBrush" Color="{DynamicResource ThemeBackgroundColor}"></SolidColorBrush>
<SolidColorBrush x:Key="ThemeBorderLightBrush" Color="{DynamicResource ThemeBorderLightColor}"></SolidColorBrush>
<SolidColorBrush x:Key="ThemeBorderLowBrush" Color="{DynamicResource ThemeBorderLowColor}"></SolidColorBrush>
<SolidColorBrush x:Key="ThemeBorderMidBrush" Color="{DynamicResource ThemeBorderMidColor}"></SolidColorBrush>
<SolidColorBrush x:Key="ThemeBorderDarkBrush" Color="{DynamicResource ThemeBorderDarkColor}"></SolidColorBrush>
<SolidColorBrush x:Key="ThemeControlLightBrush" Color="{DynamicResource ThemeControlLightColor}"></SolidColorBrush>
<SolidColorBrush x:Key="ThemeBorderHighBrush" Color="{DynamicResource ThemeBorderHighColor}"></SolidColorBrush>
<SolidColorBrush x:Key="ThemeControlLowBrush" Color="{DynamicResource ThemeControlLowColor}"></SolidColorBrush>
<SolidColorBrush x:Key="ThemeControlMidBrush" Color="{DynamicResource ThemeControlMidColor}"></SolidColorBrush>
<SolidColorBrush x:Key="ThemeControlDarkBrush" Color="{DynamicResource ThemeControlDarkColor}"></SolidColorBrush>
<SolidColorBrush x:Key="ThemeControlHighBrush" Color="{DynamicResource ThemeControlHighColor}"></SolidColorBrush>
<SolidColorBrush x:Key="ThemeControlHighlightLowBrush" Color="{DynamicResource ThemeControlHighlightLowColor}"></SolidColorBrush>
<SolidColorBrush x:Key="ThemeControlHighlightMidBrush" Color="{DynamicResource ThemeControlHighlightMidColor}"></SolidColorBrush>
<SolidColorBrush x:Key="ThemeControlHighlightDarkBrush" Color="{DynamicResource ThemeControlHighlightDarkColor}"></SolidColorBrush>
<SolidColorBrush x:Key="ThemeControlHighlightHighBrush" Color="{DynamicResource ThemeControlHighlightHighColor}"></SolidColorBrush>
<SolidColorBrush x:Key="ThemeForegroundBrush" Color="{DynamicResource ThemeForegroundColor}"></SolidColorBrush>
<SolidColorBrush x:Key="ThemeForegroundLightBrush" Color="{DynamicResource ThemeForegroundLightColor}"></SolidColorBrush>
<SolidColorBrush x:Key="ThemeForegroundLowBrush" Color="{DynamicResource ThemeForegroundLowColor}"></SolidColorBrush>
<SolidColorBrush x:Key="HighlightBrush" Color="{DynamicResource HighlightColor}"></SolidColorBrush>
<SolidColorBrush x:Key="ThemeAccentBrush" Color="{DynamicResource ThemeAccentColor}"></SolidColorBrush>
@ -44,7 +44,7 @@
<SolidColorBrush x:Key="ThemeAccentBrush3" Color="{DynamicResource ThemeAccentColor3}"></SolidColorBrush>
<SolidColorBrush x:Key="ThemeAccentBrush4" Color="{DynamicResource ThemeAccentColor4}"></SolidColorBrush>
<SolidColorBrush x:Key="ErrorBrush" Color="{DynamicResource ErrorColor}"></SolidColorBrush>
<SolidColorBrush x:Key="ErrorLightBrush" Color="{DynamicResource ErrorLightColor}"></SolidColorBrush>
<SolidColorBrush x:Key="ErrorLowBrush" Color="{DynamicResource ErrorLowColor}"></SolidColorBrush>
<Thickness x:Key="ThemeBorderThickness">2</Thickness>
<sys:Double x:Key="ThemeDisabledOpacity">0.5</sys:Double>

4
src/Avalonia.Themes.Default/Button.xaml

@ -1,7 +1,7 @@
<Styles xmlns="https://github.com/avaloniaui">
<Style Selector="Button">
<Setter Property="Background" Value="{DynamicResource ThemeControlMidBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderLightBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderLowBrush}"/>
<Setter Property="BorderThickness" Value="{DynamicResource ThemeBorderThickness}"/>
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
@ -26,7 +26,7 @@
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/>
</Style>
<Style Selector="Button:pressed /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ThemeControlDarkBrush}"/>
<Setter Property="Background" Value="{DynamicResource ThemeControlHighBrush}"/>
</Style>
<Style Selector="Button:disabled">
<Setter Property="Opacity" Value="{DynamicResource ThemeDisabledOpacity}"/>

2
src/Avalonia.Themes.Default/ButtonSpinner.xaml

@ -1,6 +1,6 @@
<Styles xmlns="https://github.com/avaloniaui">
<Style Selector="ButtonSpinner">
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderLightBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderLowBrush}"/>
<Setter Property="BorderThickness" Value="{DynamicResource ThemeBorderThickness}"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>

2
src/Avalonia.Themes.Default/Calendar.xaml

@ -9,7 +9,7 @@
<Style Selector="Calendar">
<!--<Setter Property="Focusable" Value="False" />-->
<Setter Property="BorderThickness" Value="{DynamicResource ThemeBorderThickness}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderDarkBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderHighBrush}" />
<Setter Property="Background" Value="{DynamicResource ThemeBackgroundBrush}" />
<Setter Property="HeaderBackground" Value="{DynamicResource ThemeAccentBrush2}" />
<Setter Property="Template">

4
src/Avalonia.Themes.Default/CalendarButton.xaml

@ -7,7 +7,7 @@
<Styles xmlns="https://github.com/avaloniaui">
<Style Selector="CalendarButton">
<Setter Property="Foreground" Value="{DynamicResource ThemeBorderDarkBrush}" />
<Setter Property="Foreground" Value="{DynamicResource ThemeBorderHighBrush}" />
<Setter Property="Background" Value="{DynamicResource ThemeAccentBrush2}" />
<Setter Property="FontSize" Value="{DynamicResource FontSizeSmall}" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
@ -68,7 +68,7 @@
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
</Style>
<Style Selector="CalendarButton:inactive /template/ ContentControl#Content">
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundLightBrush}"/>
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundLowBrush}"/>
</Style>

4
src/Avalonia.Themes.Default/CalendarDayButton.xaml

@ -100,10 +100,10 @@
<Style Selector="CalendarDayButton:inactive /template/ ContentControl#Content">
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundLightBrush}"/>
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundLowBrush}"/>
</Style>
<Style Selector="CalendarDayButton:today /template/ ContentControl#Content">
<Setter Property="Foreground" Value="{DynamicResource ThemeControlLightBrush}"/>
<Setter Property="Foreground" Value="{DynamicResource ThemeControlLowBrush}"/>
</Style>
<Style Selector="CalendarDayButton /template/ Path#BlackoutVisual">

2
src/Avalonia.Themes.Default/CalendarItem.xaml

@ -153,7 +153,7 @@
<Rectangle Name="DisabledVisual"
Stretch="Fill"
Fill="{DynamicResource ThemeControlLightBrush}"
Fill="{DynamicResource ThemeControlLowBrush}"
Opacity="{DynamicResource ThemeDisabledOpacity}"
Margin="0,2,0,2" />

2
src/Avalonia.Themes.Default/CheckBox.xaml

@ -50,7 +50,7 @@
<Setter Property="IsVisible" Value="False"/>
</Style>
<Style Selector="CheckBox:pointerover /template/ Border#border">
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderDarkBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderHighBrush}"/>
</Style>
<Style Selector="CheckBox /template/ Path#checkMark">
<Setter Property="IsVisible" Value="False"/>

4
src/Avalonia.Themes.Default/ContextMenu.xaml

@ -1,8 +1,8 @@
<Style xmlns="https://github.com/avaloniaui" Selector="ContextMenu">
<Setter Property="BorderBrush" Value="Gray"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Padding" Value="4,2"/>
<Setter Property="TextBlock.FontSize" Value="12" />
<Setter Property="TextBlock.FontSize" Value="{DynamicResource FontSizeNormal}" />
<Setter Property="TextBlock.FontWeight" Value="Normal" />
<Setter Property="Template">
<ControlTemplate>

2
src/Avalonia.Themes.Default/DataValidationErrors.xaml

@ -24,7 +24,7 @@
Background="Transparent">
<Canvas.Styles>
<Style Selector="ToolTip">
<Setter Property="Background" Value="{DynamicResource ErrorLightBrush}"/>
<Setter Property="Background" Value="{DynamicResource ErrorLowBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ErrorBrush}"/>
</Style>
</Canvas.Styles>

12
src/Avalonia.Themes.Default/DatePicker.xaml

@ -29,7 +29,7 @@
HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="0"
Background="{DynamicResource ThemeControlLightBrush}"
Background="{DynamicResource ThemeControlLowBrush}"
ColumnDefinitions="*,*,*,*"
RowDefinitions="23*,19*,19*,19*"
ClipToBounds="False">
@ -47,12 +47,12 @@
Grid.Row="1"
Grid.RowSpan="3"
BorderThickness="1"
BorderBrush="{DynamicResource ThemeBorderDarkBrush}"
BorderBrush="{DynamicResource ThemeBorderHighBrush}"
CornerRadius=".5" />
<Rectangle Grid.ColumnSpan="4"
Grid.RowSpan="1"
StrokeThickness="1"
Stroke="{DynamicResource ThemeBorderDarkBrush}"
Stroke="{DynamicResource ThemeBorderHighBrush}"
Fill="{DynamicResource ThemeAccentBrush}">
</Rectangle>
<TextBlock Margin="0,-1,0,0"
@ -63,13 +63,13 @@
Grid.ColumnSpan="4"
Grid.RowSpan="3"
FontSize="{DynamicResource FontSizeSmall}"
Foreground="{DynamicResource ThemeBorderDarkBrush}">
Foreground="{DynamicResource ThemeBorderHighBrush}">
<TextBlock.Text>
<Binding Source="{x:Static sys:DateTime.Today}" Path="Day"/>
</TextBlock.Text>
</TextBlock>
<Ellipse HorizontalAlignment="Center" VerticalAlignment="Center" Fill="{DynamicResource ThemeControlLightBrush}" StrokeThickness="0" Grid.ColumnSpan="4" Width="3" Height="3"/>
<Ellipse HorizontalAlignment="Center" VerticalAlignment="Center" Fill="{DynamicResource ThemeControlLowBrush}" StrokeThickness="0" Grid.ColumnSpan="4" Width="3" Height="3"/>
</Grid>
</ControlTemplate>
</Setter>
@ -123,7 +123,7 @@
</Style>
<Style Selector="DatePicker:focus /template/ TextBox#PART_TextBox">
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderDarkBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderHighBrush}"/>
</Style>
</Styles>

2
src/Avalonia.Themes.Default/DropDown.xaml

@ -52,6 +52,6 @@
</Setter>
</Style>
<Style Selector="DropDown:pointerover /template/ Border#border">
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderDarkBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderHighBrush}"/>
</Style>
</Styles>

4
src/Avalonia.Themes.Default/Expander.xaml

@ -89,7 +89,7 @@
<Border BorderThickness="1">
<Grid ColumnDefinitions="Auto,Auto">
<Border Grid.Column="0" Width="20" Height="20" HorizontalAlignment="Center" VerticalAlignment="Center">
<Path Fill="Black"
<Path Fill="{DynamicResource ThemeForegroundBrush}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M 0 2 L 4 6 L 0 10 Z" />
@ -108,7 +108,7 @@
</Setter>
</Style>
<Style Selector="Expander /template/ ToggleButton#PART_toggle:pointerover /template/ Border">
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderLightBrush}" />
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderLowBrush}" />
</Style>
<Style Selector="Expander:down:expanded /template/ ToggleButton#PART_toggle /template/ Path">
<Setter Property="RenderTransform">

4
src/Avalonia.Themes.Default/GridSplitter.xaml

@ -1,7 +1,7 @@
<Styles xmlns="https://github.com/avaloniaui">
<Style Selector="GridSplitter:vertical">
<Setter Property="Width" Value="6"/>
<Setter Property="Background" Value="{DynamicResource ThemeControlLightBrush}"/>
<Setter Property="Background" Value="{DynamicResource ThemeControlLowBrush}"/>
<Setter Property="Template">
<ControlTemplate>
<Border Background="{TemplateBinding Background}">
@ -25,7 +25,7 @@
</Style>
<Style Selector="GridSplitter:horizontal">
<Setter Property="Height" Value="6"/>
<Setter Property="Background" Value="{DynamicResource ThemeControlLightBrush}"/>
<Setter Property="Background" Value="{DynamicResource ThemeControlLowBrush}"/>
<Setter Property="Template">
<ControlTemplate>
<Border Background="{TemplateBinding Background}">

2
src/Avalonia.Themes.Default/NumericUpDown.xaml

@ -1,6 +1,6 @@
<Styles xmlns="https://github.com/avaloniaui">
<Style Selector="NumericUpDown">
<Setter Property="TemplatedControl.BorderBrush" Value="{DynamicResource ThemeBorderLightBrush}"/>
<Setter Property="TemplatedControl.BorderBrush" Value="{DynamicResource ThemeBorderLowBrush}"/>
<Setter Property="TemplatedControl.BorderThickness" Value="{DynamicResource ThemeBorderThickness}"/>
<Setter Property="TemplatedControl.Background" Value="{DynamicResource ThemeBackgroundBrush}" />
<Setter Property="TemplatedControl.Foreground" Value="{DynamicResource ThemeForegroundBrush}" />

2
src/Avalonia.Themes.Default/RadioButton.xaml

@ -39,7 +39,7 @@
</Setter>
</Style>
<Style Selector="RadioButton:pointerover /template/ Ellipse#border">
<Setter Property="Stroke" Value="{DynamicResource ThemeBorderDarkBrush}"/>
<Setter Property="Stroke" Value="{DynamicResource ThemeBorderHighBrush}"/>
</Style>
<Style Selector="RadioButton /template/ Ellipse#checkMark">
<Setter Property="IsVisible" Value="False"/>

4
src/Avalonia.Themes.Default/RepeatButton.xaml

@ -3,7 +3,7 @@
<Setter Property="Background"
Value="{DynamicResource ThemeControlMidBrush}" />
<Setter Property="BorderBrush"
Value="{DynamicResource ThemeBorderLightBrush}" />
Value="{DynamicResource ThemeBorderLowBrush}" />
<Setter Property="BorderThickness"
Value="{DynamicResource ThemeBorderThickness}" />
<Setter Property="Foreground"
@ -35,7 +35,7 @@
</Style>
<Style Selector="RepeatButton:pressed /template/ ContentPresenter">
<Setter Property="Background"
Value="{DynamicResource ThemeControlDarkBrush}" />
Value="{DynamicResource ThemeControlHighBrush}" />
</Style>
<Style Selector="RepeatButton:disabled">
<Setter Property="Opacity"

12
src/Avalonia.Themes.Default/ScrollBar.xaml

@ -10,7 +10,7 @@
Grid.Column="0">
<Path Data="M 0,4 C0,4 0,6 0,6 0,6 3.5,2.5 3.5,2.5 3.5,2.5 7,6 7,6 7,6 7,4 7,4 7,4 3.5,0.5 3.5,0.5 3.5,0.5 0,4 0,4 z"
Stretch="Uniform"
Fill="{DynamicResource ThemeForegroundLightBrush}" />
Fill="{DynamicResource ThemeForegroundLowBrush}" />
</RepeatButton>
<Track Grid.Row="1"
Grid.Column="1"
@ -30,7 +30,7 @@
<Thumb Name="thumb">
<Thumb.Template>
<ControlTemplate>
<Border Background="{DynamicResource ThemeControlDarkBrush}" />
<Border Background="{DynamicResource ThemeControlHighBrush}" />
</ControlTemplate>
</Thumb.Template>
</Thumb>
@ -41,7 +41,7 @@
Grid.Column="2">
<Path Data="M 0,2.5 C0,2.5 0,0.5 0,0.5 0,0.5 3.5,4 3.5,4 3.5,4 7,0.5 7,0.5 7,0.5 7,2.5 7,2.5 7,2.5 3.5,6 3.5,6 3.5,6 0,2.5 0,2.5 z"
Stretch="Uniform"
Fill="{DynamicResource ThemeForegroundLightBrush}" />
Fill="{DynamicResource ThemeForegroundLowBrush}" />
</RepeatButton>
</Grid>
</Border>
@ -61,7 +61,7 @@
Grid.Column="0">
<Path Data="M 3.18,7 C3.18,7 5,7 5,7 5,7 1.81,3.5 1.81,3.5 1.81,3.5 5,0 5,0 5,0 3.18,0 3.18,0 3.18,0 0,3.5 0,3.5 0,3.5 3.18,7 3.18,7 z"
Stretch="Uniform"
Fill="{DynamicResource ThemeForegroundLightBrush}" />
Fill="{DynamicResource ThemeForegroundLowBrush}" />
</RepeatButton>
<Track Grid.Row="1"
Grid.Column="1"
@ -81,7 +81,7 @@
<Thumb Name="thumb">
<Thumb.Template>
<ControlTemplate>
<Border Background="{DynamicResource ThemeControlDarkBrush}" />
<Border Background="{DynamicResource ThemeControlHighBrush}" />
</ControlTemplate>
</Thumb.Template>
</Thumb>
@ -92,7 +92,7 @@
Grid.Column="2">
<Path Data="M 1.81,7 C1.81,7 0,7 0,7 0,7 3.18,3.5 3.18,3.5 3.18,3.5 0,0 0,0 0,0 1.81,0 1.81,0 1.81,0 5,3.5 5,3.5 5,3.5 1.81,7 1.81,7 z"
Stretch="Uniform"
Fill="{DynamicResource ThemeForegroundLightBrush}" />
Fill="{DynamicResource ThemeForegroundLowBrush}" />
</RepeatButton>
</Grid>
</Border>

4
src/Avalonia.Themes.Default/Slider.xaml

@ -76,11 +76,11 @@
</Style>
<Style Selector="Slider /template/ Border#TrackBackground">
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderLightBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderLowBrush}"/>
</Style>
<Style Selector="Slider /template/ RepeatButton.repeattrack">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="{DynamicResource ThemeBorderLightBrush}"/>
<Setter Property="Foreground" Value="{DynamicResource ThemeBorderLowBrush}"/>
<Setter Property="Template">
<ControlTemplate>
<Border Background="{TemplateBinding Background}" />

2
src/Avalonia.Themes.Default/TabStripItem.xaml

@ -2,7 +2,7 @@
<Style Selector="TabStripItem">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="FontSize" Value="{DynamicResource FontSizeLarge}"/>
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundLightBrush}"/>
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundLowBrush}"/>
<Setter Property="Template">
<ControlTemplate>
<ContentPresenter Name="PART_ContentPresenter"

4
src/Avalonia.Themes.Default/TextBox.xaml

@ -54,10 +54,10 @@
</Setter>
</Style>
<Style Selector="TextBox:pointerover /template/ Border#border">
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderDarkBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderHighBrush}"/>
</Style>
<Style Selector="TextBox:focus /template/ Border#border">
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderDarkBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderHighBrush}"/>
</Style>
<Style Selector="TextBox:error /template/ Border#border">
<Setter Property="BorderBrush" Value="{DynamicResource ErrorBrush}"/>

6
src/Avalonia.Themes.Default/ToggleButton.xaml

@ -1,7 +1,7 @@
<Styles xmlns="https://github.com/avaloniaui">
<Style Selector="ToggleButton">
<Setter Property="Background" Value="{DynamicResource ThemeControlMidBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderLightBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderLowBrush}"/>
<Setter Property="BorderThickness" Value="{DynamicResource ThemeBorderThickness}"/>
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
<Setter Property="Padding" Value="4"/>
@ -23,14 +23,14 @@
</Setter>
</Style>
<Style Selector="ToggleButton:checked /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ThemeControlDarkBrush}"/>
<Setter Property="Background" Value="{DynamicResource ThemeControlHighBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/>
</Style>
<Style Selector="ToggleButton:pointerover /template/ ContentPresenter">
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/>
</Style>
<Style Selector="ToggleButton:pressed /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ThemeControlDarkBrush}"/>
<Setter Property="Background" Value="{DynamicResource ThemeControlHighBrush}"/>
</Style>
<Style Selector="ToggleButton:disabled">
<Setter Property="Opacity" Value="{DynamicResource ThemeDisabledOpacity}"/>

3
src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs

@ -66,7 +66,7 @@ namespace Avalonia.Gtk3
DisplayClassName =
Utf8Buffer.StringFromPtr(Native.GTypeName(Marshal.ReadIntPtr(Marshal.ReadIntPtr(disp))));
using (var utf = new Utf8Buffer("avalonia.app." + Guid.NewGuid()))
using (var utf = new Utf8Buffer($"avalonia.app.a{Guid.NewGuid().ToString("N")}"))
App = Native.GtkApplicationNew(utf, 0);
//Mark current thread as UI thread
s_tlsMarker = true;
@ -81,6 +81,7 @@ namespace Avalonia.Gtk3
.Bind<ISystemDialogImpl>().ToSingleton<SystemDialog>()
.Bind<IRenderLoop>().ToConstant(new RenderLoop())
.Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60))
.Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>()
.Bind<IPlatformIconLoader>().ToConstant(new PlatformIconLoader());
if (useGpu)
EglGlPlatformFeature.TryInitialize();

36
src/Gtk/Avalonia.Gtk3/KeyTransform.cs

@ -15,7 +15,7 @@ namespace Avalonia.Gtk.Common
{ GdkKey.Clear, Key.Clear },
{ GdkKey.Return, Key.Return },
{ GdkKey.Pause, Key.Pause },
//{ GdkKey.?, Key.CapsLock }
{ GdkKey.Caps_Lock, Key.CapsLock },
//{ GdkKey.?, Key.HangulMode }
//{ GdkKey.?, Key.JunjaMode }
//{ GdkKey.?, Key.FinalMode }
@ -27,7 +27,7 @@ namespace Avalonia.Gtk.Common
//{ GdkKey.?, Key.ImeModeChange }
{ GdkKey.space, Key.Space },
{ GdkKey.Prior, Key.Prior },
//{ GdkKey.?, Key.PageDown }
{ GdkKey.Page_Down, Key.PageDown },
{ GdkKey.End, Key.End },
{ GdkKey.KP_End, Key.End },
{ GdkKey.Home, Key.Home },
@ -114,16 +114,16 @@ namespace Avalonia.Gtk.Common
//{ 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.KP_0, Key.NumPad0 },
{ GdkKey.KP_1, Key.NumPad1 },
{ GdkKey.KP_2, Key.NumPad2 },
{ GdkKey.KP_3, Key.NumPad3 },
{ GdkKey.KP_4, Key.NumPad4 },
{ GdkKey.KP_5, Key.NumPad5 },
{ GdkKey.KP_6, Key.NumPad6 },
{ GdkKey.KP_7, Key.NumPad7 },
{ GdkKey.KP_8, Key.NumPad8 },
{ GdkKey.KP_9, Key.NumPad9 },
{ GdkKey.multiply, Key.Multiply },
//{ GdkKey.?, Key.Add }
//{ GdkKey.?, Key.Separator }
@ -156,12 +156,12 @@ namespace Avalonia.Gtk.Common
{ GdkKey.R4, Key.F24 },
{ GdkKey.Num_Lock, Key.NumLock },
{ GdkKey.Scroll_Lock, Key.Scroll },
//{ GdkKey.?, Key.LeftShift }
//{ GdkKey.?, Key.RightShift }
//{ GdkKey.?, Key.LeftCtrl }
//{ GdkKey.?, Key.RightCtrl }
//{ GdkKey.?, Key.LeftAlt }
//{ GdkKey.?, Key.RightAlt }
{ GdkKey.Shift_L, Key.LeftShift },
{ GdkKey.Shift_R, Key.RightShift },
{ GdkKey.Control_L, Key.LeftCtrl },
{ GdkKey.Control_R, Key.RightCtrl },
{ GdkKey.Alt_L, Key.LeftAlt },
{ GdkKey.Alt_R, Key.RightAlt },
//{ GdkKey.?, Key.BrowserBack }
//{ GdkKey.?, Key.BrowserForward }
//{ GdkKey.?, Key.BrowserRefresh }

2
src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs

@ -126,7 +126,7 @@ namespace Avalonia.Gtk3
if (state.HasFlag(GdkModifierType.ShiftMask))
rv |= InputModifiers.Shift;
if (state.HasFlag(GdkModifierType.Mod1Mask))
rv |= InputModifiers.Control;
rv |= InputModifiers.Alt;
if (state.HasFlag(GdkModifierType.Button1Mask))
rv |= InputModifiers.LeftMouseButton;
if (state.HasFlag(GdkModifierType.Button2Mask))

2
src/Linux/Avalonia.LinuxFramebuffer/LinuxFramebufferPlatform.cs

@ -5,6 +5,7 @@ using Avalonia.Controls;
using Avalonia.Controls.Embedding;
using Avalonia.Controls.Platform;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.LinuxFramebuffer;
using Avalonia.Platform;
using Avalonia.Rendering;
@ -36,6 +37,7 @@ namespace Avalonia.LinuxFramebuffer
.Bind<IPlatformSettings>().ToSingleton<PlatformSettings>()
.Bind<IPlatformThreadingInterface>().ToConstant(Threading)
.Bind<IRenderLoop>().ToConstant(new RenderLoop())
.Bind<PlatformHotkeyConfiguration>().ToSingleton<PlatformHotkeyConfiguration>()
.Bind<IRenderTimer>().ToConstant(Threading);
}

10
src/Markup/Avalonia.Markup.Xaml/AvaloniaXamlLoader.cs

@ -21,6 +21,12 @@ namespace Avalonia.Markup.Xaml
{
private readonly AvaloniaXamlSchemaContext _context = GetContext();
public bool IsDesignMode
{
get => _context.IsDesignMode;
set => _context.IsDesignMode = value;
}
private static AvaloniaXamlSchemaContext GetContext()
{
var result = AvaloniaLocator.Current.GetService<AvaloniaXamlSchemaContext>();
@ -200,7 +206,7 @@ namespace Avalonia.Markup.Xaml
internal static object LoadFromReader(XamlReader reader, AvaloniaXamlContext context = null, IAmbientProvider parentAmbientProvider = null)
{
var writer = AvaloniaXamlObjectWriter.Create(
reader.SchemaContext,
(AvaloniaXamlSchemaContext)reader.SchemaContext,
context,
parentAmbientProvider);
@ -234,4 +240,4 @@ namespace Avalonia.Markup.Xaml
public static T Parse<T>(string xaml, Assembly localAssembly = null)
=> (T)Parse(xaml, localAssembly);
}
}
}

37
src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlObjectWriter.cs

@ -4,13 +4,27 @@ using Portable.Xaml.ComponentModel;
using System.ComponentModel;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Avalonia.Controls;
using Portable.Xaml.Schema;
namespace Avalonia.Markup.Xaml.PortableXaml
{
public class AvaloniaXamlObjectWriter : XamlObjectWriter
class AvaloniaXamlObjectWriter : XamlObjectWriter
{
private static Dictionary<XamlDirective, string> DesignDirectives = new Dictionary<string, string>
{
["DataContext"] = "DataContext",
["DesignWidth"] = "Width", ["DesignHeight"] = "Height", ["PreviewWith"] = "PreviewWith"
}
.ToDictionary(p => new XamlDirective(
new[] {"http://schemas.microsoft.com/expression/blend/2008"}, p.Key,
XamlLanguage.Object, null, AllowedMemberLocations.Attribute), p => p.Value);
private readonly AvaloniaXamlSchemaContext _schemaContext;
public static AvaloniaXamlObjectWriter Create(
XamlSchemaContext schemaContext,
AvaloniaXamlSchemaContext schemaContext,
AvaloniaXamlContext context,
IAmbientProvider parentAmbientProvider = null)
{
@ -34,13 +48,14 @@ namespace Avalonia.Markup.Xaml.PortableXaml
private AvaloniaNameScope _nameScope;
private AvaloniaXamlObjectWriter(
XamlSchemaContext schemaContext,
AvaloniaXamlSchemaContext schemaContext,
XamlObjectWriterSettings settings,
AvaloniaNameScope nameScope,
IAmbientProvider parentAmbientProvider)
: base(schemaContext, settings, parentAmbientProvider)
{
_nameScope = nameScope;
_schemaContext = schemaContext;
}
protected override void Dispose(bool disposing)
@ -122,6 +137,20 @@ namespace Avalonia.Markup.Xaml.PortableXaml
(value as Avalonia.ISupportInitialize)?.EndInit();
}
public override void WriteStartMember(XamlMember property)
{
foreach(var d in DesignDirectives)
if (property == d.Key && _schemaContext.IsDesignMode)
{
base.WriteStartMember(new XamlMember(d.Value,
typeof(Design).GetMethod("Get" + d.Value, BindingFlags.Static | BindingFlags.Public),
typeof(Design).GetMethod("Set" + d.Value, BindingFlags.Static | BindingFlags.Public),
SchemaContext));
return;
}
base.WriteStartMember(property);
}
private class DelayedValuesHelper
{
private int _cnt;
@ -225,4 +254,4 @@ namespace Avalonia.Markup.Xaml.PortableXaml
}
}
}
}
}

18
src/Markup/Avalonia.Markup.Xaml/PortableXaml/AvaloniaXamlSchemaContext.cs

@ -15,6 +15,7 @@ namespace Avalonia.Markup.Xaml.PortableXaml
{
internal class AvaloniaXamlSchemaContext : XamlSchemaContext
{
public bool IsDesignMode { get; set; }
public static AvaloniaXamlSchemaContext Create(IRuntimeTypeProvider typeProvider = null)
{
return new AvaloniaXamlSchemaContext(typeProvider ?? new AvaloniaRuntimeTypeProvider());
@ -280,5 +281,20 @@ namespace Avalonia.Markup.Xaml.PortableXaml
return $"{MemberType}:{Type.Namespace}:{Type.Name}.{Member}";
}
}
public override bool TryGetCompatibleXamlNamespace(string xamlNamespace, out string compatibleNamespace)
{
//Forces XamlXmlReader to not ignore our namespace in design mode if mc:Ignorable is set
if (IsDesignMode &&
xamlNamespace == "http://schemas.microsoft.com/expression/blend/2008")
{
compatibleNamespace = xamlNamespace;
return true;
}
return base.TryGetCompatibleXamlNamespace(xamlNamespace, out compatibleNamespace);
}
}
}
}

10
src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs

@ -70,6 +70,14 @@ namespace Avalonia.Markup.Xaml.Styling
}
}
public void Detach()
{
if (Source != null)
{
Loaded.Detach();
}
}
/// <inheritdoc/>
public bool TryGetResource(string key, out object value) => Loaded.TryGetResource(key, out value);
@ -90,4 +98,4 @@ namespace Avalonia.Markup.Xaml.Styling
_parent = parent;
}
}
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save