X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=src%2Fgrrobot.c;h=98f14f2582fad574e77c4317ed16a6fd2a13318b;hb=1ce9df4b8865ac2995524272a1c2ab4071d9921d;hp=332cca55206501cda7a77790f9ec1ad1cfaf6132;hpb=5edb5be4a163427c619a98e68255eb8331f39e3b;p=grrobot diff --git a/src/grrobot.c b/src/grrobot.c index 332cca5..98f14f2 100644 --- a/src/grrobot.c +++ b/src/grrobot.c @@ -30,9 +30,12 @@ #include #include +#include + #include #include "grr_board_view.h" +#include "grr_util.h" #include "args.h" typedef struct grr_game { @@ -41,29 +44,40 @@ typedef struct grr_game { GtkWidget *window; GtkWidget *board_view; + GtkWidget *command_entry; + GtkTextBuffer *message_buffer; GtkWidget *message_view; - GtkWidget *command_entry; - GtkTextIter message_iter; + int msg_id; + int last_move_msg_id; + rr_robot_t last_move_robot; } grr_game_t; static int -grr_game_start_gui (grr_game_t *game); +grr_game_printf (grr_game_t *game, const char *fmt, ...); -static void -grr_game_read_callback (gpointer data, - gint fd, - GdkInputCondition condition); +static int +grr_game_vprintf (grr_game_t *game, const char *fmt, va_list ap); + +static int +grr_game_print (grr_game_t *game, const char *msg); + +static int +grr_game_start_gui (grr_game_t *game); static void grr_game_read_notices (grr_game_t *game); +static GSource * +grr_game_notices_source_new (grr_game_t *game); + int main (int argc, char **argv) { args_t args; rr_status_t status; grr_game_t game; + GSource *source; char *diagram; @@ -84,11 +98,14 @@ main (int argc, char **argv) } game.board = rr_board_create (16, 16); + game.msg_id = 0; + game.last_move_msg_id = 0; + game.last_move_robot = RR_ROBOT_NONE; - gdk_input_add (rr_client_fd (game.client), - GDK_INPUT_READ, - grr_game_read_callback, - &game); + source = grr_game_notices_source_new (&game); + g_source_set_priority (source, GDK_PRIORITY_EVENTS); + g_source_attach (source, NULL); + g_source_unref (source); rr_client_show (game.client, &diagram); rr_board_parse (game.board, diagram); @@ -97,14 +114,68 @@ main (int argc, char **argv) return grr_game_start_gui (&game); } -static void -grr_game_read_callback (gpointer data, - gint fd, - GdkInputCondition condition) +typedef struct _GrrNoticeSource { + GSource source; + grr_game_t *game; +} GrrNoticeSource; + +static gboolean +grr_game_notices_prepare (GSource *source, + gint *timeout) { - grr_game_t *game = data; + GrrNoticeSource *src = (GrrNoticeSource*)source; + grr_game_t *game = src->game; + gboolean retval; + + GDK_THREADS_ENTER (); + *timeout = -1; + retval = rr_client_notice_pending (game->client); + GDK_THREADS_LEAVE (); + + return retval; +} + +static gboolean +grr_game_notices_check (GSource *source) +{ + GrrNoticeSource *src = (GrrNoticeSource*)source; + grr_game_t *game = src->game; + gboolean retval; + + GDK_THREADS_ENTER (); + retval = rr_client_notice_pending (game->client); + GDK_THREADS_LEAVE (); + return retval; +} + +static gboolean +grr_game_notices_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + GrrNoticeSource *src = (GrrNoticeSource*)source; + grr_game_t *game = src->game; + grr_game_read_notices (game); + + return TRUE; +} + +static GSourceFuncs game_notices_funcs = { + grr_game_notices_prepare, + grr_game_notices_check, + grr_game_notices_dispatch, + NULL +}; + +static GSource * +grr_game_notices_source_new (grr_game_t *game) +{ + GSource *source = g_source_new (&game_notices_funcs, sizeof (GrrNoticeSource)); + GrrNoticeSource *src = (GrrNoticeSource*)source; + src->game = game; + return source; } static void @@ -112,62 +183,128 @@ grr_game_read_notices (grr_game_t *game) { rr_board_t *board = game->board; rr_status_t status; - char **notice_s; rr_notice_t *notice; - int i; while (rr_client_notice_pending (game->client)) { - status = rr_client_next_notice (game->client, ¬ice_s); + status = rr_client_next_notice (game->client, ¬ice); if (status) { - fprintf (stderr, "Error during rr_client_notice: %s\n", + fprintf (stderr, "Error during rr_client_next_notice: %s\n", rr_status_str (status)); gtk_exit (1); return; } - for (i=0; notice_s[i]; i++) { - gtk_text_buffer_insert_at_cursor (game->message_buffer, notice_s[i], -1); - gtk_text_buffer_insert_at_cursor (game->message_buffer, " ", -1); - } - gtk_text_buffer_insert_at_cursor (game->message_buffer, "\n", -1); - gtk_text_buffer_get_iter_at_offset (game->message_buffer, - &game->message_iter, - -1); - gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (game->message_view), - &game->message_iter, - 0.0, FALSE, 0.0, 0.0); - notice = rr_client_parse_notice (game->client, notice_s); switch (notice->type) { case RR_NOTICE_USER: + grr_game_printf (game, + "\nGLOBAL: %s has connected.", notice->u.string); + break; case RR_NOTICE_QUIT: + grr_game_printf (game, + "\nGLOBAL: %s has disconnected.", notice->u.string); + break; case RR_NOTICE_GAME: + grr_game_printf (game, + "\nGLOBAL: New game available: %s.", notice->u.string); + break; case RR_NOTICE_DISPOSE: + grr_game_printf (game, + "\nGLOBAL: Game %s has been disposed.", notice->u.string); + break; case RR_NOTICE_MESSAGE: + grr_game_printf (game, "\n%s> %s", + notice->u.message.username, + notice->u.message.text); + break; case RR_NOTICE_GAMESTATE: + grr_game_printf (game, "\nGame state changed to: %s.", + rr_gamestate_str (notice->u.gamestate)); + break; + case RR_NOTICE_TURN: + grr_game_print (game, "\nNew round!"); + rr_board_set_goal_target (board, notice->u.target); + gtk_widget_queue_draw (GTK_WIDGET (game->window)); + break; + case RR_NOTICE_GAMEOVER: + { + char *diagram; + grr_game_printf (game, "\nGame over. New game will begin now."); + /* XXX: Can drop this when the BOARD NOTICE is added in the server */ + rr_client_show (game->client, &diagram); + rr_board_parse (board, diagram); + free (diagram); + gtk_widget_queue_draw (GTK_WIDGET (game->window)); + } + break; case RR_NOTICE_JOIN: + grr_game_printf (game, "\nUser %s has joined the game.", + notice->u.string); + break; case RR_NOTICE_WATCH: + grr_game_printf (game, "\nUser %s has started watching the game.", + notice->u.string); + break; case RR_NOTICE_PART: + grr_game_printf (game, "\nUser %s has left the game.", + notice->u.string); + break; case RR_NOTICE_BID: + grr_game_printf (game, "\nUser %s bids %d.", + notice->u.bid.username, + notice->u.bid.number); + break; case RR_NOTICE_REVOKE: - case RR_NOTICE_TIMER: + grr_game_printf (game, "\nUser %s revokes previous bid.", + notice->u.string); + break; case RR_NOTICE_ABANDON: + grr_game_printf (game, "\nUser %s is willing to abandon this turn.", + notice->u.string); + break; case RR_NOTICE_NOBID: - case RR_NOTICE_ACTIVE: + grr_game_printf (game, "\nUser %s sees no point in bidding further this round", + notice->u.string); + break; 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)); + } else { + game->last_move_msg_id = grr_game_printf (game, "\nMove #%d: %s %s", + notice->u.move.count, + rr_robot_str (notice->u.move.robot), + rr_direction_str (notice->u.move.direction)); + game->last_move_robot = notice->u.move.robot; + } + break; case RR_NOTICE_UNDO: + grr_game_print (game, "\nUNDO"); + break; case RR_NOTICE_RESET: + grr_game_print (game, "\nRESET"); + break; case RR_NOTICE_SCORE: + grr_game_printf (game, "\nScore for %s: %d.", + notice->u.bid.username, + notice->u.bid.number); + break; case RR_NOTICE_ACTIVATE: - /* XXX: Need to actually handle many of these. */ - fprintf (stderr, "Warning: Ignoring notice of type %d\n", notice->type); + grr_game_printf (game, "\nYour turn.", + notice->u.number); break; - case RR_NOTICE_POSITION: - rr_board_position (board, notice->u.position.robot, - notice->u.position.x, notice->u.position.y); - gtk_widget_queue_draw (GTK_WIDGET (game->window)); + case RR_NOTICE_ACTIVE: + grr_game_printf (game, "\nUser %s now active to demonstrate solution in %d moves.", + notice->u.bid.username, + notice->u.bid.number); break; - case RR_NOTICE_TURN: - rr_board_set_goal_target (board, notice->u.target); + case RR_NOTICE_TIMER: + grr_game_printf (game, "\nTime remaining: %d seconds.", + notice->u.number); + break; + case RR_NOTICE_POSITION: + rr_board_position_robot (board, notice->u.position.robot, + notice->u.position.x, notice->u.position.y); gtk_widget_queue_draw (GTK_WIDGET (game->window)); break; } @@ -176,28 +313,89 @@ grr_game_read_notices (grr_game_t *game) } static void -command_callback (GtkWidget *widget, - grr_game_t *game) +grr_game_entry_callback (GtkWidget *widget, + grr_game_t *game) { + rr_status_t status; const gchar *entry_text; char **response; + int i; entry_text = gtk_entry_get_text (GTK_ENTRY (game->command_entry)); - gtk_text_buffer_insert_at_cursor (game->message_buffer, entry_text, -1); - gtk_text_buffer_insert_at_cursor (game->message_buffer, "\n", -1); - rr_client_request (game->client, entry_text, &response); + status = rr_client_request (game->client, entry_text, &response); + if (status) { + grr_game_printf (game, "\nERROR: %s.", rr_status_str (status)); + } else { + if (response[0]) { + grr_game_print (game, "\n"); + for (i=0; response[i]; i++) + grr_game_printf (game, "%s ", response[i]); + } + } gtk_entry_set_text (GTK_ENTRY (game->command_entry), ""); - grr_game_read_notices (game); - /* XXX: Huh? Why is this triggering valgrind? free (response); */ } +static int +grr_game_printf (grr_game_t *game, const char *fmt, ...) +{ + int msg_id; + + va_list ap; + + va_start (ap, fmt); + msg_id = grr_game_vprintf (game, fmt, ap); + va_end (ap); + + return msg_id; +} + +static int +grr_game_vprintf (grr_game_t *game, const char *fmt, va_list ap) +{ + char *msg; + int msg_id; + + grr_sprintf_alloc_va (&msg, fmt, ap); + if (msg == NULL) + return 0; + + msg_id = grr_game_print (game, msg); + + free (msg); + + return msg_id; +} + +static int +grr_game_print (grr_game_t *game, const char *msg) +{ + GtkTextIter iter; + GtkTextMark *mark; + + /* There might be a lighter way to do this, but this seems to + work. */ + + gtk_text_buffer_get_end_iter (game->message_buffer, &iter); + + mark = gtk_text_buffer_get_mark (game->message_buffer, "end"); + + gtk_text_buffer_move_mark (game->message_buffer, mark, &iter); + + gtk_text_buffer_insert (game->message_buffer, &iter, msg, -1); + + gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (game->message_view), + mark, 0.0, TRUE, 0.0, 1.0); + + return ++game->msg_id; +} + static int grr_game_start_gui (grr_game_t *game) { @@ -219,54 +417,61 @@ grr_game_start_gui (grr_game_t *game) G_CALLBACK (exit), NULL); gtk_container_set_border_width (GTK_CONTAINER (window), 0); - vpaned = gtk_vpaned_new (); - gtk_container_set_border_width (GTK_CONTAINER (vpaned), 5); - gtk_container_add (GTK_CONTAINER (window), vpaned); + vbox = gtk_vbox_new (FALSE, 1); + gtk_container_add (GTK_CONTAINER (window), vbox); { - board_frame = gtk_aspect_frame_new (NULL, 0.5, 0.5, 1.0, FALSE); - gtk_paned_pack1 (GTK_PANED (vpaned), board_frame, TRUE, TRUE); + GtkTextIter iter; + game->board_view = grr_board_view_new (game->board); + grr_board_view_set_client (GRR_BOARD_VIEW (game->board_view), game->client); + game->message_buffer = gtk_text_buffer_new (NULL); + gtk_text_buffer_get_iter_at_offset (game->message_buffer, + &iter, -1); + 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); + gtk_box_pack_start (GTK_BOX (vbox), vpaned, + TRUE, TRUE, 0); { - game->board_view = grr_board_view_new (game->board); + board_frame = gtk_aspect_frame_new (NULL, 0.5, 0.5, 1.0, FALSE); + gtk_paned_pack1 (GTK_PANED (vpaned), board_frame, TRUE, TRUE); + { + gtk_container_add (GTK_CONTAINER (board_frame), game->board_view); + gtk_widget_show (game->board_view); + } + gtk_widget_show (board_frame); - gtk_container_add (GTK_CONTAINER (board_frame), game->board_view); - gtk_widget_show (game->board_view); - } - gtk_widget_show (board_frame); - - vbox = gtk_vbox_new (FALSE, 1); - gtk_paned_pack2 (GTK_PANED (vpaned), vbox, FALSE, TRUE); - { sw = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); - gtk_container_add (GTK_CONTAINER (vbox), sw); + gtk_paned_pack2 (GTK_PANED (vpaned), sw, FALSE, TRUE); + { + message_view = game->message_view; + gtk_text_view_set_editable (GTK_TEXT_VIEW (message_view), FALSE); + gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (message_view), FALSE); + gtk_container_add (GTK_CONTAINER (sw), message_view); + gtk_widget_show (message_view); + } gtk_widget_show (sw); - - game->message_buffer = gtk_text_buffer_new (NULL); - game->message_view = gtk_text_view_new_with_buffer (game->message_buffer); - message_view = game->message_view; - gtk_text_view_set_editable (GTK_TEXT_VIEW (message_view), FALSE); - gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (message_view), FALSE); - gtk_container_add (GTK_CONTAINER (sw), message_view); - gtk_widget_show (message_view); - - game->command_entry = gtk_entry_new (); - command_entry = game->command_entry; - gtk_container_add (GTK_CONTAINER (vbox), command_entry); - gtk_widget_show (command_entry); - g_signal_connect (G_OBJECT (command_entry), "activate", - G_CALLBACK (command_callback), - (gpointer) game); } - gtk_widget_show (vbox); + 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); } - gtk_widget_show (vpaned); + gtk_widget_show (vbox); gtk_widget_show (window); - grr_game_read_notices (game); - gtk_main (); return 0;