You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
387 lines
11 KiB
387 lines
11 KiB
#include "settings-window.hpp"
|
|
#include "settings-manager.hpp"
|
|
|
|
#include <linux/videodev2.h>
|
|
#include <alsa/asoundlib.h>
|
|
#include <pulse/pulseaudio.h>
|
|
#include <string>
|
|
#include <list>
|
|
#include <iostream>
|
|
#include <filesystem>
|
|
#include <stdexcept>
|
|
#include <fcntl.h>
|
|
#include <libv4l2.h>
|
|
#include <sys/dir.h>
|
|
|
|
using namespace std;
|
|
|
|
SettingsWindow::SettingsWindow(OBSManager* obs)
|
|
: mBox(Gtk::Orientation::ORIENTATION_VERTICAL, 2),
|
|
mBoxSettings(Gtk::Orientation::ORIENTATION_VERTICAL, 2),
|
|
mBoxPluginDir(Gtk::Orientation::ORIENTATION_HORIZONTAL, 2),
|
|
mLabelPluginDir("Plugin Dir: "),
|
|
mBoxOutputDir(Gtk::Orientation::ORIENTATION_HORIZONTAL, 2),
|
|
mLabelOutputDir("Saved Files Dir: "),
|
|
mCheckButtonDesktop("Enable desktop recording"),
|
|
mBoxWebcam(Gtk::Orientation::ORIENTATION_VERTICAL, 2),
|
|
mCheckButtonWebcam("Enable webcam recording"),
|
|
mBoxAudio(Gtk::Orientation::ORIENTATION_VERTICAL),
|
|
mCheckButtonAudio("Enable audio recording"),
|
|
mButtonClose("Close"),
|
|
mButtonSave("Save")
|
|
{
|
|
set_title("Settings");
|
|
set_default_size(640, 480);
|
|
set_border_width(10);
|
|
set_modal(true);
|
|
set_type_hint(Gdk::WindowTypeHint::WINDOW_TYPE_HINT_DIALOG);
|
|
|
|
settings = new SettingsManager();
|
|
|
|
signal_key_press_event().connect(
|
|
sigc::mem_fun(*this, &SettingsWindow::onKeyPressed));
|
|
|
|
mButtonClose.signal_clicked().connect(
|
|
sigc::mem_fun(*this, &SettingsWindow::onClosePressed));
|
|
mButtonSave.signal_clicked().connect(
|
|
sigc::mem_fun(*this, &SettingsWindow::onSavePressed));
|
|
|
|
mBoxSettings.set_border_width(10);
|
|
|
|
mEntryPluginDir.set_hexpand(true);
|
|
mEntryPluginDir.set_text(settings->GetWithDefault(SETTINGS_KEY_PLUGIN_DIR, SETTINGS_DEFAULT_PLUGIN_DIR));
|
|
|
|
mBoxPluginDir.set_border_width(10);
|
|
mBoxPluginDir.add(mLabelPluginDir);
|
|
mBoxPluginDir.add(mEntryPluginDir);
|
|
|
|
mEntryOutputDir.set_hexpand(true);
|
|
mEntryOutputDir.set_text(settings->GetWithDefault(SETTINGS_KEY_OUTPUT_DIR, std::filesystem::current_path()));
|
|
|
|
mBoxOutputDir.set_border_width(10);
|
|
mBoxOutputDir.add(mLabelOutputDir);
|
|
mBoxOutputDir.add(mEntryOutputDir);
|
|
|
|
mFrameDesktop.set_label("Desktop");
|
|
mFrameDesktop.set_border_width(10);
|
|
mCheckButtonDesktop.set_border_width(10);
|
|
mCheckButtonDesktop.set_active(settings->GetBoolWithDefault(SETTINGS_KEY_SCREEN_ENABLED, true));
|
|
mFrameDesktop.add(mCheckButtonDesktop);
|
|
|
|
mComboBoxVideoDevice.set_border_width(10);
|
|
mFrameWebcam.set_label("Webcam");
|
|
mFrameWebcam.set_border_width(10);
|
|
mBoxWebcam.set_border_width(10);
|
|
mCheckButtonWebcam.set_active(settings->GetBoolWithDefault(SETTINGS_KEY_WEBCAM_ENABLED, true));
|
|
mBoxWebcam.add(mCheckButtonWebcam);
|
|
mBoxWebcam.add(mComboBoxVideoDevice);
|
|
mFrameWebcam.add(mBoxWebcam);
|
|
|
|
mComboBoxAudioDevice.set_border_width(10);
|
|
mFrameAudio.set_label("Microphone");
|
|
mFrameAudio.set_border_width(10);
|
|
mBoxAudio.set_border_width(10);
|
|
mCheckButtonAudio.set_border_width(10);
|
|
mCheckButtonAudio.set_active(settings->GetBoolWithDefault(SETTINGS_KEY_AUDIO_ENABLED, true));
|
|
mBoxAudio.add(mCheckButtonAudio);
|
|
mBoxAudio.add(mComboBoxAudioDevice);
|
|
mFrameAudio.add(mBoxAudio);
|
|
|
|
mBoxSettings.add(mBoxPluginDir);
|
|
mBoxSettings.add(mBoxOutputDir);
|
|
mBoxSettings.add(mFrameDesktop);
|
|
mBoxSettings.add(mFrameWebcam);
|
|
mBoxSettings.add(mFrameAudio);
|
|
|
|
mFrameSettings.set_label("Settings");
|
|
mFrameSettings.set_border_width(10);
|
|
mFrameSettings.add(mBoxSettings);
|
|
|
|
mActionBar.pack_end(mButtonSave);
|
|
mActionBar.pack_end(mButtonClose);
|
|
|
|
mBox.add(mFrameSettings);
|
|
mBox.add(mActionBar);
|
|
|
|
mBox.show_all();
|
|
add(mBox);
|
|
}
|
|
|
|
SettingsWindow::~SettingsWindow()
|
|
{
|
|
}
|
|
|
|
void SettingsWindow::on_show()
|
|
{
|
|
Gtk::Window::on_show();
|
|
populateVideoDevices();
|
|
populatePulseAudioDevices();
|
|
}
|
|
|
|
bool SettingsWindow::onKeyPressed(GdkEventKey *event)
|
|
{
|
|
if (event->keyval == GDK_KEY_Escape)
|
|
{
|
|
close();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void SettingsWindow::onClosePressed()
|
|
{
|
|
close();
|
|
}
|
|
|
|
void SettingsWindow::onSavePressed()
|
|
{
|
|
settings->Update(SETTINGS_KEY_PLUGIN_DIR, mEntryPluginDir.get_text());
|
|
settings->Update(SETTINGS_KEY_OUTPUT_DIR, mEntryOutputDir.get_text());
|
|
settings->UpdateBool(SETTINGS_KEY_SCREEN_ENABLED, mCheckButtonDesktop.get_active());
|
|
settings->UpdateBool(SETTINGS_KEY_WEBCAM_ENABLED, mCheckButtonWebcam.get_active());
|
|
settings->Update(SETTINGS_KEY_VIDEO_DEVICE_ID, mComboBoxVideoDevice.get_active_id());
|
|
settings->UpdateBool(SETTINGS_KEY_AUDIO_ENABLED, mCheckButtonAudio.get_active());
|
|
settings->Update(SETTINGS_KEY_AUDIO_DEVICE_ID, mComboBoxAudioDevice.get_active_id());
|
|
settings->SaveAll();
|
|
close();
|
|
}
|
|
|
|
void SettingsWindow::populateVideoDevices()
|
|
{
|
|
cout << "populateVideoDevices" << endl;
|
|
mComboBoxVideoDevice.remove_all();
|
|
|
|
v4l2_capability cap;
|
|
dirent *dp;
|
|
|
|
auto dirp = opendir("/sys/class/video4linux");
|
|
if (!dirp)
|
|
throw runtime_error("Failed to get video devices");
|
|
|
|
while ((dp = readdir(dirp)) != nullptr)
|
|
{
|
|
int fd;
|
|
if (dp->d_type == DT_DIR)
|
|
continue;
|
|
|
|
auto name = string(dp->d_name);
|
|
if (name.find("video") == string::npos)
|
|
continue;
|
|
|
|
auto device = "/dev/" + name;
|
|
|
|
if ((fd = v4l2_open(device.c_str(), O_RDWR | O_NONBLOCK)) == -1)
|
|
{
|
|
cout << "Failed to open device: " << device << endl;
|
|
continue;
|
|
}
|
|
|
|
if (v4l2_ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1)
|
|
{
|
|
cout << "Failed to query device: " << device << endl;
|
|
v4l2_close(fd);
|
|
continue;
|
|
}
|
|
|
|
auto n = string(reinterpret_cast<char*>(cap.card));
|
|
n += " (" + device + ")";
|
|
|
|
mComboBoxVideoDevice.append(device, n);
|
|
if (settings->Get(SETTINGS_KEY_VIDEO_DEVICE_ID) == device)
|
|
mComboBoxVideoDevice.set_active_id(device);
|
|
|
|
v4l2_close(fd);
|
|
}
|
|
}
|
|
|
|
void SettingsWindow::populateALSAAudioDevices()
|
|
{
|
|
cout << "populateALSAAudioDevices" << endl;
|
|
mComboBoxAudioDevice.remove_all();
|
|
|
|
void **hints;
|
|
void **hint;
|
|
char *name = nullptr;
|
|
char *desc = nullptr;
|
|
char *ioid = nullptr;
|
|
|
|
if (snd_device_name_hint(-1, "pcm", &hints) < 0)
|
|
throw runtime_error("Failed reading audio devices");
|
|
|
|
hint = hints;
|
|
while (*hint != NULL)
|
|
{
|
|
name = snd_device_name_get_hint(*hint, "NAME");
|
|
desc = snd_device_name_get_hint(*hint, "DESC");
|
|
ioid = snd_device_name_get_hint(*hint, "IOID");
|
|
|
|
if (name == NULL || strstr(name, "front:") == NULL)
|
|
goto next;
|
|
|
|
if (ioid != NULL && strcmp(ioid, "Input") != 0)
|
|
goto next;
|
|
|
|
mComboBoxAudioDevice.append(name, desc);
|
|
if (settings->Get(SETTINGS_KEY_AUDIO_DEVICE_ID) == name)
|
|
mComboBoxAudioDevice.set_active_id(name);
|
|
|
|
next:
|
|
if (name != NULL)
|
|
{
|
|
free(name);
|
|
name = NULL;
|
|
}
|
|
|
|
if (desc != NULL)
|
|
{
|
|
free(desc);
|
|
desc = NULL;
|
|
}
|
|
|
|
if (ioid != NULL)
|
|
{
|
|
free(ioid);
|
|
ioid = NULL;
|
|
}
|
|
|
|
hint++;
|
|
}
|
|
|
|
snd_device_name_free_hint((void**)hints);
|
|
}
|
|
|
|
class PADevice
|
|
{
|
|
public:
|
|
string name;
|
|
string desc;
|
|
uint32_t index;
|
|
};
|
|
|
|
// Pulse Audio
|
|
void pa_state_cb(pa_context *c, void *userdata)
|
|
{
|
|
int *pa_ready = (int*)userdata;
|
|
pa_context_state_t state = pa_context_get_state(c);
|
|
|
|
switch (state)
|
|
{
|
|
case PA_CONTEXT_UNCONNECTED:
|
|
case PA_CONTEXT_CONNECTING:
|
|
case PA_CONTEXT_AUTHORIZING:
|
|
case PA_CONTEXT_SETTING_NAME:
|
|
default:
|
|
break;
|
|
case PA_CONTEXT_FAILED:
|
|
case PA_CONTEXT_TERMINATED:
|
|
*pa_ready = 2;
|
|
break;
|
|
case PA_CONTEXT_READY:
|
|
*pa_ready = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata)
|
|
{
|
|
list<PADevice> *dlist = (list<PADevice>*)userdata;
|
|
|
|
if (eol != 0 || l->monitor_source == PA_INVALID_INDEX)
|
|
return;
|
|
|
|
dlist->push_back({
|
|
.name = l->name,
|
|
.desc = l->description,
|
|
.index = l->index
|
|
});
|
|
}
|
|
|
|
void pa_sourcelist_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata)
|
|
{
|
|
list<PADevice> *dlist = (list<PADevice>*)userdata;
|
|
|
|
if (eol != 0 || l->monitor_of_sink != PA_INVALID_INDEX)
|
|
return;
|
|
|
|
dlist->push_back({
|
|
.name = l->name,
|
|
.desc = l->description,
|
|
.index = l->index
|
|
});
|
|
}
|
|
|
|
void SettingsWindow::populatePulseAudioDevices()
|
|
{
|
|
cout << "populatePulseAudioDevices" << endl;
|
|
mComboBoxAudioDevice.remove_all();
|
|
|
|
int state = 0;
|
|
int pa_ready = 0;
|
|
|
|
auto input = new list<PADevice>();
|
|
auto output = new list<PADevice>();
|
|
|
|
pa_operation *pa_op;
|
|
pa_mainloop *pa_ml = pa_mainloop_new();
|
|
pa_mainloop_api *pa_mlapi = pa_mainloop_get_api(pa_ml);
|
|
pa_context *pa_ctx = pa_context_new(pa_mlapi, "screenrecorder");
|
|
|
|
pa_context_connect(pa_ctx, NULL, (pa_context_flags_t)0, NULL);
|
|
pa_context_set_state_callback(pa_ctx, pa_state_cb, &pa_ready);
|
|
|
|
for (;;)
|
|
{
|
|
if (pa_ready == 0)
|
|
{
|
|
pa_mainloop_iterate(pa_ml, 1, NULL);
|
|
continue;
|
|
}
|
|
|
|
if (pa_ready == 2)
|
|
{
|
|
pa_context_disconnect(pa_ctx);
|
|
pa_context_unref(pa_ctx);
|
|
pa_mainloop_free(pa_ml);
|
|
throw runtime_error("Failed to get pulse audio devices");
|
|
}
|
|
|
|
switch (state)
|
|
{
|
|
case 0:
|
|
pa_op = pa_context_get_sink_info_list(pa_ctx, pa_sinklist_cb, output);
|
|
state++;
|
|
break;
|
|
case 1:
|
|
if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE)
|
|
pa_operation_unref(pa_op);
|
|
|
|
pa_op = pa_context_get_source_info_list(pa_ctx, pa_sourcelist_cb, input);
|
|
state++;
|
|
break;
|
|
case 2:
|
|
if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE)
|
|
{
|
|
pa_operation_unref(pa_op);
|
|
pa_context_disconnect(pa_ctx);
|
|
pa_context_unref(pa_ctx);
|
|
pa_mainloop_free(pa_ml);
|
|
|
|
for (auto device : *input)
|
|
{
|
|
mComboBoxAudioDevice.append(device.name, device.desc);
|
|
if (settings->Get(SETTINGS_KEY_AUDIO_DEVICE_ID) == device.name)
|
|
mComboBoxAudioDevice.set_active_id(device.name);
|
|
}
|
|
|
|
return;
|
|
}
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
pa_mainloop_iterate(pa_ml, 1, NULL);
|
|
}
|
|
}
|
|
|