diff options
Diffstat (limited to 'platform')
40 files changed, 1327 insertions, 95 deletions
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index b987b3aebd..30267aa968 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -790,7 +790,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform { if (tname == "manifest" && attrname == "versionName") { if (attr_value == 0xFFFFFFFF) { - WARN_PRINT("Version name in a resource, should be plaintext") + WARN_PRINT("Version name in a resource, should be plain text"); } else string_table.write[attr_value] = version_name; } diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index f8076dfd2d..ebc319e57d 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -176,6 +176,9 @@ Error OS_Android::initialize(const VideoMode &p_desired, int p_video_driver, int input = memnew(InputDefault); input->set_fallback_mapping("Default Android Gamepad"); + ///@TODO implement a subclass for Android and instantiate that instead + camera_server = memnew(CameraServer); + //power_manager = memnew(PowerAndroid); return OK; @@ -193,6 +196,9 @@ void OS_Android::delete_main_loop() { } void OS_Android::finalize() { + + memdelete(camera_server); + memdelete(input); } diff --git a/platform/android/os_android.h b/platform/android/os_android.h index 4dbc96f4da..e74d4cfd43 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -39,6 +39,7 @@ #include "main/input_default.h" //#include "power_android.h" #include "servers/audio_server.h" +#include "servers/camera_server.h" #include "servers/visual/rasterizer.h" class GodotJavaWrapper; @@ -77,6 +78,8 @@ private: VisualServer *visual_server; + CameraServer *camera_server; + mutable String data_dir_cache; //AudioDriverAndroid audio_driver_android; diff --git a/platform/haiku/os_haiku.cpp b/platform/haiku/os_haiku.cpp index 438b50053f..9c07535c85 100644 --- a/platform/haiku/os_haiku.cpp +++ b/platform/haiku/os_haiku.cpp @@ -133,6 +133,8 @@ Error OS_Haiku::initialize(const VideoMode &p_desired, int p_video_driver, int p window->Show(); visual_server->init(); + camera_server = memnew(CameraServer); + AudioDriverManager::initialize(p_audio_driver); return OK; @@ -148,6 +150,8 @@ void OS_Haiku::finalize() { visual_server->finish(); memdelete(visual_server); + memdelete(camera_server); + memdelete(input); #if defined(OPENGL_ENABLED) diff --git a/platform/haiku/os_haiku.h b/platform/haiku/os_haiku.h index e1d4cf8d87..70d78a1978 100644 --- a/platform/haiku/os_haiku.h +++ b/platform/haiku/os_haiku.h @@ -38,6 +38,7 @@ #include "haiku_direct_window.h" #include "main/input_default.h" #include "servers/audio_server.h" +#include "servers/camera_server.h" #include "servers/visual_server.h" class OS_Haiku : public OS_Unix { @@ -49,6 +50,7 @@ private: VisualServer *visual_server; VideoMode current_video_mode; int video_driver_index; + CameraServer *camera_server; #ifdef MEDIA_KIT_ENABLED AudioDriverMediaKit driver_media_kit; diff --git a/platform/iphone/SCsub b/platform/iphone/SCsub index fa1b124561..85ba56165b 100644 --- a/platform/iphone/SCsub +++ b/platform/iphone/SCsub @@ -14,6 +14,7 @@ iphone_lib = [ 'in_app_store.mm', 'icloud.mm', 'ios.mm', + 'camera_ios.mm', ] env_ios = env.Clone() diff --git a/platform/iphone/camera_ios.h b/platform/iphone/camera_ios.h new file mode 100644 index 0000000000..e5d62af65d --- /dev/null +++ b/platform/iphone/camera_ios.h @@ -0,0 +1,47 @@ +/*************************************************************************/ +/* camera_ios.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 CAMERAIOS_H +#define CAMERAIOS_H + +///@TODO this is a near duplicate of CameraOSX, 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 CameraIOS : public CameraServer { +public: + CameraIOS(); + ~CameraIOS(); + + void update_feeds(); +}; + +#endif /* CAMERAIOS_H */
\ No newline at end of file diff --git a/platform/iphone/camera_ios.mm b/platform/iphone/camera_ios.mm new file mode 100644 index 0000000000..3994e9366a --- /dev/null +++ b/platform/iphone/camera_ios.mm @@ -0,0 +1,429 @@ +/*************************************************************************/ +/* camera_ios.mm */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 CameraOSX, 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_ios.h" +#include "servers/camera/camera_feed.h" + +#import <AVFoundation/AVFoundation.h> +#import <UIKit/UIKit.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; + + // prepare our device + [p_device lockForConfiguration:&error]; + + [p_device setFocusMode:AVCaptureFocusModeLocked]; + [p_device setExposureMode:AVCaptureExposureModeLocked]; + [p_device setWhiteBalanceMode:AVCaptureWhiteBalanceModeLocked]; + + [p_device unlockForConfiguration]; + + [self beginConfiguration]; + + // setup our capture + self.sessionPreset = AVCaptureSessionPreset1280x720; + + 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. Note that we're doing this on the main thread at the moment, we may need to change this.. + [output setSampleBufferDelegate:self queue:dispatch_get_main_queue()]; + + [self addOutput:output]; + } + + [self commitConfiguration]; + + // kick off our session.. + [self startRunning]; + }; + return self; +} + +- (void)cleanup { + // stop running + [self stopRunning]; + + // cleanup + [self beginConfiguration]; + + if (input) { + [self removeInput:input]; + // don't release this + input = nil; + } + + if (output) { + [self removeOutput:output]; + [output setSampleBufferDelegate:nil queue:NULL]; + [output release]; + output = nil; + } + + [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 { + UIDeviceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation]; + Ref<Image> img[2]; + + { + // do Y + int new_width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0); + int new_height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0); + int _bytes_per_row = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0); + + if ((width[0] != new_width) || (height[0] != new_height)) { + // printf("Camera Y plane %i, %i - %i\n", new_width, new_height, bytes_per_row); + + 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); + int bytes_per_row = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1); + + if ((width[1] != new_width) || (height[1] != new_height)) { + // printf("Camera CbCr plane %i, %i - %i\n", new_width, new_height, bytes_per_row); + + 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]); + + // update our matrix to match the orientation, note, before changing anything + // here, be aware that the project orientation settings must match your xcode + // settings or this will go wrong! + Transform2D display_transform; + switch (orientation) { + case UIInterfaceOrientationPortrait: { + display_transform = Transform2D(0.0, -1.0, -1.0, 0.0, 1.0, 1.0); + } break; + case UIInterfaceOrientationLandscapeRight: { + display_transform = Transform2D(1.0, 0.0, 0.0, -1.0, 0.0, 1.0); + } break; + case UIInterfaceOrientationLandscapeLeft: { + display_transform = Transform2D(-1.0, 0.0, 0.0, 1.0, 1.0, 0.0); + } break; + default: { + display_transform = Transform2D(0.0, 1.0, 1.0, 0.0, 0.0, 0.0); + } break; + } + + //TODO: this is correct for the camera on the back, I have a feeling this needs to be inversed for the camera on the front! + feed->set_transform(display_transform); + } + + // and unlock + CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly); +} + +@end + +////////////////////////////////////////////////////////////////////////// +// CameraFeedIOS - Subclass for camera feeds in iOS + +class CameraFeedIOS : public CameraFeed { +private: + bool is_arkit; // if true this feed is updated through ARKit (should only have one and not yet implemented) + AVCaptureDevice *device; + MyCaptureSession *capture_session; + +public: + bool get_is_arkit() const; + AVCaptureDevice *get_device() const; + + CameraFeedIOS(); + ~CameraFeedIOS(); + + void set_device(AVCaptureDevice *p_device); + + bool activate_feed(); + void deactivate_feed(); +}; + +bool CameraFeedIOS::get_is_arkit() const { + return is_arkit; +}; + +AVCaptureDevice *CameraFeedIOS::get_device() const { + return device; +}; + +CameraFeedIOS::CameraFeedIOS() { + capture_session = NULL; + device = NULL; + transform = Transform2D(1.0, 0.0, 0.0, 1.0, 0.0, 0.0); /* should re-orientate this based on device orientation */ +}; + +void CameraFeedIOS::set_device(AVCaptureDevice *p_device) { + device = p_device; + if (device == NULL) { + ///@TODO finish this! + is_arkit = true; + name = "ARKit"; + position = CameraFeed::FEED_BACK; + } else { + is_arkit = false; + [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; + }; + }; +}; + +CameraFeedIOS::~CameraFeedIOS() { + if (capture_session != NULL) { + [capture_session release]; + capture_session = NULL; + }; + + if (device != NULL) { + [device release]; + device = NULL; + }; +}; + +bool CameraFeedIOS::activate_feed() { + if (is_arkit) { + ///@TODO to implement; + } else { + if (capture_session) { + // already recording! + } else { + // start camera capture + capture_session = [[MyCaptureSession alloc] initForFeed:this andDevice:device]; + }; + }; + + return true; +}; + +void CameraFeedIOS::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 { + CameraIOS *camera_server; +} + +@end + +@implementation MyDeviceNotifications + +- (void)devices_changed:(NSNotification *)notification { + camera_server->update_feeds(); +} + +- (id)initForServer:(CameraIOS *)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; + +////////////////////////////////////////////////////////////////////////// +// CameraIOS - Subclass for our camera server on iPhone + +void CameraIOS::update_feeds() { + // this way of doing things is deprecated but still works, + // rewrite to using AVCaptureDeviceDiscoverySession + + AVCaptureDeviceDiscoverySession *session = [AVCaptureDeviceDiscoverySession discoverySessionWithDeviceTypes:[NSArray arrayWithObjects:AVCaptureDeviceTypeBuiltInTelephotoCamera, AVCaptureDeviceTypeBuiltInDualCamera, AVCaptureDeviceTypeBuiltInTrueDepthCamera] mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionUnspecified]; + + // remove devices that are gone.. + for (int i = feeds.size() - 1; i >= 0; i--) { + Ref<CameraFeedIOS> feed = (Ref<CameraFeedIOS>)feeds[i]; + + if (feed->get_is_arkit()) { + // ignore, this is our arkit entry + } else if (![session.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 session.devices) { + bool found = false; + for (int i = 0; i < feeds.size() && !found; i++) { + Ref<CameraFeedIOS> feed = (Ref<CameraFeedIOS>)feeds[i]; + if (feed->get_device() == device) { + found = true; + }; + }; + + if (!found) { + Ref<CameraFeedIOS> newfeed; + newfeed.instance(); + newfeed->set_device(device); + add_feed(newfeed); + }; + }; +}; + +CameraIOS::CameraIOS() { + [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo + completionHandler:^(BOOL granted) { + if (granted) { + // Find available cameras we have at this time + update_feeds(); + + // should only have one of these.... + device_notifications = [[MyDeviceNotifications alloc] initForServer:this]; + } else { + print_line("No access to cameras!"); + } + }]; +}; + +CameraIOS::~CameraIOS() { + [device_notifications release]; +}; diff --git a/platform/iphone/detect.py b/platform/iphone/detect.py index d9f710e456..daf4f405fd 100644 --- a/platform/iphone/detect.py +++ b/platform/iphone/detect.py @@ -144,6 +144,7 @@ def configure(env): '-framework', 'CoreAudio', '-framework', 'CoreGraphics', '-framework', 'CoreMedia', + '-framework', 'CoreVideo', '-framework', 'CoreMotion', '-framework', 'Foundation', '-framework', 'GameController', diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp index ba405ab7ae..7ca83320d0 100644 --- a/platform/iphone/export/export.cpp +++ b/platform/iphone/export/export.cpp @@ -914,7 +914,7 @@ Error EditorExportPlatformIOS::export_project(const Ref<EditorExportPreset> &p_p }; DirAccess *tmp_app_path = DirAccess::create_for_path(dest_dir); - ERR_FAIL_COND_V(!tmp_app_path, ERR_CANT_CREATE) + ERR_FAIL_COND_V(!tmp_app_path, ERR_CANT_CREATE); print_line("Unzipping..."); FileAccess *src_f = NULL; diff --git a/platform/iphone/icloud.mm b/platform/iphone/icloud.mm index e32618e8f6..c60db3d661 100644 --- a/platform/iphone/icloud.mm +++ b/platform/iphone/icloud.mm @@ -138,7 +138,7 @@ Variant nsobject_to_variant(NSObject *object) { //this is a type that icloud supports...but how did you submit it in the first place? //I guess this is a type that *might* show up, if you were, say, trying to make your game //compatible with existing cloud data written by another engine's version of your game - WARN_PRINT("NSDate unsupported, returning null Variant") + WARN_PRINT("NSDate unsupported, returning null Variant"); return Variant(); } else if ([object isKindOfClass:[NSNull class]] or object == nil) { return Variant(); diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp index 6a65cadf09..f5fce66059 100644 --- a/platform/iphone/os_iphone.cpp +++ b/platform/iphone/os_iphone.cpp @@ -167,6 +167,8 @@ Error OSIPhone::initialize(const VideoMode &p_desired, int p_video_driver, int p input = memnew(InputDefault); + camera_server = memnew(CameraIOS); + #ifdef GAME_CENTER_ENABLED game_center = memnew(GameCenter); Engine::get_singleton()->add_singleton(Engine::Singleton("GameCenter", game_center)); @@ -361,6 +363,11 @@ void OSIPhone::finalize() { if (main_loop) // should not happen? memdelete(main_loop); + if (camera_server) { + memdelete(camera_server); + camera_server = NULL; + } + visual_server->finish(); memdelete(visual_server); // memdelete(rasterizer); diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h index 017125209c..c16c29a858 100644 --- a/platform/iphone/os_iphone.h +++ b/platform/iphone/os_iphone.h @@ -37,6 +37,7 @@ #include "drivers/coreaudio/audio_driver_coreaudio.h" #include "drivers/unix/os_unix.h" +#include "camera_ios.h" #include "game_center.h" #include "icloud.h" #include "in_app_store.h" @@ -60,6 +61,8 @@ private: AudioDriverCoreAudio audio_driver; + CameraServer *camera_server; + #ifdef GAME_CENTER_ENABLED GameCenter *game_center; #endif diff --git a/platform/javascript/audio_driver_javascript.cpp b/platform/javascript/audio_driver_javascript.cpp index 11104007e2..163826f828 100644 --- a/platform/javascript/audio_driver_javascript.cpp +++ b/platform/javascript/audio_driver_javascript.cpp @@ -99,7 +99,7 @@ Error AudioDriverJavaScript::init() { return FAILED; } - if (!internal_buffer || memarr_len(internal_buffer) != buffer_length * channel_count) { + if (!internal_buffer || (int)memarr_len(internal_buffer) != buffer_length * channel_count) { if (internal_buffer) memdelete_arr(internal_buffer); internal_buffer = memnew_arr(float, buffer_length *channel_count); diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp index c69e6f0cb8..d96ffc3a55 100644 --- a/platform/javascript/os_javascript.cpp +++ b/platform/javascript/os_javascript.cpp @@ -70,6 +70,20 @@ static bool is_canvas_focused() { /* clang-format on */ } +static Point2 correct_canvas_position(int x, int y) { + int canvas_width; + int canvas_height; + emscripten_get_canvas_element_size(NULL, &canvas_width, &canvas_height); + + double element_width; + double element_height; + emscripten_get_element_css_size(NULL, &element_width, &element_height); + + x = (int)(canvas_width / element_width * x); + y = (int)(canvas_height / element_height * y); + return Point2(x, y); +} + static bool cursor_inside_canvas = true; EM_BOOL OS_JavaScript::fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data) { @@ -285,7 +299,7 @@ EM_BOOL OS_JavaScript::mouse_button_callback(int p_event_type, const EmscriptenM Ref<InputEventMouseButton> ev; ev.instance(); ev->set_pressed(p_event_type == EMSCRIPTEN_EVENT_MOUSEDOWN); - ev->set_position(Point2(p_event->canvasX, p_event->canvasY)); + ev->set_position(correct_canvas_position(p_event->canvasX, p_event->canvasY)); ev->set_global_position(ev->get_position()); dom2godot_mod(p_event, ev); switch (p_event->button) { @@ -349,7 +363,7 @@ EM_BOOL OS_JavaScript::mousemove_callback(int p_event_type, const EmscriptenMous OS_JavaScript *os = get_singleton(); int input_mask = os->input->get_mouse_button_mask(); - Point2 pos = Point2(p_event->canvasX, p_event->canvasY); + Point2 pos = correct_canvas_position(p_event->canvasX, p_event->canvasY); // For motion outside the canvas, only read mouse movement if dragging // started inside the canvas; imitating desktop app behaviour. if (!cursor_inside_canvas && !input_mask) @@ -666,7 +680,7 @@ EM_BOOL OS_JavaScript::touch_press_callback(int p_event_type, const EmscriptenTo if (!touch.isChanged) continue; ev->set_index(touch.identifier); - ev->set_position(Point2(touch.canvasX, touch.canvasY)); + ev->set_position(correct_canvas_position(touch.canvasX, touch.canvasY)); os->touches[i] = ev->get_position(); ev->set_pressed(p_event_type == EMSCRIPTEN_EVENT_TOUCHSTART); @@ -691,7 +705,7 @@ EM_BOOL OS_JavaScript::touchmove_callback(int p_event_type, const EmscriptenTouc if (!touch.isChanged) continue; ev->set_index(touch.identifier); - ev->set_position(Point2(touch.canvasX, touch.canvasY)); + ev->set_position(correct_canvas_position(touch.canvasX, touch.canvasY)); Point2 &prev = os->touches[i]; ev->set_relative(ev->get_position() - prev); prev = ev->get_position(); @@ -795,6 +809,47 @@ const char *OS_JavaScript::get_audio_driver_name(int p_driver) const { return "JavaScript"; } +// Clipboard +extern "C" EMSCRIPTEN_KEEPALIVE void update_clipboard(const char *p_text) { + // Only call set_clipboard from OS (sets local clipboard) + OS::get_singleton()->OS::set_clipboard(p_text); +} + +void OS_JavaScript::set_clipboard(const String &p_text) { + OS::set_clipboard(p_text); + /* clang-format off */ + int err = EM_ASM_INT({ + var text = UTF8ToString($0); + if (!navigator.clipboard || !navigator.clipboard.writeText) + return 1; + navigator.clipboard.writeText(text).catch(e => { + // Setting OS clipboard is only possible from an input callback. + console.error("Setting OS clipboard is only possible from an input callback for the HTML5 plafrom. Exception:", e); + }); + return 0; + }, p_text.utf8().get_data()); + /* clang-format on */ + ERR_EXPLAIN("Clipboard API is not supported."); + ERR_FAIL_COND(err); +} + +String OS_JavaScript::get_clipboard() const { + /* clang-format off */ + EM_ASM({ + try { + navigator.clipboard.readText().then(function (result) { + ccall('update_clipboard', 'void', ['string'], [result]); + }).catch(function (e) { + // Fail graciously. + }); + } catch (e) { + // Fail graciously. + } + }); + /* clang-format on */ + return this->OS::get_clipboard(); +} + // Lifecycle int OS_JavaScript::get_current_video_driver() const { return video_driver_index; @@ -901,6 +956,8 @@ Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, VisualServer *visual_server = memnew(VisualServerRaster()); input = memnew(InputDefault); + camera_server = memnew(CameraServer); + EMSCRIPTEN_RESULT result; #define EM_CHECK(ev) \ if (result != EMSCRIPTEN_RESULT_SUCCESS) \ @@ -939,6 +996,11 @@ Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, (['mouseover', 'mouseleave', 'focus', 'blur']).forEach(function(event, index) { Module.canvas.addEventListener(event, send_notification.bind(null, notifications[index])); }); + // Clipboard + const update_clipboard = cwrap('update_clipboard', null, ['string']); + window.addEventListener('paste', function(evt) { + update_clipboard(evt.clipboardData.getData('text')); + }, true); }, MainLoop::NOTIFICATION_WM_MOUSE_ENTER, MainLoop::NOTIFICATION_WM_MOUSE_EXIT, @@ -1030,6 +1092,7 @@ void OS_JavaScript::delete_main_loop() { void OS_JavaScript::finalize() { + memdelete(camera_server); memdelete(input); } @@ -1098,7 +1161,7 @@ void OS_JavaScript::set_icon(const Ref<Image> &p_icon) { Ref<Image> icon = p_icon; if (icon->is_compressed()) { icon = icon->duplicate(); - ERR_FAIL_COND(icon->decompress() != OK) + ERR_FAIL_COND(icon->decompress() != OK); } if (icon->get_format() != Image::FORMAT_RGBA8) { if (icon == p_icon) diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h index a0c7c31f2d..9635465c0d 100644 --- a/platform/javascript/os_javascript.h +++ b/platform/javascript/os_javascript.h @@ -35,6 +35,7 @@ #include "drivers/unix/os_unix.h" #include "main/input_default.h" #include "servers/audio_server.h" +#include "servers/camera_server.h" #include "servers/visual/rasterizer.h" #include <emscripten/html5.h> @@ -65,6 +66,8 @@ class OS_JavaScript : public OS_Unix { int64_t sync_wait_time; int64_t last_sync_check_time; + CameraServer *camera_server; + static EM_BOOL fullscreen_change_callback(int p_event_type, const EmscriptenFullscreenChangeEvent *p_event, void *p_user_data); static EM_BOOL keydown_callback(int p_event_type, const EmscriptenKeyboardEvent *p_event, void *p_user_data); @@ -133,6 +136,9 @@ public: virtual int get_audio_driver_count() const; virtual const char *get_audio_driver_name(int p_driver) const; + virtual void set_clipboard(const String &p_text); + virtual String get_clipboard() const; + virtual MainLoop *get_main_loop() const; void run_async(); bool main_loop_iterate(); 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..ed7a1f0a73 --- /dev/null +++ b/platform/osx/camera_osx.h @@ -0,0 +1,47 @@ +/*************************************************************************/ +/* camera_osx.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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..34486c58b1 --- /dev/null +++ b/platform/osx/camera_osx.mm @@ -0,0 +1,362 @@ +/*************************************************************************/ +/* camera_osx.mm */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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..2175797dec 100644 --- a/platform/osx/detect.py +++ b/platform/osx/detect.py @@ -128,7 +128,7 @@ def configure(env): 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(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/os_osx.h b/platform/osx/os_osx.h index 212966af11..09a9c0be4d 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" @@ -60,6 +61,7 @@ public: unsigned int osx_state; bool pressed; bool echo; + bool raw; uint32_t scancode; uint32_t unicode; }; @@ -72,6 +74,8 @@ public: //Rasterizer *rasterizer; VisualServer *visual_server; + CameraServer *camera_server; + List<String> args; MainLoop *main_loop; diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index 113c6636f0..3ee6537180 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -392,7 +392,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt @interface GodotContentView : NSView <NSTextInputClient> { NSTrackingArea *trackingArea; NSMutableAttributedString *markedText; - bool imeMode; + bool imeInputEventInProgress; } - (void)cancelComposition; - (BOOL)wantsUpdateLayer; @@ -418,7 +418,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt - (id)init { self = [super init]; trackingArea = nil; - imeMode = false; + imeInputEventInProgress = false; [self updateTrackingAreas]; [self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]]; markedText = [[NSMutableAttributedString alloc] init]; @@ -452,7 +452,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; [markedText initWithString:aString]; } if (OS_OSX::singleton->im_active) { - imeMode = true; + imeInputEventInProgress = true; OS_OSX::singleton->im_text.parse_utf8([[markedText mutableString] UTF8String]); OS_OSX::singleton->im_selection = Point2(selectedRange.location, selectedRange.length); @@ -467,7 +467,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; } - (void)unmarkText { - imeMode = false; + imeInputEventInProgress = false; [[markedText mutableString] setString:@""]; if (OS_OSX::singleton->im_active) { OS_OSX::singleton->im_text = String(); @@ -540,6 +540,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 }; ke.osx_state = [event modifierFlags]; ke.pressed = true; ke.echo = false; + ke.raw = false; // IME input event ke.scancode = 0; ke.unicode = codepoint; @@ -1001,7 +1002,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); @@ -1023,7 +1024,7 @@ static int remapKey(unsigned int key) { OSStatus err = UCKeyTranslate(keyboardLayout, key, kUCKeyActionDisplay, - 0, + (state >> 8) & 0xFF, LMGetKbdType(), kUCKeyTranslateNoDeadKeysBit, &keysDown, @@ -1045,29 +1046,52 @@ static int remapKey(unsigned int key) { - (void)keyDown:(NSEvent *)event { - //disable raw input in IME mode - if (!imeMode) { - OS_OSX::KeyEvent ke; + // Ignore all input if IME input is in progress + if (!imeInputEventInProgress) { + NSString *characters = [event characters]; + NSUInteger length = [characters length]; - ke.osx_state = [event modifierFlags]; - ke.pressed = true; - ke.echo = [event isARepeat]; - ke.scancode = remapKey([event keyCode]); - ke.unicode = 0; + 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; - push_to_key_event_buffer(ke); + ke.osx_state = [event modifierFlags]; + ke.pressed = true; + ke.echo = [event isARepeat]; + ke.scancode = remapKey([event keyCode], [event modifierFlags]); + ke.raw = true; + ke.unicode = [characters characterAtIndex:i]; + + push_to_key_event_buffer(ke); + } + } else { + OS_OSX::KeyEvent ke; + + ke.osx_state = [event modifierFlags]; + ke.pressed = true; + ke.echo = [event isARepeat]; + ke.scancode = remapKey([event keyCode], [event modifierFlags]); + ke.raw = false; + ke.unicode = 0; + + push_to_key_event_buffer(ke); + } } - if (OS_OSX::singleton->im_active == true) + // Pass events to IME handler + if (OS_OSX::singleton->im_active) [self interpretKeyEvents:[NSArray arrayWithObject:event]]; } - (void)flagsChanged:(NSEvent *)event { - if (!imeMode) { + // Ignore all input if IME input is in progress + if (!imeInputEventInProgress) { OS_OSX::KeyEvent ke; ke.echo = false; + ke.raw = true; int key = [event keyCode]; int mod = [event modifierFlags]; @@ -1105,7 +1129,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); @@ -1114,17 +1138,37 @@ static int remapKey(unsigned int key) { - (void)keyUp:(NSEvent *)event { - if (!imeMode) { + // Ignore all input if IME input is in progress + if (!imeInputEventInProgress) { + NSString *characters = [event characters]; + NSUInteger length = [characters length]; - OS_OSX::KeyEvent ke; + // Fallback unicode character handler used if IME is not active + 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 = false; - ke.scancode = remapKey([event keyCode]); - ke.unicode = 0; + ke.osx_state = [event modifierFlags]; + ke.pressed = false; + ke.echo = [event isARepeat]; + ke.scancode = remapKey([event keyCode], [event modifierFlags]); + ke.raw = true; + ke.unicode = [characters characterAtIndex:i]; - push_to_key_event_buffer(ke); + push_to_key_event_buffer(ke); + } + } else { + OS_OSX::KeyEvent ke; + + ke.osx_state = [event modifierFlags]; + ke.pressed = false; + ke.echo = [event isARepeat]; + ke.scancode = remapKey([event keyCode], [event modifierFlags]); + ke.raw = true; + ke.unicode = 0; + + push_to_key_event_buffer(ke); + } } } @@ -1498,6 +1542,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); @@ -1507,7 +1553,7 @@ 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); } return OK; @@ -1529,6 +1575,11 @@ void OS_OSX::finalize() { delete_main_loop(); + if (camera_server) { + memdelete(camera_server); + camera_server = NULL; + } + memdelete(joypad_osx); memdelete(input); @@ -2612,30 +2663,44 @@ void OS_OSX::process_key_events() { const KeyEvent &ke = key_event_buffer[i]; - if ((i == 0 && ke.scancode == 0) || (i > 0 && key_event_buffer[i - 1].scancode == 0)) { + if (ke.raw) { + // Non IME input - no composite characters, pass events as is k.instance(); get_key_modifier_state(ke.osx_state, k); k->set_pressed(ke.pressed); k->set_echo(ke.echo); - k->set_scancode(0); + k->set_scancode(ke.scancode); k->set_unicode(ke.unicode); push_input(k); - } - if (ke.scancode != 0) { - k.instance(); + } else { + // IME input + if ((i == 0 && ke.scancode == 0) || (i > 0 && key_event_buffer[i - 1].scancode == 0)) { + k.instance(); - get_key_modifier_state(ke.osx_state, k); - k->set_pressed(ke.pressed); - k->set_echo(ke.echo); - k->set_scancode(ke.scancode); + get_key_modifier_state(ke.osx_state, k); + k->set_pressed(ke.pressed); + k->set_echo(ke.echo); + k->set_scancode(0); + k->set_unicode(ke.unicode); - if (i + 1 < key_event_pos && key_event_buffer[i + 1].scancode == 0) { - k->set_unicode(key_event_buffer[i + 1].unicode); + push_input(k); } + if (ke.scancode != 0) { + k.instance(); - push_input(k); + get_key_modifier_state(ke.osx_state, k); + k->set_pressed(ke.pressed); + k->set_echo(ke.echo); + k->set_scancode(ke.scancode); + + if (i + 1 < key_event_pos && key_event_buffer[i + 1].scancode == 0) { + k->set_unicode(key_event_buffer[i + 1].unicode); + } + + push_input(k); + } } } diff --git a/platform/server/detect.py b/platform/server/detect.py index 08c2eb6aaf..a5648d8d9d 100644 --- a/platform/server/detect.py +++ b/platform/server/detect.py @@ -145,12 +145,12 @@ def configure(env): env.ParseConfig('pkg-config libpng --cflags --libs') if not env['builtin_bullet']: - # We need at least version 2.88 + # We need at least version 2.89 import subprocess bullet_version = subprocess.check_output(['pkg-config', 'bullet', '--modversion']).strip() - if str(bullet_version) < "2.88": + if str(bullet_version) < "2.89": # Abort as system bullet was requested but too old - print("Bullet: System version {0} does not match minimal requirements ({1}). Aborting.".format(bullet_version, "2.88")) + print("Bullet: System version {0} does not match minimal requirements ({1}). Aborting.".format(bullet_version, "2.89")) sys.exit(255) env.ParseConfig('pkg-config bullet --cflags --libs') diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp index cdcad33f6d..ec43a4c26f 100644 --- a/platform/uwp/export/export.cpp +++ b/platform/uwp/export/export.cpp @@ -241,7 +241,6 @@ void AppxPackager::make_block_map() { tmp_file->close(); memdelete(tmp_file); - tmp_file = NULL; } String AppxPackager::content_type(String p_extension) { @@ -291,7 +290,6 @@ void AppxPackager::make_content_types() { tmp_file->close(); memdelete(tmp_file); - tmp_file = NULL; } Vector<uint8_t> AppxPackager::make_file_header(FileMeta p_file_meta) { @@ -606,7 +604,6 @@ void AppxPackager::finish() { blockmap_file->close(); memdelete(blockmap_file); - blockmap_file = NULL; // Add content types EditorNode::progress_task_step("export", "Setting content types...", 5); @@ -622,7 +619,6 @@ void AppxPackager::finish() { types_file->close(); memdelete(types_file); - types_file = NULL; // Pre-process central directory before signing for (int i = 0; i < file_metadata.size(); i++) { diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp index f9d22481dd..9d9be44ce5 100644 --- a/platform/uwp/os_uwp.cpp +++ b/platform/uwp/os_uwp.cpp @@ -302,6 +302,10 @@ Error OS_UWP::initialize(const VideoMode &p_desired, int p_video_driver, int p_a } visual_server->init(); + + ///@TODO implement a subclass for UWP and instantiate that instead + camera_server = memnew(CameraServer); + input = memnew(InputDefault); joypad = ref new JoypadUWP(input); @@ -400,6 +404,8 @@ void OS_UWP::finalize() { memdelete(input); + memdelete(camera_server); + joypad = nullptr; } diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h index 1bb68bc75d..b7a7248f19 100644 --- a/platform/uwp/os_uwp.h +++ b/platform/uwp/os_uwp.h @@ -41,6 +41,7 @@ #include "main/input_default.h" #include "power_uwp.h" #include "servers/audio_server.h" +#include "servers/camera_server.h" #include "servers/visual/rasterizer.h" #include "servers/visual_server.h" @@ -95,6 +96,8 @@ private: VisualServer *visual_server; int pressrc; + CameraServer *camera_server; + ContextEGL_UWP *gl_context; Windows::UI::Core::CoreWindow ^ window; diff --git a/platform/windows/SCsub b/platform/windows/SCsub index 892d734734..8426ccbb89 100644 --- a/platform/windows/SCsub +++ b/platform/windows/SCsub @@ -8,6 +8,7 @@ import platform_windows_builders common_win = [ "godot_windows.cpp", + "camera_win.cpp", "context_gl_windows.cpp", "crash_handler_windows.cpp", "os_windows.cpp", diff --git a/platform/windows/camera_win.cpp b/platform/windows/camera_win.cpp new file mode 100644 index 0000000000..6eff2749f2 --- /dev/null +++ b/platform/windows/camera_win.cpp @@ -0,0 +1,94 @@ +/*************************************************************************/ +/* camera_win.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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. */ +/*************************************************************************/ + +#include "camera_win.h" + +///@TODO sorry guys, I got about 80% through implementing this using DirectShow only to find out Microsoft deprecated half the API and its replacement is as confusing as they could make it +// Joey suggested looking into libuvc which offers a more direct route to webcams over USB and this is very promissing but it wouldn't compile on windows for me... +// I've gutted the classes I implemented DirectShow in just to have a skeleton for someone to work on, mail me for more details or if you want a copy.... + +////////////////////////////////////////////////////////////////////////// +// CameraFeedWindows - Subclass for our camera feed on windows + +/// @TODO need to implement this + +class CameraFeedWindows : public CameraFeed { +private: +protected: +public: + CameraFeedWindows(); + virtual ~CameraFeedWindows(); + + bool activate_feed(); + void deactivate_feed(); +}; + +CameraFeedWindows::CameraFeedWindows(){ + ///@TODO implement this, should store information about our available camera +}; + +CameraFeedWindows::~CameraFeedWindows() { + // make sure we stop recording if we are! + if (is_active()) { + deactivate_feed(); + }; + + ///@TODO free up anything used by this +}; + +bool CameraFeedWindows::activate_feed() { + ///@TODO this should activate our camera and start the process of capturing frames + + return true; +}; + +///@TODO we should probably have a callback method here that is being called by the camera API which provides frames and call back into the CameraServer to update our texture + +void CameraFeedWindows::deactivate_feed(){ + ///@TODO this should deactivate our camera and stop the process of capturing frames +}; + +////////////////////////////////////////////////////////////////////////// +// CameraWindows - Subclass for our camera server on windows + +void CameraWindows::add_active_cameras(){ + ///@TODO scan through any active cameras and create CameraFeedWindows objects for them +}; + +CameraWindows::CameraWindows() { + // Find cameras active right now + add_active_cameras(); + + // need to add something that will react to devices being connected/removed... +}; + +CameraWindows::~CameraWindows(){ + +}; diff --git a/platform/windows/camera_win.h b/platform/windows/camera_win.h new file mode 100644 index 0000000000..61d22ac6b1 --- /dev/null +++ b/platform/windows/camera_win.h @@ -0,0 +1,46 @@ +/*************************************************************************/ +/* camera_win.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2017 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 CAMERAWIN_H +#define CAMERAWIN_H + +#include "servers/camera/camera_feed.h" +#include "servers/camera_server.h" + +class CameraWindows : public CameraServer { +private: + void add_active_cameras(); + +public: + CameraWindows(); + ~CameraWindows(); +}; + +#endif /* CAMERAWIN_H */ diff --git a/platform/windows/export/export.cpp b/platform/windows/export/export.cpp index 141ab96370..4a72d07adc 100644 --- a/platform/windows/export/export.cpp +++ b/platform/windows/export/export.cpp @@ -138,8 +138,8 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio EditorExportPlatformPC::get_export_options(r_options); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "*.ico"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "1.0.0"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/company_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Company Name"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_description"), "")); diff --git a/platform/windows/godot_res.rc b/platform/windows/godot_res.rc index f2dca10d55..1fa8957f15 100644 --- a/platform/windows/godot_res.rc +++ b/platform/windows/godot_res.rc @@ -21,7 +21,7 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "CompanyName", "Godot Engine" - VALUE "FileDescription", VERSION_NAME " Editor" + VALUE "FileDescription", VERSION_NAME VALUE "FileVersion", VERSION_NUMBER VALUE "ProductName", VERSION_NAME VALUE "Licence", "MIT" diff --git a/platform/windows/joypad_windows.cpp b/platform/windows/joypad_windows.cpp index 5a399cdf90..432060f4fe 100644 --- a/platform/windows/joypad_windows.cpp +++ b/platform/windows/joypad_windows.cpp @@ -103,17 +103,17 @@ bool JoypadWindows::is_xinput_device(const GUID *p_guid) { PRAWINPUTDEVICELIST dev_list = NULL; unsigned int dev_list_count = 0; - if (GetRawInputDeviceList(NULL, &dev_list_count, sizeof(RAWINPUTDEVICELIST)) == -1) { + if (GetRawInputDeviceList(NULL, &dev_list_count, sizeof(RAWINPUTDEVICELIST)) == (UINT)-1) { return false; } dev_list = (PRAWINPUTDEVICELIST)malloc(sizeof(RAWINPUTDEVICELIST) * dev_list_count); if (!dev_list) return false; - if (GetRawInputDeviceList(dev_list, &dev_list_count, sizeof(RAWINPUTDEVICELIST)) == -1) { + if (GetRawInputDeviceList(dev_list, &dev_list_count, sizeof(RAWINPUTDEVICELIST)) == (UINT)-1) { free(dev_list); return false; } - for (int i = 0; i < dev_list_count; i++) { + for (unsigned int i = 0; i < dev_list_count; i++) { RID_DEVICE_INFO rdi; char dev_name[128]; @@ -334,9 +334,9 @@ void JoypadWindows::process_joypads() { if (joy.state.dwPacketNumber != joy.last_packet) { int button_mask = XINPUT_GAMEPAD_DPAD_UP; - for (int i = 0; i <= 16; i++) { + for (int j = 0; j <= 16; i++) { - input->joy_button(joy.id, i, joy.state.Gamepad.wButtons & button_mask); + input->joy_button(joy.id, j, joy.state.Gamepad.wButtons & button_mask); button_mask = button_mask * 2; } @@ -406,7 +406,7 @@ void JoypadWindows::process_joypads() { // on mingw, these constants are not constants int count = 6; - int axes[] = { DIJOFS_X, DIJOFS_Y, DIJOFS_Z, DIJOFS_RX, DIJOFS_RY, DIJOFS_RZ }; + unsigned int axes[] = { DIJOFS_X, DIJOFS_Y, DIJOFS_Z, DIJOFS_RX, DIJOFS_RY, DIJOFS_RZ }; int values[] = { js.lX, js.lY, js.lZ, js.lRx, js.lRy, js.lRz }; for (int j = 0; j < joy->joy_axis.size(); j++) { @@ -426,7 +426,11 @@ void JoypadWindows::post_hat(int p_device, DWORD p_dpad) { int dpad_val = 0; - if (p_dpad == -1) { + // Should be -1 when centered, but according to docs: + // "Some drivers report the centered position of the POV indicator as 65,535. Determine whether the indicator is centered as follows: + // BOOL POVCentered = (LOWORD(dwPOV) == 0xFFFF);" + // https://docs.microsoft.com/en-us/previous-versions/windows/desktop/ee416628(v%3Dvs.85)#remarks + if (LOWORD(p_dpad) == 0xFFFF) { dpad_val = InputDefault::HAT_MASK_CENTER; } if (p_dpad == 0) { diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 6df2ad4821..db56bf5c7e 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -94,6 +94,7 @@ static BOOL CALLBACK _MonitorEnumProcSize(HMONITOR hMonitor, HDC hdcMonitor, LPR return TRUE; } +#ifdef DEBUG_ENABLED static String format_error_message(DWORD id) { LPWSTR messageBuffer = NULL; @@ -106,6 +107,7 @@ static String format_error_message(DWORD id) { return msg; } +#endif // DEBUG_ENABLED extern HINSTANCE godot_hinstance; @@ -555,6 +557,7 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) break; } } + FALLTHROUGH; case WM_MBUTTONDOWN: case WM_MBUTTONUP: case WM_RBUTTONDOWN: @@ -583,7 +586,6 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) case WM_MBUTTONDOWN: { mb->set_pressed(true); mb->set_button_index(3); - } break; case WM_MBUTTONUP: { mb->set_pressed(false); @@ -598,19 +600,16 @@ LRESULT OS_Windows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) mb->set_button_index(2); } break; case WM_LBUTTONDBLCLK: { - mb->set_pressed(true); mb->set_button_index(1); mb->set_doubleclick(true); } break; case WM_RBUTTONDBLCLK: { - mb->set_pressed(true); mb->set_button_index(2); mb->set_doubleclick(true); } break; case WM_MBUTTONDBLCLK: { - mb->set_pressed(true); mb->set_button_index(3); mb->set_doubleclick(true); @@ -1358,6 +1357,8 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int power_manager = memnew(PowerWindows); + camera_server = memnew(CameraWindows); + AudioDriverManager::initialize(p_audio_driver); TRACKMOUSEEVENT tme; @@ -1381,7 +1382,7 @@ Error OS_Windows::initialize(const VideoMode &p_desired, int p_video_driver, int SetFocus(hWnd); // Sets Keyboard Focus To } - if (p_desired.layered_splash) { + if (p_desired.layered) { set_window_per_pixel_transparency_enabled(true); } @@ -1517,6 +1518,7 @@ void OS_Windows::finalize() { memdelete(joypad); memdelete(input); + memdelete(camera_server); touch_state.clear(); visual_server->finish(); @@ -1816,11 +1818,11 @@ void OS_Windows::set_window_size(const Size2 p_size) { // Don't let the mouse leave the window when resizing to a smaller resolution if (mouse_mode == MOUSE_MODE_CONFINED) { - RECT rect; - GetClientRect(hWnd, &rect); - ClientToScreen(hWnd, (POINT *)&rect.left); - ClientToScreen(hWnd, (POINT *)&rect.right); - ClipCursor(&rect); + RECT crect; + GetClientRect(hWnd, &crect); + ClientToScreen(hWnd, (POINT *)&crect.left); + ClientToScreen(hWnd, (POINT *)&crect.right); + ClipCursor(&crect); } } void OS_Windows::set_window_fullscreen(bool p_enabled) { @@ -2193,6 +2195,8 @@ uint64_t OS_Windows::get_unix_time() const { FILETIME fep; SystemTimeToFileTime(&ep, &fep); + // FIXME: dereferencing type-punned pointer will break strict-aliasing rules (GCC warning) + // https://docs.microsoft.com/en-us/windows/desktop/api/minwinbase/ns-minwinbase-filetime#remarks return (*(uint64_t *)&ft - *(uint64_t *)&fep) / 10000000; }; @@ -2378,7 +2382,7 @@ void OS_Windows::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shap } // Finally, create the icon - ICONINFO iconinfo = { 0 }; + ICONINFO iconinfo; iconinfo.fIcon = FALSE; iconinfo.xHotspot = p_hotspot.x; iconinfo.yHotspot = p_hotspot.y; @@ -2531,9 +2535,9 @@ Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments, if (p_blocking) { - DWORD ret = WaitForSingleObject(pi.pi.hProcess, INFINITE); + DWORD ret2 = WaitForSingleObject(pi.pi.hProcess, INFINITE); if (r_exitcode) - *r_exitcode = ret; + *r_exitcode = ret2; CloseHandle(pi.pi.hProcess); CloseHandle(pi.pi.hThread); diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 0aacbcb9ff..b20d847baa 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -31,6 +31,7 @@ #ifndef OS_WINDOWS_H #define OS_WINDOWS_H +#include "camera_win.h" #include "context_gl_windows.h" #include "core/os/input.h" #include "core/os/os.h" @@ -108,6 +109,7 @@ class OS_Windows : public OS { ContextGL_Windows *gl_context; #endif VisualServer *visual_server; + CameraWindows *camera_server; int pressrc; HDC hDC; // Private GDI Device Context HINSTANCE hInstance; // Holds The Instance Of The Application diff --git a/platform/windows/power_windows.cpp b/platform/windows/power_windows.cpp index b96ae51132..0efd88c216 100644 --- a/platform/windows/power_windows.cpp +++ b/platform/windows/power_windows.cpp @@ -89,7 +89,7 @@ bool PowerWindows::GetPowerInfo_Windows() { if (pct != 255) { /* 255 == unknown */ percent_left = (pct > 100) ? 100 : pct; /* clamp between 0%, 100% */ } - if (secs != 0xFFFFFFFF) { /* ((DWORD)-1) == unknown */ + if (secs != (int)0xFFFFFFFF) { /* ((DWORD)-1) == unknown */ nsecs_left = secs; } } diff --git a/platform/windows/windows_terminal_logger.cpp b/platform/windows/windows_terminal_logger.cpp index 7def419103..adbdafb07e 100644 --- a/platform/windows/windows_terminal_logger.cpp +++ b/platform/windows/windows_terminal_logger.cpp @@ -45,7 +45,7 @@ void WindowsTerminalLogger::logv(const char *p_format, va_list p_list, bool p_er int len = vsnprintf(buf, BUFFER_SIZE, p_format, p_list); if (len <= 0) return; - if (len >= BUFFER_SIZE) + if ((unsigned int)len >= BUFFER_SIZE) len = BUFFER_SIZE; // Output is too big, will be truncated buf[len] = 0; @@ -154,4 +154,4 @@ void WindowsTerminalLogger::log_error(const char *p_function, const char *p_file WindowsTerminalLogger::~WindowsTerminalLogger() {} -#endif
\ No newline at end of file +#endif diff --git a/platform/x11/detect.py b/platform/x11/detect.py index 933ee6b72e..a502308eee 100644 --- a/platform/x11/detect.py +++ b/platform/x11/detect.py @@ -219,12 +219,12 @@ def configure(env): env.ParseConfig('pkg-config libpng --cflags --libs') if not env['builtin_bullet']: - # We need at least version 2.88 + # We need at least version 2.89 import subprocess bullet_version = subprocess.check_output(['pkg-config', 'bullet', '--modversion']).strip() - if str(bullet_version) < "2.88": + if str(bullet_version) < "2.89": # Abort as system bullet was requested but too old - print("Bullet: System version {0} does not match minimal requirements ({1}). Aborting.".format(bullet_version, "2.88")) + print("Bullet: System version {0} does not match minimal requirements ({1}). Aborting.".format(bullet_version, "2.89")) sys.exit(255) env.ParseConfig('pkg-config bullet --cflags --libs') diff --git a/platform/x11/joypad_linux.cpp b/platform/x11/joypad_linux.cpp index c4dd8fe0e0..3e9e8033e8 100644 --- a/platform/x11/joypad_linux.cpp +++ b/platform/x11/joypad_linux.cpp @@ -444,10 +444,10 @@ InputDefault::JoyAxis JoypadLinux::axis_correct(const input_absinfo *p_abs, int jx.min = -1; if (p_value < 0) { jx.value = (float)-p_value / min; + } else { + jx.value = (float)p_value / max; } - jx.value = (float)p_value / max; - } - if (min == 0) { + } else if (min == 0) { jx.min = 0; jx.value = 0.0f + (float)p_value / max; } diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index f034b2389b..146cd8bb66 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -583,6 +583,9 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a AudioDriverManager::initialize(p_audio_driver); + ///@TODO implement a subclass for Linux and instantiate that instead + camera_server = memnew(CameraServer); + input = memnew(InputDefault); window_has_focus = true; // Set focus to true at init @@ -593,7 +596,7 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a power_manager = memnew(PowerX11); - if (p_desired.layered_splash) { + if (p_desired.layered) { set_window_per_pixel_transparency_enabled(true); } @@ -783,6 +786,8 @@ void OS_X11::finalize() { memdelete(input); + memdelete(camera_server); + visual_server->finish(); memdelete(visual_server); //memdelete(rasterizer); @@ -1017,12 +1022,12 @@ void OS_X11::set_wm_fullscreen(bool p_enabled) { XFree(xsh); } - if (!p_enabled && !get_borderless_window()) { - // put decorations back if the window wasn't suppoesed to be borderless + if (!p_enabled) { + // put back or remove decorations according to the last set borderless state Hints hints; Atom property; hints.flags = 2; - hints.decorations = 1; + hints.decorations = current_videomode.borderless_window ? 0 : 1; property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True); XChangeProperty(x11_display, x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5); } @@ -1531,7 +1536,7 @@ bool OS_X11::is_window_always_on_top() const { void OS_X11::set_borderless_window(bool p_borderless) { - if (current_videomode.borderless_window == p_borderless) + if (get_borderless_window() == p_borderless) return; if (!p_borderless && layered_window) @@ -1551,7 +1556,24 @@ void OS_X11::set_borderless_window(bool p_borderless) { } bool OS_X11::get_borderless_window() { - return current_videomode.borderless_window; + + bool borderless = current_videomode.borderless_window; + Atom prop = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True); + if (prop != None) { + + Atom type; + int format; + unsigned long len; + unsigned long remaining; + unsigned char *data = NULL; + if (XGetWindowProperty(x11_display, x11_window, prop, 0, sizeof(Hints), False, AnyPropertyType, &type, &format, &len, &remaining, &data) == Success) { + if (data && (format == 32) && (len >= 5)) { + borderless = !((Hints *)data)->decorations; + } + XFree(data); + } + } + return borderless; } void OS_X11::request_attention() { @@ -2378,7 +2400,7 @@ void OS_X11::process_xevents() { Vector<String> files = String((char *)p.data).split("\n", false); for (int i = 0; i < files.size(); i++) { - files.write[i] = files[i].replace("file://", "").http_unescape().strip_escapes(); + files.write[i] = files[i].replace("file://", "").http_unescape().strip_edges(); } main_loop->drop_files(files); diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h index ad35cdb4f9..eca617bd90 100644 --- a/platform/x11/os_x11.h +++ b/platform/x11/os_x11.h @@ -42,6 +42,7 @@ #include "main/input_default.h" #include "power_x11.h" #include "servers/audio_server.h" +#include "servers/camera_server.h" #include "servers/visual/rasterizer.h" #include "servers/visual_server.h" //#include "servers/visual/visual_server_wrap_mt.h" @@ -146,6 +147,8 @@ class OS_X11 : public OS_Unix { void get_key_modifier_state(unsigned int p_x11_state, Ref<InputEventWithModifiers> state); void flush_mouse_motion(); + CameraServer *camera_server; + MouseMode mouse_mode; Point2i center; |