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
9 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
10 *
11 * RCS: @(#) $Id: tkUnixCursor.c,v 1.6 2002/08/05 04:30:40 dgp Exp $
12 */
13
14 #include "tkPort.h"
15 #include "tkInt.h"
16
17 /*
18 * The following data structure is a superset of the TkCursor structure
19 * defined in tkCursor.c. Each system specific cursor module will define
20 * a different cursor structure. All of these structures must have the
21 * same header consisting of the fields in TkCursor.
22 */
23
24
25
26 typedef struct {
27 TkCursor info; /* Generic cursor info used by tkCursor.c */
28 Display *display; /* Display for which cursor is valid. */
29 } TkUnixCursor;
30
31 /*
32 * The table below is used to map from the name of a cursor to its
33 * index in the official cursor font:
34 */
35
36 static struct CursorName {
37 char *name;
38 unsigned int shape;
39 } cursorNames[] = {
40 {"X_cursor", XC_X_cursor},
41 {"arrow", XC_arrow},
42 {"based_arrow_down", XC_based_arrow_down},
43 {"based_arrow_up", XC_based_arrow_up},
44 {"boat", XC_boat},
45 {"bogosity", XC_bogosity},
46 {"bottom_left_corner", XC_bottom_left_corner},
47 {"bottom_right_corner", XC_bottom_right_corner},
48 {"bottom_side", XC_bottom_side},
49 {"bottom_tee", XC_bottom_tee},
50 {"box_spiral", XC_box_spiral},
51 {"center_ptr", XC_center_ptr},
52 {"circle", XC_circle},
53 {"clock", XC_clock},
54 {"coffee_mug", XC_coffee_mug},
55 {"cross", XC_cross},
56 {"cross_reverse", XC_cross_reverse},
57 {"crosshair", XC_crosshair},
58 {"diamond_cross", XC_diamond_cross},
59 {"dot", XC_dot},
60 {"dotbox", XC_dotbox},
61 {"double_arrow", XC_double_arrow},
62 {"draft_large", XC_draft_large},
63 {"draft_small", XC_draft_small},
64 {"draped_box", XC_draped_box},
65 {"exchange", XC_exchange},
66 {"fleur", XC_fleur},
67 {"gobbler", XC_gobbler},
68 {"gumby", XC_gumby},
69 {"hand1", XC_hand1},
70 {"hand2", XC_hand2},
71 {"heart", XC_heart},
72 {"icon", XC_icon},
73 {"iron_cross", XC_iron_cross},
74 {"left_ptr", XC_left_ptr},
75 {"left_side", XC_left_side},
76 {"left_tee", XC_left_tee},
77 {"leftbutton", XC_leftbutton},
78 {"ll_angle", XC_ll_angle},
79 {"lr_angle", XC_lr_angle},
80 {"man", XC_man},
81 {"middlebutton", XC_middlebutton},
82 {"mouse", XC_mouse},
83 {"pencil", XC_pencil},
84 {"pirate", XC_pirate},
85 {"plus", XC_plus},
86 {"question_arrow", XC_question_arrow},
87 {"right_ptr", XC_right_ptr},
88 {"right_side", XC_right_side},
89 {"right_tee", XC_right_tee},
90 {"rightbutton", XC_rightbutton},
91 {"rtl_logo", XC_rtl_logo},
92 {"sailboat", XC_sailboat},
93 {"sb_down_arrow", XC_sb_down_arrow},
94 {"sb_h_double_arrow", XC_sb_h_double_arrow},
95 {"sb_left_arrow", XC_sb_left_arrow},
96 {"sb_right_arrow", XC_sb_right_arrow},
97 {"sb_up_arrow", XC_sb_up_arrow},
98 {"sb_v_double_arrow", XC_sb_v_double_arrow},
99 {"shuttle", XC_shuttle},
100 {"sizing", XC_sizing},
101 {"spider", XC_spider},
102 {"spraycan", XC_spraycan},
103 {"star", XC_star},
104 {"target", XC_target},
105 {"tcross", XC_tcross},
106 {"top_left_arrow", XC_top_left_arrow},
107 {"top_left_corner", XC_top_left_corner},
108 {"top_right_corner", XC_top_right_corner},
109 {"top_side", XC_top_side},
110 {"top_tee", XC_top_tee},
111 {"trek", XC_trek},
112 {"ul_angle", XC_ul_angle},
113 {"umbrella", XC_umbrella},
114 {"ur_angle", XC_ur_angle},
115 {"watch", XC_watch},
116 {"xterm", XC_xterm},
117 {NULL, 0}
118 };
119
120 /*
121 * Font to use for cursors:
122 */
123
124 #ifndef CURSORFONT
125 #define CURSORFONT "cursor"
126 #endif
127
128
129 /*
130 *----------------------------------------------------------------------
131 *
132 * TkGetCursorByName --
133 *
134 * Retrieve a cursor by name. Parse the cursor name into fields
135 * and create a cursor, either from the standard cursor font or
136 * from bitmap files.
137 *
138 * Results:
139 * Returns a new cursor, or NULL on errors.
140 *
141 * Side effects:
142 * Allocates a new cursor.
143 *
144 *----------------------------------------------------------------------
145 */
146
147 TkCursor *
TkGetCursorByName(interp,tkwin,string)148 TkGetCursorByName(interp, tkwin, string)
149 Tcl_Interp *interp; /* Interpreter to use for error reporting. */
150 Tk_Window tkwin; /* Window in which cursor will be used. */
151 CONST char *string; /* Description of cursor. See manual entry
152 * for details on legal syntax. */
153 {
154 TkUnixCursor *cursorPtr = NULL;
155 Cursor cursor = None;
156 int argc;
157 Tcl_Obj **objv = NULL;
158 Pixmap source = None;
159 Pixmap mask = None;
160 Display *display = Tk_Display(tkwin);
161 Tcl_Obj *obj = Tcl_NewStringObj(string,-1);
162
163 if (Tcl_ListObjGetElements(interp, obj, &argc, &objv) != TCL_OK) {
164 return NULL;
165 }
166 if (argc == 0) {
167 goto badString;
168 }
169 if (Tcl_GetString(objv[0])[0] != '@') {
170 XColor fg, bg;
171 unsigned int maskIndex;
172 register struct CursorName *namePtr;
173 TkDisplay *dispPtr;
174
175 /*
176 * The cursor is to come from the standard cursor font. If one
177 * arg, it is cursor name (use black and white for fg and bg).
178 * If two args, they are name and fg color (ignore mask). If
179 * three args, they are name, fg, bg. Some of the code below
180 * is stolen from the XCreateFontCursor Xlib procedure.
181 */
182
183 if (argc > 3) {
184 goto badString;
185 }
186 for (namePtr = cursorNames; ; namePtr++) {
187 if (namePtr->name == NULL) {
188 goto badString;
189 }
190 if ((namePtr->name[0] == Tcl_GetString(objv[0])[0])
191 && (strcmp(namePtr->name, Tcl_GetString(objv[0])) == 0)) {
192 break;
193 }
194 }
195 maskIndex = namePtr->shape + 1;
196 if (argc == 1) {
197 fg.red = fg.green = fg.blue = 0;
198 bg.red = bg.green = bg.blue = 65535;
199 } else {
200 if (XParseColor(display, Tk_Colormap(tkwin), argv[1],
201 &fg) == 0) {
202 Tcl_AppendResult(interp, "invalid color name \"", argv[1],
203 "\"", (char *) NULL);
204 goto cleanup;
205 }
206 if (argc == 2) {
207 bg.red = bg.green = bg.blue = 0;
208 maskIndex = namePtr->shape;
209 } else {
210 if (XParseColor(display, Tk_Colormap(tkwin), argv[2],
211 &bg) == 0) {
212 Tcl_AppendResult(interp, "invalid color name \"", argv[2],
213 "\"", (char *) NULL);
214 goto cleanup;
215 }
216 }
217 }
218 dispPtr = ((TkWindow *) tkwin)->dispPtr;
219 if (dispPtr->cursorFont == None) {
220 dispPtr->cursorFont = XLoadFont(display, CURSORFONT);
221 if (dispPtr->cursorFont == None) {
222 Tcl_SetResult(interp, "couldn't load cursor font", TCL_STATIC);
223 goto cleanup;
224 }
225 }
226 cursor = XCreateGlyphCursor(display, dispPtr->cursorFont,
227 dispPtr->cursorFont, namePtr->shape, maskIndex,
228 &fg, &bg);
229 } else {
230 int width, height, maskWidth, maskHeight;
231 int xHot, yHot, dummy1, dummy2;
232 XColor fg, bg;
233
234 /*
235 * Prevent file system access in safe interpreters.
236 */
237
238 if (Tcl_IsSafe(interp)) {
239 Tcl_AppendResult(interp, "can't get cursor from a file in",
240 " a safe interpreter", (char *) NULL);
241 cursorPtr = NULL;
242 goto cleanup;
243 }
244
245 /*
246 * The cursor is to be created by reading bitmap files. There
247 * should be either two elements in the list (source, color) or
248 * four (source mask fg bg).
249 */
250
251 if ((argc != 2) && (argc != 4)) {
252 goto badString;
253 }
254 if (TkReadBitmapFile(interp, display,
255 RootWindowOfScreen(Tk_Screen(tkwin)), &argv[0][1],
256 (unsigned int *) &width, (unsigned int *) &height,
257 &source, &xHot, &yHot) != BitmapSuccess) {
258 Tcl_AppendResult(interp, "cleanup reading bitmap file \"",
259 &argv[0][1], "\"", (char *) NULL);
260 goto cleanup;
261 }
262 if ((xHot < 0) || (yHot < 0) || (xHot >= width) || (yHot >= height)) {
263 Tcl_AppendResult(interp, "bad hot spot in bitmap file \"",
264 &argv[0][1], "\"", (char *) NULL);
265 goto cleanup;
266 }
267 if (argc == 2) {
268 if (XParseColor(display, Tk_Colormap(tkwin), argv[1],
269 &fg) == 0) {
270 Tcl_AppendResult(interp, "invalid color name \"",
271 argv[1], "\"", (char *) NULL);
272 goto cleanup;
273 }
274 cursor = XCreatePixmapCursor(display, source, source,
275 &fg, &fg, (unsigned) xHot, (unsigned) yHot);
276 } else {
277 if (TkReadBitmapFile(interp, display,
278 RootWindowOfScreen(Tk_Screen(tkwin)), argv[1],
279 (unsigned int *) &maskWidth, (unsigned int *) &maskHeight,
280 &mask, &dummy1, &dummy2) != BitmapSuccess) {
281 Tcl_AppendResult(interp, "cleanup reading bitmap file \"",
282 argv[1], "\"", (char *) NULL);
283 goto cleanup;
284 }
285 if ((maskWidth != width) && (maskHeight != height)) {
286 Tcl_SetResult(interp,
287 "source and mask bitmaps have different sizes",
288 TCL_STATIC);
289 goto cleanup;
290 }
291 if (XParseColor(display, Tk_Colormap(tkwin), argv[2],
292 &fg) == 0) {
293 Tcl_AppendResult(interp, "invalid color name \"", argv[2],
294 "\"", (char *) NULL);
295 goto cleanup;
296 }
297 if (XParseColor(display, Tk_Colormap(tkwin), argv[3],
298 &bg) == 0) {
299 Tcl_AppendResult(interp, "invalid color name \"", argv[3],
300 "\"", (char *) NULL);
301 goto cleanup;
302 }
303 cursor = XCreatePixmapCursor(display, source, mask,
304 &fg, &bg, (unsigned) xHot, (unsigned) yHot);
305 }
306 }
307
308 if (cursor != None) {
309 cursorPtr = (TkUnixCursor *) ckalloc(sizeof(TkUnixCursor));
310 cursorPtr->info.cursor = (Tk_Cursor) cursor;
311 cursorPtr->display = display;
312 }
313
314 cleanup:
315 Tcl_DecrRefCount(obj);
316 if (source != None) {
317 Tk_FreePixmap(display, source);
318 }
319 if (mask != None) {
320 Tk_FreePixmap(display, mask);
321 }
322 return (TkCursor *) cursorPtr;
323
324
325 badString:
326 Tcl_AppendResult(interp, "bad cursor spec \"", Tcl_GetString(obj), "\"",
327 (char *) NULL);
328 Tcl_DecrRefCount(obj);
329 return NULL;
330 }
331
332 /*
333 *----------------------------------------------------------------------
334 *
335 * TkCreateCursorFromData --
336 *
337 * Creates a cursor from the source and mask bits.
338 *
339 * Results:
340 * Returns a new cursor, or NULL on errors.
341 *
342 * Side effects:
343 * Allocates a new cursor.
344 *
345 *----------------------------------------------------------------------
346 */
347
348 TkCursor *
TkCreateCursorFromData(tkwin,source,mask,width,height,xHot,yHot,fgColor,bgColor)349 TkCreateCursorFromData(tkwin, source, mask, width, height, xHot, yHot,
350 fgColor, bgColor)
351 Tk_Window tkwin; /* Window in which cursor will be used. */
352 CONST char *source; /* Bitmap data for cursor shape. */
353 CONST char *mask; /* Bitmap data for cursor mask. */
354 int width, height; /* Dimensions of cursor. */
355 int xHot, yHot; /* Location of hot-spot in cursor. */
356 XColor fgColor; /* Foreground color for cursor. */
357 XColor bgColor; /* Background color for cursor. */
358 {
359 Cursor cursor;
360 Pixmap sourcePixmap, maskPixmap;
361 TkUnixCursor *cursorPtr = NULL;
362 Display *display = Tk_Display(tkwin);
363
364 sourcePixmap = XCreateBitmapFromData(display,
365 RootWindowOfScreen(Tk_Screen(tkwin)), source, (unsigned) width,
366 (unsigned) height);
367 maskPixmap = XCreateBitmapFromData(display,
368 RootWindowOfScreen(Tk_Screen(tkwin)), mask, (unsigned) width,
369 (unsigned) height);
370 cursor = XCreatePixmapCursor(display, sourcePixmap,
371 maskPixmap, &fgColor, &bgColor, (unsigned) xHot, (unsigned) yHot);
372 Tk_FreePixmap(display, sourcePixmap);
373 Tk_FreePixmap(display, maskPixmap);
374
375 if (cursor != None) {
376 cursorPtr = (TkUnixCursor *) ckalloc(sizeof(TkUnixCursor));
377 cursorPtr->info.cursor = (Tk_Cursor) cursor;
378 cursorPtr->display = display;
379 }
380 return (TkCursor *) cursorPtr;
381 }
382
383 /*
384 *----------------------------------------------------------------------
385 *
386 * TkpFreeCursor --
387 *
388 * This procedure is called to release a cursor allocated by
389 * TkGetCursorByName.
390 *
391 * Results:
392 * None.
393 *
394 * Side effects:
395 * The cursor data structure is deallocated.
396 *
397 *----------------------------------------------------------------------
398 */
399
400 void
TkpFreeCursor(cursorPtr)401 TkpFreeCursor(cursorPtr)
402 TkCursor *cursorPtr;
403 {
404 TkUnixCursor *unixCursorPtr = (TkUnixCursor *) cursorPtr;
405 XFreeCursor(unixCursorPtr->display, (Cursor) unixCursorPtr->info.cursor);
406 Tk_FreeXId(unixCursorPtr->display, (XID) unixCursorPtr->info.cursor);
407 }
408
409
410