/* Metacity UI Slave */ /* * Copyright (C) 2001 Havoc Pennington * * 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 "uislave.h" #include "window.h" #include #include #include #include #include #include static void respawn_child (MetaUISlave *uislave); static gboolean error_callback (GIOChannel *source, GIOCondition condition, gpointer data); static void kill_child (MetaUISlave *uislave); static void reset_vals (MetaUISlave *uislave); static void message_queue_func (MetaMessageQueue *mq, MetaMessage* message, gpointer data); MetaUISlave* meta_ui_slave_new (const char *display_name, MetaUISlaveFunc func, gpointer data) { MetaUISlave *uislave; uislave = g_new (MetaUISlave, 1); uislave->display_name = g_strdup (display_name); uislave->func = func; uislave->data = data; uislave->no_respawn = FALSE; reset_vals (uislave); /* This may fail; all UISlave functions become no-ops * if uislave->child_pids == 0, and metacity just runs * with no UI features other than window borders. */ respawn_child (uislave); return uislave; } void meta_ui_slave_free (MetaUISlave *uislave) { meta_verbose ("Deleting UI slave for display '%s'\n", uislave->display_name); kill_child (uislave); g_free (uislave->display_name); g_free (uislave); } void meta_ui_slave_disable (MetaUISlave *uislave) { /* Change UI slave into "black hole" mode, * we found out it's hosed for some reason. */ kill_child (uislave); uislave->no_respawn = TRUE; } static void message_queue_func (MetaMessageQueue *mq, MetaMessage* message, gpointer data) { MetaUISlave *uislave; uislave = data; (* uislave->func) (uislave, message, uislave->data); } static void respawn_child (MetaUISlave *uislave) { GError *error; const char *uislavedir; char *argv[] = { "./metacity-uislave", NULL }; char *envp[2] = { NULL, NULL }; int child_pid, inpipe, outpipe, errpipe; if (uislave->no_respawn) return; uislavedir = g_getenv ("METACITY_UISLAVE_DIR"); if (uislavedir == NULL) uislavedir = METACITY_LIBEXECDIR; envp[0] = g_strconcat ("DISPLAY=", uislave->display_name, NULL); error = NULL; if (g_spawn_async_with_pipes (uislavedir, argv, envp, /* flags */ 0, /* setup func, data */ NULL, NULL, &child_pid, &inpipe, &outpipe, &errpipe, &error)) { uislave->child_pid = child_pid; uislave->in_pipe = inpipe; uislave->err_pipe = errpipe; uislave->out_pipe = outpipe; uislave->err_channel = g_io_channel_unix_new (errpipe); uislave->errwatch = g_io_add_watch (uislave->err_channel, G_IO_IN, error_callback, uislave); uislave->mq = meta_message_queue_new (outpipe, message_queue_func, uislave); meta_verbose ("Spawned UI slave with PID %d\n", uislave->child_pid); } else { meta_warning ("Failed to create user interface process: %s\n", error->message); g_error_free (error); } g_free (envp[0]); } static gboolean error_callback (GIOChannel *source, GIOCondition condition, gpointer data) { /* Relay slave errors to WM stderr */ #define BUFSIZE 1024 MetaUISlave *uislave; char buf[1024]; int n; uislave = data; /* Classic loop from Stevens */ n = read (uislave->err_pipe, buf, BUFSIZE); if (n > 0) { if (write (2, buf, n) != n) ; /* error, but printing a message to stderr will hardly help. */ } else if (n < 0) meta_warning (_("Error reading errors from UI slave: %s\n"), g_strerror (errno)); return TRUE; #undef BUFSIZE } static void kill_child (MetaUISlave *uislave) { if (uislave->mq) meta_message_queue_free (uislave->mq); if (uislave->errwatch != 0) g_source_remove (uislave->errwatch); if (uislave->err_channel) g_io_channel_unref (uislave->err_channel); if (uislave->out_pipe >= 0) close (uislave->out_pipe); if (uislave->in_pipe >= 0) close (uislave->in_pipe); if (uislave->err_pipe >= 0) close (uislave->err_pipe); if (uislave->child_pid > 0) { /* don't care if this fails except in verbose mode */ if (kill (uislave->child_pid, SIGTERM) != 0) { meta_verbose ("Kill of UI slave process %d failed: %s\n", uislave->child_pid, g_strerror (errno)); } uislave->child_pid = 0; } reset_vals (uislave); } static void reset_vals (MetaUISlave *uislave) { uislave->mq = NULL; uislave->child_pid = 0; uislave->in_pipe = -1; uislave->err_pipe = -1; uislave->out_pipe = -1; uislave->err_channel = NULL; uislave->errwatch = 0; /* don't reset no_respawn, it's a permanent thing. */ } /* * Message delivery */ static int write_bytes (int fd, void *buf, int bytes) { const char *p; int left; left = bytes; p = (char*) buf; while (left > 0) { int written; written = write (fd, p, left); if (written < 0) return -1; left -= written; p += written; } g_assert (p == ((char*)buf) + bytes); return 0; } static void send_message (MetaUISlave *uislave, MetaMessage *message) { static int serial = 0; MetaMessageFooter *footer; message->header.serial = serial; footer = META_MESSAGE_FOOTER (message); footer->checksum = META_MESSAGE_CHECKSUM (message); ++serial; if (write_bytes (uislave->in_pipe, META_MESSAGE_ESCAPE, META_MESSAGE_ESCAPE_LEN) < 0) meta_warning ("Failed to write escape sequence: %s\n", g_strerror (errno)); if (write_bytes (uislave->in_pipe, message, message->header.length) < 0) meta_warning ("Failed to write message: %s\n", g_strerror (errno)); } void meta_ui_slave_show_tip (MetaUISlave *uislave, int root_x, int root_y, const char *markup_text) { MetaMessageShowTip showtip; memset (&showtip, 0, META_MESSAGE_LENGTH (MetaMessageShowTip)); showtip.header.message_code = MetaMessageShowTipCode; showtip.header.length = META_MESSAGE_LENGTH (MetaMessageShowTip); showtip.root_x = root_x; showtip.root_y = root_y; strncpy (showtip.markup, markup_text, META_MESSAGE_MAX_TIP_LEN); showtip.markup[META_MESSAGE_MAX_TIP_LEN] = '\0'; send_message (uislave, (MetaMessage*)&showtip); } void meta_ui_slave_hide_tip (MetaUISlave *uislave) { MetaMessageHideTip hidetip; memset (&hidetip, 0, META_MESSAGE_LENGTH (MetaMessageHideTip)); hidetip.header.message_code = MetaMessageHideTipCode; hidetip.header.length = META_MESSAGE_LENGTH (MetaMessageHideTip); send_message (uislave, (MetaMessage*)&hidetip); } void meta_ui_slave_show_window_menu (MetaUISlave *uislave, MetaWindow *window, int root_x, int root_y, int button, MetaMessageWindowMenuOps ops, MetaMessageWindowMenuOps insensitive, Time timestamp) { MetaMessageShowWindowMenu showmenu; memset (&showmenu, 0, META_MESSAGE_LENGTH (MetaMessageShowWindowMenu)); showmenu.header.message_code = MetaMessageShowWindowMenuCode; showmenu.header.length = META_MESSAGE_LENGTH (MetaMessageShowWindowMenu); showmenu.window = window->xwindow; showmenu.root_x = root_x; showmenu.root_y = root_y; showmenu.button = button; showmenu.ops = ops; showmenu.insensitive = insensitive; showmenu.timestamp = timestamp; send_message (uislave, (MetaMessage*)&showmenu); } void meta_ui_slave_hide_window_menu (MetaUISlave *uislave) { MetaMessageHideWindowMenu hidemenu; memset (&hidemenu, 0, META_MESSAGE_LENGTH (MetaMessageHideWindowMenu)); hidemenu.header.message_code = MetaMessageHideWindowMenuCode; hidemenu.header.length = META_MESSAGE_LENGTH (MetaMessageHideWindowMenu); send_message (uislave, (MetaMessage*)&hidemenu); }