-/* grrobot - Ricochet Robot using GTK+ and Xr
+/* grrobot - Ricochet Robot using GTK+, libsvg-cairo, and cairo
*
* Copyright © 2003 Carl Worth
*
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
+
#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
#include "grr_board_view.h"
+#include "grr_util.h"
#include "args.h"
typedef struct grr_game {
rr_board_t *board;
GtkWidget *window;
- GtkWidget *board_view;
+ grr_board_view_t *board_view;
+ GtkWidget *message_entry;
+
GtkTextBuffer *message_buffer;
GtkWidget *message_view;
- GtkWidget *command_entry;
+ int msg_id;
+ int last_move_msg_id;
+ 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, ...);
+
+static int
+grr_game_vprintf (grr_game_t *game, const char *fmt, va_list ap);
+
static int
-do_gui (grr_game_t *game);
+grr_game_print (grr_game_t *game, const char *msg);
-void
-game_callback (gpointer data,
- gint fd,
- GdkInputCondition condition);
+static int
+grr_game_start_gui (grr_game_t *game, const char *user);
+
+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;
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.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 {
+ 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",
+ 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);
+ }
+ } else {
+ status = rr_client_join (game.client, args.game);
+ if (status == RR_STATUS_NO_GAME) {
+ status = rr_client_new (game.client, args.game);
+ }
+ }
+
+ game.board = rr_board_create (16, 16);
+ game.msg_id = 0;
+ game.last_move_msg_id = 0;
+ game.last_move_robot = RR_ROBOT_NONE;
- 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);
+ 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);
+
+ rr_client_show (game.client, &diagram);
+ rr_board_parse (game.board, diagram);
+ free (diagram);
}
- game.board = rr_board_create (16, 16);
+ return grr_game_start_gui (&game, args.user);
+}
+
+typedef struct _GrrNoticeSource {
+ GSource source;
+ grr_game_t *game;
+} GrrNoticeSource;
+
+static gboolean
+grr_game_notices_prepare (GSource *source,
+ gint *timeout)
+{
+ 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 ();
- gdk_input_add (rr_client_fd (game.client),
- GDK_INPUT_READ,
- game_callback,
- &game);
+ return retval;
+}
+
+static gboolean
+grr_game_notices_check (GSource *source)
+{
+ GrrNoticeSource *src = (GrrNoticeSource*)source;
+ grr_game_t *game = src->game;
+ gboolean retval;
- rr_client_show (game.client, &diagram);
- rr_board_parse (game.board, diagram);
- free (diagram);
+ GDK_THREADS_ENTER ();
+ retval = rr_client_notice_pending (game->client);
+ GDK_THREADS_LEAVE ();
- return do_gui (&game);
+ 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;
}
-void
-game_callback (gpointer data,
- gint fd,
- GdkInputCondition condition)
+static void
+grr_game_read_notices (grr_game_t *game)
{
- grr_game_t *game = data;
rr_board_t *board = game->board;
rr_status_t status;
- char **notice_s;
rr_notice_t *notice;
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",
- rr_status_str (status));
- gtk_exit (1);
+ if (status == RR_STATUS_EOF)
+ fprintf (stderr, "The server has disconnected, exiting.\n");
+ else
+ fprintf (stderr, "Error during rr_client_next_notice: %s\n",
+ rr_status_str (status));
+ gtk_main_quit ();
return;
}
- notice = rr_client_parse_notice (game->client, notice_s);
+ if (!notice) {
+ fprintf (stderr, "Missing notice\n");
+ continue;
+ }
+
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_BOARD:
+ rr_board_parse (board, notice->u.string);
+ 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));
+ break;
+ case RR_NOTICE_GAMEOVER:
+ grr_game_printf (game, "\nGame over. New game will begin now.");
+ 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, ",%c",
+ rr_direction_char (notice->u.move.direction));
+ } else {
+ 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_char (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:
- /* Ignore these requests */
+ grr_game_printf (game, "\nYour turn.",
+ 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.number);
+ break;
+ case RR_NOTICE_TIMER:
+ grr_board_view_set_timer (game->board_view,
+ notice->u.number);
+ grr_board_view_start_timer (game->board_view);
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));
+ {
+ 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);
+ 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;
}
free (notice);
}
}
-void
-command_callback (GtkWidget *widget,
- grr_game_t *game)
+static gboolean
+grr_game_key_press_callback (GtkWidget *widget,
+ GdkEventKey *event,
+ grr_game_t *game)
{
+ if (GTK_WIDGET_HAS_FOCUS (GTK_WIDGET (game->message_entry))) {
+ if (event->keyval == GDK_Escape) {
+ gtk_entry_set_text (GTK_ENTRY (game->message_entry), "");
+ gtk_widget_grab_focus (GTK_WIDGET (game->message_view));
+ return TRUE;
+ } else {
+ 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_BackSpace:
+ grr_board_view_undo (game->board_view);
+ return TRUE;
+ case GDK_Up:
+ grr_board_view_move_active (game->board_view, RR_DIRECTION_NORTH);
+ return TRUE;
+ case GDK_Right:
+ grr_board_view_move_active (game->board_view, RR_DIRECTION_EAST);
+ return TRUE;
+ case GDK_Down:
+ grr_board_view_move_active (game->board_view, RR_DIRECTION_SOUTH);
+ return TRUE;
+ case GDK_Left:
+ grr_board_view_move_active (game->board_view, RR_DIRECTION_WEST);
+ return TRUE;
+ case GDK_Control_L:
+ case GDK_Control_R:
+ 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_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;
+
+ if (game->client == NULL)
+ return;
- entry_text = gtk_entry_get_text (GTK_ENTRY (widget));
- rr_client_request (game->client, entry_text, &response);
- gtk_entry_set_text (GTK_ENTRY (widget), "");
- gtk_text_buffer_insert_at_cursor (game->message_buffer, entry_text, -1);
+ entry_text = gtk_entry_get_text (GTK_ENTRY (game->message_entry));
+
+ 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));
+ } 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->message_entry), "");
+ gtk_widget_grab_focus (GTK_WIDGET (game->message_view));
/* XXX: Huh? Why is this triggering valgrind?
free (response);
}
static int
-do_gui (grr_game_t *game)
+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, 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;
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);
- {
- game->board_view = grr_board_view_new (game->board);
-
- gtk_container_add (GTK_CONTAINER (board_frame), game->board_view);
- gtk_widget_show (game->board_view);
- }
- gtk_widget_show (board_frame);
+ 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);
- vbox = gtk_vbox_new (FALSE, 1);
- gtk_paned_pack2 (GTK_PANED (vpaned), vbox, FALSE, TRUE);
+ vpaned = gtk_vpaned_new ();
+ gtk_container_set_border_width (GTK_CONTAINER (vpaned), 0);
+ gtk_box_pack_start (GTK_BOX (vbox), vpaned,
+ TRUE, TRUE, 0);
{
+ 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), GTK_WIDGET (game->board_view));
+ gtk_widget_show (GTK_WIDGET (game->board_view));
+ }
+ gtk_widget_show (board_frame);
+
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);
+ }
+ gtk_widget_show (vpaned);
+
+ 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_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),
+ 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 (vbox);
+ 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 (vpaned);
+ 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);