Skip to content

Commit

Permalink
Misc fixes and updates (#72)
Browse files Browse the repository at this point in the history
  • Loading branch information
dmadisetti authored May 24, 2024
1 parent 55cd505 commit f5d2a4f
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 52 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "steam-tui"
version = "0.2.2"
version = "0.3.0"
description = "TUI client for steamcmd."
readme = "README.md"
authors = ["madisetti <[email protected]>"]
Expand Down
21 changes: 17 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ Just a simple TUI client for `steamcmd`. Allows for the graphical launching,
updating, and downloading of steam games through a simple terminal client.
Methodology informed by [steam-cli](https://github.com/berenm/steam-cli).

> [!NOTE]
> Steam no longer has a backend only mode. This client has become more limited,
> but still potenitally useful for those who don't want to use the steam client.
<p align="center">
<img width="600" alt="Example of rules_euler in action" src="screenshot.png">
<img width="600" alt="Example of steam-tui in action" src="screenshot.png">
</p>

## Usage
Expand All @@ -20,9 +24,17 @@ steamcmd
```
Launch the binary `steam-tui`, and rejoice :tada:. Help is in the client.

Unable to launch games? Pressing space will start a bare-bones steam client (no
graphics) in the background and will let you launch games that need steam
libraries or have some sort of DRM.
Unable to launch games? Pressing space will start a steam client and will let
you launch games that need steam libraries or have some sort of DRM.

> [!WARNING]
> ## Still unable to launch games?
>
> You are not alone, but gaming on linux is getting better. You can define a
> custom script to launch a specific game by creating
> `$STEAM_TUI_SCRIPT_DIR/<game id>.sh`, steam TUI will pass in the file it
> thinks should be run, but you can ignore this and just do whatever.
> This reduces steam-tui to more of a launcher, but it's better than nothing.
## Features not in the help

Expand Down Expand Up @@ -64,6 +76,7 @@ time into this.

Thank you to those who have heeded my call for more coffee!

- @abowen @KDanisme @jharlan-hash (sponsored major update 0.3.0)
- @MathiasSven (sponsored minor update 0.2.1)
- @vaelund (sponsored major update 0.2.0)

Expand Down
85 changes: 54 additions & 31 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,11 +203,23 @@ fn execute(
) {
let err = format!("{:?}", err);
log!("Run script for:", name, "failed", err);
let mut reference = status.lock().unwrap();
*reference = Some(GameStatus::msg(
&*reference,
&format!("Error: {}", err),
));
{
let mut reference = status.lock().unwrap();
*reference = Some(GameStatus::msg(
&*reference,
&format!("Error with script (trying direct): {}", err),
));
}
// Try again as per #51
run_process(
"steam".to_string(),
vec![
"-silent".to_string(),
"-applaunch".to_string(),
id.to_string(),
],
status,
);
}
});
break;
Expand All @@ -232,14 +244,17 @@ fn execute(
.collect::<Vec<String>>();
command.append(&mut args);
log!("Finding entry");
let entry = match steam_run_wrapper() {
let entry = match steam_run_wrapper(id) {
Ok(wrapper) => wrapper.into_os_string().into_string().unwrap(),
Err(STError::Problem(_)) => command.remove(0),
Err(err) => {
let mut reference = status.lock().unwrap();
*reference = Some(GameStatus::msg(&*reference, "Could not find entry program."));
*reference = Some(GameStatus::msg(
&*reference,
"Could not find entry program.",
));
return Err(err);
}, // unwrap and rewrap to explicitly note this is an err.
} // unwrap and rewrap to explicitly note this is an err.
};
log!("Exits loop");
let status = status.clone();
Expand All @@ -248,28 +263,7 @@ fn execute(
let mut reference = status.lock().unwrap();
*reference = Some(GameStatus::msg(&*reference, "running..."));
}
match process::Command::new(entry).args(command).output() {
Ok(output) => {
let mut reference = status.lock().unwrap();
*reference =
Some(GameStatus::msg(&*reference, "Fully Installed"));
log!(
"Launching stdout:",
&std::str::from_utf8(&output.stdout)
);
log!(
"Launching stderr:",
&std::str::from_utf8(&output.stderr)
);
}
Err(err) => {
let mut reference = status.lock().unwrap();
*reference = Some(GameStatus::msg(
&*reference,
&format!("failed to launch: {}", err),
));
}
}
run_process(entry, command, status);
});
launched = true;
break;
Expand Down Expand Up @@ -444,6 +438,34 @@ fn execute(
}
}

fn run_process(entry: String, command: Vec<String>, status: Arc<Mutex<Option<GameStatus>>>) {
match process::Command::new(entry).args(command).output() {
Ok(output) => {
let stderr = output.stderr.clone();
let stderr_snippet = &(String::from_utf8_lossy(&stderr)[..50]);
let mut reference = status.lock().unwrap();
*reference = Some(GameStatus::msg(
&*reference,
&(match output.status.code() {
Some(0) => format!("ran (success)"),
Some(n) => format!("failed with code {}: ({}...)", n, stderr_snippet),
None => format!("Process terminated."),
}),
));

log!("Launching stdout:", &std::str::from_utf8(&output.stdout));
log!("Launching stderr:", &std::str::from_utf8(&output.stderr));
}
Err(err) => {
let mut reference = status.lock().unwrap();
*reference = Some(GameStatus::msg(
&*reference,
&format!("failed to launch: {}", err),
));
}
}
}

/// Manages and interfaces with SteamCmd threads.
pub struct Client {
receiver: Mutex<Receiver<String>>,
Expand Down Expand Up @@ -547,6 +569,7 @@ impl Client {

/// Binds data from 'app_status' to a `GameStatus` object.
pub fn status(&self, id: i32) -> Result<GameStatus, STError> {
log!("Getting status for", id);
let sender = self.sender.lock()?;
sender.send(Command::Cli(format!("app_status {}", id)))?;
let receiver = self.receiver.lock()?;
Expand Down Expand Up @@ -615,7 +638,7 @@ fn keys_from_licenses(licenses: String) -> Vec<i32> {

#[cfg(test)]
mod tests {
use crate::client::{keys_from_licenses, Client, Command, State};
use crate::client::{Client, Command, State};
use crate::util::error::STError;
use std::sync::mpsc::channel;
use std::sync::Arc;
Expand Down
9 changes: 6 additions & 3 deletions src/interface/game.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,14 @@ impl Game {
};
return Ok(game);
} else {
return Err(STError::Problem("File a github issue. Something may have changed in the Steam definition.".to_string()));
return Err(STError::Problem(
"File a github issue. Something may have changed in the Steam definition."
.to_string(),
));
}
} else {
log!("Cannot get key", key);
log!(parse(lines));
log!("Cannot get key", key);
log!(parse(lines));
}
} else {
log!("Cannot get nest");
Expand Down
18 changes: 8 additions & 10 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ use std::io;

use crossterm::event::KeyCode;

use crossterm::terminal::{
disable_raw_mode, enable_raw_mode,
};
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
use tui::style::{Color, Style};
use tui::{backend::CrosstermBackend, layout::Rect, Terminal};

Expand Down Expand Up @@ -59,6 +57,11 @@ fn entry() -> Result<(), Box<dyn std::error::Error>> {
let events = Events::new();
let client = Client::new();

// Login before cache, otherwise metadata is empty.
if !app.user.is_empty() {
client.login(&app.user)?;
}

// Attempt to load from cache. If not, continue as usual.
let mut game_list: StatefulList<Game> = StatefulList::new();
let mut cached: bool = false;
Expand All @@ -71,11 +74,6 @@ fn entry() -> Result<(), Box<dyn std::error::Error>> {
_ => game_list.restart(),
}

// Login after cache load, since a failed login needs to show the failure screen.
if !app.user.is_empty() {
client.login(&app.user)?;
}

loop {
terminal.draw(|frame| {
let layout = App::build_layout();
Expand Down Expand Up @@ -238,7 +236,7 @@ fn entry() -> Result<(), Box<dyn std::error::Error>> {
break;
}
}
KeyCode::Char('\n') | KeyCode::Enter => {
KeyCode::Char('\n') | KeyCode::Enter => {
let mut user = app.user.clone();
user.retain(|c| !c.is_whitespace());
terminal.hide_cursor()?;
Expand All @@ -263,7 +261,7 @@ fn entry() -> Result<(), Box<dyn std::error::Error>> {
game_list.query = "".to_string();
img = update_img(&game_list.selected());
}
KeyCode::Char('\n') | KeyCode::Enter => {
KeyCode::Char('\n') | KeyCode::Enter => {
terminal.hide_cursor()?;
app.mode = Mode::Searched;
}
Expand Down
9 changes: 7 additions & 2 deletions src/util/paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,17 @@ pub fn icon_directory() -> Result<PathBuf, STError> {
pub fn steam_directory() -> Result<PathBuf, STError> {
let dir = match env::var("STEAM_APP_DIR") {
Ok(dir) => dir,
_ => "~/.steam/steam/Steamapps/common/".to_string(),
_ => "~/.steam/steam/steamapps/common/".to_string(),
};
mkdir(dir)
}

pub fn steam_run_wrapper() -> Result<PathBuf, STError> {
pub fn steam_run_wrapper(id: i32) -> Result<PathBuf, STError> {
// Custom script always takes precedence, then env, then hardcoded path.
let custom_script = script_directory()?.join(&format!("{}.sh", id));
if custom_script.exists() {
return Ok(custom_script);
}
let run = match env::var("STEAM_RUN_WRAPPER") {
Ok(run) => run,
_ => "~/.steam/bin32/steam-runtime/run.sh".to_string(),
Expand Down

0 comments on commit f5d2a4f

Please sign in to comment.