minifb-zig-port/src/macosx/OSXWindowFrameView.m
2020-04-26 19:50:53 +02:00

334 lines
11 KiB
Objective-C

#import "OSXWindowFrameView.h"
#import "OSXWindow.h"
#include "WindowData_OSX.h"
#include <MiniFB_internal.h>
#if defined(USE_METAL_API)
#import <MetalKit/MetalKit.h>
extern id<MTLDevice> g_metal_device;
extern id<MTLLibrary> g_library;
@implementation WindowViewController
- (void) mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size {
(void)view;
(void)size;
// resize
}
- (void) drawInMTKView:(nonnull MTKView *)view {
OSXWindow *window = (OSXWindow *) view.window;
SWindowData *window_data = window->window_data;
if(window_data == 0x0) {
return;
}
// Wait to ensure only MaxBuffersInFlight number of frames are getting proccessed
// by any stage in the Metal pipeline (App, Metal, Drivers, GPU, etc)
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// Iterate through our Metal buffers, and cycle back to the first when we've written to MaxBuffersInFlight
current_buffer = (current_buffer + 1) % MaxBuffersInFlight;
// Calculate the number of bytes per row of our image.
MTLRegion region = { { 0, 0, 0 }, { window_data->buffer_width, window_data->buffer_height, 1 } };
// Copy the bytes from our data object into the texture
[texture_buffers[current_buffer] replaceRegion:region mipmapLevel:0 withBytes:window_data->draw_buffer bytesPerRow:window_data->buffer_stride];
// Create a new command buffer for each render pass to the current drawable
SWindowData_OSX *window_data_osx = (SWindowData_OSX *) window->window_data->specific;
id<MTLCommandBuffer> commandBuffer = [window_data_osx->metal.command_queue commandBuffer];
commandBuffer.label = @"minifb_command_buffer";
// Add completion hander which signals _inFlightSemaphore when Metal and the GPU has fully
// finished processing the commands we're encoding this frame. This indicates when the
// dynamic buffers filled with our vertices, that we're writing to this frame, will no longer
// be needed by Metal and the GPU, meaning we can overwrite the buffer contents without
// corrupting the rendering.
__block dispatch_semaphore_t block_sema = semaphore;
[commandBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer)
{
(void)buffer;
dispatch_semaphore_signal(block_sema);
}];
MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor;
if (renderPassDescriptor != nil) {
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0);
// Create a render command encoder so we can render into something
id<MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
renderEncoder.label = @"minifb_command_encoder";
// Set render command encoder state
OSXWindow *window = (OSXWindow *) view.window;
SWindowData_OSX *window_data_osx = (SWindowData_OSX *) window->window_data->specific;
[renderEncoder setRenderPipelineState:window_data_osx->metal.pipeline_state];
[renderEncoder setVertexBytes:window_data_osx->metal.vertices length:sizeof(window_data_osx->metal.vertices) atIndex:0];
[renderEncoder setFragmentTexture:texture_buffers[current_buffer] atIndex:0];
// Draw the vertices of our quads
[renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];
// We're done encoding commands
[renderEncoder endEncoding];
// Schedule a present once the framebuffer is complete using the current drawable
[commandBuffer presentDrawable:view.currentDrawable];
}
// Finalize rendering here & push the command buffer to the GPU
[commandBuffer commit];
}
@end
#endif
@implementation OSXWindowFrameView
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#if defined(USE_METAL_API)
- (void)updateTrackingAreas
{
if(tracking_area != nil) {
[self removeTrackingArea:tracking_area];
[tracking_area release];
}
int opts = (NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways);
tracking_area = [[NSTrackingArea alloc] initWithRect:[self bounds]
options:opts
owner:self
userInfo:nil];
[self addTrackingArea:tracking_area];
}
#else
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (NSRect)resizeRect
{
const CGFloat resizeBoxSize = 16.0;
const CGFloat contentViewPadding = 5.5;
NSRect contentViewRect = [[self window] contentRectForFrameRect:[[self window] frame]];
NSRect resizeRect = NSMakeRect(
NSMaxX(contentViewRect) + contentViewPadding,
NSMinY(contentViewRect) - resizeBoxSize - contentViewPadding,
resizeBoxSize,
resizeBoxSize
);
return resizeRect;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)drawRect:(NSRect)rect
{
(void)rect;
if(window_data == 0x0)
return;
SWindowData_OSX *window_data_osx = (SWindowData_OSX *) window_data->specific;
if (!window_data_osx || !window_data_osx->window || !window_data->draw_buffer)
return;
CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
CGDataProviderRef provider = CGDataProviderCreateWithData(0x0, window_data->draw_buffer, window_data->buffer_width * window_data->buffer_height * 4, 0x0);
CGImageRef img = CGImageCreate(window_data->buffer_width, window_data->buffer_height, 8, 32, window_data->buffer_width * 4, space, kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little,
provider, 0x0, false, kCGRenderingIntentDefault);
const CGFloat components[] = {0.0f, 0.0f, 0.0f, 1.0f};
const CGColorRef black = CGColorCreate(space, components);
CGColorSpaceRelease(space);
CGDataProviderRelease(provider);
if(window_data->dst_offset_x != 0 || window_data->dst_offset_y != 0 || window_data->dst_width != window_data->window_width || window_data->dst_height != window_data->window_height) {
CGContextSetFillColorWithColor(context, black);
CGContextFillRect(context, CGRectMake(0, 0, window_data->window_width, window_data->window_height));
}
CGContextDrawImage(context, CGRectMake(window_data->dst_offset_x, window_data->dst_offset_y, window_data->dst_width, window_data->dst_height), img);
CGImageRelease(img);
}
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (BOOL)acceptsFirstMouse:(NSEvent *)event
{
(void)event;
return YES;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)mouseDown:(NSEvent*)event
{
(void)event;
if(window_data != 0x0) {
kCall(mouse_btn_func, MOUSE_BTN_1, window_data->mod_keys, true);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)mouseUp:(NSEvent*)event
{
(void)event;
if(window_data != 0x0) {
kCall(mouse_btn_func, MOUSE_BTN_1, window_data->mod_keys, false);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)rightMouseDown:(NSEvent*)event
{
(void)event;
if(window_data != 0x0) {
kCall(mouse_btn_func, MOUSE_BTN_2, window_data->mod_keys, true);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)rightMouseUp:(NSEvent*)event
{
(void)event;
if(window_data != 0x0) {
kCall(mouse_btn_func, MOUSE_BTN_1, window_data->mod_keys, false);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)otherMouseDown:(NSEvent *)event
{
(void)event;
if(window_data != 0x0) {
kCall(mouse_btn_func, [event buttonNumber], window_data->mod_keys, true);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)otherMouseUp:(NSEvent *)event
{
(void)event;
if(window_data != 0x0) {
kCall(mouse_btn_func, [event buttonNumber], window_data->mod_keys, false);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)scrollWheel:(NSEvent *)event
{
if(window_data != 0x0) {
kCall(mouse_wheel_func, window_data->mod_keys, [event deltaX], [event deltaY]);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)mouseDragged:(NSEvent *)event
{
[self mouseMoved:event];
}
- (void)rightMouseDragged:(NSEvent *)event
{
[self mouseMoved:event];
}
- (void)otherMouseDragged:(NSEvent *)event
{
[self mouseMoved:event];
}
- (void)mouseMoved:(NSEvent *)event
{
if(window_data != 0x0) {
NSPoint point = [event locationInWindow];
//NSPoint localPoint = [self convertPoint:point fromView:nil];
window_data->mouse_pos_x = point.x;
window_data->mouse_pos_y = point.y;
kCall(mouse_move_func, point.x, point.y);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)mouseExited:(NSEvent *)event
{
(void)event;
//printf("mouse exit\n");
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)mouseEntered:(NSEvent *)event
{
(void)event;
//printf("mouse enter\n");
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (BOOL)canBecomeKeyView
{
return YES;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (NSView *)nextValidKeyView
{
return self;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (NSView *)previousValidKeyView
{
return self;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (BOOL)acceptsFirstResponder
{
return YES;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)viewDidMoveToWindow
{
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
@end