+int mac_ready_for_apple_events = 0;
+static Lisp_Object Vmac_apple_event_map;
+static Lisp_Object Qmac_apple_event_class, Qmac_apple_event_id;
+static struct
+{
+ AppleEvent *buf;
+ int size, count;
+} deferred_apple_events;
+extern Lisp_Object Qundefined;
+extern OSErr mac_store_apple_event P_ ((Lisp_Object, Lisp_Object,
+ const AEDesc *));
+
+struct apple_event_binding
+{
+ UInt32 code; /* Apple event class or ID. */
+ Lisp_Object key, binding;
+};
+
+static void
+find_event_binding_fun (key, binding, args, data)
+ Lisp_Object key, binding, args;
+ void *data;
+{
+ struct apple_event_binding *event_binding =
+ (struct apple_event_binding *)data;
+ Lisp_Object code_string;
+
+ if (!SYMBOLP (key))
+ return;
+ code_string = Fget (key, args);
+ if (STRINGP (code_string) && SBYTES (code_string) == 4
+ && (EndianU32_BtoN (*((UInt32 *) SDATA (code_string)))
+ == event_binding->code))
+ {
+ event_binding->key = key;
+ event_binding->binding = binding;
+ }
+}
+
+static void
+find_event_binding (keymap, event_binding, class_p)
+ Lisp_Object keymap;
+ struct apple_event_binding *event_binding;
+ int class_p;
+{
+ if (event_binding->code == 0)
+ event_binding->binding =
+ access_keymap (keymap, event_binding->key, 0, 1, 0);
+ else
+ {
+ event_binding->binding = Qnil;
+ map_keymap (keymap, find_event_binding_fun,
+ class_p ? Qmac_apple_event_class : Qmac_apple_event_id,
+ event_binding, 0);
+ }
+}
+
+void
+mac_find_apple_event_spec (class, id, class_key, id_key, binding)
+ AEEventClass class;
+ AEEventID id;
+ Lisp_Object *class_key, *id_key, *binding;
+{
+ struct apple_event_binding event_binding;
+ Lisp_Object keymap;
+
+ *binding = Qnil;
+
+ keymap = get_keymap (Vmac_apple_event_map, 0, 0);
+ if (NILP (keymap))
+ return;
+
+ event_binding.code = class;
+ event_binding.key = *class_key;
+ event_binding.binding = Qnil;
+ find_event_binding (keymap, &event_binding, 1);
+ *class_key = event_binding.key;
+ keymap = get_keymap (event_binding.binding, 0, 0);
+ if (NILP (keymap))
+ return;
+
+ event_binding.code = id;
+ event_binding.key = *id_key;
+ event_binding.binding = Qnil;
+ find_event_binding (keymap, &event_binding, 0);
+ *id_key = event_binding.key;
+ *binding = event_binding.binding;
+}
+
+static OSErr
+defer_apple_events (apple_event, reply)
+ const AppleEvent *apple_event, *reply;
+{
+ OSErr err;
+
+ err = AESuspendTheCurrentEvent (apple_event);
+
+ /* Mac OS 10.3 Xcode manual says AESuspendTheCurrentEvent makes
+ copies of the Apple event and the reply, but Mac OS 10.4 Xcode
+ manual says it doesn't. Anyway we create copies of them and save
+ them in `deferred_apple_events'. */
+ if (err == noErr)
+ {
+ if (deferred_apple_events.buf == NULL)
+ {
+ deferred_apple_events.size = 16;
+ deferred_apple_events.count = 0;
+ deferred_apple_events.buf =
+ xmalloc (sizeof (AppleEvent) * deferred_apple_events.size);
+ if (deferred_apple_events.buf == NULL)
+ err = memFullErr;
+ }
+ else if (deferred_apple_events.count == deferred_apple_events.size)
+ {
+ AppleEvent *newbuf;
+
+ deferred_apple_events.size *= 2;
+ newbuf = xrealloc (deferred_apple_events.buf,
+ sizeof (AppleEvent) * deferred_apple_events.size);
+ if (newbuf)
+ deferred_apple_events.buf = newbuf;
+ else
+ err = memFullErr;
+ }
+ }
+
+ if (err == noErr)
+ {
+ int count = deferred_apple_events.count;
+
+ AEDuplicateDesc (apple_event, deferred_apple_events.buf + count);
+ AEDuplicateDesc (reply, deferred_apple_events.buf + count + 1);
+ deferred_apple_events.count += 2;
+ }
+
+ return err;
+}
+
+static pascal OSErr
+mac_handle_apple_event (apple_event, reply, refcon)
+ const AppleEvent *apple_event;
+ AppleEvent *reply;
+ SInt32 refcon;
+{
+ OSErr err;
+ AEEventClass event_class;
+ AEEventID event_id;
+ Lisp_Object class_key, id_key, binding;
+
+ /* We can't handle an Apple event that requests a reply, but this
+ seems to be too restrictive. */
+#if 0
+ if (reply->descriptorType != typeNull)
+ return errAEEventNotHandled;
+#endif
+
+ if (!mac_ready_for_apple_events)
+ {
+ err = defer_apple_events (apple_event, reply);
+ if (err != noErr)
+ return errAEEventNotHandled;
+ return noErr;
+ }
+
+ err = AEGetAttributePtr (apple_event, keyEventClassAttr, typeType, NULL,
+ &event_class, sizeof (AEEventClass), NULL);
+ if (err == noErr)
+ err = AEGetAttributePtr (apple_event, keyEventIDAttr, typeType, NULL,
+ &event_id, sizeof (AEEventID), NULL);
+ if (err == noErr)
+ {
+ mac_find_apple_event_spec (event_class, event_id,
+ &class_key, &id_key, &binding);
+ if (!NILP (binding) && !EQ (binding, Qundefined))
+ {
+ if (INTEGERP (binding))
+ return XINT (binding);
+ err = mac_store_apple_event (class_key, id_key, apple_event);
+ if (err == noErr)
+ return noErr;
+ }
+ }
+ return errAEEventNotHandled;
+}
+
+void
+init_apple_event_handler ()
+{
+ OSErr err;
+ long result;
+
+ /* Make sure we have Apple events before starting. */
+ err = Gestalt (gestaltAppleEventsAttr, &result);
+ if (err != noErr)
+ abort ();
+
+ if (!(result & (1 << gestaltAppleEventsPresent)))
+ abort ();
+
+ err = AEInstallEventHandler (typeWildCard, typeWildCard,
+#if TARGET_API_MAC_CARBON
+ NewAEEventHandlerUPP (mac_handle_apple_event),
+#else
+ NewAEEventHandlerProc (mac_handle_apple_event),
+#endif
+ 0L, false);
+ if (err != noErr)
+ abort ();
+}
+
+DEFUN ("mac-process-deferred-apple-events", Fmac_process_deferred_apple_events, Smac_process_deferred_apple_events, 0, 0, 0,
+ doc: /* Process Apple events that are deferred at the startup time. */)
+ ()
+{
+ Lisp_Object result = Qnil;
+ long i;
+
+ if (mac_ready_for_apple_events)
+ return Qnil;
+
+ BLOCK_INPUT;
+ mac_ready_for_apple_events = 1;
+ if (deferred_apple_events.buf)
+ {
+ for (i = 0; i < deferred_apple_events.count; i += 2)
+ {
+ AEResumeTheCurrentEvent (deferred_apple_events.buf + i,
+ deferred_apple_events.buf + i + 1,
+ ((AEEventHandlerUPP)
+ kAEUseStandardDispatch), 0);
+ AEDisposeDesc (deferred_apple_events.buf + i);
+ AEDisposeDesc (deferred_apple_events.buf + i + 1);
+ }
+ xfree (deferred_apple_events.buf);
+ bzero (&deferred_apple_events, sizeof (deferred_apple_events));
+
+ result = Qt;
+ }
+ UNBLOCK_INPUT;
+
+ return result;
+}
+
+\f