From cac5a27c8be36ded1bba3a790cf8723f1affc2a4 Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Fri, 11 Jul 2003 01:11:42 +0000 Subject: [PATCH] Added keybindings. Can now play a board from a file without any server. --- ChangeLog | 22 +++++ src/args.c | 18 ++-- src/args.h | 2 + src/grr_board_view.c | 57 +++++++++---- src/grr_board_view.h | 13 ++- src/grrobot.c | 185 ++++++++++++++++++++++++++++++----------- src/svg/grr_icon_svg.h | 2 +- 7 files changed, 223 insertions(+), 76 deletions(-) diff --git a/ChangeLog b/ChangeLog index 84d46ab..fe01989 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,25 @@ +2003-07-09 Carl Worth + + * src/grrobot.c (grr_game_read_notices): Added support for + RR_NOTICE_BOARD, (cleans up ugly GAMEOVER handling), and should + avoid nasty inconsistent display problems if the user issues a NEW + command. + (grr_game_read_notices): Tracked some librr API changes. + (grr_game_key_press_callback): Added some global keybindings: RGBY + to select robot, arrow to move, BackSpace to undo a move, Ctrl-Q + to quit, Space to message, and '/' to enter a command. + + * src/grr_board_view.c (grr_board_view_set_active_robot): + (grr_board_view_move_active): + (grr_board_view_undo): Added an interface to grr_board_view for + moving robots. + (grr_board_view_expose): Don't display target in board center + unless it is a 16x16 board. + + * src/args.c (parse_opt): Added support to play without a server + using a board from a file given on the command-line. + (args_parse): Switch to long_only-style command-line parsing. + 2003-06-19 Carl Worth * src/grrobot.c (grr_game_print): Centralized game message diff --git a/src/args.c b/src/args.c index aee5e94..1f787d1 100644 --- a/src/args.c +++ b/src/args.c @@ -35,8 +35,7 @@ const char *argp_program_bug_address = ""; static char doc[] = "grrobot - Ricochet Robot using GTK+ and Xr"; -/* XXX: SAMPLE: */ -static char args_doc[] = ""; +static char args_doc[] = "[file]"; static struct argp_option options[] = { /* name, key, arg, flags, doc */ @@ -71,16 +70,9 @@ parse_opt (int key, char *arg, struct argp_state *state) break; case ARGP_KEY_ARG: - argp_usage (state); + args->file = arg; break; -/* - case ARGP_KEY_END: - if (state->arg_num < 1) - argp_usage (state); - break; -*/ - default: return ARGP_ERR_UNKNOWN; } @@ -107,5 +99,9 @@ args_parse(args_t *args, int argc, char *argv[]) args->game = ARGS_GAME_DEFAULT; args->watch = 0; - return argp_parse (&argp, argc, argv, 0, 0, args); + args->file = NULL; + + return argp_parse (&argp, argc, argv, + ARGP_LONG_ONLY, + NULL, args); } diff --git a/src/args.h b/src/args.h index 423411a..408c13d 100644 --- a/src/args.h +++ b/src/args.h @@ -44,6 +44,8 @@ typedef struct args char *port; char *user; char *game; + + char *file; int watch; } args_t; diff --git a/src/grr_board_view.c b/src/grr_board_view.c index 863ac20..b48c291 100644 --- a/src/grr_board_view.c +++ b/src/grr_board_view.c @@ -150,7 +150,7 @@ grr_board_view_init (grr_board_view_t *view) } } -GtkWidget* +grr_board_view_t * grr_board_view_new (rr_board_t *board) { grr_board_view_t *view; @@ -161,7 +161,7 @@ grr_board_view_new (rr_board_t *board) grr_board_view_update (view); - return GTK_WIDGET (view); + return view; } void @@ -172,6 +172,36 @@ grr_board_view_set_client (grr_board_view_t *view, rr_client_t *client) view->client = client; } +void +grr_board_view_set_active_robot (grr_board_view_t *view, rr_robot_t robot) +{ + g_return_if_fail (view != NULL); + + view->active_robot = robot; +} + +void +grr_board_view_move_active (grr_board_view_t *view, rr_direction_t dir) +{ + if (view->client) { + rr_client_move (view->client, view->active_robot, dir); + } else { + rr_board_move (view->board, view->active_robot, dir); + gtk_widget_queue_draw (GTK_WIDGET (view)); + } +} + +void +grr_board_view_undo (grr_board_view_t *view) +{ + if (view->client) { + rr_client_undo (view->client); + } else { + rr_board_undo (view->board); + gtk_widget_queue_draw (GTK_WIDGET (view)); + } +} + static void grr_board_view_destroy (GtkObject *object) { @@ -458,15 +488,16 @@ grr_board_view_expose (GtkWidget *widget, } /* Draw goal target in center of board */ - XrSave (xrs); - { + /* XXX: Not a perfect heuristic. Should check for the vacant box too. */ + if (view->board_width == 16 && view->board_height == 16) { + XrSave (xrs); XrTranslate (xrs, (view->board_width / 2 - 1) * view->cell_width, (view->board_height / 2 - 1) * view->cell_height); grr_board_view_draw_cell (view, xrs, goal_target, goal_target, view->cell_width * 2, view->cell_height * 2); + XrRestore (xrs); } - XrRestore (xrs); /* Draw walls */ for (j=0; j < view->board_height; j++) { @@ -495,10 +526,9 @@ grr_board_view_pointer_coords_to_grid (grr_board_view_t *view, *grid_y = (pointer_y - view->board_pad_y) / view->cell_height; } - static gint grr_board_view_button_press (GtkWidget *widget, - GdkEventButton *event) + GdkEventButton *event) { grr_board_view_t *view; rr_cell_t cell; @@ -520,7 +550,7 @@ grr_board_view_button_press (GtkWidget *widget, gtk_grab_add (widget); view->button = event->button; - view->drag_robot = RR_CELL_GET_ROBOT (cell); + grr_board_view_set_active_robot (view, RR_CELL_GET_ROBOT (cell)); grr_board_view_update_mouse (view, event->x, event->y); } @@ -551,7 +581,7 @@ grr_board_view_button_release (GtkWidget *widget, grr_board_view_pointer_coords_to_grid (view, event->x, event->y, &x, &y); - rr_board_find_robot (view->board, view->drag_robot, &robot_x, &robot_y); + rr_board_find_robot (view->board, view->active_robot, &robot_x, &robot_y); dx = x - robot_x; dy = y - robot_y; if (dx == 0 && dy == 0) @@ -567,11 +597,10 @@ grr_board_view_button_release (GtkWidget *widget, dir = RR_DIRECTION_SOUTH; else dir = RR_DIRECTION_NORTH; - - if (view->client) - rr_client_move (view->client, view->drag_robot, dir); - return FALSE; + grr_board_view_move_active (view, dir); + + return FALSE; } static gint @@ -623,7 +652,7 @@ grr_board_view_update_mouse (grr_board_view_t *view, gint x, gint y) g_return_if_fail (view != NULL); g_return_if_fail (GRR_IS_BOARD_VIEW (view)); - /* XXX: Should draw a robot here */ + /* XXX: Perhaps should draw a robot here */ } static void diff --git a/src/grr_board_view.h b/src/grr_board_view.h index 523cd25..706a568 100644 --- a/src/grr_board_view.h +++ b/src/grr_board_view.h @@ -58,7 +58,7 @@ struct grr_board_view int cell_width, cell_height; - rr_robot_t drag_robot; + rr_robot_t active_robot; rr_client_t *client; @@ -80,7 +80,7 @@ struct grr_board_view_class GtkWidgetClass parent_class; }; -GtkWidget* +grr_board_view_t * grr_board_view_new (rr_board_t *board); void @@ -89,6 +89,15 @@ grr_board_view_set_client (grr_board_view_t *view, rr_client_t *client); GtkType grr_board_view_get_type (void); +void +grr_board_view_set_active_robot (grr_board_view_t *view, rr_robot_t robot); + +void +grr_board_view_move_active (grr_board_view_t *view, rr_direction_t dir); + +void +grr_board_view_undo (grr_board_view_t *view); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/grrobot.c b/src/grrobot.c index 209c599..a56932d 100644 --- a/src/grrobot.c +++ b/src/grrobot.c @@ -33,6 +33,7 @@ #include #include +#include #include "grr_board_view.h" #include "grr_util.h" @@ -43,7 +44,7 @@ typedef struct grr_game { rr_board_t *board; GtkWidget *window; - GtkWidget *board_view; + grr_board_view_t *board_view; GtkWidget *command_entry; GtkTextBuffer *message_buffer; @@ -85,38 +86,47 @@ main (int argc, char **argv) args_parse (&args, argc, argv); - 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", - args.host, args.port, args.user); - return 1; - } - - if (args.watch) { - status = rr_client_watch (game.client, args.game); - if (status == RR_STATUS_NO_GAME) { - fprintf (stderr, "No game %s to watch\n", args.game); + if (args.file) { + game.board = rr_board_create_from_file (args.file); + if (game.board == NULL) { + fprintf (stderr, "Failed to parse board from %s\n", args.file); + return 1; } + game.client = NULL; } else { - status = rr_client_join (game.client, args.game); - if (status == RR_STATUS_NO_GAME) { - status = rr_client_new (game.client, args.game); + 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", + args.host, args.port, args.user); + return 1; } - } - game.board = rr_board_create (16, 16); - game.msg_id = 0; - game.last_move_msg_id = 0; - game.last_move_robot = RR_ROBOT_NONE; + if (args.watch) { + status = rr_client_watch (game.client, args.game); + if (status == RR_STATUS_NO_GAME) { + fprintf (stderr, "No game %s to watch\n", args.game); + } + } else { + status = rr_client_join (game.client, args.game); + if (status == RR_STATUS_NO_GAME) { + status = rr_client_new (game.client, args.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); + game.board = rr_board_create (16, 16); + game.msg_id = 0; + game.last_move_msg_id = 0; + game.last_move_robot = RR_ROBOT_NONE; - rr_client_show (game.client, &diagram); - rr_board_parse (game.board, diagram); - free (diagram); + 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); + free (diagram); + } return grr_game_start_gui (&game); } @@ -229,6 +239,10 @@ grr_game_read_notices (grr_game_t *game) notice->u.message.username, notice->u.message.text); break; + case RR_NOTICE_BOARD: + rr_board_parse (board, notice->u.string); + gtk_widget_queue_draw (GTK_WIDGET (game->window)); + break; case RR_NOTICE_GAMESTATE: grr_game_printf (game, "\nGame state changed to: %s.", rr_gamestate_str (notice->u.gamestate)); @@ -239,16 +253,8 @@ grr_game_read_notices (grr_game_t *game) 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; + break; case RR_NOTICE_JOIN: grr_game_printf (game, "\nUser %s has joined the game.", notice->u.string); @@ -316,15 +322,87 @@ grr_game_read_notices (grr_game_t *game) 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); + 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; + default: + fprintf (stderr, "Unknown notice: %d\n", notice->type); + break; } free (notice); } } +static gboolean +grr_game_key_press_callback (GtkWidget *widget, + GdkEventKey *event, + grr_game_t *game) +{ + if (GTK_WIDGET_HAS_FOCUS (GTK_WIDGET (game->command_entry))) { + if (event->keyval == GDK_Escape) { + gtk_entry_set_text (GTK_ENTRY (game->command_entry), ""); + gtk_widget_grab_focus (GTK_WIDGET (game->message_view)); + + return TRUE; + } else { + return FALSE; + } + } + + 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_Up: + grr_board_view_move_active (game->board_view, RR_DIRECTION_NORTH); + break; + case GDK_Right: + grr_board_view_move_active (game->board_view, RR_DIRECTION_EAST); + break; + case GDK_Down: + grr_board_view_move_active (game->board_view, RR_DIRECTION_SOUTH); + break; + 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; +} + static void grr_game_entry_callback (GtkWidget *widget, grr_game_t *game) @@ -336,18 +414,25 @@ grr_game_entry_callback (GtkWidget *widget, entry_text = gtk_entry_get_text (GTK_ENTRY (game->command_entry)); - 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]); + if (entry_text && entry_text[0] && game->client) { + 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)); + } else { + if (response[0]) { + grr_game_print (game, "\n"); + for (i=0; response[i]; i++) + grr_game_printf (game, "%s ", response[i]); + } + } + } else { + rr_client_message (game->client, entry_text); } } gtk_entry_set_text (GTK_ENTRY (game->command_entry), ""); + gtk_widget_grab_focus (GTK_WIDGET (game->message_view)); /* XXX: Huh? Why is this triggering valgrind? free (response); @@ -452,8 +537,8 @@ grr_game_start_gui (grr_game_t *game) 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_container_add (GTK_CONTAINER (board_frame), GTK_WIDGET (game->board_view)); + gtk_widget_show (GTK_WIDGET (game->board_view)); } gtk_widget_show (board_frame); @@ -483,6 +568,10 @@ grr_game_start_gui (grr_game_t *game) } gtk_widget_show (vbox); + g_signal_connect (G_OBJECT (window), "key_press_event", + G_CALLBACK (grr_game_key_press_callback), + (gpointer) game); + gtk_widget_show (window); gtk_main (); diff --git a/src/svg/grr_icon_svg.h b/src/svg/grr_icon_svg.h index 40facfa..6ac8a1a 100644 --- a/src/svg/grr_icon_svg.h +++ b/src/svg/grr_icon_svg.h @@ -1,4 +1,4 @@ -static char GRR_ICON_SVG_CELL[] = "\012\012\012\040\040\012\012"; +static char GRR_ICON_SVG_CELL[] = "\012\012\012\040\040\012\012"; static char GRR_ICON_SVG_ROBOT_BLUE[] = "\015\012\015\012\015\012\015\012\011Tux\015\012\011The\040Linux\040Penguin\040in\040SVG.\015\012\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\015\012"; static char GRR_ICON_SVG_ROBOT_GREEN[] = "\015\012\015\012\015\012\015\012\011Tux\015\012\011The\040Linux\040Penguin\040in\040SVG.\015\012\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\015\012"; static char GRR_ICON_SVG_ROBOT_RED[] = "\015\012\015\012\015\012\015\012\011Tux\015\012\011The\040Linux\040Penguin\040in\040SVG.\015\012\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\011\015\012\011\011\015\012\011\015\012\015\012"; -- 2.43.0