1/* 2 * tkMacOSXCursor.c -- 3 * 4 * This file contains Macintosh specific cursor related routines. 5 * 6 * Copyright (c) 1995-1997 Sun Microsystems, Inc. 7 * Copyright 2001, Apple Computer, Inc. 8 * Copyright (c) 2006-2007 Daniel A. Steffen <das@users.sourceforge.net> 9 * 10 * See the file "license.terms" for information on usage and redistribution 11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES. 12 * 13 * RCS: @(#) $Id: tkMacOSXCursor.c,v 1.4.2.6 2007/06/29 03:22:01 das Exp $ 14 */ 15 16#include "tkMacOSXPrivate.h" 17 18/* 19 * There are three different ways to set the cursor on the Mac. 20 * The default theme cursors (listed in cursorNames below), 21 * color resource cursors, & normal cursors. 22 */ 23 24#define NONE -1 /* Hidden cursor */ 25#define THEME 0 /* Theme cursors */ 26#define ANIMATED 1 /* Animated theme cursors */ 27#define COLOR 2 /* Cursors of type crsr. */ 28#define NORMAL 3 /* Cursors of type CURS. */ 29 30/* 31 * The following data structure contains the system specific data 32 * necessary to control Windows cursors. 33 */ 34 35typedef struct { 36 TkCursor info; /* Generic cursor info used by tkCursor.c */ 37 Handle macCursor; /* Resource containing Macintosh cursor. 38 * For theme cursors, this is -1. */ 39 int type; /* Type of Mac cursor: for theme cursors 40 * this is the theme cursor constant, 41 * otherwise one of crsr or CURS */ 42 int count; /* For animating cursors, the count for the 43 * cursor. */ 44} TkMacOSXCursor; 45 46/* 47 * The table below is used to map from the name of a predefined cursor 48 * to its resource identifier. 49 */ 50 51struct CursorName { 52 const char *name; 53 int id; 54}; 55 56static struct CursorName noneCursorName = {"none", 0}; 57 58static struct CursorName themeCursorNames[] = { 59 {"arrow", kThemeArrowCursor}, 60 {"copyarrow", kThemeCopyArrowCursor}, 61 {"aliasarrow", kThemeAliasArrowCursor}, 62 {"contextualmenuarrow", kThemeContextualMenuArrowCursor}, 63 {"ibeam", kThemeIBeamCursor}, 64 {"text", kThemeIBeamCursor}, 65 {"xterm", kThemeIBeamCursor}, 66 {"cross", kThemeCrossCursor}, 67 {"crosshair", kThemeCrossCursor}, 68 {"cross-hair", kThemeCrossCursor}, 69 {"plus", kThemePlusCursor}, 70 {"closedhand", kThemeClosedHandCursor}, 71 {"openhand", kThemeOpenHandCursor}, 72 {"pointinghand", kThemePointingHandCursor}, 73 {"resizeleft", kThemeResizeLeftCursor}, 74 {"resizeright", kThemeResizeRightCursor}, 75 {"resizeleftright", kThemeResizeLeftRightCursor}, 76 {"resizeup", kThemeResizeUpCursor}, 77 {"resizedown", kThemeResizeDownCursor}, 78 {"resizeupdown", kThemeResizeUpDownCursor}, 79 {"notallowed", kThemeNotAllowedCursor}, 80 {"poof", kThemePoofCursor}, 81 {NULL, 0} 82}; 83 84static struct CursorName animatedThemeCursorNames[] = { 85 {"watch", kThemeWatchCursor}, 86 {"countinguphand", kThemeCountingUpHandCursor}, 87 {"countingdownhand", kThemeCountingDownHandCursor}, 88 {"countingupanddownhand", kThemeCountingUpAndDownHandCursor}, 89 {"spinning", kThemeSpinningCursor}, 90 {NULL, 0} 91}; 92 93/* 94 * Declarations of static variables used in this file. 95 */ 96 97static TkMacOSXCursor * gCurrentCursor = NULL; /* A pointer to the current 98 * cursor. */ 99static int gResizeOverride = false; /* A boolean indicating whether 100 * we should use the resize 101 * cursor during installations. */ 102static int gTkOwnsCursor = true; /* A boolean indicating whether 103 * Tk owns the cursor. If not (for 104 * instance, in the case where a Tk 105 * window is embedded in another app's 106 * window, and the cursor is out of 107 * the tk window, we will not attempt 108 * to adjust the cursor */ 109 110/* 111 * Declarations of procedures local to this file 112 */ 113 114static void FindCursorByName(TkMacOSXCursor *macCursorPtr, const char *string); 115 116 117/* 118 *---------------------------------------------------------------------- 119 * 120 * FindCursorByName -- 121 * 122 * Retrieve a system cursor by name, and fill the macCursorPtr 123 * structure. If the cursor cannot be found, the macCursor field 124 * will be NULL. The function first attempts to load a color 125 * cursor. If that fails it will attempt to load a black & white 126 * cursor. 127 * 128 * Results: 129 * Fills the macCursorPtr record. 130 * 131 * Side effects: 132 * None 133 * 134 *---------------------------------------------------------------------- 135 */ 136 137void 138FindCursorByName( 139 TkMacOSXCursor *macCursorPtr, 140 const char *string) 141{ 142 Handle resource; 143 Str255 curName; 144 int destWrote, inCurLen; 145 Tcl_Encoding encoding; 146 147 inCurLen = strlen(string); 148 if (inCurLen > 255) { 149 return; 150 } 151 152 /* 153 * macRoman is the encoding that the resource fork uses. 154 */ 155 156 encoding = Tcl_GetEncoding(NULL, "macRoman"); 157 Tcl_UtfToExternal(NULL, encoding, string, inCurLen, 0, NULL, 158 (char *) &curName[1], 255, NULL, &destWrote, NULL); 159 curName[0] = destWrote; 160 Tcl_FreeEncoding(encoding); 161 162 resource = GetNamedResource('crsr', curName); 163 if (resource) { 164 short id; 165 Str255 theName; 166 ResType theType; 167 168 GetResInfo(resource, &id, &theType, theName); 169 macCursorPtr->macCursor = (Handle) GetCCursor(id); 170 macCursorPtr->type = COLOR; 171 } else { 172 macCursorPtr->macCursor = GetNamedResource('CURS', curName); 173 macCursorPtr->type = NORMAL; 174 } 175} 176 177/* 178 *---------------------------------------------------------------------- 179 * 180 * TkGetCursorByName -- 181 * 182 * Retrieve a system cursor by name. 183 * 184 * Results: 185 * Returns a new cursor, or NULL on errors. 186 * 187 * Side effects: 188 * Allocates a new cursor. 189 * 190 *---------------------------------------------------------------------- 191 */ 192 193TkCursor * 194TkGetCursorByName( 195 Tcl_Interp *interp, /* Interpreter to use for error reporting. */ 196 Tk_Window tkwin, /* Window in which cursor will be used. */ 197 Tk_Uid string) /* Description of cursor. See manual entry 198 * for details on legal syntax. */ 199{ 200 struct CursorName *namePtr; 201 TkMacOSXCursor *macCursorPtr; 202 int count = -1; 203 204 macCursorPtr = (TkMacOSXCursor *) ckalloc(sizeof(TkMacOSXCursor)); 205 macCursorPtr->info.cursor = (Tk_Cursor) macCursorPtr; 206 207 /* 208 * To find a cursor we must first determine if it is one of the 209 * builtin cursors or the standard arrow cursor. Otherwise, we 210 * attempt to load the cursor as a named Mac resource. 211 */ 212 213 if (strcmp(noneCursorName.name, string) == 0) { 214 namePtr = &noneCursorName; 215 macCursorPtr->type = NONE; 216 } else { 217 for (namePtr = themeCursorNames; namePtr->name != NULL; namePtr++) { 218 if (strcmp(namePtr->name, string) == 0) { 219 macCursorPtr->type = THEME; 220 break; 221 } 222 } 223 } 224 225 if (namePtr->name == NULL) { 226 for (namePtr = animatedThemeCursorNames; 227 namePtr->name != NULL; namePtr++) { 228 int namelen = strlen (namePtr->name); 229 if (strncmp(namePtr->name, string, namelen) == 0) { 230 const char *numPtr = string + namelen; 231 if (*numPtr) { 232 int result = Tcl_GetInt(NULL, numPtr, &count); 233 if (result != TCL_OK) { 234 continue; 235 } 236 } 237 macCursorPtr->type = ANIMATED; 238 break; 239 } 240 } 241 } 242 243 if (namePtr->name != NULL) { 244 macCursorPtr->macCursor = (Handle) namePtr; 245 macCursorPtr->count = count; 246 } else { 247 FindCursorByName(macCursorPtr, string); 248 249 if (macCursorPtr->macCursor == NULL) { 250 const char **argv; 251 int argc; 252 253 /* 254 * The user may be trying to specify an XCursor with fore 255 * & back colors. We don't want this to be an error, so pick 256 * off the first word, and try again. 257 */ 258 259 if (Tcl_SplitList(interp, string, &argc, &argv) == TCL_OK ) { 260 if (argc > 1) { 261 FindCursorByName(macCursorPtr, argv[0]); 262 } 263 ckfree((char *) argv); 264 } 265 } 266 } 267 268 if (macCursorPtr->macCursor == NULL) { 269 ckfree((char *)macCursorPtr); 270 Tcl_AppendResult(interp, "bad cursor spec \"", string, "\"", NULL); 271 return NULL; 272 } else { 273 return (TkCursor *) macCursorPtr; 274 } 275} 276 277/* 278 *---------------------------------------------------------------------- 279 * 280 * TkCreateCursorFromData -- 281 * 282 * Creates a cursor from the source and mask bits. 283 * 284 * Results: 285 * Returns a new cursor, or NULL on errors. 286 * 287 * Side effects: 288 * Allocates a new cursor. 289 * 290 *---------------------------------------------------------------------- 291 */ 292 293TkCursor * 294TkCreateCursorFromData( 295 Tk_Window tkwin, /* Window in which cursor will be used. */ 296 CONST char *source, /* Bitmap data for cursor shape. */ 297 CONST char *mask, /* Bitmap data for cursor mask. */ 298 int width, int height, /* Dimensions of cursor. */ 299 int xHot, int yHot, /* Location of hot-spot in cursor. */ 300 XColor fgColor, /* Foreground color for cursor. */ 301 XColor bgColor) /* Background color for cursor. */ 302{ 303 return NULL; 304} 305 306/* 307 *---------------------------------------------------------------------- 308 * 309 * TkpFreeCursor -- 310 * 311 * This procedure is called to release a cursor allocated by 312 * TkGetCursorByName. 313 * 314 * Results: 315 * None. 316 * 317 * Side effects: 318 * The cursor data structure is deallocated. 319 * 320 *---------------------------------------------------------------------- 321 */ 322 323void 324TkpFreeCursor( 325 TkCursor *cursorPtr) 326{ 327 TkMacOSXCursor *macCursorPtr = (TkMacOSXCursor *) cursorPtr; 328 329 switch (macCursorPtr->type) { 330 case COLOR: 331 DisposeCCursor((CCrsrHandle) macCursorPtr->macCursor); 332 break; 333 case NORMAL: 334 ReleaseResource(macCursorPtr->macCursor); 335 break; 336 } 337 338 if (macCursorPtr == gCurrentCursor) { 339 gCurrentCursor = NULL; 340 } 341} 342 343/* 344 *---------------------------------------------------------------------- 345 * 346 * TkMacOSXInstallCursor -- 347 * 348 * Installs either the current cursor as defined by TkpSetCursor 349 * or a resize cursor as the cursor the Macintosh should currently 350 * display. 351 * 352 * Results: 353 * None. 354 * 355 * Side effects: 356 * Changes the Macintosh mouse cursor. 357 * 358 *---------------------------------------------------------------------- 359 */ 360 361void 362TkMacOSXInstallCursor( 363 int resizeOverride) 364{ 365 TkMacOSXCursor *macCursorPtr = gCurrentCursor; 366 CCrsrHandle ccursor; 367 CursHandle cursor; 368 static unsigned int cursorStep = 0; 369 static int cursorHidden = 0; 370 int cursorNone = 0; 371 372 gResizeOverride = resizeOverride; 373 374 if (resizeOverride) { 375 cursor = (CursHandle) GetNamedResource('CURS', "\presize"); 376 if (cursor) { 377 SetCursor(*cursor); 378 } else { 379 TkMacOSXDbgMsg("Resize cursor failed: %d", ResError()); 380 } 381 } else if (macCursorPtr == NULL) { 382 SetThemeCursor(kThemeArrowCursor); 383 } else { 384 struct CursorName *namePtr; 385 switch (macCursorPtr->type) { 386 case NONE: 387 if (!cursorHidden) { 388 cursorHidden = 1; 389 HideCursor(); 390 } 391 cursorNone = 1; 392 break; 393 case THEME: 394 namePtr = (struct CursorName *) macCursorPtr->macCursor; 395 SetThemeCursor( 396 namePtr->id); 397 break; 398 case ANIMATED: 399 namePtr = (struct CursorName *) macCursorPtr->macCursor; 400 if (macCursorPtr->count == -1) { 401 SetAnimatedThemeCursor(namePtr->id, cursorStep++); 402 } else { 403 SetAnimatedThemeCursor(namePtr->id, macCursorPtr->count); 404 } 405 break; 406 case COLOR: 407 ccursor = (CCrsrHandle) macCursorPtr->macCursor; 408 SetCCursor(ccursor); 409 break; 410 case NORMAL: 411 cursor = (CursHandle) macCursorPtr->macCursor; 412 SetCursor(*cursor); 413 break; 414 } 415 } 416 if (cursorHidden && !cursorNone) { 417 cursorHidden = 0; 418 ShowCursor(); 419 } 420} 421 422/* 423 *---------------------------------------------------------------------- 424 * 425 * TkpSetCursor -- 426 * 427 * Set the current cursor and install it. 428 * 429 * Results: 430 * None. 431 * 432 * Side effects: 433 * Changes the current cursor. 434 * 435 *---------------------------------------------------------------------- 436 */ 437 438void 439TkpSetCursor( 440 TkpCursor cursor) 441{ 442 int cursorChanged = 1; 443 444 if (!gTkOwnsCursor) { 445 return; 446 } 447 448 if (cursor == None) { 449 /* 450 * This is a little tricky. We can't really tell whether 451 * gCurrentCursor is NULL because it was NULL last time around 452 * or because we just freed the current cursor. So if the input 453 * cursor is NULL, we always need to reset it, we can't trust the 454 * cursorChanged logic. 455 */ 456 457 gCurrentCursor = NULL; 458 } else { 459 if (gCurrentCursor == (TkMacOSXCursor *) cursor) { 460 cursorChanged = 0; 461 } 462 gCurrentCursor = (TkMacOSXCursor *) cursor; 463 } 464 465 if (Tk_MacOSXIsAppInFront() && cursorChanged) { 466 TkMacOSXInstallCursor(gResizeOverride); 467 } 468} 469 470/* 471 *---------------------------------------------------------------------- 472 * 473 * Tk_MacOSXTkOwnsCursor -- 474 * 475 * Sets whether Tk has the right to adjust the cursor. 476 * 477 * Results: 478 * None. 479 * 480 * Side effects: 481 * May keep Tk from changing the cursor. 482 * 483 *---------------------------------------------------------------------- 484 */ 485 486void 487Tk_MacOSXTkOwnsCursor( 488 int tkOwnsIt) 489{ 490 gTkOwnsCursor = tkOwnsIt; 491} 492