/* * $Id$ * * Evoke, head honcho of everything * Part of Equinox Desktop Environment (EDE). * Based on xcompmgr (c) 2003 Keith Packard. * Copyright (c) 2007 EDE Authors. * * This program is licensed under terms of the * GNU General Public License version 2 or newer. * See COPYING for details. */ #include "Composite.h" #include "ClassHack.h" #include #include #include #include // memcpy #include // malloc, realloc #include #include #define TRANSLUCENT 0xe0000000 #define OPAQUE 0xffffffff #define REFRESH_TIMEOUT 0.05 #define WIN_MODE_SOLID 0 #define WIN_MODE_TRANS 1 #define WIN_MODE_ARGB 2 static int render_error; static int damage_error; static int xfixes_error; static bool have_name_pixmap = false; static int composite_opcode; static int damage_event; static bool another_is_running; Atom _XA_NET_WM_WINDOW_OPACITY; Atom _XA_NET_WM_WINDOW_TYPE; Atom _XA_NET_WM_WINDOW_TYPE_DESKTOP; Atom _XA_NET_WM_WINDOW_TYPE_DOCK; Atom _XA_NET_WM_WINDOW_TYPE_TOOLBAR; Atom _XA_NET_WM_WINDOW_TYPE_MENU; Atom _XA_NET_WM_WINDOW_TYPE_UTILITY; Atom _XA_NET_WM_WINDOW_TYPE_SPLASH; Atom _XA_NET_WM_WINDOW_TYPE_DIALOG; Atom _XA_NET_WM_WINDOW_TYPE_NORMAL; static char* backgroundProps[] = { "_XROOTPMAP_ID", "_XSETROOT_ID", 0 }; //#define HAVE_NAME_WINDOW_PIXMAP 1 struct CWindow { Window id; #if HAVE_NAME_WINDOW_PIXMAP Pixmap pixmap; #endif XWindowAttributes attr; int mode; int damaged; Damage damage; Picture picture; Picture picture_alpha; Picture picture_shadow; XserverRegion border_size; XserverRegion extents; Picture shadow; int shadow_dx; int shadow_dy; int shadow_w; int shadow_h; unsigned int opacity; Atom window_type; unsigned long damage_sequence; // sequence when damage was created // for drawing translucent windows XserverRegion border_clip; }; XserverRegion allDamage; bool fadeWindows = true; bool clipChanged = false; bool excludeDockShadows = true; Picture rootBuffer = 0; Picture rootPicture = 0; Picture rootTile = 0; Picture blackPicture = 0; int shadowRadius = 12; int shadowOffsetX = -15; int shadowOffsetY = -15; double shadowOpacity = .75; bool hasNamePixmap = true; #define DO_BETTER_REPAINT 1 void idle_cb(void* c) { Composite* comp = (Composite*)c; #ifdef DO_BETTER_REPAINT if(allDamage != None) { comp->paint_all(allDamage); XFixesDestroyRegion(fl_display, allDamage); allDamage = None; clipChanged = false; } #else comp->paint_all(allDamage); if(allDamage != None) { XFixesDestroyRegion(fl_display, allDamage); allDamage = None; clipChanged = false; } #endif #ifndef USE_CHECK Fl::repeat_timeout(REFRESH_TIMEOUT, idle_cb, c); #endif } int xerror_handler(Display* display, XErrorEvent* xev) { if(xev->request_code == composite_opcode && xev->minor_code == X_CompositeRedirectSubwindows) { EWARNING(ESTRLOC ": Another composite manager is running\n"); another_is_running = true; return 1; } char* name = NULL; int o = xev->error_code - xfixes_error; switch(o) { case BadRegion: name = "BadRegion"; break; default: break; } o = xev->error_code - damage_error; switch(o) { case BadDamage: name = "BadDamage"; break; default: break; } o = xev->error_code - render_error; switch(o) { case BadPictFormat: name = "BadPictFormat"; break; case BadPicture: name = "BadPicture"; break; case BadPictOp: name = "BadPictOp"; break; case BadGlyphSet: name = "BadGlyphSet"; break; case BadGlyph: name = "BadGlyph"; break; default: break; } //EDEBUG(ESTRLOC ": (%s) : error %i request %i minor %i serial %i\n", // (name ? name : "unknown"), xev->error_code, xev->request_code, xev->minor_code, xev->serial); #if 0 char buff[128]; XGetErrorDatabaseText(fl_display, "XlibMessage", "XError", "", buff, 128); EDEBUG("%s", buff); XGetErrorText(fl_display, xev->error_code, buff, 128); EDEBUG(" :%s\n", buff); XGetErrorDatabaseText(fl_display, "XlibMessage", "MajorCode", "%d", buff, 128); EDEBUG(" "); EDEBUG(buff, xev->request_code); EDEBUG("\n"); XGetErrorDatabaseText(fl_display, "XlibMessage", "MinorCode", "%d", buff, 128); EDEBUG(" "); EDEBUG(buff, xev->minor_code); EDEBUG("\n"); XGetErrorDatabaseText(fl_display, "XlibMessage", "ResourceID", "%d", buff, 128); EDEBUG(" "); EDEBUG(buff, xev->resourceid); EDEBUG("\n"); #endif return 0; } void set_ignore(unsigned long sequence) { // TODO } unsigned int get_opacity_property(CWindow* win, unsigned int dflt) { Atom actual; int format; unsigned long n, left; unsigned char* data = NULL; int ret = XGetWindowProperty(fl_display, win->id, _XA_NET_WM_WINDOW_OPACITY, 0L, 1L, False, XA_CARDINAL, &actual, &format, &n, &left, &data); if(ret == Success && data != NULL) { unsigned int p; // TODO: replace memcpy call memcpy(&p, data, sizeof(unsigned int)); XFree(data); // EDEBUG(":) Opacity for %i = %i\n", win->id, p); return p; } //EDEBUG("Opacity for %i = %i\n", win->id, dflt); return dflt; } double get_opacity_percent(CWindow* win, double dflt) { unsigned int opacity = get_opacity_property(win, (unsigned int)(OPAQUE * dflt)); return opacity * 1.0 / OPAQUE; } Atom get_window_type_property(Window win) { Atom actual; int format; unsigned long n, left; unsigned char* data = NULL; int ret = XGetWindowProperty(fl_display, win, _XA_NET_WM_WINDOW_TYPE, 0L, 1L, False, XA_ATOM, &actual, &format, &n, &left, &data); if(ret == Success && data != NULL) { Atom a; // TODO: replace memcpy call memcpy(&a, data, sizeof(Atom)); XFree(data); return a; } return _XA_NET_WM_WINDOW_TYPE_NORMAL; } Atom determine_window_type(Window win) { Atom type = get_window_type_property(win); if(type != _XA_NET_WM_WINDOW_TYPE_NORMAL) return type; // scan children Window root_return, parent_return; Window* children = NULL; unsigned int nchildren; if(XQueryTree(fl_display, win, &root_return, &parent_return, &children, &nchildren) != 0) { if(children) XFree(children); return _XA_NET_WM_WINDOW_TYPE_NORMAL; } for(unsigned int i = 0; i < nchildren; i++) { type = determine_window_type(children[i]); if(type != _XA_NET_WM_WINDOW_TYPE_NORMAL) return type; } if(children) XFree(children); return _XA_NET_WM_WINDOW_TYPE_NORMAL; } void add_damage(XserverRegion damage) { if(allDamage != None) { XFixesUnionRegion(fl_display, allDamage, allDamage, damage); XFixesDestroyRegion(fl_display, damage); } else allDamage = damage; } void determine_mode(CWindow* win) { if(win->picture_alpha) { XRenderFreePicture(fl_display, win->picture_alpha); win->picture_alpha = None; } if(win->picture_shadow) { XRenderFreePicture(fl_display, win->picture_shadow); win->picture_shadow = None; } XRenderPictFormat* format; if(get_attributes_class_hack(&win->attr) == InputOnly) format = 0; else format = XRenderFindVisualFormat(fl_display, win->attr.visual); if(format && format->type == PictTypeDirect && format->direct.alphaMask) win->mode = WIN_MODE_ARGB; else if(win->opacity != OPAQUE) win->mode = WIN_MODE_TRANS; else win->mode = WIN_MODE_SOLID; if(win->extents) { XserverRegion damage = XFixesCreateRegion(fl_display, 0, 0); XFixesCopyRegion(fl_display, damage, win->extents); add_damage(damage); } } // TODO: make this a member XserverRegion set_border_size(CWindow* win) { /* * if window doesn't exist anymore, this will generate an error * as well as not generate a region. Perhaps a better XFixes * architecture would be to have a request that copies instead * of creates, that way you'd just end up with an empty region * instead of an invalid XID. */ set_ignore(NextRequest(fl_display)); XserverRegion border = XFixesCreateRegionFromWindow(fl_display, win->id, WindowRegionBounding); // translate this set_ignore(NextRequest(fl_display)); XFixesTranslateRegion(fl_display, border, win->attr.x + win->attr.border_width, win->attr.y + win->attr.border_width); return border; } // TODO: make this a part of paint_root() Picture root_tile(void) { Atom actual_type; int actual_format; unsigned long nitems; unsigned long bytes_after; unsigned char* prop; Pixmap pixmap = None; Atom pixmap_atom = XInternAtom(fl_display, "PIXMAP", False); Window root = RootWindow(fl_display, fl_screen); bool fill; for(int i = 0; backgroundProps[i]; i++) { if(XGetWindowProperty(fl_display, root, XInternAtom(fl_display, backgroundProps[i], False), 0, 4, False, AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &prop) == Success && actual_type == pixmap_atom && actual_format == 32 && nitems == 1) { memcpy(&pixmap, prop, 4); XFree(prop); fill = false; break; } } if(!pixmap) { pixmap = XCreatePixmap(fl_display, root, 1, 1, DefaultDepth(fl_display, fl_screen)); fill = true; } XRenderPictureAttributes pa; pa.repeat = True; Picture picture = XRenderCreatePicture(fl_display, pixmap, XRenderFindVisualFormat(fl_display, DefaultVisual(fl_display, fl_screen)), CPRepeat, &pa); if(fill) { XRenderColor c; c.red = c.green = c.blue = 0x8080; c.alpha = 0xffff; XRenderFillRectangle(fl_display, PictOpSrc, picture, &c, 0, 0, 1, 1); } return picture; } Picture solid_picture(bool argb, double a, double r, double g, double b) { Pixmap pixmap = XCreatePixmap(fl_display, RootWindow(fl_display, fl_screen), 1, 1, argb ? 32 : 8); if(!pixmap) return None; XRenderPictureAttributes pa; pa.repeat = True; Picture picture = XRenderCreatePicture(fl_display, pixmap, XRenderFindStandardFormat(fl_display, argb ? PictStandardARGB32 : PictStandardA8), CPRepeat, &pa); if(!picture) { XFreePixmap(fl_display, pixmap); return None; } XRenderColor color; color.alpha = a * 0xffff; color.red = r * 0xffff; color.green = g * 0xffff; color.blue = b * 0xffff; XRenderFillRectangle(fl_display, PictOpSrc, picture, &color, 0, 0, 1, 1); XFreePixmap(fl_display, pixmap); return picture; } Composite::Composite() : manual_redirect(true) { } Composite::~Composite() { EDEBUG("Composite::~Composite()\n"); #ifdef USE_CHECK Fl::remove_check(idle_cb); #else Fl::remove_timeout(idle_cb); #endif // TODO: this part should call finish_destroy_window() CWindowListIter it = window_list.begin(), it_end = window_list.end(); while(it != it_end) { delete *it; ++it; } } bool Composite::init(void) { another_is_running = false; // set error handler first XSetErrorHandler(xerror_handler); int render_event; if(!XRenderQueryExtension(fl_display, &render_event, &render_error)) { EWARNING(ESTRLOC ": No render extension\n"); return false; } // check Composite extension int composite_event, composite_error; if(!XQueryExtension(fl_display, COMPOSITE_NAME, &composite_opcode, &composite_event, &composite_error)) { EWARNING(ESTRLOC ": No composite extension\n"); return false; } int composite_major, composite_minor; XCompositeQueryVersion(fl_display, &composite_major, &composite_minor); EDEBUG(ESTRLOC ": Setting up XComposite version %i.%i\n", composite_major, composite_minor); if(composite_major > 0 || composite_minor >= 2) have_name_pixmap = true; // check XDamage extension if(!XDamageQueryExtension(fl_display, &damage_event, &damage_error)) { EWARNING(ESTRLOC ": No damage extension\n"); return false; } // check XFixes extension int xfixes_event; if(!XFixesQueryExtension(fl_display, &xfixes_event, &xfixes_error)) { EWARNING(ESTRLOC ": No XFixes extension\n"); return false; } _XA_NET_WM_WINDOW_OPACITY = XInternAtom(fl_display, "_NET_WM_WINDOW_OPACITY", False); _XA_NET_WM_WINDOW_TYPE = XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE", False); _XA_NET_WM_WINDOW_TYPE_DESKTOP = XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE_DESKTOP", False); _XA_NET_WM_WINDOW_TYPE_DOCK = XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE_DOCK", False); _XA_NET_WM_WINDOW_TYPE_TOOLBAR = XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE_TOOLBAR", False); _XA_NET_WM_WINDOW_TYPE_MENU = XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE_MENU", False); _XA_NET_WM_WINDOW_TYPE_UTILITY = XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE_UTILITY", False); _XA_NET_WM_WINDOW_TYPE_SPLASH = XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE_SPLASH", False); _XA_NET_WM_WINDOW_TYPE_DIALOG = XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE_DIALOG", False); _XA_NET_WM_WINDOW_TYPE_NORMAL = XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE_NORMAL", False); allDamage = None; clipChanged = true; XRenderPictureAttributes pa; pa.subwindow_mode = IncludeInferiors; rootPicture = XRenderCreatePicture(fl_display, RootWindow(fl_display, fl_screen), XRenderFindVisualFormat(fl_display, DefaultVisual(fl_display, fl_screen)), CPSubwindowMode, &pa); blackPicture = solid_picture(true, 1, 0, 0, 0); // now redirect all windows to the Composite XGrabServer(fl_display); /* * If another manager is running, XCompositeRedirectSubwindows() will report it in xerror_handler(). * This only works if another-manager/evoke are both set in CompositeRedirectManual mode */ if(!manual_redirect) XCompositeRedirectSubwindows(fl_display, RootWindow(fl_display, fl_screen), CompositeRedirectAutomatic); else { XCompositeRedirectSubwindows(fl_display, RootWindow(fl_display, fl_screen), CompositeRedirectManual); #if 0 // Commented, since already selected in main evoke code (evoke.cpp). XSelectInput(fl_display, RootWindow(fl_display, fl_screen), SubstructureNotifyMask | ExposureMask | StructureNotifyMask | PropertyChangeMask); #endif Window root_ret, parent_ret; Window* children; unsigned int nchildren; XQueryTree(fl_display, RootWindow(fl_display, fl_screen), &root_ret, &parent_ret, &children, &nchildren); for(unsigned int i = 0; i < nchildren; i++) add_window(children[i], i ? children[i-1] : None); XFree(children); } XUngrabServer(fl_display); /* * draw all windows now, but only if we manualy manage them * since window_list will be empty */ if(manual_redirect) { paint_all(None); /* * Using add_check() instead add_timeout() makes difference in redrawing * since add_check() callback function will be called before FLTK flushes * the display which makes it very suitable for smooth redrawing. * * On other hand, using add_timeout() will trigger timer each REFRESH_TIMEOUT * no matter for pending events (especially XDamageNotifyEvent) and due that * timeout we can miss some XDamage events, yielding little bit non-smooth redrawing. */ #ifdef USE_CHECK Fl::add_check(idle_cb, this); #else Fl::add_timeout(REFRESH_TIMEOUT, idle_cb, this); #endif } return true; } void Composite::add_window(Window id, Window previous) { CWindow* new_win = new CWindow; new_win->id = id; set_ignore(NextRequest(fl_display)); if(!XGetWindowAttributes(fl_display, id, &new_win->attr)) { delete new_win; return; } new_win->damaged = 0; #if HAVE_NAME_WINDOW_PIXMAP new_win->pixmap = None; #endif new_win->picture = None; if(get_attributes_class_hack(&(new_win->attr)) == InputOnly) { new_win->damage_sequence = 0; new_win->damage = None; } else { new_win->damage_sequence = NextRequest(fl_display); new_win->damage = XDamageCreate(fl_display, new_win->id, XDamageReportNonEmpty); } new_win->picture_alpha = None; new_win->picture_shadow = None; new_win->border_size = None; new_win->extents = None; new_win->shadow = None; new_win->shadow_dx = 0; new_win->shadow_dy = 0; new_win->shadow_w = 0; new_win->shadow_h = 0; new_win->opacity = get_opacity_property(new_win, OPAQUE); new_win->border_clip = None; new_win->window_type = determine_window_type(new_win->id); determine_mode(new_win); // put window at the top window_list.push_front(new_win); // map it if was mapped before if(new_win->attr.map_state == IsViewable) map_window(new_win->id, new_win->damage_sequence - 1, true); } void Composite::map_window(Window id, unsigned long sequence, bool fade) { CWindow* win = find_window(id); if(!win) return; win->attr.map_state = IsViewable; // this needs to be here or we loose transparency messages //XSelectInput(fl_display, win->id, PropertyChangeMask); XSelectInput(fl_display, win->id, PropertyChangeMask | win->attr.your_event_mask | StructureNotifyMask); win->damaged = 0; /* * XXX: a hack, not present in xcompmgr due main event * loop changes */ //repair_window(win); // TODO: fading support } void Composite::unmap_window(Window id, bool fade) { CWindow* win = find_window(id); if(!win) return; win->attr.map_state = IsUnmapped; #if HAVE_NAME_WINDOW_PIXMAP if(win->pixmap && fade && fadeWindows) { // TODO: fading support } else #endif finish_unmap_window(win); } void Composite::finish_unmap_window(CWindow* win) { win->damaged = 0; if(win->extents != None) { add_damage(win->extents); // destroy region win->extents = None; } #if HAVE_NAME_WINDOW_PIXMAP if(win->pixmap) { XFreePixmap(fl_display, win->pixmap); win->pixmap = None; } #endif if(win->picture) { set_ignore(NextRequest(fl_display)); XRenderFreePicture(fl_display, win->picture); win->picture = None; } // don't care about properties anymore set_ignore(NextRequest(fl_display)); XSelectInput(fl_display, win->id, 0); if(win->border_size) { set_ignore(NextRequest(fl_display)); XFixesDestroyRegion(fl_display, win->border_size); win->border_size = None; } if(win->shadow) { XRenderFreePicture(fl_display, win->shadow); win->shadow = None; } if(win->border_clip) { XFixesDestroyRegion(fl_display, win->border_clip); win->border_clip = None; } clipChanged = true; } void Composite::configure_window(const XConfigureEvent* ce) { CWindow* win = find_window(ce->window); if(!win) { if(ce->window == RootWindow(fl_display, fl_screen)) { if(rootBuffer) { XRenderFreePicture(fl_display, rootBuffer); rootBuffer = None; } // TODO: here should be updated // root_width and root_height } return; } XserverRegion damage = None; damage = XFixesCreateRegion(fl_display, 0, 0); if(win->extents != None) XFixesCopyRegion(fl_display, damage, win->extents); win->attr.x = ce->x; win->attr.y = ce->y; if(win->attr.width != ce->width || win->attr.height != ce->height) { #if HAVE_NAME_WINDOW_PIXMAP if(win->pixmap) { XFreePixmap(fl_display, win->pixmap); win->pixmap = None; if(win->picture) { XRenderFreePicture(fl_display, win->picture); win->picture = None; } } #endif if(win->shadow) { XRenderFreePicture(fl_display, win->shadow); win->shadow = None; } } win->attr.width = ce->width; win->attr.height = ce->height; win->attr.border_width = ce->border_width; win->attr.override_redirect = ce->override_redirect; #if 0 EDEBUG("--- before restack() ---\n"); CWindowListIter first = window_list.begin(); for(; first != window_list.end(); ++first) EDEBUG("0x%x ", (*first)->id); EDEBUG("\n"); EDEBUG("ME: 0x%x ABOVE: 0x%x\n", win->id, ce->above); #endif restack_window(win, ce->above); #if 0 EDEBUG("--- after restack() ---\n"); first = window_list.begin(); for(; first != window_list.end(); ++first) EDEBUG("0x%x ", (*first)->id); EDEBUG("\n"); #endif if(damage) { XserverRegion extents = window_extents(win); XFixesUnionRegion(fl_display, damage, damage, extents); XFixesDestroyRegion(fl_display, extents); add_damage(damage); } clipChanged = true; } void Composite::restack_window(CWindow* win, Window new_above) { CWindowListIter it = window_list.begin(), it_end = window_list.end(); while(it != it_end) { if(*it == win) break; ++it; } Window old_above; if(it == it_end) // not found old_above = None; else { ++it; // get the next one // check again if(it == it_end) old_above = None; else old_above = (*it)->id; } if(old_above != new_above) { --it; // return to our window EASSERT(*it == win && "Wrong code"); window_list.erase(it); /* it = window_list.begin(); it_end = window_list.end(); // find our window and remove it from current position while(it != it_end) { if(*it == win) { window_list.erase(it); break; } ++it; }*/ it = window_list.begin(); // now found new_above and insert our window while(it != it_end) { if((*it)->id == new_above) { break; } ++it; } // now insert where iterator was stopped (if not found 'new_above') it will insert at the end window_list.insert(it, win); } } void Composite::property_notify(const XPropertyEvent* pe) { for(int i = 0; backgroundProps[i]; i++) { if(pe->atom == XInternAtom(fl_display, backgroundProps[i], False)) { if(rootTile) { XClearArea(fl_display, RootWindow(fl_display, fl_screen), 0, 0, 0, 0, True); XRenderFreePicture(fl_display, rootTile); rootTile = None; break; } } } // check if Trans property was changed if(pe->atom == _XA_NET_WM_WINDOW_OPACITY) { // reset mode and redraw window CWindow* win = find_window(pe->window); if(win) { // TODO: fading support win->opacity = get_opacity_property(win, OPAQUE); determine_mode(win); if(win->shadow) { XRenderFreePicture(fl_display, win->shadow); win->shadow = None; win->extents = window_extents(win); } } } } #if 0 static XRectangle* expose_rects = 0; static int size_expose = 0; static int n_expose = 0; #endif void Composite::expose_event(const XExposeEvent* ee) { #if 0 if(ee->window != RootWindow(fl_display, fl_screen)) return; int more = ee->count + 1; if(n_expose == size_expose) { if(expose_rects) { expose_rects = (XRectangle*)realloc(expose_rects, (size_expose + more) * sizeof(XRectangle)); size_expose += more; } else { expose_rects = (XRectangle*)malloc(more * sizeof(XRectangle)); size_expose = more; } } expose_rects[n_expose].x = ee->x; expose_rects[n_expose].y = ee->y; expose_rects[n_expose].width = ee->width; expose_rects[n_expose].height = ee->height; n_expose++; if(ee->count == 0) { // expose root XserverRegion region = XFixesCreateRegion(fl_display, expose_rects, n_expose); add_damage(region); n_expose = 0; } #endif XRectangle rect[1]; rect[0].x = ee->x; rect[0].y = ee->y; rect[0].width = ee->width; rect[0].height = ee->height; XserverRegion region = XFixesCreateRegion(fl_display, rect, 1); add_damage(region); } void Composite::reparent_notify(const XReparentEvent* re) { if(re->parent == RootWindow(fl_display, fl_screen)) add_window(re->window, 0); else destroy_window(re->window, false, true); } void Composite::circulate_window(const XCirculateEvent* ce) { CWindow* win = find_window(ce->window); if(!win) return; Window new_above; if(ce->place == PlaceOnTop) new_above = (*window_list.begin())->id; else new_above = None; restack_window(win, new_above); clipChanged = true; } CWindow* Composite::find_window(Window id) { if(window_list.size() == 0) return NULL; CWindowListIter it = window_list.begin(), it_end = window_list.end(); while(it != it_end) { if((*it)->id == id) return *it; ++it; } return NULL; } XserverRegion Composite::window_extents(CWindow* win) { XRectangle r; r.x = win->attr.x; r.y = win->attr.y; r.width = win->attr.width + win->attr.border_width * 2; r.height = win->attr.height + win->attr.border_width * 2; if(!(win->window_type == _XA_NET_WM_WINDOW_TYPE_DOCK && excludeDockShadows)) { // TODO: check this in xcompmgr since server shadows are not used, only client one if(win->mode != WIN_MODE_ARGB) { win->shadow_dx = shadowOffsetX; win->shadow_dx = shadowOffsetX; // make shadow if not already made if(!win->shadow) { double opacity = shadowOpacity; if(win->mode == WIN_MODE_TRANS) opacity = opacity * ((double)win->opacity)/((double)OPAQUE); /* win->shadow = shadow_picture(opacity, win->picture_alpha, win->attr.width + win->attr.border_width * 2, win->attr.height + win->attr.border_width * 2, &win->shadow_w, &win->shadow_h); */ } } XRectangle sr; sr.x = win->attr.x + win->shadow_dx; sr.y = win->attr.y + win->shadow_dy; sr.width = win->shadow_w; sr.height = win->shadow_h; if(sr.x < r.x) { r.width = (r.x + r.width) - sr.x; r.x = sr.x; } if(sr.y < r.y) { r.height = (r.y + r.height) - sr.y; r.y = sr.y; } if(sr.x + sr.width > r.x + r.width) r.width = sr.x + sr.width - r.x; if(sr.y + sr.height > r.y + r.height) r.height = sr.y + sr.height - r.y; } return XFixesCreateRegion(fl_display, &r, 1); } void Composite::paint_root(void) { if(!rootTile) rootTile = root_tile(); XRenderComposite(fl_display, PictOpSrc, rootTile, None, rootBuffer, 0, 0, 0, 0, 0, 0, DisplayWidth(fl_display, fl_screen), DisplayHeight(fl_display, fl_screen)); } void Composite::paint_all(XserverRegion region) { //EDEBUG(ESTRLOC ": PAINT %i windows\n", window_list.size()); // if region is None repaint all screen if(!region) { XRectangle r; r.x = 0; r.y = 0; // TODO: DisplayWidth/DisplayHeight should be calculated in init() r.width = DisplayWidth(fl_display, fl_screen); r.height = DisplayHeight(fl_display, fl_screen); region = XFixesCreateRegion(fl_display, &r, 1); } if(!rootBuffer) { Pixmap root_pix = XCreatePixmap(fl_display, RootWindow(fl_display, fl_screen), DisplayWidth(fl_display, fl_screen), DisplayHeight(fl_display, fl_screen), DefaultDepth(fl_display, fl_screen)); rootBuffer = XRenderCreatePicture(fl_display, root_pix, XRenderFindVisualFormat(fl_display, DefaultVisual(fl_display, fl_screen)), 0, 0); XFreePixmap(fl_display, root_pix); } XFixesSetPictureClipRegion(fl_display, rootPicture, 0, 0, region); // paint from top to bottom CWindowListIter it = window_list.begin(), it_end = window_list.end(); for(; it != it_end; ++it) { CWindow* win = *it; // never painted, ignore it if(!win->damaged) continue; // invisible, ignore it if(win->attr.x + win->attr.width < 1 || win->attr.y + win->attr.height < 1 || win->attr.x >= DisplayWidth(fl_display, fl_screen) || win->attr.y >= DisplayHeight(fl_display, fl_screen)) { continue; } if(!win->picture) { Drawable draw = win->id; #ifdef HAVE_NAME_WINDOW_PIXMAP if(hasNamePixmap && !win->pixmap) win->pixmap = XCompositeNameWindowPixmap(fl_display, win->id); if(win->pixmap) draw = win->pixmap; #endif XRenderPictFormat* format = XRenderFindVisualFormat(fl_display, win->attr.visual); XRenderPictureAttributes pa; pa.subwindow_mode = IncludeInferiors; win->picture = XRenderCreatePicture(fl_display, draw, format, CPSubwindowMode, &pa); } if(clipChanged) { if(win->border_size) { set_ignore(NextRequest(fl_display)); XFixesDestroyRegion(fl_display, win->border_size); win->border_size = None; } if(win->extents) { XFixesDestroyRegion(fl_display, win->extents); win->extents = None; } if(win->border_clip) { XFixesDestroyRegion(fl_display, win->border_clip); win->border_clip = None; } } if(!win->border_size) win->border_size = set_border_size(win); if(!win->extents) win->extents = window_extents(win); // EDEBUG("mode :%i\n", win->mode); if(win->mode == WIN_MODE_SOLID) { int x, y, wid, hei; #if HAVE_NAME_WINDOW_PIXMAP x = win->attr.x; y = win->attr.y; wid = win->attr.width + win->attr.border_width * 2; hei = win->attr.height + win->attr.border_width * 2; #else x = win->attr.x + win->attr.border_width; y = win->attr.y + win->attr.border_width; wid = win->attr.width; hei = win->attr.height; #endif XFixesSetPictureClipRegion(fl_display, rootBuffer, 0, 0, region); set_ignore(NextRequest(fl_display)); XFixesSubtractRegion(fl_display, region, region, win->border_size); set_ignore(NextRequest(fl_display)); // EDEBUG(ESTRLOC ": Composite on %i x:%i y:%i w:%i h:%i\n", win->id, x, y, wid, hei); XRenderComposite(fl_display, PictOpSrc, win->picture, None, rootBuffer, 0, 0, 0, 0, x, y, wid, hei); } if(!win->border_clip) { win->border_clip = XFixesCreateRegion(fl_display, 0, 0); XFixesCopyRegion(fl_display, win->border_clip, region); } } XFixesSetPictureClipRegion(fl_display, rootBuffer, 0, 0, region); paint_root(); /* * paint from bottom to top * use counter since checking only iterator will not pick up first element */ it = window_list.end(); it_end = window_list.begin(); --it; // decrease iterator so it pointing to the last element for(unsigned int i = 0; i < window_list.size(); i++) { CWindow* win = *it; XFixesSetPictureClipRegion(fl_display, rootBuffer, 0, 0, win->border_clip); // TODO: server shadows // go and directly try to draw client shadows // don't draw them on desktop #if 0 if(win->shadow && win->window_type != _XA_NET_WM_WINDOW_TYPE_DESKTOP) { XRenderComposite(fl_display, PictOpOver, blackPicture, win->shadow, rootBuffer, 0, 0, 0, 0, win->attr.x + win->shadow_dx, win->attr.y + win->shadow_dy, win->shadow_w, win->shadow_h); } #endif if(win->opacity != OPAQUE && !win->picture_alpha) win->picture_alpha = solid_picture(false, (double)win->opacity / OPAQUE, 0, 0, 0); // TODO: check this, xcompmgr have the same code for WIN_MODE_TRANS and WIN_MODE_ARGB if(win->mode == WIN_MODE_TRANS || win->mode == WIN_MODE_ARGB) { int x, y, wid, hei; #if HAVE_NAME_WINDOW_PIXMAP x = win->attr.x; y = win->attr.y; wid = win->attr.width + win->attr.border_width * 2; hei = win->attr.height + win->attr.border_width * 2; #else x = win->attr.x + win->attr.border_width; y = win->attr.y + win->attr.border_width; wid = win->attr.width; hei = win->attr.height; #endif set_ignore(NextRequest(fl_display)); XRenderComposite(fl_display, PictOpOver, win->picture, win->picture_alpha, rootBuffer, 0, 0, 0, 0, x, y, wid, hei); #if 0 XRenderComposite(fl_display, PictOpOver, win->picture, win->picture_alpha, rootBuffer, 10, 0, 0, 0, x+10, y, wid-10, 100); // solid XRenderComposite(fl_display, PictOpSrc, win->picture, None, rootBuffer, 10, 100, 0, 0, x+10, y+100, wid-10, hei-100); XRenderComposite(fl_display, PictOpSrc, win->picture, None, rootBuffer, 0, 0, 0, 0, x, y, 10, hei); #endif } // XXX: a lot of errors here ? if(win->border_clip != None) { XFixesDestroyRegion(fl_display, win->border_clip); win->border_clip = None; } // this will assure we catch first element too if(it != it_end) --it; } XFixesDestroyRegion(fl_display, region); if(rootBuffer != rootPicture) { XFixesSetPictureClipRegion(fl_display, rootBuffer, 0, 0, None); XRenderComposite(fl_display, PictOpSrc, rootBuffer, None, rootPicture, 0, 0, 0, 0, 0, 0, DisplayWidth(fl_display, fl_screen), DisplayHeight(fl_display, fl_screen)); } } void Composite::damage_window(XDamageNotifyEvent* de) { CWindow* win = find_window(de->drawable); if(!win) return; #if 0 // XXX: addon while(XPending(fl_display)) { XEvent ev; if(XPeekEvent(fl_display, &ev) && ev.type == (damage_event + XDamageNotify) && ev.xany.window == win->id) { XNextEvent(fl_display, &ev); repair_window(win); EDEBUG("XXXXXXXXXXXXX\n"); continue; } break; } #endif repair_window(win); } void Composite::repair_window(CWindow* win) { XserverRegion parts; if(!win->damaged) { parts = window_extents(win); set_ignore(NextRequest(fl_display)); XDamageSubtract(fl_display, win->damage, None, None); } else { parts = XFixesCreateRegion(fl_display, 0, 0); set_ignore(NextRequest(fl_display)); XDamageSubtract(fl_display, win->damage, None, parts); XFixesTranslateRegion(fl_display, parts, win->attr.x + win->attr.border_width, win->attr.y + win->attr.border_width); // TODO: server shadows } if(parts == None) EDEBUG("parts == None\n"); add_damage(parts); win->damaged = 1; } void Composite::destroy_window(Window id, bool gone, bool fade) { #if HAVE_NAME_WINDOW_PIXMAP CWindow* win = find_window(id); if(win && win->pixmap && fade && fadeWindows) { // TODO: fading support } else #endif finish_destroy_window(id, gone); } void Composite::finish_destroy_window(Window id, bool gone) { CWindowListIter it = window_list.begin(), it_end = window_list.end(); while(it != it_end) { if((*it)->id == id) { CWindow* win = *it; if(!gone) finish_unmap_window(win); if(win->picture) { set_ignore(NextRequest(fl_display)); XRenderFreePicture(fl_display, win->picture); win->picture = None; } if(win->picture_alpha) { XRenderFreePicture(fl_display, win->picture_alpha); win->picture_alpha = None; } if(win->picture_shadow) { XRenderFreePicture(fl_display, win->picture_shadow); win->picture_shadow = None; } if(win->damage != None) { set_ignore(NextRequest(fl_display)); XDamageDestroy(fl_display, win->damage); win->damage = None; } // TODO: fading support window_list.erase(it); delete win; break; } ++it; } } void Composite::update_screen(void) { if(allDamage != None) { paint_all(allDamage); allDamage = None; clipChanged = false; } } int Composite::handle_xevents(const XEvent* xev) { if(another_is_running || !manual_redirect) return 0; switch(xev->type) { case CreateNotify: EDEBUG(ESTRLOC ": CreateNotify from composite\n"); add_window(xev->xcreatewindow.window, 0); break; case ConfigureNotify: EDEBUG(ESTRLOC ": ConfigureNotify from composite\n"); configure_window(&xev->xconfigure); break; case DestroyNotify: EDEBUG(ESTRLOC ": DestroyNotify from composite\n"); destroy_window(xev->xdestroywindow.window, true, true); break; case MapNotify: EDEBUG(ESTRLOC ": MapNotify from composite\n"); map_window(xev->xmap.window, xev->xmap.serial, true); break; case UnmapNotify: EDEBUG(ESTRLOC ": UnmapNotify from composite\n"); unmap_window(xev->xunmap.window, true); break; case ReparentNotify: EDEBUG(ESTRLOC ": ReparentNotify from composite\n"); reparent_notify(&xev->xreparent); break; case CirculateNotify: EDEBUG(ESTRLOC ": CirculateNotify from composite\n"); circulate_window(&xev->xcirculate); break; case Expose: EDEBUG(ESTRLOC ": Expose from composite\n"); expose_event(&xev->xexpose); break; case PropertyNotify: EDEBUG(ESTRLOC ": PropertyNotify from composite\n"); property_notify(&xev->xproperty); break; default: if(xev->type == (damage_event + XDamageNotify)) { //EDEBUG("---------> %i <---------\n", damage_event + XDamageNotify); damage_window((XDamageNotifyEvent*)xev); return 0; } break; } return 1; }