Writing Commands (StumpWM Manual)

### 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.

It is possible to limit the scope under which the command will be usable: a command can be defined to work only a specific group type; the three currently implemented are tile groups,f loating groups, and dynamic groups. 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) ...)

Macro: defcommand name( &rest args)( &rest interactive-args) &body body ¶

  Create a command function and store its interactive hints in *command-hash*. The local variable %interactivep% can be used to check if the command was called interactively. If it is non-NIL then it was called from a keybinding or from the colon command.

  The NAME argument can be a string, or a list of two symbols. If the latter, the first symbol names the command, and the second indicates the type of group under which this command will be usable. Currently, tile-group, floating-group and dynamic-group are the possible values.

  INTERACTIVE-ARGS is a list of the following form: ((TYPE PROMPT) (TYPE PROMPT) ...)

  each element in INTERACTIVE-ARGS declares the type and prompt for the command’s arguments.

  TYPE can be one of the following:

  :y-or-n

    A yes or no question returning T or NIL.

  :variable

    A lisp variable

  :function

    A lisp function

  :command

    A stumpwm command as a string.

  :key-seq

    A key sequence starting from *TOP-MAP*

  :window-number

    An existing window number

  :number

    An integer number

  :string

    A string

  :key

    A single key chord

  :window-name

    An existing window’s name

  :direction

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

  :gravity

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

  :group

    An existing group

  :frame

    A frame

  :shell

    A shell command

  :rest

    The rest of the input yet to be parsed.

  :module

    An existing stumpwm module

  :rotation

    A rotation symbol. One of :CL, :CLOCKWISE, :CCL, OR :COUNTERCLOCKWISE

  Note that new argument types can be created with DEFINE-STUMPWM-TYPE.

  PROMPT can be string. In this case, if the corresponding argument is missing from an interactive call, stumpwm will use prompt for its value using PROMPT. If PROMPT is missing or nil, then the argument is considered an optional interactive argument and is not prompted for when missing.

  Alternatively, instead of specifying nil for PROMPT or leaving it out, an element can just be the argument type.

Macro: defcommand-alias alias original ¶

  Since interactive commands are functions and can conflict with package symbols. But for backwards compatibility this macro creates an alias name for the command that is only accessible interactively.

Variable: *last-command* ¶

  Set to the last interactive command run.

### 3.2 Interactive Keymaps ¶

Interactive keymaps are a special type of command that basically pushes another keymap on top of the current one. The new keymap will only be removed after an exit command is run. An example is `iresize`.

The macro `define-interactive-keymap` is used to define an interactive keymap. The first argument is the same as `defcommand`. The second argument is a list of extra configurations that can be used for controlling the command and the rest are the key bindings for the new command, optionally with a `t` appended; this tells `define-interactive-keymap` to exit the keymap upon use of that keybinding.

For instance, a simple interactive keymap:

    (define-interactive-keymap my-new-command nil
      ((kbd "a") "execute-a-command")
      ((kbd "b") "execute-b-command" t))

This creates a command called `my-new-command` that, when called, will activate the interactive keymap mode. In this mode, the user can press “a” or “b” repeatedly, omitting any prefix. The default exit commands are `RET`, `C-g` and `ESC`.

This creates a command called `my-new-command` that, when called, will activate the interactive keymap mode. In this mode, the user can press “a” or “b”, omitting any prefix. The user can press “a” repeatedly, however pressing “b” exits the keymap. The default exit commands are `RET`, `C-g` and `ESC`.

The available configuration is `on-enter`, `on-exit` and `abort-if`:

    (defun before-foo () (message "start foo"))
    (defun after-foo () (message "end foo"))
    (defun foo-p () (and *bar* *baz*))
    (defparameter *custom-exit-keys* '((kbd "RET") (kbd "SPC")
                                       (kbd "C-g") (kbd "ESC")))

    (define-interactive-keymap foo (:on-enter #'before-foo
                                    :on-exit #'after-foo
                                    :abort-if #'foo-p
                                    :exit-on *custom-exit-keys*))

In the above example, the message “start foo” will appear before starting the interactive keymap, “end foo” will appear right after the command exits; We’ve added SPC as an exit key with custom exit keys. Also, the command executes only if the variables `*bar*` and `*baz*` are true.

Macro: define-interactive-keymap name( &key on-enter on-exit abort-if exit-on) &body key-bindings ¶

  Default Values:

        on-enter  nil
        on-exit   nil
        abort-if  nil
        exit-on   '((stumpwm:kbd "RET") (stumpwm:kbd "ESC")
                    (stumpwm:kbd "C-g"))

  Declare an interactive keymap mode. This can be used for developing interactive modes or command trees, such as `iresize`.

  The NAME argument follows the same convention as in `defcommand`.

  ON-ENTER and ON-EXIT are optional functions to run before and after the interactive keymap mode, respectively. If ABORT-IF is defined, the interactive keymap will only be activated if calling ABORT-IF returns true.

  KEY-BINDINGS is a list of the following form: ((KEY COMMAND) (KEY COMMAND) ...) If one appends t to the end of a binding like so: ((kbd "n") "cmd" t) then the keymap is immediately exited after running the command.

  Each element in KEY-BINDINGS declares a command inside the interactive keymap. Be aware that these commands won’t require a prefix to run.

Command: call-and-exit-kmap command exit-command ¶

  This command effectively calls two other commands in succession, via run-commands. it is designed for use in the define-interactive-keymap macro, to implement exiting the keymap on keypress.