Metacity and D-Bus

Zarko Drincic - The Electric Bus (Awarded by National Geographic Magazine)GNOME bug 531512 suggests that Metacity should have a D-Bus interface.  On the face of it, this is a good idea.  However, the problem lies in the existing EWMH specification, which allows a program to request operations from a window manager– simply put, it’s pretty much exactly what a D-Bus interface would be, but it already exists.  If we also exposed a D-Bus interface, even one called “org.freedesktop.WM” instead of “org.gnome.Metacity”, we wouldn’t be gaining anything we don’t already have, and then people would begin using it and their programs wouldn’t be compatible with other EWMH window managers.  So every WM that implements the EWMH would have to expose the same D-Bus interface, which sounds like a lot of work for not much return.  On the other hand, we could have a separate program which exposed a D-Bus interface which translated the methods into EWMH messages, and which could be used with any EWMH window manager.  Would that do as well?  What do you think, gentle reader?

Photo copyright Zarko Drincic, cc-by-nd.

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

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.

Notifisation

Fire Notifier

Listen to this.

Launchpad bug 124326 requests a new titlebar button which minimises an application to the notification area rather than ordinary minimisation. Mostly this is currently done with the close button on the apps which support it, but some people feel it would be cleaner if these two functions were distinct. This action has been given the name “iconification” by some, but since this is the name the X specification gives to what we now call minimisation, I propose the ugly word “notifisation”.

There are four problems with this idea.

  1. Adding new titlebar buttons is always problematic for reasons given earlier.
  2. The EWMH specification is going to have to include a way to tell which apps may be notifised, and a way for the WM to tell an app to notifise itself.  This is going to require arguing out on wm-spec-list.  In itself, this is not a major obstacle, but it’s important to be aware of.
  3. It is unclear what the real difference between minimisation and notifisation is in practice.  And if there is one, why shouldn’t all apps be notifisable?
  4. Using the notification area for things other than ephemeral notifications– that is, using it as a cheap way to make panel applets– is contrary to the Human Interface Guidelines.  Perhaps the HIG is wrong, but then we need careful thought before we give the practice a stamp of approval by enshrining it in the EWMH.  Besides, there is talk of enforcing notification ephemerality.

Photo by The Joy of the Mundane, cc-by-nc.

Dragging the window icon

Listen to this.

A user writes:

At the moment i’m trying to make a theme for metacity but I have no idea how I can make the document icon drag-able and to interact with the desktop environment i.e. drag to the desktop to save or to the trash to delete.

Firstly, let me thank you for being willing to work on developing the desktop.

To begin with, this idea was based on “Window Experiments”.  desrt developed this idea into a program called icanhasedit back in August, which worked by taking over the responsibility of drawing window decorations from the window manager.  That’s certainly one way of going about solving the problem, but it brings a whole raft of problems of its own.  So let’s assume we want to keep the decorator in the window manager here.

Secondly, there is nothing in Metacity corresponding to “the document icon” as such.  It happens that most themes draw the icon of the current window on the menu button– the button which shows the window menu when you click it.  Some themes do it other ways, though: for example, Atlanta draws the icon on the titlebar next to the window’s name.  But let’s assume what we want is for the menu button to be draggable.

So you need to implement drag-and-drop from the widget representing that button.  Once that’s done, you need to have a way to tell what should actually happen when the button is dragged somewhere.  The trouble is that Metacity doesn’t know much about what’s actually in the window: it knows the name and the icon and a few other things, but the actual content is the business of the application.  So you need to invent a way for the WM to tell the application that a drag is happening, and for the application to tell the WM what the data being dragged is, and what its type is.  Then you need to argue it out on wm-spec-list and then you need to get applications actually to implement it so it’s useful.

So: this is possible, but it’s complicated and probably not an ideal first project.  If you want to try solving it, please go ahead and I’ll give you what help I can.  If you’d rather do something easier first and ramp up to this, I can help you with that too.

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.

Know all men by these presents…

sheep  &amp;  her lambI tried to discuss the gtk_window_present() problem earlier, but I only managed to confuse myself. So here’s an extra rundown; perhaps we can move towards solving it.

The problem is that there’s a call, gtk_window_present(), which is very vaguely specified:

Presents a window to the user. This may mean raising the window in the stacking order, deiconifying it, moving it to the current desktop, and/or giving it the keyboard focus, possibly dependent on the user’s platform, window manager, and preferences.

There are at least four ways of dealing with this request.  I hope I can establish a consistent terminology for them here.  Suppose we are looking at workspace 1, and an application attempts to gtk_window_present() an existing window on workspace 2.  We can:

  • Summon: the window is moved to workspace 1, so now we’re looking at it.
  • Visit: the window stays still, but we move to workspace 2, so we’re looking at it.
  • Pulse: the window stays still, we stay on workspace 1, but we set “needs attention” so it pulses on the taskbar.
  • Nothing: we do nothing.  This is allowable, but I think we can discount it.

gtk_window_present() is implemented using _NET_ACTIVE_WINDOW, which according to its specification allows the WM to make the requested window active (which could mean either Summon or Visit), or Pulse, or Nothing.

Many applications use gtk_window_present(), and because of the vagueness of the spec, there’s no agreement on what they mean by it.  Three instances which have been identified are:

  • Firefox. If you click a link in your email program, it will summon your current browser.  If this is Firefox, and it’s appearing in a new tab, it will gtk_window_present().
  • Pidgin. If someone talks to you, it will gtk_window_present() the chat window.
  • Nautilus. If you use one window per directory, and you request a directory that is already open in another workspace, it will gtk_window_present() the window for that directory.

The disagreement extends even to what Metacity should do when asked to present windows in each of the cases above.  Here are some opinions; feel free to add your own in comments, or tell me where other people have asked for something, and I’ll update this:

  • Firefox: Kevin Downey says we want Pulse.
  • Pidgin: technicull says we want Summon. The devs themselves say they don’t care as long as it’s not Nothing.
  • Nautilus: I think we want Summon here.  David agrees.

Some things which Metacity should not do:

  • Decide which to do based on the application.  This is window matching, and thus a recipe for pain.
  • Add a switch somewhere.  As Havoc said, this boils down to asking “Do you want to break Firefox, or Pidgin?”  And many people (including me) run both.

When people complain about Metacity’s behaviour in this matter, it’s hard to tell whether they’re complaining about upstream or downstream Metacity.  Upstream Metacity does Summon in all cases.  Ubuntu has patched to do Pulse (except for dialogues, which do Summon), as has Fedora, and I believe SuSE has too.  As policy, we usually reconsider policy when several major distributions make the same change, and this means we should probably stop to consider what to do here.

It might be possible for the app itself to hint in the EWMH message which behaviour it wants, if it doesn’t want the default.  Patryk Zawadzki says that the behaviour should depend on the user’s action; if the user carried out some positive action to cause the gtk_window_present() (as in the Firefox case), it should result in Visit, but if it happened through some external event (as in the Pidgin case), it should always result in Pulse. Mathias Hasselmann concurs.  I think this seems a reasonable plan, but the distinction between events generated by the user and not is one which the app is best placed to make, and so we probably also need a hint here.  I’m happy to add hinting as a test; however, the default option will necessarily be “don’t care”, and therefore we will still have to choose a best option as default.

I think most applications may be happy with a default of Summon, and that a default of Pulse is at least better than Visit in most cases, but I’m entirely willing to be convinced otherwise.

What do you think?

Photo © jan glas, cc-by-nc-nd.

Communicating with Metacity

Quite often people ask whether Metacity can talk to you across D-BUS, or something similar.  It can’t.  There is no need for this, because you can do pretty much anything you want using X messages.  In particular, you can use messages from the EWMH specification to perform pretty much any task you might want, and there are some additional Metacity-specific ones (such as an instruction to change the theme).

If you want to play with a client which can send these messages, I recommend wmctrl.  Try installing it (it’s in most distros, I think), and then playing with it to see how you can

  • switch desktops
  • bring a window to a desktop
  • resize a window
  • maximise a window
  • minimise all windows
  • list all windows
  • rename a window
  • … and so on.

Read its source for all the details; it’s quite clearly laid out.

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.

If the user will not come to the window, the window shall come to the user

KissingSuppose you have two workspaces, and a window on each one. You’re looking at window A, so clearly window B is offscreen. You click something on window A, and window A attempts to present window B to you. What does that mean?

Let’s have two concrete examples:

  • 0x01: You’ve clicked a link in Pidgin’s buddy window, and it’s attempting to present the chat window to you.
  • 0x02: You’ve clicked a link in Evolution, and it’s attempting to present Firefox to you.

In 0x01, you want to stop looking at the old workspace and look at the new one.  But you don’t want the windows to move off their workspaces.  You want everything to stay where it is.

This is the way upstream Metacity currently works throughout.  However, since Firefox is a tabbed browser,((I know Firefox has had tabs since 2002)) people have been asking whether this is the wisest course all the time.  In case 0x02 above, in the old days, the browser would just have launched a new window in your workspace.  People don’t like that now, because they want all their tabs in the same window.  But if the user gets shoved onto the workspace of the existing window and then we add a new tab, eventually they’ll close it and then wonder where their mail went. (At least, that’s how I understand their argument; perhaps I’m mistaken.)  As a compromise, downstream Metacity has now been patched in Ubuntu, Fedora, and possibly other places to make the window demand attention when this happens (i.e. go pulsy on the taskbar).

So we have multiple options when this happens:

  • Bring the window to the user, always.
  • Bring the user to the window, always.  (This is what we do now.)
  • Make the window demand attention– in other words, apply the downstream patch.  This is not the path of least resistance, since judging by recent feedback it appears to really annoy anyone using, say, Pidgin.
  • Tell the target application to deal with it.  This would mean that Firefox could open a new window if you were on a workspace where it had no windows open and open a new tab if you were on a workspace where it had one already.  It would mean finding some way of dealing with windows that didn’t co-operate.  It would also mean, alone among all these solutions, that we’d have to find a way of communicating with the target application.
  • Ask the summoning application to give us a hint as to which of these it would like.  This is my (Thomas’s) favourite solution.  It will need a change to the EWMH.

Things which are not solutions:

  • Allowing the user to pick one and then requiring them to stick with it.  As Havoc said, this is basically giving them a choice between “break Pidgin” and “break Firefox”.
  • Window matching.  We do not do window matching.  We are not about to start for an issue as small as this.  That’s what devilspie is for.

Want to join in the argument fun?  Dive in at GNOME bug 482354.  The water’s lovely.

Photo credit: rofanator.

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