/* * 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., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. * * Authors: * Mark McLoughlin */ #include #include "panel-force-quit.h" #include #include #include #include #include #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 .")); 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 (); #if GTK_CHECK_VERSION (3, 0, 0) gdk_error_trap_pop_ignored (); #else gdk_error_trap_pop (); #endif } 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 GTK_CHECK_VERSION (3, 0, 0) if (!gdk_x11_window_lookup_for_display (gdk_x11_lookup_xdisplay (event->display), window)) #else if (!gdk_xid_table_lookup_for_display (gdk_x11_lookup_xdisplay (event->display), window)) #endif 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 (); }