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 optkl.textmodel.terminal;
26
27 import optkl.textmodel.tokens.Token;
28
29 import java.io.PrintStream;
30 import java.util.Arrays;
31 import java.util.function.Consumer;
32 import java.util.function.Function;
33
34 public interface ANSI<T extends ANSI<T>> extends Function<String, T> {
35 T apply(String o);
36
37 default T apply(Token t) {
38 return apply(t.asString());
39 }
40
41 default T self() {
42 return (T) this;
43 }
44
45 default void skip(){
46 }
47
48 int BLACK = 30;
49 int RED = 31;
50 int GREEN = 32;
51 int YELLOW = 33;
52 int BLUE = 34;
53 int PURPLE = 35;
54 int CYAN = 36;
55 int WHITE = 37;
56
57 default T esc() {
58 return apply("\033");
59 }
60
61 default T color(int c, Consumer<T> cc) {
62 csi().ints(c).apply("m");
63 cc.accept(self());
64 return reset();
65 }
66
67 default T color(int c1, int c2, Consumer<T> cc) {
68 csi().ints(c1, c2).apply("m");
69 cc.accept(self());
70
71 return reset();
72 }
73 default T fg(int color, Consumer<T> cc) {
74 return color(0, color, cc);
75 }
76
77
78 default T bold(int color, Consumer<T> cc) {
79 return color(1, color, cc);
80 }
81
82
83 default T boldAndBright(int color, Consumer<T> cc) {
84 return color(0, color + 60, cc);
85 }
86
87
88
89 default T bright(int color, Consumer<T> cc) {
90 return color(1, color + 60, cc);
91 }
92
93
94
95 default T bg(int color,Consumer<T> cc) {
96 return color(color + 10, cc);
97 }
98
99 default T csi() {
100 return esc().apply("[");
101 }
102
103 default T home(){
104 return csi().ints(0,0).apply("H");
105 }
106
107 default T csiQuery() {
108 return csi().apply("?");
109 }
110
111 default T ints(int... n) {
112 apply(String.valueOf(n[0]));
113 for (int i = 1; i < n.length; i++) {
114 apply(";" + n[i]);
115 }
116 return self();
117 }
118
119
120 default T inv() {
121 return csi().ints(7).apply("m");
122 }
123
124 default T reset() {
125 return csi().ints(0).apply("m");
126 }
127
128 default T inv(Consumer<T> c) {
129 inv();
130 c.accept(self());
131 return reset();
132 }
133
134
135
136 record Adaptor(Consumer<String> consumer) implements ANSI<Adaptor> {
137 @Override
138 public Adaptor apply(String s) {
139 consumer.accept(s);
140 return self();
141 }
142 }
143
144 default T repeat(String s, int count) {
145 return apply(s.repeat(count));
146 }
147
148 default T fill(int cols, String s) {
149 return apply(s).repeat(" ", Math.max(0, cols - s.length()));
150 }
151
152 class IMPL implements ANSI<IMPL> {
153 private final PrintStream printStream;
154
155 @Override
156 public IMPL apply(String s) {
157 printStream.append(s);
158 return self();
159 }
160
161 IMPL(PrintStream printStream) {
162 this.printStream = printStream;
163 }
164 }
165
166 static IMPL of(PrintStream printStream) {
167 return new IMPL(printStream);
168 }
169
170
171 class DotImage {
172 final private ANSI<?> ansi;
173 final public int width;
174 final public int height;
175 final private byte[] bytes;
176 int charWidth;
177 int charHeight;
178 final private char[] chars;
179 public DotImage(ANSI<?> ansi, int width, int height){
180 this.ansi = ansi;
181 this.width = width;
182 this.height = height;
183 this.bytes = new byte[width*height];
184 this.charWidth = width/2;
185 this.charHeight = height/4;
186 this.chars = new char[charWidth*charHeight];
187 }
188 public void set(int x, int y){
189 bytes[(y*width)+x]=1;//0xff;
190 }
191 void reset(int x, int y){
192 bytes[(y*width)+x]=0;
193 }
194 int i(int x, int y){
195 byte b = bytes[(y*width)+x];
196 return (int)(b<0?b+256:b);
197 }
198 /**
199 See the unicode mapping table here
200 https://images.app.goo.gl/ntxis4mKzn7GmrGb7
201 */
202 char dotchar(int bytebits){
203 // int mapped = (bytebits&0x07)|(bytebits&0x70)>>1|(bytebits&0x08)<<3|(bytebits&0x80);
204 //char brail = (char)(0x2800+(bytebits&0x07)|(bytebits&0x70)>>1|(bytebits&0x08)<<3|(bytebits&0x80));
205 return (char)(0x2800+(bytebits&0x07)|(bytebits&0x70)>>1|(bytebits&0x08)<<3|(bytebits&0x80));
206 }
207 public DotImage home(){
208
209 ansi.home();
210 return this;
211 }
212 public DotImage delay(int ms){
213 try{ Thread.sleep(ms); }catch(Throwable t){ }
214 return this;
215 }
216
217 public DotImage clean(){
218 Arrays.fill(bytes,(byte)0);
219 Arrays.fill(chars,(char)' ');
220 return this;
221 }
222 public DotImage map(){
223 for (int cx = 0; cx<charWidth; cx++){
224 for (int cy = 0; cy<charHeight; cy++){
225 int bytebits=0;
226 for (int dx=0;dx<2;dx++){
227 for (int dy=0;dy<4;dy++){
228 bytebits|=i(cx*2+dx,cy*4+dy)<<(dx*4+dy);
229 }
230 }
231 chars[cy*charWidth+cx]= dotchar(bytebits);
232 }
233 }
234 return this;
235 }
236
237 public DotImage write(){
238 ansi.apply("+");
239 for (int i=0;i<charWidth; i++){
240 ansi.apply("-");
241 }
242 ansi.apply("+\n|");
243 for (int i=0;i<chars.length; i++){
244 if (i>0 && (i%charWidth)==0){
245 ansi.apply("|\n|");
246 }
247 ansi.apply(Character.toString(chars[i]));
248 }
249 ansi.apply("|\n+");
250 for (int i=0;i<charWidth; i++){
251 ansi.apply("-");
252 }
253 ansi.apply("+\n");
254 return this;
255 }
256 }
257 default DotImage img(int width, int height){
258 return new DotImage(this, width,height);
259 }
260
261 }