parent
716e1e4b0a
commit
e2396db29c
|
@ -22,6 +22,7 @@
|
|||
CD2F800C24455333009C3D77 /* AboutWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = CD2F800924455333009C3D77 /* AboutWindow.xib */; };
|
||||
CD2F800D24455333009C3D77 /* AboutWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = CD2F800B24455333009C3D77 /* AboutWindowController.m */; };
|
||||
CD2F801724468A8C009C3D77 /* patrons.txt in Resources */ = {isa = PBXBuildFile; fileRef = CD2F801624468A8C009C3D77 /* patrons.txt */; };
|
||||
CD3052C72AF6D86600250347 /* OverSight Installer.app in Resources */ = {isa = PBXBuildFile; fileRef = CD3052C62AF6D86600250347 /* OverSight Installer.app */; };
|
||||
CD6836682391DB6F00CF19C1 /* security.plist in Resources */ = {isa = PBXBuildFile; fileRef = CD6836672391DB6F00CF19C1 /* security.plist */; };
|
||||
CD8FD5D523BAE2D200EFE0FB /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = CD8FD5D323BAE2D100EFE0FB /* Preferences.xib */; };
|
||||
CD8FD5D623BAE2D200EFE0FB /* PrefsWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = CD8FD5D423BAE2D200EFE0FB /* PrefsWindowController.m */; };
|
||||
|
@ -33,7 +34,6 @@
|
|||
CDC60991263CBD36006D1332 /* AVMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = CDC60990263CBD36006D1332 /* AVMonitor.m */; };
|
||||
CDC60994263CBEE7006D1332 /* Client.m in Sources */ = {isa = PBXBuildFile; fileRef = CDC60993263CBEE7006D1332 /* Client.m */; };
|
||||
CDCBF0DE26499DFF001D9F9A /* Event.m in Sources */ = {isa = PBXBuildFile; fileRef = CDCBF0DD26499DFF001D9F9A /* Event.m */; };
|
||||
CDFFBC572AEBD35E00560604 /* OverSight Installer.app in Resources */ = {isa = PBXBuildFile; fileRef = CDFFBC562AEBD35E00560604 /* OverSight Installer.app */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
|
@ -76,6 +76,7 @@
|
|||
CD2F800A24455333009C3D77 /* AboutWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AboutWindowController.h; sourceTree = "<group>"; };
|
||||
CD2F800B24455333009C3D77 /* AboutWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AboutWindowController.m; sourceTree = "<group>"; };
|
||||
CD2F801624468A8C009C3D77 /* patrons.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = patrons.txt; path = ../Shared/patrons.txt; sourceTree = "<group>"; };
|
||||
CD3052C62AF6D86600250347 /* OverSight Installer.app */ = {isa = PBXFileReference; lastKnownFileType = wrapper.application; path = "OverSight Installer.app"; sourceTree = "<group>"; };
|
||||
CD6836672391DB6F00CF19C1 /* security.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = security.plist; sourceTree = "<group>"; };
|
||||
CD8FD5D223BAE2D100EFE0FB /* PrefsWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PrefsWindowController.h; sourceTree = "<group>"; };
|
||||
CD8FD5D323BAE2D100EFE0FB /* Preferences.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = Preferences.xib; sourceTree = "<group>"; };
|
||||
|
@ -95,7 +96,6 @@
|
|||
CDC60993263CBEE7006D1332 /* Client.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Client.m; sourceTree = "<group>"; };
|
||||
CDCBF0DC26499DFF001D9F9A /* Event.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Event.h; sourceTree = "<group>"; };
|
||||
CDCBF0DD26499DFF001D9F9A /* Event.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Event.m; sourceTree = "<group>"; };
|
||||
CDFFBC562AEBD35E00560604 /* OverSight Installer.app */ = {isa = PBXFileReference; lastKnownFileType = wrapper.application; path = "OverSight Installer.app"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
@ -214,7 +214,7 @@
|
|||
CDE09AF12919829000561CFF /* Uninstaller */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
CDFFBC562AEBD35E00560604 /* OverSight Installer.app */,
|
||||
CD3052C62AF6D86600250347 /* OverSight Installer.app */,
|
||||
);
|
||||
path = Uninstaller;
|
||||
sourceTree = "<group>";
|
||||
|
@ -292,7 +292,7 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
CD8FD5D523BAE2D200EFE0FB /* Preferences.xib in Resources */,
|
||||
CDFFBC572AEBD35E00560604 /* OverSight Installer.app in Resources */,
|
||||
CD3052C72AF6D86600250347 /* OverSight Installer.app in Resources */,
|
||||
CD6836682391DB6F00CF19C1 /* security.plist in Resources */,
|
||||
CD2F800C24455333009C3D77 /* AboutWindow.xib in Resources */,
|
||||
7D16D6951F64E43300DB3161 /* UpdateWindow.xib in Resources */,
|
||||
|
@ -465,7 +465,7 @@
|
|||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = NO;
|
||||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 2.1.8;
|
||||
CURRENT_PROJECT_VERSION = 2.2.0;
|
||||
DEVELOPMENT_TEAM = VBG97UB4TA;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../Carthage/Build/Mac";
|
||||
|
@ -476,7 +476,7 @@
|
|||
LD_RUNPATH_SEARCH_PATHS = "$(LD_RUNPATH_SEARCH_PATHS_$(IS_MACCATALYST)) @executable_path/../Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = "";
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.0;
|
||||
MARKETING_VERSION = 2.1.8;
|
||||
MARKETING_VERSION = 2.2.0;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.objective-see.oversight";
|
||||
PRODUCT_NAME = OverSight;
|
||||
|
@ -491,7 +491,7 @@
|
|||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = NO;
|
||||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 2.1.8;
|
||||
CURRENT_PROJECT_VERSION = 2.2.0;
|
||||
DEVELOPMENT_TEAM = VBG97UB4TA;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../Carthage/Build/Mac";
|
||||
|
@ -502,7 +502,7 @@
|
|||
LD_RUNPATH_SEARCH_PATHS = "$(LD_RUNPATH_SEARCH_PATHS_$(IS_MACCATALYST)) @executable_path/../Frameworks";
|
||||
LIBRARY_SEARCH_PATHS = "";
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.0;
|
||||
MARKETING_VERSION = 2.1.8;
|
||||
MARKETING_VERSION = 2.2.0;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.objective-see.oversight";
|
||||
PRODUCT_NAME = OverSight;
|
||||
|
|
|
@ -43,15 +43,18 @@
|
|||
//initial camera state
|
||||
@property NSControlStateValue initialCameraState;
|
||||
|
||||
//last (camera) pid
|
||||
//last camera client
|
||||
@property NSInteger lastCameraClient;
|
||||
|
||||
//last mic off
|
||||
@property(nonatomic, retain)AVCaptureDevice* lastMicOff;
|
||||
|
||||
//last camera off
|
||||
@property(nonatomic, retain)AVCaptureDevice* lastCameraOff;
|
||||
|
||||
//last mic client
|
||||
@property NSInteger lastMicClient;
|
||||
|
||||
//last mic off
|
||||
@property(nonatomic, retain)AVCaptureDevice* lastMicOff;
|
||||
|
||||
//audio listeners
|
||||
@property(nonatomic, retain)NSMutableDictionary* audioListeners;
|
||||
|
||||
|
@ -61,18 +64,6 @@
|
|||
//per device events
|
||||
@property(nonatomic, retain)NSMutableDictionary* deviceEvents;
|
||||
|
||||
//audio event queue
|
||||
@property(nonatomic, retain)dispatch_queue_t audioEventQueue;
|
||||
|
||||
//audio event timer
|
||||
@property(nonatomic, retain)dispatch_source_t audioEventTimer;
|
||||
|
||||
//camera event queue
|
||||
@property(nonatomic, retain)dispatch_queue_t cameraEventQueue;
|
||||
|
||||
//camera event timer
|
||||
@property(nonatomic, retain)dispatch_source_t cameraEventTimer;
|
||||
|
||||
//last alert (default) interaction
|
||||
@property(nonatomic, retain)NSDate* lastNotificationDefaultAction;
|
||||
|
||||
|
|
|
@ -89,12 +89,6 @@ extern os_log_t logHandle;
|
|||
//per device events
|
||||
self.deviceEvents = [NSMutableDictionary dictionary];
|
||||
|
||||
//init audio event queue
|
||||
self.audioEventQueue = dispatch_queue_create("audio.event.timer", 0);
|
||||
|
||||
//init camera event queue
|
||||
self.cameraEventQueue = dispatch_queue_create("camera.event.timer", 0);
|
||||
|
||||
//enumerate active devices
|
||||
// then update status menu (on main thread)
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
|
@ -115,7 +109,6 @@ extern os_log_t logHandle;
|
|||
|
||||
//dbg msg
|
||||
os_log_debug(logHandle, "built-in camera: %{public}@ (device ID: %d)", self.builtInCamera.localizedName, [self getAVObjectID:self.builtInCamera]);
|
||||
|
||||
}
|
||||
|
||||
return self;
|
||||
|
@ -157,53 +150,97 @@ extern os_log_t logHandle;
|
|||
//macOS 14+
|
||||
if(@available(macOS 14.0, *)) {
|
||||
|
||||
//regex
|
||||
NSRegularExpression* regex = nil;
|
||||
//regex for mic log msg
|
||||
NSRegularExpression* micRegex = nil;
|
||||
|
||||
//regex for camera log msg
|
||||
NSRegularExpression* cameraRegex = nil;
|
||||
|
||||
//dbg msg
|
||||
os_log_debug(logHandle, ">= macOS 14+: Using log monitor for AV events via w/ 'added <private> endpoint <private> camera <private>'");
|
||||
os_log_debug(logHandle, ">= macOS 14+: Using log monitor for AV events via w/ (camera): 'added <private> endpoint <private> camera <private>' AND (mic): '-[MXCoreSession beginInterruption]: Session <ID: xx, PID = xyz,...'");
|
||||
|
||||
//init regex
|
||||
regex = [NSRegularExpression regularExpressionWithPattern:@"\\[\\{private\\}(\\d+)\\]" options:0 error:nil];
|
||||
//init mic regex
|
||||
micRegex = [NSRegularExpression regularExpressionWithPattern:@"PID = (\\d+)" options:0 error:nil];
|
||||
|
||||
//start logging
|
||||
[self.logMonitor start:[NSPredicate predicateWithFormat:@"subsystem=='com.apple.cmio'"] level:Log_Level_Debug callback:^(OSLogEvent* logEvent) {
|
||||
//init cam regex
|
||||
cameraRegex = [NSRegularExpression regularExpressionWithPattern:@"\\[\\{private\\}(\\d+)\\]" options:0 error:nil];
|
||||
|
||||
//match
|
||||
NSTextCheckingResult* match = nil;
|
||||
|
||||
//pid
|
||||
NSInteger pid = 0;
|
||||
//start log monitoring
|
||||
[self.logMonitor start:[NSPredicate predicateWithFormat:@"subsystem=='com.apple.cmio' OR subsystem=='com.apple.coremedia'"] level:Log_Level_Debug callback:^(OSLogEvent* logEvent) {
|
||||
|
||||
//sync to process
|
||||
@synchronized (self) {
|
||||
|
||||
//only interested msgs that end w/:
|
||||
//match
|
||||
NSTextCheckingResult* match = nil;
|
||||
|
||||
//pid
|
||||
NSInteger pid = 0;
|
||||
|
||||
//camera:
|
||||
// "added <private> endpoint <private> camera <private> = <pid>;"
|
||||
if(YES != [logEvent.composedMessage hasSuffix:@"added <private> endpoint <private> camera <private>"])
|
||||
if( (YES == [logEvent.subsystem isEqual:@"com.apple.cmio"]) &&
|
||||
(YES == [logEvent.composedMessage hasSuffix:@"added <private> endpoint <private> camera <private>"]) )
|
||||
{
|
||||
//reset
|
||||
self.lastCameraClient = 0;
|
||||
|
||||
//match on pid
|
||||
match = [cameraRegex firstMatchInString:logEvent.composedMessage options:0 range:NSMakeRange(0, logEvent.composedMessage.length)];
|
||||
if( (nil == match) ||
|
||||
(NSNotFound == match.range.location) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//extract/convert pid
|
||||
pid = [[logEvent.composedMessage substringWithRange:[match rangeAtIndex:1]] integerValue];
|
||||
if( (0 == pid) ||
|
||||
(-1 == pid) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//save
|
||||
self.lastCameraClient = pid;
|
||||
}
|
||||
|
||||
//mic:
|
||||
// "-[MXCoreSession beginInterruption]: Session <ID: xx, PID = xyz, ...":
|
||||
else if( (YES == [logEvent.subsystem isEqual:@"com.apple.coremedia"]) &&
|
||||
(YES == [logEvent.composedMessage hasPrefix:@"-MXCoreSession- -[MXCoreSession beginInterruption]"]) &&
|
||||
(YES == [logEvent.composedMessage hasSuffix:@"Recording = YES> is going active"]) )
|
||||
{
|
||||
|
||||
//reset
|
||||
self.lastMicClient = 0;
|
||||
|
||||
//match on pid
|
||||
match = [micRegex firstMatchInString:logEvent.composedMessage options:0 range:NSMakeRange(0, logEvent.composedMessage.length)];
|
||||
if( (nil == match) ||
|
||||
(NSNotFound == match.range.location) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//extract/convert pid
|
||||
pid = [[logEvent.composedMessage substringWithRange:[match rangeAtIndex:1]] integerValue];
|
||||
if( (0 == pid) ||
|
||||
(-1 == pid) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//save
|
||||
self.lastMicClient = pid;
|
||||
}
|
||||
|
||||
//msg not of interest
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//match on pid
|
||||
match = [regex firstMatchInString:logEvent.composedMessage options:0 range:NSMakeRange(0, logEvent.composedMessage.length)];
|
||||
if( (nil == match) ||
|
||||
(NSNotFound == match.range.location) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//extract/convert pid
|
||||
pid = [[logEvent.composedMessage substringWithRange:[match rangeAtIndex:1]] integerValue];
|
||||
if( (0 == pid) ||
|
||||
(-1 == pid) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//save
|
||||
self.lastCameraClient = pid;
|
||||
|
||||
//(re)enumerate active devices
|
||||
// delayed need as device deactiavation
|
||||
// then update status menu (on main thread)
|
||||
|
@ -218,6 +255,7 @@ extern os_log_t logHandle;
|
|||
});
|
||||
|
||||
}); //dispatch for delay
|
||||
|
||||
}
|
||||
|
||||
}];
|
||||
|
@ -232,7 +270,7 @@ extern os_log_t logHandle;
|
|||
NSRegularExpression* regex = nil;
|
||||
|
||||
//dbg msg
|
||||
os_log_debug(logHandle, ">= macOS 13.3+: uUsing 'CMIOExtensionPropertyDeviceControlPID'");
|
||||
os_log_debug(logHandle, ">= macOS 13.3+: Using 'CMIOExtensionPropertyDeviceControlPID'");
|
||||
|
||||
//init regex
|
||||
regex = [NSRegularExpression regularExpressionWithPattern:@"=\\s*(\\d+)\\s*;" options:0 error:nil];
|
||||
|
@ -255,6 +293,9 @@ extern os_log_t logHandle;
|
|||
return;
|
||||
}
|
||||
|
||||
//reset
|
||||
self.lastCameraClient = 0;
|
||||
|
||||
//match on pid
|
||||
match = [regex firstMatchInString:logEvent.composedMessage options:0 range:NSMakeRange(0, logEvent.composedMessage.length)];
|
||||
if( (nil == match) ||
|
||||
|
@ -294,7 +335,7 @@ extern os_log_t logHandle;
|
|||
|
||||
}
|
||||
|
||||
//previous versions of macoS
|
||||
//previous versions of macOS
|
||||
// use predicate: "subsystem=='com.apple.SystemStatus'"
|
||||
else
|
||||
{
|
||||
|
@ -518,96 +559,77 @@ extern os_log_t logHandle;
|
|||
//dbg msg
|
||||
os_log_debug(logHandle, "new audio event");
|
||||
|
||||
//cancel prev timer
|
||||
if(nil != self.audioEventTimer)
|
||||
//active mic
|
||||
AVCaptureDevice* activeMic = nil;
|
||||
|
||||
//audio off?
|
||||
// sent event
|
||||
if(0 == audioDifferences.insertions.count)
|
||||
{
|
||||
//cancel
|
||||
dispatch_source_cancel(self.audioEventTimer);
|
||||
self.audioEventTimer = nil;
|
||||
//dbg msg
|
||||
os_log_debug(logHandle, "audio event: off");
|
||||
|
||||
//init event
|
||||
// process (client) and device are nil
|
||||
event = [[Event alloc] init:nil device:nil deviceType:Device_Microphone state:NSControlStateValueOff];
|
||||
|
||||
//handle event
|
||||
[self handleEvent:event];
|
||||
}
|
||||
|
||||
//re-init timer
|
||||
self.audioEventTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.audioEventQueue);
|
||||
dispatch_source_set_timer(self.audioEventTimer, dispatch_walltime(NULL, 1.0 * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 0.1 * NSEC_PER_SEC);
|
||||
//audio on?
|
||||
// send event
|
||||
else
|
||||
{
|
||||
//dbg msg
|
||||
os_log_debug(logHandle, "audio event: on");
|
||||
|
||||
//set handler
|
||||
dispatch_source_set_event_handler(self.audioEventTimer, ^{
|
||||
|
||||
//active mic
|
||||
AVCaptureDevice* activeMic = nil;
|
||||
|
||||
//audio off?
|
||||
// sent event
|
||||
if(0 == audioDifferences.insertions.count)
|
||||
//send event for each process (attribution)
|
||||
for(NSOrderedCollectionChange* audioAttribution in audioDifferences.insertions)
|
||||
{
|
||||
//dbg msg
|
||||
os_log_debug(logHandle, "audio event: off");
|
||||
//init client from attribution
|
||||
client = [[Client alloc] init];
|
||||
client.pid = audioAttribution.object;
|
||||
client.path = valueForStringItem(getProcessPath(client.pid.intValue));
|
||||
client.name = valueForStringItem(getProcessName(client.path));
|
||||
|
||||
//init event
|
||||
// process (client) and device are nil
|
||||
event = [[Event alloc] init:nil device:nil deviceType:Device_Microphone state:NSControlStateValueOff];
|
||||
|
||||
//handle event
|
||||
[self handleEvent:event];
|
||||
}
|
||||
|
||||
//audio on?
|
||||
// send event
|
||||
else
|
||||
{
|
||||
//dbg msg
|
||||
os_log_debug(logHandle, "audio event: on");
|
||||
|
||||
//send event for each process (attribution)
|
||||
for(NSOrderedCollectionChange* audioAttribution in audioDifferences.insertions)
|
||||
//look for active mic
|
||||
for(AVCaptureDevice* microphone in [AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio])
|
||||
{
|
||||
//init client from attribution
|
||||
client = [[Client alloc] init];
|
||||
client.pid = audioAttribution.object;
|
||||
client.path = valueForStringItem(getProcessPath(client.pid.intValue));
|
||||
client.name = valueForStringItem(getProcessName(client.path));
|
||||
|
||||
//look for active mic
|
||||
for(AVCaptureDevice* microphone in [AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio])
|
||||
//off? skip
|
||||
if(NSControlStateValueOn != [self getMicState:microphone])
|
||||
{
|
||||
//off? skip
|
||||
if(NSControlStateValueOn != [self getMicState:microphone])
|
||||
{
|
||||
//skip
|
||||
continue;
|
||||
}
|
||||
|
||||
//dbg msg
|
||||
os_log_debug(logHandle, "audio device: %{public}@/%{public}@ is on", microphone.manufacturer, microphone.localizedName);
|
||||
|
||||
//save
|
||||
activeMic = microphone;
|
||||
|
||||
//init event
|
||||
// with client and (active) mic
|
||||
event = [[Event alloc] init:client device:activeMic deviceType:Device_Microphone state:NSControlStateValueOn];
|
||||
|
||||
//handle event
|
||||
[self handleEvent:event];
|
||||
//skip
|
||||
continue;
|
||||
}
|
||||
|
||||
//no mic found? (e.g. headphones as input)
|
||||
// show (limited) alert
|
||||
if(nil == activeMic)
|
||||
{
|
||||
//init event
|
||||
// devivce is nil
|
||||
event = [[Event alloc] init:client device:nil deviceType:Device_Microphone state:NSControlStateValueOn];
|
||||
//dbg msg
|
||||
os_log_debug(logHandle, "audio device: %{public}@/%{public}@ is on", microphone.manufacturer, microphone.localizedName);
|
||||
|
||||
//handle event
|
||||
[self handleEvent:event];
|
||||
}
|
||||
//save
|
||||
activeMic = microphone;
|
||||
|
||||
//init event
|
||||
// with client and (active) mic
|
||||
event = [[Event alloc] init:client device:activeMic deviceType:Device_Microphone state:NSControlStateValueOn];
|
||||
|
||||
//handle event
|
||||
[self handleEvent:event];
|
||||
}
|
||||
|
||||
//no mic found? (e.g. headphones as input)
|
||||
// show (limited) alert
|
||||
if(nil == activeMic)
|
||||
{
|
||||
//init event
|
||||
// devivce is nil
|
||||
event = [[Event alloc] init:client device:nil deviceType:Device_Microphone state:NSControlStateValueOn];
|
||||
|
||||
//handle event
|
||||
[self handleEvent:event];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//start audio event timer
|
||||
dispatch_resume(self.audioEventTimer);
|
||||
}
|
||||
|
||||
} //audio event
|
||||
|
||||
|
@ -620,104 +642,85 @@ extern os_log_t logHandle;
|
|||
//dbg msg
|
||||
os_log_debug(logHandle, "new camera event");
|
||||
|
||||
//cancel prev timer
|
||||
if(nil != self.cameraEventTimer)
|
||||
//active camera
|
||||
AVCaptureDevice* activeCamera = nil;
|
||||
|
||||
//camera off?
|
||||
// send event
|
||||
if(0 == cameraDifferences.insertions.count)
|
||||
{
|
||||
//cancel
|
||||
dispatch_source_cancel(self.cameraEventTimer);
|
||||
self.cameraEventTimer = nil;
|
||||
//dbg msg
|
||||
os_log_debug(logHandle, "camera event: off");
|
||||
|
||||
//init event
|
||||
// process (client) and device are nil
|
||||
event = [[Event alloc] init:nil device:nil deviceType:Device_Camera state:NSControlStateValueOff];
|
||||
|
||||
//handle event
|
||||
[self handleEvent:event];
|
||||
}
|
||||
|
||||
//re-init timer
|
||||
self.cameraEventTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.cameraEventQueue);
|
||||
dispatch_source_set_timer(self.cameraEventTimer, dispatch_walltime(NULL, 1.0 * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 0.1 * NSEC_PER_SEC);
|
||||
//camera on?
|
||||
// send event
|
||||
else
|
||||
{
|
||||
//dbg msg
|
||||
os_log_debug(logHandle, "camera event: on");
|
||||
|
||||
//set handler
|
||||
dispatch_source_set_event_handler(self.cameraEventTimer, ^{
|
||||
|
||||
//active camera
|
||||
AVCaptureDevice* activeCamera = nil;
|
||||
|
||||
//camera off?
|
||||
// send event
|
||||
if(0 == cameraDifferences.insertions.count)
|
||||
//send event for each process (attribution)
|
||||
for(NSOrderedCollectionChange* cameraAttribution in cameraDifferences.insertions)
|
||||
{
|
||||
//dbg msg
|
||||
os_log_debug(logHandle, "camera event: off");
|
||||
//init client from attribution
|
||||
client = [[Client alloc] init];
|
||||
client.pid = cameraAttribution.object;
|
||||
client.path = valueForStringItem(getProcessPath(client.pid.intValue));
|
||||
client.name = valueForStringItem(getProcessName(client.path));
|
||||
|
||||
//init event
|
||||
// process (client) and device are nil
|
||||
event = [[Event alloc] init:nil device:nil deviceType:Device_Camera state:NSControlStateValueOff];
|
||||
|
||||
//handle event
|
||||
[self handleEvent:event];
|
||||
}
|
||||
|
||||
//camera on?
|
||||
// send event
|
||||
else
|
||||
{
|
||||
//dbg msg
|
||||
os_log_debug(logHandle, "camera event: on");
|
||||
|
||||
//send event for each process (attribution)
|
||||
for(NSOrderedCollectionChange* cameraAttribution in cameraDifferences.insertions)
|
||||
//look for active camera
|
||||
for(AVCaptureDevice* camera in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo])
|
||||
{
|
||||
//init client from attribution
|
||||
client = [[Client alloc] init];
|
||||
client.pid = cameraAttribution.object;
|
||||
client.path = valueForStringItem(getProcessPath(client.pid.intValue));
|
||||
client.name = valueForStringItem(getProcessName(client.path));
|
||||
|
||||
//look for active camera
|
||||
for(AVCaptureDevice* camera in [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo])
|
||||
//off? skip
|
||||
if(NSControlStateValueOn != [self getCameraState:camera])
|
||||
{
|
||||
//off? skip
|
||||
if(NSControlStateValueOn != [self getCameraState:camera])
|
||||
{
|
||||
//skip
|
||||
continue;
|
||||
}
|
||||
|
||||
//virtual
|
||||
// TODO: is there a better way to determine this?
|
||||
if(YES == [camera.localizedName containsString:@"Virtual"])
|
||||
{
|
||||
//skip
|
||||
continue;
|
||||
}
|
||||
|
||||
//dbg msg
|
||||
os_log_debug(logHandle, "camera device: %{public}@/%{public}@ is on", camera.manufacturer, camera.localizedName);
|
||||
|
||||
//save
|
||||
activeCamera = camera;
|
||||
|
||||
//init event
|
||||
// with client and (active) camera
|
||||
event = [[Event alloc] init:client device:activeCamera deviceType:Device_Camera state:NSControlStateValueOn];
|
||||
|
||||
//handle event
|
||||
[self handleEvent:event];
|
||||
//skip
|
||||
continue;
|
||||
}
|
||||
|
||||
//no camera found?
|
||||
// show (limited) alert
|
||||
if(nil == activeCamera)
|
||||
//virtual
|
||||
// TODO: is there a better way to determine this?
|
||||
if(YES == [camera.localizedName containsString:@"Virtual"])
|
||||
{
|
||||
//init event
|
||||
// devivce is nil
|
||||
event = [[Event alloc] init:client device:nil deviceType:Device_Camera state:NSControlStateValueOn];
|
||||
|
||||
//handle event
|
||||
[self handleEvent:event];
|
||||
//skip
|
||||
continue;
|
||||
}
|
||||
|
||||
//dbg msg
|
||||
os_log_debug(logHandle, "camera device: %{public}@/%{public}@ is on", camera.manufacturer, camera.localizedName);
|
||||
|
||||
//save
|
||||
activeCamera = camera;
|
||||
|
||||
//init event
|
||||
// with client and (active) camera
|
||||
event = [[Event alloc] init:client device:activeCamera deviceType:Device_Camera state:NSControlStateValueOn];
|
||||
|
||||
//handle event
|
||||
[self handleEvent:event];
|
||||
}
|
||||
|
||||
//no camera found?
|
||||
// show (limited) alert
|
||||
if(nil == activeCamera)
|
||||
{
|
||||
//init event
|
||||
// devivce is nil
|
||||
event = [[Event alloc] init:client device:nil deviceType:Device_Camera state:NSControlStateValueOn];
|
||||
|
||||
//handle event
|
||||
[self handleEvent:event];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//start camera timer
|
||||
dispatch_resume(self.cameraEventTimer);
|
||||
}
|
||||
|
||||
} //camera event
|
||||
|
||||
|
@ -780,64 +783,62 @@ extern os_log_t logHandle;
|
|||
self.lastMicOff = device;
|
||||
}
|
||||
|
||||
//macOS 13.3
|
||||
//macOS 13.3+
|
||||
// use this as trigger
|
||||
// older version send event via log monitor
|
||||
if (@available(macOS 13.3, *)) {
|
||||
|
||||
//dbg msg
|
||||
os_log_debug(logHandle, "new audio event");
|
||||
|
||||
//cancel prev timer
|
||||
if(nil != self.audioEventTimer)
|
||||
//audio off?
|
||||
if(NSControlStateValueOff == state)
|
||||
{
|
||||
//cancel
|
||||
dispatch_source_cancel(self.audioEventTimer);
|
||||
self.audioEventTimer = nil;
|
||||
//dbg msg
|
||||
os_log_debug(logHandle, "audio event: off");
|
||||
|
||||
//init event
|
||||
// process (client) and device are nil
|
||||
event = [[Event alloc] init:nil device:device deviceType:Device_Microphone state:NSControlStateValueOff];
|
||||
|
||||
//handle event
|
||||
[self handleEvent:event];
|
||||
}
|
||||
|
||||
//re-init timer
|
||||
self.audioEventTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.audioEventQueue);
|
||||
dispatch_source_set_timer(self.audioEventTimer, dispatch_walltime(NULL, 0.5 * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 0.1 * NSEC_PER_SEC);
|
||||
//audio on?
|
||||
else if(NSControlStateValueOn == state)
|
||||
{
|
||||
//dbg msg
|
||||
os_log_debug(logHandle, "audio event: on");
|
||||
|
||||
//set handler
|
||||
dispatch_source_set_event_handler(self.audioEventTimer, ^{
|
||||
//delay
|
||||
// need time for logging to grab responsible process
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
|
||||
//audio off?
|
||||
// send event
|
||||
if(NSControlStateValueOff == state)
|
||||
{
|
||||
//dbg msg
|
||||
os_log_debug(logHandle, "audio event: off");
|
||||
//client
|
||||
Client* client = nil;
|
||||
|
||||
//init event
|
||||
// process (client) and device are nil
|
||||
event = [[Event alloc] init:nil device:nil deviceType:Device_Microphone state:NSControlStateValueOff];
|
||||
|
||||
//handle event
|
||||
[self handleEvent:event];
|
||||
}
|
||||
|
||||
//audio on?
|
||||
// send event
|
||||
else if(NSControlStateValueOn == state)
|
||||
{
|
||||
//dbg msg
|
||||
os_log_debug(logHandle, "audio event: on");
|
||||
//have client?
|
||||
if(0 != self.lastMicClient)
|
||||
{
|
||||
//init client from attribution
|
||||
client = [[Client alloc] init];
|
||||
client.pid = [NSNumber numberWithInteger:self.lastMicClient];
|
||||
client.path = valueForStringItem(getProcessPath(client.pid.intValue));
|
||||
client.name = valueForStringItem(getProcessName(client.path));
|
||||
}
|
||||
|
||||
//init event
|
||||
// devivce is nil
|
||||
event = [[Event alloc] init:nil device:nil deviceType:Device_Microphone state:NSControlStateValueOn];
|
||||
event = [[Event alloc] init:client device:device deviceType:Device_Microphone state:NSControlStateValueOn];
|
||||
|
||||
//handle event
|
||||
[self handleEvent:event];
|
||||
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//start audio event timer
|
||||
dispatch_resume(self.audioEventTimer);
|
||||
|
||||
} //macOS 13.3
|
||||
} //macOS 13.3+
|
||||
};
|
||||
|
||||
//add property listener for audio changes
|
||||
|
@ -914,6 +915,7 @@ bail:
|
|||
|
||||
//camera on?
|
||||
// macOS 13.3, use this as trigger
|
||||
// older version send event via log monitor
|
||||
if (@available(macOS 13.3, *)) {
|
||||
|
||||
//event
|
||||
|
@ -928,29 +930,22 @@ bail:
|
|||
//dbg msg
|
||||
os_log_debug(logHandle, "camera event: on");
|
||||
|
||||
//cancel prev timer
|
||||
if(nil != self.cameraEventTimer)
|
||||
{
|
||||
//cancel
|
||||
dispatch_cancel(self.cameraEventTimer);
|
||||
self.cameraEventTimer = nil;
|
||||
}
|
||||
|
||||
//re-init timer
|
||||
self.cameraEventTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.cameraEventQueue);
|
||||
dispatch_source_set_timer(self.cameraEventTimer, dispatch_walltime(NULL, 1.0 * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 0.1 * NSEC_PER_SEC);
|
||||
|
||||
//set handler
|
||||
dispatch_source_set_event_handler(self.cameraEventTimer, ^{
|
||||
//delay
|
||||
// need time for logging to grab responsible process
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
|
||||
//client
|
||||
Client* client = nil;
|
||||
|
||||
//init client from attribution
|
||||
client = [[Client alloc] init];
|
||||
client.pid = [NSNumber numberWithInteger:self.lastCameraClient];
|
||||
client.path = valueForStringItem(getProcessPath(client.pid.intValue));
|
||||
client.name = valueForStringItem(getProcessName(client.path));
|
||||
//have a client?
|
||||
if(0 != self.lastCameraClient)
|
||||
{
|
||||
//init client from attribution
|
||||
client = [[Client alloc] init];
|
||||
client.pid = [NSNumber numberWithInteger:self.lastCameraClient];
|
||||
client.path = valueForStringItem(getProcessPath(client.pid.intValue));
|
||||
client.name = valueForStringItem(getProcessName(client.path));
|
||||
}
|
||||
|
||||
//init event
|
||||
// with client and (active) camera
|
||||
|
@ -960,9 +955,6 @@ bail:
|
|||
[self handleEvent:event];
|
||||
|
||||
});
|
||||
|
||||
//start camera timer
|
||||
dispatch_resume(self.cameraEventTimer);
|
||||
}
|
||||
|
||||
//camera: off
|
||||
|
@ -1356,14 +1348,14 @@ bail:
|
|||
//macOS sometimes toggles / delivers 2x events for same device
|
||||
if(deviceLastEvent.deviceType == event.deviceType)
|
||||
{
|
||||
//ignore if last event was < 0.5 ago
|
||||
if([event.timestamp timeIntervalSinceDate:deviceLastEvent.timestamp] < 0.5f)
|
||||
//ignore if last event was < 1.0s ago
|
||||
if([event.timestamp timeIntervalSinceDate:deviceLastEvent.timestamp] < 1.0f)
|
||||
{
|
||||
//set result
|
||||
result = NOTIFICATION_SPURIOUS;
|
||||
|
||||
//dbg msg
|
||||
os_log_debug(logHandle, "ignoring event, as it happened <0.5s ago");
|
||||
os_log_debug(logHandle, "ignoring event, as it happened <1.0s ago");
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
|
@ -1373,7 +1365,6 @@ bail:
|
|||
if( (deviceLastEvent.state == event.state) &&
|
||||
([event.timestamp timeIntervalSinceDate:deviceLastEvent.timestamp] < 2.0f) )
|
||||
{
|
||||
|
||||
//set result
|
||||
result = NOTIFICATION_SPURIOUS;
|
||||
|
||||
|
@ -1386,7 +1377,6 @@ bail:
|
|||
|
||||
} //same device
|
||||
|
||||
|
||||
//client provided?
|
||||
// check if its allowed
|
||||
if(nil != event.client)
|
||||
|
@ -1498,7 +1488,7 @@ bail:
|
|||
if(nil != event.client)
|
||||
{
|
||||
//set body
|
||||
content.body = [NSString stringWithFormat:@"\r\nProcess: %@ (%@)", event.client.name, (0 != event.client.pid.intValue) ? event.client.pid : @"pid: unknown"];
|
||||
content.body = [NSString stringWithFormat:@"Process: %@ (%@)", event.client.name, (0 != event.client.pid.intValue) ? event.client.pid : @"pid: unknown"];
|
||||
|
||||
//set category
|
||||
content.categoryIdentifier = CATEGORY_ACTION;
|
||||
|
|
|
@ -329,7 +329,7 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "#Note: To build uninstaller for app (menu)\n# 0. Delete this script and Installer.app from App's on-disk folder \n# 1. Build Installer\n# 2. Copy into App \"Uninstaller\" folder (& proj if needed)\n# 3. Re-add script, and then build (archive) for deployment\n\n#archive\n# copy in main application \nif [[ $BUILT_PRODUCTS_DIR = *\"ArchiveIntermediates\"* ]]; then\n cp -R -f \"$PROJECT_TEMP_ROOT/UninstalledProducts/macosx/OverSight.app\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Resources\"\n \n#normal build\n# delete and copy in main application \nelse\n\nrm -rf \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Resources/OverSight.app\"\ncp -R -f \"$BUILT_PRODUCTS_DIR/OverSight.app\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Resources\"\n\nfi\n\n";
|
||||
shellScript = "#Note: To build uninstaller for app (menu)\n# 0. Delete this script and Installer.app from App's on-disk folder \n# 1. Build Installer\n# 2. Copy into App \"Uninstaller\" folder (& proj if needed)\n# 3. Re-add script, and then build (archive) for deployment\n\n#archive\n# copy in main application \nif [[ $BUILT_PRODUCTS_DIR = *\"ArchiveIntermediates\"* ]]; then\n cp -R -f \"$PROJECT_TEMP_ROOT/UninstalledProducts/macosx/OverSight.app\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Resources\"\n \n#normal build\n# delete and copy in main application \nelse\n\nrm -rf \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Resources/OverSight.app\"\ncp -R -f \"$BUILT_PRODUCTS_DIR/OverSight.app\" \"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/Resources\"\n\nfi\n\n\n";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
|
@ -380,14 +380,14 @@
|
|||
CODE_SIGN_ENTITLEMENTS = "";
|
||||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 2.1.8;
|
||||
CURRENT_PROJECT_VERSION = 2.2.0;
|
||||
DEVELOPMENT_TEAM = VBG97UB4TA;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../Carthage/Build/Mac";
|
||||
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = NO;
|
||||
INFOPLIST_FILE = Helper/Info.plist;
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.0;
|
||||
MARKETING_VERSION = 2.1.8;
|
||||
MARKETING_VERSION = 2.2.0;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
OTHER_CODE_SIGN_FLAGS = "";
|
||||
OTHER_LDFLAGS = (
|
||||
|
@ -415,14 +415,14 @@
|
|||
CODE_SIGN_ENTITLEMENTS = "";
|
||||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
CURRENT_PROJECT_VERSION = 2.1.8;
|
||||
CURRENT_PROJECT_VERSION = 2.2.0;
|
||||
DEVELOPMENT_TEAM = VBG97UB4TA;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../Carthage/Build/Mac";
|
||||
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = NO;
|
||||
INFOPLIST_FILE = Helper/Info.plist;
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.0;
|
||||
MARKETING_VERSION = 2.1.8;
|
||||
MARKETING_VERSION = 2.2.0;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
OTHER_CODE_SIGN_FLAGS = "";
|
||||
OTHER_LDFLAGS = (
|
||||
|
@ -451,7 +451,7 @@
|
|||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 2.1.8;
|
||||
CURRENT_PROJECT_VERSION = 2.2.0;
|
||||
DEVELOPMENT_TEAM = VBG97UB4TA;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../Carthage/Build/Mac";
|
||||
|
@ -463,7 +463,7 @@
|
|||
);
|
||||
LIBRARY_SEARCH_PATHS = "";
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.0;
|
||||
MARKETING_VERSION = 2.1.8;
|
||||
MARKETING_VERSION = 2.2.0;
|
||||
OTHER_CODE_SIGN_FLAGS = "";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.objective-see.oversight.installer";
|
||||
PRODUCT_NAME = "OverSight Installer";
|
||||
|
@ -480,7 +480,7 @@
|
|||
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 2.1.8;
|
||||
CURRENT_PROJECT_VERSION = 2.2.0;
|
||||
DEVELOPMENT_TEAM = VBG97UB4TA;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../Carthage/Build/Mac";
|
||||
|
@ -492,7 +492,7 @@
|
|||
);
|
||||
LIBRARY_SEARCH_PATHS = "";
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.0;
|
||||
MARKETING_VERSION = 2.1.8;
|
||||
MARKETING_VERSION = 2.2.0;
|
||||
OTHER_CODE_SIGN_FLAGS = "";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.objective-see.oversight.installer";
|
||||
PRODUCT_NAME = "OverSight Installer";
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"filename" : "darkMode.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"filename" : "lightMode.png",
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "light"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"filename" : "darkMode.png",
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
},
|
||||
"properties" : {
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 51 KiB |
Binary file not shown.
After Width: | Height: | Size: 51 KiB |
|
@ -41,7 +41,13 @@
|
|||
@property (strong, nonatomic) IBOutlet NSView *notificationsView;
|
||||
|
||||
//support us
|
||||
@property (weak, nonatomic) IBOutlet NSButton *gotoSupportViewButton;
|
||||
@property (weak, nonatomic) IBOutlet NSButton *gotoDNDView;
|
||||
|
||||
//do not disturb view
|
||||
@property (strong) IBOutlet NSView *doNotDisturbView;
|
||||
|
||||
//support us
|
||||
@property (weak, nonatomic) IBOutlet NSButton *gotoSupportView;
|
||||
|
||||
/* SUPPORT US */
|
||||
|
||||
|
|
|
@ -198,13 +198,13 @@ extern os_log_t logHandle;
|
|||
}
|
||||
|
||||
//show 'support' view
|
||||
case ACTION_SHOW_NOTIFICATIONS:
|
||||
case ACTION_SHOW_NOTIFICATION_VIEW:
|
||||
{
|
||||
//dbg msg
|
||||
os_log_debug(logHandle, "showing 'notifcations' view");
|
||||
|
||||
//show view
|
||||
[self showView:self.notificationsView firstResponder:self.gotoSupportViewButton];
|
||||
[self showView:self.notificationsView firstResponder:self.gotoDNDView];
|
||||
|
||||
//unset window title
|
||||
self.window.title = @"";
|
||||
|
@ -213,7 +213,22 @@ extern os_log_t logHandle;
|
|||
}
|
||||
|
||||
//show 'support' view
|
||||
case ACTION_SHOW_SUPPORT:
|
||||
case ACTION_SHOW_DND_VIEW:
|
||||
{
|
||||
//dbg msg
|
||||
os_log_debug(logHandle, "showing 'do not disturb' view");
|
||||
|
||||
//show view
|
||||
[self showView:self.doNotDisturbView firstResponder:self.gotoSupportView];
|
||||
|
||||
//unset window title
|
||||
self.window.title = @"";
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//show 'support' view
|
||||
case ACTION_SHOW_SUPPORT_VIEW:
|
||||
{
|
||||
//dbg msg
|
||||
os_log_debug(logHandle, "showing 'support' view");
|
||||
|
@ -523,7 +538,7 @@ extern os_log_t logHandle;
|
|||
self.installButton.title = ACTION_NEXT;
|
||||
|
||||
//set tag
|
||||
self.installButton.tag = ACTION_SHOW_NOTIFICATIONS;
|
||||
self.installButton.tag = ACTION_SHOW_NOTIFICATION_VIEW;
|
||||
}
|
||||
//otherwise
|
||||
// set button and tag for close/exit
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="22154" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="22155" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22154"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22155"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="ConfigureWindowController">
|
||||
<connections>
|
||||
<outlet property="activityIndicator" destination="b1w-5W-ayX" id="P5e-PO-Ozi"/>
|
||||
<outlet property="gotoSupportViewButton" destination="8I9-ag-g5s" id="7ej-Ft-edA"/>
|
||||
<outlet property="doNotDisturbView" destination="6y2-pv-Ibd" id="iO4-Zv-dm4"/>
|
||||
<outlet property="gotoDNDView" destination="8I9-ag-g5s" id="zy3-3J-OSA"/>
|
||||
<outlet property="gotoSupportView" destination="2V7-Bx-gVi" id="2hL-gj-ztg"/>
|
||||
<outlet property="installButton" destination="553-2y-kvm" id="nxf-wO-EI3"/>
|
||||
<outlet property="moreInfoButton" destination="QWu-qZ-Za2" id="8kC-Ga-5oJ"/>
|
||||
<outlet property="notificationsView" destination="DoQ-oR-CLI" id="F1Y-Mo-2E6"/>
|
||||
|
@ -25,7 +27,7 @@
|
|||
<window allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="F0z-JX-Cv5">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
||||
<rect key="contentRect" x="196" y="240" width="523" height="237"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1512" height="944"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="3440" height="1415"/>
|
||||
<view key="contentView" id="se5-gp-TjO">
|
||||
<rect key="frame" x="0.0" y="0.0" width="523" height="237"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
|
@ -140,6 +142,49 @@
|
|||
</subviews>
|
||||
<point key="canvasLocation" x="686" y="1393"/>
|
||||
</customView>
|
||||
<customView id="6y2-pv-Ibd" userLabel="Do Not Disturb">
|
||||
<rect key="frame" x="0.0" y="0.0" width="812" height="500"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<box fixedFrame="YES" boxType="custom" borderType="none" titlePosition="noTitle" translatesAutoresizingMaskIntoConstraints="NO" id="nf3-DZ-8wh">
|
||||
<rect key="frame" x="0.0" y="0.0" width="812" height="48"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<view key="contentView" id="30U-SP-9eW">
|
||||
<rect key="frame" x="0.0" y="0.0" width="812" height="48"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<button tag="4" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2V7-Bx-gVi">
|
||||
<rect key="frame" x="718" y="7" width="81" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="push" title="Next »" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="0wd-cy-xEw">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" size="13" name="Menlo-Bold"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="configureButtonHandler:" target="-2" id="qnn-kG-aKi"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
</view>
|
||||
<color key="fillColor" red="0.52700018810000004" green="0.69087679930000001" blue="0.21211786799999999" alpha="0.99595620600000001" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</box>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fbL-Nf-kJy">
|
||||
<rect key="frame" x="41" y="39" width="731" height="359"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="DoNotDisturb" id="E1E-Ha-z8D"/>
|
||||
</imageView>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" preferredMaxLayoutWidth="660" translatesAutoresizingMaskIntoConstraints="NO" id="FRR-wv-bYz">
|
||||
<rect key="frame" x="177" y="370" width="459" height="110"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="center" title="If "Do Not Disturb" mode is activated, OverSight's alerts won't be shown" id="zdX-7L-hfX">
|
||||
<font key="font" size="32" name="AvenirNextCondensed-Regular"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<point key="canvasLocation" x="686" y="1393"/>
|
||||
</customView>
|
||||
<customView id="bkk-rY-ALC" userLabel="Support">
|
||||
<rect key="frame" x="0.0" y="0.0" width="812" height="500"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
|
@ -162,7 +207,7 @@
|
|||
<action selector="configureButtonHandler:" target="-2" id="Ipm-LG-LXx"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button tag="4" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="pM7-Wp-KdU">
|
||||
<button tag="5" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="pM7-Wp-KdU">
|
||||
<rect key="frame" x="718" y="7" width="81" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<buttonCell key="cell" type="push" title="Yes!" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="E5X-AO-6V1">
|
||||
|
@ -253,6 +298,7 @@
|
|||
</customView>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="DoNotDisturb" width="1430" height="510"/>
|
||||
<image name="FriendsJamf" width="328" height="114"/>
|
||||
<image name="FriendsKandji" width="1944" height="494"/>
|
||||
<image name="FriendsKolide" width="636" height="216"/>
|
||||
|
|
|
@ -196,13 +196,16 @@
|
|||
#define ACTION_NEXT @"Next »"
|
||||
|
||||
//show info about notifications
|
||||
#define ACTION_SHOW_NOTIFICATIONS 2
|
||||
#define ACTION_SHOW_NOTIFICATION_VIEW 2
|
||||
|
||||
//show info about do not disturb
|
||||
#define ACTION_SHOW_DND_VIEW 3
|
||||
|
||||
//show friends
|
||||
#define ACTION_SHOW_SUPPORT 3
|
||||
#define ACTION_SHOW_SUPPORT_VIEW 4
|
||||
|
||||
//support us
|
||||
#define ACTION_SUPPORT 4
|
||||
#define ACTION_SUPPORT 5
|
||||
|
||||
//path to chmod
|
||||
#define CHMOD @"/bin/chmod"
|
||||
|
|
|
@ -130,4 +130,7 @@ NSModalResponse showAlert(NSString* messageText, NSString* informativeText, NSSt
|
|||
//does console user have admin privs?
|
||||
BOOL hasAdminPrivileges(void);
|
||||
|
||||
//get Do Not Distrub state
|
||||
BOOL DNDState(void);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1395,6 +1395,9 @@ NSMutableDictionary* execTask(NSString* binaryPath, NSArray* arguments, BOOL sho
|
|||
// NSTask throws if path isn't found...
|
||||
if(YES != [NSFileManager.defaultManager fileExistsAtPath:binaryPath])
|
||||
{
|
||||
//err msg
|
||||
os_log_error(logHandle, "ERROR: %{public}@ not found", binaryPath);
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
}
|
||||
|
@ -1449,7 +1452,7 @@ NSMutableDictionary* execTask(NSString* binaryPath, NSArray* arguments, BOOL sho
|
|||
@catch(NSException *exception)
|
||||
{
|
||||
//err msg
|
||||
os_log_debug(logHandle, "failed to launch task (%{public}@)", exception);
|
||||
os_log_error(logHandle, "failed to launch task (%{public}@)", exception);
|
||||
|
||||
//bail
|
||||
goto bail;
|
||||
|
@ -1585,7 +1588,7 @@ BOOL isFileRestricted(NSString* file)
|
|||
}
|
||||
|
||||
//in dark mode?
|
||||
BOOL isDarkMode()
|
||||
BOOL isDarkMode(void)
|
||||
{
|
||||
return [[[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"] isEqualToString:@"Dark"];
|
||||
}
|
||||
|
@ -1630,7 +1633,7 @@ NSModalResponse showAlert(NSString* messageText, NSString* informativeText, NSSt
|
|||
|
||||
//checks if user has admin privs
|
||||
// based off http://stackoverflow.com/questions/30000443/asking-for-admin-privileges-for-only-standard-accounts
|
||||
BOOL hasAdminPrivileges()
|
||||
BOOL hasAdminPrivileges(void)
|
||||
{
|
||||
//flag
|
||||
BOOL isAdmin = NO;
|
||||
|
@ -1667,3 +1670,17 @@ BOOL hasAdminPrivileges()
|
|||
|
||||
return isAdmin;
|
||||
}
|
||||
|
||||
//get Do Not Distrub state
|
||||
BOOL DNDState(void)
|
||||
{
|
||||
//default
|
||||
NSUserDefaults* defaults = nil;
|
||||
|
||||
//load defaults
|
||||
defaults = [[NSUserDefaults alloc] initWithSuiteName:@"com.apple.notificationcenterui"];
|
||||
|
||||
//return status of DND
|
||||
return [defaults boolForKey:@"doNotDisturb"];
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue