diff options
Diffstat (limited to 'mate-panel/panel-force-quit.c')
-rw-r--r-- | mate-panel/panel-force-quit.c | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/mate-panel/panel-force-quit.c b/mate-panel/panel-force-quit.c new file mode 100644 index 00000000..fabad1ac --- /dev/null +++ b/mate-panel/panel-force-quit.c @@ -0,0 +1,316 @@ +/* + * panel-force-quit.c: + * + * Copyright (C) 2003 Sun Microsystems, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: + * Mark McLoughlin <[email protected]> + */ + +#include <config.h> + +#include "panel-force-quit.h" + +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <gdk/gdkx.h> +#include <X11/Xlib.h> +#include <X11/keysym.h> + +#include "panel-icon-names.h" +#include "panel-stock-icons.h" + +static GdkFilterReturn popup_filter (GdkXEvent *gdk_xevent, + GdkEvent *event, + GtkWidget *popup); + +static Atom wm_state_atom = None; + +static GtkWidget * +display_popup_window (GdkScreen *screen) +{ + GtkWidget *retval; + GtkWidget *vbox; + GtkWidget *image; + GtkWidget *frame; + GtkWidget *label; + int screen_width, screen_height; + GtkAllocation allocation; + + retval = gtk_window_new (GTK_WINDOW_POPUP); + atk_object_set_role (gtk_widget_get_accessible (retval), ATK_ROLE_ALERT); + gtk_window_set_screen (GTK_WINDOW (retval), screen); + gtk_window_stick (GTK_WINDOW (retval)); + gtk_widget_add_events (retval, GDK_BUTTON_PRESS_MASK | GDK_KEY_PRESS_MASK); + + frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); + gtk_container_add (GTK_CONTAINER (retval), frame); + gtk_widget_show (frame); + + vbox = gtk_vbox_new (FALSE, 0); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 8); + gtk_container_add (GTK_CONTAINER (frame), vbox); + gtk_widget_show (vbox); + + image = gtk_image_new_from_icon_name (PANEL_ICON_FORCE_QUIT, + GTK_ICON_SIZE_DIALOG); + gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.5); + gtk_box_pack_start (GTK_BOX (vbox), image, TRUE, TRUE, 4); + gtk_widget_show (image); + + label = gtk_label_new (_("Click on a window to force the application to quit. " + "To cancel press <ESC>.")); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER); + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 4); + gtk_widget_show (label); + + gtk_widget_realize (retval); + + screen_width = gdk_screen_get_width (screen); + screen_height = gdk_screen_get_height (screen); + + gtk_widget_get_allocation (retval, &allocation); + + gtk_window_move (GTK_WINDOW (retval), + (screen_width - allocation.width) / 2, + (screen_height - allocation.height) / 2); + + gtk_widget_show (GTK_WIDGET (retval)); + + return retval; +} + +static void +remove_popup (GtkWidget *popup) +{ + GdkWindow *root; + + root = gdk_screen_get_root_window ( + gtk_window_get_screen (GTK_WINDOW (popup))); + gdk_window_remove_filter (root, (GdkFilterFunc) popup_filter, popup); + + gtk_widget_destroy (popup); + gdk_pointer_ungrab (GDK_CURRENT_TIME); + gdk_keyboard_ungrab (GDK_CURRENT_TIME); +} + +static gboolean +wm_state_set (Display *display, + Window window) +{ + gulong nitems; + gulong bytes_after; + gulong *prop; + Atom ret_type = None; + int ret_format; + int result; + + gdk_error_trap_push (); + result = XGetWindowProperty (display, window, wm_state_atom, + 0, G_MAXLONG, False, wm_state_atom, + &ret_type, &ret_format, &nitems, + &bytes_after, (gpointer) &prop); + + if (gdk_error_trap_pop ()) + return FALSE; + + if (result != Success) + return FALSE; + + XFree (prop); + + if (ret_type != wm_state_atom) + return FALSE; + + return TRUE; +} + +static Window +find_managed_window (Display *display, + Window window) +{ + Window root; + Window parent; + Window *kids = NULL; + Window retval; + guint nkids; + int i, result; + + if (wm_state_set (display, window)) + return window; + + gdk_error_trap_push (); + result = XQueryTree (display, window, &root, &parent, &kids, &nkids); + if (gdk_error_trap_pop () || !result) + return None; + + retval = None; + + for (i = 0; i < nkids; i++) { + if (wm_state_set (display, kids [i])) { + retval = kids [i]; + break; + } + + retval = find_managed_window (display, kids [i]); + if (retval != None) + break; + } + + if (kids) + XFree (kids); + + return retval; +} + +static void +kill_window_response (GtkDialog *dialog, + gint response_id, + gpointer user_data) +{ + if (response_id == GTK_RESPONSE_ACCEPT) { + Display *display; + Window window = (Window) user_data; + + display = GDK_DISPLAY_XDISPLAY (gtk_widget_get_display (GTK_WIDGET (dialog))); + + gdk_error_trap_push (); + XKillClient (display, window); + gdk_flush (); + gdk_error_trap_pop (); + } + + gtk_widget_destroy (GTK_WIDGET (dialog)); +} + +/* From marco */ +static void +kill_window_question (gpointer window) +{ + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (NULL, 0, + GTK_MESSAGE_WARNING, + GTK_BUTTONS_NONE, + _("Force this application to exit?")); + + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + _("If you choose to force an application " + "to exit, unsaved changes in any open documents " + "in it might get lost.")); + + gtk_dialog_add_buttons (GTK_DIALOG (dialog), + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, + PANEL_STOCK_FORCE_QUIT, + GTK_RESPONSE_ACCEPT, + NULL); + + gtk_dialog_set_default_response (GTK_DIALOG (dialog), + GTK_RESPONSE_CANCEL); + gtk_window_set_skip_taskbar_hint (GTK_WINDOW (dialog), FALSE); + gtk_window_set_title (GTK_WINDOW (dialog), _("Force Quit")); + + g_signal_connect (dialog, "response", + G_CALLBACK (kill_window_response), window); + + gtk_widget_show (dialog); +} + +static void +handle_button_press_event (GtkWidget *popup, + XKeyEvent *event) +{ + Window window; + + remove_popup (popup); + + if (event->subwindow == None) + return; + + if (wm_state_atom == None) + wm_state_atom = XInternAtom (event->display, "WM_STATE", FALSE); + + window = find_managed_window (event->display, event->subwindow); + + if (window != None) { + if (!gdk_xid_table_lookup_for_display (gdk_x11_lookup_xdisplay (event->display), window)) + kill_window_question ((gpointer) window); + } +} + +static GdkFilterReturn +popup_filter (GdkXEvent *gdk_xevent, + GdkEvent *event, + GtkWidget *popup) +{ + XEvent *xevent = (XEvent *) gdk_xevent; + + switch (xevent->type) { + case ButtonPress: + handle_button_press_event (popup, &xevent->xkey); + return GDK_FILTER_REMOVE; + case KeyPress: + if (xevent->xkey.keycode == XKeysymToKeycode (xevent->xany.display, XK_Escape)) { + remove_popup (popup); + return GDK_FILTER_REMOVE; + } + break; + default: + break; + } + + return GDK_FILTER_CONTINUE; +} + +void +panel_force_quit (GdkScreen *screen, + guint time) +{ + GdkGrabStatus status; + GdkCursor *cross; + GtkWidget *popup; + GdkWindow *root; + + popup = display_popup_window (screen); + + root = gdk_screen_get_root_window (screen); + + gdk_window_add_filter (root, (GdkFilterFunc) popup_filter, popup); + + cross = gdk_cursor_new (GDK_CROSS); + status = gdk_pointer_grab (root, FALSE, GDK_BUTTON_PRESS_MASK, + NULL, cross, time); + gdk_cursor_unref (cross); + if (status != GDK_GRAB_SUCCESS) { + g_warning ("Pointer grab failed\n"); + remove_popup (popup); + return; + } + + status = gdk_keyboard_grab (root, FALSE, time); + if (status != GDK_GRAB_SUCCESS) { + g_warning ("Keyboard grab failed\n"); + remove_popup (popup); + return; + } + + gdk_flush (); +} |