xqf/src/xqf.c

2838 lines
72 KiB
C

/* XQF - Quake server browser and launcher
* Copyright (C) 1998-2015 XQF Team - https://xqf.github.io
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h> /* atoi */
#include <sys/socket.h> /* inet_ntoa */
#include <netinet/in.h> /* inet_ntoa */
#include <arpa/inet.h> /* inet_ntoa */
#include <time.h> /* time */
#include <string.h> /* strlen */
#include <ctype.h> /* isspace */
#include <getopt.h>
// select, fork, pipe...
#include <sys/time.h>
#include <sys/types.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h> /* kill... */
#include <sys/wait.h>
#include <locale.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <gdk/gdk.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
#include "xqf.h"
#include "xqf-ui.h"
#include "callbacks.h"
#include "game.h"
#include "stat.h"
#include "source.h"
#include "pref.h"
#include "filter.h"
#include "flt-player.h"
#include "skin.h"
#include "dialogs.h"
#include "utils.h"
#include "server.h"
#include "launch.h"
#include "host.h"
#include "srv-list.h"
#include "srv-info.h"
#include "srv-prop.h"
#include "rc.h"
#include "pixmaps.h"
#include "rcon.h"
#include "statistics.h"
#include "psearch.h"
#include "addserver.h"
#include "addmaster.h"
#include "sort.h"
#include "config.h"
#include "debug.h"
#include "redial.h"
#include "loadpixmap.h"
#include "scripts.h"
#include "memtopixmap.h"
#ifdef USE_GEOIP
#include "country-filter.h"
#endif
const char gslisthome[] = "http://aluigi.altervista.org/papers.htm#gslist";
const char required_qstat_version[] = "2.10";
time_t xqf_start_time;
int dontlaunch = 0;
int event_type = 0;
char* xqf_PACKAGE_DATA_DIR = PACKAGE_DATA_DIR;
char* xqf_LOCALEDIR = LOCALEDIR;
char* qstat_configfile = NULL;
GtkBuilder *builder = NULL;
GtkWidget *main_window = NULL;
GtkWidget *source_ctree = NULL;
GtkCList *server_clist = NULL;
GtkCList *player_clist = NULL;
GtkCTree *srvinf_ctree = NULL;
GtkEditable *selection_manager = NULL;
GSList *cur_source = NULL; /* GSList <struct master *> */
GSList *cur_server_list = NULL; /* GSList <struct server *> */
GSList *cur_userver_list = NULL; /* GSList <struct userver *> */
struct server *cur_server = NULL;
struct stat_job *stat_process = NULL;
GtkWidget *main_filter_status_bar = NULL;
GtkWidget *main_progress_bar = NULL;
char *progress_bar_str = NULL;
GArray *server_filter_menu_items;
GtkWidget *player_skin_popup = NULL;
GtkWidget *player_skin_popup_preview = NULL;
gboolean check_launch (struct condef* con);
void refresh_selected_callback (GtkWidget *widget, gpointer data);
void launch_close_handler_part2 (struct condef *con);
void copy_text_to_clipboard (const char* text);
// build server filter menu for menubar
GtkWidget* create_filter_menu ();
GSList* filter_menu_radio_buttons = NULL; // for finding the widgets to activate
int redialserver = 0;
void sighandler_debug (int signum) {
if (signum == SIGUSR1) {
set_debug_level (get_debug_level () + 1);
}
else if (signum == SIGUSR2) {
set_debug_level (get_debug_level () - 1);
}
debug (0, "debug level now at %d", get_debug_level ());
}
// returns 0 if equal, -1 if too old, 1 if have > expected
int compare_qstat_version (const char* have, const char* expected) {
int have_major, expected_major;
int have_minor, expected_minor;
char have_pl=0, expected_pl=0;
const char* have_pos1=NULL, *expected_pos1=NULL;
const char* have_pos2=NULL, *expected_pos2=NULL;
char* buf;
debug (3, "compare_qstat_version (%s,%s)", have, expected);
if (!strcmp (have, expected)) {
return 0;
}
have_pos1 = strchr (have, '.');
expected_pos1 = strchr (expected, '.');
if (!have_pos1 || !expected_pos1) {
return -1;
}
buf = g_strndup (have, have_pos1 - have);
have_major = atoi (buf);
g_free (buf);
buf = g_strndup (expected, expected_pos1 - expected);
expected_major = atoi (buf);
g_free (buf);
debug (3, "compare_qstat_version -- compare major %d %d", have_major, expected_major);
if (have_major < expected_major) {
return -1;
}
if (have_major > expected_major) {
return 1;
}
have_pos1++;
expected_pos1++;
for (have_pos2 = have_pos1;
have_pos2 && *have_pos2 && isdigit (*have_pos2);
have_pos2++);
for (expected_pos2 = expected_pos1;
expected_pos2 && *expected_pos2 && isdigit (*expected_pos2);
expected_pos2++);
buf = g_strndup (have_pos1, have_pos2 - have_pos1);
have_minor = atoi (buf);
g_free (buf);
buf = g_strndup (expected_pos1, expected_pos2 - expected_pos1);
expected_minor = atoi (buf);
g_free (buf);
debug (3, "compare_qstat_version -- compare minor %d %d", have_minor, expected_minor);
if (have_minor < expected_minor) {
return -1;
}
if (have_minor > expected_minor) {
return 1;
}
if (have_pos2 && *have_pos2) have_pl=*have_pos2;
if (expected_pos2 && *expected_pos2) expected_pl=*expected_pos2;
debug (3, "compare_qstat_version -- compare pl %c %c", have_pl, expected_pl);
if (!have_pl && expected_pl) {
return -1;
}
if (have_pl && expected_pl && have_pl < expected_pl) {
return -1;
}
return 1;
}
void qstat_version_string (struct external_program_connection* conn) {
const char search_for[] = "qstat version";
const char *ptr, *version_end;
char* found_version = NULL;
if (!conn) {
return;
}
// we already found a valid version string
if (conn->result) {
return;
}
if (!conn->current_line) {
return;
}
if (!strncmp (conn->current_line, search_for, strlen (search_for))) {
ptr = conn->current_line + strlen (search_for);
// skip whitespace
for (; isspace (*ptr); ++ptr);
if (!*ptr) {
conn->result = FALSE;
return;
}
// skip until end of version string
for (version_end = ptr;
version_end &&
*version_end != '\0' && !isspace (*version_end);
++version_end);
found_version = g_strndup (ptr, version_end - ptr);
// debug (0, "found version <%s>", found_version);
if (compare_qstat_version (found_version, required_qstat_version) >= 0) {
conn->result = TRUE;
}
g_free (found_version);
}
}
int check_qstat_version () {
char* cmd[] = {QSTAT_EXEC, NULL};
return external_program_foreach_line (cmd, qstat_version_string, NULL);
}
int forced_filters_flag = FALSE;
void set_filters (unsigned char mask) {
unsigned n;
int i;
forced_filters_flag = TRUE;
cur_filter = mask;
for (i = 0, n = 1; i < FILTERS_TOTAL; i++, n <<= 1) {
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (filter_buttons[i]),
((cur_filter & n) != 0)? TRUE : FALSE);
}
forced_filters_flag = FALSE;
}
void filter_toggle_callback (GtkWidget *widget, unsigned char mask) {
if (!forced_filters_flag) {
cur_filter ^= mask;
server_clist_build_filtered (cur_server_list, FALSE); /* in srv-list.c */
reset_main_status_bar(builder);
}
}
// iterate through radio buttons and activate the one for the current server filter
void filter_menu_activate_current () {
unsigned int count = 0;
GSList* rbgroup = filter_menu_radio_buttons;
GtkWidget* widget = NULL;
while (rbgroup) {
if (GTK_IS_CHECK_MENU_ITEM (rbgroup->data)) {
if (count == current_server_filter) {
widget = GTK_WIDGET (rbgroup->data);
break;
}
count++;
}
rbgroup = rbgroup->next;
}
if (widget) {
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (widget), TRUE);
}
}
void set_server_filter_menu_list_text (void){
char status_buf[64];
char* name;
/* Show the active filter on the status bar
-- Add code to indicate if the filter button is checked.
*/
if (current_server_filter == 0) {
snprintf (status_buf, 64, _("No Server Filter Active"));
}
else {
name = g_array_index (server_filters, struct server_filter_vars*, current_server_filter-1)->filter_name;
if (name) {
snprintf (status_buf, 64, _("Server Filter: %s"), name);
}
else {
snprintf (status_buf, 64, _("Server Filter: %d"), current_server_filter);
xqf_error ("this is a bug");
}
}
print_status (main_filter_status_bar, status_buf);
reset_main_status_bar(builder);
}
void server_filter_select_callback (GtkWidget *widget, int number) {
if (!GTK_IS_CHECK_MENU_ITEM (widget)) {
g_warning ("no check menu item");
return;
}
if (GTK_CHECK_MENU_ITEM (widget)->active == 0) {
// signal was triggered for deactivation
return;
}
current_server_filter = number;
filters[FILTER_SERVER].changed = FILTER_CHANGED;
filters[FILTER_SERVER].last_changed = filter_time_inc ();
server_clist_build_filtered (cur_server_list, FALSE); // in srv-list.c
set_server_filter_menu_list_text ();
config_push_prefix ("/" CONFIG_FILE "/Server Filter");
config_set_int ("current_server_filter", current_server_filter);
config_pop_prefix ();
return;
}
void start_preferences_dialog (GtkWidget *widget, int page_num) {
preferences_dialog (page_num);
set_toolbar_appearance (GTK_TOOLBAR (gtk_builder_get_object (builder, "main-toolbar")), default_toolbar_style, default_toolbar_tips);
}
void start_filters_cfg_dialog (GtkWidget *widget, int page_num) {
if (filters_cfg_dialog (page_num)) {
config_sync ();
rc_save ();
gtk_menu_item_set_submenu (GTK_MENU_ITEM (gtk_builder_get_object (builder, "svfilter_menu_item")), create_filter_menu ());
filter_menu_activate_current ();
/* refresh filter status*/
set_server_filter_menu_list_text ();
//happes automagically server_clist_build_filtered (cur_server_list, TRUE);
player_clist_redraw ();
}
}
void update_server_lists_from_selected_source (void) {
GSList *cur_masters = NULL;
debug (6, "update_server_lists_from_selected_source() --");
if (cur_server_list) {
debug (6, "update_server_lists_from_selected_source() -- Free cur_server_list %lx", cur_server_list);
server_list_free (cur_server_list);
cur_server_list = NULL;
}
if (cur_userver_list) {
userver_list_free (cur_userver_list);
cur_userver_list = NULL;
}
master_selection_to_lists (cur_source, &cur_masters, &cur_server_list, &cur_userver_list);
collate_server_lists (cur_masters, &cur_server_list, &cur_userver_list);
}
// Update ui with acquired data during refresh and when refresh is done
int stat_lists_refresh (struct stat_job *job) {
int items;
int freeze;
items = g_slist_length (job->delayed.queued_servers) +
g_slist_length (job->delayed.queued_hosts);
debug (6, "stat_lists_refresh() -- Job %lx, items %d", job, items);
if (items>100) {
update_server_lists_from_selected_source ();
server_clist_build_filtered (cur_server_list, TRUE);
job->delayed.queued_servers = NULL;
job->delayed.queued_hosts = NULL;
}
else if (items) {
freeze = (items > 1) || default_refresh_sorts;
if (freeze) {
gtk_clist_freeze (server_clist);
}
g_slist_foreach (job->delayed.queued_servers, (GFunc) server_clist_refresh_server, NULL);
server_list_free (job->delayed.queued_servers);
job->delayed.queued_servers = NULL;
g_slist_foreach (job->delayed.queued_hosts, (GFunc) server_clist_show_hostname, NULL);
host_list_free (job->delayed.queued_hosts);
job->delayed.queued_hosts = NULL;
if (default_refresh_sorts) {
gtk_clist_sort (server_clist);
}
if (freeze) {
gtk_clist_thaw (server_clist);
}
}
if (job->progress.tasks > 0) {
gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(main_progress_bar), ((float)job->progress.done) / job->progress.tasks);
}
return TRUE;
}
void stat_lists_state_handler (struct stat_job *job, enum stat_state state) {
if (!main_window) {
return;
}
switch (state) {
case STAT_UPDATE_SOURCE:
progress_bar_str = _("Updating lists...");
break;
case STAT_RESOLVE_NAMES:
progress_bar_str = _("Resolving host names: %d/%d");
break;
case STAT_REFRESH_SERVERS:
progress_bar_str = _("Refreshing: %d/%d");
break;
case STAT_RESOLVE_HOSTS:
progress_bar_str = _("Resolving host addresses: %d/%d");
break;
default:
progress_bar_str = NULL;
break;
}
progress_bar_start (main_progress_bar, state == STAT_UPDATE_SOURCE || job->progress.tasks <= 1);
stat_lists_refresh (job); /* flush */
}
void stat_lists_close_handler (struct stat_job *job, int killed) {
if (!main_window) {
return;
}
if (job->need_redraw) {
update_server_lists_from_selected_source ();
server_clist_build_filtered (cur_server_list, TRUE);
}
reset_main_status_bar(builder);
progress_bar_reset (main_progress_bar);
stat_process = NULL;
set_widgets_sensitivity (builder);
}
void stat_lists_server_handler (struct stat_job *job, struct server *s) {
if (s == cur_server) {
if (job->delayed.refresh_handler) {
(*job->delayed.refresh_handler) (job);
}
player_clist_set_server (s);
srvinf_ctree_set_server (s);
}
}
void stat_lists_master_handler (struct stat_job *job, struct master *m) {
source_ctree_show_node_status (source_ctree, m);
}
void stat_lists (GSList *masters, GSList *names, GSList *servers, GSList *hosts) {
if (stat_process || (!masters && !names && !servers && !hosts)) {
return;
}
debug_increase_indent ();
debug (7, "stat_lists() -- Server List %p", servers);
stat_process = stat_job_create (masters, names, servers, hosts);
stat_process->delayed.refresh_handler = (GtkFunction) stat_lists_refresh;
stat_process->state_handlers = g_slist_prepend (stat_process->state_handlers, stat_lists_state_handler);
stat_process->close_handlers = g_slist_prepend (stat_process->close_handlers, stat_lists_close_handler);
stat_process->server_handlers = g_slist_append (stat_process->server_handlers, stat_lists_server_handler);
stat_process->master_handlers = g_slist_append (stat_process->master_handlers, stat_lists_master_handler);
stat_start (stat_process);
set_widgets_sensitivity (builder);
debug_decrease_indent ();
}
void stat_one_server (struct server *server) {
GSList *list;
debug (6, "Server %lx", server);
if (!stat_process && server) {
list = server_list_prepend (NULL, server);
stat_lists (NULL, NULL, list, NULL);
}
}
void launch_close_handler (struct stat_job *job, int killed) {
struct condef* con;
con = (struct condef *) job->data;
job->data = NULL;
if (killed) {
condef_free (con);
return;
}
gtk_timeout_add (0,(GtkFunction)check_launch, (gpointer)con);
}
/** called from inside timer, always return FALSE to stop it */
gboolean check_launch (struct condef* con) {
struct server_props *props;
gboolean launch = FALSE;
struct server *s;
if (!con) {
return FALSE;
}
s = con->s;
props = properties (s);
if (props && props->sucks) {
char* comment = props->comment;
if (comment && strlen (comment)) {
comment = g_strdup_printf (_("\n\nThe server sucks for the following reason:\n%s"), props->comment);
}
else {
comment = NULL;
}
launch = dialog_yesno (NULL, 1, _("Yes"), _("No"),
/* translator: %s = optional reason why the server sucks */
_("You said this servers sucks.\nDo you want to risk a game this time?%s"), comment?comment:"");
g_free (comment);
if (!launch) {
condef_free (con);
return FALSE;
}
}
if (s->flags & SERVER_INCOMPATIBLE) {
launch = dialog_yesno (NULL, 1, _("Yes"), _("No"),
_("This server is not compatible with the version of %s you have"
" installed.\nLaunch client anyway?"), games[s->type].name);
if (!launch) {
condef_free (con);
return FALSE;
}
}
if (s->ping >= MAX_PING) {
launch = dialog_yesno (NULL, 1, _("Launch"), _("Cancel"),
_("Server %s:%d is %s.\n\nLaunch client anyway?"),
(s->host->name)? s->host->name : inet_ntoa (s->host->ip),
s->port,
(s->ping == MAX_PING)? "unreachable" : "down");
if (!launch) {
condef_free (con);
return FALSE;
}
}
if (!launch && !con->spectate && server_need_redial (s, props)) {
launch = dialog_yesnoredial (NULL, 1, _("Launch"), _("Cancel"), _("Redial"),
_("Server %s:%d is full.\n\nLaunch client anyway?"),
(s->host->name)? s->host->name : inet_ntoa (s->host->ip),
s->port);
if (!launch) {
condef_free (con);
return FALSE;
}
else if (launch == 2)
{
redialserver = 1;
launch = redial_dialog (con->s, props);
if (launch == FALSE) {
condef_free (con);
return FALSE;
}
}
else
redialserver = 0;
}
launch_close_handler_part2 (con);
return FALSE;
}
void launch_close_handler_part2 (struct condef *con) {
struct server_props *props;
int launch = FALSE;
int save = 0;
FILE *f;
char *fn;
char *temp_name;
char *temp_mod;
char *temp_game;
struct server *s;
if (redialserver == 1) // was called from a redial
play_sound (sound_redial_success, 0);
else
play_sound (sound_server_connect, 0);
redialserver = 0; // Cancel any redialing
s = con->s;
props = properties (s);
debug (3, "rest of launch_close_handler"); // alex
if (con->spectate) {
if ((s->flags & SERVER_SP_PASSWORD) == 0) {
// XXX: what's this good for? looks useless to me -- ln
con->spectator_password = g_strdup ("1");
}
else {
if (props && props->spectator_password) {
con->spectator_password = g_strdup (props->spectator_password);
}
else {
con->spectator_password = enter_string_with_option_dialog (FALSE,
_("Save Password"), &save, _("Spectator Password:"));
if (!con->spectator_password) {
condef_free (con);
return;
}
if (save) {
if (!props) {
props = properties_new (s->host, s->port);
}
props->spectator_password = g_strdup (con->spectator_password);
props_save ();
}
}
}
}
else {
if (props && props->server_password) {
con->password = g_strdup (props->server_password);
}
else if ((s->flags & SERVER_PASSWORD) != 0) {
con->password = enter_string_with_option_dialog (FALSE,
_("Save Password"), &save, _("Server Password:"));
if (!con->password) {
condef_free (con);
return;
}
if (save) {
if (!props) {
props = properties_new (s->host, s->port);
}
props->server_password = g_strdup (con->password);
props_save ();
}
}
}
// con->server = g_strdup_printf ("%s:%5d", inet_ntoa (s->host->ip), s->port);
con->server = g_strdup_printf ("%s:%d", inet_ntoa (s->host->ip), s->port);
con->gamedir = g_strdup (s->game);
if (props && props->rcon_password) {
con->rcon_password = g_strdup (props->rcon_password);
}
if (props && props->custom_cfg) {
con->custom_cfg = g_strdup (props->custom_cfg);
}
launch = client_launch (con, TRUE);
condef_free (con);
if (!launch) {
return;
}
// Save address etc to LaunchInfo.txt
if (default_launchinfo) {
fn = file_in_dir (user_rcdir, LAUNCHINFO_FILE);
f = fopen (fn, "w");
if (f) {
temp_name = s->name;
temp_game = s->game;
temp_mod = s->gametype;
if (!temp_name) {
temp_name = "";
}
fprintf (f, "GameType %s\n", games[s->type].name);
fprintf (f, "ServerName %s\n", temp_name);
fprintf (f, "ServerAddr %s:%d\n", inet_ntoa (s->host->ip), s->port);
fprintf (f, "ServerMod ");
if (temp_game) {
fprintf (f, "%s", temp_game);
}
if (temp_game && temp_mod) {
fprintf (f, ", ");
}
if (temp_mod) {
fprintf (f, "%s", temp_mod);
}
fprintf (f, "\n");
fclose (f);
}
g_free (fn);
}
// Launch pre-launch script
if (default_prelaunchexec) {
if (fork () == 0) {
char *launchargv[4];
launchargv[0] = g_strdup_printf ("%s/PreLaunch", user_rcdir);
launchargv[1] = games[s->type].qstat_str;
launchargv[2] = g_strdup_printf ("%s:%d", inet_ntoa (s->host->ip), s->port);
launchargv[3] = NULL;
execv (launchargv[0], launchargv);
xqf_error ("PreLaunch failed");
_exit (EXIT_FAILURE);
}
}
if (main_window && default_terminate) {
gtk_widget_destroy (main_window);
}
}
void launch_server_handler (struct stat_job *job, struct server *s) {
server_clist_refresh_server (s);
if (s == cur_server) {
player_clist_set_server (s);
srvinf_ctree_set_server (s);
}
// no connection defined, maybe because of a hostname instead of ip specified
// on command line. just take first server to launch
if (!job->data) {
job->data = condef_new (s);
}
/* Don't spend time on host name lookups */
if (job->hosts) {
host_list_free (job->hosts);
job->hosts = NULL;
}
}
// Restructure: Front callback calls callback core with specified action, action sits in another function
void launch_core1_callback () {
debug (6, "launch_callback() --");
if (stat_process || !cur_server || (games[cur_server->type].flags & GAME_CONNECT) == 0) {
return;
}
if (games[cur_server->type].config_is_valid &&
!(*games[cur_server->type].config_is_valid) (cur_server)) {
start_preferences_dialog (NULL, PREF_PAGE_GAMES + cur_server->type * 256);
return;
}
}
void launch_core2_callback (int spectate, char *demo, struct condef *con) {
con = condef_new (cur_server);
con->demo = demo;
con->spectate = spectate;
stat_process = stat_job_create (NULL, NULL, server_list_prepend (NULL, cur_server), NULL);
stat_process->data = con;
stat_process->state_handlers = g_slist_prepend (
stat_process->state_handlers, stat_lists_state_handler);
stat_process->close_handlers = g_slist_prepend (
stat_process->close_handlers, stat_lists_close_handler);
stat_process->server_handlers = g_slist_append (
stat_process->server_handlers, launch_server_handler);
/* run last!!! */
stat_process->close_handlers = g_slist_append (
stat_process->close_handlers, launch_close_handler);
stat_start (stat_process);
set_widgets_sensitivity (builder);
return;
}
void launch_normal_callback (GtkWidget *widget) {
int spectate = FALSE;
char *demo = NULL;
struct condef *con = NULL;
launch_core1_callback ();
launch_core2_callback (spectate, demo, con);
return;
}
void launch_spectate_callback (GtkWidget *widget) {
int spectate = FALSE;
char *demo = NULL;
struct condef *con = NULL;
launch_core1_callback ();
if ((cur_server->flags & SERVER_SPECTATE) == 0) {
return;
}
spectate = TRUE;
launch_core2_callback (spectate, demo, con);
return;
}
void launch_record_callback (GtkWidget *widget) {
int spectate = FALSE;
char *demo = NULL;
struct condef *con = NULL;
launch_core1_callback ();
if ((games[cur_server->type].flags & GAME_RECORD) == 0) {
return;
}
if ((games[cur_server->type].flags & GAME_SPECTATE) != 0) {
demo = enter_string_with_option_dialog (TRUE, _("Spectator"), &spectate, _("Demo name:"));
}
else {
demo = enter_string_dialog (TRUE, _("Demo name:"));
}
if (!demo) {
return;
}
launch_core2_callback (spectate, demo, con);
return;
}
int server_clist_sort_mode = SORT_SERVER_PING;
void server_clist_set_sort_mode (enum ssort_mode mode) {
server_clist_sort_mode = mode;
}
int server_clist_compare_func (GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2) {
int res, mode;
GtkCListRow *row1 = (GtkCListRow *) ptr1;
GtkCListRow *row2 = (GtkCListRow *) ptr2;
struct server *s1 = (struct server *) row1->data;
struct server *s2 = (struct server *) row2->data;
debug (7, "");
mode = server_clist_def.cols[clist->sort_column].sort_mode[server_clist_def.cols[clist->sort_column].current_sort_mode];
res = compare_servers (s1, s2, mode);
// fallback
if (res == 0 && mode != SORT_SERVER_PING) {
res = compare_servers (s1, s2, SORT_SERVER_PING);
}
return res;
}
int player_clist_compare_func (GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2) {
GtkCListRow *row1 = (GtkCListRow *) ptr1;
GtkCListRow *row2 = (GtkCListRow *) ptr2;
struct player *p1 = (struct player *) row1->data;
struct player *p2 = (struct player *) row2->data;
return compare_players (p1, p2, clist->sort_column);
}
int srvinf_clist_compare_func (GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2) {
GtkCListRow *row1 = (GtkCListRow *) ptr1;
GtkCListRow *row2 = (GtkCListRow *) ptr2;
const char **i1 = (const char **) row1->data;
const char **i2 = (const char **) row2->data;
return compare_srvinfo (i1, i2, clist->sort_column);
}
void update_source_callback (GtkWidget *widget, gpointer data) {
GSList *masters = NULL;
GSList *servers = NULL;
GSList *uservers = NULL;
event_type = EVENT_UPDATE;
debug_increase_indent ();
debug (6, "update_source_callback() -- ");
if (stat_process || !cur_source) {
return;
}
master_selection_to_lists (cur_source, &masters, &servers, &uservers);
if (masters || servers || uservers) {
stat_lists (masters, uservers, servers, NULL);
}
debug_decrease_indent ();
}
void refresh_selected_callback (GtkWidget *widget, gpointer data) {
GSList *list;
if (stat_process) {
debug (1, "nope");
return;
}
event_type = EVENT_REFRESH_SELECTED;
list = server_clist_selected_servers ();
if (list) {
stat_lists (NULL, NULL, list, NULL);
}
}
void refresh_callback (GtkWidget *widget, gpointer data) {
GSList *servers;
GSList *uservers;
if (stat_process) {
return;
}
event_type = EVENT_REFRESH;
debug (7, "refresh_callback() -- Get Server List");
servers = server_clist_all_servers ();
uservers = userver_list_copy (cur_userver_list);
debug (7, "refresh_callback() -- server list %lx", servers);
if (servers || uservers) {
stat_lists (NULL, uservers, servers, NULL);
}
}
void refresh_n_server (GtkWidget * button, gpointer *data) {
GSList *list;
gint number;
if (stat_process)
return;
event_type = EVENT_REFRESH;
number = GPOINTER_TO_INT (data);
list = server_clist_get_n_servers (number);
if (list) {
stat_lists (NULL, NULL, list, NULL);
}
}
void stop_callback (GtkWidget *widget, gpointer data) {
event_type = 0; // To prevent sound from stopped action from playing
if (stat_process) {
stat_stop (stat_process);
stat_process = NULL;
}
redialserver = 0; // Reset redialserver so prompt comes up next time
play_sound (sound_stop, 0);
event_type = 0; // To prevent sound from stopped action from playing
}
void add_to_favorites_callback (GtkWidget *widget, gpointer data) {
GList *selected = server_clist->selection;
GSList *list;
GSList *tmp;
enum { buflen = 256 };
char buf[buflen]; /* if you change this, change the statement below */
int server_list_size;
debug (7, "add_to_favorites_callback() -- ");
if (stat_process || !selected) {
return;
}
list = server_clist_selected_servers ();
if (list) {
for (tmp = list; tmp; tmp = tmp->next) {
server_list_size = g_slist_length (favorites->servers);
favorites->servers = server_list_append (favorites->servers, (struct server *) tmp->data);
if (server_list_size < g_slist_length (favorites->servers)){
snprintf (buf, buflen, "Added Server #%d: '%s'",
g_slist_length (favorites->servers),
((struct server *)tmp->data)->name);
} else {
snprintf (buf, buflen, "Server '%s' Already In Favorites",
((struct server *)tmp->data)->name);
}
print_status (GTK_WIDGET (gtk_builder_get_object (builder, "main-status-bar")), buf);
}
debug (7, "add_to_favorites_callback() -- Saving To Favorites");
save_favorites ();
server_list_free (list);
}
}
// add a server to favorites
void new_server_to_favorites (struct stat_job *job, struct server *s) {
int row;
debug (6, "Server %lx, job %p", s, job);
favorites->servers = server_list_append (favorites->servers, s);
save_favorites ();
source_ctree_select_source (favorites);
if (cur_filter != 0) {
set_filters (0); /* turn off filters */
if (job) {
job->need_redraw = FALSE;
}
}
row = gtk_clist_find_row_from_data (server_clist, s);
if (row >= 0) {
server_clist_select_one (row);
server_clist_selection_visible ();
}
}
void add_server_name_handler (struct stat_job *job, struct userver *us,
enum dns_status status) {
if (us->s) {
new_server_to_favorites (job, us->s);
}
else {
progress_bar_reset (main_progress_bar);
dialog_ok (NULL, _("Host %s not found"), us->hostname);
}
}
/** check specified address is valid, resolve hostname if needed, stat server,
* finally call new_server_to_favorites
* calls free (str)!!!
*/
void prepare_new_server_to_favorites (enum server_type type, char* str, gboolean dolaunch) {
char *addr;
unsigned short port;
struct host *h;
struct server *s = NULL;
struct userver *us = NULL;
if (!str || !*str) {
return;
}
if (!parse_address (str, &addr, &port)) {
dialog_ok (NULL, _("\"%s\" is not valid host[:port] combination."), str);
g_free (str);
return;
}
g_free (str);
h = host_add (addr);
if (h) { /* IP address */
host_ref (h);
s = server_add (h, port, type);
if (s) {
new_server_to_favorites (NULL, s);
}
host_unref (h);
}
else { /* hostname */
us = userver_add (g_ascii_strdown (addr, -1), port, type);
}
g_free (addr);
if (s || us) {
stat_process = stat_job_create (NULL,
(us)? userver_list_add (NULL, us) : NULL,
(s)? server_list_prepend (NULL, s) : NULL,
NULL);
stat_process->state_handlers = g_slist_prepend (
stat_process->state_handlers, stat_lists_state_handler);
stat_process->close_handlers = g_slist_prepend (
stat_process->close_handlers, stat_lists_close_handler);
// stat_process->server_handlers = g_slist_append (stat_process->server_handlers, stat_lists_server_handler);
stat_process->name_handlers = g_slist_prepend (
stat_process->name_handlers, add_server_name_handler);
if (dolaunch) {
if (s) {
struct condef* con = condef_new (s);
stat_process->data = con;
}
stat_process->server_handlers = g_slist_append (
stat_process->server_handlers, launch_server_handler);
stat_process->close_handlers = g_slist_append (
stat_process->close_handlers, launch_close_handler);
}
stat_start (stat_process);
set_widgets_sensitivity (builder);
}
}
void add_server_callback (GtkWidget *widget, gpointer data) {
char *str = NULL;
enum server_type type = UNKNOWN_SERVER;
if (stat_process) {
return;
}
str = add_server_dialog (&type, NULL);
if (!str) {
return;
}
prepare_new_server_to_favorites (type, str, FALSE);
return;
}
void del_server_callback (GtkWidget *widget, gpointer data) {
GSList *selected;
GSList *l, *c;
int is_favorites = 0;
int delete_from_all = 0;
debug (3, "--");
if (stat_process || !cur_source) {
return;
}
selected = server_clist_selected_servers ();
if (!selected) {
return;
}
for (c = cur_source; c; c = c->next) {
struct master* m = (struct master *) c->data;
if (m == favorites) {
is_favorites = 1;
}
if (!delete_from_all && m->isgroup) {
delete_from_all = dialog_yesno (NULL, 1, _("Yes"), _("No"), _("Remove selected servers from all lists?"));
}
}
for (l = selected; l; l = l->next) {
struct server* s = (struct server*) l->data;
if (delete_from_all) {
server_remove_from_all (s);
master_remove_server (favorites, s);
}
else {
for (c = cur_source; c; c = c->next) {
struct master* m = (struct master *) c->data;
master_remove_server (m, s);
}
}
}
if (is_favorites) {
save_favorites ();
}
g_slist_free (selected);
update_server_lists_from_selected_source ();
server_clist_build_filtered (cur_server_list, FALSE);
}
void copy_server_callback (GtkWidget *widget, gpointer data) {
GList *selection = server_clist->selection;
struct server *s;
char buf[256];
int pos = 0;
gtk_editable_delete_text (selection_manager, 0, -1);
switch (g_list_length (selection)) {
case 0:
gtk_editable_select_region (selection_manager, 0, 0);
break;
default:
for (; selection; selection = selection->next) {
s = (struct server *) gtk_clist_get_row_data (
server_clist, GPOINTER_TO_INT (selection->data));
g_snprintf (buf, 256, "%s:%d%s", inet_ntoa (s->host->ip), s->port, selection->next?"\n":"");
gtk_editable_insert_text (selection_manager, buf, strlen (buf), &pos);
}
gtk_editable_select_region (selection_manager, 0, -1);
break;
}
gtk_editable_copy_clipboard (selection_manager);
}
void copy_server_info_callback (GtkWidget *widget, gpointer data) {
GList *selection = GTK_CLIST (srvinf_ctree)->selection;
int pos = 0;
gtk_editable_delete_text (selection_manager, 0, -1);
if (!g_list_length (selection)) {
gtk_editable_select_region (selection_manager, 0, 0);
}
else {
for (; selection; selection = selection->next) {
GtkCTreeNode* node = GTK_CTREE_NODE (selection->data);
char* txt = NULL;
gtk_ctree_node_get_text (GTK_CTREE (srvinf_ctree), node, 1, &txt);
gtk_editable_insert_text (selection_manager, txt, strlen (txt), &pos);
}
gtk_editable_select_region (selection_manager, 0, -1);
}
gtk_editable_copy_clipboard (selection_manager);
}
void copy_text_to_clipboard (const char* text) {
int pos = 0;
gtk_editable_delete_text (selection_manager, 0, -1);
if (text && *text) {
gtk_editable_insert_text (selection_manager, text, strlen (text), &pos);
}
gtk_editable_select_region (selection_manager, 0, pos);
gtk_editable_copy_clipboard (selection_manager);
}
void copy_server_callback_plus (GtkWidget *widget, gpointer data) {
GList *selection = server_clist->selection;
struct server *s;
char buf[256];
int pos = 0;
unsigned players;
gtk_editable_delete_text (selection_manager, 0, -1);
switch (g_list_length (selection)) {
case 0:
gtk_editable_select_region (selection_manager, 0, 0);
break;
default:
for (; selection; selection = selection->next) {
s = (struct server *) gtk_clist_get_row_data (
server_clist, GPOINTER_TO_INT (selection->data));
players = s->curplayers;
if (serverlist_countbots && s->curbots <= players) {
players-=s->curbots;
}
g_snprintf (buf, 256, "%i %s:%d %s %s %i of %i%s", s->ping, inet_ntoa
(s->host->ip), s->port, s->name, s->map, players, s->maxplayers, selection->next?"\n":"");
gtk_editable_insert_text (selection_manager, buf, strlen (buf), &pos);
}
gtk_editable_select_region (selection_manager, 0, -1);
break;
}
gtk_editable_copy_clipboard (selection_manager);
}
void update_master_builtin_callback (GtkWidget *widget, gpointer data) {
update_master_list_builtin ();
}
void update_master_gslist_callback (GtkWidget *widget, gpointer data) {
if (!have_gslist_installed ()) {
// translator: %s == url
dialog_ok (NULL, _("For Gslist support you must install the 'gslist' program available from\n%s\n"
"Don't forget that you need to run 'gslist -u' before you can use it."), gslisthome);
copy_text_to_clipboard (gslisthome);
return;
}
update_master_gslist_builtin ();
}
void add_master_callback (GtkWidget *widget, gpointer data) {
struct master *m;
if (stat_process) {
return;
}
m = add_master_dialog (NULL);
if (m) {
source_ctree_add_master (source_ctree, m);
source_ctree_select_source (m);
}
}
// does only work with one master selected
void edit_master_callback (GtkWidget *widget, gpointer data) {
struct master *master_to_edit, *master_to_add;
if (!cur_source) {
return;
}
master_to_edit = (struct master *) cur_source->data;
source_ctree_select_source (master_to_edit);
master_to_add = add_master_dialog (master_to_edit);
if (master_to_add) {
source_ctree_add_master (source_ctree, master_to_add);
source_ctree_select_source (master_to_add);
}
}
void del_master_callback (GtkWidget *widget, gpointer data) {
struct master *m;
GSList *masters = NULL;
char *master_names = NULL;
GSList *list;
int delete;
char *s1;
char *s2;
if (stat_process) {
return;
}
for (list = cur_source; list; list = list->next) {
m = (struct master *) list->data;
if (m != favorites && !m->isgroup) {
masters = g_slist_append (masters, m);
s1 = g_strdup_printf ("%s (%s)", m->name, games[m->type].name);
if (!master_names) {
master_names = s1;
}
else {
s2= master_names;
master_names = g_strconcat (s2, "\n", s1, NULL);
g_free (s1);
g_free (s2);
}
}
}
if (!masters) {
dialog_ok (NULL, _("You have to select the server you want to delete"));
return;
}
// FIXME: plural
delete = dialog_yesno (NULL, 1, _("Delete"), _("Cancel"),
_("Master%s to delete:\n\n%s"),
(g_slist_length (masters) > 1)? "s" : "",
master_names);
if (delete) {
for (list = masters; list; list = list->next) {
m = (struct master *) list->data;
source_ctree_delete_master (source_ctree, m);
free_master (m);
}
}
g_free (master_names);
g_slist_free (masters);
}
void find_player_callback (GtkWidget *widget, int find_next) {
char *pattern;
if (find_next || find_player_dialog ()) {
if (!psearch_data_is_empty ()) {
pattern = psearch_lookup_pattern ();
print_status (GTK_WIDGET (gtk_builder_get_object (builder, "main-status-bar")), _("Find Player: %s"), pattern);
g_free (pattern);
find_player (find_next);
}
}
}
void show_hostnames_callback (GtkWidget *widget, gpointer data) {
if (stat_process || !server_clist || server_clist->rows == 0) {
return;
}
if (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "view_hostnames_menu_item"))->active != show_hostnames) {
show_hostnames = GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "view_hostnames_menu_item"))->active;
server_clist_redraw ();
config_set_bool ("/" CONFIG_FILE "/Appearance/show hostnames", show_hostnames);
}
}
void show_default_port_callback (GtkWidget *widget, gpointer data) {
if (stat_process || !server_clist || server_clist->rows == 0) {
return;
}
if (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "view_defport_menu_item"))->active != show_default_port) {
show_default_port = GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "view_defport_menu_item"))->active;
server_clist_redraw ();
config_set_bool ("/" CONFIG_FILE "/Appearance/show default port", show_default_port);
}
}
void resolve_callback (GtkWidget *widget, gpointer data) {
GSList *selected;
GSList *hosts;
struct server *s;
debug (7, "resolve_callback() --");
if (stat_process) {
return;
}
if (!show_hostnames) {
gtk_check_menu_item_set_active (
GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "view_hostnames_menu_item")), TRUE);
}
selected = server_clist_selected_servers ();
if (!selected) {
return;
}
if (selected->next) {
hosts = merge_hosts_to_resolve (NULL, selected);
}
else {
/* always resolve if only one host is asked to */
s = (struct server *) selected->data;
hosts = host_list_add (NULL, s->host);
}
server_list_free (selected);
stat_lists (NULL, NULL, NULL, hosts);
}
void properties_callback (GtkWidget *widget, gpointer data) {
if (stat_process) {
return;
}
if (cur_server) {
properties_dialog (cur_server);
set_widgets_sensitivity (builder);
}
}
void rcon_callback (GtkWidget *widget, gpointer data) {
struct server_props *sp;
char *passwd = NULL;
int save = 0;
if (stat_process || !cur_server || (games[cur_server->type].flags & GAME_RCON) == 0) {
return;
}
sp = properties (cur_server);
if (!sp || !sp->rcon_password) {
passwd = enter_string_with_option_dialog (FALSE,
_("Save Password"), &save, _("Server Password:"));
if (!passwd) /* canceled */
return;
if (save) {
if (!sp) {
sp = properties_new (cur_server->host, cur_server->port);
}
sp->rcon_password = passwd;
props_save ();
passwd = NULL;
}
}
rcon_dialog (cur_server, (passwd)? passwd : sp->rcon_password);
if (passwd) {
g_free (passwd);
}
}
void server_clist_select_callback (GtkWidget *widget, int row,
int column, GdkEvent *event, GtkWidget *button) {
GdkEventButton *bevent = (GdkEventButton *) event;
debug (7, "server_clist_select_callback() -- Row %d", row);
server_clist_sync_selection ();
if (bevent && bevent->type == GDK_2BUTTON_PRESS && bevent->button == 1 &&
!((column == 6) && cur_server && (games[cur_server->type].get_mapshot))) // not for map preview
{
launch_normal_callback (NULL);
}
}
void server_clist_unselect_callback (GtkWidget *widget, int row,
int column, GdkEvent *event, GtkWidget *button) {
debug (7, "server_clist_uselect_callback() -- Row %d", row);
server_clist_sync_selection ();
}
/* Deal with key-presses in the server pane */
gboolean server_clist_keypress_callback (GtkWidget *widget, GdkEventKey *event) {
debug (7, "server_clist_keypress_callback() -- CLIST Key %x", event->keyval);
if (event->keyval == GDK_Delete) {
del_server_callback (widget, event);
return TRUE;
} else if (event->keyval == GDK_Insert) {
if (event->state & GDK_SHIFT_MASK){
add_to_favorites_callback (widget, event);
} else {
add_server_callback (widget, event);
}
return TRUE;
} else if (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter) {
launch_normal_callback (widget);
return TRUE;
}
return FALSE;
}
int source_ctree_event_callback (GtkWidget *widget, GdkEvent *event) {
GdkEventButton *bevent = (GdkEventButton *) event;
GList *selection;
int row;
GtkCTreeNode *node, *node_under_mouse;
int node_is_in_selection = 0;
if (event->type == GDK_BUTTON_PRESS &&
bevent->window == GTK_CLIST (source_ctree)->clist_window) {
switch (bevent->button) {
case 3:
// lets see which row the cursor is on
if (gtk_clist_get_selection_info (GTK_CLIST (source_ctree),
bevent->x, bevent->y, &row, NULL)) {
// list of selected items
selection = GTK_CLIST (source_ctree)->selection;
// XXX: what is the first part of the && good for?
if (!g_list_find (selection, GINT_TO_POINTER (row)) &&
(bevent->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == 0) {
node_under_mouse = gtk_ctree_node_nth (GTK_CTREE (source_ctree), row);
if (node_under_mouse) {
// go through all selected masters and search if the one under the
// cursor is among them
while (selection) {
node = GTK_CTREE_NODE (selection->data);
if (node == node_under_mouse) {
node_is_in_selection = 1;
break;
}
selection = selection->next;
}
// clear selection and only select the one under the curser
if (!node_is_in_selection) {
gtk_ctree_unselect_recursive (GTK_CTREE (source_ctree), NULL);
gtk_ctree_select (GTK_CTREE (source_ctree), node_under_mouse);
}
}
}
}
gtk_menu_popup (GTK_MENU (gtk_builder_get_object (builder, "source_tree_popup_menu")), NULL, NULL, NULL, NULL,
bevent->button, bevent->time);
return TRUE;
default:
return FALSE;
}
}
return FALSE;
}
GtkWidget *server_mapshot_popup = NULL;
GtkWidget *server_mapshot_popup_pixmap = NULL;
void server_mapshot_preview_popup_show (guchar *imagedata, size_t len, int x, int y, gushort overBrightBits) {
GtkWidget *frame;
int win_x, win_y, scr_w, scr_h;
guint w = 0, h = 0;
GdkPixmap *pix = NULL;
GdkBitmap *mask = NULL;
renderMemToGtkPixmap (imagedata, len, &pix, &mask, &w, &h, overBrightBits);
if (!pix || !w || !h) {
if (pix) gdk_pixmap_unref (pix);
if (mask) gdk_bitmap_unref (mask);
pix=stop_pix.pix;
mask=stop_pix.mask;
}
if (!server_mapshot_popup) {
server_mapshot_popup = gtk_window_new (GTK_WINDOW_POPUP);
gtk_window_set_policy (GTK_WINDOW (server_mapshot_popup), FALSE, FALSE, TRUE);
frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
gtk_container_add (GTK_CONTAINER (server_mapshot_popup), frame);
gtk_widget_show (frame);
server_mapshot_popup_pixmap = gtk_pixmap_new (pix, mask);
gtk_container_add (GTK_CONTAINER (frame), server_mapshot_popup_pixmap);
// gtk_preview_size (GTK_PREVIEW (pixmap), 320, 200);
gtk_widget_show (server_mapshot_popup_pixmap);
}
else {
gtk_widget_hide (server_mapshot_popup);
gtk_pixmap_set (GTK_PIXMAP (server_mapshot_popup_pixmap), pix, mask);
}
gdk_window_get_origin (server_clist->clist_window, &win_x, &win_y);
x += win_x;
y += win_y;
scr_w = gdk_screen_width ();
scr_h = gdk_screen_height ();
x = (x + w > scr_w)? scr_w - w : x;
y = (y + h > scr_h)? scr_h - h : y;
// debug (0,"%d %d %d %d %d %d",scr_w,scr_h,x,y,w,h);
gtk_widget_set_uposition (server_mapshot_popup, x, y);
gtk_widget_show (server_mapshot_popup);
}
int server_clist_event_callback (GtkWidget *widget, GdkEvent *event) {
GdkEventButton *bevent = (GdkEventButton *) event;
GList *selection;
int row, column;
debug (7, "server_clist_event_callback() -- ");
if (event->type == GDK_BUTTON_PRESS &&
bevent->window == server_clist->clist_window) {
switch (bevent->button) {
case 1:
if (gtk_clist_get_selection_info (server_clist, bevent->x, bevent->y, &row, &column)) {
server_clist_select_one (row);
if ((column == 6) && cur_server && (games[cur_server->type].get_mapshot)) {
size_t buflen;
guchar* buf = NULL;
buflen = games[cur_server->type].get_mapshot (cur_server, &buf);
gdk_pointer_grab (server_clist->clist_window, FALSE,
GDK_POINTER_MOTION_HINT_MASK |
GDK_BUTTON1_MOTION_MASK |
GDK_BUTTON_RELEASE_MASK,
NULL, NULL, bevent->time);
// Quake 3-like gamma ramp for some games
switch (cur_server->type) {
case DOOM3_SERVER:
case EF_SERVER:
case Q3RALLY_SERVER:
case Q3_SERVER:
case Q4_SERVER:
case REACTION_SERVER:
case SMOKINGUNS_SERVER:
case TREMFUSION_SERVER:
case TREMULOUSGPP_SERVER:
case TREMULOUS_SERVER:
case WO_SERVER:
server_mapshot_preview_popup_show (buf, buflen, bevent->x, bevent->y, 1);
break;
default:
server_mapshot_preview_popup_show (buf, buflen, bevent->x, bevent->y, 0);
}
g_free (buf);
return TRUE;
}
}
break;
case 2:
if (gtk_clist_get_selection_info (server_clist, bevent->x, bevent->y, &row, NULL)) {
server_clist_select_one (row);
stat_one_server (cur_server);
}
return TRUE;
case 3:
if (gtk_clist_get_selection_info (server_clist, bevent->x, bevent->y, &row, NULL)) {
selection = server_clist->selection;
if (!g_list_find (selection, GINT_TO_POINTER (row)) && (bevent->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == 0) {
server_clist_select_one (row);
}
}
gtk_menu_popup (GTK_MENU (gtk_builder_get_object (builder, "server_menu")), NULL, NULL, NULL, NULL,
bevent->button, bevent->time);
return TRUE;
default:
return FALSE;
}
}
if (event->type == GDK_BUTTON_RELEASE &&
bevent->window == server_clist->clist_window) {
if (server_mapshot_popup) {
gdk_pointer_ungrab (bevent->time);
gtk_widget_hide (server_mapshot_popup);
}
}
return FALSE;
}
int server_info_clist_event_callback (GtkWidget *widget, GdkEvent *event) {
GdkEventButton *bevent = (GdkEventButton *) event;
GList *selection;
GtkCTreeNode *node, *node_under_mouse;
int row = -1;
int node_is_in_selection = 0;
if (event->type == GDK_BUTTON_PRESS
&& bevent->window == GTK_CLIST (srvinf_ctree)->clist_window
&& bevent->button == 3) {
if (gtk_clist_get_selection_info (GTK_CLIST (srvinf_ctree), bevent->x, bevent->y, &row, NULL)) {
selection = GTK_CLIST (srvinf_ctree)->selection;
if (!g_list_find (selection, GINT_TO_POINTER (row)) && (bevent->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == 0) {
node_under_mouse = gtk_ctree_node_nth (GTK_CTREE (srvinf_ctree), row);
if (node_under_mouse) {
// go through all selected masters and search if the one under the
// cursor is among them
while (selection) {
node = GTK_CTREE_NODE (selection->data);
if (node == node_under_mouse) {
node_is_in_selection = 1;
break;
}
selection = selection->next;
}
// clear selection and only select the one under the curser
if (!node_is_in_selection) {
gtk_ctree_unselect_recursive (GTK_CTREE (srvinf_ctree), NULL);
gtk_ctree_select (GTK_CTREE (srvinf_ctree), node_under_mouse);
}
}
}
}
gtk_menu_popup (GTK_MENU (gtk_builder_get_object(builder, "svinfo_menu")), NULL, NULL, NULL, NULL,
bevent->button, bevent->time);
return TRUE;
}
return FALSE;
}
void source_selection_changed (void) {
GList *selection = GTK_CLIST (source_ctree)->selection;
GtkCTreeNode *node;
struct master *m;
debug (6, "souce_selection_changed() --");
if (cur_source) {
g_slist_free (cur_source);
cur_source = NULL;
}
while (selection) {
node = (GtkCTreeNode *) selection->data;
m = (struct master *) gtk_ctree_node_get_row_data (
GTK_CTREE (source_ctree), node);
cur_source = g_slist_append (cur_source, m);
selection = selection->next;
}
update_server_lists_from_selected_source ();
server_clist_set_list (cur_server_list);
reset_main_status_bar(builder);
}
void source_ctree_selection_changed_callback (GtkWidget *widget,
int row, int column, GdkEvent *event, GtkWidget *button) {
debug (6, "source_ctree_selection_changed_callback(%p,%d,%d,%p,%p)", widget, row, column, event, button);
source_selection_changed ();
}
void source_selection_clear_master_servers (void) {
struct master *m;
GSList* source = NULL;
for (source = cur_source; source; source=source->next) {
m = (struct master *) source->data;
if (m == favorites || m->isgroup) {
continue;
}
server_list_free (m->servers);
m->servers = NULL;
}
update_server_lists_from_selected_source ();
server_clist_set_list (cur_server_list);
reset_main_status_bar(builder);
}
void clear_master_servers_callback (GtkWidget *widget,
int row, int column, GdkEvent *event, GtkWidget *button) {
source_selection_clear_master_servers ();
}
void add_to_player_filter_red_callback (GtkWidget *widget) {
add_to_player_filter (PLAYER_GROUP_RED);
}
void add_to_player_filter_green_callback (GtkWidget *widget) {
add_to_player_filter (PLAYER_GROUP_GREEN);
}
void add_to_player_filter_blue_callback (GtkWidget *widget) {
add_to_player_filter (PLAYER_GROUP_BLUE);
}
void add_to_player_filter (unsigned mask) {
GList *selection = player_clist->selection;
struct player *p;
int row;
if (!selection || !cur_server || stat_process) {
return;
}
row = GPOINTER_TO_INT (selection->data);
p = (struct player *) gtk_clist_get_row_data (player_clist, row);
if (player_filter_add_player (p->name, mask)) {
server_clist_build_filtered (cur_server_list, TRUE);
player_clist_redraw ();
}
}
void player_skin_preview_popup_show (guchar *skin, int top, int bottom, int x, int y) {
GtkWidget *frame;
int win_x, win_y, scr_w, scr_h;
if (!player_skin_popup) {
player_skin_popup = gtk_window_new (GTK_WINDOW_POPUP);
gtk_window_set_policy (GTK_WINDOW (player_skin_popup), FALSE, FALSE, TRUE);
frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
gtk_container_add (GTK_CONTAINER (player_skin_popup), frame);
gtk_widget_show (frame);
player_skin_popup_preview = gtk_preview_new (GTK_PREVIEW_COLOR);
gtk_container_add (GTK_CONTAINER (frame), player_skin_popup_preview);
gtk_preview_size (GTK_PREVIEW (player_skin_popup_preview), 320, 200);
gtk_widget_show (player_skin_popup_preview);
}
else {
gtk_widget_hide (player_skin_popup);
}
gdk_window_get_origin (player_clist->clist_window, &win_x, &win_y);
x += win_x;
y += win_y;
scr_w = gdk_screen_width ();
scr_h = gdk_screen_height ();
x = (x + 320 > scr_w) ? scr_w - 320 : x;
y = (y + 200 > scr_h) ? scr_h - 200 : y;
gtk_widget_set_uposition (player_skin_popup, x, y);
gtk_widget_show (player_skin_popup);
draw_qw_skin (player_skin_popup_preview, skin, top, bottom);
}
int player_clist_event_callback (GtkWidget *widget, GdkEvent *event) {
GdkEventButton *bevent = (GdkEventButton *) event;
guchar *skindata;
int row, column;
struct player *p;
switch (event->type) {
case GDK_BUTTON_PRESS:
if (bevent->window == player_clist->clist_window) {
if (gtk_clist_get_selection_info (player_clist, bevent->x, bevent->y, &row, &column)) {
if (row >= 0 && row < g_slist_length (cur_server->players)) {
if ((column == 2 || column == 3) && cur_server &&
(games[cur_server->type].flags & GAME_QUAKE1_SKIN) != 0) {
p = (struct player *) gtk_clist_get_row_data (player_clist, row);
skindata = get_qw_skin (p->skin, games[QW_SERVER].real_dir);
gdk_pointer_grab (player_clist->clist_window, FALSE,
GDK_POINTER_MOTION_HINT_MASK |
GDK_BUTTON1_MOTION_MASK |
GDK_BUTTON_RELEASE_MASK,
NULL, NULL, bevent->time);
player_skin_preview_popup_show (skindata, p->shirt, p->pants, bevent->x, bevent->y);
if (skindata) {
g_free (skindata);
}
return TRUE;
}
else {
if (bevent->button == 3) {
gtk_clist_select_row (player_clist, row, column);
gtk_menu_popup (GTK_MENU (gtk_builder_get_object (builder, "player_menu")), NULL, NULL, NULL, NULL, bevent->button, bevent->time);
return TRUE;
}
}
}
}
}
break;
case GDK_2BUTTON_PRESS:
case GDK_3BUTTON_PRESS:
return TRUE;
case GDK_BUTTON_RELEASE:
if (player_skin_popup) {
gdk_pointer_ungrab (bevent->time);
gtk_widget_hide (player_skin_popup);
}
break;
default:
break;
}
return FALSE;
}
void statistics_callback (GtkWidget *widget) {
if (stat_process) {
return;
}
statistics_dialog ();
}
void populate_main_toolbar (void) {
GtkWidget *image;
char buf[128];
unsigned mask;
int i;
gtk_image_set_from_file (GTK_IMAGE (gtk_builder_get_object (builder, "update-image")), g_build_filename (xqf_PACKAGE_DATA_DIR, "xpm", "update.xpm", NULL));
gtk_image_set_from_file (GTK_IMAGE (gtk_builder_get_object (builder, "refresh-image")), g_build_filename (xqf_PACKAGE_DATA_DIR, "xpm", "refresh.xpm", NULL));
gtk_image_set_from_file (GTK_IMAGE (gtk_builder_get_object (builder, "refrsel-image")), g_build_filename (xqf_PACKAGE_DATA_DIR, "xpm", "refrsel.xpm", NULL));
gtk_image_set_from_file (GTK_IMAGE (gtk_builder_get_object (builder, "stop-image")), g_build_filename (xqf_PACKAGE_DATA_DIR, "xpm", "stop.xpm", NULL));
gtk_image_set_from_file (GTK_IMAGE (gtk_builder_get_object (builder, "connect-image")), g_build_filename (xqf_PACKAGE_DATA_DIR, "xpm", "connect.xpm", NULL));
// Filter buttons
for (i = 0, mask = 1; i < FILTERS_TOTAL; i++, mask <<= 1) {
if (!filters[i].pix) {
filter_buttons[i] = NULL;
continue;
}
filter_buttons[i] = GTK_WIDGET (gtk_toggle_tool_button_new ());
g_signal_connect (G_OBJECT (filter_buttons[i]), "toggled", G_CALLBACK (filter_toggle_callback), GINT_TO_POINTER (mask));
gtk_tool_button_set_label (GTK_TOOL_BUTTON (filter_buttons[i]), _(filters[i].short_name));
// Translators: e.g. Server Filter
g_snprintf (buf, 128, _("%s Filter Enable / Disable"), _(filters[i].name));
gtk_widget_set_tooltip_text (GTK_WIDGET (filter_buttons[i]), buf);
image = gtk_image_new_from_file (g_build_filename (xqf_PACKAGE_DATA_DIR, "xpm", filters[i].icon_name, NULL));
gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (filter_buttons[i]), image);
gtk_widget_show (image);
gtk_widget_show (filter_buttons[i]);
gtk_toolbar_insert (GTK_TOOLBAR (gtk_builder_get_object (builder, "main-toolbar")), GTK_TOOL_ITEM (filter_buttons[i]), -1);
gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (filter_buttons[i]), ((cur_filter & mask) != 0)? TRUE : FALSE);
}
set_toolbar_appearance (GTK_TOOLBAR (gtk_builder_get_object (builder, "main-toolbar")), default_toolbar_style, default_toolbar_tips);
}
// build server filter menu for toolbar
GtkWidget* create_filter_menu () {
unsigned int i;
GtkWidget *menu;
GtkWidget *menu_item;
struct server_filter_vars* filter = NULL;
GSList* rbgroup = NULL;
filter_menu_radio_buttons = NULL;
menu = gtk_menu_new ();
menu_item = gtk_menu_item_new_with_label (_("Configure"));
gtk_menu_append (GTK_MENU (menu), menu_item);
gtk_widget_show (menu_item);
g_signal_connect (menu_item, "activate", G_CALLBACK (start_filters_cfg_dialog), (gpointer) FILTER_SERVER);
menu_item = gtk_menu_item_new ();
gtk_widget_set_sensitive (menu_item, FALSE);
gtk_menu_append (GTK_MENU (menu), menu_item);
gtk_widget_show (menu_item);
for (i = 0; i <= server_filters->len; i++) {
char* name = NULL;
if (i == 0) {
filter = NULL;
name = _("None");
}
else {
filter = g_array_index (server_filters, struct server_filter_vars*, i-1);
name = filter->filter_name;
}
menu_item = gtk_radio_menu_item_new_with_label (rbgroup, name);
rbgroup = gtk_radio_menu_item_group (GTK_RADIO_MENU_ITEM (menu_item));
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menu_item), FALSE);
filter_menu_radio_buttons = g_slist_append (filter_menu_radio_buttons, menu_item);
gtk_menu_append (GTK_MENU (menu), menu_item);
gtk_widget_show (menu_item);
// array starts from zero but filters from 1
g_signal_connect (menu_item, "activate", G_CALLBACK (server_filter_select_callback), GINT_TO_POINTER(i));
}
return menu;
}
void quick_filter_entry_changed (GtkWidget* entry, gpointer data) {
const char* text = gtk_entry_get_text (GTK_ENTRY (entry));
int mask = 0;
debug (3, "%d <%s>", strlen (text), text);
if (!text || !*text) {
if (filter_quick_get ()) {
mask = FILTER_QUICK_MASK;
}
filter_quick_set (NULL);
}
else {
if (!filter_quick_get ()) {
mask = FILTER_QUICK_MASK;
}
filter_quick_set (text);
}
filters[FILTER_QUICK].last_changed = filter_time_inc ();
filter_toggle_callback (NULL, mask);
}
void quickfilter_delete_button_clicked (GtkWidget *widget, GtkWidget* entry) {
gtk_editable_delete_text (GTK_EDITABLE (entry), 0, -1);
gtk_widget_grab_focus (entry);
}
static gboolean create_main_window (void) {
GError *error = NULL;
#if defined GUI_GTK3
builder = gtk_builder_new_from_file (g_build_filename (xqf_PACKAGE_DATA_DIR, "ui", "xqf-gtk3.ui", NULL));
#elif defined GUI_GTK2
builder = gtk_builder_new ();
gtk_builder_add_from_file (builder, g_build_filename (xqf_PACKAGE_DATA_DIR, "ui", "xqf-gtk2.ui", NULL), &error);
#else
fprintf (stderr, "No UI has been compiled!\n");
return FALSE;
#endif
if (G_UNLIKELY (error != NULL)) {
fprintf (stderr, "Could not load UI: %s\n", error->message);
g_clear_error (&error);
return FALSE;
}
main_window = GTK_WIDGET (gtk_builder_get_object (builder, "main-window"));
gtk_builder_connect_signals (builder, NULL);
g_signal_connect (main_window, "delete_event", G_CALLBACK (window_delete_event_callback), NULL);
g_signal_connect (main_window, "destroy", G_CALLBACK (ui_done), NULL);
g_signal_connect (main_window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
gtk_window_set_title (GTK_WINDOW (main_window), "XQF");
register_window (main_window);
gtk_widget_realize (main_window);
return TRUE;
}
void populate_main_window (void) {
GtkWidget *main_vbox;
GtkWidget *hbox;
GtkWidget *entry;
GtkWidget *button;
GtkWidget *pixmap;
GtkAccelGroup *accel_group;
int i;
accel_group = gtk_accel_group_new ();
/*
Before we create the right-click server menu, we
need to set up all of the needed filter entries. This
is a terrible thing to do because in menus.c, the function
that builds the menu expects a const. However, we are not going
go g_free or otherwise reuse the memory we g_malloc so we should be
okay.
If you change the number of menu items before the various filters,
you need to change the server_filter_widget line in xqf.h.
*/
main_vbox = GTK_WIDGET (gtk_builder_get_object (builder, "main-vbox"));
// Lazy way to get `copy to clipboard' feature working.
// Don't gtk_widget_show() selection_manager widget!
selection_manager = GTK_EDITABLE (gtk_entry_new());
gtk_box_pack_start (GTK_BOX (main_vbox), GTK_WIDGET (selection_manager), FALSE, FALSE, 0);
gtk_widget_realize (GTK_WIDGET (selection_manager));
// add server filters to menu
server_filter_menu_items = g_array_new (FALSE, FALSE, sizeof (GtkWidget*));
gtk_menu_item_set_submenu (GTK_MENU_ITEM (gtk_builder_get_object (builder, "svfilter_menu_item")), create_filter_menu ());
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "view_hostnames_menu_item")), show_hostnames);
gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "view_defport_menu_item")), show_default_port);
populate_main_toolbar ();
pane1_widget = GTK_WIDGET (gtk_builder_get_object (builder, "hpaned"));
// Sources CTree
source_ctree = create_source_ctree (GTK_WIDGET (gtk_builder_get_object (builder, "scrollwin-sources")));
gtk_widget_show (source_ctree);
g_signal_connect (source_ctree, "tree_select_row", G_CALLBACK (source_ctree_selection_changed_callback), NULL);
g_signal_connect (source_ctree, "tree_unselect_row", G_CALLBACK (source_ctree_selection_changed_callback), NULL);
g_signal_connect (source_ctree, "event", G_CALLBACK (source_ctree_event_callback), NULL);
gtk_widget_show (GTK_WIDGET (gtk_builder_get_object (builder, "scrollwin-sources")));
pane2_widget = GTK_WIDGET (gtk_builder_get_object (builder, "vpaned"));
// Server CList
hbox = GTK_WIDGET (gtk_builder_get_object (builder, "hbox"));
button = GTK_WIDGET (gtk_builder_get_object (builder, "button"));
pixmap = gtk_pixmap_new (delete_pix.pix, delete_pix.mask);
gtk_container_add (GTK_CONTAINER (button), pixmap);
gtk_widget_show_all (button);
entry = GTK_WIDGET (gtk_builder_get_object (builder, "entry"));
g_signal_connect (entry, "changed", G_CALLBACK (quick_filter_entry_changed), NULL);
g_signal_connect (button, "clicked", G_CALLBACK (quickfilter_delete_button_clicked), entry);
server_clist = GTK_CLIST (create_cwidget (GTK_WIDGET (gtk_builder_get_object (builder, "scrollwin-server")), &server_clist_def));
g_signal_connect (server_clist, "click_column", G_CALLBACK (clist_set_sort_column), &server_clist_def);
g_signal_connect (server_clist, "event", G_CALLBACK (server_clist_event_callback), NULL);
g_signal_connect (server_clist, "select_row", G_CALLBACK (server_clist_select_callback), NULL);
g_signal_connect (server_clist, "unselect_row", G_CALLBACK (server_clist_unselect_callback), NULL);
g_signal_connect (server_clist, "key_press_event", G_CALLBACK (server_clist_keypress_callback), NULL);
gtk_clist_set_compare_func (server_clist, (GtkCListCompareFunc) server_clist_compare_func);
gtk_widget_show (GTK_WIDGET (server_clist));
pane3_widget = GTK_WIDGET (gtk_builder_get_object (builder, "hpaned2"));
// Player CList
player_clist = GTK_CLIST (create_cwidget (GTK_WIDGET (gtk_builder_get_object (builder, "scrollwin-player")), &player_clist_def));
g_signal_connect (player_clist, "click_column", G_CALLBACK (clist_set_sort_column), &player_clist_def);
g_signal_connect (player_clist, "event", G_CALLBACK (player_clist_event_callback), NULL);
gtk_clist_set_compare_func (player_clist, (GtkCListCompareFunc) player_clist_compare_func);
gtk_widget_show (GTK_WIDGET (player_clist));
// Server Info CList
srvinf_ctree = GTK_CTREE (create_cwidget (GTK_WIDGET (gtk_builder_get_object (builder, "scrollwin-server-info")), &srvinf_clist_def));
g_signal_connect (srvinf_ctree, "click_column", G_CALLBACK (clist_set_sort_column), &srvinf_clist_def);
g_signal_connect (srvinf_ctree, "event", G_CALLBACK (server_info_clist_event_callback), NULL);
gtk_clist_set_compare_func (GTK_CLIST (srvinf_ctree), (GtkCListCompareFunc) srvinf_clist_compare_func);
gtk_widget_show (GTK_WIDGET (srvinf_ctree));
gtk_widget_ensure_style (GTK_WIDGET (server_clist));
i = calculate_clist_row_height (GTK_WIDGET (server_clist), games[Q1_SERVER].pix->pix);
gtk_clist_set_row_height (server_clist, i);
gtk_clist_set_row_height (player_clist, i);
gtk_clist_set_row_height (GTK_CLIST (srvinf_ctree), i);
gtk_clist_set_row_height (GTK_CLIST (source_ctree), i);
// Status Bar & Progress Bar
hbox = GTK_WIDGET (gtk_builder_get_object (builder, "hbox-status-bar"));
main_filter_status_bar = GTK_WIDGET (gtk_builder_get_object (builder, "main-filter-status-bar"));
gtk_widget_set_usize (main_filter_status_bar, 100, -1); // ???
main_progress_bar = create_progress_bar ();
gtk_widget_set_usize (main_progress_bar, 200, -1);
gtk_box_pack_end (GTK_BOX (hbox), main_progress_bar, FALSE, FALSE, 0);
gtk_widget_show (main_progress_bar);
// Make sure the current filter is dispalyed and applied if needed
set_server_filter_menu_list_text ();
restore_main_window_geometry ();
gtk_window_set_default_icon_name ("xqf");
gtk_window_add_accel_group (GTK_WINDOW (main_window), accel_group);
gtk_accel_group_unref (accel_group);
// Set tooltips - also in prefs_load
tooltips = gtk_tooltips_new ();
if (default_toolbar_tips) {
gtk_tooltips_enable (tooltips);
}
else {
gtk_tooltips_disable (tooltips);
}
gtk_widget_grab_focus (entry);
}
void play_sound (const char *sound, gboolean override) {
play_sound_with (sound_player, sound, override);
}
void play_sound_with (const char* player, const char *sound, gboolean override) {
int pid;
if (!sound || !*sound) {
return;
}
if (!sound_enable && !override) {
debug (2, "sound disabled - not playing");
return;
}
if (!player || !*player) {
xqf_warning (_("no sound player configured"));
return;
}
pid = fork ();
if (pid == 0) {
char *argv[3];
argv[0] = g_strdup (player);
if (sound[0] != '/') {
// Does not start with a / so prepend user_rcdir
debug (1, "Prepending user_rcdir to sound file");
argv[1] = file_in_dir (user_rcdir, sound);
}
else {
argv[1] = g_strdup (sound);
}
argv[2] = NULL;
debug (1, "sound player (program): %s", argv[0]);
debug (1, "sound to play: %s", argv[1]);
execvp (argv[0], argv);
g_free (argv[1]);
_exit (1);
}
}
void cmdlinehelp () {
puts ("XQF Version " PACKAGE_VERSION);
puts (_(
"Usage:\n"
"\txqf [OPTIONS]\n"
"\n"
"OPTIONS:\n"
"\t--launch \"[SERVERTYPE] IP\"\tlaunch game on specified server\n"
"\t--add \"[SERVERTYPE] IP\"\tadd specified server to favorites\n"
"\t--debug <level>\t\t\tset debug level\n"
"\t--version\t\t\tprint version and exit\n"));
exit (0);
}
char* cmdline_add_server = NULL;
gboolean cmdline_launch = FALSE;
gboolean cmdline_newversion = FALSE;
// must always return FALSE to stop g_timeout
gboolean check_cmdline_launch (gpointer nothing) {
char* token[2] = {0};
enum server_type type = UNKNOWN_SERVER;
unsigned n = 0;
char* addrstring = NULL; // must point to a copy
if (!cmdline_add_server) {
return FALSE;
}
n = tokenize_bychar (cmdline_add_server, token, 2, ' ');
if (n == 2) // type and address given
{
type = id2type (token[0]);
if (type == UNKNOWN_SERVER) {
addrstring = add_server_dialog (&type, token[1]);
}
else {
addrstring = g_strdup (token[1]);
}
}
else if (n == 1) // only address
{
char *addr;
unsigned short port;
unsigned matches = 0;
if (!parse_address (token[0], &addr, &port)) {
dialog_ok (NULL, _("\"%s\" is not valid host[:port] combination."), token[0]);
g_free (cmdline_add_server);
return FALSE;
}
if (port) // guess the type from the port
{
unsigned i = 0;
for (i = KNOWN_SERVER_START; i < UNKNOWN_SERVER; i++) {
if (games[i].default_port == port) {
++matches;
if (type == UNKNOWN_SERVER) type = i;
}
}
}
if (!port || type == UNKNOWN_SERVER || matches > 1) {
addrstring = add_server_dialog (&type, token[0]);
}
else {
addrstring = g_strdup (cmdline_add_server);
}
}
prepare_new_server_to_favorites (type, addrstring, cmdline_launch);
g_free (cmdline_add_server);
return FALSE;
}
struct option long_options[] =
{
{"launch", 1, 0, 'l'},
{"add", 1, 0, 'a'},
{"debug", 1, 0, 'd'},
{"dontlaunch", 0, 0, 128},
{"version", 0, 0, 'v'},
{"help", 0, 0, 'h'},
{"newversion", 0, 0, 129},
{"nomapscan", 0, 0, 130},
{0, 0, 0, 0}
};
void parse_commandline (int argc, char* argv[]) {
while (1) {
int c;
int option_index = 0;
c = getopt_long (argc, argv, "d:l:h", long_options, &option_index);
if (c == -1) {
break;
}
switch (c) {
case 'd':
set_debug_level (atoi (optarg));
break;
case 'l':
g_free (cmdline_add_server);
cmdline_add_server = g_strdup (optarg);
cmdline_launch = TRUE;
break;
case 'a':
g_free (cmdline_add_server);
cmdline_add_server = g_strdup (optarg);
break;
case 'h':
cmdlinehelp ();
break;
case 'v':
puts ("XQF Version " PACKAGE_VERSION);
exit (0);
break;
case 128:
dontlaunch = TRUE;
break;
case 129:
cmdline_newversion = TRUE;
break;
case 130:
skip_startup_mapscan = TRUE;
break;
case '?':
case ':':
exit (1);
break;
default:
xqf_warning ("getopt error, starting anyway ...");
return;
}
}
}
void add_pixmap_path_for_theme (const char* theme) {
char dir[PATH_MAX];
snprintf (dir, sizeof (dir), "%s/%s", xqf_PACKAGE_DATA_DIR, theme);
add_pixmap_directory (dir);
snprintf (dir, sizeof (dir), "%s/.local/share/xqf/%s", g_get_home_dir (), theme);
add_pixmap_directory (dir);
}
void init_config_path () {
char dir[PATH_MAX];
config_add_dir (xqf_PACKAGE_DATA_DIR);
snprintf (dir, sizeof (dir), "%s/.local/share/xqf", g_get_home_dir ());
config_add_dir (dir);
config_add_dir (user_rcdir);
}
void init_scripts_path () {
char dir[PATH_MAX];
snprintf (dir, sizeof (dir), "%s/scripts", xqf_PACKAGE_DATA_DIR);
scripts_add_dir (dir);
snprintf (dir, sizeof (dir), "%s/.local/share/xqf/scripts", g_get_home_dir ());
scripts_add_dir (dir);
snprintf (dir, sizeof (dir), "%s/scripts", user_rcdir);
scripts_add_dir (dir);
}
int main (int argc, char *argv[]) {
char* var = NULL;
int newversion = FALSE;
xqf_start_time = time (NULL);
redialserver = 0;
var = getenv ("xqf_PACKAGE_DATA_DIR");
if (var) {
xqf_PACKAGE_DATA_DIR = var;
}
var = getenv ("xqf_LOCALEDIR");
if (var) {
xqf_LOCALEDIR = var;
}
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, xqf_LOCALEDIR);
bind_textdomain_codeset (PACKAGE, "UTF-8");
textdomain (PACKAGE);
set_debug_level (DEFAULT_DEBUG_LEVEL);
debug (5, "main() -- Debug Level Default Set at %d", DEFAULT_DEBUG_LEVEL);
// migrate config directory to follow XDG Base Directory Specification
// http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
// https://developer.gnome.org/basedir-spec/
// https://developer.gnome.org/glib/2.37/glib-Miscellaneous-Utility-Functions.html#g-get-user-config-dir
if (!rc_migrate_dir ()) {
return 1;
}
if (!init_user_info ()) {
return 1;
}
gtk_init (&argc, &argv);
parse_commandline (argc, argv);
if (dns_spawn_helper () < 0) {
xqf_error ("Unable to start DNS helper");
return 1;
}
add_pixmap_path_for_theme ("default");
add_pixmap_directory (xqf_PACKAGE_DATA_DIR);
qstat_configfile = g_build_filename (xqf_PACKAGE_DATA_DIR, "qstat.cfg", NULL);
dns_gtk_init ();
rc_check_dir ();
init_games ();
init_config_path ();
newversion = prefs_load () | cmdline_newversion;
if (default_icontheme) {
add_pixmap_path_for_theme (default_icontheme);
}
init_scripts_path ();
scripts_load ();
#ifdef USE_GEOIP
geoip_init ();
#endif
gtk_preview_set_gamma (1.5);
props_load ();
filters_init ();
host_cache_load ();
init_masters (newversion);
client_init ();
ignore_sigpipe ();
on_sig (SIGUSR1, sighandler_debug);
on_sig (SIGUSR2, sighandler_debug);
add_server_init ();
add_master_init ();
psearch_init ();
rcon_init ();
if (check_qstat_version () == FALSE) {
dialog_ok (NULL, _("You need at least qstat version %s for xqf to function properly"), required_qstat_version);
}
if (!create_main_window ())
return 1;
init_pixmaps (main_window);
play_sound (sound_xqf_start, 0);
script_action_start ();
populate_main_window ();
gtk_widget_show (main_window);
source_ctree_select_source (favorites);
filter_menu_activate_current ();
print_status (GTK_WIDGET (gtk_builder_get_object (builder, "main-status-bar")), NULL);
if (default_auto_favorites && !cmdline_add_server) {
refresh_callback (NULL, NULL);
}
g_timeout_add (0, check_cmdline_launch, NULL);
debug (1, "startup time %ds", time (NULL) - xqf_start_time);
gtk_main ();
play_sound (sound_xqf_quit, 0);
script_action_quit ();
unregister_window (main_window);
main_window = NULL;
if (stat_process) {
stop_callback (NULL, NULL);
}
debug (1, "total servers: %d", servers_total ());
debug (1, "total uservers: %d", uservers_total ());
debug (1, "total hosts: %d", hosts_total ());
if (GTK_WIDGET (gtk_builder_get_object(builder, "server_menu"))) {
debug (6, "EXIT: destroy server_menu");
gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object(builder, "server_menu")));
}
if (gtk_builder_get_object(builder, "player_menu")) {
debug (6, "EXIT: destroy player_menu");
gtk_widget_destroy (GTK_WIDGET (gtk_builder_get_object(builder, "player_menu")));
}
if (player_skin_popup) {
gtk_widget_destroy (player_skin_popup);
}
if (server_mapshot_popup) {
gtk_widget_destroy (server_mapshot_popup);
}
pixmap_cache_clear (&qw_colors_pixmap_cache, 0);
pixmap_cache_clear (&server_pixmap_cache, 0);
free_pixmaps ();
filters_done ();
props_free_all ();
debug (6, "EXIT: Free Server Lists");
g_slist_free (cur_source);
server_list_free (cur_server_list);
userver_list_free (cur_userver_list);
if (cur_server) {
server_unref (cur_server);
cur_server = NULL;
}
host_cache_save ();
host_cache_clear ();
debug (6, "EXIT: Free Master Lists");
free_masters ();
debug (6, "EXIT: Call rcon_done.");
rcon_done ();
psearch_done ();
add_server_done ();
add_master_done ();
client_detach_all ();
free_user_info ();
dns_helper_shutdown ();
config_sync ();
config_drop_all ();
#ifdef USE_GEOIP
geoip_done ();
#endif
games_done ();
debug (6, "EXIT: Done.");
return 0;
}