window-projector.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887
  1. #include <QAction>
  2. #include <QGuiApplication>
  3. #include <QMouseEvent>
  4. #include <QMenu>
  5. #include <QScreen>
  6. #include "window-basic-main.hpp"
  7. #include "display-helpers.hpp"
  8. #include "qt-wrappers.hpp"
  9. #include "platform.hpp"
  10. #define HORIZONTAL_TOP 0
  11. #define HORIZONTAL_BOTTOM 1
  12. #define VERTICAL_LEFT 2
  13. #define VERTICAL_RIGHT 3
  14. static QList<OBSProjector *> multiviewProjectors;
  15. static bool updatingMultiview = false;
  16. static int multiviewLayout = HORIZONTAL_TOP;
  17. OBSProjector::OBSProjector(QWidget *widget, obs_source_t *source_, bool window)
  18. : OBSQTDisplay (widget,
  19. Qt::Window),
  20. source (source_),
  21. removedSignal (obs_source_get_signal_handler(source),
  22. "remove", OBSSourceRemoved, this)
  23. {
  24. if (!window) {
  25. setWindowFlags(Qt::FramelessWindowHint |
  26. Qt::X11BypassWindowManagerHint);
  27. }
  28. setAttribute(Qt::WA_DeleteOnClose, true);
  29. //disable application quit when last window closed
  30. setAttribute(Qt::WA_QuitOnClose, false);
  31. installEventFilter(CreateShortcutFilter());
  32. auto addDrawCallback = [this] ()
  33. {
  34. bool isMultiview = type == ProjectorType::Multiview;
  35. obs_display_add_draw_callback(GetDisplay(),
  36. isMultiview ? OBSRenderMultiview : OBSRender,
  37. this);
  38. obs_display_set_background_color(GetDisplay(), 0x000000);
  39. };
  40. connect(this, &OBSQTDisplay::DisplayCreated, addDrawCallback);
  41. bool hideCursor = config_get_bool(GetGlobalConfig(),
  42. "BasicWindow", "HideProjectorCursor");
  43. if (hideCursor && !window) {
  44. QPixmap empty(16, 16);
  45. empty.fill(Qt::transparent);
  46. setCursor(QCursor(empty));
  47. }
  48. App()->IncrementSleepInhibition();
  49. resize(480, 270);
  50. }
  51. OBSProjector::~OBSProjector()
  52. {
  53. bool isMultiview = type == ProjectorType::Multiview;
  54. obs_display_remove_draw_callback(GetDisplay(),
  55. isMultiview ? OBSRenderMultiview : OBSRender, this);
  56. if (source)
  57. obs_source_dec_showing(source);
  58. if (isMultiview) {
  59. for (OBSWeakSource &weakSrc : multiviewScenes) {
  60. OBSSource src = OBSGetStrongRef(weakSrc);
  61. if (src)
  62. obs_source_dec_showing(src);
  63. }
  64. obs_enter_graphics();
  65. gs_vertexbuffer_destroy(outerBox);
  66. gs_vertexbuffer_destroy(innerBox);
  67. gs_vertexbuffer_destroy(leftVLine);
  68. gs_vertexbuffer_destroy(rightVLine);
  69. gs_vertexbuffer_destroy(leftLine);
  70. gs_vertexbuffer_destroy(topLine);
  71. gs_vertexbuffer_destroy(rightLine);
  72. obs_leave_graphics();
  73. }
  74. if (type == ProjectorType::Multiview)
  75. multiviewProjectors.removeAll(this);
  76. App()->DecrementSleepInhibition();
  77. }
  78. static OBSSource CreateLabel(const char *name, size_t h)
  79. {
  80. obs_data_t *settings = obs_data_create();
  81. obs_data_t *font = obs_data_create();
  82. std::string text;
  83. text += " ";
  84. text += name;
  85. text += " ";
  86. #if defined(_WIN32)
  87. obs_data_set_string(font, "face", "Arial");
  88. #elif defined(__APPLE__)
  89. obs_data_set_string(font, "face", "Helvetica");
  90. #else
  91. obs_data_set_string(font, "face", "Monospace");
  92. #endif
  93. obs_data_set_int(font, "flags", 1); // Bold text
  94. obs_data_set_int(font, "size", int(h / 9.81));
  95. obs_data_set_obj(settings, "font", font);
  96. obs_data_set_string(settings, "text", text.c_str());
  97. obs_data_set_bool(settings, "outline", false);
  98. #ifdef _WIN32
  99. const char *text_source_id = "text_gdiplus";
  100. #else
  101. const char *text_source_id = "text_ft2_source";
  102. #endif
  103. OBSSource txtSource = obs_source_create_private(text_source_id, name,
  104. settings);
  105. obs_source_release(txtSource);
  106. obs_data_release(font);
  107. obs_data_release(settings);
  108. return txtSource;
  109. }
  110. void OBSProjector::Init(int monitor, bool window, QString title,
  111. ProjectorType type_)
  112. {
  113. bool alwaysOnTop = config_get_bool(GetGlobalConfig(),
  114. "BasicWindow", "ProjectorAlwaysOnTop");
  115. if (alwaysOnTop && !window)
  116. SetAlwaysOnTop(this, true);
  117. show();
  118. if (window) {
  119. setWindowTitle(title);
  120. } else {
  121. QScreen *screen = QGuiApplication::screens()[monitor];
  122. setGeometry(screen->geometry());
  123. QAction *action = new QAction(this);
  124. action->setShortcut(Qt::Key_Escape);
  125. addAction(action);
  126. connect(action, SIGNAL(triggered()), this,
  127. SLOT(EscapeTriggered()));
  128. activateWindow();
  129. }
  130. if (source)
  131. obs_source_inc_showing(source);
  132. savedMonitor = monitor;
  133. isWindow = window;
  134. type = type_;
  135. if (type == ProjectorType::Multiview) {
  136. obs_enter_graphics();
  137. gs_render_start(true);
  138. gs_vertex2f(0.001f, 0.001f);
  139. gs_vertex2f(0.001f, 0.997f);
  140. gs_vertex2f(0.997f, 0.997f);
  141. gs_vertex2f(0.997f, 0.001f);
  142. gs_vertex2f(0.001f, 0.001f);
  143. outerBox = gs_render_save();
  144. gs_render_start(true);
  145. gs_vertex2f(0.04f, 0.04f);
  146. gs_vertex2f(0.04f, 0.96f);
  147. gs_vertex2f(0.96f, 0.96f);
  148. gs_vertex2f(0.96f, 0.04f);
  149. gs_vertex2f(0.04f, 0.04f);
  150. innerBox = gs_render_save();
  151. gs_render_start(true);
  152. gs_vertex2f(0.15f, 0.04f);
  153. gs_vertex2f(0.15f, 0.96f);
  154. leftVLine = gs_render_save();
  155. gs_render_start(true);
  156. gs_vertex2f(0.85f, 0.04f);
  157. gs_vertex2f(0.85f, 0.96f);
  158. rightVLine = gs_render_save();
  159. gs_render_start(true);
  160. gs_vertex2f(0.0f, 0.5f);
  161. gs_vertex2f(0.075f, 0.5f);
  162. leftLine = gs_render_save();
  163. gs_render_start(true);
  164. gs_vertex2f(0.5f, 0.0f);
  165. gs_vertex2f(0.5f, 0.09f);
  166. topLine = gs_render_save();
  167. gs_render_start(true);
  168. gs_vertex2f(0.925f, 0.5f);
  169. gs_vertex2f(1.0f, 0.5f);
  170. rightLine = gs_render_save();
  171. obs_leave_graphics();
  172. UpdateMultiview();
  173. multiviewProjectors.push_back(this);
  174. }
  175. ready = true;
  176. }
  177. static inline void renderVB(gs_effect_t *effect, gs_vertbuffer_t *vb,
  178. int cx, int cy)
  179. {
  180. if (!vb)
  181. return;
  182. matrix4 transform;
  183. matrix4_identity(&transform);
  184. transform.x.x = cx;
  185. transform.y.y = cy;
  186. gs_load_vertexbuffer(vb);
  187. gs_matrix_push();
  188. gs_matrix_mul(&transform);
  189. while (gs_effect_loop(effect, "Solid"))
  190. gs_draw(GS_LINESTRIP, 0, 0);
  191. gs_matrix_pop();
  192. }
  193. static inline uint32_t labelOffset(obs_source_t *label, uint32_t cx)
  194. {
  195. uint32_t w = obs_source_get_width(label);
  196. w = uint32_t(float(w) * 0.5f);
  197. return (cx / 2) - w;
  198. }
  199. void OBSProjector::OBSRenderMultiview(void *data, uint32_t cx, uint32_t cy)
  200. {
  201. OBSProjector *window = (OBSProjector *)data;
  202. if (updatingMultiview || !window->ready)
  203. return;
  204. OBSBasic *main = (OBSBasic *)obs_frontend_get_main_window();
  205. uint32_t targetCX, targetCY;
  206. int x, y;
  207. float fX, fY, halfCX, halfCY, sourceX, sourceY, labelX, labelY,
  208. quarterCX, quarterCY, scale, targetCXF, targetCYF,
  209. hiCX, hiCY, qiX, qiY, qiCX, qiCY, hiScaleX, hiScaleY,
  210. qiScaleX, qiScaleY;
  211. uint32_t offset;
  212. gs_effect_t *solid = obs_get_base_effect(OBS_EFFECT_SOLID);
  213. gs_eparam_t *color = gs_effect_get_param_by_name(solid, "color");
  214. struct obs_video_info ovi;
  215. obs_get_video_info(&ovi);
  216. targetCX = ovi.base_width;
  217. targetCY = ovi.base_height;
  218. GetScaleAndCenterPos(targetCX, targetCY, cx, cy, x, y, scale);
  219. targetCXF = float(targetCX);
  220. targetCYF = float(targetCY);
  221. fX = float(x);
  222. fY = float(y);
  223. halfCX = (targetCXF + 1) / 2;
  224. halfCY = (targetCYF + 1) / 2;
  225. hiCX = (halfCX - 4.0);
  226. hiCY = (halfCY - 4.0);
  227. hiScaleX = hiCX / targetCXF;
  228. hiScaleY = hiCY / targetCYF;
  229. quarterCX = (halfCX + 1) / 2;
  230. quarterCY = (halfCY + 1) / 2;
  231. qiCX = (quarterCX - 8.0);
  232. qiCY = (quarterCY - 8.0);
  233. qiScaleX = qiCX / targetCXF;
  234. qiScaleY = qiCY / targetCYF;
  235. OBSSource previewSrc = main->GetCurrentSceneSource();
  236. OBSSource programSrc = main->GetProgramSource();
  237. bool studioMode = main->IsPreviewProgramMode();
  238. auto drawBox = [solid, color] (float cx, float cy,
  239. uint32_t colorVal)
  240. {
  241. gs_effect_set_color(color, colorVal);
  242. while (gs_effect_loop(solid, "Solid"))
  243. gs_draw_sprite(nullptr, 0, (uint32_t)cx, (uint32_t)cy);
  244. };
  245. auto setRegion = [fX, fY, scale] (float x, float y, float cx, float cy)
  246. {
  247. float vX = int(fX + x * scale);
  248. float vY = int(fY + y * scale);
  249. float vCX = int(cx * scale);
  250. float vCY = int(cy * scale);
  251. float oL = x;
  252. float oT = y;
  253. float oR = (x + cx);
  254. float oB = (y + cy);
  255. gs_projection_push();
  256. gs_viewport_push();
  257. gs_set_viewport(vX, vY, vCX, vCY);
  258. gs_ortho(oL, oR, oT, oB, -100.0f, 100.0f);
  259. };
  260. auto resetRegion = [] ()
  261. {
  262. gs_viewport_pop();
  263. gs_projection_pop();
  264. };
  265. auto calcBaseSource = [&](size_t i)
  266. {
  267. switch (multiviewLayout) {
  268. case VERTICAL_LEFT:
  269. sourceX = halfCX;
  270. sourceY = (i / 2 ) * quarterCY;
  271. if (i % 2 != 0)
  272. sourceX = halfCX + quarterCX;
  273. break;
  274. case VERTICAL_RIGHT:
  275. sourceX = 0;
  276. sourceY = (i / 2 ) * quarterCY;
  277. if (i % 2 != 0)
  278. sourceX = quarterCX;
  279. break;
  280. case HORIZONTAL_BOTTOM:
  281. if (i < 4) {
  282. sourceX = (float(i) * quarterCX);
  283. sourceY = 0;
  284. } else {
  285. sourceX = (float(i - 4) * quarterCX);
  286. sourceY = quarterCY;
  287. }
  288. break;
  289. default: //HORIZONTAL_TOP:
  290. if (i < 4) {
  291. sourceX = (float(i) * quarterCX);
  292. sourceY = halfCY;
  293. } else {
  294. sourceX = (float(i - 4) * quarterCX);
  295. sourceY = halfCY + quarterCY;
  296. }
  297. }
  298. };
  299. auto calcPreviewProgram = [&](bool program)
  300. {
  301. switch (multiviewLayout) {
  302. case VERTICAL_LEFT:
  303. sourceX = 2.0f;
  304. sourceY = halfCY + 2.0f;
  305. labelX = offset;
  306. labelY = halfCY * 1.8f;
  307. if (program) {
  308. sourceY = 2.0f;
  309. labelY = halfCY * 0.8f;
  310. }
  311. break;
  312. case VERTICAL_RIGHT:
  313. sourceX = halfCX + 2.0f;
  314. sourceY = halfCY + 2.0f;
  315. labelX = halfCX + offset;
  316. labelY = halfCY * 1.8f;
  317. if (program) {
  318. sourceY = 2.0f;
  319. labelY = halfCY * 0.8f;
  320. }
  321. break;
  322. case HORIZONTAL_BOTTOM:
  323. sourceX = 2.0f;
  324. sourceY = halfCY + 2.0f;
  325. labelX = offset;
  326. labelY = halfCY * 1.8f;
  327. if (program) {
  328. sourceX = halfCX + 2.0f;
  329. labelX = halfCX + offset;
  330. }
  331. break;
  332. default: //HORIZONTAL_TOP:
  333. sourceX = 2.0f;
  334. sourceY = 2.0f;
  335. labelX = offset;
  336. labelY = halfCY * 0.8f;
  337. if (program) {
  338. sourceX = halfCX + 2.0f;
  339. labelX = halfCX + offset;
  340. }
  341. }
  342. };
  343. /* ----------------------------- */
  344. /* draw sources */
  345. gs_projection_push();
  346. gs_viewport_push();
  347. gs_set_viewport(x, y, targetCX * scale, targetCY * scale);
  348. gs_ortho(0.0f, targetCXF, 0.0f, targetCYF, -100.0f, 100.0f);
  349. for (size_t i = 0; i < 8; i++) {
  350. OBSSource src = OBSGetStrongRef(window->multiviewScenes[i]);
  351. obs_source *label = window->multiviewLabels[i + 2];
  352. if (!src)
  353. continue;
  354. if (!label)
  355. continue;
  356. calcBaseSource(i);
  357. qiX = sourceX + 4.0f;
  358. qiY = sourceY + 4.0f;
  359. /* ----------- */
  360. if (src == previewSrc || src == programSrc) {
  361. uint32_t colorVal = src == programSrc
  362. ? 0xFFFF0000
  363. : 0xFF00FF00;
  364. gs_matrix_push();
  365. gs_matrix_translate3f(sourceX, sourceY, 0.0f);
  366. drawBox(quarterCX, quarterCY, colorVal);
  367. gs_matrix_pop();
  368. gs_matrix_push();
  369. gs_matrix_translate3f(qiX, qiY, 0.0f);
  370. drawBox(qiCX, qiCY, 0xFF000000);
  371. gs_matrix_pop();
  372. }
  373. /* ----------- */
  374. gs_matrix_push();
  375. gs_matrix_translate3f(qiX, qiY, 0.0f);
  376. gs_matrix_scale3f(qiScaleX, qiScaleY, 1.0f);
  377. setRegion(qiX, qiY, qiCX, qiCY);
  378. obs_source_video_render(src);
  379. resetRegion();
  380. gs_effect_set_color(color, 0xFFFFFFFF);
  381. renderVB(solid, window->outerBox, targetCX, targetCY);
  382. gs_matrix_pop();
  383. /* ----------- */
  384. offset = labelOffset(label, quarterCX);
  385. cx = obs_source_get_width(label);
  386. cy = obs_source_get_height(label);
  387. gs_matrix_push();
  388. gs_matrix_translate3f(sourceX + offset,
  389. (quarterCY * 0.8f) + sourceY, 0.0f);
  390. drawBox(cx, cy + int(quarterCX * 0.015f), 0xD91F1F1F);
  391. obs_source_video_render(label);
  392. gs_matrix_pop();
  393. }
  394. gs_effect_set_color(color, 0xFFFFFFFF);
  395. /* ----------------------------- */
  396. /* draw preview */
  397. obs_source_t *previewLabel = window->multiviewLabels[0];
  398. offset = labelOffset(previewLabel, halfCX);
  399. calcPreviewProgram(false);
  400. gs_matrix_push();
  401. gs_matrix_translate3f(sourceX, sourceY, 0.0f);
  402. gs_matrix_scale3f(hiScaleX, hiScaleY, 1.0f);
  403. setRegion(sourceX, sourceY, hiCX, hiCY);
  404. if (studioMode) {
  405. obs_source_video_render(previewSrc);
  406. } else {
  407. obs_render_main_texture();
  408. }
  409. resetRegion();
  410. gs_matrix_pop();
  411. /* ----------- */
  412. gs_matrix_push();
  413. gs_matrix_translate3f(sourceX, sourceY, 0.0f);
  414. gs_matrix_scale3f(hiScaleX, hiScaleY, 1.0f);
  415. renderVB(solid, window->outerBox, targetCX, targetCY);
  416. renderVB(solid, window->innerBox, targetCX, targetCY);
  417. renderVB(solid, window->leftVLine, targetCX, targetCY);
  418. renderVB(solid, window->rightVLine, targetCX, targetCY);
  419. renderVB(solid, window->leftLine, targetCX, targetCY);
  420. renderVB(solid, window->topLine, targetCX, targetCY);
  421. renderVB(solid, window->rightLine, targetCX, targetCY);
  422. gs_matrix_pop();
  423. /* ----------- */
  424. cx = obs_source_get_width(previewLabel);
  425. cy = obs_source_get_height(previewLabel);
  426. gs_matrix_push();
  427. gs_matrix_translate3f(labelX, labelY, 0.0f);
  428. drawBox(cx, cy + int(halfCX * 0.015f), 0xD91F1F1F);
  429. obs_source_video_render(previewLabel);
  430. gs_matrix_pop();
  431. /* ----------------------------- */
  432. /* draw program */
  433. obs_source_t *programLabel = window->multiviewLabels[1];
  434. offset = labelOffset(programLabel, halfCX);
  435. calcPreviewProgram(true);
  436. gs_matrix_push();
  437. gs_matrix_translate3f(sourceX, sourceY, 0.0f);
  438. gs_matrix_scale3f(hiScaleX, hiScaleY, 1.0f);
  439. setRegion(sourceX, sourceY, hiCX, hiCY);
  440. obs_render_main_texture();
  441. resetRegion();
  442. gs_matrix_pop();
  443. /* ----------- */
  444. gs_matrix_push();
  445. gs_matrix_translate3f(sourceX, sourceY, 0.0f);
  446. gs_matrix_scale3f(hiScaleX, hiScaleY, 1.0f);
  447. renderVB(solid, window->outerBox, targetCX, targetCY);
  448. gs_matrix_pop();
  449. /* ----------- */
  450. cx = obs_source_get_width(programLabel);
  451. cy = obs_source_get_height(programLabel);
  452. gs_matrix_push();
  453. gs_matrix_translate3f(labelX, labelY, 0.0f);
  454. drawBox(cx, cy + int(halfCX * 0.015f), 0xD91F1F1F);
  455. obs_source_video_render(programLabel);
  456. gs_matrix_pop();
  457. /* ----------------------------- */
  458. gs_viewport_pop();
  459. gs_projection_pop();
  460. }
  461. void OBSProjector::OBSRender(void *data, uint32_t cx, uint32_t cy)
  462. {
  463. OBSProjector *window = reinterpret_cast<OBSProjector*>(data);
  464. if (!window->ready)
  465. return;
  466. OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow());
  467. OBSSource source = window->source;
  468. uint32_t targetCX;
  469. uint32_t targetCY;
  470. int x, y;
  471. int newCX, newCY;
  472. float scale;
  473. if (source) {
  474. targetCX = std::max(obs_source_get_width(source), 1u);
  475. targetCY = std::max(obs_source_get_height(source), 1u);
  476. } else {
  477. struct obs_video_info ovi;
  478. obs_get_video_info(&ovi);
  479. targetCX = ovi.base_width;
  480. targetCY = ovi.base_height;
  481. }
  482. GetScaleAndCenterPos(targetCX, targetCY, cx, cy, x, y, scale);
  483. newCX = int(scale * float(targetCX));
  484. newCY = int(scale * float(targetCY));
  485. gs_viewport_push();
  486. gs_projection_push();
  487. gs_ortho(0.0f, float(targetCX), 0.0f, float(targetCY), -100.0f, 100.0f);
  488. gs_set_viewport(x, y, newCX, newCY);
  489. if (window->type == ProjectorType::Preview &&
  490. main->IsPreviewProgramMode()) {
  491. OBSSource curSource = main->GetCurrentSceneSource();
  492. if (source != curSource) {
  493. obs_source_dec_showing(source);
  494. obs_source_inc_showing(curSource);
  495. source = curSource;
  496. }
  497. }
  498. if (source) {
  499. obs_source_video_render(source);
  500. } else {
  501. obs_render_main_texture();
  502. }
  503. gs_projection_pop();
  504. gs_viewport_pop();
  505. }
  506. void OBSProjector::OBSSourceRemoved(void *data, calldata_t *params)
  507. {
  508. OBSProjector *window = reinterpret_cast<OBSProjector*>(data);
  509. window->deleteLater();
  510. UNUSED_PARAMETER(params);
  511. }
  512. static int getSourceByPosition(int x, int y)
  513. {
  514. struct obs_video_info ovi;
  515. obs_get_video_info(&ovi);
  516. float ratio = float(ovi.base_width) / float(ovi.base_height);
  517. QWidget *rec = QApplication::activeWindow();
  518. int cx = rec->width();
  519. int cy = rec->height();
  520. int minX = 0;
  521. int minY = 0;
  522. int maxX = cx;
  523. int maxY = cy;
  524. int halfX = cx / 2;
  525. int halfY = cy / 2;
  526. int pos = -1;
  527. switch (multiviewLayout) {
  528. case VERTICAL_LEFT:
  529. if (float(cx) / float(cy) > ratio) {
  530. int validX = cy * ratio;
  531. maxX = halfX + (validX / 2);
  532. } else {
  533. int validY = cx / ratio;
  534. minY = halfY - (validY / 2);
  535. maxY = halfY + (validY / 2);
  536. }
  537. minX = halfX;
  538. if (x < minX || x > maxX || y < minY || y > maxY)
  539. break;
  540. pos = 2 * ((y - minY) / ((maxY - minY) / 4));
  541. if (x > minX + ((maxX - minX) / 2))
  542. pos++;
  543. break;
  544. case VERTICAL_RIGHT:
  545. if (float(cx) / float(cy) > ratio) {
  546. int validX = cy * ratio;
  547. minX = halfX - (validX / 2);
  548. } else {
  549. int validY = cx / ratio;
  550. minY = halfY - (validY / 2);
  551. maxY = halfY + (validY / 2);
  552. }
  553. maxX = halfX;
  554. if (x < minX || x > maxX || y < minY || y > maxY)
  555. break;
  556. pos = 2 * ((y - minY) / ((maxY - minY) / 4));
  557. if (x > minX + ((maxX - minX) / 2))
  558. pos++;
  559. break;
  560. case HORIZONTAL_BOTTOM:
  561. if (float(cx) / float(cy) > ratio) {
  562. int validX = cy * ratio;
  563. minX = halfX - (validX / 2);
  564. maxX = halfX + (validX / 2);
  565. } else {
  566. int validY = cx / ratio;
  567. minY = halfY - (validY / 2);
  568. }
  569. maxY = halfY;
  570. if (x < minX || x > maxX || y < minY || y > maxY)
  571. break;
  572. pos = (x - minX) / ((maxX - minX) / 4);
  573. if (y > minY + ((maxY - minY) / 2))
  574. pos += 4;
  575. break;
  576. default: // HORIZONTAL_TOP
  577. if (float(cx) / float(cy) > ratio) {
  578. int validX = cy * ratio;
  579. minX = halfX - (validX / 2);
  580. maxX = halfX + (validX / 2);
  581. } else {
  582. int validY = cx / ratio;
  583. maxY = halfY + (validY / 2);
  584. }
  585. minY = halfY;
  586. if (x < minX || x > maxX || y < minY || y > maxY)
  587. break;
  588. pos = (x - minX) / ((maxX - minX) / 4);
  589. if (y > minY + ((maxY - minY) / 2))
  590. pos += 4;
  591. }
  592. return pos;
  593. }
  594. void OBSProjector::mouseDoubleClickEvent(QMouseEvent *event)
  595. {
  596. OBSQTDisplay::mouseDoubleClickEvent(event);
  597. if (!config_get_bool(GetGlobalConfig(), "BasicWindow",
  598. "TransitionOnDoubleClick"))
  599. return;
  600. OBSBasic *main = (OBSBasic*)obs_frontend_get_main_window();
  601. if (!main->IsPreviewProgramMode())
  602. return;
  603. if (event->button() == Qt::LeftButton) {
  604. int pos = getSourceByPosition(event->x(), event->y());
  605. if (pos < 0)
  606. return;
  607. OBSSource src = OBSGetStrongRef(multiviewScenes[pos]);
  608. if (!src)
  609. return;
  610. if (main->GetProgramSource() != src)
  611. main->TransitionToScene(src);
  612. }
  613. }
  614. void OBSProjector::mousePressEvent(QMouseEvent *event)
  615. {
  616. OBSQTDisplay::mousePressEvent(event);
  617. if (event->button() == Qt::RightButton) {
  618. QMenu popup(this);
  619. popup.addAction(QTStr("Close"), this, SLOT(EscapeTriggered()));
  620. popup.exec(QCursor::pos());
  621. }
  622. if (event->button() == Qt::LeftButton) {
  623. int pos = getSourceByPosition(event->x(), event->y());
  624. if (pos < 0)
  625. return;
  626. OBSSource src = OBSGetStrongRef(multiviewScenes[pos]);
  627. if (!src)
  628. return;
  629. OBSBasic *main = (OBSBasic*)obs_frontend_get_main_window();
  630. if (main->GetCurrentSceneSource() != src)
  631. main->SetCurrentScene(src, false);
  632. }
  633. }
  634. void OBSProjector::EscapeTriggered()
  635. {
  636. if (!isWindow) {
  637. OBSBasic *main = (OBSBasic*)obs_frontend_get_main_window();
  638. main->RemoveSavedProjectors(savedMonitor);
  639. }
  640. deleteLater();
  641. }
  642. void OBSProjector::UpdateMultiview()
  643. {
  644. for (OBSWeakSource &val : multiviewScenes)
  645. val = nullptr;
  646. for (OBSSource &val : multiviewLabels)
  647. val = nullptr;
  648. struct obs_video_info ovi;
  649. obs_get_video_info(&ovi);
  650. uint32_t h = ovi.base_height;
  651. struct obs_frontend_source_list scenes = {};
  652. obs_frontend_get_scenes(&scenes);
  653. int curIdx = 0;
  654. multiviewLabels[0] = CreateLabel(Str("StudioMode.Preview"), h / 2);
  655. multiviewLabels[1] = CreateLabel(Str("StudioMode.Program"), h / 2);
  656. for (size_t i = 0; i < scenes.sources.num && curIdx < 8; i++) {
  657. obs_source_t *src = scenes.sources.array[i];
  658. OBSData data = obs_source_get_private_settings(src);
  659. obs_data_release(data);
  660. obs_data_set_default_bool(data, "show_in_multiview", true);
  661. if (!obs_data_get_bool(data, "show_in_multiview"))
  662. continue;
  663. multiviewScenes[curIdx] = OBSGetWeakRef(src);
  664. obs_source_inc_showing(src);
  665. std::string name;
  666. name += std::to_string(curIdx + 1);
  667. name += " - ";
  668. name += obs_source_get_name(src);
  669. multiviewLabels[curIdx + 2] = CreateLabel(name.c_str(), h / 3);
  670. curIdx++;
  671. }
  672. obs_frontend_source_list_free(&scenes);
  673. const char *multiviewLayoutText = config_get_string(GetGlobalConfig(),
  674. "BasicWindow", "MultiviewLayout");
  675. if (astrcmpi(multiviewLayoutText, "horizontalbottom") == 0)
  676. multiviewLayout = HORIZONTAL_BOTTOM;
  677. else if (astrcmpi(multiviewLayoutText, "verticalleft") == 0)
  678. multiviewLayout = VERTICAL_LEFT;
  679. else if (astrcmpi(multiviewLayoutText, "verticalright") == 0)
  680. multiviewLayout = VERTICAL_RIGHT;
  681. else
  682. multiviewLayout = HORIZONTAL_TOP;
  683. }
  684. OBSSource OBSProjector::GetSource()
  685. {
  686. return source;
  687. }
  688. ProjectorType OBSProjector::GetProjectorType()
  689. {
  690. return type;
  691. }
  692. int OBSProjector::GetMonitor()
  693. {
  694. return savedMonitor;
  695. }
  696. void OBSProjector::UpdateMultiviewProjectors()
  697. {
  698. obs_enter_graphics();
  699. updatingMultiview = true;
  700. obs_leave_graphics();
  701. for (auto &projector : multiviewProjectors)
  702. projector->UpdateMultiview();
  703. obs_enter_graphics();
  704. updatingMultiview = false;
  705. obs_leave_graphics();
  706. }