Crossfire Client, Trunk  R20612
main.c
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2013 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
19 #include "client.h"
20 
21 #include <errno.h>
22 #include <gtk/gtk.h>
23 #include <stdbool.h>
24 
25 #ifndef WIN32
26 #include <signal.h>
27 #endif
28 
29 #include "client-vala.h"
30 #include "image.h"
31 #include "main.h"
32 #include "mapdata.h"
33 #include "metaserver.h"
34 #include "script.h"
35 #include "gtk2proto.h"
36 
37 /* Sets up the basic colors. */
38 static const char *const colorname[NUM_COLORS] = {
39  "Black", /* 0 */
40  "White", /* 1 */
41  "Navy", /* 2 */
42  "Red", /* 3 */
43  "Orange", /* 4 */
44  "DodgerBlue", /* 5 */
45  "DarkOrange2", /* 6 */
46  "SeaGreen", /* 7 */
47  "DarkSeaGreen", /* 8 *//* Used for window background color */
48  "Grey50", /* 9 */
49  "Sienna", /* 10 */
50  "Gold", /* 11 */
51  "Khaki" /* 12 */
52 };
53 
54 static gboolean updatekeycodes = FALSE;
55 
56 /* TODO: Move these declarations to actual header files. */
57 extern int time_map_redraw;
58 extern int MINLOG;
59 extern SoundServer* server;
60 
62 static GOptionEntry options[] = {
63  { "cache", 0, 0, G_OPTION_ARG_NONE, &want_config[CONFIG_CACHE],
64  "Cache images", NULL },
65  { "prefetch", 0, 0, G_OPTION_ARG_NONE, &want_config[CONFIG_DOWNLOAD],
66  "Download images before playing", NULL },
67  { "faceset", 0, 0, G_OPTION_ARG_STRING, &face_info.want_faceset,
68  "Use the given faceset (if available)", "FACESET" },
69 
70  { "sound_server", 0, 0, G_OPTION_ARG_FILENAME, &sound_server,
71  "Path to the sound server", "PATH" },
72  { "updatekeycodes", 0, 0, G_OPTION_ARG_NONE, &updatekeycodes,
73  "Update the saved bindings for this keyboard", NULL },
74 
75  { "time-redraw", 't', 0, G_OPTION_ARG_NONE, &time_map_redraw,
76  "Print map redraw times to stdout", NULL },
77  { "verbose", 'v', 0, G_OPTION_ARG_INT, &MINLOG,
78  "Set verbosity (0 is the most verbose)", "LEVEL" },
79  { NULL }
80 };
81 
83 
85 
86 GtkBuilder *dialog_xml, *window_xml;
87 GtkWidget *window_root, *magic_map;
88 
90 bool next_tick = false;
91 
92 #ifdef WIN32 /* Win32 scripting support */
93 static int do_scriptout() {
94  script_process(NULL);
95  return (TRUE);
96 }
97 #endif /* WIN32 */
98 
103 static gboolean do_timeout(gpointer data) {
104  if (cpl.showmagic) {
105  if (gtk_notebook_get_current_page(GTK_NOTEBOOK(map_notebook)) !=
106  MAGIC_MAP_PAGE) {
107  // Stop flashing when the user switches back to the map window.
108  cpl.showmagic = 0;
109  } else {
111  cpl.showmagic ^= 2;
112  }
113  }
114  if (cpl.spells_updated) {
116  }
117  if (!tick) {
118  inventory_tick();
120  }
121 
122  return TRUE;
123 }
124 
128 static gboolean redraw(gpointer data) {
129  // Do not redraw if no tick has arrived since the last redraw.
130  if (next_tick) {
131  next_tick = false;
132  } else {
133  // Sleep for 10 ms to prevent busy wait.
134  g_usleep(10 * 1e3);
135  return TRUE;
136  }
137 
138  if (have_new_image) {
139  if (cpl.container) {
141  }
142  cpl.ob->inv_updated = 1;
143 
144  have_new_image = 0;
145  draw_map(1);
146  draw_lists();
147  } else {
148  draw_map(0);
149  }
150  return TRUE;
151 }
152 
156 void client_tick(guint32 tick) {
158  inventory_tick();
160  next_tick = true;
161 }
165 void on_window_destroy_event(GtkObject *object, gpointer user_data) {
166 #ifdef WIN32
167  script_killall();
168 #endif
169 
170  LOG(LOG_DEBUG, "main.c::client_exit", "Exiting with return value 0.");
171  exit(0);
172 }
173 
177 static gboolean do_network(GObject *stream, gpointer data) {
178  struct timeval timeout = {0, 0};
179  fd_set tmp_read;
180  int pollret;
181 
182  if (!client_is_connected()) {
183  gtk_main_quit();
184  return FALSE;
185  }
186 
187  client_run();
188 
189  FD_ZERO(&tmp_read);
190  script_fdset(&maxfd, &tmp_read);
191  pollret = select(maxfd, &tmp_read, NULL, NULL, &timeout);
192  if (pollret > 0) {
193  script_process(&tmp_read);
194  }
195 
196  draw_lists();
197  return TRUE;
198 }
199 
203 static void event_loop() {
204  guint source_redraw = g_idle_add(redraw, NULL);
205  guint source_timeout = g_timeout_add(100, do_timeout, NULL);
206 
207 #ifdef WIN32
208  g_timeout_add(250, (GtkFunction) do_scriptout, NULL);
209 #endif
210 
211  GSource *net_source = client_get_source();
212  g_assert_nonnull(net_source);
213  g_source_set_callback(net_source, (GSourceFunc)do_network, NULL, NULL);
214  g_source_attach(net_source, NULL);
215  gtk_main();
216 
217  g_source_remove(source_redraw);
218  g_source_remove(source_timeout);
219  LOG(LOG_DEBUG, "event_loop", "Disconnected");
220 }
221 
233 static void parse_args(int argc, char *argv[]) {
234  GOptionContext *context = g_option_context_new("- Crossfire GTKv2 Client");
235  GError *error = NULL;
236 
237  g_option_context_add_main_entries(context, options, NULL);
238  g_option_context_add_group(context, gtk_get_option_group(TRUE));
239 
240  if (!g_option_context_parse(context, &argc, &argv, &error)) {
241  g_print("%s\n", error->message);
242  g_error_free(error);
243  exit(EXIT_FAILURE);
244  }
245 
246  g_option_context_free(context);
247 
248  /*
249  * Move this after the parsing of command line options, since that can
250  * change the default log level.
251  */
252  LOG(LOG_DEBUG, "Client Version", VERSION_INFO);
253  if (MINLOG <= 0) {
254  g_setenv("CF_SOUND_DEBUG", "yes", false);
255  }
256 }
257 
263 void error_dialog(char *error, char *message) {
264  GtkWidget *dialog = gtk_message_dialog_new(
265  NULL, GTK_DIALOG_DESTROY_WITH_PARENT,
266  GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", error);
267  gtk_window_set_title(GTK_WINDOW(dialog), "Crossfire Client");
268  gtk_message_dialog_format_secondary_markup(
269  GTK_MESSAGE_DIALOG(dialog), "%s", message);
270  gtk_dialog_run(GTK_DIALOG(dialog));
271  gtk_widget_destroy(dialog);
272 }
273 
286 void my_log_handler(const gchar *log_domain, GLogLevelFlags log_level,
287  const gchar *message, gpointer user_data) {
288  g_usleep(1 * 1e6);
289 }
290 
291 static void init_sockets() {
292  /* Use the 'new' login method. */
293  wantloginmethod = 2;
294 
295 #ifdef WIN32
296  maxfd = 0; /* This is ignored on win32 platforms */
297 #else /* def WIN32 */
298  signal(SIGPIPE, SIG_IGN);
299 #ifdef HAVE_SYSCONF
300  maxfd = sysconf(_SC_OPEN_MAX);
301 #else
302  maxfd = getdtablesize();
303 #endif
304 #endif /* def WIN32 */
305 }
306 
310 static char *init_ui_layout(const char *name) {
311  guint retval = gtk_builder_add_from_file(window_xml, name, NULL);
312  if (retval > 0 && strlen(name) > 0) {
313  if (window_xml_file != name) { // FIXME: caught by Valgrind
314  strncpy(window_xml_file, name, sizeof(window_xml_file));
315  }
316  return window_xml_file;
317  } else {
318  return NULL;
319  }
320 }
321 
322 static void init_ui() {
323  GError *error = NULL;
324  GdkGeometry geometry;
325  int i;
326 
327  /* Load dialog windows using GtkBuilder. */
328  dialog_xml = gtk_builder_new();
329  if (!gtk_builder_add_from_file(dialog_xml, DIALOG_FILENAME, &error)) {
330  error_dialog("Couldn't load UI dialogs.", error->message);
331  g_warning("Couldn't load UI dialogs: %s", error->message);
332  g_error_free(error);
333  exit(EXIT_FAILURE);
334  }
335 
336  /* Load main window using GtkBuilder. */
337  window_xml = gtk_builder_new();
338  if (init_ui_layout(window_xml_file) == NULL) {
339  LOG(LOG_DEBUG, "init_ui_layout", "Using default layout");
340  if (init_ui_layout(DEFAULT_UI) == NULL) {
341  g_error("Could not load default layout!");
342  }
343  }
344 
345  /* Begin connecting signals for the root window. */
346  window_root = GTK_WIDGET(gtk_builder_get_object(window_xml, "window_root"));
347  if (window_root == NULL) {
348  error_dialog("Could not load main window",
349  "Check that your layout files are not corrupt.");
350  exit(EXIT_FAILURE);
351  }
352 
353  /* Request the window to receive focus in and out events */
354  gtk_widget_add_events((gpointer) window_root, GDK_FOCUS_CHANGE_MASK);
355  g_signal_connect((gpointer) window_root, "focus-out-event",
356  G_CALLBACK(focusoutfunc), NULL);
357 
358  g_signal_connect_swapped((gpointer) window_root, "key_press_event",
359  G_CALLBACK(keyfunc), GTK_OBJECT(window_root));
360  g_signal_connect_swapped((gpointer) window_root, "key_release_event",
361  G_CALLBACK(keyrelfunc), GTK_OBJECT(window_root));
362  g_signal_connect((gpointer) window_root, "destroy",
363  G_CALLBACK(on_window_destroy_event), NULL);
364 
365  /* Purely arbitrary min window size */
366  geometry.min_width=640;
367  geometry.min_height=480;
368 
369  gtk_window_set_geometry_hints(GTK_WINDOW(window_root), window_root,
370  &geometry, GDK_HINT_MIN_SIZE);
371 
372  magic_map = GTK_WIDGET(gtk_builder_get_object(window_xml,
373  "drawingarea_magic_map"));
374 
375  g_signal_connect((gpointer) magic_map, "expose_event",
376  G_CALLBACK(on_drawingarea_magic_map_expose_event), NULL);
377 
378  /* Set up colors before doing the other initialization functions */
379  for (i = 0; i < NUM_COLORS; i++) {
380  if (!gdk_color_parse(colorname[i], &root_color[i])) {
381  fprintf(stderr, "gdk_color_parse failed (%s)\n", colorname[i]);
382  }
383  if (!gdk_colormap_alloc_color(gtk_widget_get_colormap(window_root),
384  &root_color[i], FALSE, FALSE)) {
385  fprintf(stderr, "gdk_color_alloc failed\n");
386  }
387  }
388 
389  inventory_init(window_root);
390  info_init(window_root);
391  keys_init(window_root);
392  stats_init(window_root);
393  config_init(window_root);
394  pickup_init(window_root);
395  msgctrl_init(window_root);
398 
399  load_window_positions(window_root);
400 
401  init_theme();
402  load_theme(TRUE);
403  init_menu_items();
404 }
405 
409 int main(int argc, char *argv[]) {
410 #ifdef ENABLE_NLS
411  bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
412  bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
413  textdomain(GETTEXT_PACKAGE);
414 #endif
415 
416  // Initialize GTK and client library.
417  gtk_init(&argc, &argv);
418  client_init();
419 
420  // Set defaults, load configuration, and parse arguments.
421  snprintf(VERSION_INFO, MAX_BUF, "GTKv2 Client " FULL_VERSION);
422  config_load();
423  parse_args(argc, argv);
424  config_check();
425 
426  // Initialize UI, sockets, and sound server.
427  init_ui();
428  init_sockets();
429 
430  if (!want_config[CONFIG_SOUND] || !init_sounds()) {
431  use_config[CONFIG_SOUND] = FALSE;
432  } else {
433  use_config[CONFIG_SOUND] = TRUE;
434  }
435 
436  /* Load cached pixmaps. */
438 
439  while (true) {
441 
443  gtk_main();
444 
445  gtk_widget_show(window_root);
448 
449  /* The event_loop will block until connection to the server is lost. */
450  event_loop();
451 
452  gtk_widget_hide(window_root);
454  /*
455  * We know the following is the private map structure in item.c. But
456  * we don't have direct access to it, so we still use locate.
457  */
459  draw_look_list();
460 
461  /*
462  * Need to reset the images so they match up properly and prevent
463  * memory leaks.
464  */
466  client_reset();
467  if (server != NULL) {
468  sound_server_stop(server);
469  }
470  }
471 }
472 
484 void get_window_coord(GtkWidget *win, int *x, int *y, int *wx, int *wy,
485  int *w, int *h) {
486  /* Position of a window relative to its parent window. */
487  gdk_window_get_geometry(gtk_widget_get_window(win), x, y, w, h, NULL);
488  /* Position of the window in root window coordinates. */
489  gdk_window_get_origin(gtk_widget_get_window(win), wx, wy);
490  *wx -= *x;
491  *wy -= *y;
492 }
void update_spell_information(void)
Definition: spells.c:190
int maxfd
Definition: client.c:58
void error_dialog(char *error, char *message)
Definition: main.c:263
void client_negotiate(int sound)
Definition: client.c:298
void init_create_character_window()
Definition: create_char.c:791
GtkBuilder * window_xml
Definition: main.c:86
void config_check()
Definition: config.c:311
int MINLOG
Definition: misc.c:32
guint32 tick
Definition: client.c:61
gint16 use_config[CONFIG_NUMS]
Definition: init.c:40
SoundServer * server
Definition: sound.c:25
void mapdata_animation(void)
Definition: mapdata.c:1340
void inventory_init(GtkWidget *window_root)
Definition: inventory.c:452
item * locate_item(gint32 tag)
Definition: item.c:300
static void event_loop()
Definition: main.c:203
void client_init()
Definition: init.c:184
static char * init_ui_layout(const char *name)
Definition: main.c:310
guint16 inv_updated
Definition: item.h:76
item * ob
Definition: client.h:336
void reset_image_data(void)
Definition: image.c:428
void keyfunc(GtkWidget *widget, GdkEventKey *event, GtkWidget *window)
Definition: keys.c:1556
void load_theme(int reload)
Definition: config.c:138
void stats_init(GtkWidget *window_root)
Definition: stats.c:147
static const char *const colorname[NUM_COLORS]
Definition: main.c:38
void config_load()
Definition: config.c:395
char window_xml_file[MAX_BUF]
Definition: main.c:82
int wantloginmethod
Definition: client.c:59
void clear_stat_mapping(void)
Definition: stats.c:771
Face_Information face_info
Definition: image.c:169
int main(int argc, char *argv[])
Definition: main.c:409
void pickup_init(GtkWidget *window_root)
Definition: pickup.c:401
void init_image_cache_data(void)
Definition: image.c:526
void get_window_coord(GtkWidget *win, int *x, int *y, int *wx, int *wy, int *w, int *h)
Definition: main.c:484
char VERSION_INFO[MAX_BUF]
Definition: client.c:48
static GOptionEntry options[]
Definition: main.c:62
void init_theme()
Definition: config.c:76
static void init_ui()
Definition: main.c:322
#define DIALOG_FILENAME
Definition: main.h:31
void init_menu_items()
Definition: menubar.c:91
bool next_tick
Definition: main.c:90
void LOG(LogLevel level, const char *origin, const char *format,...)
Definition: misc.c:109
static gboolean redraw(gpointer data)
Definition: main.c:128
void msgctrl_init(GtkWidget *window_root)
Definition: info.c:1331
GtkWidget * magic_map
Definition: main.c:87
int time_map_redraw
Definition: map.c:50
void draw_lists(void)
Definition: inventory.c:1075
void on_window_destroy_event(GtkObject *object, gpointer user_data)
Definition: main.c:165
Client_Player cpl
Definition: client.c:68
item * container
Definition: client.h:339
static gboolean do_network(GObject *stream, gpointer data)
Definition: main.c:177
GSource * client_get_source()
Definition: client.c:293
GtkWidget * map_notebook
Definition: map.c:44
#define DEFAULT_UI
Definition: main.h:30
#define MAX_BUF
Definition: client.h:40
GdkColor root_color[NUM_COLORS]
Definition: main.c:84
void config_init(GtkWidget *window_root)
Definition: config.c:464
void info_init(GtkWidget *window_root)
Definition: info.c:649
#define CONFIG_CACHE
Definition: client.h:189
gboolean on_drawingarea_magic_map_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
void keys_init(GtkWidget *window_root)
Definition: keys.c:621
gint16 want_config[CONFIG_NUMS]
Definition: init.c:40
void client_tick(guint32 tick)
Definition: main.c:156
GtkWidget * window_root
Definition: main.c:87
#define NUM_COLORS
Definition: main.h:19
#define CONFIG_DOWNLOAD
Definition: client.h:185
void client_run()
Definition: client.c:187
int init_sounds(void)
Definition: sound.c:27
void map_init(GtkWidget *window_root)
Definition: map.c:75
guint8 showmagic
Definition: client.h:364
int have_new_image
Definition: image.c:52
guint32 spells_updated
Definition: client.h:353
#define MAGIC_MAP_PAGE
Definition: main.h:36
void draw_look_list(void)
Definition: inventory.c:739
char * sound_server
Definition: client.c:51
void my_log_handler(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data)
Definition: main.c:286
void load_window_positions(GtkWidget *window_root)
Definition: config.c:822
void client_reset()
Definition: init.c:239
void focusoutfunc(GtkWidget *widget, GdkEventKey *event, GtkWidget *window)
Definition: keys.c:1513
void script_fdset(int *maxfd, fd_set *set)
Definition: script.c:547
void remove_item_inventory(item *op)
Definition: item.c:393
void info_buffer_tick(void)
Definition: info.c:1086
void metaserver_ui_init()
Definition: metaserver.c:71
void magic_map_flash_pos(void)
Definition: magicmap.c:77
static void init_sockets()
Definition: main.c:291
#define CONFIG_SOUND
Definition: client.h:197
char * name
Definition: image.c:39
static void parse_args(int argc, char *argv[])
Definition: main.c:233
#define FULL_VERSION
Definition: version.h:1
static gboolean do_timeout(gpointer data)
Definition: main.c:103
GtkBuilder * dialog_xml
Definition: main.c:86
Useful debugging information.
Definition: client.h:441
static gboolean updatekeycodes
Definition: main.c:54
void script_process(fd_set *set)
Definition: script.c:561
bool client_is_connected()
Definition: client.c:289
void draw_map(int redraw)
void keyrelfunc(GtkWidget *widget, GdkEventKey *event, GtkWidget *window)
Definition: keys.c:1542
void metaserver_show_prompt(void)
Definition: metaserver.c:198
void inventory_tick(void)
Definition: inventory.c:1265