awesome/docs/16-using-cairo.md

164 lines
5.5 KiB
Markdown

# Using Cairo and LGI
These days, Awesome interface is mostly based on a library called LGI. It provides
access C libraries such as GTK, GLib, Cairo, Pango, PangoCairo and RSVG using
Lua code without having to write actual "glue" C code.
This is done using the GObject-introspection framework. The main advantage is
the time saved and large number of features exposed for free. The downside is
the lack of proper Lua centric documentation and examples. Some examples can be
found in [LGI's own documentation](https://github.com/pavouk/lgi/tree/master/docs),
but this does not directly explain how to use a concrete API.
Using other APIs requires some trial and error, and can be even impossible if
the introspection data is missing or inaccurate.
Using low-level APIs directly can easily cause crashes.
It is the programmer's responsibility to properly check return and error
values.
## Using LGI in Awesome
GObject and Gnome centric libraries tend to use the common C practice of
emulating namespaces using underscores in function names.
LGI exposes a proper namespace based API. For example, if the C function is:
cairo_image_surface_create()
Then the LGI equivalent is:
lgi.cairo.ImageSurface.create()
The same goes for enums:
CAIRO_FORMAT_ARGB32
becomes:
lgi.cairo.Format.ARGB32
LGI is also object oriented while the C API is function based. When those
functions take the "class" "object", then this:
cairo_line_to(cr, x, y)
can be expressed as:
cr:line_to(x, y)
It is however important to note some inconsistencies. For example, Cairo is
called `lgi.cairo` while GLib is called `lgi.GLib`. Figuring this out will
require some experimenting. The best way to do this without actually
reloading Awesome is to open the `lua` command in a terminal and use `print`:
print("This will print a table address:", require("lgi").cairo)
print("This will print an error:", require("lgi").Cairo)
It is recommended to avoid using `require` always when using a function, but
include the libraries at the top of your `rc.lua` or Lua module instead:
local cairo = require("lgi").cairo
## The Cairo API
Cairo is a 2D graphic library used by Awesome, Gnome and XFCE. It allows to
e.g. paint paths on a `surface`.
Awesome uses it internally and being able to call it directly is a powerful
feature.
The following concepts are necessary to be able to use Cairo:
**Surface**:
A surface is the area where the painting will be done. There are multiple types
of surfaces including:
* Color images with transparency (`ARGB32`) or without (`RGB24`)
* Monochrome image surfaces with transparency (`A8`) or without (`A1`)
* SVG vectorial surfaces
* Native (XCB) surfaces
* Framebuffers and other less interesting ones (from an Awesome's point of
view)
For more details see [Surfaces](https://cairographics.org/manual/cairo-Image-Surfaces.html).
**Sources**:
Sources are elements like colors, patterns or gradients. See `gears.color` for
common sources.
For more details see [Pattern](http://cairographics.org/manual/cairo-cairo-pattern-t.html).
**Context and paths**:
A context is the proxy between the program and the surface, and holds a path.
Paths are something like a line, circle or rectangle, which may or
may not be closed (a shape).
All drawing operations on a surface are done via a context.
The current path is extended until it is used and reset (see next section).
Until then nothing will be drawn to the surface. For example:
cr:rectangle(0 , 0 , 10, 10)
cr:rectangle(10, 10, 10, 10)
will not do anything until the operation is applied to the context.
For more details, read:
* [Path](http://cairographics.org/manual/cairo-Paths.html)
* [Context](http://cairographics.org/manual/cairo-cairo-t.html)
* [Transformation](http://cairographics.org/manual/cairo-Transformations.html)
A context also holds a transformation matrix (see `gears.matrix`), which is
used when applying an operation.
**Operations**:
Multiple operations can be done with the paths. The most common are:
* *fill*: Fill the path with the current source.
* *stroke*: Paint the path outline with the current source.
* *mask*: Use the current source as an alpha mask while painting with the
current operator.
* *clip*: Crop the surface's workarea so nothing outside of the clip will be
affected by all following operations.
**Operators**:
[Operators](http://cairographics.org/operators/) are modifiers used when
applying operations.
### Cairo in Awesome
All of Awesome's `wibox`es, `awful.wibar`s, `gears.wallpaper`s and
`awful.titlebar`s contain Cairo surfaces, which can be accessed through the
`drawin` API. This allows widgets to use the Cairo context directly.
See the
[declarative layout system](../documentation/03-declarative-layout.md.html)
and [new widgets](../documentation/04-new-widgets.md.html) articles for more
information and examples on how widgets work.
It is also possible to create surfaces manually. See `gears.surface` for
some examples. Here is the most simple example you can get:
-- Create a surface
local img = cairo.ImageSurface.create(cairo.Format.ARGB32, 50, 50)
-- Create a context
local cr = cairo.Context(img)
-- Set a red source
cr:set_source(1, 0, 0)
-- Alternative:
cr:set_source(gears.color("#ff0000"))
-- Add a 10px square path to the context at x=10, y=10
cr:rectangle(10, 10, 10, 10)
-- Actually draw the rectangle on img
cr:fill()
This can then be used as `bgimage` for a `wibox`, `awful.wibar` or
`wibox.container.background`:
screen.primary.mywibox.bgimage = img