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