/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2007 Iain Holmes * Based on xcompmgr - (c) 2003 Keith Packard * xfwm4 - (c) 2005-2007 Olivier Fourdan * * 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., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301, USA. */ #define _GNU_SOURCE #define _XOPEN_SOURCE 500 /* for usleep() */ #include #ifdef HAVE_COMPOSITE_EXTENSIONS #include #include #include #include #include #include #include #include #include "display.h" #include "../core/display-private.h" #include "../core/screen-private.h" #include "../core/workspace.h" #include "screen.h" #include "frame.h" #include "errors.h" #include "window.h" #include "compositor-private.h" #include "compositor-xrender.h" #include "xprops.h" #include "core.h" #include #include #include #include #include #include #ifdef HAVE_PRESENT #include #endif #define USE_IDLE_REPAINT 1 typedef enum _MetaCompWindowType { META_COMP_WINDOW_NORMAL, META_COMP_WINDOW_DND, META_COMP_WINDOW_DESKTOP, META_COMP_WINDOW_DOCK, META_COMP_WINDOW_MENU, META_COMP_WINDOW_DROP_DOWN_MENU, META_COMP_WINDOW_TOOLTIP, } MetaCompWindowType; typedef enum _MetaShadowType { META_SHADOW_SMALL, META_SHADOW_MEDIUM, META_SHADOW_LARGE, LAST_SHADOW_TYPE } MetaShadowType; typedef struct _MetaCompositorXRender { MetaCompositor compositor; MetaDisplay *display; Atom atom_x_root_pixmap; Atom atom_x_set_root; Atom atom_net_wm_window_opacity; Atom atom_net_wm_window_type_dnd; Atom atom_net_wm_window_type; Atom atom_net_wm_window_type_desktop; Atom atom_net_wm_window_type_dock; Atom atom_net_wm_window_type_menu; Atom atom_net_wm_window_type_dialog; Atom atom_net_wm_window_type_normal; Atom atom_net_wm_window_type_utility; Atom atom_net_wm_window_type_splash; Atom atom_net_wm_window_type_toolbar; Atom atom_net_wm_window_type_dropdown_menu; Atom atom_net_wm_window_type_tooltip; #ifdef USE_IDLE_REPAINT guint repaint_id; #endif guint enabled : 1; guint show_redraw : 1; guint debug : 1; #ifdef HAVE_PRESENT guint has_present : 1; int present_major; #endif /* HAVE_PRESENT */ } MetaCompositorXRender; typedef struct _conv { int size; double *data; } conv; typedef struct _shadow { conv *gaussian_map; guchar *shadow_corner; guchar *shadow_top; } shadow; #define NUM_BUFFER 2 typedef struct _MetaCompScreen { MetaScreen *screen; GList *windows; GHashTable *windows_by_xid; MetaWindow *focus_window; Window output; gboolean have_shadows; shadow *shadows[LAST_SHADOW_TYPE]; Picture root_picture; Picture root_buffers[NUM_BUFFER]; Pixmap root_pixmaps[NUM_BUFFER]; int root_current; Picture black_picture; Picture trans_black_picture; Picture root_tile; XserverRegion all_damage; #ifdef HAVE_PRESENT XserverRegion prev_damage; XID present_eid; gboolean use_present; gboolean present_pending; #endif /* HAVE_PRESENT */ guint overlays; gboolean compositor_active; gboolean clip_changed; GSList *dock_windows; } MetaCompScreen; typedef struct _MetaCompWindow { MetaScreen *screen; MetaWindow *window; /* May be NULL if this window isn't managed by Marco */ Window id; XWindowAttributes attrs; Pixmap back_pixmap; /* When the window is shaded back_pixmap will be replaced with the pixmap for the shaded window. This is a copy of the original unshaded window so that we can still see what the window looked like when it is needed for the _get_window_pixmap function */ Pixmap shaded_back_pixmap; int mode; gboolean damaged; gboolean shaped; XRectangle shape_bounds; MetaCompWindowType type; Damage damage; Picture picture; Picture alpha_pict; gboolean needs_shadow; MetaShadowType shadow_type; Picture shadow_pict; XserverRegion border_size; XserverRegion extents; Picture shadow; int shadow_dx; int shadow_dy; int shadow_width; int shadow_height; guint opacity; XserverRegion border_clip; gboolean updates_frozen; gboolean update_pending; } MetaCompWindow; #define OPAQUE 0xffffffff #define WINDOW_SOLID 0 #define WINDOW_ARGB 1 #define SHADOW_SMALL_RADIUS 3.0 #define SHADOW_MEDIUM_RADIUS 6.0 #define SHADOW_LARGE_RADIUS 12.0 #define SHADOW_SMALL_OFFSET_X (SHADOW_SMALL_RADIUS * -3 / 2) #define SHADOW_SMALL_OFFSET_Y (SHADOW_SMALL_RADIUS * -3 / 2) #define SHADOW_MEDIUM_OFFSET_X (SHADOW_MEDIUM_RADIUS * -3 / 2) #define SHADOW_MEDIUM_OFFSET_Y (SHADOW_MEDIUM_RADIUS * -5 / 4) #define SHADOW_LARGE_OFFSET_X -15 #define SHADOW_LARGE_OFFSET_Y -15 #define SHADOW_OPACITY 0.66 #define TRANS_OPACITY 0.75 #define DISPLAY_COMPOSITOR(display) ((MetaCompositorXRender *) meta_display_get_compositor (display)) /* Gaussian stuff for creating the shadows */ static double gaussian (double r, double x, double y) { return ((1 / (sqrt (2 * G_PI * r))) * exp ((- (x * x + y * y)) / (2 * r * r))); } static conv * make_gaussian_map (double r) { conv *c; int size, centre; int x, y; double t, g; size = ((int) ceil ((r * 3)) + 1) & ~1; centre = size / 2; c = g_malloc (sizeof (conv) + size * size * sizeof (double)); c->size = size; c->data = (double *) (c + 1); t = 0.0; for (y = 0; y < size; y++) { for (x = 0; x < size; x++) { g = gaussian (r, (double) (x - centre), (double) (y - centre)); t += g; c->data[y * size + x] = g; } } for (y = 0; y < size; y++) { for (x = 0; x < size; x++) { c->data[y * size + x] /= t; } } return c; } static void dump_xserver_region (const char *location, MetaDisplay *display, XserverRegion region) { MetaCompositorXRender *compositor = DISPLAY_COMPOSITOR (display); Display *xdisplay = meta_display_get_xdisplay (display); int nrects; XRectangle *rects; XRectangle bounds; if (!compositor->debug) return; if (region) { rects = XFixesFetchRegionAndBounds (xdisplay, region, &nrects, &bounds); if (nrects > 0) { int i; fprintf (stderr, "%s (XSR): %d rects, bounds: %d,%d (%d,%d)\n", location, nrects, bounds.x, bounds.y, bounds.width, bounds.height); for (i = 1; i < nrects; i++) fprintf (stderr, "\t%d,%d (%d,%d)\n", rects[i].x, rects[i].y, rects[i].width, rects[i].height); } else fprintf (stderr, "%s (XSR): empty\n", location); XFree (rects); } else fprintf (stderr, "%s (XSR): null\n", location); } /* * A picture will help * * -center 0 width width+center * -center +-----+-------------------+-----+ * | | | | * | | | | * 0 +-----+-------------------+-----+ * | | | | * | | | | * | | | | * height +-----+-------------------+-----+ * | | | | * height+ | | | | * center +-----+-------------------+-----+ */ static guchar sum_gaussian (conv *map, double opacity, int x, int y, int width, int height) { double *g_data, *g_line; double v; int fx, fy; int fx_start, fx_end; int fy_start, fy_end; int g_size, centre; g_line = map->data; g_size = map->size; centre = g_size / 2; fx_start = centre - x; if (fx_start < 0) fx_start = 0; fx_end = width + centre - x; if (fx_end > g_size) fx_end = g_size; fy_start = centre - y; if (fy_start < 0) fy_start = 0; fy_end = height + centre - y; if (fy_end > g_size) fy_end = g_size; g_line = g_line + fy_start * g_size + fx_start; v = 0.0; for (fy = fy_start; fy < fy_end; fy++) { g_data = g_line; g_line += g_size; for (fx = fx_start; fx < fx_end; fx++) v += *g_data++; } if (v > 1.0) v = 1.0; return ((guchar) (v * opacity * 255.0)); } /* precompute shadow corners and sides to save time for large windows */ static void presum_gaussian (shadow *shad) { int centre; int opacity, x, y; int msize; conv *map; map = shad->gaussian_map; msize = map->size; centre = map->size / 2; if (shad->shadow_corner) g_free (shad->shadow_corner); if (shad->shadow_top) g_free (shad->shadow_top); shad->shadow_corner = (guchar *)(g_malloc ((msize + 1) * (msize + 1) * 26)); shad->shadow_top = (guchar *) (g_malloc ((msize + 1) * 26)); for (x = 0; x <= msize; x++) { shad->shadow_top[25 * (msize + 1) + x] = sum_gaussian (map, 1, x - centre, centre, msize * 2, msize * 2); for (opacity = 0; opacity < 25; opacity++) { shad->shadow_top[opacity * (msize + 1) + x] = shad->shadow_top[25 * (msize + 1) + x] * opacity / 25; } for (y = 0; y <= x; y++) { shad->shadow_corner[25 * (msize + 1) * (msize + 1) + y * (msize + 1) + x] = sum_gaussian (map, 1, x - centre, y - centre, msize * 2, msize * 2); shad->shadow_corner[25 * (msize + 1) * (msize + 1) + x * (msize + 1) + y] = shad->shadow_corner[25 * (msize + 1) * (msize + 1) + y * (msize + 1) + x]; for (opacity = 0; opacity < 25; opacity++) { shad->shadow_corner[opacity * (msize + 1) * (msize + 1) + y * (msize + 1) + x] = shad->shadow_corner[opacity * (msize + 1) * (msize + 1) + x * (msize + 1) + y] = shad->shadow_corner[25 * (msize + 1) * (msize + 1) + y * (msize + 1) + x] * opacity / 25; } } } } static void generate_shadows (MetaCompScreen *info) { double radii[LAST_SHADOW_TYPE] = {SHADOW_SMALL_RADIUS, SHADOW_MEDIUM_RADIUS, SHADOW_LARGE_RADIUS}; int i; for (i = 0; i < LAST_SHADOW_TYPE; i++) { shadow *shad = g_new0 (shadow, 1); shad->gaussian_map = make_gaussian_map (radii[i]); presum_gaussian (shad); info->shadows[i] = shad; } } static XImage * make_shadow (MetaDisplay *display, MetaScreen *screen, MetaShadowType shadow_type, double opacity, int width, int height) { MetaCompScreen *info = meta_screen_get_compositor_data (screen); Display *xdisplay = meta_display_get_xdisplay (display); XImage *ximage; guchar *data; shadow *shad; int msize; int ylimit, xlimit; int swidth, sheight; int centre; int x, y; guchar d; int x_diff; int opacity_int = (int)(opacity * 25); int screen_number = meta_screen_get_screen_number (screen); if (info==NULL) { return NULL; } shad = info->shadows[shadow_type]; msize = shad->gaussian_map->size; swidth = width + msize; sheight = height + msize; centre = msize / 2; data = g_malloc (swidth * sheight * sizeof (guchar)); ximage = XCreateImage (xdisplay, DefaultVisual (xdisplay, screen_number), 8, ZPixmap, 0, (char *) data, swidth, sheight, 8, swidth * sizeof (guchar)); if (!ximage) { g_free (data); return NULL; } /* * Build the gaussian in sections */ /* * centre (fill the complete data array */ if (msize > 0) d = shad->shadow_top[opacity_int * (msize + 1) + msize]; else d = sum_gaussian (shad->gaussian_map, opacity, centre, centre, width, height); memset (data, d, sheight * swidth); /* * corners */ ylimit = msize; if (ylimit > sheight / 2) ylimit = (sheight + 1) / 2; xlimit = msize; if (xlimit > swidth / 2) xlimit = (swidth + 1) / 2; for (y = 0; y < ylimit; y++) { for (x = 0; x < xlimit; x++) { if (xlimit == msize && ylimit == msize) d = shad->shadow_corner[opacity_int * (msize + 1) * (msize + 1) + y * (msize + 1) + x]; else d = sum_gaussian (shad->gaussian_map, opacity, x - centre, y - centre, width, height); data[y * swidth + x] = d; data[(sheight - y - 1) * swidth + x] = d; data[(sheight - y - 1) * swidth + (swidth - x - 1)] = d; data[y * swidth + (swidth - x - 1)] = d; } } /* top/bottom */ x_diff = swidth - (msize * 2); if (x_diff > 0 && ylimit > 0) { for (y = 0; y < ylimit; y++) { if (ylimit == msize) d = shad->shadow_top[opacity_int * (msize + 1) + y]; else d = sum_gaussian (shad->gaussian_map, opacity, centre, y - centre, width, height); memset (&data[y * swidth + msize], d, x_diff); memset (&data[(sheight - y - 1) * swidth + msize], d, x_diff); } } /* * sides */ for (x = 0; x < xlimit; x++) { if (xlimit == msize) d = shad->shadow_top[opacity_int * (msize + 1) + x]; else d = sum_gaussian (shad->gaussian_map, opacity, x - centre, centre, width, height); for (y = msize; y < sheight - msize; y++) { data[y * swidth + x] = d; data[y * swidth + (swidth - x - 1)] = d; } } ximage->data = (char *) data; return ximage; } double shadow_offsets_x[LAST_SHADOW_TYPE] = {SHADOW_SMALL_OFFSET_X, SHADOW_MEDIUM_OFFSET_X, SHADOW_LARGE_OFFSET_X}; double shadow_offsets_y[LAST_SHADOW_TYPE] = {SHADOW_SMALL_OFFSET_Y, SHADOW_MEDIUM_OFFSET_Y, SHADOW_LARGE_OFFSET_Y}; static XserverRegion cairo_region_to_xserver_region (Display *xdisplay, cairo_region_t *region) { int n_rects, i; XRectangle *rects; XserverRegion xregion; n_rects = cairo_region_num_rectangles (region); rects = g_new (XRectangle, n_rects); for (i = 0; i < n_rects; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (region, i, &rect); rects[i].x = rect.x; rects[i].y = rect.y; rects[i].width = rect.width; rects[i].height = rect.height; } xregion = XFixesCreateRegion (xdisplay, rects, n_rects); g_free (rects); return xregion; } static void shadow_picture_clip (Display *xdisplay, Picture shadow_picture, MetaCompWindow *cw, MetaFrameBorders borders, int width, int height) { int shadow_dx; int shadow_dy; cairo_region_t *visible_region; XRectangle rect; XserverRegion region1; XserverRegion region2; if (!cw->window) return; visible_region = meta_window_get_frame_bounds (cw->window); if (visible_region == NULL) return; shadow_dx = -1 * (int) shadow_offsets_x [cw->shadow_type] - borders.invisible.left; shadow_dy = -1 * (int) shadow_offsets_y [cw->shadow_type] - borders.invisible.top; rect.x = 0; rect.y = 0; rect.width = width; rect.height = height; region1 = XFixesCreateRegion (xdisplay, &rect, 1); region2 = cairo_region_to_xserver_region (xdisplay, visible_region); XFixesTranslateRegion (xdisplay, region2, shadow_dx, shadow_dy); XFixesSubtractRegion (xdisplay, region1, region1, region2); XFixesSetPictureClipRegion (xdisplay, shadow_picture, 0, 0, region1); XFixesDestroyRegion (xdisplay, region1); XFixesDestroyRegion (xdisplay, region2); } static Picture shadow_picture (MetaDisplay *display, MetaScreen *screen, MetaCompWindow *cw, double opacity, MetaFrameBorders borders, int width, int height, int *wp, int *hp) { Display *xdisplay = meta_display_get_xdisplay (display); XImage *shadow_image; Pixmap shadow_pixmap; Picture shadow_picture; Window xroot = meta_screen_get_xroot (screen); GC gc; shadow_image = make_shadow (display, screen, cw->shadow_type, opacity, width, height); if (!shadow_image) return None; shadow_pixmap = XCreatePixmap (xdisplay, xroot, shadow_image->width, shadow_image->height, 8); if (!shadow_pixmap) { XDestroyImage (shadow_image); return None; } shadow_picture = XRenderCreatePicture (xdisplay, shadow_pixmap, XRenderFindStandardFormat (xdisplay, PictStandardA8), 0, 0); if (!shadow_picture) { XDestroyImage (shadow_image); XFreePixmap (xdisplay, shadow_pixmap); return None; } shadow_picture_clip (xdisplay, shadow_picture, cw, borders, shadow_image->width, shadow_image->height); gc = XCreateGC (xdisplay, shadow_pixmap, 0, 0); if (!gc) { XDestroyImage (shadow_image); XFreePixmap (xdisplay, shadow_pixmap); XRenderFreePicture (xdisplay, shadow_picture); return None; } XPutImage (xdisplay, shadow_pixmap, gc, shadow_image, 0, 0, 0, 0, shadow_image->width, shadow_image->height); *wp = shadow_image->width; *hp = shadow_image->height; XFreeGC (xdisplay, gc); XDestroyImage (shadow_image); XFreePixmap (xdisplay, shadow_pixmap); return shadow_picture; } static MetaCompWindow * find_window_for_screen (MetaScreen *screen, Window xwindow) { MetaCompScreen *info = meta_screen_get_compositor_data (screen); if (info == NULL) return NULL; return g_hash_table_lookup (info->windows_by_xid, (gpointer) xwindow); } static MetaCompWindow * find_window_in_display (MetaDisplay *display, Window xwindow) { GSList *index; for (index = meta_display_get_screens (display); index; index = index->next) { MetaCompWindow *cw = find_window_for_screen (index->data, xwindow); if (cw != NULL) return cw; } return NULL; } static MetaCompWindow * find_window_for_child_window_in_display (MetaDisplay *display, Window xwindow) { Window ignored1, *ignored2; Window parent; guint ignored_children; XQueryTree (meta_display_get_xdisplay (display), xwindow, &ignored1, &parent, &ignored2, &ignored_children); if (parent != None) return find_window_in_display (display, parent); return NULL; } #ifdef HAVE_PRESENT static MetaScreen * find_screen_from_output(MetaDisplay *display, Window output) { int i; Display *xdisplay = meta_display_get_xdisplay(display); for (i = 0; i < ScreenCount(xdisplay); i++) { MetaScreen *screen = meta_display_screen_for_x_screen(display, ScreenOfDisplay(xdisplay, i)); MetaCompScreen *info = meta_screen_get_compositor_data(screen); if (info->output == output) return screen; } return NULL; } #endif /* HAVE_PRESENT */ static Picture solid_picture (MetaDisplay *display, MetaScreen *screen, gboolean argb, double a, double r, double g, double b) { Display *xdisplay = meta_display_get_xdisplay (display); Pixmap pixmap; Picture picture; XRenderPictureAttributes pa; XRenderPictFormat *render_format; XRenderColor c; Window xroot = meta_screen_get_xroot (screen); render_format = XRenderFindStandardFormat (xdisplay, argb ? PictStandardARGB32 : PictStandardA8); pixmap = XCreatePixmap (xdisplay, xroot, 1, 1, argb ? 32 : 8); g_return_val_if_fail (pixmap != None, None); pa.repeat = TRUE; picture = XRenderCreatePicture (xdisplay, pixmap, render_format, CPRepeat, &pa); if (picture == None) { XFreePixmap (xdisplay, pixmap); g_warning ("(picture != None) failed"); return None; } c.alpha = a * 0xffff; c.red = r * 0xffff; c.green = g * 0xffff; c.blue = b * 0xffff; XRenderFillRectangle (xdisplay, PictOpSrc, picture, &c, 0, 0, 1, 1); XFreePixmap (xdisplay, pixmap); return picture; } static Picture root_tile (MetaScreen *screen) { MetaDisplay *display = meta_screen_get_display (screen); Display *xdisplay = meta_display_get_xdisplay (display); Picture picture; Pixmap pixmap; gboolean fill = FALSE; XRenderPictureAttributes pa; XRenderPictFormat *format; int p; Atom background_atoms[2]; Atom pixmap_atom; int screen_number = meta_screen_get_screen_number (screen); Window xroot = meta_screen_get_xroot (screen); pixmap = None; background_atoms[0] = DISPLAY_COMPOSITOR (display)->atom_x_root_pixmap; background_atoms[1] = DISPLAY_COMPOSITOR (display)->atom_x_set_root; pixmap_atom = XInternAtom (xdisplay, "PIXMAP", False); for (p = 0; p < 2; p++) { Atom actual_type; int actual_format; gulong nitems, bytes_after; guchar *prop; if (XGetWindowProperty (xdisplay, xroot, background_atoms[p], 0, 4, FALSE, AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &prop) == Success) { if (actual_type == pixmap_atom && actual_format == 32 && nitems == 1) { memcpy (&pixmap, prop, 4); XFree (prop); fill = FALSE; break; } } } if (!pixmap) { pixmap = XCreatePixmap (xdisplay, xroot, 1, 1, DefaultDepth (xdisplay, screen_number)); g_return_val_if_fail (pixmap != None, None); fill = TRUE; } pa.repeat = TRUE; format = XRenderFindVisualFormat (xdisplay, DefaultVisual (xdisplay, screen_number)); g_return_val_if_fail (format != NULL, None); picture = XRenderCreatePicture (xdisplay, pixmap, format, CPRepeat, &pa); if ((picture != None) && (fill)) { XRenderColor c; /* Background default to just plain black */ c.red = 0x0000; c.green = 0x0000; c.blue = 0x0000; c.alpha = 0xffff; XRenderFillRectangle (xdisplay, PictOpSrc, picture, &c, 0, 0, 1, 1); XFreePixmap (xdisplay, pixmap); } return picture; } static Pixmap create_root_pixmap (MetaScreen *screen) { MetaDisplay *display = meta_screen_get_display (screen); Display *xdisplay = meta_display_get_xdisplay (display); MetaCompScreen *info = meta_screen_get_compositor_data (screen); Window xroot = meta_screen_get_xroot (screen); Pixmap pixmap; int depth, screen_width, screen_height, screen_number; if (info == NULL) { return None; } meta_screen_get_size (screen, &screen_width, &screen_height); screen_number = meta_screen_get_screen_number (screen); depth = DefaultDepth (xdisplay, screen_number); pixmap = XCreatePixmap (xdisplay, xroot, screen_width, screen_height, depth); return pixmap; } static Picture create_root_buffer (MetaScreen *screen, Pixmap root_pixmap) { MetaDisplay *display = meta_screen_get_display (screen); Display *xdisplay = meta_display_get_xdisplay (display); MetaCompScreen *info = meta_screen_get_compositor_data (screen); Picture pict; XRenderPictFormat *format; Visual *visual; int screen_number; if (info == NULL) { return None; } g_return_val_if_fail (root_pixmap != None, None); screen_number = meta_screen_get_screen_number (screen); visual = DefaultVisual (xdisplay, screen_number); format = XRenderFindVisualFormat (xdisplay, visual); g_return_val_if_fail (format != NULL, None); pict = XRenderCreatePicture (xdisplay, root_pixmap, format, 0, NULL); return pict; } static void paint_root (MetaScreen *screen, Picture root_buffer) { MetaDisplay *display = meta_screen_get_display (screen); Display *xdisplay = meta_display_get_xdisplay (display); MetaCompScreen *info = meta_screen_get_compositor_data (screen); int width, height; if (info == NULL) { return; } g_return_if_fail (root_buffer != None); if (info->root_tile == None) { info->root_tile = root_tile (screen); g_return_if_fail (info->root_tile != None); } meta_screen_get_size (screen, &width, &height); XRenderComposite (xdisplay, PictOpSrc, info->root_tile, None, root_buffer, 0, 0, 0, 0, 0, 0, width, height); } static gboolean window_has_shadow (MetaCompWindow *cw) { MetaCompScreen *info = meta_screen_get_compositor_data (cw->screen); if (info == NULL || info->have_shadows == FALSE) return FALSE; /* Always put a shadow around windows with a frame - This should override the restriction about not putting a shadow around shaped windows as the frame might be the reason the window is shaped */ if (cw->window) { /* Do not add shadows for maximized windows */ if (meta_window_is_maximized (cw->window)) { meta_verbose ("Window has no shadow because it is maximized\n"); return FALSE; } if (meta_window_get_frame (cw->window)) { meta_verbose ("Window has shadow because it has a frame\n"); return TRUE; } } /* Do not add shadows to ARGB windows */ if (cw->mode == WINDOW_ARGB) { meta_verbose ("Window has no shadow as it is ARGB\n"); return FALSE; } /* Never put a shadow around shaped windows */ if (cw->shaped) { meta_verbose ("Window has no shadow as it is shaped\n"); return FALSE; } /* Don't put shadow around DND icon windows */ if (cw->type == META_COMP_WINDOW_DND || cw->type == META_COMP_WINDOW_DESKTOP) { meta_verbose ("Window has no shadow as it is DND or Desktop\n"); return FALSE; } if (cw->mode != WINDOW_ARGB) { meta_verbose ("Window has shadow as it is not ARGB\n"); return TRUE; } if (cw->type == META_COMP_WINDOW_MENU || cw->type == META_COMP_WINDOW_DROP_DOWN_MENU) { meta_verbose ("Window has shadow as it is a menu\n"); return TRUE; } if (cw->type == META_COMP_WINDOW_TOOLTIP) { meta_verbose ("Window has shadow as it is a tooltip\n"); return TRUE; } meta_verbose ("Window has no shadow as it fell through\n"); return FALSE; } static XserverRegion win_extents (MetaCompWindow *cw) { MetaScreen *screen = cw->screen; MetaDisplay *display = meta_screen_get_display (screen); Display *xdisplay = meta_display_get_xdisplay (display); XRectangle r; r.x = cw->attrs.x; r.y = cw->attrs.y; r.width = cw->attrs.width + cw->attrs.border_width * 2; r.height = cw->attrs.height + cw->attrs.border_width * 2; if (cw->needs_shadow) { MetaFrameBorders borders; XRectangle sr; meta_frame_borders_clear (&borders); if (cw->window) { MetaFrame *frame = meta_window_get_frame (cw->window); if (frame) meta_frame_calc_borders (frame, &borders); } cw->shadow_dx = (int) shadow_offsets_x [cw->shadow_type] + borders.invisible.left; cw->shadow_dy = (int) shadow_offsets_y [cw->shadow_type] + borders.invisible.top; if (!cw->shadow) { double opacity = SHADOW_OPACITY; int invisible_width = borders.invisible.left + borders.invisible.right; int invisible_height = borders.invisible.top + borders.invisible.bottom; if (cw->opacity != (guint) OPAQUE) opacity = opacity * ((double) cw->opacity) / ((double) OPAQUE); cw->shadow = shadow_picture (display, screen, cw, opacity, borders, cw->attrs.width - invisible_width + cw->attrs.border_width * 2, cw->attrs.height - invisible_height + cw->attrs.border_width * 2, &cw->shadow_width, &cw->shadow_height); } sr.x = cw->attrs.x + cw->shadow_dx; sr.y = cw->attrs.y + cw->shadow_dy; sr.width = cw->shadow_width; sr.height = cw->shadow_height; if (sr.x < r.x) { r.width = (r.x + r.width) - sr.x; r.x = sr.x; } if (sr.y < r.y) { r.height = (r.y + r.height) - sr.y; r.y = sr.y; } if (sr.x + sr.width > r.x + r.width) r.width = sr.x + sr.width - r.x; if (sr.y + sr.height > r.y + r.height) r.height = sr.y + sr.height - r.y; } return XFixesCreateRegion (xdisplay, &r, 1); } static XserverRegion border_size (MetaCompWindow *cw) { MetaScreen *screen = cw->screen; MetaDisplay *display = meta_screen_get_display (screen); Display *xdisplay = meta_display_get_xdisplay (display); cairo_region_t *visible_region; XserverRegion visible = None; XserverRegion border; if (cw->window) { visible_region = meta_window_get_frame_bounds (cw->window); if (visible_region != NULL) visible = cairo_region_to_xserver_region (xdisplay, visible_region); } meta_error_trap_push (display); border = XFixesCreateRegionFromWindow (xdisplay, cw->id, WindowRegionBounding); meta_error_trap_pop (display, FALSE); g_return_val_if_fail (border != None, None); XFixesTranslateRegion (xdisplay, border, cw->attrs.x + cw->attrs.border_width, cw->attrs.y + cw->attrs.border_width); if (visible != None) { XFixesTranslateRegion (xdisplay, visible, cw->attrs.x + cw->attrs.border_width, cw->attrs.y + cw->attrs.border_width); XFixesIntersectRegion (xdisplay, visible, visible, border); XFixesDestroyRegion (xdisplay, border); return visible; } return border; } static XRenderPictFormat * get_window_format (MetaCompWindow *cw) { MetaScreen *screen = cw->screen; MetaDisplay *display = meta_screen_get_display (screen); Display *xdisplay = meta_display_get_xdisplay (display); XRenderPictFormat *format; int screen_number = meta_screen_get_screen_number (screen); format = XRenderFindVisualFormat (xdisplay, cw->attrs.visual); if (!format) format = XRenderFindVisualFormat (xdisplay, DefaultVisual (xdisplay, screen_number)); return format; } static Picture get_window_picture (MetaCompWindow *cw) { MetaScreen *screen = cw->screen; MetaDisplay *display = meta_screen_get_display (screen); Display *xdisplay = meta_display_get_xdisplay (display); XRenderPictureAttributes pa; XRenderPictFormat *format; Drawable draw; int error_code; draw = cw->id; meta_error_trap_push (display); if (cw->back_pixmap == None) cw->back_pixmap = XCompositeNameWindowPixmap (xdisplay, cw->id); error_code = meta_error_trap_pop_with_return (display, FALSE); if (error_code != 0) cw->back_pixmap = None; if (cw->back_pixmap != None) draw = cw->back_pixmap; format = get_window_format (cw); if (format) { Picture pict; pa.subwindow_mode = IncludeInferiors; meta_error_trap_push (display); pict = XRenderCreatePicture (xdisplay, draw, format, CPSubwindowMode, &pa); meta_error_trap_pop (display, FALSE); return pict; } return None; } static void paint_dock_shadows (MetaScreen *screen, Picture root_buffer, XserverRegion region) { MetaDisplay *display = meta_screen_get_display (screen); Display *xdisplay = meta_display_get_xdisplay (display); MetaCompScreen *info = meta_screen_get_compositor_data (screen); GSList *d; if (info == NULL) { return; } for (d = info->dock_windows; d; d = d->next) { MetaCompWindow *cw = d->data; XserverRegion shadow_clip; if (cw->shadow) { shadow_clip = XFixesCreateRegion (xdisplay, NULL, 0); XFixesIntersectRegion (xdisplay, shadow_clip, cw->border_clip, region); XFixesSetPictureClipRegion (xdisplay, root_buffer, 0, 0, shadow_clip); XRenderComposite (xdisplay, PictOpOver, info->black_picture, cw->shadow, root_buffer, 0, 0, 0, 0, cw->attrs.x + cw->shadow_dx, cw->attrs.y + cw->shadow_dy, cw->shadow_width, cw->shadow_height); XFixesDestroyRegion (xdisplay, shadow_clip); } } } #ifdef HAVE_PRESENT static gboolean present_flip (MetaScreen *screen, XserverRegion region, Pixmap pixmap) { static uint32_t present_serial; gboolean debug; MetaCompScreen *info = meta_screen_get_compositor_data (screen); MetaDisplay *display = meta_screen_get_display (screen); Display *xdisplay = meta_display_get_xdisplay (display); meta_error_trap_push (display); XPresentPixmap(xdisplay, info->output, pixmap, present_serial, None, region, 0, 0, None, None, None, PresentOptionNone, 0, 1, 0, NULL, 0); int error_code; error_code = meta_error_trap_pop_with_return (display, FALSE); if (error_code) { debug = DISPLAY_COMPOSITOR (display)->debug; if (debug) fprintf (stderr, "XPresentPixmap window %p pixmap %p error: %i\n", (void *)info->output, (void *)pixmap, error_code); if (error_code == BadWindow) g_warning ("XPresent is not compatible with your current system configuration."); /* Disable the Present extension for this session to prevent frozen windows */ info->use_present = FALSE; return FALSE; } present_serial++; return TRUE; } #endif /* HAVE_PRESENT */ static void paint_windows (MetaScreen *screen, GList *windows, Picture root_buffer, Pixmap root_pixmap, XserverRegion region) { MetaDisplay *display = meta_screen_get_display (screen); MetaCompScreen *info = meta_screen_get_compositor_data (screen); Display *xdisplay = meta_display_get_xdisplay (display); GList *index, *last; int screen_width, screen_height; MetaCompWindow *cw; XserverRegion paint_region, desktop_region; if (info == NULL) { return; } meta_screen_get_size (screen, &screen_width, &screen_height); if (region == None) { XRectangle r; r.x = 0; r.y = 0; r.width = screen_width; r.height = screen_height; paint_region = XFixesCreateRegion (xdisplay, &r, 1); } else { paint_region = XFixesCreateRegion (xdisplay, NULL, 0); XFixesCopyRegion (xdisplay, paint_region, region); } desktop_region = None; /* * Painting from top to bottom, reducing the clipping area at * each iteration. Only the opaque windows are painted 1st. */ last = NULL; for (index = windows; index; index = index->next) { /* Store the last window we dealt with */ last = index; cw = (MetaCompWindow *) index->data; if (!cw->damaged) { /* Not damaged */ continue; } if (cw->attrs.map_state != IsViewable) continue; #if 0 if ((cw->attrs.x + cw->attrs.width < 1) || (cw->attrs.y + cw->attrs.height < 1) || (cw->attrs.x >= screen_width) || (cw->attrs.y >= screen_height)) { /* Off screen */ continue; } #endif if (cw->picture == None) cw->picture = get_window_picture (cw); /* If the clip region of the screen has been changed then we need to recreate the extents of the window */ if (info->clip_changed) { if (cw->border_size) { XFixesDestroyRegion (xdisplay, cw->border_size); cw->border_size = None; } #if 0 if (cw->extents) { XFixesDestroyRegion (xdisplay, cw->extents); cw->extents = None; } #endif } if (cw->border_size == None) cw->border_size = border_size (cw); if (cw->extents == None) cw->extents = win_extents (cw); if (cw->mode == WINDOW_SOLID) { int x, y, wid, hei; x = cw->attrs.x; y = cw->attrs.y; wid = cw->attrs.width + cw->attrs.border_width * 2; hei = cw->attrs.height + cw->attrs.border_width * 2; XFixesSetPictureClipRegion (xdisplay, root_buffer, 0, 0, paint_region); XRenderComposite (xdisplay, PictOpSrc, cw->picture, None, root_buffer, 0, 0, 0, 0, x, y, wid, hei); if (cw->type == META_COMP_WINDOW_DESKTOP) { if(desktop_region) { XFixesUnionRegion (xdisplay, desktop_region, desktop_region, paint_region); } else { desktop_region = XFixesCreateRegion (xdisplay, 0, 0); XFixesCopyRegion (xdisplay, desktop_region, paint_region); } } XFixesSubtractRegion (xdisplay, paint_region, paint_region, cw->border_size); } if (!cw->border_clip) { cw->border_clip = XFixesCreateRegion (xdisplay, 0, 0); XFixesCopyRegion (xdisplay, cw->border_clip, paint_region); } } XFixesSetPictureClipRegion (xdisplay, root_buffer, 0, 0, paint_region); paint_root (screen, root_buffer); paint_dock_shadows (screen, root_buffer, desktop_region == None ? paint_region : desktop_region); if (desktop_region != None) XFixesDestroyRegion (xdisplay, desktop_region); /* * Painting from bottom to top, translucent windows and shadows are painted */ for (index = last; index; index = index->prev) { cw = (MetaCompWindow *) index->data; if (cw->picture) { if (cw->shadow && cw->type != META_COMP_WINDOW_DOCK) { XserverRegion shadow_clip; shadow_clip = XFixesCreateRegion (xdisplay, NULL, 0); XFixesSubtractRegion (xdisplay, shadow_clip, cw->border_clip, cw->border_size); XFixesSetPictureClipRegion (xdisplay, root_buffer, 0, 0, shadow_clip); XRenderComposite (xdisplay, PictOpOver, info->black_picture, cw->shadow, root_buffer, 0, 0, 0, 0, cw->attrs.x + cw->shadow_dx, cw->attrs.y + cw->shadow_dy, cw->shadow_width, cw->shadow_height); if (shadow_clip) XFixesDestroyRegion (xdisplay, shadow_clip); } if ((cw->opacity != (guint) OPAQUE) && !(cw->alpha_pict)) { cw->alpha_pict = solid_picture (display, screen, FALSE, (double) cw->opacity / OPAQUE, 0, 0, 0); } XFixesIntersectRegion (xdisplay, cw->border_clip, cw->border_clip, cw->border_size); XFixesSetPictureClipRegion (xdisplay, root_buffer, 0, 0, cw->border_clip); if (cw->mode == WINDOW_ARGB) { int x, y, wid, hei; x = cw->attrs.x; y = cw->attrs.y; wid = cw->attrs.width + cw->attrs.border_width * 2; hei = cw->attrs.height + cw->attrs.border_width * 2; XRenderComposite (xdisplay, PictOpOver, cw->picture, cw->alpha_pict, root_buffer, 0, 0, 0, 0, x, y, wid, hei); } } if (cw->border_clip) { XFixesDestroyRegion (xdisplay, cw->border_clip); cw->border_clip = None; } } XFixesSetPictureClipRegion (xdisplay, root_buffer, 0, 0, region); #ifdef HAVE_PRESENT if (info->use_present) info->present_pending = present_flip (screen, region, root_pixmap); if (!info->use_present || !info->present_pending) #endif /* HAVE_PRESENT */ { XRenderComposite (xdisplay, PictOpSrc, root_buffer, None, info->root_picture, 0, 0, 0, 0, 0, 0, screen_width, screen_height); } XFlush (xdisplay); XFixesDestroyRegion (xdisplay, paint_region); } static void paint_all (MetaScreen *screen, XserverRegion region, int b) { MetaCompScreen *info = meta_screen_get_compositor_data (screen); MetaDisplay *display = meta_screen_get_display (screen); Display *xdisplay = meta_display_get_xdisplay (display); int screen_width, screen_height; meta_screen_get_size (screen, &screen_width, &screen_height); if (DISPLAY_COMPOSITOR (display)->show_redraw) { Picture overlay; dump_xserver_region ("paint_all", display, region); /* Make a random colour overlay */ overlay = solid_picture (display, screen, TRUE, 1, /* 0.3, alpha */ ((double) (rand () % 100)) / 100.0, ((double) (rand () % 100)) / 100.0, ((double) (rand () % 100)) / 100.0); /* Set clipping to the given region */ XFixesSetPictureClipRegion (xdisplay, info->root_picture, 0, 0, region); XRenderComposite (xdisplay, PictOpOver, overlay, None, info->root_picture, 0, 0, 0, 0, 0, 0, screen_width, screen_height); XRenderFreePicture (xdisplay, overlay); XFlush (xdisplay); usleep (100 * 1000); } if (info->root_pixmaps[b] == None) info->root_pixmaps[b] = create_root_pixmap (screen); if (info->root_buffers[b] == None) info->root_buffers[b] = create_root_buffer (screen, info->root_pixmaps[b]); paint_windows (screen, info->windows, info->root_buffers[b], info->root_pixmaps[b], region); } static void repair_screen (MetaScreen *screen) { MetaCompScreen *info = meta_screen_get_compositor_data (screen); MetaDisplay *display = meta_screen_get_display (screen); Display *xdisplay = meta_display_get_xdisplay (display); g_return_if_fail(info != NULL); if (info->all_damage != None) { #ifdef HAVE_PRESENT if (info->use_present) { if (!info->present_pending) { XserverRegion damage = info->all_damage; meta_error_trap_push (display); if (info->prev_damage) { XFixesUnionRegion(xdisplay, info->prev_damage, info->prev_damage, damage); damage = info->prev_damage; } paint_all (screen, damage, info->root_current); if (++info->root_current >= NUM_BUFFER) info->root_current = 0; if (info->prev_damage) XFixesDestroyRegion (xdisplay, info->prev_damage); info->prev_damage = info->all_damage; info->all_damage = None; info->clip_changed = FALSE; meta_error_trap_pop (display, FALSE); } } else #endif /* HAVE_PRESENT */ { meta_error_trap_push (display); paint_all (screen, info->all_damage, info->root_current); XFixesDestroyRegion (xdisplay, info->all_damage); info->all_damage = None; info->clip_changed = FALSE; meta_error_trap_pop (display, FALSE); } } } static void repair_display (MetaDisplay *display) { GSList *screens = meta_display_get_screens (display); MetaCompositorXRender *compositor = DISPLAY_COMPOSITOR (display); #ifdef USE_IDLE_REPAINT if (compositor->repaint_id > 0) { g_source_remove (compositor->repaint_id); compositor->repaint_id = 0; } #endif for (; screens; screens = screens->next) repair_screen ((MetaScreen *) screens->data); } #ifdef USE_IDLE_REPAINT static gboolean compositor_idle_cb (gpointer data) { MetaCompositorXRender *compositor = (MetaCompositorXRender *) data; compositor->repaint_id = 0; repair_display (compositor->display); return FALSE; } static void add_repair (MetaDisplay *display) { MetaCompositorXRender *compositor = DISPLAY_COMPOSITOR (display); if (compositor->repaint_id > 0) return; #if 1 compositor->repaint_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE, compositor_idle_cb, compositor, NULL); #else /* Limit it to 50fps */ compositor->repaint_id = g_timeout_add_full (G_PRIORITY_HIGH, 20, compositor_idle_cb, compositor, NULL); #endif } #endif static void add_damage (MetaScreen *screen, XserverRegion damage) { MetaDisplay *display = meta_screen_get_display (screen); Display *xdisplay = meta_display_get_xdisplay (display); MetaCompScreen *info = meta_screen_get_compositor_data (screen); /* dump_xserver_region ("add_damage", display, damage); */ if (info != NULL) { if (info->all_damage) { XFixesUnionRegion (xdisplay, info->all_damage, info->all_damage, damage); XFixesDestroyRegion (xdisplay, damage); } else { info->all_damage = damage; } } #ifdef USE_IDLE_REPAINT add_repair (display); #endif } static void damage_screen (MetaScreen *screen) { MetaDisplay *display = meta_screen_get_display (screen); Display *xdisplay = meta_display_get_xdisplay (display); XserverRegion region; int width, height; XRectangle r; r.x = 0; r.y = 0; meta_screen_get_size (screen, &width, &height); r.width = width; r.height = height; region = XFixesCreateRegion (xdisplay, &r, 1); dump_xserver_region ("damage_screen", display, region); add_damage (screen, region); } static void repair_win (MetaCompWindow *cw) { MetaScreen *screen = cw->screen; MetaDisplay *display = meta_screen_get_display (screen); Display *xdisplay = meta_display_get_xdisplay (display); XserverRegion parts; meta_error_trap_push (display); if (!cw->damaged) { parts = win_extents (cw); XDamageSubtract (xdisplay, cw->damage, None, None); } else { parts = XFixesCreateRegion (xdisplay, 0, 0); XDamageSubtract (xdisplay, cw->damage, None, parts); XFixesTranslateRegion (xdisplay, parts, cw->attrs.x + cw->attrs.border_width, cw->attrs.y + cw->attrs.border_width); } meta_error_trap_pop (display, FALSE); dump_xserver_region ("repair_win", display, parts); add_damage (screen, parts); cw->damaged = TRUE; } static void free_win (MetaCompWindow *cw, gboolean destroy) { MetaDisplay *display = meta_screen_get_display (cw->screen); Display *xdisplay = meta_display_get_xdisplay (display); MetaCompScreen *info = meta_screen_get_compositor_data (cw->screen); /* See comment in map_win */ if (cw->back_pixmap && destroy) { XFreePixmap (xdisplay, cw->back_pixmap); cw->back_pixmap = None; } if (cw->shaded_back_pixmap && destroy) { XFreePixmap (xdisplay, cw->shaded_back_pixmap); cw->shaded_back_pixmap = None; } if (cw->picture) { XRenderFreePicture (xdisplay, cw->picture); cw->picture = None; } if (cw->shadow) { XRenderFreePicture (xdisplay, cw->shadow); cw->shadow = None; } if (cw->alpha_pict) { XRenderFreePicture (xdisplay, cw->alpha_pict); cw->alpha_pict = None; } if (cw->shadow_pict) { XRenderFreePicture (xdisplay, cw->shadow_pict); cw->shadow_pict = None; } if (cw->border_size) { XFixesDestroyRegion (xdisplay, cw->border_size); cw->border_size = None; } if (cw->border_clip) { XFixesDestroyRegion (xdisplay, cw->border_clip); cw->border_clip = None; } if (cw->extents) { XFixesDestroyRegion (xdisplay, cw->extents); cw->extents = None; } if (destroy) { if (cw->damage != None) { meta_error_trap_push (display); XDamageDestroy (xdisplay, cw->damage); meta_error_trap_pop (display, FALSE); cw->damage = None; } /* The window may not have been added to the list in this case, but we can check anyway */ if (info!=NULL && cw->type == META_COMP_WINDOW_DOCK) info->dock_windows = g_slist_remove (info->dock_windows, cw); g_free (cw); } } static void constrain_tooltip_onscreen (MetaDisplay *display, MetaScreen *screen, MetaCompWindow *cw, Window id) { MetaWorkspace *workspace; MetaRectangle work_area, win_rect; const MetaXineramaScreenInfo* xinerama; gint new_x, new_y; gint active_workspace_num; /* Why this is here: * As of gtk 3.24, tooltips are positioned differently, and can end up off the * screen in certain situations in hidpi. * * See: https://github.com/GNOME/gtk/commit/14d22cb3233e * * If the panel is too tall (around > 25 or so), tooltip positioning fails both * tests in gdkwindowimpl.c (maybe_flip_position()) skipping repositioning of the * tooltip inside the workarea. This only occurs on bottom panels. * * Since the calculations are based upon the monitor's workarea and the 'attach' * (gdk) window's rectangle, there's no way to compensate for or fool gtk into * displaying it correctly. So here, we do our own check and adjustment. */ active_workspace_num = meta_core_get_active_workspace (cw->attrs.screen); workspace = meta_screen_get_workspace_by_index (screen, active_workspace_num); win_rect.x = cw->attrs.x; win_rect.y = cw->attrs.y; win_rect.width = cw->attrs.width; win_rect.height = cw->attrs.height; xinerama = meta_screen_get_xinerama_for_rect (screen, &win_rect); meta_workspace_get_work_area_for_xinerama (workspace, xinerama->number, &work_area); new_x = win_rect.x; new_y = win_rect.y; /* Valid tooltip positions seem to cheat into the panel by a few pixels - maybe * accounting for shadow margin. There's no reason the fix these, but they'd * be caught here otherwise, so 10px of overshoot in the direction of the panel * is allowed. The tooltips we're out to catch are the ones on the complete other * side of the panel (off screren), so there won't be any confusion. */ if (win_rect.y < work_area.y - 10) { new_y = work_area.y; } else if (win_rect.y + win_rect.height > work_area.y + work_area.height + 10) { new_y = (work_area.y + work_area.height - win_rect.height); } if (win_rect.x < work_area.x - 10) { new_x = work_area.x; } else if (win_rect.x + win_rect.width > work_area.x + work_area.width + 10) { new_x = (work_area.x + work_area.width - win_rect.width); } if (new_x != win_rect.x || new_y != win_rect.y) { if (DISPLAY_COMPOSITOR (display)->debug) { fprintf(stderr, "Constraining tooltip onscreen x:%d -> %d, y:%d -> %d\n", win_rect.x,new_x, win_rect.y,new_y); } XMoveWindow (display->xdisplay, cw->id, new_x, new_y); } } static void map_win (MetaDisplay *display, MetaScreen *screen, Window id) { MetaCompWindow *cw = find_window_for_screen (screen, id); Display *xdisplay = meta_display_get_xdisplay (display); if (cw == NULL) return; if (cw->type == META_COMP_WINDOW_TOOLTIP) { constrain_tooltip_onscreen (display, screen, cw, id); } /* The reason we deallocate this here and not in unmap is so that we will still have a valid pixmap for whenever the window is unmapped */ if (cw->back_pixmap) { XFreePixmap (xdisplay, cw->back_pixmap); cw->back_pixmap = None; } if (cw->shaded_back_pixmap) { XFreePixmap (xdisplay, cw->shaded_back_pixmap); cw->shaded_back_pixmap = None; } cw->attrs.map_state = IsViewable; cw->damaged = FALSE; } static void unmap_win (MetaDisplay *display, MetaScreen *screen, Window id) { MetaCompWindow *cw = find_window_for_screen (screen, id); MetaCompScreen *info = meta_screen_get_compositor_data (screen); if (cw == NULL || info == NULL) { return; } if (cw->window && cw->window == info->focus_window) info->focus_window = NULL; cw->attrs.map_state = IsUnmapped; cw->damaged = FALSE; if (cw->extents != None) { dump_xserver_region ("unmap_win", display, cw->extents); add_damage (screen, cw->extents); cw->extents = None; } free_win (cw, FALSE); info->clip_changed = TRUE; } static void determine_mode (MetaDisplay *display, MetaScreen *screen, MetaCompWindow *cw) { XRenderPictFormat *format; Display *xdisplay = meta_display_get_xdisplay (display); if (cw->alpha_pict) { XRenderFreePicture (xdisplay, cw->alpha_pict); cw->alpha_pict = None; } if (cw->shadow_pict) { XRenderFreePicture (xdisplay, cw->shadow_pict); cw->shadow_pict = None; } if (cw->attrs.class == InputOnly) format = NULL; else format = XRenderFindVisualFormat (xdisplay, cw->attrs.visual); if ((format && format->type == PictTypeDirect && format->direct.alphaMask) || cw->opacity != (guint) OPAQUE) cw->mode = WINDOW_ARGB; else cw->mode = WINDOW_SOLID; if (cw->extents) { XserverRegion damage; damage = XFixesCreateRegion (xdisplay, NULL, 0); XFixesCopyRegion (xdisplay, damage, cw->extents); dump_xserver_region ("determine_mode", display, damage); add_damage (screen, damage); } } static gboolean is_shaped (MetaDisplay *display, Window xwindow) { Display *xdisplay = meta_display_get_xdisplay (display); int xws, yws, xbs, ybs; unsigned wws, hws, wbs, hbs; int bounding_shaped, clip_shaped; if (meta_display_has_shape (display)) { XShapeQueryExtents (xdisplay, xwindow, &bounding_shaped, &xws, &yws, &wws, &hws, &clip_shaped, &xbs, &ybs, &wbs, &hbs); return (bounding_shaped != 0); } return FALSE; } static void get_window_type (MetaDisplay *display, MetaCompWindow *cw) { MetaCompositorXRender *compositor = DISPLAY_COMPOSITOR (display); int n_atoms; Atom *atoms, type_atom; int i; type_atom = None; n_atoms = 0; atoms = NULL; meta_prop_get_atom_list (display, cw->id, compositor->atom_net_wm_window_type, &atoms, &n_atoms); for (i = 0; i < n_atoms; i++) { if (atoms[i] == compositor->atom_net_wm_window_type_dnd || atoms[i] == compositor->atom_net_wm_window_type_desktop || atoms[i] == compositor->atom_net_wm_window_type_dock || atoms[i] == compositor->atom_net_wm_window_type_toolbar || atoms[i] == compositor->atom_net_wm_window_type_menu || atoms[i] == compositor->atom_net_wm_window_type_dialog || atoms[i] == compositor->atom_net_wm_window_type_normal || atoms[i] == compositor->atom_net_wm_window_type_utility || atoms[i] == compositor->atom_net_wm_window_type_splash || atoms[i] == compositor->atom_net_wm_window_type_dropdown_menu || atoms[i] == compositor->atom_net_wm_window_type_tooltip) { type_atom = atoms[i]; break; } } meta_XFree (atoms); if (type_atom == compositor->atom_net_wm_window_type_dnd) cw->type = META_COMP_WINDOW_DND; else if (type_atom == compositor->atom_net_wm_window_type_desktop) cw->type = META_COMP_WINDOW_DESKTOP; else if (type_atom == compositor->atom_net_wm_window_type_dock) cw->type = META_COMP_WINDOW_DOCK; else if (type_atom == compositor->atom_net_wm_window_type_menu) cw->type = META_COMP_WINDOW_MENU; else if (type_atom == compositor->atom_net_wm_window_type_dropdown_menu) cw->type = META_COMP_WINDOW_DROP_DOWN_MENU; else if (type_atom == compositor->atom_net_wm_window_type_tooltip) cw->type = META_COMP_WINDOW_TOOLTIP; else cw->type = META_COMP_WINDOW_NORMAL; /* meta_verbose ("Window is %d\n", cw->type); */ } /* Must be called with an error trap in place */ static void add_win (MetaScreen *screen, MetaWindow *window, Window xwindow) { MetaDisplay *display = meta_screen_get_display (screen); Display *xdisplay = meta_display_get_xdisplay (display); MetaCompScreen *info = meta_screen_get_compositor_data (screen); MetaCompWindow *cw; gulong event_mask; if (info == NULL) return; if (xwindow == info->output) return; /* If already added, ignore */ if (find_window_for_screen (screen, xwindow) != NULL) return; cw = g_new0 (MetaCompWindow, 1); cw->screen = screen; cw->window = window; cw->id = xwindow; if (!XGetWindowAttributes (xdisplay, xwindow, &cw->attrs)) { g_free (cw); return; } get_window_type (display, cw); /* If Marco has decided not to manage this window then the input events won't have been set on the window */ event_mask = cw->attrs.your_event_mask | PropertyChangeMask; XSelectInput (xdisplay, xwindow, event_mask); cw->back_pixmap = None; cw->shaded_back_pixmap = None; cw->damaged = FALSE; cw->shaped = is_shaped (display, xwindow); cw->shape_bounds.x = cw->attrs.x; cw->shape_bounds.y = cw->attrs.y; cw->shape_bounds.width = cw->attrs.width; cw->shape_bounds.height = cw->attrs.height; if (cw->attrs.class == InputOnly) cw->damage = None; else cw->damage = XDamageCreate (xdisplay, xwindow, XDamageReportNonEmpty); cw->alpha_pict = None; cw->shadow_pict = None; cw->border_size = None; cw->extents = None; cw->shadow = None; cw->shadow_dx = 0; cw->shadow_dy = 0; cw->shadow_width = 0; cw->shadow_height = 0; if (window && meta_window_has_focus (window)) cw->shadow_type = META_SHADOW_LARGE; else cw->shadow_type = META_SHADOW_MEDIUM; cw->opacity = OPAQUE; cw->border_clip = None; determine_mode (display, screen, cw); cw->needs_shadow = window_has_shadow (cw); /* Only add the window to the list of docks if it needs a shadow */ if (cw->type == META_COMP_WINDOW_DOCK && cw->needs_shadow) { meta_verbose ("Appending %p to dock windows\n", cw); info->dock_windows = g_slist_append (info->dock_windows, cw); } /* Add this to the list at the top of the stack before it is mapped so that map_win can find it again */ info->windows = g_list_prepend (info->windows, cw); g_hash_table_insert (info->windows_by_xid, (gpointer) xwindow, cw); if (cw->attrs.map_state == IsViewable) map_win (display, screen, xwindow); } static void destroy_win (MetaDisplay *display, Window xwindow, gboolean gone) { MetaScreen *screen; MetaCompScreen *info; MetaCompWindow *cw; cw = find_window_in_display (display, xwindow); if (cw == NULL) return; screen = cw->screen; if (cw->extents != None) { dump_xserver_region ("destroy_win", display, cw->extents); add_damage (screen, cw->extents); cw->extents = None; } info = meta_screen_get_compositor_data (screen); if (info != NULL) { info->windows = g_list_remove (info->windows, (gconstpointer) cw); g_hash_table_remove (info->windows_by_xid, (gpointer) xwindow); } free_win (cw, TRUE); } static void restack_win (MetaCompWindow *cw, Window above) { MetaScreen *screen; MetaCompScreen *info; Window previous_above; GList *sibling, *next; screen = cw->screen; info = meta_screen_get_compositor_data (screen); if (info == NULL) { return; } sibling = g_list_find (info->windows, (gconstpointer) cw); next = g_list_next (sibling); previous_above = None; if (next) { MetaCompWindow *ncw = (MetaCompWindow *) next->data; previous_above = ncw->id; } /* If above is set to None, the window whose state was changed is on * the bottom of the stack with respect to sibling. */ if (above == None) { /* Insert at bottom of window stack */ info->windows = g_list_delete_link (info->windows, sibling); info->windows = g_list_append (info->windows, cw); } else if (previous_above != above) { GList *index; for (index = info->windows; index; index = index->next) { MetaCompWindow *cw2 = (MetaCompWindow *) index->data; if (cw2->id == above) break; } if (index != NULL) { info->windows = g_list_delete_link (info->windows, sibling); info->windows = g_list_insert_before (info->windows, index, cw); } } } static void resize_win (MetaCompWindow *cw, int x, int y, int width, int height, int border_width, gboolean override_redirect) { MetaScreen *screen = cw->screen; MetaDisplay *display = meta_screen_get_display (screen); Display *xdisplay = meta_display_get_xdisplay (display); MetaCompScreen *info = meta_screen_get_compositor_data (screen); XserverRegion damage; XserverRegion shape; gboolean debug; debug = DISPLAY_COMPOSITOR (display)->debug; if (cw->extents) { damage = XFixesCreateRegion (xdisplay, NULL, 0); XFixesCopyRegion (xdisplay, damage, cw->extents); } else { damage = None; if (debug) fprintf (stderr, "no extents to damage !\n"); } /* { // Damage whole screen each time ! ;-) XRectangle r; r.x = 0; r.y = 0; meta_screen_get_size (screen, &r.width, &r.height); fprintf (stderr, "Damage whole screen %d,%d (%d %d)\n", r.x, r.y, r.width, r.height); damage = XFixesCreateRegion (xdisplay, &r, 1); } */ cw->attrs.x = x; cw->attrs.y = y; if (cw->attrs.width != width || cw->attrs.height != height) { if (cw->shaded_back_pixmap) { XFreePixmap (xdisplay, cw->shaded_back_pixmap); cw->shaded_back_pixmap = None; } if (cw->back_pixmap) { /* If the window is shaded, we store the old backing pixmap so we can return a proper image of the window */ if (cw->window && meta_window_is_shaded (cw->window)) { cw->shaded_back_pixmap = cw->back_pixmap; cw->back_pixmap = None; } else { XFreePixmap (xdisplay, cw->back_pixmap); cw->back_pixmap = None; } } if (cw->picture) { XRenderFreePicture (xdisplay, cw->picture); cw->picture = None; } if (cw->shadow) { XRenderFreePicture (xdisplay, cw->shadow); cw->shadow = None; } } cw->attrs.width = width; cw->attrs.height = height; cw->attrs.border_width = border_width; cw->attrs.override_redirect = override_redirect; if (cw->extents) XFixesDestroyRegion (xdisplay, cw->extents); cw->extents = win_extents (cw); if (damage) { if (debug) fprintf (stderr, "Inexplicable intersection with new extents!\n"); XFixesUnionRegion (xdisplay, damage, damage, cw->extents); } else { damage = XFixesCreateRegion (xdisplay, NULL, 0); XFixesCopyRegion (xdisplay, damage, cw->extents); } shape = XFixesCreateRegion (xdisplay, &cw->shape_bounds, 1); XFixesUnionRegion (xdisplay, damage, damage, shape); XFixesDestroyRegion (xdisplay, shape); dump_xserver_region ("resize_win", display, damage); add_damage (screen, damage); if (info != NULL) { info->clip_changed = TRUE; } } /* event processors must all be called with an error trap in place */ static void process_circulate_notify (MetaCompositorXRender *compositor, XCirculateEvent *event) { MetaCompWindow *cw = find_window_in_display (compositor->display, event->window); MetaCompWindow *top; MetaCompScreen *info; MetaScreen *screen; GList *first; Window above; if (!cw) return; screen = cw->screen; info = meta_screen_get_compositor_data (screen); first = info->windows; top = (MetaCompWindow *) first->data; if ((event->place == PlaceOnTop) && top) above = top->id; else above = None; restack_win (cw, above); if (info != NULL) { info->clip_changed = TRUE; } #ifdef USE_IDLE_REPAINT add_repair (compositor->display); #endif } static void process_configure_notify (MetaCompositorXRender *compositor, XConfigureEvent *event) { MetaDisplay *display = compositor->display; Display *xdisplay = meta_display_get_xdisplay (display); MetaCompWindow *cw = find_window_in_display (display, event->window); if (cw) { #if 0 int x = -1, y = -1, width = -1, height = -1; int ex = -1, ey = -1, ewidth = -1, eheight = -1; MetaRectangle *rect; if (cw->window) { rect = meta_window_get_rect (cw->window); x = rect->x; y = rect->y; width = rect->width; height = rect->height; } fprintf (stderr, "configure notify xy (%d %d) -> (%d %d), wh (%d %d) -> (%d %d)\n", x, y, event->x, event->y, width, height, event->width, event->height); #endif if (compositor->debug) { fprintf (stderr, "configure notify %d %d %d\n", cw->damaged, cw->shaped, cw->needs_shadow); dump_xserver_region ("\textents", display, cw->extents); fprintf (stderr, "\txy (%d %d), wh (%d %d)\n", event->x, event->y, event->width, event->height); } restack_win (cw, event->above); resize_win (cw, event->x, event->y, event->width, event->height, event->border_width, event->override_redirect); } else { MetaScreen *screen; MetaCompScreen *info; /* Might be the root window? */ screen = meta_display_screen_for_root (display, event->window); if (screen == NULL) return; info = meta_screen_get_compositor_data (screen); if (info != NULL) { int b; for (b = 0; b < NUM_BUFFER; b++) { if (info->root_buffers[b]) { XRenderFreePicture (xdisplay, info->root_buffers[b]); XFreePixmap (xdisplay, info->root_pixmaps[b]); info->root_buffers[b] = None; info->root_pixmaps[b] = None; } } } damage_screen (screen); } } static void process_property_notify (MetaCompositorXRender *compositor, XPropertyEvent *event) { MetaDisplay *display = compositor->display; Display *xdisplay = meta_display_get_xdisplay (display); MetaScreen *screen; int p; Atom background_atoms[2]; /* Check for the background property changing */ background_atoms[0] = compositor->atom_x_root_pixmap; background_atoms[1] = compositor->atom_x_set_root; for (p = 0; p < 2; p++) { if (event->atom == background_atoms[p]) { screen = meta_display_screen_for_root (display, event->window); if (screen) { MetaCompScreen *info = meta_screen_get_compositor_data (screen); Window xroot = meta_screen_get_xroot (screen); if (info != NULL && info->root_tile) { XClearArea (xdisplay, xroot, 0, 0, 0, 0, TRUE); XRenderFreePicture (xdisplay, info->root_tile); info->root_tile = None; /* Damage the whole screen as we may need to redraw the background ourselves */ damage_screen (screen); #ifdef USE_IDLE_REPAINT add_repair (display); #endif return; } } } } /* Check for the opacity changing */ if (event->atom == compositor->atom_net_wm_window_opacity) { MetaCompWindow *cw = find_window_in_display (display, event->window); gulong value; if (!cw) { /* Applications can set this for their toplevel windows, so * this must be propagated to the window managed by the compositor */ cw = find_window_for_child_window_in_display (display, event->window); } if (!cw) return; if (meta_prop_get_cardinal (display, event->window, compositor->atom_net_wm_window_opacity, &value) == FALSE) value = OPAQUE; cw->opacity = (guint)value; determine_mode (display, cw->screen, cw); cw->needs_shadow = window_has_shadow (cw); if (cw->shadow) { XRenderFreePicture (xdisplay, cw->shadow); cw->shadow = None; } if (cw->extents) XFixesDestroyRegion (xdisplay, cw->extents); cw->extents = win_extents (cw); cw->damaged = TRUE; #ifdef USE_IDLE_REPAINT add_repair (display); #endif return; } if (event->atom == compositor->atom_net_wm_window_type) { MetaCompWindow *cw = find_window_in_display (display, event->window); if (!cw) return; get_window_type (display, cw); cw->needs_shadow = window_has_shadow (cw); return; } } static void expose_area (MetaScreen *screen, XRectangle *rects, int nrects) { MetaDisplay *display = meta_screen_get_display (screen); Display *xdisplay = meta_display_get_xdisplay (display); XserverRegion region; region = XFixesCreateRegion (xdisplay, rects, nrects); dump_xserver_region ("expose_area", display, region); add_damage (screen, region); } static void process_expose (MetaCompositorXRender *compositor, XExposeEvent *event) { MetaCompWindow *cw = find_window_in_display (compositor->display, event->window); MetaScreen *screen = NULL; XRectangle rect[1]; int origin_x = 0, origin_y = 0; if (cw != NULL) { screen = cw->screen; origin_x = cw->attrs.x; /* + cw->attrs.border_width; ? */ origin_y = cw->attrs.y; /* + cw->attrs.border_width; ? */ } else { screen = meta_display_screen_for_root (compositor->display, event->window); if (screen == NULL) return; } rect[0].x = event->x + origin_x; rect[0].y = event->y + origin_y; rect[0].width = event->width; rect[0].height = event->height; expose_area (screen, rect, 1); } static void process_unmap (MetaCompositorXRender *compositor, XUnmapEvent *event) { MetaCompWindow *cw; if (event->from_configure) { /* Ignore unmap caused by parent's resize */ return; } cw = find_window_in_display (compositor->display, event->window); if (cw) unmap_win (compositor->display, cw->screen, event->window); } static void process_map (MetaCompositorXRender *compositor, XMapEvent *event) { MetaCompWindow *cw = find_window_in_display (compositor->display, event->window); if (cw) map_win (compositor->display, cw->screen, event->window); } static void process_reparent (MetaCompositorXRender *compositor, XReparentEvent *event, MetaWindow *window) { MetaScreen *screen; screen = meta_display_screen_for_root (compositor->display, event->parent); if (screen != NULL) add_win (screen, window, event->window); else destroy_win (compositor->display, event->window, FALSE); } static void process_create (MetaCompositorXRender *compositor, XCreateWindowEvent *event, MetaWindow *window) { MetaScreen *screen; /* We are only interested in top level windows, others will be caught by normal marco functions */ screen = meta_display_screen_for_root (compositor->display, event->parent); if (screen == NULL) return; if (!find_window_in_display (compositor->display, event->window)) add_win (screen, window, event->window); } static void process_destroy (MetaCompositorXRender *compositor, XDestroyWindowEvent *event) { destroy_win (compositor->display, event->window, FALSE); } static void process_damage (MetaCompositorXRender *compositor, XDamageNotifyEvent *event) { MetaCompWindow *cw = find_window_in_display (compositor->display, event->drawable); if (cw == NULL) return; repair_win (cw); #ifdef USE_IDLE_REPAINT if (event->more == FALSE) add_repair (compositor->display); #endif } static void process_shape (MetaCompositorXRender *compositor, XShapeEvent *event) { MetaCompWindow *cw = find_window_in_display (compositor->display, event->window); if (cw == NULL) return; if (event->kind == ShapeBounding) { if (!event->shaped && cw->shaped) cw->shaped = FALSE; resize_win (cw, cw->attrs.x, cw->attrs.y, event->width + event->x, event->height + event->y, cw->attrs.border_width, cw->attrs.override_redirect); if (event->shaped && !cw->shaped) cw->shaped = TRUE; if (event->shaped == True) { cw->shape_bounds.x = cw->attrs.x + event->x; cw->shape_bounds.y = cw->attrs.y + event->y; cw->shape_bounds.width = event->width; cw->shape_bounds.height = event->height; } else { cw->shape_bounds.x = cw->attrs.x; cw->shape_bounds.y = cw->attrs.y; cw->shape_bounds.width = cw->attrs.width; cw->shape_bounds.height = cw->attrs.height; } } } #ifdef HAVE_PRESENT static void xrender_present_complete(MetaScreen *screen, XPresentCompleteNotifyEvent *ce) { MetaCompScreen *info = meta_screen_get_compositor_data (screen); info->present_pending = False; repair_screen(screen); } #endif /* HAVE_PRESENT */ static void process_generic(MetaCompositorXRender *compositor, XGenericEvent *event) { XGenericEventCookie *ge = (XGenericEventCookie *) event; Display *xdisplay = meta_display_get_xdisplay (compositor->display); XGetEventData(xdisplay, ge); switch (ge->evtype) { #ifdef HAVE_PRESENT case PresentConfigureNotify: break; case PresentCompleteNotify: { if (ge->extension == compositor->present_major) { XPresentCompleteNotifyEvent *ce = ge->data; MetaScreen *screen = find_screen_from_output(compositor->display, ce->window); if (screen) xrender_present_complete(screen, ce); } } break; #endif /* HAVE_PRESENT */ } XFreeEventData(xdisplay, ge); } static int timeout_debug (MetaCompositorXRender *compositor) { compositor->show_redraw = (g_getenv ("MARCO_DEBUG_REDRAWS") != NULL); compositor->debug = (g_getenv ("MARCO_DEBUG_COMPOSITOR") != NULL); return FALSE; } static void xrender_add_window (MetaCompositor *compositor, MetaWindow *window, Window xwindow, XWindowAttributes *attrs) { #ifdef HAVE_COMPOSITE_EXTENSIONS MetaCompositorXRender *xrc = (MetaCompositorXRender *) compositor; MetaScreen *screen = meta_screen_for_x_screen (attrs->screen); meta_error_trap_push (xrc->display); add_win (screen, window, xwindow); meta_error_trap_pop (xrc->display, FALSE); #endif } static void xrender_remove_window (MetaCompositor *compositor, Window xwindow) { #ifdef HAVE_COMPOSITE_EXTENSIONS #endif } static void show_overlay_window (MetaScreen *screen, Window cow) { MetaDisplay *display = meta_screen_get_display (screen); Display *xdisplay = meta_display_get_xdisplay (display); XserverRegion region; region = XFixesCreateRegion (xdisplay, NULL, 0); XFixesSetWindowShapeRegion (xdisplay, cow, ShapeBounding, 0, 0, 0); XFixesSetWindowShapeRegion (xdisplay, cow, ShapeInput, 0, 0, region); XFixesDestroyRegion (xdisplay, region); damage_screen (screen); } static void hide_overlay_window (MetaScreen *screen, Window cow) { MetaDisplay *display = meta_screen_get_display (screen); Display *xdisplay = meta_display_get_xdisplay (display); XserverRegion region; region = XFixesCreateRegion (xdisplay, NULL, 0); XFixesSetWindowShapeRegion (xdisplay, cow, ShapeBounding, 0, 0, region); XFixesDestroyRegion (xdisplay, region); } static Window get_output_window (MetaScreen *screen) { MetaDisplay *display = meta_screen_get_display (screen); Display *xdisplay = meta_display_get_xdisplay (display); Window output, xroot; xroot = meta_screen_get_xroot (screen); output = XCompositeGetOverlayWindow (xdisplay, xroot); XSelectInput (xdisplay, output, ExposureMask); return output; } static void xrender_manage_screen (MetaCompositor *compositor, MetaScreen *screen) { #ifdef HAVE_COMPOSITE_EXTENSIONS MetaCompositorXRender *xrc = (MetaCompositorXRender *) compositor; MetaCompScreen *info; MetaDisplay *display = meta_screen_get_display (screen); Display *xdisplay = meta_display_get_xdisplay (display); GdkDisplay *gdk_display = gdk_x11_lookup_xdisplay (xdisplay); XRenderPictureAttributes pa; XRenderPictFormat *visual_format; int screen_number = meta_screen_get_screen_number (screen); Window xroot = meta_screen_get_xroot (screen); int b; /* Check if the screen is already managed */ if (meta_screen_get_compositor_data (screen)) return; gdk_x11_display_error_trap_push (gdk_display); XCompositeRedirectSubwindows (xdisplay, xroot, CompositeRedirectManual); XSync (xdisplay, FALSE); if (gdk_x11_display_error_trap_pop (gdk_display)) { g_warning ("Another compositing manager is running on screen %i", screen_number); return; } info = g_new0 (MetaCompScreen, 1); info->screen = screen; meta_screen_set_compositor_data (screen, info); visual_format = XRenderFindVisualFormat (xdisplay, DefaultVisual (xdisplay, screen_number)); if (!visual_format) { g_warning ("Cannot find visual format on screen %i", screen_number); return; } info->output = get_output_window (screen); pa.subwindow_mode = IncludeInferiors; info->root_picture = XRenderCreatePicture (xdisplay, info->output, visual_format, CPSubwindowMode, &pa); if (info->root_picture == None) { g_warning ("Cannot create root picture on screen %i", screen_number); return; } for (b = 0; b < NUM_BUFFER; b++) { info->root_buffers[b] = None; info->root_pixmaps[b] = None; } info->black_picture = solid_picture (display, screen, TRUE, 1, 0, 0, 0); info->root_tile = None; info->all_damage = None; info->windows = NULL; info->windows_by_xid = g_hash_table_new (g_direct_hash, g_direct_equal); info->focus_window = meta_display_get_focus_window (display); info->compositor_active = TRUE; info->overlays = 0; info->clip_changed = TRUE; info->have_shadows = (g_getenv("META_DEBUG_NO_SHADOW") == NULL); if (info->have_shadows) { meta_verbose ("Enabling shadows\n"); generate_shadows (info); } else meta_verbose ("Disabling shadows\n"); #ifdef HAVE_PRESENT if (xrc->has_present) { info->present_eid = XPresentSelectInput(xdisplay, info->output, PresentCompleteNotifyMask); info->use_present = TRUE; info->present_pending = FALSE; } else { info->use_present = FALSE; g_warning ("XPresent not available"); } #endif /* HAVE_PRESENT */ XClearArea (xdisplay, info->output, 0, 0, 0, 0, TRUE); meta_screen_set_cm_selection (screen); /* Now we're up and running we can show the output if needed */ show_overlay_window (screen, info->output); #endif } static void xrender_unmanage_screen (MetaCompositor *compositor, MetaScreen *screen) { #ifdef HAVE_COMPOSITE_EXTENSIONS MetaDisplay *display = meta_screen_get_display (screen); Display *xdisplay = meta_display_get_xdisplay (display); MetaCompScreen *info; Window xroot = meta_screen_get_xroot (screen); GList *index; info = meta_screen_get_compositor_data (screen); /* This screen isn't managed */ if (info == NULL) return; hide_overlay_window (screen, info->output); /* Destroy the windows */ for (index = info->windows; index; index = index->next) { MetaCompWindow *cw = (MetaCompWindow *) index->data; free_win (cw, TRUE); } g_list_free (info->windows); g_hash_table_destroy (info->windows_by_xid); if (info->root_picture) XRenderFreePicture (xdisplay, info->root_picture); if (info->black_picture) XRenderFreePicture (xdisplay, info->black_picture); if (info->have_shadows) { int i; for (i = 0; i < LAST_SHADOW_TYPE; i++) g_free (info->shadows[i]->gaussian_map); } XCompositeUnredirectSubwindows (xdisplay, xroot, CompositeRedirectManual); meta_screen_unset_cm_selection (screen); XCompositeReleaseOverlayWindow (xdisplay, info->output); g_free (info); meta_screen_set_compositor_data (screen, NULL); #endif } static void xrender_set_updates (MetaCompositor *compositor, MetaWindow *window, gboolean updates) { #ifdef HAVE_COMPOSITE_EXTENSIONS #endif } static void xrender_destroy (MetaCompositor *compositor) { #ifdef HAVE_COMPOSITE_EXTENSIONS g_free (compositor); #endif } #if 0 /* Taking these out because they're empty and never called, and the * compiler complains -- tthurman */ static void xrender_begin_move (MetaCompositor *compositor, MetaWindow *window, MetaRectangle *initial, int grab_x, int grab_y) { #ifdef HAVE_COMPOSITE_EXTENSIONS #endif } static void xrender_update_move (MetaCompositor *compositor, MetaWindow *window, int x, int y) { #ifdef HAVE_COMPOSITE_EXTENSIONS #endif } static void xrender_end_move (MetaCompositor *compositor, MetaWindow *window) { #ifdef HAVE_COMPOSITE_EXTENSIONS #endif } #endif /* 0 */ static void xrender_free_window (MetaCompositor *compositor, MetaWindow *window) { #ifdef HAVE_COMPOSITE_EXTENSIONS MetaCompositorXRender *xrc; MetaFrame *frame; Window xwindow; xrc = (MetaCompositorXRender *) compositor; frame = meta_window_get_frame (window); xwindow = None; if (frame) { xwindow = meta_frame_get_xwindow (frame); } else { /* FIXME: When an undecorated window is hidden this is called, but the * window does not get readded if it is subsequentally shown again. See: * http://bugzilla.gnome.org/show_bug.cgi?id=504876 */ /* xwindow = meta_window_get_xwindow (window); */ } if (xwindow != None) destroy_win (xrc->display, xwindow, FALSE); #endif } static void xrender_process_event (MetaCompositor *compositor, XEvent *event, MetaWindow *window) { #ifdef HAVE_COMPOSITE_EXTENSIONS MetaCompositorXRender *xrc = (MetaCompositorXRender *) compositor; /* * This trap is so that none of the compositor functions cause * X errors. This is really a hack, but I'm afraid I don't understand * enough about Marco/X to know how else you are supposed to do it */ meta_error_trap_push (xrc->display); switch (event->type) { case CirculateNotify: process_circulate_notify (xrc, (XCirculateEvent *) event); break; case ConfigureNotify: process_configure_notify (xrc, (XConfigureEvent *) event); break; case PropertyNotify: process_property_notify (xrc, (XPropertyEvent *) event); break; case Expose: process_expose (xrc, (XExposeEvent *) event); break; case UnmapNotify: process_unmap (xrc, (XUnmapEvent *) event); break; case MapNotify: process_map (xrc, (XMapEvent *) event); break; case ReparentNotify: process_reparent (xrc, (XReparentEvent *) event, window); break; case CreateNotify: process_create (xrc, (XCreateWindowEvent *) event, window); break; case DestroyNotify: process_destroy (xrc, (XDestroyWindowEvent *) event); break; case GenericEvent: process_generic (xrc, (XGenericEvent *) event); break; default: if (event->type == meta_display_get_damage_event_base (xrc->display) + XDamageNotify) process_damage (xrc, (XDamageNotifyEvent *) event); #ifdef HAVE_SHAPE else if (event->type == meta_display_get_shape_event_base (xrc->display) + ShapeNotify) process_shape (xrc, (XShapeEvent *) event); #endif /* HAVE_SHAPE */ else { meta_error_trap_pop (xrc->display, FALSE); return; } break; } meta_error_trap_pop (xrc->display, FALSE); #ifndef USE_IDLE_REPAINT repair_display (xrc->display); #endif return; #endif } static cairo_surface_t * xrender_get_window_surface (MetaCompositor *compositor, MetaWindow *window) { #ifdef HAVE_COMPOSITE_EXTENSIONS MetaFrame *frame; Window xwindow; MetaScreen *screen; MetaCompWindow *cw; MetaCompositorXRender *xrc; Display *display; Pixmap pixmap; frame = meta_window_get_frame (window); if (frame) xwindow = meta_frame_get_xwindow (frame); else xwindow = meta_window_get_xwindow (window); screen = meta_window_get_screen (window); cw = find_window_for_screen (screen, xwindow); if (cw == NULL) return NULL; xrc = (MetaCompositorXRender *) compositor; display = meta_display_get_xdisplay (xrc->display); if (meta_window_is_shaded (window)) pixmap = cw->shaded_back_pixmap; else pixmap = cw->back_pixmap; return cairo_xlib_surface_create (display, pixmap, cw->attrs.visual, cw->attrs.width, cw->attrs.height); #endif } static void xrender_set_active_window (MetaCompositor *compositor, MetaScreen *screen, MetaWindow *window) { #ifdef HAVE_COMPOSITE_EXTENSIONS MetaCompositorXRender *xrc = (MetaCompositorXRender *) compositor; MetaDisplay *display; Display *xdisplay; MetaCompWindow *old_focus = NULL, *new_focus = NULL; MetaCompScreen *info = NULL; MetaWindow *old_focus_win = NULL; if (compositor == NULL) return; display = xrc->display; xdisplay = meta_display_get_xdisplay (display); info = meta_screen_get_compositor_data (screen); if (info != NULL) { old_focus_win = info->focus_window; } if (old_focus_win) { MetaFrame *f = meta_window_get_frame (old_focus_win); old_focus = find_window_for_screen (screen, f ? meta_frame_get_xwindow (f) : meta_window_get_xwindow (old_focus_win)); } if (window) { MetaFrame *f = meta_window_get_frame (window); new_focus = find_window_for_screen (screen, f ? meta_frame_get_xwindow (f) : meta_window_get_xwindow (window)); } if (info != NULL) { info->focus_window = window; } if (old_focus) { XserverRegion damage; /* Tear down old shadows */ old_focus->shadow_type = META_SHADOW_MEDIUM; determine_mode (display, screen, old_focus); old_focus->needs_shadow = window_has_shadow (old_focus); if (old_focus->attrs.map_state == IsViewable) { if (old_focus->shadow) { XRenderFreePicture (xdisplay, old_focus->shadow); old_focus->shadow = None; } if (old_focus->extents) { damage = XFixesCreateRegion (xdisplay, NULL, 0); XFixesCopyRegion (xdisplay, damage, old_focus->extents); XFixesDestroyRegion (xdisplay, old_focus->extents); } else damage = None; /* Build new extents */ old_focus->extents = win_extents (old_focus); if (damage) XFixesUnionRegion (xdisplay, damage, damage, old_focus->extents); else { damage = XFixesCreateRegion (xdisplay, NULL, 0); XFixesCopyRegion (xdisplay, damage, old_focus->extents); } dump_xserver_region ("resize_win", display, damage); add_damage (screen, damage); if (info != NULL) { info->clip_changed = TRUE; } } } if (new_focus) { XserverRegion damage; new_focus->shadow_type = META_SHADOW_LARGE; determine_mode (display, screen, new_focus); new_focus->needs_shadow = window_has_shadow (new_focus); if (new_focus->shadow) { XRenderFreePicture (xdisplay, new_focus->shadow); new_focus->shadow = None; } if (new_focus->extents) { damage = XFixesCreateRegion (xdisplay, NULL, 0); XFixesCopyRegion (xdisplay, damage, new_focus->extents); XFixesDestroyRegion (xdisplay, new_focus->extents); } else damage = None; /* Build new extents */ new_focus->extents = win_extents (new_focus); if (damage) XFixesUnionRegion (xdisplay, damage, damage, new_focus->extents); else { damage = XFixesCreateRegion (xdisplay, NULL, 0); XFixesCopyRegion (xdisplay, damage, new_focus->extents); } dump_xserver_region ("resize_win", display, damage); add_damage (screen, damage); if (info != NULL) { info->clip_changed = TRUE; } } #ifdef USE_IDLE_REPAINT add_repair (display); #endif #endif } static void xrender_maximize_window (MetaCompositor *compositor, MetaWindow *window) { #ifdef HAVE_COMPOSITE_EXTENSIONS MetaFrame *frame = meta_window_get_frame (window); Window xid = frame ? meta_frame_get_xwindow (frame) : meta_window_get_xwindow (window); MetaCompWindow *cw = find_window_in_display (meta_window_get_display (window), xid); if (!cw) return; cw->needs_shadow = window_has_shadow (cw); #endif } static void xrender_unmaximize_window (MetaCompositor *compositor, MetaWindow *window) { #ifdef HAVE_COMPOSITE_EXTENSIONS MetaFrame *frame = meta_window_get_frame (window); Window xid = frame ? meta_frame_get_xwindow (frame) : meta_window_get_xwindow (window); MetaCompWindow *cw = find_window_in_display (meta_window_get_display (window), xid); if (!cw) return; cw->needs_shadow = window_has_shadow (cw); #endif } static MetaCompositor comp_info = { xrender_destroy, xrender_manage_screen, xrender_unmanage_screen, xrender_add_window, xrender_remove_window, xrender_set_updates, xrender_process_event, xrender_get_window_surface, xrender_set_active_window, xrender_free_window, xrender_maximize_window, xrender_unmaximize_window, }; MetaCompositor * meta_compositor_xrender_new (MetaDisplay *display) { #ifdef HAVE_COMPOSITE_EXTENSIONS char *atom_names[] = { "_XROOTPMAP_ID", "_XSETROOT_ID", "_NET_WM_WINDOW_OPACITY", "_NET_WM_WINDOW_TYPE_DND", "_NET_WM_WINDOW_TYPE", "_NET_WM_WINDOW_TYPE_DESKTOP", "_NET_WM_WINDOW_TYPE_DOCK", "_NET_WM_WINDOW_TYPE_MENU", "_NET_WM_WINDOW_TYPE_DIALOG", "_NET_WM_WINDOW_TYPE_NORMAL", "_NET_WM_WINDOW_TYPE_UTILITY", "_NET_WM_WINDOW_TYPE_SPLASH", "_NET_WM_WINDOW_TYPE_TOOLBAR", "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", "_NET_WM_WINDOW_TYPE_TOOLTIP" }; Atom atoms[G_N_ELEMENTS(atom_names)]; MetaCompositorXRender *xrc; MetaCompositor *compositor; Display *xdisplay = meta_display_get_xdisplay (display); xrc = g_new (MetaCompositorXRender, 1); xrc->compositor = comp_info; compositor = (MetaCompositor *) xrc; xrc->display = display; meta_verbose ("Creating %d atoms\n", (int) G_N_ELEMENTS (atom_names)); XInternAtoms (xdisplay, atom_names, G_N_ELEMENTS (atom_names), False, atoms); xrc->atom_x_root_pixmap = atoms[0]; xrc->atom_x_set_root = atoms[1]; xrc->atom_net_wm_window_opacity = atoms[2]; xrc->atom_net_wm_window_type_dnd = atoms[3]; xrc->atom_net_wm_window_type = atoms[4]; xrc->atom_net_wm_window_type_desktop = atoms[5]; xrc->atom_net_wm_window_type_dock = atoms[6]; xrc->atom_net_wm_window_type_menu = atoms[7]; xrc->atom_net_wm_window_type_dialog = atoms[8]; xrc->atom_net_wm_window_type_normal = atoms[9]; xrc->atom_net_wm_window_type_utility = atoms[10]; xrc->atom_net_wm_window_type_splash = atoms[11]; xrc->atom_net_wm_window_type_toolbar = atoms[12]; xrc->atom_net_wm_window_type_dropdown_menu = atoms[13]; xrc->atom_net_wm_window_type_tooltip = atoms[14]; xrc->show_redraw = FALSE; xrc->debug = FALSE; #ifdef HAVE_PRESENT xrc->has_present = XPresentQueryExtension(xdisplay, &xrc->present_major, NULL, NULL); #endif /* HAVE_PRESENT */ #ifdef USE_IDLE_REPAINT meta_verbose ("Using idle repaint\n"); xrc->repaint_id = 0; #endif xrc->enabled = TRUE; g_timeout_add (2000, (GSourceFunc) timeout_debug, xrc); return compositor; #else return NULL; #endif } #endif /* HAVE_COMPOSITE_EXTENSIONS */