I completely agree with you, dear reader — I don’t like it either that it took so long to get somewhere useful. 🙂
But, here’s two screenshots that are right now making me happy. (Aside from lunch, that is.)
Red boxes are images. On the top left of SystemPreferences you can see the menu. The white blob in SystemPreferences is where WindowMaker drew its HUD to display window position while moving the window around; doublebuffering is disabled at the moment.
Calculator uses NSWindows95InterfaceStyle
, so the menus are embedded inside the window. (A necessity under XQuartz.)
So, how did I get here?
Doublebuffering
I had no idea where to start from fixing the issues. So I turned to trying to write the backing image into a file. That’s easy to do with Core Graphics; but, there happened to be a small bug with Opal. Eric stepped in once again, and fixed saving to files. I’m really thankful to him for doing this so quickly.
Now that I could output files, I saw that backing images for menus were correctly drawn. What went wrong was sending things to the screen. Menus generally worked. Hooray! That was a big confirmation that something small was going wrong. Images painted on screen were incorrectly positioned, and when running under XQuartz on OS X, even sized incorrectly. (XQuartz forces a minimum window width even for menu windows.)
At some point, I figured out, what the hell – let’s check what CGContextDrawImage()
specification says. Lo and behold: the argument specifies only the destination rectangle, and does not relate to the parent rectangle! The solution is to use CGImageCreateFromImageWithRect()
. I ran into a small Opal bug here, which I reported to Eric; the new image still includes the original image (just like the documentation says it should), but under Opal, this means CGImageGetWidth()/Height()
report original image’s values. Also, writing such a sub-image still writes out the entire image to disk.
Unfortunately, I didn’t get this to work yet. I’m still getting weird offsets, but now I’m on the right track. Currently committed version has doublebuffering disabled (which means moving anything over the window clears that part of the surface and does not cause correct redraw).
Saving and restoring graphics state
So… -DPSgsave
and -DPSgrestore
. Although I was aware that this is the thing to look at in case things mess up with matrices, and I spent hours and hours looking around for what went wrong (very small amount of code, but a lot of exploration), it didn’t occur to me that I, perhaps, did not implement the methods.
Eric pointed it out to me that I didn’t.
I checked, and indeed: I was going around in circles just because I did not push state to the stack where appropriate.
So I implemented these methods under names -DPSsavegstate
and -DPSrestoregstate
. When it didn’t help, I looked around for ideas about what else may have went wrong.
Then today, having run out of ideas, I triplechecked what I did, and the method names were incorrect. With Eric, I only discussed that state needs to be saved; I have absolutely no idea where I pulled the method names out from or why I even though they were correct.
Then, as I went to check what the method names should be, I found they were named -DPSgsave
and -DPSgrestore
. And they are in, for example, CairoContext
— not in CairoGState
. Which is perfectly reasonable: GState
classes are exactly what these methods manipulate, wrappers for state of backends whose underlying libraries do not contain a state stack, or need to maintain additional state.
So having overridden these methods, called super and then passed them on to OpalGState
, I finally got something sensible on screen. Hooray!
Next: Cleanup and moving DPS methods to OpalContext
Eric has suggested moving (nearly) all methods from OpalGState
to OpalContext
. This definitely makes sense; if there’s some state that happens not to be maintained by Opal, it probably makes sense to add the required functionality to Opal and not to the backend.
I’ll also be cleaning up the implementation before moving on to adding fonts et al. There’s leaks all over the place because I (intentionally) did not pay enough attention while researching what the problems are.
Mid-term self-evaluation for this year’s GSoC is between July 29th and August 2nd. I’ll be submitting relatively low scores for myself; I’m personally unsatisfied with the time it took me to reach this stage. I’m hoping that the worst is behind me. On the other hand, I still have to deal with fonts. sigh