Instance.mjs 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. /*
  2. * Copyright (C) 2024-present Puter Technologies Inc.
  3. *
  4. * This file is part of Puter.
  5. *
  6. * Puter is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Affero General Public License as published
  8. * by the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Affero General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Affero General Public License
  17. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  18. */
  19. import { V86 } from "./V86Wrapper.mjs";
  20. /**
  21. * Class representing an Instance of an emulator machine.
  22. */
  23. class Instance {
  24. /**
  25. * Create an Instance.
  26. * @param {Object} options - Options for configuring the instance.
  27. * @param {boolean} [options.term=true] - Terminal option.
  28. * @param {boolean} [options.screen=false] - Screen option.
  29. * @param {number} [options.memory=1024] - Memory size for the instance; must be power of two.
  30. * @param {HTMLElement} [options.spawnRoot=undefined] - Html element where instance should be spawned.
  31. * @param {boolean} [options.autoStart=true] - Whether to automatically start the instance.
  32. * @param {string} [options.remote="./"] - Remote URL, defaults to origin.
  33. * @param {string} [options.wsUrl=""] - Websocket URL option for communication with the outside world.
  34. * @throws Will throw an error if remote URL does not end with a slash. @throws Will throw an error if the amount of memory provided is not a power of two.
  35. */
  36. constructor(options) {
  37. const defaultOptions = {
  38. term: false,
  39. screen: false,
  40. memory: 1024,
  41. spawnRoot: undefined,
  42. autoStart: true,
  43. remote: "./",
  44. instanceName: [...Array(10)].map(() => Math.random().toString(36)[2]).join(''),
  45. wsUrl: "",
  46. };
  47. const instanceOptions = { ...defaultOptions, ...options };
  48. if (!instanceOptions.remote.endsWith('/'))
  49. throw new Error("Instance ctor: Remote URL must end with a slash");
  50. if (typeof self !== 'undefined' && self.crypto) {
  51. this.instanceID = self.crypto.randomUUID();
  52. } else {
  53. this.instanceID = "Node";
  54. }
  55. this.terminals = [];
  56. let v86Options = {
  57. wasm_path: instanceOptions.remote + "third-party/v86.wasm",
  58. preserve_mac_from_state_image: true,
  59. memory_size: instanceOptions.memory * 1024 * 1024,
  60. vga_memory_size: 8 * 1024 * 1024,
  61. initial_state: { url: instanceOptions.remote + "static/image.bin" },
  62. filesystem: { baseurl: instanceOptions.remote + "static/9p-rootfs/" },
  63. autostart: instanceOptions.autoStart,
  64. };
  65. if (!(instanceOptions.wsUrl === ""))
  66. v86Options.network_relay_url = instanceOptions.wsUrl;
  67. if (!((Math.log(v86Options.memory_size) / Math.log(2)) % 1 === 0))
  68. throw new Error("Instance ctor: Amount of memory provided isn't a power of two");
  69. if (instanceOptions.screen === true) {
  70. if (instanceOptions.spawnRoot === undefined)
  71. throw new Error("Instance ctor: spawnRoot is undefined, cannot continue")
  72. instanceOptions.spawnRoot.appendChild((() => {
  73. const div = document.createElement("div");
  74. div.setAttribute("id", instanceOptions.instanceName + '-screen');
  75. const child_div = document.createElement("div");
  76. child_div.setAttribute("style", "white-space: pre; font: 14px monospace; line-height: 14px");
  77. const canvas = document.createElement("canvas");
  78. canvas.setAttribute("style", "display: none");
  79. div.appendChild(child_div);
  80. div.appendChild(canvas);
  81. return div;
  82. })());
  83. v86Options.screen_container = document.getElementById(instanceOptions.instanceName + '-screen');
  84. }
  85. this.vm = new V86(v86Options);
  86. if (instanceOptions.term === true) {
  87. if (instanceOptions.spawnRoot === undefined)
  88. throw new Error("Instance ctor: spawnRoot is undefined, cannot continue")
  89. var term = new Terminal({
  90. allowTransparency: true,
  91. });
  92. instanceOptions.spawnRoot.appendChild((() => {
  93. const div = document.createElement("div");
  94. div.setAttribute("id", instanceOptions.instanceName + '-terminal');
  95. return div;
  96. })());
  97. term.open(document.getElementById(instanceOptions.instanceName + '-terminal'));
  98. term.write("Now booting emu, please stand by ...");
  99. this.vm.add_listener("emulator-started", () => {
  100. // emulator.serial0_send("\nsh networking.sh > /dev/null 2>&1 &\n\n");
  101. // emulator.serial0_send("clear\n");
  102. term.write("Welcome to psl!");
  103. this.vm.serial0_send("\n");
  104. });
  105. this.vm.add_listener("serial0-output-byte", (byte) => {
  106. var chr = String.fromCharCode(byte);
  107. if (chr <= "~") {
  108. term.write(chr);
  109. }
  110. });
  111. term.onData(data => {
  112. this.vm.serial0_send(data);
  113. });
  114. }
  115. }
  116. }
  117. export default Instance