summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorrhp <rhp>2001-06-03 01:33:27 +0000
committerrhp <rhp>2001-06-03 01:33:27 +0000
commitce7c53bf1a65d6c136f5bfe73cef5ac800b68c0b (patch)
tree1f530f06bdcf42f075ef4f4578a9fdbf5f2a50f7 /src
parente47c4d16a27aae7c3b8831b9855f25a9dfb8473f (diff)
downloadmetacity-ce7c53bf1a65d6c136f5bfe73cef5ac800b68c0b.tar.gz
...
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-rw-r--r--src/display.c183
-rw-r--r--src/display.h4
-rw-r--r--src/frame.c348
-rw-r--r--src/frame.h29
-rwxr-xr-xsrc/run-metacity.sh3
-rw-r--r--src/screen.c82
-rw-r--r--src/screen.h5
-rw-r--r--src/theme.c2
-rw-r--r--src/uislave.c505
-rw-r--r--src/uislave.h30
-rw-r--r--src/uislave/Makefile.am8
-rw-r--r--src/uislave/main.c66
-rw-r--r--src/uislave/messages.c123
-rw-r--r--src/uislave/messages.h115
-rw-r--r--src/util.h1
-rw-r--r--src/window.c332
-rw-r--r--src/window.h21
18 files changed, 1647 insertions, 212 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 186c476b..2254e6df 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,7 +1,7 @@
SUBDIRS=uislave
-INCLUDES=@METACITY_CFLAGS@
+INCLUDES=@METACITY_CFLAGS@ -DMETACITY_LIBEXECDIR=\"$(libexecdir)\" -DHOST_ALIAS=\"@HOST_ALIAS@\"
metacity_SOURCES= \
api.c \
diff --git a/src/display.c b/src/display.c
index a32c537d..95718f87 100644
--- a/src/display.c
+++ b/src/display.c
@@ -24,14 +24,18 @@
#include "main.h"
#include "screen.h"
#include "window.h"
+#include "frame.h"
static GSList *all_displays = NULL;
+static void meta_spew_event (MetaDisplay *display,
+ XEvent *event);
+static void event_queue_callback (MetaEventQueue *queue,
+ XEvent *event,
+ gpointer data);
+static Window event_get_modified_window (MetaDisplay *display,
+ XEvent *event);
+
-static void meta_spew_event (MetaDisplay *display,
- XEvent *event);
-static void event_queue_callback (MetaEventQueue *queue,
- XEvent *event,
- gpointer data);
static gint
@@ -63,7 +67,7 @@ meta_display_open (const char *name)
GSList *screens;
GSList *tmp;
int i;
- char *atom_names[] = { "_NET_WM_NAME" };
+ char *atom_names[] = { "_NET_WM_NAME", "WM_PROTOCOLS", "WM_TAKE_FOCUS", "WM_DELETE_WINDOW" };
Atom atoms[G_N_ELEMENTS(atom_names)];
meta_verbose ("Opening display '%s'\n", XDisplayName (name));
@@ -81,7 +85,11 @@ meta_display_open (const char *name)
XSynchronize (xdisplay, True);
display = g_new (MetaDisplay, 1);
-
+
+ /* here we use XDisplayName which is what the user
+ * probably put in, vs. DisplayString(display) which is
+ * canonicalized by XOpenDisplay()
+ */
display->name = g_strdup (XDisplayName (name));
display->xdisplay = xdisplay;
display->error_traps = NULL;
@@ -277,26 +285,26 @@ event_queue_callback (MetaEventQueue *queue,
{
MetaWindow *window;
MetaDisplay *display;
- gboolean is_root;
+ Window modified;
display = data;
if (dump_events)
meta_spew_event (display, event);
- is_root = meta_display_screen_for_root (display, event->xany.window) != NULL;
- window = NULL;
+ modified = event_get_modified_window (display, event);
+
+ if (modified != None)
+ window = meta_display_lookup_x_window (display, modified);
+ else
+ window = NULL;
- if (!is_root)
+ if (window &&
+ window->frame &&
+ modified == window->frame->xwindow)
{
- if (window == NULL)
- window = meta_display_lookup_x_window (display, event->xany.window);
-
- if (window != NULL)
- {
- if (meta_window_event (window, event))
- return;
- }
+ meta_frame_event (window->frame, event);
+ return;
}
switch (event->type)
@@ -332,28 +340,59 @@ event_queue_callback (MetaEventQueue *queue,
case CreateNotify:
break;
case DestroyNotify:
+ if (window)
+ meta_window_free (window); /* Unmanage destroyed window */
break;
case UnmapNotify:
+ if (window)
+ meta_window_free (window); /* Unmanage withdrawn window */
break;
case MapNotify:
break;
case MapRequest:
- if (is_root && !event->xmap.override_redirect)
- {
- /* Window requested mapping. Manage it if we haven't. Note that
- * meta_window_new() can return NULL
- */
- window = meta_display_lookup_x_window (display,
- event->xmaprequest.window);
- if (window == NULL)
- window = meta_window_new (display, event->xmaprequest.window);
- }
+ if (window == NULL)
+ window = meta_window_new (display, event->xmaprequest.window);
break;
case ReparentNotify:
break;
case ConfigureNotify:
+ if (event->xconfigure.override_redirect)
+ {
+ /* Unmanage it, override_redirect was toggled on?
+ * Can this happen?
+ */
+ meta_window_free (window);
+ }
break;
case ConfigureRequest:
+ /* This comment and code is found in both twm and fvwm */
+ /*
+ * According to the July 27, 1988 ICCCM draft, we should ignore size and
+ * position fields in the WM_NORMAL_HINTS property when we map a window.
+ * Instead, we'll read the current geometry. Therefore, we should respond
+ * to configuration requests for windows which have never been mapped.
+ */
+ if (window == NULL)
+ {
+ unsigned int xwcm;
+ XWindowChanges xwc;
+
+ xwcm = event->xconfigurerequest.value_mask &
+ (CWX | CWY | CWWidth | CWHeight | CWBorderWidth);
+
+ xwc.x = event->xconfigurerequest.x;
+ xwc.y = event->xconfigurerequest.y;
+ xwc.width = event->xconfigurerequest.width;
+ xwc.height = event->xconfigurerequest.height;
+ xwc.border_width = event->xconfigurerequest.border_width;
+
+ XConfigureWindow (display->xdisplay, event->xconfigurerequest.window,
+ xwcm, &xwc);
+ }
+ else
+ {
+ meta_window_configure_request (window, event);
+ }
break;
case GravityNotify:
break;
@@ -364,6 +403,8 @@ event_queue_callback (MetaEventQueue *queue,
case CirculateRequest:
break;
case PropertyNotify:
+ if (window)
+ meta_window_property_notify (window, event);
break;
case SelectionClear:
break;
@@ -379,9 +420,83 @@ event_queue_callback (MetaEventQueue *queue,
break;
default:
break;
- }
+ }
}
+/* Return the window this has to do with, if any, rather
+ * than the frame or root window that was selecting
+ * for substructure
+ */
+static Window
+event_get_modified_window (MetaDisplay *display,
+ XEvent *event)
+{
+ switch (event->type)
+ {
+ case KeyPress:
+ case KeyRelease:
+ case ButtonPress:
+ case ButtonRelease:
+ case MotionNotify:
+ case EnterNotify:
+ case LeaveNotify:
+ case FocusIn:
+ case FocusOut:
+ case KeymapNotify:
+ case Expose:
+ case GraphicsExpose:
+ case NoExpose:
+ case VisibilityNotify:
+ case ResizeRequest:
+ case PropertyNotify:
+ case SelectionClear:
+ case SelectionRequest:
+ case SelectionNotify:
+ case ColormapNotify:
+ case ClientMessage:
+ return event->xany.window;
+
+ case CreateNotify:
+ return event->xcreatewindow.window;
+
+ case DestroyNotify:
+ return event->xdestroywindow.window;
+
+ case UnmapNotify:
+ return event->xunmap.window;
+
+ case MapNotify:
+ return event->xmap.window;
+
+ case MapRequest:
+ return event->xmaprequest.window;
+
+ case ReparentNotify:
+ return event->xreparent.window;
+
+ case ConfigureNotify:
+ return event->xconfigure.window;
+
+ case ConfigureRequest:
+ return event->xconfigurerequest.window;
+
+ case GravityNotify:
+ return event->xgravity.window;
+
+ case CirculateNotify:
+ return event->xcirculate.window;
+
+ case CirculateRequest:
+ return event->xcirculaterequest.window;
+
+ case MappingNotify:
+ return None;
+
+ default:
+ return None;
+ }
+}
+
static void
meta_spew_event (MetaDisplay *display,
XEvent *event)
@@ -465,6 +580,14 @@ meta_spew_event (MetaDisplay *display,
break;
case ConfigureRequest:
name = "ConfigureRequest";
+ extra = g_strdup_printf ("parent: 0x%lx window: 0x%lx x: %d y: %d w: %d h: %d border: %d",
+ event->xconfigurerequest.parent,
+ event->xconfigurerequest.window,
+ event->xconfigurerequest.x,
+ event->xconfigurerequest.y,
+ event->xconfigurerequest.width,
+ event->xconfigurerequest.height,
+ event->xconfigurerequest.border_width);
break;
case GravityNotify:
name = "GravityNotify";
diff --git a/src/display.h b/src/display.h
index 755ae414..681e7619 100644
--- a/src/display.h
+++ b/src/display.h
@@ -31,6 +31,7 @@ typedef struct _MetaDisplay MetaDisplay;
typedef struct _MetaFrame MetaFrame;
typedef struct _MetaScreen MetaScreen;
typedef struct _MetaWindow MetaWindow;
+typedef struct _MetaUISlave MetaUISlave;
struct _MetaDisplay
{
@@ -38,6 +39,9 @@ struct _MetaDisplay
Display *xdisplay;
Atom atom_net_wm_name;
+ Atom atom_wm_protocols;
+ Atom atom_wm_take_focus;
+ Atom atom_wm_delete_window;
/*< private-ish >*/
MetaEventQueue *events;
diff --git a/src/frame.c b/src/frame.c
index bd22c412..6d4adf92 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -37,109 +37,145 @@ meta_frame_init_info (MetaFrame *frame,
info->height = frame->rect.height;
}
-void
-meta_window_ensure_frame (MetaWindow *window)
+static void
+meta_frame_calc_initial_pos (MetaFrame *frame,
+ int child_root_x, int child_root_y)
{
- MetaFrame *frame;
- int child_x, child_y;
- unsigned long background_pixel;
- XSetWindowAttributes attrs;
- MetaFrameInfo info;
- MetaFrameGeometry geom;
+ MetaWindow *window;
- if (window->frame)
- return;
-
- frame = g_new (MetaFrame, 1);
-
- frame->window = window;
-
- /* Fill these in for the theme engine's benefit */
- frame->xwindow = None;
- frame->rect.width = window->rect.width;
- frame->rect.height = window->rect.height;
-
- meta_frame_init_info (frame, &info);
-
- geom.left_width = 0;
- geom.right_width = 0;
- geom.top_height = 0;
- geom.bottom_height = 0;
- geom.background_pixel = BlackPixel (frame->window->display->xdisplay,
- frame->window->screen->number);
-
- geom.shape_mask = None;
-
- frame->theme_data = window->screen->engine->acquire_frame (&info);
- window->screen->engine->fill_frame_geometry (&info, &geom,
- frame->theme_data);
-
- child_x = geom.left_width;
- child_y = geom.top_height;
-
- frame->rect.width = window->rect.width + geom.left_width + geom.right_width;
- frame->rect.height = window->rect.height + geom.top_height + geom.bottom_height;
-
- background_pixel = geom.background_pixel;
+ window = frame->window;
switch (window->size_hints.win_gravity)
{
case NorthWestGravity:
- frame->rect.x = window->rect.x;
- frame->rect.y = window->rect.y;
+ frame->rect.x = child_root_x;
+ frame->rect.y = child_root_y;
break;
case NorthGravity:
- frame->rect.x = window->rect.x - frame->rect.width / 2;
- frame->rect.y = window->rect.y;
+ frame->rect.x = child_root_x - frame->rect.width / 2;
+ frame->rect.y = child_root_y;
break;
case NorthEastGravity:
- frame->rect.x = window->rect.x - frame->rect.width;
- frame->rect.y = window->rect.y;
+ frame->rect.x = child_root_x - frame->rect.width;
+ frame->rect.y = child_root_y;
break;
case WestGravity:
- frame->rect.x = window->rect.x;
- frame->rect.y = window->rect.y - frame->rect.height / 2;
+ frame->rect.x = child_root_x;
+ frame->rect.y = child_root_y - frame->rect.height / 2;
break;
case CenterGravity:
- frame->rect.x = window->rect.x - frame->rect.width / 2;
- frame->rect.y = window->rect.y - frame->rect.height / 2;
+ frame->rect.x = child_root_x - frame->rect.width / 2;
+ frame->rect.y = child_root_y - frame->rect.height / 2;
break;
case EastGravity:
- frame->rect.x = window->rect.x - frame->rect.width;
- frame->rect.y = window->rect.y - frame->rect.height / 2;
+ frame->rect.x = child_root_x - frame->rect.width;
+ frame->rect.y = child_root_y - frame->rect.height / 2;
break;
case SouthWestGravity:
- frame->rect.x = window->rect.x;
- frame->rect.y = window->rect.y - frame->rect.height;
+ frame->rect.x = child_root_x;
+ frame->rect.y = child_root_y - frame->rect.height;
break;
case SouthGravity:
- frame->rect.x = window->rect.x - frame->rect.width / 2;
- frame->rect.y = window->rect.y - frame->rect.height;
+ frame->rect.x = child_root_x - frame->rect.width / 2;
+ frame->rect.y = child_root_y - frame->rect.height;
break;
case SouthEastGravity:
- frame->rect.x = window->rect.x - frame->rect.width;
- frame->rect.y = window->rect.y - frame->rect.height;
+ frame->rect.x = child_root_x - frame->rect.width;
+ frame->rect.y = child_root_y - frame->rect.height;
break;
case StaticGravity:
default:
- frame->rect.x = window->rect.x - child_x;
- frame->rect.y = window->rect.y - child_y;
+ frame->rect.x = child_root_x - frame->child_x;
+ frame->rect.y = child_root_y - frame->child_y;
break;
}
+}
- meta_verbose ("Creating frame %d,%d %dx%d around window 0x%lx %d,%d %dx%d with child position inside frame %d,%d and gravity %d\n",
+static void
+meta_frame_calc_geometry (MetaFrame *frame,
+ int child_width, int child_height,
+ MetaFrameGeometry *geomp)
+{
+ MetaFrameInfo info;
+ MetaFrameGeometry geom;
+ MetaWindow *window;
+
+ /* Remember this is called from the constructor
+ * pre-window-creation.
+ */
+
+ window = frame->window;
+
+ frame->rect.width = child_width;
+ frame->rect.height = child_height;
+
+ meta_frame_init_info (frame, &info);
+
+ if (!frame->theme_acquired)
+ frame->theme_data = window->screen->engine->acquire_frame (&info);
+
+ geom.left_width = 0;
+ geom.right_width = 0;
+ geom.top_height = 0;
+ geom.bottom_height = 0;
+ geom.background_pixel = BlackPixel (window->display->xdisplay,
+ window->screen->number);
+
+ geom.shape_mask = None;
+
+ window->screen->engine->fill_frame_geometry (&info, &geom,
+ frame->theme_data);
+
+ frame->child_x = geom.left_width;
+ frame->child_y = geom.top_height;
+
+ frame->rect.width = frame->rect.width + geom.left_width + geom.right_width;
+ frame->rect.height = frame->rect.height + geom.top_height + geom.bottom_height;
+
+ *geomp = geom;
+}
+
+void
+meta_window_ensure_frame (MetaWindow *window)
+{
+ MetaFrame *frame;
+ XSetWindowAttributes attrs;
+ MetaFrameGeometry geom;
+
+ if (window->frame)
+ return;
+
+ /* Need to fix Pango, it grabs the server */
+ g_return_if_fail (window->display->server_grab_count == 0);
+
+ frame = g_new (MetaFrame, 1);
+
+ /* Fill in values that calc_geometry will use */
+ frame->window = window;
+ frame->xwindow = None;
+ frame->theme_acquired = FALSE;
+
+ /* This fills in frame->rect as well. */
+ meta_frame_calc_geometry (frame,
+ window->rect.width,
+ window->rect.height,
+ &geom);
+
+ meta_frame_calc_initial_pos (frame, window->rect.x, window->rect.y);
+
+ meta_verbose ("Will create frame %d,%d %dx%d around window %s %d,%d %dx%d with child position inside frame %d,%d and gravity %d\n",
frame->rect.x, frame->rect.y,
frame->rect.width, frame->rect.height,
- window->xwindow,
+ window->desc,
window->rect.x, window->rect.y,
window->rect.width, window->rect.height,
- child_x, child_y,
+ frame->child_x, frame->child_y,
window->size_hints.win_gravity);
-
- attrs.background_pixel = background_pixel;
+
+ attrs.background_pixel = geom.background_pixel;
attrs.event_mask =
- StructureNotifyMask | ExposureMask |
- ButtonPressMask | ButtonReleaseMask |
+ StructureNotifyMask | SubstructureNotifyMask | ExposureMask |
+ ButtonPressMask | ButtonReleaseMask | OwnerGrabButtonMask |
PointerMotionMask | PointerMotionHintMask;
frame->xwindow = XCreateWindow (window->display->xdisplay,
@@ -177,10 +213,14 @@ meta_window_ensure_frame (MetaWindow *window)
XReparentWindow (window->display->xdisplay,
window->xwindow,
frame->xwindow,
- child_x,
- child_y);
+ frame->child_x,
+ frame->child_y);
meta_error_trap_pop (window->display);
+ /* Update window's location */
+ window->rect.x = frame->child_x;
+ window->rect.y = frame->child_y;
+
/* stick frame to the window */
window->frame = frame;
@@ -246,6 +286,90 @@ meta_frame_move (MetaFrame *frame,
root_x, root_y);
}
+/* Just a chunk of process_configure_event in window.c,
+ * moved here since it's the part that deals with
+ * the frame.
+ */
+void
+meta_frame_child_configure_request (MetaFrame *frame)
+{
+ MetaFrameGeometry geom;
+
+ /* This fills in frame->rect as well. */
+ meta_frame_calc_geometry (frame,
+ frame->window->size_hints.width,
+ frame->window->size_hints.height,
+ &geom);
+
+ meta_frame_calc_initial_pos (frame,
+ frame->window->size_hints.x,
+ frame->window->size_hints.y);
+
+ XMoveResizeWindow (frame->window->display->xdisplay,
+ frame->xwindow,
+ frame->rect.x,
+ frame->rect.y,
+ frame->rect.width,
+ frame->rect.height);
+}
+
+void
+meta_frame_recalc_now (MetaFrame *frame)
+{
+ int old_child_x, old_child_y;
+ MetaFrameGeometry geom;
+ XSetWindowAttributes attrs;
+
+ old_child_x = frame->child_x;
+ old_child_y = frame->child_y;
+
+ /* This fills in frame->rect as well. */
+ meta_frame_calc_geometry (frame,
+ frame->window->rect.width,
+ frame->window->rect.height,
+ &geom);
+
+ /* See if we need to move the frame to keep child in
+ * a constant position
+ */
+ if (old_child_x != frame->child_x)
+ frame->rect.x += (frame->child_x - old_child_x);
+ if (old_child_y != frame->child_y)
+ frame->rect.y += (frame->child_y - old_child_y);
+
+ XMoveResizeWindow (frame->window->display->xdisplay,
+ frame->xwindow,
+ frame->rect.x,
+ frame->rect.y,
+ frame->rect.width,
+ frame->rect.height);
+
+ attrs.background_pixel = geom.background_pixel;
+ XChangeWindowAttributes (frame->window->display->xdisplay,
+ frame->xwindow,
+ CWBackPixel,
+ &attrs);
+
+ meta_verbose ("Frame of %s recalculated to %d,%d %d x %d child %d,%d\n",
+ frame->window->desc, frame->rect.x, frame->rect.y,
+ frame->rect.width, frame->rect.height,
+ frame->child_x, frame->child_y);
+}
+
+void
+meta_frame_queue_recalc (MetaFrame *frame)
+{
+ /* FIXME */
+ meta_frame_recalc_now (frame);
+}
+
+void
+meta_frame_queue_draw (MetaFrame *frame)
+{
+ /* FIXME */
+
+}
+
static void
frame_query_root_pointer (MetaFrame *frame,
int *x, int *y)
@@ -283,6 +407,37 @@ frame_get_control (MetaFrame *frame,
frame->theme_data);
}
+static void
+update_move (MetaFrame *frame)
+{
+ int x, y;
+ int new_x, new_y;
+ frame_query_root_pointer (frame, &x, &y);
+
+ new_x = frame->rect.x + (x - frame->last_x);
+ new_y = frame->rect.y + (y - frame->last_y);
+ frame->last_x = x;
+ frame->last_y = y;
+
+ meta_frame_move (frame, new_x, new_y);
+}
+
+static void
+update_resize_se (MetaFrame *frame)
+{
+ int x, y;
+ int new_w, new_h;
+
+ frame_query_root_pointer (frame, &x, &y);
+
+ new_w = frame->window->rect.width + (x - frame->last_x);
+ new_h = frame->window->rect.height + (y - frame->last_y);
+ frame->last_x = x;
+ frame->last_y = y;
+
+ meta_window_resize (frame->window, new_w, new_h);
+}
+
gboolean
meta_frame_event (MetaFrame *frame,
XEvent *event)
@@ -323,15 +478,32 @@ meta_frame_event (MetaFrame *frame,
else if (control == META_FRAME_CONTROL_RESIZE_SE &&
event->xbutton.button == 1)
{
- /* FIXME begin a resize */
meta_verbose ("Resize control clicked on %s\n",
frame->window->desc);
+ frame->action = META_FRAME_ACTION_RESIZING_SE;
+ frame->last_x = event->xbutton.x_root;
+ frame->last_y = event->xbutton.y_root;
+ frame->start_button = event->xbutton.button;
}
}
break;
case ButtonRelease:
if (event->xbutton.button == frame->start_button)
{
+ switch (frame->action)
+ {
+ case META_FRAME_ACTION_MOVING:
+ update_move (frame);
+ break;
+
+ case META_FRAME_ACTION_RESIZING_SE:
+ update_resize_se (frame);
+ break;
+
+ default:
+ break;
+ }
+
frame->action = META_FRAME_ACTION_NONE;
}
break;
@@ -339,20 +511,13 @@ meta_frame_event (MetaFrame *frame,
switch (frame->action)
{
case META_FRAME_ACTION_MOVING:
- {
- int x, y;
- int new_x, new_y;
- frame_query_root_pointer (frame, &x, &y);
-
- new_x = frame->rect.x + (x - frame->last_x);
- new_y = frame->rect.y + (y - frame->last_y);
- frame->last_x = x;
- frame->last_y = y;
-
- meta_frame_move (frame, new_x, new_y);
- }
+ update_move (frame);
break;
+ case META_FRAME_ACTION_RESIZING_SE:
+ update_resize_se (frame);
+ break;
+
default:
break;
}
@@ -388,11 +553,16 @@ meta_frame_event (MetaFrame *frame,
case CreateNotify:
break;
case DestroyNotify:
- meta_warning ("Unexpected destruction of frame 0x%lx, not sure if this should silently fail or be considered a bug\n", frame->xwindow);
- meta_error_trap_push (frame->window->display);
- meta_window_destroy_frame (frame->window);
- meta_error_trap_pop (frame->window->display);
- return TRUE;
+ {
+ MetaDisplay *display;
+
+ meta_warning ("Unexpected destruction of frame 0x%lx, not sure if this should silently fail or be considered a bug\n", frame->xwindow);
+ display = frame->window->display;
+ meta_error_trap_push (display);
+ meta_window_destroy_frame (frame->window);
+ meta_error_trap_pop (display);
+ return TRUE;
+ }
break;
case UnmapNotify:
frame->action = META_FRAME_ACTION_NONE;
diff --git a/src/frame.h b/src/frame.h
index e63f6f83..739a74f7 100644
--- a/src/frame.h
+++ b/src/frame.h
@@ -28,7 +28,7 @@ typedef enum
{
META_FRAME_ACTION_NONE,
META_FRAME_ACTION_MOVING,
- META_FRAME_ACTION_RESIZING
+ META_FRAME_ACTION_RESIZING_SE
} MetaFrameAction;
struct _MetaFrame
@@ -43,6 +43,8 @@ struct _MetaFrame
* frame, not the result of ConfigureNotify
*/
MetaRectangle rect;
+ int child_x;
+ int child_y;
gpointer theme_data;
@@ -50,16 +52,27 @@ struct _MetaFrame
/* reference point for drags */
int last_x, last_y;
int start_button;
+
+ guint theme_acquired : 1;
};
-void meta_window_ensure_frame (MetaWindow *window);
-void meta_window_destroy_frame (MetaWindow *window);
+void meta_window_ensure_frame (MetaWindow *window);
+void meta_window_destroy_frame (MetaWindow *window);
+void meta_frame_move (MetaFrame *frame,
+ int root_x,
+ int root_y);
+void meta_frame_child_configure_request (MetaFrame *frame);
+void meta_frame_recalc_now (MetaFrame *frame);
+void meta_frame_queue_recalc (MetaFrame *frame);
+void meta_frame_queue_draw (MetaFrame *frame);
+gboolean meta_frame_event (MetaFrame *frame,
+ XEvent *event);
+
-void meta_frame_move (MetaFrame *frame,
- int root_x,
- int root_y);
-gboolean meta_frame_event (MetaFrame *frame,
- XEvent *event);
#endif
+
+
+
+
diff --git a/src/run-metacity.sh b/src/run-metacity.sh
index 6dc2cc08..3e8d68c6 100755
--- a/src/run-metacity.sh
+++ b/src/run-metacity.sh
@@ -1,6 +1,5 @@
#! /bin/bash
Xnest :1 -scrns 2 -geometry 270x270 &
-sleep 1
-DISPLAY=:1 unst $1 ./metacity
+METACITY_UISLAVE_DIR=./uislave DISPLAY=:1 unst libtool --mode=execute gdb ./metacity
killall Xnest
diff --git a/src/screen.c b/src/screen.c
index afeb30fd..b51d21ac 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -24,11 +24,19 @@
#include "errors.h"
#include "window.h"
#include "colors.h"
+#include "uislave.h"
#include <X11/cursorfont.h>
#include <locale.h>
#include <string.h>
+static void ui_slave_func (MetaUISlave *uislave,
+ MetaMessage *message,
+ gpointer data);
+static char* get_screen_name (MetaDisplay *display,
+ int number);
+
+
MetaScreen*
meta_screen_new (MetaDisplay *display,
int number)
@@ -83,14 +91,19 @@ meta_screen_new (MetaDisplay *display,
screen->display = display;
screen->number = number;
+ screen->screen_name = get_screen_name (display, number);
screen->xscreen = ScreenOfDisplay (xdisplay, number);
screen->xroot = xroot;
screen->pango_context = NULL;
-
+
screen->engine = &meta_default_engine;
+
+ screen->uislave = meta_ui_slave_new (screen->screen_name,
+ ui_slave_func,
+ screen);
- meta_verbose ("Added screen %d on display '%s' root 0x%lx\n",
- screen->number, screen->display->name, screen->xroot);
+ meta_verbose ("Added screen %d ('%s') root 0x%lx\n",
+ screen->number, screen->screen_name, screen->xroot);
return screen;
}
@@ -98,8 +111,10 @@ meta_screen_new (MetaDisplay *display,
void
meta_screen_free (MetaScreen *screen)
{
+ meta_ui_slave_free (screen->uislave);
if (screen->pango_context)
g_object_unref (G_OBJECT (screen->pango_context));
+ g_free (screen->screen_name);
g_free (screen);
}
@@ -256,3 +271,64 @@ meta_screen_for_x_screen (Screen *xscreen)
return meta_display_screen_for_x_screen (display, xscreen);
}
+
+static void
+ui_slave_func (MetaUISlave *uislave,
+ MetaMessage *message,
+ gpointer data)
+{
+ switch (message->header.message_code)
+ {
+ case MetaMessageCheckCode:
+ meta_verbose ("Received UI slave check message version: %s host alias: %s messages version: %d\n",
+ message->check.metacity_version,
+ message->check.host_alias,
+ message->check.messages_version);
+
+ if (strcmp (message->check.metacity_version, VERSION) != 0 ||
+ strcmp (message->check.host_alias, HOST_ALIAS) != 0 ||
+ message->check.messages_version != META_MESSAGES_VERSION)
+ {
+ meta_warning ("metacity-uislave has the wrong version; must use the one compiled with metacity\n");
+ meta_ui_slave_disable (uislave);
+ }
+
+ break;
+
+ default:
+ meta_verbose ("Received unhandled message from UI slave: %d\n",
+ message->header.message_code);
+ break;
+ }
+}
+
+
+static char*
+get_screen_name (MetaDisplay *display,
+ int number)
+{
+ char *p;
+ char *dname;
+ char *scr;
+
+ /* DisplayString gives us a sort of canonical display,
+ * vs. the user-entered name from XDisplayName()
+ */
+ dname = g_strdup (DisplayString (display->xdisplay));
+
+ /* Change display name to specify this screen.
+ */
+ p = strrchr (dname, ':');
+ if (p)
+ {
+ p = strchr (p, '.');
+ if (p)
+ *p = '\0';
+ }
+
+ scr = g_strdup_printf ("%s.%d", dname, number);
+
+ g_free (dname);
+
+ return scr;
+}
diff --git a/src/screen.h b/src/screen.h
index 17e7b4e4..c66cef68 100644
--- a/src/screen.h
+++ b/src/screen.h
@@ -29,6 +29,7 @@ struct _MetaScreen
{
MetaDisplay *display;
int number;
+ char *screen_name;
Screen *xscreen;
Window xroot;
@@ -39,7 +40,9 @@ struct _MetaScreen
/* we only need one since we only draw to a single visual (that of
* root window)
*/
- PangoContext *pango_context;
+ PangoContext *pango_context;
+
+ MetaUISlave *uislave;
};
MetaScreen* meta_screen_new (MetaDisplay *display,
diff --git a/src/theme.c b/src/theme.c
index 568c5c05..b7dc40ec 100644
--- a/src/theme.c
+++ b/src/theme.c
@@ -102,7 +102,7 @@ default_fill_frame_geometry (MetaFrameInfo *info,
pango_layout_set_text (d->layout, info->title, -1);
else
pango_layout_set_text (d->layout, " ", -1);
-
+
pango_layout_get_pixel_extents (d->layout, NULL, &rect);
d->title_height = rect.height + VERTICAL_TEXT_PAD * 2;
diff --git a/src/uislave.c b/src/uislave.c
index 11eb6f23..dbb7105a 100644
--- a/src/uislave.c
+++ b/src/uislave.c
@@ -20,10 +20,513 @@
*/
#include "uislave.h"
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <signal.h>
+
+typedef enum
+{
+ READ_FAILED = 0, /* FALSE */
+ READ_OK,
+ READ_EOF
+} ReadResult;
+
+static void respawn_child (MetaUISlave *uislave);
+static gboolean output_callback (GIOChannel *source,
+ GIOCondition condition,
+ gpointer data);
+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);
+
+/* 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)
+meta_ui_slave_new (const char *display_name,
+ MetaUISlaveFunc func,
+ gpointer data)
+{
+ MetaUISlave *uislave;
+ GSource *source;
+
+ source = g_source_new (&mq_funcs, sizeof (MetaUISlave));
+
+ uislave = (MetaUISlave*) source;
+
+ uislave->display_name = g_strdup (display_name);
+ uislave->queue = g_queue_new ();
+ uislave->buf = g_string_new ("");
+ uislave->current_message = g_string_new ("");
+
+ 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);
+
+ 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;
+}
+
+void
+meta_ui_slave_free (MetaUISlave *uislave)
+{
+ GSource *source;
+
+ source = (GSource*) uislave;
+
+ g_source_destroy (source);
+}
+
+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
+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_poll.fd = outpipe;
+ uislave->out_poll.events = G_IO_IN;
+
+ uislave->err_channel = g_io_channel_unix_new (errpipe);
+
+ uislave->errwatch = g_io_add_watch (uislave->err_channel,
+ G_IO_IN,
+ error_callback,
+ 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 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 */
+ g_string_append_len (uislave->current_message,
+ uislave->buf->str,
+ needed);
+ g_string_erase (uislave->buf,
+ 0, needed);
+}
+
+static gboolean
+output_callback (GIOChannel *source,
+ GIOCondition condition,
+ gpointer data)
+{
+ /* Read messages from slave */
+ MetaUISlave *uislave;
+ ReadResult res;
+
+ uislave = data;
+
+ res = read_data (uislave->buf, uislave->out_pipe);
+
+ 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;
+ }
+
+ return TRUE;
+}
+
+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
+mq_queue_messages (MetaUISlave *uislave)
+{
+ if (uislave->buf->len == 0)
+ return;
+
+ if (uislave->current_message->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;
+
+ /* note that the string from the UI slave includes the nul byte */
+ esc = META_MESSAGE_ESCAPE;
+
+ esc_pos = 0;
+ p = uislave->buf->str;
+ while (p != (uislave->buf->str + uislave->buf->len) &&
+ esc_pos < META_MESSAGE_ESCAPE_LEN)
+ {
+ if (*p != esc[esc_pos])
+ esc_pos = 0;
+ else
+ ++esc_pos;
+
+ ++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)
+ {
+ meta_verbose ("Ignoring %d bytes from UI slave\n",
+ ignored);
+
+ g_string_erase (uislave->buf, 0, ignored);
+ }
+ }
+ else if (esc_pos == 0)
+ {
+ /* End of buffer doesn't begin an escape sequence;
+ * toss out entire buffer.
+ */
+ meta_verbose ("Ignoring %d bytes from UI slave\n",
+ uislave->buf->len);
+ g_string_truncate (uislave->buf, 0);
+ }
+
+ if (uislave->buf->len < (META_MESSAGE_ESCAPE_LEN + sizeof (MetaMessageHeader)))
+ return; /* Not enough data yet. */
+
+ memcpy (&header, uislave->buf->str + META_MESSAGE_ESCAPE_LEN, sizeof (MetaMessageHeader));
+
+ /* Length includes the header even though it's in the header. */
+ meta_verbose ("Read header code: %d length: %d from UI slave\n",
+ header.message_code, header.length);
+
+ uislave->current_required_len = header.length;
+ g_string_erase (uislave->buf, 0, META_MESSAGE_ESCAPE_LEN);
+
+ append_pending (uislave);
+ }
+
+ 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;
+
+ message = g_new (MetaMessage, 1);
+
+ memcpy (message,
+ uislave->current_message->str, uislave->current_message->len);
+
+ g_queue_push_tail (uislave->queue, message);
+
+ meta_verbose ("Added %d-byte message to queue\n",
+ uislave->current_message->len);
+
+ uislave->current_required_len = 0;
+ g_string_truncate (uislave->current_message, 0);
+ }
+}
+
+static gboolean
+mq_messages_pending (MetaUISlave *uislave)
+{
+ return uislave->queue->length > 0 || uislave->buf->len > 0;
+}
+
+static gboolean
+mq_prepare (GSource *source, gint *timeout)
+{
+ MetaUISlave *uislave;
+
+ uislave = (MetaUISlave*) source;
+
+ *timeout = -1;
+
+ return mq_messages_pending (uislave);
+}
+
+static gboolean
+mq_check (GSource *source)
+{
+ MetaUISlave *uislave;
+
+ uislave = (MetaUISlave*) source;
+
+ return mq_messages_pending (uislave);
+}
+
+static gboolean
+mq_dispatch (GSource *source, GSourceFunc callback, gpointer user_data)
+{
+ MetaUISlave *uislave;
+
+ uislave = (MetaUISlave*) source;
+
+ mq_queue_messages (uislave);
+
+ 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->outwatch != 0)
+ g_source_remove (uislave->outwatch);
+
+ if (uislave->errwatch != 0)
+ g_source_remove (uislave->errwatch);
+
+ if (uislave->out_channel)
+ g_io_channel_unref (uislave->out_channel);
+
+ 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);
+
+ 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 */
+ 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->child_pid = 0;
+ uislave->in_pipe = -1;
+ uislave->out_pipe = -1;
+ uislave->err_pipe = -1;
+ uislave->no_respawn = FALSE;
+ uislave->out_channel = NULL;
+ uislave->err_channel = NULL;
+ uislave->outwatch = 0;
+ uislave->errwatch = 0;
+ uislave->current_required_len = 0;
+}
+
+static void
+mq_destroy (GSource *source)
+{
+ MetaUISlave *uislave;
+
+ uislave = (MetaUISlave*) source;
+
+ meta_verbose ("Deleting UI slave for display '%s'\n",
+ uislave->display_name);
+
+ kill_child (uislave);
+
+ g_string_free (uislave->buf, TRUE);
+ g_string_free (uislave->current_message, TRUE);
+
+ g_queue_free (uislave->queue);
+
+ g_free (uislave->display_name);
+
+ /* source itself is freed by glib */
+}
+
+static ReadResult
+read_data (GString *str,
+ gint fd)
{
+#define BUFSIZE 16
+ 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.h b/src/uislave.h
index db2f1d40..924e3e29 100644
--- a/src/uislave.h
+++ b/src/uislave.h
@@ -23,17 +23,39 @@
#define META_UI_SLAVE_H
#include "util.h"
-#include <X11/Xlib.h>
-#include <X11/Xutil.h>
+#include "uislave/messages.h"
+#include "display.h"
-typedef struct _MetaUISlave MetaUISlave;
+typedef void (* MetaUISlaveFunc) (MetaUISlave *uislave,
+ MetaMessage *message,
+ gpointer data);
struct _MetaUISlave
{
+ GSource source;
+
char *display_name;
+ int child_pid;
+ int in_pipe;
+ int err_pipe;
+ GPollFD out_poll;
+ GIOChannel *err_channel;
+ unsigned int errwatch;
+ GQueue *queue;
+ GString *buf;
+ GString *current_message;
+ int current_required_len;
+ /* if we determine that our available slave is hosed,
+ * set this bit.
+ */
+ guint no_respawn : 1;
};
-MetaUISlave* meta_ui_slave_new (const char *display_name);
+MetaUISlave* meta_ui_slave_new (const char *display_name,
+ MetaUISlaveFunc func,
+ gpointer data);
+void meta_ui_slave_free (MetaUISlave *uislave);
+void meta_ui_slave_disable (MetaUISlave *uislave);
#endif
diff --git a/src/uislave/Makefile.am b/src/uislave/Makefile.am
index 920fd824..936309d7 100644
--- a/src/uislave/Makefile.am
+++ b/src/uislave/Makefile.am
@@ -1,8 +1,10 @@
-INCLUDES=@UISLAVE_CFLAGS@
+INCLUDES=@UISLAVE_CFLAGS@ -DHOST_ALIAS=\"@HOST_ALIAS@\"
-metacity_uislave_SOURCES = \
- main.c
+metacity_uislave_SOURCES = \
+ main.c \
+ messages.c \
+ messages.h
libexec_PROGRAMS=metacity-uislave
diff --git a/src/uislave/main.c b/src/uislave/main.c
index a5260cdc..7be8598a 100644
--- a/src/uislave/main.c
+++ b/src/uislave/main.c
@@ -19,9 +19,75 @@
* 02111-1307, USA.
*/
+#include "messages.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+void
+meta_ui_warning (const char *format, ...)
+{
+ va_list args;
+ gchar *str;
+
+ g_return_if_fail (format != NULL);
+
+ va_start (args, format);
+ str = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ fputs (str, stderr);
+
+ g_free (str);
+}
+
int
main (int argc, char **argv)
{
+ int i;
+
+ /* report our nature to the window manager */
+ meta_message_send_check ();
+#if 1
+ /* Try breaking message queue system. */
+ i = 0;
+ while (i < 100)
+ {
+ meta_message_send_check ();
+ if (g_random_boolean ())
+ {
+ int j;
+ if (g_random_boolean ())
+ j = g_random_int_range (0, 15);
+ else
+ j = g_random_int_range (0, 1000);
+ while (j > 0)
+ {
+ char b;
+ b = g_random_int_range (0, 256);
+
+ write (1, &b, 1);
+ --j;
+ }
+ }
+
+ ++i;
+ }
+#endif
+
+ gtk_init (&argc, &argv);
+
+ gtk_main ();
+
return 0;
}
+
+
+
+
+
+
+
diff --git a/src/uislave/messages.c b/src/uislave/messages.c
new file mode 100644
index 00000000..0e7def8f
--- /dev/null
+++ b/src/uislave/messages.c
@@ -0,0 +1,123 @@
+/* Metacity UI Slave Messages */
+
+/*
+ * 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 "messages.h"
+#include "main.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <config.h>
+
+typedef enum
+{
+ READ_FAILED = 0, /* FALSE */
+ READ_OK,
+ READ_EOF
+} ReadResult;
+
+static ReadResult read_data (GString *str,
+ gint fd);
+
+static void send_message (MetaMessage *message);
+
+void
+meta_message_send_check (void)
+{
+ MetaMessageCheck check;
+
+ memset (&check, 0, sizeof (check));
+ check.header.message_code = MetaMessageCheckCode;
+ check.header.length = sizeof (check);
+ strcpy (check.metacity_version, VERSION);
+ strcpy (check.host_alias, HOST_ALIAS);
+ check.metacity_version[META_MESSAGE_MAX_VERSION_LEN] = '\0';
+ check.host_alias[META_MESSAGE_MAX_HOST_ALIAS_LEN] = '\0';
+ check.messages_version = META_MESSAGES_VERSION;
+
+ send_message ((MetaMessage*)&check);
+}
+
+static int
+write_bytes (void *buf, int bytes)
+{
+ const char *p;
+
+ p = (char*) buf;
+ while (bytes > 0)
+ {
+ int written;
+
+ written = write (1, p, bytes);
+
+ if (written < 0)
+ return -1;
+
+ bytes -= written;
+ p += written;
+ }
+
+ return 0;
+}
+
+static void
+send_message (MetaMessage *message)
+{
+ /* Not much point checking for errors here. We can't
+ * really report them anyway.
+ */
+
+ write_bytes (META_MESSAGE_ESCAPE, META_MESSAGE_ESCAPE_LEN);
+ write_bytes (message, message->header.length);
+}
+
+static ReadResult
+read_data (GString *str,
+ gint fd)
+{
+ gint bytes;
+ gchar buf[4096];
+
+ again:
+
+ bytes = read (fd, &buf, 4096);
+
+ 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_ui_warning (_("Failed to read data from window manager (%s)\n"),
+ g_strerror (errno));
+
+ return READ_FAILED;
+ }
+ else
+ return READ_OK;
+}
diff --git a/src/uislave/messages.h b/src/uislave/messages.h
new file mode 100644
index 00000000..bd3ec33f
--- /dev/null
+++ b/src/uislave/messages.h
@@ -0,0 +1,115 @@
+/* Metacity UI Slave Messages */
+
+/*
+ * 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_UI_SLAVE_MESSAGES_H
+#define META_UI_SLAVE_MESSAGES_H
+
+#include <glib.h>
+
+/* This header is shared between the WM and the UI slave */
+/* Note that our IPC can be kind of lame; we trust both sides
+ * of the connection, and assume that they were compiled at the
+ * same time vs. the same libs on the same arch
+ */
+
+/* We increment this when we change this header, so we can
+ * check for mismatched UI slave and WM
+ */
+#define META_MESSAGES_VERSION 1
+
+/* We have an escape sequence, just in case some part of GTK
+ * decides to write to stdout, so that we have a good chance
+ * of surviving that. GTK probably won't print this string.
+ * This string has to stay the same always so we can ping
+ * old UI slaves.
+ */
+#define META_MESSAGE_ESCAPE "|~-metacity-~|"
+/* len includes nul byte which is a required part of the escape */
+#define META_MESSAGE_ESCAPE_LEN 15
+
+#define META_MESSAGE_MAX_VERSION_LEN 15
+#define META_MESSAGE_MAX_HOST_ALIAS_LEN 50
+
+typedef union _MetaMessage MetaMessage;
+typedef struct _MetaMessageHeader MetaMessageHeader;
+typedef struct _MetaMessageCheck MetaMessageCheck;
+typedef struct _MetaMessageShowTip MetaMessageShowTip;
+typedef struct _MetaMessageHideTip MetaMessageHideTip;
+
+typedef enum
+{
+ /* Keep NullCode and CheckCode unchanged, as with the escape sequence,
+ * so we can check old UI slaves.
+ */
+ MetaMessageNullCode,
+ MetaMessageCheckCode,
+ MetaMessageShowTipCode,
+ MetaMessageHideTipCode
+} MetaMessageCode;
+
+struct _MetaMessageHeader
+{
+ MetaMessageCode message_code;
+ int length;
+};
+
+/* just a ping to see if we have the right
+ * version of UI slave.
+ */
+struct _MetaMessageCheck
+{
+ MetaMessageHeader header;
+
+ /* it's OK if the max sizes aren't large enough in all cases, these
+ * are just paranoia checks
+ */
+ char metacity_version[META_MESSAGE_MAX_VERSION_LEN + 1];
+ char host_alias[META_MESSAGE_MAX_HOST_ALIAS_LEN + 1];
+ int messages_version;
+};
+
+struct _MetaMessageShowTip
+{
+ MetaMessageHeader header;
+ int root_x;
+ int root_y;
+ /* Then a nul-terminated string follows */
+};
+
+struct _MetaMessageHideTip
+{
+ MetaMessageHeader header;
+ /* just hides the current tip */
+};
+
+union _MetaMessage
+{
+ MetaMessageHeader header;
+ MetaMessageCheck check;
+ MetaMessageShowTip show_tip;
+ MetaMessageShowTip hide_tip;
+};
+
+/* Slave-side message send/read code */
+
+void meta_message_send_check (void);
+
+#endif
diff --git a/src/util.h b/src/util.h
index 2122bb7c..2ea20e0c 100644
--- a/src/util.h
+++ b/src/util.h
@@ -54,6 +54,7 @@ void meta_fatal (const char *format,
...) G_GNUC_PRINTF (1, 2);
/* FIXME */
+#include <config.h>
#define _(x) x
#endif
diff --git a/src/window.c b/src/window.c
index 83153c2e..d8835cee 100644
--- a/src/window.c
+++ b/src/window.c
@@ -34,13 +34,11 @@ static int update_size_hints (MetaWindow *window);
static int update_title (MetaWindow *window);
static int update_protocols (MetaWindow *window);
static gboolean process_configure_request (MetaWindow *window,
- XConfigureRequestEvent *event);
+ int x, int y, int width, int height,
+ int border_width);
static gboolean process_property_notify (MetaWindow *window,
XPropertyEvent *event);
-
-
-
MetaWindow*
meta_window_new (MetaDisplay *display, Window xwindow)
{
@@ -57,9 +55,7 @@ meta_window_new (MetaDisplay *display, Window xwindow)
xwindow, &attrs) == Success &&
attrs.override_redirect)
{
- /* Oops. Probably attempted to manage override redirect window
- * in initial screen_manage_all_windows() call.
- */
+ meta_verbose ("Deciding not to manage override_redirect window 0x%lx\n", xwindow);
meta_error_trap_pop (display);
return NULL;
}
@@ -67,7 +63,7 @@ meta_window_new (MetaDisplay *display, Window xwindow)
XAddToSaveSet (display->xdisplay, xwindow);
XSelectInput (display->xdisplay, xwindow,
- StructureNotifyMask);
+ PropertyChangeMask);
if (meta_error_trap_pop (display) != Success)
{
@@ -105,6 +101,12 @@ meta_window_new (MetaDisplay *display, Window xwindow)
window->rect.y = attrs.y;
window->rect.width = attrs.width;
window->rect.height = attrs.height;
+
+ window->size_hints.x = attrs.x;
+ window->size_hints.y = attrs.y;
+ window->size_hints.width = attrs.width;
+ window->size_hints.height = attrs.height;
+
window->depth = attrs.depth;
window->xvisual = attrs.visual;
@@ -112,14 +114,17 @@ meta_window_new (MetaDisplay *display, Window xwindow)
window->iconic = FALSE;
window->desc = g_strdup_printf ("0x%lx", window->xwindow);
+
+ window->frame = NULL;
meta_display_register_x_window (display, &window->xwindow, window);
update_size_hints (window);
update_title (window);
- update_protocols (window);
+ update_protocols (window);
+
+ meta_window_resize (window, window->size_hints.width, window->size_hints.height);
- window->frame = NULL;
meta_window_ensure_frame (window);
return window;
@@ -132,10 +137,10 @@ meta_window_free (MetaWindow *window)
meta_display_unregister_x_window (window->display, window->xwindow);
- g_free (window->title);
-
meta_window_destroy_frame (window);
+ g_free (window->title);
+ g_free (window->desc);
g_free (window);
}
@@ -159,13 +164,56 @@ meta_window_hide (MetaWindow *window)
window->iconic = TRUE;
}
+void
+meta_window_resize (MetaWindow *window,
+ int w,
+ int h)
+{
+ meta_verbose ("Resizing %s to %d x %d\n", window->desc, w, h);
+ constrain_size (window, w, h, &w, &h);
+ meta_verbose ("Constrained resize of %s to %d x %d\n", window->desc, w, h);
+
+ if (w != window->rect.width ||
+ h != window->rect.height)
+ {
+ meta_error_trap_push (window->display);
+ XResizeWindow (window->display->xdisplay,
+ window->xwindow,
+ w, h);
+ meta_error_trap_pop (window->display);
+ window->rect.width = w;
+ window->rect.height = h;
+
+ if (window->frame)
+ meta_frame_queue_recalc (window->frame);
+ }
+}
+
+gboolean
+meta_window_configure_request (MetaWindow *window,
+ XEvent *event)
+{
+ return process_configure_request (window,
+ event->xconfigurerequest.x,
+ event->xconfigurerequest.y,
+ event->xconfigurerequest.width,
+ event->xconfigurerequest.height,
+ event->xconfigurerequest.border_width);
+}
+
+gboolean
+meta_window_property_notify (MetaWindow *window,
+ XEvent *event)
+{
+ return process_property_notify (window, &event->xproperty);
+}
+
+
+
gboolean
meta_window_event (MetaWindow *window,
XEvent *event)
{
- if (window->frame &&
- event->xany.window == window->frame->xwindow)
- return meta_frame_event (window->frame, event);
if (event->xany.window != window->xwindow)
return FALSE;
@@ -203,7 +251,7 @@ meta_window_event (MetaWindow *window,
case CreateNotify:
break;
case DestroyNotify:
- meta_window_free (window);
+
return TRUE;
break;
case UnmapNotify:
@@ -218,17 +266,10 @@ meta_window_event (MetaWindow *window,
case ReparentNotify:
break;
case ConfigureNotify:
- if (event->xconfigure.override_redirect)
- {
- /* Unmanage it, override_redirect was toggled on?
- * Can this happen?
- */
- meta_window_free (window);
- return TRUE;
- }
+
break;
case ConfigureRequest:
- return process_configure_request (window, &event->xconfigurerequest);
+
break;
case GravityNotify:
break;
@@ -239,7 +280,7 @@ meta_window_event (MetaWindow *window,
case CirculateRequest:
break;
case PropertyNotify:
- return process_property_notify (window, &event->xproperty);
+
break;
case SelectionClear:
break;
@@ -269,39 +310,160 @@ process_property_notify (MetaWindow *window,
event->atom == window->display->atom_net_wm_name)
{
update_title (window);
+
+ if (window->frame)
+ meta_frame_queue_recalc (window->frame);
}
else if (event->atom == XA_WM_NORMAL_HINTS)
{
update_size_hints (window);
+
+ /* See if we need to constrain current size */
+ meta_window_resize (window, window->rect.width, window->rect.height);
}
- else if (event->atom == XA_WM_PROTOCOLS)
+ else if (event->atom == window->display->atom_wm_protocols)
{
update_protocols (window);
+
+ if (window->frame)
+ meta_frame_queue_recalc (window->frame);
}
return TRUE;
}
+static void
+send_configure_notify (MetaWindow *window)
+{
+ XEvent event;
+
+ /* from twm */
+
+ event.type = ConfigureNotify;
+ event.xconfigure.display = window->display->xdisplay;
+ event.xconfigure.event = window->xwindow;
+ event.xconfigure.window = window->xwindow;
+ event.xconfigure.x = window->rect.x - window->border_width;
+ event.xconfigure.y = window->rect.y - window->border_width;
+ if (window->frame)
+ {
+ /* Need to be in root window coordinates */
+ event.xconfigure.x += window->frame->rect.x;
+ event.xconfigure.y += window->frame->rect.y;
+ }
+ event.xconfigure.width = window->rect.width;
+ event.xconfigure.height = window->rect.height;
+ event.xconfigure.border_width = window->border_width; /* requested not actual */
+ event.xconfigure.above = None; /* FIXME */
+ event.xconfigure.override_redirect = False;
+
+ meta_verbose ("Sending synthetic configure notify to %s with x: %d y: %d w: %d h: %d\n",
+ window->desc,
+ event.xconfigure.x, event.xconfigure.y,
+ event.xconfigure.width, event.xconfigure.height);
+
+ meta_error_trap_push (window->display);
+ XSendEvent(window->display->xdisplay,
+ window->xwindow,
+ False, StructureNotifyMask, &event);
+ meta_error_trap_pop (window->display);
+}
+
static gboolean
-process_configure_request (MetaWindow *window,
- XConfigureRequestEvent *event)
+process_configure_request (MetaWindow *window,
+ int x, int y,
+ int width, int height,
+ int border_width)
{
/* ICCCM 4.1.5 */
-
+ XWindowChanges values;
+ unsigned int mask;
+ int client_x, client_y;
+
/* Note that x, y is the corner of the window border,
* and width, height is the size of the window inside
* its border, but that we always deny border requests
* because we don't believe in clients who use lame-ass
* X features like that.
*/
- window->border_width = event->border_width;
- window->size_hints.x = event->x;
- window->size_hints.y = event->y;
- window->size_hints.width = event->width;
- window->size_hints.height = event->height;
+ window->border_width = border_width;
+
+ /* We're ignoring the value_mask here, since sizes
+ * not in the mask will be the current window geometry.
+ */
+
+ window->size_hints.x = x;
+ window->size_hints.y = y;
+ window->size_hints.width = width;
+ window->size_hints.height = height;
+
+ constrain_size (window,
+ window->size_hints.width,
+ window->size_hints.height,
+ &window->size_hints.width,
+ &window->size_hints.height);
- /* FIXME */
+ meta_verbose ("Constrained configure request size to %d x %d\n",
+ window->size_hints.width, window->size_hints.height);
+ if (window->frame)
+ {
+ meta_frame_child_configure_request (window->frame);
+ client_x = window->frame->child_x;
+ client_y = window->frame->child_y;
+ meta_verbose ("Will place client window %s inside frame at %d,%d\n",
+ window->desc, client_x, client_y);
+ }
+ else
+ {
+ client_x = window->size_hints.x;
+ client_y = window->size_hints.y;
+ meta_verbose ("Will place client window %s at root coordinate %d,%d\n",
+ window->desc, client_x, client_y);
+ }
+
+ values.border_width = 0;
+ values.x = client_x;
+ values.y = client_y;
+ values.width = window->size_hints.width;
+ values.height = window->size_hints.height;
+
+ mask = 0;
+ if (window->border_width != 0)
+ mask |= CWBorderWidth;
+ if (values.x != window->rect.x)
+ mask |= CWX;
+ if (values.y != window->rect.y)
+ mask |= CWY;
+ if (values.width != window->rect.width)
+ mask |= CWWidth;
+ if (values.height != window->rect.height)
+ mask |= CWHeight;
+
+ window->rect.x = values.x;
+ window->rect.y = values.y;
+ window->rect.width = values.width;
+ window->rect.height = values.height;
+
+ meta_error_trap_push (window->display);
+ XConfigureWindow (window->display->xdisplay,
+ window->xwindow,
+ mask,
+ &values);
+ meta_error_trap_pop (window->display);
+
+ if (mask & (CWBorderWidth | CWWidth | CWHeight))
+ {
+ /* Resizing, no synthetic ConfigureNotify, third case in 4.1.5 */
+ }
+ else
+ {
+ /* Moving but not resizing, second case in 4.1.5, or
+ * have to send the ConfigureNotify, first case in 4.1.5
+ */
+ send_configure_notify (window);
+ }
+
return TRUE;
}
@@ -333,7 +495,12 @@ update_size_hints (MetaWindow *window)
window->size_hints.height = h;
if (window->size_hints.flags & PBaseSize)
- ;
+ {
+ meta_verbose ("Window %s sets base size %d x %d\n",
+ window->desc,
+ window->size_hints.base_width,
+ window->size_hints.base_height);
+ }
else if (window->size_hints.flags & PMinSize)
{
window->size_hints.base_width = window->size_hints.min_width;
@@ -347,7 +514,12 @@ update_size_hints (MetaWindow *window)
window->size_hints.flags |= PBaseSize;
if (window->size_hints.flags & PMinSize)
- ;
+ {
+ meta_verbose ("Window %s sets min size %d x %d\n",
+ window->desc,
+ window->size_hints.min_width,
+ window->size_hints.min_height);
+ }
else if (window->size_hints.flags & PBaseSize)
{
window->size_hints.min_width = window->size_hints.base_width;
@@ -361,7 +533,12 @@ update_size_hints (MetaWindow *window)
window->size_hints.flags |= PMinSize;
if (window->size_hints.flags & PMaxSize)
- ;
+ {
+ meta_verbose ("Window %s sets max size %d x %d\n",
+ window->desc,
+ window->size_hints.max_width,
+ window->size_hints.max_height);
+ }
else
{
window->size_hints.max_width = G_MAXINT;
@@ -370,7 +547,22 @@ update_size_hints (MetaWindow *window)
}
if (window->size_hints.flags & PResizeInc)
- ;
+ {
+ meta_verbose ("Window %s sets resize width inc: %d height inc: %d\n",
+ window->desc,
+ window->size_hints.width_inc,
+ window->size_hints.height_inc);
+ if (window->size_hints.width_inc == 0)
+ {
+ window->size_hints.width_inc = 1;
+ meta_verbose ("Corrected 0 width_inc to 1\n");
+ }
+ if (window->size_hints.height_inc == 0)
+ {
+ window->size_hints.height_inc = 1;
+ meta_verbose ("Corrected 0 height_inc to 1\n");
+ }
+ }
else
{
window->size_hints.width_inc = 1;
@@ -380,6 +572,13 @@ update_size_hints (MetaWindow *window)
if (window->size_hints.flags & PAspect)
{
+ meta_verbose ("Window %s sets min_aspect: %d/%d max_aspect: %d/%d\n",
+ window->desc,
+ window->size_hints.min_aspect.x,
+ window->size_hints.min_aspect.y,
+ window->size_hints.max_aspect.x,
+ window->size_hints.max_aspect.y);
+
/* don't divide by 0 */
if (window->size_hints.min_aspect.y < 1)
window->size_hints.min_aspect.y = 1;
@@ -396,14 +595,16 @@ update_size_hints (MetaWindow *window)
}
if (window->size_hints.flags & PWinGravity)
- ;
+ {
+ meta_verbose ("Window %s sets gravity %d\n",
+ window->desc,
+ window->size_hints.win_gravity);
+ }
else
{
window->size_hints.win_gravity = NorthWestGravity;
window->size_hints.flags |= PWinGravity;
}
-
- /* FIXME constrain the window to these hints */
return meta_error_trap_pop (window->display);
}
@@ -480,38 +681,44 @@ update_title (MetaWindow *window)
window->title = g_strdup ("");
window->desc = g_strdup_printf ("0x%lx (%.10s)", window->xwindow, window->title);
-
+
return meta_error_trap_pop (window->display);
}
static int
update_protocols (MetaWindow *window)
{
- Atom *protocols;
- int n_protocols;
+ Atom *protocols = NULL;
+ int n_protocols = 0;
int i;
-
- meta_error_trap_push (window->display);
- XGetWMProtocols (window->display->xdisplay,
- window->xwindow,
- &protocols,
- &n_protocols);
window->take_focus = FALSE;
window->delete_window = FALSE;
- i = 0;
- while (i < n_protocols)
+
+ meta_error_trap_push (window->display);
+
+ if (XGetWMProtocols (window->display->xdisplay,
+ window->xwindow,
+ &protocols,
+ &n_protocols) == Success)
{
- if (protocols[i] == _XA_WM_TAKE_FOCUS)
- window->takes_focus = TRUE;
- else if (protocols[i] == _XA_WM_DELETE_WINDOW)
- window->delete_window = TRUE;
- ++i;
- }
+ i = 0;
+ while (i < n_protocols)
+ {
+ if (protocols[i] == window->display->atom_wm_take_focus)
+ window->take_focus = TRUE;
+ else if (protocols[i] == window->display->atom_wm_delete_window)
+ window->delete_window = TRUE;
+ ++i;
+ }
- if (protocols)
- XFree (protocols);
+ if (protocols)
+ XFree (protocols);
+ }
+ meta_verbose ("Window %s has take_focus = %d delete_window = %d\n",
+ window->desc, window->take_focus, window->delete_window);
+
return meta_error_trap_pop (window->display);
}
@@ -591,3 +798,4 @@ constrain_size (MetaWindow *window,
*new_width = width;
*new_height = height;
}
+
diff --git a/src/window.h b/src/window.h
index 4b465a8b..01f68bd5 100644
--- a/src/window.h
+++ b/src/window.h
@@ -51,14 +51,21 @@ struct _MetaWindow
XSizeHints size_hints;
};
-MetaWindow* meta_window_new (MetaDisplay *display,
- Window xwindow);
-void meta_window_free (MetaWindow *window);
-void meta_window_show (MetaWindow *window);
-void meta_window_hide (MetaWindow *window);
+MetaWindow* meta_window_new (MetaDisplay *display,
+ Window xwindow);
+void meta_window_free (MetaWindow *window);
+void meta_window_show (MetaWindow *window);
+void meta_window_hide (MetaWindow *window);
+void meta_window_resize (MetaWindow *window,
+ int w,
+ int h);
+
+gboolean meta_window_configure_request (MetaWindow *window,
+ XEvent *event);
+gboolean meta_window_property_notify (MetaWindow *window,
+ XEvent *event);
+
-gboolean meta_window_event (MetaWindow *window,
- XEvent *event);