diff --git a/po/.cvsignore b/po/.cvsignore new file mode 100644 index 000000000..809bd4aaf --- /dev/null +++ b/po/.cvsignore @@ -0,0 +1,6 @@ +Makefile.in.in +POTFILES +Makefile.in +Makefile +stamp-cat-id +cat-id-tbl.c diff --git a/src/.cvsignore b/src/.cvsignore new file mode 100644 index 000000000..37889d6a1 --- /dev/null +++ b/src/.cvsignore @@ -0,0 +1,7 @@ +.libs +Makefile.in +Makefile +.deps +metacity +messagequeue.h +messagequeue.c diff --git a/src/Makefile.am b/src/Makefile.am index eef9aae4d..9f4f4f264 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,7 +18,22 @@ BUILT_SOURCES=$(copied_sources) $(copied_sources): $(copied_sources_deps) for I in $(copied_sources); do \ rm -f $$I ; \ - cp $(srcdir)/uislave/$$I . ; \ + echo "/* DO NOT EDIT THIS FILE */" > $$I ; \ + echo "/* DO NOT EDIT THIS FILE */" >> $$I ; \ + echo "/* DO NOT EDIT THIS FILE */" >> $$I ; \ + echo "/* DO NOT EDIT THIS FILE */" >> $$I ; \ + echo "/* DO NOT EDIT THIS FILE */" >> $$I ; \ + echo "/* DO NOT EDIT THIS FILE */" >> $$I ; \ + echo "/* DO NOT EDIT THIS FILE */" >> $$I ; \ + echo "/* DO NOT EDIT THIS FILE */" >> $$I ; \ + echo "/* DO NOT EDIT THIS FILE */" >> $$I ; \ + echo "/* DO NOT EDIT THIS FILE */" >> $$I ; \ + echo "/* DO NOT EDIT THIS FILE */" >> $$I ; \ + echo "/* DO NOT EDIT THIS FILE */" >> $$I ; \ + echo "/* DO NOT EDIT THIS FILE */" >> $$I ; \ + echo "/* DO NOT EDIT THIS FILE */" >> $$I ; \ + echo "/* DO NOT EDIT THIS FILE */" >> $$I ; \ + cat $(srcdir)/uislave/$$I >> $$I ; \ done metacity_SOURCES= \ diff --git a/src/display.c b/src/display.c index 95718f872..61bc14f3a 100644 --- a/src/display.c +++ b/src/display.c @@ -356,7 +356,7 @@ event_queue_callback (MetaEventQueue *queue, case ReparentNotify: break; case ConfigureNotify: - if (event->xconfigure.override_redirect) + if (window && event->xconfigure.override_redirect) { /* Unmanage it, override_redirect was toggled on? * Can this happen? diff --git a/src/fixedtip.c b/src/fixedtip.c new file mode 100644 index 000000000..bb12e405b --- /dev/null +++ b/src/fixedtip.c @@ -0,0 +1,78 @@ +/* Metacity fixed tooltip routine */ + +/* + * 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 "fixedtip.h" + +static GtkWidget *tip = NULL; +static GtkWidget *label = NULL; + +static gint +expose_handler (GtkTooltips *tooltips) +{ + gtk_paint_flat_box (tip->style, tip->window, + GTK_STATE_NORMAL, GTK_SHADOW_OUT, + NULL, tip, "tooltip", + 0, 0, -1, -1); + + return FALSE; +} + +void +meta_fixed_tip_show (int root_x, int root_y, + const char *markup_text) +{ + if (tip == NULL) + { + tip = gtk_window_new (GTK_WINDOW_POPUP); + gtk_widget_set_app_paintable (tip, TRUE); + gtk_window_set_policy (GTK_WINDOW (tip), FALSE, FALSE, TRUE); + gtk_widget_set_name (tip, "gtk-tooltips"); + gtk_container_set_border_width (GTK_CONTAINER (tip), 4); + + gtk_signal_connect_object (GTK_OBJECT (tip), + "expose_event", + GTK_SIGNAL_FUNC (expose_handler), + NULL); + + label = gtk_label_new (NULL); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5); + gtk_widget_show (label); + + gtk_container_add (GTK_CONTAINER (tip), label); + + gtk_signal_connect (GTK_OBJECT (tip), + "destroy", + GTK_SIGNAL_FUNC (gtk_widget_destroyed), + &tip); + } + + gtk_widget_set_uposition (tip, root_x, root_y); + gtk_label_set_markup (GTK_LABEL (label), markup_text); + + gtk_widget_show (tip); +} + +void +meta_fixed_tip_hide (void) +{ + gtk_widget_destroy (tip); +} diff --git a/src/fixedtip.h b/src/fixedtip.h new file mode 100644 index 000000000..4b637fc6e --- /dev/null +++ b/src/fixedtip.h @@ -0,0 +1,32 @@ +/* Metacity fixed tooltip routine */ + +/* + * 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. + */ + +#ifndef META_FIXED_TIP_H +#define META_FIXED_TIP_H + +#include + +void meta_fixed_tip_show (int root_x, int root_y, + const char *markup_text); +void meta_fixed_tip_hide (void); + + +#endif diff --git a/src/frame.c b/src/frame.c index 6d4adf924..2f42dbe8a 100644 --- a/src/frame.c +++ b/src/frame.c @@ -21,6 +21,7 @@ #include "frame.h" #include "errors.h" +#include "uislave.h" static void meta_frame_init_info (MetaFrame *frame, @@ -517,7 +518,13 @@ meta_frame_event (MetaFrame *frame, case META_FRAME_ACTION_RESIZING_SE: update_resize_se (frame); break; - + + case META_FRAME_ACTION_NONE: + meta_ui_slave_show_tip (frame->window->screen->uislave, + frame->rect.x, + frame->rect.y, + "Hi this is a tooltip"); + break; default: break; } diff --git a/src/main.c b/src/main.c index c1c3d9c95..bfd62b9c6 100644 --- a/src/main.c +++ b/src/main.c @@ -32,8 +32,10 @@ static GMainLoop *meta_main_loop = NULL; int main (int argc, char **argv) { + g_set_prgname (PACKAGE); + meta_main_loop = g_main_loop_new (NULL, FALSE); - + meta_set_verbose (TRUE); meta_set_debugging (TRUE); meta_set_syncing (g_getenv ("METACITY_SYNC") != NULL); diff --git a/src/screen.h b/src/screen.h index c66cef68f..b335133f5 100644 --- a/src/screen.h +++ b/src/screen.h @@ -34,6 +34,8 @@ struct _MetaScreen Window xroot; MetaThemeEngine *engine; + + MetaUISlave *uislave; /*< private >*/ @@ -41,8 +43,6 @@ struct _MetaScreen * root window) */ PangoContext *pango_context; - - MetaUISlave *uislave; }; MetaScreen* meta_screen_new (MetaDisplay *display, diff --git a/src/uislave.c b/src/uislave.c index 1e372c2f4..994cb599b 100644 --- a/src/uislave.c +++ b/src/uislave.c @@ -27,37 +27,16 @@ #include #include -typedef enum -{ - READ_FAILED = 0, /* FALSE */ - READ_OK, - READ_EOF -} ReadResult; - 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 ReadResult read_data (GString *str, - gint fd); +static void message_queue_func (MetaMessageQueue *mq, + MetaMessage* message, + gpointer data); -/* Message queue main loop source */ -static gboolean mq_prepare (GSource *source, - gint *timeout); -static gboolean mq_check (GSource *source); -static gboolean mq_dispatch (GSource *source, - GSourceFunc callback, - gpointer user_data); -static void mq_destroy (GSource *source); - -static GSourceFuncs mq_funcs = { - mq_prepare, - mq_check, - mq_dispatch, - mq_destroy -}; MetaUISlave* meta_ui_slave_new (const char *display_name, @@ -65,16 +44,13 @@ meta_ui_slave_new (const char *display_name, gpointer data) { MetaUISlave *uislave; - GSource *source; - source = g_source_new (&mq_funcs, sizeof (MetaUISlave)); - - uislave = (MetaUISlave*) source; + uislave = g_new (MetaUISlave, 1); uislave->display_name = g_strdup (display_name); - uislave->queue = g_queue_new (); - uislave->buf = g_string_new (""); - uislave->current_message = g_string_new (""); + uislave->func = func; + uislave->data = data; + uislave->no_respawn = FALSE; reset_vals (uislave); @@ -82,14 +58,7 @@ meta_ui_slave_new (const char *display_name, * if uislave->child_pids == 0, and metacity just runs * with no UI features other than window borders. */ - respawn_child (uislave); - - g_source_set_priority (source, G_PRIORITY_DEFAULT); - g_source_set_can_recurse (source, TRUE); - - g_source_set_callback (source, (GSourceFunc) func, data, NULL); - - g_source_attach (source, NULL); + respawn_child (uislave); return uislave; } @@ -97,11 +66,15 @@ meta_ui_slave_new (const char *display_name, void meta_ui_slave_free (MetaUISlave *uislave) { - GSource *source; - source = (GSource*) uislave; + meta_verbose ("Deleting UI slave for display '%s'\n", + uislave->display_name); - g_source_destroy (source); + kill_child (uislave); + + g_free (uislave->display_name); + + g_free (uislave); } void @@ -114,6 +87,18 @@ meta_ui_slave_disable (MetaUISlave *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) { @@ -147,6 +132,7 @@ respawn_child (MetaUISlave *uislave) 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); @@ -154,11 +140,10 @@ respawn_child (MetaUISlave *uislave) G_IO_IN, error_callback, uislave); - - uislave->out_poll.fd = outpipe; - uislave->out_poll.events = G_IO_IN; - g_source_add_poll ((GSource*)uislave, &uislave->out_poll); + uislave->mq = meta_message_queue_new (outpipe, + message_queue_func, + uislave); meta_verbose ("Spawned UI slave with PID %d\n", uislave->child_pid); } @@ -172,72 +157,6 @@ respawn_child (MetaUISlave *uislave) g_free (envp[0]); } -static void -append_pending (MetaUISlave *uislave) -{ - int needed; - - needed = uislave->current_required_len - uislave->current_message->len; - g_assert (needed >= 0); - - needed = MIN (needed, uislave->buf->len); - - /* Move data from buf to current_message */ - if (needed > 0) - { - meta_verbose ("Moving %d bytes from buffer to current incomplete message\n", - needed); - g_string_append_len (uislave->current_message, - uislave->buf->str, - needed); - g_string_erase (uislave->buf, - 0, needed); - } - - g_assert (uislave->current_message->len <= uislave->current_required_len); - - if (uislave->current_required_len > 0 && - uislave->current_message->len == uislave->current_required_len) - { - MetaMessage *message; - MetaMessageFooter *footer; - - message = g_new (MetaMessage, 1); - - memcpy (message, - uislave->current_message->str, uislave->current_message->len); - - if (message->header.length != uislave->current_required_len) - meta_bug ("Message length changed?\n"); - - if (message->header.serial != uislave->last_serial) - meta_bug ("Message serial changed?\n"); - - footer = META_MESSAGE_FOOTER (message); - - if (footer->checksum == META_MESSAGE_CHECKSUM (message)) - { - g_queue_push_tail (uislave->queue, message); - - meta_verbose ("Added %d-byte message serial %d to queue\n", - uislave->current_message->len, message->header.serial); - } - else - { - meta_bug ("Bad checksum %d on %d-byte message from UI slave\n", - footer->checksum, uislave->current_message->len); - } - - uislave->current_required_len = 0; - g_string_truncate (uislave->current_message, 0); - } - else if (uislave->current_required_len > 0) - { - meta_verbose ("Storing %d bytes of incomplete message\n", - uislave->current_message->len); - } -} - static gboolean error_callback (GIOChannel *source, GIOCondition condition, @@ -266,220 +185,20 @@ error_callback (GIOChannel *source, #undef BUFSIZE } -static void -mq_queue_messages (MetaUISlave *uislave) -{ - if (uislave->out_poll.revents & G_IO_IN) - { - ReadResult res; - - res = read_data (uislave->buf, uislave->out_poll.fd); - - switch (res) - { - case READ_OK: - meta_verbose ("Read data from slave, %d bytes in buffer\n", - uislave->buf->len); - break; - case READ_EOF: - meta_verbose ("EOF reading stdout from slave process\n"); - break; - - case READ_FAILED: - /* read_data printed the error */ - break; - } - } - - while (uislave->buf->len > 0) - { - if (uislave->current_required_len > 0) - { - /* We had a pending message. */ - append_pending (uislave); - } - else if (uislave->buf->len > META_MESSAGE_ESCAPE_LEN) - { - /* See if we can start a current message */ - const char *p; - int esc_pos; - const char *esc; - MetaMessageHeader header; - - g_assert (uislave->current_required_len == 0); - g_assert (uislave->current_message->len == 0); - - meta_verbose ("Scanning for escape sequence in %d bytes\n", - uislave->buf->len); - - /* note that the data from the UI slave includes the nul byte */ - esc = META_MESSAGE_ESCAPE; - - esc_pos = -1; - p = uislave->buf->str; - while (p != (uislave->buf->str + uislave->buf->len) && - esc_pos < META_MESSAGE_ESCAPE_LEN) - { - ++esc_pos; - if (*p != esc[esc_pos]) - esc_pos = -1; - - ++p; - } - - if (esc_pos == META_MESSAGE_ESCAPE_LEN) - { - /* We found an entire escape sequence; can safely toss - * out the entire buffer before it - */ - int ignored; - - ignored = p - uislave->buf->str; - ignored -= META_MESSAGE_ESCAPE_LEN; - - g_assert (ignored >= 0); - - if (ignored > 0) - { - g_string_erase (uislave->buf, 0, ignored); - meta_verbose ("Ignoring %d bytes before escape, new buffer len %d\n", - ignored, uislave->buf->len); - } - else - { - g_assert (p == (uislave->buf->str + META_MESSAGE_ESCAPE_LEN)); - } - } - else if (esc_pos < 0) - { - /* End of buffer doesn't begin an escape sequence; - * toss out entire buffer. - */ - meta_verbose ("Emptying %d-byte buffer not containing escape sequence\n", - uislave->buf->len); - g_string_truncate (uislave->buf, 0); - goto need_more_data; - } - else - { - meta_verbose ("Buffer ends with partial escape sequence: '%s'\n", - uislave->buf->str + (uislave->buf->len - esc_pos)); - goto need_more_data; - } - - g_assert (strcmp (uislave->buf->str, META_MESSAGE_ESCAPE) == 0); - - if (uislave->buf->len < (META_MESSAGE_ESCAPE_LEN + sizeof (MetaMessageHeader))) - { - meta_verbose ("Buffer has full escape sequence but lacks header\n"); - goto need_more_data; - } - - g_string_erase (uislave->buf, 0, META_MESSAGE_ESCAPE_LEN); - meta_verbose ("Stripped escape off front of buffer, new buffer len %d\n", - uislave->buf->len); - - g_assert (uislave->buf->len >= sizeof (MetaMessageHeader)); - - memcpy (&header, uislave->buf->str, sizeof (MetaMessageHeader)); - - /* Length includes the header even though it's in the header. */ - meta_verbose ("Read header, code: %d length: %d serial: %d\n", - header.message_code, header.length, header.serial); - - if (header.serial != uislave->last_serial + 1) - meta_bug ("Wrong message serial number %d from UI slave!\n", header.serial); - - uislave->last_serial = header.serial; - uislave->current_required_len = header.length; - - append_pending (uislave); - } - else - goto need_more_data; - } - - need_more_data: - return; -} - -static gboolean -mq_messages_pending (MetaUISlave *uislave) -{ - return uislave->queue->length > 0 || - uislave->buf->len > 0 || - uislave->current_message->len > 0; -} - -static gboolean -mq_prepare (GSource *source, gint *timeout) -{ - MetaUISlave *uislave; - - uislave = (MetaUISlave*) source; - - *timeout = -1; - - mq_queue_messages (uislave); - - return mq_messages_pending (uislave); -} - -static gboolean -mq_check (GSource *source) -{ - MetaUISlave *uislave; - - uislave = (MetaUISlave*) source; - - mq_queue_messages (uislave); - uislave->out_poll.revents = 0; - - return mq_messages_pending (uislave); -} - -static gboolean -mq_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) -{ - MetaUISlave *uislave; - - uislave = (MetaUISlave*) source; - - if (uislave->queue->length > 0) - { - MetaUISlaveFunc func; - MetaMessage *msg; - static int count = 0; - - ++count; - - msg = g_queue_pop_head (uislave->queue); - func = (MetaUISlaveFunc) callback; - - (* func) (uislave, msg, user_data); - - meta_verbose ("%d messages dispatched\n", count); - - g_free (msg); - } - - return TRUE; -} - 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_poll.fd >= 0) - { - g_source_remove_poll ((GSource*)uislave, &uislave->out_poll); - close (uislave->out_poll.fd); - } + if (uislave->out_pipe >= 0) + close (uislave->out_pipe); if (uislave->in_pipe >= 0) close (uislave->in_pipe); @@ -487,21 +206,6 @@ kill_child (MetaUISlave *uislave) if (uislave->err_pipe >= 0) close (uislave->err_pipe); - while (uislave->queue->length > 0) - { - MetaMessage *msg; - - msg = g_queue_pop_head (uislave->queue); - - g_free (msg); - } - - if (uislave->buf->len > 0) - g_string_truncate (uislave->buf, 0); - - if (uislave->current_message->len > 0) - g_string_truncate (uislave->current_message, 0); - if (uislave->child_pid > 0) { /* don't care if this fails except in verbose mode */ @@ -520,67 +224,84 @@ kill_child (MetaUISlave *uislave) static void reset_vals (MetaUISlave *uislave) { + uislave->mq = NULL; uislave->child_pid = 0; uislave->in_pipe = -1; uislave->err_pipe = -1; - uislave->out_poll.fd = -1; - uislave->no_respawn = FALSE; + uislave->out_pipe = -1; uislave->err_channel = NULL; uislave->errwatch = 0; - uislave->current_required_len = 0; - uislave->last_serial = -1; + /* 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 -mq_destroy (GSource *source) +send_message (MetaUISlave *uislave, MetaMessage *message) { - MetaUISlave *uislave; - - uislave = (MetaUISlave*) source; - - meta_verbose ("Deleting UI slave for display '%s'\n", - uislave->display_name); + static int serial = 0; + MetaMessageFooter *footer; - kill_child (uislave); - - g_string_free (uislave->buf, TRUE); - g_string_free (uislave->current_message, TRUE); + message->header.serial = serial; + footer = META_MESSAGE_FOOTER (message); - g_queue_free (uislave->queue); - - g_free (uislave->display_name); + footer->checksum = META_MESSAGE_CHECKSUM (message); + ++serial; - /* source itself is freed by glib */ + 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)); } -static ReadResult -read_data (GString *str, - gint fd) +void +meta_ui_slave_show_tip (MetaUISlave *uislave, + int root_x, + int root_y, + const char *markup_text) { -#define BUFSIZE 4000 - gint bytes; - gchar buf[BUFSIZE]; + MetaMessageShowTip showtip; - again: - - bytes = read (fd, &buf, BUFSIZE); + memset (&showtip, 0, META_MESSAGE_LENGTH (MetaMessageShowTip)); + showtip.header.message_code = MetaMessageShowTipCode; + showtip.header.length = META_MESSAGE_LENGTH (MetaMessageShowTip); - if (bytes == 0) - return READ_EOF; - else if (bytes > 0) - { - g_string_append_len (str, buf, bytes); - return READ_OK; - } - else if (bytes < 0 && errno == EINTR) - goto again; - else if (bytes < 0) - { - meta_warning (_("Failed to read data from UI slave: %s\n"), - g_strerror (errno)); - - return READ_FAILED; - } - else - return READ_OK; + 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); } diff --git a/src/uislave.h b/src/uislave.h index 9b959b64e..09c59dd6f 100644 --- a/src/uislave.h +++ b/src/uislave.h @@ -24,6 +24,7 @@ #include "util.h" #include "uislave/messages.h" +#include "messagequeue.h" #include "display.h" typedef void (* MetaUISlaveFunc) (MetaUISlave *uislave, @@ -32,20 +33,19 @@ typedef void (* MetaUISlaveFunc) (MetaUISlave *uislave, struct _MetaUISlave { - GSource source; - char *display_name; int child_pid; int in_pipe; + int out_pipe; int err_pipe; - GPollFD out_poll; GIOChannel *err_channel; unsigned int errwatch; - GQueue *queue; - GString *buf; - GString *current_message; - int current_required_len; - int last_serial; + + MetaMessageQueue *mq; + + MetaUISlaveFunc func; + gpointer data; + /* if we determine that our available slave is hosed, * set this bit. */ @@ -58,5 +58,9 @@ MetaUISlave* meta_ui_slave_new (const char *display_name, void meta_ui_slave_free (MetaUISlave *uislave); void meta_ui_slave_disable (MetaUISlave *uislave); +void meta_ui_slave_show_tip (MetaUISlave *uislave, + int root_x, + int root_y, + const char *markup_text); #endif diff --git a/src/uislave/.cvsignore b/src/uislave/.cvsignore new file mode 100644 index 000000000..603f53e80 --- /dev/null +++ b/src/uislave/.cvsignore @@ -0,0 +1,5 @@ +Makefile.in +Makefile +.libs +.deps +metacity-uislave diff --git a/src/uislave/Makefile.am b/src/uislave/Makefile.am index 89b6f26fb..b70f94635 100644 --- a/src/uislave/Makefile.am +++ b/src/uislave/Makefile.am @@ -2,6 +2,8 @@ INCLUDES=@UISLAVE_CFLAGS@ -DHOST_ALIAS=\"@HOST_ALIAS@\" metacity_uislave_SOURCES = \ + fixedtip.h \ + fixedtip.c \ main.c \ messages.c \ messages.h \ diff --git a/src/uislave/fixedtip.c b/src/uislave/fixedtip.c new file mode 100644 index 000000000..bb12e405b --- /dev/null +++ b/src/uislave/fixedtip.c @@ -0,0 +1,78 @@ +/* Metacity fixed tooltip routine */ + +/* + * 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 "fixedtip.h" + +static GtkWidget *tip = NULL; +static GtkWidget *label = NULL; + +static gint +expose_handler (GtkTooltips *tooltips) +{ + gtk_paint_flat_box (tip->style, tip->window, + GTK_STATE_NORMAL, GTK_SHADOW_OUT, + NULL, tip, "tooltip", + 0, 0, -1, -1); + + return FALSE; +} + +void +meta_fixed_tip_show (int root_x, int root_y, + const char *markup_text) +{ + if (tip == NULL) + { + tip = gtk_window_new (GTK_WINDOW_POPUP); + gtk_widget_set_app_paintable (tip, TRUE); + gtk_window_set_policy (GTK_WINDOW (tip), FALSE, FALSE, TRUE); + gtk_widget_set_name (tip, "gtk-tooltips"); + gtk_container_set_border_width (GTK_CONTAINER (tip), 4); + + gtk_signal_connect_object (GTK_OBJECT (tip), + "expose_event", + GTK_SIGNAL_FUNC (expose_handler), + NULL); + + label = gtk_label_new (NULL); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5); + gtk_widget_show (label); + + gtk_container_add (GTK_CONTAINER (tip), label); + + gtk_signal_connect (GTK_OBJECT (tip), + "destroy", + GTK_SIGNAL_FUNC (gtk_widget_destroyed), + &tip); + } + + gtk_widget_set_uposition (tip, root_x, root_y); + gtk_label_set_markup (GTK_LABEL (label), markup_text); + + gtk_widget_show (tip); +} + +void +meta_fixed_tip_hide (void) +{ + gtk_widget_destroy (tip); +} diff --git a/src/uislave/fixedtip.h b/src/uislave/fixedtip.h new file mode 100644 index 000000000..4b637fc6e --- /dev/null +++ b/src/uislave/fixedtip.h @@ -0,0 +1,32 @@ +/* Metacity fixed tooltip routine */ + +/* + * 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. + */ + +#ifndef META_FIXED_TIP_H +#define META_FIXED_TIP_H + +#include + +void meta_fixed_tip_show (int root_x, int root_y, + const char *markup_text); +void meta_fixed_tip_hide (void); + + +#endif diff --git a/src/uislave/main.c b/src/uislave/main.c index 13d7efcb8..fc4f9ae1e 100644 --- a/src/uislave/main.c +++ b/src/uislave/main.c @@ -20,6 +20,9 @@ */ #include "messages.h" +#include "messagequeue.h" +#include "fixedtip.h" +#include "main.h" #include #include @@ -27,6 +30,47 @@ #include +static void message_callback (MetaMessageQueue *mq, + MetaMessage *message, + gpointer data); + +int +main (int argc, char **argv) +{ + MetaMessageQueue *mq; + + /* report our nature to the window manager */ + meta_message_send_check (); + + gtk_init (&argc, &argv); + + mq = meta_message_queue_new (0, message_callback, NULL); + + gtk_main (); + + return 0; +} + +static void +message_callback (MetaMessageQueue *mq, + MetaMessage *message, + gpointer data) +{ + switch (message->header.message_code) + { + case MetaMessageShowTipCode: + meta_fixed_tip_show (message->show_tip.root_x, + message->show_tip.root_y, + message->show_tip.markup); + break; + + default: + meta_ui_warning ("Unhandled message code %d\n", + message->header.message_code); + break; + } +} + void meta_ui_warning (const char *format, ...) { @@ -44,22 +88,16 @@ meta_ui_warning (const char *format, ...) g_free (str); } -int -main (int argc, char **argv) + +#if 0 { int i; - - /* report our nature to the window manager */ - meta_message_send_check (); - write (1, "abcdefghijklmnopqrstuvwxyz", 27); - -#if 1 /* Try breaking message queue system. */ i = 0; while (i < 1500) { meta_message_send_check (); - write (1, "abcdefghijklmnopqrstuvwxyz", 27); + if (g_random_boolean ()) { int j; @@ -79,18 +117,6 @@ main (int argc, char **argv) ++i; } -#endif - - gtk_init (&argc, &argv); - - gtk_main (); - - return 0; } - - - - - - +#endif diff --git a/src/uislave/messagequeue.c b/src/uislave/messagequeue.c index 20fd7556a..da9e69a8e 100644 --- a/src/uislave/messagequeue.c +++ b/src/uislave/messagequeue.c @@ -20,19 +20,458 @@ */ #include "messagequeue.h" +#include +#include +#include +#include + +#ifdef METACITY_COMPILE +#include "util.h" +#else +#include "main.h" +void +meta_debug_spew (const char *format, ...) +{ +} + +void +meta_verbose (const char *format, ...) +{ +} + +void +meta_bug (const char *format, ...) +{ + /* stop us in a debugger */ + abort (); +} + +void +meta_warning (const char *format, ...) +{ +} + +void +meta_fatal (const char *format, ...) +{ + exit (1); +} +#endif /* !METACITY_COMPILE */ + +typedef enum +{ + READ_FAILED = 0, /* FALSE */ + READ_OK, + READ_EOF +} ReadResult; + +static ReadResult read_data (GString *str, + gint fd); + +/* Message queue main loop source */ +static gboolean mq_prepare (GSource *source, + gint *timeout); +static gboolean mq_check (GSource *source); +static gboolean mq_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data); +static void mq_destroy (GSource *source); + +static GSourceFuncs mq_funcs = { + mq_prepare, + mq_check, + mq_dispatch, + mq_destroy +}; + +struct _MetaMessageQueue +{ + GSource source; + + GPollFD out_poll; + GQueue *queue; + GString *buf; + GString *current_message; + int current_required_len; + int last_serial; +}; MetaMessageQueue* meta_message_queue_new (int fd, MetaMessageQueueFunc func, gpointer data) { + MetaMessageQueue *mq; + GSource *source; + source = g_source_new (&mq_funcs, sizeof (MetaMessageQueue)); + mq = (MetaMessageQueue*) source; + + mq->queue = g_queue_new (); + mq->buf = g_string_new (""); + mq->current_message = g_string_new (""); + mq->current_required_len = 0; + mq->last_serial = -1; + mq->out_poll.fd = fd; + mq->out_poll.events = G_IO_IN; + + g_source_add_poll (source, &mq->out_poll); + + g_source_set_priority (source, G_PRIORITY_DEFAULT); + g_source_set_can_recurse (source, TRUE); + + g_source_set_callback (source, (GSourceFunc) func, data, NULL); + + g_source_attach (source, NULL); + + return mq; } void meta_message_queue_free (MetaMessageQueue *mq) { - + GSource *source; + source = (GSource*) mq; + + g_source_destroy (source); +} + +static void +append_pending (MetaMessageQueue *mq) +{ + int needed; + + needed = mq->current_required_len - mq->current_message->len; + g_assert (needed >= 0); + + needed = MIN (needed, mq->buf->len); + + /* Move data from buf to current_message */ + if (needed > 0) + { + meta_verbose ("Moving %d bytes from buffer to current incomplete message\n", + needed); + g_string_append_len (mq->current_message, + mq->buf->str, + needed); + g_string_erase (mq->buf, + 0, needed); + } + + g_assert (mq->current_message->len <= mq->current_required_len); + + if (mq->current_required_len > 0 && + mq->current_message->len == mq->current_required_len) + { + MetaMessage *message; + MetaMessageFooter *footer; + + message = g_new (MetaMessage, 1); + + memcpy (message, + mq->current_message->str, mq->current_message->len); + + if (message->header.length != mq->current_required_len) + meta_bug ("Message length changed?\n"); + + if (message->header.serial != mq->last_serial) + meta_bug ("Message serial changed?\n"); + + footer = META_MESSAGE_FOOTER (message); + + if (footer->checksum == META_MESSAGE_CHECKSUM (message)) + { + g_queue_push_tail (mq->queue, message); + + meta_verbose ("Added %d-byte message serial %d to queue\n", + mq->current_message->len, message->header.serial); + } + else + { + meta_bug ("Bad checksum %d on %d-byte message from UI slave\n", + footer->checksum, mq->current_message->len); + } + + mq->current_required_len = 0; + g_string_truncate (mq->current_message, 0); + } + else if (mq->current_required_len > 0) + { + meta_verbose ("Storing %d bytes of incomplete message\n", + mq->current_message->len); + } +} + +static void +mq_queue_messages (MetaMessageQueue *mq) +{ + if (mq->out_poll.revents & G_IO_IN) + { + ReadResult res; + + res = read_data (mq->buf, mq->out_poll.fd); + + switch (res) + { + case READ_OK: + meta_verbose ("Read data from slave, %d bytes in buffer\n", + mq->buf->len); + break; + case READ_EOF: + meta_verbose ("EOF reading stdout from slave process\n"); + break; + + case READ_FAILED: + /* read_data printed the error */ + break; + } + } + + while (mq->buf->len > 0) + { + if (mq->current_required_len > 0) + { + /* We had a pending message. */ + append_pending (mq); + } + else if (mq->buf->len > META_MESSAGE_ESCAPE_LEN) + { + /* See if we can start a current message */ + const char *p; + int esc_pos; + const char *esc; + MetaMessageHeader header; + + g_assert (mq->current_required_len == 0); + g_assert (mq->current_message->len == 0); + + meta_verbose ("Scanning for escape sequence in %d bytes\n", + mq->buf->len); + + /* note that the data from the UI slave includes the nul byte */ + esc = META_MESSAGE_ESCAPE; + + esc_pos = -1; + p = mq->buf->str; + while (p != (mq->buf->str + mq->buf->len)) + { + if (*p == *esc) + { + esc_pos = 0; + while (*p == esc[esc_pos]) + { + ++esc_pos; + ++p; + + if (esc_pos == META_MESSAGE_ESCAPE_LEN || + p == (mq->buf->str + mq->buf->len)) + goto out; + } + + esc_pos = -1; + } + else + { + ++p; + } + } + + out: + if (esc_pos == META_MESSAGE_ESCAPE_LEN) + { + /* We found an entire escape sequence; can safely toss + * out the entire buffer before it + */ + int ignored; + + g_assert (esc[META_MESSAGE_ESCAPE_LEN-1] == *(p-1)); + + ignored = p - mq->buf->str; + ignored -= META_MESSAGE_ESCAPE_LEN; + + g_assert (ignored >= 0); + g_assert (mq->buf->str[ignored] == esc[0]); + + if (ignored > 0) + { + g_string_erase (mq->buf, 0, ignored); + meta_verbose ("Ignoring %d bytes before escape, new buffer len %d\n", + ignored, mq->buf->len); + } + else + { + g_assert (p == (mq->buf->str + META_MESSAGE_ESCAPE_LEN)); + } + } + else if (esc_pos < 0) + { + /* End of buffer doesn't begin an escape sequence; + * toss out entire buffer. + */ + meta_verbose ("Emptying %d-byte buffer not containing escape sequence\n", + mq->buf->len); + g_string_truncate (mq->buf, 0); + goto need_more_data; + } + else + { + meta_verbose ("Buffer ends with partial escape sequence\n"); + goto need_more_data; + } + + g_assert (strcmp (mq->buf->str, META_MESSAGE_ESCAPE) == 0); + + if (mq->buf->len < (META_MESSAGE_ESCAPE_LEN + sizeof (MetaMessageHeader))) + { + meta_verbose ("Buffer has full escape sequence but lacks header\n"); + goto need_more_data; + } + + g_string_erase (mq->buf, 0, META_MESSAGE_ESCAPE_LEN); + meta_verbose ("Stripped escape off front of buffer, new buffer len %d\n", + mq->buf->len); + + g_assert (mq->buf->len >= sizeof (MetaMessageHeader)); + + memcpy (&header, mq->buf->str, sizeof (MetaMessageHeader)); + + /* Length includes the header even though it's in the header. */ + meta_verbose ("Read header, code: %d length: %d serial: %d\n", + header.message_code, header.length, header.serial); + + if (header.serial != mq->last_serial + 1) + meta_bug ("Wrong message serial number %d from UI slave!\n", header.serial); + + mq->last_serial = header.serial; + mq->current_required_len = header.length; + + append_pending (mq); + } + else + goto need_more_data; + } + + need_more_data: + return; +} + +static gboolean +mq_messages_pending (MetaMessageQueue *mq) +{ + return mq->queue->length > 0 || + mq->buf->len > 0 || + mq->current_message->len > 0; +} + +static gboolean +mq_prepare (GSource *source, gint *timeout) +{ + MetaMessageQueue *mq; + + mq = (MetaMessageQueue*) source; + + *timeout = -1; + + mq_queue_messages (mq); + + return mq_messages_pending (mq); +} + +static gboolean +mq_check (GSource *source) +{ + MetaMessageQueue *mq; + + mq = (MetaMessageQueue*) source; + + mq_queue_messages (mq); + mq->out_poll.revents = 0; + + return mq_messages_pending (mq); +} + +static gboolean +mq_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) +{ + MetaMessageQueue *mq; + + mq = (MetaMessageQueue*) source; + + if (mq->queue->length > 0) + { + MetaMessageQueueFunc func; + MetaMessage *msg; + static int count = 0; + + ++count; + + msg = g_queue_pop_head (mq->queue); + func = (MetaMessageQueueFunc) callback; + + (* func) (mq, msg, user_data); + + meta_verbose ("%d messages dispatched\n", count); + + g_free (msg); + } + + return TRUE; +} + +static void +mq_destroy (GSource *source) +{ + MetaMessageQueue *mq; + + mq = (MetaMessageQueue*) source; + + while (mq->queue->length > 0) + { + MetaMessage *msg; + + msg = g_queue_pop_head (mq->queue); + + g_free (msg); + } + + g_string_free (mq->buf, TRUE); + g_string_free (mq->current_message, TRUE); + + g_queue_free (mq->queue); + + /* source itself is freed by glib */ +} + +static ReadResult +read_data (GString *str, + gint fd) +{ +#define BUFSIZE 1024 + gint bytes; + gchar buf[BUFSIZE]; + + again: + + bytes = read (fd, &buf, BUFSIZE); + + if (bytes == 0) + return READ_EOF; + else if (bytes > 0) + { + g_string_append_len (str, buf, bytes); + return READ_OK; + } + else if (bytes < 0 && errno == EINTR) + goto again; + else if (bytes < 0) + { + meta_warning (_("Failed to read data from UI slave: %s\n"), + g_strerror (errno)); + + return READ_FAILED; + } + else + return READ_OK; } diff --git a/src/uislave/messagequeue.h b/src/uislave/messagequeue.h index 6d38de7e7..763716f92 100644 --- a/src/uislave/messagequeue.h +++ b/src/uislave/messagequeue.h @@ -36,18 +36,6 @@ typedef void (* MetaMessageQueueFunc) (MetaMessageQueue *mq, MetaMessage *message, gpointer data); -struct _MetaMessageQueue -{ - GSource source; - - GPollFD out_poll; - GQueue *queue; - GString *buf; - GString *current_message; - int current_required_len; - int last_serial; -}; - MetaMessageQueue* meta_message_queue_new (int fd, MetaMessageQueueFunc func, gpointer data); diff --git a/src/uislave/messages.c b/src/uislave/messages.c index 1fb1df472..fe3eb16cb 100644 --- a/src/uislave/messages.c +++ b/src/uislave/messages.c @@ -42,9 +42,6 @@ static ReadResult read_data (GString *str, static void send_message (MetaMessage *message); -#define META_MESSAGE_LENGTH(real_type) \ - (G_STRUCT_OFFSET (real_type, footer) + sizeof (MetaMessageFooter)) - void meta_message_send_check (void) { @@ -53,10 +50,13 @@ meta_message_send_check (void) memset (&check, 0, META_MESSAGE_LENGTH (MetaMessageCheck)); check.header.message_code = MetaMessageCheckCode; check.header.length = META_MESSAGE_LENGTH (MetaMessageCheck); - strcpy (check.metacity_version, VERSION); - strcpy (check.host_alias, HOST_ALIAS); + + strncpy (check.metacity_version, VERSION, META_MESSAGE_MAX_VERSION_LEN); check.metacity_version[META_MESSAGE_MAX_VERSION_LEN] = '\0'; + + strncpy (check.host_alias, HOST_ALIAS, META_MESSAGE_MAX_HOST_ALIAS_LEN); check.host_alias[META_MESSAGE_MAX_HOST_ALIAS_LEN] = '\0'; + check.messages_version = META_MESSAGES_VERSION; send_message ((MetaMessage*)&check); diff --git a/src/uislave/messages.h b/src/uislave/messages.h index 50fa3a915..c4fadd21e 100644 --- a/src/uislave/messages.h +++ b/src/uislave/messages.h @@ -51,12 +51,14 @@ /* This is totally useless of course. Playing around. */ #define META_MESSAGE_CHECKSUM(msg) ((msg)->header.length | (msg)->header.serial << 16) #define META_MESSAGE_FOOTER(msg) ((MetaMessageFooter*) (((char*)(msg)) + ((msg)->header.length - sizeof (MetaMessageFooter)))); +#define META_MESSAGE_LENGTH(real_type) \ + (G_STRUCT_OFFSET (real_type, footer) + sizeof (MetaMessageFooter)) #define META_MESSAGE_MAX_SIZE (sizeof(MetaMessage)); #define META_MESSAGE_MAX_VERSION_LEN 15 #define META_MESSAGE_MAX_HOST_ALIAS_LEN 50 -#define META_MESSAGE_MAX_TIP_TEXT 128 +#define META_MESSAGE_MAX_TIP_LEN 128 typedef union _MetaMessage MetaMessage; typedef struct _MetaMessageHeader MetaMessageHeader; @@ -111,7 +113,7 @@ struct _MetaMessageShowTip MetaMessageHeader header; int root_x; int root_y; - char markup[META_MESSAGE_MAX_TIP_TEXT]; + char markup[META_MESSAGE_MAX_TIP_LEN + 1]; MetaMessageFooter footer; };