Commands

LightWave

Handlers DynaValues Table of Contents

Commands

Commands are the natural counterparts of globals. While globals are used primarily to read information about LightWave's state, commands are used to change its state by loading and saving files, setting parameters, and performing operations. Most commands available to plug-ins are parallels of actions users can take through LightWave's interface.

With one exception described below, commands can only be issued from plug-ins of specific classes: LayoutGeneric and MasterHandler in Layout, and CommandSequence in Modeler. Layout and Modeler have command sets unique to each of them, but they also support a common set of commands that operate on components they share, such as the surface and graph editors and the image display.

Two Methods

There are two ways to issue commands, the "lookup/execute" method and the "evaluate" method. Although both methods are available in both Layout and Modeler, the first is native to Modeler and the second to Layout, and there are minor differences in the way they're implemented in each program.

cmdcode = lookup( data, cmdname )
result = execute( data, cmdcode, argc, argv, [opsel], cmdresult )
The lookup function returns an integer code corresponding to the command name. The command is issued by passing the command code to the execute function. Command codes are constant for a given session, so lookup only needs to be called once per command, after which the codes can be cached and then used in any number of calls to execute. The command's arguments are passed in an array of DynaValues.

Modeler's version of execute takes an additional opsel argument that determines which geometry will be affected by the command. It can be any one of the EltOpSelect codes except OPSEL_MODIFY. Modeler's execute returns 0 (CSERR_NONE) if it succeeds, or an error code if it fails. Layout's execute returns 1 if it succeeds and 0 if it fails.

result = evaluate( data, cmdstring )
The evaluate function uses a single string to issue the command. The command's name and its arguments are delimited by spaces.

The evaluate method usually requires you to write less code, particularly if you wrap it in a function that builds the command string. The following does this for a LayoutGeneric.

   static int lwcommand( LWLayoutGeneric *local, const char *cmdname,
      const char *fmt, ... )
   {
      static char cmd[ 512 ], arg[ 512 ];

      if ( fmt ) {
         va_list ap;
         va_start( ap, fmt );
         vsprintf( arg, fmt, ap );
         va_end( ap );
         sprintf( cmd, "%s %s", cmdname, arg );
         return local->evaluate( local->data, cmd );
      }
      else
         return local->evaluate( local->data, cmdname );
   }

The fmt argument is a printf format string, and the variable number of arguments that follow it correspond to the arguments you'd pass to printf.

In Modeler, however, the lookup/execute method has a couple of advantages. It runs slightly faster, since Modeler doesn't have to perform the lookup or the "unstringizing" of the arguments itself, and it allows you to specify a selection criterion.

Layout Command Global

Layout makes available a global that allows plug-ins of any class, not just generics and masters, to issue commands. Currently, this global isn't a first-class citizen of the plug-in API. It isn't prototyped in the SDK headers, and it doesn't have its own document page. This is primarily because there are lots of ways it could be used unsafely, some of which are difficult to anticipate.

You can use this global by adding something like the following to your code.

   #define LWCOMMANDFUNC_GLOBAL "LW Command Interface"
   typedef int ( *LWCommandFunc )( const char *cmd );

   LWCommandFunc *evaluate;
   evaluate = global( LWCOMMANDFUNC_GLOBAL, GFUSE_TRANSIENT );

Note that if this global, or something like it, is eventually elevated to first-class status, its prototype will likely be different from the above.

A common use for this global is the application of dependent plug-ins. The HyperVoxels volumetric, for example, uses it from within its interface to apply or remove its related custom object plug-in.

But be careful. Test your use of this global thoroughly. Issuing commands at the wrong time or in the wrong context can easily cause a crash. In particular, never issue commands during rendering, and don't remove yourself, or the item you're applied to.