forked from AuroraMiddleware/gtk
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:
parent
641fbd86d7
commit
26c24328d5
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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__ */
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user