1
0
Эх сурвалжийг харах

Add source properties window (very preliminary)

 - Add a properties window for sources so that you can now actually edit
   the settings for sources.  Also, display the source by itself in the
   window (Note: not working on mac, and possibly not working on linux).

   When changing the settings for a source, it will call
   obs_source_update on that source when you have modified any values
   automatically.

 - Add a properties 'widget', eventually I want to turn this in to a
   regular nice properties view like you'd see in the designer, but
   right now it just uses a form layout in a QScrollArea with regular
   controls to display the properties.  It's clunky but works for the
   time being.

 - Make it so that swap chains and the main graphics subsystem will
   automatically use at least one backbuffer if none was specified

 - Fix bug where displays weren't added to the main display array

 - Make it so that you can get the properties of a source via the actual
   pointer of a source/encoder/output in addition to being able to look
   up properties via identifier.

 - When registering source types, check for required functions (wasn't
   doing it before).  getheight/getwidth should not be optional if it's
   a video source as well.

 - Add an RAII OBSObj wrapper to obs.hpp for non-reference-counted
   libobs pointers

 - Add an RAII OBSSignal wrapper to obs.hpp for libobs signals to
   automatically disconnect them on destruction

 - Move the "scale and center" calculation in window-basic-main.cpp to
   its own function and in its own source file

 - Add an 'update' callback to WASAPI audio sources
jp9000 11 жил өмнө
parent
commit
d9251f9e87

+ 3 - 0
libobs/graphics/graphics.c

@@ -114,6 +114,9 @@ int gs_create(graphics_t *pgraphics, const char *module,
 	graphics_t graphics = bzalloc(sizeof(struct graphics_subsystem));
 	pthread_mutex_init_value(&graphics->mutex);
 
+	if (!data->num_backbuffers)
+		data->num_backbuffers = 1;
+
 	graphics->module = os_dlopen(module);
 	if (!graphics->module) {
 		errcode = GS_ERROR_MODULENOTFOUND;

+ 14 - 0
libobs/obs-display.c

@@ -48,11 +48,22 @@ obs_display_t obs_display_create(struct gs_init_data *graphics_data)
 {
 	struct obs_display *display = bzalloc(sizeof(struct obs_display));
 
+	gs_entercontext(obs_graphics());
+
+	if (!graphics_data->num_backbuffers)
+		graphics_data->num_backbuffers = 1;
+
 	if (!obs_display_init(display, graphics_data)) {
 		obs_display_destroy(display);
 		display = NULL;
+	} else {
+		pthread_mutex_lock(&obs->data.displays_mutex);
+		da_push_back(obs->data.displays, &display);
+		pthread_mutex_unlock(&obs->data.displays_mutex);
 	}
 
+	gs_leavecontext();
+
 	return display;
 }
 
@@ -74,7 +85,10 @@ void obs_display_destroy(obs_display_t display)
 		da_erase_item(obs->data.displays, &display);
 		pthread_mutex_unlock(&obs->data.displays_mutex);
 
+		gs_entercontext(obs_graphics());
 		obs_display_free(display);
+		gs_leavecontext();
+
 		bfree(display);
 	}
 }

+ 9 - 1
libobs/obs-encoder.c

@@ -208,7 +208,7 @@ obs_data_t obs_encoder_defaults(const char *id)
 	return NULL;
 }
 
-obs_properties_t obs_encoder_properties(const char *id, const char *locale)
+obs_properties_t obs_get_encoder_properties(const char *id, const char *locale)
 {
 	const struct obs_encoder_info *ei = get_encoder_info(id);
 	if (ei && ei->properties)
@@ -216,6 +216,14 @@ obs_properties_t obs_encoder_properties(const char *id, const char *locale)
 	return NULL;
 }
 
+obs_properties_t obs_encoder_properties(obs_encoder_t encoder,
+		const char *locale)
+{
+	if (encoder && encoder->info.properties)
+		return encoder->info.properties(locale);
+	return NULL;
+}
+
 void obs_encoder_update(obs_encoder_t encoder, obs_data_t settings)
 {
 	if (!encoder) return;

+ 31 - 26
libobs/obs-module.c

@@ -130,14 +130,42 @@ void free_module(struct obs_module *mod)
 	bfree(mod->name);
 }
 
+#define REGISTER_OBS_DEF(size_var, structure, dest, info)                 \
+	do {                                                              \
+		struct structure data = {0};                              \
+		if (!size_var) {                                          \
+			blog(LOG_ERROR, "Tried to register " #structure   \
+			               " outside of obs_module_load");    \
+			return;                                           \
+		}                                                         \
+                                                                          \
+		memcpy(&data, info, size_var);                            \
+		da_push_back(dest, &data);                                \
+	} while (false)
+
+#define CHECK_REQUIRED_VAL(info, val, func) \
+	do { \
+		if (!info->val) {\
+			blog(LOG_ERROR, "Required value '" #val " for" \
+			                "'%s' not found.  " #func \
+			                " failed.", info->id); \
+			return; \
+		} \
+	} while (false)
+
 void obs_register_source(const struct obs_source_info *info)
 {
 	struct obs_source_info data = {0};
 	struct darray *array;
 
-	if (!info) {
-		blog(LOG_ERROR, "obs_register_source: NULL info");
-		return;
+	CHECK_REQUIRED_VAL(info, getname,   obs_register_source);
+	CHECK_REQUIRED_VAL(info, create,    obs_register_source);
+	CHECK_REQUIRED_VAL(info, destroy,   obs_register_source);
+
+	if (info->type == OBS_SOURCE_TYPE_INPUT &&
+	    info->output_flags & OBS_SOURCE_VIDEO) {
+		CHECK_REQUIRED_VAL(info, getwidth,  obs_register_source);
+		CHECK_REQUIRED_VAL(info, getheight, obs_register_source);
 	}
 
 	if (!cur_source_info_size) {
@@ -163,29 +191,6 @@ void obs_register_source(const struct obs_source_info *info)
 	darray_push_back(sizeof(struct obs_source_info), array, &data);
 }
 
-#define REGISTER_OBS_DEF(size_var, structure, dest, info)                 \
-	do {                                                              \
-		struct structure data = {0};                              \
-		if (!size_var) {                                          \
-			blog(LOG_ERROR, "Tried to register " #structure   \
-			               " outside of obs_module_load");    \
-			return;                                           \
-		}                                                         \
-                                                                          \
-		memcpy(&data, info, size_var);                            \
-		da_push_back(dest, &data);                                \
-	} while (false)
-
-#define CHECK_REQUIRED_VAL(info, val, func) \
-	do { \
-		if (!info->val) {\
-			blog(LOG_ERROR, "Required value '" #val " for" \
-			                "'%s' not found.  " #func \
-			                " failed.", info->id); \
-			return; \
-		} \
-	} while (false)
-
 void obs_register_output(const struct obs_output_info *info)
 {
 	CHECK_REQUIRED_VAL(info, getname, obs_register_output);

+ 8 - 1
libobs/obs-output.c

@@ -128,7 +128,7 @@ obs_data_t obs_output_defaults(const char *id)
 	return NULL;
 }
 
-obs_properties_t obs_output_properties(const char *id, const char *locale)
+obs_properties_t obs_get_output_properties(const char *id, const char *locale)
 {
 	const struct obs_output_info *info = find_output(id);
 	if (info && info->properties)
@@ -136,6 +136,13 @@ obs_properties_t obs_output_properties(const char *id, const char *locale)
 	return NULL;
 }
 
+obs_properties_t obs_output_properties(obs_output_t output, const char *locale)
+{
+	if (output && output->info.properties)
+		return output->info.properties(locale);
+	return NULL;
+}
+
 void obs_output_update(obs_output_t output, obs_data_t settings)
 {
 	if (!output) return;

+ 1 - 1
libobs/obs-properties.c

@@ -315,7 +315,7 @@ const char *obs_property_description(obs_property_t p)
 	return p ? p->desc : NULL;
 }
 
-enum obs_property_type obs_property_type(obs_property_t p)
+enum obs_property_type obs_property_get_type(obs_property_t p)
 {
 	return p ? p->type : OBS_PROPERTY_INVALID;
 }

+ 1 - 1
libobs/obs-properties.h

@@ -87,7 +87,7 @@ EXPORT void obs_property_list_add_item(obs_property_t p,
 
 EXPORT const char *           obs_property_name(obs_property_t p);
 EXPORT const char *           obs_property_description(obs_property_t p);
-EXPORT enum obs_property_type obs_property_type(obs_property_t p);
+EXPORT enum obs_property_type obs_property_get_type(obs_property_t p);
 
 EXPORT bool                   obs_property_next(obs_property_t *p);
 

+ 8 - 1
libobs/obs-source.c

@@ -327,7 +327,7 @@ obs_data_t obs_source_settings(enum obs_source_type type, const char *id)
 	return NULL;
 }
 
-obs_properties_t obs_source_properties(enum obs_source_type type,
+obs_properties_t obs_get_source_properties(enum obs_source_type type,
 		const char *id, const char *locale)
 {
 	const struct obs_source_info *info = get_source_info(type, id);
@@ -336,6 +336,13 @@ obs_properties_t obs_source_properties(enum obs_source_type type,
 	return NULL;
 }
 
+obs_properties_t obs_source_properties(obs_source_t source, const char *locale)
+{
+	if (source && source->info.properties)
+		return source->info.properties(locale);
+	return NULL;
+}
+
 uint32_t obs_source_get_output_flags(obs_source_t source)
 {
 	return source ? source->info.output_flags : 0;

+ 6 - 6
libobs/obs-source.h

@@ -132,6 +132,12 @@ struct obs_source_info {
 	/** Destroys the private data for the source */
 	void (*destroy)(void *data);
 
+	/** Returns the width of the source.  Required if input and video */
+	uint32_t (*getwidth)(void *data);
+
+	/** Returns the height of the source.  Required if input and video */
+	uint32_t (*getheight)(void *data);
+
 	/* ----------------------------------------------------------------- */
 	/* Optional implementation */
 
@@ -210,12 +216,6 @@ struct obs_source_info {
 	 */
 	void (*video_render)(void *data, effect_t effect);
 
-	/** @return The width of the source */
-	uint32_t (*getwidth)(void *data);
-
-	/** @return The height of the source */
-	uint32_t (*getheight)(void *data);
-
 	/**
 	 * Called to filter raw async video data.
 	 *

+ 27 - 6
libobs/obs.h

@@ -1,5 +1,5 @@
 /******************************************************************************
-    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+    Copyright (C) 2013-2014 by Hugh Bailey <[email protected]>
 
     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
@@ -42,7 +42,7 @@ struct obs_encoder;
 struct obs_service;
 
 typedef struct obs_display    *obs_display_t;
-typedef struct obs_view   *obs_view_t;
+typedef struct obs_view       *obs_view_t;
 typedef struct obs_source     *obs_source_t;
 typedef struct obs_scene      *obs_scene_t;
 typedef struct obs_scene_item *obs_sceneitem_t;
@@ -438,13 +438,20 @@ EXPORT bool obs_source_removed(obs_source_t source);
 EXPORT uint32_t obs_source_get_output_flags(obs_source_t source);
 
 /** Gets the default settings for a source type */
-EXPORT obs_data_t obs_source_defaults(enum obs_source_type type,
+EXPORT obs_data_t obs_get_source_defaults(enum obs_source_type type,
 		const char *id);
 
 /** Returns the property list, if any.  Free with obs_properties_destroy */
-EXPORT obs_properties_t obs_source_properties(enum obs_source_type type,
+EXPORT obs_properties_t obs_get_source_properties(enum obs_source_type type,
 		const char *id, const char *locale);
 
+/**
+ * Returns the properties list for a specific existing source.  Free with
+ * obs_properties_destroy
+ */
+EXPORT obs_properties_t obs_source_properties(obs_source_t source,
+		const char *locale);
+
 /** Updates settings for this source */
 EXPORT void obs_source_update(obs_source_t source, obs_data_t settings);
 
@@ -662,7 +669,14 @@ EXPORT bool obs_output_active(obs_output_t output);
 EXPORT obs_data_t obs_output_defaults(const char *id);
 
 /** Returns the property list, if any.  Free with obs_properties_destroy */
-EXPORT obs_properties_t obs_output_properties(const char *id,
+EXPORT obs_properties_t obs_get_output_properties(const char *id,
+		const char *locale);
+
+/**
+ * Returns the property list of an existing output, if any.  Free with
+ * obs_properties_destroy
+ */
+EXPORT obs_properties_t obs_output_properties(obs_output_t output,
 		const char *locale);
 
 /** Updates the settings for this output context */
@@ -742,7 +756,14 @@ EXPORT void obs_encoder_stop(obs_encoder_t encoder,
 EXPORT obs_data_t obs_encoder_defaults(const char *id);
 
 /** Returns the property list, if any.  Free with obs_properties_destroy */
-EXPORT obs_properties_t obs_encoder_properties(const char *id,
+EXPORT obs_properties_t obs_get_encoder_properties(const char *id,
+		const char *locale);
+
+/**
+ * Returns the property list of an existing encoder, if any.  Free with
+ * obs_properties_destroy
+ */
+EXPORT obs_properties_t obs_encoder_properties(obs_encoder_t encoder,
 		const char *locale);
 
 /**

+ 83 - 2
libobs/obs.hpp

@@ -63,10 +63,91 @@ public:
 };
 
 using OBSSource = OBSRef<obs_source_t, obs_source_addref, obs_source_release>;
-using OBSScene = OBSRef<obs_scene_t, obs_scene_addref, obs_scene_release>;
+using OBSScene = OBSRef<obs_scene_t,  obs_scene_addref,  obs_scene_release>;
 using OBSSceneItem = OBSRef<obs_sceneitem_t, obs_sceneitem_addref,
 						obs_sceneitem_release>;
-
 using OBSData = OBSRef<obs_data_t, obs_data_addref, obs_data_release>;
 using OBSDataArray = OBSRef<obs_data_array_t, obs_data_array_addref,
 						obs_data_array_release>;
+
+/* objects that are not meant to be instanced */
+template<typename T, void destroy(T)> class OBSObj {
+	T obj;
+
+public:
+	inline OBSObj() : obj(nullptr)    {}
+	inline OBSObj(T obj_) : obj(obj_) {}
+
+	inline ~OBSObj()                  {destroy(obj);}
+
+	inline OBSObj &operator=(T obj_)
+	{
+		if (obj_ != obj)
+			destroy(obj);
+		obj = obj_;
+		return *this;
+	}
+
+	inline operator T() const {return obj;}
+
+	inline bool operator==(T p) const {return obj == p;}
+	inline bool operator!=(T p) const {return obj != p;}
+};
+
+using OBSDisplay = OBSObj<obs_display_t, obs_display_destroy>;
+using OBSEncoder = OBSObj<obs_encoder_t, obs_encoder_destroy>;
+using OBSView    = OBSObj<obs_view_t,    obs_view_destroy>;
+using OBSOutput  = OBSObj<obs_output_t,  obs_output_destroy>;
+
+/* signal handler connection */
+class OBSSignal {
+	signal_handler_t  handler;
+	const char        *signal;
+	signal_callback_t callback;
+	void              *param;
+
+public:
+	inline OBSSignal()
+		: handler  (nullptr),
+		  signal   (nullptr),
+		  callback (nullptr),
+		  param    (nullptr)
+	{}
+
+	inline OBSSignal(signal_handler_t handler_,
+			const char *signal_,
+			signal_callback_t callback_,
+			void *param_)
+		: handler  (handler_),
+		  signal   (signal_),
+		  callback (callback_),
+		  param    (param_)
+	{
+		signal_handler_connect(handler, signal, callback, param);
+	}
+
+	inline void Disconnect()
+	{
+		signal_handler_disconnect(handler, signal, callback, param);
+		handler  = nullptr;
+		signal   = nullptr;
+		callback = nullptr;
+		param    = nullptr;
+	}
+
+	inline ~OBSSignal() {Disconnect();}
+
+	inline void Connect(signal_handler_t handler_,
+			const char *signal_,
+			signal_callback_t callback_,
+			void *param_)
+	{
+		Disconnect();
+
+		handler  = handler_;
+		signal   = signal_;
+		callback = callback_;
+		param    = param_;
+		signal_handler_connect(handler, signal, callback, param);
+	}
+};

+ 7 - 1
obs/CMakeLists.txt

@@ -51,7 +51,9 @@ set(obs_SOURCES
 	obs-app.cpp
 	window-basic-main.cpp
 	window-basic-settings.cpp
+	window-basic-properties.cpp
 	window-namedialog.cpp
+	properties-view.cpp
 	qt-wrappers.cpp)
 
 set(obs_HEADERS
@@ -60,14 +62,18 @@ set(obs_HEADERS
 	window-main.hpp
 	window-basic-main.hpp
 	window-basic-settings.hpp
+	window-basic-properties.hpp
 	window-namedialog.hpp
+	properties-view.hpp
+	display-helpers.hpp
 	qt-display.hpp
 	qt-wrappers.hpp)
 
 set(obs_UI
 	forms/NameDialog.ui
 	forms/OBSBasic.ui
-	forms/OBSBasicSettings.ui)
+	forms/OBSBasicSettings.ui
+	forms/OBSBasicProperties.ui)
 
 set(obs_QRC
 	forms/obs.qrc)

+ 42 - 0
obs/display-helpers.hpp

@@ -0,0 +1,42 @@
+/******************************************************************************
+    Copyright (C) 2014 by Hugh Bailey <[email protected]>
+
+    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
+    the Free Software Foundation, either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    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/>.
+******************************************************************************/
+
+#pragma once
+
+static inline void GetScaleAndCenterPos(
+		int baseCX, int baseCY, int windowCX, int windowCY,
+		int &x, int &y, float &scale)
+{
+	double windowAspect, baseAspect;
+	int newCX, newCY;
+
+	windowAspect = double(windowCX) / double(windowCY);
+	baseAspect   = double(baseCX)   / double(baseCY);
+
+	if (windowAspect > baseAspect) {
+		scale = float(windowCY) / float(baseCY);
+		newCX = int(double(windowCY) * baseAspect);
+		newCY = windowCY;
+	} else {
+		scale = float(windowCX) / float(baseCX);
+		newCX = windowCX;
+		newCY = int(float(windowCX) / baseAspect);
+	}
+
+	x = windowCX/2 - newCX/2;
+	y = windowCY/2 - newCY/2;
+}

+ 1 - 1
obs/forms/OBSBasic.ui

@@ -433,7 +433,7 @@
   </action>
   <action name="actionSourceProperties">
    <property name="enabled">
-    <bool>false</bool>
+    <bool>true</bool>
    </property>
    <property name="icon">
     <iconset resource="obs.qrc">

+ 48 - 0
obs/forms/OBSBasicProperties.ui

@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>OBSBasicProperties</class>
+ <widget class="QDialog" name="OBSBasicProperties">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>841</width>
+    <height>479</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Dialog</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <item>
+    <widget class="OBSQTDisplay" name="preview" native="true">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="minimumSize">
+      <size>
+       <width>20</width>
+       <height>20</height>
+      </size>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>OBSQTDisplay</class>
+   <extends>QWidget</extends>
+   <header>qt-display.hpp</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>

+ 244 - 0
obs/properties-view.cpp

@@ -0,0 +1,244 @@
+#include <QFormLayout>
+#include <QLabel>
+#include <QCheckBox>
+#include <QLineEdit>
+#include <QSpinBox>
+#include <QDoubleSpinBox>
+#include <QComboBox>
+#include "qt-wrappers.hpp"
+#include "properties-view.hpp"
+#include <string>
+
+using namespace std;
+
+OBSPropertiesView::OBSPropertiesView(OBSData settings_,
+		obs_properties_t properties_, void *obj_,
+		PropertiesUpdateCallback callback_)
+	: QScrollArea (nullptr),
+	  properties  (properties_),
+	  settings    (settings_),
+	  obj         (obj_),
+	  callback    (callback_)
+{
+	widget = new QWidget();
+
+	QFormLayout *layout = new QFormLayout;
+	widget->setLayout(layout);
+
+	QSizePolicy policy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+	widget->setSizePolicy(policy);
+	layout->setSizeConstraint(QLayout::SetMaximumSize);
+	layout->setLabelAlignment(Qt::AlignRight);
+
+	obs_property_t property = obs_properties_first(properties);
+
+	while (property) {
+		AddProperty(property, layout);
+		obs_property_next(&property);
+	}
+
+	setWidget(widget);
+	setSizePolicy(policy);
+}
+
+QWidget *OBSPropertiesView::NewWidget(obs_property_t prop, QWidget *widget,
+		const char *signal)
+{
+	WidgetInfo *info = new WidgetInfo(this, prop, widget);
+	connect(widget, signal, info, SLOT(ControlChanged()));
+	children.push_back(std::move(unique_ptr<WidgetInfo>(info)));
+	return widget;
+}
+
+QWidget *OBSPropertiesView::AddCheckbox(obs_property_t prop)
+{
+	const char *desc = obs_property_description(prop);
+
+	return NewWidget(prop, new QCheckBox(QT_UTF8(desc)),
+			SIGNAL(stateChanged(int)));
+}
+
+QWidget *OBSPropertiesView::AddText(obs_property_t prop)
+{
+	return NewWidget(prop, new QLineEdit(),
+			SIGNAL(textEdited(const QString &)));
+}
+
+void OBSPropertiesView::AddPath(obs_property_t prop, QFormLayout *layout)
+{
+	/* TODO */
+	UNUSED_PARAMETER(prop);
+	UNUSED_PARAMETER(layout);
+}
+
+QWidget *OBSPropertiesView::AddInt(obs_property_t prop)
+{
+	QSpinBox *spin = new QSpinBox();
+	spin->setMinimum(obs_property_int_min(prop));
+	spin->setMaximum(obs_property_int_max(prop));
+	spin->setSingleStep(obs_property_int_step(prop));
+
+	return NewWidget(prop, spin, SIGNAL(valueChanged(int)));
+}
+
+QWidget *OBSPropertiesView::AddFloat(obs_property_t prop)
+{
+	QDoubleSpinBox *spin = new QDoubleSpinBox();
+	spin->setMinimum(obs_property_float_min(prop));
+	spin->setMaximum(obs_property_float_max(prop));
+	spin->setSingleStep(obs_property_float_step(prop));
+
+	return NewWidget(prop, spin, SIGNAL(valueChanged(double)));
+}
+
+QWidget *OBSPropertiesView::AddList(obs_property_t prop)
+{
+	QComboBox *combo        = new QComboBox();
+	obs_combo_type   type   = obs_property_list_type(prop);
+	size_t           count  = obs_property_list_item_count(prop);
+
+	for (size_t i = 0; i < count; i++) {
+		const char *name  = obs_property_list_item_name(prop, i);
+		const char *val   = obs_property_list_item_value(prop, i);
+		combo->addItem(QT_UTF8(name), QT_UTF8(val));
+	}
+
+	if (type == OBS_COMBO_TYPE_EDITABLE) {
+		combo->setEditable(true);
+		return NewWidget(prop, combo,
+				SLOT(editTextChanged(const QString &)));
+	}
+
+	return NewWidget(prop, combo, SIGNAL(currentIndexChanged(int)));
+}
+
+void OBSPropertiesView::AddProperty(obs_property_t property,
+		QFormLayout *layout)
+{
+	obs_property_type type = obs_property_get_type(property);
+
+	QWidget *widget = nullptr;
+
+	switch (type) {
+	case OBS_PROPERTY_INVALID:
+		return;
+	case OBS_PROPERTY_BOOL:
+		widget = AddCheckbox(property);
+		break;
+	case OBS_PROPERTY_INT:
+		widget = AddInt(property);
+		break;
+	case OBS_PROPERTY_FLOAT:
+		widget = AddFloat(property);
+		break;
+	case OBS_PROPERTY_TEXT:
+		widget = AddText(property);
+		break;
+	case OBS_PROPERTY_PATH:
+		AddPath(property, layout);
+		break;
+	case OBS_PROPERTY_LIST:
+		widget = AddList(property);
+		break;
+	case OBS_PROPERTY_COLOR:
+		/* TODO */
+		break;
+	}
+
+	if (!widget)
+		return;
+
+	QLabel *label = nullptr;
+	if (type != OBS_PROPERTY_BOOL)
+		label = new QLabel(QT_UTF8(obs_property_description(property)));
+
+	layout->addRow(label, widget);
+}
+
+void WidgetInfo::BoolChanged(const char *setting)
+{
+	QCheckBox *checkbox = static_cast<QCheckBox*>(widget);
+	obs_data_setbool(view->settings, setting,
+			checkbox->checkState() == Qt::Checked);
+}
+
+void WidgetInfo::IntChanged(const char *setting)
+{
+	QSpinBox *spin = static_cast<QSpinBox*>(widget);
+	obs_data_setint(view->settings, setting, spin->value());
+}
+
+void WidgetInfo::FloatChanged(const char *setting)
+{
+	QDoubleSpinBox *spin = static_cast<QDoubleSpinBox*>(widget);
+	obs_data_setdouble(view->settings, setting, spin->value());
+}
+
+void WidgetInfo::TextChanged(const char *setting)
+{
+	QLineEdit *edit = static_cast<QLineEdit*>(widget);
+	obs_data_setstring(view->settings, setting, QT_TO_UTF8(edit->text()));
+}
+
+void WidgetInfo::PathChanged(const char *setting)
+{
+	/* TODO */
+	UNUSED_PARAMETER(setting);
+}
+
+void WidgetInfo::ListChanged(const char *setting)
+{
+	QComboBox        *combo = static_cast<QComboBox*>(widget);
+	obs_combo_format format = obs_property_list_format(property);
+	obs_combo_type   type   = obs_property_list_type(property);
+
+	string val;
+	if (type == OBS_COMBO_TYPE_EDITABLE) {
+		val = QT_TO_UTF8(combo->currentText());
+	} else {
+		int index = combo->currentIndex();
+		if (index != -1) {
+			QVariant variant = combo->itemData(index);
+			val = QT_TO_UTF8(variant.toString());
+		}
+	}
+
+	switch (format) {
+	case OBS_COMBO_FORMAT_INVALID:
+		return;
+	case OBS_COMBO_FORMAT_INT:
+		obs_data_setint(view->settings, setting, stol(val));
+		break;
+	case OBS_COMBO_FORMAT_FLOAT:
+		obs_data_setdouble(view->settings, setting, stod(val));
+		break;
+	case OBS_COMBO_FORMAT_STRING:
+		obs_data_setstring(view->settings, setting, val.c_str());
+		break;
+	}
+}
+
+void WidgetInfo::ColorChanged(const char *setting)
+{
+	/* TODO */
+	UNUSED_PARAMETER(setting);
+}
+
+void WidgetInfo::ControlChanged()
+{
+	const char        *setting = obs_property_name(property);
+	obs_property_type type     = obs_property_get_type(property);
+
+	switch (type) {
+	case OBS_PROPERTY_INVALID: return;
+	case OBS_PROPERTY_BOOL:    BoolChanged(setting); break;
+	case OBS_PROPERTY_INT:     IntChanged(setting); break;
+	case OBS_PROPERTY_FLOAT:   FloatChanged(setting); break;
+	case OBS_PROPERTY_TEXT:    TextChanged(setting); break;
+	case OBS_PROPERTY_PATH:    PathChanged(setting); break;
+	case OBS_PROPERTY_LIST:    ListChanged(setting); break;
+	case OBS_PROPERTY_COLOR:   ColorChanged(setting); break;
+	}
+
+	view->callback(view->obj, view->settings);
+}

+ 77 - 0
obs/properties-view.hpp

@@ -0,0 +1,77 @@
+#pragma once
+
+#include <QScrollArea>
+#include <obs.hpp>
+#include <vector>
+#include <memory>
+
+class QFormLayout;
+class OBSPropertiesView;
+
+typedef void (*PropertiesUpdateCallback)(void *obj, obs_data_t settings);
+
+/* ------------------------------------------------------------------------- */
+
+class WidgetInfo : public QObject {
+	Q_OBJECT
+
+private:
+	OBSPropertiesView *view;
+	obs_property_t    property;
+	QWidget           *widget;
+
+	void BoolChanged(const char *setting);
+	void IntChanged(const char *setting);
+	void FloatChanged(const char *setting);
+	void TextChanged(const char *setting);
+	void PathChanged(const char *setting);
+	void ListChanged(const char *setting);
+	void ColorChanged(const char *setting);
+
+public:
+	inline WidgetInfo(OBSPropertiesView *view_, obs_property_t prop,
+			QWidget *widget_)
+		: view(view_), property(prop), widget(widget_)
+	{}
+
+public slots:
+	void ControlChanged();
+};
+
+/* ------------------------------------------------------------------------- */
+
+class OBSPropertiesView : public QScrollArea {
+	Q_OBJECT
+
+	friend class WidgetInfo;
+
+private:
+	QWidget                                  *widget;
+	obs_properties_t                         properties;
+	OBSData                                  settings;
+	void                                     *obj;
+	PropertiesUpdateCallback                 callback;
+	std::vector<std::unique_ptr<WidgetInfo>> children;
+
+	QWidget *NewWidget(obs_property_t prop, QWidget *widget,
+			const char *signal);
+
+	QWidget *AddCheckbox(obs_property_t prop);
+	QWidget *AddText(obs_property_t prop);
+	void     AddPath(obs_property_t prop, QFormLayout *layout);
+	QWidget *AddInt(obs_property_t prop);
+	QWidget *AddFloat(obs_property_t prop);
+	QWidget *AddList(obs_property_t prop);
+
+	void AddProperty(obs_property_t property, QFormLayout *layout);
+
+public:
+	OBSPropertiesView(OBSData settings,
+			obs_properties_t properties,
+			void *obj, PropertiesUpdateCallback callback);
+
+	inline ~OBSPropertiesView()
+	{
+		obs_properties_destroy(properties);
+	}
+};

+ 24 - 18
obs/window-basic-main.cpp

@@ -29,7 +29,9 @@
 #include "window-basic-settings.hpp"
 #include "window-namedialog.hpp"
 #include "window-basic-main.hpp"
+#include "window-basic-properties.hpp"
 #include "qt-wrappers.hpp"
+#include "display-helpers.hpp"
 
 #include "ui_OBSBasic.h"
 
@@ -43,6 +45,7 @@ OBSBasic::OBSBasic(QWidget *parent)
 	  outputTest    (NULL),
 	  sceneChanging (false),
 	  resizeTimer   (0),
+	  properties    (nullptr),
 	  ui            (new Ui::OBSBasic)
 {
 	ui->setupUi(this);
@@ -51,7 +54,7 @@ OBSBasic::OBSBasic(QWidget *parent)
 static inline bool HasAudioDevices(const char *source_id)
 {
 	const char *output_id = source_id;
-	obs_properties_t props = obs_source_properties(
+	obs_properties_t props = obs_get_source_properties(
 			OBS_SOURCE_TYPE_INPUT, output_id, App()->GetLocale());
 	size_t count = 0;
 
@@ -182,6 +185,9 @@ void OBSBasic::OBSInit()
 
 OBSBasic::~OBSBasic()
 {
+	if (properties)
+		delete properties;
+
 	/* free the lists before shutting down to remove the scene/item
 	 * references */
 	ui->sources->clear();
@@ -499,26 +505,13 @@ void OBSBasic::ResetAudioDevices()
 
 void OBSBasic::ResizePreview(uint32_t cx, uint32_t cy)
 {
-	double targetAspect, baseAspect;
 	QSize  targetSize;
 
 	/* resize preview panel to fix to the top section of the window */
-	targetSize   = ui->preview->size();
-	targetAspect = double(targetSize.width()) / double(targetSize.height());
-	baseAspect   = double(cx) / double(cy);
-
-	if (targetAspect > baseAspect) {
-		previewScale = float(targetSize.height()) / float(cy);
-		cx = targetSize.height() * baseAspect;
-		cy = targetSize.height();
-	} else {
-		previewScale = float(targetSize.width()) / float(cx);
-		cx = targetSize.width();
-		cy = targetSize.width() / baseAspect;
-	}
-
-	previewX = targetSize.width() /2 - cx/2;
-	previewY = targetSize.height()/2 - cy/2;
+	targetSize = ui->preview->size();
+	GetScaleAndCenterPos(int(cx), int(cy),
+			targetSize.width(), targetSize.height(),
+			previewX, previewY, previewScale);
 
 	if (isVisible()) {
 		if (resizeTimer)
@@ -769,6 +762,14 @@ void OBSBasic::on_actionRemoveSource_triggered()
 
 void OBSBasic::on_actionSourceProperties_triggered()
 {
+	OBSSceneItem item = GetCurrentSceneItem();
+	OBSSource source = obs_sceneitem_getsource(item);
+
+	if (source) {
+		delete properties;
+		properties = new OBSBasicProperties(this, source);
+		properties->Init();
+	}
 }
 
 void OBSBasic::on_actionSourceUp_triggered()
@@ -915,3 +916,8 @@ config_t OBSBasic::Config() const
 {
 	return basicConfig;
 }
+
+void OBSBasic::UnloadProperties()
+{
+	properties = nullptr;
+}

+ 5 - 0
obs/window-basic-main.hpp

@@ -21,6 +21,7 @@
 #include <unordered_map>
 #include <memory>
 #include "window-main.hpp"
+#include "window-basic-properties.hpp"
 
 #include <util/util.hpp>
 
@@ -42,6 +43,8 @@ private:
 
 	ConfigFile   basicConfig;
 
+	OBSBasicProperties *properties;
+
 	void GetFPSCommon(uint32_t &num, uint32_t &den) const;
 	void GetFPSInteger(uint32_t &num, uint32_t &den) const;
 	void GetFPSFraction(uint32_t &num, uint32_t &den) const;
@@ -93,6 +96,8 @@ public:
 	void SaveProject();
 	void LoadProject();
 
+	void UnloadProperties();
+
 protected:
 	virtual void closeEvent(QCloseEvent *event) override;
 	virtual void changeEvent(QEvent *event) override;

+ 127 - 0
obs/window-basic-properties.cpp

@@ -0,0 +1,127 @@
+/******************************************************************************
+    Copyright (C) 2014 by Hugh Bailey <[email protected]>
+
+    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
+    the Free Software Foundation, either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    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/>.
+******************************************************************************/
+
+#include "obs-app.hpp"
+#include "window-basic-properties.hpp"
+#include "window-basic-main.hpp"
+#include "qt-wrappers.hpp"
+#include "display-helpers.hpp"
+
+using namespace std;
+
+OBSBasicProperties::OBSBasicProperties(QWidget *parent, OBSSource source_)
+	: QDialog       (parent),
+	  main          (qobject_cast<OBSBasic*>(parent)),
+	  resizeTimer   (0),
+	  ui            (new Ui::OBSBasicProperties),
+	  source        (source_),
+	  removedSignal (obs_source_signalhandler(source), "remove",
+	                 OBSBasicProperties::SourceRemoved, this)
+{
+	setAttribute(Qt::WA_DeleteOnClose);
+
+	ui->setupUi(this);
+
+	OBSData settings = obs_source_getsettings(source);
+	obs_data_release(settings);
+
+	view = new OBSPropertiesView(settings,
+			obs_source_properties(source, App()->GetLocale()),
+			source, (PropertiesUpdateCallback)obs_source_update);
+
+	layout()->addWidget(view);
+	layout()->setAlignment(view, Qt::AlignRight);
+	view->show();
+}
+
+void OBSBasicProperties::SourceRemoved(void *data, calldata_t params)
+{
+	QMetaObject::invokeMethod(static_cast<OBSBasicProperties*>(data),
+			"close");
+
+	UNUSED_PARAMETER(params);
+}
+
+void OBSBasicProperties::DrawPreview(void *data, uint32_t cx, uint32_t cy)
+{
+	OBSBasicProperties *window = static_cast<OBSBasicProperties*>(data);
+
+	if (!window->source)
+		return;
+
+	uint32_t sourceCX = obs_source_getwidth(window->source);
+	uint32_t sourceCY = obs_source_getheight(window->source);
+
+	int   x, y;
+	float scale;
+
+	GetScaleAndCenterPos(sourceCX, sourceCY, cx, cy, x, y, scale);
+
+	gs_matrix_push();
+	gs_matrix_scale3f(scale, scale, 1.0f);
+	gs_matrix_translate3f(-x, -y, 0.0f);
+	obs_source_video_render(window->source);
+	gs_matrix_pop();
+}
+
+void OBSBasicProperties::closeEvent(QCloseEvent *event)
+{
+	main->UnloadProperties();
+	UNUSED_PARAMETER(event);
+}
+
+void OBSBasicProperties::resizeEvent(QResizeEvent *event)
+{
+	if (isVisible()) {
+		if (resizeTimer)
+			killTimer(resizeTimer);
+		resizeTimer = startTimer(100);
+	}
+
+	UNUSED_PARAMETER(event);
+}
+
+void OBSBasicProperties::timerEvent(QTimerEvent *event)
+{
+	if (event->timerId() == resizeTimer) {
+		killTimer(resizeTimer);
+		resizeTimer = 0;
+
+		QSize size = ui->preview->size();
+		obs_display_resize(display, size.width(), size.height());
+	}
+}
+
+void OBSBasicProperties::Init()
+{
+	gs_init_data init_data = {};
+
+	show();
+	App()->processEvents();
+
+	QSize previewSize = ui->preview->size();
+	init_data.cx      = uint32_t(previewSize.width());
+	init_data.cy      = uint32_t(previewSize.height());
+	init_data.format  = GS_RGBA;
+	QTToGSWindow(ui->preview->winId(), init_data.window);
+
+	display = obs_display_create(&init_data);
+
+	if (display)
+		obs_display_add_draw_callback(display,
+				OBSBasicProperties::DrawPreview, this);
+}

+ 56 - 0
obs/window-basic-properties.hpp

@@ -0,0 +1,56 @@
+/******************************************************************************
+    Copyright (C) 2014 by Hugh Bailey <[email protected]>
+
+    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
+    the Free Software Foundation, either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    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/>.
+******************************************************************************/
+
+#pragma once
+
+#include <QDialog>
+#include <memory>
+
+#include <obs.hpp>
+
+#include "properties-view.hpp"
+
+class OBSBasic;
+
+#include "ui_OBSBasicProperties.h"
+
+class OBSBasicProperties : public QDialog {
+	Q_OBJECT
+
+private:
+	OBSBasic   *main;
+	int        resizeTimer;
+
+	std::unique_ptr<Ui::OBSBasicProperties> ui;
+	OBSSource  source;
+	OBSDisplay display;
+	OBSSignal  removedSignal;
+	OBSPropertiesView *view;
+
+	static void SourceRemoved(void *data, calldata_t params);
+	static void DrawPreview(void *data, uint32_t cx, uint32_t cy);
+
+public:
+	OBSBasicProperties(QWidget *parent, OBSSource source_);
+
+	void Init();
+
+protected:
+	virtual void closeEvent(QCloseEvent *event) override;
+	virtual void resizeEvent(QResizeEvent *event) override;
+	virtual void timerEvent(QTimerEvent *event) override;
+};

+ 2 - 2
obs/window-basic-settings.cpp

@@ -374,9 +374,9 @@ void OBSBasicSettings::LoadAudioDevices()
 	const char *input_id  = App()->InputAudioSource();
 	const char *output_id = App()->OutputAudioSource();
 
-	obs_properties_t input_props = obs_source_properties(
+	obs_properties_t input_props = obs_get_source_properties(
 			OBS_SOURCE_TYPE_INPUT, input_id, App()->GetLocale());
-	obs_properties_t output_props = obs_source_properties(
+	obs_properties_t output_props = obs_get_source_properties(
 			OBS_SOURCE_TYPE_INPUT, output_id, App()->GetLocale());
 
 	if (input_props) {

+ 14 - 0
plugins/win-capture/monitor-capture.c

@@ -138,6 +138,18 @@ static void monitor_capture_render(void *data, effect_t effect)
 	dc_capture_render(&capture->data, capture->opaque_effect);
 }
 
+static uint32_t monitor_capture_width(void *data)
+{
+	struct monitor_capture *capture = data;
+	return capture->data.width;
+}
+
+static uint32_t monitor_capture_height(void *data)
+{
+	struct monitor_capture *capture = data;
+	return capture->data.height;
+}
+
 struct obs_source_info monitor_capture_info = {
 	.id           = "monitor_capture",
 	.type         = OBS_SOURCE_TYPE_INPUT,
@@ -145,6 +157,8 @@ struct obs_source_info monitor_capture_info = {
 	.getname      = monitor_capture_getname,
 	.create       = monitor_capture_create,
 	.destroy      = monitor_capture_destroy,
+	.getwidth     = monitor_capture_width,
+	.getheight    = monitor_capture_height,
 	.defaults     = monitor_capture_defaults,
 	.video_render = monitor_capture_render,
 	.video_tick   = monitor_capture_tick

+ 44 - 7
plugins/win-wasapi/win-wasapi.cpp

@@ -44,6 +44,8 @@ class WASAPISource {
 
 	bool ProcessCaptureData();
 
+	inline void Start();
+	inline void Stop();
 	void Reconnect();
 
 	bool InitDevice(IMMDeviceEnumerator *enumerator);
@@ -55,11 +57,13 @@ class WASAPISource {
 
 	bool TryInitialize();
 
+	void UpdateSettings(obs_data_t settings);
+
 public:
 	WASAPISource(obs_data_t settings, obs_source_t source_, bool input);
 	inline ~WASAPISource();
 
-	void UpdateSettings(obs_data_t settings);
+	void Update(obs_data_t settings);
 };
 
 WASAPISource::WASAPISource(obs_data_t settings, obs_source_t source_,
@@ -81,6 +85,11 @@ WASAPISource::WASAPISource(obs_data_t settings, obs_source_t source_,
 	if (!receiveSignal.Valid())
 		throw "Could not create receive signal";
 
+	Start();
+}
+
+inline void WASAPISource::Start()
+{
 	if (!TryInitialize()) {
 		blog(LOG_INFO, "[WASAPISource::WASAPISource] "
 		               "Device '%s' not found.  Waiting for device",
@@ -89,18 +98,25 @@ WASAPISource::WASAPISource(obs_data_t settings, obs_source_t source_,
 	}
 }
 
-inline WASAPISource::~WASAPISource()
+inline void WASAPISource::Stop()
 {
-	if (active)
-		blog(LOG_INFO, "WASAPI: Device '%s' Terminated",
-				device_name.c_str());
-
 	SetEvent(stopSignal);
 
-	if (active)
+	if (active) {
+		blog(LOG_INFO, "WASAPI: Device '%s' Terminated",
+				device_name.c_str());
 		WaitForSingleObject(captureThread, INFINITE);
+	}
+
 	if (reconnecting)
 		WaitForSingleObject(reconnectThread, INFINITE);
+
+	ResetEvent(stopSignal);
+}
+
+inline WASAPISource::~WASAPISource()
+{
+	Stop();
 }
 
 void WASAPISource::UpdateSettings(obs_data_t settings)
@@ -110,6 +126,20 @@ void WASAPISource::UpdateSettings(obs_data_t settings)
 	isDefaultDevice = _strcmpi(device_id.c_str(), "default") == 0;
 }
 
+void WASAPISource::Update(obs_data_t settings)
+{
+	string newDevice = obs_data_getstring(settings, "device_id");
+	bool restart = newDevice.compare(device_id) != 0;
+
+	if (restart)
+		Stop();
+
+	UpdateSettings(settings);
+
+	if (restart)
+		Start();
+}
+
 bool WASAPISource::InitDevice(IMMDeviceEnumerator *enumerator)
 {
 	HRESULT res;
@@ -431,6 +461,11 @@ static void DestroyWASAPISource(void *obj)
 	delete static_cast<WASAPISource*>(obj);
 }
 
+static void UpdateWASAPISource(void *obj, obs_data_t settings)
+{
+	static_cast<WASAPISource*>(obj)->Update(settings);
+}
+
 static obs_properties_t GetWASAPIProperties(const char *locale, bool input)
 {
 	obs_properties_t props = obs_properties_create();
@@ -477,6 +512,7 @@ void RegisterWASAPIInput()
 	info.getname         = GetWASAPIInputName;
 	info.create          = CreateWASAPIInput;
 	info.destroy         = DestroyWASAPISource;
+	info.update          = UpdateWASAPISource;
 	info.defaults        = GetWASAPIDefaults;
 	info.properties      = GetWASAPIPropertiesInput;
 	obs_register_source(&info);
@@ -491,6 +527,7 @@ void RegisterWASAPIOutput()
 	info.getname         = GetWASAPIOutputName;
 	info.create          = CreateWASAPIOutput;
 	info.destroy         = DestroyWASAPISource;
+	info.update          = UpdateWASAPISource;
 	info.defaults        = GetWASAPIDefaults;
 	info.properties      = GetWASAPIPropertiesOutput;
 	obs_register_source(&info);

+ 76 - 0
vs/2013/obs-studio/obs-studio.vcxproj

@@ -20,6 +20,7 @@
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\..\libobs\obs-ui.h" />
+    <ClInclude Include="..\..\..\obs\display-helpers.hpp" />
     <ClInclude Include="..\..\..\obs\platform.hpp" />
     <CustomBuild Include="..\..\..\obs\qt-display.hpp">
       <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
@@ -39,6 +40,24 @@
       <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
       <Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp"  -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB "-I.\..\..\..\libobs" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I.\..\..\..\obs"</Command>
     </CustomBuild>
+    <CustomBuild Include="..\..\..\obs\properties-view.hpp">
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Moc%27ing properties-view.hpp...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp"  -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB "-I.\..\..\..\libobs" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I.\..\..\..\obs"</Command>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing properties-view.hpp...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp"  -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB "-I.\..\..\..\libobs" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I.\..\..\..\obs"</Command>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Moc%27ing properties-view.hpp...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp"  -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB "-I.\..\..\..\libobs" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I.\..\..\..\obs"</Command>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Moc%27ing properties-view.hpp...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp"  -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB "-I.\..\..\..\libobs" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I.\..\..\..\obs"</Command>
+    </CustomBuild>
     <ClInclude Include="..\..\..\obs\qt-wrappers.hpp" />
     <CustomBuild Include="..\..\..\obs\window-namedialog.hpp">
       <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
@@ -94,8 +113,27 @@
       <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
       <Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp"  -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB "-I.\..\..\..\libobs" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I.\..\..\..\obs"</Command>
     </CustomBuild>
+    <CustomBuild Include="..\..\..\obs\window-basic-properties.hpp">
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Moc%27ing window-basic-properties.hpp...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp"  -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB "-I.\..\..\..\libobs" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I.\..\..\..\obs"</Command>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing window-basic-properties.hpp...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp"  -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB "-I.\..\..\..\libobs" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I.\..\..\..\obs"</Command>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Moc%27ing window-basic-properties.hpp...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp"  -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB "-I.\..\..\..\libobs" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I.\..\..\..\obs"</Command>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Moc%27ing window-basic-properties.hpp...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp"  -DUNICODE -DWIN32 -DWIN64 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_WIDGETS_LIB "-I.\..\..\..\libobs" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtWidgets" "-I.\..\..\..\obs"</Command>
+    </CustomBuild>
     <ClInclude Include="GeneratedFiles\ui_NameDialog.h" />
     <ClInclude Include="GeneratedFiles\ui_OBSBasic.h" />
+    <ClInclude Include="GeneratedFiles\ui_OBSBasicProperties.h" />
     <ClInclude Include="GeneratedFiles\ui_OBSBasicSettings.h" />
     <CustomBuild Include="..\..\..\obs\window-basic-main.hpp">
       <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
@@ -137,14 +175,20 @@
   <ItemGroup>
     <ClCompile Include="..\..\..\obs\obs-app.cpp" />
     <ClCompile Include="..\..\..\obs\platform-windows.cpp" />
+    <ClCompile Include="..\..\..\obs\properties-view.cpp" />
     <ClCompile Include="..\..\..\obs\qt-wrappers.cpp" />
     <ClCompile Include="..\..\..\obs\window-basic-main.cpp" />
+    <ClCompile Include="..\..\..\obs\window-basic-properties.cpp" />
     <ClCompile Include="..\..\..\obs\window-basic-settings.cpp" />
     <ClCompile Include="..\..\..\obs\window-namedialog.cpp" />
     <ClCompile Include="GeneratedFiles\Debug\moc_obs-app.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
     </ClCompile>
+    <ClCompile Include="GeneratedFiles\Debug\moc_properties-view.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+    </ClCompile>
     <ClCompile Include="GeneratedFiles\Debug\moc_qt-display.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
@@ -153,6 +197,10 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
     </ClCompile>
+    <ClCompile Include="GeneratedFiles\Debug\moc_window-basic-properties.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+    </ClCompile>
     <ClCompile Include="GeneratedFiles\Debug\moc_window-basic-settings.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
@@ -179,6 +227,10 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
     </ClCompile>
+    <ClCompile Include="GeneratedFiles\Release\moc_properties-view.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+    </ClCompile>
     <ClCompile Include="GeneratedFiles\Release\moc_qt-display.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
@@ -187,6 +239,10 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
     </ClCompile>
+    <ClCompile Include="GeneratedFiles\Release\moc_window-basic-properties.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+    </ClCompile>
     <ClCompile Include="GeneratedFiles\Release\moc_window-basic-settings.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
@@ -354,6 +410,26 @@
       <Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\uic.exe" -o ".\GeneratedFiles\ui_%(Filename).h" "%(FullPath)"</Command>
     </CustomBuild>
   </ItemGroup>
+  <ItemGroup>
+    <CustomBuild Include="..\..\..\obs\forms\OBSBasicProperties.ui">
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\bin\uic.exe;%(AdditionalInputs)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Uic%27ing %(Identity)...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\GeneratedFiles\ui_%(Filename).h;%(Outputs)</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(QTDIR)\bin\uic.exe" -o ".\GeneratedFiles\ui_%(Filename).h" "%(FullPath)"</Command>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\uic.exe;%(AdditionalInputs)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Uic%27ing %(Identity)...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\GeneratedFiles\ui_%(Filename).h;%(Outputs)</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\uic.exe" -o ".\GeneratedFiles\ui_%(Filename).h" "%(FullPath)"</Command>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(QTDIR)\bin\uic.exe;%(AdditionalInputs)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Uic%27ing %(Identity)...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\GeneratedFiles\ui_%(Filename).h;%(Outputs)</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(QTDIR)\bin\uic.exe" -o ".\GeneratedFiles\ui_%(Filename).h" "%(FullPath)"</Command>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\uic.exe;%(AdditionalInputs)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Uic%27ing %(Identity)...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\GeneratedFiles\ui_%(Filename).h;%(Outputs)</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\uic.exe" -o ".\GeneratedFiles\ui_%(Filename).h" "%(FullPath)"</Command>
+    </CustomBuild>
+  </ItemGroup>
   <PropertyGroup Label="Globals">
     <ProjectGuid>{B12702AD-ABFB-343A-A199-8E24837244A3}</ProjectGuid>
     <Keyword>Qt4VSv1.0</Keyword>

+ 33 - 0
vs/2013/obs-studio/obs-studio.vcxproj.filters

@@ -65,6 +65,15 @@
     <CustomBuild Include="..\..\..\obs\window-basic-settings.hpp">
       <Filter>Header Files</Filter>
     </CustomBuild>
+    <CustomBuild Include="..\..\..\obs\forms\OBSBasicProperties.ui">
+      <Filter>Form Files</Filter>
+    </CustomBuild>
+    <CustomBuild Include="..\..\..\obs\window-basic-properties.hpp">
+      <Filter>Header Files</Filter>
+    </CustomBuild>
+    <CustomBuild Include="..\..\..\obs\properties-view.hpp">
+      <Filter>Header Files</Filter>
+    </CustomBuild>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="..\..\..\obs\platform.hpp">
@@ -85,6 +94,12 @@
     <ClInclude Include="..\..\..\libobs\obs-ui.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\..\obs\display-helpers.hpp">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="GeneratedFiles\ui_OBSBasicProperties.h">
+      <Filter>Generated Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="..\..\..\obs\obs-app.cpp">
@@ -144,6 +159,24 @@
     <ClCompile Include="GeneratedFiles\Release\moc_window-basic-settings.cpp">
       <Filter>Generated Files\Release</Filter>
     </ClCompile>
+    <ClCompile Include="..\..\..\obs\window-basic-properties.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="GeneratedFiles\Debug\moc_window-basic-properties.cpp">
+      <Filter>Generated Files\Debug</Filter>
+    </ClCompile>
+    <ClCompile Include="GeneratedFiles\Release\moc_window-basic-properties.cpp">
+      <Filter>Generated Files\Release</Filter>
+    </ClCompile>
+    <ClCompile Include="GeneratedFiles\Debug\moc_properties-view.cpp">
+      <Filter>Generated Files\Debug</Filter>
+    </ClCompile>
+    <ClCompile Include="GeneratedFiles\Release\moc_properties-view.cpp">
+      <Filter>Generated Files\Release</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\obs\properties-view.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <Image Include="..\..\..\obs\forms\images\add.ico">