/* * wmwrapper.c: * a gtk wrapper that acts as a fallback to keep X running in * the event of a window manager crash, non-zero exit status, * or brief run time. * * Compiling: * gcc wmwrapper.c -o wmwrapper `pkg-config --cflags gtk+-2.0` \ * `pkg-config --libs gtk+-2.0` * * Usage (~/.xinitrc): * wmwrapper * wmwrapper openbox * wmwrapper fluxbox * * Example Configuration (~/.wmwrapperrc): * [config] * # specify a list of window managers to show in the dropdown * wmlist=blackbox;fluxbox;openbox;wmaker;xterm * # end X session if window manager exits with a status of 0, * # false to always show wmwrapper dialog * exit_on_zero=true * # if exit_on_zero is true and the window manager exits with * # status of 0, but runs less than safety_timeout seconds, * # show dialog * safety_timeout=5 * # if exit_on_zero is true and the window manager exits with * # a status of 0, but this file exists, show dialog * noexit_file=~/.wmwrapper_noexit * * Related: * http://ordiluc.net/selectwm/ * * Mike Hokenson */ #include #include #include #include #include #include /* configuration options */ static int safety_timeout = 5; static int exit_on_zero = TRUE; static gchar **wmlist, *noexit_file; static GtkWidget *status_label; void set_status(const gchar *text) { if (text) { gchar *markup = g_strdup_printf("%s", text); gtk_label_set_markup(GTK_LABEL(status_label), markup); g_free(markup); gtk_widget_show(status_label); } else gtk_widget_hide(status_label); } void execute(GtkWidget *widget, gpointer data) { GtkComboBox *combo = data; GtkWidget *entry = GTK_BIN(combo)->child; GtkWidget *window = GTK_WIDGET(combo)->parent->parent; const gchar *command = gtk_entry_get_text(GTK_ENTRY(entry)); GError *err = NULL; time_t start; gint status; set_status(NULL); if (!*command) { set_status("empty command"); return; } gtk_widget_hide(window); while (gtk_events_pending()) gtk_main_iteration(); start = time(NULL); /* capturing stdout/stderr would be nice, but it's making a zombie of the wm until any and all applications it spawned exit, meaning the wmwrapper dialog won't show up */ if (!g_spawn_command_line_sync(command, NULL, NULL, &status, &err)) { set_status(err->message); g_error_free(err); } else { if (exit_on_zero && status == 0) { gint seconds = time(NULL) - start; gchar *text = NULL; if (safety_timeout && seconds < safety_timeout) text = g_strdup_printf("\"%s\" finished in %d second(s), below the safety_timeout of %d second(s)", command, seconds, safety_timeout); else if (g_file_test(noexit_file, G_FILE_TEST_IS_REGULAR)) text = g_strdup_printf("\"%s\" found", noexit_file); else { if (gtk_main_level() > 0) gtk_main_quit(); else exit(0); } if (text) set_status(text); g_free(text); } gtk_combo_box_prepend_text(combo, command); } gtk_widget_show(window); gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1); } void destroy(GtkWidget *widget, gpointer data) { gtk_main_quit(); } gchar *expand_tilde(const gchar *str) { if (!str || !str[0]) return g_strdup(""); if (strncmp(str, "~/", 2)) return g_strdup(str); return g_build_filename(g_get_home_dir(), str + 2, NULL); } int main(int argc, char *argv[]) { GtkWidget *window; GtkWidget *vbox; GtkWidget *hbox; GtkWidget *button; GtkComboBox *combo; GtkWidget *entry; GString *command; gchar *config; GKeyFile *keyfile; /* -- load configuration ------------------------------------------------- */ config = g_build_filename(g_get_home_dir(), ".wmwrapperrc", NULL); keyfile = g_key_file_new(); if (g_file_test(config, G_FILE_TEST_IS_REGULAR)) { GError *err = NULL; if (!g_key_file_load_from_file(keyfile, config, G_KEY_FILE_NONE, &err)) { g_critical("%s\n", err->message); g_error_free(err); } if (g_key_file_has_key(keyfile, "config", "wmlist", NULL)) wmlist = g_key_file_get_string_list(keyfile, "config", "wmlist", NULL, NULL); if (g_key_file_has_key(keyfile, "config", "safety_timeout", NULL)) { gint n = g_key_file_get_integer(keyfile, "config", "safety_timeout", NULL); if (n >= 0) safety_timeout = n; } if (g_key_file_has_key(keyfile, "config", "exit_on_zero", NULL)) exit_on_zero = g_key_file_get_boolean(keyfile, "config", "exit_on_zero", NULL); if (g_key_file_has_key(keyfile, "config", "noexit_file", NULL)) { gchar *s = g_key_file_get_string(keyfile, "config", "noexit_file", NULL); noexit_file = expand_tilde(s); g_free(s); } } else noexit_file = g_build_filename(g_get_home_dir(), ".wmwrapper_noexit", NULL); /* -- process any command line arguments --------------------------------- */ command = g_string_new(NULL); if (argc > 1) { gint i; for (i = 1; i < argc; i++) { if (command->len) command = g_string_append(command, " "); command = g_string_append(command, argv[i]); } } /* -- create window ------------------------------------------------------ */ gtk_init(&argc, &argv); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_stick(GTK_WINDOW(window)); gtk_window_set_resizable(GTK_WINDOW(window), FALSE); gtk_window_set_keep_above(GTK_WINDOW(window), TRUE); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_widget_set_size_request(window, 250, -1); g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy), NULL); gtk_container_set_border_width(GTK_CONTAINER(window), 10); vbox = gtk_vbox_new(FALSE, 3); gtk_container_add(GTK_CONTAINER(window), vbox); gtk_widget_show(vbox); combo = (GtkComboBox *) gtk_combo_box_entry_new_text(); gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(combo), FALSE, FALSE, 0); gtk_widget_show(GTK_WIDGET(combo)); status_label = gtk_label_new(NULL); gtk_widget_set_size_request(status_label, 225, -1); gtk_label_set_line_wrap(GTK_LABEL(status_label), TRUE); gtk_label_set_justify(GTK_LABEL(status_label), GTK_JUSTIFY_CENTER); gtk_box_pack_start(GTK_BOX(vbox), GTK_WIDGET(status_label), FALSE, FALSE, 0); entry = GTK_BIN(combo)->child; gtk_entry_set_text(GTK_ENTRY(entry), command->str); gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1); gtk_entry_set_activates_default(GTK_ENTRY(GTK_BIN(combo)->child), TRUE); if (wmlist) { gint i; for (i = 0; wmlist[i]; i++) gtk_combo_box_append_text(combo, wmlist[i]); } else { gtk_combo_box_append_text(combo, "fluxbox"); gtk_combo_box_append_text(combo, "openbox"); gtk_combo_box_append_text(combo, "wmaker"); gtk_combo_box_append_text(combo, "xterm"); } hbox = gtk_hbox_new(FALSE, 2); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); gtk_widget_show(hbox); gtk_box_set_homogeneous(GTK_BOX(hbox), TRUE); button = gtk_button_new_from_stock(GTK_STOCK_EXECUTE); gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0); gtk_widget_show(button); GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT); gtk_window_set_default(GTK_WINDOW(window), button); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(execute), combo); button = gtk_button_new_from_stock(GTK_STOCK_QUIT); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(destroy), NULL); gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0); gtk_widget_show(button); if (command->len) execute(NULL, combo); else gtk_widget_show(window); gtk_main(); g_string_free(command, TRUE); g_strfreev(wmlist); g_free(noexit_file); g_key_file_free(keyfile); g_free(config); exit(0); }