OBSStudioAPI.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756
  1. #include "OBSStudioAPI.hpp"
  2. #include <models/SceneCollection.hpp>
  3. #include <widgets/OBSBasic.hpp>
  4. #include <widgets/OBSProjector.hpp>
  5. #include <qt-wrappers.hpp>
  6. extern volatile bool streaming_active;
  7. extern volatile bool recording_active;
  8. extern volatile bool recording_paused;
  9. extern volatile bool replaybuf_active;
  10. extern volatile bool virtualcam_active;
  11. template<typename T>
  12. inline size_t GetCallbackIdx(vector<OBSStudioCallback<T>> &callbacks, T callback, void *private_data)
  13. {
  14. for (size_t i = 0; i < callbacks.size(); i++) {
  15. OBSStudioCallback<T> curCB = callbacks[i];
  16. if (curCB.callback == callback && curCB.private_data == private_data)
  17. return i;
  18. }
  19. return (size_t)-1;
  20. }
  21. void *OBSStudioAPI::obs_frontend_get_main_window()
  22. {
  23. return (void *)main;
  24. }
  25. void *OBSStudioAPI::obs_frontend_get_main_window_handle()
  26. {
  27. return (void *)main->winId();
  28. }
  29. void *OBSStudioAPI::obs_frontend_get_system_tray()
  30. {
  31. return (void *)main->trayIcon.data();
  32. }
  33. void OBSStudioAPI::obs_frontend_get_scenes(struct obs_frontend_source_list *sources)
  34. {
  35. for (int i = 0; i < main->ui->scenes->count(); i++) {
  36. QListWidgetItem *item = main->ui->scenes->item(i);
  37. OBSScene scene = GetOBSRef<OBSScene>(item);
  38. obs_source_t *source = obs_scene_get_source(scene);
  39. if (obs_source_get_ref(source) != nullptr)
  40. da_push_back(sources->sources, &source);
  41. }
  42. }
  43. obs_source_t *OBSStudioAPI::obs_frontend_get_current_scene()
  44. {
  45. if (main->IsPreviewProgramMode()) {
  46. return obs_weak_source_get_source(main->programScene);
  47. } else {
  48. OBSSource source = main->GetCurrentSceneSource();
  49. return obs_source_get_ref(source);
  50. }
  51. }
  52. void OBSStudioAPI::obs_frontend_set_current_scene(obs_source_t *scene)
  53. {
  54. if (main->IsPreviewProgramMode()) {
  55. QMetaObject::invokeMethod(main, "TransitionToScene", WaitConnection(),
  56. Q_ARG(OBSSource, OBSSource(scene)));
  57. } else {
  58. QMetaObject::invokeMethod(main, "SetCurrentScene", WaitConnection(), Q_ARG(OBSSource, OBSSource(scene)),
  59. Q_ARG(bool, false));
  60. }
  61. }
  62. void OBSStudioAPI::obs_frontend_get_transitions(struct obs_frontend_source_list *sources)
  63. {
  64. for (int i = 0; i < main->ui->transitions->count(); i++) {
  65. OBSSource tr = main->ui->transitions->itemData(i).value<OBSSource>();
  66. if (!tr)
  67. continue;
  68. if (obs_source_get_ref(tr) != nullptr)
  69. da_push_back(sources->sources, &tr);
  70. }
  71. }
  72. obs_source_t *OBSStudioAPI::obs_frontend_get_current_transition()
  73. {
  74. OBSSource tr = main->GetCurrentTransition();
  75. return obs_source_get_ref(tr);
  76. }
  77. void OBSStudioAPI::obs_frontend_set_current_transition(obs_source_t *transition)
  78. {
  79. QMetaObject::invokeMethod(main, "SetTransition", Q_ARG(OBSSource, OBSSource(transition)));
  80. }
  81. int OBSStudioAPI::obs_frontend_get_transition_duration()
  82. {
  83. return main->ui->transitionDuration->value();
  84. }
  85. void OBSStudioAPI::obs_frontend_set_transition_duration(int duration)
  86. {
  87. QMetaObject::invokeMethod(main->ui->transitionDuration, "setValue", Q_ARG(int, duration));
  88. }
  89. void OBSStudioAPI::obs_frontend_release_tbar()
  90. {
  91. QMetaObject::invokeMethod(main, "TBarReleased");
  92. }
  93. void OBSStudioAPI::obs_frontend_set_tbar_position(int position)
  94. {
  95. QMetaObject::invokeMethod(main, "TBarChanged", Q_ARG(int, position));
  96. }
  97. int OBSStudioAPI::obs_frontend_get_tbar_position()
  98. {
  99. return main->tBar->value();
  100. }
  101. void OBSStudioAPI::obs_frontend_get_scene_collections(std::vector<std::string> &strings)
  102. {
  103. for (auto &[collectionName, collection] : main->GetSceneCollectionCache()) {
  104. strings.emplace_back(collectionName);
  105. }
  106. }
  107. char *OBSStudioAPI::obs_frontend_get_current_scene_collection()
  108. {
  109. try {
  110. const OBS::SceneCollection &currentCollection = main->GetCurrentSceneCollection();
  111. return bstrdup(currentCollection.getName().c_str());
  112. } catch (const std::exception &error) {
  113. blog(LOG_DEBUG, "%s", error.what());
  114. blog(LOG_ERROR, "Failed to get current scene collection name");
  115. return nullptr;
  116. }
  117. }
  118. void OBSStudioAPI::obs_frontend_set_current_scene_collection(const char *collection)
  119. {
  120. QList<QAction *> menuActions = main->ui->sceneCollectionMenu->actions();
  121. QString qstrCollection = QT_UTF8(collection);
  122. for (int i = 0; i < menuActions.count(); i++) {
  123. QAction *action = menuActions[i];
  124. QVariant v = action->property("file_name");
  125. if (v.typeName() != nullptr) {
  126. if (action->text() == qstrCollection) {
  127. action->trigger();
  128. break;
  129. }
  130. }
  131. }
  132. }
  133. bool OBSStudioAPI::obs_frontend_add_scene_collection(const char *name)
  134. {
  135. bool success = false;
  136. QMetaObject::invokeMethod(main, "CreateNewSceneCollection", WaitConnection(), Q_RETURN_ARG(bool, success),
  137. Q_ARG(QString, QT_UTF8(name)));
  138. return success;
  139. }
  140. void OBSStudioAPI::obs_frontend_get_profiles(std::vector<std::string> &strings)
  141. {
  142. const OBSProfileCache &profiles = main->GetProfileCache();
  143. for (auto &[profileName, profile] : profiles) {
  144. strings.emplace_back(profileName);
  145. }
  146. }
  147. char *OBSStudioAPI::obs_frontend_get_current_profile()
  148. {
  149. const OBSProfile &profile = main->GetCurrentProfile();
  150. return bstrdup(profile.name.c_str());
  151. }
  152. char *OBSStudioAPI::obs_frontend_get_current_profile_path()
  153. {
  154. const OBSProfile &profile = main->GetCurrentProfile();
  155. return bstrdup(profile.path.u8string().c_str());
  156. }
  157. void OBSStudioAPI::obs_frontend_set_current_profile(const char *profile)
  158. {
  159. QList<QAction *> menuActions = main->ui->profileMenu->actions();
  160. QString qstrProfile = QT_UTF8(profile);
  161. for (int i = 0; i < menuActions.count(); i++) {
  162. QAction *action = menuActions[i];
  163. QVariant v = action->property("file_name");
  164. if (v.typeName() != nullptr) {
  165. if (action->text() == qstrProfile) {
  166. action->trigger();
  167. break;
  168. }
  169. }
  170. }
  171. }
  172. void OBSStudioAPI::obs_frontend_create_profile(const char *name)
  173. {
  174. QMetaObject::invokeMethod(main, "CreateNewProfile", Q_ARG(QString, name));
  175. }
  176. void OBSStudioAPI::obs_frontend_duplicate_profile(const char *name)
  177. {
  178. QMetaObject::invokeMethod(main, "CreateDuplicateProfile", Q_ARG(QString, name));
  179. }
  180. void OBSStudioAPI::obs_frontend_delete_profile(const char *profile)
  181. {
  182. QMetaObject::invokeMethod(main, "DeleteProfile", Q_ARG(QString, profile));
  183. }
  184. void OBSStudioAPI::obs_frontend_streaming_start()
  185. {
  186. QMetaObject::invokeMethod(main, "StartStreaming");
  187. }
  188. void OBSStudioAPI::obs_frontend_streaming_stop()
  189. {
  190. QMetaObject::invokeMethod(main, "StopStreaming");
  191. }
  192. bool OBSStudioAPI::obs_frontend_streaming_active()
  193. {
  194. return os_atomic_load_bool(&streaming_active);
  195. }
  196. void OBSStudioAPI::obs_frontend_recording_start()
  197. {
  198. QMetaObject::invokeMethod(main, "StartRecording");
  199. }
  200. void OBSStudioAPI::obs_frontend_recording_stop()
  201. {
  202. QMetaObject::invokeMethod(main, "StopRecording");
  203. }
  204. bool OBSStudioAPI::obs_frontend_recording_active()
  205. {
  206. return os_atomic_load_bool(&recording_active);
  207. }
  208. void OBSStudioAPI::obs_frontend_recording_pause(bool pause)
  209. {
  210. QMetaObject::invokeMethod(main, pause ? "PauseRecording" : "UnpauseRecording");
  211. }
  212. bool OBSStudioAPI::obs_frontend_recording_paused()
  213. {
  214. return os_atomic_load_bool(&recording_paused);
  215. }
  216. bool OBSStudioAPI::obs_frontend_recording_split_file()
  217. {
  218. if (os_atomic_load_bool(&recording_active) && !os_atomic_load_bool(&recording_paused)) {
  219. proc_handler_t *ph = obs_output_get_proc_handler(main->outputHandler->fileOutput);
  220. uint8_t stack[128];
  221. calldata cd;
  222. calldata_init_fixed(&cd, stack, sizeof(stack));
  223. proc_handler_call(ph, "split_file", &cd);
  224. bool result = calldata_bool(&cd, "split_file_enabled");
  225. return result;
  226. } else {
  227. return false;
  228. }
  229. }
  230. bool OBSStudioAPI::obs_frontend_recording_add_chapter(const char *name)
  231. {
  232. if (!os_atomic_load_bool(&recording_active) || os_atomic_load_bool(&recording_paused))
  233. return false;
  234. proc_handler_t *ph = obs_output_get_proc_handler(main->outputHandler->fileOutput);
  235. calldata cd;
  236. calldata_init(&cd);
  237. calldata_set_string(&cd, "chapter_name", name);
  238. bool result = proc_handler_call(ph, "add_chapter", &cd);
  239. calldata_free(&cd);
  240. return result;
  241. }
  242. void OBSStudioAPI::obs_frontend_replay_buffer_start()
  243. {
  244. QMetaObject::invokeMethod(main, "StartReplayBuffer");
  245. }
  246. void OBSStudioAPI::obs_frontend_replay_buffer_save()
  247. {
  248. QMetaObject::invokeMethod(main, "ReplayBufferSave");
  249. }
  250. void OBSStudioAPI::obs_frontend_replay_buffer_stop()
  251. {
  252. QMetaObject::invokeMethod(main, "StopReplayBuffer");
  253. }
  254. bool OBSStudioAPI::obs_frontend_replay_buffer_active()
  255. {
  256. return os_atomic_load_bool(&replaybuf_active);
  257. }
  258. void *OBSStudioAPI::obs_frontend_add_tools_menu_qaction(const char *name)
  259. {
  260. main->ui->menuTools->setEnabled(true);
  261. QAction *action = main->ui->menuTools->addAction(QT_UTF8(name));
  262. action->setMenuRole(QAction::NoRole);
  263. return static_cast<void *>(action);
  264. }
  265. void OBSStudioAPI::obs_frontend_add_tools_menu_item(const char *name, obs_frontend_cb callback, void *private_data)
  266. {
  267. main->ui->menuTools->setEnabled(true);
  268. auto func = [private_data, callback]() {
  269. callback(private_data);
  270. };
  271. QAction *action = main->ui->menuTools->addAction(QT_UTF8(name));
  272. action->setMenuRole(QAction::NoRole);
  273. QObject::connect(action, &QAction::triggered, func);
  274. }
  275. void *OBSStudioAPI::obs_frontend_add_dock(void *dock)
  276. {
  277. QDockWidget *d = static_cast<QDockWidget *>(dock);
  278. QString name = d->objectName();
  279. if (name.isEmpty() || main->IsDockObjectNameUsed(name)) {
  280. blog(LOG_WARNING, "The object name of the added dock is empty or already used,"
  281. " a temporary one will be set to avoid conflicts");
  282. char *uuid = os_generate_uuid();
  283. name = QT_UTF8(uuid);
  284. bfree(uuid);
  285. name.append("_oldExtraDock");
  286. d->setObjectName(name);
  287. }
  288. return (void *)main->AddDockWidget(d);
  289. }
  290. bool OBSStudioAPI::obs_frontend_add_dock_by_id(const char *id, const char *title, void *widget)
  291. {
  292. if (main->IsDockObjectNameUsed(QT_UTF8(id))) {
  293. blog(LOG_WARNING,
  294. "Dock id '%s' already used! "
  295. "Duplicate library?",
  296. id);
  297. return false;
  298. }
  299. OBSDock *dock = new OBSDock(main);
  300. dock->setWidget((QWidget *)widget);
  301. dock->setWindowTitle(QT_UTF8(title));
  302. dock->setObjectName(QT_UTF8(id));
  303. main->AddDockWidget(dock, Qt::RightDockWidgetArea);
  304. dock->setVisible(false);
  305. dock->setFloating(true);
  306. return true;
  307. }
  308. void OBSStudioAPI::obs_frontend_remove_dock(const char *id)
  309. {
  310. main->RemoveDockWidget(QT_UTF8(id));
  311. }
  312. bool OBSStudioAPI::obs_frontend_add_custom_qdock(const char *id, void *dock)
  313. {
  314. if (main->IsDockObjectNameUsed(QT_UTF8(id))) {
  315. blog(LOG_WARNING,
  316. "Dock id '%s' already used! "
  317. "Duplicate library?",
  318. id);
  319. return false;
  320. }
  321. QDockWidget *d = static_cast<QDockWidget *>(dock);
  322. d->setObjectName(QT_UTF8(id));
  323. main->AddCustomDockWidget(d);
  324. return true;
  325. }
  326. void OBSStudioAPI::obs_frontend_add_event_callback(obs_frontend_event_cb callback, void *private_data)
  327. {
  328. size_t idx = GetCallbackIdx(callbacks, callback, private_data);
  329. if (idx == (size_t)-1)
  330. callbacks.emplace_back(callback, private_data);
  331. }
  332. void OBSStudioAPI::obs_frontend_remove_event_callback(obs_frontend_event_cb callback, void *private_data)
  333. {
  334. size_t idx = GetCallbackIdx(callbacks, callback, private_data);
  335. if (idx == (size_t)-1)
  336. return;
  337. callbacks.erase(callbacks.begin() + idx);
  338. }
  339. obs_output_t *OBSStudioAPI::obs_frontend_get_streaming_output()
  340. {
  341. auto multitrackVideo = main->outputHandler->multitrackVideo.get();
  342. auto mtvOutput = multitrackVideo ? obs_output_get_ref(multitrackVideo->StreamingOutput()) : nullptr;
  343. if (mtvOutput)
  344. return mtvOutput;
  345. OBSOutput output = main->outputHandler->streamOutput.Get();
  346. return obs_output_get_ref(output);
  347. }
  348. obs_output_t *OBSStudioAPI::obs_frontend_get_recording_output()
  349. {
  350. OBSOutput out = main->outputHandler->fileOutput.Get();
  351. return obs_output_get_ref(out);
  352. }
  353. obs_output_t *OBSStudioAPI::obs_frontend_get_replay_buffer_output()
  354. {
  355. OBSOutput out = main->outputHandler->replayBuffer.Get();
  356. return obs_output_get_ref(out);
  357. }
  358. config_t *OBSStudioAPI::obs_frontend_get_profile_config()
  359. {
  360. return main->activeConfiguration;
  361. }
  362. config_t *OBSStudioAPI::obs_frontend_get_global_config()
  363. {
  364. blog(LOG_WARNING,
  365. "DEPRECATION: obs_frontend_get_global_config is deprecated. Read from global or user configuration explicitly instead.");
  366. return App()->GetAppConfig();
  367. }
  368. config_t *OBSStudioAPI::obs_frontend_get_app_config()
  369. {
  370. return App()->GetAppConfig();
  371. }
  372. config_t *OBSStudioAPI::obs_frontend_get_user_config()
  373. {
  374. return App()->GetUserConfig();
  375. }
  376. void OBSStudioAPI::obs_frontend_open_projector(const char *type, int monitor, const char *geometry, const char *name)
  377. {
  378. SavedProjectorInfo proj = {
  379. ProjectorType::Preview,
  380. monitor,
  381. geometry ? geometry : "",
  382. name ? name : "",
  383. };
  384. if (type) {
  385. if (astrcmpi(type, "Source") == 0)
  386. proj.type = ProjectorType::Source;
  387. else if (astrcmpi(type, "Scene") == 0)
  388. proj.type = ProjectorType::Scene;
  389. else if (astrcmpi(type, "StudioProgram") == 0)
  390. proj.type = ProjectorType::StudioProgram;
  391. else if (astrcmpi(type, "Multiview") == 0)
  392. proj.type = ProjectorType::Multiview;
  393. }
  394. QMetaObject::invokeMethod(main, "OpenSavedProjector", WaitConnection(), Q_ARG(SavedProjectorInfo *, &proj));
  395. }
  396. void OBSStudioAPI::obs_frontend_save()
  397. {
  398. main->SaveProject();
  399. }
  400. void OBSStudioAPI::obs_frontend_defer_save_begin()
  401. {
  402. QMetaObject::invokeMethod(main, "DeferSaveBegin");
  403. }
  404. void OBSStudioAPI::obs_frontend_defer_save_end()
  405. {
  406. QMetaObject::invokeMethod(main, "DeferSaveEnd");
  407. }
  408. void OBSStudioAPI::obs_frontend_add_save_callback(obs_frontend_save_cb callback, void *private_data)
  409. {
  410. size_t idx = GetCallbackIdx(saveCallbacks, callback, private_data);
  411. if (idx == (size_t)-1)
  412. saveCallbacks.emplace_back(callback, private_data);
  413. }
  414. void OBSStudioAPI::obs_frontend_remove_save_callback(obs_frontend_save_cb callback, void *private_data)
  415. {
  416. size_t idx = GetCallbackIdx(saveCallbacks, callback, private_data);
  417. if (idx == (size_t)-1)
  418. return;
  419. saveCallbacks.erase(saveCallbacks.begin() + idx);
  420. }
  421. void OBSStudioAPI::obs_frontend_add_preload_callback(obs_frontend_save_cb callback, void *private_data)
  422. {
  423. size_t idx = GetCallbackIdx(preloadCallbacks, callback, private_data);
  424. if (idx == (size_t)-1)
  425. preloadCallbacks.emplace_back(callback, private_data);
  426. }
  427. void OBSStudioAPI::obs_frontend_remove_preload_callback(obs_frontend_save_cb callback, void *private_data)
  428. {
  429. size_t idx = GetCallbackIdx(preloadCallbacks, callback, private_data);
  430. if (idx == (size_t)-1)
  431. return;
  432. preloadCallbacks.erase(preloadCallbacks.begin() + idx);
  433. }
  434. void OBSStudioAPI::obs_frontend_push_ui_translation(obs_frontend_translate_ui_cb translate)
  435. {
  436. App()->PushUITranslation(translate);
  437. }
  438. void OBSStudioAPI::obs_frontend_pop_ui_translation()
  439. {
  440. App()->PopUITranslation();
  441. }
  442. void OBSStudioAPI::obs_frontend_set_streaming_service(obs_service_t *service)
  443. {
  444. main->SetService(service);
  445. }
  446. obs_service_t *OBSStudioAPI::obs_frontend_get_streaming_service()
  447. {
  448. return main->GetService();
  449. }
  450. void OBSStudioAPI::obs_frontend_save_streaming_service()
  451. {
  452. main->SaveService();
  453. }
  454. bool OBSStudioAPI::obs_frontend_preview_program_mode_active()
  455. {
  456. return main->IsPreviewProgramMode();
  457. }
  458. void OBSStudioAPI::obs_frontend_set_preview_program_mode(bool enable)
  459. {
  460. main->SetPreviewProgramMode(enable);
  461. }
  462. void OBSStudioAPI::obs_frontend_preview_program_trigger_transition()
  463. {
  464. QMetaObject::invokeMethod(main, "TransitionClicked");
  465. }
  466. bool OBSStudioAPI::obs_frontend_preview_enabled()
  467. {
  468. return main->previewEnabled;
  469. }
  470. void OBSStudioAPI::obs_frontend_set_preview_enabled(bool enable)
  471. {
  472. if (main->previewEnabled != enable)
  473. main->EnablePreviewDisplay(enable);
  474. }
  475. obs_source_t *OBSStudioAPI::obs_frontend_get_current_preview_scene()
  476. {
  477. if (main->IsPreviewProgramMode()) {
  478. OBSSource source = main->GetCurrentSceneSource();
  479. return obs_source_get_ref(source);
  480. }
  481. return nullptr;
  482. }
  483. void OBSStudioAPI::obs_frontend_set_current_preview_scene(obs_source_t *scene)
  484. {
  485. if (main->IsPreviewProgramMode()) {
  486. QMetaObject::invokeMethod(main, "SetCurrentScene", Q_ARG(OBSSource, OBSSource(scene)),
  487. Q_ARG(bool, false));
  488. }
  489. }
  490. void OBSStudioAPI::obs_frontend_take_screenshot()
  491. {
  492. QMetaObject::invokeMethod(main, "Screenshot");
  493. }
  494. void OBSStudioAPI::obs_frontend_take_source_screenshot(obs_source_t *source)
  495. {
  496. QMetaObject::invokeMethod(main, "Screenshot", Q_ARG(OBSSource, OBSSource(source)));
  497. }
  498. obs_output_t *OBSStudioAPI::obs_frontend_get_virtualcam_output()
  499. {
  500. OBSOutput output = main->outputHandler->virtualCam.Get();
  501. return obs_output_get_ref(output);
  502. }
  503. void OBSStudioAPI::obs_frontend_start_virtualcam()
  504. {
  505. QMetaObject::invokeMethod(main, "StartVirtualCam");
  506. }
  507. void OBSStudioAPI::obs_frontend_stop_virtualcam()
  508. {
  509. QMetaObject::invokeMethod(main, "StopVirtualCam");
  510. }
  511. bool OBSStudioAPI::obs_frontend_virtualcam_active()
  512. {
  513. return os_atomic_load_bool(&virtualcam_active);
  514. }
  515. void OBSStudioAPI::obs_frontend_reset_video()
  516. {
  517. main->ResetVideo();
  518. }
  519. void OBSStudioAPI::obs_frontend_open_source_properties(obs_source_t *source)
  520. {
  521. QMetaObject::invokeMethod(main, "OpenProperties", Q_ARG(OBSSource, OBSSource(source)));
  522. }
  523. void OBSStudioAPI::obs_frontend_open_source_filters(obs_source_t *source)
  524. {
  525. QMetaObject::invokeMethod(main, "OpenFilters", Q_ARG(OBSSource, OBSSource(source)));
  526. }
  527. void OBSStudioAPI::obs_frontend_open_source_interaction(obs_source_t *source)
  528. {
  529. QMetaObject::invokeMethod(main, "OpenInteraction", Q_ARG(OBSSource, OBSSource(source)));
  530. }
  531. void OBSStudioAPI::obs_frontend_open_sceneitem_edit_transform(obs_sceneitem_t *item)
  532. {
  533. QMetaObject::invokeMethod(main, "OpenEditTransform", Q_ARG(OBSSceneItem, OBSSceneItem(item)));
  534. }
  535. char *OBSStudioAPI::obs_frontend_get_current_record_output_path()
  536. {
  537. const char *recordOutputPath = main->GetCurrentOutputPath();
  538. return bstrdup(recordOutputPath);
  539. }
  540. const char *OBSStudioAPI::obs_frontend_get_locale_string(const char *string)
  541. {
  542. return Str(string);
  543. }
  544. bool OBSStudioAPI::obs_frontend_is_theme_dark()
  545. {
  546. return App()->IsThemeDark();
  547. }
  548. char *OBSStudioAPI::obs_frontend_get_last_recording()
  549. {
  550. return bstrdup(main->outputHandler->lastRecordingPath.c_str());
  551. }
  552. char *OBSStudioAPI::obs_frontend_get_last_screenshot()
  553. {
  554. return bstrdup(main->lastScreenshot.c_str());
  555. }
  556. char *OBSStudioAPI::obs_frontend_get_last_replay()
  557. {
  558. return bstrdup(main->lastReplay.c_str());
  559. }
  560. void OBSStudioAPI::obs_frontend_add_undo_redo_action(const char *name, const undo_redo_cb undo, const undo_redo_cb redo,
  561. const char *undo_data, const char *redo_data, bool repeatable)
  562. {
  563. main->undo_s.add_action(
  564. name, [undo](const std::string &data) { undo(data.c_str()); },
  565. [redo](const std::string &data) { redo(data.c_str()); }, undo_data, redo_data, repeatable);
  566. }
  567. void OBSStudioAPI::obs_frontend_get_canvases(obs_frontend_canvas_list *canvas_list)
  568. {
  569. for (const auto &canvas : main->canvases) {
  570. obs_canvas_t *ref = obs_canvas_get_ref(canvas);
  571. if (ref)
  572. da_push_back(canvas_list->canvases, &ref);
  573. }
  574. }
  575. obs_canvas_t *OBSStudioAPI::obs_frontend_add_canvas(const char *name, obs_video_info *ovi, int flags)
  576. {
  577. auto &canvas = main->AddCanvas(std::string(name), ovi, flags);
  578. return obs_canvas_get_ref(canvas);
  579. }
  580. bool OBSStudioAPI::obs_frontend_remove_canvas(obs_canvas_t *canvas)
  581. {
  582. return main->RemoveCanvas(canvas);
  583. }
  584. void OBSStudioAPI::on_load(obs_data_t *settings)
  585. {
  586. for (size_t i = saveCallbacks.size(); i > 0; i--) {
  587. auto cb = saveCallbacks[i - 1];
  588. cb.callback(settings, false, cb.private_data);
  589. }
  590. }
  591. void OBSStudioAPI::on_preload(obs_data_t *settings)
  592. {
  593. for (size_t i = preloadCallbacks.size(); i > 0; i--) {
  594. auto cb = preloadCallbacks[i - 1];
  595. cb.callback(settings, false, cb.private_data);
  596. }
  597. }
  598. void OBSStudioAPI::on_save(obs_data_t *settings)
  599. {
  600. for (size_t i = saveCallbacks.size(); i > 0; i--) {
  601. auto cb = saveCallbacks[i - 1];
  602. cb.callback(settings, true, cb.private_data);
  603. }
  604. }
  605. void OBSStudioAPI::on_event(enum obs_frontend_event event)
  606. {
  607. if (main->disableSaving && event != OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP &&
  608. event != OBS_FRONTEND_EVENT_EXIT)
  609. return;
  610. for (size_t i = callbacks.size(); i > 0; i--) {
  611. auto cb = callbacks[i - 1];
  612. cb.callback(event, cb.private_data);
  613. }
  614. }
  615. obs_frontend_callbacks *InitializeAPIInterface(OBSBasic *main)
  616. {
  617. obs_frontend_callbacks *api = new OBSStudioAPI(main);
  618. obs_frontend_set_callbacks_internal(api);
  619. return api;
  620. }