/* $Id$ 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, 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., Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. xcompmgr - (c) 2003 Keith Packard metacity - (c) 2003, 2004 Red Hat, Inc. xfwm4 - (c) 2005-2021 Olivier Fourdan */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #ifdef HAVE_EPOXY #include #include #ifdef HAVE_XSYNC_EXTENSION #include #endif /* HAVE_XSYNC_EXTENSION */ #endif /* HAVE_EPOXY */ #ifdef HAVE_PRESENT_EXTENSION #ifndef PRESENT_FUTURE_VERSION #define PRESENT_FUTURE_VERSION 0 #endif /* PRESENT_FUTURE_VERSION */ #include #endif /* HAVE_PRESENT_EXTENSION */ #include "display.h" #include "screen.h" #include "client.h" #include "frame.h" #include "hints.h" #include "compositor.h" #ifdef HAVE_COMPOSITOR #include "common/xfwm-common.h" #include #include #include #ifndef SHADOW_RADIUS #define SHADOW_RADIUS 12 #endif /* SHADOW_RADIUS */ #ifndef SHADOW_OFFSET_X #define SHADOW_OFFSET_X (-3 * SHADOW_RADIUS / 2) #endif /* SHADOW_OFFSET_X */ #ifndef SHADOW_OFFSET_Y #define SHADOW_OFFSET_Y (-3 * SHADOW_RADIUS / 2) #endif /* SHADOW_OFFSET_Y */ /* Some convenient macros */ #define WIN_HAS_CLIENT(cw) (cw->c) #define WIN_HAS_FRAME(cw) (WIN_HAS_CLIENT(cw) && CLIENT_HAS_FRAME(cw->c)) #define WIN_NO_SHADOW(cw) ((cw->c) && \ (FLAG_TEST (cw->c->flags, CLIENT_FLAG_FULLSCREEN | CLIENT_FLAG_BELOW) || \ (cw->c->type & WINDOW_DESKTOP))) #define WIN_IS_DOCK(cw) (WIN_HAS_CLIENT(cw) && (cw->c->type & WINDOW_DOCK)) #define WIN_IS_OVERRIDE(cw) (cw->attr.override_redirect) #define WIN_IS_ARGB(cw) (cw->argb) #define WIN_IS_OPAQUE(cw) ((cw->opacity == NET_WM_OPAQUE) && !WIN_IS_ARGB(cw)) #define WIN_IS_NATIVE_OPAQUE(cw) ((cw->native_opacity) && !WIN_IS_ARGB(cw)) #define WIN_IS_FULLSCREEN(cw) ((cw->attr.x <= 0) && \ (cw->attr.y <= 0) && \ (cw->attr.width + 2 * cw->attr.border_width >= cw->screen_info->width) && \ (cw->attr.height + 2 * cw->attr.border_width >= cw->screen_info->height)) #define WIN_IS_SHAPED(cw) ((WIN_HAS_CLIENT(cw) && FLAG_TEST (cw->c->flags, CLIENT_FLAG_HAS_SHAPE)) || \ (WIN_IS_OVERRIDE(cw) && (cw->shaped))) #define WIN_IS_MAXIMIZED(cw) (WIN_HAS_CLIENT(cw) && FLAG_TEST_ALL (cw->c->flags, CLIENT_FLAG_MAXIMIZED)) #define WIN_IS_VIEWABLE(cw) (cw->viewable) #define WIN_HAS_DAMAGE(cw) (cw->damage) #define WIN_IS_VISIBLE(cw) (WIN_IS_VIEWABLE(cw) && WIN_HAS_DAMAGE(cw)) #define WIN_IS_DAMAGED(cw) (cw->damaged) #define WIN_IS_REDIRECTED(cw) (cw->redirected) #define WIN_IS_SHADED(cw) (WIN_HAS_CLIENT(cw) && FLAG_TEST (cw->c->flags, CLIENT_FLAG_SHADED)) #ifndef TIMEOUT_REPAINT_PRIORITY #define TIMEOUT_REPAINT_PRIORITY G_PRIORITY_DEFAULT #endif /* TIMEOUT_REPAINT_PRIORITY */ #ifndef TIMEOUT_THROTTLED_REPAINT_PRIORITY #define TIMEOUT_THROTTLED_REPAINT_PRIORITY G_PRIORITY_LOW #endif /* TIMEOUT_THROTTLED_REPAINT_PRIORITY */ #ifndef TIMEOUT_REPAINT_MS #define TIMEOUT_REPAINT_MS 1 #endif /* TIMEOUT_REPAINT_MS */ #ifndef TIMEOUT_THROTTLED_REPAINT_MS #define TIMEOUT_THROTTLED_REPAINT_MS 500 #endif /* TIMEOUT_THROTTLED_REPAINT_MS */ #ifndef MONITOR_ROOT_PIXMAP #define MONITOR_ROOT_PIXMAP 1 #endif /* MONITOR_ROOT_PIXMAP */ typedef struct _CWindow CWindow; struct _CWindow { ScreenInfo *screen_info; Client *c; Window id; XWindowAttributes attr; gboolean damaged; gboolean viewable; gboolean shaped; gboolean redirected; gboolean fulloverlay; gboolean argb; gboolean skipped; gboolean native_opacity; gboolean opacity_locked; Damage damage; #if HAVE_NAME_WINDOW_PIXMAP Pixmap name_window_pixmap; #endif /* HAVE_NAME_WINDOW_PIXMAP */ Picture picture; Picture saved_picture; Picture shadow; Picture alphaPict; Picture shadowPict; Picture alphaBorderPict; XserverRegion borderSize; XserverRegion clientSize; XserverRegion borderClip; XserverRegion extents; XserverRegion opaque_region; gint shadow_dx; gint shadow_dy; gint shadow_width; gint shadow_height; guint32 opacity; guint32 bypass_compositor; }; static CWindow* find_cwindow_in_screen (ScreenInfo *screen_info, Window id) { g_return_val_if_fail (id != None, NULL); g_return_val_if_fail (screen_info != NULL, NULL); TRACE ("window 0x%lx", id); return g_hash_table_lookup(screen_info->cwindow_hash, (gpointer) id); } static CWindow* find_cwindow_in_display (DisplayInfo *display_info, Window id) { GSList *list; g_return_val_if_fail (id != None, NULL); g_return_val_if_fail (display_info != NULL, NULL); TRACE ("window 0x%lx", id); for (list = display_info->screens; list; list = g_slist_next (list)) { ScreenInfo *screen_info = (ScreenInfo *) list->data; CWindow *cw; if (!compositorIsActive (screen_info)) { continue; } cw = find_cwindow_in_screen (screen_info, id); if (cw) { return (cw); } } return NULL; } static gboolean is_output (DisplayInfo *display_info, Window id) { GSList *list; g_return_val_if_fail (id != None, FALSE); g_return_val_if_fail (display_info != NULL, FALSE); TRACE ("window 0x%lx", id); for (list = display_info->screens; list; list = g_slist_next (list)) { ScreenInfo *screen_info = (ScreenInfo *) list->data; #if HAVE_OVERLAYS if (id == screen_info->output || id == screen_info->overlay) #else if (id == screen_info->output) #endif { return TRUE; } } return FALSE; } static gboolean is_shaped (DisplayInfo *display_info, Window id) { int xws, yws, xbs, ybs; unsigned wws, hws, wbs, hbs; int boundingShaped, clipShaped; int result; g_return_val_if_fail (display_info != NULL, FALSE); if (display_info->have_shape) { myDisplayErrorTrapPush (display_info); XShapeQueryExtents (display_info->dpy, id, &boundingShaped, &xws, &yws, &wws, &hws, &clipShaped, &xbs, &ybs, &wbs, &hbs); result = myDisplayErrorTrapPop (display_info); return ((result == Success) && (boundingShaped != 0)); } return FALSE; } static gboolean is_fullscreen (CWindow *cw) { GdkRectangle rect; /* First, check the good old way, the window is larger than the screen size */ if (WIN_IS_FULLSCREEN(cw)) { return TRUE; } /* Next check against the monitors which compose the entire screen */ myScreenFindMonitorAtPoint (cw->screen_info, cw->attr.x + (cw->attr.width + 2 * cw->attr.border_width) / 2, cw->attr.y + (cw->attr.height + 2 * cw->attr.border_width) / 2, &rect); return ((cw->attr.x == rect.x) && (cw->attr.y == rect.y) && (cw->attr.width + 2 * cw->attr.border_width == rect.width) && (cw->attr.height + 2 * cw->attr.border_width == rect.height)); } static gboolean is_on_compositor (CWindow *cw) { return (cw && compositorIsActive (cw->screen_info)); } static gdouble gaussian (gdouble r, gdouble x, gdouble y) { return ((1 / (sqrt (2 * G_PI * r))) * exp ((- (x * x + y * y)) / (2 * r * r))); } static gaussian_conv * make_gaussian_map (gdouble r) { gaussian_conv *c; gint size, center; gint x, y; gdouble t; gdouble g; TRACE ("entering"); size = ((gint) ceil ((r * 3)) + 1) & ~1; center = size / 2; c = g_malloc0 (sizeof (gaussian_conv) + size * size * sizeof (gdouble)); c->size = size; c->data = (gdouble *) (c + 1); t = 0.0; for (y = 0; y < size; y++) { for (x = 0; x < size; x++) { g = gaussian (r, (gdouble) (x - center), (gdouble) (y - center)); 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; } /* * A picture will help * * -center 0 width width+center * -center +-----+-------------------+-----+ * | | | | * | | | | * 0 +-----+-------------------+-----+ * | | | | * | | | | * | | | | * height +-----+-------------------+-----+ * | | | | * height+ | | | | * center +-----+-------------------+-----+ */ static guchar sum_gaussian (gaussian_conv *map, gdouble opacity, gint x, gint y, gint width, gint height) { gdouble *g_data, *g_line; gdouble v; gint fx, fy; gint fx_start, fx_end; gint fy_start, fy_end; gint g_size, center; g_return_val_if_fail (map != NULL, (guchar) 255.0); TRACE ("(%i,%i) [%i×%i]", x, y, width, height); g_line = map->data; g_size = map->size; center = g_size / 2; fx_start = center - x; if (fx_start < 0) { fx_start = 0; } fx_end = width + center - x; if (fx_end > g_size) { fx_end = g_size; } fy_start = center - y; if (fy_start < 0) { fy_start = 0; } fy_end = height + center - y; if (fy_end > g_size) { fy_end = g_size; } g_line = g_line + fy_start * g_size + fx_start; v = 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) { v = 1; } return ((guchar) (v * opacity * 255.0)); } /* precompute shadow corners and sides to save time for large windows */ static void presum_gaussian (ScreenInfo *screen_info) { gint center; gint opacity, x, y; gaussian_conv * map; g_return_if_fail (screen_info != NULL); g_return_if_fail (screen_info->gaussianMap != NULL); TRACE ("entering"); map = screen_info->gaussianMap; screen_info->gaussianSize = map->size; center = map->size / 2; if (screen_info->shadowCorner) { g_free (screen_info->shadowCorner); } if (screen_info->shadowTop) { g_free (screen_info->shadowTop); } screen_info->shadowCorner = (guchar *) (g_malloc0 ((screen_info->gaussianSize + 1) * (screen_info->gaussianSize + 1) * 26)); screen_info->shadowTop = (guchar *) (g_malloc0 ((screen_info->gaussianSize + 1) * 26)); for (x = 0; x <= screen_info->gaussianSize; x++) { screen_info->shadowTop[25 * (screen_info->gaussianSize + 1) + x] = sum_gaussian (map, 1, x - center, center, screen_info->gaussianSize * 2, screen_info->gaussianSize * 2); for(opacity = 0; opacity < 25; opacity++) { screen_info->shadowTop[opacity * (screen_info->gaussianSize + 1) + x] = screen_info->shadowTop[25 * (screen_info->gaussianSize + 1) + x] * opacity / 25; } for(y = 0; y <= x; y++) { screen_info->shadowCorner[25 * (screen_info->gaussianSize + 1) * (screen_info->gaussianSize + 1) + y * (screen_info->gaussianSize + 1) + x] = sum_gaussian (map, 1, x - center, y - center, screen_info->gaussianSize * 2, screen_info->gaussianSize * 2); screen_info->shadowCorner[25 * (screen_info->gaussianSize + 1) * (screen_info->gaussianSize + 1) + x * (screen_info->gaussianSize + 1) + y] = screen_info->shadowCorner[25 * (screen_info->gaussianSize + 1) * (screen_info->gaussianSize + 1) + y * (screen_info->gaussianSize + 1) + x]; for(opacity = 0; opacity < 25; opacity++) { screen_info->shadowCorner[opacity * (screen_info->gaussianSize + 1) * (screen_info->gaussianSize + 1) + y * (screen_info->gaussianSize + 1) + x] = screen_info->shadowCorner[opacity * (screen_info->gaussianSize + 1) * (screen_info->gaussianSize + 1) + x * (screen_info->gaussianSize + 1) + y] = screen_info->shadowCorner[25 * (screen_info->gaussianSize + 1) * (screen_info->gaussianSize + 1) + y * (screen_info->gaussianSize + 1) + x] * opacity / 25; } } } } static XImage * make_shadow (ScreenInfo *screen_info, gdouble opacity, gint width, gint height) { DisplayInfo *display_info; XImage *ximage; guchar *data; guchar d; gint gaussianSize; gint ylimit, xlimit; gint swidth; gint sheight; gint center; gint x, y; gint x_diff; gint opacity_int; gint x_swidth; gint y_swidth; g_return_val_if_fail (screen_info != NULL, NULL); TRACE ("entering"); display_info = screen_info->display_info; gaussianSize = screen_info->gaussianMap->size; swidth = width + gaussianSize - screen_info->params->shadow_delta_width - screen_info->params->shadow_delta_x; sheight = height + gaussianSize - screen_info->params->shadow_delta_height - screen_info->params->shadow_delta_y; center = gaussianSize / 2; opacity_int = (gint) (opacity * 25); if ((swidth < 1) || (sheight < 1)) { return NULL; } data = g_malloc0 (swidth * sheight * sizeof (guchar)); ximage = XCreateImage (display_info->dpy, DefaultVisual(display_info->dpy, screen_info->screen), 8, ZPixmap, 0, (char *) data, swidth, sheight, 8, swidth * sizeof (guchar)); if (ximage == NULL) { g_free (data); g_warning ("(ximage != NULL) failed"); return NULL; } /* * Build the gaussian in sections */ if (screen_info->gaussianSize > 0) { d = screen_info->shadowTop[opacity_int * (screen_info->gaussianSize + 1) + screen_info->gaussianSize]; } else { d = sum_gaussian (screen_info->gaussianMap, opacity, center, center, width, height); } memset(data, d, sheight * swidth); /* * corners */ ylimit = gaussianSize; if (ylimit > sheight / 2) { ylimit = (sheight + 1) / 2; } xlimit = gaussianSize; if (xlimit > swidth / 2) { xlimit = (swidth + 1) / 2; } for (y = 0; y < ylimit; y++) { for (x = 0; x < xlimit; x++) { if ((xlimit == screen_info->gaussianSize) && (ylimit == screen_info->gaussianSize)) { d = screen_info->shadowCorner[opacity_int * (screen_info->gaussianSize + 1) * (screen_info->gaussianSize + 1) + y * (screen_info->gaussianSize + 1) + x]; } else { d = sum_gaussian (screen_info->gaussianMap, opacity, x - center, y - center, 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 - (gaussianSize * 2); if (x_diff > 0 && ylimit > 0) { for (y = 0; y < ylimit; y++) { if (ylimit == screen_info->gaussianSize) { d = screen_info->shadowTop[opacity_int * (screen_info->gaussianSize + 1) + y]; } else { d = sum_gaussian (screen_info->gaussianMap, opacity, center, y - center, width, height); } memset (&data[y * swidth + gaussianSize], d, x_diff); memset (&data[(sheight - y - 1) * swidth + gaussianSize], d, x_diff); } } /* * sides */ for (x = 0; x < xlimit; x++) { if (xlimit == screen_info->gaussianSize) { d = screen_info->shadowTop[opacity_int * (screen_info->gaussianSize + 1) + x]; } else { d = sum_gaussian (screen_info->gaussianMap, opacity, x - center, center, width, height); } x_swidth = swidth - x - 1; for (y = gaussianSize; y < sheight - gaussianSize; y++) { y_swidth = y * swidth; data[y_swidth + x] = d; data[y_swidth + x_swidth] = d; } } return ximage; } static Picture shadow_picture (ScreenInfo *screen_info, gdouble opacity, gint width, gint height, gint *wp, gint *hp) { DisplayInfo *display_info; XImage *shadowImage; Pixmap shadowPixmap; Picture shadowPicture; XRenderPictFormat *render_format; GC gc; g_return_val_if_fail (screen_info != NULL, None); TRACE ("entering"); display_info = screen_info->display_info; render_format = XRenderFindStandardFormat (display_info->dpy, PictStandardA8); g_return_val_if_fail (render_format != NULL, None); shadowImage = make_shadow (screen_info, opacity, width, height); if (shadowImage == NULL) { *wp = *hp = 0; g_warning ("(shadowImage != NULL) failed"); return (None); } shadowPixmap = XCreatePixmap (display_info->dpy, screen_info->output, shadowImage->width, shadowImage->height, 8); if (shadowPixmap == None) { XDestroyImage (shadowImage); g_warning ("(shadowPixmap != None) failed"); return None; } shadowPicture = XRenderCreatePicture (display_info->dpy, shadowPixmap, render_format, 0, NULL); if (shadowPicture == None) { XDestroyImage (shadowImage); XFreePixmap (display_info->dpy, shadowPixmap); g_warning ("(shadowPicture != None) failed"); return None; } gc = XCreateGC (display_info->dpy, shadowPixmap, 0, NULL); XPutImage (display_info->dpy, shadowPixmap, gc, shadowImage, 0, 0, 0, 0, shadowImage->width, shadowImage->height); *wp = shadowImage->width; *hp = shadowImage->height; XFreeGC (display_info->dpy, gc); XDestroyImage (shadowImage); XFreePixmap (display_info->dpy, shadowPixmap); return shadowPicture; } static Picture solid_picture (ScreenInfo *screen_info, gboolean argb, gdouble a, gdouble r, gdouble g, gdouble b) { DisplayInfo *display_info; Pixmap pixmap; Picture picture; XRenderPictureAttributes pa; XRenderPictFormat *render_format; XRenderColor c; g_return_val_if_fail (screen_info, None); TRACE ("entering"); display_info = screen_info->display_info; render_format = XRenderFindStandardFormat (display_info->dpy, argb ? PictStandardARGB32 : PictStandardA8); g_return_val_if_fail (render_format != NULL , None); pixmap = XCreatePixmap (display_info->dpy, screen_info->output, 1, 1, argb ? 32 : 8); g_return_val_if_fail (pixmap != None, None); pa.repeat = TRUE; picture = XRenderCreatePicture (display_info->dpy, pixmap, render_format, CPRepeat, &pa); if (picture == None) { XFreePixmap (display_info->dpy, 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 (display_info->dpy, PictOpSrc, picture, &c, 0, 0, 1, 1); XFreePixmap (display_info->dpy, pixmap); return picture; } static void translate_to_client_region (CWindow *cw, XserverRegion region) { DisplayInfo *display_info; ScreenInfo *screen_info; int x, y; g_return_if_fail (cw != NULL); TRACE ("window 0x%lx", cw->id); screen_info = cw->screen_info; display_info = screen_info->display_info; if (WIN_HAS_FRAME(cw)) { x = frameX (cw->c) + frameLeft (cw->c); y = frameY (cw->c) + frameTop (cw->c); } else { x = cw->attr.x + cw->attr.border_width; y = cw->attr.y + cw->attr.border_width; } XFixesTranslateRegion (display_info->dpy, region, x, y); } static XserverRegion client_size (CWindow *cw) { DisplayInfo *display_info; ScreenInfo *screen_info; XserverRegion border; g_return_val_if_fail (cw != NULL, None); TRACE ("window 0x%lx", cw->id); screen_info = cw->screen_info; display_info = screen_info->display_info; border = None; if (WIN_HAS_FRAME(cw)) { XRectangle r; Client *c; c = cw->c; r.x = frameX (c) + frameLeft (c); r.y = frameY (c) + frameTop (c); r.width = frameWidth (c) - frameLeft (c) - frameRight (c); r.height = frameHeight (c) - frameTop (c) - frameBottom (c); border = XFixesCreateRegion (display_info->dpy, &r, 1); } return border; } static XserverRegion border_size (CWindow *cw) { DisplayInfo *display_info; ScreenInfo *screen_info; XserverRegion border; g_return_val_if_fail (cw != NULL, None); TRACE ("window 0x%lx", cw->id); screen_info = cw->screen_info; display_info = screen_info->display_info; myDisplayErrorTrapPush (display_info); border = XFixesCreateRegionFromWindow (display_info->dpy, cw->id, WindowRegionBounding); XFixesSetPictureClipRegion (display_info->dpy, cw->picture, 0, 0, border); XFixesTranslateRegion (display_info->dpy, border, cw->attr.x + cw->attr.border_width, cw->attr.y + cw->attr.border_width); if (myDisplayErrorTrapPop (display_info) != Success) { return None; } return border; } static void free_win_data (CWindow *cw, gboolean delete) { DisplayInfo *display_info; ScreenInfo *screen_info; screen_info = cw->screen_info; display_info = screen_info->display_info; myDisplayErrorTrapPush (display_info); #if HAVE_NAME_WINDOW_PIXMAP if (cw->name_window_pixmap) { XFreePixmap (display_info->dpy, cw->name_window_pixmap); cw->name_window_pixmap = None; } #endif if (cw->shadow) { XRenderFreePicture (display_info->dpy, cw->shadow); cw->shadow = None; } if (cw->alphaPict) { XRenderFreePicture (display_info->dpy, cw->alphaPict); cw->alphaPict = None; } if (cw->shadowPict) { XRenderFreePicture (display_info->dpy, cw->shadowPict); cw->shadowPict = None; } if (cw->alphaBorderPict) { XRenderFreePicture (display_info->dpy, cw->alphaBorderPict); cw->alphaBorderPict = None; } if (cw->borderSize) { XFixesDestroyRegion (display_info->dpy, cw->borderSize); cw->borderSize = None; } if (cw->clientSize) { XFixesDestroyRegion (display_info->dpy, cw->clientSize); cw->clientSize = None; } if (cw->borderClip) { XFixesDestroyRegion (display_info->dpy, cw->borderClip); cw->borderClip = None; } if (cw->extents) { XFixesDestroyRegion (display_info->dpy, cw->extents); cw->extents = None; } if (delete) { if (cw->picture) { XRenderFreePicture (display_info->dpy, cw->picture); cw->picture = None; } /* No need to keep this around */ if (cw->saved_picture) { XRenderFreePicture (display_info->dpy, cw->saved_picture); cw->saved_picture = None; } if (cw->damage) { XDamageDestroy (display_info->dpy, cw->damage); cw->damage = None; } if (cw->opaque_region) { XFixesDestroyRegion (display_info->dpy, cw->opaque_region); cw->opaque_region = None; } g_slice_free (CWindow, cw); } else { if (cw->saved_picture) { XRenderFreePicture (display_info->dpy, cw->saved_picture); } cw->saved_picture = cw->picture; cw->picture = None; } myDisplayErrorTrapPopIgnored (display_info); } static Picture root_tile (ScreenInfo *screen_info) { DisplayInfo *display_info; Display *dpy; Picture picture = None; #if MONITOR_ROOT_PIXMAP Pixmap pixmap; XRenderPictureAttributes pa; XRenderPictFormat *format; gint p; Atom backgroundProps[2]; #endif g_return_val_if_fail (screen_info != NULL, None); TRACE ("entering"); display_info = screen_info->display_info; dpy = display_info->dpy; #if MONITOR_ROOT_PIXMAP backgroundProps[0] = display_info->atoms[XROOTPMAP]; backgroundProps[1] = display_info->atoms[XSETROOT]; for (p = 0; p < 2; p++) { Atom actual_type; gint actual_format; unsigned long nitems; unsigned long bytes_after; guchar *prop; gint result; result = XGetWindowProperty (dpy, screen_info->xroot, backgroundProps[p], 0, 4, False, AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &prop); if ((result == Success) && (actual_type == display_info->atoms[PIXMAP]) && (actual_format == 32) && (nitems == 1)) { memcpy (&pixmap, prop, 4); XFree (prop); pa.repeat = TRUE; format = XRenderFindVisualFormat (dpy, screen_info->visual); g_return_val_if_fail (format != NULL, None); picture = XRenderCreatePicture (dpy, pixmap, format, CPRepeat, &pa); break; } } #endif if (picture == None) { XRenderColor c; /* Background default to just plain black */ c.red = 0x0000; c.green = 0x0000; c.blue = 0x0000; c.alpha = 0xffff; picture = XRenderCreateSolidFill (dpy, &c); } return picture; } static Pixmap create_root_pixmap (ScreenInfo *screen_info) { return XCreatePixmap (myScreenGetXDisplay (screen_info), screen_info->xroot, screen_info->width, screen_info->height, screen_info->depth); } static Picture create_root_buffer (ScreenInfo *screen_info, Pixmap pixmap) { DisplayInfo *display_info; Picture pict; XRenderPictFormat *format; g_return_val_if_fail (screen_info != NULL, None); g_return_val_if_fail (pixmap != None, None); TRACE ("entering"); display_info = screen_info->display_info; format = XRenderFindVisualFormat (display_info->dpy, screen_info->visual); g_return_val_if_fail (format != NULL, None); pict = XRenderCreatePicture (display_info->dpy, pixmap, format, 0, NULL); return pict; } static Picture cursor_to_picture (ScreenInfo *screen_info, XFixesCursorImage *cursor) { DisplayInfo *display_info; XRenderPictFormat *render_format; XImage *ximage; guint32 *data; Pixmap pixmap; Picture picture; GC gc; gint i; g_return_val_if_fail (screen_info != NULL, None); TRACE ("entering"); display_info = screen_info->display_info; /* XFixesGetCursorImage() returns an array of long but actual data is 32bit */ data = g_malloc0 (cursor->width * cursor->height * sizeof (guint32)); for (i = 0; i < cursor->width * cursor->height; i++) { data[i] = cursor->pixels[i]; } ximage = XCreateImage (display_info->dpy, screen_info->visual, 32, ZPixmap, 0, (char *) data, cursor->width, cursor->height, 32, cursor->width * sizeof (guint32)); if (!ximage) { g_warning ("Failed to create the cursor image"); return None; } pixmap = XCreatePixmap (display_info->dpy, screen_info->output, cursor->width, cursor->height, 32); gc = XCreateGC (display_info->dpy, pixmap, 0, NULL); XPutImage (display_info->dpy, pixmap, gc, ximage, 0, 0, 0, 0, cursor->width, cursor->height); XFreeGC (display_info->dpy, gc); XDestroyImage (ximage); render_format = XRenderFindStandardFormat (display_info->dpy, PictStandardARGB32); picture = XRenderCreatePicture (display_info->dpy, pixmap, render_format, 0, NULL); XFreePixmap (display_info->dpy, pixmap); return picture; } #ifdef HAVE_EPOXY static gboolean check_gl_error (void) { gboolean clean = TRUE; #ifdef DEBUG /* glGetError() is expensive, keep it for debug only */ GLenum error; error = glGetError(); while (error != GL_NO_ERROR) { clean = FALSE; switch (error) { case GL_INVALID_ENUM: g_warning ("GL error: Invalid enum"); break; case GL_INVALID_VALUE: g_warning ("GL error: Invalid value"); break; case GL_INVALID_OPERATION: g_warning ("GL error: Invalid operation"); break; case GL_INVALID_FRAMEBUFFER_OPERATION: g_warning ("GL error: Invalid frame buffer operation"); break; case GL_OUT_OF_MEMORY: g_warning ("GL error: Out of memory"); break; default: break; } error = glGetError(); } #endif return clean; } static gboolean check_glx_renderer (ScreenInfo *screen_info) { const char *glRenderer; const char *blacklisted[] = { "Software Rasterizer", "Mesa X11", "llvmpipe", "SVGA3D", "virgl", NULL }; #if HAVE_PRESENT_EXTENSION const char *prefer_xpresent[] = { "Intel", /* Cannot add AMD and Radeon until the fix for * https://gitlab.freedesktop.org/xorg/driver/xf86-video-amdgpu/-/issues/10 * is included in a release. */ /* "AMD", */ /* "Radeon", */ NULL }; #endif /* HAVE_PRESENT_EXTENSION */ int i; g_return_val_if_fail (screen_info != NULL, FALSE); TRACE ("entering"); glRenderer = (const char *) glGetString (GL_RENDERER); if (glRenderer == NULL) { g_warning ("Cannot identify GLX renderer."); return FALSE; } DBG ("Using GL renderer: %s", glRenderer); #if HAVE_PRESENT_EXTENSION if (screen_info->vblank_mode == VBLANK_AUTO) { i = 0; while (prefer_xpresent[i] && !strcasestr (glRenderer, prefer_xpresent[i])) i++; if (prefer_xpresent[i]) { g_info ("Prefer XPresent with %s", glRenderer); return FALSE; } } #endif /* HAVE_PRESENT_EXTENSION */ i = 0; while (blacklisted[i] && !strcasestr (glRenderer, blacklisted[i])) i++; if (blacklisted[i]) { g_warning ("Unsupported GL renderer (%s).", glRenderer); return FALSE; } return TRUE; } static gboolean check_gl_extensions (ScreenInfo *screen_info) { g_return_val_if_fail (screen_info != NULL, FALSE); /* Note: the GL context must be current for this to work */ if (screen_info->texture_type == GL_TEXTURE_2D) { /* * If all we have is GL_TEXTURE_2D then we ought to have * GL_ARB_texture_non_power_of_two, otherwise we'll fail. */ return (epoxy_has_gl_extension ("GL_ARB_texture_non_power_of_two")); } return TRUE; } static gboolean choose_glx_settings (ScreenInfo *screen_info) { static GLint visual_attribs[] = { GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT | GLX_WINDOW_BIT, GLX_X_RENDERABLE, True, GLX_DOUBLEBUFFER, True, GLX_CONFIG_CAVEAT, GLX_NONE, GLX_DEPTH_SIZE, 1, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, GLX_RENDER_TYPE, GLX_RGBA_BIT, None }; GLint texture_type = 0; GLint texture_target = 0; GLint texture_format = 0; gboolean texture_inverted = FALSE; int n_configs, i; int value, status; GLXFBConfig *configs, fb_config; XVisualInfo *visual_info; gboolean fb_match; VisualID xvisual_id; g_return_val_if_fail (screen_info != NULL, FALSE); TRACE ("entering"); configs = glXChooseFBConfig (myScreenGetXDisplay (screen_info), screen_info->screen, visual_attribs, &n_configs); if (configs == NULL) { g_warning ("Cannot retrieve GLX frame buffer config."); return FALSE; } fb_match = FALSE; xvisual_id = XVisualIDFromVisual (screen_info->visual); for (i = 0; i < n_configs; i++) { visual_info = glXGetVisualFromFBConfig (myScreenGetXDisplay (screen_info), configs[i]); if (!visual_info) { DBG ("%i/%i: no visual info, skipped", i + 1, n_configs); continue; } if (visual_info->visualid != xvisual_id) { DBG ("%i/%i: xvisual id 0x%lx != 0x%lx, skipped", i + 1, n_configs, visual_info->visualid, xvisual_id); XFree (visual_info); continue; } XFree (visual_info); status = glXGetFBConfigAttrib (myScreenGetXDisplay (screen_info), configs[i], GLX_DRAWABLE_TYPE, &value); if (status != Success || !(value & GLX_PIXMAP_BIT)) { DBG ("%i/%i: No GLX_PIXMAP_BIT, skipped", i + 1, n_configs); continue; } status = glXGetFBConfigAttrib (myScreenGetXDisplay (screen_info), configs[i], GLX_BIND_TO_TEXTURE_TARGETS_EXT, &value); if (status != Success) { DBG ("%i/%i: No GLX_BIND_TO_TEXTURE_TARGETS_EXT, skipped", i + 1, n_configs); continue; } if (value & GLX_TEXTURE_RECTANGLE_BIT_EXT) { DBG ("Using texture GL_TEXTURE_RECTANGLE_ARB target GLX_TEXTURE_RECTANGLE_EXT"); texture_type = GL_TEXTURE_RECTANGLE_ARB; texture_target = GLX_TEXTURE_RECTANGLE_EXT; } else if (value & GLX_TEXTURE_2D_BIT_EXT) { DBG ("Using texture GL_TEXTURE_2D target GLX_TEXTURE_2D_EXT"); texture_type = GL_TEXTURE_2D; texture_target = GLX_TEXTURE_2D_EXT; } else { DBG ("%i/%i: No GLX_TEXTURE_*_BIT_EXT, skipped", i + 1, n_configs); continue; } status = glXGetFBConfigAttrib (myScreenGetXDisplay (screen_info), configs[i], GLX_RED_SIZE, &value); if (status == Success && value > 8) { DBG ("%i/%i: RGB10 config, skipped", i + 1, n_configs); continue; } status = glXGetFBConfigAttrib (myScreenGetXDisplay (screen_info), configs[i], GLX_BIND_TO_TEXTURE_RGBA_EXT, &value); if (status == Success && value == TRUE) { DBG ("Using texture format GLX_TEXTURE_FORMAT_RGBA_EXT"); texture_format = GLX_TEXTURE_FORMAT_RGBA_EXT; } else { status = glXGetFBConfigAttrib (myScreenGetXDisplay (screen_info), configs[i], GLX_BIND_TO_TEXTURE_RGB_EXT, &value); if (status == Success && value == TRUE) { DBG ("Using texture format GLX_TEXTURE_FORMAT_RGB_EXT"); texture_format = GLX_TEXTURE_FORMAT_RGB_EXT; } else { DBG ("%i/%i: No GLX_BIND_TO_TEXTURE_RGB/RGBA_EXT, skipped", i + 1, n_configs); continue; } } #if 0 status = glXGetFBConfigAttrib (myScreenGetXDisplay (screen_info), configs[i], GLX_Y_INVERTED_EXT, &value); texture_inverted = (status == Success && value == True); if (texture_inverted) { DBG ("Using texture attribute GLX_Y_INVERTED_EXT"); } #endif fb_config = configs[i]; fb_match = TRUE; break; } XFree(configs); if (fb_match == FALSE) { g_warning ("Cannot find a matching visual for the frame buffer config."); return FALSE; } DBG ("Selected texture type 0x%x target 0x%x format 0x%x (%s)", texture_type, texture_target, texture_format, texture_inverted ? "inverted" : "non inverted"); screen_info->texture_type = texture_type; screen_info->texture_target = texture_target; screen_info->texture_format = texture_format; screen_info->texture_inverted = texture_inverted; screen_info->glx_fbconfig = fb_config; return TRUE; } static void free_glx_data (ScreenInfo *screen_info) { DisplayInfo *display_info; g_return_if_fail (screen_info != NULL); display_info = screen_info->display_info; myDisplayErrorTrapPush (display_info); glXMakeCurrent (myScreenGetXDisplay (screen_info), None, NULL); if (screen_info->glx_context) { glXDestroyContext (myScreenGetXDisplay (screen_info), screen_info->glx_context); screen_info->glx_context = None; } if (screen_info->glx_window) { glXDestroyWindow (myScreenGetXDisplay (screen_info), screen_info->glx_window); screen_info->glx_window = None; } if (screen_info->has_ext_arb_sync && screen_info->gl_sync) { #if defined (glDeleteSync) glDeleteSync (screen_info->gl_sync); #else #warning glDeleteSync() not supported by libepoxy, please update your version of libepoxy #endif screen_info->gl_sync = 0; } myDisplayErrorTrapPopIgnored (display_info); } static gboolean init_glx (ScreenInfo *screen_info) { int error_base, event_base; int version; g_return_val_if_fail (screen_info != NULL, FALSE); TRACE ("entering"); if (!xfwm_is_default_screen (screen_info->gscr)) { g_warning ("GLX not enabled in multi-screen setups"); return FALSE; } if (!glXQueryExtension (myScreenGetXDisplay (screen_info), &error_base, &event_base)) { g_warning ("GLX extension missing, GLX support disabled."); return FALSE; } version = epoxy_glx_version (myScreenGetXDisplay (screen_info), screen_info->screen); DBG ("Using GLX version %d", version); if (version < 13) { g_warning ("GLX version %d is too old, GLX support disabled.", version); return FALSE; } if (!choose_glx_settings (screen_info)) { g_warning ("Cannot find a matching GLX config, vsync disabled."); return FALSE; } screen_info->glx_context = glXCreateNewContext (myScreenGetXDisplay (screen_info), screen_info->glx_fbconfig, GLX_RGBA_TYPE, 0, TRUE); if (!screen_info->glx_context) { g_warning ("Could not create GLX context."); return FALSE; } screen_info->glx_window = glXCreateWindow (myScreenGetXDisplay (screen_info), screen_info->glx_fbconfig, screen_info->output, NULL); if (!screen_info->glx_window) { g_warning ("Could not create GLX window."); free_glx_data (screen_info); return FALSE; } if (!glXMakeCurrent (myScreenGetXDisplay (screen_info), screen_info->glx_window, screen_info->glx_context)) { g_warning ("Could not make GL context current."); free_glx_data (screen_info); return FALSE; } if (!check_glx_renderer (screen_info)) { free_glx_data (screen_info); return FALSE; } if (!check_gl_extensions (screen_info)) { g_warning ("Screen is missing required GL extension, GL support disabled."); free_glx_data (screen_info); return FALSE; } glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glDisable(GL_BLEND); glLoadIdentity(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* Swap control methods available */ screen_info->has_mesa_swap_control = epoxy_has_glx_extension (myScreenGetXDisplay (screen_info), screen_info->screen, "GLX_MESA_swap_control"); screen_info->has_ext_swap_control = epoxy_has_glx_extension (myScreenGetXDisplay (screen_info), screen_info->screen, "GLX_EXT_swap_control"); screen_info->has_ext_swap_control_tear = epoxy_has_glx_extension (myScreenGetXDisplay (screen_info), screen_info->screen, "GLX_EXT_swap_control_tear"); /* Sync */ screen_info->has_ext_arb_sync = epoxy_has_gl_extension ("GL_ARB_sync"); check_gl_error(); return TRUE; } static void enable_glx_texture (ScreenInfo *screen_info) { g_return_if_fail (screen_info != NULL); TRACE ("entering"); glBindTexture(screen_info->texture_type, screen_info->rootTexture); glEnable(screen_info->texture_type); } static void disable_glx_texture (ScreenInfo *screen_info) { g_return_if_fail (screen_info != NULL); TRACE ("entering"); glBindTexture(screen_info->texture_type, None); glDisable(screen_info->texture_type); } static void fence_create (ScreenInfo *screen_info, gushort buffer) { #ifdef HAVE_XSYNC if (screen_info->fence[buffer] == None) { DBG ("Create fence for buffer %i", buffer); screen_info->fence[buffer] = XSyncCreateFence (myScreenGetXDisplay (screen_info), screen_info->rootPixmap[buffer], FALSE); } #endif /* HAVE_XSYNC */ } static void fence_reset (ScreenInfo *screen_info, gushort buffer) { #ifdef HAVE_XSYNC if (screen_info->fence[buffer] == None) { return; } DBG ("Reset fence for buffer %i", buffer); XSyncResetFence(myScreenGetXDisplay (screen_info), screen_info->fence[buffer]); #endif /* HAVE_XSYNC */ } static gboolean is_fence_triggered (ScreenInfo *screen_info, gushort buffer) { #ifdef HAVE_XSYNC Bool triggered = False; if (screen_info->fence[buffer] == None) { DBG ("No fence for buffer %i", buffer); return TRUE; } if (!XSyncQueryFence(myScreenGetXDisplay (screen_info), screen_info->fence[buffer], &triggered)) { DBG ("Cannot query fence for buffer %i", buffer); return TRUE; } DBG ("Fence for buffer %i is %striggered", buffer, triggered ? "" : "not "); return (gboolean) triggered; #else return TRUE; #endif /* HAVE_XSYNC */ } static void fence_sync (ScreenInfo *screen_info, gushort buffer) { #ifdef HAVE_XSYNC #ifdef DEBUG gint64 t1, t2; #endif /* DEBUG */ if (is_fence_triggered (screen_info, buffer)) { DBG ("Fence for buffer %i already triggered", buffer); return; } TRACE ("Awaiting fence for buffer %i", buffer); #ifdef DEBUG t1 = g_get_monotonic_time (); #endif /* DEBUG */ XSyncTriggerFence(myScreenGetXDisplay (screen_info), screen_info->fence[buffer]); XSyncAwaitFence(myScreenGetXDisplay (screen_info), &screen_info->fence[buffer], 1); #ifdef DEBUG t2 = g_get_monotonic_time (); DBG ("Fence sync time %luμs", t2 - t1); #endif /* DEBUG */ fence_reset (screen_info, buffer); #else XSync (myScreenGetXDisplay (screen_info), FALSE); glXWaitX (); #endif /* HAVE_XSYNC */ } static void fence_destroy (ScreenInfo *screen_info, gushort buffer) { #ifdef HAVE_XSYNC if (screen_info->fence[buffer]) { DBG ("Destroying fence for buffer %i", buffer); XSyncDestroyFence (myScreenGetXDisplay (screen_info), screen_info->fence[buffer]); screen_info->fence[buffer] = None; } #endif /* HAVE_XSYNC */ } static void set_swap_interval (ScreenInfo *screen_info, gushort buffer) { #if defined (glXSwapIntervalEXT) if (screen_info->has_ext_swap_control) { if (screen_info->has_ext_swap_control_tear) { DBG ("Setting adaptive vsync using GLX_EXT_swap_control"); glXSwapIntervalEXT (myScreenGetXDisplay (screen_info), screen_info->glx_drawable[buffer], -1); } else { DBG ("Setting swap interval using GLX_EXT_swap_control"); glXSwapIntervalEXT (myScreenGetXDisplay (screen_info), screen_info->glx_drawable[buffer], 1); } return; } #else #warning glXSwapIntervalEXT() not supported by libepoxy, please update your version of libepoxy #endif #if defined (glXSwapIntervalMESA) if (screen_info->has_mesa_swap_control) { DBG ("Setting swap interval using GLX_MESA_swap_control"); glXSwapIntervalMESA (1); return; } #else #warning glXSwapIntervalMESA() not supported by libepoxy, please update your version of libepoxy #endif DBG ("No swap control available"); } static void create_glx_drawable (ScreenInfo *screen_info, gushort buffer) { int pixmap_attribs[] = { GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT, GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGB_EXT, None }; g_return_if_fail (screen_info != NULL); TRACE ("entering"); pixmap_attribs[1] = screen_info->texture_target; pixmap_attribs[3] = screen_info->texture_format; screen_info->glx_drawable[buffer] = glXCreatePixmap (myScreenGetXDisplay (screen_info), screen_info->glx_fbconfig, screen_info->rootPixmap[buffer], pixmap_attribs); check_gl_error(); TRACE ("created GLX pixmap 0x%lx for buffer %i", screen_info->glx_drawable[buffer], buffer); } static void bind_glx_texture (ScreenInfo *screen_info, gushort buffer) { g_return_if_fail (screen_info != NULL); TRACE ("entering"); if (screen_info->rootTexture == None) { glGenTextures(1, &screen_info->rootTexture); TRACE ("generated texture 0x%x", screen_info->rootTexture); } if (screen_info->glx_drawable[buffer] == None) { create_glx_drawable (screen_info, buffer); set_swap_interval (screen_info, buffer); } TRACE ("(re)Binding GLX pixmap 0x%lx to texture 0x%x", screen_info->glx_drawable[buffer], screen_info->rootTexture); enable_glx_texture (screen_info); glXBindTexImageEXT (myScreenGetXDisplay (screen_info), screen_info->glx_drawable[buffer], GLX_FRONT_EXT, NULL); check_gl_error(); } static void unbind_glx_texture (ScreenInfo *screen_info, gushort buffer) { g_return_if_fail (screen_info != NULL); TRACE ("entering"); if (screen_info->glx_drawable[buffer]) { TRACE ("unbinding GLX drawable 0x%lx", screen_info->glx_drawable[buffer]); glXReleaseTexImageEXT (myScreenGetXDisplay (screen_info), screen_info->glx_drawable[buffer], GLX_FRONT_EXT); } } static void destroy_glx_drawable (ScreenInfo *screen_info, gushort buffer) { g_return_if_fail (screen_info != NULL); TRACE ("entering"); if (screen_info->glx_drawable[buffer]) { unbind_glx_texture (screen_info, buffer); glXDestroyPixmap(myScreenGetXDisplay (screen_info), screen_info->glx_drawable[buffer]); screen_info->glx_drawable[buffer] = None; } if (screen_info->rootTexture) { disable_glx_texture (screen_info); glDeleteTextures (1, &screen_info->rootTexture); screen_info->rootTexture = None; } check_gl_error(); } static void set_glx_scale (ScreenInfo *screen_info, gint width, gint height, double scale) { if (screen_info->texture_type == GL_TEXTURE_RECTANGLE_ARB) { glScaled ((double) width * scale, (double) height * scale, 1.0); } else { glScaled(1.0 * scale, 1.0 * scale, 1.0); } } static void redraw_glx_rects (ScreenInfo *screen_info, XRectangle *rects, int nrects) { int i; glBegin(GL_QUADS); for (i = 0; i < nrects; i++) { double texture_x1 = (double) (rects[i].x) / screen_info->width; double texture_y1 = (double) (rects[i].y) / screen_info->height; double texture_x2 = (double) (rects[i].x + rects[i].width) / screen_info->width; double texture_y2 = (double) (rects[i].y + rects[i].height) / screen_info->height; double vertice_x1 = 2 * texture_x1 - 1.0; double vertice_y1 = -2 * texture_y1 + 1.0; double vertice_x2 = 2 * texture_x2 - 1.0; double vertice_y2 = -2 * texture_y2 + 1.0; if (screen_info->texture_inverted) { texture_y1 = 1.0 - texture_y1; texture_y2 = texture_y2 - 1.0; } TRACE ("Rect#%i: Texture (%.2f,%.2f,%.2f,%.2f) to vertice (%.2f,%.2f,%.2f,%.2f)", i, texture_x1, texture_y1, texture_x2, texture_y2, vertice_x1, vertice_y1, vertice_x2, vertice_y2); glTexCoord2f (texture_x1, texture_y1); glVertex2f (vertice_x1, vertice_y1); glTexCoord2f (texture_x2, texture_y1); glVertex2f (vertice_x2, vertice_y1); glTexCoord2f (texture_x2, texture_y2); glVertex2f (vertice_x2, vertice_y2); glTexCoord2f (texture_x1, texture_y2); glVertex2f (vertice_x1, vertice_y2); } glEnd(); } static void redraw_glx_screen (ScreenInfo *screen_info) { XRectangle root_rect = { 0, 0, screen_info->width, screen_info->height}; redraw_glx_rects (screen_info, &root_rect, 1); } static void redraw_glx_texture (ScreenInfo *screen_info, gushort buffer) { #ifdef DEBUG gint64 t1, t2; #endif /* DEBUG */ g_return_if_fail (screen_info != NULL); TRACE ("(re)Drawing GLX pixmap 0x%lx/texture 0x%x", screen_info->glx_drawable[buffer], screen_info->rootTexture); fence_sync (screen_info, buffer); #ifdef DEBUG t1 = g_get_monotonic_time (); #endif /* DEBUG */ if (screen_info->has_ext_arb_sync) { #if defined (glDeleteSync) glDeleteSync (screen_info->gl_sync); #else #warning glDeleteSync() not supported by libepoxy, please update your version of libepoxy #endif screen_info->gl_sync = 0; } bind_glx_texture (screen_info, buffer); glDrawBuffer (GL_BACK); glViewport(0, 0, screen_info->width, screen_info->height); glMatrixMode(GL_TEXTURE); glPushMatrix(); if (screen_info->zoomed) { /* Reuse the values from the XRender matrix */ XFixed zf = screen_info->transform.matrix[0][0]; XFixed xp = screen_info->transform.matrix[0][2]; XFixed yp = screen_info->transform.matrix[1][2]; double zoom = XFixedToDouble (zf); double x = XFixedToDouble (xp) / (screen_info->width * zoom); double y = XFixedToDouble (yp) / (screen_info->height * zoom); glTexParameteri(screen_info->texture_type, GL_TEXTURE_MIN_FILTER, screen_info->texture_filter); glTexParameteri(screen_info->texture_type, GL_TEXTURE_MAG_FILTER, screen_info->texture_filter); set_glx_scale (screen_info, screen_info->width, screen_info->height, zoom); glTranslated (x, y, 0.0); } else { set_glx_scale (screen_info, screen_info->width, screen_info->height, 1.0); glTranslated (0.0, 0.0, 0.0); } redraw_glx_screen (screen_info); glXSwapBuffers (myScreenGetXDisplay (screen_info), screen_info->glx_window); glPopMatrix(); unbind_glx_texture (screen_info, buffer); if (screen_info->has_ext_arb_sync) { #if defined (glFenceSync) screen_info->gl_sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); #else #warning glFenceSync() not supported by libepoxy, please update your version of libepoxy #endif } #ifdef DEBUG t2 = g_get_monotonic_time (); DBG ("Swap buffer time %luμs", t2 - t1); #endif /* DEBUG */ check_gl_error(); } #endif /* HAVE_EPOXY */ #ifdef HAVE_PRESENT_EXTENSION static void present_error (ScreenInfo *screen_info, int error_code) { char buf[64]; XGetErrorText (myScreenGetXDisplay (screen_info), error_code, buf, 63); g_warning ("Dismissing XPresent as unusable, error %i (%s)", error_code, buf); screen_info->present_pending = FALSE; screen_info->use_present = FALSE; } static void present_flip (ScreenInfo *screen_info, XserverRegion region, gushort buffer) { static guint32 present_serial; DisplayInfo *display_info; int result; g_return_if_fail (screen_info != NULL); g_return_if_fail (region != None); TRACE ("serial %d", present_serial); display_info = screen_info->display_info; myDisplayErrorTrapPush (display_info); XPresentPixmap (display_info->dpy, screen_info->output, screen_info->rootPixmap[buffer], present_serial++, None, region, 0, 0, None, None, None, PresentOptionNone, 0, 1, 0, NULL, 0); result = myDisplayErrorTrapPop (display_info); if (result != 0) { present_error (screen_info, result); return; } screen_info->present_pending = TRUE; DBG ("present flip requested, present pending..."); } #endif /* HAVE_PRESENT_EXTENSION */ static XserverRegion win_extents (CWindow *cw) { DisplayInfo *display_info; ScreenInfo *screen_info; XRectangle r; g_return_val_if_fail (cw != NULL, None); TRACE ("window 0x%lx", cw->id); screen_info = cw->screen_info; display_info = screen_info->display_info; r.x = cw->attr.x; r.y = cw->attr.y; r.width = cw->attr.width + cw->attr.border_width * 2; r.height = cw->attr.height + cw->attr.border_width * 2; /* We apply a shadow to the window if: - It's a window with a frame and the user asked for shadows under regular windows, - it's an override redirect window that is not shaped, not an argb and the user asked for shadows on so called "popup" windows. */ if ((screen_info->params->show_popup_shadow && WIN_IS_OVERRIDE(cw) && !(WIN_IS_ARGB(cw) || WIN_IS_SHAPED(cw)) && !WIN_IS_FULLSCREEN(cw)) || (screen_info->params->show_frame_shadow && !WIN_IS_OVERRIDE(cw) && !WIN_NO_SHADOW(cw) && !WIN_IS_DOCK(cw) && !WIN_IS_MAXIMIZED(cw) && (WIN_HAS_FRAME(cw) || !(WIN_IS_ARGB(cw) || WIN_IS_SHAPED(cw)))) || (screen_info->params->show_dock_shadow && WIN_IS_DOCK(cw) && !WIN_NO_SHADOW(cw) && !WIN_IS_OVERRIDE(cw) && (!WIN_IS_SHAPED(cw)))) { XRectangle sr; TRACE ("window 0x%lx has extents", cw->id); cw->shadow_dx = SHADOW_OFFSET_X + screen_info->params->shadow_delta_x; cw->shadow_dy = SHADOW_OFFSET_Y + screen_info->params->shadow_delta_y; if (!(cw->shadow)) { double shadow_opacity; shadow_opacity = (double) screen_info->params->frame_opacity * (screen_info->params->shadow_opacity / 100.0) * cw->opacity / (NET_WM_OPAQUE * 100.0); cw->shadow = shadow_picture (screen_info, shadow_opacity, cw->attr.width + 2 * cw->attr.border_width, cw->attr.height + 2 * cw->attr.border_width, &cw->shadow_width, &cw->shadow_height); } sr.x = cw->attr.x + cw->shadow_dx; sr.y = cw->attr.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; } } else if (cw->shadow) { XRenderFreePicture (display_info->dpy, cw->shadow); cw->shadow = None; } return XFixesCreateRegion (display_info->dpy, &r, 1); } static void get_paint_bounds (CWindow *cw, gint *x, gint *y, guint *w, guint *h) { g_return_if_fail (cw != NULL); TRACE ("window 0x%lx", cw->id); #if HAVE_NAME_WINDOW_PIXMAP *x = cw->attr.x; *y = cw->attr.y; *w = cw->attr.width + 2 * cw->attr.border_width; *h = cw->attr.height + 2 * cw->attr.border_width; #else *x = cw->attr.x + cw->attr.border_width; *y = cw->attr.y + cw->attr.border_width; *w = cw->attr.width; *h = cw->attr.height; #endif } static XRenderPictFormat * get_window_format (CWindow *cw) { DisplayInfo *display_info; ScreenInfo *screen_info; XRenderPictFormat *format; g_return_val_if_fail (cw != NULL, NULL); TRACE ("window 0x%lx", cw->id); screen_info = cw->screen_info; display_info = screen_info->display_info; format = XRenderFindVisualFormat (display_info->dpy, cw->attr.visual); if (!format) { format = XRenderFindVisualFormat (display_info->dpy, DefaultVisual (display_info->dpy, screen_info->screen)); } return format; } static Picture get_window_picture (CWindow *cw) { DisplayInfo *display_info; ScreenInfo *screen_info; XRenderPictureAttributes pa; XRenderPictFormat *format; Drawable draw; g_return_val_if_fail (cw != NULL, None); TRACE ("window 0x%lx", cw->id); draw = cw->id; screen_info = cw->screen_info; display_info = screen_info->display_info; #if HAVE_NAME_WINDOW_PIXMAP myDisplayErrorTrapPush (display_info); if ((display_info->have_name_window_pixmap) && (cw->name_window_pixmap == None)) { cw->name_window_pixmap = XCompositeNameWindowPixmap (display_info->dpy, cw->id); } if ((myDisplayErrorTrapPop (display_info) == Success) && (cw->name_window_pixmap != None)) { draw = cw->name_window_pixmap; } #endif format = get_window_format (cw); if (format) { Picture pict; myDisplayErrorTrapPush (display_info); pa.subwindow_mode = IncludeInferiors; pict = XRenderCreatePicture (display_info->dpy, draw, format, CPSubwindowMode, &pa); if (myDisplayErrorTrapPop (display_info) == Success) { return pict; } } return None; } static XserverRegion get_screen_region (ScreenInfo *screen_info) { DisplayInfo *display_info; XserverRegion region; XRectangle r; display_info = screen_info->display_info; if (screen_info->width > 0 && screen_info->height > 0) { r.x = 0; r.y = 0; r.width = screen_info->width; r.height = screen_info->height; region = XFixesCreateRegion (display_info->dpy, &r, 1); } else { region = XFixesCreateRegion (display_info->dpy, NULL, 0); } return region; } static void unredirect_win (CWindow *cw) { ScreenInfo *screen_info; DisplayInfo *display_info; g_return_if_fail (cw != NULL); TRACE ("window 0x%lx", cw->id); if (WIN_IS_REDIRECTED(cw)) { screen_info = cw->screen_info; display_info = screen_info->display_info; myDisplayErrorTrapPush (display_info); XCompositeUnredirectWindow (display_info->dpy, cw->id, CompositeRedirectManual); myDisplayErrorTrapPopIgnored (display_info); free_win_data (cw, FALSE); cw->redirected = FALSE; TRACE ("window 0x%lx unredirected, wins_unredirected is %i", cw->id, screen_info->wins_unredirected); } } static void paint_root (ScreenInfo *screen_info, Picture paint_buffer) { g_return_if_fail (screen_info != NULL); TRACE ("entering"); if (screen_info->rootTile == None) { screen_info->rootTile = root_tile (screen_info); g_return_if_fail (screen_info->rootTile != None); } XRenderComposite (myScreenGetXDisplay (screen_info), PictOpSrc, screen_info->rootTile, None, paint_buffer, 0, 0, 0, 0, 0, 0, screen_info->width, screen_info->height); } static void paint_cursor (ScreenInfo *screen_info, XserverRegion region, Picture paint_buffer) { XFixesSetPictureClipRegion (myScreenGetXDisplay (screen_info), paint_buffer, 0, 0, region); XRenderComposite (myScreenGetXDisplay (screen_info), PictOpOver, screen_info->cursorPicture, None, paint_buffer, 0, 0, 0, 0, screen_info->cursorLocation.x, screen_info->cursorLocation.y, screen_info->cursorLocation.width, screen_info->cursorLocation.height); } static void paint_win (CWindow *cw, XserverRegion region, Picture paint_buffer, gboolean solid_part) { ScreenInfo *screen_info; DisplayInfo *display_info; gboolean paint_solid; g_return_if_fail (cw != NULL); TRACE ("window 0x%lx", cw->id); screen_info = cw->screen_info; display_info = screen_info->display_info; paint_solid = (solid_part && WIN_IS_OPAQUE(cw)); if (WIN_HAS_FRAME(cw) && (screen_info->params->frame_opacity < 100)) { int frame_x, frame_y, frame_width, frame_height; int frame_top, frame_bottom, frame_left, frame_right; frame_x = cw->attr.x; frame_y = cw->attr.y; frame_width = cw->attr.width; frame_height = cw->attr.height; frame_top = frameTop (cw->c); frame_bottom = frameBottom (cw->c); frame_left = frameLeft (cw->c); frame_right = frameRight (cw->c); if (!solid_part) { if (!cw->alphaBorderPict) { double frame_opacity; frame_opacity = (double) cw->opacity * screen_info->params->frame_opacity / (NET_WM_OPAQUE * 100.0); cw->alphaBorderPict = solid_picture (screen_info, FALSE, frame_opacity, 0.0, /* red */ 0.0, /* green */ 0.0 /* blue */); } /* Top Border (title bar) */ XRenderComposite (display_info->dpy, PictOpOver, cw->picture, cw->alphaBorderPict, paint_buffer, 0, 0, 0, 0, frame_x, frame_y, frame_width, frame_top); /* Bottom Border */ XRenderComposite (display_info->dpy, PictOpOver, cw->picture, cw->alphaBorderPict, paint_buffer, 0, frame_height - frame_bottom, 0, 0, frame_x, frame_y + frame_height - frame_bottom, frame_width, frame_bottom); /* Left Border */ XRenderComposite (display_info->dpy, PictOpOver, cw->picture, cw->alphaBorderPict, paint_buffer, 0, frame_top, 0, 0, frame_x, frame_y + frame_top, frame_left, frame_height - frame_top - frame_bottom); /* Right Border */ XRenderComposite (display_info->dpy, PictOpOver, cw->picture, cw->alphaBorderPict, paint_buffer, frame_width - frame_right, frame_top, 0, 0, frame_x + frame_width - frame_right, frame_y + frame_top, frame_right, frame_height - frame_top - frame_bottom); } /* Client Window */ if (paint_solid) { XFixesSetPictureClipRegion (display_info->dpy, paint_buffer, 0, 0, region); XRenderComposite (display_info->dpy, PictOpSrc, cw->picture, None, paint_buffer, frame_left, frame_top, 0, 0, frame_x + frame_left, frame_y + frame_top, frame_width - frame_left - frame_right, frame_height - frame_top - frame_bottom); /* clientSize is set in paint_all() prior to calling paint_win() */ XFixesSubtractRegion (display_info->dpy, region, region, cw->clientSize); } else if (!solid_part) { XRenderComposite (display_info->dpy, PictOpOver, cw->picture, cw->alphaPict, paint_buffer, frame_left, frame_top, 0, 0, frame_x + frame_left, frame_y + frame_top, frame_width - frame_left - frame_right, frame_height - frame_top - frame_bottom); } } else { gint x, y; guint w, h; get_paint_bounds (cw, &x, &y, &w, &h); if (paint_solid) { XFixesSetPictureClipRegion (display_info->dpy, paint_buffer, 0, 0, region); XRenderComposite (display_info->dpy, PictOpSrc, cw->picture, None, paint_buffer, 0, 0, 0, 0, x, y, w, h); XFixesSubtractRegion (display_info->dpy, region, region, cw->borderSize); } else if (!solid_part) { XRenderComposite (display_info->dpy, PictOpOver, cw->picture, cw->alphaPict, paint_buffer, 0, 0, 0, 0, x, y, w, h); } } } static void clip_opaque_region (CWindow *cw, XserverRegion region) { ScreenInfo *screen_info; DisplayInfo *display_info; XserverRegion opaque_region; g_return_if_fail (cw != NULL); TRACE ("window 0x%lx", cw->id); if (cw->opaque_region == None) { TRACE ("window 0x%lx has no opaque region", cw->id); return; } screen_info = cw->screen_info; display_info = screen_info->display_info; opaque_region = XFixesCreateRegion (display_info->dpy, NULL, 0); XFixesCopyRegion (display_info->dpy, opaque_region, cw->opaque_region); translate_to_client_region (cw, opaque_region); /* cw->borderSize and cw->clientSize are already updated in paint_all() */ if (cw->clientSize) { XFixesIntersectRegion (display_info->dpy, opaque_region, opaque_region, cw->clientSize); } XFixesIntersectRegion (display_info->dpy, opaque_region, opaque_region, cw->borderSize); XFixesSubtractRegion (display_info->dpy, region, region, opaque_region); XFixesDestroyRegion (display_info->dpy, opaque_region); } static int get_region_bounds (Display *dpy, XserverRegion region, XRectangle *bounds) { XRectangle *rects; int nrects; rects = XFixesFetchRegionAndBounds (dpy, region, &nrects, bounds); XFree (rects); return nrects; } static gboolean is_region_empty (Display *dpy, XserverRegion region) { XRectangle bounds; int nrects; nrects = get_region_bounds (dpy, region, &bounds); return (nrects == 0 || bounds.width == 0 || bounds.height == 0); } static void paint_all (ScreenInfo *screen_info, XserverRegion region, gushort buffer) { DisplayInfo *display_info; XserverRegion paint_region; XRectangle region_bounds; Picture paint_buffer; Display *dpy; GList *list; gint screen_width; gint screen_height; CWindow *cw; TRACE ("buffer %d", buffer); g_return_if_fail (screen_info); display_info = screen_info->display_info; dpy = display_info->dpy; screen_width = screen_info->width; screen_height = screen_info->height; myDisplayErrorTrapPush (display_info); /* Create root buffer if not done yet */ if (screen_info->rootPixmap[buffer] == None) { screen_info->rootPixmap[buffer] = create_root_pixmap (screen_info); #ifdef HAVE_EPOXY if (screen_info->use_glx) { fence_create (screen_info, buffer); } #endif /* HAVE_EPOXY */ } if (screen_info->rootBuffer[buffer] == None) { screen_info->rootBuffer[buffer] = create_root_buffer (screen_info, screen_info->rootPixmap[buffer]); } if (screen_info->zoomed && !screen_info->use_glx) { if (screen_info->zoomBuffer == None) { Pixmap pixmap; pixmap = create_root_pixmap (screen_info); screen_info->zoomBuffer = create_root_buffer (screen_info, pixmap); XFreePixmap (display_info->dpy, pixmap); } paint_buffer = screen_info->zoomBuffer; } else { paint_buffer = screen_info->rootBuffer[buffer]; } /* Copy the original given region */ paint_region = XFixesCreateRegion (dpy, NULL, 0); XFixesCopyRegion (dpy, paint_region, region); /* * Painting from top to bottom, reducing the clipping area at each iteration. * Only the opaque windows are painted 1st. */ for (list = screen_info->cwindows; list; list = g_list_next (list)) { cw = (CWindow *) list->data; TRACE ("painting forward 0x%lx", cw->id); if (!WIN_IS_VISIBLE(cw) || !WIN_IS_DAMAGED(cw)) { TRACE ("skipped, not damaged or not viewable 0x%lx", cw->id); cw->skipped = TRUE; continue; } if (!WIN_IS_REDIRECTED(cw)) { TRACE ("skipped, not redirected 0x%lx", cw->id); cw->skipped = TRUE; continue; } if ((cw->attr.x + cw->attr.width < 1) || (cw->attr.y + cw->attr.height < 1) || (cw->attr.x >= screen_width) || (cw->attr.y >= screen_height)) { TRACE ("skipped, off screen 0x%lx", cw->id); cw->skipped = TRUE; continue; } if (cw->extents == None) { cw->extents = win_extents (cw); } if (cw->picture == None) { cw->picture = get_window_picture (cw); } if (cw->borderSize == None) { cw->borderSize = border_size (cw); } if (cw->clientSize == None) { cw->clientSize = client_size (cw); } if (WIN_IS_OPAQUE(cw)) { paint_win (cw, paint_region, paint_buffer, TRUE); } if (cw->borderClip == None) { cw->borderClip = XFixesCreateRegion (dpy, NULL, 0); XFixesCopyRegion (dpy, cw->borderClip, paint_region); } if ((cw->opacity == NET_WM_OPAQUE) && !WIN_IS_SHADED(cw)) { clip_opaque_region (cw, paint_region); } cw->skipped = FALSE; } /* * region has changed because of the XFixesSubtractRegion (), * reapply clipping for the last iteration. */ XFixesSetPictureClipRegion (dpy, paint_buffer, 0, 0, paint_region); if (!is_region_empty (dpy, paint_region)) { paint_root (screen_info, paint_buffer); } /* * Painting from bottom to top, translucent windows and shadows are painted now... */ for (list = g_list_last(screen_info->cwindows); list; list = g_list_previous (list)) { XserverRegion shadowClip; cw = (CWindow *) list->data; shadowClip = None; TRACE ("painting backward 0x%lx", cw->id); if (cw->skipped) { TRACE ("skipped 0x%lx", cw->id); continue; } if (cw->shadow) { shadowClip = XFixesCreateRegion (dpy, NULL, 0); XFixesSubtractRegion (dpy, shadowClip, cw->borderClip, cw->borderSize); XFixesSetPictureClipRegion (dpy, paint_buffer, 0, 0, shadowClip); XRenderComposite (dpy, PictOpOver, screen_info->blackPicture, cw->shadow, paint_buffer, 0, 0, 0, 0, cw->attr.x + cw->shadow_dx, cw->attr.y + cw->shadow_dy, cw->shadow_width, cw->shadow_height); } if (cw->picture) { if ((cw->opacity != NET_WM_OPAQUE) && !(cw->alphaPict)) { cw->alphaPict = solid_picture (screen_info, FALSE, (double) cw->opacity / NET_WM_OPAQUE, 0.0, /* red */ 0.0, /* green */ 0.0 /* blue */); } XFixesIntersectRegion (dpy, cw->borderClip, cw->borderClip, cw->borderSize); XFixesSetPictureClipRegion (dpy, paint_buffer, 0, 0, cw->borderClip); paint_win (cw, paint_region, paint_buffer, FALSE); } if (shadowClip) { XFixesDestroyRegion (dpy, shadowClip); } if (cw->borderClip) { XFixesDestroyRegion (dpy, cw->borderClip); cw->borderClip = None; } } TRACE ("copying data back to screen"); #ifdef HAVE_EPOXY if (screen_info->use_glx) { if (screen_info->zoomed && screen_info->cursor_is_zoomed) { paint_cursor (screen_info, region, paint_buffer); } fence_reset (screen_info, buffer); } else #endif /* HAVE_EPOXY */ { if (screen_info->zoomed) { if (screen_info->cursor_is_zoomed) { paint_cursor (screen_info, region, paint_buffer); } /* Fixme: copy back whole screen if zoomed It would be better to scale the clipping region if possible. */ XFixesSetPictureClipRegion (dpy, screen_info->rootBuffer[buffer], 0, 0, None); XFixesSetPictureClipRegion (dpy, paint_buffer, 0, 0, None); } else { /* Set clipping back to the given region */ XFixesSetPictureClipRegion (dpy, screen_info->rootBuffer[buffer], 0, 0, region); } } #ifdef HAVE_PRESENT_EXTENSION if (screen_info->use_present) { if (screen_info->zoomed) { XRenderComposite (dpy, PictOpSrc, screen_info->zoomBuffer, None, screen_info->rootBuffer[buffer], 0, 0, 0, 0, 0, 0, screen_width, screen_height); } present_flip (screen_info, region, buffer); } else #endif /* HAVE_PRESENT_EXTENSION */ #ifdef HAVE_EPOXY if (screen_info->use_glx) { redraw_glx_texture (screen_info, buffer); } else #endif /* HAVE_EPOXY */ { if (screen_info->zoomed) { XRenderComposite (dpy, PictOpSrc, screen_info->zoomBuffer, None, screen_info->rootPicture, 0, 0, 0, 0, 0, 0, screen_width, screen_height); } else { get_region_bounds (dpy, region, ®ion_bounds); XRenderComposite (dpy, PictOpSrc, paint_buffer, None, screen_info->rootPicture, region_bounds.x, region_bounds.y, region_bounds.x, region_bounds.y, region_bounds.x, region_bounds.y, region_bounds.width, region_bounds.height); } XFlush (dpy); } XFixesDestroyRegion (dpy, paint_region); myDisplayErrorTrapPopIgnored (display_info); } static void remove_timeouts (ScreenInfo *screen_info) { if (screen_info->compositor_timeout_id != 0) { g_source_remove (screen_info->compositor_timeout_id); screen_info->compositor_timeout_id = 0; } } static gboolean repair_screen (ScreenInfo *screen_info) { DisplayInfo *display_info; XserverRegion damage; g_return_val_if_fail (screen_info, FALSE); TRACE ("entering"); if (!screen_info->compositor_active) { return FALSE; } if (screen_info->width == 0 || screen_info->height == 0) { return FALSE; } #ifdef HAVE_PRESENT_EXTENSION /* * We do not paint the screen because we are waiting for * a pending present notification, do not cancel the callback yet... */ if (screen_info->use_present && screen_info->present_pending) { DBG ("Waiting for Present"); return (screen_info->allDamage != None); } #endif /* HAVE_PRESENT_EXTENSION */ #ifdef HAVE_EPOXY /* * We do not paint the screen because we are waiting for * the GL pipeline to complete, do not cancel the callback yet... */ if (screen_info->use_glx && screen_info->gl_sync) { #if defined (GL_SIGNALED) GLint status = GL_SIGNALED; #if defined (glGetSynciv) glGetSynciv(screen_info->gl_sync, GL_SYNC_STATUS, sizeof(GLint), NULL, &status); #else #warning glGetSynciv() not supported by libepoxy, please update your version of libepoxy #endif if (status != GL_SIGNALED) { DBG ("Waiting for GL pipeline"); return (screen_info->allDamage != None); } } #else #warning GL_SIGNALED not supported by libepoxy, please update your version of libepoxy #endif #endif /* HAVE_EPOXY */ display_info = screen_info->display_info; damage = screen_info->allDamage; if (damage) { if (screen_info->use_n_buffers > 1) { if (screen_info->prevDamage) { XFixesUnionRegion(display_info->dpy, screen_info->prevDamage, screen_info->prevDamage, damage); damage = screen_info->prevDamage; } } remove_timeouts (screen_info); paint_all (screen_info, damage, screen_info->current_buffer); if (screen_info->use_n_buffers > 1) { screen_info->current_buffer = (screen_info->current_buffer + 1) % screen_info->use_n_buffers; if (screen_info->prevDamage) { XFixesDestroyRegion (display_info->dpy, screen_info->prevDamage); } screen_info->prevDamage = screen_info->allDamage; } else { XFixesDestroyRegion (display_info->dpy, screen_info->allDamage); } screen_info->allDamage = None; } return FALSE; } static gboolean compositor_timeout_cb (gpointer data) { static guint number_of_retries = 0; ScreenInfo *screen_info; gboolean retry; screen_info = (ScreenInfo *) data; retry = repair_screen (screen_info); if (retry) { if (number_of_retries <= 100) { number_of_retries++; } if (number_of_retries == 100) { DBG ("Throttling repaint after 100 unsuccessful retries"); /* Stop the current timeout repain */ g_source_remove (screen_info->compositor_timeout_id); retry = FALSE; /* Recreate a throttled timeout instead */ screen_info->compositor_timeout_id = g_timeout_add_full (TIMEOUT_THROTTLED_REPAINT_PRIORITY, TIMEOUT_THROTTLED_REPAINT_MS, compositor_timeout_cb, screen_info, NULL); } } else { screen_info->compositor_timeout_id = 0; number_of_retries = 0; } return retry; } static void add_repair (ScreenInfo *screen_info) { if (screen_info->compositor_timeout_id == 0) { screen_info->compositor_timeout_id = g_timeout_add_full (TIMEOUT_REPAINT_PRIORITY, TIMEOUT_REPAINT_MS, compositor_timeout_cb, screen_info, NULL); } } static void add_damage (ScreenInfo *screen_info, XserverRegion damage) { DisplayInfo *display_info; TRACE ("entering"); if (damage == None) { return; } if (screen_info->screenRegion == None) { screen_info->screenRegion = get_screen_region (screen_info); } display_info = screen_info->display_info; XFixesIntersectRegion (display_info->dpy, damage, damage, screen_info->screenRegion); if (screen_info->allDamage != None) { XFixesUnionRegion (display_info->dpy, screen_info->allDamage, screen_info->allDamage, damage); XFixesDestroyRegion (display_info->dpy, damage); } else { screen_info->allDamage = damage; } /* The per-screen allDamage region is freed by repair_screen () */ add_repair (screen_info); } static void fix_region (CWindow *cw, XserverRegion region) { GList *list; ScreenInfo *screen_info; DisplayInfo *display_info; screen_info = cw->screen_info; display_info = screen_info->display_info; /* Exclude opaque windows in front of the given area */ for (list = screen_info->cwindows; list; list = g_list_next (list)) { CWindow *cw2; cw2 = (CWindow *) list->data; if (cw2 == cw) { break; } else if (WIN_IS_OPAQUE(cw2) && WIN_IS_VISIBLE(cw2)) { /* Make sure the window's areas are up-to-date... */ if (cw2->picture == None) { cw2->picture = get_window_picture (cw2); } if (cw2->borderSize == None) { cw2->borderSize = border_size (cw2); } if (cw2->clientSize == None) { cw2->clientSize = client_size (cw2); } /* ...before subtracting them from the damaged zone. */ if ((cw2->clientSize) && (screen_info->params->frame_opacity < 100)) { XFixesSubtractRegion (display_info->dpy, region, region, cw2->clientSize); } else if (cw2->borderSize) { XFixesSubtractRegion (display_info->dpy, region, region, cw2->borderSize); } } } } static void repair_win (CWindow *cw, XRectangle *r) { DisplayInfo *display_info; ScreenInfo *screen_info; XserverRegion parts; g_return_if_fail (cw != NULL); TRACE ("entering"); screen_info = cw->screen_info; display_info = screen_info->display_info; if (!(cw->damage)) { /* Input Only have no damage */ return; } myDisplayErrorTrapPush (display_info); if (cw->damaged) { parts = XFixesCreateRegion (display_info->dpy, NULL, 0); /* Copy the damage region to parts, subtracting it from the window's damage */ XDamageSubtract (display_info->dpy, cw->damage, None, parts); XFixesTranslateRegion (display_info->dpy, parts, cw->attr.x + cw->attr.border_width, cw->attr.y + cw->attr.border_width); } else { parts = win_extents (cw); /* Subtract all damage from the window's damage */ XDamageSubtract (display_info->dpy, cw->damage, None, None); } myDisplayErrorTrapPopIgnored (display_info); if (parts) { fix_region (cw, parts); /* parts region will be destroyed by add_damage () */ add_damage (cw->screen_info, parts); cw->damaged = TRUE; } } static void damage_screen (ScreenInfo *screen_info) { XserverRegion region; region = get_screen_region (screen_info); /* region will be freed by add_damage () */ add_damage (screen_info, region); } static void damage_win (CWindow *cw) { XserverRegion extents; g_return_if_fail (cw != NULL); TRACE ("window 0x%lx", cw->id); extents = win_extents (cw); fix_region (cw, extents); /* extents region will be freed by add_damage () */ add_damage (cw->screen_info, extents); } static void update_extents (CWindow *cw) { DisplayInfo *display_info; ScreenInfo *screen_info; g_return_if_fail (cw != NULL); TRACE ("window 0x%lx", cw->id); screen_info = cw->screen_info; display_info = screen_info->display_info; if (WIN_IS_VISIBLE(cw)) { damage_win (cw); } if (cw->extents) { XFixesDestroyRegion (display_info->dpy, cw->extents); cw->extents = None; } } static void determine_mode (CWindow *cw) { DisplayInfo *display_info; ScreenInfo *screen_info; XRenderPictFormat *format; g_return_if_fail (cw != NULL); TRACE ("window 0x%lx", cw->id); screen_info = cw->screen_info; display_info = screen_info->display_info; format = NULL; if (cw->alphaPict) { XRenderFreePicture (display_info->dpy, cw->alphaPict); cw->alphaPict = None; } if (cw->shadowPict) { XRenderFreePicture (display_info->dpy, cw->shadowPict); cw->shadowPict = None; } if (cw->alphaBorderPict) { XRenderFreePicture (display_info->dpy, cw->alphaBorderPict); cw->alphaBorderPict = None; } format = XRenderFindVisualFormat (display_info->dpy, cw->attr.visual); cw->argb = ((format) && (format->type == PictTypeDirect) && (format->direct.alphaMask)); if (cw->extents) { XserverRegion damage; damage = XFixesCreateRegion (display_info->dpy, NULL, 0); XFixesCopyRegion (display_info->dpy, damage, cw->extents); fix_region (cw, damage); /* damage region will be destroyed by add_damage () */ add_damage (screen_info, damage); } } static void expose_area (ScreenInfo *screen_info, XRectangle *rects, gint nrects) { DisplayInfo *display_info; XserverRegion region; g_return_if_fail (rects != NULL); g_return_if_fail (nrects > 0); TRACE ("entering"); display_info = screen_info->display_info; region = XFixesCreateRegion (display_info->dpy, rects, nrects); /* region will be destroyed by add_damage () */ add_damage (screen_info, region); } static void set_win_opacity (CWindow *cw, guint32 opacity) { DisplayInfo *display_info; ScreenInfo *screen_info; g_return_if_fail (cw != NULL); TRACE ("window 0x%lx", cw->id); screen_info = cw->screen_info; display_info = screen_info->display_info; cw->opacity = opacity; determine_mode(cw); if (cw->shadow) { XRenderFreePicture (display_info->dpy, cw->shadow); cw->shadow = None; if (cw->extents) { XFixesDestroyRegion (display_info->dpy, cw->extents); } cw->extents = win_extents (cw); add_repair (screen_info); } } static void map_win (CWindow *cw) { ScreenInfo *screen_info; DisplayInfo *display_info; g_return_if_fail (cw != NULL); TRACE ("window 0x%lx", cw->id); screen_info = cw->screen_info; display_info = screen_info->display_info; if (!WIN_IS_REDIRECTED(cw)) { cw->fulloverlay = is_fullscreen (cw); if (cw->fulloverlay) { /* * To be safe, we only count the fullscreen un-redirected windows. * We do not want a smaller override redirect such as a tooltip * for example to prevent the overlay to be remapped and leave * a black screen until the tooltip is unmapped... */ screen_info->wins_unredirected++; TRACE ("mapping fullscreen window 0x%lx, wins_unredirected increased to %i", cw->id, screen_info->wins_unredirected); } TRACE ("mapping unredirected window 0x%lx, wins_unredirected is now %i", cw->id, screen_info->wins_unredirected); #if HAVE_OVERLAYS if ((screen_info->wins_unredirected == 1) && (display_info->have_overlays)) { TRACE ("unmapping overlay window"); XUnmapWindow (myScreenGetXDisplay (screen_info), screen_info->overlay); } #endif /* HAVE_OVERLAYS */ return; } cw->viewable = TRUE; cw->damaged = FALSE; /* Check for new windows to un-redirect. */ if (WIN_HAS_DAMAGE(cw) && WIN_IS_NATIVE_OPAQUE(cw) && WIN_IS_REDIRECTED(cw) && !WIN_IS_SHAPED(cw) && ((screen_info->wins_unredirected > 0) || is_fullscreen (cw))) { /* Make those opaque, we don't want them to be transparent */ cw->opacity = NET_WM_OPAQUE; /* For NET_WM_BYPASS_COMPOSITOR, 0 indicates no preference, 1 hints * the compositor to disabling compositing. */ if ((cw->bypass_compositor == 1) || (screen_info->params->unredirect_overlays && WIN_IS_OVERRIDE(cw) && cw->bypass_compositor == 0)) { TRACE ("unredirecting toplevel window 0x%lx", cw->id); unredirect_win (cw); } } } static void unmap_win (CWindow *cw) { ScreenInfo *screen_info; DisplayInfo *display_info; g_return_if_fail (cw != NULL); TRACE ("window 0x%lx", cw->id); screen_info = cw->screen_info; display_info = screen_info->display_info; if (!WIN_IS_REDIRECTED(cw) && (screen_info->wins_unredirected > 0)) { if (cw->fulloverlay) { screen_info->wins_unredirected--; TRACE ("unmapping fullscreen window 0x%lx, wins_unredirected decreased to %i", cw->id, screen_info->wins_unredirected); } TRACE ("unmapped window 0x%lx, wins_unredirected is now %i", cw->id, screen_info->wins_unredirected); if (!screen_info->wins_unredirected) { /* Restore the overlay if that was the last unredirected window */ #if HAVE_OVERLAYS if (display_info->have_overlays) { TRACE ("remapping overlay window"); XMapWindow (myScreenGetXDisplay (screen_info), screen_info->overlay); } #endif /* HAVE_OVERLAYS */ damage_screen (screen_info); } } else if (WIN_IS_VISIBLE(cw)) { damage_win (cw); } cw->viewable = FALSE; cw->damaged = FALSE; cw->redirected = TRUE; cw->fulloverlay = FALSE; free_win_data (cw, FALSE); } static void init_opacity (CWindow *cw) { ScreenInfo *screen_info; DisplayInfo *display_info; Client *c; g_return_if_fail (cw != NULL); TRACE ("window 0x%lx", cw->id); screen_info = cw->screen_info; display_info = screen_info->display_info; c = cw->c; cw->native_opacity = FALSE; if (c) { cw->opacity_locked = FLAG_TEST (c->xfwm_flags, XFWM_FLAG_OPACITY_LOCKED); cw->opacity = c->opacity_applied; cw->native_opacity = WIN_IS_OPAQUE(cw); } else if (getOpacity (display_info, cw->id, &cw->opacity)) { cw->native_opacity = WIN_IS_OPAQUE(cw); cw->opacity_locked = getOpacityLock (display_info, cw->id); } else { cw->opacity = (double) (screen_info->params->popup_opacity / 100.0) * NET_WM_OPAQUE; cw->native_opacity = TRUE; cw->opacity_locked = getOpacityLock (display_info, cw->id); } } static void update_opaque_region (CWindow *cw, Window id) { DisplayInfo *display_info; ScreenInfo *screen_info; XRectangle *rects = NULL; unsigned int nrects; XserverRegion old_opaque_region; g_return_if_fail (cw != NULL); TRACE ("window 0x%lx", cw->id); screen_info = cw->screen_info; display_info = screen_info->display_info; old_opaque_region = cw->opaque_region; nrects = getOpaqueRegionRects (display_info, id, &rects); if (nrects) { cw->opaque_region = XFixesCreateRegion (display_info->dpy, rects, nrects); g_free (rects); } else { cw->opaque_region = None; } if (old_opaque_region != None) { if (!WIN_IS_VISIBLE(cw) || !WIN_IS_REDIRECTED(cw)) { XFixesDestroyRegion (display_info->dpy, old_opaque_region); } else { if (cw->opaque_region) { XFixesSubtractRegion (display_info->dpy, old_opaque_region, old_opaque_region, cw->opaque_region); } translate_to_client_region (cw, old_opaque_region); /* old_opaque_region region will be destroyed by add_damage () */ add_damage (screen_info, old_opaque_region); } } } static void add_win (DisplayInfo *display_info, Window id, Client *c) { ScreenInfo *screen_info; CWindow *new; int result, status; TRACE ("window 0x%lx", id); if (is_output (display_info, id)) { TRACE ("not adding output window 0x%lx", id); return; } new = find_cwindow_in_display (display_info, id); if (new) { TRACE ("window 0x%lx already added", id); return; } new = g_slice_alloc0 (sizeof(CWindow)); myDisplayGrabServer (display_info); myDisplayErrorTrapPush (display_info); status = XGetWindowAttributes (display_info->dpy, id, &new->attr); result = myDisplayErrorTrapPop (display_info); if ((result != Success) || !status) { g_slice_free (CWindow, new); myDisplayUngrabServer (display_info); TRACE ("an error occurred getting window attributes, 0x%lx not added", id); return; } if (c) { screen_info = c->screen_info; } else { screen_info = myDisplayGetScreenFromRoot (display_info, new->attr.root); } if (!screen_info) { g_slice_free (CWindow, new); myDisplayUngrabServer (display_info); TRACE ("couldn't get screen from window, 0x%lx not added", id); return; } if (!screen_info->compositor_active) { g_slice_free (CWindow, new); myDisplayUngrabServer (display_info); TRACE ("compositor not active on screen %i, 0x%lx not added", screen_info->screen, id); return; } myDisplayErrorTrapPush (display_info); if (c == NULL) { /* We must be notified of property changes for transparency, even if the win is not managed */ XSelectInput (display_info->dpy, id, new->attr.your_event_mask | PropertyChangeMask | StructureNotifyMask); } /* Listen for XShape events if applicable */ if (display_info->have_shape) { XShapeSelectInput (display_info->dpy, id, ShapeNotifyMask); } myDisplayErrorTrapPopIgnored (display_info); new->c = c; new->screen_info = screen_info; new->id = id; new->damaged = FALSE; new->redirected = TRUE; new->fulloverlay = FALSE; new->shaped = is_shaped (display_info, id); new->viewable = (new->attr.map_state == IsViewable); if (new->attr.class != InputOnly) { myDisplayErrorTrapPush (display_info); new->damage = XDamageCreate (display_info->dpy, id, XDamageReportNonEmpty); if (myDisplayErrorTrapPop (display_info) != Success) { new->damage = None; } } else { new->damage = None; } #if HAVE_NAME_WINDOW_PIXMAP new->name_window_pixmap = None; #endif new->picture = None; new->saved_picture = None; new->alphaPict = None; new->alphaBorderPict = None; new->shadowPict = None; new->borderSize = None; new->clientSize = None; new->extents = None; new->shadow = None; new->shadow_dx = 0; new->shadow_dy = 0; new->shadow_width = 0; new->shadow_height = 0; new->borderClip = None; if (c) { update_opaque_region (new, c->window); } else { update_opaque_region (new, id); } getBypassCompositor (display_info, id, &new->bypass_compositor); init_opacity (new); determine_mode (new); /* Insert window at top of stack */ screen_info->cwindows = g_list_prepend (screen_info->cwindows, new); g_hash_table_insert(screen_info->cwindow_hash, (gpointer) new->id, new); if (WIN_IS_VISIBLE(new)) { map_win (new); } TRACE ("window 0x%lx added", id); myDisplayUngrabServer (display_info); } static void restack_win (CWindow *cw, Window above) { ScreenInfo *screen_info; Window previous_above; GList *sibling; GList *next; g_return_if_fail (cw != NULL); TRACE ("window 0x%lx above 0x%lx", cw->id, above); screen_info = cw->screen_info; sibling = g_list_find (screen_info->cwindows, (gconstpointer) cw); next = g_list_next (sibling); previous_above = None; if (next) { CWindow *ncw = (CWindow *) next; 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 */ screen_info->cwindows = g_list_delete_link (screen_info->cwindows, sibling); screen_info->cwindows = g_list_append (screen_info->cwindows, cw); } else if (previous_above != above) { GList *list; for (list = screen_info->cwindows; list; list = g_list_next (list)) { CWindow *cw2 = (CWindow *) list->data; if (cw2->id == above) { break; } } if (list != NULL) { screen_info->cwindows = g_list_delete_link (screen_info->cwindows, sibling); screen_info->cwindows = g_list_insert_before (screen_info->cwindows, list, cw); } } } static void resize_win (CWindow *cw, gint x, gint y, gint width, gint height, gint bw) { DisplayInfo *display_info; ScreenInfo *screen_info; XserverRegion damage; g_return_if_fail (cw != NULL); TRACE ("window 0x%lx, (%i,%i) %ix%i", cw->id, x, y, width, height); screen_info = cw->screen_info; display_info = screen_info->display_info; damage = None; myDisplayErrorTrapPush (display_info); if (WIN_IS_VISIBLE(cw)) { damage = XFixesCreateRegion (display_info->dpy, NULL, 0); if (cw->extents) { XFixesCopyRegion (display_info->dpy, damage, cw->extents); } } if (cw->extents) { XFixesDestroyRegion (display_info->dpy, cw->extents); cw->extents = None; } if ((cw->attr.width != width) || (cw->attr.height != height)) { #if HAVE_NAME_WINDOW_PIXMAP if (cw->name_window_pixmap) { XFreePixmap (display_info->dpy, cw->name_window_pixmap); cw->name_window_pixmap = None; } #endif if (cw->picture) { XRenderFreePicture (display_info->dpy, cw->picture); cw->picture = None; } if (cw->saved_picture) { XRenderFreePicture (display_info->dpy, cw->saved_picture); cw->saved_picture = None; } if (cw->shadow) { XRenderFreePicture (display_info->dpy, cw->shadow); cw->shadow = None; } } if ((cw->attr.width != width) || (cw->attr.height != height) || (cw->attr.x != x) || (cw->attr.y != y)) { if (cw->borderSize) { XFixesDestroyRegion (display_info->dpy, cw->borderSize); cw->borderSize = None; } if (cw->clientSize) { XFixesDestroyRegion (display_info->dpy, cw->clientSize); cw->clientSize = None; } } cw->attr.x = x; cw->attr.y = y; cw->attr.width = width; cw->attr.height = height; cw->attr.border_width = bw; if (damage) { cw->extents = win_extents (cw); XFixesUnionRegion (display_info->dpy, damage, damage, cw->extents); fix_region (cw, damage); /* damage region will be destroyed by add_damage () */ add_damage (screen_info, damage); } myDisplayErrorTrapPopIgnored (display_info); } static void reshape_win (CWindow *cw) { DisplayInfo *display_info; ScreenInfo *screen_info; XserverRegion damage; g_return_if_fail (cw != NULL); TRACE ("window 0x%lx", cw->id); screen_info = cw->screen_info; display_info = screen_info->display_info; damage = None; myDisplayErrorTrapPush (display_info); if (WIN_IS_VISIBLE(cw)) { damage = XFixesCreateRegion (display_info->dpy, NULL, 0); if (cw->extents) { XFixesCopyRegion (display_info->dpy, damage, cw->extents); } } if (cw->extents) { XFixesDestroyRegion (display_info->dpy, cw->extents); cw->extents = None; } if (cw->shadow) { XRenderFreePicture (display_info->dpy, cw->shadow); cw->shadow = None; } if (cw->borderSize) { XFixesDestroyRegion (display_info->dpy, cw->borderSize); cw->borderSize = None; } if (cw->clientSize) { XFixesDestroyRegion (display_info->dpy, cw->clientSize); cw->clientSize = None; } if (damage) { cw->extents = win_extents (cw); XFixesUnionRegion (display_info->dpy, damage, damage, cw->extents); /* A shape notify will likely change the shadows too, so clear the extents */ XFixesDestroyRegion (display_info->dpy, cw->extents); cw->extents = None; fix_region (cw, damage); /* damage region will be destroyed by add_damage () */ add_damage (screen_info, damage); } myDisplayErrorTrapPopIgnored (display_info); } static void destroy_win (DisplayInfo *display_info, Window id) { CWindow *cw; g_return_if_fail (display_info != NULL); g_return_if_fail (id != None); TRACE ("window 0x%lx", id); cw = find_cwindow_in_display (display_info, id); if (is_on_compositor (cw)) { ScreenInfo *screen_info; if (WIN_IS_VIEWABLE (cw)) { unmap_win (cw); } screen_info = cw->screen_info; g_hash_table_remove(screen_info->cwindow_hash, (gpointer) cw->id); screen_info->cwindows = g_list_remove (screen_info->cwindows, (gconstpointer) cw); free_win_data (cw, TRUE); } } static void update_cursor (ScreenInfo *screen_info) { XFixesCursorImage *cursor; g_return_if_fail (screen_info != NULL); TRACE ("entering"); cursor = XFixesGetCursorImage (screen_info->display_info->dpy); if (cursor == NULL) { g_warning ("Failed to retrieve cursor image!"); return; } if (screen_info->cursorSerial != cursor->cursor_serial) { if (screen_info->zoomed) { expose_area (screen_info, &screen_info->cursorLocation, 1); } if (screen_info->cursorPicture) { XRenderFreePicture (screen_info->display_info->dpy, screen_info->cursorPicture); } screen_info->cursorPicture = cursor_to_picture (screen_info, cursor); screen_info->cursorSerial = cursor->cursor_serial; screen_info->cursorOffsetX = cursor->xhot; screen_info->cursorOffsetY = cursor->yhot; screen_info->cursorLocation.x = cursor->x - cursor->xhot; screen_info->cursorLocation.y = cursor->y - cursor->yhot; screen_info->cursorLocation.width = cursor->width; screen_info->cursorLocation.height = cursor->height; if (screen_info->zoomed) { expose_area (screen_info, &screen_info->cursorLocation, 1); } } XFree (cursor); } static void recenter_zoomed_area (ScreenInfo *screen_info, int x_root, int y_root) { int zf = screen_info->transform.matrix[0][0]; double zoom = XFixedToDouble (zf); if (screen_info->zoomed) { int xp = x_root * (1 - zoom); int yp = y_root * (1 - zoom); screen_info->transform.matrix[0][2] = (xp << 16); screen_info->transform.matrix[1][2] = (yp << 16); } if (zf > (1 << 14) && zf < (1 << 16)) { #ifdef HAVE_EPOXY if (screen_info->use_glx) { screen_info->texture_filter = GL_LINEAR; } #endif /* HAVE_EPOXY */ if (screen_info->zoomBuffer) { XRenderSetPictureFilter (myScreenGetXDisplay (screen_info), screen_info->zoomBuffer, FilterBilinear, NULL, 0); } } else { #ifdef HAVE_EPOXY if (screen_info->use_glx) { screen_info->texture_filter = GL_NEAREST; } #endif /* HAVE_EPOXY */ if (screen_info->zoomBuffer) { XRenderSetPictureFilter (myScreenGetXDisplay (screen_info), screen_info->zoomBuffer, FilterNearest, NULL, 0); } } /* zoomBuffer might be None if we're using GLX */ if (screen_info->zoomBuffer) { XRenderSetPictureTransform (myScreenGetXDisplay (screen_info), screen_info->zoomBuffer, &screen_info->transform); } damage_screen (screen_info); } static gboolean zoom_timeout_cb (gpointer data) { ScreenInfo *screen_info; int x_root, y_root; screen_info = (ScreenInfo *) data; if (!screen_info->zoomed) { screen_info->zoom_timeout_id = 0; if (screen_info->zoomBuffer) { XRenderFreePicture (myScreenGetXDisplay (screen_info), screen_info->zoomBuffer); screen_info->zoomBuffer = None; } return FALSE; /* stop calling this callback */ } getMouseXY (screen_info, &x_root, &y_root); if (screen_info->cursorLocation.x + screen_info->cursorOffsetX != x_root || screen_info->cursorLocation.y + screen_info->cursorOffsetY != y_root) { screen_info->cursorLocation.x = x_root - screen_info->cursorOffsetX; screen_info->cursorLocation.y = y_root - screen_info->cursorOffsetY; recenter_zoomed_area (screen_info, x_root, y_root); } return TRUE; } static void compositorHandleDamage (DisplayInfo *display_info, XDamageNotifyEvent *ev) { ScreenInfo *screen_info; CWindow *cw; g_return_if_fail (display_info != NULL); g_return_if_fail (ev != NULL); TRACE ("drawable 0x%lx", ev->drawable); /* ev->drawable is the window ID of the damaged window ev->geometry is the geometry of the damaged window ev->area is the bounding rect for the damaged area ev->damage is the damage handle returned by XDamageCreate() */ cw = find_cwindow_in_display (display_info, ev->drawable); if (is_on_compositor (cw) && WIN_IS_REDIRECTED(cw)) { screen_info = cw->screen_info; repair_win (cw, &ev->area); screen_info->damages_pending = ev->more; } } static void compositorHandlePropertyNotify (DisplayInfo *display_info, XPropertyEvent *ev) { #if MONITOR_ROOT_PIXMAP gint p; Atom backgroundProps[2]; #endif g_return_if_fail (display_info != NULL); g_return_if_fail (ev != NULL); TRACE ("window 0x%lx", ev->window); #if MONITOR_ROOT_PIXMAP backgroundProps[0] = display_info->atoms[XROOTPMAP]; backgroundProps[1] = display_info->atoms[XSETROOT]; for (p = 0; p < 2; p++) { if (ev->atom == backgroundProps[p] && ev->state == PropertyNewValue) { ScreenInfo *screen_info = myDisplayGetScreenFromRoot (display_info, ev->window); if ((screen_info) && (screen_info->compositor_active) && (screen_info->rootTile)) { myDisplayErrorTrapPush (display_info); XClearArea (display_info->dpy, screen_info->output, 0, 0, 0, 0, TRUE); XRenderFreePicture (display_info->dpy, screen_info->rootTile); myDisplayErrorTrapPopIgnored (display_info); screen_info->rootTile = None; damage_screen (screen_info); return; } } } #endif /* check if Trans property was changed */ if (ev->atom == display_info->atoms[NET_WM_WINDOW_OPACITY]) { CWindow *cw = find_cwindow_in_display (display_info, ev->window); TRACE ("window 0x%lx", ev->window); if (is_on_compositor (cw)) { Client *c = cw->c; TRACE ("NET_WM_WINDOW_OPACITY changed for 0x%lx", cw->id); if (!getOpacity (display_info, cw->id, &cw->opacity)) { /* The property was removed */ cw->opacity = NET_WM_OPAQUE; } set_win_opacity (cw, cw->opacity); cw->native_opacity = WIN_IS_OPAQUE(cw); /* Transset changes the property on the frame, not the client window. We need to check and update the client "opacity" value accordingly. */ if (c) { if (c->opacity != cw->opacity) { clientSetOpacity (c, cw->opacity, 0, 0); } } } } else if (ev->atom == display_info->atoms[NET_WM_WINDOW_OPACITY_LOCKED]) { CWindow *cw = find_cwindow_in_display (display_info, ev->window); TRACE ("NET_WM_WINDOW_OPACITY_LOCKED changed for id 0x%lx", ev->window); if (is_on_compositor (cw)) { cw->opacity_locked = getOpacityLock (display_info, cw->id); if (cw->c) { if (cw->opacity_locked) { FLAG_SET (cw->c->xfwm_flags, XFWM_FLAG_OPACITY_LOCKED); } else { FLAG_UNSET (cw->c->xfwm_flags, XFWM_FLAG_OPACITY_LOCKED); } } } } else if (ev->atom == display_info->atoms[NET_WM_BYPASS_COMPOSITOR]) { CWindow *cw = find_cwindow_in_display (display_info, ev->window); TRACE ("NET_WM_BYPASS_COMPOSITOR changed for id 0x%lx", ev->window); if (is_on_compositor (cw)) { getBypassCompositor (display_info, cw->id, &cw->bypass_compositor); } } else if (ev->atom == display_info->atoms[NET_WM_OPAQUE_REGION]) { Client* c; CWindow *cw; c = myDisplayGetClientFromWindow (display_info, ev->window, SEARCH_WINDOW); if (c) { cw = find_cwindow_in_display (display_info, c->frame); } else { cw = find_cwindow_in_display (display_info, ev->window); } if (is_on_compositor (cw)) { update_opaque_region (cw, ev->window); } } else { TRACE ("no compositor property changed for id 0x%lx", ev->window); } } static void compositorHandleExpose (DisplayInfo *display_info, XExposeEvent *ev) { ScreenInfo *screen_info; XRectangle rect[1]; CWindow *cw; g_return_if_fail (display_info); TRACE ("window 0x%lx", ev->window); cw = find_cwindow_in_display (display_info, ev->window); if (cw != NULL) { screen_info = cw->screen_info; } else { /* Get the screen structure from the root of the event */ screen_info = myDisplayGetScreenFromRoot (display_info, ev->window); } if (!screen_info || !compositorIsActive (screen_info)) { return; } rect[0].x = ev->x; rect[0].y = ev->y; rect[0].width = ev->width; rect[0].height = ev->height; expose_area (screen_info, rect, 1); } static void compositorHandleConfigureNotify (DisplayInfo *display_info, XConfigureEvent *ev) { CWindow *cw; g_return_if_fail (display_info != NULL); g_return_if_fail (ev != NULL); TRACE ("window 0x%lx", ev->window); cw = find_cwindow_in_display (display_info, ev->window); if (is_on_compositor (cw)) { restack_win (cw, ev->above); resize_win (cw, ev->x, ev->y, ev->width, ev->height, ev->border_width); } } static void compositorHandleCirculateNotify (DisplayInfo *display_info, XCirculateEvent *ev) { CWindow *cw; CWindow *top; GList *first; Window above; g_return_if_fail (display_info != NULL); g_return_if_fail (ev != NULL); TRACE ("window 0x%lx", ev->window); cw = find_cwindow_in_display (display_info, ev->window); if (!(is_on_compositor (cw))) { return; } first = cw->screen_info->cwindows; top = (CWindow *) first; if ((ev->place == PlaceOnTop) && (top)) { above = top->id; } else { above = None; } restack_win (cw, above); } static void compositorHandleCreateNotify (DisplayInfo *display_info, XCreateWindowEvent *ev) { g_return_if_fail (display_info != NULL); g_return_if_fail (ev != NULL); TRACE ("window 0x%lx", ev->window); /* We are only interested in top level windows, other will be caught by the WM. */ if (myDisplayGetScreenFromRoot (display_info, ev->parent) != NULL) { if (!find_cwindow_in_display (display_info, ev->window)) { add_win (display_info, ev->window, NULL); } } } static void compositorHandleReparentNotify (DisplayInfo *display_info, XReparentEvent *ev) { g_return_if_fail (display_info != NULL); g_return_if_fail (ev != NULL); TRACE ("window 0x%lx", ev->window); if (myDisplayGetScreenFromRoot (display_info, ev->parent) != NULL) { add_win (display_info, ev->window, NULL); } else { destroy_win (display_info, ev->window); } } static void compositorHandleDestroyNotify (DisplayInfo *display_info, XDestroyWindowEvent *ev) { g_return_if_fail (display_info != NULL); g_return_if_fail (ev != NULL); TRACE ("window 0x%lx", ev->window); destroy_win (display_info, ev->window); } static void compositorHandleMapNotify (DisplayInfo *display_info, XMapEvent *ev) { CWindow *cw; g_return_if_fail (display_info != NULL); g_return_if_fail (ev != NULL); TRACE ("window 0x%lx", ev->window); cw = find_cwindow_in_display (display_info, ev->window); if (is_on_compositor (cw)) { map_win (cw); } } static void compositorHandleUnmapNotify (DisplayInfo *display_info, XUnmapEvent *ev) { CWindow *cw; g_return_if_fail (display_info != NULL); g_return_if_fail (ev != NULL); TRACE ("window 0x%lx", ev->window); if (ev->from_configure) { TRACE ("ignoring UnmapNotify caused by parent's resize"); return; } cw = find_cwindow_in_display (display_info, ev->window); if (is_on_compositor (cw)) { if (WIN_IS_VIEWABLE (cw)) { unmap_win (cw); } } } static void compositorHandleShapeNotify (DisplayInfo *display_info, XShapeEvent *ev) { CWindow *cw; g_return_if_fail (display_info != NULL); g_return_if_fail (ev != NULL); TRACE ("window 0x%lx", ev->window); cw = find_cwindow_in_display (display_info, ev->window); if (is_on_compositor (cw)) { if (ev->kind == ShapeBounding) { if (!(ev->shaped) && (cw->shaped)) { cw->shaped = FALSE; } reshape_win (cw); if ((ev->shaped) && !(cw->shaped)) { cw->shaped = TRUE; } } } } static void compositorHandleCursorNotify (DisplayInfo *display_info, XFixesCursorNotifyEvent *ev) { ScreenInfo *screen_info; g_return_if_fail (display_info != NULL); g_return_if_fail (ev != NULL); TRACE ("window 0x%lx", ev->window); screen_info = myDisplayGetScreenFromRoot (display_info, ev->window); if (screen_info && screen_info->cursor_is_zoomed) { update_cursor (screen_info); } } static gboolean compositorCheckCMSelection (ScreenInfo *screen_info) { DisplayInfo *display_info; gchar selection[32]; Atom a; display_info = screen_info->display_info; /* Newer EWMH standard property "_NET_WM_CM_S" */ g_snprintf (selection, sizeof (selection), "_NET_WM_CM_S%d", screen_info->screen); a = XInternAtom (display_info->dpy, selection, FALSE); if (XGetSelectionOwner (display_info->dpy, a) != None) { return TRUE; } /* Older property "COMPOSITING_MANAGER" */ if (XGetSelectionOwner (display_info->dpy, display_info->atoms[COMPOSITING_MANAGER]) != None) { return TRUE; } return FALSE; } #ifdef HAVE_PRESENT_EXTENSION static void compositorHandlePresentCompleteNotify (DisplayInfo *display_info, XPresentCompleteNotifyEvent *ev) { ScreenInfo *screen_info; GSList *list; g_return_if_fail (display_info != NULL); g_return_if_fail (ev != NULL); TRACE ("window 0x%lx", ev->window); for (list = display_info->screens; list; list = g_slist_next (list)) { screen_info = (ScreenInfo *) list->data; if (screen_info->output == ev->window) { DBG ("present completed, present pending cleared"); screen_info->present_pending = FALSE; break; } } } static void compositorHandleGenericEvent(DisplayInfo *display_info, XGenericEvent *ev) { XGenericEventCookie *ev_cookie = (XGenericEventCookie *) ev; g_return_if_fail (display_info != NULL); g_return_if_fail (ev != NULL); TRACE ("entering"); if (ev_cookie->extension == display_info->present_opcode) { XGetEventData (display_info->dpy, ev_cookie); if (ev_cookie->evtype == PresentCompleteNotify) { compositorHandlePresentCompleteNotify (display_info, (XPresentCompleteNotifyEvent *) ev_cookie->data); } XFreeEventData (display_info->dpy, ev_cookie); } } #endif /* HAVE_PRESENT_EXTENSION */ static void compositorSetCMSelection (ScreenInfo *screen_info, Window w) { DisplayInfo *display_info; gchar selection[32]; Atom a; g_return_if_fail (screen_info != NULL); display_info = screen_info->display_info; /* Newer EWMH standard property "_NET_WM_CM_S" */ g_snprintf (selection, sizeof (selection), "_NET_WM_CM_S%d", screen_info->screen); a = XInternAtom (display_info->dpy, selection, FALSE); setXAtomManagerOwner (display_info, a, screen_info->xroot, w); } static Pixmap compositorScaleWindowPixmap (CWindow *cw, guint *width, guint *height) { Display *dpy; ScreenInfo *screen_info; DisplayInfo *display_info; Picture srcPicture, tmpPicture, destPicture; Pixmap tmpPixmap, dstPixmap; XTransform transform; XRenderPictFormat *render_format; double scale; int tx, ty; int src_x, src_y; int src_size, dest_size; unsigned int src_w, src_h; unsigned int dst_w, dst_h; XRenderColor c = { 0x7fff, 0x7fff, 0x7fff, 0xffff }; screen_info = cw->screen_info; display_info = screen_info->display_info; dpy = myScreenGetXDisplay (screen_info); srcPicture = cw->picture; if (!srcPicture) { srcPicture = cw->saved_picture; } /* Could not get a usable picture, bail out */ if (!srcPicture) { return None; } /* Get the source pixmap size to compute the scale */ get_paint_bounds (cw, &tx, &ty, &src_w, &src_h); if (WIN_HAS_CLIENT(cw)) { src_x = ABS(frameExtentLeft (cw->c)); src_y = ABS(frameExtentTop (cw->c)); src_w = src_w - src_x - ABS(frameExtentRight (cw->c)); src_h = src_h - src_y - ABS(frameExtentBottom (cw->c)); } else { src_x = 0; src_y = 0; } src_size = MAX (src_w, src_h); /* Shaped windows have no height */ if (src_w == 0 || src_h == 0) { return None; } /*/ * Caller may pass either NULL or 0. * If 0, we return the actual unscalled size. */ dst_w = (width != NULL && *width > 0) ? *width : src_w; dst_h = (height != NULL && *height > 0) ? *height : src_h; dest_size = MIN (dst_w, dst_h); scale = (double) dest_size / (double) src_size; dst_w = src_w * scale; dst_h = src_h * scale; transform.matrix[0][0] = XDoubleToFixed (1.0); transform.matrix[0][1] = XDoubleToFixed (0.0); transform.matrix[0][2] = XDoubleToFixed (0.0); transform.matrix[1][0] = XDoubleToFixed (0.0); transform.matrix[1][1] = XDoubleToFixed (1.0); transform.matrix[1][2] = XDoubleToFixed (0.0); transform.matrix[2][0] = XDoubleToFixed (0.0); transform.matrix[2][1] = XDoubleToFixed (0.0); transform.matrix[2][2] = XDoubleToFixed (scale); tmpPixmap = XCreatePixmap (dpy, screen_info->output, src_w, src_h, 32); if (!tmpPixmap) { return None; } dstPixmap = XCreatePixmap (dpy, screen_info->output, dst_w, dst_h, 32); if (!dstPixmap) { XFreePixmap (dpy, tmpPixmap); return None; } render_format = get_window_format (cw); if (!render_format) { XFreePixmap (dpy, dstPixmap); XFreePixmap (dpy, tmpPixmap); return None; } myDisplayErrorTrapPush (display_info); render_format = XRenderFindStandardFormat (dpy, PictStandardARGB32); tmpPicture = XRenderCreatePicture (dpy, tmpPixmap, render_format, 0, NULL); XRenderFillRectangle (dpy, PictOpSrc, tmpPicture, &c, 0, 0, src_w, src_h); XFixesSetPictureClipRegion (dpy, tmpPicture, 0, 0, None); XRenderComposite (dpy, PictOpOver, srcPicture, None, tmpPicture, src_x, src_y, 0, 0, 0, 0, src_w, src_h); XRenderSetPictureFilter (dpy, tmpPicture, FilterBest, NULL, 0); XRenderSetPictureTransform (dpy, tmpPicture, &transform); destPicture = XRenderCreatePicture (dpy, dstPixmap, render_format, 0, NULL); XRenderComposite (dpy, PictOpOver, tmpPicture, None, destPicture, 0, 0, 0, 0, 0, 0, dst_w, dst_h); XRenderFreePicture (dpy, tmpPicture); XRenderFreePicture (dpy, destPicture); XFreePixmap (dpy, tmpPixmap); /* Update given size if requested */ if (width != NULL) { *width = dst_w; } if (height != NULL) { *height = dst_h; } myDisplayErrorTrapPopIgnored (display_info); return dstPixmap; } #endif /* HAVE_COMPOSITOR */ gboolean compositorIsUsable (DisplayInfo *display_info) { #ifdef HAVE_COMPOSITOR return display_info->enable_compositor; #endif /* HAVE_COMPOSITOR */ return FALSE; } gboolean compositorIsActive (ScreenInfo *screen_info) { #ifdef HAVE_COMPOSITOR g_return_val_if_fail (screen_info != NULL, FALSE); return screen_info->compositor_active; #endif /* HAVE_COMPOSITOR */ return FALSE; } void compositorAddWindow (DisplayInfo *display_info, Window id, Client *c) { #ifdef HAVE_COMPOSITOR g_return_if_fail (display_info != NULL); g_return_if_fail (id != None); TRACE ("window 0x%lx", id); if (!compositorIsUsable (display_info)) { return; } if (!compositorSetClient (display_info, id, c)) { add_win (display_info, id, c); } #endif /* HAVE_COMPOSITOR */ } gboolean compositorSetClient (DisplayInfo *display_info, Window id, Client *c) { #ifdef HAVE_COMPOSITOR CWindow *cw; g_return_val_if_fail (display_info != NULL, FALSE); g_return_val_if_fail (id != None, FALSE); TRACE ("window 0x%lx", id); if (!compositorIsUsable (display_info)) { return FALSE; } cw = find_cwindow_in_display (display_info, id); if (is_on_compositor (cw)) { if (cw->c != c) { update_extents (cw); cw->c = c; } return TRUE; } #endif /* HAVE_COMPOSITOR */ return FALSE; } void compositorRemoveWindow (DisplayInfo *display_info, Window id) { #ifdef HAVE_COMPOSITOR g_return_if_fail (display_info != NULL); g_return_if_fail (id != None); TRACE ("window 0x%lx", id); if (!compositorIsUsable (display_info)) { return; } destroy_win (display_info, id); #endif /* HAVE_COMPOSITOR */ } void compositorDamageWindow (DisplayInfo *display_info, Window id) { #ifdef HAVE_COMPOSITOR CWindow *cw; g_return_if_fail (display_info != NULL); g_return_if_fail (id != None); TRACE ("window 0x%lx", id); if (!compositorIsUsable (display_info)) { return; } cw = find_cwindow_in_display (display_info, id); if (is_on_compositor (cw)) { /* that will also damage the window */ update_extents (cw); } #endif /* HAVE_COMPOSITOR */ } void compositorResizeWindow (DisplayInfo *display_info, Window id, int x, int y, int width, int height) { #ifdef HAVE_COMPOSITOR CWindow *cw; g_return_if_fail (display_info != NULL); g_return_if_fail (id != None); TRACE ("window 0x%lx", id); if (!compositorIsUsable (display_info)) { return; } cw = find_cwindow_in_display (display_info, id); if (is_on_compositor (cw)) { resize_win (cw, x, y, width, height, 0); } #endif /* HAVE_COMPOSITOR */ } /* May return None if: * - The xserver does not support name window pixmaps * - The compositor is disabled at run time * - The compositor is disabled at build time * The pixmap may be an older copy of the last know good pixmap * if the window is unmapped. */ Pixmap compositorGetWindowPixmapAtSize (ScreenInfo *screen_info, Window id, guint *width, guint *height) { #ifdef HAVE_NAME_WINDOW_PIXMAP #ifdef HAVE_COMPOSITOR CWindow *cw; TRACE ("window 0x%lx", id); if (!screen_info->compositor_active) { return None; } g_return_val_if_fail (id != None, None); if (!compositorIsActive (screen_info)) { return None; } cw = find_cwindow_in_screen (screen_info, id); if (is_on_compositor (cw)) { return compositorScaleWindowPixmap (cw, width, height); } #endif /* HAVE_COMPOSITOR */ #endif /* HAVE_NAME_WINDOW_PIXMAP */ return None; } void compositorHandleEvent (DisplayInfo *display_info, XEvent *ev) { #ifdef HAVE_COMPOSITOR g_return_if_fail (display_info != NULL); g_return_if_fail (ev != NULL); TRACE ("event type %i", ev->type); if (!compositorIsUsable (display_info)) { return; } else if (ev->type == CreateNotify) { compositorHandleCreateNotify (display_info, (XCreateWindowEvent *) ev); } else if (ev->type == DestroyNotify) { compositorHandleDestroyNotify (display_info, (XDestroyWindowEvent *) ev); } else if (ev->type == ConfigureNotify) { compositorHandleConfigureNotify (display_info, (XConfigureEvent *) ev); } else if (ev->type == ReparentNotify) { compositorHandleReparentNotify (display_info, (XReparentEvent *) ev); } else if (ev->type == Expose) { compositorHandleExpose (display_info, (XExposeEvent *) ev); } else if (ev->type == CirculateNotify) { compositorHandleCirculateNotify (display_info, (XCirculateEvent *) ev); } else if (ev->type == PropertyNotify) { compositorHandlePropertyNotify (display_info, (XPropertyEvent *) ev); } else if (ev->type == MapNotify) { compositorHandleMapNotify (display_info, (XMapEvent *) ev); } else if (ev->type == UnmapNotify) { compositorHandleUnmapNotify (display_info, (XUnmapEvent *) ev); } else if (ev->type == (display_info->damage_event_base + XDamageNotify)) { compositorHandleDamage (display_info, (XDamageNotifyEvent *) ev); } else if (ev->type == (display_info->shape_event_base + ShapeNotify)) { compositorHandleShapeNotify (display_info, (XShapeEvent *) ev); } else if (ev->type == (display_info->fixes_event_base + XFixesCursorNotify)) { compositorHandleCursorNotify (display_info, (XFixesCursorNotifyEvent *) ev); } #ifdef HAVE_PRESENT_EXTENSION else if (ev->type == GenericEvent) { compositorHandleGenericEvent (display_info, (XGenericEvent *) ev); } #endif /* HAVE_PRESENT_EXTENSION */ #endif /* HAVE_COMPOSITOR */ } void compositorZoomIn (ScreenInfo *screen_info, XfwmEventButton *event) { TRACE ("entering"); #ifdef HAVE_COMPOSITOR if (!screen_info->compositor_active) { return; } screen_info->transform.matrix[0][0] -= 4096; screen_info->transform.matrix[1][1] -= 4096; if (screen_info->transform.matrix[0][0] < (1 << 10)) { screen_info->transform.matrix[0][0] = (1 << 10); screen_info->transform.matrix[1][1] = (1 << 10); } if (!screen_info->zoomed) { screen_info->cursor_is_zoomed = screen_info->params->zoom_pointer; if (screen_info->cursor_is_zoomed) { XFixesHideCursor (screen_info->display_info->dpy, screen_info->xroot); screen_info->cursorLocation.x = event->x_root - screen_info->cursorOffsetX; screen_info->cursorLocation.y = event->y_root - screen_info->cursorOffsetY; update_cursor (screen_info); } } screen_info->zoomed = TRUE; if (!screen_info->zoom_timeout_id) { gint timeout_rate; timeout_rate = xfwm_get_primary_refresh_rate (screen_info->gscr); screen_info->zoom_timeout_id = g_timeout_add ((1000 / timeout_rate /* per second */), zoom_timeout_cb, screen_info); } recenter_zoomed_area (screen_info, event->x_root, event->y_root); #endif /* HAVE_COMPOSITOR */ } void compositorZoomOut (ScreenInfo *screen_info, XfwmEventButton *event) { TRACE ("entering"); #ifdef HAVE_COMPOSITOR if (!screen_info->compositor_active) { return; } /* don't do anything if the user disabled the zoom feature */ if (screen_info->zoomed) { screen_info->transform.matrix[0][0] += 4096; screen_info->transform.matrix[1][1] += 4096; if (screen_info->transform.matrix[0][0] >= (1 << 16)) { screen_info->transform.matrix[0][0] = (1 << 16); screen_info->transform.matrix[1][1] = (1 << 16); screen_info->transform.matrix[0][2] = 0; screen_info->transform.matrix[1][2] = 0; screen_info->zoomed = FALSE; if (screen_info->cursor_is_zoomed) { XFixesShowCursor (screen_info->display_info->dpy, screen_info->xroot); } } recenter_zoomed_area (screen_info, event->x_root, event->y_root); } #endif /* HAVE_COMPOSITOR */ } void compositorInitDisplay (DisplayInfo *display_info) { #ifdef HAVE_COMPOSITOR int composite_major, composite_minor; composite_major = 0; composite_minor = 0; if (!XCompositeQueryExtension (display_info->dpy, &display_info->composite_event_base, &display_info->composite_error_base)) { g_warning ("The display does not support the XComposite extension."); display_info->have_composite = FALSE; display_info->composite_event_base = 0; display_info->composite_error_base = 0; } else { display_info->have_composite = TRUE; XCompositeQueryVersion (display_info->dpy, &composite_major, &composite_minor); DBG ("composite event base: %i", display_info->composite_event_base); DBG ("composite error base: %i", display_info->composite_error_base); DBG ("composite version: %i.%i", composite_major, composite_minor); } if (!XDamageQueryExtension (display_info->dpy, &display_info->damage_event_base, &display_info->damage_error_base)) { g_warning ("The display does not support the XDamage extension."); display_info->have_damage = FALSE; display_info->damage_event_base = 0; display_info->damage_error_base = 0; } else { display_info->have_damage = TRUE; DBG ("damage event base: %i", display_info->damage_event_base); DBG ("damage error base: %i", display_info->damage_error_base); } if (!XFixesQueryExtension (display_info->dpy, &display_info->fixes_event_base, &display_info->fixes_error_base)) { g_warning ("The display does not support the XFixes extension."); display_info->have_fixes = FALSE; display_info->fixes_event_base = 0; display_info->fixes_error_base = 0; } else { display_info->have_fixes = TRUE; DBG ("fixes event base: %i", display_info->fixes_event_base); DBG ("fixes error base: %i", display_info->fixes_error_base); } #ifdef HAVE_PRESENT_EXTENSION if (!XPresentQueryExtension (display_info->dpy, &display_info->present_opcode, &display_info->present_event_base, &display_info->present_error_base)) { g_warning ("The display does not support the XPresent extension."); display_info->have_present = FALSE; display_info->present_opcode = 0; display_info->present_event_base = 0; display_info->present_error_base = 0; } else { display_info->have_present = TRUE; DBG ("present opcode: %i", display_info->present_opcode); DBG ("present event base: %i", display_info->present_event_base); DBG ("present error base: %i", display_info->present_error_base); } #endif /* HAVE_PRESENT_EXTENSION */ display_info->enable_compositor = ((display_info->have_render) && (display_info->have_composite) && (display_info->have_damage) && (display_info->have_fixes)); if (!display_info->enable_compositor) { g_warning ("Compositing manager disabled."); } #if HAVE_NAME_WINDOW_PIXMAP display_info->have_name_window_pixmap = ((composite_major > 0) || (composite_minor >= 2)); #else /* HAVE_NAME_WINDOW_PIXMAP */ display_info->have_name_window_pixmap = FALSE; #endif /* HAVE_NAME_WINDOW_PIXMAP */ #if HAVE_OVERLAYS display_info->have_overlays = ((composite_major > 0) || (composite_minor >= 3)); #endif /* HAVE_OVERLAYS */ #else /* HAVE_COMPOSITOR */ display_info->enable_compositor = FALSE; #endif /* HAVE_COMPOSITOR */ } gboolean compositorManageScreen (ScreenInfo *screen_info) { #ifdef HAVE_COMPOSITOR DisplayInfo *display_info; XRenderPictureAttributes pa; XRenderPictFormat *visual_format; gushort buffer; g_return_val_if_fail (screen_info != NULL, FALSE); TRACE ("entering compositorManageScreen"); if (compositorCheckCMSelection (screen_info)) { g_warning ("Another compositing manager is running on screen %i", screen_info->screen); return FALSE; } compositorSetCMSelection (screen_info, screen_info->xfwm4_win); display_info = screen_info->display_info; screen_info->output = screen_info->xroot; #if HAVE_OVERLAYS if (display_info->have_overlays) { screen_info->overlay = XCompositeGetOverlayWindow (display_info->dpy, screen_info->xroot); if (screen_info->overlay != None) { XserverRegion region; XSetWindowAttributes attributes; XMapRaised (display_info->dpy, screen_info->overlay); region = XFixesCreateRegion (display_info->dpy, NULL, 0); XFixesSetWindowShapeRegion (display_info->dpy, screen_info->overlay, ShapeBounding, 0, 0, 0); XFixesSetWindowShapeRegion (display_info->dpy, screen_info->overlay, ShapeInput, 0, 0, region); XFixesDestroyRegion (display_info->dpy, region); screen_info->root_overlay = XCreateWindow (display_info->dpy, screen_info->overlay, 0, 0, screen_info->width, screen_info->height, 0, screen_info->depth, InputOutput, screen_info->visual, 0, &attributes); XMapRaised (display_info->dpy, screen_info->root_overlay); screen_info->output = screen_info->root_overlay; DBG ("Overlay enabled (0x%lx -> 0x%lx)", screen_info->overlay, screen_info->root_overlay); } else { /* Something is wrong with overlay support */ DBG ("Cannot get root window overlay, overlay support disabled"); display_info->have_overlays = FALSE; } } #endif /* HAVE_OVERLAYS */ DBG ("Window used for output: 0x%lx (%s)", screen_info->output, display_info->have_overlays ? "overlay" : "root"); XCompositeRedirectSubwindows (display_info->dpy, screen_info->xroot, CompositeRedirectManual); screen_info->compositor_active = TRUE; visual_format = XRenderFindVisualFormat (display_info->dpy, DefaultVisual (display_info->dpy, screen_info->screen)); if (!visual_format) { g_warning ("Cannot find visual format on screen %i", screen_info->screen); compositorUnmanageScreen (screen_info); return FALSE; } pa.subwindow_mode = IncludeInferiors; screen_info->rootPicture = XRenderCreatePicture (display_info->dpy, screen_info->output, visual_format, CPSubwindowMode, &pa); if (screen_info->rootPicture == None) { g_warning ("Cannot create root picture on screen %i", screen_info->screen); compositorUnmanageScreen (screen_info); return FALSE; } screen_info->gaussianSize = -1; screen_info->gaussianMap = make_gaussian_map(SHADOW_RADIUS); presum_gaussian (screen_info); screen_info->cursorPicture = None; /* Change following argb values to play with shadow colors */ screen_info->blackPicture = solid_picture (screen_info, TRUE, 1.0, /* alpha */ 0.0, /* red */ 0.0, /* green */ 0.0 /* blue */); screen_info->rootTile = None; screen_info->allDamage = None; screen_info->prevDamage = None; screen_info->screenRegion = get_screen_region (screen_info); screen_info->cwindows = NULL; screen_info->cwindow_hash = g_hash_table_new(g_direct_hash, g_direct_equal); screen_info->wins_unredirected = 0; screen_info->compositor_timeout_id = 0; screen_info->zoomed = FALSE; screen_info->zoom_timeout_id = 0; screen_info->damages_pending = FALSE; screen_info->current_buffer = 0; memset(screen_info->transform.matrix, 0, 9); screen_info->transform.matrix[0][0] = 1 << 16; screen_info->transform.matrix[1][1] = 1 << 16; screen_info->transform.matrix[2][2] = 1 << 16; screen_info->zoomBuffer = None; screen_info->use_n_buffers = 1; for (buffer = 0; buffer < N_BUFFERS; buffer++) { screen_info->rootPixmap[buffer] = None; screen_info->rootBuffer[buffer] = None; #ifdef HAVE_EPOXY screen_info->glx_drawable[buffer] = None; #ifdef HAVE_XSYNC screen_info->fence[buffer] = None; #endif /* HAVE_XSYNC */ #endif /* HAVE_EPOXY */ } XClearArea (display_info->dpy, screen_info->output, 0, 0, 0, 0, TRUE); TRACE ("manual compositing enabled"); #ifdef HAVE_EPOXY screen_info->use_glx = (screen_info->vblank_mode == VBLANK_AUTO || screen_info->vblank_mode == VBLANK_GLX); #ifdef HAVE_XSYNC screen_info->use_glx &= display_info->have_xsync; #endif /* HAVE_XSYNC */ if (screen_info->use_glx) { screen_info->use_n_buffers = 1; screen_info->glx_context = None; screen_info->glx_window = None; screen_info->rootTexture = None; screen_info->texture_filter = GL_LINEAR; screen_info->gl_sync = 0; screen_info->use_glx = init_glx (screen_info); } #else /* HAVE_EPOXY */ screen_info->use_glx = FALSE; #endif /* HAVE_EPOXY */ #ifdef HAVE_PRESENT_EXTENSION screen_info->use_present = display_info->have_present && #ifdef HAVE_EPOXY !screen_info->use_glx && #endif /* HAVE_EPOXY */ (screen_info->vblank_mode == VBLANK_AUTO || screen_info->vblank_mode == VBLANK_XPRESENT); if (screen_info->use_present) { screen_info->use_n_buffers = N_BUFFERS; screen_info->present_pending = FALSE; XPresentSelectInput (display_info->dpy, screen_info->output, PresentCompleteNotifyMask); } #else /* HAVE_PRESENT_EXTENSION */ screen_info->use_present = FALSE; #endif /* HAVE_PRESENT_EXTENSION */ if (screen_info->use_present) { g_info ("Compositor using XPresent for vsync"); } else if (screen_info->use_glx) { g_info ("Compositor using GLX for vsync"); } else { g_info ("No vsync support in compositor"); } XFixesSelectCursorInput (display_info->dpy, screen_info->xroot, XFixesDisplayCursorNotifyMask); return TRUE; #else return FALSE; #endif /* HAVE_COMPOSITOR */ } void compositorUnmanageScreen (ScreenInfo *screen_info) { #ifdef HAVE_COMPOSITOR DisplayInfo *display_info; GList *list; gushort buffer; g_return_if_fail (screen_info != NULL); TRACE ("entering compositorUnmanageScreen"); display_info = screen_info->display_info; if (!compositorIsUsable (display_info)) { return; } if (!screen_info->compositor_active) { TRACE ("compositor not active on screen %i", screen_info->screen); return; } screen_info->compositor_active = FALSE; remove_timeouts (screen_info); myDisplayErrorTrapPush (display_info); for (list = screen_info->cwindows; list; list = g_list_next (list)) { CWindow *cw2 = (CWindow *) list->data; free_win_data (cw2, TRUE); } g_hash_table_destroy(screen_info->cwindow_hash); screen_info->cwindow_hash = NULL; g_list_free (screen_info->cwindows); screen_info->cwindows = NULL; #ifdef HAVE_EPOXY if (screen_info->use_glx) { free_glx_data (screen_info); for (buffer = 0; buffer < screen_info->use_n_buffers; buffer++) { destroy_glx_drawable (screen_info, buffer); } } #endif /* HAVE_EPOXY */ for (buffer = 0; buffer < screen_info->use_n_buffers; buffer++) { if (screen_info->rootPixmap[buffer]) { XFreePixmap (display_info->dpy, screen_info->rootPixmap[buffer]); screen_info->rootPixmap[buffer] = None; } if (screen_info->rootBuffer[buffer]) { XRenderFreePicture (display_info->dpy, screen_info->rootBuffer[buffer]); screen_info->rootBuffer[buffer] = None; } #ifdef HAVE_EPOXY fence_destroy (screen_info, buffer); #endif /* HAVE_EPOXY */ } if (screen_info->allDamage) { XFixesDestroyRegion (display_info->dpy, screen_info->allDamage); screen_info->allDamage = None; } if (screen_info->prevDamage) { XFixesDestroyRegion (display_info->dpy, screen_info->prevDamage); screen_info->prevDamage = None; } if (screen_info->screenRegion) { XFixesDestroyRegion (display_info->dpy, screen_info->screenRegion); screen_info->screenRegion = None; } if (screen_info->zoomBuffer) { XRenderFreePicture (display_info->dpy, screen_info->zoomBuffer); screen_info->zoomBuffer = None; } if (screen_info->rootPicture) { XRenderFreePicture (display_info->dpy, screen_info->rootPicture); screen_info->rootPicture = None; } if (screen_info->blackPicture) { XRenderFreePicture (display_info->dpy, screen_info->blackPicture); screen_info->blackPicture = None; } if (screen_info->cursorPicture) { XRenderFreePicture (display_info->dpy, screen_info->cursorPicture); screen_info->cursorPicture = None; } if (screen_info->shadowTop) { g_free (screen_info->shadowTop); screen_info->shadowTop = NULL; } if (screen_info->shadowCorner) { g_free (screen_info->shadowCorner); screen_info->shadowCorner = NULL; } if (screen_info->gaussianMap) { g_free (screen_info->gaussianMap); screen_info->gaussianMap = NULL; } #if HAVE_OVERLAYS if (display_info->have_overlays) { XDestroyWindow (display_info->dpy, screen_info->root_overlay); screen_info->root_overlay = None; XCompositeReleaseOverlayWindow (display_info->dpy, screen_info->overlay); screen_info->overlay = None; } #endif /* HAVE_OVERLAYS */ screen_info->gaussianSize = -1; screen_info->wins_unredirected = 0; if (screen_info->zoomed) { XFixesShowCursor (display_info->dpy, screen_info->xroot); } XCompositeUnredirectSubwindows (display_info->dpy, screen_info->xroot, CompositeRedirectManual); screen_info->output = screen_info->xroot; compositorSetCMSelection (screen_info, None); myDisplayErrorTrapPopIgnored (display_info); #endif /* HAVE_COMPOSITOR */ } void compositorAddAllWindows (ScreenInfo *screen_info) { #ifdef HAVE_COMPOSITOR DisplayInfo *display_info; Window w1, w2, *wins; unsigned int count, i; TRACE ("entering"); display_info = screen_info->display_info; if (!compositorIsUsable (display_info)) { return; } myDisplayGrabServer (display_info); XQueryTree (display_info->dpy, screen_info->xroot, &w1, &w2, &wins, &count); for (i = 0; i < count; i++) { Client *c; c = myScreenGetClientFromWindow (screen_info, wins[i], SEARCH_FRAME); compositorAddWindow (display_info, wins[i], c); } if (wins) { XFree (wins); } myDisplayUngrabServer (display_info); #endif /* HAVE_COMPOSITOR */ } gboolean compositorActivateScreen (ScreenInfo *screen_info, gboolean active) { #ifdef HAVE_COMPOSITOR DisplayInfo *display_info; g_return_val_if_fail (screen_info != NULL, FALSE); TRACE ("active %s", active ? "TRUE" : "FALSE"); display_info = screen_info->display_info; if (!compositorIsUsable (display_info)) { return FALSE; } if (screen_info->compositor_active == active) { return FALSE; } if (active) { compositorManageScreen (screen_info); compositorAddAllWindows (screen_info); } else { compositorUnmanageScreen (screen_info); } return TRUE; #else /* HAVE_COMPOSITOR */ return FALSE; #endif /* HAVE_COMPOSITOR */ } void compositorUpdateScreenSize (ScreenInfo *screen_info) { #ifdef HAVE_COMPOSITOR DisplayInfo *display_info; gushort buffer; g_return_if_fail (screen_info != NULL); TRACE ("entering"); display_info = screen_info->display_info; if (!compositorIsUsable (display_info)) { return; } myDisplayErrorTrapPush (display_info); #if HAVE_OVERLAYS if (display_info->have_overlays) { XResizeWindow (display_info->dpy, screen_info->overlay, screen_info->width, screen_info->height); XResizeWindow (display_info->dpy, screen_info->root_overlay, screen_info->width, screen_info->height); } #endif if (screen_info->zoomBuffer) { XRenderFreePicture (display_info->dpy, screen_info->zoomBuffer); screen_info->zoomBuffer = None; } #ifdef HAVE_EPOXY if (screen_info->use_glx) { for (buffer = 0; buffer < screen_info->use_n_buffers; buffer++) { destroy_glx_drawable (screen_info, buffer); } } #endif /* HAVE_EPOXY */ for (buffer = 0; buffer < screen_info->use_n_buffers; buffer++) { if (screen_info->rootPixmap[buffer]) { XFreePixmap (display_info->dpy, screen_info->rootPixmap[buffer]); screen_info->rootPixmap[buffer] = None; } if (screen_info->rootBuffer[buffer]) { XRenderFreePicture (display_info->dpy, screen_info->rootBuffer[buffer]); screen_info->rootBuffer[buffer] = None; } #ifdef HAVE_EPOXY fence_destroy (screen_info, buffer); #endif /* HAVE_EPOXY */ } if (screen_info->screenRegion) { XFixesDestroyRegion (display_info->dpy, screen_info->screenRegion); screen_info->screenRegion = None; } damage_screen (screen_info); myDisplayErrorTrapPopIgnored (display_info); #endif /* HAVE_COMPOSITOR */ } void compositorWindowSetOpacity (DisplayInfo *display_info, Window id, guint32 opacity) { #ifdef HAVE_COMPOSITOR CWindow *cw; g_return_if_fail (display_info != NULL); g_return_if_fail (id != None); TRACE ("window 0x%lx", id); if (!compositorIsUsable (display_info)) { return; } cw = find_cwindow_in_display (display_info, id); if (cw) { set_win_opacity (cw, opacity); } #endif /* HAVE_COMPOSITOR */ } void compositorRebuildScreen (ScreenInfo *screen_info) { #ifdef HAVE_COMPOSITOR DisplayInfo *display_info; GList *list; g_return_if_fail (screen_info != NULL); TRACE ("entering"); display_info = screen_info->display_info; if (!compositorIsUsable (display_info)) { return; } for (list = screen_info->cwindows; list; list = g_list_next (list)) { CWindow *cw2 = (CWindow *) list->data; free_win_data (cw2, FALSE); init_opacity (cw2); } damage_screen (screen_info); #endif /* HAVE_COMPOSITOR */ } gboolean compositorTestServer (DisplayInfo *display_info) { #ifdef HAVE_COMPOSITOR char *vendor; g_return_val_if_fail (display_info != NULL, FALSE); TRACE ("entering"); vendor = ServerVendor (display_info->dpy); if (vendor && (!strstr ("X.Org", vendor))) { /* Check the version, X.org 6.8.x has some bugs that makes it not suitable for use with xfwm4 compositor */ if ((VendorRelease(display_info->dpy) / 10) <= 68) { return FALSE; } } return TRUE; #else /* HAVE_COMPOSITOR */ return FALSE; #endif /* HAVE_COMPOSITOR */ } vblankMode compositorParseVblankMode (const gchar *vblank_setting) { #ifdef HAVE_PRESENT_EXTENSION if (g_ascii_strcasecmp (vblank_setting, "xpresent") == 0) { return VBLANK_XPRESENT; } else #endif /* HAVE_PRESENT_EXTENSION */ #ifdef HAVE_EPOXY if (g_ascii_strcasecmp (vblank_setting, "glx") == 0) { return VBLANK_GLX; } else #endif /* HAVE_EPOXY */ if (g_ascii_strcasecmp (vblank_setting, "off") == 0) { return VBLANK_OFF; } return VBLANK_AUTO; } void compositorSetVblankMode (ScreenInfo *screen_info, vblankMode vblank_mode) { #ifdef HAVE_COMPOSITOR g_return_if_fail (screen_info != NULL); TRACE ("entering"); screen_info->vblank_mode = vblank_mode; #endif /* HAVE_COMPOSITOR */ }