api-interface.cpp 20 KB

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