avoid exit on startup for demo "julia" and handle EWMH delete window. See bug https...
[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   /* Listen to X client messages in order to be able to pickup
235      the "delete window" message that is generated for example
236      when someone clicks the top-right X button within the window
237      manager decoration (or when user hits ALT-F4). */
238   xcb_set_wm_protocols (f->xcb.c, f->xcb.draw, 1, &deleteWindowAtom);
239
240   bool finished = false;
241   while (!finished)
242     {
243       if ((e = xcb_poll_for_event (f->xcb.c)))
244         {
245           switch (e->response_type & 0x7f)
246             {
247             case XCB_EXPOSE:
248               xcb_copy_area(f->xcb.c, f->xcb.pixmap, f->xcb.draw, gc,
249                           0, 0, 0, 0, BG_W, BG_H);
250               xcb_flush (f->xcb.c);
251               break;
252             case XCB_CLIENT_MESSAGE:
253               if (((xcb_client_message_event_t *)e)->data.data32[0] == deleteWindowAtom)
254                 {
255                   finished = true;
256                 }
257               break;
258             case XCB_BUTTON_PRESS:
259               finished = true;
260               break;
261             }
262           free (e);
263         }
264       flame_draw_flame (f);
265       xcb_flush (f->xcb.c);
266     }
267
268   flame_shutdown (f);
269
270   return 0;
271 }
272
273 static void title_set (flame *f, const char *title)
274 {
275   xcb_intern_atom_reply_t *rep;
276   xcb_atom_t           encoding;
277   char             *atom_name;
278
279   /* encoding */
280   atom_name = "UTF8_STRING";
281   rep = xcb_intern_atom_reply (f->xcb.c,
282                             xcb_intern_atom (f->xcb.c,
283                                            0,
284                                            strlen (atom_name),
285                                            atom_name),
286                             NULL);
287   encoding = rep->atom;
288   free (rep);
289
290   /* ICCCM */
291 /*   SetWMName (f->xcb.c, f->xcb.draw.window, encoding, strlen (title), title); */
292
293   /* NETWM */
294   atom_name = "_NET_WM_NAME";
295   rep = xcb_intern_atom_reply (f->xcb.c,
296                             xcb_intern_atom (f->xcb.c,
297                                            0,
298                                            strlen (atom_name),
299                                            atom_name),
300                             NULL);
301   xcb_change_property(f->xcb.c, XCB_PROP_MODE_REPLACE,
302                     f->xcb.draw,
303                     rep->atom, encoding, 8, strlen (title), title);
304   free (rep);
305 }
306
307 static void
308 flame_draw_flame (flame *f)
309 {
310   xcb_image_t     *image;
311   unsigned int *ptr;
312   int           x;
313   int           y;
314   int           xx;
315   int           yy;
316   int           cl;
317   int           cl1;
318   int           cl2;
319   int           cl3;
320   int           cl4;
321
322   image = xcb_image_get (f->xcb.c, f->xcb.draw,
323                        0, 0, BG_W, BG_H,
324                        XCB_ALL_PLANES, XCB_IMAGE_FORMAT_Z_PIXMAP);
325   /* If the top-level window is minimized (iconic) then the xcb_image_get()
326      will return NULL. In this case, we'll skip both updating and drawing
327      the flame, and we will also do a small sleep so that the program doesn't
328      hog as much CPU while minimized. 
329    
330      Another (non-polling == cleaner) way to not hog the CPU while minimized 
331      would be to pass the XCB_EVENT_MASK_STRUCTURE_NOTIFY flag to
332      xcb_create_window(). This will give you XCB_UNMAP_NOTIFY and
333      XCB_MAP_NOTIFY events whenever the window is "minimized" (made iconic)
334      and "unminimized" (made normal again). This information would then be
335      used to make the main loop use the xcb_wait_for_event() instead of
336      xcb_poll_for_event() while the window is minimized (iconic).     */
337   if (image == NULL)
338     {
339       usleep (100000);
340       return;
341     }
342   f->im = (unsigned int *)image->data;
343
344   /* modify the base of the flame */
345   flame_modify_flame_base (f);
346   /* process the flame array, propagating the flames up the array */
347   flame_process_flame (f);
348
349   for (y = 0 ; y < ((BG_H >> 1) - 1) ; y++)
350     {
351       for (x = 0 ; x < ((BG_W >> 1) - 1) ; x++)
352         {
353           xx = x << 1;
354           yy = y << 1;
355
356           ptr = f->flame2 + (y << f->ws) + x;
357           cl1 = cl = (int)*ptr;
358           ptr = f->flame2 + (y << f->ws) + x + 1;
359           cl2 = (int)*ptr;
360           ptr = f->flame2 + ((y + 1) << f->ws) + x + 1;
361           cl3 = (int)*ptr;
362           ptr = f->flame2 + ((y + 1) << f->ws) + x;
363           cl4 = (int)*ptr;
364
365           
366           xcb_image_put_pixel (image,
367                             xx, yy,
368                             f->pal[cl]);
369           xcb_image_put_pixel (image,
370                             xx + 1, yy,
371                             f->pal[((cl1+cl2) >> 1)]);
372           xcb_image_put_pixel (image,
373                             xx, yy + 1,
374                             f->pal[((cl1 + cl3) >> 1)]);
375           xcb_image_put_pixel (image,
376                             xx + 1, yy + 1,
377                             f->pal[((cl1 + cl4) >> 1)]);
378         }
379     }
380   xcb_image_put (f->xcb.c, f->xcb.draw, f->xcb.gc, image,
381                0, 0, 0);
382   xcb_image_destroy (image);
383 }
384
385 /* set the flame palette */
386 static void
387 flame_set_palette (flame *f)
388 {
389   xcb_alloc_color_cookie_t cookies[IMAX];
390   xcb_alloc_color_reply_t *rep;
391   int               i;
392   int               r;
393   int               g;
394   int               b;
395
396   for (i = 0 ; i < IMAX ; i++)
397     {
398       r = i * 3;
399       g = (i - 80) * 3;
400       b = (i - 160) * 3;
401
402       if (r < 0)   r = 0;
403       if (r > 255) r = 255;
404       if (g < 0)   g = 0;
405       if (g > 255) g = 255;
406       if (b < 0)   b = 0;
407       if (b > 255) b = 255;
408
409       cookies[i] = xcb_alloc_color (f->xcb.c, f->xcb.cmap,
410                                   r << 8, g << 8, b << 8);
411     }
412
413   for (i = 0 ; i < IMAX ; i++)
414     {
415       rep = xcb_alloc_color_reply (f->xcb.c, cookies[i], NULL);
416       f->pal[i] = rep->pixel;
417       free (rep);
418     }
419 }
420
421 /* set the flame array to zero */
422 static void
423 flame_set_flame_zero (flame *f)
424 {
425   int           x;
426   int           y;
427   unsigned int *ptr;
428
429   for (y = 0 ; y < (BG_H >> 1) ; y++)
430     {
431       for (x = 0 ; x < (BG_W >> 1) ; x++)
432         {
433           ptr = f->flame + (y << f->ws) + x;
434           *ptr = 0;
435         }
436     }
437 }
438
439 static void
440 flame_set_random_flame_base (flame *f)
441 {
442   int           x;
443   int           y;
444   unsigned int *ptr;
445   
446   /* initialize a random number seed from the time, so we get random */
447   /* numbers each time */
448   srand (time(NULL));
449   y = (BG_H >> 1) - 1;
450   for (x = 0 ; x < (BG_W >> 1) ; x++)
451     {
452       ptr = f->flame + (y << f->ws) + x;
453       *ptr = rand ()%IMAX;
454     }
455 }
456
457 /* modify the base of the flame with random values */
458 static void
459 flame_modify_flame_base (flame *f)
460 {
461   int           x;
462   int           y;
463   unsigned int *ptr;
464   int           val;
465   
466   y = (BG_H >> 1) - 1;
467   for (x = 0 ; x < (BG_W >> 1) ; x++)
468     {
469       ptr = f->flame + (y << f->ws) + x;
470       *ptr += ((rand ()%VARIANCE) - VARTREND);
471       val = *ptr;
472       if (val > IMAX) *ptr = 0;
473       if (val < 0)    *ptr = 0;
474     }
475 }
476
477 /* process entire flame array */
478 static void
479 flame_process_flame (flame *f)
480 {
481   int           x;
482   int           y;
483   unsigned int *ptr;
484   unsigned int *p;
485   unsigned int  val;
486   unsigned int  tmp;
487   
488   for (y = ((BG_H >> 1) - 1) ; y >= 2 ; y--)
489     {
490       for (x = 1 ; x < ((BG_W >> 1) - 1) ; x++)
491         {
492           ptr = f->flame + (y << f->ws) + x;
493           val = (unsigned int)*ptr;
494           if (val > IMAX)
495             *ptr = (unsigned int)IMAX;
496           val = (unsigned int)*ptr;
497           if (val > 0)
498             {
499               tmp = (val * VSPREAD) >> 8;
500               p   = ptr - (2 << f->ws);
501               *p  = *p + (tmp >> 1);
502               p   = ptr - (1 << f->ws);
503               *p  = *p + tmp;
504               tmp = (val * HSPREAD) >> 8;
505               p   = ptr - (1 << f->ws) - 1;
506               *p  = *p + tmp;
507               p   = ptr - (1 << f->ws) + 1;
508               *p  = *p + tmp;
509               p   = ptr - 1;
510               *p  = *p + (tmp >>1 );
511               p   = ptr + 1;
512               *p  = *p + (tmp >> 1);
513               p   = f->flame2 + (y << f->ws) + x;
514               *p  = val;
515               if (y < ((BG_H >> 1) - 1))
516                 *ptr = (val * RESIDUAL) >> 8;
517             }
518         }
519     }
520 }
521
522 static int
523 ilog2 (unsigned int n)
524 {
525   int p = -1;
526
527   assert(n > 0);
528   while (n > 0) {
529     p++;
530     n >>= 1;
531   }
532
533   return p;
534 }