/* * libjingle * Copyright 2012, Google Inc * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import "talk/base/maccocoasocketserver.h" #import #import #include #include "talk/base/scoped_autorelease_pool.h" // MacCocoaSocketServerHelper serves as a delegate to NSMachPort or a target for // a timeout. @interface MacCocoaSocketServerHelper : NSObject { // This is a weak reference. This works fine since the // talk_base::MacCocoaSocketServer owns this object. talk_base::MacCocoaSocketServer* socketServer_; // Weak. } @end @implementation MacCocoaSocketServerHelper - (id)initWithSocketServer:(talk_base::MacCocoaSocketServer*)ss { self = [super init]; if (self) { socketServer_ = ss; } return self; } - (void)timerFired:(NSTimer*)timer { socketServer_->WakeUp(); } @end namespace talk_base { MacCocoaSocketServer::MacCocoaSocketServer() { helper_ = [[MacCocoaSocketServerHelper alloc] initWithSocketServer:this]; timer_ = nil; // Initialize the shared NSApplication [NSApplication sharedApplication]; } MacCocoaSocketServer::~MacCocoaSocketServer() { [timer_ invalidate]; [timer_ release]; [helper_ release]; } bool MacCocoaSocketServer::Wait(int cms, bool process_io) { talk_base::ScopedAutoreleasePool pool; if (!process_io && cms == 0) { // No op. return true; } if (!process_io) { // No way to listen to common modes and not get socket events, unless // we disable each one's callbacks. EnableSocketCallbacks(false); } if (kForever != cms) { // Install a timer that fires wakeup after cms has elapsed. timer_ = [NSTimer scheduledTimerWithTimeInterval:cms / 1000.0 target:helper_ selector:@selector(timerFired:) userInfo:nil repeats:NO]; [timer_ retain]; } // Run until WakeUp is called, which will call stop and exit this loop. [NSApp run]; if (!process_io) { // Reenable them. Hopefully this won't cause spurious callbacks or // missing ones while they were disabled. EnableSocketCallbacks(true); } return true; } void MacCocoaSocketServer::WakeUp() { // Timer has either fired or shortcutted. [timer_ invalidate]; [timer_ release]; timer_ = nil; [NSApp stop:nil]; // NSApp stop only exits after finishing processing of the // current event. Since we're potentially in a timer callback // and not an NSEvent handler, we need to trigger a dummy one // and turn the loop over. We may be able to skip this if we're // on the ss' thread and not inside the app loop already. NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined location:NSMakePoint(0,0) modifierFlags:0 timestamp:0 windowNumber:0 context:nil subtype:1 data1:1 data2:1]; [NSApp postEvent:event atStart:YES]; } } // namespace talk_base