1 2/* Jim - A small embeddable Tcl interpreter 3 * 4 * Copyright 2005 Salvatore Sanfilippo <antirez@invece.org> 5 * Copyright 2005 Clemens Hintze <c.hintze@gmx.net> 6 * Copyright 2005 patthoyts - Pat Thoyts <patthoyts@users.sf.net> 7 * Copyright 2008 oharboe - ��yvind Harboe - oyvind.harboe@zylin.com 8 * Copyright 2008 Andrew Lunn <andrew@lunn.ch> 9 * Copyright 2008 Duane Ellis <openocd@duaneellis.com> 10 * Copyright 2008 Uwe Klein <uklein@klein-messgeraete.de> 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above 19 * copyright notice, this list of conditions and the following 20 * disclaimer in the documentation and/or other materials 21 * provided with the distribution. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY 24 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 25 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 26 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 28 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 34 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 * 36 * The views and conclusions contained in the software and documentation 37 * are those of the authors and should not be interpreted as representing 38 * official policies, either expressed or implied, of the Jim Tcl Project. 39 **/ 40 41#include "jim.h" 42#include "jimautoconf.h" 43#include "jim-eventloop.h" 44 45/* POSIX includes */ 46#include <sys/time.h> 47#include <sys/types.h> 48#include <string.h> 49#include <unistd.h> 50#include <errno.h> 51 52#if defined(__MINGW32__) 53#include <windows.h> 54#include <winsock.h> 55#define msleep Sleep 56#ifndef HAVE_USLEEP 57#define usleep(US) msleep((US) / 1000) 58#endif 59#else 60#include <sys/select.h> 61 62#ifndef HAVE_USLEEP 63/* XXX: Implement this in terms of select() or nanosleep() */ 64#define usleep(US) 65#endif 66#define msleep(MS) sleep((MS) / 1000); usleep(((MS) % 1000) * 1000); 67#endif 68 69/* --- */ 70 71/* File event structure */ 72typedef struct Jim_FileEvent 73{ 74 FILE *handle; 75 int mask; /* one of JIM_EVENT_(READABLE|WRITABLE|EXCEPTION) */ 76 Jim_FileProc *fileProc; 77 Jim_EventFinalizerProc *finalizerProc; 78 void *clientData; 79 struct Jim_FileEvent *next; 80} Jim_FileEvent; 81 82/* Time event structure */ 83typedef struct Jim_TimeEvent 84{ 85 jim_wide id; /* time event identifier. */ 86 int mode; /* restart, repetitive .. UK */ 87 long initialms; /* initial relativ timer value UK */ 88 long when_sec; /* seconds */ 89 long when_ms; /* milliseconds */ 90 Jim_TimeProc *timeProc; 91 Jim_EventFinalizerProc *finalizerProc; 92 void *clientData; 93 struct Jim_TimeEvent *next; 94} Jim_TimeEvent; 95 96/* Per-interp stucture containing the state of the event loop */ 97typedef struct Jim_EventLoop 98{ 99 jim_wide timeEventNextId; 100 Jim_FileEvent *fileEventHead; 101 Jim_TimeEvent *timeEventHead; 102 int suppress_bgerror; /* bgerror returned break, so don't call it again */ 103} Jim_EventLoop; 104 105static void JimAfterTimeHandler(Jim_Interp *interp, void *clientData); 106static void JimAfterTimeEventFinalizer(Jim_Interp *interp, void *clientData); 107 108int Jim_EvalObjBackground(Jim_Interp *interp, Jim_Obj *scriptObjPtr) 109{ 110 Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop"); 111 Jim_CallFrame *savedFramePtr; 112 int retval; 113 114 savedFramePtr = interp->framePtr; 115 interp->framePtr = interp->topFramePtr; 116 retval = Jim_EvalObj(interp, scriptObjPtr); 117 interp->framePtr = savedFramePtr; 118 /* Try to report the error (if any) via the bgerror proc */ 119 if (retval != JIM_OK && !eventLoop->suppress_bgerror) { 120 Jim_Obj *objv[2]; 121 int rc = JIM_ERR; 122 123 objv[0] = Jim_NewStringObj(interp, "bgerror", -1); 124 objv[1] = Jim_GetResult(interp); 125 Jim_IncrRefCount(objv[0]); 126 Jim_IncrRefCount(objv[1]); 127 if (Jim_GetCommand(interp, objv[0], JIM_NONE) == NULL || (rc = Jim_EvalObjVector(interp, 2, objv)) != JIM_OK) { 128 if (rc == JIM_BREAK) { 129 /* No more bgerror calls */ 130 eventLoop->suppress_bgerror++; 131 } 132 else { 133 /* Report the error to stderr. */ 134 Jim_MakeErrorMessage(interp); 135 fprintf(stderr, "%s\n", Jim_String(Jim_GetResult(interp))); 136 /* And reset the result */ 137 Jim_SetResultString(interp, "", -1); 138 } 139 } 140 Jim_DecrRefCount(interp, objv[0]); 141 Jim_DecrRefCount(interp, objv[1]); 142 } 143 return retval; 144} 145 146 147void Jim_CreateFileHandler(Jim_Interp *interp, FILE * handle, int mask, 148 Jim_FileProc * proc, void *clientData, Jim_EventFinalizerProc * finalizerProc) 149{ 150 Jim_FileEvent *fe; 151 Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop"); 152 153 fe = Jim_Alloc(sizeof(*fe)); 154 fe->handle = handle; 155 fe->mask = mask; 156 fe->fileProc = proc; 157 fe->finalizerProc = finalizerProc; 158 fe->clientData = clientData; 159 fe->next = eventLoop->fileEventHead; 160 eventLoop->fileEventHead = fe; 161} 162 163void Jim_DeleteFileHandler(Jim_Interp *interp, FILE * handle) 164{ 165 Jim_FileEvent *fe, *prev = NULL; 166 Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop"); 167 168 fe = eventLoop->fileEventHead; 169 while (fe) { 170 if (fe->handle == handle) { 171 if (prev == NULL) 172 eventLoop->fileEventHead = fe->next; 173 else 174 prev->next = fe->next; 175 if (fe->finalizerProc) 176 fe->finalizerProc(interp, fe->clientData); 177 Jim_Free(fe); 178 return; 179 } 180 prev = fe; 181 fe = fe->next; 182 } 183} 184 185static void JimGetTime(long *seconds, long *milliseconds) 186{ 187 struct timeval tv; 188 189 gettimeofday(&tv, NULL); 190 *seconds = tv.tv_sec; 191 *milliseconds = tv.tv_usec / 1000; 192} 193 194jim_wide Jim_CreateTimeHandler(Jim_Interp *interp, jim_wide milliseconds, 195 Jim_TimeProc * proc, void *clientData, Jim_EventFinalizerProc * finalizerProc) 196{ 197 Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop"); 198 jim_wide id = eventLoop->timeEventNextId++; 199 Jim_TimeEvent *te, *e, *prev; 200 long cur_sec, cur_ms; 201 202 JimGetTime(&cur_sec, &cur_ms); 203 204 te = Jim_Alloc(sizeof(*te)); 205 te->id = id; 206 te->mode = 0; 207 te->initialms = milliseconds; 208 te->when_sec = cur_sec + milliseconds / 1000; 209 te->when_ms = cur_ms + milliseconds % 1000; 210 if (te->when_ms >= 1000) { 211 te->when_sec++; 212 te->when_ms -= 1000; 213 } 214 te->timeProc = proc; 215 te->finalizerProc = finalizerProc; 216 te->clientData = clientData; 217 218 /* Add to the appropriate place in the list */ 219 if (eventLoop->timeEventHead) { 220 prev = NULL; 221 for (e = eventLoop->timeEventHead; e; e = e->next) { 222 if (te->when_sec < e->when_sec || (te->when_sec == e->when_sec && te->when_ms < e->when_ms)) { 223 break; 224 } 225 prev = e; 226 } 227 if (prev) { 228 te->next = prev->next; 229 prev->next = te; 230 return id; 231 } 232 } 233 234 te->next = eventLoop->timeEventHead; 235 eventLoop->timeEventHead = te; 236 237 return id; 238} 239 240static jim_wide JimParseAfterId(Jim_Obj *idObj) 241{ 242 int len; 243 const char *tok = Jim_GetString(idObj, &len); 244 jim_wide id; 245 246 if (strncmp(tok, "after#", 6) == 0 && Jim_StringToWide(tok + 6, &id, 10) == JIM_OK) { 247 /* Got an event by id */ 248 return id; 249 } 250 return -1; 251} 252 253static jim_wide JimFindAfterByScript(Jim_EventLoop *eventLoop, Jim_Obj *scriptObj) 254{ 255 Jim_TimeEvent *te; 256 257 for (te = eventLoop->timeEventHead; te; te = te->next) { 258 /* Is this an 'after' event? */ 259 if (te->timeProc == JimAfterTimeHandler) { 260 if (Jim_StringEqObj(scriptObj, te->clientData)) { 261 return te->id; 262 } 263 } 264 } 265 return -1; /* NO event with the specified ID found */ 266} 267 268static Jim_TimeEvent *JimFindTimeHandlerById(Jim_EventLoop *eventLoop, jim_wide id) 269{ 270 Jim_TimeEvent *te; 271 272 for (te = eventLoop->timeEventHead; te; te = te->next) { 273 if (te->id == id) { 274 return te; 275 } 276 } 277 return NULL; 278} 279 280static Jim_TimeEvent *Jim_RemoveTimeHandler(Jim_EventLoop *eventLoop, jim_wide id) 281{ 282 Jim_TimeEvent *te, *prev = NULL; 283 284 for (te = eventLoop->timeEventHead; te; te = te->next) { 285 if (te->id == id) { 286 if (prev == NULL) 287 eventLoop->timeEventHead = te->next; 288 else 289 prev->next = te->next; 290 return te; 291 } 292 prev = te; 293 } 294 return NULL; 295} 296 297static void Jim_FreeTimeHandler(Jim_Interp *interp, Jim_TimeEvent *te) 298{ 299 if (te->finalizerProc) 300 te->finalizerProc(interp, te->clientData); 301 Jim_Free(te); 302} 303 304jim_wide Jim_DeleteTimeHandler(Jim_Interp *interp, jim_wide id) 305{ 306 Jim_TimeEvent *te; 307 Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop"); 308 309 if (id >= eventLoop->timeEventNextId) { 310 return -2; /* wrong event ID */ 311 } 312 313 te = Jim_RemoveTimeHandler(eventLoop, id); 314 if (te) { 315 jim_wide remain; 316 long cur_sec, cur_ms; 317 318 JimGetTime(&cur_sec, &cur_ms); 319 320 remain = (te->when_sec - cur_sec) * 1000; 321 remain += (te->when_ms - cur_ms); 322 remain = (remain < 0) ? 0 : remain; 323 324 Jim_FreeTimeHandler(interp, te); 325 return remain; 326 } 327 return -1; /* NO event with the specified ID found */ 328} 329 330/* --- POSIX version of Jim_ProcessEvents, for now the only available --- */ 331 332/* Process every pending time event, then every pending file event 333 * (that may be registered by time event callbacks just processed). 334 * Without special flags the function sleeps until some file event 335 * fires, or when the next time event occurrs (if any). 336 * 337 * If flags is 0, the function does nothing and returns. 338 * if flags has JIM_ALL_EVENTS set, all the kind of events are processed. 339 * if flags has JIM_FILE_EVENTS set, file events are processed. 340 * if flags has JIM_TIME_EVENTS set, time events are processed. 341 * if flags has JIM_DONT_WAIT set the function returns ASAP until all 342 * the events that's possible to process without to wait are processed. 343 * 344 * The function returns the number of events processed or -1 if 345 * there are no matching handlers, or -2 on error. 346 */ 347int Jim_ProcessEvents(Jim_Interp *interp, int flags) 348{ 349 jim_wide sleep_ms = -1; 350 int processed = 0; 351 Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop"); 352 Jim_FileEvent *fe = eventLoop->fileEventHead; 353 Jim_TimeEvent *te; 354 jim_wide maxId; 355 356 if ((flags & JIM_FILE_EVENTS) == 0 || fe == NULL) { 357 /* No file events */ 358 if ((flags & JIM_TIME_EVENTS) == 0 || eventLoop->timeEventHead == NULL) { 359 /* No time events */ 360 return -1; 361 } 362 } 363 364 /* Note that we want call select() even if there are no 365 * file events to process as long as we want to process time 366 * events, in order to sleep until the next time event is ready 367 * to fire. */ 368 369 if (flags & JIM_DONT_WAIT) { 370 /* Wait no time */ 371 sleep_ms = 0; 372 } 373 else if (flags & JIM_TIME_EVENTS) { 374 /* The nearest timer is always at the head of the list */ 375 if (eventLoop->timeEventHead) { 376 Jim_TimeEvent *shortest = eventLoop->timeEventHead; 377 long now_sec, now_ms; 378 379 /* Calculate the time missing for the nearest 380 * timer to fire. */ 381 JimGetTime(&now_sec, &now_ms); 382 sleep_ms = 1000 * (shortest->when_sec - now_sec) + (shortest->when_ms - now_ms); 383 if (sleep_ms < 0) { 384 sleep_ms = 1; 385 } 386 } 387 else { 388 /* Wait forever */ 389 sleep_ms = -1; 390 } 391 } 392 393#ifdef HAVE_SELECT 394 if (flags & JIM_FILE_EVENTS) { 395 int retval; 396 struct timeval tv, *tvp = NULL; 397 fd_set rfds, wfds, efds; 398 int maxfd = -1; 399 400 FD_ZERO(&rfds); 401 FD_ZERO(&wfds); 402 FD_ZERO(&efds); 403 404 /* Check file events */ 405 while (fe != NULL) { 406 int fd = fileno(fe->handle); 407 408 if (fe->mask & JIM_EVENT_READABLE) 409 FD_SET(fd, &rfds); 410 if (fe->mask & JIM_EVENT_WRITABLE) 411 FD_SET(fd, &wfds); 412 if (fe->mask & JIM_EVENT_EXCEPTION) 413 FD_SET(fd, &efds); 414 if (maxfd < fd) 415 maxfd = fd; 416 fe = fe->next; 417 } 418 419 if (sleep_ms >= 0) { 420 tvp = &tv; 421 tvp->tv_sec = sleep_ms / 1000; 422 tvp->tv_usec = 1000 * (sleep_ms % 1000); 423 } 424 425 retval = select(maxfd + 1, &rfds, &wfds, &efds, tvp); 426 427 if (retval < 0) { 428 if (errno == EINVAL) { 429 /* This can happen on mingw32 if a non-socket filehandle is passed */ 430 Jim_SetResultString(interp, "non-waitable filehandle", -1); 431 return -2; 432 } 433 /* XXX: What about EINTR? */ 434 } 435 else if (retval > 0) { 436 fe = eventLoop->fileEventHead; 437 while (fe != NULL) { 438 int fd = fileno(fe->handle); 439 int mask = 0; 440 441 if ((fe->mask & JIM_EVENT_READABLE) && FD_ISSET(fd, &rfds)) 442 mask |= JIM_EVENT_READABLE; 443 if (fe->mask & JIM_EVENT_WRITABLE && FD_ISSET(fd, &wfds)) 444 mask |= JIM_EVENT_WRITABLE; 445 if (fe->mask & JIM_EVENT_EXCEPTION && FD_ISSET(fd, &efds)) 446 mask |= JIM_EVENT_EXCEPTION; 447 448 if (mask) { 449 if (fe->fileProc(interp, fe->clientData, mask) != JIM_OK) { 450 /* Remove the element on handler error */ 451 Jim_DeleteFileHandler(interp, fe->handle); 452 } 453 processed++; 454 /* After an event is processed our file event list 455 * may no longer be the same, so what we do 456 * is to clear the bit for this file descriptor and 457 * restart again from the head. */ 458 fe = eventLoop->fileEventHead; 459 FD_CLR(fd, &rfds); 460 FD_CLR(fd, &wfds); 461 FD_CLR(fd, &efds); 462 } 463 else { 464 fe = fe->next; 465 } 466 } 467 } 468 } 469#else 470 if (sleep_ms > 0) { 471 msleep(sleep_ms); 472 } 473#endif 474 475 /* Check time events */ 476 te = eventLoop->timeEventHead; 477 maxId = eventLoop->timeEventNextId - 1; 478 while (te) { 479 long now_sec, now_ms; 480 jim_wide id; 481 482 if (te->id > maxId) { 483 te = te->next; 484 continue; 485 } 486 JimGetTime(&now_sec, &now_ms); 487 if (now_sec > te->when_sec || (now_sec == te->when_sec && now_ms >= te->when_ms)) { 488 id = te->id; 489 /* Remove from the list before executing */ 490 Jim_RemoveTimeHandler(eventLoop, id); 491 te->timeProc(interp, te->clientData); 492 /* After an event is processed our time event list may 493 * no longer be the same, so we restart from head. 494 * Still we make sure to don't process events registered 495 * by event handlers itself in order to don't loop forever 496 * even in case an [after 0] that continuously register 497 * itself. To do so we saved the max ID we want to handle. */ 498 Jim_FreeTimeHandler(interp, te); 499 500 te = eventLoop->timeEventHead; 501 processed++; 502 } 503 else { 504 te = te->next; 505 } 506 } 507 508 return processed; 509} 510 511/* ---------------------------------------------------------------------- */ 512 513static void JimELAssocDataDeleProc(Jim_Interp *interp, void *data) 514{ 515 void *next; 516 Jim_FileEvent *fe; 517 Jim_TimeEvent *te; 518 Jim_EventLoop *eventLoop = data; 519 520 fe = eventLoop->fileEventHead; 521 while (fe) { 522 next = fe->next; 523 if (fe->finalizerProc) 524 fe->finalizerProc(interp, fe->clientData); 525 Jim_Free(fe); 526 fe = next; 527 } 528 529 te = eventLoop->timeEventHead; 530 while (te) { 531 next = te->next; 532 if (te->finalizerProc) 533 te->finalizerProc(interp, te->clientData); 534 Jim_Free(te); 535 te = next; 536 } 537 Jim_Free(data); 538} 539 540static int JimELVwaitCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 541{ 542 Jim_EventLoop *eventLoop = Jim_CmdPrivData(interp); 543 Jim_Obj *oldValue; 544 int rc; 545 546 if (argc != 2) { 547 Jim_WrongNumArgs(interp, 1, argv, "name"); 548 return JIM_ERR; 549 } 550 551 oldValue = Jim_GetGlobalVariable(interp, argv[1], JIM_NONE); 552 if (oldValue) { 553 Jim_IncrRefCount(oldValue); 554 } 555 else { 556 /* If a result was left, it is an error */ 557 int len; 558 Jim_GetString(interp->result, &len); 559 if (len) { 560 return JIM_ERR; 561 } 562 } 563 564 eventLoop->suppress_bgerror = 0; 565 566 while ((rc = Jim_ProcessEvents(interp, JIM_ALL_EVENTS)) >= 0) { 567 Jim_Obj *currValue; 568 currValue = Jim_GetGlobalVariable(interp, argv[1], JIM_NONE); 569 /* Stop the loop if the vwait-ed variable changed value, 570 * or if was unset and now is set (or the contrary). */ 571 if ((oldValue && !currValue) || 572 (!oldValue && currValue) || 573 (oldValue && currValue && !Jim_StringEqObj(oldValue, currValue))) 574 break; 575 } 576 if (oldValue) 577 Jim_DecrRefCount(interp, oldValue); 578 579 580 if (rc == -2) { 581 return JIM_ERR; 582 } 583 584 Jim_SetEmptyResult(interp); 585 return JIM_OK; 586} 587 588static int JimELUpdateCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 589{ 590 Jim_EventLoop *eventLoop = Jim_CmdPrivData(interp); 591 static const char * const options[] = { 592 "idletasks", NULL 593 }; 594 enum { UPDATE_IDLE, UPDATE_NONE }; 595 int option = UPDATE_NONE; 596 int flags = JIM_TIME_EVENTS; 597 598 if (argc == 1) { 599 flags = JIM_ALL_EVENTS; 600 } 601 else if (argc > 2 || Jim_GetEnum(interp, argv[1], options, &option, NULL, JIM_ERRMSG | JIM_ENUM_ABBREV) != JIM_OK) { 602 Jim_WrongNumArgs(interp, 1, argv, "?idletasks?"); 603 return JIM_ERR; 604 } 605 606 eventLoop->suppress_bgerror = 0; 607 608 while (Jim_ProcessEvents(interp, flags | JIM_DONT_WAIT) > 0) { 609 } 610 611 return JIM_OK; 612} 613 614static void JimAfterTimeHandler(Jim_Interp *interp, void *clientData) 615{ 616 Jim_Obj *objPtr = clientData; 617 618 Jim_EvalObjBackground(interp, objPtr); 619} 620 621static void JimAfterTimeEventFinalizer(Jim_Interp *interp, void *clientData) 622{ 623 Jim_Obj *objPtr = clientData; 624 625 Jim_DecrRefCount(interp, objPtr); 626} 627 628static int JimELAfterCommand(Jim_Interp *interp, int argc, Jim_Obj *const *argv) 629{ 630 Jim_EventLoop *eventLoop = Jim_CmdPrivData(interp); 631 jim_wide ms = 0, id; 632 Jim_Obj *objPtr, *idObjPtr; 633 static const char * const options[] = { 634 "cancel", "info", "idle", NULL 635 }; 636 enum 637 { AFTER_CANCEL, AFTER_INFO, AFTER_IDLE, AFTER_RESTART, AFTER_EXPIRE, AFTER_CREATE }; 638 int option = AFTER_CREATE; 639 640 if (argc < 2) { 641 Jim_WrongNumArgs(interp, 1, argv, "option ?arg ...?"); 642 return JIM_ERR; 643 } 644 if (Jim_GetWide(interp, argv[1], &ms) != JIM_OK) { 645 if (Jim_GetEnum(interp, argv[1], options, &option, "argument", JIM_ERRMSG) != JIM_OK) { 646 return JIM_ERR; 647 } 648 Jim_SetEmptyResult(interp); 649 } 650 else if (argc == 2) { 651 /* Simply a sleep */ 652 msleep(ms); 653 return JIM_OK; 654 } 655 656 switch (option) { 657 case AFTER_IDLE: 658 if (argc < 3) { 659 Jim_WrongNumArgs(interp, 2, argv, "script ?script ...?"); 660 return JIM_ERR; 661 } 662 /* fall through */ 663 case AFTER_CREATE: { 664 Jim_Obj *scriptObj = Jim_ConcatObj(interp, argc - 2, argv + 2); 665 Jim_IncrRefCount(scriptObj); 666 id = Jim_CreateTimeHandler(interp, ms, JimAfterTimeHandler, scriptObj, 667 JimAfterTimeEventFinalizer); 668 objPtr = Jim_NewStringObj(interp, NULL, 0); 669 Jim_AppendString(interp, objPtr, "after#", -1); 670 idObjPtr = Jim_NewIntObj(interp, id); 671 Jim_IncrRefCount(idObjPtr); 672 Jim_AppendObj(interp, objPtr, idObjPtr); 673 Jim_DecrRefCount(interp, idObjPtr); 674 Jim_SetResult(interp, objPtr); 675 return JIM_OK; 676 } 677 case AFTER_CANCEL: 678 if (argc < 3) { 679 Jim_WrongNumArgs(interp, 2, argv, "id|command"); 680 return JIM_ERR; 681 } 682 else { 683 jim_wide remain = 0; 684 685 id = JimParseAfterId(argv[2]); 686 if (id < 0) { 687 /* Not an event id, so search by script */ 688 Jim_Obj *scriptObj = Jim_ConcatObj(interp, argc - 2, argv + 2); 689 id = JimFindAfterByScript(eventLoop, scriptObj); 690 Jim_FreeNewObj(interp, scriptObj); 691 if (id < 0) { 692 /* Not found */ 693 break; 694 } 695 } 696 remain = Jim_DeleteTimeHandler(interp, id); 697 if (remain >= 0) { 698 Jim_SetResultInt(interp, remain); 699 } 700 } 701 break; 702 703 case AFTER_INFO: 704 if (argc == 2) { 705 Jim_TimeEvent *te = eventLoop->timeEventHead; 706 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0); 707 char buf[30]; 708 const char *fmt = "after#%" JIM_WIDE_MODIFIER; 709 710 while (te) { 711 snprintf(buf, sizeof(buf), fmt, te->id); 712 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, buf, -1)); 713 te = te->next; 714 } 715 Jim_SetResult(interp, listObj); 716 } 717 else if (argc == 3) { 718 id = JimParseAfterId(argv[2]); 719 if (id >= 0) { 720 Jim_TimeEvent *e = JimFindTimeHandlerById(eventLoop, id); 721 if (e && e->timeProc == JimAfterTimeHandler) { 722 Jim_Obj *listObj = Jim_NewListObj(interp, NULL, 0); 723 Jim_ListAppendElement(interp, listObj, e->clientData); 724 Jim_ListAppendElement(interp, listObj, Jim_NewStringObj(interp, e->initialms ? "timer" : "idle", -1)); 725 Jim_SetResult(interp, listObj); 726 return JIM_OK; 727 } 728 } 729 Jim_SetResultFormatted(interp, "event \"%#s\" doesn't exist", argv[2]); 730 return JIM_ERR; 731 } 732 else { 733 Jim_WrongNumArgs(interp, 2, argv, "?id?"); 734 return JIM_ERR; 735 } 736 break; 737 } 738 return JIM_OK; 739} 740 741int Jim_eventloopInit(Jim_Interp *interp) 742{ 743 Jim_EventLoop *eventLoop; 744 745 if (Jim_PackageProvide(interp, "eventloop", "1.0", JIM_ERRMSG)) 746 return JIM_ERR; 747 748 eventLoop = Jim_Alloc(sizeof(*eventLoop)); 749 eventLoop->fileEventHead = NULL; 750 eventLoop->timeEventHead = NULL; 751 eventLoop->timeEventNextId = 1; 752 eventLoop->suppress_bgerror = 0; 753 Jim_SetAssocData(interp, "eventloop", JimELAssocDataDeleProc, eventLoop); 754 755 Jim_CreateCommand(interp, "vwait", JimELVwaitCommand, eventLoop, NULL); 756 Jim_CreateCommand(interp, "update", JimELUpdateCommand, eventLoop, NULL); 757 Jim_CreateCommand(interp, "after", JimELAfterCommand, eventLoop, NULL); 758 759 return JIM_OK; 760} 761