www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.announce - Native GTK2 D Bindings

reply Artur Skawina <art.08.09 gmail.com> writes:
                   Native GTK2 bindings for D.


"Native" for two reasons:

1. Uses the C API directly. No class wrappers and no function wrappers.
2. OO interface, giving a native D look and feel.


The code generated when using these bindings is identical to the
equivalent C version. Bit-for-bit, there is zero overhead. [1]


BINDINGS

The D modules in this package give access to:

 GLib-2.0
 GModule-2.0
 GObject-2.0
 Gio-2.0
 GdkPixbuf-2.0
 Pango-1.0
 Gdk-2.0
 Atk-1.0
 Gtk-2.0
 
There is also a minimal <cairo-1.0> stub, which only allows the GTK
stack to build, but isn't really usable.

[...more info in the README.txt file...]

---------------------------------------------------------------------------------

This is a *very* early release. Works for me (tm), but i've only tried the basic
functionality so far. I don't know how much time I'll be able to spend on this
in the next days and as it's already in a usable state. maybe somebody else
wants to play with it already. At this point i'm mostly interested in API
related thoughts -- i'd like to get it right *before* adding GTK3 support.

The code is available at http://repo.or.cz/w/girtod.git

The bindings are in the "gtk2" branch; the "master" branch has the tool used
to generate all the gtk2/*.d modules, some examples and a README.
 
Yes, there's even a section in there on "Why?" and the answer isn't NIH. :)


The gtk2 package (from the "gtk2" branch) can be used as-is, but if you'd like
to play with (re-)generating the bindings:

- The makefile is just an example of how to use the thing. It's GDC specific,
  x86 specific; the "configure" machinery still needs to be done. But it
  should be functional enough to rebuild the gtk2/*.d modules, with a few local
  tweaks. Which can then be moved and used in your application directly
  (place them in a "gtk2" subdirectory).
- See ./buildem for how to build the bindings.


artur
Jan 22 2012
next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 1/22/2012 1:54 PM, Artur Skawina wrote:
                     Native GTK2 bindings for D.


 "Native" for two reasons:

 1. Uses the C API directly. No class wrappers and no function wrappers.
This makes me suspect that part of this would be an ideal candidate for inclusion in the Deimos project: https://github.com/D-Programming-Deimos/
Jan 22 2012
prev sibling next sibling parent reply Trass3r <un known.com> writes:
The function names should be converted to camelCase.

 and a README.
gdc does cross module inlining if you pass all modules to it at once.
Jan 22 2012
parent reply Artur Skawina <art.08.09 gmail.com> writes:
On 01/23/12 00:16, Trass3r wrote:
 The function names should be converted to camelCase.
No. I named it "native" for a reason. The method names are not manipulated in any way - they come directly from GTK. I could *add* all kind of aliases, including camelCased ones, but why would anyone want to use those?
 gdc does cross module inlining if you pass all modules to it at once.
 
As i mentioned in the README; but that's not really a solution. For me, LTO works for all apps using these bindings, but i added that footnote so that i could write "zero-cost" and not have people complain that the calls are not going directly to the PLT (as they do in C). :) artur
Jan 22 2012
parent reply Trass3r <un known.com> writes:
 No. I named it "native" for a reason. The method names are not  
 manipulated in any way - they come directly from GTK.

 I could *add* all kind of aliases, including camelCased ones, but why  
 would anyone want to use those?
Cause those C names with underscores are just crappy.
 gdc does cross module inlining if you pass all modules to it at once.
As i mentioned in the README; but that's not really a solution.
No, in there you claim gdc didn't support cross module inlining.
Jan 22 2012
next sibling parent Trass3r <un known.com> writes:
 No, in there you claim gdc didn't support cross module inlining.
Or at least it suggests that: "As long as GDC cross module inlining doesn't work"
Jan 22 2012
prev sibling parent reply Artur Skawina <art.08.09 gmail.com> writes:
On 01/23/12 00:52, Trass3r wrote:
 No. I named it "native" for a reason. The method names are not manipulated in
any way - they come directly from GTK.

 I could *add* all kind of aliases, including camelCased ones, but why would
anyone want to use those?
Cause those C names with underscores are just crappy.
Well, it would be a trivial addition. I'd just like to avoid having several conventions around, unless necessary. I'd be more interested in comments on how to make the code using these bindings cleaner (the other meaning of "native" is "as-close- -to-natural-D-as-possible"). So far, a sample D GTK app looks like this: http://repo.or.cz/w/girtod.git/blob/refs/heads/master:/example_gtk3.d Anything I could change API-wise to improve things, that doesn't require language changes?
 gdc does cross module inlining if you pass all modules to it at once.
As i mentioned in the README; but that's not really a solution.
No, in there you claim gdc didn't support cross module inlining.
From the README:
 (apparently, another workaround is to build all the *.d files together;
 I've never tried this)
It's a workaround, not a solution. artur
Jan 22 2012
parent reply bearophile <bearophileHUGS lycos.com> writes:
Artur Skawina:

 So far, a sample D GTK app looks like this:
 
 http://repo.or.cz/w/girtod.git/blob/refs/heads/master:/example_gtk3.d
 
 Anything I could change API-wise to improve things, that doesn't
 require language changes?
The convention for D classes/structs/enums is CamelCase, while functions and methods are the same but start with a lowercase. This line of code seems an example for people that like named arguments in D: gtk.init(null,null); Such lines of code contains a bad cast: set_title(cast(char*)"Hello World!"); but.signal_connect!"button-press-event"(&bpress_cb, cast(void*)label); I think toStringz shouldn't return a const pointer: str0 = cast(char*)toStringz(display_string); Bye, bearophile
Jan 22 2012
parent reply Artur Skawina <art.08.09 gmail.com> writes:
On 01/23/12 03:17, bearophile wrote:
 Artur Skawina:
 
 So far, a sample D GTK app looks like this:

 http://repo.or.cz/w/girtod.git/blob/refs/heads/master:/example_gtk3.d

 Anything I could change API-wise to improve things, that doesn't
 require language changes?
The convention for D classes/structs/enums is CamelCase, while functions and methods are the same but start with a lowercase.
There is no such thing as a language mandated identifier naming convention. If you think otherwise - make the compiler enforce it. :)
 This line of code seems an example for people that like named arguments in D:
 gtk.init(null,null);
This has nothing to do with named arguments, it's gtk wanting to parse and modify the C argv[], which i didn't want to rebuild in these examples. /* void gtk_init(/*inout*/ int* argc, /*inout*/ char*** argv=null);*/ A function which handles the argv conversion both ways would be a good idea. On the list. Thanks.
 Such lines of code contains a bad cast:
 set_title(cast(char*)"Hello World!");
If you had seen the glib code i was initially testing with... :) Unfortunately the immutable-strings don't mix well with non-D APIs, especially, like in this case, when practically all const annotations are lost in translation (the GIR files are already missing them). Initially, i wanted to add a cstring type which would handle all the casts and zero-termination transparently, but then decided to see how things will work without that kind of magic. Not only would most strings have to be duped, but an extra reference to the copies would be needed, in case the string escaped. Requiring explicit casts for most strings is ugly, but not something that can be fixed both safely and cheaply. For a GUI toolkit the amount of casts needed may be acceptable. [1] I did remove the glib-using code from the repo because it made D look bad...
 but.signal_connect!"button-press-event"(&bpress_cb, cast(void*)label);
Here <label> is really an opaque (void*) that gets passed to the callback. I don't see a way to get rid of the cast, while improving type safety...
 I think toStringz shouldn't return a const pointer:
 str0 = cast(char*)toStringz(display_string);
(immutable(char)*) C APIs are not exactly common. :) IOW i think you're right.
 Bye,
 bearophile
Thanks, artur [1] Yes, this *will* cause bugs. But what's the alternative? A strongly typed C-string type would help catching most non-zero-terminated strings, but would need casts too (eg when initialized from literals) and still not help with the escaping references (libraries keeping the pointer around internally).
Jan 22 2012
next sibling parent Trass3r <un known.com> writes:
 The convention for D classes/structs/enums is CamelCase, while  
 functions and methods are the same but start with a lowercase.
There is no such thing as a language mandated identifier naming convention.
It's not mandated, it's standard. http://www.d-programming-language.org/dstyle.html
 A function which handles the argv conversion both ways would be a good
 idea. On the list. Thanks.
I think there was a thread recently which described a way to get ahold of argv.
Jan 23 2012
prev sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Artur Skawina:

 There is no such thing as a language mandated identifier naming convention.
 If you think otherwise - make the compiler enforce it. :)
There is a D style guide.
 This line of code seems an example for people that like named arguments in D:
 gtk.init(null,null);
This has nothing to do with named arguments,
It's an example of code where named arguments are useful for the person that reads the code to know what those two arguments are :-)
 Requiring explicit casts for most strings is ugly, but not something
 that can be fixed both safely and cheaply. For a GUI toolkit the
 amount of casts needed may be acceptable. [1]
But a string is like a 2-tuple, it's not a pointer. It's not meant to allow a cast to pointer. So it's better to cast the ".ptr" of an array: cast(char*)"Hello World!".ptr Bye, bearophile
Jan 23 2012
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 01/24/2012 12:18 AM, bearophile wrote:
 Artur Skawina:

 There is no such thing as a language mandated identifier naming convention.
 If you think otherwise - make the compiler enforce it. :)
There is a D style guide.
 This line of code seems an example for people that like named arguments in D:
 gtk.init(null,null);
This has nothing to do with named arguments,
It's an example of code where named arguments are useful for the person that reads the code to know what those two arguments are :-)
 Requiring explicit casts for most strings is ugly, but not something
 that can be fixed both safely and cheaply. For a GUI toolkit the
 amount of casts needed may be acceptable. [1]
But a string is like a 2-tuple, it's not a pointer. It's not meant to allow a cast to pointer. So it's better to cast the ".ptr" of an array: cast(char*)"Hello World!".ptr Bye, bearophile
This is wrong. String literals are meant to allow a cast to a pointer. String literals *implicitly* convert to immutable(char)*. They are designed to simultaneously be D style strings and C style strings. There is really no need for that .ptr there. It is just noise.
Jan 23 2012
prev sibling next sibling parent Gour <gour atmarama.net> writes:
On Sun, 22 Jan 2012 22:54:03 +0100
Artur Skawina <art.08.09 gmail.com> wrote:

 1. Uses the C API directly. No class wrappers and no function
 wrappers. 2. OO interface, giving a native D look and feel.
=20
=20
 The code generated when using these bindings is identical to the
 equivalent C version. Bit-for-bit, there is zero overhead. [1]
It is very nice to see that the GUI world of D is moving forward!
 Yes, there's even a section in there on "Why?" and the answer isn't
 NIH. :)
...although it would be nice to save breadth and work together. Hopefully your project will stay maintained and developed further. Sincerely, Gour --=20 In the material world, one who is unaffected by whatever good=20 or evil he may obtain, neither praising it nor despising it,=20 is firmly fixed in perfect knowledge. http://atmarama.net | Hlapicina (Croatia) | GPG: 52B5C810
Jan 23 2012
prev sibling parent reply "Kagamin" <spam here.lot> writes:
On Sunday, 22 January 2012 at 21:56:24 UTC, Artur Skawina wrote:
                  Native GTK2 bindings for D.


 "Native" for two reasons:

 1. Uses the C API directly. No class wrappers and no function 
 wrappers.
 2. OO interface, giving a native D look and feel.
Once again. Is it direct C bindings OR OO interface OR both?
Jan 24 2012
parent reply Artur Skawina <art.08.09 gmail.com> writes:
On 01/24/12 12:15, Kagamin wrote:
 On Sunday, 22 January 2012 at 21:56:24 UTC, Artur Skawina wrote:
                  Native GTK2 bindings for D.


 "Native" for two reasons:

 1. Uses the C API directly. No class wrappers and no function wrappers.
 2. OO interface, giving a native D look and feel.
Once again. Is it direct C bindings OR OO interface OR both?
Both. There are basically three parts: 1) data type definitions - ie structs, aliases (typedefs), and enums (constants) 2) C prototypes; this, plus (1) above, let's you use the C API directly 3) "methods", which are actually disguised GTK functions. An example: struct Rectangle { int x, y, width, height; // Calculates the intersection of two rectangles. It is allowed for // do not intersect, dest's width and height is set to 0 and its x // and y values are undefined. If you are only interested in whether // the rectangles intersect, but not in the intersecting area itself, // pass %NULL for dest. // RETURNS: %TRUE if the rectangles intersect. // <src2>: a #GdkRectangle // <dest>: return location for the intersection of src1 and src2, or %NULL int intersect(Rectangle* src2, /*out*/ Rectangle* dest=null) { return gdk_rectangle_intersect(&this, src2, dest); } // Calculates the union of two rectangles. // The union of rectangles src1 and src2 is the smallest rectangle which // includes both src1 and src2 within it. // It is allowed for dest to be the same as either src1 or src2. // <src2>: a #GdkRectangle // <dest>: return location for the union of src1 and src2 void union_(Rectangle* src2, /*out*/ Rectangle* dest) { gdk_rectangle_union(&this, src2, dest); } } So instead of:
 gdk_rectangle_union(&dirtyRect, &event.area, &dirtyRect);
you can write:
 dirtyRect.union_(&event.area, &dirtyRect);
and, once i add the (forgotten) overload:
 dirtyRect.union_(&event.area);
and all of these will result in identical code being emitted. All of the gtk2/*.d files are browsable online: http://repo.or.cz/w/girtod.git/tree/refs/heads/gtk2:/gtk2 (I see no advantage in splitting out the trivial "methods") artur
Jan 24 2012
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2012-01-24 17:09, Artur Skawina wrote:
 On 01/24/12 12:15, Kagamin wrote:
 On Sunday, 22 January 2012 at 21:56:24 UTC, Artur Skawina wrote:
                   Native GTK2 bindings for D.


 "Native" for two reasons:

 1. Uses the C API directly. No class wrappers and no function wrappers.
 2. OO interface, giving a native D look and feel.
Once again. Is it direct C bindings OR OO interface OR both?
Both. There are basically three parts: 1) data type definitions - ie structs, aliases (typedefs), and enums (constants) 2) C prototypes; this, plus (1) above, let's you use the C API directly 3) "methods", which are actually disguised GTK functions. An example: struct Rectangle { int x, y, width, height; // Calculates the intersection of two rectangles. It is allowed for // do not intersect, dest's width and height is set to 0 and its x // and y values are undefined. If you are only interested in whether // the rectangles intersect, but not in the intersecting area itself, // pass %NULL for dest. // RETURNS: %TRUE if the rectangles intersect. //<src2>: a #GdkRectangle //<dest>: return location for the intersection of src1 and src2, or %NULL int intersect(Rectangle* src2, /*out*/ Rectangle* dest=null) { return gdk_rectangle_intersect(&this, src2, dest); } // Calculates the union of two rectangles. // The union of rectangles src1 and src2 is the smallest rectangle which // includes both src1 and src2 within it. // It is allowed for dest to be the same as either src1 or src2. //<src2>: a #GdkRectangle //<dest>: return location for the union of src1 and src2 void union_(Rectangle* src2, /*out*/ Rectangle* dest) { gdk_rectangle_union(&this, src2, dest); } } So instead of:
 gdk_rectangle_union(&dirtyRect,&event.area,&dirtyRect);
you can write:
 dirtyRect.union_(&event.area,&dirtyRect);
and, once i add the (forgotten) overload:
 dirtyRect.union_(&event.area);
and all of these will result in identical code being emitted. All of the gtk2/*.d files are browsable online: http://repo.or.cz/w/girtod.git/tree/refs/heads/gtk2:/gtk2 (I see no advantage in splitting out the trivial "methods") artur
So what's the difference compared to gtkD: http://dsource.org/projects/gtkd -- /Jacob Carlborg
Jan 24 2012
next sibling parent Gour <gour atmarama.net> writes:
On Tue, 24 Jan 2012 21:03:22 +0100
Jacob Carlborg <doob me.com> wrote:

 So what's the difference compared to gtkD:
I believe it was already mentioned: NIH syndrome. ;) Sincerely, Gour --=20 Those persons who execute their duties according to My injunctions=20 and who follow this teaching faithfully, without envy, become free=20 from the bondage of fruitive actions. http://atmarama.net | Hlapicina (Croatia) | GPG: 52B5C810
Jan 24 2012
prev sibling parent Artur Skawina <art.08.09 gmail.com> writes:
On 01/24/12 21:03, Jacob Carlborg wrote:
 On 2012-01-24 17:09, Artur Skawina wrote:
 On 01/24/12 12:15, Kagamin wrote:
 On Sunday, 22 January 2012 at 21:56:24 UTC, Artur Skawina wrote:
                   Native GTK2 bindings for D.


 "Native" for two reasons:

 1. Uses the C API directly. No class wrappers and no function wrappers.
 2. OO interface, giving a native D look and feel.
Once again. Is it direct C bindings OR OO interface OR both?
Both. There are basically three parts: 1) data type definitions - ie structs, aliases (typedefs), and enums (constants) 2) C prototypes; this, plus (1) above, let's you use the C API directly 3) "methods", which are actually disguised GTK functions. An example: struct Rectangle { int x, y, width, height; // Calculates the intersection of two rectangles. It is allowed for // do not intersect, dest's width and height is set to 0 and its x // and y values are undefined. If you are only interested in whether // the rectangles intersect, but not in the intersecting area itself, // pass %NULL for dest. // RETURNS: %TRUE if the rectangles intersect. //<src2>: a #GdkRectangle //<dest>: return location for the intersection of src1 and src2, or %NULL int intersect(Rectangle* src2, /*out*/ Rectangle* dest=null) { return gdk_rectangle_intersect(&this, src2, dest); } // Calculates the union of two rectangles. // The union of rectangles src1 and src2 is the smallest rectangle which // includes both src1 and src2 within it. // It is allowed for dest to be the same as either src1 or src2. //<src2>: a #GdkRectangle //<dest>: return location for the union of src1 and src2 void union_(Rectangle* src2, /*out*/ Rectangle* dest) { gdk_rectangle_union(&this, src2, dest); } } So instead of:
 gdk_rectangle_union(&dirtyRect,&event.area,&dirtyRect);
you can write:
 dirtyRect.union_(&event.area,&dirtyRect);
and, once i add the (forgotten) overload:
 dirtyRect.union_(&event.area);
and all of these will result in identical code being emitted. All of the gtk2/*.d files are browsable online: http://repo.or.cz/w/girtod.git/tree/refs/heads/gtk2:/gtk2 (I see no advantage in splitting out the trivial "methods") artur
So what's the difference compared to gtkD:
The simple struct above maps to this *module* in gtkD: (i've stripped some comments and whitespace) ------------------------------------------------------- module gdk.Rectangle; public import gtkc.gdktypes; private import gtkc.gdk; private import glib.ConstructionException; public class Rectangle { /** the main Gtk struct */ protected GdkRectangle* gdkRectangle; public GdkRectangle* getRectangleStruct() { return gdkRectangle; } /** the main Gtk struct as a void* */ protected void* getStruct() { return cast(void*)gdkRectangle; } /** * Sets our main struct and passes it to the parent class */ public this (GdkRectangle* gdkRectangle) { if(gdkRectangle is null) { this = null; return; } this.gdkRectangle = gdkRectangle; } /** * Calculates the intersection of two rectangles. It is allowed for * dest to be the same as either src1 or src2. If the rectangles * do not intersect, dest's width and height is set to 0 and its x * and y values are undefined. If you are only interested in whether * the rectangles intersect, but not in the intersecting area itself, * pass NULL for dest. * Params: * src2 = a GdkRectangle * dest = return location for the * intersection of src1 and src2, or NULL. [out caller-allocates][allow-none caller-allocates] * Returns: TRUE if the rectangles intersect. */ public int intersect(Rectangle src2, Rectangle dest) { return gdk_rectangle_intersect(gdkRectangle, (src2 is null) ? null : src2.getRectangleStruct(), (dest is null) ? null : dest.getRectangleStruct()); } /** * Calculates the union of two rectangles. * The union of rectangles src1 and src2 is the smallest rectangle which * includes both src1 and src2 within it. * It is allowed for dest to be the same as either src1 or src2. * Params: * src2 = a GdkRectangle * dest = return location for the union of src1 and src2 */ public void unio(Rectangle src2, Rectangle dest) { gdk_rectangle_union(gdkRectangle, (src2 is null) ? null : src2.getRectangleStruct(), (dest is null) ? null : dest.getRectangleStruct()); } } ------------------------------------------------------------- Now, imagine how eg simple exposure compression code looks like, using both approaches... Where you have one Rectangle storing the area that needs to be repainted, and receive several events, all of them with another embedded Rectangle struct containing newly damaged regions.. Hint: the gtkD approach gives you no way to directly use the gdkRectangle structs passed - you need to allocate a new class instance for every single one of them. Using my bindings, that same code is just:
 dirtyRect.union_(&event.area);
And this is just a simple example. gtkD also does some other silly things, like adds another level of indirection, by not linking against the GTK libs, but manually trying to load them, dlopen-style. Failing and/or scaring the user at runtime if it finds a version it didn't expect. There are probably more issues, but i didn't look at gtkD closely, after being bitten by the latter and realizing the former. D makes it possible to map most of the API 1:1 completely for free, why would anyone want to use a middle layer that adds almost no value, but a significant cost? (think GC; the code overhead wouldn't really be noticeable in most cases, yes) artur
Jan 24 2012
prev sibling parent reply "Kagamin" <spam here.lot> writes:
On Tuesday, 24 January 2012 at 16:09:21 UTC, Artur Skawina wrote:
 So instead of:

 gdk_rectangle_union(&dirtyRect, &event.area, &dirtyRect);
you can write:
 dirtyRect.union_(&event.area, &dirtyRect);
For some reason gdk_rectangle_union(&dirtyRect, &event.area, &dirtyRect); looks better.
 and all of these will result in identical code being emitted.
what if struct Rectangle { GdkRectangle r; alias r this; ... methods ... } Will a different code be emitted? Will alias this work with C api?

 (I see no advantage in splitting out the trivial "methods")
C bindings are a value on their own, this is probably why deimos users(libraries) of the bindings.
Jan 28 2012
parent Artur Skawina <art.08.09 gmail.com> writes:
On 01/28/12 17:28, Kagamin wrote:
 On Tuesday, 24 January 2012 at 16:09:21 UTC, Artur Skawina wrote:
 and all of these will result in identical code being emitted.
what if struct Rectangle { GdkRectangle r; alias r this; ... methods ... } Will a different code be emitted? Will alias this work with C api?
Wrapping one struct in another, with no extra fields, shouldn't make any difference wrt the resulting code. But i don't see any reason to do it like that, and, in addition to having two not 100% compatible ways to refer to the same thing, it would make some things harder and/or impossible. Things like the pseudo struct inheritance magic (you can call methods present in *parent* objects and the right thing will happen; the Rectangle is not a good example for this, see the AppwWin struct in example_gtk3.d, where eg. the last "add()" actually is a GtkContainer method, not a GtkWindow one) and implicit conversions [1]. alias-this makes no difference for the C API, it only matters when you use the D extras.

 (I see no advantage in splitting out the trivial "methods")
C bindings are a value on their own, this is probably why deimos doesn't like
The problem with splitting out the methods is that it would create another module. As it is, an app only needs to "import gtk2.gtk2;" and everything works. Having some apps use this and others use eg a "gtk2.gtkd2" would be bad. Having different identifiers (like the suggested camelCased ones) would be even worse. Let's reduce the pointless balkanization, not encourage it. artur [1] D needs a way to control all implicit conversions for structs/classes, both *if* these should happen and *how*. Also *all* casts should be properly lowered, right now you have to eg use alias-this tricks to catch implicit casts.
Jan 28 2012