Running games list (Closes #398)
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
<layout>
|
||||
<include src="t_tab_title.xml" />
|
||||
<include src="../t_group_box.xml" />
|
||||
|
||||
<elements>
|
||||
<TabTitle translation="GAMES" icon="dashboard/games.svg" />
|
||||
<rectangle macro="group_box" id="running_games_list_parent" align_self="center" />
|
||||
<div id="game_list_parent" align_items="center" />
|
||||
</elements>
|
||||
</layout>
|
||||
@@ -31,7 +31,7 @@
|
||||
<label translation="DISPLAY_BRIGHTNESS" />
|
||||
<Slider id="slider_brightness" width="300" height="24" min_value="0" max_value="140" />
|
||||
|
||||
<label translation="LIST_OF_PROCESSES" />
|
||||
<label translation="PROCESS_LIST" />
|
||||
<div id="list_parent" flex_direction="column" gap="8">
|
||||
<!-- filled at runtime -->
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<layout>
|
||||
<include src="../t_group_box.xml" />
|
||||
|
||||
<elements>
|
||||
<div id="list_parent" gap="8" flex_direction="row" flex_wrap="wrap" justify_content="center" />
|
||||
<div flex_direction="column">
|
||||
<div id="list_parent" gap="8" flex_direction="row" flex_wrap="wrap" justify_content="center" />
|
||||
</div>
|
||||
</elements>
|
||||
</layout>
|
||||
20
dash-frontend/assets/gui/view/running_games_list.xml
Normal file
20
dash-frontend/assets/gui/view/running_games_list.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<layout>
|
||||
<include src="../t_group_box.xml" />
|
||||
|
||||
<template name="RunningGameCell">
|
||||
<rectangle macro="group_box" flex_direction="row">
|
||||
<Button id="btn_stop" sprite_src_builtin="dashboard/remove_circle.svg" tooltip="PROCESS.STOP" />
|
||||
<Button id="btn_kill" sprite_src_builtin="dashboard/knife.svg" tooltip="PROCESS.FORCE_KILL" />
|
||||
<label id="label_name" weight="bold" />
|
||||
</rectangle>
|
||||
</template>
|
||||
|
||||
<elements>
|
||||
<div align_items="center" gap="8">
|
||||
<Button id="btn_refresh" tooltip="REFRESH" width="32" height="32" sprite_src_builtin="dashboard/refresh.svg" />
|
||||
<sprite src_builtin="dashboard/cpu.svg" width="24" height="24" />
|
||||
<label translation="GAME_LIST.RUNNING_GAMES_LIST" />
|
||||
</div>
|
||||
<div id="list_parent" gap="8" />
|
||||
</elements>
|
||||
</layout>
|
||||
@@ -111,12 +111,13 @@
|
||||
"HIDE": "Verbergen",
|
||||
"REMOVE": "Entfernen",
|
||||
"SHOW": "Anzeigen",
|
||||
"PROCESS_LIST": {},
|
||||
"FAILED_TO_LAUNCH_APPLICATION": "Fehler beim Starten der Anwendung:",
|
||||
"APPLICATION_STARTED": "Anwendung gestartet",
|
||||
"CLOSE_WINDOW": "Fenster schließen",
|
||||
"GAME_LIST": {
|
||||
"NO_GAMES_FOUND": "Keine Spiele gefunden"
|
||||
"NO_GAMES_FOUND": "Keine Spiele gefunden",
|
||||
"RUNNING_GAMES_LIST": "Liste der laufenden Spiele",
|
||||
"NO_RUNNING_GAME_FOUND": "Kein laufendes Spiel gefunden"
|
||||
},
|
||||
"TERMINATE_PROCESS": "Prozess beenden",
|
||||
"GAME_LAUNCHED": "Spiel gestartet",
|
||||
@@ -146,5 +147,11 @@
|
||||
"AUTOSTART": "Automatisch beim Start ausführen",
|
||||
"LAUNCH": "Starten"
|
||||
},
|
||||
"DISPLAY_BRIGHTNESS": "Bildschirmhelligkeit"
|
||||
"DISPLAY_BRIGHTNESS": "Bildschirmhelligkeit",
|
||||
"PROCESS_LIST": "Prozessliste",
|
||||
"REFRESH": "Aktualisieren",
|
||||
"PROCESS": {
|
||||
"STOP": "Stopp",
|
||||
"FORCE_KILL": "Erzwinge Beenden"
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@
|
||||
"APP_SETTINGS": {
|
||||
"ALLOW_SLIDING": "Stick interaction during grab",
|
||||
"ANIMATION_SPEED": "UI Animation speed",
|
||||
"UI_GRADIENT_INTENSITY": "UI Gradient intensity",
|
||||
"AUTOSTART_APPS": "Apps to run on startup",
|
||||
"BLOCK_GAME_INPUT": "Block game input",
|
||||
"BLOCK_GAME_INPUT_HELP": "Blocks all input when an overlay is hovered",
|
||||
"BLOCK_GAME_INPUT_IGNORE_WATCH": "Ignore watch when blocking input",
|
||||
@@ -52,6 +52,8 @@
|
||||
"DOUBLE_CURSOR_FIX_HELP": "Enable this if you see 2 cursors",
|
||||
"FEATURES": "Features",
|
||||
"FOCUS_FOLLOWS_MOUSE_MODE": "Mouse move on trigger touch",
|
||||
"HANDSFREE_POINTER": "Handsfree mode",
|
||||
"HANDSFREE_POINTER_HELP": "Input to use when motion\ncontrollers are unavailable.\nLeft pinch is grab, right is click.",
|
||||
"HIDE_GRAB_HELP": "Hide grab help",
|
||||
"HIDE_USERNAME": "Hide username",
|
||||
"INVERT_SCROLL_DIRECTION_X": "Invert horizontal scroll direction",
|
||||
@@ -68,11 +70,11 @@
|
||||
"NOTIFICATIONS_SOUND_ENABLED": "Notification sounds",
|
||||
"OPAQUE_BACKGROUND": "Opaque background",
|
||||
"OPTION": {
|
||||
"NONE": "None",
|
||||
"HMD_PINCH": "HMD + pinch",
|
||||
"EYE_PINCH": "Eye + pinch",
|
||||
"AUTO": "Automatic",
|
||||
"AUTO_HELP": "ScreenCopy GPU if supported,\notherwise PipeWire GPU.",
|
||||
"EYE_PINCH": "Eye + pinch",
|
||||
"HMD_PINCH": "HMD + pinch",
|
||||
"NONE": "None",
|
||||
"PIPEWIRE_HELP": "Fast GPU capture,\nstandard on all desktops.",
|
||||
"PW_FALLBACK_HELP": "Slow method with high CPU usage.\nTry in case PipeWire GPU doesn't work",
|
||||
"SCREENCOPY_GPU_HELP": "Fast, no screen share popups.\nWorks on: Hyprland, Niri, River, Sway",
|
||||
@@ -90,6 +92,7 @@
|
||||
"SPACE_DRAG_UNLOCKED": "Allow space drag on all axes",
|
||||
"SPACE_ROTATE_UNLOCKED": "Allow space rotate on all axes",
|
||||
"TROUBLESHOOTING": "Troubleshooting",
|
||||
"UI_GRADIENT_INTENSITY": "UI Gradient intensity",
|
||||
"UPRIGHT_SCREEN_FIX": "Upright screen fix",
|
||||
"UPRIGHT_SCREEN_FIX_HELP": "Fixes upright screens on some desktops",
|
||||
"USE_PASSTHROUGH": "Enable passthrough",
|
||||
@@ -100,10 +103,7 @@
|
||||
"XR_CLICK_SENSITIVITY_HELP": "Analog trigger sensitivity",
|
||||
"XR_CLICK_SENSITIVITY_RELEASE": "XR release sensitivity",
|
||||
"XR_CLICK_SENSITIVITY_RELEASE_HELP": "Must be lower than click",
|
||||
"XWAYLAND_BY_DEFAULT": "Run apps in Compatibility mode by default",
|
||||
"HANDSFREE_POINTER": "Handsfree mode",
|
||||
"HANDSFREE_POINTER_HELP": "Input to use when motion\ncontrollers are unavailable.\nLeft pinch is grab, right is click.",
|
||||
"AUTOSTART_APPS": "Apps to run on startup"
|
||||
"XWAYLAND_BY_DEFAULT": "Run apps in Compatibility mode by default"
|
||||
},
|
||||
"APPLICATION_LAUNCHER": "Application launcher",
|
||||
"APPLICATION_STARTED": "Application started",
|
||||
@@ -127,7 +127,9 @@
|
||||
"FAILED_TO_LAUNCH_APPLICATION": "Failed to launch a application:",
|
||||
"GAME_LAUNCHED": "Game launched",
|
||||
"GAME_LIST": {
|
||||
"NO_GAMES_FOUND": "No games found"
|
||||
"NO_GAMES_FOUND": "No games found",
|
||||
"RUNNING_GAMES_LIST": "List of running games",
|
||||
"NO_RUNNING_GAME_FOUND": "No running game found"
|
||||
},
|
||||
"GAMES": "Games",
|
||||
"GENERAL_SETTINGS": "General settings",
|
||||
@@ -140,6 +142,12 @@
|
||||
"POPUP_ADD_DISPLAY": {
|
||||
"RESOLUTION": "Resolution"
|
||||
},
|
||||
"PROCESS": {
|
||||
"STOP": "Stop",
|
||||
"FORCE_KILL": "Force-kill"
|
||||
},
|
||||
"PROCESS_LIST": "Process list",
|
||||
"REFRESH": "Refresh",
|
||||
"REMOVE": "Remove",
|
||||
"SETTINGS": "Settings",
|
||||
"SHOW": "Show",
|
||||
|
||||
@@ -111,12 +111,13 @@
|
||||
"HIDE": "Ocultar",
|
||||
"REMOVE": "Eliminar",
|
||||
"SHOW": "Mostrar",
|
||||
"PROCESS_LIST": {},
|
||||
"FAILED_TO_LAUNCH_APPLICATION": "No se pudo iniciar la aplicación:",
|
||||
"APPLICATION_STARTED": "Aplicación iniciada",
|
||||
"CLOSE_WINDOW": "Cerrar ventana",
|
||||
"GAME_LIST": {
|
||||
"NO_GAMES_FOUND": "No se encontraron juegos"
|
||||
"NO_GAMES_FOUND": "No se encontraron juegos",
|
||||
"RUNNING_GAMES_LIST": "Lista de juegos en ejecución",
|
||||
"NO_RUNNING_GAME_FOUND": "No se encontró ningún juego en ejecución"
|
||||
},
|
||||
"TERMINATE_PROCESS": "Finalizar proceso",
|
||||
"GAME_LAUNCHED": "Juego lanzado",
|
||||
@@ -146,5 +147,11 @@
|
||||
"AUTOSTART": "Ejecutar automáticamente al inicio",
|
||||
"LAUNCH": "Iniciar"
|
||||
},
|
||||
"DISPLAY_BRIGHTNESS": "Brillo de la pantalla"
|
||||
"DISPLAY_BRIGHTNESS": "Brillo de la pantalla",
|
||||
"PROCESS_LIST": "Lista de procesos",
|
||||
"REFRESH": "Actualizar",
|
||||
"PROCESS": {
|
||||
"STOP": "Detener",
|
||||
"FORCE_KILL": "Forzar cierre"
|
||||
}
|
||||
}
|
||||
@@ -127,7 +127,9 @@
|
||||
"FAILED_TO_LAUNCH_APPLICATION": "Impossibile avviare l'applicazione:",
|
||||
"GAME_LAUNCHED": "Gioco lanciato",
|
||||
"GAME_LIST": {
|
||||
"NO_GAMES_FOUND": "Nessun gioco trovato"
|
||||
"NO_GAMES_FOUND": "Nessun gioco trovato",
|
||||
"RUNNING_GAMES_LIST": "Lista dei giochi in esecuzione",
|
||||
"NO_RUNNING_GAME_FOUND": "Nessun gioco in esecuzione trovato"
|
||||
},
|
||||
"GAMES": "Giochi",
|
||||
"GENERAL_SETTINGS": "Impostazioni generali",
|
||||
@@ -140,10 +142,15 @@
|
||||
"POPUP_ADD_DISPLAY": {
|
||||
"RESOLUTION": "Risoluzione"
|
||||
},
|
||||
"PROCESS_LIST": {},
|
||||
"REMOVE": "Rimuovi",
|
||||
"SETTINGS": "Impostazioni",
|
||||
"SHOW": "Mostra",
|
||||
"TERMINATE_PROCESS": "Termina processo",
|
||||
"WIDTH": "Larghezza"
|
||||
"WIDTH": "Larghezza",
|
||||
"PROCESS_LIST": "Elenco processi",
|
||||
"REFRESH": "Aggiorna",
|
||||
"PROCESS": {
|
||||
"STOP": "Interrompi",
|
||||
"FORCE_KILL": "Uccidi forzatamente"
|
||||
}
|
||||
}
|
||||
@@ -111,12 +111,13 @@
|
||||
"HIDE": "隠す",
|
||||
"REMOVE": "削除",
|
||||
"SHOW": "表示",
|
||||
"PROCESS_LIST": {},
|
||||
"FAILED_TO_LAUNCH_APPLICATION": "アプリケーションの起動に失敗しました:",
|
||||
"APPLICATION_STARTED": "アプリケーションが起動しました",
|
||||
"CLOSE_WINDOW": "ウィンドウを閉じる",
|
||||
"GAME_LIST": {
|
||||
"NO_GAMES_FOUND": "ゲームが見つかりませんでした"
|
||||
"NO_GAMES_FOUND": "ゲームが見つかりませんでした",
|
||||
"RUNNING_GAMES_LIST": "実行中のゲーム一覧",
|
||||
"NO_RUNNING_GAME_FOUND": "実行中のゲームが見つかりません"
|
||||
},
|
||||
"TERMINATE_PROCESS": "プロセスを終了する",
|
||||
"GAME_LAUNCHED": "ゲームが起動しました",
|
||||
@@ -146,5 +147,11 @@
|
||||
"AUTOSTART": "起動時に自動実行",
|
||||
"LAUNCH": "起動"
|
||||
},
|
||||
"DISPLAY_BRIGHTNESS": "ディスプレイの明るさ"
|
||||
"DISPLAY_BRIGHTNESS": "ディスプレイの明るさ",
|
||||
"PROCESS_LIST": "プロセスリスト",
|
||||
"REFRESH": "更新",
|
||||
"PROCESS": {
|
||||
"STOP": "停止",
|
||||
"FORCE_KILL": "強制終了"
|
||||
}
|
||||
}
|
||||
@@ -111,12 +111,13 @@
|
||||
"SETTINGS": "Ustawienia",
|
||||
"SHOW": "Pokaż",
|
||||
"WIDTH": "Szerokość",
|
||||
"PROCESS_LIST": {},
|
||||
"FAILED_TO_LAUNCH_APPLICATION": "Nie udało się uruchomić aplikacji:",
|
||||
"APPLICATION_STARTED": "Aplikacja uruchomiona",
|
||||
"CLOSE_WINDOW": "Zamknij okno",
|
||||
"GAME_LIST": {
|
||||
"NO_GAMES_FOUND": "Nie znaleziono gier"
|
||||
"NO_GAMES_FOUND": "Nie znaleziono gier",
|
||||
"RUNNING_GAMES_LIST": "Lista uruchomionych gier",
|
||||
"NO_RUNNING_GAME_FOUND": "Nie znaleziono uruchomionej gry"
|
||||
},
|
||||
"TERMINATE_PROCESS": "Zakończ proces",
|
||||
"GAME_LAUNCHED": "Gra uruchomiona",
|
||||
@@ -146,5 +147,11 @@
|
||||
"AUTOSTART": "Uruchom automatycznie przy starcie",
|
||||
"LAUNCH": "Uruchom"
|
||||
},
|
||||
"DISPLAY_BRIGHTNESS": "Jasność wyświetlacza"
|
||||
"DISPLAY_BRIGHTNESS": "Jasność wyświetlacza",
|
||||
"PROCESS_LIST": "Lista procesów",
|
||||
"REFRESH": "Odśwież",
|
||||
"PROCESS": {
|
||||
"STOP": "Zatrzymaj",
|
||||
"FORCE_KILL": "Wymuś zakończenie"
|
||||
}
|
||||
}
|
||||
@@ -127,7 +127,9 @@
|
||||
"FAILED_TO_LAUNCH_APPLICATION": "启动应用失败:",
|
||||
"GAME_LAUNCHED": "游戏已启动",
|
||||
"GAME_LIST": {
|
||||
"NO_GAMES_FOUND": "未找到游戏"
|
||||
"NO_GAMES_FOUND": "未找到游戏",
|
||||
"RUNNING_GAMES_LIST": "正在运行的游戏列表",
|
||||
"NO_RUNNING_GAME_FOUND": "未找到正在运行的游戏"
|
||||
},
|
||||
"GAMES": "游戏",
|
||||
"GENERAL_SETTINGS": "通用设置",
|
||||
@@ -140,10 +142,15 @@
|
||||
"POPUP_ADD_DISPLAY": {
|
||||
"RESOLUTION": "分辨率"
|
||||
},
|
||||
"PROCESS_LIST": {},
|
||||
"REMOVE": "移除",
|
||||
"SETTINGS": "设置",
|
||||
"SHOW": "显示",
|
||||
"TERMINATE_PROCESS": "终止进程",
|
||||
"WIDTH": "宽度"
|
||||
"WIDTH": "宽度",
|
||||
"PROCESS_LIST": "进程列表",
|
||||
"REFRESH": "刷新",
|
||||
"PROCESS": {
|
||||
"STOP": "停止",
|
||||
"FORCE_KILL": "强制关闭"
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,11 @@ use wgui::{
|
||||
widget::{label::WidgetLabel, rectangle::WidgetRectangle},
|
||||
windowing::window::{WguiWindow, WguiWindowParams, WguiWindowParamsExtra, WguiWindowPlacement},
|
||||
};
|
||||
use wlx_common::{audio, dash_interface::BoxDashInterface, timestep::Timestep};
|
||||
use wlx_common::{
|
||||
audio,
|
||||
dash_interface::BoxDashInterface,
|
||||
timestep::{self, Timestep},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
assets,
|
||||
@@ -218,8 +222,10 @@ impl<T: 'static> Frontend<T> {
|
||||
self.process_task(&mut params, task)?;
|
||||
}
|
||||
|
||||
let time_ms = timestep::get_micros() / 1000;
|
||||
|
||||
if let Some(mut tab) = self.current_tab.take() {
|
||||
tab.update(self, params.data)?;
|
||||
tab.update(self, time_ms as u32, params.data)?;
|
||||
|
||||
self.current_tab = Some(tab);
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ impl<T> Tab<T> for TabApps<T> {
|
||||
TabType::Apps
|
||||
}
|
||||
|
||||
fn update(&mut self, frontend: &mut Frontend<T>, data: &mut T) -> anyhow::Result<()> {
|
||||
fn update(&mut self, frontend: &mut Frontend<T>, _time_ms: u32, data: &mut T) -> anyhow::Result<()> {
|
||||
let mut state = self.state.borrow_mut();
|
||||
|
||||
for task in self.tasks.drain() {
|
||||
|
||||
@@ -9,7 +9,8 @@ use wgui::{
|
||||
use crate::{
|
||||
frontend::Frontend,
|
||||
tab::{Tab, TabType},
|
||||
views::game_list,
|
||||
util::steam_utils::SteamUtils,
|
||||
views::{game_list, running_games_list},
|
||||
};
|
||||
|
||||
pub struct TabGames<T> {
|
||||
@@ -17,6 +18,8 @@ pub struct TabGames<T> {
|
||||
pub state: ParserState,
|
||||
|
||||
view_game_list: game_list::View,
|
||||
view_running_games_list: running_games_list::View,
|
||||
steam_utils: SteamUtils,
|
||||
marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
@@ -25,17 +28,22 @@ impl<T> Tab<T> for TabGames<T> {
|
||||
TabType::Games
|
||||
}
|
||||
|
||||
fn update(&mut self, frontend: &mut Frontend<T>, _data: &mut T) -> anyhow::Result<()> {
|
||||
self.view_game_list.update(&mut frontend.layout, &frontend.executor)?;
|
||||
fn update(&mut self, frontend: &mut Frontend<T>, time_ms: u32, _data: &mut T) -> anyhow::Result<()> {
|
||||
self
|
||||
.view_game_list
|
||||
.update(&mut frontend.layout, &mut self.steam_utils, &frontend.executor)?;
|
||||
self.view_running_games_list.update(&mut frontend.layout, time_ms)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TabGames<T> {
|
||||
pub fn new(frontend: &mut Frontend<T>, parent_id: WidgetID) -> anyhow::Result<Self> {
|
||||
let globals = frontend.layout.state.globals.clone();
|
||||
|
||||
let state = wgui::parser::parse_from_assets(
|
||||
&ParseDocumentParams {
|
||||
globals: frontend.layout.state.globals.clone(),
|
||||
globals: globals.clone(),
|
||||
path: AssetPath::BuiltIn("gui/tab/games.xml"),
|
||||
extra: Default::default(),
|
||||
},
|
||||
@@ -44,19 +52,32 @@ impl<T> TabGames<T> {
|
||||
)?;
|
||||
|
||||
let game_list_parent = state.get_widget_id("game_list_parent")?;
|
||||
let id_running_games_list_parent = state.get_widget_id("running_games_list_parent")?;
|
||||
|
||||
let view_game_list = game_list::View::new(game_list::Params {
|
||||
executor: frontend.executor.clone(),
|
||||
frontend_tasks: frontend.tasks.clone(),
|
||||
globals: frontend.layout.state.globals.clone(),
|
||||
globals: globals.clone(),
|
||||
layout: &mut frontend.layout,
|
||||
parent_id: game_list_parent,
|
||||
})?;
|
||||
|
||||
let mut steam_utils = SteamUtils::new()?;
|
||||
|
||||
let view_running_games_list = running_games_list::View::new(running_games_list::Params {
|
||||
globals: globals.clone(),
|
||||
layout: &mut frontend.layout,
|
||||
parent_id: id_running_games_list_parent,
|
||||
steam_utils: &mut steam_utils,
|
||||
frontend_tasks: frontend.tasks.clone(),
|
||||
})?;
|
||||
|
||||
Ok(Self {
|
||||
state,
|
||||
view_game_list,
|
||||
view_running_games_list,
|
||||
marker: PhantomData,
|
||||
steam_utils,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ pub trait Tab<T> {
|
||||
#[allow(dead_code)]
|
||||
fn get_type(&self) -> TabType;
|
||||
|
||||
fn update(&mut self, _: &mut Frontend<T>, _: &mut T) -> anyhow::Result<()> {
|
||||
fn update(&mut self, _frontend: &mut Frontend<T>, _time_ms: u32, _user_data: &mut T) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ impl<T> Tab<T> for TabMonado<T> {
|
||||
TabType::Games
|
||||
}
|
||||
|
||||
fn update(&mut self, frontend: &mut Frontend<T>, data: &mut T) -> anyhow::Result<()> {
|
||||
fn update(&mut self, frontend: &mut Frontend<T>, _time_ms: u32, data: &mut T) -> anyhow::Result<()> {
|
||||
for task in self.tasks.drain() {
|
||||
match task {
|
||||
Task::Refresh => self.refresh(frontend, data)?,
|
||||
|
||||
@@ -80,7 +80,7 @@ impl<T> Tab<T> for TabSettings<T> {
|
||||
TabType::Settings
|
||||
}
|
||||
|
||||
fn update(&mut self, frontend: &mut Frontend<T>, data: &mut T) -> anyhow::Result<()> {
|
||||
fn update(&mut self, frontend: &mut Frontend<T>, _time_ms: u32, data: &mut T) -> anyhow::Result<()> {
|
||||
let mut changed = false;
|
||||
for task in self.tasks.drain() {
|
||||
match task {
|
||||
|
||||
@@ -5,3 +5,4 @@ pub mod popup_manager;
|
||||
pub mod steam_utils;
|
||||
pub mod toast_manager;
|
||||
pub mod various;
|
||||
pub mod wgui_simple;
|
||||
|
||||
@@ -35,6 +35,7 @@ pub struct AppManifest {
|
||||
// TODO @oo8dev: game sort methods
|
||||
#[allow(dead_code)]
|
||||
pub enum GameSortMethod {
|
||||
None,
|
||||
NameAsc,
|
||||
NameDesc,
|
||||
PlayDateDesc,
|
||||
@@ -127,9 +128,6 @@ pub fn launch(app_id: &AppID) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO @oo8dev: running games list (#398)
|
||||
/*
|
||||
|
||||
pub fn stop(app_id: AppID, force_kill: bool) -> anyhow::Result<()> {
|
||||
log::info!("Stopping Steam game with AppID {}", app_id);
|
||||
|
||||
@@ -220,7 +218,7 @@ pub fn list_running_games() -> anyhow::Result<Vec<RunningGame>> {
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
} */
|
||||
}
|
||||
|
||||
fn call_steam(arg: &str) -> anyhow::Result<()> {
|
||||
match std::process::Command::new("xdg-open").arg(arg).spawn() {
|
||||
@@ -286,6 +284,7 @@ impl SteamUtils {
|
||||
.collect();
|
||||
|
||||
match sort_method {
|
||||
GameSortMethod::None => {}
|
||||
GameSortMethod::NameAsc => {
|
||||
games.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
}
|
||||
|
||||
23
dash-frontend/src/util/wgui_simple.rs
Normal file
23
dash-frontend/src/util/wgui_simple.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use wgui::{
|
||||
i18n::Translation,
|
||||
layout::{Layout, WidgetID},
|
||||
renderer_vk::text::TextStyle,
|
||||
widget::label::{WidgetLabel, WidgetLabelParams},
|
||||
};
|
||||
|
||||
pub fn create_label(layout: &mut Layout, parent: WidgetID, content: Translation) -> anyhow::Result<()> {
|
||||
let label = WidgetLabel::create(
|
||||
&mut layout.state.globals.get(),
|
||||
WidgetLabelParams {
|
||||
content,
|
||||
style: TextStyle {
|
||||
wrap: true,
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
layout.add_child(parent, label, Default::default())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -180,7 +180,7 @@ impl View {
|
||||
let tasks = tasks.clone();
|
||||
Box::new(move |_, ev| {
|
||||
if let Some(mode) = ev.value.and_then(|v| {
|
||||
CompositorMode::from_str(&*v)
|
||||
CompositorMode::from_str(&v)
|
||||
.inspect_err(|_| {
|
||||
log::error!(
|
||||
"Invalid value for compositor: '{v}'. Valid values are: {:?}",
|
||||
@@ -199,7 +199,7 @@ impl View {
|
||||
let tasks = tasks.clone();
|
||||
Box::new(move |_, ev| {
|
||||
if let Some(mode) = ev.value.and_then(|v| {
|
||||
ResMode::from_str(&*v)
|
||||
ResMode::from_str(&v)
|
||||
.inspect_err(|_| {
|
||||
log::error!(
|
||||
"Invalid value for resolution: '{v}'. Valid values are: {:?}",
|
||||
@@ -237,7 +237,7 @@ impl View {
|
||||
let tasks = tasks.clone();
|
||||
Box::new(move |_, ev| {
|
||||
if let Some(mode) = ev.value.and_then(|v| {
|
||||
OrientationMode::from_str(&*v)
|
||||
OrientationMode::from_str(&v)
|
||||
.inspect_err(|_| {
|
||||
log::error!(
|
||||
"Invalid value for orientation: '{v}'. Valid values are: {:?}",
|
||||
|
||||
@@ -55,7 +55,6 @@ pub struct View {
|
||||
frontend_tasks: FrontendTasks,
|
||||
globals: WguiGlobals,
|
||||
id_list_parent: WidgetID,
|
||||
steam_utils: steam_utils::SteamUtils,
|
||||
cells: HashMap<AppID, Cell>,
|
||||
game_cover_view_common: game_cover::ViewCommon,
|
||||
executor: AsyncExecutor,
|
||||
@@ -75,8 +74,6 @@ impl View {
|
||||
|
||||
let tasks = Tasks::new();
|
||||
|
||||
let steam_utils = SteamUtils::new()?;
|
||||
|
||||
tasks.push(Task::Refresh);
|
||||
|
||||
Ok(Self {
|
||||
@@ -85,7 +82,6 @@ impl View {
|
||||
frontend_tasks: params.frontend_tasks,
|
||||
globals: params.globals.clone(),
|
||||
id_list_parent: list_parent.id,
|
||||
steam_utils,
|
||||
cells: HashMap::new(),
|
||||
game_cover_view_common: game_cover::ViewCommon::new(params.globals.clone()),
|
||||
state: Rc::new(RefCell::new(State { view_launcher: None })),
|
||||
@@ -93,7 +89,12 @@ impl View {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn update(&mut self, layout: &mut Layout, executor: &AsyncExecutor) -> anyhow::Result<()> {
|
||||
pub fn update(
|
||||
&mut self,
|
||||
layout: &mut Layout,
|
||||
steam_utils: &mut SteamUtils,
|
||||
executor: &AsyncExecutor,
|
||||
) -> anyhow::Result<()> {
|
||||
loop {
|
||||
let tasks = self.tasks.drain();
|
||||
if tasks.is_empty() {
|
||||
@@ -101,7 +102,7 @@ impl View {
|
||||
}
|
||||
for task in tasks {
|
||||
match task {
|
||||
Task::Refresh => self.refresh(layout, executor)?,
|
||||
Task::Refresh => self.refresh(layout, steam_utils, executor)?,
|
||||
Task::AppManifestClicked(manifest) => self.action_app_manifest_clicked(manifest)?,
|
||||
Task::SetCoverArt(app_id, cover_art) => self.set_cover_art(layout, app_id, cover_art),
|
||||
Task::CloseLauncher => self.state.borrow_mut().view_launcher = None,
|
||||
@@ -162,20 +163,23 @@ fn fill_game_list(
|
||||
}
|
||||
|
||||
impl View {
|
||||
fn game_list(&self) -> anyhow::Result<Games> {
|
||||
let manifests = self
|
||||
.steam_utils
|
||||
.list_installed_games(steam_utils::GameSortMethod::PlayDateDesc)?;
|
||||
fn game_list(&self, steam_utils: &mut SteamUtils) -> anyhow::Result<Games> {
|
||||
let manifests = steam_utils.list_installed_games(steam_utils::GameSortMethod::PlayDateDesc)?;
|
||||
|
||||
Ok(Games { manifests })
|
||||
}
|
||||
|
||||
fn refresh(&mut self, layout: &mut Layout, executor: &AsyncExecutor) -> anyhow::Result<()> {
|
||||
fn refresh(
|
||||
&mut self,
|
||||
layout: &mut Layout,
|
||||
steam_utils: &mut SteamUtils,
|
||||
executor: &AsyncExecutor,
|
||||
) -> anyhow::Result<()> {
|
||||
layout.remove_children(self.id_list_parent);
|
||||
self.cells.clear();
|
||||
|
||||
let mut text: Option<Translation> = None;
|
||||
match self.game_list() {
|
||||
match self.game_list(steam_utils) {
|
||||
Ok(list) => {
|
||||
if list.manifests.is_empty() {
|
||||
text = Some(Translation::from_translation_key("GAME_LIST.NO_GAMES_FOUND"))
|
||||
|
||||
@@ -3,3 +3,4 @@ pub mod audio_settings;
|
||||
pub mod game_cover;
|
||||
pub mod game_launcher;
|
||||
pub mod game_list;
|
||||
pub mod running_games_list;
|
||||
|
||||
178
dash-frontend/src/views/running_games_list.rs
Normal file
178
dash-frontend/src/views/running_games_list.rs
Normal file
@@ -0,0 +1,178 @@
|
||||
use wgui::{
|
||||
assets::AssetPath,
|
||||
components::button::ComponentButton,
|
||||
globals::WguiGlobals,
|
||||
i18n::Translation,
|
||||
layout::{Layout, WidgetID},
|
||||
parser::{Fetchable, ParseDocumentParams, ParserState},
|
||||
task::Tasks,
|
||||
widget::label::WidgetLabel,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
frontend::{FrontendTask, FrontendTasks},
|
||||
util::{
|
||||
steam_utils::{self, AppID, AppManifest, GameSortMethod, SteamUtils},
|
||||
wgui_simple,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
enum Task {
|
||||
Refresh,
|
||||
StopGame(AppID, bool /* kill */),
|
||||
}
|
||||
|
||||
pub struct Params<'a> {
|
||||
pub globals: WguiGlobals,
|
||||
pub layout: &'a mut Layout,
|
||||
pub parent_id: WidgetID,
|
||||
pub steam_utils: &'a mut SteamUtils,
|
||||
pub frontend_tasks: FrontendTasks,
|
||||
}
|
||||
|
||||
pub struct View {
|
||||
#[allow(dead_code)]
|
||||
state: ParserState,
|
||||
tasks: Tasks<Task>,
|
||||
last_update_ms: u32,
|
||||
id_list_parent: WidgetID,
|
||||
installed_games: Vec<AppManifest>,
|
||||
frontend_tasks: FrontendTasks,
|
||||
}
|
||||
|
||||
fn doc_params(globals: WguiGlobals) -> ParseDocumentParams<'static> {
|
||||
ParseDocumentParams {
|
||||
globals,
|
||||
path: AssetPath::BuiltIn("gui/view/running_games_list.xml"),
|
||||
extra: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
impl View {
|
||||
pub fn new(params: Params) -> anyhow::Result<Self> {
|
||||
let state = wgui::parser::parse_from_assets(&doc_params(params.globals.clone()), params.layout, params.parent_id)?;
|
||||
let btn_refresh = state.fetch_component_as::<ComponentButton>("btn_refresh")?;
|
||||
let id_list_parent = state.get_widget_id("list_parent")?;
|
||||
|
||||
let installed_games = params
|
||||
.steam_utils
|
||||
.list_installed_games(GameSortMethod::None)
|
||||
.unwrap_or(Vec::new());
|
||||
|
||||
let tasks = Tasks::<Task>::new();
|
||||
|
||||
tasks.handle_button(&btn_refresh, Task::Refresh);
|
||||
tasks.push(Task::Refresh);
|
||||
|
||||
Ok(Self {
|
||||
state,
|
||||
tasks,
|
||||
last_update_ms: 0,
|
||||
id_list_parent,
|
||||
installed_games,
|
||||
frontend_tasks: params.frontend_tasks,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn update(&mut self, layout: &mut Layout, time_ms: u32) -> anyhow::Result<()> {
|
||||
loop {
|
||||
let tasks = self.tasks.drain();
|
||||
if tasks.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
for task in tasks {
|
||||
match task {
|
||||
Task::Refresh => self.refresh(layout)?,
|
||||
Task::StopGame(app_id, kill) => self.stop_game(app_id, kill),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.last_update_ms + 5000 < time_ms {
|
||||
self.last_update_ms = time_ms;
|
||||
self.tasks.push(Task::Refresh);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn extract_name_from_appid<'a>(app_id: &AppID, manifests: &[AppManifest]) -> String {
|
||||
for manifest in manifests {
|
||||
if manifest.app_id == *app_id {
|
||||
return manifest.name.clone();
|
||||
}
|
||||
}
|
||||
|
||||
format!("Unknown AppID {}", app_id)
|
||||
}
|
||||
|
||||
fn fill_list(&mut self, layout: &mut Layout, games: Vec<steam_utils::RunningGame>) -> anyhow::Result<()> {
|
||||
if games.is_empty() {
|
||||
wgui_simple::create_label(
|
||||
layout,
|
||||
self.id_list_parent,
|
||||
Translation::from_translation_key("GAME_LIST.NO_RUNNING_GAME_FOUND"),
|
||||
)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for game in games {
|
||||
let game_name = View::extract_name_from_appid(&game.app_id, &self.installed_games);
|
||||
|
||||
let t = self.state.parse_template(
|
||||
&doc_params(layout.state.globals.clone()),
|
||||
"RunningGameCell",
|
||||
layout,
|
||||
self.id_list_parent,
|
||||
Default::default(),
|
||||
)?;
|
||||
|
||||
let mut label_name = t.fetch_widget_as::<WidgetLabel>(&layout.state, "label_name")?;
|
||||
|
||||
self.tasks.handle_button(
|
||||
&t.fetch_component_as::<ComponentButton>("btn_stop")?,
|
||||
Task::StopGame(game.app_id.clone(), false),
|
||||
);
|
||||
|
||||
self.tasks.handle_button(
|
||||
&t.fetch_component_as::<ComponentButton>("btn_kill")?,
|
||||
Task::StopGame(game.app_id, true),
|
||||
);
|
||||
|
||||
label_name.set_text_simple(
|
||||
&mut layout.state.globals.get(),
|
||||
Translation::from_raw_text_string(game_name),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn refresh(&mut self, layout: &mut Layout) -> anyhow::Result<()> {
|
||||
log::debug!("refreshing running games list");
|
||||
|
||||
layout.remove_children(self.id_list_parent);
|
||||
|
||||
match steam_utils::list_running_games() {
|
||||
Ok(games) => self.fill_list(layout, games)?,
|
||||
Err(e) => {
|
||||
log::error!("failed to list games: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn stop_game(&mut self, app_id: AppID, kill: bool) {
|
||||
if let Err(e) = steam_utils::stop(app_id, kill) {
|
||||
self
|
||||
.frontend_tasks
|
||||
.push(FrontendTask::PushToast(Translation::from_raw_text_string(format!(
|
||||
"Error: {}",
|
||||
e
|
||||
))));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user