1 /* grrobot - Ricochet Robot using GTK+ and Xr
3 * Copyright © 2003 Carl Worth
5 * Permission to use, copy, modify, distribute, and sell this software
6 * and its documentation for any purpose is hereby granted without
7 * fee, provided that the above copyright notice appear in all copies
8 * and that both that copyright notice and this permission notice
9 * appear in supporting documentation, and that the name of Carl Worth
10 * not be used in advertising or publicity pertaining to distribution
11 * of the software without specific, written prior permission.
12 * Carl Worth makes no representations about the suitability of this
13 * software for any purpose. It is provided "as is" without express
14 * or implied warranty.
16 * CARL WORTH DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
18 * NO EVENT SHALL CARL WORTH BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
20 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
21 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24 * Author: Carl Worth <carl@theworths.org>
35 #include "grr_board_view.h"
38 typedef struct grr_game {
43 GtkWidget *board_view;
44 GtkTextBuffer *message_buffer;
45 GtkWidget *message_view;
46 GtkWidget *command_entry;
47 GtkTextIter message_iter;
51 grr_game_start_gui (grr_game_t *game);
54 grr_game_read_notices (grr_game_t *game);
57 grr_game_notices_source_new (grr_game_t *game);
60 main (int argc, char **argv)
69 gtk_init (&argc, &argv);
71 args_parse (&args, argc, argv);
73 game.client = rr_client_create (args.host, args.port, args.user);
74 if (game.client == NULL) {
75 fprintf (stderr, "Failed connecting to %s:%s as %s\n",
76 args.host, args.port, args.user);
80 status = rr_client_join (game.client, args.game);
81 if (status == RR_STATUS_NO_GAME) {
82 status = rr_client_new (game.client, args.game);
85 game.board = rr_board_create (16, 16);
87 source = grr_game_notices_source_new (&game);
88 g_source_set_priority (source, GDK_PRIORITY_EVENTS);
89 g_source_attach (source, NULL);
90 g_source_unref (source);
92 rr_client_show (game.client, &diagram);
93 rr_board_parse (game.board, diagram);
96 return grr_game_start_gui (&game);
99 typedef struct _GrrNoticeSource {
105 grr_game_notices_prepare (GSource *source,
108 GrrNoticeSource *src = (GrrNoticeSource*)source;
109 grr_game_t *game = src->game;
112 GDK_THREADS_ENTER ();
114 retval = rr_client_notice_pending (game->client);
115 GDK_THREADS_LEAVE ();
121 grr_game_notices_check (GSource *source)
123 GrrNoticeSource *src = (GrrNoticeSource*)source;
124 grr_game_t *game = src->game;
127 GDK_THREADS_ENTER ();
128 retval = rr_client_notice_pending (game->client);
129 GDK_THREADS_LEAVE ();
135 grr_game_notices_dispatch (GSource *source,
136 GSourceFunc callback,
139 GrrNoticeSource *src = (GrrNoticeSource*)source;
140 grr_game_t *game = src->game;
142 grr_game_read_notices (game);
147 static GSourceFuncs game_notices_funcs = {
148 grr_game_notices_prepare,
149 grr_game_notices_check,
150 grr_game_notices_dispatch,
155 grr_game_notices_source_new (grr_game_t *game)
157 GSource *source = g_source_new (&game_notices_funcs, sizeof (GrrNoticeSource));
158 GrrNoticeSource *src = (GrrNoticeSource*)source;
164 grr_game_read_notices (grr_game_t *game)
166 rr_board_t *board = game->board;
172 while (rr_client_notice_pending (game->client)) {
173 status = rr_client_next_notice (game->client, ¬ice_s);
175 fprintf (stderr, "Error during rr_client_notice: %s\n",
176 rr_status_str (status));
180 for (i=0; notice_s[i]; i++) {
181 gtk_text_buffer_insert_at_cursor (game->message_buffer, notice_s[i], -1);
182 gtk_text_buffer_insert_at_cursor (game->message_buffer, " ", -1);
184 gtk_text_buffer_insert_at_cursor (game->message_buffer, "\n", -1);
185 gtk_text_buffer_get_iter_at_offset (game->message_buffer,
188 gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (game->message_view),
190 0.0, FALSE, 0.0, 0.0);
192 notice = rr_client_parse_notice (game->client, notice_s);
193 if (notice == NULL) {
195 fprintf (stderr, "Failed to parse notice: ");
196 for (i=0; notice_s[i]; i++)
197 fprintf (stderr, " %s", notice_s[i]);
198 fprintf (stderr, "\n");
202 switch (notice->type) {
206 case RR_NOTICE_DISPOSE:
207 case RR_NOTICE_MESSAGE:
208 case RR_NOTICE_GAMESTATE:
210 case RR_NOTICE_WATCH:
213 case RR_NOTICE_REVOKE:
214 case RR_NOTICE_TIMER:
215 case RR_NOTICE_ABANDON:
216 case RR_NOTICE_NOBID:
217 case RR_NOTICE_ACTIVE:
220 case RR_NOTICE_RESET:
221 case RR_NOTICE_SCORE:
222 case RR_NOTICE_ACTIVATE:
223 /* XXX: Need to actually handle many of these. */
224 fprintf (stderr, "Warning: Ignoring notice of type %d\n", notice->type);
226 case RR_NOTICE_POSITION:
227 rr_board_position (board, notice->u.position.robot,
228 notice->u.position.x, notice->u.position.y);
229 gtk_widget_queue_draw (GTK_WIDGET (game->window));
232 rr_board_set_goal_target (board, notice->u.target);
233 gtk_widget_queue_draw (GTK_WIDGET (game->window));
241 grr_game_entry_callback (GtkWidget *widget,
245 const gchar *entry_text;
249 entry_text = gtk_entry_get_text (GTK_ENTRY (game->command_entry));
251 status = rr_client_request (game->client, entry_text, &response);
253 gtk_text_buffer_insert_at_cursor (game->message_buffer,
255 gtk_text_buffer_insert_at_cursor (game->message_buffer,
256 rr_status_str (status), -1);
257 gtk_text_buffer_insert_at_cursor (game->message_buffer,
260 for (i=0; response[i]; i++) {
261 gtk_text_buffer_insert_at_cursor (game->message_buffer,
263 gtk_text_buffer_insert_at_cursor (game->message_buffer,
266 gtk_text_buffer_insert_at_cursor (game->message_buffer, "\n", -1);
269 gtk_entry_set_text (GTK_ENTRY (game->command_entry), "");
271 /* XXX: Huh? Why is this triggering valgrind?
278 grr_game_start_gui (grr_game_t *game)
280 GtkWidget *board_frame;
285 GtkWidget *message_view;
286 GtkWidget *command_entry;
288 game->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
289 window = game->window;
291 gtk_window_set_title (GTK_WINDOW (window), "Ricochet Robot");
292 gtk_window_set_default_size (GTK_WINDOW (window), 512, 512);
294 g_signal_connect (G_OBJECT (window), "destroy",
295 G_CALLBACK (exit), NULL);
296 gtk_container_set_border_width (GTK_CONTAINER (window), 0);
298 vbox = gtk_vbox_new (FALSE, 1);
299 gtk_container_add (GTK_CONTAINER (window), vbox);
301 game->board_view = grr_board_view_new (game->board);
302 grr_board_view_set_client (GRR_BOARD_VIEW (game->board_view), game->client);
303 game->message_buffer = gtk_text_buffer_new (NULL);
304 game->message_view = gtk_text_view_new_with_buffer (game->message_buffer);
305 game->command_entry = gtk_entry_new ();
307 vpaned = gtk_vpaned_new ();
308 gtk_container_set_border_width (GTK_CONTAINER (vpaned), 0);
309 gtk_box_pack_start (GTK_BOX (vbox), vpaned,
312 board_frame = gtk_aspect_frame_new (NULL, 0.5, 0.5, 1.0, FALSE);
313 gtk_paned_pack1 (GTK_PANED (vpaned), board_frame, TRUE, TRUE);
315 gtk_container_add (GTK_CONTAINER (board_frame), game->board_view);
316 gtk_widget_show (game->board_view);
318 gtk_widget_show (board_frame);
320 sw = gtk_scrolled_window_new (NULL, NULL);
321 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
323 GTK_POLICY_AUTOMATIC);
324 gtk_paned_pack2 (GTK_PANED (vpaned), sw, FALSE, TRUE);
326 message_view = game->message_view;
327 gtk_text_view_set_editable (GTK_TEXT_VIEW (message_view), FALSE);
328 gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (message_view), FALSE);
329 gtk_container_add (GTK_CONTAINER (sw), message_view);
330 gtk_widget_show (message_view);
332 gtk_widget_show (sw);
334 gtk_widget_show (vpaned);
336 command_entry = game->command_entry;
337 gtk_box_pack_end (GTK_BOX (vbox), command_entry,
339 gtk_widget_show (command_entry);
340 g_signal_connect (G_OBJECT (command_entry), "activate",
341 G_CALLBACK (grr_game_entry_callback),
344 gtk_widget_show (vbox);
346 gtk_widget_show (window);