[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

3. Commands

If you’ve used Emacs before you’ll find the distinction between commands and functions familiar. Commands are simply functions that can be bound to keys and executed interactively from StumpWM’s input bar. Whereas, in Emacs, the special "(interactive)" declaration is used to turn a function into a command, in StumpWM commands are made with a separate defcommand macro.

Once a command is defined, you can call it by invoking the colon command (C-t ;), and typing the name of the command. This may be sufficient for commands that aren’t used very often. To see all the currently-defined commands, invoke the command called commands: ie press C-t ;, type “commands”, and hit return.

Commonly-used commands can also be bound to a keystroke, which is much more convenient. To do this, use the define-key function (see Key Bindings), giving the name of the command as a string. For example:

(define-key *root-map* (kbd "d") "exchange-direction")

You cannot give the command name as a symbol, nor can you bind a key to a regular function defined with defun.

If the command takes arguments (see Writing Commands), you can fix those arguments when defining the key-binding, by including the arguments in the same string as the command name, separated by a space. For instance, the exchange-direction command, which is unbound by default, requires a direction in which to exchange windows. If you call exchange-direction directly, it will prompt you for the direction. If you know that you often exchange in left/right directions, and want those actions bound to keys, you can use the following in your customization file:

(define-key *root-map* (kbd "[") "exchange-direction left")
(define-key *root-map* (kbd "]") "exchange-direction right")

Multiple arguments can be included by adding them to the command string, separated by spaces. Not all argument types can be represented as strings, but StumpWM will do its best to convert types.

StumpWM does not implement the Emacs concept of prefix arguments.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

3.1 Writing Commands

StumpWM commands are written much like any Lisp function. The main difference is in the way command arguments are specified. The defcommand macro takes a list of arguments as its first form (similar to the defun macro), and a corresponding list of types as its second form. All arguments must belong to a “type”. Each type specification has two parts: a keyword specifying the argument type, and a string prompt that will be displayed when asking the user to enter the argument value. A typical defcommand might look like this:

(defcommand now-we-are-six (name age)
    ((:string "Enter your name: ")
     (:number "Enter your age: "))
  (message "~a, in six years you will be ~a" name (+ 6 age)))

If now-we-are-six is called interactively via the colon command, the user will be prompted for a string and a number, which will then be bound to “name” and “age”, respectively, in the body of the command.

When invoking the command via a key-binding, it is possible to provide some or all of the arguments directly:

(define-key *root-map* (kbd "L") "now-we-are-six John")

In this case, hitting C-t L will only prompt for an age (the first string argument is already bound to “John”). Argument values provided this way always bind to the earliest arguments defined: ie, it is not possible to specify an age, but prompt the user for a name.

If the type declaration does not include a prompt (ie, it looks like “(:type nil)”, or “(:type)” or just “:type”), the argument is considered optional. It can be provided via a key-binding invocation, as above, but if it isn’t, the user will not be prompted, and the argument will be bound to nil.

Lastly, it is possible to limit the scope under which the command will be usable: a command can be defined to work only in tile groups, or only in floating groups (the only two types of groups that currently exist). This is done by replacing the name of the command with a two-element list: the name of the command as a symbol, and either the symbol tile-group or floating-group. For instance, the next command, which only functions in tile groups, is defined this way:

(defcommand (next tile-group) …)

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

3.2 StumpWM Types

All command arguments must be of a defined “StumpWM type”. The following types are pre-defined:


A yes or no question returning T or NIL.


A lisp variable


A lisp function


A stumpwm command as a string.


A key sequence starting from *TOP-MAP*


An existing window number


An integer number


A string


A single key chord


An existing window’s name


A direction symbol. One of :UP :DOWN :LEFT :RIGHT


A gravity symbol. One of :center :top :right :bottom :left :top-right :top-left :bottom-right :bottom-left


An existing group


A frame


A shell command


The rest of the input yes to be parsed.


An existing stumpwm module

Additional types can be defined using the macro define-stumpwm-type. Emacs users who are accustomed to writing more complicated interactive declarations using "(interactive (list …))" forms will find that similar logic can be put into StumpWM type definitions. The macro is called like this:

(define-stumpwm-type :type-name (input prompt) body)

The keyword :type-name will then be available for use in defcommand macros. When commands are called, the bodies of these type definitions are called in turn to produce actual argument values.

Type definitions produce their value in one of several ways: by reading it from the argument line bound to a keystroke, by prompting the user to enter a value, or by generating it programmatically.

Within the body of the type definition, the argument “input” is bound to the argument line provided in the command string, and “prompt” to the string prompt provided in the defcommand form. The usual convention is to first check if an argument has been provided in “input” and, if it hasn’t, to prompt for it using “prompt”.

StumpWM provides several convenience functions for handling the value of “input”:

As an example, here’s a new type called :smart-direction. The existing :direction type simply asks for one of the four directions “left”, “right”, “up” or “down”, without checking to see if there’s a frame in that direction. Our new type, :smart-direction, will look around the current frame, and only allow the user to choose a direction in which another frame lies. If only one direction is possible it will return that automatically without troubling the user. It signals an error for invalid directions; it could alternately return a “nil” value in those cases, and let the command handle that.

(define-stumpwm-type :smart-direction (input prompt)
  (let ((valid-dirs
         (loop  ; gather all the directions in which there's a neighbouring frame
            with values = '(("up" :up)
                            ("down" :down)
                            ("left" :left)
                            ("right" :right))
            with frame-set =
              (group-frames (window-group (current-window)))
            for dir in values
            for neighbour = (neighbour
                             (second dir)
                             (window-frame (current-window)) frame-set)
            if (and neighbour (frame-window neighbour))
            collect dir))
        (arg (argument-pop input)))  ; store a possible argument
    (cond ((null valid-dirs)  ; no directions, bail out
           (throw 'error "No valid directions"))
          (arg  ; an arg was bound, but is it valid?
           (or (second (assoc arg valid-dirs :test #'string=))
               (throw 'error "Not a valid direction")))
          ((= 1 (length valid-dirs))  ; only one valid direction
           (second (car valid-dirs)))
          (t  ; multiple possibilities, prompt for direction
           (second (assoc (completing-read input prompt valid-dirs
                                           :require-match t)
                          valid-dirs :test #'string=))))))

(defcommand smarty (dir) ((:smart-direction "Pick a direction: "))
  ;; `dir' is a keyword here
  (message "You're going ~a" (string-downcase dir)))

(define-key *root-map* (kbd "R") "smarty right")

[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by David Bjergaard on January 12, 2017 using texi2html 1.82.