Module: Teek

Defined in:
lib/teek.rb,
lib/teek/wm.rb,
lib/teek/photo.rb,
lib/teek/winfo.rb,
lib/teek/widget.rb,
lib/teek/dialogs.rb,
lib/teek/version.rb,
lib/teek/debugger.rb,
lib/teek/platform.rb,
lib/teek/ractor_support.rb,
lib/teek/background_none.rb,
lib/teek/menu_interceptor.rb,
lib/teek/background_thread.rb,
lib/teek/callback_registry.rb,
lib/teek/background_ractor4x.rb,
lib/teek/command_interceptors.rb,
lib/teek/tag_bind_interceptor.rb,
lib/teek/canvas_bind_interceptor.rb,
lib/teek/method_coverage_service.rb,
ext/teek/tcltkbridge.c

Overview

Standalone platform detection — no dependencies on the rest of Teek. Safe to require from extconf.rb or any context where the full gem isn't loaded yet.

Defined Under Namespace

Modules: BackgroundNone, BackgroundRactor4x, BackgroundThread, CanvasBindInterceptor, MenuInterceptor, RELEASE_TYPE, TagBindInterceptor Classes: AmbiguousCommandError, App, BackgroundWork, CallbackRegistry, CommandInterceptors, Debugger, EventSource, Interp, MethodCoverageService, Photo, Platform, RactorStream, RepeatingTimer, TclError, Widget, Winfo, Wm

Constant Summary collapse

WIDGET_COMMANDS =
%w[
  button label frame entry text canvas listbox
  scrollbar scale spinbox menu menubutton message
  panedwindow labelframe checkbutton radiobutton
  toplevel
  ttk::button ttk::label ttk::frame ttk::entry
  ttk::combobox ttk::checkbutton ttk::radiobutton
  ttk::scale ttk::scrollbar ttk::spinbox ttk::separator
  ttk::sizegrip ttk::progressbar ttk::notebook
  ttk::panedwindow ttk::labelframe ttk::menubutton
  ttk::treeview
].freeze
VERSION =
"0.2.0"
WINDOW_EVENTS =

Event flags as constants

INT2NUM(TCL_WINDOW_EVENTS)
FILE_EVENTS =
INT2NUM(TCL_FILE_EVENTS)
TIMER_EVENTS =
INT2NUM(TCL_TIMER_EVENTS)
IDLE_EVENTS =
INT2NUM(TCL_IDLE_EVENTS)
ALL_EVENTS =
INT2NUM(TCL_ALL_EVENTS)
DONT_WAIT =
INT2NUM(TCL_DONT_WAIT)
CALLBACK_BREAK =

:App#register_callback catch/throw)

Callback control flow symbols (used by Teek
CALLBACK_CONTINUE =
ID2SYM(rb_intern("teek_continue"))
CALLBACK_RETURN =
ID2SYM(rb_intern("teek_return"))

Class Method Summary collapse

Class Method Details

._register_event_source(fn_ptr, data_ptr, interval) ⇒ Object

Teek._register_event_source(check_fn_ptr, client_data_ptr, interval_ms) -> EventSource

Registers a C function as a Tcl event source. The function will be called on every event loop iteration with no Ruby overhead.

check_fn_ptr: Integer — address of a C function with signature void()(void) client_data_ptr: Integer — address passed to check_fn (0 for NULL) interval_ms: Integer — max block time in ms (e.g. 16 for ~60fps)

Returns an opaque EventSource object. Hold a reference to keep it alive. Call #unregister or let GC collect it to remove the event source.



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'ext/teek/tkeventsource.c', line 117

static VALUE
teek_register_event_source(VALUE self, VALUE fn_ptr, VALUE data_ptr, VALUE interval)
{
    struct event_source *es;
    VALUE obj;
    int ms;

    /* Validate */
    event_source_check_fn fn = (event_source_check_fn)(uintptr_t)NUM2ULL(fn_ptr);
    if (!fn) {
        rb_raise(rb_eArgError, "check_fn_ptr must not be NULL");
    }

    ms = NUM2INT(interval);
    if (ms < 1) ms = 1;

    /* Allocate and populate */
    obj = TypedData_Make_Struct(cEventSource, struct event_source, &event_source_type, es);
    es->check_fn = fn;
    es->client_data = (void *)(uintptr_t)NUM2ULL(data_ptr);
    es->max_block.sec = ms / 1000;
    es->max_block.usec = (ms % 1000) * 1000;
    es->registered = 0;

    /* Register with Tcl */
    Tcl_CreateEventSource(es_setup_proc, es_check_proc, (ClientData)es);
    es->registered = 1;

    return obj;
}

.bool_to_tcl(val) ⇒ Object



75
76
77
# File 'lib/teek.rb', line 75

def self.bool_to_tcl(val)
  val ? "1" : "0"
end

.get_versionObject


TclTkLib.get_version - Get Tcl version as [major, minor, type, patchlevel]

WHY COMPILE-TIME MACROS INSTEAD OF Tcl_GetVersion()?

With stubs enabled (-DUSE_TCL_STUBS), Tcl_GetVersion() becomes a macro that dereferences tclStubsPtr->tcl_GetVersion. But tclStubsPtr is NULL until Tcl_InitStubs() is called - which requires an interpreter.

So the "proper" API to get the version needs an interpreter to exist first. That's backwards - callers often want version info before deciding whether to create an interpreter.

The workaround: use the compile-time macros from tcl.h directly. These are just #defines, no stubs table needed. The version reported is what we compiled against, which must match the runtime major version (stubs enforce this). Minor/patch may differ at runtime - use TclTkIp#tcl_version for the exact runtime patchlevel.



1433
1434
1435
1436
1437
1438
1439
1440
1441
# File 'ext/teek/tcltkbridge.c', line 1433

static VALUE
lib_get_version(VALUE self)
{
    return rb_ary_new3(4,
        INT2NUM(TCL_MAJOR_VERSION),
        INT2NUM(TCL_MINOR_VERSION),
        INT2NUM(TCL_RELEASE_LEVEL),
        INT2NUM(TCL_RELEASE_SERIAL));
}

.in_callback?Boolean


TclTkLib.in_callback? - Check if currently inside a Tk callback

Used to detect unsafe operations (exit/destroy from callback).

Returns:

  • (Boolean)


1407
1408
1409
1410
1411
# File 'ext/teek/tcltkbridge.c', line 1407

static VALUE
lib_in_callback_p(VALUE self)
{
    return rbtk_callback_depth > 0 ? Qtrue : Qfalse;
}

.make_list(*args) ⇒ Object

Module functions for Tcl value conversion (no interpreter needed)



1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
# File 'ext/teek/tcltkbridge.c', line 1260

static VALUE
teek_make_list(int argc, VALUE *argv, VALUE self)
{
    struct make_list_state st;

    if (argc == 0) return rb_utf8_str_new_cstr("");

    st.listobj = Tcl_NewListObj(0, NULL);
    Tcl_IncrRefCount(st.listobj);
    st.argc = argc;
    st.argv = argv;

    return rb_ensure(make_list_body, (VALUE)&st,
                     make_list_cleanup, (VALUE)&st);
}

.platformObject



26
27
28
# File 'lib/teek/platform.rb', line 26

def self.platform
  @platform ||= Platform.new
end

.split_list(list_str) ⇒ Object


Teek.split_list(str) - Parse Tcl list into Ruby array

Module function — uses utility_interp for error reporting. Single C call instead of N+1 eval round-trips. Returns array of strings (does not recursively parse nested lists).



1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
# File 'ext/teek/tcltkbridge.c', line 1284

static VALUE
teek_split_list(VALUE self, VALUE list_str)
{
    Tcl_Obj *listobj;
    Tcl_Size objc;
    Tcl_Obj **objv;
    VALUE ary;
    Tcl_Size i;
    int result;

    if (NIL_P(list_str)) {
        return rb_ary_new();
    }

    StringValue(list_str);
    if (RSTRING_LEN(list_str) == 0) {
        return rb_ary_new();
    }

    /* Create Tcl object from Ruby string */
    listobj = Tcl_NewStringObj(RSTRING_PTR(list_str), RSTRING_LEN(list_str));
    Tcl_IncrRefCount(listobj);

    /* Use utility_interp for error reporting */
    result = Tcl_ListObjGetElements(utility_interp, listobj, &objc, &objv);
    if (result != TCL_OK) {
        const char *msg = Tcl_GetStringResult(utility_interp);
        Tcl_DecrRefCount(listobj);
        rb_raise(eTclError, "invalid Tcl list: %s", msg);
    }

    /* Convert to Ruby array of strings */
    ary = rb_ary_new2(objc);
    for (i = 0; i < objc; i++) {
        Tcl_Size len;
        const char *str = Tcl_GetStringFromObj(objv[i], &len);
        rb_ary_push(ary, rb_utf8_str_new(str, len));
    }

    Tcl_DecrRefCount(listobj);
    return ary;
}

.tcl_to_bool(str) ⇒ Object


Teek.tcl_to_bool(str) - Convert Tcl boolean string to Ruby true/false

Uses Tcl_GetBooleanFromObj which recognizes: true/false, yes/no, on/off, 1/0 (case-insensitive) and any numeric value (0 = false, non-zero = true)



1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
# File 'ext/teek/tcltkbridge.c', line 1200

static VALUE
teek_tcl_to_bool(VALUE self, VALUE str)
{
    Tcl_Obj *obj;
    int bval;

    StringValue(str);
    obj = Tcl_NewStringObj(RSTRING_PTR(str), RSTRING_LEN(str));
    Tcl_IncrRefCount(obj);

    if (Tcl_GetBooleanFromObj(utility_interp, obj, &bval) != TCL_OK) {
        const char *msg = Tcl_GetStringResult(utility_interp);
        Tcl_DecrRefCount(obj);
        rb_raise(eTclError, "%s", msg);
    }

    Tcl_DecrRefCount(obj);
    return bval ? Qtrue : Qfalse;
}