/****************************************************************************** Copyright (C) 2013 by Hugh Bailey 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 . ******************************************************************************/ #include #include "obs-app.hpp" #include "settings-basic.hpp" #include "window-basic-settings.hpp" #include "wx-wrappers.hpp" #include "platform.hpp" #include #include using namespace std; class BasicVideoData : public BasicSettingsData { ConnectorList connections; int AddRes(uint32_t cx, uint32_t cy); void LoadOther(); void LoadResolutionData(); void LoadFPSData(); void LoadFPSCommon(); void LoadFPSInteger(); void LoadFPSFraction(); void LoadFPSNanoseconds(); void ResetScaleList(uint32_t cx, uint32_t cy); void RendererChanged(wxCommandEvent &event); void BaseResChanged(wxCommandEvent &event); void OutputResChanged(wxCommandEvent &event); void FPSChanged(wxCommandEvent &event); void SaveOther(); void SaveFPSData(); void SaveFPSCommon(); void SaveFPSInteger(); void SaveFPSFraction(); void SaveFPSNanoseconds(); virtual void SetChanged() { BasicSettingsData::SetChanged(); window->videoChangedText->Show(); } public: BasicVideoData(OBSBasicSettings *window); virtual void Apply(); }; struct BaseLexer { lexer lex; public: inline BaseLexer() {lexer_init(&lex);} inline ~BaseLexer() {lexer_free(&lex);} operator lexer*() {return &lex;} }; /* parses "[width]x[height]", string, i.e. 1024x768 */ static bool ConvertTextRes(wxComboBox *combo, uint32_t &cx, uint32_t &cy) { string str = combo->GetValue().ToStdString(); BaseLexer lex; lexer_start(lex, str.c_str()); base_token token; /* parse width */ if (!lexer_getbasetoken(lex, &token, IGNORE_WHITESPACE)) return false; if (token.type != BASETOKEN_DIGIT) return false; cx = std::stoul(token.text.array); /* parse 'x' */ if (!lexer_getbasetoken(lex, &token, IGNORE_WHITESPACE)) return false; if (strref_cmpi(&token.text, "x") != 0) return false; /* parse height */ if (!lexer_getbasetoken(lex, &token, IGNORE_WHITESPACE)) return false; if (token.type != BASETOKEN_DIGIT) return false; cy = std::stoul(token.text.array); /* shouldn't be any more tokens after this */ if (lexer_getbasetoken(lex, &token, IGNORE_WHITESPACE)) return false; return true; } int BasicVideoData::AddRes(uint32_t cx, uint32_t cy) { stringstream res; res << cx << "x" << cy; return window->baseResList->Append(res.str()); } void BasicVideoData::LoadOther() { const char *renderer = config_get_string(GetGlobalConfig(), "Video", "Renderer"); window->rendererList->Clear(); window->rendererList->Append("OpenGL"); #ifdef _WIN32 window->rendererList->Append("Direct3D 11"); #endif int sel = window->rendererList->FindString(renderer); if (sel == wxNOT_FOUND) sel = 0; window->rendererList->SetSelection(sel); } static uint64_t append_uint32_t(uint64_t first, uint64_t second) { return (first << 32) | second; } void BasicVideoData::LoadResolutionData() { window->baseResList->Clear(); uint32_t cx = config_get_uint(GetGlobalConfig(), "Video", "BaseCX"); uint32_t cy = config_get_uint(GetGlobalConfig(), "Video", "BaseCY"); vector monitors; GetMonitors(monitors); unordered_set resolutions; for (size_t i = 0; i < monitors.size(); i++) { uint64_t res = append_uint32_t(monitors[i].cx, monitors[i].cy); if(resolutions.emplace(res).second) AddRes(monitors[i].cx, monitors[i].cy); } stringstream res; res << cx << "x" << cy; window->baseResList->SetValue(res.str()); ResetScaleList(cx, cy); cx = config_get_uint(GetGlobalConfig(), "Video", "OutputCX"); cy = config_get_uint(GetGlobalConfig(), "Video", "OutputCY"); res.str(string()); res.clear(); res << cx << "x" << cy; window->outputResList->SetValue(res.str()); } void BasicVideoData::LoadFPSData() { const char *fpsType = config_get_string(GetGlobalConfig(), "Video", "FPSType"); LoadFPSCommon(); LoadFPSInteger(); LoadFPSFraction(); LoadFPSNanoseconds(); if (!fpsType) return; else if (strcmp(fpsType, "Common") == 0) window->fpsTypeList->SetSelection(0); else if (strcmp(fpsType, "Integer") == 0) window->fpsTypeList->SetSelection(1); else if (strcmp(fpsType, "Fraction") == 0) window->fpsTypeList->SetSelection(2); else if (strcmp(fpsType, "Nanoseconds") == 0) window->fpsTypeList->SetSelection(3); } void BasicVideoData::LoadFPSCommon() { const char *val = config_get_string(GetGlobalConfig(), "Video", "FPSCommon"); int sel = window->fpsCommonList->FindString(val); if (sel == wxNOT_FOUND) sel = window->fpsCommonList->FindString("30"); window->fpsCommonList->SetSelection(sel); } void BasicVideoData::LoadFPSInteger() { window->fpsTypeList->SetSelection(1); int val = config_get_int(GetGlobalConfig(), "Video", "FPSInt"); window->fpsIntegerScroller->SetValue(val); } void BasicVideoData::LoadFPSFraction() { window->fpsTypeList->SetSelection(2); int num = config_get_int(GetGlobalConfig(), "Video", "FPSNum"); int den = config_get_int(GetGlobalConfig(), "Video", "FPSDen"); window->fpsNumeratorScroller->SetValue(num); window->fpsDenominatorScroller->SetValue(den); } void BasicVideoData::LoadFPSNanoseconds() { window->fpsTypeList->SetSelection(3); int val = config_get_int(GetGlobalConfig(), "Video", "FPSNS"); window->fpsNanosecondsScroller->SetValue(val); } /* some nice default output resolution vals */ static const double vals[] = { 1.0, 1.25, (1.0/0.75), 1.5, (1.0/0.6), 1.75, 2.0, 2.25, 2.5, 2.75, 3.0 }; static const size_t numVals = sizeof(vals)/sizeof(double); void BasicVideoData::ResetScaleList(uint32_t cx, uint32_t cy) { window->outputResList->Clear(); for (size_t i = 0; i < numVals; i++) { stringstream res; res << uint32_t(double(cx) / vals[i]); res << "x"; res << uint32_t(double(cy) / vals[i]); window->outputResList->Append(res.str()); } if (numVals) { stringstream str; str << cx << "x" << cy; window->outputResList->SetValue(str.str()); } } #define ADD_CONNECT(control, event, func) \ connections.Add(window->control, event, \ wxCommandEventHandler(BasicVideoData::func), \ NULL, this) BasicVideoData::BasicVideoData(OBSBasicSettings *window) : BasicSettingsData(window) { LoadResolutionData(); LoadFPSData(); LoadOther(); /* load connectors after loading data to prevent them from triggering */ ADD_CONNECT(baseResList, wxEVT_TEXT, BaseResChanged); ADD_CONNECT(outputResList, wxEVT_TEXT, OutputResChanged); ADD_CONNECT(rendererList, wxEVT_COMBOBOX, RendererChanged); ADD_CONNECT(fpsCommonList, wxEVT_COMBOBOX, FPSChanged); ADD_CONNECT(fpsIntegerScroller, wxEVT_SPINCTRL, FPSChanged); ADD_CONNECT(fpsNumeratorScroller, wxEVT_SPINCTRL, FPSChanged); ADD_CONNECT(fpsDenominatorScroller, wxEVT_SPINCTRL, FPSChanged); ADD_CONNECT(fpsNanosecondsScroller, wxEVT_SPINCTRL, FPSChanged); ADD_CONNECT(fpsIntegerScroller, wxEVT_TEXT, FPSChanged); ADD_CONNECT(fpsNumeratorScroller, wxEVT_TEXT, FPSChanged); ADD_CONNECT(fpsDenominatorScroller, wxEVT_TEXT, FPSChanged); ADD_CONNECT(fpsNanosecondsScroller, wxEVT_TEXT, FPSChanged); ADD_CONNECT(fpsTypeList, wxEVT_CHOICEBOOK_PAGE_CHANGED, FPSChanged); window->videoChangedText->Hide(); } void BasicVideoData::RendererChanged(wxCommandEvent &event) { SetChanged(); window->videoChangedText->SetLabel(WXStr("Settings.ProgramRestart")); window->videoChangedText->Show(); } void BasicVideoData::FPSChanged(wxCommandEvent &event) { SetChanged(); } void BasicVideoData::BaseResChanged(wxCommandEvent &event) { uint32_t cx, cy; if (!ConvertTextRes(window->baseResList, cx, cy)) { window->videoChangedText->SetLabel( WXStr("Settings.Video.InvalidResolution")); window->videoChangedText->Show(); return; } SetChanged(); ResetScaleList(cx, cy); } void BasicVideoData::OutputResChanged(wxCommandEvent &event) { uint32_t cx, cy; if (!ConvertTextRes(window->outputResList, cx, cy)) { window->videoChangedText->SetLabel( WXStr("Settings.Video.InvalidResolution")); window->videoChangedText->Show(); return; } SetChanged(); } void BasicVideoData::SaveOther() { int sel = window->rendererList->GetSelection(); if (sel == wxNOT_FOUND) return; wxString renderer = window->rendererList->GetString(sel); config_set_string(GetGlobalConfig(), "Video", "Renderer", renderer.c_str()); } void BasicVideoData::SaveFPSData() { int id = window->fpsTypeList->GetCurrentPage()->GetId(); const char *type; switch (id) { case ID_FPSPANEL_COMMON: type = "Common"; break; case ID_FPSPANEL_INTEGER: type = "Integer"; break; case ID_FPSPANEL_FRACTION: type = "Fraction"; break; case ID_FPSPANEL_NANOSECONDS: type = "Nanoseconds"; break; } config_set_string(GetGlobalConfig(), "Video", "FPSType", type); SaveFPSCommon(); SaveFPSInteger(); SaveFPSFraction(); SaveFPSNanoseconds(); } void BasicVideoData::SaveFPSCommon() { int sel = window->fpsCommonList->GetSelection(); wxString str = window->fpsCommonList->GetString(sel); config_set_string(GetGlobalConfig(), "Video", "FPSCommon", str.c_str()); } void BasicVideoData::SaveFPSInteger() { int val = window->fpsIntegerScroller->GetValue(); config_set_int(GetGlobalConfig(), "Video", "FPSInt", val); } void BasicVideoData::SaveFPSFraction() { int num = window->fpsNumeratorScroller->GetValue(); int den = window->fpsDenominatorScroller->GetValue(); config_set_int(GetGlobalConfig(), "Video", "FPSNum", num); config_set_int(GetGlobalConfig(), "Video", "FPSDen", den); } void BasicVideoData::SaveFPSNanoseconds() { int val = window->fpsNanosecondsScroller->GetValue(); config_set_int(GetGlobalConfig(), "Video", "FPSNS", val); } void BasicVideoData::Apply() { uint32_t cx, cy; if (!ConvertTextRes(window->baseResList, cx, cy)) { config_set_uint(GetGlobalConfig(), "Video", "BaseCX", cx); config_set_uint(GetGlobalConfig(), "Video", "BaseCY", cy); } if (ConvertTextRes(window->outputResList, cx, cy)) { config_set_uint(GetGlobalConfig(), "Video", "OutputCX", cx); config_set_uint(GetGlobalConfig(), "Video", "OutputCY", cy); } SaveOther(); SaveFPSData(); config_save(GetGlobalConfig()); /* TODO: If resolutiosn/fps were chaned, reset video */ SetSaved(); } BasicSettingsData *CreateBasicVideoSettings(OBSBasicSettings *window) { return new BasicVideoData(window); }