
2006-10-01 Elijah Newren <newren gmail com> * src/*.[ch]: Stick an emacs comment directive at the beginning of all the code files so that people using emacs will be more likely to get coding style correct in their patches. We still need a similar vi directive. #358866
517 lines
13 KiB
C
517 lines
13 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
|
|
/* Metacity window deletion */
|
|
|
|
/*
|
|
* Copyright (C) 2001, 2002 Havoc Pennington
|
|
* Copyright (C) 2004 Elijah Newren
|
|
*
|
|
* 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, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
* 02111-1307, USA.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include "util.h"
|
|
#include "window.h"
|
|
#include "errors.h"
|
|
#include "workspace.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <signal.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
static void meta_window_present_delete_dialog (MetaWindow *window,
|
|
guint32 timestamp);
|
|
|
|
static void
|
|
delete_ping_reply_func (MetaDisplay *display,
|
|
Window xwindow,
|
|
guint32 timestamp,
|
|
void *user_data)
|
|
{
|
|
meta_topic (META_DEBUG_PING,
|
|
"Got reply to delete ping for %s\n",
|
|
((MetaWindow*)user_data)->desc);
|
|
|
|
/* we do nothing */
|
|
}
|
|
|
|
static Window
|
|
window_from_string (const char *str)
|
|
{
|
|
char *end;
|
|
unsigned long l;
|
|
|
|
end = NULL;
|
|
|
|
l = strtoul (str, &end, 16);
|
|
|
|
if (end == NULL || end == str)
|
|
{
|
|
meta_warning (_("Could not parse \"%s\" as an integer"),
|
|
str);
|
|
return None;
|
|
}
|
|
|
|
if (*end != '\0')
|
|
{
|
|
meta_warning (_("Did not understand trailing characters \"%s\" in string \"%s\""),
|
|
end, str);
|
|
return None;
|
|
}
|
|
|
|
return l;
|
|
}
|
|
|
|
static int
|
|
pid_from_string (const char *str)
|
|
{
|
|
char *end;
|
|
long l;
|
|
|
|
end = NULL;
|
|
|
|
l = strtol (str, &end, 10);
|
|
|
|
if (end == NULL || end == str)
|
|
{
|
|
meta_warning (_("Could not parse \"%s\" as an integer"),
|
|
str);
|
|
return None;
|
|
}
|
|
|
|
if (*end != '\0')
|
|
{
|
|
meta_warning (_("Did not understand trailing characters \"%s\" in string \"%s\""),
|
|
end, str);
|
|
return None;
|
|
}
|
|
|
|
return l;
|
|
}
|
|
|
|
static gboolean
|
|
parse_dialog_output (const char *str,
|
|
int *pid_out,
|
|
Window *win_out)
|
|
{
|
|
char **split;
|
|
|
|
split = g_strsplit (str, "\n", 2);
|
|
if (split && split[0] && split[1])
|
|
{
|
|
g_strchomp (split[0]);
|
|
g_strchomp (split[1]);
|
|
|
|
*pid_out = pid_from_string (split[0]);
|
|
*win_out = window_from_string (split[1]);
|
|
|
|
g_strfreev (split);
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
g_strfreev (split);
|
|
meta_warning (_("Failed to parse message \"%s\" from dialog process\n"),
|
|
str);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
search_and_destroy_window (int pid,
|
|
Window xwindow)
|
|
{
|
|
/* Find the window with the given dialog PID,
|
|
* double check that it matches "xwindow", then
|
|
* kill the window.
|
|
*/
|
|
GSList *tmp;
|
|
gboolean found;
|
|
|
|
if (xwindow == None)
|
|
{
|
|
meta_topic (META_DEBUG_PING,
|
|
"Window to destroy is None, doing nothing\n");
|
|
return;
|
|
}
|
|
|
|
found = FALSE;
|
|
tmp = meta_displays_list ();
|
|
while (tmp != NULL)
|
|
{
|
|
GSList *windows = meta_display_list_windows (tmp->data);
|
|
GSList *tmp2;
|
|
|
|
tmp2 = windows;
|
|
while (tmp2 != NULL)
|
|
{
|
|
MetaWindow *w = tmp2->data;
|
|
|
|
if (w->dialog_pid == pid)
|
|
{
|
|
if (w->xwindow != xwindow)
|
|
meta_topic (META_DEBUG_PING,
|
|
"Dialog pid matches but not xwindow (0x%lx vs. 0x%lx)\n",
|
|
w->xwindow, xwindow);
|
|
else
|
|
{
|
|
meta_window_kill (w);
|
|
found = TRUE;
|
|
}
|
|
}
|
|
|
|
tmp2 = tmp2->next;
|
|
}
|
|
|
|
g_slist_free (windows);
|
|
|
|
tmp = tmp->next;
|
|
}
|
|
|
|
if (!found)
|
|
meta_topic (META_DEBUG_PING,
|
|
"Did not find a window with dialog pid %d xwindow 0x%lx\n",
|
|
pid, xwindow);
|
|
}
|
|
|
|
static void
|
|
release_window_with_fd (int fd)
|
|
{
|
|
/* Find the window with the given dialog PID,
|
|
* double check that it matches "xwindow", then
|
|
* kill the window.
|
|
*/
|
|
GSList *tmp;
|
|
gboolean found;
|
|
|
|
found = FALSE;
|
|
|
|
tmp = meta_displays_list ();
|
|
while (tmp != NULL)
|
|
{
|
|
GSList *windows = meta_display_list_windows (tmp->data);
|
|
GSList *tmp2;
|
|
|
|
tmp2 = windows;
|
|
while (tmp2 != NULL)
|
|
{
|
|
MetaWindow *w = tmp2->data;
|
|
|
|
if (w->dialog_pid >= 0 &&
|
|
w->dialog_pipe == fd)
|
|
{
|
|
meta_topic (META_DEBUG_PING,
|
|
"Removing dialog with fd %d pid %d from window %s\n",
|
|
fd, w->dialog_pid, w->desc);
|
|
meta_window_free_delete_dialog (w);
|
|
found = TRUE;
|
|
}
|
|
|
|
tmp2 = tmp2->next;
|
|
}
|
|
|
|
g_slist_free (windows);
|
|
|
|
tmp = tmp->next;
|
|
}
|
|
|
|
if (!found)
|
|
meta_topic (META_DEBUG_PING,
|
|
"Did not find a window with a dialog pipe %d\n",
|
|
fd);
|
|
}
|
|
|
|
static gboolean
|
|
io_from_ping_dialog (GIOChannel *channel,
|
|
GIOCondition condition,
|
|
gpointer data)
|
|
{
|
|
meta_topic (META_DEBUG_PING,
|
|
"IO handler from ping dialog, condition = %x\n",
|
|
condition);
|
|
|
|
if (condition & G_IO_IN)
|
|
{
|
|
char *str;
|
|
unsigned int len;
|
|
GError *err;
|
|
|
|
/* Go ahead and block for all data from child */
|
|
str = NULL;
|
|
len = 0;
|
|
err = NULL;
|
|
g_io_channel_read_to_end (channel,
|
|
&str, &len,
|
|
&err);
|
|
|
|
if (err)
|
|
{
|
|
meta_warning (_("Error reading from dialog display process: %s\n"),
|
|
err->message);
|
|
g_error_free (err);
|
|
}
|
|
|
|
meta_topic (META_DEBUG_PING,
|
|
"Read %d bytes strlen %d \"%s\" from child\n",
|
|
len, str ? strlen (str) : 0, str ? str : "NULL");
|
|
|
|
if (len > 0)
|
|
{
|
|
/* We're supposed to kill the given window */
|
|
int pid;
|
|
Window xwindow;
|
|
|
|
if (parse_dialog_output (str, &pid, &xwindow))
|
|
search_and_destroy_window (pid, xwindow);
|
|
}
|
|
|
|
g_free (str);
|
|
}
|
|
|
|
release_window_with_fd (g_io_channel_unix_get_fd (channel));
|
|
|
|
/* Remove the callback */
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
delete_ping_timeout_func (MetaDisplay *display,
|
|
Window xwindow,
|
|
guint32 timestamp,
|
|
void *user_data)
|
|
{
|
|
MetaWindow *window = user_data;
|
|
GError *err;
|
|
int child_pid;
|
|
int outpipe;
|
|
char *argv[9];
|
|
char numbuf[32];
|
|
char timestampbuf[32];
|
|
char *window_id_str;
|
|
GIOChannel *channel;
|
|
|
|
meta_topic (META_DEBUG_PING,
|
|
"Got delete ping timeout for %s\n",
|
|
window->desc);
|
|
|
|
if (window->dialog_pid >= 0)
|
|
{
|
|
meta_window_present_delete_dialog (window, timestamp);
|
|
return;
|
|
}
|
|
|
|
window_id_str = g_strdup_printf ("0x%lx", window->xwindow);
|
|
|
|
sprintf (numbuf, "%d", window->screen->number);
|
|
sprintf (timestampbuf, "%u", timestamp);
|
|
|
|
argv[0] = METACITY_LIBEXECDIR"/metacity-dialog";
|
|
argv[1] = "--screen";
|
|
argv[2] = numbuf;
|
|
argv[3] = "--timestamp";
|
|
argv[4] = timestampbuf;
|
|
argv[5] = "--kill-window-question";
|
|
argv[6] = window->title;
|
|
argv[7] = window_id_str;
|
|
argv[8] = NULL;
|
|
|
|
err = NULL;
|
|
if (!g_spawn_async_with_pipes ("/",
|
|
argv,
|
|
NULL,
|
|
0,
|
|
NULL, NULL,
|
|
&child_pid,
|
|
NULL,
|
|
&outpipe,
|
|
NULL,
|
|
&err))
|
|
{
|
|
meta_warning (_("Error launching metacity-dialog to ask about killing an application: %s\n"),
|
|
err->message);
|
|
g_error_free (err);
|
|
goto out;
|
|
}
|
|
|
|
window->dialog_pid = child_pid;
|
|
window->dialog_pipe = outpipe;
|
|
|
|
channel = g_io_channel_unix_new (window->dialog_pipe);
|
|
g_io_add_watch_full (channel, G_PRIORITY_DEFAULT,
|
|
G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
|
|
io_from_ping_dialog,
|
|
NULL, NULL);
|
|
g_io_channel_unref (channel);
|
|
|
|
out:
|
|
g_free (window_id_str);
|
|
}
|
|
|
|
void
|
|
meta_window_delete (MetaWindow *window,
|
|
guint32 timestamp)
|
|
{
|
|
meta_error_trap_push (window->display);
|
|
if (window->delete_window)
|
|
{
|
|
meta_topic (META_DEBUG_WINDOW_OPS,
|
|
"Deleting %s with delete_window request\n",
|
|
window->desc);
|
|
meta_window_send_icccm_message (window,
|
|
window->display->atom_wm_delete_window,
|
|
timestamp);
|
|
}
|
|
else
|
|
{
|
|
meta_topic (META_DEBUG_WINDOW_OPS,
|
|
"Deleting %s with explicit kill\n",
|
|
window->desc);
|
|
XKillClient (window->display->xdisplay, window->xwindow);
|
|
}
|
|
meta_error_trap_pop (window->display, FALSE);
|
|
|
|
meta_display_ping_window (window->display,
|
|
window,
|
|
timestamp,
|
|
delete_ping_reply_func,
|
|
delete_ping_timeout_func,
|
|
window);
|
|
|
|
if (window->has_focus)
|
|
{
|
|
/* FIXME Clean this up someday
|
|
* http://bugzilla.gnome.org/show_bug.cgi?id=108706
|
|
*/
|
|
#if 0
|
|
/* This is unfortunately going to result in weirdness
|
|
* if the window doesn't respond to the delete event.
|
|
* I don't know how to avoid that though.
|
|
*/
|
|
meta_topic (META_DEBUG_FOCUS,
|
|
"Focusing default window because focus window %s was deleted/killed\n",
|
|
window->desc);
|
|
meta_workspace_focus_default_window (window->screen->active_workspace,
|
|
window);
|
|
#else
|
|
meta_topic (META_DEBUG_FOCUS,
|
|
"Not unfocusing %s on delete/kill\n",
|
|
window->desc);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
meta_topic (META_DEBUG_FOCUS,
|
|
"Window %s was deleted/killed but didn't have focus\n",
|
|
window->desc);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
meta_window_kill (MetaWindow *window)
|
|
{
|
|
char buf[257];
|
|
|
|
meta_topic (META_DEBUG_WINDOW_OPS,
|
|
"Killing %s brutally\n",
|
|
window->desc);
|
|
|
|
if (window->wm_client_machine != NULL &&
|
|
window->net_wm_pid > 0)
|
|
{
|
|
if (gethostname (buf, sizeof(buf)-1) == 0)
|
|
{
|
|
if (strcmp (buf, window->wm_client_machine) == 0)
|
|
{
|
|
meta_topic (META_DEBUG_WINDOW_OPS,
|
|
"Killing %s with kill()\n",
|
|
window->desc);
|
|
|
|
if (kill (window->net_wm_pid, 9) < 0)
|
|
meta_topic (META_DEBUG_WINDOW_OPS,
|
|
"Failed to signal %s: %s\n",
|
|
window->desc, strerror (errno));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
meta_warning (_("Failed to get hostname: %s\n"),
|
|
strerror (errno));
|
|
}
|
|
}
|
|
|
|
meta_topic (META_DEBUG_WINDOW_OPS,
|
|
"Disconnecting %s with XKillClient()\n",
|
|
window->desc);
|
|
meta_error_trap_push (window->display);
|
|
XKillClient (window->display->xdisplay, window->xwindow);
|
|
meta_error_trap_pop (window->display, FALSE);
|
|
}
|
|
|
|
void
|
|
meta_window_free_delete_dialog (MetaWindow *window)
|
|
{
|
|
if (window->dialog_pid >= 0)
|
|
{
|
|
kill (window->dialog_pid, 9);
|
|
close (window->dialog_pipe);
|
|
window->dialog_pid = -1;
|
|
window->dialog_pipe = -1;
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_window_present_delete_dialog (MetaWindow *window, guint32 timestamp)
|
|
{
|
|
meta_topic (META_DEBUG_PING,
|
|
"Presenting existing ping dialog for %s\n",
|
|
window->desc);
|
|
|
|
if (window->dialog_pid >= 0)
|
|
{
|
|
GSList *windows;
|
|
GSList *tmp;
|
|
|
|
/* Activate transient for window that belongs to
|
|
* metacity-dialog
|
|
*/
|
|
|
|
windows = meta_display_list_windows (window->display);
|
|
tmp = windows;
|
|
while (tmp != NULL)
|
|
{
|
|
MetaWindow *w = tmp->data;
|
|
|
|
if (w->xtransient_for == window->xwindow &&
|
|
w->res_class &&
|
|
g_strcasecmp (w->res_class, "metacity-dialog") == 0)
|
|
{
|
|
meta_window_activate (w, timestamp);
|
|
break;
|
|
}
|
|
|
|
tmp = tmp->next;
|
|
}
|
|
|
|
g_slist_free (windows);
|
|
}
|
|
}
|