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