Mercurial > emacs
changeset 60632:fa8be36e244d
Include macterm.h instead of directly including Carbon.h.
[TARGET_API_MAC_CARBON] (Qstring, Qnumber, Qboolean, Qdate, Qdata)
(Qarray, Qdictionary): New variables.
(syms_of_mac) [TARGET_API_MAC_CARBON]: Initialize them.
[TARGET_API_MAC_CARBON] (Qutf_8): Add extern.
[TARGET_API_MAC_CARBON] (DECODE_UTF_8): New macro.
[TARGET_API_MAC_CARBON] (struct cfdict_context): New struct used in
callback for CFDictionaryApplyFunction.
[TARGET_API_MAC_CARBON] (cfdata_to_lisp, cfstring_to_lisp)
(cfnumber_to_lisp, cfdate_to_lisp, cfboolean_to_lisp)
(cfobject_desc_to_lisp, cfdictionary_add_to_list)
(cfdictionary_puthash, cfproperty_list_to_lisp): New functions.
[TARGET_API_MAC_CARBON] (Fmac_get_preference): New function.
(syms_of_mac) [TARGET_API_MAC_CARBON]: Defsubr it.
(P, LOOSE_BINDING, SINGLE_COMPONENT, HASHKEY_TERMINAL): New macro.
(skip_while_space, parse_comment, parse_include_file)
(parse_binding, parse_component, parse_resource_name, parse_value)
(parse_resource_line, xrm_create_database, xrm_q_put_resource)
(xrm_merge_string_database, xrm_q_get_resource, xrm_get_resource)
(xrm_cfproperty_list_to_value, xrm_get_preference_database): New
functions.
author | YAMAMOTO Mitsuharu <mituharu@math.s.chiba-u.ac.jp> |
---|---|
date | Wed, 16 Mar 2005 08:05:56 +0000 |
parents | b218b0216c5e |
children | 9497994ad6fc |
files | src/mac.c |
diffstat | 1 files changed, 974 insertions(+), 32 deletions(-) [+] |
line wrap: on
line diff
--- a/src/mac.c Wed Mar 16 08:02:13 2005 +0000 +++ b/src/mac.c Wed Mar 16 08:05:56 2005 +0000 @@ -26,31 +26,15 @@ #include <errno.h> #include <time.h> -#ifdef HAVE_CARBON -#ifdef MAC_OSX -#undef mktime -#undef DEBUG -#undef free -#undef malloc -#undef realloc -#undef init_process -#include <Carbon/Carbon.h> -#undef mktime -#define mktime emacs_mktime -#undef free -#define free unexec_free -#undef malloc -#define malloc unexec_malloc -#undef realloc -#define realloc unexec_realloc -#undef init_process -#define init_process emacs_init_process -#else /* not MAC_OSX */ -#undef SIGHUP -#define OLDP2C 1 -#include <Carbon.h> -#endif /* not MAC_OSX */ -#else /* not HAVE_CARBON */ +#include "lisp.h" +#include "process.h" +#include "sysselect.h" +#include "systime.h" +#include "blockinput.h" + +#include "macterm.h" + +#ifndef HAVE_CARBON #include <Files.h> #include <MacTypes.h> #include <TextUtils.h> @@ -81,12 +65,6 @@ #include <unistd.h> #endif -#include "lisp.h" -#include "process.h" -#include "sysselect.h" -#include "systime.h" -#include "blockinput.h" - Lisp_Object QCLIPBOARD; /* An instance of the AppleScript component. */ @@ -272,7 +250,25 @@ return 1; } + +/*********************************************************************** + Conversion between Lisp and Core Foundation objects + ***********************************************************************/ + #if TARGET_API_MAC_CARBON +static Lisp_Object Qstring, Qnumber, Qboolean, Qdate, Qdata; +static Lisp_Object Qarray, Qdictionary; +extern Lisp_Object Qutf_8; +#define DECODE_UTF_8(str) code_convert_string_norecord (str, Qutf_8, 0) + +struct cfdict_context +{ + Lisp_Object *result; + int with_tag, hash_bound; +}; + +/* C string to CFString. */ + CFStringRef cfstring_create_with_utf8_cstring (c_str) const char *c_str; @@ -286,8 +282,807 @@ return str; } + + +/* From CFData to a lisp string. Always returns a unibyte string. */ + +Lisp_Object +cfdata_to_lisp (data) + CFDataRef data; +{ + CFIndex len = CFDataGetLength (data); + Lisp_Object result = make_uninit_string (len); + + CFDataGetBytes (data, CFRangeMake (0, len), SDATA (result)); + + return result; +} + + +/* From CFString to a lisp string. Never returns a unibyte string + (even if it only contains ASCII characters). + This may cause GC during code conversion. */ + +Lisp_Object +cfstring_to_lisp (string) + CFStringRef string; +{ + Lisp_Object result = Qnil; + const char *s = CFStringGetCStringPtr (string, kCFStringEncodingUTF8); + + if (s) + result = make_unibyte_string (s, strlen (s)); + else + { + CFDataRef data = + CFStringCreateExternalRepresentation (NULL, string, + kCFStringEncodingUTF8, '?'); + + if (data) + { + result = cfdata_to_lisp (data); + CFRelease (data); + } + } + + if (!NILP (result)) + { + result = DECODE_UTF_8 (result); + /* This may be superfluous. Just to make sure that the result + is a multibyte string. */ + result = string_to_multibyte (result); + } + + return result; +} + + +/* CFNumber to a lisp integer or a lisp float. */ + +Lisp_Object +cfnumber_to_lisp (number) + CFNumberRef number; +{ + Lisp_Object result = Qnil; +#if BITS_PER_EMACS_INT > 32 + SInt64 int_val; + CFNumberType emacs_int_type = kCFNumberSInt64Type; +#else + SInt32 int_val; + CFNumberType emacs_int_type = kCFNumberSInt32Type; #endif - + double float_val; + + if (CFNumberGetValue (number, emacs_int_type, &int_val) + && !FIXNUM_OVERFLOW_P (int_val)) + result = make_number (int_val); + else + if (CFNumberGetValue (number, kCFNumberDoubleType, &float_val)) + result = make_float (float_val); + return result; +} + + +/* CFDate to a list of three integers as in a return value of + `current-time'xo. */ + +Lisp_Object +cfdate_to_lisp (date) + CFDateRef date; +{ + static CFGregorianDate epoch_gdate = {1970, 1, 1, 0, 0, 0.0}; + static CFAbsoluteTime epoch = 0.0, sec; + int high, low; + + if (epoch == 0.0) + epoch = CFGregorianDateGetAbsoluteTime (epoch_gdate, NULL); + + sec = CFDateGetAbsoluteTime (date) - epoch; + high = sec / 65536.0; + low = sec - high * 65536.0; + + return list3 (make_number (high), make_number (low), make_number (0)); +} + + +/* CFBoolean to a lisp symbol, `t' or `nil'. */ + +Lisp_Object +cfboolean_to_lisp (boolean) + CFBooleanRef boolean; +{ + return CFBooleanGetValue (boolean) ? Qt : Qnil; +} + + +/* Any Core Foundation object to a (lengthy) lisp string. */ + +Lisp_Object +cfobject_desc_to_lisp (object) + CFTypeRef object; +{ + Lisp_Object result = Qnil; + CFStringRef desc = CFCopyDescription (object); + + if (desc) + { + result = cfstring_to_lisp (desc); + CFRelease (desc); + } + + return result; +} + + +/* Callback functions for cfproperty_list_to_lisp. */ + +static void +cfdictionary_add_to_list (key, value, context) + const void *key; + const void *value; + void *context; +{ + struct cfdict_context *cxt = (struct cfdict_context *)context; + + *cxt->result = + Fcons (Fcons (cfstring_to_lisp (key), + cfproperty_list_to_lisp (value, cxt->with_tag, + cxt->hash_bound)), + *cxt->result); +} + +static void +cfdictionary_puthash (key, value, context) + const void *key; + const void *value; + void *context; +{ + Lisp_Object lisp_key = cfstring_to_lisp (key); + struct cfdict_context *cxt = (struct cfdict_context *)context; + struct Lisp_Hash_Table *h = XHASH_TABLE (*(cxt->result)); + unsigned hash_code; + + hash_lookup (h, lisp_key, &hash_code); + hash_put (h, lisp_key, + cfproperty_list_to_lisp (value, cxt->with_tag, cxt->hash_bound), + hash_code); +} + + +/* Convert CFPropertyList PLIST to a lisp object. If WITH_TAG is + non-zero, a symbol that represents the type of the original Core + Foundation object is prepended. HASH_BOUND specifies which kinds + of the lisp objects, alists or hash tables, are used as the targets + of the conversion from CFDictionary. If HASH_BOUND is negative, + always generate alists. If HASH_BOUND >= 0, generate an alist if + the number of keys in the dictionary is smaller than HASH_BOUND, + and a hash table otherwise. */ + +Lisp_Object +cfproperty_list_to_lisp (plist, with_tag, hash_bound) + CFPropertyListRef plist; + int with_tag, hash_bound; +{ + CFTypeID type_id = CFGetTypeID (plist); + Lisp_Object tag = Qnil, result = Qnil; + struct gcpro gcpro1, gcpro2; + + GCPRO2 (tag, result); + + if (type_id == CFStringGetTypeID ()) + { + tag = Qstring; + result = cfstring_to_lisp (plist); + } + else if (type_id == CFNumberGetTypeID ()) + { + tag = Qnumber; + result = cfnumber_to_lisp (plist); + } + else if (type_id == CFBooleanGetTypeID ()) + { + tag = Qboolean; + result = cfboolean_to_lisp (plist); + } + else if (type_id == CFDateGetTypeID ()) + { + tag = Qdate; + result = cfdate_to_lisp (plist); + } + else if (type_id == CFDataGetTypeID ()) + { + tag = Qdata; + result = cfdata_to_lisp (plist); + } + else if (type_id == CFArrayGetTypeID ()) + { + CFIndex index, count = CFArrayGetCount (plist); + + tag = Qarray; + result = Fmake_vector (make_number (count), Qnil); + for (index = 0; index < count; index++) + XVECTOR (result)->contents[index] = + cfproperty_list_to_lisp (CFArrayGetValueAtIndex (plist, index), + with_tag, hash_bound); + } + else if (type_id == CFDictionaryGetTypeID ()) + { + struct cfdict_context context; + CFIndex count = CFDictionaryGetCount (plist); + + tag = Qdictionary; + context.result = &result; + context.with_tag = with_tag; + context.hash_bound = hash_bound; + if (hash_bound < 0 || count < hash_bound) + { + result = Qnil; + CFDictionaryApplyFunction (plist, cfdictionary_add_to_list, + &context); + } + else + { + result = make_hash_table (Qequal, + make_number (count), + make_float (DEFAULT_REHASH_SIZE), + make_float (DEFAULT_REHASH_THRESHOLD), + Qnil, Qnil, Qnil); + CFDictionaryApplyFunction (plist, cfdictionary_puthash, + &context); + } + } + else + abort (); + + UNGCPRO; + + if (with_tag) + result = Fcons (tag, result); + + return result; +} +#endif + + +/*********************************************************************** + Emulation of the X Resource Manager + ***********************************************************************/ + +/* Parser functions for resource lines. Each function takes an + address of a variable whose value points to the head of a string. + The value will be advanced so that it points to the next character + of the parsed part when the function returns. + + A resource name such as "Emacs*font" is parsed into a non-empty + list called `quarks'. Each element is either a Lisp string that + represents a concrete component, a Lisp symbol LOOSE_BINDING + (actually Qlambda) that represents any number (>=0) of intervening + components, or a Lisp symbol SINGLE_COMPONENT (actually Qquote) + that represents as any single component. */ + +#define P (*p) + +#define LOOSE_BINDING Qlambda /* '*' ("L"oose) */ +#define SINGLE_COMPONENT Qquote /* '?' ("Q"uestion) */ + +static void +skip_while_space (p) + char **p; +{ + /* WhiteSpace = {<space> | <horizontal tab>} */ + while (*P == ' ' || *P == '\t') + P++; +} + +static int +parse_comment (p) + char **p; +{ + /* Comment = "!" {<any character except null or newline>} */ + if (*P == '!') + { + P++; + while (*P) + if (*P++ == '\n') + break; + return 1; + } + else + return 0; +} + +/* Don't interpret filename. Just skip until the newline. */ +static int +parse_include_file (p) + char **p; +{ + /* IncludeFile = "#" WhiteSpace "include" WhiteSpace FileName WhiteSpace */ + if (*P == '#') + { + P++; + while (*P) + if (*P++ == '\n') + break; + return 1; + } + else + return 0; +} + +static char +parse_binding (p) + char **p; +{ + /* Binding = "." | "*" */ + if (*P == '.' || *P == '*') + { + char binding = *P++; + + while (*P == '.' || *P == '*') + if (*P++ == '*') + binding = '*'; + return binding; + } + else + return '\0'; +} + +static Lisp_Object +parse_component (p) + char **p; +{ + /* Component = "?" | ComponentName + ComponentName = NameChar {NameChar} + NameChar = "a"-"z" | "A"-"Z" | "0"-"9" | "_" | "-" */ + if (*P == '?') + { + P++; + return SINGLE_COMPONENT; + } + else if (isalnum (*P) || *P == '_' || *P == '-') + { + char *start = P++; + + while (isalnum (*P) || *P == '_' || *P == '-') + P++; + + return make_unibyte_string (start, P - start); + } + else + return Qnil; +} + +static Lisp_Object +parse_resource_name (p) + char **p; +{ + Lisp_Object result = Qnil, component; + char binding; + + /* ResourceName = [Binding] {Component Binding} ComponentName */ + if (parse_binding (p) == '*') + result = Fcons (LOOSE_BINDING, result); + + component = parse_component (p); + if (NILP (component)) + return Qnil; + + result = Fcons (component, result); + while (binding = parse_binding (p)) + { + if (binding == '*') + result = Fcons (LOOSE_BINDING, result); + component = parse_component (p); + if (NILP (component)) + return Qnil; + else + result = Fcons (component, result); + } + + /* The final component should not be '?'. */ + if (EQ (component, SINGLE_COMPONENT)) + return Qnil; + + return Fnreverse (result); +} + +static Lisp_Object +parse_value (p) + char **p; +{ + char *q, *buf; + Lisp_Object seq = Qnil, result; + int buf_len, total_len = 0, len, continue_p; + + q = strchr (P, '\n'); + buf_len = q ? q - P : strlen (P); + buf = xmalloc (buf_len); + + while (1) + { + q = buf; + continue_p = 0; + while (*P) + { + if (*P == '\n') + { + P++; + break; + } + else if (*P == '\\') + { + P++; + if (*P == '\0') + break; + else if (*P == '\n') + { + P++; + continue_p = 1; + break; + } + else if (*P == 'n') + { + *q++ = '\n'; + P++; + } + else if ('0' <= P[0] && P[0] <= '7' + && '0' <= P[1] && P[1] <= '7' + && '0' <= P[2] && P[2] <= '7') + { + *q++ = (P[0] - '0' << 6) + (P[1] - '0' << 3) + (P[2] - '0'); + P += 3; + } + else + *q++ = *P++; + } + else + *q++ = *P++; + } + len = q - buf; + seq = Fcons (make_unibyte_string (buf, len), seq); + total_len += len; + + if (continue_p) + { + q = strchr (P, '\n'); + len = q ? q - P : strlen (P); + if (len > buf_len) + { + xfree (buf); + buf_len = len; + buf = xmalloc (buf_len); + } + } + else + break; + } + xfree (buf); + + if (SBYTES (XCAR (seq)) == total_len) + return make_string (SDATA (XCAR (seq)), total_len); + else + { + buf = xmalloc (total_len); + q = buf + total_len; + for (; CONSP (seq); seq = XCDR (seq)) + { + len = SBYTES (XCAR (seq)); + q -= len; + memcpy (q, SDATA (XCAR (seq)), len); + } + result = make_string (buf, total_len); + xfree (buf); + return result; + } +} + +static Lisp_Object +parse_resource_line (p) + char **p; +{ + Lisp_Object quarks, value; + + /* ResourceLine = Comment | IncludeFile | ResourceSpec | <empty line> */ + if (parse_comment (p) || parse_include_file (p)) + return Qnil; + + /* ResourceSpec = WhiteSpace ResourceName WhiteSpace ":" WhiteSpace Value */ + skip_while_space (p); + quarks = parse_resource_name (p); + if (NILP (quarks)) + goto cleanup; + skip_while_space (p); + if (*P != ':') + goto cleanup; + P++; + skip_while_space (p); + value = parse_value (p); + return Fcons (quarks, value); + + cleanup: + /* Skip the remaining data as a dummy value. */ + parse_value (p); + return Qnil; +} + +#undef P + +/* Equivalents of X Resource Manager functions. + + An X Resource Database acts as a collection of resource names and + associated values. It is implemented as a trie on quarks. Namely, + each edge is labeled by either a string, LOOSE_BINDING, or + SINGLE_COMPONENT. Nodes of the trie are implemented as Lisp hash + tables, and a value associated with a resource name is recorded as + a value for HASHKEY_TERMINAL at the hash table whose path from the + root is the quarks of the resource name. */ + +#define HASHKEY_TERMINAL Qt /* "T"erminal */ + +static XrmDatabase +xrm_create_database () +{ + return make_hash_table (Qequal, make_number (DEFAULT_HASH_SIZE), + make_float (DEFAULT_REHASH_SIZE), + make_float (DEFAULT_REHASH_THRESHOLD), + Qnil, Qnil, Qnil); +} + +static void +xrm_q_put_resource (database, quarks, value) + XrmDatabase database; + Lisp_Object quarks, value; +{ + struct Lisp_Hash_Table *h; + unsigned hash_code; + int i; + + for (; CONSP (quarks); quarks = XCDR (quarks)) + { + h = XHASH_TABLE (database); + i = hash_lookup (h, XCAR (quarks), &hash_code); + if (i < 0) + { + database = xrm_create_database (); + hash_put (h, XCAR (quarks), database, hash_code); + } + else + database = HASH_VALUE (h, i); + } + + Fputhash (HASHKEY_TERMINAL, value, database); +} + +/* Merge multiple resource entries specified by DATA into a resource + database DATABASE. DATA points to the head of a null-terminated + string consisting of multiple resource lines. It's like a + combination of XrmGetStringDatabase and XrmMergeDatabases. */ + +void +xrm_merge_string_database (database, data) + XrmDatabase database; + char *data; +{ + Lisp_Object quarks_value; + + while (*data) + { + quarks_value = parse_resource_line (&data); + if (!NILP (quarks_value)) + xrm_q_put_resource (database, + XCAR (quarks_value), XCDR (quarks_value)); + } +} + +static Lisp_Object +xrm_q_get_resource (database, quark_name, quark_class) + XrmDatabase database; + Lisp_Object quark_name, quark_class; +{ + struct Lisp_Hash_Table *h = XHASH_TABLE (database); + Lisp_Object keys[3], value; + int i, k; + + if (!CONSP (quark_name)) + return Fgethash (HASHKEY_TERMINAL, database, Qnil); + + /* First, try tight bindings */ + keys[0] = XCAR (quark_name); + keys[1] = XCAR (quark_class); + keys[2] = SINGLE_COMPONENT; + + for (k = 0; k < sizeof (keys) / sizeof (*keys); k++) + { + i = hash_lookup (h, keys[k], NULL); + if (i >= 0) + { + value = xrm_q_get_resource (HASH_VALUE (h, i), + XCDR (quark_name), XCDR (quark_class)); + if (!NILP (value)) + return value; + } + } + + /* Then, try loose bindings */ + i = hash_lookup (h, LOOSE_BINDING, NULL); + if (i >= 0) + { + value = xrm_q_get_resource (HASH_VALUE (h, i), quark_name, quark_class); + if (!NILP (value)) + return value; + else + return xrm_q_get_resource (database, + XCDR (quark_name), XCDR (quark_class)); + } + else + return Qnil; +} + +/* Retrieve a resource value for the specified NAME and CLASS from the + resource database DATABASE. It corresponds to XrmGetResource. */ + +Lisp_Object +xrm_get_resource (database, name, class) + XrmDatabase database; + char *name, *class; +{ + Lisp_Object quark_name, quark_class, tmp; + int nn, nc; + + quark_name = parse_resource_name (&name); + if (*name != '\0') + return Qnil; + for (tmp = quark_name, nn = 0; CONSP (tmp); tmp = XCDR (tmp), nn++) + if (!STRINGP (XCAR (tmp))) + return Qnil; + + quark_class = parse_resource_name (&class); + if (*class != '\0') + return Qnil; + for (tmp = quark_class, nc = 0; CONSP (tmp); tmp = XCDR (tmp), nc++) + if (!STRINGP (XCAR (tmp))) + return Qnil; + + if (nn != nc) + return Qnil; + else + return xrm_q_get_resource (database, quark_name, quark_class); +} + +#if TARGET_API_MAC_CARBON +static Lisp_Object +xrm_cfproperty_list_to_value (plist) + CFPropertyListRef plist; +{ + CFTypeID type_id = CFGetTypeID (plist); + + if (type_id == CFStringGetTypeID ()) + return cfstring_to_lisp (plist); + else if (type_id == CFNumberGetTypeID ()) + { + CFStringRef string; + Lisp_Object result = Qnil; + + string = CFStringCreateWithFormat (NULL, NULL, CFSTR ("%@"), plist); + if (string) + { + result = cfstring_to_lisp (string); + CFRelease (string); + } + return result; + } + else if (type_id == CFBooleanGetTypeID ()) + { + static value_true = NULL, value_false = NULL; + + if (value_true == NULL) + { + value_true = build_string ("true"); + value_false = build_string ("false"); + } + return CFBooleanGetValue (plist) ? value_true : value_false; + } + else if (type_id == CFDataGetTypeID ()) + return cfdata_to_lisp (plist); + else + return Qnil; +} +#endif + +/* Create a new resource database from the preferences for the + application APPLICATION. APPLICATION is either a string that + specifies an application ID, or NULL that represents the current + application. */ + +XrmDatabase +xrm_get_preference_database (application) + char *application; +{ +#if TARGET_API_MAC_CARBON + CFStringRef app_id, *keys, user_doms[2], host_doms[2]; + CFMutableSetRef key_set = NULL; + CFArrayRef key_array; + CFIndex index, count; + char *res_name; + XrmDatabase database; + Lisp_Object quarks = Qnil, value = Qnil; + CFPropertyListRef plist; + int iu, ih; + struct gcpro gcpro1, gcpro2, gcpro3; + + user_doms[0] = kCFPreferencesCurrentUser; + user_doms[1] = kCFPreferencesAnyUser; + host_doms[0] = kCFPreferencesCurrentHost; + host_doms[1] = kCFPreferencesAnyHost; + + database = xrm_create_database (); + + GCPRO3 (database, quarks, value); + + BLOCK_INPUT; + + app_id = kCFPreferencesCurrentApplication; + if (application) + { + app_id = cfstring_create_with_utf8_cstring (application); + if (app_id == NULL) + goto out; + } + + key_set = CFSetCreateMutable (NULL, 0, &kCFCopyStringSetCallBacks); + if (key_set == NULL) + goto out; + for (iu = 0; iu < sizeof (user_doms) / sizeof (*user_doms) ; iu++) + for (ih = 0; ih < sizeof (host_doms) / sizeof (*host_doms); ih++) + { + key_array = CFPreferencesCopyKeyList (app_id, user_doms[iu], + host_doms[ih]); + if (key_array) + { + count = CFArrayGetCount (key_array); + for (index = 0; index < count; index++) + CFSetAddValue (key_set, + CFArrayGetValueAtIndex (key_array, index)); + CFRelease (key_array); + } + } + + count = CFSetGetCount (key_set); + keys = xmalloc (sizeof (CFStringRef) * count); + if (keys == NULL) + goto out; + CFSetGetValues (key_set, (const void **)keys); + for (index = 0; index < count; index++) + { + res_name = SDATA (cfstring_to_lisp (keys[index])); + quarks = parse_resource_name (&res_name); + if (!(NILP (quarks) || *res_name)) + { + plist = CFPreferencesCopyAppValue (keys[index], app_id); + value = xrm_cfproperty_list_to_value (plist); + CFRelease (plist); + if (!NILP (value)) + xrm_q_put_resource (database, quarks, value); + } + } + + xfree (keys); + out: + if (key_set) + CFRelease (key_set); + CFRelease (app_id); + + UNBLOCK_INPUT; + + UNGCPRO; + + return database; +#else + return xrm_create_database (); +#endif +} + + #ifndef MAC_OSX /* The following functions with "sys_" prefix are stubs to Unix @@ -2825,6 +3620,124 @@ return Qnil; } +#if TARGET_API_MAC_CARBON +static Lisp_Object Qxml; + +DEFUN ("mac-get-preference", Fmac_get_preference, Smac_get_preference, 1, 4, 0, + doc: /* Return the application preference value for KEY. +KEY is either a string specifying a preference key, or a list of key +strings. If it is a list, the (i+1)-th element is used as a key for +the CFDictionary value obtained by the i-th element. If lookup is +failed at some stage, nil is returned. + +Optional arg APPLICATION is an application ID string. If omitted or +nil, that stands for the current application. + +Optional arg FORMAT specifies the data format of the return value. If +omitted or nil, each Core Foundation object is converted into a +corresponding Lisp object as follows: + + Core Foundation Lisp Tag + ------------------------------------------------------------ + CFString Multibyte string string + CFNumber Integer or float number + CFBoolean Symbol (t or nil) boolean + CFDate List of three integers date + (cf. `current-time') + CFData Unibyte string data + CFArray Array array + CFDictionary Alist or hash table dictionary + (depending on HASH-BOUND) + +If it is t, a symbol that represents the type of the original Core +Foundation object is prepended. If it is `xml', the value is returned +as an XML representation. + +Optional arg HASH-BOUND specifies which kinds of the list objects, +alists or hash tables, are used as the targets of the conversion from +CFDictionary. If HASH-BOUND is a negative integer or nil, always +generate alists. If HASH-BOUND >= 0, generate an alist if the number +of keys in the dictionary is smaller than HASH-BOUND, and a hash table +otherwise. */) + (key, application, format, hash_bound) + Lisp_Object key, application, format, hash_bound; +{ + CFStringRef app_id, key_str; + CFPropertyListRef app_plist = NULL, plist; + Lisp_Object result = Qnil, tmp; + + if (STRINGP (key)) + key = Fcons (key, Qnil); + else + { + CHECK_CONS (key); + for (tmp = key; CONSP (tmp); tmp = XCDR (tmp)) + CHECK_STRING_CAR (tmp); + if (!NILP (tmp)) + wrong_type_argument (Qlistp, key); + } + if (!NILP (application)) + CHECK_STRING (application); + CHECK_SYMBOL (format); + if (!NILP (hash_bound)) + CHECK_NUMBER (hash_bound); + + BLOCK_INPUT; + + app_id = kCFPreferencesCurrentApplication; + if (!NILP (application)) + { + app_id = cfstring_create_with_utf8_cstring (SDATA (application)); + if (app_id == NULL) + goto out; + } + key_str = cfstring_create_with_utf8_cstring (SDATA (XCAR (key))); + if (key_str == NULL) + goto out; + app_plist = CFPreferencesCopyAppValue (key_str, app_id); + CFRelease (key_str); + if (app_plist == NULL) + goto out; + + plist = app_plist; + for (key = XCDR (key); CONSP (key); key = XCDR (key)) + { + if (CFGetTypeID (plist) != CFDictionaryGetTypeID ()) + break; + key_str = cfstring_create_with_utf8_cstring (SDATA (XCAR (key))); + if (key_str == NULL) + goto out; + plist = CFDictionaryGetValue (plist, key_str); + CFRelease (key_str); + if (plist == NULL) + goto out; + } + + if (NILP (key)) + if (EQ (format, Qxml)) + { + CFDataRef data = CFPropertyListCreateXMLData (NULL, plist); + if (data == NULL) + goto out; + result = cfdata_to_lisp (data); + CFRelease (data); + } + else + result = + cfproperty_list_to_lisp (plist, EQ (format, Qt), + NILP (hash_bound) ? -1 : XINT (hash_bound)); + + out: + if (app_plist) + CFRelease (app_plist); + CFRelease (app_id); + + UNBLOCK_INPUT; + + return result; +} +#endif /* TARGET_API_MAC_CARBON */ + 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. */) @@ -3243,9 +4156,38 @@ QCLIPBOARD = intern ("CLIPBOARD"); staticpro (&QCLIPBOARD); +#if TARGET_API_MAC_CARBON + Qstring = intern ("string"); + staticpro (&Qstring); + + Qnumber = intern ("number"); + staticpro (&Qnumber); + + Qboolean = intern ("boolean"); + staticpro (&Qboolean); + + Qdate = intern ("date"); + staticpro (&Qdate); + + Qdata = intern ("data"); + staticpro (&Qdata); + + Qarray = intern ("array"); + staticpro (&Qarray); + + Qdictionary = intern ("dictionary"); + staticpro (&Qdictionary); + + Qxml = intern ("xml"); + staticpro (&Qxml); +#endif + defsubr (&Smac_paste_function); defsubr (&Smac_cut_function); defsubr (&Sx_selection_exists_p); +#if TARGET_API_MAC_CARBON + defsubr (&Smac_get_preference); +#endif defsubr (&Smac_clear_font_name_table); defsubr (&Sdo_applescript);