src/video/cocoa/SDL_cocoametalview.m
author Sam Lantinga <slouken@libsdl.org>
Thu, 16 Jan 2020 20:49:25 -0800
changeset 13422 fd6a12de91c7
parent 13113 eab802de5fbb
permissions -rw-r--r--
Updated copyright date for 2020
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 /*
    22  * @author Mark Callow, www.edgewise-consulting.com.
    23  *
    24  * Thanks to Alex Szpakowski, @slime73 on GitHub, for his gist showing
    25  * how to add a CAMetalLayer backed view.
    26  */
    27 
    28 #import "SDL_cocoametalview.h"
    29 
    30 #if SDL_VIDEO_DRIVER_COCOA && (SDL_VIDEO_VULKAN || SDL_VIDEO_METAL)
    31 
    32 #include "SDL_assert.h"
    33 #include "SDL_events.h"
    34 
    35 static int SDLCALL
    36 SDL_MetalViewEventWatch(void *userdata, SDL_Event *event)
    37 {
    38     /* Update the drawable size when SDL receives a size changed event for
    39      * the window that contains the metal view. It would be nice to use
    40      * - (void)resizeWithOldSuperviewSize:(NSSize)oldSize and
    41      * - (void)viewDidChangeBackingProperties instead, but SDL's size change
    42      * events don't always happen in the same frame (for example when a
    43      * resizable window exits a fullscreen Space via the user pressing the OS
    44      * exit-space button). */
    45     if (event->type == SDL_WINDOWEVENT && event->window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
    46         @autoreleasepool {
    47             SDL_cocoametalview *view = (__bridge SDL_cocoametalview *)userdata;
    48             if (view.sdlWindowID == event->window.windowID) {
    49                 [view updateDrawableSize];
    50             }
    51         }
    52     }
    53     return 0;
    54 }
    55 
    56 @implementation SDL_cocoametalview
    57 
    58 /* Return a Metal-compatible layer. */
    59 + (Class)layerClass
    60 {
    61     return NSClassFromString(@"CAMetalLayer");
    62 }
    63 
    64 /* Indicate the view wants to draw using a backing layer instead of drawRect. */
    65 - (BOOL)wantsUpdateLayer
    66 {
    67     return YES;
    68 }
    69 
    70 /* When the wantsLayer property is set to YES, this method will be invoked to
    71  * return a layer instance.
    72  */
    73 - (CALayer*)makeBackingLayer
    74 {
    75     return [self.class.layerClass layer];
    76 }
    77 
    78 - (instancetype)initWithFrame:(NSRect)frame
    79                       highDPI:(BOOL)highDPI
    80                      windowID:(Uint32)windowID;
    81 {
    82     if ((self = [super initWithFrame:frame])) {
    83         self.highDPI = highDPI;
    84         self.sdlWindowID = windowID;
    85         self.wantsLayer = YES;
    86 
    87         /* Allow resize. */
    88         self.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
    89 
    90         SDL_AddEventWatch(SDL_MetalViewEventWatch, self);
    91 
    92         [self updateDrawableSize];
    93     }
    94   
    95     return self;
    96 }
    97 
    98 - (void)dealloc
    99 {
   100     SDL_DelEventWatch(SDL_MetalViewEventWatch, self);
   101     [super dealloc];
   102 }
   103 
   104 - (NSInteger)tag
   105 {
   106     return METALVIEW_TAG;
   107 }
   108 
   109 - (void)updateDrawableSize
   110 {
   111     CAMetalLayer *metalLayer = (CAMetalLayer *)self.layer;
   112     NSSize size = self.bounds.size;
   113     NSSize backingSize = size;
   114 
   115     if (self.highDPI) {
   116         /* Note: NSHighResolutionCapable must be set to true in the app's
   117          * Info.plist in order for the backing size to be high res.
   118          */
   119         backingSize = [self convertSizeToBacking:size];
   120     }
   121 
   122     metalLayer.contentsScale = backingSize.height / size.height;
   123     metalLayer.drawableSize = NSSizeToCGSize(backingSize);
   124 }
   125 
   126 @end
   127 
   128 SDL_MetalView
   129 Cocoa_Metal_CreateView(_THIS, SDL_Window * window)
   130 { @autoreleasepool {
   131     SDL_WindowData* data = (__bridge SDL_WindowData *)window->driverdata;
   132     NSView *view = data->nswindow.contentView;
   133     BOOL highDPI = (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) != 0;
   134     Uint32 windowID = SDL_GetWindowID(window);
   135     SDL_cocoametalview *newview;
   136     SDL_MetalView metalview;
   137 
   138     newview = [[SDL_cocoametalview alloc] initWithFrame:view.frame
   139                                                 highDPI:highDPI
   140                                                 windowID:windowID];
   141     if (newview == nil) {
   142         return NULL;
   143     }
   144 
   145     [view addSubview:newview];
   146 
   147     metalview = (SDL_MetalView)CFBridgingRetain(newview);
   148     [newview release];
   149 
   150     return metalview;
   151 }}
   152 
   153 void
   154 Cocoa_Metal_DestroyView(_THIS, SDL_MetalView view)
   155 { @autoreleasepool {
   156     SDL_cocoametalview *metalview = CFBridgingRelease(view);
   157     [metalview removeFromSuperview];
   158 }}
   159 
   160 void
   161 Cocoa_Metal_GetDrawableSize(SDL_Window * window, int * w, int * h)
   162 { @autoreleasepool {
   163     SDL_WindowData *data = (__bridge SDL_WindowData *)window->driverdata;
   164     NSView *view = data->nswindow.contentView;
   165     SDL_cocoametalview* metalview = [view viewWithTag:METALVIEW_TAG];
   166     if (metalview) {
   167         CAMetalLayer *layer = (CAMetalLayer*)metalview.layer;
   168         SDL_assert(layer != NULL);
   169         if (w) {
   170             *w = layer.drawableSize.width;
   171         }
   172         if (h) {
   173             *h = layer.drawableSize.height;
   174         }
   175     } else {
   176         SDL_GetWindowSize(window, w, h);
   177     }
   178 }}
   179 
   180 #endif /* SDL_VIDEO_DRIVER_COCOA && (SDL_VIDEO_VULKAN || SDL_VIDEO_METAL) */
   181 
   182 /* vi: set ts=4 sw=4 expandtab: */