From eec2f92ce9b699c935fd40a0d83747094ce9d75c Mon Sep 17 00:00:00 2001
From: deuce <>
Date: Mon, 6 Apr 2020 07:47:42 +0000
Subject: [PATCH] Add mouse support.

This does have a terrible hack where it mangles ESC sequences going into
ungetstr() because inkey() will mangle them coming out... inkey() puts ESC
arguments at the end of the key buffer.  If the key buffer isn't empty,
this results in a re-ordering.
---
 exec/lbshell.js       |  52 +++++++++++++-
 exec/load/lightbar.js | 160 ++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 207 insertions(+), 5 deletions(-)

diff --git a/exec/lbshell.js b/exec/lbshell.js
index 4eb694ad89..de0b170fad 100644
--- a/exec/lbshell.js
+++ b/exec/lbshell.js
@@ -51,6 +51,17 @@ var lastmessage_type=0;
 var orig_passthru=console.ctrlkey_passthru;
 var hangup_now=false;
 
+/*
+ * TODO: This mangles the ANSI sequence so it will pass through inkey()
+ * properly... inkey re-inserts ESC back into the stream using ungetkey()
+ * which changes the order of if the string, putting sequence arguments
+ * at the end.
+ */
+function mangle_mouse_seq(seq)
+{
+	return seq[0]+seq[5]+seq.substr(1,4);
+}
+
 function handle_a_ctrlkey(key)
 {
 	var i;
@@ -233,6 +244,7 @@ ShellLB.prototype.rpadding="\xb3";
 ShellLB.prototype.timeout=100;
 ShellLB.prototype.callback = message_callback;
 ShellLB.prototype.hotkeys = KEY_LEFT+KEY_RIGHT+"\b\x7f\x1b"+ctrl('O')+ctrl('U')+ctrl('T')+ctrl('K')+ctrl('P');
+ShellLB.prototype.mouse_miss_key = '\b';
 
 function Mainbar()
 {
@@ -241,6 +253,7 @@ function Mainbar()
 	this.xpos=2;
 	this.ypos=1;
 	this.hotkeys=KEY_DOWN+";"+ctrl('O')+ctrl('U')+ctrl('T')+ctrl('K')+ctrl('P');
+	this.mouse_miss_key = undefined;
 	this.add("|File","F",undefined,undefined,undefined,bbs.compare_ars("REST T"));
 	this.add("|Messages","M");
 	this.add("|Email","E",undefined,undefined,undefined,bbs.compare_ars("REST SE"));
@@ -639,8 +652,13 @@ while(bbs.online) {
 					main_right();
 					break;
 				}
-				if(x_sec=='\b' || x_sec=='\x7f' || x_sec=='\x1b')
+				if(x_sec=='\b' || x_sec=='\x7f' || x_sec=='\x1b') {
+					if (xtrnsec.mouse_miss_str !== undefined) {
+						console.ungetstr(mangle_mouse_seq(xtrnsec.mouse_miss_str));
+						delete xtrnsec.mouse_miss_str;
+					}
 					break;
+				}
 				if(x_sec==ctrl('O')
 						|| x_sec==ctrl('U')
 						|| x_sec==ctrl('T')
@@ -663,6 +681,10 @@ while(bbs.online) {
 					}
 					if(x_prog==KEY_RIGHT || x_prog=='\b' || x_prog=='\x7f' || x_prog=='\x1b') {
 						this_xtrnsec.erase();
+						if (this_xtrnsec.mouse_miss_str !== undefined) {
+							console.ungetstr(mangle_mouse_seq(this_xtrnsec.mouse_miss_str));
+							delete this_xtrnsec.mouse_miss_str;
+						}
 						break;
 					}
 					if(x_prog==ctrl('O')
@@ -758,6 +780,10 @@ while(bbs.online) {
 									break infoloop;
 								case KEY_RIGHT:
 								case '\b':
+									if (userlists.mouse_miss_str !== undefined) {
+										console.ungetstr(mangle_mouse_seq(userlists.mouse_miss_str));
+										delete userlists.mouse_miss_str;
+									}
 								case '\x7f':
 								case '\x1b':
 									userlists.erase();
@@ -797,6 +823,10 @@ while(bbs.online) {
 						done=1;
 						break infoloop;
 					case '\b':
+						if (infomenu.mouse_miss_str !== undefined) {
+							console.ungetstr(mangle_mouse_seq(infomenu.mouse_miss_str));
+							delete infomenu.mouse_miss_str;
+						}
 					case '\x7f':
 					case '\x1b':
 						break infoloop;
@@ -914,6 +944,10 @@ function show_filemenu()
 				menus_displayed.pop();
 				return;
 			case '\b':
+				if (filemenu.mouse_miss_str !== undefined) {
+					console.ungetstr(mangle_mouse_seq(filemenu.mouse_miss_str));
+					delete filemenu.mouse_miss_str;
+				}
 			case '\x7f':
 			case '\x1b':
 				filemenu.erase();
@@ -1571,6 +1605,10 @@ function show_messagemenu()
 				main_left();
 				return;
 			case '\b':
+				if (messagemenu.mouse_miss_str !== undefined) {
+					console.ungetstr(mangle_mouse_seq(messagemenu.mouse_miss_str));
+					delete messagemenu.mouse_miss_str;
+				}
 			case '\x7f':
 			case '\x1b':
 				cleararea(messagemenu.xpos,messagemenu.ypos,messagemenu.items[0].text.length,messagemenu.items.length,true);
@@ -1975,6 +2013,10 @@ function show_emailmenu()
 				menus_displayed.pop();
 				return;
 			case '\b':
+				if (emailmenu.mouse_miss_str !== undefined) {
+					console.ungetstr(mangle_mouse_seq(emailmenu.mouse_miss_str));
+					delete emailmenu.mouse_miss_str;
+				}
 			case '\x7f':
 			case '\x1b':
 				cleararea(emailmenu.xpos,emailmenu.ypos,emailmenu.items[0].text.length,emailmenu.items.length,true);
@@ -2133,6 +2175,10 @@ function show_chatmenu()
 				menus_displayed.pop();
 				return;
 			case '\b':
+				if (chatmenu.mouse_miss_str !== undefined) {
+					console.ungetstr(mangle_mouse_seq(chatmenu.mouse_miss_str));
+					delete chatmenu.mouse_miss_str;
+				}
 			case '\x7f':
 			case '\x1b':
 				cleararea(chatmenu.xpos,chatmenu.ypos,chatmenu.items[0].text.length,chatmenu.items.length,true);
@@ -2290,6 +2336,10 @@ function show_settingsmenu()
 				menus_displayed.pop();
 				return;
 			case '\b':
+				if (settingsmenu.mouse_miss_str !== undefined) {
+					console.ungetstr(mangle_mouse_seq(settingsmenu.mouse_miss_str));
+					delete settingsmenu.mouse_miss_str;
+				}
 			case '\x7f':
 			case '\x1b':
 				settingsmenu.erase();
diff --git a/exec/load/lightbar.js b/exec/load/lightbar.js
index 10a2e923ab..be0915ac6a 100644
--- a/exec/load/lightbar.js
+++ b/exec/load/lightbar.js
@@ -76,6 +76,8 @@ Lightbar.prototype.hblanks=2;
 Lightbar.prototype.hotkeys='';
 Lightbar.prototype.callback=undefined;
 Lightbar.prototype.timeout=0;
+Lightbar.prototype.mouse_miss_key=undefined;
+Lightbar.prototype.mouse_miss_str=undefined;
 
 Lightbar.prototype.add = function(txt, retval, width, lpadding, rpadding, disabled, nodraw)
 {
@@ -138,24 +140,82 @@ Lightbar.prototype.getval = function(current,key)
 	var ret=undefined;
 	var last_cur;
 	var ansi = '';
-	
+	var button;
+	var x;
+	var y;
+
+	function restuff()
+	{
+		console.ungetstr(ansi.substr(1));
+		ansi = '';
+	}
+
 	if(!this.nodraw)
 		this.draw();
-		
+	delete this.mouse_miss_str;
+
 	/* Main loop */
 	while(bbs.online) {
-	
 		last_cur=this.current;
 		/* Get input */
 		if(key==undefined || key=='' || key==null || ansi.length > 0) {
 			if(this.callback != undefined)
 				this.callback();
+			console.write("\x1b[?1000h");
 			if(this.timeout>1)
 				key=console.inkey(K_UPPER,this.timeout);
 			else
 				key=console.getkey(K_UPPER|K_NOSPIN);
+			console.write("\x1b[?1000l");
+			if (key !== '') {
+				if (key === '\x1b') {
+					if (ansi.length > 0) {
+						ansi += key;
+						restuff();
+						key = '\x1b';
+					}
+					else {
+						ansi += key;
+						key = undefined;
+					}
+				}
+				else if (ansi.length === 1) {
+					if (key === '[') {
+						ansi += key;
+						key = undefined;
+					}
+					else {
+						ansi += key;
+						restuff();
+						key = '\x1b';
+					}
+				}
+				else if (ansi.length === 2) {
+					if (key === 'M') {
+						ansi += key;
+						key = undefined;
+					}
+					else {
+						ansi += key;
+						restuff();
+						key = '\x1b';
+					}
+				}
+				else if (ansi.length > 2) {
+					ansi += key;
+					key = undefined;
+					if (ansi.length >= 6) {
+						button = ascii(ansi[3]) - ascii(' ');
+						x = ascii(ansi[4]) - ascii('!') + 1;
+						y = ascii(ansi[5]) - ascii('!') + 1;
+						key = 'Mouse';
+						this.mouse_miss_str = ansi;
+						ansi = '';
+					}
+				}
+			}
 		}
-		
+
 		else {
 			if(this.hotkeys.indexOf(key)!=-1) {
 				this.nodraw=false;
@@ -163,6 +223,27 @@ Lightbar.prototype.getval = function(current,key)
 			}
 			
 			switch(key) {
+				case 'Mouse':
+					ansi = '';
+					if (button === 0) {
+						var hit = this.mouse_hit(x, y);
+						if (hit === -1) {
+							if (this.mouse_miss_key !== undefined) {
+								return this.mouse_miss_key;
+							}
+						}
+						else {
+							delete this.mouse_miss_str;
+							this.draw(hit);
+							this.nodraw=false;
+							if(this.items[this.current].retval==undefined)
+								return(undefined);
+							return(this.items[this.current].retval);
+						}
+					}
+					else
+						delete this.mouse_miss_str;
+					break;
 				case KEY_UP:
 					if(this.direction==0) {
 						do {
@@ -248,6 +329,77 @@ Lightbar.prototype.getval = function(current,key)
 	}
 };
 
+Lightbar.prototype.mouse_hit = function(x, y)
+{
+	if(this.direction < 0 || this.direction > 1) {
+		alert("Unknown lightbar direction!");
+		return -1;
+	}
+
+	var i;
+	var curx=this.xpos;
+	var cury=this.ypos;
+
+	for(i=0; i<this.items.length; i++) {
+		var width;
+
+		// Some basic validation.
+		if(this.items[i]==undefined) {
+			alert("Sparse items array!");
+			return -1;
+		}
+		if(this.items[i].text==undefined) {
+			alert("No text for item "+i+"!");
+			return -1;
+		}
+
+		// Set up a cleaned version for length calculations.
+		var cleaned=this.items[i].text;
+		cleaned=cleaned.replace(/\|/g,'');
+
+		/*
+		 * Calculate the width.  Forced width, item width, text width
+		 * In that order.  First one wins.
+		 */
+		if(this.force_width>0)
+			width=this.force_width;
+		else {
+			if(this.items[i].width!=undefined)
+				width=this.items[i].width;
+			else
+				width=cleaned.length;
+		}
+
+		var lpadding=this.lpadding;
+		if(this.items[i].lpadding!=undefined)
+			lpadding=this.items[i].lpadding;
+
+		if(lpadding != undefined && lpadding != null) {
+			curx+=lpadding.length;
+		}
+
+		if (cury === y) {
+			if (x >= curx && x <= curx + width)
+				return i;
+		}
+		curx += width;
+
+		var rpadding=this.rpadding;
+		if(this.items[i].rpadding!=undefined)
+			rpadding=this.items[i].rpadding;
+		curx += rpadding.length;
+
+		if(this.direction==0) {
+			cury++;
+			curx=this.xpos;
+		}
+		else {
+			curx += this.hblanks;
+		}
+	}
+	return -1;
+};
+
 Lightbar.prototype.draw = function(current)
 {
 	var attr=this.bg<<4|this.fg;
-- 
GitLab