summaryrefslogtreecommitdiff
path: root/platform/osx
diff options
context:
space:
mode:
Diffstat (limited to 'platform/osx')
-rw-r--r--platform/osx/SCsub1
-rw-r--r--platform/osx/camera_osx.h47
-rw-r--r--platform/osx/camera_osx.mm362
-rw-r--r--platform/osx/detect.py14
-rw-r--r--platform/osx/export/export.cpp2
-rw-r--r--platform/osx/os_osx.h12
-rw-r--r--platform/osx/os_osx.mm122
7 files changed, 542 insertions, 18 deletions
diff --git a/platform/osx/SCsub b/platform/osx/SCsub
index e15b4339a7..9620863b96 100644
--- a/platform/osx/SCsub
+++ b/platform/osx/SCsub
@@ -13,6 +13,7 @@ files = [
'dir_access_osx.mm',
'joypad_osx.cpp',
'power_osx.cpp',
+ 'camera_osx.mm',
]
prog = env.add_program('#bin/godot', files)
diff --git a/platform/osx/camera_osx.h b/platform/osx/camera_osx.h
new file mode 100644
index 0000000000..80ca3759ba
--- /dev/null
+++ b/platform/osx/camera_osx.h
@@ -0,0 +1,47 @@
+/*************************************************************************/
+/* camera_osx.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef CAMERAOSX_H
+#define CAMERAOSX_H
+
+///@TODO this is a near duplicate of CameraIOS, we should find a way to combine those to minimise code duplication!!!!
+// If you fix something here, make sure you fix it there as wel!
+
+#include "servers/camera_server.h"
+
+class CameraOSX : public CameraServer {
+public:
+ CameraOSX();
+ ~CameraOSX();
+
+ void update_feeds();
+};
+
+#endif /* CAMERAOSX_H */ \ No newline at end of file
diff --git a/platform/osx/camera_osx.mm b/platform/osx/camera_osx.mm
new file mode 100644
index 0000000000..f13cf76beb
--- /dev/null
+++ b/platform/osx/camera_osx.mm
@@ -0,0 +1,362 @@
+/*************************************************************************/
+/* camera_osx.mm */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+///@TODO this is a near duplicate of CameraIOS, we should find a way to combine those to minimise code duplication!!!!
+// If you fix something here, make sure you fix it there as wel!
+
+#include "camera_osx.h"
+#include "servers/camera/camera_feed.h"
+#import <AVFoundation/AVFoundation.h>
+
+//////////////////////////////////////////////////////////////////////////
+// MyCaptureSession - This is a little helper class so we can capture our frames
+
+@interface MyCaptureSession : AVCaptureSession <AVCaptureVideoDataOutputSampleBufferDelegate> {
+ Ref<CameraFeed> feed;
+ size_t width[2];
+ size_t height[2];
+ PoolVector<uint8_t> img_data[2];
+
+ AVCaptureDeviceInput *input;
+ AVCaptureVideoDataOutput *output;
+}
+
+@end
+
+@implementation MyCaptureSession
+
+- (id)initForFeed:(Ref<CameraFeed>)p_feed andDevice:(AVCaptureDevice *)p_device {
+ if (self = [super init]) {
+ NSError *error;
+ feed = p_feed;
+ width[0] = 0;
+ height[0] = 0;
+ width[1] = 0;
+ height[1] = 0;
+
+ [self beginConfiguration];
+
+ input = [AVCaptureDeviceInput deviceInputWithDevice:p_device error:&error];
+ if (!input) {
+ print_line("Couldn't get input device for camera");
+ } else {
+ [self addInput:input];
+ }
+
+ output = [AVCaptureVideoDataOutput new];
+ if (!output) {
+ print_line("Couldn't get output device for camera");
+ } else {
+ NSDictionary *settings = @{ (NSString *)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) };
+ output.videoSettings = settings;
+
+ // discard if the data output queue is blocked (as we process the still image)
+ [output setAlwaysDiscardsLateVideoFrames:YES];
+
+ // now set ourselves as the delegate to receive new frames.
+ [output setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
+
+ // this takes ownership
+ [self addOutput:output];
+ }
+
+ [self commitConfiguration];
+
+ // kick off our session..
+ [self startRunning];
+ };
+ return self;
+}
+
+- (void)cleanup {
+ // stop running
+ [self stopRunning];
+
+ // cleanup
+ [self beginConfiguration];
+
+ // remove input
+ if (input) {
+ [self removeInput:input];
+ // don't release this
+ input = NULL;
+ }
+
+ // free up our output
+ if (output) {
+ [self removeOutput:output];
+ [output setSampleBufferDelegate:nil queue:NULL];
+ [output release];
+ output = NULL;
+ }
+
+ [self commitConfiguration];
+}
+
+- (void)dealloc {
+ // bye bye
+ [super dealloc];
+}
+
+- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
+ // This gets called every time our camera has a new image for us to process.
+ // May need to investigate in a way to throttle this if we get more images then we're rendering frames..
+
+ // For now, version 1, we're just doing the bare minimum to make this work...
+ CVImageBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
+ // int _width = CVPixelBufferGetWidth(pixelBuffer);
+ // int _height = CVPixelBufferGetHeight(pixelBuffer);
+
+ // It says that we need to lock this on the documentation pages but it's not in the samples
+ // need to lock our base address so we can access our pixel buffers, better safe then sorry?
+ CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
+
+ // get our buffers
+ unsigned char *dataY = (unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);
+ unsigned char *dataCbCr = (unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1);
+ if (dataY == NULL) {
+ print_line("Couldn't access Y pixel buffer data");
+ } else if (dataCbCr == NULL) {
+ print_line("Couldn't access CbCr pixel buffer data");
+ } else {
+ Ref<Image> img[2];
+
+ {
+ // do Y
+ int new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
+ int new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
+
+ if ((width[0] != new_width) || (height[0] != new_height)) {
+ width[0] = new_width;
+ height[0] = new_height;
+ img_data[0].resize(new_width * new_height);
+ }
+
+ PoolVector<uint8_t>::Write w = img_data[0].write();
+ memcpy(w.ptr(), dataY, new_width * new_height);
+
+ img[0].instance();
+ img[0]->create(new_width, new_height, 0, Image::FORMAT_R8, img_data[0]);
+ }
+
+ {
+ // do CbCr
+ int new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 1);
+ int new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 1);
+
+ if ((width[1] != new_width) || (height[1] != new_height)) {
+ width[1] = new_width;
+ height[1] = new_height;
+ img_data[1].resize(2 * new_width * new_height);
+ }
+
+ PoolVector<uint8_t>::Write w = img_data[1].write();
+ memcpy(w.ptr(), dataCbCr, 2 * new_width * new_height);
+
+ ///TODO GLES2 doesn't support FORMAT_RG8, need to do some form of conversion
+ img[1].instance();
+ img[1]->create(new_width, new_height, 0, Image::FORMAT_RG8, img_data[1]);
+ }
+
+ // set our texture...
+ feed->set_YCbCr_imgs(img[0], img[1]);
+ }
+
+ // and unlock
+ CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
+}
+
+@end
+
+//////////////////////////////////////////////////////////////////////////
+// CameraFeedOSX - Subclass for camera feeds in OSX
+
+class CameraFeedOSX : public CameraFeed {
+private:
+ AVCaptureDevice *device;
+ MyCaptureSession *capture_session;
+
+public:
+ AVCaptureDevice *get_device() const;
+
+ CameraFeedOSX();
+ ~CameraFeedOSX();
+
+ void set_device(AVCaptureDevice *p_device);
+
+ bool activate_feed();
+ void deactivate_feed();
+};
+
+AVCaptureDevice *CameraFeedOSX::get_device() const {
+ return device;
+};
+
+CameraFeedOSX::CameraFeedOSX() {
+ device = NULL;
+ capture_session = NULL;
+};
+
+void CameraFeedOSX::set_device(AVCaptureDevice *p_device) {
+ device = p_device;
+ [device retain];
+
+ // get some info
+ NSString *device_name = p_device.localizedName;
+ name = device_name.UTF8String;
+ position = CameraFeed::FEED_UNSPECIFIED;
+ if ([p_device position] == AVCaptureDevicePositionBack) {
+ position = CameraFeed::FEED_BACK;
+ } else if ([p_device position] == AVCaptureDevicePositionFront) {
+ position = CameraFeed::FEED_FRONT;
+ };
+};
+
+CameraFeedOSX::~CameraFeedOSX() {
+ if (capture_session != NULL) {
+ [capture_session release];
+ capture_session = NULL;
+ };
+
+ if (device != NULL) {
+ [device release];
+ device = NULL;
+ };
+};
+
+bool CameraFeedOSX::activate_feed() {
+ if (capture_session) {
+ // already recording!
+ } else {
+ // start camera capture
+ capture_session = [[MyCaptureSession alloc] initForFeed:this andDevice:device];
+ };
+
+ return true;
+};
+
+void CameraFeedOSX::deactivate_feed() {
+ // end camera capture if we have one
+ if (capture_session) {
+ [capture_session cleanup];
+ [capture_session release];
+ capture_session = NULL;
+ };
+};
+
+//////////////////////////////////////////////////////////////////////////
+// MyDeviceNotifications - This is a little helper class gets notifications
+// when devices are connected/disconnected
+
+@interface MyDeviceNotifications : NSObject {
+ CameraOSX *camera_server;
+}
+
+@end
+
+@implementation MyDeviceNotifications
+
+- (void)devices_changed:(NSNotification *)notification {
+ camera_server->update_feeds();
+}
+
+- (id)initForServer:(CameraOSX *)p_server {
+ if (self = [super init]) {
+ camera_server = p_server;
+
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(devices_changed:) name:AVCaptureDeviceWasConnectedNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(devices_changed:) name:AVCaptureDeviceWasDisconnectedNotification object:nil];
+ };
+ return self;
+}
+
+- (void)dealloc {
+ // remove notifications
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceWasConnectedNotification object:nil];
+ [[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceWasDisconnectedNotification object:nil];
+
+ [super dealloc];
+}
+
+@end
+
+MyDeviceNotifications *device_notifications = nil;
+
+//////////////////////////////////////////////////////////////////////////
+// CameraOSX - Subclass for our camera server on OSX
+
+void CameraOSX::update_feeds() {
+ NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
+
+ // remove devices that are gone..
+ for (int i = feeds.size() - 1; i >= 0; i--) {
+ Ref<CameraFeedOSX> feed = (Ref<CameraFeedOSX>)feeds[i];
+
+ if (![devices containsObject:feed->get_device()]) {
+ // remove it from our array, this will also destroy it ;)
+ remove_feed(feed);
+ };
+ };
+
+ // add new devices..
+ for (AVCaptureDevice *device in devices) {
+ bool found = false;
+ for (int i = 0; i < feeds.size() && !found; i++) {
+ Ref<CameraFeedOSX> feed = (Ref<CameraFeedOSX>)feeds[i];
+ if (feed->get_device() == device) {
+ found = true;
+ };
+ };
+
+ if (!found) {
+ Ref<CameraFeedOSX> newfeed;
+ newfeed.instance();
+ newfeed->set_device(device);
+
+ // assume display camera so inverse
+ Transform2D transform = Transform2D(-1.0, 0.0, 0.0, -1.0, 1.0, 1.0);
+ newfeed->set_transform(transform);
+
+ add_feed(newfeed);
+ };
+ };
+};
+
+CameraOSX::CameraOSX() {
+ // Find available cameras we have at this time
+ update_feeds();
+
+ // should only have one of these....
+ device_notifications = [[MyDeviceNotifications alloc] initForServer:this];
+};
+
+CameraOSX::~CameraOSX() {
+ [device_notifications release];
+};
diff --git a/platform/osx/detect.py b/platform/osx/detect.py
index 4c88f91d13..881ed05025 100644
--- a/platform/osx/detect.py
+++ b/platform/osx/detect.py
@@ -56,7 +56,7 @@ def configure(env):
env.Prepend(CCFLAGS=['-O2'])
else: #optimize for size
env.Prepend(CCFLAGS=['-Os'])
- env.Prepend(CPPFLAGS=['-DDEBUG_ENABLED'])
+ env.Prepend(CPPDEFINES=['DEBUG_ENABLED'])
if (env["debug_symbols"] == "yes"):
env.Prepend(CCFLAGS=['-g1'])
if (env["debug_symbols"] == "full"):
@@ -64,7 +64,7 @@ def configure(env):
elif (env["target"] == "debug"):
env.Prepend(CCFLAGS=['-g3'])
- env.Prepend(CPPFLAGS=['-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED'])
+ env.Prepend(CPPDEFINES=['DEBUG_ENABLED', 'DEBUG_MEMORY_ENABLED'])
## Architecture
@@ -90,7 +90,7 @@ def configure(env):
env['AR'] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/llvm-ar"
env['RANLIB'] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/llvm-ranlib"
env['AS'] = mpprefix + "/libexec/llvm-" + mpclangver + "/bin/llvm-as"
- env.Append(CPPFLAGS=['-D__MACPORTS__']) #hack to fix libvpx MM256_BROADCASTSI128_SI256 define
+ env.Append(CPPDEFINES=['__MACPORTS__']) #hack to fix libvpx MM256_BROADCASTSI128_SI256 define
detect_darwin_sdk_path('osx', env)
env.Append(CCFLAGS=['-isysroot', '$MACOS_SDK_PATH'])
@@ -112,10 +112,10 @@ def configure(env):
env['AR'] = basecmd + "ar"
env['RANLIB'] = basecmd + "ranlib"
env['AS'] = basecmd + "as"
- env.Append(CPPFLAGS=['-D__MACPORTS__']) #hack to fix libvpx MM256_BROADCASTSI128_SI256 define
+ env.Append(CPPDEFINES=['__MACPORTS__']) #hack to fix libvpx MM256_BROADCASTSI128_SI256 define
if (env["CXX"] == "clang++"):
- env.Append(CPPFLAGS=['-DTYPED_METHOD_BIND'])
+ env.Append(CPPDEFINES=['TYPED_METHOD_BIND'])
env["CC"] = "clang"
env["LINK"] = "clang++"
@@ -127,8 +127,8 @@ def configure(env):
## Flags
env.Prepend(CPPPATH=['#platform/osx'])
- env.Append(CPPFLAGS=['-DOSX_ENABLED', '-DUNIX_ENABLED', '-DGLES_ENABLED', '-DAPPLE_STYLE_KEYS', '-DCOREAUDIO_ENABLED', '-DCOREMIDI_ENABLED'])
- env.Append(LINKFLAGS=['-framework', 'Cocoa', '-framework', 'Carbon', '-framework', 'OpenGL', '-framework', 'AGL', '-framework', 'AudioUnit', '-framework', 'CoreAudio', '-framework', 'CoreMIDI', '-lz', '-framework', 'IOKit', '-framework', 'ForceFeedback', '-framework', 'CoreVideo'])
+ env.Append(CPPDEFINES=['OSX_ENABLED', 'UNIX_ENABLED', 'GLES_ENABLED', 'APPLE_STYLE_KEYS', 'COREAUDIO_ENABLED', 'COREMIDI_ENABLED'])
+ env.Append(LINKFLAGS=['-framework', 'Cocoa', '-framework', 'Carbon', '-framework', 'OpenGL', '-framework', 'AGL', '-framework', 'AudioUnit', '-framework', 'CoreAudio', '-framework', 'CoreMIDI', '-lz', '-framework', 'IOKit', '-framework', 'ForceFeedback', '-framework', 'AVFoundation', '-framework', 'CoreMedia', '-framework', 'CoreVideo'])
env.Append(LIBS=['pthread'])
env.Append(CCFLAGS=['-mmacosx-version-min=10.9'])
diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp
index 9dabbb12fc..8cabc45250 100644
--- a/platform/osx/export/export.cpp
+++ b/platform/osx/export/export.cpp
@@ -570,7 +570,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
if (export_format == "dmg") {
// write it into our application bundle
- file = tmp_app_path_name + "/" + file;
+ file = tmp_app_path_name.plus_file(file);
// write the file, need to add chmod
FileAccess *f = FileAccess::open(file, FileAccess::WRITE);
diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h
index eed230ba89..a83d5084ed 100644
--- a/platform/osx/os_osx.h
+++ b/platform/osx/os_osx.h
@@ -31,6 +31,7 @@
#ifndef OS_OSX_H
#define OS_OSX_H
+#include "camera_osx.h"
#include "core/os/input.h"
#include "crash_handler_osx.h"
#include "drivers/coreaudio/audio_driver_coreaudio.h"
@@ -73,6 +74,8 @@ public:
//Rasterizer *rasterizer;
VisualServer *visual_server;
+ CameraServer *camera_server;
+
List<String> args;
MainLoop *main_loop;
@@ -116,6 +119,7 @@ public:
CursorShape cursor_shape;
NSCursor *cursors[CURSOR_MAX];
+ Map<CursorShape, Vector<Variant> > cursors_cache;
MouseMode mouse_mode;
String title;
@@ -134,6 +138,9 @@ public:
String im_text;
Point2 im_selection;
+ Size2 min_size;
+ Size2 max_size;
+
PowerOSX *power_manager;
CrashHandler crash_handler;
@@ -182,6 +189,7 @@ public:
virtual void warp_mouse_position(const Point2 &p_to);
virtual Point2 get_mouse_position() const;
virtual int get_mouse_button_state() const;
+ void update_real_mouse_position();
virtual void set_window_title(const String &p_title);
virtual Size2 get_window_size() const;
@@ -232,6 +240,10 @@ public:
virtual Point2 get_window_position() const;
virtual void set_window_position(const Point2 &p_position);
+ virtual Size2 get_max_window_size() const;
+ virtual Size2 get_min_window_size() const;
+ virtual void set_min_window_size(const Size2 p_size);
+ virtual void set_max_window_size(const Size2 p_size);
virtual void set_window_size(const Size2 p_size);
virtual void set_window_fullscreen(bool p_enabled);
virtual bool is_window_fullscreen() const;
diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm
index 567d24de3e..726882438b 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -267,10 +267,23 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
- (void)windowDidEnterFullScreen:(NSNotification *)notification {
OS_OSX::singleton->zoomed = true;
+
+ [OS_OSX::singleton->window_object setContentMinSize:NSMakeSize(0, 0)];
+ [OS_OSX::singleton->window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
}
- (void)windowDidExitFullScreen:(NSNotification *)notification {
OS_OSX::singleton->zoomed = false;
+
+ if (OS_OSX::singleton->min_size != Size2()) {
+ Size2 size = OS_OSX::singleton->min_size / OS_OSX::singleton->_display_scale();
+ [OS_OSX::singleton->window_object setContentMinSize:NSMakeSize(size.x, size.y)];
+ }
+ if (OS_OSX::singleton->max_size != Size2()) {
+ Size2 size = OS_OSX::singleton->max_size / OS_OSX::singleton->_display_scale();
+ [OS_OSX::singleton->window_object setContentMaxSize:NSMakeSize(size.x, size.y)];
+ }
+
if (!OS_OSX::singleton->resizable)
[OS_OSX::singleton->window_object setStyleMask:[OS_OSX::singleton->window_object styleMask] & ~NSWindowStyleMaskResizable];
}
@@ -1002,7 +1015,7 @@ static const _KeyCodeMap _keycodes[55] = {
{ '/', KEY_SLASH }
};
-static int remapKey(unsigned int key) {
+static int remapKey(unsigned int key, unsigned int state) {
if (isNumpadKey(key))
return translateKey(key);
@@ -1024,7 +1037,7 @@ static int remapKey(unsigned int key) {
OSStatus err = UCKeyTranslate(keyboardLayout,
key,
kUCKeyActionDisplay,
- 0,
+ (state >> 8) & 0xFF,
LMGetKbdType(),
kUCKeyTranslateNoDeadKeysBit,
&keysDown,
@@ -1051,7 +1064,7 @@ static int remapKey(unsigned int key) {
NSString *characters = [event characters];
NSUInteger length = [characters length];
- if (!OS_OSX::singleton->im_active && length > 0 && keycode_has_unicode(remapKey([event keyCode]))) {
+ if (!OS_OSX::singleton->im_active && length > 0 && keycode_has_unicode(remapKey([event keyCode], [event modifierFlags]))) {
// Fallback unicode character handler used if IME is not active
for (NSUInteger i = 0; i < length; i++) {
OS_OSX::KeyEvent ke;
@@ -1059,7 +1072,7 @@ static int remapKey(unsigned int key) {
ke.osx_state = [event modifierFlags];
ke.pressed = true;
ke.echo = [event isARepeat];
- ke.scancode = remapKey([event keyCode]);
+ ke.scancode = remapKey([event keyCode], [event modifierFlags]);
ke.raw = true;
ke.unicode = [characters characterAtIndex:i];
@@ -1071,7 +1084,7 @@ static int remapKey(unsigned int key) {
ke.osx_state = [event modifierFlags];
ke.pressed = true;
ke.echo = [event isARepeat];
- ke.scancode = remapKey([event keyCode]);
+ ke.scancode = remapKey([event keyCode], [event modifierFlags]);
ke.raw = false;
ke.unicode = 0;
@@ -1129,7 +1142,7 @@ static int remapKey(unsigned int key) {
}
ke.osx_state = mod;
- ke.scancode = remapKey(key);
+ ke.scancode = remapKey(key, mod);
ke.unicode = 0;
push_to_key_event_buffer(ke);
@@ -1144,14 +1157,14 @@ static int remapKey(unsigned int key) {
NSUInteger length = [characters length];
// Fallback unicode character handler used if IME is not active
- if (!OS_OSX::singleton->im_active && length > 0 && keycode_has_unicode(remapKey([event keyCode]))) {
+ if (!OS_OSX::singleton->im_active && length > 0 && keycode_has_unicode(remapKey([event keyCode], [event modifierFlags]))) {
for (NSUInteger i = 0; i < length; i++) {
OS_OSX::KeyEvent ke;
ke.osx_state = [event modifierFlags];
ke.pressed = false;
ke.echo = [event isARepeat];
- ke.scancode = remapKey([event keyCode]);
+ ke.scancode = remapKey([event keyCode], [event modifierFlags]);
ke.raw = true;
ke.unicode = [characters characterAtIndex:i];
@@ -1163,7 +1176,7 @@ static int remapKey(unsigned int key) {
ke.osx_state = [event modifierFlags];
ke.pressed = false;
ke.echo = [event isARepeat];
- ke.scancode = remapKey([event keyCode]);
+ ke.scancode = remapKey([event keyCode], [event modifierFlags]);
ke.raw = true;
ke.unicode = 0;
@@ -1542,6 +1555,8 @@ Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
visual_server->init();
AudioDriverManager::initialize(p_audio_driver);
+ camera_server = memnew(CameraOSX);
+
input = memnew(InputDefault);
joypad_osx = memnew(JoypadOSX);
@@ -1551,9 +1566,12 @@ Error OS_OSX::initialize(const VideoMode &p_desired, int p_video_driver, int p_a
restore_rect = Rect2(get_window_position(), get_window_size());
- if (p_desired.layered_splash) {
+ if (p_desired.layered) {
set_window_per_pixel_transparency_enabled(true);
}
+
+ update_real_mouse_position();
+
return OK;
}
@@ -1573,6 +1591,11 @@ void OS_OSX::finalize() {
delete_main_loop();
+ if (camera_server) {
+ memdelete(camera_server);
+ camera_server = NULL;
+ }
+
memdelete(joypad_osx);
memdelete(input);
@@ -1747,7 +1770,20 @@ OS::CursorShape OS_OSX::get_cursor_shape() const {
}
void OS_OSX::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) {
+
if (p_cursor.is_valid()) {
+
+ Map<CursorShape, Vector<Variant> >::Element *cursor_c = cursors_cache.find(p_shape);
+
+ if (cursor_c) {
+ if (cursor_c->get()[0] == p_cursor && cursor_c->get()[1] == p_hotspot) {
+ set_cursor_shape(p_shape);
+ return;
+ }
+
+ cursors_cache.erase(p_shape);
+ }
+
Ref<Texture> texture = p_cursor;
Ref<AtlasTexture> atlas_texture = p_cursor;
Ref<Image> image;
@@ -1832,6 +1868,11 @@ void OS_OSX::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c
[cursors[p_shape] release];
cursors[p_shape] = cursor;
+ Vector<Variant> params;
+ params.push_back(p_cursor);
+ params.push_back(p_hotspot);
+ cursors_cache.insert(p_shape, params);
+
if (p_shape == cursor_shape) {
if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) {
[cursor set];
@@ -1887,6 +1928,12 @@ void OS_OSX::warp_mouse_position(const Point2 &p_to) {
}
}
+void OS_OSX::update_real_mouse_position() {
+
+ get_mouse_pos([window_object mouseLocationOutsideOfEventStream], [window_view backingScaleFactor]);
+ input->set_mouse_position(Point2(mouse_x, mouse_y));
+}
+
Point2 OS_OSX::get_mouse_position() const {
return Vector2(mouse_x, mouse_y);
@@ -2337,6 +2384,8 @@ void OS_OSX::set_window_position(const Point2 &p_position) {
// Godot passes a positive value
position.y *= -1;
set_native_window_position(get_screens_origin() + position);
+
+ update_real_mouse_position();
};
Size2 OS_OSX::get_window_size() const {
@@ -2350,6 +2399,46 @@ Size2 OS_OSX::get_real_window_size() const {
return Size2(frame.size.width, frame.size.height) * _display_scale();
}
+Size2 OS_OSX::get_max_window_size() const {
+ return max_size;
+}
+
+Size2 OS_OSX::get_min_window_size() const {
+ return min_size;
+}
+
+void OS_OSX::set_min_window_size(const Size2 p_size) {
+
+ if ((p_size != Size2()) && (max_size != Size2()) && ((p_size.x > max_size.x) || (p_size.y > max_size.y))) {
+ WARN_PRINT("Minimum window size can't be larger than maximum window size!");
+ return;
+ }
+ min_size = p_size;
+
+ if ((min_size != Size2()) && !zoomed) {
+ Size2 size = min_size / _display_scale();
+ [window_object setContentMinSize:NSMakeSize(size.x, size.y)];
+ } else {
+ [window_object setContentMinSize:NSMakeSize(0, 0)];
+ }
+}
+
+void OS_OSX::set_max_window_size(const Size2 p_size) {
+
+ if ((p_size != Size2()) && ((p_size.x < min_size.x) || (p_size.y < min_size.y))) {
+ WARN_PRINT("Maximum window size can't be smaller than minimum window size!");
+ return;
+ }
+ max_size = p_size;
+
+ if ((max_size != Size2()) && !zoomed) {
+ Size2 size = max_size / _display_scale();
+ [window_object setContentMaxSize:NSMakeSize(size.x, size.y)];
+ } else {
+ [window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
+ }
+}
+
void OS_OSX::set_window_size(const Size2 p_size) {
Size2 size = p_size / _display_scale();
@@ -2379,6 +2468,19 @@ void OS_OSX::set_window_fullscreen(bool p_enabled) {
set_window_per_pixel_transparency_enabled(false);
if (!resizable)
[window_object setStyleMask:[window_object styleMask] | NSWindowStyleMaskResizable];
+ if (p_enabled) {
+ [window_object setContentMinSize:NSMakeSize(0, 0)];
+ [window_object setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
+ } else {
+ if (min_size != Size2()) {
+ Size2 size = min_size / _display_scale();
+ [window_object setContentMinSize:NSMakeSize(size.x, size.y)];
+ }
+ if (max_size != Size2()) {
+ Size2 size = max_size / _display_scale();
+ [window_object setContentMaxSize:NSMakeSize(size.x, size.y)];
+ }
+ }
[window_object toggleFullScreen:nil];
}
zoomed = p_enabled;