feat(vm): add show_error function#353
Conversation
|
Added desktop popups for Windows and macOS on all backends, and for Linux on SDL2 and SDL3. I haven't tested Windows or Linux but I think it should work. For GLFW and SDL1, macOS and Windows use the native system calls to show an error message. This functionality is included in SDL2 and SDL3 though, so we can just use that. |
|
Do we need a dedicated Mac thing? Doesn't glfw have a message box function we can use instead? Also we would need a ton of modification to the build system to add objective C because the makefile is only setup to build .c files. My iOS port handled this by building everything with -ObjC and just putting the objc in a .c file but idk if that would work well with cmake. |
|
Makefile modification is minimal: if(APPLE AND CMAKE_SYSTEM_NAME STREQUAL "Darwin")
target_sources(butterscotch PRIVATE src/desktop/backends/platform/macos.m)
find_library(COCOA_LIBRARY Cocoa REQUIRED)
target_link_libraries(butterscotch PRIVATE ${COCOA_LIBRARY})
endif()As far as I can tell, GLFW and SDL1 do not have error popup functions. |
|
Honestly if glfw doesn't have a message box API then maybe it would be better to just phone it in and call printf and not have a popup then to deal with objc, unless there's some super clean way to do it I haven't thought of. Maybe we could just tell cmake to always pass |
That code block is cmake not make |
|
Isn't cmake used to build the project? I don't see what's particularly problematic about this implementation. I guess it's possible to do it in pure C using the objc runtime but I don't think it's necessary - it's not hard to compile objc and it only affects macOS. |
We also have a makefile build for legacy systems that can't run cmake easily, and stuff with weird incomplete C libraries. I guess we could add some of the incomplete C library stuff to cmake too but I haven't bothered. |
|
fwiw it does also print to console no matter what, the popup is just a nice bonus |
i guess but this wouldn't affect those systems surely |
|
The makefile build also supports modern stuff that uses the desktop backend. I use it over cmake because it lets me skip actually running cmake before I can run make. |
|
fair enough, let me see if i can update the makefile too |
|
I eventually wanna make the makefile build support all platforms. I don't want to drop macOS support from the makefile, especially when old macOS versions are a key use case for the makefile. |
|
Would adding this into the Makefile in the GLFW3, GLFW2 and SDL1 if statements not work? ifeq ($(OS),Darwin)
SRCS += src/desktop/backends/platform/macos.m
LIBS += -framework Cocoa
endifMakefile doesn't work for me and i cba figure out why but that should be fine surely |
|
okay i figured out why it wasn't working and now it's all fixed and it all works yay |
|
I don't want to duplicate the compile rule is the issue |
|
Can you just put it in a .c file and pass |
|
how about this? no duplicate compiler rule there |
|
the issue with just putting it as a C file is that then #import <Cocoa/Cocoa.h>
void show_error_box(const char *message)
{
@autoreleasepool {
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:@"Error"];
[alert setInformativeText:
[NSString stringWithUTF8String:message]];
[alert addButtonWithTitle:@"OK"];
[alert setAlertStyle:NSAlertStyleCritical];
[alert runModal];
}
}becomes #include <stdio.h>
#include <objc/objc.h>
#include <objc/runtime.h>
#include <objc/message.h>
static id send_id(id obj, SEL sel)
{
return ((id (*)(id, SEL))objc_msgSend)(obj, sel);
}
static id send_id_id(id obj, SEL sel, id arg)
{
return ((id (*)(id, SEL, id))objc_msgSend)(obj, sel, arg);
}
static void send_void_id(id obj, SEL sel, id arg)
{
((void (*)(id, SEL, id))objc_msgSend)(obj, sel, arg);
}
static void send_void_int(id obj, SEL sel, int arg)
{
((void (*)(id, SEL, int))objc_msgSend)(obj, sel, arg);
}
void show_error_box(const char *message)
{
Class NSAlert = (Class)objc_getClass("NSAlert");
Class NSString = (Class)objc_getClass("NSString");
id alert = send_id(send_id((id)NSAlert, sel_registerName("alloc")),
sel_registerName("init"));
id title = send_id_id((id)NSString,
sel_registerName("stringWithUTF8String:"),
(id)"Error");
id text = send_id_id((id)NSString,
sel_registerName("stringWithUTF8String:"),
(id)message);
send_void_id(alert, sel_registerName("setMessageText:"), title);
send_void_id(alert, sel_registerName("setInformativeText:"), text);
id ok = send_id_id((id)NSString,
sel_registerName("stringWithUTF8String:"),
(id)"OK");
send_id_id(alert, sel_registerName("addButtonWithTitle:"), ok);
send_void_int(alert, sel_registerName("setAlertStyle:"), 2);
send_id(alert, sel_registerName("runModal"));
}one of those is a lot more maintainable and readable |
|
Oh shit does that work? I spent way too fuckin long tryna figure out a way to do that when doing iOS. Good shit. |
|
🫡 |
Oh no my idea was literally having objc in a .c file, not writing actual C. If you pass |
|
oo right sorry i misunderstood lmao |
|
You should merge upstream cuz with the msvc 4.0 patch there was a minor change to the makefile and there might be a conflict. |
|
now the diff shows the changes from those commits. github being dumb. maybe rebase instead of merging |
#import <Cocoa/Cocoa.h>
void show_error_box(const char *message)
{
#if __has_feature(objc_arc)
@autoreleasepool {
#else
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
#endif
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:@"Error"];
[alert setInformativeText:[NSString stringWithUTF8String:message]];
[alert addButtonWithTitle:@"OK"];
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
[alert setAlertStyle:NSAlertStyleCritical];
#else
[alert setAlertStyle:NSCriticalAlertStyle];
#endif
[alert runModal];
#if !__has_feature(objc_arc)
[alert release];
#endif
#if __has_feature(objc_arc)
}
#else
[pool drain];
#endif
}This should work on OSX 10.3 and above. Before that, NSAlert doesn't exist. |
|
why have all those |
|
we could but if someone does compile it with arc then it'll fail 🤷 this is the most portable i could get it |
|
Also I think right now the code should in theory work on 10.0, though maybe not in practice since I think SDL 1.2 won't build. Maybe an older version of SDL 1.2 will build. Worst case just put the whole block behind a compile-time version check so it can build on 10.2 and below, though a runtime check would be nicer. |
|
i mean NSAlert doesn't exist on 10.0 so you'd have to use |
|
guess we could do #include <AvailabilityMacros.h>
#import <AppKit/AppKit.h>
void show_error_box(const char *message)
{
#if __has_feature(objc_arc)
@autoreleasepool {
#else
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
#endif
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1030
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:@"Error"];
[alert setInformativeText:[NSString stringWithUTF8String:message]];
[alert addButtonWithTitle:@"OK"];
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
[alert setAlertStyle:NSAlertStyleCritical];
#else
[alert setAlertStyle:NSCriticalAlertStyle];
#endif
[alert runModal];
#if !__has_feature(objc_arc)
[alert release];
#endif
#else
NSRunAlertPanel(
@"Error",
[NSString stringWithUTF8String:message],
@"OK",
nil,
nil
);
#endif
#if !__has_feature(objc_arc)
[pool drain];
#endif
#if __has_feature(objc_arc)
}
#endif
} |
|
I need to get out my powermac again because I can't remember if the old apple compilers on 10.2 even defined the modern version macros at all. I'll prolly do that tomorrow maybe, or later today if i cant sleep.
My idea was just to do nothing and fallback to printf on stuff that can't do NSAlert, but more portability is always welcome. |
|
there we go - full macOS 10 compatibility lol |
Adds in the debug show_error function. In GameMaker, this would open a popup window but due to this project needing to support multiple frontends, I've just made it print the error instead and optionally exit the game if the game marks it necessary.
When this is called:
show_error("Assertion failed: " + _msg, true);, the following is output:and the game quits.