1= f.functions 2 3This document will describe how functions get written and dispatched. It 4should be a useful reference when you intend to add a new function, or 5understand how the code works. 6 7 8== Document Conventions 9 10Unless otherwise stated, most mentions of "`f.function`" (in singular or 11plural) are meant to be generic references to any `f.anything` that ctwm 12implements, rather than specifically the `f.function` function. This is 13done because just calling them "`functions`" can be ambiguous, especially 14when talking about the implementation, because the implementation of a 15ctwm _function_ is done in terms of a C _function_, so there's often 16opportunity for terminological confusion. 17 18 19== Functional Considerations 20 21There are a few choices in the way functions work to consider in any 22given case. 23 24[[func-arguments,Arguments]] 25=== Arguments 26 27Some functions take an argument, while others don't. For example, the 28case of <<example-gotoworkspace>> as described below takes an argument, 29so you'd have something like `f.gotoworkspace "one"` in a key binding or 30menu. Contrarily, <<example-identify>> doesn't, so you'd merely have 31`f.identify` in the config. 32 33This is controlled by a column in the `functions_defs.list` file; see 34below where the <<impl-functions-defs-sections>> are discussed. 35 36[[cons-deferral,Deferral]] 37=== Deferral 38 39There is also a concept of _function deferral_. This happens in the case 40of f.functions that in some way target a window (`f.move` and friends, 41`f.resize`, `f.occupy`, and a great many others). When you activate them 42from a mouse/key binding or titlebar icon or the like, ctwm can see which 43window you're pointing at, and targets it from there. However, when run 44from a menu, you can't be pointing at a window; you're pointing at the 45menu. 46 47As a result, ctwm _defers_ the execution of the f.function. It changes 48the mouse cursor to something to prod the user, and waits for you to 49click on a window. _Then_ it runs back into the function execution to 50actually to the work. 51 52So any f.function that has to do something related to a window has to be 53setup to defer, or it won't work from a menu. This is also controlled in 54`functions_defs.list`; x-ref the description of the 55<<impl-functions-defs-sections>>. The right cursor for any given case is 56a matter of judgement, but generally move/resize actions have one cursor 57(the `DC_MOVE` choice), and other functions use the other (`DC_SELECT`). 58 59=== Magic and Internal 60 61There are a few "`synthetic`" or "`internal`" f.functions, which exist 62only to link up some magic like the `TwmWindows` auto-generated menu. 63Unless you're working with magic menus, you never need to go near or know 64anything about them. 65 66There are also two somewhat magical f.functions. One is `f.function` 67which runs a user-defined function, which is a sequence of other existing 68functions. This is commonly used in conjunction with the other magical 69function, `f.deltastop`, to let you do stuff to a window that varies 70depending on whether you move the mouse or not. See the user manual for 71details of them. They get executed slightly differently than other 72functions; see the <<impl-dispatch>> section below for details. 73 74 75== Implementation Overview 76 77Much over the overall control for dispatching and finding f.functions is 78done via generated code, from the definitions in `functions_def.list`. 79f.function execution begins by calling into the `ExecuteFunction()` 80function from various places (usually event handlers for menu selections 81or mouse/key bindings, but there are a few other ways). There it uses 82various of the autogenerated bits to look up what sort of deferral or 83other magic it might do, and then falls down into individual C functions 84for implementing each ctwm f.function. 85 86=== `functions_defs.list` and autogenerated controls. 87 88As part of the build process, `tools/mk_function_bits.sh` builds various 89generated header files (_i.e._, `build/functions_*.h`) from the 90`functions_defs.list` file. Comments in that file give a good reference 91to the details of the syntax. We'll skim the higher-level overview here. 92 93[[impl-functions-defs-sections,functions_defs.list sections]] 94==== Sections 95 96There are 3 sections in the file, delineated by comments like 97`#START(section)` and `#END(section)`; these are used as markers by the 98`mk_function_bits.sh` script to find the bits it needs at any given time. 99 100The `aliases` and `synthetic` section are almost certainly not anything 101you need to touch. `aliases` are alternate names for f.functions. Those 102that exist are historical, and we should probably avoid adding any new 103ones; just name a function what it should be named, and don't add 104confusion by having multiple names. `synthetic` are f.functions not 105exposed to the user (_i.e._, not available in config files) but get 106called from things like the magic `TwmWindow` menu. Both are very 107special cases, so unless you're doing something very unusual, you'll 108never go near them. 109 110The `main` section is where you'll be playing. It contains space 111delimited columns (mostly visually lined up in the file for convenience; 112the script only cares about whitespace). First is the name; obvious. 113Second determines whether it's a f.function that takes an argument (like 114<<example-gotoworkspace>> below) or one that doesn't. 115 116The third column defines the deferral cursor; this has the side effect of 117determining whether it's a deferred f.function or not; see discussion of 118<<cons-deferral>> above. And the fourth allows hiding info about the 119function behind an #ifdef. The only current use of that is for the 120rplay-based sound support, and it should probably be avoided for new 121functions. Generally, the function should be available all the time, and 122just do nothing (or beep, or something appropriate) when the conditional 123code isn't available. This saves users from some complication in writing 124their config files. 125 126==== Generated Files 127 128From that, `mk_function_bits.sh` generates header files that contain the 129various info about the f.functions. 130 131* One file contains the ``#define``'s for all the `F_WHATEVER` contants 132used in the code to refer to the f.functions internally. This only 133really needs the names. 134 135* It also generates the `funckeytable` lookup table the config file 136parser (in `parse_keyword()`) uses to look up the functions referred to 137in the config table. This needs the second column to distinguish 138functions taking argument from those that don't. It also uses bits from 139the `aliases` section, since we need to parse those names when give (and 140treat them the same as the real f.function names). 141 142* It generates the `fdef_table` lookup table which is used in the 143f.function execution (in `EF_main()`) to determine whether to defer 144calling the function, and what X cursor to set when it defers. This uses 145the third column (and only includes f.functions that have something 146there). See earlier discussion of <<cons-deferral>>. 147 148* And finally, it generates the `func_dispatch` table used in `EF_main()` 149to dispatch the actual execution of the f.function to the underlying C 150function that implements it. This is just built off the names. 151 152[[impl-dispatch,Function Dispatching]] 153=== Dispatching and Executing 154 155Some mechanism (usually invocation from menu or button/key binding) calls 156some f.function. This calls into `ExecuteFunction()` to do the 157dispatching, which is just an external thunk into `EF_main()`. This 158checks the environment and the `fdef_table` we generated to determine 159whether the function should be deferred; if so, it sets the deferral 160cursor and returns. Actual execution then happens via another fresh call 161into `ExecuteFunction()` via slightly creepy magic in the `ButtonPress` 162event handling code. You don't want to know. 163 164Then it falls into actually dispatching the f.function. There are two 165special cases described below. Most f.functions simply run through to an 166individual C function that implements them, via the `func_dispatch` table 167and specific naming; the implemetation of the ctwm function `f.abcdef` 168will be in the C function `f_abcdef_impl()`. 169 170The two special cases revolve around the `f.function` construction which 171allows user creation of ctwm functions that alias or chain multiple other 172f.functions (x-ref `Function` keyword in the user name). The first is 173`f.function` itself, which loops over the list of things the user told it 174to do and recurses back into `EF_main()` for them. The second is the 175magic `f.deltastop` (which is only meaningful as part of a 176``f.function``'s chain), which checks its magic and returns a value from 177`EF_main()` to tell the calling `f.function` invocation to stop where it 178is instead of proceeding. _This is the only use of ``EF_main()``'s 179return value_. 180 181 182== Implementating A Function 183 184Most of the work of implementing a new f.function should be whatever code 185you actually need to write to _do_ what the function is supposed to do. 186We want to minimize the boilerplate you need to do to hook it up. 187 188Generally, you only need to do two things: 189 190. Add it to the `main` section of the `functions_defs.list` file, with 191whatever options are appropriate. The build system will notice the 192change and add it to the generated files next time you build. Then it's 193ready to be parsed from a config file and executed at runtime. Note that 194this will cause a compile failure until you also 195 196. Create the implementation in the appropriately named C function. The 197`DFHANDLER()` macro exists to set the right name and argument list; use 198it instead of trying to do it manually. Even an empty function will be 199enough to satify the compiler and get you running. 200 201=== Internal Macros And Details 202 203The `functions_internal.h` file contains a few macros used in defining 204and calling f.function implementations, the prototypes for all those 205implementations, and a few other bits that get shared among the 206`function_*.c` implementation files. 207 208`EF_FULLPROTO` gives the full list of arguments that `ExecuteFunction()` 209and all the f.function handlers takes. It's also used in some backend 210functions the handlers call. Commonly these are cases where several 211functions act almost identically, and so just thunk through to a shared 212backend function; _e.g._, how all of `f.move`, `f.forcemove`, 213`f.movepack`, and `f.movepush` merely call `movewindow()` in 214`functions_win_moveresize.c`. The `EF_ARGS` macro is the same set of 215arguments, just in the form of the names as you'd use in calling the 216function; you can see its usage in those same cases. 217 218The `DFHANDLER()` macro is used in **D**efining a **F**unction 219**HANDLER**. It's used in both the prototypes in `functions_internal.h` 220and in all the implementations in the `functions_*.c` files. By just 221calling it with the function name, we can automate away making sure the 222implementation is named correctly so the generated `func_dispatch` table 223can find them in the dispatch (x-ref <<impl-dispatch>>), and that it 224takes the right args. Along with the mentioned `EF_*` macros, that will 225save us a lot of trouble visiting hundreds of places if/when we change 226the set of args we pass around function execution and handlers. 227 228 229== Implementation Examples 230 231[[example-identify,f.identify]] 232=== `f.identify` and `f.version` 233 234`f.version` pops up a window with info about the ctwm build and version. 235`f.identify` pops up a window with information about a given window, 236which has also all that `f.version` information up top. So they can be 237considered variants of the same thing. And in fact, they both wind up 238implemented by the same code on the backend. 239 240So, to trace from the top, we find the `version` and `identify` lines in 241the `main` section of `functions_defs.list`. The `version` line has 242nothing in the other 3 fields; it takes no argument, and since it doesn't 243target a window it doesn't need any deferral. `identify` also takes no 244argument, but _does_ target a window, so it needs to be deferred; the 245`CS` entry means we're using the "`select`" style cursor. From that 246file, the various lookup arrays for deferring and dispatching get 247autogenerated. 248 249The implementations are in `functions_identify.c`. As with all 250functions, the `DFHANDLER()` macro is used to name the function and 251arguments. Each of those implementations just calls the `Identify()` 252backend function for the implementation; `f.identify` passes the 253targetted window (the `tmp_win` argument to the handler), while 254`f.version` passes `NULL`. `Identify()` then builds the window with the 255ctwm version/build info, and then the window info if it were given one. 256 257[[example-gotoworkspace,f.gotoworkspace]] 258=== `f.gotoworkspace` 259 260`f.gotoworkspace` warps you to a named workspace, so it takes an 261argument. See discussion in <<func-arguments>> above. So we see in its 262line in `functions_defs.list` that it has an `S` in the first field, 263indicating it's taking a string argument (the only choice other than the 264stand-in `-` for functions not taking args). 265 266The implementation in `functions_workspaces.c` is then a fairly thin 267wrapper around the existing `GotoWorkSpaceByName()` function used 268elsewhere. The `action` argument to the handler contains the value of 269the argument given in the config file, which in the case is a string of 270the name of the workspace, and `GotoWorkSpaceByName()` does its thing. 271