creat shutdown.h i system_utils.h

fix: arreglants uns includes per a windows
This commit is contained in:
2025-08-11 08:15:54 +02:00
parent 6011365330
commit 3df13fdfa6
9 changed files with 527 additions and 187 deletions

View File

@@ -81,7 +81,9 @@ set(APP_SOURCES
source/input_types.cpp
source/mouse.cpp
source/options.cpp
source/shutdown.cpp
source/stage.cpp
source/system_utils.cpp
source/utils.cpp
)

View File

@@ -22,37 +22,42 @@ RASPI_RELEASE := $(TARGET_FILE)-$(VERSION)-raspberry.tar.gz
# Lista completa de archivos fuente (basada en CMakeLists.txt)
APP_SOURCES := \
source/animated_sprite.cpp \
source/asset.cpp \
source/audio.cpp \
source/director.cpp \
source/global_events.cpp \
source/global_inputs.cpp \
source/input.cpp \
source/lang.cpp \
source/main.cpp \
source/param.cpp \
source/resource.cpp \
source/screen.cpp \
source/text.cpp \
source/writer.cpp \
source/ui/menu_option.cpp \
source/ui/menu_renderer.cpp \
source/ui/notifier.cpp \
source/ui/service_menu.cpp \
source/ui/ui_message.cpp \
source/ui/window_message.cpp \
source/background.cpp \
source/balloon_formations.cpp \
source/balloon_manager.cpp \
source/balloon.cpp \
source/bullet.cpp \
source/color.cpp \
source/define_buttons.cpp \
source/difficulty.cpp \
source/director.cpp \
source/enter_name.cpp \
source/explosions.cpp \
source/external/gif.cpp \
source/external/jail_audio.cpp \
source/external/jail_shader.cpp \
source/fade.cpp \
source/game_logo.cpp \
source/global_events.cpp \
source/global_inputs.cpp \
source/input_types.cpp \
source/input.cpp \
source/item.cpp \
source/lang.cpp \
source/main.cpp \
source/manage_hiscore_table.cpp \
source/mouse.cpp \
source/moving_sprite.cpp \
source/options.cpp \
source/param.cpp \
source/path_sprite.cpp \
source/player.cpp \
source/resource.cpp \
source/scoreboard.cpp \
source/tabe.cpp \
source/screen.cpp \
source/sections/credits.cpp \
source/sections/game.cpp \
source/sections/hiscore_table.cpp \
@@ -60,26 +65,23 @@ APP_SOURCES := \
source/sections/intro.cpp \
source/sections/logo.cpp \
source/sections/title.cpp \
source/animated_sprite.cpp \
source/background.cpp \
source/fade.cpp \
source/moving_sprite.cpp \
source/path_sprite.cpp \
source/shutdown.cpp \
source/smart_sprite.cpp \
source/sprite.cpp \
source/stage.cpp \
source/system_utils.cpp \
source/tabe.cpp \
source/text.cpp \
source/texture.cpp \
source/tiled_bg.cpp \
source/color.cpp \
source/define_buttons.cpp \
source/difficulty.cpp \
source/input_types.cpp \
source/mouse.cpp \
source/options.cpp \
source/stage.cpp \
source/ui/menu_option.cpp \
source/ui/menu_renderer.cpp \
source/ui/notifier.cpp \
source/ui/service_menu.cpp \
source/ui/ui_message.cpp \
source/ui/window_message.cpp \
source/utils.cpp \
source/external/jail_shader.cpp \
source/external/jail_audio.cpp \
source/external/gif.cpp
source/writer.cpp
# Includes
INCLUDES := -Isource -Isource/external

View File

@@ -8,9 +8,8 @@ fi
FILE="$1"
include-what-you-use -D DEBUG -D VERBOSE -std=c++20 -Wall "$FILE" \
include-what-you-use -D _DEBUG -std=c++20 -Wall "$FILE" \
-I../source \
-Xiwyu --mapping_file=sdl3_mapping.imp \
-Xiwyu --update_comments \
-Xiwyu --verbose=3 \
| \
python3 /usr/bin/fix_includes.py --update_comments
-Xiwyu --verbose=3

View File

@@ -1,22 +1,17 @@
// IWYU pragma: no_include <bits/chrono.h>
#include "director.h"
#include <SDL3/SDL.h> // Para SDL_LogCategory, SDL_LogInfo, SDL_SetLogPriority, SDL_LogPriority, SDL_Quit
#include <sys/stat.h> // Para mkdir, stat, S_IRWXU
#include <sys/wait.h>
#include <unistd.h> // Para getuid
#include <SDL3/SDL.h> // Para SDL_LogInfo, SDL_LogCategory, SDL_SetLogPriority, SDL_LogPriority, SDL_Quit
#include <cerrno> // Para errno, EEXIST, EACCES, ENAMETOOLONG
#include <cstdio> // Para printf, perror
#include <cstdlib> // Para exit, EXIT_FAILURE, srand, rand, system
#include <cstdlib> // Para srand, exit, rand, EXIT_FAILURE
#include <ctime> // Para time
#include <iostream> // Para std::cerr
#include <iostream> // Para basic_ostream, operator<<, cerr, endl
#include <memory> // Para make_unique, unique_ptr
#include <span> // Para span
#include <stdexcept> // Para runtime_error
#include <string> // Para operator+, allocator, char_traits, operator==, string, basic_string
#include <string> // Para allocator, char_traits, operator==, string, basic_string, operator+
#include "asset.h" // Para Asset, AssetType
#include "asset.h" // Para Asset
#include "audio.h" // Para Audio
#include "input.h" // Para Input
#include "lang.h" // Para setLanguage
@@ -34,19 +29,12 @@
#include "sections/intro.h" // Para Intro
#include "sections/logo.h" // Para Logo
#include "sections/title.h" // Para Title
#include "shutdown.h" // Para resultToString, shutdownSystem, ShutdownResult
#include "system_utils.h" // Para createApplicationFolder, resultToString, Result
#include "ui/notifier.h" // Para Notifier
#include "ui/service_menu.h" // Para ServiceMenu
#include "utils.h" // Para Overrides, overrides, getPath
#ifndef _WIN32
#include <pwd.h> // Para getpwuid, passwd
#endif
#ifdef _WIN32
#include <winbase.h>
#include <windows.h>
#endif
// Constructor
Director::Director(int argc, std::span<char *> argv) {
#ifdef RECORDING
@@ -90,11 +78,7 @@ void Director::init() {
// Configuración inicial de parametros
Asset::init(executable_path_); // Inicializa el sistema de gestión de archivos
loadAssets(); // Crea el índice de archivos
Input::init(
Asset::get()->get("gamecontrollerdb.txt"),
Asset::get()->get("controllers.json")); // Carga configuración de controles
Input::init(Asset::get()->get("gamecontrollerdb.txt"), Asset::get()->get("controllers.json")); // Carga configuración de controles
Options::setConfigFile(Asset::get()->get("config.txt")); // Establece el fichero de configuración
Options::setControllersFile(Asset::get()->get("controllers.json")); // Establece el fichero de configuración de mandos
Options::loadFromFile(); // Carga el archivo de configuración
@@ -200,58 +184,14 @@ void Director::checkProgramArguments(int argc, std::span<char *> argv) {
// Crea la carpeta del sistema donde guardar datos
void Director::createSystemFolder(const std::string &folder) {
#ifdef _WIN32
system_folder_ = std::string(getenv("APPDATA")) + "/" + folder;
#elif __APPLE__
struct passwd *pw = getpwuid(getuid());
const char *homedir = pw->pw_dir;
system_folder_ = std::string(homedir) + "/Library/Application Support" + "/" + folder;
#elif __linux__
struct passwd *pw = getpwuid(getuid());
const char *homedir = pw->pw_dir;
system_folder_ = std::string(homedir) + "/.config/" + folder;
auto result = SystemUtils::createApplicationFolder(folder, system_folder_);
{
// Intenta crear ".config", per si no existeix
std::string config_base_folder = std::string(homedir) + "/.config";
int ret = mkdir(config_base_folder.c_str(), S_IRWXU);
if (ret == -1 && errno != EEXIST) {
printf("ERROR CREATING CONFIG BASE FOLDER.");
if (result != SystemUtils::Result::SUCCESS) {
std::cerr << "Error creando carpeta del sistema: "
<< SystemUtils::resultToString(result) << std::endl;
exit(EXIT_FAILURE);
}
}
#endif
struct stat st = {0};
if (stat(system_folder_.c_str(), &st) == -1) {
errno = 0;
#ifdef _WIN32
int ret = mkdir(system_folder_.c_str());
#else
int ret = mkdir(system_folder_.c_str(), S_IRWXU);
#endif
if (ret == -1) {
switch (errno) {
case EACCES:
printf("the parent directory does not allow write");
exit(EXIT_FAILURE);
case EEXIST:
printf("pathname already exists");
exit(EXIT_FAILURE);
case ENAMETOOLONG:
printf("pathname is too long");
exit(EXIT_FAILURE);
default:
perror("mkdir");
exit(EXIT_FAILURE);
}
}
}
}
// Ejecuta la sección con el logo
void Director::runLogo() {
@@ -377,75 +317,11 @@ auto Director::run() -> int {
// Apaga el sistema de forma segura
void Director::shutdownSystem(bool should_shutdown) {
if (!should_shutdown) {
return;
}
if (should_shutdown) {
auto result = SystemShutdown::shutdownSystem(5, true); // 5 segundos, forzar apps
#ifdef _WIN32
// Windows: Usar API nativa de Windows
if (!InitiateSystemShutdownA(
NULL, // lpMachineName (NULL = local machine)
"Sistema apagándose...", // lpMessage
5, // dwTimeout (5 segundos)
TRUE, // bForceAppsClosed
FALSE // bRebootAfterShutdown (FALSE = shutdown)
)) {
std::cerr << "Error: No se pudo iniciar el apagado en Windows. Código: "
<< GetLastError() << std::endl;
if (result != SystemShutdown::ShutdownResult::SUCCESS) {
std::cerr << SystemShutdown::resultToString(result) << std::endl;
}
#elif __APPLE__
// macOS: Usar execvp con fork
pid_t pid = fork();
if (pid == 0) {
// Proceso hijo - ejecutar shutdown
char *args[] = {
const_cast<char *>("shutdown"),
const_cast<char *>("-h"),
const_cast<char *>("+0.1"),
NULL};
execvp("shutdown", args);
// Si execvp falla, salir del proceso hijo
std::cerr << "Error: execvp falló en macOS" << std::endl;
_exit(1);
} else if (pid > 0) {
// Proceso padre - esperar al hijo
int status;
waitpid(pid, &status, 0);
if (WEXITSTATUS(status) != 0) {
std::cerr << "Error: Comando shutdown falló en macOS" << std::endl;
}
} else {
std::cerr << "Error: No se pudo crear proceso fork en macOS" << std::endl;
}
#elif __linux__
// Linux: Usar execvp con fork
pid_t pid = fork();
if (pid == 0) {
// Proceso hijo - primero sleep, luego shutdown
sleep(5);
char *args[] = {
const_cast<char *>("shutdown"),
const_cast<char *>("-h"),
const_cast<char *>("now"),
NULL};
execvp("shutdown", args);
// Si execvp falla, salir del proceso hijo
std::cerr << "Error: execvp falló en Linux" << std::endl;
_exit(1);
} else if (pid > 0) {
// Proceso padre - esperar al hijo
int status;
waitpid(pid, &status, 0);
if (WEXITSTATUS(status) != 0) {
std::cerr << "Error: Comando shutdown falló en Linux" << std::endl;
}
} else {
std::cerr << "Error: No se pudo crear proceso fork en Linux" << std::endl;
}
#else
#error "Sistema operativo no soportado"
#endif
}

View File

@@ -1,7 +1,7 @@
#pragma once
#include <span>
#include <string> // Para manejar cadenas de texto
#include <span> // Para Span
#include <string> // Para string
namespace Lang {
enum class Code : int;

165
source/shutdown.cpp Normal file
View File

@@ -0,0 +1,165 @@
#include "shutdown.h"
#include <iostream>
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#include <sys/wait.h>
#include <string>
#endif
namespace SystemShutdown {
#ifndef _WIN32
// Función auxiliar para sistemas Unix-like
ShutdownResult executeUnixShutdown(const char* command, char* const args[]) {
pid_t pid = fork();
if (pid == 0) {
// Proceso hijo
execvp(command, args);
// Si llegamos aquí, execvp falló
std::cerr << "Error: No se pudo ejecutar " << command << std::endl;
_exit(1);
} else if (pid > 0) {
// Proceso padre
int status;
waitpid(pid, &status, 0);
return (WEXITSTATUS(status) == 0) ? ShutdownResult::SUCCESS : ShutdownResult::ERROR_SYSTEM_CALL;
} else {
return ShutdownResult::ERROR_FORK_FAILED;
}
}
#endif
// Implementación de las funciones públicas
ShutdownResult shutdownSystem() {
ShutdownConfig config;
return shutdownSystem(config);
}
ShutdownResult shutdownSystem(int delay_seconds, bool force_apps) {
ShutdownConfig config;
config.delay_seconds = delay_seconds;
config.force_close_apps = force_apps;
return shutdownSystem(config);
}
ShutdownResult shutdownSystem(const ShutdownConfig& config) {
#ifdef _WIN32
// Windows: Usar CreateProcess
STARTUPINFOA si = {0};
PROCESS_INFORMATION pi = {0};
si.cb = sizeof(si);
// Crear comando con el delay especificado
std::string command = "shutdown.exe /s /t " + std::to_string(config.delay_seconds);
if (config.force_close_apps) {
command += " /f";
}
// CreateProcess necesita un array de char modificable
char* cmd_buffer = new char[command.length() + 1];
strcpy(cmd_buffer, command.c_str());
bool success = CreateProcessA(
NULL, // lpApplicationName
cmd_buffer, // lpCommandLine
NULL, // lpProcessAttributes
NULL, // lpThreadAttributes
FALSE, // bInheritHandles
0, // dwCreationFlags
NULL, // lpEnvironment
NULL, // lpCurrentDirectory
&si, // lpStartupInfo
&pi // lpProcessInformation
);
delete[] cmd_buffer;
if (success) {
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return ShutdownResult::SUCCESS;
} else {
DWORD error = GetLastError();
if (error == ERROR_ACCESS_DENIED) {
return ShutdownResult::ERROR_PERMISSION;
}
return ShutdownResult::ERROR_SYSTEM_CALL;
}
#elif __APPLE__
// macOS
std::string delay_str = "+" + std::to_string(config.delay_seconds / 60.0, 1); // convertir a minutos
if (config.delay_seconds < 60) {
delay_str = "+0.1"; // mínimo delay en macOS
}
char* args[] = {
const_cast<char*>("shutdown"),
const_cast<char*>("-h"),
const_cast<char*>(delay_str.c_str()),
NULL
};
return executeUnixShutdown("shutdown", args);
#elif __linux__
// Linux
std::string delay_str = "+" + std::to_string((config.delay_seconds + 59) / 60); // redondear hacia arriba a minutos
if (config.delay_seconds == 0) {
delay_str = "now";
}
char* args[] = {
const_cast<char*>("shutdown"),
const_cast<char*>("-h"),
const_cast<char*>(delay_str.c_str()),
NULL
};
return executeUnixShutdown("shutdown", args);
#else
return ShutdownResult::ERROR_UNSUPPORTED;
#endif
}
const char* resultToString(ShutdownResult result) {
switch (result) {
case ShutdownResult::SUCCESS:
return "Apagado iniciado exitosamente";
case ShutdownResult::ERROR_PERMISSION:
return "Error: Permisos insuficientes";
case ShutdownResult::ERROR_SYSTEM_CALL:
return "Error: Fallo en la llamada al sistema";
case ShutdownResult::ERROR_FORK_FAILED:
return "Error: No se pudo crear proceso hijo";
case ShutdownResult::ERROR_UNSUPPORTED:
return "Error: Sistema operativo no soportado";
default:
return "Error desconocido";
}
}
bool isShutdownSupported() {
#if defined(_WIN32) || defined(__APPLE__) || defined(__linux__)
return true;
#else
return false;
#endif
}
const char* getRequiredPermissions() {
#ifdef _WIN32
return "Requiere permisos de Administrador en Windows";
#elif defined(__APPLE__) || defined(__linux__)
return "Requiere permisos de root/sudo en Unix";
#else
return "Sistema no soportado";
#endif
}
} // namespace SystemShutdown

47
source/shutdown.h Normal file
View File

@@ -0,0 +1,47 @@
#pragma once
// Utilidad multiplataforma para apagar el sistema de forma segura
namespace SystemShutdown {
// Códigos de resultado para las operaciones de apagado
enum class ShutdownResult {
SUCCESS = 0,
ERROR_PERMISSION, // Error de permisos insuficientes
ERROR_SYSTEM_CALL, // Error en la llamada al sistema
ERROR_FORK_FAILED, // Error al crear proceso hijo (Unix)
ERROR_UNSUPPORTED // Sistema operativo no soportado
};
// Configuración para el apagado del sistema
struct ShutdownConfig {
int delay_seconds;
bool force_close_apps;
const char* shutdown_message;
// Constructor con valores por defecto
ShutdownConfig()
: delay_seconds(5)
, force_close_apps(true)
, shutdown_message("El sistema se apagará...")
{}
};
// Apaga el sistema con configuración por defecto
ShutdownResult shutdownSystem();
// Apaga el sistema con configuración personalizada
ShutdownResult shutdownSystem(const ShutdownConfig& config);
// Apaga el sistema con parámetros simples
ShutdownResult shutdownSystem(int delay_seconds, bool force_apps = true);
// Convierte un código de resultado a string descriptivo
const char* resultToString(ShutdownResult result);
// Verifica si el sistema actual soporta apagado programático
bool isShutdownSupported();
// Obtiene información sobre los permisos necesarios en la plataforma actual
const char* getRequiredPermissions();
} // namespace SystemShutdown

190
source/system_utils.cpp Normal file
View File

@@ -0,0 +1,190 @@
#include "system_utils.h"
#include <iostream>
#include <sys/stat.h>
#include <cerrno>
#include <cstdlib>
#ifdef _WIN32
#include <windows.h>
#include <shlobj.h>
#include <direct.h>
// Evitar conflictos con macros de Windows
#ifdef ERROR_ALREADY_EXISTS
#undef ERROR_ALREADY_EXISTS
#endif
#else
#include <pwd.h>
#include <unistd.h>
#endif
namespace SystemUtils {
// Función auxiliar para crear una carpeta individual
Result createSingleFolder(const std::string& path, int permissions) {
struct stat st = {0};
// Verificar si ya existe
if (stat(path.c_str(), &st) == 0) {
return Result::SUCCESS; // Ya existe, no es error por defecto
}
// Intentar crear la carpeta
int result;
#ifdef _WIN32
result = _mkdir(path.c_str());
#else
result = mkdir(path.c_str(), permissions);
#endif
if (result == -1) {
switch (errno) {
case EACCES:
return Result::PERMISSION_DENIED;
case EEXIST:
return Result::ALREADY_EXISTS;
case ENAMETOOLONG:
return Result::PATH_TOO_LONG;
default:
return Result::UNKNOWN_ERROR;
}
}
return Result::SUCCESS;
}
// Función auxiliar para crear carpetas padre recursivamente
Result createParentFolders(const std::string& path, int permissions) {
size_t pos = 0;
while ((pos = path.find('/', pos + 1)) != std::string::npos) {
std::string parent = path.substr(0, pos);
if (!parent.empty() && !folderExists(parent)) {
Result result = createSingleFolder(parent, permissions);
if (result != Result::SUCCESS && result != Result::ALREADY_EXISTS) {
return result;
}
}
}
return Result::SUCCESS;
}
Result createApplicationFolder(const std::string& app_name, std::string& out_path) {
FolderConfig config;
return createApplicationFolder(app_name, out_path, config);
}
Result createApplicationFolder(const std::string& app_name, std::string& out_path, const FolderConfig& config) {
out_path = getApplicationDataPath(app_name);
return createFolder(out_path, config);
}
Result createFolder(const std::string& path) {
FolderConfig config;
return createFolder(path, config);
}
Result createFolder(const std::string& path, const FolderConfig& config) {
if (path.empty()) {
return Result::INVALID_PATH;
}
// Verificar si ya existe y si eso es un error
if (folderExists(path) && config.fail_if_exists) {
return Result::ALREADY_EXISTS;
}
// Crear carpetas padre si es necesario
if (config.create_parents) {
Result parent_result = createParentFolders(path, config.permissions);
if (parent_result != Result::SUCCESS) {
return parent_result;
}
}
// Crear la carpeta final
return createSingleFolder(path, config.permissions);
}
std::string getApplicationDataPath(const std::string& app_name) {
#ifdef _WIN32
char* appdata = getenv("APPDATA");
if (appdata) {
return std::string(appdata) + "/" + app_name;
}
return "C:/Users/Default/AppData/Roaming/" + app_name;
#elif __APPLE__
std::string home = getHomeDirectory();
return home + "/Library/Application Support/" + app_name;
#elif __linux__
std::string home = getHomeDirectory();
return home + "/.config/" + app_name;
#else
// Fallback genérico
std::string home = getHomeDirectory();
return home + "/." + app_name;
#endif
}
bool folderExists(const std::string& path) {
struct stat st = {0};
return (stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode));
}
const char* resultToString(Result result) {
switch (result) {
case Result::SUCCESS:
return "Operación exitosa";
case Result::PERMISSION_DENIED:
return "Error: Permisos insuficientes";
case Result::PATH_TOO_LONG:
return "Error: Ruta demasiado larga";
case Result::ALREADY_EXISTS:
return "Error: La carpeta ya existe";
case Result::INVALID_PATH:
return "Error: Ruta inválida";
case Result::UNKNOWN_ERROR:
return "Error desconocido";
default:
return "Error no identificado";
}
}
std::string getHomeDirectory() {
#ifdef _WIN32
char* userprofile = getenv("USERPROFILE");
if (userprofile) {
return std::string(userprofile);
}
return "C:/Users/Default";
#else
struct passwd *pw = getpwuid(getuid());
if (pw && pw->pw_dir) {
return std::string(pw->pw_dir);
}
// Fallback
char* home = getenv("HOME");
if (home) {
return std::string(home);
}
return "/tmp";
#endif
}
std::string getTempDirectory() {
#ifdef _WIN32
char* temp = getenv("TEMP");
if (temp) {
return std::string(temp);
}
return "C:/Windows/Temp";
#else
return "/tmp";
#endif
}
} // namespace SystemUtils

59
source/system_utils.h Normal file
View File

@@ -0,0 +1,59 @@
#pragma once
#include <string>
// Utilidades multiplataforma para operaciones del sistema
namespace SystemUtils {
// Códigos de resultado para operaciones del sistema
enum class Result {
SUCCESS = 0,
PERMISSION_DENIED, // Sin permisos para crear la carpeta
PATH_TOO_LONG, // Ruta demasiado larga
ALREADY_EXISTS, // Ya existe (solo si se considera error)
INVALID_PATH, // Ruta inválida
UNKNOWN_ERROR // Error desconocido
};
// Configuración para creación de carpetas
struct FolderConfig {
bool create_parents; // Crear carpetas padre si no existen
bool fail_if_exists; // Fallar si la carpeta ya existe
int permissions; // Permisos Unix (ignorado en Windows)
// Constructor con valores por defecto
FolderConfig()
: create_parents(true)
, fail_if_exists(false)
, permissions(0755) // rwxr-xr-x
{}
};
// Crea la carpeta del sistema donde guardar datos de la aplicación
Result createApplicationFolder(const std::string& app_name, std::string& out_path);
// Crea la carpeta del sistema con configuración personalizada
Result createApplicationFolder(const std::string& app_name, std::string& out_path, const FolderConfig& config);
// Crea una carpeta en la ruta especificada
Result createFolder(const std::string& path);
// Crea una carpeta con configuración personalizada
Result createFolder(const std::string& path, const FolderConfig& config);
// Obtiene la ruta de datos de la aplicación (sin crearla)
std::string getApplicationDataPath(const std::string& app_name);
// Verifica si una carpeta existe
bool folderExists(const std::string& path);
// Convierte un código de resultado a string descriptivo
const char* resultToString(Result result);
// Obtiene el directorio home del usuario
std::string getHomeDirectory();
// Obtiene el directorio temporal del sistema
std::string getTempDirectory();
} // namespace SystemUtils