Skip to content

Commit

Permalink
Merge commit 'refs/pull/39/head' of github.com:SimonSapin/cairocffi
Browse files Browse the repository at this point in the history
Conflicts:
	cairocffi/__init__.py
	cairocffi/constants.py
	utils/mkconstants.py
  • Loading branch information
SimonSapin committed Sep 23, 2014
2 parents 3d88008 + 132098d commit 2f43835
Show file tree
Hide file tree
Showing 8 changed files with 305 additions and 3 deletions.
7 changes: 4 additions & 3 deletions .travis.yml
Expand Up @@ -6,6 +6,7 @@ python:
- "3.3"
- "3.4"
- "pypy"
install: "pip install ."
script: "py.test"

install:
- "pip install xcffib pytest-xdist"
- "pip install ."
script: "py.test --boxed"
4 changes: 4 additions & 0 deletions cairocffi/__init__.py
Expand Up @@ -100,6 +100,10 @@ def install_as_pycairo():

from .surfaces import (Surface, ImageSurface, PDFSurface, PSSurface,
SVGSurface, RecordingSurface, Win32PrintingSurface)
try:
from .xcb import XCBSurface
except ImportError:
pass
from .patterns import (Pattern, SolidPattern, SurfacePattern,
Gradient, LinearGradient, RadialGradient)
from .fonts import FontFace, ToyFontFace, ScaledFont, FontOptions
Expand Down
213 changes: 213 additions & 0 deletions cairocffi/test_xcb.py
@@ -0,0 +1,213 @@
# coding: utf8
"""
cairocffi.test_xcb
~~~~~~~~~~~~~~~~~~
Test suite for cairocffi.xcb.
:copyright: Copyright 2014 by Simon Sapin
:license: BSD, see LICENSE for details.
"""

import os
import time
import xcffib
import xcffib.xproto
from xcffib.xproto import ConfigWindow, CW, EventMask, GC

import pytest

from . import Context, XCBSurface

@pytest.fixture
def xcb_conn(request):
"""
Fixture that will setup and take down a xcffib.Connection object running on
a display spawned by xvfb
"""
display = os.environ.get('DISPLAY')
if display:
conn = xcffib.connect(display)
def teardown_conn():
conn.disconnect()
else:
pytest.skip('DISPLAY environment variable not set')

request.addfinalizer(teardown_conn)
return conn

def find_root_visual(conn):
"""Find the xcffib.xproto.VISUALTYPE corresponding to the root visual"""
default_screen = conn.setup.roots[conn.pref_screen]
for i in default_screen.allowed_depths:
for v in i.visuals:
if v.visual_id == default_screen.root_visual:
return v

def create_window(conn, width, height):
"""Creates a window of the given dimensions and returns the XID"""
wid = conn.generate_id()
default_screen = conn.setup.roots[conn.pref_screen]

conn.core.CreateWindow(
default_screen.root_depth, # depth
wid, # id
default_screen.root, # parent
0, 0, width, height, 0, # x, y, w, h, border width
xcffib.xproto.WindowClass.InputOutput, # window class
default_screen.root_visual, # visual
CW.BackPixel | CW.EventMask, # value mask
[ # value list
default_screen.black_pixel,
EventMask.Exposure | EventMask.StructureNotify
]
)

return wid

def create_pixmap(conn, wid, width, height):
"""Creates a window of the given dimensions and returns the XID"""
pixmap = conn.generate_id()
default_screen = conn.setup.roots[conn.pref_screen]

conn.core.CreatePixmap(
default_screen.root_depth, # depth
pixmap, wid, # pixmap id, drawable id (window)
width, height
)

return pixmap

def create_gc(conn):
"""Creates a simple graphics context"""
gc = conn.generate_id()
default_screen = conn.setup.roots[conn.pref_screen]

conn.core.CreateGC(
gc, default_screen.root, # gc id, drawable
GC.Foreground | GC.Background, # value mask
[ # value list
default_screen.black_pixel,
default_screen.white_pixel
]
)

return gc

def test_xcb_pixmap(xcb_conn):
width = 10
height = 10

# create a new window
wid = create_window(xcb_conn, width, height)
# create the pixmap used to draw with cairo
pixmap = create_pixmap(xcb_conn, wid, width, height)
# create graphics context to copy pixmap on window
gc = create_gc(xcb_conn)

# create XCB surface on pixmap
root_visual = find_root_visual(xcb_conn)
surface = XCBSurface(xcb_conn, pixmap, root_visual, width, height)
assert surface

# use xcb surface to create context, draw white
ctx = Context(surface)
ctx.set_source_rgb(1, 1, 1)
ctx.paint()

# map the window and wait for it to appear
xcb_conn.core.MapWindow(wid)
xcb_conn.flush()

start = time.time()
while time.time() < start + 10:
event = xcb_conn.wait_for_event()
if isinstance(event, xcffib.xproto.ExposeEvent):
break
else:
pytest.fail("Never received ExposeEvent")

# copy the pixmap to the window
xcb_conn.core.CopyArea(
pixmap, # source
wid, # dest
gc, # gc
0, 0, # source x, source y
0, 0, # dest x, dest y
width, height
)

# flush the connection, make sure no errors were thrown
xcb_conn.flush()
while event:
event = xcb_conn.poll_for_event()


def test_xcb_window(xcb_conn):
width = 10
height = 10

# create a new window used to draw with cairo
wid = create_window(xcb_conn, width, height)

# map the window and wait for it to appear
xcb_conn.core.MapWindow(wid)
xcb_conn.flush()

start = time.time()
while time.time() < start + 10:
event = xcb_conn.wait_for_event()
if isinstance(event, xcffib.xproto.ExposeEvent):
break
else:
pytest.fail("Never received ExposeEvent")

# create XCB surface on window
root_visual = find_root_visual(xcb_conn)
surface = XCBSurface(xcb_conn, wid, root_visual, width, height)
assert surface

# use xcb surface to create context, draw white
ctx = Context(surface)
ctx.set_source_rgb(1, 1, 1)
ctx.paint()

xcb_conn.flush()

# now move the window and change its size
xcb_conn.core.ConfigureWindow(
wid,
ConfigWindow.X | ConfigWindow.Y | ConfigWindow.Width | ConfigWindow.Height,
[
5, 5, # x, y
width * 2, height * 2 # width, height
]
)
xcb_conn.flush()

# wait for the notification of the size change
start = time.time()
while time.time() < start + 10:
event = xcb_conn.wait_for_event()

if isinstance(event, xcffib.xproto.ConfigureNotifyEvent):
assert event.width == 2*width
assert event.height == 2*height
width = event.width
height = event.height
break
else:
pytest.fail("Never received ConfigureNotifyEvent")

# re-size and re-draw the surface
surface.set_size(width, height)
ctx = Context(surface)
ctx.set_source_rgb(1, 1, 1)
ctx.paint()

# flush the connection, make sure no errors were thrown
xcb_conn.flush()
while event:
event = xcb_conn.poll_for_event()
63 changes: 63 additions & 0 deletions cairocffi/xcb.py
@@ -0,0 +1,63 @@
# coding: utf8
"""
cairocffi.xcb
~~~~~~~~~~~~~
Bindings for XCB surface objects using xcffib.
:copyright: Copyright 2014 by Simon Sapin
:license: BSD, see LICENSE for details.
"""
from xcffib import ffi as xcb_ffi, visualtype_to_c_struct

from . import ffi, dlopen, constants
from .surfaces import Surface, SURFACE_TYPE_TO_CLASS

ffi.include(xcb_ffi)
ffi.cdef(constants._CAIRO_XCB_HEADERS)
cairo_xcb = dlopen(ffi, 'libcairo.so.2', 'libcairo.2.dylib', 'libcairo-2.dll',
'cairo', 'libcairo-2')

class XCBSurface(Surface):
"""The XCB surface is used to render cairo graphics to X Window System
windows and pixmaps using the XCB library.
Creates a cairo surface that targets the given drawable (pixmap or window).
.. note::
This class works using objects and libraries in :mod:`xcffib`
:param conn: The :class:`xcffib.Connection` for an open XCB connection
:param drawable:
An XID corresponding to an XCB drawable (a pixmap or a window)
:param visual: An :class:`xcffib.xproto.VISUALTYPE` object.
:param width: integer
:param height: integer
"""
def __init__(self, conn, drawable, visual, width, height):
c_visual = visualtype_to_c_struct(visual)

p = cairo_xcb.cairo_xcb_surface_create(
conn._conn, drawable, c_visual, width, height)
Surface.__init__(self, p)

def set_size(self, width, height):
"""
Informs cairo of the new size of the X Drawable underlying the surface.
For a surface created for a Window (rather than a Pixmap), this
function must be called each time the size of the window changes (for
a subwindow, you are normally resizing the window yourself, but for a
toplevel window, it is necessary to listen for
:class:`xcffib.xproto.ConfigureNotifyEvent`'s).
A Pixmap can never change size, so it is never necessary to call this
function on a surface created for a Pixmap.
:param width: integer
:param height: integer
"""
cairo_xcb.cairo_xcb_surface_set_size(self._pointer, width, height)
self._check_status()

SURFACE_TYPE_TO_CLASS[constants.SURFACE_TYPE_XCB] = XCBSurface
1 change: 1 addition & 0 deletions docs/index.rst
Expand Up @@ -9,5 +9,6 @@ Documentation
overview
api
pixbuf
xcb
cffi_api
changelog
8 changes: 8 additions & 0 deletions docs/overview.rst
Expand Up @@ -14,6 +14,13 @@ This will automatically install CFFI,
which on CPython requires ``python-dev`` and ``libffi-dev``.
See the `CFFI documentation`_ for details.

cairocffi can also be setup to utizile XCB support via xcffib_.
This can also be installed automatically with pip_::

pip install cairocffi[xcb]

In addition to other dependencies, this will install xcffib.


Importing
---------
Expand All @@ -36,6 +43,7 @@ where to find shared libraries.

.. _pip: http://pip-installer.org/
.. _CFFI documentation: http://cffi.readthedocs.org/
.. _xcffib: https://github.com/tych0/xcffib/
.. _Pycairo: http://cairographics.org/pycairo/


Expand Down
11 changes: 11 additions & 0 deletions docs/xcb.rst
@@ -0,0 +1,11 @@
.. module:: cairocffi.xcb

Using XCB surfaces with xcffib
==============================

The :mod:`cairocffi.xcb` module uses xcffib_ as the XCB library to create
graphics for X windows and pixmaps.

.. autoclass:: XCBSurface

.. _xcffib: https://github.com/tych0/xcffib/
1 change: 1 addition & 0 deletions setup.py
Expand Up @@ -37,4 +37,5 @@
],
packages=find_packages(),
install_requires=['cffi>=0.6'],
extras_require={'xcb': ['xcffib']}
)

0 comments on commit 2f43835

Please sign in to comment.