From 989806022389e41ae72281b0158425dacbf29765 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Fri, 17 Oct 2014 04:01:18 -0700 Subject: [PATCH] win-capture: Separate window finding source code Because other capture methods may end up needing to share this code, separate the window finding source code to window-helpers.c and window-helpers.h. This include a function to fill out a property list with windows, a function to find a window based upon priority/title/class/exe, and a function to decode the window title/class/exe strings from a window setting string. --- plugins/win-capture/CMakeLists.txt | 2 + plugins/win-capture/window-capture.c | 261 +-------------------------- plugins/win-capture/window-helpers.c | 258 ++++++++++++++++++++++++++ plugins/win-capture/window-helpers.h | 25 +++ 4 files changed, 290 insertions(+), 256 deletions(-) create mode 100644 plugins/win-capture/window-helpers.c create mode 100644 plugins/win-capture/window-helpers.h diff --git a/plugins/win-capture/CMakeLists.txt b/plugins/win-capture/CMakeLists.txt index eb35b93b2..f7f090f20 100644 --- a/plugins/win-capture/CMakeLists.txt +++ b/plugins/win-capture/CMakeLists.txt @@ -1,10 +1,12 @@ project(win-capture) set(win-capture_HEADERS + window-helpers.h dc-capture.h) set(win-capture_SOURCES dc-capture.c + window-helpers.c monitor-capture.c window-capture.c plugin-main.c) diff --git a/plugins/win-capture/window-capture.c b/plugins/win-capture/window-capture.c index e0ab440fe..951e18be0 100644 --- a/plugins/win-capture/window-capture.c +++ b/plugins/win-capture/window-capture.c @@ -1,7 +1,7 @@ #include #include #include "dc-capture.h" -#include +#include "window-helpers.h" #define TEXT_WINDOW_CAPTURE obs_module_text("WindowCapture") #define TEXT_WINDOW obs_module_text("WindowCapture.Window") @@ -12,12 +12,6 @@ #define TEXT_CAPTURE_CURSOR obs_module_text("CaptureCursor") #define TEXT_COMPATIBILITY obs_module_text("Compatibility") -enum window_priority { - WINDOW_PRIORITY_CLASS, - WINDOW_PRIORITY_TITLE, - WINDOW_PRIORITY_EXE, -}; - struct window_capture { obs_source_t *source; @@ -39,21 +33,6 @@ struct window_capture { RECT last_rect; }; -void encode_dstr(struct dstr *str) -{ - dstr_replace(str, "#", "#22"); - dstr_replace(str, ":", "#3A"); -} - -char *decode_str(const char *src) -{ - struct dstr str = {0}; - dstr_copy(&str, src); - dstr_replace(&str, "#3A", ":"); - dstr_replace(&str, "#22", "#"); - return str.array; -} - static void update_settings(struct window_capture *wc, obs_data_t *s) { const char *window = obs_data_get_string(s, "window"); @@ -62,245 +41,14 @@ static void update_settings(struct window_capture *wc, obs_data_t *s) bfree(wc->title); bfree(wc->class); bfree(wc->executable); - wc->title = NULL; - wc->class = NULL; - wc->executable = NULL; - if (window) { - char **strlist = strlist_split(window, ':', true); - - if (strlist && strlist[0] && strlist[1] && strlist[2]) { - wc->title = decode_str(strlist[0]); - wc->class = decode_str(strlist[1]); - wc->executable = decode_str(strlist[2]); - } - - strlist_free(strlist); - } + build_window_strings(window, &wc->class, &wc->title, &wc->executable); wc->priority = (enum window_priority)priority; wc->cursor = obs_data_get_bool(s, "cursor"); wc->use_wildcards = obs_data_get_bool(s, "use_wildcards"); } -static bool get_exe_name(struct dstr *name, HWND window) -{ - wchar_t wname[MAX_PATH]; - struct dstr temp = {0}; - bool success = false; - HANDLE process = NULL; - char *slash; - DWORD id; - - GetWindowThreadProcessId(window, &id); - if (id == GetCurrentProcessId()) - return false; - - process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, id); - if (!process) - goto fail; - - if (!GetProcessImageFileNameW(process, wname, MAX_PATH)) - goto fail; - - dstr_from_wcs(&temp, wname); - slash = strrchr(temp.array, '\\'); - if (!slash) - goto fail; - - dstr_copy(name, slash+1); - success = true; - -fail: - if (!success) - dstr_copy(name, "unknown"); - - dstr_free(&temp); - CloseHandle(process); - return true; -} - -static void get_window_title(struct dstr *name, HWND hwnd) -{ - wchar_t *temp; - int len; - - len = GetWindowTextLengthW(hwnd); - if (!len) - return; - - temp = malloc(sizeof(wchar_t) * (len+1)); - GetWindowTextW(hwnd, temp, len+1); - dstr_from_wcs(name, temp); - free(temp); -} - -static void get_window_class(struct dstr *class, HWND hwnd) -{ - wchar_t temp[256]; - - temp[0] = 0; - GetClassNameW(hwnd, temp, sizeof(temp)); - dstr_from_wcs(class, temp); -} - -static void add_window(obs_property_t *p, HWND hwnd, - struct dstr *title, - struct dstr *class, - struct dstr *executable) -{ - struct dstr encoded = {0}; - struct dstr desc = {0}; - - if (!get_exe_name(executable, hwnd)) - return; - get_window_title(title, hwnd); - get_window_class(class, hwnd); - - dstr_printf(&desc, "[%s]: %s", executable->array, title->array); - - encode_dstr(title); - encode_dstr(class); - encode_dstr(executable); - - dstr_cat_dstr(&encoded, title); - dstr_cat(&encoded, ":"); - dstr_cat_dstr(&encoded, class); - dstr_cat(&encoded, ":"); - dstr_cat_dstr(&encoded, executable); - - obs_property_list_add_string(p, desc.array, encoded.array); - - dstr_free(&encoded); - dstr_free(&desc); -} - -static bool check_window_valid(HWND window, - struct dstr *title, - struct dstr *class, - struct dstr *executable) -{ - DWORD styles, ex_styles; - RECT rect; - - if (!IsWindowVisible(window) || IsIconic(window)) - return false; - - GetClientRect(window, &rect); - styles = (DWORD)GetWindowLongPtr(window, GWL_STYLE); - ex_styles = (DWORD)GetWindowLongPtr(window, GWL_EXSTYLE); - - if (ex_styles & WS_EX_TOOLWINDOW) - return false; - if (styles & WS_CHILD) - return false; - if (rect.bottom == 0 || rect.right == 0) - return false; - - if (!get_exe_name(executable, window)) - return false; - get_window_title(title, window); - get_window_class(class, window); - return true; -} - -static inline HWND next_window(HWND window, - struct dstr *title, - struct dstr *class, - struct dstr *exe) -{ - while (true) { - window = GetNextWindow(window, GW_HWNDNEXT); - if (!window || check_window_valid(window, title, class, exe)) - break; - } - - return window; -} - -static inline HWND first_window( - struct dstr *title, - struct dstr *class, - struct dstr *executable) -{ - HWND window = GetWindow(GetDesktopWindow(), GW_CHILD); - if (!check_window_valid(window, title, class, executable)) - window = next_window(window, title, class, executable); - return window; -} - -static void fill_window_list(obs_property_t *p) -{ - struct dstr title = {0}; - struct dstr class = {0}; - struct dstr executable = {0}; - - HWND window = first_window(&title, &class, &executable); - - while (window) { - add_window(p, window, &title, &class, &executable); - window = next_window(window, &title, &class, &executable); - } - - dstr_free(&title); - dstr_free(&class); - dstr_free(&executable); -} - -static int window_rating(struct window_capture *wc, - struct dstr *title, - struct dstr *class, - struct dstr *executable) -{ - int class_val = 1; - int title_val = 1; - int exe_val = 0; - int total = 0; - - if (wc->priority == WINDOW_PRIORITY_CLASS) - class_val += 3; - else if (wc->priority == WINDOW_PRIORITY_TITLE) - title_val += 3; - else - exe_val += 3; - - if (dstr_cmpi(class, wc->class) == 0) - total += class_val; - if (dstr_cmpi(title, wc->title) == 0) - total += title_val; - if (dstr_cmpi(executable, wc->executable) == 0) - total += exe_val; - - return total; -} - -static HWND find_window(struct window_capture *wc) -{ - struct dstr title = {0}; - struct dstr class = {0}; - struct dstr exe = {0}; - - HWND window = first_window(&title, &class, &exe); - HWND best_window = NULL; - int best_rating = 0; - - while (window) { - int rating = window_rating(wc, &title, &class, &exe); - if (rating > best_rating) { - best_rating = rating; - best_window = window; - } - - window = next_window(window, &title, &class, &exe); - } - - dstr_free(&title); - dstr_free(&class); - dstr_free(&exe); - - return best_window; -} - /* ------------------------------------------------------------------------- */ static const char *wc_getname(void) @@ -378,7 +126,7 @@ static obs_properties_t *wc_properties(void *unused) p = obs_properties_add_list(ppts, "window", TEXT_WINDOW, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); - fill_window_list(p); + fill_window_list(p, EXCLUDE_MINIMIZED); p = obs_properties_add_list(ppts, "priority", TEXT_MATCH_PRIORITY, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); @@ -405,7 +153,8 @@ static void wc_tick(void *data, float seconds) if (!wc->title && !wc->class) return; - wc->window = find_window(wc); + wc->window = find_window(EXCLUDE_MINIMIZED, wc->priority, + wc->class, wc->title, wc->executable); if (!wc->window) return; diff --git a/plugins/win-capture/window-helpers.c b/plugins/win-capture/window-helpers.c new file mode 100644 index 000000000..0e152db21 --- /dev/null +++ b/plugins/win-capture/window-helpers.c @@ -0,0 +1,258 @@ +#include +#include + +#include +#include +#include "window-helpers.h" + +#define inline __inline + +static inline void encode_dstr(struct dstr *str) +{ + dstr_replace(str, "#", "#22"); + dstr_replace(str, ":", "#3A"); +} + +static inline char *decode_str(const char *src) +{ + struct dstr str = {0}; + dstr_copy(&str, src); + dstr_replace(&str, "#3A", ":"); + dstr_replace(&str, "#22", "#"); + return str.array; +} + +extern void build_window_strings(const char *str, + char **class, + char **title, + char **exe) +{ + char **strlist; + + if (!str) { + *class = NULL; + *title = NULL; + *exe = NULL; + return; + } + + strlist = strlist_split(str, ':', true); + + if (strlist && strlist[0] && strlist[1] && strlist[2]) { + *title = decode_str(strlist[0]); + *class = decode_str(strlist[1]); + *exe = decode_str(strlist[2]); + } + + strlist_free(strlist); +} + +static bool get_window_exe(struct dstr *name, HWND window) +{ + wchar_t wname[MAX_PATH]; + struct dstr temp = {0}; + bool success = false; + HANDLE process = NULL; + char *slash; + DWORD id; + + GetWindowThreadProcessId(window, &id); + if (id == GetCurrentProcessId()) + return false; + + process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, id); + if (!process) + goto fail; + + if (!GetProcessImageFileNameW(process, wname, MAX_PATH)) + goto fail; + + dstr_from_wcs(&temp, wname); + slash = strrchr(temp.array, '\\'); + if (!slash) + goto fail; + + dstr_copy(name, slash+1); + success = true; + +fail: + if (!success) + dstr_copy(name, "unknown"); + + dstr_free(&temp); + CloseHandle(process); + return true; +} + +static void get_window_title(struct dstr *name, HWND hwnd) +{ + wchar_t *temp; + int len; + + len = GetWindowTextLengthW(hwnd); + if (!len) + return; + + temp = malloc(sizeof(wchar_t) * (len+1)); + GetWindowTextW(hwnd, temp, len+1); + dstr_from_wcs(name, temp); + free(temp); +} + +static void get_window_class(struct dstr *class, HWND hwnd) +{ + wchar_t temp[256]; + + temp[0] = 0; + GetClassNameW(hwnd, temp, sizeof(temp)); + dstr_from_wcs(class, temp); +} + +static void add_window(obs_property_t *p, HWND hwnd) +{ + struct dstr class = {0}; + struct dstr title = {0}; + struct dstr exe = {0}; + struct dstr encoded = {0}; + struct dstr desc = {0}; + + if (!get_window_exe(&exe, hwnd)) + return; + get_window_title(&title, hwnd); + get_window_class(&class, hwnd); + + dstr_printf(&desc, "[%s]: %s", exe.array, title.array); + + encode_dstr(&title); + encode_dstr(&class); + encode_dstr(&exe); + + dstr_cat_dstr(&encoded, &title); + dstr_cat(&encoded, ":"); + dstr_cat_dstr(&encoded, &class); + dstr_cat(&encoded, ":"); + dstr_cat_dstr(&encoded, &exe); + + obs_property_list_add_string(p, desc.array, encoded.array); + + dstr_free(&encoded); + dstr_free(&desc); + dstr_free(&class); + dstr_free(&title); + dstr_free(&exe); +} + +static bool check_window_valid(HWND window, enum window_search_mode mode) +{ + DWORD styles, ex_styles; + RECT rect; + + if (!IsWindowVisible(window) || + (mode == EXCLUDE_MINIMIZED && IsIconic(window))) + return false; + + GetClientRect(window, &rect); + styles = (DWORD)GetWindowLongPtr(window, GWL_STYLE); + ex_styles = (DWORD)GetWindowLongPtr(window, GWL_EXSTYLE); + + if (ex_styles & WS_EX_TOOLWINDOW) + return false; + if (styles & WS_CHILD) + return false; + if (rect.bottom == 0 || rect.right == 0) + return false; + + return true; +} + +static inline HWND next_window(HWND window, enum window_search_mode mode) +{ + while (true) { + window = GetNextWindow(window, GW_HWNDNEXT); + if (!window || check_window_valid(window, mode)) + break; + } + + return window; +} + +static inline HWND first_window(enum window_search_mode mode) +{ + HWND window = GetWindow(GetDesktopWindow(), GW_CHILD); + if (!check_window_valid(window, mode)) + window = next_window(window, mode); + return window; +} + +void fill_window_list(obs_property_t *p, enum window_search_mode mode) +{ + HWND window = first_window(mode); + + while (window) { + add_window(p, window); + window = next_window(window, mode); + } +} + +static int window_rating(HWND window, + enum window_priority priority, + const char *class, + const char *title, + const char *exe) +{ + struct dstr cur_class = {0}; + struct dstr cur_title = {0}; + struct dstr cur_exe = {0}; + int class_val = 1; + int title_val = 1; + int exe_val = 0; + int total = 0; + + if (!get_window_exe(&cur_exe, window)) + return 0; + get_window_title(&cur_title, window); + get_window_class(&cur_class, window); + + if (priority == WINDOW_PRIORITY_CLASS) + class_val += 3; + else if (priority == WINDOW_PRIORITY_TITLE) + title_val += 3; + else + exe_val += 3; + + if (dstr_cmpi(&cur_class, class) == 0) + total += class_val; + if (dstr_cmpi(&cur_title, title) == 0) + total += title_val; + if (dstr_cmpi(&cur_exe, exe) == 0) + total += exe_val; + + dstr_free(&cur_class); + dstr_free(&cur_title); + dstr_free(&cur_exe); + + return total; +} + +HWND find_window(enum window_search_mode mode, + enum window_priority priority, + const char *class, + const char *title, + const char *exe) +{ + HWND window = first_window(mode); + HWND best_window = NULL; + int best_rating = 0; + + while (window) { + int rating = window_rating(window, priority, class, title, exe); + if (rating > best_rating) { + best_rating = rating; + best_window = window; + } + + window = next_window(window, mode); + } + + return best_window; +} diff --git a/plugins/win-capture/window-helpers.h b/plugins/win-capture/window-helpers.h new file mode 100644 index 000000000..9199aa4bc --- /dev/null +++ b/plugins/win-capture/window-helpers.h @@ -0,0 +1,25 @@ +#pragma once + +enum window_priority { + WINDOW_PRIORITY_CLASS, + WINDOW_PRIORITY_TITLE, + WINDOW_PRIORITY_EXE, +}; + +enum window_search_mode { + INCLUDE_MINIMIZED, + EXCLUDE_MINIMIZED +}; + +extern void fill_window_list(obs_property_t *p, enum window_search_mode mode); + +extern void build_window_strings(const char *str, + char **class, + char **title, + char **exe); + +extern HWND find_window(enum window_search_mode mode, + enum window_priority priority, + const char *class, + const char *title, + const char *exe);