window-basic-main.cpp 12.8 KB
Newer Older
1 2
/******************************************************************************
    Copyright (C) 2013 by Hugh Bailey <obs.jim@gmail.com>
3
    Copyright (C) 2014 by Zachary Lund <admin@computerquip.com>
4 5 6

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
7
    the Free Software Foundation, either version 2 of the License, or
8 9 10
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
12 13 14 15 16 17 18
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/

J
jp9000 已提交
19
#include <obs.hpp>
J
jp9000 已提交
20
#include <QMessageBox>
21
#include <QShowEvent>
22

23
#include "obs-app.hpp"
24
#include "window-basic-settings.hpp"
25
#include "window-namedialog.hpp"
J
jp9000 已提交
26 27 28
#include "window-basic-main.hpp"
#include "qt-wrappers.hpp"
#include "qt-ptr-variant.hpp"
29

J
jp9000 已提交
30
#include "ui_OBSBasic.h"
31

32
using namespace std;
J
jp9000 已提交
33

34 35
obs_scene_t OBSBasic::GetCurrentScene()
{
J
jp9000 已提交
36 37 38
	QListWidgetItem *item = ui->scenes->currentItem();
	return item ? VariantPtr<obs_scene_t>(item->data(Qt::UserRole)) :
		nullptr;
39 40 41
}

void OBSBasic::AddScene(obs_source_t source)
42 43 44
{
	const char *name  = obs_source_getname(source);
	obs_scene_t scene = obs_scene_fromsource(source);
J
jp9000 已提交
45 46 47 48

	QListWidgetItem *item = new QListWidgetItem(QT_UTF8(name));
	item->setData(Qt::UserRole, PtrVariant(scene));
	ui->scenes->addItem(item);
49 50

	signal_handler_t handler = obs_source_signalhandler(source);
J
jp9000 已提交
51
	signal_handler_connect(handler, "add", OBSBasic::SceneItemAdded, this);
52 53
	signal_handler_connect(handler, "remove", OBSBasic::SceneItemRemoved,
			this);
54 55
}

56
void OBSBasic::RemoveScene(obs_source_t source)
J
jp9000 已提交
57 58 59
{
	const char *name = obs_source_getname(source);

J
jp9000 已提交
60 61 62
	QListWidgetItem *sel = ui->scenes->currentItem();
	QList<QListWidgetItem*> items = ui->scenes->findItems(QT_UTF8(name),
			Qt::MatchExactly);
J
jp9000 已提交
63

J
jp9000 已提交
64 65 66 67
	if (sel != nullptr) {
		if (items.contains(sel))
			ui->sources->clear();
		delete sel;
J
jp9000 已提交
68
	}
69 70 71 72
}

void OBSBasic::AddSceneItem(obs_sceneitem_t item)
{
J
jp9000 已提交
73
	obs_scene_t scene = obs_sceneitem_getscene(item);
74 75
	obs_source_t source = obs_sceneitem_getsource(item);
	const char *name = obs_source_getname(source);
J
jp9000 已提交
76

J
jp9000 已提交
77 78 79 80 81 82
	if (GetCurrentScene() == scene) {
		QListWidgetItem *listItem = new QListWidgetItem(QT_UTF8(name));
		listItem->setData(Qt::UserRole, PtrVariant(item));

		ui->sources->insertItem(0, listItem);
	}
J
jp9000 已提交
83 84

	sourceSceneRefs[source] = sourceSceneRefs[source] + 1;
85 86 87 88
}

void OBSBasic::RemoveSceneItem(obs_sceneitem_t item)
{
J
jp9000 已提交
89
	obs_scene_t scene = obs_sceneitem_getscene(item);
90

J
jp9000 已提交
91
	if (GetCurrentScene() == scene) {
B
BtbN 已提交
92
		for (int i = 0; i < ui->sources->count(); i++) {
J
jp9000 已提交
93 94
			QListWidgetItem *listItem = ui->sources->item(i);
			QVariant userData = listItem->data(Qt::UserRole);
J
jp9000 已提交
95

J
jp9000 已提交
96 97
			if (item == VariantPtr<obs_sceneitem_t>(userData)) {
				delete listItem;
J
jp9000 已提交
98 99
				break;
			}
100 101
		}
	}
J
jp9000 已提交
102 103 104 105 106 107 108 109 110 111

	obs_source_t source = obs_sceneitem_getsource(item);
	obs_source_addref(source);
	obs_source_release(source);

	int scenes = sourceSceneRefs[source] - 1;
	if (scenes == 0) {
		obs_source_remove(source);
		sourceSceneRefs.erase(source);
	}
112 113 114 115
}

void OBSBasic::UpdateSources(obs_scene_t scene)
{
J
jp9000 已提交
116
	ui->sources->clear();
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132

	obs_scene_enum_items(scene,
			[] (obs_scene_t scene, obs_sceneitem_t item, void *p)
			{
				OBSBasic *window = static_cast<OBSBasic*>(p);
				window->AddSceneItem(item);
				return true;
			}, this);
}

void OBSBasic::UpdateSceneSelection(obs_source_t source)
{
	if (source) {
		obs_source_type type;
		obs_source_gettype(source, &type, NULL);

J
jp9000 已提交
133 134
		if (type != SOURCE_SCENE)
			return;
135

J
jp9000 已提交
136 137 138 139 140 141 142 143 144 145
		obs_scene_t scene = obs_scene_fromsource(source);
		const char *name = obs_source_getname(source);

		QListWidgetItem *sel = ui->scenes->currentItem();
		QList<QListWidgetItem*> items =
			ui->scenes->findItems(QT_UTF8(name), Qt::MatchExactly);

		if (items.contains(sel)) {
			ui->scenes->setCurrentItem(sel);
			UpdateSources(scene);
146
		}
J
jp9000 已提交
147
	}
148 149 150 151 152 153 154 155 156 157
}

/* OBS Callbacks */

void OBSBasic::SceneItemAdded(void *data, calldata_t params)
{
	OBSBasic *window = static_cast<OBSBasic*>(data);

	obs_scene_t scene = (obs_scene_t)calldata_ptr(params, "scene");
	obs_sceneitem_t item = (obs_sceneitem_t)calldata_ptr(params, "item");
J
jp9000 已提交
158

J
jp9000 已提交
159
	window->AddSceneItem(item);
J
jp9000 已提交
160 161
}

162
void OBSBasic::SceneItemRemoved(void *data, calldata_t params)
163
{
164
	OBSBasic *window = static_cast<OBSBasic*>(data);
165

166 167 168
	obs_scene_t scene = (obs_scene_t)calldata_ptr(params, "scene");
	obs_sceneitem_t item = (obs_sceneitem_t)calldata_ptr(params, "item");

J
jp9000 已提交
169
	window->RemoveSceneItem(item);
170 171 172 173 174
}

void OBSBasic::SourceAdded(void *data, calldata_t params)
{
	obs_source_t source = (obs_source_t)calldata_ptr(params, "source");
175 176 177 178 179

	obs_source_type type;
	obs_source_gettype(source, &type, NULL);

	if (type == SOURCE_SCENE)
180
		static_cast<OBSBasic*>(data)->AddScene(source);
181 182 183 184
}

void OBSBasic::SourceDestroyed(void *data, calldata_t params)
{
185
	obs_source_t source = (obs_source_t)calldata_ptr(params, "source");
186

J
jp9000 已提交
187 188 189 190
	obs_source_type type;
	obs_source_gettype(source, &type, NULL);

	if (type == SOURCE_SCENE)
191
		static_cast<OBSBasic*>(data)->RemoveScene(source);
192 193
}

194 195 196 197 198 199 200 201 202 203 204
void OBSBasic::ChannelChanged(void *data, calldata_t params)
{
	obs_source_t source = (obs_source_t)calldata_ptr(params, "source");
	uint32_t channel = calldata_uint32(params, "channel");

	if (channel == 0)
		static_cast<OBSBasic*>(data)->UpdateSceneSelection(source);
}

/* Main class functions */

J
jp9000 已提交
205
OBSBasic::OBSBasic(QWidget *parent)
206 207
	: OBSMainWindow (parent),
	  ui            (new Ui::OBSBasic)
J
jp9000 已提交
208
{
J
jp9000 已提交
209
	ui->setupUi(this);
210 211 212 213 214 215 216
}

void OBSBasic::OBSInit()
{
	/* make sure it's fully displayed before doing any initialization */
	show();
	App()->processEvents();
J
jp9000 已提交
217

J
jp9000 已提交
218
	if (!obs_startup())
J
jp9000 已提交
219
		throw "Failed to initialize libobs";
J
jp9000 已提交
220
	if (!InitGraphics())
J
jp9000 已提交
221
		throw "Failed to initialize graphics";
J
jp9000 已提交
222
	if (!InitAudio())
J
jp9000 已提交
223
		throw "Failed to initialize audio";
J
jp9000 已提交
224

225 226 227 228
	signal_handler_connect(obs_signalhandler(), "source-add",
			OBSBasic::SourceAdded, this);
	signal_handler_connect(obs_signalhandler(), "source-destroy",
			OBSBasic::SourceDestroyed, this);
229 230
	signal_handler_connect(obs_signalhandler(), "channel-change",
			OBSBasic::ChannelChanged, this);
231

232 233
	/* TODO: this is a test */
	obs_load_module("test-input");
234 235 236 237 238 239

#ifdef _WIN32
	/* HACK: fixes a windows qt bug with native widgets with native
	 * repaint */
	ui->previewContainer->repaint();
#endif
J
jp9000 已提交
240 241 242 243 244 245 246 247 248 249
}

OBSBasic::~OBSBasic()
{
	obs_shutdown();
}

bool OBSBasic::InitGraphics()
{
	struct obs_video_info ovi;
J
jp9000 已提交
250 251 252 253

	App()->GetConfigFPS(ovi.fps_num, ovi.fps_den);

	ovi.graphics_module = App()->GetRenderModule();
J
jp9000 已提交
254 255 256 257 258 259 260 261 262 263
	ovi.base_width    = (uint32_t)config_get_uint(GetGlobalConfig(),
			"Video", "BaseCX");
	ovi.base_height   = (uint32_t)config_get_uint(GetGlobalConfig(),
			"Video", "BaseCY");
	ovi.output_width  = (uint32_t)config_get_uint(GetGlobalConfig(),
			"Video", "OutputCX");
	ovi.output_height = (uint32_t)config_get_uint(GetGlobalConfig(),
			"Video", "OutputCY");
	ovi.output_format = VIDEO_FORMAT_RGBA;
	ovi.adapter       = 0;
264

J
jp9000 已提交
265
//#ifdef __WXGTK__
266
	/* Ugly hack for GTK, I'm hoping this can be avoided eventually... */
J
jp9000 已提交
267 268
//	gtk_widget_realize(previewPanel->GetHandle());
//#endif
269

J
jp9000 已提交
270
	QTToGSWindow(ui->preview, ovi.window);
J
jp9000 已提交
271 272

	//required to make opengl display stuff on osx(?)
J
jp9000 已提交
273
	ResizePreview(ovi.base_width, ovi.base_height);
J
jp9000 已提交
274

J
jp9000 已提交
275 276 277
	QSize size = ui->preview->size();
	ovi.window_width  = size.width();
	ovi.window_height = size.height();
J
jp9000 已提交
278

J
jp9000 已提交
279 280
	return obs_reset_video(&ovi);
}
J
jp9000 已提交
281

J
jp9000 已提交
282 283 284
bool OBSBasic::InitAudio()
{
	/* TODO: load audio settings from config */
J
jp9000 已提交
285
	struct audio_output_info ai;
J
jp9000 已提交
286 287 288 289 290 291 292
	ai.name = "test";
	ai.samples_per_sec = 44100;
	ai.format = AUDIO_FORMAT_16BIT;
	ai.speakers = SPEAKERS_STEREO;
	ai.buffer_ms = 700;

	return obs_reset_audio(&ai);
J
jp9000 已提交
293 294
}

J
jp9000 已提交
295
void OBSBasic::ResizePreview(uint32_t cx, uint32_t cy)
296
{
J
jp9000 已提交
297
	double targetAspect, baseAspect;
298 299
	QSize  targetSize;
	int x, y;
J
jp9000 已提交
300

301
	/* resize preview panel to fix to the top section of the window */
J
jp9000 已提交
302 303 304
	targetSize   = ui->previewContainer->size();
	targetAspect = double(targetSize.width()) / double(targetSize.height());
	baseAspect   = double(cx) / double(cy);
305

306 307 308 309 310 311 312 313 314 315
	if (targetAspect > baseAspect) {
		cx = targetSize.height() * baseAspect;
		cy = targetSize.height();
	} else {
		cx = targetSize.width();
		cy = targetSize.width() / baseAspect;
	}

	x = targetSize.width() /2 - cx/2;
	y = targetSize.height()/2 - cy/2;
316

317
	ui->preview->setGeometry(x, y, cx, cy);
J
jp9000 已提交
318 319

	graphics_t graphics = obs_graphics();
320
	if (graphics && isVisible()) {
J
jp9000 已提交
321
		gs_entercontext(graphics);
322
		gs_resize(cx, cy);
J
jp9000 已提交
323
		gs_leavecontext();
324
	}
J
jp9000 已提交
325 326
}

J
jp9000 已提交
327
void OBSBasic::closeEvent(QCloseEvent *event)
J
jp9000 已提交
328
{
329 330
}

J
jp9000 已提交
331
void OBSBasic::changeEvent(QEvent *event)
332
{
333 334
}

J
jp9000 已提交
335
void OBSBasic::resizeEvent(QResizeEvent *event)
336
{
J
jp9000 已提交
337 338 339 340
	struct obs_video_info ovi;

	if (obs_get_video_info(&ovi))
		ResizePreview(ovi.base_width, ovi.base_height);
341 342
}

J
jp9000 已提交
343
void OBSBasic::on_action_New_triggered()
344 345 346
{
}

J
jp9000 已提交
347
void OBSBasic::on_action_Open_triggered()
348 349 350
{
}

J
jp9000 已提交
351
void OBSBasic::on_action_Save_triggered()
352 353 354
{
}

J
jp9000 已提交
355
void OBSBasic::on_scenes_itemChanged(QListWidgetItem *item)
356 357
{
	obs_source_t source = NULL;
J
jp9000 已提交
358 359 360 361 362

	if (item) {
		obs_scene_t scene;

		scene = VariantPtr<obs_scene_t>(item->data(Qt::UserRole));
363
		source = obs_scene_getsource(scene);
364
		UpdateSources(scene);
365 366
	}

367
	/* TODO: allow transitions */
368 369 370
	obs_set_output_source(0, source);
}

J
jp9000 已提交
371
void OBSBasic::on_scenes_customContextMenuRequested(const QPoint &pos)
372 373 374
{
}

J
jp9000 已提交
375
void OBSBasic::on_actionAddScene_triggered()
376
{
377
	string name;
J
jp9000 已提交
378 379 380
	bool accepted = NameDialog::AskForName(this,
			QTStr("MainWindow.AddSceneDlg.Title"),
			QTStr("MainWindow.AddSceneDlg.Text"),
381 382
			name);

J
jp9000 已提交
383
	if (accepted) {
384 385
		obs_source_t source = obs_get_source_by_name(name.c_str());
		if (source) {
J
jp9000 已提交
386 387 388
			QMessageBox::information(this,
					QTStr("MainWindow.NameExists.Title"),
					QTStr("MainWindow.NameExists.Text"));
389 390

			obs_source_release(source);
J
jp9000 已提交
391
			on_actionAddScene_triggered();
392 393 394
			return;
		}

395
		obs_scene_t scene = obs_scene_create(name.c_str());
396 397
		source = obs_scene_getsource(scene);
		obs_add_source(source);
398
		obs_scene_release(scene);
399 400

		obs_set_output_source(0, source);
401
	}
402 403
}

J
jp9000 已提交
404
void OBSBasic::on_actionRemoveScene_triggered()
405
{
J
jp9000 已提交
406 407
	QListWidgetItem *item = ui->scenes->currentItem();
	if (!item)
J
jp9000 已提交
408 409
		return;

J
jp9000 已提交
410 411
	QVariant userData = item->data(Qt::UserRole);
	obs_scene_t scene = VariantPtr<obs_scene_t>(userData);
J
jp9000 已提交
412 413
	obs_source_t source = obs_scene_getsource(scene);
	obs_source_remove(source);
414 415
}

J
jp9000 已提交
416
void OBSBasic::on_actionSceneProperties_triggered()
417 418 419
{
}

J
jp9000 已提交
420
void OBSBasic::on_actionSceneUp_triggered()
421 422 423
{
}

J
jp9000 已提交
424
void OBSBasic::on_actionSceneDown_triggered()
425 426 427
{
}

J
jp9000 已提交
428
void OBSBasic::on_sources_itemChanged(QListWidgetItem *item)
429 430 431
{
}

J
jp9000 已提交
432
void OBSBasic::on_sources_customContextMenuRequested(const QPoint &pos)
433 434 435
{
}

436 437 438 439 440 441
void OBSBasic::AddSource(obs_scene_t scene, const char *id)
{
	string name;

	bool success = false;
	while (!success) {
J
jp9000 已提交
442
		bool accepted = NameDialog::AskForName(this,
443 444 445 446
				Str("MainWindow.AddSourceDlg.Title"),
				Str("MainWindow.AddSourceDlg.Text"),
				name);

J
jp9000 已提交
447
		if (!accepted)
448 449
			break;

J
jp9000 已提交
450
		obs_source_t source = obs_get_source_by_name(name.c_str());
451 452 453
		if (!source) {
			success = true;
		} else {
J
jp9000 已提交
454 455 456
			QMessageBox::information(this,
					QTStr("MainWindow.NameExists.Title"),
					QTStr("MainWindow.NameExists.Text"));
457 458 459 460 461 462 463
			obs_source_release(source);
		}
	}

	if (success) {
		obs_source_t source = obs_source_create(SOURCE_INPUT, id,
				name.c_str(), NULL);
J
jp9000 已提交
464 465 466

		sourceSceneRefs[source] = 0;

467 468 469 470 471 472
		obs_add_source(source);
		obs_sceneitem_t item = obs_scene_add(scene, source);
		obs_source_release(source);
	}
}

J
jp9000 已提交
473
void OBSBasic::AddSourcePopupMenu(const QPoint &pos)
474
{
J
jp9000 已提交
475
	QListWidgetItem *sel = ui->scenes->currentItem();
476
	const char *type;
J
jp9000 已提交
477 478
	bool foundValues = false;
	size_t idx = 0;
479

J
jp9000 已提交
480
	if (!sel)
481 482
		return;

J
jp9000 已提交
483
	obs_scene_t scene = VariantPtr<obs_scene_t>(sel->data(Qt::UserRole));
484 485
	obs_scene_addref(scene);

J
jp9000 已提交
486 487
	QMenu popup;
	while (obs_enum_input_types(idx++, &type)) {
488
		const char *name = obs_source_getdisplayname(SOURCE_INPUT,
J
jp9000 已提交
489
				type, App()->GetLocale());
490

J
jp9000 已提交
491 492 493
		QAction *popupItem = new QAction(QT_UTF8(name), this);
		popupItem->setData(QT_UTF8(type));
		popup.addAction(popupItem);
494

J
jp9000 已提交
495
		foundValues = true;
496 497
	}

J
jp9000 已提交
498 499 500 501
	if (foundValues) {
		QAction *ret = popup.exec(pos);
		if (ret)
			AddSource(scene, ret->data().toString().toUtf8());
502 503 504 505 506
	}

	obs_scene_release(scene);
}

J
jp9000 已提交
507
void OBSBasic::on_actionAddSource_triggered()
508
{
J
jp9000 已提交
509
	AddSourcePopupMenu(QCursor::pos());
510 511
}

J
jp9000 已提交
512
void OBSBasic::on_actionRemoveSource_triggered()
513
{
J
jp9000 已提交
514 515
	QListWidgetItem *sel = ui->sources->currentItem();
	if (!sel)
J
jp9000 已提交
516 517
		return;

P
Palana 已提交
518 519 520 521
	obs_scene_t scene = GetCurrentScene();
	if (!scene)
		return;

522
	gs_entercontext(obs_graphics());
P
Palana 已提交
523 524
	obs_scene_addref(scene);

J
jp9000 已提交
525 526
	QVariant userData = sel->data(Qt::UserRole);
	obs_sceneitem_t item = VariantPtr<obs_sceneitem_t>(userData);
527
	obs_sceneitem_destroy(item);
P
Palana 已提交
528 529

	obs_scene_release(scene);
530
	gs_leavecontext();
531 532
}

J
jp9000 已提交
533
void OBSBasic::on_actionSourceProperties_triggered()
534 535 536
{
}

J
jp9000 已提交
537
void OBSBasic::on_actionSourceUp_triggered()
538 539
{
}
J
jp9000 已提交
540

J
jp9000 已提交
541
void OBSBasic::on_actionSourceDown_triggered()
542 543 544
{
}

J
jp9000 已提交
545
void OBSBasic::on_settingsButton_clicked()
J
jp9000 已提交
546
{
547 548
	OBSBasicSettings settings(this);
	settings.exec();
J
jp9000 已提交
549
}