From: Luca Bacci Date: Fri, 26 Aug 2022 13:34:01 +0000 (+0200) Subject: Implement GtkColorPicker for Windows X-Git-Tag: archive/raspbian/4.8.3+ds-2+rpi1~3^2~20^2~3^2~20^2 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=86a38918d78d079d0a51a9911ecc68df2fb19328;p=gtk4.git Implement GtkColorPicker for Windows Fixes https://gitlab.gnome.org/GNOME/gtk/-/issues/5136 --- 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 @@ -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 . + */ + +#include "config.h" + +#include "gtkcolorpickerwin32private.h" + +#define WIN32_LEAN_AND_MEAN +#include + +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 . + */ + +#ifndef __GTK_COLOR_PICKER_WIN32_H__ +#define __GTK_COLOR_PICKER_WIN32_H__ + + +#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION) +#error "Only can be included directly." +#endif + +#include + +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)