Index: src/npw-viewer.c =================================================================== --- src/npw-viewer.c (révision 645) +++ src/npw-viewer.c (copie de travail) @@ -163,6 +163,23 @@ CorePart core; } WidgetRec, CoreRec; +typedef struct _TimerEventRec { + struct timeval te_timer_value; + struct _TimerEventRec *te_next; + XtTimerCallbackProc te_proc; + XtAppContext app; + XtPointer te_closure; +} TimerEventRec; + +struct _XtAppStruct { + XtAppContext next; /* link to next app in process context */ + void *process; /* back pointer to our process context */ + void *destroy_callbacks; + Display **list; + TimerEventRec *timerQueue; + /* ... don't care about other members */ +}; + extern void XtResizeWidget( Widget /* widget */, _XtDimension /* width */, @@ -3151,18 +3168,84 @@ // Xt events static GPollFD xt_event_poll_fd; +static const int XT_MAX_TIMEOUT = 25; +static const int XT_MAX_EVENTS = 5; +static void xt_has_compatible_appcontext_cb(XtPointer closure, XtIntervalId *id) +{ + /* dummy function, never called */ +} + +static int xt_has_compatible_appcontext(void) +{ + int is_compatible; + XtIntervalId id; + TimerEventRec *tq, *tq_probe; + + /* XXX: poor man's attempt to guess the pointer to the next + allocated TimerEventRec. Besides, we MUST be the first to call + XtAppAddTimeOut() so that to be sure any "free" TimerEventRec + pointer cache is empty. */ + tq = XtNew(TimerEventRec); + XtFree((char *)tq); + tq_probe = XtNew(TimerEventRec); + XtFree((char *)tq_probe); + if (tq != tq_probe) + return 0; + + id = XtAppAddTimeOut(x_app_context, 0, + xt_has_compatible_appcontext_cb, + GUINT_TO_POINTER(0xdeadbeef)); + + tq = x_app_context->timerQueue; + is_compatible = tq == tq_probe + && tq->app == x_app_context + && tq->te_proc == xt_has_compatible_appcontext_cb + && tq->te_closure == GUINT_TO_POINTER(0xdeadbeef) + ; + + XtRemoveTimeOut(id); + return is_compatible; +} + +static int xt_get_next_timeout(GSource *source) +{ + static int has_compatible_appcontext = -1; + if (has_compatible_appcontext < 0) { + if ((has_compatible_appcontext = xt_has_compatible_appcontext()) == 0) + npw_printf("WARNING: xt_get_next_timeout() is not optimizable\n"); + } + if (has_compatible_appcontext) { + /* We can block if there is no timer registered */ + if (x_app_context->timerQueue == NULL) + return -1; + /* Determine delay to next timeout. Zero means timeout expired */ + struct timeval *next = &x_app_context->timerQueue->te_timer_value; + GTimeVal now; + int64_t diff; + g_source_get_current_time(source, &now); + if ((diff = (int64_t)next->tv_sec - (int64_t)now.tv_sec) < 0) + return 0; + if ((diff = diff*1000 + ((int64_t)next->tv_usec - (int64_t)now.tv_usec)/1000) <= 0) + return 0; + return diff; + } + return XT_MAX_TIMEOUT; +} + static gboolean xt_event_prepare(GSource *source, gint *timeout) { int mask = XtAppPending(x_app_context); - return mask & XtIMXEvent; + if (mask) + return TRUE; + return (*timeout = xt_get_next_timeout(source)) == 0; } static gboolean xt_event_check(GSource *source) { if (xt_event_poll_fd.revents & G_IO_IN) { int mask = XtAppPending(x_app_context); - if (mask & XtIMXEvent) + if (mask) return TRUE; } return FALSE; @@ -3171,11 +3254,13 @@ static gboolean xt_event_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) { int i; - for (i = 0; i < 5; i++) { + /* Process only a few events here so that to give some time to other + sources as well (e.g. RPC) */ + for (i = 0; i < XT_MAX_EVENTS; i++) { int mask = XtAppPending(x_app_context); - if ((mask & XtIMXEvent) == 0) + if (mask == 0) break; - XtAppProcessEvent(x_app_context, XtIMXEvent); + XtAppProcessEvent(x_app_context, XtIMAll); } return TRUE; } @@ -3189,17 +3274,6 @@ (GSourceDummyMarshal)NULL }; -static gboolean xt_event_polling_timer_callback(gpointer user_data) -{ - int i; - for (i = 0; i < 5; i++) { - if ((XtAppPending(x_app_context) & (XtIMAll & ~XtIMXEvent)) == 0) - break; - XtAppProcessEvent(x_app_context, XtIMAll & ~XtIMXEvent); - } - return TRUE; -} - // RPC events static GPollFD rpc_event_poll_fd; @@ -3316,10 +3393,6 @@ xt_event_poll_fd.revents = 0; g_source_add_poll(xt_source, &xt_event_poll_fd); - gint xt_polling_timer_id = g_timeout_add(25, - xt_event_polling_timer_callback, - NULL); - // Initialize RPC events listener GSource *rpc_source = g_source_new(&rpc_event_funcs, sizeof(GSource)); if (rpc_source == NULL) { @@ -3337,7 +3410,6 @@ gtk_main(); D(bug("--- EXIT ---\n")); - g_source_remove(xt_polling_timer_id); g_source_destroy(rpc_source); g_source_destroy(xt_source);