ede/evoke/Composite.cpp
2008-01-14 12:53:38 +00:00

1363 lines
34 KiB
C++

/*
* $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 <edelib/Debug.h>
#include <FL/x.h>
#include <FL/Fl.h>
#include <string.h> // memcpy
#include <stdlib.h> // malloc, realloc
#include <X11/extensions/Xcomposite.h>
#include <X11/extensions/Xrender.h>
#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;
}