X-Git-Url: http://vcs.maemo.org/git/?a=blobdiff_plain;f=rfk.c;h=55be231eca03317bc6b35173b54cf9f89a334fc2;hb=2dbf7631a24fc1a045766019b3195e1404e96765;hp=e63edaed1abc76bcba52039132b33fb5b483833a;hpb=8b36b4af805d8e46cbf084af9982f7f96bc028e4;p=rfk diff --git a/rfk.c b/rfk.c index e63edae..55be231 100644 --- a/rfk.c +++ b/rfk.c @@ -3,9 +3,10 @@ * ported to maemo by Thomas Thurman, 2009 * suggestions welcome * Compile with: - * gcc -Wall -g rfk.c -o rfk `pkg-config --cflags --libs gtk+-2.0 hildon-1` + * gcc -Wall -g rfk.c -o rfk `pkg-config --cflags --libs gtk+-2.0 hildon-1 dbus-glib-1 dbus-1` */ +#include #include #include #include @@ -19,16 +20,25 @@ const int amount_of_random_stuff = 15; +typedef enum { + STATE_PROLOGUE, + STATE_PLAYING, + STATE_EPILOGUE, + STATE_LAST +} StateOfPlay; + +StateOfPlay current_state = STATE_LAST; +GtkWidget* state_widget[STATE_LAST]; + GSList *nki = NULL; guint nki_count = 0; GtkWidget *arena[ARENA_WIDTH][ARENA_HEIGHT]; -GtkWidget *intro, *table, *window, *robot, *kitten; +GtkWidget *window, *robot, *kitten; int robot_x, robot_y; gboolean *used = NULL; GdkPixbuf *robot_pic, *love_pic, *kitten_pic; -GtkWidget *animation_area; const GdkColor black = { 0, }; @@ -36,7 +46,7 @@ const GdkColor black = { 0, }; /* Random object descriptions. */ /****************************************************************/ -char * +static char * description (void) { int r; @@ -46,8 +56,8 @@ description (void) r = random() % nki_count; } while (used[r]); - used[r] = TRUE; + return g_slist_nth_data (nki, r); } @@ -55,12 +65,12 @@ description (void) /* Placing objects. */ /****************************************************************/ -void +static void place_in_arena_at_xy (GtkWidget *item, int x, int y) { arena[x][y] = item; - gtk_table_attach_defaults (GTK_TABLE (table), + gtk_table_attach_defaults (GTK_TABLE (state_widget[STATE_PLAYING]), item, x, x+1, y, y+1); @@ -72,7 +82,7 @@ place_in_arena_at_xy (GtkWidget *item, int x, int y) } } -void +static void place_in_arena_randomly (GtkWidget *item) { int x, y; @@ -91,7 +101,7 @@ place_in_arena_randomly (GtkWidget *item) /* Labels representing things the robot might find. */ /****************************************************************/ -GtkWidget * +static GtkWidget * random_character (gchar *description) { gchar character[2] = { random() % ('~'-'!') + '!', 0 }; @@ -115,12 +125,13 @@ random_character (gchar *description) /* Talking back to the user. */ /****************************************************************/ -void +static void show_message (const char *message) { HildonNote* note = HILDON_NOTE (hildon_note_new_information (GTK_WINDOW (window), - message)); + message?message: + "Some message was supposed to be here.")); gtk_dialog_run (GTK_DIALOG (note)); gtk_widget_destroy (GTK_WIDGET (note)); } @@ -128,7 +139,8 @@ show_message (const char *message) /****************************************************************/ /* Loading the non-kitten objects. */ /****************************************************************/ -void + +static void ensure_messages_loaded (void) { FILE *nki_file = NULL; @@ -173,11 +185,9 @@ ensure_messages_loaded (void) } fclose (nki_file); - - used = g_malloc0 (nki_count); } -void +static void load_images (void) { robot_pic = gdk_pixbuf_new_from_file ("/usr/share/rfk/rfk-robot.png", NULL); @@ -186,76 +196,189 @@ load_images (void) } /****************************************************************/ +/* Stop doing that, and do something else. */ +/****************************************************************/ + +static void +switch_state (StateOfPlay new_state) +{ + if (current_state != STATE_LAST) + { + gtk_container_remove (GTK_CONTAINER (window), state_widget[current_state]); + } + gtk_container_add (GTK_CONTAINER (window), state_widget[new_state]); + + gtk_widget_show_all (window); + gdk_window_set_events (GTK_WIDGET (window)->window, + gdk_window_get_events(GTK_WIDGET (window)->window) | GDK_BUTTON_PRESS_MASK); + + current_state = new_state; +} + +/****************************************************************/ +/* Things we need DBus for: online help, and vibration. */ +/****************************************************************/ + +static void +call_dbus (DBusBusType type, + char *name, + char *path, + char *interface, + char *method, + char *parameter) +{ + DBusGConnection *connection; + GError *error = NULL; + + DBusGProxy *proxy; + + connection = dbus_g_bus_get (type, + &error); + if (connection == NULL) + { + show_message (error->message); + g_error_free (error); + return; + } + + proxy = dbus_g_proxy_new_for_name (connection, name, path, interface); + + error = NULL; + if (!dbus_g_proxy_call (proxy, method, &error, + G_TYPE_STRING, parameter, + G_TYPE_INVALID, + G_TYPE_INVALID)) + { + show_message (error->message); + g_error_free (error); + } +} + +static gboolean +get_help (gpointer button, gpointer data) +{ + call_dbus (DBUS_BUS_SESSION, + "com.nokia.osso_browser", + "/com/nokia/osso_browser/request", + "com.nokia.osso_browser", + "load_url", + "/usr/share/rfk/help.html"); + return FALSE; +} + +static void +vibrate (void) +{ + call_dbus (DBUS_BUS_SYSTEM, + "com.nokia.mce", + "/com/nokia/mce/request", + "com.nokia.mce.request", + "req_vibrator_pattern_activate", + "PatternIncomingMessage"); +} + +/****************************************************************/ /* The ending animation. */ /****************************************************************/ +gboolean animation_running = FALSE; + static gboolean ending_animation_quit (gpointer data) { - gtk_main_quit (); + switch_state (STATE_PROLOGUE); return FALSE; } static gboolean ending_animation_draw (GtkWidget *widget, GdkEventExpose *event, gpointer data) { - /* We only run through once, so just make it static. */ static int cycle_count = 0; static int robot_x = 0; static int robot_stop = 0; static int kitten_x = 0; static int all_y = 0; + static GdkGC *gc = NULL; const int stepsize = 3; + static int love_size = 40; if (!kitten_x) { + gc = gdk_gc_new (GDK_DRAWABLE (widget->window)); + all_y = (event->area.height - gdk_pixbuf_get_height (love_pic)) / 2; robot_stop = gdk_pixbuf_get_width (robot_pic) + gdk_pixbuf_get_width (love_pic); kitten_x = event->area.width - (cycle_count*stepsize + gdk_pixbuf_get_width (kitten_pic)); } - gdk_gc_set_foreground (widget->style->fg_gc[GTK_WIDGET_STATE (widget)], - &black); + gdk_gc_set_foreground (gc, &black); - gdk_draw_rectangle (GDK_DRAWABLE(widget->window), - widget->style->fg_gc[GTK_WIDGET_STATE (widget)], + gdk_draw_rectangle (GDK_DRAWABLE (widget->window), + gc, TRUE, 0, 0, event->area.width, event->area.height); - gdk_draw_pixbuf (GDK_DRAWABLE(widget->window), - widget->style->fg_gc[GTK_WIDGET_STATE (widget)], + gdk_draw_pixbuf (GDK_DRAWABLE (widget->window), + gc, robot_pic, 0, 0, robot_x, all_y, -1, -1, GDK_RGB_DITHER_NONE, 0, 0); - gdk_draw_pixbuf (GDK_DRAWABLE(widget->window), - widget->style->fg_gc[GTK_WIDGET_STATE (widget)], + gdk_draw_pixbuf (GDK_DRAWABLE (widget->window), + gc, kitten_pic, 0, 0, kitten_x, all_y, -1, -1, GDK_RGB_DITHER_NONE, 0, 0); - cycle_count++; - robot_x += stepsize; - kitten_x -= stepsize; - - if (robot_x+robot_stop >= kitten_x) + if (robot_x+robot_stop < kitten_x) + { + cycle_count++; + robot_x += stepsize; + kitten_x -= stepsize; + } + else { + GdkPixbuf *scaled_love_pic = + gdk_pixbuf_scale_simple (love_pic, + love_size, + love_size, + GDK_INTERP_BILINEAR); + gdk_draw_pixbuf (GDK_DRAWABLE(widget->window), - widget->style->fg_gc[GTK_WIDGET_STATE (widget)], - love_pic, 0, 0, - robot_x + gdk_pixbuf_get_width (robot_pic), all_y, + gc, + scaled_love_pic, 0, 0, + robot_x + gdk_pixbuf_get_width (robot_pic) + + (gdk_pixbuf_get_width (love_pic)-love_size)/2, + all_y + (gdk_pixbuf_get_height (love_pic)-love_size)/2, -1, -1, GDK_RGB_DITHER_NONE, 0, 0); - g_object_unref (love_pic); - love_pic = NULL; + love_size += 10; - g_timeout_add (2000, ending_animation_quit, NULL); + if (love_size >= gdk_pixbuf_get_width (love_pic)) + { + /* all done! */ + + vibrate (); + + animation_running = FALSE; + + g_timeout_add (2000, ending_animation_quit, NULL); + + gdk_gc_unref (gc); + love_size = 40; + cycle_count = 0; + robot_x = 0; + robot_stop = 0; + kitten_x = 0; + all_y = 0; + gc = NULL; + } } return TRUE; @@ -264,9 +387,9 @@ ending_animation_draw (GtkWidget *widget, GdkEventExpose *event, gpointer data) static gboolean ending_animation_step (gpointer data) { - if (love_pic) + if (animation_running) { - gdk_window_invalidate_rect (animation_area->window, + gdk_window_invalidate_rect (state_widget[STATE_EPILOGUE]->window, NULL, TRUE); return TRUE; @@ -278,15 +401,11 @@ ending_animation_step (gpointer data) static void ending_animation () { - animation_area = gtk_drawing_area_new (); - - gtk_container_remove (GTK_CONTAINER (window), GTK_WIDGET (table)); - gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (animation_area)); - gtk_widget_show_all (window); - - g_signal_connect (G_OBJECT (animation_area), - "expose_event", G_CALLBACK (ending_animation_draw), NULL); - g_timeout_add (10, ending_animation_step, NULL); + if (current_state!=STATE_EPILOGUE) + { + animation_running = TRUE; + g_timeout_add (10, ending_animation_step, NULL); + } } /****************************************************************/ @@ -311,7 +430,7 @@ direction directions[] = { { GDK_Up, 'k', 0, -1 } }; -gboolean +static gboolean move_robot (guint8 whichway) { GtkWidget *new_space; @@ -335,7 +454,7 @@ move_robot (guint8 whichway) if (new_space == kitten) { - ending_animation (); + switch_state (STATE_EPILOGUE); } return TRUE; @@ -346,8 +465,8 @@ move_robot (guint8 whichway) g_object_ref (new_space); - gtk_container_remove (GTK_CONTAINER (table), robot); - gtk_container_remove (GTK_CONTAINER (table), new_space); + gtk_container_remove (GTK_CONTAINER (state_widget[STATE_PLAYING]), robot); + gtk_container_remove (GTK_CONTAINER (state_widget[STATE_PLAYING]), new_space); place_in_arena_at_xy (new_space, robot_x, robot_y); place_in_arena_at_xy (robot, robot_x+dx, robot_y+dy); @@ -362,7 +481,7 @@ move_robot (guint8 whichway) /* Event handlers. */ /****************************************************************/ -gboolean +static gboolean on_window_clicked (GtkWidget *widget, GdkEventButton *event, gpointer user_data) @@ -371,6 +490,11 @@ on_window_clicked (GtkWidget *widget, int rx, ry; double angle; + if (current_state!=STATE_PLAYING) + { + return TRUE; + } + rx = (robot->allocation.x+robot->allocation.width/2); ry = (robot->allocation.y+robot->allocation.height/2); @@ -383,7 +507,7 @@ on_window_clicked (GtkWidget *widget, return TRUE; } -gboolean +static gboolean on_key_pressed (GtkWidget *widget, GdkEventKey *event, gpointer user_data) @@ -391,6 +515,11 @@ on_key_pressed (GtkWidget *widget, gint i; guint keyval = event->keyval; + if (current_state!=STATE_PLAYING) + { + return FALSE; + } + if (keyval>='A' && keyval<='Z') { keyval += ('a'-'A'); @@ -416,75 +545,93 @@ on_key_pressed (GtkWidget *widget, } } + if (keyval=='d' && event->state & GDK_CONTROL_MASK) + { + /* secret debugging key */ + show_message (gtk_label_get_text (GTK_LABEL (kitten))); + } + return FALSE; } -void -create_window (void) +static void +play_game (gpointer button, gpointer data) { - window = gtk_window_new (GTK_WINDOW_TOPLEVEL); - gtk_window_set_title (GTK_WINDOW (window), "robotfindskitten"); - gtk_widget_modify_bg (window, GTK_STATE_NORMAL, &black); + switch_state (STATE_PLAYING); } -void -set_up_game (void) +static void +restart (gpointer button, gpointer data) { - guint x, y; + if (current_state == STATE_EPILOGUE) + { + show_message ("Have patience while robotfindskitten."); + } + else + { + switch_state (STATE_PROLOGUE); + } +} - g_signal_connect (G_OBJECT (window), "button-press-event", G_CALLBACK (on_window_clicked), NULL); - g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (on_key_pressed), NULL); - g_signal_connect (G_OBJECT (window), "delete_event", G_CALLBACK (gtk_main_quit), NULL); - gdk_window_set_events (GTK_WIDGET (window)->window, - gdk_window_get_events(GTK_WIDGET (window)->window) | GDK_BUTTON_PRESS_MASK); - - table = gtk_table_new (ARENA_HEIGHT, ARENA_WIDTH, TRUE); - gtk_container_remove (GTK_CONTAINER (window), GTK_WIDGET (intro)); - gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (table)); +static void +set_up_board (void) +{ + guint x, y; - robot = gtk_label_new ("#"); - g_object_ref (robot); - kitten = random_character ("You found kitten! Way to go, robot!"); + if (current_state==STATE_PLAYING) + { + /* end of the game; clean up */ - place_in_arena_randomly (robot); - place_in_arena_randomly (kitten); + for (x=0; x < ARENA_WIDTH; x++) + for (y=0; y < ARENA_HEIGHT; y++) + if (arena[x][y]) + { + gtk_container_remove (GTK_CONTAINER (state_widget[STATE_PLAYING]), + arena[x][y]); + arena[x][y] = NULL; + } - if (nki_count < amount_of_random_stuff) - { - gtk_widget_show_all (window); - show_message ("There are too few non-kitten items to play a meaningful game."); - exit (EXIT_FAILURE); + g_object_unref (robot); + g_object_unref (kitten); } + else + { + /* make everything new */ + + g_free (used); + used = g_malloc0 (nki_count * sizeof(gboolean)); - for (x=0; x < amount_of_random_stuff; x++) - place_in_arena_randomly (random_character (description ())); + robot = gtk_label_new ("#"); + g_object_ref (robot); + kitten = random_character ("You found kitten! Way to go, robot!"); + g_object_ref (kitten); - for (x=0; x < ARENA_WIDTH; x++) - for (y=0; y < ARENA_HEIGHT; y++) - if (!arena[x][y]) - place_in_arena_at_xy (gtk_label_new (NULL), x, y); + place_in_arena_randomly (robot); + place_in_arena_randomly (kitten); - gtk_widget_show_all (window); -} + if (nki_count < amount_of_random_stuff) + { + /* sanity check failed */ + show_message ("There are too few non-kitten items to play a meaningful game."); + exit (EXIT_FAILURE); + } -void -get_help (gpointer button, gpointer data) -{ - show_message ("Not yet implemented."); -} + for (x=0; x < amount_of_random_stuff; x++) + place_in_arena_randomly (random_character (description ())); -void -play_game (gpointer button, gpointer data) -{ - set_up_game (); + for (x=0; x < ARENA_WIDTH; x++) + for (y=0; y < ARENA_HEIGHT; y++) + if (!arena[x][y]) + place_in_arena_at_xy (gtk_label_new (NULL), x, y); + } } -void -show_intro (void) +static void +set_up_widgets (void) { GtkWidget *middle = gtk_hbox_new (FALSE, 0); GtkWidget *buttons = gtk_hbox_new (TRUE, 0); - GtkWidget *explain = NULL, *help_button, *play_button; + GtkWidget *explain = NULL, *button, *intro; const char *explanation = "In this game, you are robot (#). " "Your job is to find kitten. This task is complicated " @@ -495,7 +642,23 @@ show_intro (void) "arrow keys."; GKeyFile *desktop = g_key_file_new (); gchar *version; + guint x, y; + HildonAppMenu *menu = HILDON_APP_MENU (hildon_app_menu_new ()); + + /* The window */ + window = hildon_window_new (); + gtk_window_set_title (GTK_WINDOW (window), "robotfindskitten"); + gtk_widget_modify_bg (window, GTK_STATE_NORMAL, &black); + g_signal_connect (G_OBJECT (window), "button-press-event", G_CALLBACK (on_window_clicked), NULL); + g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK (on_key_pressed), NULL); + g_signal_connect (G_OBJECT (window), "delete_event", G_CALLBACK (gtk_main_quit), NULL); + + /* The prologue */ + + /* Get the rather odd version string. The RFK spec says that + * it should read v... + */ if (g_key_file_load_from_file (desktop, "/usr/share/applications/hildon/rfk.desktop", G_KEY_FILE_NONE, @@ -511,35 +674,65 @@ show_intro (void) version = g_strdup(""); } - help_button = hildon_button_new_with_text (HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_THUMB_HEIGHT, + button = hildon_button_new_with_text (HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_THUMB_HEIGHT, HILDON_BUTTON_ARRANGEMENT_HORIZONTAL, - "Help", NULL); - g_signal_connect (help_button, "clicked", G_CALLBACK (get_help), NULL); + "Play", NULL); + g_signal_connect (button, "clicked", G_CALLBACK (play_game), NULL); + + gtk_box_pack_end (GTK_BOX (buttons), button, TRUE, TRUE, 0); - play_button = hildon_button_new_with_text (HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_THUMB_HEIGHT, + button = hildon_button_new_with_text (HILDON_SIZE_AUTO_WIDTH | HILDON_SIZE_THUMB_HEIGHT, HILDON_BUTTON_ARRANGEMENT_HORIZONTAL, - "Play", NULL); - g_signal_connect (play_button, "clicked", G_CALLBACK (play_game), NULL); + "Help", NULL); + g_signal_connect (button, "clicked", G_CALLBACK (get_help), NULL); + gtk_box_pack_end (GTK_BOX (buttons), button, TRUE, TRUE, 0); + + /* and another help button, this time for the menu */ + button = gtk_button_new_with_label ("Help"); + g_signal_connect (button, "clicked", G_CALLBACK (get_help), NULL); + hildon_app_menu_append (menu, GTK_BUTTON (button)); - gtk_box_pack_end (buttons, play_button, TRUE, TRUE, 0); - gtk_box_pack_end (buttons, help_button, TRUE, TRUE, 0); + button = gtk_button_new_with_label ("Restart"); + g_signal_connect (button, "clicked", G_CALLBACK (restart), NULL); + hildon_app_menu_append (menu, GTK_BUTTON (button)); + + gtk_widget_show_all (GTK_WIDGET (menu)); + hildon_window_set_app_menu (HILDON_WINDOW (window), menu); explain = gtk_label_new (explanation); - gtk_label_set_line_wrap (explain, TRUE); + gtk_label_set_line_wrap (GTK_LABEL (explain), TRUE); - gtk_box_pack_end (middle, explain, TRUE, TRUE, 0); - gtk_box_pack_end (middle, gtk_image_new_from_pixbuf (robot_pic), FALSE, FALSE, 0); + gtk_box_pack_end (GTK_BOX (middle), explain, TRUE, TRUE, 0); + gtk_box_pack_end (GTK_BOX (middle), gtk_image_new_from_pixbuf (robot_pic), FALSE, FALSE, 0); intro = gtk_vbox_new (FALSE, 0); gtk_box_pack_end (GTK_BOX (intro), buttons, FALSE, FALSE, 0); gtk_box_pack_end (GTK_BOX (intro), middle, TRUE, TRUE, 0); gtk_box_pack_end (GTK_BOX (intro), gtk_label_new (version), FALSE, FALSE, 0); - g_free (version); - gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (intro)); + state_widget[STATE_PROLOGUE] = intro; - gtk_widget_show_all (window); + /* The game itself */ + + state_widget[STATE_PLAYING] = gtk_table_new (ARENA_HEIGHT, ARENA_WIDTH, TRUE); + g_signal_connect (state_widget[STATE_PLAYING], "parent-set", G_CALLBACK (set_up_board), NULL); + + for (x=0; x < ARENA_WIDTH; x++) + for (y=0; y < ARENA_HEIGHT; y++) + arena[x][y] = NULL; + + /* The epilogue */ + state_widget[STATE_EPILOGUE] = gtk_drawing_area_new (); + g_signal_connect (state_widget[STATE_EPILOGUE], "parent-set", G_CALLBACK (ending_animation), NULL); + g_signal_connect (G_OBJECT (state_widget[STATE_EPILOGUE]), + "expose_event", G_CALLBACK (ending_animation_draw), NULL); + + for (x=0; x