1 
2 extern Pixmap	pointer_pmap;							// aus Modul graph.C ...
3 extern int		pointer_off_x, pointer_off_y;		//    dto.
4 
5 #ifndef _cursor_h
6 #	include "cursor.h"
7 #endif
8 
9 // ------ PBallNorm ----------------------------------------------------------
10 
11 int    PBallNorm::instance_count = 0;
12 int    PBallNorm::pwidth  = 0;
13 int    PBallNorm::pheight = 0;
14 Pixmap PBallNorm::pmap    = 0;
15 GC     PBallNorm::gc_bit  = 0;
16 double PBallNorm::next_frame = 0.0;
17 double PBallNorm::last_frame = 0.0;
18 
19 
PBallNorm(PBallType type,char * disp_name)20 PBallNorm::PBallNorm( PBallType type, char *disp_name ) :
21 	PBallTop( type )
22 {
23 //
24 // Remote Display �ffnen
25 //
26 	rem_dpy = XOpenDisplay( disp_name );
27 	if (!rem_dpy) {
28 		fprintf( stderr, "ERROR: can't open display '%s'.\n", disp_name );
29 		exit(0);
30 	}
31 	scr=DefaultScreen(rem_dpy);
32 //
33 // Cursor �berdefinieren
34 //
35 Cursor	cursor;
36 Pixmap	pixmap;
37 XColor	col1;
38 Window	grab_win;
39 
40 	pixmap = XCreateBitmapFromData(rem_dpy,
41 					RootWindow( rem_dpy, scr ),
42 					cursor_bits, cursor_width, cursor_height );
43 	XParseColor(rem_dpy,DefaultColormap(rem_dpy,scr), "black", &col1 );
44 	cursor = XCreatePixmapCursor( rem_dpy, pixmap, pixmap, &col1, &col1,
45 							cursor_x_hot, cursor_y_hot );
46 
47 
48 	if (!strcmp(DisplayString(dpy),DisplayString(rem_dpy))) {
49 		warp_dpy = dpy;
50 		grab_win = (new_root)?new_root:win;
51 	}
52 	else {
53 		warp_dpy = rem_dpy;
54 		grab_win = RootWindow( rem_dpy, scr );
55 	}
56 	XGrabPointer( rem_dpy, grab_win,
57 			False, (unsigned)(ButtonPressMask|ButtonReleaseMask|PointerMotionMask),
58 			GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime );
59 
60 //
61 // Transformation festlegen
62 //
63 	w2n_x = DisplayWidth( rem_dpy,scr)/world_x;
64 	w2n_y = DisplayHeight(rem_dpy,scr)/world_y;
65 //
66 // Pointer auf Bildschirm initialisieren.
67 //
68 	x_old = -20;	// ausserhalb des Bildschirms (wegen erstem CopyArea)
69 	y_old = -20;
70 
71 	XFreePixmap( rem_dpy, pixmap );
72 	XFreeCursor( rem_dpy, cursor );
73 
74 
75 	if (!instance_count++) {
76 		pwidth  = 30;
77 		pheight = 30;
78 		pmap    = XCreatePixmap(dpy,win,pwidth,pheight,1);
79 		gc_bit  = XCreateGC(dpy,pmap,0,0);
80 		XSetState(dpy,gc_bit,1,0,GXxor,1);
81 		next_frame=GetCurrentTime();
82 		last_frame=GetCurrentTime();
83 	}
84 }
85 
~PBallNorm()86 PBallNorm::~PBallNorm() {
87 	if (!--instance_count) {
88 		XFreePixmap(dpy,pmap);
89 		XFreeGC(dpy,gc_bit);
90 	}
91 //
92 // Pointer vom Bildschirm l�schen
93 //
94 	XCopyArea( dpy, pointer_pmap, win, gc_cursor, 0, 0, 16, 16,
95 					x_old-pointer_off_x, y_old-pointer_off_y );
96 
97 	XSync( rem_dpy, 0 );
98 	XCloseDisplay( rem_dpy );
99 }
100 
WaitForEvents()101 void PBallNorm::WaitForEvents() {
102 PBallTop *current;
103 
104 	// if any balls are running or any of the pballs is not ReadyToSleep
105 	// we have to return directly
106 	if (!frames_per_sec) {
107 		if (g->running_balls)															return;
108 		for (current=pball_queue;current;current=current->next) {
109 			if (!(current->mode&ReadyToSleep))										return;
110 		}
111 	}
112 
113 	// collected the opened displays into a fd-structure for the select
114 	// statement and wait a bit using a select call (if there aren't already
115 	// some events in the queue.
116 	struct timeval		timeout;
117 	if (!frames_per_sec) {
118 		timeout.tv_sec  = 1;
119 		timeout.tv_usec = 0;
120 	}
121 	else {
122 		double ctime = GetCurrentTime();
123 		if (current_time<last_frame || ctime>next_frame) {
124 			last_frame = ctime;
125 			next_frame = ctime + 1.0/frames_per_sec;
126 			return;
127 		}
128 		timeout.tv_sec  = 0;
129 		timeout.tv_usec = (unsigned)(1000000*(next_frame-ctime));
130 	}
131 	int	nfds;
132 
133 #ifndef FD_ZERO
134 // if the macros aren't defined, we assume that this is a system
135 // using integers for the select-parameters
136 	int				readfds;
137 #	define FD_ZERO(n)	n=0
138 #	define FD_SET(b,n)	(n|(1<<b))
139 #	undef	 FDS_TYPE
140 #	define FDS_TYPE	(int*)
141 #else
142 	struct fd_set	readfds;
143 #endif
144 
145 	FD_ZERO( &readfds );
146 	FD_SET(  ConnectionNumber(dpy), &readfds  );
147 	nfds = ConnectionNumber(dpy)+1;
148 	if (XEventsQueued(dpy,QueuedAfterFlush))										return;
149 
150 	for (current=pball_queue;current;current=current->next) {
151 		FD_SET(ConnectionNumber(((PBallNorm*)current)->rem_dpy),&readfds);
152 		if (ConnectionNumber(((PBallNorm*)current)->rem_dpy)>=nfds) {
153 			nfds=ConnectionNumber(((PBallNorm*)current)->rem_dpy)+1;
154 		}
155 		if (XEventsQueued(((PBallNorm*)current)->rem_dpy,QueuedAfterFlush))return;
156 	}
157 
158 // FDS_TYPE should have been set in global.h to the type, that is need
159 // in the select-call for the filedescriptors.
160 	if (!select(nfds,FDS_TYPE &readfds,0,0,&timeout)) {
161 		/* TIMEOUT */
162 		if (frames_per_sec) {
163 				last_frame = next_frame;
164 				next_frame = next_frame + 1.0/frames_per_sec;
165 		}
166 	}
167 }
168 
Update()169 void PBallNorm::Update() {
170 XEvent	event;
171 
172 	while (XEventsQueued( rem_dpy, QueuedAfterFlush )) {
173 		XNextEvent( rem_dpy, &event );
174 		switch( event.xany.type ) {
175 		case MotionNotify:
176 			while( XCheckMaskEvent( rem_dpy, PointerMotionMask, &event ));
177 			PointerMoveTo(Vec2(Real(event.xmotion.x_root)/w2n_x,
178 												Real(event.xmotion.y_root)/w2n_y));
179 			break;
180 		case ButtonPress:
181 			Press( event.xbutton.button );
182 			break; // case ButtonPress
183 
184 		case ButtonRelease:
185 			Release( event.xbutton.button );
186 		} // switch(event.type)
187 	}
188 
189 	PBallTop::Update();
190 }
191 
192 
Warp(const Vec2 & dest)193 void PBallNorm::Warp( const Vec2 &dest ) {
194 XEvent	event;
195 
196 	XWarpPointer( warp_dpy, None, RootWindow( rem_dpy, scr ),
197 			0,0,0,0 ,
198 			(int)(dest.X()*w2n_x),
199 			(int)(dest.Y()*w2n_y) );
200 
201 	if (mode&UsingTool)	MoveAimingTool();
202 
203 	XSync( warp_dpy, 0 );
204 	while( XCheckMaskEvent( rem_dpy, PointerMotionMask, &event ));
205 }
206 
207 
RedrawPointer()208 void PBallNorm::RedrawPointer() {
209 	XCopyArea( dpy, pointer_pmap, win, gc_cursor, 0, 0, 16, 16,
210 					x_old-pointer_off_x, y_old-pointer_off_y );
211 }
212 
SetPointer(int x,int y)213 void PBallNorm::SetPointer( int x, int y ) {
214 	XCopyArea( dpy, pointer_pmap, win, gc_cursor, 0, 0, 16, 16,
215 					x_old-pointer_off_x, y_old-pointer_off_y );
216 	x_old = x;
217 	y_old = y;
218 //	RedrawPointer:
219 	XCopyArea( dpy, pointer_pmap, win, gc_cursor, 0, 0, 16, 16,
220 					x_old-pointer_off_x, y_old-pointer_off_y );
221 }
222 
223 
224 // ------------------------------------------------------------
225 
226 #ifndef XPix
227 #	define XPix(x)   ((int)((x)*w2n))
228 #	define YPix(y)   ((int)((y)*w2n))
229 #	define Pix(r)    ((int)((r)*w2n))
230 #endif
231 
DrawQueue()232 void PBallNorm::DrawQueue() {
233 	if (valid_queue_position) {
234 		XPoint	p[3];
235 
236 		p[0].x = XPix(q_end_s.X());
237 		p[0].y = YPix(q_end_s.Y());
238 		p[1].x = XPix(q_s1_s.X());
239 		p[1].y = YPix(q_s1_s.Y());
240 		p[2].x = XPix(q_s2_s.X());
241 		p[2].y = YPix(q_s2_s.Y());
242 		XFillPolygon(dpy,win,gc_current,p,3,0,0);
243 	}
244 }
245 
StartQueue(const Vec2 & end,const Vec2 & s1,const Vec2 & s2)246 void PBallNorm::StartQueue(const Vec2 &end, const Vec2 &s1, const Vec2 &s2) {
247 	q_end_s = end;
248 	q_s1_s  = s1;
249 	q_s2_s  = s2;
250 	valid_queue_position = 1;
251 	DrawQueue();
252 }
253 
SetMinSize(int width,int height)254 void PBallNorm::SetMinSize( int width, int height ) {
255 // ggf. vergroesserte Pixmap anlegen
256 	if (width>pwidth||height>pheight) {
257 		XFreePixmap(dpy,pmap);
258 		if (width>pwidth)			pwidth =width +10;
259 		if (height>pheight)		pheight=height+10;
260 		pmap = XCreatePixmap(dpy,win,pwidth+2,pheight+2,1);
261 	}
262 }
263 
264 #define _TEST_BUG
265 
MoveQueue(const Vec2 & end,const Vec2 & s1,const Vec2 & s2)266 void PBallNorm::MoveQueue(const Vec2 &end, const Vec2 &s1, const Vec2 &s2) {
267 XPoint	p[6];
268 unsigned	i;
269 
270 #ifdef TEST_BUG
271 //
272 // There is an optimization (-O2) bug in gcc-2.7.0, so that q_end_s always
273 // seems be equal to end. I assume, that due to the optimization, the
274 // assignment q_end_s = end is done before the first assignment:
275 //
276 	printf( "OLD POSITION: %f x %f\n", (float)q_end_s.X(), (float)q_end_s.Y() );
277 	printf( "NEW POSITION: %f x %f\n", (float)end.X(), (float)end.Y() );
278 #endif
279 
280 	p[0].x = XPix(q_end_s.X());	// triangle-coordinates of old queue
281 	p[0].y = YPix(q_end_s.Y());
282 	p[1].x = XPix(q_s1_s.X());
283 	p[1].y = YPix(q_s1_s.Y());
284 	p[2].x = XPix(q_s2_s.X());
285 	p[2].y = YPix(q_s2_s.Y());
286 
287 	q_end_s = end;						// store new position
288 	q_s1_s  = s1;
289 	q_s2_s  = s2;
290 	p[3].x = XPix(q_end_s.X());	// triangle-coordinates of new queue
291 	p[3].y = YPix(q_end_s.Y());
292 	p[4].x = XPix(q_s1_s.X());
293 	p[4].y = YPix(q_s1_s.Y());
294 	p[5].x = XPix(q_s2_s.X());
295 	p[5].y = YPix(q_s2_s.Y());
296 
297 	valid_queue_position = 1;
298 
299 int	min_x = p[0].x;
300 int	max_x = p[0].x;
301 int	min_y = p[0].y;
302 int	max_y = p[0].y;
303 
304 #ifdef TEST_BUG
305 	printf( "NOT ASSIGNED: %f x %f\n", (float)q_end_s.X(), (float)q_end_s.Y() );
306 #endif
307 
308 // Minimum suchen
309 	for (i=1;i<sizeof(p)/sizeof(XPoint);i++) {
310 		if (p[i].x < min_x)		min_x = p[i].x;
311 		if (p[i].x > max_x)		max_x = p[i].x;
312 		if (p[i].y < min_y)		min_y = p[i].y;
313 		if (p[i].y > max_y)		max_y = p[i].y;
314 	}
315 #ifdef TEST_BUG
316 	printf( "    ASSIGNED: %f x %f\n", (float)q_end_s.X(), (float)q_end_s.Y() );
317 #endif
318 
319 
320 // Punkte nun relativ zur Bitmap
321 	for (i=0;i<sizeof(p)/sizeof(XPoint);i++) {
322 		p[i].x -= min_x;
323 		p[i].y -= min_y;
324 	}
325 
326 int width  = max_x - min_x;
327 int height = max_y - min_y;
328 
329 	SetMinSize(width,height);
330 
331 // Pixmap aufbauen und ins Fenster verknuepfen
332 	XSetFunction(dpy,gc_bit,GXclear);
333 	XFillRectangle(dpy,pmap,gc_bit,0,0,width,height);
334 	XSetFunction(dpy,gc_bit,GXxor);
335 	XFillPolygon(dpy,pmap,gc_bit,&p[0],3,0,0);
336 	XFillPolygon(dpy,pmap,gc_bit,&p[3],3,0,0);
337 	XCopyPlane(dpy,pmap,win,gc_current,0,0,width,height,min_x,min_y,1);
338 }
339 
EndQueue()340 void PBallNorm::EndQueue() {
341 	DrawQueue();
342 	valid_queue_position = 0;
343 }
344 
345 // ------ PBall -------------------------------------------------------------
346 
PBall(PBallType type,char * disp_name)347 PBall::PBall( PBallType type, char *disp_name ) :
348 	PBallNorm( type, disp_name )
349 {
350 	zoom  = 0;
351 	ozoom = 0;
352 	off_x = 0.0;
353 	off_y = 0.0;
354 	mid_x = DisplayWidth( rem_dpy,scr)/2;
355 	mid_y = DisplayHeight(rem_dpy,scr)/2;
356 	zfact = 32;
357 }
358 
~PBall()359 PBall::~PBall() {
360 }
361 
Update()362 void PBall::Update() {
363 XEvent	event;
364 
365 	while (XEventsQueued( rem_dpy, QueuedAfterFlush )) {
366 		XNextEvent( rem_dpy, &event );
367 		switch( event.xany.type ) {
368 		case MotionNotify:
369 			while( XCheckMaskEvent( rem_dpy, PointerMotionMask, &event ));
370 			if (zoom) {
371 				off_x=(double)(event.xmotion.x_root-mid_x)/zfact;
372 				off_y=(double)(event.xmotion.y_root-mid_y)/zfact;
373 				PointerMoveTo(Vec2(Real(zoom_x+off_x)/w2n_x,
374 												Real(zoom_y+off_y)/w2n_y));
375 			}
376 			else {
377 				PointerMoveTo(Vec2(Real(event.xmotion.x_root+off_x)/w2n_x,
378 												Real(event.xmotion.y_root+off_y)/w2n_y));
379 			}
380 			break;
381 		case ButtonPress:
382 			if (!zoom) {
383 				if ( event.xbutton.button == Button2 ) {
384 					StartZooming( event.xbutton.x_root, event.xbutton.y_root );
385 				}
386 				else {
387 					Press( event.xbutton.button );
388 				}
389 			}
390 			break; // case ButtonPress
391 
392 		case ButtonRelease:
393 			if ( event.xbutton.button == Button2 )		StopZooming();
394 			else											Release( event.xbutton.button );
395 		} // switch(event.type)
396 	}
397 
398 	PBallTop::Update();
399 }
400 
StartZooming(int x,int y)401 void PBall::StartZooming(int x,int y) {
402 	if (!zoom) {
403 		zoom_x = x;	zoom_y = y;
404 		zoom=1;
405 		XWarpPointer( warp_dpy, None, RootWindow( rem_dpy, scr ),
406 			0,0,0,0,(int)(mid_x+zfact*off_x),(int)(mid_y+zfact*off_y) );
407 		SetPointer( (int)(dest.X()*w2n), (int)(dest.Y()*w2n) );
408 		XSync( warp_dpy, 1 );
409 	}
410 }
411 
StopZooming()412 void PBall::StopZooming() {
413 	if (zoom) {
414 		zoom_x += (int)floor(off_x);
415 		zoom_y += (int)floor(off_y);
416 		off_x    = off_x-floor(off_x);
417 		off_y    = off_y-floor(off_y);
418 		XWarpPointer( warp_dpy, None, RootWindow( rem_dpy, scr ),
419 			0,0,0,0,zoom_x,zoom_y );
420 		XSync( warp_dpy, 0 );
421 		zoom=0;
422 		SetPointer( (int)(dest.X()*w2n), (int)(dest.Y()*w2n) );
423 	}
424 }
425 
Warp(const Vec2 & dest)426 void PBall::Warp( const Vec2 &dest ) {
427 
428 	StopZooming();
429 	PBallNorm::Warp(dest);
430 }
431 
432 //
433 // when cursor moves less than ISTEP pixels (in zoom mode) at an instant
434 // both grid-displays will be double-buffered in the same pixmap
435 //
436 #define	ISTEP	2
437 
SetPointer(int x,int y)438 void PBall::SetPointer( int x, int y ) {
439 	SetMinSize(2*zfact+2+2*ISTEP,2*zfact+2+2*ISTEP);
440 
441 int	oox=ISTEP;		// internal offset in help-pixmap
442 int	ooy=ISTEP;
443 
444 	if (ozoom) {
445 							// prepare the grid of the old position (centered)
446 			XSetFunction(dpy,gc_bit,GXclear);
447 			XFillRectangle(dpy,pmap,gc_bit,0,0,2*zfact+2+2*ISTEP,2*zfact+2+2*ISTEP);
448 				/*** GRID DRAWER (identical code as in zoom) ***/
449 			XSetFunction(dpy,gc_bit,GXxor);
450 			XDrawRectangle(dpy,pmap,gc_bit,
451 					oox,ooy,2*zfact+1,2*zfact+1);
452 			XDrawLine(dpy,pmap,gc_bit,
453 					(int)(zfact-ooff_x)+oox,(int)(ooy),
454 					(int)(zfact-ooff_x)+oox,(int)(2*zfact+ooy) );
455 			XDrawLine(dpy,pmap,gc_bit,
456 					(int)(2*zfact-ooff_x)+oox,(int)(ooy),
457 					(int)(2*zfact-ooff_x)+oox,(int)(2*zfact+ooy) );
458 			XDrawLine(dpy,pmap,gc_bit,
459 					(int)(oox),(int)(zfact-ooff_y)+ooy,
460 					(int)(2*zfact+oox),(int)(zfact-ooff_y)+ooy );
461 			XDrawLine(dpy,pmap,gc_bit,
462 					(int)(oox),(int)(2*zfact-ooff_y)+ooy,
463 					(int)(2*zfact+oox),(int)(2*zfact-ooff_y)+ooy );
464 				/*** end GRID DRAWER ***/
465 	}
466 	oox = x-x_old+ISTEP;
467 	ooy = y-y_old+ISTEP;
468 
469 	XCopyArea( dpy, pointer_pmap, win, gc_cursor, 0, 0, 16, 16,
470 					x_old-pointer_off_x, y_old-pointer_off_y );
471 	x_old = x;
472 	y_old = y;
473 	XCopyArea( dpy, pointer_pmap, win, gc_cursor, 0, 0, 16, 16,
474 					x_old-pointer_off_x, y_old-pointer_off_y );
475 
476 	if (zoom) {
477 			ooff_x = (off_x-floor(off_x))*zfact;
478 			ooff_y = (off_y-floor(off_y))*zfact;
479 
480 			if (ozoom&&(oox<0)||(oox>2*ISTEP)||(ooy<0)||(ooy>2*ISTEP)) {
481 				// flush prepared map
482 				XCopyPlane(dpy,pmap,win,gc_cursor,ISTEP,ISTEP,2*zfact+2,2*zfact+2,
483 					x_old-zfact-oox+ISTEP, y_old-zfact-ooy+ISTEP, 1 );
484 				ozoom=0;
485 			}
486 			if (!ozoom) {
487 				// reset map
488 				oox = ISTEP;
489 				ooy = ISTEP;
490 				XSetFunction(dpy,gc_bit,GXclear);
491 				XFillRectangle(dpy,pmap,gc_bit,0,0,2*zfact+2+2*ISTEP,2*zfact+2+2*ISTEP);
492 			}
493 			ozoom=1;
494 
495 				/*** GRID DRAWER (identical code as in ozoom) ***/
496 			XSetFunction(dpy,gc_bit,GXxor);
497 			XDrawRectangle(dpy,pmap,gc_bit,
498 					oox,ooy,2*zfact+1,2*zfact+1);
499 			XDrawLine(dpy,pmap,gc_bit,
500 					(int)(zfact-ooff_x)+oox,(int)(ooy),
501 					(int)(zfact-ooff_x)+oox,(int)(2*zfact+ooy) );
502 			XDrawLine(dpy,pmap,gc_bit,
503 					(int)(2*zfact-ooff_x)+oox,(int)(ooy),
504 					(int)(2*zfact-ooff_x)+oox,(int)(2*zfact+ooy) );
505 			XDrawLine(dpy,pmap,gc_bit,
506 					(int)(oox),(int)(zfact-ooff_y)+ooy,
507 					(int)(2*zfact+oox),(int)(zfact-ooff_y)+ooy );
508 			XDrawLine(dpy,pmap,gc_bit,
509 					(int)(oox),(int)(2*zfact-ooff_y)+ooy,
510 					(int)(2*zfact+oox),(int)(2*zfact-ooff_y)+ooy );
511 				/*** end GRID DRAWER ***/
512 
513 			// copy full map (2*zfact+2+2*ISTEP), since ozoom&zoom might be joined
514 			XCopyPlane(dpy,pmap,win,gc_cursor,0,0,2*zfact+2+2*ISTEP,2*zfact+2+2*ISTEP,
515 					x_old-zfact-oox, y_old-zfact-ooy, 1 );
516 	}
517 	else {
518 		if (ozoom) {
519 			// flush prepared map
520 			XCopyPlane(dpy,pmap,win,gc_cursor,ISTEP,ISTEP,2*zfact+2,2*zfact+2,
521 					x_old-zfact-oox+ISTEP, y_old-zfact-ooy+ISTEP, 1 );
522 		}
523 		ozoom=0;
524 	}
525 }
526