1 /*
2  * Copyright (c) 2004-2015 Derelict Developers
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * * Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  *
16  * * Neither the names 'Derelict', 'DerelictSDL', nor the names of its contributors
17  *   may be used to endorse or promote products derived from this software
18  *   without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 module derelict.cocoa.foundation;
33 
34 import std.string;
35 import std.utf;
36 
37 import dplug.core.nogc;
38 
39 import derelict.cocoa.runtime;
40 
41 
42 // NSZone
43 
44 extern (C) nothrow @nogc
45 {
46     alias void* function(NSUInteger bytes) pfNSAllocateMemoryPages;
47     alias void function (void* ptr, NSUInteger bytes) pfNSDeallocateMemoryPages;
48     alias void function(id format, ...) pfNSLog;
49 }
50 
51 __gshared
52 {
53     pfNSDeallocateMemoryPages NSDeallocateMemoryPages;
54     pfNSAllocateMemoryPages NSAllocateMemoryPages;
55     pfNSLog NSLog;
56 }
57 
58 __gshared
59 {
60     //NSString NSDefaultRunLoopMode;
61     NSString NSRunLoopCommonModes;
62 }
63 
64 alias NSTimeInterval = double;
65 
66 
67 // Mixin'd by all Cocoa objects
68 mixin template NSObjectTemplate(T, string className)
69 {
70 nothrow @nogc:
71 
72     // create from an id
73     this (id id_)
74     {
75         this._id = id_;
76     }
77 
78     /// Allocates, but do not init
79     static T alloc()
80     {
81         alias fun_t = extern(C) id function (id obj, SEL sel) nothrow @nogc;
82         return T( (cast(fun_t)objc_msgSend)(getClassID(), sel!"alloc") );
83     }
84 
85     static Class getClass()
86     {
87         return cast(Class)( lazyClass!className() );
88     }
89 
90     static id getClassID()
91     {
92         return lazyClass!className();
93     }
94 }
95 
96 struct NSObject
97 {
98 nothrow @nogc:
99 
100     // The only field available in all NSObject hierarchy
101     // That makes all these destructs idempotent with an id,
102     // and the size of a pointer.
103     id _id = null;
104 
105     // Subtype id
106     bool opCast()
107     {
108         return _id != null;
109     }
110 
111     mixin NSObjectTemplate!(NSObject, "NSObject");
112 
113     ~this()
114     {
115     }
116 
117     NSObject init_()
118     {
119         alias fun_t = extern(C) id function (id, const(SEL)) nothrow @nogc;
120         id result = (cast(fun_t)objc_msgSend)(_id, sel!"init");
121         return NSObject(result);
122     }
123 
124     void retain()
125     {
126         alias fun_t = extern(C) void function (id, const(SEL)) nothrow @nogc;
127         (cast(fun_t)objc_msgSend)(_id, sel!"retain");
128     }
129 
130     void release()
131     {
132         alias fun_t = extern(C) void function (id, const(SEL)) nothrow @nogc;
133         (cast(fun_t)objc_msgSend)(_id, sel!"release");
134     }
135 }
136 
137 struct NSData
138 {
139 nothrow @nogc:
140 
141     NSObject parent;
142     alias parent this;
143 
144     mixin NSObjectTemplate!(NSData, "NSData");
145 
146     static NSData data()
147     {
148         alias fun_t = extern(C) id function (id obj, const(SEL) sel) nothrow @nogc;
149         id result = (cast(fun_t)objc_msgSend)(getClassID(), sel!"data");
150         return NSData(result);
151     }
152 
153     static NSData dataWithBytesNoCopy(void* bytes, NSUInteger length, bool freeWhenDone)
154     {
155         alias fun_t = extern(C) id function(id, const(SEL), void*, NSUInteger, BOOL) nothrow @nogc;
156         id result = (cast(fun_t)objc_msgSend)(getClassID(), sel!"dataWithBytesNoCopy:length:freeWhenDone:",
157             bytes, length, freeWhenDone ? YES : NO);
158         return NSData(result);
159     }
160 }
161 
162 struct NSString
163 {
164 nothrow @nogc:
165 
166     NSObject parent;
167     alias parent this;
168 
169     mixin NSObjectTemplate!(NSString, "NSString");
170 
171     static NSString stringWith (wstring str) nothrow @nogc
172     {
173         alias fun_t = extern(C) id function(id, SEL, const(wchar)*, NSUInteger) nothrow @nogc;
174         id result = (cast(fun_t)objc_msgSend)(getClassID(), sel!"stringWithCharacters:length:",
175                                  CString16(str).storage, cast(NSUInteger)(str.length));
176         return NSString(result);
177     }
178 
179     size_t length ()
180     {
181         alias fun_t = extern(C) NSUInteger function(id, SEL) nothrow @nogc;
182         return cast(size_t)( (cast(fun_t)objc_msgSend)(_id, sel!"length") );
183     }
184 
185     char* UTF8String ()
186     {
187         alias fun_t = extern(C) char* function(id, SEL) nothrow @nogc;
188         return (cast(fun_t)objc_msgSend)(_id, sel!"UTF8String");
189     }
190 
191     void getCharacters (wchar* buffer, NSRange range)
192     {
193         alias fun_t = extern(C) void function(id, SEL, wchar*, NSRange) nothrow @nogc;
194         (cast(fun_t)objc_msgSend)(_id, sel!"getCharacters:range:", buffer, range);
195     }
196 
197     NSString stringWithCharacters (wchar* chars, size_t length)
198     {
199         alias fun_t = extern(C) id function(id, SEL, wchar*, NSUInteger) nothrow @nogc;
200         id result = (cast(fun_t)objc_msgSend)(_id, sel!"stringWithCharacters:length:", chars, cast(NSUInteger)length);
201         return NSString(result);
202     }
203 
204     NSRange rangeOfString (NSString aString)
205     {
206         alias fun_t = extern(C) NSRange function(id, SEL, id) nothrow @nogc;
207         return (cast(fun_t)objc_msgSend)(_id, sel!"rangeOfString", aString._id);
208     }
209 
210     NSString stringByAppendingString (NSString aString)
211     {
212         alias fun_t = extern(C) id function(id, SEL, id) nothrow @nogc;
213         id result = (cast(fun_t)objc_msgSend)(_id, sel!"stringByAppendingString:", aString._id);
214         return NSString(result);
215     }
216 }
217 
218 struct NSURL
219 {
220 nothrow @nogc:
221     NSObject parent;
222     alias parent this;
223 
224     mixin NSObjectTemplate!(NSURL, "NSURL");
225 
226     static NSURL URLWithString(NSString str)
227     {
228         alias fun_t = extern(C) id function(id, SEL, id) nothrow @nogc;
229         id result = (cast(fun_t)objc_msgSend)(getClassID(), sel!"URLWithString:", str._id);
230         return NSURL(result);
231     }
232 
233 }
234 
235 struct NSEnumerator
236 {
237 nothrow @nogc:
238     NSObject parent;
239     alias parent this;
240 
241     mixin NSObjectTemplate!(NSEnumerator, "NSEnumerator");
242 
243     id nextObject ()
244     {
245         alias fun_t = extern(C) id function(id, SEL) nothrow @nogc;
246         return (cast(fun_t)objc_msgSend)(_id, sel!"nextObject");
247     }
248 }
249 
250 struct NSArray
251 {
252 nothrow @nogc:
253 
254     NSObject parent;
255     alias parent this;
256 
257     mixin NSObjectTemplate!(NSArray, "NSArray");
258 
259     NSEnumerator objectEnumerator ()
260     {
261         alias fun_t = extern(C) id function(id, SEL) nothrow @nogc;
262         id result = (cast(fun_t)objc_msgSend)(_id, sel!"objectEnumerator");
263         return NSEnumerator(result);
264     }
265 }
266 
267 
268 struct NSProcessInfo
269 {
270 nothrow @nogc:
271 
272     NSObject parent;
273     alias parent this;
274 
275     mixin NSObjectTemplate!(NSProcessInfo, "NSProcessInfo");
276 
277     static NSProcessInfo processInfo ()
278     {
279         alias fun_t = extern(C) id function(id, SEL) nothrow @nogc;
280         id result = (cast(fun_t)objc_msgSend)(lazyClass!"NSProcessInfo", sel!"processInfo");
281         return NSProcessInfo(result);
282     }
283 
284     NSString processName ()
285     {
286         alias fun_t = extern(C) id function(id, SEL) nothrow @nogc;
287         id result = (cast(fun_t)objc_msgSend)(_id, sel!"processName");
288         return NSString(result);
289     }
290 }
291 
292 struct NSNotification
293 {
294 nothrow @nogc:
295     NSObject parent;
296     alias parent this;
297 
298     mixin NSObjectTemplate!(NSNotification, "NSNotification");
299 }
300 
301 struct NSDictionary
302 {
303 nothrow @nogc:
304     NSObject parent;
305     alias parent this;
306 
307     mixin NSObjectTemplate!(NSDictionary, "NSDictionary");
308 
309     id objectForKey(id key)
310     {
311         alias fun_t = extern(C) id function(id, SEL, id) nothrow @nogc;
312         id result = (cast(fun_t)objc_msgSend)(_id, sel!"objectForKey:", key);
313         return result;
314     }
315 }
316 
317 struct NSAutoreleasePool
318 {
319 nothrow @nogc:
320     NSObject parent;
321     alias parent this;
322 
323     mixin NSObjectTemplate!(NSAutoreleasePool, "NSAutoreleasePool");
324 }
325 
326 enum : int
327 {
328     NSFileErrorMaximum = 1023,
329     NSFileErrorMinimum = 0,
330     NSFileLockingError = 255,
331     NSFileNoSuchFileError = 4,
332     NSFileReadCorruptFileError = 259,
333     NSFileReadInapplicableStringEncodingError = 261,
334     NSFileReadInvalidFileNameError = 258,
335     NSFileReadNoPermissionError = 257,
336     NSFileReadNoSuchFileError = 260,
337     NSFileReadUnknownError = 256,
338     NSFileReadUnsupportedSchemeError = 262,
339     NSFileWriteInapplicableStringEncodingError = 517,
340     NSFileWriteInvalidFileNameError = 514,
341     NSFileWriteNoPermissionError = 513,
342     NSFileWriteOutOfSpaceError = 640,
343     NSFileWriteUnknownError = 512,
344     NSFileWriteUnsupportedSchemeError = 518,
345     NSFormattingError = 2048,
346     NSFormattingErrorMaximum = 2559,
347     NSFormattingErrorMinimum = 2048,
348     NSKeyValueValidationError = 1024,
349     NSUserCancelledError = 3072,
350     NSValidationErrorMaximum = 2047,
351     NSValidationErrorMinimum = 1024,
352 
353     NSExecutableArchitectureMismatchError = 3585,
354     NSExecutableErrorMaximum = 3839,
355     NSExecutableErrorMinimum = 3584,
356     NSExecutableLinkError = 3588,
357     NSExecutableLoadError = 3587,
358     NSExecutableNotLoadableError = 3584,
359     NSExecutableRuntimeMismatchError = 3586,
360     NSFileReadTooLargeError = 263,
361     NSFileReadUnknownStringEncodingError = 264,
362 
363     GSFoundationPlaceHolderError = 9999
364 }
365 
366 struct NSError
367 {
368 nothrow @nogc:
369     NSObject parent;
370     alias parent this;
371 
372     mixin NSObjectTemplate!(NSError, "NSError");
373 
374     NSString localizedDescription()
375     {
376         alias fun_t = extern(C) id function(id, SEL) nothrow @nogc;
377         id res = (cast(fun_t)objc_msgSend)(_id, sel!"localizedDescription");
378         return NSString(res);
379     }
380 }
381 
382 struct NSBundle
383 {
384 nothrow @nogc:
385     NSObject parent;
386     alias parent this;
387 
388     mixin NSObjectTemplate!(NSBundle, "NSBundle");
389 
390     void initWithPath(NSString path)
391     {
392         alias fun_t = extern(C) void function(id, SEL, id) nothrow @nogc;
393         (cast(fun_t)objc_msgSend)(_id, sel!"initWithPath:", path._id);
394     }
395 
396     bool load()
397     {
398         alias fun_t = extern(C) BOOL function(id, SEL) nothrow @nogc;
399         return (cast(fun_t)objc_msgSend)(_id, sel!"load") != NO;
400     }
401 
402     bool unload()
403     {
404         alias fun_t = extern(C) BOOL function(id, SEL) nothrow @nogc;
405         return (cast(fun_t)objc_msgSend)(_id, sel!"unload") != NO;
406     }
407 
408     bool loadAndReturnError(NSError error)
409     {
410         alias fun_t = extern(C) BOOL function(id, SEL, id) nothrow @nogc;
411         return (cast(fun_t)objc_msgSend)(_id, sel!"loadAndReturnError:", error._id) != NO;
412     }
413 }
414 
415 struct NSTimer
416 {
417 nothrow @nogc:
418     NSObject parent;
419     alias parent this;
420 
421     mixin NSObjectTemplate!(NSTimer, "NSTimer");
422 
423     static NSTimer timerWithTimeInterval(double seconds, NSObject target, SEL selector, void* userInfo, bool repeats)
424     {
425         alias fun_t = extern(C) id function(id, SEL, double, id, SEL, id, BOOL) nothrow @nogc;
426         id result = (cast(fun_t)objc_msgSend)(getClassID(), sel!"timerWithTimeInterval:target:selector:userInfo:repeats:",
427                     seconds, target._id, selector, cast(id)userInfo, repeats ? YES : NO);
428         return NSTimer(result);
429     }
430 
431     void invalidate()
432     {
433         alias fun_t = extern(C) void function(id, SEL) nothrow @nogc;
434         (cast(fun_t)objc_msgSend)(_id, sel!"invalidate");
435     }
436 }
437 
438 struct NSRunLoop
439 {
440 nothrow @nogc:
441     NSObject parent;
442     alias parent this;
443 
444     mixin NSObjectTemplate!(NSRunLoop, "NSRunLoop");
445 
446     static NSRunLoop currentRunLoop()
447     {
448         alias fun_t = extern(C) id function(id, SEL) nothrow @nogc;
449         id result = (cast(fun_t)objc_msgSend)(getClassID(), sel!"currentRunLoop");
450         return NSRunLoop(result);
451     }
452 
453     void addTimer(NSTimer aTimer, NSString forMode)
454     {
455         alias fun_t = extern(C) void function(id, SEL, id, id) nothrow @nogc;
456         (cast(fun_t)objc_msgSend)(_id, sel!"addTimer:forMode:", aTimer._id, forMode._id);
457     }
458 }
459 
460 struct NSDate
461 {
462 nothrow @nogc:
463     NSObject parent;
464     alias parent this;
465 
466     mixin NSObjectTemplate!(NSDate, "NSDate");
467 
468     static NSDate date()
469     {
470         return NSDate(objc_msgSend(getClassID(), sel!"date"));
471     }
472 
473     static NSTimeInterval timeIntervalSinceReferenceDate()
474     {
475         alias fun_t = extern(C) NSTimeInterval function(id, SEL) nothrow @nogc;
476 
477          version(X86)
478             return (cast(fun_t)objc_msgSend_fpret)(getClassID(), sel!"timeIntervalSinceReferenceDate");
479         else version(X86_64)
480             return (cast(fun_t)objc_msgSend)(getClassID(), sel!"timeIntervalSinceReferenceDate");
481         else
482             static assert(false);
483     }
484 }
485 
486 
487