import gobject
import gtk
import cairo

class Blinker(gtk.DrawingArea):
    def __init__(self):
        gtk.DrawingArea.__init__(self)
        gobject.timeout_add(500, self.redraw)
        self.color = 0

    def do_size_request(self, requisition):
        requisition.width = 100
        requisition.height = 100

    def redraw(self):
        if not self.flags() & gtk.MAPPED:
            return
        print "<BLINK>!"
        cr = self.window.cairo_create()
        self.color = not self.color
        cr.set_source_rgb(1, self.color, 0)
        cr.paint()
        return True
gobject.type_register(Blinker)

class ExposeForwarder(gtk.EventBox):
    def __init__(self, forward_to):
        self.forward_to = forward_to
        gtk.EventBox.__init__(self)

    def do_expose_event(self, event):
        print "FORWARD"
        self.forward_to(self.child.window)
gobject.type_register(ExposeForwarder)

class BlinkWatcher(gtk.EventBox):
    def __init__(self, alpha):
        self.alpha = alpha
        gtk.EventBox.__init__(self)

    def do_realize(self):
        gtk.EventBox.do_realize(self)
        self._other_window = gtk.gdk.Window(self.window,
                                            width=self.allocation.width,
                                            height=self.allocation.height,
                                            window_type=gtk.gdk.WINDOW_CHILD,
                                            wclass=gtk.gdk.INPUT_OUTPUT,
                                            event_mask=self.get_events())
        self._other_window.show()

    def do_size_allocate(self, allocation):
        gtk.EventBox.do_size_allocate(self, allocation)
        if self.flags() & gtk.REALIZED:
            self._other_window.resize(allocation[2], allocation[3])

    def composite_was_damaged(self, composite_window):
        if not self.flags() & gtk.MAPPED:
            return

        print "DAMAGE!"
        cr = self._other_window.cairo_create()
        cr.set_operator(cairo.OPERATOR_SOURCE)
        cr.set_source_rgb(1, 1, 1)
        cr.paint()
        cr.set_operator(cairo.OPERATOR_OVER)
        cr.set_source_surface(composite_window.cairo_create().get_target(),
                              0, 0)
        cr.paint_with_alpha(self.alpha)
        #cr.paint()
gobject.type_register(BlinkWatcher)

##########################################################

vbox = gtk.VBox()
win = gtk.Window()
win.add(vbox)
win.show_all()

def add_watcher(vbox, intermediate, alpha):
    composited = Blinker()
    if intermediate:
        eventbox = gtk.EventBox()
        eventbox.add(composited)
        composited = eventbox
    watcher = BlinkWatcher(alpha)
    forwarder = ExposeForwarder(watcher.composite_was_damaged)
    watcher.add(forwarder)
    forwarder.add(composited)
    vbox.add(watcher)
    watcher.show_all()
    watcher._other_window.raise_()
    composited.window.set_composited(True)
    return watcher

# VBox goes:
# 1) unadorned blinker widget (uncomposited)
vbox.add(Blinker())
vbox.add(gtk.HSeparator())
# 2) blinker directly composited with paint_with_alpha(0.99)
add_watcher(vbox, False, 0.99)
vbox.add(gtk.HSeparator())
# 3) blinker directly composited with paint_with_alpha(1)
add_watcher(vbox, False, 1)
vbox.add(gtk.HSeparator())
# 4) blinker inside an eventbox that itself is then composited with
#    paint_with_alpha(0.99)
add_watcher(vbox, True, 0.99)
vbox.add(gtk.HSeparator())
# 5) blinker inside an eventbox that itself is then composited with
#    paint_with_alpha(1)
add_watcher(vbox, True, 1)

win.show_all()
gtk.main()
