1 /*
2  * tkUnixCursor.c --
3  *
4  *	This file contains X specific cursor manipulation routines.
5  *
6  * Copyright (c) 1995-1997 Sun Microsystems, Inc.
7  *
8  * See the file "license.terms" for information on usage and redistribution of
9  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
10  */
11 
12 #include "tkInt.h"
13 
14 /*
15  * The following data structure is a superset of the TkCursor structure
16  * defined in tkCursor.c. Each system specific cursor module will define a
17  * different cursor structure. All of these structures must have the same
18  * header consisting of the fields in TkCursor.
19  */
20 
21 typedef struct {
22     TkCursor info;		/* Generic cursor info used by tkCursor.c */
23     Display *display;		/* Display for which cursor is valid. */
24 } TkUnixCursor;
25 
26 /*
27  * The table below is used to map from the name of a cursor to its index in
28  * the official cursor font:
29  */
30 
31 static const struct CursorName {
32     const char *name;
33     unsigned int shape;
34 } cursorNames[] = {
35     {"X_cursor",		XC_X_cursor},
36     {"arrow",			XC_arrow},
37     {"based_arrow_down",	XC_based_arrow_down},
38     {"based_arrow_up",		XC_based_arrow_up},
39     {"boat",			XC_boat},
40     {"bogosity",		XC_bogosity},
41     {"bottom_left_corner",	XC_bottom_left_corner},
42     {"bottom_right_corner",	XC_bottom_right_corner},
43     {"bottom_side",		XC_bottom_side},
44     {"bottom_tee",		XC_bottom_tee},
45     {"box_spiral",		XC_box_spiral},
46     {"center_ptr",		XC_center_ptr},
47     {"circle",			XC_circle},
48     {"clock",			XC_clock},
49     {"coffee_mug",		XC_coffee_mug},
50     {"cross",			XC_cross},
51     {"cross_reverse",		XC_cross_reverse},
52     {"crosshair",		XC_crosshair},
53     {"diamond_cross",		XC_diamond_cross},
54     {"dot",			XC_dot},
55     {"dotbox",			XC_dotbox},
56     {"double_arrow",		XC_double_arrow},
57     {"draft_large",		XC_draft_large},
58     {"draft_small",		XC_draft_small},
59     {"draped_box",		XC_draped_box},
60     {"exchange",		XC_exchange},
61     {"fleur",			XC_fleur},
62     {"gobbler",			XC_gobbler},
63     {"gumby",			XC_gumby},
64     {"hand1",			XC_hand1},
65     {"hand2",			XC_hand2},
66     {"heart",			XC_heart},
67     {"icon",			XC_icon},
68     {"iron_cross",		XC_iron_cross},
69     {"left_ptr",		XC_left_ptr},
70     {"left_side",		XC_left_side},
71     {"left_tee",		XC_left_tee},
72     {"leftbutton",		XC_leftbutton},
73     {"ll_angle",		XC_ll_angle},
74     {"lr_angle",		XC_lr_angle},
75     {"man",			XC_man},
76     {"middlebutton",		XC_middlebutton},
77     {"mouse",			XC_mouse},
78     {"pencil",			XC_pencil},
79     {"pirate",			XC_pirate},
80     {"plus",			XC_plus},
81     {"question_arrow",		XC_question_arrow},
82     {"right_ptr",		XC_right_ptr},
83     {"right_side",		XC_right_side},
84     {"right_tee",		XC_right_tee},
85     {"rightbutton",		XC_rightbutton},
86     {"rtl_logo",		XC_rtl_logo},
87     {"sailboat",		XC_sailboat},
88     {"sb_down_arrow",		XC_sb_down_arrow},
89     {"sb_h_double_arrow",	XC_sb_h_double_arrow},
90     {"sb_left_arrow",		XC_sb_left_arrow},
91     {"sb_right_arrow",		XC_sb_right_arrow},
92     {"sb_up_arrow",		XC_sb_up_arrow},
93     {"sb_v_double_arrow",	XC_sb_v_double_arrow},
94     {"shuttle",			XC_shuttle},
95     {"sizing",			XC_sizing},
96     {"spider",			XC_spider},
97     {"spraycan",		XC_spraycan},
98     {"star",			XC_star},
99     {"target",			XC_target},
100     {"tcross",			XC_tcross},
101     {"top_left_arrow",		XC_top_left_arrow},
102     {"top_left_corner",		XC_top_left_corner},
103     {"top_right_corner",	XC_top_right_corner},
104     {"top_side",		XC_top_side},
105     {"top_tee",			XC_top_tee},
106     {"trek",			XC_trek},
107     {"ul_angle",		XC_ul_angle},
108     {"umbrella",		XC_umbrella},
109     {"ur_angle",		XC_ur_angle},
110     {"watch",			XC_watch},
111     {"xterm",			XC_xterm},
112     {NULL,			0}
113 };
114 
115 /*
116  * The table below is used to map from a cursor name to the data that defines
117  * the cursor. This table is used for cursors defined by Tk that don't exist
118  * in the X cursor table.
119  */
120 
121 #define CURSOR_NONE_DATA \
122 "#define none_width 1\n" \
123 "#define none_height 1\n" \
124 "#define none_x_hot 0\n" \
125 "#define none_y_hot 0\n" \
126 "static unsigned char none_bits[] = {\n" \
127 "  0x00};"
128 
129 /*
130  * Define test cursor to check that mask fg and bg color settings are working.
131  *
132  * . configure -cursor {center_ptr green red}
133  * . configure -cursor {@myarrow.xbm myarrow-mask.xbm green red}
134  * . configure -cursor {myarrow green red}
135  */
136 
137 /*#define DEFINE_MYARROW_CURSOR*/
138 
139 #ifdef DEFINE_MYARROW_CURSOR
140 #define CURSOR_MYARROW_DATA \
141 "#define myarrow_width 16\n" \
142 "#define myarrow_height 16\n" \
143 "#define myarrow_x_hot 7\n" \
144 "#define myarrow_y_hot 0\n" \
145 "static unsigned char myarrow_bits[] = {\n" \
146 "   0x7f, 0xff, 0xbf, 0xfe, 0xdf, 0xfd, 0xef, 0xfb, 0xf7, 0xf7, 0xfb, 0xef,\n" \
147 "   0xfd, 0xdf, 0xfe, 0xbf, 0x80, 0x00, 0xbf, 0xfe, 0xbf, 0xfe, 0xbf, 0xfe,\n" \
148 "   0xbf, 0xfe, 0xbf, 0xfe, 0xbf, 0xfe, 0x3f, 0xfe};"
149 
150 #define CURSOR_MYARROW_MASK \
151 "#define myarrow-mask_width 16\n" \
152 "#define myarrow-mask_height 16\n" \
153 "#define myarrow-mask_x_hot 7\n" \
154 "#define myarrow-mask_y_hot 0\n" \
155 "static unsigned char myarrow-mask_bits[] = {\n" \
156 "   0x80, 0x00, 0xc0, 0x01, 0xe0, 0x03, 0xf0, 0x07, 0xf8, 0x0f, 0xfc, 0x1f,\n" \
157 "   0xfe, 0x3f, 0xff, 0x7f, 0xff, 0xff, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01,\n" \
158 "   0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01};"
159 
160 #endif /* DEFINE_MYARROW_CURSOR */
161 
162 static const struct TkCursorName {
163     const char *name;
164     const char *data;
165     char *mask;
166 } tkCursorNames[] = {
167     {"none",	CURSOR_NONE_DATA,	NULL},
168 #ifdef DEFINE_MYARROW_CURSOR
169     {"myarrow",	CURSOR_MYARROW_DATA,	CURSOR_MYARROW_MASK},
170 #endif /* DEFINE_MYARROW_CURSOR */
171     {NULL,	NULL,			NULL}
172 };
173 
174 /*
175  * Font to use for cursors:
176  */
177 
178 #ifndef CURSORFONT
179 #define CURSORFONT "cursor"
180 #endif
181 
182 static Cursor		CreateCursorFromTableOrFile(Tcl_Interp *interp,
183 			    Tk_Window tkwin, int argc, const char **argv,
184 			    const struct TkCursorName *tkCursorPtr);
185 
186 /*
187  *----------------------------------------------------------------------
188  *
189  * TkGetCursorByName --
190  *
191  *	Retrieve a cursor by name. Parse the cursor name into fields and
192  *	create a cursor, either from the standard cursor font or from bitmap
193  *	files.
194  *
195  * Results:
196  *	Returns a new cursor, or NULL on errors.
197  *
198  * Side effects:
199  *	Allocates a new cursor.
200  *
201  *----------------------------------------------------------------------
202  */
203 
204 TkCursor *
TkGetCursorByName(Tcl_Interp * interp,Tk_Window tkwin,Tk_Uid string)205 TkGetCursorByName(
206     Tcl_Interp *interp,		/* Interpreter to use for error reporting. */
207     Tk_Window tkwin,		/* Window in which cursor will be used. */
208     Tk_Uid string)		/* Description of cursor. See manual entry for
209 				 * details on legal syntax. */
210 {
211     TkUnixCursor *cursorPtr = NULL;
212     Cursor cursor = None;
213     int argc;
214     const char **argv = NULL;
215     Display *display = Tk_Display(tkwin);
216     int inTkTable = 0;
217     const struct TkCursorName *tkCursorPtr = NULL;
218 
219     if (Tcl_SplitList(interp, string, &argc, &argv) != TCL_OK) {
220 	return NULL;
221     }
222     if (argc == 0) {
223 	goto badString;
224     }
225 
226     /*
227      * Check Tk specific table of cursor names. The cursor names don't overlap
228      * with cursors defined in the X table so search order does not matter.
229      */
230 
231     if (argv[0][0] != '@') {
232 	for (tkCursorPtr = tkCursorNames; ; tkCursorPtr++) {
233 	    if (tkCursorPtr->name == NULL) {
234 		tkCursorPtr = NULL;
235 		break;
236 	    }
237 	    if ((tkCursorPtr->name[0] == argv[0][0]) &&
238 		    (strcmp(tkCursorPtr->name, argv[0]) == 0)) {
239 		inTkTable = 1;
240 		break;
241 	    }
242 	}
243     }
244 
245     if ((argv[0][0] != '@') && !inTkTable) {
246 	XColor fg, bg;
247 	unsigned int maskIndex;
248 	const struct CursorName *namePtr;
249 	TkDisplay *dispPtr;
250 
251 	/*
252 	 * The cursor is to come from the standard cursor font. If one arg, it
253 	 * is cursor name (use black and white for fg and bg). If two args,
254 	 * they are name and fg color (ignore mask). If three args, they are
255 	 * name, fg, bg. Some of the code below is stolen from the
256 	 * XCreateFontCursor Xlib function.
257 	 */
258 
259 	if (argc > 3) {
260 	    goto badString;
261 	}
262 	for (namePtr = cursorNames; ; namePtr++) {
263 	    if (namePtr->name == NULL) {
264 		goto badString;
265 	    }
266 	    if ((namePtr->name[0] == argv[0][0])
267 		    && (strcmp(namePtr->name, argv[0]) == 0)) {
268 		break;
269 	    }
270 	}
271 
272 	maskIndex = namePtr->shape + 1;
273 	if (argc == 1) {
274 	    fg.red = fg.green = fg.blue = 0;
275 	    bg.red = bg.green = bg.blue = 65535;
276 	} else {
277 	    if (TkParseColor(display, Tk_Colormap(tkwin), argv[1], &fg) == 0) {
278 		Tcl_SetObjResult(interp, Tcl_ObjPrintf(
279 			"invalid color name \"%s\"", argv[1]));
280 		Tcl_SetErrorCode(interp, "TK", "CURSOR", "COLOR", NULL);
281 		goto cleanup;
282 	    }
283 	    if (argc == 2) {
284 		bg.red = bg.green = bg.blue = 0;
285 		maskIndex = namePtr->shape;
286 	    } else if (TkParseColor(display, Tk_Colormap(tkwin), argv[2],
287 		    &bg) == 0) {
288 		Tcl_SetObjResult(interp, Tcl_ObjPrintf(
289 			"invalid color name \"%s\"", argv[2]));
290 		Tcl_SetErrorCode(interp, "TK", "CURSOR", "COLOR", NULL);
291 		goto cleanup;
292 	    }
293 	}
294 	dispPtr = ((TkWindow *) tkwin)->dispPtr;
295 	if (dispPtr->cursorFont == None) {
296 	    dispPtr->cursorFont = XLoadFont(display, CURSORFONT);
297 	    if (dispPtr->cursorFont == None) {
298 		Tcl_SetObjResult(interp, Tcl_NewStringObj(
299 			"couldn't load cursor font", -1));
300 		Tcl_SetErrorCode(interp, "TK", "CURSOR", "FONT", NULL);
301 		goto cleanup;
302 	    }
303 	}
304 	cursor = XCreateGlyphCursor(display, dispPtr->cursorFont,
305 		dispPtr->cursorFont, namePtr->shape, maskIndex,
306 		&fg, &bg);
307     } else {
308 	/*
309 	 * Prevent file system access in safe interpreters.
310 	 */
311 
312 	if (!inTkTable && Tcl_IsSafe(interp)) {
313 	    Tcl_SetObjResult(interp, Tcl_NewStringObj(
314 		    "can't get cursor from a file in a safe interpreter",
315 		    -1));
316 	    Tcl_SetErrorCode(interp, "TK", "SAFE", "CURSOR_FILE", NULL);
317 	    cursorPtr = NULL;
318 	    goto cleanup;
319 	}
320 
321 	/*
322 	 * If the cursor is to be created from bitmap files, then there should
323 	 * be either two elements in the list (source, color) or four (source
324 	 * mask fg bg). A cursor defined in the Tk table accepts the same
325 	 * arguments as an X cursor.
326 	 */
327 
328 	if (inTkTable && (argc != 1) && (argc != 2) && (argc != 3)) {
329 	    goto badString;
330 	}
331 
332 	if (!inTkTable && (argc != 2) && (argc != 4)) {
333 	    goto badString;
334 	}
335 
336 	cursor = CreateCursorFromTableOrFile(interp, tkwin, argc, argv,
337 		tkCursorPtr);
338     }
339 
340     if (cursor != None) {
341 	cursorPtr = ckalloc(sizeof(TkUnixCursor));
342 	cursorPtr->info.cursor = (Tk_Cursor) cursor;
343 	cursorPtr->display = display;
344     }
345 
346   cleanup:
347     if (argv != NULL) {
348 	ckfree(argv);
349     }
350     return (TkCursor *) cursorPtr;
351 
352   badString:
353     if (argv) {
354 	ckfree(argv);
355     }
356     Tcl_SetObjResult(interp, Tcl_ObjPrintf("bad cursor spec \"%s\"", string));
357     Tcl_SetErrorCode(interp, "TK", "VALUE", "CURSOR", NULL);
358     return NULL;
359 }
360 
361 /*
362  *----------------------------------------------------------------------
363  *
364  * CreateCursorFromTableOrFile --
365  *
366  *	Create a cursor defined in a file or the Tk static cursor table. A
367  *	cursor defined in a file starts with the '@' character. This method
368  *	assumes that the number of arguments in argv has been validated
369  *	already.
370  *
371  * Results:
372  *	Returns a new cursor, or None on error.
373  *
374  * Side effects:
375  *	Allocates a new X cursor.
376  *
377  *----------------------------------------------------------------------
378  */
379 
380 static Cursor
CreateCursorFromTableOrFile(Tcl_Interp * interp,Tk_Window tkwin,int argc,const char ** argv,const struct TkCursorName * tkCursorPtr)381 CreateCursorFromTableOrFile(
382     Tcl_Interp *interp,		/* Interpreter to use for error reporting. */
383     Tk_Window tkwin,		/* Window in which cursor will be used. */
384     int argc,
385     const char **argv,		/* Cursor spec parsed into elements. */
386     const struct TkCursorName *tkCursorPtr)
387 				/* Non-NULL when cursor is defined in Tk
388 				 * table. */
389 {
390     Cursor cursor = None;
391 
392     int width, height, maskWidth, maskHeight;
393     int xHot = -1, yHot = -1;
394     int dummy1, dummy2;
395     XColor fg, bg;
396     const char *fgColor;
397     const char *bgColor;
398     int inTkTable = (tkCursorPtr != NULL);
399 
400     Display *display = Tk_Display(tkwin);
401     Drawable drawable = RootWindowOfScreen(Tk_Screen(tkwin));
402 
403     Pixmap source = None;
404     Pixmap mask = None;
405 
406     /*
407      * A cursor defined in a file accepts either 2 or 4 arguments.
408      *
409      * {srcfile fg}
410      * {srcfile maskfile fg bg}
411      *
412      * A cursor defined in the Tk table accepts 1, 2, or 3 arguments.
413      *
414      * {tkcursorname}
415      * {tkcursorname fg}
416      * {tkcursorname fg bg}
417      */
418 
419     if (inTkTable) {
420 	/*
421 	 * This logic is like TkReadBitmapFile().
422 	 */
423 
424 	char *data;
425 
426 	data = TkGetBitmapData(NULL, tkCursorPtr->data, NULL,
427 		&width, &height, &xHot, &yHot);
428 	if (data == NULL) {
429 	    Tcl_SetObjResult(interp, Tcl_ObjPrintf(
430 		    "error reading bitmap data for \"%s\"", argv[0]));
431 	    Tcl_SetErrorCode(interp, "TK", "CURSOR", "BITMAP_DATA", NULL);
432 	    goto cleanup;
433 	}
434 
435 	source = XCreateBitmapFromData(display, drawable, data, width,height);
436 	ckfree(data);
437     } else {
438 	if (TkReadBitmapFile(display, drawable, &argv[0][1],
439 		(unsigned *) &width, (unsigned *) &height,
440 		&source, &xHot, &yHot) != BitmapSuccess) {
441 	    Tcl_SetObjResult(interp, Tcl_ObjPrintf(
442 		    "cleanup reading bitmap file \"%s\"", &argv[0][1]));
443 	    Tcl_SetErrorCode(interp, "TK", "CURSOR", "BITMAP_FILE", NULL);
444 	    goto cleanup;
445 	}
446     }
447 
448     if ((xHot < 0) || (yHot < 0) || (xHot >= width) || (yHot >= height)) {
449 	if (inTkTable) {
450 	    Tcl_SetObjResult(interp, Tcl_ObjPrintf(
451 		    "bad hot spot in bitmap data for \"%s\"", argv[0]));
452 	} else {
453 	    Tcl_SetObjResult(interp, Tcl_ObjPrintf(
454 		    "bad hot spot in bitmap file \"%s\"", &argv[0][1]));
455 	}
456 	Tcl_SetErrorCode(interp, "TK", "CURSOR", "HOTSPOT", NULL);
457 	goto cleanup;
458     }
459 
460     /*
461      * Parse color names from optional fg and bg arguments
462      */
463 
464     if (argc == 1) {
465 	fg.red = fg.green = fg.blue = 0;
466 	bg.red = bg.green = bg.blue = 65535;
467     } else if (argc == 2) {
468 	fgColor = argv[1];
469 	if (TkParseColor(display, Tk_Colormap(tkwin), fgColor, &fg) == 0) {
470 	    Tcl_SetObjResult(interp, Tcl_ObjPrintf(
471 		    "invalid color name \"%s\"", fgColor));
472 	    Tcl_SetErrorCode(interp, "TK", "CURSOR", "COLOR", NULL);
473 	    goto cleanup;
474 	}
475 	if (inTkTable) {
476 	    bg.red = bg.green = bg.blue = 0;
477 	} else {
478 	    bg = fg;
479 	}
480     } else {
481 	/* 3 or 4 arguments */
482 	if (inTkTable) {
483 	    fgColor = argv[1];
484 	    bgColor = argv[2];
485 	} else {
486 	    fgColor = argv[2];
487 	    bgColor = argv[3];
488 	}
489 	if (TkParseColor(display, Tk_Colormap(tkwin), fgColor, &fg) == 0) {
490 	    Tcl_SetObjResult(interp, Tcl_ObjPrintf(
491 		    "invalid color name \"%s\"", fgColor));
492 	    Tcl_SetErrorCode(interp, "TK", "CURSOR", "COLOR", NULL);
493 	    goto cleanup;
494 	}
495 	if (TkParseColor(display, Tk_Colormap(tkwin), bgColor, &bg) == 0) {
496 	    Tcl_SetObjResult(interp, Tcl_ObjPrintf(
497 		    "invalid color name \"%s\"", bgColor));
498 	    Tcl_SetErrorCode(interp, "TK", "CURSOR", "COLOR", NULL);
499 	    goto cleanup;
500 	}
501     }
502 
503     /*
504      * If there is no mask data, then create the cursor now.
505      */
506 
507     if ((!inTkTable && (argc == 2)) || (inTkTable && tkCursorPtr->mask == NULL)) {
508 	cursor = XCreatePixmapCursor(display, source, source,
509 		&fg, &fg, (unsigned) xHot, (unsigned) yHot);
510 	goto cleanup;
511     }
512 
513     /*
514      * Parse bitmap mask data and create cursor with fg and bg colors.
515      */
516 
517     if (inTkTable) {
518 	/*
519 	 * This logic is like TkReadBitmapFile().
520 	 */
521 
522 	char *data;
523 
524 	data = TkGetBitmapData(NULL, tkCursorPtr->mask, NULL,
525 		&maskWidth, &maskHeight, &dummy1, &dummy2);
526 	if (data == NULL) {
527 	    Tcl_SetObjResult(interp, Tcl_ObjPrintf(
528 		    "error reading bitmap mask data for \"%s\"", argv[0]));
529 	    Tcl_SetErrorCode(interp, "TK", "CURSOR", "MASK_DATA", NULL);
530 	    goto cleanup;
531 	}
532 
533 	mask = XCreateBitmapFromData(display, drawable, data, maskWidth,
534 		maskHeight);
535 
536 	ckfree(data);
537     } else {
538 	if (TkReadBitmapFile(display, drawable, argv[1],
539 		(unsigned int *) &maskWidth, (unsigned int *) &maskHeight,
540 		&mask, &dummy1, &dummy2) != BitmapSuccess) {
541 	    Tcl_SetObjResult(interp, Tcl_ObjPrintf(
542 		    "cleanup reading bitmap file \"%s\"", argv[1]));
543 	    Tcl_SetErrorCode(interp, "TK", "CURSOR", "MASK_FILE", NULL);
544 	    goto cleanup;
545 	}
546     }
547 
548     if ((maskWidth != width) || (maskHeight != height)) {
549 	Tcl_SetObjResult(interp, Tcl_NewStringObj(
550 		"source and mask bitmaps have different sizes", -1));
551 	Tcl_SetErrorCode(interp, "TK", "CURSOR", "SIZE_MATCH", NULL);
552 	goto cleanup;
553     }
554 
555     cursor = XCreatePixmapCursor(display, source, mask,
556 	    &fg, &bg, (unsigned) xHot, (unsigned) yHot);
557 
558   cleanup:
559     if (source != None) {
560 	Tk_FreePixmap(display, source);
561     }
562     if (mask != None) {
563 	Tk_FreePixmap(display, mask);
564     }
565     return cursor;
566 }
567 
568 /*
569  *----------------------------------------------------------------------
570  *
571  * TkCreateCursorFromData --
572  *
573  *	Creates a cursor from the source and mask bits.
574  *
575  * Results:
576  *	Returns a new cursor, or NULL on errors.
577  *
578  * Side effects:
579  *	Allocates a new cursor.
580  *
581  *----------------------------------------------------------------------
582  */
583 
584 TkCursor *
TkCreateCursorFromData(Tk_Window tkwin,const char * source,const char * mask,int width,int height,int xHot,int yHot,XColor fgColor,XColor bgColor)585 TkCreateCursorFromData(
586     Tk_Window tkwin,		/* Window in which cursor will be used. */
587     const char *source,		/* Bitmap data for cursor shape. */
588     const char *mask,		/* Bitmap data for cursor mask. */
589     int width, int height,	/* Dimensions of cursor. */
590     int xHot, int yHot,		/* Location of hot-spot in cursor. */
591     XColor fgColor,		/* Foreground color for cursor. */
592     XColor bgColor)		/* Background color for cursor. */
593 {
594     Cursor cursor;
595     Pixmap sourcePixmap, maskPixmap;
596     TkUnixCursor *cursorPtr = NULL;
597     Display *display = Tk_Display(tkwin);
598 
599     sourcePixmap = XCreateBitmapFromData(display,
600 	    RootWindowOfScreen(Tk_Screen(tkwin)), source, (unsigned) width,
601 	    (unsigned) height);
602     maskPixmap = XCreateBitmapFromData(display,
603 	    RootWindowOfScreen(Tk_Screen(tkwin)), mask, (unsigned) width,
604 	    (unsigned) height);
605     cursor = XCreatePixmapCursor(display, sourcePixmap,
606 	    maskPixmap, &fgColor, &bgColor, (unsigned) xHot, (unsigned) yHot);
607     Tk_FreePixmap(display, sourcePixmap);
608     Tk_FreePixmap(display, maskPixmap);
609 
610     if (cursor != None) {
611 	cursorPtr = ckalloc(sizeof(TkUnixCursor));
612 	cursorPtr->info.cursor = (Tk_Cursor) cursor;
613 	cursorPtr->display = display;
614     }
615     return (TkCursor *) cursorPtr;
616 }
617 
618 /*
619  *----------------------------------------------------------------------
620  *
621  * TkpFreeCursor --
622  *
623  *	This function is called to release a cursor allocated by
624  *	TkGetCursorByName.
625  *
626  * Results:
627  *	None.
628  *
629  * Side effects:
630  *	The cursor data structure is deallocated.
631  *
632  *----------------------------------------------------------------------
633  */
634 
635 void
TkpFreeCursor(TkCursor * cursorPtr)636 TkpFreeCursor(
637     TkCursor *cursorPtr)
638 {
639     TkUnixCursor *unixCursorPtr = (TkUnixCursor *) cursorPtr;
640 
641     XFreeCursor(unixCursorPtr->display, (Cursor) unixCursorPtr->info.cursor);
642 }
643 
644 /*
645  * Local Variables:
646  * mode: c
647  * c-basic-offset: 4
648  * fill-column: 78
649  * End:
650  */
651