334 lines
11 KiB
Objective-C
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
|
|
|