Commit ddee6515 authored by Jan Djärv's avatar Jan Djärv

Improve event loop on NS so that no polling is used.

* nsmenu.m (popupSession): Remove.
(pop_down_menu): Remove endModalSession.
(timeout_handler:): New method.
(runDialogAt:): Get next timeout.  Start a NSTimer with that timeout.
Call runModalForWindow.  Check timer_fired when it returns.
If not set, cancel timer and break out of loop.
Otherwise loop again, with a new timeout.

* nsterm.h (EmacsApp): fd_handler takes id argument.
(EmacsDialogPanel): Add timer_fired and timeout_handler.

* nsterm.m: Include fcntl.h if present.
(fd_entry, t_readfds, inNsSelect): Remove.
(select_writefds, select_valid, select_timeout, selfds)
(select_mutex, apploopnr): Add.
(EV_TRAILER): Call kbd_buffer_store_event_hold only if q_event_ptr.
Otherwise call kbd_buffer_store_event.
(ns_send_appdefined): Remove release of fd_entry.
(ns_read_socket): Always send appdefined.  Remove inNsSelect check.
Increment and decrement apploopnr.
(ns_select): If no file descriptors, just do a NSTimer.
Otherwise copy read/write masks and start select thread (fd_handler).
Start main loop and wait for application defined event.
Inform select thread to stop selecting after main loop is exited.
(ns_term_init): Create selfds pipe and set non-blocking.
Initialize select_mutex. Start the select thread (fd_handler).
(fd_handler:): Loop forever, wait for info from the main thread
to either start or stop selecting.  When select returns, send
and appdefined event.
(sendScrollEventAtLoc:fromEvent:): Check if q_event_ptr is set.
If not call kbd_buffer_store_event.
parent ac4845a6
2012-08-15 Jan Djärv <jan.h.d@swipnet.se>
* nsmenu.m (popupSession): Remove.
(pop_down_menu): Remove endModalSession.
(timeout_handler:): New method.
(runDialogAt:): Get next timeout. Start a NSTimer with that timeout.
Call runModalForWindow. Check timer_fired when it returns.
If not set, cancel timer and break out of loop.
Otherwise loop again, with a new timeout.
* nsterm.m: Include fcntl.h if present.
(fd_entry, t_readfds, inNsSelect): Remove.
(select_writefds, select_valid, select_timeout, selfds)
(select_mutex, apploopnr): Add.
(EV_TRAILER): Call kbd_buffer_store_event_hold only if q_event_ptr.
Otherwise call kbd_buffer_store_event.
(ns_send_appdefined): Remove release of fd_entry.
(ns_read_socket): Always send appdefined. Remove inNsSelect check.
Increment and decrement apploopnr.
(ns_select): If no file descriptors, just do a NSTimer.
Otherwise copy read/write masks and start select thread (fd_handler).
Start main loop and wait for application defined event.
Inform select thread to stop selecting after main loop is exited.
(ns_term_init): Create selfds pipe and set non-blocking.
Initialize select_mutex. Start the select thread (fd_handler).
(fd_handler:): Loop forever, wait for info from the main thread
to either start or stop selecting. When select returns, send
and appdefined event.
(sendScrollEventAtLoc:fromEvent:): Check if q_event_ptr is set.
If not call kbd_buffer_store_event.
* nsterm.h (EmacsApp): fd_handler takes id argument.
(EmacsDialogPanel): Add timer_fired and timeout_handler.
* gtkutil.c (xg_mark_data): Use FRAME_X_P.
2012-08-15 Eli Zaretskii <eliz@gnu.org>
......
......@@ -73,7 +73,6 @@
/* Nonzero means a menu is currently active. */
static int popup_activated_flag;
static NSModalSession popupSession;
/* Nonzero means we are tracking and updating menus. */
static int trackingMenu;
......@@ -1365,8 +1364,6 @@ - (NSRect) frame
{
EmacsDialogPanel *panel = unwind_data->dialog;
popup_activated_flag = 0;
[NSApp endModalSession: popupSession];
[panel close];
[unwind_data->pool release];
[[FRAME_NS_VIEW (SELECTED_FRAME ()) window] makeKeyWindow];
......@@ -1756,20 +1753,40 @@ void process_dialog (id window, Lisp_Object list)
}
- (void)timeout_handler: (NSTimer *)timedEntry
{
timer_fired = 1;
[NSApp abortModal];
}
- (Lisp_Object)runDialogAt: (NSPoint)p
{
NSInteger ret;
NSInteger ret = 0;
/* initiate a session that will be ended by pop_down_menu */
popupSession = [NSApp beginModalSessionForWindow: self];
while (popup_activated_flag
&& (ret = [NSApp runModalSession: popupSession])
== NSRunContinuesResponse)
while (popup_activated_flag)
{
/* Run this for timers.el, indep of atimers; might not return.
TODO: use return value to avoid calling every iteration. */
timer_check ();
[NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.1]];
NSTimer *tmo = nil;
EMACS_TIME next_time = timer_check ();
if (EMACS_TIME_VALID_P (next_time))
{
double time = EMACS_TIME_TO_DOUBLE (next_time);
tmo = [NSTimer timerWithTimeInterval: time
target: self
selector: @selector (timeout_handler:)
userInfo: 0
repeats: NO];
[[NSRunLoop currentRunLoop] addTimer: tmo
forMode: NSModalPanelRunLoopMode];
}
timer_fired = 0;
ret = [NSApp runModalForWindow: self];
if (! timer_fired)
{
if (tmo != nil) [tmo invalidate]; /* Cancels timer */
break;
}
}
{ /* FIXME: BIG UGLY HACK!!! */
......
......@@ -56,7 +56,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
- (void)sendEvent: (NSEvent *)theEvent;
- (void)showPreferencesWindow: (id)sender;
- (BOOL) openFile: (NSString *)fileName;
- (void)fd_handler: (NSTimer *) fdEntry;
- (void)fd_handler: (id)unused;
- (void)timeout_handler: (NSTimer *)timedEntry;
- (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg;
@end
......@@ -195,12 +195,14 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
NSTextField *title;
NSMatrix *matrix;
int rows, cols;
int timer_fired;
}
- initFromContents: (Lisp_Object)menu isQuestion: (BOOL)isQ;
- addButton: (char *)str value: (Lisp_Object)val row: (int)row;
- addString: (char *)str row: (int)row;
- addSplit;
- (Lisp_Object)runDialogAt: (NSPoint)p;
- (void)timeout_handler: (NSTimer *)timedEntry;
@end
#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
......
......@@ -39,6 +39,10 @@ Updated by Christian Limpach (chris@nice.ch)
#include <c-strcase.h>
#include <ftoastr.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#include "lisp.h"
#include "blockinput.h"
#include "sysselect.h"
......@@ -184,17 +188,20 @@ Updated by Christian Limpach (chris@nice.ch)
static BOOL send_appdefined = YES;
static NSEvent *last_appdefined_event = 0;
static NSTimer *timed_entry = 0;
static NSTimer *fd_entry = nil;
static NSTimer *scroll_repeat_entry = nil;
static fd_set select_readfds, t_readfds;
static int select_nfds;
static fd_set select_readfds, select_writefds;
enum { SELECT_HAVE_READ = 1, SELECT_HAVE_WRITE = 2, SELECT_HAVE_TMO = 4 };
static int select_nfds = 0, select_valid = 0;
static EMACS_TIME select_timeout = { 0, 0 };
static int selfds[2] = { -1, -1 };
static pthread_mutex_t select_mutex;
static int apploopnr = 0;
static NSAutoreleasePool *outerpool;
static struct input_event *emacs_event = NULL;
static struct input_event *q_event_ptr = NULL;
static int n_emacs_events_pending = 0;
static NSMutableArray *ns_pending_files, *ns_pending_service_names,
*ns_pending_service_args;
static BOOL inNsSelect = 0;
static BOOL ns_do_open_file = NO;
/* Convert modifiers in a NeXTstep event to emacs style modifiers. */
......@@ -252,15 +259,20 @@ Updated by Christian Limpach (chris@nice.ch)
/* This is a piece of code which is common to all the event handling
methods. Maybe it should even be a function. */
#define EV_TRAILER(e) \
{ \
XSETFRAME (emacs_event->frame_or_window, emacsframe); \
if (e) emacs_event->timestamp = EV_TIMESTAMP (e); \
n_emacs_events_pending++; \
kbd_buffer_store_event_hold (emacs_event, q_event_ptr); \
EVENT_INIT (*emacs_event); \
ns_send_appdefined (-1); \
}
#define EV_TRAILER(e) \
{ \
XSETFRAME (emacs_event->frame_or_window, emacsframe); \
if (e) emacs_event->timestamp = EV_TIMESTAMP (e); \
if (q_event_ptr) \
{ \
n_emacs_events_pending++; \
kbd_buffer_store_event_hold (emacs_event, q_event_ptr); \
} \
else \
kbd_buffer_store_event (emacs_event); \
EVENT_INIT (*emacs_event); \
ns_send_appdefined (-1); \
}
void x_set_cursor_type (struct frame *, Lisp_Object, Lisp_Object);
......@@ -3377,14 +3389,6 @@ overwriting cursor (usually when cursor on a tab) */
timed_entry = nil;
}
/* Ditto for file descriptor poller */
if (fd_entry)
{
[fd_entry invalidate];
[fd_entry release];
fd_entry = nil;
}
nxev = [NSEvent otherEventWithType: NSApplicationDefined
location: NSMakePoint (0, 0)
modifierFlags: 0
......@@ -3402,7 +3406,6 @@ overwriting cursor (usually when cursor on a tab) */
}
}
static int
ns_read_socket (struct terminal *terminal, int expected,
struct input_event *hold_quit)
......@@ -3466,24 +3469,14 @@ overwriting cursor (usually when cursor on a tab) */
/* Run and wait for events. We must always send one NX_APPDEFINED event
to ourself, otherwise [NXApp run] will never exit. */
send_appdefined = YES;
ns_send_appdefined (-1);
/* If called via ns_select, this is called once with expected=1,
because we expect either the timeout or file descriptor activity.
In this case the first event through will either be real input or
one of these. read_avail_input() then calls once more with expected=0
and in that case we need to return quickly if there is nothing.
If we're being called outside of that, it's also OK to return quickly
after one iteration through the event loop, since other terms do
this and emacs expects it. */
if (!(inNsSelect && expected))
if (++apploopnr != 1)
{
/* Post an application defined event on the event queue. When this is
received the [NXApp run] will return, thus having processed all
events which are currently queued, if any. */
ns_send_appdefined (-1);
abort ();
}
[NSApp run];
--apploopnr;
}
nevents = n_emacs_events_pending;
......@@ -3503,65 +3496,89 @@ overwriting cursor (usually when cursor on a tab) */
-------------------------------------------------------------------------- */
{
int result;
double time;
NSEvent *ev;
struct timespec select_timeout;
int k, nr = 0;
struct input_event event;
char c;
/* NSTRACE (ns_select); */
if (NSApp == nil || inNsSelect == 1 /* || ([NSApp isActive] == NO &&
[NSApp nextEventMatchingMask:NSAnyEventMask untilDate:nil
inMode:NSDefaultRunLoopMode dequeue:NO] == nil) */)
for (k = 0; readfds && k < nfds+1; k++)
if (FD_ISSET(k, readfds)) ++nr;
if (NSApp == nil
|| (timeout && timeout->tv_sec == 0 && timeout->tv_nsec == 0))
return pselect (nfds, readfds, writefds, exceptfds, timeout, sigmask);
/* Save file descriptor set, which gets overwritten in calls to select ()
Note, this is called from process.c, and only readfds is ever set */
if (readfds)
[outerpool release];
outerpool = [[NSAutoreleasePool alloc] init];
send_appdefined = YES;
if (nr > 0)
{
memcpy (&select_readfds, readfds, sizeof (fd_set));
pthread_mutex_lock (&select_mutex);
select_nfds = nfds;
select_valid = 0;
if (readfds)
{
select_readfds = *readfds;
select_valid += SELECT_HAVE_READ;
}
if (writefds)
{
select_writefds = *writefds;
select_valid += SELECT_HAVE_WRITE;
}
if (timeout)
{
select_timeout = *timeout;
select_valid += SELECT_HAVE_TMO;
}
pthread_mutex_unlock (&select_mutex);
/* Inform fd_handler that select should be called */
c = 'g';
write (selfds[1], &c, 1);
}
else
select_nfds = 0;
/* Try an initial select for pending data on input files */
select_timeout.tv_sec = select_timeout.tv_nsec = 0;
result = pselect (nfds, readfds, writefds, exceptfds,
&select_timeout, sigmask);
if (result)
return result;
/* if (!timeout || timed_entry || fd_entry)
fprintf (stderr, "assertion failed: timeout null or timed_entry/fd_entry non-null in ns_select\n"); */
/* set a timeout and run the main AppKit event loop while continuing
to monitor the files */
time = EMACS_TIME_TO_DOUBLE (*timeout);
timed_entry = [[NSTimer scheduledTimerWithTimeInterval: time
target: NSApp
selector: @selector (timeout_handler:)
userInfo: 0
repeats: YES] /* for safe removal */
retain];
/* set a periodic task to try the pselect () again */
fd_entry = [[NSTimer scheduledTimerWithTimeInterval: 0.1
target: NSApp
selector: @selector (fd_handler:)
userInfo: 0
repeats: YES]
retain];
/* Let Application dispatch events until it receives an event of the type
NX_APPDEFINED, which should only be sent by timeout_handler.
We tell read_avail_input() that input is "expected" because we do expect
either the timeout or fd handler to fire, and if they don't, the original
call from process.c that got us here expects us to wait until some input
comes. */
inNsSelect = 1;
gobble_input (1);
else if (nr == 0 && timeout)
{
/* No file descriptor, just a timeout, no need to wake fd_handler */
double time = EMACS_TIME_TO_DOUBLE (*timeout);
timed_entry = [[NSTimer scheduledTimerWithTimeInterval: time
target: NSApp
selector:
@selector (timeout_handler:)
userInfo: 0
repeats: NO]
retain];
}
else /* No timeout and no file descriptors, can this happen? */
{
/* Send appdefined so we exit from the loop */
ns_send_appdefined (-1);
}
EVENT_INIT (event);
BLOCK_INPUT;
emacs_event = &event;
if (++apploopnr != 1)
{
abort();
}
[NSApp run];
--apploopnr;
emacs_event = NULL;
if (nr > 0 && readfds)
{
c = 's';
write (selfds[1], &c, 1);
}
UNBLOCK_INPUT;
ev = last_appdefined_event;
inNsSelect = 0;
if (ev)
{
......@@ -3575,25 +3592,28 @@ We tell read_avail_input() that input is "expected" because we do expect
if (t == -2)
{
/* The NX_APPDEFINED event we received was a timeout. */
return 0;
result = 0;
}
else if (t == -1)
{
/* The NX_APPDEFINED event we received was the result of
at least one real input event arriving. */
errno = EINTR;
return -1;
result = -1;
}
else
{
/* Received back from pselect () in fd_handler; copy the results */
if (readfds)
memcpy (readfds, &select_readfds, sizeof (fd_set));
return t;
/* Received back from select () in fd_handler; copy the results */
pthread_mutex_lock (&select_mutex);
if (readfds) *readfds = select_readfds;
if (writefds) *writefds = select_writefds;
if (timeout) *timeout = select_timeout;
pthread_mutex_unlock (&select_mutex);
result = t;
}
}
/* never reached, shut compiler up */
return 0;
return result;
}
......@@ -4024,6 +4044,21 @@ static Lisp_Object ns_string_to_lispmod (const char *s)
{
baud_rate = 38400;
Fset_input_interrupt_mode (Qnil);
if (selfds[0] == -1)
{
if (pipe (selfds) == -1)
{
fprintf (stderr, "Failed to create pipe: %s\n",
emacs_strerror (errno));
abort ();
}
fcntl (selfds[0], F_SETFL, O_NONBLOCK|fcntl (selfds[0], F_GETFL));
FD_ZERO (&select_readfds);
FD_ZERO (&select_writefds);
pthread_mutex_init (&select_mutex, NULL);
}
ns_initialized = 1;
}
......@@ -4039,6 +4074,11 @@ Needs to be here because ns_initialize_display_info () uses AppKit classes.
return NULL;
[NSApp setDelegate: NSApp];
/* Start the select thread. */
[NSThread detachNewThreadSelector:@selector (fd_handler:)
toTarget:NSApp
withObject:nil];
/* debugging: log all notifications */
/* [[NSNotificationCenter defaultCenter] addObserver: NSApp
selector: @selector (logNotification:)
......@@ -4547,26 +4587,91 @@ - (void)timeout_handler: (NSTimer *)timedEntry
ns_send_appdefined (-2);
}
- (void)fd_handler: (NSTimer *) fdEntry
- (void)fd_handler:(id)unused
/* --------------------------------------------------------------------------
Check data waiting on file descriptors and terminate if so
-------------------------------------------------------------------------- */
{
int result;
struct timespec select_timeout;
/* NSTRACE (fd_handler); */
int waiting = 1, nfds;
char c;
if (select_nfds == 0)
return;
SELECT_TYPE readfds, writefds, *wfds;
EMACS_TIME timeout, *tmo;
memcpy (&t_readfds, &select_readfds, sizeof (fd_set));
/* NSTRACE (fd_handler); */
select_timeout.tv_sec = select_timeout.tv_nsec = 0;
result = pselect (select_nfds, &t_readfds, NULL, NULL, &select_timeout, NULL);
if (result)
for (;;)
{
memcpy (&select_readfds, &t_readfds, sizeof (fd_set));
ns_send_appdefined (result);
if (waiting)
{
SELECT_TYPE fds;
FD_SET (selfds[0], &fds);
result = select (selfds[0]+1, &fds, NULL, NULL, NULL);
if (result > 0)
{
read (selfds[0], &c, 1);
if (c == 'g') waiting = 0;
}
}
else
{
pthread_mutex_lock (&select_mutex);
nfds = select_nfds;
if (select_valid & SELECT_HAVE_READ)
readfds = select_readfds;
else
FD_ZERO (&readfds);
if (select_valid & SELECT_HAVE_WRITE)
{
writefds = select_writefds;
wfds = &writefds;
}
else
wfds = NULL;
if (select_valid & SELECT_HAVE_TMO)
{
timeout = select_timeout;
tmo = &timeout;
}
else
tmo = NULL;
pthread_mutex_unlock (&select_mutex);
FD_SET (selfds[0], &readfds);
if (selfds[0] >= nfds) nfds = selfds[0]+1;
result = pselect (nfds, &readfds, wfds, NULL, tmo, NULL);
if (result == 0)
ns_send_appdefined (-2);
else if (result > 0)
{
if (FD_ISSET (selfds[0], &readfds))
{
read (selfds[0], &c, 1);
if (c == 's') waiting = 1;
}
else
{
pthread_mutex_lock (&select_mutex);
if (select_valid & SELECT_HAVE_READ)
select_readfds = readfds;
if (select_valid & SELECT_HAVE_WRITE)
select_writefds = writefds;
if (select_valid & SELECT_HAVE_TMO)
select_timeout = timeout;
pthread_mutex_unlock (&select_mutex);
ns_send_appdefined (result);
}
}
waiting = 1;
}
}
}
......@@ -6404,8 +6509,13 @@ - (void) sendScrollEventAtLoc: (float)loc fromEvent: (NSEvent *)e
XSETINT (emacs_event->x, loc * pixel_height);
XSETINT (emacs_event->y, pixel_height-20);
n_emacs_events_pending++;
kbd_buffer_store_event_hold (emacs_event, q_event_ptr);
if (q_event_ptr)
{
n_emacs_events_pending++;
kbd_buffer_store_event_hold (emacs_event, q_event_ptr);
}
else
kbd_buffer_store_event (emacs_event);
EVENT_INIT (*emacs_event);
ns_send_appdefined (-1);
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment