gtk2/gdk/x11/gdkcursor-x11.c
Matthias Clasen a46f9af1c0 Remove primary monitor api
We only have implementations of this on X11 and Win32,
so make it available as backend api there.

Update all callers to use either the backend api, or
just monitor 0.
2020-01-30 21:33:37 +01:00

389 lines
12 KiB
C

/* GDK - The GIMP Drawing Kit
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* 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/>.
*/
/*
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
#include "config.h"
/* needs to be first because any header might include gdk-pixbuf.h otherwise */
#define GDK_PIXBUF_ENABLE_BACKEND
#include <gdk-pixbuf/gdk-pixbuf.h>
#include "gdkcursor.h"
#include "gdkcursorprivate.h"
#include "gdkprivate-x11.h"
#include "gdkdisplay-x11.h"
#include <X11/Xlib.h>
#include <X11/cursorfont.h>
#ifdef HAVE_XCURSOR
#include <X11/Xcursor/Xcursor.h>
#endif
#ifdef HAVE_XFIXES
#include <X11/extensions/Xfixes.h>
#endif
#include <string.h>
static void
gdk_x11_cursor_remove_from_cache (gpointer data, GObject *cursor)
{
GdkDisplay *display = data;
Cursor xcursor;
xcursor = GDK_POINTER_TO_XID (g_hash_table_lookup (GDK_X11_DISPLAY (display)->cursors, cursor));
XFreeCursor (GDK_DISPLAY_XDISPLAY (display), xcursor);
g_hash_table_remove (GDK_X11_DISPLAY (display)->cursors, cursor);
}
void
_gdk_x11_cursor_display_finalize (GdkDisplay *display)
{
GHashTableIter iter;
gpointer cursor;
if (GDK_X11_DISPLAY (display)->cursors)
{
g_hash_table_iter_init (&iter, GDK_X11_DISPLAY (display)->cursors);
while (g_hash_table_iter_next (&iter, &cursor, NULL))
g_object_weak_unref (G_OBJECT (cursor), gdk_x11_cursor_remove_from_cache, display);
g_hash_table_unref (GDK_X11_DISPLAY (display)->cursors);
}
}
static Cursor
get_blank_cursor (GdkDisplay *display)
{
Pixmap pixmap;
XColor color;
Cursor cursor;
cairo_surface_t *surface;
cairo_t *cr;
surface = _gdk_x11_display_create_bitmap_surface (display, 1, 1);
/* Clear surface */
cr = cairo_create (surface);
cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
cairo_paint (cr);
cairo_destroy (cr);
pixmap = cairo_xlib_surface_get_drawable (surface);
color.pixel = 0;
color.red = color.blue = color.green = 0;
if (gdk_display_is_closed (display))
cursor = None;
else
cursor = XCreatePixmapCursor (GDK_DISPLAY_XDISPLAY (display),
pixmap, pixmap,
&color, &color, 1, 1);
cairo_surface_destroy (surface);
return cursor;
}
static const struct {
const char *css_name;
const char *traditional_name;
int cursor_glyph;
} name_map[] = {
{ "default", "left_ptr", XC_left_ptr, },
{ "help", "question_arrow", XC_question_arrow },
{ "context-menu", "left_ptr", XC_left_ptr },
{ "pointer", "hand", XC_hand1 },
{ "progress", "left_ptr_watch", XC_watch },
{ "wait", "watch", XC_watch },
{ "cell", "crosshair", XC_plus },
{ "crosshair", "cross", XC_crosshair },
{ "text", "xterm", XC_xterm },
{ "vertical-text","xterm", XC_xterm },
{ "alias", "dnd-link", XC_target },
{ "copy", "dnd-copy", XC_target },
{ "move", "dnd-move", XC_target },
{ "no-drop", "dnd-none", XC_pirate },
{ "dnd-ask", "dnd-copy", XC_target }, /* not CSS, but we want to guarantee it anyway */
{ "not-allowed", "crossed_circle", XC_pirate },
{ "grab", "hand2", XC_hand2 },
{ "grabbing", "hand2", XC_hand2 },
{ "all-scroll", "left_ptr", XC_left_ptr },
{ "col-resize", "h_double_arrow", XC_sb_h_double_arrow },
{ "row-resize", "v_double_arrow", XC_sb_v_double_arrow },
{ "n-resize", "top_side", XC_top_side },
{ "e-resize", "right_side", XC_right_side },
{ "s-resize", "bottom_side", XC_bottom_side },
{ "w-resize", "left_side", XC_left_side },
{ "ne-resize", "top_right_corner", XC_top_right_corner },
{ "nw-resize", "top_left_corner", XC_top_left_corner },
{ "se-resize", "bottom_right_corner", XC_bottom_right_corner },
{ "sw-resize", "bottom_left_corner", XC_bottom_left_corner },
{ "ew-resize", "h_double_arrow", XC_sb_h_double_arrow },
{ "ns-resize", "v_double_arrow", XC_sb_v_double_arrow },
{ "nesw-resize", "fd_double_arrow", XC_X_cursor },
{ "nwse-resize", "bd_double_arrow", XC_X_cursor },
{ "zoom-in", "left_ptr", XC_draped_box },
{ "zoom-out", "left_ptr", XC_draped_box }
};
#ifdef HAVE_XCURSOR
static XcursorImage*
create_cursor_image (GdkTexture *texture,
gint x,
gint y,
gint scale)
{
XcursorImage *xcimage;
xcimage = XcursorImageCreate (gdk_texture_get_width (texture), gdk_texture_get_height (texture));
xcimage->xhot = x;
xcimage->yhot = y;
gdk_texture_download (texture,
(guchar *) xcimage->pixels,
gdk_texture_get_width (texture) * 4);
return xcimage;
}
static Cursor
gdk_x11_cursor_create_for_texture (GdkDisplay *display,
GdkTexture *texture,
int x,
int y)
{
XcursorImage *xcimage;
Cursor xcursor;
int target_scale;
target_scale =
gdk_monitor_get_scale_factor (gdk_x11_display_get_primary_monitor (display));
xcimage = create_cursor_image (texture, x, y, target_scale);
xcursor = XcursorImageLoadCursor (GDK_DISPLAY_XDISPLAY (display), xcimage);
XcursorImageDestroy (xcimage);
return xcursor;
}
static const gchar *
name_fallback (const gchar *name)
{
gint i;
for (i = 0; i < G_N_ELEMENTS (name_map); i++)
{
if (g_str_equal (name_map[i].css_name, name))
return name_map[i].traditional_name;
}
return NULL;
}
static Cursor
gdk_x11_cursor_create_for_name (GdkDisplay *display,
const char *name)
{
Cursor xcursor;
Display *xdisplay;
if (strcmp (name, "none") == 0)
{
xcursor = get_blank_cursor (display);
}
else
{
xdisplay = GDK_DISPLAY_XDISPLAY (display);
xcursor = XcursorLibraryLoadCursor (xdisplay, name);
if (xcursor == None)
{
const char *fallback;
fallback = name_fallback (name);
if (fallback)
xcursor = XcursorLibraryLoadCursor (xdisplay, fallback);
}
}
return xcursor;
}
#else
static Cursor
gdk_x11_cursor_create_for_texture (GdkDisplay *display,
GdkTexture *texture,
int x,
int y)
{
return None;
}
static Cursor
gdk_x11_cursor_create_for_name (GdkDisplay *display,
const gchar *name)
{
gint i;
if (g_str_equal (name, "none"))
return get_blank_cursor (display);
for (i = 0; i < G_N_ELEMENTS (name_map); i++)
{
if (g_str_equal (name_map[i].css_name, name) ||
g_str_equal (name_map[i].traditional_name, name))
return XCreateFontCursor (GDK_DISPLAY_XDISPLAY (display), name_map[i].cursor_glyph);
}
return None;
}
#endif
/**
* gdk_x11_display_set_cursor_theme:
* @display: (type GdkX11Display): a #GdkDisplay
* @theme: (nullable): 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_texture() 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).
*/
void
gdk_x11_display_set_cursor_theme (GdkDisplay *display,
const gchar *theme,
const gint size)
{
#if defined(HAVE_XCURSOR) && defined(HAVE_XFIXES) && XFIXES_MAJOR >= 2
Display *xdisplay;
gchar *old_theme;
gint old_size;
gpointer cursor, xcursor;
GHashTableIter iter;
g_return_if_fail (GDK_IS_DISPLAY (display));
xdisplay = GDK_DISPLAY_XDISPLAY (display);
old_theme = XcursorGetTheme (xdisplay);
old_size = XcursorGetDefaultSize (xdisplay);
if (old_size == size &&
(old_theme == theme ||
(old_theme && theme && strcmp (old_theme, theme) == 0)))
return;
XcursorSetTheme (xdisplay, theme);
if (size > 0)
XcursorSetDefaultSize (xdisplay, size);
if (GDK_X11_DISPLAY (display)->cursors == NULL)
return;
g_hash_table_iter_init (&iter, GDK_X11_DISPLAY (display)->cursors);
while (g_hash_table_iter_next (&iter, &cursor, &xcursor))
{
const char *name = gdk_cursor_get_name (cursor);
if (name)
{
Cursor new_cursor = gdk_x11_cursor_create_for_name (display, name);
if (new_cursor != None)
{
XFixesChangeCursor (xdisplay, new_cursor, GDK_POINTER_TO_XID (xcursor));
g_hash_table_iter_replace (&iter, GDK_XID_TO_POINTER (new_cursor));
}
else
{
g_hash_table_iter_remove (&iter);
}
}
}
#endif
}
/**
* gdk_x11_display_get_xcursor:
* @display: (type GdkX11Display): a #GdkDisplay
* @cursor: a #GdkCursor.
*
* Returns the X cursor belonging to a #GdkCursor, potentially
* creating the cursor.
*
* Be aware that the returned cursor may not be unique to @cursor.
* It may for example be shared with its fallback cursor. On old
* X servers that don't support the XCursor extension, all cursors
* may even fall back to a few default cursors.
*
* Returns: an Xlib Cursor.
**/
Cursor
gdk_x11_display_get_xcursor (GdkDisplay *display,
GdkCursor *cursor)
{
GdkX11Display *x11_display = GDK_X11_DISPLAY (display);
Cursor xcursor;
g_return_val_if_fail (cursor != NULL, None);
if (gdk_display_is_closed (display))
return None;
if (x11_display->cursors == NULL)
x11_display->cursors = g_hash_table_new (gdk_cursor_hash, gdk_cursor_equal);
xcursor = GDK_POINTER_TO_XID (g_hash_table_lookup (x11_display->cursors, cursor));
if (xcursor)
return xcursor;
if (gdk_cursor_get_name (cursor))
xcursor = gdk_x11_cursor_create_for_name (display, gdk_cursor_get_name (cursor));
else
xcursor = gdk_x11_cursor_create_for_texture (display,
gdk_cursor_get_texture (cursor),
gdk_cursor_get_hotspot_x (cursor),
gdk_cursor_get_hotspot_y (cursor));
if (xcursor != None)
{
g_object_weak_ref (G_OBJECT (cursor), gdk_x11_cursor_remove_from_cache, display);
g_hash_table_insert (x11_display->cursors, cursor, GDK_XID_TO_POINTER (xcursor));
return xcursor;
}
if (gdk_cursor_get_fallback (cursor))
return gdk_x11_display_get_xcursor (display, gdk_cursor_get_fallback (cursor));
return None;
}