From 6cfcc01334baa84204d00f9f8fb7cd97be05b403 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Sun, 5 Jan 2003 07:51:02 +0000 Subject: [PATCH] handle the client having a shape mask, fixes #101806 2003-01-05 Havoc Pennington * src/frames.c (meta_frames_apply_shapes): handle the client having a shape mask, fixes #101806 * src/core.c (meta_core_get_client_xwindow): new function * src/frame.c, src/frame.h: keep a flag for whether we need to update the frame shape * src/window.c (meta_window_new): select for ShapeNotify * src/display.h, src/display.c: actually query the shape extension, instead of just using it all over the place. * src/prefs.c (update_application_based): don't let people turn on application_based, as it just causes funky bugs. We can reenable the pref when/if it ever does something useful. --- ChangeLog | 19 ++++++++ src/core.c | 16 +++++++ src/core.h | 3 ++ src/display.c | 120 ++++++++++++++++++++++++++++++++++++++++++++------ src/display.h | 7 +++ src/frame.c | 47 ++++++++++++++------ src/frame.h | 1 + src/frames.c | 118 ++++++++++++++++++++++++++++++++++++++++++++++--- src/frames.h | 6 ++- src/prefs.c | 5 +++ src/ui.c | 13 +++--- src/ui.h | 9 ++-- src/util.c | 2 + src/util.h | 3 +- src/window.c | 48 +++++++++++++++++--- src/window.h | 3 ++ 16 files changed, 369 insertions(+), 51 deletions(-) diff --git a/ChangeLog b/ChangeLog index b7ea7109b..45288b358 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +2003-01-05 Havoc Pennington + + * src/frames.c (meta_frames_apply_shapes): handle + the client having a shape mask, fixes #101806 + + * src/core.c (meta_core_get_client_xwindow): new function + + * src/frame.c, src/frame.h: keep a flag for whether we need to + update the frame shape + + * src/window.c (meta_window_new): select for ShapeNotify + + * src/display.h, src/display.c: actually query the shape + extension, instead of just using it all over the place. + + * src/prefs.c (update_application_based): don't let people turn on + application_based, as it just causes funky bugs. We can reenable + the pref when/if it ever does something useful. + 2003-01-03 Havoc Pennington * src/display.c: include the Xrandr header file diff --git a/src/core.c b/src/core.c index 49dda84bc..ee7ff2d40 100644 --- a/src/core.c +++ b/src/core.c @@ -46,6 +46,22 @@ meta_core_get_client_size (Display *xdisplay, *height = window->rect.height; } +Window +meta_core_get_client_xwindow (Display *xdisplay, + Window frame_xwindow) +{ + MetaDisplay *display; + MetaWindow *window; + + display = meta_display_for_x_display (xdisplay); + window = meta_display_lookup_x_window (display, frame_xwindow); + + if (window == NULL || window->frame == NULL) + meta_bug ("No such frame window 0x%lx!\n", frame_xwindow); + + return window->xwindow; +} + MetaFrameFlags meta_core_get_frame_flags (Display *xdisplay, Window frame_xwindow) diff --git a/src/core.h b/src/core.h index cc02d5a09..be4065a1c 100644 --- a/src/core.h +++ b/src/core.h @@ -31,6 +31,9 @@ void meta_core_get_client_size (Display *xdisplay, int *width, int *height); +Window meta_core_get_client_xwindow (Display *xdisplay, + Window frame_xwindow); + MetaFrameFlags meta_core_get_frame_flags (Display *xdisplay, Window frame_xwindow); MetaFrameType meta_core_get_frame_type (Display *xdisplay, diff --git a/src/display.c b/src/display.c index b32ad425f..010041238 100644 --- a/src/display.c +++ b/src/display.c @@ -45,6 +45,9 @@ #ifdef HAVE_RANDR #include #endif +#ifdef HAVE_SHAPE +#include +#endif #include #define USE_GDK_DISPLAY @@ -489,6 +492,27 @@ meta_display_open (const char *name) #else /* HAVE_XSYNC */ meta_verbose ("Not compiled with Xsync support\n"); #endif /* !HAVE_XSYNC */ + + +#ifdef HAVE_SHAPE + { + display->shape_error_base = 0; + display->shape_event_base = 0; + + if (!XShapeQueryExtension (display->xdisplay, + &display->shape_event_base, + &display->shape_error_base)) + { + display->shape_error_base = 0; + display->shape_event_base = 0; + } + meta_verbose ("Attempted to init Shape, found error base %d event base %d\n", + display->shape_error_base, + display->shape_event_base); + } +#else /* HAVE_SHAPE */ + meta_verbose ("Not compiled with Shape support\n"); +#endif /* !HAVE_SHAPE */ screens = NULL; @@ -1189,6 +1213,56 @@ event_callback (XEvent *event, meta_window_handle_mouse_grab_op_event (display->grab_window, event); } #endif /* HAVE_XSYNC */ + +#ifdef HAVE_SHAPE + if (META_DISPLAY_HAS_SHAPE (display) && + event->type == (display->shape_event_base + ShapeNotify)) + { + filter_out_event = TRUE; /* GTK doesn't want to see this really */ + + if (window && !frame_was_receiver) + { + XShapeEvent *sev = (XShapeEvent*) event; + + if (sev->kind == ShapeBounding) + { + if (sev->shaped && !window->has_shape) + { + window->has_shape = TRUE; + meta_topic (META_DEBUG_SHAPES, + "Window %s now has a shape\n", + window->desc); + } + else if (!sev->shaped && window->has_shape) + { + window->has_shape = FALSE; + meta_topic (META_DEBUG_SHAPES, + "Window %s no longer has a shape\n", + window->desc); + } + else + { + meta_topic (META_DEBUG_SHAPES, + "Window %s shape changed\n", + window->desc); + } + + if (window->frame) + { + window->frame->need_reapply_frame_shape = TRUE; + meta_window_queue_move_resize (window); + } + } + } + else + { + meta_topic (META_DEBUG_SHAPES, + "ShapeNotify not on a client window (window %s frame_was_receiver = %d)\n", + window ? window->desc : "(none)", + frame_was_receiver); + } + } +#endif /* HAVE_SHAPE */ switch (event->type) { @@ -1902,6 +1976,15 @@ event_get_modified_window (MetaDisplay *display, return None; default: +#ifdef HAVE_SHAPE + if (META_DISPLAY_HAS_SHAPE (display) && + event->type == (display->shape_event_base + ShapeNotify)) + { + XShapeEvent *sev = (XShapeEvent*) event; + return sev->window; + } +#endif + return None; } } @@ -2360,6 +2443,27 @@ meta_spew_event (MetaDisplay *display, } else #endif /* HAVE_XSYNC */ +#ifdef HAVE_SHAPE + if (META_DISPLAY_HAS_SHAPE (display) && + event->type == (display->shape_event_base + ShapeNotify)) + { + XShapeEvent *sev = (XShapeEvent*) event; + + name = "ShapeNotify"; + + extra = + g_strdup_printf ("kind: %s " + "x: %d y: %d w: %d h: %d " + "shaped: %d", + sev->kind == ShapeBounding ? + "ShapeBounding" : + (sev->kind == ShapeClip ? + "ShapeClip" : "(unknown)"), + sev->x, sev->y, sev->width, sev->height, + sev->shaped); + } + else +#endif /* HAVE_SHAPE */ { name = "(Unknown event)"; extra = g_strdup_printf ("type: %d", event->xany.type); @@ -3046,20 +3150,10 @@ meta_display_queue_retheme_all_windows (MetaDisplay *display) meta_window_queue_move_resize (window); if (window->frame) { + window->frame->need_reapply_frame_shape = TRUE; + meta_frame_queue_draw (window->frame); - - /* FIXME this sucks and is slooooooooow. Do it in the idle with the - * redraw or the window resize. - */ - -#if 0 - /* in case the theme doesn't affect the frame size */ - meta_ui_apply_frame_shape (window->screen->ui, - window->frame->xwindow, - window->frame->rect.width, - window->frame->rect.height); -#endif - } + } tmp = tmp->next; } diff --git a/src/display.h b/src/display.h index 2641af4d5..803f02131 100644 --- a/src/display.h +++ b/src/display.h @@ -296,6 +296,13 @@ struct _MetaDisplay #else #define META_DISPLAY_HAS_XSYNC(display) FALSE #endif +#ifdef HAVE_SHAPE + int shape_event_base; + int shape_error_base; +#define META_DISPLAY_HAS_SHAPE(display) ((display)->shape_event_base != 0) +#else +#define META_DISPLAY_HAS_SHAPE(display) FALSE +#endif }; gboolean meta_display_open (const char *name); diff --git a/src/frame.c b/src/frame.c index b981559e9..10ae0a5e2 100644 --- a/src/frame.c +++ b/src/frame.c @@ -58,6 +58,7 @@ meta_window_ensure_frame (MetaWindow *window) frame->current_cursor = 0; frame->mapped = FALSE; + frame->need_reapply_frame_shape = TRUE; attrs.event_mask = EVENT_MASK; @@ -144,7 +145,9 @@ meta_window_ensure_frame (MetaWindow *window) meta_ui_apply_frame_shape (frame->window->screen->ui, frame->xwindow, frame->rect.width, - frame->rect.height); + frame->rect.height, + frame->window->has_shape); + frame->need_reapply_frame_shape = FALSE; meta_display_ungrab (window->display); } @@ -277,6 +280,20 @@ meta_frame_calc_geometry (MetaFrame *frame, *geomp = geom; } +static void +update_shape (MetaFrame *frame) +{ + if (frame->need_reapply_frame_shape) + { + meta_ui_apply_frame_shape (frame->window->screen->ui, + frame->xwindow, + frame->rect.width, + frame->rect.height, + frame->window->has_shape); + frame->need_reapply_frame_shape = FALSE; + } +} + void meta_frame_sync_to_window (MetaFrame *frame, int resize_gravity, @@ -284,8 +301,11 @@ meta_frame_sync_to_window (MetaFrame *frame, gboolean need_resize) { if (!(need_move || need_resize)) - return; - + { + update_shape (frame); + return; + } + meta_topic (META_DEBUG_GEOMETRY, "Syncing frame geometry %d,%d %dx%d (SE: %d,%d)\n", frame->rect.x, frame->rect.y, @@ -301,18 +321,17 @@ meta_frame_sync_to_window (MetaFrame *frame, frame->rect.width, frame->rect.height); - /* Done before the window resize, because doing it before means - * part of the window being resized becomes unshaped, which may - * be sort of hard to see with bg = None. If we did it after - * window resize, part of the window being resized would become - * shaped, which might be more visible. - */ - - meta_ui_apply_frame_shape (frame->window->screen->ui, - frame->xwindow, - frame->rect.width, - frame->rect.height); + /* we need new shape if we're resized */ + frame->need_reapply_frame_shape = TRUE; } + + /* Done before the window resize, because doing it before means + * part of the window being resized becomes unshaped, which may + * be sort of hard to see with bg = None. If we did it after + * window resize, part of the window being resized would become + * shaped, which might be more visible. + */ + update_shape (frame); if (need_move && need_resize) XMoveResizeWindow (frame->window->display->xdisplay, diff --git a/src/frame.h b/src/frame.h index 20f946999..afa19557c 100644 --- a/src/frame.h +++ b/src/frame.h @@ -57,6 +57,7 @@ struct _MetaFrame int bottom_height; guint mapped : 1; + guint need_reapply_frame_shape : 1; }; void meta_window_ensure_frame (MetaWindow *window); diff --git a/src/frames.c b/src/frames.c index e64925376..978e2bcc0 100644 --- a/src/frames.c +++ b/src/frames.c @@ -495,6 +495,7 @@ meta_frames_manage_window (MetaFrames *frames, frame->text_height = -1; frame->title = NULL; frame->expose_delayed = FALSE; + frame->shape_applied = FALSE; frame->prelit_control = META_FRAME_CONTROL_NONE; meta_core_grab_buttons (gdk_display, frame->xwindow); @@ -664,7 +665,8 @@ void meta_frames_apply_shapes (MetaFrames *frames, Window xwindow, int new_window_width, - int new_window_height) + int new_window_height, + gboolean window_has_shape) { #ifdef HAVE_SHAPE /* Apply shapes as if window had new_window_width, new_window_height */ @@ -685,10 +687,25 @@ meta_frames_apply_shapes (MetaFrames *frames, if (!(fgeom.top_left_corner_rounded || fgeom.top_right_corner_rounded || fgeom.bottom_left_corner_rounded || - fgeom.bottom_right_corner_rounded)) + fgeom.bottom_right_corner_rounded || + window_has_shape)) { - XShapeCombineMask (gdk_display, frame->xwindow, - ShapeBounding, 0, 0, None, ShapeSet); + if (frame->shape_applied) + { + meta_topic (META_DEBUG_SHAPES, + "Unsetting shape mask on frame 0x%lx\n", + frame->xwindow); + + XShapeCombineMask (gdk_display, frame->xwindow, + ShapeBounding, 0, 0, None, ShapeSet); + frame->shape_applied = FALSE; + } + else + { + meta_topic (META_DEBUG_SHAPES, + "Frame 0x%lx still doesn't need a shape mask\n", + frame->xwindow); + } return; /* nothing to do */ } @@ -804,11 +821,98 @@ meta_frames_apply_shapes (MetaFrames *frames, XSubtractRegion (window_xregion, corners_xregion, window_xregion); - XShapeCombineRegion (gdk_display, frame->xwindow, - ShapeBounding, 0, 0, window_xregion, ShapeSet); + XDestroyRegion (corners_xregion); + + if (window_has_shape) + { + /* The client window is oclock or something and has a shape + * mask. To avoid a round trip to get its shape region, we + * create a fake window that's never mapped, build up our shape + * on that, then combine. Wasting the window is assumed cheaper + * than a round trip, but who really knows for sure. + */ + XSetWindowAttributes attrs; + Window shape_window; + Window client_window; + Region client_xregion; + GdkScreen *screen; + int screen_number; + + meta_topic (META_DEBUG_SHAPES, + "Frame 0x%lx needs to incorporate client shape\n", + frame->xwindow); + + screen = gtk_widget_get_screen (GTK_WIDGET (frames)); + screen_number = gdk_x11_screen_get_screen_number (screen); + + attrs.override_redirect = True; + + shape_window = XCreateWindow (gdk_display, + RootWindow (gdk_display, screen_number), + -5000, -5000, + new_window_width, + new_window_height, + 0, + CopyFromParent, + CopyFromParent, + CopyFromParent, + CWOverrideRedirect, + &attrs); + + /* Copy the client's shape to the temporary shape_window */ + client_window = meta_core_get_client_xwindow (gdk_display, + frame->xwindow); + + XShapeCombineShape (gdk_display, shape_window, ShapeBounding, + fgeom.left_width, + fgeom.top_height, + client_window, + ShapeBounding, + ShapeSet); + + /* Punch the client area out of the normal frame shape, + * then union it with the shape_window's existing shape + */ + client_xregion = XCreateRegion (); + + xrect.x = fgeom.left_width; + xrect.y = fgeom.top_height; + xrect.width = new_window_width - fgeom.right_width - xrect.x; + xrect.height = new_window_height - fgeom.bottom_height - xrect.y; + + XUnionRectWithRegion (&xrect, client_xregion, client_xregion); + + XSubtractRegion (window_xregion, client_xregion, window_xregion); + + XDestroyRegion (client_xregion); + + XShapeCombineRegion (gdk_display, shape_window, + ShapeBounding, 0, 0, window_xregion, ShapeUnion); + + /* Now copy shape_window shape to the real frame */ + XShapeCombineShape (gdk_display, frame->xwindow, ShapeBounding, + 0, 0, + shape_window, + ShapeBounding, + ShapeSet); + + XDestroyWindow (gdk_display, shape_window); + } + else + { + /* No shape on the client, so just do simple stuff */ + + meta_topic (META_DEBUG_SHAPES, + "Frame 0x%lx has shaped corners\n", + frame->xwindow); + + XShapeCombineRegion (gdk_display, frame->xwindow, + ShapeBounding, 0, 0, window_xregion, ShapeSet); + } + + frame->shape_applied = TRUE; XDestroyRegion (window_xregion); - XDestroyRegion (corners_xregion); #endif } diff --git a/src/frames.h b/src/frames.h index b0febb0a8..182b6516e 100644 --- a/src/frames.h +++ b/src/frames.h @@ -72,7 +72,8 @@ struct _MetaUIFrame int text_height; char *title; /* NULL once we have a layout */ guint expose_delayed : 1; - + guint shape_applied : 1; + /* FIXME get rid of this, it can just be in the MetaFrames struct */ MetaFrameControl prelit_control; }; @@ -127,7 +128,8 @@ void meta_frames_unflicker_bg (MetaFrames *frames, void meta_frames_apply_shapes (MetaFrames *frames, Window xwindow, int new_window_width, - int new_window_height); + int new_window_height, + gboolean window_has_shape); void meta_frames_queue_draw (MetaFrames *frames, Window xwindow); diff --git a/src/prefs.c b/src/prefs.c index 2a88164ec..aadb98e76 100644 --- a/src/prefs.c +++ b/src/prefs.c @@ -1028,7 +1028,12 @@ update_application_based (gboolean value) { gboolean old = application_based; + /* DISABLE application_based feature for now */ +#if 0 application_based = value; +#else + application_based = FALSE; +#endif return old != application_based; } diff --git a/src/ui.c b/src/ui.c index 13542e99e..e86c65b9f 100644 --- a/src/ui.c +++ b/src/ui.c @@ -238,12 +238,15 @@ meta_ui_reset_frame_bg (MetaUI *ui, } void -meta_ui_apply_frame_shape (MetaUI *ui, - Window xwindow, - int new_window_width, - int new_window_height) +meta_ui_apply_frame_shape (MetaUI *ui, + Window xwindow, + int new_window_width, + int new_window_height, + gboolean window_has_shape) { - meta_frames_apply_shapes (ui->frames, xwindow, new_window_width, new_window_height); + meta_frames_apply_shapes (ui->frames, xwindow, + new_window_width, new_window_height, + window_has_shape); } void diff --git a/src/ui.h b/src/ui.h index 488ce8e77..5415beddb 100644 --- a/src/ui.h +++ b/src/ui.h @@ -73,10 +73,11 @@ void meta_ui_unflicker_frame_bg (MetaUI *ui, void meta_ui_reset_frame_bg (MetaUI *ui, Window xwindow); -void meta_ui_apply_frame_shape (MetaUI *ui, - Window xwindow, - int new_window_width, - int new_window_height); +void meta_ui_apply_frame_shape (MetaUI *ui, + Window xwindow, + int new_window_width, + int new_window_height, + gboolean window_has_shape); void meta_ui_queue_frame_draw (MetaUI *ui, Window xwindow); diff --git a/src/util.c b/src/util.c index b54a66ce6..6e43f5fe9 100644 --- a/src/util.c +++ b/src/util.c @@ -282,6 +282,8 @@ topic_name (MetaDebugTopic topic) return "GROUPS"; case META_DEBUG_RESIZING: return "RESIZING"; + case META_DEBUG_SHAPES: + return "SHAPES"; } return "Window manager"; diff --git a/src/util.h b/src/util.h index abf29ea1e..c53230bb4 100644 --- a/src/util.h +++ b/src/util.h @@ -65,7 +65,8 @@ typedef enum META_DEBUG_STARTUP = 1 << 15, META_DEBUG_PREFS = 1 << 16, META_DEBUG_GROUPS = 1 << 17, - META_DEBUG_RESIZING = 1 << 18 + META_DEBUG_RESIZING = 1 << 18, + META_DEBUG_SHAPES = 1 << 19 } MetaDebugTopic; diff --git a/src/window.c b/src/window.c index f6fcaca85..dee569485 100644 --- a/src/window.c +++ b/src/window.c @@ -41,6 +41,10 @@ #include #include +#ifdef HAVE_SHAPE +#include +#endif + typedef enum { META_IS_CONFIGURE_REQUEST = 1 << 0, @@ -159,9 +163,11 @@ meta_window_new (MetaDisplay *display, GSList *tmp; MetaWorkspace *space; gulong existing_wm_state; + gulong event_mask; #define N_INITIAL_PROPS 10 Atom initial_props[N_INITIAL_PROPS]; int i; + gboolean has_shape; g_assert (N_INITIAL_PROPS == (int) G_N_ELEMENTS (initial_props)); @@ -241,11 +247,36 @@ meta_window_new (MetaDisplay *display, XAddToSaveSet (display->xdisplay, xwindow); - XSelectInput (display->xdisplay, xwindow, - PropertyChangeMask | - EnterWindowMask | LeaveWindowMask | - FocusChangeMask | - ColormapChangeMask); + event_mask = + PropertyChangeMask | EnterWindowMask | LeaveWindowMask | + FocusChangeMask | ColormapChangeMask; + + XSelectInput (display->xdisplay, xwindow, event_mask); + + has_shape = FALSE; +#ifdef HAVE_SHAPE + if (META_DISPLAY_HAS_SHAPE (display)) + { + int x_bounding, y_bounding, x_clip, y_clip; + unsigned w_bounding, h_bounding, w_clip, h_clip; + int bounding_shaped, clip_shaped; + + XShapeSelectInput (display->xdisplay, xwindow, ShapeNotifyMask); + + XShapeQueryExtents (display->xdisplay, xwindow, + &bounding_shaped, &x_bounding, &y_bounding, + &w_bounding, &h_bounding, + &clip_shaped, &x_clip, &y_clip, + &w_clip, &h_clip); + + has_shape = bounding_shaped != FALSE; + + meta_topic (META_DEBUG_SHAPES, + "Window has_shape = %d extents %d,%d %d x %d\n", + has_shape, x_bounding, y_bounding, + w_bounding, h_bounding); + } +#endif /* Get rid of any borders */ if (attrs.border_width != 0) @@ -311,6 +342,8 @@ meta_window_new (MetaDisplay *display, /* avoid tons of stack updates */ meta_stack_freeze (window->screen->stack); + + window->has_shape = has_shape; /* Remember this rect is the actual window size */ window->rect.x = attrs.x; @@ -977,6 +1010,11 @@ meta_window_free (MetaWindow *window) XSelectInput (window->display->xdisplay, window->xwindow, NoEventMask); + +#ifdef HAVE_SHAPE + if (META_DISPLAY_HAS_SHAPE (window->display)) + XShapeSelectInput (window->display->xdisplay, window->xwindow, NoEventMask); +#endif meta_error_trap_pop (window->display, FALSE); diff --git a/src/window.h b/src/window.h index c200c9c82..e8541f5c0 100644 --- a/src/window.h +++ b/src/window.h @@ -219,6 +219,9 @@ struct _MetaWindow guint using_net_wm_name : 1; /* vs. plain wm_name */ guint using_net_wm_icon_name : 1; /* vs. plain wm_icon_name */ + /* has a shape mask */ + guint has_shape : 1; + #ifdef HAVE_XSYNC /* XSync update counter */ XSyncCounter update_counter;