Permalink
Fetching contributors…
Cannot retrieve contributors at this time
1515 lines (1299 sloc) 41.5 KB
/*
* See Licensing and Copyright notice in naev.h
*/
/**
* @file options.c
*
* @brief Options menu
*/
#include "options.h"
#include "naev.h"
#include "nstring.h"
#include "SDL.h"
#include "log.h"
#include "input.h"
#include "toolkit.h"
#include "sound.h"
#include "music.h"
#include "nstd.h"
#include "dialogue.h"
#include "conf.h"
#include "ndata.h"
#include "player.h"
#define BUTTON_WIDTH 90 /**< Button width, standard across menus. */
#define BUTTON_HEIGHT 30 /**< Button height, standard across menus. */
#define OPT_WIN_GAMEPLAY 0
#define OPT_WIN_VIDEO 1
#define OPT_WIN_AUDIO 2
#define OPT_WIN_INPUT 3
#define OPT_WINDOWS 4
static unsigned int opt_wid = 0;
static unsigned int *opt_windows;
static const char *opt_names[] = {
"Gameplay",
"Video",
"Audio",
"Input"
};
static int opt_restart = 0;
/*
* External stuff.
*/
extern const char *keybind_info[][3]; /**< from input.c */
static const char *opt_selectedKeybind; /**< Selected keybinding. */
static int opt_lastKeyPress = 0; /**< Last keypress. */
/*
* prototypes
*/
/* Misc. */
static void opt_close( unsigned int wid, char *name );
static void opt_needRestart (void);
/* Gameplay. */
static void opt_gameplay( unsigned int wid );
static void opt_setAutonavResetSpeed( unsigned int wid, char *str );
static void opt_OK( unsigned int wid, char *str );
static int opt_gameplaySave( unsigned int wid, char *str );
static void opt_gameplayDefaults( unsigned int wid, char *str );
static void opt_gameplayUpdate( unsigned int wid, char *str );
/* Video. */
static void opt_video( unsigned int wid );
static void opt_videoRes( unsigned int wid, char *str );
static int opt_videoSave( unsigned int wid, char *str );
static void opt_videoDefaults( unsigned int wid, char *str );
/* Audio. */
static void opt_audio( unsigned int wid );
static int opt_audioSave( unsigned int wid, char *str );
static void opt_audioDefaults( unsigned int wid, char *str );
static void opt_audioUpdate( unsigned int wid );
static void opt_audioLevelStr( char *buf, int max, int type, double pos );
static void opt_setAudioLevel( unsigned int wid, char *str );
static void opt_beep( unsigned int wid, char *str );
/* Keybind menu. */
static void opt_keybinds( unsigned int wid );
static void menuKeybinds_getDim( unsigned int wid, int *w, int *h,
int *lw, int *lh, int *bw, int *bh );
static void menuKeybinds_genList( unsigned int wid );
static void menuKeybinds_update( unsigned int wid, char *name );
static void opt_keyDefaults( unsigned int wid, char *str );
/* Setting keybindings. */
static int opt_setKeyEvent( unsigned int wid, SDL_Event *event );
static void opt_setKey( unsigned int wid, char *str );
static void opt_unsetKey( unsigned int wid, char *str );
/**
* @brief Creates the options menu thingy.
*/
void opt_menu (void)
{
int w, h;
/* Dimensions. */
w = 600;
h = 525;
/* Create window and tabs. */
opt_wid = window_create( "Options", -1, -1, w, h );
window_setCancel( opt_wid, opt_close );
/* Create tabbed window. */
opt_windows = window_addTabbedWindow( opt_wid, -1, -1, -1, -1, "tabOpt",
OPT_WINDOWS, opt_names, 0 );
/* Load tabs. */
opt_gameplay( opt_windows[ OPT_WIN_GAMEPLAY ] );
opt_video( opt_windows[ OPT_WIN_VIDEO ] );
opt_audio( opt_windows[ OPT_WIN_AUDIO ] );
opt_keybinds( opt_windows[ OPT_WIN_INPUT ] );
/* Set as need restart if needed. */
if (opt_restart)
opt_needRestart();
}
/**
* @brief Saves all options and closes the options screen.
*/
static void opt_OK( unsigned int wid, char *str )
{
int ret;
ret = 0;
ret |= opt_gameplaySave( opt_windows[ OPT_WIN_GAMEPLAY ], str);
ret |= opt_audioSave( opt_windows[ OPT_WIN_AUDIO ], str);
ret |= opt_videoSave( opt_windows[ OPT_WIN_VIDEO ], str);
/* Close window if no errors occurred. */
if (!ret)
opt_close(wid, str);
}
/**
* @brief Closes the options screen without saving.
*/
static void opt_close( unsigned int wid, char *name )
{
(void) wid;
(void) name;
/* At this point, set sound levels as defined in the config file.
* This ensures that sound volumes are reset on "Cancel". */
sound_volume(conf.sound);
music_volume(conf.music);
window_destroy( opt_wid );
opt_wid = 0;
}
/**
* @brief Handles resize events for the options menu.
*/
void opt_resize (void)
{
char buf[16];
/* Nothing to do if not open. */
if (!opt_wid)
return;
/* Update the resolution input widget. */
nsnprintf( buf, sizeof(buf), "%dx%d", gl_screen.rw, gl_screen.rh );
window_setInput( opt_windows[OPT_WIN_VIDEO], "inpRes", buf );
}
/**
* @brief Opens the gameplay menu.
*/
static void opt_gameplay( unsigned int wid )
{
(void) wid;
char buf[PATH_MAX];
const char *path;
int cw;
int w, h, y, x, by, l;
char *s;
/* Get size. */
window_dimWindow( wid, &w, &h );
/* Close button */
window_addButton( wid, -20, 20,
BUTTON_WIDTH, BUTTON_HEIGHT,
"btnClose", "OK", opt_OK );
window_addButton( wid, -20 - 1*(BUTTON_WIDTH+20), 20,
BUTTON_WIDTH, BUTTON_HEIGHT,
"btnCancel", "Cancel", opt_close );
window_addButton( wid, -20 - 2*(BUTTON_WIDTH+20), 20,
BUTTON_WIDTH, BUTTON_HEIGHT,
"btnDefaults", "Defaults", opt_gameplayDefaults );
/* Information. */
cw = (w-40);
x = 20;
y = -60;
window_addText( wid, x, y, cw, 20, 1, "txtVersion",
NULL, NULL, naev_version(1) );
y -= 20;
#ifdef GIT_COMMIT
window_addText( wid, x, y, cw, 20, 1, "txtCommit",
NULL, NULL, "Commit: "GIT_COMMIT );
#endif /* GIT_COMMIT */
y -= 20;
path = ndata_getPath();
if (path == NULL)
nsnprintf( buf, sizeof(buf), "not using ndata" );
else
nsnprintf( buf, sizeof(buf), "ndata: %s", path);
window_addText( wid, x, y, cw, 20, 1, "txtNdata",
NULL, NULL, buf );
y -= 40;
by = y;
/* Compiletime stuff. */
cw = (w-60)/2;
y = by;
x = 20;
window_addText( wid, x+20, y, cw, 20, 0, "txtCompile",
NULL, &cDConsole, "Compilation Flags" );
y -= 30;
window_addText( wid, x, y, cw, h+y-20, 0, "txtFlags",
NULL, NULL,
""
#ifdef DEBUGGING
#ifdef DEBUG_PARANOID
"Debug Paranoid\n"
#else /* DEBUG_PARANOID */
"Debug\n"
#endif /* DEBUG_PARANOID */
#endif /* DEBUGGING */
#if defined(LINUX)
"Linux\n"
#elif defined(FREEBSD)
"FreeBSD\n"
#elif defined(MACOSX)
"Mac OS X\n"
#elif defined(WIN32)
"Windows\n"
#else
"Unknown OS\n"
#endif
#ifdef USE_OPENAL
"With OpenAL\n"
#endif /* USE_OPENAL */
#ifdef USE_SDLMIX
"With SDL_mixer\n"
#endif
#ifdef HAVE_LUAJIT
"Using LuaJIT\n"
#endif
#ifdef NDATA_DEF
"ndata: "NDATA_DEF"\n"
#endif /* NDATA_DEF */
);
/* Options. */
y = by;
/* Autonav abort. */
x = 20 + cw + 20;
window_addText( wid, x+65, y, 150, 150, 0, "txtAAutonav",
NULL, &cDConsole, "Stop Speedup At:" );
y -= 20;
/* Autonav abort fader. */
window_addText( wid, x, y, cw, 20, 1, "txtAutonav",
NULL, NULL, NULL );
y -= 20;
window_addFader( wid, x, y, cw, 20, "fadAutonav", 0., 1.,
conf.autonav_reset_speed, opt_setAutonavResetSpeed );
y -= 40;
window_addText( wid, x+20, y, cw, 20, 0, "txtSettings",
NULL, &cDConsole, "Settings" );
y -= 25;
window_addCheckbox( wid, x, y, cw, 20,
"chkZoomManual", "Enable manual zoom control", NULL, conf.zoom_manual );
y -= 25;
window_addCheckbox( wid, x, y, cw, 20,
"chkAfterburn", "Enable double-tap afterburn", NULL, conf.afterburn_sens );
y -= 25;
window_addCheckbox( wid, x, y, cw, 20,
"chkMouseThrust", "Enable mouse-flying thrust control", NULL, conf.mouse_thrust );
y -= 25;
window_addCheckbox( wid, x, y, cw, 20,
"chkCompress", "Enable savegame compression", NULL, conf.save_compress );
y -= 30;
s = "Visible Messages";
l = gl_printWidthRaw( NULL, s );
window_addText( wid, -100, y, l, 20, 1, "txtSMSG",
NULL, &cBlack, s );
window_addInput( wid, -50, y, 40, 20, "inpMSG", 4, 1, NULL );
y -= 30;
s = "Max Time Compression Factor";
l = gl_printWidthRaw( NULL, s );
window_addText( wid, -100, y, l, 20, 1, "txtTMax",
NULL, &cBlack, s );
window_addInput( wid, -50, y, 40, 20, "inpTMax", 4, 1, NULL );
/* Restart text. */
window_addText( wid, 20, 10, 3*(BUTTON_WIDTH + 20),
30, 0, "txtRestart", &gl_smallFont, &cBlack, NULL );
/* Update. */
opt_gameplayUpdate( wid, NULL );
}
/**
* @brief Saves the gameplay options.
*/
static int opt_gameplaySave( unsigned int wid, char *str )
{
(void) str;
int f;
char *vmsg, *tmax;
/* Checkboxes. */
f = window_checkboxState( wid, "chkAfterburn" );
if (!!conf.afterburn_sens != f)
conf.afterburn_sens = (!!f)*250;
conf.zoom_manual = window_checkboxState( wid, "chkZoomManual" );
conf.mouse_thrust = window_checkboxState(wid, "chkMouseThrust" );
conf.save_compress = window_checkboxState( wid, "chkCompress" );
/* Faders. */
conf.autonav_reset_speed = window_getFaderValue(wid, "fadAutonav");
/* Input boxes. */
vmsg = window_getInput( wid, "inpMSG" );
tmax = window_getInput( wid, "inpTMax" );
conf.mesg_visible = atoi(vmsg);
conf.compression_mult = atoi(tmax);
if (conf.mesg_visible == 0)
conf.mesg_visible = INPUT_MESSAGES_DEFAULT;
return 0;
}
/**
* @brief Sets the default gameplay options.
*/
static void opt_gameplayDefaults( unsigned int wid, char *str )
{
(void) str;
char vmsg[16], tmax[16];
/* Restore. */
/* Checkboxes. */
window_checkboxSet( wid, "chkZoomManual", MANUAL_ZOOM_DEFAULT );
window_checkboxSet( wid, "chkAfterburn", AFTERBURNER_SENSITIVITY_DEFAULT );
window_checkboxSet( wid, "chkMouseThrust", MOUSE_THRUST_DEFAULT );
window_checkboxSet( wid, "chkCompress", SAVE_COMPRESSION_DEFAULT );
/* Faders. */
window_faderValue( wid, "fadAutonav", AUTONAV_RESET_SPEED_DEFAULT );
/* Input boxes. */
nsnprintf( vmsg, sizeof(vmsg), "%d", INPUT_MESSAGES_DEFAULT );
window_setInput( wid, "inpMSG", vmsg );
nsnprintf( tmax, sizeof(tmax), "%d", TIME_COMPRESSION_DEFAULT_MULT );
window_setInput( wid, "inpTMax", tmax );
}
/**
* @brief Updates the gameplay options.
*/
static void opt_gameplayUpdate( unsigned int wid, char *str )
{
(void) str;
char vmsg[16], tmax[16];
/* Checkboxes. */
window_checkboxSet( wid, "chkZoomManual", conf.zoom_manual );
window_checkboxSet( wid, "chkAfterburn", conf.afterburn_sens );
window_checkboxSet( wid, "chkMouseThrust", conf.mouse_thrust );
window_checkboxSet( wid, "chkCompress", conf.save_compress );
/* Faders. */
window_faderValue( wid, "fadAutonav", conf.autonav_reset_speed );
/* Input boxes. */
nsnprintf( vmsg, sizeof(vmsg), "%d", conf.mesg_visible );
window_setInput( wid, "inpMSG", vmsg );
nsnprintf( tmax, sizeof(tmax), "%g", conf.compression_mult );
window_setInput( wid, "inpTMax", tmax );
}
/**
* @brief Callback to set autonav abort threshold.
*
* @param wid Window calling the callback.
* @param str Name of the widget calling the callback.
*/
static void opt_setAutonavResetSpeed( unsigned int wid, char *str )
{
char buf[PATH_MAX];
double autonav_reset_speed;
/* Set fader. */
autonav_reset_speed = window_getFaderValue(wid, str);
/* Generate message. */
if (autonav_reset_speed >= 1.)
nsnprintf( buf, sizeof(buf), "Enemy Presence" );
else if (autonav_reset_speed > 0.)
nsnprintf( buf, sizeof(buf), "%.0f%% Shield", autonav_reset_speed * 100 );
else
nsnprintf( buf, sizeof(buf), "Armour Damage" );
window_modifyText( wid, "txtAutonav", buf );
}
/**
* @brief Gets the keybind menu dimensions.
*/
static void menuKeybinds_getDim( unsigned int wid, int *w, int *h,
int *lw, int *lh, int *bw, int *bh )
{
/* Get window dimensions. */
window_dimWindow( wid, w, h );
/* Get button dimensions. */
if (bw != NULL)
*bw = BUTTON_WIDTH;
if (bh != NULL)
*bh = BUTTON_HEIGHT;
/* Get list dimensions. */
if (lw != NULL)
*lw = *w - 2*BUTTON_WIDTH - 80;
if (lh != NULL)
*lh = *h - 60;
}
/**
* @brief Opens the keybindings menu.
*/
static void opt_keybinds( unsigned int wid )
{
int w, h, lw, bw, bh;
/* Get dimensions. */
menuKeybinds_getDim( wid, &w, &h, &lw, NULL, &bw, &bh );
/* Close button. */
window_addButton( wid, -20, 20, bw, bh,
"btnClose", "OK", opt_OK );
/* Set button. */
window_addButton( wid, -20 - bw - 20, 20, bw, bh,
"btnSet", "Set Key", opt_setKey );
/* Restore deafaults button. */
window_addButton( wid, -20, 20+bh+20, bw, bh,
"btnDefaults", "Defaults", opt_keyDefaults );
/* Text stuff. */
window_addText( wid, 20+lw+20, -40, w-(20+lw+20), 30, 1, "txtName",
NULL, &cDConsole, NULL );
window_addText( wid, 20+lw+20, -90, w-(20+lw+20), h-70-60-bh,
0, "txtDesc", &gl_smallFont, NULL, NULL );
/* Generate the list. */
menuKeybinds_genList( wid );
}
/**
* @brief Generates the keybindings list.
*
* @param wid Window to update.
* @param regen Whether to destroy and recreate the widget.
*/
static void menuKeybinds_genList( unsigned int wid )
{
int i, j, l, p;
char **str, mod_text[64];
SDLKey key;
KeybindType type;
SDLMod mod;
int w, h;
int lw, lh;
int regen, pos, off;
/* Get dimensions. */
menuKeybinds_getDim( wid, &w, &h, &lw, &lh, NULL, NULL );
/* Create the list. */
for (i=0; strcmp(keybind_info[i][0],"end"); i++);
str = malloc(sizeof(char*) * i);
for (j=0; j < i; j++) {
l = 64;
str[j] = malloc(l);
key = input_getKeybind( keybind_info[j][0], &type, &mod );
switch (type) {
case KEYBIND_KEYBOARD:
/* Generate mod text. */
if (mod == NMOD_ALL)
nsnprintf( mod_text, sizeof(mod_text), "any+" );
else {
p = 0;
mod_text[0] = '\0';
if (mod & NMOD_SHIFT)
p += nsnprintf( &mod_text[p], sizeof(mod_text)-p, "shift+" );
if (mod & NMOD_CTRL)
p += nsnprintf( &mod_text[p], sizeof(mod_text)-p, "ctrl+" );
if (mod & NMOD_ALT)
p += nsnprintf( &mod_text[p], sizeof(mod_text)-p, "alt+" );
if (mod & NMOD_META)
p += nsnprintf( &mod_text[p], sizeof(mod_text)-p, "meta+" );
}
/* SDL_GetKeyName returns lowercase which is ugly. */
if (nstd_isalpha(key))
nsnprintf(str[j], l, "%s <%s%c>", keybind_info[j][1], mod_text, nstd_toupper(key) );
else
nsnprintf(str[j], l, "%s <%s%s>", keybind_info[j][1], mod_text, SDL_GetKeyName(key) );
break;
case KEYBIND_JAXISPOS:
nsnprintf(str[j], l, "%s <ja+%d>", keybind_info[j][1], key);
break;
case KEYBIND_JAXISNEG:
nsnprintf(str[j], l, "%s <ja-%d>", keybind_info[j][1], key);
break;
case KEYBIND_JBUTTON:
nsnprintf(str[j], l, "%s <jb%d>", keybind_info[j][1], key);
break;
default:
nsnprintf(str[j], l, "%s", keybind_info[j][1]);
break;
}
}
regen = widget_exists( wid, "lstKeybinds" );
if (regen) {
pos = toolkit_getListPos( wid, "lstKeybinds" );
off = toolkit_getListOffset( wid, "lstKeybinds" );
window_destroyWidget( wid, "lstKeybinds" );
}
window_addList( wid, 20, -40, lw, lh, "lstKeybinds",
str, i, 0, menuKeybinds_update );
if (regen) {
toolkit_setListPos( wid, "lstKeybinds", pos );
toolkit_setListOffset( wid, "lstKeybinds", off );
}
}
/**
* @brief Updates the keybindings menu.
*
* @param wid Window to update.
* @param name Unused.
*/
static void menuKeybinds_update( unsigned int wid, char *name )
{
(void) name;
int selected;
const char *keybind;
const char *desc;
SDLKey key;
KeybindType type;
SDLMod mod;
char buf[1024];
char binding[64];
/* Get the keybind. */
selected = toolkit_getListPos( wid, "lstKeybinds" );
/* Remove the excess. */
keybind = keybind_info[selected][0];
opt_selectedKeybind = keybind;
window_modifyText( wid, "txtName", keybind );
/* Get information. */
desc = input_getKeybindDescription( keybind );
key = input_getKeybind( keybind, &type, &mod );
/* Create the text. */
switch (type) {
case KEYBIND_NULL:
nsnprintf(binding, sizeof(binding), "Not bound");
break;
case KEYBIND_KEYBOARD:
/* SDL_GetKeyName returns lowercase which is ugly. */
if (nstd_isalpha(key))
nsnprintf(binding, sizeof(binding), "keyboard: %s%s%c",
(mod != KMOD_NONE) ? input_modToText(mod) : "",
(mod != KMOD_NONE) ? " + " : "",
nstd_toupper(key));
else
nsnprintf(binding, sizeof(binding), "keyboard: %s%s%s",
(mod != KMOD_NONE) ? input_modToText(mod) : "",
(mod != KMOD_NONE) ? " + " : "",
SDL_GetKeyName(key));
break;
case KEYBIND_JAXISPOS:
nsnprintf(binding, sizeof(binding), "joy axis pos: <%d>", key );
break;
case KEYBIND_JAXISNEG:
nsnprintf(binding, sizeof(binding), "joy axis neg: <%d>", key );
break;
case KEYBIND_JBUTTON:
nsnprintf(binding, sizeof(binding), "joy button: <%d>", key);
break;
}
/* Update text. */
nsnprintf(buf, 1024, "%s\n\n%s\n", desc, binding);
window_modifyText( wid, "txtDesc", buf );
}
/**
* @brief Restores the key defaults.
*/
static void opt_keyDefaults( unsigned int wid, char *str )
{
(void) str;
char *title, *caption, *ret;
int i, ind;
const int n = 3;
const char *opts[] = {
"WASD",
"Arrow Keys",
"Cancel"
};
title = "Restore Defaults";
caption = "Which layout do you want to use?";
dialogue_makeChoice( title, caption, 3 );
for (i=0; i<n; i++)
dialogue_addChoice( title, caption, opts[i] );
ret = dialogue_runChoice();
if (ret == NULL)
return;
/* Find the index of the matched option. */
ind = 0;
for (i=0; i<n; i++)
if (strcmp(ret, opts[i]) == 0) {
ind = i;
break;
}
if (ind == 2)
return;
/* Restore defaults. */
input_setDefault( (ind == 0) ? 1 : 0 );
/* Regenerate list widget. */
menuKeybinds_genList( wid );
/* Alert user it worked. */
dialogue_msgRaw( "Defaults Restored", "Keybindings restored to defaults.");
}
/**
* @brief Callback to set the sound or music level.
*
* @param wid Window calling the callback.
* @param str Name of the widget calling the callback.
* @param type 0 for sound, 1 for audio.
*/
static void opt_setAudioLevel( unsigned int wid, char *str )
{
char buf[32], *widget;
double vol;
vol = window_getFaderValue(wid, str);
if (strcmp(str,"fadSound")==0) {
sound_volume(vol);
widget = "txtSound";
opt_audioLevelStr( buf, sizeof(buf), 0, vol );
}
else {
music_volume(vol);
widget = "txtMusic";
opt_audioLevelStr( buf, sizeof(buf), 1, vol );
}
window_modifyText( wid, widget, buf );
}
/**
* @brief Sets the sound or music volume string based on level and sound backend.
*
* @param[out] buf Buffer to use.
* @param max Maximum length of the buffer.
* @param type 0 for sound, 1 for audio.
* @param pos Position of the fader calling the function.
*/
static void opt_audioLevelStr( char *buf, int max, int type, double pos )
{
double vol, magic;
char *str;
str = type ? "Music" : "Sound";
vol = type ? music_getVolumeLog() : sound_getVolumeLog();
if (vol == 0.)
nsnprintf( buf, max, "%s Volume: Muted", str );
else {
magic = -48. / log(0.00390625); /* -48 dB minimum divided by logarithm of volume floor. */
nsnprintf( buf, max, "%s Volume: %.2f (%.0f dB)", str, pos, log(vol) * magic );
}
}
/**
* @brief Opens the audio settings menu.
*/
static void opt_audio( unsigned int wid )
{
(void) wid;
int i, j;
int cw;
int w, h, y, x, l;
char **s;
const char *str;
/* Get size. */
window_dimWindow( wid, &w, &h );
/* Close button */
window_addButton( wid, -20, 20,
BUTTON_WIDTH, BUTTON_HEIGHT,
"btnClose", "OK", opt_OK );
window_addButton( wid, -20 - 1*(BUTTON_WIDTH+20), 20,
BUTTON_WIDTH, BUTTON_HEIGHT,
"btnCancel", "Cancel", opt_close );
window_addButton( wid, -20 - 2*(BUTTON_WIDTH+20), 20,
BUTTON_WIDTH, BUTTON_HEIGHT,
"btnDefaults", "Defaults", opt_audioDefaults );
/* General options. */
cw = (w-60)/2;
x = 20;
y = -60;
window_addText( wid, x+20, y, cw, 20, 0, "txtSGeneral",
NULL, &cDConsole, "General" );
y -= 30;
window_addCheckbox( wid, x, y, cw, 20,
"chkNosound", "Disable all sound/music", NULL, conf.nosound );
y -= 30;
str = "Backends";
l = gl_printWidthRaw( NULL, str );
window_addText( wid, x, y, l, 40, 0, "txtSBackends",
NULL, NULL, str );
l += 10;
i = 0;
j = 0;
s = malloc(sizeof(char*)*2);
#if USE_OPENAL
if (strcmp(conf.sound_backend,"openal")==0)
j = i;
s[i++] = strdup("openal");
#endif /* USE_OPENAL */
#if USE_SDLMIX
if (strcmp(conf.sound_backend,"sdlmix")==0)
j = i;
s[i++] = strdup("sdlmix");
#endif /* USE_SDLMIX */
if (i==0)
s[i++] = strdup("none");
window_addList( wid, x+l, y, cw-(x+l), 40, "lstSound", s, i, j, NULL );
y -= 50;
/* OpenAL options. */
window_addText( wid, x+20, y, cw, 20, 0, "txtSOpenal",
NULL, &cDConsole, "OpenAL" );
y -= 30;
window_addCheckbox( wid, x, y, cw, 20,
"chkEFX", "EFX (More CPU)", NULL, conf.al_efx );
/* Sound levels. */
x = 20 + cw + 20;
y = -60;
window_addText( wid, x+20, y, 100, 20, 0, "txtSVolume",
NULL, &cDConsole, "Volume Levels" );
y -= 30;
/* Sound fader. */
window_addText( wid, x, y, cw, 20, 1, "txtSound",
NULL, NULL, NULL );
y -= 20;
window_addFader( wid, x, y, cw, 20, "fadSound", 0., 1.,
sound_getVolume(), opt_setAudioLevel );
window_faderScrollDone( wid, "fadSound", opt_beep );
y -= 40;
/* Music fader. */
window_addText( wid, x, y, cw, 20, 1, "txtMusic",
NULL, NULL, NULL );
y -= 20;
window_addFader( wid, x, y, cw, 20, "fadMusic", 0., 1.,
music_getVolume(), opt_setAudioLevel );
/* Restart text. */
window_addText( wid, 20, 10, 3*(BUTTON_WIDTH + 20),
30, 0, "txtRestart", &gl_smallFont, &cBlack, NULL );
opt_audioUpdate(wid);
}
static void opt_beep( unsigned int wid, char *str )
{
(void) wid;
(void) str;
player_soundPlayGUI( snd_target, 1 );
}
/**
* @brief Saves the audio stuff.
*/
static int opt_audioSave( unsigned int wid, char *str )
{
(void) str;
int f;
char *s;
/* General. */
f = window_checkboxState( wid, "chkNosound" );
if (conf.nosound != f) {
conf.nosound = f;
opt_needRestart();
}
/* Backend. */
s = toolkit_getList( wid, "lstSound" );
if (conf.sound_backend != NULL) {
if (strcmp(s,conf.sound_backend)!=0) {
free(conf.sound_backend);
conf.sound_backend = strdup(s);
opt_needRestart();
}
}
else {
conf.sound_backend = strdup(s);
opt_needRestart();
}
/* OpenAL. */
f = window_checkboxState( wid, "chkEFX" );
if (conf.al_efx != f) {
conf.al_efx = f;
opt_needRestart();
}
/* Faders. */
conf.sound = window_getFaderValue(wid, "fadSound");
conf.music = window_getFaderValue(wid, "fadMusic");
return 0;
}
/**
* @brief Sets the audio defaults.
*/
static void opt_audioDefaults( unsigned int wid, char *str )
{
(void) str;
/* Set defaults. */
/* Faders. */
window_faderValue( wid, "fadSound", SOUND_VOLUME_DEFAULT );
window_faderValue( wid, "fadMusic", MUSIC_VOLUME_DEFAULT );
/* Checkboxes. */
window_checkboxSet( wid, "chkNosound", MUTE_SOUND_DEFAULT );
window_checkboxSet( wid, "chkEFX", USE_EFX_DEFAULT );
/* List. */
toolkit_setList( wid, "lstSound",
(conf.sound_backend==NULL) ? "none" : BACKEND_DEFAULT );
}
/**
* @brief Updates the gameplay options.
*/
static void opt_audioUpdate( unsigned int wid )
{
/* Checkboxes. */
window_checkboxSet( wid, "chkNosound", conf.nosound );
window_checkboxSet( wid, "chkEFX", conf.al_efx );
/* Faders. */
window_faderValue( wid, "fadSound", conf.sound );
window_faderValue( wid, "fadMusic", conf.music );
/* Backend box */
/* TODO */
}
/**
* @brief Tries to set the key from an event.
*/
static int opt_setKeyEvent( unsigned int wid, SDL_Event *event )
{
unsigned int parent;
KeybindType type;
int key, test_key_event;
SDLMod mod, ev_mod;
const char *str;
/* See how to handle it. */
switch (event->type) {
case SDL_KEYDOWN:
key = event->key.keysym.sym;
/* If control key make player hit twice. */
test_key_event = (key == SDLK_NUMLOCK) ||
(key == SDLK_CAPSLOCK) ||
(key == SDLK_SCROLLOCK) ||
(key == SDLK_RSHIFT) ||
(key == SDLK_LSHIFT) ||
(key == SDLK_RCTRL) ||
(key == SDLK_LCTRL) ||
(key == SDLK_RALT) ||
(key == SDLK_LALT) ||
(key == SDLK_RMETA) ||
(key == SDLK_LMETA);
#if !SDL_VERSION_ATLEAST(2,0,0) /* SUPER don't exist in 2.0.0 */
test_key_event = test_key_event ||
(key == SDLK_LSUPER) ||
(key == SDLK_RSUPER);
#endif /* !SDL_VERSION_ATLEAST(2,0,0) */
if (test_key_event && (opt_lastKeyPress != key)) {
opt_lastKeyPress = key;
return 0;
}
type = KEYBIND_KEYBOARD;
if (window_checkboxState( wid, "chkAny" ))
mod = NMOD_ALL;
else {
ev_mod = event->key.keysym.mod;
mod = 0;
if (ev_mod & (KMOD_LSHIFT | KMOD_RSHIFT))
mod |= NMOD_SHIFT;
if (ev_mod & (KMOD_LCTRL | KMOD_RCTRL))
mod |= NMOD_CTRL;
if (ev_mod & (KMOD_LALT | KMOD_RALT))
mod |= NMOD_ALT;
if (ev_mod & (KMOD_LMETA | KMOD_RMETA))
mod |= NMOD_META;
}
/* Set key. */
opt_lastKeyPress = key;
break;
case SDL_JOYAXISMOTION:
if (event->jaxis.value > 0)
type = KEYBIND_JAXISPOS;
else if (event->jaxis.value < 0)
type = KEYBIND_JAXISNEG;
else
return 0; /* Not handled. */
key = event->jaxis.axis;
mod = NMOD_ALL;
break;
case SDL_JOYBUTTONDOWN:
type = KEYBIND_JBUTTON;
key = event->jbutton.button;
mod = NMOD_ALL;
break;
/* Not handled. */
default:
return 0;
}
/* Warn if already bound. */
str = input_keyAlreadyBound( type, key, mod );
if ((str != NULL) && strcmp(str, opt_selectedKeybind))
dialogue_alert( "Key '%s' overlaps with key '%s' that was just set. "
"You may want to correct this.",
str, opt_selectedKeybind );
/* Set keybinding. */
input_setKeybind( opt_selectedKeybind, type, key, mod );
/* Close window. */
window_close( wid, NULL );
/* Update parent window. */
parent = window_getParent( wid );
menuKeybinds_genList( parent );
return 0;
}
/**
* @brief Rebinds a key.
*/
static void opt_setKey( unsigned int wid, char *str )
{
(void) wid;
(void) str;
unsigned int new_wid;
int w, h;
/* Reset key. */
opt_lastKeyPress = 0;
/* Create new window. */
w = 20 + 2*(BUTTON_WIDTH + 20);
h = 20 + BUTTON_HEIGHT + 20 + 20 + 80 + 40;
new_wid = window_create( "Set Keybinding", -1, -1, w, h );
window_handleEvents( new_wid, opt_setKeyEvent );
window_setParent( new_wid, wid );
/* Set text. */
window_addText( new_wid, 20, -40, w-40, 60, 0, "txtInfo",
&gl_smallFont, &cBlack,
"To use a modifier key hit that key twice in a row, otherwise it "
"will register as a modifier. To set with any modifier click the checkbox." );
/* Create button to cancel. */
window_addButton( new_wid, -20, 20, BUTTON_WIDTH, BUTTON_HEIGHT,
"btnCancel", "Cancel", window_close );
/* Button to unset. */
window_addButton( new_wid, 20, 20, BUTTON_WIDTH, BUTTON_HEIGHT,
"btnUnset", "Unset", opt_unsetKey );
/* Checkbox to set any modifier. */
window_addCheckbox( new_wid, -20, 20 + BUTTON_HEIGHT + 20, w-40, 20,
"chkAny", "Set any modifier", NULL, 0 );
}
/**
* @brief Unsets the key.
*/
static void opt_unsetKey( unsigned int wid, char *str )
{
(void) str;
unsigned int parent;
/* Unsets the keybind. */
input_setKeybind( opt_selectedKeybind, KEYBIND_NULL, 0, 0 );
/* Close window. */
window_close( wid, NULL );
/* Update parent window. */
parent = window_getParent( wid );
menuKeybinds_genList( parent );
}
/**
* @brief Initializes the video window.
*/
static void opt_video( unsigned int wid )
{
(void) wid;
int i, j, nres, res_def;
char buf[16];
int cw;
int w, h, y, x, l;
char **res;
const char *s;
/* Get size. */
window_dimWindow( wid, &w, &h );
/* Close button */
window_addButton( wid, -20, 20,
BUTTON_WIDTH, BUTTON_HEIGHT,
"btnClose", "OK", opt_OK );
window_addButton( wid, -20 - 1*(BUTTON_WIDTH+20), 20,
BUTTON_WIDTH, BUTTON_HEIGHT,
"btnCancel", "Cancel", opt_close );
window_addButton( wid, -20 - 2*(BUTTON_WIDTH+20), 20,
BUTTON_WIDTH, BUTTON_HEIGHT,
"btnDefaults", "Defaults", opt_videoDefaults );
/* Resolution bits. */
cw = (w-60)/2;
x = 20;
y = -60;
window_addText( wid, x+20, y, 100, 20, 0, "txtSRes",
NULL, &cDConsole, "Resolution" );
y -= 40;
window_addInput( wid, x, y, 100, 20, "inpRes", 16, 1, NULL );
window_setInputFilter( wid, "inpRes",
"abcdefghijklmnopqrstuvwyzABCDEFGHIJKLMNOPQRSTUVWXYZ[]{}()-=*/\\'\"~<>!@#$%^&|_`" );
window_addCheckbox( wid, x+20+100, y, 100, 20,
"chkFullscreen", "Fullscreen", NULL, conf.fullscreen );
y -= 30;
#if SDL_VERSION_ATLEAST(2,0,0)
SDL_DisplayMode mode;
int n = SDL_GetNumDisplayModes( 0 );
j = 1;
for (i=0; i<n; i++) {
SDL_GetDisplayMode( 0, i, &mode );
if ((mode.w == conf.width) && (mode.h == conf.height))
j = 0;
}
res = malloc( sizeof(char*) * (i+j) );
nres = 0;
res_def = 0;
if (j) {
res[0] = malloc(16);
nsnprintf( res[0], 16, "%dx%d", conf.width, conf.height );
nres = 1;
}
for (i=0; i<n; i++) {
SDL_GetDisplayMode( 0, i, &mode );
res[ nres ] = malloc(16);
nsnprintf( res[ nres ], 16, "%dx%d", mode.w, mode.h );
if ((mode.w == conf.width) && (mode.h == conf.height))
res_def = i;
nres++;
}
#else /* SDL_VERSION_ATLEAST(2,0,0) */
SDL_Rect** modes = SDL_ListModes( NULL, SDL_OPENGL | SDL_FULLSCREEN );
j = 1;
for (i=0; modes[i]; i++) {
if ((modes[i]->w == conf.width) && (modes[i]->h == conf.height))
j = 0;
}
res = malloc( sizeof(char*) * (i+j) );
nres = 0;
res_def = 0;
if (j) {
res[0] = malloc(16);
nsnprintf( res[0], 16, "%dx%d", conf.width, conf.height );
nres = 1;
}
for (i=0; modes[i]; i++) {
res[ nres ] = malloc(16);
nsnprintf( res[ nres ], 16, "%dx%d", modes[i]->w, modes[i]->h );
if ((modes[i]->w == conf.width) && (modes[i]->h == conf.height))
res_def = i;
nres++;
}
#endif /* SDL_VERSION_ATLEAST(2,0,0) */
window_addList( wid, x, y, 140, 100, "lstRes", res, nres, -1, opt_videoRes );
y -= 150;
/* FPS stuff. */
window_addText( wid, x+20, y, 100, 20, 0, "txtFPSTitle",
NULL, &cDConsole, "FPS Control" );
y -= 30;
s = "FPS Limit";
l = gl_printWidthRaw( NULL, s );
window_addText( wid, x, y, l, 20, 1, "txtSFPS",
NULL, &cBlack, s );
window_addInput( wid, x+l+20, y, 40, 20, "inpFPS", 4, 1, NULL );
toolkit_setListPos( wid, "lstRes", res_def);
window_setInputFilter( wid, "inpFPS",
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ[]{}()-=*/\\'\"~<>!@#$%^&|_`" );
nsnprintf( buf, sizeof(buf), "%d", conf.fps_max );
window_setInput( wid, "inpFPS", buf );
y -= 30;
window_addCheckbox( wid, x, y, cw, 20,
"chkFPS", "Show FPS", NULL, conf.fps_show );
/* Sets inpRes to current resolution, must be after lstRes is added. */
opt_resize();
/* OpenGL options. */
x = 20+cw+20;
y = -60;
window_addText( wid, x+20, y, 100, 20, 0, "txtSGL",
NULL, &cDConsole, "OpenGL" );
y -= 30;
window_addCheckbox( wid, x, y, cw, 20,
"chkVSync", "Vertical Sync", NULL, conf.vsync );
y -= 20;
window_addCheckbox( wid, x, y, cw, 20,
"chkVBO", "Vertex Buffer Objects*", NULL, conf.vbo );
y -= 20;
window_addCheckbox( wid, x, y, cw, 20,
"chkMipmaps", "Mipmaps*", NULL, conf.mipmaps );
y -= 20;
window_addCheckbox( wid, x, y, cw, 20,
"chkInterpolate", "Interpolation*", NULL, conf.interpolate );
y -= 20;
window_addCheckbox( wid, x, y, cw, 20,
"chkNPOT", "NPOT Textures*", NULL, conf.npot );
y -= 30;
window_addText( wid, x, y, cw, 20, 1,
"txtSCompat", NULL, &cBlack, "*Disable for compatibility." );
y -= 40;
/* Features. */
window_addText( wid, x+20, y, 100, 20, 0, "txtSFeatures",
NULL, &cDConsole, "Features" );
y -= 30;
window_addCheckbox( wid, x, y, cw, 20,
"chkEngineGlow", "Engine Glow (More RAM)", NULL, conf.engineglow );
#if SDL_VERSION_ATLEAST(2,0,0)
y -= 20;
window_addCheckbox( wid, x, y, cw, 20,
"chkMinimize", "Minimize on focus loss", NULL, conf.minimize );
#endif /* SDL_VERSION_ATLEAST(2,0,0) */
/* Restart text. */
window_addText( wid, 20, 10, 3*(BUTTON_WIDTH + 20),
30, 0, "txtRestart", &gl_smallFont, &cBlack, NULL );
}
/**
* @brief Marks that needs restart.
*/
static void opt_needRestart (void)
{
const char *s;
/* Values. */
opt_restart = 1;
s = "Restart Naev for changes to take effect.";
/* Modify widgets. */
window_modifyText( opt_windows[ OPT_WIN_GAMEPLAY ], "txtRestart", s );
window_modifyText( opt_windows[ OPT_WIN_VIDEO ], "txtRestart", s );
window_modifyText( opt_windows[ OPT_WIN_AUDIO ], "txtRestart", s );
}
/**
* @brief Callback when resolution changes.
*/
static void opt_videoRes( unsigned int wid, char *str )
{
char *buf;
buf = toolkit_getList( wid, str );
window_setInput( wid, "inpRes", buf );
}
/**
* @brief Saves the video settings.
*/
static int opt_videoSave( unsigned int wid, char *str )
{
(void) str;
int i, j, s;
char *inp, buf[16], width[16], height[16];
int w, h, f, fullscreen;
/* Handle resolution. */
inp = window_getInput( wid, "inpRes" );
memset( width, '\0', sizeof(width) );
memset( height, '\0', sizeof(height) );
j = 0;
s = 0;
for (i=0; i<16; i++) {
if (isdigit(inp[i])) {
if (j==0)
width[s++] = inp[i];
else
height[s++] = inp[i];
}
else {
j++;
s = 0;
}
}
w = atoi(width);
h = atoi(height);
if ((w==0) || (h==0)) {
dialogue_alert( "Height/Width invalid. Should be formatted like 1024x768." );
return 1;
}
/* Fullscreen. */
fullscreen = window_checkboxState( wid, "chkFullscreen" );
#if SDL_VERSION_ATLEAST(2,0,0)
int origw, origh, origf, mode, changed;
int rw, rh, nw, nh; /* Real width and height. */
SDL_DisplayMode current;
changed = 0;
SDL_GetWindowSize( gl_screen.window, &rw, &rh );
SDL_GetWindowDisplayMode( gl_screen.window, &current );
mode = (conf.modesetting) ?
SDL_WINDOW_FULLSCREEN : SDL_WINDOW_FULLSCREEN_DESKTOP;
origw = conf.width;
origh = conf.height;
origf = conf.fullscreen;
if ((w != conf.width) || (h != conf.height)) {
conf.explicit_dim = 1;
conf.width = w;
conf.height = h;
}
/* Enable or disable fullscreen. */
if (fullscreen != conf.fullscreen) {
conf.fullscreen = fullscreen;
changed = 1;
if (fullscreen) {
if (conf.modesetting) {
current.w = w;
current.h = h;
SDL_SetWindowDisplayMode( gl_screen.window, &current );
}
SDL_SetWindowFullscreen( gl_screen.window, mode );
}
else /* Restore windowed mode. */
SDL_SetWindowFullscreen( gl_screen.window, 0 );
}
/* Attempt to detect maximized state (doesn't work on X11) */
if (SDL_GetWindowFlags(gl_screen.window) & SDL_WINDOW_MAXIMIZED)
dialogue_alert("Resolution can't be changed while maximized.");
/* Set size. Done second, because it can't be set while fullscreen. */
else if ((w != rw) || (h != rh)) {
/* Can't change window size while fullscreen. */
if (fullscreen && origf)
opt_needRestart();
else if (!fullscreen) {
SDL_SetWindowSize( gl_screen.window, w, h );
naev_resize( w, h );
SDL_SetWindowPosition( gl_screen.window,
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED );
changed = 1;
}
}
/* Desktop fullscreen size must be determined dynamically. */
if (fullscreen && !conf.modesetting)
SDL_GetWindowSize( gl_screen.window, &nw, &nh );
else {
nw = conf.width;
nh = conf.height;
}
/* Settings have changed, switch and offer to reset. */
if (changed && !dialogue_YesNo("Keep Video Settings",
"Do you want to keep running at %dx%d %s?",
nw, nh, fullscreen ? "fullscreen" : "windowed")) {
conf.width = origw;
conf.height = origh;
conf.fullscreen = origf;
window_checkboxSet( wid, "chkFullscreen", conf.fullscreen );
/* Restore previous resolution. */
if ((w != rw) || (h != rw)) {
SDL_SetWindowSize( gl_screen.window, rw, rh );
naev_resize( rw, rh );
SDL_SetWindowPosition( gl_screen.window,
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED );
}
/* Restore windowed mode. */
if (fullscreen && (fullscreen != conf.fullscreen))
SDL_SetWindowFullscreen( gl_screen.window, 0 );
else if (!fullscreen && (fullscreen != conf.fullscreen)) {
if (conf.modesetting) {
current.w = origw;
current.h = origh;
SDL_SetWindowDisplayMode( gl_screen.window, &current );
}
SDL_SetWindowFullscreen( gl_screen.window, mode );
}
nsnprintf( buf, sizeof(buf), "%dx%d", conf.width, conf.height );
window_setInput( wid, "inpRes", buf );
dialogue_msg( "Video Settings Restored",
"Resolution reset to %dx%d %s.",
rw, rh, conf.fullscreen ? "fullscreen" : "windowed" );
return 1;
}
else if (changed) {
nsnprintf( buf, sizeof(buf), "%dx%d", conf.width, conf.height );
window_setInput( wid, "inpRes", buf );
}
#else /* SDL_VERSION_ATLEAST(2,0,0) */
if ((w != conf.width) || (h != conf.height)) {
conf.explicit_dim = 1;
conf.width = w;
conf.height = h;
opt_needRestart();
nsnprintf( buf, sizeof(buf), "%dx%d", conf.width, conf.height );
window_setInput( wid, "inpRes", buf );
}
if (conf.fullscreen != fullscreen) {
conf.fullscreen = fullscreen;
opt_needRestart();
}
#endif /* SDL_VERSION_ATLEAST(2,0,0) */
/* FPS. */
conf.fps_show = window_checkboxState( wid, "chkFPS" );
inp = window_getInput( wid, "inpFPS" );
conf.fps_max = atoi(inp);
/* OpenGL. */
f = window_checkboxState( wid, "chkVSync" );
if (conf.vsync != f) {
conf.vsync = f;
opt_needRestart();
}
f = window_checkboxState( wid, "chkVBO" );
if (conf.vbo != f) {
conf.vbo = f;
opt_needRestart();
}
f = window_checkboxState( wid, "chkMipmaps" );
if (conf.mipmaps != f) {
conf.mipmaps = f;
opt_needRestart();
}
f = window_checkboxState( wid, "chkInterpolate" );
if (conf.interpolate != f) {
conf.interpolate = f;
opt_needRestart();
}
f = window_checkboxState( wid, "chkNPOT" );
if (conf.npot != f) {
conf.npot = f;
opt_needRestart();
}
/* Features. */
f = window_checkboxState( wid, "chkEngineGlow" );
if (conf.engineglow != f) {
conf.engineglow = f;
opt_needRestart();
}
f = window_checkboxState( wid, "chkMinimize" );
if (conf.minimize != f) {
conf.minimize = f;
#if SDL_VERSION_ATLEAST(2,0,0)
SDL_SetHint( SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS,
conf.minimize ? "1" : "0" );
#endif /* SDL_VERSION_ATLEAST(2,0,0) */
}
return 0;
}
/**
* @brief Sets video defaults.
*/
static void opt_videoDefaults( unsigned int wid, char *str )
{
(void) str;
char buf[16];
/* Restore settings. */
/* Inputs. */
nsnprintf( buf, sizeof(buf), "%dx%d", RESOLUTION_W_DEFAULT, RESOLUTION_H_DEFAULT );
window_setInput( wid, "inpRes", buf );
nsnprintf( buf, sizeof(buf), "%d", FPS_MAX_DEFAULT );
window_setInput( wid, "inpFPS", buf );
/* Checkboxkes. */
window_checkboxSet( wid, "chkFullscreen", FULLSCREEN_DEFAULT );
window_checkboxSet( wid, "chkVSync", VSYNC_DEFAULT );
window_checkboxSet( wid, "chkVBO", VBO_DEFAULT );
window_checkboxSet( wid, "chkMipmaps", MIPMAP_DEFAULT );
window_checkboxSet( wid, "chkInterpolate", INTERPOLATION_DEFAULT );
window_checkboxSet( wid, "chkNPOT", NPOT_TEXTURES_DEFAULT );
window_checkboxSet( wid, "chkFPS", SHOW_FPS_DEFAULT );
window_checkboxSet( wid, "chkEngineGlow", ENGINE_GLOWS_DEFAULT );
window_checkboxSet( wid, "chkMinimize", MINIMIZE_DEFAULT );
}