Extra buttons

Listen to this.

Sometimes, as in GNOME bug 562650, people ask for extra buttons on the titlebar to go along with the standard set.  On the face of it, if you may bind a keystroke to some action, there is no reason why you should not be allowed to add it to the titlebar.  The problem is, though, that somehow it must be drawn with a recognisable icon.  This has led to the current policy that all themes must declare all possible buttons, and that no buttons are allowed outside that set.

Version 1 of the theme standard required all themes to declare how to draw menu, minimize, maximize, unmaximize (restore), and close.  Version 2 allowed the addition of shade, above (“always on top”) and stick (“on all workspaces”).  If you’re looking to add one of those functions, as GNOME bug 562650 actually is, the solution is to switch to a theme which has a copy in version 2; unfortunately, there aren’t many which have adopted this version.  Bright and Crux both support it, though:

metacity-theme-2

But requiring a theme to contain all permitted buttons restricts the number of buttons which can reasonably be permitted.  In the future, how can we permit any buttons to be added?  We could require unknown as a button type, which would certainly solve the problem of not being able to draw a button, but the user who was faced with two or three buttons bearing question marks might object.  Or we could allow buttons we didn’t know to be decorated with text saying, for example, “stick“,  which would work but wouldn’t be beautiful.

Alternatively, we could require all possible buttons to be declared in the base theme, Atlanta, and simply allow fallback.  This is reminiscent of the way fonts work: a font may contain any Unicode character, but most fonts don’t attempt to cover the whole range.  This means that people wanting to write any language which includes characters outside the ASCII range would often run into holes in their words– so the system will use a glyph from another font as a substitute.  This often leads to ugly rendering, though, even for names from a language as common and well-known as French:

He was Edmond Dant(oops!)s... and he was my father.
He was Edmond Dant(oops!)s... and he was my father.
Similar results would no doubt be common with a similar system for buttons.

Window matching

glass pane, shatteredWindow matching is the process of identifying a new window as one we’ve seen before.  Of course every new window is new, and so we’ve never seen it before, but there’s an intuitive understanding that if you open a document in OpenOffice and then come back to it a week later that the window is in some way the same.

Some good properties to use for this are:

  • SM_CLIENT_ID, which is set by the session manager for a particular instance of a running program. XSMP says that “a unique value called a client-ID is provided by the protocol for the purpose of disambiguating multiple instantiations of clients.”
  • WM_WINDOW_ROLE, which is optional.  The ICCCM says that “the combination of SM_CLIENT_ID and WM_WINDOW_ROLE can be used by other clients to uniquely identify a window across sessions.”  If there is no WM_WINDOW_ROLE, the ICCCM tells us to fall back on WM_CLASS and WM_NAME…
  • WM_NAME is the window title.
  • WM_CLASS is a two-part property.  Let’s call these Instance and Class, although these aren’t the real names. Instance is generally the name of the program, although it can sometimes be set on the commandline.
  • Class is “the general class of applications to which the client that owns this window belongs”, i.e. the brand name of the program (rather than “editor”).

Metacity does not currently do window matching for three good reasons.

1. Separation of concerns: in-process or not?

The EWMH allows us to do window matching outside the WM, and in fact this is what Devil’s Pie and gdevilspie are for.  They have all the other problems, but they’re not part of the WM, and if they crash they don’t bring the WM down.  It appears that because this can be done outside the WM it should be.  (Isn’t this fun?  We can pretend we’re the Hurd.)  Feel free to argue the point, of course.

2. Separation of concerns: app or WM?

It is also possible that nobody except the application itself knows where its windows should be placed, and that everything should be left up to the apps (and in practice the toolkits).  This would allow us to do away with session management entirely.  Certainly it would mean that it was none of Metacity’s business.

3. Paucity of distinguishability: who sets what?

Devil’s Pie requires you to set up rules to identify windows.  Doing window matching at the window manager level implies that the rules are written for you automatically.  This wouldn’t be a problem, except that applications set the attributes above very inconsistently.  Although WM_NAME is always set, it isn’t necessarily reliable– for example, gedit adds a star to it when you begin editing a document. I believe therefore that we cannot use it in automated window matching.

As to the others, let’s introduce a notation: Role|Instance|Class.  If Role is missing, we write [NONE]|Instance|Class.  Here is a program called same.c which will print these values for you.  Some common examples:

  • Firefox sets browser|Navigator|Firefox in all cases.  It is therefore not possible to distinguish Firefox windows from one another except by WM_NAME.
  • Thunderbird sets [NONE]|gecko|Thunderbird-bin, but it usually only has one window open.
  • nautilus sets [NONE]|nautilus|Nautilus and it is therefore impossible to tell the difference between Nautilus windows except by WM_NAME.
  • inkscape sets [NONE]|inkscape|Inkscape similarly.
  • gnome-calculator sets [NONE]|gnome-calculator|Gnome-calculator.
  • gnome-terminal sets gnome-terminal-SOME-LONG-STRING-OF-NUMBERS|gnome-terminal|Gnome-terminal with the string of numbers different each time.  This gets an A.
  • epiphany sets epiphany-window-SOME-LONG-STRING-OF-NUMBERS|epiphany-browser|Epiphany-browser with the numbers differing.  Another A.
  • The GIMP sets gimp-XXX|gimp|Gimp where XXX is “toolbox”, “dock”, “tip-of-the-day”, etc.  This gets an A+.

Please feel free to comment with more results and I’ll add them.

Conclusion

  • People often complain that Metacity doesn’t do window matching.
  • There are many reasons why it shouldn’t (but feel free to disagree in comments).
  • Window matching of any kind is needlessly difficult because the important properties are set inconsistently.
  • Therefore for the sake of window matching in general, whether we do it in Metacity or not, it would be a useful exercise to patch all programs which don’t set roles as well as the GIMP does.  (gtk_window_set_role() is the relevant GTK function; it’s not widely used.)  Perhaps this could become a GNOME Goal.

Photo: Glass pane, shattered, © nchenga nchenga, cc-by-nc.

Annotations

Perfection in the MarginsMany people, from various programming communities, have come to the conclusion that if functions are first-class objects they should be allowed properties. For example, if a function in a test suite is checking for regression of the fix to a particular ticket, it could have a property for the ticket number. Then other parts of the code can deal with it in a consistent way through introspection, without keeping related information all over the place.

In Java, these are known as annotations and look like this:

@ticket(177177)
public static void testFrobulation () { ...

Python also calls them annotations (replacing the older system of decorators, which looked similar to the Java syntax but achieved its effect in a way more like Lisp macros). Annotations in Python look like this:

def testFrobulation(ticket: 177177): ...

In the past few months, two infelicities of the Metacity codebase presented themselves. Firstly, some translators complained, rather reasonably, in GNOME bug 469361 that the same information was repeated over and over again in the long descriptions of the keybindings.  Secondly, the same information about keybindings was repeated over and over in several places in the code, and in many of those places it was required to stay in the same order as the other places and would cause subtle breakage if it didn’t.  And of course, when anyone provided a patch to add a new keybinding it needed to modify several more files than would seem really necessary.  What would have been best is to be able to write something like

static void @keybinding ("Activate the window menu", "<Alt>Space")
handle_activate_window_menu (MetaDisplay    *display, ...

But you can’t really do annotations in C (despite a half-arsed attempt by Microsoft). Or can you?

In Dr Dobb’s Journal of May 2001, Randy Meyers introduced an idea called x-macros. This idea is the mildest of preprocessor abuse: you create a file containing lines of grouped data, like these:

item (switch_windows, "Move between windows, using a popup window", "<Alt>Tab")
item (switch_to_workspace_1, "Switch to workspace 1", NULL)
...

Then you #include the file anywhere you want to work with this data, but you #define item() as appropriate each time.  This way, the data only needs to be modified in one place, and it’s guaranteed to stay in order.  It occurs to me that what we have here is pretty close to annotation.  True, it’s in another file, but C also requires that the prototypes of public functions be in a separate file, and nobody minds about that.

So Metacity in trunk is now working fine using this system.  There are a few places it could be neater;  screen and window bindings have always been kept separate, but it would possibly make sense to keep them together and distinguish them (as they already are distinguished) by the cunning use of flags.  Also, the handler functions should probably be given by name and not calculated from root and suffix, because it’s just confusing.

This whole idea also means that the GConf schemas which concern keybindings can be generated during the build process, which solves GNOME bug 469361 rather neatly.  In conversation with Rodney Dawes it also came up that most of the short and long versions of the keybinding descriptions say the same thing in twice in different words, which is an unfair burden on the translators.  I have merged them as appropriate: the number of strings has gone down by about thirty, and the .pot file is now 70% of its former size.

Photo: Perfection in the Margin, © Adam Shaylor, cc-by-nd.

Constraints

JCN releases himself from handcuffsWhen a window changes position or size, there are rules of thumb about how it should happen.  The trouble is that sometimes in real life these rules of thumb are contradictory, and we need a way to decide between them.  This all used to be done in an ad-hoc way, but in 2005 Elijah swooped in and made one much more elegant system out of the chaos.  In this system, every rule of thumb is represented by a constraint. There are currently nine of these, and going from most to least important they are:

  • constrain_partially_onscreen: “Some part of every window must appear on the screen.”
  • constrain_titlebar_visible: “Generally, some part of every window’s titlebar should always appear on the screen.”
  • constrain_size_limits: “If the application has requested that its window should not grow bigger or smaller than some size, we should honour that.”
  • constrain_fullscreen: “A fullscreen window should take up the entire xinerama area.”
  • constrain_maximization: “A maximised window should take up the full screen.”
  • constrain_size_increments: “If the application has requested that its window’s size should only be a multiple of some number, we should honour that.”
  • constrain_fully_onscreen: “It’s nice if the whole of the window appears on the screen at once.” (This rule doesn’t apply if it’s you, the user, moving the window about; you’re presumed to know where you want the window.)
  • constrain_to_single_xinerama: “It’s nice if a window appears on a single xinerama monitor.”
  • constrain_aspect_ratio: “If the application asks to have a window of a particular aspect ratio (say, square, or φ), it’s nice if we honour that.”

Every one of these constraints is represented by a C function which takes arguments of the window in question, a flag to say whether it’s allowed to modify the window, and some other things.  The return result is ignored if the function wasn’t allowed to modify the window.  But in read-only mode, it must return false only if it could never be satisfied with the window as it is.  If it returns true, the function either thinks the window is all right as it is, or believes itself capable of changing the window until it’s satisfactory.

In order to enforce constraints, first of all we call all the constraint functions, permitting them to make changes, and ignoring whether they say they were satisfied.  Then when they’re done, we call them all again in read-only mode; if any of them tells us it can’t be satisfied, then clearly there’s an inconsistent set of things we’re wanting here.  (Perhaps we should also have a constraint called constraint_i_want_a_pony.)  So we go round again, but we lower our expectations a little by taking away a group of the least important constraints, to see whether that helps us gain unanimity.  If not, we go round again with the next most trivial group removed, and so on.

If you want to know more, you should read Elijah’s overview of constraints and then consider diving into the code.  Some current bugs concern constraints, so it would be good for people to try to get their heads around them.

Photo credit: Amit Gupta, cc-by-nc.

The overview series: Drag and drop. You complain, we explain.

drag onIf there are two overlapping windows on the screen, people would like to be able to pick up an object from the lower window and drag it to the upper without bringing the lower window to the front, because if that happens the lower window will obscure the upper, and you won’t have anywhere to drag to. In this instance, we would like the same behaviour as Microsoft Windows: if the click starts a drag, raise the lower window on button release; if it doesn’t, raise the window on button press as normal.

However, Metacity (along with most other window managers) doesn’t currently do this, for want of a way to know whether the click starts a drag. This is really something that only the application owning that window can tell us. (It is possible for the user to tell us what they think, by holding down AltGr at the start of the drag. That may not be an official feature. It’s not really ideal either way.)

This whole question is something we’ve been batting around for six years now and it probably ought to be fixed one way or another. Over that time, there are also a few other reasons people have asked to be able to pick stuff up from lower windows, such as the ability to copy text from the lower window and paste it into the upper, or scrolling the lower window’s scrollbars: GNOME bug 76672 deals with this more general case, which we shan’t discuss further here now. Let’s concentrate on the most common problem, represented by GNOME bug 80984: not raising the source window when a drag and drop begins. What isn’t a solution to our problem?

What isn’t a solution

  • Always raising the lower window only on release, not on click (suggested by many people). This would solve the problem at the cost of weirding everyone out, not just breaking the expectations of existing Metacity users and users from other window managers in the world of free software, but also the expectations of Mac and Windows people.
  • Only raising a window when you click on the frame and not the insides, which was raised in GNOME bug 86108. This is a bad idea for similar reasons to the last.
  • Having a magic kind of window that Metacity promises never to raise; then the client will decide whether to raise itself or not based on whether the click was the start of a drag operation. This is how Sawfish does or did it. It’s a bad idea because it rather defeats the purpose of having a window manager if clients are going to manage their own windows, and besides applications can’t raise their own windows in Metacity anyway.

What is a solution
What needs to happen is this:

  1. We figure out a way for other clients to tell the window manager that a click in their window was the start of some kind of drag-and-drop operation.
  2. At this point, the fact that Metacity doesn’t understand this message suddenly becomes a bug in Metacity. So we fix the window manager to understand this.
  3. At this point, the fact that none of the applications out there understand how to tell Metacity about this becomes a bug in those applications, but we can’t do anything much about it without fixing the toolkits like GTK. So we do that.
  4. Now we can actually fix all the applications separately. The bug for fixing Nautilus at this point is GNOME bug 132339.

Clearly we can’t get 2, 3, and 4 sorted until we have 1 down, so let’s just talk about that for the moment. Back in 2004, Lubos Lunak (the maintainer of KDE’s window manager) proposed the first plan to do this, called _NET_WM_TAKE_ACTIVITY (a misleading name, since it’s about taking focus and not activity). When a window other than the topmost one was clicked, the window manager would send it _NET_WM_TAKE_ACTIVITY, which it would remember; after that, nothing would happen until the button was released. If the click had actually begun a drag-and-drop operation, that was all well and good, but if it hadn’t, the client should send it on to the root window and the window manager would raise the window after all. In GNOME bug 152952, Elijah Newren wrote a patch for Metacity implementing this plan.

Lubos’s original plan had a few infelicities, some of which were discussed in this meeting. It means that the window is raised when you release the mouse button, which is bad for reasons we discussed above.  It also means that a lot of policy is decided ahead of time: for example, some people would like their window manager to raise the lower window while they were copying text from it, and then drop it back down when they were done, but not do the same thing for drag-and-drop.  There was working code for KDE and GNOME, but many people objected about all the problems mentioned above including the GTK hackers.  In the end it didn’t make it into the EWMH standard, although some parts of the KDE libraries appear still to accept it to some extent.

Elijah then proposed to fix the problem with a new message type called _NET_WM_MOUSE_ACTION. With this plan, a client would send _NET_WM_MOUSE_ACTION through to the root window as soon as any button was pressed or released on it, telling the window manager what kind of action the click meant: it could be “nothing special” or “drag-and-drop”, but also “text selection” or “scrollbar drag” or “generic thing that I don’t want to explain right now but involves not raising me”.  Lubos agreed that this was a better plan, but it died even earlier in committee, and as far as I know was never implemented anywhere.

It seems to me that the best thing to do, if we can, is to go with a partial fix using _NET_WM_MOUSE_ACTION which allows us to heal this obvious problem.  Then we can carry on later and fix specific problems.  Elijah has said that _NET_WM_MOUSE_ACTION needed a great deal of work to implement on the GTK side; the closest thing we have so far to working code is a patch he then posted.  This does still need working on, preferably by someone who understands the internals of GDK (could this be you, gentle reader?).

A similar but not identical problem is the issue of raising windows when they are a drag target; this is covered in GNOME bug 112308.

Next in the overview series: why getting stacking exactly right is hard and what we’re going to do about it.

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

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.

Start reading here

This is not the Metacity home page. There is no Metacity home page. This is for the same reason there is no flashy logo: Metacity strives to be quiet, small, stable, get on with its job, and stay out of your attention.

But this is a place where you can find out about Metacity. The maintainers and contributors post here, and we invite and provide feedback. So, what sorts of things can you expect to see here?

  • Announcements of releases.
  • The Metacity Journal appears semi-regularly, when things have been busy in the Metacity world. It discusses:
    • notable changes to the code in the last day or so
    • links back to people who have written about Metacity in their own blogs and discussion of what they say
    • news articles mentioning Metacity
    • activity on bugs (either on GNOME’s own bug tracker or downstream). These are often about issues which concern users, and you should always feel free to leave a comment giving your own opinion. We thrive on it.
  • When there’s a lot of work being done on a particular sub-project, there will probably be regular posts on that project here. An obvious example was Iain’s compositor.
  • anything else we think of.
  • anything else you think of, possibly.

Comments on posts are open for the first few weeks, but are always moderated to keep the spammers away.

Photo credit: Li’l Bear. cc-by.

How Metacity creates an alt-Tab list

1926PepFrenchNovoTabsI’ve been asked to explain how to add new items to the list of programs which appears when you use alt-Tab to switch between programs in Metacity. This is a bit of a simplification, but it goes like this:

When the user is switching between applications using alt-Tab, Metacity has what’s called a “keyboard grab” on the X server. This means that it gets told about all the keyboard events, and no other X client hears about them. The grab is initiated by meta_display_begin_grab_op() and ended by meta_display_end_grab_op().

Everything Metacity needs to know about a screen’s tab popup is kept in a MetaTabPopup called “tab_popup” which lives inside the MetaScreen representing that screen. In a newly-created MetaScreen, it’s null, because there’s no popup on that screen. It gets initialised by meta_display_begin_grab_op() calling meta_screen_ensure_tab_popup() (or meta_screen_ensure_workspace_popup() if we’re actually needing to switch between workspaces, rather than applications), both of which call meta_ui_tab_popup_new() in order to actually allocate and initialise the memory.

Before it calls meta_ui_tab_popup_new(), however, meta_screen_ensure_tab_popup() needs to prepare a list of MetaTabEntry items to display in the popup. It does this by calling the function meta_display_get_tab_list(), which is where the magic truly lives.

meta_display_get_tab_list() returns a GList of MetaWindow objects. (meta_screen_ensure_tab_popup() converts the MetaWindows to MetaTabEntrys.) This is where the actual list of windows that you see when you press alt-Tab comes from.

Now, there are actually three kinds of list that meta_display_get_tab_list() might be asked for, and these are represented by the possible values of the MetaTabList enum parameter “type”. META_TAB_LIST_NORMAL means the ordinary alt-Tab sequence you’re used to, of ordinary user application windows. META_TAB_LIST_DOCKS means that what’s needed is a list of system windows; you can see this in action by pressing alt-ctrl-Tab. META_TAB_LIST_GROUP means that what’s needed is a list of all windows (even dialogues, etc.) in the same application group as the current window.

How do we deal with this headache-inducing complexity? meta_display_get_tab_list() does the sensible thing and delegates it (in this case, to a macro). It iterates over every window in the requested workspace and adds it to the result list if

  • it’s on the same screen, and
  • the IN_TAB_CHAIN macro returns true for it. We will come back to this in a moment.

The result list is ordered firstly by whether the window’s minimised (minimised windows come first, presumably because this is standard behaviour on certain other operating systems); after that, most recently used windows come first. You can verify this using alt-Tab on your own desktop.

The IN_TAB_CHAIN macro (in display.c) is written in terms of the three macros META_WINDOW_IN_*_TAB_CHAIN (in window.h). Each of these takes a single argument, a MetaWindow, and returns true or false depending on whether it’s in that particular tab chain.

The original reason I was asked to explain all this was a request to add all windows (on any workspace) which have the urgency hint to any newly-created tab chain. This wouldn’t be a suitable thing to add to the META_WINDOW_IN_*_TAB_CHAIN macros, because the list of windows passed to those is pre-filtered to include only windows on the current workspace. Instead, it should be added to meta_display_get_tab_list() as a separate, final step.

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