/*
 * Copyright (C) 2006-2010 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
 *               2006-2008 Jim Huang <jserv.tw@gmail.com>
 *               2008 Fred Chien <fred@lxde.org>
 *               2009 Jürgen Hötzel <juergen@archlinux.org>
 *               2009-2010 Marty Jack <martyj19@comcast.net>
 *               2010 Lajos Kamocsay <lajos@panka.com>
 *               2012 Piotr Sipika <Piotr.Sipika@gmail.com>
 *               2012-2013 Henry Gebhardt <hsggebhardt@gmail.com>
 *               2012 Jack Chen <speed.up08311990@gmail.com>
 *               2012 Rafał Mużyło <galtgendo@gmail.com>
 *               2012 Michael Rawson <michaelrawson76@gmail.com>
 *               2012 Julien Lavergne <julien.lavergne@gmail.com>
 *               2013 Rouslan <rouslan-k@users.sourceforge.net>
 *               2013 peadaredwards <peadaredwards@users.sourceforge.net>
 *               2014-2016 Andriy Grytsenko <andrej@rep.kiev.ua>
 *
 * This file is a part of LXPanel 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <glib/gi18n.h>
#include <stdlib.h>
#include <glib/gstdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <locale.h>
#include <string.h>
#include <gdk/gdkx.h>
#include <libfm/fm-gtk.h>
#include <keybinder.h>

#define __LXPANEL_INTERNALS__

#include "private.h"
#include "misc.h"
#include "plugin.h"

#include "lxpanelctl.h"
#include "dbg.h"
#include "space.h"

static gchar *cfgfile = NULL;
static gchar version[] = VERSION;
static int config = 0;

static gboolean mon_override = FALSE;
static gboolean is_restarting = FALSE;

static LXPanel *first_panel = NULL;

Command commands[] = {
    //{ "configure", N_("Preferences"), configure },
    { "run", N_("Run"), gtk_run },
    { "restart", N_("Restart"), restart },
    { "logout", N_("Logout"), logout },
    { NULL, NULL, NULL },
};

void restart(void)
{
    ENTER;
    is_restarting = TRUE;

    gtk_main_quit();
    RET();
}

/* copied from configurator.c */
#define UPDATE_GLOBAL_INT(panel,name,val) do { \
    config_setting_t *_s = config_setting_add(config_setting_get_elem(config_setting_get_member(config_root_setting(panel->config),""),\
                                                                      0),\
                                              name,PANEL_CONF_TYPE_INT);\
    if (_s) config_setting_set_int(_s,val); } while(0)

static void process_client_msg ( XClientMessageEvent* ev )
{
    int cmd = ev->data.b[0];
    int monitor;
    int edge;
    char *plugin_type;
    char *command;
    switch( cmd )
    {
#ifndef DISABLE_MENU
        case LXPANEL_CMD_SYS_MENU:
        {
            GSList* l;
            for( l = all_panels; l; l = l->next )
            {
                LXPanel* p = (LXPanel*)l->data;
                GList *plugins, *pl;

                if (p->priv->box == NULL)
                    continue;
                plugins = gtk_container_get_children(GTK_CONTAINER(p->priv->box));
                for (pl = plugins; pl; pl = pl->next)
                {
                    const LXPanelPluginInit *init = PLUGIN_CLASS(pl->data);
                    if (init->show_system_menu)
                        /* queue to show system menu */
                        init->show_system_menu(pl->data);
                }
                g_list_free(plugins);
            }
            break;
        }
#endif
        case LXPANEL_CMD_RUN:
            gtk_run();
            break;
        case LXPANEL_CMD_CONFIG:
            {
            LXPanel * p = ((all_panels != NULL) ? all_panels->data : NULL);
            if (p != NULL)
                panel_configure(p, 0);
            }
            break;
        case LXPANEL_CMD_RESTART:
            restart();
            break;
        case LXPANEL_CMD_EXIT:
            gtk_main_quit();
            break;
        case LXPANEL_CMD_REFRESH:
            {
                GSList *l;
                for (l = all_panels; l; l = l->next)
                {
                    LXPanel *p = (LXPanel *) l->data;
                    if (p != NULL)
                    {
                        // at some point I may need to read some more parameters here, but this will do for now...
                        char linebuf[256], posbuf[16];
                        int val;

                        // try to find a config file...
                        FILE *fp;
                        char *file;
                        file = g_build_filename (g_get_user_config_dir(), CONFIG_DIR, cprofile, "panels", p->priv->name, NULL);
                        fp = fopen (file, "rb");
                        g_free (file);
                        if (!fp)
                        {
                            const gchar * const *dir = g_get_system_config_dirs();
                            if (dir)
                            {
                                while (dir[0])
                                {
                                    file = g_build_filename (dir[0], CONFIG_DIR, cprofile, "panels", p->priv->name, NULL);
                                    fp = fopen (file, "rb");
                                    g_free (file);
                                    if (fp) break;
                                    dir++;
                                }
                            }
                        }

                        while (fp && !feof (fp))
                        {
                            if (fgets (linebuf, 256, fp))
                            {
                                if (sscanf (linebuf, "%*[ \t]iconsize=%d", &val) == 1)
                                {
                                    p->priv->icon_size = val;
                                    p->priv->height = val;
                                    panel_set_panel_configuration_changed (p->priv);
                                }
                                if (sscanf (linebuf, "%*[ \t]edge=%s", posbuf) == 1)
                                {
                                    if (!strcmp (posbuf, "bottom"))
                                    {
                                        if (p->priv->edge != EDGE_BOTTOM)
                                        {
                                            p->priv->edge = EDGE_BOTTOM;
                                            gtk_widget_queue_resize(GTK_WIDGET(p));
                                        }
                                    }
                                    else
                                    {
                                        if (p->priv->edge != EDGE_TOP)
                                        {
                                            p->priv->edge = EDGE_TOP;
                                            gtk_widget_queue_resize(GTK_WIDGET(p));
                                        }
                                    }
                                }
                                if (sscanf (linebuf, "%*[ \t]monitor=%d", &val) == 1)
                                {
                                    if (!mon_override || gdk_display_get_n_monitors (gtk_widget_get_display (GTK_WIDGET (p))) > 1)
                                    {
                                        p->priv->monitor = val;
                                        panel_set_panel_configuration_changed (p->priv);
                                    }
                                }

                                /*
                                 * This is a really unpleasant hack...
                                 * I need to be able to get the taskbar plugin to resize elements based
                                 * on an updated value in the global config file.
                                 * The taskbar plugin cannot see the name of the profile used to launch
                                 * lxpanel, so it cannot read the config file itself.
                                 * The panel does not have access to the local data structures of a plugin,
                                 * so it can't write the data directly to the plugin.
                                 * The messaging interface from panel to plugin does not support arguments to
                                 * commands.
                                 * So the only way I can see to do this is to create a single-word command which
                                 * incorporates the argument, and send that via the messaging interface.
                                 * It's nasty, but it works. I'm open to suggestions for a better (more generic)
                                 * way of doing this - ideally it should be possible to get every plugin to
                                 * update from the config file simultaneously, but after banging my head on the
                                 * keyboard for a day, I can't see a simple and efficient way to do that...
                                 */
                                if (sscanf (linebuf, "%*[ \t]MaxTaskWidth=%d", &val) == 1)
                                {
                                    GList *plugins, *pl;
                                    const LXPanelPluginInit *init;
                                    char buf[10];

                                    init = g_hash_table_lookup (lxpanel_get_all_types (), "taskbar");
                                    if (init && init->control)
                                    {
                                        plugins = gtk_container_get_children (GTK_CONTAINER(p->priv->box));
                                        for (pl = plugins; pl; pl = pl->next)
                                        {
                                            if (init == PLUGIN_CLASS(pl->data))
                                            {
                                                sprintf (buf, "mtw%d\n", val);
                                                init->control (pl->data, buf);
                                                break;
                                            }
                                        }
                                        g_list_free (plugins);
                                    }
                                }
                            }
                        }
                        if (fp) fclose (fp);
                    }
                }
            }
            break;
        case LXPANEL_CMD_MOVE:
            {
                GSList *l;
                for (l = all_panels; l; l = l->next)
                {
                    LXPanel *p = (LXPanel *) l->data;
                    if (p != NULL)
                    {
                        int ppm = p->priv->monitor;
                        if (p->priv->monitor < gdk_display_get_n_monitors (gtk_widget_get_display (GTK_WIDGET (p))) - 1)
                            p->priv->monitor++;
                        else
                            p->priv->monitor = 0;
                        if (p->priv->monitor != ppm)
                        {
                            panel_set_panel_configuration_changed (p->priv);
                            UPDATE_GLOBAL_INT (p->priv, "monitor", p->priv->monitor);
                            lxpanel_config_save (p);
                        }
                    }
                }
            }
            break;
        case LXPANEL_CMD_COMMAND:
            monitor = (ev->data.b[1] & 0xf) - 1; /* 0 for no monitor */
            edge = (ev->data.b[1] >> 4) & 0x7;
            if ((ev->data.b[1] & 0x80) != 0)
                /* some extension, not supported yet */
                break;
            plugin_type = g_strndup(&ev->data.b[2], 18);
            command = strchr(plugin_type, '\t');
            if (command) do /* use do{}while(0) to enable break */
            {
                LXPanel *p;
                GSList *l;
                GList *plugins, *pl;
                const LXPanelPluginInit *init;
                GtkWidget *plugin = NULL;

                *command++ = '\0';
                /* find the panel by monitor and edge */
                for (l = all_panels; l; l = l->next)
                {
                    p = (LXPanel*)l->data;
                    if (p->priv->box == NULL) /* inactive panel */
                        continue;
                    if (monitor >= 0 && p->priv->monitor != monitor)
                        continue;
                    if (edge == EDGE_NONE || p->priv->edge == edge)
                        break;
                }
                if (l == NULL) /* match not found */
                    break;
                /* find the plugin */
                init = g_hash_table_lookup(lxpanel_get_all_types(), plugin_type);
                if (init == NULL) /* no such plugin known */
                    break;
                plugins = gtk_container_get_children(GTK_CONTAINER(p->priv->box));
                for (pl = plugins; pl; pl = pl->next)
                {
                    if (init == PLUGIN_CLASS(pl->data))
                    {
                        plugin = pl->data;
                        break;
                    }
                }
                g_list_free(plugins);
                /* test for built-in commands ADD and DEL */
                if (strcmp(command, "ADD") == 0)
                {
                    if (plugin == NULL)
                    {
                        config_setting_t *cfg;

                        cfg = config_group_add_subgroup(config_root_setting(p->priv->config),
                                                        "Plugin");
                        config_group_set_string(cfg, "type", plugin_type);
                        plugin = lxpanel_add_plugin(p, plugin_type, cfg, -1);
                        if (plugin == NULL) /* failed to create */
                            config_setting_destroy(cfg);
                    }
                }
                else if (strcmp(command, "DEL") == 0)
                {
                    if (plugin != NULL)
                        lxpanel_remove_plugin(p, plugin);
                }
                /* send the command */
                else if (plugin && init->control)
                    init->control(plugin, command);
            } while(0);
            g_free(plugin_type);
            break;
        case LXPANEL_CMD_NOTIFY:
            monitor = (ev->data.b[1] & 0xf) - 1; /* 0 for no monitor */
            edge = (ev->data.b[1] >> 4) & 0x7;
            if ((ev->data.b[1] & 0x80) != 0)
                /* some extension, not supported yet */
                break;
            do /* use do{}while(0) to enable break */
            {
                LXPanel *p;
                GSList *l;
                size_t siz;
                FILE *fp;
                char *buf;

                /* find the panel by monitor and edge */
                for (l = all_panels; l; l = l->next)
                {
                    p = (LXPanel*)l->data;
                    if (p->priv->box == NULL) /* inactive panel */
                        continue;
                    if (monitor >= 0 && p->priv->monitor != monitor)
                        continue;
                    if (edge == EDGE_NONE || p->priv->edge == edge)
                        break;
                }
                if (l == NULL) /* match not found */
                    break;

                buf = NULL;
                fp = fopen (&ev->data.b[2], "rb");
                if (fp)
                {
                    if (getdelim (&buf, &siz, 0, fp) > 0)
                    {
                        lxpanel_notify (p, buf);
                        free (buf);
                    }
                    fclose (fp);
                }
                remove (&ev->data.b[2]);
            } while(0);
            break;
    }
}

static GdkFilterReturn
panel_event_filter(GdkXEvent *xevent, GdkEvent *event, gpointer not_used)
{
    Atom at;
    Window win;
    XEvent *ev = (XEvent *) xevent;

    ENTER;
    DBG("win = 0x%x\n", ev->xproperty.window);
    if (ev->type != PropertyNotify )
    {
        /* private client message from lxpanelctl */
        if( ev->type == ClientMessage && ev->xproperty.atom == a_LXPANEL_CMD )
        {
            process_client_msg( (XClientMessageEvent*)ev );
        }
        else if( ev->type == DestroyNotify )
        {
            fb_ev_emit_destroy( fbev, ((XDestroyWindowEvent*)ev)->window );
        }
        RET(GDK_FILTER_CONTINUE);
    }

    at = ev->xproperty.atom;
    win = ev->xproperty.window;
    if (win == GDK_ROOT_WINDOW())
    {
        if (at == a_NET_CLIENT_LIST)
        {
            fb_ev_emit(fbev, EV_CLIENT_LIST);
        }
        else if (at == a_NET_CURRENT_DESKTOP)
        {
            GSList* l;
            for( l = all_panels; l; l = l->next )
                ((LXPanel*)l->data)->priv->curdesk = get_net_current_desktop();
            fb_ev_emit(fbev, EV_CURRENT_DESKTOP);
        }
        else if (at == a_NET_NUMBER_OF_DESKTOPS)
        {
            GSList* l;
            for( l = all_panels; l; l = l->next )
                ((LXPanel*)l->data)->priv->desknum = get_net_number_of_desktops();
            fb_ev_emit(fbev, EV_NUMBER_OF_DESKTOPS);
        }
        else if (at == a_NET_DESKTOP_NAMES)
        {
            fb_ev_emit(fbev, EV_DESKTOP_NAMES);
        }
        else if (at == a_NET_ACTIVE_WINDOW)
        {
            fb_ev_emit(fbev, EV_ACTIVE_WINDOW );
        }
        else if (at == a_NET_CLIENT_LIST_STACKING)
        {
            fb_ev_emit(fbev, EV_CLIENT_LIST_STACKING);
        }
        else if (at == a_XROOTPMAP_ID)
        {
            GSList* l;
            for( l = all_panels; l; l = l->next )
                _panel_queue_update_background((LXPanel*)l->data);
        }
        else
            return GDK_FILTER_CONTINUE;

        return GDK_FILTER_REMOVE;
    }
    return GDK_FILTER_CONTINUE;
}

/* The same for new plugins type - they will be not unloaded by FmModule */
#define REGISTER_STATIC_MODULE(pc) do { \
    extern LXPanelPluginInit lxpanel_static_plugin_##pc; \
    lxpanel_register_plugin_type(#pc, &lxpanel_static_plugin_##pc); } while (0)

/* Initialize the static plugins. */
static void init_static_plugins(void)
{
#ifdef STATIC_SEPARATOR
    REGISTER_STATIC_MODULE(separator);
#endif

#ifdef STATIC_LAUNCHTASKBAR
    REGISTER_STATIC_MODULE(launchbar);
    REGISTER_STATIC_MODULE(taskbar);
#endif

#ifdef STATIC_TRAY
    REGISTER_STATIC_MODULE(tray);
#endif
}

static void
usage()
{
    g_print(_("lxpanel %s - lightweight GTK+ panel for UNIX desktops\n"), version);
    g_print(_("Command line options:\n"));
    g_print(_(" --help      -- print this help and exit\n"));
    g_print(_(" --version   -- print version and exit\n"));
//    g_print(_(" --log <number> -- set log level 0-5. 0 - none 5 - chatty\n"));
//    g_print(_(" --configure -- launch configuration utility\n"));
    g_print(_(" --profile name -- use specified profile\n"));
    g_print("\n");
    g_print(_(" -h  -- same as --help\n"));
    g_print(_(" -p  -- same as --profile\n"));
    g_print(_(" -v  -- same as --version\n"));
 //   g_print(_(" -C  -- same as --configure\n"));
    g_print(_("\nVisit http://lxde.org/ for detail.\n\n"));
}

/* Lightweight lock related functions - X clipboard hacks */

#define CLIPBOARD_NAME "LXPANEL_SELECTION"

/*
 * clipboard_get_func - dummy get_func for gtk_clipboard_set_with_data ()
 */
static void
clipboard_get_func(
    GtkClipboard *clipboard G_GNUC_UNUSED,
    GtkSelectionData *selection_data G_GNUC_UNUSED,
    guint info G_GNUC_UNUSED,
    gpointer user_data_or_owner G_GNUC_UNUSED)
{
}

/*
 * clipboard_clear_func - dummy clear_func for gtk_clipboard_set_with_data ()
 */
static void clipboard_clear_func(
    GtkClipboard *clipboard G_GNUC_UNUSED,
    gpointer user_data_or_owner G_GNUC_UNUSED)
{
}

/*
 * Lightweight version for checking single instance.
 * Try and get the CLIPBOARD_NAME clipboard instead of using file manipulation.
 *
 * Returns TRUE if successfully retrieved and FALSE otherwise.
 */
static gboolean check_main_lock()
{
    static const GtkTargetEntry targets[] = { { CLIPBOARD_NAME, 0, 0 } };
    gboolean retval = FALSE;
    GtkClipboard *clipboard;
    Atom atom;
    Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());

    atom = gdk_x11_get_xatom_by_name(CLIPBOARD_NAME);

    XGrabServer(xdisplay);

    if (XGetSelectionOwner(xdisplay, atom) != None)
        goto out;

    clipboard = gtk_clipboard_get(gdk_atom_intern(CLIPBOARD_NAME, FALSE));

    if (gtk_clipboard_set_with_data(clipboard, targets,
                                    G_N_ELEMENTS (targets),
                                    clipboard_get_func,
                                    clipboard_clear_func, NULL))
        retval = TRUE;

out:
    XUngrabServer (xdisplay);
    gdk_display_flush (gdk_display_get_default());

    return retval;
}
#undef CLIPBOARD_NAME

static void _start_panels_from_dir(const char *panel_dir, int fallback)
{
    GDir* dir = g_dir_open( panel_dir, 0, NULL );
    const gchar* name;

    if( ! dir )
    {
        return;
    }

    while((name = g_dir_read_name(dir)) != NULL)
    {
        char* panel_config = g_build_filename( panel_dir, name, NULL );
        if (strchr(panel_config, '~') == NULL && name[0] != '.')    /* Skip editor backup files in case user has hand edited in this directory */
        {
            LXPanel* panel = fallback ? panel_new_mon_fb (panel_config, name) : panel_new (panel_config, name);
            if( panel )
            {
                all_panels = g_slist_prepend( all_panels, panel );
                if (!first_panel) first_panel = panel;
            }
        }
        g_free( panel_config );
    }
    g_dir_close( dir );
}

static gboolean start_all_panels( )
{
    char *panel_dir;
    const gchar * const * dir;

    /* try user panels */
    panel_dir = _user_config_file_name(is_wizard () ? "wizard" : "panels", NULL);

    /* check to see if there are any panels which will display on monitor 0 */
    char *cmd = g_strdup_printf ("grep -l Global $(grep -L monitor=[1-9] %s/*) /dev/null | grep -c . | grep -q 0", panel_dir);
    int res = system (cmd);
    g_free (cmd);
    /* if res == 0 here, then there are no panels defined which will display on monitor 0 */
    if (res == 0) mon_override = TRUE;

    _start_panels_from_dir(panel_dir, mon_override);
    g_free(panel_dir);
    if (all_panels != NULL)
        return TRUE;
    /* else try XDG fallbacks */
    dir = g_get_system_config_dirs();
    if (dir) while (dir[0])
    {
        panel_dir = _system_config_file_name(dir[0], is_wizard () ? "wizard" : "panels");
        _start_panels_from_dir(panel_dir, 0);
        g_free(panel_dir);
        if (all_panels != NULL)
            return TRUE;
        dir++;
    }
    /* last try at old fallback for compatibility reasons */
    panel_dir = _old_system_config_file_name("panels");
    _start_panels_from_dir(panel_dir, 0);
    g_free(panel_dir);
    if (all_panels != NULL) return TRUE;

    /* use the default panel */
    LXPanel* panel = panel_new ("/etc/xdg/lxpanel-pi/default", "default");
    all_panels = g_slist_prepend (all_panels, panel);
    first_panel = panel;
    return TRUE;
}

static void _ensure_user_config_dirs(void)
{
    char *dir = g_build_filename(g_get_user_config_dir(), CONFIG_DIR, cprofile,
                                 "panels", NULL);

    /* make sure the private profile and panels dir exists */
    g_mkdir_with_parents(dir, 0700);
    g_free(dir);
}

static void destroy_widget (gpointer data, gpointer user_data)
{
    GtkWidget *widget = (GtkWidget *) data;
    gtk_widget_destroy (widget);
}

int main(int argc, char *argv[], char *env[])
{
    int i;
    const char* desktop_name;

    setlocale(LC_CTYPE, "");

#if !GLIB_CHECK_VERSION(2, 32, 0)
    g_thread_init(NULL);
#endif
/*    gdk_threads_init();
    gdk_threads_enter(); */

    gtk_init(&argc, &argv);
    keybinder_init();

    bindtextdomain ( GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR );
    bind_textdomain_codeset ( GETTEXT_PACKAGE, "UTF-8" );
    textdomain ( GETTEXT_PACKAGE );

    XSetLocaleModifiers("");
    XSetErrorHandler((XErrorHandler) panel_handle_x_error);

    resolve_atoms();

    desktop_name = g_getenv("XDG_CURRENT_DESKTOP");
    is_in_lxde = desktop_name && (0 == strcmp(desktop_name, "LXDE"));

    for (i = 1; i < argc; i++) {
        if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
            usage();
            exit(0);
        } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
            printf("lxpanel %s\n", version);
            exit(0);
        } else if (!strcmp(argv[i], "--log")) {
            i++;
            if (i == argc) {
                g_critical( "lxpanel: missing log level");
                usage();
                exit(1);
            } else {
                /* deprecated */
            }
        } else if (!strcmp(argv[i], "--configure") || !strcmp(argv[i], "-C")) {
            config = 1;
        } else if (!strcmp(argv[i], "--profile") || !strcmp(argv[i], "-p")) {
            i++;
            if (i == argc) {
                g_critical( "lxpanel: missing profile name");
                usage();
                exit(1);
            } else {
                cprofile = g_strdup(argv[i]);
            }
        } else {
            printf("lxpanel: unknown option - %s\n", argv[i]);
            usage();
            exit(1);
        }
    }

    /* Check for duplicated lxpanel instances */
    if (!check_main_lock() && !config) {
        printf("There is already an instance of LXPanel.  Now to exit\n");
        exit(1);
    }

    _ensure_user_config_dirs();

    fbev = fb_ev_new();

    is_restarting = FALSE;

    /* init LibFM */
    fm_gtk_init(NULL);

    /* prepare modules data */
    lxpanel_prepare_modules();
    lxpanel_register_plugin_type("space", &_lxpanel_static_plugin_space);
    init_static_plugins();

    load_global_config();

    /* NOTE: StructureNotifyMask is required by XRandR
     * See init_randr_support() in gdkscreen-x11.c of gtk+ for detail.
     */
    gdk_window_set_events(gdk_get_default_root_window(), GDK_STRUCTURE_MASK |
            GDK_SUBSTRUCTURE_MASK | GDK_PROPERTY_CHANGE_MASK);
    gdk_window_add_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);

    if( G_UNLIKELY( ! start_all_panels() ) )
        g_warning( "Config files are not found.\n" );

    lxpanel_notify_init (first_panel);
/*
 * FIXME: configure??
    if (config)
        configure();
*/
    gtk_main();

    XSelectInput (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), GDK_ROOT_WINDOW(), NoEventMask);
    gdk_window_remove_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);

    /* destroy all panels */
    g_slist_foreach( all_panels, destroy_widget, NULL );
    g_slist_free( all_panels );
    all_panels = NULL;
    g_free( cfgfile );

    free_global_config();

    lxpanel_unload_modules();
    fm_gtk_finalize();

    /* gdk_threads_leave(); */

    g_object_unref(fbev);

    if (!is_restarting)
        return 0;
    if (strchr(argv[0], G_DIR_SEPARATOR))
        execve(argv[0], argv, env);
    else
        execve(g_find_program_in_path(argv[0]), argv, env);
    return 1;
}
