Crossfire Client, Trunk  R20996
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 bool time_map_redraw;
58 extern bool profile_latency;
59 extern int MINLOG;
60 extern SoundServer* server;
62 
63 static char *connect_server = NULL;
64 
66 static GOptionEntry options[] = {
67  { "server", 's', 0, G_OPTION_ARG_STRING, &connect_server,
68  "Connect to the given server", "SERVER[:PORT]" },
69  { "cache", 0, 0, G_OPTION_ARG_NONE, &want_config[CONFIG_CACHE],
70  "Cache images", NULL },
71  { "prefetch", 0, 0, G_OPTION_ARG_NONE, &want_config[CONFIG_DOWNLOAD],
72  "Download images before playing", NULL },
73  { "faceset", 0, 0, G_OPTION_ARG_STRING, &face_info.want_faceset,
74  "Use the given faceset (if available)", "FACESET" },
75 
76  { "sound_server", 0, 0, G_OPTION_ARG_FILENAME, &sound_server,
77  "Path to the sound server", "PATH" },
78  { "updatekeycodes", 0, 0, G_OPTION_ARG_NONE, &updatekeycodes,
79  "Update the saved bindings for this keyboard", NULL },
80 
81  { "profile-latency", 0, 0, G_OPTION_ARG_NONE, &profile_latency,
82  "Log command acknowledgement latency to stdout", NULL },
83  { "profile-redraw", 0, 0, G_OPTION_ARG_NONE, &time_map_redraw,
84  "Print map redraw times to stdout", NULL },
85  { "verbose", 'v', 0, G_OPTION_ARG_INT, &MINLOG,
86  "Set verbosity (0 is the most verbose)", "LEVEL" },
87  { "debug-protocol", 0, 0, G_OPTION_ARG_NONE, &debug_protocol,
88  "Print commands to and from the server", NULL },
89  { NULL }
90 };
91 
93 
95 
96 GtkBuilder *dialog_xml, *window_xml;
98 GtkNotebook *main_notebook;
99 
100 #ifdef WIN32 /* Win32 scripting support */
101 static int do_scriptout() {
102  script_process(NULL);
103  return (TRUE);
104 }
105 #endif /* WIN32 */
106 
111 static gboolean redraw(gpointer data) {
112  if (have_new_image) {
113  if (cpl.container) {
115  }
116  cpl.ob->inv_updated = 1;
117 
118  have_new_image = 0;
119  draw_map(1);
120  } else {
121  draw_map(0);
122  }
123  draw_lists();
124  return FALSE;
125 }
126 
130 void client_tick(guint32 tick) {
131  if (cpl.showmagic) {
132  if (gtk_notebook_get_current_page(GTK_NOTEBOOK(map_notebook)) !=
133  MAGIC_MAP_PAGE) {
134  // Stop flashing when the user switches back to the map window.
135  cpl.showmagic = 0;
136  } else {
138  cpl.showmagic ^= 2;
139  }
140  }
141  if (cpl.spells_updated) {
143  }
144 
146  inventory_tick();
148  g_idle_add(redraw, NULL);
149 }
153 void on_window_destroy_event(GtkObject *object, gpointer user_data) {
154 #ifdef WIN32
155  script_killall();
156 #endif
157 
158  LOG(LOG_DEBUG, "main.c::client_exit", "Exiting with return value 0.");
159  exit(0);
160 }
161 
165 static gboolean do_network(GObject *stream, gpointer data) {
166  struct timeval timeout = {0, 0};
167  fd_set tmp_read;
168  int pollret;
169 
170  if (!client_is_connected()) {
171  gtk_main_quit();
172  return FALSE;
173  }
174 
175  client_run();
176 
177  FD_ZERO(&tmp_read);
178  script_fdset(&maxfd, &tmp_read);
179  pollret = select(maxfd, &tmp_read, NULL, NULL, &timeout);
180  if (pollret > 0) {
181  script_process(&tmp_read);
182  }
183 
184  return TRUE;
185 }
186 
190 static void event_loop() {
191 #ifdef WIN32
192  g_timeout_add(250, (GtkFunction) do_scriptout, NULL);
193 #endif
194 
195  GSource *net_source = client_get_source();
196  if (net_source == NULL) {
197  error_dialog("Server unexpectedly disconnected",
198  "The server unexpectedly disconnected.");
200  return;
201  }
202  g_source_set_callback(net_source, (GSourceFunc)do_network, NULL, NULL);
203  g_source_attach(net_source, NULL);
204  gtk_main();
205 
206  LOG(LOG_DEBUG, "event_loop", "Disconnected");
207 }
208 
220 static void parse_args(int argc, char *argv[]) {
221  GOptionContext *context = g_option_context_new("- Crossfire GTKv2 Client");
222  GError *error = NULL;
223 
224  g_option_context_add_main_entries(context, options, NULL);
225  g_option_context_add_group(context, gtk_get_option_group(TRUE));
226 
227  if (!g_option_context_parse(context, &argc, &argv, &error)) {
228  g_print("%s\n", error->message);
229  g_error_free(error);
230  exit(EXIT_FAILURE);
231  }
232 
233  g_option_context_free(context);
234 
235  /*
236  * Move this after the parsing of command line options, since that can
237  * change the default log level.
238  */
239  LOG(LOG_DEBUG, "Client Version", VERSION_INFO);
240  if (MINLOG <= 0) {
241  g_setenv("CF_SOUND_DEBUG", "yes", false);
242  }
243 }
244 
250 void error_dialog(char *error, char *message) {
251  GtkWidget *dialog = gtk_message_dialog_new(
252  NULL, GTK_DIALOG_DESTROY_WITH_PARENT,
253  GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", error);
254  gtk_window_set_title(GTK_WINDOW(dialog), "Crossfire Client");
255  gtk_message_dialog_format_secondary_markup(
256  GTK_MESSAGE_DIALOG(dialog), "%s", message);
257  gtk_dialog_run(GTK_DIALOG(dialog));
258  gtk_widget_destroy(dialog);
259 }
260 
273 void my_log_handler(const gchar *log_domain, GLogLevelFlags log_level,
274  const gchar *message, gpointer user_data) {
275  g_usleep(1 * 1e6);
276 }
277 
278 static void init_sockets() {
279  /* Use the 'new' login method. */
280  wantloginmethod = 2;
281 
282 #ifdef WIN32
283  maxfd = 0; /* This is ignored on win32 platforms */
284 #else /* def WIN32 */
285  signal(SIGPIPE, SIG_IGN);
286 #ifdef HAVE_SYSCONF
287  maxfd = sysconf(_SC_OPEN_MAX);
288 #else
289  maxfd = getdtablesize();
290 #endif
291 #endif /* def WIN32 */
292 }
293 
297 static char *init_ui_layout(const char *name) {
298  guint retval = gtk_builder_add_from_file(window_xml, name, NULL);
299  if (retval > 0 && strlen(name) > 0) {
300  if (window_xml_file != name) { // FIXME: caught by Valgrind
301  strncpy(window_xml_file, name, sizeof(window_xml_file));
302  }
303  return window_xml_file;
304  } else {
305  return NULL;
306  }
307 }
308 
309 static void init_ui() {
310  GError *error = NULL;
311  GdkGeometry geometry;
312  int i;
313 
314  /* Load dialog windows using GtkBuilder. */
315  dialog_xml = gtk_builder_new();
316  if (!gtk_builder_add_from_file(dialog_xml, DIALOG_FILENAME, &error)) {
317  error_dialog("Couldn't load UI dialogs.", error->message);
318  g_warning("Couldn't load UI dialogs: %s", error->message);
319  g_error_free(error);
320  exit(EXIT_FAILURE);
321  }
322  LOG(LOG_DEBUG, "init_ui", "loaded dialog_xml");
323 
324  /* Load main window using GtkBuilder. */
325  window_xml = gtk_builder_new();
326  if (init_ui_layout(window_xml_file) == NULL) {
327  LOG(LOG_DEBUG, "init_ui_layout", "Using default layout");
328  if (init_ui_layout(DEFAULT_UI) == NULL) {
329  g_error("Could not load default layout!");
330  }
331  }
332  LOG(LOG_DEBUG, "init_ui", "loaded window_xml");
333 
334  connect_window = GTK_WIDGET(gtk_builder_get_object(dialog_xml, "connect_window"));
335  gtk_window_set_transient_for(GTK_WINDOW(connect_window),
336  GTK_WINDOW(window_root));
337  g_signal_connect(connect_window, "destroy",
338  G_CALLBACK(on_window_destroy_event), NULL);
339  main_notebook =
340  GTK_NOTEBOOK(gtk_builder_get_object(dialog_xml, "main_notebook"));
341 
342  /* Begin connecting signals for the root window. */
343  window_root = GTK_WIDGET(gtk_builder_get_object(window_xml, "window_root"));
344  if (window_root == NULL) {
345  error_dialog("Could not load main window",
346  "Check that your layout files are not corrupt.");
347  exit(EXIT_FAILURE);
348  }
349 
350  /* Request the window to receive focus in and out events */
351  gtk_widget_add_events((gpointer) window_root, GDK_FOCUS_CHANGE_MASK);
352  g_signal_connect((gpointer) window_root, "focus-out-event",
353  G_CALLBACK(focusoutfunc), NULL);
354 
355  g_signal_connect_swapped((gpointer) window_root, "key_press_event",
356  G_CALLBACK(keyfunc), GTK_OBJECT(window_root));
357  g_signal_connect_swapped((gpointer) window_root, "key_release_event",
358  G_CALLBACK(keyrelfunc), GTK_OBJECT(window_root));
359  g_signal_connect((gpointer) window_root, "destroy",
360  G_CALLBACK(on_window_destroy_event), NULL);
361 
362  /* Purely arbitrary min window size */
363  geometry.min_width=640;
364  geometry.min_height=480;
365 
366  gtk_window_set_geometry_hints(GTK_WINDOW(window_root), window_root,
367  &geometry, GDK_HINT_MIN_SIZE);
368 
369  magic_map = GTK_WIDGET(gtk_builder_get_object(window_xml,
370  "drawingarea_magic_map"));
371 
372  g_signal_connect((gpointer) magic_map, "expose_event",
373  G_CALLBACK(on_drawingarea_magic_map_expose_event), NULL);
374 
375  /* Set up colors before doing the other initialization functions */
376  for (i = 0; i < NUM_COLORS; i++) {
377  if (!gdk_color_parse(colorname[i], &root_color[i])) {
378  fprintf(stderr, "gdk_color_parse failed (%s)\n", colorname[i]);
379  }
380  if (!gdk_colormap_alloc_color(gtk_widget_get_colormap(window_root),
381  &root_color[i], FALSE, FALSE)) {
382  fprintf(stderr, "gdk_color_alloc failed\n");
383  }
384  }
385 
386  LOG(LOG_DEBUG, "init_ui", "sub init");
387  inventory_init(window_root);
388  info_init(window_root);
389  keys_init(window_root);
390  stats_init(window_root);
391  config_init(window_root);
392  pickup_init(window_root);
393  msgctrl_init(window_root);
396 
397  LOG(LOG_DEBUG, "init_ui", "window positions");
398  load_window_positions(window_root);
399 
400  LOG(LOG_DEBUG, "init_ui", "init themes");
401  init_theme();
402  LOG(LOG_DEBUG, "init_ui", "load themes");
403  load_theme(TRUE);
404  LOG(LOG_DEBUG, "init_ui", "menu items");
405  init_menu_items();
406 }
407 
414  gtk_widget_show(window_root);
417 }
418 
424  gtk_widget_hide(window_root);
426  /*
427  * We know the following is the private map structure in item.c. But
428  * we don't have direct access to it, so we still use locate.
429  */
431 
432  if (server != NULL) {
433  sound_server_stop(server);
434  }
435  gtk_widget_show(connect_window);
436 }
437 
441 int main(int argc, char *argv[]) {
442  global_time = g_timer_new();
443 #ifdef ENABLE_NLS
444  bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
445  bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
446  textdomain(GETTEXT_PACKAGE);
447 #endif
448 
449  // Initialize GTK and client library.
450  gtk_init(&argc, &argv);
451  client_init();
452 
453  // Set defaults, load configuration, and parse arguments.
454  config_load();
455  parse_args(argc, argv);
456  config_check();
457  char *layout = g_path_get_basename(window_xml_file);
458  snprintf(VERSION_INFO, MAX_BUF,
459  "GTKv2 Client " FULL_VERSION " (%s)", layout);
460  g_free(layout);
461 
462  // Initialize UI, sockets, and sound server.
463  LOG(LOG_DEBUG, "main", "init UI");
464  init_ui();
465  LOG(LOG_DEBUG, "main", "init sockets");
466  init_sockets();
467 
468  LOG(LOG_DEBUG, "main", "init sound");
469  if (!want_config[CONFIG_SOUND] || !init_sounds()) {
470  use_config[CONFIG_SOUND] = FALSE;
471  } else {
472  use_config[CONFIG_SOUND] = TRUE;
473  }
474 
475  /* Load cached pixmaps. */
476  LOG(LOG_DEBUG, "main", "init image cache");
478 
479  LOG(LOG_DEBUG, "main", "init done");
480 
481  while (true) {
482  gtk_widget_show(connect_window);
483  if (connect_server == NULL) {
485  gtk_main();
486  } else {
488  if (csocket.fd == NULL) {
489  LOG(LOG_ERROR, "main", "Unable to connect to %s!", connect_server);
490  break;
491  }
493  }
494 
496  if (serverloginmethod) {
498  } else {
500  }
501 
502  /* The event_loop will block until connection to the server is lost. */
503  event_loop();
504 
506 
507  /*
508  * Need to reset the images so they match up properly and prevent
509  * memory leaks.
510  */
512  client_reset();
513  }
514 }
515 
527 void get_window_coord(GtkWidget *win, int *x, int *y, int *wx, int *wy,
528  int *w, int *h) {
529  /* Position of a window relative to its parent window. */
530  gdk_window_get_geometry(gtk_widget_get_window(win), x, y, w, h, NULL);
531  /* Position of the window in root window coordinates. */
532  gdk_window_get_origin(gtk_widget_get_window(win), wx, wy);
533  *wx -= *x;
534  *wy -= *y;
535 }
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:250
void client_negotiate(int sound)
Definition: client.c:309
void init_create_character_window()
Definition: create_char.c:791
GtkBuilder * window_xml
Definition: main.c:96
void config_check()
Definition: config.c:311
int MINLOG
Definition: misc.c:34
GSocketConnection * fd
Definition: client.h:120
gint16 use_config[CONFIG_NUMS]
Definition: init.c:40
SoundServer * server
Definition: sound.c:25
void mapdata_animation(void)
Definition: mapdata.c:1346
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:190
static char * connect_server
Definition: main.c:63
void client_init()
Definition: init.c:184
static char * init_ui_layout(const char *name)
Definition: main.c:297
guint16 inv_updated
Definition: item.h:76
item * ob
Definition: client.h:336
void reset_image_data(void)
Definition: image.c:418
void keyfunc(GtkWidget *widget, GdkEventKey *event, GtkWidget *window)
Definition: keys.c:1564
void load_theme(int reload)
Definition: config.c:138
void show_main_client()
Definition: main.c:412
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:393
char window_xml_file[MAX_BUF]
Definition: main.c:92
ClientSocket csocket
Definition: client.c:67
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:441
void pickup_init(GtkWidget *window_root)
Definition: pickup.c:401
void init_image_cache_data(void)
Definition: image.c:516
void get_window_coord(GtkWidget *win, int *x, int *y, int *wx, int *wy, int *w, int *h)
Definition: main.c:527
int serverloginmethod
Definition: client.c:59
char VERSION_INFO[MAX_BUF]
Definition: client.c:48
static GOptionEntry options[]
Definition: main.c:66
void init_theme()
Definition: config.c:76
static void init_ui()
Definition: main.c:309
#define DIALOG_FILENAME
Definition: main.h:34
void init_menu_items()
Definition: menubar.c:91
void LOG(LogLevel level, const char *origin, const char *format,...)
Definition: misc.c:111
void client_disconnect()
Definition: client.c:178
bool time_map_redraw
Definition: map.c:50
void hide_main_client()
Definition: main.c:423
bool profile_latency
Definition: player.c:33
static gboolean redraw(gpointer data)
Definition: main.c:111
void msgctrl_init(GtkWidget *window_root)
Definition: info.c:1331
GtkWidget * magic_map
Definition: main.c:97
void draw_lists(void)
Definition: inventory.c:1051
void on_window_destroy_event(GtkObject *object, gpointer user_data)
Definition: main.c:153
Client_Player cpl
Definition: client.c:66
item * container
Definition: client.h:339
static gboolean do_network(GObject *stream, gpointer data)
Definition: main.c:165
GSource * client_get_source()
Definition: client.c:304
GtkWidget * map_notebook
Definition: map.c:44
#define DEFAULT_UI
Definition: main.h:33
#define MAX_BUF
Definition: client.h:40
GdkColor root_color[NUM_COLORS]
Definition: main.c:94
void config_init(GtkWidget *window_root)
Definition: config.c:462
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:628
gint16 want_config[CONFIG_NUMS]
Definition: init.c:40
void client_tick(guint32 tick)
Definition: main.c:130
GtkWidget * window_root
Definition: main.c:97
#define NUM_COLORS
Definition: main.h:19
#define CONFIG_DOWNLOAD
Definition: client.h:185
void client_run()
Definition: client.c:185
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:42
void client_connect(const char hostname[static 1])
Definition: client.c:269
Warning that something definitely didn&#39;t work.
Definition: client.h:444
guint32 spells_updated
Definition: client.h:353
#define MAGIC_MAP_PAGE
Definition: main.h:39
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:273
void load_window_positions(GtkWidget *window_root)
Definition: config.c:820
void client_reset()
Definition: init.c:239
void focusoutfunc(GtkWidget *widget, GdkEventKey *event, GtkWidget *window)
Definition: keys.c:1520
GTimer * global_time
Definition: misc.c:31
void hide_all_login_windows(void)
Definition: account.c:99
void script_fdset(int *maxfd, fd_set *set)
Definition: script.c:551
void remove_item_inventory(item *op)
Definition: item.c:393
void account_show_login()
Definition: account.c:1306
void info_buffer_tick(void)
Definition: info.c:1086
Input_State input_state
Definition: client.h:341
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:278
bool debug_protocol
Definition: main.c:61
#define CONFIG_SOUND
Definition: client.h:197
static void parse_args(int argc, char *argv[])
Definition: main.c:220
#define FULL_VERSION
Definition: version.h:1
GtkWidget * connect_window
Definition: main.c:97
GtkNotebook * main_notebook
Definition: main.c:98
GtkBuilder * dialog_xml
Definition: main.c:96
Useful debugging information.
Definition: client.h:441
static gboolean updatekeycodes
Definition: main.c:54
void script_process(fd_set *set)
Definition: script.c:565
bool client_is_connected()
Definition: client.c:300
void draw_map(int redraw)
void keyrelfunc(GtkWidget *widget, GdkEventKey *event, GtkWidget *window)
Definition: keys.c:1549
void metaserver_show_prompt(void)
Definition: metaserver.c:189
void inventory_tick(void)
Definition: inventory.c:1190