This commit is contained in:
rhp 2001-06-03 07:41:36 +00:00
parent c82a582a94
commit 11fde3a11a
20 changed files with 878 additions and 434 deletions

6
po/.cvsignore Normal file
View File

@ -0,0 +1,6 @@
Makefile.in.in
POTFILES
Makefile.in
Makefile
stamp-cat-id
cat-id-tbl.c

7
src/.cvsignore Normal file
View File

@ -0,0 +1,7 @@
.libs
Makefile.in
Makefile
.deps
metacity
messagequeue.h
messagequeue.c

View File

@ -18,7 +18,22 @@ BUILT_SOURCES=$(copied_sources)
$(copied_sources): $(copied_sources_deps) $(copied_sources): $(copied_sources_deps)
for I in $(copied_sources); do \ for I in $(copied_sources); do \
rm -f $$I ; \ 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 done
metacity_SOURCES= \ metacity_SOURCES= \

View File

@ -356,7 +356,7 @@ event_queue_callback (MetaEventQueue *queue,
case ReparentNotify: case ReparentNotify:
break; break;
case ConfigureNotify: case ConfigureNotify:
if (event->xconfigure.override_redirect) if (window && event->xconfigure.override_redirect)
{ {
/* Unmanage it, override_redirect was toggled on? /* Unmanage it, override_redirect was toggled on?
* Can this happen? * Can this happen?

78
src/fixedtip.c Normal file
View File

@ -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);
}

32
src/fixedtip.h Normal file
View File

@ -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 <gtk/gtk.h>
void meta_fixed_tip_show (int root_x, int root_y,
const char *markup_text);
void meta_fixed_tip_hide (void);
#endif

View File

@ -21,6 +21,7 @@
#include "frame.h" #include "frame.h"
#include "errors.h" #include "errors.h"
#include "uislave.h"
static void static void
meta_frame_init_info (MetaFrame *frame, meta_frame_init_info (MetaFrame *frame,
@ -518,6 +519,12 @@ meta_frame_event (MetaFrame *frame,
update_resize_se (frame); update_resize_se (frame);
break; 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: default:
break; break;
} }

View File

@ -32,6 +32,8 @@ static GMainLoop *meta_main_loop = NULL;
int int
main (int argc, char **argv) main (int argc, char **argv)
{ {
g_set_prgname (PACKAGE);
meta_main_loop = g_main_loop_new (NULL, FALSE); meta_main_loop = g_main_loop_new (NULL, FALSE);
meta_set_verbose (TRUE); meta_set_verbose (TRUE);

View File

@ -35,14 +35,14 @@ struct _MetaScreen
MetaThemeEngine *engine; MetaThemeEngine *engine;
MetaUISlave *uislave;
/*< private >*/ /*< private >*/
/* we only need one since we only draw to a single visual (that of /* we only need one since we only draw to a single visual (that of
* root window) * root window)
*/ */
PangoContext *pango_context; PangoContext *pango_context;
MetaUISlave *uislave;
}; };
MetaScreen* meta_screen_new (MetaDisplay *display, MetaScreen* meta_screen_new (MetaDisplay *display,

View File

@ -27,37 +27,16 @@
#include <signal.h> #include <signal.h>
#include <fcntl.h> #include <fcntl.h>
typedef enum
{
READ_FAILED = 0, /* FALSE */
READ_OK,
READ_EOF
} ReadResult;
static void respawn_child (MetaUISlave *uislave); static void respawn_child (MetaUISlave *uislave);
static gboolean error_callback (GIOChannel *source, static gboolean error_callback (GIOChannel *source,
GIOCondition condition, GIOCondition condition,
gpointer data); gpointer data);
static void kill_child (MetaUISlave *uislave); static void kill_child (MetaUISlave *uislave);
static void reset_vals (MetaUISlave *uislave); static void reset_vals (MetaUISlave *uislave);
static ReadResult read_data (GString *str, static void message_queue_func (MetaMessageQueue *mq,
gint fd); 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* MetaUISlave*
meta_ui_slave_new (const char *display_name, meta_ui_slave_new (const char *display_name,
@ -65,16 +44,13 @@ meta_ui_slave_new (const char *display_name,
gpointer data) gpointer data)
{ {
MetaUISlave *uislave; MetaUISlave *uislave;
GSource *source;
source = g_source_new (&mq_funcs, sizeof (MetaUISlave)); uislave = g_new (MetaUISlave, 1);
uislave = (MetaUISlave*) source;
uislave->display_name = g_strdup (display_name); uislave->display_name = g_strdup (display_name);
uislave->queue = g_queue_new (); uislave->func = func;
uislave->buf = g_string_new (""); uislave->data = data;
uislave->current_message = g_string_new (""); uislave->no_respawn = FALSE;
reset_vals (uislave); reset_vals (uislave);
@ -84,24 +60,21 @@ meta_ui_slave_new (const char *display_name,
*/ */
respawn_child (uislave); 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);
return uislave; return uislave;
} }
void void
meta_ui_slave_free (MetaUISlave *uislave) 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 void
@ -114,6 +87,18 @@ meta_ui_slave_disable (MetaUISlave *uislave)
uislave->no_respawn = TRUE; 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 static void
respawn_child (MetaUISlave *uislave) respawn_child (MetaUISlave *uislave)
{ {
@ -147,6 +132,7 @@ respawn_child (MetaUISlave *uislave)
uislave->child_pid = child_pid; uislave->child_pid = child_pid;
uislave->in_pipe = inpipe; uislave->in_pipe = inpipe;
uislave->err_pipe = errpipe; uislave->err_pipe = errpipe;
uislave->out_pipe = outpipe;
uislave->err_channel = g_io_channel_unix_new (errpipe); uislave->err_channel = g_io_channel_unix_new (errpipe);
@ -155,10 +141,9 @@ respawn_child (MetaUISlave *uislave)
error_callback, error_callback,
uislave); uislave);
uislave->out_poll.fd = outpipe; uislave->mq = meta_message_queue_new (outpipe,
uislave->out_poll.events = G_IO_IN; message_queue_func,
uislave);
g_source_add_poll ((GSource*)uislave, &uislave->out_poll);
meta_verbose ("Spawned UI slave with PID %d\n", uislave->child_pid); meta_verbose ("Spawned UI slave with PID %d\n", uislave->child_pid);
} }
@ -172,72 +157,6 @@ respawn_child (MetaUISlave *uislave)
g_free (envp[0]); 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 static gboolean
error_callback (GIOChannel *source, error_callback (GIOChannel *source,
GIOCondition condition, GIOCondition condition,
@ -266,220 +185,20 @@ error_callback (GIOChannel *source,
#undef BUFSIZE #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 static void
kill_child (MetaUISlave *uislave) kill_child (MetaUISlave *uislave)
{ {
if (uislave->mq)
meta_message_queue_free (uislave->mq);
if (uislave->errwatch != 0) if (uislave->errwatch != 0)
g_source_remove (uislave->errwatch); g_source_remove (uislave->errwatch);
if (uislave->err_channel) if (uislave->err_channel)
g_io_channel_unref (uislave->err_channel); g_io_channel_unref (uislave->err_channel);
if (uislave->out_poll.fd >= 0) if (uislave->out_pipe >= 0)
{ close (uislave->out_pipe);
g_source_remove_poll ((GSource*)uislave, &uislave->out_poll);
close (uislave->out_poll.fd);
}
if (uislave->in_pipe >= 0) if (uislave->in_pipe >= 0)
close (uislave->in_pipe); close (uislave->in_pipe);
@ -487,21 +206,6 @@ kill_child (MetaUISlave *uislave)
if (uislave->err_pipe >= 0) if (uislave->err_pipe >= 0)
close (uislave->err_pipe); 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) if (uislave->child_pid > 0)
{ {
/* don't care if this fails except in verbose mode */ /* don't care if this fails except in verbose mode */
@ -520,67 +224,84 @@ kill_child (MetaUISlave *uislave)
static void static void
reset_vals (MetaUISlave *uislave) reset_vals (MetaUISlave *uislave)
{ {
uislave->mq = NULL;
uislave->child_pid = 0; uislave->child_pid = 0;
uislave->in_pipe = -1; uislave->in_pipe = -1;
uislave->err_pipe = -1; uislave->err_pipe = -1;
uislave->out_poll.fd = -1; uislave->out_pipe = -1;
uislave->no_respawn = FALSE;
uislave->err_channel = NULL; uislave->err_channel = NULL;
uislave->errwatch = 0; uislave->errwatch = 0;
uislave->current_required_len = 0; /* don't reset no_respawn, it's a permanent thing. */
uislave->last_serial = -1; }
/*
* 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 static void
mq_destroy (GSource *source) send_message (MetaUISlave *uislave, MetaMessage *message)
{ {
MetaUISlave *uislave; static int serial = 0;
MetaMessageFooter *footer;
uislave = (MetaUISlave*) source; message->header.serial = serial;
footer = META_MESSAGE_FOOTER (message);
meta_verbose ("Deleting UI slave for display '%s'\n", footer->checksum = META_MESSAGE_CHECKSUM (message);
uislave->display_name); ++serial;
kill_child (uislave); if (write_bytes (uislave->in_pipe,
META_MESSAGE_ESCAPE, META_MESSAGE_ESCAPE_LEN) < 0)
g_string_free (uislave->buf, TRUE); meta_warning ("Failed to write escape sequence: %s\n",
g_string_free (uislave->current_message, TRUE); g_strerror (errno));
if (write_bytes (uislave->in_pipe,
g_queue_free (uislave->queue); message, message->header.length) < 0)
meta_warning ("Failed to write message: %s\n",
g_free (uislave->display_name);
/* source itself is freed by glib */
}
static ReadResult
read_data (GString *str,
gint fd)
{
#define BUFSIZE 4000
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)); g_strerror (errno));
}
return READ_FAILED;
} void
else meta_ui_slave_show_tip (MetaUISlave *uislave,
return READ_OK; 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);
} }

View File

@ -24,6 +24,7 @@
#include "util.h" #include "util.h"
#include "uislave/messages.h" #include "uislave/messages.h"
#include "messagequeue.h"
#include "display.h" #include "display.h"
typedef void (* MetaUISlaveFunc) (MetaUISlave *uislave, typedef void (* MetaUISlaveFunc) (MetaUISlave *uislave,
@ -32,20 +33,19 @@ typedef void (* MetaUISlaveFunc) (MetaUISlave *uislave,
struct _MetaUISlave struct _MetaUISlave
{ {
GSource source;
char *display_name; char *display_name;
int child_pid; int child_pid;
int in_pipe; int in_pipe;
int out_pipe;
int err_pipe; int err_pipe;
GPollFD out_poll;
GIOChannel *err_channel; GIOChannel *err_channel;
unsigned int errwatch; unsigned int errwatch;
GQueue *queue;
GString *buf; MetaMessageQueue *mq;
GString *current_message;
int current_required_len; MetaUISlaveFunc func;
int last_serial; gpointer data;
/* if we determine that our available slave is hosed, /* if we determine that our available slave is hosed,
* set this bit. * 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_free (MetaUISlave *uislave);
void meta_ui_slave_disable (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 #endif

5
src/uislave/.cvsignore Normal file
View File

@ -0,0 +1,5 @@
Makefile.in
Makefile
.libs
.deps
metacity-uislave

View File

@ -2,6 +2,8 @@
INCLUDES=@UISLAVE_CFLAGS@ -DHOST_ALIAS=\"@HOST_ALIAS@\" INCLUDES=@UISLAVE_CFLAGS@ -DHOST_ALIAS=\"@HOST_ALIAS@\"
metacity_uislave_SOURCES = \ metacity_uislave_SOURCES = \
fixedtip.h \
fixedtip.c \
main.c \ main.c \
messages.c \ messages.c \
messages.h \ messages.h \

78
src/uislave/fixedtip.c Normal file
View File

@ -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);
}

32
src/uislave/fixedtip.h Normal file
View File

@ -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 <gtk/gtk.h>
void meta_fixed_tip_show (int root_x, int root_y,
const char *markup_text);
void meta_fixed_tip_hide (void);
#endif

View File

@ -20,6 +20,9 @@
*/ */
#include "messages.h" #include "messages.h"
#include "messagequeue.h"
#include "fixedtip.h"
#include "main.h"
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@ -27,6 +30,47 @@
#include <gtk/gtk.h> #include <gtk/gtk.h>
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 void
meta_ui_warning (const char *format, ...) meta_ui_warning (const char *format, ...)
{ {
@ -44,22 +88,16 @@ meta_ui_warning (const char *format, ...)
g_free (str); g_free (str);
} }
int
main (int argc, char **argv) #if 0
{ {
int i; int i;
/* report our nature to the window manager */
meta_message_send_check ();
write (1, "abcdefghijklmnopqrstuvwxyz", 27);
#if 1
/* Try breaking message queue system. */ /* Try breaking message queue system. */
i = 0; i = 0;
while (i < 1500) while (i < 1500)
{ {
meta_message_send_check (); meta_message_send_check ();
write (1, "abcdefghijklmnopqrstuvwxyz", 27);
if (g_random_boolean ()) if (g_random_boolean ())
{ {
int j; int j;
@ -79,18 +117,6 @@ main (int argc, char **argv)
++i; ++i;
} }
}
#endif #endif
gtk_init (&argc, &argv);
gtk_main ();
return 0;
}

View File

@ -20,19 +20,458 @@
*/ */
#include "messagequeue.h" #include "messagequeue.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#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* MetaMessageQueue*
meta_message_queue_new (int fd, meta_message_queue_new (int fd,
MetaMessageQueueFunc func, MetaMessageQueueFunc func,
gpointer data) 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 void
meta_message_queue_free (MetaMessageQueue *mq) 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;
} }

View File

@ -36,18 +36,6 @@ typedef void (* MetaMessageQueueFunc) (MetaMessageQueue *mq,
MetaMessage *message, MetaMessage *message,
gpointer data); 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, MetaMessageQueue* meta_message_queue_new (int fd,
MetaMessageQueueFunc func, MetaMessageQueueFunc func,
gpointer data); gpointer data);

View File

@ -42,9 +42,6 @@ static ReadResult read_data (GString *str,
static void send_message (MetaMessage *message); static void send_message (MetaMessage *message);
#define META_MESSAGE_LENGTH(real_type) \
(G_STRUCT_OFFSET (real_type, footer) + sizeof (MetaMessageFooter))
void void
meta_message_send_check (void) meta_message_send_check (void)
{ {
@ -53,10 +50,13 @@ meta_message_send_check (void)
memset (&check, 0, META_MESSAGE_LENGTH (MetaMessageCheck)); memset (&check, 0, META_MESSAGE_LENGTH (MetaMessageCheck));
check.header.message_code = MetaMessageCheckCode; check.header.message_code = MetaMessageCheckCode;
check.header.length = META_MESSAGE_LENGTH (MetaMessageCheck); 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'; 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.host_alias[META_MESSAGE_MAX_HOST_ALIAS_LEN] = '\0';
check.messages_version = META_MESSAGES_VERSION; check.messages_version = META_MESSAGES_VERSION;
send_message ((MetaMessage*)&check); send_message ((MetaMessage*)&check);

View File

@ -51,12 +51,14 @@
/* This is totally useless of course. Playing around. */ /* This is totally useless of course. Playing around. */
#define META_MESSAGE_CHECKSUM(msg) ((msg)->header.length | (msg)->header.serial << 16) #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_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_SIZE (sizeof(MetaMessage));
#define META_MESSAGE_MAX_VERSION_LEN 15 #define META_MESSAGE_MAX_VERSION_LEN 15
#define META_MESSAGE_MAX_HOST_ALIAS_LEN 50 #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 union _MetaMessage MetaMessage;
typedef struct _MetaMessageHeader MetaMessageHeader; typedef struct _MetaMessageHeader MetaMessageHeader;
@ -111,7 +113,7 @@ struct _MetaMessageShowTip
MetaMessageHeader header; MetaMessageHeader header;
int root_x; int root_x;
int root_y; int root_y;
char markup[META_MESSAGE_MAX_TIP_TEXT]; char markup[META_MESSAGE_MAX_TIP_LEN + 1];
MetaMessageFooter footer; MetaMessageFooter footer;
}; };