diff --git a/redo/reredo/darwin/alloc.m b/redo/reredo/darwin/alloc.m new file mode 100644 index 00000000..b20cd538 --- /dev/null +++ b/redo/reredo/darwin/alloc.m @@ -0,0 +1,84 @@ +// 4 december 2014 +#import +#import "uipriv_darwin.h" + +NSMutableArray *allocations; + +void initAlloc(void) +{ + allocations = [NSMutableArray new]; +} + +#define UINT8(p) ((uint8_t *) (p)) +#define PVOID(p) ((void *) (p)) +#define EXTRA (sizeof (size_t) + sizeof (const char **)) +#define DATA(p) PVOID(UINT8(p) + EXTRA) +#define BASE(p) PVOID(UINT8(p) - EXTRA) +#define SIZE(p) ((size_t *) (p)) +#define CCHAR(p) ((const char **) (p)) +#define TYPE(p) CCHAR(UINT8(p) + sizeof (size_t)) + +void uninitAlloc(void) +{ + if ([allocations count] == 0) { + [allocations release]; + return; + } + fprintf(stderr, "[libui] leaked allocations:\n"); + [allocations enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL *stop) { + NSValue *v; + void *ptr; + + v = (NSValue *) obj; + ptr = [v pointerValue]; + fprintf(stderr, "[libui] %p %s\n", ptr, *TYPE(ptr)); + }]; + complain("either you left something around or there's a bug in libui"); +} + +void *uiAlloc(size_t size, const char *type) +{ + void *out; + + out = malloc(EXTRA + size); + if (out == NULL) { + fprintf(stderr, "memory exhausted in uiAlloc()\n"); + abort(); + } + memset(DATA(out), 0, size); + *SIZE(out) = size; + *TYPE(out) = type; + [allocations addObject:[NSValue valueWithPointer:out]]; + return DATA(out); +} + +void *uiRealloc(void *p, size_t new, const char *type) +{ + void *out; + size_t *s; + + if (p == NULL) + return uiAlloc(new, type); + p = BASE(p); + out = realloc(p, EXTRA + new); + if (out == NULL) { + fprintf(stderr, "memory exhausted in uiRealloc()\n"); + abort(); + } + s = SIZE(out); + if (new <= *s) + memset(((uint8_t *) DATA(out)) + *s, 0, new - *s); + *s = new; + [allocations removeObject:[NSValue valueWithPointer:p]]; + [allocations addObject:[NSValue valueWithPointer:out]]; + return DATA(out); +} + +void uiFree(void *p) +{ + if (p == NULL) + complain("attempt to uiFree(NULL); there's a bug somewhere"); + p = BASE(p); + free(p); + [allocations removeObject:[NSValue valueWithPointer:p]]; +} diff --git a/redo/reredo/darwin/main.m b/redo/reredo/darwin/main.m new file mode 100644 index 00000000..172e5844 --- /dev/null +++ b/redo/reredo/darwin/main.m @@ -0,0 +1,110 @@ +// 6 april 2015 +#import "uipriv_darwin.h" + +static BOOL canQuit = NO; + +@implementation applicationClass + +// hey look! we're overriding terminate:! +// we're going to make sure we can go back to main() whether Cocoa likes it or not! +// and just how are we going to do that, hm? +// (note: this is called after applicationShouldTerminate:) +- (void)terminate:(id)sender +{ + // yes that's right folks: DO ABSOLUTELY NOTHING. + // the magic is [NSApp run] will just... stop. + + // well let's not do nothing; let's actually quit our graceful way + NSEvent *e; + + // for debugging + NSLog(@"in terminate:"); + + if (!canQuit) + complain("call to [NSApp terminate:] when not ready to terminate"); + + [realNSApp() stop:realNSApp()]; + // stop: won't register until another event has passed; let's synthesize one + e = [NSEvent otherEventWithType:NSApplicationDefined + location:NSZeroPoint + modifierFlags:0 + timestamp:[[NSProcessInfo processInfo] systemUptime] + windowNumber:0 + context:[NSGraphicsContext currentContext] + subtype:0 + data1:0 + data2:0]; + [realNSApp() postEvent:e atStart:NO]; // let pending events take priority (this is what PostQuitMessage() on Windows does so we have to do it here too for parity; thanks to mikeash in irc.freenode.net/#macdev for confirming that this parameter should indeed be NO) +} + +@end + +@implementation appDelegate + +- (void)dealloc +{ + [self.menuManager release]; + [super dealloc]; +} + +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)app +{ + // for debugging + NSLog(@"in applicationShouldTerminate:"); + if (shouldQuit()) { + canQuit = YES; + // this will call terminate:, which is the same as uiQuit() + return NSTerminateNow; + } + return NSTerminateCancel; +} + +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app +{ + return NO; +} + +@end + +uiInitOptions options; + +const char *uiInit(uiInitOptions *o) +{ + options = *o; + [applicationClass sharedApplication]; + // don't check for a NO return; something (launch services?) causes running from application bundles to always return NO when asking to change activation policy, even if the change is to the same activation policy! + // see https://github.com/andlabs/ui/issues/6 + [realNSApp() setActivationPolicy:NSApplicationActivationPolicyRegular]; + [realNSApp() setDelegate:[appDelegate new]]; + + initAlloc(); + + // always do this so we always have an application menu + appDelegate().menuManager = [menuManager new]; + [realNSApp() setMainMenu:[appDelegate().menuManager makeMenubar]]; + + return NULL; +} + +void uiUninit(void) +{ + uninitMenus(); + // TODO free application delegate + // TODO free NSApplication resources (main menu, etc.) + uninitAlloc(); +} + +void uiFreeInitError(const char *err) +{ +} + +void uiMain(void) +{ + [realNSApp() run]; +} + +void uiQuit(void) +{ + canQuit = YES; + [realNSApp() terminate:realNSApp()]; +} diff --git a/redo/reredo/darwin/stddialogs.m b/redo/reredo/darwin/stddialogs.m new file mode 100644 index 00000000..7890ad55 --- /dev/null +++ b/redo/reredo/darwin/stddialogs.m @@ -0,0 +1,24 @@ +// 26 june 2015 +#import "uipriv_darwin.h" + +char *uiOpenFile(void) +{ + // TODO + return NULL; +} + +char *uiSaveFile(void) +{ + // TODO + return NULL; +} + +void uiMsgBox(const char *title, const char *description) +{ + // TODO +} + +void uiMsgBoxError(const char *title, const char *description) +{ + // TODO +} diff --git a/redo/reredo/darwin/text.m b/redo/reredo/darwin/text.m new file mode 100644 index 00000000..f0d3dab6 --- /dev/null +++ b/redo/reredo/darwin/text.m @@ -0,0 +1,19 @@ +// 10 april 2015 +#import "uipriv_darwin.h" + +char *uiDarwinNSStringToText(NSString *s) +{ + char *out; + + out = strdup([s UTF8String]); + if (out == NULL) { + fprintf(stderr, "memory exhausted in uiDarwinNSStringToText()\n"); + abort(); + } + return out; +} + +void uiFreeText(char *s) +{ + free(s); +} diff --git a/redo/reredo/darwin/util.m b/redo/reredo/darwin/util.m new file mode 100644 index 00000000..37af7c58 --- /dev/null +++ b/redo/reredo/darwin/util.m @@ -0,0 +1,56 @@ +// 7 april 2015 +#import "uipriv_darwin.h" + +// also fine for NSCells and NSTexts (NSTextViews) +void setStandardControlFont(NSControl *control) +{ + [control setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]]]; +} + +void disableAutocorrect(NSTextView *tv) +{ + [tv setEnabledTextCheckingTypes:0]; + [tv setAutomaticDashSubstitutionEnabled:NO]; + // don't worry about automatic data detection; it won't change stringValue (thanks pretty_function in irc.freenode.net/#macdev) + [tv setAutomaticSpellingCorrectionEnabled:NO]; + [tv setAutomaticTextReplacementEnabled:NO]; + [tv setAutomaticQuoteSubstitutionEnabled:NO]; + [tv setAutomaticLinkDetectionEnabled:NO]; + [tv setSmartInsertDeleteEnabled:NO]; +} + +void complain(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fprintf(stderr, "[libui] "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + abort(); +} + +// These are based on measurements from Interface Builder. +// These seem to be based on Auto Layout constants, but I don't see an API that exposes these... +// This one is 8 for most pairs of controls that I've tried; the only difference is between two pushbuttons, where it's 12... +#define macXPadding 8 +// Likewise, this one appears to be 12 for pairs of push buttons... +#define macYPadding 8 + +uiSizing *uiDarwinNewSizing(void) +{ + uiSizing *d; + + d = uiNew(uiSizing); + d->XPadding = macXPadding; + d->YPadding = macYPadding; + d->Sys = uiNew(uiSizingSys); + return d; +} + +void uiFreeSizing(uiSizing *d) +{ + uiFree(d->Sys); + uiFree(d); +} diff --git a/redo/reredo/menu.c b/redo/reredo/menu.c new file mode 100644 index 00000000..917e4eaf --- /dev/null +++ b/redo/reredo/menu.c @@ -0,0 +1,19 @@ +// 29 may 2015 +#include "ui.h" + +static uintmax_t type_uiMenu = 0; +static uintmax_t type_uiMenuItem = 0; + +uintmax_t uiTypeMenu(void) +{ + if (type_uiMenu == 0) + type_uiMenu = uiRegisterType("uiMenu", 0, 0); + return type_uiMenu; +} + +uintmax_t uiTypeMenuItem(void) +{ + if (type_uiMenuItem == 0) + type_uiMenuItem = uiRegisterType("uiMenuItem", 0, 0); + return type_uiMenuItem; +} diff --git a/redo/reredo/ptrarray.c b/redo/reredo/ptrarray.c new file mode 100644 index 00000000..45d36f67 --- /dev/null +++ b/redo/reredo/ptrarray.c @@ -0,0 +1,49 @@ +// 5 may 2015 +#include +#include "ui.h" +#include "uipriv.h" + +struct ptrArray *newPtrArray(void) +{ + return uiNew(struct ptrArray); +} + +void ptrArrayDestroy(struct ptrArray *p) +{ + if (p->len != 0) + complain("attempt to destroy ptrarray %p while it still has pointers inside", p); + if (p->ptrs != NULL) // array was created but nothing was ever put inside + uiFree(p->ptrs); + uiFree(p); +} + +#define grow 32 + +void ptrArrayAppend(struct ptrArray *p, void *d) +{ + ptrArrayInsertAt(p, p->len, d); +} + +void ptrArrayInsertAt(struct ptrArray *p, uintmax_t i, void *d) +{ + if (i > p->len) + complain("index out of range in ptrArrayInsertAt()"); + if (p->len >= p->cap) { + p->cap += grow; + p->ptrs = (void **) uiRealloc(p->ptrs, p->cap * sizeof (void *), "void *[]"); + } + // thanks to ValleyBell + memmove(&(p->ptrs[i + 1]), &(p->ptrs[i]), (p->len - i) * sizeof (void *)); + p->ptrs[i] = d; + p->len++; +} + +void ptrArrayDelete(struct ptrArray *p, uintmax_t i) +{ + if (i >= p->len) + complain("index out of range in ptrArrayRemove()"); + // thanks to ValleyBell + memmove(&(p->ptrs[i]), &(p->ptrs[i + 1]), (p->len - i - 1) * sizeof (void *)); + p->ptrs[p->len - 1] = NULL; + p->len--; +} diff --git a/redo/reredo/shouldquit.c b/redo/reredo/shouldquit.c new file mode 100644 index 00000000..27805df8 --- /dev/null +++ b/redo/reredo/shouldquit.c @@ -0,0 +1,22 @@ +// 9 may 2015 +#include "ui.h" +#include "uipriv.h" + +static int defaultOnShouldQuit(void *data) +{ + return 0; +} + +static int (*onShouldQuit)(void *) = defaultOnShouldQuit; +static void *onShouldQuitData; + +void uiOnShouldQuit(int (*f)(void *), void *data) +{ + onShouldQuit = f; + onShouldQuitData = data; +} + +int shouldQuit(void) +{ + return (*onShouldQuit)(onShouldQuitData); +} diff --git a/redo/reredo/types.c b/redo/reredo/types.c new file mode 100644 index 00000000..62fe1543 --- /dev/null +++ b/redo/reredo/types.c @@ -0,0 +1,88 @@ +// 17 may 2015 +#include "ui.h" +#include "uipriv.h" + +struct typeinfo { + const char *name; + uintmax_t parent; + size_t size; +}; + +static struct ptrArray *types = NULL; + +uintmax_t uiRegisterType(const char *name, uintmax_t parent, size_t size) +{ + struct typeinfo *ti; + + if (types == NULL) { + types = newPtrArray(); + // reserve ID 0 + ptrArrayAppend(types, NULL); + } + // TODO prevent our size from being smaller than our parent's + ti = uiNew(struct typeinfo); + ti->name = name; + ti->parent = parent; + ti->size = size; + ptrArrayAppend(types, ti); + return types->len - 1; +} + +void *uiIsA(void *p, uintmax_t id, int fail) +{ + uiTyped *t; + struct typeinfo *ti, *ti2; + uintmax_t compareTo; + + if (id == 0 || id >= types->len) + complain("invalid type ID given to uiIsA()"); + t = (uiTyped *) p; + compareTo = t->Type; + if (compareTo == 0) + complain("object %p has no type in uiIsA()", t); + for (;;) { + if (compareTo >= types->len) + complain("invalid type ID in uiIsA()", t); + if (compareTo == id) + return t; + ti = ptrArrayIndex(types, struct typeinfo *, compareTo); + if (ti->parent == 0) + break; + compareTo = ti->parent; + } + if (fail) { + ti = ptrArrayIndex(types, struct typeinfo *, id); + ti2 = ptrArrayIndex(types, struct typeinfo *, t->Type); + complain("object %p not a %s in uiIsA() (is a %s)", t, ti->name, ti2->name); + } + return NULL; +} + +void uninitTypes(void) +{ + struct typeinfo *ti; + + if (types == NULL) // never initialized; do nothing + return; + // the first entry is NULL; get rid of it directly + ptrArrayDelete(types, 0); + while (types->len != 0) { + ti = ptrArrayIndex(types, struct typeinfo *, 0); + ptrArrayDelete(types, 0); + uiFree(ti); + } + ptrArrayDestroy(types); +} + +uiTyped *newTyped(uintmax_t type) +{ + struct typeinfo *ti; + uiTyped *instance; + + if (type == 0 || type >= types->len) + complain("invalid type ID given to newTyped()"); + ti = ptrArrayIndex(types, struct typeinfo *, type); + instance = (uiTyped *) uiAlloc(ti->size, ti->name); + instance->Type = type; + return instance; +}