diff --git a/webrtc/BUILD.gn b/webrtc/BUILD.gn index a4b07594b8..f42cc24646 100644 --- a/webrtc/BUILD.gn +++ b/webrtc/BUILD.gn @@ -562,6 +562,10 @@ if (rtc_include_tests) { "base/sslstreamadapter_unittest.cc", ] } + if (is_ios || (is_mac && target_cpu != "x86")) { + defines = [ "CARBON_DEPRECATED=YES" ] + } + if (rtc_use_quic) { sources += [ "p2p/quic/quicconnectionhelper_unittest.cc", @@ -824,6 +828,9 @@ if (rtc_include_tests) { if (is_mac) { sources += [ "base/macsocketserver_unittest.cc" ] } + if (is_ios || (is_mac && target_cpu != "x86")) { + defines = [ "CARBON_DEPRECATED=YES" ] + } if (is_clang) { # Suppress warnings from the Chromium Clang plugin. # See http://code.google.com/p/webrtc/issues/detail?id=163 for details. diff --git a/webrtc/base/BUILD.gn b/webrtc/base/BUILD.gn index fe63d2076f..a91b615c11 100644 --- a/webrtc/base/BUILD.gn +++ b/webrtc/base/BUILD.gn @@ -65,6 +65,9 @@ config("rtc_base_all_dependent_config") { "Security.framework", "SystemConfiguration.framework", ] + if (current_cpu == "x86") { + libs += [ "Carbon.framework" ] + } } } @@ -288,7 +291,6 @@ rtc_static_library("rtc_base") { defines = [ "LOGGING=1" ] sources = [ - "applefilesystem.mm", "arraysize.h", "asyncfile.cc", "asyncfile.h", @@ -337,6 +339,7 @@ rtc_static_library("rtc_base") { "httpcommon.h", "httprequest.cc", "httprequest.h", + "iosfilesystem.mm", "ipaddress.cc", "ipaddress.h", "linked_ptr.h", @@ -666,6 +669,10 @@ rtc_static_library("rtc_base") { } } + if (is_ios || (is_mac && current_cpu != "x86")) { + defines += [ "CARBON_DEPRECATED=YES" ] + } + if (is_linux || is_android) { sources += [ "linux.cc", diff --git a/webrtc/base/base.gyp b/webrtc/base/base.gyp index 9baecd6802..93f87b2957 100644 --- a/webrtc/base/base.gyp +++ b/webrtc/base/base.gyp @@ -229,7 +229,6 @@ 'LOGGING=1', ], 'sources': [ - 'applefilesystem.mm', 'arraysize.h', 'asyncfile.cc', 'asyncfile.h', @@ -278,6 +277,7 @@ 'httpcommon.h', 'httprequest.cc', 'httprequest.h', + 'iosfilesystem.mm', 'ipaddress.cc', 'ipaddress.h', 'linked_ptr.h', @@ -603,6 +603,19 @@ }, }, }, + 'conditions': [ + ['target_arch=="ia32"', { + 'all_dependent_settings': { + 'link_settings': { + 'xcode_settings': { + 'OTHER_LDFLAGS': [ + '-framework Carbon', + ], + }, + }, + }, + }], + ], }], ['OS=="win" and nacl_untrusted_build==0', { 'sources': [ @@ -651,6 +664,11 @@ }, } }], + ['OS=="ios" or (OS=="mac" and target_arch!="ia32")', { + 'defines': [ + 'CARBON_DEPRECATED=YES', + ], + }], ['OS=="linux" or OS=="android"', { 'sources': [ 'linux.cc', diff --git a/webrtc/base/applefilesystem.mm b/webrtc/base/iosfilesystem.mm similarity index 84% rename from webrtc/base/applefilesystem.mm rename to webrtc/base/iosfilesystem.mm index 9f015edf07..eb4bbecd58 100644 --- a/webrtc/base/applefilesystem.mm +++ b/webrtc/base/iosfilesystem.mm @@ -8,8 +8,8 @@ * be found in the AUTHORS file in the root of the source tree. */ -// This file only exists because various iOS and macOS system APIs are only -// available from Objective-C. See unixfilesystem.cc for the only use +// This file only exists because various iOS system APIs are only +// available from Objective-C. See unixfilesystem.cc for the only use // (enforced by a lack of a header file). #import @@ -33,7 +33,7 @@ static char* copyString(NSString* s) { } // Return a (leaked) copy of a directory name suitable for application data. -char* AppleDataDirectory() { +char* IOSDataDirectory() { NSArray* paths = NSSearchPathForDirectoriesInDomains( NSApplicationSupportDirectory, NSUserDomainMask, YES); ASSERT([paths count] == 1); @@ -41,13 +41,13 @@ char* AppleDataDirectory() { } // Return a (leaked) copy of a directory name suitable for use as a $TEMP. -char* AppleTempDirectory() { +char* IOSTempDirectory() { return copyString(NSTemporaryDirectory()); } // Return the binary's path. -void AppleAppName(rtc::Pathname* path) { - NSProcessInfo* pInfo = [NSProcessInfo processInfo]; +void IOSAppName(rtc::Pathname* path) { + NSProcessInfo *pInfo = [NSProcessInfo processInfo]; NSString* argv0 = [[pInfo arguments] objectAtIndex:0]; path->SetPathname([argv0 UTF8String]); } diff --git a/webrtc/base/macsocketserver.cc b/webrtc/base/macsocketserver.cc index b763e813b0..96b2091dd3 100644 --- a/webrtc/base/macsocketserver.cc +++ b/webrtc/base/macsocketserver.cc @@ -212,4 +212,175 @@ void MacCFSocketServer::OnWakeUpCallback() { ASSERT(run_loop_ == CFRunLoopGetCurrent()); CFRunLoopStop(run_loop_); } + +/////////////////////////////////////////////////////////////////////////////// +// MacCarbonSocketServer +/////////////////////////////////////////////////////////////////////////////// +#ifndef CARBON_DEPRECATED + +const UInt32 kEventClassSocketServer = 'MCSS'; +const UInt32 kEventWakeUp = 'WAKE'; +const EventTypeSpec kEventWakeUpSpec[] = { + { kEventClassSocketServer, kEventWakeUp } +}; + +std::string DecodeEvent(EventRef event) { + std::string str; + DecodeFourChar(::GetEventClass(event), &str); + str.push_back(':'); + DecodeFourChar(::GetEventKind(event), &str); + return str; +} + +MacCarbonSocketServer::MacCarbonSocketServer() + : event_queue_(GetCurrentEventQueue()), wake_up_(NULL) { + VERIFY(noErr == CreateEvent(NULL, kEventClassSocketServer, kEventWakeUp, 0, + kEventAttributeUserEvent, &wake_up_)); +} + +MacCarbonSocketServer::~MacCarbonSocketServer() { + if (wake_up_) { + ReleaseEvent(wake_up_); + } +} + +bool MacCarbonSocketServer::Wait(int cms, bool process_io) { + ASSERT(GetCurrentEventQueue() == event_queue_); + + // Listen to all events if we're processing I/O. + // Only listen for our wakeup event if we're not. + UInt32 num_types = 0; + const EventTypeSpec* events = NULL; + if (!process_io) { + num_types = GetEventTypeCount(kEventWakeUpSpec); + events = kEventWakeUpSpec; + } + + EventTargetRef target = GetEventDispatcherTarget(); + EventTimeout timeout = + (kForever == cms) ? kEventDurationForever : cms / 1000.0; + EventTimeout end_time = GetCurrentEventTime() + timeout; + + bool done = false; + while (!done) { + EventRef event; + OSStatus result = ReceiveNextEvent(num_types, events, timeout, true, + &event); + if (noErr == result) { + if (wake_up_ != event) { + LOG_F(LS_VERBOSE) << "Dispatching event: " << DecodeEvent(event); + result = SendEventToEventTarget(event, target); + if ((noErr != result) && (eventNotHandledErr != result)) { + LOG_E(LS_ERROR, OS, result) << "SendEventToEventTarget"; + } + } else { + done = true; + } + ReleaseEvent(event); + } else if (eventLoopTimedOutErr == result) { + ASSERT(cms != kForever); + done = true; + } else if (eventLoopQuitErr == result) { + // Ignore this... we get spurious quits for a variety of reasons. + LOG_E(LS_VERBOSE, OS, result) << "ReceiveNextEvent"; + } else { + // Some strange error occurred. Log it. + LOG_E(LS_WARNING, OS, result) << "ReceiveNextEvent"; + return false; + } + if (kForever != cms) { + timeout = end_time - GetCurrentEventTime(); + } + } + return true; +} + +void MacCarbonSocketServer::WakeUp() { + if (!IsEventInQueue(event_queue_, wake_up_)) { + RetainEvent(wake_up_); + OSStatus result = PostEventToQueue(event_queue_, wake_up_, + kEventPriorityStandard); + if (noErr != result) { + LOG_E(LS_ERROR, OS, result) << "PostEventToQueue"; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// MacCarbonAppSocketServer +/////////////////////////////////////////////////////////////////////////////// + +MacCarbonAppSocketServer::MacCarbonAppSocketServer() + : event_queue_(GetCurrentEventQueue()) { + // Install event handler + VERIFY(noErr == InstallApplicationEventHandler( + NewEventHandlerUPP(WakeUpEventHandler), 1, kEventWakeUpSpec, this, + &event_handler_)); + + // Install a timer and set it idle to begin with. + VERIFY(noErr == InstallEventLoopTimer(GetMainEventLoop(), + kEventDurationForever, + kEventDurationForever, + NewEventLoopTimerUPP(TimerHandler), + this, + &timer_)); +} + +MacCarbonAppSocketServer::~MacCarbonAppSocketServer() { + RemoveEventLoopTimer(timer_); + RemoveEventHandler(event_handler_); +} + +OSStatus MacCarbonAppSocketServer::WakeUpEventHandler( + EventHandlerCallRef next, EventRef event, void *data) { + QuitApplicationEventLoop(); + return noErr; +} + +void MacCarbonAppSocketServer::TimerHandler( + EventLoopTimerRef timer, void *data) { + QuitApplicationEventLoop(); +} + +bool MacCarbonAppSocketServer::Wait(int cms, bool process_io) { + if (!process_io && cms == 0) { + // No op. + return true; + } + if (kForever != cms) { + // Start a timer. + OSStatus error = + SetEventLoopTimerNextFireTime(timer_, cms / 1000.0); + if (error != noErr) { + LOG(LS_ERROR) << "Failed setting next fire time."; + } + } + if (!process_io) { + // No way to listen to common modes and not get socket events, unless + // we disable each one's callbacks. + EnableSocketCallbacks(false); + } + RunApplicationEventLoop(); + if (!process_io) { + // Reenable them. Hopefully this won't cause spurious callbacks or + // missing ones while they were disabled. + EnableSocketCallbacks(true); + } + return true; +} + +void MacCarbonAppSocketServer::WakeUp() { + // TODO: No-op if there's already a WakeUp in flight. + EventRef wake_up; + VERIFY(noErr == CreateEvent(NULL, kEventClassSocketServer, kEventWakeUp, 0, + kEventAttributeUserEvent, &wake_up)); + OSStatus result = PostEventToQueue(event_queue_, wake_up, + kEventPriorityStandard); + if (noErr != result) { + LOG_E(LS_ERROR, OS, result) << "PostEventToQueue"; + } + ReleaseEvent(wake_up); +} + +#endif } // namespace rtc diff --git a/webrtc/base/macsocketserver.h b/webrtc/base/macsocketserver.h index ed6d60e05e..f85628b0c8 100644 --- a/webrtc/base/macsocketserver.h +++ b/webrtc/base/macsocketserver.h @@ -11,7 +11,9 @@ #define WEBRTC_BASE_MACSOCKETSERVER_H__ #include -#include +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) // Invalid on IOS +#include +#endif #include "webrtc/base/physicalsocketserver.h" namespace rtc { @@ -74,6 +76,61 @@ class MacCFSocketServer : public MacBaseSocketServer { CFRunLoopRef run_loop_; CFRunLoopSourceRef wake_up_; }; + +#ifndef CARBON_DEPRECATED + +/////////////////////////////////////////////////////////////////////////////// +// MacCarbonSocketServer +/////////////////////////////////////////////////////////////////////////////// + +// Interacts with the Carbon event queue. While idle it will block, +// waiting for events. When the socket server has work to do, it will +// post a 'wake up' event to the queue, causing the thread to exit the +// event loop until the next call to Wait. Other events are dispatched +// to their target. Supports Carbon and Cocoa UI interaction. +class MacCarbonSocketServer : public MacBaseSocketServer { + public: + MacCarbonSocketServer(); + virtual ~MacCarbonSocketServer(); + + // SocketServer Interface + virtual bool Wait(int cms, bool process_io); + virtual void WakeUp(); + + private: + EventQueueRef event_queue_; + EventRef wake_up_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// MacCarbonAppSocketServer +/////////////////////////////////////////////////////////////////////////////// + +// Runs the Carbon application event loop on the current thread while +// idle. When the socket server has work to do, it will post an event +// to the queue, causing the thread to exit the event loop until the +// next call to Wait. Other events are automatically dispatched to +// their target. +class MacCarbonAppSocketServer : public MacBaseSocketServer { + public: + MacCarbonAppSocketServer(); + virtual ~MacCarbonAppSocketServer(); + + // SocketServer Interface + virtual bool Wait(int cms, bool process_io); + virtual void WakeUp(); + + private: + static OSStatus WakeUpEventHandler(EventHandlerCallRef next, EventRef event, + void *data); + static void TimerHandler(EventLoopTimerRef timer, void *data); + + EventQueueRef event_queue_; + EventHandlerRef event_handler_; + EventLoopTimerRef timer_; +}; + +#endif } // namespace rtc #endif // WEBRTC_BASE_MACSOCKETSERVER_H__ diff --git a/webrtc/base/macsocketserver_unittest.cc b/webrtc/base/macsocketserver_unittest.cc index 520d226899..87cfe07b2f 100644 --- a/webrtc/base/macsocketserver_unittest.cc +++ b/webrtc/base/macsocketserver_unittest.cc @@ -31,6 +31,64 @@ class WakeThread : public Thread { SocketServer* ss_; }; +#ifndef CARBON_DEPRECATED + +// Test that MacCFSocketServer::Wait works as expected. +TEST(MacCFSocketServerTest, TestWait) { + MacCFSocketServer server; + uint32_t start = Time(); + server.Wait(1000, true); + EXPECT_GE(TimeSince(start), 1000); +} + +// Test that MacCFSocketServer::Wakeup works as expected. +TEST(MacCFSocketServerTest, TestWakeup) { + MacCFSocketServer server; + WakeThread thread(&server); + uint32_t start = Time(); + thread.Start(); + server.Wait(10000, true); + EXPECT_LT(TimeSince(start), 10000); +} + +// Test that MacCarbonSocketServer::Wait works as expected. +TEST(MacCarbonSocketServerTest, TestWait) { + MacCarbonSocketServer server; + uint32_t start = Time(); + server.Wait(1000, true); + EXPECT_GE(TimeSince(start), 1000); +} + +// Test that MacCarbonSocketServer::Wakeup works as expected. +TEST(MacCarbonSocketServerTest, TestWakeup) { + MacCarbonSocketServer server; + WakeThread thread(&server); + uint32_t start = Time(); + thread.Start(); + server.Wait(10000, true); + EXPECT_LT(TimeSince(start), 10000); +} + +// Test that MacCarbonAppSocketServer::Wait works as expected. +TEST(MacCarbonAppSocketServerTest, TestWait) { + MacCarbonAppSocketServer server; + uint32_t start = Time(); + server.Wait(1000, true); + EXPECT_GE(TimeSince(start), 1000); +} + +// Test that MacCarbonAppSocketServer::Wakeup works as expected. +TEST(MacCarbonAppSocketServerTest, TestWakeup) { + MacCarbonAppSocketServer server; + WakeThread thread(&server); + uint32_t start = Time(); + thread.Start(); + server.Wait(10000, true); + EXPECT_LT(TimeSince(start), 10000); +} + +#endif + // Test that MacAsyncSocket passes all the generic Socket tests. class MacAsyncSocketTest : public SocketTest { protected: @@ -162,4 +220,20 @@ TEST_F(MacAsyncSocketTest, DISABLED_TestGetSetOptionsIPv4) { TEST_F(MacAsyncSocketTest, DISABLED_TestGetSetOptionsIPv6) { SocketTest::TestGetSetOptionsIPv6(); } + +#ifndef CARBON_DEPRECATED +class MacCarbonAppAsyncSocketTest : public MacAsyncSocketTest { + virtual MacBaseSocketServer* CreateSocketServer() { + return new MacCarbonAppSocketServer(); + }; +}; + +TEST_F(MacCarbonAppAsyncSocketTest, TestSocketServerWaitIPv4) { + SocketTest::TestSocketServerWaitIPv4(); +} + +TEST_F(MacCarbonAppAsyncSocketTest, TestSocketServerWaitIPv6) { + SocketTest::TestSocketServerWaitIPv6(); +} +#endif } // namespace rtc diff --git a/webrtc/base/macutils.cc b/webrtc/base/macutils.cc index 44eb4af19f..3a07b815c9 100644 --- a/webrtc/base/macutils.cc +++ b/webrtc/base/macutils.cc @@ -46,6 +46,7 @@ bool ToUtf16(const std::string& str8, CFStringRef* str16) { return NULL != *str16; } +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) void DecodeFourChar(UInt32 fc, std::string* out) { std::stringstream ss; ss << '\''; @@ -127,4 +128,82 @@ MacOSVersionName GetOSVersionName() { } return kMacOSNewer; } + +bool RunAppleScript(const std::string& script) { + // TODO(thaloun): Add a .mm file that contains something like this: + // NSString source from script + // NSAppleScript* appleScript = [[NSAppleScript alloc] initWithSource:&source] + // if (appleScript != nil) { + // [appleScript executeAndReturnError:nil] + // [appleScript release] +#ifndef CARBON_DEPRECATED + ComponentInstance component = NULL; + AEDesc script_desc; + AEDesc result_data; + OSStatus err; + OSAID script_id, result_id; + + AECreateDesc(typeNull, NULL, 0, &script_desc); + AECreateDesc(typeNull, NULL, 0, &result_data); + script_id = kOSANullScript; + result_id = kOSANullScript; + + component = OpenDefaultComponent(kOSAComponentType, typeAppleScript); + if (component == NULL) { + LOG(LS_ERROR) << "Failed opening Apple Script component"; + return false; + } + err = AECreateDesc(typeUTF8Text, script.data(), script.size(), &script_desc); + if (err != noErr) { + CloseComponent(component); + LOG(LS_ERROR) << "Failed creating Apple Script description"; + return false; + } + + err = OSACompile(component, &script_desc, kOSAModeCanInteract, &script_id); + if (err != noErr) { + AEDisposeDesc(&script_desc); + if (script_id != kOSANullScript) { + OSADispose(component, script_id); + } + CloseComponent(component); + LOG(LS_ERROR) << "Error compiling Apple Script"; + return false; + } + + err = OSAExecute(component, script_id, kOSANullScript, kOSAModeCanInteract, + &result_id); + + if (err == errOSAScriptError) { + LOG(LS_ERROR) << "Error when executing Apple Script: " << script; + AECreateDesc(typeNull, NULL, 0, &result_data); + OSAScriptError(component, kOSAErrorMessage, typeChar, &result_data); + int len = AEGetDescDataSize(&result_data); + char* data = (char*)malloc(len); + if (data != NULL) { + err = AEGetDescData(&result_data, data, len); + LOG(LS_ERROR) << "Script error: " << std::string(data, len); + } + AEDisposeDesc(&script_desc); + AEDisposeDesc(&result_data); + return false; + } + AEDisposeDesc(&script_desc); + if (script_id != kOSANullScript) { + OSADispose(component, script_id); + } + if (result_id != kOSANullScript) { + OSADispose(component, result_id); + } + CloseComponent(component); + return true; +#else + // TODO(thaloun): Support applescripts with the NSAppleScript API. + return false; +#endif // CARBON_DEPRECATED +} +#endif // WEBRTC_MAC && !defined(WEBRTC_IOS) + +/////////////////////////////////////////////////////////////////////////////// + } // namespace rtc diff --git a/webrtc/base/macutils.h b/webrtc/base/macutils.h index fdcb3eee5b..78c442f0e9 100644 --- a/webrtc/base/macutils.h +++ b/webrtc/base/macutils.h @@ -12,6 +12,9 @@ #define WEBRTC_BASE_MACUTILS_H__ #include +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) +#include +#endif #include namespace rtc { @@ -41,6 +44,10 @@ enum MacOSVersionName { }; MacOSVersionName GetOSVersionName(); + +// Runs the given apple script. Only supports scripts that does not +// require user interaction. +bool RunAppleScript(const std::string& script); #endif /////////////////////////////////////////////////////////////////////////////// diff --git a/webrtc/base/macutils_unittest.cc b/webrtc/base/macutils_unittest.cc index d3e33e43b5..e295eaae08 100644 --- a/webrtc/base/macutils_unittest.cc +++ b/webrtc/base/macutils_unittest.cc @@ -16,3 +16,22 @@ TEST(MacUtilsTest, GetOsVersionName) { LOG(LS_INFO) << "GetOsVersionName " << ver; EXPECT_NE(rtc::kMacOSUnknown, ver); } + +TEST(MacUtilsTest, RunAppleScriptCompileError) { + std::string script("set value to to 5"); + EXPECT_FALSE(rtc::RunAppleScript(script)); +} + +TEST(MacUtilsTest, RunAppleScriptRuntimeError) { + std::string script("set value to 5 / 0"); + EXPECT_FALSE(rtc::RunAppleScript(script)); +} + +#ifdef CARBON_DEPRECATED +TEST(MacUtilsTest, DISABLED_RunAppleScriptSuccess) { +#else +TEST(MacUtilsTest, RunAppleScriptSuccess) { +#endif + std::string script("set value to 5"); + EXPECT_TRUE(rtc::RunAppleScript(script)); +} diff --git a/webrtc/base/macwindowpicker.cc b/webrtc/base/macwindowpicker.cc index d43d0e8cbf..bb97d20f1a 100644 --- a/webrtc/base/macwindowpicker.cc +++ b/webrtc/base/macwindowpicker.cc @@ -106,7 +106,74 @@ bool MacWindowPicker::IsVisible(const WindowId& id) { } bool MacWindowPicker::MoveToFront(const WindowId& id) { - return false; + // Init if we're not already initialized. + if (get_window_list_desc_ == NULL && !Init()) { + return false; + } + CGWindowID ids[1]; + ids[0] = id.id(); + CFArrayRef window_id_array = + CFArrayCreate(NULL, reinterpret_cast(&ids), 1, NULL); + + CFArrayRef window_array = + reinterpret_cast( + get_window_list_desc_)(window_id_array); + if (window_array == NULL || 0 == CFArrayGetCount(window_array)) { + // Could not find the window. It might have been closed. + LOG(LS_INFO) << "Window not found"; + CFRelease(window_id_array); + return false; + } + + CFDictionaryRef window = reinterpret_cast( + CFArrayGetValueAtIndex(window_array, 0)); + CFStringRef window_name_ref = reinterpret_cast( + CFDictionaryGetValue(window, kCGWindowName)); + CFNumberRef application_pid = reinterpret_cast( + CFDictionaryGetValue(window, kCGWindowOwnerPID)); + + int pid_val; + CFNumberGetValue(application_pid, kCFNumberIntType, &pid_val); + std::string window_name; + ToUtf8(window_name_ref, &window_name); + + // Build an applescript that sets the selected window to front + // within the application. Then set the application to front. + bool result = true; + std::stringstream ss; + ss << "tell application \"System Events\"\n" + << "set proc to the first item of (every process whose unix id is " + << pid_val + << ")\n" + << "tell proc to perform action \"AXRaise\" of window \"" + << window_name + << "\"\n" + << "set the frontmost of proc to true\n" + << "end tell"; + if (!RunAppleScript(ss.str())) { + // This might happen to for example X applications where the X + // server spawns of processes with their own PID but the X server + // is still registered as owner to the application windows. As a + // workaround, we put the X server process to front, meaning that + // all X applications will show up. The drawback with this + // workaround is that the application that we really wanted to set + // to front might be behind another X application. + ProcessSerialNumber psn; + pid_t pid = pid_val; + int res = GetProcessForPID(pid, &psn); + if (res != 0) { + LOG(LS_ERROR) << "Failed getting process for pid"; + result = false; + } + res = SetFrontProcess(&psn); + if (res != 0) { + LOG(LS_ERROR) << "Failed setting process to front"; + result = false; + } + } + CFRelease(window_id_array); + CFRelease(window_array); + return result; } bool MacWindowPicker::GetDesktopList(DesktopDescriptionList* descriptions) { diff --git a/webrtc/base/proxydetect.cc b/webrtc/base/proxydetect.cc index 71910aa2d4..10e7a02241 100644 --- a/webrtc/base/proxydetect.cc +++ b/webrtc/base/proxydetect.cc @@ -1170,6 +1170,9 @@ bool GetMacProxySettings(ProxyInfo* proxy) { result = p_putPasswordInProxyInfo(proxy); } + // We created the dictionary with something that had the + // word 'copy' in it, so we have to release it, according + // to the Carbon memory management standards. CFRelease(proxyDict); } else { LOG(LS_ERROR) << "SCDynamicStoreCopyProxies failed"; @@ -1220,6 +1223,9 @@ bool GetiOSProxySettings(ProxyInfo* proxy) { result = true; } + // We created the dictionary with something that had the + // word 'copy' in it, so we have to release it, according + // to the Carbon memory management standards. CFRelease(proxy_dict); return result; diff --git a/webrtc/base/unixfilesystem.cc b/webrtc/base/unixfilesystem.cc index 4dea6b8620..b474324192 100644 --- a/webrtc/base/unixfilesystem.cc +++ b/webrtc/base/unixfilesystem.cc @@ -17,7 +17,7 @@ #include #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) -#include +#include #include #include #include "webrtc/base/macutils.h" @@ -50,13 +50,13 @@ #include "webrtc/base/stream.h" #include "webrtc/base/stringutils.h" -#if defined(WEBRTC_MAC) -// Defined in applefilesystem.mm. No header file to discourage use +#if defined(WEBRTC_IOS) +// Defined in iosfilesystem.mm. No header file to discourage use // elsewhere; other places should use GetApp{Data,Temp}Folder() in // this file. Don't copy/paste. I mean it. -char* AppleDataDirectory(); -char* AppleTempDirectory(); -void AppleAppName(rtc::Pathname* path); +char* IOSDataDirectory(); +char* IOSTempDirectory(); +void IOSAppName(rtc::Pathname* path); #endif namespace rtc { @@ -81,9 +81,9 @@ void UnixFilesystem::SetAppTempFolder(const std::string& folder) { UnixFilesystem::UnixFilesystem() { #if defined(WEBRTC_IOS) if (!provided_app_data_folder_) - provided_app_data_folder_ = AppleDataDirectory(); + provided_app_data_folder_ = IOSDataDirectory(); if (!provided_app_temp_folder_) - provided_app_temp_folder_ = AppleTempDirectory(); + provided_app_temp_folder_ = IOSTempDirectory(); #endif } @@ -357,10 +357,24 @@ bool UnixFilesystem::GetFileTime(const Pathname& path, FileTimeType which, } bool UnixFilesystem::GetAppPathname(Pathname* path) { -#if defined(__native_client__) +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) + ProcessSerialNumber psn = { 0, kCurrentProcess }; + CFDictionaryRef procinfo = ProcessInformationCopyDictionary(&psn, + kProcessDictionaryIncludeAllInformationMask); + if (NULL == procinfo) + return false; + CFStringRef cfpath = (CFStringRef) CFDictionaryGetValue(procinfo, + kIOBundleExecutableKey); + std::string path8; + bool success = ToUtf8(cfpath, &path8); + CFRelease(procinfo); + if (success) + path->SetPathname(path8); + return success; +#elif defined(__native_client__) return false; -#elif defined(WEBRTC_MAC) - AppleAppName(path); +#elif WEBRTC_IOS + IOSAppName(path); return true; #else // WEBRTC_MAC && !defined(WEBRTC_IOS) char buffer[PATH_MAX + 2]; diff --git a/webrtc/engine_configurations.h b/webrtc/engine_configurations.h index 011bdaeff0..be993ed273 100644 --- a/webrtc/engine_configurations.h +++ b/webrtc/engine_configurations.h @@ -60,6 +60,7 @@ // ---------------------------------------------------------------------------- #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) +// #define CARBON_RENDERING #define COCOA_RENDERING #endif diff --git a/webrtc/media/BUILD.gn b/webrtc/media/BUILD.gn index 5545aef066..029d143163 100644 --- a/webrtc/media/BUILD.gn +++ b/webrtc/media/BUILD.gn @@ -186,6 +186,17 @@ rtc_source_set("rtc_media") { "strmiids.lib", ] } + if (is_mac && current_cpu == "x86") { + sources += [ + "devices/carbonvideorenderer.cc", + "devices/carbonvideorenderer.h", + ] + libs += [ "Carbon.framework" ] + } + if (is_ios || (is_mac && current_cpu != "x86")) { + defines += [ "CARBON_DEPRECATED=YES" ] + } + deps += [ "..:webrtc_common", "../api:call_api", diff --git a/webrtc/media/devices/carbonvideorenderer.cc b/webrtc/media/devices/carbonvideorenderer.cc new file mode 100644 index 0000000000..766904231c --- /dev/null +++ b/webrtc/media/devices/carbonvideorenderer.cc @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Implementation of CarbonVideoRenderer + +#include "webrtc/media/devices/carbonvideorenderer.h" + +#include "webrtc/base/logging.h" +#include "webrtc/media/base/videocommon.h" +#include "webrtc/media/base/videoframe.h" + +namespace cricket { + +CarbonVideoRenderer::CarbonVideoRenderer(int x, int y) + : image_width_(0), + image_height_(0), + x_(x), + y_(y), + window_ref_(NULL) { +} + +CarbonVideoRenderer::~CarbonVideoRenderer() { + if (window_ref_) { + DisposeWindow(window_ref_); + } +} + +// Called from the main event loop. All renderering needs to happen on +// the main thread. +OSStatus CarbonVideoRenderer::DrawEventHandler(EventHandlerCallRef handler, + EventRef event, + void* data) { + OSStatus status = noErr; + CarbonVideoRenderer* renderer = static_cast(data); + if (renderer != NULL) { + if (!renderer->DrawFrame()) { + LOG(LS_ERROR) << "Failed to draw frame."; + } + } + return status; +} + +bool CarbonVideoRenderer::DrawFrame() { + // Grab the image lock to make sure it is not changed why we'll draw it. + rtc::CritScope cs(&image_crit_); + + if (image_.get() == NULL) { + // Nothing to draw, just return. + return true; + } + int width = image_width_; + int height = image_height_; + CGDataProviderRef provider = + CGDataProviderCreateWithData(NULL, image_.get(), width * height * 4, + NULL); + CGColorSpaceRef color_space_ref = CGColorSpaceCreateDeviceRGB(); + CGBitmapInfo bitmap_info = kCGBitmapByteOrderDefault; + CGColorRenderingIntent rendering_intent = kCGRenderingIntentDefault; + CGImageRef image_ref = CGImageCreate(width, height, 8, 32, width * 4, + color_space_ref, bitmap_info, provider, + NULL, false, rendering_intent); + CGDataProviderRelease(provider); + + if (image_ref == NULL) { + return false; + } + CGContextRef context; + SetPortWindowPort(window_ref_); + if (QDBeginCGContext(GetWindowPort(window_ref_), &context) != noErr) { + CGImageRelease(image_ref); + return false; + } + Rect window_bounds; + GetWindowPortBounds(window_ref_, &window_bounds); + + // Anchor the image to the top left corner. + int x = 0; + int y = window_bounds.bottom - CGImageGetHeight(image_ref); + CGRect dst_rect = CGRectMake(x, y, CGImageGetWidth(image_ref), + CGImageGetHeight(image_ref)); + CGContextDrawImage(context, dst_rect, image_ref); + CGContextFlush(context); + QDEndCGContext(GetWindowPort(window_ref_), &context); + CGImageRelease(image_ref); + return true; +} + +bool CarbonVideoRenderer::SetSize(int width, int height) { + if (width != image_width_ || height != image_height_) { + // Grab the image lock while changing its size. + rtc::CritScope cs(&image_crit_); + image_width_ = width; + image_height_ = height; + image_.reset(new uint8_t[width * height * 4]); + memset(image_.get(), 255, width * height * 4); + } + return true; +} + +void CarbonVideoRenderer::OnFrame(const VideoFrame& video_frame) { + { + const cricket::WebRtcVideoFrame frame( + webrtc::I420Buffer::Rotate(video_frame.video_frame_buffer(), + video_frame.rotation()), + webrtc::kVideoRotation_0, video_frame.timestamp_us()); + + if (!SetSize(frame.width(), frame.height())) { + return false; + } + + // Grab the image lock so we are not trashing up the image being drawn. + rtc::CritScope cs(&image_crit_); + frame.ConvertToRgbBuffer(cricket::FOURCC_ABGR, + image_.get(), + static_cast(frame.width()) * + frame.height() * 4, + frame.width() * 4); + } + + // Trigger a repaint event for the whole window. + Rect bounds; + InvalWindowRect(window_ref_, GetWindowPortBounds(window_ref_, &bounds)); + return true; +} + +bool CarbonVideoRenderer::Initialize() { + OSStatus err; + WindowAttributes attributes = + kWindowStandardDocumentAttributes | + kWindowLiveResizeAttribute | + kWindowFrameworkScaledAttribute | + kWindowStandardHandlerAttribute; + + struct Rect bounds; + bounds.top = y_; + bounds.bottom = 480; + bounds.left = x_; + bounds.right = 640; + err = CreateNewWindow(kDocumentWindowClass, attributes, + &bounds, &window_ref_); + if (!window_ref_ || err != noErr) { + LOG(LS_ERROR) << "CreateNewWindow failed, error code: " << err; + return false; + } + static const EventTypeSpec event_spec = { + kEventClassWindow, + kEventWindowDrawContent + }; + + err = InstallWindowEventHandler( + window_ref_, + NewEventHandlerUPP(CarbonVideoRenderer::DrawEventHandler), + GetEventTypeCount(event_spec), + &event_spec, + this, + NULL); + if (err != noErr) { + LOG(LS_ERROR) << "Failed to install event handler, error code: " << err; + return false; + } + SelectWindow(window_ref_); + ShowWindow(window_ref_); + return true; +} + +} // namespace cricket diff --git a/webrtc/media/devices/carbonvideorenderer.h b/webrtc/media/devices/carbonvideorenderer.h new file mode 100644 index 0000000000..e90c1314ee --- /dev/null +++ b/webrtc/media/devices/carbonvideorenderer.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// Definition of class CarbonVideoRenderer that implements the abstract class +// cricket::VideoRenderer via Carbon. + +#ifndef WEBRTC_MEDIA_DEVICES_CARBONVIDEORENDERER_H_ +#define WEBRTC_MEDIA_DEVICES_CARBONVIDEORENDERER_H_ + +#include + +#include + +#include "webrtc/base/criticalsection.h" +#include "webrtc/media/base/videosinkinterface.h" + +namespace cricket { + +class CarbonVideoRenderer + : public rtc::VideoSinkInterface { + public: + CarbonVideoRenderer(int x, int y); + virtual ~CarbonVideoRenderer(); + + // Implementation of VideoSinkInterface. + void OnFrame(const VideoFrame& frame) override; + + // Needs to be called on the main thread. + bool Initialize(); + + private: + bool SetSize(int width, int height); + bool DrawFrame(); + + static OSStatus DrawEventHandler(EventHandlerCallRef handler, + EventRef event, + void* data); + std::unique_ptr image_; + rtc::CriticalSection image_crit_; + int image_width_; + int image_height_; + int x_; + int y_; + WindowRef window_ref_; +}; + +} // namespace cricket + +#endif // WEBRTC_MEDIA_DEVICES_CARBONVIDEORENDERER_H_ diff --git a/webrtc/media/devices/videorendererfactory.h b/webrtc/media/devices/videorendererfactory.h index bdc56861e8..0bacdbd09a 100644 --- a/webrtc/media/devices/videorendererfactory.h +++ b/webrtc/media/devices/videorendererfactory.h @@ -17,6 +17,8 @@ #include "webrtc/media/base/videosinkinterface.h" #if defined(WEBRTC_LINUX) && defined(HAVE_GTK) #include "webrtc/media/devices/gtkvideorenderer.h" +#elif defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) && !defined(CARBON_DEPRECATED) +#include "webrtc/media/devices/carbonvideorenderer.h" #elif defined(WIN32) #include "webrtc/media/devices/gdivideorenderer.h" #endif @@ -30,6 +32,16 @@ class VideoRendererFactory { int y) { #if defined(WEBRTC_LINUX) && defined(HAVE_GTK) return new GtkVideoRenderer(x, y); + #elif defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) && \ + !defined(CARBON_DEPRECATED) + CarbonVideoRenderer* renderer = new CarbonVideoRenderer(x, y); + // Needs to be initialized on the main thread. + if (renderer->Initialize()) { + return renderer; + } else { + delete renderer; + return NULL; + } #elif defined(WIN32) return new GdiVideoRenderer(x, y); #else diff --git a/webrtc/media/media.gyp b/webrtc/media/media.gyp index 5efdec178a..2eb72710d9 100644 --- a/webrtc/media/media.gyp +++ b/webrtc/media/media.gyp @@ -173,6 +173,24 @@ }, }, }], + ['OS=="mac" and target_arch=="ia32"', { + 'sources': [ + 'devices/carbonvideorenderer.cc', + 'devices/carbonvideorenderer.h', + ], + 'link_settings': { + 'xcode_settings': { + 'OTHER_LDFLAGS': [ + '-framework Carbon', + ], + }, + }, + }], + ['OS=="ios" or (OS=="mac" and target_arch!="ia32")', { + 'defines': [ + 'CARBON_DEPRECATED=YES', + ], + }], ], }, # target rtc_media ], # targets. diff --git a/webrtc/modules/desktop_capture/mac/desktop_configuration.h b/webrtc/modules/desktop_capture/mac/desktop_configuration.h index 241c5b9db2..bb2339bb0f 100644 --- a/webrtc/modules/desktop_capture/mac/desktop_configuration.h +++ b/webrtc/modules/desktop_capture/mac/desktop_configuration.h @@ -12,6 +12,7 @@ #define WEBRTC_MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_CONFIGURATION_H_ #include +#include #include #include "webrtc/typedefs.h" @@ -48,7 +49,7 @@ struct MacDesktopConfiguration { // Returns the desktop & display configurations in Cocoa-style "bottom-up" // (the origin is the bottom-left of the primary monitor, and coordinates - // increase as you move up the screen). + // increase as you move up the screen) or Carbon-style "top-down" coordinates. static MacDesktopConfiguration GetCurrent(Origin origin); // Returns true if the given desktop configuration equals this one. diff --git a/webrtc/webrtc_tests.gypi b/webrtc/webrtc_tests.gypi index 47a6833a4a..f93b5c0a84 100644 --- a/webrtc/webrtc_tests.gypi +++ b/webrtc/webrtc_tests.gypi @@ -165,6 +165,11 @@ 'base/sslstreamadapter_unittest.cc', ], }], + ['OS=="ios" or (OS=="mac" and target_arch!="ia32")', { + 'defines': [ + 'CARBON_DEPRECATED=YES', + ], + }], ['use_quic==1', { 'sources': [ 'p2p/quic/quicconnectionhelper_unittest.cc', @@ -498,6 +503,11 @@ 'base/macsocketserver_unittest.cc', ], }], + ['OS=="ios" or (OS=="mac" and target_arch!="ia32")', { + 'defines': [ + 'CARBON_DEPRECATED=YES', + ], + }], ], }, ],