Multimonitor support in gnome shell

March 22nd, 2011 by alexl

I recently switched to using gnome-shell in my day to day work, and so far I like it a lot. However, I use a dual monitor setup, and the gnome-shell support for multihead was very rudimentary.

Here is an example desktop with a 1920×1080 primary monitor and a 1280×1024 monitor on the left:

And the corresponding overview:

There are a number of issues here:

  • All previews are on a single monitor. So, while we have a lot of space for normal windows with two monitors we need to cram them all into part of a single monitor in overview mode. This makes the windows way to small to be usable.
  • Due to internal issues the part of the overview that shows the windows is always the same aspect ratio as the screen (i.e. the total size of all monitors). This is fine in the case of one monitor, but with two monitors it doesn’t match the monitor aspect ratio making windows even smaller.
  • During the overview switch the windows on the extra monitor fly a long way, making the animation not work really well, as well as breaking the natural grouping of related windows being on the same monitor.
  • The extra monitor space is unused
  • The thumbnails that normally slide in from the side in a slick way instead overlap the other monitor and its hard to make slide in since the mouse enters the other monitor instead.
  • The thumbnails include the “dead” area due to the different sized monitors and generally look weird.

Additionally, but not visible here:

  • Its hard to hit the hot corner on the primary monitor if there is a monitor to the left. (And to hit the menu in the right corner if there is a monitor to the right.)
  • If the top-left monitor is not the primary there is no hot corner in the easy to reach corner, instead you have to mouse into the primary and then try to hit the corner.
  • If another monitor is taller than the primary, its very hard to hit the message tray on the bottom of the primary monitor, as the pointer passed over to the dead area.
  • In most typical cases the external monitor is used to show something static, like always visible information, a presentation on a projector, or a movie on a TV. However, the extra monitors are connected to the same virtual workspaces as the primary, so when you switch workspace the apps on the extra monitors switch around too.

The last few weeks I’ve been working on this, and these changes have now landed in git.

Here is how it looks now:

And the overview:

We can see here:

  • There is a window overview on each monitor, showing the windows on that monitor.
  • The window overview area is not artificially constrained by aspect ratios but as large as it can be, so the windows are larger in the overview.
  • The thumbnails are always slided in if there is a monitor to the right of the primary monitor.
  • The thumbnails only show the primary monitor. This is because the whole workspace feature only affects the primary monitor. Windows on extra monitors stay in place when switching workspaces, only the primary monitor changes.

Additionally some things we can’t see:

  • Every monitor with a natural top-left corner (i.e. the top left monitor, or a monitor to the right that is taller but aligned at the bottom) gets a hot corner that activates the overview.
  • The ever-rocking ajax has added pointer barrier support to the Xserver for us. If this is available (he backported it to Fedora 15, yay!) we add one-sided barriers in the panel and the message tray so that its easier to hit these, even if there are monitors on the sides.
  • Additionally, as part of the pointer barrier support the Xserver now automatically blocks the mouse from entering the dead areas that are not shown on any monitor. This is great if monitors have different resolutions.

These are not enormous changes, but the difference in day to day use in a multimonitor setup is like day and night. These should be in the next release which is out soon, so if you’re a multimonitor user, do try it out!

Yo Dawg!

March 16th, 2011 by alexl

Yo dawg! I herd you like browsers, so i put a browser in your browser so you can browse while you browse.

Gtk+ HTML backend update

March 15th, 2011 by alexl

The last few days I spent fixing up some more details in the new HTML5 gdk backend. Not everything is supported yet (keyboard input in particular is very weak), but much more things work now. Even thought the backend is not of production quality it is now good enough that I think its interesting for a larger audience to play around with. So, today I merged the branch into the Gtk+ master branch (i.e. what will be Gtk+ 3.2).

The new multi-backends setup in Gtk+ 3.0 makes this much easier to test. All you have to do is build Gtk+ with –enable-x11-backend –enable-broadway-backend.This will give you a normal X11-based Gtk+ where you can enable the broadway backend at runtime by setting the GDK_BACKEND enviroment variable.

The backend only supports firefox 4 atm, and Mozilla disabled websockets by default, so you have to enable enable websockets for any input to work. However when this is done you can test any app by running:

GDK_BACKEND=broadway your-application&
firefox http://127.0.0.1:8080/

I recorded a small screencast to show this stuff: (webm source file here)

Note that the recording is using a local connection, it will be slower over the network depending on the network bandwidth.

Gtk+ 3.0 html5 backend

November 26th, 2010 by alexl

I just landed the html5 backend for Gtk+ in git in the broadway branch of Gtk+.

It has several issues, as mentioned on the mailing list, but it does let people play with this code.

Gtk3 vs HTML5

November 23rd, 2010 by alexl

The last few weeks I’ve been working on an interesting new idea, hacking out a prototype.

The code is not really clean enough for public consumption yet, and a bunch of features are missing. However, its now at the stage where it can be demoed and evaluated.

I think the best way to introduce it is via a video: (original theora file)

Basically, its a backend for Gtk+ 3 that renders in a browser.

A more techincal description for the web geeks among us:

Each toplevel window is mapped to a canvas element, and the content in the windows is updated by streaming commands over a multipart/x-mixed-replace XMLHttpRequest that uses gzip Content-Encoding to compress the data. Window data is pushed as region copies (for scrolling) and image diffs. Images are sent as data: uris of uncompressed png data.

Input is gathered via dom events and sent to the server using websockets.

Right now this is Firefox 4 only, but it could be made to work in any browser with websockets.

Now, I want to know, Is this useful?

There are two basic ways to use this, you can either run your own apps on your own server and access it from anywhere (kinda like screen). Or you can put it on a public server that spawns a new instance of the app for every user (gimp on a webpage!).

If you had this technology, what cool stuff would you do with it? What apps would you run, and how would you use them?

Tracing glib

January 4th, 2010 by alexl

I saw a blog entry where Krishnan Parthasarathi at sun played with static dtrace probes in glib and it seemed like a really nice thing. Also, the systemtap developers at Red Hat continue to kick ass, and I’ve long wanted to play around with their work. This seemed like a great thing to try it out on.

I’m running Fedora 12 here, and it includes systemtap 1.0 and utrace, so it support the same kind of static probes that dtrace does (in fact, the markers are source compatible with dtrace). So, I wrote an initial set of dtrace/systemtap probes and a systemtap tapset (a library for easier using the probes in systemtap) for glib and gobject.

Its kind of limited atm, but it lets you trace memory allocation in glib, gobject lifecycle issues and signal emissions. Already this lets you do some pretty cool things, like this alive.stp:

global alive
probe gobject.object_new {
 alive[type]++
}
probe gobject.object_finalize {
 alive[type]--
}
probe end {
  printf ("Alive objects: \n")
  foreach (a in alive) {
   if (alive[a] > 0)
     printf ("%d\t%s\n", alive[a], a)
  }
}

Which you can run like this:

stap alive.stp -c "gedit /etc/passwd"

Giving something that starts:

Alive objects: 
72   GParamObject
1    GdkDisplayManager
1    GdkDisplayX11
7    GParamPointer
17   GParamDouble
1    GdkScreenX11
...

Another example is signals.stp which is a simple example to look at signal emissions:

probe gobject.signal_emit {
 printf("%s --> %p[%s]::%s\n", thread_indent(1),  object, type, signal);
}
probe gobject.signal_emit_end {
 printf("%s <-- %p[%s]::%s\n", thread_indent(-1),  object, type, signal);
}

Some example output (again from gedit), shows how size allocation works for the main window:

14406 gedit(7492):  --> 0x00000000021c2040[GeditWindow]::realize
 14862 gedit(7492):  <-- 0x00000000021c2040[GeditWindow]::realize
 14872 gedit(7492):  --> 0x00000000021c2040[GeditWindow]::check-resize
 14881 gedit(7492):   --> 0x00000000021c2040[GeditWindow]::size-request
 14890 gedit(7492):    --> 0x00000000021a2950[GtkVBox]::size-request
 14899 gedit(7492):     --> 0x000000000236f090[GtkHPaned]::size-request
 14907 gedit(7492):      --> 0x000000000236f190[GtkVPaned]::size-request
 14915 gedit(7492):       --> 0x0000000002378010[GeditNotebook]::size-request
 14927 gedit(7492):        --> 0x000000000235ac30[GeditTab]::size-request
 14935 gedit(7492):         --> 0x00000000023b79f0[GtkScrolledWindow]::size-request
 14944 gedit(7492):          --> 0x00000000023825c0[GtkHScrollbar]::size-request
 14954 gedit(7492):          <-- 0x00000000023825c0[GtkHScrollbar]::size-request
 14963 gedit(7492):          --> 0x0000000002382730[GtkVScrollbar]::size-request
 14973 gedit(7492):          <-- 0x0000000002382730[GtkVScrollbar]::size-request
 14980 gedit(7492):         <-- 0x00000000023b79f0[GtkScrolledWindow]::size-request
 14987 gedit(7492):        <-- 0x000000000235ac30[GeditTab]::size-request
 14995 gedit(7492):        --> 0x00000000023b9df0[GtkHBox]::size-request
 15002 gedit(7492):        <-- 0x00000000023b9df0[GtkHBox]::size-request
 15009 gedit(7492):       <-- 0x0000000002378010[GeditNotebook]::size-request
 15015 gedit(7492):      <-- 0x000000000236f190[GtkVPaned]::size-request
 15021 gedit(7492):     <-- 0x000000000236f090[GtkHPaned]::size-request
 15028 gedit(7492):    <-- 0x00000000021a2950[GtkVBox]::size-request
 15034 gedit(7492):   <-- 0x00000000021c2040[GeditWindow]::size-request
 15044 gedit(7492):   --> 0x00000000021c2040[GeditWindow]::size-allocate
 15051 gedit(7492):    --> 0x00000000021a2950[GtkVBox]::size-allocate
 15060 gedit(7492):     --> 0x000000000236f090[GtkHPaned]::size-allocate
 15067 gedit(7492):      --> 0x000000000236f190[GtkVPaned]::size-allocate
 15075 gedit(7492):       --> 0x0000000002378010[GeditNotebook]::size-allocate
 15084 gedit(7492):        --> 0x000000000235ac30[GeditTab]::size-allocate
 15092 gedit(7492):         --> 0x00000000023b79f0[GtkScrolledWindow]::size-allocate
 15101 gedit(7492):         <-- 0x00000000023b79f0[GtkScrolledWindow]::size-allocate
 15107 gedit(7492):        <-- 0x000000000235ac30[GeditTab]::size-allocate
 15119 gedit(7492):        --> 0x00000000023b9df0[GtkHBox]::size-allocate
 15128 gedit(7492):        <-- 0x00000000023b9df0[GtkHBox]::size-allocate
 15134 gedit(7492):       <-- 0x0000000002378010[GeditNotebook]::size-allocate
 15140 gedit(7492):      <-- 0x000000000236f190[GtkVPaned]::size-allocate
 15146 gedit(7492):     <-- 0x000000000236f090[GtkHPaned]::size-allocate
 15153 gedit(7492):    <-- 0x00000000021a2950[GtkVBox]::size-allocate
 15159 gedit(7492):   <-- 0x00000000021c2040[GeditWindow]::size-allocate
 15165 gedit(7492):  <-- 0x00000000021c2040[GeditWindow]::check-resize

This is really cool and useful stuff.

Unfortunately the current systemtap static marker implementation is a bit inefficient, so I wouldn’t at the moment enable this for a shipping production glib. However, this can and will be fixed (I even proposed some ideas of how to do this in the systemtap bugzilla).

New member in the family

October 16th, 2009 by alexl

On wednesday 19:13 (CET) my wife gave birth to a boy of 4.1 kg. Both mother and son are well.

So, I’ll be rather busy with non-computing things in the near future. Don’t expect replies or feedback from me.

GObject performance on 64bit arches

October 12th, 2009 by alexl

I just ran pahole on a Nautilus and got this:

struct _GObject {
 GTypeInstance              g_type_instance;      /*     0     8 */
 volatile guint             ref_count;            /*     8     4 */

 /* XXX 4 bytes hole, try to pack */

 GData *                    qdata;                /*    16     8 */

 /* size: 24, cachelines: 1, members: 3 */
 /* sum members: 20, holes: 1, sum holes: 4 */
 /* last cacheline: 24 bytes */
};      /* definitions: 138 */

Obviously this is a 64bit machine, so the qdata has to be 64bit aligned, but the ref_counter is only 32 bit. This leaves us with 32 bit of unused space. And at the same time we do all sort of bad-ass slow atomic hacks to extract the low two bits of the qdata pointer.

We could easily use two of these bits instead of the qdata bits on 64bit machines, and avoid lots of unnecessary atomic complex handling of qdata. And we might be able to use these extra bits for other (non-mandatory) performance tricks.

The gospel of git: Interactive Rebase

October 12th, 2009 by alexl

Today I saw this in my scrollback buffer:

<believer1> so, I changed the patch to not break dbus proto, tested it ... problem is,
            that it is followed by the sftp bit ... what is the best practice? 
            cherry-pick to the different branch apply and amend? or reset apply amend?
<believer1> any special tricks?
<believer2> checkout the commit you wanna edit into a different branch, change it, then
            rebase the original branch on top of it
<believer2> i think that's what i always do
<believer1> sounds reasonable

And it was good. These disciples are trying to follow the holy commandments of git, including the important:

Thou shalt keep a clean version history

However, while they are good of heart, doing the best they can, they are not properly enlightened on the book of rebase in the gospel of git. Fortunately such enlightenment is easily come by with some reading and training.

The first rule when it comes to rebases is:

Don’t fear the rebase

Rebasing is history rewriting operation, and most fear of the rebase come from the fear of losing ones history. This is only natural, after all those who lose their history are doomed to rewrite it again. However, once a change is comitted git never loses it (unless you manually gc which I recommend never doing). If you rewrite the history of a branch and the branch now points to this new history, the old commits and history are still stored in your .git directory. All you need to know is the sha1 id of the last commit before your rewrite and you can always access it. For instance, if you’re on a branch that you completely messed up you can get everything back using:

git reset --hard <old-sha1>

This will reset the current branch so that it points to the old history.

If you’re uncertain of how rewriting works and fear something will go wrong, just store away the previous commit which you can easily get using e.g. gitk (or using “git rev-parse HEAD” if you want to be hardcore). And, if you forgot to do this, things are not lost, you can use “git reflog” to find the old history.

So, having overcome our fear of the rebase, how does one actually use interactive rebasing in this case? The background is that you have a bunch of local commits on you branch, each implementing a small independent change (so called microcommits), but before this is applied or merged into a public repo you discover a bug in one of the changes in the middle of the series. It would be nice if such a bug was never visible in the final version history. What to do?

Typically what I do is commit the fix like usual, with a short commit message like “Fixed up the foobar change”. Then you start the interactive rebase using “git rebase -i origin“. This will bring up your chosen editor with a bunch of lines in them, looking something like this:

pick fa1afe1 Implement the foobar function
pick 124efd3 Use the foobar function
pick cafe123 Fixed up the foobar change

Each line corresponds to one commit on your branch that will be applied during the rebase. You can change the order of the lines to change the history order, or you can delete lines to drop certain commits. What we want to do here is move the fixup to just after the right commit and then change “pick” to “squash” (or just “s” for short) which will merge the two changes into one. So change it to this, save and exit:

pick fa1afe1 Implement the foobar function
s cafe123 Fixed up the foobar change
pick 124efd3 Use the foobar function

This will bring up an editor with the commit messages for the first two commits which you edit (often just remove the second one), save and exit. Then the rest of the rebase is done and we end up with a clean history.

The true disciple of git always uses a local branch for each feature he works on, and then when it is done, it is merged into master and then pushed. I must confess that I sometimes sin here, doing minor feature development directly on master, rebasing to clean up and then just pushing that. Sometimes when I do this it turns out that the feature was more complicated than I expected and I didn’t finish it before I had to work on something else. If you end up in this situation its very nice to know that you can then create a branch afterwards by just doing:

git branch <branchname>
git reset --hard origin/master

This (if run on master branch with “origin” being the upstream repo) will create a new branch with the given name that contains your local changes, then it will change the master branch so that it points to what upstream points to, allowing you do do other work and return to the branch later.

gdb over irc

October 9th, 2009 by alexl

Python scripting in gdb 7.0 kicks total ass. Today I played around with one cool usecase for it: Remote debugging.

I don’t know how many times I have tried to help someone debug over irc, with the person cutting and pasteing gdb commands and results into xchat. Well, no more! Today I hacked up a gdb python script and an xchat python script that lets you export a gdb session over irc:

From gdb:

(gdb) source gdb-server.py 
gdb irc server, Waiting for connection on /tmp/gdb-socket-500

In xchat:

> /load ~/xchat-gdb.py
Loaded xchat-gdb
> /join #test
> /gdb connect
connecting to gdb...
connected!

Loggin in as another user on irc:

> /join #test
> alex: gdb print "yey"
<alex> $6 = "yey"
> alex: gdb bt 2
<alex> #0  0x00000035010d50d3 in *__GI___poll (fds=<value optimized out>, nfds=<value optimized out>, timeout=-1) at ..
+/sysdeps/unix/sysv/linux/poll.c:87
<alex> #1  0x00007ffff52f96cc in g_main_context_poll (n_fds=<value optimized out>, fds=<value optimized out>,
+priority=<value optimized out>, timeout=<value optimized out>, context=<value optimized out>) at gmain.c:2904

Obviously this is kinda unsafe as you can do all sorts of things via gdb. It needs to be able to limit who can control your gdb instance, and you should only give such permissions to people you trust.

Git repo here, have fun with it.