PageRenderTime 102ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 2ms

/ext/tk/tcltklib.c

https://github.com/diabolo/ruby
C | 10842 lines | 8381 code | 1565 blank | 896 comment | 1327 complexity | 0afc1ec549089c571b0f78f3bdf3af26 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause
  1. /*
  2. * tcltklib.c
  3. * Aug. 27, 1997 Y. Shigehiro
  4. * Oct. 24, 1997 Y. Matsumoto
  5. */
  6. #define TCLTKLIB_RELEASE_DATE "2010-05-31"
  7. /* #define CREATE_RUBYTK_KIT */
  8. #include "ruby.h"
  9. #ifdef HAVE_RUBY_ENCODING_H
  10. #include "ruby/encoding.h"
  11. #endif
  12. #ifndef RUBY_VERSION
  13. #define RUBY_VERSION "(unknown version)"
  14. #endif
  15. #ifndef RUBY_RELEASE_DATE
  16. #define RUBY_RELEASE_DATE "unknown release-date"
  17. #endif
  18. #ifdef RUBY_VM
  19. static VALUE rb_thread_critical; /* dummy */
  20. int rb_thread_check_trap_pending();
  21. #else
  22. /* use rb_thread_critical on Ruby 1.8.x */
  23. #include "rubysig.h"
  24. #endif
  25. #if !defined(RSTRING_PTR)
  26. #define RSTRING_PTR(s) (RSTRING(s)->ptr)
  27. #define RSTRING_LEN(s) (RSTRING(s)->len)
  28. #endif
  29. #if !defined(RARRAY_PTR)
  30. #define RARRAY_PTR(s) (RARRAY(s)->ptr)
  31. #define RARRAY_LEN(s) (RARRAY(s)->len)
  32. #endif
  33. #ifdef OBJ_UNTRUST
  34. #define RbTk_OBJ_UNTRUST(x) do {OBJ_TAINT(x); OBJ_UNTRUST(x);} while (0)
  35. #else
  36. #define RbTk_OBJ_UNTRUST(x) OBJ_TAINT(x)
  37. #endif
  38. #if defined(HAVE_RB_PROC_NEW) && !defined(RUBY_VM)
  39. /* Ruby 1.8 :: rb_proc_new() was hidden from intern.h at 2008/04/22 */
  40. extern VALUE rb_proc_new _((VALUE (*)(ANYARGS/* VALUE yieldarg[, VALUE procarg] */), VALUE));
  41. #endif
  42. #undef EXTERN /* avoid conflict with tcl.h of tcl8.2 or before */
  43. #include <stdio.h>
  44. #ifdef HAVE_STDARG_PROTOTYPES
  45. #include <stdarg.h>
  46. #define va_init_list(a,b) va_start(a,b)
  47. #else
  48. #include <varargs.h>
  49. #define va_init_list(a,b) va_start(a)
  50. #endif
  51. #include <string.h>
  52. #if !defined HAVE_VSNPRINTF && !defined vsnprintf
  53. # ifdef WIN32
  54. /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */
  55. # define vsnprintf _vsnprintf
  56. # else
  57. # ifdef HAVE_RUBY_RUBY_H
  58. # include "ruby/missing.h"
  59. # else
  60. # include "missing.h"
  61. # endif
  62. # endif
  63. #endif
  64. #include <tcl.h>
  65. #include <tk.h>
  66. #ifndef HAVE_RUBY_NATIVE_THREAD_P
  67. #define ruby_native_thread_p() is_ruby_native_thread()
  68. #undef RUBY_USE_NATIVE_THREAD
  69. #else
  70. #define RUBY_USE_NATIVE_THREAD 1
  71. #endif
  72. #ifndef HAVE_RB_ERRINFO
  73. #define rb_errinfo() (ruby_errinfo+0) /* cannot be l-value */
  74. #else
  75. VALUE rb_errinfo(void);
  76. #endif
  77. #ifndef HAVE_RB_SAFE_LEVEL
  78. #define rb_safe_level() (ruby_safe_level+0)
  79. #endif
  80. #ifndef HAVE_RB_SOURCEFILE
  81. #define rb_sourcefile() (ruby_sourcefile+0)
  82. #endif
  83. #include "stubs.h"
  84. #ifndef TCL_ALPHA_RELEASE
  85. #define TCL_ALPHA_RELEASE 0 /* "alpha" */
  86. #define TCL_BETA_RELEASE 1 /* "beta" */
  87. #define TCL_FINAL_RELEASE 2 /* "final" */
  88. #endif
  89. static struct {
  90. int major;
  91. int minor;
  92. int type; /* ALPHA==0, BETA==1, FINAL==2 */
  93. int patchlevel;
  94. } tcltk_version = {0, 0, 0, 0};
  95. static void
  96. set_tcltk_version()
  97. {
  98. if (tcltk_version.major) return;
  99. Tcl_GetVersion(&(tcltk_version.major),
  100. &(tcltk_version.minor),
  101. &(tcltk_version.patchlevel),
  102. &(tcltk_version.type));
  103. }
  104. #if TCL_MAJOR_VERSION >= 8
  105. # ifndef CONST84
  106. # if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION <= 4 /* Tcl8.0.x -- 8.4b1 */
  107. # define CONST84
  108. # else /* unknown (maybe TCL_VERSION >= 8.5) */
  109. # ifdef CONST
  110. # define CONST84 CONST
  111. # else
  112. # define CONST84
  113. # endif
  114. # endif
  115. # endif
  116. #else /* TCL_MAJOR_VERSION < 8 */
  117. # ifdef CONST
  118. # define CONST84 CONST
  119. # else
  120. # define CONST
  121. # define CONST84
  122. # endif
  123. #endif
  124. #ifndef CONST86
  125. # if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION <= 5 /* Tcl8.0.x -- 8.5.x */
  126. # define CONST86
  127. # else
  128. # define CONST86 CONST84
  129. # endif
  130. #endif
  131. /* copied from eval.c */
  132. #define TAG_RETURN 0x1
  133. #define TAG_BREAK 0x2
  134. #define TAG_NEXT 0x3
  135. #define TAG_RETRY 0x4
  136. #define TAG_REDO 0x5
  137. #define TAG_RAISE 0x6
  138. #define TAG_THROW 0x7
  139. #define TAG_FATAL 0x8
  140. /* for ruby_debug */
  141. #define DUMP1(ARG1) if (ruby_debug) { fprintf(stderr, "tcltklib: %s\n", ARG1); fflush(stderr); }
  142. #define DUMP2(ARG1, ARG2) if (ruby_debug) { fprintf(stderr, "tcltklib: ");\
  143. fprintf(stderr, ARG1, ARG2); fprintf(stderr, "\n"); fflush(stderr); }
  144. #define DUMP3(ARG1, ARG2, ARG3) if (ruby_debug) { fprintf(stderr, "tcltklib: ");\
  145. fprintf(stderr, ARG1, ARG2, ARG3); fprintf(stderr, "\n"); fflush(stderr); }
  146. /*
  147. #define DUMP1(ARG1)
  148. #define DUMP2(ARG1, ARG2)
  149. #define DUMP3(ARG1, ARG2, ARG3)
  150. */
  151. /* release date */
  152. static const char tcltklib_release_date[] = TCLTKLIB_RELEASE_DATE;
  153. /* finalize_proc_name */
  154. static const char finalize_hook_name[] = "INTERP_FINALIZE_HOOK";
  155. static void ip_finalize _((Tcl_Interp*));
  156. static int at_exit = 0;
  157. #ifdef HAVE_RUBY_ENCODING_H
  158. static VALUE cRubyEncoding;
  159. /* encoding */
  160. static int ENCODING_INDEX_UTF8;
  161. static int ENCODING_INDEX_BINARY;
  162. #endif
  163. static VALUE ENCODING_NAME_UTF8;
  164. static VALUE ENCODING_NAME_BINARY;
  165. static VALUE create_dummy_encoding_for_tk_core _((VALUE, VALUE, VALUE));
  166. static VALUE create_dummy_encoding_for_tk _((VALUE, VALUE));
  167. static int update_encoding_table _((VALUE, VALUE, VALUE));
  168. static VALUE encoding_table_get_name_core _((VALUE, VALUE, VALUE));
  169. static VALUE encoding_table_get_obj_core _((VALUE, VALUE, VALUE));
  170. static VALUE encoding_table_get_name _((VALUE, VALUE));
  171. static VALUE encoding_table_get_obj _((VALUE, VALUE));
  172. static VALUE create_encoding_table _((VALUE));
  173. static VALUE ip_get_encoding_table _((VALUE));
  174. /* for callback break & continue */
  175. static VALUE eTkCallbackReturn;
  176. static VALUE eTkCallbackBreak;
  177. static VALUE eTkCallbackContinue;
  178. static VALUE eLocalJumpError;
  179. static VALUE eTkLocalJumpError;
  180. static VALUE eTkCallbackRetry;
  181. static VALUE eTkCallbackRedo;
  182. static VALUE eTkCallbackThrow;
  183. static VALUE tcltkip_class;
  184. static ID ID_at_enc;
  185. static ID ID_at_interp;
  186. static ID ID_encoding_name;
  187. static ID ID_encoding_table;
  188. static ID ID_stop_p;
  189. static ID ID_alive_p;
  190. static ID ID_kill;
  191. static ID ID_join;
  192. static ID ID_value;
  193. static ID ID_call;
  194. static ID ID_backtrace;
  195. static ID ID_message;
  196. static ID ID_at_reason;
  197. static ID ID_return;
  198. static ID ID_break;
  199. static ID ID_next;
  200. static ID ID_to_s;
  201. static ID ID_inspect;
  202. static VALUE ip_invoke_real _((int, VALUE*, VALUE));
  203. static VALUE ip_invoke _((int, VALUE*, VALUE));
  204. static VALUE ip_invoke_with_position _((int, VALUE*, VALUE, Tcl_QueuePosition));
  205. static VALUE tk_funcall _((VALUE(), int, VALUE*, VALUE));
  206. static VALUE callq_safelevel_handler _((VALUE, VALUE));
  207. /* Tcl's object type */
  208. #if TCL_MAJOR_VERSION >= 8
  209. static const char Tcl_ObjTypeName_ByteArray[] = "bytearray";
  210. static CONST86 Tcl_ObjType *Tcl_ObjType_ByteArray;
  211. static const char Tcl_ObjTypeName_String[] = "string";
  212. static CONST86 Tcl_ObjType *Tcl_ObjType_String;
  213. #if TCL_MAJOR_VERSION > 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION >= 1)
  214. #define IS_TCL_BYTEARRAY(obj) ((obj)->typePtr == Tcl_ObjType_ByteArray)
  215. #define IS_TCL_STRING(obj) ((obj)->typePtr == Tcl_ObjType_String)
  216. #define IS_TCL_VALID_STRING(obj) ((obj)->bytes != (char*)NULL)
  217. #endif
  218. #endif
  219. #ifndef HAVE_RB_HASH_LOOKUP
  220. #define rb_hash_lookup rb_hash_aref
  221. #endif
  222. /* safe Tcl_Eval and Tcl_GlobalEval */
  223. static int
  224. #ifdef HAVE_PROTOTYPES
  225. tcl_eval(Tcl_Interp *interp, const char *cmd)
  226. #else
  227. tcl_eval(interp, cmd)
  228. Tcl_Interp *interp;
  229. const char *cmd; /* don't have to be writable */
  230. #endif
  231. {
  232. char *buf = strdup(cmd);
  233. int ret;
  234. Tcl_AllowExceptions(interp);
  235. ret = Tcl_Eval(interp, buf);
  236. free(buf);
  237. return ret;
  238. }
  239. #undef Tcl_Eval
  240. #define Tcl_Eval tcl_eval
  241. static int
  242. #ifdef HAVE_PROTOTYPES
  243. tcl_global_eval(Tcl_Interp *interp, const char *cmd)
  244. #else
  245. tcl_global_eval(interp, cmd)
  246. Tcl_Interp *interp;
  247. const char *cmd; /* don't have to be writable */
  248. #endif
  249. {
  250. char *buf = strdup(cmd);
  251. int ret;
  252. Tcl_AllowExceptions(interp);
  253. ret = Tcl_GlobalEval(interp, buf);
  254. free(buf);
  255. return ret;
  256. }
  257. #undef Tcl_GlobalEval
  258. #define Tcl_GlobalEval tcl_global_eval
  259. /* Tcl_{Incr|Decr}RefCount for tcl7.x or earlier */
  260. #if TCL_MAJOR_VERSION < 8
  261. #define Tcl_IncrRefCount(obj) (1)
  262. #define Tcl_DecrRefCount(obj) (1)
  263. #endif
  264. /* Tcl_GetStringResult for tcl7.x or earlier */
  265. #if TCL_MAJOR_VERSION < 8
  266. #define Tcl_GetStringResult(interp) ((interp)->result)
  267. #endif
  268. /* Tcl_[GS]etVar2Ex for tcl8.0 */
  269. #if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION == 0
  270. static Tcl_Obj *
  271. Tcl_GetVar2Ex(interp, name1, name2, flags)
  272. Tcl_Interp *interp;
  273. CONST char *name1;
  274. CONST char *name2;
  275. int flags;
  276. {
  277. Tcl_Obj *nameObj1, *nameObj2 = NULL, *retObj;
  278. nameObj1 = Tcl_NewStringObj((char*)name1, -1);
  279. Tcl_IncrRefCount(nameObj1);
  280. if (name2) {
  281. nameObj2 = Tcl_NewStringObj((char*)name2, -1);
  282. Tcl_IncrRefCount(nameObj2);
  283. }
  284. retObj = Tcl_ObjGetVar2(interp, nameObj1, nameObj2, flags);
  285. if (name2) {
  286. Tcl_DecrRefCount(nameObj2);
  287. }
  288. Tcl_DecrRefCount(nameObj1);
  289. return retObj;
  290. }
  291. static Tcl_Obj *
  292. Tcl_SetVar2Ex(interp, name1, name2, newValObj, flags)
  293. Tcl_Interp *interp;
  294. CONST char *name1;
  295. CONST char *name2;
  296. Tcl_Obj *newValObj;
  297. int flags;
  298. {
  299. Tcl_Obj *nameObj1, *nameObj2 = NULL, *retObj;
  300. nameObj1 = Tcl_NewStringObj((char*)name1, -1);
  301. Tcl_IncrRefCount(nameObj1);
  302. if (name2) {
  303. nameObj2 = Tcl_NewStringObj((char*)name2, -1);
  304. Tcl_IncrRefCount(nameObj2);
  305. }
  306. retObj = Tcl_ObjSetVar2(interp, nameObj1, nameObj2, newValObj, flags);
  307. if (name2) {
  308. Tcl_DecrRefCount(nameObj2);
  309. }
  310. Tcl_DecrRefCount(nameObj1);
  311. return retObj;
  312. }
  313. #endif
  314. /* from tkAppInit.c */
  315. #if TCL_MAJOR_VERSION < 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION < 4)
  316. # if !defined __MINGW32__ && !defined __BORLANDC__
  317. /*
  318. * The following variable is a special hack that is needed in order for
  319. * Sun shared libraries to be used for Tcl.
  320. */
  321. extern int matherr();
  322. int *tclDummyMathPtr = (int *) matherr;
  323. # endif
  324. #endif
  325. /*---- module TclTkLib ----*/
  326. struct invoke_queue {
  327. Tcl_Event ev;
  328. int argc;
  329. #if TCL_MAJOR_VERSION >= 8
  330. Tcl_Obj **argv;
  331. #else /* TCL_MAJOR_VERSION < 8 */
  332. char **argv;
  333. #endif
  334. VALUE interp;
  335. int *done;
  336. int safe_level;
  337. VALUE result;
  338. VALUE thread;
  339. };
  340. struct eval_queue {
  341. Tcl_Event ev;
  342. char *str;
  343. int len;
  344. VALUE interp;
  345. int *done;
  346. int safe_level;
  347. VALUE result;
  348. VALUE thread;
  349. };
  350. struct call_queue {
  351. Tcl_Event ev;
  352. VALUE (*func)();
  353. int argc;
  354. VALUE *argv;
  355. VALUE interp;
  356. int *done;
  357. int safe_level;
  358. VALUE result;
  359. VALUE thread;
  360. };
  361. void
  362. invoke_queue_mark(struct invoke_queue *q)
  363. {
  364. rb_gc_mark(q->interp);
  365. rb_gc_mark(q->result);
  366. rb_gc_mark(q->thread);
  367. }
  368. void
  369. eval_queue_mark(struct eval_queue *q)
  370. {
  371. rb_gc_mark(q->interp);
  372. rb_gc_mark(q->result);
  373. rb_gc_mark(q->thread);
  374. }
  375. void
  376. call_queue_mark(struct call_queue *q)
  377. {
  378. int i;
  379. for(i = 0; i < q->argc; i++) {
  380. rb_gc_mark(q->argv[i]);
  381. }
  382. rb_gc_mark(q->interp);
  383. rb_gc_mark(q->result);
  384. rb_gc_mark(q->thread);
  385. }
  386. static VALUE eventloop_thread;
  387. static Tcl_Interp *eventloop_interp;
  388. #ifdef RUBY_USE_NATIVE_THREAD
  389. Tcl_ThreadId tk_eventloop_thread_id; /* native thread ID of Tcl interpreter */
  390. #endif
  391. static VALUE eventloop_stack;
  392. static int window_event_mode = ~0;
  393. static VALUE watchdog_thread;
  394. Tcl_Interp *current_interp;
  395. /* thread control strategy */
  396. /* multi-tk works with the following settings only ???
  397. : CONTROL_BY_STATUS_OF_RB_THREAD_WAITING_FOR_VALUE 1
  398. : USE_TOGGLE_WINDOW_MODE_FOR_IDLE 0
  399. : DO_THREAD_SCHEDULE_AT_CALLBACK_DONE 0
  400. */
  401. #ifdef RUBY_USE_NATIVE_THREAD
  402. #define CONTROL_BY_STATUS_OF_RB_THREAD_WAITING_FOR_VALUE 1
  403. #define USE_TOGGLE_WINDOW_MODE_FOR_IDLE 0
  404. #define DO_THREAD_SCHEDULE_AT_CALLBACK_DONE 1
  405. #else /* ! RUBY_USE_NATIVE_THREAD */
  406. #define CONTROL_BY_STATUS_OF_RB_THREAD_WAITING_FOR_VALUE 1
  407. #define USE_TOGGLE_WINDOW_MODE_FOR_IDLE 0
  408. #define DO_THREAD_SCHEDULE_AT_CALLBACK_DONE 0
  409. #endif
  410. #if CONTROL_BY_STATUS_OF_RB_THREAD_WAITING_FOR_VALUE
  411. static int have_rb_thread_waiting_for_value = 0;
  412. #endif
  413. /*
  414. * 'event_loop_max' is a maximum events which the eventloop processes in one
  415. * term of thread scheduling. 'no_event_tick' is the count-up value when
  416. * there are no event for processing.
  417. * 'timer_tick' is a limit of one term of thread scheduling.
  418. * If 'timer_tick' == 0, then not use the timer for thread scheduling.
  419. */
  420. #ifdef RUBY_USE_NATIVE_THREAD
  421. #define DEFAULT_EVENT_LOOP_MAX 800/*counts*/
  422. #define DEFAULT_NO_EVENT_TICK 10/*counts*/
  423. #define DEFAULT_NO_EVENT_WAIT 1/*milliseconds ( 1 -- 999 ) */
  424. #define WATCHDOG_INTERVAL 10/*milliseconds ( 1 -- 999 ) */
  425. #define DEFAULT_TIMER_TICK 0/*milliseconds ( 0 -- 999 ) */
  426. #define NO_THREAD_INTERRUPT_TIME 100/*milliseconds ( 1 -- 999 ) */
  427. #else /* ! RUBY_USE_NATIVE_THREAD */
  428. #define DEFAULT_EVENT_LOOP_MAX 800/*counts*/
  429. #define DEFAULT_NO_EVENT_TICK 10/*counts*/
  430. #define DEFAULT_NO_EVENT_WAIT 20/*milliseconds ( 1 -- 999 ) */
  431. #define WATCHDOG_INTERVAL 10/*milliseconds ( 1 -- 999 ) */
  432. #define DEFAULT_TIMER_TICK 0/*milliseconds ( 0 -- 999 ) */
  433. #define NO_THREAD_INTERRUPT_TIME 100/*milliseconds ( 1 -- 999 ) */
  434. #endif
  435. #define EVENT_HANDLER_TIMEOUT 100/*milliseconds*/
  436. static int event_loop_max = DEFAULT_EVENT_LOOP_MAX;
  437. static int no_event_tick = DEFAULT_NO_EVENT_TICK;
  438. static int no_event_wait = DEFAULT_NO_EVENT_WAIT;
  439. static int timer_tick = DEFAULT_TIMER_TICK;
  440. static int req_timer_tick = DEFAULT_TIMER_TICK;
  441. static int run_timer_flag = 0;
  442. static int event_loop_wait_event = 0;
  443. static int event_loop_abort_on_exc = 1;
  444. static int loop_counter = 0;
  445. static int check_rootwidget_flag = 0;
  446. /* call ruby interpreter */
  447. #if TCL_MAJOR_VERSION >= 8
  448. static int ip_ruby_eval _((ClientData, Tcl_Interp *, int, Tcl_Obj *CONST*));
  449. static int ip_ruby_cmd _((ClientData, Tcl_Interp *, int, Tcl_Obj *CONST*));
  450. #else /* TCL_MAJOR_VERSION < 8 */
  451. static int ip_ruby_eval _((ClientData, Tcl_Interp *, int, char **));
  452. static int ip_ruby_cmd _((ClientData, Tcl_Interp *, int, char **));
  453. #endif
  454. struct cmd_body_arg {
  455. VALUE receiver;
  456. ID method;
  457. VALUE args;
  458. };
  459. /*----------------------------*/
  460. /* use Tcl internal functions */
  461. /*----------------------------*/
  462. #ifndef TCL_NAMESPACE_DEBUG
  463. #define TCL_NAMESPACE_DEBUG 0
  464. #endif
  465. #if TCL_NAMESPACE_DEBUG
  466. #if TCL_MAJOR_VERSION >= 8
  467. EXTERN struct TclIntStubs *tclIntStubsPtr;
  468. #endif
  469. /*-- Tcl_GetCurrentNamespace --*/
  470. #if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION < 5
  471. /* Tcl7.x doesn't have namespace support. */
  472. /* Tcl8.5+ has definition of Tcl_GetCurrentNamespace() in tclDecls.h */
  473. # ifndef Tcl_GetCurrentNamespace
  474. EXTERN Tcl_Namespace * Tcl_GetCurrentNamespace _((Tcl_Interp *));
  475. # endif
  476. # if defined(USE_TCL_STUBS) && !defined(USE_TCL_STUB_PROCS)
  477. # ifndef Tcl_GetCurrentNamespace
  478. # ifndef FunctionNum_of_GetCurrentNamespace
  479. #define FunctionNum_of_GetCurrentNamespace 124
  480. # endif
  481. struct DummyTclIntStubs_for_GetCurrentNamespace {
  482. int magic;
  483. struct TclIntStubHooks *hooks;
  484. void (*func[FunctionNum_of_GetCurrentNamespace])();
  485. Tcl_Namespace * (*tcl_GetCurrentNamespace) _((Tcl_Interp *));
  486. };
  487. #define Tcl_GetCurrentNamespace \
  488. (((struct DummyTclIntStubs_for_GetCurrentNamespace *)tclIntStubsPtr)->tcl_GetCurrentNamespace)
  489. # endif
  490. # endif
  491. #endif
  492. /* namespace check */
  493. /* ip_null_namespace(Tcl_Interp *interp) */
  494. #if TCL_MAJOR_VERSION < 8
  495. #define ip_null_namespace(interp) (0)
  496. #else /* support namespace */
  497. #define ip_null_namespace(interp) \
  498. (Tcl_GetCurrentNamespace(interp) == (Tcl_Namespace *)NULL)
  499. #endif
  500. /* rbtk_invalid_namespace(tcltkip *ptr) */
  501. #if TCL_MAJOR_VERSION < 8
  502. #define rbtk_invalid_namespace(ptr) (0)
  503. #else /* support namespace */
  504. #define rbtk_invalid_namespace(ptr) \
  505. ((ptr)->default_ns == (Tcl_Namespace*)NULL || Tcl_GetCurrentNamespace((ptr)->ip) != (ptr)->default_ns)
  506. #endif
  507. /*-- Tcl_PopCallFrame & Tcl_PushCallFrame --*/
  508. #if TCL_MAJOR_VERSION >= 8
  509. # ifndef CallFrame
  510. typedef struct CallFrame {
  511. Tcl_Namespace *nsPtr;
  512. int dummy1;
  513. int dummy2;
  514. char *dummy3;
  515. struct CallFrame *callerPtr;
  516. struct CallFrame *callerVarPtr;
  517. int level;
  518. char *dummy7;
  519. char *dummy8;
  520. int dummy9;
  521. char* dummy10;
  522. } CallFrame;
  523. # endif
  524. # if !defined(TclGetFrame) && !defined(TclGetFrame_TCL_DECLARED)
  525. EXTERN int TclGetFrame _((Tcl_Interp *, CONST char *, CallFrame **));
  526. # endif
  527. # if defined(USE_TCL_STUBS) && !defined(USE_TCL_STUB_PROCS)
  528. # ifndef TclGetFrame
  529. # ifndef FunctionNum_of_GetFrame
  530. #define FunctionNum_of_GetFrame 32
  531. # endif
  532. struct DummyTclIntStubs_for_GetFrame {
  533. int magic;
  534. struct TclIntStubHooks *hooks;
  535. void (*func[FunctionNum_of_GetFrame])();
  536. int (*tclGetFrame) _((Tcl_Interp *, CONST char *, CallFrame **));
  537. };
  538. #define TclGetFrame \
  539. (((struct DummyTclIntStubs_for_GetFrame *)tclIntStubsPtr)->tclGetFrame)
  540. # endif
  541. # endif
  542. # if !defined(Tcl_PopCallFrame) && !defined(Tcl_PopCallFrame_TCL_DECLARED)
  543. EXTERN void Tcl_PopCallFrame _((Tcl_Interp *));
  544. EXTERN int Tcl_PushCallFrame _((Tcl_Interp *, Tcl_CallFrame *, Tcl_Namespace *, int));
  545. # endif
  546. # if defined(USE_TCL_STUBS) && !defined(USE_TCL_STUB_PROCS)
  547. # ifndef Tcl_PopCallFrame
  548. # ifndef FunctionNum_of_PopCallFrame
  549. #define FunctionNum_of_PopCallFrame 128
  550. # endif
  551. struct DummyTclIntStubs_for_PopCallFrame {
  552. int magic;
  553. struct TclIntStubHooks *hooks;
  554. void (*func[FunctionNum_of_PopCallFrame])();
  555. void (*tcl_PopCallFrame) _((Tcl_Interp *));
  556. int (*tcl_PushCallFrame) _((Tcl_Interp *, Tcl_CallFrame *, Tcl_Namespace *, int));
  557. };
  558. #define Tcl_PopCallFrame \
  559. (((struct DummyTclIntStubs_for_PopCallFrame *)tclIntStubsPtr)->tcl_PopCallFrame)
  560. #define Tcl_PushCallFrame \
  561. (((struct DummyTclIntStubs_for_PopCallFrame *)tclIntStubsPtr)->tcl_PushCallFrame)
  562. # endif
  563. # endif
  564. #else /* Tcl7.x */
  565. # ifndef CallFrame
  566. typedef struct CallFrame {
  567. Tcl_HashTable varTable;
  568. int level;
  569. int argc;
  570. char **argv;
  571. struct CallFrame *callerPtr;
  572. struct CallFrame *callerVarPtr;
  573. } CallFrame;
  574. # endif
  575. # ifndef Tcl_CallFrame
  576. #define Tcl_CallFrame CallFrame
  577. # endif
  578. # if !defined(TclGetFrame) && !defined(TclGetFrame_TCL_DECLARED)
  579. EXTERN int TclGetFrame _((Tcl_Interp *, CONST char *, CallFrame **));
  580. # endif
  581. # if !defined(Tcl_PopCallFrame) && !defined(Tcl_PopCallFrame_TCL_DECLARED)
  582. typedef struct DummyInterp {
  583. char *dummy1;
  584. char *dummy2;
  585. int dummy3;
  586. Tcl_HashTable dummy4;
  587. Tcl_HashTable dummy5;
  588. Tcl_HashTable dummy6;
  589. int numLevels;
  590. int maxNestingDepth;
  591. CallFrame *framePtr;
  592. CallFrame *varFramePtr;
  593. } DummyInterp;
  594. static void
  595. Tcl_PopCallFrame(interp)
  596. Tcl_Interp *interp;
  597. {
  598. DummyInterp *iPtr = (DummyInterp*)interp;
  599. CallFrame *frame = iPtr->varFramePtr;
  600. /* **** DUMMY **** */
  601. iPtr->framePtr = frame.callerPtr;
  602. iPtr->varFramePtr = frame.callerVarPtr;
  603. return TCL_OK;
  604. }
  605. /* dummy */
  606. #define Tcl_Namespace char
  607. static int
  608. Tcl_PushCallFrame(interp, framePtr, nsPtr, isProcCallFrame)
  609. Tcl_Interp *interp;
  610. Tcl_CallFrame *framePtr;
  611. Tcl_Namespace *nsPtr;
  612. int isProcCallFrame;
  613. {
  614. DummyInterp *iPtr = (DummyInterp*)interp;
  615. CallFrame *frame = (CallFrame *)framePtr;
  616. /* **** DUMMY **** */
  617. Tcl_InitHashTable(&frame.varTable, TCL_STRING_KEYS);
  618. if (iPtr->varFramePtr != NULL) {
  619. frame.level = iPtr->varFramePtr->level + 1;
  620. } else {
  621. frame.level = 1;
  622. }
  623. frame.callerPtr = iPtr->framePtr;
  624. frame.callerVarPtr = iPtr->varFramePtr;
  625. iPtr->framePtr = &frame;
  626. iPtr->varFramePtr = &frame;
  627. return TCL_OK;
  628. }
  629. # endif
  630. #endif
  631. #endif /* TCL_NAMESPACE_DEBUG */
  632. /*---- class TclTkIp ----*/
  633. struct tcltkip {
  634. Tcl_Interp *ip; /* the interpreter */
  635. #if TCL_NAMESPACE_DEBUG
  636. Tcl_Namespace *default_ns; /* default namespace */
  637. #endif
  638. #ifdef RUBY_USE_NATIVE_THREAD
  639. Tcl_ThreadId tk_thread_id; /* native thread ID of Tcl interpreter */
  640. #endif
  641. int has_orig_exit; /* has original 'exit' command ? */
  642. Tcl_CmdInfo orig_exit_info; /* command info of original 'exit' command */
  643. int ref_count; /* reference count of rbtk_preserve_ip call */
  644. int allow_ruby_exit; /* allow exiting ruby by 'exit' function */
  645. int return_value; /* return value */
  646. };
  647. static struct tcltkip *
  648. get_ip(self)
  649. VALUE self;
  650. {
  651. struct tcltkip *ptr;
  652. Data_Get_Struct(self, struct tcltkip, ptr);
  653. if (ptr == 0) {
  654. /* rb_raise(rb_eTypeError, "uninitialized TclTkIp"); */
  655. return((struct tcltkip *)NULL);
  656. }
  657. if (ptr->ip == (Tcl_Interp*)NULL) {
  658. /* rb_raise(rb_eRuntimeError, "deleted IP"); */
  659. return((struct tcltkip *)NULL);
  660. }
  661. return ptr;
  662. }
  663. static int
  664. deleted_ip(ptr)
  665. struct tcltkip *ptr;
  666. {
  667. if (!ptr || !ptr->ip || Tcl_InterpDeleted(ptr->ip)
  668. #if TCL_NAMESPACE_DEBUG
  669. || rbtk_invalid_namespace(ptr)
  670. #endif
  671. ) {
  672. DUMP1("ip is deleted");
  673. return 1;
  674. }
  675. return 0;
  676. }
  677. /* increment/decrement reference count of tcltkip */
  678. static int
  679. rbtk_preserve_ip(ptr)
  680. struct tcltkip *ptr;
  681. {
  682. ptr->ref_count++;
  683. if (ptr->ip == (Tcl_Interp*)NULL) {
  684. /* deleted IP */
  685. ptr->ref_count = 0;
  686. } else {
  687. Tcl_Preserve((ClientData)ptr->ip);
  688. }
  689. return(ptr->ref_count);
  690. }
  691. static int
  692. rbtk_release_ip(ptr)
  693. struct tcltkip *ptr;
  694. {
  695. ptr->ref_count--;
  696. if (ptr->ref_count < 0) {
  697. ptr->ref_count = 0;
  698. } else if (ptr->ip == (Tcl_Interp*)NULL) {
  699. /* deleted IP */
  700. ptr->ref_count = 0;
  701. } else {
  702. Tcl_Release((ClientData)ptr->ip);
  703. }
  704. return(ptr->ref_count);
  705. }
  706. static VALUE
  707. #ifdef HAVE_STDARG_PROTOTYPES
  708. create_ip_exc(VALUE interp, VALUE exc, const char *fmt, ...)
  709. #else
  710. create_ip_exc(interp, exc, fmt, va_alist)
  711. VALUE interp:
  712. VALUE exc;
  713. const char *fmt;
  714. va_dcl
  715. #endif
  716. {
  717. va_list args;
  718. char buf[BUFSIZ];
  719. VALUE einfo;
  720. struct tcltkip *ptr = get_ip(interp);
  721. va_init_list(args,fmt);
  722. vsnprintf(buf, BUFSIZ, fmt, args);
  723. buf[BUFSIZ - 1] = '\0';
  724. va_end(args);
  725. einfo = rb_exc_new2(exc, buf);
  726. rb_ivar_set(einfo, ID_at_interp, interp);
  727. if (ptr) {
  728. Tcl_ResetResult(ptr->ip);
  729. }
  730. return einfo;
  731. }
  732. /*-------------------------------------------------------*/
  733. #if defined CREATE_RUBYTK_KIT || defined CREATE_RUBYKIT
  734. /* Tcl/Tk stubs may work, but probably it is meaningless. */
  735. #if defined USE_TCL_STUBS || defined USE_TK_STUBS
  736. # error Not support Tcl/Tk stubs with Ruby/Tk-Kit or Rubykit.
  737. #endif
  738. #ifndef KIT_INCLUDES_TK
  739. # define KIT_INCLUDES_TK 1
  740. #endif
  741. /* #define KIT_INCLUDES_ITCL 1 */
  742. /* #define KIT_INCLUDES_THREAD 1 */
  743. #ifdef KIT_INCLUDES_ITCL
  744. Tcl_AppInitProc Itcl_Init;
  745. #endif
  746. Tcl_AppInitProc Mk4tcl_Init, Vfs_Init, Rechan_Init, Zlib_Init;
  747. #if 10 * TCL_MAJOR_VERSION + TCL_MINOR_VERSION < 85
  748. Tcl_AppInitProc Pwb_Init;
  749. #endif
  750. #if defined TCL_THREADS && defined KIT_INCLUDES_THREAD
  751. Tcl_AppInitProc Thread_Init;
  752. #endif
  753. #ifdef _WIN32
  754. Tcl_AppInitProc Dde_Init, Registry_Init;
  755. #endif
  756. static const char *tcltklib_filepath = "[info nameofexecutable]";
  757. static char *rubytkkit_preInitCmd = (char *)NULL;
  758. static const char *rubytkkit_preInitCmd_head = "set ::rubytkkit_exe [list ";
  759. static const char *rubytkkit_preInitCmd_tail =
  760. "]\n"
  761. /*=== following init scripts are quoted from kitInit.c of Tclkit ===*/
  762. /* Tclkit license terms ---
  763. LICENSE
  764. The Tclkit-specific sources are license free, they just have a copyright.
  765. Hold the author(s) harmless and any lawful use is permitted.
  766. This does *not* apply to any of the sources of the other major Open Source
  767. Software used in Tclkit, which each have very liberal BSD/MIT-like licenses:
  768. Tcl/Tk, Incrtcl, Metakit, TclVFS, Zlib
  769. */
  770. #ifdef _WIN32_WCE
  771. /* silly hack to get wince port to launch, some sort of std{in,out,err} problem
  772. */
  773. "open /kitout.txt a; open /kitout.txt a; open /kitout.txt a\n"
  774. /* this too seems to be needed on wince - it appears to be related to the above
  775. */
  776. "catch {rename source ::tcl::source}\n"
  777. "proc source file {\n"
  778. "set old [info script]\n"
  779. "info script $file\n"
  780. "set fid [open $file]\n"
  781. "set data [read $fid]\n"
  782. "close $fid\n"
  783. "set code [catch {uplevel 1 $data} res]\n"
  784. "info script $old\n"
  785. "if {$code == 2} { set code 0 }\n"
  786. "return -code $code $res\n"
  787. "}\n"
  788. #endif
  789. "proc tclKitInit {} {\n"
  790. "rename tclKitInit {}\n"
  791. "load {} Mk4tcl\n"
  792. #if defined KIT_VFS_WRITABLE && !defined CREATE_RUBYKIT
  793. /* running command cannot open itself for writing */
  794. "mk::file open exe $::rubytkkit_exe\n"
  795. #else
  796. "mk::file open exe $::rubytkkit_exe -readonly\n"
  797. #endif
  798. "set n [mk::select exe.dirs!0.files name boot.tcl]\n"
  799. "if {$n != \"\"} {\n"
  800. "set s [mk::get exe.dirs!0.files!$n contents]\n"
  801. "if {![string length $s]} { error \"empty boot.tcl\" }\n"
  802. "catch {load {} zlib}\n"
  803. "if {[mk::get exe.dirs!0.files!$n size] != [string length $s]} {\n"
  804. "set s [zlib decompress $s]\n"
  805. "}\n"
  806. "} else {\n"
  807. "set f [open setup.tcl]\n"
  808. "set s [read $f]\n"
  809. "close $f\n"
  810. "}\n"
  811. "uplevel #0 $s\n"
  812. #ifdef _WIN32
  813. "package ifneeded dde 1.3.1 {load {} dde}\n"
  814. "package ifneeded registry 1.1.5 {load {} registry}\n"
  815. #endif
  816. "}\n"
  817. "tclKitInit"
  818. ;
  819. #if 0
  820. /* Not use this script.
  821. It's a memo to support an initScript for Tcl interpreters in the future. */
  822. static const char initScript[] =
  823. "if {[file isfile [file join $::rubytkkit_exe main.tcl]]} {\n"
  824. "if {[info commands console] != {}} { console hide }\n"
  825. "set tcl_interactive 0\n"
  826. "incr argc\n"
  827. "set argv [linsert $argv 0 $argv0]\n"
  828. "set argv0 [file join $::rubytkkit_exe main.tcl]\n"
  829. "} else continue\n"
  830. ;
  831. #endif
  832. #if !defined(TclSetPreInitScript) && !defined(TclSetPreInitScript_TCL_DECLARED)
  833. EXTERN char* TclSetPreInitScript _((char *));
  834. #endif
  835. static char*
  836. setup_preInitCmd(const char *path)
  837. {
  838. int head_len, path_len, tail_len;
  839. char *ptr;
  840. head_len = strlen(rubytkkit_preInitCmd_head);
  841. path_len = strlen(path);
  842. tail_len = strlen(rubytkkit_preInitCmd_tail);
  843. rubytkkit_preInitCmd = ALLOC_N(char, head_len + path_len + tail_len + 1);
  844. ptr = rubytkkit_preInitCmd;
  845. memcpy(ptr, rubytkkit_preInitCmd_head, head_len);
  846. ptr += head_len;
  847. memcpy(ptr, path, path_len);
  848. ptr += path_len;
  849. memcpy(ptr, rubytkkit_preInitCmd_tail, tail_len);
  850. ptr += tail_len;
  851. *ptr = '\0';
  852. return TclSetPreInitScript(rubytkkit_preInitCmd);
  853. }
  854. static void
  855. init_static_tcltk_packages()
  856. {
  857. #ifdef KIT_INCLUDES_ITCL
  858. Tcl_StaticPackage(0, "Itcl", Itcl_Init, NULL);
  859. #endif
  860. Tcl_StaticPackage(0, "Mk4tcl", Mk4tcl_Init, NULL);
  861. #if 10 * TCL_MAJOR_VERSION + TCL_MINOR_VERSION < 85
  862. Tcl_StaticPackage(0, "pwb", Pwb_Init, NULL);
  863. #endif
  864. Tcl_StaticPackage(0, "rechan", Rechan_Init, NULL);
  865. Tcl_StaticPackage(0, "vfs", Vfs_Init, NULL);
  866. Tcl_StaticPackage(0, "zlib", Zlib_Init, NULL);
  867. #if defined TCL_THREADS && defined KIT_INCLUDES_THREAD
  868. Tcl_StaticPackage(0, "Thread", Thread_Init, NULL);
  869. #endif
  870. #ifdef _WIN32
  871. Tcl_StaticPackage(0, "dde", Dde_Init, NULL);
  872. Tcl_StaticPackage(0, "registry", Registry_Init, NULL);
  873. #endif
  874. #ifdef KIT_INCLUDES_TK
  875. Tcl_StaticPackage(0, "Tk", Tk_Init, Tk_SafeInit);
  876. #endif
  877. }
  878. /* SetExecName -- Hack to get around Tcl bug 1224888. */
  879. void SetExecName(Tcl_Interp *interp) {
  880. /* dummy */
  881. }
  882. #endif /* defined CREATE_RUBYTK_KIT || defined CREATE_RUBYKIT */
  883. static int
  884. call_tclkit_init_script(Tcl_Interp *interp)
  885. {
  886. #if 0
  887. /* Currently, nothing do in this function.
  888. It's a memo (quoted from kitInit.c of Tclkit)
  889. to support an initScript for Tcl interpreters in the future. */
  890. if (Tcl_Eval(interp, initScript) == TCL_OK) {
  891. Tcl_Obj* path = TclGetStartupScriptPath();
  892. TclSetStartupScriptPath(Tcl_GetObjResult(interp));
  893. if (path == NULL)
  894. Tcl_Eval(interp, "incr argc -1; set argv [lrange $argv 1 end]");
  895. }
  896. #endif
  897. return 1;
  898. }
  899. /**********************************************************************/
  900. /* stub status */
  901. static void
  902. tcl_stubs_check()
  903. {
  904. if (!tcl_stubs_init_p()) {
  905. int st = ruby_tcl_stubs_init();
  906. switch(st) {
  907. case TCLTK_STUBS_OK:
  908. break;
  909. case NO_TCL_DLL:
  910. rb_raise(rb_eLoadError, "tcltklib: fail to open tcl_dll");
  911. case NO_FindExecutable:
  912. rb_raise(rb_eLoadError, "tcltklib: can't find Tcl_FindExecutable");
  913. case NO_CreateInterp:
  914. rb_raise(rb_eLoadError, "tcltklib: can't find Tcl_CreateInterp()");
  915. case NO_DeleteInterp:
  916. rb_raise(rb_eLoadError, "tcltklib: can't find Tcl_DeleteInterp()");
  917. case FAIL_CreateInterp:
  918. rb_raise(rb_eRuntimeError, "tcltklib: fail to create a new IP to call Tcl_InitStubs()");
  919. case FAIL_Tcl_InitStubs:
  920. rb_raise(rb_eRuntimeError, "tcltklib: fail to Tcl_InitStubs()");
  921. default:
  922. rb_raise(rb_eRuntimeError, "tcltklib: unknown error(%d) on ruby_tcl_stubs_init()", st);
  923. }
  924. }
  925. }
  926. static VALUE
  927. tcltkip_init_tk(interp)
  928. VALUE interp;
  929. {
  930. struct tcltkip *ptr = get_ip(interp);
  931. #if TCL_MAJOR_VERSION >= 8
  932. int st;
  933. if (Tcl_IsSafe(ptr->ip)) {
  934. DUMP1("Tk_SafeInit");
  935. st = ruby_tk_stubs_safeinit(ptr->ip);
  936. switch(st) {
  937. case TCLTK_STUBS_OK:
  938. break;
  939. case NO_Tk_Init:
  940. return rb_exc_new2(rb_eLoadError,
  941. "tcltklib: can't find Tk_SafeInit()");
  942. case FAIL_Tk_Init:
  943. return create_ip_exc(interp, rb_eRuntimeError,
  944. "tcltklib: fail to Tk_SafeInit(). %s",
  945. Tcl_GetStringResult(ptr->ip));
  946. case FAIL_Tk_InitStubs:
  947. return create_ip_exc(interp, rb_eRuntimeError,
  948. "tcltklib: fail to Tk_InitStubs(). %s",
  949. Tcl_GetStringResult(ptr->ip));
  950. default:
  951. return create_ip_exc(interp, rb_eRuntimeError,
  952. "tcltklib: unknown error(%d) on ruby_tk_stubs_safeinit", st);
  953. }
  954. } else {
  955. DUMP1("Tk_Init");
  956. st = ruby_tk_stubs_init(ptr->ip);
  957. switch(st) {
  958. case TCLTK_STUBS_OK:
  959. break;
  960. case NO_Tk_Init:
  961. return rb_exc_new2(rb_eLoadError,
  962. "tcltklib: can't find Tk_Init()");
  963. case FAIL_Tk_Init:
  964. return create_ip_exc(interp, rb_eRuntimeError,
  965. "tcltklib: fail to Tk_Init(). %s",
  966. Tcl_GetStringResult(ptr->ip));
  967. case FAIL_Tk_InitStubs:
  968. return create_ip_exc(interp, rb_eRuntimeError,
  969. "tcltklib: fail to Tk_InitStubs(). %s",
  970. Tcl_GetStringResult(ptr->ip));
  971. default:
  972. return create_ip_exc(interp, rb_eRuntimeError,
  973. "tcltklib: unknown error(%d) on ruby_tk_stubs_init", st);
  974. }
  975. }
  976. #else /* TCL_MAJOR_VERSION < 8 */
  977. DUMP1("Tk_Init");
  978. if (ruby_tk_stubs_init(ptr->ip) != TCLTK_STUBS_OK) {
  979. return rb_exc_new2(rb_eRuntimeError, ptr->ip->result);
  980. }
  981. #endif
  982. #ifdef RUBY_USE_NATIVE_THREAD
  983. ptr->tk_thread_id = Tcl_GetCurrentThread();
  984. #endif
  985. return Qnil;
  986. }
  987. /* treat excetiopn on Tcl side */
  988. static VALUE rbtk_pending_exception;
  989. static int rbtk_eventloop_depth = 0;
  990. static int rbtk_internal_eventloop_handler = 0;
  991. static int
  992. pending_exception_check0()
  993. {
  994. volatile VALUE exc = rbtk_pending_exception;
  995. if (!NIL_P(exc) && rb_obj_is_kind_of(exc, rb_eException)) {
  996. DUMP1("find a pending exception");
  997. if (rbtk_eventloop_depth > 0
  998. || rbtk_internal_eventloop_handler > 0
  999. ) {
  1000. return 1; /* pending */
  1001. } else {
  1002. rbtk_pending_exception = Qnil;
  1003. if (rb_obj_is_kind_of(exc, eTkCallbackRetry)) {
  1004. DUMP1("pending_exception_check0: call rb_jump_tag(retry)");
  1005. rb_jump_tag(TAG_RETRY);
  1006. } else if (rb_obj_is_kind_of(exc, eTkCallbackRedo)) {
  1007. DUMP1("pending_exception_check0: call rb_jump_tag(redo)");
  1008. rb_jump_tag(TAG_REDO);
  1009. } else if (rb_obj_is_kind_of(exc, eTkCallbackThrow)) {
  1010. DUMP1("pending_exception_check0: call rb_jump_tag(throw)");
  1011. rb_jump_tag(TAG_THROW);
  1012. }
  1013. rb_exc_raise(exc);
  1014. }
  1015. } else {
  1016. return 0;
  1017. }
  1018. }
  1019. static int
  1020. pending_exception_check1(thr_crit_bup, ptr)
  1021. int thr_crit_bup;
  1022. struct tcltkip *ptr;
  1023. {
  1024. volatile VALUE exc = rbtk_pending_exception;
  1025. if (!NIL_P(exc) && rb_obj_is_kind_of(exc, rb_eException)) {
  1026. DUMP1("find a pending exception");
  1027. if (rbtk_eventloop_depth > 0
  1028. || rbtk_internal_eventloop_handler > 0
  1029. ) {
  1030. return 1; /* pending */
  1031. } else {
  1032. rbtk_pending_exception = Qnil;
  1033. if (ptr != (struct tcltkip *)NULL) {
  1034. /* Tcl_Release(ptr->ip); */
  1035. rbtk_release_ip(ptr);
  1036. }
  1037. rb_thread_critical = thr_crit_bup;
  1038. if (rb_obj_is_kind_of(exc, eTkCallbackRetry)) {
  1039. DUMP1("pending_exception_check1: call rb_jump_tag(retry)");
  1040. rb_jump_tag(TAG_RETRY);
  1041. } else if (rb_obj_is_kind_of(exc, eTkCallbackRedo)) {
  1042. DUMP1("pending_exception_check1: call rb_jump_tag(redo)");
  1043. rb_jump_tag(TAG_REDO);
  1044. } else if (rb_obj_is_kind_of(exc, eTkCallbackThrow)) {
  1045. DUMP1("pending_exception_check1: call rb_jump_tag(throw)");
  1046. rb_jump_tag(TAG_THROW);
  1047. }
  1048. rb_exc_raise(exc);
  1049. }
  1050. } else {
  1051. return 0;
  1052. }
  1053. }
  1054. /* call original 'exit' command */
  1055. static void
  1056. call_original_exit(ptr, state)
  1057. struct tcltkip *ptr;
  1058. int state;
  1059. {
  1060. int thr_crit_bup;
  1061. Tcl_CmdInfo *info;
  1062. #if TCL_MAJOR_VERSION >= 8
  1063. Tcl_Obj *cmd_obj;
  1064. Tcl_Obj *state_obj;
  1065. #endif
  1066. DUMP1("original_exit is called");
  1067. if (!(ptr->has_orig_exit)) return;
  1068. thr_crit_bup = rb_thread_critical;
  1069. rb_thread_critical = Qtrue;
  1070. Tcl_ResetResult(ptr->ip);
  1071. info = &(ptr->orig_exit_info);
  1072. /* memory allocation for arguments of this command */
  1073. #if TCL_MAJOR_VERSION >= 8
  1074. state_obj = Tcl_NewIntObj(state);
  1075. Tcl_IncrRefCount(state_obj);
  1076. if (info->isNativeObjectProc) {
  1077. Tcl_Obj **argv;
  1078. #define USE_RUBY_ALLOC 0
  1079. #if USE_RUBY_ALLOC
  1080. argv = (Tcl_Obj **)ALLOC_N(Tcl_Obj *, 3);
  1081. #else /* not USE_RUBY_ALLOC */
  1082. argv = (Tcl_Obj **)ckalloc(sizeof(Tcl_Obj *) * 3);
  1083. #if 0 /* use Tcl_Preserve/Release */
  1084. Tcl_Preserve((ClientData)argv); /* XXXXXXXX */
  1085. #endif
  1086. #endif
  1087. cmd_obj = Tcl_NewStringObj("exit", 4);
  1088. Tcl_IncrRefCount(cmd_obj);
  1089. argv[0] = cmd_obj;
  1090. argv[1] = state_obj;
  1091. argv[2] = (Tcl_Obj *)NULL;
  1092. ptr->return_value
  1093. = (*(info->objProc))(info->objClientData, ptr->ip, 2, argv);
  1094. Tcl_DecrRefCount(cmd_obj);
  1095. #if USE_RUBY_ALLOC
  1096. xfree(argv);
  1097. #else /* not USE_RUBY_ALLOC */
  1098. #if 0 /* use Tcl_EventuallyFree */
  1099. Tcl_EventuallyFree((ClientData)argv, TCL_DYNAMIC); /* XXXXXXXX */
  1100. #else
  1101. #if 0 /* use Tcl_Preserve/Release */
  1102. Tcl_Release((ClientData)argv); /* XXXXXXXX */
  1103. #else
  1104. /* free(argv); */
  1105. ckfree((char*)argv);
  1106. #endif
  1107. #endif
  1108. #endif
  1109. #undef USE_RUBY_ALLOC
  1110. } else {
  1111. /* string interface */
  1112. CONST84 char **argv;
  1113. #define USE_RUBY_ALLOC 0
  1114. #if USE_RUBY_ALLOC
  1115. argv = ALLOC_N(char *, 3); /* XXXXXXXXXX */
  1116. #else /* not USE_RUBY_ALLOC */
  1117. argv = (CONST84 char **)ckalloc(sizeof(char *) * 3);
  1118. #if 0 /* use Tcl_Preserve/Release */
  1119. Tcl_Preserve((ClientData)argv); /* XXXXXXXX */
  1120. #endif
  1121. #endif
  1122. argv[0] = "exit";
  1123. /* argv[1] = Tcl_GetString(state_obj); */
  1124. argv[1] = Tcl_GetStringFromObj(state_obj, (int*)NULL);
  1125. argv[2] = (char *)NULL;
  1126. ptr->return_value = (*(info->proc))(info->clientData, ptr->ip, 2, argv);
  1127. #if USE_RUBY_ALLOC
  1128. xfree(argv);
  1129. #else /* not USE_RUBY_ALLOC */
  1130. #if 0 /* use Tcl_EventuallyFree */
  1131. Tcl_EventuallyFree((ClientData)argv, TCL_DYNAMIC); /* XXXXXXXX */
  1132. #else
  1133. #if 0 /* use Tcl_Preserve/Release */
  1134. Tcl_Release((ClientData)argv); /* XXXXXXXX */
  1135. #else
  1136. /* free(argv); */
  1137. ckfree((char*)argv);
  1138. #endif
  1139. #endif
  1140. #endif
  1141. #undef USE_RUBY_ALLOC
  1142. }
  1143. Tcl_DecrRefCount(state_obj);
  1144. #else /* TCL_MAJOR_VERSION < 8 */
  1145. {
  1146. /* string interface */
  1147. char **argv;
  1148. #define USE_RUBY_ALLOC 0
  1149. #if USE_RUBY_ALLOC
  1150. argv = (char **)ALLOC_N(char *, 3);
  1151. #else /* not USE_RUBY_ALLOC */
  1152. argv = (char **)ckalloc(sizeof(char *) * 3);
  1153. #if 0 /* use Tcl_Preserve/Release */
  1154. Tcl_Preserve((ClientData)argv); /* XXXXXXXX */
  1155. #endif
  1156. #endif
  1157. argv[0] = "exit";
  1158. argv[1] = RSTRING_PTR(rb_fix2str(INT2NUM(state), 10));
  1159. argv[2] = (char *)NULL;
  1160. ptr->return_value = (*(info->proc))(info->clientData, ptr->ip,
  1161. 2, argv);
  1162. #if USE_RUBY_ALLOC
  1163. xfree(argv);
  1164. #else /* not USE_RUBY_ALLOC */
  1165. #if 0 /* use Tcl_EventuallyFree */
  1166. Tcl_EventuallyFree((ClientData)argv, TCL_DYNAMIC); /* XXXXXXXX */
  1167. #else
  1168. #if 0 /* use Tcl_Preserve/Release */
  1169. Tcl_Release((ClientData)argv); /* XXXXXXXX */
  1170. #else
  1171. /* free(argv); */
  1172. ckfree(argv);
  1173. #endif
  1174. #endif
  1175. #endif
  1176. #undef USE_RUBY_ALLOC
  1177. }
  1178. #endif
  1179. DUMP1("complete original_exit");
  1180. rb_thread_critical = thr_crit_bup;
  1181. }
  1182. /* Tk_ThreadTimer */
  1183. static Tcl_TimerToken timer_token = (Tcl_TimerToken)NULL;
  1184. /* timer callback */
  1185. static void _timer_for_tcl _((ClientData));
  1186. static void
  1187. _timer_for_tcl(clientData)
  1188. ClientData clientData;
  1189. {
  1190. int thr_crit_bup;
  1191. /* struct invoke_queue *q, *tmp; */
  1192. /* VALUE thread; */
  1193. DUMP1("call _timer_for_tcl");
  1194. thr_crit_bup = rb_thread_critical;
  1195. rb_thread_critical = Qtrue;
  1196. Tcl_DeleteTimerHandler(timer_token);
  1197. run_timer_flag = 1;
  1198. if (timer_tick > 0) {
  1199. timer_token = Tcl_CreateTimerHandler(timer_tick, _timer_for_tcl,
  1200. (ClientData)0);
  1201. } else {
  1202. timer_token = (Tcl_TimerToken)NULL;
  1203. }
  1204. rb_thread_critical = thr_crit_bup;
  1205. /* rb_thread_schedule(); */
  1206. /* tick_counter += event_loop_max; */
  1207. }
  1208. #ifdef RUBY_USE_NATIVE_THREAD
  1209. #if USE_TOGGLE_WINDOW_MODE_FOR_IDLE
  1210. static int
  1211. toggle_eventloop_window_mode_for_idle()
  1212. {
  1213. if (window_event_mode & TCL_IDLE_EVENTS) {
  1214. /* idle -> event */
  1215. window_event_mode |= TCL_WINDOW_EVENTS;
  1216. window_event_mode &= ~TCL_IDLE_EVENTS;
  1217. return 1;
  1218. } else {
  1219. /* event -> idle */
  1220. window_event_mode |= TCL_IDLE_EVENTS;
  1221. window_event_mode &= ~TCL_WINDOW_EVENTS;
  1222. return 0;
  1223. }
  1224. }
  1225. #endif
  1226. #endif
  1227. static VALUE
  1228. set_eventloop_window_mode(self, mode)
  1229. VALUE self;
  1230. VALUE mode;
  1231. {
  1232. rb_secure(4);
  1233. if (RTEST(mode)) {
  1234. window_event_mode = ~0;
  1235. } else {
  1236. window_event_mode = ~TCL_WINDOW_EVENTS;
  1237. }
  1238. return mode;
  1239. }
  1240. static VALUE
  1241. get_eventloop_window_mode(self)
  1242. VALUE self;
  1243. {
  1244. if ( ~window_event_mode ) {
  1245. return Qfalse;
  1246. } else {
  1247. return Qtrue;
  1248. }
  1249. }
  1250. static VALUE
  1251. set_eventloop_tick(self, tick)
  1252. VALUE self;
  1253. VALUE tick;
  1254. {
  1255. int ttick = NUM2INT(tick);
  1256. int thr_crit_bup;
  1257. rb_secure(4);
  1258. if (ttick < 0) {
  1259. rb_raise(rb_eArgError,
  1260. "timer-tick parameter must be 0 or positive number");
  1261. }
  1262. thr_crit_bup = rb_thread_critical;
  1263. rb_thread_critical = Qtrue;
  1264. /* delete old timer callback */
  1265. Tcl_DeleteTimerHandler(timer_token);
  1266. timer_tick = req_timer_tick = ttick;
  1267. if (timer_tick > 0) {
  1268. /* start timer callback */
  1269. timer_token = Tcl_CreateTimerHandler(timer_tick, _timer_for_tcl,
  1270. (ClientData)0);
  1271. } else {
  1272. timer_token = (Tcl_TimerToken)NULL;
  1273. }
  1274. rb_thread_critical = thr_crit_bup;
  1275. return tick;
  1276. }
  1277. static VALUE
  1278. get_eventloop_tick(self)
  1279. VALUE self;
  1280. {
  1281. return INT2NUM(timer_tick);
  1282. }
  1283. static VALUE
  1284. ip_set_eventloop_tick(self, tick)
  1285. VALUE self;
  1286. VALUE tick;
  1287. {
  1288. struct tcltkip *ptr = get_ip(self);
  1289. /* ip is deleted? */
  1290. if (deleted_ip(ptr)) {
  1291. return get_eventloop_tick(self);
  1292. }
  1293. if (Tcl_GetMaster(ptr->ip) != (Tcl_Interp*)NULL) {
  1294. /* slave IP */
  1295. return get_eventloop_tick(self);
  1296. }
  1297. return set_eventloop_tick(self, tick);
  1298. }
  1299. static VALUE
  1300. ip_get_eventloop_tick(self)
  1301. VALUE self;
  1302. {
  1303. return get_eventloop_tick(self);
  1304. }
  1305. static VALUE
  1306. set_no_event_wait(self, wait)
  1307. VALUE self;
  1308. VALUE wait;
  1309. {
  1310. int t_wait = NUM2INT(wait);
  1311. rb_secure(4);
  1312. if (t_wait <= 0) {
  1313. rb_raise(rb_eArgError,
  1314. "no_event_wait parameter must be positive number");
  1315. }
  1316. no_event_wait = t_wait;
  1317. return wait;
  1318. }
  1319. static VALUE
  1320. get_no_event_wait(self)
  1321. VALUE self;
  1322. {
  1323. return INT2NUM(no_event_wait);
  1324. }
  1325. static VALUE
  1326. ip_set_no_event_wait(self, wait)
  1327. VALUE self;
  1328. VALUE wait;
  1329. {
  1330. struct tcltkip *ptr = get_ip(self);
  1331. /* ip is deleted? */
  1332. if (deleted_ip(ptr)) {
  1333. return get_no_event_wait(self);
  1334. }
  1335. if (Tcl_GetMaster(ptr->ip) != (Tcl_Interp*)NULL) {
  1336. /* slave IP */
  1337. return get_no_event_wait(self);
  1338. }
  1339. return set_no_event_wait(self, wait);
  1340. }
  1341. static VALUE
  1342. ip_get_no_event_wait(self)
  1343. VALUE self;
  1344. {
  1345. return get_no_event_wait(self);
  1346. }
  1347. static VALUE
  1348. set_eventloop_weight(self, loop_max, no_event)
  1349. VALUE self;
  1350. VALUE loop_max;
  1351. VALUE no_event;
  1352. {
  1353. int lpmax = NUM2INT(loop_max);
  1354. int no_ev = NUM2INT(no_event);
  1355. rb_secure(4);
  1356. if (lpmax <= 0 || no_ev <= 0) {
  1357. rb_raise(rb_eArgError, "weight parameters must be positive numbers");
  1358. }
  1359. event_loop_max = lpmax;
  1360. no_event_tick = no_ev;
  1361. return rb_ary_new3(2, loop_max, no_event);
  1362. }
  1363. static VALUE
  1364. get_eventloop_weight(self)
  1365. VALUE self;
  1366. {
  1367. return rb_ary_new3(2, INT2NUM(event_loop_max), INT2NUM(no_event_tick));
  1368. }
  1369. static VALUE
  1370. ip_set_eventloop_weight(self, loop_max, no_event)
  1371. VALUE self;
  1372. VALUE loop_max;
  1373. VALUE no_event;
  1374. {
  1375. struct tcltkip *ptr = get_ip(self);
  1376. /* ip is deleted? */
  1377. if (deleted_ip(ptr)) {
  1378. return get_eventloop_weight(self);
  1379. }
  1380. if (Tcl_GetMaster(ptr->ip) != (Tcl_Interp*)NULL) {
  1381. /* slave IP */
  1382. return get_eventloop_weight(self);
  1383. }
  1384. return set_eventloop_weight(self, loop_max, no_event);
  1385. }
  1386. static VALUE
  1387. ip_get_eventloop_weight(self)
  1388. VALUE self;
  1389. {
  1390. return get_eventloop_weight(self);
  1391. }
  1392. static VALUE
  1393. set_max_block_time(self, time)
  1394. VALUE self;
  1395. VALUE time;
  1396. {
  1397. struct Tcl_Time tcl_time;
  1398. VALUE divmod;
  1399. switch(TYPE(time)) {
  1400. case T_FIXNUM:
  1401. case T_BIGNUM:
  1402. /* time is micro-second value */
  1403. divmod = rb_funcall(time, rb_intern("divmod"), 1, LONG2NUM(1000000));
  1404. tcl_time.sec = NUM2LONG(RARRAY_PTR(divmod)[0]);
  1405. tcl_time.usec = NUM2LONG(RARRAY_PTR(divmod)[1]);
  1406. break;
  1407. case T_FLOAT:
  1408. /* time is second value */
  1409. divmod = rb_funcall(time, rb_intern("divmod"), 1, INT2FIX(1));
  1410. tcl_time.sec = NUM2LONG(RARRAY_PTR(divmod)[0]);
  1411. tcl_time.usec = (long)(NUM2DBL(RARRAY_PTR(divmod)[1]) * 1000000);
  1412. default:
  1413. {
  1414. VALUE tmp = rb_funcall(time, ID_inspect, 0, 0);
  1415. rb_raise(rb_eArgError, "invalid value for time: '%s'",
  1416. StringValuePtr(tmp));
  1417. }
  1418. }
  1419. Tcl_SetMaxBlockTime(&tcl_time);
  1420. return Qnil;
  1421. }
  1422. static VALUE
  1423. lib_evloop_thread_p(self)
  1424. VALUE self;
  1425. {
  1426. if (NIL_P(eventloop_thread)) {
  1427. return Qnil; /* no eventloop */
  1428. } else if (rb_thread_current() == eventloop_thread) {
  1429. return Qtrue; /* is eventloop */
  1430. } else {
  1431. return Qfalse; /* not eventloop */
  1432. }
  1433. }
  1434. static VALUE
  1435. lib_evloop_abort_on_exc(self)
  1436. VALUE self;
  1437. {
  1438. if (event_loop_abort_on_exc > 0) {
  1439. return Qtrue;
  1440. } else if (event_loop_abort_on_exc == 0) {
  1441. return Qfalse;
  1442. } else {
  1443. return Qnil;
  1444. }
  1445. }
  1446. static VALUE
  1447. ip_evloop_abort_on_exc(self)
  1448. VALUE self;
  1449. {
  1450. return lib_evloop_abort_on_exc(self);
  1451. }
  1452. static VALUE
  1453. lib_evloop_abort_on_exc_set(self, val)
  1454. VALUE self, val;
  1455. {
  1456. rb_secure(4);
  1457. if (RTEST(val)) {
  1458. event_loop_abort_on_exc = 1;
  1459. } else if (NIL_P(val)) {
  1460. event_loop_abort_on_exc = -1;
  1461. } else {
  1462. event_loop_abort_on_exc = 0;
  1463. }
  1464. return lib_evloop_abort_on_exc(self);
  1465. }
  1466. static VALUE
  1467. ip_evloop_abort_on_exc_set(self, val)
  1468. VALUE self, val;
  1469. {
  1470. struct tcltkip *ptr = get_ip(self);
  1471. rb_secure(4);
  1472. /* ip is deleted? */
  1473. if (deleted_ip(ptr)) {
  1474. return lib_evloop_abort_on_exc(self);
  1475. }
  1476. if (Tcl_GetMaster(ptr->ip) != (Tcl_Interp*)NULL) {
  1477. /* slave IP */
  1478. return lib_evloop_abort_on_exc(self);
  1479. }
  1480. return lib_evloop_abort_on_exc_set(self, val);
  1481. }
  1482. static VALUE
  1483. lib_num_of_mainwindows_core(self, argc, argv)
  1484. VALUE self;
  1485. int argc; /* dummy */
  1486. VALUE *argv; /* dummy */
  1487. {
  1488. if (tk_stubs_init_p()) {
  1489. return INT2FIX(Tk_GetNumMainWindows());
  1490. } else {
  1491. return INT2FIX(0);
  1492. }
  1493. }
  1494. static VALUE
  1495. lib_num_of_mainwindows(self)
  1496. VALUE self;
  1497. {
  1498. #ifdef RUBY_USE_NATIVE_THREAD /* Ruby 1.9+ !!! */
  1499. return tk_funcall(lib_num_of_mainwindows_core, 0, (VALUE*)NULL, self);
  1500. #else
  1501. return lib_num_of_mainwindows_core(self, 0, (VALUE*)NULL);
  1502. #endif
  1503. }
  1504. #ifdef RUBY_USE_NATIVE_THREAD /* Ruby 1.9+ !!! */
  1505. static VALUE
  1506. #ifdef HAVE_PROTOTYPES
  1507. call_DoOneEvent_core(VALUE flag_val)
  1508. #else
  1509. call_DoOneEvent_core(flag_val)
  1510. VALUE flag_val;
  1511. #endif
  1512. {
  1513. int flag;
  1514. flag = FIX2INT(flag_val);
  1515. if (Tcl_DoOneEvent(flag)) {
  1516. return Qtrue;
  1517. } else {
  1518. return Qfalse;
  1519. }
  1520. }
  1521. static VALUE
  1522. #ifdef HAVE_PROTOTYPES
  1523. call_DoOneEvent(VALUE flag_val)
  1524. #else
  1525. call_DoOneEvent(flag_val)
  1526. VALUE flag_val;
  1527. #endif
  1528. {
  1529. return tk_funcall(call_DoOneEvent_core, 0, (VALUE*)NULL, flag_val);
  1530. }
  1531. #else /* Ruby 1.8- */
  1532. static VALUE
  1533. #ifdef HAVE_PROTOTYPES
  1534. call_DoOneEvent(VALUE flag_val)
  1535. #else
  1536. call_DoOneEvent(flag_val)
  1537. VALUE flag_val;
  1538. #endif
  1539. {
  1540. int flag;
  1541. flag = FIX2INT(flag_val);
  1542. if (Tcl_DoOneEvent(flag)) {
  1543. return Qtrue;
  1544. } else {
  1545. return Qfalse;
  1546. }
  1547. }
  1548. #endif
  1549. static VALUE
  1550. #ifdef HAVE_PROTOTYPES
  1551. eventloop_sleep(VALUE dummy)
  1552. #else
  1553. eventloop_sleep(dummy)
  1554. VALUE dummy;
  1555. #endif
  1556. {
  1557. struct timeval t;
  1558. if (no_event_wait <= 0) {
  1559. return Qnil;
  1560. }
  1561. t.tv_sec = 0;
  1562. t.tv_usec = (long)(no_event_wait*1000.0);
  1563. #ifdef HAVE_NATIVETHREAD
  1564. #ifndef RUBY_USE_NATIVE_THREAD
  1565. if (!ruby_native_thread_p()) {
  1566. rb_bug("cross-thread violation on eventloop_sleep()");
  1567. }
  1568. #endif
  1569. #endif
  1570. DUMP2("eventloop_sleep: rb_thread_wait_for() at thread : %lx", rb_thread_current());
  1571. rb_thread_wait_for(t);
  1572. DUMP2("eventloop_sleep: finish at thread : %lx", rb_thread_current());
  1573. #ifdef HAVE_NATIVETHREAD
  1574. #ifndef RUBY_USE_NATIVE_THREAD
  1575. if (!ruby_native_thread_p()) {
  1576. rb_bug("cross-thread violation on eventloop_sleep()");
  1577. }
  1578. #endif
  1579. #endif
  1580. return Qnil;
  1581. }
  1582. #define USE_EVLOOP_THREAD_ALONE_CHECK_FLAG 0
  1583. #if USE_EVLOOP_THREAD_ALONE_CHECK_FLAG
  1584. static int
  1585. get_thread_alone_check_flag()
  1586. {
  1587. #ifdef RUBY_USE_NATIVE_THREAD
  1588. return 0;
  1589. #else
  1590. set_tcltk_version();
  1591. if (tcltk_version.major < 8) {
  1592. /* Tcl/Tk 7.x */
  1593. return 1;
  1594. } else if (tcltk_version.major == 8) {
  1595. if (tcltk_version.minor < 5) {
  1596. /* Tcl/Tk 8.0 - 8.4 */
  1597. return 1;
  1598. } else if (tcltk_version.minor == 5) {
  1599. if (tcltk_version.type < TCL_FINAL_RELEASE) {
  1600. /* Tcl/Tk 8.5a? - 8.5b? */
  1601. return 1;
  1602. } else {
  1603. /* Tcl/Tk 8.5.x */
  1604. return 0;
  1605. }
  1606. } else {
  1607. /* Tcl/Tk 8.6 - 8.9 ?? */
  1608. return 0;
  1609. }
  1610. } else {
  1611. /* Tcl/Tk 9+ ?? */
  1612. return 0;
  1613. }
  1614. #endif
  1615. }
  1616. #endif
  1617. #define TRAP_CHECK() do { \
  1618. if (trap_check(check_var) == 0) return 0; \
  1619. } while (0)
  1620. static int
  1621. trap_check(int *check_var)
  1622. {
  1623. DUMP1("trap check");
  1624. #ifdef RUBY_VM
  1625. if (rb_thread_check_trap_pending()) {
  1626. if (check_var != (int*)NULL) {
  1627. /* wait command */
  1628. return 0;
  1629. }
  1630. else {
  1631. rb_thread_check_ints();
  1632. }
  1633. }
  1634. #else
  1635. if (rb_trap_pending) {
  1636. run_timer_flag = 0;
  1637. if (rb_prohibit_interrupt || check_var != (int*)NULL) {
  1638. /* pending or on wait command */
  1639. return 0;
  1640. } else {
  1641. rb_trap_exec();
  1642. }
  1643. }
  1644. #endif
  1645. return 1;
  1646. }
  1647. static int
  1648. check_eventloop_interp()
  1649. {
  1650. DUMP1("check eventloop_interp");
  1651. if (eventloop_interp != (Tcl_Interp*)NULL
  1652. && Tcl_InterpDeleted(eventloop_interp)) {
  1653. DUMP2("eventloop_interp(%p) was deleted", eventloop_interp);
  1654. return 1;
  1655. }
  1656. return 0;
  1657. }
  1658. static int
  1659. lib_eventloop_core(check_root, update_flag, check_var, interp)
  1660. int check_root;
  1661. int update_flag;
  1662. int *check_var;
  1663. Tcl_Interp *interp;
  1664. {
  1665. volatile VALUE current = eventloop_thread;
  1666. int found_event = 1;
  1667. int event_flag;
  1668. struct timeval t;
  1669. int thr_crit_bup;
  1670. int status;
  1671. int depth = rbtk_eventloop_depth;
  1672. #if USE_EVLOOP_THREAD_ALONE_CHECK_FLAG
  1673. int thread_alone_check_flag = 1;
  1674. #endif
  1675. if (update_flag) DUMP1("update loop start!!");
  1676. t.tv_sec = 0;
  1677. t.tv_usec = (long)(no_event_wait*1000.0);
  1678. Tcl_DeleteTimerHandler(timer_token);
  1679. run_timer_flag = 0;
  1680. if (timer_tick > 0) {
  1681. thr_crit_bup = rb_thread_critical;
  1682. rb_thread_critical = Qtrue;
  1683. timer_token = Tcl_CreateTimerHandler(timer_tick, _timer_for_tcl,
  1684. (ClientData)0);
  1685. rb_thread_critical = thr_crit_bup;
  1686. } else {
  1687. timer_token = (Tcl_TimerToken)NULL;
  1688. }
  1689. #if USE_EVLOOP_THREAD_ALONE_CHECK_FLAG
  1690. /* version check */
  1691. thread_alone_check_flag = get_thread_alone_check_flag();
  1692. #endif
  1693. for(;;) {
  1694. if (check_eventloop_interp()) return 0;
  1695. #if USE_EVLOOP_THREAD_ALONE_CHECK_FLAG
  1696. if (thread_alone_check_flag && rb_thread_alone()) {
  1697. #else
  1698. if (rb_thread_alone()) {
  1699. #endif
  1700. DUMP1("no other thread");
  1701. event_loop_wait_event = 0;
  1702. if (update_flag) {
  1703. event_flag = update_flag | TCL_DONT_WAIT; /* for safety */
  1704. } else {
  1705. event_flag = TCL_ALL_EVENTS;
  1706. /* event_flag = TCL_ALL_EVENTS | TCL_DONT_WAIT; */
  1707. }
  1708. if (timer_tick == 0 && update_flag == 0) {
  1709. timer_tick = NO_THREAD_INTERRUPT_TIME;
  1710. timer_token = Tcl_CreateTimerHandler(timer_tick,
  1711. _timer_for_tcl,
  1712. (ClientData)0);
  1713. }
  1714. if (check_var != (int *)NULL) {
  1715. if (*check_var || !found_event) {
  1716. return found_event;
  1717. }
  1718. if (interp != (Tcl_Interp*)NULL
  1719. && Tcl_InterpDeleted(interp)) {
  1720. /* IP for check_var is deleted */
  1721. return 0;
  1722. }
  1723. }
  1724. /* found_event = Tcl_DoOneEvent(event_flag); */
  1725. found_event = RTEST(rb_protect(call_DoOneEvent,
  1726. INT2FIX(event_flag), &status));
  1727. if (status) {
  1728. switch (status) {
  1729. case TAG_RAISE:
  1730. if (NIL_P(rb_errinfo())) {
  1731. rbtk_pending_exception
  1732. = rb_exc_new2(rb_eException, "unknown exception");
  1733. } else {
  1734. rbtk_pending_exception = rb_errinfo();
  1735. if (!NIL_P(rbtk_pending_exception)) {
  1736. if (rbtk_eventloop_depth == 0) {
  1737. VALUE exc = rbtk_pending_exception;
  1738. rbtk_pending_exception = Qnil;
  1739. rb_exc_raise(exc);
  1740. } else {
  1741. return 0;
  1742. }
  1743. }
  1744. }
  1745. break;
  1746. case TAG_FATAL:
  1747. if (NIL_P(rb_errinfo())) {
  1748. rb_exc_raise(rb_exc_new2(rb_eFatal, "FATAL"));
  1749. } else {
  1750. rb_exc_raise(rb_errinfo());
  1751. }
  1752. }
  1753. }
  1754. if (depth != rbtk_eventloop_depth) {
  1755. DUMP2("DoOneEvent(1) abnormal exit!! %d",
  1756. rbtk_eventloop_depth);
  1757. }
  1758. if (check_var != (int*)NULL && !NIL_P(rbtk_pending_exception)) {
  1759. DUMP1("exception on wait");
  1760. return 0;
  1761. }
  1762. if (pending_exception_check0()) {
  1763. /* pending -> upper level */
  1764. return 0;
  1765. }
  1766. if (update_flag != 0) {
  1767. if (found_event) {
  1768. DUMP1("next update loop");
  1769. continue;
  1770. } else {
  1771. DUMP1("update complete");
  1772. return 0;
  1773. }
  1774. }
  1775. TRAP_CHECK();
  1776. if (check_eventloop_interp()) return 0;
  1777. DUMP1("check Root Widget");
  1778. if (check_root && tk_stubs_init_p() && Tk_GetNumMainWindows() == 0) {
  1779. run_timer_flag = 0;
  1780. TRAP_CHECK();
  1781. return 1;
  1782. }
  1783. if (loop_counter++ > 30000) {
  1784. /* fprintf(stderr, "loop_counter > 30000\n"); */
  1785. loop_counter = 0;
  1786. }
  1787. } else {
  1788. int tick_counter;
  1789. DUMP1("there are other threads");
  1790. event_loop_wait_event = 1;
  1791. found_event = 1;
  1792. if (update_flag) {
  1793. event_flag = update_flag | TCL_DONT_WAIT; /* for safety */
  1794. } else {
  1795. event_flag = TCL_ALL_EVENTS | TCL_DONT_WAIT;
  1796. }
  1797. timer_tick = req_timer_tick;
  1798. tick_counter = 0;
  1799. while(tick_counter < event_loop_max) {
  1800. if (check_var != (int *)NULL) {
  1801. if (*check_var || !found_event) {
  1802. return found_event;
  1803. }
  1804. if (interp != (Tcl_Interp*)NULL
  1805. && Tcl_InterpDeleted(interp)) {
  1806. /* IP for check_var is deleted */
  1807. return 0;
  1808. }
  1809. }
  1810. if (NIL_P(eventloop_thread) || current == eventloop_thread) {
  1811. int st;
  1812. int status;
  1813. #ifdef RUBY_USE_NATIVE_THREAD
  1814. if (update_flag) {
  1815. st = RTEST(rb_protect(call_DoOneEvent,
  1816. INT2FIX(event_flag), &status));
  1817. } else {
  1818. st = RTEST(rb_protect(call_DoOneEvent,
  1819. INT2FIX(event_flag & window_event_mode),
  1820. &status));
  1821. #if USE_TOGGLE_WINDOW_MODE_FOR_IDLE
  1822. if (!st) {
  1823. if (toggle_eventloop_window_mode_for_idle()) {
  1824. /* idle-mode -> event-mode*/
  1825. tick_counter = event_loop_max;
  1826. } else {
  1827. /* event-mode -> idle-mode */
  1828. tick_counter = 0;
  1829. }
  1830. }
  1831. #endif
  1832. }
  1833. #else
  1834. /* st = Tcl_DoOneEvent(event_flag); */
  1835. st = RTEST(rb_protect(call_DoOneEvent,
  1836. INT2FIX(event_flag), &status));
  1837. #endif
  1838. #if CONTROL_BY_STATUS_OF_RB_THREAD_WAITING_FOR_VALUE
  1839. if (have_rb_thread_waiting_for_value) {
  1840. have_rb_thread_waiting_for_value = 0;
  1841. rb_thread_schedule();
  1842. }
  1843. #endif
  1844. if (status) {
  1845. switch (status) {
  1846. case TAG_RAISE:
  1847. if (NIL_P(rb_errinfo())) {
  1848. rbtk_pending_exception
  1849. = rb_exc_new2(rb_eException,
  1850. "unknown exception");
  1851. } else {
  1852. rbtk_pending_exception = rb_errinfo();
  1853. if (!NIL_P(rbtk_pending_exception)) {
  1854. if (rbtk_eventloop_depth == 0) {
  1855. VALUE exc = rbtk_pending_exception;
  1856. rbtk_pending_exception = Qnil;
  1857. rb_exc_raise(exc);
  1858. } else {
  1859. return 0;
  1860. }
  1861. }
  1862. }
  1863. break;
  1864. case TAG_FATAL:
  1865. if (NIL_P(rb_errinfo())) {
  1866. rb_exc_raise(rb_exc_new2(rb_eFatal, "FATAL"));
  1867. } else {
  1868. rb_exc_raise(rb_errinfo());
  1869. }
  1870. }
  1871. }
  1872. if (depth != rbtk_eventloop_depth) {
  1873. DUMP2("DoOneEvent(2) abnormal exit!! %d",
  1874. rbtk_eventloop_depth);
  1875. return 0;
  1876. }
  1877. TRAP_CHECK();
  1878. if (check_var != (int*)NULL
  1879. && !NIL_P(rbtk_pending_exception)) {
  1880. DUMP1("exception on wait");
  1881. return 0;
  1882. }
  1883. if (pending_exception_check0()) {
  1884. /* pending -> upper level */
  1885. return 0;
  1886. }
  1887. if (st) {
  1888. tick_counter++;
  1889. } else {
  1890. if (update_flag != 0) {
  1891. DUMP1("update complete");
  1892. return 0;
  1893. }
  1894. tick_counter += no_event_tick;
  1895. /* rb_thread_wait_for(t); */
  1896. rb_protect(eventloop_sleep, Qnil, &status);
  1897. if (status) {
  1898. switch (status) {
  1899. case TAG_RAISE:
  1900. if (NIL_P(rb_errinfo())) {
  1901. rbtk_pending_exception
  1902. = rb_exc_new2(rb_eException,
  1903. "unknown exception");
  1904. } else {
  1905. rbtk_pending_exception = rb_errinfo();
  1906. if (!NIL_P(rbtk_pending_exception)) {
  1907. if (rbtk_eventloop_depth == 0) {
  1908. VALUE exc = rbtk_pending_exception;
  1909. rbtk_pending_exception = Qnil;
  1910. rb_exc_raise(exc);
  1911. } else {
  1912. return 0;
  1913. }
  1914. }
  1915. }
  1916. break;
  1917. case TAG_FATAL:
  1918. if (NIL_P(rb_errinfo())) {
  1919. rb_exc_raise(rb_exc_new2(rb_eFatal,
  1920. "FATAL"));
  1921. } else {
  1922. rb_exc_raise(rb_errinfo());
  1923. }
  1924. }
  1925. }
  1926. }
  1927. } else {
  1928. DUMP2("sleep eventloop %lx", current);
  1929. DUMP2("eventloop thread is %lx", eventloop_thread);
  1930. /* rb_thread_stop(); */
  1931. rb_thread_sleep_forever();
  1932. }
  1933. if (!NIL_P(watchdog_thread) && eventloop_thread != current) {
  1934. return 1;
  1935. }
  1936. TRAP_CHECK();
  1937. if (check_eventloop_interp()) return 0;
  1938. DUMP1("check Root Widget");
  1939. if (check_root && tk_stubs_init_p() && Tk_GetNumMainWindows() == 0) {
  1940. run_timer_flag = 0;
  1941. TRAP_CHECK();
  1942. return 1;
  1943. }
  1944. if (loop_counter++ > 30000) {
  1945. /* fprintf(stderr, "loop_counter > 30000\n"); */
  1946. loop_counter = 0;
  1947. }
  1948. if (run_timer_flag) {
  1949. /*
  1950. DUMP1("timer interrupt");
  1951. run_timer_flag = 0;
  1952. */
  1953. break; /* switch to other thread */
  1954. }
  1955. }
  1956. DUMP1("thread scheduling");
  1957. rb_thread_schedule();
  1958. }
  1959. DUMP1("trap check & thread scheduling");
  1960. #ifdef RUBY_USE_NATIVE_THREAD
  1961. /* if (update_flag == 0) CHECK_INTS; */ /*XXXXXXXXXXXXX TODO !!!! */
  1962. #else
  1963. if (update_flag == 0) CHECK_INTS;
  1964. #endif
  1965. }
  1966. return 1;
  1967. }
  1968. struct evloop_params {
  1969. int check_root;
  1970. int update_flag;
  1971. int *check_var;
  1972. Tcl_Interp *interp;
  1973. int thr_crit_bup;
  1974. };
  1975. VALUE
  1976. lib_eventloop_main_core(args)
  1977. VALUE args;
  1978. {
  1979. struct evloop_params *params = (struct evloop_params *)args;
  1980. check_rootwidget_flag = params->check_root;
  1981. if (lib_eventloop_core(params->check_root,
  1982. params->update_flag,
  1983. params->check_var,
  1984. params->interp)) {
  1985. return Qtrue;
  1986. } else {
  1987. return Qfalse;
  1988. }
  1989. }
  1990. VALUE
  1991. lib_eventloop_main(args)
  1992. VALUE args;
  1993. {
  1994. return lib_eventloop_main_core(args);
  1995. #if 0
  1996. volatile VALUE ret;
  1997. int status = 0;
  1998. ret = rb_protect(lib_eventloop_main_core, args, &status);
  1999. switch (status) {
  2000. case TAG_RAISE:
  2001. if (NIL_P(rb_errinfo())) {
  2002. rbtk_pending_exception
  2003. = rb_exc_new2(rb_eException, "unknown exception");
  2004. } else {
  2005. rbtk_pending_exception = rb_errinfo();
  2006. }
  2007. return Qnil;
  2008. case TAG_FATAL:
  2009. if (NIL_P(rb_errinfo())) {
  2010. rbtk_pending_exception = rb_exc_new2(rb_eFatal, "FATAL");
  2011. } else {
  2012. rbtk_pending_exception = rb_errinfo();
  2013. }
  2014. return Qnil;
  2015. }
  2016. return ret;
  2017. #endif
  2018. }
  2019. VALUE
  2020. lib_eventloop_ensure(args)
  2021. VALUE args;
  2022. {
  2023. struct evloop_params *ptr = (struct evloop_params *)args;
  2024. volatile VALUE current_evloop = rb_thread_current();
  2025. DUMP2("eventloop_ensure: current-thread : %lx", current_evloop);
  2026. DUMP2("eventloop_ensure: eventloop-thread : %lx", eventloop_thread);
  2027. if (eventloop_thread != current_evloop) {
  2028. DUMP2("finish eventloop %lx (NOT current eventloop)", current_evloop);
  2029. rb_thread_critical = ptr->thr_crit_bup;
  2030. xfree(ptr);
  2031. /* ckfree((char*)ptr); */
  2032. return Qnil;
  2033. }
  2034. while((eventloop_thread = rb_ary_pop(eventloop_stack))) {
  2035. DUMP2("eventloop-ensure: new eventloop-thread -> %lx",
  2036. eventloop_thread);
  2037. if (eventloop_thread == current_evloop) {
  2038. rbtk_eventloop_depth--;
  2039. DUMP2("eventloop %lx : back from recursive call", current_evloop);
  2040. break;
  2041. }
  2042. if (NIL_P(eventloop_thread)) {
  2043. Tcl_DeleteTimerHandler(timer_token);
  2044. timer_token = (Tcl_TimerToken)NULL;
  2045. break;
  2046. }
  2047. #ifdef RUBY_VM
  2048. if (RTEST(rb_funcall(eventloop_thread, ID_alive_p, 0, 0))) {
  2049. #else
  2050. if (RTEST(rb_thread_alive_p(eventloop_thread))) {
  2051. #endif
  2052. DUMP2("eventloop-enshure: wake up parent %lx", eventloop_thread);
  2053. rb_thread_wakeup(eventloop_thread);
  2054. break;
  2055. }
  2056. }
  2057. #ifdef RUBY_USE_NATIVE_THREAD
  2058. if (NIL_P(eventloop_thread)) {
  2059. tk_eventloop_thread_id = (Tcl_ThreadId) 0;
  2060. }
  2061. #endif
  2062. rb_thread_critical = ptr->thr_crit_bup;
  2063. xfree(ptr);
  2064. /* ckfree((char*)ptr);*/
  2065. DUMP2("finish current eventloop %lx", current_evloop);
  2066. return Qnil;
  2067. }
  2068. static VALUE
  2069. lib_eventloop_launcher(check_root, update_flag, check_var, interp)
  2070. int check_root;
  2071. int update_flag;
  2072. int *check_var;
  2073. Tcl_Interp *interp;
  2074. {
  2075. volatile VALUE parent_evloop = eventloop_thread;
  2076. struct evloop_params *args = ALLOC(struct evloop_params);
  2077. /* struct evloop_params *args = (struct evloop_params *)ckalloc(sizeof(struct evloop_params)); */
  2078. tcl_stubs_check();
  2079. eventloop_thread = rb_thread_current();
  2080. #ifdef RUBY_USE_NATIVE_THREAD
  2081. tk_eventloop_thread_id = Tcl_GetCurrentThread();
  2082. #endif
  2083. if (parent_evloop == eventloop_thread) {
  2084. DUMP2("eventloop: recursive call on %lx", parent_evloop);
  2085. rbtk_eventloop_depth++;
  2086. }
  2087. if (!NIL_P(parent_evloop) && parent_evloop != eventloop_thread) {
  2088. DUMP2("wait for stop of parent_evloop %lx", parent_evloop);
  2089. while(!RTEST(rb_funcall(parent_evloop, ID_stop_p, 0))) {
  2090. DUMP2("parent_evloop %lx doesn't stop", parent_evloop);
  2091. rb_thread_run(parent_evloop);
  2092. }
  2093. DUMP1("succeed to stop parent");
  2094. }
  2095. rb_ary_push(eventloop_stack, parent_evloop);
  2096. DUMP3("tcltklib: eventloop-thread : %lx -> %lx\n",
  2097. parent_evloop, eventloop_thread);
  2098. args->check_root = check_root;
  2099. args->update_flag = update_flag;
  2100. args->check_var = check_var;
  2101. args->interp = interp;
  2102. args->thr_crit_bup = rb_thread_critical;
  2103. rb_thread_critical = Qfalse;
  2104. #if 0
  2105. return rb_ensure(lib_eventloop_main, (VALUE)args,
  2106. lib_eventloop_ensure, (VALUE)args);
  2107. #endif
  2108. return rb_ensure(lib_eventloop_main_core, (VALUE)args,
  2109. lib_eventloop_ensure, (VALUE)args);
  2110. }
  2111. /* execute Tk_MainLoop */
  2112. static VALUE
  2113. lib_mainloop(argc, argv, self)
  2114. int argc;
  2115. VALUE *argv;
  2116. VALUE self;
  2117. {
  2118. VALUE check_rootwidget;
  2119. if (rb_scan_args(argc, argv, "01", &check_rootwidget) == 0) {
  2120. check_rootwidget = Qtrue;
  2121. } else if (RTEST(check_rootwidget)) {
  2122. check_rootwidget = Qtrue;
  2123. } else {
  2124. check_rootwidget = Qfalse;
  2125. }
  2126. return lib_eventloop_launcher(RTEST(check_rootwidget), 0,
  2127. (int*)NULL, (Tcl_Interp*)NULL);
  2128. }
  2129. static VALUE
  2130. ip_mainloop(argc, argv, self)
  2131. int argc;
  2132. VALUE *argv;
  2133. VALUE self;
  2134. {
  2135. volatile VALUE ret;
  2136. struct tcltkip *ptr = get_ip(self);
  2137. /* ip is deleted? */
  2138. if (deleted_ip(ptr)) {
  2139. return Qnil;
  2140. }
  2141. if (Tcl_GetMaster(ptr->ip) != (Tcl_Interp*)NULL) {
  2142. /* slave IP */
  2143. return Qnil;
  2144. }
  2145. eventloop_interp = ptr->ip;
  2146. ret = lib_mainloop(argc, argv, self);
  2147. eventloop_interp = (Tcl_Interp*)NULL;
  2148. return ret;
  2149. }
  2150. static VALUE
  2151. watchdog_evloop_launcher(check_rootwidget)
  2152. VALUE check_rootwidget;
  2153. {
  2154. return lib_eventloop_launcher(RTEST(check_rootwidget), 0,
  2155. (int*)NULL, (Tcl_Interp*)NULL);
  2156. }
  2157. #define EVLOOP_WAKEUP_CHANCE 3
  2158. static VALUE
  2159. lib_watchdog_core(check_rootwidget)
  2160. VALUE check_rootwidget;
  2161. {
  2162. VALUE evloop;
  2163. int prev_val = -1;
  2164. int chance = 0;
  2165. int check = RTEST(check_rootwidget);
  2166. struct timeval t0, t1;
  2167. t0.tv_sec = 0;
  2168. t0.tv_usec = (long)((NO_THREAD_INTERRUPT_TIME)*1000.0);
  2169. t1.tv_sec = 0;
  2170. t1.tv_usec = (long)((WATCHDOG_INTERVAL)*1000.0);
  2171. /* check other watchdog thread */
  2172. if (!NIL_P(watchdog_thread)) {
  2173. if (RTEST(rb_funcall(watchdog_thread, ID_stop_p, 0))) {
  2174. rb_funcall(watchdog_thread, ID_kill, 0);
  2175. } else {
  2176. return Qnil;
  2177. }
  2178. }
  2179. watchdog_thread = rb_thread_current();
  2180. /* watchdog start */
  2181. do {
  2182. if (NIL_P(eventloop_thread)
  2183. || (loop_counter == prev_val && chance >= EVLOOP_WAKEUP_CHANCE)) {
  2184. /* start new eventloop thread */
  2185. DUMP2("eventloop thread %lx is sleeping or dead",
  2186. eventloop_thread);
  2187. evloop = rb_thread_create(watchdog_evloop_launcher,
  2188. (void*)&check_rootwidget);
  2189. DUMP2("create new eventloop thread %lx", evloop);
  2190. loop_counter = -1;
  2191. chance = 0;
  2192. rb_thread_run(evloop);
  2193. } else {
  2194. prev_val = loop_counter;
  2195. if (RTEST(rb_funcall(eventloop_thread, ID_stop_p, 0))) {
  2196. ++chance;
  2197. } else {
  2198. chance = 0;
  2199. }
  2200. if (event_loop_wait_event) {
  2201. rb_thread_wait_for(t0);
  2202. } else {
  2203. rb_thread_wait_for(t1);
  2204. }
  2205. /* rb_thread_schedule(); */
  2206. }
  2207. } while(!check || !tk_stubs_init_p() || Tk_GetNumMainWindows() != 0);
  2208. return Qnil;
  2209. }
  2210. VALUE
  2211. lib_watchdog_ensure(arg)
  2212. VALUE arg;
  2213. {
  2214. eventloop_thread = Qnil; /* stop eventloops */
  2215. #ifdef RUBY_USE_NATIVE_THREAD
  2216. tk_eventloop_thread_id = (Tcl_ThreadId) 0;
  2217. #endif
  2218. return Qnil;
  2219. }
  2220. static VALUE
  2221. lib_mainloop_watchdog(argc, argv, self)
  2222. int argc;
  2223. VALUE *argv;
  2224. VALUE self;
  2225. {
  2226. VALUE check_rootwidget;
  2227. #ifdef RUBY_VM
  2228. rb_raise(rb_eNotImpError,
  2229. "eventloop_watchdog is not implemented on Ruby VM.");
  2230. #endif
  2231. if (rb_scan_args(argc, argv, "01", &check_rootwidget) == 0) {
  2232. check_rootwidget = Qtrue;
  2233. } else if (RTEST(check_rootwidget)) {
  2234. check_rootwidget = Qtrue;
  2235. } else {
  2236. check_rootwidget = Qfalse;
  2237. }
  2238. return rb_ensure(lib_watchdog_core, check_rootwidget,
  2239. lib_watchdog_ensure, Qnil);
  2240. }
  2241. static VALUE
  2242. ip_mainloop_watchdog(argc, argv, self)
  2243. int argc;
  2244. VALUE *argv;
  2245. VALUE self;
  2246. {
  2247. struct tcltkip *ptr = get_ip(self);
  2248. /* ip is deleted? */
  2249. if (deleted_ip(ptr)) {
  2250. return Qnil;
  2251. }
  2252. if (Tcl_GetMaster(ptr->ip) != (Tcl_Interp*)NULL) {
  2253. /* slave IP */
  2254. return Qnil;
  2255. }
  2256. return lib_mainloop_watchdog(argc, argv, self);
  2257. }
  2258. /* thread-safe(?) interaction between Ruby and Tk */
  2259. struct thread_call_proc_arg {
  2260. VALUE proc;
  2261. int *done;
  2262. };
  2263. void
  2264. _thread_call_proc_arg_mark(struct thread_call_proc_arg *q)
  2265. {
  2266. rb_gc_mark(q->proc);
  2267. }
  2268. static VALUE
  2269. _thread_call_proc_core(arg)
  2270. VALUE arg;
  2271. {
  2272. struct thread_call_proc_arg *q = (struct thread_call_proc_arg*)arg;
  2273. return rb_funcall(q->proc, ID_call, 0);
  2274. }
  2275. static VALUE
  2276. _thread_call_proc_ensure(arg)
  2277. VALUE arg;
  2278. {
  2279. struct thread_call_proc_arg *q = (struct thread_call_proc_arg*)arg;
  2280. *(q->done) = 1;
  2281. return Qnil;
  2282. }
  2283. static VALUE
  2284. _thread_call_proc(arg)
  2285. VALUE arg;
  2286. {
  2287. struct thread_call_proc_arg *q = (struct thread_call_proc_arg*)arg;
  2288. return rb_ensure(_thread_call_proc_core, (VALUE)q,
  2289. _thread_call_proc_ensure, (VALUE)q);
  2290. }
  2291. static VALUE
  2292. #ifdef HAVE_PROTOTYPES
  2293. _thread_call_proc_value(VALUE th)
  2294. #else
  2295. _thread_call_proc_value(th)
  2296. VALUE th;
  2297. #endif
  2298. {
  2299. return rb_funcall(th, ID_value, 0);
  2300. }
  2301. static VALUE
  2302. lib_thread_callback(argc, argv, self)
  2303. int argc;
  2304. VALUE *argv;
  2305. VALUE self;
  2306. {
  2307. struct thread_call_proc_arg *q;
  2308. VALUE proc, th, ret;
  2309. int status, foundEvent;
  2310. if (rb_scan_args(argc, argv, "01", &proc) == 0) {
  2311. proc = rb_block_proc();
  2312. }
  2313. q = (struct thread_call_proc_arg *)ALLOC(struct thread_call_proc_arg);
  2314. /* q = (struct thread_call_proc_arg *)ckalloc(sizeof(struct thread_call_proc_arg)); */
  2315. q->proc = proc;
  2316. q->done = (int*)ALLOC(int);
  2317. /* q->done = (int*)ckalloc(sizeof(int)); */
  2318. *(q->done) = 0;
  2319. /* create call-proc thread */
  2320. th = rb_thread_create(_thread_call_proc, (void*)q);
  2321. rb_thread_schedule();
  2322. /* start sub-eventloop */
  2323. foundEvent = RTEST(lib_eventloop_launcher(/* not check root-widget */0, 0,
  2324. q->done, (Tcl_Interp*)NULL));
  2325. #ifdef RUBY_VM
  2326. if (RTEST(rb_funcall(th, ID_alive_p, 0))) {
  2327. #else
  2328. if (RTEST(rb_thread_alive_p(th))) {
  2329. #endif
  2330. rb_funcall(th, ID_kill, 0);
  2331. ret = Qnil;
  2332. } else {
  2333. ret = rb_protect(_thread_call_proc_value, th, &status);
  2334. }
  2335. xfree(q->done);
  2336. xfree(q);
  2337. /* ckfree((char*)q->done); */
  2338. /* ckfree((char*)q); */
  2339. if (NIL_P(rbtk_pending_exception)) {
  2340. /* return rb_errinfo(); */
  2341. if (status) {
  2342. rb_exc_raise(rb_errinfo());
  2343. }
  2344. } else {
  2345. VALUE exc = rbtk_pending_exception;
  2346. rbtk_pending_exception = Qnil;
  2347. /* return exc; */
  2348. rb_exc_raise(exc);
  2349. }
  2350. return ret;
  2351. }
  2352. /* do_one_event */
  2353. static VALUE
  2354. lib_do_one_event_core(argc, argv, self, is_ip)
  2355. int argc;
  2356. VALUE *argv;
  2357. VALUE self;
  2358. int is_ip;
  2359. {
  2360. volatile VALUE vflags;
  2361. int flags;
  2362. int found_event;
  2363. if (!NIL_P(eventloop_thread)) {
  2364. rb_raise(rb_eRuntimeError, "eventloop is already running");
  2365. }
  2366. tcl_stubs_check();
  2367. if (rb_scan_args(argc, argv, "01", &vflags) == 0) {
  2368. flags = TCL_ALL_EVENTS | TCL_DONT_WAIT;
  2369. } else {
  2370. Check_Type(vflags, T_FIXNUM);
  2371. flags = FIX2INT(vflags);
  2372. }
  2373. if (rb_safe_level() >= 4 || (rb_safe_level() >=1 && OBJ_TAINTED(vflags))) {
  2374. flags |= TCL_DONT_WAIT;
  2375. }
  2376. if (is_ip) {
  2377. /* check IP */
  2378. struct tcltkip *ptr = get_ip(self);
  2379. /* ip is deleted? */
  2380. if (deleted_ip(ptr)) {
  2381. return Qfalse;
  2382. }
  2383. if (Tcl_GetMaster(ptr->ip) != (Tcl_Interp*)NULL) {
  2384. /* slave IP */
  2385. flags |= TCL_DONT_WAIT;
  2386. }
  2387. }
  2388. /* found_event = Tcl_DoOneEvent(TCL_ALL_EVENTS | TCL_DONT_WAIT); */
  2389. found_event = Tcl_DoOneEvent(flags);
  2390. if (pending_exception_check0()) {
  2391. return Qfalse;
  2392. }
  2393. if (found_event) {
  2394. return Qtrue;
  2395. } else {
  2396. return Qfalse;
  2397. }
  2398. }
  2399. static VALUE
  2400. lib_do_one_event(argc, argv, self)
  2401. int argc;
  2402. VALUE *argv;
  2403. VALUE self;
  2404. {
  2405. return lib_do_one_event_core(argc, argv, self, 0);
  2406. }
  2407. static VALUE
  2408. ip_do_one_event(argc, argv, self)
  2409. int argc;
  2410. VALUE *argv;
  2411. VALUE self;
  2412. {
  2413. return lib_do_one_event_core(argc, argv, self, 0);
  2414. }
  2415. static void
  2416. ip_set_exc_message(interp, exc)
  2417. Tcl_Interp *interp;
  2418. VALUE exc;
  2419. {
  2420. char *buf;
  2421. Tcl_DString dstr;
  2422. volatile VALUE msg;
  2423. int thr_crit_bup;
  2424. #if TCL_MAJOR_VERSION > 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION > 0)
  2425. volatile VALUE enc;
  2426. Tcl_Encoding encoding;
  2427. #endif
  2428. thr_crit_bup = rb_thread_critical;
  2429. rb_thread_critical = Qtrue;
  2430. msg = rb_funcall(exc, ID_message, 0, 0);
  2431. StringValue(msg);
  2432. #if TCL_MAJOR_VERSION > 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION > 0)
  2433. enc = rb_attr_get(exc, ID_at_enc);
  2434. if (NIL_P(enc)) {
  2435. enc = rb_attr_get(msg, ID_at_enc);
  2436. }
  2437. if (NIL_P(enc)) {
  2438. encoding = (Tcl_Encoding)NULL;
  2439. } else if (TYPE(enc) == T_STRING) {
  2440. /* encoding = Tcl_GetEncoding(interp, RSTRING_PTR(enc)); */
  2441. encoding = Tcl_GetEncoding((Tcl_Interp*)NULL, RSTRING_PTR(enc));
  2442. } else {
  2443. enc = rb_funcall(enc, ID_to_s, 0, 0);
  2444. /* encoding = Tcl_GetEncoding(interp, RSTRING_PTR(enc)); */
  2445. encoding = Tcl_GetEncoding((Tcl_Interp*)NULL, RSTRING_PTR(enc));
  2446. }
  2447. /* to avoid a garbled error message dialog */
  2448. /* buf = ALLOC_N(char, (RSTRING(msg)->len)+1);*/
  2449. /* memcpy(buf, RSTRING(msg)->ptr, RSTRING(msg)->len);*/
  2450. /* buf[RSTRING(msg)->len] = 0; */
  2451. buf = ALLOC_N(char, RSTRING_LEN(msg)+1);
  2452. /* buf = ckalloc(sizeof(char)*((RSTRING_LEN(msg))+1)); */
  2453. memcpy(buf, RSTRING_PTR(msg), RSTRING_LEN(msg));
  2454. buf[RSTRING_LEN(msg)] = 0;
  2455. Tcl_DStringInit(&dstr);
  2456. Tcl_DStringFree(&dstr);
  2457. Tcl_ExternalToUtfDString(encoding, buf, RSTRING_LEN(msg), &dstr);
  2458. Tcl_AppendResult(interp, Tcl_DStringValue(&dstr), (char*)NULL);
  2459. DUMP2("error message:%s", Tcl_DStringValue(&dstr));
  2460. Tcl_DStringFree(&dstr);
  2461. xfree(buf);
  2462. /* ckfree(buf); */
  2463. #else /* TCL_VERSION <= 8.0 */
  2464. Tcl_AppendResult(interp, RSTRING_PTR(msg), (char*)NULL);
  2465. #endif
  2466. rb_thread_critical = thr_crit_bup;
  2467. }
  2468. static VALUE
  2469. TkStringValue(obj)
  2470. VALUE obj;
  2471. {
  2472. switch(TYPE(obj)) {
  2473. case T_STRING:
  2474. return obj;
  2475. case T_NIL:
  2476. return rb_str_new2("");
  2477. case T_TRUE:
  2478. return rb_str_new2("1");
  2479. case T_FALSE:
  2480. return rb_str_new2("0");
  2481. case T_ARRAY:
  2482. return rb_funcall(obj, ID_join, 1, rb_str_new2(" "));
  2483. default:
  2484. if (rb_respond_to(obj, ID_to_s)) {
  2485. return rb_funcall(obj, ID_to_s, 0, 0);
  2486. }
  2487. }
  2488. return rb_funcall(obj, ID_inspect, 0, 0);
  2489. }
  2490. static int
  2491. #ifdef HAVE_PROTOTYPES
  2492. tcl_protect_core(Tcl_Interp *interp, VALUE (*proc)(VALUE), VALUE data)
  2493. #else
  2494. tcl_protect_core(interp, proc, data) /* should not raise exception */
  2495. Tcl_Interp *interp;
  2496. VALUE (*proc)();
  2497. VALUE data;
  2498. #endif
  2499. {
  2500. volatile VALUE ret, exc = Qnil;
  2501. int status = 0;
  2502. int thr_crit_bup = rb_thread_critical;
  2503. Tcl_ResetResult(interp);
  2504. rb_thread_critical = Qfalse;
  2505. ret = rb_protect(proc, data, &status);
  2506. rb_thread_critical = Qtrue;
  2507. if (status) {
  2508. char *buf;
  2509. VALUE old_gc;
  2510. volatile VALUE type, str;
  2511. old_gc = rb_gc_disable();
  2512. switch(status) {
  2513. case TAG_RETURN:
  2514. type = eTkCallbackReturn;
  2515. goto error;
  2516. case TAG_BREAK:
  2517. type = eTkCallbackBreak;
  2518. goto error;
  2519. case TAG_NEXT:
  2520. type = eTkCallbackContinue;
  2521. goto error;
  2522. error:
  2523. str = rb_str_new2("LocalJumpError: ");
  2524. rb_str_append(str, rb_obj_as_string(rb_errinfo()));
  2525. exc = rb_exc_new3(type, str);
  2526. break;
  2527. case TAG_RETRY:
  2528. if (NIL_P(rb_errinfo())) {
  2529. DUMP1("rb_protect: retry");
  2530. exc = rb_exc_new2(eTkCallbackRetry, "retry jump error");
  2531. } else {
  2532. exc = rb_errinfo();
  2533. }
  2534. break;
  2535. case TAG_REDO:
  2536. if (NIL_P(rb_errinfo())) {
  2537. DUMP1("rb_protect: redo");
  2538. exc = rb_exc_new2(eTkCallbackRedo, "redo jump error");
  2539. } else {
  2540. exc = rb_errinfo();
  2541. }
  2542. break;
  2543. case TAG_RAISE:
  2544. if (NIL_P(rb_errinfo())) {
  2545. exc = rb_exc_new2(rb_eException, "unknown exception");
  2546. } else {
  2547. exc = rb_errinfo();
  2548. }
  2549. break;
  2550. case TAG_FATAL:
  2551. if (NIL_P(rb_errinfo())) {
  2552. exc = rb_exc_new2(rb_eFatal, "FATAL");
  2553. } else {
  2554. exc = rb_errinfo();
  2555. }
  2556. break;
  2557. case TAG_THROW:
  2558. if (NIL_P(rb_errinfo())) {
  2559. DUMP1("rb_protect: throw");
  2560. exc = rb_exc_new2(eTkCallbackThrow, "throw jump error");
  2561. } else {
  2562. exc = rb_errinfo();
  2563. }
  2564. break;
  2565. default:
  2566. buf = ALLOC_N(char, 256);
  2567. /* buf = ckalloc(sizeof(char) * 256); */
  2568. sprintf(buf, "unknown loncaljmp status %d", status);
  2569. exc = rb_exc_new2(rb_eException, buf);
  2570. xfree(buf);
  2571. /* ckfree(buf); */
  2572. break;
  2573. }
  2574. if (old_gc == Qfalse) rb_gc_enable();
  2575. ret = Qnil;
  2576. }
  2577. rb_thread_critical = thr_crit_bup;
  2578. Tcl_ResetResult(interp);
  2579. /* status check */
  2580. if (!NIL_P(exc)) {
  2581. volatile VALUE eclass = rb_obj_class(exc);
  2582. volatile VALUE backtrace;
  2583. DUMP1("(failed)");
  2584. thr_crit_bup = rb_thread_critical;
  2585. rb_thread_critical = Qtrue;
  2586. DUMP1("set backtrace");
  2587. if (!NIL_P(backtrace = rb_funcall(exc, ID_backtrace, 0, 0))) {
  2588. backtrace = rb_ary_join(backtrace, rb_str_new2("\n"));
  2589. Tcl_AddErrorInfo(interp, StringValuePtr(backtrace));
  2590. }
  2591. rb_thread_critical = thr_crit_bup;
  2592. ip_set_exc_message(interp, exc);
  2593. if (eclass == eTkCallbackReturn)
  2594. return TCL_RETURN;
  2595. if (eclass == eTkCallbackBreak)
  2596. return TCL_BREAK;
  2597. if (eclass == eTkCallbackContinue)
  2598. return TCL_CONTINUE;
  2599. if (eclass == rb_eSystemExit || eclass == rb_eInterrupt) {
  2600. rbtk_pending_exception = exc;
  2601. return TCL_RETURN;
  2602. }
  2603. if (rb_obj_is_kind_of(exc, eTkLocalJumpError)) {
  2604. rbtk_pending_exception = exc;
  2605. return TCL_ERROR;
  2606. }
  2607. if (rb_obj_is_kind_of(exc, eLocalJumpError)) {
  2608. VALUE reason = rb_ivar_get(exc, ID_at_reason);
  2609. if (TYPE(reason) == T_SYMBOL) {
  2610. if (SYM2ID(reason) == ID_return)
  2611. return TCL_RETURN;
  2612. if (SYM2ID(reason) == ID_break)
  2613. return TCL_BREAK;
  2614. if (SYM2ID(reason) == ID_next)
  2615. return TCL_CONTINUE;
  2616. }
  2617. }
  2618. return TCL_ERROR;
  2619. }
  2620. /* result must be string or nil */
  2621. if (!NIL_P(ret)) {
  2622. /* copy result to the tcl interpreter */
  2623. thr_crit_bup = rb_thread_critical;
  2624. rb_thread_critical = Qtrue;
  2625. ret = TkStringValue(ret);
  2626. DUMP1("Tcl_AppendResult");
  2627. Tcl_AppendResult(interp, RSTRING_PTR(ret), (char *)NULL);
  2628. rb_thread_critical = thr_crit_bup;
  2629. }
  2630. DUMP2("(result) %s", NIL_P(ret) ? "nil" : RSTRING_PTR(ret));
  2631. return TCL_OK;
  2632. }
  2633. static int
  2634. tcl_protect(interp, proc, data)
  2635. Tcl_Interp *interp;
  2636. VALUE (*proc)();
  2637. VALUE data;
  2638. {
  2639. int code;
  2640. #ifdef HAVE_NATIVETHREAD
  2641. #ifndef RUBY_USE_NATIVE_THREAD
  2642. if (!ruby_native_thread_p()) {
  2643. rb_bug("cross-thread violation on tcl_protect()");
  2644. }
  2645. #endif
  2646. #endif
  2647. #ifdef RUBY_VM
  2648. code = tcl_protect_core(interp, proc, data);
  2649. #else
  2650. do {
  2651. int old_trapflag = rb_trap_immediate;
  2652. rb_trap_immediate = 0;
  2653. code = tcl_protect_core(interp, proc, data);
  2654. rb_trap_immediate = old_trapflag;
  2655. } while (0);
  2656. #endif
  2657. return code;
  2658. }
  2659. static int
  2660. #if TCL_MAJOR_VERSION >= 8
  2661. ip_ruby_eval(clientData, interp, argc, argv)
  2662. ClientData clientData;
  2663. Tcl_Interp *interp;
  2664. int argc;
  2665. Tcl_Obj *CONST argv[];
  2666. #else /* TCL_MAJOR_VERSION < 8 */
  2667. ip_ruby_eval(clientData, interp, argc, argv)
  2668. ClientData clientData;
  2669. Tcl_Interp *interp;
  2670. int argc;
  2671. char *argv[];
  2672. #endif
  2673. {
  2674. char *arg;
  2675. int thr_crit_bup;
  2676. int code;
  2677. if (interp == (Tcl_Interp*)NULL) {
  2678. rbtk_pending_exception = rb_exc_new2(rb_eRuntimeError,
  2679. "IP is deleted");
  2680. return TCL_ERROR;
  2681. }
  2682. /* ruby command has 1 arg. */
  2683. if (argc != 2) {
  2684. #if 0
  2685. rb_raise(rb_eArgError,
  2686. "wrong number of arguments (%d for 1)", argc - 1);
  2687. #else
  2688. char buf[sizeof(int)*8 + 1];
  2689. Tcl_ResetResult(interp);
  2690. sprintf(buf, "%d", argc-1);
  2691. Tcl_AppendResult(interp, "wrong number of arguments (",
  2692. buf, " for 1)", (char *)NULL);
  2693. rbtk_pending_exception = rb_exc_new2(rb_eArgError,
  2694. Tcl_GetStringResult(interp));
  2695. return TCL_ERROR;
  2696. #endif
  2697. }
  2698. /* get C string from Tcl object */
  2699. #if TCL_MAJOR_VERSION >= 8
  2700. {
  2701. char *str;
  2702. int len;
  2703. thr_crit_bup = rb_thread_critical;
  2704. rb_thread_critical = Qtrue;
  2705. str = Tcl_GetStringFromObj(argv[1], &len);
  2706. arg = ALLOC_N(char, len + 1);
  2707. /* arg = ckalloc(sizeof(char) * (len + 1)); */
  2708. memcpy(arg, str, len);
  2709. arg[len] = 0;
  2710. rb_thread_critical = thr_crit_bup;
  2711. }
  2712. #else /* TCL_MAJOR_VERSION < 8 */
  2713. arg = argv[1];
  2714. #endif
  2715. /* evaluate the argument string by ruby */
  2716. DUMP2("rb_eval_string(%s)", arg);
  2717. code = tcl_protect(interp, rb_eval_string, (VALUE)arg);
  2718. #if TCL_MAJOR_VERSION >= 8
  2719. xfree(arg);
  2720. /* ckfree(arg); */
  2721. #endif
  2722. return code;
  2723. }
  2724. /* Tcl command `ruby_cmd' */
  2725. static VALUE
  2726. ip_ruby_cmd_core(arg)
  2727. struct cmd_body_arg *arg;
  2728. {
  2729. volatile VALUE ret;
  2730. int thr_crit_bup;
  2731. DUMP1("call ip_ruby_cmd_core");
  2732. thr_crit_bup = rb_thread_critical;
  2733. rb_thread_critical = Qfalse;
  2734. ret = rb_apply(arg->receiver, arg->method, arg->args);
  2735. DUMP2("rb_apply return:%lx", ret);
  2736. rb_thread_critical = thr_crit_bup;
  2737. DUMP1("finish ip_ruby_cmd_core");
  2738. return ret;
  2739. }
  2740. #define SUPPORT_NESTED_CONST_AS_IP_RUBY_CMD_RECEIVER 1
  2741. static VALUE
  2742. ip_ruby_cmd_receiver_const_get(name)
  2743. char *name;
  2744. {
  2745. volatile VALUE klass = rb_cObject;
  2746. #if 0
  2747. char *head, *tail;
  2748. #endif
  2749. int state;
  2750. #if SUPPORT_NESTED_CONST_AS_IP_RUBY_CMD_RECEIVER
  2751. klass = rb_eval_string_protect(name, &state);
  2752. if (state) {
  2753. return Qnil;
  2754. } else {
  2755. return klass;
  2756. }
  2757. #else
  2758. return rb_const_get(klass, rb_intern(name));
  2759. #endif
  2760. /* TODO!!!!!! */
  2761. /* support nest of classes/modules */
  2762. /* return rb_eval_string(name); */
  2763. /* return rb_eval_string_protect(name, &state); */
  2764. #if 0 /* doesn't work!! (fail to autoload?) */
  2765. /* duplicate */
  2766. head = name = strdup(name);
  2767. /* has '::' at head ? */
  2768. if (*head == ':') head += 2;
  2769. tail = head;
  2770. /* search */
  2771. while(*tail) {
  2772. if (*tail == ':') {
  2773. *tail = '\0';
  2774. klass = rb_const_get(klass, rb_intern(head));
  2775. tail += 2;
  2776. head = tail;
  2777. } else {
  2778. tail++;
  2779. }
  2780. }
  2781. free(name);
  2782. return rb_const_get(klass, rb_intern(head));
  2783. #endif
  2784. }
  2785. static VALUE
  2786. ip_ruby_cmd_receiver_get(str)
  2787. char *str;
  2788. {
  2789. volatile VALUE receiver;
  2790. #if !SUPPORT_NESTED_CONST_AS_IP_RUBY_CMD_RECEIVER
  2791. int state;
  2792. #endif
  2793. if (str[0] == ':' || ('A' <= str[0] && str[0] <= 'Z')) {
  2794. /* class | module | constant */
  2795. #if SUPPORT_NESTED_CONST_AS_IP_RUBY_CMD_RECEIVER
  2796. receiver = ip_ruby_cmd_receiver_const_get(str);
  2797. #else
  2798. receiver = rb_protect(ip_ruby_cmd_receiver_const_get, (VALUE)str, &state);
  2799. if (state) return Qnil;
  2800. #endif
  2801. } else if (str[0] == '$') {
  2802. /* global variable */
  2803. receiver = rb_gv_get(str);
  2804. } else {
  2805. /* global variable omitted '$' */
  2806. char *buf;
  2807. int len;
  2808. len = strlen(str);
  2809. buf = ALLOC_N(char, len + 2);
  2810. /* buf = ckalloc(sizeof(char) * (len + 2)); */
  2811. buf[0] = '$';
  2812. memcpy(buf + 1, str, len);
  2813. buf[len + 1] = 0;
  2814. receiver = rb_gv_get(buf);
  2815. xfree(buf);
  2816. /* ckfree(buf); */
  2817. }
  2818. return receiver;
  2819. }
  2820. /* ruby_cmd receiver method arg ... */
  2821. static int
  2822. #if TCL_MAJOR_VERSION >= 8
  2823. ip_ruby_cmd(clientData, interp, argc, argv)
  2824. ClientData clientData;
  2825. Tcl_Interp *interp;
  2826. int argc;
  2827. Tcl_Obj *CONST argv[];
  2828. #else /* TCL_MAJOR_VERSION < 8 */
  2829. ip_ruby_cmd(clientData, interp, argc, argv)
  2830. ClientData clientData;
  2831. Tcl_Interp *interp;
  2832. int argc;
  2833. char *argv[];
  2834. #endif
  2835. {
  2836. volatile VALUE receiver;
  2837. volatile ID method;
  2838. volatile VALUE args;
  2839. char *str;
  2840. int i;
  2841. int len;
  2842. struct cmd_body_arg *arg;
  2843. int thr_crit_bup;
  2844. VALUE old_gc;
  2845. int code;
  2846. if (interp == (Tcl_Interp*)NULL) {
  2847. rbtk_pending_exception = rb_exc_new2(rb_eRuntimeError,
  2848. "IP is deleted");
  2849. return TCL_ERROR;
  2850. }
  2851. if (argc < 3) {
  2852. #if 0
  2853. rb_raise(rb_eArgError, "too few arguments");
  2854. #else
  2855. Tcl_ResetResult(interp);
  2856. Tcl_AppendResult(interp, "too few arguments", (char *)NULL);
  2857. rbtk_pending_exception = rb_exc_new2(rb_eArgError,
  2858. Tcl_GetStringResult(interp));
  2859. return TCL_ERROR;
  2860. #endif
  2861. }
  2862. /* get arguments from Tcl objects */
  2863. thr_crit_bup = rb_thread_critical;
  2864. rb_thread_critical = Qtrue;
  2865. old_gc = rb_gc_disable();
  2866. /* get receiver */
  2867. #if TCL_MAJOR_VERSION >= 8
  2868. str = Tcl_GetStringFromObj(argv[1], &len);
  2869. #else /* TCL_MAJOR_VERSION < 8 */
  2870. str = argv[1];
  2871. #endif
  2872. DUMP2("receiver:%s",str);
  2873. /* receiver = rb_protect(ip_ruby_cmd_receiver_get, (VALUE)str, &code); */
  2874. receiver = ip_ruby_cmd_receiver_get(str);
  2875. if (NIL_P(receiver)) {
  2876. #if 0
  2877. rb_raise(rb_eArgError,
  2878. "unknown class/module/global-variable '%s'", str);
  2879. #else
  2880. Tcl_ResetResult(interp);
  2881. Tcl_AppendResult(interp, "unknown class/module/global-variable '",
  2882. str, "'", (char *)NULL);
  2883. rbtk_pending_exception = rb_exc_new2(rb_eArgError,
  2884. Tcl_GetStringResult(interp));
  2885. if (old_gc == Qfalse) rb_gc_enable();
  2886. return TCL_ERROR;
  2887. #endif
  2888. }
  2889. /* get metrhod */
  2890. #if TCL_MAJOR_VERSION >= 8
  2891. str = Tcl_GetStringFromObj(argv[2], &len);
  2892. #else /* TCL_MAJOR_VERSION < 8 */
  2893. str = argv[2];
  2894. #endif
  2895. method = rb_intern(str);
  2896. /* get args */
  2897. args = rb_ary_new2(argc - 2);
  2898. for(i = 3; i < argc; i++) {
  2899. VALUE s;
  2900. #if TCL_MAJOR_VERSION >= 8
  2901. str = Tcl_GetStringFromObj(argv[i], &len);
  2902. s = rb_tainted_str_new(str, len);
  2903. #else /* TCL_MAJOR_VERSION < 8 */
  2904. str = argv[i];
  2905. s = rb_tainted_str_new2(str);
  2906. #endif
  2907. DUMP2("arg:%s",str);
  2908. #ifndef HAVE_STRUCT_RARRAY_LEN
  2909. rb_ary_push(args, s);
  2910. #else
  2911. RARRAY(args)->ptr[RARRAY(args)->len++] = s;
  2912. #endif
  2913. }
  2914. if (old_gc == Qfalse) rb_gc_enable();
  2915. rb_thread_critical = thr_crit_bup;
  2916. /* allocate */
  2917. arg = ALLOC(struct cmd_body_arg);
  2918. /* arg = (struct cmd_body_arg *)ckalloc(sizeof(struct cmd_body_arg)); */
  2919. arg->receiver = receiver;
  2920. arg->method = method;
  2921. arg->args = args;
  2922. /* evaluate the argument string by ruby */
  2923. code = tcl_protect(interp, ip_ruby_cmd_core, (VALUE)arg);
  2924. xfree(arg);
  2925. /* ckfree((char*)arg); */
  2926. return code;
  2927. }
  2928. /*****************************/
  2929. /* relpace of 'exit' command */
  2930. /*****************************/
  2931. static int
  2932. #if TCL_MAJOR_VERSION >= 8
  2933. #ifdef HAVE_PROTOTYPES
  2934. ip_InterpExitObjCmd(ClientData clientData, Tcl_Interp *interp,
  2935. int argc, Tcl_Obj *CONST argv[])
  2936. #else
  2937. ip_InterpExitObjCmd(clientData, interp, argc, argv)
  2938. ClientData clientData;
  2939. Tcl_Interp *interp;
  2940. int argc;
  2941. Tcl_Obj *CONST argv[];
  2942. #endif
  2943. #else /* TCL_MAJOR_VERSION < 8 */
  2944. #ifdef HAVE_PROTOTYPES
  2945. ip_InterpExitCommand(ClientData clientData, Tcl_Interp *interp,
  2946. int argc, char *argv[])
  2947. #else
  2948. ip_InterpExitCommand(clientData, interp, argc, argv)
  2949. ClientData clientData;
  2950. Tcl_Interp *interp;
  2951. int argc;
  2952. char *argv[];
  2953. #endif
  2954. #endif
  2955. {
  2956. DUMP1("start ip_InterpExitCommand");
  2957. if (interp != (Tcl_Interp*)NULL
  2958. && !Tcl_InterpDeleted(interp)
  2959. #if TCL_NAMESPACE_DEBUG
  2960. && !ip_null_namespace(interp)
  2961. #endif
  2962. ) {
  2963. Tcl_ResetResult(interp);
  2964. /* Tcl_Preserve(interp); */
  2965. /* Tcl_Eval(interp, "interp eval {} {destroy .}; interp delete {}"); */
  2966. if (!Tcl_InterpDeleted(interp)) {
  2967. ip_finalize(interp);
  2968. Tcl_DeleteInterp(interp);
  2969. Tcl_Release(interp);
  2970. }
  2971. }
  2972. return TCL_OK;
  2973. }
  2974. static int
  2975. #if TCL_MAJOR_VERSION >= 8
  2976. #ifdef HAVE_PROTOTYPES
  2977. ip_RubyExitObjCmd(ClientData clientData, Tcl_Interp *interp,
  2978. int argc, Tcl_Obj *CONST argv[])
  2979. #else
  2980. ip_RubyExitObjCmd(clientData, interp, argc, argv)
  2981. ClientData clientData;
  2982. Tcl_Interp *interp;
  2983. int argc;
  2984. Tcl_Obj *CONST argv[];
  2985. #endif
  2986. #else /* TCL_MAJOR_VERSION < 8 */
  2987. #ifdef HAVE_PROTOTYPES
  2988. ip_RubyExitCommand(ClientData clientData, Tcl_Interp *interp,
  2989. int argc, char *argv[])
  2990. #else
  2991. ip_RubyExitCommand(clientData, interp, argc, argv)
  2992. ClientData clientData;
  2993. Tcl_Interp *interp;
  2994. int argc;
  2995. char *argv[];
  2996. #endif
  2997. #endif
  2998. {
  2999. int state;
  3000. char *cmd, *param;
  3001. #if TCL_MAJOR_VERSION < 8
  3002. char *endptr;
  3003. cmd = argv[0];
  3004. #endif
  3005. DUMP1("start ip_RubyExitCommand");
  3006. #if TCL_MAJOR_VERSION >= 8
  3007. /* cmd = Tcl_GetString(argv[0]); */
  3008. cmd = Tcl_GetStringFromObj(argv[0], (int*)NULL);
  3009. #endif
  3010. if (argc < 1 || argc > 2) {
  3011. /* arguemnt error */
  3012. Tcl_AppendResult(interp,
  3013. "wrong number of arguments: should be \"",
  3014. cmd, " ?returnCode?\"", (char *)NULL);
  3015. return TCL_ERROR;
  3016. }
  3017. if (interp == (Tcl_Interp*)NULL) return TCL_OK;
  3018. Tcl_ResetResult(interp);
  3019. if (rb_safe_level() >= 4 || Tcl_IsSafe(interp)) {
  3020. if (!Tcl_InterpDeleted(interp)) {
  3021. ip_finalize(interp);
  3022. Tcl_DeleteInterp(interp);
  3023. Tcl_Release(interp);
  3024. }
  3025. return TCL_OK;
  3026. }
  3027. switch(argc) {
  3028. case 1:
  3029. /* rb_exit(0); */ /* not return if succeed */
  3030. Tcl_AppendResult(interp,
  3031. "fail to call \"", cmd, "\"", (char *)NULL);
  3032. rbtk_pending_exception = rb_exc_new2(rb_eSystemExit,
  3033. Tcl_GetStringResult(interp));
  3034. rb_iv_set(rbtk_pending_exception, "status", INT2FIX(0));
  3035. return TCL_RETURN;
  3036. case 2:
  3037. #if TCL_MAJOR_VERSION >= 8
  3038. if (Tcl_GetIntFromObj(interp, argv[1], &state) == TCL_ERROR) {
  3039. return TCL_ERROR;
  3040. }
  3041. /* param = Tcl_GetString(argv[1]); */
  3042. param = Tcl_GetStringFromObj(argv[1], (int*)NULL);
  3043. #else /* TCL_MAJOR_VERSION < 8 */
  3044. state = (int)strtol(argv[1], &endptr, 0);
  3045. if (*endptr) {
  3046. Tcl_AppendResult(interp,
  3047. "expected integer but got \"",
  3048. argv[1], "\"", (char *)NULL);
  3049. return TCL_ERROR;
  3050. }
  3051. param = argv[1];
  3052. #endif
  3053. /* rb_exit(state); */ /* not return if succeed */
  3054. Tcl_AppendResult(interp, "fail to call \"", cmd, " ",
  3055. param, "\"", (char *)NULL);
  3056. rbtk_pending_exception = rb_exc_new2(rb_eSystemExit,
  3057. Tcl_GetStringResult(interp));
  3058. rb_iv_set(rbtk_pending_exception, "status", INT2FIX(state));
  3059. return TCL_RETURN;
  3060. default:
  3061. /* arguemnt error */
  3062. Tcl_AppendResult(interp,
  3063. "wrong number of arguments: should be \"",
  3064. cmd, " ?returnCode?\"", (char *)NULL);
  3065. return TCL_ERROR;
  3066. }
  3067. }
  3068. /**************************/
  3069. /* based on tclEvent.c */
  3070. /**************************/
  3071. /*********************/
  3072. /* replace of update */
  3073. /*********************/
  3074. #if TCL_MAJOR_VERSION >= 8
  3075. static int ip_rbUpdateObjCmd _((ClientData, Tcl_Interp *, int,
  3076. Tcl_Obj *CONST []));
  3077. static int
  3078. ip_rbUpdateObjCmd(clientData, interp, objc, objv)
  3079. ClientData clientData;
  3080. Tcl_Interp *interp;
  3081. int objc;
  3082. Tcl_Obj *CONST objv[];
  3083. #else /* TCL_MAJOR_VERSION < 8 */
  3084. static int ip_rbUpdateCommand _((ClientData, Tcl_Interp *, int, char *[]));
  3085. static int
  3086. ip_rbUpdateCommand(clientData, interp, objc, objv)
  3087. ClientData clientData;
  3088. Tcl_Interp *interp;
  3089. int objc;
  3090. char *objv[];
  3091. #endif
  3092. {
  3093. int optionIndex;
  3094. int ret;
  3095. int flags = 0;
  3096. static CONST char *updateOptions[] = {"idletasks", (char *) NULL};
  3097. enum updateOptions {REGEXP_IDLETASKS};
  3098. DUMP1("Ruby's 'update' is called");
  3099. if (interp == (Tcl_Interp*)NULL) {
  3100. rbtk_pending_exception = rb_exc_new2(rb_eRuntimeError,
  3101. "IP is deleted");
  3102. return TCL_ERROR;
  3103. }
  3104. #ifdef HAVE_NATIVETHREAD
  3105. #ifndef RUBY_USE_NATIVE_THREAD
  3106. if (!ruby_native_thread_p()) {
  3107. rb_bug("cross-thread violation on ip_ruby_eval()");
  3108. }
  3109. #endif
  3110. #endif
  3111. Tcl_ResetResult(interp);
  3112. if (objc == 1) {
  3113. flags = TCL_DONT_WAIT;
  3114. } else if (objc == 2) {
  3115. #if TCL_MAJOR_VERSION >= 8
  3116. if (Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)updateOptions,
  3117. "option", 0, &optionIndex) != TCL_OK) {
  3118. return TCL_ERROR;
  3119. }
  3120. switch ((enum updateOptions) optionIndex) {
  3121. case REGEXP_IDLETASKS: {
  3122. flags = TCL_IDLE_EVENTS;
  3123. break;
  3124. }
  3125. default: {
  3126. rb_bug("ip_rbUpdateObjCmd: bad option index to UpdateOptions");
  3127. }
  3128. }
  3129. #else
  3130. if (strncmp(objv[1], "idletasks", strlen(objv[1])) != 0) {
  3131. Tcl_AppendResult(interp, "bad option \"", objv[1],
  3132. "\": must be idletasks", (char *) NULL);
  3133. return TCL_ERROR;
  3134. }
  3135. flags = TCL_IDLE_EVENTS;
  3136. #endif
  3137. } else {
  3138. #ifdef Tcl_WrongNumArgs
  3139. Tcl_WrongNumArgs(interp, 1, objv, "[ idletasks ]");
  3140. #else
  3141. # if TCL_MAJOR_VERSION >= 8
  3142. int dummy;
  3143. Tcl_AppendResult(interp, "wrong number of arguments: should be \"",
  3144. Tcl_GetStringFromObj(objv[0], &dummy),
  3145. " [ idletasks ]\"",
  3146. (char *) NULL);
  3147. # else /* TCL_MAJOR_VERSION < 8 */
  3148. Tcl_AppendResult(interp, "wrong number of arguments: should be \"",
  3149. objv[0], " [ idletasks ]\"", (char *) NULL);
  3150. # endif
  3151. #endif
  3152. return TCL_ERROR;
  3153. }
  3154. Tcl_Preserve(interp);
  3155. /* call eventloop */
  3156. /* ret = lib_eventloop_core(0, flags, (int *)NULL);*/ /* ignore result */
  3157. ret = RTEST(lib_eventloop_launcher(0, flags, (int *)NULL, interp)); /* ignore result */
  3158. /* exception check */
  3159. if (!NIL_P(rbtk_pending_exception)) {
  3160. Tcl_Release(interp);
  3161. /*
  3162. if (rb_obj_is_kind_of(rbtk_pending_exception, rb_eSystemExit)) {
  3163. */
  3164. if (rb_obj_is_kind_of(rbtk_pending_exception, rb_eSystemExit)
  3165. || rb_obj_is_kind_of(rbtk_pending_exception, rb_eInterrupt)) {
  3166. return TCL_RETURN;
  3167. } else{
  3168. return TCL_ERROR;
  3169. }
  3170. }
  3171. /* trap check */
  3172. #ifdef RUBY_VM
  3173. if (rb_thread_check_trap_pending()) {
  3174. #else
  3175. if (rb_trap_pending) {
  3176. #endif
  3177. Tcl_Release(interp);
  3178. return TCL_RETURN;
  3179. }
  3180. /*
  3181. * Must clear the interpreter's result because event handlers could
  3182. * have executed commands.
  3183. */
  3184. DUMP2("last result '%s'", Tcl_GetStringResult(interp));
  3185. Tcl_ResetResult(interp);
  3186. Tcl_Release(interp);
  3187. DUMP1("finish Ruby's 'update'");
  3188. return TCL_OK;
  3189. }
  3190. /**********************/
  3191. /* update with thread */
  3192. /**********************/
  3193. struct th_update_param {
  3194. VALUE thread;
  3195. int done;
  3196. };
  3197. static void rb_threadUpdateProc _((ClientData));
  3198. static void
  3199. rb_threadUpdateProc(clientData)
  3200. ClientData clientData; /* Pointer to integer to set to 1. */
  3201. {
  3202. struct th_update_param *param = (struct th_update_param *) clientData;
  3203. DUMP1("threadUpdateProc is called");
  3204. param->done = 1;
  3205. rb_thread_wakeup(param->thread);
  3206. return;
  3207. }
  3208. #if TCL_MAJOR_VERSION >= 8
  3209. static int ip_rb_threadUpdateObjCmd _((ClientData, Tcl_Interp *, int,
  3210. Tcl_Obj *CONST []));
  3211. static int
  3212. ip_rb_threadUpdateObjCmd(clientData, interp, objc, objv)
  3213. ClientData clientData;
  3214. Tcl_Interp *interp;
  3215. int objc;
  3216. Tcl_Obj *CONST objv[];
  3217. #else /* TCL_MAJOR_VERSION < 8 */
  3218. static int ip_rb_threadUpdateCommand _((ClientData, Tcl_Interp *, int,
  3219. char *[]));
  3220. static int
  3221. ip_rb_threadUpdateCommand(clientData, interp, objc, objv)
  3222. ClientData clientData;
  3223. Tcl_Interp *interp;
  3224. int objc;
  3225. char *objv[];
  3226. #endif
  3227. {
  3228. int optionIndex;
  3229. int flags = 0;
  3230. struct th_update_param *param;
  3231. static CONST char *updateOptions[] = {"idletasks", (char *) NULL};
  3232. enum updateOptions {REGEXP_IDLETASKS};
  3233. volatile VALUE current_thread = rb_thread_current();
  3234. struct timeval t;
  3235. DUMP1("Ruby's 'thread_update' is called");
  3236. if (interp == (Tcl_Interp*)NULL) {
  3237. rbtk_pending_exception = rb_exc_new2(rb_eRuntimeError,
  3238. "IP is deleted");
  3239. return TCL_ERROR;
  3240. }
  3241. #ifdef HAVE_NATIVETHREAD
  3242. #ifndef RUBY_USE_NATIVE_THREAD
  3243. if (!ruby_native_thread_p()) {
  3244. rb_bug("cross-thread violation on ip_rb_threadUpdateCommand()");
  3245. }
  3246. #endif
  3247. #endif
  3248. if (rb_thread_alone()
  3249. || NIL_P(eventloop_thread) || eventloop_thread == current_thread) {
  3250. #if TCL_MAJOR_VERSION >= 8
  3251. DUMP1("call ip_rbUpdateObjCmd");
  3252. return ip_rbUpdateObjCmd(clientData, interp, objc, objv);
  3253. #else /* TCL_MAJOR_VERSION < 8 */
  3254. DUMP1("call ip_rbUpdateCommand");
  3255. return ip_rbUpdateCommand(clientData, interp, objc, objv);
  3256. #endif
  3257. }
  3258. DUMP1("start Ruby's 'thread_update' body");
  3259. Tcl_ResetResult(interp);
  3260. if (objc == 1) {
  3261. flags = TCL_DONT_WAIT;
  3262. } else if (objc == 2) {
  3263. #if TCL_MAJOR_VERSION >= 8
  3264. if (Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)updateOptions,
  3265. "option", 0, &optionIndex) != TCL_OK) {
  3266. return TCL_ERROR;
  3267. }
  3268. switch ((enum updateOptions) optionIndex) {
  3269. case REGEXP_IDLETASKS: {
  3270. flags = TCL_IDLE_EVENTS;
  3271. break;
  3272. }
  3273. default: {
  3274. rb_bug("ip_rb_threadUpdateObjCmd: bad option index to UpdateOptions");
  3275. }
  3276. }
  3277. #else
  3278. if (strncmp(objv[1], "idletasks", strlen(objv[1])) != 0) {
  3279. Tcl_AppendResult(interp, "bad option \"", objv[1],
  3280. "\": must be idletasks", (char *) NULL);
  3281. return TCL_ERROR;
  3282. }
  3283. flags = TCL_IDLE_EVENTS;
  3284. #endif
  3285. } else {
  3286. #ifdef Tcl_WrongNumArgs
  3287. Tcl_WrongNumArgs(interp, 1, objv, "[ idletasks ]");
  3288. #else
  3289. # if TCL_MAJOR_VERSION >= 8
  3290. int dummy;
  3291. Tcl_AppendResult(interp, "wrong number of arguments: should be \"",
  3292. Tcl_GetStringFromObj(objv[0], &dummy),
  3293. " [ idletasks ]\"",
  3294. (char *) NULL);
  3295. # else /* TCL_MAJOR_VERSION < 8 */
  3296. Tcl_AppendResult(interp, "wrong number of arguments: should be \"",
  3297. objv[0], " [ idletasks ]\"", (char *) NULL);
  3298. # endif
  3299. #endif
  3300. return TCL_ERROR;
  3301. }
  3302. DUMP1("pass argument check");
  3303. /* param = (struct th_update_param *)Tcl_Alloc(sizeof(struct th_update_param)); */
  3304. param = (struct th_update_param *)ckalloc(sizeof(struct th_update_param));
  3305. #if 0 /* use Tcl_Preserve/Release */
  3306. Tcl_Preserve((ClientData)param);
  3307. #endif
  3308. param->thread = current_thread;
  3309. param->done = 0;
  3310. DUMP1("set idle proc");
  3311. Tcl_DoWhenIdle(rb_threadUpdateProc, (ClientData) param);
  3312. t.tv_sec = 0;
  3313. t.tv_usec = (long)((EVENT_HANDLER_TIMEOUT)*1000.0);
  3314. while(!param->done) {
  3315. DUMP1("wait for complete idle proc");
  3316. /* rb_thread_stop(); */
  3317. /* rb_thread_sleep_forever(); */
  3318. rb_thread_wait_for(t);
  3319. if (NIL_P(eventloop_thread)) {
  3320. break;
  3321. }
  3322. }
  3323. #if 0 /* use Tcl_EventuallyFree */
  3324. Tcl_EventuallyFree((ClientData)param, TCL_DYNAMIC); /* XXXXXXXX */
  3325. #else
  3326. #if 0 /* use Tcl_Preserve/Release */
  3327. Tcl_Release((ClientData)param);
  3328. #else
  3329. /* Tcl_Free((char *)param); */
  3330. ckfree((char *)param);
  3331. #endif
  3332. #endif
  3333. DUMP1("finish Ruby's 'thread_update'");
  3334. return TCL_OK;
  3335. }
  3336. /***************************/
  3337. /* replace of vwait/tkwait */
  3338. /***************************/
  3339. #if TCL_MAJOR_VERSION >= 8
  3340. static int ip_rbVwaitObjCmd _((ClientData, Tcl_Interp *, int,
  3341. Tcl_Obj *CONST []));
  3342. static int ip_rb_threadVwaitObjCmd _((ClientData, Tcl_Interp *, int,
  3343. Tcl_Obj *CONST []));
  3344. static int ip_rbTkWaitObjCmd _((ClientData, Tcl_Interp *, int,
  3345. Tcl_Obj *CONST []));
  3346. static int ip_rb_threadTkWaitObjCmd _((ClientData, Tcl_Interp *, int,
  3347. Tcl_Obj *CONST []));
  3348. #else
  3349. static int ip_rbVwaitCommand _((ClientData, Tcl_Interp *, int, char *[]));
  3350. static int ip_rb_threadVwaitCommand _((ClientData, Tcl_Interp *, int,
  3351. char *[]));
  3352. static int ip_rbTkWaitCommand _((ClientData, Tcl_Interp *, int, char *[]));
  3353. static int ip_rb_threadTkWaitCommand _((ClientData, Tcl_Interp *, int,
  3354. char *[]));
  3355. #endif
  3356. #if TCL_MAJOR_VERSION >= 8
  3357. static char *VwaitVarProc _((ClientData, Tcl_Interp *,
  3358. CONST84 char *,CONST84 char *, int));
  3359. static char *
  3360. VwaitVarProc(clientData, interp, name1, name2, flags)
  3361. ClientData clientData; /* Pointer to integer to set to 1. */
  3362. Tcl_Interp *interp; /* Interpreter containing variable. */
  3363. CONST84 char *name1; /* Name of variable. */
  3364. CONST84 char *name2; /* Second part of variable name. */
  3365. int flags; /* Information about what happened. */
  3366. #else /* TCL_MAJOR_VERSION < 8 */
  3367. static char *VwaitVarProc _((ClientData, Tcl_Interp *, char *, char *, int));
  3368. static char *
  3369. VwaitVarProc(clientData, interp, name1, name2, flags)
  3370. ClientData clientData; /* Pointer to integer to set to 1. */
  3371. Tcl_Interp *interp; /* Interpreter containing variable. */
  3372. char *name1; /* Name of variable. */
  3373. char *name2; /* Second part of variable name. */
  3374. int flags; /* Information about what happened. */
  3375. #endif
  3376. {
  3377. int *donePtr = (int *) clientData;
  3378. *donePtr = 1;
  3379. return (char *) NULL;
  3380. }
  3381. #if TCL_MAJOR_VERSION >= 8
  3382. static int
  3383. ip_rbVwaitObjCmd(clientData, interp, objc, objv)
  3384. ClientData clientData; /* Not used */
  3385. Tcl_Interp *interp;
  3386. int objc;
  3387. Tcl_Obj *CONST objv[];
  3388. #else /* TCL_MAJOR_VERSION < 8 */
  3389. static int
  3390. ip_rbVwaitCommand(clientData, interp, objc, objv)
  3391. ClientData clientData; /* Not used */
  3392. Tcl_Interp *interp;
  3393. int objc;
  3394. char *objv[];
  3395. #endif
  3396. {
  3397. int ret, done, foundEvent;
  3398. char *nameString;
  3399. int dummy;
  3400. int thr_crit_bup;
  3401. DUMP1("Ruby's 'vwait' is called");
  3402. if (interp == (Tcl_Interp*)NULL) {
  3403. rbtk_pending_exception = rb_exc_new2(rb_eRuntimeError,
  3404. "IP is deleted");
  3405. return TCL_ERROR;
  3406. }
  3407. #if 0
  3408. if (!rb_thread_alone()
  3409. && eventloop_thread != Qnil
  3410. && eventloop_thread != rb_thread_current()) {
  3411. #if TCL_MAJOR_VERSION >= 8
  3412. DUMP1("call ip_rb_threadVwaitObjCmd");
  3413. return ip_rb_threadVwaitObjCmd(clientData, interp, objc, objv);
  3414. #else /* TCL_MAJOR_VERSION < 8 */
  3415. DUMP1("call ip_rb_threadVwaitCommand");
  3416. return ip_rb_threadVwaitCommand(clientData, interp, objc, objv);
  3417. #endif
  3418. }
  3419. #endif
  3420. Tcl_Preserve(interp);
  3421. #ifdef HAVE_NATIVETHREAD
  3422. #ifndef RUBY_USE_NATIVE_THREAD
  3423. if (!ruby_native_thread_p()) {
  3424. rb_bug("cross-thread violation on ip_rbVwaitCommand()");
  3425. }
  3426. #endif
  3427. #endif
  3428. Tcl_ResetResult(interp);
  3429. if (objc != 2) {
  3430. #ifdef Tcl_WrongNumArgs
  3431. Tcl_WrongNumArgs(interp, 1, objv, "name");
  3432. #else
  3433. thr_crit_bup = rb_thread_critical;
  3434. rb_thread_critical = Qtrue;
  3435. #if TCL_MAJOR_VERSION >= 8
  3436. /* nameString = Tcl_GetString(objv[0]); */
  3437. nameString = Tcl_GetStringFromObj(objv[0], &dummy);
  3438. #else /* TCL_MAJOR_VERSION < 8 */
  3439. nameString = objv[0];
  3440. #endif
  3441. Tcl_AppendResult(interp, "wrong number of arguments: should be \"",
  3442. nameString, " name\"", (char *) NULL);
  3443. rb_thread_critical = thr_crit_bup;
  3444. #endif
  3445. Tcl_Release(interp);
  3446. return TCL_ERROR;
  3447. }
  3448. thr_crit_bup = rb_thread_critical;
  3449. rb_thread_critical = Qtrue;
  3450. #if TCL_MAJOR_VERSION >= 8
  3451. Tcl_IncrRefCount(objv[1]);
  3452. /* nameString = Tcl_GetString(objv[1]); */
  3453. nameString = Tcl_GetStringFromObj(objv[1], &dummy);
  3454. #else /* TCL_MAJOR_VERSION < 8 */
  3455. nameString = objv[1];
  3456. #endif
  3457. /*
  3458. if (Tcl_TraceVar(interp, nameString,
  3459. TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  3460. VwaitVarProc, (ClientData) &done) != TCL_OK) {
  3461. return TCL_ERROR;
  3462. }
  3463. */
  3464. ret = Tcl_TraceVar(interp, nameString,
  3465. TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  3466. VwaitVarProc, (ClientData) &done);
  3467. rb_thread_critical = thr_crit_bup;
  3468. if (ret != TCL_OK) {
  3469. #if TCL_MAJOR_VERSION >= 8
  3470. Tcl_DecrRefCount(objv[1]);
  3471. #endif
  3472. Tcl_Release(interp);
  3473. return TCL_ERROR;
  3474. }
  3475. done = 0;
  3476. foundEvent = RTEST(lib_eventloop_launcher(/* not check root-widget */0,
  3477. 0, &done, interp));
  3478. thr_crit_bup = rb_thread_critical;
  3479. rb_thread_critical = Qtrue;
  3480. Tcl_UntraceVar(interp, nameString,
  3481. TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  3482. VwaitVarProc, (ClientData) &done);
  3483. rb_thread_critical = thr_crit_bup;
  3484. /* exception check */
  3485. if (!NIL_P(rbtk_pending_exception)) {
  3486. #if TCL_MAJOR_VERSION >= 8
  3487. Tcl_DecrRefCount(objv[1]);
  3488. #endif
  3489. Tcl_Release(interp);
  3490. /*
  3491. if (rb_obj_is_kind_of(rbtk_pending_exception, rb_eSystemExit)) {
  3492. */
  3493. if (rb_obj_is_kind_of(rbtk_pending_exception, rb_eSystemExit)
  3494. || rb_obj_is_kind_of(rbtk_pending_exception, rb_eInterrupt)) {
  3495. return TCL_RETURN;
  3496. } else{
  3497. return TCL_ERROR;
  3498. }
  3499. }
  3500. /* trap check */
  3501. #ifdef RUBY_VM
  3502. if (rb_thread_check_trap_pending()) {
  3503. #else
  3504. if (rb_trap_pending) {
  3505. #endif
  3506. #if TCL_MAJOR_VERSION >= 8
  3507. Tcl_DecrRefCount(objv[1]);
  3508. #endif
  3509. Tcl_Release(interp);
  3510. return TCL_RETURN;
  3511. }
  3512. /*
  3513. * Clear out the interpreter's result, since it may have been set
  3514. * by event handlers.
  3515. */
  3516. Tcl_ResetResult(interp);
  3517. if (!foundEvent) {
  3518. thr_crit_bup = rb_thread_critical;
  3519. rb_thread_critical = Qtrue;
  3520. Tcl_AppendResult(interp, "can't wait for variable \"", nameString,
  3521. "\": would wait forever", (char *) NULL);
  3522. rb_thread_critical = thr_crit_bup;
  3523. #if TCL_MAJOR_VERSION >= 8
  3524. Tcl_DecrRefCount(objv[1]);
  3525. #endif
  3526. Tcl_Release(interp);
  3527. return TCL_ERROR;
  3528. }
  3529. #if TCL_MAJOR_VERSION >= 8
  3530. Tcl_DecrRefCount(objv[1]);
  3531. #endif
  3532. Tcl_Release(interp);
  3533. return TCL_OK;
  3534. }
  3535. /**************************/
  3536. /* based on tkCmd.c */
  3537. /**************************/
  3538. #if TCL_MAJOR_VERSION >= 8
  3539. static char *WaitVariableProc _((ClientData, Tcl_Interp *,
  3540. CONST84 char *,CONST84 char *, int));
  3541. static char *
  3542. WaitVariableProc(clientData, interp, name1, name2, flags)
  3543. ClientData clientData; /* Pointer to integer to set to 1. */
  3544. Tcl_Interp *interp; /* Interpreter containing variable. */
  3545. CONST84 char *name1; /* Name of variable. */
  3546. CONST84 char *name2; /* Second part of variable name. */
  3547. int flags; /* Information about what happened. */
  3548. #else /* TCL_MAJOR_VERSION < 8 */
  3549. static char *WaitVariableProc _((ClientData, Tcl_Interp *,
  3550. char *, char *, int));
  3551. static char *
  3552. WaitVariableProc(clientData, interp, name1, name2, flags)
  3553. ClientData clientData; /* Pointer to integer to set to 1. */
  3554. Tcl_Interp *interp; /* Interpreter containing variable. */
  3555. char *name1; /* Name of variable. */
  3556. char *name2; /* Second part of variable name. */
  3557. int flags; /* Information about what happened. */
  3558. #endif
  3559. {
  3560. int *donePtr = (int *) clientData;
  3561. *donePtr = 1;
  3562. return (char *) NULL;
  3563. }
  3564. static void WaitVisibilityProc _((ClientData, XEvent *));
  3565. static void
  3566. WaitVisibilityProc(clientData, eventPtr)
  3567. ClientData clientData; /* Pointer to integer to set to 1. */
  3568. XEvent *eventPtr; /* Information about event (not used). */
  3569. {
  3570. int *donePtr = (int *) clientData;
  3571. if (eventPtr->type == VisibilityNotify) {
  3572. *donePtr = 1;
  3573. }
  3574. if (eventPtr->type == DestroyNotify) {
  3575. *donePtr = 2;
  3576. }
  3577. }
  3578. static void WaitWindowProc _((ClientData, XEvent *));
  3579. static void
  3580. WaitWindowProc(clientData, eventPtr)
  3581. ClientData clientData; /* Pointer to integer to set to 1. */
  3582. XEvent *eventPtr; /* Information about event. */
  3583. {
  3584. int *donePtr = (int *) clientData;
  3585. if (eventPtr->type == DestroyNotify) {
  3586. *donePtr = 1;
  3587. }
  3588. }
  3589. #if TCL_MAJOR_VERSION >= 8
  3590. static int
  3591. ip_rbTkWaitObjCmd(clientData, interp, objc, objv)
  3592. ClientData clientData;
  3593. Tcl_Interp *interp;
  3594. int objc;
  3595. Tcl_Obj *CONST objv[];
  3596. #else /* TCL_MAJOR_VERSION < 8 */
  3597. static int
  3598. ip_rbTkWaitCommand(clientData, interp, objc, objv)
  3599. ClientData clientData;
  3600. Tcl_Interp *interp;
  3601. int objc;
  3602. char *objv[];
  3603. #endif
  3604. {
  3605. Tk_Window tkwin = (Tk_Window) clientData;
  3606. Tk_Window window;
  3607. int done, index;
  3608. static CONST char *optionStrings[] = { "variable", "visibility", "window",
  3609. (char *) NULL };
  3610. enum options { TKWAIT_VARIABLE, TKWAIT_VISIBILITY, TKWAIT_WINDOW };
  3611. char *nameString;
  3612. int ret, dummy;
  3613. int thr_crit_bup;
  3614. DUMP1("Ruby's 'tkwait' is called");
  3615. if (interp == (Tcl_Interp*)NULL) {
  3616. rbtk_pending_exception = rb_exc_new2(rb_eRuntimeError,
  3617. "IP is deleted");
  3618. return TCL_ERROR;
  3619. }
  3620. #if 0
  3621. if (!rb_thread_alone()
  3622. && eventloop_thread != Qnil
  3623. && eventloop_thread != rb_thread_current()) {
  3624. #if TCL_MAJOR_VERSION >= 8
  3625. DUMP1("call ip_rb_threadTkWaitObjCmd");
  3626. return ip_rb_threadTkWaitObjCmd((ClientData)tkwin, interp, objc, objv);
  3627. #else /* TCL_MAJOR_VERSION < 8 */
  3628. DUMP1("call ip_rb_threadTkWaitCommand");
  3629. return ip_rb_threadTkWwaitCommand((ClientData)tkwin, interp, objc, objv);
  3630. #endif
  3631. }
  3632. #endif
  3633. Tcl_Preserve(interp);
  3634. Tcl_ResetResult(interp);
  3635. if (objc != 3) {
  3636. #ifdef Tcl_WrongNumArgs
  3637. Tcl_WrongNumArgs(interp, 1, objv, "variable|visibility|window name");
  3638. #else
  3639. thr_crit_bup = rb_thread_critical;
  3640. rb_thread_critical = Qtrue;
  3641. #if TCL_MAJOR_VERSION >= 8
  3642. Tcl_AppendResult(interp, "wrong number of arguments: should be \"",
  3643. Tcl_GetStringFromObj(objv[0], &dummy),
  3644. " variable|visibility|window name\"",
  3645. (char *) NULL);
  3646. #else /* TCL_MAJOR_VERSION < 8 */
  3647. Tcl_AppendResult(interp, "wrong number of arguments: should be \"",
  3648. objv[0], " variable|visibility|window name\"",
  3649. (char *) NULL);
  3650. #endif
  3651. rb_thread_critical = thr_crit_bup;
  3652. #endif
  3653. Tcl_Release(interp);
  3654. return TCL_ERROR;
  3655. }
  3656. #if TCL_MAJOR_VERSION >= 8
  3657. thr_crit_bup = rb_thread_critical;
  3658. rb_thread_critical = Qtrue;
  3659. /*
  3660. if (Tcl_GetIndexFromObj(interp, objv[1],
  3661. (CONST84 char **)optionStrings,
  3662. "option", 0, &index) != TCL_OK) {
  3663. return TCL_ERROR;
  3664. }
  3665. */
  3666. ret = Tcl_GetIndexFromObj(interp, objv[1],
  3667. (CONST84 char **)optionStrings,
  3668. "option", 0, &index);
  3669. rb_thread_critical = thr_crit_bup;
  3670. if (ret != TCL_OK) {
  3671. Tcl_Release(interp);
  3672. return TCL_ERROR;
  3673. }
  3674. #else /* TCL_MAJOR_VERSION < 8 */
  3675. {
  3676. int c = objv[1][0];
  3677. size_t length = strlen(objv[1]);
  3678. if ((c == 'v') && (strncmp(objv[1], "variable", length) == 0)
  3679. && (length >= 2)) {
  3680. index = TKWAIT_VARIABLE;
  3681. } else if ((c == 'v') && (strncmp(objv[1], "visibility", length) == 0)
  3682. && (length >= 2)) {
  3683. index = TKWAIT_VISIBILITY;
  3684. } else if ((c == 'w') && (strncmp(objv[1], "window", length) == 0)) {
  3685. index = TKWAIT_WINDOW;
  3686. } else {
  3687. Tcl_AppendResult(interp, "bad option \"", objv[1],
  3688. "\": must be variable, visibility, or window",
  3689. (char *) NULL);
  3690. Tcl_Release(interp);
  3691. return TCL_ERROR;
  3692. }
  3693. }
  3694. #endif
  3695. thr_crit_bup = rb_thread_critical;
  3696. rb_thread_critical = Qtrue;
  3697. #if TCL_MAJOR_VERSION >= 8
  3698. Tcl_IncrRefCount(objv[2]);
  3699. /* nameString = Tcl_GetString(objv[2]); */
  3700. nameString = Tcl_GetStringFromObj(objv[2], &dummy);
  3701. #else /* TCL_MAJOR_VERSION < 8 */
  3702. nameString = objv[2];
  3703. #endif
  3704. rb_thread_critical = thr_crit_bup;
  3705. switch ((enum options) index) {
  3706. case TKWAIT_VARIABLE:
  3707. thr_crit_bup = rb_thread_critical;
  3708. rb_thread_critical = Qtrue;
  3709. /*
  3710. if (Tcl_TraceVar(interp, nameString,
  3711. TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  3712. WaitVariableProc, (ClientData) &done) != TCL_OK) {
  3713. return TCL_ERROR;
  3714. }
  3715. */
  3716. ret = Tcl_TraceVar(interp, nameString,
  3717. TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  3718. WaitVariableProc, (ClientData) &done);
  3719. rb_thread_critical = thr_crit_bup;
  3720. if (ret != TCL_OK) {
  3721. #if TCL_MAJOR_VERSION >= 8
  3722. Tcl_DecrRefCount(objv[2]);
  3723. #endif
  3724. Tcl_Release(interp);
  3725. return TCL_ERROR;
  3726. }
  3727. done = 0;
  3728. /* lib_eventloop_core(check_rootwidget_flag, 0, &done); */
  3729. lib_eventloop_launcher(check_rootwidget_flag, 0, &done, interp);
  3730. thr_crit_bup = rb_thread_critical;
  3731. rb_thread_critical = Qtrue;
  3732. Tcl_UntraceVar(interp, nameString,
  3733. TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  3734. WaitVariableProc, (ClientData) &done);
  3735. #if TCL_MAJOR_VERSION >= 8
  3736. Tcl_DecrRefCount(objv[2]);
  3737. #endif
  3738. rb_thread_critical = thr_crit_bup;
  3739. /* exception check */
  3740. if (!NIL_P(rbtk_pending_exception)) {
  3741. Tcl_Release(interp);
  3742. /*
  3743. if (rb_obj_is_kind_of(rbtk_pending_exception, rb_eSystemExit)) {
  3744. */
  3745. if (rb_obj_is_kind_of(rbtk_pending_exception, rb_eSystemExit)
  3746. || rb_obj_is_kind_of(rbtk_pending_exception, rb_eInterrupt)) {
  3747. return TCL_RETURN;
  3748. } else{
  3749. return TCL_ERROR;
  3750. }
  3751. }
  3752. /* trap check */
  3753. #ifdef RUBY_VM
  3754. if (rb_thread_check_trap_pending()) {
  3755. #else
  3756. if (rb_trap_pending) {
  3757. #endif
  3758. Tcl_Release(interp);
  3759. return TCL_RETURN;
  3760. }
  3761. break;
  3762. case TKWAIT_VISIBILITY:
  3763. thr_crit_bup = rb_thread_critical;
  3764. rb_thread_critical = Qtrue;
  3765. /* This function works on the Tk eventloop thread only. */
  3766. if (!tk_stubs_init_p() || Tk_MainWindow(interp) == (Tk_Window)NULL) {
  3767. window = NULL;
  3768. } else {
  3769. window = Tk_NameToWindow(interp, nameString, tkwin);
  3770. }
  3771. if (window == NULL) {
  3772. Tcl_AppendResult(interp, ": tkwait: ",
  3773. "no main-window (not Tk application?)",
  3774. (char*)NULL);
  3775. rb_thread_critical = thr_crit_bup;
  3776. #if TCL_MAJOR_VERSION >= 8
  3777. Tcl_DecrRefCount(objv[2]);
  3778. #endif
  3779. Tcl_Release(interp);
  3780. return TCL_ERROR;
  3781. }
  3782. Tk_CreateEventHandler(window,
  3783. VisibilityChangeMask|StructureNotifyMask,
  3784. WaitVisibilityProc, (ClientData) &done);
  3785. rb_thread_critical = thr_crit_bup;
  3786. done = 0;
  3787. /* lib_eventloop_core(check_rootwidget_flag, 0, &done); */
  3788. lib_eventloop_launcher(check_rootwidget_flag, 0, &done, interp);
  3789. /* exception check */
  3790. if (!NIL_P(rbtk_pending_exception)) {
  3791. #if TCL_MAJOR_VERSION >= 8
  3792. Tcl_DecrRefCount(objv[2]);
  3793. #endif
  3794. Tcl_Release(interp);
  3795. /*
  3796. if (rb_obj_is_kind_of(rbtk_pending_exception, rb_eSystemExit)) {
  3797. */
  3798. if (rb_obj_is_kind_of(rbtk_pending_exception, rb_eSystemExit)
  3799. || rb_obj_is_kind_of(rbtk_pending_exception, rb_eInterrupt)) {
  3800. return TCL_RETURN;
  3801. } else{
  3802. return TCL_ERROR;
  3803. }
  3804. }
  3805. /* trap check */
  3806. #ifdef RUBY_VM
  3807. if (rb_thread_check_trap_pending()) {
  3808. #else
  3809. if (rb_trap_pending) {
  3810. #endif
  3811. #if TCL_MAJOR_VERSION >= 8
  3812. Tcl_DecrRefCount(objv[2]);
  3813. #endif
  3814. Tcl_Release(interp);
  3815. return TCL_RETURN;
  3816. }
  3817. if (done != 1) {
  3818. /*
  3819. * Note that we do not delete the event handler because it
  3820. * was deleted automatically when the window was destroyed.
  3821. */
  3822. thr_crit_bup = rb_thread_critical;
  3823. rb_thread_critical = Qtrue;
  3824. Tcl_ResetResult(interp);
  3825. Tcl_AppendResult(interp, "window \"", nameString,
  3826. "\" was deleted before its visibility changed",
  3827. (char *) NULL);
  3828. rb_thread_critical = thr_crit_bup;
  3829. #if TCL_MAJOR_VERSION >= 8
  3830. Tcl_DecrRefCount(objv[2]);
  3831. #endif
  3832. Tcl_Release(interp);
  3833. return TCL_ERROR;
  3834. }
  3835. thr_crit_bup = rb_thread_critical;
  3836. rb_thread_critical = Qtrue;
  3837. #if TCL_MAJOR_VERSION >= 8
  3838. Tcl_DecrRefCount(objv[2]);
  3839. #endif
  3840. Tk_DeleteEventHandler(window,
  3841. VisibilityChangeMask|StructureNotifyMask,
  3842. WaitVisibilityProc, (ClientData) &done);
  3843. rb_thread_critical = thr_crit_bup;
  3844. break;
  3845. case TKWAIT_WINDOW:
  3846. thr_crit_bup = rb_thread_critical;
  3847. rb_thread_critical = Qtrue;
  3848. /* This function works on the Tk eventloop thread only. */
  3849. if (!tk_stubs_init_p() || Tk_MainWindow(interp) == (Tk_Window)NULL) {
  3850. window = NULL;
  3851. } else {
  3852. window = Tk_NameToWindow(interp, nameString, tkwin);
  3853. }
  3854. #if TCL_MAJOR_VERSION >= 8
  3855. Tcl_DecrRefCount(objv[2]);
  3856. #endif
  3857. if (window == NULL) {
  3858. Tcl_AppendResult(interp, ": tkwait: ",
  3859. "no main-window (not Tk application?)",
  3860. (char*)NULL);
  3861. rb_thread_critical = thr_crit_bup;
  3862. Tcl_Release(interp);
  3863. return TCL_ERROR;
  3864. }
  3865. Tk_CreateEventHandler(window, StructureNotifyMask,
  3866. WaitWindowProc, (ClientData) &done);
  3867. rb_thread_critical = thr_crit_bup;
  3868. done = 0;
  3869. /* lib_eventloop_core(check_rootwidget_flag, 0, &done); */
  3870. lib_eventloop_launcher(check_rootwidget_flag, 0, &done, interp);
  3871. /* exception check */
  3872. if (!NIL_P(rbtk_pending_exception)) {
  3873. Tcl_Release(interp);
  3874. /*
  3875. if (rb_obj_is_kind_of(rbtk_pending_exception, rb_eSystemExit)) {
  3876. */
  3877. if (rb_obj_is_kind_of(rbtk_pending_exception, rb_eSystemExit)
  3878. || rb_obj_is_kind_of(rbtk_pending_exception, rb_eInterrupt)) {
  3879. return TCL_RETURN;
  3880. } else{
  3881. return TCL_ERROR;
  3882. }
  3883. }
  3884. /* trap check */
  3885. #ifdef RUBY_VM
  3886. if (rb_thread_check_trap_pending()) {
  3887. #else
  3888. if (rb_trap_pending) {
  3889. #endif
  3890. Tcl_Release(interp);
  3891. return TCL_RETURN;
  3892. }
  3893. /*
  3894. * Note: there's no need to delete the event handler. It was
  3895. * deleted automatically when the window was destroyed.
  3896. */
  3897. break;
  3898. }
  3899. /*
  3900. * Clear out the interpreter's result, since it may have been set
  3901. * by event handlers.
  3902. */
  3903. Tcl_ResetResult(interp);
  3904. Tcl_Release(interp);
  3905. return TCL_OK;
  3906. }
  3907. /****************************/
  3908. /* vwait/tkwait with thread */
  3909. /****************************/
  3910. struct th_vwait_param {
  3911. VALUE thread;
  3912. int done;
  3913. };
  3914. #if TCL_MAJOR_VERSION >= 8
  3915. static char *rb_threadVwaitProc _((ClientData, Tcl_Interp *,
  3916. CONST84 char *,CONST84 char *, int));
  3917. static char *
  3918. rb_threadVwaitProc(clientData, interp, name1, name2, flags)
  3919. ClientData clientData; /* Pointer to integer to set to 1. */
  3920. Tcl_Interp *interp; /* Interpreter containing variable. */
  3921. CONST84 char *name1; /* Name of variable. */
  3922. CONST84 char *name2; /* Second part of variable name. */
  3923. int flags; /* Information about what happened. */
  3924. #else /* TCL_MAJOR_VERSION < 8 */
  3925. static char *rb_threadVwaitProc _((ClientData, Tcl_Interp *,
  3926. char *, char *, int));
  3927. static char *
  3928. rb_threadVwaitProc(clientData, interp, name1, name2, flags)
  3929. ClientData clientData; /* Pointer to integer to set to 1. */
  3930. Tcl_Interp *interp; /* Interpreter containing variable. */
  3931. char *name1; /* Name of variable. */
  3932. char *name2; /* Second part of variable name. */
  3933. int flags; /* Information about what happened. */
  3934. #endif
  3935. {
  3936. struct th_vwait_param *param = (struct th_vwait_param *) clientData;
  3937. if (flags & (TCL_INTERP_DESTROYED | TCL_TRACE_DESTROYED)) {
  3938. param->done = -1;
  3939. } else {
  3940. param->done = 1;
  3941. }
  3942. if (param->done != 0) rb_thread_wakeup(param->thread);
  3943. return (char *)NULL;
  3944. }
  3945. #define TKWAIT_MODE_VISIBILITY 1
  3946. #define TKWAIT_MODE_DESTROY 2
  3947. static void rb_threadWaitVisibilityProc _((ClientData, XEvent *));
  3948. static void
  3949. rb_threadWaitVisibilityProc(clientData, eventPtr)
  3950. ClientData clientData; /* Pointer to integer to set to 1. */
  3951. XEvent *eventPtr; /* Information about event (not used). */
  3952. {
  3953. struct th_vwait_param *param = (struct th_vwait_param *) clientData;
  3954. if (eventPtr->type == VisibilityNotify) {
  3955. param->done = TKWAIT_MODE_VISIBILITY;
  3956. }
  3957. if (eventPtr->type == DestroyNotify) {
  3958. param->done = TKWAIT_MODE_DESTROY;
  3959. }
  3960. if (param->done != 0) rb_thread_wakeup(param->thread);
  3961. }
  3962. static void rb_threadWaitWindowProc _((ClientData, XEvent *));
  3963. static void
  3964. rb_threadWaitWindowProc(clientData, eventPtr)
  3965. ClientData clientData; /* Pointer to integer to set to 1. */
  3966. XEvent *eventPtr; /* Information about event. */
  3967. {
  3968. struct th_vwait_param *param = (struct th_vwait_param *) clientData;
  3969. if (eventPtr->type == DestroyNotify) {
  3970. param->done = TKWAIT_MODE_DESTROY;
  3971. }
  3972. if (param->done != 0) rb_thread_wakeup(param->thread);
  3973. }
  3974. #if TCL_MAJOR_VERSION >= 8
  3975. static int
  3976. ip_rb_threadVwaitObjCmd(clientData, interp, objc, objv)
  3977. ClientData clientData;
  3978. Tcl_Interp *interp;
  3979. int objc;
  3980. Tcl_Obj *CONST objv[];
  3981. #else /* TCL_MAJOR_VERSION < 8 */
  3982. static int
  3983. ip_rb_threadVwaitCommand(clientData, interp, objc, objv)
  3984. ClientData clientData; /* Not used */
  3985. Tcl_Interp *interp;
  3986. int objc;
  3987. char *objv[];
  3988. #endif
  3989. {
  3990. struct th_vwait_param *param;
  3991. char *nameString;
  3992. int ret, dummy;
  3993. int thr_crit_bup;
  3994. volatile VALUE current_thread = rb_thread_current();
  3995. struct timeval t;
  3996. DUMP1("Ruby's 'thread_vwait' is called");
  3997. if (interp == (Tcl_Interp*)NULL) {
  3998. rbtk_pending_exception = rb_exc_new2(rb_eRuntimeError,
  3999. "IP is deleted");
  4000. return TCL_ERROR;
  4001. }
  4002. if (rb_thread_alone() || eventloop_thread == current_thread) {
  4003. #if TCL_MAJOR_VERSION >= 8
  4004. DUMP1("call ip_rbVwaitObjCmd");
  4005. return ip_rbVwaitObjCmd(clientData, interp, objc, objv);
  4006. #else /* TCL_MAJOR_VERSION < 8 */
  4007. DUMP1("call ip_rbVwaitCommand");
  4008. return ip_rbVwaitCommand(clientData, interp, objc, objv);
  4009. #endif
  4010. }
  4011. Tcl_Preserve(interp);
  4012. Tcl_ResetResult(interp);
  4013. if (objc != 2) {
  4014. #ifdef Tcl_WrongNumArgs
  4015. Tcl_WrongNumArgs(interp, 1, objv, "name");
  4016. #else
  4017. thr_crit_bup = rb_thread_critical;
  4018. rb_thread_critical = Qtrue;
  4019. #if TCL_MAJOR_VERSION >= 8
  4020. /* nameString = Tcl_GetString(objv[0]); */
  4021. nameString = Tcl_GetStringFromObj(objv[0], &dummy);
  4022. #else /* TCL_MAJOR_VERSION < 8 */
  4023. nameString = objv[0];
  4024. #endif
  4025. Tcl_AppendResult(interp, "wrong number of arguments: should be \"",
  4026. nameString, " name\"", (char *) NULL);
  4027. rb_thread_critical = thr_crit_bup;
  4028. #endif
  4029. Tcl_Release(interp);
  4030. return TCL_ERROR;
  4031. }
  4032. #if TCL_MAJOR_VERSION >= 8
  4033. Tcl_IncrRefCount(objv[1]);
  4034. /* nameString = Tcl_GetString(objv[1]); */
  4035. nameString = Tcl_GetStringFromObj(objv[1], &dummy);
  4036. #else /* TCL_MAJOR_VERSION < 8 */
  4037. nameString = objv[1];
  4038. #endif
  4039. thr_crit_bup = rb_thread_critical;
  4040. rb_thread_critical = Qtrue;
  4041. /* param = (struct th_vwait_param *)Tcl_Alloc(sizeof(struct th_vwait_param)); */
  4042. param = (struct th_vwait_param *)ckalloc(sizeof(struct th_vwait_param));
  4043. #if 1 /* use Tcl_Preserve/Release */
  4044. Tcl_Preserve((ClientData)param);
  4045. #endif
  4046. param->thread = current_thread;
  4047. param->done = 0;
  4048. /*
  4049. if (Tcl_TraceVar(interp, nameString,
  4050. TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  4051. rb_threadVwaitProc, (ClientData) param) != TCL_OK) {
  4052. return TCL_ERROR;
  4053. }
  4054. */
  4055. ret = Tcl_TraceVar(interp, nameString,
  4056. TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  4057. rb_threadVwaitProc, (ClientData) param);
  4058. rb_thread_critical = thr_crit_bup;
  4059. if (ret != TCL_OK) {
  4060. #if 0 /* use Tcl_EventuallyFree */
  4061. Tcl_EventuallyFree((ClientData)param, TCL_DYNAMIC); /* XXXXXXXX */
  4062. #else
  4063. #if 1 /* use Tcl_Preserve/Release */
  4064. Tcl_Release((ClientData)param);
  4065. #else
  4066. /* Tcl_Free((char *)param); */
  4067. ckfree((char *)param);
  4068. #endif
  4069. #endif
  4070. #if TCL_MAJOR_VERSION >= 8
  4071. Tcl_DecrRefCount(objv[1]);
  4072. #endif
  4073. Tcl_Release(interp);
  4074. return TCL_ERROR;
  4075. }
  4076. t.tv_sec = 0;
  4077. t.tv_usec = (long)((EVENT_HANDLER_TIMEOUT)*1000.0);
  4078. while(!param->done) {
  4079. /* rb_thread_stop(); */
  4080. /* rb_thread_sleep_forever(); */
  4081. rb_thread_wait_for(t);
  4082. if (NIL_P(eventloop_thread)) {
  4083. break;
  4084. }
  4085. }
  4086. thr_crit_bup = rb_thread_critical;
  4087. rb_thread_critical = Qtrue;
  4088. if (param->done > 0) {
  4089. Tcl_UntraceVar(interp, nameString,
  4090. TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  4091. rb_threadVwaitProc, (ClientData) param);
  4092. }
  4093. #if 0 /* use Tcl_EventuallyFree */
  4094. Tcl_EventuallyFree((ClientData)param, TCL_DYNAMIC); /* XXXXXXXX */
  4095. #else
  4096. #if 1 /* use Tcl_Preserve/Release */
  4097. Tcl_Release((ClientData)param);
  4098. #else
  4099. /* Tcl_Free((char *)param); */
  4100. ckfree((char *)param);
  4101. #endif
  4102. #endif
  4103. rb_thread_critical = thr_crit_bup;
  4104. #if TCL_MAJOR_VERSION >= 8
  4105. Tcl_DecrRefCount(objv[1]);
  4106. #endif
  4107. Tcl_Release(interp);
  4108. return TCL_OK;
  4109. }
  4110. #if TCL_MAJOR_VERSION >= 8
  4111. static int
  4112. ip_rb_threadTkWaitObjCmd(clientData, interp, objc, objv)
  4113. ClientData clientData;
  4114. Tcl_Interp *interp;
  4115. int objc;
  4116. Tcl_Obj *CONST objv[];
  4117. #else /* TCL_MAJOR_VERSION < 8 */
  4118. static int
  4119. ip_rb_threadTkWaitCommand(clientData, interp, objc, objv)
  4120. ClientData clientData;
  4121. Tcl_Interp *interp;
  4122. int objc;
  4123. char *objv[];
  4124. #endif
  4125. {
  4126. struct th_vwait_param *param;
  4127. Tk_Window tkwin = (Tk_Window) clientData;
  4128. Tk_Window window;
  4129. int index;
  4130. static CONST char *optionStrings[] = { "variable", "visibility", "window",
  4131. (char *) NULL };
  4132. enum options { TKWAIT_VARIABLE, TKWAIT_VISIBILITY, TKWAIT_WINDOW };
  4133. char *nameString;
  4134. int ret, dummy;
  4135. int thr_crit_bup;
  4136. volatile VALUE current_thread = rb_thread_current();
  4137. struct timeval t;
  4138. DUMP1("Ruby's 'thread_tkwait' is called");
  4139. if (interp == (Tcl_Interp*)NULL) {
  4140. rbtk_pending_exception = rb_exc_new2(rb_eRuntimeError,
  4141. "IP is deleted");
  4142. return TCL_ERROR;
  4143. }
  4144. if (rb_thread_alone() || eventloop_thread == current_thread) {
  4145. #if TCL_MAJOR_VERSION >= 8
  4146. DUMP1("call ip_rbTkWaitObjCmd");
  4147. DUMP2("eventloop_thread %lx", eventloop_thread);
  4148. DUMP2("current_thread %lx", current_thread);
  4149. return ip_rbTkWaitObjCmd(clientData, interp, objc, objv);
  4150. #else /* TCL_MAJOR_VERSION < 8 */
  4151. DUMP1("call rb_VwaitCommand");
  4152. return ip_rbTkWaitCommand(clientData, interp, objc, objv);
  4153. #endif
  4154. }
  4155. Tcl_Preserve(interp);
  4156. Tcl_Preserve(tkwin);
  4157. Tcl_ResetResult(interp);
  4158. if (objc != 3) {
  4159. #ifdef Tcl_WrongNumArgs
  4160. Tcl_WrongNumArgs(interp, 1, objv, "variable|visibility|window name");
  4161. #else
  4162. thr_crit_bup = rb_thread_critical;
  4163. rb_thread_critical = Qtrue;
  4164. #if TCL_MAJOR_VERSION >= 8
  4165. Tcl_AppendResult(interp, "wrong number of arguments: should be \"",
  4166. Tcl_GetStringFromObj(objv[0], &dummy),
  4167. " variable|visibility|window name\"",
  4168. (char *) NULL);
  4169. #else /* TCL_MAJOR_VERSION < 8 */
  4170. Tcl_AppendResult(interp, "wrong number of arguments: should be \"",
  4171. objv[0], " variable|visibility|window name\"",
  4172. (char *) NULL);
  4173. #endif
  4174. rb_thread_critical = thr_crit_bup;
  4175. #endif
  4176. Tcl_Release(tkwin);
  4177. Tcl_Release(interp);
  4178. return TCL_ERROR;
  4179. }
  4180. #if TCL_MAJOR_VERSION >= 8
  4181. thr_crit_bup = rb_thread_critical;
  4182. rb_thread_critical = Qtrue;
  4183. /*
  4184. if (Tcl_GetIndexFromObj(interp, objv[1],
  4185. (CONST84 char **)optionStrings,
  4186. "option", 0, &index) != TCL_OK) {
  4187. return TCL_ERROR;
  4188. }
  4189. */
  4190. ret = Tcl_GetIndexFromObj(interp, objv[1],
  4191. (CONST84 char **)optionStrings,
  4192. "option", 0, &index);
  4193. rb_thread_critical = thr_crit_bup;
  4194. if (ret != TCL_OK) {
  4195. Tcl_Release(tkwin);
  4196. Tcl_Release(interp);
  4197. return TCL_ERROR;
  4198. }
  4199. #else /* TCL_MAJOR_VERSION < 8 */
  4200. {
  4201. int c = objv[1][0];
  4202. size_t length = strlen(objv[1]);
  4203. if ((c == 'v') && (strncmp(objv[1], "variable", length) == 0)
  4204. && (length >= 2)) {
  4205. index = TKWAIT_VARIABLE;
  4206. } else if ((c == 'v') && (strncmp(objv[1], "visibility", length) == 0)
  4207. && (length >= 2)) {
  4208. index = TKWAIT_VISIBILITY;
  4209. } else if ((c == 'w') && (strncmp(objv[1], "window", length) == 0)) {
  4210. index = TKWAIT_WINDOW;
  4211. } else {
  4212. Tcl_AppendResult(interp, "bad option \"", objv[1],
  4213. "\": must be variable, visibility, or window",
  4214. (char *) NULL);
  4215. Tcl_Release(tkwin);
  4216. Tcl_Release(interp);
  4217. return TCL_ERROR;
  4218. }
  4219. }
  4220. #endif
  4221. thr_crit_bup = rb_thread_critical;
  4222. rb_thread_critical = Qtrue;
  4223. #if TCL_MAJOR_VERSION >= 8
  4224. Tcl_IncrRefCount(objv[2]);
  4225. /* nameString = Tcl_GetString(objv[2]); */
  4226. nameString = Tcl_GetStringFromObj(objv[2], &dummy);
  4227. #else /* TCL_MAJOR_VERSION < 8 */
  4228. nameString = objv[2];
  4229. #endif
  4230. /* param = (struct th_vwait_param *)Tcl_Alloc(sizeof(struct th_vwait_param)); */
  4231. param = (struct th_vwait_param *)ckalloc(sizeof(struct th_vwait_param));
  4232. #if 1 /* use Tcl_Preserve/Release */
  4233. Tcl_Preserve((ClientData)param);
  4234. #endif
  4235. param->thread = current_thread;
  4236. param->done = 0;
  4237. rb_thread_critical = thr_crit_bup;
  4238. switch ((enum options) index) {
  4239. case TKWAIT_VARIABLE:
  4240. thr_crit_bup = rb_thread_critical;
  4241. rb_thread_critical = Qtrue;
  4242. /*
  4243. if (Tcl_TraceVar(interp, nameString,
  4244. TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  4245. rb_threadVwaitProc, (ClientData) param) != TCL_OK) {
  4246. return TCL_ERROR;
  4247. }
  4248. */
  4249. ret = Tcl_TraceVar(interp, nameString,
  4250. TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  4251. rb_threadVwaitProc, (ClientData) param);
  4252. rb_thread_critical = thr_crit_bup;
  4253. if (ret != TCL_OK) {
  4254. #if 0 /* use Tcl_EventuallyFree */
  4255. Tcl_EventuallyFree((ClientData)param, TCL_DYNAMIC); /* XXXXXXXX */
  4256. #else
  4257. #if 1 /* use Tcl_Preserve/Release */
  4258. Tcl_Release(param);
  4259. #else
  4260. /* Tcl_Free((char *)param); */
  4261. ckfree((char *)param);
  4262. #endif
  4263. #endif
  4264. #if TCL_MAJOR_VERSION >= 8
  4265. Tcl_DecrRefCount(objv[2]);
  4266. #endif
  4267. Tcl_Release(tkwin);
  4268. Tcl_Release(interp);
  4269. return TCL_ERROR;
  4270. }
  4271. t.tv_sec = 0;
  4272. t.tv_usec = (long)((EVENT_HANDLER_TIMEOUT)*1000.0);
  4273. while(!param->done) {
  4274. /* rb_thread_stop(); */
  4275. /* rb_thread_sleep_forever(); */
  4276. rb_thread_wait_for(t);
  4277. if (NIL_P(eventloop_thread)) {
  4278. break;
  4279. }
  4280. }
  4281. thr_crit_bup = rb_thread_critical;
  4282. rb_thread_critical = Qtrue;
  4283. if (param->done > 0) {
  4284. Tcl_UntraceVar(interp, nameString,
  4285. TCL_GLOBAL_ONLY|TCL_TRACE_WRITES|TCL_TRACE_UNSETS,
  4286. rb_threadVwaitProc, (ClientData) param);
  4287. }
  4288. #if TCL_MAJOR_VERSION >= 8
  4289. Tcl_DecrRefCount(objv[2]);
  4290. #endif
  4291. rb_thread_critical = thr_crit_bup;
  4292. break;
  4293. case TKWAIT_VISIBILITY:
  4294. thr_crit_bup = rb_thread_critical;
  4295. rb_thread_critical = Qtrue;
  4296. #if 0 /* variable 'tkwin' must keep the token of MainWindow */
  4297. if (!tk_stubs_init_p() || Tk_MainWindow(interp) == (Tk_Window)NULL) {
  4298. window = NULL;
  4299. } else {
  4300. window = Tk_NameToWindow(interp, nameString, tkwin);
  4301. }
  4302. #else
  4303. if (!tk_stubs_init_p() || tkwin == (Tk_Window)NULL) {
  4304. window = NULL;
  4305. } else {
  4306. /* Tk_NameToWindow() returns right token on non-eventloop thread */
  4307. Tcl_CmdInfo info;
  4308. if (Tcl_GetCommandInfo(interp, ".", &info)) { /* check root */
  4309. window = Tk_NameToWindow(interp, nameString, tkwin);
  4310. } else {
  4311. window = NULL;
  4312. }
  4313. }
  4314. #endif
  4315. if (window == NULL) {
  4316. Tcl_AppendResult(interp, ": thread_tkwait: ",
  4317. "no main-window (not Tk application?)",
  4318. (char*)NULL);
  4319. rb_thread_critical = thr_crit_bup;
  4320. #if 0 /* use Tcl_EventuallyFree */
  4321. Tcl_EventuallyFree((ClientData)param, TCL_DYNAMIC); /* XXXXXXXX */
  4322. #else
  4323. #if 1 /* use Tcl_Preserve/Release */
  4324. Tcl_Release(param);
  4325. #else
  4326. /* Tcl_Free((char *)param); */
  4327. ckfree((char *)param);
  4328. #endif
  4329. #endif
  4330. #if TCL_MAJOR_VERSION >= 8
  4331. Tcl_DecrRefCount(objv[2]);
  4332. #endif
  4333. Tcl_Release(tkwin);
  4334. Tcl_Release(interp);
  4335. return TCL_ERROR;
  4336. }
  4337. Tcl_Preserve(window);
  4338. Tk_CreateEventHandler(window,
  4339. VisibilityChangeMask|StructureNotifyMask,
  4340. rb_threadWaitVisibilityProc, (ClientData) param);
  4341. rb_thread_critical = thr_crit_bup;
  4342. t.tv_sec = 0;
  4343. t.tv_usec = (long)((EVENT_HANDLER_TIMEOUT)*1000.0);
  4344. while(param->done != TKWAIT_MODE_VISIBILITY) {
  4345. if (param->done == TKWAIT_MODE_DESTROY) break;
  4346. /* rb_thread_stop(); */
  4347. /* rb_thread_sleep_forever(); */
  4348. rb_thread_wait_for(t);
  4349. if (NIL_P(eventloop_thread)) {
  4350. break;
  4351. }
  4352. }
  4353. thr_crit_bup = rb_thread_critical;
  4354. rb_thread_critical = Qtrue;
  4355. /* when a window is destroyed, no need to call Tk_DeleteEventHandler */
  4356. if (param->done != TKWAIT_MODE_DESTROY) {
  4357. Tk_DeleteEventHandler(window,
  4358. VisibilityChangeMask|StructureNotifyMask,
  4359. rb_threadWaitVisibilityProc,
  4360. (ClientData) param);
  4361. }
  4362. if (param->done != 1) {
  4363. Tcl_ResetResult(interp);
  4364. Tcl_AppendResult(interp, "window \"", nameString,
  4365. "\" was deleted before its visibility changed",
  4366. (char *) NULL);
  4367. rb_thread_critical = thr_crit_bup;
  4368. Tcl_Release(window);
  4369. #if 0 /* use Tcl_EventuallyFree */
  4370. Tcl_EventuallyFree((ClientData)param, TCL_DYNAMIC); /* XXXXXXXX */
  4371. #else
  4372. #if 1 /* use Tcl_Preserve/Release */
  4373. Tcl_Release(param);
  4374. #else
  4375. /* Tcl_Free((char *)param); */
  4376. ckfree((char *)param);
  4377. #endif
  4378. #endif
  4379. #if TCL_MAJOR_VERSION >= 8
  4380. Tcl_DecrRefCount(objv[2]);
  4381. #endif
  4382. Tcl_Release(tkwin);
  4383. Tcl_Release(interp);
  4384. return TCL_ERROR;
  4385. }
  4386. Tcl_Release(window);
  4387. #if TCL_MAJOR_VERSION >= 8
  4388. Tcl_DecrRefCount(objv[2]);
  4389. #endif
  4390. rb_thread_critical = thr_crit_bup;
  4391. break;
  4392. case TKWAIT_WINDOW:
  4393. thr_crit_bup = rb_thread_critical;
  4394. rb_thread_critical = Qtrue;
  4395. #if 0 /* variable 'tkwin' must keep the token of MainWindow */
  4396. if (!tk_stubs_init_p() || Tk_MainWindow(interp) == (Tk_Window)NULL) {
  4397. window = NULL;
  4398. } else {
  4399. window = Tk_NameToWindow(interp, nameString, tkwin);
  4400. }
  4401. #else
  4402. if (!tk_stubs_init_p() || tkwin == (Tk_Window)NULL) {
  4403. window = NULL;
  4404. } else {
  4405. /* Tk_NameToWindow() returns right token on non-eventloop thread */
  4406. Tcl_CmdInfo info;
  4407. if (Tcl_GetCommandInfo(interp, ".", &info)) { /* check root */
  4408. window = Tk_NameToWindow(interp, nameString, tkwin);
  4409. } else {
  4410. window = NULL;
  4411. }
  4412. }
  4413. #endif
  4414. #if TCL_MAJOR_VERSION >= 8
  4415. Tcl_DecrRefCount(objv[2]);
  4416. #endif
  4417. if (window == NULL) {
  4418. Tcl_AppendResult(interp, ": thread_tkwait: ",
  4419. "no main-window (not Tk application?)",
  4420. (char*)NULL);
  4421. rb_thread_critical = thr_crit_bup;
  4422. #if 0 /* use Tcl_EventuallyFree */
  4423. Tcl_EventuallyFree((ClientData)param, TCL_DYNAMIC); /* XXXXXXXX */
  4424. #else
  4425. #if 1 /* use Tcl_Preserve/Release */
  4426. Tcl_Release(param);
  4427. #else
  4428. /* Tcl_Free((char *)param); */
  4429. ckfree((char *)param);
  4430. #endif
  4431. #endif
  4432. Tcl_Release(tkwin);
  4433. Tcl_Release(interp);
  4434. return TCL_ERROR;
  4435. }
  4436. Tcl_Preserve(window);
  4437. Tk_CreateEventHandler(window, StructureNotifyMask,
  4438. rb_threadWaitWindowProc, (ClientData) param);
  4439. rb_thread_critical = thr_crit_bup;
  4440. t.tv_sec = 0;
  4441. t.tv_usec = (long)((EVENT_HANDLER_TIMEOUT)*1000.0);
  4442. while(param->done != TKWAIT_MODE_DESTROY) {
  4443. /* rb_thread_stop(); */
  4444. /* rb_thread_sleep_forever(); */
  4445. rb_thread_wait_for(t);
  4446. if (NIL_P(eventloop_thread)) {
  4447. break;
  4448. }
  4449. }
  4450. Tcl_Release(window);
  4451. /* when a window is destroyed, no need to call Tk_DeleteEventHandler
  4452. thr_crit_bup = rb_thread_critical;
  4453. rb_thread_critical = Qtrue;
  4454. Tk_DeleteEventHandler(window, StructureNotifyMask,
  4455. rb_threadWaitWindowProc, (ClientData) param);
  4456. rb_thread_critical = thr_crit_bup;
  4457. */
  4458. break;
  4459. } /* end of 'switch' statement */
  4460. #if 0 /* use Tcl_EventuallyFree */
  4461. Tcl_EventuallyFree((ClientData)param, TCL_DYNAMIC); /* XXXXXXXX */
  4462. #else
  4463. #if 1 /* use Tcl_Preserve/Release */
  4464. Tcl_Release((ClientData)param);
  4465. #else
  4466. /* Tcl_Free((char *)param); */
  4467. ckfree((char *)param);
  4468. #endif
  4469. #endif
  4470. /*
  4471. * Clear out the interpreter's result, since it may have been set
  4472. * by event handlers.
  4473. */
  4474. Tcl_ResetResult(interp);
  4475. Tcl_Release(tkwin);
  4476. Tcl_Release(interp);
  4477. return TCL_OK;
  4478. }
  4479. static VALUE
  4480. ip_thread_vwait(self, var)
  4481. VALUE self;
  4482. VALUE var;
  4483. {
  4484. VALUE argv[2];
  4485. volatile VALUE cmd_str = rb_str_new2("thread_vwait");
  4486. argv[0] = cmd_str;
  4487. argv[1] = var;
  4488. return ip_invoke_with_position(2, argv, self, TCL_QUEUE_TAIL);
  4489. }
  4490. static VALUE
  4491. ip_thread_tkwait(self, mode, target)
  4492. VALUE self;
  4493. VALUE mode;
  4494. VALUE target;
  4495. {
  4496. VALUE argv[3];
  4497. volatile VALUE cmd_str = rb_str_new2("thread_tkwait");
  4498. argv[0] = cmd_str;
  4499. argv[1] = mode;
  4500. argv[2] = target;
  4501. return ip_invoke_with_position(3, argv, self, TCL_QUEUE_TAIL);
  4502. }
  4503. /* delete slave interpreters */
  4504. #if TCL_MAJOR_VERSION >= 8
  4505. static void
  4506. delete_slaves(ip)
  4507. Tcl_Interp *ip;
  4508. {
  4509. int thr_crit_bup;
  4510. Tcl_Interp *slave;
  4511. Tcl_Obj *slave_list, *elem;
  4512. char *slave_name;
  4513. int i, len;
  4514. DUMP1("delete slaves");
  4515. thr_crit_bup = rb_thread_critical;
  4516. rb_thread_critical = Qtrue;
  4517. if (!Tcl_InterpDeleted(ip) && Tcl_Eval(ip, "interp slaves") == TCL_OK) {
  4518. slave_list = Tcl_GetObjResult(ip);
  4519. Tcl_IncrRefCount(slave_list);
  4520. if (Tcl_ListObjLength((Tcl_Interp*)NULL, slave_list, &len) == TCL_OK) {
  4521. for(i = 0; i < len; i++) {
  4522. Tcl_ListObjIndex((Tcl_Interp*)NULL, slave_list, i, &elem);
  4523. if (elem == (Tcl_Obj*)NULL) continue;
  4524. Tcl_IncrRefCount(elem);
  4525. /* get slave */
  4526. /* slave_name = Tcl_GetString(elem); */
  4527. slave_name = Tcl_GetStringFromObj(elem, (int*)NULL);
  4528. DUMP2("delete slave:'%s'", slave_name);
  4529. Tcl_DecrRefCount(elem);
  4530. slave = Tcl_GetSlave(ip, slave_name);
  4531. if (slave == (Tcl_Interp*)NULL) continue;
  4532. if (!Tcl_InterpDeleted(slave)) {
  4533. /* call ip_finalize */
  4534. ip_finalize(slave);
  4535. Tcl_DeleteInterp(slave);
  4536. /* Tcl_Release(slave); */
  4537. }
  4538. }
  4539. }
  4540. Tcl_DecrRefCount(slave_list);
  4541. }
  4542. rb_thread_critical = thr_crit_bup;
  4543. }
  4544. #else /* TCL_MAJOR_VERSION < 8 */
  4545. static void
  4546. delete_slaves(ip)
  4547. Tcl_Interp *ip;
  4548. {
  4549. int thr_crit_bup;
  4550. Tcl_Interp *slave;
  4551. int argc;
  4552. char **argv;
  4553. char *slave_list;
  4554. char *slave_name;
  4555. int i, len;
  4556. DUMP1("delete slaves");
  4557. thr_crit_bup = rb_thread_critical;
  4558. rb_thread_critical = Qtrue;
  4559. if (!Tcl_InterpDeleted(ip) && Tcl_Eval(ip, "interp slaves") == TCL_OK) {
  4560. slave_list = ip->result;
  4561. if (Tcl_SplitList((Tcl_Interp*)NULL,
  4562. slave_list, &argc, &argv) == TCL_OK) {
  4563. for(i = 0; i < argc; i++) {
  4564. slave_name = argv[i];
  4565. DUMP2("delete slave:'%s'", slave_name);
  4566. slave = Tcl_GetSlave(ip, slave_name);
  4567. if (slave == (Tcl_Interp*)NULL) continue;
  4568. if (!Tcl_InterpDeleted(slave)) {
  4569. /* call ip_finalize */
  4570. ip_finalize(slave);
  4571. Tcl_DeleteInterp(slave);
  4572. }
  4573. }
  4574. }
  4575. }
  4576. rb_thread_critical = thr_crit_bup;
  4577. }
  4578. #endif
  4579. /* finalize operation */
  4580. static void
  4581. #ifdef HAVE_PROTOTYPES
  4582. lib_mark_at_exit(VALUE self)
  4583. #else
  4584. lib_mark_at_exit(self)
  4585. VALUE self;
  4586. #endif
  4587. {
  4588. at_exit = 1;
  4589. }
  4590. static int
  4591. #if TCL_MAJOR_VERSION >= 8
  4592. #ifdef HAVE_PROTOTYPES
  4593. ip_null_proc(ClientData clientData, Tcl_Interp *interp,
  4594. int argc, Tcl_Obj *CONST argv[])
  4595. #else
  4596. ip_null_proc(clientData, interp, argc, argv)
  4597. ClientData clientData;
  4598. Tcl_Interp *interp;
  4599. int argc;
  4600. Tcl_Obj *CONST argv[];
  4601. #endif
  4602. #else /* TCL_MAJOR_VERSION < 8 */
  4603. #ifdef HAVE_PROTOTYPES
  4604. ip_null_proc(ClientData clientData, Tcl_Interp *interp, int argc, char *argv[])
  4605. #else
  4606. ip_null_proc(clientData, interp, argc, argv)
  4607. ClientData clientData;
  4608. Tcl_Interp *interp;
  4609. int argc;
  4610. char *argv[];
  4611. #endif
  4612. #endif
  4613. {
  4614. Tcl_ResetResult(interp);
  4615. return TCL_OK;
  4616. }
  4617. static void
  4618. ip_finalize(ip)
  4619. Tcl_Interp *ip;
  4620. {
  4621. Tcl_CmdInfo info;
  4622. int thr_crit_bup;
  4623. VALUE rb_debug_bup, rb_verbose_bup;
  4624. /* When ruby is exiting, printing debug messages in some callback
  4625. operations from Tcl-IP sometimes cause SEGV. I don't know the
  4626. reason. But I got SEGV when calling "rb_io_write(rb_stdout, ...)".
  4627. So, in some part of this function, debug mode and verbose mode
  4628. are disabled. If you know the reason, please fix it.
  4629. -- Hidetoshi NAGAI (nagai@ai.kyutech.ac.jp) */
  4630. DUMP1("start ip_finalize");
  4631. if (ip == (Tcl_Interp*)NULL) {
  4632. DUMP1("ip is NULL");
  4633. return;
  4634. }
  4635. if (Tcl_InterpDeleted(ip)) {
  4636. DUMP2("ip(%p) is already deleted", ip);
  4637. return;
  4638. }
  4639. #if TCL_NAMESPACE_DEBUG
  4640. if (ip_null_namespace(ip)) {
  4641. DUMP2("ip(%p) has null namespace", ip);
  4642. return;
  4643. }
  4644. #endif
  4645. thr_crit_bup = rb_thread_critical;
  4646. rb_thread_critical = Qtrue;
  4647. rb_debug_bup = ruby_debug;
  4648. rb_verbose_bup = ruby_verbose;
  4649. Tcl_Preserve(ip);
  4650. /* delete slaves */
  4651. delete_slaves(ip);
  4652. /* shut off some connections from Tcl-proc to Ruby */
  4653. if (at_exit) {
  4654. /* NOTE: Only when at exit.
  4655. Because, ruby removes objects, which depends on the deleted
  4656. interpreter, on some callback operations.
  4657. It is important for GC. */
  4658. #if TCL_MAJOR_VERSION >= 8
  4659. Tcl_CreateObjCommand(ip, "ruby", ip_null_proc,
  4660. (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
  4661. Tcl_CreateObjCommand(ip, "ruby_eval", ip_null_proc,
  4662. (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
  4663. Tcl_CreateObjCommand(ip, "ruby_cmd", ip_null_proc,
  4664. (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
  4665. #else /* TCL_MAJOR_VERSION < 8 */
  4666. Tcl_CreateCommand(ip, "ruby", ip_null_proc,
  4667. (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
  4668. Tcl_CreateCommand(ip, "ruby_eval", ip_null_proc,
  4669. (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
  4670. Tcl_CreateCommand(ip, "ruby_cmd", ip_null_proc,
  4671. (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
  4672. #endif
  4673. /*
  4674. rb_thread_critical = thr_crit_bup;
  4675. return;
  4676. */
  4677. }
  4678. /* delete root widget */
  4679. #ifdef RUBY_VM
  4680. /* cause SEGV on Ruby 1.9 */
  4681. #else
  4682. DUMP1("check `destroy'");
  4683. if (Tcl_GetCommandInfo(ip, "destroy", &info)) {
  4684. DUMP1("call `destroy .'");
  4685. Tcl_GlobalEval(ip, "catch {destroy .}");
  4686. }
  4687. #endif
  4688. #if 1
  4689. DUMP1("destroy root widget");
  4690. if (tk_stubs_init_p() && Tk_MainWindow(ip) != (Tk_Window)NULL) {
  4691. /*
  4692. * On Ruby VM, this code piece may be not called, because
  4693. * Tk_MainWindow() returns NULL on a native thread except
  4694. * the thread which initialize Tk environment.
  4695. * Of course, that is a problem. But maybe not so serious.
  4696. * All widgets are destroyed when the Tcl interp is deleted.
  4697. * At then, Ruby may raise exceptions on the delete hook
  4698. * callbacks which registered for the deleted widgets, and
  4699. * may fail to clear objects which depends on the widgets.
  4700. * Although it is the problem, it is possibly avoidable by
  4701. * rescuing exceptions and the finalize hook of the interp.
  4702. */
  4703. Tk_Window win = Tk_MainWindow(ip);
  4704. DUMP1("call Tk_DestroyWindow");
  4705. ruby_debug = Qfalse;
  4706. ruby_verbose = Qnil;
  4707. if (! (((Tk_FakeWin*)win)->flags & TK_ALREADY_DEAD)) {
  4708. Tk_DestroyWindow(win);
  4709. }
  4710. ruby_debug = rb_debug_bup;
  4711. ruby_verbose = rb_verbose_bup;
  4712. }
  4713. #endif
  4714. /* call finalize-hook-proc */
  4715. DUMP1("check `finalize-hook-proc'");
  4716. if ( Tcl_GetCommandInfo(ip, finalize_hook_name, &info)) {
  4717. DUMP2("call finalize hook proc '%s'", finalize_hook_name);
  4718. ruby_debug = Qfalse;
  4719. ruby_verbose = Qnil;
  4720. Tcl_GlobalEval(ip, finalize_hook_name);
  4721. ruby_debug = rb_debug_bup;
  4722. ruby_verbose = rb_verbose_bup;
  4723. }
  4724. DUMP1("check `foreach' & `after'");
  4725. if ( Tcl_GetCommandInfo(ip, "foreach", &info)
  4726. && Tcl_GetCommandInfo(ip, "after", &info) ) {
  4727. DUMP1("cancel after callbacks");
  4728. ruby_debug = Qfalse;
  4729. ruby_verbose = Qnil;
  4730. Tcl_GlobalEval(ip, "catch {foreach id [after info] {after cancel $id}}");
  4731. ruby_debug = rb_debug_bup;
  4732. ruby_verbose = rb_verbose_bup;
  4733. }
  4734. Tcl_Release(ip);
  4735. DUMP1("finish ip_finalize");
  4736. ruby_debug = rb_debug_bup;
  4737. ruby_verbose = rb_verbose_bup;
  4738. rb_thread_critical = thr_crit_bup;
  4739. }
  4740. /* destroy interpreter */
  4741. static void
  4742. ip_free(ptr)
  4743. struct tcltkip *ptr;
  4744. {
  4745. int thr_crit_bup;
  4746. DUMP2("free Tcl Interp %lx", (unsigned long)ptr->ip);
  4747. if (ptr) {
  4748. thr_crit_bup = rb_thread_critical;
  4749. rb_thread_critical = Qtrue;
  4750. if ( ptr->ip != (Tcl_Interp*)NULL
  4751. && !Tcl_InterpDeleted(ptr->ip)
  4752. && Tcl_GetMaster(ptr->ip) != (Tcl_Interp*)NULL
  4753. && !Tcl_InterpDeleted(Tcl_GetMaster(ptr->ip)) ) {
  4754. DUMP2("parent IP(%lx) is not deleted",
  4755. (unsigned long)Tcl_GetMaster(ptr->ip));
  4756. DUMP2("slave IP(%lx) should not be deleted",
  4757. (unsigned long)ptr->ip);
  4758. xfree(ptr);
  4759. /* ckfree((char*)ptr); */
  4760. rb_thread_critical = thr_crit_bup;
  4761. return;
  4762. }
  4763. if (ptr->ip == (Tcl_Interp*)NULL) {
  4764. DUMP1("ip_free is called for deleted IP");
  4765. xfree(ptr);
  4766. /* ckfree((char*)ptr); */
  4767. rb_thread_critical = thr_crit_bup;
  4768. return;
  4769. }
  4770. if (!Tcl_InterpDeleted(ptr->ip)) {
  4771. ip_finalize(ptr->ip);
  4772. Tcl_DeleteInterp(ptr->ip);
  4773. Tcl_Release(ptr->ip);
  4774. }
  4775. ptr->ip = (Tcl_Interp*)NULL;
  4776. xfree(ptr);
  4777. /* ckfree((char*)ptr); */
  4778. rb_thread_critical = thr_crit_bup;
  4779. }
  4780. DUMP1("complete freeing Tcl Interp");
  4781. }
  4782. /* create and initialize interpreter */
  4783. static VALUE ip_alloc _((VALUE));
  4784. static VALUE
  4785. ip_alloc(self)
  4786. VALUE self;
  4787. {
  4788. return Data_Wrap_Struct(self, 0, ip_free, 0);
  4789. }
  4790. static void
  4791. ip_replace_wait_commands(interp, mainWin)
  4792. Tcl_Interp *interp;
  4793. Tk_Window mainWin;
  4794. {
  4795. /* replace 'vwait' command */
  4796. #if TCL_MAJOR_VERSION >= 8
  4797. DUMP1("Tcl_CreateObjCommand(\"vwait\")");
  4798. Tcl_CreateObjCommand(interp, "vwait", ip_rbVwaitObjCmd,
  4799. (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
  4800. #else /* TCL_MAJOR_VERSION < 8 */
  4801. DUMP1("Tcl_CreateCommand(\"vwait\")");
  4802. Tcl_CreateCommand(interp, "vwait", ip_rbVwaitCommand,
  4803. (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
  4804. #endif
  4805. /* replace 'tkwait' command */
  4806. #if TCL_MAJOR_VERSION >= 8
  4807. DUMP1("Tcl_CreateObjCommand(\"tkwait\")");
  4808. Tcl_CreateObjCommand(interp, "tkwait", ip_rbTkWaitObjCmd,
  4809. (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
  4810. #else /* TCL_MAJOR_VERSION < 8 */
  4811. DUMP1("Tcl_CreateCommand(\"tkwait\")");
  4812. Tcl_CreateCommand(interp, "tkwait", ip_rbTkWaitCommand,
  4813. (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
  4814. #endif
  4815. /* add 'thread_vwait' command */
  4816. #if TCL_MAJOR_VERSION >= 8
  4817. DUMP1("Tcl_CreateObjCommand(\"thread_vwait\")");
  4818. Tcl_CreateObjCommand(interp, "thread_vwait", ip_rb_threadVwaitObjCmd,
  4819. (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
  4820. #else /* TCL_MAJOR_VERSION < 8 */
  4821. DUMP1("Tcl_CreateCommand(\"thread_vwait\")");
  4822. Tcl_CreateCommand(interp, "thread_vwait", ip_rb_threadVwaitCommand,
  4823. (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
  4824. #endif
  4825. /* add 'thread_tkwait' command */
  4826. #if TCL_MAJOR_VERSION >= 8
  4827. DUMP1("Tcl_CreateObjCommand(\"thread_tkwait\")");
  4828. Tcl_CreateObjCommand(interp, "thread_tkwait", ip_rb_threadTkWaitObjCmd,
  4829. (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
  4830. #else /* TCL_MAJOR_VERSION < 8 */
  4831. DUMP1("Tcl_CreateCommand(\"thread_tkwait\")");
  4832. Tcl_CreateCommand(interp, "thread_tkwait", ip_rb_threadTkWaitCommand,
  4833. (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
  4834. #endif
  4835. /* replace 'update' command */
  4836. #if TCL_MAJOR_VERSION >= 8
  4837. DUMP1("Tcl_CreateObjCommand(\"update\")");
  4838. Tcl_CreateObjCommand(interp, "update", ip_rbUpdateObjCmd,
  4839. (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
  4840. #else /* TCL_MAJOR_VERSION < 8 */
  4841. DUMP1("Tcl_CreateCommand(\"update\")");
  4842. Tcl_CreateCommand(interp, "update", ip_rbUpdateCommand,
  4843. (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
  4844. #endif
  4845. /* add 'thread_update' command */
  4846. #if TCL_MAJOR_VERSION >= 8
  4847. DUMP1("Tcl_CreateObjCommand(\"thread_update\")");
  4848. Tcl_CreateObjCommand(interp, "thread_update", ip_rb_threadUpdateObjCmd,
  4849. (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
  4850. #else /* TCL_MAJOR_VERSION < 8 */
  4851. DUMP1("Tcl_CreateCommand(\"thread_update\")");
  4852. Tcl_CreateCommand(interp, "thread_update", ip_rb_threadUpdateCommand,
  4853. (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
  4854. #endif
  4855. }
  4856. #if TCL_MAJOR_VERSION >= 8
  4857. static int
  4858. ip_rb_replaceSlaveTkCmdsObjCmd(clientData, interp, objc, objv)
  4859. ClientData clientData;
  4860. Tcl_Interp *interp;
  4861. int objc;
  4862. Tcl_Obj *CONST objv[];
  4863. #else /* TCL_MAJOR_VERSION < 8 */
  4864. static int
  4865. ip_rb_replaceSlaveTkCmdsCommand(clientData, interp, objc, objv)
  4866. ClientData clientData;
  4867. Tcl_Interp *interp;
  4868. int objc;
  4869. char *objv[];
  4870. #endif
  4871. {
  4872. char *slave_name;
  4873. Tcl_Interp *slave;
  4874. Tk_Window mainWin;
  4875. if (objc != 2) {
  4876. #ifdef Tcl_WrongNumArgs
  4877. Tcl_WrongNumArgs(interp, 1, objv, "slave_name");
  4878. #else
  4879. char *nameString;
  4880. #if TCL_MAJOR_VERSION >= 8
  4881. nameString = Tcl_GetStringFromObj(objv[0], (int*)NULL);
  4882. #else /* TCL_MAJOR_VERSION < 8 */
  4883. nameString = objv[0];
  4884. #endif
  4885. Tcl_AppendResult(interp, "wrong number of arguments: should be \"",
  4886. nameString, " slave_name\"", (char *) NULL);
  4887. #endif
  4888. }
  4889. #if TCL_MAJOR_VERSION >= 8
  4890. slave_name = Tcl_GetStringFromObj(objv[1], (int*)NULL);
  4891. #else
  4892. slave_name = objv[1];
  4893. #endif
  4894. slave = Tcl_GetSlave(interp, slave_name);
  4895. if (slave == NULL) {
  4896. Tcl_AppendResult(interp, "cannot find slave \"",
  4897. slave_name, "\"", (char *)NULL);
  4898. return TCL_ERROR;
  4899. }
  4900. mainWin = Tk_MainWindow(slave);
  4901. /* replace 'exit' command --> 'interp_exit' command */
  4902. #if TCL_MAJOR_VERSION >= 8
  4903. DUMP1("Tcl_CreateObjCommand(\"exit\") --> \"interp_exit\"");
  4904. Tcl_CreateObjCommand(slave, "exit", ip_InterpExitObjCmd,
  4905. (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
  4906. #else /* TCL_MAJOR_VERSION < 8 */
  4907. DUMP1("Tcl_CreateCommand(\"exit\") --> \"interp_exit\"");
  4908. Tcl_CreateCommand(slave, "exit", ip_InterpExitCommand,
  4909. (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
  4910. #endif
  4911. /* replace vwait and tkwait */
  4912. ip_replace_wait_commands(slave, mainWin);
  4913. return TCL_OK;
  4914. }
  4915. #if TCL_MAJOR_VERSION >= 8
  4916. static int ip_rbNamespaceObjCmd _((ClientData, Tcl_Interp *, int,
  4917. Tcl_Obj *CONST []));
  4918. static int
  4919. ip_rbNamespaceObjCmd(clientData, interp, objc, objv)
  4920. ClientData clientData;
  4921. Tcl_Interp *interp;
  4922. int objc;
  4923. Tcl_Obj *CONST objv[];
  4924. {
  4925. Tcl_CmdInfo info;
  4926. int ret;
  4927. if (!Tcl_GetCommandInfo(interp, "__orig_namespace_command__", &(info))) {
  4928. Tcl_ResetResult(interp);
  4929. Tcl_AppendResult(interp,
  4930. "invalid command name \"namespace\"", (char*)NULL);
  4931. return TCL_ERROR;
  4932. }
  4933. rbtk_eventloop_depth++;
  4934. /* DUMP2("namespace wrapper enter depth == %d", rbtk_eventloop_depth); */
  4935. if (info.isNativeObjectProc) {
  4936. ret = (*(info.objProc))(info.objClientData, interp, objc, objv);
  4937. } else {
  4938. /* string interface */
  4939. int i;
  4940. char **argv;
  4941. /* argv = (char **)Tcl_Alloc(sizeof(char *) * (objc + 1)); */
  4942. argv = (char **)ckalloc(sizeof(char *) * (objc + 1));
  4943. #if 0 /* use Tcl_Preserve/Release */
  4944. Tcl_Preserve((ClientData)argv); /* XXXXXXXX */
  4945. #endif
  4946. for(i = 0; i < objc; i++) {
  4947. /* argv[i] = Tcl_GetString(objv[i]); */
  4948. argv[i] = Tcl_GetStringFromObj(objv[i], (int*)NULL);
  4949. }
  4950. argv[objc] = (char *)NULL;
  4951. ret = (*(info.proc))(info.clientData, interp,
  4952. objc, (CONST84 char **)argv);
  4953. #if 0 /* use Tcl_EventuallyFree */
  4954. Tcl_EventuallyFree((ClientData)argv, TCL_DYNAMIC); /* XXXXXXXX */
  4955. #else
  4956. #if 0 /* use Tcl_Preserve/Release */
  4957. Tcl_Release((ClientData)argv); /* XXXXXXXX */
  4958. #else
  4959. /* Tcl_Free((char*)argv); */
  4960. ckfree((char*)argv);
  4961. #endif
  4962. #endif
  4963. }
  4964. /* DUMP2("namespace wrapper exit depth == %d", rbtk_eventloop_depth); */
  4965. rbtk_eventloop_depth--;
  4966. return ret;
  4967. }
  4968. #endif
  4969. static void
  4970. ip_wrap_namespace_command(interp)
  4971. Tcl_Interp *interp;
  4972. {
  4973. #if TCL_MAJOR_VERSION >= 8
  4974. Tcl_CmdInfo orig_info;
  4975. if (!Tcl_GetCommandInfo(interp, "namespace", &(orig_info))) {
  4976. return;
  4977. }
  4978. if (orig_info.isNativeObjectProc) {
  4979. Tcl_CreateObjCommand(interp, "__orig_namespace_command__",
  4980. orig_info.objProc, orig_info.objClientData,
  4981. orig_info.deleteProc);
  4982. } else {
  4983. Tcl_CreateCommand(interp, "__orig_namespace_command__",
  4984. orig_info.proc, orig_info.clientData,
  4985. orig_info.deleteProc);
  4986. }
  4987. Tcl_CreateObjCommand(interp, "namespace", ip_rbNamespaceObjCmd,
  4988. (ClientData) 0, (Tcl_CmdDeleteProc *)NULL);
  4989. #endif
  4990. }
  4991. /* call when interpreter is deleted */
  4992. static void
  4993. #ifdef HAVE_PROTOTYPES
  4994. ip_CallWhenDeleted(ClientData clientData, Tcl_Interp *ip)
  4995. #else
  4996. ip_CallWhenDeleted(clientData, ip)
  4997. ClientData clientData;
  4998. Tcl_Interp *ip;
  4999. #endif
  5000. {
  5001. int thr_crit_bup;
  5002. /* Tk_Window main_win = (Tk_Window) clientData; */
  5003. DUMP1("start ip_CallWhenDeleted");
  5004. thr_crit_bup = rb_thread_critical;
  5005. rb_thread_critical = Qtrue;
  5006. ip_finalize(ip);
  5007. DUMP1("finish ip_CallWhenDeleted");
  5008. rb_thread_critical = thr_crit_bup;
  5009. }
  5010. /*--------------------------------------------------------*/
  5011. #ifdef __WIN32__
  5012. /* #include <tkWinInt.h> *//* conflict definition of struct timezone */
  5013. /* #include <tkIntPlatDecls.h> */
  5014. /* #include <windows.h> */
  5015. EXTERN void TkWinSetHINSTANCE(HINSTANCE hInstance);
  5016. void rbtk_win32_SetHINSTANCE(const char *module_name)
  5017. {
  5018. /* TCHAR szBuf[256]; */
  5019. HINSTANCE hInst;
  5020. /* hInst = GetModuleHandle(NULL); */
  5021. /* hInst = GetModuleHandle("tcltklib.so"); */
  5022. hInst = GetModuleHandle(module_name);
  5023. TkWinSetHINSTANCE(hInst);
  5024. /* GetModuleFileName(hInst, szBuf, sizeof(szBuf) / sizeof(TCHAR)); */
  5025. /* MessageBox(NULL, szBuf, TEXT("OK"), MB_OK); */
  5026. }
  5027. #endif
  5028. /*--------------------------------------------------------*/
  5029. /* initialize interpreter */
  5030. static VALUE
  5031. ip_init(argc, argv, self)
  5032. int argc;
  5033. VALUE *argv;
  5034. VALUE self;
  5035. {
  5036. struct tcltkip *ptr; /* tcltkip data struct */
  5037. VALUE argv0, opts;
  5038. int cnt;
  5039. int st;
  5040. int with_tk = 1;
  5041. Tk_Window mainWin = (Tk_Window)NULL;
  5042. /* security check */
  5043. if (rb_safe_level() >= 4) {
  5044. rb_raise(rb_eSecurityError,
  5045. "Cannot create a TclTkIp object at level %d",
  5046. rb_safe_level());
  5047. }
  5048. /* create object */
  5049. Data_Get_Struct(self, struct tcltkip, ptr);
  5050. ptr = ALLOC(struct tcltkip);
  5051. /* ptr = (struct tcltkip *)ckalloc(sizeof(struct tcltkip)); */
  5052. DATA_PTR(self) = ptr;
  5053. #ifdef RUBY_USE_NATIVE_THREAD
  5054. ptr->tk_thread_id = 0;
  5055. #endif
  5056. ptr->ref_count = 0;
  5057. ptr->allow_ruby_exit = 1;
  5058. ptr->return_value = 0;
  5059. /* from Tk_Main() */
  5060. DUMP1("Tcl_CreateInterp");
  5061. ptr->ip = ruby_tcl_create_ip_and_stubs_init(&st);
  5062. if (ptr->ip == NULL) {
  5063. switch(st) {
  5064. case TCLTK_STUBS_OK:
  5065. break;
  5066. case NO_TCL_DLL:
  5067. rb_raise(rb_eLoadError, "tcltklib: fail to open tcl_dll");
  5068. case NO_FindExecutable:
  5069. rb_raise(rb_eLoadError, "tcltklib: can't find Tcl_FindExecutable");
  5070. case NO_CreateInterp:
  5071. rb_raise(rb_eLoadError, "tcltklib: can't find Tcl_CreateInterp()");
  5072. case NO_DeleteInterp:
  5073. rb_raise(rb_eLoadError, "tcltklib: can't find Tcl_DeleteInterp()");
  5074. case FAIL_CreateInterp:
  5075. rb_raise(rb_eRuntimeError, "tcltklib: fail to create a new IP");
  5076. case FAIL_Tcl_InitStubs:
  5077. rb_raise(rb_eRuntimeError, "tcltklib: fail to Tcl_InitStubs()");
  5078. default:
  5079. rb_raise(rb_eRuntimeError, "tcltklib: unknown error(%d) on ruby_tcl_create_ip_and_stubs_init", st);
  5080. }
  5081. }
  5082. #if TCL_MAJOR_VERSION >= 8
  5083. #if TCL_NAMESPACE_DEBUG
  5084. DUMP1("get current namespace");
  5085. if ((ptr->default_ns = Tcl_GetCurrentNamespace(ptr->ip))
  5086. == (Tcl_Namespace*)NULL) {
  5087. rb_raise(rb_eRuntimeError, "a new Tk interpreter has a NULL namespace");
  5088. }
  5089. #endif
  5090. #endif
  5091. rbtk_preserve_ip(ptr);
  5092. DUMP2("IP ref_count = %d", ptr->ref_count);
  5093. current_interp = ptr->ip;
  5094. call_tclkit_init_script(current_interp);
  5095. ptr->has_orig_exit
  5096. = Tcl_GetCommandInfo(ptr->ip, "exit", &(ptr->orig_exit_info));
  5097. /* from Tcl_AppInit() */
  5098. DUMP1("Tcl_Init");
  5099. if (Tcl_Init(ptr->ip) == TCL_ERROR) {
  5100. rb_raise(rb_eRuntimeError, "%s", Tcl_GetStringResult(ptr->ip));
  5101. }
  5102. /* set variables */
  5103. cnt = rb_scan_args(argc, argv, "02", &argv0, &opts);
  5104. switch(cnt) {
  5105. case 2:
  5106. /* options */
  5107. if (NIL_P(opts) || opts == Qfalse) {
  5108. /* without Tk */
  5109. with_tk = 0;
  5110. } else {
  5111. /* Tcl_SetVar(ptr->ip, "argv", StringValuePtr(opts), 0); */
  5112. Tcl_SetVar(ptr->ip, "argv", StringValuePtr(opts), TCL_GLOBAL_ONLY);
  5113. }
  5114. case 1:
  5115. /* argv0 */
  5116. if (!NIL_P(argv0)) {
  5117. if (strncmp(StringValuePtr(argv0), "-e", 3) == 0
  5118. || strncmp(StringValuePtr(argv0), "-", 2) == 0) {
  5119. Tcl_SetVar(ptr->ip, "argv0", "ruby", TCL_GLOBAL_ONLY);
  5120. } else {
  5121. /* Tcl_SetVar(ptr->ip, "argv0", StringValuePtr(argv0), 0); */
  5122. Tcl_SetVar(ptr->ip, "argv0", StringValuePtr(argv0),
  5123. TCL_GLOBAL_ONLY);
  5124. }
  5125. }
  5126. case 0:
  5127. /* no args */
  5128. ;
  5129. }
  5130. st = ruby_tcl_stubs_init();
  5131. /* from Tcl_AppInit() */
  5132. if (with_tk) {
  5133. DUMP1("Tk_Init");
  5134. st = ruby_tk_stubs_init(ptr->ip);
  5135. switch(st) {
  5136. case TCLTK_STUBS_OK:
  5137. break;
  5138. case NO_Tk_Init:
  5139. rb_raise(rb_eLoadError, "tcltklib: can't find Tk_Init()");
  5140. case FAIL_Tk_Init:
  5141. rb_raise(rb_eRuntimeError, "tcltklib: fail to Tk_Init(). %s",
  5142. Tcl_GetStringResult(ptr->ip));
  5143. case FAIL_Tk_InitStubs:
  5144. rb_raise(rb_eRuntimeError, "tcltklib: fail to Tk_InitStubs(). %s",
  5145. Tcl_GetStringResult(ptr->ip));
  5146. default:
  5147. rb_raise(rb_eRuntimeError, "tcltklib: unknown error(%d) on ruby_tk_stubs_init", st);
  5148. }
  5149. DUMP1("Tcl_StaticPackage(\"Tk\")");
  5150. #if TCL_MAJOR_VERSION >= 8
  5151. Tcl_StaticPackage(ptr->ip, "Tk", Tk_Init, Tk_SafeInit);
  5152. #else /* TCL_MAJOR_VERSION < 8 */
  5153. Tcl_StaticPackage(ptr->ip, "Tk", Tk_Init,
  5154. (Tcl_PackageInitProc *) NULL);
  5155. #endif
  5156. #ifdef RUBY_USE_NATIVE_THREAD
  5157. /* set Tk thread ID */
  5158. ptr->tk_thread_id = Tcl_GetCurrentThread();
  5159. #endif
  5160. /* get main window */
  5161. mainWin = Tk_MainWindow(ptr->ip);
  5162. Tk_Preserve((ClientData)mainWin);
  5163. }
  5164. /* add ruby command to the interpreter */
  5165. #if TCL_MAJOR_VERSION >= 8
  5166. DUMP1("Tcl_CreateObjCommand(\"ruby\")");
  5167. Tcl_CreateObjCommand(ptr->ip, "ruby", ip_ruby_eval, (ClientData)NULL,
  5168. (Tcl_CmdDeleteProc *)NULL);
  5169. DUMP1("Tcl_CreateObjCommand(\"ruby_eval\")");
  5170. Tcl_CreateObjCommand(ptr->ip, "ruby_eval", ip_ruby_eval, (ClientData)NULL,
  5171. (Tcl_CmdDeleteProc *)NULL);
  5172. DUMP1("Tcl_CreateObjCommand(\"ruby_cmd\")");
  5173. Tcl_CreateObjCommand(ptr->ip, "ruby_cmd", ip_ruby_cmd, (ClientData)NULL,
  5174. (Tcl_CmdDeleteProc *)NULL);
  5175. #else /* TCL_MAJOR_VERSION < 8 */
  5176. DUMP1("Tcl_CreateCommand(\"ruby\")");
  5177. Tcl_CreateCommand(ptr->ip, "ruby", ip_ruby_eval, (ClientData)NULL,
  5178. (Tcl_CmdDeleteProc *)NULL);
  5179. DUMP1("Tcl_CreateCommand(\"ruby_eval\")");
  5180. Tcl_CreateCommand(ptr->ip, "ruby_eval", ip_ruby_eval, (ClientData)NULL,
  5181. (Tcl_CmdDeleteProc *)NULL);
  5182. DUMP1("Tcl_CreateCommand(\"ruby_cmd\")");
  5183. Tcl_CreateCommand(ptr->ip, "ruby_cmd", ip_ruby_cmd, (ClientData)NULL,
  5184. (Tcl_CmdDeleteProc *)NULL);
  5185. #endif
  5186. /* add 'interp_exit', 'ruby_exit' and replace 'exit' command */
  5187. #if TCL_MAJOR_VERSION >= 8
  5188. DUMP1("Tcl_CreateObjCommand(\"interp_exit\")");
  5189. Tcl_CreateObjCommand(ptr->ip, "interp_exit", ip_InterpExitObjCmd,
  5190. (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
  5191. DUMP1("Tcl_CreateObjCommand(\"ruby_exit\")");
  5192. Tcl_CreateObjCommand(ptr->ip, "ruby_exit", ip_RubyExitObjCmd,
  5193. (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
  5194. DUMP1("Tcl_CreateObjCommand(\"exit\") --> \"ruby_exit\"");
  5195. Tcl_CreateObjCommand(ptr->ip, "exit", ip_RubyExitObjCmd,
  5196. (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
  5197. #else /* TCL_MAJOR_VERSION < 8 */
  5198. DUMP1("Tcl_CreateCommand(\"interp_exit\")");
  5199. Tcl_CreateCommand(ptr->ip, "interp_exit", ip_InterpExitCommand,
  5200. (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
  5201. DUMP1("Tcl_CreateCommand(\"ruby_exit\")");
  5202. Tcl_CreateCommand(ptr->ip, "ruby_exit", ip_RubyExitCommand,
  5203. (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
  5204. DUMP1("Tcl_CreateCommand(\"exit\") --> \"ruby_exit\"");
  5205. Tcl_CreateCommand(ptr->ip, "exit", ip_RubyExitCommand,
  5206. (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
  5207. #endif
  5208. /* replace vwait and tkwait */
  5209. ip_replace_wait_commands(ptr->ip, mainWin);
  5210. /* wrap namespace command */
  5211. ip_wrap_namespace_command(ptr->ip);
  5212. /* define command to replace commands which depend on slave's MainWindow */
  5213. #if TCL_MAJOR_VERSION >= 8
  5214. Tcl_CreateObjCommand(ptr->ip, "__replace_slave_tk_commands__",
  5215. ip_rb_replaceSlaveTkCmdsObjCmd,
  5216. (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
  5217. #else /* TCL_MAJOR_VERSION < 8 */
  5218. Tcl_CreateCommand(ptr->ip, "__replace_slave_tk_commands__",
  5219. ip_rb_replaceSlaveTkCmdsCommand,
  5220. (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
  5221. #endif
  5222. /* set finalizer */
  5223. Tcl_CallWhenDeleted(ptr->ip, ip_CallWhenDeleted, (ClientData)mainWin);
  5224. if (mainWin != (Tk_Window)NULL) {
  5225. Tk_Release((ClientData)mainWin);
  5226. }
  5227. return self;
  5228. }
  5229. static VALUE
  5230. ip_create_slave_core(interp, argc, argv)
  5231. VALUE interp;
  5232. int argc;
  5233. VALUE *argv;
  5234. {
  5235. struct tcltkip *master = get_ip(interp);
  5236. struct tcltkip *slave = ALLOC(struct tcltkip);
  5237. /* struct tcltkip *slave = (struct tcltkip *)ckalloc(sizeof(struct tcltkip)); */
  5238. VALUE safemode;
  5239. VALUE name;
  5240. int safe;
  5241. int thr_crit_bup;
  5242. Tk_Window mainWin;
  5243. /* ip is deleted? */
  5244. if (deleted_ip(master)) {
  5245. return rb_exc_new2(rb_eRuntimeError,
  5246. "deleted master cannot create a new slave");
  5247. }
  5248. name = argv[0];
  5249. safemode = argv[1];
  5250. if (Tcl_IsSafe(master->ip) == 1) {
  5251. safe = 1;
  5252. } else if (safemode == Qfalse || NIL_P(safemode)) {
  5253. safe = 0;
  5254. /* rb_secure(4); */ /* already checked */
  5255. } else {
  5256. safe = 1;
  5257. }
  5258. thr_crit_bup = rb_thread_critical;
  5259. rb_thread_critical = Qtrue;
  5260. #if 0
  5261. /* init Tk */
  5262. if (RTEST(with_tk)) {
  5263. volatile VALUE exc;
  5264. if (!tk_stubs_init_p()) {
  5265. exc = tcltkip_init_tk(interp);
  5266. if (!NIL_P(exc)) {
  5267. rb_thread_critical = thr_crit_bup;
  5268. return exc;
  5269. }
  5270. }
  5271. }
  5272. #endif
  5273. /* create slave-ip */
  5274. #ifdef RUBY_USE_NATIVE_THREAD
  5275. /* slave->tk_thread_id = 0; */
  5276. slave->tk_thread_id = master->tk_thread_id; /* == current thread */
  5277. #endif
  5278. slave->ref_count = 0;
  5279. slave->allow_ruby_exit = 0;
  5280. slave->return_value = 0;
  5281. slave->ip = Tcl_CreateSlave(master->ip, StringValuePtr(name), safe);
  5282. if (slave->ip == NULL) {
  5283. rb_thread_critical = thr_crit_bup;
  5284. return rb_exc_new2(rb_eRuntimeError,
  5285. "fail to create the new slave interpreter");
  5286. }
  5287. #if TCL_MAJOR_VERSION >= 8
  5288. #if TCL_NAMESPACE_DEBUG
  5289. slave->default_ns = Tcl_GetCurrentNamespace(slave->ip);
  5290. #endif
  5291. #endif
  5292. rbtk_preserve_ip(slave);
  5293. slave->has_orig_exit
  5294. = Tcl_GetCommandInfo(slave->ip, "exit", &(slave->orig_exit_info));
  5295. /* replace 'exit' command --> 'interp_exit' command */
  5296. mainWin = (tk_stubs_init_p())? Tk_MainWindow(slave->ip): (Tk_Window)NULL;
  5297. #if TCL_MAJOR_VERSION >= 8
  5298. DUMP1("Tcl_CreateObjCommand(\"exit\") --> \"interp_exit\"");
  5299. Tcl_CreateObjCommand(slave->ip, "exit", ip_InterpExitObjCmd,
  5300. (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
  5301. #else /* TCL_MAJOR_VERSION < 8 */
  5302. DUMP1("Tcl_CreateCommand(\"exit\") --> \"interp_exit\"");
  5303. Tcl_CreateCommand(slave->ip, "exit", ip_InterpExitCommand,
  5304. (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
  5305. #endif
  5306. /* replace vwait and tkwait */
  5307. ip_replace_wait_commands(slave->ip, mainWin);
  5308. /* wrap namespace command */
  5309. ip_wrap_namespace_command(slave->ip);
  5310. /* define command to replace cmds which depend on slave-slave's MainWin */
  5311. #if TCL_MAJOR_VERSION >= 8
  5312. Tcl_CreateObjCommand(slave->ip, "__replace_slave_tk_commands__",
  5313. ip_rb_replaceSlaveTkCmdsObjCmd,
  5314. (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
  5315. #else /* TCL_MAJOR_VERSION < 8 */
  5316. Tcl_CreateCommand(slave->ip, "__replace_slave_tk_commands__",
  5317. ip_rb_replaceSlaveTkCmdsCommand,
  5318. (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
  5319. #endif
  5320. /* set finalizer */
  5321. Tcl_CallWhenDeleted(slave->ip, ip_CallWhenDeleted, (ClientData)mainWin);
  5322. rb_thread_critical = thr_crit_bup;
  5323. return Data_Wrap_Struct(CLASS_OF(interp), 0, ip_free, slave);
  5324. }
  5325. static VALUE
  5326. ip_create_slave(argc, argv, self)
  5327. int argc;
  5328. VALUE *argv;
  5329. VALUE self;
  5330. {
  5331. struct tcltkip *master = get_ip(self);
  5332. VALUE safemode;
  5333. VALUE name;
  5334. VALUE callargv[2];
  5335. /* ip is deleted? */
  5336. if (deleted_ip(master)) {
  5337. rb_raise(rb_eRuntimeError,
  5338. "deleted master cannot create a new slave interpreter");
  5339. }
  5340. /* argument check */
  5341. if (rb_scan_args(argc, argv, "11", &name, &safemode) == 1) {
  5342. safemode = Qfalse;
  5343. }
  5344. if (Tcl_IsSafe(master->ip) != 1
  5345. && (safemode == Qfalse || NIL_P(safemode))) {
  5346. rb_secure(4);
  5347. }
  5348. StringValue(name);
  5349. callargv[0] = name;
  5350. callargv[1] = safemode;
  5351. return tk_funcall(ip_create_slave_core, 2, callargv, self);
  5352. }
  5353. /* self is slave of master? */
  5354. static VALUE
  5355. ip_is_slave_of_p(self, master)
  5356. VALUE self, master;
  5357. {
  5358. if (!rb_obj_is_kind_of(master, tcltkip_class)) {
  5359. rb_raise(rb_eArgError, "expected TclTkIp object");
  5360. }
  5361. if (Tcl_GetMaster(get_ip(self)->ip) == get_ip(master)->ip) {
  5362. return Qtrue;
  5363. } else {
  5364. return Qfalse;
  5365. }
  5366. }
  5367. /* create console (if supported) */
  5368. #if defined(MAC_TCL) || defined(__WIN32__)
  5369. #if TCL_MAJOR_VERSION < 8 \
  5370. || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION == 0) \
  5371. || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION == 1 \
  5372. && (TCL_RELEASE_LEVEL == TCL_ALPHA_RELEASE \
  5373. || (TCL_RELEASE_LEVEL == TCL_BETA_RELEASE \
  5374. && TCL_RELEASE_SERIAL < 2) ) )
  5375. EXTERN void TkConsoleCreate _((void));
  5376. #endif
  5377. #if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION == 1 \
  5378. && ( (TCL_RELEASE_LEVEL == TCL_FINAL_RELEASE \
  5379. && TCL_RELEASE_SERIAL == 0) \
  5380. || (TCL_RELEASE_LEVEL == TCL_BETA_RELEASE \
  5381. && TCL_RELEASE_SERIAL >= 2) )
  5382. EXTERN void TkConsoleCreate_ _((void));
  5383. #endif
  5384. #endif
  5385. static VALUE
  5386. ip_create_console_core(interp, argc, argv)
  5387. VALUE interp;
  5388. int argc; /* dummy */
  5389. VALUE *argv; /* dummy */
  5390. {
  5391. struct tcltkip *ptr = get_ip(interp);
  5392. if (!tk_stubs_init_p()) {
  5393. tcltkip_init_tk(interp);
  5394. }
  5395. if (Tcl_GetVar(ptr->ip,"tcl_interactive",TCL_GLOBAL_ONLY) == (char*)NULL) {
  5396. Tcl_SetVar(ptr->ip, "tcl_interactive", "0", TCL_GLOBAL_ONLY);
  5397. }
  5398. #if TCL_MAJOR_VERSION > 8 \
  5399. || (TCL_MAJOR_VERSION == 8 \
  5400. && (TCL_MINOR_VERSION > 1 \
  5401. || (TCL_MINOR_VERSION == 1 \
  5402. && TCL_RELEASE_LEVEL == TCL_FINAL_RELEASE \
  5403. && TCL_RELEASE_SERIAL >= 1) ) )
  5404. Tk_InitConsoleChannels(ptr->ip);
  5405. if (Tk_CreateConsoleWindow(ptr->ip) != TCL_OK) {
  5406. rb_raise(rb_eRuntimeError, "fail to create console-window");
  5407. }
  5408. #else
  5409. #if defined(MAC_TCL) || defined(__WIN32__)
  5410. #if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION == 1 \
  5411. && ( (TCL_RELEASE_LEVEL == TCL_FINAL_RELEASE && TCL_RELEASE_SERIAL == 0) \
  5412. || (TCL_RELEASE_LEVEL == TCL_BETA_RELEASE && TCL_RELEASE_SERIAL >= 2) )
  5413. TkConsoleCreate_();
  5414. #else
  5415. TkConsoleCreate();
  5416. #endif
  5417. if (TkConsoleInit(ptr->ip) != TCL_OK) {
  5418. rb_raise(rb_eRuntimeError, "fail to create console-window");
  5419. }
  5420. #else
  5421. rb_notimplement();
  5422. #endif
  5423. #endif
  5424. return interp;
  5425. }
  5426. static VALUE
  5427. ip_create_console(self)
  5428. VALUE self;
  5429. {
  5430. struct tcltkip *ptr = get_ip(self);
  5431. /* ip is deleted? */
  5432. if (deleted_ip(ptr)) {
  5433. rb_raise(rb_eRuntimeError, "interpreter is deleted");
  5434. }
  5435. return tk_funcall(ip_create_console_core, 0, (VALUE*)NULL, self);
  5436. }
  5437. /* make ip "safe" */
  5438. static VALUE
  5439. ip_make_safe_core(interp, argc, argv)
  5440. VALUE interp;
  5441. int argc; /* dummy */
  5442. VALUE *argv; /* dummy */
  5443. {
  5444. struct tcltkip *ptr = get_ip(interp);
  5445. Tk_Window mainWin;
  5446. /* ip is deleted? */
  5447. if (deleted_ip(ptr)) {
  5448. return rb_exc_new2(rb_eRuntimeError, "interpreter is deleted");
  5449. }
  5450. if (Tcl_MakeSafe(ptr->ip) == TCL_ERROR) {
  5451. /* return rb_exc_new2(rb_eRuntimeError,
  5452. Tcl_GetStringResult(ptr->ip)); */
  5453. return create_ip_exc(interp, rb_eRuntimeError,
  5454. Tcl_GetStringResult(ptr->ip));
  5455. }
  5456. ptr->allow_ruby_exit = 0;
  5457. /* replace 'exit' command --> 'interp_exit' command */
  5458. mainWin = (tk_stubs_init_p())? Tk_MainWindow(ptr->ip): (Tk_Window)NULL;
  5459. #if TCL_MAJOR_VERSION >= 8
  5460. DUMP1("Tcl_CreateObjCommand(\"exit\") --> \"interp_exit\"");
  5461. Tcl_CreateObjCommand(ptr->ip, "exit", ip_InterpExitObjCmd,
  5462. (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
  5463. #else /* TCL_MAJOR_VERSION < 8 */
  5464. DUMP1("Tcl_CreateCommand(\"exit\") --> \"interp_exit\"");
  5465. Tcl_CreateCommand(ptr->ip, "exit", ip_InterpExitCommand,
  5466. (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
  5467. #endif
  5468. return interp;
  5469. }
  5470. static VALUE
  5471. ip_make_safe(self)
  5472. VALUE self;
  5473. {
  5474. struct tcltkip *ptr = get_ip(self);
  5475. /* ip is deleted? */
  5476. if (deleted_ip(ptr)) {
  5477. rb_raise(rb_eRuntimeError, "interpreter is deleted");
  5478. }
  5479. return tk_funcall(ip_make_safe_core, 0, (VALUE*)NULL, self);
  5480. }
  5481. /* is safe? */
  5482. static VALUE
  5483. ip_is_safe_p(self)
  5484. VALUE self;
  5485. {
  5486. struct tcltkip *ptr = get_ip(self);
  5487. /* ip is deleted? */
  5488. if (deleted_ip(ptr)) {
  5489. rb_raise(rb_eRuntimeError, "interpreter is deleted");
  5490. }
  5491. if (Tcl_IsSafe(ptr->ip)) {
  5492. return Qtrue;
  5493. } else {
  5494. return Qfalse;
  5495. }
  5496. }
  5497. /* allow_ruby_exit? */
  5498. static VALUE
  5499. ip_allow_ruby_exit_p(self)
  5500. VALUE self;
  5501. {
  5502. struct tcltkip *ptr = get_ip(self);
  5503. /* ip is deleted? */
  5504. if (deleted_ip(ptr)) {
  5505. rb_raise(rb_eRuntimeError, "interpreter is deleted");
  5506. }
  5507. if (ptr->allow_ruby_exit) {
  5508. return Qtrue;
  5509. } else {
  5510. return Qfalse;
  5511. }
  5512. }
  5513. /* allow_ruby_exit = mode */
  5514. static VALUE
  5515. ip_allow_ruby_exit_set(self, val)
  5516. VALUE self, val;
  5517. {
  5518. struct tcltkip *ptr = get_ip(self);
  5519. Tk_Window mainWin;
  5520. rb_secure(4);
  5521. /* ip is deleted? */
  5522. if (deleted_ip(ptr)) {
  5523. rb_raise(rb_eRuntimeError, "interpreter is deleted");
  5524. }
  5525. if (Tcl_IsSafe(ptr->ip)) {
  5526. rb_raise(rb_eSecurityError,
  5527. "insecure operation on a safe interpreter");
  5528. }
  5529. /*
  5530. * Because of cross-threading, the following line may fail to find
  5531. * the MainWindow, even if the Tcl/Tk interpreter has one or more.
  5532. * But it has no problem. Current implementation of both type of
  5533. * the "exit" command don't need maiinWin token.
  5534. */
  5535. mainWin = (tk_stubs_init_p())? Tk_MainWindow(ptr->ip): (Tk_Window)NULL;
  5536. if (RTEST(val)) {
  5537. ptr->allow_ruby_exit = 1;
  5538. #if TCL_MAJOR_VERSION >= 8
  5539. DUMP1("Tcl_CreateObjCommand(\"exit\") --> \"ruby_exit\"");
  5540. Tcl_CreateObjCommand(ptr->ip, "exit", ip_RubyExitObjCmd,
  5541. (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
  5542. #else /* TCL_MAJOR_VERSION < 8 */
  5543. DUMP1("Tcl_CreateCommand(\"exit\") --> \"ruby_exit\"");
  5544. Tcl_CreateCommand(ptr->ip, "exit", ip_RubyExitCommand,
  5545. (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
  5546. #endif
  5547. return Qtrue;
  5548. } else {
  5549. ptr->allow_ruby_exit = 0;
  5550. #if TCL_MAJOR_VERSION >= 8
  5551. DUMP1("Tcl_CreateObjCommand(\"exit\") --> \"interp_exit\"");
  5552. Tcl_CreateObjCommand(ptr->ip, "exit", ip_InterpExitObjCmd,
  5553. (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
  5554. #else /* TCL_MAJOR_VERSION < 8 */
  5555. DUMP1("Tcl_CreateCommand(\"exit\") --> \"interp_exit\"");
  5556. Tcl_CreateCommand(ptr->ip, "exit", ip_InterpExitCommand,
  5557. (ClientData)mainWin, (Tcl_CmdDeleteProc *)NULL);
  5558. #endif
  5559. return Qfalse;
  5560. }
  5561. }
  5562. /* delete interpreter */
  5563. static VALUE
  5564. ip_delete(self)
  5565. VALUE self;
  5566. {
  5567. int thr_crit_bup;
  5568. struct tcltkip *ptr = get_ip(self);
  5569. /* if (ptr == (struct tcltkip *)NULL || ptr->ip == (Tcl_Interp*)NULL) { */
  5570. if (deleted_ip(ptr)) {
  5571. DUMP1("delete deleted IP");
  5572. return Qnil;
  5573. }
  5574. thr_crit_bup = rb_thread_critical;
  5575. rb_thread_critical = Qtrue;
  5576. DUMP1("delete interp");
  5577. if (!Tcl_InterpDeleted(ptr->ip)) {
  5578. DUMP1("call ip_finalize");
  5579. ip_finalize(ptr->ip);
  5580. Tcl_DeleteInterp(ptr->ip);
  5581. Tcl_Release(ptr->ip);
  5582. }
  5583. rb_thread_critical = thr_crit_bup;
  5584. return Qnil;
  5585. }
  5586. /* is deleted? */
  5587. static VALUE
  5588. ip_has_invalid_namespace_p(self)
  5589. VALUE self;
  5590. {
  5591. struct tcltkip *ptr = get_ip(self);
  5592. if (ptr == (struct tcltkip *)NULL || ptr->ip == (Tcl_Interp *)NULL) {
  5593. /* deleted IP */
  5594. return Qtrue;
  5595. }
  5596. #if TCL_NAMESPACE_DEBUG
  5597. if (rbtk_invalid_namespace(ptr)) {
  5598. return Qtrue;
  5599. } else {
  5600. return Qfalse;
  5601. }
  5602. #else
  5603. return Qfalse;
  5604. #endif
  5605. }
  5606. static VALUE
  5607. ip_is_deleted_p(self)
  5608. VALUE self;
  5609. {
  5610. struct tcltkip *ptr = get_ip(self);
  5611. if (deleted_ip(ptr)) {
  5612. return Qtrue;
  5613. } else {
  5614. return Qfalse;
  5615. }
  5616. }
  5617. static VALUE
  5618. ip_has_mainwindow_p_core(self, argc, argv)
  5619. VALUE self;
  5620. int argc; /* dummy */
  5621. VALUE *argv; /* dummy */
  5622. {
  5623. struct tcltkip *ptr = get_ip(self);
  5624. if (deleted_ip(ptr) || !tk_stubs_init_p()) {
  5625. return Qnil;
  5626. } else if (Tk_MainWindow(ptr->ip) == (Tk_Window)NULL) {
  5627. return Qfalse;
  5628. } else {
  5629. return Qtrue;
  5630. }
  5631. }
  5632. static VALUE
  5633. ip_has_mainwindow_p(self)
  5634. VALUE self;
  5635. {
  5636. return tk_funcall(ip_has_mainwindow_p_core, 0, (VALUE*)NULL, self);
  5637. }
  5638. /*** ruby string <=> tcl object ***/
  5639. #if TCL_MAJOR_VERSION >= 8
  5640. static VALUE
  5641. get_str_from_obj(obj)
  5642. Tcl_Obj *obj;
  5643. {
  5644. int len, binary = 0;
  5645. const char *s;
  5646. volatile VALUE str;
  5647. #if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION == 0
  5648. s = Tcl_GetStringFromObj(obj, &len);
  5649. #else
  5650. #if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION <= 3
  5651. /* TCL_VERSION 8.1 -- 8.3 */
  5652. if (Tcl_GetCharLength(obj) != Tcl_UniCharLen(Tcl_GetUnicode(obj))) {
  5653. /* possibly binary string */
  5654. s = (char *)Tcl_GetByteArrayFromObj(obj, &len);
  5655. binary = 1;
  5656. } else {
  5657. /* possibly text string */
  5658. s = Tcl_GetStringFromObj(obj, &len);
  5659. }
  5660. #else /* TCL_VERSION >= 8.4 */
  5661. if (IS_TCL_BYTEARRAY(obj)) {
  5662. s = (char *)Tcl_GetByteArrayFromObj(obj, &len);
  5663. binary = 1;
  5664. } else {
  5665. s = Tcl_GetStringFromObj(obj, &len);
  5666. }
  5667. #endif
  5668. #endif
  5669. str = s ? rb_str_new(s, len) : rb_str_new2("");
  5670. if (binary) {
  5671. #ifdef HAVE_RUBY_ENCODING_H
  5672. rb_enc_associate_index(str, ENCODING_INDEX_BINARY);
  5673. #endif
  5674. rb_ivar_set(str, ID_at_enc, ENCODING_NAME_BINARY);
  5675. #if TCL_MAJOR_VERSION > 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION >= 1)
  5676. } else {
  5677. #ifdef HAVE_RUBY_ENCODING_H
  5678. rb_enc_associate_index(str, ENCODING_INDEX_UTF8);
  5679. #endif
  5680. rb_ivar_set(str, ID_at_enc, ENCODING_NAME_UTF8);
  5681. #endif
  5682. }
  5683. return str;
  5684. }
  5685. static Tcl_Obj *
  5686. get_obj_from_str(str)
  5687. VALUE str;
  5688. {
  5689. const char *s = StringValuePtr(str);
  5690. #if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION == 0
  5691. return Tcl_NewStringObj((char*)s, RSTRING_LEN(str));
  5692. #else /* TCL_VERSION >= 8.1 */
  5693. VALUE enc = rb_attr_get(str, ID_at_enc);
  5694. if (!NIL_P(enc)) {
  5695. StringValue(enc);
  5696. if (strcmp(RSTRING_PTR(enc), "binary") == 0) {
  5697. /* binary string */
  5698. return Tcl_NewByteArrayObj((const unsigned char *)s, RSTRING_LEN(str));
  5699. } else {
  5700. /* text string */
  5701. return Tcl_NewStringObj(s, RSTRING_LEN(str));
  5702. }
  5703. #ifdef HAVE_RUBY_ENCODING_H
  5704. } else if (rb_enc_get_index(str) == ENCODING_INDEX_BINARY) {
  5705. /* binary string */
  5706. return Tcl_NewByteArrayObj((const unsigned char *)s, RSTRING_LEN(str));
  5707. #endif
  5708. } else if (memchr(s, 0, RSTRING_LEN(str))) {
  5709. /* probably binary string */
  5710. return Tcl_NewByteArrayObj((const unsigned char *)s, RSTRING_LEN(str));
  5711. } else {
  5712. /* probably text string */
  5713. return Tcl_NewStringObj(s, RSTRING_LEN(str));
  5714. }
  5715. #endif
  5716. }
  5717. #endif /* ruby string <=> tcl object */
  5718. static VALUE
  5719. ip_get_result_string_obj(interp)
  5720. Tcl_Interp *interp;
  5721. {
  5722. #if TCL_MAJOR_VERSION >= 8
  5723. Tcl_Obj *retObj;
  5724. volatile VALUE strval;
  5725. retObj = Tcl_GetObjResult(interp);
  5726. Tcl_IncrRefCount(retObj);
  5727. strval = get_str_from_obj(retObj);
  5728. RbTk_OBJ_UNTRUST(strval);
  5729. Tcl_ResetResult(interp);
  5730. Tcl_DecrRefCount(retObj);
  5731. return strval;
  5732. #else
  5733. return rb_tainted_str_new2(interp->result);
  5734. #endif
  5735. }
  5736. /* call Tcl/Tk functions on the eventloop thread */
  5737. static VALUE
  5738. callq_safelevel_handler(arg, callq)
  5739. VALUE arg;
  5740. VALUE callq;
  5741. {
  5742. struct call_queue *q;
  5743. Data_Get_Struct(callq, struct call_queue, q);
  5744. DUMP2("(safe-level handler) $SAFE = %d", q->safe_level);
  5745. rb_set_safe_level(q->safe_level);
  5746. return((q->func)(q->interp, q->argc, q->argv));
  5747. }
  5748. static int call_queue_handler _((Tcl_Event *, int));
  5749. static int
  5750. call_queue_handler(evPtr, flags)
  5751. Tcl_Event *evPtr;
  5752. int flags;
  5753. {
  5754. struct call_queue *q = (struct call_queue *)evPtr;
  5755. volatile VALUE ret;
  5756. volatile VALUE q_dat;
  5757. volatile VALUE thread = q->thread;
  5758. struct tcltkip *ptr;
  5759. DUMP2("do_call_queue_handler : evPtr = %p", evPtr);
  5760. DUMP2("call_queue_handler thread : %lx", rb_thread_current());
  5761. DUMP2("added by thread : %lx", thread);
  5762. if (*(q->done)) {
  5763. DUMP1("processed by another event-loop");
  5764. return 0;
  5765. } else {
  5766. DUMP1("process it on current event-loop");
  5767. }
  5768. #ifdef RUBY_VM
  5769. if (RTEST(rb_funcall(thread, ID_alive_p, 0))
  5770. && ! RTEST(rb_funcall(thread, ID_stop_p, 0))) {
  5771. #else
  5772. if (RTEST(rb_thread_alive_p(thread))
  5773. && ! RTEST(rb_funcall(thread, ID_stop_p, 0))) {
  5774. #endif
  5775. DUMP1("caller is not yet ready to receive the result -> pending");
  5776. return 0;
  5777. }
  5778. /* process it */
  5779. *(q->done) = 1;
  5780. /* deleted ipterp ? */
  5781. ptr = get_ip(q->interp);
  5782. if (deleted_ip(ptr)) {
  5783. /* deleted IP --> ignore */
  5784. return 1;
  5785. }
  5786. /* incr internal handler mark */
  5787. rbtk_internal_eventloop_handler++;
  5788. /* check safe-level */
  5789. if (rb_safe_level() != q->safe_level) {
  5790. /* q_dat = Data_Wrap_Struct(rb_cData,0,-1,q); */
  5791. q_dat = Data_Wrap_Struct(rb_cData,call_queue_mark,-1,q);
  5792. ret = rb_funcall(rb_proc_new(callq_safelevel_handler, q_dat),
  5793. ID_call, 0);
  5794. rb_gc_force_recycle(q_dat);
  5795. q_dat = (VALUE)NULL;
  5796. } else {
  5797. DUMP2("call function (for caller thread:%lx)", thread);
  5798. DUMP2("call function (current thread:%lx)", rb_thread_current());
  5799. ret = (q->func)(q->interp, q->argc, q->argv);
  5800. }
  5801. /* set result */
  5802. RARRAY_PTR(q->result)[0] = ret;
  5803. ret = (VALUE)NULL;
  5804. /* decr internal handler mark */
  5805. rbtk_internal_eventloop_handler--;
  5806. /* complete */
  5807. *(q->done) = -1;
  5808. /* unlink ruby objects */
  5809. q->argv = (VALUE*)NULL;
  5810. q->interp = (VALUE)NULL;
  5811. q->result = (VALUE)NULL;
  5812. q->thread = (VALUE)NULL;
  5813. /* back to caller */
  5814. #ifdef RUBY_VM
  5815. if (RTEST(rb_funcall(thread, ID_alive_p, 0, 0))) {
  5816. #else
  5817. if (RTEST(rb_thread_alive_p(thread))) {
  5818. #endif
  5819. DUMP2("back to caller (caller thread:%lx)", thread);
  5820. DUMP2(" (current thread:%lx)", rb_thread_current());
  5821. #if CONTROL_BY_STATUS_OF_RB_THREAD_WAITING_FOR_VALUE
  5822. have_rb_thread_waiting_for_value = 1;
  5823. rb_thread_wakeup(thread);
  5824. #else
  5825. rb_thread_run(thread);
  5826. #endif
  5827. DUMP1("finish back to caller");
  5828. #if DO_THREAD_SCHEDULE_AT_CALLBACK_DONE
  5829. rb_thread_schedule();
  5830. #endif
  5831. } else {
  5832. DUMP2("caller is dead (caller thread:%lx)", thread);
  5833. DUMP2(" (current thread:%lx)", rb_thread_current());
  5834. }
  5835. /* end of handler : remove it */
  5836. return 1;
  5837. }
  5838. static VALUE
  5839. tk_funcall(func, argc, argv, obj)
  5840. VALUE (*func)();
  5841. int argc;
  5842. VALUE *argv;
  5843. VALUE obj;
  5844. {
  5845. struct call_queue *callq;
  5846. struct tcltkip *ptr;
  5847. int *alloc_done;
  5848. int thr_crit_bup;
  5849. int is_tk_evloop_thread;
  5850. volatile VALUE current = rb_thread_current();
  5851. volatile VALUE ip_obj = obj;
  5852. volatile VALUE result;
  5853. volatile VALUE ret;
  5854. struct timeval t;
  5855. if (!NIL_P(ip_obj) && rb_obj_is_kind_of(ip_obj, tcltkip_class)) {
  5856. ptr = get_ip(ip_obj);
  5857. if (deleted_ip(ptr)) return Qnil;
  5858. } else {
  5859. ptr = (struct tcltkip *)NULL;
  5860. }
  5861. #ifdef RUBY_USE_NATIVE_THREAD
  5862. if (ptr) {
  5863. /* on Tcl interpreter */
  5864. is_tk_evloop_thread = (ptr->tk_thread_id == (Tcl_ThreadId) 0
  5865. || ptr->tk_thread_id == Tcl_GetCurrentThread());
  5866. } else {
  5867. /* on Tcl/Tk library */
  5868. is_tk_evloop_thread = (tk_eventloop_thread_id == (Tcl_ThreadId) 0
  5869. || tk_eventloop_thread_id == Tcl_GetCurrentThread());
  5870. }
  5871. #else
  5872. is_tk_evloop_thread = 1;
  5873. #endif
  5874. if (is_tk_evloop_thread
  5875. && (NIL_P(eventloop_thread) || current == eventloop_thread)
  5876. ) {
  5877. if (NIL_P(eventloop_thread)) {
  5878. DUMP2("tk_funcall from thread:%lx but no eventloop", current);
  5879. } else {
  5880. DUMP2("tk_funcall from current eventloop %lx", current);
  5881. }
  5882. result = (func)(ip_obj, argc, argv);
  5883. if (rb_obj_is_kind_of(result, rb_eException)) {
  5884. rb_exc_raise(result);
  5885. }
  5886. return result;
  5887. }
  5888. DUMP2("tk_funcall from thread %lx (NOT current eventloop)", current);
  5889. thr_crit_bup = rb_thread_critical;
  5890. rb_thread_critical = Qtrue;
  5891. /* allocate memory (argv cross over thread : must be in heap) */
  5892. if (argv) {
  5893. /* VALUE *temp = ALLOC_N(VALUE, argc); */
  5894. VALUE *temp = (VALUE*)ckalloc(sizeof(VALUE) * argc);
  5895. #if 0 /* use Tcl_Preserve/Release */
  5896. Tcl_Preserve((ClientData)temp); /* XXXXXXXX */
  5897. #endif
  5898. MEMCPY(temp, argv, VALUE, argc);
  5899. argv = temp;
  5900. }
  5901. /* allocate memory (keep result) */
  5902. /* alloc_done = (int*)ALLOC(int); */
  5903. alloc_done = (int*)ckalloc(sizeof(int));
  5904. #if 0 /* use Tcl_Preserve/Release */
  5905. Tcl_Preserve((ClientData)alloc_done); /* XXXXXXXX */
  5906. #endif
  5907. *alloc_done = 0;
  5908. /* allocate memory (freed by Tcl_ServiceEvent) */
  5909. /* callq = (struct call_queue *)Tcl_Alloc(sizeof(struct call_queue)); */
  5910. callq = (struct call_queue *)ckalloc(sizeof(struct call_queue));
  5911. #if 0 /* use Tcl_Preserve/Release */
  5912. Tcl_Preserve(callq);
  5913. #endif
  5914. /* allocate result obj */
  5915. result = rb_ary_new3(1, Qnil);
  5916. /* construct event data */
  5917. callq->done = alloc_done;
  5918. callq->func = func;
  5919. callq->argc = argc;
  5920. callq->argv = argv;
  5921. callq->interp = ip_obj;
  5922. callq->result = result;
  5923. callq->thread = current;
  5924. callq->safe_level = rb_safe_level();
  5925. callq->ev.proc = call_queue_handler;
  5926. /* add the handler to Tcl event queue */
  5927. DUMP1("add handler");
  5928. #ifdef RUBY_USE_NATIVE_THREAD
  5929. if (ptr && ptr->tk_thread_id) {
  5930. /* Tcl_ThreadQueueEvent(ptr->tk_thread_id,
  5931. &(callq->ev), TCL_QUEUE_HEAD); */
  5932. Tcl_ThreadQueueEvent(ptr->tk_thread_id,
  5933. (Tcl_Event*)callq, TCL_QUEUE_HEAD);
  5934. Tcl_ThreadAlert(ptr->tk_thread_id);
  5935. } else if (tk_eventloop_thread_id) {
  5936. /* Tcl_ThreadQueueEvent(tk_eventloop_thread_id,
  5937. &(callq->ev), TCL_QUEUE_HEAD); */
  5938. Tcl_ThreadQueueEvent(tk_eventloop_thread_id,
  5939. (Tcl_Event*)callq, TCL_QUEUE_HEAD);
  5940. Tcl_ThreadAlert(tk_eventloop_thread_id);
  5941. } else {
  5942. /* Tcl_QueueEvent(&(callq->ev), TCL_QUEUE_HEAD); */
  5943. Tcl_QueueEvent((Tcl_Event*)callq, TCL_QUEUE_HEAD);
  5944. }
  5945. #else
  5946. /* Tcl_QueueEvent(&(callq->ev), TCL_QUEUE_HEAD); */
  5947. Tcl_QueueEvent((Tcl_Event*)callq, TCL_QUEUE_HEAD);
  5948. #endif
  5949. rb_thread_critical = thr_crit_bup;
  5950. /* wait for the handler to be processed */
  5951. t.tv_sec = 0;
  5952. t.tv_usec = (long)((EVENT_HANDLER_TIMEOUT)*1000.0);
  5953. DUMP2("callq wait for handler (current thread:%lx)", current);
  5954. while(*alloc_done >= 0) {
  5955. DUMP2("*** callq wait for handler (current thread:%lx)", current);
  5956. /* rb_thread_stop(); */
  5957. /* rb_thread_sleep_forever(); */
  5958. rb_thread_wait_for(t);
  5959. DUMP2("*** callq wakeup (current thread:%lx)", current);
  5960. DUMP2("*** (eventloop thread:%lx)", eventloop_thread);
  5961. if (NIL_P(eventloop_thread)) {
  5962. DUMP1("*** callq lost eventloop thread");
  5963. break;
  5964. }
  5965. }
  5966. DUMP2("back from handler (current thread:%lx)", current);
  5967. /* get result & free allocated memory */
  5968. ret = RARRAY_PTR(result)[0];
  5969. #if 0 /* use Tcl_EventuallyFree */
  5970. Tcl_EventuallyFree((ClientData)alloc_done, TCL_DYNAMIC); /* XXXXXXXX */
  5971. #else
  5972. #if 0 /* use Tcl_Preserve/Release */
  5973. Tcl_Release((ClientData)alloc_done); /* XXXXXXXX */
  5974. #else
  5975. /* free(alloc_done); */
  5976. ckfree((char*)alloc_done);
  5977. #endif
  5978. #endif
  5979. /* if (argv) free(argv); */
  5980. if (argv) {
  5981. /* if argv != NULL, alloc as 'temp' */
  5982. int i;
  5983. for(i = 0; i < argc; i++) { argv[i] = (VALUE)NULL; }
  5984. #if 0 /* use Tcl_EventuallyFree */
  5985. Tcl_EventuallyFree((ClientData)argv, TCL_DYNAMIC); /* XXXXXXXX */
  5986. #else
  5987. #if 0 /* use Tcl_Preserve/Release */
  5988. Tcl_Release((ClientData)argv); /* XXXXXXXX */
  5989. #else
  5990. ckfree((char*)argv);
  5991. #endif
  5992. #endif
  5993. }
  5994. #if 0 /* callq is freed by Tcl_ServiceEvent */
  5995. #if 0 /* use Tcl_Preserve/Release */
  5996. Tcl_Release(callq);
  5997. #else
  5998. ckfree((char*)callq);
  5999. #endif
  6000. #endif
  6001. /* exception? */
  6002. if (rb_obj_is_kind_of(ret, rb_eException)) {
  6003. DUMP1("raise exception");
  6004. /* rb_exc_raise(ret); */
  6005. rb_exc_raise(rb_exc_new3(rb_obj_class(ret),
  6006. rb_funcall(ret, ID_to_s, 0, 0)));
  6007. }
  6008. DUMP1("exit tk_funcall");
  6009. return ret;
  6010. }
  6011. /* eval string in tcl by Tcl_Eval() */
  6012. #if TCL_MAJOR_VERSION >= 8
  6013. struct call_eval_info {
  6014. struct tcltkip *ptr;
  6015. Tcl_Obj *cmd;
  6016. };
  6017. static VALUE
  6018. #ifdef HAVE_PROTOTYPES
  6019. call_tcl_eval(VALUE arg)
  6020. #else
  6021. call_tcl_eval(arg)
  6022. VALUE arg;
  6023. #endif
  6024. {
  6025. struct call_eval_info *inf = (struct call_eval_info *)arg;
  6026. Tcl_AllowExceptions(inf->ptr->ip);
  6027. inf->ptr->return_value = Tcl_EvalObj(inf->ptr->ip, inf->cmd);
  6028. return Qnil;
  6029. }
  6030. #endif
  6031. static VALUE
  6032. ip_eval_real(self, cmd_str, cmd_len)
  6033. VALUE self;
  6034. char *cmd_str;
  6035. int cmd_len;
  6036. {
  6037. volatile VALUE ret;
  6038. struct tcltkip *ptr = get_ip(self);
  6039. int thr_crit_bup;
  6040. #if TCL_MAJOR_VERSION >= 8
  6041. /* call Tcl_EvalObj() */
  6042. {
  6043. Tcl_Obj *cmd;
  6044. thr_crit_bup = rb_thread_critical;
  6045. rb_thread_critical = Qtrue;
  6046. cmd = Tcl_NewStringObj(cmd_str, cmd_len);
  6047. Tcl_IncrRefCount(cmd);
  6048. /* ip is deleted? */
  6049. if (deleted_ip(ptr)) {
  6050. Tcl_DecrRefCount(cmd);
  6051. rb_thread_critical = thr_crit_bup;
  6052. ptr->return_value = TCL_OK;
  6053. return rb_tainted_str_new2("");
  6054. } else {
  6055. int status;
  6056. struct call_eval_info inf;
  6057. /* Tcl_Preserve(ptr->ip); */
  6058. rbtk_preserve_ip(ptr);
  6059. #if 0
  6060. ptr->return_value = Tcl_EvalObj(ptr->ip, cmd);
  6061. /* ptr->return_value = Tcl_GlobalEvalObj(ptr->ip, cmd); */
  6062. #else
  6063. inf.ptr = ptr;
  6064. inf.cmd = cmd;
  6065. ret = rb_protect(call_tcl_eval, (VALUE)&inf, &status);
  6066. switch(status) {
  6067. case TAG_RAISE:
  6068. if (NIL_P(rb_errinfo())) {
  6069. rbtk_pending_exception = rb_exc_new2(rb_eException,
  6070. "unknown exception");
  6071. } else {
  6072. rbtk_pending_exception = rb_errinfo();
  6073. }
  6074. break;
  6075. case TAG_FATAL:
  6076. if (NIL_P(rb_errinfo())) {
  6077. rbtk_pending_exception = rb_exc_new2(rb_eFatal, "FATAL");
  6078. } else {
  6079. rbtk_pending_exception = rb_errinfo();
  6080. }
  6081. }
  6082. #endif
  6083. }
  6084. Tcl_DecrRefCount(cmd);
  6085. }
  6086. if (pending_exception_check1(thr_crit_bup, ptr)) {
  6087. rbtk_release_ip(ptr);
  6088. return rbtk_pending_exception;
  6089. }
  6090. /* if (ptr->return_value == TCL_ERROR) { */
  6091. if (ptr->return_value != TCL_OK) {
  6092. if (event_loop_abort_on_exc > 0 && !Tcl_InterpDeleted(ptr->ip)) {
  6093. volatile VALUE exc;
  6094. switch (ptr->return_value) {
  6095. case TCL_RETURN:
  6096. exc = create_ip_exc(self, eTkCallbackReturn,
  6097. "ip_eval_real receives TCL_RETURN");
  6098. case TCL_BREAK:
  6099. exc = create_ip_exc(self, eTkCallbackBreak,
  6100. "ip_eval_real receives TCL_BREAK");
  6101. case TCL_CONTINUE:
  6102. exc = create_ip_exc(self, eTkCallbackContinue,
  6103. "ip_eval_real receives TCL_CONTINUE");
  6104. default:
  6105. exc = create_ip_exc(self, rb_eRuntimeError, "%s",
  6106. Tcl_GetStringResult(ptr->ip));
  6107. }
  6108. rbtk_release_ip(ptr);
  6109. rb_thread_critical = thr_crit_bup;
  6110. return exc;
  6111. } else {
  6112. if (event_loop_abort_on_exc < 0) {
  6113. rb_warning("%s (ignore)", Tcl_GetStringResult(ptr->ip));
  6114. } else {
  6115. rb_warn("%s (ignore)", Tcl_GetStringResult(ptr->ip));
  6116. }
  6117. Tcl_ResetResult(ptr->ip);
  6118. rbtk_release_ip(ptr);
  6119. rb_thread_critical = thr_crit_bup;
  6120. return rb_tainted_str_new2("");
  6121. }
  6122. }
  6123. /* pass back the result (as string) */
  6124. ret = ip_get_result_string_obj(ptr->ip);
  6125. rbtk_release_ip(ptr);
  6126. rb_thread_critical = thr_crit_bup;
  6127. return ret;
  6128. #else /* TCL_MAJOR_VERSION < 8 */
  6129. DUMP2("Tcl_Eval(%s)", cmd_str);
  6130. /* ip is deleted? */
  6131. if (deleted_ip(ptr)) {
  6132. ptr->return_value = TCL_OK;
  6133. return rb_tainted_str_new2("");
  6134. } else {
  6135. /* Tcl_Preserve(ptr->ip); */
  6136. rbtk_preserve_ip(ptr);
  6137. ptr->return_value = Tcl_Eval(ptr->ip, cmd_str);
  6138. /* ptr->return_value = Tcl_GlobalEval(ptr->ip, cmd_str); */
  6139. }
  6140. if (pending_exception_check1(thr_crit_bup, ptr)) {
  6141. rbtk_release_ip(ptr);
  6142. return rbtk_pending_exception;
  6143. }
  6144. /* if (ptr->return_value == TCL_ERROR) { */
  6145. if (ptr->return_value != TCL_OK) {
  6146. volatile VALUE exc;
  6147. switch (ptr->return_value) {
  6148. case TCL_RETURN:
  6149. exc = create_ip_exc(self, eTkCallbackReturn,
  6150. "ip_eval_real receives TCL_RETURN");
  6151. case TCL_BREAK:
  6152. exc = create_ip_exc(self, eTkCallbackBreak,
  6153. "ip_eval_real receives TCL_BREAK");
  6154. case TCL_CONTINUE:
  6155. exc = create_ip_exc(self, eTkCallbackContinue,
  6156. "ip_eval_real receives TCL_CONTINUE");
  6157. default:
  6158. exc = create_ip_exc(self, rb_eRuntimeError, "%s", ptr->ip->result);
  6159. }
  6160. rbtk_release_ip(ptr);
  6161. return exc;
  6162. }
  6163. DUMP2("(TCL_Eval result) %d", ptr->return_value);
  6164. /* pass back the result (as string) */
  6165. ret = ip_get_result_string_obj(ptr->ip);
  6166. rbtk_release_ip(ptr);
  6167. return ret;
  6168. #endif
  6169. }
  6170. static VALUE
  6171. evq_safelevel_handler(arg, evq)
  6172. VALUE arg;
  6173. VALUE evq;
  6174. {
  6175. struct eval_queue *q;
  6176. Data_Get_Struct(evq, struct eval_queue, q);
  6177. DUMP2("(safe-level handler) $SAFE = %d", q->safe_level);
  6178. rb_set_safe_level(q->safe_level);
  6179. return ip_eval_real(q->interp, q->str, q->len);
  6180. }
  6181. int eval_queue_handler _((Tcl_Event *, int));
  6182. int
  6183. eval_queue_handler(evPtr, flags)
  6184. Tcl_Event *evPtr;
  6185. int flags;
  6186. {
  6187. struct eval_queue *q = (struct eval_queue *)evPtr;
  6188. volatile VALUE ret;
  6189. volatile VALUE q_dat;
  6190. volatile VALUE thread = q->thread;
  6191. struct tcltkip *ptr;
  6192. DUMP2("do_eval_queue_handler : evPtr = %p", evPtr);
  6193. DUMP2("eval_queue_thread : %lx", rb_thread_current());
  6194. DUMP2("added by thread : %lx", thread);
  6195. if (*(q->done)) {
  6196. DUMP1("processed by another event-loop");
  6197. return 0;
  6198. } else {
  6199. DUMP1("process it on current event-loop");
  6200. }
  6201. #ifdef RUBY_VM
  6202. if (RTEST(rb_funcall(thread, ID_alive_p, 0))
  6203. && ! RTEST(rb_funcall(thread, ID_stop_p, 0))) {
  6204. #else
  6205. if (RTEST(rb_thread_alive_p(thread))
  6206. && ! RTEST(rb_funcall(thread, ID_stop_p, 0))) {
  6207. #endif
  6208. DUMP1("caller is not yet ready to receive the result -> pending");
  6209. return 0;
  6210. }
  6211. /* process it */
  6212. *(q->done) = 1;
  6213. /* deleted ipterp ? */
  6214. ptr = get_ip(q->interp);
  6215. if (deleted_ip(ptr)) {
  6216. /* deleted IP --> ignore */
  6217. return 1;
  6218. }
  6219. /* incr internal handler mark */
  6220. rbtk_internal_eventloop_handler++;
  6221. /* check safe-level */
  6222. if (rb_safe_level() != q->safe_level) {
  6223. #ifdef HAVE_NATIVETHREAD
  6224. #ifndef RUBY_USE_NATIVE_THREAD
  6225. if (!ruby_native_thread_p()) {
  6226. rb_bug("cross-thread violation on eval_queue_handler()");
  6227. }
  6228. #endif
  6229. #endif
  6230. /* q_dat = Data_Wrap_Struct(rb_cData,0,-1,q); */
  6231. q_dat = Data_Wrap_Struct(rb_cData,eval_queue_mark,-1,q);
  6232. ret = rb_funcall(rb_proc_new(evq_safelevel_handler, q_dat),
  6233. ID_call, 0);
  6234. rb_gc_force_recycle(q_dat);
  6235. q_dat = (VALUE)NULL;
  6236. } else {
  6237. ret = ip_eval_real(q->interp, q->str, q->len);
  6238. }
  6239. /* set result */
  6240. RARRAY_PTR(q->result)[0] = ret;
  6241. ret = (VALUE)NULL;
  6242. /* decr internal handler mark */
  6243. rbtk_internal_eventloop_handler--;
  6244. /* complete */
  6245. *(q->done) = -1;
  6246. /* unlink ruby objects */
  6247. q->interp = (VALUE)NULL;
  6248. q->result = (VALUE)NULL;
  6249. q->thread = (VALUE)NULL;
  6250. /* back to caller */
  6251. #ifdef RUBY_VM
  6252. if (RTEST(rb_funcall(thread, ID_alive_p, 0, 0))) {
  6253. #else
  6254. if (RTEST(rb_thread_alive_p(thread))) {
  6255. #endif
  6256. DUMP2("back to caller (caller thread:%lx)", thread);
  6257. DUMP2(" (current thread:%lx)", rb_thread_current());
  6258. #if CONTROL_BY_STATUS_OF_RB_THREAD_WAITING_FOR_VALUE
  6259. have_rb_thread_waiting_for_value = 1;
  6260. rb_thread_wakeup(thread);
  6261. #else
  6262. rb_thread_run(thread);
  6263. #endif
  6264. DUMP1("finish back to caller");
  6265. #if DO_THREAD_SCHEDULE_AT_CALLBACK_DONE
  6266. rb_thread_schedule();
  6267. #endif
  6268. } else {
  6269. DUMP2("caller is dead (caller thread:%lx)", thread);
  6270. DUMP2(" (current thread:%lx)", rb_thread_current());
  6271. }
  6272. /* end of handler : remove it */
  6273. return 1;
  6274. }
  6275. static VALUE
  6276. ip_eval(self, str)
  6277. VALUE self;
  6278. VALUE str;
  6279. {
  6280. struct eval_queue *evq;
  6281. #ifdef RUBY_USE_NATIVE_THREAD
  6282. struct tcltkip *ptr;
  6283. #endif
  6284. char *eval_str;
  6285. int *alloc_done;
  6286. int thr_crit_bup;
  6287. volatile VALUE current = rb_thread_current();
  6288. volatile VALUE ip_obj = self;
  6289. volatile VALUE result;
  6290. volatile VALUE ret;
  6291. Tcl_QueuePosition position;
  6292. struct timeval t;
  6293. thr_crit_bup = rb_thread_critical;
  6294. rb_thread_critical = Qtrue;
  6295. StringValue(str);
  6296. rb_thread_critical = thr_crit_bup;
  6297. #ifdef RUBY_USE_NATIVE_THREAD
  6298. ptr = get_ip(ip_obj);
  6299. DUMP2("eval status: ptr->tk_thread_id %p", ptr->tk_thread_id);
  6300. DUMP2("eval status: Tcl_GetCurrentThread %p", Tcl_GetCurrentThread());
  6301. #else
  6302. DUMP2("status: Tcl_GetCurrentThread %p", Tcl_GetCurrentThread());
  6303. #endif
  6304. DUMP2("status: eventloopt_thread %lx", eventloop_thread);
  6305. if (
  6306. #ifdef RUBY_USE_NATIVE_THREAD
  6307. (ptr->tk_thread_id == 0 || ptr->tk_thread_id == Tcl_GetCurrentThread())
  6308. &&
  6309. #endif
  6310. (NIL_P(eventloop_thread) || current == eventloop_thread)
  6311. ) {
  6312. if (NIL_P(eventloop_thread)) {
  6313. DUMP2("eval from thread:%lx but no eventloop", current);
  6314. } else {
  6315. DUMP2("eval from current eventloop %lx", current);
  6316. }
  6317. result = ip_eval_real(self, RSTRING_PTR(str), RSTRING_LEN(str));
  6318. if (rb_obj_is_kind_of(result, rb_eException)) {
  6319. rb_exc_raise(result);
  6320. }
  6321. return result;
  6322. }
  6323. DUMP2("eval from thread %lx (NOT current eventloop)", current);
  6324. thr_crit_bup = rb_thread_critical;
  6325. rb_thread_critical = Qtrue;
  6326. /* allocate memory (keep result) */
  6327. /* alloc_done = (int*)ALLOC(int); */
  6328. alloc_done = (int*)ckalloc(sizeof(int));
  6329. #if 0 /* use Tcl_Preserve/Release */
  6330. Tcl_Preserve((ClientData)alloc_done); /* XXXXXXXX */
  6331. #endif
  6332. *alloc_done = 0;
  6333. /* eval_str = ALLOC_N(char, RSTRING_LEN(str) + 1); */
  6334. eval_str = ckalloc(sizeof(char) * (RSTRING_LEN(str) + 1));
  6335. #if 0 /* use Tcl_Preserve/Release */
  6336. Tcl_Preserve((ClientData)eval_str); /* XXXXXXXX */
  6337. #endif
  6338. memcpy(eval_str, RSTRING_PTR(str), RSTRING_LEN(str));
  6339. eval_str[RSTRING_LEN(str)] = 0;
  6340. /* allocate memory (freed by Tcl_ServiceEvent) */
  6341. /* evq = (struct eval_queue *)Tcl_Alloc(sizeof(struct eval_queue)); */
  6342. evq = (struct eval_queue *)ckalloc(sizeof(struct eval_queue));
  6343. #if 0 /* use Tcl_Preserve/Release */
  6344. Tcl_Preserve(evq);
  6345. #endif
  6346. /* allocate result obj */
  6347. result = rb_ary_new3(1, Qnil);
  6348. /* construct event data */
  6349. evq->done = alloc_done;
  6350. evq->str = eval_str;
  6351. evq->len = RSTRING_LEN(str);
  6352. evq->interp = ip_obj;
  6353. evq->result = result;
  6354. evq->thread = current;
  6355. evq->safe_level = rb_safe_level();
  6356. evq->ev.proc = eval_queue_handler;
  6357. position = TCL_QUEUE_TAIL;
  6358. /* add the handler to Tcl event queue */
  6359. DUMP1("add handler");
  6360. #ifdef RUBY_USE_NATIVE_THREAD
  6361. if (ptr->tk_thread_id) {
  6362. /* Tcl_ThreadQueueEvent(ptr->tk_thread_id, &(evq->ev), position); */
  6363. Tcl_ThreadQueueEvent(ptr->tk_thread_id, (Tcl_Event*)evq, position);
  6364. Tcl_ThreadAlert(ptr->tk_thread_id);
  6365. } else if (tk_eventloop_thread_id) {
  6366. Tcl_ThreadQueueEvent(tk_eventloop_thread_id, (Tcl_Event*)evq, position);
  6367. /* Tcl_ThreadQueueEvent(tk_eventloop_thread_id,
  6368. &(evq->ev), position); */
  6369. Tcl_ThreadAlert(tk_eventloop_thread_id);
  6370. } else {
  6371. /* Tcl_QueueEvent(&(evq->ev), position); */
  6372. Tcl_QueueEvent((Tcl_Event*)evq, position);
  6373. }
  6374. #else
  6375. /* Tcl_QueueEvent(&(evq->ev), position); */
  6376. Tcl_QueueEvent((Tcl_Event*)evq, position);
  6377. #endif
  6378. rb_thread_critical = thr_crit_bup;
  6379. /* wait for the handler to be processed */
  6380. t.tv_sec = 0;
  6381. t.tv_usec = (long)((EVENT_HANDLER_TIMEOUT)*1000.0);
  6382. DUMP2("evq wait for handler (current thread:%lx)", current);
  6383. while(*alloc_done >= 0) {
  6384. DUMP2("*** evq wait for handler (current thread:%lx)", current);
  6385. /* rb_thread_stop(); */
  6386. /* rb_thread_sleep_forever(); */
  6387. rb_thread_wait_for(t);
  6388. DUMP2("*** evq wakeup (current thread:%lx)", current);
  6389. DUMP2("*** (eventloop thread:%lx)", eventloop_thread);
  6390. if (NIL_P(eventloop_thread)) {
  6391. DUMP1("*** evq lost eventloop thread");
  6392. break;
  6393. }
  6394. }
  6395. DUMP2("back from handler (current thread:%lx)", current);
  6396. /* get result & free allocated memory */
  6397. ret = RARRAY_PTR(result)[0];
  6398. #if 0 /* use Tcl_EventuallyFree */
  6399. Tcl_EventuallyFree((ClientData)alloc_done, TCL_DYNAMIC); /* XXXXXXXX */
  6400. #else
  6401. #if 0 /* use Tcl_Preserve/Release */
  6402. Tcl_Release((ClientData)alloc_done); /* XXXXXXXX */
  6403. #else
  6404. /* free(alloc_done); */
  6405. ckfree((char*)alloc_done);
  6406. #endif
  6407. #endif
  6408. #if 0 /* use Tcl_EventuallyFree */
  6409. Tcl_EventuallyFree((ClientData)eval_str, TCL_DYNAMIC); /* XXXXXXXX */
  6410. #else
  6411. #if 0 /* use Tcl_Preserve/Release */
  6412. Tcl_Release((ClientData)eval_str); /* XXXXXXXX */
  6413. #else
  6414. /* free(eval_str); */
  6415. ckfree(eval_str);
  6416. #endif
  6417. #endif
  6418. #if 0 /* evq is freed by Tcl_ServiceEvent */
  6419. #if 0 /* use Tcl_Preserve/Release */
  6420. Tcl_Release(evq);
  6421. #else
  6422. ckfree((char*)evq);
  6423. #endif
  6424. #endif
  6425. if (rb_obj_is_kind_of(ret, rb_eException)) {
  6426. DUMP1("raise exception");
  6427. /* rb_exc_raise(ret); */
  6428. rb_exc_raise(rb_exc_new3(rb_obj_class(ret),
  6429. rb_funcall(ret, ID_to_s, 0, 0)));
  6430. }
  6431. return ret;
  6432. }
  6433. static int
  6434. ip_cancel_eval_core(interp, msg, flag)
  6435. Tcl_Interp *interp;
  6436. VALUE msg;
  6437. int flag;
  6438. {
  6439. #if TCL_MAJOR_VERSION < 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION < 6)
  6440. rb_raise(rb_eNotImpError,
  6441. "cancel_eval is supported Tcl/Tk8.6 or later.");
  6442. #else
  6443. Tcl_Obj *msg_obj;
  6444. if (NIL_P(msg)) {
  6445. msg_obj = NULL;
  6446. } else {
  6447. msg_obj = Tcl_NewStringObj(RSTRING_PTR(msg), RSTRING_LEN(msg));
  6448. Tcl_IncrRefCount(msg_obj);
  6449. }
  6450. return Tcl_CancelEval(interp, msg_obj, 0, flag);
  6451. #endif
  6452. }
  6453. static VALUE
  6454. ip_cancel_eval(argc, argv, self)
  6455. int argc;
  6456. VALUE *argv;
  6457. VALUE self;
  6458. {
  6459. VALUE retval;
  6460. if (rb_scan_args(argc, argv, "01", &retval) == 0) {
  6461. retval = Qnil;
  6462. }
  6463. if (ip_cancel_eval_core(get_ip(self)->ip, retval, 0) == TCL_OK) {
  6464. return Qtrue;
  6465. } else {
  6466. return Qfalse;
  6467. }
  6468. }
  6469. #ifndef TCL_CANCEL_UNWIND
  6470. #define TCL_CANCEL_UNWIND 0x100000
  6471. #endif
  6472. static VALUE
  6473. ip_cancel_eval_unwind(argc, argv, self)
  6474. int argc;
  6475. VALUE *argv;
  6476. VALUE self;
  6477. {
  6478. int flag = 0;
  6479. VALUE retval;
  6480. if (rb_scan_args(argc, argv, "01", &retval) == 0) {
  6481. retval = Qnil;
  6482. }
  6483. flag |= TCL_CANCEL_UNWIND;
  6484. if (ip_cancel_eval_core(get_ip(self)->ip, retval, flag) == TCL_OK) {
  6485. return Qtrue;
  6486. } else {
  6487. return Qfalse;
  6488. }
  6489. }
  6490. /* restart Tk */
  6491. static VALUE
  6492. lib_restart_core(interp, argc, argv)
  6493. VALUE interp;
  6494. int argc; /* dummy */
  6495. VALUE *argv; /* dummy */
  6496. {
  6497. volatile VALUE exc;
  6498. struct tcltkip *ptr = get_ip(interp);
  6499. int thr_crit_bup;
  6500. /* rb_secure(4); */ /* already checked */
  6501. /* tcl_stubs_check(); */ /* already checked */
  6502. /* ip is deleted? */
  6503. if (deleted_ip(ptr)) {
  6504. return rb_exc_new2(rb_eRuntimeError, "interpreter is deleted");
  6505. }
  6506. thr_crit_bup = rb_thread_critical;
  6507. rb_thread_critical = Qtrue;
  6508. /* Tcl_Preserve(ptr->ip); */
  6509. rbtk_preserve_ip(ptr);
  6510. /* destroy the root wdiget */
  6511. ptr->return_value = Tcl_Eval(ptr->ip, "destroy .");
  6512. /* ignore ERROR */
  6513. DUMP2("(TCL_Eval result) %d", ptr->return_value);
  6514. Tcl_ResetResult(ptr->ip);
  6515. #if TCL_MAJOR_VERSION >= 8
  6516. /* delete namespace ( tested on tk8.4.5 ) */
  6517. ptr->return_value = Tcl_Eval(ptr->ip, "namespace delete ::tk::msgcat");
  6518. /* ignore ERROR */
  6519. DUMP2("(TCL_Eval result) %d", ptr->return_value);
  6520. Tcl_ResetResult(ptr->ip);
  6521. #endif
  6522. /* delete trace proc ( tested on tk8.4.5 ) */
  6523. ptr->return_value = Tcl_Eval(ptr->ip, "trace vdelete ::tk_strictMotif w ::tk::EventMotifBindings");
  6524. /* ignore ERROR */
  6525. DUMP2("(TCL_Eval result) %d", ptr->return_value);
  6526. Tcl_ResetResult(ptr->ip);
  6527. /* execute Tk_Init or Tk_SafeInit */
  6528. exc = tcltkip_init_tk(interp);
  6529. if (!NIL_P(exc)) {
  6530. rb_thread_critical = thr_crit_bup;
  6531. rbtk_release_ip(ptr);
  6532. return exc;
  6533. }
  6534. /* Tcl_Release(ptr->ip); */
  6535. rbtk_release_ip(ptr);
  6536. rb_thread_critical = thr_crit_bup;
  6537. /* return Qnil; */
  6538. return interp;
  6539. }
  6540. static VALUE
  6541. lib_restart(self)
  6542. VALUE self;
  6543. {
  6544. struct tcltkip *ptr = get_ip(self);
  6545. rb_secure(4);
  6546. tcl_stubs_check();
  6547. /* ip is deleted? */
  6548. if (deleted_ip(ptr)) {
  6549. rb_raise(rb_eRuntimeError, "interpreter is deleted");
  6550. }
  6551. return tk_funcall(lib_restart_core, 0, (VALUE*)NULL, self);
  6552. }
  6553. static VALUE
  6554. ip_restart(self)
  6555. VALUE self;
  6556. {
  6557. struct tcltkip *ptr = get_ip(self);
  6558. rb_secure(4);
  6559. tcl_stubs_check();
  6560. /* ip is deleted? */
  6561. if (deleted_ip(ptr)) {
  6562. rb_raise(rb_eRuntimeError, "interpreter is deleted");
  6563. }
  6564. if (Tcl_GetMaster(ptr->ip) != (Tcl_Interp*)NULL) {
  6565. /* slave IP */
  6566. return Qnil;
  6567. }
  6568. return lib_restart(self);
  6569. }
  6570. static VALUE
  6571. lib_toUTF8_core(ip_obj, src, encodename)
  6572. VALUE ip_obj;
  6573. VALUE src;
  6574. VALUE encodename;
  6575. {
  6576. volatile VALUE str = src;
  6577. #ifdef TCL_UTF_MAX
  6578. Tcl_Interp *interp;
  6579. Tcl_Encoding encoding;
  6580. Tcl_DString dstr;
  6581. int taint_flag = OBJ_TAINTED(str);
  6582. struct tcltkip *ptr;
  6583. char *buf;
  6584. int thr_crit_bup;
  6585. #endif
  6586. tcl_stubs_check();
  6587. if (NIL_P(src)) {
  6588. return rb_str_new2("");
  6589. }
  6590. #ifdef TCL_UTF_MAX
  6591. if (NIL_P(ip_obj)) {
  6592. interp = (Tcl_Interp *)NULL;
  6593. } else {
  6594. ptr = get_ip(ip_obj);
  6595. /* ip is deleted? */
  6596. if (deleted_ip(ptr)) {
  6597. interp = (Tcl_Interp *)NULL;
  6598. } else {
  6599. interp = ptr->ip;
  6600. }
  6601. }
  6602. thr_crit_bup = rb_thread_critical;
  6603. rb_thread_critical = Qtrue;
  6604. if (NIL_P(encodename)) {
  6605. if (TYPE(str) == T_STRING) {
  6606. volatile VALUE enc;
  6607. #ifdef HAVE_RUBY_ENCODING_H
  6608. enc = rb_funcall(rb_obj_encoding(str), ID_to_s, 0, 0);
  6609. #else
  6610. enc = rb_attr_get(str, ID_at_enc);
  6611. #endif
  6612. if (NIL_P(enc)) {
  6613. if (NIL_P(ip_obj)) {
  6614. encoding = (Tcl_Encoding)NULL;
  6615. } else {
  6616. enc = rb_attr_get(ip_obj, ID_at_enc);
  6617. if (NIL_P(enc)) {
  6618. encoding = (Tcl_Encoding)NULL;
  6619. } else {
  6620. /* StringValue(enc); */
  6621. enc = rb_funcall(enc, ID_to_s, 0, 0);
  6622. /* encoding = Tcl_GetEncoding(interp, RSTRING_PTR(enc)); */
  6623. if (!RSTRING_LEN(enc)) {
  6624. encoding = (Tcl_Encoding)NULL;
  6625. } else {
  6626. encoding = Tcl_GetEncoding((Tcl_Interp*)NULL,
  6627. RSTRING_PTR(enc));
  6628. if (encoding == (Tcl_Encoding)NULL) {
  6629. rb_warning("Tk-interp has unknown encoding information (@encoding:'%s')", RSTRING_PTR(enc));
  6630. }
  6631. }
  6632. }
  6633. }
  6634. } else {
  6635. StringValue(enc);
  6636. if (strcmp(RSTRING_PTR(enc), "binary") == 0) {
  6637. #ifdef HAVE_RUBY_ENCODING_H
  6638. rb_enc_associate_index(str, ENCODING_INDEX_BINARY);
  6639. #endif
  6640. rb_ivar_set(str, ID_at_enc, ENCODING_NAME_BINARY);
  6641. rb_thread_critical = thr_crit_bup;
  6642. return str;
  6643. }
  6644. /* encoding = Tcl_GetEncoding(interp, RSTRING_PTR(enc)); */
  6645. encoding = Tcl_GetEncoding((Tcl_Interp*)NULL,
  6646. RSTRING_PTR(enc));
  6647. if (encoding == (Tcl_Encoding)NULL) {
  6648. rb_warning("string has unknown encoding information (@encoding:'%s')", RSTRING_PTR(enc));
  6649. }
  6650. }
  6651. } else {
  6652. encoding = (Tcl_Encoding)NULL;
  6653. }
  6654. } else {
  6655. StringValue(encodename);
  6656. if (strcmp(RSTRING_PTR(encodename), "binary") == 0) {
  6657. #ifdef HAVE_RUBY_ENCODING_H
  6658. rb_enc_associate_index(str, ENCODING_INDEX_BINARY);
  6659. #endif
  6660. rb_ivar_set(str, ID_at_enc, ENCODING_NAME_BINARY);
  6661. rb_thread_critical = thr_crit_bup;
  6662. return str;
  6663. }
  6664. /* encoding = Tcl_GetEncoding(interp, RSTRING_PTR(encodename)); */
  6665. encoding = Tcl_GetEncoding((Tcl_Interp*)NULL, RSTRING_PTR(encodename));
  6666. if (encoding == (Tcl_Encoding)NULL) {
  6667. /*
  6668. rb_warning("unknown encoding name '%s'",
  6669. RSTRING_PTR(encodename));
  6670. */
  6671. rb_raise(rb_eArgError, "unknown encoding name '%s'",
  6672. RSTRING_PTR(encodename));
  6673. }
  6674. }
  6675. StringValue(str);
  6676. if (!RSTRING_LEN(str)) {
  6677. rb_thread_critical = thr_crit_bup;
  6678. return str;
  6679. }
  6680. buf = ALLOC_N(char, RSTRING_LEN(str)+1);
  6681. /* buf = ckalloc(sizeof(char) * (RSTRING_LEN(str)+1)); */
  6682. memcpy(buf, RSTRING_PTR(str), RSTRING_LEN(str));
  6683. buf[RSTRING_LEN(str)] = 0;
  6684. Tcl_DStringInit(&dstr);
  6685. Tcl_DStringFree(&dstr);
  6686. /* Tcl_ExternalToUtfDString(encoding,buf,strlen(buf),&dstr); */
  6687. Tcl_ExternalToUtfDString(encoding, buf, RSTRING_LEN(str), &dstr);
  6688. /* str = rb_tainted_str_new2(Tcl_DStringValue(&dstr)); */
  6689. /* str = rb_str_new2(Tcl_DStringValue(&dstr)); */
  6690. str = rb_str_new(Tcl_DStringValue(&dstr), Tcl_DStringLength(&dstr));
  6691. #ifdef HAVE_RUBY_ENCODING_H
  6692. rb_enc_associate_index(str, ENCODING_INDEX_UTF8);
  6693. #endif
  6694. if (taint_flag) RbTk_OBJ_UNTRUST(str);
  6695. rb_ivar_set(str, ID_at_enc, ENCODING_NAME_UTF8);
  6696. /*
  6697. if (encoding != (Tcl_Encoding)NULL) {
  6698. Tcl_FreeEncoding(encoding);
  6699. }
  6700. */
  6701. Tcl_DStringFree(&dstr);
  6702. xfree(buf);
  6703. /* ckfree(buf); */
  6704. rb_thread_critical = thr_crit_bup;
  6705. #endif
  6706. return str;
  6707. }
  6708. static VALUE
  6709. lib_toUTF8(argc, argv, self)
  6710. int argc;
  6711. VALUE *argv;
  6712. VALUE self;
  6713. {
  6714. VALUE str, encodename;
  6715. if (rb_scan_args(argc, argv, "11", &str, &encodename) == 1) {
  6716. encodename = Qnil;
  6717. }
  6718. return lib_toUTF8_core(Qnil, str, encodename);
  6719. }
  6720. static VALUE
  6721. ip_toUTF8(argc, argv, self)
  6722. int argc;
  6723. VALUE *argv;
  6724. VALUE self;
  6725. {
  6726. VALUE str, encodename;
  6727. if (rb_scan_args(argc, argv, "11", &str, &encodename) == 1) {
  6728. encodename = Qnil;
  6729. }
  6730. return lib_toUTF8_core(self, str, encodename);
  6731. }
  6732. static VALUE
  6733. lib_fromUTF8_core(ip_obj, src, encodename)
  6734. VALUE ip_obj;
  6735. VALUE src;
  6736. VALUE encodename;
  6737. {
  6738. volatile VALUE str = src;
  6739. #ifdef TCL_UTF_MAX
  6740. Tcl_Interp *interp;
  6741. Tcl_Encoding encoding;
  6742. Tcl_DString dstr;
  6743. int taint_flag = OBJ_TAINTED(str);
  6744. char *buf;
  6745. int thr_crit_bup;
  6746. #endif
  6747. tcl_stubs_check();
  6748. if (NIL_P(src)) {
  6749. return rb_str_new2("");
  6750. }
  6751. #ifdef TCL_UTF_MAX
  6752. if (NIL_P(ip_obj)) {
  6753. interp = (Tcl_Interp *)NULL;
  6754. } else if (get_ip(ip_obj) == (struct tcltkip *)NULL) {
  6755. interp = (Tcl_Interp *)NULL;
  6756. } else {
  6757. interp = get_ip(ip_obj)->ip;
  6758. }
  6759. thr_crit_bup = rb_thread_critical;
  6760. rb_thread_critical = Qtrue;
  6761. if (NIL_P(encodename)) {
  6762. volatile VALUE enc;
  6763. if (TYPE(str) == T_STRING) {
  6764. enc = rb_attr_get(str, ID_at_enc);
  6765. if (!NIL_P(enc)) {
  6766. StringValue(enc);
  6767. if (strcmp(RSTRING_PTR(enc), "binary") == 0) {
  6768. #ifdef HAVE_RUBY_ENCODING_H
  6769. rb_enc_associate_index(str, ENCODING_INDEX_BINARY);
  6770. #endif
  6771. rb_ivar_set(str, ID_at_enc, ENCODING_NAME_BINARY);
  6772. rb_thread_critical = thr_crit_bup;
  6773. return str;
  6774. }
  6775. #ifdef HAVE_RUBY_ENCODING_H
  6776. } else if (rb_enc_get_index(str) == ENCODING_INDEX_BINARY) {
  6777. rb_enc_associate_index(str, ENCODING_INDEX_BINARY);
  6778. rb_ivar_set(str, ID_at_enc, ENCODING_NAME_BINARY);
  6779. rb_thread_critical = thr_crit_bup;
  6780. return str;
  6781. #endif
  6782. }
  6783. }
  6784. if (NIL_P(ip_obj)) {
  6785. encoding = (Tcl_Encoding)NULL;
  6786. } else {
  6787. enc = rb_attr_get(ip_obj, ID_at_enc);
  6788. if (NIL_P(enc)) {
  6789. encoding = (Tcl_Encoding)NULL;
  6790. } else {
  6791. /* StringValue(enc); */
  6792. enc = rb_funcall(enc, ID_to_s, 0, 0);
  6793. /* encoding = Tcl_GetEncoding(interp, RSTRING_PTR(enc)); */
  6794. if (!RSTRING_LEN(enc)) {
  6795. encoding = (Tcl_Encoding)NULL;
  6796. } else {
  6797. encoding = Tcl_GetEncoding((Tcl_Interp*)NULL,
  6798. RSTRING_PTR(enc));
  6799. if (encoding == (Tcl_Encoding)NULL) {
  6800. rb_warning("Tk-interp has unknown encoding information (@encoding:'%s')", RSTRING_PTR(enc));
  6801. } else {
  6802. encodename = rb_obj_dup(enc);
  6803. }
  6804. }
  6805. }
  6806. }
  6807. } else {
  6808. StringValue(encodename);
  6809. if (strcmp(RSTRING_PTR(encodename), "binary") == 0) {
  6810. Tcl_Obj *tclstr;
  6811. char *s;
  6812. int len;
  6813. StringValue(str);
  6814. tclstr = Tcl_NewStringObj(RSTRING_PTR(str), RSTRING_LEN(str));
  6815. Tcl_IncrRefCount(tclstr);
  6816. s = (char*)Tcl_GetByteArrayFromObj(tclstr, &len);
  6817. str = rb_tainted_str_new(s, len);
  6818. s = (char*)NULL;
  6819. Tcl_DecrRefCount(tclstr);
  6820. #ifdef HAVE_RUBY_ENCODING_H
  6821. rb_enc_associate_index(str, ENCODING_INDEX_BINARY);
  6822. #endif
  6823. rb_ivar_set(str, ID_at_enc, ENCODING_NAME_BINARY);
  6824. rb_thread_critical = thr_crit_bup;
  6825. return str;
  6826. }
  6827. /* encoding = Tcl_GetEncoding(interp, RSTRING_PTR(encodename)); */
  6828. encoding = Tcl_GetEncoding((Tcl_Interp*)NULL, RSTRING_PTR(encodename));
  6829. if (encoding == (Tcl_Encoding)NULL) {
  6830. /*
  6831. rb_warning("unknown encoding name '%s'",
  6832. RSTRING_PTR(encodename));
  6833. encodename = Qnil;
  6834. */
  6835. rb_raise(rb_eArgError, "unknown encoding name '%s'",
  6836. RSTRING_PTR(encodename));
  6837. }
  6838. }
  6839. StringValue(str);
  6840. if (RSTRING_LEN(str) == 0) {
  6841. rb_thread_critical = thr_crit_bup;
  6842. return rb_tainted_str_new2("");
  6843. }
  6844. buf = ALLOC_N(char, RSTRING_LEN(str)+1);
  6845. /* buf = ckalloc(sizeof(char) * (RSTRING_LEN(str)+1)); */
  6846. memcpy(buf, RSTRING_PTR(str), RSTRING_LEN(str));
  6847. buf[RSTRING_LEN(str)] = 0;
  6848. Tcl_DStringInit(&dstr);
  6849. Tcl_DStringFree(&dstr);
  6850. /* Tcl_UtfToExternalDString(encoding,buf,strlen(buf),&dstr); */
  6851. Tcl_UtfToExternalDString(encoding,buf,RSTRING_LEN(str),&dstr);
  6852. /* str = rb_tainted_str_new2(Tcl_DStringValue(&dstr)); */
  6853. /* str = rb_str_new2(Tcl_DStringValue(&dstr)); */
  6854. str = rb_str_new(Tcl_DStringValue(&dstr), Tcl_DStringLength(&dstr));
  6855. #ifdef HAVE_RUBY_ENCODING_H
  6856. if (interp) {
  6857. /* can access encoding_table of TclTkIp */
  6858. /* -> try to use encoding_table */
  6859. VALUE tbl = ip_get_encoding_table(ip_obj);
  6860. VALUE encobj = encoding_table_get_obj(tbl, encodename);
  6861. rb_enc_associate_index(str, rb_to_encoding_index(encobj));
  6862. } else {
  6863. /* cannot access encoding_table of TclTkIp */
  6864. /* -> try to find on Ruby Encoding */
  6865. rb_enc_associate_index(str, rb_enc_find_index(RSTRING_PTR(encodename)));
  6866. }
  6867. #endif
  6868. if (taint_flag) RbTk_OBJ_UNTRUST(str);
  6869. rb_ivar_set(str, ID_at_enc, encodename);
  6870. /*
  6871. if (encoding != (Tcl_Encoding)NULL) {
  6872. Tcl_FreeEncoding(encoding);
  6873. }
  6874. */
  6875. Tcl_DStringFree(&dstr);
  6876. xfree(buf);
  6877. /* ckfree(buf); */
  6878. rb_thread_critical = thr_crit_bup;
  6879. #endif
  6880. return str;
  6881. }
  6882. static VALUE
  6883. lib_fromUTF8(argc, argv, self)
  6884. int argc;
  6885. VALUE *argv;
  6886. VALUE self;
  6887. {
  6888. VALUE str, encodename;
  6889. if (rb_scan_args(argc, argv, "11", &str, &encodename) == 1) {
  6890. encodename = Qnil;
  6891. }
  6892. return lib_fromUTF8_core(Qnil, str, encodename);
  6893. }
  6894. static VALUE
  6895. ip_fromUTF8(argc, argv, self)
  6896. int argc;
  6897. VALUE *argv;
  6898. VALUE self;
  6899. {
  6900. VALUE str, encodename;
  6901. if (rb_scan_args(argc, argv, "11", &str, &encodename) == 1) {
  6902. encodename = Qnil;
  6903. }
  6904. return lib_fromUTF8_core(self, str, encodename);
  6905. }
  6906. static VALUE
  6907. lib_UTF_backslash_core(self, str, all_bs)
  6908. VALUE self;
  6909. VALUE str;
  6910. int all_bs;
  6911. {
  6912. #ifdef TCL_UTF_MAX
  6913. char *src_buf, *dst_buf, *ptr;
  6914. int read_len = 0, dst_len = 0;
  6915. int taint_flag = OBJ_TAINTED(str);
  6916. int thr_crit_bup;
  6917. tcl_stubs_check();
  6918. StringValue(str);
  6919. if (!RSTRING_LEN(str)) {
  6920. return str;
  6921. }
  6922. thr_crit_bup = rb_thread_critical;
  6923. rb_thread_critical = Qtrue;
  6924. /* src_buf = ALLOC_N(char, RSTRING_LEN(str)+1); */
  6925. src_buf = ckalloc(sizeof(char) * (RSTRING_LEN(str)+1));
  6926. #if 0 /* use Tcl_Preserve/Release */
  6927. Tcl_Preserve((ClientData)src_buf); /* XXXXXXXX */
  6928. #endif
  6929. memcpy(src_buf, RSTRING_PTR(str), RSTRING_LEN(str));
  6930. src_buf[RSTRING_LEN(str)] = 0;
  6931. /* dst_buf = ALLOC_N(char, RSTRING_LEN(str)+1); */
  6932. dst_buf = ckalloc(sizeof(char) * (RSTRING_LEN(str)+1));
  6933. #if 0 /* use Tcl_Preserve/Release */
  6934. Tcl_Preserve((ClientData)dst_buf); /* XXXXXXXX */
  6935. #endif
  6936. ptr = src_buf;
  6937. while(RSTRING_LEN(str) > ptr - src_buf) {
  6938. if (*ptr == '\\' && (all_bs || *(ptr + 1) == 'u')) {
  6939. dst_len += Tcl_UtfBackslash(ptr, &read_len, (dst_buf + dst_len));
  6940. ptr += read_len;
  6941. } else {
  6942. *(dst_buf + (dst_len++)) = *(ptr++);
  6943. }
  6944. }
  6945. str = rb_str_new(dst_buf, dst_len);
  6946. if (taint_flag) RbTk_OBJ_UNTRUST(str);
  6947. #ifdef HAVE_RUBY_ENCODING_H
  6948. rb_enc_associate_index(str, ENCODING_INDEX_UTF8);
  6949. #endif
  6950. rb_ivar_set(str, ID_at_enc, ENCODING_NAME_UTF8);
  6951. #if 0 /* use Tcl_EventuallyFree */
  6952. Tcl_EventuallyFree((ClientData)src_buf, TCL_DYNAMIC); /* XXXXXXXX */
  6953. #else
  6954. #if 0 /* use Tcl_Preserve/Release */
  6955. Tcl_Release((ClientData)src_buf); /* XXXXXXXX */
  6956. #else
  6957. /* free(src_buf); */
  6958. ckfree(src_buf);
  6959. #endif
  6960. #endif
  6961. #if 0 /* use Tcl_EventuallyFree */
  6962. Tcl_EventuallyFree((ClientData)dst_buf, TCL_DYNAMIC); /* XXXXXXXX */
  6963. #else
  6964. #if 0 /* use Tcl_Preserve/Release */
  6965. Tcl_Release((ClientData)dst_buf); /* XXXXXXXX */
  6966. #else
  6967. /* free(dst_buf); */
  6968. ckfree(dst_buf);
  6969. #endif
  6970. #endif
  6971. rb_thread_critical = thr_crit_bup;
  6972. #endif
  6973. return str;
  6974. }
  6975. static VALUE
  6976. lib_UTF_backslash(self, str)
  6977. VALUE self;
  6978. VALUE str;
  6979. {
  6980. return lib_UTF_backslash_core(self, str, 0);
  6981. }
  6982. static VALUE
  6983. lib_Tcl_backslash(self, str)
  6984. VALUE self;
  6985. VALUE str;
  6986. {
  6987. return lib_UTF_backslash_core(self, str, 1);
  6988. }
  6989. static VALUE
  6990. lib_get_system_encoding(self)
  6991. VALUE self;
  6992. {
  6993. #if TCL_MAJOR_VERSION > 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION > 0)
  6994. tcl_stubs_check();
  6995. return rb_str_new2(Tcl_GetEncodingName((Tcl_Encoding)NULL));
  6996. #else
  6997. return Qnil;
  6998. #endif
  6999. }
  7000. static VALUE
  7001. lib_set_system_encoding(self, enc_name)
  7002. VALUE self;
  7003. VALUE enc_name;
  7004. {
  7005. #if TCL_MAJOR_VERSION > 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION > 0)
  7006. tcl_stubs_check();
  7007. if (NIL_P(enc_name)) {
  7008. Tcl_SetSystemEncoding((Tcl_Interp *)NULL, (CONST char *)NULL);
  7009. return lib_get_system_encoding(self);
  7010. }
  7011. enc_name = rb_funcall(enc_name, ID_to_s, 0, 0);
  7012. if (Tcl_SetSystemEncoding((Tcl_Interp *)NULL,
  7013. StringValuePtr(enc_name)) != TCL_OK) {
  7014. rb_raise(rb_eArgError, "unknown encoding name '%s'",
  7015. RSTRING_PTR(enc_name));
  7016. }
  7017. return enc_name;
  7018. #else
  7019. return Qnil;
  7020. #endif
  7021. }
  7022. /* invoke Tcl proc */
  7023. struct invoke_info {
  7024. struct tcltkip *ptr;
  7025. Tcl_CmdInfo cmdinfo;
  7026. #if TCL_MAJOR_VERSION >= 8
  7027. int objc;
  7028. Tcl_Obj **objv;
  7029. #else
  7030. int argc;
  7031. char **argv;
  7032. #endif
  7033. };
  7034. static VALUE
  7035. #ifdef HAVE_PROTOTYPES
  7036. invoke_tcl_proc(VALUE arg)
  7037. #else
  7038. invoke_tcl_proc(arg)
  7039. VALUE arg;
  7040. #endif
  7041. {
  7042. struct invoke_info *inf = (struct invoke_info *)arg;
  7043. int i, len;
  7044. #if TCL_MAJOR_VERSION >= 8
  7045. int argc = inf->objc;
  7046. char **argv = (char **)NULL;
  7047. #endif
  7048. /* memory allocation for arguments of this command */
  7049. #if TCL_MAJOR_VERSION >= 8
  7050. if (!inf->cmdinfo.isNativeObjectProc) {
  7051. /* string interface */
  7052. /* argv = (char **)ALLOC_N(char *, argc+1);*/ /* XXXXXXXXXX */
  7053. argv = (char **)ckalloc(sizeof(char *)*(argc+1));
  7054. #if 0 /* use Tcl_Preserve/Release */
  7055. Tcl_Preserve((ClientData)argv); /* XXXXXXXX */
  7056. #endif
  7057. for (i = 0; i < argc; ++i) {
  7058. argv[i] = Tcl_GetStringFromObj(inf->objv[i], &len);
  7059. }
  7060. argv[argc] = (char *)NULL;
  7061. }
  7062. #endif
  7063. Tcl_ResetResult(inf->ptr->ip);
  7064. /* Invoke the C procedure */
  7065. #if TCL_MAJOR_VERSION >= 8
  7066. if (inf->cmdinfo.isNativeObjectProc) {
  7067. inf->ptr->return_value
  7068. = (*(inf->cmdinfo.objProc))(inf->cmdinfo.objClientData,
  7069. inf->ptr->ip, inf->objc, inf->objv);
  7070. }
  7071. else
  7072. #endif
  7073. {
  7074. #if TCL_MAJOR_VERSION >= 8
  7075. inf->ptr->return_value
  7076. = (*(inf->cmdinfo.proc))(inf->cmdinfo.clientData, inf->ptr->ip,
  7077. argc, (CONST84 char **)argv);
  7078. #if 0 /* use Tcl_EventuallyFree */
  7079. Tcl_EventuallyFree((ClientData)argv, TCL_DYNAMIC); /* XXXXXXXX */
  7080. #else
  7081. #if 0 /* use Tcl_Preserve/Release */
  7082. Tcl_Release((ClientData)argv); /* XXXXXXXX */
  7083. #else
  7084. /* free(argv); */
  7085. ckfree((char*)argv);
  7086. #endif
  7087. #endif
  7088. #else /* TCL_MAJOR_VERSION < 8 */
  7089. inf->ptr->return_value
  7090. = (*(inf->cmdinfo.proc))(inf->cmdinfo.clientData, inf->ptr->ip,
  7091. inf->argc, inf->argv);
  7092. #endif
  7093. }
  7094. return Qnil;
  7095. }
  7096. #if TCL_MAJOR_VERSION >= 8
  7097. static VALUE
  7098. ip_invoke_core(interp, objc, objv)
  7099. VALUE interp;
  7100. int objc;
  7101. Tcl_Obj **objv;
  7102. #else
  7103. static VALUE
  7104. ip_invoke_core(interp, argc, argv)
  7105. VALUE interp;
  7106. int argc;
  7107. char **argv;
  7108. #endif
  7109. {
  7110. struct tcltkip *ptr;
  7111. Tcl_CmdInfo info;
  7112. char *cmd;
  7113. int len;
  7114. int thr_crit_bup;
  7115. int unknown_flag = 0;
  7116. #if 1 /* wrap tcl-proc call */
  7117. struct invoke_info inf;
  7118. int status;
  7119. VALUE ret;
  7120. #else
  7121. #if TCL_MAJOR_VERSION >= 8
  7122. int argc = objc;
  7123. char **argv = (char **)NULL;
  7124. /* Tcl_Obj *resultPtr; */
  7125. #endif
  7126. #endif
  7127. /* get the data struct */
  7128. ptr = get_ip(interp);
  7129. /* get the command name string */
  7130. #if TCL_MAJOR_VERSION >= 8
  7131. cmd = Tcl_GetStringFromObj(objv[0], &len);
  7132. #else /* TCL_MAJOR_VERSION < 8 */
  7133. cmd = argv[0];
  7134. #endif
  7135. /* get the data struct */
  7136. ptr = get_ip(interp);
  7137. /* ip is deleted? */
  7138. if (deleted_ip(ptr)) {
  7139. return rb_tainted_str_new2("");
  7140. }
  7141. /* Tcl_Preserve(ptr->ip); */
  7142. rbtk_preserve_ip(ptr);
  7143. /* map from the command name to a C procedure */
  7144. DUMP2("call Tcl_GetCommandInfo, %s", cmd);
  7145. if (!Tcl_GetCommandInfo(ptr->ip, cmd, &info)) {
  7146. DUMP1("error Tcl_GetCommandInfo");
  7147. DUMP1("try auto_load (call 'unknown' command)");
  7148. if (!Tcl_GetCommandInfo(ptr->ip,
  7149. #if TCL_MAJOR_VERSION >= 8
  7150. "::unknown",
  7151. #else
  7152. "unknown",
  7153. #endif
  7154. &info)) {
  7155. DUMP1("fail to get 'unknown' command");
  7156. /* if (event_loop_abort_on_exc || cmd[0] != '.') { */
  7157. if (event_loop_abort_on_exc > 0) {
  7158. /* Tcl_Release(ptr->ip); */
  7159. rbtk_release_ip(ptr);
  7160. /*rb_ip_raise(obj,rb_eNameError,"invalid command name `%s'",cmd);*/
  7161. return create_ip_exc(interp, rb_eNameError,
  7162. "invalid command name `%s'", cmd);
  7163. } else {
  7164. if (event_loop_abort_on_exc < 0) {
  7165. rb_warning("invalid command name `%s' (ignore)", cmd);
  7166. } else {
  7167. rb_warn("invalid command name `%s' (ignore)", cmd);
  7168. }
  7169. Tcl_ResetResult(ptr->ip);
  7170. /* Tcl_Release(ptr->ip); */
  7171. rbtk_release_ip(ptr);
  7172. return rb_tainted_str_new2("");
  7173. }
  7174. } else {
  7175. #if TCL_MAJOR_VERSION >= 8
  7176. Tcl_Obj **unknown_objv;
  7177. #else
  7178. char **unknown_argv;
  7179. #endif
  7180. DUMP1("find 'unknown' command -> set arguemnts");
  7181. unknown_flag = 1;
  7182. #if TCL_MAJOR_VERSION >= 8
  7183. /* unknown_objv = (Tcl_Obj **)ALLOC_N(Tcl_Obj *, objc+2); */
  7184. unknown_objv = (Tcl_Obj **)ckalloc(sizeof(Tcl_Obj *) * (objc+2));
  7185. #if 0 /* use Tcl_Preserve/Release */
  7186. Tcl_Preserve((ClientData)unknown_objv); /* XXXXXXXX */
  7187. #endif
  7188. unknown_objv[0] = Tcl_NewStringObj("::unknown", 9);
  7189. Tcl_IncrRefCount(unknown_objv[0]);
  7190. memcpy(unknown_objv + 1, objv, sizeof(Tcl_Obj *)*objc);
  7191. unknown_objv[++objc] = (Tcl_Obj*)NULL;
  7192. objv = unknown_objv;
  7193. #else
  7194. /* unknown_argv = (char **)ALLOC_N(char *, argc+2); */
  7195. unknown_argv = (char **)ckalloc(sizeof(char *) * (argc+2));
  7196. #if 0 /* use Tcl_Preserve/Release */
  7197. Tcl_Preserve((ClientData)unknown_argv); /* XXXXXXXX */
  7198. #endif
  7199. unknown_argv[0] = strdup("unknown");
  7200. memcpy(unknown_argv + 1, argv, sizeof(char *)*argc);
  7201. unknown_argv[++argc] = (char *)NULL;
  7202. argv = unknown_argv;
  7203. #endif
  7204. }
  7205. }
  7206. DUMP1("end Tcl_GetCommandInfo");
  7207. thr_crit_bup = rb_thread_critical;
  7208. rb_thread_critical = Qtrue;
  7209. #if 1 /* wrap tcl-proc call */
  7210. /* setup params */
  7211. inf.ptr = ptr;
  7212. inf.cmdinfo = info;
  7213. #if TCL_MAJOR_VERSION >= 8
  7214. inf.objc = objc;
  7215. inf.objv = objv;
  7216. #else
  7217. inf.argc = argc;
  7218. inf.argv = argv;
  7219. #endif
  7220. /* invoke tcl-proc */
  7221. ret = rb_protect(invoke_tcl_proc, (VALUE)&inf, &status);
  7222. switch(status) {
  7223. case TAG_RAISE:
  7224. if (NIL_P(rb_errinfo())) {
  7225. rbtk_pending_exception = rb_exc_new2(rb_eException,
  7226. "unknown exception");
  7227. } else {
  7228. rbtk_pending_exception = rb_errinfo();
  7229. }
  7230. break;
  7231. case TAG_FATAL:
  7232. if (NIL_P(rb_errinfo())) {
  7233. rbtk_pending_exception = rb_exc_new2(rb_eFatal, "FATAL");
  7234. } else {
  7235. rbtk_pending_exception = rb_errinfo();
  7236. }
  7237. }
  7238. #else /* !wrap tcl-proc call */
  7239. /* memory allocation for arguments of this command */
  7240. #if TCL_MAJOR_VERSION >= 8
  7241. if (!info.isNativeObjectProc) {
  7242. int i;
  7243. /* string interface */
  7244. /* argv = (char **)ALLOC_N(char *, argc+1); */
  7245. argv = (char **)ckalloc(sizeof(char *) * (argc+1));
  7246. #if 0 /* use Tcl_Preserve/Release */
  7247. Tcl_Preserve((ClientData)argv); /* XXXXXXXX */
  7248. #endif
  7249. for (i = 0; i < argc; ++i) {
  7250. argv[i] = Tcl_GetStringFromObj(objv[i], &len);
  7251. }
  7252. argv[argc] = (char *)NULL;
  7253. }
  7254. #endif
  7255. Tcl_ResetResult(ptr->ip);
  7256. /* Invoke the C procedure */
  7257. #if TCL_MAJOR_VERSION >= 8
  7258. if (info.isNativeObjectProc) {
  7259. ptr->return_value = (*info.objProc)(info.objClientData, ptr->ip,
  7260. objc, objv);
  7261. #if 0
  7262. /* get the string value from the result object */
  7263. resultPtr = Tcl_GetObjResult(ptr->ip);
  7264. Tcl_SetResult(ptr->ip, Tcl_GetStringFromObj(resultPtr, &len),
  7265. TCL_VOLATILE);
  7266. #endif
  7267. }
  7268. else
  7269. #endif
  7270. {
  7271. #if TCL_MAJOR_VERSION >= 8
  7272. ptr->return_value = (*info.proc)(info.clientData, ptr->ip,
  7273. argc, (CONST84 char **)argv);
  7274. #if 0 /* use Tcl_EventuallyFree */
  7275. Tcl_EventuallyFree((ClientData)argv, TCL_DYNAMIC); /* XXXXXXXX */
  7276. #else
  7277. #if 0 /* use Tcl_Preserve/Release */
  7278. Tcl_Release((ClientData)argv); /* XXXXXXXX */
  7279. #else
  7280. /* free(argv); */
  7281. ckfree((char*)argv);
  7282. #endif
  7283. #endif
  7284. #else /* TCL_MAJOR_VERSION < 8 */
  7285. ptr->return_value = (*info.proc)(info.clientData, ptr->ip,
  7286. argc, argv);
  7287. #endif
  7288. }
  7289. #endif /* ! wrap tcl-proc call */
  7290. /* free allocated memory for calling 'unknown' command */
  7291. if (unknown_flag) {
  7292. #if TCL_MAJOR_VERSION >= 8
  7293. Tcl_DecrRefCount(objv[0]);
  7294. #if 0 /* use Tcl_EventuallyFree */
  7295. Tcl_EventuallyFree((ClientData)objv, TCL_DYNAMIC); /* XXXXXXXX */
  7296. #else
  7297. #if 0 /* use Tcl_Preserve/Release */
  7298. Tcl_Release((ClientData)objv); /* XXXXXXXX */
  7299. #else
  7300. /* free(objv); */
  7301. ckfree((char*)objv);
  7302. #endif
  7303. #endif
  7304. #else /* TCL_MAJOR_VERSION < 8 */
  7305. free(argv[0]);
  7306. /* ckfree(argv[0]); */
  7307. #if 0 /* use Tcl_EventuallyFree */
  7308. Tcl_EventuallyFree((ClientData)argv, TCL_DYNAMIC); /* XXXXXXXX */
  7309. #else
  7310. #if 0 /* use Tcl_Preserve/Release */
  7311. Tcl_Release((ClientData)argv); /* XXXXXXXX */
  7312. #else
  7313. /* free(argv); */
  7314. ckfree((char*)argv);
  7315. #endif
  7316. #endif
  7317. #endif
  7318. }
  7319. /* exception on mainloop */
  7320. if (pending_exception_check1(thr_crit_bup, ptr)) {
  7321. return rbtk_pending_exception;
  7322. }
  7323. rb_thread_critical = thr_crit_bup;
  7324. /* if (ptr->return_value == TCL_ERROR) { */
  7325. if (ptr->return_value != TCL_OK) {
  7326. if (event_loop_abort_on_exc > 0 && !Tcl_InterpDeleted(ptr->ip)) {
  7327. switch (ptr->return_value) {
  7328. case TCL_RETURN:
  7329. return create_ip_exc(interp, eTkCallbackReturn,
  7330. "ip_invoke_core receives TCL_RETURN");
  7331. case TCL_BREAK:
  7332. return create_ip_exc(interp, eTkCallbackBreak,
  7333. "ip_invoke_core receives TCL_BREAK");
  7334. case TCL_CONTINUE:
  7335. return create_ip_exc(interp, eTkCallbackContinue,
  7336. "ip_invoke_core receives TCL_CONTINUE");
  7337. default:
  7338. return create_ip_exc(interp, rb_eRuntimeError, "%s",
  7339. Tcl_GetStringResult(ptr->ip));
  7340. }
  7341. } else {
  7342. if (event_loop_abort_on_exc < 0) {
  7343. rb_warning("%s (ignore)", Tcl_GetStringResult(ptr->ip));
  7344. } else {
  7345. rb_warn("%s (ignore)", Tcl_GetStringResult(ptr->ip));
  7346. }
  7347. Tcl_ResetResult(ptr->ip);
  7348. return rb_tainted_str_new2("");
  7349. }
  7350. }
  7351. /* pass back the result (as string) */
  7352. return ip_get_result_string_obj(ptr->ip);
  7353. }
  7354. #if TCL_MAJOR_VERSION >= 8
  7355. static Tcl_Obj **
  7356. #else /* TCL_MAJOR_VERSION < 8 */
  7357. static char **
  7358. #endif
  7359. alloc_invoke_arguments(argc, argv)
  7360. int argc;
  7361. VALUE *argv;
  7362. {
  7363. int i;
  7364. int thr_crit_bup;
  7365. #if TCL_MAJOR_VERSION >= 8
  7366. Tcl_Obj **av;
  7367. #else /* TCL_MAJOR_VERSION < 8 */
  7368. char **av;
  7369. #endif
  7370. thr_crit_bup = rb_thread_critical;
  7371. rb_thread_critical = Qtrue;
  7372. /* memory allocation */
  7373. #if TCL_MAJOR_VERSION >= 8
  7374. /* av = ALLOC_N(Tcl_Obj *, argc+1);*/ /* XXXXXXXXXX */
  7375. av = (Tcl_Obj**)ckalloc(sizeof(Tcl_Obj *)*(argc+1));
  7376. #if 0 /* use Tcl_Preserve/Release */
  7377. Tcl_Preserve((ClientData)av); /* XXXXXXXX */
  7378. #endif
  7379. for (i = 0; i < argc; ++i) {
  7380. av[i] = get_obj_from_str(argv[i]);
  7381. Tcl_IncrRefCount(av[i]);
  7382. }
  7383. av[argc] = NULL;
  7384. #else /* TCL_MAJOR_VERSION < 8 */
  7385. /* string interface */
  7386. /* av = ALLOC_N(char *, argc+1); */
  7387. av = (char**)ckalloc(sizeof(char *) * (argc+1));
  7388. #if 0 /* use Tcl_Preserve/Release */
  7389. Tcl_Preserve((ClientData)av); /* XXXXXXXX */
  7390. #endif
  7391. for (i = 0; i < argc; ++i) {
  7392. av[i] = strdup(StringValuePtr(argv[i]));
  7393. }
  7394. av[argc] = NULL;
  7395. #endif
  7396. rb_thread_critical = thr_crit_bup;
  7397. return av;
  7398. }
  7399. static void
  7400. free_invoke_arguments(argc, av)
  7401. int argc;
  7402. #if TCL_MAJOR_VERSION >= 8
  7403. Tcl_Obj **av;
  7404. #else /* TCL_MAJOR_VERSION < 8 */
  7405. char **av;
  7406. #endif
  7407. {
  7408. int i;
  7409. for (i = 0; i < argc; ++i) {
  7410. #if TCL_MAJOR_VERSION >= 8
  7411. Tcl_DecrRefCount(av[i]);
  7412. av[i] = (Tcl_Obj*)NULL;
  7413. #else /* TCL_MAJOR_VERSION < 8 */
  7414. free(av[i]);
  7415. av[i] = (char*)NULL;
  7416. #endif
  7417. }
  7418. #if TCL_MAJOR_VERSION >= 8
  7419. #if 0 /* use Tcl_EventuallyFree */
  7420. Tcl_EventuallyFree((ClientData)av, TCL_DYNAMIC); /* XXXXXXXX */
  7421. #else
  7422. #if 0 /* use Tcl_Preserve/Release */
  7423. Tcl_Release((ClientData)av); /* XXXXXXXX */
  7424. #else
  7425. ckfree((char*)av);
  7426. #endif
  7427. #endif
  7428. #else /* TCL_MAJOR_VERSION < 8 */
  7429. #if 0 /* use Tcl_EventuallyFree */
  7430. Tcl_EventuallyFree((ClientData)av, TCL_DYNAMIC); /* XXXXXXXX */
  7431. #else
  7432. #if 0 /* use Tcl_Preserve/Release */
  7433. Tcl_Release((ClientData)av); /* XXXXXXXX */
  7434. #else
  7435. /* free(av); */
  7436. ckfree((char*)av);
  7437. #endif
  7438. #endif
  7439. #endif
  7440. }
  7441. static VALUE
  7442. ip_invoke_real(argc, argv, interp)
  7443. int argc;
  7444. VALUE *argv;
  7445. VALUE interp;
  7446. {
  7447. VALUE v;
  7448. struct tcltkip *ptr; /* tcltkip data struct */
  7449. #if TCL_MAJOR_VERSION >= 8
  7450. Tcl_Obj **av = (Tcl_Obj **)NULL;
  7451. #else /* TCL_MAJOR_VERSION < 8 */
  7452. char **av = (char **)NULL;
  7453. #endif
  7454. DUMP2("invoke_real called by thread:%lx", rb_thread_current());
  7455. /* get the data struct */
  7456. ptr = get_ip(interp);
  7457. /* ip is deleted? */
  7458. if (deleted_ip(ptr)) {
  7459. return rb_tainted_str_new2("");
  7460. }
  7461. /* allocate memory for arguments */
  7462. av = alloc_invoke_arguments(argc, argv);
  7463. /* Invoke the C procedure */
  7464. Tcl_ResetResult(ptr->ip);
  7465. v = ip_invoke_core(interp, argc, av);
  7466. /* free allocated memory */
  7467. free_invoke_arguments(argc, av);
  7468. return v;
  7469. }
  7470. VALUE
  7471. ivq_safelevel_handler(arg, ivq)
  7472. VALUE arg;
  7473. VALUE ivq;
  7474. {
  7475. struct invoke_queue *q;
  7476. Data_Get_Struct(ivq, struct invoke_queue, q);
  7477. DUMP2("(safe-level handler) $SAFE = %d", q->safe_level);
  7478. rb_set_safe_level(q->safe_level);
  7479. return ip_invoke_core(q->interp, q->argc, q->argv);
  7480. }
  7481. int invoke_queue_handler _((Tcl_Event *, int));
  7482. int
  7483. invoke_queue_handler(evPtr, flags)
  7484. Tcl_Event *evPtr;
  7485. int flags;
  7486. {
  7487. struct invoke_queue *q = (struct invoke_queue *)evPtr;
  7488. volatile VALUE ret;
  7489. volatile VALUE q_dat;
  7490. volatile VALUE thread = q->thread;
  7491. struct tcltkip *ptr;
  7492. DUMP2("do_invoke_queue_handler : evPtr = %p", evPtr);
  7493. DUMP2("invoke queue_thread : %lx", rb_thread_current());
  7494. DUMP2("added by thread : %lx", thread);
  7495. if (*(q->done)) {
  7496. DUMP1("processed by another event-loop");
  7497. return 0;
  7498. } else {
  7499. DUMP1("process it on current event-loop");
  7500. }
  7501. #ifdef RUBY_VM
  7502. if (RTEST(rb_funcall(thread, ID_alive_p, 0))
  7503. && ! RTEST(rb_funcall(thread, ID_stop_p, 0))) {
  7504. #else
  7505. if (RTEST(rb_thread_alive_p(thread))
  7506. && ! RTEST(rb_funcall(thread, ID_stop_p, 0))) {
  7507. #endif
  7508. DUMP1("caller is not yet ready to receive the result -> pending");
  7509. return 0;
  7510. }
  7511. /* process it */
  7512. *(q->done) = 1;
  7513. /* deleted ipterp ? */
  7514. ptr = get_ip(q->interp);
  7515. if (deleted_ip(ptr)) {
  7516. /* deleted IP --> ignore */
  7517. return 1;
  7518. }
  7519. /* incr internal handler mark */
  7520. rbtk_internal_eventloop_handler++;
  7521. /* check safe-level */
  7522. if (rb_safe_level() != q->safe_level) {
  7523. /* q_dat = Data_Wrap_Struct(rb_cData,0,0,q); */
  7524. q_dat = Data_Wrap_Struct(rb_cData,invoke_queue_mark,-1,q);
  7525. ret = rb_funcall(rb_proc_new(ivq_safelevel_handler, q_dat),
  7526. ID_call, 0);
  7527. rb_gc_force_recycle(q_dat);
  7528. q_dat = (VALUE)NULL;
  7529. } else {
  7530. DUMP2("call invoke_real (for caller thread:%lx)", thread);
  7531. DUMP2("call invoke_real (current thread:%lx)", rb_thread_current());
  7532. ret = ip_invoke_core(q->interp, q->argc, q->argv);
  7533. }
  7534. /* set result */
  7535. RARRAY_PTR(q->result)[0] = ret;
  7536. ret = (VALUE)NULL;
  7537. /* decr internal handler mark */
  7538. rbtk_internal_eventloop_handler--;
  7539. /* complete */
  7540. *(q->done) = -1;
  7541. /* unlink ruby objects */
  7542. q->interp = (VALUE)NULL;
  7543. q->result = (VALUE)NULL;
  7544. q->thread = (VALUE)NULL;
  7545. /* back to caller */
  7546. #ifdef RUBY_VM
  7547. if (RTEST(rb_funcall(thread, ID_alive_p, 0, 0))) {
  7548. #else
  7549. if (RTEST(rb_thread_alive_p(thread))) {
  7550. #endif
  7551. DUMP2("back to caller (caller thread:%lx)", thread);
  7552. DUMP2(" (current thread:%lx)", rb_thread_current());
  7553. #if CONTROL_BY_STATUS_OF_RB_THREAD_WAITING_FOR_VALUE
  7554. have_rb_thread_waiting_for_value = 1;
  7555. rb_thread_wakeup(thread);
  7556. #else
  7557. rb_thread_run(thread);
  7558. #endif
  7559. DUMP1("finish back to caller");
  7560. #if DO_THREAD_SCHEDULE_AT_CALLBACK_DONE
  7561. rb_thread_schedule();
  7562. #endif
  7563. } else {
  7564. DUMP2("caller is dead (caller thread:%lx)", thread);
  7565. DUMP2(" (current thread:%lx)", rb_thread_current());
  7566. }
  7567. /* end of handler : remove it */
  7568. return 1;
  7569. }
  7570. static VALUE
  7571. ip_invoke_with_position(argc, argv, obj, position)
  7572. int argc;
  7573. VALUE *argv;
  7574. VALUE obj;
  7575. Tcl_QueuePosition position;
  7576. {
  7577. struct invoke_queue *ivq;
  7578. #ifdef RUBY_USE_NATIVE_THREAD
  7579. struct tcltkip *ptr;
  7580. #endif
  7581. int *alloc_done;
  7582. int thr_crit_bup;
  7583. volatile VALUE current = rb_thread_current();
  7584. volatile VALUE ip_obj = obj;
  7585. volatile VALUE result;
  7586. volatile VALUE ret;
  7587. struct timeval t;
  7588. #if TCL_MAJOR_VERSION >= 8
  7589. Tcl_Obj **av = (Tcl_Obj **)NULL;
  7590. #else /* TCL_MAJOR_VERSION < 8 */
  7591. char **av = (char **)NULL;
  7592. #endif
  7593. if (argc < 1) {
  7594. rb_raise(rb_eArgError, "command name missing");
  7595. }
  7596. #ifdef RUBY_USE_NATIVE_THREAD
  7597. ptr = get_ip(ip_obj);
  7598. DUMP2("invoke status: ptr->tk_thread_id %p", ptr->tk_thread_id);
  7599. DUMP2("invoke status: Tcl_GetCurrentThread %p", Tcl_GetCurrentThread());
  7600. #else
  7601. DUMP2("status: Tcl_GetCurrentThread %p", Tcl_GetCurrentThread());
  7602. #endif
  7603. DUMP2("status: eventloopt_thread %lx", eventloop_thread);
  7604. if (
  7605. #ifdef RUBY_USE_NATIVE_THREAD
  7606. (ptr->tk_thread_id == 0 || ptr->tk_thread_id == Tcl_GetCurrentThread())
  7607. &&
  7608. #endif
  7609. (NIL_P(eventloop_thread) || current == eventloop_thread)
  7610. ) {
  7611. if (NIL_P(eventloop_thread)) {
  7612. DUMP2("invoke from thread:%lx but no eventloop", current);
  7613. } else {
  7614. DUMP2("invoke from current eventloop %lx", current);
  7615. }
  7616. result = ip_invoke_real(argc, argv, ip_obj);
  7617. if (rb_obj_is_kind_of(result, rb_eException)) {
  7618. rb_exc_raise(result);
  7619. }
  7620. return result;
  7621. }
  7622. DUMP2("invoke from thread %lx (NOT current eventloop)", current);
  7623. thr_crit_bup = rb_thread_critical;
  7624. rb_thread_critical = Qtrue;
  7625. /* allocate memory (for arguments) */
  7626. av = alloc_invoke_arguments(argc, argv);
  7627. /* allocate memory (keep result) */
  7628. /* alloc_done = (int*)ALLOC(int); */
  7629. alloc_done = (int*)ckalloc(sizeof(int));
  7630. #if 0 /* use Tcl_Preserve/Release */
  7631. Tcl_Preserve((ClientData)alloc_done); /* XXXXXXXX */
  7632. #endif
  7633. *alloc_done = 0;
  7634. /* allocate memory (freed by Tcl_ServiceEvent) */
  7635. /* ivq = (struct invoke_queue *)Tcl_Alloc(sizeof(struct invoke_queue)); */
  7636. ivq = (struct invoke_queue *)ckalloc(sizeof(struct invoke_queue));
  7637. #if 0 /* use Tcl_Preserve/Release */
  7638. Tcl_Preserve((ClientData)ivq); /* XXXXXXXX */
  7639. #endif
  7640. /* allocate result obj */
  7641. result = rb_ary_new3(1, Qnil);
  7642. /* construct event data */
  7643. ivq->done = alloc_done;
  7644. ivq->argc = argc;
  7645. ivq->argv = av;
  7646. ivq->interp = ip_obj;
  7647. ivq->result = result;
  7648. ivq->thread = current;
  7649. ivq->safe_level = rb_safe_level();
  7650. ivq->ev.proc = invoke_queue_handler;
  7651. /* add the handler to Tcl event queue */
  7652. DUMP1("add handler");
  7653. #ifdef RUBY_USE_NATIVE_THREAD
  7654. if (ptr->tk_thread_id) {
  7655. /* Tcl_ThreadQueueEvent(ptr->tk_thread_id, &(ivq->ev), position); */
  7656. Tcl_ThreadQueueEvent(ptr->tk_thread_id, (Tcl_Event*)ivq, position);
  7657. Tcl_ThreadAlert(ptr->tk_thread_id);
  7658. } else if (tk_eventloop_thread_id) {
  7659. /* Tcl_ThreadQueueEvent(tk_eventloop_thread_id,
  7660. &(ivq->ev), position); */
  7661. Tcl_ThreadQueueEvent(tk_eventloop_thread_id,
  7662. (Tcl_Event*)ivq, position);
  7663. Tcl_ThreadAlert(tk_eventloop_thread_id);
  7664. } else {
  7665. /* Tcl_QueueEvent(&(ivq->ev), position); */
  7666. Tcl_QueueEvent((Tcl_Event*)ivq, position);
  7667. }
  7668. #else
  7669. /* Tcl_QueueEvent(&(ivq->ev), position); */
  7670. Tcl_QueueEvent((Tcl_Event*)ivq, position);
  7671. #endif
  7672. rb_thread_critical = thr_crit_bup;
  7673. /* wait for the handler to be processed */
  7674. t.tv_sec = 0;
  7675. t.tv_usec = (long)((EVENT_HANDLER_TIMEOUT)*1000.0);
  7676. DUMP2("ivq wait for handler (current thread:%lx)", current);
  7677. while(*alloc_done >= 0) {
  7678. /* rb_thread_stop(); */
  7679. /* rb_thread_sleep_forever(); */
  7680. rb_thread_wait_for(t);
  7681. DUMP2("*** ivq wakeup (current thread:%lx)", current);
  7682. DUMP2("*** (eventloop thread:%lx)", eventloop_thread);
  7683. if (NIL_P(eventloop_thread)) {
  7684. DUMP1("*** ivq lost eventloop thread");
  7685. break;
  7686. }
  7687. }
  7688. DUMP2("back from handler (current thread:%lx)", current);
  7689. /* get result & free allocated memory */
  7690. ret = RARRAY_PTR(result)[0];
  7691. #if 0 /* use Tcl_EventuallyFree */
  7692. Tcl_EventuallyFree((ClientData)alloc_done, TCL_DYNAMIC); /* XXXXXXXX */
  7693. #else
  7694. #if 0 /* use Tcl_Preserve/Release */
  7695. Tcl_Release((ClientData)alloc_done); /* XXXXXXXX */
  7696. #else
  7697. /* free(alloc_done); */
  7698. ckfree((char*)alloc_done);
  7699. #endif
  7700. #endif
  7701. #if 0 /* ivq is freed by Tcl_ServiceEvent */
  7702. #if 0 /* use Tcl_EventuallyFree */
  7703. Tcl_EventuallyFree((ClientData)ivq, TCL_DYNAMIC); /* XXXXXXXX */
  7704. #else
  7705. #if 0 /* use Tcl_Preserve/Release */
  7706. Tcl_Release(ivq);
  7707. #else
  7708. ckfree((char*)ivq);
  7709. #endif
  7710. #endif
  7711. #endif
  7712. /* free allocated memory */
  7713. free_invoke_arguments(argc, av);
  7714. /* exception? */
  7715. if (rb_obj_is_kind_of(ret, rb_eException)) {
  7716. DUMP1("raise exception");
  7717. /* rb_exc_raise(ret); */
  7718. rb_exc_raise(rb_exc_new3(rb_obj_class(ret),
  7719. rb_funcall(ret, ID_to_s, 0, 0)));
  7720. }
  7721. DUMP1("exit ip_invoke");
  7722. return ret;
  7723. }
  7724. /* get return code from Tcl_Eval() */
  7725. static VALUE
  7726. ip_retval(self)
  7727. VALUE self;
  7728. {
  7729. struct tcltkip *ptr; /* tcltkip data struct */
  7730. /* get the data strcut */
  7731. ptr = get_ip(self);
  7732. /* ip is deleted? */
  7733. if (deleted_ip(ptr)) {
  7734. return rb_tainted_str_new2("");
  7735. }
  7736. return (INT2FIX(ptr->return_value));
  7737. }
  7738. static VALUE
  7739. ip_invoke(argc, argv, obj)
  7740. int argc;
  7741. VALUE *argv;
  7742. VALUE obj;
  7743. {
  7744. return ip_invoke_with_position(argc, argv, obj, TCL_QUEUE_TAIL);
  7745. }
  7746. static VALUE
  7747. ip_invoke_immediate(argc, argv, obj)
  7748. int argc;
  7749. VALUE *argv;
  7750. VALUE obj;
  7751. {
  7752. /* POTENTIALY INSECURE : can create infinite loop */
  7753. rb_secure(4);
  7754. return ip_invoke_with_position(argc, argv, obj, TCL_QUEUE_HEAD);
  7755. }
  7756. /* access Tcl variables */
  7757. static VALUE
  7758. ip_get_variable2_core(interp, argc, argv)
  7759. VALUE interp;
  7760. int argc;
  7761. VALUE *argv;
  7762. {
  7763. struct tcltkip *ptr = get_ip(interp);
  7764. int thr_crit_bup;
  7765. volatile VALUE varname, index, flag;
  7766. varname = argv[0];
  7767. index = argv[1];
  7768. flag = argv[2];
  7769. /*
  7770. StringValue(varname);
  7771. if (!NIL_P(index)) StringValue(index);
  7772. */
  7773. #if TCL_MAJOR_VERSION >= 8
  7774. {
  7775. Tcl_Obj *ret;
  7776. volatile VALUE strval;
  7777. thr_crit_bup = rb_thread_critical;
  7778. rb_thread_critical = Qtrue;
  7779. /* ip is deleted? */
  7780. if (deleted_ip(ptr)) {
  7781. rb_thread_critical = thr_crit_bup;
  7782. return rb_tainted_str_new2("");
  7783. } else {
  7784. /* Tcl_Preserve(ptr->ip); */
  7785. rbtk_preserve_ip(ptr);
  7786. ret = Tcl_GetVar2Ex(ptr->ip, RSTRING_PTR(varname),
  7787. NIL_P(index) ? NULL : RSTRING_PTR(index),
  7788. FIX2INT(flag));
  7789. }
  7790. if (ret == (Tcl_Obj*)NULL) {
  7791. volatile VALUE exc;
  7792. /* exc = rb_exc_new2(rb_eRuntimeError,
  7793. Tcl_GetStringResult(ptr->ip)); */
  7794. exc = create_ip_exc(interp, rb_eRuntimeError,
  7795. Tcl_GetStringResult(ptr->ip));
  7796. /* Tcl_Release(ptr->ip); */
  7797. rbtk_release_ip(ptr);
  7798. rb_thread_critical = thr_crit_bup;
  7799. return exc;
  7800. }
  7801. Tcl_IncrRefCount(ret);
  7802. strval = get_str_from_obj(ret);
  7803. RbTk_OBJ_UNTRUST(strval);
  7804. Tcl_DecrRefCount(ret);
  7805. /* Tcl_Release(ptr->ip); */
  7806. rbtk_release_ip(ptr);
  7807. rb_thread_critical = thr_crit_bup;
  7808. return(strval);
  7809. }
  7810. #else /* TCL_MAJOR_VERSION < 8 */
  7811. {
  7812. char *ret;
  7813. volatile VALUE strval;
  7814. /* ip is deleted? */
  7815. if (deleted_ip(ptr)) {
  7816. return rb_tainted_str_new2("");
  7817. } else {
  7818. /* Tcl_Preserve(ptr->ip); */
  7819. rbtk_preserve_ip(ptr);
  7820. ret = Tcl_GetVar2(ptr->ip, RSTRING_PTR(varname),
  7821. NIL_P(index) ? NULL : RSTRING_PTR(index),
  7822. FIX2INT(flag));
  7823. }
  7824. if (ret == (char*)NULL) {
  7825. volatile VALUE exc;
  7826. exc = rb_exc_new2(rb_eRuntimeError, Tcl_GetStringResult(ptr->ip));
  7827. /* Tcl_Release(ptr->ip); */
  7828. rbtk_release_ip(ptr);
  7829. rb_thread_critical = thr_crit_bup;
  7830. return exc;
  7831. }
  7832. strval = rb_tainted_str_new2(ret);
  7833. /* Tcl_Release(ptr->ip); */
  7834. rbtk_release_ip(ptr);
  7835. rb_thread_critical = thr_crit_bup;
  7836. return(strval);
  7837. }
  7838. #endif
  7839. }
  7840. static VALUE
  7841. ip_get_variable2(self, varname, index, flag)
  7842. VALUE self;
  7843. VALUE varname;
  7844. VALUE index;
  7845. VALUE flag;
  7846. {
  7847. VALUE argv[3];
  7848. VALUE retval;
  7849. StringValue(varname);
  7850. if (!NIL_P(index)) StringValue(index);
  7851. argv[0] = varname;
  7852. argv[1] = index;
  7853. argv[2] = flag;
  7854. retval = tk_funcall(ip_get_variable2_core, 3, argv, self);
  7855. if (NIL_P(retval)) {
  7856. return rb_tainted_str_new2("");
  7857. } else {
  7858. return retval;
  7859. }
  7860. }
  7861. static VALUE
  7862. ip_get_variable(self, varname, flag)
  7863. VALUE self;
  7864. VALUE varname;
  7865. VALUE flag;
  7866. {
  7867. return ip_get_variable2(self, varname, Qnil, flag);
  7868. }
  7869. static VALUE
  7870. ip_set_variable2_core(interp, argc, argv)
  7871. VALUE interp;
  7872. int argc;
  7873. VALUE *argv;
  7874. {
  7875. struct tcltkip *ptr = get_ip(interp);
  7876. int thr_crit_bup;
  7877. volatile VALUE varname, index, value, flag;
  7878. varname = argv[0];
  7879. index = argv[1];
  7880. value = argv[2];
  7881. flag = argv[3];
  7882. /*
  7883. StringValue(varname);
  7884. if (!NIL_P(index)) StringValue(index);
  7885. StringValue(value);
  7886. */
  7887. #if TCL_MAJOR_VERSION >= 8
  7888. {
  7889. Tcl_Obj *valobj, *ret;
  7890. volatile VALUE strval;
  7891. thr_crit_bup = rb_thread_critical;
  7892. rb_thread_critical = Qtrue;
  7893. valobj = get_obj_from_str(value);
  7894. Tcl_IncrRefCount(valobj);
  7895. /* ip is deleted? */
  7896. if (deleted_ip(ptr)) {
  7897. Tcl_DecrRefCount(valobj);
  7898. rb_thread_critical = thr_crit_bup;
  7899. return rb_tainted_str_new2("");
  7900. } else {
  7901. /* Tcl_Preserve(ptr->ip); */
  7902. rbtk_preserve_ip(ptr);
  7903. ret = Tcl_SetVar2Ex(ptr->ip, RSTRING_PTR(varname),
  7904. NIL_P(index) ? NULL : RSTRING_PTR(index),
  7905. valobj, FIX2INT(flag));
  7906. }
  7907. Tcl_DecrRefCount(valobj);
  7908. if (ret == (Tcl_Obj*)NULL) {
  7909. volatile VALUE exc;
  7910. /* exc = rb_exc_new2(rb_eRuntimeError,
  7911. Tcl_GetStringResult(ptr->ip)); */
  7912. exc = create_ip_exc(interp, rb_eRuntimeError,
  7913. Tcl_GetStringResult(ptr->ip));
  7914. /* Tcl_Release(ptr->ip); */
  7915. rbtk_release_ip(ptr);
  7916. rb_thread_critical = thr_crit_bup;
  7917. return exc;
  7918. }
  7919. Tcl_IncrRefCount(ret);
  7920. strval = get_str_from_obj(ret);
  7921. RbTk_OBJ_UNTRUST(strval);
  7922. Tcl_DecrRefCount(ret);
  7923. /* Tcl_Release(ptr->ip); */
  7924. rbtk_release_ip(ptr);
  7925. rb_thread_critical = thr_crit_bup;
  7926. return(strval);
  7927. }
  7928. #else /* TCL_MAJOR_VERSION < 8 */
  7929. {
  7930. CONST char *ret;
  7931. volatile VALUE strval;
  7932. /* ip is deleted? */
  7933. if (deleted_ip(ptr)) {
  7934. return rb_tainted_str_new2("");
  7935. } else {
  7936. /* Tcl_Preserve(ptr->ip); */
  7937. rbtk_preserve_ip(ptr);
  7938. ret = Tcl_SetVar2(ptr->ip, RSTRING_PTR(varname),
  7939. NIL_P(index) ? NULL : RSTRING_PTR(index),
  7940. RSTRING_PTR(value), FIX2INT(flag));
  7941. }
  7942. if (ret == (char*)NULL) {
  7943. return rb_exc_new2(rb_eRuntimeError, ptr->ip->result);
  7944. }
  7945. strval = rb_tainted_str_new2(ret);
  7946. /* Tcl_Release(ptr->ip); */
  7947. rbtk_release_ip(ptr);
  7948. rb_thread_critical = thr_crit_bup;
  7949. return(strval);
  7950. }
  7951. #endif
  7952. }
  7953. static VALUE
  7954. ip_set_variable2(self, varname, index, value, flag)
  7955. VALUE self;
  7956. VALUE varname;
  7957. VALUE index;
  7958. VALUE value;
  7959. VALUE flag;
  7960. {
  7961. VALUE argv[4];
  7962. VALUE retval;
  7963. StringValue(varname);
  7964. if (!NIL_P(index)) StringValue(index);
  7965. StringValue(value);
  7966. argv[0] = varname;
  7967. argv[1] = index;
  7968. argv[2] = value;
  7969. argv[3] = flag;
  7970. retval = tk_funcall(ip_set_variable2_core, 4, argv, self);
  7971. if (NIL_P(retval)) {
  7972. return rb_tainted_str_new2("");
  7973. } else {
  7974. return retval;
  7975. }
  7976. }
  7977. static VALUE
  7978. ip_set_variable(self, varname, value, flag)
  7979. VALUE self;
  7980. VALUE varname;
  7981. VALUE value;
  7982. VALUE flag;
  7983. {
  7984. return ip_set_variable2(self, varname, Qnil, value, flag);
  7985. }
  7986. static VALUE
  7987. ip_unset_variable2_core(interp, argc, argv)
  7988. VALUE interp;
  7989. int argc;
  7990. VALUE *argv;
  7991. {
  7992. struct tcltkip *ptr = get_ip(interp);
  7993. volatile VALUE varname, index, flag;
  7994. varname = argv[0];
  7995. index = argv[1];
  7996. flag = argv[2];
  7997. /*
  7998. StringValue(varname);
  7999. if (!NIL_P(index)) StringValue(index);
  8000. */
  8001. /* ip is deleted? */
  8002. if (deleted_ip(ptr)) {
  8003. return Qtrue;
  8004. }
  8005. ptr->return_value = Tcl_UnsetVar2(ptr->ip, RSTRING_PTR(varname),
  8006. NIL_P(index) ? NULL : RSTRING_PTR(index),
  8007. FIX2INT(flag));
  8008. if (ptr->return_value == TCL_ERROR) {
  8009. if (FIX2INT(flag) & TCL_LEAVE_ERR_MSG) {
  8010. /* return rb_exc_new2(rb_eRuntimeError,
  8011. Tcl_GetStringResult(ptr->ip)); */
  8012. return create_ip_exc(interp, rb_eRuntimeError,
  8013. Tcl_GetStringResult(ptr->ip));
  8014. }
  8015. return Qfalse;
  8016. }
  8017. return Qtrue;
  8018. }
  8019. static VALUE
  8020. ip_unset_variable2(self, varname, index, flag)
  8021. VALUE self;
  8022. VALUE varname;
  8023. VALUE index;
  8024. VALUE flag;
  8025. {
  8026. VALUE argv[3];
  8027. VALUE retval;
  8028. StringValue(varname);
  8029. if (!NIL_P(index)) StringValue(index);
  8030. argv[0] = varname;
  8031. argv[1] = index;
  8032. argv[2] = flag;
  8033. retval = tk_funcall(ip_unset_variable2_core, 3, argv, self);
  8034. if (NIL_P(retval)) {
  8035. return rb_tainted_str_new2("");
  8036. } else {
  8037. return retval;
  8038. }
  8039. }
  8040. static VALUE
  8041. ip_unset_variable(self, varname, flag)
  8042. VALUE self;
  8043. VALUE varname;
  8044. VALUE flag;
  8045. {
  8046. return ip_unset_variable2(self, varname, Qnil, flag);
  8047. }
  8048. static VALUE
  8049. ip_get_global_var(self, varname)
  8050. VALUE self;
  8051. VALUE varname;
  8052. {
  8053. return ip_get_variable(self, varname,
  8054. INT2FIX(TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG));
  8055. }
  8056. static VALUE
  8057. ip_get_global_var2(self, varname, index)
  8058. VALUE self;
  8059. VALUE varname;
  8060. VALUE index;
  8061. {
  8062. return ip_get_variable2(self, varname, index,
  8063. INT2FIX(TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG));
  8064. }
  8065. static VALUE
  8066. ip_set_global_var(self, varname, value)
  8067. VALUE self;
  8068. VALUE varname;
  8069. VALUE value;
  8070. {
  8071. return ip_set_variable(self, varname, value,
  8072. INT2FIX(TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG));
  8073. }
  8074. static VALUE
  8075. ip_set_global_var2(self, varname, index, value)
  8076. VALUE self;
  8077. VALUE varname;
  8078. VALUE index;
  8079. VALUE value;
  8080. {
  8081. return ip_set_variable2(self, varname, index, value,
  8082. INT2FIX(TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG));
  8083. }
  8084. static VALUE
  8085. ip_unset_global_var(self, varname)
  8086. VALUE self;
  8087. VALUE varname;
  8088. {
  8089. return ip_unset_variable(self, varname,
  8090. INT2FIX(TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG));
  8091. }
  8092. static VALUE
  8093. ip_unset_global_var2(self, varname, index)
  8094. VALUE self;
  8095. VALUE varname;
  8096. VALUE index;
  8097. {
  8098. return ip_unset_variable2(self, varname, index,
  8099. INT2FIX(TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG));
  8100. }
  8101. /* treat Tcl_List */
  8102. static VALUE
  8103. lib_split_tklist_core(ip_obj, list_str)
  8104. VALUE ip_obj;
  8105. VALUE list_str;
  8106. {
  8107. Tcl_Interp *interp;
  8108. volatile VALUE ary, elem;
  8109. int idx;
  8110. int taint_flag = OBJ_TAINTED(list_str);
  8111. #ifdef HAVE_RUBY_ENCODING_H
  8112. int list_enc_idx;
  8113. volatile VALUE list_ivar_enc;
  8114. #endif
  8115. int result;
  8116. VALUE old_gc;
  8117. tcl_stubs_check();
  8118. if (NIL_P(ip_obj)) {
  8119. interp = (Tcl_Interp *)NULL;
  8120. } else if (get_ip(ip_obj) == (struct tcltkip *)NULL) {
  8121. interp = (Tcl_Interp *)NULL;
  8122. } else {
  8123. interp = get_ip(ip_obj)->ip;
  8124. }
  8125. StringValue(list_str);
  8126. #ifdef HAVE_RUBY_ENCODING_H
  8127. list_enc_idx = rb_enc_get_index(list_str);
  8128. list_ivar_enc = rb_ivar_get(list_str, ID_at_enc);
  8129. #endif
  8130. {
  8131. #if TCL_MAJOR_VERSION >= 8
  8132. /* object style interface */
  8133. Tcl_Obj *listobj;
  8134. int objc;
  8135. Tcl_Obj **objv;
  8136. int thr_crit_bup;
  8137. listobj = get_obj_from_str(list_str);
  8138. Tcl_IncrRefCount(listobj);
  8139. result = Tcl_ListObjGetElements(interp, listobj, &objc, &objv);
  8140. if (result == TCL_ERROR) {
  8141. Tcl_DecrRefCount(listobj);
  8142. if (interp == (Tcl_Interp*)NULL) {
  8143. rb_raise(rb_eRuntimeError, "can't get elements from list");
  8144. } else {
  8145. rb_raise(rb_eRuntimeError, "%s", Tcl_GetStringResult(interp));
  8146. }
  8147. }
  8148. for(idx = 0; idx < objc; idx++) {
  8149. Tcl_IncrRefCount(objv[idx]);
  8150. }
  8151. thr_crit_bup = rb_thread_critical;
  8152. rb_thread_critical = Qtrue;
  8153. ary = rb_ary_new2(objc);
  8154. if (taint_flag) RbTk_OBJ_UNTRUST(ary);
  8155. old_gc = rb_gc_disable();
  8156. for(idx = 0; idx < objc; idx++) {
  8157. elem = get_str_from_obj(objv[idx]);
  8158. if (taint_flag) RbTk_OBJ_UNTRUST(elem);
  8159. #ifdef HAVE_RUBY_ENCODING_H
  8160. if (rb_enc_get_index(elem) == ENCODING_INDEX_BINARY) {
  8161. rb_enc_associate_index(elem, ENCODING_INDEX_BINARY);
  8162. rb_ivar_set(elem, ID_at_enc, ENCODING_NAME_BINARY);
  8163. } else {
  8164. rb_enc_associate_index(elem, list_enc_idx);
  8165. rb_ivar_set(elem, ID_at_enc, list_ivar_enc);
  8166. }
  8167. #endif
  8168. /* RARRAY(ary)->ptr[idx] = elem; */
  8169. rb_ary_push(ary, elem);
  8170. }
  8171. /* RARRAY(ary)->len = objc; */
  8172. if (old_gc == Qfalse) rb_gc_enable();
  8173. rb_thread_critical = thr_crit_bup;
  8174. for(idx = 0; idx < objc; idx++) {
  8175. Tcl_DecrRefCount(objv[idx]);
  8176. }
  8177. Tcl_DecrRefCount(listobj);
  8178. #else /* TCL_MAJOR_VERSION < 8 */
  8179. /* string style interface */
  8180. int argc;
  8181. char **argv;
  8182. if (Tcl_SplitList(interp, RSTRING_PTR(list_str),
  8183. &argc, &argv) == TCL_ERROR) {
  8184. if (interp == (Tcl_Interp*)NULL) {
  8185. rb_raise(rb_eRuntimeError, "can't get elements from list");
  8186. } else {
  8187. rb_raise(rb_eRuntimeError, "%s", interp->result);
  8188. }
  8189. }
  8190. ary = rb_ary_new2(argc);
  8191. if (taint_flag) RbTk_OBJ_UNTRUST(ary);
  8192. old_gc = rb_gc_disable();
  8193. for(idx = 0; idx < argc; idx++) {
  8194. if (taint_flag) {
  8195. elem = rb_tainted_str_new2(argv[idx]);
  8196. } else {
  8197. elem = rb_str_new2(argv[idx]);
  8198. }
  8199. /* rb_ivar_set(elem, ID_at_enc, rb_str_new2("binary")); */
  8200. /* RARRAY(ary)->ptr[idx] = elem; */
  8201. rb_ary_push(ary, elem)
  8202. }
  8203. /* RARRAY(ary)->len = argc; */
  8204. if (old_gc == Qfalse) rb_gc_enable();
  8205. #endif
  8206. }
  8207. return ary;
  8208. }
  8209. static VALUE
  8210. lib_split_tklist(self, list_str)
  8211. VALUE self;
  8212. VALUE list_str;
  8213. {
  8214. return lib_split_tklist_core(Qnil, list_str);
  8215. }
  8216. static VALUE
  8217. ip_split_tklist(self, list_str)
  8218. VALUE self;
  8219. VALUE list_str;
  8220. {
  8221. return lib_split_tklist_core(self, list_str);
  8222. }
  8223. static VALUE
  8224. lib_merge_tklist(argc, argv, obj)
  8225. int argc;
  8226. VALUE *argv;
  8227. VALUE obj;
  8228. {
  8229. int num, len;
  8230. int *flagPtr;
  8231. char *dst, *result;
  8232. volatile VALUE str;
  8233. int taint_flag = 0;
  8234. int thr_crit_bup;
  8235. VALUE old_gc;
  8236. if (argc == 0) return rb_str_new2("");
  8237. tcl_stubs_check();
  8238. thr_crit_bup = rb_thread_critical;
  8239. rb_thread_critical = Qtrue;
  8240. old_gc = rb_gc_disable();
  8241. /* based on Tcl/Tk's Tcl_Merge() */
  8242. /* flagPtr = ALLOC_N(int, argc); */
  8243. flagPtr = (int *)ckalloc(sizeof(int) * argc);
  8244. #if 0 /* use Tcl_Preserve/Release */
  8245. Tcl_Preserve((ClientData)flagPtr); /* XXXXXXXXXX */
  8246. #endif
  8247. /* pass 1 */
  8248. len = 1;
  8249. for(num = 0; num < argc; num++) {
  8250. if (OBJ_TAINTED(argv[num])) taint_flag = 1;
  8251. dst = StringValuePtr(argv[num]);
  8252. #if TCL_MAJOR_VERSION >= 8
  8253. len += Tcl_ScanCountedElement(dst, RSTRING_LEN(argv[num]),
  8254. &flagPtr[num]) + 1;
  8255. #else /* TCL_MAJOR_VERSION < 8 */
  8256. len += Tcl_ScanElement(dst, &flagPtr[num]) + 1;
  8257. #endif
  8258. }
  8259. /* pass 2 */
  8260. /* result = (char *)Tcl_Alloc(len); */
  8261. result = (char *)ckalloc(len);
  8262. #if 0 /* use Tcl_Preserve/Release */
  8263. Tcl_Preserve((ClientData)result);
  8264. #endif
  8265. dst = result;
  8266. for(num = 0; num < argc; num++) {
  8267. #if TCL_MAJOR_VERSION >= 8
  8268. len = Tcl_ConvertCountedElement(RSTRING_PTR(argv[num]),
  8269. RSTRING_LEN(argv[num]),
  8270. dst, flagPtr[num]);
  8271. #else /* TCL_MAJOR_VERSION < 8 */
  8272. len = Tcl_ConvertElement(RSTRING_PTR(argv[num]), dst, flagPtr[num]);
  8273. #endif
  8274. dst += len;
  8275. *dst = ' ';
  8276. dst++;
  8277. }
  8278. if (dst == result) {
  8279. *dst = 0;
  8280. } else {
  8281. dst[-1] = 0;
  8282. }
  8283. #if 0 /* use Tcl_EventuallyFree */
  8284. Tcl_EventuallyFree((ClientData)flagPtr, TCL_DYNAMIC); /* XXXXXXXX */
  8285. #else
  8286. #if 0 /* use Tcl_Preserve/Release */
  8287. Tcl_Release((ClientData)flagPtr);
  8288. #else
  8289. /* free(flagPtr); */
  8290. ckfree((char*)flagPtr);
  8291. #endif
  8292. #endif
  8293. /* create object */
  8294. str = rb_str_new(result, dst - result - 1);
  8295. if (taint_flag) RbTk_OBJ_UNTRUST(str);
  8296. #if 0 /* use Tcl_EventuallyFree */
  8297. Tcl_EventuallyFree((ClientData)result, TCL_DYNAMIC); /* XXXXXXXX */
  8298. #else
  8299. #if 0 /* use Tcl_Preserve/Release */
  8300. Tcl_Release((ClientData)result); /* XXXXXXXXXXX */
  8301. #else
  8302. /* Tcl_Free(result); */
  8303. ckfree(result);
  8304. #endif
  8305. #endif
  8306. if (old_gc == Qfalse) rb_gc_enable();
  8307. rb_thread_critical = thr_crit_bup;
  8308. return str;
  8309. }
  8310. static VALUE
  8311. lib_conv_listelement(self, src)
  8312. VALUE self;
  8313. VALUE src;
  8314. {
  8315. int len, scan_flag;
  8316. volatile VALUE dst;
  8317. int taint_flag = OBJ_TAINTED(src);
  8318. int thr_crit_bup;
  8319. tcl_stubs_check();
  8320. thr_crit_bup = rb_thread_critical;
  8321. rb_thread_critical = Qtrue;
  8322. StringValue(src);
  8323. #if TCL_MAJOR_VERSION >= 8
  8324. len = Tcl_ScanCountedElement(RSTRING_PTR(src), RSTRING_LEN(src),
  8325. &scan_flag);
  8326. dst = rb_str_new(0, len + 1);
  8327. len = Tcl_ConvertCountedElement(RSTRING_PTR(src), RSTRING_LEN(src),
  8328. RSTRING_PTR(dst), scan_flag);
  8329. #else /* TCL_MAJOR_VERSION < 8 */
  8330. len = Tcl_ScanElement(RSTRING_PTR(src), &scan_flag);
  8331. dst = rb_str_new(0, len + 1);
  8332. len = Tcl_ConvertElement(RSTRING_PTR(src), RSTRING_PTR(dst), scan_flag);
  8333. #endif
  8334. rb_str_resize(dst, len);
  8335. if (taint_flag) RbTk_OBJ_UNTRUST(dst);
  8336. rb_thread_critical = thr_crit_bup;
  8337. return dst;
  8338. }
  8339. static VALUE
  8340. lib_getversion(self)
  8341. VALUE self;
  8342. {
  8343. set_tcltk_version();
  8344. return rb_ary_new3(4, INT2NUM(tcltk_version.major),
  8345. INT2NUM(tcltk_version.minor),
  8346. INT2NUM(tcltk_version.type),
  8347. INT2NUM(tcltk_version.patchlevel));
  8348. }
  8349. static VALUE
  8350. lib_get_reltype_name(self)
  8351. VALUE self;
  8352. {
  8353. set_tcltk_version();
  8354. switch(tcltk_version.type) {
  8355. case TCL_ALPHA_RELEASE:
  8356. return rb_str_new2("alpha");
  8357. case TCL_BETA_RELEASE:
  8358. return rb_str_new2("beta");
  8359. case TCL_FINAL_RELEASE:
  8360. return rb_str_new2("final");
  8361. default:
  8362. rb_raise(rb_eRuntimeError, "tcltklib has invalid release type number");
  8363. }
  8364. }
  8365. static VALUE
  8366. tcltklib_compile_info()
  8367. {
  8368. volatile VALUE ret;
  8369. int size;
  8370. char form[]
  8371. = "tcltklib %s :: Ruby%s (%s) %s pthread :: Tcl%s(%s)/Tk%s(%s) %s";
  8372. char *info;
  8373. size = strlen(form)
  8374. + strlen(TCLTKLIB_RELEASE_DATE)
  8375. + strlen(RUBY_VERSION)
  8376. + strlen(RUBY_RELEASE_DATE)
  8377. + strlen("without")
  8378. + strlen(TCL_PATCH_LEVEL)
  8379. + strlen("without stub")
  8380. + strlen(TK_PATCH_LEVEL)
  8381. + strlen("without stub")
  8382. + strlen("unknown tcl_threads");
  8383. info = ALLOC_N(char, size);
  8384. /* info = ckalloc(sizeof(char) * size); */ /* SEGV */
  8385. sprintf(info, form,
  8386. TCLTKLIB_RELEASE_DATE,
  8387. RUBY_VERSION, RUBY_RELEASE_DATE,
  8388. #ifdef HAVE_NATIVETHREAD
  8389. "with",
  8390. #else
  8391. "without",
  8392. #endif
  8393. TCL_PATCH_LEVEL,
  8394. #ifdef USE_TCL_STUBS
  8395. "with stub",
  8396. #else
  8397. "without stub",
  8398. #endif
  8399. TK_PATCH_LEVEL,
  8400. #ifdef USE_TK_STUBS
  8401. "with stub",
  8402. #else
  8403. "without stub",
  8404. #endif
  8405. #ifdef WITH_TCL_ENABLE_THREAD
  8406. # if WITH_TCL_ENABLE_THREAD
  8407. "with tcl_threads"
  8408. # else
  8409. "without tcl_threads"
  8410. # endif
  8411. #else
  8412. "unknown tcl_threads"
  8413. #endif
  8414. );
  8415. ret = rb_obj_freeze(rb_str_new2(info));
  8416. xfree(info);
  8417. /* ckfree(info); */
  8418. return ret;
  8419. }
  8420. /*###############################################*/
  8421. static VALUE
  8422. create_dummy_encoding_for_tk_core(interp, name, error_mode)
  8423. VALUE interp;
  8424. VALUE name;
  8425. VALUE error_mode;
  8426. {
  8427. get_ip(interp);
  8428. rb_secure(4);
  8429. StringValue(name);
  8430. #if TCL_MAJOR_VERSION > 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION >= 1)
  8431. if (Tcl_GetEncoding((Tcl_Interp*)NULL, RSTRING_PTR(name)) == (Tcl_Encoding)NULL) {
  8432. if (RTEST(error_mode)) {
  8433. rb_raise(rb_eArgError, "invalid Tk encoding name '%s'",
  8434. RSTRING_PTR(name));
  8435. } else {
  8436. return Qnil;
  8437. }
  8438. }
  8439. #endif
  8440. #ifdef HAVE_RUBY_ENCODING_H
  8441. if (RTEST(rb_define_dummy_encoding(RSTRING_PTR(name)))) {
  8442. int idx = rb_enc_find_index(StringValueCStr(name));
  8443. return rb_enc_from_encoding(rb_enc_from_index(idx));
  8444. } else {
  8445. if (RTEST(error_mode)) {
  8446. rb_raise(rb_eRuntimeError, "fail to create dummy encoding for '%s'",
  8447. RSTRING_PTR(name));
  8448. } else {
  8449. return Qnil;
  8450. }
  8451. }
  8452. #else
  8453. return name;
  8454. #endif
  8455. }
  8456. static VALUE
  8457. create_dummy_encoding_for_tk(interp, name)
  8458. VALUE interp;
  8459. VALUE name;
  8460. {
  8461. return create_dummy_encoding_for_tk_core(interp, name, Qtrue);
  8462. }
  8463. #ifdef HAVE_RUBY_ENCODING_H
  8464. static int
  8465. update_encoding_table(table, interp, error_mode)
  8466. VALUE table;
  8467. VALUE interp;
  8468. VALUE error_mode;
  8469. {
  8470. struct tcltkip *ptr;
  8471. int retry = 0;
  8472. int i, idx, objc;
  8473. Tcl_Obj **objv;
  8474. Tcl_Obj *enc_list;
  8475. volatile VALUE encname = Qnil;
  8476. volatile VALUE encobj = Qnil;
  8477. /* interpreter check */
  8478. if (NIL_P(interp)) return 0;
  8479. ptr = get_ip(interp);
  8480. if (ptr == (struct tcltkip *) NULL) return 0;
  8481. if (deleted_ip(ptr)) return 0;
  8482. /* get Tcl's encoding list */
  8483. Tcl_GetEncodingNames(ptr->ip);
  8484. enc_list = Tcl_GetObjResult(ptr->ip);
  8485. Tcl_IncrRefCount(enc_list);
  8486. if (Tcl_ListObjGetElements(ptr->ip, enc_list,
  8487. &objc, &objv) != TCL_OK) {
  8488. Tcl_DecrRefCount(enc_list);
  8489. /* rb_raise(rb_eRuntimeError, "failt to get Tcl's encoding names");*/
  8490. return 0;
  8491. }
  8492. /* check each encoding name */
  8493. for(i = 0; i < objc; i++) {
  8494. encname = rb_str_new2(Tcl_GetString(objv[i]));
  8495. if (NIL_P(rb_hash_lookup(table, encname))) {
  8496. /* new Tk encoding -> add to table */
  8497. idx = rb_enc_find_index(StringValueCStr(encname));
  8498. if (idx < 0) {
  8499. encobj = create_dummy_encoding_for_tk_core(interp,encname,error_mode);
  8500. } else {
  8501. encobj = rb_enc_from_encoding(rb_enc_from_index(idx));
  8502. }
  8503. encname = rb_obj_freeze(encname);
  8504. rb_hash_aset(table, encname, encobj);
  8505. if (!NIL_P(encobj) && NIL_P(rb_hash_lookup(table, encobj))) {
  8506. rb_hash_aset(table, encobj, encname);
  8507. }
  8508. retry = 1;
  8509. }
  8510. }
  8511. Tcl_DecrRefCount(enc_list);
  8512. return retry;
  8513. }
  8514. static VALUE
  8515. encoding_table_get_name_core(table, enc_arg, error_mode)
  8516. VALUE table;
  8517. VALUE enc_arg;
  8518. VALUE error_mode;
  8519. {
  8520. volatile VALUE enc = enc_arg;
  8521. volatile VALUE name = Qnil;
  8522. volatile VALUE tmp = Qnil;
  8523. volatile VALUE interp = rb_ivar_get(table, ID_at_interp);
  8524. struct tcltkip *ptr = (struct tcltkip *) NULL;
  8525. int idx;
  8526. /* deleted interp ? */
  8527. if (!NIL_P(interp)) {
  8528. ptr = get_ip(interp);
  8529. if (deleted_ip(ptr)) {
  8530. ptr = (struct tcltkip *) NULL;
  8531. }
  8532. }
  8533. /* encoding argument check */
  8534. /* 1st: default encoding setting of interp */
  8535. if (ptr && NIL_P(enc)) {
  8536. if (rb_respond_to(interp, ID_encoding_name)) {
  8537. enc = rb_funcall(interp, ID_encoding_name, 0, 0);
  8538. }
  8539. }
  8540. /* 2nd: Encoding.default_internal */
  8541. if (NIL_P(enc)) {
  8542. enc = rb_enc_default_internal();
  8543. }
  8544. /* 3rd: encoding system of Tcl/Tk */
  8545. if (NIL_P(enc)) {
  8546. enc = rb_str_new2(Tcl_GetEncodingName((Tcl_Encoding)NULL));
  8547. }
  8548. /* 4th: Encoding.default_external */
  8549. if (NIL_P(enc)) {
  8550. enc = rb_enc_default_external();
  8551. }
  8552. /* 5th: Encoding.locale_charmap */
  8553. if (NIL_P(enc)) {
  8554. enc = rb_locale_charmap(rb_cEncoding);
  8555. }
  8556. if (RTEST(rb_obj_is_kind_of(enc, cRubyEncoding))) {
  8557. /* Ruby's Encoding object */
  8558. name = rb_hash_lookup(table, enc);
  8559. if (!NIL_P(name)) {
  8560. /* find */
  8561. return name;
  8562. }
  8563. /* is it new ? */
  8564. /* update check of Tk encoding names */
  8565. if (update_encoding_table(table, interp, error_mode)) {
  8566. /* add new relations to the table */
  8567. /* RETRY: registered Ruby encoding? */
  8568. name = rb_hash_lookup(table, enc);
  8569. if (!NIL_P(name)) {
  8570. /* find */
  8571. return name;
  8572. }
  8573. }
  8574. /* fail to find */
  8575. } else {
  8576. /* String or Symbol? */
  8577. name = rb_funcall(enc, ID_to_s, 0, 0);
  8578. if (!NIL_P(rb_hash_lookup(table, name))) {
  8579. /* find */
  8580. return name;
  8581. }
  8582. /* is it new ? */
  8583. idx = rb_enc_find_index(StringValueCStr(name));
  8584. if (idx >= 0) {
  8585. enc = rb_enc_from_encoding(rb_enc_from_index(idx));
  8586. /* registered Ruby encoding? */
  8587. tmp = rb_hash_lookup(table, enc);
  8588. if (!NIL_P(tmp)) {
  8589. /* find */
  8590. return tmp;
  8591. }
  8592. /* update check of Tk encoding names */
  8593. if (update_encoding_table(table, interp, error_mode)) {
  8594. /* add new relations to the table */
  8595. /* RETRY: registered Ruby encoding? */
  8596. tmp = rb_hash_lookup(table, enc);
  8597. if (!NIL_P(tmp)) {
  8598. /* find */
  8599. return tmp;
  8600. }
  8601. }
  8602. }
  8603. /* fail to find */
  8604. }
  8605. if (RTEST(error_mode)) {
  8606. enc = rb_funcall(enc_arg, ID_to_s, 0, 0);
  8607. rb_raise(rb_eArgError, "unsupported Tk encoding '%s'", RSTRING_PTR(enc));
  8608. }
  8609. return Qnil;
  8610. }
  8611. static VALUE
  8612. encoding_table_get_obj_core(table, enc, error_mode)
  8613. VALUE table;
  8614. VALUE enc;
  8615. VALUE error_mode;
  8616. {
  8617. volatile VALUE obj = Qnil;
  8618. obj = rb_hash_lookup(table,
  8619. encoding_table_get_name_core(table, enc, error_mode));
  8620. if (RTEST(rb_obj_is_kind_of(obj, cRubyEncoding))) {
  8621. return obj;
  8622. } else {
  8623. return Qnil;
  8624. }
  8625. }
  8626. #else /* ! HAVE_RUBY_ENCODING_H */
  8627. #if TCL_MAJOR_VERSION > 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION >= 1)
  8628. static int
  8629. update_encoding_table(table, interp, error_mode)
  8630. VALUE table;
  8631. VALUE interp;
  8632. VALUE error_mode;
  8633. {
  8634. struct tcltkip *ptr;
  8635. int retry = 0;
  8636. int i, objc;
  8637. Tcl_Obj **objv;
  8638. Tcl_Obj *enc_list;
  8639. volatile VALUE encname = Qnil;
  8640. /* interpreter check */
  8641. if (NIL_P(interp)) return 0;
  8642. ptr = get_ip(interp);
  8643. if (ptr == (struct tcltkip *) NULL) return 0;
  8644. if (deleted_ip(ptr)) return 0;
  8645. /* get Tcl's encoding list */
  8646. Tcl_GetEncodingNames(ptr->ip);
  8647. enc_list = Tcl_GetObjResult(ptr->ip);
  8648. Tcl_IncrRefCount(enc_list);
  8649. if (Tcl_ListObjGetElements(ptr->ip, enc_list, &objc, &objv) != TCL_OK) {
  8650. Tcl_DecrRefCount(enc_list);
  8651. /* rb_raise(rb_eRuntimeError, "failt to get Tcl's encoding names"); */
  8652. return 0;
  8653. }
  8654. /* get encoding name and set it to table */
  8655. for(i = 0; i < objc; i++) {
  8656. encname = rb_str_new2(Tcl_GetString(objv[i]));
  8657. if (NIL_P(rb_hash_lookup(table, encname))) {
  8658. /* new Tk encoding -> add to table */
  8659. encname = rb_obj_freeze(encname);
  8660. rb_hash_aset(table, encname, encname);
  8661. retry = 1;
  8662. }
  8663. }
  8664. Tcl_DecrRefCount(enc_list);
  8665. return retry;
  8666. }
  8667. static VALUE
  8668. encoding_table_get_name_core(table, enc, error_mode)
  8669. VALUE table;
  8670. VALUE enc;
  8671. VALUE error_mode;
  8672. {
  8673. volatile VALUE name = Qnil;
  8674. enc = rb_funcall(enc, ID_to_s, 0, 0);
  8675. name = rb_hash_lookup(table, enc);
  8676. if (!NIL_P(name)) {
  8677. /* find */
  8678. return name;
  8679. }
  8680. /* update check */
  8681. if (update_encoding_table(table, rb_ivar_get(table, ID_at_interp),
  8682. error_mode)) {
  8683. /* add new relations to the table */
  8684. /* RETRY: registered Ruby encoding? */
  8685. name = rb_hash_lookup(table, enc);
  8686. if (!NIL_P(name)) {
  8687. /* find */
  8688. return name;
  8689. }
  8690. }
  8691. if (RTEST(error_mode)) {
  8692. rb_raise(rb_eArgError, "unsupported Tk encoding '%s'", RSTRING_PTR(enc));
  8693. }
  8694. return Qnil;
  8695. }
  8696. static VALUE
  8697. encoding_table_get_obj_core(table, enc, error_mode)
  8698. VALUE table;
  8699. VALUE enc;
  8700. VALUE error_mode;
  8701. {
  8702. return encoding_table_get_name_core(table, enc, error_mode);
  8703. }
  8704. #else /* Tcl/Tk 7.x or 8.0 */
  8705. static VALUE
  8706. encoding_table_get_name_core(table, enc, error_mode)
  8707. VALUE table;
  8708. VALUE enc;
  8709. VALUE error_mode;
  8710. {
  8711. return Qnil;
  8712. }
  8713. static VALUE
  8714. encoding_table_get_obj_core(table, enc, error_mode)
  8715. VALUE table;
  8716. VALUE enc;
  8717. VALUE error_mode;
  8718. {
  8719. return Qnil;
  8720. }
  8721. #endif /* end of dependency for the version of Tcl/Tk */
  8722. #endif
  8723. static VALUE
  8724. encoding_table_get_name(table, enc)
  8725. VALUE table;
  8726. VALUE enc;
  8727. {
  8728. return encoding_table_get_name_core(table, enc, Qtrue);
  8729. }
  8730. static VALUE
  8731. encoding_table_get_obj(table, enc)
  8732. VALUE table;
  8733. VALUE enc;
  8734. {
  8735. return encoding_table_get_obj_core(table, enc, Qtrue);
  8736. }
  8737. #ifdef HAVE_RUBY_ENCODING_H
  8738. static VALUE
  8739. create_encoding_table_core(arg, interp)
  8740. VALUE arg;
  8741. VALUE interp;
  8742. {
  8743. struct tcltkip *ptr = get_ip(interp);
  8744. volatile VALUE table = rb_hash_new();
  8745. volatile VALUE encname = Qnil;
  8746. volatile VALUE encobj = Qnil;
  8747. int i, idx, objc;
  8748. Tcl_Obj **objv;
  8749. Tcl_Obj *enc_list;
  8750. #ifdef HAVE_RB_SET_SAFE_LEVEL_FORCE
  8751. rb_set_safe_level_force(0);
  8752. #else
  8753. rb_set_safe_level(0);
  8754. #endif
  8755. /* set 'binary' encoding */
  8756. encobj = rb_enc_from_encoding(rb_enc_from_index(ENCODING_INDEX_BINARY));
  8757. rb_hash_aset(table, ENCODING_NAME_BINARY, encobj);
  8758. rb_hash_aset(table, encobj, ENCODING_NAME_BINARY);
  8759. /* Tcl stub check */
  8760. tcl_stubs_check();
  8761. /* get Tcl's encoding list */
  8762. Tcl_GetEncodingNames(ptr->ip);
  8763. enc_list = Tcl_GetObjResult(ptr->ip);
  8764. Tcl_IncrRefCount(enc_list);
  8765. if (Tcl_ListObjGetElements(ptr->ip, enc_list, &objc, &objv) != TCL_OK) {
  8766. Tcl_DecrRefCount(enc_list);
  8767. rb_raise(rb_eRuntimeError, "failt to get Tcl's encoding names");
  8768. }
  8769. /* get encoding name and set it to table */
  8770. for(i = 0; i < objc; i++) {
  8771. int name2obj, obj2name;
  8772. name2obj = 1; obj2name = 1;
  8773. encname = rb_obj_freeze(rb_str_new2(Tcl_GetString(objv[i])));
  8774. idx = rb_enc_find_index(StringValueCStr(encname));
  8775. if (idx < 0) {
  8776. /* fail to find ruby encoding -> check known encoding */
  8777. if (strcmp(RSTRING_PTR(encname), "identity") == 0) {
  8778. name2obj = 1; obj2name = 0;
  8779. idx = ENCODING_INDEX_BINARY;
  8780. } else if (strcmp(RSTRING_PTR(encname), "shiftjis") == 0) {
  8781. name2obj = 1; obj2name = 0;
  8782. idx = rb_enc_find_index("Shift_JIS");
  8783. } else if (strcmp(RSTRING_PTR(encname), "unicode") == 0) {
  8784. name2obj = 1; obj2name = 0;
  8785. idx = ENCODING_INDEX_UTF8;
  8786. } else if (strcmp(RSTRING_PTR(encname), "symbol") == 0) {
  8787. name2obj = 1; obj2name = 0;
  8788. idx = rb_enc_find_index("ASCII-8BIT");
  8789. } else {
  8790. /* regist dummy encoding */
  8791. name2obj = 1; obj2name = 1;
  8792. }
  8793. }
  8794. if (idx < 0) {
  8795. /* unknown encoding -> create dummy */
  8796. encobj = create_dummy_encoding_for_tk(interp, encname);
  8797. } else {
  8798. encobj = rb_enc_from_encoding(rb_enc_from_index(idx));
  8799. }
  8800. if (name2obj) {
  8801. DUMP2("create_encoding_table: name2obj: %s", RSTRING_PTR(encname));
  8802. rb_hash_aset(table, encname, encobj);
  8803. }
  8804. if (obj2name) {
  8805. DUMP2("create_encoding_table: obj2name: %s", RSTRING_PTR(encname));
  8806. rb_hash_aset(table, encobj, encname);
  8807. }
  8808. }
  8809. Tcl_DecrRefCount(enc_list);
  8810. rb_ivar_set(table, ID_at_interp, interp);
  8811. rb_ivar_set(interp, ID_encoding_table, table);
  8812. return table;
  8813. }
  8814. #else /* ! HAVE_RUBY_ENCODING_H */
  8815. #if TCL_MAJOR_VERSION > 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION >= 1)
  8816. static VALUE
  8817. create_encoding_table_core(arg, interp)
  8818. VALUE arg;
  8819. VALUE interp;
  8820. {
  8821. struct tcltkip *ptr = get_ip(interp);
  8822. volatile VALUE table = rb_hash_new();
  8823. volatile VALUE encname = Qnil;
  8824. int i, objc;
  8825. Tcl_Obj **objv;
  8826. Tcl_Obj *enc_list;
  8827. rb_secure(4);
  8828. /* set 'binary' encoding */
  8829. rb_hash_aset(table, ENCODING_NAME_BINARY, ENCODING_NAME_BINARY);
  8830. /* get Tcl's encoding list */
  8831. Tcl_GetEncodingNames(ptr->ip);
  8832. enc_list = Tcl_GetObjResult(ptr->ip);
  8833. Tcl_IncrRefCount(enc_list);
  8834. if (Tcl_ListObjGetElements(ptr->ip, enc_list, &objc, &objv) != TCL_OK) {
  8835. Tcl_DecrRefCount(enc_list);
  8836. rb_raise(rb_eRuntimeError, "failt to get Tcl's encoding names");
  8837. }
  8838. /* get encoding name and set it to table */
  8839. for(i = 0; i < objc; i++) {
  8840. encname = rb_obj_freeze(rb_str_new2(Tcl_GetString(objv[i])));
  8841. rb_hash_aset(table, encname, encname);
  8842. }
  8843. Tcl_DecrRefCount(enc_list);
  8844. rb_ivar_set(table, ID_at_interp, interp);
  8845. rb_ivar_set(interp, ID_encoding_table, table);
  8846. return table;
  8847. }
  8848. #else /* Tcl/Tk 7.x or 8.0 */
  8849. static VALUE
  8850. create_encoding_table_core(arg, interp)
  8851. VALUE arg;
  8852. VALUE interp;
  8853. {
  8854. volatile VALUE table = rb_hash_new();
  8855. rb_secure(4);
  8856. rb_ivar_set(interp, ID_encoding_table, table);
  8857. return table;
  8858. }
  8859. #endif
  8860. #endif
  8861. static VALUE
  8862. create_encoding_table(interp)
  8863. VALUE interp;
  8864. {
  8865. return rb_funcall(rb_proc_new(create_encoding_table_core, interp),
  8866. ID_call, 0);
  8867. }
  8868. static VALUE
  8869. ip_get_encoding_table(interp)
  8870. VALUE interp;
  8871. {
  8872. volatile VALUE table = Qnil;
  8873. table = rb_ivar_get(interp, ID_encoding_table);
  8874. if (NIL_P(table)) {
  8875. /* initialize encoding_table */
  8876. table = create_encoding_table(interp);
  8877. rb_define_singleton_method(table, "get_name", encoding_table_get_name, 1);
  8878. rb_define_singleton_method(table, "get_obj", encoding_table_get_obj, 1);
  8879. }
  8880. return table;
  8881. }
  8882. /*###############################################*/
  8883. /*
  8884. * The following is based on tkMenu.[ch]
  8885. * of Tcl/Tk (Tk8.0 -- Tk8.5b1) source code.
  8886. */
  8887. #if TCL_MAJOR_VERSION >= 8
  8888. #define MASTER_MENU 0
  8889. #define TEAROFF_MENU 1
  8890. #define MENUBAR 2
  8891. struct dummy_TkMenuEntry {
  8892. int type;
  8893. struct dummy_TkMenu *menuPtr;
  8894. /* , and etc. */
  8895. };
  8896. struct dummy_TkMenu {
  8897. Tk_Window tkwin;
  8898. Display *display;
  8899. Tcl_Interp *interp;
  8900. Tcl_Command widgetCmd;
  8901. struct dummy_TkMenuEntry **entries;
  8902. int numEntries;
  8903. int active;
  8904. int menuType; /* MASTER_MENU, TEAROFF_MENU, or MENUBAR */
  8905. Tcl_Obj *menuTypePtr;
  8906. /* , and etc. */
  8907. };
  8908. struct dummy_TkMenuRef {
  8909. struct dummy_TkMenu *menuPtr;
  8910. char *dummy1;
  8911. char *dummy2;
  8912. char *dummy3;
  8913. };
  8914. #if 0 /* was available on Tk8.0 -- Tk8.4 */
  8915. EXTERN struct dummy_TkMenuRef *TkFindMenuReferences(Tcl_Interp*, char*);
  8916. #else /* based on Tk8.0 -- Tk8.5.0 */
  8917. #define MENU_HASH_KEY "tkMenus"
  8918. #endif
  8919. #endif
  8920. static VALUE
  8921. ip_make_menu_embeddable_core(interp, argc, argv)
  8922. VALUE interp;
  8923. int argc;
  8924. VALUE *argv;
  8925. {
  8926. #if TCL_MAJOR_VERSION >= 8
  8927. volatile VALUE menu_path;
  8928. struct tcltkip *ptr = get_ip(interp);
  8929. struct dummy_TkMenuRef *menuRefPtr = NULL;
  8930. XEvent event;
  8931. Tcl_HashTable *menuTablePtr;
  8932. Tcl_HashEntry *hashEntryPtr;
  8933. menu_path = argv[0];
  8934. StringValue(menu_path);
  8935. #if 0 /* was available on Tk8.0 -- Tk8.4 */
  8936. menuRefPtr = TkFindMenuReferences(ptr->ip, RSTRING_PTR(menu_path));
  8937. #else /* based on Tk8.0 -- Tk8.5b1 */
  8938. if ((menuTablePtr
  8939. = (Tcl_HashTable *) Tcl_GetAssocData(ptr->ip, MENU_HASH_KEY, NULL))
  8940. != NULL) {
  8941. if ((hashEntryPtr
  8942. = Tcl_FindHashEntry(menuTablePtr, RSTRING_PTR(menu_path)))
  8943. != NULL) {
  8944. menuRefPtr = (struct dummy_TkMenuRef *) Tcl_GetHashValue(hashEntryPtr);
  8945. }
  8946. }
  8947. #endif
  8948. if (menuRefPtr == (struct dummy_TkMenuRef *) NULL) {
  8949. rb_raise(rb_eArgError, "not a menu widget, or invalid widget path");
  8950. }
  8951. if (menuRefPtr->menuPtr == (struct dummy_TkMenu *) NULL) {
  8952. rb_raise(rb_eRuntimeError,
  8953. "invalid menu widget (maybe already destroyed)");
  8954. }
  8955. if ((menuRefPtr->menuPtr)->menuType != MENUBAR) {
  8956. rb_raise(rb_eRuntimeError,
  8957. "target menu widget must be a MENUBAR type");
  8958. }
  8959. (menuRefPtr->menuPtr)->menuType = TEAROFF_MENU;
  8960. #if 0 /* cause SEGV */
  8961. {
  8962. /* char *s = "tearoff"; */
  8963. char *s = "normal";
  8964. /* Tcl_SetStringObj((menuRefPtr->menuPtr)->menuTypePtr, s, strlen(s));*/
  8965. (menuRefPtr->menuPtr)->menuTypePtr = Tcl_NewStringObj(s, strlen(s));
  8966. /* Tcl_IncrRefCount((menuRefPtr->menuPtr)->menuTypePtr); */
  8967. /* (menuRefPtr->menuPtr)->menuType = TEAROFF_MENU; */
  8968. (menuRefPtr->menuPtr)->menuType = MASTER_MENU;
  8969. }
  8970. #endif
  8971. #if 0 /* was available on Tk8.0 -- Tk8.4 */
  8972. TkEventuallyRecomputeMenu(menuRefPtr->menuPtr);
  8973. TkEventuallyRedrawMenu(menuRefPtr->menuPtr,
  8974. (struct dummy_TkMenuEntry *)NULL);
  8975. #else /* based on Tk8.0 -- Tk8.5b1 */
  8976. memset((void *) &event, 0, sizeof(event));
  8977. event.xany.type = ConfigureNotify;
  8978. event.xany.serial = NextRequest(Tk_Display((menuRefPtr->menuPtr)->tkwin));
  8979. event.xany.send_event = 0; /* FALSE */
  8980. event.xany.window = Tk_WindowId((menuRefPtr->menuPtr)->tkwin);
  8981. event.xany.display = Tk_Display((menuRefPtr->menuPtr)->tkwin);
  8982. event.xconfigure.window = event.xany.window;
  8983. Tk_HandleEvent(&event);
  8984. #endif
  8985. #else /* TCL_MAJOR_VERSION <= 7 */
  8986. rb_notimplement();
  8987. #endif
  8988. return interp;
  8989. }
  8990. static VALUE
  8991. ip_make_menu_embeddable(interp, menu_path)
  8992. VALUE interp;
  8993. VALUE menu_path;
  8994. {
  8995. VALUE argv[1];
  8996. argv[0] = menu_path;
  8997. return tk_funcall(ip_make_menu_embeddable_core, 1, argv, interp);
  8998. }
  8999. /*###############################################*/
  9000. /*---- initialization ----*/
  9001. void
  9002. Init_tcltklib()
  9003. {
  9004. int ret;
  9005. VALUE lib = rb_define_module("TclTkLib");
  9006. VALUE ip = rb_define_class("TclTkIp", rb_cObject);
  9007. VALUE ev_flag = rb_define_module_under(lib, "EventFlag");
  9008. VALUE var_flag = rb_define_module_under(lib, "VarAccessFlag");
  9009. VALUE release_type = rb_define_module_under(lib, "RELEASE_TYPE");
  9010. /* --------------------------------------------------------------- */
  9011. tcltkip_class = ip;
  9012. /* --------------------------------------------------------------- */
  9013. #ifdef HAVE_RUBY_ENCODING_H
  9014. rb_global_variable(&cRubyEncoding);
  9015. cRubyEncoding = rb_path2class("Encoding");
  9016. ENCODING_INDEX_UTF8 = rb_enc_to_index(rb_utf8_encoding());
  9017. ENCODING_INDEX_BINARY = rb_enc_find_index("binary");
  9018. #endif
  9019. rb_global_variable(&ENCODING_NAME_UTF8);
  9020. rb_global_variable(&ENCODING_NAME_BINARY);
  9021. ENCODING_NAME_UTF8 = rb_obj_freeze(rb_str_new2("utf-8"));
  9022. ENCODING_NAME_BINARY = rb_obj_freeze(rb_str_new2("binary"));
  9023. /* --------------------------------------------------------------- */
  9024. rb_global_variable(&eTkCallbackReturn);
  9025. rb_global_variable(&eTkCallbackBreak);
  9026. rb_global_variable(&eTkCallbackContinue);
  9027. rb_global_variable(&eventloop_thread);
  9028. rb_global_variable(&eventloop_stack);
  9029. rb_global_variable(&watchdog_thread);
  9030. rb_global_variable(&rbtk_pending_exception);
  9031. /* --------------------------------------------------------------- */
  9032. rb_define_const(lib, "COMPILE_INFO", tcltklib_compile_info());
  9033. rb_define_const(lib, "RELEASE_DATE",
  9034. rb_obj_freeze(rb_str_new2(tcltklib_release_date)));
  9035. rb_define_const(lib, "FINALIZE_PROC_NAME",
  9036. rb_str_new2(finalize_hook_name));
  9037. /* --------------------------------------------------------------- */
  9038. #ifdef __WIN32__
  9039. # define TK_WINDOWING_SYSTEM "win32"
  9040. #else
  9041. # ifdef MAC_TCL
  9042. # define TK_WINDOWING_SYSTEM "classic"
  9043. # else
  9044. # ifdef MAC_OSX_TK
  9045. # define TK_WINDOWING_SYSTEM "aqua"
  9046. # else
  9047. # define TK_WINDOWING_SYSTEM "x11"
  9048. # endif
  9049. # endif
  9050. #endif
  9051. rb_define_const(lib, "WINDOWING_SYSTEM",
  9052. rb_obj_freeze(rb_str_new2(TK_WINDOWING_SYSTEM)));
  9053. /* --------------------------------------------------------------- */
  9054. rb_define_const(ev_flag, "NONE", INT2FIX(0));
  9055. rb_define_const(ev_flag, "WINDOW", INT2FIX(TCL_WINDOW_EVENTS));
  9056. rb_define_const(ev_flag, "FILE", INT2FIX(TCL_FILE_EVENTS));
  9057. rb_define_const(ev_flag, "TIMER", INT2FIX(TCL_TIMER_EVENTS));
  9058. rb_define_const(ev_flag, "IDLE", INT2FIX(TCL_IDLE_EVENTS));
  9059. rb_define_const(ev_flag, "ALL", INT2FIX(TCL_ALL_EVENTS));
  9060. rb_define_const(ev_flag, "DONT_WAIT", INT2FIX(TCL_DONT_WAIT));
  9061. /* --------------------------------------------------------------- */
  9062. rb_define_const(var_flag, "NONE", INT2FIX(0));
  9063. rb_define_const(var_flag, "GLOBAL_ONLY", INT2FIX(TCL_GLOBAL_ONLY));
  9064. #ifdef TCL_NAMESPACE_ONLY
  9065. rb_define_const(var_flag, "NAMESPACE_ONLY", INT2FIX(TCL_NAMESPACE_ONLY));
  9066. #else /* probably Tcl7.6 */
  9067. rb_define_const(var_flag, "NAMESPACE_ONLY", INT2FIX(0));
  9068. #endif
  9069. rb_define_const(var_flag, "LEAVE_ERR_MSG", INT2FIX(TCL_LEAVE_ERR_MSG));
  9070. rb_define_const(var_flag, "APPEND_VALUE", INT2FIX(TCL_APPEND_VALUE));
  9071. rb_define_const(var_flag, "LIST_ELEMENT", INT2FIX(TCL_LIST_ELEMENT));
  9072. #ifdef TCL_PARSE_PART1
  9073. rb_define_const(var_flag, "PARSE_VARNAME", INT2FIX(TCL_PARSE_PART1));
  9074. #else /* probably Tcl7.6 */
  9075. rb_define_const(var_flag, "PARSE_VARNAME", INT2FIX(0));
  9076. #endif
  9077. /* --------------------------------------------------------------- */
  9078. rb_define_module_function(lib, "get_version", lib_getversion, -1);
  9079. rb_define_module_function(lib, "get_release_type_name",
  9080. lib_get_reltype_name, -1);
  9081. rb_define_const(release_type, "ALPHA", INT2FIX(TCL_ALPHA_RELEASE));
  9082. rb_define_const(release_type, "BETA", INT2FIX(TCL_BETA_RELEASE));
  9083. rb_define_const(release_type, "FINAL", INT2FIX(TCL_FINAL_RELEASE));
  9084. /* --------------------------------------------------------------- */
  9085. eTkCallbackReturn = rb_define_class("TkCallbackReturn", rb_eStandardError);
  9086. eTkCallbackBreak = rb_define_class("TkCallbackBreak", rb_eStandardError);
  9087. eTkCallbackContinue = rb_define_class("TkCallbackContinue",
  9088. rb_eStandardError);
  9089. /* --------------------------------------------------------------- */
  9090. eLocalJumpError = rb_const_get(rb_cObject, rb_intern("LocalJumpError"));
  9091. eTkLocalJumpError = rb_define_class("TkLocalJumpError", eLocalJumpError);
  9092. eTkCallbackRetry = rb_define_class("TkCallbackRetry", eTkLocalJumpError);
  9093. eTkCallbackRedo = rb_define_class("TkCallbackRedo", eTkLocalJumpError);
  9094. eTkCallbackThrow = rb_define_class("TkCallbackThrow", eTkLocalJumpError);
  9095. /* --------------------------------------------------------------- */
  9096. ID_at_enc = rb_intern("@encoding");
  9097. ID_at_interp = rb_intern("@interp");
  9098. ID_encoding_name = rb_intern("encoding_name");
  9099. ID_encoding_table = rb_intern("encoding_table");
  9100. ID_stop_p = rb_intern("stop?");
  9101. ID_alive_p = rb_intern("alive?");
  9102. ID_kill = rb_intern("kill");
  9103. ID_join = rb_intern("join");
  9104. ID_value = rb_intern("value");
  9105. ID_call = rb_intern("call");
  9106. ID_backtrace = rb_intern("backtrace");
  9107. ID_message = rb_intern("message");
  9108. ID_at_reason = rb_intern("@reason");
  9109. ID_return = rb_intern("return");
  9110. ID_break = rb_intern("break");
  9111. ID_next = rb_intern("next");
  9112. ID_to_s = rb_intern("to_s");
  9113. ID_inspect = rb_intern("inspect");
  9114. /* --------------------------------------------------------------- */
  9115. rb_define_module_function(lib, "mainloop", lib_mainloop, -1);
  9116. rb_define_module_function(lib, "mainloop_thread?",
  9117. lib_evloop_thread_p, 0);
  9118. rb_define_module_function(lib, "mainloop_watchdog",
  9119. lib_mainloop_watchdog, -1);
  9120. rb_define_module_function(lib, "do_thread_callback",
  9121. lib_thread_callback, -1);
  9122. rb_define_module_function(lib, "do_one_event", lib_do_one_event, -1);
  9123. rb_define_module_function(lib, "mainloop_abort_on_exception",
  9124. lib_evloop_abort_on_exc, 0);
  9125. rb_define_module_function(lib, "mainloop_abort_on_exception=",
  9126. lib_evloop_abort_on_exc_set, 1);
  9127. rb_define_module_function(lib, "set_eventloop_window_mode",
  9128. set_eventloop_window_mode, 1);
  9129. rb_define_module_function(lib, "get_eventloop_window_mode",
  9130. get_eventloop_window_mode, 0);
  9131. rb_define_module_function(lib, "set_eventloop_tick",set_eventloop_tick,1);
  9132. rb_define_module_function(lib, "get_eventloop_tick",get_eventloop_tick,0);
  9133. rb_define_module_function(lib, "set_no_event_wait", set_no_event_wait, 1);
  9134. rb_define_module_function(lib, "get_no_event_wait", get_no_event_wait, 0);
  9135. rb_define_module_function(lib, "set_eventloop_weight",
  9136. set_eventloop_weight, 2);
  9137. rb_define_module_function(lib, "set_max_block_time", set_max_block_time,1);
  9138. rb_define_module_function(lib, "get_eventloop_weight",
  9139. get_eventloop_weight, 0);
  9140. rb_define_module_function(lib, "num_of_mainwindows",
  9141. lib_num_of_mainwindows, 0);
  9142. /* --------------------------------------------------------------- */
  9143. rb_define_module_function(lib, "_split_tklist", lib_split_tklist, 1);
  9144. rb_define_module_function(lib, "_merge_tklist", lib_merge_tklist, -1);
  9145. rb_define_module_function(lib, "_conv_listelement",
  9146. lib_conv_listelement, 1);
  9147. rb_define_module_function(lib, "_toUTF8", lib_toUTF8, -1);
  9148. rb_define_module_function(lib, "_fromUTF8", lib_fromUTF8, -1);
  9149. rb_define_module_function(lib, "_subst_UTF_backslash",
  9150. lib_UTF_backslash, 1);
  9151. rb_define_module_function(lib, "_subst_Tcl_backslash",
  9152. lib_Tcl_backslash, 1);
  9153. rb_define_module_function(lib, "encoding_system",
  9154. lib_get_system_encoding, 0);
  9155. rb_define_module_function(lib, "encoding_system=",
  9156. lib_set_system_encoding, 1);
  9157. rb_define_module_function(lib, "encoding",
  9158. lib_get_system_encoding, 0);
  9159. rb_define_module_function(lib, "encoding=",
  9160. lib_set_system_encoding, 1);
  9161. /* --------------------------------------------------------------- */
  9162. rb_define_alloc_func(ip, ip_alloc);
  9163. rb_define_method(ip, "initialize", ip_init, -1);
  9164. rb_define_method(ip, "create_slave", ip_create_slave, -1);
  9165. rb_define_method(ip, "slave_of?", ip_is_slave_of_p, 1);
  9166. rb_define_method(ip, "make_safe", ip_make_safe, 0);
  9167. rb_define_method(ip, "safe?", ip_is_safe_p, 0);
  9168. rb_define_method(ip, "allow_ruby_exit?", ip_allow_ruby_exit_p, 0);
  9169. rb_define_method(ip, "allow_ruby_exit=", ip_allow_ruby_exit_set, 1);
  9170. rb_define_method(ip, "delete", ip_delete, 0);
  9171. rb_define_method(ip, "deleted?", ip_is_deleted_p, 0);
  9172. rb_define_method(ip, "has_mainwindow?", ip_has_mainwindow_p, 0);
  9173. rb_define_method(ip, "invalid_namespace?", ip_has_invalid_namespace_p, 0);
  9174. rb_define_method(ip, "_eval", ip_eval, 1);
  9175. rb_define_method(ip, "_cancel_eval", ip_cancel_eval, -1);
  9176. rb_define_method(ip, "_cancel_eval_unwind", ip_cancel_eval_unwind, -1);
  9177. rb_define_method(ip, "_toUTF8", ip_toUTF8, -1);
  9178. rb_define_method(ip, "_fromUTF8", ip_fromUTF8, -1);
  9179. rb_define_method(ip, "_thread_vwait", ip_thread_vwait, 1);
  9180. rb_define_method(ip, "_thread_tkwait", ip_thread_tkwait, 2);
  9181. rb_define_method(ip, "_invoke", ip_invoke, -1);
  9182. rb_define_method(ip, "_immediate_invoke", ip_invoke_immediate, -1);
  9183. rb_define_method(ip, "_return_value", ip_retval, 0);
  9184. rb_define_method(ip, "_create_console", ip_create_console, 0);
  9185. /* --------------------------------------------------------------- */
  9186. rb_define_method(ip, "create_dummy_encoding_for_tk",
  9187. create_dummy_encoding_for_tk, 1);
  9188. rb_define_method(ip, "encoding_table", ip_get_encoding_table, 0);
  9189. /* --------------------------------------------------------------- */
  9190. rb_define_method(ip, "_get_variable", ip_get_variable, 2);
  9191. rb_define_method(ip, "_get_variable2", ip_get_variable2, 3);
  9192. rb_define_method(ip, "_set_variable", ip_set_variable, 3);
  9193. rb_define_method(ip, "_set_variable2", ip_set_variable2, 4);
  9194. rb_define_method(ip, "_unset_variable", ip_unset_variable, 2);
  9195. rb_define_method(ip, "_unset_variable2", ip_unset_variable2, 3);
  9196. rb_define_method(ip, "_get_global_var", ip_get_global_var, 1);
  9197. rb_define_method(ip, "_get_global_var2", ip_get_global_var2, 2);
  9198. rb_define_method(ip, "_set_global_var", ip_set_global_var, 2);
  9199. rb_define_method(ip, "_set_global_var2", ip_set_global_var2, 3);
  9200. rb_define_method(ip, "_unset_global_var", ip_unset_global_var, 1);
  9201. rb_define_method(ip, "_unset_global_var2", ip_unset_global_var2, 2);
  9202. /* --------------------------------------------------------------- */
  9203. rb_define_method(ip, "_make_menu_embeddable", ip_make_menu_embeddable, 1);
  9204. /* --------------------------------------------------------------- */
  9205. rb_define_method(ip, "_split_tklist", ip_split_tklist, 1);
  9206. rb_define_method(ip, "_merge_tklist", lib_merge_tklist, -1);
  9207. rb_define_method(ip, "_conv_listelement", lib_conv_listelement, 1);
  9208. /* --------------------------------------------------------------- */
  9209. rb_define_method(ip, "mainloop", ip_mainloop, -1);
  9210. rb_define_method(ip, "mainloop_watchdog", ip_mainloop_watchdog, -1);
  9211. rb_define_method(ip, "do_one_event", ip_do_one_event, -1);
  9212. rb_define_method(ip, "mainloop_abort_on_exception",
  9213. ip_evloop_abort_on_exc, 0);
  9214. rb_define_method(ip, "mainloop_abort_on_exception=",
  9215. ip_evloop_abort_on_exc_set, 1);
  9216. rb_define_method(ip, "set_eventloop_tick", ip_set_eventloop_tick, 1);
  9217. rb_define_method(ip, "get_eventloop_tick", ip_get_eventloop_tick, 0);
  9218. rb_define_method(ip, "set_no_event_wait", ip_set_no_event_wait, 1);
  9219. rb_define_method(ip, "get_no_event_wait", ip_get_no_event_wait, 0);
  9220. rb_define_method(ip, "set_eventloop_weight", ip_set_eventloop_weight, 2);
  9221. rb_define_method(ip, "get_eventloop_weight", ip_get_eventloop_weight, 0);
  9222. rb_define_method(ip, "set_max_block_time", set_max_block_time, 1);
  9223. rb_define_method(ip, "restart", ip_restart, 0);
  9224. /* --------------------------------------------------------------- */
  9225. eventloop_thread = Qnil;
  9226. eventloop_interp = (Tcl_Interp*)NULL;
  9227. #ifndef DEFAULT_EVENTLOOP_DEPTH
  9228. #define DEFAULT_EVENTLOOP_DEPTH 7
  9229. #endif
  9230. eventloop_stack = rb_ary_new2(DEFAULT_EVENTLOOP_DEPTH);
  9231. RbTk_OBJ_UNTRUST(eventloop_stack);
  9232. watchdog_thread = Qnil;
  9233. rbtk_pending_exception = Qnil;
  9234. /* --------------------------------------------------------------- */
  9235. #ifdef HAVE_NATIVETHREAD
  9236. /* if ruby->nativethread-supprt and tcltklib->doen't,
  9237. the following will cause link-error. */
  9238. ruby_native_thread_p();
  9239. #endif
  9240. /* --------------------------------------------------------------- */
  9241. rb_set_end_proc(lib_mark_at_exit, 0);
  9242. /* --------------------------------------------------------------- */
  9243. ret = ruby_open_tcl_dll(rb_argv0 ? RSTRING_PTR(rb_argv0) : 0);
  9244. switch(ret) {
  9245. case TCLTK_STUBS_OK:
  9246. break;
  9247. case NO_TCL_DLL:
  9248. rb_raise(rb_eLoadError, "tcltklib: fail to open tcl_dll");
  9249. case NO_FindExecutable:
  9250. rb_raise(rb_eLoadError, "tcltklib: can't find Tcl_FindExecutable");
  9251. default:
  9252. rb_raise(rb_eLoadError, "tcltklib: unknown error(%d) on ruby_open_tcl_dll", ret);
  9253. }
  9254. /* --------------------------------------------------------------- */
  9255. #if defined CREATE_RUBYTK_KIT
  9256. #ifdef __WIN32__
  9257. rbtk_win32_SetHINSTANCE("tcltklib.so");
  9258. #endif
  9259. tcltklib_filepath = strdup(rb_sourcefile());
  9260. #endif
  9261. #if defined CREATE_RUBYTK_KIT || defined CREATE_RUBYKIT
  9262. init_static_tcltk_packages();
  9263. setup_preInitCmd(tcltklib_filepath);
  9264. #endif
  9265. /* --------------------------------------------------------------- */
  9266. /* Tcl stub check */
  9267. tcl_stubs_check();
  9268. Tcl_ObjType_ByteArray = Tcl_GetObjType(Tcl_ObjTypeName_ByteArray);
  9269. Tcl_ObjType_String = Tcl_GetObjType(Tcl_ObjTypeName_String);
  9270. /* --------------------------------------------------------------- */
  9271. (void)call_original_exit;
  9272. }
  9273. /* eof */