mirror of
https://github.com/PancakeTAS/lsfg-vk.git
synced 2026-04-22 02:11:43 +00:00
refactor(cleanup): enable profile creation/deletion through ui
This commit is contained in:
parent
effd469b5e
commit
08f7c77fd2
13 changed files with 99 additions and 571 deletions
|
|
@ -25,19 +25,19 @@ namespace ls {
|
|||
/// game profile configuration
|
||||
struct GameConf {
|
||||
/// name of the profile
|
||||
std::string name;
|
||||
std::string name{"Profile"};
|
||||
/// optional activation string/array
|
||||
std::vector<std::string> active_in;
|
||||
/// gpu to use (in case of multiple)
|
||||
std::optional<std::string> gpu;
|
||||
/// multiplier for frame generation
|
||||
size_t multiplier;
|
||||
size_t multiplier{2};
|
||||
/// non-inverted flow scale
|
||||
float flow_scale;
|
||||
float flow_scale{1.00F};
|
||||
/// use performance mode
|
||||
bool performance_mode;
|
||||
bool performance_mode{false};
|
||||
/// pacing method
|
||||
Pacing pacing;
|
||||
Pacing pacing{Pacing::None};
|
||||
};
|
||||
|
||||
/// automatically updating configuration
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ set(UI_SOURCES
|
|||
"src/main.cpp")
|
||||
|
||||
set(UI_RESOURCES
|
||||
"rsc/panes/CenteredDialog.qml"
|
||||
"rsc/panes/Group.qml"
|
||||
"rsc/panes/GroupEntry.qml"
|
||||
"rsc/panes/Pane.qml"
|
||||
|
|
|
|||
|
|
@ -13,48 +13,41 @@ ApplicationWindow {
|
|||
minimumHeight: 400
|
||||
visible: true
|
||||
|
||||
Dialog {
|
||||
id: dialog_name
|
||||
title: "(...)"
|
||||
standardButtons: Dialog.Ok | Dialog.Cancel
|
||||
CenteredDialog {
|
||||
id: create_dialog
|
||||
name: "Create New Profile"
|
||||
onConfirm: backend.createProfile(create_name.text)
|
||||
|
||||
modal: true
|
||||
dim: true
|
||||
x: (parent.width - width) / 2
|
||||
y: (parent.height - height) / 2
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
spacing: 8
|
||||
|
||||
TextField {
|
||||
Layout.fillWidth: true
|
||||
|
||||
id: nameField
|
||||
placeholderText: "Choose a profile name"
|
||||
selectByMouse: true
|
||||
focus: true
|
||||
}
|
||||
TextField {
|
||||
Layout.fillWidth: true
|
||||
id: create_name
|
||||
placeholderText: "Choose a profile name"
|
||||
focus: true
|
||||
}
|
||||
}
|
||||
|
||||
Dialog {
|
||||
id: dialog_confirm
|
||||
title: "Confirm Deletion"
|
||||
standardButtons: Dialog.Ok | Dialog.Cancel
|
||||
CenteredDialog {
|
||||
id: rename_dialog
|
||||
name: "Rename Profile"
|
||||
onConfirm: backend.renameProfile(rename_name.text)
|
||||
|
||||
modal: true
|
||||
dim: true
|
||||
x: (parent.width - width) / 2
|
||||
y: (parent.height - height) / 2
|
||||
TextField {
|
||||
Layout.fillWidth: true
|
||||
id: rename_name
|
||||
placeholderText: "Choose a profile name"
|
||||
focus: true
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
spacing: 8
|
||||
CenteredDialog {
|
||||
id: delete_dialog
|
||||
name: "Confirm Deletion"
|
||||
onConfirm: backend.deleteProfile()
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: "Are you sure you want to delete the selected profile?"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: "Are you sure you want to delete the selected profile?"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -84,27 +77,24 @@ ApplicationWindow {
|
|||
Layout.fillWidth: true
|
||||
text: "Create New Profile"
|
||||
onClicked: {
|
||||
dialog_name.title = "Create New Profile"
|
||||
nameField.text = ""
|
||||
|
||||
dialog_name.open()
|
||||
create_name.text = ""
|
||||
create_dialog.open()
|
||||
}
|
||||
}
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
text: "Rename Profile"
|
||||
onClicked: {
|
||||
dialog_name.title = "Rename Profile"
|
||||
nameField.text = "(...)"
|
||||
|
||||
dialog_name.open()
|
||||
var idx = backend.profiles.index(backend.profile_index, 0);
|
||||
rename_name.text = backend.profiles.data(idx);
|
||||
rename_dialog.open()
|
||||
}
|
||||
}
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
text: "Delete Profile"
|
||||
onClicked: {
|
||||
dialog_confirm.open()
|
||||
delete_dialog.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
24
lsfg-vk-ui/rsc/panes/CenteredDialog.qml
Normal file
24
lsfg-vk-ui/rsc/panes/CenteredDialog.qml
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
Dialog {
|
||||
property string name
|
||||
default property alias content: inner.children
|
||||
signal confirm()
|
||||
|
||||
id: root
|
||||
title: name
|
||||
standardButtons: Dialog.Ok | Dialog.Cancel
|
||||
onAccepted: root.confirm()
|
||||
|
||||
modal: true
|
||||
dim: true
|
||||
x: (parent.width - width) / 2
|
||||
y: (parent.height - height) / 2
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
id: inner
|
||||
spacing: 8
|
||||
}
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@ using namespace lsfgvk::ui;
|
|||
|
||||
Backend::Backend() {
|
||||
// load configuration
|
||||
ls::Configuration config{false};
|
||||
ls::Configuration config;
|
||||
config.reload();
|
||||
|
||||
this->m_global = config.getGlobalConf();
|
||||
|
|
|
|||
|
|
@ -139,6 +139,40 @@ namespace lsfgvk::ui {
|
|||
MARK_DIRTY()
|
||||
}
|
||||
|
||||
Q_INVOKABLE void createProfile(const QString& name) {
|
||||
ls::GameConf conf;
|
||||
conf.name = name.toStdString();
|
||||
this->m_profiles.push_back(std::move(conf));
|
||||
|
||||
auto& model = this->m_profile_list_model;
|
||||
model->insertRow(model->rowCount());
|
||||
model->setData(model->index(model->rowCount() - 1), name);
|
||||
|
||||
this->m_profile_index = static_cast<int>(this->m_profiles.size() - 1);
|
||||
MARK_DIRTY()
|
||||
}
|
||||
Q_INVOKABLE void renameProfile(const QString& name) {
|
||||
VALIDATE_AND_GET_PROFILE()
|
||||
conf.name = name.toStdString();
|
||||
auto& model = this->m_profile_list_model;
|
||||
model->setData(model->index(this->m_profile_index), name);
|
||||
MARK_DIRTY()
|
||||
}
|
||||
Q_INVOKABLE void deleteProfile() {
|
||||
if (!isValidProfileIndex())
|
||||
return;
|
||||
|
||||
auto& profiles = this->m_profiles;
|
||||
profiles.erase(profiles.begin() + this->m_profile_index);
|
||||
auto& model = this->m_profile_list_model;
|
||||
model->removeRow(this->m_profile_index);
|
||||
if (!this->m_profiles.empty())
|
||||
this->m_profile_index = 0;
|
||||
else
|
||||
this->m_profile_index = -1;
|
||||
MARK_DIRTY()
|
||||
}
|
||||
|
||||
#undef VALIDATE_AND_GET_PROFILE
|
||||
#undef MARK_DIRTY
|
||||
|
||||
|
|
|
|||
|
|
@ -1,95 +0,0 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// multiplier
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct Multiplier(i64);
|
||||
impl Default for Multiplier {
|
||||
fn default() -> Self { Multiplier(2) }
|
||||
}
|
||||
impl From<i64> for Multiplier {
|
||||
fn from(value: i64) -> Self { Multiplier(value) }
|
||||
}
|
||||
impl Into<f64> for Multiplier {
|
||||
fn into(self) -> f64 { self.0 as f64 }
|
||||
}
|
||||
|
||||
// flow scale
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct FlowScale(f64);
|
||||
impl Default for FlowScale {
|
||||
fn default() -> Self { FlowScale(1.0) }
|
||||
}
|
||||
impl From<f64> for FlowScale {
|
||||
fn from(value: f64) -> Self { FlowScale(value) }
|
||||
}
|
||||
impl Into<f64> for FlowScale {
|
||||
fn into(self) -> f64 { self.0 }
|
||||
}
|
||||
|
||||
// present mode
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub enum PresentMode {
|
||||
#[serde(rename = "fifo", alias = "vsync")]
|
||||
Vsync,
|
||||
#[serde(rename = "immediate")]
|
||||
Immediate,
|
||||
#[serde(rename = "mailbox")]
|
||||
Mailbox,
|
||||
}
|
||||
impl Default for PresentMode {
|
||||
fn default() -> Self { PresentMode::Vsync }
|
||||
}
|
||||
impl From<i64> for PresentMode {
|
||||
fn from(value: i64) -> Self {
|
||||
match value {
|
||||
0 => PresentMode::Vsync,
|
||||
1 => PresentMode::Mailbox,
|
||||
2 => PresentMode::Immediate,
|
||||
_ => PresentMode::Vsync,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Into<u32> for PresentMode {
|
||||
fn into(self) -> u32 {
|
||||
match self {
|
||||
PresentMode::Vsync => 0,
|
||||
PresentMode::Mailbox => 1,
|
||||
PresentMode::Immediate => 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Global configuration for the application
|
||||
#[derive(Debug, Default, Clone, Deserialize, Serialize)]
|
||||
pub struct TomlGlobal {
|
||||
pub dll: Option<String>,
|
||||
#[serde(default)]
|
||||
pub no_fp16: bool
|
||||
}
|
||||
|
||||
/// Game-specific configuration
|
||||
#[derive(Debug, Default, Clone, Deserialize, Serialize)]
|
||||
pub struct TomlGame {
|
||||
pub exe: String,
|
||||
|
||||
#[serde(default)]
|
||||
pub multiplier: Multiplier,
|
||||
#[serde(default)]
|
||||
pub flow_scale: FlowScale,
|
||||
#[serde(default)]
|
||||
pub performance_mode: bool,
|
||||
#[serde(default)]
|
||||
pub hdr_mode: bool,
|
||||
#[serde(default)]
|
||||
pub experimental_present_mode: PresentMode
|
||||
}
|
||||
|
||||
/// Main configuration structure
|
||||
#[derive(Debug, Default, Clone, Deserialize, Serialize)]
|
||||
pub struct TomlConfig {
|
||||
pub version: i64,
|
||||
#[serde(default)]
|
||||
pub global: TomlGlobal,
|
||||
#[serde(default)]
|
||||
pub game: Vec<TomlGame>
|
||||
}
|
||||
43
ui/src/ui.rs
43
ui/src/ui.rs
|
|
@ -1,43 +0,0 @@
|
|||
use adw::{self, subclass::prelude::ObjectSubclassIsExt};
|
||||
use gtk::prelude::{WidgetExt, EditableExt, GtkWindowExt};
|
||||
|
||||
use crate::config;
|
||||
use crate::wrapper;
|
||||
|
||||
pub mod entry_handler;
|
||||
pub mod main_handler;
|
||||
pub mod sidebar_handler;
|
||||
|
||||
pub fn build(app: &adw::Application) {
|
||||
// create the main window
|
||||
let window = wrapper::Window::new(app);
|
||||
window.set_application(Some(app));
|
||||
let imp = window.imp();
|
||||
|
||||
// load profiles from configuration
|
||||
let config = config::get_config().unwrap();
|
||||
for game in config.game.iter() {
|
||||
let entry = wrapper::entry::Entry::new();
|
||||
entry.set_exe(game.exe.clone());
|
||||
entry_handler::add_entry(entry, imp.sidebar.imp().profiles.clone());
|
||||
}
|
||||
|
||||
if let Some(dll_path) = config.global.dll {
|
||||
imp.main.imp().dll.imp().entry.set_text(&dll_path);
|
||||
}
|
||||
imp.main.imp().no_fp16.imp().switch.set_active(config.global.no_fp16);
|
||||
|
||||
// register handlers on sidebar pane.
|
||||
sidebar_handler::register_signals(&imp.sidebar, imp.main.clone());
|
||||
|
||||
// register handlers on main pane.
|
||||
main_handler::register_signals(imp.sidebar.clone(), &imp.main);
|
||||
|
||||
// activate the first profile if available
|
||||
if let Some(entry) = imp.sidebar.imp().profiles.row_at_index(0) {
|
||||
entry.activate();
|
||||
}
|
||||
|
||||
// present the window
|
||||
window.present();
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
use adw::subclass::prelude::ObjectSubclassIsExt;
|
||||
use gtk::{gio, glib::object::CastNone, prelude::{ButtonExt, ListBoxRowExt, WidgetExt}};
|
||||
|
||||
use crate::{config, wrapper::entry, STATE};
|
||||
|
||||
///
|
||||
/// Register signals for removing presets when adding a new entry.
|
||||
///
|
||||
pub fn add_entry(entry_: entry::Entry, profiles_: gtk::ListBox) {
|
||||
let entry = entry_.clone();
|
||||
let profiles = profiles_.clone();
|
||||
entry_.imp().delete.connect_clicked(move |btn| {
|
||||
// prompt for confirmation
|
||||
let dialog = gtk::AlertDialog::builder()
|
||||
.message("Delete Profile")
|
||||
.detail("Are you sure you want to delete this profile?")
|
||||
.buttons(vec!["Cancel".to_string(), "Delete".to_string()])
|
||||
.cancel_button(0)
|
||||
.default_button(1)
|
||||
.modal(true)
|
||||
.build();
|
||||
let window = btn.root()
|
||||
.and_downcast::<gtk::Window>()
|
||||
.expect("Button root is not a Window");
|
||||
|
||||
let profiles = profiles.clone();
|
||||
let entry = entry.clone();
|
||||
dialog.choose(Some(&window), gio::Cancellable::NONE, move |result| {
|
||||
if result.is_err() || result.unwrap() != 1 {
|
||||
return;
|
||||
}
|
||||
|
||||
// remove config entry
|
||||
let _ = config::edit_config(|config| {
|
||||
config.game.remove(entry.index() as usize);
|
||||
});
|
||||
|
||||
// remove ui entry
|
||||
profiles.remove(&entry);
|
||||
|
||||
// select next entry
|
||||
let state = STATE.get().unwrap().clone();
|
||||
if let Ok(mut state) = state.write() {
|
||||
state.selected_game = None;
|
||||
}
|
||||
|
||||
if let Some(entry) = profiles.row_at_index(0) {
|
||||
entry.activate();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
profiles_.append(&entry_);
|
||||
}
|
||||
|
|
@ -1,176 +0,0 @@
|
|||
use adw::subclass::prelude::ObjectSubclassIsExt;
|
||||
use gtk::{gio::{self, prelude::FileExt}, glib::object::CastNone, prelude::{ButtonExt, EditableExt, GtkWindowExt, ListBoxRowExt, RangeExt, WidgetExt}};
|
||||
|
||||
use crate::{config, utils, wrapper::{entry, pane, popup}, STATE};
|
||||
|
||||
// update the currently selected game configuration
|
||||
fn update_game<F: FnOnce(&mut config::TomlGame)>(update: F) {
|
||||
if let Ok(state) = STATE.get().unwrap().try_read() {
|
||||
if let Some(selected_game) = state.selected_game {
|
||||
let _ = config::edit_config(|config| {
|
||||
update(&mut config.game[selected_game])
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Register signals for preset preferences.
|
||||
///
|
||||
pub fn register_signals(sidebar_: pane::PaneSidebar, main: &pane::PaneMain) {
|
||||
let main = main.imp();
|
||||
let exe = main.profile_name.imp();
|
||||
let multiplier = main.multiplier.imp();
|
||||
let flow_scale = main.flow_scale.imp();
|
||||
let performance_mode = main.performance_mode.imp();
|
||||
let hdr_mode = main.hdr_mode.imp();
|
||||
let experimental_present_mode = main.experimental_present_mode.imp();
|
||||
|
||||
// preset opts
|
||||
let sidebar = sidebar_.clone();
|
||||
exe.entry.connect_changed(move |entry| {
|
||||
let mut exe = entry.text().to_string();
|
||||
if exe.trim().is_empty() {
|
||||
exe = "new preset".to_string();
|
||||
}
|
||||
|
||||
// rename list entry
|
||||
let row_option = sidebar.imp().profiles.selected_row()
|
||||
.and_downcast::<entry::Entry>();
|
||||
|
||||
if let Some(row) = row_option {
|
||||
row.set_exe(exe.clone());
|
||||
}
|
||||
|
||||
// update the game configuration
|
||||
update_game(|conf| {
|
||||
conf.exe = exe;
|
||||
});
|
||||
});
|
||||
multiplier.number.connect_value_changed(|dropdown| {
|
||||
update_game(|conf| {
|
||||
conf.multiplier = (dropdown.value() as i64).into();
|
||||
})
|
||||
});
|
||||
flow_scale.slider.connect_value_changed(|slider| {
|
||||
update_game(|conf| {
|
||||
conf.flow_scale = (slider.value() / 100.0).into();
|
||||
});
|
||||
});
|
||||
performance_mode.switch.connect_state_notify(|switch| {
|
||||
update_game(|conf| {
|
||||
conf.performance_mode = switch.state();
|
||||
});
|
||||
});
|
||||
hdr_mode.switch.connect_state_notify(|switch| {
|
||||
update_game(|conf| {
|
||||
conf.hdr_mode = switch.state();
|
||||
});
|
||||
});
|
||||
experimental_present_mode.dropdown.connect_selected_notify(|dropdown| {
|
||||
update_game(|conf| {
|
||||
conf.experimental_present_mode = match dropdown.selected() {
|
||||
0 => config::PresentMode::Vsync,
|
||||
1 => config::PresentMode::Mailbox,
|
||||
2 => config::PresentMode::Immediate,
|
||||
_ => config::PresentMode::Vsync,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
// global opts
|
||||
let dll = main.dll.imp();
|
||||
dll.entry.connect_changed(|entry| {
|
||||
let _ = config::edit_config(|config| {
|
||||
let mut text = entry.text().to_string();
|
||||
if text.trim().is_empty() {
|
||||
config.global.dll = None;
|
||||
} else {
|
||||
if text.contains("~") {
|
||||
let home = std::env::var("HOME").unwrap_or_else(|_| String::from("/"));
|
||||
text = text.replace("~", &home);
|
||||
}
|
||||
config.global.dll = Some(text);
|
||||
}
|
||||
});
|
||||
});
|
||||
let no_fp16 = main.no_fp16.imp();
|
||||
no_fp16.switch.connect_state_notify(|switch| {
|
||||
let _ = config::edit_config(|config| {
|
||||
config.global.no_fp16 = switch.state();
|
||||
});
|
||||
});
|
||||
|
||||
// utility buttons
|
||||
let entry = dll.entry.clone();
|
||||
dll.btn.connect_clicked(move |btn| {
|
||||
let dialog = gtk::FileDialog::new();
|
||||
dialog.set_title("Select Lossless.dll");
|
||||
|
||||
let filter = gtk::FileFilter::new();
|
||||
filter.set_name(Some("Lossless.dll"));
|
||||
filter.add_pattern("Lossless.dll");
|
||||
|
||||
let filters = gio::ListStore::new::<gtk::FileFilter>();
|
||||
filters.append(&filter);
|
||||
dialog.set_filters(Some(&filters));
|
||||
dialog.set_default_filter(Some(&filter));
|
||||
|
||||
let window = btn.root()
|
||||
.and_downcast::<gtk::Window>()
|
||||
.unwrap();
|
||||
let entry = entry.clone();
|
||||
dialog.open(Some(&window), gio::Cancellable::NONE, move |result| {
|
||||
if result.is_err() || result.as_ref().unwrap().path().is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
let path = result.unwrap().path().unwrap();
|
||||
let path_str = path.to_string_lossy().to_string();
|
||||
|
||||
entry.set_text(&path_str);
|
||||
let _ = config::edit_config(|config| {
|
||||
config.global.dll = Some(path_str);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
let entry = exe.entry.clone();
|
||||
exe.btn.connect_clicked(move |btn| {
|
||||
let window = btn.root()
|
||||
.and_downcast::<gtk::ApplicationWindow>()
|
||||
.unwrap()
|
||||
.application()
|
||||
.unwrap();
|
||||
let picker = popup::ProcessPicker::new();
|
||||
picker.set_application(Some(&window));
|
||||
|
||||
let list = picker.imp().processes.clone();
|
||||
let processes = utils::find_vulkan_processes().unwrap_or_default();
|
||||
for process in &processes {
|
||||
let entry = popup::ProcessEntry::new();
|
||||
entry.set_exe(process.0.clone());
|
||||
list.append(&entry);
|
||||
}
|
||||
|
||||
let entry = entry.clone();
|
||||
let picker_ = picker.clone();
|
||||
picker.imp().processes.connect_row_activated(move |_, row| {
|
||||
let comm_str = processes[row.index() as usize].1.clone();
|
||||
|
||||
entry.set_text(&comm_str);
|
||||
update_game(|conf| {
|
||||
conf.exe = comm_str;
|
||||
});
|
||||
|
||||
picker_.close();
|
||||
});
|
||||
|
||||
let picker_ = picker.clone();
|
||||
picker.imp().close.connect_clicked(move |_| {
|
||||
picker_.close();
|
||||
});
|
||||
|
||||
picker.present();
|
||||
});
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
use adw::subclass::prelude::ObjectSubclassIsExt;
|
||||
use gtk::prelude::{ButtonExt, EditableExt, ListBoxRowExt, RangeExt, WidgetExt};
|
||||
|
||||
use crate::{config, ui::entry_handler, wrapper::{pane, entry}, STATE};
|
||||
|
||||
///
|
||||
/// Register signals for adding and selecting presets.
|
||||
///
|
||||
pub fn register_signals(sidebar_: &pane::PaneSidebar, main: pane::PaneMain) {
|
||||
// activate signal
|
||||
let state = STATE.get().unwrap().clone();
|
||||
sidebar_.imp().profiles.connect_row_activated(move |_, entry| {
|
||||
// find config entry by index
|
||||
let index = entry.index() as usize;
|
||||
let config = config::get_config();
|
||||
if config.is_err() {
|
||||
return;
|
||||
}
|
||||
let config = config.unwrap();
|
||||
let conf = config.game[index].clone();
|
||||
|
||||
// update main pane
|
||||
let main = main.imp();
|
||||
let exe = main.profile_name.imp();
|
||||
let multiplier = main.multiplier.imp();
|
||||
let flow_scale = main.flow_scale.imp();
|
||||
let performance_mode = main.performance_mode.imp();
|
||||
let hdr_mode = main.hdr_mode.imp();
|
||||
let experimental_present_mode = main.experimental_present_mode.imp();
|
||||
|
||||
// (lock state early, so the ui update doesn't override the config)
|
||||
if let Ok(mut state) = state.write() {
|
||||
exe.entry.set_text(&conf.exe);
|
||||
multiplier.number.set_value(conf.multiplier.into());
|
||||
flow_scale.slider.set_value(Into::<f64>::into(conf.flow_scale) * 100.0);
|
||||
performance_mode.switch.set_active(conf.performance_mode);
|
||||
hdr_mode.switch.set_active(conf.hdr_mode);
|
||||
experimental_present_mode.dropdown.set_selected(conf.experimental_present_mode.into());
|
||||
|
||||
// update state
|
||||
state.selected_game = Some(index);
|
||||
}
|
||||
});
|
||||
|
||||
// create signal
|
||||
let sidebar = sidebar_.clone();
|
||||
sidebar_.imp().create.connect_clicked(move |_| {
|
||||
// ensure no config entry with the same name exist
|
||||
let config = config::get_config().unwrap();
|
||||
if config.game.iter().any(|e| e.exe == "new profile") {
|
||||
return;
|
||||
}
|
||||
|
||||
// create config entry
|
||||
let mut conf_entry = config::TomlGame::default();
|
||||
conf_entry.exe = "new profile".to_string();
|
||||
let _ = config::edit_config(|config| {
|
||||
config.game.push(conf_entry.clone());
|
||||
});
|
||||
|
||||
// add entry to sidebar
|
||||
let entry = entry::Entry::new();
|
||||
entry.set_exe(conf_entry.exe);
|
||||
entry_handler::add_entry(entry.clone(), sidebar.imp().profiles.clone());
|
||||
|
||||
// select the new entry
|
||||
entry.activate();
|
||||
});
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
use procfs::{process, ProcResult};
|
||||
|
||||
pub fn find_vulkan_processes() -> ProcResult<Vec<(String, String)>> {
|
||||
let mut processes = Vec::new();
|
||||
let apps = process::all_processes()?;
|
||||
for app in apps {
|
||||
let Ok(prc) = app else { continue; };
|
||||
|
||||
// ensure vulkan is loaded
|
||||
let Ok(maps) = proc_maps::get_process_maps(prc.pid()) else {
|
||||
continue;
|
||||
};
|
||||
let result = maps.iter()
|
||||
.filter_map(|map| map.filename())
|
||||
.map(|filename| filename.to_string_lossy().to_string())
|
||||
.any(|filename| filename.to_lowercase().contains("vulkan"));
|
||||
if !result {
|
||||
continue;
|
||||
}
|
||||
|
||||
// find executed binary
|
||||
let mut exe = prc.exe()?.to_string_lossy().to_string();
|
||||
|
||||
// replace binary with exe for wine apps
|
||||
if exe.contains("wine") || exe.contains("proton") {
|
||||
let result = maps.iter()
|
||||
.filter_map(|map| map.filename())
|
||||
.map(|filename| filename.to_string_lossy().to_string())
|
||||
.find(|filename| filename.ends_with(".exe"));
|
||||
|
||||
if let Some(exe_name) = result {
|
||||
exe = exe_name;
|
||||
}
|
||||
}
|
||||
|
||||
// split off last part of the path
|
||||
exe = exe.split('/').last().unwrap_or(&exe).to_string();
|
||||
|
||||
// format process information
|
||||
let pid = prc.pid();
|
||||
let process_info = format!("PID {}: {}", pid, exe);
|
||||
processes.push((process_info, exe));
|
||||
}
|
||||
|
||||
Ok(processes)
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
use gtk::glib;
|
||||
use gtk;
|
||||
use adw;
|
||||
|
||||
pub mod process;
|
||||
pub mod process_entry;
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct ProcessPicker(ObjectSubclass<process::ProcessPicker>)
|
||||
@extends
|
||||
adw::ApplicationWindow, adw::Window,
|
||||
gtk::ApplicationWindow, gtk::Window, gtk::Widget,
|
||||
@implements
|
||||
gtk::gio::ActionGroup, gtk::gio::ActionMap,
|
||||
gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget,
|
||||
gtk::Native, gtk::Root, gtk::ShortcutManager;
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct ProcessEntry(ObjectSubclass<process_entry::ProcessEntry>)
|
||||
@extends
|
||||
gtk::ListBoxRow, gtk::Widget,
|
||||
@implements
|
||||
gtk::Accessible, gtk::Actionable, gtk::Buildable, gtk::ConstraintTarget;
|
||||
}
|
||||
|
||||
|
||||
impl ProcessPicker {
|
||||
pub fn new() -> Self {
|
||||
glib::Object::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl ProcessEntry {
|
||||
pub fn new() -> Self {
|
||||
glib::Object::new()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue