]> git.cworth.org Git - grrobot/blob - src/grrobot.c
b7433ca583f06aa20dbeeff3e4aee6706c284895
[grrobot] / src / grrobot.c
1 /* grrobot - Ricochet Robot using GTK+ and Xr
2  *
3  * Copyright © 2003 Carl Worth
4  *
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.
15  * 
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.
23  *
24  * Author: Carl Worth <carl@theworths.org>
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <gtk/gtk.h>
34
35 #include "grr_board_view.h"
36 #include "args.h"
37
38 typedef struct grr_game {
39     rr_client_t *client;
40     rr_board_t *board;
41
42     GtkWidget *window;
43     GtkWidget *board_view;
44     GtkTextBuffer *message_buffer;
45     GtkWidget *message_view;
46     GtkWidget *command_entry;
47     GtkTextIter message_iter;
48 } grr_game_t;
49
50 static int
51 grr_game_start_gui (grr_game_t *game);
52
53 static void
54 grr_game_read_notices (grr_game_t *game);
55
56 static GSource *
57 grr_game_notices_source_new (grr_game_t *game);
58
59 int 
60 main (int argc, char **argv)
61 {
62     args_t args;
63     rr_status_t status;
64     grr_game_t game;
65     GSource *source;
66
67     char *diagram;
68
69     gtk_init (&argc, &argv);
70     
71     args_parse (&args, argc, argv);
72
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);
77         return 1;
78     }
79
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);
83     }
84
85     game.board = rr_board_create (16, 16);
86
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);
91
92     rr_client_show (game.client, &diagram);
93     rr_board_parse (game.board, diagram);
94     free (diagram);
95
96     return grr_game_start_gui (&game);
97 }
98
99 typedef struct _GrrNoticeSource {
100     GSource source;
101     grr_game_t *game;
102 } GrrNoticeSource;
103
104 static gboolean
105 grr_game_notices_prepare (GSource   *source,
106                           gint      *timeout)
107 {
108     GrrNoticeSource *src = (GrrNoticeSource*)source;
109     grr_game_t *game = src->game;
110     gboolean retval;
111
112     GDK_THREADS_ENTER ();
113     *timeout = -1;
114     retval = rr_client_notice_pending (game->client);
115     GDK_THREADS_LEAVE ();
116
117     return retval;
118 }
119
120 static gboolean
121 grr_game_notices_check (GSource     *source)
122 {
123     GrrNoticeSource *src = (GrrNoticeSource*)source;
124     grr_game_t *game = src->game;
125     gboolean retval;
126
127     GDK_THREADS_ENTER ();
128     retval = rr_client_notice_pending (game->client);
129     GDK_THREADS_LEAVE ();
130
131     return retval;
132 }
133
134 static gboolean
135 grr_game_notices_dispatch (GSource      *source,
136                            GSourceFunc  callback,
137                            gpointer     user_data)
138 {
139     GrrNoticeSource *src = (GrrNoticeSource*)source;
140     grr_game_t *game = src->game;
141     
142     grr_game_read_notices (game);
143
144     return TRUE;
145 }
146
147 static GSourceFuncs game_notices_funcs = {
148   grr_game_notices_prepare,
149   grr_game_notices_check,
150   grr_game_notices_dispatch,
151   NULL
152 };
153
154 static GSource *
155 grr_game_notices_source_new (grr_game_t *game)
156 {
157     GSource *source = g_source_new (&game_notices_funcs, sizeof (GrrNoticeSource));
158     GrrNoticeSource *src = (GrrNoticeSource*)source;
159     src->game = game;
160     return source;
161 }
162
163 static void
164 grr_game_read_notices (grr_game_t *game)
165 {
166     rr_board_t *board = game->board;
167     rr_status_t status;
168     char **notice_s;
169     rr_notice_t *notice;
170     int i;
171
172     while (rr_client_notice_pending (game->client)) {
173         status = rr_client_next_notice (game->client, &notice_s);
174         if (status) {
175             fprintf (stderr, "Error during rr_client_notice: %s\n",
176                      rr_status_str (status));
177             gtk_exit (1);
178             return;
179         }
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);
183         }
184         gtk_text_buffer_insert_at_cursor (game->message_buffer, "\n", -1);
185         gtk_text_buffer_get_iter_at_offset (game->message_buffer,
186                                             &game->message_iter,
187                                             -1);
188         gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (game->message_view),
189                                       &game->message_iter,
190                                       0.0, FALSE, 0.0, 0.0);
191
192         notice = rr_client_parse_notice (game->client, notice_s);
193         if (notice == NULL) {
194             int i;
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");
199             return;
200         }
201
202         switch (notice->type) {
203         case RR_NOTICE_USER:
204         case RR_NOTICE_QUIT:
205         case RR_NOTICE_GAME:
206         case RR_NOTICE_DISPOSE:
207         case RR_NOTICE_MESSAGE:
208         case RR_NOTICE_GAMESTATE:
209         case RR_NOTICE_JOIN:
210         case RR_NOTICE_WATCH:
211         case RR_NOTICE_PART:
212         case RR_NOTICE_BID:
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:
218         case RR_NOTICE_MOVE:
219         case RR_NOTICE_UNDO:
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);
225             break;
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));
230             break;
231         case RR_NOTICE_TURN:
232             rr_board_set_goal_target (board, notice->u.target);
233             gtk_widget_queue_draw (GTK_WIDGET (game->window));
234             break;
235         }
236         free (notice);
237     }
238 }
239
240 static void
241 grr_game_entry_callback (GtkWidget *widget,
242                          grr_game_t *game)
243 {
244     rr_status_t status;
245     const gchar *entry_text;
246     char **response;
247     int i;
248
249     entry_text = gtk_entry_get_text (GTK_ENTRY (game->command_entry));
250
251     status = rr_client_request (game->client, entry_text, &response);
252     if (status) {
253         gtk_text_buffer_insert_at_cursor (game->message_buffer,
254                                           "ERROR: ", -1);
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,
258                                           "\n", -1);
259     } else {
260         for (i=0; response[i]; i++) {
261             gtk_text_buffer_insert_at_cursor (game->message_buffer,
262                                               response[i], -1);
263             gtk_text_buffer_insert_at_cursor (game->message_buffer,
264                                               " ", -1);
265         }
266         gtk_text_buffer_insert_at_cursor (game->message_buffer, "\n", -1);
267     }
268
269     gtk_entry_set_text (GTK_ENTRY (game->command_entry), "");
270
271 /* XXX: Huh? Why is this triggering valgrind?
272     free (response);
273 */
274
275 }
276
277 static int
278 grr_game_start_gui (grr_game_t *game)
279 {
280     GtkWidget *board_frame;
281     GtkWidget *vpaned;
282     GtkWidget *sw;
283     GtkWidget *vbox;
284     GtkWidget *window;
285     GtkWidget *message_view;
286     GtkWidget *command_entry;
287
288     game->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
289     window = game->window;
290
291     gtk_window_set_title (GTK_WINDOW (window), "Ricochet Robot");
292     gtk_window_set_default_size (GTK_WINDOW (window), 512, 512);
293     
294     g_signal_connect (G_OBJECT (window), "destroy",
295                       G_CALLBACK (exit), NULL);
296     gtk_container_set_border_width (GTK_CONTAINER (window), 0);
297
298     vbox = gtk_vbox_new (FALSE, 1);
299     gtk_container_add (GTK_CONTAINER (window), vbox);
300     {
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 ();
306
307         vpaned = gtk_vpaned_new ();
308         gtk_container_set_border_width (GTK_CONTAINER (vpaned), 0);
309         gtk_box_pack_start (GTK_BOX (vbox), vpaned,
310                             TRUE, TRUE, 0);
311         {
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);
314             {
315                 gtk_container_add (GTK_CONTAINER (board_frame), game->board_view);
316                 gtk_widget_show (game->board_view);
317             }
318             gtk_widget_show (board_frame);
319
320             sw = gtk_scrolled_window_new (NULL, NULL);
321             gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
322                                             GTK_POLICY_NEVER,
323                                             GTK_POLICY_AUTOMATIC);
324             gtk_paned_pack2 (GTK_PANED (vpaned), sw, FALSE, TRUE);
325             {
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);
331             }
332             gtk_widget_show (sw);
333         }
334         gtk_widget_show (vpaned);
335
336         command_entry = game->command_entry;
337         gtk_box_pack_end (GTK_BOX (vbox), command_entry,
338                           FALSE, FALSE, 0);
339         gtk_widget_show (command_entry);
340         g_signal_connect (G_OBJECT (command_entry), "activate",
341                           G_CALLBACK (grr_game_entry_callback),
342                           (gpointer) game);
343     }
344     gtk_widget_show (vbox);
345
346     gtk_widget_show (window);
347     
348     gtk_main ();
349     
350     return 0;
351 }