diff --git a/src/Makefile.am b/src/Makefile.am index 7e9954b09..365d096a6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -177,9 +177,6 @@ libmetacityinclude_HEADERS = \ metacity_theme_viewer_SOURCES= \ ui/theme-viewer.c -metacity_dialog_SOURCES= \ - ui/metacity-dialog.c - schema_bindings_SOURCES = \ core/schema-bindings.c \ metacity.schemas.in.in @@ -190,7 +187,6 @@ metacity.schemas.in: schema_bindings ${srcdir}/metacity.schemas.in.in ${builddir}/schema_bindings ${srcdir}/metacity.schemas.in.in ${builddir}/metacity.schemas.in bin_PROGRAMS=metacity metacity-theme-viewer -libexec_PROGRAMS=metacity-dialog api_version = $(METACITY_MAJOR_VERSION).$(METACITY_MINOR_VERSION) @@ -232,7 +228,6 @@ metacity_LDADD=@METACITY_LIBS@ libmetacity-private.la $(EFENCE) metacity_LDFLAGS=-export-dynamic metacity_theme_viewer_LDADD= @METACITY_LIBS@ libmetacity-private.la -metacity_dialog_LDADD=@METACITY_LIBS@ testboxes_SOURCES=include/util.h core/util.c include/boxes.h core/boxes.c core/testboxes.c testgradient_SOURCES=ui/gradient.h ui/gradient.c ui/testgradient.c diff --git a/src/core/delete.c b/src/core/delete.c index abd1fd2c4..629442325 100644 --- a/src/core/delete.c +++ b/src/core/delete.c @@ -55,227 +55,26 @@ delete_ping_reply_func (MetaDisplay *display, /* we do nothing */ } -static Window -window_from_string (const char *str) -{ - char *end; - unsigned long l; - - end = NULL; - - l = strtoul (str, &end, 16); - - if (end == NULL || end == str) - { - meta_warning (_("Could not parse \"%s\" as an integer"), - str); - return None; - } - - if (*end != '\0') - { - meta_warning (_("Did not understand trailing characters \"%s\" in string \"%s\""), - end, str); - return None; - } - - return l; -} - -static int -pid_from_string (const char *str) -{ - char *end; - long l; - - end = NULL; - - l = strtol (str, &end, 10); - - if (end == NULL || end == str) - { - meta_warning (_("Could not parse \"%s\" as an integer"), - str); - return None; - } - - if (*end != '\0') - { - meta_warning (_("Did not understand trailing characters \"%s\" in string \"%s\""), - end, str); - return None; - } - - return l; -} - static gboolean -parse_dialog_output (const char *str, - int *pid_out, - Window *win_out) +delete_window_callback (gpointer w_p) { - char **split; + meta_window_kill ((MetaWindow*) w_p); - split = g_strsplit (str, "\n", 2); - if (split && split[0] && split[1]) - { - g_strchomp (split[0]); - g_strchomp (split[1]); - - *pid_out = pid_from_string (split[0]); - *win_out = window_from_string (split[1]); - - g_strfreev (split); - - return TRUE; - } - else - { - g_strfreev (split); - meta_warning (_("Failed to parse message \"%s\" from dialog process\n"), - str); - return FALSE; - } + return FALSE; /* don't do it again */ } static void -search_and_destroy_window (int pid, - Window xwindow) +sigchld_handler (MetaNexus *nexus, guint arg1, gpointer arg2, gpointer user_data) { - /* Find the window with the given dialog PID, - * double check that it matches "xwindow", then - * kill the window. - */ - GSList *tmp; - gboolean found = FALSE; - GSList *windows; + MetaWindow *ours = (MetaWindow*) user_data; - if (xwindow == None) + if (GPOINTER_TO_INT (arg2) == ours->dialog_pid) { - meta_topic (META_DEBUG_PING, - "Window to destroy is None, doing nothing\n"); - return; + if (arg1 == 1 /* pressed "force quit" */) + g_idle_add (delete_window_callback, user_data); + + ours->dialog_pid = -1; /* forget it anyway */ } - - windows = meta_display_list_windows (meta_get_display ()); - tmp = windows; - - while (tmp != NULL) - { - MetaWindow *w = tmp->data; - - if (w->dialog_pid == pid) - { - if (w->xwindow != xwindow) - meta_topic (META_DEBUG_PING, - "Dialog pid matches but not xwindow (0x%lx vs. 0x%lx)\n", - w->xwindow, xwindow); - else - { - meta_window_kill (w); - found = TRUE; - } - } - - tmp = tmp->next; - } - - g_slist_free (windows); - - if (!found) - meta_topic (META_DEBUG_PING, - "Did not find a window with dialog pid %d xwindow 0x%lx\n", - pid, xwindow); -} - -static void -release_window_with_fd (int fd) -{ - /* Find the window with the given dialog PID, - * double check that it matches "xwindow", then - * kill the window. - */ - gboolean found = FALSE; - - GSList *windows = meta_display_list_windows (meta_get_display ()); - GSList *tmp = windows; - - while (tmp != NULL) - { - MetaWindow *w = tmp->data; - - if (w->dialog_pid >= 0 && - w->dialog_pipe == fd) - { - meta_topic (META_DEBUG_PING, - "Removing dialog with fd %d pid %d from window %s\n", - fd, w->dialog_pid, w->desc); - meta_window_free_delete_dialog (w); - found = TRUE; - } - - tmp = tmp->next; - } - - g_slist_free (windows); - - if (!found) - meta_topic (META_DEBUG_PING, - "Did not find a window with a dialog pipe %d\n", - fd); -} - -static gboolean -io_from_ping_dialog (GIOChannel *channel, - GIOCondition condition, - gpointer data) -{ - meta_topic (META_DEBUG_PING, - "IO handler from ping dialog, condition = %x\n", - condition); - - if (condition & G_IO_IN) - { - char *str; - gsize len; - GError *err; - - /* Go ahead and block for all data from child */ - str = NULL; - len = 0; - err = NULL; - g_io_channel_read_to_end (channel, - &str, &len, - &err); - - if (err) - { - meta_warning (_("Error reading from dialog display process: %s\n"), - err->message); - g_error_free (err); - } - - meta_topic (META_DEBUG_PING, - "Read %" G_GSIZE_FORMAT " bytes strlen %d \"%s\" from child\n", - len, str ? (int) strlen (str) : 0, str ? str : "NULL"); - - if (len > 0) - { - /* We're supposed to kill the given window */ - int pid; - Window xwindow; - - if (parse_dialog_output (str, &pid, &xwindow)) - search_and_destroy_window (pid, xwindow); - } - - g_free (str); - } - - release_window_with_fd (g_io_channel_unix_get_fd (channel)); - - /* Remove the callback */ - return FALSE; } static void @@ -285,15 +84,9 @@ delete_ping_timeout_func (MetaDisplay *display, void *user_data) { MetaWindow *window = user_data; - GError *err; - int child_pid; - int outpipe; - char *argv[9]; - char numbuf[32]; - char timestampbuf[32]; - char *window_id_str; char *window_title; - GIOChannel *channel; + gchar *window_content; + GPid dialog_pid; meta_topic (META_DEBUG_PING, "Got delete ping timeout for %s\n", @@ -304,54 +97,32 @@ delete_ping_timeout_func (MetaDisplay *display, meta_window_present_delete_dialog (window, timestamp); return; } - - window_id_str = g_strdup_printf ("0x%lx", window->xwindow); + window_title = g_locale_from_utf8 (window->title, -1, NULL, NULL, NULL); - sprintf (numbuf, "%d", window->screen->number); - sprintf (timestampbuf, "%u", timestamp); - - argv[0] = METACITY_LIBEXECDIR"/metacity-dialog"; - argv[1] = "--screen"; - argv[2] = numbuf; - argv[3] = "--timestamp"; - argv[4] = timestampbuf; - argv[5] = "--kill-window-question"; - argv[6] = window_title; - argv[7] = window_id_str; - argv[8] = NULL; - - err = NULL; - if (!g_spawn_async_with_pipes ("/", - argv, - NULL, - 0, - NULL, NULL, - &child_pid, - NULL, - &outpipe, - NULL, - &err)) - { - meta_warning (_("Error launching metacity-dialog to ask about killing an application: %s\n"), - err->message); - g_error_free (err); - goto out; - } + window_content = g_strdup_printf( + _("%s is not responding.\n\n" + "You may choose to wait a short while for it to " + "continue or force the application to quit entirely."), + window_title); - window->dialog_pid = child_pid; - window->dialog_pipe = outpipe; - - channel = g_io_channel_unix_new (window->dialog_pipe); - g_io_add_watch_full (channel, G_PRIORITY_DEFAULT, - G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - io_from_ping_dialog, - NULL, NULL); - g_io_channel_unref (channel); - - out: g_free (window_title); - g_free (window_id_str); + + dialog_pid = + meta_show_dialog ("--question", + window_content, 0, + window->screen->number, + _("_Wait"), _("_Force Quit"), window->xwindow, + NULL, NULL); + + g_free (window_content); + + window->dialog_pid = dialog_pid; + + g_signal_connect (sigchld_nexus, "sigchld", + G_CALLBACK (sigchld_handler), + window); + } void @@ -461,9 +232,7 @@ meta_window_free_delete_dialog (MetaWindow *window) if (window->dialog_pid >= 0) { kill (window->dialog_pid, 9); - close (window->dialog_pipe); window->dialog_pid = -1; - window->dialog_pipe = -1; } } diff --git a/src/core/keybindings.c b/src/core/keybindings.c index a7c6adead..27fe298d5 100644 --- a/src/core/keybindings.c +++ b/src/core/keybindings.c @@ -2416,6 +2416,7 @@ error_on_command (int command_index, text, NULL, screen_number, + NULL, NULL, 0, NULL, NULL); g_free (text); @@ -2427,6 +2428,7 @@ error_on_command (int command_index, message, NULL, screen_number, + NULL, NULL, 0, NULL, NULL); } } diff --git a/src/core/main.c b/src/core/main.c index 0fafeaf58..52e32795d 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -59,6 +59,7 @@ #include #include +#include #include #include #include @@ -467,6 +468,29 @@ sigterm_handler (int signum) exit (meta_exit_code); } + +static guint sigchld_signal_id = 0; + +static void +sigchld_handler (int signum, siginfo_t *info, void *context) +{ + int stat; + + if (info->si_code == CLD_EXITED) + { + g_signal_emit (sigchld_nexus, sigchld_signal_id, 0, + info->si_status, + GINT_TO_POINTER (info->si_pid)); + } + + g_signal_handlers_disconnect_matched (sigchld_nexus, + G_SIGNAL_MATCH_DATA, + sigchld_signal_id, + 0, NULL, NULL, + GINT_TO_POINTER (info->si_pid)); + + waitpid (info->si_pid, &stat, WNOHANG); +} /** * This is where the story begins. It parses commandline options and @@ -497,6 +521,8 @@ main (int argc, char **argv) if (setlocale (LC_ALL, "") == NULL) meta_warning ("Locale not understood by C library, internationalization will not work\n"); + + g_type_init (); sigemptyset (&empty_mask); act.sa_handler = SIG_IGN; @@ -516,6 +542,24 @@ main (int argc, char **argv) g_printerr ("Failed to register SIGTERM handler: %s\n", g_strerror (errno)); + sigchld_nexus = g_object_new (META_TYPE_NEXUS, NULL); + + sigchld_signal_id = + g_signal_new ("sigchld", META_TYPE_NEXUS, + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__UINT_POINTER, + G_TYPE_NONE, + 2, + G_TYPE_UINT, G_TYPE_POINTER); + + act.sa_flags = SA_NOCLDSTOP | SA_SIGINFO; + act.sa_handler = SIG_DFL; + act.sa_sigaction = &sigchld_handler; + if (sigaction (SIGCHLD, &act, NULL) < 0) + g_printerr ("Failed to register SIGCHLD handler: %s\n", + g_strerror (errno)); + if (g_getenv ("METACITY_VERBOSE")) meta_set_verbose (TRUE); if (g_getenv ("METACITY_DEBUG")) diff --git a/src/core/session.c b/src/core/session.c index f26195265..da2c59a9e 100644 --- a/src/core/session.c +++ b/src/core/session.c @@ -1737,13 +1737,6 @@ windows_cmp_by_title (MetaWindow *a, return g_utf8_collate (a->title, b->title); } -typedef struct -{ - int child_pid; - int child_pipe; - gboolean shutdown; -} LameClientsDialogData; - static void finish_interact (gboolean shutdown) { @@ -1755,63 +1748,26 @@ finish_interact (gboolean shutdown) } } -static gboolean -io_from_warning_dialog (GIOChannel *channel, - GIOCondition condition, - gpointer data) +static void +sigchld_handler (MetaNexus *nexus, guint arg1, gpointer arg2, gpointer user_data) { - LameClientsDialogData *d; + gboolean shutdown = GPOINTER_TO_INT (user_data); - d = data; - - meta_topic (META_DEBUG_PING, - "IO handler from lame clients dialog, condition = %x\n", - condition); - - if (condition & (G_IO_HUP | G_IO_NVAL | G_IO_ERR)) + if (arg1 == 0) /* pressed "OK" */ { - finish_interact (d->shutdown); - - /* Remove the callback, freeing data */ - return FALSE; + finish_interact (shutdown); } - else if (condition & G_IO_IN) - { - /* Check for EOF */ - - char buf[16]; - int ret; - - ret = read (d->child_pipe, buf, sizeof (buf)); - if (ret == 0) - { - finish_interact (d->shutdown); - return FALSE; - } - } - - /* Keep callback installed */ - return TRUE; } static void warn_about_lame_clients_and_finish_interact (gboolean shutdown) { - GSList *lame; + GSList *lame = NULL; GSList *windows; - char **argv; - int i; + GSList *lame_details = NULL; GSList *tmp; - int len; - int child_pid; - int child_pipe; - GError *err; - GIOChannel *channel; - LameClientsDialogData *d; - guint32 timestamp; - char timestampbuf[32]; + GSList *columns = NULL; - lame = NULL; windows = meta_display_list_windows (meta_get_display ()); tmp = windows; while (tmp != NULL) @@ -1838,77 +1794,43 @@ warn_about_lame_clients_and_finish_interact (gboolean shutdown) finish_interact (shutdown); return; } - + + columns = g_slist_prepend (columns, "Window"); + columns = g_slist_prepend (columns, "Class"); + lame = g_slist_sort (lame, (GCompareFunc) windows_cmp_by_title); - timestamp = meta_display_get_current_time_roundtrip (meta_get_display ()); - sprintf (timestampbuf, "%u", timestamp); - - len = g_slist_length (lame); - len *= 2; /* titles and also classes */ - len += 2; /* --timestamp flag and actual timestamp */ - len += 1; /* NULL term */ - len += 2; /* metacity-dialog command and option */ - - argv = g_new0 (char*, len); - - i = 0; - - argv[i] = METACITY_LIBEXECDIR"/metacity-dialog"; - ++i; - argv[i] = "--timestamp"; - ++i; - argv[i] = timestampbuf; - ++i; - argv[i] = "--warn-about-no-sm-support"; - ++i; - tmp = lame; while (tmp != NULL) { MetaWindow *w = tmp->data; - argv[i] = w->title; - ++i; - argv[i] = w->res_class ? w->res_class : ""; - ++i; + lame_details = g_slist_prepend (lame_details, + w->res_class ? w->res_class : ""); + lame_details = g_slist_prepend (lame_details, + w->title); tmp = tmp->next; } - - child_pipe = -1; - child_pid = -1; - err = NULL; - if (!g_spawn_async_with_pipes ("/", - argv, - NULL, - 0, - NULL, NULL, - &child_pid, - NULL, - &child_pipe, - NULL, - &err)) - { - meta_warning (_("Error launching metacity-dialog to warn about apps that don't support session management: %s\n"), - err->message); - g_error_free (err); - } - - g_free (argv); g_slist_free (lame); - d = g_new0 (LameClientsDialogData, 1); - d->child_pipe = child_pipe; - d->child_pid = child_pid; - d->shutdown = shutdown; - - channel = g_io_channel_unix_new (d->child_pipe); - g_io_add_watch_full (channel, G_PRIORITY_DEFAULT, - G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - io_from_warning_dialog, - d, g_free); - g_io_channel_unref (channel); + meta_show_dialog("--list", + _("These windows do not support "save current setup" " + "and will have to be restarted manually next time " + "you log in."), + "240", + meta_screen_get_screen_number (meta_get_display()->active_screen), + NULL, NULL, + None, + columns, + lame_details); + + g_slist_free (lame_details); + + g_signal_connect (sigchld_nexus, "sigchld", + G_CALLBACK (sigchld_handler), + GINT_TO_POINTER (shutdown)); + } #endif /* HAVE_SM */ diff --git a/src/core/util.c b/src/core/util.c index bcfcb7f4d..299cd79af 100644 --- a/src/core/util.c +++ b/src/core/util.c @@ -37,6 +37,8 @@ #include /* must explicitly be included for Solaris; #326746 */ #include /* Just for the definition of the various gravities */ +MetaNexus *sigchld_nexus; + #ifdef HAVE_BACKTRACE #include void @@ -538,35 +540,26 @@ meta_gravity_to_string (int gravity) } } -void +GPid meta_show_dialog (const char *type, const char *message, const char *timeout, const gint screen_number, - const char **columns, - const char **entries) + const char *ok_text, + const char *cancel_text, + const int transient_for, + GSList *columns, + GSList *entries) { GError *error = NULL; char *screen_number_text = g_strdup_printf("%d", screen_number); - - /* - We want: - -zenity --display X --screen S --title Metacity --error --text "There was an error running terminal:\n\nYour computer is on fire." - ** with no pipes - -zenity --display X --screen S --title Metacity --question --text "%s is not responding.\n\nYou may choose to wait a short while for it to continue or force the application to quit entirely." - -zenity --display X --screen S --title Metacity --list --timeout 240 --text "These windows do not support \"save current setup\" and will have to be restarted manually next time you log in." --column "Window" --column "Class" "Firefox" "foo" "Duke Nukem Forever" "bar" - */ - - const char **argvl; + GSList *tmp; int i=0; GPid child_pid; - - argvl = g_malloc(sizeof (char*) * - (9 + (timeout?2:0)) - ); + const char **argvl = g_malloc(sizeof (char*) * + (15 + + g_slist_length (columns)*2 + + g_slist_length (entries))); argvl[i++] = "zenity"; argvl[i++] = type; @@ -584,18 +577,54 @@ zenity --display X --screen S --title Metacity --list --timeout 240 --text "Thes argvl[i++] = timeout; } + if (ok_text) + { + argvl[i++] = "--ok-label"; + argvl[i++] = ok_text; + } + + if (cancel_text) + { + argvl[i++] = "--cancel-label"; + argvl[i++] = cancel_text; + } + + tmp = columns; + while (tmp) + { + argvl[i++] = "--column"; + argvl[i++] = tmp->data; + tmp = tmp->next; + } + + tmp = entries; + while (tmp) + { + argvl[i++] = tmp->data; + tmp = tmp->next; + } + argvl[i] = NULL; - g_spawn_async_with_pipes ( - "/", - (char**) argvl, /* ugh */ - NULL, - G_SPAWN_SEARCH_PATH, - NULL, NULL, - &child_pid, - NULL, NULL, NULL, - &error - ); + if (transient_for) + { + gchar *env = g_strdup_printf("%d", transient_for); + setenv ("WINDOWID", env, 1); + g_free (env); + } + + g_spawn_async ( + "/", + (gchar**) argvl, /* ugh */ + NULL, + G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, + NULL, NULL, + &child_pid, + &error + ); + + if (transient_for) + unsetenv ("WINDOWID"); g_free (argvl); g_free (screen_number_text); @@ -605,6 +634,33 @@ zenity --display X --screen S --title Metacity --list --timeout 240 --text "Thes meta_warning ("%s\n", error->message); g_error_free (error); } + + return child_pid; +} + +GType +meta_nexus_get_type (void) +{ + static GType nexus_type = 0; + + if (!nexus_type) + { + static const GTypeInfo nexus_info = + { + sizeof (MetaNexusClass), + NULL, NULL, NULL, NULL, NULL, + sizeof (MetaNexus), + 0, + NULL, NULL + }; + + nexus_type = g_type_register_static (G_TYPE_OBJECT, + "MetaNexus", + &nexus_info, + 0); + } + + return nexus_type; } /* eof util.c */ diff --git a/src/core/window-private.h b/src/core/window-private.h index fe688e9a1..3f3980c85 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -354,7 +354,6 @@ struct _MetaWindow /* Current dialog open for this window */ int dialog_pid; - int dialog_pipe; /* maintained by group.c */ MetaGroup *group; diff --git a/src/core/window.c b/src/core/window.c index 2d389d67c..b5a945f8b 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -601,7 +601,6 @@ meta_window_new_with_attrs (MetaDisplay *display, window->constructing = TRUE; window->dialog_pid = -1; - window->dialog_pipe = -1; window->xwindow = xwindow; diff --git a/src/include/util.h b/src/include/util.h index 5041f5ecc..24b5a6957 100644 --- a/src/include/util.h +++ b/src/include/util.h @@ -26,6 +26,7 @@ #define META_UTIL_H #include +#include gboolean meta_is_verbose (void); void meta_set_verbose (gboolean setting); @@ -97,12 +98,15 @@ char* meta_g_utf8_strndup (const gchar *src, gsize n); void meta_free_gslist_and_elements (GSList *list_to_deep_free); -void meta_show_dialog (const char *type, +GPid meta_show_dialog (const char *type, const char *title, const char *message, gint timeout, - const char **columns, - const char **entries); + const char *ok_text, + const char *cancel_text, + const int transient_for, + GSList *columns, + GSList *entries); /* To disable verbose mode, we make these functions into no-ops */ #ifdef WITH_VERBOSE_MODE @@ -127,6 +131,36 @@ void meta_show_dialog (const char *type, #endif /* !WITH_VERBOSE_MODE */ +#include + +#define META_TYPE_NEXUS (meta_nexus_get_type ()) +#define META_NEXUS(obj) (GTK_CHECK_CAST ((obj), META_TYPE_NEXUS, MetaNexus)) +#define META_NEXUS_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), META_TYPE_NEXUS, MetaNexusClass)) +#define META_IS_NEXUS(obj) (GTK_CHECK_TYPE ((obj), META_TYPE_NEXUS)) +#define META_IS_NEXUS_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), META_TYPE_NEXUS)) +#define META_NEXUS_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), META_TYPE_NEXUS, MetaNexusClass)) + +typedef struct _MetaNexus +{ + GObject parent_instance; +} MetaNexus; + +typedef struct _MetaNexusClass +{ + GObjectClass parent_class; +} MetaNexusClass; + +GType meta_nexus_get_type (void) G_GNUC_CONST; +MetaNexus *meta_nexus_new (); + +/** + * An object which exists purely to attach signals to; this is to receive + * signals when a child process exits. The signal is "sigchld" with no detail. + * + * \bug Eventually we should have a specialised type for objects like these. + */ +extern MetaNexus *sigchld_nexus; + #endif /* META_UTIL_H */ diff --git a/src/ui/metacity-dialog.c b/src/ui/metacity-dialog.c deleted file mode 100644 index d47a9b3ec..000000000 --- a/src/ui/metacity-dialog.c +++ /dev/null @@ -1,438 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* Metacity dialog process */ - -/* - * Copyright (C) 2001 Havoc Pennington - * Copyright (C) 2004 Elijah Newren - * - * 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 -#include -#include -#include -#include - -#include -#define _(x) dgettext (GETTEXT_PACKAGE, x) -#define N_(x) x - -#include -#include - -static Window -window_from_string (const char *str) -{ - char *end; - unsigned long l; - - end = NULL; - - l = strtoul (str, &end, 16); - - if (end == NULL || end == str) - { - g_printerr (_("Could not parse \"%s\" as an integer"), - str); - return None; - } - - if (*end != '\0') - { - g_printerr (_("Did not understand trailing characters \"%s\" in string \"%s\""), - end, str); - return None; - } - - return l; -} - -static void -on_realize (GtkWidget *dialog, - void *data) -{ - const char *parent_str = data; - Window xwindow; - - xwindow = window_from_string (parent_str); - - gdk_error_trap_push (); - XSetTransientForHint (gdk_display, GDK_WINDOW_XID (dialog->window), - xwindow); - XSync (gdk_display, False); - gdk_error_trap_pop (); -} - -static int -kill_window_question (const char *window_name, - const char *parent_str, - guint32 timestamp) -{ - GtkWidget *dialog; - char *str, *tmp; - - tmp = g_markup_escape_text (window_name, -1); - str = g_strdup_printf (_("\"%s\" is not responding."), tmp); - g_free (tmp); - dialog = gtk_message_dialog_new (NULL, 0, - GTK_MESSAGE_WARNING, - GTK_BUTTONS_NONE, - "%s\n\n%s", - str, - _("You may choose to wait a short while " - "for it to continue or force the application " - "to quit entirely.")); - g_free (str); - gtk_window_set_icon_name (GTK_WINDOW (dialog), "stock_dialog-warning"); - - gtk_label_set_use_markup (GTK_LABEL (GTK_MESSAGE_DIALOG (dialog)->label), TRUE); - gtk_label_set_line_wrap (GTK_LABEL (GTK_MESSAGE_DIALOG (dialog)->label), TRUE); - - gtk_dialog_add_buttons (GTK_DIALOG (dialog), - _("_Wait"), - GTK_RESPONSE_REJECT, - _("_Force Quit"), - GTK_RESPONSE_ACCEPT, - NULL); - - gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_REJECT); - - g_signal_connect (G_OBJECT (dialog), "realize", - G_CALLBACK (on_realize), (char*) parent_str); - - gtk_widget_realize (dialog); - gdk_x11_window_set_user_time (dialog->window, timestamp); - - /* return our PID, then window ID that should be killed */ - if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) - g_print ("%d\n%s\n", (int) getpid (), parent_str); - else - g_print ("%d\n0x0\n", (int) getpid ()); - - return 0; -} - -static char* -latin1_to_utf8 (const char *text) -{ - GString *str; - const char *p; - - str = g_string_new (""); - - p = text; - while (*p) - { - g_string_append_unichar (str, *p); - ++p; - } - - return g_string_free (str, FALSE); -} - -enum -{ - COLUMN_TITLE, - COLUMN_CLASS, - COLUMN_LAST -}; - -static GtkWidget* -create_lame_apps_list (char **lame_apps) -{ - GtkTreeSelection *selection; - GtkCellRenderer *cell; - GtkWidget *tree_view; - GtkTreeViewColumn *column; - GtkListStore *model; - GtkTreeIter iter; - int i; - - model = gtk_list_store_new (COLUMN_LAST, - G_TYPE_STRING, - G_TYPE_STRING); - - tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model)); - - g_object_unref (G_OBJECT (model)); - - selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view)); - - gtk_tree_selection_set_mode (GTK_TREE_SELECTION (selection), - GTK_SELECTION_NONE); - - i = 0; - while (lame_apps[i]) - { - char *s; - - gtk_list_store_append (model, &iter); - - /* window class is latin-1 */ - s = latin1_to_utf8 (lame_apps[i+1]); - - gtk_list_store_set (model, - &iter, - COLUMN_TITLE, lame_apps[i], - COLUMN_CLASS, s, - -1); - - g_free (s); - - i += 2; - } - - cell = gtk_cell_renderer_text_new (); - - g_object_set (G_OBJECT (cell), - "xpad", 2, - NULL); - - column = gtk_tree_view_column_new_with_attributes (_("Title"), - cell, - "text", COLUMN_TITLE, - NULL); - - gtk_tree_view_column_set_sort_column_id (column, COLUMN_TITLE); - - gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), - GTK_TREE_VIEW_COLUMN (column)); - - cell = gtk_cell_renderer_text_new (); - - column = gtk_tree_view_column_new_with_attributes (_("Class"), - cell, - "text", COLUMN_CLASS, - NULL); - - gtk_tree_view_column_set_sort_column_id (column, COLUMN_CLASS); - - gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), - GTK_TREE_VIEW_COLUMN (column)); - - return tree_view; -} - -static int -warn_about_no_sm_support (char **lame_apps, - guint32 timestamp) -{ - GtkWidget *dialog; - GtkWidget *list; - GtkWidget *sw; - GtkWidget *button; - - dialog = gtk_message_dialog_new (NULL, - 0, - GTK_MESSAGE_WARNING, - GTK_BUTTONS_NONE, - _("These windows do not support \"save current setup\" and will have to be restarted manually next time you log in.")); - gtk_window_set_icon_name (GTK_WINDOW (dialog), "stock_dialog-warning"); - - g_signal_connect (G_OBJECT (dialog), - "response", - G_CALLBACK (gtk_main_quit), - NULL); - - /* Wait 4 minutes then force quit, so we don't wait around all night */ - g_timeout_add (4 * 60 * 1000, (GSourceFunc) gtk_main_quit, NULL); - - button = gtk_dialog_add_button (GTK_DIALOG (dialog), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE); - gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CLOSE); - list = create_lame_apps_list (lame_apps); - - sw = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), - GTK_POLICY_AUTOMATIC, - GTK_POLICY_AUTOMATIC); - gtk_container_set_border_width (GTK_CONTAINER (sw), 3); - - gtk_container_add (GTK_CONTAINER (sw), list); - - /* sw as geometry widget */ - gtk_window_set_geometry_hints (GTK_WINDOW (dialog), - sw, NULL, 0); - - gtk_window_set_resizable (GTK_WINDOW(dialog), TRUE); - - /* applies to geometry widget; try to avoid scrollbars, - * but don't make the window huge - */ - gtk_window_set_default_size (GTK_WINDOW (dialog), - 400, 225); - - gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), - sw, - TRUE, TRUE, 0); - - gtk_window_stick (GTK_WINDOW (dialog)); - - gtk_widget_realize (dialog); - gdk_x11_window_set_user_time (dialog->window, timestamp); - - gtk_widget_grab_focus (button); - gtk_widget_show_all (dialog); - - gtk_main (); - - return 0; -} - -static int -error_about_command (const char *gconf_key, - const char *command, - const char *error, - guint32 timestamp) -{ - GtkWidget *dialog; - - /* FIXME offer to change the value of the command's gconf key */ - - if (*command != '\0') - dialog = gtk_message_dialog_new (NULL, 0, - GTK_MESSAGE_ERROR, - GTK_BUTTONS_CLOSE, - _("There was an error running \"%s\":\n" - "%s."), - command, error); - else - dialog = gtk_message_dialog_new (NULL, 0, - GTK_MESSAGE_ERROR, - GTK_BUTTONS_CLOSE, - "%s", error); - gtk_window_set_icon_name (GTK_WINDOW (dialog), "stock_dialog-error"); - - gtk_widget_realize (dialog); - gdk_x11_window_set_user_time (dialog->window, timestamp); - - gtk_dialog_run (GTK_DIALOG (dialog)); - - gtk_widget_destroy (dialog); - - return 0; -} - -static gchar *screen = NULL; -static gchar *timestamp_string = NULL; -static gboolean isset_kill_window_question = FALSE; -static gboolean isset_warn_about_no_sm_support = FALSE; -static gboolean isset_command_failed_error = FALSE; -static gchar **remaining_args; - -static const GOptionEntry options[] = { - { "screen", 0, 0, G_OPTION_ARG_STRING, &screen, NULL, NULL}, - { "timestamp", 0, 0, G_OPTION_ARG_STRING, ×tamp_string, NULL, NULL}, - { "kill-window-question", 'k', 0, G_OPTION_ARG_NONE, - &isset_kill_window_question, NULL, NULL}, - { "warn-about-no-sm-support", 'w', 0, G_OPTION_ARG_NONE, - &isset_warn_about_no_sm_support, NULL, NULL}, - { "command-failed-error", 'c', 0, G_OPTION_ARG_NONE, - &isset_command_failed_error, NULL, NULL}, - { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, - &remaining_args, NULL, NULL}, - { NULL} -}; - -int -main (int argc, char **argv) -{ - GOptionContext *ctx; - guint32 timestamp = 0; - gint num_args = 0; - - bindtextdomain (GETTEXT_PACKAGE, METACITY_LOCALEDIR); - bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); - textdomain (GETTEXT_PACKAGE); - - gtk_init (&argc, &argv); - - ctx = g_option_context_new ("- Dialogs for metacity. " - "This program is intented for use by metacity only."); - g_option_context_add_main_entries (ctx, options, GETTEXT_PACKAGE); - g_option_context_parse (ctx, &argc, &argv, NULL); - g_option_context_free (ctx); - - if (timestamp_string != NULL) - { - timestamp = strtoul (timestamp_string, NULL, 10); - } - - if (remaining_args != NULL) - { - num_args = g_strv_length (remaining_args); - } - - if ((isset_kill_window_question && isset_warn_about_no_sm_support) || - (isset_kill_window_question && isset_command_failed_error) || - (isset_warn_about_no_sm_support && isset_command_failed_error) || - timestamp == 0) - { - g_printerr ("bad args to metacity-dialog\n"); - return 1; - } - - else if (isset_kill_window_question) - { - if (num_args < 2) - { - g_printerr ("bad args to metacity-dialog\n"); - return 1; - } - else - { - return kill_window_question (remaining_args[0], - remaining_args[1], timestamp); - } - } - - else if (isset_warn_about_no_sm_support) - { - /* argc must be even because we want title-class pairs */ - if (num_args == 0 || (num_args % 2) != 0) - { - g_printerr ("bad args to metacity-dialog\n"); - return 1; - } - else - { - return warn_about_no_sm_support (&remaining_args[0], timestamp); - } - } - - else if (isset_command_failed_error) - { - /* the args are the gconf key of the failed command, the text of - * the command, and the error message - */ - if (num_args != 3) - { - g_printerr ("bad args to metacity-dialog\n"); - return 1; - } - else - { - return error_about_command (remaining_args[0], - remaining_args[1], remaining_args[2], timestamp); - } - } - else - { - g_printerr ("bad args to metacity-dialog\n"); - return 1; - } -}