Mtk enhancement
Thomas Charles CONWAY
conway at cs.mu.oz.au
Mon May 26 09:42:00 AEST 1997
Oops - I meant to include the new src in the previous message.
Here it is....
--
ZZ:wq!
^X^C
Thomas Conway conway at cs.mu.oz.au
AD DEUM ET VINUM Every sword has two edges.
%-----------------------------------------------------------------------------%
% Copyright (C) 1997 University of Melbourne.
% This file may only be copied under the terms of the GNU Library General
% Public License - see the file COPYING.LIB in the Mercury distribution.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
% mtk -- the Mercury interface to Tk/Tcl
% authors: conway, fjh
% Stability: medium.
%-----------------------------------------------------------------------------%
%
% To use the Mercury interface to Tk/Tcl, you will need to
% link with the Tcl and Tk libraries. To do this, you'll need
% to add a line like the following to your Mmake file:
%
% MLLIBS = -ltcl7.5 -ltk4.1
% and on linux, -ldl
%
% If Tcl and Tk are installed in places that are not on the
% default search path for libraries then you'll need to add
% a -L flag to your MFLAGS to search there. You may also need
% to add flags to make the executable look in the right places
% for dynamic libraries. On the Solaris machines at cs.mu.oz.au
% I had to use the following MLFLAGS to get things going:
%
% MLFLAGS = -Wl,-R,/usr/local/lib -L/usr/local/lib
% MLLIBS = -ltcl7.5 -ltk4.1
%
%-----------------------------------------------------------------------------%
:- module mtk.
:- interface.
:- import_module io.
:- import_module list, string.
% The tcl_interp type is an abstract data type that
% represents a Tcl interpreter.
:- type tcl_interp.
% The tcl_status type is the type that is returned by tcl
% commands to indicate whether or not they were successful.
:- type tcl_status ---> tcl_ok ; tcl_error.
% mtk:main(Callback, Args):
% first initializes a Tcl interpreter `Interp' using `Args';
% the first `Arg' should be the program name (which you
% can obtain using io__progname), and the remainder
% are arguments that are passed to Interp
% then invokes `Callback' with `Interp', which you can use to
% add your own Tcl commands and/or to invoke Tk commands
% finally starts the Tk event loop
%
:- pred mtk:main(pred(tcl_interp, io__state, io__state),
list(string), io__state, io__state).
:- mode mtk:main(pred(in, di, uo) is det, in, di, uo) is det.
% mtk:eval(Interp, Command, Status, Result):
% evaluates `Command' in `Interp'.
% if successful, returns `Status = tcl_ok'
% and binds `Result' to the return string,
% otherwise returns `Status = tcl_error'
% and binds `Result' to the error message.
:- pred mtk:eval(tcl_interp, string, tcl_status, string, io__state, io__state).
:- mode mtk:eval(in, in, out, out, di, uo) is det.
% mtk:create_command(Interp, Name, Command):
% creates a new Tcl command called `Name' in `Interp'.
% Whenever `Name' is evaluated as a Tcl command in `Interp',
% the Tcl interpreter will use
% `call(Command, Interp, Args, Status, Result)'
% to invoke the Mercury procedure `Command' passing `Interp'
% and `Args', which is the list of arguments (including the
% command name `Name') passed to the command. If successful,
% `Command' should return `Status = tcl_ok' and a return value
% in `Result'. If an error occurs, `Command' should return
% `Status = tcl_error' and should bind `Result' to an
% appropriate error message.
:- pred mtk:create_command(tcl_interp, string,
pred(tcl_interp, list(string), tcl_status, string,
io__state, io__state),
io__state, io__state).
:- mode mtk:create_command(in, in, pred(in, in, out, out, di, uo) is det,
di, uo) is det.
% mtk:delete_command(Interp, Name, Result):
% deletes the Tcl/Tk command called `Name' in `Interp'
% and returns `Result'.
:- pred mtk:delete_command(tcl_interp, string, tcl_status,
io__state, io__state).
:- mode mtk:delete_command(in, in, out, di, uo) is det.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- type tcl_interp == c_pointer.
:- pragma c_header_code("
/*
* tkAppInit.c --
*
* Provides a default version of the Tcl_AppInit procedure for
* use in wish and similar Tk-based applications.
*
* Copyright (c) 1993 The Regents of the University of California.
* Copyright (c) 1994 Sun Microsystems, Inc.
*
* See the file ""license.terms"" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
* SCCS: @(#) tkAppInit.c 1.20 96/02/15 18:55:27
*/
#include ""tk.h""
#include ""mtk.h""
").
/*
* The following variable is a special hack that is needed in order for
* Sun shared libraries to be used for Tcl.
*/
:- pragma c_code("
extern int matherr(void);
int *tclDummyMathPtr = (int *) matherr;
").
:- pragma c_header_code("
extern Word mtk_current_io_state;
extern Word mtk_mercury_initializer;
char *mtk_strdup(const char *str);
").
:- pragma c_code("
Word mtk_current_io_state;
Word mtk_mercury_initializer;
").
:- pragma c_code(mtk:main(Closure::pred(in, di, uo) is det, Args::in,
IO0::di, IO::uo), may_call_mercury, "
{
Word l;
int argc, i;
char **argv;
/*
** convert arguments from a list of strings to an array of strings
*/
argc = 0;
for(l = Args; l != list_empty(); l = list_tail(l))
argc++;
incr_hp((Word)argv, argc+1);
for(i = 0, l = Args; l != list_empty(); l = list_tail(l), i++)
argv[i] = (char *) list_head(l);
argv[i] = NULL;
mtk_mercury_initializer = Closure;
mtk_current_io_state = IO0;
Tk_Main(argc, argv, Tcl_AppInit);
IO = mtk_current_io_state;
}").
:- pred call_mercury_initializer(pred(tcl_interp, io__state, io__state),
tcl_interp, io__state, io__state).
:- mode call_mercury_initializer(pred(in, di, uo) is det, in, di, uo) is det.
call_mercury_initializer(Closure, Interp) -->
call(Closure, Interp).
:- pragma export(call_mercury_initializer(pred(in, di, uo) is det, in, di, uo),
"mtk_call_mercury_initializer").
:- pragma c_code("
/*
*----------------------------------------------------------------------
*
* Tcl_AppInit --
*
* This procedure performs application-specific initialization.
* Most applications, especially those that incorporate additional
* packages, will have their own version of this procedure.
*
* Results:
* Returns a standard Tcl completion code, and leaves an error
* message in interp->result if an error occurs.
*
* Side effects:
* Depends on the startup script.
*
*----------------------------------------------------------------------
*/
int
Tcl_AppInit(Tcl_Interp *interp)
{
static char tk_str[] = ""Tk"";
static char tcl_rcFileName_str[] = ""tcl_rcFileName"";
static char gwarsrc_str[] = ""~/.gwarsrc"";
if (Tcl_Init(interp) == TCL_ERROR) {
return TCL_ERROR;
}
if (Tk_Init(interp) == TCL_ERROR) {
return TCL_ERROR;
}
Tcl_StaticPackage(interp, tk_str, Tk_Init, (Tcl_PackageInitProc *) NULL);
mtk_call_mercury_initializer(mtk_mercury_initializer,
(Word)interp, mtk_current_io_state, &mtk_current_io_state);
Tcl_SetVar(interp, tcl_rcFileName_str, gwarsrc_str, TCL_GLOBAL_ONLY);
return TCL_OK;
}
").
% XXX Had to change Status to RStatus because using Status
% resulted in *parse errors* in gcc :-(
:- pragma c_code(eval(Interp::in, Cmd::in, RStatus::out, Result::out,
IO0::di, IO::uo), may_call_mercury, "
{
int err;
mtk_current_io_state = IO0;
err = Tcl_Eval((Tcl_Interp *)Interp, (char *)Cmd);
switch (err) {
case TCL_OK:
RStatus = 0;
break;
case TCL_ERROR:
RStatus = 1;
break;
default:
fatal_error(""Tcl_Eval returned neither ""
""TCL_OK or TCL_ERROR"");
}
Result = mtk_strdup(((Tcl_Interp *)Interp)->result);
IO = mtk_current_io_state;
}
").
:- pragma c_code("
char *mtk_strdup(const char *str)
{
Word newstr;
assert(str);
incr_hp_atomic(newstr, (strlen(str)+sizeof(Word))/sizeof(Word));
assert(newstr);
strcpy((char *)newstr, str);
return (char *)newstr;
}
").
:- pragma c_header_code("
int mtk_do_callback(ClientData clientData, Tcl_Interp *interp,
int argc, char *argv[]);
").
:- pred call_mercury_closure(
pred(tcl_interp, list(string), tcl_status, string,
io__state, io__state),
tcl_interp, list(string), tcl_status, string,
io__state, io__state).
:- mode call_mercury_closure(pred(in, in, out, out, di, uo) is det,
in, in, out, out, di, uo) is det.
call_mercury_closure(Closure, Interp, Args, Status, Result) -->
call(Closure, Interp, Args, Status, Result).
:- pragma export(call_mercury_closure(pred(in, in, out, out, di, uo) is det,
in, in, out, out, di, uo),
"mtk_call_mercury_closure").
:- pragma c_code("
int mtk_do_callback(ClientData clientData, Tcl_Interp *interp,
int argc, char *argv[])
{
Word status;
Word args;
int i;
/* convert the array of strings into a Mercury list of strings */
args = list_empty();
for (i = argc - 1; i >= 0; i--) {
args = list_cons(mtk_strdup(argv[i]), args);
}
mtk_call_mercury_closure((Word) clientData, (Word) interp,
args, &status, &interp->result,
mtk_current_io_state, &mtk_current_io_state);
/*
fprintf(stderr, ""mercury result: `%s'\n"", interp->result);
*/
return (mtk_tcl_status_ok(status) ? TCL_OK : TCL_ERROR);
}
").
:- pred tcl_status_ok(tcl_status::in) is semidet.
:- pragma export(tcl_status_ok(in), "mtk_tcl_status_ok").
tcl_status_ok(tcl_ok).
:- pragma c_code(create_command(Interp::in, Name::in,
Closure::pred(in, in, out, out, di, uo) is det,
IO0::di, IO::uo), may_call_mercury,
"{
mtk_current_io_state = IO0;
Tcl_CreateCommand((Tcl_Interp *)Interp, Name, mtk_do_callback,
(ClientData)Closure, NULL);
IO = mtk_current_io_state;
}").
:- pragma c_code(delete_command(Interp::in, Name::in, Result::out,
IO0::di, IO::uo),
"{
int err;
mtk_current_io_state = IO0;
err = Tcl_DeleteCommand((Tcl_Interp *)Interp, Name);
Result = (err == 0 ? 0 : 1);
IO = mtk_current_io_state;
}").
:- pragma c_code("
#ifdef CONSERVATIVE_GC
/*
** The addresses of the closures that we pass to Tk
** will be stored by Tk in malloc()'ed memory.
** However, it is essential that these pointers be
** visible to the garbage collector, otherwise it will
** think that the closures are unreferenced and reuse the storage.
** Hence we redefine malloc() and friends to call GC_malloc().
*/
void *malloc(size_t s)
{
return GC_MALLOC(s);
}
void *calloc(size_t s, size_t n)
{
void *t;
t = GC_MALLOC(s*n);
memset(t, 0, s*n);
return t;
}
void *realloc(void *ptr, size_t s)
{
return GC_REALLOC(ptr, s);
}
void free(void *ptr)
{
GC_FREE(ptr);
}
#endif
").
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
More information about the developers
mailing list