Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make a haskell-gi based version of the cairo library #148

Open
cohomology opened this issue Apr 5, 2018 · 52 comments
Open

Make a haskell-gi based version of the cairo library #148

cohomology opened this issue Apr 5, 2018 · 52 comments
Assignees

Comments

@cohomology
Copy link
Collaborator

I am a Haskell newbie, but I managed to write a non-trivial program with gtk2hs and converted it completely to Haskell GI yesterday. I wanted to share some pain points of the conversion.

  1. One main pain point is the Cairo integration. There should be something (in Haskell GI, external library?), which abstracts this ForeignPtr stuff. Also, I used Pango Layouts deep inside the Render monad. Thus I needed to pass the GI.Cairo.Context inside the Render monad and switch between both of them. I found this very uggly. Generally I like the monadic interface of gtk2hs. Couldn't there be a special version of the Cairo library, which works with GI out of the box? Also gtk2hs seems to be unmaintained and the set of stackage resolvers where both GI and gtk2hs compile is nearly empty. So beeing dependent on both of them will be a pain in the long run.

  2. There are quite a few functions which should take enumerations, but take Int32 instead, for example dialogRun and dialogAddButton. In the Leksah source code, there are functions dialogRun' and dialogAddButton' which take GTK.ResponseType instead. I also would like to have an enumeration for the mouse buttons in getEventButtonButton, like gtk2hs had, maybe wrapping inside Maybe, if anybody has a mouse with more than 20 buttons and there will ever be a Haskell GI program taking advantage of it.

  3. I originally switched from Rust to Haskell, because in gtk-rs there was a mess of types used. Some functions used f64, some i32 and some u32. Sometimes similar sounding functions used another data type for the same kind of parameter. This was better with gtk2hs which used Int throughout. I now know that this mess comes from the introspection data itself, but could Haskell GI just also use Int? I do not like Int32 very much, as all conversions can go wrong and I'd rather love the library handle it, in a high level language like Haskell.

  4. I actually liked the monadic nature of gtk2hs, e.g. for Events. Couldn't something like this be autogenerated, too?

  5. I found it hard to find the correct pieces in the documentation. But after 1 day of digging, I am much better know. My first try was to grep in the generated binaries ;-). Nowadays I grep inside Leksah's source code, which, although I was not able to compile it on any platform, is a great source of inspiration.

Thanks in advance for some thoughts on the above points.

@garetxe
Copy link
Collaborator

garetxe commented Apr 6, 2018

Thanks a lot for the feedback! This is very valuable. I was aware of these things, but having confirmation that they are a pain in practice helps me prioritize them.

Some replies:

Couldn't there be a special version of the Cairo library, which works with GI out of the box?

There should be, certainly! It's just that nobody has written it yet. Ideally, gi-cairo should not be autogenerated, but rather a port of cairo using the haskell-gi conventions. This is probably an easy job, but nobody has done it yet. (It's on the roadmap, but since a workaround exists I have been prioritizing other things.)

There are quite a few functions which should take enumerations, but take Int32 instead, for example dialogRun and dialogAddButton.

What is there right now is sensible, in that this is what the introspection data says the type should be. And the introspection data is right, since the C API allows for any Int32, not just the ones coming from enumerations. But I agree completely that in practice this is a bit of a pain. gi-gtk-hs exists precisely to mitigate some of these pain points, if you feel like sending a pull request adding dialogRun and dialogAddButton that would be awesome!

I now know that this mess comes from the introspection data itself, but could Haskell GI just also use Int?

Converting gint to Int instead of Int32 should be doable, I think (I should tinker around and see what breaks...). Before I do that, could you please elaborate on when was Int32 vs. Int a problem in practice?

I actually liked the monadic nature of gtk2hs, e.g. for Events. Couldn't something like this be autogenerated, too?

I am not sure that it can be sensible autogenerated, but adding some Event monad to gi-gtk-hs (simply porting the stuff in gtk2hs) would be a good idea, I think!

I found it hard to find the correct pieces in the documentation.

I see that you filed #147, I will add some comments there.

Thanks again for the comments!

@garetxe
Copy link
Collaborator

garetxe commented Apr 6, 2018

Converting gint to Int instead of Int32 should be doable, I think (I should tinker around and see what breaks...).

Actually, this was wrong! In my my system at least sizeOf (0 :: Int) == 8 but sizeOf (0 :: CInt) == 4 (and gint == CInt). So going CInt -> Int is safe, but not the other way round.

In fact I have some vague recollection that at some point I did do the Int32 choice on purpose, precisely because of this fact.

@cohomology
Copy link
Collaborator Author

cohomology commented Apr 6, 2018

So how would you proceed with 1.? Perhaps I can help with that?

Would it be better to create a separate repository, or to work inside the Haskell-gi repository? I guess it is very easy to:

  • Move all the stuff from the traditional gtk2hs Cairo repository
  • Remove all dependencies to gtk2hs buildtools
  • Change the namespace

Then GTK would need to be adapted that instead of GI.Cairo.Context a Cairo.Render instance is created at the appropriate places. But more importantly, GI.Pango and GI.PangoCairo need to be adjusted that they work inside the Render monad (for the few functions, that do).

This seems like there is more to do inside GTK / Pango / PangoCairo than in Cairo itself. Is that correct?

Of course, there are functions like cairoFontMapGetDefault in Cairo, which could be removed in favor of PangoCairo.fontMapGetDefault.

--

Sorry for my collection of incoherent thoughts about integer types in the next section:

Regarding the Integer types: you really have to decide how low level you want Haskell GI to be. Do the users of your library have to deal with the conversions and the corresponding errors, or does the library deal with it? For the users, it is more easy if they don't have to care and their programs are easier to maintain. Idiomatic Haskell would certainly be to take Int or Integer and handle the conversions internally by throwing exceptions if the number gets to large.

I had all these integer conversions going on for many Pango functions, e.g. getRectangleWith, layoutSetMarkup and some GTK functions like widgetQueueDrawArea.

It is not really a problem though. Just dozens of fromIntegral around the code. In gtk-rs it was more of a problem, as they used f64 and i32 very often for coordinates, and I was not not sure if they pass me negative coordinates sometimes. The problem arised there because I decided to internally work with unsigned integers, which is not done in Haskell very often. But if you go to a semantic level, also in Haskell all kind of weird stuff occurs, if your library would pass, e.g. negative coordinates to user ;-). So Data.Word would propably be a safer candidate than Int. But for example layoutSetMarkup in Pango GI takes an Int32 although the argument can certainly never be negative.

But take for example layoutSetWidth. According to GTK, the width is signed, and -1 indicating no wrapping. The Haskell type system should prevent that the user gives -5. So Maybe Word would be much better.

The more I think about it, all these points are weaknesses from the GTK API, coming from weaknesses in the C type system.

@garetxe
Copy link
Collaborator

garetxe commented Apr 7, 2018

So how would you proceed with 1.? Perhaps I can help with that?

That would be fantastic, thanks! Here is what I think we should do. (@hamishmack Please let me know if you have a better idea for how to do things.)

We should fork the cairo library, and make gi-cairo this fork (so the fork will live in haskell-gi/bindings/gi-cairo). This involves the three steps you mention.

A hard requirement is that the API exported from the new gi-cairo is a strict superset of the current API exported from gi-cairo, so we don't need to add cairo-specific knowledge to the binding generator (I think that this is too much trouble for very little gain), and in particular we don't need to modify any other bindings. So we keep the current GI.Cairo.Context type, and all the other types currently there, but extend the API by adding all the operations available in the cairo bindings (switching to the gi-cairo types when applicable).

And we add renderWithContext from https://github.com/haskell-gi/haskell-gi/wiki/Using-Cairo-with-haskell-gi-generated-bindings (I guess changing the prototype to MonadIO m => GI.Cairo.Context -> Render a -> m a for convenience).

So, in practice, as an example, this would mean that the draw signal of Gtk.Widget would still get a Cairo.Context as a argument, so the typical handler will be:

widgetDraw :: Cairo.Context -> IO Bool
widgetDraw cr = renderWithContext cr $ do (stuff in the render monad)

Does this sound reasonable? This seems to me convenient enough, while still staying close enough to the C API so that maintenance is not much of a pain.

If you agree, and feel like giving it a try, I would be happy to help.

@garetxe
Copy link
Collaborator

garetxe commented Apr 7, 2018

Regarding the Integer types: you really have to decide how low level you want Haskell GI to be. Do the users of your library have to deal with the conversions and the corresponding errors, or does the library deal with it?

My current guiding principle is making the bindings as high level as possible, while being correct in the sense that, assuming that the C API has no bugs, no bugs are introduced by the bindings themselves.

So stuff like the overloaded labels (set widget [#sensitive := True] or on button #clicked $ do..) is good, since it improves the type safety of the bindings: there is more compile type checking of the code than in the equivalent C code using g_object_set or g_signal_connect.

But silently translating Int32 to Int violates this principle: one can naively write

f :: Int -> Int
f x = ...

i32 <- get #intProp widget
set widget [#intProp := f i32]

and even if f is careful not to overflow in Int, the set call will raise an exception if the given Int does not fit into Int32 (and not raising an exception is stricly worse, in my opinion, since it will lead to silent bugs). I find that this is not very good design in a library: the user may or may not care about the overflow, but I should not be making any assumption as the library writer, specially if neither outcome is good: either potential silent logic errors from the (Int -> Int32) truncation, or very sporadic exceptions that may easily elude testing.

So I think I am reasonably satisfied with the current tradeoff. We are binding a C library which uses a type Int32 which is not isomorphic to Int, after all, and I don't think that we should pretend otherwise.

layoutSetMarkup in Pango GI takes an Int32 although the argument can certainly never be negative.

Indeed! This is #128, and I agree that it should be fixed (but the fix is not straightforward, and would require a small extension of the introspection spec).

But take for example layoutSetWidth. According to GTK, the width is signed, and -1 indicating no wrapping. The Haskell type system should prevent that the user gives -5. So Maybe Word would be much better.

Very nice suggestion! I had not thought of this before, good idea. There is unfortunately currently nothing in the introspection API that specifies this information, though, so in the same way as #128, one would need some upstream work first. Maybe worth it!

The more I think about it, all these points are weaknesses from the GTK API, coming from weaknesses in the C type system.

Indeed! We should strive to make the haskell-gi bindings as userfriendly as possible, but the C API is what it is, warts and all...

Along these lines, and since you asked about the general design philosophy :) One thing that should be possible, and may be an interesting experiment, is to build a much more high-level GUI toolkit based on haskell-gi, but more idiomatic. Since with gobject-introspection you have programmatic access to the API, you could just keep "the good parts" (signals, properties, and constructors), and build fairly easily something reasonably nice (with a escape hatch to the full haskell-gi bindings, if necessary, so it is important that haskell-gi, as it currently exists, is there, so you can do in Haskell anything you can do in C, without having to worry too much about marshaling datatypes and memory management). I think that there is some work in this direction, building for example an interface between FRP and gi-gtk: https://github.com/mr/reactive-banana-gi-gtk .

Doing the same thing for reflex, or perhaps other approaches to GUI programming in Haskell should be quite nice.

@cohomology
Copy link
Collaborator Author

Ok, I will give it a try.

Regarding the project structure

For me it is not quite clear, if it really should live inside the bindings folder. The logic is quite special there and it would be the first non-generated source file in there. And we should make non-equal stuff separate. Perhaps we could create an extra repository inside haskell-gi?

Also from the dependency chain, the gi-cairo-render package (working name, okay?) will be dependent on Haskell GI but not the other way around, right?

Regarding the GI.PangoCairo invocation inside Cairo

We still need the reverse of the "trick" you mentioned above, e.g. a function with the signature:

getContext :: Cairo.Render GI.Cairo.Context

We can assume safely, that the Cairo.Render context is created via your first "trick".

@garetxe
Copy link
Collaborator

garetxe commented Apr 8, 2018

Ok, I will give it a try.

Excellent, thanks!

For me it is not quite clear, if it really should live inside the bindings folder. The logic is quite special there and it would be the first non-generated source file in there. And we should make non-equal stuff separate.

Sure, it could easily live anywhere else in the repo.

Perhaps we could create an extra repository inside haskell-gi?

I did this originally with haskell-gi-base, and others, but with time I realized it makes things simpler (particularly for testing) to keep everything in a single repo.

Also from the dependency chain, the gi-cairo-render package (working name, okay?) will be dependent on Haskell GI but not the other way around, right?

Right. gi-cairo-render would depend on haskell-gi (and gi-glib, I guess?), and gi-gtk and friends would depend on gi-cairo-render.

We still need the reverse of the "trick" you mentioned above, e.g. a function with the signature getContext :: Cairo.Render GI.Cairo.Context

I am not sure that such a function can be constructed (Render is something like ReaderT Context IO, so it does not include any particular context). Why do you need this?

@cohomology
Copy link
Collaborator Author

cohomology commented Apr 8, 2018

Perhaps I understand something wrong. Assume, if we have the "trick 2" function, then haskell-gi, including gi-gtk will be completely independent of gi-cairo-render (as if it where a complete different library).

Actually one could even take the old cairo library, if there wouldn't be the dependency to gtk2hs, especially gtk2hs-buildtools which we want to drop. Also the old cairo library, defines a lot of functions which reduplicate behaviour of GI.Pango and GI.PangoCairo, which we don't need anymore.

To be more precise:

As you wrote, the user can use renderWithContext to invoke some user defined draw function for the canvas. He can use all of Cairo inside this draw function. The problem occurs, if the user wants to call some GI.Pango or GI.PangoCairo function which takes a GI.Cairo.Context, say PangoCairo.showLayout. If we have the "trick 2" function then the user can write:

drawCanvasHandler :: Cairo.Render ()
drawCanvasHandler = 
  do ... 
       ... 
       context <- getContext
       PangoCairo.showLayout context layout

This even works, 5 level of function calls down.

This would be an acceptable API for my taste. I has the advantage, that haskell-gi can be completely unchanged.

Letting the Haskell type yoga aside, both Cairo.Render and GI.Cairo.Context must contain a pointer to the plain old Cairo C context, so transforming them into each other should work somehow, right?

@garetxe
Copy link
Collaborator

garetxe commented Apr 8, 2018

Oh, I misread the type signature!

getContext :: Cairo.Render GI.Cairo.Context

should be trivial to construct, of course. I misread it as

getContext :: Cairo.Render -> GI.Cairo.Context

which is impossible to construct.

I completely agree with the rest of your comment, and I also had this kind of "decoupled" design in mind.

@garetxe
Copy link
Collaborator

garetxe commented Apr 8, 2018

Thinking more about the example you gave, we probably also want something like

toRender :: (Cairo.Context -> IO a) -> Render a

to be able to embed something like PangoCairo.showLayout into the Render monad. When you peel off the newtypes, this function is just id (so toRender = coerce).

@cohomology
Copy link
Collaborator Author

cohomology commented Apr 8, 2018

Ok, I will then:

  1. Fork cairo and put everything into GI.Cairo.Render namespace.
  2. Remove all dependencies to gtk2hs.
  3. Remove all functions which provide functionality provided by GI.Pango and GI.PangoCairo or any other haskell-gi library.
  4. Provide a GI.Cairo.Render.Base which provides renderWithContext, toRender and getContext functions.
  5. I will put everything in a separate repository first, as this is more easy to me (I am completely independent of the build yoga of Haskell GI, which I don't need). You can merge this into Haskell GI at your liking later on or keep it as a separate repository inside haskell-gi.

What I would really like to know, if this is okay for the developers of gtk2hs, especially @hamishmack. If they wanted to invest further in cairo, this whole idea would be bad.

@garetxe
Copy link
Collaborator

garetxe commented Apr 9, 2018

This sounds like an excellent plan, thanks!

@cohomology
Copy link
Collaborator Author

cohomology commented Apr 11, 2018

I still have to learn c2hs. Especially I do not understand the difference between c2hs and gtk2hsC2hs.

I still don't get why the did it this way and didn't try to get their changes upstream.

  1. The build mechanism: Most of the code is 1:1 a copy of the cabal source code, with slight modifications. Why? They should have bring it upstream. Especially their x-c2hs-header annotation makes sense for other people, too (after giving it a reasonable name).

I thought I get rid of it altogether and prefer using #include a few times instead of 1000 lines of cabal source code copy.

  1. But c2hs seems to tick slightly different than gtk2hsC2hs. I now get many of the these errors:
./GI/Cairo/Render/Internal/Drawing/Patterns.chs:39: (column 69) [ERROR]  >>> Missing "in" marshaller!
  There is no default marshaller for this combination of Haskell and C type:
  Haskell type: Matrix
  C type      : (MatrixPtr)

From what I see, this "marshaller" is defined in Matrix.chs, which is included via {#import GI.Cairo.Render.Types#} which in turn includes {#import GI.Cairo.Render.Matrix#}. But even if I include {#import GI.Cairo.Render.Matrix#} directly, it doesn't work out.

Do you have a clue? The code is here.

@cohomology
Copy link
Collaborator Author

cohomology commented Apr 12, 2018

The problem is with the following minimal code:

module Matrix where

data Matrix = Matrix { xx :: !Double, yx :: !Double,
                       xy :: !Double, yy :: !Double,
                       x0 :: !Double, y0 :: !Double }
  deriving (Show, Eq) 

{#pointer *cairo_matrix_t as MatrixPtr -> Matrix#}

instance Storable Matrix where
  sizeOf _ = {#sizeof cairo_matrix_t#}
  alignment _ = alignment (undefined :: CDouble)
  peek p = do
    xx <- {#get cairo_matrix_t->xx#} p
    yx <- {#get cairo_matrix_t->yx#} p
    xy <- {#get cairo_matrix_t->xy#} p
    yy <- {#get cairo_matrix_t->yy#} p
    x0 <- {#get cairo_matrix_t->x0#} p
    y0 <- {#get cairo_matrix_t->y0#} p
    return $ Matrix (realToFrac xx) (realToFrac yx)
                    (realToFrac xy) (realToFrac yy)
                    (realToFrac x0) (realToFrac y0)

  poke p (Matrix xx yx xy yy x0 y0) = do
    {#set cairo_matrix_t->xx#} p (realToFrac xx)
    {#set cairo_matrix_t->yx#} p (realToFrac yx)
    {#set cairo_matrix_t->xy#} p (realToFrac xy)
    {#set cairo_matrix_t->yy#} p (realToFrac yy)
    {#set cairo_matrix_t->x0#} p (realToFrac x0)
    {#set cairo_matrix_t->y0#} p (realToFrac y0)
    return () 

{#context lib="cairo" #}

{#fun cairo_pattern_set_matrix as patternSetMatrix { unPattern `Pattern', `Matrix'} -> `()'#}
{#fun cairo_pattern_get_matrix as patternGetMatrix { unPattern `Pattern', alloca- `Matrix' peek*} -> `()'#}    

This is valid code for gtk2hsC2hs but invalid for c2hs. Both can compile the second fun, e.g. cairo_pattern_get_matrix, although the generated code is different (but seems semantically equivalent).

So:

`Matrix'               -> invalid as in marshaller in c2hs, valid in gtk2HsC2hs
alloca- `Matrix' peek* -> valid for both

I can't understand the behaviour of c2hs. According to the docs, this should all be valid.

@cohomology
Copy link
Collaborator Author

@garetxe
Copy link
Collaborator

garetxe commented Apr 12, 2018

I am afraid that I don't know the answer, but as an ugly workaround (until we figure out something better), perhaps you could just copy and paste by hand the code gtk2hsC2hs generates?

@cohomology
Copy link
Collaborator Author

cohomology commented Apr 12, 2018

Ok, after looking at the generated code, you have to write:

{#fun cairo_pattern_set_matrix as patternSetMatrix { `Pattern', with* `Matrix'} -> `()'#}

instead of

{#fun cairo_pattern_set_matrix as patternSetMatrix { unpattern `Pattern', `Matrix'} -> `()'#}

This syntax is just awesome.

I now fixed all c2hs errors, but now I have 500 Haskell compile errors due to the different newtype handling (e.g. the unPattern above is unnecessary in c2hs).

@cohomology
Copy link
Collaborator Author

cohomology commented Apr 13, 2018

Hi @garetxe

I now made everything compile and added three magic functions.

  1. I now created two libraries gi-cairo-render and gi-cairo-connector. The former is completely independent of everything (except base, mtl, text, array) and contains the original bindings. The latter combines both by providing the magic functions. As one can see with all the build problems of gtk2hs, the fewer dependencies the better. Is this okay for you?

  2. Can you look at the magic functions? I am very unsure about my usage of newManagedPtr. See 3. I added you in the copyright. Is this okay?

  3. There can be two ways to invoke a Render context. The first would be inside Haskell GI, i.e. via renderWithContext from a draw method. The second one would be directly from cairo, e.g. via renderWith. I am totally unsure to write getContext in a way, which handles both cases well. Or does it already work?

  4. I exchanged the parameters of renderWithContext. It worked out better with switched parameters in the examples, i.e. you can write

GTK.onWidgetDraw canvas $ renderWithContext drawCanvasHandler 

where drawCanvasHandler :: Render () instead of

GTK.onWidgetDraw canvas $ renderWithContext $ \context -> drawCanvasHandler context

Can you adopt me inside Haskell GI namespace or inside the Haskell GI project and still keep me as a co-maintainer?

What still needs to be done:

  • Translate the examples to use Haskell GI
  • Remove unnecessary functions
  • Add documentation

@garetxe
Copy link
Collaborator

garetxe commented Apr 16, 2018

Thanks a lot!

Can you look at the magic functions? I am very unsure about my usage of newManagedPtr.

I think that makeContext is not correct (well, it's probably fine if used only inside the Render monad, but let's make it safe), since the object referenced by the pointer might be destroyed (due to garbage collection) without us noticing. The safe way of doing this would be with newBoxed, which adds a reference to the boxed object.

Otherwise everything looks good, I think.

@garetxe
Copy link
Collaborator

garetxe commented Apr 16, 2018

I now created two libraries gi-cairo-render and gi-cairo-connector. The former is completely independent of everything (except base, mtl, text, array) and contains the original bindings. The latter combines both by providing the magic functions. As one can see with all the build problems of gtk2hs, the fewer dependencies the better. Is this okay for you?

Perhaps I would architect things slightly differently. We could have a cairo-base (tentative name) that is independent of haskell-gi, and then make gi-cairo a thin wrapper over this with what's currently in gi-cairo, adding the magic functions, and re-exporting everything in cairo-base in a suitable namespace. This way users of gi-* bindings do not have to worry about having the existence of cairo-base.

The overall decision to split off the base library is sound, I think, but calling it gi-cairo-render when it requires some glue code to use with gi-cairo seems to me like it will be confusing to users.

I added you in the copyright. Is this okay?

Sure!

@cohomology
Copy link
Collaborator Author

cohomology commented Apr 16, 2018

I totally agree.

If I understand correctly:

  1. We create a new project haskell-gi/cairo-base, which contains the content of the gi-cairo-render project (the examples will mostly use GTK+, so could live in some haskell-gi/haskell-gi subdirectory; some examples use SDL though or nothing at all. We have to think what we do with them).

We have to still find a suitable namespace. How about Graphics.Cairo without Rendering? From what I understand GI would be a bad name.

  1. We upload cairo-base to hackage.

  2. We actually use cairo-base in haskell-gi/haskell-gi by reexporting Graphics.Cairo inside the gi-cairo namespace and adding the contents of gi-cairo-connector to gi-cairo.

From what I understand, most of this can only be done by you. Especially most of 1 (e.g. creating a project, renaming namespaces can be done by me) and for 3. you would also be the ideal candidate.

I always wanted to upload a package to hackage though ;-).

If you don't want to have cairo-base inside haskell-gi we could also alternatively create some cairo-hs/cairo-base github project.

@garetxe
Copy link
Collaborator

garetxe commented Apr 17, 2018

There can be two ways to invoke a Render context. The first would be inside Haskell GI, i.e. via renderWithContext from a draw method. The second one would be directly from cairo, e.g. via renderWith. I am totally unsure to write getContext in a way, which handles both cases well. Or does it already work?

I don't see how to make it work for both while we keep the libraries separate (unless we use a typeclass or something, which I'd rather not, since it will make type inference worse).

One possibility is to make the Cairo type the same as GI.Cairo.Context. This would involve adding a dependency on haskell-gi-base, and some (easy, I think) rewiring of the internals of cairo-base. Note that (in contrast to haskell-gi itself) this is a rather small library, and only depends on base, bytestring and containers (and transformers for GHC < 8.0). So perhaps this is not too burdensome of a dependency.

What do you think? One advantage of this approach, in addition to having a single type, is that if we depend on haskell-gi-base we can end up with a single library, which will be less confusing for users.

@garetxe
Copy link
Collaborator

garetxe commented Apr 17, 2018

We create a new project haskell-gi/cairo-base, which contains the content of the gi-cairo-render project (the examples will mostly use GTK+, so could live in some haskell-gi/haskell-gi subdirectory; some examples use SDL though or nothing at all. We have to think what we do with them).

This depends a bit on how we decide to go. If cairo-base remains completely independent of haskell-gi-base, then haskell-gi/cairo-base sounds fine. Otherwise I think it would be better to put it in haskell-gi/haskell-gi, for ease of testing.

We have to still find a suitable namespace. How about Graphics.Cairo without Rendering? From what I understand GI would be a bad name.

Same here. If we go the route of adding a dependency on haskell-gi-base, then we can have a single library, and reuse GI.Cairo. Otherwise Graphics.Cairo sounds good, yep.

From what I understand, most of this can only be done by you. Especially most of 1 (e.g. creating a project, renaming namespaces can be done by me) and for 3. you would also be the ideal candidate.

Sounds good :) As soon as we agree on the design I can spend some time on this.

I always wanted to upload a package to hackage though ;-)

Sure :)

@cohomology
Copy link
Collaborator Author

cohomology commented Apr 17, 2018

Ok. Let's proceed with the idea, that we rewrite cairo to use a similar (!) representation than GI.Cairo.Context, i.e. assume we define

import Data.GI.Base.ManagedPtr
newtype Cairo = Cairo (ManagedPtr Cairo)

Then, lets look how to rewrite renderWith to use Haskell GI.

Sorry, I don't get it anymore this evening (it's 23.00 in germany), but I wrote the compiling version:

import Data.GI.Base
import Foreign.Ptr

{#context lib="cairo" prefix="cairo"#} 

newtype Cairo = Cairo (ManagedPtr Cairo)
{#pointer *cairo_t as CairoPtr -> Cairo#} 
unCairo (Cairo x) = x

newtype Surface = Surface (ManagedPtr Surface)
unSurface (Surface x) = x
{#pointer *surface_t as SurfacePtr -> Surface #}

{#fun create { withManagedPtr* `Surface' } -> `Cairo' magicFunction*#} 

magicFunction :: Ptr Cairo -> IO Cairo
magicFunction = undefined 

This is translated by c2hs to:

import qualified Foreign.Ptr as C2HSImp
import Data.GI.Base
import Foreign.Ptr

newtype Cairo = Cairo (ManagedPtr Cairo)
type CairoPtr = C2HSImp.Ptr (Cairo) 
unCairo (Cairo x) = x

newtype Surface = Surface (ManagedPtr Surface)
unSurface (Surface x) = x
type SurfacePtr = C2HSImp.Ptr (Surface)

create :: (Surface) -> IO ((Cairo))
create a1 =
  withManagedPtr a1 $ \a1' -> 
  create'_ a1' >>= \res ->
  magicFunction res >>= \res' ->
  return (res')
 
magicFunction :: Ptr Cairo -> IO Cairo
magicFunction = undefined

foreign import ccall safe "Graphics/Cairo.chs.h cairo_create"
  create'_ :: ((SurfacePtr) -> (IO (CairoPtr))) 

So I need to define the magic function, which must be something with newManagedPtr, right?

@garetxe
Copy link
Collaborator

garetxe commented Apr 18, 2018

Something like the following should work, I believe:

foreign import ccall "cairo_gobject_context_get_type" c_cairo_gobject_context_get_type :: 
    IO GType

instance BoxedObject Cairo where
    boxedType _ = c_cairo_gobject_context_get_type

magicFunction = wrapBoxed Cairo

@cohomology
Copy link
Collaborator Author

cohomology commented Apr 18, 2018

I rewrote all of the cairo lib, such that the type for Cairo is Cairo (ManagedPtr Cairo). The only function which makes trouble is actually renderWith.

I don't understand it completely.

The memory management of cairo is totally clear to me.

The original renderWith function is coded as follows:

renderWith surface (Render m) = liftIO $
  bracket (Internal.create surface)
          (\context -> do status <- Internal.status context
                          Internal.destroy context
                          unless (status == StatusSuccess) $
                            fail =<< Internal.statusToString status)
          (\context -> runReaderT m context) 

So we execute some function, while before we call Internal.create surface and afterwards we call Internal.destroy (where Internal.create and Internal.destroy map to C calls of the corresponding cairo functions).

I guess, calling withManagedPtr Internal.destroy would be the worst thing one can do, so the function has to be rewritten (we don't want to steal the ManagedPtr the underlying memory, right?).

So I did the following:

renderWith surface (Render m) = liftIO $
  bracket (do context <- Internal.create surface
              wrapBoxed Cairo context)
          (\context -> do status <- Internal.status context
                          freeBoxed context
                          unless (status == StatusSuccess) $
                            fail =<< Internal.statusToString status)
          (\context -> runReaderT m context) 

So I guess, that freeBoxed destroys the pointer with other means, e.g. this fancy C call you gave me?

But I feel, there must be simpler way. Couldn't I just call withTransient and replace the whole function by that? The pointer does not need to be garbage collected, as we know exactly how long it will live: the livetime of the callback!

But I don't see that the pointer is really destroyed in withTransient. But actually you call withTransient in GTK when building the onWidgetDraw callback.

You can find my changes in my use-gi branch.

@garetxe
Copy link
Collaborator

garetxe commented Apr 19, 2018

But I feel, there must be simpler way. Couldn't I just call withTransient and replace the whole function by that? The pointer does not need to be garbage collected, as we know exactly how long it will live: the livetime of the callback!

The rule is that wrapBoxed will take ownership of the pointer, and decrease its refcount when the Haskell object is garbage collected. So in the above:

renderWith surface (Render m) = liftIO $
  bracket (do context <- Internal.create surface
              wrapBoxed Cairo context)
          (\context -> do status <- Internal.status context
                          -- freeBoxed context
                          unless (status == StatusSuccess) $
                            fail =<< Internal.statusToString status)
          (\context -> runReaderT m context) 

the freeBoxed call is not necessary (and putting it in leads to a double free).

One could indeed also write a version with withTransient, and managing memory manually. Something like the following:

renderWith surface (Render m) = liftIO $
  bracket (do context <- Internal.create surface)
          (\context -> do status <- Internal.status context
                          Internal.destroy context
                          unless (status == StatusSuccess) $
                            fail =<< Internal.statusToString status)
          (\context -> withTransient Context context $ runReaderT m) 

Note that in either case there will be an object of type Cairo being garbage collected (withTransient does create one internally). So, unless GHC is clever at optimizing this (it may well be), the first version will induce pretty much the same amount of GC pressure.

Calling a destructor when an object is being collected by the GC is very small overhead, pretty much the same as calling Internal.destroy manually, I would guess, so in the end which version we prefer is a bit of a matter of taste. But if you prefer to be explicit about memory management the second one is probably best.

@cohomology
Copy link
Collaborator Author

cohomology commented Apr 19, 2018

Ok, now everything compiles.

I rewrote the three magic functions and the Clock and SDL examples are running (so both onWidgetDraw and renderWith) are working (ok, far away from exhaustive tests).

I now wanted (first in a fork of haskell-gi/haskell-gi, to not destroy anything):

  • Generate a version of gi-cairo which reexport "gi-cairo-base" and include the magic functions; also the cabal file must depend on gi-cairo-base. How to do that?

  • The last one is probably simple, add the dependency to pkg.info.

  • Can the other stuff be done inside the .overrides file or has the code of Haskell-gi to be changed?

What I would like most, if GI.Cairo.Context would be just equal Cairo or simply a type alias. This would spare the coerce stuff in the magic functions.

What would be even better (much!), if Haskell GI would do:

  • if a function has cairo_t * as a parameter, then lift the function into the Render monad.

I guess the last one is impossible without massive development in Haskell GI, right?


I also ported my labyrinth game to the new infrastructure. It is in the examples folder and uses all magic functions.

@garetxe
Copy link
Collaborator

garetxe commented Apr 21, 2018

Ok, now everything compiles.

Great, thanks for doing this!

Generate a version of gi-cairo which reexport "gi-cairo-base" and include the magic functions; also the cabal file must depend on gi-cairo-base. How to do that?

gi-cairo is a fairly special case, I think that the best thing is to write it by hand (copying the existing version). Currently the bindings depend on haskell-gi, but this is because of the cabal configure step uses some magic in haskell-gi to generate the bindings at cabal configure time. The generated bindings themselves do not depend on haskell-gi, only on haskell-gi-base.

So in a hand-written version of gi-cairo we only need to depend on haskell-gi-base. The simplest solution is then to simply merge gi-cairo-render with gi-cairo. This adds no new dependencies, and simplifies the story. I can do this if you agree. (So instead of ending up with gi-cairo-connector, gi-cairo-render and gi-cairo, we only have gi-cairo. This adds no new dependencies over gi-cairo-render itself.)

What I would like most, if GI.Cairo.Context would be just equal Cairo or simply a type alias.

I completely agree. To avoid special casing Cairo stuff in the code generator we would need to rename Cairo to GI.Cairo.Context, but this is a simple thing to do. I can do this when merging your gi-cairo-render with gi-cairo, if you like. (Another reason to do this is that Cairo.Context is more descriptive than Cairo.Cairo.)

What would be even better (much!), if Haskell GI would do:

  • if a function has cairo_t * as a parameter, then lift the function into the Render monad.

I guess the last one is impossible without massive development in Haskell GI, right?

No, I think this would be fairly easy to implement (although the interesting case is callbacks, not ordinary functions, right?). But making this automatically makes me uneasy: it is very easy now to slap a renderWithContext in the right place, but if we get it wrong it is painful for the user to get around it.

I also ported my labyrinth game to the new infrastructure. It is in the examples folder and uses all magic functions.

Great! The memory management in the new functions seems OK to me, so if they work in a non-trivial example they are probably correct.

@cohomology
Copy link
Collaborator Author

Perfect! Just merge it!

What about automatic tests? I didn't think about how to tests it, but actually we should have some automatic test which verify that we leak no memory.

What about the demos? Shall we translate the other too and put them into the examples folder or in gi-gtk-examples?

Best regards,
Kilian.

@garetxe
Copy link
Collaborator

garetxe commented Apr 22, 2018

Perfect! Just merge it!

Great. I will import everything from your gi-cairo-render into the main haskell-gi repo (preserving history), and work from there. I just gave you write access to the main repo.

What about automatic tests? I didn't think about how to tests it, but actually we should have some automatic test which verify that we leak no memory.

Any ideas on how to do this? I have used valgrind in the past, but it's not easy to automatize.

What about the demos? Shall we translate the other too and put them into the examples folder or in gi-gtk-examples?

I think putting things in the examples folder is best, let's do that.

@cohomology
Copy link
Collaborator Author

Thank you!

I will wait until you commit and then port the examples and think about tests. I also think about porting cairo-svg. This will be very easy then.

@cohomology
Copy link
Collaborator Author

cohomology commented May 7, 2018

Hi,

I did not come very far the last days, due to time constraints. I had a few minutes today and tried to further evolve my branch gi-cairo-cleanup.

I now got compile errors in GI/Gdk/Objects/Screen.hs:971:21

  • Could not deduce (WrappedPtr Cairo.FontOptions.FontOptions)
        arising from a use of ‘newPtr’
      from the context: (B.CallStack.HasCallStack, MonadIO m, IsScreen a)
        bound by the type signature for:
                   screenGetFontOptions :: (B.CallStack.HasCallStack, MonadIO m,
                                            IsScreen a) =>
                                           a -> m (Maybe Cairo.FontOptions.FontOptions)
        at GI/Gdk/Objects/Screen.hs:(960,1)-(964,46)
    • In a stmt of a 'do' block:
        result'' <- (newPtr Cairo.FontOptions.FontOptions) result'
      In the expression:
        do { result'' <- (newPtr Cairo.FontOptions.FontOptions) result';
             return result'' }
      In the second argument of ‘($)’, namely
        ‘\ result'
           -> do { result'' <- (newPtr Cairo.FontOptions.FontOptions) result';
                   return result'' }’

This instance should have come out of the generated source, right? Do you have a clue, why it isn't there?

Best regards,
Cohomology

P.S.: I understand it now. There are two conflicting declarations for FontOptions. I will fix it!

@cohomology
Copy link
Collaborator Author

cohomology commented May 8, 2018

Hi Inaki,

can you comment on this? I do not understand it at all. It does also not work with the plain generated cairo bindings, if I generate the bindings from hand (e.g. with haskell-gi). It does work when using cabal on the whole of haskell-gi (e.g. cabal new-build). This is why your standalone version of gi-cairo does not build (see my branch). So this whole stuff is a bug (?) in haskell-gi and has nothing to do with gi-cairo-render.

Here are the details:

In GI.Gdk/Objects/Screen.hs there is a function screenGetFontOptions. This function inherently wants Cairo.FontOptions to be of class WrappedPtr. If I generate the bindings via cabal, this instance is provided in FontOptions.hs so:

instance WrappedPtr FontOptions where
    wrappedPtrCalloc = return nullPtr
    wrappedPtrCopy = return
    wrappedPtrFree = Nothing 

This seems wrong to me and probably leaks memory.

When I generate the bindings from hand via haskell-gi tool (like you did when creating the separate library in /cairo), then the file looks different. Instead of WrappedPtr, and instance to BoxedPtr is provided. This looks better to me, but this does not build Gdk, i.e. it gives compile errors in screenGetFontOptions.

You can test it, by checking out my branch and typing cabal new-build all.

@garetxe
Copy link
Collaborator

garetxe commented May 9, 2018

Thanks for the analysis! I am a little bit puzzled why Gdk would think that FontOptions is not boxed. I suspect there is a bug here, I saw some weird related behavior recently. I will investigate further as soon as I can.

@cohomology
Copy link
Collaborator Author

What is even worse is, that when building via cabal the boxed type is not created. This means that all real users will get memory leaks, right?

@garetxe
Copy link
Collaborator

garetxe commented May 9, 2018

Here is what screenGetFontOptions is on my computer:

screenGetFontOptions ::
    (B.CallStack.HasCallStack, MonadIO m, IsScreen a) =>
    a
    {- ^ /@screen@/: a 'GI.Gdk.Objects.Screen.Screen' -}
    -> m (Maybe Cairo.FontOptions.FontOptions)
    {- ^ __Returns:__ the current font options, or 'Nothing' if no
 default font options have been set. -}
screenGetFontOptions screen = liftIO $ do
    screen' <- unsafeManagedPtrCastPtr screen
    result <- gdk_screen_get_font_options screen'
    maybeResult <- convertIfNonNull result $ \result' -> do
        result'' <- (newBoxed Cairo.FontOptions.FontOptions) result'
        return result''
    touchManagedPtr screen
    return maybeResult

You see that it assumes that FontOptions is boxed, which is correct. But haskell-gi will only know this if it is stated in the cairo-1.0.gir file, which in my system does assert this:

    <record name="FontOptions" c:type="cairo_font_options_t" foreign="1"
	    glib:type-name="CairoFontOptions"
	    glib:get-type="cairo_gobject_font_options_get_type"/>

What does it say on your system? (In my system this is at /usr/share/gir-1.0/cairo-1.0.gir)

@garetxe
Copy link
Collaborator

garetxe commented May 9, 2018

You can test it, by checking out my branch and typing cabal new-build all.

I just did this, and gi-gdk builds fine for me. So this suggests again some difference in something external, probably the cairo-1.0.gir file I mention above.

@cohomology
Copy link
Collaborator Author

cohomology commented May 10, 2018

In my system it shows:

<record name="FontOptions" c:type="cairo_font_options_t" foreign="1"/> 

I have libgirepository-1.0.dev, 1.50.0-1+b1 in Debian 9.

@garetxe
Copy link
Collaborator

garetxe commented May 10, 2018

OK, that explains the difference between what I see and what you see. haskell-gi will only be able to determine that the struct is boxed if the glib:get-type attribute is set. And if it cannot figure out it is boxed, it will just assume that it is an opaque pointer, and leak memory, as you say. This leak is not a bug in haskell-gi: it is doing the best it can do without any kind of information regarding allocation/deallocation.

I just need to figure out how to patch things up so gi-gdk in your system thinks that Cairo.FontOptions is boxed. It should be doable with overrides, but somehow the obvious thing I am trying is not working, I need to investigate a bit further.

@garetxe
Copy link
Collaborator

garetxe commented May 10, 2018

Ouch, I was just making a silly typo. An override works just fine, try adding the following to Gdk/Gdk.overrides (and make sure that the bindings are regenerated, you can delete the Gdk/Gdk/GI folder)

set-attr cairo/FontOptions glib:get-type cairo_gobject_font_options_get_type

@cohomology
Copy link
Collaborator Author

cohomology commented May 14, 2018

My comment in the pull request was concerned with the following. I did:

  1. Clone the repository git clone https://github.com/haskell-gi/haskell-gi.
  2. Edit bindings/cairo/pkg.info and increase the cairo version to, e.g. 1.0.17.
  3. Type cabal new-build all.
  4. Expected: cairo version 1.0.17 is build.
    Actual: cairo version 1.0.16 is builld.

This is, at least, surprising.

@garetxe
Copy link
Collaborator

garetxe commented May 15, 2018

This is, at least, surprising.

Oh, I see! This is indeed badly documented. The issue is that you need to generate the appropriate .cabal files for the bindings before building:

$ cd bindings
$ cabal new-run genBuildInfo $(./PKGS.sh)

Then cabal new-build should pick up the autogenerated gi-*.cabal files, and things should work as expected.

@cohomology
Copy link
Collaborator Author

cohomology commented May 15, 2018

Oh, thanks! Of course you need to have already haskell-gi installed such that cabal new-run genBuildInfo works, right?

What is really missing for me to develop haskell-gi is some reliable incremental build script, say build.sh with the following three properties:

  1. It reliable builds all of haskell-gi and its bindings without pulling some parts of it from hackage accidentally (of course, external dependencies should be pulled from hackage).
  2. It is incremental, e.g. if I change only, say Gtk/pkg.info then only Gtk is rebuild.
  3. It is reproducable

The steps in travis.yml come close to fulfill 1, although you would probably use a sandboxed environment on a local machine and I experienced that it is possible that he pulls parts of haskell-gi from hackage if something fails locally.

The more I use cabal, the more I feel that cabal is by design not able to provide 1., 2. and 3., even in its new - mode. Perhaps stack or even some external build tool, like GNU make?

Do you have any clue or am I simply too stupid to use cabal ;-) ?

P.S.: Can you push your new version of gtk?

@garetxe
Copy link
Collaborator

garetxe commented May 16, 2018

Of course you need to have already haskell-gi installed such that cabal new-run genBuildInfo works, right?

No, it should not be necessary (I just checked just in case :) ).

What is really missing for me to develop haskell-gi is some reliable incremental build script, say build.sh with the following three properties:

I think the following should work as you want:

$ cd bindings
$ cabal new-run genBuildInfo $(./PKGS.sh)
$ cd ..
$ cabal new-build

The genBuildInfo step is only necessary after a fresh checkout, to generate the gi-* files (or when you bump a version in pkg.info), and not needed during normal development, for which cabal new-build should suffice.

The build is not entirely reproducible, in that it will depend on the latest versions of hackage packages (if you want to avoid this locally, you can use cabal new-freeze), and the globally installed versions of the libraries being wrapped (but this is by design).

If you forget to run genBuildInfo after a fresh checkout it is true that cabal may pull the missing gi-* versions from hackage, but this is hardly cabal's fault :) It's just that the way the repo is organized is a little bit unconventional, in that many .cabal files are autogenerated. A BUILDING document with instructions would certainly help newcomers here.

P.S.: Can you push your new version of gtk?

Thanks for the reminder, done!

@garetxe
Copy link
Collaborator

garetxe commented May 16, 2018

I just changed some things around that should make the experience of using cabal new-build slightly better. Now if you try to run cabal new-build on the top directory right after cloning the repo, it will not work (instead of silently pulling whatever is the latest version in hackage). There is also a comment in cabal.project explaining what to do.

Definitely not perfect, but at least less misleading.

With this in place, and once you generate the gi-*.cabal files once, it should be rather straightforward to use cabal new-build for development, I think.

@cohomology
Copy link
Collaborator Author

cohomology commented May 18, 2018

Works like a charm. Of course, haskell-gi is built 2 times with this procedure. But that's okay for me.

@cohomology
Copy link
Collaborator Author

cohomology commented May 18, 2018

I guess before releasing the new version of cairo, we have to fix the old one, so that the generated sources are correct.

I now have a problem with Path.hs. This is still generated with WrappedPtr.

If I look in cairo.h on my system, there is a function cairo_path_destroy which could be used to release the memory of this object.

If I look in cairo-gobject.h, there is a function cairo_gobject_path_data_type_get_type. But this is concerned to PathDataType, a different object.

So two questions:

  1. Which overrides do I need that haskell-gi makes automatic memory management via cairo_path_destroy, i.e. generates something like newPtr.
  2. Which is the best documentation on the cairo_gobject c-calls? How is it ensured that I do not wrongly annotate, e.g. Path with cairo_gobject_matrix_get_type? These c-calls don't seem very type safe. (Ok, I looked now at the C code, which is very straightforward.)

@cohomology
Copy link
Collaborator Author

cohomology commented May 18, 2018

The more I understand about this Path in the C source code, the more I think it should be disabled in Haskell GI.

You can get the Path (i.e. first move from a to b, then circle to c, then line to d, etc.) from a cairo_t. It's a kind of C style array of the uggliest kind. It's only purpose is that you manipulate it with C style array operations, e.g. create your own path segments and then use cairo_append_path to append them to the path of a given context.

There is no gobject data, because the memory management is obscure.

1st possibility:
If you build your own path via C, then you manage the memory by yourself. At the end you call cairo_append_path to append the path to a given Cairo context (and Cairo copies it, managing the memory of the copy inside the context).

  1. possibility:
    If you call cairo_copy_path, then you get a copy from the path of a Cairo context and Cairo manages the memory of the path, so you must call cairo_path_destroy at the end.

The corresponding code to actually use Paths was commented out in the gtk2hs library. The author thought about a nice abstraction, but this would not be that easy to write.

But there is nearly no use for all this functionality, because a path can be directly constructed inside a cairo_t context, e.g. by calling the drawing primitives on the context directly.

And if we write an abstraction, this should not come out of the generated code, which is useless.

@garetxe
Copy link
Collaborator

garetxe commented May 19, 2018

I agree completely. It sounds reasonable to me to omit Cairo.Path from the cairo bindings, I doubt that anyone is using it.

If someone decides to implement a nicer Haskell abstraction on top of that that would be great, of course, but I don't think that should be a priority right now.

@cohomology
Copy link
Collaborator Author

In my Debian 9 installation the cairo-1.0.gir file contains:

<record name="FontType" c:type="cairo_font_type_t" foreign="1"/> 

Upstream it equals:

<enumeration name="FontType" c:type="cairo_font_type_t"
	 glib:type-name="cairo_font_type_t"
	 glib:get-type="cairo_gobject_font_type_get_type">
  <member name="toy"
      value="0"
      c:identifier="CAIRO_FONT_TYPE_TOY"/>
  <member name="ft"
      value="1"
      c:identifier="CAIRO_FONT_TYPE_FT"/>
  <member name="win32"
      value="2"
      c:identifier="CAIRO_FONT_TYPE_WIN32"/>
  <member name="quartz"
      value="3"
      c:identifier="CAIRO_FONT_TYPE_QUARTZ"/>
  <member name="user"
      value="4"
      c:identifier="CAIRO_FONT_TYPE_USER"/>
</enumeration> 

is there an override for that? It even changes from structure to enumeration. Why is Debians .gir file such a mess?

@garetxe
Copy link
Collaborator

garetxe commented Jun 1, 2018

is there an override for that? It even changes from structure to enumeration.

It can be fixed with overrides (since xml nodes can be arbitrarily added/removed), but it would be a mess... Perhaps the best thing would be to just include the Fedora version of the .gir file with the bindings (or some slightly fixed version of that).

We don't do that generally, since the API of libraries do change, but in this case it may be OK. Here is the copy in my system says:

<?xml version="1.0"?>
<repository version="1.2"
	    xmlns="http://www.gtk.org/introspection/core/1.0"
	    xmlns:c="http://www.gtk.org/introspection/c/1.0"
	    xmlns:glib="http://www.gtk.org/introspection/glib/1.0">
  <package name="cairo-gobject"/>
  <namespace name="cairo" version="1.0"
	     shared-library="libcairo-gobject.so.2"
	     c:identifier-prefixes="cairo"
	     c:symbol-prefixes="cairo">
    <record name="Context" c:type="cairo_t" foreign="1"
	    glib:type-name="CairoContext"
	    glib:get-type="cairo_gobject_context_get_type"/>
    <record name="Device" c:type="cairo_device_t" foreign="1"
	    glib:type-name="CairoDevice"
	    glib:get-type="cairo_gobject_device_get_type"/>
    <record name="Surface" c:type="cairo_surface_t" foreign="1"
	    glib:type-name="CairoSurface"
	    glib:get-type="cairo_gobject_surface_get_type"/>
    <record name="Matrix" c:type="cairo_matrix_t" foreign="1"/>
    <record name="Pattern" c:type="cairo_pattern_t" foreign="1"
	    glib:type-name="CairoPattern"
	    glib:get-type="cairo_gobject_pattern_get_type"/>
    <record name="Region" c:type="cairo_region_t" foreign="1"
	    glib:type-name="CairoRegion"
	    glib:get-type="cairo_gobject_region_get_type"/>
    <enumeration name="Status" c:type="cairo_status_t"
		 glib:type-name="cairo_status_t"
		 glib:get-type="cairo_gobject_status_get_type">
      <member name="success"
	      value="0"
	      c:identifier="CAIRO_STATUS_SUCCESS"/>
      <member name="no_memory"
	      value="1"
	      c:identifier="CAIRO_STATUS_NO_MEMORY"/>
      <member name="invalid_restore"
	      value="2"
	      c:identifier="CAIRO_STATUS_INVALID_RESTORE"/>
      <member name="invalid_pop_group"
	      value="3"
	      c:identifier="CAIRO_STATUS_INVALID_POP_GROUP"/>
      <member name="no_current_point"
	      value="4"
	      c:identifier="CAIRO_STATUS_NO_CURRENT_POINT"/>
      <member name="invalid_matrix"
	      value="5"
	      c:identifier="CAIRO_STATUS_INVALID_MATRIX"/>
      <member name="invalid_status"
	      value="6"
	      c:identifier="CAIRO_STATUS_INVALID_STATUS"/>
      <member name="null_pointer"
	      value="7"
	      c:identifier="CAIRO_STATUS_NULL_POINTER"/>
      <member name="invalid_string"
	      value="8"
	      c:identifier="CAIRO_STATUS_INVALID_STRING"/>
      <member name="invalid_path_data"
	      value="9"
	      c:identifier="CAIRO_STATUS_INVALID_PATH_DATA"/>
      <member name="read_error"
	      value="10"
	      c:identifier="CAIRO_STATUS_READ_ERROR"/>
      <member name="write_error"
	      value="11"
	      c:identifier="CAIRO_STATUS_WRITE_ERROR"/>
      <member name="surface_finished"
	      value="12"
	      c:identifier="CAIRO_STATUS_SURFACE_FINISHED"/>
      <member name="surface_type_mismatch"
	      value="13"
	      c:identifier="CAIRO_STATUS_SURFACE_TYPE_MISMATCH"/>
      <member name="pattern_type_mismatch"
	      value="14"
	      c:identifier="CAIRO_STATUS_PATTERN_TYPE_MISMATCH"/>
      <member name="invalid_content"
	      value="15"
	      c:identifier="CAIRO_STATUS_INVALID_CONTENT"/>
      <member name="invalid_format"
	      value="16"
	      c:identifier="CAIRO_STATUS_INVALID_FORMAT"/>
      <member name="invalid_visual"
	      value="17"
	      c:identifier="CAIRO_STATUS_INVALID_VISUAL"/>
      <member name="file_not_found"
	      value="18"
	      c:identifier="CAIRO_STATUS_FILE_NOT_FOUND"/>
      <member name="invalid_dash"
	      value="19"
	      c:identifier="CAIRO_STATUS_INVALID_DASH"/>
      <member name="invalid_dsc_comment"
	      value="20"
	      c:identifier="CAIRO_STATUS_INVALID_DSC_COMMENT"/>
      <member name="invalid_index"
	      value="21"
	      c:identifier="CAIRO_STATUS_INVALID_INDEX"/>
      <member name="clip_not_representable"
	      value="22"
	      c:identifier="CAIRO_STATUS_CLIP_NOT_REPRESENTABLE"/>
      <member name="temp_file_error"
	      value="23"
	      c:identifier="CAIRO_STATUS_TEMP_FILE_ERROR"/>
      <member name="invalid_stride"
	      value="24"
	      c:identifier="CAIRO_STATUS_INVALID_STRIDE"/>
      <member name="font_type_mismatch"
	      value="25"
	      c:identifier="CAIRO_STATUS_FONT_TYPE_MISMATCH"/>
      <member name="user_font_immutable"
	      value="26"
	      c:identifier="CAIRO_STATUS_USER_FONT_IMMUTABLE"/>
      <member name="user_font_error"
	      value="27"
	      c:identifier="CAIRO_STATUS_USER_FONT_ERROR"/>
      <member name="negative_count"
	      value="28"
	      c:identifier="CAIRO_STATUS_NEGATIVE_COUNT"/>
      <member name="invalid_clusters"
	      value="29"
	      c:identifier="CAIRO_STATUS_INVALID_CLUSTERS"/>
      <member name="invalid_slant"
	      value="30"
	      c:identifier="CAIRO_STATUS_INVALID_SLANT"/>
      <member name="invalid_weight"
	      value="31"
	      c:identifier="CAIRO_STATUS_INVALID_WEIGHT"/>
      <member name="invalid_size"
	      value="32"
	      c:identifier="CAIRO_STATUS_INVALID_SIZE"/>
      <member name="user_font_not_implemented"
	      value="33"
	      c:identifier="CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED"/>
      <member name="device_type_mismatch"
	      value="34"
	      c:identifier="CAIRO_STATUS_DEVICE_TYPE_MISMATCH"/>
      <member name="device_error"
	      value="35"
	      c:identifier="CAIRO_STATUS_DEVICE_ERROR"/>
      <member name="invalid_mesh_construction"
	      value="36"
	      c:identifier="CAIRO_STATUS_INVALID_MESH_CONSTRUCTION"/>
      <member name="device_finished"
	      value="37"
	      c:identifier="CAIRO_STATUS_DEVICE_FINISHED"/>
      <member name="jbig2_global_missing"
	      value="38"
	      c:identifier="CAIRO_STATUS_JBIG2_GLOBAL_MISSING"/>
    </enumeration>
    <enumeration name="Content" c:type="cairo_content_t"
		 glib:type-name="cairo_content_t"
		 glib:get-type="cairo_gobject_content_get_type">
      <member name="color"
	      value="4096"
	      c:identifier="CAIRO_CONTENT_COLOR"/>
      <member name="alpha"
	      value="8192"
	      c:identifier="CAIRO_CONTENT_ALPHA"/>
      <member name="color_alpha"
	      value="12288"
	      c:identifier="CAIRO_CONTENT_COLOR_ALPHA"/>
    </enumeration>
    <enumeration name="Operator" c:type="cairo_operator_t"
		 glib:type-name="cairo_operator_t"
		 glib:get-type="cairo_gobject_operator_get_type">
      <member name="clear"
	      value="0"
	      c:identifier="CAIRO_OPERATOR_CLEAR"/>
      <member name="source"
	      value="1"
	      c:identifier="CAIRO_OPERATOR_SOURCE"/>
      <member name="over"
	      value="2"
	      c:identifier="CAIRO_OPERATOR_OVER"/>
      <member name="in"
	      value="3"
	      c:identifier="CAIRO_OPERATOR_IN"/>
      <member name="out"
	      value="4"
	      c:identifier="CAIRO_OPERATOR_OUT"/>
      <member name="atop"
	      value="5"
	      c:identifier="CAIRO_OPERATOR_ATOP"/>
      <member name="dest"
	      value="6"
	      c:identifier="CAIRO_OPERATOR_DEST"/>
      <member name="dest_over"
	      value="7"
	      c:identifier="CAIRO_OPERATOR_DEST_OVER"/>
      <member name="dest_in"
	      value="8"
	      c:identifier="CAIRO_OPERATOR_DEST_IN"/>
      <member name="dest_out"
	      value="9"
	      c:identifier="CAIRO_OPERATOR_DEST_OUT"/>
      <member name="dest_atop"
	      value="10"
	      c:identifier="CAIRO_OPERATOR_DEST_ATOP"/>
      <member name="xor"
	      value="11"
	      c:identifier="CAIRO_OPERATOR_XOR"/>
      <member name="add"
	      value="12"
	      c:identifier="CAIRO_OPERATOR_ADD"/>
      <member name="saturate"
	      value="13"
	      c:identifier="CAIRO_OPERATOR_SATURATE"/>
      <member name="multiply"
	      value="14"
	      c:identifier="CAIRO_OPERATOR_MULTIPLY"/>
      <member name="screen"
	      value="15"
	      c:identifier="CAIRO_OPERATOR_SCREEN"/>
      <member name="overlay"
	      value="16"
	      c:identifier="CAIRO_OPERATOR_OVERLAY"/>
      <member name="darken"
	      value="17"
	      c:identifier="CAIRO_OPERATOR_DARKEN"/>
      <member name="lighten"
	      value="18"
	      c:identifier="CAIRO_OPERATOR_LIGHTEN"/>
      <member name="color_dodge"
	      value="19"
	      c:identifier="CAIRO_OPERATOR_COLOR_DODGE"/>
      <member name="color_burn"
	      value="20"
	      c:identifier="CAIRO_OPERATOR_COLOR_BURN"/>
      <member name="hard_light"
	      value="21"
	      c:identifier="CAIRO_OPERATOR_HARD_LIGHT"/>
      <member name="soft_light"
	      value="22"
	      c:identifier="CAIRO_OPERATOR_SOFT_LIGHT"/>
      <member name="difference"
	      value="23"
	      c:identifier="CAIRO_OPERATOR_DIFFERENCE"/>
      <member name="exclusion"
	      value="24"
	      c:identifier="CAIRO_OPERATOR_EXCLUSION"/>
      <member name="hsl_hue"
	      value="25"
	      c:identifier="CAIRO_OPERATOR_HSL_HUE"/>
      <member name="hsl_saturation"
	      value="26"
	      c:identifier="CAIRO_OPERATOR_HSL_SATURATION"/>
      <member name="hsl_color"
	      value="27"
	      c:identifier="CAIRO_OPERATOR_HSL_COLOR"/>
      <member name="hsl_luminosity"
	      value="28"
	      c:identifier="CAIRO_OPERATOR_HSL_LUMINOSITY"/>
    </enumeration>
    <enumeration name="Antialias" c:type="cairo_antialias_t"
		 glib:type-name="cairo_antialias_t"
		 glib:get-type="cairo_gobject_antialias_get_type">
      <member name="default"
	      value="0"
	      c:identifier="CAIRO_ANTIALIAS_DEFAULT"/>
      <member name="none"
	      value="1"
	      c:identifier="CAIRO_ANTIALIAS_NONE"/>
      <member name="gray"
	      value="2"
	      c:identifier="CAIRO_ANTIALIAS_GRAY"/>
      <member name="subpixel"
	      value="3"
	      c:identifier="CAIRO_ANTIALIAS_SUBPIXEL"/>
      <member name="fast"
	      value="4"
	      c:identifier="CAIRO_ANTIALIAS_FAST"/>
      <member name="good"
	      value="5"
	      c:identifier="CAIRO_ANTIALIAS_GOOD"/>
      <member name="best"
	      value="6"
	      c:identifier="CAIRO_ANTIALIAS_BEST"/>
    </enumeration>
    <enumeration name="FillRule" c:type="cairo_fill_rule_t"
		 glib:type-name="cairo_fill_rule_t"
		 glib:get-type="cairo_gobject_fill_rule_get_type">
      <member name="winding"
	      value="0"
	      c:identifier="CAIRO_FILL_RULE_WINDING"/>
      <member name="even_odd"
	      value="1"
	      c:identifier="CAIRO_FILL_RULE_EVEN_ODD"/>
    </enumeration>
    <enumeration name="LineCap" c:type="cairo_line_cap_t"
		 glib:type-name="cairo_line_cap_t"
		 glib:get-type="cairo_gobject_line_cap_get_type">
      <member name="butt"
	      value="0"
	      c:identifier="CAIRO_LINE_CAP_BUTT"/>
      <member name="round"
	      value="1"
	      c:identifier="CAIRO_LINE_CAP_ROUND"/>
      <member name="square"
	      value="2"
	      c:identifier="CAIRO_LINE_CAP_SQUARE"/>
    </enumeration>
    <enumeration name="LineJoin" c:type="cairo_line_join_t"
		 glib:type-name="cairo_line_join_t"
		 glib:get-type="cairo_gobject_line_join_get_type">
      <member name="miter"
	      value="0"
	      c:identifier="CAIRO_LINE_JOIN_MITER"/>
      <member name="round"
	      value="1"
	      c:identifier="CAIRO_LINE_JOIN_ROUND"/>
      <member name="bevel"
	      value="2"
	      c:identifier="CAIRO_LINE_JOIN_BEVEL"/>
    </enumeration>
    <enumeration name="TextClusterFlags" c:type="cairo_text_cluster_flags_t"
		 glib:type-name="cairo_text_cluster_flags_t"
		 glib:get-type="cairo_gobject_text_cluster_flags_get_type">
      <member name="backward"
	      value="1"
	      c:identifier="CAIRO_TEXT_CLUSTER_FLAG_BACKWARD"/>
    </enumeration>
    <enumeration name="FontSlant" c:type="cairo_font_slant_t"
		 glib:type-name="cairo_font_slant_t"
		 glib:get-type="cairo_gobject_font_slant_get_type">
      <member name="normal"
	      value="0"
	      c:identifier="CAIRO_FONT_SLANT_NORMAL"/>
      <member name="italic"
	      value="1"
	      c:identifier="CAIRO_FONT_SLANT_ITALIC"/>
      <member name="oblique"
	      value="2"
	      c:identifier="CAIRO_FONT_SLANT_OBLIQUE"/>
    </enumeration>
    <enumeration name="FontWeight" c:type="cairo_font_weight_t"
		 glib:type-name="cairo_font_weight_t"
		 glib:get-type="cairo_gobject_font_weight_get_type">
      <member name="normal"
	      value="0"
	      c:identifier="CAIRO_FONT_WEIGHT_NORMAL"/>
      <member name="bold"
	      value="1"
	      c:identifier="CAIRO_FONT_WEIGHT_BOLD"/>
    </enumeration>
    <enumeration name="SubpixelOrder" c:type="cairo_subpixel_order_t"
		 glib:type-name="cairo_subpixel_order_t"
		 glib:get-type="cairo_gobject_subpixel_order_get_type">
      <member name="default"
	      value="0"
	      c:identifier="CAIRO_SUBPIXEL_ORDER_DEFAULT"/>
      <member name="rgb"
	      value="1"
	      c:identifier="CAIRO_SUBPIXEL_ORDER_RGB"/>
      <member name="bgr"
	      value="2"
	      c:identifier="CAIRO_SUBPIXEL_ORDER_BGR"/>
      <member name="vrgb"
	      value="3"
	      c:identifier="CAIRO_SUBPIXEL_ORDER_VRGB"/>
      <member name="vbgr"
	      value="4"
	      c:identifier="CAIRO_SUBPIXEL_ORDER_VBGR"/>
    </enumeration>
    <enumeration name="HintStyle" c:type="cairo_hint_style_t"
		 glib:type-name="cairo_hint_style_t"
		 glib:get-type="cairo_gobject_hint_style_get_type">
      <member name="default"
	      value="0"
	      c:identifier="CAIRO_HINT_STYLE_DEFAULT"/>
      <member name="none"
	      value="1"
	      c:identifier="CAIRO_HINT_STYLE_NONE"/>
      <member name="slight"
	      value="2"
	      c:identifier="CAIRO_HINT_STYLE_SLIGHT"/>
      <member name="medium"
	      value="3"
	      c:identifier="CAIRO_HINT_STYLE_MEDIUM"/>
      <member name="full"
	      value="4"
	      c:identifier="CAIRO_HINT_STYLE_FULL"/>
    </enumeration>
    <enumeration name="HintMetrics" c:type="cairo_hint_metrics_t"
		 glib:type-name="cairo_hint_metrics_t"
		 glib:get-type="cairo_gobject_hint_metrics_get_type">
      <member name="default"
	      value="0"
	      c:identifier="CAIRO_HINT_METRICS_DEFAULT"/>
      <member name="off"
	      value="1"
	      c:identifier="CAIRO_HINT_METRICS_OFF"/>
      <member name="on"
	      value="2"
	      c:identifier="CAIRO_HINT_METRICS_ON"/>
    </enumeration>
    <record name="FontOptions" c:type="cairo_font_options_t" foreign="1"
	    glib:type-name="CairoFontOptions"
	    glib:get-type="cairo_gobject_font_options_get_type"/>
    <enumeration name="FontType" c:type="cairo_font_type_t"
		 glib:type-name="cairo_font_type_t"
		 glib:get-type="cairo_gobject_font_type_get_type">
      <member name="toy"
	      value="0"
	      c:identifier="CAIRO_FONT_TYPE_TOY"/>
      <member name="ft"
	      value="1"
	      c:identifier="CAIRO_FONT_TYPE_FT"/>
      <member name="win32"
	      value="2"
	      c:identifier="CAIRO_FONT_TYPE_WIN32"/>
      <member name="quartz"
	      value="3"
	      c:identifier="CAIRO_FONT_TYPE_QUARTZ"/>
      <member name="user"
	      value="4"
	      c:identifier="CAIRO_FONT_TYPE_USER"/>
    </enumeration>
    <enumeration name="PathDataType" c:type="cairo_path_data_type_t"
		 glib:type-name="cairo_path_data_type_t"
		 glib:get-type="cairo_gobject_path_data_type_get_type">
      <member name="move_to"
	      value="0"
	      c:identifier="CAIRO_PATH_MOVE_TO"/>
      <member name="line_to"
	      value="1"
	      c:identifier="CAIRO_PATH_LINE_TO"/>
      <member name="curve_to"
	      value="2"
	      c:identifier="CAIRO_PATH_CURVE_TO"/>
      <member name="close_path"
	      value="3"
	      c:identifier="CAIRO_PATH_CLOSE_PATH"/>
    </enumeration>
    <enumeration name="DeviceType" c:type="cairo_device_type_t"
		 glib:type-name="cairo_device_type_t"
		 glib:get-type="cairo_gobject_device_type_get_type">
      <member name="drm"
	      value="0"
	      c:identifier="CAIRO_DEVICE_TYPE_DRM"/>
      <member name="gl"
	      value="1"
	      c:identifier="CAIRO_DEVICE_TYPE_GL"/>
      <member name="script"
	      value="2"
	      c:identifier="CAIRO_DEVICE_TYPE_SCRIPT"/>
      <member name="xcb"
	      value="3"
	      c:identifier="CAIRO_DEVICE_TYPE_XCB"/>
      <member name="xlib"
	      value="4"
	      c:identifier="CAIRO_DEVICE_TYPE_XLIB"/>
      <member name="xml"
	      value="5"
	      c:identifier="CAIRO_DEVICE_TYPE_XML"/>
      <member name="cogl"
	      value="6"
	      c:identifier="CAIRO_DEVICE_TYPE_COGL"/>
      <member name="win32"
	      value="7"
	      c:identifier="CAIRO_DEVICE_TYPE_WIN32"/>
      <member name="invalid"
	      value="-1"
	      c:identifier="CAIRO_DEVICE_TYPE_INVALID"/>
    </enumeration>
    <enumeration name="SurfaceType" c:type="cairo_surface_type_t"
		 glib:type-name="cairo_surface_type_t"
		 glib:get-type="cairo_gobject_surface_type_get_type">
      <member name="image"
	      value="0"
	      c:identifier="CAIRO_SURFACE_TYPE_IMAGE"/>
      <member name="pdf"
	      value="1"
	      c:identifier="CAIRO_SURFACE_TYPE_PDF"/>
      <member name="ps"
	      value="2"
	      c:identifier="CAIRO_SURFACE_TYPE_PS"/>
      <member name="xlib"
	      value="3"
	      c:identifier="CAIRO_SURFACE_TYPE_XLIB"/>
      <member name="xcb"
	      value="4"
	      c:identifier="CAIRO_SURFACE_TYPE_XCB"/>
      <member name="glitz"
	      value="5"
	      c:identifier="CAIRO_SURFACE_TYPE_GLITZ"/>
      <member name="quartz"
	      value="6"
	      c:identifier="CAIRO_SURFACE_TYPE_QUARTZ"/>
      <member name="win32"
	      value="7"
	      c:identifier="CAIRO_SURFACE_TYPE_WIN32"/>
      <member name="beos"
	      value="8"
	      c:identifier="CAIRO_SURFACE_TYPE_BEOS"/>
      <member name="directfb"
	      value="9"
	      c:identifier="CAIRO_SURFACE_TYPE_DIRECTFB"/>
      <member name="svg"
	      value="10"
	      c:identifier="CAIRO_SURFACE_TYPE_SVG"/>
      <member name="os2"
	      value="11"
	      c:identifier="CAIRO_SURFACE_TYPE_OS2"/>
      <member name="win32_printing"
	      value="12"
	      c:identifier="CAIRO_SURFACE_TYPE_WIN32_PRINTING"/>
      <member name="quartz_image"
	      value="13"
	      c:identifier="CAIRO_SURFACE_TYPE_QUARTZ_IMAGE"/>
      <member name="script"
	      value="14"
	      c:identifier="CAIRO_SURFACE_TYPE_SCRIPT"/>
      <member name="qt"
	      value="15"
	      c:identifier="CAIRO_SURFACE_TYPE_QT"/>
      <member name="recording"
	      value="16"
	      c:identifier="CAIRO_SURFACE_TYPE_RECORDING"/>
      <member name="vg"
	      value="17"
	      c:identifier="CAIRO_SURFACE_TYPE_VG"/>
      <member name="gl"
	      value="18"
	      c:identifier="CAIRO_SURFACE_TYPE_GL"/>
      <member name="drm"
	      value="19"
	      c:identifier="CAIRO_SURFACE_TYPE_DRM"/>
      <member name="tee"
	      value="20"
	      c:identifier="CAIRO_SURFACE_TYPE_TEE"/>
      <member name="xml"
	      value="21"
	      c:identifier="CAIRO_SURFACE_TYPE_XML"/>
      <member name="skia"
	      value="22"
	      c:identifier="CAIRO_SURFACE_TYPE_SKIA"/>
      <member name="subsurface"
	      value="23"
	      c:identifier="CAIRO_SURFACE_TYPE_SUBSURFACE"/>
      <member name="cogl"
	      value="24"
	      c:identifier="CAIRO_SURFACE_TYPE_COGL"/>
    </enumeration>
    <enumeration name="Format" c:type="cairo_format_t"
		 glib:type-name="cairo_format_t"
		 glib:get-type="cairo_gobject_format_get_type">
      <member name="invalid"
	      value="-1"
	      c:identifier="CAIRO_FORMAT_INVALID"/>
      <member name="argb32"
	      value="0"
	      c:identifier="CAIRO_FORMAT_ARGB32"/>
      <member name="rgb24"
	      value="1"
	      c:identifier="CAIRO_FORMAT_RGB24"/>
      <member name="a8"
	      value="2"
	      c:identifier="CAIRO_FORMAT_A8"/>
      <member name="a1"
	      value="3"
	      c:identifier="CAIRO_FORMAT_A1"/>
      <member name="rgb16_565"
	      value="4"
	      c:identifier="CAIRO_FORMAT_RGB16_565"/>
      <member name="rgb30"
	      value="5"
	      c:identifier="CAIRO_FORMAT_RGB30"/>
    </enumeration>
    <enumeration name="PatternType" c:type="cairo_pattern_type_t"
		 glib:type-name="cairo_pattern_type_t"
		 glib:get-type="cairo_gobject_pattern_type_get_type">
      <member name="solid"
	      value="0"
	      c:identifier="CAIRO_PATTERN_TYPE_SOLID"/>
      <member name="surface"
	      value="1"
	      c:identifier="CAIRO_PATTERN_TYPE_SURFACE"/>
      <member name="linear"
	      value="2"
	      c:identifier="CAIRO_PATTERN_TYPE_LINEAR"/>
      <member name="radial"
	      value="3"
	      c:identifier="CAIRO_PATTERN_TYPE_RADIAL"/>
      <member name="mesh"
	      value="4"
	      c:identifier="CAIRO_PATTERN_TYPE_MESH"/>
      <member name="raster_source"
	      value="5"
	      c:identifier="CAIRO_PATTERN_TYPE_RASTER_SOURCE"/>
    </enumeration>
    <enumeration name="Extend" c:type="cairo_extend_t"
		 glib:type-name="cairo_extend_t"
		 glib:get-type="cairo_gobject_extend_get_type">
      <member name="none"
	      value="0"
	      c:identifier="CAIRO_EXTEND_NONE"/>
      <member name="repeat"
	      value="1"
	      c:identifier="CAIRO_EXTEND_REPEAT"/>
      <member name="reflect"
	      value="2"
	      c:identifier="CAIRO_EXTEND_REFLECT"/>
      <member name="pad"
	      value="3"
	      c:identifier="CAIRO_EXTEND_PAD"/>
    </enumeration>
    <enumeration name="Filter" c:type="cairo_filter_t"
		 glib:type-name="cairo_filter_t"
		 glib:get-type="cairo_gobject_filter_get_type">
      <member name="fast"
	      value="0"
	      c:identifier="CAIRO_FILTER_FAST"/>
      <member name="good"
	      value="1"
	      c:identifier="CAIRO_FILTER_GOOD"/>
      <member name="best"
	      value="2"
	      c:identifier="CAIRO_FILTER_BEST"/>
      <member name="nearest"
	      value="3"
	      c:identifier="CAIRO_FILTER_NEAREST"/>
      <member name="bilinear"
	      value="4"
	      c:identifier="CAIRO_FILTER_BILINEAR"/>
      <member name="gaussian"
	      value="5"
	      c:identifier="CAIRO_FILTER_GAUSSIAN"/>
    </enumeration>
    <enumeration name="RegionOverlap" c:type="cairo_region_overlap_t"
		 glib:type-name="cairo_region_overlap_t"
		 glib:get-type="cairo_gobject_region_overlap_get_type">
      <member name="in"
	      value="0"
	      c:identifier="CAIRO_REGION_OVERLAP_IN"/>
      <member name="out"
	      value="1"
	      c:identifier="CAIRO_REGION_OVERLAP_OUT"/>
      <member name="part"
	      value="2"
	      c:identifier="CAIRO_REGION_OVERLAP_PART"/>
    </enumeration>
    <record name="FontFace" c:type="cairo_font_face_t" foreign="1"
	    glib:type-name="CairoFontFace"
	    glib:get-type="cairo_gobject_font_face_get_type"/>
    <record name="ScaledFont" c:type="cairo_scaled_font_t" foreign="1"
	    glib:type-name="CairoScaledFont"
	    glib:get-type="cairo_gobject_scaled_font_get_type"/>
    <record name="Path" c:type="cairo_path_t" foreign="1"/>
    <record name="RectangleInt" c:type="cairo_rectangle_int_t"
	    glib:type-name="CairoRectangleInt"
	    glib:get-type="cairo_gobject_rectangle_int_get_type">
      <field name="x" writable="1">
	<type name="gint" c:type="gint"/>
      </field>
      <field name="y" writable="1">
	<type name="gint" c:type="gint"/>
      </field>
      <field name="width" writable="1">
	<type name="gint" c:type="gint"/>
      </field>
      <field name="height" writable="1">
	<type name="gint" c:type="gint"/>
      </field>
    </record>
    <function name="image_surface_create" c:identifier="cairo_image_surface_create">
      <return-value transfer-ownership="none">
	<type name="none" c:type="void"/>
      </return-value>
      <parameters>
      </parameters>
    </function>
  </namespace>
</repository>

@garetxe garetxe changed the title Some thoughts from converting a project from gtk2hs to Haskell GI Make a haskell-gi based version of the cairo library Jun 10, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants