]> code.delx.au - gnu-emacs/blobdiff - src/mac.c
*** empty log message ***
[gnu-emacs] / src / mac.c
index 9d3af053495595535dcd08d20e422feb5c95449c..cce1b858cb3a43d6958951e0f614697fa6fdf077 100644 (file)
--- a/src/mac.c
+++ b/src/mac.c
@@ -24,12 +24,14 @@ Boston, MA 02111-1307, USA.  */
 
 #include <stdio.h>
 #include <errno.h>
+#include <time.h>
 #include <utime.h>
 #include <dirent.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <string.h>
 #include <pwd.h>
+#include <grp.h>
 #include <sys/param.h>
 #include <stdlib.h>
 #include <fcntl.h>
@@ -45,6 +47,8 @@ Boston, MA 02111-1307, USA.  */
 #undef realloc
 #undef init_process
 #include <Carbon/Carbon.h>
+#undef mktime
+#define mktime emacs_mktime
 #undef free
 #define free unexec_free
 #undef malloc
@@ -71,6 +75,7 @@ Boston, MA 02111-1307, USA.  */
 #include "process.h"
 #include "sysselect.h"
 #include "systime.h"
+#include "blockinput.h"
 
 Lisp_Object QCLIPBOARD;
 
@@ -257,6 +262,22 @@ posix_to_mac_pathname (const char *ufn, char *mfn, int mfnbuflen)
   return 1;
 }
 
+#if TARGET_API_MAC_CARBON
+CFStringRef
+cfstring_create_with_utf8_cstring (c_str)
+     const char *c_str;
+{
+  CFStringRef str;
+
+  str = CFStringCreateWithCString (NULL, c_str, kCFStringEncodingUTF8);
+  if (str == NULL)
+    /* Failed to interpret as UTF 8.  Fall back on Mac Roman.  */
+    str = CFStringCreateWithCString (NULL, c_str, kCFStringEncodingMacRoman);
+
+  return str;
+}
+#endif
+
 #ifndef MAC_OSX
 
 /* The following functions with "sys_" prefix are stubs to Unix
@@ -824,6 +845,8 @@ check_alarm ()
 }
 
 
+extern Boolean mac_wait_next_event (EventRecord *, UInt32, Boolean);
+
 int
 select (n,  rfds, wfds, efds, timeout)
   int n;
@@ -832,49 +855,24 @@ select (n,  rfds, wfds, efds, timeout)
   SELECT_TYPE *efds;
   struct timeval *timeout;
 {
-#ifdef TARGET_API_MAC_CARBON
+#if TARGET_API_MAC_CARBON
   return 1;
 #else /* not TARGET_API_MAC_CARBON */
-  EMACS_TIME end_time, now;
   EventRecord e;
+  UInt32 sleep_time = EMACS_SECS (*timeout) * 60 +
+    ((EMACS_USECS (*timeout) * 60) / 1000000);
 
   /* Can only handle wait for keyboard input.  */
   if (n > 1 || wfds || efds)
     return -1;
 
-  EMACS_GET_TIME (end_time);
-  EMACS_ADD_TIME (end_time, end_time, *timeout);
-
-  do
-    {
-      /* 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 (FD_ISSET (0, rfds) && EventAvail (everyEvent, &e))
-        return 1;
-
-      /* Also check movement of the mouse.  */
-      {
-        Point mouse_pos;
-        static Point old_mouse_pos = {-1, -1};
-
-        GetMouse (&mouse_pos);
-        if (!EqualPt (mouse_pos, old_mouse_pos))
-          {
-            old_mouse_pos = mouse_pos;
-            return 1;
-          }
-      }
-
-      WaitNextEvent (0, &e, 1UL, NULL);        /* Accept no event; wait 1
-                                          tic. by T.I. */
-
-      EMACS_GET_TIME (now);
-      EMACS_SUB_TIME (now, end_time, now);
-    }
-  while (!EMACS_TIME_NEG_P (now));
+  /* 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 (FD_ISSET (0, rfds) && mac_wait_next_event (&e, sleep_time, false))
+    return 1;
 
   return 0;
 #endif /* not TARGET_API_MAC_CARBON */
@@ -1164,6 +1162,13 @@ static struct passwd my_passwd =
   my_passwd_dir,
 };
 
+static struct group my_group =
+{
+  /* There are no groups on the mac, so we just return "root" as the
+     group name.  */
+  "root",
+};
+
 
 /* Initialized by main () in macterm.c to pathname of emacs directory.  */
 
@@ -1258,6 +1263,13 @@ getpwuid (uid_t uid)
 }
 
 
+struct group *
+getgrgid (gid_t gid)
+{
+  return &my_group;
+}
+
+
 struct passwd *
 getpwnam (const char *name)
 {
@@ -1961,7 +1973,7 @@ run_mac_command (argv, workdir, infn, outfn, errfn)
      const char *workdir;
      const char *infn, *outfn, *errfn;
 {
-#ifdef TARGET_API_MAC_CARBON
+#if TARGET_API_MAC_CARBON
   return -1;
 #else /* not TARGET_API_MAC_CARBON */
   char macappname[MAXPATHLEN+1], macworkdir[MAXPATHLEN+1];
@@ -2046,7 +2058,7 @@ run_mac_command (argv, workdir, infn, outfn, errfn)
          strcat (t, newargv[0]);
 #endif /* 0 */
          Lisp_Object path;
-         openp (Vexec_path, build_string (newargv[0]), EXEC_SUFFIXES, &path,
+         openp (Vexec_path, build_string (newargv[0]), Vexec_suffixes, &path,
                 make_number (X_OK));
 
          if (NILP (path))
@@ -2532,7 +2544,9 @@ component.  */)
 
   CHECK_STRING (script);
 
+  BLOCK_INPUT;
   status = do_applescript (SDATA (script), &result);
+  UNBLOCK_INPUT;
   if (status)
     {
       if (!result)
@@ -2602,26 +2616,23 @@ DEFUN ("mac-paste-function", Fmac_paste_function, Smac_paste_function, 0, 0, 0,
      ()
 {
 #if TARGET_API_MAC_CARBON
+  OSStatus err;
   ScrapRef scrap;
   ScrapFlavorFlags sff;
   Size s;
   int i;
   char *data;
 
-  if (GetCurrentScrap (&scrap) != noErr)
-    return Qnil;
-
-  if (GetScrapFlavorFlags (scrap, kScrapFlavorTypeText, &sff) != noErr)
-    return Qnil;
-
-  if (GetScrapFlavorSize (scrap, kScrapFlavorTypeText, &s) != noErr)
-    return Qnil;
-
-  if ((data = (char*) alloca (s)) == NULL)
-    return Qnil;
-
-  if (GetScrapFlavorData (scrap, kScrapFlavorTypeText, &s, data) != noErr
-      || s == 0)
+  BLOCK_INPUT;
+  err = GetCurrentScrap (&scrap);
+  if (err == noErr)
+    err = GetScrapFlavorFlags (scrap, kScrapFlavorTypeText, &sff);
+  if (err == noErr)
+    err = GetScrapFlavorSize (scrap, kScrapFlavorTypeText, &s);
+  if (err == noErr && (data = (char*) alloca (s)))
+    err = GetScrapFlavorData (scrap, kScrapFlavorTypeText, &s, data);
+  UNBLOCK_INPUT;
+  if (err != noErr || s == 0)
     return Qnil;
 
   /* Emacs expects clipboard contents have Unix-style eol's */
@@ -2686,13 +2697,22 @@ DEFUN ("mac-cut-function", Fmac_cut_function, Smac_cut_function, 1, 2, 0,
 #if TARGET_API_MAC_CARBON
   {
     ScrapRef scrap;
+
+    BLOCK_INPUT;
     ClearCurrentScrap ();
     if (GetCurrentScrap (&scrap) != noErr)
-      error ("cannot get current scrap");
+      {
+       UNBLOCK_INPUT;
+       error ("cannot get current scrap");
+      }
 
     if (PutScrapFlavor (scrap, kScrapFlavorTypeText, kScrapFlavorMaskNone, len,
                        buf) != noErr)
-      error ("cannot put to scrap");
+      {
+       UNBLOCK_INPUT;
+       error ("cannot put to scrap");
+      }
+    UNBLOCK_INPUT;
   }
 #else /* not TARGET_API_MAC_CARBON */
   ZeroScrap ();
@@ -2727,9 +2747,11 @@ and t is the same as `SECONDARY'.  */)
       ScrapRef scrap;
       ScrapFlavorFlags sff;
 
+      BLOCK_INPUT;
       if (GetCurrentScrap (&scrap) == noErr)
         if (GetScrapFlavorFlags (scrap, kScrapFlavorTypeText, &sff) == noErr)
           val = Qt;
+      UNBLOCK_INPUT;
 #else /* not TARGET_API_MAC_CARBON */
       Handle my_handle;
       long rc, scrap_offset;
@@ -2748,97 +2770,284 @@ and t is the same as `SECONDARY'.  */)
   return Qnil;
 }
 
+extern void mac_clear_font_name_table P_ ((void));
+
+DEFUN ("mac-clear-font-name-table", Fmac_clear_font_name_table, Smac_clear_font_name_table, 0, 0, 0,
+       doc: /* Clear the font name table.  */)
+  ()
+{
+  check_mac ();
+  mac_clear_font_name_table ();
+  return Qnil;
+}
+
 #ifdef MAC_OSX
 #undef select
 
 extern int inhibit_window_system;
 extern int noninteractive;
 
-/* When Emacs is started from the Finder, SELECT always immediately
-   returns as if input is present when file descriptor 0 is polled for
-   input.  Strangely, when Emacs is run as a GUI application from the
-   command line, it blocks in the same situation.  This `wrapper' of
-   the system call SELECT corrects this discrepancy.  */
+/* Unlike in X11, window events in Carbon do not come from sockets.
+   So we cannot simply use `select' to monitor two kinds of inputs:
+   window events and process outputs.  We emulate such functionality
+   by regarding fd 0 as the window event channel and simultaneously
+   monitoring both kinds of input channels.  It is implemented by
+   dividing into some cases:
+   1. The window event channel is not involved.
+      -> 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
+      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 an `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.
+   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_TIMEOUT_THRESHOLD_RUNLOOP 0.2
+#define EVENT_CLASS_SOCK 'Sock'
+
+static void
+socket_callback (s, type, address, data, info)
+     CFSocketRef s;
+     CFSocketCallBackType type;
+     CFDataRef address;
+     const void *data;
+     void *info;
+{
+  EventRef event;
+
+  CreateEvent (NULL, EVENT_CLASS_SOCK, 0, 0, kEventAttributeNone, &event);
+  PostEventToQueue (GetCurrentEventQueue (), event, kEventPriorityStandard);
+  ReleaseEvent (event);
+}
+#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;
+{
+  int r;
+  OSErr err;
+
+  r = select (n, rfds, wfds, efds, timeout);
+  if (r != -1)
+    {
+      BLOCK_INPUT;
+      err = ReceiveNextEvent (0, NULL, kEventDurationNoWait,
+                             kEventLeaveInQueue, NULL);
+      UNBLOCK_INPUT;
+      if (err == noErr)
+       {
+         FD_SET (0, rfds);
+         r++;
+       }
+    }
+  return r;
+}
+
+#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;
+     int n;
+     SELECT_TYPE *rfds;
+     SELECT_TYPE *wfds;
+     SELECT_TYPE *efds;
+     struct timeval *timeout;
 {
-  if (!inhibit_window_system && rfds && FD_ISSET (0, rfds))
-    return 1;
-  else if (inhibit_window_system || noninteractive ||
-          (timeout && (EMACS_SECS(*timeout)==0) &&
-           (EMACS_USECS(*timeout)==0)))
-    return select(n, rfds, wfds, efds, timeout);
-  else
+  OSErr err;
+  int i, r;
+  EMACS_TIME select_timeout;
+
+  if (inhibit_window_system || noninteractive
+      || rfds == NULL || !FD_ISSET (0, rfds))
+    return select (n, rfds, wfds, efds, timeout);
+
+  FD_CLR (0, rfds);
+
+  if (wfds == NULL && efds == NULL)
     {
-      EMACS_TIME end_time, now;
+      int nsocks = 0;
+      SELECT_TYPE orfds = *rfds;
 
-      EMACS_GET_TIME (end_time);
-      if (timeout)
-       EMACS_ADD_TIME (end_time, end_time, *timeout);
+      EventTimeout timeout_sec =
+       (timeout
+        ? (EMACS_SECS (*timeout) * kEventDurationSecond
+           + EMACS_USECS (*timeout) * kEventDurationMicrosecond)
+        : kEventDurationForever);
 
-      do
-       {
-         int r;
-         EMACS_TIME one_second;
-         SELECT_TYPE orfds;
+      for (i = 1; i < n; i++)
+       if (FD_ISSET (i, rfds))
+         nsocks++;
 
-         FD_ZERO (&orfds);
-         if (rfds)
+      if (nsocks == 0)
+       {
+         BLOCK_INPUT;
+         err = ReceiveNextEvent (0, NULL, timeout_sec,
+                                 kEventLeaveInQueue, NULL);
+         UNBLOCK_INPUT;
+         if (err == noErr)
            {
-             orfds = *rfds;
+             FD_SET (0, rfds);
+             return 1;
            }
+         else
+           return 0;
+       }
+
+      /* 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)
+       return r;
+
+      *rfds = orfds;
+
+#ifdef SELECT_USE_CFSOCKET
+      if (timeout_sec > 0 && timeout_sec <= SELECT_TIMEOUT_THRESHOLD_RUNLOOP)
+       goto poll_periodically;
 
-         EMACS_SET_SECS (one_second, 1);
-         EMACS_SET_USECS (one_second, 0);
+      {
+       CFRunLoopRef runloop =
+         (CFRunLoopRef) GetCFRunLoopFromEventLoop (GetCurrentEventLoop ());
+       EventTypeSpec specs[] = {{EVENT_CLASS_SOCK, 0}};
+#ifdef SELECT_INVALIDATE_CFSOCKET
+       CFSocketRef *shead, *s;
+#else
+       CFRunLoopSourceRef *shead, *s;
+#endif
 
-         if (timeout && EMACS_TIME_LT(*timeout, one_second))
-           one_second = *timeout;
+       BLOCK_INPUT;
 
-         if ((r = select (n, &orfds, wfds, efds, &one_second)) > 0)
+#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))
            {
-             *rfds = orfds;
-             return r;
+             CFSocketRef socket =
+               CFSocketCreateWithNative (NULL, i, kCFSocketReadCallBack,
+                                         socket_callback, NULL);
+             CFRunLoopSourceRef source =
+               CFSocketCreateRunLoopSource (NULL, socket, 0);
+
+#ifdef SELECT_INVALIDATE_CFSOCKET
+             CFSocketSetSocketFlags (socket, 0);
+#endif
+             CFRunLoopAddSource (runloop, source, kCFRunLoopDefaultMode);
+#ifdef SELECT_INVALIDATE_CFSOCKET
+             CFRelease (source);
+             *s = socket;
+#else
+             CFRelease (socket);
+             *s = source;
+#endif
+             s++;
            }
 
-         mac_check_for_quit_char();
+       err = ReceiveNextEvent (0, NULL, timeout_sec, kEventLeaveInQueue, NULL);
 
-         EMACS_GET_TIME (now);
-         EMACS_SUB_TIME (now, end_time, now);
-       }
-      while (!timeout || !EMACS_TIME_NEG_P (now));
+       do
+         {
+           --s;
+#ifdef SELECT_INVALIDATE_CFSOCKET
+           CFSocketInvalidate (*s);
+#else
+           CFRunLoopRemoveSource (runloop, *s, kCFRunLoopDefaultMode);
+#endif
+           CFRelease (*s);
+         }
+       while (s != shead);
 
-      return 0;
-    }
-}
+       xfree (shead);
 
-#undef read
-int sys_read (fds, buf, nbyte)
-     int fds;
-     char *buf;
-     unsigned int nbyte;
-{
-  SELECT_TYPE rfds;
-  EMACS_TIME one_second;
-  int r;
+       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);
+         }
 
-  /* Use select to block on IO while still checking for quit_char */
-  if (!inhibit_window_system && !noninteractive &&
-      ! (fcntl(fds, F_GETFL, 0) & O_NONBLOCK))
-    {
-      FD_ZERO (&rfds);
-      FD_SET (fds, &rfds);
-      if (sys_select (fds+1, &rfds, 0, 0, NULL) < 0)
-       return -1;
+       UNBLOCK_INPUT;
+
+       return r;
+      }
+#endif /* SELECT_USE_CFSOCKET */
     }
 
-  return read (fds, buf, nbyte);
-}
+ 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;
+       EMACS_GET_TIME (now);
+       EMACS_ADD_TIME (end_time, now, remaining_time);
+      }
+
+    do
+      {
+       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);
+       if (r != 0)
+         return r;
+
+       *rfds = orfds;
+       if (wfds)
+         *wfds = owfds;
+       if (efds)
+         *efds = oefds;
+
+       if (timeout)
+         {
+           EMACS_GET_TIME (now);
+           EMACS_SUB_TIME (remaining_time, end_time, now);
+         }
+      }
+    while (!timeout || EMACS_TIME_LT (now, end_time));
 
+    FD_ZERO (rfds);
+    if (wfds)
+      FD_ZERO (wfds);
+    if (efds)
+      FD_ZERO (efds);
+    return 0;
+  }
+}
 
 /* Set up environment variables so that Emacs can correctly find its
    support files when packaged as an application bundle.  Directories
@@ -2983,6 +3192,7 @@ syms_of_mac ()
   defsubr (&Smac_paste_function);
   defsubr (&Smac_cut_function);
   defsubr (&Sx_selection_exists_p);
+  defsubr (&Smac_clear_font_name_table);
 
   defsubr (&Sdo_applescript);
   defsubr (&Smac_file_name_to_posix);