Java and Multiple Desktops

If you’re using a single monitor, this article won’t be of much use to you. For the rest of you who have two (or more) monitors plugged into your box, you may be able to glean something of useful from this post. I’m sure the topic has been written to death elsewhere, and while there’s a few different ways to accomplish the same thing, this is my solution.

An example and the wrong solution, told as a story

First, the problem. If you’re written any GUI applications, I know you’ve done this at least once before: You create your GUI, you start attaching widgets to it, you launch it, you debug it, and about two or three hours into the project, you’re growing increasingly more annoyed with the window positioning. Maybe the window is attaching itself to the upper left corner, or maybe your window manager is genuinely trying hard to do the Right Thing thus leaving your application to appear randomly around the screen in a feeble attempt to cascade against something that doesn’t exist. Frustrated, you do something like this (SWT example):

// This assumes that display has already been created elsewhere.
// app is a class representing the main window for the application.
private void centerOnScreen ()
    Rectangle bounds = new Rectangle(0, 0, 0, 0);
    Rectangle desktopBounds = display.getBounds();
    bounds.x = (desktopBounds.width / 2) - (app.DEFAULT_WIDTH / 2);
    bounds.y = (desktopBounds.height / 2) - (app.DEFAULT_HEIGHT / 2);
    bounds.width = app.DEFAULT_WIDTH;
    bounds.height = app.DEFAULT_HEIGHT;
    shell.setBounds(bounds); // That'll teach 'em.

Your application is now centering itself on the screen. Great! No longer do you have to hunt around for the silly thing during debugging to drag it around, resize it, or otherwise mutter unsavory curses under your breath.

You then plug in a second monitor, and immediately those same unsavory words you uttered earlier have grown significantly worse. Now, with a second monitor, your nifty centerOnScreen() method centers the application between windows. “Curses,” you say, “I though I had the damn thing fixed!” Since your boss was kind enough to purchase two identical monitors, you figure you’ll draft up a quick fix:

// This assumes that display has already been created elsewhere.
// app is a class representing the main window for the application.
private void centerOnScreen ()
    Rectangle bounds = new Rectangle(0, 0, 0, 0);
    Rectangle desktopBounds = display.getBounds();
    bounds.x = (desktopBounds.width / 2) - (app.DEFAULT_WIDTH / 2);
    bounds.y = (desktopBounds.height / 2) - (app.DEFAULT_HEIGHT / 2);
    bounds.width = app.DEFAULT_WIDTH;
    bounds.height = app.DEFAULT_HEIGHT;
    // XXX: No one will ever have a monitor greater than 1920 pixels wide.
    if (desktopBounds.width > 1920)
        bounds.x = (desktopBounds.width / 2 / 2) - (app.DEFAULT_WIDTH / 2);
    shell.setBounds(bounds); // That'll teach 'em.

After such harrowing labor, your application is now back to normal and centering itself on the left-most monitor. You smile smugly, commit the changes, and go home for the day.

The next morning, you get an e-mail from one of the other developers in the office. He’s not particularly happy:

From: Bob Jones <>
To: UI Design Team <>
Subject: what’s wrong with the ui?

Hey guys, I just noticed that some changes made since yesterday have the application appearing kind of off my left screen. It spills over a bit onto the right monitor.

For what it’s worth, my monitors are of two different sizes so it’s kinda funky.

Oops. Dividing the desktop size in two (you have two monitors of identical dimensions) and then dividing that number in two doesn’t quite work when individual attached screens differ in width. Worse, what happens when someone buys a monitor with a horizontal resolution greater than 1920?

From: Milton Pencilpicker <>
To: UI Design Team <>
Subject: App keeps appearing waaaaaaay off to the side


Did you say the app was supposed to start centering after the changes made yesterday? It’s still broken. It centers vertically just fine, but it’s about a quarter of the way over to the left. Just thought I’d let you know.

Yeah, this solution isn’t going to work. You need to account for resolutions on a per screen basis. So what do you do?

Probe some screens

Java exposes individuals screens through the GraphicsEnvironment singleton. The advantage of this method over getBounds() (or the Swing equivalent) is that you can easily determine the default device. Here’s one such example:

private GraphicsDevice defaultDevice;
private int defaultDeviceOffset = 0;
private ArrayList<Dimension> screens;
private int totalWidth = 0;
 * Probe attached displays.
 * This method collects data related to all attached displays.
 * For illustrative purposes, we're recording the dimension of each
 * attached screen and recording it in the local screens arraylist.
 * We then add up the total screen width.
private void probeDisplays ()
    GraphicsDevice[] devices = GraphicsEnvironment
    defaultDevice = GraphicsEnvironment
    for (int i = 0; i < devices.length; i++) {
        if (devices[i].equals(defaultDevice))
            // Do something when we encounter the default device.
            // One example would be to calculate the total screen
            // width thusfar. For our example purposes, we're going
            // to record the default device offset versus all other
            // attached screens.
            defaultDeviceOffset = i; // Mostly meaningless; sample purposes only.
        DisplayMode dm = devices[i].getDisplayMode();
        Dimension d = new Dimension(dm.getWidth(), dm.getHeight());
        totalWidth += dm.getWidth();

By recording individual screens, our centerOnScreen method can now be performed on a screen-by-screen basis:

 * Revised centerOnScreen.
private void centerOnScreen ()
    // Center the window based upon the default device dimensions.
    Rectangle bounds = new Rectangle(0, 0, 0, 0);
    int widthSoFar = 0;
    for (int i = 0; i <= defaultDeviceOffset; i++) {
        widthSoFar += screens.get(i).getWidth();
    bounds.x = (screens.get(i).getWidth() / 2) - (app.DEFAULT_WIDTH / 2) + widthSoFar;
    bounds.y = (screens.get(i).getHeight() / 2) - (app.DEFAULT_HEIGHT / 2);
    bounds.width = app.DEFAULT_WIDTH;
    bounds.height = app.DEFAULT_HEIGHT;

By collecting metrics from the GraphicsEnvironment singleton, we’ve established what 1) the default device dimensions and and 2) have established code that will automatically correct for centering the application on the default device regardless of whether it is to the left or right of the other monitor or monitors.

Some Improvements

I want to keep this article short, so I’ll offer some suggestions for further improvements.

  • If you wanted to restore the window position after it has been closed and restarted, just record the current window boundaries. Assuming monitor positions don’t change between launches, the application will restart from the last position it was closed at.
  • It would be trivial to examine the window positions just prior to restoring the previous session to determine if it will be drawn within the boundaries of the desktop. You could either examine each screen individually (more accurate) or by gathering the desktop boundaries regardless of display (less code, less accurate). The best option is certainly to compare the window’s last position with the dimensions and structure of the probed screen devices. You can correct for changes in resolution, missing monitors, and a few other unexpected situations.
  • This example only corrects for the most common scenario where desktop monitors are side-by-side. This is the default on Windows without 3rd party software and is generally a safe assumption. For platforms using Xorg (or similar), individual monitors can be to the left, right, top, or bottom of the primary display. NVIDIA’s drives provide fairly fine-grained control over monitor positioning, so if you’re targeting a multi-monitor environment under *nix, you may wish to do further testing and examine window positions based on height.
  • This code can easily be adapted to Swing.
  • While the sample code won’t work out of the box with JFace, it shouldn’t be too difficult to modify the sample code so it’ll work. Be sure to use getShell() to obtain window locations and dimensions but be mindful that this will probably break if you try to access the shell object during a close action. I’ll be making another post to demonstrate one possible solution.

Should you find bugs or would like to offer corrections to the example code in this article, please feel free to post. Obviously, corrections or suggestions that extend beyond the spirit of the code won’t be included. If you would like to offer criticism, please post full code snippets (even if you’re copying part of what I’ve written in this article) for clarity; should someone new to Java come across my blog, you would be doing them a fantastic service by limiting the amount of vertical scrolling necessary to read the example code and your critiques. :)

I waive all rights to the code in this article and hereby place it into the public domain.

No comments.

The End of Gentoo

Gentoo’s dead.

Before I hear you say “Long live Gentoo!” I should add that, yes, I know it has an active supporting community. Certainly there are other forks of Gentoo (Sabayon and Funtoo among the most well known of these) that contribute fixes upstream. There’s even a lively community discussing things on the gentoo-dev list. There’s also a lot of good, clever and very smart individuals working on Gentoo from around the world. I envy each and every one of them and admire their dedication and their technical capabilities and contributions.

For me, however, Gentoo is dying on the vine. It will continue to live on for others–there’s plenty of dedicated souls who are more than willing to support it, run it on their servers, home computers, and suffer through another rough period in Gentoo’s colorful history–but my days of running Gentoo are coming to an end. Why? Because I feel that:

  • It lacks clear direction
  • Recent security-related issues are a concern
  • Package support and maintainers are dwindling in number
  • Changing personal preferences

Clear Direction?

Does Gentoo have a clear direction? I think so–or I used to. When the maintainer of OpenRC left to move on to bigger and better things, a lengthy discussion took place, and unsurprisingly, some individuals were calling out for Gentoo to follow suit with other distros to use other init systems. Fortunately, I think the conclusion at the end was that OpenRC will still have a home with Gentoo, and it will be moved (or moved back) into being an in-house project. The discussion does raise some red flags, and I sincerely hope Gentoo doesn’t change toward adopting init systems that go against the spirit of the distribution. Personally, I liked OpenRC–it was fast, it showed a great deal of promise, and that there are forces within the community that would rather move toward a more mainstream (and bloated) init system is worrisome. I’m not sure that sentiment is shared with the majority of developers (I hope not), but that this is even a concern is troubling because I do not want to migrate my systems to one or more different init systems over the next year or two.

I think it’s also fair to deduce that Gentoo no longer pushes updates as frequently as other distributions. This isn’t necessarily a bad thing–and some might argue that it’s a sign of maturity–but it does call into question whether or not certain critical security patches will ever be rolled out relatively quickly. Moreover, many languages, libraries, and other developer-centric tools aren’t being included or updated as frequently as other distributions. Updates, if available, seem to stagnate in ~arch for an eternity before they’re deemed suitable for general availability. While I don’t want Gentoo to become yet another Debian or Ubuntu, subsequently pushing out moderately unstable software in effort to stay on the bleeding edge, I would have appreciated the integration of some established tools–like PHP 5.3–sooner. PHP 5.3 wasn’t introduced into portage until May of 2010 at version 5.3.2, and I think this sort of delay is endemic to a good chunk of the system. That’s to say nothing about how long it took to adopt Python version 2.5 several years ago, mostly thanks to Portage’s dependency on Python, but at least Python 3 has been available in parallel with Python 2 for quite some time. Other software isn’t so lucky.


Years ago, Gentoo used to push updates to various critical components like glibc often enough to coin the joke “I’m sorry I can’t go out tonight, I’m having to recompile my kernel again.” Unfortunately and for various reasons, the security team has been stretched to the point that critical vulnerabilities have gone untouched for an undesirably long time. This isn’t the fault of Gentoo, nor is it the fault of anyone in particular. As Tobias Heinlein stated on the mailing list:

The Gentoo Security team is functional, but running on low flame. There is a huge backlog (a huge amount of open bugs and GLSAs that still need to be sent) and due to a small amount of active members not all bugs are filed/handled in a timely manner and bigger packages (Firefox, Java, etc.) are not easy to draft GLSAs for for various reasons.

There’s just too much work to do and too few hands to do it. It certainly explains the IA32 emulation issue on 64-bit multilib builds, and it possibly explains why advisories like this one were delivered an uncomfortably long time after the exploits were discovered. Of course, patches are no replacement for end-user security as, ultimately, security rests on the shoulders of the systems administrator.

There is a silver lining. In spite of Gentoo’s recent lapse with pushing updates, they have still managed to beat both Microsoft and Adobe in terms of patch turn around and deployment.

Package Support

Whether or not Gentoo is currently hemorrhaging developers is something of a rumor. I don’t think it is. I do think that the current economy has put unwanted constraints on individual contributors who have already been constrained by time; that’s expected. Every distribution sees a slow turn over rate for developers, and I think Gentoo is in that phase where the number of developers leaving is somewhat greater than the number of new developers taking their place. I deduce this mostly by anecdotal evidence. Also, things like the following don’t help either (this is a comparison between several major players in the F/OSS OS market):


[sagittarius:~]$ emerge --search yui
[ Results for search key : yui ]
[ Applications found : 0 ]

Ubuntu 10.10 (server)

[corvus:~]$ aptitude search yui
p   libjs-yui                                                             - Yahoo User Interface Library
p   libjs-yui-doc                                                         - Documentation and examples for the Yahoo User Interface Library
p   yui-compressor                                                        - JavaScript/CSS minifier

FreeBSD 8.1

[exfar:~]$ portsearch -n yui
Port:   yui-3.2.04_2
Path:   /usr/ports/editors/yui
Info:   Rich-featured multi-windows console text editor
B-deps: autoconf-2.67 autoconf-wrapper-20071109 m4-1.4.15,1 perl-5.10.1_2
Port:   yuicompressor-2.4.2
Path:   /usr/ports/www/yuicompressor
Info:   The Yahoo! JavaScript and CSS Compressor
R-deps: desktop-file-utils-0.15_2 gamin-0.1.10_4 gettext- gio-fam-backend-2.24.2 glib-2.24.2 inputproto-2.0 javavmwrapper-2.3.5 jdk- kbproto-1.0.4 libX11-1.3.3_1,1 libXau-1.0.5 libXdmcp-1.0.3 libXext-1.1.1,1 libXi-1.3,1 libXtst-1.1.0 libiconv-1.13.1_1 libpthread-stubs-0.3_3 libxcb-1.7 pcre-8.10 perl-5.10.1_2 pkg-config-0.25_1 python26-2.6.6 recordproto-1.14 xextproto-7.1.1 xproto-7.0.16
2 ports

There are comparisons like this all across the board. It holds true for the YUI libraries, for the Spring framework, and a handful of other minor packages here and there that I’ve run into over the last year and a half. It should be noted that there were three reasons I migrated from FreeBSD to Gentoo some 5-6 years ago: 1) the ports collection wasn’t being updated as frequently as I would have liked, 2) the ports collection often didn’t have packages I needed or wanted, and 3) FreeBSD (-release–I don’t know about –current or –stable from the time period) did not easily support the new printer I had purchased whereas Gentoo did. Oh, how the tables turn.

Now, it should be stated that missing packages aren’t the end of the world. One can easily go search for the latest version, find it, and install it. That’s not the problem. The problem is that as I’ve gotten older (lazier) and more time constrained (impatient), I like to spend less and less time hunting down sundry packages and more time actually doing things. Package managers are the path of least resistance, and as long as they make something easier to do, I’m going to use them. ./configure && make && make install was fun when I was 20. I’m now 29 and have a gazillion different hobbies in addition to paid labor (usually writing code but mostly dealing with data migration and the sorts). I don’t want to have to fool around with some inane package manager any more than necessary (although I do like Portage). Which brings me to…

Personal Preference

This is the one point no one is going to be able to argue with. Gentoo is a labor of love. I used to enjoy having authoritarian control over every little package on the system. It was fantastic building a new install and having only those packages I wanted (and their dependencies) and nothing else. Nowdays, any time I run an:

# emerge -pvu world # or system

…and see more than 30 packages listed, I breathe a sigh of frustration. I used Gentoo as my primary desktop OS for about 2 years between 2006 and 2008, so I know how long it takes to build X, build KDE, and build the various silly libraries needed to support each of them (and whatever else I wanted). In fact, I still have that particular installation of Gentoo sitting on my other drive at home. Ever since I took a .NET class during my last year of college, I realized that sometimes it’s better to get things done than it is to sit, twiddling my thumbs, waiting for that overnight build to finish so I can check e-mail. (Then getting frustrated and turning on my laptop or cancelling the build and booting to Windows.) Granted, I always loved having two disparate operating systems on the same machine: If one failed for whatever reason (Windows), I could boot to Gentoo and still get stuff done. Anymore, though, I’m rather wishing I had installed Ubuntu in place of Gentoo. If I had a nickle for every hour I spent waiting for various window manager breaking emerges to finish…

Crux of the Matter

The crux of this entire long winded post is that I’m getting really damn frustrated with Gentoo. I love the operating system. I’ve used it for years. (Side note and minor correction: I actually first used Gentoo in late 2003/early 2004, but I don’t count those because my home server was still running FreeBSD.) I want to continue using it, but I’m afraid my patience is wearing thin. I seem to remember that a particular friend of mine Tweeted about dumping Gentoo for the U-word earlier this year. I hate to say it, but I’m afraid I’m going to follow suit. “Enter Ubuntu,” as he said. Enter Ubuntu indeed.

And to think: I hate Debian with a passion. I guess there are other BSD-ish options, although I do seem to see VirtualBox OSE in the FreeBSD ports collection… Tempting!

Update July 24th, 2011

Gentoo’s not dead! Rather, that is for me it isn’t. This entire post should have been taken with a grain of salt, but I’ll explain in depth later this week why I feel that this post was not only entirely unfair, but also why I feel that Gentoo has redeemed itself, and why I believe competition is important (or more to the point: why sampling the competition is important).

No comments.

Twisted Python and IPv6

» I hate walls of text. Take me to the downloads! «

Updated October 11th, 2010: Added support for Twisted applications and epoll (and possibly kqueue).

Updated December 3rd, 2010: Changed a few things with the patch distribution. See comments for details. Be aware that this information is or will soon be deprecated. Twisted 10.2 is now available along with a number of improvements, and this patch will likely be ported to plugin status. I am leaving this post mostly intact for historical purposes, although corrections have been made to switch the namespace to tx from twistedpatch to clarify further that this patch has absolutely nothing to do with the wonderful folks who write Twisted.


IPv6 support doesn’t exist in the base distribution of Twisted Python (the current version as of this writing is v10.1). Over the years, there have been several inquiries related to IPv6 support including a proposed patch, but support for the protocol is still forthcoming. Personally, I don’t like patching a base distribution. After all, it’ll be overwritten the next time I update, and I really don’t want to go through the effort of patching a second time. And who’s to whether or not the patch will apply cleanly in a few months? If you’re not certain this is an important subject, consider that some statistics estimate IPv4 exhaustion will occur in about 230+ days. Food for thought.

My solution is a little different than just simply modifying a handful of files somewhere in $PYTHONPATH and borrows from some of the methods Zope has used for quite some time with their hot fixes. Instead of patching Twisted directly, I have elected to monkey patch Twisted at runtime. This holds several advantages:

  • Whenever IPv6 support is finally added to Twisted, it should be fairly simple to remove this patch. Simply change two lines: Replace the appropriate import statement for the Twisted reactor and replace the listenTCP6 method call with whatever Twisted eventually settles on.
  • The patch consists of logically separate Python code and is therefore easier to maintain, update, and make other interesting changes to.
  • Zope uses monkey patching to issue hot fixes; if it’s good enough for them, it’s good enough for us.
  • There’s no need to modify your base Twisted distribution. That’s what this patch does at runtime. This means less hassle every time your distribution pushes an update to Twisted.
  • Less code, less duplication. All IPv6 implementations in this patch make use of existing Twisted classes. Nearly everything is provided by subclassing IPv4 Twisted classes and overriding the appropriate methods. This means that IPv6 support should be functionally equivalent to IPv4–when it’s finished.

A word of warning: This will not currently work with .tac Twisted apps and you it has not been tested with a reactor other than the select reactor. This patch only works if you are launching the reactor yourself. If someone wants to take this and modify it to work with twistd, feel free. It shouldn’t be too hard. Also, be aware that IPv6 support appears to be going forward in the Twisted base distribution, so this patch is probably superfluous. I have posted a version of the Twisted patch that should work with twistd. This means you can now use the patch in your .tac Twisted application code! The same caveats apply, of course, and there are some additional usage instructions. See below. You should only consider this as an interim solution. This patch is not a perfect solution, it isn’t the best solution, but it is a solution that precludes you from having to patch Twisted directly.

It’s worth mentioning that this patch uses listenTCP6 instead of outright replacing listenTCP; the latter appears to be the preferred solution. I don’t necessarily agree. Instead, I recommend using the listenTCP6 nomenclature, because it provides the opportunity to listen separately on IPv4 and IPv6 interfaces simply by changing the method call. However, if you wish to use a single method (like listenTCP) to listen on both IPv4 and IPv6 interfaces, simply comment out line 86 in

            s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1)

But before you do, you should probably consider the implications of using IPv4-mapped addresses which is what the socket may fall back to using when an IPv4 endpoint is connected. You were warned.

I also argue that adding another method like listenTCP6 is in developers’ best interests because this reason is no longer valid. DNS AAAA records extend IPv6 support to the same hostname an IPv4 address can reference; after all, how does one expect implemented the dancing turtle?

Usage Instrutions – Using the Reactor Directly

Usage is exceedingly basic. Extract the twistedpatch into your source directory and replace this import:

from twisted.internet import reactor

With this import:

from tx.ipv6.internet import reactor

This does not import a separate reactor. Instead, what you receive is the exact same reactor import as you would from importing twisted.internet (in fact, that’s what the twistedpatch does) with the listenTCP6 method patched into it. You may then change your application code to read as follows:

if __name__ == "__main__":
    app = MyApp()
    reactor.listenTCP(8080, app)
    reactor.listenTCP6(8080, app) # Add IPv6 support.

Usage Instrutions – Making a Twisted Application

As with the listenTCP call, I have committed further blasphemy and added a new class to the Twisted application framework. This allows you to isolate and control IPv4 and IPv6 instances independently without having to worry about IPv4-mapped addresses and other interesting side effects. Remember: This solution isn’t optimal; the Twisted developers would much rather have a single TCP instance to keep the API clean. (I disagree, because IPv6 is a different protocol entirely compared to IPv4… but no matter.) Here’s how you would construct a Twisted application with this patch:

# Import the Twisted service. DO NOT import the internet features.
from twisted.application import service
# Import the internet features separately from the twisted patch.
from tx.ipv6.application import internet
application = service.Application("myapp")
internet.TCPServer(8000, Application()).setServiceParent(application)
internet.TCP6Server(8000, Application()).setServiceParent(application) # Note the use of TCP6 here

This will launch two separate factory application instances, both on port 8000, using IPv4 and IPv6. You can control each instance independently.

Obligatory Warnings

I have neither presented this patch to Twisted Matrix Laboratories nor have I addressed IPv6 with them in any way. Thus, you should infer that this patch is entirely unauthorized and unsupported by Twisted Matrix Labs. This patch is mostly unfinished and I have yet to run any of Twisted’s unit tests on it. I suspect they will probably fail. It does, however, appear to work rather well with Cyclone:

2010-10-06 14:54:05-0600 [HTTPConnection,0,2001:470:d:407:2d62:6624:eac6:1b96] 200 GET / (2001:470:d:407:2d62:6624:eac6:1b96) 3.29ms

Effectively, here’s what you should expect:

  • There is no UDP support. Yet. It should be trivial to add. Simply subclass the appropriate items in twisted.internet.udp and copy the results into tx/ipv6/internet/
  • Address resolution as supplied by Twisted will probably break. Frankly, anything related to addresses internally in Twisted will probably break when faced with IPv6. This is expected.
  • listenTCP6 will listen only on IPv6 addresses. This is by design; if there is a compelling reason to outright replace listenTCP, I may implement that in the future. As it stands right now, I suspect that IPv4-mapped addresses have a potential to cause more issues than allowing them would otherwise solve. I also don’t believe that this encumbers the Twisted API in any way. After all, Twisted does have listenUNIX for domain sockets–why not add something unique for IPv6?
  • Importing the reactor from tx.ipv6.internet instead of twisted.internet should not break existing code. If it does, there’s a problem. This patch does not introduce a new reactor, it simply patches the existing one.
  • This patch is fully unsupported by Twisted Matrix Laboratories. Do not submit tickets to them if you have this patch installed; instead, revert the two changes (mentioned above), and test your code. If the problem persists, then it is probably a bug in your code–or a bug in Twisted (unlikely). If the problem disappears, then it’s a problem in my code, and you may complain to me about it. Do not complain to Twisted Matrix under any circumstance unless you are absolutely convinced that it is a problem with Twisted.
  • I am not a particularly good Python programmer, and this patch was written hastily in about a half hour. My expertise currently lies in the unfortunate realm of PHP and Java with about 3 or 4 other languages (Python included) added somewhere into that mix. I don’t know enough about Twisted internals or Python to know whether or not I am committing a horrible blasphemy with this patch. Thus, if you don’t like this patch, please submit corrections or write your own. I really don’t mind. I’m just one guy who happens to want IPv6 support distributed as widely as possible, and if this patch can help meet or inspire others to meet that goal, I’ll be happy.


If I haven’t scared you off with the notion that this patch might just kick your puppy late one night or bring a swarm of locusts barreling through your neighborhood, perhaps you wish to give it a try. Download it here: