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 /**
33     Dynamic bindings to the Foundation framework.
34 */
35 module derelict.cocoa.foundation;
36 
37 import std..string;
38 import std.utf;
39 
40 import dplug.core.nogc;
41 
42 import derelict.cocoa.runtime;
43 
44 
45 // NSZone
46 
47 extern (C) nothrow @nogc
48 {
49     alias void* function(NSUInteger bytes) pfNSAllocateMemoryPages;
50     alias void function (void* ptr, NSUInteger bytes) pfNSDeallocateMemoryPages;
51     alias void function(id format, ...) pfNSLog;
52 }
53 
54 __gshared
55 {
56     pfNSDeallocateMemoryPages NSDeallocateMemoryPages;
57     pfNSAllocateMemoryPages NSAllocateMemoryPages;
58     pfNSLog NSLog;
59 }
60 
61 __gshared
62 {
63     //NSString NSDefaultRunLoopMode;
64     NSString NSRunLoopCommonModes;
65 }
66 
67 alias NSTimeInterval = double;
68 
69 
70 // Mixin'd by all Cocoa objects
71 mixin template NSObjectTemplate(T, string className)
72 {
73 nothrow @nogc:
74 
75     // create from an id
76     this (id id_)
77     {
78         this._id = id_;
79     }
80 
81     /// Allocates, but do not init
82     static T alloc()
83     {
84         alias fun_t = extern(C) id function (id obj, SEL sel) nothrow @nogc;
85         return T( (cast(fun_t)objc_msgSend)(getClassID(), sel!"alloc") );
86     }
87 
88     static Class getClass()
89     {
90         return cast(Class)( lazyClass!className() );
91     }
92 
93     static id getClassID()
94     {
95         return lazyClass!className();
96     }
97 }
98 
99 struct NSObject
100 {
101 nothrow @nogc:
102 
103     // The only field available in all NSObject hierarchy
104     // That makes all these destructs idempotent with an id,
105     // and the size of a pointer.
106     id _id = null;
107 
108     // Subtype id
109     bool opCast()
110     {
111         return _id != null;
112     }
113 
114     mixin NSObjectTemplate!(NSObject, "NSObject");
115 
116     ~this()
117     {
118     }
119 
120     NSObject init_()
121     {
122         alias fun_t = extern(C) id function (id, const(SEL)) nothrow @nogc;
123         id result = (cast(fun_t)objc_msgSend)(_id, sel!"init");
124         return NSObject(result);
125     }
126 
127     void retain()
128     {
129         alias fun_t = extern(C) void function (id, const(SEL)) nothrow @nogc;
130         (cast(fun_t)objc_msgSend)(_id, sel!"retain");
131     }
132 
133     void release()
134     {
135         alias fun_t = extern(C) void function (id, const(SEL)) nothrow @nogc;
136         (cast(fun_t)objc_msgSend)(_id, sel!"release");
137     }
138 }
139 
140 struct NSData
141 {
142 nothrow @nogc:
143 
144     NSObject parent;
145     alias parent this;
146 
147     mixin NSObjectTemplate!(NSData, "NSData");
148 
149     static NSData data()
150     {
151         alias fun_t = extern(C) id function (id obj, const(SEL) sel) nothrow @nogc;
152         id result = (cast(fun_t)objc_msgSend)(getClassID(), sel!"data");
153         return NSData(result);
154     }
155 
156     static NSData dataWithBytesNoCopy(void* bytes, NSUInteger length, bool freeWhenDone)
157     {
158         alias fun_t = extern(C) id function(id, const(SEL), void*, NSUInteger, BOOL) nothrow @nogc;
159         id result = (cast(fun_t)objc_msgSend)(getClassID(), sel!"dataWithBytesNoCopy:length:freeWhenDone:",
160             bytes, length, freeWhenDone ? YES : NO);
161         return NSData(result);
162     }
163 }
164 
165 struct NSString
166 {
167 nothrow @nogc:
168 
169     NSObject parent;
170     alias parent this;
171 
172     mixin NSObjectTemplate!(NSString, "NSString");
173 
174     static NSString stringWith (wstring str) nothrow @nogc
175     {
176         alias fun_t = extern(C) id function(id, SEL, const(wchar)*, NSUInteger) nothrow @nogc;
177         id result = (cast(fun_t)objc_msgSend)(getClassID(), sel!"stringWithCharacters:length:",
178                                  CString16(str).storage, cast(NSUInteger)(str.length));
179         return NSString(result);
180     }
181 
182     size_t length ()
183     {
184         alias fun_t = extern(C) NSUInteger function(id, SEL) nothrow @nogc;
185         return cast(size_t)( (cast(fun_t)objc_msgSend)(_id, sel!"length") );
186     }
187 
188     char* UTF8String ()
189     {
190         alias fun_t = extern(C) char* function(id, SEL) nothrow @nogc;
191         return (cast(fun_t)objc_msgSend)(_id, sel!"UTF8String");
192     }
193 
194     void getCharacters (wchar* buffer, NSRange range)
195     {
196         alias fun_t = extern(C) void function(id, SEL, wchar*, NSRange) nothrow @nogc;
197         (cast(fun_t)objc_msgSend)(_id, sel!"getCharacters:range:", buffer, range);
198     }
199 
200     NSString stringWithCharacters (wchar* chars, size_t length)
201     {
202         alias fun_t = extern(C) id function(id, SEL, wchar*, NSUInteger) nothrow @nogc;
203         id result = (cast(fun_t)objc_msgSend)(_id, sel!"stringWithCharacters:length:", chars, cast(NSUInteger)length);
204         return NSString(result);
205     }
206 
207     NSRange rangeOfString (NSString aString)
208     {
209         alias fun_t = extern(C) NSRange function(id, SEL, id) nothrow @nogc;
210         return (cast(fun_t)objc_msgSend)(_id, sel!"rangeOfString", aString._id);
211     }
212 
213     NSString stringByAppendingString (NSString aString)
214     {
215         alias fun_t = extern(C) id function(id, SEL, id) nothrow @nogc;
216         id result = (cast(fun_t)objc_msgSend)(_id, sel!"stringByAppendingString:", aString._id);
217         return NSString(result);
218     }
219 
220     /// Returns: The character at a given UTF-16 code unit index.
221     wchar characterAtIndex(int index)
222     {
223         alias fun_t = extern(C) wchar function(id, SEL, NSUInteger) nothrow @nogc;
224         return (cast(fun_t)objc_msgSend)(_id, sel!"characterAtIndex:", cast(NSUInteger)index);
225     }
226 }
227 
228 struct NSURL
229 {
230 nothrow @nogc:
231     NSObject parent;
232     alias parent this;
233 
234     mixin NSObjectTemplate!(NSURL, "NSURL");
235 
236     static NSURL URLWithString(NSString str)
237     {
238         alias fun_t = extern(C) id function(id, SEL, id) nothrow @nogc;
239         id result = (cast(fun_t)objc_msgSend)(getClassID(), sel!"URLWithString:", str._id);
240         return NSURL(result);
241     }
242 
243 }
244 
245 struct NSEnumerator
246 {
247 nothrow @nogc:
248     NSObject parent;
249     alias parent this;
250 
251     mixin NSObjectTemplate!(NSEnumerator, "NSEnumerator");
252 
253     id nextObject ()
254     {
255         alias fun_t = extern(C) id function(id, SEL) nothrow @nogc;
256         return (cast(fun_t)objc_msgSend)(_id, sel!"nextObject");
257     }
258 }
259 
260 struct NSArray
261 {
262 nothrow @nogc:
263 
264     NSObject parent;
265     alias parent this;
266 
267     mixin NSObjectTemplate!(NSArray, "NSArray");
268 
269     NSEnumerator objectEnumerator ()
270     {
271         alias fun_t = extern(C) id function(id, SEL) nothrow @nogc;
272         id result = (cast(fun_t)objc_msgSend)(_id, sel!"objectEnumerator");
273         return NSEnumerator(result);
274     }
275 }
276 
277 
278 struct NSProcessInfo
279 {
280 nothrow @nogc:
281 
282     NSObject parent;
283     alias parent this;
284 
285     mixin NSObjectTemplate!(NSProcessInfo, "NSProcessInfo");
286 
287     static NSProcessInfo processInfo ()
288     {
289         alias fun_t = extern(C) id function(id, SEL) nothrow @nogc;
290         id result = (cast(fun_t)objc_msgSend)(lazyClass!"NSProcessInfo", sel!"processInfo");
291         return NSProcessInfo(result);
292     }
293 
294     NSString processName ()
295     {
296         alias fun_t = extern(C) id function(id, SEL) nothrow @nogc;
297         id result = (cast(fun_t)objc_msgSend)(_id, sel!"processName");
298         return NSString(result);
299     }
300 }
301 
302 struct NSNotification
303 {
304 nothrow @nogc:
305     NSObject parent;
306     alias parent this;
307 
308     mixin NSObjectTemplate!(NSNotification, "NSNotification");
309 }
310 
311 struct NSDictionary
312 {
313 nothrow @nogc:
314     NSObject parent;
315     alias parent this;
316 
317     mixin NSObjectTemplate!(NSDictionary, "NSDictionary");
318 
319     static NSDictionary dictionaryWithObjectsAndKeys(Args...)(id firstObject, Args args)
320     {
321         alias fun_t = extern(C) id function(id, SEL, id, ...) nothrow @nogc;
322         auto result = (cast(fun_t) objc_msgSend)(getClassID(), sel!"dictionaryWithObjectsAndKeys:", firstObject, args);
323 
324         return NSDictionary(result);
325     }
326 
327     id objectForKey(id key)
328     {
329         alias fun_t = extern(C) id function(id, SEL, id) nothrow @nogc;
330         id result = (cast(fun_t)objc_msgSend)(_id, sel!"objectForKey:", key);
331         return result;
332     }
333 }
334 
335 struct NSAutoreleasePool
336 {
337 nothrow @nogc:
338     NSObject parent;
339     alias parent this;
340 
341     mixin NSObjectTemplate!(NSAutoreleasePool, "NSAutoreleasePool");
342 }
343 
344 enum : int
345 {
346     NSFileErrorMaximum = 1023,
347     NSFileErrorMinimum = 0,
348     NSFileLockingError = 255,
349     NSFileNoSuchFileError = 4,
350     NSFileReadCorruptFileError = 259,
351     NSFileReadInapplicableStringEncodingError = 261,
352     NSFileReadInvalidFileNameError = 258,
353     NSFileReadNoPermissionError = 257,
354     NSFileReadNoSuchFileError = 260,
355     NSFileReadUnknownError = 256,
356     NSFileReadUnsupportedSchemeError = 262,
357     NSFileWriteInapplicableStringEncodingError = 517,
358     NSFileWriteInvalidFileNameError = 514,
359     NSFileWriteNoPermissionError = 513,
360     NSFileWriteOutOfSpaceError = 640,
361     NSFileWriteUnknownError = 512,
362     NSFileWriteUnsupportedSchemeError = 518,
363     NSFormattingError = 2048,
364     NSFormattingErrorMaximum = 2559,
365     NSFormattingErrorMinimum = 2048,
366     NSKeyValueValidationError = 1024,
367     NSUserCancelledError = 3072,
368     NSValidationErrorMaximum = 2047,
369     NSValidationErrorMinimum = 1024,
370 
371     NSExecutableArchitectureMismatchError = 3585,
372     NSExecutableErrorMaximum = 3839,
373     NSExecutableErrorMinimum = 3584,
374     NSExecutableLinkError = 3588,
375     NSExecutableLoadError = 3587,
376     NSExecutableNotLoadableError = 3584,
377     NSExecutableRuntimeMismatchError = 3586,
378     NSFileReadTooLargeError = 263,
379     NSFileReadUnknownStringEncodingError = 264,
380 
381     GSFoundationPlaceHolderError = 9999
382 }
383 
384 struct NSError
385 {
386 nothrow @nogc:
387     NSObject parent;
388     alias parent this;
389 
390     mixin NSObjectTemplate!(NSError, "NSError");
391 
392     NSString localizedDescription()
393     {
394         alias fun_t = extern(C) id function(id, SEL) nothrow @nogc;
395         id res = (cast(fun_t)objc_msgSend)(_id, sel!"localizedDescription");
396         return NSString(res);
397     }
398 }
399 
400 struct NSBundle
401 {
402 nothrow @nogc:
403     NSObject parent;
404     alias parent this;
405 
406     mixin NSObjectTemplate!(NSBundle, "NSBundle");
407 
408     void initWithPath(NSString path)
409     {
410         alias fun_t = extern(C) void function(id, SEL, id) nothrow @nogc;
411         (cast(fun_t)objc_msgSend)(_id, sel!"initWithPath:", path._id);
412     }
413 
414     bool load()
415     {
416         alias fun_t = extern(C) BOOL function(id, SEL) nothrow @nogc;
417         return (cast(fun_t)objc_msgSend)(_id, sel!"load") != NO;
418     }
419 
420     bool unload()
421     {
422         alias fun_t = extern(C) BOOL function(id, SEL) nothrow @nogc;
423         return (cast(fun_t)objc_msgSend)(_id, sel!"unload") != NO;
424     }
425 
426     bool loadAndReturnError(NSError error)
427     {
428         alias fun_t = extern(C) BOOL function(id, SEL, id) nothrow @nogc;
429         return (cast(fun_t)objc_msgSend)(_id, sel!"loadAndReturnError:", error._id) != NO;
430     }
431 }
432 
433 struct NSTimer
434 {
435 nothrow @nogc:
436     NSObject parent;
437     alias parent this;
438 
439     mixin NSObjectTemplate!(NSTimer, "NSTimer");
440 
441     static NSTimer timerWithTimeInterval(double seconds, NSObject target, SEL selector, void* userInfo, bool repeats)
442     {
443         alias fun_t = extern(C) id function(id, SEL, double, id, SEL, id, BOOL) nothrow @nogc;
444         id result = (cast(fun_t)objc_msgSend)(getClassID(), sel!"timerWithTimeInterval:target:selector:userInfo:repeats:",
445                     seconds, target._id, selector, cast(id)userInfo, repeats ? YES : NO);
446         return NSTimer(result);
447     }
448 
449     void invalidate()
450     {
451         alias fun_t = extern(C) void function(id, SEL) nothrow @nogc;
452         (cast(fun_t)objc_msgSend)(_id, sel!"invalidate");
453     }
454 }
455 
456 struct NSRunLoop
457 {
458 nothrow @nogc:
459     NSObject parent;
460     alias parent this;
461 
462     mixin NSObjectTemplate!(NSRunLoop, "NSRunLoop");
463 
464     static NSRunLoop currentRunLoop()
465     {
466         alias fun_t = extern(C) id function(id, SEL) nothrow @nogc;
467         id result = (cast(fun_t)objc_msgSend)(getClassID(), sel!"currentRunLoop");
468         return NSRunLoop(result);
469     }
470 
471     static NSRunLoop mainRunLoop()
472     {
473         alias fun_t = extern(C) id function(id, SEL) nothrow @nogc;
474         id result = (cast(fun_t)objc_msgSend)(getClassID(), sel!"mainRunLoop");
475         return NSRunLoop(result);
476     }
477 
478     void runUntilDate(NSDate limitDate)
479     {
480         alias fun_t = extern(C) void function(id, SEL, id) nothrow @nogc;
481         (cast(fun_t)objc_msgSend)(_id, sel!"runUntilDate:", limitDate._id);
482     }
483 
484     void addTimer(NSTimer aTimer, NSString forMode)
485     {
486         alias fun_t = extern(C) void function(id, SEL, id, id) nothrow @nogc;
487         (cast(fun_t)objc_msgSend)(_id, sel!"addTimer:forMode:", aTimer._id, forMode._id);
488     }
489 }
490 
491 struct NSDate
492 {
493 nothrow @nogc:
494     NSObject parent;
495     alias parent this;
496 
497     mixin NSObjectTemplate!(NSDate, "NSDate");
498 
499     static NSDate date()
500     {
501         return NSDate(objc_msgSend(getClassID(), sel!"date"));
502     }
503 
504     static NSTimeInterval timeIntervalSinceReferenceDate()
505     {
506         alias fun_t = extern(C) NSTimeInterval function(id, SEL) nothrow @nogc;
507 
508         version(X86)
509             return (cast(fun_t)objc_msgSend_fpret)(getClassID(), sel!"timeIntervalSinceReferenceDate");
510         else version(X86_64)
511             return (cast(fun_t)objc_msgSend)(getClassID(), sel!"timeIntervalSinceReferenceDate");
512         else
513             static assert(false);
514     }
515 
516     static NSDate dateWithTimeIntervalSinceNow(double secs)
517     {
518         alias fun_t = extern(C) id function(id, SEL, double) nothrow @nogc;
519         id res = (cast(fun_t)objc_msgSend)(getClassID(), sel!"dateWithTimeIntervalSinceNow:", secs);
520 
521         return NSDate(res);
522     }
523 
524     NSString description()
525     {
526         alias fun_t = extern(C) id function(id, SEL) nothrow @nogc;
527         id result = (cast(fun_t)objc_msgSend)(_id, sel!"description");
528 
529         return NSString(result);
530     }
531 
532     NSString descriptionWithLocale()
533     {
534         alias fun_t = extern(C) id function(id, SEL, void*) nothrow @nogc;
535         id result = (cast(fun_t)objc_msgSend)(_id, sel!"descriptionWithLocale:", null);
536         return NSString(result);
537     }
538 }
539 
540 struct NSNumber
541 {
542 nothrow @nogc:
543     NSObject parent;
544     alias parent this;
545 
546     mixin NSObjectTemplate!(NSNumber, "NSNumber");
547 
548     static NSNumber numberWithUnsignedInt(uint value)
549     {
550         alias fun_t = extern(C) id function(id, SEL, uint) nothrow @nogc;
551         id result = (cast(fun_t)objc_msgSend)(getClassID(), sel!"numberWithUnsignedInt:", value);
552 
553         return NSNumber(result);
554     }
555 }