= f.functions This document will describe how functions get written and dispatched. It should be a useful reference when you intend to add a new function, or understand how the code works. == Document Conventions Unless otherwise stated, most mentions of "`f.function`" (in singular or plural) are meant to be generic references to any `f.anything` that ctwm implements, rather than specifically the `f.function` function. This is done because just calling them "`functions`" can be ambiguous, especially when talking about the implementation, because the implementation of a ctwm _function_ is done in terms of a C _function_, so there's often opportunity for terminological confusion. == Functional Considerations There are a few choices in the way functions work to consider in any given case. [[func-arguments,Arguments]] === Arguments Some functions take an argument, while others don't. For example, the case of <> as described below takes an argument, so you'd have something like `f.gotoworkspace "one"` in a key binding or menu. Contrarily, <> doesn't, so you'd merely have `f.identify` in the config. This is controlled by a column in the `functions_defs.list` file; see below where the <> are discussed. [[cons-deferral,Deferral]] === Deferral There is also a concept of _function deferral_. This happens in the case of f.functions that in some way target a window (`f.move` and friends, `f.resize`, `f.occupy`, and a great many others). When you activate them from a mouse/key binding or titlebar icon or the like, ctwm can see which window you're pointing at, and targets it from there. However, when run from a menu, you can't be pointing at a window; you're pointing at the menu. As a result, ctwm _defers_ the execution of the f.function. It changes the mouse cursor to something to prod the user, and waits for you to click on a window. _Then_ it runs back into the function execution to actually to the work. So any f.function that has to do something related to a window has to be setup to defer, or it won't work from a menu. This is also controlled in `functions_defs.list`; x-ref the description of the <>. The right cursor for any given case is a matter of judgement, but generally move/resize actions have one cursor (the `DC_MOVE` choice), and other functions use the other (`DC_SELECT`). === Magic and Internal There are a few "`synthetic`" or "`internal`" f.functions, which exist only to link up some magic like the `TwmWindows` auto-generated menu. Unless you're working with magic menus, you never need to go near or know anything about them. There are also two somewhat magical f.functions. One is `f.function` which runs a user-defined function, which is a sequence of other existing functions. This is commonly used in conjunction with the other magical function, `f.deltastop`, to let you do stuff to a window that varies depending on whether you move the mouse or not. See the user manual for details of them. They get executed slightly differently than other functions; see the <> section below for details. == Implementation Overview Much over the overall control for dispatching and finding f.functions is done via generated code, from the definitions in `functions_def.list`. f.function execution begins by calling into the `ExecuteFunction()` function from various places (usually event handlers for menu selections or mouse/key bindings, but there are a few other ways). There it uses various of the autogenerated bits to look up what sort of deferral or other magic it might do, and then falls down into individual C functions for implementing each ctwm f.function. === `functions_defs.list` and autogenerated controls. As part of the build process, `tools/mk_function_bits.sh` builds various generated header files (_i.e._, `build/functions_*.h`) from the `functions_defs.list` file. Comments in that file give a good reference to the details of the syntax. We'll skim the higher-level overview here. [[impl-functions-defs-sections,functions_defs.list sections]] ==== Sections There are 3 sections in the file, delineated by comments like `#START(section)` and `#END(section)`; these are used as markers by the `mk_function_bits.sh` script to find the bits it needs at any given time. The `aliases` and `synthetic` section are almost certainly not anything you need to touch. `aliases` are alternate names for f.functions. Those that exist are historical, and we should probably avoid adding any new ones; just name a function what it should be named, and don't add confusion by having multiple names. `synthetic` are f.functions not exposed to the user (_i.e._, not available in config files) but get called from things like the magic `TwmWindow` menu. Both are very special cases, so unless you're doing something very unusual, you'll never go near them. The `main` section is where you'll be playing. It contains space delimited columns (mostly visually lined up in the file for convenience; the script only cares about whitespace). First is the name; obvious. Second determines whether it's a f.function that takes an argument (like <> below) or one that doesn't. The third column defines the deferral cursor; this has the side effect of determining whether it's a deferred f.function or not; see discussion of <> above. And the fourth allows hiding info about the function behind an #ifdef. The only current use of that is for the rplay-based sound support, and it should probably be avoided for new functions. Generally, the function should be available all the time, and just do nothing (or beep, or something appropriate) when the conditional code isn't available. This saves users from some complication in writing their config files. ==== Generated Files From that, `mk_function_bits.sh` generates header files that contain the various info about the f.functions. * One file contains the ``#define``'s for all the `F_WHATEVER` contants used in the code to refer to the f.functions internally. This only really needs the names. * It also generates the `funckeytable` lookup table the config file parser (in `parse_keyword()`) uses to look up the functions referred to in the config table. This needs the second column to distinguish functions taking argument from those that don't. It also uses bits from the `aliases` section, since we need to parse those names when give (and treat them the same as the real f.function names). * It generates the `fdef_table` lookup table which is used in the f.function execution (in `EF_main()`) to determine whether to defer calling the function, and what X cursor to set when it defers. This uses the third column (and only includes f.functions that have something there). See earlier discussion of <>. * And finally, it generates the `func_dispatch` table used in `EF_main()` to dispatch the actual execution of the f.function to the underlying C function that implements it. This is just built off the names. [[impl-dispatch,Function Dispatching]] === Dispatching and Executing Some mechanism (usually invocation from menu or button/key binding) calls some f.function. This calls into `ExecuteFunction()` to do the dispatching, which is just an external thunk into `EF_main()`. This checks the environment and the `fdef_table` we generated to determine whether the function should be deferred; if so, it sets the deferral cursor and returns. Actual execution then happens via another fresh call into `ExecuteFunction()` via slightly creepy magic in the `ButtonPress` event handling code. You don't want to know. Then it falls into actually dispatching the f.function. There are two special cases described below. Most f.functions simply run through to an individual C function that implements them, via the `func_dispatch` table and specific naming; the implemetation of the ctwm function `f.abcdef` will be in the C function `f_abcdef_impl()`. The two special cases revolve around the `f.function` construction which allows user creation of ctwm functions that alias or chain multiple other f.functions (x-ref `Function` keyword in the user name). The first is `f.function` itself, which loops over the list of things the user told it to do and recurses back into `EF_main()` for them. The second is the magic `f.deltastop` (which is only meaningful as part of a ``f.function``'s chain), which checks its magic and returns a value from `EF_main()` to tell the calling `f.function` invocation to stop where it is instead of proceeding. _This is the only use of ``EF_main()``'s return value_. == Implementating A Function Most of the work of implementing a new f.function should be whatever code you actually need to write to _do_ what the function is supposed to do. We want to minimize the boilerplate you need to do to hook it up. Generally, you only need to do two things: . Add it to the `main` section of the `functions_defs.list` file, with whatever options are appropriate. The build system will notice the change and add it to the generated files next time you build. Then it's ready to be parsed from a config file and executed at runtime. Note that this will cause a compile failure until you also . Create the implementation in the appropriately named C function. The `DFHANDLER()` macro exists to set the right name and argument list; use it instead of trying to do it manually. Even an empty function will be enough to satify the compiler and get you running. === Internal Macros And Details The `functions_internal.h` file contains a few macros used in defining and calling f.function implementations, the prototypes for all those implementations, and a few other bits that get shared among the `function_*.c` implementation files. `EF_FULLPROTO` gives the full list of arguments that `ExecuteFunction()` and all the f.function handlers takes. It's also used in some backend functions the handlers call. Commonly these are cases where several functions act almost identically, and so just thunk through to a shared backend function; _e.g._, how all of `f.move`, `f.forcemove`, `f.movepack`, and `f.movepush` merely call `movewindow()` in `functions_win_moveresize.c`. The `EF_ARGS` macro is the same set of arguments, just in the form of the names as you'd use in calling the function; you can see its usage in those same cases. The `DFHANDLER()` macro is used in **D**efining a **F**unction **HANDLER**. It's used in both the prototypes in `functions_internal.h` and in all the implementations in the `functions_*.c` files. By just calling it with the function name, we can automate away making sure the implementation is named correctly so the generated `func_dispatch` table can find them in the dispatch (x-ref <>), and that it takes the right args. Along with the mentioned `EF_*` macros, that will save us a lot of trouble visiting hundreds of places if/when we change the set of args we pass around function execution and handlers. == Implementation Examples [[example-identify,f.identify]] === `f.identify` and `f.version` `f.version` pops up a window with info about the ctwm build and version. `f.identify` pops up a window with information about a given window, which has also all that `f.version` information up top. So they can be considered variants of the same thing. And in fact, they both wind up implemented by the same code on the backend. So, to trace from the top, we find the `version` and `identify` lines in the `main` section of `functions_defs.list`. The `version` line has nothing in the other 3 fields; it takes no argument, and since it doesn't target a window it doesn't need any deferral. `identify` also takes no argument, but _does_ target a window, so it needs to be deferred; the `CS` entry means we're using the "`select`" style cursor. From that file, the various lookup arrays for deferring and dispatching get autogenerated. The implementations are in `functions_identify.c`. As with all functions, the `DFHANDLER()` macro is used to name the function and arguments. Each of those implementations just calls the `Identify()` backend function for the implementation; `f.identify` passes the targetted window (the `tmp_win` argument to the handler), while `f.version` passes `NULL`. `Identify()` then builds the window with the ctwm version/build info, and then the window info if it were given one. [[example-gotoworkspace,f.gotoworkspace]] === `f.gotoworkspace` `f.gotoworkspace` warps you to a named workspace, so it takes an argument. See discussion in <> above. So we see in its line in `functions_defs.list` that it has an `S` in the first field, indicating it's taking a string argument (the only choice other than the stand-in `-` for functions not taking args). The implementation in `functions_workspaces.c` is then a fairly thin wrapper around the existing `GotoWorkSpaceByName()` function used elsewhere. The `action` argument to the handler contains the value of the argument given in the config file, which in the case is a string of the name of the workspace, and `GotoWorkSpaceByName()` does its thing.