Actions implemented as fungw functions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1. Intro All librnd/application actions are fungw functions. This helps the code to do all the data type conversions easier, save a few conversions where we don't need conversions. More importantly, this makes a more seamless integration between user scripts and C code, whether scripts are calling C actions or the user is executing script defined actions. To stay compatible with the old action mechanism, the current version applies the following restrictions: - we have only one large fungw object (pcb_fgw_obj) that hosts all functions; this emulates the old central hash of functions - before a function enters the object or a function is looked up in the object, the function name is converted to lowercase; this emulates the case intensity of the old action system, which is still essential for the user interface (GUI or CLI command entry and menu). - a single action call will result in a single fungw function call; that is, the fungw function that serves the action is free to convert any of the arguments. 2. converting arguments Arguments are normally converted using RND_ACT_CONVARG(); the last argument is a statement that can make the value assignment to a local variable if the conversion was succesful. For example fetching the 2nd argument into a local coord: { rnd_coord_t c; RND_ACT_CONVARG(2, FGW_COORD, ActioName, c = fgw_coord(&argv[2])); } If conversion fails or the argument is not available, the macro returns from the function with error. Anoter variant of the macro called RND_ACT_MAY_CONVARG() omits the error-return part, allowing the code to have optional arguments. When the coordinate conversion needs to keep extra info, e.g. whether the coordinate was specified in absolute or relative, the special type fgw_coords_t shall be used: { fgw_coords_t *crd; RND_ACT_CONVARG(1, FGW_COORDS, d1, crd = fgw_coords(&argv[1])); } 3. returning An action function has two return values: the C function return value and the "res" argument. If the action could not start executing code because of a calling error, e.g. there are not enough arguments or arguments can not be converted or the function is invoked out-of-context, it should retrun a fungw error using the C return value. This indicates that performing the call itself failed. Else, if actual action code runs, the C return value should be 0 and the res argument should be loaded with the return value of the action. This indicates that the call took place, the action was executed and the conclusion is stored in res. The semantic of the result is action-specifiec. The common convention is returning int, 0 meaning success. The shortnad for this: { RND_ACT_IRES(0); return 0; } RND_ACT_IRES() loads res with type and value. There are other similar, type-specific macros available. 4. multi-call Currently librnd does not depend on fungw's multi-call, thus actions are free to convert their arguments - the same arguments will not be reused in other actions. 5. calling an action from another action There are two common cases: wrapping/dispatching and invoking. In the wrap/dispatch setup an outer action is called that does not alter the arguments (not even converting them!), but has to call another action with exactly the same arguments. This can be done by using the macro RND_ACT_CALL_C(). When an action code needs to invoke another action, there are three ways doing it. 5.1. string call The slow, string parsing way using pcb_actionl() or the more efficient direct call. 5.2. direct C call Direct C function call is possible only if both the caller and callee actions are in core, or are in the same plugin. Before the direct call the caller needs to set up a fungw argv array and after the call the array members need to be free'd using fgw_argv_free() to avoid leaking argument memory. This needs to be done even if the caller did not allocate any memory for argv members - the callee can allocate memory by converting arguments. 5.3. indirect C call Call pcb_find_action() to get an fgw_func_t * to the action - this holds the C function pointer. Set up argv, make sure argv[0] is set up to be the target function: fgw_func_t *fn = rnd_find_action(...); argv[0].type = FGW_FUNC; argv[0].val.func = fn; Call rnd_actionv_() with fn and the arguments. Always call fgw_argv_free() afterward. Also call fgw_arg_free() on the res.