2013-12-07 09:22:56 -08:00
|
|
|
/******************************************************************************
|
|
|
|
Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
******************************************************************************/
|
|
|
|
|
2014-07-11 11:32:27 -07:00
|
|
|
#include <algorithm>
|
2013-12-07 09:22:56 -08:00
|
|
|
#include <sstream>
|
2014-07-21 07:26:57 -07:00
|
|
|
#include "obs-config.h"
|
2014-07-11 11:32:27 -07:00
|
|
|
#include "obs-app.hpp"
|
2013-12-07 09:22:56 -08:00
|
|
|
#include "platform.hpp"
|
|
|
|
|
2015-08-01 14:44:59 -07:00
|
|
|
#include <util/windows/win-version.h>
|
2013-12-12 20:47:42 -08:00
|
|
|
#include <util/platform.h>
|
|
|
|
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
|
|
#include <windows.h>
|
2014-05-20 23:08:47 -07:00
|
|
|
#include <shellapi.h>
|
|
|
|
#include <shlobj.h>
|
2015-05-25 01:34:49 -07:00
|
|
|
#include <Dwmapi.h>
|
2017-04-17 06:38:16 -07:00
|
|
|
#include <mmdeviceapi.h>
|
|
|
|
#include <audiopolicy.h>
|
|
|
|
|
2017-05-14 15:25:34 -07:00
|
|
|
#include <util/windows/WinHandle.hpp>
|
2017-04-17 06:38:16 -07:00
|
|
|
#include <util/windows/HRError.hpp>
|
|
|
|
#include <util/windows/ComPtr.hpp>
|
2013-12-12 20:47:42 -08:00
|
|
|
|
2019-10-15 21:08:40 -07:00
|
|
|
using namespace std;
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
static inline bool check_path(const char *data, const char *path,
|
|
|
|
string &output)
|
2013-12-07 09:22:56 -08:00
|
|
|
{
|
2014-01-25 13:11:57 -08:00
|
|
|
ostringstream str;
|
|
|
|
str << path << data;
|
2013-12-07 09:22:56 -08:00
|
|
|
output = str.str();
|
2014-01-25 13:11:57 -08:00
|
|
|
|
|
|
|
printf("Attempted path: %s\n", output.c_str());
|
|
|
|
|
2013-12-12 20:47:42 -08:00
|
|
|
return os_file_exists(output.c_str());
|
2013-12-07 09:22:56 -08:00
|
|
|
}
|
2013-12-15 23:06:13 -08:00
|
|
|
|
2014-01-25 13:11:57 -08:00
|
|
|
bool GetDataFilePath(const char *data, string &output)
|
|
|
|
{
|
|
|
|
if (check_path(data, "data/obs-studio/", output))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return check_path(data, OBS_DATA_PATH "/obs-studio/", output);
|
|
|
|
}
|
|
|
|
|
2014-01-08 16:17:30 -08:00
|
|
|
bool InitApplicationBundle()
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
2014-05-20 23:08:47 -07:00
|
|
|
|
|
|
|
string GetDefaultVideoSavePath()
|
|
|
|
{
|
|
|
|
wchar_t path_utf16[MAX_PATH];
|
2019-06-22 22:13:45 -07:00
|
|
|
char path_utf8[MAX_PATH] = {};
|
2014-05-20 23:08:47 -07:00
|
|
|
|
|
|
|
SHGetFolderPathW(NULL, CSIDL_MYVIDEO, NULL, SHGFP_TYPE_CURRENT,
|
2019-06-22 22:13:45 -07:00
|
|
|
path_utf16);
|
2014-05-20 23:08:47 -07:00
|
|
|
|
2014-05-22 03:42:43 -07:00
|
|
|
os_wcs_to_utf8(path_utf16, wcslen(path_utf16), path_utf8, MAX_PATH);
|
2014-05-20 23:08:47 -07:00
|
|
|
return string(path_utf8);
|
|
|
|
}
|
2014-07-11 11:32:27 -07:00
|
|
|
|
|
|
|
static vector<string> GetUserPreferredLocales()
|
|
|
|
{
|
|
|
|
vector<string> result;
|
|
|
|
|
|
|
|
ULONG num, length = 0;
|
2019-06-22 22:13:45 -07:00
|
|
|
if (!GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &num, nullptr,
|
|
|
|
&length))
|
2014-07-11 11:32:27 -07:00
|
|
|
return result;
|
|
|
|
|
|
|
|
vector<wchar_t> buffer(length);
|
|
|
|
if (!GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &num,
|
2019-06-22 22:13:45 -07:00
|
|
|
&buffer.front(), &length))
|
2014-07-11 11:32:27 -07:00
|
|
|
return result;
|
|
|
|
|
|
|
|
result.reserve(num);
|
|
|
|
auto start = begin(buffer);
|
2019-06-22 22:13:45 -07:00
|
|
|
auto end_ = end(buffer);
|
2014-07-11 11:32:27 -07:00
|
|
|
decltype(start) separator;
|
|
|
|
while ((separator = find(start, end_, 0)) != end_) {
|
|
|
|
if (result.size() == num)
|
|
|
|
break;
|
|
|
|
|
|
|
|
char conv[MAX_PATH] = {};
|
|
|
|
os_wcs_to_utf8(&*start, separator - start, conv, MAX_PATH);
|
|
|
|
|
|
|
|
result.emplace_back(conv);
|
|
|
|
|
|
|
|
start = separator + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
vector<string> GetPreferredLocales()
|
|
|
|
{
|
|
|
|
vector<string> windows_locales = GetUserPreferredLocales();
|
|
|
|
auto obs_locales = GetLocaleNames();
|
|
|
|
auto windows_to_obs = [&obs_locales](string windows) {
|
|
|
|
string lang_match;
|
|
|
|
|
|
|
|
for (auto &locale_pair : obs_locales) {
|
|
|
|
auto &locale = locale_pair.first;
|
|
|
|
if (locale == windows.substr(0, locale.size()))
|
|
|
|
return locale;
|
|
|
|
|
|
|
|
if (lang_match.size())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (locale.substr(0, 2) == windows.substr(0, 2))
|
|
|
|
lang_match = locale;
|
|
|
|
}
|
|
|
|
|
|
|
|
return lang_match;
|
|
|
|
};
|
|
|
|
|
|
|
|
vector<string> result;
|
|
|
|
result.reserve(obs_locales.size());
|
|
|
|
|
|
|
|
for (const string &locale : windows_locales) {
|
|
|
|
string match = windows_to_obs(locale);
|
|
|
|
if (!match.size())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (find(begin(result), end(result), match) != end(result))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
result.emplace_back(match);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2015-05-25 01:34:49 -07:00
|
|
|
|
|
|
|
uint32_t GetWindowsVersion()
|
|
|
|
{
|
|
|
|
static uint32_t ver = 0;
|
|
|
|
|
|
|
|
if (ver == 0) {
|
2015-08-01 14:44:59 -07:00
|
|
|
struct win_version_info ver_info;
|
2015-05-25 01:34:49 -07:00
|
|
|
|
2015-08-01 14:44:59 -07:00
|
|
|
get_win_ver(&ver_info);
|
|
|
|
ver = (ver_info.major << 8) | ver_info.minor;
|
2015-05-25 01:34:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return ver;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetAeroEnabled(bool enable)
|
|
|
|
{
|
2019-06-22 22:13:45 -07:00
|
|
|
static HRESULT(WINAPI * func)(UINT) = nullptr;
|
2015-05-25 01:34:49 -07:00
|
|
|
static bool failed = false;
|
|
|
|
|
|
|
|
if (!func) {
|
|
|
|
if (failed) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
HMODULE dwm = LoadLibraryW(L"dwmapi");
|
|
|
|
if (!dwm) {
|
|
|
|
failed = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
func = reinterpret_cast<decltype(func)>(
|
|
|
|
GetProcAddress(dwm, "DwmEnableComposition"));
|
2015-05-25 01:34:49 -07:00
|
|
|
if (!func) {
|
|
|
|
failed = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func(enable ? DWM_EC_ENABLECOMPOSITION : DWM_EC_DISABLECOMPOSITION);
|
|
|
|
}
|
2016-01-22 07:33:29 -08:00
|
|
|
|
2016-08-21 16:11:32 -07:00
|
|
|
bool IsAlwaysOnTop(QWidget *window)
|
2016-01-22 07:33:29 -08:00
|
|
|
{
|
|
|
|
DWORD exStyle = GetWindowLong((HWND)window->winId(), GWL_EXSTYLE);
|
|
|
|
return (exStyle & WS_EX_TOPMOST) != 0;
|
|
|
|
}
|
|
|
|
|
2016-08-21 16:11:32 -07:00
|
|
|
void SetAlwaysOnTop(QWidget *window, bool enable)
|
2016-01-22 07:33:29 -08:00
|
|
|
{
|
|
|
|
HWND hwnd = (HWND)window->winId();
|
|
|
|
SetWindowPos(hwnd, enable ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0,
|
2019-06-22 22:13:45 -07:00
|
|
|
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
2016-01-22 07:33:29 -08:00
|
|
|
}
|
2016-07-01 10:27:27 -07:00
|
|
|
|
|
|
|
void SetProcessPriority(const char *priority)
|
|
|
|
{
|
|
|
|
if (!priority)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (strcmp(priority, "High") == 0)
|
|
|
|
SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
|
|
|
|
else if (strcmp(priority, "AboveNormal") == 0)
|
2019-06-22 22:13:45 -07:00
|
|
|
SetPriorityClass(GetCurrentProcess(),
|
|
|
|
ABOVE_NORMAL_PRIORITY_CLASS);
|
2016-07-01 10:27:27 -07:00
|
|
|
else if (strcmp(priority, "Normal") == 0)
|
|
|
|
SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
|
2017-07-26 14:44:58 -07:00
|
|
|
else if (strcmp(priority, "BelowNormal") == 0)
|
2019-06-22 22:13:45 -07:00
|
|
|
SetPriorityClass(GetCurrentProcess(),
|
|
|
|
BELOW_NORMAL_PRIORITY_CLASS);
|
2016-07-01 10:27:27 -07:00
|
|
|
else if (strcmp(priority, "Idle") == 0)
|
|
|
|
SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS);
|
|
|
|
}
|
2016-09-26 12:40:23 -07:00
|
|
|
|
|
|
|
void SetWin32DropStyle(QWidget *window)
|
|
|
|
{
|
|
|
|
HWND hwnd = (HWND)window->winId();
|
|
|
|
LONG_PTR ex_style = GetWindowLongPtr(hwnd, GWL_EXSTYLE);
|
|
|
|
ex_style |= WS_EX_ACCEPTFILES;
|
|
|
|
SetWindowLongPtr(hwnd, GWL_EXSTYLE, ex_style);
|
|
|
|
}
|
2017-04-17 06:38:16 -07:00
|
|
|
|
|
|
|
bool DisableAudioDucking(bool disable)
|
|
|
|
{
|
2019-06-22 22:13:45 -07:00
|
|
|
ComPtr<IMMDeviceEnumerator> devEmum;
|
|
|
|
ComPtr<IMMDevice> device;
|
2017-04-17 06:38:16 -07:00
|
|
|
ComPtr<IAudioSessionManager2> sessionManager2;
|
2019-06-22 22:13:45 -07:00
|
|
|
ComPtr<IAudioSessionControl> sessionControl;
|
2017-04-17 06:38:16 -07:00
|
|
|
ComPtr<IAudioSessionControl2> sessionControl2;
|
|
|
|
|
2019-06-22 22:13:45 -07:00
|
|
|
HRESULT result = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr,
|
|
|
|
CLSCTX_INPROC_SERVER,
|
|
|
|
__uuidof(IMMDeviceEnumerator),
|
|
|
|
(void **)&devEmum);
|
2017-04-17 06:38:16 -07:00
|
|
|
if (FAILED(result))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
result = devEmum->GetDefaultAudioEndpoint(eRender, eConsole, &device);
|
|
|
|
if (FAILED(result))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
result = device->Activate(__uuidof(IAudioSessionManager2),
|
2019-06-22 22:13:45 -07:00
|
|
|
CLSCTX_INPROC_SERVER, nullptr,
|
|
|
|
(void **)&sessionManager2);
|
2017-04-17 06:38:16 -07:00
|
|
|
if (FAILED(result))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
result = sessionManager2->GetAudioSessionControl(nullptr, 0,
|
2019-06-22 22:13:45 -07:00
|
|
|
&sessionControl);
|
2017-04-17 06:38:16 -07:00
|
|
|
if (FAILED(result))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
result = sessionControl->QueryInterface(&sessionControl2);
|
|
|
|
if (FAILED(result))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
result = sessionControl2->SetDuckingPreference(disable);
|
|
|
|
return SUCCEEDED(result);
|
|
|
|
}
|
2017-05-13 01:28:00 -07:00
|
|
|
|
2017-05-14 15:25:34 -07:00
|
|
|
struct RunOnceMutexData {
|
|
|
|
WinHandle handle;
|
|
|
|
|
|
|
|
inline RunOnceMutexData(HANDLE h) : handle(h) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
RunOnceMutex::RunOnceMutex(RunOnceMutex &&rom)
|
|
|
|
{
|
|
|
|
delete data;
|
|
|
|
data = rom.data;
|
|
|
|
rom.data = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
RunOnceMutex::~RunOnceMutex()
|
|
|
|
{
|
|
|
|
delete data;
|
|
|
|
}
|
|
|
|
|
|
|
|
RunOnceMutex &RunOnceMutex::operator=(RunOnceMutex &&rom)
|
|
|
|
{
|
|
|
|
delete data;
|
|
|
|
data = rom.data;
|
|
|
|
rom.data = nullptr;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
RunOnceMutex GetRunOnceMutex(bool &already_running)
|
|
|
|
{
|
|
|
|
string name;
|
|
|
|
|
|
|
|
if (!portable_mode) {
|
|
|
|
name = "OBSStudioCore";
|
|
|
|
} else {
|
|
|
|
char path[500];
|
2020-02-03 20:00:42 -08:00
|
|
|
char absPath[512];
|
2017-05-14 15:25:34 -07:00
|
|
|
*path = 0;
|
2020-02-03 20:00:42 -08:00
|
|
|
*absPath = 0;
|
2017-05-14 15:25:34 -07:00
|
|
|
GetConfigPath(path, sizeof(path), "");
|
2020-02-03 20:00:42 -08:00
|
|
|
os_get_abs_path(path, absPath, sizeof(absPath));
|
2017-05-14 15:25:34 -07:00
|
|
|
name = "OBSStudioPortable";
|
2020-02-03 20:00:42 -08:00
|
|
|
name += absPath;
|
2017-05-14 15:25:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
BPtr<wchar_t> wname;
|
|
|
|
os_utf8_to_wcs_ptr(name.c_str(), name.size(), &wname);
|
|
|
|
|
|
|
|
if (wname) {
|
|
|
|
wchar_t *temp = wname;
|
|
|
|
while (*temp) {
|
|
|
|
if (!iswalnum(*temp))
|
|
|
|
*temp = L'_';
|
|
|
|
temp++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
HANDLE h = OpenMutexW(SYNCHRONIZE, false, wname.Get());
|
|
|
|
already_running = !!h;
|
|
|
|
|
|
|
|
if (!already_running)
|
|
|
|
h = CreateMutexW(nullptr, false, wname.Get());
|
|
|
|
|
|
|
|
RunOnceMutex rom(h ? new RunOnceMutexData(h) : nullptr);
|
|
|
|
return rom;
|
|
|
|
}
|