chiark / gitweb /
can use original model now
[moebius2.git] / view.c
1 /*
2  * Displays a conformation
3  */
4
5 #include <X11/Xlib.h>
6 #include <X11/Xutil.h>
7
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <sys/poll.h>
11
12 #include "mgraph.h"
13
14 #define MAXTRIS (N*2)
15
16 typedef struct { double vertex[3][D3]; } Triangle;
17
18 static Triangle trisbuffer[MAXTRIS], *displaylist[MAXTRIS];
19 static int ntris;
20 static Vertices conformation;
21
22 static double transform[D3][D3]= {{1,0,0}, {0,1,0}, {0,0,1}};
23 static GSL_MATRIX(transform);
24
25 static FILE *input_f;
26 static struct stat input_stab;
27 static const char *input_filename;
28
29 static void read_input(void) {
30   int r;
31   
32   if (input_f) fclose(input_f);
33   input_f= fopen(input_filename, "rb");  if (!input_f) diee("input file");
34
35   if (fstat(fileno(input_f), &input_stab)) diee("fstat input file");
36
37   errno= 0;
38   r= fread(&conformation,sizeof(conformation),1,input_f);
39   if (r!=1) diee("fread");
40 }
41
42 static void transform_coordinates(void) {
43   double result[D3];
44   GSL_VECTOR(result);
45   gsl_vector input_gsl= { D3,1 };
46
47   int v, k;
48   
49   FOR_VERTEX(v) {
50     input_gsl.data= &conformation[v][0];
51     GA( gsl_blas_dgemv(CblasNoTrans, 1.0,&transform_gsl, &input_gsl,
52                        0.0, &result_gsl) );
53     K conformation[v][k]= result[k];
54   }
55 }
56
57 static int vertex_in_triangles[N], vertex_in_triangles_checked;
58
59 static void addtriangle(int va, int vb, int vc) {
60   Triangle *t= &trisbuffer[ntris];
61   int k;
62   
63   assert(ntris < MAXTRIS);
64   K {
65     t->vertex[0][k]= conformation[va][k];
66     t->vertex[1][k]= conformation[vb][k];
67     t->vertex[2][k]= conformation[vc][k];
68   }
69   if (!vertex_in_triangles_checked) {
70     vertex_in_triangles[va]++;
71     vertex_in_triangles[vb]++;
72     vertex_in_triangles[vc]++;
73   }
74   displaylist[ntris++]= t;
75 }
76
77 static void generate_display_list(void) {
78   int vb, ve[V6], e;
79
80   ntris= 0;
81   FOR_VERTEX(vb) {
82     /* We use the two triangles in the parallelogram vb, vb+e5, vb+e0, vb+e1.
83      * We go round each triangle clockwise (although our surface is non-
84      * orientable so it shouldn't matter).  Picking the parallelogram
85      * to our right avoids getting it wrong at the join.
86      */
87 //if ((vb & YMASK) > Y1) continue;
88 //if ((vb & XMASK) > 2) continue; 
89     for (e=0; e<V6; e++) ve[e]= EDGE_END2(vb,e);
90     assert(ve[0]>=0);
91     if (ve[5]>=0) addtriangle(vb,ve[0],ve[5]);
92 //continue;
93     if (ve[1]>=0) addtriangle(vb,ve[1],ve[0]);
94   }
95
96   if (!vertex_in_triangles_checked) {
97     int v, expd;
98     FOR_VERTEX(v) {
99       expd= RIM_VERTEX_P(v) ? 3 : 6;
100       if (vertex_in_triangles[v] != expd) {
101         fprintf(stderr,"vertex %02x used for %d triangles, expected %d\n",
102                 v, vertex_in_triangles[v], expd);
103 //      abort();
104       }
105     }
106     vertex_in_triangles_checked= 1;
107   }
108 }    
109
110 static int dl_compare(const void *tav, const void *tbv) {
111   int i;
112   const Triangle *const *tap= tav, *ta= *tap;
113   const Triangle *const *tbp= tbv, *tb= *tbp;
114   double za=0, zb=0;
115   for (i=0; i<3; i++) {
116     za += ta->vertex[i][2];
117     zb += tb->vertex[i][2];
118   }
119   return za > zb ? -1 :
120          za < zb ? +1 : 0;
121 }
122
123 static void sort_display_list(void) {
124   qsort(displaylist, ntris, sizeof(*displaylist), dl_compare);
125 }
126
127 /*---------- X stuff ----------*/
128
129 #define WSZ 400
130
131 typedef struct { GC fillgc, linegc; } DrawingMode;
132
133 static Display *display;
134 static Pixmap pixmap, doublebuffers[2];
135 static Window window;
136
137 static DrawingMode dmred, dmblue, dmwhite;
138 static const DrawingMode *dmcurrent;
139 static int wwidth=WSZ, wheight=WSZ, wmindim=WSZ, wmaxdim=WSZ;
140 static int ncut, currentbuffer, x11depth, x11screen, wireframe;
141 XVisualInfo visinfo;
142
143 static double sizeadj_scale= 0.3, eyes_apart, scale_wmindim;
144 static double eye_z= -10, eye_x=0;
145 static double cut_z= -9;
146 static const double eyes_apart_preferred=0.05, eyes_apart_min= -0.02;
147
148
149 static void drawtriangle(const Triangle *t) {
150   XPoint points[4];
151   int i;
152
153   for (i=0; i<3; i++) {
154     const double *v= t->vertex[i];
155     double x= v[0];
156     double y= v[1];
157     double z= v[2];
158
159     if (z < cut_z) { ncut++; return; }
160     
161     double zezezp= eye_z / (eye_z - z);
162     points[i].x= scale_wmindim * (zezezp * (x - eye_x) + eye_x) + wwidth/2;
163     points[i].y= scale_wmindim * (zezezp *  y                 ) + wheight/2;
164   }
165   points[3]= points[0];
166
167   if (!wireframe)
168     XA( XFillPolygon(display,pixmap, dmcurrent->fillgc,
169                      points,3,Convex,CoordModeOrigin) );
170   XA( XDrawLines(display,pixmap, dmcurrent->linegc,
171                  points, 4,CoordModeOrigin) );
172 }
173
174 static const unsigned long core_event_mask=
175   ButtonPressMask|ButtonReleaseMask|StructureNotifyMask|ButtonMotionMask|
176   KeyPressMask;
177
178 static void mkpixmaps(void) {
179   for (currentbuffer=0; currentbuffer<2; currentbuffer++) {
180     XA( pixmap= XCreatePixmap(display,window,wwidth,wheight,x11depth) );
181     doublebuffers[currentbuffer]= pixmap;
182   }
183   currentbuffer= 0;
184 }
185
186 static void mkgcs(DrawingMode *dm, unsigned long planes) {
187   XGCValues gcv;
188
189   gcv.function= GXcopy;
190   gcv.foreground= WhitePixel(display,x11screen);
191   gcv.plane_mask= planes;
192   dm->linegc= XCreateGC(display,pixmap,
193                         GCFunction|GCForeground|GCPlaneMask,
194                         &gcv);
195
196   gcv.function= GXclear;
197   dm->fillgc= XCreateGC(display,pixmap,
198                         GCFunction|GCPlaneMask,
199                         &gcv);
200 }
201
202 static void display_prepare(void) {
203   XSetWindowAttributes wa;
204   XSizeHints hints;
205   
206   XA( display= XOpenDisplay(0) );
207   x11screen= DefaultScreen(display);
208   x11depth= DefaultDepth(display,x11screen);
209   XA( XMatchVisualInfo(display,x11screen,x11depth, TrueColor,&visinfo) );
210   
211   wa.event_mask= core_event_mask;
212   XA( window= XCreateWindow(display, DefaultRootWindow(display),
213                             0,0, wwidth,wheight, 0,x11depth,
214                             InputOutput, visinfo.visual,
215                             CWEventMask, &wa) );
216
217   hints.flags= USPosition;
218   hints.x= 10;
219   hints.y= 10;
220   XSetWMNormalHints(display,window,&hints);
221
222   mkpixmaps();
223
224   mkgcs(&dmwhite, AllPlanes);
225   mkgcs(&dmblue, visinfo.blue_mask);
226   mkgcs(&dmred, visinfo.red_mask);
227 }
228
229 static void drawtriangles(const DrawingMode *dm) {
230   Triangle *const *t;
231   int i;
232
233   dmcurrent= dm;
234   for (i=0, t=displaylist, ncut=0; i<ntris; i++, t++)
235     drawtriangle(*t);
236 }
237
238 static void display_conformation(void) {
239   pixmap= doublebuffers[currentbuffer];
240
241   XA( XFillRectangle(display,pixmap,dmwhite.fillgc,0,0,wwidth,wheight) );
242
243   if (eyes_apart > 0) {
244     const double stationary= 0.07;
245
246     eye_x= eyes_apart < eyes_apart_preferred
247               ? eyes_apart :
248            eyes_apart < (eyes_apart_preferred + stationary)
249               ? eyes_apart_preferred
250               : eyes_apart - stationary;
251     eye_x /= sizeadj_scale;
252     drawtriangles(&dmblue);
253     eye_x= -eye_x;
254     drawtriangles(&dmred);
255   } else {
256     drawtriangles(&dmwhite);
257     printf("shown, %d/%d triangles cut\n", ncut, ntris);
258   }
259   
260   XA( XSetWindowBackgroundPixmap(display,window,pixmap) );
261   XA( XClearWindow(display,window) );
262   currentbuffer= !currentbuffer;
263 }
264
265 static void show(void) {
266   scale_wmindim= sizeadj_scale * wmindim;
267   read_input();
268   transform_coordinates();
269   generate_display_list();
270   sort_display_list();
271   display_conformation();
272 }
273
274 typedef struct {
275   const char *name;
276   void (*start)(const XButtonEvent *e);
277   void (*delta)(double dx, double dy);
278   void (*conclude)(void);
279   void (*abandon)(void);
280 } Drag;
281
282 #define DRAG(x)                                 \
283   static const Drag drag_##x= {                 \
284     #x, drag_##x##_start, drag_##x##_delta,     \
285     drag_##x##_conclude, drag_##x##_abandon     \
286   }
287
288 #define DRAG_SAVING(x, thing, hook)                     \
289   static typeof(thing) original_##thing;                \
290   static void drag_##x##_start(const XButtonEvent *e) { \
291     memcpy(&original_##thing, &thing, sizeof(thing));   \
292     hook;                                               \
293   }                                                     \
294   static void drag_##x##_conclude(void) { }             \
295   static void drag_##x##_abandon(void) {                \
296     memcpy(&thing, &original_##thing, sizeof(thing));   \
297     show();                                             \
298   }                                                     \
299   DRAG(x)
300
301 static void drag_none_start(const XButtonEvent *e) { }
302 static void drag_none_delta(double dx, double dy) { }
303 static void drag_none_conclude(void) { }
304 static void drag_none_abandon(void) { }
305 DRAG(none);
306
307 static void pvectorcore(const char *n, double v[D3]) {
308   int k;
309   printf("%10s [ ",n);
310   K printf("%# 10.10f ",v[k]);
311   printf("]\n");
312 }
313 static void pvector(const char *n, double v[D3]) {
314   pvectorcore(n,v);
315   putchar('\n');
316 }
317 static void pmatrix(const char *n, double m[D3][D3]) {
318   int j;
319   for (j=0; j<D3; j++) { pvectorcore(n,m[j]); n=""; }
320   putchar('\n');
321 }
322 #define PMATRIX(x) pmatrix(#x,x);
323
324 static double drag_transform_conv_x_z= 0;
325 static double drag_transform_conv_y_z= 0;
326
327 static void drag_transform_prep(const XButtonEvent *e) {
328   static const double factor= 2.5;
329   drag_transform_conv_x_z= MAX( MIN(e->y * factor / wheight - (factor/2),
330                                     1.0), -1.0);
331   drag_transform_conv_y_z= MAX( MIN(e->x * factor / wwidth  - (factor/2),
332                                     1.0), -1.0);
333   printf("drag_transform_conv_{x,y}_z = %g,%g\n",
334          drag_transform_conv_x_z, drag_transform_conv_y_z);
335 }
336
337 static void make_z_rotation(double rotz[D3][D3], double cz, double sz) {
338   rotz[0][0]=  cz;    rotz[0][1]=  sz;     rotz[0][2]=  0;
339   rotz[1][0]= -sz;    rotz[1][1]=  cz;     rotz[1][2]=  0;
340   rotz[2][0]=   0;    rotz[2][1]=   0;     rotz[2][2]=  1;
341 }  
342
343 static void drag_rotate_delta(double dx, double dy) {
344   /* We multiple our transformation matrix by a matrix:
345    *
346    * If we just had y movement, we would rotate about x axis:
347    *  rotation X = [  1    0   0 ]
348    *               [  0   cy  sy ]
349    *               [  0  -sy  cy ]
350    *  where cy and sy are sin and cos of y rotation
351    *
352    * But we should pre-rotate this by a rotation about the z axis
353    * to get it to the right angle (to include x rotation).  So
354    * we make cy and sy be cos() and sin(hypot(x,y)) and use
355    * with cr,sr as cos() and sin(atan2(y,y)):
356    *
357    * Ie we would do  T' = Z R^T X R T   where
358    *             or  T' = Z    C    T   where  C = R^T X R  and
359    *
360    *  adjustment R = [  cr  sr  0 ]
361    *                 [ -sr  cr  0 ]
362    *                 [  0    0  1 ]    or make_z_rotation(cr,sr)
363    *
364    *  rotation Z   = [  cz  sz  0 ]
365    *                 [ -sz  cz  0 ]
366    *                 [  0    0  1 ]    or make_z_rotation(cz,sz)
367    */
368
369   double rotx[D3][D3], adjr[D3][D3], rotz[D3][D3];
370   GSL_MATRIX(rotx);
371   GSL_MATRIX(adjr);
372   GSL_MATRIX(rotz);
373
374   static double temp[D3][D3], change[D3][D3];
375   static GSL_MATRIX(temp);
376   static GSL_MATRIX(change);
377
378   printf("\nTRANSFORM %g, %g\n", dx,dy);
379
380   double dz= -drag_transform_conv_x_z * dx +
381               drag_transform_conv_y_z * dy;
382           
383   dx *= (1 - fabs(drag_transform_conv_x_z));
384   dy *= (1 - fabs(drag_transform_conv_y_z));
385
386   double d= hypot(dx,dy);
387
388   printf(" dx,dy,dz = %g, %g, %g    d = %g\n", dx,dy,dz, d);
389
390   if (hypot(d,dz) < 1e-6) return;
391
392   printf(" no xy rotation\n");
393
394   double ang= d*2.0;
395   
396   double cy= cos(ang);
397   double sy= sin(ang);
398
399   double cr, sr;
400   if (d > 1e-6) {
401     cr= -dy / d;
402     sr=  dx / d;
403   } else {
404     cr= 1;
405     sr= 0;
406   }
407   printf("\n d=%g cy,sy=%g,%g cr,sr=%g,%g\n\n", d,cy,sy,cr,sr);
408   
409   rotx[0][0]=   1;    rotx[0][1]=   0;     rotx[0][2]=  0;
410   rotx[1][0]=   0;    rotx[1][1]=  cy;     rotx[1][2]= sy;
411   rotx[2][0]=   0;    rotx[2][1]= -sy;     rotx[2][2]= cy;
412   PMATRIX(rotx);
413
414   make_z_rotation(adjr,cr,sr);
415   PMATRIX(adjr);
416
417   GA( gsl_blas_dgemm(CblasNoTrans,CblasNoTrans, 1.0,
418                      &rotx_gsl,&adjr_gsl,
419                      0.0, &temp_gsl) );
420   pmatrix("X R", temp);
421   
422   GA( gsl_blas_dgemm(CblasTrans,CblasNoTrans, 1.0,
423                      &adjr_gsl,&temp_gsl,
424                      0.0, &change_gsl) );
425   PMATRIX(change);
426
427   double angz= dz*2.0;
428   make_z_rotation(rotz,cos(angz),sin(angz));
429   PMATRIX(rotz);
430
431   GA( gsl_blas_dgemm(CblasTrans,CblasNoTrans, 1.0,
432                      &rotz_gsl,&change_gsl,
433                      0.0, &temp_gsl) );
434   pmatrix("Z C", temp);
435
436   static double skew[D3][D3];
437   static GSL_MATRIX(skew);
438   
439   GA( gsl_blas_dgemm(CblasNoTrans,CblasNoTrans, 1.0,
440                      &temp_gsl, &transform_gsl,
441                      0.0, &skew_gsl) );
442   PMATRIX(skew);
443
444   memcpy(&transform,&skew,sizeof(transform));
445   show();
446   return;
447
448   /* Now we want to normalise skew, the result becomes new transform */
449   double svd_v[D3][D3];
450   GSL_MATRIX(svd_v);
451
452   double sigma[D3], tau[D3];
453   GSL_VECTOR(sigma);
454   GSL_VECTOR(tau);
455   
456   /* We use notation from Wikipedia Polar_decomposition
457    *     Wikipedia's  W      is GSL's U
458    *     Wikipedia's  Sigma  is GSL's S
459    *     Wikipedia's  V      is GSL's V
460    *     Wikipedia's  U      is our desired result
461    * Wikipedia which says if the SVD is    A = W Sigma V*
462    * then the polar decomposition is       A = U P
463    *   where                               P = V Sigma V*
464    *   and                                 U = W V*
465    */
466   
467   GA( gsl_linalg_SV_decomp(&skew_gsl, &svd_v_gsl, &sigma_gsl, &tau_gsl) );
468   pmatrix("W",    skew);
469   pvector("Sigma",sigma);
470   pmatrix("V",    svd_v);
471
472   /* We only need U, not P. */
473   GA( gsl_blas_dgemm(CblasNoTrans,CblasTrans, 1.0,
474                      &skew_gsl,&svd_v_gsl,
475                      0.0,&transform_gsl) );
476
477   pmatrix("U", transform);
478
479   printf("drag_rotate_delta...\n");
480   show();
481 }
482 DRAG_SAVING(rotate, transform, drag_transform_prep(e));
483
484 static void drag_sizeadj_delta(double dx, double dy) {
485   sizeadj_scale *= pow(3.0, -dy);
486   show();
487 }
488 DRAG_SAVING(sizeadj, sizeadj_scale, );
489
490 static void drag_3d_delta(double dx, double dy) {
491   eyes_apart += dx * 0.1;
492   if (eyes_apart < eyes_apart_min) eyes_apart= eyes_apart_min;
493   printf("sizeadj eyes_apart %g\n", eyes_apart);
494   show();
495 }
496 DRAG_SAVING(3d, eyes_apart, );
497
498 static const Drag *drag= &drag_none;
499
500 static int drag_last_x, drag_last_y;
501
502 static void drag_position(int x, int y) {
503   drag->delta((x - drag_last_x) * 1.0 / wmaxdim,
504               (y - drag_last_y) * 1.0 / wmaxdim);
505   drag_last_x= x;
506   drag_last_y= y;
507 }
508
509 static void event_button(XButtonEvent *e) {
510   if (e->window != window || !e->same_screen) return;
511   if (e->type == ButtonPress) {
512     if (e->state || drag != &drag_none) {
513       printf("drag=%s press state=0x%lx abandon\n",
514              drag->name, (unsigned long)e->state);
515       drag->abandon();
516       drag= &drag_none;
517       return;
518     }
519     switch (e->button) {
520     case Button1: drag= &drag_rotate;  break;
521     case Button2: drag= &drag_sizeadj; break;
522     case Button3: drag= &drag_3d;      break;
523     default: printf("unknown drag start %d\n", e->button);
524     }
525     printf("drag=%s press button=%lu start %d,%d\n",
526            drag->name, (unsigned long)e->button, e->x, e->y);
527     drag_last_x= e->x;
528     drag_last_y= e->y;
529     drag->start(e);
530   }
531   if (e->type == ButtonRelease) {
532     printf("drag=%s release %d,%d\n", drag->name, e->x, e->y);
533     drag_position(e->x, e->y);
534     drag->conclude();
535     drag= &drag_none;
536   }
537 }
538
539 static void event_motion(int x, int y) {
540   printf("drag=%s motion %d,%d\n", drag->name, x, y);
541   drag_position(x,y);
542 }
543
544 static void event_key(XKeyEvent *e) {
545   KeySym ks;
546   char buf[10];
547   int r;
548
549   r= XLookupString(e,buf,sizeof(buf)-1,&ks,0);
550   if (!r) {
551     printf("XLookupString keycode=%u state=0x%x gave %d\n",
552            e->keycode, e->state, r);
553     return;
554   }
555
556   if (!strcmp(buf,"q")) exit(0);
557   if (!strcmp(buf,"w")) {
558     wireframe= !wireframe;
559     show();
560     return;
561   }
562   if (!strcmp(buf,"d")) {
563     eyes_apart= eyes_apart>0 ? eyes_apart_min : eyes_apart_preferred;
564     show();
565     return;
566   }
567 }
568
569 static void event_config(XConfigureEvent *e) {
570   if (e->width == wwidth && e->height == wheight)
571     return;
572
573   wwidth= e->width;  wheight= e->height;
574   wmaxdim= wwidth > wheight ? wwidth : wheight;
575   wmindim= wwidth < wheight ? wwidth : wheight;
576   
577   XA( XSetWindowBackground(display,window,BlackPixel(display,x11screen)) );
578   for (currentbuffer=0; currentbuffer<2; currentbuffer++)
579     XA( XFreePixmap(display, doublebuffers[currentbuffer]) );
580
581   mkpixmaps();
582   show();
583 }
584
585 static void check_input(void) {
586   struct stat newstab;
587   int r;
588
589   r= stat(input_filename, &newstab);
590   if (r<0) diee("could not check input");
591
592 #define CI(x) if (newstab.st_##x == input_stab.st_##x) ; else goto changed
593   CI(dev);
594   CI(ino);
595   CI(size);
596   CI(mtime);
597 #undef CI
598   return;
599
600  changed:
601   show();
602 }
603
604 static void topocheck(void) {
605   int v1,e,v2,eprime,v1prime, count;
606   FOR_EDGE(v1,e,v2) {
607     count= 0;
608     FOR_VEDGE(v2,eprime,v1prime)
609       if (v1prime==v1) count++;
610     if (count!=1) {
611       fprintf(stderr,"%02x -%d-> %02x  reverse edge count = %d!\n",
612               v1,e,v2, count);
613       FOR_VEDGE(v2,eprime,v1prime)
614         fprintf(stderr,"%02x -%d-> %02x -> %d -> %02x\n",
615                 v1,e,v2,eprime,v1prime);
616       exit(-1);
617     }
618   }
619 }
620
621 int main(int argc, const char *const *argv) {
622   static const int wantedevents= POLLIN|POLLPRI|POLLERR|POLLHUP;
623
624   XEvent event;
625   int k, i, r, *xfds, nxfds, polls_alloc=0;
626   struct pollfd *polls=0;
627   int motion_deferred=0, motion_x=-1, motion_y=-1;
628
629   topocheck();
630   if (argc==1) { printf("topology self-consistent, ok\n"); exit(0); }
631   
632   if (argc != 2 || argv[1][0]=='-') {
633     fputs("need filename\n",stderr); exit(8);
634   }
635   input_filename= argv[1];
636
637   read_input();
638   K transform[k][k]= 1.0;
639   display_prepare();
640   show();
641
642   XMapWindow(display,window);
643   for (;;) {
644
645     XA( XInternalConnectionNumbers(display, &xfds, &nxfds) );
646     if (polls_alloc <= nxfds) {
647       polls_alloc= nxfds + polls_alloc + 1;
648       polls= realloc(polls, sizeof(*polls) * polls_alloc);
649       if (!polls) diee("realloc for pollfds");
650     }
651     for (i=0; i<nxfds; i++) {
652       polls[i].fd= xfds[i];
653       polls[i].events= wantedevents;
654       polls[i].revents= 0;
655     }
656     XFree(xfds);
657
658     polls[i].fd= ConnectionNumber(display);
659     polls[i].events= wantedevents;
660
661     r= poll(polls, nxfds+1, motion_deferred ? 0 : 200);
662     if (r<0) diee("poll");
663
664     for (i=0; i<nxfds; i++)
665       if (polls[i].revents)
666         XProcessInternalConnection(display, polls[i].fd);
667
668     r= XCheckMaskEvent(display,~0UL,&event);
669     if (!r) {
670       if (motion_deferred) {
671         event_motion(motion_x, motion_y);
672         motion_deferred=0;
673       }
674       check_input();
675       continue;
676     }
677     
678     switch (event.type) {
679
680     case ButtonPress:
681     case ButtonRelease:     event_button(&event.xbutton);     break;
682       
683     case KeyPress:          event_key(&event.xkey);           break;
684
685     case ConfigureNotify:   event_config(&event.xconfigure);  break;
686
687     case MotionNotify:
688       motion_x= event.xmotion.x;
689       motion_y= event.xmotion.y;
690       motion_deferred= 1;
691       continue;
692       
693     default:
694       printf("unknown event type %u 0x%x\n", event.type,event.type);
695     }
696   }
697 }
698