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