1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 
3 /* Metacity window deletion */
4 
5 /*
6  * Copyright (C) 2001, 2002 Havoc Pennington
7  * Copyright (C) 2004 Elijah Newren
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation; either version 2 of the
12  * License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #define _GNU_SOURCE
24 #define _XOPEN_SOURCE /* for gethostname() and kill() */
25 
26 #include <config.h>
27 #include "util.h"
28 #include "window-private.h"
29 #include "errors.h"
30 #include "workspace.h"
31 
32 #include <sys/types.h>
33 #include <sys/wait.h>
34 #include <signal.h>
35 #include <unistd.h>
36 #include <errno.h>
37 #include <string.h>
38 #include <stdio.h>
39 
40 static void meta_window_present_delete_dialog (MetaWindow *window,
41                                                guint32     timestamp);
42 
43 static void
delete_ping_reply_func(MetaDisplay * display,Window xwindow,guint32 timestamp,void * user_data)44 delete_ping_reply_func (MetaDisplay *display,
45                         Window       xwindow,
46                         guint32      timestamp,
47                         void        *user_data)
48 {
49   meta_topic (META_DEBUG_PING,
50               "Got reply to delete ping for %s\n",
51               ((MetaWindow*)user_data)->desc);
52 
53   /* we do nothing */
54 }
55 
56 static void
dialog_exited(GPid pid,int status,gpointer user_data)57 dialog_exited (GPid     pid,
58                int      status,
59                gpointer user_data)
60 {
61   MetaWindow *ours = (MetaWindow*) user_data;
62 
63   ours->dialog_pid = -1;
64 
65   /* exit status of 1 means the user pressed "Force Quit" */
66   if (WIFEXITED (status) && WEXITSTATUS (status) == 1)
67     meta_window_kill (ours);
68 }
69 
70 static void
delete_ping_timeout_func(MetaDisplay * display,Window xwindow,guint32 timestamp,void * user_data)71 delete_ping_timeout_func (MetaDisplay *display,
72                           Window       xwindow,
73                           guint32      timestamp,
74                           void        *user_data)
75 {
76   MetaWindow *window = user_data;
77   char *window_title;
78   gchar *window_content, *tmp;
79   GPid dialog_pid;
80 
81   meta_topic (META_DEBUG_PING,
82               "Got delete ping timeout for %s\n",
83               window->desc);
84 
85   if (window->dialog_pid >= 0)
86     {
87       meta_window_present_delete_dialog (window, timestamp);
88       return;
89     }
90 
91   window_title = g_locale_from_utf8 (window->title, -1, NULL, NULL, NULL);
92 
93   /* Translators: %s is a window title */
94   tmp = g_strdup_printf (_("<tt>%s</tt> is not responding."),
95                          window_title);
96   window_content = g_strdup_printf (
97       "<big><b>%s</b></big>\n\n<i>%s</i>",
98       tmp,
99       _("You may choose to wait a short while for it to "
100         "continue or force the application to quit entirely."));
101 
102   g_free (window_title);
103 
104   dialog_pid =
105     meta_show_dialog ("--question",
106                       window_content, NULL,
107                       window->screen->screen_name,
108                       _("_Wait"), _("_Force Quit"), window->xwindow,
109                       NULL, NULL);
110 
111   g_free (window_content);
112   g_free (tmp);
113 
114   window->dialog_pid = dialog_pid;
115   g_child_watch_add (dialog_pid, dialog_exited, window);
116 }
117 
118 void
meta_window_delete(MetaWindow * window,guint32 timestamp)119 meta_window_delete (MetaWindow  *window,
120                     guint32      timestamp)
121 {
122   meta_error_trap_push (window->display);
123   if (window->delete_window)
124     {
125       meta_topic (META_DEBUG_WINDOW_OPS,
126                   "Deleting %s with delete_window request\n",
127                   window->desc);
128       meta_window_send_icccm_message (window,
129                                       window->display->atom_WM_DELETE_WINDOW,
130                                       timestamp);
131     }
132   else
133     {
134       meta_topic (META_DEBUG_WINDOW_OPS,
135                   "Deleting %s with explicit kill\n",
136                   window->desc);
137       XKillClient (window->display->xdisplay, window->xwindow);
138     }
139   meta_error_trap_pop (window->display);
140 
141   meta_display_ping_window (window->display,
142                             window,
143                             timestamp,
144                             delete_ping_reply_func,
145                             delete_ping_timeout_func,
146                             window);
147 }
148 
149 void
meta_window_kill(MetaWindow * window)150 meta_window_kill (MetaWindow *window)
151 {
152   pid_t client_pid;
153   char buf[257];
154 
155   meta_topic (META_DEBUG_WINDOW_OPS,
156               "Killing %s brutally\n",
157               window->desc);
158 
159   client_pid = meta_window_get_client_pid (window);
160 
161   if (window->wm_client_machine != NULL &&
162       client_pid > 0)
163     {
164       if (gethostname (buf, sizeof(buf)-1) == 0)
165         {
166           if (strcmp (buf, window->wm_client_machine) == 0)
167             {
168               meta_topic (META_DEBUG_WINDOW_OPS,
169                           "Killing %s with kill()\n",
170                           window->desc);
171 
172               if (kill (client_pid, 9) < 0)
173                 meta_topic (META_DEBUG_WINDOW_OPS,
174                             "Failed to signal %s: %s\n",
175                             window->desc, strerror (errno));
176             }
177         }
178       else
179         {
180           g_warning ("Failed to get hostname: %s", strerror (errno));
181         }
182     }
183 
184   meta_topic (META_DEBUG_WINDOW_OPS,
185               "Disconnecting %s with XKillClient()\n",
186               window->desc);
187   meta_error_trap_push (window->display);
188   XKillClient (window->display->xdisplay, window->xwindow);
189   meta_error_trap_pop (window->display);
190 }
191 
192 void
meta_window_free_delete_dialog(MetaWindow * window)193 meta_window_free_delete_dialog (MetaWindow *window)
194 {
195   if (window->dialog_pid >= 0)
196     {
197       kill (window->dialog_pid, 9);
198       window->dialog_pid = -1;
199     }
200 }
201 
202 static void
meta_window_present_delete_dialog(MetaWindow * window,guint32 timestamp)203 meta_window_present_delete_dialog (MetaWindow *window, guint32 timestamp)
204 {
205   meta_topic (META_DEBUG_PING,
206               "Presenting existing ping dialog for %s\n",
207               window->desc);
208 
209   if (window->dialog_pid >= 0)
210     {
211       GSList *windows;
212       GSList *tmp;
213 
214       /* Activate transient for window that belongs to
215        * metacity-dialog
216        */
217 
218       windows = meta_display_list_windows (window->display, META_LIST_DEFAULT);
219       tmp = windows;
220       while (tmp != NULL)
221         {
222           MetaWindow *w = tmp->data;
223 
224           if (w->xtransient_for == window->xwindow &&
225               w->res_class &&
226               g_ascii_strcasecmp (w->res_class, "metacity-dialog") == 0)
227             {
228               meta_window_activate (w, timestamp);
229               break;
230             }
231 
232           tmp = tmp->next;
233         }
234 
235       g_slist_free (windows);
236     }
237 }
238