diff --git a/configure.in b/configure.in index 33e4dc94b..6a337a1b2 100644 --- a/configure.in +++ b/configure.in @@ -62,6 +62,9 @@ PKG_CHECK_MODULES(METACITY, $PANGO_PACKAGES) # Check for shaped window extension AC_CHECK_LIB(Xext, XShapeCombineMask, AC_DEFINE(HAVE_SHAPE_EXT),,$METACITY_LIBS) +HOST_ALIAS=$host_alias +AC_SUBST(HOST_ALIAS) + AC_OUTPUT([ Makefile intl/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index 186c476bb..2254e6dff 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 a32c537d4..95718f872 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 755ae4144..681e7619c 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 bd22c4129..6d4adf924 100644 --- a/src/frame.c +++ b/src/frame.c @@ -37,109 +37,145 @@ meta_frame_init_info (MetaFrame *frame, info->height = frame->rect.height; } +static void +meta_frame_calc_initial_pos (MetaFrame *frame, + int child_root_x, int child_root_y) +{ + MetaWindow *window; + + window = frame->window; + + switch (window->size_hints.win_gravity) + { + case NorthWestGravity: + frame->rect.x = child_root_x; + frame->rect.y = child_root_y; + break; + case NorthGravity: + frame->rect.x = child_root_x - frame->rect.width / 2; + frame->rect.y = child_root_y; + break; + case NorthEastGravity: + frame->rect.x = child_root_x - frame->rect.width; + frame->rect.y = child_root_y; + break; + case WestGravity: + frame->rect.x = child_root_x; + frame->rect.y = child_root_y - frame->rect.height / 2; + break; + case CenterGravity: + 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 = child_root_x - frame->rect.width; + frame->rect.y = child_root_y - frame->rect.height / 2; + break; + case SouthWestGravity: + frame->rect.x = child_root_x; + frame->rect.y = child_root_y - frame->rect.height; + break; + case SouthGravity: + 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 = child_root_x - frame->rect.width; + frame->rect.y = child_root_y - frame->rect.height; + break; + case StaticGravity: + default: + frame->rect.x = child_root_x - frame->child_x; + frame->rect.y = child_root_y - frame->child_y; + break; + } +} + +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; - int child_x, child_y; - unsigned long background_pixel; XSetWindowAttributes attrs; - MetaFrameInfo info; 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; - - /* 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); + frame->theme_acquired = FALSE; - 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); + /* This fills in frame->rect as well. */ + meta_frame_calc_geometry (frame, + window->rect.width, + window->rect.height, + &geom); - 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; - - switch (window->size_hints.win_gravity) - { - case NorthWestGravity: - frame->rect.x = window->rect.x; - frame->rect.y = window->rect.y; - break; - case NorthGravity: - frame->rect.x = window->rect.x - frame->rect.width / 2; - frame->rect.y = window->rect.y; - break; - case NorthEastGravity: - frame->rect.x = window->rect.x - frame->rect.width; - frame->rect.y = window->rect.y; - break; - case WestGravity: - frame->rect.x = window->rect.x; - frame->rect.y = window->rect.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; - break; - case EastGravity: - frame->rect.x = window->rect.x - frame->rect.width; - frame->rect.y = window->rect.y - frame->rect.height / 2; - break; - case SouthWestGravity: - frame->rect.x = window->rect.x; - frame->rect.y = window->rect.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; - break; - case SouthEastGravity: - frame->rect.x = window->rect.x - frame->rect.width; - frame->rect.y = window->rect.y - frame->rect.height; - break; - case StaticGravity: - default: - frame->rect.x = window->rect.x - child_x; - frame->rect.y = window->rect.y - 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", + 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 e63f6f832..739a74f74 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 6dc2cc082..3e8d68c6d 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 afeb30fd4..b51d21acc 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 #include #include +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; - meta_verbose ("Added screen %d on display '%s' root 0x%lx\n", - screen->number, screen->display->name, screen->xroot); + screen->engine = &meta_default_engine; + + screen->uislave = meta_ui_slave_new (screen->screen_name, + ui_slave_func, + screen); + + 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 17e7b4e42..c66cef68f 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 568c5c05a..b7dc40ecd 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 11eb6f235..dbb7105a4 100644 --- a/src/uislave.c +++ b/src/uislave.c @@ -20,10 +20,513 @@ */ #include "uislave.h" +#include +#include +#include +#include +#include + +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 db2f1d40f..924e3e292 100644 --- a/src/uislave.h +++ b/src/uislave.h @@ -23,17 +23,39 @@ #define META_UI_SLAVE_H #include "util.h" -#include -#include +#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 920fd8248..936309d72 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 a5260cdc3..7be8598a0 100644 --- a/src/uislave/main.c +++ b/src/uislave/main.c @@ -19,9 +19,75 @@ * 02111-1307, USA. */ +#include "messages.h" + +#include +#include + +#include + +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 000000000..0e7def8f4 --- /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 +#include +#include +#include +#include +#include + +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 000000000..bd3ec33f9 --- /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 + +/* 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 2122bb7c4..2ea20e0c6 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 #define _(x) x #endif diff --git a/src/window.c b/src/window.c index 83153c2ea..d8835ceee 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; - /* FIXME */ + /* 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); + + 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 4b465a8b5..01f68bd5c 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);