/* Style.cc

Copyright © 2003 David Reveman.

This file is part of Waimea.

Waimea 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.

Waimea 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 Waimea; see the file COPYING. If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */

#ifdef    HAVE_CONFIG_H
#  include "../config.h"
#endif // HAVE_CONFIG_H

extern "C" {
#include <X11/Xlib.h>

#ifdef    STDC_HEADERS
#  include <stdlib.h>
#  include <string.h>
#endif // STDC_HEADERS

#ifdef    HAVE_STRINGS_H
#  include <strings.h>
#endif // HAVE_STRINGS_H
    
#ifdef    HAVE_LIBGEN_H
#  include <libgen.h>
#else
    inline char *basename(char *name) {
        int i = strlen(name);
        for (; i >= 0 && name[i] != '/'; i--);
        if (name[i] == '/') i++;
        return &name[i];
    }
#endif // HAVE_LIBGEN_H

#ifdef    LIMITS_H
#  include <limits.h>
#endif // LIMITS_H

#ifdef    HAVE_UNISTD_H
#  include <unistd.h>
#  include <sys/types.h>
#  include <sys/wait.h>
#endif // HAVE_UNISTD_H

#ifdef    HAVE_MATH_H
#  include <math.h>
#endif // HAVE_MATH_H

}

#include "Style.hh"

Style::Style(WaScreen *_ws, char *_name) : RenderGroup(_ws, _name) {
    translucent = shaped = false;
    focusable = false;
    textops = NULL;
    subs = NULL;
    cursor = (Cursor) 0;
    left_spacing = right_spacing = top_spacing = bottom_spacing =
        grid_spacing = 0;
    left_spacing_u = right_spacing_u = top_spacing_u = bottom_spacing_u =
        grid_spacing_u = PXLenghtUnitType;
    _w = RENDER_WIDTH_DEFAULT;
    _h = RENDER_HEIGHT_DEFAULT;
    _wu = _hu = PXLenghtUnitType;
    is_a_style = true;
    shapemask = NULL;
    orientation = VerticalOrientationType;
    serial = 0;
}

Style::~Style(void) {
    if (shapemask) shapemask->unref();
    clear();
}

void Style::clear(void) {
    if (textops) {
        while (! textops->empty()) {
            textops->back()->unref();
            textops->pop_back();
        }
        delete textops;
        textops = NULL;
    }
    
    if (subs) {
        while (! subs->empty()) {
            subs->back()->unref();
            subs->pop_back();
        }
        delete subs;
        subs = NULL;
    }
    
    ((RenderGroup *) this)->clear();
}

void Style::inheritContent(Style *inherit_style) {
    if (inherit_style->subs) {
        list<SubwinInfo *>::iterator it = inherit_style->subs->begin();
        for (; it != inherit_style->subs->end(); it++) {
            if (! subs) subs = new list<SubwinInfo *>;
            subs->push_back((*it)->ref());
        }
    }
    ((RenderGroup *) this)->inheritContent((RenderGroup *) inherit_style);
}

void Style::inheritAttributes(Style *inherit_style) {
    translucent = inherit_style->translucent;
    focusable = inherit_style->focusable;
    shaped = inherit_style->shaped;
    left_spacing = inherit_style->left_spacing;
    right_spacing = inherit_style->right_spacing;
    top_spacing = inherit_style->top_spacing;
    bottom_spacing = inherit_style->bottom_spacing;
    grid_spacing = inherit_style->grid_spacing;
    left_spacing_u = inherit_style->left_spacing_u;
    right_spacing_u = inherit_style->right_spacing_u;
    top_spacing_u = inherit_style->top_spacing_u;
    bottom_spacing_u = inherit_style->bottom_spacing_u;
    grid_spacing_u = inherit_style->grid_spacing_u;
    _w = inherit_style->_w;
    _h = inherit_style->_h;
    _wu = inherit_style->_wu;
    _hu = inherit_style->_hu;
    cursor = inherit_style->cursor;
    orientation = inherit_style->orientation;
}

void Style::applyAttributes(Parser *parser, Tst<char *> *attr) {
    char *value = parser->attrGetString(attr, "inherit", NULL);
    if (value) {
        Style *inherit_style = ws->getStyleNamed(value, false);
        if (inherit_style == this) {
            parser->warning("style %s cannot inherit itself\n", name);
        } else if (inherit_style) {
            inheritAttributes(inherit_style);
        } else {
            if (! parser->attrGetBool(attr, "ignore_missing", false))
                parser->warning("undefined style: %s\n", value);
        }
    }
    
    ((RenderGroup *) this)->applyAttributes(parser, attr);
    shaped = parser->attrGetBool(attr, "alpha_level_shape", shaped);
    translucent = parser->attrGetBool(attr, "translucent", translucent);
    focusable = parser->attrGetBool(attr, "focusable", focusable);
    left_spacing = parser->attrGetLength(attr, "left_spacing", left_spacing,
                                         &left_spacing_u);
    right_spacing = parser->attrGetLength(attr, "right_spacing", right_spacing,
                                          &right_spacing_u);
    top_spacing = parser->attrGetLength(attr, "top_spacing", top_spacing,
                                        &top_spacing_u);
    bottom_spacing = parser->attrGetLength(attr, "bottom_spacing",
                                           bottom_spacing, &bottom_spacing_u);
    grid_spacing = parser->attrGetLength(attr, "grid_spacing", grid_spacing,
                                         &grid_spacing_u);
    value = parser->attrGetString(attr, "cursor", NULL);
    if (value) {
        
#ifdef    XCURSOR
        char *cursorfile = smartfile(value, parser->filename, false);
        if (cursorfile)
            cursor = ws->waimea->cursor->getCursor(cursorfile);
        else
#endif // XCURSOR
            
            cursor = ws->waimea->cursor->getCursor(value);
    }
    value = parser->attrGetString(attr, "orientation", NULL);
    if (value && (! strcmp(value, "horizontal")))
        orientation = HorizontalOrientationType;
    
}

DWindowObject::DWindowObject(WaScreen *_ws, Window _id, int _type,
                             WaStringMap *_sm, char *_name) :
    AWindowObject(_ws, _id, _type, _sm, _name) {
    style = NULL;
    sb = NULL;
    hidden = force_texture = force_alpha = force_setbg = true;
    mapRequest = false;

#ifdef    THREAD
    if (__render_thread_count)
        pthread_mutex_init(&__win__render_safe_mutex, NULL);
#endif // THREAD

    memset(styles, 0, sizeof(styles));
    memset(default_styles, 0, sizeof(default_styles));

    for (int i = 0; i < WIN_STATE_LAST; i++) {
        style_buffers[i].style = NULL;
        style_buffers[i].surface = NULL;
        style_buffers[i].text_table = NULL;
        style_buffers[i].x = INT_MAX;
        style_buffers[i].y = INT_MAX;
        style_buffers[i].bg = None;
        style_buffers[i].width = style_buffers[i].height = 0;
    }
}

DWindowObject::~DWindowObject(void) {

    DWIN_RENDER_GET(this);

    RENDER_LIST_LOCK;

    list<DWindowObject *>::iterator it = __render_list.begin();
    while (it != __render_list.end()) {
        if (*it == this) it = __render_list.erase(it);
        else it++;
    }
    
    RENDER_LIST_RELEASE;

    DWIN_RENDER_RELEASE(this);
    
#ifdef    THREAD
    if (__render_thread_count)
        pthread_mutex_destroy(&__win__render_safe_mutex);
#endif // THREAD

    MAPUNREFSECOND(dynamic_groups);
    
    for (int i = 0; i < WIN_STATE_LAST; i++) {
        if (style_buffers[i].style) style_buffers[i].style->unref();
        if (style_buffers[i].surface) style_buffers[i].surface->unref();
        MAPUNREFSECOND(style_buffers[i].cache_table);
        if (style_buffers[i].text_table)
            MAPPTRCLEAR(style_buffers[i].text_table);
        MAPUNREFSECOND(style_buffers[i].dynamic_groups);
    }

    for (int i = 0; i < WIN_STATE_LAST; i++) {
        if (styles[i]) styles[i]->unref();
        if (default_styles[i]) default_styles[i]->unref();
    }

    if (style) style->unref();
}

void DWindowObject::resetStyle(void) {
    if (! ws) return;
    
    list<StyleRegex *> *ls = NULL;

    destroySubwindows();

    force_texture = true;

    for (int i = 0; i < WIN_STATE_LAST; i++) {
        if (style_buffers[i].style) style_buffers[i].style->unref();
        style_buffers[i].style = NULL;
        if (style_buffers[i].surface) style_buffers[i].surface->unref();
        style_buffers[i].surface = NULL;
        MAPUNREFSECOND(style_buffers[i].cache_table);
        if (style_buffers[i].text_table)
            MAPPTRCLEAR(style_buffers[i].text_table);
        style_buffers[i].text_table = NULL;
        style_buffers[i].x = INT_MAX;
        style_buffers[i].y = INT_MAX;
        style_buffers[i].bg = None;
        style_buffers[i].width = style_buffers[i].height = 0;
        MAPUNREFSECOND(style_buffers[i].dynamic_groups);
    }    

    for (int i = 0; i < WIN_STATE_LAST; i++) {
        if (styles[i]) styles[i]->unref();
        if (default_styles[i]) default_styles[i]->unref();
    }

    memset(styles, 0, sizeof(styles));
    memset(default_styles, 0, sizeof(default_styles));
 
    switch (decor_root->type) {
        case WindowFrameType:
            ls = &ws->window_styles;
            break;
        case DockHandlerType:
            ls = &ws->dockappholder_styles;
            break;
        case RootType:
            ls = &ws->root_styles;
            break;
        case MenuType:
        case MenuItemType:
            ls = &ws->menu_styles;
            break;
    }    
    
    if (style) style->unref();
    style = NULL;
    sb = NULL;
    
    if (ls) {
        list<StyleRegex *>::iterator it = ls->begin();
        for (; it != ls->end(); it++) {
            if ((styles[WIN_STATE_PASSIVE] =
                 (*it)->match(ids, WIN_STATE_PASSIVE, window_name))) {
                break;
            }
        }
        for (it = ls->begin(); it != ls->end(); it++) {
            if ((styles[WIN_STATE_ACTIVE] =
                 (*it)->match(ids, WIN_STATE_ACTIVE, window_name))) {
                break;
            }
        }
        for (it = ls->begin(); it != ls->end(); it++) {
            if ((styles[WIN_STATE_HOVER] =
                 (*it)->match(ids, WIN_STATE_HOVER, window_name))) {
                break;
            }
        }
        for (it = ls->begin(); it != ls->end(); it++) {
            if ((styles[WIN_STATE_PRESSED] =
                 (*it)->match(ids, WIN_STATE_PRESSED, window_name))) {
                break;
            }
        }
    }

    if (! styles[WIN_STATE_PASSIVE])
        styles[WIN_STATE_PASSIVE] = (Style *) (*ws->styles.begin())->ref();
    
    for (int i = 0; i < WIN_STATE_LAST; i++) {
        if (styles[i]) default_styles[i] = (Style *) styles[i]->ref();
    }

    sb = &style_buffers[WIN_STATE_PASSIVE];
    style = (Style *) styles[WIN_STATE_PASSIVE]->ref();
}

void DWindowObject::updateSubwindows(void) {
    list<SubwinInfo *>::iterator subinfo_it;
    map<int, SubWindowObject *>::iterator sub_it;

    if (type == SubwindowType)
        if (((SubWindowObject *) this)->subwin_level > MAX_SUBWIN_LEVEL)
            return;
    
    if (sb->style->subs) {
        subinfo_it = sb->style->subs->begin();
        for (; subinfo_it != sb->style->subs->end(); subinfo_it++) {
            if ((subs.find((*subinfo_it)->ident) == subs.end())) {
                SubWindowObject *sw;
                bool ancestor_failure = false;

                DWindowObject *ancestor = this;
                while (ancestor->type == SubwindowType) {
                    if (((SubWindowObject *) ancestor)->sub_info->ident ==
                        (*subinfo_it)->ident) {
                        ancestor_failure = true;
                        ws->showWarningMessage(
                            __FUNCTION__, "could not create sub-window, an "
                            "ancestor had the same sub-window ID.");
                        break;
                    }
                    ancestor = ((SubWindowObject *) ancestor)->parent;
                }

                if (! ancestor_failure) {
                    if (type == WindowFrameType)
                        sw = new FrameSubWindowObject(this, *subinfo_it);
                    else
                        sw = new SubWindowObject(this, *subinfo_it);
                    
                    subs.insert(make_pair((*subinfo_it)->ident, sw));

                    sw->commonStyleUpdate();

                    if (sw->decor_root->type == WindowFrameType) {
                        EventDetail ed;
                        ed.x = ed.y = INT_MAX;
                        ed.x11mod = ed.wamod = 0;
                        ed.type = WindowStateChangeNotify;
                        ws->waimea->eh->evAct(NULL, sw->id, &ed);
                    }
                }
            }
        }
    }
        
    sub_it = subs.begin();
    for (; sub_it != subs.end(); sub_it++) {
        SubWindowObject *sw = (*sub_it).second;
        bool shown = false;
        if (sb->style->subs) {
            subinfo_it = sb->style->subs->begin();
            for (; subinfo_it != sb->style->subs->end(); subinfo_it++) {
                if ((*sub_it).first == (*subinfo_it)->ident) {
                    sw->hidden = false;
                    shown = true;

                    MAPUNREFSECOND(sw->dynamic_groups);
                    map<int, RenderGroup *>::iterator dit =
                        dynamic_groups.begin();
                    for (; dit != dynamic_groups.end(); dit++) {
                        sw->dynamic_groups.insert(
                            make_pair((*dit).first,
                                      (RenderGroup *) ((*dit).second)->ref()));
                    }
                    
                    sw->showSubwindow();
                    break;
                }
            }
        }
        if ((! shown) && (! sw->hidden)) {
            sw->hidden = true;
            sw->hideSubwindow();
        }
    }

    if (type == WindowFrameType && sb->style->subs) {
        XLowerWindow(ws->display, ((WaFrameWindow *) this)->wa->id);
    }
}

void DWindowObject::destroySubwindows(void) {
    while (! subs.empty()) {
        ((*subs.begin()).second)->unref();
        subs.erase(subs.begin());
    }
}

void DWindowObject::initTextOpTable(void) {
    if (type == WindowFrameType) return;
        
    if (sb->text_table)
        MAPPTRCLEAR(sb->text_table);
    
    sb->text_table = NULL;
    if (sb->style->textops) {
        list<RenderOpText *>::iterator tit =
            sb->style->textops->begin();
        for (; tit != sb->style->textops->end(); tit++) {
            if (! (*tit)->is_static) {
                if (! sb->text_table)
                    sb->text_table =
                        new map<RenderOpText *, char *>;
                sb->text_table->insert(
                    make_pair(*tit, WA_STRDUP("")));
            }
        }
    }
}

void DWindowObject::setDynamicGroup(int type, RenderGroup *group) {
    map<int, RenderGroup *>::iterator dit =
        dynamic_groups.find(type);
    if (dit != dynamic_groups.end()) {
        (*dit).second->unref();
        dynamic_groups.erase(dit);
    }
    
    if (group)
        dynamic_groups.insert(make_pair(type, (RenderGroup *) group->ref()));
    
    map<int, SubWindowObject *>::iterator it = subs.begin();
    for (; it != subs.end(); it++)
        ((*it).second)->setDynamicGroup(type, group);
}

void DWindowObject::commonStyleUpdate(void) {
    int current_window_state =
        STATE_FROM_MASK_AND_LIST(window_state_mask, styles);
    
    if (style) style->unref();
    style = (Style *) styles[current_window_state]->ref();
    
    if (sb->style != style ||
        (sb->style && sb->style->serial != sb->serial)) {
        sb = &style_buffers[current_window_state];

        force_setbg = true;
        
        if (sb->style == style) {
            force_alpha = true;
            
            if (! sb->surface)
                force_texture = true;
                    
            if (sb->serial != style->serial) {
                force_texture = true;
                if (sb->text_table) {
                    MAPPTRCLEAR(sb->text_table);
                    sb->text_table = NULL;
                }
                
                MAPUNREFSECOND(sb->cache_table);
                
                initTextOpTable();
            }
        } else {
            if (sb->style) sb->style->unref();
            sb->style = (Style *) style->ref();
            if (sb->surface) sb->surface->unref();
            sb->surface = NULL;

            MAPUNREFSECOND(sb->dynamic_groups);
            
            sb->bg = None;
            sb->width = sb->height = 0;
            if (sb->text_table)
                MAPPTRCLEAR(sb->text_table);
            sb->text_table = NULL;
            sb->x = INT_MAX;
            sb->y = INT_MAX;
            
            force_texture = true;
            
            MAPUNREFSECOND(sb->cache_table);
            
            initTextOpTable();
        }

        if (sb->style->cursor)
            XDefineCursor(ws->display, id, sb->style->cursor);
        
        updateSubwindows();
        
        sb->serial = sb->style->serial;
    }
}

void DWindowObject::styleUpdate(bool, bool) {
    pushRenderEvent();
}

void DWindowObject::renderWindow(cairo_t *cr, bool from_bgparent) {
    int x, y;
    unsigned int w, h;
    bool render_texture = false;
    bool render_alpha = false;
    bool update_shape = false;
    Pixmap pixmap = (Pixmap) 0;
    Pixmap bitmap = (Pixmap) 0;

    DWIN_RENDER_SAFE_LOCK(this);

    if (force_texture) {
        render_texture = true;
        force_texture = false;
        force_alpha = false;
    } else if (force_alpha) {
        render_alpha = true;
        force_alpha = false;
    }

    startRender();

    currentPositionAndSize(&x, &y, &w, &h);

    bool pos_change, size_change;
    if (x != sb->x || y != sb->y) pos_change = true;
    else pos_change = false;
    if (w != sb->width || h != sb->height) size_change = true;
    else size_change = false;

    if (size_change)
        render_texture = true;
    else if (pos_change)
        render_alpha = true;
    
    sb->x = x;
    sb->y = y;
    sb->width = w;
    sb->height = h;
    
    evalWhatToRender(pos_change, size_change,
                     &render_texture, &render_alpha, &update_shape);
    
    commonEvalWhatToRender(pos_change, size_change,
                           &render_texture, &render_alpha, &update_shape);
    
    if (type == WindowFrameType) {
        render_texture = false;
        render_alpha = false;
        update_shape = false;
    }

    if (render_texture) {
        render_alpha = true;
        update_shape = true;
    }

    bool alpha = (sb->style->translucent && type != RootType);
    
    bool shape = false;
    
#ifdef    SHAPE
    shape = (sb->style->shaped && type != RootType);
#endif // SHAPE

    /* ARGB visual */
    if (ws->screen_depth == 32) {
      if (alpha) {
        int p_x, p_y;
        DWindowObject *bgparent;
        WaSurface *bgsurface = getBgInfo (&bgparent, &p_x, &p_y);
        if (bgsurface && bgsurface != ws->bg_surface)
        {
          if (ws->waimea->client_side_rendering) {
            if (!bgsurface->data)
              alpha = false;
          } else {
            if (bgsurface->pixmap == None)
              alpha = false;
          }
        } else
          alpha = false;
      }
      
#ifdef    SHAPE
        shape = false;
#endif // SHAPE

    }
    
    if (render_texture || (alpha && render_alpha)) {
        Pixmap root_pixmap = None;
        unsigned char *root_data = NULL;
        cairo_surface_t *root_surface = NULL;
        
        if (alpha || shape) {
            int p_x, p_y;
            DWindowObject *bgparent;
            WaSurface *bgsurface = getBgInfo(&bgparent, &p_x, &p_y);

            if (! from_bgparent) {
                DWIN_RENDER_SAFE_LOCK(bgparent);
            }
              
              if (ws->waimea->client_side_rendering) {
                root_data = ws->getRootBgImage(
                  (bgsurface)? bgsurface->data: NULL,
                  (bgsurface)? bgsurface->width: 0,
                  (bgsurface)? bgsurface->height: 0,
                  p_x, p_y, w, h);
                root_surface =
                  cairo_image_surface_create_for_data(root_data,
                                                 CAIRO_FORMAT_ARGB32,
                                                 w, h, w * sizeof(WaPixel));
              } else {
                root_pixmap = ws->getRootBgPixmap(
                  (bgsurface)? bgsurface->pixmap: None,
                  (bgsurface)? bgsurface->width: 0,
                  (bgsurface)? bgsurface->height: 0,
                  p_x, p_y, w, h);

                  root_surface =
                    cairo_xlib_surface_create(ws->display, root_pixmap,
                                              ws->visual,
                                              (ws->screen_depth == 32)?
                                              CAIRO_FORMAT_ARGB32:
                                              CAIRO_FORMAT_RGB24,
                                              ws->colormap);
              }

              if (! from_bgparent) {
                DWIN_RENDER_SAFE_RELEASE(bgparent);
              }
        }

        CacheSurface *return_surface;
        sb->style->parent_surface = root_surface;
        sb->style->return_surface = &return_surface;
        sb->style->render(this, cr, NULL, w, h);
        sb->style->return_surface = NULL;
        sb->style->parent_surface = NULL;
        
        if (alpha || shape) {
            cairo_destroy(cr);
            cr = cairo_create(root_surface);
            cairo_set_operator(cr, RENDER_OPERATOR_DEFAULT);
            if (sb->style->xrop_set)
                cairo_set_operator(cr, sb->style->xrop);
            cairo_identity_matrix(cr);
            cairo_set_source_surface(cr, return_surface->crsurface, 0., 0.);
            cairo_rectangle(cr, 0., 0., w, h);
            cairo_clip(cr);
            cairo_paint_with_alpha(cr, sb->style->opacity);
            cairo_reset_clip(cr);
        }
        
        if (shape) {
            cairo_surface_t *alpha_surface;
            unsigned char *shape_data;
            if (ws->waimea->client_side_rendering) {
                shape_data = new unsigned char[w * h * sizeof(WaPixel)];
                memset (shape_data, 0, w * h * sizeof(WaPixel));
                alpha_surface =
                    cairo_image_surface_create_for_data(shape_data,
                                                   CAIRO_FORMAT_ARGB32,
                                                   w, h, w * sizeof(WaPixel));
            } else {
                GC gc;
                XGCValues gcv;
                
                bitmap = XCreatePixmap(ws->display, ws->id, w, h, 1);

                gcv.foreground = 0x00000000;
                gc = XCreateGC(ws->display, bitmap, GCForeground, &gcv);
                XFillRectangle(ws->display, bitmap, gc, 0, 0, w, h);
                XFreeGC(ws->display, gc);
                
                alpha_surface =
                    cairo_xlib_surface_create(ws->display, bitmap,
                                              NULL, CAIRO_FORMAT_A1,
                                              ws->colormap);
            }

            cairo_destroy(cr);
            cr = cairo_create(alpha_surface);
            cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
            cairo_set_source_surface(cr, return_surface->crsurface, 0., 0.);
            cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
            cairo_rectangle(cr, 0, 0, w, h);
            cairo_clip(cr);
            cairo_paint_with_alpha(cr, sb->style->opacity);
            cairo_reset_clip(cr);
            
            if (sb->style->shapemask) {
                cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
                sb->style->shapemask->render(this, cr, alpha_surface, w, h);
            } else {
                cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
                cairo_set_source_surface(cr, return_surface->crsurface, 0., 0.);
                cairo_rectangle(cr, 0.0, 0.0, w, h);
                cairo_clip(cr);
                cairo_paint_with_alpha(cr, sb->style->opacity);
                cairo_reset_clip(cr);
            }
            
            if (ws->waimea->client_side_rendering) {
                /* XXX: can this be done with CAIRO_FORMAT_A1 image data
                   instead? */
                unsigned int *src = (unsigned int *) shape_data;
                unsigned int _x, _y, bit = 0x01;
                unsigned char *shape_map = new unsigned char[(w + 7) / 8 * h];
                unsigned char *dst = shape_map;
                unsigned char value = 0;
                unsigned char src_alpha;
                for (_y = 0; _y < h; _y++) {
                    for (_x = 0; _x < w; _x++) {
                        src_alpha = *src >> 24;
                        if (src_alpha >= 127) value |= bit;
                        if (bit == 0x80) {
                            *dst++ = value;
                            value = 0;
                            bit = 0x01;
                        } else
                            bit <<= 1;
                        
                        src++;
                    }
                    if (bit != 0x01) *dst++ = value;
                    value = 0;
                    bit = 0x01;
                }

                bitmap = XCreateBitmapFromData(ws->display, ws->id,
                                               (char *) shape_map, w, h);
                delete [] shape_data;
                delete [] shape_map;
            }
            
            cairo_surface_destroy(alpha_surface);
        }
            
        CacheSurface *surface;
        if (root_surface) {
            return_surface->unref();
            surface = new CacheSurface(ws->display, root_surface, root_pixmap,
                                       root_data, w, h, &sb->dynamic_groups);
            surface->bitmap = bitmap;
        } else
            surface = return_surface;
        
        if (ws->waimea->client_side_rendering) {
            Visual *visual;
            int depth;
            
            if (type == RootType) {
              XWindowAttributes attr;
                  
              XGetWindowAttributes (ws->display, ws->id, &attr);
              visual = attr.visual;
              depth = attr.depth;
            } else {
              visual = ws->visual;
              depth = ws->screen_depth;
            }
          
            pixmap = XCreatePixmap(ws->display, ws->id, surface->width,
                                   surface->height, depth);
            XImage *xim =
                XCreateImage(ws->display, visual, depth,
                             ZPixmap, 0, (char *) surface->data, w, h, 32, 0);
            
            GC gc = XCreateGC(ws->display, pixmap, 0, NULL);
            XPutImage(ws->display, pixmap, gc, xim, 0, 0, 0, 0, w, h);
            xim->data = NULL;
            XDestroyImage(xim);
            XFreeGC(ws->display, gc);
            surface->pixmap = pixmap;
        }
        
        if (sb->surface) sb->surface->unref();
        sb->surface = surface;
        
        pixmap = sb->surface->pixmap;
    } else if (force_setbg && sb->surface) {
        pixmap = sb->surface->pixmap;
        update_shape = true;
    }

#ifdef    SHAPE
    if (shape && update_shape) {
        if (sb->surface)
            bitmap = sb->surface->bitmap;

        if (bitmap)
           setShape(bitmap);
        
    } else if (update_shape)
       unsetShape();
#endif // SHAPE
    
    if (pixmap) {
        force_setbg = false;

        XSetWindowBackgroundPixmap(ws->display, id, pixmap); 
        XClearWindow(ws->display, id);
    }
    
    map<int, SubWindowObject *>::iterator it = subs.begin();
    for (; it != subs.end(); it++) {
        if (! ((*it).second)->hidden) {
            
            DWindowObject *dwin = (*it).second;
            
            RENDER_LIST_LOCK;

            list<DWindowObject *>::iterator it = __render_list.begin();
            while (it != __render_list.end()) {
                if (*it == dwin) it = __render_list.erase(it);
                else it++;
            }
            
            RENDER_LIST_RELEASE;
            
            dwin->renderWindow(cr, (type == WindowFrameType)? false: true);
        }
    }

    if (mapRequest) {
      if (type != RootType)
        XMapWindow (ws->display, id);
      mapRequest = false;
    }

    DWIN_RENDER_SAFE_RELEASE(this);

    endRender(pixmap);
    
}

void DWindowObject::commonEvalWhatToRender(bool, bool, bool *render_texture,
                                           bool *, bool *) {
    if (sb->style->has_dynamic_op) {
        map<int, RenderGroup *>::iterator sbdit;
        map<int, RenderGroup *>::iterator dit = dynamic_groups.begin();
        for (; dit != dynamic_groups.end(); dit++) {
            sbdit = sb->dynamic_groups.find((*dit).first);
            if (sbdit == sb->dynamic_groups.end() ||
                (*sbdit).second != (*dit).second) {
                if (sbdit != sb->dynamic_groups.end()) {
                    ((*sbdit).second)->unref();
                    sb->dynamic_groups.erase(sbdit);
                }
                sb->dynamic_groups.insert(
                    make_pair((*dit).first,
                              (RenderGroup *) ((*dit).second)->ref()));
                *render_texture = true;
            }
        }

        sbdit = sb->dynamic_groups.begin();
        for (; sbdit != sb->dynamic_groups.end(); sbdit++) {
            dit = dynamic_groups.find((*sbdit).first);
            if (dit == dynamic_groups.end()) {
                ((*sbdit).second)->unref();
                sb->dynamic_groups.erase(sbdit);
                *render_texture = true;
            }
        }
    }

    if (sb->serial != sb->style->serial) {
        *render_texture = true;
        initTextOpTable();
    }

    if (sb->text_table) {
        WaWindow *ww = getWindow();
        MenuItem *mi = getMenuItem();
        map<RenderOpText *, char *>::iterator tit = sb->text_table->begin();
        for (; tit != sb->text_table->end(); tit++) {
            char *newstr = expand(((*tit).first)->utf8, ww, mi);
            if (strcmp((*tit).second, newstr)) {
                *render_texture = true;
                delete [] (*tit).second;
                (*tit).second = newstr;
            } else
                delete [] newstr;
        }
    }
}

#ifdef    SHAPE
void DWindowObject::commonSetShape(Pixmap bitmap) {
    XShapeCombineMask(ws->display, id,
                      ShapeBounding, 0, 0, bitmap, ShapeSet);
}

void DWindowObject::commonUnsetShape(void) {
    XShapeCombineMask(ws->display, id,
                      ShapeBounding, 0, 0, None, ShapeSet);
}

void DWindowObject::setShape(Pixmap bitmap) {
    commonSetShape(bitmap);
}

void DWindowObject::unsetShape(void) {
    commonUnsetShape();
}
#endif // SHAPE

void DWindowObject::pushRenderEvent(void) {
    if (type == RootType && ws->config.external_bg) return;
    if (hidden) return;
    
    RENDER_LIST_LOCK;
    __render_list.push_back(this);
    RENDER_LIST_SIGNAL;
}

SubWindowObject::SubWindowObject(DWindowObject *_parent, SubwinInfo *sinfo) :
    DWindowObject(NULL, (Window) 0, SubwindowType, NULL, NULL) {
    parent = _parent;
    ws = parent->ws;
    sub_info = sinfo;

    if (parent->type == SubwindowType)
        subwin_level = ((SubWindowObject *) parent)->subwin_level + 1;
    else
        subwin_level = 1;
    
    decor_root = parent->decor_root;

    char *name = ws->getSubwindowName(sinfo->ident);

    if (window_name) delete [] window_name;
    if (parent->window_name) {
        window_name = new char[strlen(parent->window_name) +
                              strlen(name) + 2];
        sprintf(window_name, "%s.%s", parent->window_name, name);
    } else
        window_name = WA_STRDUP(name);
    
    XSetWindowAttributes attrib_set;
    int create_mask = CWOverrideRedirect | CWEventMask;

    attrib_set.override_redirect = true;
    attrib_set.event_mask = ButtonPressMask | ButtonReleaseMask |
        EnterWindowMask | LeaveWindowMask;
    id = XCreateWindow(ws->display, parent->id, 0, 0, 1, 1, 0,
                       CopyFromParent, CopyFromParent, CopyFromParent,
                       create_mask, &attrib_set);

    ws->waimea->window_table.insert(make_pair(id, this));

    if (parent->window_state_mask & WIN_STATE_ACTIVE_MASK)
        window_state_mask = WIN_STATE_ACTIVE_MASK;
    else
        window_state_mask = 0L;
    
    if (sinfo->raised && (! (parent->type == RootType)))
        XRaiseWindow(ws->display, id);
    else XLowerWindow(ws->display, id);

    resetActionList(parent->ids->ref());
    resetStyle();
}

SubWindowObject::~SubWindowObject(void) {
    ws->waimea->window_table.erase(id);
    destroySubwindows();
    XDestroyWindow(ws->display, id);
}

void SubWindowObject::showSubwindow(void) {
    XMapWindow(ws->display, id);
}

void SubWindowObject::hideSubwindow(void) {
    XUnmapWindow(ws->display, id);
}

void SubWindowObject::currentPositionAndSize(int *x, int *y,
                                             unsigned int *w,
                                             unsigned int *h) {
    double nx, ny, nw, nh;
    int ox, oy;
    unsigned int ow, oh, ud;
    Window dw;
        
    style->calcPositionAndSize(parent->sb->width, parent->sb->height,
                               ws->hdpi, ws->vdpi, &nx, &ny, &nw, &nh);
    
    *x = WA_ROUND(nx);
    *y = WA_ROUND(ny);
    *w = WA_ROUND_U(nw);
    *h = WA_ROUND_U(nh);
    if (*w == 0) *w = 1;
    if (*h == 0) *h = 1;

    XGetGeometry(ws->display, id, &dw, &ox, &oy, &ow, &oh, &ud, &ud);

    if (ow != *w || oh != *h) {
        XMoveResizeWindow(ws->display, id, *x, *y, *w,* h);
    } else if (ox != *x || oy != *y) {
        XMoveWindow(ws->display, id, *x, *y);
    }
}

void SubWindowObject::evalWhatToRender(bool, bool size_change,
                                       bool *render_texture,
                                       bool *render_alpha,
                                       bool *update_shape) {
    Window dw;
    int x, y;
    XTranslateCoordinates(ws->display, id, ws->id, 0, 0, &x, &y, &dw);

    if ((! size_change) &&
        (x > (int) ws->width || y > (int) ws->height ||
         x + (int) sb->width < 0 || y + (int) sb->height < 0)) {
        *render_texture = false;
        *render_alpha = false;
        *update_shape = false;
        return;
    }

    Pixmap newbg = (parent->sb->surface)? parent->sb->surface->pixmap: None;
    
    if (sb->bg != newbg) {
        *render_alpha = true;
        sb->bg = newbg;
    }
}

WaSurface *SubWindowObject::getBgInfo(DWindowObject **dwo, int *x, int *y) {
    *x = sb->x;
    *y = sb->y;
    *dwo = parent;
    return parent->sb->surface;
}

FrameSubWindowObject::FrameSubWindowObject(DWindowObject *_parent,
                                           SubwinInfo *sinfo) :
    SubWindowObject(_parent, sinfo) {
    last_x = last_y = current_x = current_y = 0;
    frame = (WaFrameWindow *) parent;
    
#ifdef    SHAPE
    shapeinfo = new ShapeInfo(id);
#endif // SHAPE
    
}

FrameSubWindowObject::~FrameSubWindowObject(void) {
    
#ifdef    SHAPE
    if (shapeinfo->in_list)
        frame->removeShapeInfo(shapeinfo);
    delete shapeinfo;
#endif // SHAPE
    
}

void FrameSubWindowObject::showSubwindow(void) {
    
#ifdef    SHAPE
    frame->addShapeInfo(shapeinfo);
    shapeinfo->in_list = true;
#endif // SHAPE
    
    XMapWindow(ws->display, id);
}

void FrameSubWindowObject::hideSubwindow(void) {

#ifdef    SHAPE
    if (shapeinfo->in_list) {
        frame->removeShapeInfo(shapeinfo);
        shapeinfo->in_list = false;
    }
#endif // SHAPE
    
    XUnmapWindow(ws->display, id);
}


void FrameSubWindowObject::currentPositionAndSize(int *x, int *y,
                                                  unsigned int *w,
                                                  unsigned int *h) {
    double nx, ny, nw, nh;
    int ox, oy;
    unsigned int ow, oh, ud;
    Window dw;
        
    style->calcPositionAndSize(parent->sb->width, parent->sb->height,
                               ws->hdpi, ws->vdpi, &nx, &ny, &nw, &nh);
    
    current_x = WA_ROUND(nx);
    current_y = WA_ROUND(ny);
    *w = WA_ROUND_U(nw);
    *h = WA_ROUND_U(nh);
    if (*w == 0) *w = 1;
    if (*h == 0) *h = 1;

    XGetGeometry(ws->display, id, &dw, &ox, &oy, &ow, &oh, &ud, &ud);

    if (ow != *w || oh != *h) {
        XMoveResizeWindow(ws->display, id, current_x, current_y, *w, *h);
    } else if (ox != current_x || oy != current_y) {
        XMoveWindow(ws->display, id, current_x, current_y);
    }

    XTranslateCoordinates(ws->display, id, ws->id, 0, 0, x, y, &dw);
}


void FrameSubWindowObject::evalWhatToRender(bool, bool size_change,
                                            bool *render_texture,
                                            bool *render_alpha,
                                            bool *update_shape) {
    Window dw;
    int x, y;
    XTranslateCoordinates(ws->display, id, ws->id, 0, 0, &x, &y, &dw);

    if ((! size_change) &&
        (x > (int) ws->width || y > (int) ws->height ||
         x + (int) sb->width < 0 || y + (int) sb->height < 0)) {
        *render_texture = false;
        *render_alpha = false;
        *update_shape = false;
        return;
    }

    if (current_x != last_x || current_y != last_y) {
       *update_shape = true;
    }

    Pixmap newbg = (ws->bg_surface)? ws->bg_surface->pixmap: None;

    if (sb->bg != newbg) {
        *render_alpha = true;
        sb->bg = newbg;
    }
}

WaSurface *FrameSubWindowObject::getBgInfo(DWindowObject **dwo,
                                           int *x, int *y) {
    *x = sb->x;
    *y = sb->y;
    *dwo = ws;
    return ws->bg_surface;
}

#ifdef    SHAPE
void FrameSubWindowObject::setShape(Pixmap bitmap) {
    commonSetShape(bitmap);
    shapeinfo->setShapeOffset(current_x, current_y);
    frame->shapeUpdateNotify();
    
    last_x = current_x;
    last_y = current_y;
}

void FrameSubWindowObject::unsetShape(void) {
    commonUnsetShape();
    shapeinfo->setShapeOffset(current_x, current_y);
    frame->shapeUpdateNotify();
    
    last_x = current_x;
    last_y = current_y;
}
#endif // SHAPE

RootWindowObject::RootWindowObject(WaScreen *_ws, Window _id, int _type,
                                   WaStringMap *sm, char *_wname) :
    DWindowObject(_ws, _id, _type, sm, _wname) {
    
#ifdef    THREAD
    if (__render_thread_count) {
        pthread_mutex_init(&__win__render_mutex, NULL);
        pthread_cond_init(&__win__render_cond, NULL);
        __win__render_count = 0;
        __win__render_get_count = 0;
    }
#endif // THREAD

    hidden = false;

    decor_root = this;

    resetStyle();
}

RootWindowObject::~RootWindowObject(void) {

    DWIN_RENDER_GET(this);

    destroySubwindows();
    
#ifdef    THREAD
    if (__render_thread_count) {
        pthread_mutex_destroy(&__win__render_mutex);
        pthread_cond_destroy(&__win__render_cond);
    }
#endif // THREAD

    if (type != RootType)
        XDestroyWindow(ws->display, id);

}

void RootWindowObject::currentPositionAndSize(int *x, int *y,
                                              unsigned int *w,
                                              unsigned int *h) {
    Window dw;
    unsigned int ud;
    XGetGeometry(ws->display, id, &dw, x, y, w, h, &ud, &ud);
    XTranslateCoordinates(ws->display, id, ws->id, 0, 0, x, y, &dw);
}

void RootWindowObject::evalWhatToRender(bool, bool size_change,
                                        bool *render_texture,
                                        bool *render_alpha,
                                        bool *update_shape) {

    if ((! size_change) &&
        (sb->x > (int) ws->width || sb->y > (int) ws->height ||
         sb->x + (int) sb->width < 0 || sb->y + (int) sb->height < 0)) {
        *render_texture = false;
        *render_alpha = false;
        *update_shape = false;
        return;
    }

    Pixmap newbg = (ws->bg_surface)? ws->bg_surface->pixmap: None;

    if (sb->bg != newbg) {
        *render_alpha = true;
        sb->bg = newbg;
    }
}

WaSurface *RootWindowObject::getBgInfo(DWindowObject **dwo, int *x, int *y) {
    *x = sb->x;
    *y = sb->y;
    *dwo = ws;
    return ws->bg_surface;
}


syntax highlighted by Code2HTML, v. 0.9.1