From f2f30fe0068f7c739ea9fe39c6edb6f214827a29 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 7 Feb 2017 23:47:08 +0300 Subject: [PATCH 1/2] Implemented WindowState for GTK3 --- src/Gtk/Avalonia.Gtk3/Interop/Native.cs | 21 +++++++++++++ src/Gtk/Avalonia.Gtk3/WindowImpl.cs | 41 ++++++++++++++++++++----- 2 files changed, 54 insertions(+), 8 deletions(-) diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs index cffdebeeab..45c197e71d 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs @@ -156,6 +156,22 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] public delegate IntPtr gdk_window_get_pointer(IntPtr raw, out int x, out int y, out int mask); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] + public delegate GdkWindowState gdk_window_get_state(IntPtr window); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] + public delegate void gdk_window_iconify(IntPtr window); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] + public delegate void gdk_window_deiconify(IntPtr window); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] + public delegate void gdk_window_maximize(IntPtr window); + + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] + public delegate void gdk_window_unmaximize(IntPtr window); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)] public delegate void gtk_window_set_geometry_hints(GtkWindow window, IntPtr geometry_widget, ref GdkGeometry geometry, GdkWindowHints geom_mask); @@ -303,6 +319,11 @@ namespace Avalonia.Gtk3.Interop public static D.gdk_cursor_new GdkCursorNew; public static D.gdk_window_get_origin GdkWindowGetOrigin; public static D.gdk_window_get_pointer GdkWindowGetPointer; + public static D.gdk_window_get_state GdkWindowGetState; + public static D.gdk_window_iconify GdkWindowIconify; + public static D.gdk_window_deiconify GdkWindowDeiconify; + public static D.gdk_window_maximize GdkWindowMaximize; + public static D.gdk_window_unmaximize GdkWindowUnmaximize; public static D.gdk_window_begin_move_drag GdkWindowBeginMoveDrag; public static D.gdk_window_begin_resize_drag GdkWindowBeginResizeDrag; public static D.gdk_event_request_motions GdkEventRequestMotions; diff --git a/src/Gtk/Avalonia.Gtk3/WindowImpl.cs b/src/Gtk/Avalonia.Gtk3/WindowImpl.cs index d1a689b75f..631944872f 100644 --- a/src/Gtk/Avalonia.Gtk3/WindowImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/WindowImpl.cs @@ -7,21 +7,42 @@ namespace Avalonia.Gtk3 { class WindowImpl : TopLevelImpl, IWindowImpl { - public WindowState WindowState { get; set; } //STUB + public WindowImpl() : base(Native.GtkWindowNew(GtkWindowType.TopLevel)) + { + } + public void SetTitle(string title) { using (var t = new Utf8Buffer(title)) Native.GtkWindowSetTitle(GtkWidget, t); } - class EmptyDisposable : IDisposable + public WindowState WindowState { - public void Dispose() + get + { + var state = Native.GdkWindowGetState(Native.GtkWidgetGetWindow(GtkWidget)); + if (state.HasFlag(GdkWindowState.Iconified)) + return WindowState.Minimized; + if (state.HasFlag(GdkWindowState.Maximized)) + return WindowState.Maximized; + return WindowState.Normal; + } + set { - + var w = Native.GtkWidgetGetWindow(GtkWidget); + if (value == WindowState.Minimized) + Native.GdkWindowIconify(w); + else if (value == WindowState.Maximized) + Native.GdkWindowMaximize(w); + else + { + Native.GdkWindowUnmaximize(w); + Native.GdkWindowDeiconify(w); + } } } - + public IDisposable ShowDialog() { Native.GtkWindowSetModal(GtkWidget, true); @@ -33,13 +54,17 @@ namespace Avalonia.Gtk3 public void SetIcon(IWindowIconImpl icon) => Native.GtkWindowSetIcon(GtkWidget, (Pixbuf) icon); - public WindowImpl() : base(Native.GtkWindowNew(GtkWindowType.TopLevel)) + public void SetCoverTaskbarWhenMaximized(bool enable) { + //Why do we even have that? } - public void SetCoverTaskbarWhenMaximized(bool enable) + class EmptyDisposable : IDisposable { - //Why do we even have that? + public void Dispose() + { + + } } } } From 10bf7385456f694587785fafb59f30a8c968ba3f Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Wed, 8 Feb 2017 01:07:34 +0300 Subject: [PATCH 2/2] [GTK3] Implemented HiDPI support (needs GTK 3.10) --- src/Gtk/Avalonia.Gtk3/FramebufferManager.cs | 2 +- .../Avalonia.Gtk3/ImageSurfaceFramebuffer.cs | 21 ++++++++++++++++--- src/Gtk/Avalonia.Gtk3/Interop/DynLoader.cs | 10 ++++----- src/Gtk/Avalonia.Gtk3/Interop/Native.cs | 9 ++++++++ src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs | 10 +++++---- src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs | 11 ++++++++-- 6 files changed, 48 insertions(+), 15 deletions(-) diff --git a/src/Gtk/Avalonia.Gtk3/FramebufferManager.cs b/src/Gtk/Avalonia.Gtk3/FramebufferManager.cs index 0a0a8fe0a6..1b2317ed6a 100644 --- a/src/Gtk/Avalonia.Gtk3/FramebufferManager.cs +++ b/src/Gtk/Avalonia.Gtk3/FramebufferManager.cs @@ -26,7 +26,7 @@ namespace Avalonia.Gtk3 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, width, height); + return new ImageSurfaceFramebuffer(_window.CurrentCairoContext, _window.GtkWidget, width, height); } } } diff --git a/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs b/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs index d1964bf05d..3263018a17 100644 --- a/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs +++ b/src/Gtk/Avalonia.Gtk3/ImageSurfaceFramebuffer.cs @@ -13,12 +13,19 @@ namespace Avalonia.Gtk3 class ImageSurfaceFramebuffer : ILockedFramebuffer { private IntPtr _context; + private readonly GtkWidget _widget; 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; + _widget = widget; + _factor = (int)(Native.GtkWidgetGetScaleFactor?.Invoke(_widget) ?? 1u); + width *= _factor; + height *= _factor; _surface = Native.CairoImageSurfaceCreate(1, width, height); + Width = width; Height = height; Address = Native.CairoImageSurfaceGetData(_surface); @@ -31,6 +38,7 @@ namespace Avalonia.Gtk3 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; @@ -43,8 +51,15 @@ namespace Avalonia.Gtk3 public int Height { 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; } diff --git a/src/Gtk/Avalonia.Gtk3/Interop/DynLoader.cs b/src/Gtk/Avalonia.Gtk3/Interop/DynLoader.cs index afe526e2c3..93fb82d285 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/DynLoader.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/DynLoader.cs @@ -13,7 +13,7 @@ namespace Avalonia.Gtk3.Interop internal interface IDynLoader { 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; } - public IntPtr GetProcAddress(IntPtr dll, string proc) + public IntPtr GetProcAddress(IntPtr dll, string proc, bool optional) { var ptr = DlSym(dll, proc); - if (ptr == IntPtr.Zero) + if (ptr == IntPtr.Zero && !optional) throw new NativeException(DlErrorString()); return ptr; } @@ -118,10 +118,10 @@ namespace Avalonia.Gtk3.Interop 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); - if (ptr == IntPtr.Zero) + if (ptr == IntPtr.Zero && !optional) throw new NativeException("Error " + Marshal.GetLastWin32Error()); return ptr; } diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs index 45c197e71d..9f38861b07 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs @@ -50,6 +50,10 @@ namespace Avalonia.Gtk3.Interop [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] 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)] public delegate IntPtr gtk_widget_get_screen(GtkWidget gtkWidget); [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)] @@ -130,6 +134,9 @@ 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_scale(IntPtr context, double sx, double sy); + [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Cairo)] 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.gdk_get_native_handle GetNativeGdkWindowHandle; 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_realize GtkWidgetRealize; 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_destroy CairoSurfaceDestroy; public static D.cairo_set_source_surface CairoSetSourceSurface; + public static D.cairo_scale CairoScale; public static D.cairo_paint CairoPaint; } diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs b/src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs index c6b6326f80..cf065b86dc 100644 --- a/src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs +++ b/src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs @@ -128,9 +128,11 @@ namespace Avalonia.Gtk3.Interop if(import == null) continue; IntPtr lib = dlls[import.Dll]; - - var funcPtr = loader.GetProcAddress(lib, import.Name ?? fieldInfo.FieldType.Name); - fieldInfo.SetValue(null, Marshal.GetDelegateForFunctionPointer(funcPtr, fieldInfo.FieldType)); + + var funcPtr = loader.GetProcAddress(lib, import.Name ?? fieldInfo.FieldType.Name, import.Optional); + + 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" }; @@ -139,7 +141,7 @@ namespace Avalonia.Gtk3.Interop try { 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; } catch { } diff --git a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs index aadbe16d1d..0063c13910 100644 --- a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs +++ b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs @@ -21,6 +21,7 @@ namespace Avalonia.Gtk3 protected readonly List Disposables = new List(); private Size _lastSize; private Point _lastPosition; + private double _lastScaling; private uint _lastKbdEvent; private uint _lastSmoothScrollEvent; @@ -63,7 +64,12 @@ namespace Avalonia.Gtk3 PositionChanged?.Invoke(pos); _lastPosition = pos; } - + var scaling = Scaling; + if (_lastScaling != scaling) + { + ScalingChanged?.Invoke(scaling); + _lastScaling = scaling; + } 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; string IPlatformHandle.HandleDescriptor => "HWND";