obs-app.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. /******************************************************************************
  2. Copyright (C) 2013 by Hugh Bailey <[email protected]>
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ******************************************************************************/
  14. #include <time.h>
  15. #include <stdio.h>
  16. #include <sstream>
  17. #include <util/bmem.h>
  18. #include <util/dstr.h>
  19. #include <util/platform.h>
  20. #include <obs-config.h>
  21. #include <obs.hpp>
  22. #include <QProxyStyle>
  23. #include "qt-wrappers.hpp"
  24. #include "obs-app.hpp"
  25. #include "window-basic-main.hpp"
  26. #include "platform.hpp"
  27. #include <fstream>
  28. #ifdef _WIN32
  29. #include <windows.h>
  30. #define snprintf _snprintf
  31. #else
  32. #include <signal.h>
  33. #endif
  34. using namespace std;
  35. static log_handler_t def_log_handler;
  36. static void do_log(int log_level, const char *msg, va_list args, void *param)
  37. {
  38. fstream &logFile = *static_cast<fstream*>(param);
  39. char str[4096];
  40. va_list args2;
  41. va_copy(args2, args);
  42. vsnprintf(str, 4095, msg, args);
  43. #ifdef _WIN32
  44. OutputDebugStringA(str);
  45. OutputDebugStringA("\n");
  46. #else
  47. def_log_handler(log_level, msg, args2, nullptr);
  48. #endif
  49. if (log_level <= LOG_INFO)
  50. logFile << str << endl;
  51. #ifdef _WIN32
  52. if (log_level <= LOG_ERROR && IsDebuggerPresent())
  53. __debugbreak();
  54. #endif
  55. }
  56. bool OBSApp::InitGlobalConfigDefaults()
  57. {
  58. config_set_default_string(globalConfig, "General", "Language", "en");
  59. config_set_default_uint(globalConfig, "General", "MaxLogs", 10);
  60. #if _WIN32
  61. config_set_default_string(globalConfig, "Video", "Renderer",
  62. "Direct3D 11");
  63. #else
  64. config_set_default_string(globalConfig, "Video", "Renderer", "OpenGL");
  65. #endif
  66. return true;
  67. }
  68. static bool do_mkdir(const char *path)
  69. {
  70. if (os_mkdir(path) == MKDIR_ERROR) {
  71. OBSErrorBox(NULL, "Failed to create directory %s", path);
  72. return false;
  73. }
  74. return true;
  75. }
  76. static bool MakeUserDirs()
  77. {
  78. BPtr<char> path;
  79. path = os_get_config_path("obs-studio");
  80. if (!do_mkdir(path))
  81. return false;
  82. path = os_get_config_path("obs-studio/basic");
  83. if (!do_mkdir(path))
  84. return false;
  85. path = os_get_config_path("obs-studio/studio");
  86. if (!do_mkdir(path))
  87. return false;
  88. path = os_get_config_path("obs-studio/logs");
  89. if (!do_mkdir(path))
  90. return false;
  91. return true;
  92. }
  93. bool OBSApp::InitGlobalConfig()
  94. {
  95. BPtr<char> path(os_get_config_path("obs-studio/global.ini"));
  96. int errorcode = globalConfig.Open(path, CONFIG_OPEN_ALWAYS);
  97. if (errorcode != CONFIG_SUCCESS) {
  98. OBSErrorBox(NULL, "Failed to open global.ini: %d", errorcode);
  99. return false;
  100. }
  101. return InitGlobalConfigDefaults();
  102. }
  103. #define DEFAULT_LANG "en"
  104. bool OBSApp::InitLocale()
  105. {
  106. const char *lang = config_get_string(globalConfig, "General",
  107. "Language");
  108. locale = lang;
  109. stringstream file;
  110. file << "locale/" << lang << ".txt";
  111. string englishPath;
  112. if (!GetDataFilePath("locale/" DEFAULT_LANG ".txt", englishPath)) {
  113. OBSErrorBox(NULL, "Failed to find locale/" DEFAULT_LANG ".txt");
  114. return false;
  115. }
  116. textLookup = text_lookup_create(englishPath.c_str());
  117. if (!textLookup) {
  118. OBSErrorBox(NULL, "Failed to create locale from file '%s'",
  119. englishPath.c_str());
  120. return false;
  121. }
  122. if (astrcmpi(lang, DEFAULT_LANG) == 0)
  123. return true;
  124. string path;
  125. if (GetDataFilePath(file.str().c_str(), path)) {
  126. if (!text_lookup_add(textLookup, path.c_str()))
  127. blog(LOG_ERROR, "Failed to add locale file '%s'",
  128. path.c_str());
  129. } else {
  130. blog(LOG_ERROR, "Could not find locale file '%s'",
  131. file.str().c_str());
  132. }
  133. return true;
  134. }
  135. OBSApp::OBSApp(int &argc, char **argv)
  136. : QApplication(argc, argv)
  137. {
  138. if (!InitApplicationBundle())
  139. throw "Failed to initialize application bundle";
  140. if (!MakeUserDirs())
  141. throw "Failed to created required user directories";
  142. if (!InitGlobalConfig())
  143. throw "Failed to initialize global config";
  144. if (!InitLocale())
  145. throw "Failed to load locale";
  146. }
  147. const char *OBSApp::GetRenderModule() const
  148. {
  149. const char *renderer = config_get_string(globalConfig, "Video",
  150. "Renderer");
  151. if (astrcmpi(renderer, "Direct3D 11") == 0)
  152. return "libobs-d3d11";
  153. else
  154. return "libobs-opengl";
  155. }
  156. void OBSApp::OBSInit()
  157. {
  158. mainWindow = move(unique_ptr<OBSBasic>(new OBSBasic()));
  159. mainWindow->OBSInit();
  160. }
  161. string OBSApp::GetVersionString() const
  162. {
  163. stringstream ver;
  164. ver << "v" <<
  165. LIBOBS_API_MAJOR_VER << "." <<
  166. LIBOBS_API_MINOR_VER << "." <<
  167. LIBOBS_API_PATCH_VER;
  168. #ifdef HAVE_OBSCONFIG_H
  169. ver << " (" << OBS_VERSION << ")";
  170. #endif
  171. #ifdef _WIN32
  172. if (sizeof(void*) == 8)
  173. ver << " (64bit)";
  174. else
  175. ver << " (32bit)";
  176. #endif
  177. blog(LOG_INFO, "%s", ver.str().c_str());
  178. return ver.str();
  179. }
  180. #ifdef __APPLE__
  181. #define INPUT_AUDIO_SOURCE "coreaudio_input_capture"
  182. #define OUTPUT_AUDIO_SOURCE "coreaudio_output_capture"
  183. #elif _WIN32
  184. #define INPUT_AUDIO_SOURCE "wasapi_input_capture"
  185. #define OUTPUT_AUDIO_SOURCE "wasapi_output_capture"
  186. #else
  187. #define INPUT_AUDIO_SOURCE "pulse_input_capture"
  188. #define OUTPUT_AUDIO_SOURCE "pulse_output_capture"
  189. #endif
  190. const char *OBSApp::InputAudioSource() const
  191. {
  192. return INPUT_AUDIO_SOURCE;
  193. }
  194. const char *OBSApp::OutputAudioSource() const
  195. {
  196. return OUTPUT_AUDIO_SOURCE;
  197. }
  198. QString OBSTranslator::translate(const char *context, const char *sourceText,
  199. const char *disambiguation, int n) const
  200. {
  201. const char *out = nullptr;
  202. if (!text_lookup_getstr(App()->GetTextLookup(), sourceText, &out))
  203. return QString();
  204. UNUSED_PARAMETER(context);
  205. UNUSED_PARAMETER(disambiguation);
  206. UNUSED_PARAMETER(n);
  207. return QT_UTF8(out);
  208. }
  209. struct NoFocusFrameStyle : QProxyStyle
  210. {
  211. void drawControl(ControlElement element, const QStyleOption *option,
  212. QPainter *painter, const QWidget *widget=nullptr)
  213. const override
  214. {
  215. if (element == CE_FocusFrame)
  216. return;
  217. QProxyStyle::drawControl(element, option, painter, widget);
  218. }
  219. };
  220. static void delete_oldest_log(void)
  221. {
  222. BPtr<char> logDir(os_get_config_path("obs-studio/logs"));
  223. char firstLog[256] = {};
  224. struct os_dirent *entry;
  225. unsigned int maxLogs = (unsigned int)config_get_uint(
  226. App()->GlobalConfig(), "General", "MaxLogs");
  227. os_dir_t dir = os_opendir(logDir);
  228. if (dir) {
  229. unsigned int count = 0;
  230. while ((entry = os_readdir(dir)) != NULL) {
  231. if (entry->directory)
  232. continue;
  233. /* no hidden files */
  234. if (*entry->d_name == '.')
  235. continue;
  236. if (!*firstLog)
  237. strncpy(firstLog, entry->d_name, 255);
  238. count++;
  239. }
  240. os_closedir(dir);
  241. if (count > maxLogs && *firstLog) {
  242. stringstream delPath;
  243. delPath << logDir << "/" << firstLog;
  244. os_unlink(delPath.str().c_str());
  245. }
  246. }
  247. }
  248. static void create_log_file(fstream &logFile)
  249. {
  250. stringstream dst;
  251. time_t now = time(0);
  252. struct tm *cur_time;
  253. cur_time = localtime(&now);
  254. if (cur_time) {
  255. char file[256] = {};
  256. snprintf(file, sizeof(file), "%d-%02d-%02d %02d-%02d-%02d.txt",
  257. cur_time->tm_year+1900,
  258. cur_time->tm_mon+1,
  259. cur_time->tm_mday,
  260. cur_time->tm_hour,
  261. cur_time->tm_min,
  262. cur_time->tm_sec);
  263. dst << "obs-studio/logs/" << file;
  264. BPtr<char> path(os_get_config_path(dst.str().c_str()));
  265. logFile.open(path,
  266. ios_base::in | ios_base::out | ios_base::trunc);
  267. }
  268. if (logFile.is_open()) {
  269. delete_oldest_log();
  270. base_set_log_handler(do_log, &logFile);
  271. } else {
  272. blog(LOG_ERROR, "Failed to open log file");
  273. }
  274. }
  275. static int run_program(fstream &logFile, int argc, char *argv[])
  276. {
  277. int ret = -1;
  278. QCoreApplication::addLibraryPath(".");
  279. try {
  280. OBSApp program(argc, argv);
  281. OBSTranslator translator;
  282. create_log_file(logFile);
  283. program.installTranslator(&translator);
  284. program.setStyle(new NoFocusFrameStyle);
  285. program.OBSInit();
  286. ret = program.exec();
  287. } catch (const char *error) {
  288. blog(LOG_ERROR, "%s", error);
  289. }
  290. return ret;
  291. }
  292. int main(int argc, char *argv[])
  293. {
  294. #ifndef WIN32
  295. signal(SIGPIPE, SIG_IGN);
  296. #endif
  297. base_get_log_handler(&def_log_handler, nullptr);
  298. fstream logFile;
  299. int ret = run_program(logFile, argc, argv);
  300. blog(LOG_INFO, "Number of memory leaks: %ld", bnum_allocs());
  301. base_set_log_handler(nullptr, nullptr);
  302. return ret;
  303. }