Skip to content

Instantly share code, notes, and snippets.

@jalbright015
Last active April 17, 2021 05:27
Show Gist options
  • Save jalbright015/35be75ad0c0dddc3080bfc6590e4dad8 to your computer and use it in GitHub Desktop.
Save jalbright015/35be75ad0c0dddc3080bfc6590e4dad8 to your computer and use it in GitHub Desktop.
Status bar - Telnet control code proof of concept
/*
I apparently didn't write a header when I wrote this years ago, but here is the gist:
This is a proof of concept item that will place a static status bar at the bottom of the user's terminal window. The purpose for writing this was as a proof of concept for telnet control code utilzation in LPC. It uses telnet options and negotiation to perform the core of the functionality including automatically detecting window size and placement of the status bar.
Disclaimer: There are some things in this file I would definitely do differently today, but the methods I used as a means to an end are pretty crafty even to current Me.
Jezu@Astaria 17 April 2021
*/
#include <mudlib.h>
#define ESC(x) sprintf ("%c%s", 27, "x")
#define ESC_C sprintf ("%c", 27)
#define PATH "/u/j/jezu/obj/"
#define SHADOW_OBJ PATH+"status_bar_shadow.c"
inherit OBJECT;
int update_bar(string str);
int do_progress(int i);
varargs int get_window_size(string str);
int get_health_stats(object ob);
int configure_health_bar();
int window_size, new_size;
object shadow;
mapping health_stats = ([ "hp" : 0, "max_hp" : 0,
"sp" : 0, "max_sp" : 0,
"ep" : 0, "max_ep" : 0 ]);
void create()
{
seteuid(geteuid());
set("id", ({ "status bar", "nifty status bar", "nifty bar",
"bar" }) );
set("short", "a nifty status bar");
set("long", "\n\n\
This adds a nifty status bar at the bottom of your mud client's window.\n\
Below is a list of commands that are currently available.\n\
\n\
Syntax: sbar <command>\n\
\n\
Commands:\n\
config Attempts to automatically determine window height.\n\
enable Turns on the status bar (Note: 'config' must be done first).\n\
reset Clears the status bar and resets terminal.\n\
set <type> Displays <type> data on the status bar.\n\
\n\
Set types:\n\
progress A simple progress counter demonstration.\n\
health Displays health stats.\n\n");
set("name", "status bar");
set("mass", 100);
set("bulk", 20);
set("value", ({ 0, "copper" }) );
set("no_vault", 1);
set("no_corrode", 1);
//set("prevent_drop", 1);
set("autoload", 1);
}
void init()
{
//if(!interactive(TP)) return;
add_action("cmd_sbar", "sbar");
}
int cmd_sbar(string str) {
string *args;
int num_args, i;
string arg = "";
string strarg = "";
if (!str)
return notify_fail("This command requires at least one argument.\n");
args = explode(str, " ");
num_args = sizeof(args);
switch (args[0]) {
case "cursor" :
if (num_args == 1) {
notify_fail("The 'cursor' argument requires at least one parameter.\n");
break;
}
switch (args[1]) {
case "save" :
write("Saving cursor position...\n");
write(ESC([s));
break;
case "restore" :
write("Restoring cursor position...\n");
write(ESC([u));
break;
case "up" :
write("Setting cursor position 'up' by: 1\n");
write(ESC([1A));
break;
case "down" :
write("Setting cursor position 'down' by: 1\n");
write(ESC([1B));
break;
case "forward" :
write("Setting cursor position 'forward' by: 1\n");
write(ESC([1C));
break;
case "backward" :
write("Setting cursor position 'backward' by: 1\n");
write(ESC([1D));
break;
default :
return notify_fail("Invalid parameter for 'cursor'.\n");
break;
}
break;
case "config" :
i = get_window_size();
if ( !( (i&(1<<31)) | !i) ) {
return notify_fail("A problem occurred during the configuration.\n");
break;
}
break;
case "enable" :
if (!window_size) {
return notify_fail("\nNo window data has been configured. Please run the 'config' command first.\n");
break;
}
new_size = window_size - 1;
write("[1;"+new_size+"r["+new_size+";1H");
write("Status bar successfully enabled.\n");
break;
case "reset" :
if (!window_size) {
write("\nNo window data has been set. Resetting window using a best guess.\n");
}
new_size = window_size ? window_size + 15 : 75;
write(ESC_C+"[1;"+new_size+"r"+ESC_C+"["+new_size+";100H"+ESC_C+"[2K");
write(ESC_C+"[500D");
new_size = 0;
remove_call_out("configure_health_bar");
break;
case "set" :
if (num_args < 2) {
return notify_fail("The 'set' command requires an additional parameter.\n");
break;
}
if (!new_size) {
return notify_fail("\nThe status bar has not been enabled. Please run the 'enable' command first.\n");
break;
}
remove_call_out("configure_health_bar");
switch (args[1]) {
case "progress" :
do_progress(0);
break;
case "health" :
if ( !configure_health_bar() ) {
return notify_fail("A problem occurred while configuring the health status bar.\n");
break;
}
break;
default :
return notify_fail("Invalid parameter for 'set' option: "+args[1]+"\n");
break;
}
break;
case "manual" :
for (i=0;i<num_args;i++) {
debug("jezu", "arg["+i+"]: "+args[i]+"\n");
arg += ESC_C+args[i];
if (i > 0) strarg += "+";
strarg += "ESC"+args[i];
}
debug("jezu", "Sending escape code: "+strarg+" "+arg+" [END OF LINE]\n");
/*
[1K - clear everything on line (typically clears from ESC to beginning of line)
7 [0;22r [22H - makes window 22 lines only, bottom half ignored
[6n - reports cursor position to input buffer of client
[c - reports terminal type to input buffer of client ([?1;0c)
'ansi on' - does some fuckery with adding [37m and [0m to every returned line
> - makes script debugger echo back some received lines
Z - identify terminal type, like [c
/ - makes received text dark black until ansi color changes it
[J - makes buffer stay and you type behind/below it
*/
//write(arg);
break;
default :
return notify_fail("Invalid status bar command.\n");
break;
}
return 1;
}
int update_bar(string str) {
string prefix = ESC_C+"[50B"+ESC_C+"[100C"+ESC_C+"[1K"+ESC_C+"[1000D";
string suffix = ESC_C+"[0m";
string post = ESC_C+"[1000D"+ESC_C+"[1A";
if (!str)
return notify_fail("A problem occurred while updating the status bar.\n");
write(sprintf("%s%s%s%s", prefix, str, suffix, post));
//write(ESC_C+"[1000D"+ESC_C+"[1A");
return 1;
}
int do_progress(int i) {
string data;
i++;
data = ESC_C+"[1;7m "+(i*10)+"% ";
update_bar(data);
if (i == 10) return 1;
call_out("do_progress", 1, i);
}
varargs int get_window_size(string str) {
if (!str) {
write(ESC_C+"[6n");
write("\n=== [ Press ENTER to set window size ] ===\n");
write(ESC_C+"[1000D");
input_to("get_window_size", 1);
return -1;
}
if (!sscanf(str, "%*s[%d;%*d%*s", window_size))
return notify_fail("\nError parsing window size.\n");
write("Window height set to "+window_size+" lines.\n");
return 1;
}
int get_health_stats(object ob) {
int i, sz;
string *health_keys = keys(health_stats);
sz = sizeof(health_keys);
for (i = 0; i < sz; i++) {
if ( !(health_stats[health_keys[i]] = call_other(ob, "query_"+health_keys[i])) ) {
return notify_fail("An error occurred while checking your "+health_keys[i]+".\n");
}
}
return 1;
}
int configure_health_bar() {
string hp_bg = ESC_C+"[42m";
string hp_fg = ESC_C+"[30m";
string res_col = ESC_C+"[0m";
string bar_data;
get_health_stats(TP);
bar_data = sprintf(" hp: [%s %d/%d %s], sp: [ %d/%d ], ep: [ %d/%d ]",
hp_bg+hp_fg,
health_stats["hp"], health_stats["max_hp"],
res_col,
health_stats["sp"], health_stats["max_sp"],
health_stats["ep"], health_stats["max_ep"] );
update_bar(bar_data);
call_out("configure_health_bar", 2);
return 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment