delete.c

Go to the documentation of this file.
00001 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
00002 
00003 /* Metacity window deletion */
00004 
00005 /* 
00006  * Copyright (C) 2001, 2002 Havoc Pennington
00007  * Copyright (C) 2004 Elijah Newren
00008  * 
00009  * This program is free software; you can redistribute it and/or
00010  * modify it under the terms of the GNU General Public License as
00011  * published by the Free Software Foundation; either version 2 of the
00012  * License, or (at your option) any later version.
00013  *
00014  * This program is distributed in the hope that it will be useful, but
00015  * WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017  * General Public License for more details.
00018  * 
00019  * You should have received a copy of the GNU General Public License
00020  * along with this program; if not, write to the Free Software
00021  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
00022  * 02111-1307, USA.
00023  */
00024 
00025 #define _GNU_SOURCE
00026 #define _SVID_SOURCE /* for gethostname() */
00027 
00028 #include <config.h>
00029 #include "util.h"
00030 #include "window-private.h"
00031 #include "errors.h"
00032 #include "workspace.h"
00033 
00034 #include <sys/types.h>
00035 #include <signal.h>
00036 #include <unistd.h>
00037 #include <errno.h>
00038 #include <string.h>
00039 #include <stdlib.h>
00040 #include <stdio.h>
00041 
00042 static void meta_window_present_delete_dialog (MetaWindow *window,
00043                                                guint32     timestamp);
00044 
00045 static void
00046 delete_ping_reply_func (MetaDisplay *display,
00047                         Window       xwindow,
00048                         guint32      timestamp,
00049                         void        *user_data)
00050 {
00051   meta_topic (META_DEBUG_PING,
00052               "Got reply to delete ping for %s\n",
00053               ((MetaWindow*)user_data)->desc);
00054 
00055   /* we do nothing */
00056 }
00057 
00058 static Window
00059 window_from_string (const char *str)
00060 {
00061   char *end;
00062   unsigned long l;
00063 
00064   end = NULL;
00065   
00066   l = strtoul (str, &end, 16);
00067 
00068   if (end == NULL || end == str)
00069     {
00070       meta_warning (_("Could not parse \"%s\" as an integer"),
00071                     str);
00072       return None;
00073     }
00074 
00075   if (*end != '\0')
00076     {
00077       meta_warning (_("Did not understand trailing characters \"%s\" in string \"%s\""),
00078                     end, str);
00079       return None;
00080     }
00081 
00082   return l;
00083 }
00084 
00085 static int
00086 pid_from_string (const char *str)
00087 {
00088   char *end;
00089   long l;
00090 
00091   end = NULL;
00092   
00093   l = strtol (str, &end, 10);
00094 
00095   if (end == NULL || end == str)
00096     {
00097       meta_warning (_("Could not parse \"%s\" as an integer"),
00098                     str);
00099       return None;
00100     }
00101 
00102   if (*end != '\0')
00103     {
00104       meta_warning (_("Did not understand trailing characters \"%s\" in string \"%s\""),
00105                     end, str);
00106       return None;
00107     }
00108 
00109   return l;
00110 }
00111 
00112 static gboolean
00113 parse_dialog_output (const char *str,
00114                      int        *pid_out,
00115                      Window     *win_out)
00116 {
00117   char **split;
00118 
00119   split = g_strsplit (str, "\n", 2);
00120   if (split && split[0] && split[1])
00121     {
00122       g_strchomp (split[0]);
00123       g_strchomp (split[1]);
00124 
00125       *pid_out = pid_from_string (split[0]);
00126       *win_out = window_from_string (split[1]);
00127 
00128       g_strfreev (split);
00129       
00130       return TRUE;
00131     }
00132   else
00133     {
00134       g_strfreev (split);
00135       meta_warning (_("Failed to parse message \"%s\" from dialog process\n"),
00136                     str);
00137       return FALSE;
00138     }
00139 }
00140 
00141 static void
00142 search_and_destroy_window (int    pid,
00143                            Window xwindow)
00144 {
00145   /* Find the window with the given dialog PID,
00146    * double check that it matches "xwindow", then
00147    * kill the window.
00148    */
00149   GSList *tmp;
00150   gboolean found = FALSE;
00151   GSList *windows;
00152 
00153   if (xwindow == None)
00154     {
00155       meta_topic (META_DEBUG_PING,
00156                   "Window to destroy is None, doing nothing\n");
00157       return;
00158     }
00159 
00160   windows = meta_display_list_windows (meta_get_display ());
00161   tmp = windows;
00162 
00163   while (tmp != NULL)
00164     {
00165       MetaWindow *w = tmp->data;
00166 
00167       if (w->dialog_pid == pid)
00168         {
00169           if (w->xwindow != xwindow)
00170             meta_topic (META_DEBUG_PING,
00171                         "Dialog pid matches but not xwindow (0x%lx vs. 0x%lx)\n",
00172                         w->xwindow, xwindow);
00173           else
00174             {
00175               meta_window_kill (w);
00176               found = TRUE;
00177             }
00178         }
00179           
00180       tmp = tmp->next;
00181     }
00182 
00183   g_slist_free (windows);
00184   
00185   if (!found)
00186     meta_topic (META_DEBUG_PING,
00187                 "Did not find a window with dialog pid %d xwindow 0x%lx\n",
00188                 pid, xwindow);
00189 }
00190 
00191 static void
00192 release_window_with_fd (int fd)
00193 {
00194   /* Find the window with the given dialog PID,
00195    * double check that it matches "xwindow", then
00196    * kill the window.
00197    */
00198   gboolean found = FALSE;
00199   
00200   GSList *windows = meta_display_list_windows (meta_get_display ());
00201   GSList *tmp = windows;
00202 
00203   while (tmp != NULL)
00204     {
00205       MetaWindow *w = tmp->data;
00206 
00207       if (w->dialog_pid >= 0 &&
00208           w->dialog_pipe == fd)
00209         {
00210           meta_topic (META_DEBUG_PING,
00211                       "Removing dialog with fd %d pid %d from window %s\n",
00212                       fd, w->dialog_pid, w->desc);
00213           meta_window_free_delete_dialog (w);
00214           found = TRUE;
00215         }
00216        
00217       tmp = tmp->next;
00218     }
00219 
00220   g_slist_free (windows);
00221       
00222   if (!found)
00223     meta_topic (META_DEBUG_PING,
00224                 "Did not find a window with a dialog pipe %d\n",
00225                 fd);
00226 }
00227 
00228 static gboolean  
00229 io_from_ping_dialog (GIOChannel   *channel,
00230                      GIOCondition  condition,
00231                      gpointer      data)
00232 {  
00233   meta_topic (META_DEBUG_PING,
00234               "IO handler from ping dialog, condition = %x\n",
00235               condition);
00236   
00237   if (condition & G_IO_IN)
00238     {
00239       char *str;
00240       gsize len;
00241       GError *err;
00242 
00243       /* Go ahead and block for all data from child */
00244       str = NULL;
00245       len = 0;
00246       err = NULL;
00247       g_io_channel_read_to_end (channel,
00248                                 &str, &len,
00249                                 &err);
00250       
00251       if (err)
00252         {
00253           meta_warning (_("Error reading from dialog display process: %s\n"),
00254                         err->message);
00255           g_error_free (err);
00256         }
00257 
00258       meta_topic (META_DEBUG_PING,
00259                   "Read %" G_GSIZE_FORMAT " bytes strlen %d \"%s\" from child\n",
00260                   len, str ? (int) strlen (str) : 0, str ? str : "NULL");
00261       
00262       if (len > 0)
00263         {
00264           /* We're supposed to kill the given window */
00265           int pid;
00266           Window xwindow;
00267 
00268           if (parse_dialog_output (str, &pid, &xwindow))
00269             search_and_destroy_window (pid, xwindow);
00270         }
00271 
00272       g_free (str);
00273     }
00274 
00275   release_window_with_fd (g_io_channel_unix_get_fd (channel));
00276   
00277   /* Remove the callback */
00278   return FALSE; 
00279 }
00280 
00281 static void
00282 delete_ping_timeout_func (MetaDisplay *display,
00283                           Window       xwindow,
00284                           guint32      timestamp,
00285                           void        *user_data)
00286 {
00287   MetaWindow *window = user_data;
00288   GError *err;
00289   int child_pid;
00290   int outpipe;
00291   char *argv[9];
00292   char numbuf[32];
00293   char timestampbuf[32];
00294   char *window_id_str;
00295   char *window_title;
00296   GIOChannel *channel;
00297   
00298   meta_topic (META_DEBUG_PING,
00299               "Got delete ping timeout for %s\n",
00300               window->desc);
00301 
00302   if (window->dialog_pid >= 0)
00303     {
00304       meta_window_present_delete_dialog (window, timestamp);
00305       return;
00306     }
00307   
00308   window_id_str = g_strdup_printf ("0x%lx", window->xwindow);
00309   window_title = g_locale_from_utf8 (window->title, -1, NULL, NULL, NULL);
00310 
00311   sprintf (numbuf, "%d", window->screen->number);
00312   sprintf (timestampbuf, "%u", timestamp);
00313   
00314   argv[0] = METACITY_LIBEXECDIR"/metacity-dialog";
00315   argv[1] = "--screen";
00316   argv[2] = numbuf;
00317   argv[3] = "--timestamp";
00318   argv[4] = timestampbuf;
00319   argv[5] = "--kill-window-question";
00320   argv[6] = window_title;
00321   argv[7] = window_id_str;
00322   argv[8] = NULL;
00323   
00324   err = NULL;
00325   if (!g_spawn_async_with_pipes ("/",
00326                                  argv,
00327                                  NULL,
00328                                  0,
00329                                  NULL, NULL,
00330                                  &child_pid,
00331                                  NULL,
00332                                  &outpipe,
00333                                  NULL,
00334                                  &err))
00335     {
00336       meta_warning (_("Error launching metacity-dialog to ask about killing an application: %s\n"),
00337                     err->message);
00338       g_error_free (err);
00339       goto out;
00340     }
00341 
00342   window->dialog_pid = child_pid;
00343   window->dialog_pipe = outpipe;  
00344 
00345   channel = g_io_channel_unix_new (window->dialog_pipe);
00346   g_io_add_watch_full (channel, G_PRIORITY_DEFAULT,
00347                        G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
00348                        io_from_ping_dialog,
00349                        NULL, NULL);
00350   g_io_channel_unref (channel);
00351   
00352  out:
00353   g_free (window_title);
00354   g_free (window_id_str);
00355 }
00356 
00357 void
00358 meta_window_delete (MetaWindow  *window,
00359                     guint32      timestamp)
00360 {
00361   meta_error_trap_push (window->display);
00362   if (window->delete_window)
00363     {
00364       meta_topic (META_DEBUG_WINDOW_OPS,
00365                   "Deleting %s with delete_window request\n",
00366                   window->desc);
00367       meta_window_send_icccm_message (window,
00368                                       window->display->atom_WM_DELETE_WINDOW,
00369                                       timestamp);
00370     }
00371   else
00372     {
00373       meta_topic (META_DEBUG_WINDOW_OPS,
00374                   "Deleting %s with explicit kill\n",
00375                   window->desc);
00376       XKillClient (window->display->xdisplay, window->xwindow);
00377     }
00378   meta_error_trap_pop (window->display, FALSE);
00379 
00380   meta_display_ping_window (window->display,
00381                             window,
00382                             timestamp,
00383                             delete_ping_reply_func,
00384                             delete_ping_timeout_func,
00385                             window);
00386   
00387   if (window->has_focus)
00388     {
00389       /* FIXME Clean this up someday 
00390        * http://bugzilla.gnome.org/show_bug.cgi?id=108706
00391        */
00392 #if 0
00393       /* This is unfortunately going to result in weirdness
00394        * if the window doesn't respond to the delete event.
00395        * I don't know how to avoid that though.
00396        */
00397       meta_topic (META_DEBUG_FOCUS,
00398                   "Focusing default window because focus window %s was deleted/killed\n",
00399                   window->desc);
00400       meta_workspace_focus_default_window (window->screen->active_workspace,
00401                                            window);
00402 #else
00403       meta_topic (META_DEBUG_FOCUS,
00404                   "Not unfocusing %s on delete/kill\n",
00405                   window->desc);
00406 #endif
00407     }
00408   else
00409     {
00410       meta_topic (META_DEBUG_FOCUS,
00411                   "Window %s was deleted/killed but didn't have focus\n",
00412                   window->desc);
00413     }
00414 }
00415 
00416 
00417 void
00418 meta_window_kill (MetaWindow *window)
00419 {
00420   char buf[257];
00421   
00422   meta_topic (META_DEBUG_WINDOW_OPS,
00423               "Killing %s brutally\n",
00424               window->desc);
00425 
00426   if (window->wm_client_machine != NULL &&
00427       window->net_wm_pid > 0)
00428     {
00429       if (gethostname (buf, sizeof(buf)-1) == 0)
00430         {
00431           if (strcmp (buf, window->wm_client_machine) == 0)
00432             {
00433               meta_topic (META_DEBUG_WINDOW_OPS,
00434                           "Killing %s with kill()\n",
00435                           window->desc);
00436 
00437               if (kill (window->net_wm_pid, 9) < 0)
00438                 meta_topic (META_DEBUG_WINDOW_OPS,
00439                             "Failed to signal %s: %s\n",
00440                             window->desc, strerror (errno));
00441             }
00442         }
00443       else
00444         {
00445           meta_warning (_("Failed to get hostname: %s\n"),
00446                         strerror (errno));
00447         }
00448     }
00449   
00450   meta_topic (META_DEBUG_WINDOW_OPS,
00451               "Disconnecting %s with XKillClient()\n",
00452               window->desc);
00453   meta_error_trap_push (window->display);
00454   XKillClient (window->display->xdisplay, window->xwindow);
00455   meta_error_trap_pop (window->display, FALSE);
00456 }
00457 
00458 void
00459 meta_window_free_delete_dialog (MetaWindow *window)
00460 {
00461   if (window->dialog_pid >= 0)
00462     {
00463       kill (window->dialog_pid, 9);
00464       close (window->dialog_pipe);
00465       window->dialog_pid = -1;
00466       window->dialog_pipe = -1;
00467     }
00468 }
00469 
00470 static void
00471 meta_window_present_delete_dialog (MetaWindow *window, guint32 timestamp)
00472 {
00473   meta_topic (META_DEBUG_PING,
00474               "Presenting existing ping dialog for %s\n",
00475               window->desc);
00476   
00477   if (window->dialog_pid >= 0)
00478     {
00479       GSList *windows;
00480       GSList *tmp;
00481 
00482       /* Activate transient for window that belongs to
00483        * metacity-dialog
00484        */
00485       
00486       windows = meta_display_list_windows (window->display);
00487       tmp = windows;
00488       while (tmp != NULL)
00489         {
00490           MetaWindow *w = tmp->data;
00491 
00492           if (w->xtransient_for == window->xwindow &&
00493               w->res_class &&
00494               g_strcasecmp (w->res_class, "metacity-dialog") == 0)
00495             {
00496               meta_window_activate (w, timestamp);
00497               break;
00498             }
00499           
00500           tmp = tmp->next;
00501         }
00502 
00503       g_slist_free (windows);
00504     }
00505 }

Generated on Sat Aug 23 22:04:16 2008 for metacity by  doxygen 1.5.5