/*
 * Copyright (C) 2001-2017 the xine-project
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
 * USA
 *
 * gtk2 / gtk3 ui for xine
 *
 * main
 */

#include "globals.h"
#include "version.h"

#include <string.h>
#include <signal.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#ifdef HAVE_X11
#include <X11/Xlib.h>
#endif

#include <stdio.h>
#include <stdlib.h>

#ifdef HAVE_GETOPT_LONG
#include <getopt.h>
#endif

#include <glib.h>
#include <gdk/gdk.h>
#include <gtk/gtk.h>

#include <xine/xineutils.h>

#include "desktop_integration.h"
#include "engine.h"
#include "player.h"
#include "wizards.h"
#include "xml_widgets.h"
#include "noskin_window.h"
#include "server.h"
#include "open_mrl.h"
#include "log_window.h"
#include "preferences.h"
#include "key_events.h"
#include "mediamarks.h"
#include "playlist.h"
#include "settings.h"
#include "snapshot.h"
#include "play_item.h"
#include "lirc.h"
#include "stream_info.h"
#include "ui.h"
#include "utils.h"
#include "systray.h"
#include "vis.h"
#include "post.h"
#include "console_output.h"

/*
 * globals
 */

int             verbosity;
GtkWidget      *app = NULL;
const char     *plugindir, *bindir, *logodir, *pixmapdir, *icondir, *miscdir, *confdir;

#define X_INIT_THREADS() \
  if (!XInitThreads ()) \
  { \
    g_printerr (_("gtkvideo: XInitThreads failed - looks like you don't have a thread-safe xlib.\n")); \
    return 2; \
  }

static void
gxine_try_remote (int argc, char *argv[], gboolean enqueue, gboolean autoplay)
{
  if (!server_client_connect())
    return;

  /*
   * pass on files to play
   */

  if (!enqueue)
    server_client_send ("playlist_clear();\n");

  if (optind < argc)
  {
    int i;
    for (i = optind; i < argc; ++i)
    {
      if (autoplay && i == optind)
	server_client_send ("playlist_play (");
      server_client_send ("playlist_add (\"");
      char *uri = make_path_uri (argv[i]);
      server_client_send (uri ? uri : argv[i]);
      server_client_send ((autoplay && i == optind) ? "\"));\n" : "\");\n");
      free (uri);
    }
  }

  exit (0);
}

static gboolean splash_destroy (gpointer splash)
{
  gtk_widget_destroy (splash);
  return FALSE;
}

static void splash_show (void)
{
  gchar     *pathname;
  GtkWidget *splash, *fixed, *img, *text;

  gdk_threads_enter();

  splash = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  GtkWindow *w = GTK_WINDOW (splash);
  gtk_window_set_decorated (w, FALSE);
  gtk_window_set_type_hint (w, GDK_WINDOW_TYPE_HINT_SPLASHSCREEN);
  gtk_window_set_position (w, GTK_WIN_POS_CENTER_ALWAYS);
  gtk_window_set_keep_above (w, TRUE);
  gtk_window_stick (w);

  pathname = g_build_filename (pixmapdir, "splash.png", NULL);
  img = gtk_image_new_from_file (pathname);
  g_free (pathname);

  fixed = gtk_fixed_new ();
  gtk_container_add (GTK_CONTAINER (splash), fixed);
  gtk_fixed_put (GTK_FIXED (fixed), img, 0, 0);

  text = ui_label_new_with_markup ("<span font_desc='sans 12' color='#DDDDDD'>"VERSION"</span>");
#if GTK_CHECK_VERSION(3, 14, 0)
  gtk_label_set_xalign (GTK_LABEL (text), 1);
  gtk_label_set_yalign (GTK_LABEL (text), 1);
#else
  gtk_misc_set_alignment (GTK_MISC (text), 1, 1);
#endif
  gtk_fixed_put (GTK_FIXED (fixed), text, 0, 0);

  gtk_widget_show_all (splash);
  /* the widgets need to be realised before we can move the label */
  GtkAllocation allocation_img = { 0 };
  GtkAllocation allocation_text = { 0 };
  gtk_widget_get_allocation (img, &allocation_img);
  gtk_widget_get_allocation (text, &allocation_text);
  gtk_fixed_move (GTK_FIXED (fixed), text,
                  allocation_img.width  - allocation_text.width,
                  allocation_img.height - allocation_text.height);

  g_timeout_add (4000, splash_destroy, splash);

  do_pending_events ();
  gdk_threads_leave();
}

#ifndef GLIB_USES_SYSTEM_MALLOC
#include <assert.h>
gpointer xmalloc (gsize size)
{
  gpointer p = malloc (size);
  if (size)
    assert (p != NULL);
  return p;
}

gpointer xrealloc (gpointer p, gsize size)
{
  p = realloc (p, size);
  if (size)
    assert (p != NULL);
  return p;
}
#endif

/* Initialise some engine settings from saved preferences */
static gboolean post_init_configure (void)
{
  xine_cfg_entry_t entry;

  if (xine_config_lookup_entry (xine, "gui.post_plugins.deinterlace", &entry))
    gtk_video_set_post_plugins_deinterlace ((GtkVideo *)gtv, entry.str_value);

  if (xine_config_lookup_entry (xine, "gui.post_plugins.video", &entry))
    gtk_video_set_post_plugins_video ((GtkVideo *)gtv, entry.str_value);

  if (xine_config_lookup_entry (xine, "gui.post_plugins.audio", &entry))
    gtk_video_set_post_plugins_audio ((GtkVideo *)gtv, entry.str_value, audio_port);

  if (xine_config_lookup_entry (xine, "gui.post_plugins.deinterlace_enable",
				&entry))
    gtk_video_set_use_post_plugins_deinterlace ((GtkVideo *)gtv, entry.num_value);

  if (xine_config_lookup_entry (xine, "gui.post_plugins.video_enable", &entry))
    gtk_video_set_use_post_plugins_video ((GtkVideo *)gtv, entry.num_value);

  if (xine_config_lookup_entry (xine, "gui.post_plugins.audio_enable", &entry))
    gtk_video_set_use_post_plugins_audio ((GtkVideo *)gtv, entry.num_value, audio_port);

  if (xine_config_lookup_entry (xine, "gui.post_plugins.audio_visualisation",
				&entry))
    vis_set (entry.enum_values[entry.num_value]);

  if (xine_config_lookup_entry (xine, "gui.windowedmode_unblank", &entry))
    gtk_video_set_windowed_unblank ((GtkVideo *)gtv, entry.num_value);

  return FALSE;
}

static gboolean main_start_play (gpointer data)
{
  //if (gxine_init_count)
  //  return TRUE;
  playlist_play (*(int *)data);
  return FALSE;
}

#ifdef WITH_WATCHDOG
/* Watchdog functions */

static gboolean watchdog_timer_reset (gpointer data)
{
  alarm (30);
  return TRUE;
}

static void __attribute__ ((noreturn)) watchdog_timeout (int sig)
{
  /* if this happens, we have a problem... */
  g_printerr (_("gxine: killed by watchdog bite\n"));
  abort ();
}
#endif

/* Our own locking for GDK */

static GRecMutex gdk_lock;

static void gxine_gdk_lock (void)
{
  g_rec_mutex_lock (&gdk_lock);
}

static void gxine_gdk_unlock (void)
{
  g_rec_mutex_unlock (&gdk_lock);
}

/* Faults */

static void __attribute__ ((noreturn)) fault_handler (int sig)
{
  static gboolean recurse = FALSE;
  if (recurse)
    abort ();

  signal (sig, fault_handler);
  recurse = TRUE;

  const char *sigtext = strsignal (sig);
  fputs (_("gxine has suffered a fatal internal error.\n"
	   "To get a backtrace, run gxine in a debugger such as gdb.\n"
	   "Then, when the error occurs:\n"
	   "  (gdb) thread apply all bt\n"), stderr);
  display_error_modal (FROM_GXINE, _("Fatal error"), "%s",
		       sigtext ? : _("Unknown"));
  exit (2);
}

/* Main */

static const char *
getdir (const char *env, const char *dir)
{
  return getenv (env) ? : dir;
}

static void
list_plugins (const char *msg, typeof (xine_list_audio_output_plugins) list_func)
{
  static xine_t *xine = NULL;
  if (!xine)
    xine_init (xine = xine_new ());
  g_print ("%s", msg);
  const char *const *list = list_func (xine);
  while (list && *list)
  {
    g_print (" %s", *list);
    ++list;
  }
  g_print ("\n");
}

static void config_change (gpointer data, gpointer user)
{
  xine_cfg_entry_t entry;
  int i;
  char *text = data;
  char *equals = strchr (text, '=');
  if (!equals)
    return;
  *equals = 0;

  if (xine_config_lookup_entry (xine, text, &entry))
    switch (entry.type)
    {
      case XINE_CONFIG_TYPE_RANGE:
	i = atoi (equals + 1);
	entry.num_value = MIN (entry.range_max, MAX (entry.range_min, i));
	xine_config_update_entry (xine, &entry);
	break;

      case XINE_CONFIG_TYPE_NUM:
	entry.num_value = atoi (equals + 1);
	xine_config_update_entry (xine, &entry);
	break;

      case XINE_CONFIG_TYPE_BOOL:
	entry.num_value = !!atoi (equals + 1);
	xine_config_update_entry (xine, &entry);
	break;

      case XINE_CONFIG_TYPE_STRING:
	entry.str_value = equals + 1;
	xine_config_update_entry (xine, &entry);
	break;

      case XINE_CONFIG_TYPE_ENUM:
	for (i = 0; entry.enum_values[i]; ++i)
	  if (!strcmp (equals + 1, entry.enum_values[i]))
	    break;
	if (entry.enum_values[i])
	{
	  entry.num_value = i;
	  xine_config_update_entry (xine, &entry);
	}
	else /* given value isn't in the list of valid values */
	  g_printerr (_("gxine: warning: bad value for config item '%s'\n"), text);
	break;
    }
  else
    g_printerr (_("gxine: warning: config item '%s' not found\n"), text);

  *equals = '=';
}

#if !GTK_CHECK_VERSION(3, 0, 0)
static void strv_add_uniq(gchar **v, const char *s)
{
  size_t i;
  for (i = 0; v[i]; i++)
    if (!strcmp(v[i], s))
      return;
  v[i] = strdup(s);
}

static void add_gtkrc_files(void)
{
  const gchar  *home = g_get_home_dir ();
  size_t        homelen = strlen (home);
  gchar       **file = gtk_rc_get_default_files ();
  guint         length = file ? g_strv_length (file) : 0;
  guint         i;

  /* deep-copy the gtkrc filenames, allocating space for two more */
  gchar       **copy = calloc (length + 3, sizeof (gchar *));
  for (i = 0; file && file[i]; ++i)
    strv_add_uniq(copy, file[i]);
  length = g_strv_length (copy);

  /* find the index of the first *user* gtkrc filename */
  for (i = 0; copy[i]; ++i)
    if (!strncmp (copy[i], home, homelen) && copy[i][homelen] == '/')
      break;
  /* insert our system gtkrc filename before it (strdup for convenience) */
  if (i < length)
    memmove (copy + i + 1, copy + i, (length - i) * sizeof (gchar *));
  copy[i] = g_build_filename (confdir, FILE_GTKRC, NULL);
  /* now append our user gtkrc, terminate the list and tell GTK+ about it */
  copy[length + 1] = get_config_filename (FILE_GTKRC);
  copy[length + 2] = NULL;
  gtk_rc_set_default_files (copy);
  /* free the list */
  g_strfreev (copy);
}
#endif /* GTK2 */

#if GTK_CHECK_VERSION(3, 0, 0)
static void load_css_file(const char *path, guint priority)
{
  GtkCssProvider *css_provider;
  GError *gerror = NULL;
  struct stat st;

  if (stat (path, &st))
    return;

  css_provider = gtk_css_provider_new();
  gtk_css_provider_load_from_path(css_provider, path, &gerror);

  if (gerror) {
    fprintf (stderr, "Error loading styles from %s:\n\t%s\n", path, gerror->message);
    g_error_free (gerror);
    g_object_unref (css_provider);
    return;
  }

  gtk_style_context_add_provider_for_screen (gdk_display_get_default_screen (gdk_display_get_default ()),
                                             GTK_STYLE_PROVIDER (css_provider),
                                             priority);
}

static void load_gtk_css(void)
{
  gchar *path;

  path = g_build_filename (confdir, FILE_GTKCSS, NULL);
  load_css_file (path, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
  g_free (path);

  path = get_config_filename (FILE_GTKCSS);
  load_css_file (path, GTK_STYLE_PROVIDER_PRIORITY_USER);
  g_free (path);
}
#endif /* GTK3 */

gpointer cssprovider;
int main(int argc, char* argv[])
{
  gboolean enqueue, autoplay, fullscreen;
  gboolean no_connect, debug;
  int optstate;
  int show_splash = 2; /* default */
  const char *cmd = NULL;
  char *geometry = NULL;
  xine_cfg_entry_t entry;
  GPtrArray *cfg_changes;
  gboolean migrated_config_dir = FALSE;
  const char *video_driver_id = NULL;
  const char *audio_driver_id = NULL;

#if !defined(USE_SUN_XLIB_WORKAROUND) && defined(HAVE_X11)
  /* start using X... */
  X_INIT_THREADS ();
#endif

#ifndef GLIB_USES_SYSTEM_MALLOC
  GMemVTable vtab = { xmalloc, xrealloc, free, NULL, NULL, NULL };
  g_mem_set_vtable (&vtab);
#endif

  setlinebuf (stdout);
  setlinebuf (stderr);

  /*
   * init paths here. defaults are compiled in and may
   * be overwritten by environment variables
   */

  plugindir = getdir ("GXINE_PLUGINDIR", GXINE_PLUGINDIR);
  bindir    = getdir ("GXINE_BINDIR", GXINE_BINDIR);
  logodir   = getdir ("GXINE_LOGODIR", GXINE_LOGODIR);
  pixmapdir = getdir ("GXINE_PIXMAPDIR", GXINE_PIXMAPDIR);
  icondir   = getdir ("GXINE_ICONDIR", GXINE_ICONDIR);
  miscdir   = getdir ("GXINE_MISCDIR", GXINE_MISCDIR);
  confdir   = getdir ("GXINE_CONFDIR", GXINE_CONFDIR);

  /* set up our gtkrc stuff */
#if !GTK_CHECK_VERSION(3, 0, 0)
  add_gtkrc_files();
#endif

  /*
   * init glib/gdk/gtk thread safe/aware
   */

#ifdef ENABLE_NLS
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  bindtextdomain (PACKAGE".theme", LOCALEDIR);
  /* The next two lines prevent GTK errors caused by gettext's conversion of
   * text from UTF-8.
   */
  bind_textdomain_codeset (PACKAGE, "UTF-8");
  bind_textdomain_codeset (PACKAGE".theme", "UTF-8");
  bind_textdomain_codeset (LIB_PACKAGE, "UTF-8");
  textdomain (PACKAGE);
#endif

  console_output_init ();
  gdk_threads_set_lock_functions (gxine_gdk_lock, gxine_gdk_unlock);
  gdk_threads_init ();

#if GTK_CHECK_VERSION(3,0,0)
    /* gxine currently assumes that Gtk means X11, so it cannot use native Gtk
     * support for other backends such as Wayland. */
    gdk_set_allowed_backends ("x11,win32");
#endif

  if (!gtk_parse_args (&argc, &argv))
    return 2;

  /*
   * parse command line arguments
   */

  verbosity = 0;
  enqueue = FALSE;
  autoplay = TRUE;
  fullscreen = FALSE;
  no_connect = FALSE;
  debug = FALSE;
  optstate = 0;
  cfg_changes = g_ptr_array_new ();
  for (;;)
  {
#define OPTS "hvaefV:A:sSc:C:n"
#ifdef HAVE_GETOPT_LONG
    static const struct option longopts[] = {
      { "help", no_argument, NULL, 'h' },
      { "version", no_argument, NULL, 1 },
      { "verbose", no_argument, NULL, 'v' },
      { "add", no_argument, NULL, 'a' },
      { "enqueue", no_argument, NULL, 'e' },
      { "full-screen", no_argument, NULL, 'f' },
      { "video", required_argument, NULL, 'V' },
      { "audio", required_argument, NULL, 'A' },
      { "splash", no_argument, NULL, 's' },
      { "no-splash", no_argument, NULL, 'S' },
      { "command", required_argument, NULL, 'c' },
      { "configure", required_argument, NULL, 'C' },
      { "geometry", required_argument, NULL, 2 },
      { "debug", no_argument, NULL, 3 },
      { NULL }
    };
    int index = 0;
    int opt = getopt_long (argc, argv, OPTS, longopts, &index);
#else
    int opt = getopt(argc, argv, OPTS);
#endif
    if (opt == -1)
      break;

    switch (opt)
    {
    case 'h':
      optstate |= 1;
      break;
    case 1:
      optstate |= 4;
      break;
    case 2:
      geometry = optarg;
      break;
    case 3:
      debug = TRUE;
      break;
    case 'v':
      verbosity++;
      break;
    case 'a':
      autoplay = TRUE;
      enqueue = TRUE;
      break;
    case 'e':
      autoplay = FALSE;
      enqueue = TRUE;
      break;
    case 'f':
      fullscreen = TRUE;
      break;
    case 'V':
      video_driver_id = optarg;
      break;
    case 'A':
      audio_driver_id = optarg;
      break;
    case 's':
      show_splash = TRUE;
      break;
    case 'S':
      show_splash = FALSE;
      break;
    case 'c':
      cmd = optarg;
      break;
    case 'n':
      no_connect = TRUE;
      break;
    case 'C':
      if (strchr (optarg, '='))
	g_ptr_array_add (cfg_changes, optarg);
      else
	g_printerr (_("gxine: warning: bad parameter for --configure: '%s'\n"), optarg);
      break;
    default:
      optstate |= 2;
      break;
    }
  }

  if (optstate & 1)
  {
    g_print (_("\
gxine %s (%s)\n\
usage: %s [options] [MRLs...]\n\
options:\n\
  -h, --help		this help text\n\
  -A, --audio DRIVER	try to use this audio driver\n\
  -V, --video DRIVER	try to use this video driver\n\
  -S, --no-splash	don't show the splash window\n\
  -c, --command TEXT	Javascript command(s) to be executed at startup\n\
  -a, --add		don't clear the playlist, play the first new item\n\
  -e, --enqueue		don't clear the playlist, don't play the new items\n\
  -f, --full-screen	start in full-screen mode\n\
  -v, --verbose		be more verbose\n\
  -C, --configure KEY=VALUE\n\
			alter a configuration item\n\
\n"), VERSION, VENDOR_PKG_VERSION CSET_ID, argv[0]);
    list_plugins (_("Available video drivers:"), gxine_list_video_output_plugins);
    list_plugins (_("Available audio drivers:"), xine_list_audio_output_plugins);
  }
  else if (optstate & 4)
    g_print (_("gxine %s (%s)\nusing xine-lib %s, compiled using %s\n%s\n\
This is free software; see the source for copying conditions.  There is NO\n\
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE,\n\
to the extent permitted by law.\n"),
	     VERSION, VENDOR_PKG_VERSION CSET_ID,
	     xine_get_version_string (), XINE_VERSION,
	     get_copyright_notice ());
  if (optstate & 2)
  {
    g_printerr (_("gxine: invalid option (try -h or --help)\n"));
    return 1;
  }

  if (optstate)
    return 0;

  /* init gtk windowing */

  gtk_init(&argc, &argv);
  gtk_window_set_auto_startup_notification (FALSE);

  if (!debug)
    signal (SIGSEGV, fault_handler);

  /*
   * find out if gxine is already running, if so
   * just pass on the files to play
   */

  sched_yield ();
  if (!no_connect)
    gxine_try_remote (argc, argv, enqueue, autoplay);

  struct stat st;
  char *oldconfig = g_build_filename (g_get_home_dir (), ".gxine", NULL);
  char *newconfig = get_config_filename (NULL);

  /*
   * make sure $XDG_CONFIG_DIR/gxine exists, migrating from ~/.gxine if needed
   */
  if (strcmp (oldconfig, newconfig) &&
      !stat (oldconfig, &st) && stat (newconfig, &st) && errno == ENOENT)
  {
    char *tail = strrchr (newconfig, '/');
    *tail = 0; /* chop off the leafname for mkdir */
    int r = ensure_path_exists (newconfig, 0700);
    *tail = '/'; /* restore for the rename */
    if (r || rename (oldconfig, newconfig))
      display_warning
	(FROM_GXINE, _("File creation error"),
	 _("Couldn't create %s: %s.\n"
	   "Configuration, playlist and media marks will not be saved."),
	 newconfig, strerror (r ? r : errno));
    else
      migrated_config_dir = TRUE;
  }
  else
  {
    int r = ensure_path_exists (newconfig, 0700);
    if (r)
      display_warning
	(FROM_GXINE, _("File creation error"),
	 _("Couldn't create %s: %s.\n"
	   "Configuration, playlist and media marks will not be saved."),
	 newconfig, strerror (r));
  }

#ifdef WITH_WATCHDOG
  /*
   * Watchdog
   */

  if (!debug)
  {
    g_timeout_add (2500, watchdog_timer_reset, NULL); /* <- will set the alarm */
    signal (SIGALRM, watchdog_timeout);
  }
#endif

  /*
   * Load GTK3 css styles
   */

#if GTK_CHECK_VERSION(3, 0, 0)
  load_gtk_css();
#endif

  /*
   * init xine, set up script engine
   */

  engine_init ();
  server_setup ();

  static const xine_config_entry_translation_t gxine_config_xlate[] = {
    { "gui.windowwindow_size", "gui.windowed_mode.window_size" },
    { "gui.magnify_lowres_video", "gui.windowed_mode.magnify_lowres_video" },
    { "gui.auto_resize", "gui.windowed_mode.auto_resize" },
    { "gui.fullscreen_always_sticky", "gui.fullscreen_mode.always_sticky" },
    { "gui.fullscreen_toolbar", "gui.fullscreen_mode.toolbar" },
    { "gui.windowedmode_separate_toolbar", "gui.windowed_mode.separate_toolbar" },
    { "gui.windowedmode_unblank", "gui.windowed_mode.unblank" },
    { NULL, NULL }
  };
  xine_config_set_translation_user (gxine_config_xlate);

  /*
   * a splash screen for the impatient
   */

  if (show_splash == 2)
    show_splash = !xine_config_lookup_entry (xine, "gui.show_splash", &entry)
		  || entry.num_value;

  if (show_splash)
    splash_show ();

  gdk_threads_enter ();
  do_pending_events ();

  /*
   * finish configuration migration (if needed)
   */
  if (migrated_config_dir && xine_config_get_first_entry (xine, &entry))
  {
    int oldlen = strlen (oldconfig);

    do
    {
      if (entry.type == XINE_CONFIG_TYPE_STRING && entry.num_value &&
	  !strncmp (entry.str_value, oldconfig, oldlen) &&
	  (entry.str_value[oldlen] == '/' || entry.str_value[oldlen] == 0))
      {
	entry.str_value = g_strconcat (newconfig, entry.str_value + oldlen, NULL);
	logprintf (_("updating config item %s to %s\n"), entry.key, entry.str_value);
	xine_config_update_entry (xine, &entry);
	g_free (entry.str_value);
      }
    } while (xine_config_get_next_entry (xine, &entry));
  }

  if (migrated_config_dir)
    display_info (FROM_GXINE, _("gxine configuration moved"),
		  _("gxine's configuration has been moved to a new location, "
		    "in accordance with the freedesktop.org Desktop Entry "
		    "Specification."));

  g_free (oldconfig);
  g_free (newconfig);

  /*
   * set up player and some UI basics
   */

  player_init (audio_driver_id);
  ui_init ();

  gtk_window_set_default_icon_name (GXINE_LOGO);


  {
    char *fname = get_config_filename (FILE_ACCELS);
    gtk_accel_map_load (fname);
    free (fname);
  }

  do_pending_events ();
  gtk_window_set_auto_startup_notification (TRUE);
  noskin_main_init (video_driver_id, geometry, fullscreen);

#if defined(USE_SUN_XLIB_WORKAROUND) && defined(HAVE_X11)
  /* call XInitThreads after the window opens to avoid deadlock in libXi */
  X_INIT_THREADS ();
#endif

  do_pending_events ();

  /*
   * create all dialogue boxes etc.
   */

  file_dialog_init ();
  utils_init       ();
  open_mrl_init    ();
  log_window_init  ();
  preferences_init ();
  playlist_init    ();
  settings_init    ();
  mediamarks_init  ();
  play_item_init   ();
  key_events_init  ();
  snapshot_init    ();
  vis_init	   ();
  systray_init	   ();
  post_init	   ();
  stream_info_init ();
  gxine_vfs_init   ();
  wizards_init     ();
  gxine_lirc_init  ();

  g_ptr_array_foreach (cfg_changes, config_change, NULL);

  engine_startup_script ();

  /*
   * wizards (first run only)
   */

  run_wizards (FALSE);
  gdk_threads_leave ();

  post_init_configure ();
  initialised = TRUE;

  playlist_logo ((gpointer) 1);

  /*
   * argument parsing
   */

  if (optind < argc)
  {
    int i;
    static int first = -1;

    if (!enqueue)
      playlist_clear();

    for (i = optind; i < argc; ++i)
      if (first < 0)
	first = playlist_add_mrl (argv[i], -1);
      else
	playlist_add_mrl (argv[i], -1);

    if (first >= 0)
      g_idle_add (main_start_play, &first);
  }

  server_start ();

  /* execute any command-line JS */
  if (cmd)
    se_eval (gse, cmd, NULL, NULL, NULL, "--command");

  engine_startup ();
  noskin_post_init ();
  noskin_main_post_init (fullscreen);
  ui_post_init ();

  //gdk_threads_enter();
  gtk_main();
  //gdk_threads_leave();

  return 0;
}
