diff options
Diffstat (limited to 'gg_elife/src/elife_evas_smart.c')
-rw-r--r-- | gg_elife/src/elife_evas_smart.c | 558 |
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; +} + |