blob: a0bbe5056bea7c4e9c1ecbd222592962b6c2aefd [file] [log] [blame]
Jack Jansen7cc57351998-08-18 14:54:11 +00001"""Tools for use in AppleEvent clients and servers:
2conversion between AE types and python types
3
4pack(x) converts a Python object to an AEDesc object
5unpack(desc) does the reverse
6coerce(x, wanted_sample) coerces a python object to another python object
7"""
8
9#
10# This code was originally written by Guido, and modified/extended by Jack
11# to include the various types that were missing. The reference used is
12# Apple Event Registry, chapter 9.
13#
14
15import struct
16import string
17import types
18from string import strip
19from types import *
20import AE
21from AppleEvents import *
22from AERegistry import *
23from AEObjects import *
24import MacOS
25import macfs
26import StringIO
27import baetypes
28from baetypes import mkenum, mktype
29
30import calldll
31
32OSL = calldll.getlibrary('ObjectSupportLib')
33
34# These ones seem to be missing from AppleEvents
35# (they're in AERegistry.h)
36
37#typeColorTable = 'clrt'
38#typeDrawingArea = 'cdrw'
39#typePixelMap = 'cpix'
40#typePixelMapMinus = 'tpmm'
41#typeRotation = 'trot'
42#typeTextStyles = 'tsty'
43#typeStyledText = 'STXT'
44#typeAEText = 'tTXT'
45#typeEnumeration = 'enum'
46
47#
48# Some AE types are immedeately coerced into something
49# we like better (and which is equivalent)
50#
51unpacker_coercions = {
52 typeComp : typeExtended,
53 typeColorTable : typeAEList,
54 typeDrawingArea : typeAERecord,
55 typeFixed : typeExtended,
56 typeFloat : typeExtended,
57 typePixelMap : typeAERecord,
58 typeRotation : typeAERecord,
59 typeStyledText : typeAERecord,
60 typeTextStyles : typeAERecord,
61};
62
63#
64# Some python types we need in the packer:
65#
66AEDescType = type(AE.AECreateDesc('TEXT', ''))
67_sample_fss = macfs.FSSpec(':')
68_sample_alias = _sample_fss.NewAliasMinimal()
69FSSType = type(_sample_fss)
70AliasType = type(_sample_alias)
71
72def pack(x, forcetype = None):
73 """Pack a python object into an AE descriptor"""
74# print 'aepack', x, type(x), forcetype
75# if type(x) == TupleType:
76# forcetype, x = x
77 if forcetype:
78 print x, forcetype
79 if type(x) is StringType:
80 return AE.AECreateDesc(forcetype, x)
81 else:
82 return pack(x).AECoerceDesc(forcetype)
83
84 if x == None:
85 return AE.AECreateDesc('null', '')
86
87 t = type(x)
88 if t == AEDescType:
89 return x
90 if t == FSSType:
91 return AE.AECreateDesc('fss ', x.data)
92 if t == AliasType:
93 return AE.AECreateDesc('alis', x.data)
94 if t == IntType:
95 return AE.AECreateDesc('long', struct.pack('l', x))
96 if t == FloatType:
97 #
98 # XXXX (note by Guido) Weird thing -- Think C's "double" is 10 bytes, but
99 # struct.pack('d') return 12 bytes (and struct.unpack requires
100 # them, too). The first 2 bytes seem to be repeated...
101 # Probably an alignment problem
102 # XXXX (note by Jack) haven't checked this under MW
103 #
104# return AE.AECreateDesc('exte', struct.pack('d', x)[2:])
105 return AE.AECreateDesc('exte', struct.pack('d', x))
106 if t == StringType:
107 return AE.AECreateDesc('TEXT', x)
108 if t == ListType:
109 list = AE.AECreateList('', 0)
110 for item in x:
111 list.AEPutDesc(0, pack(item))
112 return list
113 if t == DictionaryType:
114 record = AE.AECreateList('', 1)
115 for key, value in x.items():
116 record.AEPutParamDesc(key, pack(value))
117 return record
118 if t == InstanceType and hasattr(x, '__aepack__'):
119 return x.__aepack__()
120 return AE.AECreateDesc('TEXT', repr(x)) # Copout
121
122def unpack(desc):
123 """Unpack an AE descriptor to a python object"""
124 t = desc.type
125# print t
126
127 if unpacker_coercions.has_key(t):
128 desc = desc.AECoerceDesc(unpacker_coercions[t])
129 t = desc.type # This is a guess by Jack....
130
131 if t == typeAEList:
132 l = []
133 for i in range(desc.AECountItems()):
134 keyword, item = desc.AEGetNthDesc(i+1, '****')
135 l.append(unpack(item))
136 return l
137 if t == typeAERecord:
138 d = {}
139 for i in range(desc.AECountItems()):
140 keyword, item = desc.AEGetNthDesc(i+1, '****')
141 d[keyword] = unpack(item)
142 return d
143 if t == typeAEText:
144 record = desc.AECoerceDesc('reco')
145 return mkaetext(unpack(record))
146 if t == typeAlias:
147 return macfs.RawAlias(desc.data)
148 # typeAppleEvent returned as unknown
149 if t == typeBoolean:
150 return struct.unpack('b', desc.data)[0]
151 if t == typeChar:
152 return desc.data
153 # typeColorTable coerced to typeAEList
154 # typeComp coerced to extended
155 # typeData returned as unknown
156 # typeDrawingArea coerced to typeAERecord
157 if t == typeEnumeration:
158 return mkenum(desc.data)
159 # typeEPS returned as unknown
160 if t == typeExtended:
161# print desc, type(desc), len(desc)
162 data = desc.data
163# print `data[:8]`, type(data), len(data[:8])
164# print struct.unpack('=d', data[:8])[0]
165# print string.atoi(data), type(data), len(data)
166# print struct.calcsize(data)
167 # XXX See corresponding note for pack()
168# return struct.unpack('d', data[:2] + data)[0]
169 return struct.unpack('d', data[:8])[0]
170 if t == typeFalse:
171 return 0
172 # typeFixed coerced to extended
173 # typeFloat coerced to extended
174 if t == typeFSS:
175 return macfs.RawFSSpec(desc.data)
176 if t == typeInsertionLoc:
177 record = desc.AECoerceDesc('reco')
178 return mkinsertionloc(unpack(record))
179 # typeInteger equal to typeLongInteger
180 if t == typeIntlText:
181 script, language = struct.unpack('hh', desc.data[:4])
182 return baetypes.IntlText(script, language, desc.data[4:])
183 if t == typeIntlWritingCode:
184 script, language = struct.unpack('hh', desc.data)
185 return baetypes.IntlWritingCode(script, language)
186 if t == typeKeyword:
187 return mkkeyword(desc.data)
188 # typeLongFloat is equal to typeFloat
189 if t == typeLongInteger:
190# print t, struct.unpack('l', desc.data)
191 return struct.unpack('l', desc.data)[0]
192 if t == typeNull:
193 return None
194 if t == typeMagnitude:
195 v = struct.unpack('l', desc.data)
196 if v < 0:
197 v = 0x100000000L + v
198 return v
199 if t == typeObjectSpecifier:
200 import Res
201# print desc, type(desc)
202# print desc.__members__
203# print desc.data, desc.type
204# print unpack(desc)
205# getOSL = calldll.newcall(OSL.AEResolve, 'OSErr', 'InHandle', 'InShort')#, 'InString')
206# print 'OSL', getOSL(rdesc, 0)#, desc.data)
207 record = desc.AECoerceDesc('reco')
208# print record
209 return mkobject(unpack(record))
210 # typePict returned as unknown
211 # typePixelMap coerced to typeAERecord
212 # typePixelMapMinus returned as unknown
213 # typeProcessSerialNumber returned as unknown
214 if t == typeQDPoint:
215 v, h = struct.unpack('hh', desc.data)
216 return baetypes.QDPoint(v, h)
217 if t == typeQDRectangle:
218 v0, h0, v1, h1 = struct.unpack('hhhh', desc.data)
219 return baetypes.QDRectangle(v0, h0, v1, h1)
220 if t == typeRGBColor:
221 r, g, b = struct.unpack('hhh', desc.data)
222 return baetypes.RGBColor(r, g, b)
223 # typeRotation coerced to typeAERecord
224 # typeScrapStyles returned as unknown
225 # typeSessionID returned as unknown
226 if t == typeShortFloat:
227 return struct.unpack('f', desc.data)[0]
228 if t == typeShortInteger:
229# print t, desc.data
230# print struct.unpack('h', desc.data)[0]
231 return struct.unpack('h', desc.data)[0]
232 # typeSMFloat identical to typeShortFloat
233 # typeSMInt indetical to typeShortInt
234 # typeStyledText coerced to typeAERecord
235 if t == typeTargetID:
236 return mktargetid(desc.data)
237 # typeTextStyles coerced to typeAERecord
238 # typeTIFF returned as unknown
239 if t == typeTrue:
240 return 1
241 if t == typeType:
242# print t, desc.data
243 return mktype(desc.data)
244 #
245 # The following are special
246 #
247 if t == 'rang':
248 record = desc.AECoerceDesc('reco')
249 return mkrange(unpack(record))
250 if t == 'cmpd':
251 record = desc.AECoerceDesc('reco')
252 return mkcomparison(unpack(record))
253 if t == 'logi':
254 record = desc.AECoerceDesc('reco')
255 return mklogical(unpack(record))
256 return mkunknown(desc.type, desc.data)
257
258def coerce(data, egdata):
259 """Coerce a python object to another type using the AE coercers"""
260 pdata = pack(data)
261 pegdata = pack(egdata)
262 pdata = pdata.AECoerceDesc(pegdata.type)
263 return unpack(pdata)
264
265#
266# Helper routines for unpack
267#
268def mktargetid(data):
269 sessionID = getlong(data[:4])
270 name = mkppcportrec(data[4:4+72])
271 location = mklocationnamerec(data[76:76+36])
272 rcvrName = mkppcportrec(data[112:112+72])
273 return sessionID, name, location, rcvrName
274
275def mkppcportrec(rec):
276 namescript = getword(rec[:2])
277 name = getpstr(rec[2:2+33])
278 portkind = getword(rec[36:38])
279 if portkind == 1:
280 ctor = rec[38:42]
281 type = rec[42:46]
282 identity = (ctor, type)
283 else:
284 identity = getpstr(rec[38:38+33])
285 return namescript, name, portkind, identity
286
287def mklocationnamerec(rec):
288 kind = getword(rec[:2])
289 stuff = rec[2:]
290 if kind == 0: stuff = None
291 if kind == 2: stuff = getpstr(stuff)
292 return kind, stuff
293
294def mkunknown(type, data):
295 return baetypes.Unknown(type, data)
296
297def getpstr(s):
298 return s[1:1+ord(s[0])]
299
300def getlong(s):
301 return (ord(s[0])<<24) | (ord(s[1])<<16) | (ord(s[2])<<8) | ord(s[3])
302
303def getword(s):
304 return (ord(s[0])<<8) | (ord(s[1])<<0)
305
306def mkkeyword(keyword):
307 return baetypes.Keyword(keyword)
308
309def mkrange(dict):
310 return baetypes.Range(dict['star'], dict['stop'])
311
312def mkcomparison(dict):
313 return baetypes.Comparison(dict['obj1'], dict['relo'].enum, dict['obj2'])
314
315def mklogical(dict):
316 return baetypes.Logical(dict['logc'], dict['term'])
317
318def mkstyledtext(dict):
319 return baetypes.StyledText(dict['ksty'], dict['ktxt'])
320
321def mkaetext(dict):
322 return baetypes.AEText(dict[keyAEScriptTag], dict[keyAEStyles], dict[keyAEText])
323
324def mkinsertionloc(dict):
325 return baetypes.InsertionLoc(dict[keyAEObject], dict[keyAEPosition])
326
327def mkobject(dict):
328 want = dict['want'].type
329 form = dict['form'].enum
330 seld = dict['seld']
331 fr = dict['from']
332 if form in ('name', 'indx', 'rang', 'test'):
333 if want == 'text': return baetypes.Text(seld, fr)
334 if want == 'cha ': return baetypes.Character(seld, fr)
335 if want == 'cwor': return baetypes.Word(seld, fr)
336 if want == 'clin': return baetypes.Line(seld, fr)
337 if want == 'cpar': return baetypes.Paragraph(seld, fr)
338 if want == 'cwin': return baetypes.Window(seld, fr)
339 if want == 'docu': return baetypes.Document(seld, fr)
340 if want == 'file': return baetypes.File(seld, fr)
341 if want == 'cins': return baetypes.InsertionPoint(seld, fr)
342 if want == 'prop' and form == 'prop' and baetypes.IsType(seld):
343 return baetypes.Property(seld.type, fr)
344 return baetypes.ObjectSpecifier(want, form, seld, fr)
345
346def _test():
347 """Test program. Pack and unpack various things"""
348 objs = [
349 'a string',
350 12,
351 12.0,
352 None,
353 ['a', 'list', 'of', 'strings'],
354 {'key1': 'value1', 'key2':'value2'},
355 macfs.FSSpec(':'),
356 macfs.FSSpec(':').NewAliasMinimal(),
357 baetypes.Enum('enum'),
358 baetypes.Type('type'),
359 baetypes.Keyword('kwrd'),
360 baetypes.Range(1, 10),
361 baetypes.Comparison(1, '< ', 10),
362 baetypes.Logical('not ', 1),
363 # Cannot do StyledText
364 # Cannot do AEText
365 baetypes.IntlText(0, 0, 'international text'),
366 baetypes.IntlWritingCode(0,0),
367 baetypes.QDPoint(50,100),
368 baetypes.QDRectangle(50,100,150,200),
369 baetypes.RGBColor(0x7000, 0x6000, 0x5000),
370 baetypes.Unknown('xxxx', 'unknown type data'),
371 baetypes.Character(1),
372 baetypes.Character(2, baetypes.Line(2)),
373 ]
374 for o in objs:
375 print 'BEFORE', o, `o`
376 print type(o)
377 packed = pack(o)
378 unpacked = unpack(packed)
379 print 'AFTER ', unpacked, `unpacked`
380 import sys
381 sys.exit(1)
382
383if __name__ == '__main__':
384 _test()
385