summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Third <alan@idiocy.org>2017-01-04 12:41:29 +0000
committerAlan Third <alan@idiocy.org>2017-01-04 21:00:52 +0000
commit308d5962236448a84795f49d775601599688d78d (patch)
tree3fed7f15fff4d3c25dc5304a5390b7d1e72a3694
parent44c588a25ce231ce05fb535cd6d7162e91214f45 (diff)
downloademacs-308d5962236448a84795f49d775601599688d78d.tar.gz
Revert "Rework NS event handling (bug#25265)"
This reverts commit e0e5b0f4a4ce1d19ee0240c514dedd873d4165dc.
-rw-r--r--src/nsterm.h1
-rw-r--r--src/nsterm.m380
2 files changed, 313 insertions, 68 deletions
diff --git a/src/nsterm.h b/src/nsterm.h
index 161c3c29646..534ec68c22b 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -392,6 +392,7 @@ char const * nstrace_fullscreen_type_name (int);
- (void)sendEvent: (NSEvent *)theEvent;
- (void)showPreferencesWindow: (id)sender;
- (BOOL) openFile: (NSString *)fileName;
+- (void)fd_handler: (id)unused;
- (void)timeout_handler: (NSTimer *)timedEntry;
- (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg;
#ifdef NS_IMPL_GNUSTEP
diff --git a/src/nsterm.m b/src/nsterm.m
index 1d038cdcb05..47fc6c18acd 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -279,10 +279,18 @@ static BOOL ns_menu_bar_is_hidden = NO;
/*static int debug_lock = 0; */
/* event loop */
+static BOOL send_appdefined = YES;
#define NO_APPDEFINED_DATA (-8)
static int last_appdefined_event_data = NO_APPDEFINED_DATA;
static NSTimer *timed_entry = 0;
static NSTimer *scroll_repeat_entry = nil;
+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 struct timespec 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;
@@ -449,6 +457,7 @@ hold_event (struct input_event *event)
hold_event_q.q[hold_event_q.nr++] = *event;
/* Make sure ns_read_socket is called, i.e. we have input. */
raise (SIGIO);
+ send_appdefined = YES;
}
static Lisp_Object
@@ -3863,17 +3872,31 @@ ns_send_appdefined (int value)
return;
}
- /* Only post this event if we haven't already posted one. This will
- end the [NXApp run] main loop after having processed all events
- queued at this moment. */
- NSEvent *appev = [NSApp nextEventMatchingMask:NSEventMaskApplicationDefined
- untilDate:[NSDate distantPast]
- inMode:NSDefaultRunLoopMode
- dequeue:NO];
- if (! appev)
+ /* Only post this event if we haven't already posted one. This will end
+ the [NXApp run] main loop after having processed all events queued at
+ this moment. */
+
+#ifdef NS_IMPL_COCOA
+ if (! send_appdefined)
+ {
+ /* OS X 10.10.1 swallows the AppDefined event we are sending ourselves
+ in certain situations (rapid incoming events).
+ So check if we have one, if not add one. */
+ NSEvent *appev = [NSApp nextEventMatchingMask:NSEventMaskApplicationDefined
+ untilDate:[NSDate distantPast]
+ inMode:NSDefaultRunLoopMode
+ dequeue:NO];
+ if (! appev) send_appdefined = YES;
+ }
+#endif
+
+ if (send_appdefined)
{
NSEvent *nxev;
+ /* We only need one NX_APPDEFINED event to stop NXApp from running. */
+ send_appdefined = NO;
+
/* Don't need wakeup timer any more */
if (timed_entry)
{
@@ -3988,6 +4011,14 @@ ns_check_pending_open_menu ()
}
#endif /* NS_IMPL_COCOA */
+static void
+unwind_apploopnr (Lisp_Object not_used)
+{
+ --apploopnr;
+ n_emacs_events_pending = 0;
+ ns_finish_events ();
+ q_event_ptr = NULL;
+}
static int
ns_read_socket (struct terminal *terminal, struct input_event *hold_quit)
@@ -3998,10 +4029,13 @@ ns_read_socket (struct terminal *terminal, struct input_event *hold_quit)
-------------------------------------------------------------------------- */
{
struct input_event ev;
- int nevents = 0;
+ int nevents;
NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_read_socket");
+ if (apploopnr > 0)
+ return -1; /* Already within event loop. */
+
#ifdef HAVE_NATIVE_FS
check_native_fs ();
#endif
@@ -4018,50 +4052,55 @@ ns_read_socket (struct terminal *terminal, struct input_event *hold_quit)
return i;
}
- if ([NSThread isMainThread])
+ block_input ();
+ n_emacs_events_pending = 0;
+ ns_init_events (&ev);
+ q_event_ptr = hold_quit;
+
+ /* we manage autorelease pools by allocate/reallocate each time around
+ the loop; strict nesting is occasionally violated but seems not to
+ matter.. earlier methods using full nesting caused major memory leaks */
+ [outerpool release];
+ outerpool = [[NSAutoreleasePool alloc] init];
+
+ /* If have pending open-file requests, attend to the next one of those. */
+ if (ns_pending_files && [ns_pending_files count] != 0
+ && [(EmacsApp *)NSApp openFile: [ns_pending_files objectAtIndex: 0]])
{
- block_input ();
- n_emacs_events_pending = 0;
- ns_init_events (&ev);
- q_event_ptr = hold_quit;
-
- /* we manage autorelease pools by allocate/reallocate each time around
- the loop; strict nesting is occasionally violated but seems not to
- matter.. earlier methods using full nesting caused major memory leaks */
- [outerpool release];
- outerpool = [[NSAutoreleasePool alloc] init];
-
- /* If have pending open-file requests, attend to the next one of those. */
- if (ns_pending_files && [ns_pending_files count] != 0
- && [(EmacsApp *)NSApp openFile: [ns_pending_files objectAtIndex: 0]])
- {
- [ns_pending_files removeObjectAtIndex: 0];
- }
- /* Deal with pending service requests. */
- else if (ns_pending_service_names && [ns_pending_service_names count] != 0
- && [(EmacsApp *)
- NSApp fulfillService: [ns_pending_service_names objectAtIndex: 0]
- withArg: [ns_pending_service_args objectAtIndex: 0]])
- {
- [ns_pending_service_names removeObjectAtIndex: 0];
- [ns_pending_service_args removeObjectAtIndex: 0];
- }
- else
- {
- /* Run and wait for events. We must always send one NX_APPDEFINED event
- to ourself, otherwise [NXApp run] will never exit. */
- ns_send_appdefined (-1);
+ [ns_pending_files removeObjectAtIndex: 0];
+ }
+ /* Deal with pending service requests. */
+ else if (ns_pending_service_names && [ns_pending_service_names count] != 0
+ && [(EmacsApp *)
+ NSApp fulfillService: [ns_pending_service_names objectAtIndex: 0]
+ withArg: [ns_pending_service_args objectAtIndex: 0]])
+ {
+ [ns_pending_service_names removeObjectAtIndex: 0];
+ [ns_pending_service_args removeObjectAtIndex: 0];
+ }
+ else
+ {
+ ptrdiff_t specpdl_count = SPECPDL_INDEX ();
+ /* 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);
- [NSApp run];
+ if (++apploopnr != 1)
+ {
+ emacs_abort ();
}
-
- nevents = n_emacs_events_pending;
- n_emacs_events_pending = 0;
- ns_finish_events ();
- q_event_ptr = NULL;
- unblock_input ();
+ record_unwind_protect (unwind_apploopnr, Qt);
+ [NSApp run];
+ unbind_to (specpdl_count, Qnil); /* calls unwind_apploopnr */
}
+ nevents = n_emacs_events_pending;
+ n_emacs_events_pending = 0;
+ ns_finish_events ();
+ q_event_ptr = NULL;
+ unblock_input ();
+
return nevents;
}
@@ -4075,11 +4114,15 @@ ns_select (int nfds, fd_set *readfds, fd_set *writefds,
-------------------------------------------------------------------------- */
{
int result;
- NSDate *timeout_date = nil;
- NSEvent *ns_event;
+ int t, k, nr = 0;
+ struct input_event event;
+ char c;
NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "ns_select");
+ if (apploopnr > 0)
+ return -1; /* Already within event loop. */
+
#ifdef HAVE_NATIVE_FS
check_native_fs ();
#endif
@@ -4092,34 +4135,121 @@ ns_select (int nfds, fd_set *readfds, fd_set *writefds,
return -1;
}
+ for (k = 0; k < nfds+1; k++)
+ {
+ if (readfds && FD_ISSET(k, readfds)) ++nr;
+ if (writefds && FD_ISSET(k, writefds)) ++nr;
+ }
+
if (NSApp == nil
- || ![NSThread isMainThread]
|| (timeout && timeout->tv_sec == 0 && timeout->tv_nsec == 0))
- return pselect(nfds, readfds, writefds,
- exceptfds, timeout, sigmask);
-
- result = pselect(nfds, readfds, writefds, exceptfds,
- &(struct timespec){.tv_sec = 0, .tv_nsec = 100},
- sigmask);
+ return pselect (nfds, readfds, writefds, exceptfds, timeout, sigmask);
[outerpool release];
outerpool = [[NSAutoreleasePool alloc] init];
- if (timeout)
+
+ send_appdefined = YES;
+ if (nr > 0)
+ {
+ 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';
+ emacs_write_sig (selfds[1], &c, 1);
+ }
+ else if (nr == 0 && timeout)
{
+ /* No file descriptor, just a timeout, no need to wake fd_handler */
double time = timespectod (*timeout);
- timeout_date = [NSDate dateWithTimeIntervalSinceNow:time];
+ 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);
+ }
+
+ block_input ();
+ ns_init_events (&event);
+ if (++apploopnr != 1)
+ {
+ emacs_abort ();
}
- /* Listen for a new NSEvent. */
- ns_event = [NSApp nextEventMatchingMask:NSEventMaskAny
- untilDate:timeout_date
- inMode:NSDefaultRunLoopMode
- dequeue:NO];
+ {
+ ptrdiff_t specpdl_count = SPECPDL_INDEX ();
+ record_unwind_protect (unwind_apploopnr, Qt);
+ [NSApp run];
+ unbind_to (specpdl_count, Qnil); /* calls unwind_apploopnr */
+ }
- if (ns_event != nil)
+ ns_finish_events ();
+ if (nr > 0 && readfds)
{
- raise (SIGIO);
+ c = 's';
+ emacs_write_sig (selfds[1], &c, 1);
+ }
+ unblock_input ();
+
+ t = last_appdefined_event_data;
+
+ if (t != NO_APPDEFINED_DATA)
+ {
+ last_appdefined_event_data = NO_APPDEFINED_DATA;
+
+ if (t == -2)
+ {
+ /* The NX_APPDEFINED event we received was a timeout. */
+ 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;
+ result = -1;
+ }
+ else
+ {
+ /* 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;
+ pthread_mutex_unlock (&select_mutex);
+ result = t;
+ }
+ }
+ else
+ {
+ errno = EINTR;
+ result = -1;
}
return result;
@@ -4635,6 +4765,21 @@ ns_term_init (Lisp_Object display_name)
baud_rate = 38400;
Fset_input_interrupt_mode (Qnil);
+ if (selfds[0] == -1)
+ {
+ if (emacs_pipe (selfds) != 0)
+ {
+ fprintf (stderr, "Failed to create pipe: %s\n",
+ emacs_strerror (errno));
+ emacs_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_pending_files = [[NSMutableArray alloc] init];
ns_pending_service_names = [[NSMutableArray alloc] init];
ns_pending_service_args = [[NSMutableArray alloc] init];
@@ -4647,6 +4792,11 @@ ns_term_init (Lisp_Object display_name)
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:)
@@ -5028,6 +5178,10 @@ ns_term_shutdown (int sig)
last_appdefined_event_data = [theEvent data1];
[self stop: self];
}
+ else
+ {
+ send_appdefined = YES;
+ }
}
@@ -5330,6 +5484,95 @@ not_in_argv (NSString *arg)
ns_send_appdefined (nextappdefined);
}
+- (void)fd_handler:(id)unused
+/* --------------------------------------------------------------------------
+ Check data waiting on file descriptors and terminate if so
+ -------------------------------------------------------------------------- */
+{
+ int result;
+ int waiting = 1, nfds;
+ char c;
+
+ fd_set readfds, writefds, *wfds;
+ struct timespec timeout, *tmo;
+ NSAutoreleasePool *pool = nil;
+
+ /* NSTRACE ("fd_handler"); */
+
+ for (;;)
+ {
+ [pool release];
+ pool = [[NSAutoreleasePool alloc] init];
+
+ if (waiting)
+ {
+ fd_set fds;
+ FD_ZERO (&fds);
+ FD_SET (selfds[0], &fds);
+ result = select (selfds[0]+1, &fds, NULL, NULL, NULL);
+ if (result > 0 && read (selfds[0], &c, 1) == 1 && 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))
+ {
+ if (read (selfds[0], &c, 1) == 1 && 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;
+ }
+ }
+}
+
/* ==========================================================================
@@ -6118,7 +6361,7 @@ not_in_argv (NSString *arg)
help_echo_object, help_echo_pos);
}
- if (emacsframe->mouse_moved)
+ if (emacsframe->mouse_moved && send_appdefined)
ns_send_appdefined (-1);
}
@@ -6815,7 +7058,8 @@ not_in_argv (NSString *arg)
SET_FRAME_VISIBLE (emacsframe, 1);
SET_FRAME_GARBAGED (emacsframe);
- ns_send_appdefined (-1);
+ if (send_appdefined)
+ ns_send_appdefined (-1);
}