зеркало из https://github.com/mozilla/pluotsorbet.git
572 строки
24 KiB
JavaScript
572 строки
24 KiB
JavaScript
var Terminal;
|
|
(function (Terminal) {
|
|
function unexpected(message) {
|
|
console.error(message);
|
|
}
|
|
function clamp(value, min, max) {
|
|
return Math.max(min, Math.min(max, value));
|
|
}
|
|
function createProgramFromSource(gl, vertex, fragment) {
|
|
var key = vertex + "-" + fragment;
|
|
var program = createProgram(gl, [
|
|
createShader(gl, gl.VERTEX_SHADER, vertex),
|
|
createShader(gl, gl.FRAGMENT_SHADER, fragment)
|
|
]);
|
|
queryProgramAttributesAndUniforms(gl, program);
|
|
return program;
|
|
}
|
|
function createProgram(gl, shaders) {
|
|
var program = gl.createProgram();
|
|
shaders.forEach(function (shader) {
|
|
gl.attachShader(program, shader);
|
|
});
|
|
gl.linkProgram(program);
|
|
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
var lastError = gl.getProgramInfoLog(program);
|
|
unexpected("Cannot link program: " + lastError);
|
|
gl.deleteProgram(program);
|
|
}
|
|
return program;
|
|
}
|
|
function createShader(gl, shaderType, shaderSource) {
|
|
var shader = gl.createShader(shaderType);
|
|
gl.shaderSource(shader, shaderSource);
|
|
gl.compileShader(shader);
|
|
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
var lastError = gl.getShaderInfoLog(shader);
|
|
unexpected("Cannot compile shader: " + lastError);
|
|
gl.deleteShader(shader);
|
|
return null;
|
|
}
|
|
return shader;
|
|
}
|
|
function queryProgramAttributesAndUniforms(gl, program) {
|
|
program.uniforms = {};
|
|
program.attributes = {};
|
|
for (var i = 0, j = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); i < j; i++) {
|
|
var attribute = gl.getActiveAttrib(program, i);
|
|
program.attributes[attribute.name] = attribute;
|
|
attribute.location = gl.getAttribLocation(program, attribute.name);
|
|
}
|
|
for (var i = 0, j = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); i < j; i++) {
|
|
var uniform = gl.getActiveUniform(program, i);
|
|
program.uniforms[uniform.name] = uniform;
|
|
uniform.location = gl.getUniformLocation(program, uniform.name);
|
|
}
|
|
}
|
|
function create2DProjection(width, height, depth) {
|
|
// Note: This matrix flips the Y axis so 0 is at the top.
|
|
return new Float32Array([
|
|
2 / width,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
-2 / height,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
2 / depth,
|
|
0,
|
|
-1,
|
|
1,
|
|
0,
|
|
1,
|
|
]);
|
|
}
|
|
(function (CharacterCode) {
|
|
CharacterCode[CharacterCode["NewLine"] = 10] = "NewLine";
|
|
})(Terminal.CharacterCode || (Terminal.CharacterCode = {}));
|
|
var CharacterCode = Terminal.CharacterCode;
|
|
var Cursor = (function () {
|
|
function Cursor(x, y) {
|
|
this.x = x;
|
|
this.y = y;
|
|
// ...
|
|
}
|
|
return Cursor;
|
|
})();
|
|
Terminal.Cursor = Cursor;
|
|
var Buffer = (function () {
|
|
function Buffer() {
|
|
this.color = 0xFFFF;
|
|
this.clear();
|
|
this.starts = new Uint32Array(32);
|
|
this.buffer = new Uint8Array(1024 * 1024);
|
|
this.colors = new Uint16Array(1024 * 1024);
|
|
}
|
|
Object.defineProperty(Buffer.prototype, "w", {
|
|
/**
|
|
* Number of columns.
|
|
*/
|
|
get: function () {
|
|
return Math.max(this.previousMaxLineWidth, this.i - this.starts[this.h]);
|
|
},
|
|
enumerable: true,
|
|
configurable: true
|
|
});
|
|
Buffer.prototype.clear = function () {
|
|
this.h = 0;
|
|
this.i = 0;
|
|
this.version = 0;
|
|
this.previousMaxLineWidth = 0;
|
|
};
|
|
Buffer.prototype.writeCharCode = function (x) {
|
|
if (this.buffer.length === this.i) {
|
|
var buffer = new Uint8Array(this.buffer.length * 2);
|
|
buffer.set(this.buffer, 0);
|
|
this.buffer = buffer;
|
|
var colors = new Uint16Array(this.colors.length * 2);
|
|
colors.set(this.colors, 0);
|
|
this.colors = colors;
|
|
}
|
|
this.colors[this.i] = this.color;
|
|
this.buffer[this.i] = x;
|
|
this.i++;
|
|
this.version++;
|
|
};
|
|
Buffer.prototype.writeString = function (s) {
|
|
for (var i = 0; i < s.length; i++) {
|
|
var c = s.charCodeAt(i);
|
|
if (c === 10 /* NewLine */) {
|
|
this.writeLine();
|
|
continue;
|
|
}
|
|
this.writeCharCode(c);
|
|
}
|
|
};
|
|
Buffer.prototype.writeLine = function () {
|
|
if (this.starts.length === this.h + 1) {
|
|
var starts = new Uint32Array(this.starts.length * 2);
|
|
starts.set(this.starts, 0);
|
|
this.starts = starts;
|
|
}
|
|
this.previousMaxLineWidth = Math.max(this.previousMaxLineWidth, this.i - this.starts[this.h]);
|
|
this.starts[++this.h] = this.i;
|
|
this.version++;
|
|
};
|
|
Buffer.prototype.getLine = function (l) {
|
|
l = clamp(l, 0, this.h - 1);
|
|
var s = this.starts[l];
|
|
var e = i === this.h - 1 ? this.i : this.starts[l + 1];
|
|
var c = [];
|
|
for (var i = s; i < e; i++) {
|
|
c.push(String.fromCharCode(this.buffer[i]));
|
|
}
|
|
return c.join("");
|
|
};
|
|
return Buffer;
|
|
})();
|
|
Terminal.Buffer = Buffer;
|
|
var Screen = (function () {
|
|
function Screen(container, fontSize) {
|
|
if (fontSize === void 0) { fontSize = 12; }
|
|
this.container = container;
|
|
this.fontSize = fontSize;
|
|
this.wrap = false;
|
|
this.canvas = document.createElement("canvas");
|
|
container.appendChild(this.canvas);
|
|
var gl = this.gl = this.canvas.getContext("webgl", { alpha: false });
|
|
gl.clearColor(0.0, 0.0, 0.0, 1);
|
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
this.cursor = new Cursor(0, 0);
|
|
this.color = 0xFFFF;
|
|
this.initialize();
|
|
this.initializeColorPaletteTexture();
|
|
this.listenForContainerSizeChanges();
|
|
this.enterRenderLoop();
|
|
}
|
|
Screen.prototype.initialize = function () {
|
|
var gl = this.gl;
|
|
this.program = createProgramFromSource(gl, Screen.vertexShader, Screen.fragmentShader);
|
|
gl.useProgram(this.program);
|
|
this.vertexBuffer = gl.createBuffer();
|
|
this.tileTexture = gl.createTexture();
|
|
this.tileMapTexture = gl.createTexture();
|
|
this.colorPaletteTexture = gl.createTexture();
|
|
this.invalidate();
|
|
};
|
|
Screen.prototype.initializeColorPaletteTexture = function () {
|
|
var gl = this.gl;
|
|
gl.bindTexture(gl.TEXTURE_2D, this.colorPaletteTexture);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
var colorPalette = new Uint8Array(256 * 256 * 4);
|
|
var j = 0;
|
|
for (var i = 0; i < 256 * 256; i++) {
|
|
var r = (i >> 11) & 0x1F;
|
|
var g = (i >> 5) & 0x3F;
|
|
var b = (i >> 0) & 0x1F;
|
|
colorPalette[j++] = (r / 32) * 256;
|
|
colorPalette[j++] = (g / 64) * 256;
|
|
colorPalette[j++] = (b / 32) * 256;
|
|
colorPalette[j++] = 255;
|
|
}
|
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 256, 256, 0, gl.RGBA, gl.UNSIGNED_BYTE, colorPalette);
|
|
};
|
|
Screen.prototype.listenForContainerSizeChanges = function () {
|
|
var pollInterval = 10;
|
|
var w = this.containerWidth;
|
|
var h = this.containerHeight;
|
|
this.onContainerSizeChanged();
|
|
var self = this;
|
|
setInterval(function () {
|
|
if (w !== self.containerWidth || h !== self.containerHeight) {
|
|
self.onContainerSizeChanged();
|
|
w = self.containerWidth;
|
|
h = self.containerHeight;
|
|
}
|
|
}, pollInterval);
|
|
};
|
|
Screen.prototype.onContainerSizeChanged = function () {
|
|
var cw = this.containerWidth;
|
|
var ch = this.containerHeight;
|
|
var devicePixelRatio = window.devicePixelRatio || 1;
|
|
var backingStoreRatio = 1;
|
|
if (devicePixelRatio !== backingStoreRatio) {
|
|
this.ratio = devicePixelRatio / backingStoreRatio;
|
|
this.canvas.width = cw * this.ratio;
|
|
this.canvas.height = ch * this.ratio;
|
|
this.canvas.style.width = cw + 'px';
|
|
this.canvas.style.height = ch + 'px';
|
|
}
|
|
else {
|
|
this.ratio = 1;
|
|
this.canvas.width = cw;
|
|
this.canvas.height = ch;
|
|
}
|
|
this.resize();
|
|
};
|
|
Object.defineProperty(Screen.prototype, "containerWidth", {
|
|
get: function () {
|
|
return this.container.clientWidth;
|
|
},
|
|
enumerable: true,
|
|
configurable: true
|
|
});
|
|
Object.defineProperty(Screen.prototype, "containerHeight", {
|
|
get: function () {
|
|
return this.container.clientHeight;
|
|
},
|
|
enumerable: true,
|
|
configurable: true
|
|
});
|
|
Screen.prototype.resize = function () {
|
|
this.initializeSpriteSheet();
|
|
var gl = this.gl;
|
|
var program = this.program;
|
|
var screenW = this.w = this.canvas.width / this.tileW | 0;
|
|
var screenH = this.h = this.canvas.height / this.tileH | 0;
|
|
gl.viewport(0, 0, this.canvas.width, this.canvas.height);
|
|
this.screenBuffer = new Uint8Array(screenW * screenH * 4);
|
|
this.screenBufferView = new Uint32Array(this.screenBuffer.buffer);
|
|
gl.bindTexture(gl.TEXTURE_2D, this.tileMapTexture);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, screenW, screenH, 0, gl.RGBA, gl.UNSIGNED_BYTE, this.screenBuffer);
|
|
var matrix = create2DProjection(this.canvas.width, this.canvas.height, 2000);
|
|
gl.uniformMatrix4fv(this.program.uniforms.uTransformMatrix3D.location, false, matrix);
|
|
var w = this.canvas.width;
|
|
var h = this.canvas.height;
|
|
var f32 = new Float32Array([
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
w,
|
|
0,
|
|
1,
|
|
0,
|
|
w,
|
|
h,
|
|
1,
|
|
1,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
w,
|
|
h,
|
|
1,
|
|
1,
|
|
0,
|
|
h,
|
|
0,
|
|
1
|
|
]);
|
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
|
|
gl.bufferData(gl.ARRAY_BUFFER, f32, gl.DYNAMIC_DRAW);
|
|
gl.enableVertexAttribArray(program.attributes.aPosition.location);
|
|
gl.enableVertexAttribArray(program.attributes.aCoordinate.location);
|
|
gl.vertexAttribPointer(program.attributes.aPosition.location, 2, gl.FLOAT, false, 16, 0);
|
|
gl.vertexAttribPointer(program.attributes.aCoordinate.location, 2, gl.FLOAT, false, 16, 8);
|
|
gl.uniform2f(program.uniforms.uTileSize.location, this.tileW / this.spriteCanvas.width, this.tileH / this.spriteCanvas.height);
|
|
gl.uniform2f(program.uniforms.uScaledTileSize.location, 1 / screenW, 1 / screenH);
|
|
this.cursor.x = this.cursor.y = 0;
|
|
};
|
|
Screen.prototype.initializeSpriteSheet = function () {
|
|
var fontSize = this.fontSize * this.ratio;
|
|
this.spriteCanvas = document.createElement("canvas");
|
|
var context = this.spriteCanvas.getContext("2d");
|
|
this.spriteCanvas.width = 1024;
|
|
this.spriteCanvas.height = 256;
|
|
context.fillStyle = "#000000";
|
|
context.fillRect(0, 0, this.spriteCanvas.width, this.spriteCanvas.height);
|
|
context.clearRect(0, 0, this.spriteCanvas.width, this.spriteCanvas.height);
|
|
context.fillStyle = "white";
|
|
context.font = fontSize + 'px Input Mono Condensed, Consolas, Courier, monospace';
|
|
context.textBaseline = "bottom";
|
|
var metrics = context.measureText("A");
|
|
var tileW = this.tileW = Math.ceil(metrics.width);
|
|
var hPadding = 4 * this.ratio;
|
|
var tileH = this.tileH = fontSize + hPadding;
|
|
var tileColumns = this.tileColumns = this.spriteCanvas.width / tileW | 0;
|
|
var j = 0;
|
|
for (var i = 0; i < 256; i++) {
|
|
var x = (j % tileColumns) | 0;
|
|
var y = (j / tileColumns) | 0;
|
|
var c = String.fromCharCode(i);
|
|
context.fillText(c, x * tileW, fontSize + hPadding + y * tileH);
|
|
j++;
|
|
}
|
|
var gl = this.gl;
|
|
gl.bindTexture(gl.TEXTURE_2D, this.tileTexture);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.spriteCanvas);
|
|
// this.container.appendChild(this.spriteCanvas);
|
|
};
|
|
Screen.prototype.uploadScreenTexture = function () {
|
|
var gl = this.gl;
|
|
gl.bindTexture(gl.TEXTURE_2D, this.tileMapTexture);
|
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.w, this.h, 0, gl.RGBA, gl.UNSIGNED_BYTE, this.screenBuffer);
|
|
};
|
|
Screen.prototype.render = function () {
|
|
var gl = this.gl;
|
|
var program = this.program;
|
|
gl.activeTexture(gl.TEXTURE0);
|
|
gl.bindTexture(gl.TEXTURE_2D, this.tileTexture);
|
|
gl.uniform1i(program.uniforms.uTileSampler.location, 0);
|
|
gl.activeTexture(gl.TEXTURE0 + 1);
|
|
gl.bindTexture(gl.TEXTURE_2D, this.tileMapTexture);
|
|
gl.uniform1i(program.uniforms.uTileMapSampler.location, 1);
|
|
if (program.uniforms.uColorPaletteSampler) {
|
|
gl.activeTexture(gl.TEXTURE0 + 2);
|
|
gl.bindTexture(gl.TEXTURE_2D, this.colorPaletteTexture);
|
|
gl.uniform1i(program.uniforms.uColorPaletteSampler.location, 2);
|
|
}
|
|
gl.uniform1f(program.uniforms.uTime.location, performance.now() / 1000);
|
|
gl.clearColor(0x33 / 256, 0x33 / 256, 0x33 / 256, 1.0);
|
|
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
gl.disable(gl.DEPTH_TEST);
|
|
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
|
gl.enable(gl.BLEND);
|
|
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
|
};
|
|
Screen.prototype.enterRenderLoop = function () {
|
|
var self = this;
|
|
var gl = this.gl;
|
|
var program = this.program;
|
|
function tick() {
|
|
if (self.dirty) {
|
|
self.uploadScreenTexture();
|
|
self.render();
|
|
self.dirty = false;
|
|
}
|
|
requestAnimationFrame(tick);
|
|
}
|
|
requestAnimationFrame(tick);
|
|
};
|
|
Screen.prototype.move = function (x, y) {
|
|
this.cursor.x = x;
|
|
this.cursor.y = y;
|
|
};
|
|
Screen.prototype.next = function () {
|
|
this.cursor.x++;
|
|
if (this.cursor.x > this.w && this.wrap) {
|
|
this.cursor.x = 0;
|
|
this.cursor.y++;
|
|
}
|
|
if (this.cursor.y > this.h) {
|
|
this.cursor.x = 0;
|
|
this.cursor.y = 0;
|
|
this.scroll(1);
|
|
}
|
|
};
|
|
Screen.prototype.nextLine = function () {
|
|
this.cursor.y++;
|
|
this.cursor.x = 0;
|
|
if (this.cursor.y >= this.h) {
|
|
this.cursor.y--;
|
|
this.scroll(1);
|
|
}
|
|
};
|
|
Screen.prototype.invalidate = function () {
|
|
this.dirty = true;
|
|
};
|
|
Screen.prototype.clear = function () {
|
|
var view = this.screenBufferView;
|
|
for (var i = 0; i < view.length; i++) {
|
|
view[i] = 0;
|
|
}
|
|
};
|
|
Screen.prototype.scroll = function (n) {
|
|
var h = this.h;
|
|
var w = this.w;
|
|
var view = this.screenBufferView;
|
|
for (var y = 0; y < h; y++) {
|
|
if (y >= h - n) {
|
|
for (var x = 0; x < w; x++) {
|
|
view[y * w + x] = 0;
|
|
}
|
|
}
|
|
else {
|
|
for (var x = 0; x < w; x++) {
|
|
view[y * w + x] = view[(y + n) * w + x];
|
|
}
|
|
}
|
|
}
|
|
};
|
|
Screen.prototype.setColor = function (r, g, b) {
|
|
r = clamp(r, 0, 255);
|
|
g = clamp(g, 0, 255);
|
|
b = clamp(b, 0, 255);
|
|
this.color = ((r / 256 * 32) & 0x1F) << 11 | ((g / 256 * 64) & 0x3F) << 5 | ((b / 256 * 32) & 0x1F) << 0;
|
|
};
|
|
Screen.prototype.writeCharCode = function (c) {
|
|
this.invalidate();
|
|
if (c === 10 /* NewLine */) {
|
|
this.nextLine();
|
|
return;
|
|
}
|
|
var cursor = this.cursor;
|
|
var w = this.w;
|
|
var h = this.h;
|
|
if (cursor.x > w || cursor.x < 0 || cursor.y > h || cursor.y < 0) {
|
|
return;
|
|
}
|
|
var buffer = this.screenBuffer;
|
|
var i = (this.w * cursor.y + cursor.x) * 4;
|
|
var x = (c % this.tileColumns) | 0;
|
|
var y = (c / this.tileColumns) | 0;
|
|
buffer[i++] = x;
|
|
buffer[i++] = y;
|
|
buffer[i++] = this.color;
|
|
buffer[i++] = this.color >> 8;
|
|
this.next();
|
|
};
|
|
Screen.prototype.writeText = function (s) {
|
|
for (var i = 0; i < s.length; i++) {
|
|
this.writeCharCode(s.charCodeAt(i));
|
|
}
|
|
};
|
|
Screen.prototype.putChar = function (c, x, y, color) {
|
|
var i = (y * this.w + x) * 4;
|
|
var buffer = this.screenBuffer;
|
|
buffer[i++] = (c % this.tileColumns) | 0;
|
|
buffer[i++] = (c / this.tileColumns) | 0;
|
|
buffer[i++] = color;
|
|
buffer[i++] = color >> 8;
|
|
};
|
|
Screen.prototype.writeBuffer = function (buffer, x, y) {
|
|
var h = this.h, w = this.w;
|
|
x = clamp(x, 0, buffer.w - 1);
|
|
y = clamp(y, 0, buffer.h - 1);
|
|
var r = Math.min(h, buffer.h - y);
|
|
for (var j = 0; j < r; j++) {
|
|
var s = buffer.starts[y + j];
|
|
var e = buffer.starts[y + j + 1];
|
|
var l = e - s - x;
|
|
for (var i = 0; i < l; i++) {
|
|
var p = s + x + i;
|
|
var c = buffer.buffer[p];
|
|
var color = buffer.colors[p];
|
|
this.putChar(c, i, j, color);
|
|
}
|
|
}
|
|
this.invalidate();
|
|
};
|
|
Screen.vertexShader = "uniform mat4 uTransformMatrix3D; " + "attribute vec4 aPosition; " + "attribute vec2 aCoordinate; " + "varying vec2 vCoordinate; " + "varying vec2 vCoordinate2; " + "void main() { " + " gl_Position = uTransformMatrix3D * aPosition; " + " vCoordinate = aCoordinate; " + " vCoordinate2 = aCoordinate; " + "}";
|
|
Screen.fragmentShader = "precision mediump float; " + "uniform sampler2D uTileSampler; " + "uniform sampler2D uTileMapSampler; " + "uniform sampler2D uColorPaletteSampler; " + "varying vec2 vCoordinate; " + "varying vec2 vCoordinate2; " + "uniform float uTime; " + "uniform vec2 uTileSize; " + "uniform vec2 uScaledTileSize; " + "void main() { " + " float time = uTime; " + " vec4 tile = texture2D(uTileMapSampler, vCoordinate); " + " if (tile.x == 0.0 && tile.y == 0.0) { discard; } " + " vec2 tileOffset = floor(tile.xy * 256.0) * uTileSize; " + " vec2 tileCoordinate = tileOffset + mod(vCoordinate, uScaledTileSize) * (uTileSize / uScaledTileSize);" + " vec4 color = texture2D(uTileSampler, tileCoordinate) * texture2D(uColorPaletteSampler, tile.zw); " + " color.rgb *= color.a;" + " gl_FragColor = color;" + "}";
|
|
return Screen;
|
|
})();
|
|
Terminal.Screen = Screen;
|
|
function getTargetMousePos(event, target) {
|
|
var rect = target.getBoundingClientRect();
|
|
return {
|
|
x: event.clientX - rect.left,
|
|
y: event.clientY - rect.top
|
|
};
|
|
}
|
|
var View = (function () {
|
|
function View(screen, buffer) {
|
|
this.x = 0;
|
|
this.y = 0;
|
|
this.screen = screen;
|
|
this.buffer = buffer;
|
|
this.version = buffer.version;
|
|
this.enterRenderLoop();
|
|
var boundOnMouseWheel = this.onMouseWheel.bind(this);
|
|
screen.canvas.addEventListener(("onwheel" in document ? "wheel" : "mousewheel"), boundOnMouseWheel, false);
|
|
}
|
|
View.prototype.onMouseWheel = function (event) {
|
|
if (!event.altKey && !event.ctrlKey && !event.shiftKey) {
|
|
event.preventDefault();
|
|
var deltaX = event.deltaX;
|
|
var deltaY = event.deltaY;
|
|
if (event.deltaMode === WheelEvent.DOM_DELTA_PIXEL) {
|
|
deltaX /= 40;
|
|
deltaY /= 40;
|
|
}
|
|
else if (event.deltaMode === WheelEvent.DOM_DELTA_LINE) {
|
|
deltaX *= 10;
|
|
deltaY *= 10;
|
|
}
|
|
var x = clamp(deltaX, -1, 1);
|
|
var y = clamp(deltaY, -1, 1);
|
|
if (event.metaKey) {
|
|
x *= 10;
|
|
}
|
|
if (event.metaKey) {
|
|
y *= 100;
|
|
}
|
|
this.scroll(x, y);
|
|
}
|
|
};
|
|
View.prototype.scroll = function (x, y) {
|
|
this.y = clamp(this.y + y, 0, this.buffer.h - this.screen.h);
|
|
this.x = clamp(this.x + x, 0, this.buffer.w - this.screen.w);
|
|
this.version = 0;
|
|
};
|
|
View.prototype.scrollToBottom = function () {
|
|
this.x = 0;
|
|
this.y = clamp(this.buffer.h - this.screen.h, 0, this.buffer.h);
|
|
this.render();
|
|
};
|
|
View.prototype.render = function () {
|
|
this.screen.clear();
|
|
this.screen.writeBuffer(this.buffer, this.x | 0, this.y | 0);
|
|
};
|
|
View.prototype.enterRenderLoop = function () {
|
|
var self = this;
|
|
function tick() {
|
|
if (self.version !== self.buffer.version) {
|
|
self.render();
|
|
self.version = self.buffer.version;
|
|
}
|
|
requestAnimationFrame(tick);
|
|
}
|
|
requestAnimationFrame(tick);
|
|
};
|
|
return View;
|
|
})();
|
|
Terminal.View = View;
|
|
})(Terminal || (Terminal = {}));
|
|
//# sourceMappingURL=terminal.js.map
|