1 /*
2 * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package hat.tools.textmodel.terminal;
26
27
28 import hat.tools.textmodel.tokens.Token;
29
30 import java.io.PrintStream;
31 import java.util.Arrays;
32 import java.util.function.Consumer;
33 import java.util.function.Function;
34
35 public interface ANSI<T extends ANSI<T>> extends Function<String, T> {
36 T apply(String o);
37
38 default T apply(Token t) {
39 return apply(t.asString());
40 }
41
42 default T self() {
43 return (T) this;
44 }
45
46 default void skip(){
47 }
48
49 int BLACK = 30;
50 int RED = 31;
51 int GREEN = 32;
52 int YELLOW = 33;
53 int BLUE = 34;
54 int PURPLE = 35;
55 int CYAN = 36;
56 int WHITE = 37;
57
58 default T esc() {
59 return apply("\033");
60 }
61
62 default T color(int c, Consumer<T> cc) {
63 csi().ints(c).apply("m");
64 cc.accept(self());
65 return reset();
66 }
67
68 default T color(int c1, int c2, Consumer<T> cc) {
69 csi().ints(c1, c2).apply("m");
70 cc.accept(self());
71
72 return reset();
73 }
74 default T fg(int color, Consumer<T> cc) {
75 return color(0, color, cc);
76 }
77
78
79 default T bold(int color, Consumer<T> cc) {
80 return color(1, color, cc);
81 }
82
83
84 default T boldAndBright(int color, Consumer<T> cc) {
85 return color(0, color + 60, cc);
86 }
87
88
89
90 default T bright(int color, Consumer<T> cc) {
91 return color(1, color + 60, cc);
92 }
93
94
95
96 default T bg(int color,Consumer<T> cc) {
97 return color(color + 10, cc);
98 }
99
100 default T csi() {
101 return esc().apply("[");
102 }
103
104 default T home(){
105 return csi().ints(0,0).apply("H");
106 }
107
108 default T csiQuery() {
109 return csi().apply("?");
110 }
111
112 default T ints(int... n) {
113 apply(String.valueOf(n[0]));
114 for (int i = 1; i < n.length; i++) {
115 apply(";" + n[i]);
116 }
117 return self();
118 }
119
120
121 default T inv() {
122 return csi().ints(7).apply("m");
123 }
124
125 default T reset() {
126 return csi().ints(0).apply("m");
127 }
128
129 default T inv(Consumer<T> c) {
130 inv();
131 c.accept(self());
132 return reset();
133 }
134
135
136
137 record Adaptor(Consumer<String> consumer) implements ANSI<Adaptor> {
138 @Override
139 public Adaptor apply(String s) {
140 consumer.accept(s);
141 return self();
142 }
143 }
144
145 default T repeat(String s, int count) {
146 return apply(s.repeat(count));
147 }
148
149 default T fill(int cols, String s) {
150 return apply(s).repeat(" ", Math.max(0, cols - s.length()));
151 }
152
153 class IMPL implements ANSI<IMPL> {
154 private final PrintStream printStream;
155
156 @Override
157 public IMPL apply(String s) {
158 printStream.append(s);
159 return self();
160 }
161
162 IMPL(PrintStream printStream) {
163 this.printStream = printStream;
164 }
165 }
166
167 static IMPL of(PrintStream printStream) {
168 return new IMPL(printStream);
169 }
170
171
172 class DotImage {
173 final private ANSI<?> ansi;
174 final public int width;
175 final public int height;
176 final private byte[] bytes;
177 int charWidth;
178 int charHeight;
179 final private char[] chars;
180 public DotImage(ANSI<?> ansi, int width, int height){
181 this.ansi = ansi;
182 this.width = width;
183 this.height = height;
184 this.bytes = new byte[width*height];
185 this.charWidth = width/2;
186 this.charHeight = height/4;
187 this.chars = new char[charWidth*charHeight];
188 }
189 public void set(int x, int y){
190 bytes[(y*width)+x]=1;//0xff;
191 }
192 void reset(int x, int y){
193 bytes[(y*width)+x]=0;
194 }
195 int i(int x, int y){
196 byte b = bytes[(y*width)+x];
197 return (int)(b<0?b+256:b);
198 }
199 /**
200 See the unicode mapping table here
201 https://images.app.goo.gl/ntxis4mKzn7GmrGb7
202 */
203 char dotchar(int bytebits){
204 // int mapped = (bytebits&0x07)|(bytebits&0x70)>>1|(bytebits&0x08)<<3|(bytebits&0x80);
205 //char brail = (char)(0x2800+(bytebits&0x07)|(bytebits&0x70)>>1|(bytebits&0x08)<<3|(bytebits&0x80));
206 return (char)(0x2800+(bytebits&0x07)|(bytebits&0x70)>>1|(bytebits&0x08)<<3|(bytebits&0x80));
207 }
208 public DotImage home(){
209
210 ansi.home();
211 return this;
212 }
213 public DotImage delay(int ms){
214 try{ Thread.sleep(ms); }catch(Throwable t){ }
215 return this;
216 }
217
218 public DotImage clean(){
219 Arrays.fill(bytes,(byte)0);
220 Arrays.fill(chars,(char)' ');
221 return this;
222 }
223 public DotImage map(){
224 for (int cx = 0; cx<charWidth; cx++){
225 for (int cy = 0; cy<charHeight; cy++){
226 int bytebits=0;
227 for (int dx=0;dx<2;dx++){
228 for (int dy=0;dy<4;dy++){
229 bytebits|=i(cx*2+dx,cy*4+dy)<<(dx*4+dy);
230 }
231 }
232 chars[cy*charWidth+cx]= dotchar(bytebits);
233 }
234 }
235 return this;
236 }
237
238 public DotImage write(){
239 ansi.apply("+");
240 for (int i=0;i<charWidth; i++){
241 ansi.apply("-");
242 }
243 ansi.apply("+\n|");
244 for (int i=0;i<chars.length; i++){
245 if (i>0 && (i%charWidth)==0){
246 ansi.apply("|\n|");
247 }
248 ansi.apply(Character.toString(chars[i]));
249 }
250 ansi.apply("|\n+");
251 for (int i=0;i<charWidth; i++){
252 ansi.apply("-");
253 }
254 ansi.apply("+\n");
255 return this;
256 }
257 }
258 default DotImage img(int width, int height){
259 return new DotImage(this, width,height);
260 }
261
262 }