changeset 9:826a7ced69f8

Make the emulator screen resizable.
author John "Elwin" Edwards <elwin@sdf.org>
date Wed, 09 May 2012 13:38:05 -0700
parents ad0a31e52007
children d051aad3e95f
files rlgterm.js shterm.js termemu.js webtty.js
diffstat 4 files changed, 95 insertions(+), 12 deletions(-) [+]
line wrap: on
line diff
--- a/rlgterm.js	Mon May 07 16:08:59 2012 -0700
+++ b/rlgterm.js	Wed May 09 13:38:05 2012 -0700
@@ -405,7 +405,7 @@
       nsize = 48;
   }
   document.getElementById("term").style.fontSize = nsize.toString() + "px";
-  termemu.resize();
+  termemu.fixsize();
   debug(1, "Changing font size to " + nsize.toString());
   return;
 }
--- a/shterm.js	Mon May 07 16:08:59 2012 -0700
+++ b/shterm.js	Wed May 09 13:38:05 2012 -0700
@@ -270,7 +270,7 @@
 
 function setup() {
   keyHexCodes.init();
-  termemu.init("termwrap", 25, 80);
+  termemu.init("termwrap", 24, 80);
   setTitle("Not connected.");
   return;
 }
@@ -295,7 +295,7 @@
   return;
 }
 
-function login() {
+function login(h, w) {
   if (termemu.alive)
     return;
   var req = new XMLHttpRequest();
@@ -304,6 +304,8 @@
       var datalines = req.responseText.split("\n");
       if (datalines[0] == 'l1') {
         /* Success */
+        // FIXME extract the size from the response instead of hardcoding
+        termemu.resize(25, 80);
         termemu.alive = true;
 	setTitle("Logged in");
         debug(1, "Logged in with id " + datalines[1]);
@@ -314,7 +316,7 @@
     }
   };
   req.open('POST', '/login', true);
-  req.send("login=login");
+  req.send("login=login&h=" + String(h) + "&w=" + String(w));
   return;
 }
 
@@ -388,7 +390,7 @@
       nsize = 48;
   }
   document.getElementById("term").style.fontSize = nsize.toString() + "px";
-  termemu.resize();
+  termemu.fixsize();
   debug(1, "Changing font size to " + nsize.toString());
   return;
 }
--- a/termemu.js	Mon May 07 16:08:59 2012 -0700
+++ b/termemu.js	Wed May 09 13:38:05 2012 -0700
@@ -54,6 +54,7 @@
 // An object representing the terminal emulator.
 var termemu = {
   sessid: null, // Session key assigned by the server
+  alive: false,
   /* Some elements of the page. */
   inwrap: null,     // A non-table div wrapping the screen
   view: null,     // The div holding the terminal screen
@@ -192,14 +193,67 @@
     /* Attach them. */
     this.view = termdiv;
     this.screen = this.normbuf;
-    this.resize();
+    this.fixsize();
     this.cmove(0, 0);
   },
+  resize: function (h, w) {
+    if (this.screen == null)
+      return;
+    if (!(h > 0 && h < 256))
+      h = this.h;
+    if (!(w > 0 && w < 256))
+      w = this.w;
+    /* First give all the rows the right number of cells. */
+    var allrows = Array().concat(this.histbuf.childNodes, 
+                                 this.normbuf.childNodes, 
+                                 this.altbuf.childNodes);
+    for (var i = 0; i < allrows.length; i++) {
+      var row = allrows[i];
+      for (var j = Math.min(w, this.w); j < Math.max(w, this.w); j++) {
+        if (w < this.w)
+          row.removeChild(row.childNodes.lastChild);
+        else
+          row.appendChild(this.makeCell(' '));
+      }
+    }
+    this.w = w;
+    /* Now the rows. */
+    /* Resizing altbuf isn't always necessary. */
+    /* TODO resize and scrolling region interact in complicated ways that I 
+     * don't want to reverse-engineer.  For the moment, just don't do that.
+     */
+    if (h > this.h) {
+      for (var i = this.h; i < h; i++) {
+        this.normbuf.appendChild(this.makeRow());
+        this.altbuf.appendChild(this.makeRow());
+      }
+    }
+    else if (h < this.h) {
+      for (var i = h; i < this.h; i++) {
+        this.histbuf.appendChild(this.normbuf.firstChild);
+        if (this.altbuf.firstChild)
+          this.altbuf.removeChild(this.altbuf.firstChild);
+      }
+    }
+    /* Keep it on the bottom */
+    if (this.scrB == this.h - 1)
+      this.scrB = h - 1;
+    else if (this.scrB >= h)
+      this.scrB = h - 1;
+    if (this.scrT >= h - 1)
+      this.scrT = 0;
+    this.h = h;
+    this.fixsize();
+    /* If the cursor is now offscreen, cmove()'s sanity checks will fix it. */
+    this.cmove(null, null);
+    debug(1, "Size is now " + this.w + "x" + this.h);
+    return;
+  },
   valign: function () {
     if (this.screen == this.normbuf)
       this.inwrap.scrollTop = this.histbuf.clientHeight;
   },
-  resize: function () {
+  fixsize: function () {
     var owrap = document.getElementById("termwrap");
     /* Set the height up properly. */
     this.inwrap.style.height = this.screen.scrollHeight.toString() + "px";
@@ -215,7 +269,9 @@
   flipCursor: function () {
     /* Swaps the text and background colors of the active location. */
     /* This will change when other cursor styles are supported. */
-    if (this.c.x != null && this.c.y != null) {
+    /* Check: the cursor might be offscreen if it was resized. */
+    if (this.c.x != null && this.c.y != null && this.c.x >= 0 &&
+        this.c.x < this.w && this.c.y >= 0 && this.c.y < this.h) {
       var oldcell = this.screen.childNodes[this.c.y].childNodes[this.c.x];
       var tempswap = oldcell.style.color;
       oldcell.style.color = oldcell.style.backgroundColor;
--- a/webtty.js	Mon May 07 16:08:59 2012 -0700
+++ b/webtty.js	Wed May 09 13:38:05 2012 -0700
@@ -14,13 +14,23 @@
 /* Constructor for TermSessions.  Note that it opens the terminal and 
  * adds itself to the sessions dict. 
  */
-function TermSession(sessid) {
+function TermSession(sessid, h, w) {
+  /* Set up the sizes. */
+  w = Math.floor(Number(w));
+  if (!(w > 0 && w < 256))
+    w = 80;
+  this.w = w;
+  h = Math.floor(Number(h));
+  if (!(h > 0 && h < 256))
+    h = 25;
+  this.h = h;
+  /* Customize the environment. */
   var childenv = {};
   for (var key in process.env) {
     if (!(key in env_dontuse))
       childenv[key] = process.env[key];
   }
-  childenv["PTYHELPER"] = "25x80";
+  childenv["PTYHELPER"] = String(this.h) + "x" + String(this.w);
   // Should setsid get set?
   var spawnopts = {"env": childenv, "cwd": process.env["HOME"]};
   this.child = child_process.spawn(ptyhelp, ["bash"], spawnopts);
@@ -154,10 +164,25 @@
 function login(req, res, formdata) {
   var resheaders = {'Content-Type': 'text/plain'};
   var sessid = randkey();
-  var nsession = new TermSession(sessid);
+  /* The TermSession constructor will check these thoroughly too, but 
+   * you can't be too suspicious of client-supplied data. */
+  var w = 80;
+  var h = 25;
+  var t;
+  if ("w" in formdata) {
+    t = Math.floor(Number(formdata["w"]));
+    if (t > 0 && t < 256)
+      w = t;
+  }
+  if ("h" in formdata) {
+    t = Math.floor(Number(formdata["h"]));
+    if (t > 0 && t < 256)
+      h = t;
+  }
+  var nsession = new TermSession(sessid, h, w);
   resheaders["Set-Cookie"] = "ID=" + sessid;
   res.writeHead(200, resheaders);
-  res.write("l1\n" + sessid + "\n");
+  res.write("l1\n" + sessid + "\n" + String(h) + "x" + String(w) + "\n");
   res.end();
   console.log("Started new session with key " + sessid + ", pid " + nsession.child.pid);
   return;