diff options
Diffstat (limited to '')
-rw-r--r-- | gnome-session/gsm-fail-whale-dialog.c | 461 |
1 files changed, 461 insertions, 0 deletions
diff --git a/gnome-session/gsm-fail-whale-dialog.c b/gnome-session/gsm-fail-whale-dialog.c new file mode 100644 index 0000000..d6e8407 --- /dev/null +++ b/gnome-session/gsm-fail-whale-dialog.c @@ -0,0 +1,461 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2011 Red Hat, Inc. + * Copyright (C) 2019 Canonical Ltd. + * + * 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, see <http://www.gnu.org/licenses/>. + * + * Authors: + * Colin Walters <walters@verbum.org> + * Marco Trevisan <marco@ubuntu.com> + */ + +#include <config.h> + +#include <stdlib.h> + +#include <glib/gi18n.h> + +#include <gtk/gtk.h> +#ifdef GDK_WINDOWING_X11 +#include <gtk/gtkx.h> +#endif + +#include "gsm-fail-whale-dialog.h" + +#include "gsm-icon-names.h" + +struct _GsmFailWhaleDialog +{ + GtkWindow parent; + + gboolean debug_mode; + gboolean allow_logout; + gboolean extensions; + GdkMonitor *monitor; + GdkRectangle geometry; +}; + +G_DEFINE_TYPE (GsmFailWhaleDialog, gsm_fail_whale_dialog, GTK_TYPE_WINDOW); + +/* derived from tomboy */ +static void +_window_override_user_time (GsmFailWhaleDialog *window) +{ + guint32 ev_time = gtk_get_current_event_time (); + GdkWindow *gdk_window = gtk_widget_get_window (GTK_WIDGET (window)); + +#ifdef GDK_WINDOWING_X11 + if (!GDK_IS_X11_WINDOW (gdk_window)) + return; + + if (ev_time == 0) { + gint ev_mask = gtk_widget_get_events (GTK_WIDGET (window)); + if (!(ev_mask & GDK_PROPERTY_CHANGE_MASK)) { + gtk_widget_add_events (GTK_WIDGET (window), + GDK_PROPERTY_CHANGE_MASK); + } + + /* + * NOTE: Last resort for D-BUS or other non-interactive + * openings. Causes roundtrip to server. Lame. + */ + ev_time = gdk_x11_get_server_time (gdk_window); + } + + gdk_x11_window_set_user_time (gdk_window, ev_time); +#endif +} + +static void +_window_move_resize_window (GsmFailWhaleDialog *window, + gboolean move, + gboolean resize) +{ + if (window->debug_mode) + return; + + g_debug ("Move and/or resize window x=%d y=%d w=%d h=%d", + window->geometry.x, + window->geometry.y, + window->geometry.width, + window->geometry.height); + + if (resize) { + gtk_window_resize (GTK_WINDOW (window), + window->geometry.width, + window->geometry.height); + } + + if (move) { + gtk_window_move (GTK_WINDOW (window), + window->geometry.x, + window->geometry.y); + } +} + +static void +update_geometry (GsmFailWhaleDialog *fail_dialog) +{ + gdk_monitor_get_geometry (fail_dialog->monitor, &fail_dialog->geometry); +} + +static void +on_screen_size_changed (GdkScreen *screen, + GsmFailWhaleDialog *fail_dialog) +{ + gtk_widget_queue_resize (GTK_WIDGET (fail_dialog)); +} + +static void +gsm_fail_whale_dialog_realize (GtkWidget *widget) +{ + if (GTK_WIDGET_CLASS (gsm_fail_whale_dialog_parent_class)->realize) { + GTK_WIDGET_CLASS (gsm_fail_whale_dialog_parent_class)->realize (widget); + } + + _window_override_user_time (GSM_FAIL_WHALE_DIALOG (widget)); + update_geometry (GSM_FAIL_WHALE_DIALOG (widget)); + _window_move_resize_window (GSM_FAIL_WHALE_DIALOG (widget), TRUE, TRUE); + + g_signal_connect (gtk_window_get_screen (GTK_WINDOW (widget)), + "size_changed", + G_CALLBACK (on_screen_size_changed), + widget); +} + +static void +gsm_fail_whale_dialog_unrealize (GtkWidget *widget) +{ + g_signal_handlers_disconnect_by_func (gtk_window_get_screen (GTK_WINDOW (widget)), + on_screen_size_changed, + widget); + + if (GTK_WIDGET_CLASS (gsm_fail_whale_dialog_parent_class)->unrealize) { + GTK_WIDGET_CLASS (gsm_fail_whale_dialog_parent_class)->unrealize (widget); + } +} + +static void +gsm_fail_whale_dialog_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + GsmFailWhaleDialog *fail_dialog; + GdkRectangle old_geometry; + int position_changed = FALSE; + int size_changed = FALSE; + + fail_dialog = GSM_FAIL_WHALE_DIALOG (widget); + + old_geometry = fail_dialog->geometry; + + update_geometry (fail_dialog); + + requisition->width = fail_dialog->geometry.width; + requisition->height = fail_dialog->geometry.height; + + if (!gtk_widget_get_realized (widget)) { + return; + } + + if (old_geometry.width != fail_dialog->geometry.width || + old_geometry.height != fail_dialog->geometry.height) { + size_changed = TRUE; + } + + if (old_geometry.x != fail_dialog->geometry.x || + old_geometry.y != fail_dialog->geometry.y) { + position_changed = TRUE; + } + + _window_move_resize_window (fail_dialog, + position_changed, size_changed); +} + +static void +gsm_fail_whale_dialog_get_preferred_width (GtkWidget *widget, + gint *minimal_width, + gint *natural_width) +{ + GtkRequisition requisition; + + gsm_fail_whale_dialog_size_request (widget, &requisition); + + *minimal_width = *natural_width = requisition.width; +} + +static void +gsm_fail_whale_dialog_get_preferred_width_for_height (GtkWidget *widget, + gint for_height, + gint *minimal_width, + gint *natural_width) +{ + GtkRequisition requisition; + + gsm_fail_whale_dialog_size_request (widget, &requisition); + + *minimal_width = *natural_width = requisition.width; +} + +static void +gsm_fail_whale_dialog_get_preferred_height (GtkWidget *widget, + gint *minimal_height, + gint *natural_height) +{ + GtkRequisition requisition; + + gsm_fail_whale_dialog_size_request (widget, &requisition); + + *minimal_height = *natural_height = requisition.height; +} + +static void +gsm_fail_whale_dialog_get_preferred_height_for_width (GtkWidget *widget, + gint for_width, + gint *minimal_height, + gint *natural_height) +{ + GtkRequisition requisition; + + gsm_fail_whale_dialog_size_request (widget, &requisition); + + *minimal_height = *natural_height = requisition.height; +} + +static void +gsm_fail_whale_dialog_class_init (GsmFailWhaleDialogClass *klass) +{ + GtkWidgetClass *widget_class; + + widget_class = GTK_WIDGET_CLASS (klass); + + widget_class->realize = gsm_fail_whale_dialog_realize; + widget_class->unrealize = gsm_fail_whale_dialog_unrealize; + widget_class->get_preferred_width = gsm_fail_whale_dialog_get_preferred_width; + widget_class->get_preferred_height = gsm_fail_whale_dialog_get_preferred_height; + widget_class->get_preferred_width_for_height = gsm_fail_whale_dialog_get_preferred_width_for_height; + widget_class->get_preferred_height_for_width = gsm_fail_whale_dialog_get_preferred_height_for_width; +} + +static void +on_logout_clicked (GtkWidget *button, + GsmFailWhaleDialog *fail_dialog) +{ + if (!fail_dialog->debug_mode) { + g_spawn_command_line_async ("gnome-session-quit --force", NULL); + } + gtk_main_quit (); +} + +static void +setup_window (GsmFailWhaleDialog *fail_dialog) +{ + GtkWidget *box; + GtkWidget *image; + GtkWidget *label; + GtkWidget *message_label; + GtkWidget *button_box; + GtkWidget *button; + GdkPixbuf *fail_icon; + GdkDisplay *display; + char *markup; + int scale_factor; + int i; + + gtk_window_set_title (GTK_WINDOW (fail_dialog), ""); + gtk_window_set_icon_name (GTK_WINDOW (fail_dialog), GSM_ICON_COMPUTER_FAIL); + + gtk_window_set_skip_taskbar_hint (GTK_WINDOW (fail_dialog), TRUE); + gtk_window_set_keep_above (GTK_WINDOW (fail_dialog), TRUE); + gtk_window_stick (GTK_WINDOW (fail_dialog)); + gtk_window_set_position (GTK_WINDOW (fail_dialog), GTK_WIN_POS_CENTER_ALWAYS); + + /* only works if there is a window manager which is unlikely */ + display = gtk_widget_get_display (GTK_WIDGET (fail_dialog)); + for (i = 0; i < gdk_display_get_n_monitors (display); i++) { + if (gdk_display_get_monitor (display, i) == fail_dialog->monitor) { + GdkScreen *screen; + + screen = gtk_widget_get_screen (GTK_WIDGET (fail_dialog)); + gtk_window_fullscreen_on_monitor (GTK_WINDOW (fail_dialog), + screen, i); + break; + } + } + + box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10); + gtk_widget_set_valign (box, GTK_ALIGN_CENTER); + gtk_widget_show (box); + + gtk_container_add (GTK_CONTAINER (fail_dialog), box); + + scale_factor = gdk_monitor_get_scale_factor (fail_dialog->monitor); + fail_icon = gtk_icon_theme_load_icon_for_scale (gtk_icon_theme_get_default (), + GSM_ICON_COMPUTER_FAIL, + 128, + scale_factor, + 0, + NULL); + if (fail_icon != NULL) { + image = gtk_image_new_from_pixbuf (fail_icon); + gtk_widget_show (image); + gtk_box_pack_start (GTK_BOX (box), image, FALSE, FALSE, 0); + g_object_unref (fail_icon); + } + + label = gtk_label_new (NULL); + markup = g_strdup_printf ("<b><big>%s</big></b>", _("Oh no! Something has gone wrong.")); + gtk_label_set_markup (GTK_LABEL (label), markup); + g_free (markup); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0); + + if (!fail_dialog->allow_logout) + message_label = gtk_label_new (_("A problem has occurred and the system can’t recover. Please contact a system administrator")); + else if (fail_dialog->extensions) + message_label = gtk_label_new (_("A problem has occurred and the system can’t recover. All extensions have been disabled as a precaution.")); + else + message_label = gtk_label_new (_("A problem has occurred and the system can’t recover.\nPlease log out and try again.")); + + gtk_label_set_justify (GTK_LABEL (message_label), GTK_JUSTIFY_CENTER); + gtk_label_set_line_wrap (GTK_LABEL (message_label), TRUE); + gtk_widget_show (message_label); + gtk_box_pack_start (GTK_BOX (box), + message_label, FALSE, FALSE, 0); + + button_box = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL); + gtk_container_set_border_width (GTK_CONTAINER (button_box), 20); + gtk_widget_show (button_box); + gtk_box_pack_end (GTK_BOX (box), + button_box, FALSE, FALSE, 0); + + if (fail_dialog->allow_logout) { + button = gtk_button_new_with_mnemonic (_("_Log Out")); + gtk_widget_show (button); + gtk_box_pack_end (GTK_BOX (button_box), + button, FALSE, FALSE, 0); + g_signal_connect (button, "clicked", + G_CALLBACK (on_logout_clicked), fail_dialog); + } +} + +static void +gsm_fail_whale_dialog_init (GsmFailWhaleDialog *fail_dialog) +{ +} + +static gboolean debug_mode = FALSE; +static gboolean allow_logout = FALSE; +static gboolean extensions = FALSE; +static GList *dialogs = NULL; + +static void +create_fail_dialog (GdkMonitor *monitor) +{ + GsmFailWhaleDialog *fail_dialog; + + fail_dialog = g_object_new (GSM_TYPE_FAIL_WHALE_DIALOG, NULL); + fail_dialog->debug_mode = debug_mode; + fail_dialog->allow_logout = allow_logout; + fail_dialog->extensions = extensions; + fail_dialog->monitor = monitor; + + setup_window (fail_dialog); + + g_signal_connect (fail_dialog, "destroy", + G_CALLBACK (gtk_main_quit), NULL); + + gtk_widget_show (GTK_WIDGET (fail_dialog)); + + dialogs = g_list_prepend (dialogs, fail_dialog); +} + +static void +on_monitor_added (GdkDisplay *display, + GdkMonitor *monitor) +{ + create_fail_dialog (monitor); +} + +static void +on_monitor_removed (GdkDisplay *display, + GdkMonitor *monitor) +{ + GList *l; + + for (l = dialogs; l;) { + GList *next = l->next; + GsmFailWhaleDialog *fail_dialog = l->data; + + if (fail_dialog->monitor == monitor) { + dialogs = g_list_delete_link (dialogs, l); + gtk_widget_destroy (GTK_WIDGET (fail_dialog)); + } + l = next; + } +} + +int main (int argc, char *argv[]) +{ + GOptionEntry entries[] = { + { "debug", 0, 0, G_OPTION_ARG_NONE, &debug_mode, N_("Enable debugging code"), NULL }, + { "allow-logout", 0, 0, G_OPTION_ARG_NONE, &allow_logout, N_("Allow logout"), NULL }, + { "extensions", 0, 0, G_OPTION_ARG_NONE, &extensions, N_("Show extension warning"), NULL }, + { NULL, 0, 0, 0, NULL, NULL, NULL } + }; + + GError *error = NULL; + GdkDisplay *display; + int i; + + bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + if (!gtk_init_with_args (&argc, &argv, " - fail whale", + entries, GETTEXT_PACKAGE, + &error)) { + if (error != NULL) { + g_warning ("%s", error->message); + exit (1); + } + + /* display server probably went away. Could be for legitimate reasons, could be for + * unexpected reasons. If it went away unexpectantly, that's logged elsewhere, so + * let's not add noise by logging here. + */ + return 0; + } + + /* Force-off allow_logout when running inside GDM, this is needed + * because the systemd service always passes --allow-logout + */ + if (g_strcmp0 (g_getenv ("RUNNING_UNDER_GDM"), "true") == 0) + allow_logout = FALSE; + + display = gdk_display_get_default (); + for (i = 0; i < gdk_display_get_n_monitors (display); i++) { + create_fail_dialog (gdk_display_get_monitor (display, i)); + } + + g_signal_connect (display, "monitor-added", + G_CALLBACK (on_monitor_added), &dialogs); + g_signal_connect (display, "monitor-removed", + G_CALLBACK (on_monitor_removed), &dialogs); + + gtk_main (); + + return 0; +} + |