A machine's screen can now be used to display initialization (diagnostic) messages, up until the Computer component is ready to power/start the CPU, at which point control of the screen reverts to the Video component

This commit is contained in:
Jeff Parsons 2017-06-28 16:24:33 -07:00 committed by Jeff Parsons
commit ea82dfec93
35 changed files with 6152 additions and 5539 deletions

View file

@ -143,8 +143,7 @@ class Computer extends Component {
{
super("Computer", parmsComputer, Messages.COMPUTER);
this.flags.powered = false;
var cmp = this;
this.setMachineParms(parmsMachine);
this.fAutoPower = this.getMachineParm('autoPower', parmsComputer);
@ -202,28 +201,39 @@ class Computer extends Component {
this.bus = new Bus({'id': this.idMachine + '.bus', 'busWidth': this.nBusWidth}, this.cpu, this.dbg);
/*
* Iterate through all the components and override their notice() and println() methods so that their
* output can be rerouted to an Initialization Display or a Control Panel, if any.
* Iterate through all the components and override their notice() and println() methods so
* that their output can be rerouted to an Initialization Display or a Control Panel, if any.
*/
var iComponent, component;
var aComponents = Component.getComponents(this.id);
this.panel = /** @type {Panel} */ (Component.getComponentByType("Panel", this.id));
this.controlPrint = this.panel && this.panel.bindings['print'];
this.controlPanel = this.panel && this.panel.bindings['print'];
if (this.controlPrint) {
for (iComponent = 0; iComponent < aComponents.length; iComponent++) {
component = aComponents[iComponent];
/*
* I can think of many "cleaner" ways for the Control Panel component to pass its
* notice(), println(), etc, overrides on to all the other components, but it's just
* too darn convenient to slam those overrides into the components directly.
*/
component.notice = this.panel.notice;
component.print = this.panel.print;
component.println = this.panel.println;
}
this.noticeComputer = this.notice;
this.printComputer = this.print;
this.printlnComputer = this.println;
if (this.controlPanel) {
this.noticeComputer = this.panel.notice;
this.printComputer = this.panel.print;
this.printlnComputer = this.panel.println;
}
for (iComponent = 0; iComponent < aComponents.length; iComponent++) {
component = aComponents[iComponent];
component.notice = function noticeComputer(s, fPrintOnly, id) {
cmp.outputDiagnostics(s);
return cmp.noticeComputer.call(this, s, fPrintOnly, id);
}.bind(component);
component.print = function printComputer(s) {
return cmp.printComputer.call(this, s);
}.bind(component);
component.println = function printlnComputer(s, type, id) {
cmp.outputDiagnostics(s, type);
return cmp.printlnComputer.call(this, s, type, id);
}.bind(component);
}
this.cDiagnosticScreens = 0;
if (!this.controlPanel) this.enableDiagnostics();
this.println(PCX86.APPNAME + " v" + (XMLVERSION || PCX86.APPVERSION) + "\n" + COPYRIGHT + "\n" + LICENSE);
@ -298,7 +308,6 @@ class Computer extends Component {
if (!sStatePath) {
this.setReady();
} else {
var cmp = this;
Web.getResource(sStatePath, null, true, function(sURL, sResource, nErrorCode) {
cmp.doneLoad(sURL, sResource, nErrorCode);
});
@ -319,8 +328,73 @@ class Computer extends Component {
*/
clearPanel()
{
if (this.controlPrint) {
this.controlPrint.value = "";
if (this.controlPanel) {
this.controlPanel.value = "";
}
}
/**
* enableDiagnostics()
*
* @this {Computer}
*/
enableDiagnostics()
{
for (var i = 0; i < this.aVideo.length; i++) {
var video = this.aVideo[i];
if (video) {
var control = video.getTextArea();
if (control) {
control.style.opacity = "1";
control.style.lineHeight = "1";
this.cDiagnosticScreens++;
}
}
}
}
/**
* disableDiagnostics()
*
* @this {Computer}
*/
disableDiagnostics()
{
for (var i = 0; i < this.aVideo.length; i++) {
var video = this.aVideo[i];
if (video) {
var control = video.getTextArea();
if (control) {
control.style.opacity = "0";
control.style.lineHeight = "0";
}
}
}
this.cDiagnosticScreens = 0;
}
/**
* outputDiagnostics(sMessage, sType)
*
* @this {Computer}
* @param {string} sMessage
* @param {string} [sType]
*/
outputDiagnostics(sMessage, sType)
{
if (!this.cDiagnosticScreens) return;
for (var i = 0; i < this.aVideo.length; i++) {
var video = this.aVideo[i];
if (video) {
var control = video.getTextArea();
if (control) {
if (sType != Component.TYPE.PROGRESS || sMessage.slice(-3) != "...") {
Component.appendControl(control, sMessage + '\n');
} else {
Component.replaceControl(control, sMessage, sMessage + '.');
}
}
}
}
}
@ -411,28 +485,6 @@ class Computer extends Component {
return this.sUserID || "";
}
/**
* setResourceStatus(sMessage)
*
* @this {Computer}
* @param {string} sMessage
*/
setResourceStatus(sMessage)
{
// var video = this.aVideo[0];
// if (video) {
// var eTextArea = video.getTextArea();
// if (eTextArea) {
// eTextArea.style.opacity = "1";
// eTextArea.style.color = "#ffffff";
// eTextArea.style.lineHeight = "1";
// eTextArea.style.fontSize = "large";
// eTextArea.style.background = "rgba(0, 0, 0, .5)";
// eTextArea.value = sMessage;
// }
// }
}
/**
* doneLoad(sURL, sStateData, nErrorCode)
*
@ -759,6 +811,8 @@ class Computer extends Component {
}
}
component.flags.initDone = true;
if (!fRepower && component.comment) {
var asComments = component.comment.split("|");
for (var i = 0; i < asComments.length; i++) {
@ -787,7 +841,11 @@ class Computer extends Component {
this.printMessage("Computer.donePowerOn(): redundant");
}
this.fInitialized = true;
if (!this.flags.initDone) {
this.disableDiagnostics();
this.flags.initDone = true;
}
this.flags.powered = true;
var controlPower = this.bindings["power"];
if (controlPower) controlPower.textContent = "Shutdown";
@ -1636,10 +1694,10 @@ class Computer extends Component {
computer.flags.unloading = false;
if (DEBUG && computer.messageEnabled()) {
computer.printMessage("onShow(" + computer.fInitialized + "," + computer.flags.powered + ")");
computer.printMessage("onShow(" + computer.flags.initDone + "," + computer.flags.powered + ")");
}
if (computer.fInitialized && !computer.flags.powered) {
if (computer.flags.initDone && !computer.flags.powered) {
/**
* Repower the computer, notifying every component to continue running as-is.
*/

View file

@ -531,8 +531,11 @@ class Disk extends Component {
}
}
}
var sProgress = "Loading " + sDiskURL + "...";
return !!Web.getResource(sDiskURL, null, true, function loadDone(sURL, sResponse, nErrorCode) {
disk.doneLoad(sURL, sResponse, nErrorCode);
}, function(nState) {
disk.println(sProgress, Component.TYPE.PROGRESS);
});
}

View file

@ -141,9 +141,11 @@ class ROM extends Component {
this.cpu = cpu;
this.dbg = dbg;
if (this.sFileURL) {
this.cmp.setResourceStatus(this.sFileURL);
var sProgress = "Loading " + this.sFileURL + "...";
Web.getResource(this.sFileURL, null, true, function(sURL, sResponse, nErrorCode) {
rom.doneLoad(sURL, sResponse, nErrorCode);
}, function(nState) {
rom.println(sProgress, Component.TYPE.PROGRESS);
});
}
}

View file

@ -6699,21 +6699,21 @@ class Video extends Component {
*/
static init()
{
var aeVideo = Component.getElementsByClass(document, PCX86.APPCLASS, "video");
for (var iVideo = 0; iVideo < aeVideo.length; iVideo++) {
var eVideo = aeVideo[iVideo];
var parmsVideo = Component.getComponentParms(eVideo);
var aElement = Component.getElementsByClass(document, PCX86.APPCLASS, "video");
for (var iVideo = 0; iVideo < aElement.length; iVideo++) {
var element = aElement[iVideo];
var parmsVideo = Component.getComponentParms(element);
var eCanvas = /** @type {HTMLCanvasElement} */ (document.createElement("canvas"));
if (eCanvas === undefined || !eCanvas.getContext) {
eVideo.innerHTML = "<br/>Missing &lt;canvas&gt; support. Please try a newer web browser.";
var canvas = /** @type {HTMLCanvasElement} */ (document.createElement("canvas"));
if (canvas === undefined || !canvas.getContext) {
element.innerHTML = "<br/>Missing &lt;canvas&gt; support. Please try a newer web browser.";
return;
}
eCanvas.setAttribute("class", "pcjs-canvas");
eCanvas.setAttribute("width", parmsVideo['screenWidth']);
eCanvas.setAttribute("height", parmsVideo['screenHeight']);
eCanvas.style.backgroundColor = parmsVideo['screenColor'];
canvas.setAttribute("class", "pcjs-canvas");
canvas.setAttribute("width", parmsVideo['screenWidth']);
canvas.setAttribute("height", parmsVideo['screenHeight']);
canvas.style.backgroundColor = parmsVideo['screenColor'];
/*
* The "contenteditable" attribute on a canvas element NOTICEABLY slows down canvas drawing on
@ -6721,7 +6721,7 @@ class Video extends Component {
* up; click on the canvas, and drawing slows down). So the "transparent textarea hack" that we
* once employed as only a work-around for Android devices is now our default.
*
* eCanvas.setAttribute("contenteditable", "true");
* canvas.setAttribute("contenteditable", "true");
*
* HACK: A canvas style of "auto" provides for excellent responsive canvas scaling in EVERY browser
* except IE9/IE10, so I recalculate the appropriate CSS height every time the parent DIV is resized;
@ -6730,14 +6730,14 @@ class Video extends Component {
* The other reason it's good to keep this particular hack limited to IE9/IE10 is that most other
* browsers don't actually support an 'onresize' handler on anything but the window object.
*/
eCanvas.style.height = "auto";
canvas.style.height = "auto";
if (Web.getUserAgent().indexOf("MSIE") >= 0) {
eVideo.onresize = function(eParent, eChild, cx, cy) {
element.onresize = function(eParent, eChild, cx, cy) {
return function onResizeVideo() {
eChild.style.height = (((eParent.clientWidth * cy) / cx) | 0) + "px";
};
}(eVideo, eCanvas, parmsVideo['screenWidth'], parmsVideo['screenHeight']);
eVideo.onresize(null);
}(element, canvas, parmsVideo['screenWidth'], parmsVideo['screenHeight']);
element.onresize(null);
}
/*
* The following is a related hack that allows the user to force the screen to use a particular aspect
@ -6767,10 +6767,10 @@ class Video extends Component {
*/
eChild.style.height = ((eParent.clientWidth / aspectRatio)|0) + "px";
};
}(eVideo, eCanvas, aspect));
}(element, canvas, aspect));
window['onresize']();
}
eVideo.appendChild(eCanvas);
element.appendChild(canvas);
/*
* HACK: Android-based browsers, like the Silk (Amazon) browser and Chrome for Android, don't honor the
@ -6793,14 +6793,14 @@ class Video extends Component {
* clearly see the overlaid semi-transparent input field, but none of the input characters were passed along,
* with the exception of the "Go" (Enter) key.
*
* var eInput = document.createElement("input");
* eInput.setAttribute("type", "password");
* eInput.setAttribute("style", "position:absolute; left:0; top:0; width:100%; height:100%; opacity:0.5");
* eVideo.appendChild(eInput);
* var input = document.createElement("input");
* input.setAttribute("type", "password");
* input.setAttribute("style", "position:absolute; left:0; top:0; width:100%; height:100%; opacity:0.5");
* element.appendChild(input);
*
* See this Chromium issue for more information: https://code.google.com/p/chromium/issues/detail?id=118639
*/
var eTextArea = /** @type {HTMLTextAreaElement} */ (document.createElement("textarea"));
var textarea = /** @type {HTMLTextAreaElement} */ (document.createElement("textarea"));
/*
* As noted in keyboard.js, the keyboard on an iOS device tends to pop up with the SHIFT key depressed,
@ -6808,8 +6808,8 @@ class Video extends Component {
* these "auto" attributes will help.
*/
if (Web.isUserAgent("iOS")) {
eTextArea.setAttribute("autocapitalize", "off");
eTextArea.setAttribute("autocorrect", "off");
textarea.setAttribute("autocapitalize", "off");
textarea.setAttribute("autocorrect", "off");
/*
* One of the problems on iOS devices is that after a soft-key control is clicked, we need to give
* focus back to the above textarea, usually by calling cmp.updateFocus(), but in doing so, iOS may
@ -6818,21 +6818,21 @@ class Video extends Component {
* Googling reveals that another way to prevent those jarring unintentional zooms is to simply set the
* font-size of the text control to 16px. So that's what we do.
*/
eTextArea.style.fontSize = "16px";
textarea.style.fontSize = "16px";
}
eVideo.appendChild(eTextArea);
element.appendChild(textarea);
/*
* Now we can create the Video object, record it, and wire it up to the associated document elements.
*/
var eContext = /** @type {CanvasRenderingContext2D} */ (eCanvas.getContext("2d"));
var video = new Video(parmsVideo, eCanvas, eContext, eTextArea /* || eInput */, eVideo);
var context = /** @type {CanvasRenderingContext2D} */ (canvas.getContext("2d"));
var video = new Video(parmsVideo, canvas, context, textarea /* || input */, element);
/*
* Bind any video-specific controls (eg, the Refresh button). There are no essential controls, however;
* even the "Refresh" button is just a diagnostic tool, to ensure that the screen contents are up-to-date.
*/
Component.bindComponentControls(video, eVideo, PCX86.APPCLASS);
Component.bindComponentControls(video, element, PCX86.APPCLASS);
}
}
}