vtac-terminal 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/VTAC/VTAC.js CHANGED
@@ -1,19 +1,8 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.VTAC = void 0;
7
- const sdl_1 = __importDefault(require("@kmamal/sdl"));
8
- const serialport_1 = require("serialport");
9
4
  class VTAC {
10
5
  constructor() {
11
- this.baudRate = 9600;
12
- this.parity = 'none';
13
- this.dataBits = 8;
14
- this.stopBits = 1;
15
- this.scale = 2;
16
- this.fullscreen = false;
17
6
  this.buffer = Buffer.alloc(VTAC.WIDTH * VTAC.HEIGHT).fill(0x00);
18
7
  this.mode = 'text';
19
8
  this.column = 0;
@@ -24,107 +13,16 @@ class VTAC {
24
13
  this.cursorChar = 0x00; // OFF
25
14
  this.cursorMode = 'solid';
26
15
  this.cursorCharNextByte = false;
27
- this.cursorBlinkTime = 0;
28
- this.cursorVisible = true;
29
- this.cursorBlinkInterval = 500; // Blink every 500ms (twice per second)
30
16
  this.bellDuration = 0x3C; // Duration in jiffies (1/60th of a second) (Default: 1 second)
31
17
  this.bellFrequency = 0x3D; // Frequency value (Default: C6)
32
18
  this.bellDurationNextByte = false;
33
19
  this.bellFrequencyNextByte = false;
34
20
  this.bellQueue = [];
35
- this.isBellPlaying = false;
36
- this.bellAudioDevice = undefined;
37
21
  this.dataNextByte = false;
38
22
  this.foregroundColor = 0xFF; // White
39
23
  this.backgroundColor = 0x00; // Black
40
24
  this.foregroundColorNextByte = false;
41
25
  this.backgroundColorNextByte = false;
42
- this.lastRenderTime = 0;
43
- this.targetFrameTime = 1000 / 60; // 60fps = ~16.67ms per frame
44
- //
45
- // MAIN
46
- //
47
- this.begin = () => {
48
- if (this.path) {
49
- this.port = new serialport_1.SerialPort({
50
- path: this.path,
51
- baudRate: this.baudRate,
52
- parity: this.parity,
53
- dataBits: this.dataBits,
54
- stopBits: this.stopBits
55
- }, (err) => {
56
- if (err) {
57
- console.log('Error: ', err.message);
58
- }
59
- });
60
- this.port.on('data', this.receive);
61
- }
62
- this.window = sdl_1.default.video.createWindow({
63
- title: "VT-AC",
64
- width: VTAC.WIDTH * this.scale,
65
- height: VTAC.HEIGHT * this.scale,
66
- fullscreen: this.fullscreen,
67
- resizable: false
68
- });
69
- this.window.on('keyDown', this.onKey);
70
- this.window.on('textInput', this.onText);
71
- this.window.on('close', (event) => {
72
- if (this.port && this.port.isOpen) {
73
- this.port.close();
74
- }
75
- });
76
- // Start the render loop
77
- this.render();
78
- };
79
- this.render = () => {
80
- if (!this.window) {
81
- return;
82
- }
83
- if (this.window.destroyed) {
84
- return;
85
- }
86
- const now = Date.now();
87
- const elapsed = now - this.lastRenderTime;
88
- // Update cursor blink state
89
- if (this.cursorMode === 'blinking') {
90
- this.cursorBlinkTime += elapsed;
91
- if (this.cursorBlinkTime >= this.cursorBlinkInterval) {
92
- this.cursorVisible = !this.cursorVisible;
93
- this.cursorBlinkTime = 0;
94
- }
95
- }
96
- else {
97
- this.cursorVisible = true;
98
- }
99
- // Create temporary render buffer
100
- const renderBuffer = Buffer.from(this.buffer);
101
- // Draw cursor if visible and cursorChar is not 0x00 (OFF)
102
- if (this.cursorVisible && this.cursorChar !== 0x00) {
103
- this.drawCursor(renderBuffer);
104
- }
105
- this.window.render(VTAC.WIDTH, VTAC.HEIGHT, VTAC.WIDTH, 'rgb332', renderBuffer);
106
- this.lastRenderTime = now;
107
- // Calculate delay to maintain target frame rate
108
- const renderTime = Date.now() - now;
109
- const delay = Math.max(0, this.targetFrameTime - renderTime);
110
- setTimeout(this.render, delay);
111
- };
112
- this.drawCursor = (renderBuffer) => {
113
- const character = VTAC.CHARACTERS[this.cursorChar];
114
- const startRow = this.row * 8;
115
- const startColumn = this.column * 8;
116
- // Draw cursor character with inverted colors
117
- for (let y = 0; y < 8; y++) {
118
- const rowByte = character[y];
119
- const bufferRowStart = (startRow + y) * VTAC.WIDTH;
120
- for (let x = 0; x < 8; x++) {
121
- const bit = (rowByte >> (7 - x)) & 1;
122
- // Invert the colors: if bit is 1, use background; if 0, use foreground
123
- const color = bit ? this.backgroundColor : this.foregroundColor;
124
- renderBuffer[bufferRowStart + startColumn + x] = color;
125
- }
126
- }
127
- };
128
26
  //
129
27
  // METHODS
130
28
  //
@@ -139,14 +37,6 @@ class VTAC {
139
37
  this.foregroundColor = 0xFF;
140
38
  this.bellDuration = 0x3C;
141
39
  this.bellFrequency = 0x3D;
142
- this.bellQueue = [];
143
- this.isBellPlaying = false;
144
- // Close audio device if open
145
- if (this.bellAudioDevice) {
146
- this.bellAudioDevice.clearQueue();
147
- this.bellAudioDevice.close();
148
- this.bellAudioDevice = undefined;
149
- }
150
40
  this.columnNextByte = false;
151
41
  this.rowNextByte = false;
152
42
  this.cursorCharNextByte = false;
@@ -154,6 +44,7 @@ class VTAC {
154
44
  this.backgroundColorNextByte = false;
155
45
  this.bellDurationNextByte = false;
156
46
  this.bellFrequencyNextByte = false;
47
+ this.bellQueue = [];
157
48
  this.buffer.fill(0x00);
158
49
  };
159
50
  this.bell = () => {
@@ -164,80 +55,14 @@ class VTAC {
164
55
  return;
165
56
  }
166
57
  // Add bell request to queue
167
- this.bellQueue.push({
168
- frequency: frequency,
169
- duration: this.bellDuration
170
- });
171
- // Start processing queue if not already playing
172
- if (!this.isBellPlaying) {
173
- this.processBellQueue();
174
- }
58
+ this.bellQueue.push({ frequency, duration: this.bellDuration });
175
59
  };
176
- this.processBellQueue = () => {
177
- // If queue is empty, stop processing and close audio device
178
- if (this.bellQueue.length === 0) {
179
- this.isBellPlaying = false;
180
- if (this.bellAudioDevice) {
181
- this.bellAudioDevice.clearQueue();
182
- this.bellAudioDevice.pause();
183
- this.bellAudioDevice.close();
184
- this.bellAudioDevice = undefined;
185
- }
186
- return;
187
- }
188
- // Mark as playing
189
- this.isBellPlaying = true;
190
- // Open audio device if not already open
191
- if (!this.bellAudioDevice) {
192
- const sampleRate = 44100;
193
- this.bellAudioDevice = sdl_1.default.audio.openDevice({ type: 'playback' }, {
194
- channels: 1,
195
- frequency: sampleRate,
196
- format: 'f32',
197
- buffered: 4096 // Buffer size must be power of 2
198
- });
199
- if (!this.bellAudioDevice) {
200
- // Fallback to console beep if audio device cannot be opened
201
- process.stdout.write('\u0007');
202
- // Clear queue and stop
203
- this.bellQueue = [];
204
- this.isBellPlaying = false;
205
- return;
206
- }
207
- // Start playback
208
- this.bellAudioDevice.play();
209
- }
210
- // Get next bell request from queue
211
- const bellRequest = this.bellQueue.shift();
212
- if (!bellRequest) {
213
- this.processBellQueue(); // Check if queue is empty
214
- return;
215
- }
216
- const { frequency, duration } = bellRequest;
217
- // Convert jiffies (1/60th of a second) to milliseconds
218
- const durationMs = (duration / 60) * 1000;
219
- // Generate sine wave samples
220
- const sampleRate = 44100;
221
- const numSamples = Math.floor((durationMs / 1000) * sampleRate);
222
- const samples = new Float32Array(numSamples);
223
- for (let i = 0; i < numSamples; i++) {
224
- // Generate sine wave: amplitude * sin(2π * frequency * time)
225
- const time = i / sampleRate;
226
- const amplitude = 0.2; // Keep volume at 20% to avoid distortion
227
- samples[i] = amplitude * Math.sin(2 * Math.PI * frequency * time);
228
- // Apply fade out in the last 10% to avoid clicks
229
- const fadeStartSample = numSamples * 0.9;
230
- if (i > fadeStartSample) {
231
- const fadeProgress = (i - fadeStartSample) / (numSamples - fadeStartSample);
232
- samples[i] *= (1 - fadeProgress);
233
- }
234
- }
235
- // Enqueue the audio samples
236
- this.bellAudioDevice.enqueue(Buffer.from(samples.buffer));
237
- // Process next bell in queue after this one finishes
238
- setTimeout(() => {
239
- this.processBellQueue();
240
- }, durationMs);
60
+ this.getNextBell = () => {
61
+ // Remove and return the first bell from queue
62
+ return this.bellQueue.shift();
63
+ };
64
+ this.hasQueuedBells = () => {
65
+ return this.bellQueue.length > 0;
241
66
  };
242
67
  this.backspace = () => {
243
68
  if (this.column > 0) {
@@ -629,345 +454,6 @@ class VTAC {
629
454
  break;
630
455
  }
631
456
  };
632
- this.transmit = (data) => {
633
- var _a;
634
- (_a = this.port) === null || _a === void 0 ? void 0 : _a.write([data], 'hex', (err) => {
635
- if (err) {
636
- console.log('Error: ', err.message);
637
- }
638
- });
639
- };
640
- this.receive = (data) => {
641
- for (let i = 0; i < data.length; i++) {
642
- this.parse(data[i]);
643
- }
644
- };
645
- this.onKey = (event) => {
646
- switch (event.key) {
647
- case 'backspace':
648
- this.transmit(0x08);
649
- break;
650
- case 'tab':
651
- this.transmit(0x09);
652
- break;
653
- case 'enter':
654
- case 'return':
655
- this.transmit(0x0D);
656
- this.transmit(0x0A);
657
- break;
658
- case 'escape':
659
- this.transmit(0x1B);
660
- break;
661
- case 'left':
662
- this.transmit(0x1C);
663
- break;
664
- case 'right':
665
- this.transmit(0x1D);
666
- break;
667
- case 'up':
668
- this.transmit(0x1E);
669
- break;
670
- case 'down':
671
- this.transmit(0x1F);
672
- break;
673
- case 'delete':
674
- this.transmit(0x7F);
675
- break;
676
- default:
677
- break;
678
- }
679
- };
680
- this.onText = (event) => {
681
- switch (event.text) {
682
- case ' ':
683
- this.transmit(0x20);
684
- break;
685
- case '!':
686
- this.transmit(0x21);
687
- break;
688
- case '"':
689
- this.transmit(0x22);
690
- break;
691
- case '#':
692
- this.transmit(0x23);
693
- break;
694
- case '$':
695
- this.transmit(0x24);
696
- break;
697
- case '%':
698
- this.transmit(0x25);
699
- break;
700
- case '&':
701
- this.transmit(0x26);
702
- break;
703
- case '\'':
704
- this.transmit(0x27);
705
- break;
706
- case '(':
707
- this.transmit(0x28);
708
- break;
709
- case ')':
710
- this.transmit(0x29);
711
- break;
712
- case '*':
713
- this.transmit(0x2A);
714
- break;
715
- case '+':
716
- this.transmit(0x2B);
717
- break;
718
- case ',':
719
- this.transmit(0x2C);
720
- break;
721
- case '-':
722
- this.transmit(0x2D);
723
- break;
724
- case '.':
725
- this.transmit(0x2E);
726
- break;
727
- case '/':
728
- this.transmit(0x2F);
729
- break;
730
- case '0':
731
- this.transmit(0x30);
732
- break;
733
- case '1':
734
- this.transmit(0x31);
735
- break;
736
- case '2':
737
- this.transmit(0x32);
738
- break;
739
- case '3':
740
- this.transmit(0x33);
741
- break;
742
- case '4':
743
- this.transmit(0x34);
744
- break;
745
- case '5':
746
- this.transmit(0x35);
747
- break;
748
- case '6':
749
- this.transmit(0x36);
750
- break;
751
- case '7':
752
- this.transmit(0x37);
753
- break;
754
- case '8':
755
- this.transmit(0x38);
756
- break;
757
- case '9':
758
- this.transmit(0x39);
759
- break;
760
- case ':':
761
- this.transmit(0x3A);
762
- break;
763
- case ';':
764
- this.transmit(0x3B);
765
- break;
766
- case '<':
767
- this.transmit(0x3C);
768
- break;
769
- case '=':
770
- this.transmit(0x3D);
771
- break;
772
- case '>':
773
- this.transmit(0x3E);
774
- break;
775
- case '?':
776
- this.transmit(0x3F);
777
- break;
778
- case '@':
779
- this.transmit(0x40);
780
- break;
781
- case 'A':
782
- this.transmit(0x41);
783
- break;
784
- case 'B':
785
- this.transmit(0x42);
786
- break;
787
- case 'C':
788
- this.transmit(0x43);
789
- break;
790
- case 'D':
791
- this.transmit(0x44);
792
- break;
793
- case 'E':
794
- this.transmit(0x45);
795
- break;
796
- case 'F':
797
- this.transmit(0x46);
798
- break;
799
- case 'G':
800
- this.transmit(0x47);
801
- break;
802
- case 'H':
803
- this.transmit(0x48);
804
- break;
805
- case 'I':
806
- this.transmit(0x49);
807
- break;
808
- case 'J':
809
- this.transmit(0x4A);
810
- break;
811
- case 'K':
812
- this.transmit(0x4B);
813
- break;
814
- case 'L':
815
- this.transmit(0x4C);
816
- break;
817
- case 'M':
818
- this.transmit(0x4D);
819
- break;
820
- case 'N':
821
- this.transmit(0x4E);
822
- break;
823
- case 'O':
824
- this.transmit(0x4F);
825
- break;
826
- case 'P':
827
- this.transmit(0x50);
828
- break;
829
- case 'Q':
830
- this.transmit(0x51);
831
- break;
832
- case 'R':
833
- this.transmit(0x52);
834
- break;
835
- case 'S':
836
- this.transmit(0x53);
837
- break;
838
- case 'T':
839
- this.transmit(0x54);
840
- break;
841
- case 'U':
842
- this.transmit(0x55);
843
- break;
844
- case 'V':
845
- this.transmit(0x56);
846
- break;
847
- case 'W':
848
- this.transmit(0x57);
849
- break;
850
- case 'X':
851
- this.transmit(0x58);
852
- break;
853
- case 'Y':
854
- this.transmit(0x59);
855
- break;
856
- case 'Z':
857
- this.transmit(0x5A);
858
- break;
859
- case '[':
860
- this.transmit(0x5B);
861
- break;
862
- case '\\':
863
- this.transmit(0x5C);
864
- break;
865
- case ']':
866
- this.transmit(0x5D);
867
- break;
868
- case '^':
869
- this.transmit(0x5E);
870
- break;
871
- case '_':
872
- this.transmit(0x5F);
873
- break;
874
- case '`':
875
- this.transmit(0x60);
876
- break;
877
- case 'a':
878
- this.transmit(0x61);
879
- break;
880
- case 'b':
881
- this.transmit(0x62);
882
- break;
883
- case 'c':
884
- this.transmit(0x63);
885
- break;
886
- case 'd':
887
- this.transmit(0x64);
888
- break;
889
- case 'e':
890
- this.transmit(0x65);
891
- break;
892
- case 'f':
893
- this.transmit(0x66);
894
- break;
895
- case 'g':
896
- this.transmit(0x67);
897
- break;
898
- case 'h':
899
- this.transmit(0x68);
900
- break;
901
- case 'i':
902
- this.transmit(0x69);
903
- break;
904
- case 'j':
905
- this.transmit(0x6A);
906
- break;
907
- case 'k':
908
- this.transmit(0x6B);
909
- break;
910
- case 'l':
911
- this.transmit(0x6C);
912
- break;
913
- case 'm':
914
- this.transmit(0x6D);
915
- break;
916
- case 'n':
917
- this.transmit(0x6E);
918
- break;
919
- case 'o':
920
- this.transmit(0x6F);
921
- break;
922
- case 'p':
923
- this.transmit(0x70);
924
- break;
925
- case 'q':
926
- this.transmit(0x71);
927
- break;
928
- case 'r':
929
- this.transmit(0x72);
930
- break;
931
- case 's':
932
- this.transmit(0x73);
933
- break;
934
- case 't':
935
- this.transmit(0x74);
936
- break;
937
- case 'u':
938
- this.transmit(0x75);
939
- break;
940
- case 'v':
941
- this.transmit(0x76);
942
- break;
943
- case 'w':
944
- this.transmit(0x77);
945
- break;
946
- case 'x':
947
- this.transmit(0x78);
948
- break;
949
- case 'y':
950
- this.transmit(0x79);
951
- break;
952
- case 'z':
953
- this.transmit(0x7A);
954
- break;
955
- case '{':
956
- this.transmit(0x7B);
957
- break;
958
- case '|':
959
- this.transmit(0x7C);
960
- break;
961
- case '}':
962
- this.transmit(0x7D);
963
- break;
964
- case '~':
965
- this.transmit(0x7E);
966
- break;
967
- default:
968
- break;
969
- }
970
- };
971
457
  }
972
458
  }
973
459
  exports.VTAC = VTAC;