Edgy: A mouse-based interface for X

Edgy lets you perform actions by clicking at the corners and edges of the screen.

The actions edgy can perform are:

You configure edgy by associating combinations of

with actions.

Edgy is meant to be used in addition to a window manager: most window managers (and applications) can be controlled by key press or program actions.

Why edgy

The edges and corners of the screen are easy to hit with the mouse (see any good book on UI theory) but generally underused (a notable exception is the menu bar in the Mac OS). It's so easy that not only is it easier than, for example, hitting a button on the border of a window, but I can often do it without looking at the mouse pointer!

Once you've tried controlling your windows this way you might even remove the visible controls from your windows (how you do that depends on your window manager). The original motivation for edgy was that I wanted to use only minimal decorations on my windows in order to increase screen real-estate and reduce visual clutter. So I needed another way to perform window actions.

But edgy isn't just for controlling windows. I don't like having to find an exposed part of the desktop (the root window) to get at its menu, so instead I have right-click in the upper-right corner of the screen bring up the menu. I like to change the font size a lot in Mozilla, but the Mozilla I have doesn't have visible controls for that. But it does have key bindings, so I have mouse scroll up/down at the left edge of the screen generate those keys when Mozilla has the focus.

Download

edgy.tar.gz

Put it in a new directory, change to that directory and do:

    tar xzf edgy.tar.gz

Configuration

To configure edgy edit the file configuration.in and recompile edgy.

The end of the configuration file contains the association between triggers and actions in the following format:

    EFFECTS {
      {trigger, action},
      {trigger, action},
      ...
    };

The distribution has an example configuration file. The configuration file is case sensitive. Spacing isn't important, but punctuation is. (There's one more detail that shouldn't affect most of you: if you need a " or \ character inside a menu label, command or application class then you need to put a \ before it like in the following example: "Email \"mom\"").

Triggers

TRIGGER(button #,
        one of: TOP BOTTOM LEFT RIGHT TOP_LEFT TOP_RIGHT BOTTOM_LEFT BOTTOM_RIGHT,
        optional "application class")

Examples:

The order of triggers matters: the first match is used. So, e.g., TRIGGER(1, TOP_LEFT) before TRIGGER(1, TOP) can be worthwhile, but switch their order and the second one can't occur.

On most systems¹ button numbers are: left: 1, middle: 2, right: 3, scroll up/down: 4/5.

The trigger occurs when the button is released: if you move away from the trigger area(s) and then release, it doesn't trigger.

To determine the class of an application, run xprop (included with your X distribution) and click on the application: the second string following WM_CLASS(STRING) in the output is the class.

If you click in an area of one of the triggers but none of the triggers matches (because it's the wrong button or focused application), the click is passed on with the mouse pointer moved one pixel towards the inside of the screen².

¹ You can check your button mapping by running: xmodmap -pp. To watch button events you can run xev from a terminal and click inside xev's window (the information goes to the terminal).

² A plea to application authors: don't put inactive borders separating your menus and other controls from the edge of your application's window (or each other for that matter). When you do, users have to stop exactly one (or a small number) of pixels short of the edge of the screen when they have your application full screen. If you don't understand this, or don't believe it's important, please read "The Humane Interface" by Jef Raskin.

Actions

KEY(optional area, optional modifier key, optional modifier key, key)

Examples:

Simulate the press of key.

The modifier(s) you include are held down while the key is pressed. You can use xev (see above) to determine key names: look for "(keysym 0x...., key-name)" in the output, and then use XK_key-name.

The names of areas are the same as for triggers. If you include an area, when the action is triggered it will first move the mouse pointer to that part of the currently focused window. I find this useful for actions that start window moving or resizing.


STATEFUL_KEY(modifier key, button #, key, button #, key)

Example:

This is for things like cycling through windows, where a modifer key needs to remain held down. When triggered the modifier key is pressed. Subsequent button presses press and release the corresponding key, until you move away from the trigger areas or press a different button. Then the modifier key is released.


COMMAND("command")

Example:

Run an application.

Technically, it runs it as:

    /bin/sh -c command &

MENU(menu-items-name)

Example:

Pop up a menu with the labels and actions in MENU_ITEMS(menu-items-name) (see below). You can use the scroll wheel (as well as mouse movements) to move between menu items: try it! It can also be fun if the trigger for this action is a scroll.


GESTURES(area, menu-items-name, cursors-name)

Example:

Start a gestural pie menu with the actions from MENU_ITEMS(menu-items-name) and the cursor shapes from CURSORS(cursors-name) (see below).

When the menu is triggered the mouse pointer changes to a circle. The screen away from the edge/corner where you clicked is (invisibly) divided clockwise into pie slices, one for each menu-item, that you click in. You can tell which slice you're in by the cursors you specified. There is a small area around where you initially clicked that aborts the menu, in case you decide you don't want to choose an item.

For technical reasons, the area you specify must match the one in the trigger you use for the action. The number of cursors must be at least the number of menu-items. The menu-items and cursors names don't have to match each other.


MENU_ITEMS(menu-items-name) {
  {"label", action},
  {"label", action},
  ...
};

Example:

Used for MENU and GESTURES actions.

I've only tested KEY, COMMAND and NO_ACTION actions in menus. There's no guarantee that other actions will work (or even make sense) when triggered from a menu.


CURSORS(cursors-name) {cursor-name, cursor-name, ...};

Example:

In X the mouse pointer is called the "cursor". The cursors specified here are used when creating a GESTURES action. They indicate which part of the menu you're in.

The possible cursor names are in cursorfont.h of your X distribution. Try running: locate cursorfont.h. On my system it's in /usr/X11R6/include/X11.


NO_ACTION

Do nothing. This is useful if you want spacers in a MENU or GESTURES action.


Compiling

To compile edgy I do:

    gcc -o edgy edgy.cc gfblib.cc -O2 -L/usr/X11R6/lib -lX11 -lXtst -lstdc++

To see what you should put after -L find the directory on your system containing libX11.so and libXtst.so (try running: locate libX11.so).

If all goes well there should be an executable edgy in your current directory, and you can execute it.