DOCUMENTATION FOR TCL4MIRC (FOR INSTALLATION SEE README.TXT) ======================================================================= Last update on October 20th 2007 for version 1.0 0. FAQ > What is "supported mode"? 1. RUNNING TCL > Invoking the interpreter > Notes on $dllcall and asynchronous calls > Embedding Tcl into scripts > Caching embedded code 2. COMMUNICATING WITH MIRC > mirc package > mirc function > Passing variables from mIRC 3. MIRC SIGNAL CALLBACKS AND OUTPUT HANDLING > TCL_LOADED > TCL_ERROR > TCL_STDOUT_BEGIN / TCL_STDERR_BEGIN > TCL_STDOUT / TCL_STDERR > TCL_STDOUT_END / TCL_STDERR_END 4. USING TK ======================================================================= 0. FAQ > What is "supported mode"? Tcl4mIRC relies on the CLB (Common Language Bridge) which is part of the Dynamic Languages 4 mIRC project. The CLB extends the way a DLL can communicate with mIRC, enabling it specifically to be able to access event specific identifiers, like $opnick in the ON OP event. In addition to extended functionality, this mode is roughly 3 times faster than using the standard SendMessage API documented in the mIRC help (/help SendMessage). However, a drawback of this mode is that it requires specific knowledge about the makeup of the mIRC binary that is being run. In other words, to make use of the CLB's extended functionality, the version of mIRC used with the DLL must be explicitly supported by the CLB, hence "supported mode". CLB is able to fallback on the standard SendMessage API when the version of mIRC is not supported, but beware that you may be using a script that requires the extended functionality that the CLB provides. If you are using Tcl4mIRC in unsupported mode, verify that you do not have any embedded scripts inside events that make use of event specific identifiers. In such a case, your events will not function properly. 1. RUNNING TCL > Invoking the interpreter The first way to run Tcl code is to simply invoke the evaluation method by simply calling: /tcl CODE_HERE * Note that /tcl is a convenience alias specified in tcl.mrc You can also use $tcl(CODE_HERE) to retrieve the return value from a block of Tcl code. > Notes on $dllcall and asynchronous calls Short answer: don't use it. Long answer: Tcl has an event driven interpreter model. Threading is implicit, not explicit. Therefore, making asynchronous calls in Tcl is much like making asynchronous calls in mIRC: you use a timer which will inevitably be processed by the Tcl event loop. This means that you cannot and should not use `$dllcall` to perform asynchronous calls, because Tcl does not work properly across threads. Doing so will likely crash mIRC. > Embedding Tcl into scripts Most people don't just want Tcl sitting on the sidelines while mIRC does its thing. For this reason, you have the ability to embed your Ruby code directly into your mIRC scripts seamlessly. To do this, you must "memorize" the following snippet of code to have Tcl4mIRC jump from mIRC code into your Tcl script: if $($has_tcl,2) { # Tcl code goes here } If you're curious to know what it does, I can explain. $has_tcl will return a call to the DLL to invoke the interpreter and parse the code starting at the beginning of the if extending to the end. The command must be $() evaluated to get $scriptline so that it knows where to begin parsing from. The identifier then returns $false so that mIRC ignores the if-block, happily skipping over the otherwise- invalid script. > Caching embedded code To speed up the execution of each embedded call, you can cache your code in memory to avoid recompiling the Tcl code each round. To do this, simply use $has_cached_tcl (or $has_cached_async_tcl) instead. IMPORTANT: Note that the Tcl caching model is such that once you cache a block of code it can not be overidden by calling the non- cached function anymore. To clear the cache you will have to unload the DLL completely with `/tcl_unload`. Caching is not "on" by default because most people will most likely have to make changes to their code before finalizing it, and having Tcl4mIRC cache all the time would cause a lot of headaches in debugging. For this reason, the recommended flow of coding is to use $has_tcl until you are positive that you have a working product, and only then change the starting line to $has_cached_tcl. It may also be beneficial to add a note to yourself that you are caching the script results. 2. COMMUNICATING WITH MIRC > mirc package Tcl4mIRC registers a package called 'mirc' for to get information about the DLL version, etc. It can be accessed via the standard Tcl package info command: /tcl puts [package present mirc] > mirc function The `mirc` function is the main command to use to have Tcl communicate with mIRC. The function takes a command, variable, or identifier as an argument and evaluates the expression, returning a result (if it is not a command). For example: mirc "echo -a hello world" Notice that the '/' prefix is not necessary, though it is allowed. * Note: The mirc command will decide whether to evaluate code as an identifier or variable depending on the first character in the command string. If the first character is '%' or '$', the string will be evaluated, otherwise it will be executed. This means that the command "%x = 2" will not attempt to set the variable %x to 2. Please use "set %x 2" to do that instead. Subsequent versions of Tcl4mIRC will address this issue, perhaps by splitting evaluation and commands into two separate functions. To evaluate an identifier use (in Tcl we always escape '$' when we are not accessing a Tcl variable): puts [mirc \$calc(1 + 2)] Or: puts [mirc %a_variable] * Note: Remember to escape '$' and '%' characters if you call the above code using /tcl or $tcl since you will be subject to parsing by mIRC before your data reaches Tcl4mIRC. In embedded code there is no need to escape anything for mIRC's sake. If you run Tcl in "supported mode" (See "supported mode" in the FAQ for details) then you can take advantage of this command to evaluate event specific identifiers, for example, $opnick in an ON OP event. > Passing variables from mIRC It is likely that you will need to pass data to and from Tcl when creating a script. While the CLB does support event specific identifiers to reduce the number of data needed to be manually passed in and out of mIRC, the CLB does not support local variables or the $1- identifier family, and therefore cannot communicate completely transparently with mIRC. To solve this problem, you must unfortunately set the variables that Tcl4mIRC needs to read globally, unsetting them at the end of your alias or event. /set -u0 will not work to avoid the unset command at the end. A way to access local variables via the CLB is being worked on. An example of communicating between mIRC and Tcl would be: alias tcl_strlen { set %data $1- tcl puts [string length [mirc {%data}]] unset %data } /tcl_strlen Hello world 3. MIRC SIGNAL CALLBACKS AND OUTPUT HANDLING Tcl4mIRC sends a few signals to mIRC now and then that can be caught by the scripter. For more information on the SIGNAL event, read mIRC's help under "ON SIGNAL". The following signals are supplied: > TCL_ERROR This signal will be triggered in the rare case where the CLB cannot communicate with mIRC. This is a critical error and should be handled by halting further script processing. Error details will be filled into $1-. > TCL_LOADED Triggered when TCL is finished loading into memory. You can use this to load any important TCL support libraries. The identifier $1 will be filled with the "supported mode" value as $true / $false (whether or not the CLB is running in supported mode). See "supported mode" in the FAQ for more information on this value. > TCL_STDOUT_BEGIN / TCL_STDERR_BEGIN Triggered when Tcl is about to read a block of text either from standard output or standard error. You can use this to /linesep if you wish. > TCL_STDOUT / TCL_STDERR Triggered when Tcl reads *one line* of data from standard output or standard error. $1- is filled with the line contents. > TCL_STDOUT_END / TCL_STDERR_END Triggered when Tcl finishes printing a block of text from standard output or standard error. 4. USING TK Ideally, Tk should be run within a separate process from mIRC. This will enable your Tk application to perform proper shutdown procedures. You can do this by using `exec` to run `wish` on your Tcl script (exec wish myfile.tcl) However, if you must run Tk code within the same process as mIRC, there are a few things to keep in mind: 1. As hinted above, Tcl/Tk is running in the same process space as mIRC. This means that if your Tk application calls `exit` to shutdown, it will close mIRC as well. 2. Closing the root level window (the one that pops up when you require Tk) will disable the Tk interpreter completely. You will probably see the following error message if you do so and try to use Tk: can't invoke "tk" command: application has been destroyed You can reinvoke Tk by unloading the dll (/dll -u $tcl_dll) and reloading it. Instead of closing the root window, hide it with: /tcl wm withdraw . You can reshow any withdrawn windows with `wm deiconify path`: /tcl wm deiconify . To ensure that you do not accidentally click the X button, you can add the following to any Tk initialization commands you may be doing: alias tk_init { tcl package require Tk tcl wm protocol . WM_DELETE_WINDOW {wm withdraw .} } Again, the above issues can be ignored if you run your Tk applications in their own process space.