/* * tcltklib.c * Aug. 27, 1997 Y. Shigehiro * Oct. 24, 1997 Y. Matsumoto */ #include "ruby.h" #include "rubysig.h" #undef EXTERN /* avoid conflict with tcl.h of tcl8.2 or before */ #include #ifdef HAVE_STDARG_PROTOTYPES #include #define va_init_list(a,b) va_start(a,b) #else #include #define va_init_list(a,b) va_start(a) #endif #include #include #include #ifdef __MACOS__ # include # include #endif #if TCL_MAJOR_VERSION >= 8 # ifndef CONST84 # if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION <= 4 /* Tcl8.0.x -- 8.4b1 */ # define CONST84 # else /* unknown (maybe TCL_VERSION >= 8.5) */ # ifdef CONST # define CONST84 CONST # else # define CONST84 # endif # endif # endif #else /* TCL_MAJOR_VERSION < 8 */ # ifdef CONST # define CONST84 CONST # else # define CONST # define CONST84 # endif #endif /* copied from eval.c */ #define TAG_RETURN 0x1 #define TAG_BREAK 0x2 #define TAG_NEXT 0x3 #define TAG_RETRY 0x4 #define TAG_REDO 0x5 #define TAG_RAISE 0x6 #define TAG_FATAL 0x8 /* for ruby_debug */ #define DUMP1(ARG1) if (ruby_debug) { fprintf(stderr, "tcltklib: %s\n", ARG1); fflush(stderr); } #define DUMP2(ARG1, ARG2) if (ruby_debug) { fprintf(stderr, "tcltklib: ");\ fprintf(stderr, ARG1, ARG2); fprintf(stderr, "\n"); fflush(stderr); } /* #define DUMP1(ARG1) #define DUMP2(ARG1, ARG2) */ /* finalize_proc_name */ static char *finalize_hook_name = "INTERP_FINALIZE_HOOK"; /* for callback break & continue */ static VALUE eTkCallbackReturn; static VALUE eTkCallbackBreak; static VALUE eTkCallbackContinue; static VALUE eLocalJumpError; static ID ID_at_enc; static ID ID_at_interp; static ID ID_stop_p; static ID ID_kill; static ID ID_join; static ID ID_call; static ID ID_backtrace; static ID ID_message; static ID ID_at_reason; static ID ID_return; static ID ID_break; static ID ID_next; static ID ID_to_s; static ID ID_inspect; static VALUE ip_invoke_real _((int, VALUE*, VALUE)); static VALUE ip_invoke _((int, VALUE*, VALUE)); /* from tkAppInit.c */ #if !defined __MINGW32__ /* * The following variable is a special hack that is needed in order for * Sun shared libraries to be used for Tcl. */ extern int matherr(); int *tclDummyMathPtr = (int *) matherr; #endif /*---- module TclTkLib ----*/ struct invoke_queue { Tcl_Event ev; int argc; #if TCL_MAJOR_VERSION >= 8 Tcl_Obj **argv; #else /* TCL_MAJOR_VERSION < 8 */ char **argv; #endif VALUE interp; int *done; int safe_level; VALUE result; VALUE thread; }; struct eval_queue { Tcl_Event ev; char *str; int len; VALUE interp; int *done; int safe_level; VALUE result; VALUE thread; }; void invoke_queue_mark(struct invoke_queue *q) { rb_gc_mark(q->interp); rb_gc_mark(q->result); rb_gc_mark(q->thread); } void eval_queue_mark(struct eval_queue *q) { rb_gc_mark(q->interp); rb_gc_mark(q->result); rb_gc_mark(q->thread); } static VALUE eventloop_thread; static VALUE watchdog_thread; Tcl_Interp *current_interp; /* * 'event_loop_max' is a maximum events which the eventloop processes in one * term of thread scheduling. 'no_event_tick' is the count-up value when * there are no event for processing. * 'timer_tick' is a limit of one term of thread scheduling. * If 'timer_tick' == 0, then not use the timer for thread scheduling. */ #define DEFAULT_EVENT_LOOP_MAX 800/*counts*/ #define DEFAULT_NO_EVENT_TICK 10/*counts*/ #define DEFAULT_NO_EVENT_WAIT 20/*milliseconds ( 1 -- 999 ) */ #define WATCHDOG_INTERVAL 10/*milliseconds ( 1 -- 999 ) */ #define DEFAULT_TIMER_TICK 0/*milliseconds ( 0 -- 999 ) */ #define NO_THREAD_INTERRUPT_TIME 100/*milliseconds ( 1 -- 999 ) */ static int event_loop_max = DEFAULT_EVENT_LOOP_MAX; static int no_event_tick = DEFAULT_NO_EVENT_TICK; static int no_event_wait = DEFAULT_NO_EVENT_WAIT; static int timer_tick = DEFAULT_TIMER_TICK; static int req_timer_tick = DEFAULT_TIMER_TICK; static int run_timer_flag = 0; static int event_loop_wait_event = 0; static int event_loop_abort_on_exc = 1; static int loop_counter = 0; static int check_rootwidget_flag = 0; #if TCL_MAJOR_VERSION >= 8 static int ip_ruby_eval _((ClientData, Tcl_Interp *, int, Tcl_Obj *CONST*)); static int ip_ruby_cmd _((ClientData, Tcl_Interp *, int, Tcl_Obj *CONST*)); #else /* TCL_MAJOR_VERSION < 8 */ static int ip_ruby_eval _((ClientData, Tcl_Interp *, int, char **)); static int ip_ruby_cmd _((ClientData, Tcl_Interp *, int, char **)); #endif /*---- class TclTkIp ----*/ struct tcltkip { Tcl_Interp *ip; /* the interpreter */ int return_value; /* return value */ }; static struct tcltkip * get_ip(self) VALUE self; { struct tcltkip *ptr; Data_Get_Struct(self, struct tcltkip, ptr); if (ptr == 0) { rb_raise(rb_eTypeError, "uninitialized TclTkIp"); } return ptr; } /* Tk_ThreadTimer */ static Tcl_TimerToken timer_token = (Tcl_TimerToken)NULL; /* timer callback */ static void _timer_for_tcl _((ClientData)); static void _timer_for_tcl(clientData) ClientData clientData; { int thr_crit_bup; /* struct invoke_queue *q, *tmp; */ /* VALUE thread; */ DUMP1("called timer_for_tcl"); thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; Tk_DeleteTimerHandler(timer_token); run_timer_flag = 1; if (timer_tick > 0) { timer_token = Tk_CreateTimerHandler(timer_tick, _timer_for_tcl, (ClientData)0); } else { timer_token = (Tcl_TimerToken)NULL; } rb_thread_critical = thr_crit_bup; /* rb_thread_schedule(); */ /* tick_counter += event_loop_max; */ } static VALUE set_eventloop_tick(self, tick) VALUE self; VALUE tick; { int ttick = NUM2INT(tick); int thr_crit_bup; rb_secure(4); if (ttick < 0) { rb_raise(rb_eArgError, "timer-tick parameter must be 0 or positive number"); } thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; /* delete old timer callback */ Tk_DeleteTimerHandler(timer_token); timer_tick = req_timer_tick = ttick; if (timer_tick > 0) { /* start timer callback */ timer_token = Tk_CreateTimerHandler(timer_tick, _timer_for_tcl, (ClientData)0); } else { timer_token = (Tcl_TimerToken)NULL; } rb_thread_critical = thr_crit_bup; return tick; } static VALUE get_eventloop_tick(self) VALUE self; { return INT2NUM(timer_tick); } static VALUE ip_set_eventloop_tick(self, tick) VALUE self; VALUE tick; { struct tcltkip *ptr = get_ip(self); if (Tcl_GetMaster(ptr->ip) != (Tcl_Interp*)NULL) { /* slave IP */ return get_eventloop_tick(self); } return set_eventloop_tick(self, tick); } static VALUE ip_get_eventloop_tick(self) VALUE self; { return get_eventloop_tick(self); } static VALUE set_no_event_wait(self, wait) VALUE self; VALUE wait; { int t_wait = NUM2INT(wait); rb_secure(4); if (t_wait <= 0) { rb_raise(rb_eArgError, "no_event_wait parameter must be positive number"); } no_event_wait = t_wait; return wait; } static VALUE get_no_event_wait(self) VALUE self; { return INT2NUM(no_event_wait); } static VALUE ip_set_no_event_wait(self, wait) VALUE self; VALUE wait; { struct tcltkip *ptr = get_ip(self); if (Tcl_GetMaster(ptr->ip) != (Tcl_Interp*)NULL) { /* slave IP */ return get_no_event_wait(self); } return set_no_event_wait(self, wait); } static VALUE ip_get_no_event_wait(self) VALUE self; { return get_no_event_wait(self); } static VALUE set_eventloop_weight(self, loop_max, no_event) VALUE self; VALUE loop_max; VALUE no_event; { int lpmax = NUM2INT(loop_max); int no_ev = NUM2INT(no_event); rb_secure(4); if (lpmax <= 0 || no_ev <= 0) { rb_raise(rb_eArgError, "weight parameters must be positive numbers"); } event_loop_max = lpmax; no_event_tick = no_ev; return rb_ary_new3(2, loop_max, no_event); } static VALUE get_eventloop_weight(self) VALUE self; { return rb_ary_new3(2, INT2NUM(event_loop_max), INT2NUM(no_event_tick)); } static VALUE ip_set_eventloop_weight(self, loop_max, no_event) VALUE self; VALUE loop_max; VALUE no_event; { struct tcltkip *ptr = get_ip(self); if (Tcl_GetMaster(ptr->ip) != (Tcl_Interp*)NULL) { /* slave IP */ return get_eventloop_weight(self); } return set_eventloop_weight(self, loop_max, no_event); } static VALUE ip_get_eventloop_weight(self) VALUE self; { return get_eventloop_weight(self); } static VALUE set_max_block_time(self, time) VALUE self; VALUE time; { struct Tcl_Time tcl_time; VALUE divmod; switch(TYPE(time)) { case T_FIXNUM: case T_BIGNUM: /* time is micro-second value */ divmod = rb_funcall(time, rb_intern("divmod"), 1, LONG2NUM(1000000)); tcl_time.sec = NUM2LONG(RARRAY(divmod)->ptr[0]); tcl_time.usec = NUM2LONG(RARRAY(divmod)->ptr[1]); break; case T_FLOAT: /* time is second value */ divmod = rb_funcall(time, rb_intern("divmod"), 1, INT2FIX(1)); tcl_time.sec = NUM2LONG(RARRAY(divmod)->ptr[0]); tcl_time.usec = (long)(NUM2DBL(RARRAY(divmod)->ptr[1]) * 1000000); default: rb_raise(rb_eArgError, "invalid value for time: '%s'", RSTRING(rb_funcall(time, ID_inspect, 0, 0))->ptr); } Tcl_SetMaxBlockTime(&tcl_time); return Qnil; } static VALUE lib_evloop_abort_on_exc(self) VALUE self; { if (event_loop_abort_on_exc > 0) { return Qtrue; } else if (event_loop_abort_on_exc == 0) { return Qfalse; } else { return Qnil; } } static VALUE ip_evloop_abort_on_exc(self) VALUE self; { return lib_evloop_abort_on_exc(self); } static VALUE lib_evloop_abort_on_exc_set(self, val) VALUE self, val; { rb_secure(4); if (RTEST(val)) { event_loop_abort_on_exc = 1; } else if (NIL_P(val)) { event_loop_abort_on_exc = -1; } else { event_loop_abort_on_exc = 0; } return lib_evloop_abort_on_exc(self); } static VALUE ip_evloop_abort_on_exc_set(self, val) VALUE self, val; { struct tcltkip *ptr = get_ip(self); rb_secure(4); if (Tcl_GetMaster(ptr->ip) != (Tcl_Interp*)NULL) { /* slave IP */ return lib_evloop_abort_on_exc(self); } return lib_evloop_abort_on_exc_set(self, val); } static VALUE lib_num_of_mainwindows(self) VALUE self; { return INT2FIX(Tk_GetNumMainWindows()); } static int lib_eventloop_core(check_root, update_flag, check_var) int check_root; int update_flag; int *check_var; { VALUE current = eventloop_thread; int found_event = 1; int event_flag; struct timeval t; int thr_crit_bup; if (update_flag) DUMP1("update loop start!!"); t.tv_sec = (time_t)0; t.tv_usec = (time_t)(no_event_wait*1000.0); Tk_DeleteTimerHandler(timer_token); run_timer_flag = 0; if (timer_tick > 0) { thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; timer_token = Tk_CreateTimerHandler(timer_tick, _timer_for_tcl, (ClientData)0); rb_thread_critical = thr_crit_bup; } else { timer_token = (Tcl_TimerToken)NULL; } for(;;) { if (rb_thread_alone()) { DUMP1("no other thread"); event_loop_wait_event = 0; if (update_flag) { event_flag = update_flag | TCL_DONT_WAIT; /* for safety */ } else { event_flag = TCL_ALL_EVENTS; } if (timer_tick == 0 && update_flag == 0) { timer_tick = NO_THREAD_INTERRUPT_TIME; timer_token = Tk_CreateTimerHandler(timer_tick, _timer_for_tcl, (ClientData)0); } if (check_var != (int *)NULL) { if (*check_var || !found_event) { return found_event; } } found_event = Tcl_DoOneEvent(event_flag); if (update_flag != 0) { if (found_event) { DUMP1("next update loop"); continue; } else { DUMP1("update complete"); return 0; } } DUMP1("check Root Widget"); if (check_root && Tk_GetNumMainWindows() == 0) { run_timer_flag = 0; if (!rb_prohibit_interrupt) { if (rb_trap_pending) rb_trap_exec(); } return 1; } if (loop_counter++ > 30000) { /* fprintf(stderr, "loop_counter > 30000\n"); */ loop_counter = 0; } } else { int tick_counter; DUMP1("there are other threads"); event_loop_wait_event = 1; found_event = 1; if (update_flag) { event_flag = update_flag | TCL_DONT_WAIT; /* for safety */ } else { event_flag = TCL_ALL_EVENTS | TCL_DONT_WAIT; } timer_tick = req_timer_tick; tick_counter = 0; while(tick_counter < event_loop_max) { if (check_var != (int *)NULL) { if (*check_var || !found_event) { return found_event; } } if (Tcl_DoOneEvent(event_flag)) { tick_counter++; } else { if (update_flag != 0) { DUMP1("update complete"); return 0; } tick_counter += no_event_tick; rb_thread_wait_for(t); } if (watchdog_thread != 0 && eventloop_thread != current) { return 1; } DUMP1("check Root Widget"); if (check_root && Tk_GetNumMainWindows() == 0) { run_timer_flag = 0; if (!rb_prohibit_interrupt) { if (rb_trap_pending) rb_trap_exec(); } return 1; } DUMP1("trap check"); if (!rb_prohibit_interrupt) { if (rb_trap_pending) rb_trap_exec(); } if (loop_counter++ > 30000) { /* fprintf(stderr, "loop_counter > 30000\n"); */ loop_counter = 0; } if (run_timer_flag) { /* DUMP1("timer interrupt"); run_timer_flag = 0; */ break; /* switch to other thread */ } } } DUMP1("trap check & thread scheduling"); if (update_flag == 0) CHECK_INTS; } return 1; } VALUE lib_eventloop_main(check_rootwidget) VALUE check_rootwidget; { check_rootwidget_flag = RTEST(check_rootwidget); if (lib_eventloop_core(check_rootwidget_flag, 0, (int *)NULL)) { return Qtrue; } else { return Qfalse; } } VALUE lib_eventloop_ensure(parent_evloop) VALUE parent_evloop; { Tk_DeleteTimerHandler(timer_token); timer_token = (Tcl_TimerToken)NULL; DUMP2("eventloop-ensure: current-thread : %lx\n", rb_thread_current()); DUMP2("eventloop-ensure: eventloop-thread : %lx\n", eventloop_thread); if (eventloop_thread == rb_thread_current()) { DUMP2("eventloop-thread -> %lx\n", parent_evloop); eventloop_thread = parent_evloop; } return Qnil; } static VALUE lib_eventloop_launcher(check_rootwidget) VALUE check_rootwidget; { VALUE parent_evloop = eventloop_thread; eventloop_thread = rb_thread_current(); if (ruby_debug) { fprintf(stderr, "tcltklib: eventloop-thread : %lx -> %lx\n", parent_evloop, eventloop_thread); } return rb_ensure(lib_eventloop_main, check_rootwidget, lib_eventloop_ensure, parent_evloop); } /* execute Tk_MainLoop */ static VALUE lib_mainloop(argc, argv, self) int argc; VALUE *argv; VALUE self; { VALUE check_rootwidget; if (rb_scan_args(argc, argv, "01", &check_rootwidget) == 0) { check_rootwidget = Qtrue; } else if (RTEST(check_rootwidget)) { check_rootwidget = Qtrue; } else { check_rootwidget = Qfalse; } return lib_eventloop_launcher(check_rootwidget); } static VALUE ip_mainloop(argc, argv, self) int argc; VALUE *argv; VALUE self; { struct tcltkip *ptr = get_ip(self); if (Tcl_GetMaster(ptr->ip) != (Tcl_Interp*)NULL) { /* slave IP */ return Qnil; } return lib_mainloop(argc, argv, self); } VALUE lib_watchdog_core(check_rootwidget) VALUE check_rootwidget; { VALUE evloop; int prev_val = -1; int chance = 0; int check = RTEST(check_rootwidget); struct timeval t0, t1; t0.tv_sec = (time_t)0; t0.tv_usec = (time_t)((NO_THREAD_INTERRUPT_TIME)*1000.0); t1.tv_sec = (time_t)0; t1.tv_usec = (time_t)((WATCHDOG_INTERVAL)*1000.0); /* check other watchdog thread */ if (watchdog_thread != 0) { if (RTEST(rb_funcall(watchdog_thread, ID_stop_p, 0))) { rb_funcall(watchdog_thread, ID_kill, 0); } else { return Qnil; } } watchdog_thread = rb_thread_current(); /* watchdog start */ do { if (eventloop_thread == 0 || (loop_counter == prev_val && RTEST(rb_funcall(eventloop_thread, ID_stop_p, 0)) && ++chance >= 3 ) ) { /* start new eventloop thread */ DUMP2("eventloop thread %lx is sleeping or dead", eventloop_thread); evloop = rb_thread_create(lib_eventloop_launcher, (void*)&check_rootwidget); DUMP2("create new eventloop thread %lx", evloop); loop_counter = -1; chance = 0; rb_thread_run(evloop); } else { loop_counter = prev_val; chance = 0; if (event_loop_wait_event) { rb_thread_wait_for(t0); } else { rb_thread_wait_for(t1); } /* rb_thread_schedule(); */ } } while(!check || Tk_GetNumMainWindows() != 0); return Qnil; } VALUE lib_watchdog_ensure(arg) VALUE arg; { eventloop_thread = 0; /* stop eventloops */ return Qnil; } static VALUE lib_mainloop_watchdog(argc, argv, self) int argc; VALUE *argv; VALUE self; { VALUE check_rootwidget; if (rb_scan_args(argc, argv, "01", &check_rootwidget) == 0) { check_rootwidget = Qtrue; } else if (RTEST(check_rootwidget)) { check_rootwidget = Qtrue; } else { check_rootwidget = Qfalse; } return rb_ensure(lib_watchdog_core, check_rootwidget, lib_watchdog_ensure, Qnil); } static VALUE ip_mainloop_watchdog(argc, argv, self) int argc; VALUE *argv; VALUE self; { struct tcltkip *ptr = get_ip(self); if (Tcl_GetMaster(ptr->ip) != (Tcl_Interp*)NULL) { /* slave IP */ return Qnil; } return lib_mainloop_watchdog(argc, argv, self); } static VALUE lib_do_one_event_core(argc, argv, self, is_ip) int argc; VALUE *argv; VALUE self; int is_ip; { volatile VALUE vflags; int flags; int found_event; if (rb_scan_args(argc, argv, "01", &vflags) == 0) { flags = TCL_ALL_EVENTS | TCL_DONT_WAIT; } else { Check_Type(vflags, T_FIXNUM); flags = FIX2INT(vflags); } if (rb_safe_level() >= 4 || (rb_safe_level() >=1 && OBJ_TAINTED(vflags))) { flags |= TCL_DONT_WAIT; } if (is_ip) { /* check IP */ struct tcltkip *ptr = get_ip(self); if (Tcl_GetMaster(ptr->ip) != (Tcl_Interp*)NULL) { /* slave IP */ flags |= TCL_DONT_WAIT; } } /* found_event = Tcl_DoOneEvent(TCL_ALL_EVENTS | TCL_DONT_WAIT); */ found_event = Tcl_DoOneEvent(flags); if (found_event) { return Qtrue; } else { return Qfalse; } } static VALUE lib_do_one_event(argc, argv, self) int argc; VALUE *argv; VALUE self; { return lib_do_one_event_core(argc, argv, self, 0); } static VALUE ip_do_one_event(argc, argv, self) int argc; VALUE *argv; VALUE self; { return lib_do_one_event_core(argc, argv, self, 0); } static void ip_set_exc_message(interp, exc) Tcl_Interp *interp; VALUE exc; { char *buf; Tcl_DString dstr; volatile VALUE msg; int thr_crit_bup; #if TCL_MAJOR_VERSION > 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION > 0) volatile VALUE enc; Tcl_Encoding encoding; #endif thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; msg = rb_funcall(exc, ID_message, 0, 0); #if TCL_MAJOR_VERSION > 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION > 0) enc = Qnil; if (RTEST(rb_ivar_defined(exc, ID_at_enc))) { enc = rb_ivar_get(exc, ID_at_enc); } if (NIL_P(enc) && RTEST(rb_ivar_defined(msg, ID_at_enc))) { enc = rb_ivar_get(msg, ID_at_enc); } if (NIL_P(enc)) { encoding = (Tcl_Encoding)NULL; } else if (TYPE(enc) == T_STRING) { encoding = Tcl_GetEncoding(interp, RSTRING(enc)->ptr); } else { enc = rb_funcall(enc, ID_to_s, 0, 0); encoding = Tcl_GetEncoding(interp, RSTRING(enc)->ptr); } /* to avoid a garbled error message dialog */ buf = ALLOC_N(char, (RSTRING(msg)->len)+1); strncpy(buf, RSTRING(msg)->ptr, RSTRING(msg)->len); buf[RSTRING(msg)->len] = 0; Tcl_DStringInit(&dstr); Tcl_DStringFree(&dstr); Tcl_ExternalToUtfDString(encoding, buf, RSTRING(msg)->len, &dstr); Tcl_AppendResult(interp, Tcl_DStringValue(&dstr), (char*)NULL); DUMP2("error message:%s", Tcl_DStringValue(&dstr)); free(buf); #else /* TCL_VERSION <= 8.0 */ Tcl_AppendResult(interp, RSTRING(msg)->ptr, (char*)NULL); #endif rb_thread_critical = thr_crit_bup; } static VALUE TkStringValue(obj) VALUE obj; { switch(TYPE(obj)) { case T_STRING: return obj; case T_NIL: return rb_str_new2(""); case T_TRUE: return rb_str_new2("1"); case T_FALSE: return rb_str_new2("0"); case T_ARRAY: return rb_funcall(obj, ID_join, 1, rb_str_new2(" ")); default: if (rb_respond_to(obj, ID_to_s)) { return rb_funcall(obj, ID_to_s, 0, 0); } } return rb_funcall(obj, ID_inspect, 0, 0); } /* Tcl command `ruby'|`ruby_eval' */ static VALUE ip_ruby_eval_rescue(failed, einfo) VALUE failed; VALUE einfo; { DUMP1("call ip_ruby_eval_rescue"); RARRAY(failed)->ptr[0] = einfo; return Qnil; } struct eval_body_arg { char *string; VALUE failed; }; static VALUE ip_ruby_eval_body(arg) struct eval_body_arg *arg; { VALUE ret; int status = 0; int thr_crit_bup; thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; DUMP1("call ip_ruby_eval_body"); rb_trap_immediate = 0; #if 0 ret = rb_rescue2(rb_eval_string, (VALUE)arg->string, ip_ruby_eval_rescue, arg->failed, rb_eStandardError, rb_eScriptError, rb_eSystemExit, (VALUE)0); #else rb_thread_critical = Qfalse; ret = rb_eval_string_protect(arg->string, &status); rb_thread_critical = Qtrue; if (status) { char *errtype, *buf; int errtype_len, len; VALUE old_gc; old_gc = rb_gc_disable(); switch(status) { case TAG_RETURN: errtype = "LocalJumpError: "; errtype_len = strlen(errtype); len = errtype_len + RSTRING(rb_obj_as_string(ruby_errinfo))->len; buf = ALLOC_N(char, len + 1); strncpy(buf, errtype, errtype_len); strncpy(buf + errtype_len, RSTRING(rb_obj_as_string(ruby_errinfo))->ptr, RSTRING(rb_obj_as_string(ruby_errinfo))->len); *(buf + len) = 0; RARRAY(arg->failed)->ptr[0] = rb_exc_new2(eTkCallbackReturn, buf); free(buf); break; case TAG_BREAK: errtype = "LocalJumpError: "; errtype_len = strlen(errtype); len = errtype_len + RSTRING(rb_obj_as_string(ruby_errinfo))->len; buf = ALLOC_N(char, len + 1); strncpy(buf, errtype, errtype_len); strncpy(buf + errtype_len, RSTRING(rb_obj_as_string(ruby_errinfo))->ptr, RSTRING(rb_obj_as_string(ruby_errinfo))->len); *(buf + len) = 0; RARRAY(arg->failed)->ptr[0] = rb_exc_new2(eTkCallbackBreak, buf); free(buf); break; case TAG_NEXT: errtype = "LocalJumpError: "; errtype_len = strlen(errtype); len = errtype_len + RSTRING(rb_obj_as_string(ruby_errinfo))->len; buf = ALLOC_N(char, len + 1); strncpy(buf, errtype, errtype_len); strncpy(buf + errtype_len, RSTRING(rb_obj_as_string(ruby_errinfo))->ptr, RSTRING(rb_obj_as_string(ruby_errinfo))->len); *(buf + len) = 0; RARRAY(arg->failed)->ptr[0] = rb_exc_new2(eTkCallbackContinue,buf); free(buf); break; case TAG_RETRY: case TAG_REDO: RARRAY(arg->failed)->ptr[0] = ruby_errinfo; break; case TAG_RAISE: case TAG_FATAL: RARRAY(arg->failed)->ptr[0] = ruby_errinfo; break; default: buf = ALLOC_N(char, 256); sprintf(buf, "unknown loncaljmp status %d", status); RARRAY(arg->failed)->ptr[0] = rb_exc_new2(rb_eException, buf); free(buf); break; } if (old_gc == Qfalse) rb_gc_enable(); ret = Qnil; } #endif rb_thread_critical = thr_crit_bup; return ret; } static VALUE ip_ruby_eval_ensure(trapflag) VALUE trapflag; { rb_trap_immediate = NUM2INT(trapflag); return Qnil; } static int #if TCL_MAJOR_VERSION >= 8 ip_ruby_eval(clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; int argc; Tcl_Obj *CONST argv[]; #else /* TCL_MAJOR_VERSION < 8 */ ip_ruby_eval(clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; int argc; char *argv[]; #endif { volatile VALUE res; volatile VALUE exception = rb_ary_new2(1); int old_trapflag; struct eval_body_arg *arg; int thr_crit_bup; /* ruby command has 1 arg. */ if (argc != 2) { rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)", argc - 1); } /* allocate */ arg = ALLOC(struct eval_body_arg); /* get C string from Tcl object */ #if TCL_MAJOR_VERSION >= 8 { char *str; int len; thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; str = Tcl_GetStringFromObj(argv[1], &len); arg->string = ALLOC_N(char, len + 1); strncpy(arg->string, str, len); arg->string[len] = 0; rb_thread_critical = thr_crit_bup; } #else /* TCL_MAJOR_VERSION < 8 */ arg->string = argv[1]; #endif /* arg.failed = 0; */ RARRAY(exception)->ptr[0] = Qnil; arg->failed = exception; /* evaluate the argument string by ruby */ DUMP2("rb_eval_string(%s)", arg->string); old_trapflag = rb_trap_immediate; #ifdef HAVE_NATIVETHREAD if (!is_ruby_native_thread()) { rb_bug("cross-thread violation on ip_ruby_eval()"); } #endif res = rb_ensure(ip_ruby_eval_body, (VALUE)arg, ip_ruby_eval_ensure, INT2FIX(old_trapflag)); #if TCL_MAJOR_VERSION >= 8 free(arg->string); #endif free(arg); /* status check */ /* if (arg.failed) { */ if (!NIL_P(RARRAY(exception)->ptr[0])) { VALUE eclass; volatile VALUE backtrace; DUMP1("(rb_eval_string result) failed"); Tcl_ResetResult(interp); res = RARRAY(exception)->ptr[0]; eclass = rb_obj_class(res); thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; DUMP1("set backtrace"); backtrace = rb_ary_join(rb_funcall(res, ID_backtrace, 0, 0), rb_str_new2("\n")); StringValue(backtrace); Tcl_AddErrorInfo(interp, RSTRING(backtrace)->ptr); rb_thread_critical = thr_crit_bup; if (eclass == eTkCallbackReturn) { ip_set_exc_message(interp, res); return TCL_RETURN; } else if (eclass == eTkCallbackBreak) { ip_set_exc_message(interp, res); return TCL_BREAK; } else if (eclass == eTkCallbackContinue) { ip_set_exc_message(interp, res); return TCL_CONTINUE; } else if (eclass == rb_eSystemExit) { thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; /* Tcl_Eval(interp, "destroy ."); */ if (Tk_GetNumMainWindows() > 0) { Tk_Window main_win = Tk_MainWindow(interp); if (main_win != (Tk_Window)NULL) { Tk_DestroyWindow(main_win); } } /* StringValue(res); */ res = rb_funcall(res, ID_message, 0, 0); Tcl_AppendResult(interp, RSTRING(res)->ptr, (char*)NULL); rb_thread_critical = thr_crit_bup; rb_raise(rb_eSystemExit, RSTRING(res)->ptr); } else if (rb_obj_is_kind_of(res, eLocalJumpError)) { VALUE reason = rb_ivar_get(res, ID_at_reason); if (TYPE(reason) != T_SYMBOL) { ip_set_exc_message(interp, res); return TCL_ERROR; } if (SYM2ID(reason) == ID_return) { ip_set_exc_message(interp, res); return TCL_RETURN; } else if (SYM2ID(reason) == ID_break) { ip_set_exc_message(interp, res); return TCL_BREAK; } else if (SYM2ID(reason) == ID_next) { ip_set_exc_message(interp, res); return TCL_CONTINUE; } else { ip_set_exc_message(interp, res); return TCL_ERROR; } } else { ip_set_exc_message(interp, res); return TCL_ERROR; } } /* result must be string or nil */ if (NIL_P(res)) { DUMP1("(rb_eval_string result) nil"); Tcl_ResetResult(interp); return TCL_OK; } /* copy result to the tcl interpreter */ thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; res = TkStringValue(res); DUMP2("(rb_eval_string result) %s", RSTRING(res)->ptr); DUMP1("Tcl_AppendResult"); Tcl_ResetResult(interp); Tcl_AppendResult(interp, RSTRING(res)->ptr, (char *)NULL); rb_thread_critical = thr_crit_bup; return TCL_OK; } /* Tcl command `ruby_cmd' */ struct cmd_body_arg { VALUE receiver; ID method; VALUE args; VALUE failed; }; static VALUE ip_ruby_cmd_core(arg) struct cmd_body_arg *arg; { VALUE ret; int thr_crit_bup; DUMP1("call ip_ruby_cmd_core"); thr_crit_bup = rb_thread_critical; rb_thread_critical = Qfalse; ret = rb_apply(arg->receiver, arg->method, arg->args); rb_thread_critical = thr_crit_bup; DUMP1("finish ip_ruby_cmd_core"); return ret; } static VALUE ip_ruby_cmd_rescue(failed, einfo) VALUE failed; VALUE einfo; { DUMP1("call ip_ruby_cmd_rescue"); RARRAY(failed)->ptr[0] = einfo; return Qnil; } static VALUE ip_ruby_cmd_body(arg) struct cmd_body_arg *arg; { VALUE ret; int status = 0; int thr_crit_bup; VALUE old_gc; volatile VALUE receiver = arg->receiver; volatile VALUE args = arg->args; volatile VALUE failed = arg->failed; thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; DUMP1("call ip_ruby_cmd_body"); rb_trap_immediate = 0; #if 0 ret = rb_rescue2(ip_ruby_cmd_core, (VALUE)arg, ip_ruby_cmd_rescue, arg->failed, rb_eStandardError, rb_eScriptError, rb_eSystemExit, (VALUE)0); #else ret = rb_protect(ip_ruby_cmd_core, (VALUE)arg, &status); if (status) { char *errtype, *buf; int errtype_len, len; old_gc = rb_gc_disable(); switch(status) { case TAG_RETURN: errtype = "LocalJumpError: "; errtype_len = strlen(errtype); len = errtype_len + RSTRING(rb_obj_as_string(ruby_errinfo))->len; buf = ALLOC_N(char, len + 1); strncpy(buf, errtype, errtype_len); strncpy(buf + errtype_len, RSTRING(rb_obj_as_string(ruby_errinfo))->ptr, RSTRING(rb_obj_as_string(ruby_errinfo))->len); *(buf + len) = 0; RARRAY(arg->failed)->ptr[0] = rb_exc_new2(eTkCallbackReturn, buf); free(buf); break; case TAG_BREAK: errtype = "LocalJumpError: "; errtype_len = strlen(errtype); len = errtype_len + RSTRING(rb_obj_as_string(ruby_errinfo))->len; buf = ALLOC_N(char, len + 1); strncpy(buf, errtype, errtype_len); strncpy(buf + errtype_len, RSTRING(rb_obj_as_string(ruby_errinfo))->ptr, RSTRING(rb_obj_as_string(ruby_errinfo))->len); *(buf + len) = 0; RARRAY(arg->failed)->ptr[0] = rb_exc_new2(eTkCallbackBreak, buf); free(buf); break; case TAG_NEXT: errtype = "LocalJumpError: "; errtype_len = strlen(errtype); len = errtype_len + RSTRING(rb_obj_as_string(ruby_errinfo))->len; buf = ALLOC_N(char, len + 1); strncpy(buf, errtype, errtype_len); strncpy(buf + errtype_len, RSTRING(rb_obj_as_string(ruby_errinfo))->ptr, RSTRING(rb_obj_as_string(ruby_errinfo))->len); *(buf + len) = 0; RARRAY(arg->failed)->ptr[0] = rb_exc_new2(eTkCallbackContinue,buf); free(buf); break; case TAG_RETRY: case TAG_REDO: RARRAY(arg->failed)->ptr[0] = ruby_errinfo; break; case TAG_RAISE: case TAG_FATAL: RARRAY(arg->failed)->ptr[0] = ruby_errinfo; break; default: buf = ALLOC_N(char, 256); rb_warn(buf, "unknown loncaljmp status %d", status); RARRAY(arg->failed)->ptr[0] = rb_exc_new2(rb_eException, buf); free(buf); break; } if (old_gc == Qfalse) rb_gc_enable(); ret = Qnil; } #endif rb_thread_critical = thr_crit_bup; DUMP1("finish ip_ruby_cmd_body"); return ret; } static VALUE ip_ruby_cmd_ensure(trapflag) VALUE trapflag; { rb_trap_immediate = NUM2INT(trapflag); return Qnil; } /* ruby_cmd receiver method arg ... */ static int #if TCL_MAJOR_VERSION >= 8 ip_ruby_cmd(clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; int argc; Tcl_Obj *CONST argv[]; #else /* TCL_MAJOR_VERSION < 8 */ ip_ruby_cmd(clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; int argc; char *argv[]; #endif { volatile VALUE res; volatile VALUE receiver; volatile ID method; volatile VALUE args = rb_ary_new2(argc - 2); volatile VALUE exception = rb_ary_new2(1); char *str; int i; int len; int old_trapflag; struct cmd_body_arg *arg; int thr_crit_bup; VALUE old_gc; if (argc < 3) { rb_raise(rb_eArgError, "too few arguments"); } /* allocate */ arg = ALLOC(struct cmd_body_arg); /* get arguments from Tcl objects */ thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; old_gc = rb_gc_disable(); /* get receiver */ #if TCL_MAJOR_VERSION >= 8 str = Tcl_GetStringFromObj(argv[1], &len); #else /* TCL_MAJOR_VERSION < 8 */ str = argv[1]; #endif DUMP2("receiver:%s",str); if (str[0] == ':' || ('A' <= str[0] && str[0] <= 'Z')) { /* class | module | constant */ receiver = rb_const_get(rb_cObject, rb_intern(str)); } else if (str[0] == '$') { /* global variable */ receiver = rb_gv_get(str); } else { /* global variable omitted '$' */ char *buf; len = strlen(str); buf = ALLOC_N(char, len + 2); buf[0] = '$'; strncpy(buf + 1, str, len); buf[len + 1] = 0; receiver = rb_gv_get(buf); free(buf); } if (NIL_P(receiver)) { rb_raise(rb_eArgError, "unknown class/module/global-variable '%s'", str); } /* get metrhod */ #if TCL_MAJOR_VERSION >= 8 str = Tcl_GetStringFromObj(argv[2], &len); #else /* TCL_MAJOR_VERSION < 8 */ str = argv[2]; #endif method = rb_intern(str); /* get args */ RARRAY(args)->len = 0; for(i = 3; i < argc; i++) { #if TCL_MAJOR_VERSION >= 8 str = Tcl_GetStringFromObj(argv[i], &len); DUMP2("arg:%s",str); RARRAY(args)->ptr[RARRAY(args)->len++] = rb_tainted_str_new(str, len); #else /* TCL_MAJOR_VERSION < 8 */ DUMP2("arg:%s",argv[i]); RARRAY(args)->ptr[RARRAY(args)->len++] = rb_tainted_str_new2(argv[i]); #endif } if (old_gc == Qfalse) rb_gc_enable(); rb_thread_critical = thr_crit_bup; RARRAY(exception)->ptr[0] = Qnil; arg->receiver = receiver; arg->method = method; arg->args = args; arg->failed = exception; /* evaluate the argument string by ruby */ old_trapflag = rb_trap_immediate; #ifdef HAVE_NATIVETHREAD if (!is_ruby_native_thread()) { rb_bug("cross-thread violation on ip_ruby_cmd()"); } #endif res = rb_ensure(ip_ruby_cmd_body, (VALUE)arg, ip_ruby_cmd_ensure, INT2FIX(old_trapflag)); free(arg); /* status check */ /* if (arg.failed) { */ if (!NIL_P(RARRAY(exception)->ptr[0])) { VALUE eclass; volatile VALUE backtrace; DUMP1("(rb_eval_cmd result) failed"); Tcl_ResetResult(interp); res = RARRAY(exception)->ptr[0]; eclass = rb_obj_class(res); thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; DUMP1("set backtrace"); backtrace = rb_ary_join(rb_funcall(res, ID_backtrace, 0, 0), rb_str_new2("\n")); StringValue(backtrace); Tcl_AddErrorInfo(interp, RSTRING(backtrace)->ptr); rb_thread_critical = thr_crit_bup; if (eclass == eTkCallbackReturn) { ip_set_exc_message(interp, res); return TCL_RETURN; } else if (eclass == eTkCallbackBreak) { ip_set_exc_message(interp, res); return TCL_BREAK; } else if (eclass == eTkCallbackContinue) { ip_set_exc_message(interp, res); return TCL_CONTINUE; } else if (eclass == rb_eSystemExit) { thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; /* Tcl_Eval(interp, "destroy ."); */ if (Tk_GetNumMainWindows() > 0) { Tk_Window main_win = Tk_MainWindow(interp); if (main_win != (Tk_Window)NULL) { Tk_DestroyWindow(main_win); } } /* StringValue(res); */ res = rb_funcall(res, ID_message, 0, 0); Tcl_AppendResult(interp, RSTRING(res)->ptr, (char*)NULL); rb_thread_critical = thr_crit_bup; rb_raise(rb_eSystemExit, RSTRING(res)->ptr); } else if (rb_obj_is_kind_of(res, eLocalJumpError)) { VALUE reason = rb_ivar_get(res, ID_at_reason); if (TYPE(reason) != T_SYMBOL) { ip_set_exc_message(interp, res); return TCL_ERROR; } if (SYM2ID(reason) == ID_return) { ip_set_exc_message(interp, res); return TCL_RETURN; } else if (SYM2ID(reason) == ID_break) { ip_set_exc_message(interp, res); return TCL_BREAK; } else if (SYM2ID(reason) == ID_next) { ip_set_exc_message(interp, res); return TCL_CONTINUE; } else { ip_set_exc_message(interp, res); return TCL_ERROR; } } else { ip_set_exc_message(interp, res); return TCL_ERROR; } } /* result must be string or nil */ if (NIL_P(res)) { DUMP1("(rb_eval_cmd result) nil"); Tcl_ResetResult(interp); return TCL_OK; } /* copy result to the tcl interpreter */ thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; old_gc = rb_gc_disable(); res = TkStringValue(res); if (old_gc == Qfalse) rb_gc_enable(); DUMP2("(rb_eval_cmd result) '%s'", RSTRING(res)->ptr); DUMP1("Tcl_AppendResult"); Tcl_ResetResult(interp); Tcl_AppendResult(interp, RSTRING(res)->ptr, (char *)NULL); rb_thread_critical = thr_crit_bup; DUMP1("end of ip_ruby_cmd"); return TCL_OK; } /**************************/ /* based on tclEvent.c */ /**************************/ #if 0 /* Disable the following "update" and "thread_update". Bcause, they don't work in a callback-proc. After calling update in a callback-proc, the callback proc never be worked. If the problem will be fixed in the future, may enable the functions. */ /*********************/ /* replace of update */ /*********************/ #if TCL_MAJOR_VERSION >= 8 static int ip_rbUpdateObjCmd _((ClientData, Tcl_Interp *, int, Tcl_Obj *CONST [])); static int ip_rbUpdateObjCmd(clientData, interp, objc, objv) ClientData clientData; Tcl_Interp *interp; int objc; Tcl_Obj *CONST objv[]; #else /* TCL_MAJOR_VERSION < 8 */ static int ip_rbUpdateCommand _((ClientData, Tcl_Interp *, int, char *[])); static int ip_rbUpdateCommand(clientData, interp, objc, objv) ClientData clientData; Tcl_Interp *interp; int objc; char *objv[]; #endif { int optionIndex; int ret, done; int flags = 0; static CONST char *updateOptions[] = {"idletasks", (char *) NULL}; enum updateOptions {REGEXP_IDLETASKS}; char *nameString; int dummy; DUMP1("Ruby's 'update' is called"); if (objc == 1) { flags = TCL_ALL_EVENTS|TCL_DONT_WAIT; } else if (objc == 2) { if (Tcl_GetIndexFromObj(interp, objv[1], updateOptions, "option", 0, &optionIndex) != TCL_OK) { return TCL_ERROR; } switch ((enum updateOptions) optionIndex) { case REGEXP_IDLETASKS: { flags = TCL_WINDOW_EVENTS|TCL_IDLE_EVENTS|TCL_DONT_WAIT; break; } default: { Tcl_Panic("ip_rbUpdateObjCmd: bad option index to UpdateOptions"); } } } else { #ifdef Tcl_WrongNumArgs Tcl_WrongNumArgs(interp, 1, objv, "[ idletasks ]"); #else # if TCL_MAJOR_VERSION >= 8 Tcl_AppendResult(interp, "wrong number of arguments: should be \"", Tcl_GetStringFromObj(objv[0], &dummy), " [ idletasks ]\"", (char *) NULL); # else /* TCL_MAJOR_VERSION < 8 */ Tcl_AppendResult(interp, "wrong number of arguments: should be \"", objv[0], " [ idletasks ]\"", (char *) NULL); # endif #endif return TCL_ERROR; } /* call eventloop */ #if 1 ret = lib_eventloop_core(0, flags, (int *)NULL); /* ignore result */ #else Tcl_UpdateObjCmd(clientData, interp, objc, objv); #endif /* * Must clear the interpreter's result because event handlers could * have executed commands. */ DUMP2("last result '%s'", Tcl_GetStringResult(interp)); Tcl_ResetResult(interp); DUMP1("finish Ruby's 'update'"); return TCL_OK; } /**********************/ /* update with thread */ /**********************/ struct th_update_param { VALUE thread; int done; }; static void rb_threadUpdateProc _((ClientData)); static void rb_threadUpdateProc(clientData) ClientData clientData; /* Pointer to integer to set to 1. */ { struct th_update_param *param = (struct th_update_param *) clientData; DUMP1("threadUpdateProc is called"); param->done = 1; rb_thread_run(param->thread); return; } #if TCL_MAJOR_VERSION >= 8 static int ip_rb_threadUpdateObjCmd _((ClientData, Tcl_Interp *, int, Tcl_Obj *CONST [])); static int ip_rb_threadUpdateObjCmd(clientData, interp, objc, objv) ClientData clientData; Tcl_Interp *interp; int objc; Tcl_Obj *CONST objv[]; #else /* TCL_MAJOR_VERSION < 8 */ static int ip_rb_threadUpdateCommand _((ClientData, Tcl_Interp *, int, char *[])); static int ip_rb_threadUpdateCommand(clientData, interp, objc, objv) ClientData clientData; Tcl_Interp *interp; int objc; char *objv[]; #endif { int optionIndex; int ret, done; int flags = 0; int dummy; struct th_update_param *param; static CONST char *updateOptions[] = {"idletasks", (char *) NULL}; enum updateOptions {REGEXP_IDLETASKS}; volatile VALUE current_thread = rb_thread_current(); DUMP1("Ruby's 'thread_update' is called"); if (rb_thread_alone() || eventloop_thread == current_thread) { #define USE_TCL_UPDATE 0 #if TCL_MAJOR_VERSION >= 8 # if USE_TCL_UPDATE DUMP1("call Tcl_UpdateObjCmd"); return Tcl_UpdateObjCmd(clientData, interp, objc, objv); # else DUMP1("call ip_rbUpdateObjCmd"); return ip_rbUpdateObjCmd(clientData, interp, objc, objv); # endif #else /* TCL_MAJOR_VERSION < 8 */ # if USE_TCL_UPDATE DUMP1("call ip_rbUpdateCommand"); return Tcl_UpdateCommand(clientData, interp, objc, objv); # else DUMP1("call ip_rbUpdateCommand"); return ip_rbUpdateCommand(clientData, interp, objc, objv); # endif #endif } DUMP1("start Ruby's 'thread_update' body"); if (objc == 1) { flags = TCL_ALL_EVENTS|TCL_DONT_WAIT; } else if (objc == 2) { if (Tcl_GetIndexFromObj(interp, objv[1], updateOptions, "option", 0, &optionIndex) != TCL_OK) { return TCL_ERROR; } switch ((enum updateOptions) optionIndex) { case REGEXP_IDLETASKS: { flags = TCL_WINDOW_EVENTS|TCL_IDLE_EVENTS|TCL_DONT_WAIT; break; } default: { Tcl_Panic("ip_rb_threadUpdateObjCmd: bad option index to UpdateOptions"); } } } else { #ifdef Tcl_WrongNumArgs Tcl_WrongNumArgs(interp, 1, objv, "[ idletasks ]"); #else # if TCL_MAJOR_VERSION >= 8 Tcl_AppendResult(interp, "wrong number of arguments: should be \"", Tcl_GetStringFromObj(objv[0], &dummy), " [ idletasks ]\"", (char *) NULL); # else /* TCL_MAJOR_VERSION < 8 */ Tcl_AppendResult(interp, "wrong number of arguments: should be \"", objv[0], " [ idletasks ]\"", (char *) NULL); # endif #endif return TCL_ERROR; } DUMP1("pass argument check"); param = (struct th_update_param *)Tcl_Alloc(sizeof(struct th_update_param)); param->thread = current_thread; param->done = 0; DUMP1("set idle proc"); Tcl_DoWhenIdle(rb_threadUpdateProc, (ClientData) param); while(!param->done) { DUMP1("wait for complete idle proc"); rb_thread_stop(); } Tcl_Free((char *)param); DUMP1("finish Ruby's 'thread_update'"); return TCL_OK; } #endif /* update and thread_update don't work internal callback proc */ /***************************/ /* replace of vwait/tkwait */ /***************************/ #if TCL_MAJOR_VERSION >= 8 static char *VwaitVarProc _((ClientData, Tcl_Interp *, CONST84 char *,CONST84 char *, int)); static char * VwaitVarProc(clientData, interp, name1, name2, flags) ClientData clientData; /* Pointer to integer to set to 1. */ Tcl_Interp *interp; /* Interpreter containing variable. */ CONST84 char *name1; /* Name of variable. */ CONST84 char *name2; /* Second part of variable name. */ int flags; /* Information about what happened. */ #else /* TCL_MAJOR_VERSION < 8 */ static char *VwaitVarProc _((ClientData, Tcl_Interp *, char *, char *, int)); static char * VwaitVarProc(clientData, interp, name1, name2, flags) ClientData clientData; /* Pointer to integer to set to 1. */ Tcl_Interp *interp; /* Interpreter containing variable. */ char *name1; /* Name of variable. */ char *name2; /* Second part of variable name. */ int flags; /* Information about what happened. */ #endif { int *donePtr = (int *) clientData; *donePtr = 1; return (char *) NULL; } #if TCL_MAJOR_VERSION >= 8 static int ip_rbVwaitObjCmd _((ClientData, Tcl_Interp *, int, Tcl_Obj *CONST [])); static int ip_rbVwaitObjCmd(clientData, interp, objc, objv) ClientData clientData; Tcl_Interp *interp; int objc; Tcl_Obj *CONST objv[]; #else /* TCL_MAJOR_VERSION < 8 */ static int ip_rbVwaitCommand _((ClientData, Tcl_Interp *, int, char *[])); static int ip_rbVwaitCommand(clientData, interp, objc, objv) ClientData clientData; Tcl_Interp *interp; int objc; char *objv[]; #endif { int ret, done, foundEvent; char *nameString; int dummy; int thr_crit_bup; DUMP1("Ruby's 'vwait' is called"); if (objc != 2) { #ifdef Tcl_WrongNumArgs Tcl_WrongNumArgs(interp, 1, objv, "name"); #else thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; #if TCL_MAJOR_VERSION >= 8 /* nameString = Tcl_GetString(objv[0]); */ nameString = Tcl_GetStringFromObj(objv[0], &dummy); #else /* TCL_MAJOR_VERSION < 8 */ nameString = objv[0]; #endif Tcl_AppendResult(interp, "wrong number of arguments: should be \"", nameString, " name\"", (char *) NULL); rb_thread_critical = thr_crit_bup; #endif return TCL_ERROR; } thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; #if TCL_MAJOR_VERSION >= 8 /* nameString = Tcl_GetString(objv[1]); */ nameString = Tcl_GetStringFromObj(objv[1], &dummy); #else /* TCL_MAJOR_VERSION < 8 */ nameString = objv[1]; #endif /* if (Tcl_TraceVar(interp, nameString, TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, VwaitVarProc, (ClientData) &done) != TCL_OK) { return TCL_ERROR; } */ ret = Tcl_TraceVar(interp, nameString, TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, VwaitVarProc, (ClientData) &done); rb_thread_critical = thr_crit_bup; if (ret != TCL_OK) { return TCL_ERROR; } done = 0; foundEvent = lib_eventloop_core(/* not check root-widget */0, 0, &done); thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; Tcl_UntraceVar(interp, nameString, TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, VwaitVarProc, (ClientData) &done); rb_thread_critical = thr_crit_bup; /* * Clear out the interpreter's result, since it may have been set * by event handlers. */ Tcl_ResetResult(interp); if (!foundEvent) { thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; Tcl_AppendResult(interp, "can't wait for variable \"", nameString, "\": would wait forever", (char *) NULL); rb_thread_critical = thr_crit_bup; return TCL_ERROR; } return TCL_OK; } /**************************/ /* based on tkCmd.c */ /**************************/ #if TCL_MAJOR_VERSION >= 8 static char *WaitVariableProc _((ClientData, Tcl_Interp *, CONST84 char *,CONST84 char *, int)); static char * WaitVariableProc(clientData, interp, name1, name2, flags) ClientData clientData; /* Pointer to integer to set to 1. */ Tcl_Interp *interp; /* Interpreter containing variable. */ CONST84 char *name1; /* Name of variable. */ CONST84 char *name2; /* Second part of variable name. */ int flags; /* Information about what happened. */ #else /* TCL_MAJOR_VERSION < 8 */ static char *WaitVariableProc _((ClientData, Tcl_Interp *, char *, char *, int)); static char * WaitVariableProc(clientData, interp, name1, name2, flags) ClientData clientData; /* Pointer to integer to set to 1. */ Tcl_Interp *interp; /* Interpreter containing variable. */ char *name1; /* Name of variable. */ char *name2; /* Second part of variable name. */ int flags; /* Information about what happened. */ #endif { int *donePtr = (int *) clientData; *donePtr = 1; return (char *) NULL; } static void WaitVisibilityProc _((ClientData, XEvent *)); static void WaitVisibilityProc(clientData, eventPtr) ClientData clientData; /* Pointer to integer to set to 1. */ XEvent *eventPtr; /* Information about event (not used). */ { int *donePtr = (int *) clientData; if (eventPtr->type == VisibilityNotify) { *donePtr = 1; } if (eventPtr->type == DestroyNotify) { *donePtr = 2; } } static void WaitWindowProc _((ClientData, XEvent *)); static void WaitWindowProc(clientData, eventPtr) ClientData clientData; /* Pointer to integer to set to 1. */ XEvent *eventPtr; /* Information about event. */ { int *donePtr = (int *) clientData; if (eventPtr->type == DestroyNotify) { *donePtr = 1; } } #if TCL_MAJOR_VERSION >= 8 static int ip_rbTkWaitObjCmd _((ClientData, Tcl_Interp *, int, Tcl_Obj *CONST [])); static int ip_rbTkWaitObjCmd(clientData, interp, objc, objv) ClientData clientData; Tcl_Interp *interp; int objc; Tcl_Obj *CONST objv[]; #else /* TCL_MAJOR_VERSION < 8 */ static int ip_rbTkWaitCommand _((ClientData, Tcl_Interp *, int, char *[])); static int ip_rbTkWaitCommand(clientData, interp, objc, objv) ClientData clientData; Tcl_Interp *interp; int objc; char *objv[]; #endif { Tk_Window tkwin = (Tk_Window) clientData; int done, index; static CONST char *optionStrings[] = { "variable", "visibility", "window", (char *) NULL }; enum options { TKWAIT_VARIABLE, TKWAIT_VISIBILITY, TKWAIT_WINDOW }; char *nameString; int ret, dummy; int thr_crit_bup; DUMP1("Ruby's 'tkwait' is called"); if (objc != 3) { #ifdef Tcl_WrongNumArgs Tcl_WrongNumArgs(interp, 1, objv, "variable|visibility|window name"); #else thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; #if TCL_MAJOR_VERSION >= 8 Tcl_AppendResult(interp, "wrong number of arguments: should be \"", Tcl_GetStringFromObj(objv[0], &dummy), " variable|visibility|window name\"", (char *) NULL); #else /* TCL_MAJOR_VERSION < 8 */ Tcl_AppendResult(interp, "wrong number of arguments: should be \"", objv[0], " variable|visibility|window name\"", (char *) NULL); #endif rb_thread_critical = thr_crit_bup; #endif return TCL_ERROR; } #if TCL_MAJOR_VERSION >= 8 thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; /* if (Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)optionStrings, "option", 0, &index) != TCL_OK) { return TCL_ERROR; } */ ret = Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)optionStrings, "option", 0, &index); rb_thread_critical = thr_crit_bup; if (ret != TCL_OK) { return TCL_ERROR; } #else /* TCL_MAJOR_VERSION < 8 */ { int c = objv[1][0]; size_t length = strlen(objv[1]); if ((c == 'v') && (strncmp(objv[1], "variable", length) == 0) && (length >= 2)) { index = TKWAIT_VARIABLE; } else if ((c == 'v') && (strncmp(objv[1], "visibility", length) == 0) && (length >= 2)) { index = TKWAIT_VISIBILITY; } else if ((c == 'w') && (strncmp(objv[1], "window", length) == 0)) { index = TKWAIT_WINDOW; } else { Tcl_AppendResult(interp, "bad option \"", objv[1], "\": must be variable, visibility, or window", (char *) NULL); return TCL_ERROR; } } #endif thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; #if TCL_MAJOR_VERSION >= 8 /* nameString = Tcl_GetString(objv[2]); */ nameString = Tcl_GetStringFromObj(objv[2], &dummy); #else /* TCL_MAJOR_VERSION < 8 */ nameString = objv[2]; #endif rb_thread_critical = thr_crit_bup; switch ((enum options) index) { case TKWAIT_VARIABLE: { thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; /* if (Tcl_TraceVar(interp, nameString, TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, WaitVariableProc, (ClientData) &done) != TCL_OK) { return TCL_ERROR; } */ ret = Tcl_TraceVar(interp, nameString, TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, WaitVariableProc, (ClientData) &done); rb_thread_critical = thr_crit_bup; if (ret != TCL_OK) { return TCL_ERROR; } done = 0; lib_eventloop_core(check_rootwidget_flag, 0, &done); thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; Tcl_UntraceVar(interp, nameString, TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, WaitVariableProc, (ClientData) &done); rb_thread_critical = thr_crit_bup; break; } case TKWAIT_VISIBILITY: { Tk_Window window; thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; window = Tk_NameToWindow(interp, nameString, tkwin); if (window == NULL) { rb_thread_critical = thr_crit_bup; return TCL_ERROR; } Tk_CreateEventHandler(window, VisibilityChangeMask|StructureNotifyMask, WaitVisibilityProc, (ClientData) &done); rb_thread_critical = thr_crit_bup; done = 0; lib_eventloop_core(check_rootwidget_flag, 0, &done); if (done != 1) { /* * Note that we do not delete the event handler because it * was deleted automatically when the window was destroyed. */ thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; Tcl_ResetResult(interp); Tcl_AppendResult(interp, "window \"", nameString, "\" was deleted before its visibility changed", (char *) NULL); rb_thread_critical = thr_crit_bup; return TCL_ERROR; } thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; Tk_DeleteEventHandler(window, VisibilityChangeMask|StructureNotifyMask, WaitVisibilityProc, (ClientData) &done); rb_thread_critical = thr_crit_bup; break; } case TKWAIT_WINDOW: { Tk_Window window; thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; window = Tk_NameToWindow(interp, nameString, tkwin); if (window == NULL) { rb_thread_critical = thr_crit_bup; return TCL_ERROR; } Tk_CreateEventHandler(window, StructureNotifyMask, WaitWindowProc, (ClientData) &done); rb_thread_critical = thr_crit_bup; done = 0; lib_eventloop_core(check_rootwidget_flag, 0, &done); /* * Note: there's no need to delete the event handler. It was * deleted automatically when the window was destroyed. */ break; } } /* * Clear out the interpreter's result, since it may have been set * by event handlers. */ Tcl_ResetResult(interp); return TCL_OK; } /****************************/ /* vwait/tkwait with thread */ /****************************/ struct th_vwait_param { VALUE thread; int done; }; #if TCL_MAJOR_VERSION >= 8 static char *rb_threadVwaitProc _((ClientData, Tcl_Interp *, CONST84 char *,CONST84 char *, int)); static char * rb_threadVwaitProc(clientData, interp, name1, name2, flags) ClientData clientData; /* Pointer to integer to set to 1. */ Tcl_Interp *interp; /* Interpreter containing variable. */ CONST84 char *name1; /* Name of variable. */ CONST84 char *name2; /* Second part of variable name. */ int flags; /* Information about what happened. */ #else /* TCL_MAJOR_VERSION < 8 */ static char *rb_threadVwaitProc _((ClientData, Tcl_Interp *, char *, char *, int)); static char * rb_threadVwaitProc(clientData, interp, name1, name2, flags) ClientData clientData; /* Pointer to integer to set to 1. */ Tcl_Interp *interp; /* Interpreter containing variable. */ char *name1; /* Name of variable. */ char *name2; /* Second part of variable name. */ int flags; /* Information about what happened. */ #endif { struct th_vwait_param *param = (struct th_vwait_param *) clientData; param->done = 1; rb_thread_run(param->thread); return (char *)NULL; } static void rb_threadWaitVisibilityProc _((ClientData, XEvent *)); static void rb_threadWaitVisibilityProc(clientData, eventPtr) ClientData clientData; /* Pointer to integer to set to 1. */ XEvent *eventPtr; /* Information about event (not used). */ { struct th_vwait_param *param = (struct th_vwait_param *) clientData; if (eventPtr->type == VisibilityNotify) { param->done = 1; } if (eventPtr->type == DestroyNotify) { param->done = 2; } rb_thread_run(param->thread); } static void rb_threadWaitWindowProc _((ClientData, XEvent *)); static void rb_threadWaitWindowProc(clientData, eventPtr) ClientData clientData; /* Pointer to integer to set to 1. */ XEvent *eventPtr; /* Information about event. */ { struct th_vwait_param *param = (struct th_vwait_param *) clientData; if (eventPtr->type == DestroyNotify) { param->done = 1; } rb_thread_run(param->thread); } #if TCL_MAJOR_VERSION >= 8 static int ip_rb_threadVwaitObjCmd _((ClientData, Tcl_Interp *, int, Tcl_Obj *CONST [])); static int ip_rb_threadVwaitObjCmd(clientData, interp, objc, objv) ClientData clientData; Tcl_Interp *interp; int objc; Tcl_Obj *CONST objv[]; #else /* TCL_MAJOR_VERSION < 8 */ static int ip_rb_threadVwaitCommand _((ClientData, Tcl_Interp *, int, char *[])); static int ip_rb_threadVwaitCommand(clientData, interp, objc, objv) ClientData clientData; Tcl_Interp *interp; int objc; char *objv[]; #endif { struct th_vwait_param *param; char *nameString; int ret, dummy; int thr_crit_bup; volatile VALUE current_thread = rb_thread_current(); DUMP1("Ruby's 'thread_vwait' is called"); if (rb_thread_alone() || eventloop_thread == current_thread) { #if TCL_MAJOR_VERSION >= 8 DUMP1("call ip_rbVwaitObjCmd"); return ip_rbVwaitObjCmd(clientData, interp, objc, objv); #else /* TCL_MAJOR_VERSION < 8 */ DUMP1("call ip_rbVwaitCommand"); return ip_rbVwaitCommand(clientData, interp, objc, objv); #endif } if (objc != 2) { #ifdef Tcl_WrongNumArgs Tcl_WrongNumArgs(interp, 1, objv, "name"); #else thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; #if TCL_MAJOR_VERSION >= 8 /* nameString = Tcl_GetString(objv[0]); */ nameString = Tcl_GetStringFromObj(objv[0], &dummy); #else /* TCL_MAJOR_VERSION < 8 */ nameString = objv[0]; #endif Tcl_AppendResult(interp, "wrong number of arguments: should be \"", nameString, " name\"", (char *) NULL); rb_thread_critical = thr_crit_bup; #endif return TCL_ERROR; } #if TCL_MAJOR_VERSION >= 8 /* nameString = Tcl_GetString(objv[1]); */ nameString = Tcl_GetStringFromObj(objv[1], &dummy); #else /* TCL_MAJOR_VERSION < 8 */ nameString = objv[1]; #endif thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; param = (struct th_vwait_param *)Tcl_Alloc(sizeof(struct th_vwait_param)); param->thread = current_thread; param->done = 0; /* if (Tcl_TraceVar(interp, nameString, TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, rb_threadVwaitProc, (ClientData) param) != TCL_OK) { return TCL_ERROR; } */ ret = Tcl_TraceVar(interp, nameString, TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, rb_threadVwaitProc, (ClientData) param); rb_thread_critical = thr_crit_bup; if (ret != TCL_OK) { return TCL_ERROR; } /* if (!param->done) { */ while(!param->done) { rb_thread_stop(); } thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; Tcl_UntraceVar(interp, nameString, TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, rb_threadVwaitProc, (ClientData) param); Tcl_Free((char *)param); rb_thread_critical = thr_crit_bup; return TCL_OK; } #if TCL_MAJOR_VERSION >= 8 static int ip_rb_threadTkWaitObjCmd _((ClientData, Tcl_Interp *, int, Tcl_Obj *CONST [])); static int ip_rb_threadTkWaitObjCmd(clientData, interp, objc, objv) ClientData clientData; Tcl_Interp *interp; int objc; Tcl_Obj *CONST objv[]; #else /* TCL_MAJOR_VERSION < 8 */ static int ip_rb_threadTkWaitCommand _((ClientData, Tcl_Interp *, int, char *[])); static int ip_rb_threadTkWaitCommand(clientData, interp, objc, objv) ClientData clientData; Tcl_Interp *interp; int objc; char *objv[]; #endif { struct th_vwait_param *param; Tk_Window tkwin = (Tk_Window) clientData; int index; static CONST char *optionStrings[] = { "variable", "visibility", "window", (char *) NULL }; enum options { TKWAIT_VARIABLE, TKWAIT_VISIBILITY, TKWAIT_WINDOW }; char *nameString; int ret, dummy; int thr_crit_bup; volatile VALUE current_thread = rb_thread_current(); DUMP1("Ruby's 'thread_tkwait' is called"); if (rb_thread_alone() || eventloop_thread == current_thread) { #if TCL_MAJOR_VERSION >= 8 DUMP1("call ip_rbTkWaitObjCmd"); return ip_rbTkWaitObjCmd(clientData, interp, objc, objv); #else /* TCL_MAJOR_VERSION < 8 */ DUMP1("call rb_VwaitCommand"); return ip_rbTkWaitCommand(clientData, interp, objc, objv); #endif } if (objc != 3) { #ifdef Tcl_WrongNumArgs Tcl_WrongNumArgs(interp, 1, objv, "variable|visibility|window name"); #else thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; #if TCL_MAJOR_VERSION >= 8 Tcl_AppendResult(interp, "wrong number of arguments: should be \"", Tcl_GetStringFromObj(objv[0], &dummy), " variable|visibility|window name\"", (char *) NULL); #else /* TCL_MAJOR_VERSION < 8 */ Tcl_AppendResult(interp, "wrong number of arguments: should be \"", objv[0], " variable|visibility|window name\"", (char *) NULL); #endif rb_thread_critical = thr_crit_bup; #endif return TCL_ERROR; } #if TCL_MAJOR_VERSION >= 8 thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; /* if (Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)optionStrings, "option", 0, &index) != TCL_OK) { return TCL_ERROR; } */ ret = Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)optionStrings, "option", 0, &index); rb_thread_critical = thr_crit_bup; if (ret != TCL_OK) { return TCL_ERROR; } #else /* TCL_MAJOR_VERSION < 8 */ { int c = objv[1][0]; size_t length = strlen(objv[1]); if ((c == 'v') && (strncmp(objv[1], "variable", length) == 0) && (length >= 2)) { index = TKWAIT_VARIABLE; } else if ((c == 'v') && (strncmp(objv[1], "visibility", length) == 0) && (length >= 2)) { index = TKWAIT_VISIBILITY; } else if ((c == 'w') && (strncmp(objv[1], "window", length) == 0)) { index = TKWAIT_WINDOW; } else { Tcl_AppendResult(interp, "bad option \"", objv[1], "\": must be variable, visibility, or window", (char *) NULL); return TCL_ERROR; } } #endif thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; #if TCL_MAJOR_VERSION >= 8 /* nameString = Tcl_GetString(objv[2]); */ nameString = Tcl_GetStringFromObj(objv[2], &dummy); #else /* TCL_MAJOR_VERSION < 8 */ nameString = objv[2]; #endif param = (struct th_vwait_param *)Tcl_Alloc(sizeof(struct th_vwait_param)); param->thread = current_thread; param->done = 0; rb_thread_critical = thr_crit_bup; switch ((enum options) index) { case TKWAIT_VARIABLE: { thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; /* if (Tcl_TraceVar(interp, nameString, TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, rb_threadVwaitProc, (ClientData) param) != TCL_OK) { return TCL_ERROR; } */ ret = Tcl_TraceVar(interp, nameString, TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, rb_threadVwaitProc, (ClientData) param); rb_thread_critical = thr_crit_bup; if (ret != TCL_OK) { return TCL_ERROR; } /* if (!param->done) { */ while(!param->done) { rb_thread_stop(); } thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; Tcl_UntraceVar(interp, nameString, TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, rb_threadVwaitProc, (ClientData) param); rb_thread_critical = thr_crit_bup; break; } case TKWAIT_VISIBILITY: { Tk_Window window; thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; window = Tk_NameToWindow(interp, nameString, tkwin); if (window == NULL) { rb_thread_critical = thr_crit_bup; return TCL_ERROR; } Tk_CreateEventHandler(window, VisibilityChangeMask|StructureNotifyMask, rb_threadWaitVisibilityProc, (ClientData) param); rb_thread_critical = thr_crit_bup; /* if (!param->done) { */ while(!param->done) { rb_thread_stop(); } thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; Tk_DeleteEventHandler(window, VisibilityChangeMask|StructureNotifyMask, rb_threadWaitVisibilityProc, (ClientData) param); if (param->done != 1) { Tcl_ResetResult(interp); Tcl_AppendResult(interp, "window \"", nameString, "\" was deleted before its visibility changed", (char *) NULL); rb_thread_critical = thr_crit_bup; return TCL_ERROR; } rb_thread_critical = thr_crit_bup; break; } case TKWAIT_WINDOW: { Tk_Window window; thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; window = Tk_NameToWindow(interp, nameString, tkwin); if (window == NULL) { rb_thread_critical = thr_crit_bup; return TCL_ERROR; } Tk_CreateEventHandler(window, StructureNotifyMask, rb_threadWaitWindowProc, (ClientData) param); rb_thread_critical = thr_crit_bup; /* if (!param->done) { */ while(!param->done) { rb_thread_stop(); } thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; Tk_DeleteEventHandler(window, StructureNotifyMask, rb_threadWaitWindowProc, (ClientData) param); rb_thread_critical = thr_crit_bup; break; } } /* end of 'switch' statement */ Tcl_Free((char *)param); /* * Clear out the interpreter's result, since it may have been set * by event handlers. */ Tcl_ResetResult(interp); return TCL_OK; } static VALUE ip_thread_vwait(self, var) VALUE self; VALUE var; { VALUE argv[2]; argv[0] = rb_str_new2("thread_vwait"); argv[1] = var; return ip_invoke_real(2, argv, self); } static VALUE ip_thread_tkwait(self, mode, target) VALUE self; VALUE mode; VALUE target; { VALUE argv[3]; argv[0] = rb_str_new2("thread_tkwait"); argv[1] = mode; argv[2] = target; return ip_invoke_real(3, argv, self); } /* destroy interpreter */ VALUE del_root(ip) Tcl_Interp *ip; { Tcl_Preserve(ip); Tk_DestroyWindow(Tk_MainWindow(ip)); Tcl_Release(ip); return Qnil; } static void ip_free(ptr) struct tcltkip *ptr; { int try = 3; Tcl_CmdInfo info; int thr_crit_bup; DUMP1("free Tcl Interp"); if (ptr) { thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; if (!Tcl_InterpDeleted(ptr->ip)) { Tcl_ResetResult(ptr->ip); Tcl_Preserve(ptr->ip); if (Tcl_GetCommandInfo(ptr->ip, finalize_hook_name, &info)) { DUMP2("call finalize hook proc '%s'", finalize_hook_name); Tcl_Eval(ptr->ip, finalize_hook_name); } for(; try > 0; try--) { if (!Tk_GetNumMainWindows()) break; rb_protect(del_root, (VALUE)(ptr->ip), 0); } Tcl_Release(ptr->ip); Tcl_DeleteInterp(ptr->ip); } Tcl_Release((ClientData)ptr->ip); free(ptr); rb_thread_critical = thr_crit_bup; } DUMP1("complete freeing Tcl Interp"); } /* create and initialize interpreter */ static VALUE ip_alloc _((VALUE)); static VALUE ip_alloc(self) VALUE self; { return Data_Wrap_Struct(self, 0, ip_free, 0); } static VALUE ip_init(argc, argv, self) int argc; VALUE *argv; VALUE self; { struct tcltkip *ptr; /* tcltkip data struct */ VALUE argv0, opts; int cnt; int with_tk = 1; Tk_Window mainWin; /* create object */ Data_Get_Struct(self, struct tcltkip, ptr); ptr = ALLOC(struct tcltkip); DATA_PTR(self) = ptr; ptr->return_value = 0; /* from Tk_Main() */ DUMP1("Tcl_CreateInterp"); ptr->ip = Tcl_CreateInterp(); Tcl_Preserve((ClientData)ptr->ip); current_interp = ptr->ip; /* from Tcl_AppInit() */ DUMP1("Tcl_Init"); if (Tcl_Init(ptr->ip) == TCL_ERROR) { #if TCL_MAJOR_VERSION >= 8 rb_raise(rb_eRuntimeError, "%s", Tcl_GetStringResult(ptr->ip)); #else /* TCL_MAJOR_VERSION < 8 */ rb_raise(rb_eRuntimeError, "%s", ptr->ip->result); #endif } /* set variables */ cnt = rb_scan_args(argc, argv, "02", &argv0, &opts); switch(cnt) { case 2: /* options */ if (NIL_P(opts) || opts == Qfalse) { /* without Tk */ with_tk = 0; } else { Tcl_SetVar(ptr->ip, "argv", StringValuePtr(opts), 0); } case 1: /* argv0 */ if (!NIL_P(argv0)) { Tcl_SetVar(ptr->ip, "argv0", StringValuePtr(argv0), 0); } case 0: /* no args */ ; } /* from Tcl_AppInit() */ if (with_tk) { DUMP1("Tk_Init"); if (Tk_Init(ptr->ip) == TCL_ERROR) { #if TCL_MAJOR_VERSION >= 8 rb_raise(rb_eRuntimeError, "%s", Tcl_GetStringResult(ptr->ip)); #else /* TCL_MAJOR_VERSION < 8 */ rb_raise(rb_eRuntimeError, "%s", ptr->ip->result); #endif } DUMP1("Tcl_StaticPackage(\"Tk\")"); #if TCL_MAJOR_VERSION >= 8 Tcl_StaticPackage(ptr->ip, "Tk", Tk_Init, Tk_SafeInit); #else /* TCL_MAJOR_VERSION < 8 */ Tcl_StaticPackage(ptr->ip, "Tk", Tk_Init, (Tcl_PackageInitProc *) NULL); #endif } /* get main window */ mainWin = Tk_MainWindow(ptr->ip); /* add ruby command to the interpreter */ #if TCL_MAJOR_VERSION >= 8 DUMP1("Tcl_CreateObjCommand(\"ruby\")"); Tcl_CreateObjCommand(ptr->ip, "ruby", ip_ruby_eval, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); DUMP1("Tcl_CreateObjCommand(\"ruby_eval\")"); Tcl_CreateObjCommand(ptr->ip, "ruby_eval", ip_ruby_eval, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); DUMP1("Tcl_CreateObjCommand(\"ruby_cmd\")"); Tcl_CreateObjCommand(ptr->ip, "ruby_cmd", ip_ruby_cmd, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); #else /* TCL_MAJOR_VERSION < 8 */ DUMP1("Tcl_CreateCommand(\"ruby\")"); Tcl_CreateCommand(ptr->ip, "ruby", ip_ruby_eval, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); DUMP1("Tcl_CreateCommand(\"ruby_eval\")"); Tcl_CreateCommand(ptr->ip, "ruby_eval", ip_ruby_eval, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); DUMP1("Tcl_CreateCommand(\"ruby_cmd\")"); Tcl_CreateCommand(ptr->ip, "ruby_cmd", ip_ruby_cmd, (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); #endif #if 0 /* Disable the following "update" and "thread_update". Bcause, they don't work in a callback-proc. After calling update in a callback-proc, the callback proc never be worked. If the problem will be fixed in the future, may enable the functions. */ /* replace 'update' command */ # if TCL_MAJOR_VERSION >= 8 DUMP1("Tcl_CreateObjCommand(\"update\")"); Tcl_CreateObjCommand(ptr->ip, "update", ip_rbUpdateObjCmd, (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL); # else /* TCL_MAJOR_VERSION < 8 */ DUMP1("Tcl_CreateCommand(\"update\")"); Tcl_CreateCommand(ptr->ip, "update", ip_rbUpdateCommand, (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL); # endif /* add 'thread_update' command */ # if TCL_MAJOR_VERSION >= 8 DUMP1("Tcl_CreateObjCommand(\"thread_update\")"); Tcl_CreateObjCommand(ptr->ip, "thread_update", ip_rb_threadUpdateObjCmd, (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL); # else /* TCL_MAJOR_VERSION < 8 */ DUMP1("Tcl_CreateCommand(\"thread_update\")"); Tcl_CreateCommand(ptr->ip, "thread_update", ip_rb_threadUpdateCommand, (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL); # endif #endif /* replace 'vwait' command */ #if TCL_MAJOR_VERSION >= 8 DUMP1("Tcl_CreateObjCommand(\"vwait\")"); Tcl_CreateObjCommand(ptr->ip, "vwait", ip_rbVwaitObjCmd, (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL); #else /* TCL_MAJOR_VERSION < 8 */ DUMP1("Tcl_CreateCommand(\"vwait\")"); Tcl_CreateCommand(ptr->ip, "vwait", ip_rbVwaitCommand, (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL); #endif /* replace 'tkwait' command */ #if TCL_MAJOR_VERSION >= 8 DUMP1("Tcl_CreateObjCommand(\"tkwait\")"); Tcl_CreateObjCommand(ptr->ip, "tkwait", ip_rbTkWaitObjCmd, (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL); #else /* TCL_MAJOR_VERSION < 8 */ DUMP1("Tcl_CreateCommand(\"tkwait\")"); Tcl_CreateCommand(ptr->ip, "tkwait", ip_rbTkWaitCommand, (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL); #endif /* add 'thread_vwait' command */ #if TCL_MAJOR_VERSION >= 8 DUMP1("Tcl_CreateObjCommand(\"thread_vwait\")"); Tcl_CreateObjCommand(ptr->ip, "thread_vwait", ip_rb_threadVwaitObjCmd, (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL); #else /* TCL_MAJOR_VERSION < 8 */ DUMP1("Tcl_CreateCommand(\"thread_vwait\")"); Tcl_CreateCommand(ptr->ip, "thread_vwait", ip_rb_threadVwaitCommand, (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL); #endif /* add 'thread_tkwait' command */ #if TCL_MAJOR_VERSION >= 8 DUMP1("Tcl_CreateObjCommand(\"thread_tkwait\")"); Tcl_CreateObjCommand(ptr->ip, "thread_tkwait", ip_rb_threadTkWaitObjCmd, (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL); #else /* TCL_MAJOR_VERSION < 8 */ DUMP1("Tcl_CreateCommand(\"thread_tkwait\")"); Tcl_CreateCommand(ptr->ip, "thread_tkwait", ip_rb_threadTkWaitCommand, (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL); #endif return self; } static VALUE ip_create_slave(argc, argv, self) int argc; VALUE *argv; VALUE self; { struct tcltkip *master = get_ip(self); struct tcltkip *slave = ALLOC(struct tcltkip); VALUE safemode; VALUE name; int safe; int thr_crit_bup; /* safe-mode check */ if (rb_scan_args(argc, argv, "11", &name, &safemode) == 1) { safemode = Qfalse; } if (Tcl_IsSafe(master->ip) == 1) { safe = 1; } else if (safemode == Qfalse || NIL_P(safemode)) { safe = 0; rb_secure(4); } else { safe = 1; } thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; /* create slave-ip */ slave->ip = Tcl_CreateSlave(master->ip, StringValuePtr(name), safe); if (slave->ip == NULL) { rb_raise(rb_eRuntimeError, "fail to create the new slave interpreter"); } Tcl_Preserve((ClientData)slave->ip); slave->return_value = 0; rb_thread_critical = thr_crit_bup; return Data_Wrap_Struct(CLASS_OF(self), 0, ip_free, slave); } /* make ip "safe" */ static VALUE ip_make_safe(self) VALUE self; { struct tcltkip *ptr = get_ip(self); if (Tcl_MakeSafe(ptr->ip) == TCL_ERROR) { #if TCL_MAJOR_VERSION >= 8 rb_raise(rb_eRuntimeError, "%s", Tcl_GetStringResult(ptr->ip)); #else /* TCL_MAJOR_VERSION < 8 */ rb_raise(rb_eRuntimeError, "%s", ptr->ip->result); #endif } return self; } /* is safe? */ static VALUE ip_is_safe_p(self) VALUE self; { struct tcltkip *ptr = get_ip(self); if (Tcl_IsSafe(ptr->ip)) { return Qtrue; } else { return Qfalse; } } /* delete interpreter */ static VALUE ip_delete(self) VALUE self; { struct tcltkip *ptr = get_ip(self); Tcl_DeleteInterp(ptr->ip); return Qnil; } /* is deleted? */ static VALUE ip_is_deleted_p(self) VALUE self; { struct tcltkip *ptr = get_ip(self); if (Tcl_InterpDeleted(ptr->ip)) { return Qtrue; } else { return Qfalse; } } static VALUE #ifdef HAVE_STDARG_PROTOTYPES create_ip_exc(VALUE interp, VALUE exc, const char *fmt, ...) #else create_ip_exc(interp, exc, fmt, va_alist) VALUE interp: VALUE exc; const char *fmt; va_dcl #endif { va_list args; char buf[BUFSIZ]; VALUE einfo; va_init_list(args,fmt); vsnprintf(buf, BUFSIZ, fmt, args); buf[BUFSIZ - 1] = '\0'; va_end(args); einfo = rb_exc_new2(exc, buf); rb_ivar_set(einfo, ID_at_interp, interp); Tcl_ResetResult(get_ip(interp)->ip); return einfo; } static VALUE ip_get_result_string_obj(interp) Tcl_Interp *interp; { #if TCL_MAJOR_VERSION >= 8 int len; char *s; # if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION == 0 s = Tcl_GetStringFromObj(Tcl_GetObjResult(interp), &len); return(rb_tainted_str_new(s, len)); # else /* TCL_VERSION >= 8.1 */ volatile VALUE strval; Tcl_Obj *retobj = Tcl_GetObjResult(interp); int thr_crit_bup; Tcl_IncrRefCount(retobj); thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; if (Tcl_GetCharLength(retobj) != Tcl_UniCharLen(Tcl_GetUnicode(retobj))) { /* possibly binary string */ s = Tcl_GetByteArrayFromObj(retobj, &len); strval = rb_tainted_str_new(s, len); rb_ivar_set(strval, ID_at_enc, rb_str_new2("binary")); } else { /* possibly text string */ s = Tcl_GetStringFromObj(retobj, &len); strval = rb_tainted_str_new(s, len); } rb_thread_critical = thr_crit_bup; Tcl_DecrRefCount(retobj); return(strval); # endif #else /* TCL_MAJOR_VERSION < 8 */ return(rb_tainted_str_new2(interp->result)); #endif } /* eval string in tcl by Tcl_Eval() */ static VALUE ip_eval_real(self, cmd_str, cmd_len) VALUE self; char *cmd_str; int cmd_len; { char *s; int len; struct tcltkip *ptr = get_ip(self); int thr_crit_bup; #if TCL_MAJOR_VERSION >= 8 /* call Tcl_EvalObj() */ { Tcl_Obj *cmd; thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; cmd = Tcl_NewStringObj(cmd_str, cmd_len); Tcl_IncrRefCount(cmd); ptr->return_value = Tcl_EvalObj(ptr->ip, cmd); /* ptr->return_value = Tcl_GlobalEvalObj(ptr->ip, cmd); */ Tcl_DecrRefCount(cmd); rb_thread_critical = thr_crit_bup; } #else /* TCL_MAJOR_VERSION < 8 */ DUMP2("Tcl_Eval(%s)", cmd_str); ptr->return_value = Tcl_Eval(ptr->ip, cmd_str); /* ptr->return_value = Tcl_GlobalEval(ptr->ip, cmd_str); */ #endif if (ptr->return_value == TCL_ERROR) { #if TCL_MAJOR_VERSION >= 8 return create_ip_exc(self, rb_eRuntimeError, "%s", Tcl_GetStringResult(ptr->ip)); #else /* TCL_MAJOR_VERSION < 8 */ return create_ip_exc(self, rb_eRuntimeError, "%s", ptr->ip->result); #endif } DUMP2("(TCL_Eval result) %d", ptr->return_value); /* pass back the result (as string) */ return ip_get_result_string_obj(ptr->ip); } static VALUE evq_safelevel_handler(arg, evq) VALUE arg; VALUE evq; { struct eval_queue *q; Data_Get_Struct(evq, struct eval_queue, q); DUMP2("(safe-level handler) $SAFE = %d", q->safe_level); rb_set_safe_level(q->safe_level); return ip_eval_real(q->interp, q->str, q->len); } int eval_queue_handler _((Tcl_Event *, int)); int eval_queue_handler(evPtr, flags) Tcl_Event *evPtr; int flags; { struct eval_queue *q = (struct eval_queue *)evPtr; volatile VALUE ret; DUMP2("do_eval_queue_handler : evPtr = %p", evPtr); DUMP2("eval queue_thread : %lx", rb_thread_current()); DUMP2("added by thread : %lx", q->thread); if (*(q->done)) { DUMP1("processed by another event-loop"); return 0; } else { DUMP1("process it on current event-loop"); } /* process it */ *(q->done) = 1; /* check safe-level */ if (rb_safe_level() != q->safe_level) { volatile VALUE q_dat; #ifdef HAVE_NATIVETHREAD if (!is_ruby_native_thread()) { rb_bug("cross-thread violation on eval_queue_handler()"); } #endif q_dat = Data_Wrap_Struct(rb_cData,eval_queue_mark,0,q); ret = rb_funcall(rb_proc_new(evq_safelevel_handler, q_dat), ID_call, 0); } else { DUMP2("call eval_real (for caller thread:%lx)", q->thread); DUMP2("call eval_real (current thread:%lx)", rb_thread_current()); ret = ip_eval_real(q->interp, q->str, q->len); } /* set result */ RARRAY(q->result)->ptr[0] = ret; /* complete */ *(q->done) = -1; /* back to caller */ DUMP2("back to caller (caller thread:%lx)", q->thread); DUMP2(" (current thread:%lx)", rb_thread_current()); rb_thread_run(q->thread); DUMP1("finish back to caller"); /* end of handler : remove it */ return 1; } static VALUE ip_eval(self, str) VALUE self; VALUE str; { struct eval_queue *evq; char *eval_str; int *alloc_done; int thr_crit_bup; VALUE current = rb_thread_current(); volatile VALUE result = rb_ary_new2(1); volatile VALUE ret; Tcl_QueuePosition position; thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; StringValue(str); rb_thread_critical = thr_crit_bup; if (eventloop_thread == 0 || current == eventloop_thread) { if (eventloop_thread) { DUMP2("eval from current eventloop %lx", current); } else { DUMP2("eval from thread:%lx but no eventloop", current); } result = ip_eval_real(self, RSTRING(str)->ptr, RSTRING(str)->len); if (rb_obj_is_kind_of(result, rb_eException)) { rb_exc_raise(result); } return result; } DUMP2("eval from thread %lx (NOT current eventloop)", current); thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; /* allocate memory (protected from Tcl_ServiceEvent) */ alloc_done = (int*)ALLOC(int); *alloc_done = 0; eval_str = ALLOC_N(char, RSTRING(str)->len + 1); strncpy(eval_str, RSTRING(str)->ptr, RSTRING(str)->len); eval_str[RSTRING(str)->len] = 0; /* allocate memory (freed by Tcl_ServiceEvent) */ evq = (struct eval_queue *)Tcl_Alloc(sizeof(struct eval_queue)); /* construct event data */ evq->done = alloc_done; evq->str = eval_str; evq->len = RSTRING(str)->len; evq->interp = self; evq->result = result; evq->thread = current; evq->safe_level = rb_safe_level(); evq->ev.proc = eval_queue_handler; position = TCL_QUEUE_TAIL; /* add the handler to Tcl event queue */ DUMP1("add handler"); Tcl_QueueEvent(&(evq->ev), position); rb_thread_critical = thr_crit_bup; /* wait for the handler to be processed */ DUMP2("wait for handler (current thread:%lx)", current); while(*alloc_done >= 0) { rb_thread_stop(); } DUMP2("back from handler (current thread:%lx)", current); /* get result & free allocated memory */ ret = RARRAY(result)->ptr[0]; free(alloc_done); free(eval_str); if (rb_obj_is_kind_of(ret, rb_eException)) { rb_exc_raise(ret); } return ret; } /* restart Tk */ static VALUE lib_restart(self) VALUE self; { struct tcltkip *ptr = get_ip(self); int thr_crit_bup; rb_secure(4); thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; /* destroy the root wdiget */ ptr->return_value = Tcl_Eval(ptr->ip, "destroy ."); /* ignore ERROR */ DUMP2("(TCL_Eval result) %d", ptr->return_value); Tcl_ResetResult(ptr->ip); /* delete namespace ( tested on tk8.4.5 ) */ ptr->return_value = Tcl_Eval(ptr->ip, "namespace delete ::tk::msgcat"); /* ignore ERROR */ DUMP2("(TCL_Eval result) %d", ptr->return_value); Tcl_ResetResult(ptr->ip); /* delete trace proc ( tested on tk8.4.5 ) */ ptr->return_value = Tcl_Eval(ptr->ip, "trace vdelete ::tk_strictMotif w ::tk::EventMotifBindings"); /* ignore ERROR */ DUMP2("(TCL_Eval result) %d", ptr->return_value); Tcl_ResetResult(ptr->ip); /* execute Tk_Init of Tk_SafeInit */ #if TCL_MAJOR_VERSION >= 8 if (Tcl_IsSafe(ptr->ip)) { DUMP1("Tk_SafeInit"); if (Tk_SafeInit(ptr->ip) == TCL_ERROR) { rb_thread_critical = thr_crit_bup; /* rb_raise(rb_eRuntimeError, "%s", ptr->ip->result); */ rb_raise(rb_eRuntimeError, "%s", Tcl_GetStringResult(ptr->ip)); } } else { DUMP1("Tk_Init"); if (Tk_Init(ptr->ip) == TCL_ERROR) { rb_thread_critical = thr_crit_bup; /* rb_raise(rb_eRuntimeError, "%s", ptr->ip->result); */ rb_raise(rb_eRuntimeError, "%s", Tcl_GetStringResult(ptr->ip)); } } #else /* TCL_MAJOR_VERSION < 8 */ DUMP1("Tk_Init"); if (Tk_Init(ptr->ip) == TCL_ERROR) { rb_raise(rb_eRuntimeError, "%s", ptr->ip->result); } #endif rb_thread_critical = thr_crit_bup; return Qnil; } static VALUE ip_restart(self) VALUE self; { struct tcltkip *ptr = get_ip(self); rb_secure(4); if (Tcl_GetMaster(ptr->ip) != (Tcl_Interp*)NULL) { /* slave IP */ return Qnil; } return lib_restart(self); } static VALUE lib_toUTF8_core(ip_obj, src, encodename) VALUE ip_obj; VALUE src; VALUE encodename; { volatile VALUE str = src; #ifdef TCL_UTF_MAX Tcl_Interp *interp; Tcl_Encoding encoding; Tcl_DString dstr; int taint_flag = OBJ_TAINTED(str); struct tcltkip *ptr; char *buf; int thr_crit_bup; if (NIL_P(ip_obj)) { interp = (Tcl_Interp *)NULL; } else { interp = get_ip(ip_obj)->ip; } thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; if (NIL_P(encodename)) { if (TYPE(str) == T_STRING) { volatile VALUE enc; enc = Qnil; if (RTEST(rb_ivar_defined(str, ID_at_enc))) { enc = rb_ivar_get(str, ID_at_enc); } if (NIL_P(enc)) { if (NIL_P(ip_obj)) { encoding = (Tcl_Encoding)NULL; } else { if (RTEST(rb_ivar_defined(ip_obj, ID_at_enc))) { enc = rb_ivar_get(ip_obj, ID_at_enc); } if (NIL_P(enc)) { encoding = (Tcl_Encoding)NULL; } else { StringValue(enc); encoding = Tcl_GetEncoding(interp, RSTRING(enc)->ptr); if (encoding == (Tcl_Encoding)NULL) { rb_warning("Tk-interp has unknown encoding information (@encoding:'%s')", RSTRING(enc)->ptr); } } } } else { StringValue(enc); if (strcmp(RSTRING(enc)->ptr, "binary") == 0) { rb_thread_critical = thr_crit_bup; return str; } encoding = Tcl_GetEncoding(interp, RSTRING(enc)->ptr); if (encoding == (Tcl_Encoding)NULL) { rb_warning("string has unknown encoding information (@encoding:'%s')", RSTRING(enc)->ptr); } } } else { encoding = (Tcl_Encoding)NULL; } } else { StringValue(encodename); encoding = Tcl_GetEncoding(interp, RSTRING(encodename)->ptr); if (encoding == (Tcl_Encoding)NULL) { /* rb_warning("unknown encoding name '%s'", RSTRING(encodename)->ptr); */ rb_raise(rb_eArgError, "unknown encoding name '%s'", RSTRING(encodename)->ptr); } } StringValue(str); if (!RSTRING(str)->len) { rb_thread_critical = thr_crit_bup; return str; } buf = ALLOC_N(char,(RSTRING(str)->len)+1); strncpy(buf, RSTRING(str)->ptr, RSTRING(str)->len); buf[RSTRING(str)->len] = 0; Tcl_DStringInit(&dstr); Tcl_DStringFree(&dstr); /* Tcl_ExternalToUtfDString(encoding,buf,strlen(buf),&dstr); */ Tcl_ExternalToUtfDString(encoding, buf, RSTRING(str)->len, &dstr); /* str = rb_tainted_str_new2(Tcl_DStringValue(&dstr)); */ str = rb_str_new2(Tcl_DStringValue(&dstr)); rb_ivar_set(str, ID_at_enc, rb_tainted_str_new2("utf-8")); if (taint_flag) OBJ_TAINT(str); if (encoding != (Tcl_Encoding)NULL) { Tcl_FreeEncoding(encoding); } Tcl_DStringFree(&dstr); free(buf); rb_thread_critical = thr_crit_bup; #endif return str; } static VALUE lib_toUTF8(argc, argv, self) int argc; VALUE *argv; VALUE self; { VALUE str, encodename; if (rb_scan_args(argc, argv, "11", &str, &encodename) == 1) { encodename = Qnil; } return lib_toUTF8_core(Qnil, str, encodename); } static VALUE ip_toUTF8(argc, argv, self) int argc; VALUE *argv; VALUE self; { VALUE str, encodename; if (rb_scan_args(argc, argv, "11", &str, &encodename) == 1) { encodename = Qnil; } return lib_toUTF8_core(self, str, encodename); } static VALUE lib_fromUTF8_core(ip_obj, src, encodename) VALUE ip_obj; VALUE src; VALUE encodename; { volatile VALUE str = src; #ifdef TCL_UTF_MAX Tcl_Interp *interp; Tcl_Encoding encoding; Tcl_DString dstr; int taint_flag = OBJ_TAINTED(str); char *buf; int thr_crit_bup; if (NIL_P(ip_obj)) { interp = (Tcl_Interp *)NULL; } else { interp = get_ip(ip_obj)->ip; } thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; if (NIL_P(encodename)) { volatile VALUE enc; if (TYPE(str) == T_STRING) { enc = Qnil; if (RTEST(rb_ivar_defined(str, ID_at_enc))) { enc = rb_ivar_get(str, ID_at_enc); } if (!NIL_P(enc) && strcmp(StringValuePtr(enc), "binary") == 0) { rb_thread_critical = thr_crit_bup; return str; } } if (NIL_P(ip_obj)) { encoding = (Tcl_Encoding)NULL; } else { enc = Qnil; if (RTEST(rb_ivar_defined(ip_obj, ID_at_enc))) { enc = rb_ivar_get(ip_obj, ID_at_enc); } if (NIL_P(enc)) { encoding = (Tcl_Encoding)NULL; } else { StringValue(enc); encoding = Tcl_GetEncoding(interp, RSTRING(enc)->ptr); if (encoding == (Tcl_Encoding)NULL) { rb_warning("Tk-interp has unknown encoding information (@encoding:'%s')", RSTRING(enc)->ptr); } else { encodename = rb_obj_dup(enc); } } } } else { StringValue(encodename); if (strcmp(RSTRING(encodename)->ptr, "binary") == 0) { char *s; int len; s = Tcl_GetByteArrayFromObj(Tcl_NewStringObj(RSTRING(str)->ptr, RSTRING(str)->len), &len); str = rb_tainted_str_new(s, len); rb_ivar_set(str, ID_at_enc, rb_tainted_str_new2("binary")); rb_thread_critical = thr_crit_bup; return str; } encoding = Tcl_GetEncoding(interp, RSTRING(encodename)->ptr); if (encoding == (Tcl_Encoding)NULL) { /* rb_warning("unknown encoding name '%s'", RSTRING(encodename)->ptr); encodename = Qnil; */ rb_raise(rb_eArgError, "unknown encoding name '%s'", RSTRING(encodename)->ptr); } } StringValue(str); if (RSTRING(str)->len == 0) { rb_thread_critical = thr_crit_bup; return rb_tainted_str_new2(""); } buf = ALLOC_N(char,strlen(RSTRING(str)->ptr)+1); strncpy(buf, RSTRING(str)->ptr, RSTRING(str)->len); buf[RSTRING(str)->len] = 0; Tcl_DStringInit(&dstr); Tcl_DStringFree(&dstr); /* Tcl_UtfToExternalDString(encoding,buf,strlen(buf),&dstr); */ Tcl_UtfToExternalDString(encoding,buf,RSTRING(str)->len,&dstr); /* str = rb_tainted_str_new2(Tcl_DStringValue(&dstr)); */ str = rb_str_new2(Tcl_DStringValue(&dstr)); rb_ivar_set(str, ID_at_enc, encodename); if (taint_flag) OBJ_TAINT(str); if (encoding != (Tcl_Encoding)NULL) { Tcl_FreeEncoding(encoding); } Tcl_DStringFree(&dstr); free(buf); rb_thread_critical = thr_crit_bup; #endif return str; } static VALUE lib_fromUTF8(argc, argv, self) int argc; VALUE *argv; VALUE self; { VALUE str, encodename; if (rb_scan_args(argc, argv, "11", &str, &encodename) == 1) { encodename = Qnil; } return lib_fromUTF8_core(Qnil, str, encodename); } static VALUE ip_fromUTF8(argc, argv, self) int argc; VALUE *argv; VALUE self; { VALUE str, encodename; if (rb_scan_args(argc, argv, "11", &str, &encodename) == 1) { encodename = Qnil; } return lib_fromUTF8_core(self, str, encodename); } static VALUE lib_UTF_backslash_core(self, str, all_bs) VALUE self; VALUE str; int all_bs; { #ifdef TCL_UTF_MAX char *src_buf, *dst_buf, *ptr; int read_len = 0, dst_len = 0; int taint_flag = OBJ_TAINTED(str); int thr_crit_bup; StringValue(str); if (!RSTRING(str)->len) { return str; } thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; src_buf = ALLOC_N(char,(RSTRING(str)->len)+1); strncpy(src_buf, RSTRING(str)->ptr, RSTRING(str)->len); src_buf[RSTRING(str)->len] = 0; dst_buf = ALLOC_N(char,(RSTRING(str)->len)+1); ptr = src_buf; while(RSTRING(str)->len > ptr - src_buf) { if (*ptr == '\\' && (all_bs || *(ptr + 1) == 'u')) { dst_len += Tcl_UtfBackslash(ptr, &read_len, (dst_buf + dst_len)); ptr += read_len; } else { *(dst_buf + (dst_len++)) = *(ptr++); } } str = rb_str_new(dst_buf, dst_len); if (taint_flag) OBJ_TAINT(str); free(src_buf); free(dst_buf); rb_thread_critical = thr_crit_bup; #endif return str; } static VALUE lib_UTF_backslash(self, str) VALUE self; VALUE str; { return lib_UTF_backslash_core(self, str, 0); } static VALUE lib_Tcl_backslash(self, str) VALUE self; VALUE str; { return lib_UTF_backslash_core(self, str, 1); } #if TCL_MAJOR_VERSION >= 8 static VALUE ip_invoke_core(interp, objc, objv) VALUE interp; int objc; Tcl_Obj **objv; #else static VALUE ip_invoke_core(interp, argc, argv) VALUE interp; int argc; char **argv; #endif { struct tcltkip *ptr; int i; Tcl_CmdInfo info; char *cmd; char *s; int len; int thr_crit_bup; #if TCL_MAJOR_VERSION >= 8 int argc = objc; char **argv = (char **)NULL; Tcl_Obj *resultPtr; #endif /* get the data struct */ ptr = get_ip(interp); /* ip is deleted? */ if (Tcl_InterpDeleted(ptr->ip)) { DUMP1("ip is deleted"); return rb_tainted_str_new2(""); } /* get the command name string */ #if TCL_MAJOR_VERSION >= 8 cmd = Tcl_GetStringFromObj(objv[0], &len); #else /* TCL_MAJOR_VERSION < 8 */ cmd = argv[0]; #endif /* map from the command name to a C procedure */ DUMP2("call Tcl_GetCommandInfo, %s", cmd); if (!Tcl_GetCommandInfo(ptr->ip, cmd, &info)) { DUMP1("error Tcl_GetCommandInfo"); /* if (event_loop_abort_on_exc || cmd[0] != '.') { */ if (event_loop_abort_on_exc > 0) { /*rb_ip_raise(obj,rb_eNameError,"invalid command name `%s'",cmd);*/ return create_ip_exc(interp, rb_eNameError, "invalid command name `%s'", cmd); } else { if (event_loop_abort_on_exc < 0) { rb_warning("invalid command name `%s' (ignore)", cmd); } else { rb_warn("invalid command name `%s' (ignore)", cmd); } Tcl_ResetResult(ptr->ip); return rb_tainted_str_new2(""); } } DUMP1("end Tcl_GetCommandInfo"); thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; /* memory allocation for arguments of this command */ #if TCL_MAJOR_VERSION >= 8 if (!info.isNativeObjectProc) { /* string interface */ argv = (char **)ALLOC_N(char *, argc+1); for (i = 0; i < argc; ++i) { argv[i] = Tcl_GetStringFromObj(objv[i], &len); } argv[argc] = (char *)NULL; } #endif Tcl_ResetResult(ptr->ip); /* Invoke the C procedure */ #if TCL_MAJOR_VERSION >= 8 if (info.isNativeObjectProc) { ptr->return_value = (*info.objProc)(info.objClientData, ptr->ip, objc, objv); #if 0 /* get the string value from the result object */ resultPtr = Tcl_GetObjResult(ptr->ip); Tcl_SetResult(ptr->ip, Tcl_GetStringFromObj(resultPtr, &len), TCL_VOLATILE); #endif } else #endif { #if TCL_MAJOR_VERSION >= 8 ptr->return_value = (*info.proc)(info.clientData, ptr->ip, argc, (CONST84 char **)argv); free(argv); #else /* TCL_MAJOR_VERSION < 8 */ ptr->return_value = (*info.proc)(info.clientData, ptr->ip, argc, argv); #endif } rb_thread_critical = thr_crit_bup; /* exception on mainloop */ if (ptr->return_value == TCL_ERROR) { if (event_loop_abort_on_exc > 0 && !Tcl_InterpDeleted(ptr->ip)) { #if TCL_MAJOR_VERSION >= 8 return create_ip_exc(interp, rb_eRuntimeError, "%s", Tcl_GetStringResult(ptr->ip)); #else /* TCL_MAJOR_VERSION < 8 */ return create_ip_exc(interp, rb_eRuntimeError, "%s", ptr->ip->result); #endif } else { if (event_loop_abort_on_exc < 0) { #if TCL_MAJOR_VERSION >= 8 rb_warning("%s (ignore)", Tcl_GetStringResult(ptr->ip)); #else /* TCL_MAJOR_VERSION < 8 */ rb_warning("%s (ignore)", ptr->ip->result); #endif } else { #if TCL_MAJOR_VERSION >= 8 rb_warn("%s (ignore)", Tcl_GetStringResult(ptr->ip)); #else /* TCL_MAJOR_VERSION < 8 */ rb_warn("%s (ignore)", ptr->ip->result); #endif } Tcl_ResetResult(ptr->ip); return rb_tainted_str_new2(""); } } /* pass back the result (as string) */ return ip_get_result_string_obj(ptr->ip); } #if TCL_MAJOR_VERSION >= 8 static Tcl_Obj ** #else /* TCL_MAJOR_VERSION < 8 */ static char ** #endif alloc_invoke_arguments(argc, argv) int argc; VALUE *argv; { int i; VALUE v; char *s; int thr_crit_bup; #if TCL_MAJOR_VERSION >= 8 Tcl_Obj **av = (Tcl_Obj **)NULL; Tcl_Obj *resultPtr; #else /* TCL_MAJOR_VERSION < 8 */ char **av = (char **)NULL; #endif thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; /* memory allocation */ #if TCL_MAJOR_VERSION >= 8 av = (Tcl_Obj **)ALLOC_N(Tcl_Obj *, argc+1); for (i = 0; i < argc; ++i) { VALUE enc; v = argv[i]; s = StringValuePtr(v); # if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION == 0 av[i] = Tcl_NewStringObj(s, RSTRING(v)->len); # else /* TCL_VERSION >= 8.1 */ enc = Qnil; if (RTEST(rb_ivar_defined(v, ID_at_enc))) { enc = rb_ivar_get(v, ID_at_enc); } if (!NIL_P(enc) && strcmp(StringValuePtr(enc), "binary") == 0) { /* binary string */ av[i] = Tcl_NewByteArrayObj(s, RSTRING(v)->len); } else if (strlen(s) != RSTRING(v)->len) { /* probably binary string */ av[i] = Tcl_NewByteArrayObj(s, RSTRING(v)->len); } else { /* probably text string */ av[i] = Tcl_NewStringObj(s, RSTRING(v)->len); } # endif Tcl_IncrRefCount(av[i]); } av[argc] = (Tcl_Obj *)NULL; #else /* TCL_MAJOR_VERSION < 8 */ /* string interface */ av = (char **)ALLOC_N(char *, argc+1); for (i = 0; i < argc; ++i) { v = argv[i]; s = StringValuePtr(v); av[i] = ALLOC_N(char, strlen(s)+1); strcpy(av[i], s); } av[argc] = (char *)NULL; #endif rb_thread_critical = thr_crit_bup; return av; } static void free_invoke_arguments(argc, av) int argc; #if TCL_MAJOR_VERSION >= 8 Tcl_Obj **av; #else /* TCL_MAJOR_VERSION < 8 */ char **av; #endif { int i; for (i = 0; i < argc; ++i) { #if TCL_MAJOR_VERSION >= 8 Tcl_DecrRefCount(av[i]); #else /* TCL_MAJOR_VERSION < 8 */ free(av[i]); #endif } free(av); } static VALUE ip_invoke_real(argc, argv, interp) int argc; VALUE *argv; VALUE interp; { VALUE v; struct tcltkip *ptr; /* tcltkip data struct */ int i; Tcl_CmdInfo info; char *s; int len; int thr_crit_bup; #if TCL_MAJOR_VERSION >= 8 Tcl_Obj **av = (Tcl_Obj **)NULL; Tcl_Obj *resultPtr; #else /* TCL_MAJOR_VERSION < 8 */ char **av = (char **)NULL; #endif DUMP2("invoke_real called by thread:%lx", rb_thread_current()); /* get the data struct */ ptr = get_ip(interp); /* ip is deleted? */ if (Tcl_InterpDeleted(ptr->ip)) { DUMP1("ip is deleted"); return rb_tainted_str_new2(""); } /* allocate memory for arguments */ av = alloc_invoke_arguments(argc, argv); /* Invoke the C procedure */ Tcl_ResetResult(ptr->ip); v = ip_invoke_core(interp, argc, av); /* free allocated memory */ free_invoke_arguments(argc, av); return v; } VALUE ivq_safelevel_handler(arg, ivq) VALUE arg; VALUE ivq; { struct invoke_queue *q; Data_Get_Struct(ivq, struct invoke_queue, q); DUMP2("(safe-level handler) $SAFE = %d", q->safe_level); rb_set_safe_level(q->safe_level); return ip_invoke_core(q->interp, q->argc, q->argv); } int invoke_queue_handler _((Tcl_Event *, int)); int invoke_queue_handler(evPtr, flags) Tcl_Event *evPtr; int flags; { struct invoke_queue *q = (struct invoke_queue *)evPtr; volatile VALUE ret; DUMP2("do_invoke_queue_handler : evPtr = %p", evPtr); DUMP2("invoke queue_thread : %lx", rb_thread_current()); DUMP2("added by thread : %lx", q->thread); if (*(q->done)) { DUMP1("processed by another event-loop"); return 0; } else { DUMP1("process it on current event-loop"); } /* process it */ *(q->done) = 1; /* check safe-level */ if (rb_safe_level() != q->safe_level) { volatile VALUE q_dat; q_dat = Data_Wrap_Struct(rb_cData,invoke_queue_mark,0,q); ret = rb_funcall(rb_proc_new(evq_safelevel_handler, q_dat), ID_call, 0); } else { DUMP2("call invoke_real (for caller thread:%lx)", q->thread); DUMP2("call invoke_real (current thread:%lx)", rb_thread_current()); ret = ip_invoke_core(q->interp, q->argc, q->argv); } /* set result */ RARRAY(q->result)->ptr[0] = ret; /* complete */ *(q->done) = -1; /* back to caller */ DUMP2("back to caller (caller thread:%lx)", q->thread); DUMP2(" (current thread:%lx)", rb_thread_current()); rb_thread_run(q->thread); DUMP1("finish back to caller"); /* end of handler : remove it */ return 1; } static VALUE ip_invoke_with_position(argc, argv, obj, position) int argc; VALUE *argv; VALUE obj; Tcl_QueuePosition position; { struct invoke_queue *ivq; char *s; int len; int i; int *alloc_done; int thr_crit_bup; VALUE v; VALUE current = rb_thread_current(); volatile VALUE result = rb_ary_new2(1); volatile VALUE ret; #if TCL_MAJOR_VERSION >= 8 Tcl_Obj **av = (Tcl_Obj **)NULL; #else /* TCL_MAJOR_VERSION < 8 */ char **av = (char **)NULL; #endif if (argc < 1) { rb_raise(rb_eArgError, "command name missing"); } if (eventloop_thread == 0 || current == eventloop_thread) { if (eventloop_thread) { DUMP2("invoke from current eventloop %lx", current); } else { DUMP2("invoke from thread:%lx but no eventloop", current); } result = ip_invoke_real(argc, argv, obj); if (rb_obj_is_kind_of(result, rb_eException)) { rb_exc_raise(result); } return result; } DUMP2("invoke from thread %lx (NOT current eventloop)", current); thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; /* allocate memory (for arguments) */ av = alloc_invoke_arguments(argc, argv); /* allocate memory (keep result) */ alloc_done = (int*)ALLOC(int); *alloc_done = 0; /* allocate memory (freed by Tcl_ServiceEvent) */ ivq = (struct invoke_queue *)Tcl_Alloc(sizeof(struct invoke_queue)); /* construct event data */ ivq->done = alloc_done; ivq->argc = argc; ivq->argv = av; ivq->interp = obj; ivq->result = result; ivq->thread = current; ivq->safe_level = rb_safe_level(); ivq->ev.proc = invoke_queue_handler; /* add the handler to Tcl event queue */ DUMP1("add handler"); Tcl_QueueEvent(&(ivq->ev), position); rb_thread_critical = thr_crit_bup; /* wait for the handler to be processed */ DUMP2("wait for handler (current thread:%lx)", current); while(*alloc_done >= 0) { rb_thread_stop(); } DUMP2("back from handler (current thread:%lx)", current); /* get result & free allocated memory */ ret = RARRAY(result)->ptr[0]; free(alloc_done); /* free allocated memory */ free_invoke_arguments(argc, av); /* exception? */ if (rb_obj_is_kind_of(ret, rb_eException)) { DUMP1("raise exception"); rb_exc_raise(ret); } DUMP1("exit ip_invoke"); return ret; } /* get return code from Tcl_Eval() */ static VALUE ip_retval(self) VALUE self; { struct tcltkip *ptr; /* tcltkip data struct */ /* get the data strcut */ ptr = get_ip(self); return (INT2FIX(ptr->return_value)); } static VALUE ip_invoke(argc, argv, obj) int argc; VALUE *argv; VALUE obj; { return ip_invoke_with_position(argc, argv, obj, TCL_QUEUE_TAIL); } static VALUE ip_invoke_immediate(argc, argv, obj) int argc; VALUE *argv; VALUE obj; { return ip_invoke_with_position(argc, argv, obj, TCL_QUEUE_HEAD); } /* access Tcl variables */ static VALUE ip_get_variable(self, varname_arg, flag_arg) VALUE self; VALUE varname_arg; VALUE flag_arg; { struct tcltkip *ptr = get_ip(self); int thr_crit_bup; volatile VALUE varname, flag; varname = varname_arg; flag = flag_arg; StringValue(varname); #if TCL_MAJOR_VERSION >= 8 { Tcl_Obj *nameobj, *ret; char *s; int len; VALUE strval; thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; nameobj = Tcl_NewStringObj(RSTRING(varname)->ptr, RSTRING(varname)->len); Tcl_IncrRefCount(nameobj); ret = Tcl_ObjGetVar2(ptr->ip, nameobj, (Tcl_Obj*)NULL, FIX2INT(flag)); Tcl_DecrRefCount(nameobj); rb_thread_critical = thr_crit_bup; if (ret == (Tcl_Obj*)NULL) { #if TCL_MAJOR_VERSION >= 8 rb_raise(rb_eRuntimeError, "%s", Tcl_GetStringResult(ptr->ip)); #else /* TCL_MAJOR_VERSION < 8 */ rb_raise(rb_eRuntimeError, "%s", ptr->ip->result); #endif } Tcl_IncrRefCount(ret); # if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION == 0 s = Tcl_GetStringFromObj(ret, &len); strval = rb_tainted_str_new(s, len); Tcl_DecrRefCount(ret); return(strval); # else /* TCL_VERSION >= 8.1 */ { thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; if (Tcl_GetCharLength(ret) != Tcl_UniCharLen(Tcl_GetUnicode(ret))) { /* possibly binary string */ s = Tcl_GetByteArrayFromObj(ret, &len); strval = rb_tainted_str_new(s, len); rb_ivar_set(strval, ID_at_enc, rb_tainted_str_new2("binary")); } else { /* possibly text string */ s = Tcl_GetStringFromObj(ret, &len); strval = rb_tainted_str_new(s, len); } rb_thread_critical = thr_crit_bup; } Tcl_DecrRefCount(ret); return(strval); # endif } #else /* TCL_MAJOR_VERSION < 8 */ { char *ret; ret = Tcl_GetVar2(ptr->ip, RSTRING(varname)->ptr, (char*)NULL, FIX2INT(flag)); if (ret == (char*)NULL) { rb_raise(rb_eRuntimeError, "%s", ptr->ip->result); } return(rb_tainted_str_new2(ret)); } #endif } static VALUE ip_get_variable2(self, varname_arg, index_arg, flag_arg) VALUE self; VALUE varname_arg; VALUE index_arg; VALUE flag_arg; { struct tcltkip *ptr = get_ip(self); int thr_crit_bup; volatile VALUE varname, index, flag; if (NIL_P(index_arg)) { return ip_get_variable(self, varname_arg, flag_arg); } varname = varname_arg; index = index_arg; flag = flag_arg; StringValue(varname); StringValue(index); #if TCL_MAJOR_VERSION >= 8 { Tcl_Obj *nameobj, *idxobj, *ret; char *s; int len; volatile VALUE strval; thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; nameobj = Tcl_NewStringObj(RSTRING(varname)->ptr, RSTRING(varname)->len); Tcl_IncrRefCount(nameobj); idxobj = Tcl_NewStringObj(RSTRING(index)->ptr, RSTRING(index)->len); Tcl_IncrRefCount(idxobj); ret = Tcl_ObjGetVar2(ptr->ip, nameobj, idxobj, FIX2INT(flag)); Tcl_DecrRefCount(nameobj); Tcl_DecrRefCount(idxobj); rb_thread_critical = thr_crit_bup; if (ret == (Tcl_Obj*)NULL) { #if TCL_MAJOR_VERSION >= 8 rb_raise(rb_eRuntimeError, "%s", Tcl_GetStringResult(ptr->ip)); #else /* TCL_MAJOR_VERSION < 8 */ rb_raise(rb_eRuntimeError, "%s", ptr->ip->result); #endif } Tcl_IncrRefCount(ret); # if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION == 0 s = Tcl_GetStringFromObj(ret, &len); strval = rb_tainted_str_new(s, len); Tcl_DecrRefCount(ret); return(strval); # else /* TCL_VERSION >= 8.1 */ { thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; if (Tcl_GetCharLength(ret) != Tcl_UniCharLen(Tcl_GetUnicode(ret))) { /* possibly binary string */ s = Tcl_GetByteArrayFromObj(ret, &len); strval = rb_tainted_str_new(s, len); rb_ivar_set(strval, ID_at_enc, rb_tainted_str_new2("binary")); } else { /* possibly text string */ s = Tcl_GetStringFromObj(ret, &len); strval = rb_tainted_str_new(s, len); } rb_thread_critical = thr_crit_bup; } Tcl_DecrRefCount(ret); return(strval); # endif } #else /* TCL_MAJOR_VERSION < 8 */ { char *ret; ret = Tcl_GetVar2(ptr->ip, RSTRING(varname)->ptr, RSTRING(index)->ptr, FIX2INT(flag)); if (ret == (char*)NULL) { rb_raise(rb_eRuntimeError, "%s", ptr->ip->result); } return(rb_tainted_str_new2(ret)); } #endif } static VALUE ip_set_variable(self, varname_arg, value_arg, flag_arg) VALUE self; VALUE varname_arg; VALUE value_arg; VALUE flag_arg; { struct tcltkip *ptr = get_ip(self); int thr_crit_bup; volatile VALUE varname, value, flag; varname = varname_arg; value = value_arg; flag = flag_arg; StringValue(varname); StringValue(value); #if TCL_MAJOR_VERSION >= 8 { Tcl_Obj *nameobj, *valobj, *ret; char *s; int len; volatile VALUE strval; thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; nameobj = Tcl_NewStringObj(RSTRING(varname)->ptr, RSTRING(varname)->len); Tcl_IncrRefCount(nameobj); # if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION == 0 valobj = Tcl_NewStringObj(RSTRING(value)->ptr, RSTRING(value)->len); Tcl_IncrRefCount(valobj); # else /* TCL_VERSION >= 8.1 */ { VALUE enc = Qnil; if (RTEST(rb_ivar_defined(value, ID_at_enc))) { enc = rb_ivar_get(value, ID_at_enc); } if (!NIL_P(enc) && strcmp(StringValuePtr(enc), "binary") == 0) { /* binary string */ valobj = Tcl_NewByteArrayObj(RSTRING(value)->ptr, RSTRING(value)->len); } else if (strlen(RSTRING(value)->ptr) != RSTRING(value)->len) { /* probably binary string */ valobj = Tcl_NewByteArrayObj(RSTRING(value)->ptr, RSTRING(value)->len); } else { /* probably text string */ valobj = Tcl_NewStringObj(RSTRING(value)->ptr, RSTRING(value)->len); } Tcl_IncrRefCount(valobj); } # endif ret = Tcl_ObjSetVar2(ptr->ip, nameobj, (Tcl_Obj*)NULL, valobj, FIX2INT(flag)); Tcl_DecrRefCount(nameobj); Tcl_DecrRefCount(valobj); if (ret == (Tcl_Obj*)NULL) { #if TCL_MAJOR_VERSION >= 8 rb_raise(rb_eRuntimeError, "%s", Tcl_GetStringResult(ptr->ip)); #else /* TCL_MAJOR_VERSION < 8 */ rb_raise(rb_eRuntimeError, "%s", ptr->ip->result); #endif } Tcl_IncrRefCount(ret); # if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION == 0 s = Tcl_GetStringFromObj(ret, &len); strval = rb_tainted_str_new(s, len); # else /* TCL_VERSION >= 8.1 */ if (Tcl_GetCharLength(ret) != Tcl_UniCharLen(Tcl_GetUnicode(ret))) { /* possibly binary string */ s = Tcl_GetByteArrayFromObj(ret, &len); strval = rb_tainted_str_new(s, len); rb_ivar_set(strval, ID_at_enc, rb_str_new2("binary")); } else { /* possibly text string */ s = Tcl_GetStringFromObj(ret, &len); strval = rb_tainted_str_new(s, len); } # endif rb_thread_critical = thr_crit_bup; Tcl_DecrRefCount(ret); return(strval); } #else /* TCL_MAJOR_VERSION < 8 */ { CONST char *ret; ret = Tcl_SetVar2(ptr->ip, RSTRING(varname)->ptr, (char*)NULL, RSTRING(value)->ptr, (int)FIX2INT(flag)); if (ret == NULL) { rb_raise(rb_eRuntimeError, "%s", ptr->ip->result); } return(rb_tainted_str_new2(ret)); } #endif } static VALUE ip_set_variable2(self, varname_arg, index_arg, value_arg, flag_arg) VALUE self; VALUE varname_arg; VALUE index_arg; VALUE value_arg; VALUE flag_arg; { struct tcltkip *ptr = get_ip(self); int thr_crit_bup; volatile VALUE varname, index, value, flag; if (NIL_P(index_arg)) { return ip_set_variable(self, varname_arg, value_arg, flag_arg); } varname = varname_arg; index = index_arg; value = value_arg; flag = flag_arg; StringValue(varname); StringValue(index); StringValue(value); #if TCL_MAJOR_VERSION >= 8 { Tcl_Obj *nameobj, *idxobj, *valobj, *ret; char *s; int len; volatile VALUE strval; thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; nameobj = Tcl_NewStringObj(RSTRING(varname)->ptr, RSTRING(varname)->len); Tcl_IncrRefCount(nameobj); idxobj = Tcl_NewStringObj(RSTRING(index)->ptr, RSTRING(index)->len); Tcl_IncrRefCount(idxobj); # if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION == 0 valobj = Tcl_NewStringObj(RSTRING(value)->ptr, RSTRING(value)->len); # else /* TCL_VERSION >= 8.1 */ { VALUE enc = Qnil; if (RTEST(rb_ivar_defined(value, ID_at_enc))) { enc = rb_ivar_get(value, ID_at_enc); } if (!NIL_P(enc) && strcmp(StringValuePtr(enc), "binary") == 0) { /* binary string */ valobj = Tcl_NewByteArrayObj(RSTRING(value)->ptr, RSTRING(value)->len); } else if (strlen(RSTRING(value)->ptr) != RSTRING(value)->len) { /* probably binary string */ valobj = Tcl_NewByteArrayObj(RSTRING(value)->ptr, RSTRING(value)->len); } else { /* probably text string */ valobj = Tcl_NewStringObj(RSTRING(value)->ptr, RSTRING(value)->len); } } # endif Tcl_IncrRefCount(valobj); ret = Tcl_ObjSetVar2(ptr->ip, nameobj, idxobj, valobj, FIX2INT(flag)); Tcl_DecrRefCount(nameobj); Tcl_DecrRefCount(idxobj); Tcl_DecrRefCount(valobj); rb_thread_critical = thr_crit_bup; if (ret == (Tcl_Obj*)NULL) { #if TCL_MAJOR_VERSION >= 8 rb_raise(rb_eRuntimeError, "%s", Tcl_GetStringResult(ptr->ip)); #else /* TCL_MAJOR_VERSION < 8 */ rb_raise(rb_eRuntimeError, "%s", ptr->ip->result); #endif } Tcl_IncrRefCount(ret); thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; # if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION == 0 s = Tcl_GetStringFromObj(ret, &len); strval = rb_tainted_str_new(s, len); # else /* TCL_VERSION >= 8.1 */ if (Tcl_GetCharLength(ret) != Tcl_UniCharLen(Tcl_GetUnicode(ret))) { /* possibly binary string */ s = Tcl_GetByteArrayFromObj(ret, &len); strval = rb_tainted_str_new(s, len); rb_ivar_set(strval, ID_at_enc, rb_str_new2("binary")); } else { /* possibly text string */ s = Tcl_GetStringFromObj(ret, &len); strval = rb_tainted_str_new(s, len); } # endif rb_thread_critical = thr_crit_bup; Tcl_DecrRefCount(ret); return(strval); } #else /* TCL_MAJOR_VERSION < 8 */ { CONST char *ret; ret = Tcl_SetVar2(ptr->ip, RSTRING(varname)->ptr, RSTRING(index)->ptr, RSTRING(value)->ptr, FIX2INT(flag)); if (ret == (char*)NULL) { rb_raise(rb_eRuntimeError, "%s", ptr->ip->result); } return(rb_tainted_str_new2(ret)); } #endif } static VALUE ip_unset_variable(self, varname_arg, flag_arg) VALUE self; VALUE varname_arg; VALUE flag_arg; { struct tcltkip *ptr = get_ip(self); volatile VALUE varname, value, flag; varname = varname_arg; flag = flag_arg; StringValue(varname); ptr->return_value = Tcl_UnsetVar(ptr->ip, RSTRING(varname)->ptr, FIX2INT(flag)); if (ptr->return_value == TCL_ERROR) { if (FIX2INT(flag) & TCL_LEAVE_ERR_MSG) { #if TCL_MAJOR_VERSION >= 8 rb_raise(rb_eRuntimeError, "%s", Tcl_GetStringResult(ptr->ip)); #else /* TCL_MAJOR_VERSION < 8 */ rb_raise(rb_eRuntimeError, "%s", ptr->ip->result); #endif } return Qfalse; } return Qtrue; } static VALUE ip_unset_variable2(self, varname_arg, index_arg, flag_arg) VALUE self; VALUE varname_arg; VALUE index_arg; VALUE flag_arg; { struct tcltkip *ptr = get_ip(self); volatile VALUE varname, index, value, flag; if (NIL_P(index_arg)) { return ip_unset_variable(self, varname_arg, flag_arg); } varname = varname_arg; index = index_arg; flag = flag_arg; StringValue(varname); StringValue(index); ptr->return_value = Tcl_UnsetVar2(ptr->ip, RSTRING(varname)->ptr, RSTRING(index)->ptr, FIX2INT(flag)); if (ptr->return_value == TCL_ERROR) { if (FIX2INT(flag) & TCL_LEAVE_ERR_MSG) { #if TCL_MAJOR_VERSION >= 8 rb_raise(rb_eRuntimeError, "%s", Tcl_GetStringResult(ptr->ip)); #else /* TCL_MAJOR_VERSION < 8 */ rb_raise(rb_eRuntimeError, "%s", ptr->ip->result); #endif } return Qfalse; } return Qtrue; } static VALUE ip_get_global_var(self, varname) VALUE self; VALUE varname; { return ip_get_variable(self, varname, INT2FIX(TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG)); } static VALUE ip_get_global_var2(self, varname, index) VALUE self; VALUE varname; VALUE index; { return ip_get_variable2(self, varname, index, INT2FIX(TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG)); } static VALUE ip_set_global_var(self, varname, value) VALUE self; VALUE varname; VALUE value; { return ip_set_variable(self, varname, value, INT2FIX(TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG)); } static VALUE ip_set_global_var2(self, varname, index, value) VALUE self; VALUE varname; VALUE index; VALUE value; { return ip_set_variable2(self, varname, index, value, INT2FIX(TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG)); } static VALUE ip_unset_global_var(self, varname) VALUE self; VALUE varname; { return ip_unset_variable(self, varname, INT2FIX(TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG)); } static VALUE ip_unset_global_var2(self, varname, index) VALUE self; VALUE varname; VALUE index; { return ip_unset_variable2(self, varname, index, INT2FIX(TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG)); } /* treat Tcl_List */ static VALUE lib_split_tklist_core(ip_obj, list_str) VALUE ip_obj; VALUE list_str; { Tcl_Interp *interp; volatile VALUE ary, elem; int idx; int taint_flag = OBJ_TAINTED(list_str); int result; VALUE old_gc; if (NIL_P(ip_obj)) { interp = (Tcl_Interp *)NULL; } else { interp = get_ip(ip_obj)->ip; } StringValue(list_str); { #if TCL_MAJOR_VERSION >= 8 /* object style interface */ Tcl_Obj *listobj; int objc; Tcl_Obj **objv; int thr_crit_bup; # if 1 # if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION == 0 listobj = Tcl_NewStringObj(RSTRING(list_str)->ptr, RSTRING(list_str)->len); # else /* TCL_VERSION >= 8.1 */ thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; { VALUE enc = Qnil; if (RTEST(rb_ivar_defined(list_str, ID_at_enc))) { enc = rb_ivar_get(list_str, ID_at_enc); } if (!NIL_P(enc) && strcmp(StringValuePtr(enc), "binary") == 0) { /* binary string */ listobj = Tcl_NewByteArrayObj(RSTRING(list_str)->ptr, RSTRING(list_str)->len); } else if (strlen(RSTRING(list_str)->ptr) != RSTRING(list_str)->len) { /* probably binary string */ listobj = Tcl_NewByteArrayObj(RSTRING(list_str)->ptr, RSTRING(list_str)->len); } else { /* probably text string */ listobj = Tcl_NewStringObj(RSTRING(list_str)->ptr, RSTRING(list_str)->len); } } rb_thread_critical = thr_crit_bup; # endif # else listobj = Tcl_NewStringObj(RSTRING(list_str)->ptr, RSTRING(list_str)->len); # endif Tcl_IncrRefCount(listobj); result = Tcl_ListObjGetElements(interp, listobj, &objc, &objv); if (result == TCL_ERROR) { Tcl_DecrRefCount(listobj); if (interp == (Tcl_Interp*)NULL) { rb_raise(rb_eRuntimeError, "cannot get elements from list"); } else { #if TCL_MAJOR_VERSION >= 8 rb_raise(rb_eRuntimeError, "%s", Tcl_GetStringResult(interp)); #else /* TCL_MAJOR_VERSION < 8 */ rb_raise(rb_eRuntimeError, "%s", interp->result); #endif } } for(idx = 0; idx < objc; idx++) { Tcl_IncrRefCount(objv[idx]); } thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; ary = rb_ary_new2(objc); if (taint_flag) OBJ_TAINT(ary); old_gc = rb_gc_disable(); for(idx = 0; idx < objc; idx++) { char *str; int len; # if 1 # if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION == 0 str = Tcl_GetStringFromObj(objv[idx], &len); elem = rb_str_new(str, len); # else /* TCL_VERSION >= 8.1 */ if (Tcl_GetCharLength(objv[idx]) != Tcl_UniCharLen(Tcl_GetUnicode(objv[idx]))) { /* possibly binary string */ str = Tcl_GetByteArrayFromObj(objv[idx], &len); elem = rb_str_new(str, len); rb_ivar_set(elem, ID_at_enc, rb_tainted_str_new2("binary")); } else { /* possibly text string */ str = Tcl_GetStringFromObj(objv[idx], &len); elem = rb_str_new(str, len); } # endif # else str = Tcl_GetStringFromObj(objv[idx], &len); elem = rb_str_new(str, len); # endif if (taint_flag) OBJ_TAINT(elem); RARRAY(ary)->ptr[idx] = elem; } RARRAY(ary)->len = objc; if (old_gc == Qfalse) rb_gc_enable(); rb_thread_critical = thr_crit_bup; for(idx = 0; idx < objc; idx++) { Tcl_DecrRefCount(objv[idx]); } Tcl_DecrRefCount(listobj); #else /* TCL_MAJOR_VERSION < 8 */ /* string style interface */ int argc; char **argv; if (Tcl_SplitList(interp, RSTRING(list_str)->ptr, &argc, &argv) == TCL_ERROR) { if (interp == (Tcl_Interp*)NULL) { rb_raise(rb_eRuntimeError, "cannot get elements from list"); } else { rb_raise(rb_eRuntimeError, "%s", interp->result); } } ary = rb_ary_new2(argc); if (taint_flag) OBJ_TAINT(ary); old_gc = rb_gc_disable(); for(idx = 0; idx < argc; idx++) { if (taint_flag) { elem = rb_tainted_str_new2(argv[idx]); } else { elem = rb_str_new2(argv[idx]); } /* rb_ivar_set(elem, ID_at_enc, rb_str_new2("binary")); */ RARRAY(ary)->ptr[idx] = elem; } RARRAY(ary)->len = argc; if (old_gc == Qfalse) rb_gc_enable(); #endif } return ary; } static VALUE lib_split_tklist(self, list_str) VALUE self; VALUE list_str; { return lib_split_tklist_core(Qnil, list_str); } static VALUE ip_split_tklist(self, list_str) VALUE self; VALUE list_str; { return lib_split_tklist_core(self, list_str); } static VALUE lib_merge_tklist(argc, argv, obj) int argc; VALUE *argv; VALUE obj; { int num, len; int *flagPtr; char *dst, *result; volatile VALUE str; int taint_flag = 0; int thr_crit_bup; VALUE old_gc; if (argc == 0) return rb_str_new2(""); thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; old_gc = rb_gc_disable(); /* based on Tcl/Tk's Tcl_Merge() */ flagPtr = ALLOC_N(unsigned, argc); /* pass 1 */ len = 1; for(num = 0; num < argc; num++) { if (OBJ_TAINTED(argv[num])) taint_flag = 1; dst = StringValuePtr(argv[num]); #if TCL_MAJOR_VERSION >= 8 len += Tcl_ScanCountedElement(dst, RSTRING(argv[num])->len, &flagPtr[num]) + 1; #else /* TCL_MAJOR_VERSION < 8 */ len += Tcl_ScanElement(dst, &flagPtr[num]) + 1; #endif } /* pass 2 */ result = (char *)Tcl_Alloc(len); dst = result; for(num = 0; num < argc; num++) { #if TCL_MAJOR_VERSION >= 8 len = Tcl_ConvertCountedElement(RSTRING(argv[num])->ptr, RSTRING(argv[num])->len, dst, flagPtr[num]); #else /* TCL_MAJOR_VERSION < 8 */ len = Tcl_ConvertElement(RSTRING(argv[num])->ptr, dst, flagPtr[num]); #endif dst += len; *dst = ' '; dst++; } if (dst == result) { *dst = 0; } else { dst[-1] = 0; } free(flagPtr); /* create object */ str = rb_str_new(result, dst - result - 1); if (taint_flag) OBJ_TAINT(str); Tcl_Free(result); if (old_gc == Qfalse) rb_gc_enable(); rb_thread_critical = thr_crit_bup; return str; } static VALUE lib_conv_listelement(self, src) VALUE self; VALUE src; { int len, scan_flag; volatile VALUE dst; int taint_flag = OBJ_TAINTED(src); int thr_crit_bup; thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; StringValue(src); #if TCL_MAJOR_VERSION >= 8 len = Tcl_ScanCountedElement(RSTRING(src)->ptr, RSTRING(src)->len, &scan_flag); dst = rb_str_new(0, len + 1); len = Tcl_ConvertCountedElement(RSTRING(src)->ptr, RSTRING(src)->len, RSTRING(dst)->ptr, scan_flag); #else /* TCL_MAJOR_VERSION < 8 */ len = Tcl_ScanElement(RSTRING(src)->ptr, &scan_flag); dst = rb_str_new(0, len + 1); len = Tcl_ConvertElement(RSTRING(src)->ptr, RSTRING(dst)->ptr, scan_flag); #endif RSTRING(dst)->len = len; RSTRING(dst)->ptr[len] = '\0'; if (taint_flag) OBJ_TAINT(dst); rb_thread_critical = thr_crit_bup; return dst; } #ifdef __MACOS__ static void _macinit() { tcl_macQdPtr = &qd; /* setup QuickDraw globals */ Tcl_MacSetEventProc(TkMacConvertEvent); /* setup event handler */ } #endif /*---- initialization ----*/ void Init_tcltklib() { int thr_crit_bup; VALUE lib = rb_define_module("TclTkLib"); VALUE ip = rb_define_class("TclTkIp", rb_cObject); VALUE ev_flag = rb_define_module_under(lib, "EventFlag"); VALUE var_flag = rb_define_module_under(lib, "VarAccessFlag"); /* --------------------------------------------------------------- */ #if defined USE_TCL_STUBS && defined USE_TK_STUBS extern int ruby_tcltk_stubs(); int ret = ruby_tcltk_stubs(); if (ret) rb_raise(rb_eLoadError, "tcltklib: tcltk_stubs init error(%d)", ret); #endif /* --------------------------------------------------------------- */ rb_global_variable(&eTkCallbackReturn); rb_global_variable(&eTkCallbackBreak); rb_global_variable(&eTkCallbackContinue); rb_global_variable(&eventloop_thread); rb_global_variable(&watchdog_thread); /* --------------------------------------------------------------- */ rb_define_const(lib, "FINALIZE_PROC_NAME", rb_str_new2(finalize_hook_name)); /* --------------------------------------------------------------- */ rb_define_const(ev_flag, "NONE", INT2FIX(0)); rb_define_const(ev_flag, "WINDOW", INT2FIX(TCL_WINDOW_EVENTS)); rb_define_const(ev_flag, "FILE", INT2FIX(TCL_FILE_EVENTS)); rb_define_const(ev_flag, "TIMER", INT2FIX(TCL_TIMER_EVENTS)); rb_define_const(ev_flag, "IDLE", INT2FIX(TCL_IDLE_EVENTS)); rb_define_const(ev_flag, "ALL", INT2FIX(TCL_ALL_EVENTS)); rb_define_const(ev_flag, "DONT_WAIT", INT2FIX(TCL_DONT_WAIT)); /* --------------------------------------------------------------- */ rb_define_const(var_flag, "NONE", INT2FIX(0)); rb_define_const(var_flag, "GLOBAL_ONLY", INT2FIX(TCL_GLOBAL_ONLY)); #ifdef TCL_NAMESPACE_ONLY rb_define_const(var_flag, "NAMESPACE_ONLY", INT2FIX(TCL_NAMESPACE_ONLY)); #else /* probably Tcl7.6 */ rb_define_const(var_flag, "NAMESPACE_ONLY", INT2FIX(0)); #endif rb_define_const(var_flag, "LEAVE_ERR_MSG", INT2FIX(TCL_LEAVE_ERR_MSG)); rb_define_const(var_flag, "APPEND_VALUE", INT2FIX(TCL_APPEND_VALUE)); rb_define_const(var_flag, "LIST_ELEMENT", INT2FIX(TCL_LIST_ELEMENT)); #ifdef TCL_PARSE_PART1 rb_define_const(var_flag, "PARSE_VARNAME", INT2FIX(TCL_PARSE_PART1)); #else /* probably Tcl7.6 */ rb_define_const(var_flag, "PARSE_VARNAME", INT2FIX(0)); #endif /* --------------------------------------------------------------- */ eTkCallbackBreak = rb_define_class("TkCallbackReturn", rb_eStandardError); eTkCallbackBreak = rb_define_class("TkCallbackBreak", rb_eStandardError); eTkCallbackContinue = rb_define_class("TkCallbackContinue", rb_eStandardError); /* --------------------------------------------------------------- */ eLocalJumpError = rb_const_get(rb_cObject, rb_intern("LocalJumpError")); ID_at_enc = rb_intern("@encoding"); ID_at_interp = rb_intern("@interp"); ID_stop_p = rb_intern("stop?"); ID_kill = rb_intern("kill"); ID_join = rb_intern("join"); ID_call = rb_intern("call"); ID_backtrace = rb_intern("backtrace"); ID_message = rb_intern("message"); ID_at_reason = rb_intern("@reason"); ID_return = rb_intern("return"); ID_break = rb_intern("break"); ID_next = rb_intern("next"); ID_to_s = rb_intern("to_s"); ID_inspect = rb_intern("inspect"); /* --------------------------------------------------------------- */ rb_define_module_function(lib, "mainloop", lib_mainloop, -1); rb_define_module_function(lib, "mainloop_watchdog", lib_mainloop_watchdog, -1); rb_define_module_function(lib, "do_one_event", lib_do_one_event, -1); rb_define_module_function(lib, "mainloop_abort_on_exception", lib_evloop_abort_on_exc, 0); rb_define_module_function(lib, "mainloop_abort_on_exception=", lib_evloop_abort_on_exc_set, 1); rb_define_module_function(lib, "set_eventloop_tick",set_eventloop_tick,1); rb_define_module_function(lib, "get_eventloop_tick",get_eventloop_tick,0); rb_define_module_function(lib, "set_no_event_wait", set_no_event_wait, 1); rb_define_module_function(lib, "get_no_event_wait", get_no_event_wait, 0); rb_define_module_function(lib, "set_eventloop_weight", set_eventloop_weight, 2); rb_define_module_function(lib, "set_max_block_time", set_max_block_time,1); rb_define_module_function(lib, "get_eventloop_weight", get_eventloop_weight, 0); rb_define_module_function(lib, "num_of_mainwindows", lib_num_of_mainwindows, 0); /* --------------------------------------------------------------- */ rb_define_module_function(lib, "_split_tklist", lib_split_tklist, 1); rb_define_module_function(lib, "_merge_tklist", lib_merge_tklist, -1); rb_define_module_function(lib, "_conv_listelement", lib_conv_listelement, 1); rb_define_module_function(lib, "_toUTF8", lib_toUTF8, -1); rb_define_module_function(lib, "_fromUTF8", lib_fromUTF8, -1); rb_define_module_function(lib, "_subst_UTF_backslash", lib_UTF_backslash, 1); rb_define_module_function(lib, "_subst_Tcl_backslash", lib_Tcl_backslash, 1); /* --------------------------------------------------------------- */ rb_define_alloc_func(ip, ip_alloc); rb_define_method(ip, "initialize", ip_init, -1); rb_define_method(ip, "create_slave", ip_create_slave, -1); rb_define_method(ip, "make_safe", ip_make_safe, 0); rb_define_method(ip, "safe?", ip_is_safe_p, 0); rb_define_method(ip, "delete", ip_delete, 0); rb_define_method(ip, "deleted?", ip_is_deleted_p, 0); rb_define_method(ip, "_eval", ip_eval, 1); rb_define_method(ip, "_toUTF8", ip_toUTF8, -1); rb_define_method(ip, "_fromUTF8", ip_fromUTF8, -1); rb_define_method(ip, "_thread_vwait", ip_thread_vwait, 1); rb_define_method(ip, "_thread_tkwait", ip_thread_tkwait, 2); rb_define_method(ip, "_invoke", ip_invoke, -1); rb_define_method(ip, "_return_value", ip_retval, 0); /* --------------------------------------------------------------- */ rb_define_method(ip, "_get_variable", ip_get_variable, 2); rb_define_method(ip, "_get_variable2", ip_get_variable2, 3); rb_define_method(ip, "_set_variable", ip_set_variable, 3); rb_define_method(ip, "_set_variable2", ip_set_variable2, 4); rb_define_method(ip, "_unset_variable", ip_unset_variable, 2); rb_define_method(ip, "_unset_variable2", ip_unset_variable2, 3); rb_define_method(ip, "_get_global_var", ip_get_global_var, 1); rb_define_method(ip, "_get_global_var2", ip_get_global_var2, 2); rb_define_method(ip, "_set_global_var", ip_set_global_var, 2); rb_define_method(ip, "_set_global_var2", ip_set_global_var2, 3); rb_define_method(ip, "_unset_global_var", ip_unset_global_var, 1); rb_define_method(ip, "_unset_global_var2", ip_unset_global_var2, 2); /* --------------------------------------------------------------- */ rb_define_method(ip, "_split_tklist", ip_split_tklist, 1); rb_define_method(ip, "_merge_tklist", lib_merge_tklist, -1); rb_define_method(ip, "_conv_listelement", lib_conv_listelement, 1); /* --------------------------------------------------------------- */ rb_define_method(ip, "mainloop", ip_mainloop, -1); rb_define_method(ip, "mainloop_watchdog", ip_mainloop_watchdog, -1); rb_define_method(ip, "do_one_event", ip_do_one_event, -1); rb_define_method(ip, "mainloop_abort_on_exception", ip_evloop_abort_on_exc, 0); rb_define_method(ip, "mainloop_abort_on_exception=", ip_evloop_abort_on_exc_set, 1); rb_define_method(ip, "set_eventloop_tick", ip_set_eventloop_tick, 1); rb_define_method(ip, "get_eventloop_tick", ip_get_eventloop_tick, 0); rb_define_method(ip, "set_no_event_wait", ip_set_no_event_wait, 1); rb_define_method(ip, "get_no_event_wait", ip_get_no_event_wait, 0); rb_define_method(ip, "set_eventloop_weight", ip_set_eventloop_weight, 2); rb_define_method(ip, "get_eventloop_weight", ip_get_eventloop_weight, 0); rb_define_method(ip, "set_max_block_time", set_max_block_time, 1); rb_define_method(ip, "restart", ip_restart, 0); /* --------------------------------------------------------------- */ eventloop_thread = 0; watchdog_thread = 0; /* --------------------------------------------------------------- */ #ifdef __MACOS__ _macinit(); #endif /* from Tk_Main() */ DUMP1("Tcl_FindExecutable"); Tcl_FindExecutable(RSTRING(rb_argv0)->ptr); /* --------------------------------------------------------------- */ } /* eof */