obs-studio/plugins/mac-capture/window-utils.m

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

281 lines
9.2 KiB
Mathematica
Raw Normal View History

2014-08-19 22:36:24 +00:00
#include "window-utils.h"
#include <util/platform.h>
2019-07-09 18:29:39 +00:00
#define WINDOW_NAME ((NSString *) kCGWindowName)
#define WINDOW_NUMBER ((NSString *) kCGWindowNumber)
#define OWNER_NAME ((NSString *) kCGWindowOwnerName)
#define OWNER_PID ((NSString *) kCGWindowOwnerPID)
2014-08-19 22:36:24 +00:00
2019-07-09 18:29:39 +00:00
static NSComparator win_info_cmp = ^(NSDictionary *o1, NSDictionary *o2) {
2014-08-19 22:36:24 +00:00
NSComparisonResult res = [o1[OWNER_NAME] compare:o2[OWNER_NAME]];
if (res != NSOrderedSame)
return res;
res = [o1[OWNER_PID] compare:o2[OWNER_PID]];
if (res != NSOrderedSame)
return res;
res = [o1[WINDOW_NAME] compare:o2[WINDOW_NAME]];
if (res != NSOrderedSame)
return res;
return [o1[WINDOW_NUMBER] compare:o2[WINDOW_NUMBER]];
};
NSArray *enumerate_windows(void)
{
2019-07-09 18:29:39 +00:00
NSArray *arr = (NSArray *) CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
2014-08-19 22:36:24 +00:00
[arr autorelease];
return [arr sortedArrayUsingComparator:win_info_cmp];
2014-08-19 22:36:24 +00:00
}
#define WAIT_TIME_MS 500
#define WAIT_TIME_US WAIT_TIME_MS * 1000
#define WAIT_TIME_NS WAIT_TIME_US * 1000
bool find_window(cocoa_window_t cw, obs_data_t *settings, bool force)
2014-08-19 22:36:24 +00:00
{
if (!force && cw->next_search_time > os_gettime_ns())
2014-08-19 22:36:24 +00:00
return false;
cw->next_search_time = os_gettime_ns() + WAIT_TIME_NS;
2014-08-19 22:36:24 +00:00
pthread_mutex_lock(&cw->name_lock);
2014-08-19 22:36:24 +00:00
if (!cw->window_name.length && !cw->owner_name.length)
goto invalid_name;
2014-08-19 22:36:24 +00:00
for (NSDictionary *dict in enumerate_windows()) {
if (![cw->owner_name isEqualToString:dict[OWNER_NAME]])
continue;
2014-08-19 22:36:24 +00:00
if (![cw->window_name isEqualToString:dict[WINDOW_NAME]])
2014-08-19 22:36:24 +00:00
continue;
pthread_mutex_unlock(&cw->name_lock);
NSNumber *window_id = (NSNumber *) dict[WINDOW_NUMBER];
cw->window_id = window_id.intValue;
NSNumber *owner_pid = (NSNumber *) dict[OWNER_PID];
cw->owner_pid = owner_pid.intValue;
2014-08-19 22:36:24 +00:00
obs_data_set_int(settings, "window", cw->window_id);
obs_data_set_int(settings, "owner_pid", cw->owner_pid);
2014-08-19 22:36:24 +00:00
return true;
}
invalid_name:
pthread_mutex_unlock(&cw->name_lock);
2014-08-19 22:36:24 +00:00
return false;
}
void init_window(cocoa_window_t cw, obs_data_t *settings)
2014-08-19 22:36:24 +00:00
{
pthread_mutex_init(&cw->name_lock, NULL);
2019-07-09 18:29:39 +00:00
cw->owner_name = @(obs_data_get_string(settings, "owner_name"));
2014-08-19 22:36:24 +00:00
cw->window_name = @(obs_data_get_string(settings, "window_name"));
2019-07-09 18:29:39 +00:00
[cw->owner_name retain];
2014-08-19 22:36:24 +00:00
[cw->window_name retain];
// Find initial window.
pthread_mutex_lock(&cw->name_lock);
if (!cw->window_name.length && !cw->owner_name.length)
goto invalid_name;
NSNumber *owner_pid = @(obs_data_get_int(settings, "owner_pid"));
NSNumber *window_id = @(obs_data_get_int(settings, "window"));
for (NSDictionary *dict in enumerate_windows()) {
bool owner_names_match = [cw->owner_name isEqualToString:dict[OWNER_NAME]];
bool ids_match = [owner_pid isEqualToNumber:dict[OWNER_PID]] && [window_id isEqualToNumber:dict[WINDOW_NUMBER]];
bool window_names_match = [cw->window_name isEqualToString:dict[WINDOW_NAME]];
if (owner_names_match && (ids_match || window_names_match)) {
pthread_mutex_unlock(&cw->name_lock);
cw->window_id = [dict[WINDOW_NUMBER] intValue];
cw->owner_pid = [dict[OWNER_PID] intValue];
obs_data_set_int(settings, "window", cw->window_id);
obs_data_set_int(settings, "owner_pid", cw->owner_pid);
return;
}
}
invalid_name:
pthread_mutex_unlock(&cw->name_lock);
return;
2014-08-19 22:36:24 +00:00
}
void destroy_window(cocoa_window_t cw)
{
pthread_mutex_destroy(&cw->name_lock);
2019-07-09 18:29:39 +00:00
[cw->owner_name release];
2014-08-19 22:36:24 +00:00
[cw->window_name release];
}
void update_window(cocoa_window_t cw, obs_data_t *settings)
2014-08-19 22:36:24 +00:00
{
pthread_mutex_lock(&cw->name_lock);
2019-07-09 18:29:39 +00:00
[cw->owner_name release];
2014-08-19 22:36:24 +00:00
[cw->window_name release];
2019-07-09 18:29:39 +00:00
cw->owner_name = @(obs_data_get_string(settings, "owner_name"));
cw->window_name = @(obs_data_get_string(settings, "window_name"));
[cw->owner_name retain];
2014-08-19 22:36:24 +00:00
[cw->window_name retain];
pthread_mutex_unlock(&cw->name_lock);
cw->owner_pid = (int) obs_data_get_int(settings, "owner_pid");
cw->window_id = (unsigned int) obs_data_get_int(settings, "window");
2014-08-19 22:36:24 +00:00
}
static inline const char *make_name(NSString *owner, NSString *name)
2014-08-19 22:36:24 +00:00
{
if (!owner.length)
return "";
2014-08-19 22:36:24 +00:00
NSString *str = [NSString stringWithFormat:@"[%@] %@", owner, name];
return str.UTF8String;
}
static inline NSDictionary *find_window_dict(NSArray *arr, int window_id)
{
for (NSDictionary *dict in arr) {
NSNumber *wid = (NSNumber *) dict[WINDOW_NUMBER];
if (wid.intValue == window_id)
return dict;
}
return nil;
2014-08-19 22:36:24 +00:00
}
static inline bool window_changed_internal(obs_property_t *p, obs_data_t *settings)
2014-08-19 22:36:24 +00:00
{
int window_id = (int) obs_data_get_int(settings, "window");
NSString *window_owner = @(obs_data_get_string(settings, "owner_name"));
2019-07-09 18:29:39 +00:00
NSString *window_name = @(obs_data_get_string(settings, "window_name"));
2014-08-19 22:36:24 +00:00
NSDictionary *win_info = @ {
OWNER_NAME: window_owner,
2014-08-19 22:36:24 +00:00
WINDOW_NAME: window_name,
};
2014-08-19 22:36:24 +00:00
NSArray *arr = enumerate_windows();
bool show_empty_names = obs_data_get_bool(settings, "show_empty_names");
NSDictionary *cur = find_window_dict(arr, window_id);
2014-08-19 22:36:24 +00:00
bool window_found = cur != nil;
bool window_added = window_found;
2014-08-19 22:36:24 +00:00
obs_property_list_clear(p);
for (NSDictionary *dict in arr) {
NSString *owner = (NSString *) dict[OWNER_NAME];
NSString *name = (NSString *) dict[WINDOW_NAME];
NSNumber *wid = (NSNumber *) dict[WINDOW_NUMBER];
2019-07-09 18:29:39 +00:00
if (!window_added && win_info_cmp(win_info, dict) == NSOrderedAscending) {
2014-08-19 22:36:24 +00:00
window_added = true;
size_t idx = obs_property_list_add_int(p, make_name(window_owner, window_name), window_id);
2014-08-19 22:36:24 +00:00
obs_property_list_item_disable(p, idx, true);
}
if (!show_empty_names && !name.length && window_id != wid.intValue)
2014-08-19 22:36:24 +00:00
continue;
obs_property_list_add_int(p, make_name(owner, name), wid.intValue);
2014-08-19 22:36:24 +00:00
}
2014-08-19 22:36:24 +00:00
if (!window_added) {
size_t idx = obs_property_list_add_int(p, make_name(window_owner, window_name), window_id);
2014-08-19 22:36:24 +00:00
obs_property_list_item_disable(p, idx, true);
}
2014-08-19 22:36:24 +00:00
if (!window_found)
return true;
NSString *owner = (NSString *) cur[OWNER_NAME];
NSString *window = (NSString *) cur[WINDOW_NAME];
obs_data_set_string(settings, "owner_name", owner.UTF8String);
obs_data_set_string(settings, "window_name", window.UTF8String);
2014-08-19 22:36:24 +00:00
return true;
}
static bool window_changed(obs_properties_t *props, obs_property_t *p, obs_data_t *settings)
2014-08-19 22:36:24 +00:00
{
UNUSED_PARAMETER(props);
@autoreleasepool {
return window_changed_internal(p, settings);
}
}
static bool toggle_empty_names(obs_properties_t *props, obs_property_t *p, obs_data_t *settings)
2014-08-19 22:36:24 +00:00
{
UNUSED_PARAMETER(p);
return window_changed(props, obs_properties_get(props, "window"), settings);
}
void window_defaults(obs_data_t *settings)
2014-08-19 22:36:24 +00:00
{
obs_data_set_default_int(settings, "window", kCGNullWindowID);
obs_data_set_default_bool(settings, "show_empty_names", false);
}
void add_window_properties(obs_properties_t *props)
2014-08-19 22:36:24 +00:00
{
2019-07-09 18:29:39 +00:00
obs_property_t *window_list = obs_properties_add_list(props, "window", obs_module_text("WindowUtils.Window"),
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
2014-08-19 22:36:24 +00:00
obs_property_set_modified_callback(window_list, window_changed);
2019-07-09 18:29:39 +00:00
obs_property_t *empty =
obs_properties_add_bool(props, "show_empty_names", obs_module_text("WindowUtils.ShowEmptyNames"));
2014-08-19 22:36:24 +00:00
obs_property_set_modified_callback(empty, toggle_empty_names);
}
void show_window_properties(obs_properties_t *props, bool show)
2014-08-19 22:36:24 +00:00
{
obs_property_set_visible(obs_properties_get(props, "window"), show);
2019-07-09 18:29:39 +00:00
obs_property_set_visible(obs_properties_get(props, "show_empty_names"), show);
2014-08-19 22:36:24 +00:00
}
CGDirectDisplayID get_display_migrate_settings(obs_data_t *settings)
{
bool legacy_display_id = obs_data_has_user_value(settings, "display");
bool new_display_id = obs_data_has_user_value(settings, "display_uuid");
if (legacy_display_id && !new_display_id) {
CGDirectDisplayID display_id = (CGDirectDisplayID) obs_data_get_int(settings, "display");
CFUUIDRef display_uuid = CGDisplayCreateUUIDFromDisplayID(display_id);
if (display_uuid) {
CFStringRef uuid_string = CFUUIDCreateString(kCFAllocatorDefault, display_uuid);
obs_data_set_string(settings, "display_uuid", CFStringGetCStringPtr(uuid_string, kCFStringEncodingUTF8));
obs_data_erase(settings, "display");
CFRelease(uuid_string);
CFRelease(display_uuid);
} else {
return 0;
}
} else if (legacy_display_id && new_display_id) {
obs_data_erase(settings, "display");
}
const char *display_uuid = obs_data_get_string(settings, "display_uuid");
if (display_uuid) {
CFStringRef uuid_string = CFStringCreateWithCString(kCFAllocatorDefault, display_uuid, kCFStringEncodingUTF8);
CFUUIDRef uuid_ref = CFUUIDCreateFromString(kCFAllocatorDefault, uuid_string);
CGDirectDisplayID display = CGDisplayGetDisplayIDFromUUID(uuid_ref);
CFRelease(uuid_string);
CFRelease(uuid_ref);
return display;
} else {
return 0;
}
}