Hello,
I’ve been trying in vain to integrate an SDL renderer into an existing Cocoa window. I’m struggling to find information in the SDL3 documentation.
The programme runs without a hitch but displays two windows. This makes sense, as the programme is supposed to create two, as the code below shows. However, it must also embed one within the other.
#include "SDL3/SDL_error.h"
#include "SDL3/SDL_events.h"
#include "SDL3/SDL_init.h"
#include "SDL3/SDL_log.h"
#include "SDL3/SDL_properties.h"
#include "SDL3/SDL_render.h"
#include "SDL3/SDL_video.h"
#import <Cocoa/Cocoa.h>
#import <CoreGraphics/CoreGraphics.h>
#include <Foundation/Foundation.h>
#include <cstddef>
#include <cstdlib>
#include <SDL3/SDL.h>
extern "C" void sdl_render_frame(SDL_Renderer *renderer, void *user);
static bool g_running = false;
static SDL_Window *g_sdlWindow = nullptr;
static SDL_Renderer *g_sdlRenderer = nullptr;
@interface BridgeView : NSView
@property(nonatomic, assign) NSTimer *sdlTimer;
@end
@implementation BridgeView
- (void)dealloc {
if (_sdlTimer) {
[_sdlTimer invalidate];
_sdlTimer = nil;
}
[super dealloc];
}
- (void)viewDidMoveToWindow {
[super viewDidMoveToWindow];
if (!self.sdlTimer) {
self.sdlTimer = [NSTimer scheduledTimerWithTimeInterval:(1.0 / 120.0)
target:self
selector:@selector(sdlTick:)
userInfo:nil
repeats:YES];
}
}
- (void)sdlTick:(NSTimer *)t {
SDL_Event ev;
while (SDL_PollEvent(&ev)) {
if (ev.type == SDL_EVENT_QUIT) {
g_running = false;
[NSApp terminate:nil];
return;
}
}
if (g_sdlRenderer) {
SDL_SetRenderDrawColor(g_sdlRenderer, 50, 50, 50, 255);
SDL_RenderClear(g_sdlRenderer);
extern void sdl_render_frame(SDL_Renderer * renderer, void *user);
sdl_render_frame(g_sdlRenderer, NULL);
SDL_RenderPresent(g_sdlRenderer);
}
}
@end
static SDL_PropertiesID make_propes_for_ns_view(void *nsViewPtr, int w, int h,
const char *title) {
SDL_PropertiesID props = SDL_CreateProperties();
if (!props)
return 0;
SDL_SetPointerProperty(props, SDL_PROP_WINDOW_COCOA_WINDOW_POINTER,
nsViewPtr);
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, w);
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, h);
if (title)
SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, title);
return props;
}
#pragma mark - Public API
extern "C" int app_bridge_init_and_run(int width, int height,
const char *title) {
@autoreleasepool {
[NSApplication sharedApplication];
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
NSRect frame = NSMakeRect(0, 0, width, height);
NSUInteger style = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
NSWindowStyleMaskResizable |
NSWindowStyleMaskMiniaturizable;
NSWindow *win = [[NSWindow alloc] initWithContentRect:frame
styleMask:style
backing:NSBackingStoreBuffered
defer:NO];
if (title)
[win setTitle:[NSString stringWithUTF8String:title]];
[win center];
NSRect contentFrame = NSMakeRect(0, 0, width, height);
BridgeView *view = [[BridgeView alloc] initWithFrame:contentFrame];
view.wantsLayer = YES;
view.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
NSView *content = [win contentView];
[content addSubview:view];
[win makeKeyAndOrderFront:nil];
[NSApp activateIgnoringOtherApps:YES];
if (SDL_Init(SDL_INIT_EVENTS | SDL_INIT_VIDEO) != 1) {
NSLog(@"SDL_Init failed: %s", SDL_GetError());
return EXIT_FAILURE;
}
void *nativeViewPtr = (__bridge void *)view;
SDL_PropertiesID props =
make_propes_for_ns_view(nativeViewPtr, width, height, title);
if (!props) {
NSLog(@"Failed to create SDL props");
SDL_Quit();
return EXIT_FAILURE;
}
g_sdlWindow = SDL_CreateWindowWithProperties(props);
SDL_DestroyProperties(props);
if (!g_sdlWindow) {
NSLog(@"SDL_CreateWindowWithProperties failed: %s", SDL_GetError());
SDL_Quit();
return EXIT_FAILURE;
}
g_sdlRenderer = SDL_CreateRenderer(g_sdlWindow, title);
[NSApp run];
if (g_sdlRenderer) {
SDL_DestroyRenderer(g_sdlRenderer);
g_sdlRenderer = NULL;
}
if (g_sdlWindow) {
SDL_DestroyWindow(g_sdlWindow);
g_sdlWindow = NULL;
}
SDL_Quit();
return EXIT_SUCCESS;
}
}
extern "C" void request_redraw() {
@autoreleasepool {
NSWindow *win = [NSApp mainWindow];
if (!win)
return;
NSView *v = [win contentView];
if (v)
[v setNeedsDisplay:YES];
}
}
Could you help me?