Title banner
SDL, images, and saving!
 

Last night, after several days of steady hair loss, I finally managed to release the first version of my library that allows SDL_Surfaces to be saved, either to files or SDL_RWops, as jpeg or png data. This self-imposed torture1 was needed because, while there are several ways to get images into SDL - SDL_image being the most commonly used and widely recognised library for doing this - support for actually saving from SDL surfaces to files is, at best, poor. SDL itself provides a function to save surfaces as BMP files, but if you want to save in any sane format, you're basically stuck with rolling your own.

The save code I have written is in its early stages: as it stands, it supports writing as png and jpeg, as those are the formats I need right now. I have plans in the works to add TGA, TIFF, and possibly PCX (although the latter is a long, long way down the priorities), but supporting saving to other formats that SDL_image supports2 is not really something I have any interest in as they are all of questionable utility in real situations.

I have designed the library such that the code needed to save each format is nearly encapsulated in its own file, and with naming schemes that betray a deeper motive: once I have more formats (TGA and TIFF at least) added, and have tested the code more thoroughly, I will be looking into making a major patch against SDL_image, and contacting the maintainer of that library to try and get my code rolled into it. The lack of save facility has been a deficiency in the library for a long time, and hopefully I can start to address that properly. I can at least try, anyway!

Anyway, the release message can be read here. There you will find links to downloads, git repository links, bug reporting facilities, and more. Now I go to start integrating this into a Real Program...

1 Seriously, what is it with image library developers and documentation? They appear to be decent enough at writing conversational introductions to the use of the library, but if you want actual API docs, with full explanations of function purposes, arguments, and return values? Aahahah, no.
2CUR, GIF, ICO, ILBM, PNM, XCF, XPM, XV

Mediawiki, groups, and the persuit of filtering...
 

Over the past week, work has seen me looking long and hard at some of the MediaWiki code1 in order to add more explicit visibility of group membership (every time a user link appears, it includes information about the groups the user is a member of), and group filtering facilities to the Recent Changes and Contributions pages, and their associated feeds. This entry is an attempt to document the overall changes made, primarily so that I have a reference down the line, but it may be of interest to Like Minded Lunatics.

A side issue here is that the changes I've made are all against the 1.15.4 stable release, so realistically I can't actually submit the patches to the Foundation (despite the fact that, for example, group filtering of Recent Changes has been a feature request since 2006!) Having checked out the current subversion tree, I have found that some significant changes have been made to some of the files I have patched2, but the majority will simply involve me copy&pasting my changes into place, and one change even removes the need to patch one file entirely, so I could conceivably insert my changes into the latest code and work out patches from that... I may consider that next week sometime.

But anyway, for now I will leave the whole issue of explicitly displaying group membership, probably for another entry, and will concentrate on the group filtering of Recent Changes and Contributions. My current patch modifies four files to add the filtering: ChangesFeed, SpecialContributions, SpecialRecentchanges, and MessagesEn, the latter being required to add two messages rather than hard-code the text shown in forms.

The first change I had to make was in includes/ChangesFeed.php, the code that is used to generate feeds for Recent Changes and Contributions. In the stable release3 the problem I encountered was that the caching being done to reduce feed generation overhead was too 'dumb': the code attempts to generate a memory cache key based on only three of the potential query string arguments - the number of items to show, whether minor edits should be shown, and the target. Amusingly, it appears that the maintainers of ChangesFeed and SpecialRecentchanges were somewhat opposed to talking to each other, as SpecialRecentchanges labeled the behaviour of ChangesFeed as a bug, when in fact it was merely a side effect of the latter code not accepting the full range of possible recent changes settings when working out its cache key. I needed to expand the arguments it was aware of so that it would correctly generate and cache the correct feed when one of my new arguments was specified. In the end, I did it the lazy way, and just added extra parameters to the appropriate function - the cutting-edge code handles it in a rather nicer fashion by taking an array of options, and I may backport that code.

The changes to includes/special/SpecialRecentchanges.php are more extensive. Filtering on group requires that the code is actually aware of groups! First it needs a 'group' argument added to the recognised query string arguments, and a select box adding to the form through which the user controls the settings to allow specification of a group. The code to add group filtering to the database query actually proved fairly simple - a bit of code to add a condition to the query if a non-implicit group is selected, and another bit that joins in the user_groups table if needed. While working on adding the form changes, I was originally just allowing for filtering on non-implicit groups (ie: groups other than 'user' and 'anonymous'). After running into the feature request linked above, I decided that I needed to add the ability to filter on the user and anonymous groups, but I also realised that I could cheat, as the ability to filter on those two groups is already there: the controls already allow you to hide or show anonymous edits and user edits, all I had to do was detect when a user had selected to filter on 'user' or 'anonymous' and then flip the appropriate flags in the code that generates the database query!

Fairly similar levels of modification were needed in includes/special/SpecialContributions.php - again, the addition of a new query string option was needed, and the supporting form modifications. Unlike SpecialRecentchanges, this class doesn't use the MediaWiki 'FormOptions' class to handle arguments, so some additional work was needed to handle the group filtering option. The database changes actually get pushed down into the ContribsPager class, and mainly consist of adding an extra condition and table join if a group filter is specified.

Overall the changes are fairly simple, something I wasn't expecting when I started it (a good three days of the time I spent on the problem were given over to getting my brain into the same space as the MediaWiki developer's, to work out how to add the functionality). The patch file is 15K unpacked, but actually a lot of that is patch context, the meat of the changes is really much smaller - I was surprised, given how long the issue has remained unaddressed. But now, I need to refactor the bulk of my course processor's output code...

1 which is the usual mixed-bag of quality I've come to expect from major projects. Some files are quite exemplary, while others are vaguely nightmarish. The worst ones aren't as bad as some code I've seen, but even the best ones make me realise that my habit of making comments take up around 30% of any source is deeply atypical.
2 none of the changes actually replicate the functionality my work has added, so it hasn't rendered the overall project redundant, thankfully.
3 but not, as far as I can tell, in the cutting-edge code. It appears that the ChangesFeed in the subversion tree completely overhauls the handling of arguments to some key functions that replicate the functionality I had to add, so it looks like people have actually started talking to each other...

"Open"SSL
 

So, I have finally (I think, I won't trust it for a while yet, I suspect) managed to get a working version of OpenSSL, including both shared and static library versions, compiled in MinGW+MSYS. I say "I think", because I thought I'd already done it, except that actually attempting to use it proved that I was wrong - so far, this seems not to be the case now.

Having achieved this moderately Herculean task, I must now ask the OpenSSL developers one question:

What the everliving fuck, guys?

I'm now moderately convinced that, at the very least, the package maintainer and configuration and build system maintainers have set out to be as deliberately hostile to any attempt to compile on windows as possible while grudgingly providing something that is vaguely close to a system that works. Let's look at this, shall we?

First, the official source tarball contains a nest of symbolic links so that various headers appear in multiple locations throughout the tree. Why they couldn't have set the build system up to look in one location for these files - you know, like virtually every other vaguely sane package does - I know not. The practical upshot however is that unpacking the thing on windows loses all those symbolic links. However, woe betide you if you do something like copy the files before you run the configure script, as it will helpfully remove them.

Which brings us to the configure script, in all its hilarious uniqueness. Not content with the normal practice of using a shell script to perform this action, the developers decided that their configure script a) needs to be called 'Configure' rather than 'configure', and b) should be written in perl. At this point, even running it requires all sorts of shenanigans with cygwin, using the practically Jurassic version included in the MSYS Developer Toolkit (5.6.1, from 2001), or arsing around with activeperl. In the end, I used the MSYS DTK version, and it at least worked.

Trying to get it to build, however, doesn't work because of missing headers - to fix that requires a load of copying around to get headers into the right place (And there's yet another problem in here related to building one directory of code, but I just completely ripped that out as I didn't need it).

But, it gets better! Even when all the build finally works and finishes, there's a big problem: the DLLs that the build script generates are fucking broken - they export no symbols at all, they are bloody unusable. To fix that problem requires editing the Configure script, and editing some of the flags used when linking to force the build to export all the symbols in the DLLs!

I mean, has anyone there even fucking tried their own build system on MinGW+MSYS? Is this simply inexcusable incompetence, or is this rampant arsehattery and a deliberate attempt to make it difficult for people trying to compile the thing for the OS still used by the majority of desktop systems?

But, at least now I have most of the stuff I will need installed, and a fairly decent process in place to update things if I need to...

Of code, and apps, and fiddly things...
 

This post has been declared to be Long and Rambling. You Have Been Warned.

For a long time I've needed to do several things as far as development stuff is concerned, and I've finally got my arse in gear to do them. In no particular order:

  • Consolidate, organise, and rationalise my various libraries and disparate pieces of development code into a sane structure rather than having multiple copies of some across different projects. This worked wonders for my perl web code, and I'm hoping it works as well for my c/c++ code.
  • Set up a working windows development system. Much as I detest windows in generaly, I do need to target it as well as linux. Until now, most of the fiddling around I've done has been supported by Dev-C++ or cygwin, but support for the former is patchy and getting worse1, while the latter is drastically unsuitable for inflicting on 'normal' users.
  • Ditch Bugzilla and replace it with a sane alternative. Bugzilla is probably completely awesome for really big development teams, and it is insanely powerful, but it's also a complete and total pain in the gluteus maximus to use, and keeping it up to date is hilarious as the bugzilla devs appear to think that requiring cutting-edge versions of perl modules (ones that won't hit most distro's repositories for weeks or more) with every single update is a great idea.

The first of these involved trawling through several directories containing various projects, and attempting to determine which pieces of code should be shared, and which of the various duplicates represented the most complete and up-to-date version. Thankfully, this proved to be fairly simple, but it did highlight some significant holes in the documentation for things that I will need to address soon - certainly before I start using them in anger. This will have the useful side-effect of getting me reacquainted with some pieces of code that have been sitting around unused for far too long, and will probably trigger improvements (I may even finally get around to fixing up the version of SDL_image that can save images as well as load them...)

But yes, Much More Organised Code, and hopefully this will keep me from ending up in the versioning hell that was looking likely.

Next, after fixing up various Datanet issues (installing an RSS feed mod... and then fixing the fallout caused by the mod breaking bits of the forum!), I turned my eye on Bugzilla. I've tried to use this several times to keep track of things (even the things it was originally designed to keep track of: bugs and other undocumented creatures), and it's vaguely akin to using a chainsaw to prune roses: it works, but it is complete overkill and there's a good chance of getting thorns in places that should never get thorns in them. Every time I've installed and tried to use it, I've ended up avoiding using the installation simply because the damned thing is so unwieldy for simple jobs and basic setups like mine, especially when keeping it up to date is an exercise in frustration and broken perl setups.

Years ago - back in the days when I was coding webapps using REBOL (2001 or so, in fact) - I developed a bug tracking system called ROACH, the name being a cunning bacronym I have since completely forgotten the expansion of. ROACH was very specifically tailored to smaller development team bug and enhancement request tracking, primarily the environment I found myself in at work, where I needed a system that clerical staff could actually use without weeks of training. It had a decent range of features, but the design was such that the user wasn't swamped by the interface. If I had time, I'd reimplement the system in perl, as it was exactly what I'd need now. However, I don't have time (or the inclination, really) and would much prefer to use an existing OSS online bug tracker. Looking around for alternatives to Bugzilla, all I could find were other similar Super Issue Trackers, almost all of which appear to have tried to integrate project management, ticketing, bug tracking, dish washing, and pet grooming all in gigantic uberapps that promise to save your project from having insufficient complication and bureaucracy.

After much considerable amounts of digging through Wikipedia, Google, and the darkest forbidden depths of the Technonomicon, I found myself poking at Flyspray. Flyspray seems far better tailored to small team bug tracking, and while the interface is slightly cumbersome in places, it appears to be decently designed for its job. I've now installed it at flyspray.starforge.co.uk and will need to play around with it some more before I decide for certain, but it certainly seems to be the most suitable looking alternative at the moment. If I'm still using it two months from now, it will have beaten Bugzilla, at least...

This left me with the development platform, and this is where the pain really hit. Minimally, I need to be able to compile applications that use one or more of OpenGL, SDL (including several extension libraries), GTK+2, and libCURL. This doesn't sound like much, until you factor in the dependencies that several of these have (GTK+2 consists of 5 core libraries, and 8 or more dependencies, and the SDL libraries have numerous dependencies too).

On Windows, the state of compilers seems to be... something of a state. If you don't want to fight Microsoft's compilers, or pay extortionate amounts for others, you're basically stuck with Cygwin (which is capable, but far from convenient or even really viable for normal users), or MinGW+MSYS to get a compiler environment.

I started by getting a MinGW system installed. This was, in itself, not particularly painful, although I winced bodily at the 4 year old compiler version in there. At this point, I naively assumed I was almost good to go, until I started trying to get the packages I needed installed.

Sweet merciful Cthulhu, what a goddamn bloody mess. Seriously, this reminded me of trying to kludge together a working linux install on my old Amiga back in the late 90s, for Hastur's sake.

Several of the packages I need share library dependencies, but the prebuilt binary versions are, of course, built against different versions of the libraries, and some will not play nice with versions from other packages. The way around this to build as much as possible from source, so that everything is using the same versions of libraries... and to do that, I had to get MSYS installed (which took several hours to figure out, as they seem to have made a conscious effort to be as deliberately opaque and obtuse as possible with the package naming, and the sourceforge page is a complete unmitigated disaster). Once I had something vaguely working, I started compiling packages... some worked perfectly, and I was deeply impressed by how well they'd made them portable. Some - like OpenSSL - I still haven't managed to build or even get a properly working install of yet. Trying to get the whole thing to work has proved to be a complete nightmare (I still have to compile GTK+2, although at least I've found a vaguely recent, well-written description of the process for that), and it has made me truly appreciate just how bloody amazingly nice things like Gentoo's portage system are. On Gentoo, I can tell it to install something and it automatically resolves any outstanding dependencies, downloads all the required sources, compiles them in the required order (even while still downloading later packages) with the options I tell it to use, installs them cleanly, and generally turns what has taken me a good thirty or more hours of frustrated and irritated digging on windows into a couple of commands in a terminal, and a wait while it does all the work.

Not pleased. At all. The GTK+2 compile had better work well, or I may lose what little hair I have left.

1 Dev-C++ is an integrated C/C++ development environment. Development of the frontend software effectively ceased back in 2005, and the compiler it contains is even older (circa 2004). Trying to get it to work with new libraries and software is increasingly tricky and frustrating.

Div-isive popups
 

So, as part of a semi-Sekrit Projekt, I have been working on trying to get an arbitrary number of <div>s in a web page to behave as popups: they remain hidden until the mouse hovers over specific elements in the page, at which point they appear.

This is not, of itself, all that complicated to do - there's a plethora of solutions out there for this.

Of course, this is me, it's not that simple. I need a bunch of Extra Features for this:

  • Popups should not appear immediately on the mouse hovering over the element. I've gone for a 2 second delay there. Similarly, they should hang around for half a second after the mouse leaves the element.
  • Any muppet can do simple appear/disappear, these must fade in and out.
  • If the user makes the popup appear, they need to be able to move the mouse into the popup and have it remain open.
  • The popups must be placed at specific locations relative to the element they are attached to.

The first three of these are not too bad, especially as mootools - a javascript framework to provide a lot of nice new features - can help with things like easy to use timed events, and fading. The problem was the last point on the list.

Mootools provides a few methods to obtain the location of elements on a page (so that you can position one element relative to another). It even provides a position() function that should make the process of positioning one element relative to another trivial. Unfortunately, they are all crap. They either produce different coordinates in different browsers, or completely incorrect coordinates, or bugger all.

And so, I set about making my own code to work it out. There's a fair amount of code out there on the net to do this, too. Again, all the ones I tried fall down in some situations, and I ran into the problem with my own code too: the coordinates returned looked correct - screenshotting the browser window, and plotting the coordinates in a graphics program appeared to show the correct place - but when the left and top of the popup div were set to the coordinates, the popup would consistently appear a significant distance below or to the left of the location it should be.

It took me a couple of hours to finally realise what the problem was, and now it seems so obvious I've no idea why I didn't realise it before: all the solutions I had run into and tried had the unstated, implicit assumption that the element being positioned was in the top level of the document, or all of its parent elements has their css position attribute set to 'relative' or 'inherit'. If one of the parent elements had 'position: absolute' (as the 'main content' container div in my pages does), then the position of the div needs to be relative to that, not the page! Once I'd realised this, the following code represents the solution to locating the position of an element in a page (and it appears to work fine in firefox, Seamonkey, Opera, and Safari. I haven't tried it in IE because, frankly, I don't care it works there or not):

Code:
function getRelativePos(element, xhandle, yhandle) {
var obj = element;
var curleft = 0;
var curtop = 0;

// Work out the top left corner of the specified element.
// Note that we need to stop the search at the first absolute
// position element encountered, as coords for left and top will
// be relative to that element!
while(obj.offsetParent && obj.getStyle('position') != 'absolute') {
curleft += obj.offsetLeft;
curtop += obj.offsetTop;
obj = obj.offsetParent;
}

// If the x handle is set to something other than 'left', deal with it
if(xhandle == 'right') {
curleft += element.offsetWidth;
} else if(xhandle == 'center') {
curleft += parseInt(element.offsetWidth / 2);
}

// Now do the same for the y
if(yhandle == 'bottom') {
curtop += element.offsetHeight;
} else if(yhandle == 'center') {
curtop += parseInt(element.offsetHeight / 2);
}

return { x: curleft, y: curtop }
}

mootools-core is needed for the getStyle() function, but that could be replaced if that causes a problem. For an example of it in action, the mockup page for the system it will be used in can be found here. Leave the mouse over the first columns of the top three rows to see the popup effect.

Even better, I think I should be able to reuse the same code verbatim in the CBT package framework we use in work, to replace the clunky new window popups entirely!