Browse Source

[GTK3] Implemented HiDPI support (needs GTK 3.10)

pull/884/head
Nikita Tsukanov 9 years ago
parent
commit
10bf738545
  1. 2
      src/Gtk/Avalonia.Gtk3/FramebufferManager.cs
  2. 21
      src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs
  3. 10
      src/Gtk/Avalonia.Gtk3/Interop/DynLoader.cs
  4. 9
      src/Gtk/Avalonia.Gtk3/Interop/Native.cs
  5. 10
      src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs
  6. 11
      src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs

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

@ -26,7 +26,7 @@ namespace Avalonia.Gtk3
throw new InvalidOperationException("Window is not in drawing state"); throw new InvalidOperationException("Window is not in drawing state");
var width = (int) _window.ClientSize.Width; var width = (int) _window.ClientSize.Width;
var height = (int) _window.ClientSize.Height; var height = (int) _window.ClientSize.Height;
return new ImageSurfaceFramebuffer(_window.CurrentCairoContext, width, height); return new ImageSurfaceFramebuffer(_window.CurrentCairoContext, _window.GtkWidget, width, height);
} }
} }
} }

21
src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs

@ -13,12 +13,19 @@ namespace Avalonia.Gtk3
class ImageSurfaceFramebuffer : ILockedFramebuffer class ImageSurfaceFramebuffer : ILockedFramebuffer
{ {
private IntPtr _context; private IntPtr _context;
private readonly GtkWidget _widget;
private CairoSurface _surface; private CairoSurface _surface;
private int _factor;
public ImageSurfaceFramebuffer(IntPtr context, int width, int height) public ImageSurfaceFramebuffer(IntPtr context, GtkWidget widget, int width, int height)
{ {
_context = context; _context = context;
_widget = widget;
_factor = (int)(Native.GtkWidgetGetScaleFactor?.Invoke(_widget) ?? 1u);
width *= _factor;
height *= _factor;
_surface = Native.CairoImageSurfaceCreate(1, width, height); _surface = Native.CairoImageSurfaceCreate(1, width, height);
Width = width; Width = width;
Height = height; Height = height;
Address = Native.CairoImageSurfaceGetData(_surface); Address = Native.CairoImageSurfaceGetData(_surface);
@ -31,6 +38,7 @@ namespace Avalonia.Gtk3
if(_context == IntPtr.Zero || _surface == null) if(_context == IntPtr.Zero || _surface == null)
return; return;
Native.CairoSurfaceMarkDirty(_surface); Native.CairoSurfaceMarkDirty(_surface);
Native.CairoScale(_context, 1d / _factor, 1d / _factor);
Native.CairoSetSourceSurface(_context, _surface, 0, 0); Native.CairoSetSourceSurface(_context, _surface, 0, 0);
Native.CairoPaint(_context); Native.CairoPaint(_context);
_context = IntPtr.Zero; _context = IntPtr.Zero;
@ -43,8 +51,15 @@ namespace Avalonia.Gtk3
public int Height { get; } public int Height { get; }
public int RowBytes { get; } public int RowBytes { get; }
//TODO: Proper DPI detect
public Size Dpi => new Size(96, 96); public Size Dpi
{
get
{
return new Size(96, 96) * _factor;
}
}
public PixelFormat Format => PixelFormat.Bgra8888; public PixelFormat Format => PixelFormat.Bgra8888;
} }

10
src/Gtk/Avalonia.Gtk3/Interop/DynLoader.cs

@ -13,7 +13,7 @@ namespace Avalonia.Gtk3.Interop
internal interface IDynLoader internal interface IDynLoader
{ {
IntPtr LoadLibrary(string dll); IntPtr LoadLibrary(string dll);
IntPtr GetProcAddress(IntPtr dll, string proc); IntPtr GetProcAddress(IntPtr dll, string proc, bool optional);
} }
@ -91,10 +91,10 @@ namespace Avalonia.Gtk3.Interop
return handle; return handle;
} }
public IntPtr GetProcAddress(IntPtr dll, string proc) public IntPtr GetProcAddress(IntPtr dll, string proc, bool optional)
{ {
var ptr = DlSym(dll, proc); var ptr = DlSym(dll, proc);
if (ptr == IntPtr.Zero) if (ptr == IntPtr.Zero && !optional)
throw new NativeException(DlErrorString()); throw new NativeException(DlErrorString());
return ptr; return ptr;
} }
@ -118,10 +118,10 @@ namespace Avalonia.Gtk3.Interop
throw new NativeException("Error loading " + dll + " error " + err); throw new NativeException("Error loading " + dll + " error " + err);
} }
IntPtr IDynLoader.GetProcAddress(IntPtr dll, string proc) IntPtr IDynLoader.GetProcAddress(IntPtr dll, string proc, bool optional)
{ {
var ptr = GetProcAddress(dll, proc); var ptr = GetProcAddress(dll, proc);
if (ptr == IntPtr.Zero) if (ptr == IntPtr.Zero && !optional)
throw new NativeException("Error " + Marshal.GetLastWin32Error()); throw new NativeException("Error " + Marshal.GetLastWin32Error());
return ptr; return ptr;
} }

9
src/Gtk/Avalonia.Gtk3/Interop/Native.cs

@ -50,6 +50,10 @@ namespace Avalonia.Gtk3.Interop
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
public delegate IntPtr gtk_widget_get_window(GtkWidget gtkWidget); public delegate IntPtr gtk_widget_get_window(GtkWidget gtkWidget);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk, optional: true)]
public delegate uint gtk_widget_get_scale_factor(GtkWidget gtkWidget);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
public delegate IntPtr gtk_widget_get_screen(GtkWidget gtkWidget); public delegate IntPtr gtk_widget_get_screen(GtkWidget gtkWidget);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
@ -130,6 +134,9 @@ namespace Avalonia.Gtk3.Interop
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
public delegate void cairo_set_source_surface(IntPtr cr, CairoSurface surface, double x, double y); 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_scale(IntPtr context, double sx, double sy);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)]
public delegate void cairo_paint(IntPtr context); public delegate void cairo_paint(IntPtr context);
@ -275,6 +282,7 @@ namespace Avalonia.Gtk3.Interop
public static D.gtk_widget_hide GtkWidgetHide; public static D.gtk_widget_hide GtkWidgetHide;
public static D.gdk_get_native_handle GetNativeGdkWindowHandle; public static D.gdk_get_native_handle GetNativeGdkWindowHandle;
public static D.gtk_widget_get_window GtkWidgetGetWindow; public static D.gtk_widget_get_window GtkWidgetGetWindow;
public static D.gtk_widget_get_scale_factor GtkWidgetGetScaleFactor;
public static D.gtk_widget_get_screen GtkWidgetGetScreen; public static D.gtk_widget_get_screen GtkWidgetGetScreen;
public static D.gtk_widget_realize GtkWidgetRealize; public static D.gtk_widget_realize GtkWidgetRealize;
public static D.gtk_window_get_size GtkWindowGetSize; public static D.gtk_window_get_size GtkWindowGetSize;
@ -343,6 +351,7 @@ namespace Avalonia.Gtk3.Interop
public static D.cairo_surface_flush CairoSurfaceFlush; public static D.cairo_surface_flush CairoSurfaceFlush;
public static D.cairo_surface_destroy CairoSurfaceDestroy; public static D.cairo_surface_destroy CairoSurfaceDestroy;
public static D.cairo_set_source_surface CairoSetSourceSurface; public static D.cairo_set_source_surface CairoSetSourceSurface;
public static D.cairo_scale CairoScale;
public static D.cairo_paint CairoPaint; public static D.cairo_paint CairoPaint;
} }

10
src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs

@ -128,9 +128,11 @@ namespace Avalonia.Gtk3.Interop
if(import == null) if(import == null)
continue; continue;
IntPtr lib = dlls[import.Dll]; IntPtr lib = dlls[import.Dll];
var funcPtr = loader.GetProcAddress(lib, import.Name ?? fieldInfo.FieldType.Name); var funcPtr = loader.GetProcAddress(lib, import.Name ?? fieldInfo.FieldType.Name, import.Optional);
fieldInfo.SetValue(null, Marshal.GetDelegateForFunctionPointer(funcPtr, fieldInfo.FieldType));
if (funcPtr != IntPtr.Zero)
fieldInfo.SetValue(null, Marshal.GetDelegateForFunctionPointer(funcPtr, fieldInfo.FieldType));
} }
var nativeHandleNames = new[] { "gdk_win32_window_get_handle", "gdk_x11_window_get_xid", "gdk_quartz_window_get_nswindow" }; var nativeHandleNames = new[] { "gdk_win32_window_get_handle", "gdk_x11_window_get_xid", "gdk_quartz_window_get_nswindow" };
@ -139,7 +141,7 @@ namespace Avalonia.Gtk3.Interop
try try
{ {
Native.GetNativeGdkWindowHandle = (Native.D.gdk_get_native_handle)Marshal Native.GetNativeGdkWindowHandle = (Native.D.gdk_get_native_handle)Marshal
.GetDelegateForFunctionPointer(loader.GetProcAddress(dlls[GtkDll.Gdk], name), typeof(Native.D.gdk_get_native_handle)); .GetDelegateForFunctionPointer(loader.GetProcAddress(dlls[GtkDll.Gdk], name, false), typeof(Native.D.gdk_get_native_handle));
break; break;
} }
catch { } catch { }

11
src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs

@ -21,6 +21,7 @@ namespace Avalonia.Gtk3
protected readonly List<IDisposable> Disposables = new List<IDisposable>(); protected readonly List<IDisposable> Disposables = new List<IDisposable>();
private Size _lastSize; private Size _lastSize;
private Point _lastPosition; private Point _lastPosition;
private double _lastScaling;
private uint _lastKbdEvent; private uint _lastKbdEvent;
private uint _lastSmoothScrollEvent; private uint _lastSmoothScrollEvent;
@ -63,7 +64,12 @@ namespace Avalonia.Gtk3
PositionChanged?.Invoke(pos); PositionChanged?.Invoke(pos);
_lastPosition = pos; _lastPosition = pos;
} }
var scaling = Scaling;
if (_lastScaling != scaling)
{
ScalingChanged?.Invoke(scaling);
_lastScaling = scaling;
}
return false; return false;
} }
@ -228,7 +234,8 @@ namespace Avalonia.Gtk3
} }
public double Scaling => 1; //TODO: Implement scaling public double Scaling => (double) 1 / (Native.GtkWidgetGetScaleFactor?.Invoke(GtkWidget) ?? 1);
public IPlatformHandle Handle => this; public IPlatformHandle Handle => this;
string IPlatformHandle.HandleDescriptor => "HWND"; string IPlatformHandle.HandleDescriptor => "HWND";

Loading…
Cancel
Save