]> git.cworth.org Git - grrobot/blobdiff - src/grrobot.c
* configure.in:
[grrobot] / src / grrobot.c
index a56932d5cdd23b5462908fb07970a481cf4284b5..8c42f26106111b4738b1936e832ad0f549d8b055 100644 (file)
@@ -1,4 +1,4 @@
-/* grrobot - Ricochet Robot using GTK+ and Xr
+/* grrobot - Ricochet Robot using GTK+, libsvg-cairo, and cairo
  *
  * Copyright © 2003 Carl Worth
  *
@@ -45,15 +45,26 @@ typedef struct grr_game {
 
     GtkWidget *window;
     grr_board_view_t *board_view;
-    GtkWidget *command_entry;
+    GtkWidget *message_entry;
 
     GtkTextBuffer *message_buffer;
     GtkWidget *message_view;
     int msg_id;
     int last_move_msg_id;
-    rr_robot_t last_move_robot;    
+    rr_robot_t last_move_robot;
+
+    rr_gamestate_t state;
 } grr_game_t;
 
+static void
+grr_game_pass (grr_game_t *game);
+
+static void
+grr_game_zap (grr_game_t *game);
+
+static void
+grr_game_next (grr_game_t *game);
+
 static int
 grr_game_printf (grr_game_t *game, const char *fmt, ...);
 
@@ -64,7 +75,7 @@ static int
 grr_game_print (grr_game_t *game, const char *msg);
 
 static int
-grr_game_start_gui (grr_game_t *game);
+grr_game_start_gui (grr_game_t *game, const char *user);
 
 static void
 grr_game_read_notices (grr_game_t *game);
@@ -94,6 +105,8 @@ main (int argc, char **argv)
        }
        game.client = NULL;
     } else {
+       GPollFD *poll_fd;
+
        game.client = rr_client_create (args.host, args.port, args.user);
        if (game.client == NULL) {
            fprintf (stderr, "Failed connecting to %s:%s as %s\n",
@@ -119,6 +132,11 @@ main (int argc, char **argv)
        game.last_move_robot = RR_ROBOT_NONE;
 
        source = grr_game_notices_source_new (&game);
+       poll_fd = g_new (GPollFD, 1);
+       poll_fd->fd = rr_client_fd (game.client);
+       poll_fd->events = G_IO_IN;
+       g_source_add_poll (source, poll_fd);
+
        g_source_set_priority (source, GDK_PRIORITY_EVENTS);
        g_source_attach (source, NULL);
        g_source_unref (source);
@@ -128,7 +146,7 @@ main (int argc, char **argv)
        free (diagram);
     }
 
-    return grr_game_start_gui (&game);
+    return grr_game_start_gui (&game, args.user);
 }
 
 typedef struct _GrrNoticeSource {
@@ -210,13 +228,14 @@ grr_game_read_notices (grr_game_t *game)
            else
                fprintf (stderr, "Error during rr_client_next_notice: %s\n",
                         rr_status_str (status));
-           gtk_exit (1);
+           gtk_main_quit ();
+           return;
        }
        if (!notice) {
            fprintf (stderr, "Missing notice\n");
            continue;
        }
-
        switch (notice->type) {
        case RR_NOTICE_USER:
            grr_game_printf (game,
@@ -244,10 +263,17 @@ grr_game_read_notices (grr_game_t *game)
            gtk_widget_queue_draw (GTK_WIDGET (game->window));
            break;
        case RR_NOTICE_GAMESTATE:
+           game->state = notice->u.gamestate;
            grr_game_printf (game, "\nGame state changed to: %s.",
                             rr_gamestate_str (notice->u.gamestate));
            break;
        case RR_NOTICE_TURN:
+           /* XXX: Setting game->state here is goofy. Cleaner would
+               be to make the server send a NOTICE GAMESTATE NEW
+               here. */
+           game->state = RR_GAMESTATE_NEW;
+
+           grr_board_view_set_timer (game->board_view, 60.0);
            grr_game_print (game, "\nNew round!");
            rr_board_set_goal_target (board, notice->u.target);
            gtk_widget_queue_draw (GTK_WIDGET (game->window));
@@ -287,13 +313,13 @@ grr_game_read_notices (grr_game_t *game)
        case RR_NOTICE_MOVE:
            if (game->msg_id == game->last_move_msg_id
                && game->last_move_robot == notice->u.move.robot) {
-               game->last_move_msg_id = grr_game_printf (game, " %s",
-                                rr_direction_str (notice->u.move.direction));
+               game->last_move_msg_id = grr_game_printf (game, ",%c",
+                                rr_direction_char (notice->u.move.direction));
            } else {
-               game->last_move_msg_id = grr_game_printf (game, "\nMove #%d: %s %s",
+               game->last_move_msg_id = grr_game_printf (game, "\nMove #%d: %s %c",
                                 notice->u.move.count,
                                 rr_robot_str (notice->u.move.robot),
-                                rr_direction_str (notice->u.move.direction));
+                                rr_direction_char (notice->u.move.direction));
                game->last_move_robot = notice->u.move.robot;
            }
            break;
@@ -313,19 +339,27 @@ grr_game_read_notices (grr_game_t *game)
                             notice->u.number);
            break;
        case RR_NOTICE_ACTIVE:
+           grr_board_view_zap_timer (game->board_view);
            grr_game_printf (game, "\nUser %s now active to demonstrate solution in %d moves.",
-                              notice->u.bid.username,
+                            notice->u.bid.username,
                             notice->u.bid.number);
            break;
        case RR_NOTICE_TIMER:
-           grr_game_printf (game, "\nTime remaining: %d seconds.",
-                            notice->u.number);
+           grr_board_view_set_timer (game->board_view,
+                                     notice->u.number);
+           grr_board_view_start_timer (game->board_view);
            break;
        case RR_NOTICE_POSITION:
+       {
+           int x, y;
+           rr_board_find_robot (board, notice->u.position.robot, &x, &y);
            rr_board_add_robot (board, notice->u.position.robot,
                                notice->u.position.x, notice->u.position.y);
-           gtk_widget_queue_draw (GTK_WIDGET (game->window));
-           break;
+           grr_board_view_mark_damage (game->board_view, x, y);
+           grr_board_view_mark_damage (game->board_view,
+                                       notice->u.position.x, notice->u.position.y);
+       }
+       break;
        default:
            fprintf (stderr, "Unknown notice: %d\n", notice->type);
            break;
@@ -339,83 +373,161 @@ grr_game_key_press_callback (GtkWidget *widget,
                             GdkEventKey *event,
                             grr_game_t *game)
 {
-    if (GTK_WIDGET_HAS_FOCUS (GTK_WIDGET (game->command_entry))) {
+    if (GTK_WIDGET_HAS_FOCUS (GTK_WIDGET (game->message_entry))) {
        if (event->keyval == GDK_Escape) {
-           gtk_entry_set_text (GTK_ENTRY (game->command_entry), "");
+           gtk_entry_set_text (GTK_ENTRY (game->message_entry), "");
            gtk_widget_grab_focus (GTK_WIDGET (game->message_view));
-
            return TRUE;
        } else {
-           return FALSE;
+           if ((event->state & GDK_CONTROL_MASK) == 0)
+               return FALSE;
+       }
+    }
+
+    if (event->state & GDK_CONTROL_MASK) {
+       switch (event->keyval) {
+       case GDK_N:
+       case GDK_n:
+       case GDK_T:
+       case GDK_t:
+           grr_game_next (game);
+           return TRUE;
+       case GDK_P:
+       case GDK_p:
+           grr_game_pass (game);
+           return TRUE;
+       case GDK_Z:
+       case GDK_z:
+           grr_game_zap (game);
+           return TRUE;
+       case GDK_B:
+       case GDK_b:
+           grr_board_view_set_active_robot (game->board_view, RR_ROBOT_BLUE);
+           return TRUE;
+       case GDK_G:
+       case GDK_g:
+           grr_board_view_set_active_robot (game->board_view, RR_ROBOT_GREEN);
+           return TRUE;
+       case GDK_R:
+       case GDK_r:
+           grr_board_view_set_active_robot (game->board_view, RR_ROBOT_RED);
+           return TRUE;
+       case GDK_Y:
+       case GDK_y:
+           grr_board_view_set_active_robot (game->board_view, RR_ROBOT_YELLOW);
+           return TRUE;
+       case GDK_Q:
+       case GDK_q:
+           if (event->state & GDK_CONTROL_MASK) {
+               gtk_main_quit ();
+           }
+           return TRUE;
        }
     }
 
     switch (event->keyval) {
-    case GDK_B:
-    case GDK_b:
-       grr_board_view_set_active_robot (game->board_view, RR_ROBOT_BLUE);
-       break;
-    case GDK_G:
-    case GDK_g:
-       grr_board_view_set_active_robot (game->board_view, RR_ROBOT_GREEN);
-       break;
-    case GDK_R:
-    case GDK_r:
-       grr_board_view_set_active_robot (game->board_view, RR_ROBOT_RED);
-       break;
-    case GDK_Y:
-    case GDK_y:
-       grr_board_view_set_active_robot (game->board_view, RR_ROBOT_YELLOW);
-       break;
+    case GDK_BackSpace:
+       grr_board_view_undo (game->board_view);
+       return TRUE;
     case GDK_Up:
        grr_board_view_move_active (game->board_view, RR_DIRECTION_NORTH);
-       break;
+       return TRUE;
     case GDK_Right:
        grr_board_view_move_active (game->board_view, RR_DIRECTION_EAST);
-       break;
+       return TRUE;
     case GDK_Down:
        grr_board_view_move_active (game->board_view, RR_DIRECTION_SOUTH);
-       break;
+       return TRUE;
     case GDK_Left:
        grr_board_view_move_active (game->board_view, RR_DIRECTION_WEST);
-       break;
-    case GDK_BackSpace:
-       grr_board_view_undo (game->board_view);
-       break;
-    case GDK_space:
-    case GDK_slash:
-       gtk_widget_grab_focus (GTK_WIDGET (game->command_entry));
-       if (event->keyval == GDK_slash) {
-           int pos = -1;
-           gtk_editable_insert_text (GTK_EDITABLE (game->command_entry),
-                                     "/", 1, &pos);
-           gtk_editable_set_position (GTK_EDITABLE (game->command_entry), -1);
-       }
-       break;
-    case GDK_Q:
-    case GDK_q:
-       if (event->state & GDK_CONTROL_MASK) {
-           gtk_exit (0);
-       }
-       break;
+       return TRUE;
+    case GDK_Control_L:
+    case GDK_Control_R:
+       return TRUE;
     }
 
-    return TRUE;
+    gtk_widget_grab_focus (GTK_WIDGET (game->message_entry));
+    return FALSE;
+}
+
+/* XXX: Messy, messy. I can't seem to make up my mind whether client
+   functions like such as these should be called here or in
+   grr_board_view. */
+static void
+grr_game_pass (grr_game_t *game)
+{
+    if (game->client == NULL)
+       return;
+
+    if (game->state == RR_GAMESTATE_SHOW)
+       rr_client_pass (game->client);
+    else
+       rr_client_abandon (game->client);
+}
+
+static void
+grr_game_zap (grr_game_t *game)
+{
+    if (game->client == NULL)
+       return;
+
+    rr_client_nobid (game->client);
+}
+
+static void
+grr_game_next (grr_game_t *game)
+{
+    if (game->client == NULL)
+       return;
+
+    rr_client_turn (game->client);
+}
+
+static void
+grr_game_pass_callback (GtkWidget *widget,
+                       grr_game_t *game)
+{
+    grr_game_pass (game);
 }
 
 static void
-grr_game_entry_callback (GtkWidget *widget,
-                        grr_game_t *game)
+grr_game_zap_callback (GtkWidget *widget,
+                       grr_game_t *game)
+{
+    grr_game_zap (game);
+}
+
+static void
+grr_game_next_callback (GtkWidget *widget,
+                       grr_game_t *game)
+{
+    grr_game_next (game);
+}
+
+static void
+grr_game_message_entry_callback (GtkWidget *widget,
+                                grr_game_t *game)
 {
     rr_status_t status;
     const gchar *entry_text;
     char **response;
+    char *end;
+    long bid;
     int i;
 
-    entry_text = gtk_entry_get_text (GTK_ENTRY (game->command_entry));
+    if (game->client == NULL)
+       return;
+
+    entry_text = gtk_entry_get_text (GTK_ENTRY (game->message_entry));
 
-    if (entry_text && entry_text[0] && game->client) {
-       if (entry_text[0] == '/') {
+    if (entry_text && entry_text[0]) {
+       bid = strtol (entry_text, &end, 10);
+       if (*end == '\0' || isspace (*end)) {
+           if (bid == 0)
+               rr_client_revoke (game->client);
+           else
+               rr_client_bid (game->client, bid);
+       } else  if (entry_text[0] == '/') {
            status = rr_client_request (game->client, entry_text + 1, &response);
            if (status) {
                grr_game_printf (game, "\nERROR: %s.", rr_status_str (status));
@@ -431,7 +543,7 @@ grr_game_entry_callback (GtkWidget *widget,
        }
     }
 
-    gtk_entry_set_text (GTK_ENTRY (game->command_entry), "");
+    gtk_entry_set_text (GTK_ENTRY (game->message_entry), "");
     gtk_widget_grab_focus (GTK_WIDGET (game->message_view));
 
 /* XXX: Huh? Why is this triggering valgrind?
@@ -495,15 +607,22 @@ grr_game_print (grr_game_t *game, const char *msg)
 }
 
 static int
-grr_game_start_gui (grr_game_t *game)
+grr_game_start_gui (grr_game_t *game, const char *user)
 {
     GtkWidget *board_frame;
     GtkWidget *vpaned;
     GtkWidget *sw;
     GtkWidget *vbox;
+    GtkWidget *hbox;
     GtkWidget *window;
     GtkWidget *message_view;
-    GtkWidget *command_entry;
+    GtkWidget *message_entry;
+    GtkWidget *pass_button;
+    GtkWidget *zap_button;
+    GtkWidget *next_button;
+    GtkWidget *user_label;
+
+    game->state = RR_GAMESTATE_NEW;
 
     game->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
     window = game->window;
@@ -527,7 +646,6 @@ grr_game_start_gui (grr_game_t *game)
        gtk_text_buffer_create_mark (game->message_buffer,
                                     "end", &iter, FALSE);
        game->message_view = gtk_text_view_new_with_buffer (game->message_buffer);
-       game->command_entry = gtk_entry_new ();
 
        vpaned = gtk_vpaned_new ();
        gtk_container_set_border_width (GTK_CONTAINER (vpaned), 0);
@@ -558,13 +676,53 @@ grr_game_start_gui (grr_game_t *game)
        }
        gtk_widget_show (vpaned);
 
-       command_entry = game->command_entry;
-       gtk_box_pack_end (GTK_BOX (vbox), command_entry,
-                         FALSE, FALSE, 0);
-       gtk_widget_show (command_entry);
-       g_signal_connect (G_OBJECT (command_entry), "activate",
-                         G_CALLBACK (grr_game_entry_callback),
-                         (gpointer) game);
+       hbox = gtk_hbox_new (FALSE, 1);
+       gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+       {
+           user_label = gtk_label_new (user);
+           gtk_box_pack_start (GTK_BOX (hbox), user_label,
+                               FALSE, FALSE, 1);
+           gtk_widget_show (user_label);
+
+           game->message_entry = gtk_entry_new ();
+           message_entry = game->message_entry;
+           gtk_box_pack_start (GTK_BOX (hbox), message_entry,
+                               TRUE, TRUE, 1);
+           gtk_widget_show (message_entry);
+           g_signal_connect (G_OBJECT (message_entry), "activate",
+                             G_CALLBACK (grr_game_message_entry_callback),
+                             (gpointer) game);
+       }
+       gtk_widget_show (hbox);
+
+       hbox = gtk_hbox_new (FALSE, 1);
+       gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+       {
+           pass_button = gtk_button_new_with_label ("Pass");
+           gtk_box_pack_start (GTK_BOX (hbox), pass_button,
+                               FALSE, FALSE, 1);
+           gtk_widget_show (pass_button);
+           g_signal_connect (G_OBJECT (pass_button), "clicked",
+                             G_CALLBACK (grr_game_pass_callback),
+                             (gpointer) game);
+
+           zap_button = gtk_button_new_with_label ("Zap timer");
+           gtk_box_pack_start (GTK_BOX (hbox), zap_button,
+                               FALSE, FALSE, 1);
+           gtk_widget_show (zap_button);
+           g_signal_connect (G_OBJECT (zap_button), "clicked",
+                             G_CALLBACK (grr_game_zap_callback),
+                             (gpointer) game);
+
+           next_button = gtk_button_new_with_label ("Next turn");
+           gtk_box_pack_start (GTK_BOX (hbox), next_button,
+                               FALSE, FALSE, 1);
+           gtk_widget_show (next_button);
+           g_signal_connect (G_OBJECT (next_button), "clicked",
+                             G_CALLBACK (grr_game_next_callback),
+                             (gpointer) game);
+       }
+       gtk_widget_show (hbox);
     }
     gtk_widget_show (vbox);