aboutsummaryrefslogtreecommitdiffstats
path: root/gg_elife/src/elife_evas_smart.c
diff options
context:
space:
mode:
Diffstat (limited to 'gg_elife/src/elife_evas_smart.c')
-rw-r--r--gg_elife/src/elife_evas_smart.c558
1 files changed, 558 insertions, 0 deletions
diff --git a/gg_elife/src/elife_evas_smart.c b/gg_elife/src/elife_evas_smart.c
new file mode 100644
index 0000000..acbd9a2
--- /dev/null
+++ b/gg_elife/src/elife_evas_smart.c
@@ -0,0 +1,558 @@
+/*
+ * elife - Game of life / Living graphic
+ */
+
+/*
+ * Copyright (c) 2012 Laurent Ghigonis <laurent@p1sec.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <err.h>
+
+#include "elife_evas_smart.h"
+
+#ifdef HAVE_GLOUGLOU
+#include <libglouglou.h>
+#endif
+
+#define NCELL_X 100
+#define NCELL_Y 100
+#define INJECT_PROBA 40
+#define INITCELL_PROBA 7
+#define CONWAY_GROW_DIE 600
+#define DEBUG 0
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+#define CELL_GET(grid, x, y) &(grid->cells[(y*grid->w) + x])
+
+typedef struct lifepattern_s {
+ const int w;
+ const int h;
+ const char *pat;
+} lifepattern_s;
+
+enum lifepattern_t {
+ ELIFE_PAT_BEEHIVE = 0,
+ ELIFE_PAT_BOAT = 1,
+ ELIFE_PAT_SHIP = 2,
+ ELIFE_PAT_LOAF = 3,
+ ELIFE_PAT_QUEENBEE = 4,
+ ELIFE_PAT_GLIDER = 5,
+ ELIFE_PAT_LWSS = 6,
+};
+#define PATTERN_COUNT 7
+
+lifepattern_s lifepatterns[] = {
+ [ELIFE_PAT_BEEHIVE] = {3, 4, "010101101010"},
+ [ELIFE_PAT_BOAT] = {3, 3, "010101011"},
+ [ELIFE_PAT_SHIP] = {3, 3, "110101011"},
+ [ELIFE_PAT_LOAF] = {4, 4, "0110100101010010"},
+ [ELIFE_PAT_QUEENBEE] = {4, 7, "1100001000010001000100101100"},
+ [ELIFE_PAT_GLIDER] = {3, 3, "010011101"},
+ [ELIFE_PAT_LWSS] = {5, 4, "10010000011000101111"},
+};
+
+enum lifemode {
+ ELIFE_MODE_CONWAY,
+ ELIFE_MODE_CONWAY_GROW,
+ ELIFE_MODE_LORAN_CIVILISATION,
+};
+
+struct cell {
+ int age, newage;
+ u_int32_t forced_color;
+ int x, y;
+ int r, g, b;
+};
+
+struct grid {
+ Evas_Object *container;
+ struct cell *cells;
+ Evas_Object *image;
+ int *mem;
+ int w, h;
+ int pix_w, pix_h;
+ int cell_pix_w, cell_pix_h;
+ int age;
+ enum lifemode mode;
+#ifdef HAVE_GLOUGLOU
+ struct gg_client *ggcli;
+ struct event_base *ev_base;
+#endif
+};
+
+static struct grid *grid_new(Evas_Object *container,
+ int w, int h, enum lifemode mode);
+static void grid_del(struct grid *grid);
+static int grid_evolution(struct grid *grid);
+static void grid_inject_pattern(struct grid *grid, u_int32_t color);
+static int grid_redraw(struct grid *grid, int w, int h);
+static int cell_neighbours_count(struct cell *cell, struct grid *grid);
+static void cell_redraw(struct cell *c, struct grid *g);
+static void grid_mouse_down(void *data, Evas *evas, Evas_Object *child,
+ void *event_info);
+
+Evas_Object *elife_smart_new(Evas *e);
+static void elife_on_refresh(void *data, Evas_Object *o,
+ void *event_info);
+static Evas_Object *elife_object_new(Evas *evas);
+static Evas_Smart *_elife_object_smart_get(void);
+static void _elife_object_del(Evas_Object *o);
+static void _elife_object_move(Evas_Object *o,
+ Evas_Coord x, Evas_Coord y);
+static void _elife_object_resize(Evas_Object *o,
+ Evas_Coord w, Evas_Coord h);
+/* Not implemented
+static void _elife_object_show(Evas_Object *o);
+static void _elife_object_hide(Evas_Object *o);
+static void _elife_object_color_set(Evas_Object *o, int r, int g, int b, int a);
+static void _elife_object_clip_set(Evas_Object *o, Evas_Object *clip);
+static void _elife_object_clip_unset(Evas_Object *o);
+*/
+void *xmalloc(size_t size);
+
+static struct {
+ Evas_Smart_Class klass;
+} elife_evas_smart_g = {
+ .klass = {
+ .name = "elife_object",
+ .version = EVAS_SMART_CLASS_VERSION,
+ .add = NULL,
+ .del = _elife_object_del,
+ .move = _elife_object_move,
+ .resize = _elife_object_resize,
+ .show = NULL,
+ .hide = NULL,
+ .color_set = NULL,
+ .clip_set = NULL,
+ .clip_unset = NULL,
+ .calculate = NULL,
+ .member_add = NULL,
+ .member_del = NULL,
+ .parent = NULL,
+ .callbacks = NULL,
+ .interfaces = NULL,
+ .data = NULL,
+ },
+#define _G elife_evas_smart_g
+};
+
+#ifdef HAVE_GLOUGLOU
+int
+gg_packet(struct gg_client *cli, struct gg_packet *pkt)
+{
+ struct grid *grid;
+ u_int32_t color;
+
+ grid = cli->usrdata;
+ switch(pkt->type) {
+ case PACKET_FORK: color=0xff0000; break;
+ case PACKET_EXEC: color=0x00ff00; break;
+ case PACKET_EXIT: color=0x0000ff; break;
+ default: return 0;
+ }
+ grid_inject_pattern(grid, color);
+
+ return 0;
+}
+#endif
+
+static struct grid *
+grid_new(Evas_Object *container, int w, int h, enum lifemode mode)
+{
+ struct grid *g;
+ struct cell *cell;
+ Evas_Object *o;
+ int i, j;
+
+ g = xmalloc(sizeof(struct grid));
+ g->container = container;
+ o = evas_object_image_filled_add(evas_object_evas_get(container));
+ if (!o)
+ err(1, "Cannot create image for grid");
+ evas_object_image_alpha_set(o, EINA_FALSE);
+ evas_object_image_smooth_scale_set(o, EINA_FALSE);
+ evas_object_data_set(o, "grid", g);
+ evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_DOWN,
+ grid_mouse_down, g);
+ evas_object_smart_member_add(o, container);
+ evas_object_show(o);
+ g->image = o;
+ g->mem = NULL;
+ g->w = w;
+ g->h = h;
+ g->age = 0;
+ g->mode = mode;
+ g->cells = xmalloc(sizeof(struct cell) * w * h);
+ for (j=0; j<h; j++) {
+ for (i=0; i<w; i++) {
+ cell = CELL_GET(g, i, j);
+ cell->x = i;
+ cell->y = j;
+ cell->r = 0;
+ cell->g = 0;
+ cell->b = 0;
+ cell->forced_color = 0x000000;
+ cell->age = !(rand() % INITCELL_PROBA);
+ }
+ }
+
+#ifdef HAVE_GLOUGLOU
+ g->ev_base = event_base_new();
+ g->ggcli = gg_client_connect(g->ev_base, "127.0.0.1", GLOUGLOU_ANALY_DEFAULT_PORT,
+ NULL, gg_packet, g);
+#endif
+
+ return g;
+}
+
+static void
+grid_del(struct grid *grid)
+{
+#ifdef HAVE_GLOUGLOU
+ gg_client_disconnect(grid->ggcli);
+#endif
+ free(grid->cells);
+ free(grid->mem);
+ free(grid);
+}
+
+static int
+grid_evolution(struct grid *grid)
+{
+ struct cell *cell;
+ int i, j;
+ int neighbours;
+
+ for (j=0; j<grid->h; j++) {
+ for (i=0; i<grid->w; i++) {
+ cell = CELL_GET(grid, i, j);
+ neighbours = cell_neighbours_count(cell, grid);
+ switch (grid->mode) {
+ case ELIFE_MODE_CONWAY:
+ if (cell->age == 0 && neighbours == 3)
+ cell->newage = 1;
+ else if (cell->age > 0 && (neighbours == 2 || neighbours == 3))
+ cell->newage = cell->age;
+ else
+ cell->newage = 0;
+ break;
+ case ELIFE_MODE_CONWAY_GROW:
+ if (cell->age == 0 && neighbours == 3)
+ cell->newage = 1;
+ else if (cell->age > 0 && (neighbours == 2 || neighbours == 3))
+ if (cell->age > CONWAY_GROW_DIE)
+ cell->newage = 0;
+ else
+ cell->newage = cell->age + 1;
+ else
+ cell->newage = 0;
+ break;
+ case ELIFE_MODE_LORAN_CIVILISATION:
+ if (neighbours == 4 || neighbours < 2)
+ cell->newage = 0;
+ else if (neighbours > 2 && neighbours != 5)
+ cell->newage = cell->age + 1;
+ else if (neighbours == 2 && cell->age > 1)
+ cell->newage = 0;
+ else
+ cell->newage = cell->age;
+ if (cell->age > 100)
+ cell->newage = 0;
+ break;
+ }
+ if (cell->newage == 0 && cell->forced_color)
+ cell->forced_color = 0;
+ if (DEBUG)
+ printf("evolution: %d %d n %d age %d newage %d\n", i, j,
+ neighbours, cell->age, cell->newage);
+ }
+ }
+
+#ifdef HAVE_GLOUGLOU
+ event_base_loop(grid->ev_base, EVLOOP_NONBLOCK);
+#else
+ if (rand() % INJECT_PROBA == 0)
+ grid_inject_pattern(grid, 0);
+#endif
+
+ for (j=0; j<grid->h; j++) {
+ for (i=0; i<grid->w; i++) {
+ cell = CELL_GET(grid, i, j);
+ cell->age = cell->newage;
+ cell_redraw(cell, grid);
+ }
+ }
+ evas_object_image_pixels_dirty_set(grid->image, EINA_TRUE);
+
+ grid->age++;
+}
+
+static void
+grid_inject_pattern(struct grid *grid, u_int32_t color)
+{
+ struct cell *cell;
+ enum lifepattern_t npat;
+ lifepattern_s *pat;
+ int x, y, cx, cy;
+ int i, j;
+ int age;
+
+ npat = rand() % PATTERN_COUNT;
+ if (DEBUG)
+ printf("grid_inject_pattern: %d\n", npat);
+ pat = &lifepatterns[npat];
+ x = rand() % (grid->w - pat->w);
+ y = rand() % (grid->h - pat->h);
+ for (j=0; j<pat->h; j++) {
+ for (i=0; i<pat->w; i++) {
+ cx = x + i;
+ cy = y + j;
+ if (cx >= grid->w || cy >= grid->h)
+ continue;
+ cell = CELL_GET(grid, cx, cy);
+ age = (pat->pat[pat->w*j + i] - '0') * 27;
+ if (age != 0)
+ cell->newage = age;
+ cell->forced_color = color;
+ cell->age = cell->newage;
+ // cell_redraw(cell, grid);
+ }
+ }
+}
+
+static int
+grid_redraw(struct grid *grid, int w, int h)
+{
+ struct cell *cell;
+ int i,j;
+ Evas_Object *o;
+
+ grid->cell_pix_w = w / grid->w;
+ grid->cell_pix_h = h / grid->h;
+ grid->pix_w = (w / grid->w) * grid->w;
+ grid->pix_h = (h / grid->h) * grid->h;
+ if (DEBUG)
+ printf("redraw: container %d %d cell %d %d\n", w, h, grid->cell_pix_w, grid->cell_pix_h);
+
+ evas_object_image_fill_set(grid->image, 0, 0, grid->pix_w, grid->pix_h);
+ evas_object_image_size_set (grid->image, grid->pix_w, grid->pix_h);
+ evas_object_resize(grid->image, w, h);
+ /* XXX could be optimized to avoid flickering on resize */
+ if (grid->mem)
+ free(grid->mem);
+ grid->mem = calloc(w * h, sizeof(int));
+ if (!grid->mem)
+ err(1, "could not calloc grid->mem");
+ evas_object_image_data_set(grid->image, (void *) grid->mem);
+ for (j=0; j<grid->h; j++) {
+ for (i=0; i<grid->w; i++) {
+ cell = CELL_GET(grid, i, j);
+ cell_redraw(cell, grid);
+ }
+ }
+}
+
+static int
+cell_neighbours_count(struct cell *cell, struct grid *grid)
+{
+ struct cell *ncell;
+ int m, n, cx, cy;
+ int count;
+
+ count = 0;
+ for (n=-1; n<=1; n++) {
+ for (m=-1; m<=1; m++) {
+ if (m == 0 && n == 0)
+ continue;
+ cx = cell->x + m;
+ cy = cell->y + n;
+ if (cx < 0 || cx >= grid->w || cy < 0 || cy >= grid->h)
+ continue;
+ ncell = CELL_GET(grid, cx, cy);
+ if (ncell->age > 0)
+ count++;
+ }
+ }
+
+ return count;
+}
+
+static void
+cell_redraw(struct cell *c, struct grid *grid)
+{
+ u_int8_t r, g, b, a;
+ int x, y, w, h;
+ int i, j;
+ int color;
+
+ if (c->forced_color && c->age > 0) {
+ r = (c->forced_color & 0xff0000) >> 16;
+ g = (c->forced_color & 0xff00) >> 8;
+ b = (c->forced_color & 0xff);
+ } else if (c->age < 100) {
+ r = (c->age > 0) ? 128 : 0;
+ g = (c->age > 0) ? (33 + c->age * 7) : 0;
+ b = 0;
+ } else if (c->age < CONWAY_GROW_DIE - 50) {
+ r = 128 - c->age;
+ g = 255 - c->age;
+ b = (c->age - 100) * 2;
+ } else {
+ r = 255;
+ g = (c->age % 2) * 255;
+ b = 255;
+ }
+ a = 255;
+ if (r == c->r && g == c->g && b == c->b)
+ return;
+ c->r = r;
+ c->g = g;
+ c->b = b;
+
+ x = c->x * grid->cell_pix_w;
+ y = c->y * grid->cell_pix_h;
+ w = grid->cell_pix_w;
+ h = grid->cell_pix_h;
+ for (j=y; j<y+h; j++) {
+ for (i=x; i<x+w; i++) {
+ color = 0xff000000 + (r << 16) + (g << 8) + (b);
+ grid->mem[j*grid->pix_w + i] = color;
+ }
+ }
+}
+
+static void
+grid_mouse_down(void *data,
+ Evas *evas,
+ Evas_Object *child,
+ void *event_info)
+{
+ Evas_Coord x, y, w, h;
+ Evas_Event_Mouse_Up *evt = event_info;
+ struct grid *grid;
+
+ grid = data;
+ grid_inject_pattern(grid, 0);
+}
+
+Evas_Object *
+elife_smart_new(Evas *e)
+{
+ return elife_object_new(e);
+}
+
+static void
+elife_on_refresh(void *data, Evas_Object *o, void *event_info)
+{
+ Evas_Coord x, y, w, h;
+ Evas_Object *child;
+ struct grid *grid;
+
+ grid = evas_object_data_get(o, "grid");
+
+ grid_evolution(grid);
+}
+
+static Evas_Object *
+elife_object_new(Evas *evas)
+{
+ Evas_Object *elife_object;
+ struct grid *grid;
+
+ elife_object = evas_object_smart_add(evas,
+ _elife_object_smart_get());
+ grid = grid_new(elife_object, NCELL_X, NCELL_Y, ELIFE_MODE_CONWAY_GROW);
+ evas_object_data_set(elife_object, "grid", grid);
+ evas_object_smart_callback_add(elife_object,
+ "refresh",
+ elife_on_refresh,
+ NULL);
+
+ return elife_object;
+}
+
+static Evas_Smart *
+_elife_object_smart_get(void)
+{
+ static Evas_Smart *smart = NULL;
+
+ if (smart)
+ return smart;
+
+ smart = evas_smart_class_new(&_G.klass);
+ return smart;
+}
+
+static void
+_elife_object_del(Evas_Object *o)
+{
+ Evas_Object *child;
+ struct grid *grid;
+ Eina_List *list;
+
+ list = evas_object_smart_members_get(o);
+ EINA_LIST_FREE(list, child) {
+ evas_object_smart_member_del(child);
+ evas_object_del(child);
+ }
+ grid = evas_object_data_del(o, "grid");
+ grid_del(grid);
+}
+
+static void
+_elife_object_move(Evas_Object *o, Evas_Coord x, Evas_Coord y)
+{
+ Evas_Coord orig_x, orig_y, dx, dy;
+ Eina_List *lst;
+ Evas_Object *child;
+ void *data;
+
+ if (DEBUG)
+ printf("oject_move: %d %d\n", x, y);
+ evas_object_geometry_get(o, &orig_x, &orig_y, NULL, NULL);
+ dx = x - orig_x;
+ dy = y - orig_y;
+
+ lst = evas_object_smart_members_get(o);
+ EINA_LIST_FREE(lst, data) {
+ child = data;
+ evas_object_geometry_get(child, &orig_x, &orig_y, NULL, NULL);
+ evas_object_move(child, orig_x + dx, orig_y + dy);
+ }
+}
+
+static void
+_elife_object_resize(Evas_Object *o, Evas_Coord w, Evas_Coord h)
+{
+ struct grid *grid;
+
+ if (DEBUG)
+ printf("oject_resize: %d %d\n", w, h);
+ grid = evas_object_data_get(o, "grid");
+ grid_redraw(grid, w, h);
+}
+
+void *
+xmalloc(size_t size)
+{
+ void *x;
+
+ x = malloc(size);
+ if (!x)
+ err(1, "Error: failed to allocate %d", size);
+ return x;
+}
+