make flames/julia/neko demos use XCB_EVENT_RESPONSE_TYPE to determine event type
[free-sw/xcb/demo] / tests / flames.c
1 /*****************************************************************************/
2 /*                                  XCB Flame                                */
3 /*****************************************************************************/
4 /* Originally By:                                                            */
5 /*     The Rasterman (Carsten Haitzler)                                      */
6 /*      Copyright (C) 1996                                                   */
7 /*****************************************************************************/
8 /* XcB port:                                                                 */
9 /*     d'Oursse (Vincent TORRI), 2006                                        */
10 /*****************************************************************************/
11 /* This code is Freeware. You may copy it, modify it or do with it as you    */
12 /* please, but you may not claim copyright on any code wholly or partly      */
13 /* based on this code. I accept no responisbility for any consequences of    */
14 /* using this code, be they proper or otherwise.                             */
15 /*****************************************************************************/
16 /* Okay, now all the legal mumbo-jumbo is out of the way, I will just say    */
17 /* this: enjoy this program, do with it as you please and watch out for more */
18 /* code releases from The Rasterman running under X... the only way to code. */
19 /*****************************************************************************/
20
21 /* standard library */
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <stdbool.h>
25 #include <time.h>
26 #include <assert.h>
27 #include <string.h>
28 #include <unistd.h>
29
30 #include <xcb/xcb.h>
31 #include <xcb/shm.h>
32 #include <xcb/xcb_aux.h>
33 #include <xcb/xcb_image.h>
34
35 /* Needed for xcb_set_wm_protocols() */
36 #include <xcb/xcb_icccm.h>
37
38 #define XCB_ALL_PLANES ~0
39 #include <xcb/xcb_icccm.h>
40
41 /* some defines for the flame */
42 #define HSPREAD 26
43 #define VSPREAD 78
44 #define VARIANCE 5
45 #define VARTREND 2
46 #define RESIDUAL 68
47
48 /* default width and height of the window */
49 #define BG_W 256
50 #define BG_H 256
51
52 #define IMAX 300
53
54 typedef struct
55 {
56   struct
57   {
58     xcb_connection_t *c;
59     xcb_drawable_t    draw;
60     xcb_drawable_t    pixmap;
61     xcb_colormap_t    cmap;
62     uint8_t          depth;
63     xcb_gcontext_t    gc;
64   }xcb;
65   
66   /* the palette */
67   unsigned int  pal[IMAX];
68   unsigned int *im;
69   int           ims;
70   
71   /* the flame arrays */
72   int           ws;
73   unsigned int *flame;
74   unsigned int *flame2;
75   
76 }flame;
77
78 static xcb_atom_t get_atom (xcb_connection_t *connection, const char *atomName);
79 static void title_set (flame *f, const char *title);
80 static int  ilog2 (unsigned int n);
81 static void flame_set_palette (flame *f);
82 static void flame_set_flame_zero (flame *f);
83 static void flame_set_random_flame_base (flame *f);
84 static void flame_modify_flame_base (flame *f);
85 static void flame_process_flame (flame *f);
86 static void flame_draw_flame (flame *f);
87
88 xcb_atom_t
89 get_atom (xcb_connection_t *connection, const char *atomName)
90 {
91   if (atomName == NULL)
92     return XCB_NONE;
93   xcb_atom_t atom = XCB_NONE;
94   xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(connection,
95         xcb_intern_atom(connection, 0, strlen(atomName), atomName), NULL);
96   if (reply)
97     {
98       atom = reply->atom;
99       free(reply);
100     }
101   return atom;
102 }
103
104 flame *
105 flame_init ()
106 {
107   flame       *f;
108   xcb_screen_t   *screen;
109   xcb_gcontext_t  gc = { 0 };
110   int          screen_nbr;
111   uint32_t       mask;
112   uint32_t       values[2];
113   int          size;
114   int          flame_width;
115   int          flame_height;
116   xcb_rectangle_t rect_coord = { 0, 0, BG_W, BG_H};
117
118   f = (flame *)malloc (sizeof (flame));
119   if (!f)
120     return NULL;
121
122   f->xcb.c = xcb_connect (NULL, &screen_nbr);
123   if (!f->xcb.c)
124     {
125       free (f);
126       return NULL;
127     }
128   screen = xcb_aux_get_screen (f->xcb.c, screen_nbr);
129
130   f->xcb.draw = screen->root;
131   f->xcb.gc = xcb_generate_id (f->xcb.c);
132   mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES;
133   values[0] = screen->black_pixel;
134   values[1] = 0; /* no graphics exposures */
135   xcb_create_gc (f->xcb.c, f->xcb.gc, f->xcb.draw, mask, values);
136
137   gc = xcb_generate_id (f->xcb.c);
138   mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES;
139   values[0] = screen->white_pixel;
140   values[1] = 0; /* no graphics exposures */
141   xcb_create_gc (f->xcb.c, gc, f->xcb.draw, mask, values);
142
143   f->xcb.depth = xcb_aux_get_depth (f->xcb.c, screen);
144   mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
145   values[0] = screen->white_pixel;
146   values[1] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS;
147   f->xcb.draw = xcb_generate_id (f->xcb.c);
148   xcb_create_window (f->xcb.c, f->xcb.depth,
149                    f->xcb.draw,
150                    screen->root,
151                    0, 0, BG_W, BG_H,
152                    0,
153                    XCB_WINDOW_CLASS_INPUT_OUTPUT,
154                    screen->root_visual,
155                    mask, values);
156   title_set (f, "XCB Flames");
157   
158   f->xcb.pixmap = xcb_generate_id (f->xcb.c);
159   xcb_create_pixmap (f->xcb.c, f->xcb.depth,
160                    f->xcb.pixmap, f->xcb.draw,
161                    BG_W, BG_H);
162   xcb_poly_fill_rectangle(f->xcb.c, f->xcb.pixmap, gc, 1, &rect_coord);
163
164   xcb_map_window (f->xcb.c, f->xcb.draw);
165   xcb_flush (f->xcb.c);
166
167   f->xcb.cmap = xcb_generate_id (f->xcb.c);
168   xcb_create_colormap (f->xcb.c,
169                      XCB_COLORMAP_ALLOC_NONE,
170                      f->xcb.cmap,
171                      f->xcb.draw,
172                      screen->root_visual);
173
174   /* Allocation of the flame arrays */
175   flame_width  = BG_W >> 1;
176   flame_height = BG_H >> 1;
177   f->ws = ilog2 (flame_width);
178   size = (1 << f->ws) * flame_height * sizeof (unsigned int);
179   f->flame = (unsigned int *)malloc (size);
180   if (! f->flame)
181     {
182       xcb_disconnect (f->xcb.c);
183       free (f);
184       return NULL;
185     }
186   f->flame2 = (unsigned int *)malloc (size);
187   if (! f->flame2)
188     {
189       free (f->flame);
190       xcb_disconnect (f->xcb.c);
191       free (f);
192       return NULL;
193     }
194
195   /* allocation of the image */
196   f->ims = ilog2 (BG_W);
197
198   /* initialization of the palette */
199   flame_set_palette (f);
200
201   return f;
202 }
203
204 void
205 flame_shutdown (flame *f)
206 {
207   if (!f)
208     return;
209
210   free (f->flame2);
211   free (f->flame);
212   xcb_disconnect (f->xcb.c);
213   free (f);
214 }
215
216 int
217 main ()
218 {
219   flame *f;
220   xcb_generic_event_t *e;
221   xcb_gcontext_t gc = { 0 };
222
223   f = flame_init ();
224   if (!f)
225     {
226       printf ("Can't initialize global data\nExiting...\n");
227       return -1;
228     }
229
230   flame_set_flame_zero (f);
231   flame_set_random_flame_base (f);
232
233   xcb_atom_t deleteWindowAtom = get_atom(f->xcb.c, "WM_DELETE_WINDOW");
234   xcb_atom_t wmprotocolsAtom = get_atom(f->xcb.c, "WM_PROTOCOLS");
235   /* Listen to X client messages in order to be able to pickup
236      the "delete window" message that is generated for example
237      when someone clicks the top-right X button within the window
238      manager decoration (or when user hits ALT-F4). */
239   xcb_set_wm_protocols (f->xcb.c, wmprotocolsAtom, f->xcb.draw, 1, &deleteWindowAtom);
240
241   bool finished = false;
242   while (!finished)
243     {
244       if ((e = xcb_poll_for_event (f->xcb.c)))
245         {
246           switch (XCB_EVENT_RESPONSE_TYPE(e))
247             {
248             case XCB_EXPOSE:
249               xcb_copy_area(f->xcb.c, f->xcb.pixmap, f->xcb.draw, gc,
250                           0, 0, 0, 0, BG_W, BG_H);
251               xcb_flush (f->xcb.c);
252               break;
253             case XCB_CLIENT_MESSAGE:
254               if (((xcb_client_message_event_t *)e)->data.data32[0] == deleteWindowAtom)
255                 {
256                   finished = true;
257                 }
258               break;
259             case XCB_BUTTON_PRESS:
260               finished = true;
261               break;
262             }
263           free (e);
264         }
265       flame_draw_flame (f);
266       xcb_flush (f->xcb.c);
267     }
268
269   flame_shutdown (f);
270
271   return 0;
272 }
273
274 static void title_set (flame *f, const char *title)
275 {
276   xcb_intern_atom_reply_t *rep;
277   xcb_atom_t           encoding;
278   char             *atom_name;
279
280   /* encoding */
281   atom_name = "UTF8_STRING";
282   rep = xcb_intern_atom_reply (f->xcb.c,
283                             xcb_intern_atom (f->xcb.c,
284                                            0,
285                                            strlen (atom_name),
286                                            atom_name),
287                             NULL);
288   encoding = rep->atom;
289   free (rep);
290
291   /* ICCCM */
292 /*   SetWMName (f->xcb.c, f->xcb.draw.window, encoding, strlen (title), title); */
293
294   /* NETWM */
295   atom_name = "_NET_WM_NAME";
296   rep = xcb_intern_atom_reply (f->xcb.c,
297                             xcb_intern_atom (f->xcb.c,
298                                            0,
299                                            strlen (atom_name),
300                                            atom_name),
301                             NULL);
302   xcb_change_property(f->xcb.c, XCB_PROP_MODE_REPLACE,
303                     f->xcb.draw,
304                     rep->atom, encoding, 8, strlen (title), title);
305   free (rep);
306 }
307
308 static void
309 flame_draw_flame (flame *f)
310 {
311   xcb_image_t     *image;
312   unsigned int *ptr;
313   int           x;
314   int           y;
315   int           xx;
316   int           yy;
317   int           cl;
318   int           cl1;
319   int           cl2;
320   int           cl3;
321   int           cl4;
322
323   image = xcb_image_get (f->xcb.c, f->xcb.draw,
324                        0, 0, BG_W, BG_H,
325                        XCB_ALL_PLANES, XCB_IMAGE_FORMAT_Z_PIXMAP);
326   /* If the top-level window is minimized (iconic) then the xcb_image_get()
327      will return NULL. In this case, we'll skip both updating and drawing
328      the flame, and we will also do a small sleep so that the program doesn't
329      hog as much CPU while minimized. 
330    
331      Another (non-polling == cleaner) way to not hog the CPU while minimized 
332      would be to pass the XCB_EVENT_MASK_STRUCTURE_NOTIFY flag to
333      xcb_create_window(). This will give you XCB_UNMAP_NOTIFY and
334      XCB_MAP_NOTIFY events whenever the window is "minimized" (made iconic)
335      and "unminimized" (made normal again). This information would then be
336      used to make the main loop use the xcb_wait_for_event() instead of
337      xcb_poll_for_event() while the window is minimized (iconic).     */
338   if (image == NULL)
339     {
340       usleep (100000);
341       return;
342     }
343   f->im = (unsigned int *)image->data;
344
345   /* modify the base of the flame */
346   flame_modify_flame_base (f);
347   /* process the flame array, propagating the flames up the array */
348   flame_process_flame (f);
349
350   for (y = 0 ; y < ((BG_H >> 1) - 1) ; y++)
351     {
352       for (x = 0 ; x < ((BG_W >> 1) - 1) ; x++)
353         {
354           xx = x << 1;
355           yy = y << 1;
356
357           ptr = f->flame2 + (y << f->ws) + x;
358           cl1 = cl = (int)*ptr;
359           ptr = f->flame2 + (y << f->ws) + x + 1;
360           cl2 = (int)*ptr;
361           ptr = f->flame2 + ((y + 1) << f->ws) + x + 1;
362           cl3 = (int)*ptr;
363           ptr = f->flame2 + ((y + 1) << f->ws) + x;
364           cl4 = (int)*ptr;
365
366           
367           xcb_image_put_pixel (image,
368                             xx, yy,
369                             f->pal[cl]);
370           xcb_image_put_pixel (image,
371                             xx + 1, yy,
372                             f->pal[((cl1+cl2) >> 1)]);
373           xcb_image_put_pixel (image,
374                             xx, yy + 1,
375                             f->pal[((cl1 + cl3) >> 1)]);
376           xcb_image_put_pixel (image,
377                             xx + 1, yy + 1,
378                             f->pal[((cl1 + cl4) >> 1)]);
379         }
380     }
381   xcb_image_put (f->xcb.c, f->xcb.draw, f->xcb.gc, image,
382                0, 0, 0);
383   xcb_image_destroy (image);
384 }
385
386 /* set the flame palette */
387 static void
388 flame_set_palette (flame *f)
389 {
390   xcb_alloc_color_cookie_t cookies[IMAX];
391   xcb_alloc_color_reply_t *rep;
392   int               i;
393   int               r;
394   int               g;
395   int               b;
396
397   for (i = 0 ; i < IMAX ; i++)
398     {
399       r = i * 3;
400       g = (i - 80) * 3;
401       b = (i - 160) * 3;
402
403       if (r < 0)   r = 0;
404       if (r > 255) r = 255;
405       if (g < 0)   g = 0;
406       if (g > 255) g = 255;
407       if (b < 0)   b = 0;
408       if (b > 255) b = 255;
409
410       cookies[i] = xcb_alloc_color (f->xcb.c, f->xcb.cmap,
411                                   r << 8, g << 8, b << 8);
412     }
413
414   for (i = 0 ; i < IMAX ; i++)
415     {
416       rep = xcb_alloc_color_reply (f->xcb.c, cookies[i], NULL);
417       f->pal[i] = rep->pixel;
418       free (rep);
419     }
420 }
421
422 /* set the flame array to zero */
423 static void
424 flame_set_flame_zero (flame *f)
425 {
426   int           x;
427   int           y;
428   unsigned int *ptr;
429
430   for (y = 0 ; y < (BG_H >> 1) ; y++)
431     {
432       for (x = 0 ; x < (BG_W >> 1) ; x++)
433         {
434           ptr = f->flame + (y << f->ws) + x;
435           *ptr = 0;
436         }
437     }
438 }
439
440 static void
441 flame_set_random_flame_base (flame *f)
442 {
443   int           x;
444   int           y;
445   unsigned int *ptr;
446   
447   /* initialize a random number seed from the time, so we get random */
448   /* numbers each time */
449   srand (time(NULL));
450   y = (BG_H >> 1) - 1;
451   for (x = 0 ; x < (BG_W >> 1) ; x++)
452     {
453       ptr = f->flame + (y << f->ws) + x;
454       *ptr = rand ()%IMAX;
455     }
456 }
457
458 /* modify the base of the flame with random values */
459 static void
460 flame_modify_flame_base (flame *f)
461 {
462   int           x;
463   int           y;
464   unsigned int *ptr;
465   int           val;
466   
467   y = (BG_H >> 1) - 1;
468   for (x = 0 ; x < (BG_W >> 1) ; x++)
469     {
470       ptr = f->flame + (y << f->ws) + x;
471       *ptr += ((rand ()%VARIANCE) - VARTREND);
472       val = *ptr;
473       if (val > IMAX) *ptr = 0;
474       if (val < 0)    *ptr = 0;
475     }
476 }
477
478 /* process entire flame array */
479 static void
480 flame_process_flame (flame *f)
481 {
482   int           x;
483   int           y;
484   unsigned int *ptr;
485   unsigned int *p;
486   unsigned int  val;
487   unsigned int  tmp;
488   
489   for (y = ((BG_H >> 1) - 1) ; y >= 2 ; y--)
490     {
491       for (x = 1 ; x < ((BG_W >> 1) - 1) ; x++)
492         {
493           ptr = f->flame + (y << f->ws) + x;
494           val = (unsigned int)*ptr;
495           if (val > IMAX)
496             *ptr = (unsigned int)IMAX;
497           val = (unsigned int)*ptr;
498           if (val > 0)
499             {
500               tmp = (val * VSPREAD) >> 8;
501               p   = ptr - (2 << f->ws);
502               *p  = *p + (tmp >> 1);
503               p   = ptr - (1 << f->ws);
504               *p  = *p + tmp;
505               tmp = (val * HSPREAD) >> 8;
506               p   = ptr - (1 << f->ws) - 1;
507               *p  = *p + tmp;
508               p   = ptr - (1 << f->ws) + 1;
509               *p  = *p + tmp;
510               p   = ptr - 1;
511               *p  = *p + (tmp >>1 );
512               p   = ptr + 1;
513               *p  = *p + (tmp >> 1);
514               p   = f->flame2 + (y << f->ws) + x;
515               *p  = val;
516               if (y < ((BG_H >> 1) - 1))
517                 *ptr = (val * RESIDUAL) >> 8;
518             }
519         }
520     }
521 }
522
523 static int
524 ilog2 (unsigned int n)
525 {
526   int p = -1;
527
528   assert(n > 0);
529   while (n > 0) {
530     p++;
531     n >>= 1;
532   }
533
534   return p;
535 }