Mercurial > emacs
changeset 61777:c323711ae68f
New file for selection processing on Mac OS.
author | YAMAMOTO Mitsuharu <mituharu@math.s.chiba-u.ac.jp> |
---|---|
date | Sun, 24 Apr 2005 05:58:47 +0000 |
parents | 5cf63ac314d6 |
children | 1b44d34506e4 |
files | src/macselect.c |
diffstat | 1 files changed, 1118 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/macselect.c Sun Apr 24 05:58:47 2005 +0000 @@ -0,0 +1,1118 @@ +/* Selection processing for Emacs on Mac OS. + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Emacs is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Emacs; see the file COPYING. If not, write to +the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ + +#include <config.h> + +#include "lisp.h" +#include "macterm.h" +#include "blockinput.h" + +#if !TARGET_API_MAC_CARBON +#include <Endian.h> +typedef int ScrapRef; +typedef ResType ScrapFlavorType; +#endif /* !TARGET_API_MAC_CARBON */ + +static OSErr get_scrap_from_symbol P_ ((Lisp_Object, int, ScrapRef *)); +static ScrapFlavorType get_flavor_type_from_symbol P_ ((Lisp_Object)); +static int valid_scrap_target_type_p P_ ((Lisp_Object)); +static OSErr clear_scrap P_ ((ScrapRef *)); +static OSErr put_scrap_string P_ ((ScrapRef, Lisp_Object, Lisp_Object)); +static OSErr put_scrap_private_timestamp P_ ((ScrapRef, unsigned long)); +static ScrapFlavorType scrap_has_target_type P_ ((ScrapRef, Lisp_Object)); +static Lisp_Object get_scrap_string P_ ((ScrapRef, Lisp_Object)); +static OSErr get_scrap_private_timestamp P_ ((ScrapRef, unsigned long *)); +static Lisp_Object get_scrap_target_type_list P_ ((ScrapRef)); +static void x_own_selection P_ ((Lisp_Object, Lisp_Object)); +static Lisp_Object x_get_local_selection P_ ((Lisp_Object, Lisp_Object, int)); +static Lisp_Object x_get_foreign_selection P_ ((Lisp_Object, + Lisp_Object, + Lisp_Object)); +EXFUN (Fx_selection_owner_p, 1); +#ifdef MAC_OSX +static OSStatus mac_handle_service_event P_ ((EventHandlerCallRef, + EventRef, void *)); +void init_service_handler P_ ((void)); +#endif + +Lisp_Object QPRIMARY, QSECONDARY, QTIMESTAMP, QTARGETS; + +static Lisp_Object Vx_lost_selection_functions; +/* Coding system for communicating with other programs via scrap. */ +static Lisp_Object Vselection_coding_system; + +/* Coding system for the next communicating with other programs. */ +static Lisp_Object Vnext_selection_coding_system; + +static Lisp_Object Qforeign_selection; + +/* The timestamp of the last input event Emacs received from the + window server. */ +/* Defined in keyboard.c. */ +extern unsigned long last_event_timestamp; + +/* This is an association list whose elements are of the form + ( SELECTION-NAME SELECTION-VALUE SELECTION-TIMESTAMP FRAME) + SELECTION-NAME is a lisp symbol. + SELECTION-VALUE is the value that emacs owns for that selection. + It may be any kind of Lisp object. + SELECTION-TIMESTAMP is the time at which emacs began owning this selection, + as a cons of two 16-bit numbers (making a 32 bit time.) + FRAME is the frame for which we made the selection. + If there is an entry in this alist, and the data for the flavor + type SCRAP_FLAVOR_TYPE_EMACS_TIMESTAMP in the corresponding scrap + (if exists) coincides with SELECTION-TIMESTAMP, then it can be + assumed that Emacs owns that selection. + The only (eq) parts of this list that are visible from Lisp are the + selection-values. */ +static Lisp_Object Vselection_alist; + +#define SCRAP_FLAVOR_TYPE_EMACS_TIMESTAMP 'Etsp' + +/* This is an alist whose CARs are selection-types and whose CDRs are + the names of Lisp functions to call to convert the given Emacs + selection value to a string representing the given selection type. + This is for Lisp-level extension of the emacs selection + handling. */ +static Lisp_Object Vselection_converter_alist; + +/* A selection name (represented as a Lisp symbol) can be associated + with a named scrap via `mac-scrap-name' property. Likewise for a + selection type with a scrap flavor type via `mac-ostype'. */ +static Lisp_Object Qmac_scrap_name, Qmac_ostype; + +/* Selection name for communication via Services menu. */ +static Lisp_Object Vmac_services_selection; + +/* Get a reference to the scrap corresponding to the symbol SYM. The + reference is set to *SCRAP, and it becomes NULL if there's no + corresponding scrap. Clear the scrap if CLEAR_P is non-zero. */ + +static OSErr +get_scrap_from_symbol (sym, clear_p, scrap) + Lisp_Object sym; + int clear_p; + ScrapRef *scrap; +{ + OSErr err = noErr; + Lisp_Object str = Fget (sym, Qmac_scrap_name); + + if (!STRINGP (str)) + *scrap = NULL; + else + { +#if TARGET_API_MAC_CARBON +#ifdef MAC_OSX + CFStringRef scrap_name = cfstring_create_with_string (str); + OptionBits options = (clear_p ? kScrapClearNamedScrap + : kScrapGetNamedScrap); + + err = GetScrapByName (scrap_name, options, scrap); + CFRelease (scrap_name); +#else /* !MAC_OSX */ + if (clear_p) + err = ClearCurrentScrap (); + if (err == noErr) + err = GetCurrentScrap (scrap); +#endif /* !MAC_OSX */ +#else /* !TARGET_API_MAC_CARBON */ + if (clear_p) + err = ZeroScrap (); + if (err == noErr) + *scrap = 1; +#endif /* !TARGET_API_MAC_CARBON */ + } + + return err; +} + +/* Get a scrap flavor type from the symbol SYM. Return 0 if no + corresponding flavor type. */ + +static ScrapFlavorType +get_flavor_type_from_symbol (sym) + Lisp_Object sym; +{ + ScrapFlavorType val; + Lisp_Object str = Fget (sym, Qmac_ostype); + + if (STRINGP (str) && SBYTES (str) == 4) + return EndianU32_BtoN (*((UInt32 *) SDATA (str))); + + return 0; +} + +/* Check if the symbol SYM has a corresponding scrap flavor type. */ + +static int +valid_scrap_target_type_p (sym) + Lisp_Object sym; +{ + return get_flavor_type_from_symbol (sym) != 0; +} + +/* Clear the scrap whose reference is *SCRAP. */ + +static INLINE OSErr +clear_scrap (scrap) + ScrapRef *scrap; +{ +#if TARGET_API_MAC_CARBON +#ifdef MAC_OSX + return ClearScrap (scrap); +#else + return ClearCurrentScrap (); +#endif +#else /* !TARGET_API_MAC_CARBON */ + return ZeroScrap (); +#endif /* !TARGET_API_MAC_CARBON */ +} + +/* Put Lisp String STR to the scrap SCRAP. The target type is + specified by TYPE. */ + +static OSErr +put_scrap_string (scrap, type, str) + ScrapRef scrap; + Lisp_Object type, str; +{ + ScrapFlavorType flavor_type = get_flavor_type_from_symbol (type); + + if (flavor_type == 0) + return noTypeErr; + +#if TARGET_API_MAC_CARBON + return PutScrapFlavor (scrap, flavor_type, kScrapFlavorMaskNone, + SBYTES (str), SDATA (str)); +#else /* !TARGET_API_MAC_CARBON */ + return PutScrap (SBYTES (str), flavor_type, SDATA (str)); +#endif /* !TARGET_API_MAC_CARBON */ +} + +/* Put TIMESTAMP to the scrap SCRAP. The timestamp is used for + checking if the scrap is owned by the process. */ + +static INLINE OSErr +put_scrap_private_timestamp (scrap, timestamp) + ScrapRef scrap; + unsigned long timestamp; +{ +#if TARGET_API_MAC_CARBON + return PutScrapFlavor (scrap, SCRAP_FLAVOR_TYPE_EMACS_TIMESTAMP, + kScrapFlavorMaskSenderOnly, + sizeof (timestamp), ×tamp); +#else /* !TARGET_API_MAC_CARBON */ + return PutScrap (sizeof (timestamp), SCRAP_FLAVOR_TYPE_EMACS_TIMESTAMP, + ×tamp); +#endif /* !TARGET_API_MAC_CARBON */ +} + +/* Check if data for the target type TYPE is available in SCRAP. */ + +static ScrapFlavorType +scrap_has_target_type (scrap, type) + ScrapRef scrap; + Lisp_Object type; +{ + OSErr err; + ScrapFlavorType flavor_type = get_flavor_type_from_symbol (type); + + if (flavor_type) + { +#if TARGET_API_MAC_CARBON + ScrapFlavorFlags flags; + + err = GetScrapFlavorFlags (scrap, flavor_type, &flags); + if (err != noErr) + flavor_type = 0; +#else /* !TARGET_API_MAC_CARBON */ + SInt32 size, offset; + + size = GetScrap (NULL, flavor_type, &offset); + if (size < 0) + flavor_type = 0; +#endif /* !TARGET_API_MAC_CARBON */ + } + + return flavor_type; +} + +/* Get data for the target type TYPE from SCRAP and create a Lisp + string. Return nil if failed to get data. */ + +static Lisp_Object +get_scrap_string (scrap, type) + ScrapRef scrap; + Lisp_Object type; +{ + OSErr err; + Lisp_Object result = Qnil; + ScrapFlavorType flavor_type = get_flavor_type_from_symbol (type); +#if TARGET_API_MAC_CARBON + Size size; + + if (flavor_type) + { + err = GetScrapFlavorSize (scrap, flavor_type, &size); + if (err == noErr) + { + do + { + result = make_uninit_string (size); + err = GetScrapFlavorData (scrap, flavor_type, + &size, SDATA (result)); + if (err != noErr) + result = Qnil; + else if (size < SBYTES (result)) + result = make_unibyte_string (SDATA (result), size); + } + while (STRINGP (result) && size > SBYTES (result)); + } + } +#else + Handle handle; + SInt32 size, offset; + + if (flavor_type) + size = GetScrap (NULL, flavor_type, &offset); + if (size >= 0) + { + handle = NewHandle (size); + HLock (handle); + size = GetScrap (handle, flavor_type, &offset); + if (size >= 0) + result = make_unibyte_string (*handle, size); + DisposeHandle (handle); + } +#endif + + return result; +} + +/* Get timestamp from the scrap SCRAP and set to *TIMPSTAMP. */ + +static OSErr +get_scrap_private_timestamp (scrap, timestamp) + ScrapRef scrap; + unsigned long *timestamp; +{ + OSErr err = noErr; +#if TARGET_API_MAC_CARBON + ScrapFlavorFlags flags; + + err = GetScrapFlavorFlags (scrap, SCRAP_FLAVOR_TYPE_EMACS_TIMESTAMP, &flags); + if (err == noErr) + if (!(flags & kScrapFlavorMaskSenderOnly)) + err = noTypeErr; + else + { + Size size = sizeof (*timestamp); + + err = GetScrapFlavorData (scrap, SCRAP_FLAVOR_TYPE_EMACS_TIMESTAMP, + &size, timestamp); + if (err == noErr && size != sizeof (*timestamp)) + err = noTypeErr; + } +#else /* !TARGET_API_MAC_CARBON */ + Handle handle; + SInt32 size, offset; + + size = GetScrap (NULL, SCRAP_FLAVOR_TYPE_EMACS_TIMESTAMP, &offset); + if (size == sizeof (*timestamp)) + { + handle = NewHandle (size); + HLock (handle); + size = GetScrap (handle, SCRAP_FLAVOR_TYPE_EMACS_TIMESTAMP, &offset); + if (size == sizeof (*timestamp)) + *timestamp = *((unsigned long *) *handle); + DisposeHandle (handle); + } + if (size != sizeof (*timestamp)) + err = noTypeErr; +#endif /* !TARGET_API_MAC_CARBON */ + + return err; +} + +/* Get the list of target types in SCRAP. The return value is a list + of target type symbols possibly followed by scrap flavor type + strings. */ + +static Lisp_Object +get_scrap_target_type_list (scrap) + ScrapRef scrap; +{ + Lisp_Object result = Qnil, rest, target_type; +#if TARGET_API_MAC_CARBON + OSErr err; + UInt32 count, i, type; + ScrapFlavorInfo *flavor_info = NULL; + Lisp_Object strings = Qnil; + + err = GetScrapFlavorCount (scrap, &count); + if (err == noErr) + flavor_info = xmalloc (sizeof (ScrapFlavorInfo) * count); + if (err == noErr && flavor_info) + { + err = GetScrapFlavorInfoList (scrap, &count, flavor_info); + if (err != noErr) + { + xfree (flavor_info); + flavor_info = NULL; + } + } +#endif + for (rest = Vselection_converter_alist; CONSP (rest); rest = XCDR (rest)) + { + ScrapFlavorType flavor_type = 0; + + if (CONSP (XCAR (rest)) && SYMBOLP (target_type = XCAR (XCAR (rest))) + && (flavor_type = scrap_has_target_type (scrap, target_type))) + { + result = Fcons (target_type, result); +#if TARGET_API_MAC_CARBON + for (i = 0; i < count; i++) + if (flavor_info[i].flavorType == flavor_type) + { + flavor_info[i].flavorType = 0; + break; + } +#endif + } + } +#if TARGET_API_MAC_CARBON + if (flavor_info) + { + for (i = 0; i < count; i++) + if (flavor_info[i].flavorType) + { + type = EndianU32_NtoB (flavor_info[i].flavorType); + strings = Fcons (make_unibyte_string ((char *) &type, 4), strings); + } + result = nconc2 (result, strings); + xfree (flavor_info); + } +#endif + + return result; +} + +/* Do protocol to assert ourself as a selection owner. + Update the Vselection_alist so that we can reply to later requests for + our selection. */ + +static void +x_own_selection (selection_name, selection_value) + Lisp_Object selection_name, selection_value; +{ + OSErr err; + ScrapRef scrap; + struct gcpro gcpro1, gcpro2; + Lisp_Object rest, handler_fn, value, type; + int count; + + CHECK_SYMBOL (selection_name); + + GCPRO2 (selection_name, selection_value); + + BLOCK_INPUT; + + err = get_scrap_from_symbol (selection_name, 1, &scrap); + if (err == noErr && scrap) + { + /* Don't allow a quit within the converter. + When the user types C-g, he would be surprised + if by luck it came during a converter. */ + count = SPECPDL_INDEX (); + specbind (Qinhibit_quit, Qt); + + for (rest = Vselection_converter_alist; CONSP (rest); rest = XCDR (rest)) + { + if (!(CONSP (XCAR (rest)) + && SYMBOLP (type = XCAR (XCAR (rest))) + && valid_scrap_target_type_p (type) + && SYMBOLP (handler_fn = XCDR (XCAR (rest))))) + continue; + + if (!NILP (handler_fn)) + value = call3 (handler_fn, selection_name, + type, selection_value); + + if (CONSP (value) + && EQ (XCAR (value), type) + && STRINGP (XCDR (value))) + err = put_scrap_string (scrap, type, XCDR (value)); + } + + unbind_to (count, Qnil); + + if (err == noErr) + err = put_scrap_private_timestamp (scrap, last_event_timestamp); + } + + UNBLOCK_INPUT; + + UNGCPRO; + + if (scrap && err != noErr) + error ("Can't set selection"); + + /* Now update the local cache */ + { + Lisp_Object selection_time; + Lisp_Object selection_data; + Lisp_Object prev_value; + + selection_time = long_to_cons (last_event_timestamp); + selection_data = Fcons (selection_name, + Fcons (selection_value, + Fcons (selection_time, + Fcons (selected_frame, Qnil)))); + prev_value = assq_no_quit (selection_name, Vselection_alist); + + Vselection_alist = Fcons (selection_data, Vselection_alist); + + /* If we already owned the selection, remove the old selection data. + Perhaps we should destructively modify it instead. + Don't use Fdelq as that may QUIT. */ + if (!NILP (prev_value)) + { + Lisp_Object rest; /* we know it's not the CAR, so it's easy. */ + for (rest = Vselection_alist; !NILP (rest); rest = Fcdr (rest)) + if (EQ (prev_value, Fcar (XCDR (rest)))) + { + XSETCDR (rest, Fcdr (XCDR (rest))); + break; + } + } + } +} + +/* Given a selection-name and desired type, look up our local copy of + the selection value and convert it to the type. + The value is nil or a string. + This function is used both for remote requests (LOCAL_REQUEST is zero) + and for local x-get-selection-internal (LOCAL_REQUEST is nonzero). + + This calls random Lisp code, and may signal or gc. */ + +static Lisp_Object +x_get_local_selection (selection_symbol, target_type, local_request) + Lisp_Object selection_symbol, target_type; + int local_request; +{ + Lisp_Object local_value; + Lisp_Object handler_fn, value, type, check; + int count; + + if (NILP (Fx_selection_owner_p (selection_symbol))) + return Qnil; + + local_value = assq_no_quit (selection_symbol, Vselection_alist); + + /* TIMESTAMP is a special case 'cause that's easiest. */ + if (EQ (target_type, QTIMESTAMP)) + { + handler_fn = Qnil; + value = XCAR (XCDR (XCDR (local_value))); + } +#if 0 + else if (EQ (target_type, QDELETE)) + { + handler_fn = Qnil; + Fx_disown_selection_internal + (selection_symbol, + XCAR (XCDR (XCDR (local_value)))); + value = QNULL; + } +#endif + else + { + /* Don't allow a quit within the converter. + When the user types C-g, he would be surprised + if by luck it came during a converter. */ + count = SPECPDL_INDEX (); + specbind (Qinhibit_quit, Qt); + + CHECK_SYMBOL (target_type); + handler_fn = Fcdr (Fassq (target_type, Vselection_converter_alist)); + /* gcpro is not needed here since nothing but HANDLER_FN + is live, and that ought to be a symbol. */ + + if (!NILP (handler_fn)) + value = call3 (handler_fn, + selection_symbol, (local_request ? Qnil : target_type), + XCAR (XCDR (local_value))); + else + value = Qnil; + unbind_to (count, Qnil); + } + + /* Make sure this value is of a type that we could transmit + to another X client. */ + + check = value; + if (CONSP (value) + && SYMBOLP (XCAR (value))) + type = XCAR (value), + check = XCDR (value); + + if (STRINGP (check) + || VECTORP (check) + || SYMBOLP (check) + || INTEGERP (check) + || NILP (value)) + return value; + /* Check for a value that cons_to_long could handle. */ + else if (CONSP (check) + && INTEGERP (XCAR (check)) + && (INTEGERP (XCDR (check)) + || + (CONSP (XCDR (check)) + && INTEGERP (XCAR (XCDR (check))) + && NILP (XCDR (XCDR (check)))))) + return value; + else + return + Fsignal (Qerror, + Fcons (build_string ("invalid data returned by selection-conversion function"), + Fcons (handler_fn, Fcons (value, Qnil)))); +} + + +/* Clear all selections that were made from frame F. + We do this when about to delete a frame. */ + +void +x_clear_frame_selections (f) + FRAME_PTR f; +{ + Lisp_Object frame; + Lisp_Object rest; + + XSETFRAME (frame, f); + + /* Otherwise, we're really honest and truly being told to drop it. + Don't use Fdelq as that may QUIT;. */ + + /* Delete elements from the beginning of Vselection_alist. */ + while (!NILP (Vselection_alist) + && EQ (frame, Fcar (Fcdr (Fcdr (Fcdr (Fcar (Vselection_alist))))))) + { + /* Let random Lisp code notice that the selection has been stolen. */ + Lisp_Object hooks, selection_symbol; + + hooks = Vx_lost_selection_functions; + selection_symbol = Fcar (Fcar (Vselection_alist)); + + if (!EQ (hooks, Qunbound)) + { + for (; CONSP (hooks); hooks = Fcdr (hooks)) + call1 (Fcar (hooks), selection_symbol); +#if 0 /* This can crash when deleting a frame + from x_connection_closed. Anyway, it seems unnecessary; + something else should cause a redisplay. */ + redisplay_preserve_echo_area (21); +#endif + } + + Vselection_alist = Fcdr (Vselection_alist); + } + + /* Delete elements after the beginning of Vselection_alist. */ + for (rest = Vselection_alist; !NILP (rest); rest = Fcdr (rest)) + if (EQ (frame, Fcar (Fcdr (Fcdr (Fcdr (Fcar (XCDR (rest)))))))) + { + /* Let random Lisp code notice that the selection has been stolen. */ + Lisp_Object hooks, selection_symbol; + + hooks = Vx_lost_selection_functions; + selection_symbol = Fcar (Fcar (XCDR (rest))); + + if (!EQ (hooks, Qunbound)) + { + for (; CONSP (hooks); hooks = Fcdr (hooks)) + call1 (Fcar (hooks), selection_symbol); +#if 0 /* See above */ + redisplay_preserve_echo_area (22); +#endif + } + XSETCDR (rest, Fcdr (XCDR (rest))); + break; + } +} + +/* Do protocol to read selection-data from the server. + Converts this to Lisp data and returns it. */ + +static Lisp_Object +x_get_foreign_selection (selection_symbol, target_type, time_stamp) + Lisp_Object selection_symbol, target_type, time_stamp; +{ + OSErr err; + ScrapRef scrap; + Lisp_Object result = Qnil; + + BLOCK_INPUT; + + err = get_scrap_from_symbol (selection_symbol, 0, &scrap); + if (err == noErr && scrap) + if (EQ (target_type, QTARGETS)) + { + result = get_scrap_target_type_list (scrap); + result = Fvconcat (1, &result); + } + else + { + result = get_scrap_string (scrap, target_type); + if (STRINGP (result)) + Fput_text_property (make_number (0), make_number (SBYTES (result)), + Qforeign_selection, target_type, result); + } + + UNBLOCK_INPUT; + + return result; +} + + +DEFUN ("x-own-selection-internal", Fx_own_selection_internal, + Sx_own_selection_internal, 2, 2, 0, + doc: /* Assert a selection of the given TYPE with the given VALUE. +TYPE is a symbol, typically `PRIMARY', `SECONDARY', or `CLIPBOARD'. +VALUE is typically a string, or a cons of two markers, but may be +anything that the functions on `selection-converter-alist' know about. */) + (selection_name, selection_value) + Lisp_Object selection_name, selection_value; +{ + check_mac (); + CHECK_SYMBOL (selection_name); + if (NILP (selection_value)) error ("selection-value may not be nil"); + x_own_selection (selection_name, selection_value); + return selection_value; +} + + +/* Request the selection value from the owner. If we are the owner, + simply return our selection value. If we are not the owner, this + will block until all of the data has arrived. */ + +DEFUN ("x-get-selection-internal", Fx_get_selection_internal, + Sx_get_selection_internal, 2, 3, 0, + doc: /* Return text selected from some Mac window. +SELECTION is a symbol, typically `PRIMARY', `SECONDARY', or `CLIPBOARD'. +TYPE is the type of data desired, typically `STRING'. +TIME_STAMP is ignored on Mac. */) + (selection_symbol, target_type, time_stamp) + Lisp_Object selection_symbol, target_type, time_stamp; +{ + Lisp_Object val = Qnil; + struct gcpro gcpro1, gcpro2; + GCPRO2 (target_type, val); /* we store newly consed data into these */ + check_mac (); + CHECK_SYMBOL (selection_symbol); + CHECK_SYMBOL (target_type); + + val = x_get_local_selection (selection_symbol, target_type, 1); + + if (NILP (val)) + { + val = x_get_foreign_selection (selection_symbol, target_type, time_stamp); + goto DONE; + } + + if (CONSP (val) + && SYMBOLP (XCAR (val))) + { + val = XCDR (val); + if (CONSP (val) && NILP (XCDR (val))) + val = XCAR (val); + } + DONE: + UNGCPRO; + return val; +} + +DEFUN ("x-disown-selection-internal", Fx_disown_selection_internal, + Sx_disown_selection_internal, 1, 2, 0, + doc: /* If we own the selection SELECTION, disown it. +Disowning it means there is no such selection. */) + (selection, time) + Lisp_Object selection; + Lisp_Object time; +{ + OSErr err; + ScrapRef scrap; + Lisp_Object local_selection_data; + + check_mac (); + CHECK_SYMBOL (selection); + + if (NILP (Fx_selection_owner_p (selection))) + return Qnil; /* Don't disown the selection when we're not the owner. */ + + local_selection_data = assq_no_quit (selection, Vselection_alist); + + /* Don't use Fdelq as that may QUIT;. */ + + if (EQ (local_selection_data, Fcar (Vselection_alist))) + Vselection_alist = Fcdr (Vselection_alist); + else + { + Lisp_Object rest; + for (rest = Vselection_alist; !NILP (rest); rest = Fcdr (rest)) + if (EQ (local_selection_data, Fcar (XCDR (rest)))) + { + XSETCDR (rest, Fcdr (XCDR (rest))); + break; + } + } + + /* Let random lisp code notice that the selection has been stolen. */ + + { + Lisp_Object rest; + rest = Vx_lost_selection_functions; + if (!EQ (rest, Qunbound)) + { + for (; CONSP (rest); rest = Fcdr (rest)) + call1 (Fcar (rest), selection); + prepare_menu_bars (); + redisplay_preserve_echo_area (20); + } + } + + BLOCK_INPUT; + + err = get_scrap_from_symbol (selection, 0, &scrap); + if (err == noErr && scrap) + clear_scrap (&scrap); + + UNBLOCK_INPUT; + + return Qt; +} + + +DEFUN ("x-selection-owner-p", Fx_selection_owner_p, Sx_selection_owner_p, + 0, 1, 0, + doc: /* Whether the current Emacs process owns the given Selection. +The arg should be the name of the selection in question, typically one of +the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'. +For convenience, the symbol nil is the same as `PRIMARY', +and t is the same as `SECONDARY'. */) + (selection) + Lisp_Object selection; +{ + OSErr err; + ScrapRef scrap; + Lisp_Object result = Qnil, local_selection_data; + + check_mac (); + CHECK_SYMBOL (selection); + if (EQ (selection, Qnil)) selection = QPRIMARY; + if (EQ (selection, Qt)) selection = QSECONDARY; + + local_selection_data = assq_no_quit (selection, Vselection_alist); + + if (NILP (local_selection_data)) + return Qnil; + + BLOCK_INPUT; + + err = get_scrap_from_symbol (selection, 0, &scrap); + if (err == noErr && scrap) + { + unsigned long timestamp; + + err = get_scrap_private_timestamp (scrap, ×tamp); + if (err == noErr + && (timestamp + == cons_to_long (XCAR (XCDR (XCDR (local_selection_data)))))) + result = Qt; + } + else + result = Qt; + + UNBLOCK_INPUT; + + return result; +} + +DEFUN ("x-selection-exists-p", Fx_selection_exists_p, Sx_selection_exists_p, + 0, 1, 0, + doc: /* Whether there is an owner for the given Selection. +The arg should be the name of the selection in question, typically one of +the symbols `PRIMARY', `SECONDARY', or `CLIPBOARD'. +For convenience, the symbol nil is the same as `PRIMARY', +and t is the same as `SECONDARY'. */) + (selection) + Lisp_Object selection; +{ + OSErr err; + ScrapRef scrap; + Lisp_Object result = Qnil, rest; + + /* It should be safe to call this before we have an Mac frame. */ + if (! FRAME_MAC_P (SELECTED_FRAME ())) + return Qnil; + + CHECK_SYMBOL (selection); + if (!NILP (Fx_selection_owner_p (selection))) + return Qt; + if (EQ (selection, Qnil)) selection = QPRIMARY; + if (EQ (selection, Qt)) selection = QSECONDARY; + + BLOCK_INPUT; + + err = get_scrap_from_symbol (selection, 0, &scrap); + if (err == noErr && scrap) + for (rest = Vselection_converter_alist; CONSP (rest); rest = XCDR (rest)) + { + if (CONSP (XCAR (rest)) && SYMBOLP (XCAR (XCAR (rest))) + && scrap_has_target_type (scrap, XCAR (XCAR (rest)))) + { + result = Qt; + break; + } + } + + UNBLOCK_INPUT; + + return result; +} + + +#ifdef MAC_OSX +void +init_service_handler () +{ + EventTypeSpec specs[] = {{kEventClassService, kEventServiceGetTypes}, + {kEventClassService, kEventServiceCopy}, + {kEventClassService, kEventServicePaste}, + {kEventClassService, kEventServicePerform}}; + InstallApplicationEventHandler (NewEventHandlerUPP (mac_handle_service_event), + GetEventTypeCount (specs), specs, NULL, NULL); +} + +extern void mac_store_services_event P_ ((EventRef)); + +static OSStatus +mac_handle_service_event (call_ref, event, data) + EventHandlerCallRef call_ref; + EventRef event; + void *data; +{ + OSStatus err = noErr; + ScrapRef cur_scrap; + + /* Check if Vmac_services_selection is a valid selection that has a + corresponding scrap. */ + if (!SYMBOLP (Vmac_services_selection)) + err = eventNotHandledErr; + else + err = get_scrap_from_symbol (Vmac_services_selection, 0, &cur_scrap); + if (!(err == noErr && cur_scrap)) + return eventNotHandledErr; + + switch (GetEventKind (event)) + { + case kEventServiceGetTypes: + { + CFMutableArrayRef copy_types, paste_types; + CFStringRef type; + Lisp_Object rest; + ScrapFlavorType flavor_type; + + /* Set paste types. */ + err = GetEventParameter (event, kEventParamServicePasteTypes, + typeCFMutableArrayRef, NULL, + sizeof (CFMutableArrayRef), NULL, + &paste_types); + if (err == noErr) + for (rest = Vselection_converter_alist; CONSP (rest); + rest = XCDR (rest)) + if (CONSP (XCAR (rest)) && SYMBOLP (XCAR (XCAR (rest))) + && (flavor_type = + get_flavor_type_from_symbol (XCAR (XCAR (rest))))) + { + type = CreateTypeStringWithOSType (flavor_type); + if (type) + { + CFArrayAppendValue (paste_types, type); + CFRelease (type); + } + } + + /* Set copy types. */ + err = GetEventParameter (event, kEventParamServiceCopyTypes, + typeCFMutableArrayRef, NULL, + sizeof (CFMutableArrayRef), NULL, + ©_types); + if (err == noErr + && !NILP (Fx_selection_owner_p (Vmac_services_selection))) + for (rest = get_scrap_target_type_list (cur_scrap); + CONSP (rest) && SYMBOLP (XCAR (rest)); rest = XCDR (rest)) + { + flavor_type = get_flavor_type_from_symbol (XCAR (rest)); + if (flavor_type) + { + type = CreateTypeStringWithOSType (flavor_type); + if (type) + { + CFArrayAppendValue (copy_types, type); + CFRelease (type); + } + } + } + } + break; + + case kEventServiceCopy: + { + ScrapRef specific_scrap; + Lisp_Object rest, data; + + err = GetEventParameter (event, kEventParamScrapRef, + typeScrapRef, NULL, + sizeof (ScrapRef), NULL, &specific_scrap); + if (err == noErr + && !NILP (Fx_selection_owner_p (Vmac_services_selection))) + for (rest = get_scrap_target_type_list (cur_scrap); + CONSP (rest) && SYMBOLP (XCAR (rest)); rest = XCDR (rest)) + { + data = get_scrap_string (cur_scrap, XCAR (rest)); + if (STRINGP (data)) + err = put_scrap_string (specific_scrap, XCAR (rest), data); + } + else + err = eventNotHandledErr; + } + break; + + case kEventServicePaste: + case kEventServicePerform: + { + ScrapRef specific_scrap; + Lisp_Object rest, data; + int data_exists_p = 0; + + err = GetEventParameter (event, kEventParamScrapRef, typeScrapRef, + NULL, sizeof (ScrapRef), NULL, + &specific_scrap); + if (err == noErr) + err = clear_scrap (&cur_scrap); + if (err == noErr) + for (rest = Vselection_converter_alist; CONSP (rest); + rest = XCDR (rest)) + { + if (! (CONSP (XCAR (rest)) && SYMBOLP (XCAR (XCAR (rest))))) + continue; + data = get_scrap_string (specific_scrap, XCAR (XCAR (rest))); + if (STRINGP (data)) + { + err = put_scrap_string (cur_scrap, XCAR (XCAR (rest)), + data); + if (err != noErr) + break; + data_exists_p = 1; + } + } + if (err == noErr) + if (data_exists_p) + mac_store_application_menu_event (event); + else + err = eventNotHandledErr; + } + break; + } + + return err; +} +#endif + + +void +syms_of_macselect () +{ + defsubr (&Sx_get_selection_internal); + defsubr (&Sx_own_selection_internal); + defsubr (&Sx_disown_selection_internal); + defsubr (&Sx_selection_owner_p); + defsubr (&Sx_selection_exists_p); + + Vselection_alist = Qnil; + staticpro (&Vselection_alist); + + DEFVAR_LISP ("selection-converter-alist", &Vselection_converter_alist, + doc: /* An alist associating selection-types with functions. +These functions are called to convert the selection, with three args: +the name of the selection (typically `PRIMARY', `SECONDARY', or `CLIPBOARD'); +a desired type to which the selection should be converted; +and the local selection value (whatever was given to `x-own-selection'). + +The function should return the value to send to the Scrap Manager +\(a string). A return value of nil +means that the conversion could not be done. +A return value which is the symbol `NULL' +means that a side-effect was executed, +and there is no meaningful selection value. */); + Vselection_converter_alist = Qnil; + + DEFVAR_LISP ("x-lost-selection-functions", &Vx_lost_selection_functions, + doc: /* A list of functions to be called when Emacs loses a selection. +\(This happens when a Lisp program explicitly clears the selection.) +The functions are called with one argument, the selection type +\(a symbol, typically `PRIMARY', `SECONDARY', or `CLIPBOARD'). */); + Vx_lost_selection_functions = Qnil; + + DEFVAR_LISP ("selection-coding-system", &Vselection_coding_system, + doc: /* Coding system for communicating with other programs. +When sending or receiving text via cut_buffer, selection, and clipboard, +the text is encoded or decoded by this coding system. +The default value is determined by the system script code. */); + Vselection_coding_system = Qnil; + + DEFVAR_LISP ("next-selection-coding-system", &Vnext_selection_coding_system, + doc: /* Coding system for the next communication with other programs. +Usually, `selection-coding-system' is used for communicating with +other programs. But, if this variable is set, it is used for the +next communication only. After the communication, this variable is +set to nil. */); + Vnext_selection_coding_system = Qnil; + + DEFVAR_LISP ("mac-services-selection", &Vmac_services_selection, + doc: /* Selection name for communication via Services menu. */); + Vmac_services_selection = intern ("CLIPBOARD"); + + QPRIMARY = intern ("PRIMARY"); staticpro (&QPRIMARY); + QSECONDARY = intern ("SECONDARY"); staticpro (&QSECONDARY); + QTIMESTAMP = intern ("TIMESTAMP"); staticpro (&QTIMESTAMP); + QTARGETS = intern ("TARGETS"); staticpro (&QTARGETS); + + Qforeign_selection = intern ("foreign-selection"); + staticpro (&Qforeign_selection); + + Qmac_scrap_name = intern ("mac-scrap-name"); + staticpro (&Qmac_scrap_name); + + Qmac_ostype = intern ("mac-ostype"); + staticpro (&Qmac_ostype); +}