mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-09 13:01:29 -10:00
This will use SDL_GetPrefPath to determine a suitable config directory which will be used for all runtime writing. Additionally all platforms will now use the configuration ini files, the Windows registry will be deprecated. For better compatibility with current installs there is a "legacy mode" which will work like the current install. It will be used as long as the launcher doesn't write the configuration file to the new location. Migrating old installs requires copying the existing pilot files to the new location and copying any existing configuration data.
551 lines
12 KiB
C++
551 lines
12 KiB
C++
/*
|
|
* Copyright (C) Volition, Inc. 1999. All rights reserved.
|
|
*
|
|
* All source code herein is the property of Volition, Inc. You may not sell
|
|
* or otherwise commercially exploit the source or things you created based on the
|
|
* source.
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <stdarg.h>
|
|
#include <algorithm>
|
|
|
|
#include "globalincs/pstypes.h"
|
|
#include "io/key.h"
|
|
#include "palman/palman.h"
|
|
#include "io/mouse.h"
|
|
#include "osapi/outwnd.h"
|
|
#include "sound/sound.h"
|
|
#include "freespace2/freespaceresource.h"
|
|
#include "playerman/managepilot.h"
|
|
#include "io/joy.h"
|
|
#include "io/joy_ff.h"
|
|
#include "gamesequence/gamesequence.h"
|
|
#include "freespace2/freespace.h"
|
|
#include "osapi/osregistry.h"
|
|
#include "cmdline/cmdline.h"
|
|
#include "sound/voicerec.h"
|
|
#include "graphics/2d.h"
|
|
#include "cmdline/cmdline.h"
|
|
|
|
|
|
#define THREADED // to use the proper set of macros
|
|
#include "osapi/osapi.h"
|
|
|
|
#include "SDL_syswm.h"
|
|
|
|
#include <SDL_assert.h>
|
|
|
|
|
|
// used to be a THREADED define but only use multiple process threads if this is defined
|
|
// NOTE: may hang if set
|
|
//#define THREADED_PROCESS
|
|
|
|
namespace
|
|
{
|
|
const char* ORGANIZATION_NAME = "HardLightProductions";
|
|
const char* APPLICATION_NAME = "FreeSpaceOpen";
|
|
|
|
char* preferencesPath = nullptr;
|
|
|
|
bool checkedLegacyMode = false;
|
|
bool legacyMode = false;
|
|
|
|
const char* getPreferencesPath()
|
|
{
|
|
// Lazily initialize the preferences path
|
|
if (!preferencesPath) {
|
|
preferencesPath = SDL_GetPrefPath(ORGANIZATION_NAME, APPLICATION_NAME);
|
|
if (!preferencesPath) {
|
|
mprintf(("Failed to get preferences path from SDL: %s\n", SDL_GetError()));
|
|
}
|
|
}
|
|
|
|
if (preferencesPath) {
|
|
return preferencesPath;
|
|
}
|
|
else {
|
|
// No preferences path, try current directory
|
|
return "." DIR_SEPARATOR_STR;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------------------
|
|
// PLATFORM SPECIFIC FUNCTION FOLLOWING
|
|
//
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
// Windows specific includes
|
|
#include <windows.h>
|
|
#include <windowsx.h>
|
|
#include <commctrl.h>
|
|
#include <io.h>
|
|
#include <winsock.h>
|
|
#include <direct.h>
|
|
|
|
// For FRED
|
|
void os_set_window_from_hwnd(HWND handle)
|
|
{
|
|
SDL_InitSubSystem(SDL_INIT_VIDEO);
|
|
|
|
if (SDL_GL_LoadLibrary(NULL) < 0)
|
|
Error(LOCATION, "Failed to load OpenGL library: %s!", SDL_GetError());
|
|
|
|
SDL_Window* window = SDL_CreateWindowFrom((void*) handle);
|
|
|
|
os_set_window(window);
|
|
}
|
|
|
|
// go through all windows and try and find the one that matches the search string
|
|
BOOL __stdcall os_enum_windows( HWND hwnd, char * search_string )
|
|
{
|
|
char tmp[128];
|
|
int len;
|
|
|
|
len = GetWindowText( hwnd, tmp, 127 );
|
|
|
|
if ( len ) {
|
|
if ( strstr( tmp, search_string )) {
|
|
Os_debugger_running = 1; // found the search string!
|
|
return FALSE; // stop enumerating windows
|
|
}
|
|
}
|
|
|
|
return TRUE; // continue enumeration
|
|
}
|
|
|
|
// Fills in the Os_debugger_running with non-zero if debugger detected.
|
|
void os_check_debugger()
|
|
{
|
|
HMODULE hMod;
|
|
char search_string[256];
|
|
char myname[128];
|
|
int namelen;
|
|
char * p;
|
|
|
|
Os_debugger_running = 0; // Assume its not
|
|
|
|
// Find my EXE file name
|
|
hMod = GetModuleHandle(NULL);
|
|
if ( !hMod ) return;
|
|
namelen = GetModuleFileName( hMod, myname, 127 );
|
|
if ( namelen < 1 ) return;
|
|
|
|
// Strip off the .EXE
|
|
p = strstr( myname, ".exe" );
|
|
if (!p) return;
|
|
*p = '\0';
|
|
|
|
// Move p to point to first letter of EXE filename
|
|
while( (*p!='\\') && (*p!='/') && (*p!=':') )
|
|
p--;
|
|
p++;
|
|
if ( strlen(p) < 1 ) return;
|
|
|
|
// Build what the debugger's window title would be if the debugger is running...
|
|
sprintf( search_string, "[run] - %s -", p );
|
|
|
|
// ... and then search for it.
|
|
EnumWindows( (int (__stdcall *)(struct HWND__ *,long))os_enum_windows, (long)&search_string );
|
|
}
|
|
|
|
void os_set_process_affinity()
|
|
{
|
|
HANDLE pHandle = GetCurrentProcess();
|
|
DWORD pMaskProcess = 0, pMaskSystem = 0;
|
|
|
|
if ( GetProcessAffinityMask(pHandle, &pMaskProcess, &pMaskSystem) ) {
|
|
// only do this if we have at least 2 procs
|
|
if (pMaskProcess >= 3) {
|
|
// prefer running on the second processor by default
|
|
pMaskProcess = os_config_read_uint(NULL, "ProcessorAffinity", 2);
|
|
|
|
if (pMaskProcess > 0) {
|
|
SetProcessAffinityMask(pHandle, pMaskProcess);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // WIN32
|
|
|
|
|
|
// ----------------------------------------------------------------------------------------------------
|
|
// OSAPI DEFINES/VARS
|
|
//
|
|
|
|
static SDL_Window* main_window = NULL;
|
|
|
|
// os-wide globals
|
|
static bool fAppActive = false;
|
|
static char szWinTitle[128];
|
|
static char szWinClass[128];
|
|
static int Os_inited = 0;
|
|
|
|
static SDL_mutex* Os_lock;
|
|
|
|
int Os_debugger_running = 0;
|
|
|
|
// ----------------------------------------------------------------------------------------------------
|
|
// OSAPI FORWARD DECLARATIONS
|
|
//
|
|
|
|
// called at shutdown. Makes sure all thread processing terminates.
|
|
void os_deinit();
|
|
|
|
// ----------------------------------------------------------------------------------------------------
|
|
// OSAPI FUNCTIONS
|
|
//
|
|
|
|
// initialization/shutdown functions -----------------------------------------------
|
|
|
|
// If app_name is NULL or ommited, then TITLE is used
|
|
// for the app name, which is where registry keys are stored.
|
|
void os_init(const char * wclass, const char * title, const char *app_name, const char *version_string )
|
|
{
|
|
os_init_registry_stuff(Osreg_company_name, title, version_string);
|
|
|
|
strcpy_s( szWinTitle, title );
|
|
strcpy_s( szWinClass, wclass );
|
|
|
|
Os_lock = SDL_CreateMutex();
|
|
|
|
mprintf((" Initializing SDL...\n"));
|
|
|
|
if (SDL_Init(SDL_INIT_EVENTS) < 0)
|
|
{
|
|
fprintf(stderr, "Couldn't init SDL: %s", SDL_GetError());
|
|
mprintf(("Couldn't init SDL: %s", SDL_GetError()));
|
|
|
|
exit(1);
|
|
return;
|
|
}
|
|
|
|
#ifdef FS2_VOICER
|
|
SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); // We currently only need this for voice recognition
|
|
#endif
|
|
|
|
// initialized
|
|
Os_inited = 1;
|
|
|
|
#ifdef WIN32
|
|
// check to see if we're running under msdev
|
|
os_check_debugger();
|
|
|
|
if (Cmdline_set_cpu_affinity)
|
|
{
|
|
// deal with processor affinity
|
|
os_set_process_affinity();
|
|
}
|
|
#endif // WIN32
|
|
|
|
atexit(os_deinit);
|
|
}
|
|
|
|
// set the main window title
|
|
void os_set_title( const char * title )
|
|
{
|
|
strcpy_s( szWinTitle, title );
|
|
|
|
SDL_SetWindowTitle(main_window, szWinTitle);
|
|
}
|
|
|
|
extern void gr_opengl_shutdown();
|
|
// call at program end
|
|
void os_cleanup()
|
|
{
|
|
gr_opengl_shutdown();
|
|
|
|
#ifndef NDEBUG
|
|
outwnd_close();
|
|
#endif
|
|
}
|
|
|
|
// window management -----------------------------------------------------------------
|
|
|
|
// Returns 1 if app is not the foreground app.
|
|
int os_foreground()
|
|
{
|
|
return fAppActive;
|
|
}
|
|
|
|
// Returns the handle to the main window
|
|
SDL_Window* os_get_window()
|
|
{
|
|
return main_window;
|
|
}
|
|
|
|
// Returns the handle to the main window
|
|
void os_set_window(SDL_Window* new_handle)
|
|
{
|
|
main_window = new_handle;
|
|
fAppActive = true;
|
|
}
|
|
|
|
// process management -----------------------------------------------------------------
|
|
|
|
// Sleeps for n milliseconds or until app becomes active.
|
|
void os_sleep(uint ms)
|
|
{
|
|
#ifdef __APPLE__
|
|
// ewwww, I hate this!! SDL_Delay() is causing issues for us though and this
|
|
// basically matches Apple examples of the same thing. Same as SDL_Delay() but
|
|
// we aren't hitting up the system for anything during the process
|
|
uint then = SDL_GetTicks() + ms;
|
|
|
|
while (then > SDL_GetTicks());
|
|
#else
|
|
SDL_Delay(ms);
|
|
#endif
|
|
}
|
|
|
|
// Used to stop message processing
|
|
void os_suspend()
|
|
{
|
|
SDL_LockMutex( Os_lock );
|
|
}
|
|
|
|
// resume message processing
|
|
void os_resume()
|
|
{
|
|
SDL_UnlockMutex( Os_lock );
|
|
}
|
|
|
|
bool os_is_legacy_mode()
|
|
{
|
|
// Make this check a little faster by caching the result
|
|
if (checkedLegacyMode)
|
|
{
|
|
return legacyMode;
|
|
}
|
|
|
|
if (Cmdline_portable_mode) {
|
|
// When the portable mode option is given, non-legacy is implied
|
|
legacyMode = false;
|
|
checkedLegacyMode = true;
|
|
}
|
|
else {
|
|
SCP_stringstream path_stream;
|
|
path_stream << getPreferencesPath() << DIR_SEPARATOR_CHAR << Osreg_config_file_name;
|
|
|
|
// Use the existance of the fs2_open.ini file for determining if the launcher supports the new mode
|
|
auto file = fopen(path_stream.str().c_str(), "r");
|
|
|
|
if (file == nullptr)
|
|
{
|
|
legacyMode = true;
|
|
}
|
|
else
|
|
{
|
|
fclose(file);
|
|
legacyMode = false;
|
|
}
|
|
}
|
|
|
|
if (legacyMode) {
|
|
// Print a message for the people running it from the terminal
|
|
fprintf(stdout, "FSO is running in legacy config mode. Please either update your launcher or"
|
|
" copy the configuration and pilot files to '%s' for better future compatibility.", getPreferencesPath());
|
|
}
|
|
|
|
checkedLegacyMode = true;
|
|
return legacyMode;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------------------
|
|
// OSAPI FORWARD DECLARATIONS
|
|
//
|
|
|
|
// called at shutdown. Makes sure all thread processing terminates.
|
|
void os_deinit()
|
|
{
|
|
if (preferencesPath) {
|
|
SDL_free(preferencesPath);
|
|
preferencesPath = nullptr;
|
|
}
|
|
|
|
SDL_DestroyMutex(Os_lock);
|
|
|
|
SDL_Quit();
|
|
}
|
|
|
|
extern SCP_map<int, int> SDLtoFS2;
|
|
extern void joy_set_button_state(int button, int state);
|
|
extern void joy_set_hat_state(int position);
|
|
|
|
void os_poll()
|
|
{
|
|
SDL_Event event;
|
|
|
|
while (SDL_PollEvent(&event)) {
|
|
switch (event.type) {
|
|
case SDL_WINDOWEVENT: {
|
|
if (event.window.windowID == SDL_GetWindowID(os_get_window())) {
|
|
switch (event.window.event) {
|
|
case SDL_WINDOWEVENT_MINIMIZED:
|
|
case SDL_WINDOWEVENT_FOCUS_LOST:
|
|
{
|
|
if (fAppActive) {
|
|
if (!Cmdline_no_unfocus_pause) {
|
|
game_pause();
|
|
}
|
|
|
|
fAppActive = false;
|
|
}
|
|
break;
|
|
}
|
|
case SDL_WINDOWEVENT_MAXIMIZED:
|
|
case SDL_WINDOWEVENT_RESTORED:
|
|
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
|
{
|
|
if (!fAppActive) {
|
|
if (!Cmdline_no_unfocus_pause) {
|
|
game_unpause();
|
|
}
|
|
|
|
fAppActive = true;
|
|
}
|
|
break;
|
|
}
|
|
case SDL_WINDOWEVENT_CLOSE:
|
|
gameseq_post_event(GS_EVENT_QUIT_GAME);
|
|
break;
|
|
}
|
|
}
|
|
|
|
gr_activate(fAppActive);
|
|
|
|
break;
|
|
}
|
|
|
|
case SDL_SYSWMEVENT:
|
|
#ifdef WIN32
|
|
#ifdef FS2_VOICER
|
|
switch(event.syswm.msg->msg.win.msg)
|
|
{
|
|
case WM_RECOEVENT:
|
|
if ( Game_mode & GM_IN_MISSION && Cmdline_voice_recognition)
|
|
{
|
|
VOICEREC_process_event( event.syswm.msg->msg.win.hwnd );
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
#endif // FS2_VOICER
|
|
#endif // WIN32
|
|
break;
|
|
|
|
case SDL_KEYDOWN:
|
|
if (SDLtoFS2[event.key.keysym.scancode]) {
|
|
key_mark(SDLtoFS2[event.key.keysym.scancode], 1, 0);
|
|
}
|
|
break;
|
|
|
|
case SDL_KEYUP:
|
|
if (SDLtoFS2[event.key.keysym.scancode]) {
|
|
key_mark(SDLtoFS2[event.key.keysym.scancode], 0, 0);
|
|
}
|
|
break;
|
|
|
|
case SDL_MOUSEBUTTONDOWN:
|
|
case SDL_MOUSEBUTTONUP:
|
|
if (event.button.button == SDL_BUTTON_LEFT)
|
|
mouse_mark_button(MOUSE_LEFT_BUTTON, event.button.state);
|
|
else if (event.button.button == SDL_BUTTON_MIDDLE)
|
|
mouse_mark_button(MOUSE_MIDDLE_BUTTON, event.button.state);
|
|
else if (event.button.button == SDL_BUTTON_RIGHT)
|
|
mouse_mark_button(MOUSE_RIGHT_BUTTON, event.button.state);
|
|
|
|
break;
|
|
|
|
case SDL_JOYAXISMOTION:
|
|
joy_event(event.jaxis.which, event.jaxis.axis, event.jaxis.value);
|
|
break;
|
|
|
|
case SDL_JOYHATMOTION:
|
|
joy_set_hat_state(event.jhat.value);
|
|
break;
|
|
|
|
case SDL_JOYBUTTONDOWN:
|
|
case SDL_JOYBUTTONUP:
|
|
if (event.jbutton.button < JOY_NUM_BUTTONS) {
|
|
joy_set_button_state(event.jbutton.button, event.jbutton.state);
|
|
}
|
|
break;
|
|
|
|
case SDL_JOYDEVICEADDED:
|
|
case SDL_JOYDEVICEREMOVED:
|
|
joy_device_changed(event.jdevice.type, event.jdevice.which);
|
|
break;
|
|
case SDL_MOUSEMOTION:
|
|
mouse_event(event.motion.x, event.motion.y, event.motion.xrel, event.motion.yrel);
|
|
break;
|
|
|
|
case SDL_MOUSEWHEEL:
|
|
mousewheel_motion(event.wheel.x, event.wheel.y);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
SCP_string os_get_config_path(const SCP_string& subpath)
|
|
{
|
|
// Make path platform compatible
|
|
SCP_string compatiblePath(subpath);
|
|
std::replace(compatiblePath.begin(), compatiblePath.end(), '/', DIR_SEPARATOR_CHAR);
|
|
|
|
SCP_stringstream ss;
|
|
|
|
if (Cmdline_portable_mode) {
|
|
// Use the current directory
|
|
ss << "." << DIR_SEPARATOR_CHAR << compatiblePath;
|
|
return ss.str();
|
|
}
|
|
|
|
// Avoid infinite recursion when checking legacy mode
|
|
if (os_is_legacy_mode()) {
|
|
#ifdef WIN32
|
|
// Use the current directory
|
|
ss << ".";
|
|
#else
|
|
extern const char* Osreg_user_dir_legacy;
|
|
// Use the home directory
|
|
ss << getenv("HOME") << DIR_SEPARATOR_CHAR << Osreg_user_dir_legacy;
|
|
#endif
|
|
|
|
ss << DIR_SEPARATOR_CHAR << compatiblePath;
|
|
return ss.str();
|
|
}
|
|
|
|
ss << getPreferencesPath() << compatiblePath;
|
|
|
|
return ss.str();
|
|
}
|
|
|
|
void debug_int3(char *file, int line)
|
|
{
|
|
mprintf(("Int3(): From %s at line %d\n", file, line));
|
|
|
|
gr_activate(0);
|
|
|
|
mprintf(("%s\n", dump_stacktrace().c_str()));
|
|
|
|
#ifndef NDEBUG
|
|
SDL_TriggerBreakpoint();
|
|
#endif
|
|
|
|
gr_activate(1);
|
|
// we have to call os_deinit() before abort() so we make sure that SDL gets
|
|
// closed out and we don't lose video/input control
|
|
os_deinit();
|
|
|
|
abort();
|
|
}
|