window-basic-main.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. /******************************************************************************
  2. Copyright (C) 2013 by Hugh Bailey <[email protected]>
  3. Copyright (C) 2014 by Zachary Lund <[email protected]>
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. ******************************************************************************/
  15. #include <obs.hpp>
  16. #include <wx/msgdlg.h>
  17. #include "obs-app.hpp"
  18. #include "wx-wrappers.hpp"
  19. #include "window-basic-settings.hpp"
  20. #include "window-basic-main.hpp"
  21. #include "window-namedialog.hpp"
  22. #ifdef __WXGTK__
  23. #include <gtk/gtk.h>
  24. #endif
  25. using namespace std;
  26. obs_scene_t OBSBasic::GetCurrentScene()
  27. {
  28. int sel = scenes->GetSelection();
  29. if (sel == wxNOT_FOUND)
  30. return NULL;
  31. return (obs_scene_t)scenes->GetClientData(sel);
  32. }
  33. void OBSBasic::AddScene(obs_source_t source)
  34. {
  35. const char *name = obs_source_getname(source);
  36. obs_scene_t scene = obs_scene_fromsource(source);
  37. scenes->Append(WX_UTF8(name), scene);
  38. signal_handler_t handler = obs_source_signalhandler(source);
  39. signal_handler_connect(handler, "add", OBSBasic::SceneItemAdded,
  40. this);
  41. signal_handler_connect(handler, "remove", OBSBasic::SceneItemRemoved,
  42. this);
  43. }
  44. void OBSBasic::RemoveScene(obs_source_t source)
  45. {
  46. const char *name = obs_source_getname(source);
  47. int idx = scenes->FindString(name);
  48. int sel = scenes->GetSelection();
  49. if (idx != wxNOT_FOUND) {
  50. if (sel == idx)
  51. sources->Clear();
  52. scenes->Delete(idx);
  53. }
  54. }
  55. void OBSBasic::AddSceneItem(obs_sceneitem_t item)
  56. {
  57. obs_scene_t scene = obs_sceneitem_getscene(item);
  58. obs_source_t source = obs_sceneitem_getsource(item);
  59. const char *name = obs_source_getname(source);
  60. if (GetCurrentScene() == scene)
  61. sources->Insert(WX_UTF8(name), 0, item);
  62. sourceSceneRefs[source] = sourceSceneRefs[source] + 1;
  63. }
  64. void OBSBasic::RemoveSceneItem(obs_sceneitem_t item)
  65. {
  66. obs_scene_t scene = obs_sceneitem_getscene(item);
  67. if (GetCurrentScene() == scene) {
  68. for (unsigned int idx = 0; idx < sources->GetCount(); idx++) {
  69. obs_sceneitem_t curItem;
  70. curItem = (obs_sceneitem_t)sources->GetClientData(idx);
  71. if (item == curItem) {
  72. sources->Delete(idx);
  73. break;
  74. }
  75. }
  76. }
  77. obs_source_t source = obs_sceneitem_getsource(item);
  78. obs_source_addref(source);
  79. obs_source_release(source);
  80. int scenes = sourceSceneRefs[source] - 1;
  81. if (scenes == 0) {
  82. obs_source_remove(source);
  83. sourceSceneRefs.erase(source);
  84. }
  85. }
  86. void OBSBasic::UpdateSources(obs_scene_t scene)
  87. {
  88. sources->Clear();
  89. obs_scene_enum_items(scene,
  90. [] (obs_scene_t scene, obs_sceneitem_t item, void *p)
  91. {
  92. OBSBasic *window = static_cast<OBSBasic*>(p);
  93. window->AddSceneItem(item);
  94. return true;
  95. }, this);
  96. }
  97. void OBSBasic::UpdateSceneSelection(obs_source_t source)
  98. {
  99. if (source) {
  100. obs_source_type type;
  101. obs_source_gettype(source, &type, NULL);
  102. if (type == SOURCE_SCENE) {
  103. obs_scene_t scene = obs_scene_fromsource(source);
  104. const char *name = obs_source_getname(source);
  105. int idx = scenes->FindString(WX_UTF8(name));
  106. int sel = scenes->GetSelection();
  107. if (idx != sel) {
  108. scenes->SetSelection(idx);
  109. UpdateSources(scene);
  110. }
  111. }
  112. } else {
  113. scenes->SetSelection(wxNOT_FOUND);
  114. }
  115. }
  116. /* OBS Callbacks */
  117. void OBSBasic::SceneItemAdded(void *data, calldata_t params)
  118. {
  119. OBSBasic *window = static_cast<OBSBasic*>(data);
  120. obs_scene_t scene = (obs_scene_t)calldata_ptr(params, "scene");
  121. obs_sceneitem_t item = (obs_sceneitem_t)calldata_ptr(params, "item");
  122. window->AddSceneItem(item);
  123. }
  124. void OBSBasic::SceneItemRemoved(void *data, calldata_t params)
  125. {
  126. OBSBasic *window = static_cast<OBSBasic*>(data);
  127. obs_scene_t scene = (obs_scene_t)calldata_ptr(params, "scene");
  128. obs_sceneitem_t item = (obs_sceneitem_t)calldata_ptr(params, "item");
  129. window->RemoveSceneItem(item);
  130. }
  131. void OBSBasic::SourceAdded(void *data, calldata_t params)
  132. {
  133. obs_source_t source = (obs_source_t)calldata_ptr(params, "source");
  134. obs_source_type type;
  135. obs_source_gettype(source, &type, NULL);
  136. if (type == SOURCE_SCENE)
  137. static_cast<OBSBasic*>(data)->AddScene(source);
  138. }
  139. void OBSBasic::SourceDestroyed(void *data, calldata_t params)
  140. {
  141. obs_source_t source = (obs_source_t)calldata_ptr(params, "source");
  142. obs_source_type type;
  143. obs_source_gettype(source, &type, NULL);
  144. if (type == SOURCE_SCENE)
  145. static_cast<OBSBasic*>(data)->RemoveScene(source);
  146. }
  147. void OBSBasic::ChannelChanged(void *data, calldata_t params)
  148. {
  149. obs_source_t source = (obs_source_t)calldata_ptr(params, "source");
  150. uint32_t channel = calldata_uint32(params, "channel");
  151. if (channel == 0)
  152. static_cast<OBSBasic*>(data)->UpdateSceneSelection(source);
  153. }
  154. /* Main class functions */
  155. bool OBSBasic::Init()
  156. {
  157. if (!obs_startup())
  158. return false;
  159. if (!InitGraphics())
  160. return false;
  161. if (!InitAudio())
  162. return false;
  163. signal_handler_connect(obs_signalhandler(), "source-add",
  164. OBSBasic::SourceAdded, this);
  165. signal_handler_connect(obs_signalhandler(), "source-destroy",
  166. OBSBasic::SourceDestroyed, this);
  167. signal_handler_connect(obs_signalhandler(), "channel-change",
  168. OBSBasic::ChannelChanged, this);
  169. /* TODO: this is a test */
  170. obs_load_module("test-input");
  171. return true;
  172. }
  173. OBSBasic::~OBSBasic()
  174. {
  175. obs_shutdown();
  176. }
  177. bool OBSBasic::InitGraphics()
  178. {
  179. struct obs_video_info ovi;
  180. wxGetApp().GetConfigFPS(ovi.fps_num, ovi.fps_den);
  181. ovi.graphics_module = wxGetApp().GetRenderModule();
  182. ovi.base_width = (uint32_t)config_get_uint(GetGlobalConfig(),
  183. "Video", "BaseCX");
  184. ovi.base_height = (uint32_t)config_get_uint(GetGlobalConfig(),
  185. "Video", "BaseCY");
  186. ovi.output_width = (uint32_t)config_get_uint(GetGlobalConfig(),
  187. "Video", "OutputCX");
  188. ovi.output_height = (uint32_t)config_get_uint(GetGlobalConfig(),
  189. "Video", "OutputCY");
  190. ovi.output_format = VIDEO_FORMAT_RGBA;
  191. ovi.adapter = 0;
  192. #ifdef __WXGTK__
  193. /* Ugly hack for GTK, I'm hoping this can be avoided eventually... */
  194. gtk_widget_realize(previewPanel->GetHandle());
  195. #endif
  196. ovi.window = WxToGSWindow(previewPanel);
  197. //required to make opengl display stuff on osx(?)
  198. ResizePreview(ovi.base_width, ovi.base_height);
  199. SendSizeEvent();
  200. wxSize size = previewPanel->GetClientSize();
  201. ovi.window_width = size.x;
  202. ovi.window_height = size.y;
  203. return obs_reset_video(&ovi);
  204. }
  205. bool OBSBasic::InitAudio()
  206. {
  207. /* TODO: load audio settings from config */
  208. struct audio_output_info ai;
  209. ai.name = "test";
  210. ai.samples_per_sec = 44100;
  211. ai.format = AUDIO_FORMAT_16BIT;
  212. ai.speakers = SPEAKERS_STEREO;
  213. ai.buffer_ms = 700;
  214. return obs_reset_audio(&ai);
  215. }
  216. void OBSBasic::OnClose(wxCloseEvent &event)
  217. {
  218. wxGetApp().ExitMainLoop();
  219. event.Skip();
  220. }
  221. void OBSBasic::OnMinimize(wxIconizeEvent &event)
  222. {
  223. event.Skip();
  224. }
  225. void OBSBasic::ResizePreview(uint32_t cx, uint32_t cy)
  226. {
  227. /* resize preview panel to fix to the top section of the window */
  228. wxSize targetSize = GetPreviewContainer()->GetSize();
  229. double targetAspect = double(targetSize.x) / double(targetSize.y);
  230. double baseAspect = double(cx) / double(cy);
  231. wxSize newSize;
  232. if (targetAspect > baseAspect)
  233. newSize = wxSize(targetSize.y * baseAspect, targetSize.y);
  234. else
  235. newSize = wxSize(targetSize.x, targetSize.x / baseAspect);
  236. GetPreviewPanel()->SetMinSize(newSize);
  237. }
  238. void OBSBasic::OnSize(wxSizeEvent &event)
  239. {
  240. struct obs_video_info ovi;
  241. event.Skip();
  242. if (!obs_get_video_info(&ovi))
  243. return;
  244. ResizePreview(ovi.base_width, ovi.base_height);
  245. }
  246. void OBSBasic::OnResizePreview(wxSizeEvent &event)
  247. {
  248. event.Skip();
  249. wxSize newSize = previewPanel->GetClientSize();
  250. graphics_t graphics = obs_graphics();
  251. if (graphics) {
  252. gs_entercontext(graphics);
  253. gs_resize(newSize.x, newSize.y);
  254. gs_leavecontext();
  255. }
  256. }
  257. void OBSBasic::fileNewClicked(wxCommandEvent &event)
  258. {
  259. }
  260. void OBSBasic::fileOpenClicked(wxCommandEvent &event)
  261. {
  262. }
  263. void OBSBasic::fileSaveClicked(wxCommandEvent &event)
  264. {
  265. }
  266. void OBSBasic::fileExitClicked(wxCommandEvent &event)
  267. {
  268. wxGetApp().ExitMainLoop();
  269. }
  270. void OBSBasic::scenesClicked(wxCommandEvent &event)
  271. {
  272. int sel = scenes->GetSelection();
  273. obs_source_t source = NULL;
  274. if (sel != wxNOT_FOUND) {
  275. obs_scene_t scene = (obs_scene_t)scenes->GetClientData(sel);
  276. source = obs_scene_getsource(scene);
  277. UpdateSources(scene);
  278. }
  279. /* TODO: allow transitions */
  280. obs_set_output_source(0, source);
  281. }
  282. void OBSBasic::scenesRDown(wxMouseEvent &event)
  283. {
  284. }
  285. void OBSBasic::sceneAddClicked(wxCommandEvent &event)
  286. {
  287. string name;
  288. int ret = NameDialog::AskForName(this,
  289. Str("MainWindow.AddSceneDlg.Title"),
  290. Str("MainWindow.AddSceneDlg.Text"),
  291. name);
  292. if (ret == wxID_OK) {
  293. obs_source_t source = obs_get_source_by_name(name.c_str());
  294. if (source) {
  295. wxMessageBox(WXStr("MainWindow.NameExists.Text"),
  296. WXStr("MainWindow.NameExists.Title"),
  297. wxOK|wxCENTRE, this);
  298. obs_source_release(source);
  299. sceneAddClicked(event);
  300. return;
  301. }
  302. obs_scene_t scene = obs_scene_create(name.c_str());
  303. source = obs_scene_getsource(scene);
  304. obs_add_source(source);
  305. obs_scene_release(scene);
  306. obs_set_output_source(0, source);
  307. }
  308. }
  309. void OBSBasic::sceneRemoveClicked(wxCommandEvent &event)
  310. {
  311. int sel = scenes->GetSelection();
  312. if (sel == wxNOT_FOUND)
  313. return;
  314. obs_scene_t scene = (obs_scene_t)scenes->GetClientData(sel);
  315. obs_source_t source = obs_scene_getsource(scene);
  316. obs_source_remove(source);
  317. }
  318. void OBSBasic::scenePropertiesClicked(wxCommandEvent &event)
  319. {
  320. }
  321. void OBSBasic::sceneUpClicked(wxCommandEvent &event)
  322. {
  323. }
  324. void OBSBasic::sceneDownClicked(wxCommandEvent &event)
  325. {
  326. }
  327. void OBSBasic::sourcesClicked(wxCommandEvent &event)
  328. {
  329. }
  330. void OBSBasic::sourcesToggled(wxCommandEvent &event)
  331. {
  332. }
  333. void OBSBasic::sourcesRDown(wxMouseEvent &event)
  334. {
  335. }
  336. void OBSBasic::AddSource(obs_scene_t scene, const char *id)
  337. {
  338. string name;
  339. bool success = false;
  340. while (!success) {
  341. int ret = NameDialog::AskForName(this,
  342. Str("MainWindow.AddSourceDlg.Title"),
  343. Str("MainWindow.AddSourceDlg.Text"),
  344. name);
  345. if (ret == wxID_CANCEL)
  346. break;
  347. obs_source_t source = obs_get_source_by_name(
  348. name.c_str());
  349. if (!source) {
  350. success = true;
  351. } else {
  352. wxMessageBox(WXStr("MainWindow.NameExists.Text"),
  353. WXStr("MainWindow.NameExists.Title"),
  354. wxOK|wxCENTRE, this);
  355. obs_source_release(source);
  356. }
  357. }
  358. if (success) {
  359. obs_source_t source = obs_source_create(SOURCE_INPUT, id,
  360. name.c_str(), NULL);
  361. sourceSceneRefs[source] = 0;
  362. obs_add_source(source);
  363. obs_sceneitem_t item = obs_scene_add(scene, source);
  364. obs_source_release(source);
  365. }
  366. }
  367. void OBSBasic::AddSourcePopupMenu()
  368. {
  369. int sceneSel = scenes->GetSelection();
  370. size_t idx = 0;
  371. const char *type;
  372. vector<const char *> types;
  373. if (sceneSel == wxNOT_FOUND)
  374. return;
  375. obs_scene_t scene = (obs_scene_t)scenes->GetClientData(sceneSel);
  376. obs_scene_addref(scene);
  377. unique_ptr<wxMenu> popup(new wxMenu());
  378. while (obs_enum_input_types(idx, &type)) {
  379. const char *name = obs_source_getdisplayname(SOURCE_INPUT,
  380. type, wxGetApp().GetLocale());
  381. types.push_back(type);
  382. popup->Append((int)idx+1, wxString(name, wxConvUTF8));
  383. idx++;
  384. }
  385. if (idx) {
  386. int id = WXDoPopupMenu(this, popup.get());
  387. if (id != -1)
  388. AddSource(scene, types[id-1]);
  389. }
  390. obs_scene_release(scene);
  391. }
  392. void OBSBasic::sourceAddClicked(wxCommandEvent &event)
  393. {
  394. AddSourcePopupMenu();
  395. }
  396. void OBSBasic::sourceRemoveClicked(wxCommandEvent &event)
  397. {
  398. int sel = sources->GetSelection();
  399. if (sel == wxNOT_FOUND)
  400. return;
  401. obs_sceneitem_t item = (obs_sceneitem_t)sources->GetClientData(sel);
  402. obs_source_t source = obs_sceneitem_getsource(item);
  403. obs_sceneitem_destroy(item);
  404. }
  405. void OBSBasic::sourcePropertiesClicked(wxCommandEvent &event)
  406. {
  407. }
  408. void OBSBasic::sourceUpClicked(wxCommandEvent &event)
  409. {
  410. }
  411. void OBSBasic::sourceDownClicked(wxCommandEvent &event)
  412. {
  413. }
  414. void OBSBasic::settingsClicked(wxCommandEvent &event)
  415. {
  416. OBSBasicSettings test(this);
  417. test.ShowModal();
  418. }
  419. void OBSBasic::exitClicked(wxCommandEvent &event)
  420. {
  421. wxGetApp().ExitMainLoop();
  422. }