1/* 2 Copyright 2019-2020 David Robillard <d@drobilla.net> 3 4 Permission to use, copy, modify, and/or distribute this software for any 5 purpose with or without fee is hereby granted, provided that the above 6 copyright notice and this permission notice appear in all copies. 7 8 THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15*/ 16 17#include "implementation.h" 18#include "mac.h" 19#include "stub.h" 20 21#include "pugl/gl.h" 22 23#ifndef __MAC_10_10 24# define NSOpenGLProfileVersion4_1Core NSOpenGLProfileVersion3_2Core 25#endif 26 27@interface PuglOpenGLView : NSOpenGLView 28@end 29 30@implementation PuglOpenGLView { 31@public 32 PuglView* puglview; 33} 34 35- (id)initWithFrame:(NSRect)frame 36{ 37 const bool compat = puglview->hints[PUGL_USE_COMPAT_PROFILE]; 38 const unsigned samples = (unsigned)puglview->hints[PUGL_SAMPLES]; 39 const int major = puglview->hints[PUGL_CONTEXT_VERSION_MAJOR]; 40 const unsigned profile = 41 ((compat || major < 3) ? NSOpenGLProfileVersionLegacy 42 : (major >= 4 ? NSOpenGLProfileVersion4_1Core 43 : NSOpenGLProfileVersion3_2Core)); 44 45 // Set attributes to default if they are unset 46 // (There is no GLX_DONT_CARE equivalent on MacOS) 47 if (puglview->hints[PUGL_DEPTH_BITS] == PUGL_DONT_CARE) { 48 puglview->hints[PUGL_DEPTH_BITS] = 0; 49 } 50 if (puglview->hints[PUGL_STENCIL_BITS] == PUGL_DONT_CARE) { 51 puglview->hints[PUGL_STENCIL_BITS] = 0; 52 } 53 if (puglview->hints[PUGL_SAMPLES] == PUGL_DONT_CARE) { 54 puglview->hints[PUGL_SAMPLES] = 1; 55 } 56 if (puglview->hints[PUGL_DOUBLE_BUFFER] == PUGL_DONT_CARE) { 57 puglview->hints[PUGL_DOUBLE_BUFFER] = 1; 58 } 59 if (puglview->hints[PUGL_SWAP_INTERVAL] == PUGL_DONT_CARE) { 60 puglview->hints[PUGL_SWAP_INTERVAL] = 1; 61 } 62 63 const unsigned colorSize = (unsigned)(puglview->hints[PUGL_RED_BITS] + 64 puglview->hints[PUGL_BLUE_BITS] + 65 puglview->hints[PUGL_GREEN_BITS] + 66 puglview->hints[PUGL_ALPHA_BITS]); 67 68 // clang-format off 69 NSOpenGLPixelFormatAttribute pixelAttribs[17] = { 70 NSOpenGLPFADoubleBuffer, 71 NSOpenGLPFAAccelerated, 72 NSOpenGLPFAOpenGLProfile, profile, 73 NSOpenGLPFAColorSize, colorSize, 74 NSOpenGLPFADepthSize, (unsigned)puglview->hints[PUGL_DEPTH_BITS], 75 NSOpenGLPFAStencilSize, (unsigned)puglview->hints[PUGL_STENCIL_BITS], 76 NSOpenGLPFAMultisample, samples ? 1 : 0, 77 NSOpenGLPFASampleBuffers, samples ? 1 : 0, 78 NSOpenGLPFASamples, samples, 79 0}; 80 // clang-format on 81 82 NSOpenGLPixelFormat* pixelFormat = 83 [[NSOpenGLPixelFormat alloc] initWithAttributes:pixelAttribs]; 84 85 if (pixelFormat) { 86 self = [super initWithFrame:frame pixelFormat:pixelFormat]; 87 [pixelFormat release]; 88 } else { 89 self = [super initWithFrame:frame]; 90 } 91 92 [self setWantsBestResolutionOpenGLSurface:YES]; 93 94 if (self) { 95 [[self openGLContext] makeCurrentContext]; 96 [self reshape]; 97 } 98 return self; 99} 100 101- (void)reshape 102{ 103 PuglWrapperView* wrapper = (PuglWrapperView*)[self superview]; 104 105 [super reshape]; 106 [wrapper setReshaped]; 107} 108 109- (void)drawRect:(NSRect)rect 110{ 111 PuglWrapperView* wrapper = (PuglWrapperView*)[self superview]; 112 [wrapper dispatchExpose:rect]; 113} 114 115@end 116 117static PuglStatus 118puglMacGlCreate(PuglView* view) 119{ 120 PuglInternals* impl = view->impl; 121 PuglOpenGLView* drawView = [PuglOpenGLView alloc]; 122 123 drawView->puglview = view; 124 [drawView initWithFrame:[impl->wrapperView bounds]]; 125 if (view->hints[PUGL_RESIZABLE]) { 126 [drawView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; 127 } else { 128 [drawView setAutoresizingMask:NSViewNotSizable]; 129 } 130 131 impl->drawView = drawView; 132 return PUGL_SUCCESS; 133} 134 135static PuglStatus 136puglMacGlDestroy(PuglView* view) 137{ 138 PuglOpenGLView* const drawView = (PuglOpenGLView*)view->impl->drawView; 139 140 [drawView removeFromSuperview]; 141 [drawView release]; 142 143 view->impl->drawView = nil; 144 return PUGL_SUCCESS; 145} 146 147static PuglStatus 148puglMacGlEnter(PuglView* view, const PuglEventExpose* PUGL_UNUSED(expose)) 149{ 150 PuglOpenGLView* const drawView = (PuglOpenGLView*)view->impl->drawView; 151 152 [[drawView openGLContext] makeCurrentContext]; 153 return PUGL_SUCCESS; 154} 155 156static PuglStatus 157puglMacGlLeave(PuglView* view, const PuglEventExpose* expose) 158{ 159 PuglOpenGLView* const drawView = (PuglOpenGLView*)view->impl->drawView; 160 161 if (expose) { 162 [[drawView openGLContext] flushBuffer]; 163 } 164 165 [NSOpenGLContext clearCurrentContext]; 166 167 return PUGL_SUCCESS; 168} 169 170PuglGlFunc 171puglGetProcAddress(const char* name) 172{ 173 CFBundleRef framework = 174 CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); 175 176 CFStringRef symbol = CFStringCreateWithCString( 177 kCFAllocatorDefault, name, kCFStringEncodingASCII); 178 179 PuglGlFunc func = 180 (PuglGlFunc)CFBundleGetFunctionPointerForName(framework, symbol); 181 182 CFRelease(symbol); 183 184 return func; 185} 186 187PuglStatus 188puglEnterContext(PuglView* view) 189{ 190 return view->backend->enter(view, NULL); 191} 192 193PuglStatus 194puglLeaveContext(PuglView* view) 195{ 196 return view->backend->leave(view, NULL); 197} 198 199const PuglBackend* 200puglGlBackend(void) 201{ 202 static const PuglBackend backend = {puglStubConfigure, 203 puglMacGlCreate, 204 puglMacGlDestroy, 205 puglMacGlEnter, 206 puglMacGlLeave, 207 puglStubGetContext}; 208 209 return &backend; 210} 211