/* 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"
#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>
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
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 },
{ "all-resize" , "move" , XC_target }, /* not CSS, but we want to guarantee it anyway */
};
#ifdef HAVE_XCURSOR
static XcursorImage*
create_cursor_image (GdkTexture *texture,
int x,
int y)
{
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;
xcimage = create_cursor_image (texture, x, y);
xcursor = XcursorImageLoadCursor (GDK_DISPLAY_XDISPLAY (display), xcimage);
XcursorImageDestroy (xcimage);
return xcursor;
}
static const char *
name_fallback (const char *name)
{
int 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 char *name)
{
int 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 [ ctor @ Gdk . Cursor . new_from_name ] are updated to reflect the theme
* change . Custom cursors constructed with [ ctor @ 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 ` ) .
*
* Deprecated : 4 . 16 : Use the cursor - related properties of
* [ GtkSettings ] ( . . / gtk4 / class . Settings . html ) to set the cursor theme
*/
void
gdk_x11_display_set_cursor_theme (GdkDisplay *display,
const char *theme,
const int size)
{
_gdk_x11_display_set_cursor_theme (display, theme, size);
}
void
_gdk_x11_display_set_cursor_theme (GdkDisplay *display,
const char *theme,
const int size)
{
#if defined (HAVE_XCURSOR) && defined (HAVE_XFIXES) && XFIXES_MAJOR >= 2
GdkX11Screen *x11_screen;
Display *xdisplay;
char *old_theme;
int real_size;
int old_size;
gpointer cursor, xcursor;
GHashTableIter iter;
g_return_if_fail (GDK_IS_DISPLAY (display));
x11_screen = gdk_x11_display_get_screen (display);
xdisplay = GDK_DISPLAY_XDISPLAY (display);
real_size = size * x11_screen->surface_scale;
old_theme = XcursorGetTheme (xdisplay);
old_size = XcursorGetDefaultSize (xdisplay);
if (old_size == real_size &&
(old_theme == theme ||
(old_theme && theme && strcmp (old_theme, theme) == 0 )))
return ;
XcursorSetTheme (xdisplay, theme);
if (real_size > 0 )
XcursorSetDefaultSize (xdisplay, real_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
}
Cursor
_gdk_x11_display_get_xcursor_with_scale (GdkDisplay *display,
GdkCursor *cursor,
int scale)
{
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 if (gdk_cursor_get_texture (cursor))
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));
else
{
int size;
GdkTexture *texture;
int width, height;
int hotspot_x, hotspot_y;
size = XcursorGetDefaultSize (GDK_DISPLAY_XDISPLAY (display));
texture = gdk_cursor_get_texture_for_size (cursor, size, scale,
&width, &height,
&hotspot_x, &hotspot_y);
if (texture)
{
xcursor = gdk_x11_cursor_create_for_texture (display,
texture,
hotspot_x,
hotspot_y);
g_object_unref (texture);
}
}
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_with_scale (display, gdk_cursor_get_fallback (cursor), scale);
return None;
}
/**
* 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 .
*
* Deprecated : 4 . 18
*/
Cursor
gdk_x11_display_get_xcursor (GdkDisplay *display,
GdkCursor *cursor)
{
return _gdk_x11_display_get_xcursor_with_scale (display, cursor, 1 );
}
Messung V0.5 in Prozent C=98 H=95 G=96
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-07-02)
¤
*© Formatika GbR, Deutschland