|THIS PAGE IS OUT OF DATE|
|ScummVM has changed considerably since this page was created, rendering most of its content obsolete. It is only preserved here for reference purposes.|
We are considering to change our backends to use a more modular approach to things. This would be reflect in both the implementations and also in the interface (i.e. OSystem).
The idea is that a more modular approach makes it easier to reuse code in backends. It also makes it possible to have generic default implementations of certain features, while still allowing backends to customize them if needed. This is also what makes it so interesting for the Small Devices Backend project.
Instead of talking at length about hypothetical advantages of this, let me give you some specific examples I hope to see eventually.
The X11 backend directly uses the X11 API to implement graphics output for ScummVM on any system that supports X11. But of course, being a backend, it also needs to provide a way to output audio. X11 doesn't offer an API for that (AFAIK, at least), so it has to use something else. Sadly, this limits its portability. Right now, it has code for any system that supports
linux/soundcard.h (I think that means OSS)
With the modular system approach, we'd split it into a "graphics & input" module which was implemented based on X11, and a "sound" module implemented based on OSS.
Then if somebody wants to use it on a system which doesn't have OSS, they could either choose to use the "null" ("dummy" ? "empty" ?) sound module, or write their own sound module, based on whatever sound API is available on their platform.
And if somebody decides to write an fbcon graphics/input module at some point, they could still reuse the OSS module.
Another typical candidate for modularization is the Timer code. Currently, most backends are happy using our default Time class, which is based on
OSystem::setTimerCallback method. But the Morphos backend actually needs a custom implementation. The results in a rather nasty hack.
If we were doing things in a somewhat more modularized fashion, then this would become much simpler: Essentially, we'd have a "timer" module, and a default implementation of that which would be 99% identical to our current timer code. The difference being that we make it explicitly possible to replace it. Most backends would simple keep using the default implementation. But porters could also add an alternate implementation if desired.
The higher level code would simply ask the backend once for the "timer manager" via a new
OSystem::getTimerManager method (similar to the existing
OSystem::getSavefileManager method). The result is virtually identical for the client code (only a bit in main.cpp has to be changed), and also virtually identical for porters
Timer class would only need a minor change: Instead of taking an
OSystem parameter, the backend would be responsible to hook up the timer callback appropriately, which amounts to a single line of code. In other words, the change would be almost trivial.
Modularization must be done in such a way that it doesn't hurt porters -- after all it's meant to *help* them. But many ports are rather monolithic, and actually wouldn't benefit much from being modularized (e.g. the PSP, PS2, Dreamcast, etc. ports) compared to the current situation. I.e. it should be relatively pain free to transit to the new system, and not force anybody to reinvent the wheel.
In particular, we want to avoid bloat. Implementing new backends should becomes *easier* with the module approach, not harder. So we have to try to avoid over design, and also should avoid breaking things down into too small bits, because then it becomes harder to maintain them. In particular, we probably still want to allow relatively "monolithic" implementations, by not forcing porters to split everything into multiple files (but maybe encouraging it).
It's usually a good idea to look at how SDL manages its backends & modules. For example, graphics, input and event code are usually all found in
Possible modules include
- Input (events) -> possibly have graphics, input & event code all in a single type of module, just like SDL does -- after all they are usually tightly tied together anyway.
- Sound (possibly allow for custom mixer implementations. Maybe even make it possible to use custom MP3 playback implementations and such things, for platforms that offere optimized implementations. Of course, this is tricky, and may not be possible at all)
- Audio CD (could be mixed with Audio/Sound, but then again the code overlap is probably non-existent, at least in the current backends)
- Timers (the current code would be left as default implementation, but e.g. Morphos port could override it easily)
- Mutexes (only used when timers & threads are involved, so could be part of that)
- Virtual keyboard
- Filesystem code (already modular, see backends/fs/)
- SaveFileManager (already modular to an extent, but should be moved to backends/saves/)
- Native MIDI drivers (already modular, backends/midi/)
Regarding the source layout: I imagine we will add subdirs to backends based on functionality, while retaining some / all of the current platform specific dirs (though we might be move the latter into a new subdir called "platform" or so). I.e. I envision the source hierarchy to eventually look something like this:
I don't want to be too strict on how far we use subdirs or not. E.g.
backends/midi currently does not use subdirs. And the subdirs in
backends/fs are not really needed either, since each implementation so far consists of a single source file only, so we might want to flatten it. Those are fine details we still can decide (and change) latter.
The point of keeping a "platforms" directory is to allow backends to stay monolithic if they prefer: If, for example, it's easier for the DC backend to stay in a single file, then it shall be able to do so. Adding subdirs to the backends/MODULE/ dirs is mostly interesting if those are likely to be reused (but of course porters are welcome to do this separation anyway if they like to).