source of events doesn't always trigger event:read. An example might
be a stream that supports "unget". In such a case there may be
content to get, but not from the file descriptor.
+ comm2 should return a positive number if it did anything, and 0 if
+ if there was nothing to do this time.
+
+- event:on-idle - The comm2 command will be called soon after the
+ current event completes. The command will only be called once and
+ must be explicitly rescheduled if it is wanted again.
+ 'num' is a priority level.
+
+ 0/ is for background tasks. Only one of these is run before checking
+ for regular events.
+ 1/ is for pane_refresh(), and maybe similar tasks. It is probably
+ needed every time around the loop, and does non-trivial work
+ 2/ is for simple high priority tasks like freeing memory that was in
+ using during the previous event.
- event:free - Any register event from the focus pane which is supposed
to call the given comm2 will be deactivated. If no comm2 command is
enum {
EV_LIST, /* Events handled by libevent */
POLL_LIST, /* Events to poll before calling event_base_loop */
+ PRIO_0_LIST, /* background task - run one per loop */
+ PRIO_1_LIST, /* non-trivial follow-up tasks, line pane_refresh() */
+ PRIO_2_LIST, /* fast follow-up tasks like freeing memory */
NR_LISTS
};
struct pane *home safe;
int dont_block;
int deactivated;
- struct command read, write, signal, timer, poll, run, deactivate,
- free, refresh, noblock;
+ struct command read, write, signal, timer, poll, on_idle,
+ run, deactivate, free, refresh, noblock;
};
struct evt {
return 1;
}
-static int run_list(struct event_info *ei safe, int list, char *cb safe)
+DEF_CMD(libevent_on_idle)
+{
+ struct event_info *ei = container_of(ci->comm, struct event_info, on_idle);
+ struct evt *ev;
+ int prio = ci->num;
+
+ if (!ci->comm2)
+ return Enoarg;
+
+ ev = malloc(sizeof(*ev));
+
+ if (!ei->base)
+ ei->base = event_base_new();
+
+ ev->home = ci->focus;
+ pane_add_notify(ei->home, ev->home, "Notify:Close");
+ ev->comm = command_get(ci->comm2);
+ ev->active = 0;
+ ev->event = "event:on-idle";
+ if (prio < 0)
+ prio = 0;
+ if (prio > 2)
+ prio = 2;
+ ev->num = prio;
+ list_add(&ev->lst, &ei->event_list[PRIO_0_LIST + prio]);
+ return 1;
+}
+
+static int run_list(struct event_info *ei safe, int list, char *cb safe,
+ bool stop_on_first)
{
bool dont_block = False;
struct evt *ev;
ev->active = 1;
if (comm_call(ev->comm, cb, ev->home, ev->num) >= 1)
dont_block = True;
- if (ev->active == 2) {
+ if (ev->active == 2 || list >= PRIO_0_LIST) {
list_del(&ev->lst);
command_put(ev->comm);
free(ev);
break;
} else
ev->active = 0;
- if (dont_block)
+ if (dont_block && stop_on_first)
/* Other things might have been removed from list */
break;
}
}
/* First run any 'poll' events */
- if (run_list(ei, POLL_LIST, "callback:poll"))
+ if (run_list(ei, POLL_LIST, "callback:poll", True))
dont_block = True;
+ for (i = PRIO_0_LIST ; i <= PRIO_2_LIST; i++)
+ if (!list_empty(&ei->event_list[i]))
+ dont_block = 1;
+
/* Disable any alarm set by python (or other interpreter) */
alarm(0);
event_base_loop(b, EVLOOP_ONCE | (dont_block ? EVLOOP_NONBLOCK : 0));
+
+ time_start(TIME_IDLE);
+ /* Prio 2 comes first - unconditional */
+ run_list(ei, PRIO_2_LIST, "callback:on-idle", False);
+ /* Now prio1 */
+ run_list(ei, PRIO_1_LIST, "callback:on-idle", False);
+ /* Repeat PRIO_2 just in case */
+ run_list(ei, PRIO_2_LIST, "callback:on-idle", False);
+ /* And do one background task */
+ run_list(ei, PRIO_0_LIST, "callback:on-idle", True);
+ time_stop(TIME_IDLE);
+
+ /* Check if we have been deactivated. */
if (ei->base == b)
return 1;
+
for (i = 0 ; i < NR_LISTS; i++) {
while (!list_empty(&ei->event_list[i])) {
ev = list_first_entry(&ei->event_list[i], struct evt, lst);
ei->signal = libevent_signal;
ei->timer = libevent_timer;
ei->poll = libevent_poll;
+ ei->on_idle = libevent_on_idle;
ei->run = libevent_run;
ei->deactivate = libevent_deactivate;
ei->free = libevent_free;
0, NULL, "event:timer-zz");
call_comm("global-set-command", ci->focus, &ei->poll,
0, NULL, "event:poll-zz");
+ call_comm("global-set-command", ci->focus, &ei->on_idle,
+ 0, NULL, "event:on-idle-zz");
call_comm("global-set-command", ci->focus, &ei->run,
0, NULL, "event:run-zz");
call_comm("global-set-command", ci->focus, &ei->deactivate,
self.events = {}
self.sigs = {}
self.poll_list = []
+ self.idle_list = [ [], [], [] ]
self.ev_num = 0
self.dont_block = False
self.maxloops = 10
ev = self.add_ev(focus, comm2, 'event:poll', -2)
self.poll_list.append(ev)
+ def on_idle(self, key, focus, num, comm2, **a):
+ if num < 0:
+ num = 0
+ if num > 2:
+ num = 2
+ ev = self.add_ev(focus, comm2, 'event:on-idle', num)
+ self.idle_list[num].append(ev)
+
def dotimeout(self, comm2, focus, ev):
if ev not in self.events:
return False
def nonblock(self, key, **a):
self.dont_block = True
+ def run_idle_list(self, prio, just_one):
+ while self.idle_list[prio]:
+ s = self.idle_list[prio].pop()
+ f,c,e,n = self.events[s]
+ if c("callback:on-idle", f, n) > 0:
+ if just_one:
+ return
+
def run(self, key, **a):
if self.active:
dont_block = self.dont_block
self.dont_block = False
for s in self.poll_list:
f,c,e,n = self.events[s]
- if c("callback:poll", f, -1) > 0:
+ if c("callback:poll", f, n) > 0:
dont_block = True
if not dont_block:
# Disable any alarm set by python (or other interpreter)
signal.alarm(0)
Gtk.main_iteration_do(False)
events += 1
+ edlib.time_start(edlib.TIME_IDLE)
+ self.run_idle_list(2, False)
+ self.run_idle_list(1, False)
+ self.run_idle_list(2, False)
+ self.run_idle_list(0, True)
+ edlib.time_stop(edlib.TIME_IDLE)
if self.active:
return 1
else:
pass
if source in self.poll_list:
self.poll_list.remove(source)
+ for i in range(0,3):
+ if source in self.idle_list[i]:
+ self.idle_list[i].remove(source)
try_again = True
break
focus.call("global-set-command", "event:signal-python", ev.signal)
focus.call("global-set-command", "event:timer-python", ev.timer)
focus.call("global-set-command", "event:poll-python", ev.poll)
+ focus.call("global-set-command", "event:on-idle-python", ev.on_idle)
focus.call("global-set-command", "event:run-python", ev.run)
focus.call("global-set-command", "event:deactivate-python", ev.deactivate)
focus.call("global-set-command", "event:free-python", ev.free)