| /* |
| * 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 <Foundation/Foundation.h> |
| #import <AppKit/AppKit.h> |
| #include <assert.h> |
| |
| #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 |