Xfmedia Refactoring

I've finally started my huge Xfmedia refactoring project. The idea is to make it a lot more maintainable and modularised. I've been doing this in tiny pieces for many months now (XfmediaPlaylist, XfmediaVideoWindow), but I can't really go any further without ripping huge pieces of the codebase apart and stitching them back together the way I want everything to be. I realise it's been a couple months since I released 0.9.1, but it'll likely be a couple more months (at best) before 0.10.0. I think 0.9.1 is fairly stable and featureful. Unfortunately, 0.10.0 probably won't have many user-visible changes, but I expect the plugin interface will finally be stable, or at least in a state where I can promise backward-compatibility for future releases.

Anyway, here's an initial sketch of how my current thinking looks with regard to object structure:

XfmediaApplication: This is more of a meta-object, but it'll keep track of "global" data and handle starting up the app, as well as tearing it down. I haven't decided yet, but it eventually may make sense to allow XfmediaApplication to instantiate multiple xfmedia main windows in the same process, so you can have multiple copies of xfmedia running in the same process space. I'm not really sure what this means for plugin interaction yet, so this may never become a reality. I doubt 0.10.0 will have this ability, but maybe in a future version.

I'm a bit less certain about how the next two objects fit.

XfmediaPlayer will be a very thin layer on top of XfmediaXine. It will know how to play streams and handle usual playback operations, as well as interact with the GUI.

XfmediaMainWindow is a GtkWindow representing the controls/playlist window. It'll act as a container for the XfmediaPlaylist, and hold the time label, song/status label, and control buttons and slider.

I'm not sure if XfmediaPlayer should be the container for XfmediaMainWindow, or the reverse. On one hand, it makes sense for the window to contain the player, but I think I'd rather have the player as the super-object, which will hold the main window, and know that it should do some UI-related things when playback events occur. E.g., if a stream is started from the stop state, the play button in the UI should change to a pause button. There doesn't seem to be a great way to handle this case with the reverse relationship.

So I guess the object relationships should go like this:



  • XfmediaXine XfmediaMainWindow

    • XfmediaInfobar
    • XfmediaPlaylist
    • XfmediaPlaylistQueue
  • XfmediaVideoWindow

  • XfmediaTrayIcon (?)

There will be only one XfmediaApplication per process, and this can be considered as a pseudo-global. The various XfmediaPlugin objects (one per plugin) will live as sort of a peer to XfmediaPlayer, and can (obviously) control playback, and probably modify or interact with some parts of the UI. I'll have to be careful to compartmentalise this properly, as I'm sure there are parts of the UI plugins shouldn't be allowed to touch. Fortunately, the XfmediaPlugin object shouldn't have to change, aside from adding new object signals.

There are a few other things that are just floating around.

There's the settings system, which isn't an object at all: just a set of global functions that act on a global settings store. That's definitely a problem if I want multiple xfmedia instances in a single process, since, e.g., you might not want a systray icon for each instance, or you might not want each instance to be sticky on all workspaces. This is going to be a bit of a pain, because all the xfmedia_settings_get_*() calls have only one argument for convenience. Also, when xfmedia exits, which instance gets to write out the settings file? Only the first one? This sounds a bit messy. But supporting multiple settings files for each instance is pretty messy too.

The remote control interface isn't an object either, but it's probably ok (almost) as-is, since it knows how to operate on a specific "session" of xfmedia.

Next we have the keybindings system, which probably needs to be mostly redone. Fortunately it currently acts on a specific XfmediaMainwin instance (which will become XfmediaMainWindow), so it should be relatively easy to modify it to operate on an XfmediaPlayer, and thus be multi-instance-safe. However, all the keybindings are stored in the same file, so I'm not sure it's useful to have separate keybindings for each instance. Also, I'd rather use a standard GTK mechanism for doing keybindings, so I'll probably rethink this at some point (GtkAction?).

The mediamarks editor and menu are keyed to a particular mainwin instance, but, again, I'm not sure it makes sense to make this per-instance. There's only one mediamarks file, supporting more than one in a "smart" manner is pretty difficult, and if you look at them like bookmarks, a web browser doesn't have different bookmarks per window.

The tray icon is something I'm not sure about yet. Having multiple tray icons for each instance sounds a bit silly. I'm thinking about the possibility of a single tray icon being able to control all the instances of xfmedia in the same process. The problem with this approach kinda defeats the purpose of the tray icon: to offer a few simple features of xfmedia just a couple quick mouse clicks away. Increasing the complexity of the tray icon directly contradicts this. At some point I might move the tray icon into a plugin.

Next there's playlist-files.h, which is mainly a set of utility functions for reading and saving various playlist types. It mostly operates on a XfmediaPlaylist instance right now, which I think is fine. The playlist files don't need to know about the player engine at all; they just need to know the contents of the playlist (or just how to load stuff into it).

The "jump to file" box is a bit awkward right now. Originally it was a separate window, but now it's integrated into the XfmediaPlaylist widget. So it's in a separate file, somewhat kludged into playlist.c. This needs to be integrated properly (though it really does work fine as-is) for maintenance's sake.

The last piece is the equaliser window, which I never finished because xine handles EQ in a rather weird way, and I never figured it out well enough to make it work right. I might come back to this at some point, in which case it'll just be a child of XfmediaPlayer, and nobody else needs to know a thing about it, except perhaps how to show and hide it.

A final question is what to do about XfmediaXine: should plugins be able to interact with it directly. I'm thinking no. This allows me to support other playback engines in the future (which probably won't happen, but it's not wise to close the door on the possibility), and helps make sure I've abstracted player details into XfmediaPlayer properly.

So I guess that's about it. Comments? Questions? Not saying I'll listen, but I'm curious to know what people think...