GDK: Add cursor theme support to W32 backend

Load themed cursors from the same places they are loaded on freedesktop systems,
but use W32 API functions to do so (works for .cur/.ani cursors instead of X
cursors).

Refactor the code for cursor handling. Prefer loading cursors by name.

Do not load actual cursors when loading the theme. Find the files and remember
the arguments/calls for loading them instead. Keeping HCURSOR instance in the
hashmap would result in multiple GdkCursors using the same HCURSOR. Given that
we use DestroyCursor() to off them, this would cause problems (at the very
least - DestroyCursor() would fail).

Store GdkCursor instances in a cache. Update cached cursors when theme changes.

Recognize "system" theme as a special (and default) case. When it is set,
prefer system cursors and fall back to Adwaita cursors and (as a last resort)
built-in X cursors. Otherwise prefer theme cursors and fall back to system and
X cursors.

Force GTK to use "left_ptr" cursor when no cursor is set. Using NULL makes
it use the system default "arrow", which is not the intended behaviour when
a non-system theme is selected.

Ignore cursor size setting and query the OS for the required cursor size, as
Windows (almost) does not allow setting cursors of arbitrary size.

https://bugzilla.gnome.org/show_bug.cgi?id=749287
This commit is contained in:
Руслан Ижбулатов 2015-05-13 07:45:40 +00:00
parent 641fbd86d7
commit 26c24328d5
8 changed files with 759 additions and 76 deletions

View File

@ -107,6 +107,33 @@ in 256 color mode.
</refsect2>
<refsect2 id="win32-cursors">
<title>Windows-specific handling of cursors</title>
<para>
By default the "system" cursor theme is used. This makes GTK prefer cursors
that Windows currently uses, falling back to Adwaita cursors and (as the last
resort) built-in X cursors.
</para>
<para>
When any other cursor theme is used, GTK will prefer cursors from that theme,
falling back to Windows cursors and built-in X cursors.
</para>
<para>
Theme can be changed by setting <literal>gtk-cursor-theme-name</literal> GTK+ setting. Users can override GTK+ settings in the <filename>settings.ini</filename> file or at runtime in the GTK+ Inspector.
</para>
<para>
Themes are loaded from normal Windows variants of the XDG locations:
<filename>%HOME%/icons/THEME/cursors</filename>,
<filename>%APPDATA%/icons/THEME/cursors</filename>,
<filename>RUNTIME_PREFIX/share/icons/THEME/cursors</filename>.
</para>
<para>
The <literal>gtk-cursor-theme-size</literal> setting is ignored, GTK will use the cursor size that Windows tells it to use.
</para>
</refsect2>
<para>
More information about GTK+ on Windows, including detailed build
instructions, binary downloads, etc, can be found

View File

@ -23,34 +23,58 @@
#include "gdkcursor.h"
#include "gdkwin32.h"
#include "gdkdisplay-win32.h"
#ifdef __MINGW32__
#include <w32api.h>
#endif
#include "xcursors.h"
typedef struct _DefaultCursor {
char *name;
char *id;
} DefaultCursor;
static DefaultCursor default_cursors[] = {
{ "appstarting", IDC_APPSTARTING },
{ "arrow", IDC_ARROW },
{ "cross", IDC_CROSS },
{ "hand", IDC_HAND },
{ "help", IDC_HELP },
{ "ibeam", IDC_IBEAM },
/* an X cursor name, for compatibility with GTK: */
{ "left_ptr_watch", IDC_APPSTARTING },
{ "sizeall", IDC_SIZEALL },
{ "sizenesw", IDC_SIZENESW },
{ "sizens", IDC_SIZENS },
{ "sizenwse", IDC_SIZENWSE },
{ "sizewe", IDC_SIZEWE },
{ "uparrow", IDC_UPARROW },
{ "wait", IDC_WAIT },
/* css cursor names: */
{ "default", IDC_ARROW },
{ "pointer", IDC_HAND },
{ "progress", IDC_APPSTARTING },
{ "crosshair", IDC_CROSS },
{ "text", IDC_IBEAM },
{ "move", IDC_SIZEALL },
{ "not-allowed", IDC_NO },
{ "ew-resize", IDC_SIZEWE },
{ "ns-resize", IDC_SIZENS },
{ "nesw-resize", IDC_SIZENESW },
{ "nwse-resize", IDC_SIZENWSE }
};
static HCURSOR
hcursor_from_type (GdkCursorType cursor_type)
hcursor_from_x_cursor (gint i,
GdkCursorType cursor_type)
{
gint i, j, x, y, ofs;
gint j, x, y, ofs;
HCURSOR rv;
gint w, h;
guchar *and_plane, *xor_plane;
if (cursor_type != GDK_BLANK_CURSOR)
{
for (i = 0; i < G_N_ELEMENTS (cursors); i++)
if (cursors[i].type == cursor_type)
break;
if (i >= G_N_ELEMENTS (cursors) || !cursors[i].name)
return NULL;
/* Use real Win32 cursor if possible */
if (cursors[i].builtin)
return LoadCursor (NULL, cursors[i].builtin);
}
w = GetSystemMetrics (SM_CXCURSOR);
h = GetSystemMetrics (SM_CYCURSOR);
@ -79,6 +103,7 @@ hcursor_from_type (GdkCursorType cursor_type)
if (data)
{
RESET_BIT (and_plane[pofs], bit);
if (data == 1)
SET_BIT (xor_plane[pofs], bit);
}
@ -96,14 +121,307 @@ hcursor_from_type (GdkCursorType cursor_type)
rv = CreateCursor (_gdk_app_hmodule, 0, 0,
w, h, and_plane, xor_plane);
}
if (rv == NULL)
WIN32_API_FAILED ("CreateCursor");
g_free (and_plane);
g_free (xor_plane);
return rv;
}
static HCURSOR
win32_cursor_create_hcursor (Win32Cursor *cursor)
{
HCURSOR result;
switch (cursor->load_type)
{
case GDK_WIN32_CURSOR_LOAD_FROM_FILE:
result = LoadImageW (NULL,
cursor->resource_name,
IMAGE_CURSOR,
cursor->width,
cursor->height,
cursor->load_flags);
break;
case GDK_WIN32_CURSOR_LOAD_FROM_RESOURCE_NULL:
result = LoadImageA (NULL,
(const gchar *) cursor->resource_name,
IMAGE_CURSOR,
cursor->width,
cursor->height,
cursor->load_flags);
break;
case GDK_WIN32_CURSOR_LOAD_FROM_RESOURCE_THIS:
result = LoadImageA (_gdk_app_hmodule,
(const gchar *) cursor->resource_name,
IMAGE_CURSOR,
cursor->width,
cursor->height,
cursor->load_flags);
break;
case GDK_WIN32_CURSOR_CREATE:
result = hcursor_from_x_cursor (cursor->xcursor_number,
cursor->cursor_type);
break;
default:
result = NULL;
}
return result;
}
static Win32Cursor *
win32_cursor_new (GdkWin32CursorLoadType load_type,
gpointer resource_name,
gint width,
gint height,
guint load_flags,
gint xcursor_number,
GdkCursorType cursor_type)
{
Win32Cursor *result;
result = g_new (Win32Cursor, 1);
result->load_type = load_type;
result->resource_name = resource_name;
result->width = width;
result->height = height;
result->load_flags = load_flags;
result->xcursor_number = xcursor_number;
result->cursor_type = cursor_type;
return result;
}
static void
win32_cursor_destroy (gpointer data)
{
Win32Cursor *cursor = data;
/* resource_name could be a resource ID (uint16_t stored as a pointer),
* which shouldn't be freed.
*/
if (cursor->load_type == GDK_WIN32_CURSOR_LOAD_FROM_FILE)
g_free (cursor->resource_name);
g_free (cursor);
}
static void
win32_cursor_theme_load_from (Win32CursorTheme *theme,
gint size,
const gchar *dir)
{
GDir *gdir;
const gchar *filename;
HCURSOR hcursor;
gdir = g_dir_open (dir, 0, NULL);
if (gdir == NULL)
return;
while ((filename = g_dir_read_name (gdir)) != NULL)
{
gchar *fullname;
gunichar2 *filenamew;
gchar *cursor_name;
gchar *dot;
Win32Cursor *cursor;
fullname = g_strconcat (dir, "/", filename, NULL);
filenamew = g_utf8_to_utf16 (fullname, -1, NULL, NULL, NULL);
g_free (fullname);
if (filenamew == NULL)
continue;
hcursor = LoadImageW (NULL, filenamew, IMAGE_CURSOR, size, size,
LR_LOADFROMFILE | (size == 0 ? LR_DEFAULTSIZE : 0));
if (hcursor == NULL)
{
g_free (filenamew);
continue;
}
DestroyCursor (hcursor);
dot = strchr (filename, '.');
cursor_name = dot ? g_strndup (filename, dot - filename) : g_strdup (filename);
cursor = win32_cursor_new (GDK_WIN32_CURSOR_LOAD_FROM_FILE,
filenamew,
size,
size,
LR_LOADFROMFILE | (size == 0 ? LR_DEFAULTSIZE : 0),
0,
0);
g_hash_table_insert (theme->named_cursors, cursor_name, cursor);
}
}
static void
win32_cursor_theme_load_from_dirs (Win32CursorTheme *theme,
const gchar *name,
gint size)
{
gchar *theme_dir;
const gchar * const *dirs;
gint i;
dirs = g_get_system_data_dirs ();
/* <prefix>/share/icons */
for (i = 0; dirs[i]; i++)
{
theme_dir = g_strconcat (dirs[i], "/icons/", name, "/cursors", NULL);
win32_cursor_theme_load_from (theme, size, theme_dir);
g_free (theme_dir);
}
/* ~/.icons */
theme_dir = g_strconcat (g_get_home_dir (), "/icons/", name, "/cursors", NULL);
win32_cursor_theme_load_from (theme, size, theme_dir);
g_free (theme_dir);
}
static void
win32_cursor_theme_load_system (Win32CursorTheme *theme,
gint size)
{
gint i;
HCURSOR hcursor;
Win32Cursor *cursor;
for (i = 0; i < G_N_ELEMENTS (cursors); i++)
{
if (cursors[i].name == NULL)
break;
hcursor = NULL;
/* Prefer W32 cursors */
if (cursors[i].builtin)
hcursor = LoadImageA (NULL, cursors[i].builtin, IMAGE_CURSOR,
size, size,
LR_SHARED | (size == 0 ? LR_DEFAULTSIZE : 0));
/* Fall back to X cursors, but only if we've got no theme cursor */
if (hcursor == NULL && g_hash_table_lookup (theme->named_cursors, cursors[i].name) == NULL)
hcursor = hcursor_from_x_cursor (i, cursors[i].type);
if (hcursor == NULL)
continue;
DestroyCursor (hcursor);
cursor = win32_cursor_new (GDK_WIN32_CURSOR_LOAD_FROM_RESOURCE_NULL,
(gpointer) cursors[i].builtin,
size,
size,
LR_SHARED | (size == 0 ? LR_DEFAULTSIZE : 0),
0,
0);
g_hash_table_insert (theme->named_cursors,
g_strdup (cursors[i].name),
cursor);
}
for (i = 0; i < G_N_ELEMENTS (default_cursors); i++)
{
if (default_cursors[i].name == NULL)
break;
hcursor = LoadImageA (NULL, default_cursors[i].id, IMAGE_CURSOR, size, size,
LR_SHARED | (size == 0 ? LR_DEFAULTSIZE : 0));
if (hcursor == NULL)
continue;
DestroyCursor (hcursor);
cursor = win32_cursor_new (GDK_WIN32_CURSOR_LOAD_FROM_RESOURCE_NULL,
(gpointer) default_cursors[i].id,
size,
size,
LR_SHARED | (size == 0 ? LR_DEFAULTSIZE : 0),
0,
0);
g_hash_table_insert (theme->named_cursors,
g_strdup (default_cursors[i].name),
cursor);
}
}
Win32CursorTheme *
win32_cursor_theme_load (const gchar *name,
gint size)
{
Win32CursorTheme *result = g_new0 (Win32CursorTheme, 1);
result->named_cursors = g_hash_table_new_full (g_str_hash,
g_str_equal,
g_free,
win32_cursor_destroy);
if (strcmp (name, "system") == 0)
{
win32_cursor_theme_load_from_dirs (result, "Adwaita", size);
win32_cursor_theme_load_system (result, size);
}
else
{
win32_cursor_theme_load_from_dirs (result, name, size);
}
if (g_hash_table_size (result->named_cursors) > 0)
return result;
win32_cursor_theme_destroy (result);
return NULL;
}
void
win32_cursor_theme_destroy (Win32CursorTheme *theme)
{
g_hash_table_destroy (theme->named_cursors);
g_free (theme);
}
Win32Cursor *
win32_cursor_theme_get_cursor (Win32CursorTheme *theme,
const gchar *name)
{
return g_hash_table_lookup (theme->named_cursors, name);
}
static HCURSOR
hcursor_from_type (GdkCursorType cursor_type)
{
gint i = 0;
if (cursor_type != GDK_BLANK_CURSOR)
{
for (i = 0; i < G_N_ELEMENTS (cursors); i++)
if (cursors[i].type == cursor_type)
break;
if (i >= G_N_ELEMENTS (cursors) || !cursors[i].name)
return NULL;
/* Use real Win32 cursor if possible */
if (cursors[i].builtin)
return LoadImageA (NULL, cursors[i].builtin, IMAGE_CURSOR, 0, 0,
LR_SHARED | LR_DEFAULTSIZE);
}
return hcursor_from_x_cursor (i, cursor_type);
}
struct _GdkWin32CursorClass
{
GdkCursorClass cursor_class;
@ -120,14 +438,91 @@ _gdk_win32_cursor_finalize (GObject *object)
SetCursor (NULL);
if (!DestroyCursor (private->hcursor))
WIN32_API_FAILED ("DestroyCursor");
g_warning (G_STRLOC ": DestroyCursor (%p) failed: %lu", private->hcursor, GetLastError ());
g_free (private->name);
G_OBJECT_CLASS (gdk_win32_cursor_parent_class)->finalize (object);
}
static HCURSOR
hcursor_idc_from_name (const gchar *name)
{
int i;
for (i = 0; i < G_N_ELEMENTS (default_cursors); i++)
{
if (strcmp (default_cursors[i].name, name) != 0)
continue;
return LoadImageA (NULL, default_cursors[i].id, IMAGE_CURSOR, 0, 0,
LR_SHARED | LR_DEFAULTSIZE);
}
return NULL;
}
static HCURSOR
hcursor_x_from_name (const gchar *name)
{
gint i;
for (i = 0; i < G_N_ELEMENTS (cursors); i++)
if (cursors[i].name == NULL || strcmp (cursors[i].name, name) == 0)
return hcursor_from_x_cursor (i, cursors[i].type);
return NULL;
}
static HCURSOR
hcursor_from_theme (GdkDisplay *display,
const gchar *name)
{
Win32CursorTheme *theme;
Win32Cursor *theme_cursor;
GdkWin32Display *win32_display = GDK_WIN32_DISPLAY (display);
if (name == NULL)
return NULL;
theme = _gdk_win32_display_get_cursor_theme (win32_display);
theme_cursor = win32_cursor_theme_get_cursor (theme, name);
if (theme_cursor == NULL)
return NULL;
return win32_cursor_create_hcursor (theme_cursor);
}
static HCURSOR
hcursor_from_name (GdkDisplay *display,
const gchar *name)
{
HCURSOR hcursor;
if (strcmp (name, "none") == 0)
return hcursor_from_type (GDK_BLANK_CURSOR);
/* Try current theme first */
hcursor = hcursor_from_theme (display, name);
if (hcursor != NULL)
return hcursor;
hcursor = hcursor_idc_from_name (name);
if (hcursor != NULL)
return hcursor;
hcursor = hcursor_x_from_name (name);
return hcursor;
}
static GdkCursor*
cursor_new_from_hcursor (HCURSOR hcursor,
GdkCursorType cursor_type)
cursor_new_from_hcursor (HCURSOR hcursor,
const gchar *name,
GdkCursorType cursor_type)
{
GdkWin32Cursor *private;
GdkCursor *cursor;
@ -136,21 +531,120 @@ cursor_new_from_hcursor (HCURSOR hcursor,
"cursor-type", cursor_type,
"display", _gdk_display,
NULL);
private->name = g_strdup (name);
private->hcursor = hcursor;
cursor = (GdkCursor*) private;
return cursor;
}
static gboolean
_gdk_win32_cursor_update (GdkWin32Display *win32_display,
GdkWin32Cursor *cursor)
{
HCURSOR hcursor = NULL;
Win32CursorTheme *theme;
Win32Cursor *theme_cursor;
/* Do nothing if this is not a named cursor. */
if (cursor->name == NULL)
return FALSE;
theme = _gdk_win32_display_get_cursor_theme (win32_display);
theme_cursor = win32_cursor_theme_get_cursor (theme, cursor->name);
if (theme_cursor != NULL)
hcursor = win32_cursor_create_hcursor (theme_cursor);
if (hcursor == NULL)
{
g_warning (G_STRLOC ": Unable to load %s from the cursor theme", cursor->name);
hcursor = hcursor_idc_from_name (cursor->name);
if (hcursor == NULL)
hcursor = hcursor_x_from_name (cursor->name);
if (hcursor == NULL)
return FALSE;
}
if (GetCursor () == cursor->hcursor)
SetCursor (hcursor);
if (!DestroyCursor (cursor->hcursor))
g_warning (G_STRLOC ": DestroyCursor (%p) failed: %lu", cursor->hcursor, GetLastError ());
cursor->hcursor = hcursor;
return TRUE;
}
void
_gdk_win32_display_update_cursors (GdkWin32Display *display)
{
GHashTableIter iter;
const char *name;
GdkWin32Cursor *cursor;
g_hash_table_iter_init (&iter, display->cursor_cache);
while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &cursor))
_gdk_win32_cursor_update (display, cursor);
}
void
_gdk_win32_display_init_cursors (GdkWin32Display *display)
{
display->cursor_cache = g_hash_table_new_full (g_str_hash,
g_str_equal,
NULL,
g_object_unref);
display->cursor_theme_name = g_strdup ("system");
}
void
_gdk_win32_display_finalize_cursors (GdkWin32Display *display)
{
g_free (display->cursor_theme_name);
if (display->cursor_theme)
win32_cursor_theme_destroy (display->cursor_theme);
g_hash_table_destroy (display->cursor_cache);
}
GdkCursor*
_gdk_win32_display_get_cursor_for_type (GdkDisplay *display,
GdkCursorType cursor_type)
{
GEnumClass *enum_class;
GEnumValue *enum_value;
gchar *cursor_name;
HCURSOR hcursor;
GdkCursor *result;
GdkWin32Display *win32_display = GDK_WIN32_DISPLAY (display);
g_return_val_if_fail (display == _gdk_display, NULL);
enum_class = g_type_class_ref (GDK_TYPE_CURSOR_TYPE);
enum_value = g_enum_get_value (enum_class, cursor_type);
cursor_name = g_strdup (enum_value->value_nick);
g_strdelimit (cursor_name, "-", '_');
g_type_class_unref (enum_class);
hcursor = hcursor_from_type (cursor_type);
result = g_hash_table_lookup (win32_display->cursor_cache, cursor_name);
if (result)
{
g_free (cursor_name);
return g_object_ref (result);
}
hcursor = hcursor_from_name (display, cursor_name);
if (hcursor == NULL)
hcursor = hcursor_from_type (cursor_type);
if (hcursor == NULL)
g_warning ("gdk_cursor_new_for_display: no cursor %d found", cursor_type);
@ -158,67 +652,62 @@ _gdk_win32_display_get_cursor_for_type (GdkDisplay *display,
GDK_NOTE (CURSOR, g_print ("gdk_cursor_new_for_display: %d: %p\n",
cursor_type, hcursor));
return cursor_new_from_hcursor (hcursor, cursor_type);
result = cursor_new_from_hcursor (hcursor, cursor_name, cursor_type);
if (result == NULL)
return result;
/* Blank cursor case */
if (cursor_type == GDK_BLANK_CURSOR ||
!cursor_name ||
g_str_equal (cursor_name, "none") ||
g_str_equal (cursor_name, "blank_cursor"))
{
g_free (cursor_name);
return result;
}
g_hash_table_insert (win32_display->cursor_cache,
cursor_name,
g_object_ref (result));
return result;
}
/* FIXME: The named cursors below are presumably not really useful, as
* the names are Win32-specific. No GTK+ application developed on Unix
* (and most cross-platform GTK+ apps are developed on Unix) is going
* to look for cursors under these Win32 names anyway.
*
* Would the following make any sense: The ms-windows theme engine
* calls some (to-be-defined private) API here in gdk/win32 to
* register the relevant cursors used by the currently active XP
* visual style under the names that libgtk uses to look for them
* ("color-picker", "dnd-ask", "dnd-copy", etc), and then when libgtk
* asks for those we return the ones registered by the ms-windows
* theme engine, if any.
*/
static struct {
char *name;
char *id;
} default_cursors[] = {
{ "appstarting", IDC_APPSTARTING },
{ "arrow", IDC_ARROW },
{ "cross", IDC_CROSS },
#ifdef IDC_HAND
{ "hand", IDC_HAND },
#endif
{ "help", IDC_HELP },
{ "ibeam", IDC_IBEAM },
{ "left_ptr_watch", IDC_APPSTARTING },
{ "sizeall", IDC_SIZEALL },
{ "sizenesw", IDC_SIZENESW },
{ "sizens", IDC_SIZENS },
{ "sizenwse", IDC_SIZENWSE },
{ "sizewe", IDC_SIZEWE },
{ "uparrow", IDC_UPARROW },
{ "wait", IDC_WAIT }
};
GdkCursor*
_gdk_win32_display_get_cursor_for_name (GdkDisplay *display,
const gchar *name)
{
HCURSOR hcursor = NULL;
int i;
GdkCursor *result;
GdkWin32Display *win32_display = GDK_WIN32_DISPLAY (display);
g_return_val_if_fail (display == _gdk_display, NULL);
result = g_hash_table_lookup (win32_display->cursor_cache, name);
if (result)
return g_object_ref (result);
hcursor = hcursor_from_name (display, name);
for (i = 0; i < G_N_ELEMENTS(default_cursors); i++)
{
if (0 == strcmp(default_cursors[i].name, name))
hcursor = LoadCursor (NULL, default_cursors[i].id);
}
/* allow to load named cursor resources linked into the executable */
if (!hcursor)
hcursor = LoadCursor (_gdk_app_hmodule, name);
if (hcursor)
return cursor_new_from_hcursor (hcursor, GDK_X_CURSOR);
if (hcursor == NULL)
return NULL;
return NULL;
result = cursor_new_from_hcursor (hcursor, name, GDK_X_CURSOR);
/* Blank cursor case */
if (!name ||
g_str_equal (name, "none") ||
g_str_equal (name, "blank_cursor"))
return result;
g_hash_table_insert (win32_display->cursor_cache,
g_strdup (name),
g_object_ref (result));
return result;
}
GdkPixbuf *
@ -419,10 +908,10 @@ _gdk_win32_cursor_get_surface (GdkCursor *cursor,
}
GdkCursor *
_gdk_win32_display_get_cursor_for_surface (GdkDisplay *display,
cairo_surface_t *surface,
gdouble x,
gdouble y)
_gdk_win32_display_get_cursor_for_surface (GdkDisplay *display,
cairo_surface_t *surface,
gdouble x,
gdouble y)
{
HCURSOR hcursor;
GdkPixbuf *pixbuf;
@ -448,7 +937,7 @@ _gdk_win32_display_get_cursor_for_surface (GdkDisplay *display,
g_object_unref (pixbuf);
if (!hcursor)
return NULL;
return cursor_new_from_hcursor (hcursor, GDK_CURSOR_IS_PIXMAP);
return cursor_new_from_hcursor (hcursor, NULL, GDK_CURSOR_IS_PIXMAP);
}
gboolean
@ -474,6 +963,12 @@ _gdk_win32_display_get_default_cursor_size (GdkDisplay *display,
{
g_return_if_fail (display == _gdk_display);
/* TODO: Use per-monitor DPI functions (8.1 and newer) or
* calculate DPI ourselves and use that, assuming that 72 dpi
* corresponds to 32x32 cursors. Take into account that DPI
* can be artificially increased by the user to make stuff bigger.
*/
if (width)
*width = GetSystemMetrics (SM_CXCURSOR);
if (height)

View File

@ -26,6 +26,100 @@
#include "gdkwin32window.h"
#include "gdkwin32.h"
/**
* gdk_win32_display_set_cursor_theme:
* @display: (type GdkWin32Display): a #GdkDisplay
* @theme: (allow-none) the name of the cursor theme to use, or %NULL to unset
* a previously set value
* @size: the cursor size to use, or 0 to keep the previous size
*
* Sets the cursor theme from which the images for cursor
* should be taken.
*
* If the windowing system supports it, existing cursors created
* with gdk_cursor_new(), gdk_cursor_new_for_display() and
* gdk_cursor_new_from_name() are updated to reflect the theme
* change. Custom cursors constructed with
* gdk_cursor_new_from_pixbuf() will have to be handled
* by the application (GTK+ applications can learn about
* cursor theme changes by listening for change notification
* for the corresponding #GtkSetting).
*
* Since: 3.18
*/
void
gdk_win32_display_set_cursor_theme (GdkDisplay *display,
const gchar *name,
const gint size)
{
gint cursor_size;
gint w, h;
Win32CursorTheme *theme;
GdkWin32Display *win32_display = GDK_WIN32_DISPLAY (display);
g_assert (win32_display);
if (name == NULL)
name = "system";
w = GetSystemMetrics (SM_CXCURSOR);
h = GetSystemMetrics (SM_CYCURSOR);
/* We can load cursors of any size, but SetCursor() will scale them back
* to this value. It's possible to break that restrictions with SetSystemCursor(),
* but that will override cursors for the whole desktop session.
*/
cursor_size = (w == h) ? w : size;
if (win32_display->cursor_theme_name != NULL &&
g_strcmp0 (name, win32_display->cursor_theme_name) == 0 &&
win32_display->cursor_theme_size == cursor_size)
return;
theme = win32_cursor_theme_load (name, cursor_size);
if (theme == NULL)
{
g_warning ("Failed to load cursor theme %s", name);
return;
}
if (win32_display->cursor_theme)
{
win32_cursor_theme_destroy (win32_display->cursor_theme);
win32_display->cursor_theme = NULL;
}
win32_display->cursor_theme = theme;
g_free (win32_display->cursor_theme_name);
win32_display->cursor_theme_name = g_strdup (name);
win32_display->cursor_theme_size = cursor_size;
_gdk_win32_display_update_cursors (win32_display);
}
Win32CursorTheme *
_gdk_win32_display_get_cursor_theme (GdkWin32Display *win32_display)
{
Win32CursorTheme *theme;
g_assert (win32_display->cursor_theme_name);
theme = win32_display->cursor_theme;
if (!theme)
{
theme = win32_cursor_theme_load (win32_display->cursor_theme_name,
win32_display->cursor_theme_size);
if (theme == NULL)
{
g_warning ("Failed to load cursor theme %s",
win32_display->cursor_theme_name);
return NULL;
}
win32_display->cursor_theme = theme;
}
return theme;
}
static gulong
gdk_win32_display_get_next_serial (GdkDisplay *display)
@ -542,11 +636,17 @@ gdk_win32_display_dispose (GObject *object)
static void
gdk_win32_display_finalize (GObject *object)
{
GdkWin32Display *display_win32 = GDK_WIN32_DISPLAY (object);
_gdk_win32_display_finalize_cursors (display_win32);
G_OBJECT_CLASS (gdk_win32_display_parent_class)->finalize (object);
}
static void
gdk_win32_display_init(GdkWin32Display *display)
gdk_win32_display_init (GdkWin32Display *display)
{
_gdk_win32_display_init_cursors (display);
}
static void

View File

@ -26,6 +26,11 @@ struct _GdkWin32Display
{
GdkDisplay display;
Win32CursorTheme *cursor_theme;
gchar *cursor_theme_name;
int cursor_theme_size;
GHashTable *cursor_cache;
/* WGL/OpenGL Items */
guint have_wgl : 1;
guint gl_version;

View File

@ -37,6 +37,7 @@
#include <gdk/gdkprivate.h>
#include <gdk/gdkcursorprivate.h>
#include <gdk/win32/gdkwindow-win32.h>
#include <gdk/win32/gdkwin32display.h>
#include "gdkinternals.h"
@ -114,6 +115,8 @@ typedef struct _GdkWin32SingleFont GdkWin32SingleFont;
struct _GdkWin32Cursor
{
GdkCursor cursor;
gchar *name;
HCURSOR hcursor;
};
@ -374,6 +377,42 @@ HICON _gdk_win32_pixbuf_to_hcursor (GdkPixbuf *pixbuf,
gint x_hotspot,
gint y_hotspot);
void _gdk_win32_display_init_cursors (GdkWin32Display *display);
void _gdk_win32_display_finalize_cursors (GdkWin32Display *display);
void _gdk_win32_display_update_cursors (GdkWin32Display *display);
typedef struct _Win32CursorTheme Win32CursorTheme;
struct _Win32CursorTheme {
GHashTable *named_cursors;
};
typedef enum GdkWin32CursorLoadType {
GDK_WIN32_CURSOR_LOAD_FROM_FILE = 0,
GDK_WIN32_CURSOR_LOAD_FROM_RESOURCE_NULL = 1,
GDK_WIN32_CURSOR_LOAD_FROM_RESOURCE_THIS = 2,
GDK_WIN32_CURSOR_CREATE = 3,
} GdkWin32CursorLoadType;
typedef struct _Win32Cursor Win32Cursor;
struct _Win32Cursor {
GdkWin32CursorLoadType load_type;
gunichar2 *resource_name;
gint width;
gint height;
guint load_flags;
gint xcursor_number;
GdkCursorType cursor_type;
};
Win32CursorTheme *win32_cursor_theme_load (const gchar *name,
gint size);
Win32Cursor * win32_cursor_theme_get_cursor (Win32CursorTheme *theme,
const gchar *name);
void win32_cursor_theme_destroy (Win32CursorTheme *theme);
Win32CursorTheme *_gdk_win32_display_get_cursor_theme (GdkWin32Display *win32_display);
/* GdkDisplay member functions */
GdkCursor *_gdk_win32_display_get_cursor_for_type (GdkDisplay *display,
GdkCursorType cursor_type);

View File

@ -50,6 +50,11 @@ typedef struct _GdkWin32DisplayClass GdkWin32DisplayClass;
GDK_AVAILABLE_IN_ALL
GType gdk_win32_display_get_type (void);
GDK_AVAILABLE_IN_3_18
void gdk_win32_display_set_cursor_theme (GdkDisplay *display,
const gchar *theme,
gint size);
G_END_DECLS
#endif /* __GDK_WIN32_DISPLAY_H__ */

View File

@ -1972,7 +1972,10 @@ gdk_win32_window_set_device_cursor (GdkWindow *window,
if (cursor)
impl->cursor = g_object_ref (cursor);
else
impl->cursor = NULL;
/* Use default cursor otherwise. Setting it to NULL will make it use
* system-default cursor, which is not controlled by GTK cursor theming.
*/
impl->cursor = _gdk_win32_display_get_cursor_for_type (_gdk_display, GDK_LEFT_PTR);
/* Destroy the previous cursor */
if (previous_cursor != NULL)

View File

@ -51,6 +51,10 @@
#include "quartz/gdkquartz.h"
#endif
#ifdef GDK_WINDOWING_WIN32
#include "win32/gdkwin32.h"
#endif
#ifdef G_OS_WIN32
#include "gtkwin32themeprivate.h"
#endif
@ -2918,7 +2922,7 @@ settings_update_cursor_theme (GtkSettings *settings)
{
gchar *theme = NULL;
gint size = 0;
#if defined(GDK_WINDOWING_X11) || defined(GDK_WINDOWING_WAYLAND)
#if defined(GDK_WINDOWING_X11) || defined(GDK_WINDOWING_WAYLAND) || defined(GDK_WINDOWING_WIN32)
GdkDisplay *display = gdk_screen_get_display (settings->priv->screen);
#endif
@ -2937,6 +2941,11 @@ settings_update_cursor_theme (GtkSettings *settings)
if (GDK_IS_WAYLAND_DISPLAY (display))
gdk_wayland_display_set_cursor_theme (display, theme, size);
else
#endif
#ifdef GDK_WINDOWING_WIN32
if (GDK_IS_WIN32_DISPLAY (display))
gdk_win32_display_set_cursor_theme (display, theme, size);
else
#endif
g_warning ("GtkSettings Cursor Theme: Unsupported GDK backend\n");
g_free (theme);