messy commit beacuse I used another IDE so line ends are messed up, anyway I added the command line option to idle an app. By the way I love long commit notes

This commit is contained in:
Paul C 2019-05-27 19:55:11 +02:00
parent 46e33e7f81
commit 50612ace0c
9 changed files with 118 additions and 50 deletions

View File

@ -14,12 +14,23 @@ Contributions are welcome!
Thanks :)
# Command line options
While SamRewritten offers a nice GUI, you linux geeks love to use command line options.
It would be exciting to expand the command line options, but I do not have much more time to invest
in this project, and a rewriting would not hurt. Anyway you can do:
* -a <appid>
* `./bin/launch.sh -a 10` will idle Counter Strike
# Building
Just run ./make.sh. Makefiles? Never heard of it. Packaging? What's that?
Packages needed vary on distros, but make sure to install at least Steam, yajl, GTK 3, and GLib.
On Lubuntu: "sudo apt install libgtk-3-dev libcurl4-gnutls-dev libyajl-dev"
To run it, launch ./bin/launch.sh
Once again, all contributions are VERY welcome, even though this code is already aging and very badly written.
Once again, all contributions are VERY welcome, even though this code is already aging and very badly written.

View File

@ -5,10 +5,10 @@
****************************/
/**
* Used by the child process when the parent tells him to
* Used by the child process when the parent tells him to
* stop the steam app. The child process will die.
*/
void
void
handle_sigterm(int signum) {
SteamAPI_Shutdown();
exit(EXIT_SUCCESS);
@ -18,7 +18,7 @@ handle_sigterm(int signum) {
* Used by the parent process to remove the zombie process
* of the child, once it terminated
*/
void
void
handle_sigchld(int signum) {
pid_t pid;
GameEmulator* emulator = GameEmulator::get_instance();
@ -38,16 +38,16 @@ handle_sigchld(int signum) {
/**
* When the parent receives SIGUSR1, it will read the pipe,
* and if everything goes well, it should fill the member
* and if everything goes well, it should fill the member
* achievements list, and the achievement count.
* Once done, will update the view
*
*
* This one might need a little more documentation on the technical side.
* This method is only received on the parent process. The son,
* This method is only received on the parent process. The son,
* will retrieve the stats and achievements from steam, and once it is done,
* it will send a SIGUSR1 to the parent back. The parent will then save the
* new data, and update the view accordingly.
*
*
* TODO: Check for errors
*/
void handle_sigusr1_parent(int signum) {
@ -61,7 +61,7 @@ void handle_sigusr1_parent(int signum) {
}
inst->m_achievement_list = (Achievement_t*)malloc(inst->m_achievement_count * sizeof(Achievement_t));
if (!inst->m_achievement_list) {
std::cerr << "ERROR: could not allocate memory." << std::endl;
exit(EXIT_FAILURE);
@ -78,15 +78,15 @@ void handle_sigusr1_parent(int signum) {
* We are the child and we will receive data/stats of achievements to unlock/relock
* then auto-commit changes and update the parent. Coodinating based on the number
* of changes avoids potentially losing signals because of multiple being in flight.
*
*
* Data will have this shitty format:
* - 1 unsigned for number of changes
* - 1 char, 'a' for achievement, 's' for "stat"
* - 1 unsigned int, 0 => locked, 1 => unlocked, or the stat progression
* - The length of MAX_ID_LENGTH to get the achievement ID
*
*
* If someone wants to update this repo, maybe IPC is too low level for what I want
* to achieve. This doesn't require super fast speed or anything, maybe see if
* to achieve. This doesn't require super fast speed or anything, maybe see if
* TCP server is easier to use. Or at least one type of interfacing because this code
* gets really ugly over time, bad practices become usual.
*/
@ -95,7 +95,7 @@ void handle_sigusr1_child(int signum) {
ISteamUserStats *stats_api = SteamUserStats();
int* pipe = inst->m_pipe;
char type;
// Read number of changes
unsigned num_changes;
read_count(pipe[0], &num_changes, sizeof(unsigned));
@ -140,9 +140,9 @@ void handle_sigusr1_child(int signum) {
if (!stats_api->StoreStats()) {
std::cerr << "Committing changes failed" << std::endl;
}
// After the child changes achievements/stats, it will retrieve all achievements,
// and send a SIGUSR1 signal to the parent when done, and start writing
// and send a SIGUSR1 signal to the parent when done, and start writing
// to the pipe.
std::cerr << "Child is updating parent" << std::endl;
inst->retrieve_achievements();
@ -154,7 +154,7 @@ void handle_sigusr1_child(int signum) {
* CLASS METHODS DEFINITION
********************************/
GameEmulator::GameEmulator() :
GameEmulator::GameEmulator() :
m_CallbackUserStatsReceived( this, &GameEmulator::OnUserStatsReceived ),
m_achievement_list( nullptr ),
m_son_pid( -1 ),
@ -192,12 +192,13 @@ GameEmulator::init_app(const std::string& app_id) {
//Son's process
setenv("SteamAppId", app_id.c_str(), 1);
if( !SteamAPI_Init() ) {
std::cerr << "An error occurred launching the steam API. Aborting." << std::endl;
std::cerr << "An error occurred launching the Steam API. Aborting." << std::endl;
std::cerr << "Make sure you are trying to run an app you own, and running with lauch.sh" << std::endl;
exit(EXIT_FAILURE);
}
signal(SIGTERM, handle_sigterm);
// Communicate game stats to parent, and
// Communicate game stats to parent, and
// read from parents stats to modify
signal(SIGUSR1, handle_sigusr1_child);
@ -237,7 +238,7 @@ GameEmulator::kill_running_app() {
else {
if (g_main_gui != NULL)
std::cerr << "Warning: trying to kill the Steam Game while it's not running." << std::endl;
return true;
}
@ -276,7 +277,7 @@ GameEmulator::update_view() {
* It reset the GUI window in anticipation of the child
* process sending it new information on the achievements
* via handle_sigusr1_parent
*
*
*/
void
GameEmulator::update_data_and_view() {
@ -292,13 +293,13 @@ GameEmulator::update_data_and_view() {
}
// => update_data_and_view
bool
bool
GameEmulator::send_num_changes(unsigned num_changes) const {
// We assume the son process is already running
// Write the number of changes
write(m_pipe[1], &num_changes, sizeof(unsigned));
// Send it a signal after buffering the number of changes
kill(m_son_pid, SIGUSR1);
@ -307,11 +308,11 @@ GameEmulator::send_num_changes(unsigned num_changes) const {
// => unlock_achievement
/**
* The parent process requests the son process to unlock an
* The parent process requests the son process to unlock an
* achievement. So this code will be executed in the parent process,
* so send a message to the son, associated with an achievement ID
*/
bool
bool
GameEmulator::unlock_achievement(const char* ach_api_name) const {
// We assume the son process is already running
static const unsigned unlock_state = 1;
@ -325,7 +326,7 @@ GameEmulator::unlock_achievement(const char* ach_api_name) const {
}
// => unlock_achievement
bool
bool
GameEmulator::relock_achievement(const char* ach_api_name) const {
// We assume the son process is already running
static const unsigned unlock_state = 0;
@ -346,7 +347,7 @@ GameEmulator::relock_achievement(const char* ach_api_name) const {
****************************************/
/**
* Retrieves all achievements data, then pipes the data to the
* Retrieves all achievements data, then pipes the data to the
* parent process.
*/
void
@ -356,7 +357,7 @@ GameEmulator::OnUserStatsReceived(UserStatsReceived_t *callback) {
if ( k_EResultOK == callback->m_eResult ) {
ISteamUserStats *stats_api = SteamUserStats();
// ==============================
// RETRIEVE IDS
// ==============================
@ -369,7 +370,7 @@ GameEmulator::OnUserStatsReceived(UserStatsReceived_t *callback) {
if (m_achievement_list != nullptr) {
free(m_achievement_list);
m_achievement_list = nullptr;
}
}
m_achievement_list = (Achievement_t*)malloc(num_ach * sizeof(Achievement_t));
@ -379,12 +380,12 @@ GameEmulator::OnUserStatsReceived(UserStatsReceived_t *callback) {
// making sure strings are NULL terminated
// see "man strncpy" for a possible implementation
strncpy(
m_achievement_list[i].id,
stats_api->GetAchievementName(i),
m_achievement_list[i].id,
stats_api->GetAchievementName(i),
MAX_ACHIEVEMENT_ID_LENGTH);
strncpy(
m_achievement_list[i].name,
m_achievement_list[i].name,
stats_api->GetAchievementDisplayAttribute(m_achievement_list[i].id, "name"),
MAX_ACHIEVEMENT_NAME_LENGTH);
@ -404,12 +405,12 @@ GameEmulator::OnUserStatsReceived(UserStatsReceived_t *callback) {
//Tell parent that he must read
kill(getppid(), SIGUSR1);
//Start writing
write(m_pipe[1], &num_ach, sizeof(unsigned));
// We could send all the memory bloc at once, but the pipe buffer
// We could send all the memory bloc at once, but the pipe buffer
// might not be big enough on some systems, so let's just loop
for(unsigned i = 0; i < num_ach; i++) {
write(m_pipe[1], &(m_achievement_list[i]), sizeof(Achievement_t));
@ -421,6 +422,6 @@ GameEmulator::OnUserStatsReceived(UserStatsReceived_t *callback) {
}
} else {
std::cerr << "Received stats for wrong game" << std::endl;
}
}
}
// => OnUserStatsReceived
// => OnUserStatsReceived

42
SAM.Picker/cli_funcs.cpp Normal file
View File

@ -0,0 +1,42 @@
#include "cli_funcs.h"
#include "MySteam.h"
#include <string>
#include <iostream>
void handle_sigint_cli(int signum) {
std::cout << "Quitting cli idling" << std::endl;
g_steam->quit_game();
exit(0);
}
void idle_app(std::string appid) {
std::cout << "Idling from command line " << appid << std::endl;
g_steam->launch_game(appid);
signal(SIGINT, handle_sigint_cli);
}
bool go_cli_mode(int argc, char* argv[]) {
bool cli = false;
int opt;
while((opt = getopt(argc, argv, ":a:")) != -1)
{
switch(opt)
{
case 'a':
const std::string appid = std::string(optarg);
idle_app(appid);
cli = true;
break;
}
}
// If cli, wait for ctrl+c, so we can kill both processes, otherwise
// GUI process will exit while game process still goes on
if ( cli ) {
for(;;) {
sleep(10000);
}
}
return cli;
}

8
SAM.Picker/cli_funcs.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
#include "globals.h"
/**
* Process command-line arguments. If there are some, returns true
*/
bool go_cli_mode(int argc, char* argv[]);

View File

@ -8,6 +8,7 @@
#include "MySteam.h"
#include "MainPickerWindow.h"
#include "globals.h"
#include "cli_funcs.h"
/**************************************
* Declare global variables imported from globals.h
@ -21,7 +22,7 @@ char* g_cache_folder = nullptr;
/**************************************
* Main entry point
**************************************/
int
int
main(int argc, char *argv[])
{
// Test if glib2 is installed, gtk will not work without it.
@ -29,15 +30,20 @@ main(int argc, char *argv[])
std::cerr << "Sorry, but gmodules are not supported on your platform :(. Try installing as many gnome libs as you can maybe.." << std::endl;
exit(EXIT_FAILURE);
}
gtk_init(&argc, &argv);
g_cache_folder = concat( getenv("HOME"), "/.SamRewritten" );
g_steam = MySteam::get_instance();
g_main_gui = new MainPickerWindow();
gtk_widget_show( g_main_gui->get_main_window() );
gtk_main();
// Check for command-line options, which may prevent showing the GUI
// Note that a rewriting should be done to further separate the GUI
// from a command-line interface
if(!go_cli_mode(argc, argv)) {
gtk_widget_show( g_main_gui->get_main_window() );
gtk_main();
}
return 0;
}
}

View File

@ -7,4 +7,4 @@ export LD_LIBRARY_PATH=$SCRIPTPATH
echo "Library path is" $LD_LIBRARY_PATH
cd $SCRIPTPATH
cd ../
$SCRIPTPATH/samrewritten
$SCRIPTPATH/samrewritten $@

View File

@ -20,8 +20,8 @@ void read_count(int fd, void *buf, size_t count)
}
bool file_exists(const std::string& name) {
struct stat buffer;
return (stat (name.c_str(), &buffer) == 0);
struct stat buffer;
return (stat (name.c_str(), &buffer) == 0);
}
char* concat(const char *s1, const char *s2)
@ -42,4 +42,4 @@ bool strstri(const std::string & strHaystack, const std::string & strNeedle)
[](char ch1, char ch2) { return std::toupper(ch1) == std::toupper(ch2); }
);
return (it != strHaystack.end() );
}
}

View File

@ -33,4 +33,4 @@ char* concat(const char *s1, const char *s2);
/**
* Insensitive "string in string"
*/
bool strstri(const std::string & strHaystack, const std::string & strNeedle);
bool strstri(const std::string & strHaystack, const std::string & strNeedle);

View File

@ -13,6 +13,6 @@ common/*.cpp \
-L$SCRIPTPATH/bin \
-o $SCRIPTPATH/bin/samrewritten \
`pkg-config --libs gtk+-3.0` \
-lpthread -lgmodule-2.0 -lsteam_api -lcurl -lyajl \
&& \
./bin/launch.sh
-lpthread -lgmodule-2.0 -lsteam_api -lcurl -lyajl
echo "If there wasn't any compilation error, you can launch the manager with ./bin/launch.sh"