# A native Cairo binding, shipped The [vector chapter](vector-cairo-svg.md) drew with `Cairo` as an ordinary CPAN module. This chapter shows the other side of that same code: pperl ships a **native reimplementation of `Cairo`**, so `use Cairo` works on pperl with no CPAN Cairo distribution installed and no C compiler involved. The Perl you write is identical; what backs it is pperl’s own FFI reaching `libcairo` directly. Cairo makes the ideal worked example because its C API is almost entirely scalars and one opaque handle, which is precisely the shape that reaches cleanly today. This is a real, shipped binding, not a sketch. Reading it tells you what a curated native graphics binding is, how the pieces fit, and how to recognize (or ask for) the same treatment for another library. Building such a binding is a runtime task; what you write as a user is the Perl on the far side of it, and that Perl is just the [`Cairo` API](vector-cairo-svg.md) you already saw. ## The Perl API: exactly the CPAN one The native binding exposes the **real `Cairo` package names and methods**: `Cairo::ImageSurface`, `Cairo::Context`, `Cairo::PdfSurface`, and the rest, with the upstream method signatures. Code written for CPAN `Cairo` runs unchanged; ported Cairo test suites pass against it. The program from the vector chapter is the program here: ```perl use Cairo; my $surface = Cairo::ImageSurface->create('argb32', 200, 200); my $cr = Cairo::Context->create($surface); $cr->set_source_rgb(1, 1, 1); $cr->paint; # white background $cr->set_source_rgb(0, 0, 0); $cr->set_line_width(3); $cr->move_to(20, 20); $cr->line_to(180, 20); $cr->stroke; $cr->arc(100, 100, 40, 0, 6.28318); $cr->set_source_rgba(1, 0, 0, 0.5); $cr->fill; $surface->write_to_png('out.png'); # the surface and context free their C objects when they go out of scope ``` Nothing in that program hints at how it is implemented. There is no `dlopen`, no signature string, no binding object: just `Cairo`. The rest of this chapter is what makes `use Cairo` do that on pperl, and the pattern is the same one behind [`Math::GMP`](ffi-graphics-primer.md). ## Step 1: open the library once, resolve the symbols The binding opens `libcairo.so.2` a single time, when the module loads, and resolves each C function it needs into a table of function pointers held for the life of the process. This is the same shape as the [`Math::GMP`](ffi-graphics-primer.md) binding, which opens `libgmp` once and resolves its `mpz_*` symbols. For Cairo the resolved symbols are the ones behind the methods above: ```default open libcairo.so.2 once when the module loads resolve and store, as function pointers: cairo_image_surface_create(format, width, height) -> surface* cairo_create(surface*) -> context* cairo_move_to(context*, x, y) cairo_line_to(context*, x, y) cairo_arc(context*, xc, yc, radius, a1, a2) cairo_set_source_rgb(context*, r, g, b) cairo_set_source_rgba(context*, r, g, b, a) cairo_set_line_width(context*, w) cairo_stroke(context*) cairo_fill(context*) cairo_paint(context*) cairo_surface_write_to_png(surface*, path) -> status cairo_destroy(context*) cairo_surface_destroy(surface*) ``` If `libcairo` is not installed on the system, the binding reports it once with a helpful message rather than crashing. Every one of those C functions has a signature the FFI’s type codes already express: the arguments are the opaque handle (an address, so an integer), doubles, and a string path. There is no by-value struct in the core drawing API, which is why Cairo is the clean example. Cairo is a large library, so the real binding is organized by package: `Cairo::Surface`, `Cairo::Context`, `Cairo::Path`, `Cairo::Pattern`, `Cairo::Matrix`, `Cairo::Font`, and `Cairo::Region` each resolve the symbols they need. That is an implementation convenience; the user sees one `Cairo`. ## Step 2: the opaque handle as a blessed integer Cairo hands back a `cairo_surface_t*` and a `cairo_t*`: pointers to C structs whose insides you never touch. The binding does not need to understand their layout, only to hold the pointer and pass it back on every call. pperl represents such a handle the same way [`Math::GMP`](ffi-graphics-primer.md) holds its `mpz_t` pointer: as an **integer inside a blessed reference**. ```default Cairo::ImageSurface object = bless(\ (surface_ptr as integer), 'Cairo::ImageSurface') Cairo::Context object = bless(\ (context_ptr as integer), 'Cairo::Context') ``` When a method is called, the binding reads the integer back out of the blessed reference, casts it to the C pointer, and calls the function. The blessing gives you method dispatch (`$cr->stroke`), inheritance (every concrete surface class is a `Cairo::Surface` subclass), and, crucially, a `DESTROY` hook. The only difference from `Math::GMP` is that the opaque thing is a drawing surface instead of a big integer. ## Step 3: lifetime tied to DESTROY A C graphics object must be freed exactly once. Leak it and you burn memory every drawing; free it twice and you corrupt the heap. The binding ties the C object’s life to the Perl object’s: when the blessed reference goes out of scope, Perl calls `DESTROY`, and the binding calls the matching C destructor, respecting cairo’s reference counting. ```default Cairo::Context DESTROY -> cairo_destroy(context_ptr) Cairo::Surface DESTROY -> cairo_surface_destroy(surface_ptr) ``` [`Math::GMP`](ffi-graphics-primer.md) does precisely this: its destructor calls `mpz_clear` and frees the `mpz_t`. You, the user, never call a free function; the drawing objects clean up when the Perl variables holding them go away. The binding enforces the ordering cairo requires, releasing a context before the surface it draws to. ## Step 4: the methods Each Perl method is a small native function that reads the handle, reads the scalar arguments, and makes one C call. `Cairo::Context::move_to` reads the context pointer and two doubles and calls `cairo_move_to`; `Cairo::Context::set_source_rgba` reads the pointer and four doubles and calls `cairo_set_source_rgba`; `Cairo::Surface::write_to_png` reads the pointer and a path string. The mapping is one method to one C call: ```default Cairo::Context::move_to($cr, $x, $y) -> read context ptr from $cr; call cairo_move_to(ptr, x, y) Cairo::Context::set_source_rgba($cr, $r, $g, $b, $a) -> call cairo_set_source_rgba(ptr, r, g, b, a) Cairo::Surface::write_to_png($surface, $path) -> call cairo_surface_write_to_png(ptr, path) ``` The C-level signatures live in the binding and are settled there once. A user calling `$cr->set_source_rgba(1, 0, 0, 0.5)` never writes a signature and cannot get one wrong. That single point of truth for the C calls is the third of the [three reasons](ffi-graphics-primer.md) a curated binding beats scattered raw calls: object lifetime, ergonomics, and the signature written down once. ## What this reaches, and what it does not With those four pieces (open once, handle as a blessed integer, DESTROY frees, methods over the resolved function table) pperl delivers a real, memory-safe `Cairo` with no CPAN distribution installed, no C compiler, and no build step. The full [vector chapter](vector-cairo-svg.md) drawing model is available, and the objects manage themselves. What the pattern does not, by itself, reach is the same frontier the [primer](ffi-graphics-primer.md) named. Cairo avoids all of it in its core drawing API, which is why it is the first graphics library brought up this way. A library that passes colors or points **by value as structs**, that **calls back** into your code, or that hands you **union-typed events** needs more than this pattern has: the by-value and union cases wait on the [data-description layer](ffi-graphics-primer.md), and callbacks need function-pointer support that re-enters Perl. The [2D game capstone](capstone-2d-game.md) hits exactly that wall with windowing and event libraries, and says so at the point it does. ## The takeaway A graphics library whose C API is scalars plus opaque handles can be brought up on pperl as a curated native binding, and `Cairo` is the first one shipped: open the library once, hold each C object as a blessed integer, free it in `DESTROY`, and back each method with its C call settled in one place. The result is the upstream `Cairo` API, running natively. Other libraries of the same shape are on the way. The [next chapter](capstone-drawing-app.md) stops describing and starts building: a drawing application on the maintained toolkit stack, assembling the whole guide into something that runs.