зеркало из https://github.com/AvaloniaUI/Avalonia.git
Various fixes for GTK and DeferredRenderer support
This commit is contained in:
Родитель
2abd980713
Коммит
70c24908a9
|
@ -11,6 +11,7 @@ namespace ControlCatalog
|
|||
{
|
||||
this.InitializeComponent();
|
||||
this.AttachDevTools();
|
||||
//Renderer.DrawFps = true;
|
||||
//Renderer.DrawDirtyRects = Renderer.DrawFps = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,21 +44,26 @@ namespace Avalonia.Threading
|
|||
/// The job will be processed with the same priority as render.
|
||||
/// </summary>
|
||||
Render = 7,
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The job will be processed with the same priority as render.
|
||||
/// </summary>
|
||||
Layout = 8,
|
||||
|
||||
/// <summary>
|
||||
/// The job will be processed with the same priority as data binding.
|
||||
/// </summary>
|
||||
DataBind = 8,
|
||||
DataBind = 9,
|
||||
|
||||
/// <summary>
|
||||
/// The job will be processed with normal priority.
|
||||
/// </summary>
|
||||
Normal = 9,
|
||||
Normal = 10,
|
||||
|
||||
/// <summary>
|
||||
/// The job will be processed before other asynchronous operations.
|
||||
/// </summary>
|
||||
Send = 10,
|
||||
MaxValue = 10
|
||||
Send = 11,
|
||||
MaxValue = 11
|
||||
}
|
||||
}
|
||||
|
|
|
@ -203,7 +203,7 @@ namespace Avalonia.Layout
|
|||
{
|
||||
if (!_queued && !_running)
|
||||
{
|
||||
Dispatcher.UIThread.InvokeAsync(ExecuteLayoutPass, DispatcherPriority.Render);
|
||||
Dispatcher.UIThread.InvokeAsync(ExecuteLayoutPass, DispatcherPriority.Layout);
|
||||
_queued = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace Avalonia.Rendering
|
|||
/// </remarks>
|
||||
public class DefaultRenderLoop : IRenderLoop
|
||||
{
|
||||
private IPlatformThreadingInterface _threading;
|
||||
private IRuntimePlatform _runtime;
|
||||
private int _subscriberCount;
|
||||
private EventHandler<EventArgs> _tick;
|
||||
private IDisposable _subscription;
|
||||
|
@ -78,12 +78,12 @@ namespace Avalonia.Rendering
|
|||
/// </remarks>
|
||||
protected virtual IDisposable StartCore(Action tick)
|
||||
{
|
||||
if (_threading == null)
|
||||
if (_runtime == null)
|
||||
{
|
||||
_threading = AvaloniaLocator.Current.GetService<IPlatformThreadingInterface>();
|
||||
_runtime = AvaloniaLocator.Current.GetService<IRuntimePlatform>();
|
||||
}
|
||||
|
||||
return _threading.StartTimer(TimeSpan.FromSeconds(1.0 / FramesPerSecond), tick);
|
||||
return _runtime.StartSystemTimer(TimeSpan.FromSeconds(1.0 / FramesPerSecond), tick);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -23,11 +23,11 @@ namespace Avalonia.Gtk3
|
|||
|
||||
public ILockedFramebuffer Lock()
|
||||
{
|
||||
if(_window.CurrentCairoContext == IntPtr.Zero)
|
||||
throw new InvalidOperationException("Window is not in drawing state");
|
||||
var width = (int) _window.ClientSize.Width;
|
||||
var height = (int) _window.ClientSize.Height;
|
||||
return new ImageSurfaceFramebuffer(_window.CurrentCairoContext, _window.GtkWidget, width, height);
|
||||
// This method may be called from non-UI thread, don't touch anything that calls back to GTK/GDK
|
||||
var s = _window.ClientSize;
|
||||
var width = (int) s.Width;
|
||||
var height = (int) s.Height;
|
||||
return new ImageSurfaceFramebuffer(_window, width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ namespace Avalonia.Gtk3
|
|||
internal static readonly MouseDevice Mouse = new MouseDevice();
|
||||
internal static readonly KeyboardDevice Keyboard = new KeyboardDevice();
|
||||
internal static IntPtr App { get; set; }
|
||||
public static bool UseDeferredRendering = true;
|
||||
public static void Initialize()
|
||||
{
|
||||
Resolver.Resolve();
|
||||
|
@ -66,12 +67,17 @@ namespace Avalonia.Gtk3
|
|||
|
||||
public IDisposable StartTimer(TimeSpan interval, Action tick)
|
||||
{
|
||||
return GlibTimeout.StarTimer((uint) interval.TotalMilliseconds, tick);
|
||||
var msec = interval.TotalMilliseconds;
|
||||
if (msec <= 0)
|
||||
throw new ArgumentException("Don't know how to create a timer with zero or negative interval");
|
||||
var imsec = (uint) msec;
|
||||
if (imsec == 0)
|
||||
imsec = 1;
|
||||
return GlibTimeout.StarTimer(imsec, tick);
|
||||
}
|
||||
|
||||
private bool[] _signaled = new bool[(int) DispatcherPriority.MaxValue + 1];
|
||||
object _lock = new object();
|
||||
|
||||
public void Signal(DispatcherPriority prio)
|
||||
{
|
||||
var idx = (int) prio;
|
||||
|
@ -97,7 +103,6 @@ namespace Avalonia.Gtk3
|
|||
private static bool s_tlsMarker;
|
||||
|
||||
public bool CurrentThreadIsLoopThread => s_tlsMarker;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,10 +110,11 @@ namespace Avalonia
|
|||
{
|
||||
public static class Gtk3AppBuilderExtensions
|
||||
{
|
||||
public static T UseGtk3<T>(this AppBuilderBase<T> builder, ICustomGtk3NativeLibraryResolver resolver = null)
|
||||
public static T UseGtk3<T>(this AppBuilderBase<T> builder, bool deferredRendering = true, ICustomGtk3NativeLibraryResolver resolver = null)
|
||||
where T : AppBuilderBase<T>, new()
|
||||
{
|
||||
Resolver.Custom = resolver;
|
||||
Gtk3Platform.UseDeferredRendering = deferredRendering;
|
||||
return builder.UseWindowingSubsystem(Gtk3Platform.Initialize, "GTK3");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
using System;
|
||||
|
||||
namespace Avalonia.Gtk3
|
||||
{
|
||||
public interface IDeferredRenderOperation : IDisposable
|
||||
{
|
||||
void RenderNow();
|
||||
}
|
||||
}
|
|
@ -1,26 +1,28 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Controls.Platform.Surfaces;
|
||||
using Avalonia.Gtk3.Interop;
|
||||
using Avalonia.Platform;
|
||||
using Avalonia.Threading;
|
||||
|
||||
|
||||
namespace Avalonia.Gtk3
|
||||
{
|
||||
class ImageSurfaceFramebuffer : ILockedFramebuffer
|
||||
{
|
||||
private IntPtr _context;
|
||||
private readonly WindowBaseImpl _impl;
|
||||
private readonly GtkWidget _widget;
|
||||
private CairoSurface _surface;
|
||||
private int _factor;
|
||||
|
||||
public ImageSurfaceFramebuffer(IntPtr context, GtkWidget widget, int width, int height)
|
||||
private object _lock = new object();
|
||||
public ImageSurfaceFramebuffer(WindowBaseImpl impl, int width, int height)
|
||||
{
|
||||
_context = context;
|
||||
_widget = widget;
|
||||
_impl = impl;
|
||||
_widget = impl.GtkWidget;
|
||||
_factor = (int)(Native.GtkWidgetGetScaleFactor?.Invoke(_widget) ?? 1u);
|
||||
width *= _factor;
|
||||
height *= _factor;
|
||||
|
@ -32,18 +34,97 @@ namespace Avalonia.Gtk3
|
|||
RowBytes = Native.CairoImageSurfaceGetStride(_surface);
|
||||
Native.CairoSurfaceFlush(_surface);
|
||||
}
|
||||
|
||||
static void Draw(IntPtr context, CairoSurface surface, double factor)
|
||||
{
|
||||
|
||||
Native.CairoSurfaceMarkDirty(surface);
|
||||
Native.CairoScale(context, 1d / factor, 1d / factor);
|
||||
Native.CairoSetSourceSurface(context, surface, 0, 0);
|
||||
Native.CairoPaint(context);
|
||||
|
||||
}
|
||||
/*
|
||||
static Stopwatch St =Stopwatch.StartNew();
|
||||
private static int _frames;
|
||||
private static int _fps;*/
|
||||
static void DrawToWidget(GtkWidget widget, CairoSurface surface, int width, int height, double factor)
|
||||
{
|
||||
if(surface == null || widget.IsClosed)
|
||||
return;
|
||||
var window = Native.GtkWidgetGetWindow(widget);
|
||||
if(window == IntPtr.Zero)
|
||||
return;
|
||||
var rc = new GdkRectangle {Width = width, Height = height};
|
||||
Native.GdkWindowBeginPaintRect(window, ref rc);
|
||||
var context = Native.GdkCairoCreate(window);
|
||||
Draw(context, surface, factor);
|
||||
/*
|
||||
_frames++;
|
||||
var el = St.Elapsed;
|
||||
if (el.TotalSeconds > 1)
|
||||
{
|
||||
_fps = (int) (_frames / el.TotalSeconds);
|
||||
_frames = 0;
|
||||
St = Stopwatch.StartNew();
|
||||
}
|
||||
|
||||
Native.CairoSetSourceRgba(context, 1, 0, 0, 1);
|
||||
Native.CairoMoveTo(context, 20, 20);
|
||||
Native.CairoSetFontSize(context, 30);
|
||||
using (var txt = new Utf8Buffer("FPS: " + _fps))
|
||||
Native.CairoShowText(context, txt);
|
||||
*/
|
||||
|
||||
Native.CairoDestroy(context);
|
||||
Native.GdkWindowEndPaint(window);
|
||||
}
|
||||
|
||||
class RenderOp : IDeferredRenderOperation
|
||||
{
|
||||
private readonly GtkWidget _widget;
|
||||
private CairoSurface _surface;
|
||||
private readonly double _factor;
|
||||
private readonly int _width;
|
||||
private readonly int _height;
|
||||
|
||||
public RenderOp(GtkWidget widget, CairoSurface _surface, double factor, int width, int height)
|
||||
{
|
||||
_widget = widget;
|
||||
this._surface = _surface;
|
||||
_factor = factor;
|
||||
_width = width;
|
||||
_height = height;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_surface?.Dispose();
|
||||
_surface = null;
|
||||
}
|
||||
|
||||
public void RenderNow()
|
||||
{
|
||||
DrawToWidget(_widget, _surface, _width, _height, _factor);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if(_context == IntPtr.Zero || _surface == null)
|
||||
return;
|
||||
Native.CairoSurfaceMarkDirty(_surface);
|
||||
Native.CairoScale(_context, 1d / _factor, 1d / _factor);
|
||||
Native.CairoSetSourceSurface(_context, _surface, 0, 0);
|
||||
Native.CairoPaint(_context);
|
||||
_context = IntPtr.Zero;
|
||||
_surface.Dispose();
|
||||
_surface = null;
|
||||
lock (_lock)
|
||||
{
|
||||
if (Dispatcher.UIThread.CheckAccess())
|
||||
{
|
||||
if (_impl.CurrentCairoContext != IntPtr.Zero)
|
||||
Draw(_impl.CurrentCairoContext, _surface, _factor);
|
||||
else
|
||||
DrawToWidget(_widget, _surface, Width, Height, _factor);
|
||||
_surface.Dispose();
|
||||
}
|
||||
else
|
||||
_impl.SetNextRenderOperation(new RenderOp(_widget, _surface, _factor, Width, Height));
|
||||
_surface = null;
|
||||
}
|
||||
}
|
||||
|
||||
public IntPtr Address { get; }
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Avalonia.Gtk3.Interop
|
||||
|
|
|
@ -22,23 +22,25 @@ namespace Avalonia.Gtk3.Interop
|
|||
if (prio == DispatcherPriority.Send)
|
||||
return High;
|
||||
if (prio == DispatcherPriority.Normal)
|
||||
return Default - 4;
|
||||
if (prio == DispatcherPriority.DataBind)
|
||||
return Default - 3;
|
||||
if (prio == DispatcherPriority.Render)
|
||||
return Default -2;
|
||||
if (prio == DispatcherPriority.Loaded)
|
||||
return Default - 1;
|
||||
if (prio == DispatcherPriority.Input)
|
||||
return Default;
|
||||
if (prio == DispatcherPriority.DataBind)
|
||||
return Default + 1;
|
||||
if (prio == DispatcherPriority.Layout)
|
||||
return Default + 2;
|
||||
if (prio == DispatcherPriority.Render)
|
||||
return Default + 3;
|
||||
if (prio == DispatcherPriority.Loaded)
|
||||
return GtkPaint + 20;
|
||||
if (prio == DispatcherPriority.Input)
|
||||
return GtkPaint + 21;
|
||||
if (prio == DispatcherPriority.Background)
|
||||
return DefaultIdle;
|
||||
return DefaultIdle + 1;
|
||||
if (prio == DispatcherPriority.ContextIdle)
|
||||
return DefaultIdle + 100;
|
||||
return DefaultIdle + 2;
|
||||
if (prio == DispatcherPriority.ApplicationIdle)
|
||||
return DefaultIdle + 200;
|
||||
return DefaultIdle + 3;
|
||||
if (prio == DispatcherPriority.SystemIdle)
|
||||
return DefaultIdle + 200;
|
||||
return DefaultIdle + 4;
|
||||
throw new ArgumentException("Unknown priority");
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ using System.Linq;
|
|||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Threading;
|
||||
|
||||
namespace Avalonia.Gtk3.Interop
|
||||
{
|
||||
|
@ -33,7 +34,8 @@ namespace Avalonia.Gtk3.Interop
|
|||
public static void Add(int priority, uint interval, Func<bool> callback)
|
||||
{
|
||||
var handle = GCHandle.Alloc(callback);
|
||||
Native.GTimeoutAdd(interval, PinnedHandler, GCHandle.ToIntPtr(handle));
|
||||
//Native.GTimeoutAdd(interval, PinnedHandler, GCHandle.ToIntPtr(handle));
|
||||
Native.GTimeoutAddFull(priority, interval, PinnedHandler, GCHandle.ToIntPtr(handle), IntPtr.Zero);
|
||||
}
|
||||
|
||||
class Timer : IDisposable
|
||||
|
@ -48,8 +50,10 @@ namespace Avalonia.Gtk3.Interop
|
|||
|
||||
public static IDisposable StarTimer(uint interval, Action tick)
|
||||
{
|
||||
if (interval == 0)
|
||||
throw new ArgumentException("Don't know how to create a timer with zero or negative interval");
|
||||
var timer = new Timer ();
|
||||
GlibTimeout.Add(101, interval,
|
||||
GlibTimeout.Add(GlibPriority.FromDispatcherPriority(DispatcherPriority.Background), interval,
|
||||
() =>
|
||||
{
|
||||
if (timer.Stopped)
|
||||
|
|
|
@ -169,6 +169,9 @@ namespace Avalonia.Gtk3.Interop
|
|||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
|
||||
public delegate void cairo_surface_mark_dirty(CairoSurface surface);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
|
||||
public delegate void cairo_surface_write_to_png(CairoSurface surface, Utf8Buffer path);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
|
||||
public delegate void cairo_surface_flush(CairoSurface surface);
|
||||
|
@ -178,15 +181,36 @@ namespace Avalonia.Gtk3.Interop
|
|||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
|
||||
public delegate void cairo_set_source_surface(IntPtr cr, CairoSurface surface, double x, double y);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
|
||||
public delegate void cairo_set_source_rgba(IntPtr cr, double r, double g, double b, double a);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
|
||||
public delegate void cairo_scale(IntPtr context, double sx, double sy);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
|
||||
public delegate void cairo_paint(IntPtr context);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
|
||||
public delegate void cairo_show_text(IntPtr context, Utf8Buffer text);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
|
||||
public delegate void cairo_set_font_size(IntPtr context, double size);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
|
||||
public delegate void cairo_select_font_face(IntPtr context, Utf8Buffer face, int slant, int weight);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
|
||||
public delegate void cairo_move_to(IntPtr context, double x, double y);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
|
||||
public delegate void cairo_destroy(IntPtr context);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
|
||||
public delegate void gtk_widget_queue_draw_area(GtkWidget widget, int x, int y, int width, int height);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
|
||||
public delegate void gtk_widget_add_tick_callback(GtkWidget widget, TickCallback callback, IntPtr userData, IntPtr destroy);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
|
||||
public delegate GtkImContext gtk_im_multicontext_new();
|
||||
|
@ -238,6 +262,12 @@ namespace Avalonia.Gtk3.Interop
|
|||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
|
||||
public delegate void gdk_window_process_updates(IntPtr window, bool updateChildren);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
|
||||
public delegate void gdk_window_begin_paint_rect(IntPtr window, ref GdkRectangle rect);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
|
||||
public delegate void gdk_window_end_paint(IntPtr window);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
|
||||
public delegate void gdk_event_request_motions(IntPtr ev);
|
||||
|
@ -276,6 +306,9 @@ namespace Avalonia.Gtk3.Interop
|
|||
public delegate bool gdk_pixbuf_save_to_bufferv(Pixbuf pixbuf, out IntPtr buffer, out IntPtr buffer_size,
|
||||
Utf8Buffer type, IntPtr option_keys, IntPtr option_values, out IntPtr error);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
|
||||
public delegate IntPtr gdk_cairo_create(IntPtr window);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)]
|
||||
public delegate void g_object_unref(IntPtr instance);
|
||||
|
||||
|
@ -326,6 +359,11 @@ namespace Avalonia.Gtk3.Interop
|
|||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void GtkClipboardTextReceivedFunc(IntPtr clipboard, IntPtr utf8string, IntPtr userdata);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate bool TickCallback(IntPtr widget, IntPtr clock, IntPtr userdata);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public static D.gdk_display_get_n_screens GdkDisplayGetNScreens;
|
||||
|
@ -380,6 +418,7 @@ namespace Avalonia.Gtk3.Interop
|
|||
public static D.gtk_widget_set_events GtkWidgetSetEvents;
|
||||
public static D.gdk_window_invalidate_rect GdkWindowInvalidateRect;
|
||||
public static D.gtk_widget_queue_draw_area GtkWidgetQueueDrawArea;
|
||||
public static D.gtk_widget_add_tick_callback GtkWidgetAddTickCallback;
|
||||
public static D.gtk_widget_activate GtkWidgetActivate;
|
||||
public static D.gtk_clipboard_get_for_display GtkClipboardGetForDisplay;
|
||||
public static D.gtk_clipboard_request_text GtkClipboardRequestText;
|
||||
|
@ -406,6 +445,9 @@ namespace Avalonia.Gtk3.Interop
|
|||
public static D.gdk_window_begin_resize_drag GdkWindowBeginResizeDrag;
|
||||
public static D.gdk_event_request_motions GdkEventRequestMotions;
|
||||
public static D.gdk_window_process_updates GdkWindowProcessUpdates;
|
||||
public static D.gdk_window_begin_paint_rect GdkWindowBeginPaintRect;
|
||||
public static D.gdk_window_end_paint GdkWindowEndPaint;
|
||||
|
||||
|
||||
public static D.gdk_pixbuf_new_from_file GdkPixbufNewFromFile;
|
||||
public static D.gtk_icon_theme_get_default GtkIconThemeGetDefault;
|
||||
|
@ -414,16 +456,24 @@ namespace Avalonia.Gtk3.Interop
|
|||
public static D.gdk_window_set_cursor GdkWindowSetCursor;
|
||||
public static D.gdk_pixbuf_new_from_stream GdkPixbufNewFromStream;
|
||||
public static D.gdk_pixbuf_save_to_bufferv GdkPixbufSaveToBufferv;
|
||||
|
||||
public static D.gdk_cairo_create GdkCairoCreate;
|
||||
|
||||
public static D.cairo_image_surface_create CairoImageSurfaceCreate;
|
||||
public static D.cairo_image_surface_get_data CairoImageSurfaceGetData;
|
||||
public static D.cairo_image_surface_get_stride CairoImageSurfaceGetStride;
|
||||
public static D.cairo_surface_mark_dirty CairoSurfaceMarkDirty;
|
||||
public static D.cairo_surface_write_to_png CairoSurfaceWriteToPng;
|
||||
public static D.cairo_surface_flush CairoSurfaceFlush;
|
||||
public static D.cairo_surface_destroy CairoSurfaceDestroy;
|
||||
public static D.cairo_set_source_surface CairoSetSourceSurface;
|
||||
public static D.cairo_set_source_rgba CairoSetSourceRgba;
|
||||
public static D.cairo_scale CairoScale;
|
||||
public static D.cairo_paint CairoPaint;
|
||||
public static D.cairo_show_text CairoShowText;
|
||||
public static D.cairo_select_font_face CairoSelectFontFace;
|
||||
public static D.cairo_set_font_size CairoSetFontSize;
|
||||
public static D.cairo_move_to CairoMoveTo;
|
||||
public static D.cairo_destroy CairoDestroy;
|
||||
}
|
||||
|
||||
public enum GtkWindowType
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace Avalonia.Gtk3
|
|||
{
|
||||
public int ScreenCount
|
||||
{
|
||||
get => _allScreens.Length;
|
||||
get => AllScreens.Length;
|
||||
}
|
||||
|
||||
private Screen[] _allScreens;
|
||||
|
@ -23,7 +23,7 @@ namespace Avalonia.Gtk3
|
|||
IntPtr display = Native.GdkGetDefaultDisplay();
|
||||
GdkScreen screen = Native.GdkDisplayGetDefaultScreen(display);
|
||||
short primary = Native.GdkScreenGetPrimaryMonitor(screen);
|
||||
Screen[] screens = new Screen[ScreenCount];
|
||||
Screen[] screens = new Screen[Native.GdkScreenGetNMonitors(screen)];
|
||||
for (short i = 0; i < screens.Length; i++)
|
||||
{
|
||||
GdkRectangle workArea = new GdkRectangle(), geometry = new GdkRectangle();
|
||||
|
|
|
@ -26,7 +26,9 @@ namespace Avalonia.Gtk3
|
|||
private double _lastScaling;
|
||||
private uint _lastKbdEvent;
|
||||
private uint _lastSmoothScrollEvent;
|
||||
private bool _hasDirtyRects;
|
||||
private GCHandle _gcHandle;
|
||||
private object _lock = new object();
|
||||
private IDeferredRenderOperation _nextRenderOperation;
|
||||
|
||||
public WindowBaseImpl(GtkWindow gtkWidget)
|
||||
{
|
||||
|
@ -52,11 +54,25 @@ namespace Avalonia.Gtk3
|
|||
Connect<Native.D.signal_generic>("destroy", OnDestroy);
|
||||
Native.GtkWidgetRealize(gtkWidget);
|
||||
_lastSize = ClientSize;
|
||||
GlibTimeout.Add(0, 16, () =>
|
||||
{
|
||||
Invalidate(default(Rect));
|
||||
return true;
|
||||
});
|
||||
if (Gtk3Platform.UseDeferredRendering)
|
||||
{
|
||||
Native.GtkWidgetSetDoubleBuffered(gtkWidget, false);
|
||||
_gcHandle = GCHandle.Alloc(this);
|
||||
Native.GtkWidgetAddTickCallback(GtkWidget, PinnedStaticCallback, GCHandle.ToIntPtr(_gcHandle), IntPtr.Zero);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private bool OnConfigured(IntPtr gtkwidget, IntPtr ev, IntPtr userdata)
|
||||
{
|
||||
var size = ClientSize;
|
||||
int w, h;
|
||||
Native.GtkWindowGetSize(GtkWidget, out w, out h);
|
||||
var size = ClientSize = new Size(w, h);
|
||||
if (_lastSize != size)
|
||||
{
|
||||
Resized?.Invoke(size);
|
||||
|
@ -224,13 +240,52 @@ namespace Avalonia.Gtk3
|
|||
|
||||
private bool OnDraw(IntPtr gtkwidget, IntPtr cairocontext, IntPtr userdata)
|
||||
{
|
||||
_hasDirtyRects = false;
|
||||
CurrentCairoContext = cairocontext;
|
||||
Paint?.Invoke(new Rect(ClientSize));
|
||||
CurrentCairoContext = IntPtr.Zero;
|
||||
if (!Gtk3Platform.UseDeferredRendering)
|
||||
{
|
||||
CurrentCairoContext = cairocontext;
|
||||
Paint?.Invoke(new Rect(ClientSize));
|
||||
CurrentCairoContext = IntPtr.Zero;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Native.D.TickCallback PinnedStaticCallback = StaticTickCallback;
|
||||
|
||||
static bool StaticTickCallback(IntPtr widget, IntPtr clock, IntPtr userData)
|
||||
{
|
||||
var impl = (WindowBaseImpl) GCHandle.FromIntPtr(userData).Target;
|
||||
impl.OnRenderTick();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SetNextRenderOperation(IDeferredRenderOperation op)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_nextRenderOperation?.Dispose();
|
||||
_nextRenderOperation = op;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRenderTick()
|
||||
{
|
||||
IDeferredRenderOperation op = null;
|
||||
lock (_lock)
|
||||
{
|
||||
if (_nextRenderOperation != null)
|
||||
{
|
||||
op = _nextRenderOperation;
|
||||
_nextRenderOperation = null;
|
||||
}
|
||||
}
|
||||
if (op != null)
|
||||
{
|
||||
op?.RenderNow();
|
||||
op?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
//We are calling it here, since signal handler will be detached
|
||||
|
@ -239,6 +294,10 @@ namespace Avalonia.Gtk3
|
|||
foreach(var d in Disposables.AsEnumerable().Reverse())
|
||||
d.Dispose();
|
||||
Disposables.Clear();
|
||||
if (_gcHandle.IsAllocated)
|
||||
{
|
||||
_gcHandle.Free();
|
||||
}
|
||||
}
|
||||
|
||||
public Size MaxClientSize
|
||||
|
@ -273,20 +332,8 @@ namespace Avalonia.Gtk3
|
|||
{
|
||||
if(GtkWidget.IsClosed)
|
||||
return;
|
||||
Native.GtkWidgetQueueDrawArea(GtkWidget, (int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height);
|
||||
if (!_hasDirtyRects)
|
||||
{
|
||||
_hasDirtyRects = true;
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
if (GtkWidget.IsClosed)
|
||||
return;
|
||||
var window = Native.GtkWidgetGetWindow(GtkWidget);
|
||||
if (window == IntPtr.Zero)
|
||||
return;
|
||||
Native.GdkWindowProcessUpdates(window, false);
|
||||
}, DispatcherPriority.Render);
|
||||
}
|
||||
var s = ClientSize;
|
||||
Native.GtkWidgetQueueDrawArea(GtkWidget, 0, 0, (int) s.Width, (int) s.Height);
|
||||
}
|
||||
|
||||
public void SetInputRoot(IInputRoot inputRoot) => _inputRoot = inputRoot;
|
||||
|
@ -339,17 +386,7 @@ namespace Avalonia.Gtk3
|
|||
}
|
||||
|
||||
|
||||
public Size ClientSize
|
||||
{
|
||||
get
|
||||
{
|
||||
if (GtkWidget.IsClosed)
|
||||
return new Size();
|
||||
int w, h;
|
||||
Native.GtkWindowGetSize(GtkWidget, out w, out h);
|
||||
return new Size(w, h);
|
||||
}
|
||||
}
|
||||
public Size ClientSize { get; private set; }
|
||||
|
||||
public void Resize(Size value)
|
||||
{
|
||||
|
@ -376,7 +413,10 @@ namespace Avalonia.Gtk3
|
|||
|
||||
public IRenderer CreateRenderer(IRenderRoot root)
|
||||
{
|
||||
return new ImmediateRenderer(root);
|
||||
var loop = AvaloniaLocator.Current.GetService<IRenderLoop>();
|
||||
return Gtk3Platform.UseDeferredRendering
|
||||
? (IRenderer) new DeferredRenderer(root, loop)
|
||||
: new ImmediateRenderer(root);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче