Looking at the existing [...GState DPS...]
methods and comparing them to the Core Graphics functions, it looked like implementing the backend will be as much of a cakewalk as I originally presumed.
Turns out it is not meant to be.
(Today’s text is a report as much as it is a way for me to contemplate next steps. It’s a noisy stream of thoughts. Sorry about that.)
All is drawn, but nothing is seen (a.k.a. doublebuffering)
The Cairo backend contains code intended to render into a backing store and then draw the content on screen when requested to do so by X. (Presumably, also to blit the content when it updates.) Theoretically, it should be trivial to add this functionality: add a new CGBitmapContext
, redirect all drawing into this new context, and then blit this content on screen when requested.
When creating the surface that targets the screen, one gets a gswindow_device_t*
that describes the X11 window. To tell the rest of the gnustep-back
that this surface can handle its doublebuffering (although it really can’t), we set this gswindow_device_t
‘s gdriver
to self
, and we add flags GDriverHandlesExpose
and GDriverHandlesBacking
to its gdriverProtocol
bitmask. This is done only if the surface is not NSBackingStoreNonRetained
.
(From the above description one would think by default, backing stores would be already handled elsewhere in gnustep-back
. Unfortunately, although it seems like gnustep-back
might be trying to handle the case where the surface does not handle X11’s Expose
event nor does it handle having the backing store, it does not do so, at least not properly.)
So, I’ve tried to do it. All drawing is redirected to a CGBitmapContext
, and then in -handleExpose
I’m trying to blit the content on the X11-backed Opal surface. It seems that Opal currently has some issues with the following code (i.e. I get everything drawn in pale yellow):
- (void) handleExposeRect: (NSRect)rect
{
CGRect cgRect = CGRectMake(rect.origin.x, rect.origin.y,
rect.size.width, rect.size.height);
CGImageRef backingImage = CGBitmapContextCreateImage(_backingCGContext);
CGContextDrawImage(_x11CGContext, cgRect, backingImage);
CGImageRelease(backingImage);
}
Debug output from Opal suggest it’s going through all the expected dance moves: creating that image, converting it into destination context’s colorspace, et al. I’ll poke Eric to see if he has a better idea of what’s going wrong.
An interesting behavior is that if I simply don’t redirect drawing to the bitmap context, then I get some content on screen. Despite not having an actual backing store. Maybe GDriverHandlesBacking
has side effects? I’ll have to dig in more.
(Oh, and gotShmCompletion
methods seemed like they might be related. No change if I implement them.)
Everything is flipped
When I first got some content on the screen, it turned out that GNUstep’s zero-zero seems to be based on top-left corner of the drawable — opposite from what Opal expects.
So as a first attempt I went in and implemented matrix functions. While things don’t get drawn in the appropriate place yet, it seems like I’m on the right track. The thing that worries me is that I’m not sure what’s the orientation of DPS matrix as opposed to the orientation of the CG matrix. Also, I’m now overriding DPS methods and not calling super
, yet there’s a bunch of code in GSGState
‘s implementation that makes me think this is not such a good idea. DPS functions seem to be maintaining matrix state; hopefully they don’t ever use it directly. If they don’t, then it might be safe to simply override the getter for the matrix.
To be able to reset the Opal matrix to the identity (or, near-identity, considering it’s also flipping the coordinate system), I’ve also added a method to Opal that allows this precise operation. This is an extension over what Apple provides: Apple provides absolutely no way to set the CTM
(presumably, this stands for current transform matrix) directly, which makes only a little bit of sense. I don’t see a good reason this functionality is not provided to developers. (I can think of one that’s just a tiny little bit good.)
Menus have a single item
Now all I get in a menu window is a single menu item. Drawn in place of the menu window’s title.
Meaning, drawn on a completely incorrect place. I’m not really sure if this is a menu item drawn in an incorrect place, or if this is caused by the fact that -compositeGState:...
, which seems to permit blitting one GState
‘s backing surface’s content on top of another GState
‘s content, is not implemented.
If this is so, this may prove very troublesome to implement without adding further hacks into Opal. Core Graphics seems to provide no real way to copy a portion of a context on top of another context, apart from using CGBitmapContext
.
Fonts. Oooh, boy.
Cairo backend does a lot of stuff when it comes fonts. A lot, lot of stuff. I wonder why most of that is not exposed to the rest of gnustep-back
, so I’ll probably be moving that out of the Cairo backend and into an additional module within gnustep-back
called fonts
, or something like that. Or perhaps even into the existing gsc
module.
Just taking a quick look, art
backend seems to be supporting just fonts with the .nfont
extension (explaining Helvetica.nfont
), but I may be wrong. Taking a quick look at xlib
backend, and it seems to be doing something similar to the cairo
backend.
It’s going to be a pain to implement fonts, especially to do it properly. I’ll have to think about how to deal with refactoring this — cairo backend is popular and accidentally breaking it won’t be looked at kindly by the community. Still, it’ll be worth it.
Unless it turns out I should have dealt with Opal. In which case Cairo backend’s implementation will stay Cairo backend’s implementation, and Opal backend will leverage Opal for its text needs.
Yes — just as the title of this section implies, I’m not sure what I’m talking about.
Other upcoming steps
Among other things, I’ll look into getting image painting to work. GNUstep does a lot of things with images, so seeing the images might help me figure out what’s going on more easily.