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
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();
|
|
}
|