display.c revision 6b7436ae
11.21Smjacob/*
21.5Scgd * Copyright © 2024 Thomas E. Dickey
31.1Scgd * Copyright © 2002 Keith Packard
41.11Sbouyer *
51.4Smycroft * Permission to use, copy, modify, distribute, and sell this software and its
61.4Smycroft * documentation for any purpose is hereby granted without fee, provided that
71.4Smycroft * the above copyright notice appear in all copies and that both that
81.4Smycroft * copyright notice and this permission notice appear in supporting
91.1Scgd * documentation, and that the name of Keith Packard not be used in
101.1Scgd * advertising or publicity pertaining to distribution of the software without
111.1Scgd * specific, written prior permission.  Keith Packard makes no
121.1Scgd * representations about the suitability of this software for any purpose.  It
131.1Scgd * is provided "as is" without express or implied warranty.
141.1Scgd *
151.1Scgd * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
161.12Senami * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
171.1Scgd * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
181.1Scgd * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
191.1Scgd * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
201.1Scgd * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
211.1Scgd * PERFORMANCE OF THIS SOFTWARE.
221.1Scgd */
231.1Scgd
241.1Scgd#include "xcursorint.h"
251.12Senami#include <X11/Xlibint.h>
261.1Scgd#include <ctype.h>
271.1Scgd#include <unistd.h>		/* for getpid */
281.1Scgd
291.1Scgdstatic XcursorDisplayInfo *_XcursorDisplayInfo;
301.1Scgd
311.1Scgdstatic void
321.1Scgd_XcursorFreeDisplayInfo (XcursorDisplayInfo *info)
331.1Scgd{
341.1Scgd    if (info->theme)
351.1Scgd	free (info->theme);
361.1Scgd
371.1Scgd    if (info->theme_from_config)
381.1Scgd	free (info->theme_from_config);
391.1Scgd
401.1Scgd    while (info->fonts)
411.12Senami    {
421.1Scgd      XcursorFontInfo *fi = info->fonts;
431.1Scgd      info->fonts = fi->next;
441.1Scgd      free (fi);
451.1Scgd    }
461.1Scgd
471.1Scgd    free (info);
481.1Scgd}
491.1Scgd
501.1Scgdstatic int
511.1Scgd_XcursorCloseDisplay (Display *dpy, XExtCodes *codes)
521.1Scgd{
531.1Scgd    XcursorDisplayInfo  *info, **prev;
541.1Scgd
551.17Sthorpej    (void) codes;	/* UNUSED */
561.17Sthorpej
571.17Sthorpej    /*
581.17Sthorpej     * Unhook from the global list
591.17Sthorpej     */
601.17Sthorpej    _XLockMutex (_Xglobal_lock);
611.17Sthorpej    for (prev = &_XcursorDisplayInfo; (info = *prev); prev = &(*prev)->next)
621.17Sthorpej	if (info->display == dpy)
631.17Sthorpej	{
641.17Sthorpej            *prev = info->next;
651.17Sthorpej	    break;
661.17Sthorpej	}
671.17Sthorpej    _XUnlockMutex (_Xglobal_lock);
681.17Sthorpej
691.17Sthorpej    if (info)
701.17Sthorpej	_XcursorFreeDisplayInfo (info);
711.17Sthorpej    return 0;
721.17Sthorpej}
731.17Sthorpej
741.17Sthorpejstatic int
751.17Sthorpej_XcursorDefaultParseBool (char *v)
761.17Sthorpej{
771.17Sthorpej    char    c0;
781.17Sthorpej
791.17Sthorpej    c0 = *v;
801.17Sthorpej    if (isupper ((int)c0))
811.17Sthorpej	c0 = (char) tolower (c0);
821.17Sthorpej    if (c0 == 't' || c0 == 'y' || c0 == '1')
831.17Sthorpej	return 1;
841.17Sthorpej    if (c0 == 'f' || c0 == 'n' || c0 == '0')
851.17Sthorpej	return 0;
861.17Sthorpej    if (c0 == 'o')
871.17Sthorpej    {
881.17Sthorpej	char c1 = v[1];
891.17Sthorpej	if (isupper ((int)c1))
901.17Sthorpej	    c1 = (char) tolower (c1);
911.17Sthorpej	if (c1 == 'n')
921.17Sthorpej	    return 1;
931.17Sthorpej	if (c1 == 'f')
941.17Sthorpej	    return 0;
951.17Sthorpej    }
961.17Sthorpej    return -1;
971.17Sthorpej}
981.17Sthorpej
991.17SthorpejXcursorDisplayInfo *
1001.17Sthorpej_XcursorGetDisplayInfo (Display *dpy)
1011.17Sthorpej{
1021.17Sthorpej    XcursorDisplayInfo	*info, **prev, *old;
1031.17Sthorpej    int			event_base, error_base;
1041.17Sthorpej    int			major, minor;
1051.17Sthorpej    char		*v;
1061.17Sthorpej    int			i;
1071.17Sthorpej
1081.17Sthorpej    _XLockMutex (_Xglobal_lock);
1091.17Sthorpej    for (prev = &_XcursorDisplayInfo; (info = *prev); prev = &(*prev)->next)
1101.17Sthorpej    {
1111.17Sthorpej	if (info->display == dpy)
1121.17Sthorpej	{
1131.17Sthorpej	    /*
1141.17Sthorpej	     * MRU the list
1151.17Sthorpej	     */
1161.17Sthorpej	    if (prev != &_XcursorDisplayInfo)
1171.17Sthorpej	    {
1181.17Sthorpej		*prev = info->next;
1191.17Sthorpej		info->next = _XcursorDisplayInfo;
1201.17Sthorpej		_XcursorDisplayInfo = info;
1211.17Sthorpej	    }
1221.17Sthorpej	    break;
1231.17Sthorpej	}
1241.21Smjacob    }
1251.17Sthorpej    _XUnlockMutex (_Xglobal_lock);
1261.21Smjacob    if (info)
1271.17Sthorpej        return info;
1281.17Sthorpej    info = (XcursorDisplayInfo *) malloc (sizeof (XcursorDisplayInfo));
1291.17Sthorpej    if (!info)
1301.17Sthorpej	return NULL;
1311.21Smjacob    info->next = NULL;
1321.17Sthorpej    info->display = dpy;
1331.21Smjacob
1341.17Sthorpej    info->codes = XAddExtension (dpy);
1351.1Scgd    if (!info->codes)
1361.11Sbouyer    {
1371.11Sbouyer	free (info);
1381.11Sbouyer	return NULL;
1391.11Sbouyer    }
1401.11Sbouyer    (void) XESetCloseDisplay (dpy, info->codes->extension, _XcursorCloseDisplay);
1411.11Sbouyer
1421.7Smycroft    /*
1431.9Smycroft     * The debugging-trace for new info-blocks begins here.
1441.9Smycroft     * As a reminder that multiple processes/threads use this library,
1451.9Smycroft     * the current process-id is logged.
1461.13Sthorpej     */
1471.13Sthorpej    traceOpts((T_CALLED(_XcursorGetDisplayInfo) " info %p, pid %d\n",
1481.13Sthorpej	      (void*)info, getpid()));
1491.13Sthorpej
1501.13Sthorpej    /*
1511.13Sthorpej     * Check whether the display supports the Render CreateCursor request
1521.13Sthorpej     */
1531.13Sthorpej    info->has_render_cursor = XcursorFalse;
1541.13Sthorpej    info->has_anim_cursor = XcursorFalse;
1551.13Sthorpej    if (XRenderQueryExtension (dpy, &event_base, &error_base) &&
1561.13Sthorpej	XRenderQueryVersion (dpy, &major, &minor))
1571.9Smycroft    {
1581.1Scgd	if (major > 0 || minor >= 5)
1591.1Scgd	{
1601.11Sbouyer	    info->has_render_cursor = XcursorTrue;
1611.11Sbouyer	    v = getenv ("XCURSOR_CORE");
1621.7Smycroft	    if (!v)
1631.9Smycroft		v = XGetDefault (dpy, "Xcursor", "core");
1641.9Smycroft	    if (v && _XcursorDefaultParseBool (v) == 1)
1651.4Smycroft		info->has_render_cursor = XcursorFalse;
1661.9Smycroft	    traceOpts((T_OPTION(XCURSOR_CORE) ": %d\n", info->has_render_cursor));
1671.16Sthorpej	}
1681.16Sthorpej	if (info->has_render_cursor && (major > 0 || minor >= 8))
1691.16Sthorpej	{
1701.16Sthorpej	    info->has_anim_cursor = XcursorTrue;
1711.16Sthorpej	    v = getenv ("XCURSOR_ANIM");
1721.16Sthorpej	    if (!v)
1731.16Sthorpej		v = XGetDefault (dpy, "Xcursor", "anim");
1741.16Sthorpej	    if (v && _XcursorDefaultParseBool (v) == 0)
1751.16Sthorpej		info->has_anim_cursor = XcursorFalse;
1761.16Sthorpej	    traceOpts((T_OPTION(XCURSOR_ANIM) ": %d\n", info->has_anim_cursor));
1771.16Sthorpej	}
1781.16Sthorpej    }
1791.16Sthorpej
1801.16Sthorpej    info->size = 0;
1811.16Sthorpej
1821.9Smycroft    /*
1831.1Scgd     * Get desired cursor size
1841.1Scgd     */
1851.11Sbouyer    v = getenv ("XCURSOR_SIZE");
1861.1Scgd    if (!v)
1871.7Smycroft	v = XGetDefault (dpy, "Xcursor", "size");
1881.9Smycroft    if (v)
1891.9Smycroft	info->size = atoi (v);
1901.7Smycroft    traceOpts((T_OPTION(XCURSOR_SIZE) ": %d\n", info->size));
1911.9Smycroft
1921.1Scgd    /*
1931.1Scgd     * Use the Xft size to guess a size; make cursors 16 "points" tall
1941.1Scgd     */
1951.11Sbouyer    if (info->size == 0)
1961.9Smycroft    {
1971.1Scgd	int dpi = 0;
1981.9Smycroft	v = XGetDefault (dpy, "Xft", "dpi");
1991.9Smycroft	if (v)
2001.9Smycroft	    dpi = atoi (v);
2011.9Smycroft	if (dpi)
2021.9Smycroft	    info->size = dpi * 16 / 72;
2031.9Smycroft	traceOpts((T_OPTION(XCURSOR_SIZE) ": %d\n", info->size));
2041.9Smycroft    }
2051.9Smycroft
2061.9Smycroft    /*
2071.9Smycroft     * Use display size to guess a size
2081.9Smycroft     */
2091.9Smycroft    if (info->size == 0)
2101.9Smycroft    {
2111.9Smycroft	int dim;
2121.9Smycroft
2131.9Smycroft	if (DisplayHeight (dpy, DefaultScreen (dpy)) <
2141.9Smycroft	    DisplayWidth (dpy, DefaultScreen (dpy)))
2151.9Smycroft	    dim = DisplayHeight (dpy, DefaultScreen (dpy));
2161.1Scgd	else
2171.1Scgd	    dim = DisplayWidth (dpy, DefaultScreen (dpy));
2181.9Smycroft	/*
2191.9Smycroft	 * 16 pixels on a display of dimension 768
2201.9Smycroft	 */
2211.9Smycroft	info->size = dim / 48;
2221.9Smycroft	traceOpts((T_OPTION(XCURSOR_SIZE) ": %d\n", info->size));
2231.9Smycroft    }
2241.9Smycroft
2251.9Smycroft    info->theme = NULL;
2261.9Smycroft    info->theme_from_config = NULL;
2271.8Sthorpej
2281.8Sthorpej    /*
2291.8Sthorpej     * Provide for making cursors resized to match the requested size
2301.8Sthorpej     */
2311.8Sthorpej    info->resized_cursors = XcursorFalse;
2321.9Smycroft    v = getenv ("XCURSOR_RESIZED");
2331.9Smycroft    if (!v)
2341.9Smycroft	v = XGetDefault (dpy, "Xcursor", "resized");
2351.9Smycroft    if (v)
2361.9Smycroft    {
2371.1Scgd	i = _XcursorDefaultParseBool (v);
2381.10Schristos	if (i >= 0)
2391.10Schristos	    info->resized_cursors = i;
2401.10Schristos    }
2411.10Schristos    traceOpts((T_OPTION(XCURSOR_RESIZED) ": %d\n", info->resized_cursors));
2421.10Schristos
2431.10Schristos    /*
2441.10Schristos     * Get the desired theme
2451.10Schristos     */
2461.10Schristos    v = getenv ("XCURSOR_THEME");
2471.10Schristos    if (!v)
2481.10Schristos	v = XGetDefault (dpy, "Xcursor", "theme");
2491.10Schristos    if (v)
2501.10Schristos    {
2511.10Schristos	info->theme = strdup (v);
2521.10Schristos	info->theme_from_config = strdup (v);
2531.10Schristos    }
2541.10Schristos    traceOpts((T_OPTION(XCURSOR_THEME) ": %s\n", NonNull(info->theme)));
2551.10Schristos
2561.10Schristos    /*
2571.10Schristos     * Get the desired dither
2581.10Schristos     */
2591.10Schristos    info->dither = XcursorDitherThreshold;
2601.10Schristos    v = getenv ("XCURSOR_DITHER");
2611.10Schristos    if (!v)
2621.10Schristos	v = XGetDefault (dpy, "Xcursor", "dither");
2631.10Schristos    if (v)
2641.10Schristos    {
2651.10Schristos	if (!strcmp (v, "threshold"))
2661.10Schristos	    info->dither = XcursorDitherThreshold;
2671.10Schristos	if (!strcmp (v, "median"))
2681.18Sthorpej	    info->dither = XcursorDitherMedian;
2691.18Sthorpej	if (!strcmp (v, "ordered"))
2701.18Sthorpej	    info->dither = XcursorDitherOrdered;
2711.18Sthorpej	if (!strcmp (v, "diffuse"))
2721.18Sthorpej	    info->dither = XcursorDitherDiffuse;
2731.18Sthorpej    }
2741.18Sthorpej    traceOpts((T_OPTION(XCURSOR_DITHER) ": %d\n", info->dither));
2751.20Sthorpej
2761.20Sthorpej    info->theme_core = False;
2771.20Sthorpej    /*
2781.20Sthorpej     * Find out if core cursors should
2791.20Sthorpej     * be themed
2801.18Sthorpej     */
2811.18Sthorpej    v = getenv ("XCURSOR_THEME_CORE");
2821.18Sthorpej    if (!v)
2831.18Sthorpej	v = XGetDefault (dpy, "Xcursor", "theme_core");
2841.18Sthorpej    if (v)
2851.18Sthorpej    {
2861.18Sthorpej	i = _XcursorDefaultParseBool (v);
2871.18Sthorpej	if (i >= 0)
2881.18Sthorpej	    info->theme_core = i;
2891.20Sthorpej    }
2901.20Sthorpej    traceOpts((T_OPTION(XCURSOR_THEME_CORE) ": %d\n", info->theme_core));
2911.20Sthorpej
2921.20Sthorpej    info->fonts = NULL;
2931.20Sthorpej    for (i = 0; i < NUM_BITMAPS; i++)
2941.20Sthorpej	info->bitmaps[i].bitmap = None;
2951.20Sthorpej
2961.20Sthorpej    /*
2971.20Sthorpej     * Link new info info list, making sure another
2981.20Sthorpej     * thread hasn't inserted something into the list while
2991.19Sthorpej     * this one was busy setting up the data
3001.7Smycroft     */
301    _XLockMutex (_Xglobal_lock);
302    for (old = _XcursorDisplayInfo; old; old = old->next)
303	if (old->display == dpy)
304	    break;
305    if (old)
306    {
307	_XcursorFreeDisplayInfo (info);
308	info = old;
309    }
310    else
311    {
312	info->next = _XcursorDisplayInfo;
313	_XcursorDisplayInfo = info;
314    }
315    _XUnlockMutex (_Xglobal_lock);
316
317    returnAddr(info);
318}
319
320XcursorBool
321XcursorSupportsARGB (Display *dpy)
322{
323    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
324
325    return info && info->has_render_cursor;
326}
327
328XcursorBool
329XcursorSupportsAnim (Display *dpy)
330{
331    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
332
333    return info && info->has_anim_cursor;
334}
335
336XcursorBool
337XcursorSetDefaultSize (Display *dpy, int size)
338{
339    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
340
341    if (!info)
342	return XcursorFalse;
343    info->size = size;
344    return XcursorTrue;
345}
346
347int
348XcursorGetDefaultSize (Display *dpy)
349{
350    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
351
352    if (!info)
353	return 0;
354    return info->size;
355}
356
357XcursorBool
358XcursorSetResizable (Display *dpy, XcursorBool flag)
359{
360    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
361
362    if (!info)
363	return XcursorFalse;
364    info->resized_cursors = flag;
365    return XcursorTrue;
366}
367
368XcursorBool
369XcursorGetResizable (Display *dpy)
370{
371    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
372
373    if (!info)
374	return 0;
375    return info->resized_cursors;
376}
377
378XcursorBool
379XcursorSetTheme (Display *dpy, const char *theme)
380{
381    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
382    char		*copy;
383
384    if (!info)
385	return XcursorFalse;
386
387    if (!theme)
388	theme = info->theme_from_config;
389
390    if (theme)
391    {
392	copy = strdup (theme);
393	if (!copy)
394	    return XcursorFalse;
395    }
396    else
397	copy = NULL;
398    if (info->theme)
399	free (info->theme);
400    info->theme = copy;
401    return XcursorTrue;
402}
403
404char *
405XcursorGetTheme (Display *dpy)
406{
407    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
408
409    if (!info)
410	return NULL;
411    return info->theme;
412}
413
414XcursorBool
415XcursorGetThemeCore (Display *dpy)
416{
417    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
418
419    if (!info)
420	return XcursorFalse;
421    return info->theme_core;
422}
423
424XcursorBool
425XcursorSetThemeCore (Display *dpy, XcursorBool theme_core)
426{
427    XcursorDisplayInfo	*info = _XcursorGetDisplayInfo (dpy);
428
429    if (!info)
430	return XcursorFalse;
431    info->theme_core = theme_core;
432    return XcursorTrue;
433}
434