/* * * t k - g l u e . c - Glue function between the scheme and Tk worlds * * Copyright © 1993-1996 Erick Gallesio - I3S-CNRS/ESSI * * * Permission to use, copy, and/or distribute this software and its * documentation for any purpose and without fee is hereby granted, provided * that both the above copyright notice and this permission notice appear in * all copies and derived works. Fees for distribution or use of this * software or derived works may only be charged with express written * permission of the copyright holder. * This software is provided ``as is'' without express or implied warranty. * * This software is a derivative work of other copyrighted softwares; the * copyright notices of these softwares are placed in the file COPYRIGHTS * * * Author: Erick Gallesio [eg@unice.fr] * Creation date: 19-Feb-1993 22:15 * Last file update: 13-Jun-1996 19:10 * * */ #ifdef USE_TK #include "stk.h" #include "tk-glue.h" #include "gc.h" #include "tkInt.h" #define MAXARG 64 /* Max args on stack. Use malloc if greater */ /* Scheme objects used to represent the "." pseudo widget and its name */ SCM STk_root_window; SCM STk_root_window_name; /* Last result of Tcl_GlobalEval (as a SCM object rather than a string) */ SCM STk_last_Tk_result; static SCM TkResult2Scheme(Tcl_Interp *interp) { register char*s= interp->result; register SCM tmp1, tmp2, z, port; SCM result = NIL; int eof; if (*s) { /* Create a string port to read in the result */ port = STk_internal_open_input_string(s); result = STk_internal_read_from_string(port, &eof, TRUE); if (result == Sym_dot) result = STk_root_window; if (!eof) { /* Result was a list of value, build a proper Scheme list */ tmp1 = result = LIST1(result); for ( ; ; ) { z = STk_internal_read_from_string(port, &eof, TRUE); if (z == EVAL_ERROR || EOFP(z)) break; if (z == Sym_dot) z = STk_root_window; NEWCELL(tmp2, tc_cons); CAR(tmp2) = z; CDR(tmp1) = tmp2; tmp1 = tmp2; } CDR(tmp1) = NIL; } /* close_string_port(port); */ } Tcl_ResetResult(interp); return (result == EVAL_ERROR)? UNDEFINED: result; } char *STk_convert_for_Tk(SCM obj, SCM *res) { switch (TYPE(obj)) { case tc_symbol: *res = obj; return PNAME(obj); case tc_integer: case tc_bignum: case tc_flonum: *res = STk_number2string(obj, UNBOUND); return CHARS(*res); case tc_string: *res = obj; return CHARS(obj); case tc_tkcommand: return (obj->storage_as.tk.data)->Id; case tc_keyword: *res = obj; return obj->storage_as.keyword.data; case tc_boolean: return (obj == Truth)? "#t" : "#f"; default: /* Ok, take the big hammer (i.e. use a string port for * type coercion) Here, use write (and not display) * since it handles complex data structures containing * eventually special chars which must be escaped * Ex: (bind .w "" '(display "")) * First is unquotted and second is not */ { SCM port; port = STk_open_output_string(); STk_print(obj, port, TK_MODE); *res = STk_get_output_string(port); return CHARS(*res); } } } SCM STk_execute_Tcl_lib_cmd(SCM cmd, SCM args, SCM env, int eval_args) { char *buffer[MAXARG+2]; int tkres; char **argv = buffer; int argc = STk_llength(args); SCM conv_res, start = args; struct Tk_command *W = cmd->storage_as.tk.data; if (argc >= MAXARG) { /* allocate dynamically the argv array (one extra for argv[0] and one * for the NULL terminator -dsf */ argv=(char **) must_malloc((argc+2) * sizeof(char *)); } /* * conv_res is (roughly) a vector of the values returned by convert_for_Tk. * It serves only to have pointers in the stack on the converted values. * This permits to avoid GC problems (i.e. a GC between 1 and argc * whereas convert_for_Tk has created new cells in a previous iteration */ conv_res = STk_makevect(argc+2, NIL); /* First initialize an argv array */ argv[0] = cmd->storage_as.tk.data->Id; for (argc = 1; NNULLP(args); argc++, args=CDR(args)) { if (NCONSP(args)) Err("Malformed list of arguments", start); argv[argc] = STk_convert_for_Tk(eval_args ? STk_eval(CAR(args), env):CAR(args), &(VECT(conv_res)[argc])); } argv[argc] = NULL; /* Now, call the Tk library function */ Tcl_ResetResult(STk_main_interp); tkres = (*W->fct)(W->ptr, STk_main_interp, argc, argv); if (argv != buffer) { /* argv was allocated dynamically. Dispose it */ free(argv); } /* return result as a string or "evaluated" depending of string_result field */ if (tkres == TCL_OK) return TkResult2Scheme(STk_main_interp); Err(STk_main_interp->result, NIL); } /****************************************************************************** * * Callback management * ******************************************************************************/ static Tcl_HashTable Tk_callbacks; int STk_valid_callback(char *s, void **closure) { /* A callback is valid iff it is of the form "#pxxxx" where xxxx is composed * only of hexadecimal digit. * Furthermore, the given address must be a valid adress */ int l = strlen(s); char *p; *closure = NULL; if (l > 2) { if (s[0] == '#' && s[1] == 'p') { /* Verify that the rest of the string only contains hexadecimal digits */ for (p = s + 2; *p; p++) if (!isxdigit(*p)) return FALSE; sscanf(s+2, "%lx", (unsigned long) closure); if (!STk_valid_address((SCM) *closure)) return FALSE; } } return TRUE; } void STk_add_callback(char *key1, char *key2, char *key3, SCM closure) { Tcl_HashEntry *entry; Tcl_HashTable *secondary_hash_table; int new; char key[200]; /* Largely sufficient */ if (*key2) { /* We have two keys. Use a secondary hash table */ if (entry=Tcl_FindHashEntry(&Tk_callbacks, key1)) /* Key already in hash table */ secondary_hash_table = Tcl_GetHashValue(entry); else { secondary_hash_table = (Tcl_HashTable *) must_malloc(sizeof(Tcl_HashTable)); Tcl_InitHashTable(secondary_hash_table, TCL_STRING_KEYS); entry = Tcl_CreateHashEntry(&Tk_callbacks, (char *) key1, &new); Tcl_SetHashValue(entry, secondary_hash_table); } /* Enter a new key (obtained from key2 and key3) in the hash table. * Don't worry about old value: since it is no more pointed by the * hash table, it will be garbaged at next GC run */ sprintf(key, "%s#%s", key2, key3);/* Create a new key from key2 and key3 */ entry = Tcl_CreateHashEntry(secondary_hash_table, key, &new); Tcl_SetHashValue(entry, closure); } else { /* Only one key. No need for a secondary hash table */ entry =Tcl_CreateHashEntry(&Tk_callbacks, key1, &new); Tcl_SetHashValue(entry, closure); } } void STk_delete_callback(char *key) { /* * key is destroyed. We only need to free the entry associated to it in the * Tk_callback hash table (if it exists). */ Tcl_HashEntry *entry; Tcl_HashTable *secondary_hash_table; if (entry=Tcl_FindHashEntry(&Tk_callbacks, key)) { if (*key != 'a' && strncmp(key, "after#", 6) != 0) { /* Delete the secondary hash table associated to this entry */ secondary_hash_table = Tcl_GetHashValue(entry); Tcl_DeleteHashTable(secondary_hash_table); free(secondary_hash_table); } /* Delete the entry itself */ Tcl_DeleteHashEntry(entry); } } void STk_mark_callbacks(void) { Tcl_HashEntry *entry1, *entry2; Tcl_HashSearch search1, search2; Tcl_HashTable *secondary; char *key; for (entry1 = Tcl_FirstHashEntry(&Tk_callbacks, &search1); entry1; entry1 = Tcl_NextHashEntry(&search1)) { key = Tcl_GetHashKey(&Tk_callbacks, entry1); if (*key == 'a' && strncmp(key, "after#", 6) == 0) { /* No secondary hash table */ STk_gc_mark((SCM) Tcl_GetHashValue(entry1)); } else { /* We have a secondary hash table. Scan it */ secondary = Tcl_GetHashValue(entry1); for (entry2 = Tcl_FirstHashEntry(secondary, &search2); entry2; entry2 = Tcl_NextHashEntry(&search2)) { STk_gc_mark((SCM) Tcl_GetHashValue(entry2)); } } } } /* * Return the parameters associated to the callback contained (as a string) * in the value parameter. If an error occurs, this function returns NULL * */ char *STk_append_callback_parameters(SCM proc) { SCM param, port; if (!CLOSUREP(proc)) return NULL; param = CLOSURE_PARAMETERS(proc); if (NULLP(param) || CONSP(param)) { port = STk_open_output_string(); STk_print(Cons(proc, param) , port, TK_MODE); return CHARS(STk_get_output_string(port)); } return NULL; } /****************************************************************************** * * Tcl result manipulation functions * ******************************************************************************/ void STk_sharp_dot_result(Tcl_Interp *interp, char *value) { /* Transform Tcl result in #.result so that it is evaluated when read */ int len = strlen(value); char *s; s = (char *) STk_must_malloc(len + 3); s[0] = '#'; s[1] = '.'; strcpy(s+2, value); Tcl_SetResult(interp, s, TCL_VOLATILE); } void STk_stringify_result(Tcl_Interp *interp, char *value) { /* Transform Tcl result in "result" with " and \ escaped */ Tcl_SetResult(interp, STk_stringify(value, 0), TCL_VOLATILE); } SCM STk_last_Tk_as_SCM(void) { return STk_last_Tk_result; } SCM STk_get_NIL_value(void) { return NIL; } /* * STk_stringify permits to transform the string "s" in a valid STk string. * Original string is deallocated if free_original is 1 */ char *STk_stringify(char *s, int free_original) { char *res, *d; if (s == NULL) s = ""; res = d = must_malloc(2 * strlen(s) + 3); /* worst overestimation */ for ( *d++ = '"'; *s; s++, d++) { if (*s == '"' || *s == '\\') *d++ = '\\'; *d = *s; } *d++ = '"'; *d = '\0'; if (free_original) free(s); return res; } /****************************************************************************** * * Motif simulation * * Tk 4.0 uses a field in the Tk_Window structure to tell the library if it * must be conform to Motif look. This field is Tcl_LinkVar'ed. * ******************************************************************************/ static SCM get_Motif(char *s) { TkWindow *p = (TkWindow *) Tk_MainWindow(STk_main_interp); return (p->mainPtr->strictMotif) ? Truth: Ntruth; } static void set_Motif(char *s, SCM value) { TkWindow *p = (TkWindow *) Tk_MainWindow(STk_main_interp); p->mainPtr->strictMotif = !(value == Ntruth); } void STk_init_glue(void) { /* * Take into account the fact that Tk main window name (i.e. ``.'') * cannot be used in list since it leads to erroneous evaluation * (e.g. [focus .] would produce an error since read will find a malformed * pair). * */ STk_root_window_name=Intern(ROOT_WINDOW); STk_gc_protect(&STk_root_window_name); STk_root_window =STk_eval(Sym_dot, NIL);STk_gc_protect(&STk_root_window); VCELL(STk_root_window_name) = STk_root_window; /* Init the callback table */ Tcl_InitHashTable(&Tk_callbacks, TCL_STRING_KEYS); /* Associate a getter and a setter for the global variable *Tk-strict-Motif* */ STk_define_C_variable("*tk-strict-motif*", get_Motif, set_Motif); } #endif /* USE_TK */