1 #include "unix/guts.h"
2 #include "Icon.h"
3 
4 static int
5 cursor_map[] = {
6 	/* crArrow           => */   XC_left_ptr,
7 	/* crText            => */   XC_xterm,
8 	/* crWait            => */   XC_watch,
9 	/* crSize            => */   XC_sizing,
10 	/* crMove            => */   XC_fleur,
11 	/* crSizeWest        => */   XC_left_side,
12 	/* crSizeEast        => */   XC_right_side,
13 	/* crSizeNE          => */   XC_sb_h_double_arrow,
14 	/* crSizeNorth       => */   XC_top_side,
15 	/* crSizeSouth       => */   XC_bottom_side,
16 	/* crSizeNS          => */   XC_sb_v_double_arrow,
17 	/* crSizeNW          => */   XC_top_left_corner,
18 	/* crSizeSE          => */   XC_bottom_right_corner,
19 	/* crSizeNE          => */   XC_top_right_corner,
20 	/* crSizeSW          => */   XC_bottom_left_corner,
21 	/* crInvalid         => */   XC_X_cursor,
22         /* crDragNone        => */   XC_X_cursor,
23         /* crDragCopy        => */   XC_bottom_side,
24         /* crDragMove        => */   XC_bottom_side,
25         /* crDragLink        => */   XC_bottom_side,
26         /* crCrosshair       => */   XC_crosshair,
27         /* crUpArrow         => */   XC_sb_up_arrow,
28         /* crQuestionArrow   => */   XC_question_arrow,
29         /* crHand            => */   XC_hand2
30 };
31 
32 #ifdef HAVE_X11_XCURSOR_XCURSOR_H
33 static const char *
34 xcursor_map[] = {
35 	/* crArrow           => */   "left_ptr",
36 	/* crText            => */   "xterm",
37 	/* crWait            => */   "watch",
38 	/* crSize            => */   "sizing",
39 	/* crMove            => */   "fleur",
40 	/* crSizeWest        => */   "left_side",
41 	/* crSizeEast        => */   "right_side",
42 	/* crSizeNE          => */   "sb_h_double_arrow",
43 	/* crSizeNorth       => */   "top_side",
44 	/* crSizeSouth       => */   "bottom_side",
45 	/* crSizeNS          => */   "sb_v_double_arrow",
46 	/* crSizeNW          => */   "top_left_corner",
47 	/* crSizeSE          => */   "bottom_right_corner",
48 	/* crSizeNE          => */   "top_right_corner",
49 	/* crSizeSW          => */   "bottom_left_corner",
50 	/* crInvalid         => */   "X_cursor",
51         /* crDragNone        => */   "dnd-none",
52         /* crDragCopy        => */   "dnd-copy",
53         /* crDragMove        => */   "dnd-move",
54         /* crDragLink        => */   "dnd-link",
55         /* crCrosshair       => */   "crosshair",
56         /* crUpArrow         => */   "sb_up_arrow",
57         /* crQuestionArrow   => */   "question_arrow",
58         /* crHand            => */   "hand"
59 };
60 #endif
61 
62 Cursor
63 predefined_cursors[] = {
64 /* crArrow           => */	None,
65 /* crText            => */	None,
66 /* crWait            => */	None,
67 /* crSize            => */	None,
68 /* crMove            => */	None,
69 /* crSizeWest        => */	None,
70 /* crSizeEast        => */	None,
71 /* crSizeNE          => */	None,
72 /* crSizeNorth       => */	None,
73 /* crSizeSouth       => */	None,
74 /* crSizeNS          => */	None,
75 /* crSizeNW          => */	None,
76 /* crSizeSE          => */	None,
77 /* crSizeNE          => */	None,
78 /* crSizeSW          => */	None,
79 /* crInvalid         => */	None,
80 /* crDragNone        => */	None,
81 /* crDragCopy        => */	None,
82 /* crDragMove        => */	None,
83 /* crDragLink        => */	None,
84 /* crCrosshair       => */	None,
85 /* crUpArrow         => */	None,
86 /* crQuestionArrow   => */	None,
87 /* crHand            => */	None,
88 /* crUser            => */	None
89 
90 };
91 static CustomPointer*
92 is_drag_cursor_available(int id);
93 
94 static Bool
create_cursor(CustomPointer * cp,Handle icon,Point hot_spot)95 create_cursor(CustomPointer* cp, Handle icon, Point hot_spot)
96 {
97 #ifdef HAVE_X11_XCURSOR_XCURSOR_H
98 	XcursorImage* i;
99 	PIcon c = PIcon(icon);
100 	Bool kill;
101 	int x, y;
102 	XcursorPixel * dst;
103 	Byte * src_data, * src_mask;
104 
105 	if ( hot_spot. x < 0) hot_spot. x = 0;
106 	if ( hot_spot. y < 0) hot_spot. y = 0;
107 	if ( hot_spot. x >= c-> w) hot_spot. x = c-> w - 1;
108 	if ( hot_spot. y >= c-> h) hot_spot. y = c-> h - 1;
109 	cp-> hot_spot = hot_spot;
110 	if (( i = XcursorImageCreate( c-> w, c-> h )) == NULL) {
111 		warn( "XcursorImageCreate(%d,%d) error", c->w, c->h);
112 		return false;
113 	}
114 	i-> xhot = hot_spot. x;
115 	i-> yhot = c-> h - hot_spot. y - 1;
116 
117 	if ( c-> type != imRGB || c-> maskType != imbpp8 ) {
118 		icon = CIcon(icon)->dup(icon);
119 		kill = true;
120 		PIcon(icon)-> autoMasking = amNone;
121 		CIcon(icon)-> set_type( icon, imRGB );
122 		CIcon(icon)-> set_maskType( icon, imbpp8 );
123 	} else
124 		kill = false;
125 	c = PIcon(icon);
126 	src_data = c->data + c->lineSize * ( c-> h - 1 );
127 	src_mask = c->mask + c->maskLine * ( c-> h - 1 );
128 	dst = i->pixels;
129 	for ( y = 0; y < c-> h; y++) {
130 		Byte * s_data = src_data, * s_mask = src_mask;
131 		for ( x = 0; x < c-> w; x++) {
132 			*(dst++) =
133 				s_data[0]|
134 				(s_data[1] << 8)|
135 				(s_data[2] << 16)|
136 				(*(s_mask++) << 24)
137 				;
138 			s_data += 3;
139 		}
140 		src_mask -= c->maskLine;
141 		src_data -= c->lineSize;
142 	}
143 	if ( kill ) Object_destroy(icon);
144 
145 	cp-> cursor = XcursorImageLoadCursor(DISP, i);
146 	if ( cp-> cursor == None) {
147 		XcursorImageDestroy(i);
148 		warn( "error creating cursor");
149 		return false;
150 	}
151 	cp-> xcursor = i;
152 
153 	return true;
154 
155 #else
156 
157 	Handle cursor;
158 	Bool noSZ  = PIcon(icon)-> w != guts.cursor_width || PIcon(icon)-> h != guts.cursor_height;
159 	Bool noBPP = (PIcon(icon)-> type & imBPP) != 1;
160 	XColor xcb, xcw;
161 	PIcon c;
162 
163 	if ( noSZ || noBPP) {
164 		cursor = CIcon(icon)->dup(icon);
165 		c = PIcon(cursor);
166 		if ( cursor == NULL_HANDLE) {
167 			warn( "Error duping user cursor");
168 			return false;
169 		}
170 		if ( noSZ) {
171 			CIcon(cursor)-> stretch( cursor, guts.cursor_width, guts.cursor_height);
172 			if ( c-> w != guts.cursor_width || c-> h != guts.cursor_height) {
173 				warn( "Error stretching user cursor");
174 				Object_destroy( cursor);
175 				return false;
176 			}
177 		}
178 		if ( noBPP) {
179 			CIcon(cursor)-> set_type( cursor, imMono);
180 			if ((c-> type & imBPP) != 1) {
181 				warn( "Error black-n-whiting user cursor");
182 				Object_destroy( cursor);
183 				return false;
184 			}
185 		}
186 	} else
187 		cursor = icon;
188 	if ( !prima_create_icon_pixmaps( cursor, &cp->xor, &cp->and)) {
189 		warn( "Error creating user cursor pixmaps");
190 		if ( noSZ || noBPP)
191 			Object_destroy( cursor);
192 		return false;
193 	}
194 	if ( noSZ || noBPP)
195 		Object_destroy( cursor);
196 	if ( hot_spot. x < 0) hot_spot. x = 0;
197 	if ( hot_spot. y < 0) hot_spot. y = 0;
198 	if ( hot_spot. x >= guts. cursor_width)  hot_spot. x = guts. cursor_width  - 1;
199 	if ( hot_spot. y >= guts. cursor_height) hot_spot. y = guts. cursor_height - 1;
200 	cp-> hot_spot = hot_spot;
201 	xcb. red = xcb. green = xcb. blue = 0;
202 	xcw. red = xcw. green = xcw. blue = 0xFFFF;
203 	xcb. pixel = guts. monochromeMap[0];
204 	xcw. pixel = guts. monochromeMap[1];
205 	xcb. flags = xcw. flags = DoRed | DoGreen | DoBlue;
206 	cp-> cursor = XCreatePixmapCursor( DISP, cp-> xor,
207 		cp-> and, &xcw, &xcb,
208 		hot_spot. x, guts.cursor_height - hot_spot. y - 1);
209 	if ( cp-> cursor == None) {
210 		warn( "error creating cursor from pixmaps");
211 		return false;
212 	}
213 	return true;
214 #endif
215 }
216 
217 
218 static Bool
load_pointer_font(void)219 load_pointer_font( void)
220 {
221 	if ( !guts.pointer_font)
222 		guts.pointer_font = XLoadQueryFont( DISP, "cursor");
223 	if ( !guts.pointer_font) {
224 		warn( "Cannot load cursor font");
225 		return false;
226 	}
227 	return true;
228 }
229 
230 static void
draw_poly(Handle self,int n_points,Point * pts,int dx,int dy)231 draw_poly( Handle self, int n_points, Point * pts, int dx, int dy)
232 {
233 	RegionRec rgnrec, *prgnrec;
234 	PIcon i = (PIcon) self;
235 	ImgPaintContext ctx = {
236 		{0,0,0,0},
237 		{0,0,0,0},
238 		ropCopyPut, false,
239 		{0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff },
240 		{0,0},
241 		lpSolid,
242 		NULL,
243 		{dx,dy}
244 	};
245 	Image mask;
246 	img_fill_dummy(&mask, i->w, i->h, i->maskType, i->mask, NULL);
247 
248 	Handle rgn = (Handle) create_object("Prima::Region", "");
249 	apc_region_destroy(rgn);
250 	rgnrec.type = rgnPolygon;
251 	rgnrec.data.polygon.n_points  = n_points;
252 	rgnrec.data.polygon.points    = pts;
253 	rgnrec.data.polygon.fill_mode = fmWinding;
254 	apc_region_create(rgn, &rgnrec);
255 	apc_region_offset(rgn, dx, dy);
256 	prgnrec = apc_region_copy_rects(rgn);
257 	Object_destroy(rgn);
258 
259 	ctx.region = &prgnrec->data.box;
260 	memset( &ctx.color, 0xff, sizeof(ctx.color));
261 	img_bar( self, 0, 0, i->w, i-> h, &ctx);
262 	memset( &ctx.color, 0x0, sizeof(ctx.color));
263 	img_bar((Handle) &mask, 0, 0, i->w, i-> h, &ctx);
264 	free(prgnrec);
265 	ctx.region = NULL;
266 
267 	memset( &ctx.color, 0x0, sizeof(ctx.color));
268 	img_polyline( self, n_points, pts, &ctx);
269 	img_polyline((Handle) &mask, n_points, pts, &ctx);
270 }
271 
272 static void
xdnd_synthesize_cursor(Handle self,int id)273 xdnd_synthesize_cursor(Handle self, int id)
274 {
275 	PIcon i  = (PIcon) self;
276 	int side = ((i->h < i->w) ? i->h : i->w) / 2;
277 	int s1 = side / 3; /* worst case 2 */
278 	int s2 = s1 * 2;
279 	int s3 = s1 * 3;
280 
281 	CIcon(self)->set_maskType(self, 1);
282 
283 	/* draw a little + = or arrow for copy,move,link in the bottom quarter */
284 	switch ( id ) {
285 	case crDragCopy: {
286 		Point plus[13] = {
287 			{s1, 0},  {s2, 0},  {s2, s1}, {s3, s1},
288 			{s3, s2}, {s2, s2}, {s2, s3}, {s1, s3},
289 			{s1, s2}, {0, s2},  {0, s1},  {s1, s1},
290 			{s1, 0}
291 		};
292 		draw_poly( self, 13, plus, i->w/2, 0 );
293 		break;
294 	}
295 	case crDragMove: {
296 		Point arrow[10] = {
297 			{s2, 0}, {s3, s1}, {s2, s2}, {s3, s2}, {s3, s3},
298 			{0,s3}, {0,0}, {s1, 0}, {s1, s1}, {s2, 0},
299 		};
300 		draw_poly( self, 10, arrow, i->w/2, 0 );
301 		break;
302 	}
303 	case crDragLink: {
304 		Point bar[5] = {{0, 0}, {s1, 0}, {s1, s1}, {0, s1}, {0, 0}};
305 		draw_poly( self, 5, bar, i->w/2, 0 );
306 		draw_poly( self, 5, bar, i->w/2 + s1, s1);
307 		draw_poly( self, 5, bar, i->w/2 + s2, s2);
308 		break;
309 	}}
310 }
311 
312 static Point
get_predefined_hot_spot(int id)313 get_predefined_hot_spot( int id)
314 {
315 	int idx;
316 	XFontStruct *fs;
317 	XCharStruct *cs;
318 	Point ret = {0,0};
319 
320 	if ( id < crDefault || id >= crUser)  return ret;
321 
322 #ifdef HAVE_X11_XCURSOR_XCURSOR_H
323 	{
324 		XcursorImage * i;
325 		if (( i = XcursorLibraryLoadImage( xcursor_map[id] , NULL, guts. cursor_width )) != NULL) {
326 			ret.x = i->xhot;
327 			ret.y = i->height - i->yhot - 1;
328 			XcursorImageDestroy(i);
329 			return ret;
330 		}
331 	}
332 #endif
333 
334 	if ( !load_pointer_font())           return ret;
335 
336 	idx = cursor_map[id];
337 	fs = guts.pointer_font;
338 	if ( !fs-> per_char)
339 		cs = &fs-> min_bounds;
340 	else if ( idx < fs-> min_char_or_byte2 || idx > fs-> max_char_or_byte2) {
341 		int default_char = fs-> default_char;
342 		if ( default_char < fs-> min_char_or_byte2 || default_char > fs-> max_char_or_byte2)
343 		default_char = fs-> min_char_or_byte2;
344 		cs = fs-> per_char + default_char - fs-> min_char_or_byte2;
345 	} else
346 		cs = fs-> per_char + idx - fs-> min_char_or_byte2;
347 	ret. x = -cs->lbearing;
348 	ret. y = guts.cursor_height - cs->ascent;
349 	if ( ret. x < 0) ret. x = 0;
350 	if ( ret. y < 0) ret. y = 0;
351 	if ( ret. x >= guts. cursor_width)  ret. x = guts. cursor_width  - 1;
352 	if ( ret. y >= guts. cursor_height) ret. y = guts. cursor_height - 1;
353 	return ret;
354 }
355 
356 static Bool
xlib_cursor_load(Handle self,int id,Handle icon)357 xlib_cursor_load( Handle self, int id, Handle icon)
358 {
359 	XImage *im;
360 	Pixmap p1 = None, p2 = None;
361 	Bool free_pixmap = true, success = false;
362 	GC gc;
363 	XGCValues gcv;
364 	char c;
365 	int w = guts.cursor_width, h = guts.cursor_height;
366 	CustomPointer *cp = NULL;
367 
368 	cp = is_drag_cursor_available(id);
369 	if ( cp != NULL ) {
370 		free_pixmap = false;
371 	} else if ( id == crUser ) {
372 		if ( !self ) return false;
373 		cp = &X(self)->user_pointer;
374 		free_pixmap = false;
375 	} else {
376 		XFontStruct *fs;
377 		XCharStruct *cs;
378 		int idx = cursor_map[id];
379 
380 		if ( !load_pointer_font()) return false;
381 		fs = guts.pointer_font;
382 		if ( !fs-> per_char)
383 			cs = &fs-> min_bounds;
384 		else if ( idx < fs-> min_char_or_byte2 || idx > fs-> max_char_or_byte2) {
385 			int default_char = fs-> default_char;
386 			if ( default_char < fs-> min_char_or_byte2 || default_char > fs-> max_char_or_byte2)
387 				default_char = fs-> min_char_or_byte2;
388 			cs = fs-> per_char + default_char - fs-> min_char_or_byte2;
389 		} else
390 			cs = fs-> per_char + idx - fs-> min_char_or_byte2;
391 
392 		p1 = XCreatePixmap( DISP, guts. root, w, h, 1);
393 		p2 = XCreatePixmap( DISP, guts. root, w, h, 1);
394 		gcv. background = 1;
395 		gcv. foreground = 0;
396 		gcv. font = guts.pointer_font-> fid;
397 		gc = XCreateGC( DISP, p1, GCBackground | GCForeground | GCFont, &gcv);
398 		XFillRectangle( DISP, p1, gc, 0, 0, w, h);
399 		gcv. background = 0;
400 		gcv. foreground = 1;
401 		XChangeGC( DISP, gc, GCBackground | GCForeground, &gcv);
402 		XFillRectangle( DISP, p2, gc, 0, 0, w, h);
403 		XDrawString( DISP, p1, gc, 1-cs-> lbearing, 1+cs-> ascent, (c = (char)(idx+1), &c), 1);
404 		gcv. background = 1;
405 		gcv. foreground = 0;
406 		XChangeGC( DISP, gc, GCBackground | GCForeground, &gcv);
407 		XDrawString( DISP, p2, gc, 1-cs-> lbearing, 1+cs-> ascent, (c = (char)(idx+1), &c), 1);
408 		XDrawString( DISP, p1, gc, 1-cs-> lbearing, 1+cs-> ascent, (c = (char)idx, &c), 1);
409 		XFreeGC( DISP, gc);
410 	}
411 	if ( cp != NULL ) {
412 		p1 = cp-> xor;
413 		p2 = cp-> and;
414 	}
415 	CIcon(icon)-> create_empty( icon, w, h, imBW);
416 	if (( im = XGetImage( DISP, p1, 0, 0, w, h, 1, XYPixmap)) == NULL)
417 		goto FAIL;
418 	prima_copy_xybitmap(
419 		PIcon(icon)-> data, (Byte*)im-> data,
420 		PIcon(icon)-> w, PIcon(icon)-> h,
421 		PIcon(icon)-> lineSize, im-> bytes_per_line);
422 	XDestroyImage( im);
423 	if (( im = XGetImage( DISP, p2, 0, 0, w, h, 1, XYPixmap)) == NULL)
424 		goto FAIL;
425 	prima_copy_xybitmap(
426 		PIcon(icon)-> mask, (Byte*)im-> data,
427 		PIcon(icon)-> w, PIcon(icon)-> h,
428 		PIcon(icon)-> maskLine, im-> bytes_per_line);
429 	if ( cp != NULL ) {
430 		int i;
431 		Byte * mask = PIcon(icon)-> mask;
432 		for ( i = 0; i < PIcon(icon)-> maskSize; i++)
433 			mask[i] = ~mask[i];
434 	}
435 	success = true;
436 FAIL:
437 	if (im != NULL) XDestroyImage( im);
438 	if ( free_pixmap) {
439 		XFreePixmap( DISP, p1);
440 		XFreePixmap( DISP, p2);
441 	}
442 	return success;
443 }
444 
445 
446 static Bool
create_xdnd_pointer(int id,CustomPointer * cp)447 create_xdnd_pointer(int id, CustomPointer* cp)
448 {
449 	Handle self;
450 	if ( cp-> status > 0 ) return true;
451 	if ( cp-> status < 0 ) return false;
452 
453 #ifdef HAVE_X11_XCURSOR_XCURSOR_H
454 	if (( cp->xcursor = XcursorLibraryLoadImage( xcursor_map[id] , NULL, guts. cursor_width )) != NULL) {
455 		if ((cp-> cursor = XcursorImageLoadCursor(DISP, cp->xcursor)) != None) {
456 			cp-> hot_spot.x = cp->xcursor->xhot;
457 			cp-> hot_spot.y = cp->xcursor->height - cp->xcursor->yhot - 1;
458 			cp-> status = 1;
459 			return true;
460 		} else {
461 			XcursorImageDestroy(cp-> xcursor);
462 			cp-> xcursor = NULL;
463 		}
464 	}
465 #endif
466 
467 	if ( id == crDragNone ) {
468 		/* X11 has good enough glyphs for this */
469 		cp->status = -1;
470 		return true;
471 	}
472 
473 	self = (Handle) create_object("Prima::Icon", "");
474 	xlib_cursor_load(NULL_HANDLE, crArrow, self);
475 	if ( PImage(self)->w < 16 || PImage(self)->h < 16 ) {
476 		/* too small */
477 		cp-> status = -1;
478 		Object_destroy(self);
479 		return false;
480 	}
481 	xdnd_synthesize_cursor(self, id);
482 
483 	cp-> status = 1;
484 	create_cursor(cp, self, get_predefined_hot_spot(crArrow));
485 	Object_destroy(self);
486 
487 	return true;
488 }
489 
490 static CustomPointer*
is_drag_cursor_available(int id)491 is_drag_cursor_available(int id)
492 {
493 	if ( id >= crDragNone && id <= crDragLink ) {
494 		CustomPointer *cp = guts.xdnd_pointers + id - crDragNone;
495 		if (cp->status == 0)
496 			create_xdnd_pointer(id, cp);
497 		return (cp->status > 0) ? cp : NULL;
498 	}
499 	return NULL;
500 }
501 
502 #ifdef HAVE_X11_XCURSOR_XCURSOR_H
503 static Bool
xcursor_load(Handle self,int id,Handle icon)504 xcursor_load( Handle self, int id, Handle icon)
505 {
506 	PIcon c;
507 	int x, y;
508 	XcursorPixel * src;
509 	XcursorImage* i = NULL;
510 	Byte * dst_data, * dst_mask;
511 	Bool kill = false;
512 	CustomPointer* cp;
513 
514 	if (( cp = is_drag_cursor_available(id)) != NULL ) {
515 		i = cp->xcursor;
516 		goto READ_BITMAP;
517 	}
518 
519 	if ( i == NULL && id != crUser ) {
520 		if (( i = XcursorLibraryLoadImage( xcursor_map[id] , NULL, guts. cursor_width )) == NULL)
521 			return false;
522 		kill = true;
523 	} else if ( self ) {
524 		DEFXX;
525 		i = XX-> user_pointer.xcursor;
526 		kill = false;
527 	} else
528 		return false;
529 
530 READ_BITMAP:
531 	c = PIcon(icon);
532 	CIcon(icon)-> create_empty_icon( icon, i->width, i->height, imRGB, imbpp8);
533 	dst_data = c->data + c->lineSize * ( c-> h - 1 );
534 	dst_mask = c->mask + c->maskLine * ( c-> h - 1 );
535 	src = i->pixels;
536 	for ( y = 0; y < c-> h; y++) {
537 		Byte * d_data = dst_data, * d_mask = dst_mask;
538 		for ( x = 0; x < c-> w; x++) {
539 			*d_data++ = *src & 0xff;
540 			*d_data++ = (*src >> 8) & 0xff;
541 			*d_data++ = (*src >> 16) & 0xff;
542 			*d_mask++ = (*src >> 24) & 0xff;
543 			src++;
544 		}
545 		dst_mask -= c->maskLine;
546 		dst_data -= c->lineSize;
547 	}
548 	if ( kill ) XcursorImageDestroy(i);
549 	return true;
550 }
551 #endif
552 
553 static int
get_cursor(Handle self,Pixmap * source,Pixmap * mask,Point * hot_spot,Cursor * cursor)554 get_cursor( Handle self, Pixmap *source, Pixmap *mask, Point *hot_spot, Cursor *cursor)
555 {
556 	CustomPointer *cp = NULL;
557 	int id = X(self)-> pointer_id;
558 
559 	while ( self && ( id = X(self)-> pointer_id) == crDefault)
560 		self = PWidget(self)-> owner;
561 	switch ( id ) {
562 	case crDefault:
563 		id = crArrow;
564 		break;
565 	case crUser:
566 		cp = &X(self)->user_pointer;
567 		break;
568 	default:
569 		cp = is_drag_cursor_available(id);
570 		break;
571 	}
572 
573 	if ( cp ) {
574 		if (source)   *source   = cp->xor;
575 		if (mask)     *mask     = cp->and;
576 		if (hot_spot) *hot_spot = cp->hot_spot;
577 		if (cursor)   *cursor   = cp->cursor;
578 	}
579 
580 	return id;
581 }
582 
583 Cursor
prima_get_cursor(Handle self)584 prima_get_cursor(Handle self)
585 {
586 	DEFXX;
587 	CustomPointer* cp;
588 	if ( XX-> flags. pointer_obscured )
589 		return prima_null_pointer();
590 	else if ( XX->pointer_id == crUser )
591 		return XX-> user_pointer.cursor;
592 	else if (( cp = is_drag_cursor_available( XX-> pointer_id )) != NULL )
593 		return cp->cursor;
594 	else
595 		return XX->actual_pointer;
596 }
597 
598 Point
apc_pointer_get_hot_spot(Handle self)599 apc_pointer_get_hot_spot( Handle self)
600 {
601 	Point hot_spot;
602 	int id = get_cursor(self, NULL, NULL, &hot_spot, NULL);
603 	return (id == crUser || is_drag_cursor_available(id) != NULL)
604 		? hot_spot : get_predefined_hot_spot(id);
605 }
606 
607 Point
apc_pointer_get_pos(Handle self)608 apc_pointer_get_pos( Handle self)
609 {
610 	Point p;
611 	XWindow root, child;
612 	int x, y;
613 	unsigned int mask;
614 
615 	if ( !XQueryPointer( DISP, guts. root,
616 			&root, &child, &p. x, &p. y,
617 			&x, &y, &mask))
618 		return guts. displaySize;
619 	p. y = guts. displaySize. y - p. y - 1;
620 	return p;
621 }
622 
623 int
apc_pointer_get_shape(Handle self)624 apc_pointer_get_shape( Handle self)
625 {
626 	return X(self)->pointer_id;
627 }
628 
629 Point
apc_pointer_get_size(Handle self)630 apc_pointer_get_size( Handle self)
631 {
632 	Point p;
633 	p.x = guts.cursor_width;
634 	p.y = guts.cursor_height;
635 	return p;
636 }
637 
638 Bool
apc_pointer_get_bitmap(Handle self,Handle icon)639 apc_pointer_get_bitmap( Handle self, Handle icon)
640 {
641 	int id = get_cursor( self, NULL, NULL, NULL, NULL);
642 	if ( id < crDefault || id > crUser)  return false;
643 
644 #ifdef HAVE_X11_XCURSOR_XCURSOR_H
645 	if (xcursor_load(self, id, icon))
646 		return true;
647 #endif
648 	return xlib_cursor_load(self, id, icon);
649 }
650 
651 Bool
apc_pointer_get_visible(Handle self)652 apc_pointer_get_visible( Handle self)
653 {
654 	return guts. pointer_invisible_count == 0;
655 }
656 
657 Bool
apc_pointer_set_pos(Handle self,int x,int y)658 apc_pointer_set_pos( Handle self, int x, int y)
659 {
660 	XEvent ev;
661 	if ( !XWarpPointer( DISP, None, guts. root,
662 		0, 0, guts. displaySize.x, guts. displaySize.y, x, guts. displaySize.y - y - 1))
663 		return false;
664 	XCHECKPOINT;
665 	XSync( DISP, false);
666 	while ( XCheckMaskEvent( DISP, PointerMotionMask|EnterWindowMask|LeaveWindowMask, &ev))
667 		prima_handle_event( &ev, NULL);
668 	return true;
669 }
670 
671 Bool
apc_pointer_set_shape(Handle self,int id)672 apc_pointer_set_shape( Handle self, int id)
673 {
674 	DEFXX;
675 	Cursor uc = None;
676 
677 	if ( id < crDefault || id > crUser)  return false;
678 	XX-> pointer_id = id;
679 	id = get_cursor( self, NULL, NULL, NULL, &uc);
680 	if ( id == crUser || is_drag_cursor_available(id)) {
681 		if ( uc != None ) {
682 			if ( self != application) {
683 				if ( guts. pointer_invisible_count < 0) {
684 					if ( !XX-> flags. pointer_obscured) {
685 						XDefineCursor( DISP, XX-> udrawable, prima_null_pointer());
686 						XX-> flags. pointer_obscured = 1;
687 					}
688 				} else {
689 					XDefineCursor( DISP, XX-> udrawable, uc);
690 					XX-> flags. pointer_obscured = 0;
691 				}
692 				XCHECKPOINT;
693 			}
694 		} else
695 			id = crArrow;
696 	} else {
697 		if ( predefined_cursors[id] == None) {
698 			predefined_cursors[id] =
699 				XCreateFontCursor( DISP, cursor_map[id]);
700 			XCHECKPOINT;
701 		}
702 		XX-> actual_pointer = predefined_cursors[id];
703 		if ( self != application) {
704 			if ( guts. pointer_invisible_count < 0) {
705 				if ( !XX-> flags. pointer_obscured) {
706 					XDefineCursor( DISP, XX-> udrawable, prima_null_pointer());
707 					XX-> flags. pointer_obscured = 1;
708 				}
709 			} else {
710 				XDefineCursor( DISP, XX-> udrawable, predefined_cursors[id]);
711 				XX-> flags. pointer_obscured = 0;
712 			}
713 			XCHECKPOINT;
714 		}
715 	}
716 	XFlush( DISP);
717 	if ( guts. grab_widget)
718 		apc_widget_set_capture( guts. grab_widget, true, guts. grab_confine);
719 	return true;
720 }
721 
722 Bool
apc_pointer_set_user(Handle self,Handle icon,Point hot_spot)723 apc_pointer_set_user( Handle self, Handle icon, Point hot_spot)
724 {
725 	DEFXX;
726 
727 	if ( XX-> user_pointer.cursor != None) {
728 		XFreeCursor( DISP, XX-> user_pointer.cursor);
729 		XX-> user_pointer.cursor = None;
730 	}
731 	if ( XX-> user_pointer.xor != None) {
732 		XFreePixmap( DISP, XX-> user_pointer.xor);
733 		XX-> user_pointer.xor = None;
734 	}
735 	if ( XX-> user_pointer.and != None) {
736 		XFreePixmap( DISP, XX-> user_pointer.and);
737 		XX-> user_pointer.and = None;
738 	}
739 #ifdef HAVE_X11_XCURSOR_XCURSOR_H
740 	if ( XX-> user_pointer.xcursor != NULL) {
741 		XcursorImageDestroy(XX-> user_pointer.xcursor);
742 		XX-> user_pointer.xcursor = NULL;
743 	}
744 #endif
745 	if ( icon != NULL_HANDLE) {
746 		Bool ok;
747 		ok = create_cursor(&XX->user_pointer, icon, hot_spot);
748 		if ( !ok ) return false;
749 
750 		if ( XX-> pointer_id == crUser && self != application) {
751 			if ( guts. pointer_invisible_count < 0) {
752 				if ( !XX-> flags. pointer_obscured) {
753 					XDefineCursor( DISP, XX-> udrawable, prima_null_pointer());
754 					XX-> flags. pointer_obscured = 1;
755 				}
756 			} else {
757 				XDefineCursor( DISP, XX-> udrawable, XX-> user_pointer.cursor);
758 				XX-> flags. pointer_obscured = 0;
759 			}
760 			XCHECKPOINT;
761 		}
762 	}
763 	XFlush( DISP);
764 	if ( guts. grab_widget)
765 		apc_widget_set_capture( guts. grab_widget, true, guts. grab_confine);
766 	return true;
767 }
768 
769 Bool
apc_pointer_set_visible(Handle self,Bool visible)770 apc_pointer_set_visible( Handle self, Bool visible)
771 {
772 	/* maintaining hide/show count */
773 	if ( visible) {
774 		if ( guts. pointer_invisible_count == 0)
775 			return true;
776 		if ( ++guts. pointer_invisible_count < 0)
777 			return true;
778 	} else {
779 		if ( guts. pointer_invisible_count-- < 0)
780 			return true;
781 	}
782 
783 	/* setting pointer for widget under cursor */
784 	{
785 		Point p    = apc_pointer_get_pos( application);
786 		Handle wij = apc_application_get_widget_from_point( application, p);
787 		if ( wij) {
788 			X(wij)-> flags. pointer_obscured = (visible ? 0 : 1);
789 			XDefineCursor( DISP, X(wij)-> udrawable, prima_get_cursor(wij));
790 		}
791 	}
792 	XFlush( DISP);
793 	if ( guts. grab_widget)
794 		apc_widget_set_capture( guts. grab_widget, true, guts. grab_confine);
795 	return true;
796 }
797 
798 Cursor
prima_null_pointer(void)799 prima_null_pointer( void)
800 {
801 	if ( guts. null_pointer == NULL_HANDLE) {
802 		Handle nullc = ( Handle) create_object( "Prima::Icon", "", NULL);
803 		PIcon  n = ( PIcon) nullc;
804 		Pixmap xor, and;
805 		XColor xc;
806 		if ( nullc == NULL_HANDLE) {
807 			warn("Error creating icon object");
808 			return NULL_HANDLE;
809 		}
810 		n-> self-> create_empty( nullc, 16, 16, imBW);
811 		memset( n-> mask, 0xFF, n-> maskSize);
812 		if ( !prima_create_icon_pixmaps( nullc, &xor, &and)) {
813 			warn( "Error creating null cursor pixmaps");
814 			Object_destroy( nullc);
815 			return NULL_HANDLE;
816 		}
817 		Object_destroy( nullc);
818 		xc. red = xc. green = xc. blue = 0;
819 		xc. pixel = guts. monochromeMap[0];
820 		xc. flags = DoRed | DoGreen | DoBlue;
821 		guts. null_pointer = XCreatePixmapCursor( DISP, xor, and, &xc, &xc, 0, 0);
822 		XCHECKPOINT;
823 		XFreePixmap( DISP, xor);
824 		XFreePixmap( DISP, and);
825 		if ( !guts. null_pointer) {
826 			warn( "Error creating null cursor from pixmaps");
827 			return NULL_HANDLE;
828 		}
829 	}
830 	return guts. null_pointer;
831 }
832