[gtk: 1/2] Implement GtkColorPicker for Windows




commit 86a38918d78d079d0a51a9911ecc68df2fb19328
Author: Luca Bacci <luca bacci982 gmail com>
Date:   Fri Aug 26 15:34:01 2022 +0200

    Implement GtkColorPicker for Windows
    
    Fixes https://gitlab.gnome.org/GNOME/gtk/-/issues/5136

 gtk/gtkcolorpicker.c             |  15 ++-
 gtk/gtkcolorpickerwin32.c        | 246 +++++++++++++++++++++++++++++++++++++++
 gtk/gtkcolorpickerwin32private.h |  41 +++++++
 gtk/meson.build                  |   5 +-
 4 files changed, 304 insertions(+), 3 deletions(-)
---
diff --git a/gtk/gtkcolorpicker.c b/gtk/gtkcolorpicker.c
index 6b3f5f79c1..2c1b62b2fb 100644
--- a/gtk/gtkcolorpicker.c
+++ b/gtk/gtkcolorpicker.c
@@ -21,6 +21,11 @@
 #include "gtkcolorpickerportalprivate.h"
 #include "gtkcolorpickershellprivate.h"
 #include "gtkcolorpickerkwinprivate.h"
+
+#ifdef G_OS_WIN32
+#include "gtkcolorpickerwin32private.h"
+#endif
+
 #include <gio/gio.h>
 
 
@@ -51,13 +56,19 @@ gtk_color_picker_pick_finish (GtkColorPicker  *picker,
 GtkColorPicker *
 gtk_color_picker_new (void)
 {
-  GtkColorPicker *picker;
+  GtkColorPicker *picker = NULL;
 
-  picker = gtk_color_picker_portal_new ();
+#if defined (G_OS_UNIX)
+  if (!picker)
+    picker = gtk_color_picker_portal_new ();
   if (!picker)
     picker = gtk_color_picker_shell_new ();
   if (!picker)
     picker = gtk_color_picker_kwin_new ();
+#elif defined (G_OS_WIN32)
+  if (!picker)
+    picker = gtk_color_picker_win32_new ();
+#endif
 
   if (!picker)
     g_debug ("No suitable GtkColorPicker implementation");
diff --git a/gtk/gtkcolorpickerwin32.c b/gtk/gtkcolorpickerwin32.c
new file mode 100644
index 0000000000..c53364ef5e
--- /dev/null
+++ b/gtk/gtkcolorpickerwin32.c
@@ -0,0 +1,246 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2022 the GTK team
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gtkcolorpickerwin32private.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+GList *pickers;
+HHOOK  hook;
+
+static void remove_hook (void);
+
+extern IMAGE_DOS_HEADER __ImageBase;
+#define this_hmodule ((HMODULE)&__ImageBase)
+
+struct _GtkColorPickerWin32
+{
+  GObject parent_instance;
+
+  GTask *task;
+  POINT  point;
+};
+
+struct _GtkColorPickerWin32Class
+{
+  GObjectClass parent_class;
+};
+
+static GInitableIface *initable_parent_iface;
+static void gtk_color_picker_win32_initable_iface_init (GInitableIface *iface);
+static void gtk_color_picker_win32_iface_init (GtkColorPickerInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GtkColorPickerWin32, gtk_color_picker_win32, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, gtk_color_picker_win32_initable_iface_init)
+                         G_IMPLEMENT_INTERFACE (GTK_TYPE_COLOR_PICKER, gtk_color_picker_win32_iface_init))
+
+static gboolean
+gtk_color_picker_win32_initable_init (GInitable     *initable,
+                                      GCancellable  *cancellable,
+                                      GError       **error)
+{
+  return TRUE;
+}
+
+static void
+gtk_color_picker_win32_initable_iface_init (GInitableIface *iface)
+{
+  initable_parent_iface = g_type_interface_peek_parent (iface);
+  iface->init = gtk_color_picker_win32_initable_init;
+}
+
+static void
+gtk_color_picker_win32_init (GtkColorPickerWin32 *picker)
+{
+}
+
+static void
+gtk_color_picker_win32_class_init (GtkColorPickerWin32Class *class)
+{
+}
+
+GtkColorPicker *
+gtk_color_picker_win32_new (void)
+{
+  return GTK_COLOR_PICKER (g_initable_new (GTK_TYPE_COLOR_PICKER_WIN32, NULL, NULL, NULL));
+}
+
+static void
+on_task_completed (GObject    *object,
+                   GParamSpec *pspec,
+                   gpointer    user_data)
+{
+  gpointer source = g_task_get_source_object (G_TASK (object));
+  GtkColorPickerWin32 *picker = GTK_COLOR_PICKER_WIN32 (source);
+
+  g_clear_object (&picker->task);
+}
+
+static void
+pick_color (GTask         *task,
+            gpointer       source_object,
+            gpointer       task_data,
+            GCancellable  *cancellable)
+{
+  GtkColorPickerWin32 *picker = GTK_COLOR_PICKER_WIN32 (source_object);
+  GdkRGBA rgba = (GdkRGBA) { 1.0, 1.0, 1.0, 1.0 };
+  HDC hdc = GetDC(HWND_DESKTOP);
+
+  if (hdc)
+    {
+      COLORREF color = GetPixel(hdc, picker->point.x, picker->point.y);
+
+      rgba = (GdkRGBA){
+        (double) GetRValue (color) / 255.0,
+        (double) GetGValue (color) / 255.0,
+        (double) GetBValue (color) / 255.0,
+        1.0,
+      };
+
+      ReleaseDC (HWND_DESKTOP, hdc);
+    }
+
+  g_task_return_pointer (task,
+                         gdk_rgba_copy (&rgba),
+                         (GDestroyNotify) gdk_rgba_free);
+}
+
+static void
+picked (GtkColorPickerWin32 *picker)
+{
+  g_task_run_in_thread (picker->task, pick_color);
+}
+
+static LRESULT CALLBACK
+mouse_proc (int    nCode,
+            WPARAM wParam,
+            LPARAM lParam)
+{
+  if (nCode == HC_ACTION)
+    {
+      MSLLHOOKSTRUCT *info = (MSLLHOOKSTRUCT*) lParam;
+
+      switch (wParam)
+        {
+        case WM_LBUTTONDOWN:
+        case WM_MBUTTONDOWN:
+        case WM_RBUTTONDOWN:
+        case WM_XBUTTONDOWN:
+          {
+            GtkColorPickerWin32 *picker = GTK_COLOR_PICKER_WIN32 (pickers->data);
+
+            if (!pickers)
+              break;
+
+            /* A low-level mouse hook always receives screen points in
+             * per-monitor DPI aware screen coordinates, regardless of
+             * the DPI awareness setting of the application. */
+            picker->point = info->pt;
+
+            picked (picker);
+
+            pickers = g_list_delete_link (pickers, pickers);
+
+            /* It's safe to remove a hook from within its callback */
+            if (!pickers)
+              remove_hook ();
+
+            return 1;
+          }
+        break;
+        default:
+        break;
+        }
+    }
+
+  return CallNextHookEx(NULL, nCode, wParam, lParam);
+}
+
+static gboolean
+ensure_mouse_hook (void)
+{
+  if (!hook)
+    {
+      hook = SetWindowsHookEx (WH_MOUSE_LL, mouse_proc, this_hmodule, 0);
+      if (!hook)
+        {
+          g_warning ("SetWindowsHookEx failed with error code "
+                     "%"G_GUINT32_FORMAT, (unsigned) GetLastError ());
+          return FALSE;
+        }
+    }
+
+  return TRUE;
+}
+
+static void
+remove_hook (void)
+{
+  if (hook)
+    {
+      UnhookWindowsHookEx (hook);
+      hook = NULL;
+    }
+}
+
+static void
+gtk_color_picker_win32_pick (GtkColorPicker      *cp,
+                             GAsyncReadyCallback  callback,
+                             gpointer             user_data)
+{
+  GtkColorPickerWin32 *picker = GTK_COLOR_PICKER_WIN32 (cp);
+
+  if (picker->task)
+    return;
+
+  picker->task = g_task_new (picker, NULL, callback, user_data);
+  g_task_set_name (picker->task, "GtkColorPicker");
+  g_signal_connect (picker->task, "notify::completed",
+                    G_CALLBACK (on_task_completed),
+                    NULL);
+
+  if (!ensure_mouse_hook ())
+    {
+      g_task_return_new_error (picker->task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_FAILED,
+                               "Cannot capture the mouse pointer");
+      return;
+    }
+
+  pickers = g_list_prepend (pickers, cp);
+}
+
+static GdkRGBA *
+gtk_color_picker_win32_pick_finish (GtkColorPicker  *cp,
+                                    GAsyncResult    *res,
+                                    GError         **error)
+{
+  g_return_val_if_fail (g_task_is_valid (res, cp), NULL);
+
+  return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+gtk_color_picker_win32_iface_init (GtkColorPickerInterface *iface)
+{
+  iface->pick = gtk_color_picker_win32_pick;
+  iface->pick_finish = gtk_color_picker_win32_pick_finish;
+}
diff --git a/gtk/gtkcolorpickerwin32private.h b/gtk/gtkcolorpickerwin32private.h
new file mode 100644
index 0000000000..1d4a423637
--- /dev/null
+++ b/gtk/gtkcolorpickerwin32private.h
@@ -0,0 +1,41 @@
+/*
+ * GTK - The GIMP Toolkit
+ * Copyright (C) 2022 the GTK team
+ * All rights reserved.
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GTK_COLOR_PICKER_WIN32_H__
+#define __GTK_COLOR_PICKER_WIN32_H__
+
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gtk/gtkcolorpickerprivate.h>
+
+G_BEGIN_DECLS
+
+
+#define GTK_TYPE_COLOR_PICKER_WIN32 gtk_color_picker_win32_get_type ()
+G_DECLARE_FINAL_TYPE (GtkColorPickerWin32, gtk_color_picker_win32, GTK, COLOR_PICKER_WIN32, GObject)
+
+GDK_AVAILABLE_IN_ALL
+GtkColorPicker * gtk_color_picker_win32_new (void);
+
+G_END_DECLS
+
+#endif  /* __GTK_COLOR_PICKER_WIN32_H__ */
diff --git a/gtk/meson.build b/gtk/meson.build
index 5a76290dad..080bffe0a6 100644
--- a/gtk/meson.build
+++ b/gtk/meson.build
@@ -793,7 +793,10 @@ if os_win32
   ])
 
   gtk_sources += gtk_win32_print_sources
-  gtk_sources += ['gtkimcontextime.c']
+  gtk_sources += [
+    'gtkcolorpickerwin32.c',
+    'gtkimcontextime.c'
+  ]
 
   if cc.has_header_symbol('windows.h', 'IPrintDialogCallback')
     cdata.set('HAVE_IPRINTDIALOGCALLBACK', 1)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]