Themers

I think most people who design Metacity themes don’t read this blog, which is a shame because they’re a particularly specialist bunch of users and I’d like their opinions on things. Does anyone know where enough of them hang out that I could contact them? Or should I just bcc contact addresses in a bunch of theme files and suggest they come over?

Half-finished code finishing marathon time

I have several half-finished bits of code lying around.  I think I’ll make an effort to merge them in, at least in test branches, to see what people think.  (When we get a DVCS, this will be easier.)

  1. Veracity, a test suite.  This is about two-thirds done, but will require a bit of autotools magic to link into the main build process.  I may need some help with that.
  2. Window matching: something to remember window positions across sessions.  There’s been a few requests for it recently (most recently, Launchpad bug 311615).  We’ve always said we wouldn’t do this, but maybe there’s no harm done in trying it in a branch as an experiment.
  3. Opacity, a simple WYSIWYG theme editor.  About a quarter to a third of it is written.  Probably would be best to make this a separate project.
  4. Actions.  The idea has often come up (e.g. GNOME bug 345233) that if there’s something you can bind a keystroke to, you should be able to put it into the window menu or make it a titlebar button or whatever.  This may be over-configurable, but there may be advantages of a simplified architecture in making it possible at all. There’s experimental code to do this, but it’s about half done.
  5. “Cringe”: how much can we avoid keeping in memory at once?  This is an answer to what I think is our oldest current bug, GNOME bug 144242.  Saving a few bytes here and there per window can really add up.  I’ve done a small amount of playing around with this, but more is needed.  Having Veracity working will really make this easier because then we’ll just be able to run a stress test inside valgrind.

Gentle reader, which should be moved out of Metacity Labs first?

Theme speed

The speed Metacity renders decorations depends on the theme in use. If you want to time all the themes installed and view them, use:

  • for G in $(locate metacity-theme-1|grep /usr/share/themes|cut -d/ -f5); do metacity-theme-viewer $G; done

Mean client-side times on my system to draw each frame, in ascending order of speed:

  • Prelude (the theme given in the previous post): 1.3ms
  • Bright: 1.9ms
  • Atlanta: 2.0ms
  • Mist: 2.1ms
  • AgingGorilla: 2.2ms
  • Metabox: 2.2ms
  • Simple: 2.3ms
  • Esco: 2.4ms
  • Glider: 3.7ms
  • Crux: 3.8ms
  • DarkRoom: 3.9ms
  • ClearlooksClassic: 4.3ms
  • Glossy: 4.5ms
  • Clearlooks: 4.6ms
  • Inverted: 4.6ms
  • Human: 6.0ms

A simple theme: Prelude

This is in answer to Stuart Langridge’s question about how the XFCE theme Prelude can be ported to Metacity.  Here is a quick attempt at porting it; I’ll be referring to that in what follows.  In order to install this theme for yourself, do:

  • mkdir -p ~/.themes/Prelude/metacity-1
  • wget -O ~/.themes/Prelude/metacity-1/metacity-theme-1.xml http://www.gnome.org/~tthurman/pics/metacity/Prelude.xml

I’m not talking about SVG here, because that’s still an experimental technology within Metacity, and I’m not talking much about version 2 of the theme format for simplicity.

We begin with the metadata, which isn’t unusual enough to explain.  Next comes the geometry, which is the key to the whole business: we only have the one, and it has rounded corners and no title.  We have to mark that there’s no title or Metacity will make space for one even though there’s no instruction to draw it.  If we were making a v2 theme, we could specify the degree of rounding, but v1 only allows you to say whether or not rounding is happening on each corner.

After the geometry we declare some draw_ops.  These are lists of instructions about how to draw each item.  All the draw_ops here are simply coloured rectangles.  One of them, “white”, is to draw the whole background of the window, but the entries for buttons stop slightly short in order to give the impression of gaps between the buttons but to leave the gaps clickable.  We declare the button colours twice in order to have a slightly lit-up version for the prelight.

I added a blue button for the menu button, but if you wanted you could use “white” there and have the menu button present but invisible.  In v2 you would need to declare three other button colours, which could get confusing.

Next, we only have one frame_style and it only draws one piece.  The differences between all the possible pieces can get confusing, but here we only wish to draw an enormous white rectangle over the whole window surface, and so we can use the “entire_background” piece and be done with it.

Then we have a frame_style_set which points in all cases to the one frame_style, and after that we simply declare that each window uses this one frame_style_set.  And there we have it.

There are many possible improvements, including dimming the button colours when a window isn’t focussed, and perhaps using the colours from the desktop theme instead of white all the time.

Edit: Stuart has tweaked the theme a little and uploaded it to gnome-look here.

Take that, Descartes

Die mosquito

Listen to this.

Here are two ideas with which I broadly agree:

  1. A theme format is less useful without a program to edit the theme files.  Having to modify XML by hand is not a prospect which most people relish.
  2. If there’s to be a third version of the Metacity theme format, it would be helpful if it was based around SVG, since theme files describe vector graphics and SVG has become a standard for such files.  It must not however sacrifice the ability of our current format to prevent themes making the computer unusable.

Back in August, I added simple SVG support to a branch of Metacity that I named “Vectacity“.  My project was shelved because of more urgent business elsewhere, but Iain has also done some work in this area.

In order to play around with the Vectacity format, I have since built a very simple version of an editor.  However, I have run into a theoretical problem for which I seek your insight, gentle reader.

There are certain problems which a theme file has to solve which are not faced by ordinary vector graphics.  One of them, which is easily answered, is the question of how to add the buttons which are currently in use to the titlebar, when this is not known in advance.  Another simple question is how to represent colours from the desktop theme.  A more serious problem is that the width and height of the window is not known in advance either, and nor is the width of the window’s title.  Metacity currently solves this problem using a simple expression language in which these variables can appear as terms.

However we solve this problem in Vectacity, it’s not a problem to modify the coordinates of elements according to the size of the window: the SVG in each theme file is going to have to go through a cleaner anyway before it hits the screen.  Some possible solutions include:

  • reuse. Use the existing expression language: <rect x="3" y="10" width="{w-3}" height="{h-3}"/>;.  This is how Vectacity currently works.  But this means we’re using the SVG namespace but including invalid SVG.
  • vectacity:expr. Mark the attributes which need recalculating, but leave precalculated values in the file: <rect x="3" y="10" width="997" height="997" vectacity:expr="width:w-3; height: h-3"/>.
  • stretchyspace. Subvert the concept of Cartesian coordinates by entering stretchyspace.  In stretchyspace there are 1001 coordinates; addresses below 0 and above 1000 are undefined.  Addresses between 0 and 300 are translated with respect to the left or top, addresses between 700 and 1000 with respect to the right or bottom, and addresses between 301 and 699 are as close to the centre as the address is close to 500: <rect x="3" y="10" width="997" height="997"/>.

Stretchyspace neatly keeps us SVG-compliant, but it’s a much less powerful representation than the existing system.  It’s true that the power of the existing system is rarely used to anywhere near its full extent, but there are existing behaviours which stretchyspace cannot represent.  In particular, titlebar decoration often places non-text graphics with respect to the length of the title:

Examples of the same titlebar shown in three themes
Fig. 1: Same titlebar, three themes.

The first line, for Crux, shows a graphic which has been pushed to the right by the length of the titlebar text.  The second, Atlanta, shows the window icon which is centred along with the titlebar text.  The third, Human, involves printing many copies of the titlebar text; this can be simulated in SVG using styling, but the other two are more of a problem, since they involve non-text participating in text flow.   If we’re using an expression language, this can be handled using coordinate substitution, but if we’re using something like stretchyspace, it will need to be handled in the SVG itself.  Non-text elements appearing in text flow may be possible in SVG1.2, but I don’t know how to do it in SVG1.1.  (Is there a way?  Do you know?)  For this reason, stretchyspace is not a useful answer for the representation of relative coordinates.

Another question we need to consider is whether external SVG editors should be usable.  There are many fine SVG editors around, such as Inkscape.  Allowing the use of one of these, with a tool which extracted SVG data and spawned an external editor, would permit people all the power of an existing application and save us the trouble of writing a new one.

If we are to use an external editor, though, we must consider how relative coordinates may be represented whilst within it.  One obvious conclusion is that we could use stretchyspace coordinates, but the same problems with title placement exist in this case as well; they could perhaps be worked around, hackily, since the title field in a saved SVG has a known length.  Other possibilities include using using the vectacity:expr field and copying its value into and then out of the description field of the relevant object, at a severe cost to usability.

If we are not to use an external editor, the value of using SVG at all becomes a little reduced.

Photo by kozzmen, cc-by-nc-nd.

The one where we argue about themes a lot

Dog washingThe Metacity theme format has been roughly the same for six years now.  Many people are quite happy with it as it is, but we fairly regularly get people coming along and asking why it can’t do this or that and how they can make it do so.  The trouble is that changing the theme format is a fairly large upheaval, and not something we’d like to do much.  Recently, two ideas have been put forward to change all that by making it not our problem to change the format if some newfangled idea comes about that we don’t support already.

  1. It’s GTK’s problem.  Metacity doesn’t draw borders at all any more.  This makes us a much more lightweight window manager, because all we’d have to do is keep out of people’s way and manage windows.  It also means that the toolkit can interact more easily with the borders, leading to fun stuff like these Tango experiments.  (Of course, this is a source of OOH SHINY, which gets people writing about GNOME more than anything else.)  Or as alex-weej points out, we could do things like this using standard libraries.  Downsides:  non-GTK applications have a bit of a problem; we would obviously still need to figure out what we’re doing about window border theme file formats; all this will need a lot of coordination between people from different projects.  However, it does have the seeds of great awesomeness within it.
  2. It’s librsvg’s problem. Make a new file format where everything is based on SVG files.  Feed these into librsvg, substituting results of expressions as appropriate.  There you have a simple format which can do anything librsvg can do, and keeps on growing as librsvg keeps growing.  (This is all possible; I’ve done a little bit of test work here.)  Allow no other instructions but a set of SVGs (I have the way to do this in my head if anyone’s interested, but I won’t bother writing this down if this is going to be a blind alley).  Add a script which can take out an SVG so you can edit it with Inkscape.  Disadvantages: it’s an evolution, not a revolution;  it’s slower than the current system in tests (though I haven’t tried to optimise it); it doesn’t let us do all the fun stuff the other option does.
  3. Both.
  4. Neither.

Your thoughts, Gentle Readers, both on the pros and cons of each and of the best choice?  I shall be updating the post with your ideas, with attributions.

Photo: © swizzled, cc-by-nc-sa

Update: See desrt’s mockup inspired in part by this entry.

Justifying window titles

It was said that Metacity doesn’t let you decide whether titles of windows are left-justified (as in modern versions of MS Windows) or centre-justified (as on the Mac). But actually, it’s a theme issue to decide how a title is drawn. Here’s how to change your theme from centre-justified to left-justified.

First, find out what theme you’re using. We’ll use gconftool to do this, because it’s the easiest to demonstrate.

$ gconftool -g /apps/metacity/general/theme
Human

Okay, so we’re using Ubuntu’s Human theme here (you may well be using something else, of course; substitute its name for Human in what follows). Presumably you don’t want to work on the main system version of the theme, so take a personal copy and tell Metacity to use it:

$ cp -R /usr/share/themes/Human ~/.themes/LeftHuman
$ gconftool --type=string -s /apps/metacity/general/theme LeftHuman

It will switch to the new theme, but obviously it will look identical at present.

Unfortunately, as I mentioned the other day, there’s no fancy editor for Metacity theme files: you’ll have to edit the file by hand. But it only needs to be done once. So, open ~/.themes/LeftHuman/metacity-1/metacity-theme-1.xml in your favourite editor and search for <title (“title” preceded by a less-than sign, i.e. the opening of a “title” tag in XML). You will see that any of these you find have a formula for their x attribute that involves taking widths and halving them, which screams about centring. All you have to do is change this formula to zero for all the title tags in the file.

However, in the Ubuntu Human theme they obtain a shadow effect on the letters by printing the title multiple times at different tiny offsets. For cases like these, you need to keep the offsets in order to keep the shadow effect. So you’d change
<title color="shade/gtk:bg[SELECTED]/0.75"
x="(3 `max` (width-title_width)) / 2 + 1"
y="(((height - title_height) / 2) `max` 0) + 2"/>
<title color="shade/gtk:bg[SELECTED]/0.7"
x="(3 `max` (width-title_width)) / 2 + 2"
y="(((height - title_height) / 2) `max` 0) + 2"/>
<title color="shade/gtk:bg[SELECTED]/0.4"
x="(3 `max` (width-title_width)) / 2 + 1"
y="(((height - title_height) / 2) `max` 0) + 1"/>
<title color="#ffffff"
x="(3 `max` (width-title_width)) / 2"
y="(((height - title_height) / 2) `max` 0)"/>

to:

<title color="shade/gtk:bg[SELECTED]/0.75"
x="1"
y="(((height - title_height) / 2) `max` 0) + 2"/>
<title color="shade/gtk:bg[SELECTED]/0.7"
x="2"
y="(((height - title_height) / 2) `max` 0) + 2"/>
<title color="shade/gtk:bg[SELECTED]/0.4"
x="1"
y="(((height - title_height) / 2) `max` 0) + 1"/>
<title color="#ffffff"
x="0"
y="(((height - title_height) / 2) `max` 0)"/>

and so on throughout the file.

When you’re done:

$ metacity-message reload-theme

and presto!

You might say that this is a lot of work compared to just selecting a “left justify” button somewhere, and it is, but assuming your original theme file was free software, you can distribute derivatives of it. So you should feel free to give your new left-justified version to your friends or stick it up on the web somewhere. Don’t forget to add your name and copyright in the <info> section first.

Understanding Metacity themes

cleaning windowsThis is an article about how to theme Metacity. It is a work in progress, and I have had to dig deeply to find some answers; I may well have made mistakes and I welcome corrections and suggestions.

GNOME lets you theme a bunch of different things, but we’re only talking about window border themes here, which some people call Metacity themes; Wikipedia begins a sentence with “Despite the incomplete state of Metacity theme development documentation”, and though there is documentation in the source, apparently not many people find it, and it’s written more for programmers than theme designers. Glynn Foster also wrote a very good introduction to Metacity themes [de] six years ago, but things have changed a little since then. Metacity themes can also be used by Compiz, and perhaps by other window managers for all I know.

So, a Metacity theme is a set of instructions about how to “decorate” (draw the borders around) a window. Presumably you don’t want to style all windows identically, so the format lets you specify details for different kinds of window:

  • state: Every window must be in exactly one of these states: normal, dialog, modal dialog (i.e. a dialogue which means you can’t interact with the rest of the program while it’s up), menu (torn off from the main application, not that people do that much these days), utility (that is, palettes and toolboxes and things), and border. X also allows a window to explicitly ask to be undecorated, but of course we don’t provide for those in a list of decoration instructions.
  • focused: Every window is either the active window (which X people call “focused”), or it isn’t.
  • maximized: Every window is either (fully) maximised (horizontal and vertical only don’t count), or it isn’t.
  • shaded: Every window is either rolled up to show just its titlebar (which techies call “shaded” for some reason I can’t fathom), or it isn’t.
  • If a window is not fully maximised and not shaded, it either allows horizontal resizing, or it doesn’t.
  • If a window is not fully maximised and not shaded, it either allows vertical resizing, or it doesn’t.

What’s in the file

The files must be called either

  • ~/.themes/N/metacity-1/metacity-theme-V.xml
    for a theme used only by you, or
  • /usr/share/themes/N/metacity-1/metacity-theme-V.xml
    for a theme installed for all users.

where N is the name of the theme and V is the version of the format. Version 2, introduced in October 2006, adds a few extra features, but it’s rarely used. Version 1 is the original format. The formats are fixed once they’re stable for both backwards and forwards compatibility; new features can’t be added without introducing a new version number, which is why improvements come out rarely and in large clumps. metacity-1 in the names is a fossil and doesn’t mean version 1 of anything.

The metacity-theme-V.xml files are GMarkup files, which are very similar to XML. For now, you actually have to write these in a text editor or something; you can either start with a blank page, or modify a theme someone else has made. (I am thinking of writing a general theme editor program, but that’ll have to wait until I’ve reduced Metacity’s open bug queue a little.) If you want to see a fully-fledged one, you can look at the current version of “Atlanta”, one of the simplest themes, but even that is quite complicated-looking at first.

So, let’s talk about what actually goes inside the files. As in any XML file, <!-- ... --> are comments. At its most basic, it would go:

<metacity_theme>
<!-- Helper stuff: -->
<info ...> <!-- to be explained -->
<constant ...> <!-- maybe; to be explained -->
<draw_ops ...> <!-- maybe; to be explained -->

<!-- Things we build the top level onto: -->
<frame_geometry ...> <!-- to be explained -->
<frame_style ...> <!-- to be explained -->
<frame_style_set ...> <!-- to be explained -->

<!-- And the top level: -->

<window type="normal" style_set="..." />
<window type="dialog" style_set="..." />
<window type="modal_dialog" style_set="..." />
<window type="menu" style_set="..." />
<window type="utility" style_set="..." />
<window type="border" style_set="..." />
</metacity_theme>

Matching windows

window: You see that at the top level we have a list of <window> tags, one for each window state we discussed above. The style_set argument of each of these gives the name of a frame_style_set.

frame_style_set: …tells Metacity how to draw windows according to whether they’re focused or not, maximised or not, shaded or not, and allowing resizing vertically, horizontally, both, or neither. It looks like this:


<frame_style_set>
<frame focus="F" state="S" resize="R" style="N"/>
<frame... />
...
</frame_style_set>

where:
F is yes for focused, no for unfocused
S combines the shaded and maximized flags: normal, maximized, shaded, or maximized_and_shaded
R represents resize permissions that the window gives us: none, vertical, horizontal, or both. Frame settings for maximised windows, which can’t be resized, don’t have this attribute.
N is the name of a frame_style to apply to a window which has these attributes.

A frame_style_set tag may also have a “parent” tag, which should be the name of another frame_style_set. This means that if Metacity wants to know about a kind of window which that frame_style_set doesn’t describe, it should look in the parent. Most of the more complicated tags in Metacity theme files also have a “parent” attribute which work the same way. This is particularly useful because, taken together, all the frame_style_sets in a theme file must be capable of matching every possible kind of window; if a window turns up that they can’t match, there will be an error at runtime.

Let’s recap what we’ve seen so far. The combination of a window, which matches a window’s state (normal, dialog, and so forth), with an entry in the corresponding frame_style_set, which matches its focus, shadedness, maximisedness, and resize permissions where relevant, will allow you to make a list of rules to match any window against. The next piece of this puzzle lets you specify what Metacity should do with such windows once it’s matched them.

Actually drawing stuff

frame_style: This is probably the most complicated part of the whole system. A frame_style is a series of pieces and buttons. It looks like this:

<frame_style name="..." geometry="G">
<piece position="P">
<draw_ops>
</draw_ops>
</piece>
...
<button function="F" state="S" draw_ops="D"/>
<draw_ops>
</draw_ops>
</button>
...
</frame_style>

The pieces are pieces of the window frame. When Metacity draws a window frame, it renders its various pieces always in the same order. The bolded parts are all the possible values of P:

  • the entire_background, covering the whole frame
  • the titlebar, covering the entire background of the titlebar
  • the titlebar_middle, the part of the titlebar that doesn’t touch its edges
  • the left_titlebar_edge, right_titlebar_edge, top_titlebar_edge, and bottom_titlebar_edge
  • the title, just exactly that area which is covered by the text on the titlebar
  • the left_edge, right_edge, and bottom_edge of the frame (yes, there is no top_edge: it’s identical to top_titlebar_edge, isn’t it?)
  • the overlay, which covers everything– the same as entire_background, but done last instead of first.

What Metacity draws in these pieces is decided by the theme. If a frame_style or its parents don’t specify a particular piece, nothing will be drawn for that piece. You have two ways to specify what to draw: one is that the piece tag can have a draw_ops tag inside it which lists a sequence of drawing operations in Metacity’s custom format. You might ask why we don’t use SVG; one answer is that SVG support wasn’t very strong when this format was designed, and another answer is that these days you can use SVG all you like; just include it as an image and Metacity will know what to do.

An alternative to including a draw_ops tag inside a piece tag is to add a draw_ops attribute to the piece tag. Then you can add a draw_ops tag at top level (inside the metacity_theme tag) with a name attribute, and Metacity will use that. This is useful if you use similar draw_ops over and over.

I’m not going to document draw_ops at present, because this is already very long. I will write it up later and link it from here.

The button tag tells Metacity how, but not where, to draw buttons. Buttons are drawn after all the pieces are finished, and the way to draw them is also given using draw_ops. You ought to provide buttons for all the possible kinds of button; if you don’t give one it won’t be drawn, which is unfortunate for the user who wants to use it:

  • left_left_background, left_middle_background, and left_right_background don’t represent buttons as such, but the background behind them, assuming there can be at most three buttons on the left. These days there can be more, so the extra ones also use left_middle_background.
  • right_left_background, right_middle_background, and right_right_background similarly.
  • close, minimize, maximize are the obvious original three buttons.
  • menu is the menu button you can click to get a list of actions you can perform on the window.
  • shade, above, stick are similar to the original buttons but only allowed in version 2
  • unshade, unabove, unstick are the toggled versions of these buttons. Again, version 2 only. It is not immediately apparent to me how the toggled version of maximize is generated, though I suppose I must have known at some point.

Update: The reason there are toggled versions of shade, above, and stick, and not maximize, is that by the time you get this far you’ve probably already decided whether you’re drawing a maximised window. So if you are drawing a maximised window, you can make the button called “maximize” look how you want the restore button to be; otherwise, make it look like you want the maximise button to be.

For each button tag you should also set a “state” attribute; this time the state is either normal (the way you see it most of the time), pressed, or prelight (this makes the buttons subtly light up when you hover over them). You only really need “normal”, but the others are good to have too.

The “geometry” attribute of a frame_style tag is the name of a…

Geometry

The geometry tag defines the sizes of things around the window. It is important, but not easy to explain, and again this file has gone on too long. I’ll write it up later.

Other things which lie around a file

The most important other thing in a theme file is the metadata held in the info tag. This contains a set of tags each of which contains some text explaining something about the theme itself, in a sort of Dublin Core sort of way. (Next time around, we should probably use the actual Dublin Core.) The tags are name, author, copyright, date, and description.

Version 1 of the format had a menu_icon tag at top level, which let themes specify the icons beside options in the menu you get from the menu icon. This has become redundant; the icons are taken from the icon theme! The tag can still be used in all formats, but does nothing and is deprecated.

Version 2 of the format has a fallback tag at top level, which let the theme specify what icon a window should be considered to have if it doesn’t provide an icon of its own. This should also be taken from the icon theme, if anyone fancies fixing it, and the tag should also then be deprecated. It shouldn’t be hard.

When you’re working on a theme

When you’re editing a theme, you can view it without using it on the whole desktop using
metacity-theme-viewer YourThemeName

and view it on the whole desktop using
gconftool --type=string --set /apps/metacity/general/theme YourThemeName

Whenever you change the selected theme in GConf, Metacity will load the newly-chosen theme. This is how control-center does it. But when you change a theme, as you’re working on it, you might want to ask Metacity to reload the theme which is currently used on the whole desktop to reflect your changes. You can do this using the little-known metacity-message program, with the command metacity-message reload-theme. This works by sending the ClientMessage _METACITY_RELOAD_THEME_MESSAGE to the root window, in case you’re interested.

Once you’re done with your theme, consider submitting it to the art.gnome.org site, or the gnome-look site.

The future

Please feel free to link to this so people don’t have to keep asking the basic questions and can start asking the deeper ones. One of the important deeper ones is: where should we go in the future? Since this format is becoming something of a de facto standard between window managers, should we set up some kind of freedesktop.org standards discussion? Would it be useful to spin off Metacity’s theme parsing code into a separate, LGPL-licensed library so that other applications could use it more easily?

What would a version 3 of this format look like? Could we simplify the window / frame_style_set system? (I can imagine abolishing both, and being able to write <frame_style for="normal+unfocused+maximized">... and having Metacity assume it applied to all resize permissions and shadednesses.) Maybe we should try to do everything with SVG we can? Getting more wild and handwavey, is it worth keeping XML-like? Maybe if other window managers were dealing with the files, .ini-style files would be more universally useful? Or perhaps not. And then of course we need a decent graphical editor for it. I have a few ideas, but if anyone fancies jumping in…

Photo: “Cleaning Windows”, cc-by-nc-sa, by John Harvey.

Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported.