From b1bed92a36048c30f0ad9e0252826d07aa85b9b0 Mon Sep 17 00:00:00 2001 From: nick Date: Wed, 6 Feb 2008 00:37:36 +0000 Subject: [PATCH] Go Cowsay go git-svn-id: http://svn.nickg.me.uk/work/xcowsay@269 a97b1542-0b21-0410-a459-e47997c36f34 --- Makefile | 4 +- display_cow.c | 94 ++++++++++++++++++++++++++++++++------ display_cow.h | 2 +- floating_shape.c | 9 +++- floating_shape.h | 3 +- settings.c | 116 +++++++++++++++++++++++++++++++++++++++++++++++ settings.h | 18 ++++++++ xcowsay.c | 27 +++++++++-- 8 files changed, 249 insertions(+), 24 deletions(-) create mode 100644 settings.c create mode 100644 settings.h diff --git a/Makefile b/Makefile index f0a5096..cfe7e2a 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ APPS:=xcowsay -OBJS:=xcowsay.o floating_shape.o display_cow.o -HEADERS:=floating_shape.h display_cow.h +OBJS:=xcowsay.o floating_shape.o display_cow.o settings.o +HEADERS:=floating_shape.h display_cow.h settings.h CFLAGS:=-Wall -g `pkg-config --cflags gtk+-2.0` LDFLAGS:=`pkg-config --libs gtk+-2.0` diff --git a/display_cow.c b/display_cow.c index 136ff1a..67e0af1 100644 --- a/display_cow.c +++ b/display_cow.c @@ -1,33 +1,57 @@ #include #include #include +#include #include #include #include "floating_shape.h" #include "display_cow.h" +#include "settings.h" -#define SPEED 10 // Horizontal speed in pixels per 100ms #define LEFT_BUF 5 // Amount of pixels to leave after cow's tail #define TIP_WIDTH 20 // Length of the triangle bit on the speech bubble #define CORNER_RADIUS 30 // Radius of corners on the speech bubble #define CORNER_DIAM CORNER_RADIUS*2 #define BUBBLE_BORDER 5 // Pixels to leave free around edge of bubble #define BUBBLE_XOFFSET 10 // Distance from cow to bubble -#define MIN_TIP_HEIGHT 15 +#define MIN_TIP_HEIGHT 15 + +#define TICK_TIMEOUT 100 + +typedef enum { + csLeadIn, csDisplay, csLeadOut, csCleanup +} cowstate_t; typedef struct { float_shape_t *cow, *bubble; int bubble_width, bubble_height; GdkPixbuf *cow_pixbuf, *bubble_pixbuf; + cowstate_t state; + int transition_timeout; } xcowsay_t; static xcowsay_t xcowsay; +static cowstate_t next_state(cowstate_t state) +{ + switch (state) { + case csLeadIn: + return csDisplay; + case csDisplay: + return csLeadOut; + case csLeadOut: + return csCleanup; + case csCleanup: + default: + return csCleanup; + } +} + static GdkPixbuf *load_cow() { - GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file("cow_large.png", NULL); + GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file("cow_med.png", NULL); if (NULL == pixbuf) { fprintf(stderr, "Failed to load cow image!\n"); exit(EXIT_FAILURE); @@ -192,6 +216,35 @@ static GdkPixbuf *create_bubble(char *text) return pixbuf; } +static gboolean tick(gpointer data) +{ + xcowsay.transition_timeout -= TICK_TIMEOUT; + if (xcowsay.transition_timeout <= 0) { + xcowsay.state = next_state(xcowsay.state); + switch (xcowsay.state) { + case csLeadIn: + fprintf(stderr, "Internal Error: Invalid state csLeadIn\n"); + exit(EXIT_FAILURE); + case csDisplay: + show_shape(xcowsay.bubble); + xcowsay.transition_timeout = get_int_option("display_time"); + break; + case csLeadOut: + hide_shape(xcowsay.bubble); + xcowsay.transition_timeout = get_int_option("lead_out_time"); + break; + case csCleanup: + destroy_shape(xcowsay.cow); + xcowsay.cow = NULL; + destroy_shape(xcowsay.bubble); + xcowsay.bubble = NULL; + break; + } + } + + return TRUE; +} + void cowsay_init(void) { xcowsay.cow = NULL; @@ -200,27 +253,40 @@ void cowsay_init(void) xcowsay.cow_pixbuf = load_cow(); } -void display_cow(char *text) +char *copy_string(const char *s) { + char *copy = malloc(strlen(s)+1); + strcpy(copy, s); + return copy; +} + +void display_cow(const char *text) +{ + char *text_copy = copy_string(text); + + // Trim any trailing newline + size_t len = strlen(text_copy); + if ('\n' == text_copy[len-1]) + text_copy[len-1] = '\0'; + g_assert(xcowsay.cow_pixbuf); xcowsay.cow = make_shape_from_pixbuf(xcowsay.cow_pixbuf); + move_shape(xcowsay.cow, 10, 30); show_shape(xcowsay.cow); - xcowsay.bubble_pixbuf = create_bubble(text); + xcowsay.bubble_pixbuf = create_bubble(text_copy); xcowsay.bubble = make_shape_from_pixbuf(xcowsay.bubble_pixbuf); int bx = shape_x(xcowsay.cow) + shape_width(xcowsay.cow) + BUBBLE_XOFFSET; int by = shape_y(xcowsay.cow) - + (shape_width(xcowsay.cow) - shape_width(xcowsay.bubble))/2; + + (shape_height(xcowsay.cow) - shape_height(xcowsay.bubble))/2; move_shape(xcowsay.bubble, bx, by); - - show_shape(xcowsay.bubble); + + xcowsay.state = csLeadIn; + xcowsay.transition_timeout = get_int_option("lead_in_time"); + g_timeout_add(TICK_TIMEOUT, tick, NULL); gtk_main(); - free_shape(xcowsay.bubble); - free_shape(xcowsay.cow); - xcowsay.bubble = NULL; - xcowsay.cow = NULL; - - printf("Here!\n"); + g_object_unref(xcowsay.bubble_pixbuf); + xcowsay.bubble_pixbuf = NULL; } diff --git a/display_cow.h b/display_cow.h index 482b927..cc88122 100644 --- a/display_cow.h +++ b/display_cow.h @@ -5,7 +5,7 @@ #include // Show a cow with the given string and clean up afterwards -void display_cow(char *text); +void display_cow(const char *text); void cowsay_init(void); #endif diff --git a/floating_shape.c b/floating_shape.c index 046db51..7892a6a 100644 --- a/floating_shape.c +++ b/floating_shape.c @@ -98,6 +98,11 @@ void show_shape(float_shape_t *shape) gtk_widget_show_all(shape->window); } +void hide_shape(float_shape_t *shape) +{ + gtk_widget_hide_all(shape->window); +} + void move_shape(float_shape_t *shape, int x, int y) { shape->x = x; @@ -105,9 +110,11 @@ void move_shape(float_shape_t *shape, int x, int y) gtk_window_move(GTK_WINDOW(shape->window), shape->x, shape->y); } -void free_shape(float_shape_t *shape) +void destroy_shape(float_shape_t *shape) { g_assert(shape); + + gtk_widget_destroy(shape->window); free(shape); } diff --git a/floating_shape.h b/floating_shape.h index ed0ced6..e335fe6 100644 --- a/floating_shape.h +++ b/floating_shape.h @@ -18,7 +18,8 @@ typedef struct { float_shape_t *make_shape_from_pixbuf(GdkPixbuf *pixbuf); void move_shape(float_shape_t *shape, int x, int y); void show_shape(float_shape_t *shape); -void free_shape(float_shape_t *shape); +void hide_shape(float_shape_t *shape); +void destroy_shape(float_shape_t *shape); #define shape_window(s) (s->window) #define shape_x(s) (s->x) diff --git a/settings.c b/settings.c new file mode 100644 index 0000000..53a1012 --- /dev/null +++ b/settings.c @@ -0,0 +1,116 @@ +#include +#include +#include +#include "settings.h" + +typedef enum { + optInt, optBool, optString +} option_type_t; + +typedef union { + int ival; + bool bval; + const char *sval; +} option_value_t; + +typedef struct { + option_type_t type; + option_value_t u; + const char *name; +} option_t; + +typedef struct _option_list_t { + struct _option_list_t *next; + option_t opt; +} option_list_t; + +static option_list_t *options = NULL; + +static option_list_t *alloc_node() +{ + option_list_t *node = (option_list_t*)malloc(sizeof(option_list_t)); + assert(node); + node->next = NULL; + return node; +} + +static option_t *get_option(const char *name) +{ + option_list_t *it; + for (it = options; it != NULL; it = it->next) { + if (strcmp(name, it->opt.name) == 0) + return &it->opt; + } + fprintf(stderr, "Internal Error: Invalid option %s\n", name); + exit(EXIT_FAILURE); +} + +int get_int_option(const char *name) +{ + option_t *opt = get_option(name); + if (optInt == opt->type) + return opt->u.ival; + else { + fprintf(stderr, "Error: Option %s is not of type integer\n", name); + exit(EXIT_FAILURE); + } +} + +bool get_bool_option(const char *name) +{ + option_t *opt = get_option(name); + if (optBool == opt->type) + return opt->u.bval; + else { + fprintf(stderr, "Error: Option %s is not of type Boolean\n", name); + exit(EXIT_FAILURE); + } +} + +const char *get_string_option(const char *name) +{ + option_t *opt = get_option(name); + if (optString == opt->type) + return opt->u.sval; + else { + fprintf(stderr, "Error: Option %s is not of type string\n", name); + exit(EXIT_FAILURE); + } +} + +static void add_option(const char *name, option_type_t type, option_value_t def) +{ + option_t opt = { type, def, name }; + option_list_t *node = alloc_node(); + node->opt = opt; + node->next = options; + options = node; +} + +void add_int_option(const char *name, int ival) +{ + option_value_t u; + u.ival = ival; + add_option(name, optInt, u); +} + +void add_bool_option(const char *name, bool bval) +{ + option_value_t u; + u.bval = bval; + add_option(name, optBool, u); +} + +static const char *copy_string(const char *s) +{ + char *copy = malloc(strlen(s)+1); + strcpy(copy, s); + return copy; +} + +void add_string_option(const char *name, const char *sval) +{ + option_value_t u; + u.sval = copy_string(sval); + add_option(name, optString, u); +} diff --git a/settings.h b/settings.h new file mode 100644 index 0000000..83bb363 --- /dev/null +++ b/settings.h @@ -0,0 +1,18 @@ +#ifndef INC_SETTINGS_H +#define INC_SETTINGS_H + +#include +#include + +void settings_init(); + +void add_int_option(const char *name, int ival); +void add_bool_option(const char *name, bool bval); +void add_string_option(const char *name, const char *sval); + +int get_int_option(const char *name); +bool get_bool_option(const char *name); +const char *get_string_option(const char *name); + + +#endif diff --git a/xcowsay.c b/xcowsay.c index deaf4fe..1aeceba 100644 --- a/xcowsay.c +++ b/xcowsay.c @@ -2,19 +2,36 @@ #include #include "display_cow.h" +#include "settings.h" -#define MAX_LINE 1024 // Max chars to read in one go + +// Default settings +#define DEF_LEAD_IN_TIME 250 +#define DEF_DISPLAY_TIME 4000 +#define DEF_LEAD_OUT_TIME LEAD_IN_TIME + +#define MAX_STDIN 4096 // Maximum chars to read from stdin static void read_from_stdin(void) { - /*char line[MAX_LINE]; - while (EOF != fgets(line, MAX_LINE, stdin)) { - display_cow(line); - }*/ + char *data = malloc(MAX_STDIN); + size_t n = fread(data, 1, MAX_STDIN, stdin); + if (n == MAX_STDIN) { + fprintf(stderr, "Warning: Excess input truncated\n"); + n--; + } + data[n] = '\0'; + + display_cow(data); + free(data); } int main(int argc, char **argv) { + add_int_option("lead_in_time", DEF_LEAD_IN_TIME); + add_int_option("display_time", DEF_DISPLAY_TIME); + add_int_option("lead_out_time", get_int_option("lead_in_time")); + gtk_init(&argc, &argv); cowsay_init(); -- 2.39.2