PageRenderTime 81ms CodeModel.GetById 14ms app.highlight 62ms RepoModel.GetById 1ms app.codeStats 0ms

/core/externals/google-toolbox-for-mac/AppKit/GTMNSWorkspace+Running.m

http://macfuse.googlecode.com/
Objective C | 291 lines | 230 code | 32 blank | 29 comment | 28 complexity | 8e52e324b2cb19a16bddbe1e246ef2e0 MD5 | raw file
  1//
  2//  GTMNSWorkspace+Running.m
  3//
  4//  Copyright 2007-2008 Google Inc.
  5//
  6//  Licensed under the Apache License, Version 2.0 (the "License"); you may not
  7//  use this file except in compliance with the License.  You may obtain a copy
  8//  of the License at
  9//
 10//  http://www.apache.org/licenses/LICENSE-2.0
 11//
 12//  Unless required by applicable law or agreed to in writing, software
 13//  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 14//  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 15//  License for the specific language governing permissions and limitations under
 16//  the License.
 17//
 18
 19#import "GTMNSWorkspace+Running.h"
 20#import <Carbon/Carbon.h>
 21#import <unistd.h>
 22#import "GTMSystemVersion.h"
 23
 24
 25NSString *const kGTMWorkspaceRunningPSN = @"PSN";
 26NSString *const kGTMWorkspaceRunningFlavor = @"Flavor";
 27NSString *const kGTMWorkspaceRunningAttributes = @"Attributes";
 28NSString *const kGTMWorkspaceRunningParentPSN = @"ParentPSN";
 29NSString *const kGTMWorkspaceRunningFileType = @"FileType";
 30NSString *const kGTMWorkspaceRunningFileCreator = @"FileCreator";
 31NSString *const kGTMWorkspaceRunningPID = @"pid";
 32NSString *const kGTMWorkspaceRunningLSBackgroundOnly = @"LSBackgroundOnly";
 33NSString *const kGTMWorkspaceRunningLSUIElement = @"LSUIElement";
 34NSString *const kGTMWorkspaceRunningIsHidden = @"IsHiddenAttr";
 35NSString *const kGTMWorkspaceRunningCheckedIn = @"IsCheckedInAttr";
 36NSString *const kGTMWorkspaceRunningLSUIPresentationMode
 37  = @"LSUIPresentationMode";
 38NSString *const kGTMWorkspaceRunningBundlePath = @"BundlePath";
 39NSString *const kGTMWorkspaceRunningBundleVersion = @"CFBundleVersion";
 40
 41@interface GTMWorkspaceRunningApplicationList : NSObject {
 42 @private
 43  NSArray *launchedApps_;
 44}
 45+ (GTMWorkspaceRunningApplicationList *)sharedApplicationList;
 46- (NSArray *)launchedApplications;
 47- (void)didLaunchOrTerminateApp:(NSNotification *)notification;
 48@end
 49
 50@implementation NSWorkspace (GTMWorkspaceRunningAdditions)
 51
 52/// Returns a YES/NO if a process w/ the given identifier is running
 53- (BOOL)gtm_isAppWithIdentifierRunning:(NSString *)identifier {
 54  if ([identifier length] == 0) return NO;
 55  NSArray *launchedApps = [self gtm_launchedApplications];
 56  NSArray *buildIDs
 57    = [launchedApps valueForKey:@"NSApplicationBundleIdentifier"];
 58  return [buildIDs containsObject:identifier];
 59}
 60
 61- (NSDictionary *)gtm_processInfoDictionaryForPID:(pid_t)pid {
 62  NSDictionary *dict = nil;
 63  ProcessSerialNumber psn;
 64  if (GetProcessForPID(pid, &psn) == noErr) {
 65    dict = [self gtm_processInfoDictionaryForPSN:&psn];
 66  }
 67  return dict;
 68}
 69
 70- (NSDictionary *)gtm_processInfoDictionaryForPSN:(ProcessSerialNumberPtr const)psn {
 71  NSDictionary *dict = nil;
 72  if (psn) {
 73    CFDictionaryRef cfDict
 74      = ProcessInformationCopyDictionary(psn,
 75                                         kProcessDictionaryIncludeAllInformationMask);
 76    dict = GTMCFAutorelease(cfDict);
 77  }
 78  return dict;
 79}
 80
 81- (NSDictionary *)gtm_processInfoDictionary {
 82  NSDictionary *dict = nil;
 83  ProcessSerialNumber selfNumber;
 84  if (MacGetCurrentProcess(&selfNumber) == noErr) {
 85    dict = [self gtm_processInfoDictionaryForPSN:&selfNumber];
 86  }
 87  return dict;
 88}
 89
 90- (NSDictionary *)gtm_processInfoDictionaryForActiveApp {
 91  NSDictionary *processDict = nil;
 92  ProcessSerialNumber psn;
 93  OSStatus status = GetFrontProcess(&psn);
 94  if (status == noErr) {
 95    processDict = [self gtm_processInfoDictionaryForPSN:&psn];
 96  }
 97  return processDict;
 98}
 99
100- (BOOL)gtm_wasLaunchedAsLoginItem {
101  // If the launching process was 'loginwindow', we were launched as a login
102  // item
103  return [self gtm_wasLaunchedByProcess:@"com.apple.loginwindow"];
104}
105
106- (BOOL)gtm_wasLaunchedByProcess:(NSString*)bundleid {
107  BOOL wasLaunchedByProcess = NO;
108  NSDictionary *processInfo = [self gtm_processInfoDictionary];
109  if (processInfo) {
110    NSNumber *processNumber
111      = [processInfo objectForKey:kGTMWorkspaceRunningParentPSN];
112    ProcessSerialNumber parentPSN
113      = [self gtm_numberToProcessSerialNumber:processNumber];
114    NSDictionary *parentProcessInfo
115      = [self gtm_processInfoDictionaryForPSN:&parentPSN];
116    NSString *parentBundle
117      = [parentProcessInfo objectForKey:kGTMWorkspaceRunningBundleIdentifier];
118    wasLaunchedByProcess
119      = [parentBundle isEqualToString:bundleid];
120  }
121  return wasLaunchedByProcess;
122}
123
124- (BOOL)gtm_processSerialNumber:(ProcessSerialNumber*)outPSN
125                   withBundleID:(NSString*)bundleID {
126  if (!outPSN || [bundleID length] == 0) {
127    return NO;
128  }
129
130  NSArray *apps = [self gtm_launchedApplications];
131
132  NSEnumerator *enumerator = [apps objectEnumerator];
133  NSDictionary *dict;
134
135  while ((dict = [enumerator nextObject])) {
136    NSString *nextID = [dict objectForKey:@"NSApplicationBundleIdentifier"];
137
138    if ([nextID isEqualToString:bundleID]) {
139      NSNumber *psn
140        = [dict objectForKey:@"NSApplicationProcessSerialNumberLow"];
141      outPSN->lowLongOfPSN = [psn unsignedIntValue];
142
143      psn = [dict objectForKey:@"NSApplicationProcessSerialNumberHigh"];
144      outPSN->highLongOfPSN = [psn unsignedIntValue];
145
146      return YES;
147    }
148  }
149
150  return NO;
151}
152
153- (ProcessSerialNumber)gtm_numberToProcessSerialNumber:(NSNumber*)number {
154  // There is a bug in Tiger where they were packing ProcessSerialNumbers
155  // incorrectly into the longlong that they stored in the dictionary.
156  // This fixes it.
157  ProcessSerialNumber outPSN = { kNoProcess, kNoProcess};
158  if (number) {
159    long long temp = [number longLongValue];
160    UInt32 hi = (UInt32)((temp >> 32) & 0x00000000FFFFFFFFLL);
161    UInt32 lo = (UInt32)((temp >> 0) & 0x00000000FFFFFFFFLL);
162#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
163    outPSN.highLongOfPSN = hi;
164    outPSN.lowLongOfPSN = lo;
165#else  // MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4
166    if ([GTMSystemVersion isLeopardOrGreater]) {
167      outPSN.highLongOfPSN = hi;
168      outPSN.lowLongOfPSN = lo;
169    } else {
170#if TARGET_RT_BIG_ENDIAN
171      outPSN.highLongOfPSN = hi;
172      outPSN.lowLongOfPSN = lo;
173#else
174      outPSN.highLongOfPSN = lo;
175      outPSN.lowLongOfPSN = hi;
176#endif  // TARGET_RT_BIG_ENDIAN
177    }
178#endif  // MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4
179  }
180  return outPSN;
181}
182
183- (NSArray *)gtm_launchedApplications {
184  GTMWorkspaceRunningApplicationList *list
185    = [GTMWorkspaceRunningApplicationList sharedApplicationList];
186  return [list launchedApplications];
187}
188@end
189
190@implementation GTMWorkspaceRunningApplicationList
191
192+ (GTMWorkspaceRunningApplicationList *)sharedApplicationList {
193  static GTMWorkspaceRunningApplicationList *obj;
194  if (!obj) {
195    obj = [[self alloc] init];
196  }
197  return obj;
198}
199
200- (id)init {
201  if ((self = [super init])) {
202    [self didLaunchOrTerminateApp:nil];
203  }
204  return self;
205}
206
207- (void)finalize {
208  [self didLaunchOrTerminateApp:nil];
209  [super finalize];
210}
211
212- (void)dealloc {
213  [self didLaunchOrTerminateApp:nil];
214  [super dealloc];
215}
216
217- (void)didLaunchOrTerminateApp:(NSNotification *)notification {
218  @synchronized (self) {
219    [launchedApps_ release];
220    NSNotificationCenter *workSpaceNC
221      = [[NSWorkspace sharedWorkspace] notificationCenter];
222    [workSpaceNC removeObserver:self];
223    launchedApps_ = nil;
224  }
225}
226
227- (NSArray *)currentApps {
228  // Not using any NSWorkspace calls because they are not documented as being
229  // threadsafe.
230  ProcessSerialNumber psn = { kNoProcess, kNoProcess };
231  NSMutableArray *launchedApps = [NSMutableArray array];
232  while (GetNextProcess(&psn) == noErr) {
233    CFDictionaryRef cfDict
234      = ProcessInformationCopyDictionary(&psn,
235                                         kProcessDictionaryIncludeAllInformationMask);
236    NSDictionary *carbonDict = GTMCFAutorelease(cfDict);
237    // Check to make sure we actually have a dictionary. The process could
238    // have disappeared between the call to GetNextProcess and
239    // ProcessInformationCopyDictionary.
240    if (carbonDict) {
241      NSMutableDictionary *cocoaDict = [NSMutableDictionary dictionary];
242      NSString *path = [carbonDict objectForKey:@"BundlePath"];
243      if (path) {
244        [cocoaDict setObject:path forKey:@"NSApplicationPath"];
245      }
246      NSString *name = [carbonDict objectForKey:(id)kCFBundleNameKey];
247      if (name) {
248        [cocoaDict setObject:name forKey:@"NSApplicationName"];
249      }
250      NSString *bundleID = [carbonDict objectForKey:(id)kCFBundleIdentifierKey];
251      if (bundleID) {
252        [cocoaDict setObject:bundleID forKey:@"NSApplicationBundleIdentifier"];
253      }
254      NSNumber *pid = [carbonDict objectForKey:@"pid"];
255      if (pid) {
256        [cocoaDict setObject:pid forKey:@"NSApplicationProcessIdentifier"];
257      }
258      [cocoaDict setObject:[NSNumber numberWithUnsignedLong:psn.highLongOfPSN]
259                    forKey:@"NSApplicationProcessSerialNumberHigh"];
260      [cocoaDict setObject:[NSNumber numberWithUnsignedLong:psn.lowLongOfPSN]
261                    forKey:@"NSApplicationProcessSerialNumberLow"];
262      [launchedApps addObject:cocoaDict];
263    }
264  }
265  return launchedApps;
266}
267
268
269- (NSArray *)launchedApplications {
270  NSArray *localReturn = nil;
271  @synchronized (self) {
272    if (!launchedApps_) {
273      launchedApps_ = [[self currentApps] retain];
274      NSWorkspace *ws = [NSWorkspace sharedWorkspace];
275      NSNotificationCenter *workSpaceNC = [ws notificationCenter];
276      [workSpaceNC addObserver:self
277                      selector:@selector(didLaunchOrTerminateApp:)
278                          name:NSWorkspaceDidLaunchApplicationNotification
279                        object:nil];
280      [workSpaceNC addObserver:self
281                      selector:@selector(didLaunchOrTerminateApp:)
282                          name:NSWorkspaceDidTerminateApplicationNotification
283                        object:nil];
284    }
285    // We want to keep launchedApps_ in the autoreleasepool of this thread
286    localReturn = [launchedApps_ retain];
287  }
288  return [localReturn autorelease];
289}
290
291@end