1 /*
2  * The Rubik's cube.
3  *
4  * Sed - april 1999 / december 2003.
5  *
6  * This program is in the public domain.
7  *--------------------
8  * The screen stuff, ie X.
9  *
10  * Heavily hacked by J.-P. Demailly during week-end in October 2003,
11  * to make following additions work :
12  * wm_delete ClientMessage intercepted + menu popup + automatic resize
13  *
14  */
15 
16 #include "screen.h"
17 
18 #include <X11/Xlib.h>
19 #include <X11/Xutil.h>
20 #include <X11/keysym.h>
21 #include <X11/cursorfont.h>
22 #include <string.h>
23 #include <math.h>
24 #include <sys/time.h>
25 #include <unistd.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 
29 #include "cube.h"
30 #include "device.h"
31 
32 extern char help1[];
33 extern char help2[];
34 extern char help3[];
35 extern char control_str[];
36 extern char close_str[];
37 extern char *controlwin_str[];
38 extern char save_output_str[];
39 extern char perspective_str[];
40 extern char cube_saved_str[];
41 
42 
43 static Atom wm_protocols, wm_delete_window;
44 
45 static int mark = 1;
46 static short color_marks[54];
47 
48 static int gl_u[6] = { 150, 30, 150, 270, 390, 150};
49 static int gl_v[6] = { 120, 240, 240, 240, 240, 360};
50 
51 /* color pixels */
52 unsigned long pixel[11];
53 
set_colors(SCREEN * s)54 int set_colors(SCREEN *s)
55 {
56   XColor color, exact;
57   int i, failed;
58 
59   failed = 0;
60   for (i=10; i>=0; i--) {
61     if (!XAllocNamedColor(s->d, s->cm, palette[i], &color, &exact))
62        failed = 1;
63     pixel[i] = color.pixel;
64     /*
65     printf("%lu %d %d %d\n",  pixel[i],
66 	   color.red, color.green, color.blue);
67     */
68   }
69   /* printf("Failure : %d\n", failed); */
70   return failed;
71 }
72 
error_statement(SCREEN * s)73 void error_statement(SCREEN *s)
74 {
75   perror("");
76   fprintf(stderr, "Meaning no rubix\n");
77   XCloseDisplay(s->d);
78   s->d=(Display *)0;
79 }
80 
reset_data_buffers(SCREEN * s)81 int reset_data_buffers(SCREEN *s)
82 {
83 
84   if (s->im)
85     XDestroyImage(s->im);
86 
87   s->buffer=(char *)malloc(((SCREEN_X+7)/8)*SCREEN_Y*s->depth);
88 
89   if (!s->buffer) {
90     error_statement(s);
91     return -1;
92   }
93 
94   if (the_lines)
95     free(the_lines);
96   the_lines = (device_line *)malloc(SCREEN_Y*sizeof(device_line));
97 
98   if (!the_lines) {
99     error_statement(s);
100     return -1;
101   }
102 
103   /* create our XImage */
104   /* bitmap_pad, what the heck is it ?? mail sed@free.fr if you know, please...
105    * Well, seems it has to be 32...
106    */
107   /* Sed - december 2003 - no it has to be 8 */
108   s->im=XCreateImage(s->d, DefaultVisual(s->d, DefaultScreen(s->d)),
109 		     s->depth==32?24:s->depth, ZPixmap,
110 		     0, s->buffer, SCREEN_X, SCREEN_Y, 8, 0);
111 
112   if (!s->im) {
113     error_statement(s);
114     return -1;
115   } else
116     return 0;
117 }
118 
set_cube_size()119 void set_cube_size()
120 {
121   int s;
122   s = SCREEN_X;
123   if (SCREEN_Y < s) s = SCREEN_Y;
124   CUBE_SIZE = s * (0.95 - 0.2 * fabs(PERSPECTIVE));
125 }
126 
127 /* return -1 if error, 0 if ok */
init_screen(SCREEN * s)128 int init_screen(SCREEN *s)
129 {
130   extern device d;
131   XGCValues gcv;
132   XEvent ev;
133   XClassHint          xch;
134 
135   memset(s, 0, sizeof(SCREEN));
136 
137   if (!(s->d=XOpenDisplay((char *)0)))
138     return -1;
139 
140   s->depth=DefaultDepth(s->d, DefaultScreen(s->d));
141 
142   if (s->depth!=8 && s->depth!=16 && s->depth!=24) {
143     fprintf(stderr, "screen depth not supported (only 8, 16 and 24bpp (which means 32bpp too) handled\n");
144     XCloseDisplay(s->d);
145     s->d=(Display *)0;
146     return -1;
147   }
148 
149   wm_protocols = XInternAtom(s->d, "WM_PROTOCOLS", False);
150   wm_delete_window = XInternAtom(s->d, "WM_DELETE_WINDOW", False);
151 
152   s->font = XLoadQueryFont(s->d, FONT);
153   if (!s->font) {
154     fprintf(stderr, "cannot load font %s !!\n", FONT);
155     return -1;
156   }
157   HELP_X = (help_maxlen + 1) * XTextWidth(s->font, "w", 1);
158   HELP_Y = (help_numlines + 2) *
159            (s->font->max_bounds.ascent + s->font->max_bounds.descent + 2) + 3;
160 
161   /* we don't know if s->depth==24 if it's 24 or 32bpp, let's check it */
162   if (s->depth==24) {
163     int n;
164     XPixmapFormatValues *v;
165 
166     v=XListPixmapFormats(s->d, &n);
167     if (!v || !n) {
168       if (v)
169 	XFree(v);
170       fprintf(stderr, "Error while trying to distinguish between 24 or 32bpp, damned...\n");
171       XCloseDisplay(s->d);
172       s->d=(Display *)0;
173       return -1;
174     }
175     for (n--; n>=0; n--)
176       if (v[n].bits_per_pixel==32)
177 	s->depth=32;
178     XFree(v);
179   }
180 
181   s->cm = DefaultColormap(s->d, DefaultScreen(s->d));
182 
183   /* let's create and map our window */
184   s->w=XCreateWindow(s->d, DefaultRootWindow(s->d), 0, 0,
185 		       SCREEN_X, SCREEN_Y, 3, s->depth==32?24:s->depth,
186 		       CopyFromParent, CopyFromParent, 0, NULL);
187   s->h=XCreateWindow(s->d, DefaultRootWindow(s->d), 0, 0,
188 		       HELP_X, HELP_Y, 3, s->depth==32?24:s->depth,
189 		       CopyFromParent, CopyFromParent, 0, NULL);
190 
191   xch.res_name = "rubix";
192   xch.res_class = "Rubix";
193   XSetClassHint(s->d, s->w, &xch);
194   XStoreName(s->d, s->w, xch.res_name);
195 
196   XSelectInput(s->d, s->w,
197     KeyPressMask|ExposureMask|StructureNotifyMask|
198     PointerMotionMask|ButtonPressMask|ButtonReleaseMask);
199   XSetWMProtocols(s->d, s->w, &wm_delete_window, 1);
200 
201   XSelectInput(s->d, s->h, ExposureMask|ButtonReleaseMask);
202   XSetWMProtocols(s->d, s->h, &wm_delete_window, 1);
203 
204   XMapWindow(s->d, s->w);
205   do
206     XNextEvent(s->d, &ev);
207   while (ev.type!=Expose || ev.xexpose.window!=s->w);
208 /*  XFlush(s->d); no xflush */
209   XSetWMProtocols(s->d, s->w, &wm_delete_window, 1);
210 
211   gcv.function=GXcopy;
212   s->gc=XCreateGC(s->d, s->w, GCFunction, &gcv);
213   XSetFont(s->d, s->gc, s->font->fid);
214 
215   if (reset_data_buffers(s) == -1)
216     return -1;
217 
218   d.buffer=(unsigned char*)s->buffer;
219   d.depth=s->depth;
220   return 0;
221 }
222 
realize_colors(SCREEN * s)223 void realize_colors(SCREEN *s)
224 {
225   if (set_colors(s) && s->depth<=8) {
226     s->cm=XCreateColormap(s->d, DefaultRootWindow(s->d),
227 	    DefaultVisual(s->d, DefaultScreen(s->d)), AllocNone);
228     set_colors(s);
229   }
230 
231   if (s->depth<=8) {
232      XSetWindowColormap(s->d, s->w, s->cm);
233      XSetWindowColormap(s->d, s->h, s->cm);
234   }
235 
236   XSetWindowBackground(s->d, s->w, pixel[10]);
237   XSetWindowBackground(s->d, s->h, pixel[10]);
238 }
239 
grab_pointer(SCREEN * s,int mode)240 void grab_pointer(SCREEN *s, int mode)
241 {
242   static Cursor void_cursor = None;
243   static Cursor arrow_cursor = None;
244   Pixmap p;
245   XColor a;
246   GC pixgc;
247   XSetWindowAttributes attributs;
248 
249   if (mode == 0) {
250     XUngrabPointer(s->d, 0);
251     if (arrow_cursor == None)
252       arrow_cursor = XCreateFontCursor(s->d, XC_hand2);
253     attributs.cursor = arrow_cursor;
254     XChangeWindowAttributes(s->d, s->w, CWCursor, &attributs);
255     return;
256   }
257 
258 
259   /* let's create a void cursor for our window */
260   if (void_cursor == None) {
261     p=XCreatePixmap(s->d, DefaultRootWindow(s->d), 1, 1, 1);
262     pixgc=XCreateGC(s->d, p, 0, 0);
263     a.red=a.green=a.blue=0;
264     XFillRectangle(s->d, p, pixgc, 0, 0, 2, 2);
265     XFreeGC(s->d, pixgc);
266     void_cursor = XCreatePixmapCursor(s->d, p, p, &a, &a, 0, 0);
267     /* XFreePixmap(s->d, p); */
268   }
269   attributs.cursor = void_cursor;
270   XChangeWindowAttributes(s->d, s->w, CWCursor, &attributs);
271 
272   if (XGrabPointer(s->d, s->w, False,
273       PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
274       GrabModeAsync, GrabModeAsync, s->w, None, 0))
275     fprintf(stderr, "Could not grab the pointer, the program won't run very nice.\n");
276   place_mouse(s);
277 }
278 
display_help(SCREEN * s)279 void display_help(SCREEN *s)
280 {
281   int i, dx, dy;
282   XStoreName(s->d, s->h, "rubix/help");
283 /*  XFlush(s->d);   no xflush */
284 /*  usleep(50000);  no usleep */
285   XSetForeground(s->d, s->gc, pixel[9]);
286   dy = s->font->max_bounds.ascent + s->font->max_bounds.descent + 2;
287   for (i=0; i<help_numlines; i++)
288   XDrawString(s->d, s->h, s->gc, 6,
289 		s->font->max_bounds.ascent + 6 + i * dy,
290 		help_index[i], strlen(help_index[i]));
291 
292   dx = XTextWidth(s->font, control_str, strlen(control_str));
293   XDrawString(s->d, s->h, s->gc, 13,
294 	      HELP_Y -  s->font->max_bounds.descent - 12, control_str, strlen(control_str));
295   XDrawRectangle(s->d, s->h, s->gc, 6, HELP_Y - dy - 14, dx+14, dy+6);
296   XDrawRectangle(s->d, s->h, s->gc, 5, HELP_Y - dy - 15, dx+16, dy+8);
297 
298   dx = XTextWidth(s->font, close_str, strlen(close_str));
299   XDrawString(s->d, s->h, s->gc, HELP_X - dx - 15,
300 	      HELP_Y -  s->font->max_bounds.descent - 12, close_str, strlen(close_str));
301   XDrawRectangle(s->d, s->h, s->gc, HELP_X - dx - 22, HELP_Y - dy - 14, dx+14, dy+6);
302   XDrawRectangle(s->d, s->h, s->gc, HELP_X - dx - 23, HELP_Y - dy - 15, dx+16, dy+8);
303 /*  XFlush(s->d); no xflush */
304 }
305 
306 void
show_text(SCREEN * s,int mode)307 show_text(SCREEN *s, int mode)
308 {
309   char dum[50];
310   int h;
311   h = s->font->max_bounds.ascent + s->font->max_bounds.descent + 17;
312   XClearArea(s->d, s->w, 0, SCREEN_Y - h, SCREEN_X, h, 0);
313   switch(mode) {
314     case 0: sprintf(dum, "%s = %s",
315 	       save_output_str, formats[FORMAT]); break;
316     case 1: sprintf(dum, "%s = %.1f",
317 	       perspective_str, PERSPECTIVE); break;
318     case 2: sprintf(dum, "%s ./rubix.%s",
319 	       cube_saved_str, formats[FORMAT]);
320             dum[25] = '\0'; break;
321     default: strcpy(dum, "???"); break;
322   }
323   XSetForeground(s->d, s->gc, pixel[9]);
324   XDrawString(s->d, s->w, s->gc,
325 	        SCREEN_X - XTextWidth(s->font, dum, strlen(dum)) - 8,
326 		SCREEN_Y - s->font->max_bounds.descent - 6,
327 		dum, strlen(dum));
328 }
329 
get_marks(CUBE * c)330 void get_marks(CUBE *c)
331 {
332   c->cubes[25].colors[1] = color_marks[0];
333   c->cubes[22].colors[1] = color_marks[1];
334   c->cubes[20].colors[1] = color_marks[2];
335   c->cubes[24].colors[1] = color_marks[3];
336   c->cubes[1].colors[1]  = color_marks[4];
337   c->cubes[19].colors[1] = color_marks[5];
338   c->cubes[23].colors[1] = color_marks[6];
339   c->cubes[21].colors[1] = color_marks[7];
340   c->cubes[18].colors[1] = color_marks[8];
341 
342   c->cubes[25].colors[2] = color_marks[9];
343   c->cubes[24].colors[2] = color_marks[10];
344   c->cubes[23].colors[2] = color_marks[11];
345   c->cubes[23].colors[4] = color_marks[18];
346   c->cubes[21].colors[4] = color_marks[19];
347   c->cubes[18].colors[4] = color_marks[20];
348   c->cubes[18].colors[3] = color_marks[27];
349   c->cubes[19].colors[3] = color_marks[28];
350   c->cubes[20].colors[3] = color_marks[29];
351   c->cubes[20].colors[5] = color_marks[36];
352   c->cubes[22].colors[5] = color_marks[37];
353   c->cubes[25].colors[5] = color_marks[38];
354 
355   c->cubes[17].colors[2] = color_marks[12];
356   c->cubes[2].colors[2]  = color_marks[13];
357   c->cubes[16].colors[2] = color_marks[14];
358   c->cubes[16].colors[4] = color_marks[21];
359   c->cubes[4].colors[4]  = color_marks[22];
360   c->cubes[14].colors[4] = color_marks[23];
361   c->cubes[14].colors[3] = color_marks[30];
362   c->cubes[3].colors[3]  = color_marks[31];
363   c->cubes[15].colors[3] = color_marks[32];
364   c->cubes[15].colors[5] = color_marks[39];
365   c->cubes[5].colors[5]  = color_marks[40];
366   c->cubes[17].colors[5] = color_marks[41];
367 
368   c->cubes[13].colors[2] = color_marks[15];
369   c->cubes[12].colors[2] = color_marks[16];
370   c->cubes[11].colors[2] = color_marks[17];
371   c->cubes[11].colors[4] = color_marks[24];
372   c->cubes[9].colors[4]  = color_marks[25];
373   c->cubes[6].colors[4]  = color_marks[26];
374   c->cubes[6].colors[3]  = color_marks[33];
375   c->cubes[7].colors[3]  = color_marks[34];
376   c->cubes[8].colors[3]  = color_marks[35];
377   c->cubes[8].colors[5]  = color_marks[42];
378   c->cubes[10].colors[5] = color_marks[43];
379   c->cubes[13].colors[5] = color_marks[44];
380 
381   c->cubes[11].colors[0] = color_marks[45];
382   c->cubes[9].colors[0]  = color_marks[46];
383   c->cubes[6].colors[0]  = color_marks[47];
384   c->cubes[12].colors[0] = color_marks[48];
385   c->cubes[0].colors[0]  = color_marks[49];
386   c->cubes[7].colors[0]  = color_marks[50];
387   c->cubes[13].colors[0] = color_marks[51];
388   c->cubes[10].colors[0] = color_marks[52];
389   c->cubes[8].colors[0]  = color_marks[53];
390 }
391 
set_marks(CUBE * c)392 void set_marks(CUBE *c)
393 {
394   color_marks[0] = c->cubes[25].colors[1];
395   color_marks[1] = c->cubes[22].colors[1];
396   color_marks[2] = c->cubes[20].colors[1];
397   color_marks[3] = c->cubes[24].colors[1];
398   color_marks[4] = c->cubes[1].colors[1];
399   color_marks[5] = c->cubes[19].colors[1];
400   color_marks[6] = c->cubes[23].colors[1];
401   color_marks[7] = c->cubes[21].colors[1];
402   color_marks[8] = c->cubes[18].colors[1];
403 
404   color_marks[9] = c->cubes[25].colors[2];
405   color_marks[10] = c->cubes[24].colors[2];
406   color_marks[11] = c->cubes[23].colors[2];
407   color_marks[18] = c->cubes[23].colors[4];
408   color_marks[19] = c->cubes[21].colors[4];
409   color_marks[20] = c->cubes[18].colors[4];
410   color_marks[27] = c->cubes[18].colors[3];
411   color_marks[28] = c->cubes[19].colors[3];
412   color_marks[29]= c->cubes[20].colors[3];
413   color_marks[36]= c->cubes[20].colors[5];
414   color_marks[37]= c->cubes[22].colors[5];
415   color_marks[38]= c->cubes[25].colors[5];
416 
417   color_marks[12] = c->cubes[17].colors[2];
418   color_marks[13] = c->cubes[2].colors[2];
419   color_marks[14] = c->cubes[16].colors[2];
420   color_marks[21] = c->cubes[16].colors[4];
421   color_marks[22] = c->cubes[4].colors[4];
422   color_marks[23] = c->cubes[14].colors[4];
423   color_marks[30] = c->cubes[14].colors[3];
424   color_marks[31] = c->cubes[3].colors[3];
425   color_marks[32]= c->cubes[15].colors[3];
426   color_marks[39]= c->cubes[15].colors[5];
427   color_marks[40]= c->cubes[5].colors[5];
428   color_marks[41]= c->cubes[17].colors[5];
429 
430   color_marks[15] = c->cubes[13].colors[2];
431   color_marks[16] = c->cubes[12].colors[2];
432   color_marks[17] = c->cubes[11].colors[2];
433   color_marks[24] = c->cubes[11].colors[4];
434   color_marks[25] = c->cubes[9].colors[4];
435   color_marks[26] = c->cubes[6].colors[4];
436   color_marks[33] = c->cubes[6].colors[3];
437   color_marks[34] = c->cubes[7].colors[3];
438   color_marks[35]= c->cubes[8].colors[3];
439   color_marks[42]= c->cubes[8].colors[5];
440   color_marks[43]= c->cubes[10].colors[5];
441   color_marks[44]= c->cubes[13].colors[5];
442 
443   color_marks[45] = c->cubes[11].colors[0];
444   color_marks[46] = c->cubes[9].colors[0];
445   color_marks[47] = c->cubes[6].colors[0];
446   color_marks[48] = c->cubes[12].colors[0];
447   color_marks[49] = c->cubes[0].colors[0];
448   color_marks[50] = c->cubes[7].colors[0];
449   color_marks[51] = c->cubes[13].colors[0];
450   color_marks[52] = c->cubes[10].colors[0];
451   color_marks[53] = c->cubes[8].colors[0];
452 }
453 
display_controls(SCREEN * s)454 void display_controls(SCREEN *s)
455 {
456   int i, j, k, p, dx, dy;
457   static char faces[] = "BCEDFA";
458 
459   dy = s->font->max_bounds.ascent + s->font->max_bounds.descent + 2;
460 
461   XStoreName(s->d, s->h, "rubix/control");
462 /*  XFlush(s->d); no xflush */
463 /*  usleep(50000); no usleep */
464 
465   color_marks[4] = 2;
466   color_marks[13] = 3;
467   color_marks[22] = 5;
468   color_marks[31] = 4;
469   color_marks[40] = 6;
470   color_marks[49] = 1;
471 
472   for (i=0; i<6; i++) {
473     XSetForeground(s->d, s->gc, (i+1==mark)?pixel[9]:pixel[10]);
474     XDrawRectangle(s->d, s->h, s->gc, 29+60*i, 19, 42, 42);
475     XSetForeground(s->d, s->gc, pixel[7]);
476     XDrawRectangle(s->d, s->h, s->gc, 30+60*i, 20, 40, 40);
477     XDrawRectangle(s->d, s->h, s->gc, 31+60*i, 21, 38, 38);
478     XSetForeground(s->d, s->gc, pixel[i+1]);
479     XFillRectangle(s->d, s->h, s->gc, 32+60*i, 22, 37, 37);
480   }
481 
482   for (i=0; i<6; i++) {
483     for (j=0; j<3; j++)
484     for (k=0; k<3; k++) {
485       XSetForeground(s->d, s->gc, pixel[7]);
486       XDrawRectangle(s->d, s->h, s->gc, gl_u[i]+40*j, gl_v[i]+40*k, 40, 40);
487       p = color_marks[9*i+j+3*k];
488       XSetForeground(s->d, s->gc, pixel[p]);
489       XFillRectangle(s->d, s->h, s->gc, gl_u[i]+40*j+1, gl_v[i]+40*k+1, 39, 39);
490     }
491     XSetForeground(s->d, s->gc, pixel[8]);
492     XDrawString(s->d, s->h, s->gc, gl_u[i]+58, gl_v[i]+64, faces+i, 1);
493     XSetForeground(s->d, s->gc, pixel[7]);
494     XDrawRectangle(s->d, s->h, s->gc, gl_u[i], gl_v[i], 120, 120);
495     XDrawRectangle(s->d, s->h, s->gc, gl_u[i]+1, gl_v[i]+1, 118, 118);
496 
497   }
498 
499   XSetForeground(s->d, s->gc, pixel[9]);
500   dx = XTextWidth(s->font, controlwin_str[3], strlen(controlwin_str[3]));
501   for (i=0; i<5; i++) {
502     XDrawString(s->d, s->h, s->gc, 30 + (dx+40)*i,
503       HELP_Y -  s->font->max_bounds.descent - 12, controlwin_str[i], strlen(controlwin_str[i]));
504     XDrawRectangle(s->d, s->h, s->gc, 20+(dx+40)*i, HELP_Y - dy - 14, dx+14, dy+6);
505     XDrawRectangle(s->d, s->h, s->gc, 19+(dx+40)*i, HELP_Y - dy - 15, dx+16, dy+8);
506   }
507 /*  XFlush(s->d); no xflush */
508 }
509 
510 /* return -1 if end, 0 otherwise */
screen_key(SCREEN * s,CUBE * c,XKeyEvent * ev)511 int screen_key(SCREEN *s, CUBE *c, XKeyEvent *ev)
512 {
513   char dum[50];
514   KeySym ks;
515   int n;
516 
517   XLookupString(ev, dum, 50, &ks, (XComposeStatus *)0);
518 
519   switch(ks) {
520     case XK_Escape :
521     case XK_q :
522     case XK_Q :
523       return -1;
524     case XK_a:
525     case XK_A:
526     case XK_b:
527     case XK_B:
528     case XK_c:
529     case XK_C:
530     case XK_d:
531     case XK_D:
532     case XK_e:
533     case XK_E:
534     case XK_f:
535     case XK_F:
536          n = -1;
537          if (!c->letters) {
538 	    c->letters = 1;
539             display_cube(c, s);
540             break;
541 	 }
542          if ((ks>=XK_a) && (ks<=XK_f)) {
543 	    c->rotated = n = ks-XK_a;
544             c->orient = 0;
545 	 }
546          if ((ks>=XK_A) && (ks<=XK_F)) {
547 	    c->rotated = n = ks-XK_A;
548             c->orient = 1;
549 	 }
550          if (n == -1) break;
551          c->letters = 1;
552          c->anim = -1;
553          c->time = get_time();
554          break;
555     case XK_l:
556     case XK_L:
557          c->letters = 1 - c->letters;
558          display_cube(c, s);
559          break;
560     case XK_exclam :
561          XMapRaised(s->d, s->h);
562          XClearWindow(s->d, s->h);
563          s->control = 2;
564          set_marks(c);
565          display_controls(s);
566          s->pause = 0;
567          goto do_pause;
568     case XK_asterisk :
569     case XK_KP_Multiply :
570          FORMAT = (FORMAT+1)%3;
571          show_text(s, 0);
572          break;
573     case XK_plus :
574     case XK_KP_Add :
575          n = (int) (10*PERSPECTIVE + 10.5);
576          ++n;
577          if (n>20) n = 20;
578          PERSPECTIVE = 0.1 * n - 1.0;
579          set_cube_size();
580          display_cube(c, s);
581          show_text(s, 1);
582          break;
583     case XK_minus :
584     case XK_KP_Subtract :
585          n = (int) (10*PERSPECTIVE + 10.5);
586          --n;
587          if (n<0) n = 0;
588          PERSPECTIVE = 0.1 * n - 1.0;
589          set_cube_size();
590          display_cube(c, s);
591          show_text(s, 1);
592          break;
593     case XK_h :
594     case XK_H :
595       XMapRaised(s->d, s->h);
596       XClearWindow(s->d, s->h);
597       s->pause = 0;
598       s->control = 0;
599       display_help(s);
600     case XK_space :
601     do_pause:
602       s->pause=1-s->pause;
603       if (s->pause) {
604 	grab_pointer(s, 0);
605       } else {
606 	grab_pointer(s, 1);
607       }
608       display_cube(c, s);
609       break;
610     case XK_i :
611     case XK_I :
612       init_cube(c, NULL, -1);
613       display_cube(c, s);
614       break;
615     case XK_p :
616     case XK_P :
617       init_cube(c, NULL, 1);
618       display_cube(c, s);
619       break;
620     case XK_r :
621     case XK_R :
622       reset_cube(c);
623       display_cube(c, s);
624       break;
625     case XK_s :
626     case XK_S :
627       save_cube(c);
628       show_text(s, 2);
629      break;
630     case XK_Left :
631       if (!s->pause)
632 	cube_generate_rotate(c, s, 0);
633       break;
634     case XK_Right :
635       if (!s->pause)
636 	cube_generate_rotate(c, s, 1);
637       break;
638   }
639 
640   return 0;
641 }
642 
place_mouse(SCREEN * s)643 void place_mouse(SCREEN *s)
644 {
645   if (!s->pause) {
646     XWarpPointer(s->d, None, s->w, 0, 0, 0, 0, SCREEN_X/2, SCREEN_Y/2);
647 /*    XFlush(s->d);  no xflush */
648   }
649 }
650 
mouse_pre_event(XMotionEvent * ev,int * a,int * b)651 void mouse_pre_event(XMotionEvent *ev, int *a, int *b)
652 {
653   if (ev->x==SCREEN_X/2 && ev->y==SCREEN_Y/2)
654     return;
655 
656   *a=ev->y-SCREEN_Y/2;
657   *b=ev->x-SCREEN_X/2;
658 }
659 
mouse_event(SCREEN * s,CUBE * c,int a,int b)660 void mouse_event(SCREEN *s, CUBE *c, int a, int b)
661 {
662 #define SPEED (1024.)
663   if (s->pause)
664     return;
665 
666   if (a==0 && b==0)
667     return;
668 
669   place_mouse(s);
670 
671   /* the x is y and vice-versa */
672   cube_rotate(s, c, a*M_PI/SPEED, b*M_PI/SPEED);
673   display_cube(c, s);
674 }
675 
676 /* danger: this returns the incorrect height with some window managers */
get_window_size(SCREEN * s,int * x,int * y)677 void get_window_size(SCREEN *s, int *x, int*y)
678 {
679   Window root, parent, *children;
680   int u, v;
681   unsigned int b, d;
682   unsigned int n;
683   XQueryTree(s->d, s->w, &root, &parent, &children, &n);
684 
685   if (!parent) {
686     fprintf(stderr, "Cannot query window tree!\n");
687     return;
688   }
689 
690   XGetGeometry(s->d, parent, &root, &u, &v,
691                (unsigned int *)x, (unsigned int *)y, &b, &d);
692 }
693 
evpred(Display * d,XEvent * ev,XPointer a)694 Bool evpred(Display *d, XEvent *ev, XPointer a)
695 {
696   return (True);
697 }
698 
confpred(Display * d,XEvent * ev,XPointer a)699 Bool confpred(Display *d, XEvent *ev, XPointer a)
700 {
701   if (ev->type==ConfigureNotify)
702     return (True);
703   else
704     return (False);
705 }
706 
707 /* Discard all events but last expose event */
purge_events(Display * dpy)708 void purge_events(Display *dpy)
709 {
710   XEvent ev, evp;
711   evp.type = 0;
712   while (XPending(dpy)) {
713     XNextEvent(dpy, &ev);
714     if (ev.type==Expose) evp = ev;
715   }
716   if (evp.type==Expose) XPutBackEvent(dpy, &evp);
717 }
718 
719 /* return -1 if end, 0 if ok */
screen_event(SCREEN * s,CUBE * c)720 int screen_event(SCREEN *s, CUBE *c)
721 {
722 #define EXPOSE 1
723 #define MOTION 2
724   int win_expose = 0; /* 1: w win, 2: h win: 3: both */
725   extern device d;
726   int type=0;
727   int i, j, k, a=0, b=0, dx, dy;
728   static int new_x, new_y, notify = 0;
729 
730   XEvent ev;
731 
732   dy = s->font->max_bounds.ascent + s->font->max_bounds.descent + 2;
733 
734   while(XCheckIfEvent(s->d, &ev, evpred, (XPointer)0)) {
735     switch(ev.type) {
736       case ClientMessage :
737         if (ev.xclient.message_type == wm_protocols &&
738           ev.xclient.format == 32 &&
739           ev.xclient.data.l[0] == wm_delete_window) {
740           if (ev.xclient.window == s->w) {
741             close_screen(s);
742             exit(0);
743           }
744           if (ev.xclient.window == s->h) {
745             XUnmapWindow(s->d, s->h);
746 	    s->control = 0;
747           }
748 	}
749         break;
750       case Expose :
751 	type|=EXPOSE;
752         if (ev.xexpose.window == s->w) win_expose |= 1;
753         if (ev.xexpose.window == s->h) win_expose |= 2;
754 	break;
755       case KeyPress :
756 	if (screen_key(s, c, &ev.xkey)==-1)
757 	  return -1;
758 	break;
759       case ButtonRelease :
760         if (ev.xbutton.window==s->w) {
761 	  if (!s->pause) break;
762 	  dx = XTextWidth(s->font, help1, strlen(help1));
763 	  if (ev.xbutton.x>=6 && ev.xbutton.x<=dx+20 &&
764 	      ev.xbutton.y>=6 && ev.xbutton.y<=dy+12) {
765             s->pause=0;
766 	    grab_pointer(s, 1);
767             display_cube(c, s);
768             break;
769 	  }
770 	  dx = XTextWidth(s->font, help2, strlen(help2));
771 	  if (ev.xbutton.x>=6 && ev.xbutton.x<=dx+20 &&
772 	      ev.xbutton.y>=SCREEN_Y - dy - 14 &&
773 	      ev.xbutton.y<=SCREEN_Y - 8) {
774             XMapRaised(s->d, s->h);
775             XClearWindow(s->d, s->h);
776             s->control = 0;
777             display_help(s);
778 	    break;;
779 	  }
780 	  dx = XTextWidth(s->font, help3, strlen(help3));
781 	  if (ev.xbutton.x>=SCREEN_X - dx - 22  && ev.xbutton.x<=SCREEN_X - 8 &&
782 	      ev.xbutton.y>=SCREEN_Y - dy - 14 && ev.xbutton.y<=SCREEN_Y - 8) {
783             XMapRaised(s->d, s->h);
784             XClearWindow(s->d, s->h);
785             set_marks(c);
786             s->control = 2;
787             display_controls(s);
788             break;
789 	  }
790 	  break;
791 	}
792         if (ev.xbutton.window==s->h) {
793 	  dx = XTextWidth(s->font, control_str, strlen(control_str));
794 	  if (s->control == 0 &&
795 	      ev.xbutton.x>=6 && ev.xbutton.x<=dx+20 &&
796 	      ev.xbutton.y>=HELP_Y - dy - 14 && ev.xbutton.y<=HELP_Y - 8) {
797 	    s->control = 2;
798 	    XClearWindow(s->d, s->h);
799 	    set_marks(c);
800 	    display_controls(s);
801 	    break;
802 	  }
803 	  dx = XTextWidth(s->font, close_str, strlen(close_str));
804 	  if (s->control == 0 &&
805 	      ev.xbutton.x>=HELP_X - dx - 22 && ev.xbutton.x<=HELP_X - 8 &&
806 	      ev.xbutton.y>=HELP_Y - dy - 14 && ev.xbutton.y<=HELP_Y - 8) {
807 	    XUnmapWindow(s->d, s->h);
808 	  }
809 	  if (s->control == 0) break;
810 	  j = -1;
811 	  dx = XTextWidth(s->font, controlwin_str[3], strlen(controlwin_str[3]));
812           for (i=0; i<5; i++)
813 	    if (ev.xbutton.x>=20+(dx+40)*i && ev.xbutton.x<=-6+(dx+40)*(i+1) &&
814 	        ev.xbutton.y>=HELP_Y - dy - 14 && ev.xbutton.y<=HELP_Y - 8) {
815 	      j = i;
816 	      break;
817 	    }
818 	  if (j>=0)
819 	  switch(j) {
820 	    case 0: s->control = 1;
821 	            memset(color_marks, 0, 54*sizeof(short));
822 	            goto skip;
823 	    case 1: s->control = 2;
824 	            get_marks(c);
825 	            display_cube(c, s);
826 	            goto endcontrol;
827 	    case 2: s->control = 2;
828 	            init_cube(c, NULL, -1);
829 	            display_cube(c, s);
830 	            goto skip;
831 	    case 3: s->control = 2;
832 	            init_cube(c, NULL, 1);
833 	            display_cube(c, s);
834 	            goto skip;
835 	    case 4: s->control = 0;
836 	            XUnmapWindow(s->d, s->h);
837 	            goto endcontrol;
838 	  }
839 	  for (i=0; i<6; i++) {
840 	    if (ev.xbutton.x>=30+60*i && ev.xbutton.x<=70+60*i &&
841 		ev.xbutton.y>=20 && ev.xbutton.y<=60) {
842 	      mark = i+1;
843 	      goto skip;
844 	    }
845 	  }
846           for (i=0; i<6; i++) {
847             for (j=0; j<3; j++)
848             for (k=0; k<3; k++) {
849 	      if (ev.xbutton.x>=gl_u[i]+40*j && ev.xbutton.x<=gl_u[i]+40+40*j &&
850 	          ev.xbutton.y>=gl_v[i]+40*k && ev.xbutton.y<=gl_v[i]+40+40*k) {
851 		 color_marks[9*i+j+3*k] = mark;
852 		if (s->control==2) s->control = 1;
853 		goto skip;
854 	      }
855 	    }
856 	  }
857         skip:
858 	  display_controls(s);
859 	  if (!s->control) break;
860 	}
861         endcontrol:
862         break;
863       case ButtonPress :
864         if (ev.xbutton.window != s->w)
865 	  break;
866         if (c->letters) {
867           c->letters = 0;
868           display_cube(c, s);
869           break;
870         }
871 	if (ev.xbutton.button==Button1) {
872 	  cube_generate_rotate(c, s, 0);
873 	} else if (ev.xbutton.button==Button2) {
874           s->pause=1;
875 	  grab_pointer(s, 0);
876           display_cube(c, s);
877 	} else if (ev.xbutton.button==Button3) {
878 	  cube_generate_rotate(c, s, 1);
879 	}
880 	break;
881       case MotionNotify :
882         if (ev.xmotion.window != s->w)
883 	  break;
884 	mouse_pre_event(&ev.xmotion, &a, &b);
885 	type |= MOTION;
886 	break;
887       case PropertyNotify:
888       case ConfigureNotify:
889 	new_x = ev.xconfigure.width;
890 	new_y = ev.xconfigure.height;
891 	if (new_x!=SCREEN_X || new_y!=SCREEN_Y)
892 	  notify = 1;
893       break;
894 #if 0
895       case FocusOut :
896         if (!s->pause)
897 	  place_mouse(s);
898       break;
899 #endif
900     }
901   }
902 
903   if (notify) {
904 /*    XFlush(s->d); no xflush */
905 /*    usleep(200000); no usleep */
906     if (new_x!=SCREEN_X || new_y!=SCREEN_Y ||
907 	XCheckIfEvent(s->d, &ev, confpred, (XPointer)0)) {
908       if (new_x<80) new_x = 80;
909       if (new_y<60) new_y = 60;
910       XResizeWindow(s->d, s->w, new_x, new_y);
911       XClearArea(s->d, s->w, 0, 0, 1, 1, True);
912       SCREEN_X = new_x;
913       SCREEN_Y = new_y;
914       reset_data_buffers(s);
915       d.buffer=(unsigned char *)s->buffer;
916       set_cube_size();
917     } else {
918       display_cube(c, s);
919 /*      XFlush(s->d); no xflush */
920       put_screen(c, s);
921       notify = 0;
922     }
923     purge_events(s->d);
924     /* just in case purge_events removed some xexpose for the other window */
925     if (s->control) display_controls(s);
926     if (!s->control) display_help(s);
927     return 0;
928   }
929 
930   if (type & EXPOSE) {
931     /*if (ev.xexpose.window == s->w) */
932     if (win_expose & 1)
933       put_screen(c, s);
934     /* if (ev.xexpose.window == s->h) { */
935     if (win_expose & 2) {
936       if (s->control) display_controls(s);
937       if (!s->control) display_help(s);
938     }
939   }
940 
941   if (type & MOTION) {
942     i = s->control;
943     s->control = 0;
944     mouse_event(s, c, a, b);
945     s->control = i;
946   }
947 
948   return 0;
949 }
950 
close_screen(SCREEN * s)951 void close_screen(SCREEN *s)
952 {
953   XDestroyWindow(s->d, s->w);
954   XFreeGC(s->d, s->gc);
955   if (s->depth==8)
956     XFreeColormap(s->d, s->cm);
957   XCloseDisplay(s->d);
958 }
959 
clear_screen(SCREEN * s)960 void clear_screen(SCREEN *s)
961 {
962   /* fill s->im with pixel[10], avoid XPutPixel which is rather slow... */
963   int i, w, dw;
964   w = (SCREEN_X+7)/8;
965   dw = s->depth/8;
966   XPutPixel(s->im, 0, 0, pixel[10]);
967   for (i=1; i<8*w; i++)
968      memcpy(s->buffer + i*dw, s->buffer, dw);
969   w = w * s->depth;
970   for (i=1; i<SCREEN_Y; i++)
971      memcpy(s->buffer + i*w, s->buffer, w);
972 }
973 
put_screen(CUBE * c,SCREEN * s)974 void put_screen(CUBE *c, SCREEN *s)
975 {
976   int dx, dy;
977   XPutImage(s->d, s->w, s->gc, s->im, 0, 0, 0, 0, SCREEN_X, SCREEN_Y);
978   if (s->pause) {
979     XSetForeground(s->d, s->gc, pixel[9]);
980     dy = s->font->max_bounds.ascent + s->font->max_bounds.descent + 2;
981 
982     dx = XTextWidth(s->font, help1, strlen(help1));
983     XDrawString(s->d, s->w, s->gc, 13,
984 		s->font->max_bounds.ascent + 10, help1, strlen(help1));
985     XDrawRectangle(s->d, s->w, s->gc, 6, 6, dx+14, dy+6);
986     XDrawRectangle(s->d, s->w, s->gc, 5, 5, dx+16, dy+8);
987 
988     dx = XTextWidth(s->font, help2, strlen(help2));
989     XDrawString(s->d, s->w, s->gc, 13,
990 		SCREEN_Y - s->font->max_bounds.descent - 12,
991 		help2, strlen(help2));
992     XDrawRectangle(s->d, s->w, s->gc, 6,
993 		   SCREEN_Y - dy - 14, dx+14, dy+6);
994     XDrawRectangle(s->d, s->w, s->gc, 5,
995 		   SCREEN_Y - dy - 15, dx+16, dy+8);
996 
997     dx = XTextWidth(s->font, help3, strlen(help3));
998     XDrawString(s->d, s->w, s->gc, SCREEN_X - dx - 15,
999 		SCREEN_Y - s->font->max_bounds.descent - 12,
1000 		help3, strlen(help3));
1001     XDrawRectangle(s->d, s->w, s->gc, SCREEN_X - dx - 22,
1002 		   SCREEN_Y - dy - 14, dx+14, dy+6);
1003     XDrawRectangle(s->d, s->w, s->gc, SCREEN_X - dx - 23,
1004 		   SCREEN_Y - dy - 15, dx+16, dy+8);
1005     XFlush(s->d);
1006   }
1007   if (s->control == 2 && c->anim==0) {
1008     set_marks(c);
1009     display_controls(s);
1010 /*    XFlush(s->d);     no xflush */
1011   }
1012   /* XFlush(s->d); */
1013 }
1014 
1015 /* return ms */
get_time(void)1016 struct timeval get_time(void)
1017 {
1018   struct timeval t;
1019   struct timezone d;
1020   if (gettimeofday(&t, &d)==-1) {
1021     perror("gettimeofday");
1022     return t;
1023   }
1024   return t;
1025 }
1026