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