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