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