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

#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);
}
}