# HG changeset patch # User YAMAMOTO Mitsuharu # Date 1157703478 0 # Node ID 49fb9398679c2afbb6db1f361bfe97cdb21a984d # Parent ca1234ea6e5f909df8b5d1f722735602d0679163 [!MAC_OSX] Don't include keyboard.h. [!MAC_OSX] (select): Try detect_input_pending before ReceiveNextEvent in the same BLOCK_INPUT block, in case that some input has already been read asynchronously. Pretend to be interrupted by a signal if some input is available. [MAC_OSX] (select_and_poll_event, sys_select): Likewise. (SELECT_POLLING_PERIOD_USEC) [SELECT_USE_CFSOCKET]: Change to 100000. Now used for ReceiveNextEvent timeout instead of select timeout. (EVENT_CLASS_SOCK) [SELECT_USE_CFSOCKET]: Remove macro. [SELECT_USE_CFSOCKET] (socket_callback): Add non-blocking connect support. Quit event loop. [MAC_OSX] (sys_select) [SELECT_USE_CFSOCKET]: Add non-blocking connect support. Reuse previously allocated CFRunLoopSource. (Fmac_process_hi_command) [TARGET_API_MAC_CARBON]: New function. (syms_of_mac) [TARGET_API_MAC_CARBON]: Defsubr it. diff -r ca1234ea6e5f -r 49fb9398679c src/mac.c --- a/src/mac.c Fri Sep 08 08:17:21 2006 +0000 +++ b/src/mac.c Fri Sep 08 08:17:58 2006 +0000 @@ -2413,75 +2413,69 @@ } -#include "keyboard.h" -extern Boolean mac_wait_next_event (EventRecord *, UInt32, Boolean); +extern Boolean mac_wait_next_event P_ ((EventRecord *, UInt32, Boolean)); int select (n, rfds, wfds, efds, timeout) - int n; - SELECT_TYPE *rfds; - SELECT_TYPE *wfds; - SELECT_TYPE *efds; - struct timeval *timeout; + int nfds; + SELECT_TYPE *rfds, *wfds, *efds; + EMACS_TIME *timeout; { - OSStatus err; -#if TARGET_API_MAC_CARBON - EventTimeout timeout_sec = - (timeout - ? (EMACS_SECS (*timeout) * kEventDurationSecond - + EMACS_USECS (*timeout) * kEventDurationMicrosecond) - : kEventDurationForever); - - BLOCK_INPUT; - err = ReceiveNextEvent (0, NULL, timeout_sec, kEventLeaveInQueue, NULL); - UNBLOCK_INPUT; -#else /* not TARGET_API_MAC_CARBON */ - EventRecord e; - UInt32 sleep_time = EMACS_SECS (*timeout) * 60 + - ((EMACS_USECS (*timeout) * 60) / 1000000); + OSStatus err = noErr; /* Can only handle wait for keyboard input. */ - if (n > 1 || wfds || efds) + if (nfds > 1 || wfds || efds) return -1; - /* Also return true if an event other than a keyDown has occurred. - This causes kbd_buffer_get_event in keyboard.c to call - read_avail_input which in turn calls XTread_socket to poll for - these events. Otherwise these never get processed except but a - very slow poll timer. */ - if (mac_wait_next_event (&e, sleep_time, false)) - err = noErr; + /* Try detect_input_pending before ReceiveNextEvent in the same + BLOCK_INPUT block, in case that some input has already been read + asynchronously. */ + BLOCK_INPUT; + if (!detect_input_pending ()) + { +#if TARGET_API_MAC_CARBON + EventTimeout timeoutval = + (timeout + ? (EMACS_SECS (*timeout) * kEventDurationSecond + + EMACS_USECS (*timeout) * kEventDurationMicrosecond) + : kEventDurationForever); + + if (timeoutval == 0.0) + err = eventLoopTimedOutErr; + else + err = ReceiveNextEvent (0, NULL, timeoutval, + kEventLeaveInQueue, NULL); +#else /* not TARGET_API_MAC_CARBON */ + EventRecord e; + UInt32 sleep_time = EMACS_SECS (*timeout) * 60 + + ((EMACS_USECS (*timeout) * 60) / 1000000); + + if (sleep_time == 0) + err = -9875; /* eventLoopTimedOutErr */ + else + { + if (mac_wait_next_event (&e, sleep_time, false)) + err = noErr; + else + err = -9875; /* eventLoopTimedOutErr */ + } +#endif /* not TARGET_API_MAC_CARBON */ + } + UNBLOCK_INPUT; + + if (err == noErr) + { + /* Pretend that `select' is interrupted by a signal. */ + detect_input_pending (); + errno = EINTR; + return -1; + } else - err = -9875; /* eventLoopTimedOutErr */ -#endif /* not TARGET_API_MAC_CARBON */ - - if (FD_ISSET (0, rfds)) - if (err == noErr) - return 1; - else - { + { + if (rfds) FD_ZERO (rfds); - return 0; - } - else - if (err == noErr) - { - if (input_polling_used ()) - { - /* It could be confusing if a real alarm arrives while - processing the fake one. Turn it off and let the - handler reset it. */ - extern void poll_for_input_1 P_ ((void)); - int old_poll_suppress_count = poll_suppress_count; - poll_suppress_count = 1; - poll_for_input_1 (); - poll_suppress_count = old_poll_suppress_count; - } - errno = EINTR; - return -1; - } - else return 0; + } } @@ -4904,6 +4898,30 @@ return result; } + +DEFUN ("mac-process-hi-command", Fmac_process_hi_command, Smac_process_hi_command, 1, 1, 0, + doc: /* Send a HI command whose ID is COMMAND-ID to the command chain. +COMMAND-ID must be a 4-character string. Some common command IDs are +defined in the Carbon Event Manager. */) + (command_id) + Lisp_Object command_id; +{ + OSStatus err; + HICommand command; + + bzero (&command, sizeof (HICommand)); + command.commandID = mac_get_code_from_arg (command_id , 0); + + BLOCK_INPUT; + err = ProcessHICommand (&command); + UNBLOCK_INPUT; + + if (err != noErr) + error ("HI command (command ID: '%s') not handled.", SDATA (command_id)); + + return Qnil; +} + #endif /* TARGET_API_MAC_CARBON */ @@ -4944,23 +4962,22 @@ -> Use `select'. 2. Sockets are not involved. -> Use ReceiveNextEvent. - 3. [If SELECT_USE_CFSOCKET is defined] - Only the window event channel and socket read channels are + 3. [If SELECT_USE_CFSOCKET is set] + Only the window event channel and socket read/write channels are involved, and timeout is not too short (greater than SELECT_TIMEOUT_THRESHHOLD_RUNLOOP seconds). -> Create CFSocket for each socket and add it into the current - event RunLoop so that a `ready-to-read' event can be posted - to the event queue that is also used for window events. Then - ReceiveNextEvent can wait for both kinds of inputs. + event RunLoop so that the current event loop gets quit when + the socket becomes ready. Then ReceiveNextEvent can wait for + both kinds of inputs. 4. Otherwise. -> Periodically poll the window input channel while repeatedly executing `select' with a short timeout (SELECT_POLLING_PERIOD_USEC microseconds). */ -#define SELECT_POLLING_PERIOD_USEC 20000 -#ifdef SELECT_USE_CFSOCKET +#define SELECT_POLLING_PERIOD_USEC 100000 +#if SELECT_USE_CFSOCKET #define SELECT_TIMEOUT_THRESHOLD_RUNLOOP 0.2 -#define EVENT_CLASS_SOCK 'Sock' static void socket_callback (s, type, address, data, info) @@ -4970,196 +4987,208 @@ const void *data; void *info; { - EventRef event; - - CreateEvent (NULL, EVENT_CLASS_SOCK, 0, 0, kEventAttributeNone, &event); - PostEventToQueue (GetCurrentEventQueue (), event, kEventPriorityStandard); - ReleaseEvent (event); + int fd = CFSocketGetNative (s); + SELECT_TYPE *ofds = (SELECT_TYPE *)info; + + if ((type == kCFSocketReadCallBack && FD_ISSET (fd, &ofds[0])) + || (type == kCFSocketConnectCallBack && FD_ISSET (fd, &ofds[1]))) + QuitEventLoop (GetCurrentEventLoop ()); } #endif /* SELECT_USE_CFSOCKET */ static int -select_and_poll_event (n, rfds, wfds, efds, timeout) - int n; - SELECT_TYPE *rfds; - SELECT_TYPE *wfds; - SELECT_TYPE *efds; - struct timeval *timeout; +select_and_poll_event (nfds, rfds, wfds, efds, timeout) + int nfds; + SELECT_TYPE *rfds, *wfds, *efds; + EMACS_TIME *timeout; { - int r; - OSStatus err; - - r = select (n, rfds, wfds, efds, timeout); - if (r != -1) + OSStatus err = noErr; + int r = 0; + + /* Try detect_input_pending before ReceiveNextEvent in the same + BLOCK_INPUT block, in case that some input has already been read + asynchronously. */ + BLOCK_INPUT; + if (!detect_input_pending ()) { - BLOCK_INPUT; - err = ReceiveNextEvent (0, NULL, kEventDurationNoWait, - kEventLeaveInQueue, NULL); - UNBLOCK_INPUT; - if (err == noErr) + EMACS_TIME select_timeout; + EventTimeout timeoutval = + (timeout + ? (EMACS_SECS (*timeout) * kEventDurationSecond + + EMACS_USECS (*timeout) * kEventDurationMicrosecond) + : kEventDurationForever); + + EMACS_SET_SECS_USECS (select_timeout, 0, 0); + r = select (nfds, rfds, wfds, efds, &select_timeout); + if (timeoutval == 0.0) + err = eventLoopTimedOutErr; + else if (r == 0) { - FD_SET (0, rfds); - r++; +#if USE_CG_DRAWING + mac_prepare_for_quickdraw (NULL); +#endif + err = ReceiveNextEvent (0, NULL, timeoutval, + kEventLeaveInQueue, NULL); } } - return r; + UNBLOCK_INPUT; + + if (r != 0) + return r; + else if (err == noErr) + { + /* Pretend that `select' is interrupted by a signal. */ + detect_input_pending (); + errno = EINTR; + return -1; + } + else + return 0; } -#if MAC_OS_X_VERSION_MAX_ALLOWED < 1020 -#undef SELECT_INVALIDATE_CFSOCKET -#endif - int -sys_select (n, rfds, wfds, efds, timeout) - int n; - SELECT_TYPE *rfds; - SELECT_TYPE *wfds; - SELECT_TYPE *efds; - struct timeval *timeout; +sys_select (nfds, rfds, wfds, efds, timeout) + int nfds; + SELECT_TYPE *rfds, *wfds, *efds; + EMACS_TIME *timeout; { - OSStatus err; - int i, r; + OSStatus err = noErr; + int r; EMACS_TIME select_timeout; + SELECT_TYPE ofds[3]; if (inhibit_window_system || noninteractive || rfds == NULL || !FD_ISSET (0, rfds)) - return select (n, rfds, wfds, efds, timeout); + return select (nfds, rfds, wfds, efds, timeout); FD_CLR (0, rfds); - - if (wfds == NULL && efds == NULL) + ofds[0] = *rfds; + + if (wfds) + ofds[1] = *wfds; + else + FD_ZERO (&ofds[1]); + + if (efds) + ofds[2] = *efds; + else { - int nsocks = 0; - SELECT_TYPE orfds = *rfds; - - EventTimeout timeout_sec = + int maxfd; + EventTimeout timeoutval = (timeout ? (EMACS_SECS (*timeout) * kEventDurationSecond + EMACS_USECS (*timeout) * kEventDurationMicrosecond) : kEventDurationForever); - for (i = 1; i < n; i++) - if (FD_ISSET (i, rfds)) - nsocks++; - - if (nsocks == 0) - { - BLOCK_INPUT; - err = ReceiveNextEvent (0, NULL, timeout_sec, - kEventLeaveInQueue, NULL); - UNBLOCK_INPUT; - if (err == noErr) - { - FD_SET (0, rfds); - return 1; - } - else - return 0; - } - -#if USE_CG_DRAWING - mac_prepare_for_quickdraw (NULL); -#endif + for (maxfd = nfds - 1; maxfd > 0; maxfd--) + if (FD_ISSET (maxfd, rfds) || (wfds && FD_ISSET (maxfd, wfds))) + break; + + if (maxfd == 0) + return select_and_poll_event (nfds, rfds, wfds, efds, timeout); + /* Avoid initial overhead of RunLoop setup for the case that some input is already available. */ EMACS_SET_SECS_USECS (select_timeout, 0, 0); - r = select_and_poll_event (n, rfds, wfds, efds, &select_timeout); - if (r != 0 || timeout_sec == 0.0) + r = select_and_poll_event (nfds, rfds, wfds, efds, &select_timeout); + if (r != 0 || timeoutval == 0.0) return r; - *rfds = orfds; - -#ifdef SELECT_USE_CFSOCKET - if (timeout_sec > 0 && timeout_sec <= SELECT_TIMEOUT_THRESHOLD_RUNLOOP) + *rfds = ofds[0]; + if (wfds) + *wfds = ofds[1]; + +#if SELECT_USE_CFSOCKET + if (timeoutval > 0 && timeoutval <= SELECT_TIMEOUT_THRESHOLD_RUNLOOP) goto poll_periodically; - { - CFRunLoopRef runloop = - (CFRunLoopRef) GetCFRunLoopFromEventLoop (GetCurrentEventLoop ()); - EventTypeSpec specs[] = {{EVENT_CLASS_SOCK, 0}}; -#ifdef SELECT_INVALIDATE_CFSOCKET - CFSocketRef *shead, *s; -#else - CFRunLoopSourceRef *shead, *s; -#endif - - BLOCK_INPUT; - -#ifdef SELECT_INVALIDATE_CFSOCKET - shead = xmalloc (sizeof (CFSocketRef) * nsocks); -#else - shead = xmalloc (sizeof (CFRunLoopSourceRef) * nsocks); -#endif - s = shead; - for (i = 1; i < n; i++) - if (FD_ISSET (i, rfds)) - { - CFSocketRef socket = - CFSocketCreateWithNative (NULL, i, kCFSocketReadCallBack, - socket_callback, NULL); - CFRunLoopSourceRef source = - CFSocketCreateRunLoopSource (NULL, socket, 0); - -#ifdef SELECT_INVALIDATE_CFSOCKET - CFSocketSetSocketFlags (socket, 0); + /* Try detect_input_pending before ReceiveNextEvent in the same + BLOCK_INPUT block, in case that some input has already been + read asynchronously. */ + BLOCK_INPUT; + if (!detect_input_pending ()) + { + int minfd, fd; + CFRunLoopRef runloop = + (CFRunLoopRef) GetCFRunLoopFromEventLoop (GetCurrentEventLoop ()); + static CFSocketContext context; + static CFMutableDictionaryRef sources; + + context.info = ofds; + if (sources == NULL) + sources = + CFDictionaryCreateMutable (NULL, 0, NULL, + &kCFTypeDictionaryValueCallBacks); + + for (minfd = 1; minfd < maxfd; minfd++) + if (FD_ISSET (minfd, rfds) || (wfds && FD_ISSET (minfd, wfds))) + break; + + for (fd = minfd; fd <= maxfd; fd++) + if (FD_ISSET (fd, rfds) || (wfds && FD_ISSET (fd, wfds))) + { + void *key = (void *) fd; + CFRunLoopSourceRef source = + (CFRunLoopSourceRef) CFDictionaryGetValue (sources, key); + + if (source == NULL) + { + CFSocketRef socket = + CFSocketCreateWithNative (NULL, fd, + (kCFSocketReadCallBack + | kCFSocketConnectCallBack), + socket_callback, &context); + + if (socket == NULL) + continue; + source = CFSocketCreateRunLoopSource (NULL, socket, 0); + CFRelease (socket); + if (source == NULL) + continue; + CFDictionaryAddValue (sources, key, source); + CFRelease (source); + } + CFRunLoopAddSource (runloop, source, kCFRunLoopDefaultMode); + } + +#if USE_CG_DRAWING + mac_prepare_for_quickdraw (NULL); #endif - CFRunLoopAddSource (runloop, source, kCFRunLoopDefaultMode); -#ifdef SELECT_INVALIDATE_CFSOCKET - CFRelease (source); - *s = socket; -#else - CFRelease (socket); - *s = source; -#endif - s++; - } - - err = ReceiveNextEvent (0, NULL, timeout_sec, kEventLeaveInQueue, NULL); - - do - { - --s; -#ifdef SELECT_INVALIDATE_CFSOCKET - CFSocketInvalidate (*s); -#else - CFRunLoopRemoveSource (runloop, *s, kCFRunLoopDefaultMode); -#endif - CFRelease (*s); - } - while (s != shead); - - xfree (shead); - - if (err) - { - FD_ZERO (rfds); - r = 0; - } - else - { - FlushEventsMatchingListFromQueue (GetCurrentEventQueue (), - GetEventTypeCount (specs), - specs); - EMACS_SET_SECS_USECS (select_timeout, 0, 0); - r = select_and_poll_event (n, rfds, wfds, efds, &select_timeout); - } - - UNBLOCK_INPUT; - - return r; - } + err = ReceiveNextEvent (0, NULL, timeoutval, + kEventLeaveInQueue, NULL); + + for (fd = minfd; fd <= maxfd; fd++) + if (FD_ISSET (fd, rfds) || (wfds && FD_ISSET (fd, wfds))) + { + void *key = (void *) fd; + CFRunLoopSourceRef source = + (CFRunLoopSourceRef) CFDictionaryGetValue (sources, key); + + CFRunLoopRemoveSource (runloop, source, kCFRunLoopDefaultMode); + } + } + UNBLOCK_INPUT; + + if (err == noErr || err == eventLoopQuitErr) + { + EMACS_SET_SECS_USECS (select_timeout, 0, 0); + return select_and_poll_event (nfds, rfds, wfds, efds, + &select_timeout); + } + else + { + FD_ZERO (rfds); + if (wfds) + FD_ZERO (wfds); + return 0; + } #endif /* SELECT_USE_CFSOCKET */ } poll_periodically: { EMACS_TIME end_time, now, remaining_time; - SELECT_TYPE orfds = *rfds, owfds, oefds; - - if (wfds) - owfds = *wfds; - if (efds) - oefds = *efds; + if (timeout) { remaining_time = *timeout; @@ -5172,15 +5201,15 @@ EMACS_SET_SECS_USECS (select_timeout, 0, SELECT_POLLING_PERIOD_USEC); if (timeout && EMACS_TIME_LT (remaining_time, select_timeout)) select_timeout = remaining_time; - r = select_and_poll_event (n, rfds, wfds, efds, &select_timeout); + r = select_and_poll_event (nfds, rfds, wfds, efds, &select_timeout); if (r != 0) return r; - *rfds = orfds; + *rfds = ofds[0]; if (wfds) - *wfds = owfds; + *wfds = ofds[1]; if (efds) - *efds = oefds; + *efds = ofds[2]; if (timeout) { @@ -5190,12 +5219,8 @@ } while (!timeout || EMACS_TIME_LT (now, end_time)); - FD_ZERO (rfds); - if (wfds) - FD_ZERO (wfds); - if (efds) - FD_ZERO (efds); - return 0; + EMACS_SET_SECS_USECS (select_timeout, 0, 0); + return select_and_poll_event (nfds, rfds, wfds, efds, &select_timeout); } } @@ -5387,6 +5412,7 @@ #if TARGET_API_MAC_CARBON defsubr (&Smac_get_preference); defsubr (&Smac_code_convert_string); + defsubr (&Smac_process_hi_command); #endif defsubr (&Smac_set_file_creator);