mirror of OpenBSD xenocara tree
github.com/openbsd/xenocara
openbsd
1/*
2 * Copyright © 2024 Thomas E. Dickey
3 * Copyright © 2002 Keith Packard
4 *
5 * Permission to use, copy, modify, distribute, and sell this software and its
6 * documentation for any purpose is hereby granted without fee, provided that
7 * the above copyright notice appear in all copies and that both that
8 * copyright notice and this permission notice appear in supporting
9 * documentation, and that the name of Keith Packard not be used in
10 * advertising or publicity pertaining to distribution of the software without
11 * specific, written prior permission. Keith Packard makes no
12 * representations about the suitability of this software for any purpose. It
13 * is provided "as is" without express or implied warranty.
14 *
15 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
21 * PERFORMANCE OF THIS SOFTWARE.
22 */
23
24#include "xcursorint.h"
25#include <X11/Xlibint.h>
26#include <X11/Xatom.h>
27#include <stdlib.h>
28
29static XcursorBool
30_XcursorFontIsCursor (Display *dpy, Font font)
31{
32 XcursorFontInfo *fi;
33 XcursorDisplayInfo *info;
34 XcursorBool ret;
35 XFontStruct *fs;
36 Atom cursor;
37
38 if (!dpy || !font)
39 return XcursorFalse;
40
41 if (font == dpy->cursor_font)
42 return XcursorTrue;
43
44 info = _XcursorGetDisplayInfo (dpy);
45 if (!info)
46 return XcursorFalse;
47 LockDisplay (dpy);
48 for (fi = info->fonts; fi; fi = fi->next)
49 if (fi->font == font)
50 {
51 ret = fi->is_cursor_font;
52 UnlockDisplay (dpy);
53 return ret;
54 }
55 UnlockDisplay (dpy);
56 ret = XcursorFalse;
57 fs = XQueryFont (dpy, font);
58 if (fs)
59 {
60 int n;
61 cursor = XInternAtom (dpy, "cursor", False);
62 for (n = 0; n < fs->n_properties; n++)
63 if (fs->properties[n].name == XA_FONT)
64 {
65 ret = (fs->properties[n].card32 == cursor);
66 break;
67 }
68 XFreeFontInfo (NULL, fs, 1);
69 }
70 fi = malloc (sizeof (XcursorFontInfo));
71 if (fi)
72 {
73 fi->font = font;
74 fi->is_cursor_font = ret;
75 LockDisplay (dpy);
76 fi->next = info->fonts;
77 info->fonts = fi;
78 UnlockDisplay (dpy);
79 }
80 return ret;
81}
82
83Cursor
84XcursorTryShapeCursor (Display *dpy,
85 Font source_font,
86 Font mask_font,
87 unsigned int source_char,
88 unsigned int mask_char,
89 XColor _Xconst *foreground,
90 XColor _Xconst *background)
91{
92 Cursor cursor = None;
93
94 enterFunc((T_CALLED(XcursorTryShapeCursor) "(%p, %lu, %lu, %u, %u, %p, %p)\n",
95 (void*)dpy, source_font, mask_font,
96 source_char, mask_char,
97 (const void*)foreground,
98 (const void*)background));
99
100 if (!dpy || !source_font || !mask_font || !foreground || !background)
101 returnLong(None);
102
103 if (!XcursorSupportsARGB (dpy) && !XcursorGetThemeCore (dpy))
104 returnLong(None);
105
106 if (source_font == mask_font &&
107 _XcursorFontIsCursor (dpy, source_font) &&
108 source_char + 1 == mask_char)
109 {
110 XcursorImages *images = _XcursorShapeLoadImages (dpy, source_char);
111
112 if (images)
113 {
114 cursor = XcursorImagesLoadCursor (dpy, images);
115 XcursorImagesDestroy (images);
116 }
117 }
118 returnLong(cursor);
119}
120
121void
122XcursorNoticeCreateBitmap (Display *dpy,
123 Pixmap pid,
124 unsigned int width,
125 unsigned int height)
126{
127 XcursorDisplayInfo *info;
128 unsigned long oldest;
129 unsigned long now;
130 int i;
131 int replace = 0;
132 XcursorBitmapInfo *bmi;
133
134 enterFunc((T_CALLED(XcursorNoticeCreateBitmap) "(%p, %lu, %u, %u)\n",
135 (void*)dpy, pid, width, height));
136
137 if (!dpy)
138 returnVoid();
139
140 if (!XcursorSupportsARGB (dpy) && !XcursorGetThemeCore (dpy))
141 returnVoid();
142
143 if (width > MAX_BITMAP_CURSOR_SIZE || height > MAX_BITMAP_CURSOR_SIZE)
144 returnVoid();
145
146 info = _XcursorGetDisplayInfo (dpy);
147 if (!info)
148 returnVoid();
149
150 LockDisplay (dpy);
151 replace = 0;
152 now = dpy->request;
153 oldest = now;
154 for (i = 0; i < NUM_BITMAPS; i++)
155 {
156 if (!info->bitmaps[i].bitmap)
157 {
158 replace = i;
159 break;
160 }
161 if ((long) (now - info->bitmaps[i].sequence) >
162 (long) (now - oldest))
163 {
164 replace = i;
165 oldest = info->bitmaps[i].sequence;
166 }
167 }
168 bmi = &info->bitmaps[replace];
169 bmi->bitmap = pid;
170 bmi->sequence = now;
171 bmi->width = width;
172 bmi->height = height;
173 bmi->has_image = False;
174 UnlockDisplay (dpy);
175
176 returnVoid();
177}
178
179static XcursorBitmapInfo *
180_XcursorGetBitmap (Display *dpy, Pixmap bitmap)
181{
182 XcursorDisplayInfo *info;
183 int i;
184
185 if (!dpy || !bitmap)
186 return NULL;
187
188 info = _XcursorGetDisplayInfo (dpy);
189
190 if (!info)
191 return NULL;
192 LockDisplay (dpy);
193 for (i = 0; i < NUM_BITMAPS; i++)
194 if (info->bitmaps[i].bitmap == bitmap)
195 {
196 info->bitmaps[i].sequence = dpy->request;
197 UnlockDisplay (dpy);
198 return &info->bitmaps[i];
199 }
200 UnlockDisplay (dpy);
201 return NULL;
202}
203
204static Bool
205_XcursorClientLSB (void)
206{
207 int v = 1;
208 return *((char *) &v) == 1;
209}
210
211/* stolen from Xlib */
212static unsigned char const _reverse_byte[0x100] = {
213 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
214 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
215 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
216 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
217 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
218 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
219 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
220 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
221 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
222 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
223 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
224 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
225 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
226 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
227 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
228 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
229 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
230 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
231 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
232 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
233 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
234 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
235 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
236 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
237 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
238 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
239 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
240 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
241 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
242 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
243 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
244 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
245};
246
247#define RotByte(t,i) (((t) << (i)) | ((t) >> (8 - (i))))
248
249void
250XcursorImageHash (XImage *image,
251 unsigned char hash[XCURSOR_BITMAP_HASH_SIZE])
252{
253 int i;
254 int x, y;
255 unsigned char *line;
256 unsigned char t;
257 int low_addr;
258 Bool bit_swap;
259
260 enterFunc((T_CALLED(XcursorImageHash) "(%p, %p)\n",
261 (void*)image, (void*)hash));
262
263 if (!image)
264 returnVoid();
265
266 for (i = 0; i < XCURSOR_BITMAP_HASH_SIZE; i++)
267 hash[i] = 0;
268 /*
269 * Flip byte order on MSB machines where the bitmap_unit isn't
270 * in bytes
271 */
272 low_addr = 0;
273 if (image->bitmap_unit != 8)
274 {
275 if (!_XcursorClientLSB())
276 switch (image->bitmap_unit) {
277 case 16:
278 low_addr = 1;
279 break;
280 case 32:
281 low_addr = 3;
282 break;
283 }
284 }
285 /*
286 * Flip bit order on MSB images
287 */
288 bit_swap = (image->bitmap_bit_order != LSBFirst);
289
290 line = (unsigned char *) image->data;
291 i = 0;
292 /*
293 * Compute the hash. Yes, it might be nice to use
294 * a stronger hash function, but MD5 and SHA1 are both
295 * a bit to expensive in time and space for this,
296 * and cursors are generally small enough that a weak
297 * hash is sufficient to distinguish among them.
298 */
299 for (y = 0; y < image->height; y++)
300 {
301 for (x = 0; x < image->bytes_per_line; x++)
302 {
303 t = line[x^low_addr];
304 if (bit_swap)
305 t = _reverse_byte[t];
306 if (t)
307 hash[(i++) & (XCURSOR_BITMAP_HASH_SIZE - 1)] ^= (unsigned char) RotByte (t, y & 7);
308 }
309 line += image->bytes_per_line;
310 }
311
312 returnVoid();
313}
314
315static Bool
316_XcursorLogDiscover (void)
317{
318 static Bool been_here;
319 static Bool log;
320
321 if (!been_here)
322 {
323 been_here = True;
324
325 if (getenv ("XCURSOR_DISCOVER"))
326 log = True;
327 traceOpts((T_OPTION(XCURSOR_DISCOVER) ": %d\n", log));
328 }
329 return log;
330}
331
332void
333XcursorNoticePutBitmap (Display *dpy,
334 Drawable draw,
335 XImage *image)
336{
337 XcursorBitmapInfo *bmi;
338
339 enterFunc((T_CALLED(XcursorNoticePutBitmap ) "(%p, %lu, %p)\n",
340 (void*)dpy, draw, (void*)image));
341
342 if (!dpy || !image)
343 returnVoid();
344
345 if (!XcursorSupportsARGB (dpy) && !XcursorGetThemeCore (dpy))
346 returnVoid();
347
348 if (image->width > MAX_BITMAP_CURSOR_SIZE ||
349 image->height > MAX_BITMAP_CURSOR_SIZE)
350 returnVoid();
351
352 bmi = _XcursorGetBitmap (dpy, (Pixmap) draw);
353 if (!bmi)
354 returnVoid();
355 /*
356 * Make sure the image fills the bitmap
357 */
358 if (image->width != (int)bmi->width || image->height != (int)bmi->height)
359 {
360 bmi->bitmap = 0;
361 returnVoid();
362 }
363 /*
364 * If multiple images are placed in the same bitmap,
365 * assume it's not going to be a cursor
366 */
367 if (bmi->has_image)
368 {
369 bmi->bitmap = 0;
370 returnVoid();
371 }
372 /*
373 * Make sure the image is valid
374 */
375 if (image->bytes_per_line & ((image->bitmap_unit >> 3) - 1))
376 {
377 bmi->bitmap = 0;
378 returnVoid();
379 }
380 /*
381 * Hash the image
382 */
383 XcursorImageHash (image, bmi->hash);
384 /*
385 * Display the hash value and the image if
386 * requested so that users can find out what
387 * cursor name is associated with each image
388 */
389 if (_XcursorLogDiscover())
390 {
391 int x, y;
392 int i;
393 XImage t = *image;
394
395 XInitImage (&t);
396
397 printf ("Cursor image name: ");
398 for (i = 0; i < XCURSOR_BITMAP_HASH_SIZE; i++)
399 printf ("%02x", bmi->hash[i]);
400 printf ("\n");
401 for (y = 0; y < image->height; y++)
402 {
403 for (x = 0; x < image->width; x++)
404 putchar (XGetPixel (&t, x, y) ? '*' : ' ');
405 putchar ('\n');
406 }
407 }
408 bmi->has_image = True;
409 returnVoid();
410}
411
412Cursor
413XcursorTryShapeBitmapCursor (Display *dpy,
414 Pixmap source,
415 Pixmap mask,
416 XColor *foreground,
417 XColor *background,
418 unsigned int x,
419 unsigned int y)
420{
421 XcursorBitmapInfo *bmi;
422 char name[8 * XCURSOR_BITMAP_HASH_SIZE];
423 int i;
424 Cursor cursor;
425
426 (void) mask; /* UNUSED */
427 (void) x; /* UNUSED */
428 (void) y; /* UNUSED */
429
430 enterFunc((T_CALLED(XcursorTryShapeBitmapCursor)
431 "(%p, %lu, %lu, %p, %p, %u, %u)\n",
432 (void*)dpy,
433 source, mask,
434 (void*)foreground,
435 (void*)background,
436 x, y));
437
438 if (!dpy || !foreground || !background)
439 returnLong(None);
440
441 if (!XcursorSupportsARGB (dpy) && !XcursorGetThemeCore (dpy))
442 returnLong(None);
443
444 bmi = _XcursorGetBitmap (dpy, source);
445 if (!bmi || !bmi->has_image)
446 returnLong(None);
447 for (i = 0; i < XCURSOR_BITMAP_HASH_SIZE; i++)
448 sprintf (name + 2 * i, "%02x", bmi->hash[i]);
449 cursor = XcursorLibraryLoadCursor (dpy, name);
450 if (_XcursorLogDiscover())
451 printf ("Cursor hash %s returns 0x%x\n", name, (unsigned int) cursor);
452 returnLong(cursor);
453}