summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/macselect.c367
1 files changed, 324 insertions, 43 deletions
diff --git a/src/macselect.c b/src/macselect.c
index 1b6a1a46cc3..40a3db9ca08 100644
--- a/src/macselect.c
+++ b/src/macselect.c
@@ -909,14 +909,13 @@ and t is the same as `SECONDARY'. */)
}
+/***********************************************************************
+ Apple event support
+***********************************************************************/
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;
+static Lisp_Object Qemacs_suspension_id;
extern Lisp_Object Qundefined;
extern void mac_store_apple_event P_ ((Lisp_Object, Lisp_Object,
const AEDesc *));
@@ -927,6 +926,19 @@ struct apple_event_binding
Lisp_Object key, binding;
};
+struct suspended_ae_info
+{
+ UInt32 expiration_tick, suspension_id;
+ AppleEvent apple_event, reply;
+ struct suspended_ae_info *next;
+};
+
+/* List of deferred apple events at the startup time. */
+static struct suspended_ae_info *deferred_apple_events = NULL;
+
+/* List of suspended apple events, in order of expiration_tick. */
+static struct suspended_ae_info *suspended_apple_events = NULL;
+
static void
find_event_binding_fun (key, binding, args, data)
Lisp_Object key, binding, args;
@@ -1003,6 +1015,12 @@ defer_apple_events (apple_event, reply)
const AppleEvent *apple_event, *reply;
{
OSErr err;
+ struct suspended_ae_info *new;
+
+ new = xmalloc (sizeof (struct suspended_ae_info));
+ bzero (new, sizeof (struct suspended_ae_info));
+ new->apple_event.descriptorType = typeNull;
+ new->reply.descriptorType = typeNull;
err = AESuspendTheCurrentEvent (apple_event);
@@ -1011,30 +1029,88 @@ defer_apple_events (apple_event, reply)
manual says it doesn't. Anyway we create copies of them and save
them in `deferred_apple_events'. */
if (err == noErr)
+ err = AEDuplicateDesc (apple_event, &new->apple_event);
+ if (err == noErr)
+ err = AEDuplicateDesc (reply, &new->reply);
+ 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);
- }
- else if (deferred_apple_events.count == deferred_apple_events.size)
+ new->next = deferred_apple_events;
+ deferred_apple_events = new;
+ }
+ else
+ {
+ AEDisposeDesc (&new->apple_event);
+ AEDisposeDesc (&new->reply);
+ xfree (new);
+ }
+
+ return err;
+}
+
+static OSErr
+mac_handle_apple_event_1 (class, id, apple_event, reply)
+ Lisp_Object class, id;
+ const AppleEvent *apple_event;
+ AppleEvent *reply;
+{
+ OSErr err;
+ static UInt32 suspension_id = 0;
+ struct suspended_ae_info *new;
+
+ new = xmalloc (sizeof (struct suspended_ae_info));
+ bzero (new, sizeof (struct suspended_ae_info));
+ new->apple_event.descriptorType = typeNull;
+ new->reply.descriptorType = typeNull;
+
+ err = AESuspendTheCurrentEvent (apple_event);
+ if (err == noErr)
+ err = AEDuplicateDesc (apple_event, &new->apple_event);
+ if (err == noErr)
+ err = AEDuplicateDesc (reply, &new->reply);
+ if (err == noErr)
+ err = AEPutAttributePtr (&new->apple_event, KEY_EMACS_SUSPENSION_ID_ATTR,
+ typeUInt32, &suspension_id, sizeof (UInt32));
+ if (err == noErr)
+ {
+ OSErr err1;
+ SInt32 reply_requested;
+
+ err1 = AEGetAttributePtr (&new->apple_event, keyReplyRequestedAttr,
+ typeSInt32, NULL, &reply_requested,
+ sizeof (SInt32), NULL);
+ if (err1 != noErr)
{
- deferred_apple_events.size *= 2;
- deferred_apple_events.buf
- = xrealloc (deferred_apple_events.buf,
- sizeof (AppleEvent) * deferred_apple_events.size);
+ /* Emulate keyReplyRequestedAttr in older versions. */
+ reply_requested = reply->descriptorType != typeNull;
+ err = AEPutAttributePtr (&new->apple_event, keyReplyRequestedAttr,
+ typeSInt32, &reply_requested,
+ sizeof (SInt32));
}
}
-
if (err == noErr)
{
- int count = deferred_apple_events.count;
+ SInt32 timeout = 0;
+ struct suspended_ae_info **p;
+
+ new->suspension_id = suspension_id;
+ suspension_id++;
+ err = AEGetAttributePtr (apple_event, keyTimeoutAttr, typeSInt32,
+ NULL, &timeout, sizeof (SInt32), NULL);
+ new->expiration_tick = TickCount () + timeout;
+
+ for (p = &suspended_apple_events; *p; p = &(*p)->next)
+ if ((*p)->expiration_tick >= new->expiration_tick)
+ break;
+ new->next = *p;
+ *p = new;
- AEDuplicateDesc (apple_event, deferred_apple_events.buf + count);
- AEDuplicateDesc (reply, deferred_apple_events.buf + count + 1);
- deferred_apple_events.count += 2;
+ mac_store_apple_event (class, id, &new->apple_event);
+ }
+ else
+ {
+ AEDisposeDesc (&new->reply);
+ AEDisposeDesc (&new->apple_event);
+ xfree (new);
}
return err;
@@ -1047,17 +1123,11 @@ mac_handle_apple_event (apple_event, reply, refcon)
SInt32 refcon;
{
OSErr err;
+ UInt32 suspension_id;
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);
@@ -1066,6 +1136,13 @@ mac_handle_apple_event (apple_event, reply, refcon)
return noErr;
}
+ err = AEGetAttributePtr (apple_event, KEY_EMACS_SUSPENSION_ID_ATTR,
+ typeUInt32, NULL,
+ &suspension_id, sizeof (UInt32), NULL);
+ if (err == noErr)
+ /* Previously suspended event. Pass it to the next handler. */
+ return errAEEventNotHandled;
+
err = AEGetAttributePtr (apple_event, keyEventClassAttr, typeType, NULL,
&event_class, sizeof (AEEventClass), NULL);
if (err == noErr)
@@ -1079,11 +1156,47 @@ mac_handle_apple_event (apple_event, reply, refcon)
{
if (INTEGERP (binding))
return XINT (binding);
- mac_store_apple_event (class_key, id_key, apple_event);
- return noErr;
+ err = mac_handle_apple_event_1 (class_key, id_key,
+ apple_event, reply);
}
}
- return errAEEventNotHandled;
+ if (err == noErr)
+ return noErr;
+ else
+ return errAEEventNotHandled;
+}
+
+static int
+cleanup_suspended_apple_events (head, all_p)
+ struct suspended_ae_info **head;
+ int all_p;
+{
+ UInt32 current_tick = TickCount (), nresumed = 0;
+ struct suspended_ae_info *p, *next;
+
+ for (p = *head; p; p = next)
+ {
+ if (!all_p && p->expiration_tick > current_tick)
+ break;
+ AESetTheCurrentEvent (&p->apple_event);
+ AEResumeTheCurrentEvent (&p->apple_event, &p->reply,
+ (AEEventHandlerUPP) kAENoDispatch, 0);
+ AEDisposeDesc (&p->reply);
+ AEDisposeDesc (&p->apple_event);
+ nresumed++;
+ next = p->next;
+ xfree (p);
+ }
+ *head = p;
+
+ return nresumed;
+}
+
+static void
+cleanup_all_suspended_apple_events ()
+{
+ cleanup_suspended_apple_events (&deferred_apple_events, 1);
+ cleanup_suspended_apple_events (&suspended_apple_events, 1);
}
void
@@ -1109,34 +1222,190 @@ init_apple_event_handler ()
0L, false);
if (err != noErr)
abort ();
+
+ atexit (cleanup_all_suspended_apple_events);
}
+static UInt32
+get_suspension_id (apple_event)
+ Lisp_Object apple_event;
+{
+ Lisp_Object tem;
+
+ CHECK_CONS (apple_event);
+ CHECK_STRING_CAR (apple_event);
+ if (SBYTES (XCAR (apple_event)) != 4
+ || strcmp (SDATA (XCAR (apple_event)), "aevt") != 0)
+ error ("Not an apple event");
+
+ tem = assq_no_quit (Qemacs_suspension_id, XCDR (apple_event));
+ if (NILP (tem))
+ error ("Suspension ID not available");
+
+ tem = XCDR (tem);
+ if (!(CONSP (tem)
+ && STRINGP (XCAR (tem)) && SBYTES (XCAR (tem)) == 4
+ && strcmp (SDATA (XCAR (tem)), "magn") == 0
+ && STRINGP (XCDR (tem)) && SBYTES (XCDR (tem)) == 4))
+ error ("Bad suspension ID format");
+
+ return *((UInt32 *) SDATA (XCDR (tem)));
+}
+
+
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)
+ if (deferred_apple_events)
{
- for (i = 0; i < deferred_apple_events.count; i += 2)
+ struct suspended_ae_info *prev, *tail, *next;
+
+ /* `nreverse' deferred_apple_events. */
+ prev = NULL;
+ for (tail = deferred_apple_events; tail; tail = next)
{
- AEResumeTheCurrentEvent (deferred_apple_events.buf + i,
- deferred_apple_events.buf + i + 1,
+ next = tail->next;
+ tail->next = prev;
+ prev = tail;
+ }
+
+ /* Now `prev' points to the first cell. */
+ for (tail = prev; tail; tail = next)
+ {
+ next = tail->next;
+ AEResumeTheCurrentEvent (&tail->apple_event, &tail->reply,
((AEEventHandlerUPP)
kAEUseStandardDispatch), 0);
- AEDisposeDesc (deferred_apple_events.buf + i);
- AEDisposeDesc (deferred_apple_events.buf + i + 1);
+ AEDisposeDesc (&tail->reply);
+ AEDisposeDesc (&tail->apple_event);
+ xfree (tail);
}
- xfree (deferred_apple_events.buf);
- bzero (&deferred_apple_events, sizeof (deferred_apple_events));
+ deferred_apple_events = NULL;
+ }
+ UNBLOCK_INPUT;
+
+ return Qt;
+}
+
+DEFUN ("mac-cleanup-expired-apple-events", Fmac_cleanup_expired_apple_events, Smac_cleanup_expired_apple_events, 0, 0, 0,
+ doc: /* Clean up expired Apple events.
+Return the number of expired events. */)
+ ()
+{
+ int nexpired;
+
+ BLOCK_INPUT;
+ nexpired = cleanup_suspended_apple_events (&suspended_apple_events, 0);
+ UNBLOCK_INPUT;
+
+ return make_number (nexpired);
+}
+
+DEFUN ("mac-ae-set-reply-parameter", Fmac_ae_set_reply_parameter, Smac_ae_set_reply_parameter, 3, 3, 0,
+ doc: /* Set parameter KEYWORD to DESCRIPTOR on reply of APPLE-EVENT.
+KEYWORD is a 4-byte string. DESCRIPTOR is a Lisp representation of an
+Apple event descriptor. It has the form of (TYPE . DATA), where TYPE
+is a 4-byte string. Valid format of DATA is as follows:
+
+ * If TYPE is "null", then DATA is nil.
+ * If TYPE is "list", then DATA is a list (DESCRIPTOR1 ... DESCRIPTORn).
+ * If TYPE is "reco", then DATA is a list ((KEYWORD1 . DESCRIPTOR1)
+ ... (KEYWORDn . DESCRIPTORn)).
+ * If TYPE is "aevt", then DATA is ignored and the descriptor is
+ treated as null.
+ * Otherwise, DATA is a string.
+
+If a (sub-)descriptor is in an invalid format, it is silently treated
+as null.
+
+Return t if the parameter is successfully set. Otherwise return nil. */)
+ (apple_event, keyword, descriptor)
+ Lisp_Object apple_event, keyword, descriptor;
+{
+ Lisp_Object result = Qnil;
+ UInt32 suspension_id;
+ struct suspended_ae_info *p;
+
+ suspension_id = get_suspension_id (apple_event);
+
+ CHECK_STRING (keyword);
+ if (SBYTES (keyword) != 4)
+ error ("Apple event keyword must be a 4-byte string: %s",
+ SDATA (keyword));
+
+ BLOCK_INPUT;
+ for (p = suspended_apple_events; p; p = p->next)
+ if (p->suspension_id == suspension_id)
+ break;
+ if (p && p->reply.descriptorType != typeNull)
+ {
+ OSErr err;
+
+ err = mac_ae_put_lisp (&p->reply,
+ EndianU32_BtoN (*((UInt32 *) SDATA (keyword))),
+ descriptor);
+ if (err == noErr)
+ result = Qt;
+ }
+ UNBLOCK_INPUT;
+
+ return result;
+}
+
+DEFUN ("mac-resume-apple-event", Fmac_resume_apple_event, Smac_resume_apple_event, 1, 2, 0,
+ doc: /* Resume handling of APPLE-EVENT.
+Every Apple event handled by the Lisp interpreter is suspended first.
+This function resumes such a suspended event either to complete Apple
+event handling to give a reply, or to redispatch it to other handlers.
+
+If optional ERROR-CODE is an integer, it specifies the error number
+that is set in the reply. If ERROR-CODE is t, the resumed event is
+handled with the standard dispatching mechanism, but it is not handled
+by Emacs again, thus it is redispatched to other handlers.
+
+Return t if APPLE-EVENT is successfully resumed. Otherwise return
+nil, which means the event is already resumed or expired. */)
+ (apple_event, error_code)
+ Lisp_Object apple_event, error_code;
+{
+ Lisp_Object result = Qnil;
+ UInt32 suspension_id;
+ struct suspended_ae_info **p, *ae;
+
+ suspension_id = get_suspension_id (apple_event);
+
+ BLOCK_INPUT;
+ for (p = &suspended_apple_events; *p; p = &(*p)->next)
+ if ((*p)->suspension_id == suspension_id)
+ break;
+ if (*p)
+ {
+ ae = *p;
+ *p = (*p)->next;
+ if (INTEGERP (error_code)
+ && ae->apple_event.descriptorType != typeNull)
+ {
+ SInt32 errn = XINT (error_code);
+
+ AEPutParamPtr (&ae->reply, keyErrorNumber, typeSInt32,
+ &errn, sizeof (SInt32));
+ }
+ AESetTheCurrentEvent (&ae->apple_event);
+ AEResumeTheCurrentEvent (&ae->apple_event, &ae->reply,
+ ((AEEventHandlerUPP)
+ (EQ (error_code, Qt) ?
+ kAEUseStandardDispatch : kAENoDispatch)),
+ 0);
+ AEDisposeDesc (&ae->reply);
+ AEDisposeDesc (&ae->apple_event);
+ xfree (ae);
result = Qt;
}
UNBLOCK_INPUT;
@@ -1145,6 +1414,9 @@ DEFUN ("mac-process-deferred-apple-events", Fmac_process_deferred_apple_events,
}
+/***********************************************************************
+ Drag and drop support
+***********************************************************************/
#if TARGET_API_MAC_CARBON
static Lisp_Object Vmac_dnd_known_types;
static pascal OSErr mac_do_track_drag P_ ((DragTrackingMessage, WindowRef,
@@ -1337,6 +1609,9 @@ remove_drag_handler (window)
}
+/***********************************************************************
+ Services menu support
+***********************************************************************/
#ifdef MAC_OSX
void
init_service_handler ()
@@ -1554,6 +1829,9 @@ syms_of_macselect ()
defsubr (&Sx_selection_owner_p);
defsubr (&Sx_selection_exists_p);
defsubr (&Smac_process_deferred_apple_events);
+ defsubr (&Smac_cleanup_expired_apple_events);
+ defsubr (&Smac_resume_apple_event);
+ defsubr (&Smac_ae_set_reply_parameter);
Vselection_alist = Qnil;
staticpro (&Vselection_alist);
@@ -1635,6 +1913,9 @@ The types are chosen in the order they appear in the list. */);
Qmac_apple_event_id = intern ("mac-apple-event-id");
staticpro (&Qmac_apple_event_id);
+
+ Qemacs_suspension_id = intern ("emacs-suspension-id");
+ staticpro (&Qemacs_suspension_id);
}
/* arch-tag: f3c91ad8-99e0-4bd6-9eef-251b2f848732