diff --git a/config.l b/config.l index afcc8a6..e60493c 100644 --- a/config.l +++ b/config.l @@ -83,6 +83,7 @@ cursor { return TYPE_CURSOR; } "lockfile" { return TYPE_PATH_LOCKFILE; } "inprogressdir" { return TYPE_PATH_INPROGRESS; } "game_args" { return TYPE_GAME_ARGS; } +extra_info_file { return TYPE_EXTRA_INFO_FILE; } "max_idle_time" { return TYPE_MAX_IDLE_TIME; } "rc_fmt" { return TYPE_RC_FMT; } "ttyrecdir" { return TYPE_PATH_TTYREC; } diff --git a/config.y b/config.y index 935e796..4474b0c 100644 --- a/config.y +++ b/config.y @@ -57,7 +57,7 @@ static int sortmode_number(const char *sortmode_name) { %token TYPE_MALSTRING TYPE_PATH_INPROGRESS TYPE_GAME_ARGS TYPE_RC_FMT %token TYPE_CMDQUEUE TYPE_DEFINE_MENU TYPE_BANNER_FILE TYPE_CURSOR %token TYPE_POSTCMDQUEUE -%token TYPE_MAX_IDLE_TIME TYPE_MENU_MAX_IDLE_TIME +%token TYPE_MAX_IDLE_TIME TYPE_MENU_MAX_IDLE_TIME TYPE_EXTRA_INFO_FILE %token TYPE_VALUE %token TYPE_NUMBER TYPE_CMDQUEUENAME %type KeyType @@ -422,6 +422,10 @@ game_definition : TYPE_CMDQUEUE { /* nothing */ } + | TYPE_EXTRA_INFO_FILE '=' TYPE_VALUE + { + myconfig[ncnf]->extra_info_file = strdup($3); + } | TYPE_MAX_IDLE_TIME '=' TYPE_NUMBER { myconfig[ncnf]->max_idle_time = $3; diff --git a/dgamelaunch.c b/dgamelaunch.c index 8575dc6..905ac41 100644 --- a/dgamelaunch.c +++ b/dgamelaunch.c @@ -756,6 +756,78 @@ sortmode_increment(struct dg_watchcols **watchcols, *sortmode = old_sortmode; } +static +void +game_get_column_data(struct dg_game *game, + char selectorchar, + time_t ctime, struct dg_shm_game *shm_dg_game, + char *data, int bufsz, int *hilite, + dg_sortmode which_data) +{ + *data = 0; + + switch (which_data) { + default: break; + case SORTMODE_NONE: + data[0] = selectorchar; data[1] = '\0'; + break; + + case SORTMODE_USERNAME: + snprintf(data, bufsz, "%s", game->name); + break; + + case SORTMODE_GAMENUM: + snprintf(data, bufsz, "%s", + myconfig[game->gamenum]->shortname); + break; + + case SORTMODE_WINDOWSIZE: + snprintf(data, bufsz, "%3dx%3d", game->ws_col, game->ws_row); + if ((game->ws_col > COLS || game->ws_row > LINES)) + *hilite = CLR_RED; + break; + + case SORTMODE_STARTTIME: + snprintf(data, bufsz, "%s %s", game->date, + game->time); + break; + + case SORTMODE_IDLETIME: + { + long secs, mins, hours; + + secs = (ctime - game->idle_time); + hours = (secs / 3600); + secs -= (hours * 3600); + mins = (secs / 60) % 60; + secs -= (mins*60); + if (hours) + snprintf(data, 10, "%ldh %ldm", hours, mins); + else if (mins) + snprintf(data, 10, "%ldm %lds", mins, secs); + else if (secs > 4) + snprintf(data, 10, "%lds", secs); + else + snprintf(data, 10, " "); + break; + } + + case SORTMODE_EXTRA_INFO: + if (game->extra_info) + strlcpy(data, game->extra_info, bufsz); + break; + +#ifdef USE_SHMEM + case SORTMODE_WATCHERS: + snprintf(data, bufsz, "%li", + (game->is_in_shm ? + shm_dg_game[game->shm_idx].nwatchers : -1)); + break; +#endif + } + data[bufsz - 1] = '\0'; +} + void inprogressmenu (int gameid) { @@ -847,48 +919,15 @@ inprogressmenu (int gameid) if (i + offset == selected) attron(selected_attr); - snprintf (gametype, sizeof gametype, "%3dx%3d", - games[i + offset]->ws_col, games[i + offset]->ws_row); - - { - long secs, mins, hours; - - secs = (ctime - games[i + offset]->idle_time); - hours = (secs / 3600); - secs -= (hours * 3600); - mins = (secs / 60) % 60; - secs -= (mins*60); - if (hours) - snprintf(idletime, 10, "%ldh %ldm", hours, mins); - else if (mins) - snprintf(idletime, 10, "%ldm %lds", mins, secs); - else if (secs > 4) - snprintf(idletime, 10, "%lds", secs); - else - snprintf(idletime, 10, " "); - } - for (curr_watchcol = watchcols; *curr_watchcol; ++curr_watchcol) { struct dg_watchcols *col = *curr_watchcol; char tmpbuf[80]; int hilite = 0; - switch (col->dat) { - default: break; - case 0: tmpbuf[0] = selectorchars[i]; tmpbuf[1] = '\0'; break; - case 1: snprintf(tmpbuf, 80, "%s", games[i + offset]->name); break; - case 2: snprintf(tmpbuf, 80, "%s", myconfig[games[i + offset]->gamenum]->shortname); break; - case 3: - snprintf(tmpbuf, 80, "%s", gametype); - if ((games[i+offset]->ws_col > COLS || games[i+offset]->ws_row > LINES)) - hilite = CLR_RED; - break; - case 4: snprintf(tmpbuf, 80, "%s %s", games[i + offset]->date, games[i + offset]->time); break; - case 5: snprintf(tmpbuf, 80, "%s", idletime); break; -#ifdef USE_SHMEM - case 6: snprintf(tmpbuf, 80, "%li", (games[i+offset]->is_in_shm ? shm_dg_game[games[i+offset]->shm_idx].nwatchers : -1)); break; -#endif - } - tmpbuf[79] = '\0'; + game_get_column_data(games[i + offset], + selectorchars[i], + ctime, shm_dg_game, + tmpbuf, sizeof tmpbuf, &hilite, + (dg_sortmode)col->dat); if (hilite) attron(hilite); mvprintw(top_banner_hei + 1 + i, col->x, col->fmt, tmpbuf); if (hilite) { diff --git a/dgamelaunch.h b/dgamelaunch.h index fdfe579..7be7b3a 100644 --- a/dgamelaunch.h +++ b/dgamelaunch.h @@ -81,6 +81,7 @@ typedef enum SORTMODE_WINDOWSIZE, SORTMODE_STARTTIME, SORTMODE_IDLETIME, + SORTMODE_EXTRA_INFO, #ifdef USE_SHMEM SORTMODE_WATCHERS, #endif @@ -94,6 +95,7 @@ static const char *SORTMODE_NAME[NUM_SORTMODES] = { "Windowsize", "Starttime", "Idletime", + "Extrainfo", #ifdef USE_SHMEM "Watchers", #endif @@ -175,6 +177,9 @@ struct dg_game int is_in_shm; int shm_idx; int nwatchers; + + char *extra_info; + int extra_info_weight; }; struct dg_config @@ -192,6 +197,7 @@ struct dg_config struct dg_cmdpart *cmdqueue; struct dg_cmdpart *postcmdqueue; int max_idle_time; + char *extra_info_file; }; struct dg_watchcols { diff --git a/dgl-common.c b/dgl-common.c index dd6221d..6c93e74 100644 --- a/dgl-common.c +++ b/dgl-common.c @@ -47,7 +47,8 @@ struct dg_config defconfig = { /* rc_fmt = */ "%rrcfiles/%n.nethackrc", /* [dglroot]rcfiles/[username].nethackrc */ /* cmdqueue = */ NULL, /* postcmdqueue = */ NULL, - /* max_idle_time = */ 0 + /* max_idle_time = */ 0, + /* extra_info_file = */ NULL }; char* config = NULL; @@ -414,6 +415,16 @@ sort_game_idletime(const void *g1, const void *g2) return strcasecmp(game1->name, game2->name); } +static int +sort_game_extrainfo(const void *g1, const void *g2) +{ + const int extra_weight1 = + (*(const struct dg_game **) g1)->extra_info_weight; + const int extra_weight2 = + (*(const struct dg_game **) g2)->extra_info_weight; + return dglsign(extra_weight2 - extra_weight1); +} + static int sort_game_gamenum(const void *g1, const void *g2) { @@ -477,6 +488,12 @@ sort_games (struct dg_game **games, int len, dg_sortmode sortmode) qsort(games, len, sizeof(struct dg_game *), sort_game_idletime); break; case SORTMODE_STARTTIME: qsort(games, len, sizeof(struct dg_game *), sort_game_starttime); break; + + case SORTMODE_EXTRA_INFO: + qsort(games, len, sizeof(struct dg_game *), + sort_game_extrainfo); + break; + #ifdef USE_SHMEM case SORTMODE_WATCHERS: (void) time(&sort_ctime); @@ -512,11 +529,50 @@ free_populated_games(struct dg_game **games, int len) if (games[i]->name) free(games[i]->name); if (games[i]->date) free(games[i]->date); if (games[i]->time) free(games[i]->time); + if (games[i]->extra_info) free(games[i]->extra_info); free(games[i]); } free(games); } +static +void +game_read_extra_info(struct dg_game *game, const char *extra_info_file) +{ + FILE *ei = NULL; + char *sep = NULL; + char buffer[120]; + int buflen; + + if (game->extra_info) { + free(game->extra_info); + game->extra_info = NULL; + } + game->extra_info_weight = 0; + + if (!extra_info_file) + return; + + if (!(ei = fopen(extra_info_file, "r"))) + return; + *buffer = 0; + fgets(buffer, sizeof buffer, ei); + fclose(ei); + + buflen = strlen(buffer); + if (buflen && buffer[buflen - 1] == '\n') + buffer[buflen - 1] = 0; + + /* The extra info file format is | */ + sep = strchr(buffer, '|'); + game->extra_info = strdup(sep? sep + 1 : buffer); + + if (sep) { + *sep = 0; + game->extra_info_weight = atoi(buffer); + } +} + struct dg_game ** populate_games (int xgame, int *l, struct dg_user *me) { @@ -637,6 +693,17 @@ populate_games (int xgame, int *l, struct dg_user *me) games[len]->ws_row = 24; games[len]->ws_col = 80; } + + games[len]->extra_info = NULL; + games[len]->extra_info_weight = 0; + if (myconfig[game]->extra_info_file) { + char *extra_info_file = + dgl_format_str(game, NULL, + myconfig[game]->extra_info_file, + games[len]->name); + game_read_extra_info(games[len], extra_info_file); + } + len++; } } diff --git a/examples/dgamelaunch.conf b/examples/dgamelaunch.conf index d7bb306..21c5ab1 100644 --- a/examples/dgamelaunch.conf +++ b/examples/dgamelaunch.conf @@ -221,6 +221,16 @@ menu["watchmenu_help"] { # # receive a sighup. Default value is 0, which disables the idling timer. # max_idle_time = 2000 # +# # Player-specific path to an extra information file written by the game +# # The game should write the extra information on one line in this format: +# # |extra-information +# # For example, the game might write: "100|Astral", "1|D:1", etc. to indicate +# # where the player is in the game world. The numeric weight is used when +# # a spectator sorts games by the extra information field: higher weights +# # will be sorted to appear before lower weights. +# # +# extra_info_file = "%rgamedir/%n.extrainfo" +# # # Make sure the inprogress dir actually exists. default is "inprogress/" # # Each game you define here must have it's own. # inprogressdir = "%rinprogress-nethackstub/"