c9df41c1e2
Typedef pointers are unsafe. If you do: typedef struct bla *bla_t; then you cannot use it as a constant, such as: const bla_t, because that constant will be to the pointer itself rather than to the underlying data. I admit this was a fundamental mistake that must be corrected. All typedefs that were pointer types will now have their pointers removed from the type itself, and the pointers will be used when they are actually used as variables/parameters/returns instead. This does not break ABI though, which is pretty nice.
234 lines
6.0 KiB
Objective-C
234 lines
6.0 KiB
Objective-C
#include "window-utils.h"
|
|
|
|
#include <util/platform.h>
|
|
|
|
#define WINDOW_NAME ((NSString*)kCGWindowName)
|
|
#define WINDOW_NUMBER ((NSString*)kCGWindowNumber)
|
|
#define OWNER_NAME ((NSString*)kCGWindowOwnerName)
|
|
#define OWNER_PID ((NSNumber*)kCGWindowOwnerPID)
|
|
|
|
static NSComparator win_info_cmp = ^(NSDictionary *o1, NSDictionary *o2)
|
|
{
|
|
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)
|
|
{
|
|
NSArray *arr = (NSArray*)CGWindowListCopyWindowInfo(
|
|
kCGWindowListOptionOnScreenOnly,
|
|
kCGNullWindowID);
|
|
|
|
[arr autorelease];
|
|
|
|
return [arr sortedArrayUsingComparator:win_info_cmp];
|
|
}
|
|
|
|
#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)
|
|
{
|
|
if (!force && cw->next_search_time > os_gettime_ns())
|
|
return false;
|
|
|
|
cw->next_search_time = os_gettime_ns() + WAIT_TIME_NS;
|
|
|
|
pthread_mutex_lock(&cw->name_lock);
|
|
|
|
if (!cw->window_name.length && !cw->owner_name.length)
|
|
goto invalid_name;
|
|
|
|
for (NSDictionary *dict in enumerate_windows()) {
|
|
if (![cw->owner_name isEqualToString:dict[OWNER_NAME]])
|
|
continue;
|
|
|
|
if (![cw->window_name isEqualToString:dict[WINDOW_NAME]])
|
|
continue;
|
|
|
|
pthread_mutex_unlock(&cw->name_lock);
|
|
|
|
NSNumber *window_id = (NSNumber*)dict[WINDOW_NUMBER];
|
|
cw->window_id = window_id.intValue;
|
|
|
|
obs_data_set_int(settings, "window", cw->window_id);
|
|
return true;
|
|
}
|
|
|
|
invalid_name:
|
|
pthread_mutex_unlock(&cw->name_lock);
|
|
return false;
|
|
}
|
|
|
|
void init_window(cocoa_window_t cw, obs_data_t *settings)
|
|
{
|
|
pthread_mutex_init(&cw->name_lock, NULL);
|
|
|
|
cw->owner_name = @(obs_data_get_string(settings, "owner_name"));
|
|
cw->window_name = @(obs_data_get_string(settings, "window_name"));
|
|
[cw->owner_name retain];
|
|
[cw->window_name retain];
|
|
find_window(cw, settings, true);
|
|
}
|
|
|
|
void destroy_window(cocoa_window_t cw)
|
|
{
|
|
pthread_mutex_destroy(&cw->name_lock);
|
|
[cw->owner_name release];
|
|
[cw->window_name release];
|
|
}
|
|
|
|
void update_window(cocoa_window_t cw, obs_data_t *settings)
|
|
{
|
|
pthread_mutex_lock(&cw->name_lock);
|
|
[cw->owner_name release];
|
|
[cw->window_name release];
|
|
cw->owner_name = @(obs_data_get_string(settings, "owner_name"));
|
|
cw->window_name = @(obs_data_get_string(settings, "window_name"));
|
|
[cw->owner_name retain];
|
|
[cw->window_name retain];
|
|
pthread_mutex_unlock(&cw->name_lock);
|
|
|
|
cw->window_id = obs_data_get_int(settings, "window");
|
|
}
|
|
|
|
static inline const char *make_name(NSString *owner, NSString *name)
|
|
{
|
|
if (!owner.length)
|
|
return "";
|
|
|
|
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;
|
|
}
|
|
|
|
static inline bool window_changed_internal(obs_property_t *p,
|
|
obs_data_t *settings)
|
|
{
|
|
int window_id = obs_data_get_int(settings, "window");
|
|
NSString *window_owner = @(obs_data_get_string(settings, "owner_name"));
|
|
NSString *window_name =
|
|
@(obs_data_get_string(settings, "window_name"));
|
|
|
|
NSDictionary *win_info = @{
|
|
OWNER_NAME: window_owner,
|
|
WINDOW_NAME: window_name,
|
|
};
|
|
|
|
NSArray *arr = enumerate_windows();
|
|
|
|
bool show_empty_names = obs_data_get_bool(settings, "show_empty_names");
|
|
|
|
NSDictionary *cur = find_window_dict(arr, window_id);
|
|
bool window_found = cur != nil;
|
|
bool window_added = window_found;
|
|
|
|
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];
|
|
|
|
if (!window_added &&
|
|
win_info_cmp(win_info, dict) == NSOrderedAscending) {
|
|
window_added = true;
|
|
size_t idx = obs_property_list_add_int(p,
|
|
make_name(window_owner, window_name),
|
|
window_id);
|
|
obs_property_list_item_disable(p, idx, true);
|
|
}
|
|
|
|
if (!show_empty_names && !name.length &&
|
|
window_id != wid.intValue)
|
|
continue;
|
|
|
|
obs_property_list_add_int(p, make_name(owner, name),
|
|
wid.intValue);
|
|
}
|
|
|
|
if (!window_added) {
|
|
size_t idx = obs_property_list_add_int(p,
|
|
make_name(window_owner, window_name),
|
|
window_id);
|
|
obs_property_list_item_disable(p, idx, true);
|
|
}
|
|
|
|
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);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool window_changed(obs_properties_t *props, obs_property_t *p,
|
|
obs_data_t *settings)
|
|
{
|
|
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)
|
|
{
|
|
UNUSED_PARAMETER(p);
|
|
|
|
return window_changed(props, obs_properties_get(props, "window"),
|
|
settings);
|
|
}
|
|
|
|
void window_defaults(obs_data_t *settings)
|
|
{
|
|
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)
|
|
{
|
|
obs_property_t *window_list = obs_properties_add_list(props,
|
|
"window", obs_module_text("WindowUtils.Window"),
|
|
OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
|
|
obs_property_set_modified_callback(window_list, window_changed);
|
|
|
|
obs_property_t *empty = obs_properties_add_bool(props,
|
|
"show_empty_names",
|
|
obs_module_text("WindowUtils.ShowEmptyNames"));
|
|
obs_property_set_modified_callback(empty, toggle_empty_names);
|
|
}
|
|
|
|
void show_window_properties(obs_properties_t *props, bool show)
|
|
{
|
|
obs_property_set_visible(obs_properties_get(props, "window"), show);
|
|
obs_property_set_visible(
|
|
obs_properties_get(props, "show_empty_names"), show);
|
|
}
|