Skip to content

Instantly share code, notes, and snippets.

@nzjrs
Created January 19, 2010 00:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nzjrs/280520 to your computer and use it in GitHub Desktop.
Save nzjrs/280520 to your computer and use it in GitHub Desktop.
PyGtk client side windows demo
#!/usr/bin/env python
# Gtk+ client-side-windows demo in python
# John Stowers
import gobject
import cairo
import gtk
import gtk.gdk as gdk
#hacks for using functions not exposed in this pygtk version
import ctypes
cgdk = ctypes.CDLL("libgdk-x11-2.0.so")
class _PyGObject_Functions(ctypes.Structure):
_fields_ = [
('register_class',
ctypes.PYFUNCTYPE(ctypes.c_void_p, ctypes.c_char_p,
ctypes.c_int, ctypes.py_object,
ctypes.py_object)),
('register_wrapper',
ctypes.PYFUNCTYPE(ctypes.c_void_p, ctypes.py_object)),
('register_sinkfunc',
ctypes.PYFUNCTYPE(ctypes.py_object, ctypes.c_void_p)),
('lookupclass',
ctypes.PYFUNCTYPE(ctypes.py_object, ctypes.c_int)),
('newgobj',
ctypes.PYFUNCTYPE(ctypes.py_object, ctypes.c_void_p)),
]
class PyGObjectCPAI(object):
def __init__(self):
addr = ctypes.pythonapi.PyCObject_AsVoidPtr(
ctypes.py_object(gobject._PyGObject_API))
self._api = _PyGObject_Functions.from_address(addr)
def pygobject_new(self, addr):
return self._api.newgobj(addr)
cgobject = PyGObjectCPAI()
def gdk_offscreen_window_set_embedder(window, embedder):
cgdk.gdk_offscreen_window_set_embedder(hash(window), hash(embedder))
def gdk_offscreen_window_get_pixmap(window):
pm = cgdk.gdk_offscreen_window_get_pixmap(hash(window))
return cgobject.pygobject_new(ctypes.c_void_p(pm))
class GtkMirrorBin(gtk.Bin):
__gsignals__ = {
"damage_event" : "override"
}
def __init__(self):
# gobject.GObject.__init__(self)
gtk.Bin.__init__(self)
self.child = None
self.offscreen_window = None
self.unset_flags(gtk.NO_WINDOW)
def _to_child(self, widget_x, widget_y):
return widget_x, widget_y
def _to_parent(self, offscreen_x, offscreen_y):
return offscreen_x, offscreen_y
def _pick_offscreen_child(self, offscreen_window, widget_x, widget_y):
print "pick_child"
if self.child and self.child.flags() & gtk.VISIBLE:
x,y = self._to_child(widget_x, widget_y)
ca = self.child.allocation
if (x >= 0 and x < ca.width and y >= 0 and y < ca.height):
return self.offscreen_window
return None
def _offscreen_window_to_parent(self, offscreen_window, offscreen_x, offscreen_y, parent_x, parent_y):
print "to_parent"
x,y = self._to_parent(offscreen_x, offscreen_y)
#FIXME: parent_x and parent_y are gpointers...
px = ctypes.c_void_p(hash(parent_x))
px.contents = x
py = ctypes.c_void_p(hash(parent_y))
py.contents = y
def _offscreen_window_from_parent(self, parent_window, parent_x, parent_y, offscreen_x, offscreen_y):
print "to_child"
x,y = self._to_child(parent_x, parent_y)
#FIXME: offscreen_x and offscreen_y are gpointers...
ox = ctypes.c_void_p(hash(offscreen_x))
ox.contents = x
oy = ctypes.c_void_p(hash(offscreen_y))
oy.contents = y
def do_realize(self):
print "do_realize"
self.set_flags(gtk.REALIZED)
border_width = self.border_width
w = self.allocation.width - 2*border_width
h = self.allocation.height - 2*border_width
self.window = gdk.Window(
self.get_parent_window(),
x=self.allocation.x + border_width,
y=self.allocation.y + border_width,
width=w,
height=h,
window_type=gdk.WINDOW_CHILD,
event_mask=self.get_events()
| gdk.EXPOSURE_MASK
| gdk.POINTER_MOTION_MASK
| gdk.BUTTON_PRESS_MASK
| gdk.BUTTON_RELEASE_MASK
| gdk.SCROLL_MASK
| gdk.ENTER_NOTIFY_MASK
| gdk.LEAVE_NOTIFY_MASK,
visual=self.get_visual(),
colormap=self.get_colormap(),
wclass=gdk.INPUT_OUTPUT)
self.window.set_user_data(self)
self.window.connect("pick-embedded-child", self._pick_offscreen_child)
if self.child and self.child.flags() & gtk.VISIBLE:
w = self.child.allocation.width
h = self.child.allocation.height
self.offscreen_window = gdk.Window(
self.get_root_window(),
x=self.allocation.x + border_width,
y=self.allocation.y + border_width,
width=w,
height=h,
window_type=gdk.WINDOW_OFFSCREEN,
event_mask=self.get_events()
| gdk.EXPOSURE_MASK
| gdk.POINTER_MOTION_MASK
| gdk.BUTTON_PRESS_MASK
| gdk.BUTTON_RELEASE_MASK
| gdk.SCROLL_MASK
| gdk.ENTER_NOTIFY_MASK
| gdk.LEAVE_NOTIFY_MASK,
visual=self.get_visual(),
colormap=self.get_colormap(),
wclass=gdk.INPUT_OUTPUT)
self.offscreen_window.set_user_data(self)
if self.child:
self.child.set_parent_window(self.offscreen_window)
# self.offscreen_window.set_embedder(self.window)
gdk_offscreen_window_set_embedder(self.offscreen_window, self.window)
self.offscreen_window.connect("to-embedder", self._offscreen_window_to_parent)
self.offscreen_window.connect("from-embedder", self._offscreen_window_from_parent)
self.style.attach(self.window)
self.style.set_background(self.window, gtk.STATE_NORMAL)
self.style.set_background(self.offscreen_window, gtk.STATE_NORMAL)
self.offscreen_window.show()
def do_unrealize(self):
self.offscreen_window.set_user_data(None)
self.offscreen_window = None
def do_add(self, widget):
print "do_add"
if not self.child:
widget.set_parent_window(self.offscreen_window)
widget.set_parent(self)
self.child = widget
else:
print "GtkMirrorBind cannot have more than one child"
def do_remove(self, widget):
print "do_remove"
was_visible = widget.flags() & gtk.VISIBLE
if self.child == widget:
widget.unparent()
self.child = None
if was_visible and (self.flags() & gtk.VISIBLE):
self.queue_resize()
def do_forall(self, internal, callback, data):
print "do_forall"
if self.child:
callback(self.child, data)
def do_size_request(self, r):
print "do_size_request"
cw, ch = 0,0;
if self.child and (self.child.flags() & gtk.VISIBLE):
cw, ch = self.child.size_request()
r.width = self.border_width * 2 + cw + 10
r.height = self.border_width * 2 + ch * 2 + 10
def do_child_type(self):
print "do_child_type"
#FIXME: This never seems to get called...
if self.child:
return None
return gtk.Widget.__gtype__
def do_size_allocate(self, allocation):
print "do_size_allocate"
self.allocation = allocation
border_width = self.border_width
w = self.allocation.width - 2*border_width
h = self.allocation.height - 2*border_width
if self.flags() & gtk.REALIZED:
self.window.move_resize(
allocation.x + border_width,
allocation.y + border_width,
w,h)
if self.child and self.child.flags() & gtk.VISIBLE:
cw, ch = self.child.get_child_requisition()
ca = gtk.gdk.Rectangle(x=0,y=0,width=cw,height=ch)
if self.flags() & gtk.REALIZED:
self.offscreen_window.move_resize(
allocation.x + border_width,
allocation.y + border_width,
cw, ch)
self.child.size_allocate(ca)
def do_damage_event(self, eventexpose):
print "do_damage"
#invalidate the whole window
self.window.invalidate_rect(None, False)
return True
def do_expose_event(self, event):
print "do_expose"
if self.flags() & gtk.VISIBLE and self.flags() & gtk.MAPPED:
if event.window == self.window:
#FIXME: beware pointers...
pm = gdk_offscreen_window_get_pixmap(self.offscreen_window)
w,h = pm.get_size()
print "expose both %dx%d" % (w,h)
cr = self.window.cairo_create()
cr.save()
cr.rectangle(0,0,w,h)
cr.clip()
#paint the offscreen child
cr.set_source_pixmap(pm, 0, 0)
cr.paint()
cr.restore()
matrix = cairo.Matrix(1.0, 0.0, 0.3, 1.0, 0.0, 0.0)
matrix.scale(1.0, -1.0)
matrix.translate(-10, -3 * h - 10)
cr.transform(matrix)
cr.rectangle(0,h,w,h)
cr.clip()
cr.set_source_pixmap(pm, 0, h)
#create linear gradient
mask = cairo.LinearGradient(0.0, h, 0.0, 2*h)
mask.add_color_stop_rgba(0.0, 0.0, 0.0, 0.0, 0.0)
mask.add_color_stop_rgba(0.25, 0.0, 0.0, 0.0, 0.01)
mask.add_color_stop_rgba(0.5, 0.0, 0.0, 0.0, 0.25)
mask.add_color_stop_rgba(0.75, 0.0, 0.0, 0.0, 0.5)
mask.add_color_stop_rgba(1.0, 0.0, 0.0, 0.0, 1.0)
#paint the reflection
cr.mask(mask)
elif event.window == self.offscreen_window:
print "expose offscreen"
self.style.paint_flat_box(
event.window,
gtk.STATE_NORMAL, gtk.SHADOW_NONE,
event.area, self, "blah",
0, 0, -1, -1)
if self.child:
self.propagate_expose(
self.child,
event)
return False
gobject.type_register(GtkMirrorBin)
if __name__ == "__main__":
w = gtk.Window()
w.set_border_width(10)
hb = gtk.HBox(6)
hb.pack_start(gtk.Button(stock=gtk.STOCK_GO_BACK))
hb.pack_start(gtk.Entry())
bin = GtkMirrorBin()
bin.add(hb)
w.add(bin)
w.show_all()
gtk.main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment