boo/lib/mac/WindowCocoa.mm

1567 lines
47 KiB
Plaintext
Raw Normal View History

2015-11-01 00:06:56 +00:00
#include "boo/graphicsdev/GL.hpp"
#include "boo/graphicsdev/glew.h"
#include "boo/graphicsdev/Metal.hpp"
2015-11-09 02:24:45 +00:00
#include "CocoaCommon.hpp"
#import <AppKit/AppKit.h>
2015-11-01 00:06:56 +00:00
#import <CoreVideo/CVDisplayLink.h>
2015-09-02 19:09:13 +00:00
#include "boo/IApplication.hpp"
#include "boo/IWindow.hpp"
#include "boo/IGraphicsContext.hpp"
2015-11-01 00:06:56 +00:00
#include <LogVisor/LogVisor.hpp>
2015-12-27 04:20:07 +00:00
#if !__has_feature(objc_arc)
#error ARC Required
#endif
namespace boo {class WindowCocoa; class GraphicsContextCocoa;}
2015-09-02 19:09:13 +00:00
@interface WindowCocoaInternal : NSWindow
{
2015-09-02 19:09:13 +00:00
boo::WindowCocoa* booWindow;
}
2015-09-02 19:09:13 +00:00
- (id)initWithBooWindow:(boo::WindowCocoa*)bw title:(const std::string&)title;
2015-05-06 00:50:57 +00:00
- (void)setFrameDefault;
- (NSRect)genFrameDefault;
@end
2016-02-24 21:07:48 +00:00
2015-09-02 19:09:13 +00:00
/* AppKit applies OpenGL much differently than other platforms
* the NSOpenGLView class composes together all necessary
* OGL context members and provides the necessary event hooks
* for KB/Mouse/Touch events
*/
static const NSOpenGLPixelFormatAttribute PF_RGBA8_ATTRS[] =
{
NSOpenGLPFAAccelerated,
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAColorSize, 24,
NSOpenGLPFAAlphaSize, 8,
2016-01-16 06:11:25 +00:00
0, 0
2015-09-02 19:09:13 +00:00
};
2015-09-02 19:09:13 +00:00
static const NSOpenGLPixelFormatAttribute PF_RGBA8_Z24_ATTRS[] =
{
NSOpenGLPFAAccelerated,
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAColorSize, 24,
NSOpenGLPFAAlphaSize, 8,
NSOpenGLPFADepthSize, 24,
2016-01-16 06:11:25 +00:00
0, 0
2015-09-02 19:09:13 +00:00
};
static const NSOpenGLPixelFormatAttribute PF_RGBAF32_ATTRS[] =
{
NSOpenGLPFAAccelerated,
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAColorFloat,
NSOpenGLPFAColorSize, 96,
NSOpenGLPFAAlphaSize, 32,
2016-01-16 06:11:25 +00:00
0, 0
2015-09-02 19:09:13 +00:00
};
static const NSOpenGLPixelFormatAttribute PF_RGBAF32_Z24_ATTRS[] =
{
NSOpenGLPFAAccelerated,
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAColorFloat,
NSOpenGLPFAColorSize, 96,
NSOpenGLPFAAlphaSize, 32,
NSOpenGLPFADepthSize, 24,
2016-01-16 06:11:25 +00:00
0, 0
2015-09-02 19:09:13 +00:00
};
static const NSOpenGLPixelFormatAttribute* PF_TABLE[] =
{
NULL,
PF_RGBA8_ATTRS,
PF_RGBA8_Z24_ATTRS,
PF_RGBAF32_ATTRS,
PF_RGBAF32_Z24_ATTRS
};
2015-12-27 04:20:07 +00:00
@interface BooCocoaResponder : NSResponder <NSTextInputClient>
{
@public
NSUInteger lastModifiers;
boo::GraphicsContextCocoa* booContext;
NSView* parentView;
NSTextInputContext* textContext;
}
- (id)initWithBooContext:(boo::GraphicsContextCocoa*)bctx View:(NSView*)view;
@end
namespace boo
{
2016-02-24 21:07:48 +00:00
class GraphicsContextCocoa : public IGraphicsContext
{
protected:
EGraphicsAPI m_api;
EPixelFormat m_pf;
IWindow* m_parentWindow;
CVDisplayLinkRef m_dispLink = nullptr;
2016-02-24 21:07:48 +00:00
GraphicsContextCocoa(EGraphicsAPI api, EPixelFormat pf, IWindow* parentWindow)
: m_api(api), m_pf(pf), m_parentWindow(parentWindow) {}
2016-02-24 21:07:48 +00:00
std::mutex m_dlmt;
std::condition_variable m_dlcv;
2016-02-24 21:07:48 +00:00
2015-11-16 22:03:46 +00:00
static CVReturn DLCallback(CVDisplayLinkRef displayLink,
const CVTimeStamp * inNow,
const CVTimeStamp * inOutputTime,
CVOptionFlags flagsIn,
2015-11-16 22:03:46 +00:00
CVOptionFlags * flagsOut,
GraphicsContextCocoa* ctx)
{
ctx->m_dlcv.notify_one();
return kCVReturnSuccess;
}
2016-02-24 21:07:48 +00:00
2015-12-27 04:20:07 +00:00
public:
~GraphicsContextCocoa()
{
if (m_dispLink)
{
CVDisplayLinkStop(m_dispLink);
CVDisplayLinkRelease(m_dispLink);
}
}
2016-02-24 21:07:48 +00:00
IWindowCallback* m_callback = nullptr;
void waitForRetrace()
{
std::unique_lock<std::mutex> lk(m_dlmt);
m_dlcv.wait(lk);
}
2015-12-27 04:20:07 +00:00
virtual BooCocoaResponder* responder() const=0;
};
class GraphicsContextCocoaGL;
class GraphicsContextCocoaMetal;
}
@interface GraphicsContextCocoaGLInternal : NSOpenGLView
{
2015-12-27 04:20:07 +00:00
@public
BooCocoaResponder* resp;
}
- (id)initWithBooContext:(boo::GraphicsContextCocoaGL*)bctx;
@end
@interface GraphicsContextCocoaMetalInternal : NSView
{
2015-12-27 04:20:07 +00:00
@public
BooCocoaResponder* resp;
2015-11-09 02:24:45 +00:00
boo::MetalContext* m_ctx;
2015-12-09 22:23:22 +00:00
boo::IWindow* m_window;
}
- (id)initWithBooContext:(boo::GraphicsContextCocoaMetal*)bctx;
2015-12-09 22:23:22 +00:00
- (void)reshapeHandler;
2015-09-02 19:09:13 +00:00
@end
2016-02-24 21:07:48 +00:00
namespace boo
{
2016-03-08 21:18:38 +00:00
static logvisor::Module Log("boo::WindowCocoa");
IGraphicsCommandQueue* _NewGLCommandQueue(IGraphicsContext* parent);
2015-11-09 02:24:45 +00:00
IGraphicsCommandQueue* _NewMetalCommandQueue(MetalContext* ctx, IWindow* parentWindow,
IGraphicsContext* parent);
2015-11-01 00:06:56 +00:00
void _CocoaUpdateLastGLCtx(NSOpenGLContext* lastGLCtx);
2016-02-24 21:07:48 +00:00
class GraphicsContextCocoaGL : public GraphicsContextCocoa
2015-09-02 19:09:13 +00:00
{
GraphicsContextCocoaGLInternal* m_nsContext = nullptr;
2016-02-24 21:07:48 +00:00
2015-11-01 00:06:56 +00:00
IGraphicsCommandQueue* m_commandQueue = nullptr;
IGraphicsDataFactory* m_dataFactory = nullptr;
2015-11-17 20:33:12 +00:00
NSOpenGLContext* m_mainCtx = nullptr;
2015-11-01 00:06:56 +00:00
NSOpenGLContext* m_loadCtx = nullptr;
2016-02-24 21:07:48 +00:00
2015-09-02 19:09:13 +00:00
public:
2015-11-01 00:06:56 +00:00
NSOpenGLContext* m_lastCtx = nullptr;
2016-02-24 21:07:48 +00:00
GraphicsContextCocoaGL(EGraphicsAPI api, IWindow* parentWindow,
NSOpenGLContext* lastGLCtx, uint32_t sampleCount)
2015-11-21 02:16:15 +00:00
: GraphicsContextCocoa(api, EPixelFormat::RGBA8, parentWindow),
m_lastCtx(lastGLCtx)
{
2016-02-24 21:07:48 +00:00
m_dataFactory = new GLDataFactory(this, sampleCount);
}
2016-02-24 21:07:48 +00:00
~GraphicsContextCocoaGL()
2015-09-02 19:09:13 +00:00
{
2015-11-01 00:06:56 +00:00
delete m_dataFactory;
delete m_commandQueue;
2015-09-02 19:09:13 +00:00
}
2016-02-24 21:07:48 +00:00
2015-09-02 19:09:13 +00:00
void _setCallback(IWindowCallback* cb)
{
m_callback = cb;
}
2016-02-24 21:07:48 +00:00
2015-09-02 19:09:13 +00:00
EGraphicsAPI getAPI() const
{
return m_api;
}
2016-02-24 21:07:48 +00:00
2015-09-02 19:09:13 +00:00
EPixelFormat getPixelFormat() const
{
return m_pf;
}
2016-02-24 21:07:48 +00:00
2015-09-02 19:09:13 +00:00
void setPixelFormat(EPixelFormat pf)
{
2015-11-21 02:16:15 +00:00
if (pf > EPixelFormat::RGBAF32_Z24)
2015-09-02 19:09:13 +00:00
return;
m_pf = pf;
}
2016-02-24 21:07:48 +00:00
2016-07-01 02:30:29 +00:00
void initializeContext(void*)
2015-09-02 19:09:13 +00:00
{
m_nsContext = [[GraphicsContextCocoaGLInternal alloc] initWithBooContext:this];
2015-11-01 00:06:56 +00:00
if (!m_nsContext)
2016-03-08 21:18:38 +00:00
Log.report(logvisor::Fatal, "unable to make new NSOpenGLView");
2015-12-27 04:20:07 +00:00
[(__bridge NSWindow*)(void*)m_parentWindow->getPlatformHandle() setContentView:m_nsContext];
2015-11-01 00:06:56 +00:00
CVDisplayLinkCreateWithActiveCGDisplays(&m_dispLink);
CVDisplayLinkSetOutputCallback(m_dispLink, (CVDisplayLinkOutputCallback)DLCallback, this);
CVDisplayLinkStart(m_dispLink);
m_commandQueue = _NewGLCommandQueue(this);
2015-09-02 19:09:13 +00:00
}
2016-02-24 21:07:48 +00:00
2015-11-01 00:06:56 +00:00
void makeCurrent()
2015-09-02 19:09:13 +00:00
{
2015-11-01 00:06:56 +00:00
[[m_nsContext openGLContext] makeCurrentContext];
2015-09-02 19:09:13 +00:00
}
2016-02-24 21:07:48 +00:00
2015-11-01 00:06:56 +00:00
void postInit()
{
}
2016-02-24 21:07:48 +00:00
2015-11-01 00:06:56 +00:00
IGraphicsCommandQueue* getCommandQueue()
2015-09-02 19:09:13 +00:00
{
2015-11-01 00:06:56 +00:00
return m_commandQueue;
2015-09-02 19:09:13 +00:00
}
2016-02-24 21:07:48 +00:00
2015-11-01 00:06:56 +00:00
IGraphicsDataFactory* getDataFactory()
2015-09-02 19:09:13 +00:00
{
2015-11-01 00:06:56 +00:00
return m_dataFactory;
2015-09-02 19:09:13 +00:00
}
2016-02-24 21:07:48 +00:00
2015-11-17 20:33:12 +00:00
IGraphicsDataFactory* getMainContextDataFactory()
{
if (!m_mainCtx)
{
2015-11-21 02:16:15 +00:00
NSOpenGLPixelFormat* nspf = [[NSOpenGLPixelFormat alloc] initWithAttributes:PF_TABLE[int(m_pf)]];
2015-11-17 20:33:12 +00:00
m_mainCtx = [[NSOpenGLContext alloc] initWithFormat:nspf shareContext:[m_nsContext openGLContext]];
if (!m_mainCtx)
2016-03-08 21:18:38 +00:00
Log.report(logvisor::Fatal, "unable to make main NSOpenGLContext");
2015-11-17 20:33:12 +00:00
}
2015-11-18 23:55:25 +00:00
[m_mainCtx makeCurrentContext];
2015-11-17 20:33:12 +00:00
return m_dataFactory;
}
2016-02-24 21:07:48 +00:00
2015-11-01 00:06:56 +00:00
IGraphicsDataFactory* getLoadContextDataFactory()
{
if (!m_loadCtx)
{
2015-11-21 02:16:15 +00:00
NSOpenGLPixelFormat* nspf = [[NSOpenGLPixelFormat alloc] initWithAttributes:PF_TABLE[int(m_pf)]];
2015-11-01 00:06:56 +00:00
m_loadCtx = [[NSOpenGLContext alloc] initWithFormat:nspf shareContext:[m_nsContext openGLContext]];
if (!m_loadCtx)
2016-03-08 21:18:38 +00:00
Log.report(logvisor::Fatal, "unable to make load NSOpenGLContext");
2015-11-01 00:06:56 +00:00
}
2015-11-18 23:55:25 +00:00
[m_loadCtx makeCurrentContext];
return m_dataFactory;
2015-11-01 00:06:56 +00:00
}
2016-02-24 21:07:48 +00:00
2015-11-01 00:06:56 +00:00
void present()
2015-09-02 19:09:13 +00:00
{
[[m_nsContext openGLContext] flushBuffer];
}
2016-02-24 21:07:48 +00:00
2015-12-27 04:20:07 +00:00
BooCocoaResponder* responder() const
{
if (!m_nsContext)
return nullptr;
return m_nsContext->resp;
}
2016-02-24 21:07:48 +00:00
2015-09-02 19:09:13 +00:00
};
IGraphicsContext* _GraphicsContextCocoaGLNew(IGraphicsContext::EGraphicsAPI api,
2016-02-24 21:07:48 +00:00
IWindow* parentWindow, NSOpenGLContext* lastGLCtx,
uint32_t sampleCount)
2015-09-02 19:09:13 +00:00
{
2015-11-21 02:16:15 +00:00
if (api != IGraphicsContext::EGraphicsAPI::OpenGL3_3 && api != IGraphicsContext::EGraphicsAPI::OpenGL4_2)
2015-09-02 19:09:13 +00:00
return NULL;
2016-02-24 21:07:48 +00:00
2015-09-02 19:09:13 +00:00
/* Create temporary context to query GL version */
NSOpenGLPixelFormat* nspf = [[NSOpenGLPixelFormat alloc] initWithAttributes:PF_RGBA8_ATTRS];
if (!nspf)
return NULL;
NSOpenGLContext* nsctx = [[NSOpenGLContext alloc] initWithFormat:nspf shareContext:nil];
if (!nsctx)
return NULL;
[nsctx makeCurrentContext];
const char* glVersion = (char*)glGetString(GL_VERSION);
unsigned major = 0;
unsigned minor = 0;
if (glVersion)
{
major = glVersion[0] - '0';
minor = glVersion[2] - '0';
}
[NSOpenGLContext clearCurrentContext];
if (!glVersion)
return NULL;
2016-02-24 21:07:48 +00:00
2015-09-02 19:09:13 +00:00
if (major > 4 || (major == 4 && minor >= 2))
2015-11-21 02:16:15 +00:00
api = IGraphicsContext::EGraphicsAPI::OpenGL4_2;
2015-09-02 19:09:13 +00:00
else if (major == 3 && minor >= 3)
2015-11-21 02:16:15 +00:00
if (api == IGraphicsContext::EGraphicsAPI::OpenGL4_2)
2015-09-02 19:09:13 +00:00
return NULL;
2016-02-24 21:07:48 +00:00
return new GraphicsContextCocoaGL(api, parentWindow, lastGLCtx, sampleCount);
2015-09-02 19:09:13 +00:00
}
2016-02-24 21:07:48 +00:00
2015-11-16 22:03:46 +00:00
#if BOO_HAS_METAL
class GraphicsContextCocoaMetal : public GraphicsContextCocoa
2015-09-02 19:09:13 +00:00
{
GraphicsContextCocoaMetalInternal* m_nsContext = nullptr;
2016-02-24 21:07:48 +00:00
IGraphicsCommandQueue* m_commandQueue = nullptr;
IGraphicsDataFactory* m_dataFactory = nullptr;
2016-02-24 21:07:48 +00:00
public:
2015-12-09 22:23:22 +00:00
IWindow* m_parentWindow;
2015-11-09 02:24:45 +00:00
MetalContext* m_metalCtx;
GraphicsContextCocoaMetal(EGraphicsAPI api, IWindow* parentWindow,
2016-02-24 21:07:48 +00:00
MetalContext* metalCtx, uint32_t sampleCount)
2015-11-21 02:16:15 +00:00
: GraphicsContextCocoa(api, EPixelFormat::RGBA8, parentWindow),
2015-12-09 22:23:22 +00:00
m_parentWindow(parentWindow), m_metalCtx(metalCtx)
2015-11-01 00:06:56 +00:00
{
2016-02-24 21:07:48 +00:00
m_dataFactory = new MetalDataFactory(this, metalCtx, sampleCount);
2015-11-01 00:06:56 +00:00
}
2016-02-24 21:07:48 +00:00
~GraphicsContextCocoaMetal()
{
delete m_dataFactory;
delete m_commandQueue;
2015-11-09 02:24:45 +00:00
m_metalCtx->m_windows.erase(m_parentWindow);
}
2016-02-24 21:07:48 +00:00
void _setCallback(IWindowCallback* cb)
{
m_callback = cb;
}
2016-02-24 21:07:48 +00:00
EGraphicsAPI getAPI() const
{
return m_api;
}
2016-02-24 21:07:48 +00:00
EPixelFormat getPixelFormat() const
{
return m_pf;
}
2016-02-24 21:07:48 +00:00
void setPixelFormat(EPixelFormat pf)
{
2015-11-21 02:16:15 +00:00
if (pf > EPixelFormat::RGBAF32_Z24)
return;
m_pf = pf;
}
2016-02-24 21:07:48 +00:00
2016-07-01 02:30:29 +00:00
void initializeContext(void*)
{
2015-11-09 02:24:45 +00:00
MetalContext::Window& w = m_metalCtx->m_windows[m_parentWindow];
m_nsContext = [[GraphicsContextCocoaMetalInternal alloc] initWithBooContext:this];
if (!m_nsContext)
2016-03-08 21:18:38 +00:00
Log.report(logvisor::Fatal, "unable to make new NSView for Metal");
2015-11-09 02:24:45 +00:00
w.m_metalLayer = (CAMetalLayer*)m_nsContext.layer;
2015-12-27 04:20:07 +00:00
[(__bridge NSWindow*)(void*)m_parentWindow->getPlatformHandle() setContentView:m_nsContext];
CVDisplayLinkCreateWithActiveCGDisplays(&m_dispLink);
CVDisplayLinkSetOutputCallback(m_dispLink, (CVDisplayLinkOutputCallback)DLCallback, this);
CVDisplayLinkStart(m_dispLink);
2015-11-09 02:24:45 +00:00
m_commandQueue = _NewMetalCommandQueue(m_metalCtx, m_parentWindow, this);
}
2016-02-24 21:07:48 +00:00
void makeCurrent()
{
}
2016-02-24 21:07:48 +00:00
void postInit()
{
}
2016-02-24 21:07:48 +00:00
IGraphicsCommandQueue* getCommandQueue()
{
return m_commandQueue;
}
2016-02-24 21:07:48 +00:00
IGraphicsDataFactory* getDataFactory()
{
return m_dataFactory;
}
2016-02-24 21:07:48 +00:00
2015-11-18 23:55:25 +00:00
IGraphicsDataFactory* getMainContextDataFactory()
{
return m_dataFactory;
}
2016-02-24 21:07:48 +00:00
IGraphicsDataFactory* getLoadContextDataFactory()
{
return m_dataFactory;
}
2016-02-24 21:07:48 +00:00
void present()
{
}
2016-02-24 21:07:48 +00:00
2015-12-27 04:20:07 +00:00
BooCocoaResponder* responder() const
{
if (!m_nsContext)
return nullptr;
return m_nsContext->resp;
}
2016-02-24 21:07:48 +00:00
};
2015-09-02 19:09:13 +00:00
IGraphicsContext* _GraphicsContextCocoaMetalNew(IGraphicsContext::EGraphicsAPI api,
IWindow* parentWindow,
2016-02-24 21:07:48 +00:00
MetalContext* metalCtx,
uint32_t sampleCount)
{
2015-11-21 02:16:15 +00:00
if (api != IGraphicsContext::EGraphicsAPI::Metal)
return nullptr;
2016-02-24 21:07:48 +00:00
return new GraphicsContextCocoaMetal(api, parentWindow, metalCtx, sampleCount);
}
2015-11-16 22:03:46 +00:00
#endif
}
@implementation BooCocoaResponder
- (id)initWithBooContext:(boo::GraphicsContextCocoa*)bctx View:(NSView*)view
2015-09-02 19:09:13 +00:00
{
lastModifiers = 0;
booContext = bctx;
parentView = view;
2015-12-26 05:21:13 +00:00
textContext = [[NSTextInputContext alloc] initWithClient:self];
return self;
2015-09-02 19:09:13 +00:00
}
2015-12-26 05:21:13 +00:00
- (BOOL)hasMarkedText
{
if (booContext->m_callback)
{
boo::ITextInputCallback* textCb = booContext->m_callback->getTextInputCallback();
if (textCb)
return textCb->hasMarkedText();
}
return false;
}
- (NSRange)markedRange
{
if (booContext->m_callback)
{
boo::ITextInputCallback* textCb = booContext->m_callback->getTextInputCallback();
if (textCb)
{
std::pair<int,int> rng = textCb->markedRange();
return NSMakeRange(rng.first < 0 ? NSNotFound : rng.first, rng.second);
}
}
return NSMakeRange(NSNotFound, 0);
}
- (NSRange)selectedRange
{
if (booContext->m_callback)
{
boo::ITextInputCallback* textCb = booContext->m_callback->getTextInputCallback();
if (textCb)
{
std::pair<int,int> rng = textCb->selectedRange();
return NSMakeRange(rng.first < 0 ? NSNotFound : rng.first, rng.second);
}
}
return NSMakeRange(NSNotFound, 0);
}
- (void)setMarkedText:(id)aString selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
{
if (booContext->m_callback)
{
boo::ITextInputCallback* textCb = booContext->m_callback->getTextInputCallback();
if (textCb)
{
2015-12-27 04:20:07 +00:00
NSString* plainStr = aString;
2015-12-26 05:21:13 +00:00
if ([aString isKindOfClass:[NSAttributedString class]])
plainStr = ((NSAttributedString*)aString).string;
textCb->setMarkedText([plainStr UTF8String],
std::make_pair(selectedRange.location, selectedRange.length),
2015-12-27 04:20:07 +00:00
std::make_pair(replacementRange.location==NSNotFound ? -1 : replacementRange.location,
replacementRange.length));
2015-12-26 05:21:13 +00:00
}
}
}
- (void)unmarkText
{
if (booContext->m_callback)
{
boo::ITextInputCallback* textCb = booContext->m_callback->getTextInputCallback();
if (textCb)
textCb->unmarkText();
}
}
2016-07-05 21:47:04 +00:00
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101100
2015-12-26 05:21:13 +00:00
- (NSArray<NSString*>*)validAttributesForMarkedText
{
2015-12-27 04:20:07 +00:00
return @[];
2015-12-26 05:21:13 +00:00
}
2016-07-05 21:47:04 +00:00
#else
- (NSArray*)validAttributesForMarkedText
{
return @[];
}
#endif
2015-12-26 05:21:13 +00:00
- (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
{
if (booContext->m_callback)
{
boo::ITextInputCallback* textCb = booContext->m_callback->getTextInputCallback();
if (textCb)
{
std::pair<int,int> actualRng;
std::string str = textCb->substringForRange(std::make_pair(aRange.location, aRange.length), actualRng);
if (str.empty())
return nil;
actualRange->location = actualRng.first;
actualRange->length = actualRng.second;
NSString* nsStr = [NSString stringWithUTF8String:str.c_str()];
NSAttributedString* ret = [[NSAttributedString alloc] initWithString:nsStr];
return ret;
}
}
return nil;
}
- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange
{
if (booContext->m_callback)
{
boo::ITextInputCallback* textCb = booContext->m_callback->getTextInputCallback();
if (textCb)
{
2015-12-27 04:20:07 +00:00
NSString* plainStr = aString;
2015-12-26 05:21:13 +00:00
if ([aString isKindOfClass:[NSAttributedString class]])
plainStr = ((NSAttributedString*)aString).string;
textCb->insertText([plainStr UTF8String],
2015-12-27 04:20:07 +00:00
std::make_pair(replacementRange.location == NSNotFound ? -1 : replacementRange.location,
replacementRange.length));
2015-12-26 05:21:13 +00:00
}
}
}
- (NSUInteger)characterIndexForPoint:(NSPoint)aPoint
{
if (booContext->m_callback)
{
boo::ITextInputCallback* textCb = booContext->m_callback->getTextInputCallback();
if (textCb)
{
NSPoint backingPoint = [parentView convertPointToBacking:aPoint];
boo::SWindowCoord coord = {{int(backingPoint.x), int(backingPoint.y)}, {int(aPoint.x), int(aPoint.y)}};
int idx = textCb->characterIndexAtPoint(coord);
if (idx < 0)
return NSNotFound;
return idx;
}
}
return NSNotFound;
}
- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
{
if (booContext->m_callback)
{
boo::ITextInputCallback* textCb = booContext->m_callback->getTextInputCallback();
if (textCb)
{
std::pair<int,int> actualRng;
boo::SWindowRect rect =
textCb->rectForCharacterRange(std::make_pair(aRange.location, aRange.length), actualRng);
actualRange->location = actualRng.first;
actualRange->length = actualRng.second;
return [[parentView window] convertRectToScreen:
[parentView convertRectFromBacking:NSMakeRect(rect.location[0], rect.location[1],
rect.size[0], rect.size[1])]];
}
}
return NSMakeRect(0, 0, 0, 0);
}
- (void)doCommandBySelector:(SEL)aSelector
{
}
static inline boo::EModifierKey getMod(NSUInteger flags)
2015-09-02 19:09:13 +00:00
{
2015-11-21 02:16:15 +00:00
boo::EModifierKey ret = boo::EModifierKey::None;
2015-09-02 19:09:13 +00:00
if (flags & NSControlKeyMask)
2015-11-21 02:16:15 +00:00
ret |= boo::EModifierKey::Ctrl;
2015-09-02 19:09:13 +00:00
if (flags & NSAlternateKeyMask)
2015-11-21 02:16:15 +00:00
ret |= boo::EModifierKey::Alt;
2015-09-02 19:09:13 +00:00
if (flags & NSShiftKeyMask)
2015-11-21 02:16:15 +00:00
ret |= boo::EModifierKey::Shift;
2015-09-02 19:09:13 +00:00
if (flags & NSCommandKeyMask)
2015-11-21 02:16:15 +00:00
ret |= boo::EModifierKey::Command;
return ret;
2015-09-02 19:09:13 +00:00
}
static inline boo::EMouseButton getButton(NSEvent* event)
2015-09-02 19:09:13 +00:00
{
NSInteger buttonNumber = event.buttonNumber;
if (buttonNumber == 3)
2015-11-21 02:16:15 +00:00
return boo::EMouseButton::Middle;
2015-09-02 19:09:13 +00:00
else if (buttonNumber == 4)
2015-11-21 02:16:15 +00:00
return boo::EMouseButton::Aux1;
2015-09-02 19:09:13 +00:00
else if (buttonNumber == 5)
2015-11-21 02:16:15 +00:00
return boo::EMouseButton::Aux2;
return boo::EMouseButton::None;
2015-09-02 19:09:13 +00:00
}
2015-09-02 19:09:13 +00:00
- (void)mouseDown:(NSEvent*)theEvent
{
if (!booContext->m_callback)
return;
NSPoint liw = [parentView convertPoint:[theEvent locationInWindow] fromView:nil];
float pixelFactor = [[parentView window] backingScaleFactor];
NSRect frame = [parentView frame];
boo::SWindowCoord coord =
2015-09-02 19:09:13 +00:00
{
2015-12-04 02:26:10 +00:00
{int(liw.x * pixelFactor), int(liw.y * pixelFactor)},
{int(liw.x), int(liw.y)},
{float(liw.x / frame.size.width), float(liw.y / frame.size.height)}
2015-09-02 19:09:13 +00:00
};
2015-11-21 02:16:15 +00:00
booContext->m_callback->mouseDown(coord, boo::EMouseButton::Primary,
2015-09-02 19:09:13 +00:00
getMod([theEvent modifierFlags]));
2015-12-26 05:21:13 +00:00
[textContext handleEvent:theEvent];
2015-09-02 19:09:13 +00:00
}
- (void)mouseUp:(NSEvent*)theEvent
{
if (!booContext->m_callback)
return;
NSPoint liw = [parentView convertPoint:[theEvent locationInWindow] fromView:nil];
float pixelFactor = [[parentView window] backingScaleFactor];
NSRect frame = [parentView frame];
boo::SWindowCoord coord =
2015-09-02 19:09:13 +00:00
{
2015-12-04 02:26:10 +00:00
{int(liw.x * pixelFactor), int(liw.y * pixelFactor)},
{int(liw.x), int(liw.y)},
{float(liw.x / frame.size.width), float(liw.y / frame.size.height)}
2015-09-02 19:09:13 +00:00
};
2015-11-21 02:16:15 +00:00
booContext->m_callback->mouseUp(coord, boo::EMouseButton::Primary,
2015-09-02 19:09:13 +00:00
getMod([theEvent modifierFlags]));
2015-12-26 05:21:13 +00:00
[textContext handleEvent:theEvent];
2015-09-02 19:09:13 +00:00
}
- (void)rightMouseDown:(NSEvent*)theEvent
{
if (!booContext->m_callback)
return;
NSPoint liw = [parentView convertPoint:[theEvent locationInWindow] fromView:nil];
float pixelFactor = [[parentView window] backingScaleFactor];
NSRect frame = [parentView frame];
boo::SWindowCoord coord =
2015-09-02 19:09:13 +00:00
{
2015-12-04 02:26:10 +00:00
{int(liw.x * pixelFactor), int(liw.y * pixelFactor)},
{int(liw.x), int(liw.y)},
{float(liw.x / frame.size.width), float(liw.y / frame.size.height)}
2015-09-02 19:09:13 +00:00
};
2015-11-21 02:16:15 +00:00
booContext->m_callback->mouseDown(coord, boo::EMouseButton::Secondary,
2015-09-02 19:09:13 +00:00
getMod([theEvent modifierFlags]));
2015-12-26 05:21:13 +00:00
[textContext handleEvent:theEvent];
2015-09-02 19:09:13 +00:00
}
- (void)rightMouseUp:(NSEvent*)theEvent
{
if (!booContext->m_callback)
return;
NSPoint liw = [parentView convertPoint:[theEvent locationInWindow] fromView:nil];
float pixelFactor = [[parentView window] backingScaleFactor];
NSRect frame = [parentView frame];
boo::SWindowCoord coord =
2015-09-02 19:09:13 +00:00
{
2015-12-04 02:26:10 +00:00
{int(liw.x * pixelFactor), int(liw.y * pixelFactor)},
{int(liw.x), int(liw.y)},
{float(liw.x / frame.size.width), float(liw.y / frame.size.height)}
2015-09-02 19:09:13 +00:00
};
2015-11-21 02:16:15 +00:00
booContext->m_callback->mouseUp(coord, boo::EMouseButton::Secondary,
2015-09-02 19:09:13 +00:00
getMod([theEvent modifierFlags]));
2015-12-26 05:21:13 +00:00
[textContext handleEvent:theEvent];
2015-09-02 19:09:13 +00:00
}
- (void)otherMouseDown:(NSEvent*)theEvent
{
if (!booContext->m_callback)
return;
boo::EMouseButton button = getButton(theEvent);
2015-11-21 02:16:15 +00:00
if (button == boo::EMouseButton::None)
2015-09-02 19:09:13 +00:00
return;
NSPoint liw = [parentView convertPoint:[theEvent locationInWindow] fromView:nil];
float pixelFactor = [[parentView window] backingScaleFactor];
NSRect frame = [parentView frame];
boo::SWindowCoord coord =
2015-09-02 19:09:13 +00:00
{
2015-12-04 02:26:10 +00:00
{int(liw.x * pixelFactor), int(liw.y * pixelFactor)},
{int(liw.x), int(liw.y)},
{float(liw.x / frame.size.width), float(liw.y / frame.size.height)}
2015-09-02 19:09:13 +00:00
};
booContext->m_callback->mouseDown(coord, button, getMod([theEvent modifierFlags]));
2015-12-26 05:21:13 +00:00
[textContext handleEvent:theEvent];
2015-09-02 19:09:13 +00:00
}
- (void)otherMouseUp:(NSEvent*)theEvent
{
if (!booContext->m_callback)
return;
boo::EMouseButton button = getButton(theEvent);
2015-11-21 02:16:15 +00:00
if (button == boo::EMouseButton::None)
2015-09-02 19:09:13 +00:00
return;
NSPoint liw = [parentView convertPoint:[theEvent locationInWindow] fromView:nil];
float pixelFactor = [[parentView window] backingScaleFactor];
NSRect frame = [parentView frame];
boo::SWindowCoord coord =
2015-09-02 19:09:13 +00:00
{
2015-12-04 02:26:10 +00:00
{int(liw.x * pixelFactor), int(liw.y * pixelFactor)},
{int(liw.x), int(liw.y)},
{float(liw.x / frame.size.width), float(liw.y / frame.size.height)}
2015-09-02 19:09:13 +00:00
};
booContext->m_callback->mouseUp(coord, button, getMod([theEvent modifierFlags]));
2015-12-26 05:21:13 +00:00
[textContext handleEvent:theEvent];
2015-09-02 19:09:13 +00:00
}
- (void)mouseMoved:(NSEvent*)theEvent
{
if (!booContext->m_callback)
return;
NSPoint liw = [parentView convertPoint:[theEvent locationInWindow] fromView:nil];
if (theEvent.window == [parentView window] && NSPointInRect(liw, parentView.frame))
2015-09-02 19:09:13 +00:00
{
float pixelFactor = [[parentView window] backingScaleFactor];
NSRect frame = [parentView frame];
boo::SWindowCoord coord =
{
2015-12-04 02:26:10 +00:00
{int(liw.x * pixelFactor), int(liw.y * pixelFactor)},
{int(liw.x), int(liw.y)},
{float(liw.x / frame.size.width), float(liw.y / frame.size.height)}
};
booContext->m_callback->mouseMove(coord);
}
2015-12-26 05:21:13 +00:00
[textContext handleEvent:theEvent];
2015-09-02 19:09:13 +00:00
}
2015-09-02 19:09:13 +00:00
- (void)mouseDragged:(NSEvent*)theEvent
{
[self mouseMoved:theEvent];
}
2015-09-02 19:09:13 +00:00
- (void)rightMouseDragged:(NSEvent*)theEvent
{
[self mouseMoved:theEvent];
}
2015-09-02 19:09:13 +00:00
- (void)otherMouseDragged:(NSEvent*)theEvent
{
[self mouseMoved:theEvent];
}
- (void)mouseEntered:(NSEvent*)theEvent
{
if (!booContext->m_callback)
return;
NSPoint liw = [parentView convertPoint:[theEvent locationInWindow] fromView:nil];
float pixelFactor = [[parentView window] backingScaleFactor];
NSRect frame = [parentView frame];
boo::SWindowCoord coord =
{
{int(liw.x * pixelFactor), int(liw.y * pixelFactor)},
{int(liw.x), int(liw.y)},
{float(liw.x / frame.size.width), float(liw.y / frame.size.height)}
};
booContext->m_callback->mouseEnter(coord);
}
- (void)mouseExited:(NSEvent*)theEvent
{
if (!booContext->m_callback)
return;
NSPoint liw = [parentView convertPoint:[theEvent locationInWindow] fromView:nil];
float pixelFactor = [[parentView window] backingScaleFactor];
NSRect frame = [parentView frame];
boo::SWindowCoord coord =
{
{int(liw.x * pixelFactor), int(liw.y * pixelFactor)},
{int(liw.x), int(liw.y)},
{float(liw.x / frame.size.width), float(liw.y / frame.size.height)}
};
booContext->m_callback->mouseLeave(coord);
}
2015-09-02 19:09:13 +00:00
- (void)scrollWheel:(NSEvent*)theEvent
{
if (!booContext->m_callback)
return;
NSPoint liw = [parentView convertPoint:[theEvent locationInWindow] fromView:nil];
float pixelFactor = [[parentView window] backingScaleFactor];
NSRect frame = [parentView frame];
boo::SWindowCoord coord =
2015-09-02 19:09:13 +00:00
{
2015-12-04 02:26:10 +00:00
{int(liw.x * pixelFactor), int(liw.y * pixelFactor)},
{int(liw.x), int(liw.y)},
{float(liw.x / frame.size.width), float(liw.y / frame.size.height)}
2015-09-02 19:09:13 +00:00
};
boo::SScrollDelta scroll =
2015-09-02 19:09:13 +00:00
{
{(float)[theEvent scrollingDeltaX], (float)[theEvent scrollingDeltaY]},
(bool)[theEvent hasPreciseScrollingDeltas], true
2015-09-02 19:09:13 +00:00
};
booContext->m_callback->scroll(coord, scroll);
2015-12-26 05:21:13 +00:00
[textContext handleEvent:theEvent];
2015-09-02 19:09:13 +00:00
}
- (void)touchesBeganWithEvent:(NSEvent*)event
{
if (!booContext->m_callback)
return;
for (NSTouch* touch in [event touchesMatchingPhase:NSTouchPhaseBegan inView:nil])
{
NSPoint pos = touch.normalizedPosition;
boo::STouchCoord coord =
2015-09-02 19:09:13 +00:00
{
{(float)pos.x, (float)pos.y}
};
booContext->m_callback->touchDown(coord, (uintptr_t)touch.identity);
}
}
- (void)touchesEndedWithEvent:(NSEvent*)event
{
if (!booContext->m_callback)
return;
for (NSTouch* touch in [event touchesMatchingPhase:NSTouchPhaseEnded inView:nil])
{
NSPoint pos = touch.normalizedPosition;
boo::STouchCoord coord =
2015-09-02 19:09:13 +00:00
{
{(float)pos.x, (float)pos.y}
};
booContext->m_callback->touchUp(coord, (uintptr_t)touch.identity);
}
}
- (void)touchesMovedWithEvent:(NSEvent*)event
{
if (!booContext->m_callback)
return;
for (NSTouch* touch in [event touchesMatchingPhase:NSTouchPhaseMoved inView:nil])
{
NSPoint pos = touch.normalizedPosition;
boo::STouchCoord coord =
2015-09-02 19:09:13 +00:00
{
{(float)pos.x, (float)pos.y}
};
booContext->m_callback->touchMove(coord, (uintptr_t)touch.identity);
}
}
- (void)touchesCancelledWithEvent:(NSEvent*)event
{
if (!booContext->m_callback)
return;
for (NSTouch* touch in [event touchesMatchingPhase:NSTouchPhaseCancelled inView:nil])
{
NSPoint pos = touch.normalizedPosition;
boo::STouchCoord coord =
2015-09-02 19:09:13 +00:00
{
{(float)pos.x, (float)pos.y}
};
booContext->m_callback->touchUp(coord, (uintptr_t)touch.identity);
}
}
/* keycodes for keys that are independent of keyboard layout*/
enum
{
kVK_Return = 0x24,
kVK_Tab = 0x30,
kVK_Space = 0x31,
kVK_Delete = 0x33,
kVK_Escape = 0x35,
kVK_Command = 0x37,
kVK_Shift = 0x38,
kVK_CapsLock = 0x39,
kVK_Option = 0x3A,
kVK_Control = 0x3B,
kVK_RightShift = 0x3C,
kVK_RightOption = 0x3D,
kVK_RightControl = 0x3E,
kVK_Function = 0x3F,
kVK_F17 = 0x40,
kVK_VolumeUp = 0x48,
kVK_VolumeDown = 0x49,
kVK_Mute = 0x4A,
kVK_F18 = 0x4F,
kVK_F19 = 0x50,
kVK_F20 = 0x5A,
kVK_F5 = 0x60,
kVK_F6 = 0x61,
kVK_F7 = 0x62,
kVK_F3 = 0x63,
kVK_F8 = 0x64,
kVK_F9 = 0x65,
kVK_F11 = 0x67,
kVK_F13 = 0x69,
kVK_F16 = 0x6A,
kVK_F14 = 0x6B,
kVK_F10 = 0x6D,
kVK_F12 = 0x6F,
kVK_F15 = 0x71,
kVK_Help = 0x72,
kVK_Home = 0x73,
kVK_PageUp = 0x74,
kVK_ForwardDelete = 0x75,
kVK_F4 = 0x76,
kVK_End = 0x77,
kVK_F2 = 0x78,
kVK_PageDown = 0x79,
kVK_F1 = 0x7A,
kVK_LeftArrow = 0x7B,
kVK_RightArrow = 0x7C,
kVK_DownArrow = 0x7D,
kVK_UpArrow = 0x7E
};
static boo::ESpecialKey translateKeycode(short code)
2015-09-02 19:09:13 +00:00
{
2015-12-21 00:40:52 +00:00
switch (code)
{
2015-09-02 19:09:13 +00:00
case kVK_F1:
2015-11-21 02:16:15 +00:00
return boo::ESpecialKey::F1;
2015-09-02 19:09:13 +00:00
case kVK_F2:
2015-11-21 02:16:15 +00:00
return boo::ESpecialKey::F2;
2015-09-02 19:09:13 +00:00
case kVK_F3:
2015-11-21 02:16:15 +00:00
return boo::ESpecialKey::F3;
2015-09-02 19:09:13 +00:00
case kVK_F4:
2015-11-21 02:16:15 +00:00
return boo::ESpecialKey::F4;
2015-09-02 19:09:13 +00:00
case kVK_F5:
2015-11-21 02:16:15 +00:00
return boo::ESpecialKey::F5;
2015-09-02 19:09:13 +00:00
case kVK_F6:
2015-11-21 02:16:15 +00:00
return boo::ESpecialKey::F6;
2015-09-02 19:09:13 +00:00
case kVK_F7:
2015-11-21 02:16:15 +00:00
return boo::ESpecialKey::F7;
2015-09-02 19:09:13 +00:00
case kVK_F8:
2015-11-21 02:16:15 +00:00
return boo::ESpecialKey::F8;
2015-09-02 19:09:13 +00:00
case kVK_F9:
2015-11-21 02:16:15 +00:00
return boo::ESpecialKey::F9;
2015-09-02 19:09:13 +00:00
case kVK_F10:
2015-11-21 02:16:15 +00:00
return boo::ESpecialKey::F10;
2015-09-02 19:09:13 +00:00
case kVK_F11:
2015-11-21 02:16:15 +00:00
return boo::ESpecialKey::F11;
2015-09-02 19:09:13 +00:00
case kVK_F12:
2015-11-21 02:16:15 +00:00
return boo::ESpecialKey::F12;
2015-09-02 19:09:13 +00:00
case kVK_Escape:
2015-11-21 02:16:15 +00:00
return boo::ESpecialKey::Esc;
2015-09-02 19:09:13 +00:00
case kVK_Return:
2015-11-21 02:16:15 +00:00
return boo::ESpecialKey::Enter;
2015-09-02 19:09:13 +00:00
case kVK_Delete:
2015-11-21 02:16:15 +00:00
return boo::ESpecialKey::Backspace;
2015-09-02 19:09:13 +00:00
case kVK_ForwardDelete:
2015-11-21 02:16:15 +00:00
return boo::ESpecialKey::Delete;
2015-09-02 19:09:13 +00:00
case kVK_Home:
2015-11-21 02:16:15 +00:00
return boo::ESpecialKey::Home;
2015-09-02 19:09:13 +00:00
case kVK_End:
2015-11-21 02:16:15 +00:00
return boo::ESpecialKey::End;
2015-09-02 19:09:13 +00:00
case kVK_PageUp:
2015-11-21 02:16:15 +00:00
return boo::ESpecialKey::PgUp;
2015-09-02 19:09:13 +00:00
case kVK_PageDown:
2015-11-21 02:16:15 +00:00
return boo::ESpecialKey::PgDown;
2015-09-02 19:09:13 +00:00
case kVK_LeftArrow:
2015-11-21 02:16:15 +00:00
return boo::ESpecialKey::Left;
2015-09-02 19:09:13 +00:00
case kVK_RightArrow:
2015-11-21 02:16:15 +00:00
return boo::ESpecialKey::Right;
2015-09-02 19:09:13 +00:00
case kVK_UpArrow:
2015-11-21 02:16:15 +00:00
return boo::ESpecialKey::Up;
2015-09-02 19:09:13 +00:00
case kVK_DownArrow:
2015-11-21 02:16:15 +00:00
return boo::ESpecialKey::Down;
2015-09-02 19:09:13 +00:00
default:
2015-11-21 02:16:15 +00:00
return boo::ESpecialKey::None;
2015-09-02 19:09:13 +00:00
}
}
- (void)keyDown:(NSEvent*)theEvent
{
if (!booContext->m_callback)
return;
2015-12-21 00:40:52 +00:00
boo::ESpecialKey special = translateKeycode(theEvent.keyCode);
2015-12-28 00:23:15 +00:00
boo::EModifierKey mods = getMod(theEvent.modifierFlags);
NSString* chars;
if ((mods & boo::EModifierKey::Ctrl) != boo::EModifierKey::None)
chars = theEvent.charactersIgnoringModifiers;
else
chars = theEvent.characters;
2015-12-21 00:40:52 +00:00
if (special != boo::ESpecialKey::None)
booContext->m_callback->specialKeyDown(special,
2015-12-28 00:23:15 +00:00
mods,
2015-09-02 19:09:13 +00:00
theEvent.isARepeat);
2015-12-21 00:40:52 +00:00
else if ([chars length])
2015-09-02 19:09:13 +00:00
booContext->m_callback->charKeyDown([chars characterAtIndex:0],
2015-12-28 00:23:15 +00:00
mods,
2015-09-02 19:09:13 +00:00
theEvent.isARepeat);
2015-12-26 05:21:13 +00:00
[textContext handleEvent:theEvent];
2015-09-02 19:09:13 +00:00
}
- (void)keyUp:(NSEvent*)theEvent
{
if (!booContext->m_callback)
return;
2015-12-21 00:40:52 +00:00
boo::ESpecialKey special = translateKeycode(theEvent.keyCode);
2015-12-28 00:23:15 +00:00
boo::EModifierKey mods = getMod(theEvent.modifierFlags);
NSString* chars;
if ((mods & boo::EModifierKey::Ctrl) != boo::EModifierKey::None)
chars = theEvent.charactersIgnoringModifiers;
else
chars = theEvent.characters;
2015-12-21 00:40:52 +00:00
if (special != boo::ESpecialKey::None)
booContext->m_callback->specialKeyUp(special,
2015-12-28 00:23:15 +00:00
mods);
2015-12-21 00:40:52 +00:00
else if ([chars length])
2015-09-02 19:09:13 +00:00
booContext->m_callback->charKeyUp([chars characterAtIndex:0],
2015-12-28 00:23:15 +00:00
mods);
2015-12-27 04:20:07 +00:00
//[textContext handleEvent:theEvent];
2015-09-02 19:09:13 +00:00
}
- (void)flagsChanged:(NSEvent*)theEvent
{
if (!booContext->m_callback)
return;
NSUInteger modFlags = theEvent.modifierFlags;
if (modFlags != lastModifiers)
{
NSUInteger changedFlags = modFlags ^ lastModifiers;
2016-02-24 21:07:48 +00:00
2015-09-02 19:09:13 +00:00
NSUInteger downFlags = changedFlags & modFlags;
if (downFlags & NSControlKeyMask)
2015-11-21 02:16:15 +00:00
booContext->m_callback->modKeyDown(boo::EModifierKey::Ctrl, false);
2015-09-02 19:09:13 +00:00
if (downFlags & NSAlternateKeyMask)
2015-11-21 02:16:15 +00:00
booContext->m_callback->modKeyDown(boo::EModifierKey::Alt, false);
2015-09-02 19:09:13 +00:00
if (downFlags & NSShiftKeyMask)
2015-11-21 02:16:15 +00:00
booContext->m_callback->modKeyDown(boo::EModifierKey::Shift, false);
2015-09-02 19:09:13 +00:00
if (downFlags & NSCommandKeyMask)
2015-11-21 02:16:15 +00:00
booContext->m_callback->modKeyDown(boo::EModifierKey::Command, false);
2016-02-24 21:07:48 +00:00
2015-09-02 19:09:13 +00:00
NSUInteger upFlags = changedFlags & ~modFlags;
if (upFlags & NSControlKeyMask)
2015-11-21 02:16:15 +00:00
booContext->m_callback->modKeyUp(boo::EModifierKey::Ctrl);
2015-09-02 19:09:13 +00:00
if (upFlags & NSAlternateKeyMask)
2015-11-21 02:16:15 +00:00
booContext->m_callback->modKeyUp(boo::EModifierKey::Alt);
2015-09-02 19:09:13 +00:00
if (upFlags & NSShiftKeyMask)
2015-11-21 02:16:15 +00:00
booContext->m_callback->modKeyUp(boo::EModifierKey::Shift);
2015-09-02 19:09:13 +00:00
if (upFlags & NSCommandKeyMask)
2015-11-21 02:16:15 +00:00
booContext->m_callback->modKeyUp(boo::EModifierKey::Command);
2016-02-24 21:07:48 +00:00
2015-09-02 19:09:13 +00:00
lastModifiers = modFlags;
}
2015-12-26 05:21:13 +00:00
[textContext handleEvent:theEvent];
2015-09-02 19:09:13 +00:00
}
- (BOOL)acceptsTouchEvents
{
return YES;
}
- (BOOL)acceptsFirstResponder
{
return YES;
}
@end
2016-02-24 21:07:48 +00:00
@implementation GraphicsContextCocoaGLInternal
- (id)initWithBooContext:(boo::GraphicsContextCocoaGL*)bctx
{
resp = [[BooCocoaResponder alloc] initWithBooContext:bctx View:self];
boo::IGraphicsContext::EPixelFormat pf = bctx->getPixelFormat();
2015-11-21 02:16:15 +00:00
NSOpenGLPixelFormat* nspf = [[NSOpenGLPixelFormat alloc] initWithAttributes:PF_TABLE[int(pf)]];
self = [self initWithFrame:NSMakeRect(0, 0, 100, 100) pixelFormat:nspf];
if (bctx->m_lastCtx)
{
NSOpenGLContext* sharedCtx = [[NSOpenGLContext alloc] initWithFormat:nspf shareContext:bctx->m_lastCtx];
[self setOpenGLContext:sharedCtx];
[sharedCtx setView:self];
}
NSTrackingArea* trackingArea = [[NSTrackingArea alloc] initWithRect:NSZeroRect
options:(NSTrackingMouseEnteredAndExited |
NSTrackingMouseMoved |
NSTrackingActiveAlways |
NSTrackingInVisibleRect)
owner:self
userInfo:nil];
[self addTrackingArea:trackingArea];
return self;
}
- (void)reshape
{
2016-01-03 04:05:21 +00:00
boo::SWindowRect rect = {int(self.frame.origin.x), int(self.frame.origin.y),
int(self.frame.size.width), int(self.frame.size.height)};
2015-12-02 03:06:14 +00:00
if (resp->booContext->m_callback)
resp->booContext->m_callback->resized(rect);
[super reshape];
}
- (BOOL)acceptsTouchEvents
{
return YES;
}
- (BOOL)acceptsFirstResponder
{
return YES;
}
- (NSResponder*)nextResponder
{
return resp;
}
@end
2015-11-16 22:03:46 +00:00
#if BOO_HAS_METAL
@implementation GraphicsContextCocoaMetalInternal
- (id)initWithBooContext:(boo::GraphicsContextCocoaMetal*)bctx
{
2015-11-09 02:24:45 +00:00
m_ctx = bctx->m_metalCtx;
2015-12-09 22:23:22 +00:00
m_window = bctx->m_parentWindow;
2015-11-09 06:45:14 +00:00
self = [self initWithFrame:NSMakeRect(0, 0, 100, 100)];
[self setWantsLayer:YES];
resp = [[BooCocoaResponder alloc] initWithBooContext:bctx View:self];
NSTrackingArea* trackingArea = [[NSTrackingArea alloc] initWithRect:NSZeroRect
options:(NSTrackingMouseEnteredAndExited |
NSTrackingMouseMoved |
NSTrackingActiveAlways |
NSTrackingInVisibleRect)
owner:self
userInfo:nil];
[self addTrackingArea:trackingArea];
return self;
}
2015-11-09 02:24:45 +00:00
- (CALayer*)makeBackingLayer
{
CAMetalLayer* layer = [CAMetalLayer new];
2016-01-11 22:26:40 +00:00
layer.device = m_ctx->m_dev;
2015-11-09 02:24:45 +00:00
layer.pixelFormat = MTLPixelFormatBGRA8Unorm;
layer.framebufferOnly = NO;
return layer;
}
- (BOOL)acceptsTouchEvents
{
return YES;
}
- (BOOL)acceptsFirstResponder
{
return YES;
}
- (NSResponder*)nextResponder
{
return resp;
}
2015-12-09 22:23:22 +00:00
- (void)reshapeHandler
{
2016-01-03 04:05:21 +00:00
boo::SWindowRect rect = {int(self.frame.origin.x), int(self.frame.origin.y),
int(self.frame.size.width), int(self.frame.size.height)};
2015-12-09 22:23:22 +00:00
boo::MetalContext::Window& w = m_ctx->m_windows[m_window];
std::unique_lock<std::mutex> lk(w.m_resizeLock);
if (resp->booContext->m_callback)
resp->booContext->m_callback->resized(rect);
w.m_size = CGSizeMake(rect.size[0], rect.size[1]);
w.m_needsResize = YES;
}
- (void)setFrameSize:(NSSize)newSize
{
[super setFrameSize:newSize];
[self reshapeHandler];
}
- (void)setBoundsSize:(NSSize)newSize
{
[super setBoundsSize:newSize];
[self reshapeHandler];
}
- (void)viewDidChangeBackingProperties
{
[super viewDidChangeBackingProperties];
[self reshapeHandler];
}
2015-09-02 19:09:13 +00:00
@end
2015-11-16 22:03:46 +00:00
#endif
2015-09-02 19:09:13 +00:00
namespace boo
{
2016-02-24 21:07:48 +00:00
2015-12-27 04:20:07 +00:00
static NSString* ClipboardTypes[] =
{
0, NSPasteboardTypeString, NSPasteboardTypeString, NSPasteboardTypePNG
};
2015-09-02 19:09:13 +00:00
class WindowCocoa : public IWindow
{
2016-02-24 21:07:48 +00:00
2015-09-02 19:09:13 +00:00
WindowCocoaInternal* m_nsWindow;
2015-12-27 04:20:07 +00:00
GraphicsContextCocoa* m_gfxCtx;
2015-11-30 03:41:03 +00:00
EMouseCursor m_cursor = EMouseCursor::None;
bool m_closed = false;
public:
2016-02-24 21:07:48 +00:00
WindowCocoa(const std::string& title, NSOpenGLContext* lastGLCtx, MetalContext* metalCtx, uint32_t sampleCount)
{
2016-01-16 06:11:25 +00:00
dispatch_sync(dispatch_get_main_queue(),
^{
m_nsWindow = [[WindowCocoaInternal alloc] initWithBooWindow:this title:title];
2015-11-16 22:03:46 +00:00
#if BOO_HAS_METAL
2016-01-16 06:11:25 +00:00
if (metalCtx->m_dev)
2016-02-24 21:07:48 +00:00
m_gfxCtx = static_cast<GraphicsContextCocoa*>(_GraphicsContextCocoaMetalNew(IGraphicsContext::EGraphicsAPI::Metal,
this, metalCtx, sampleCount));
2016-01-16 06:11:25 +00:00
else
2015-11-16 22:03:46 +00:00
#endif
2016-02-24 21:07:48 +00:00
m_gfxCtx = static_cast<GraphicsContextCocoa*>(_GraphicsContextCocoaGLNew(IGraphicsContext::EGraphicsAPI::OpenGL3_3,
this, lastGLCtx, sampleCount));
2016-07-01 02:30:29 +00:00
m_gfxCtx->initializeContext(nullptr);
2016-01-16 06:11:25 +00:00
});
}
2016-02-24 21:07:48 +00:00
2015-05-06 00:50:57 +00:00
void _clearWindow()
{
m_closed = true;
2015-05-06 00:50:57 +00:00
}
2016-02-24 21:07:48 +00:00
2015-09-02 19:09:13 +00:00
~WindowCocoa()
{
if (!m_closed)
[m_nsWindow orderOut:nil];
delete m_gfxCtx;
2015-09-02 19:09:13 +00:00
APP->_deletedWindow(this);
}
2016-02-24 21:07:48 +00:00
void setCallback(IWindowCallback* cb)
{
m_gfxCtx->_setCallback(cb);
}
2016-02-24 21:07:48 +00:00
void showWindow()
{
2015-11-01 00:06:56 +00:00
dispatch_sync(dispatch_get_main_queue(),
^{
[m_nsWindow makeKeyAndOrderFront:nil];
});
}
2016-02-24 21:07:48 +00:00
void hideWindow()
{
2015-11-01 00:06:56 +00:00
dispatch_sync(dispatch_get_main_queue(),
^{
[m_nsWindow orderOut:nil];
});
}
2016-02-24 21:07:48 +00:00
std::string getTitle()
{
return [[m_nsWindow title] UTF8String];
}
2016-02-24 21:07:48 +00:00
void setTitle(const std::string& title)
{
2015-11-01 00:06:56 +00:00
dispatch_sync(dispatch_get_main_queue(),
^{
2015-12-27 04:20:07 +00:00
[m_nsWindow setTitle:[NSString stringWithUTF8String:title.c_str()]];
2015-11-01 00:06:56 +00:00
});
}
2016-02-24 21:07:48 +00:00
2015-11-30 03:41:03 +00:00
void setCursor(EMouseCursor cursor)
{
if (cursor == m_cursor)
return;
m_cursor = cursor;
dispatch_async(dispatch_get_main_queue(),
^{
switch (cursor)
{
case EMouseCursor::Pointer:
[[NSCursor arrowCursor] set];
break;
case EMouseCursor::HorizontalArrow:
[[NSCursor resizeLeftRightCursor] set];
break;
case EMouseCursor::VerticalArrow:
[[NSCursor resizeUpDownCursor] set];
break;
2015-12-21 00:40:52 +00:00
case EMouseCursor::IBeam:
[[NSCursor IBeamCursor] set];
break;
2016-01-10 06:42:00 +00:00
case EMouseCursor::Crosshairs:
[[NSCursor crosshairCursor] set];
break;
2015-11-30 03:41:03 +00:00
default: break;
}
});
}
2016-02-24 21:07:48 +00:00
2015-11-30 03:41:03 +00:00
void setWaitCursor(bool wait) {}
2016-02-24 21:07:48 +00:00
void setWindowFrameDefault()
{
2015-11-01 00:06:56 +00:00
dispatch_sync(dispatch_get_main_queue(),
^{
NSScreen* mainScreen = [NSScreen mainScreen];
NSRect scrFrame = mainScreen.frame;
float x_off = scrFrame.size.width / 3.0;
float y_off = scrFrame.size.height / 3.0;
[m_nsWindow setFrame:NSMakeRect(x_off, y_off, x_off * 2.0, y_off * 2.0) display:NO];
});
}
2016-02-24 21:07:48 +00:00
void getWindowFrame(float& xOut, float& yOut, float& wOut, float& hOut) const
{
2016-07-05 21:47:04 +00:00
NSRect wFrame = [[m_nsWindow contentView] frame];
xOut = wFrame.origin.x;
yOut = wFrame.origin.y;
wOut = wFrame.size.width;
hOut = wFrame.size.height;
}
2016-02-24 21:07:48 +00:00
void getWindowFrame(int& xOut, int& yOut, int& wOut, int& hOut) const
{
2016-07-05 21:47:04 +00:00
NSRect wFrame = [[m_nsWindow contentView] frame];
xOut = wFrame.origin.x;
yOut = wFrame.origin.y;
wOut = wFrame.size.width;
hOut = wFrame.size.height;
}
2016-02-24 21:07:48 +00:00
void setWindowFrame(float x, float y, float w, float h)
{
2015-11-01 00:06:56 +00:00
dispatch_sync(dispatch_get_main_queue(),
^{
2015-12-09 22:23:22 +00:00
[m_nsWindow setContentSize:NSMakeSize(w, h)];
[m_nsWindow setFrameOrigin:NSMakePoint(x, y)];
2015-11-01 00:06:56 +00:00
});
}
2016-02-24 21:07:48 +00:00
void setWindowFrame(int x, int y, int w, int h)
{
dispatch_sync(dispatch_get_main_queue(),
^{
2015-12-09 22:23:22 +00:00
[m_nsWindow setContentSize:NSMakeSize(w, h)];
[m_nsWindow setFrameOrigin:NSMakePoint(x, y)];
});
}
2016-02-24 21:07:48 +00:00
float getVirtualPixelFactor() const
{
return [m_nsWindow backingScaleFactor];
}
2016-02-24 21:07:48 +00:00
bool isFullscreen() const
{
return ([m_nsWindow styleMask] & NSFullScreenWindowMask) == NSFullScreenWindowMask;
}
2016-02-24 21:07:48 +00:00
void setFullscreen(bool fs)
{
if ((fs && !isFullscreen()) || (!fs && isFullscreen()))
2015-11-01 00:06:56 +00:00
dispatch_sync(dispatch_get_main_queue(),
^{
[m_nsWindow toggleFullScreen:nil];
});
}
2016-02-24 21:07:48 +00:00
2015-12-26 05:21:13 +00:00
void claimKeyboardFocus(const int coord[2])
{
2015-12-27 04:20:07 +00:00
BooCocoaResponder* resp = m_gfxCtx->responder();
if (resp)
{
dispatch_async(dispatch_get_main_queue(),
^{
if (coord)
[resp->textContext activate];
else
[resp->textContext deactivate];
});
}
2015-12-26 05:21:13 +00:00
}
2016-02-24 21:07:48 +00:00
2015-12-26 05:21:13 +00:00
bool clipboardCopy(EClipboardType type, const uint8_t* data, size_t sz)
{
2015-12-27 04:20:07 +00:00
NSPasteboard* pb = [NSPasteboard generalPasteboard];
[pb clearContents];
NSData* d = [NSData dataWithBytes:data length:sz];
[pb setData:d forType:ClipboardTypes[int(type)]];
return true;
2015-12-26 05:21:13 +00:00
}
2016-02-24 21:07:48 +00:00
2015-12-26 05:21:13 +00:00
std::unique_ptr<uint8_t[]> clipboardPaste(EClipboardType type, size_t& sz)
{
2015-12-27 04:20:07 +00:00
NSPasteboard* pb = [NSPasteboard generalPasteboard];
NSData* d = [pb dataForType:ClipboardTypes[int(type)]];
if (!d)
return std::unique_ptr<uint8_t[]>();
sz = [d length];
std::unique_ptr<uint8_t[]> ret(new uint8_t[sz]);
[d getBytes:ret.get() length:sz];
return ret;
2015-12-26 05:21:13 +00:00
}
2016-02-24 21:07:48 +00:00
ETouchType getTouchType() const
{
2015-11-21 02:16:15 +00:00
return ETouchType::Trackpad;
}
2016-02-24 21:07:48 +00:00
void setStyle(EWindowStyle style)
{
2015-11-16 22:03:46 +00:00
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
2015-11-21 02:16:15 +00:00
if ((style & EWindowStyle::Titlebar) != EWindowStyle::None)
m_nsWindow.titleVisibility = NSWindowTitleVisible;
else
m_nsWindow.titleVisibility = NSWindowTitleHidden;
2015-11-16 22:03:46 +00:00
#endif
2016-02-24 21:07:48 +00:00
2015-11-21 02:16:15 +00:00
if ((style & EWindowStyle::Close) != EWindowStyle::None)
m_nsWindow.styleMask |= NSClosableWindowMask;
else
m_nsWindow.styleMask &= ~NSClosableWindowMask;
2016-02-24 21:07:48 +00:00
2015-11-21 02:16:15 +00:00
if ((style & EWindowStyle::Resize) != EWindowStyle::None)
m_nsWindow.styleMask |= NSResizableWindowMask;
else
m_nsWindow.styleMask &= ~NSResizableWindowMask;
}
2016-02-24 21:07:48 +00:00
EWindowStyle getStyle() const
{
2015-11-21 02:16:15 +00:00
EWindowStyle retval = EWindowStyle::None;
2015-11-16 22:03:46 +00:00
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101000
2015-11-21 02:16:15 +00:00
retval |= m_nsWindow.titleVisibility == NSWindowTitleVisible ? EWindowStyle::Titlebar : EWindowStyle::None;
2015-11-16 22:03:46 +00:00
#else
2015-11-21 02:16:15 +00:00
retval |= EWindowStyle::Titlebar;
2015-11-16 22:03:46 +00:00
#endif
2015-11-21 02:16:15 +00:00
retval |= (m_nsWindow.styleMask & NSClosableWindowMask) ? EWindowStyle::Close : EWindowStyle::None;
retval |= (m_nsWindow.styleMask & NSResizableWindowMask) ? EWindowStyle::Resize: EWindowStyle::None;
return retval;
}
2016-02-24 21:07:48 +00:00
2015-09-02 19:09:13 +00:00
void waitForRetrace()
{
2015-11-01 00:06:56 +00:00
static_cast<GraphicsContextCocoa*>(m_gfxCtx)->waitForRetrace();
2015-09-02 19:09:13 +00:00
}
2016-02-24 21:07:48 +00:00
2015-05-09 05:33:48 +00:00
uintptr_t getPlatformHandle() const
2015-05-06 00:50:57 +00:00
{
2015-05-09 05:33:48 +00:00
return (uintptr_t)m_nsWindow;
2015-05-06 00:50:57 +00:00
}
2016-02-24 21:07:48 +00:00
2015-11-01 00:06:56 +00:00
IGraphicsCommandQueue* getCommandQueue()
{
return m_gfxCtx->getCommandQueue();
}
2016-02-24 21:07:48 +00:00
2015-11-01 00:06:56 +00:00
IGraphicsDataFactory* getDataFactory()
{
return m_gfxCtx->getDataFactory();
}
2016-02-24 21:07:48 +00:00
2015-11-17 20:33:12 +00:00
IGraphicsDataFactory* getMainContextDataFactory()
{
return m_gfxCtx->getMainContextDataFactory();
}
2016-02-24 21:07:48 +00:00
2015-11-01 00:06:56 +00:00
IGraphicsDataFactory* getLoadContextDataFactory()
{
return m_gfxCtx->getLoadContextDataFactory();
}
2016-02-24 21:07:48 +00:00
};
2016-02-24 21:07:48 +00:00
IWindow* _WindowCocoaNew(const SystemString& title, NSOpenGLContext* lastGLCtx,
MetalContext* metalCtx, uint32_t sampleCount)
{
2016-02-24 21:07:48 +00:00
return new WindowCocoa(title, lastGLCtx, metalCtx, sampleCount);
}
2016-02-24 21:07:48 +00:00
}
2015-09-02 19:09:13 +00:00
@implementation WindowCocoaInternal
- (id)initWithBooWindow:(boo::WindowCocoa *)bw title:(const boo::SystemString&)title
{
2015-05-06 00:50:57 +00:00
self = [self initWithContentRect:[self genFrameDefault]
styleMask:NSTitledWindowMask|
NSClosableWindowMask|
NSMiniaturizableWindowMask|
NSResizableWindowMask
backing:NSBackingStoreBuffered
defer:YES];
self.releasedWhenClosed = NO;
2015-12-27 04:20:07 +00:00
self.title = [NSString stringWithUTF8String:title.c_str()];
booWindow = bw;
return self;
}
2015-05-06 00:50:57 +00:00
- (void)setFrameDefault
{
[self setFrame:[self genFrameDefault] display:NO];
}
- (NSRect)genFrameDefault
{
NSScreen* mainScreen = [NSScreen mainScreen];
NSRect scrFrame = mainScreen.frame;
float width = scrFrame.size.width * 2.0 / 3.0;
float height = scrFrame.size.height * 2.0 / 3.0;
return NSMakeRect((scrFrame.size.width - width) / 2.0,
(scrFrame.size.height - height) / 2.0,
width, height);
}
- (void)close
{
booWindow->_clearWindow();
[super close];
}
- (BOOL)acceptsFirstResponder
{
return YES;
}
- (BOOL)acceptsMouseMovedEvents
{
return YES;
}
- (NSWindowCollectionBehavior)collectionBehavior
{
return NSWindowCollectionBehaviorFullScreenPrimary;
}
@end