You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

397 lines
9.9 KiB

#include "main.h"
#include "scene.h"
#include "canvas_base.h"
#ifdef HAVE_SDL
#include "canvas_sdl.h"
#endif
#ifdef HAVE_GLX
#include "canvas_glx.h"
#endif
#include <iostream>
#include <stdlib.h>
#ifdef WIN32
#include <windows.h>
#else
#include <argp.h>
#endif
CanvasBase *canvas;
Scene scene;
enum {CANVAS_SDL, CANVAS_GLX} canvas_type = CANVAS_SDL;
int window_id = 0;
int mspf = 1000/30;
bool full_screen = false;
#ifdef WIN32
// mingw doesn't have argp. implement half-assed version
#define OPTION_HIDDEN 1
#define OPTION_DOC 2
#define ARGP_KEY_ARG 0
#define ARGP_ERR_UNKNOWN 0
#define ARGP_LONG_ONLY 1
typedef int (*argp_parser_t)(int, char*, struct argp_state*);
struct argp_option {
const char *name;
int key;
const char *arg;
int flags;
const char *doc;
int group;
};
struct argp {
const struct argp_option *options;
argp_parser_t parser;
const char *args_doc;
const char *doc;
// there are more members, but this is all I need
};
struct argp_state {
int argc;
char **argv;
int next;
char *name;
// there are more members, but this is all I need
};
void argp_help(struct argp *argp_s)
{
cerr << argp_s->doc << endl;
for (int i = 0; argp_s->options[i].name; i++) {
if (argp_s->options[i].flags & OPTION_HIDDEN)
continue;
if (argp_s->options[i].flags & OPTION_DOC)
cerr << argp_s->options[i].name << endl;
else {
if (isprint(argp_s->options[i].key))
cerr << " -" << (char)argp_s->options[i].key << ",";
else
cerr << " ";
cerr << " --" << argp_s->options[i].name << "\t";
cerr << argp_s->options[i].doc << endl;
}
}
}
// doesn't work exactly as real argp, but suits this program
int argp_parse(struct argp *argp_s, int argc, char *argv[], int, int *, void *)
{
char *opt, *arg=0;
int optind = 0;
struct argp_state state;
state.argc = argc;
state.argv = argv;
state.name = argv[0];
for (int i = 1; i < state.argc; i = state.next) {
opt = state.argv[i];
arg = 0;
optind = -1;
state.next = i+1;
if (opt[0] != '-') {
if ((*argp_s->parser)(ARGP_KEY_ARG, 0, &state) < 0)
return -1;
continue;
}
opt++;
if (*opt == '-')
opt++;
if ((arg = strchr(opt, '=')))
*arg++ = 0;
if (opt[1] == 0) { // short option
for (int j = 0; argp_s->options[j].name; j++) {
if (opt[0] == argp_s->options[j].key) {
optind = j;
break;
}
}
if (optind >= 0)
;
else if (opt[0] == 'h') {
argp_help(argp_s);
return -1;
}
else if (opt[0] == 'u') {
argp_help(argp_s);
return -1;
}
else if (opt[0] == 'V') {
extern char *argp_program_version;
cerr << argp_program_version << endl;
return -1;
}
}
else { // long option
for (int j = 0; argp_s->options[j].name; j++) {
if (strcmp(opt, argp_s->options[j].name)==0) {
optind = j;
break;
}
}
if (optind >= 0)
;
else if (strcmp(opt, "help") == 0) {
argp_help(argp_s);
return -1;
}
else if (strcmp(opt, "usage") == 0) {
argp_help(argp_s);
return -1;
}
else if (strcmp(opt, "version") == 0) {
extern char *argp_program_version;
cerr << argp_program_version << endl;
return -1;
}
}
if (optind < 0) { // doesn't exist
cerr << state.name << ": unrecognized option `"
<< state.argv[i] << "'" << endl;
cerr << "Try `" << state.name << " --help' or `"
<< state.name << " --usage' for more information" << endl;
return -1;
}
if (argp_s->options[optind].arg != 0 && arg == 0) {
state.next = i+2;
if (i+1 >= state.argc) {
cerr << state.name << ": option requires an argument -- "
<< opt << endl;
return -1;
}
arg = state.argv[i+1];
}
if ((*argp_s->parser)(argp_s->options[optind].key, arg, &state) < 0)
return -1;
}
return 0;
}
#endif
#define OPT_FULLSCREEN 1
#define OPT_FPS 2
#define OPT_FASTFORWARD 3
char *mode_help =
"\n"
"Per-swarm modes and their default probabilities:\n"
" 1: normal p=20\n"
" 2: stop (bait stays still for a brief period) p=10\n"
" 3: circle (bait loops in a circle briefly) p=10\n"
" 4: rainbow (color cycle speeds up) p=10\n"
" 5: glow (tails glow as their width increases) p=15\n"
" 6: hyperspeed (swarm speeds up) p=10\n"
" 7: faded (colors fade) p=10\n"
"Major modes and their default probabilities:\n"
" 1: all swarms pick a random per-swarm mode p=5\n"
" 2: kill some flies p=10\n"
" 3: make some new flies p=10\n"
" 4: wind speed picks up p=10\n"
" 5: matrix-style pause and rotate of the scene p=10\n"
" 6: split a random swarm into two swarms p=10\n"
" 7: merge two random swarms into one p=10\n"
"Note: mode probabilities are relative. For example, if A has p=5, and B has\n"
"p=1, that simply means A is 5 times more likely to occur than B.";
const char *argp_program_version =
"Fireflies " PACKAGE_VERSION " by Mattperry <guy@somewhere.fscked.org>";
static char doc[] =
"Moving swarms of delicious eye candy.";
static struct argp_option options[] = {
{"window-id", 'W', "WinID", OPTION_HIDDEN},
{"root", 'r', 0, 0, "Draw on the root window"},
{"fullscreen", OPT_FULLSCREEN, 0, 0, "Full screen mode"},
{"fps", OPT_FPS, "NUM", 0, "Frames per second (default = 30 fps)"},
{"fastforward", OPT_FASTFORWARD, "NUM", 0, "Fast forward factor (default = 1)"},
{"minbaits", 'b', "NUM", 0, "Minimum baits (default = 2)"},
{"maxbaits", 'B', "NUM", 0, "Maximum baits (default = 5)"},
{"minflies", 'f', "NUM", 0, "Minimum total fireflies (default = 100)"},
{"maxflies", 'F', "NUM", 0, "Maximum total fireflies (default = 175)"},
{"size", 's', "NUM", 0, "Firefly size (default = 15)"},
{"bspeed", 'V', "NUM", 0, "Bait speed (default = 50)"},
{"baccel", 'A', "NUM", 0, "Bait acceleration (default = 600)"},
{"fspeed", 'v', "NUM", 0, "Firefly speed (default = 100)"},
{"faccel", 'a', "NUM", 0, "Firefly acceleration (default = 300)"},
{"colorspeed", 'c', "NUM", 0, "Swarm's color cycling speed (default = 15)"},
{"taillength", 't', "NUM", 0, "Firefly's tail length (default = 23)"},
{"tailwidth", 'T', "NUM", 0, "Firefly's tail width (default = 25)"},
{"tailopacity", 'o', "NUM", 0,
"Firefly's tail opacity/brightness ([0-100] default = 60)"},
{"glowfactor", 'g', "NUM", 0,
"Factor by which tailwidth increases during glow (default = 20)"},
{"wind", 'w', "NUM", 0, "Wind speed (default = 30)"},
{"drawbait", 'd', 0, 0, "Draw the baits that the fireflies chase"},
{"modeswarm", 'm', "MODENUM VAL", 0,
"Change the frequency of per-swarm mode MODENUM to VAL"},
{"modemajor", 'M', "MODENUM VAL", 0,
"Change the frequency of major mode MODENUM to VAL"},
{mode_help, 0, 0, OPTION_DOC, 0, -1},
{0, 0, 0, 0}
};
int parse_opt(int key, char *arg, struct argp_state *state)
{
switch (key) {
case 'W':
canvas_type = CANVAS_GLX;
window_id = strtol(arg, 0, 0);
break;
case 'r':
canvas_type = CANVAS_GLX;
break;
case OPT_FULLSCREEN:
full_screen = true;
break;
case OPT_FPS:
mspf = 1000/atoi(arg);
break;
case OPT_FASTFORWARD:
scene.fast_forward = atoi(arg);
if (scene.fast_forward == 0) {
cerr << state->name
<< ": -fastforward must be > 0" << endl;
return -1;
}
break;
case 'b':
scene.minbaits = (unsigned)atoi(arg);
break;
case 'B':
scene.maxbaits = (unsigned)atoi(arg);
break;
case 'f':
scene.minflies = (unsigned)atoi(arg);
break;
case 'F':
scene.maxflies = (unsigned)atoi(arg);
break;
case 's':
scene.fsize = atoi(arg)/10.0;
break;
case 'V':
scene.bspeed = atoi(arg);
break;
case 'A':
scene.baccel = atoi(arg);
break;
case 'v':
scene.fspeed = atoi(arg);
break;
case 'a':
scene.faccel = atoi(arg);
break;
case 'c':
scene.hue_rate = atoi(arg);
break;
case 't':
scene.tail_length = atoi(arg)/10.0;
break;
case 'T':
scene.tail_width = atoi(arg)/10.0;
break;
case 'o':
scene.tail_opaq = atoi(arg)/100.0;
if (scene.tail_opaq < 0 || scene.tail_opaq > 1) {
cerr << state->name
<< ": -tailopacity must be in the range [0,100]" << endl;
return -1;
}
break;
case 'g':
scene.glow_factor = atoi(arg)/10.0;
break;
case 'w':
scene.wind_speed = atoi(arg)/10.0;
break;
case 'd':
scene.draw_bait = true;
break;
case 'm': {
int which = atoi(arg);
unsigned val;
if (state->next < state->argc) {
val = (unsigned)atoi(state->argv[state->next]);
state->next++;
}
else {
cerr << state->name << ": option -modebait requires 2 arguments" << endl;
return -1;
}
scene.bmodes.change(which, (double)val);
break;
}
case 'M': {
int which = atoi(arg);
unsigned val;
if (state->next < state->argc) {
val = (unsigned)atoi(state->argv[state->next]);
state->next++;
}
else {
cerr << state->name << ": option -modebait requires 2 arguments" << endl;
return -1;
}
scene.smodes.change(which, (double)val);
break;
}
default:
return ARGP_ERR_UNKNOWN;
break;
}
return 0;
}
static struct argp argp_s = { options, parse_opt, 0, doc };
int main(int argc, char **argv)
{
if (argp_parse (&argp_s, argc, argv, ARGP_LONG_ONLY, 0, 0) != 0)
return 0;
switch (canvas_type) {
case CANVAS_GLX:
#ifdef HAVE_GLX
canvas = new CanvasGLX(&scene, full_screen, mspf, window_id);
#else
cerr << argv[0] << ": cannot make GLX window (you must have GLX support enabled)" << endl;
return 1;
#endif
break;
case CANVAS_SDL:
#ifdef HAVE_SDL
canvas = new CanvasSDL(&scene, full_screen, mspf, "Fireflies", "fireflies");
#else
cerr << argv[0] << ": cannot make SDL window (you must have SDL support enabled)" << endl;
return 1;
#endif
break;
}
if (canvas->init() < 0) {
cerr << "Can't init display." << endl;
return 0;
}
scene.create();
return canvas->loop();
}