1 /* coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
2 * Understanding is not required. Only obedience.
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, version 3 of the License ONLY.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 module iv
.cmdcon
.tty
/*is aliced*/;
20 public import iv
.cmdcon
;
26 // ////////////////////////////////////////////////////////////////////////// //
27 // public void glconInit (); -- call in `visibleForTheFirstTime`
28 // public void glconDraw (); -- call in `redrawOpenGlScene` (it tries hard to not modify render modes)
29 // public bool glconKeyEvent (KeyEvent event); -- returns `true` if event was eaten
30 // public bool glconCharEvent (dchar ch); -- returns `true` if event was eaten
32 // public bool conProcessQueue (); (from iv.cmdcon)
33 // call this in your main loop to process all accumulated console commands.
34 // WARNING! this is NOT thread-safe, you MUST call this in your "processing thread", and
35 // you MUST put `consoleLock()/consoleUnlock()` around the call!
37 // ////////////////////////////////////////////////////////////////////////// //
38 public bool isConsoleVisible () nothrow @trusted @nogc { pragma(inline
, true); return rConsoleVisible
; } ///
39 public bool isQuitRequested () nothrow @trusted @nogc { pragma(inline
, true); import core
.atomic
; return atomicLoad(vquitRequested
); } ///
40 public void setQuitRequested () nothrow @trusted @nogc { pragma(inline
, true); import core
.atomic
; atomicStore(vquitRequested
, true); } ///
43 // ////////////////////////////////////////////////////////////////////////// //
44 /// you may call this manually, but `ttyconEvent()` will do that for you
45 public void ttyconCharInput (char ch
) {
48 scope(exit
) consoleUnlock();
50 if (ch
== ConInputChar
.PageUp
) {
51 int lnx
= rConsoleHeight
-2;
58 if (ch
== ConInputChar
.PageDown
) {
59 if (conskiplines
> 0) {
60 int lnx
= rConsoleHeight
-2;
62 if ((conskiplines
-= lnx
) < 0) conskiplines
= 0;
68 if (ch
== ConInputChar
.LineUp
) {
74 if (ch
== ConInputChar
.LineDown
) {
75 if (conskiplines
> 0) {
82 if (ch
== ConInputChar
.Enter
) {
83 if (conskiplines
) { conskiplines
= 0; conLastChange
= 0; }
84 auto s
= conInputBuffer
;
87 conInputBufferClear(true); // add to history
93 //if (ch == '`' && conInputBuffer.length == 0) { concmd("r_console ona"); return; }
95 auto pcc
= conInputLastChange();
97 if (pcc
!= conInputLastChange()) conLastChange
= 0;
101 // ////////////////////////////////////////////////////////////////////////// //
102 __gshared
char rPromptChar
= '>';
103 __gshared
bool rConsoleVisible
= false;
104 __gshared
int rConsoleHeight
= 10;
105 __gshared
uint rConTextColor
= 0x00ff00; // rgb
106 __gshared
uint rConCursorColor
= 0xff7f00; // rgb
107 __gshared
uint rConInputColor
= 0xffff00; // rgb
108 __gshared
uint rConPromptColor
= 0xffffff; // rgb
109 __gshared
uint rConBackColor
= 0x000080; // rgb
110 shared bool vquitRequested
= false;
111 __gshared
char* conOutBuf
= null; // tty buffer
112 __gshared
uint conOBPos
, conOBSize
;
113 __gshared
int conskiplines
= 0;
116 void conOReset () nothrow @trusted @nogc { conOBPos
= 0; }
118 void conOFlush () nothrow @trusted @nogc { if (conOBPos
) ttyRawWrite(conOutBuf
[0..conOBPos
]); conOBPos
= 0; }
120 void conOPut (const(char)[] s
...) nothrow @trusted @nogc {
121 if (s
.length
== 0) return;
122 if (s
.length
> 1024*1024) assert(0, "wtf?!");
123 if (conOBPos
+s
.length
>= conOBSize
) {
124 import core
.stdc
.stdlib
: realloc
;
125 auto nsz
= cast(uint)(conOBPos
+s
.length
+8192);
126 //if (nsz < conOBSize) assert(0, "wtf?!");
127 auto nb
= realloc(conOutBuf
, nsz
);
128 if (nb
is null) assert(0, "wtf?!");
129 conOutBuf
= cast(char*)nb
;
131 conOutBuf
[conOBPos
..conOBPos
+s
.length
] = s
[];
132 conOBPos
+= cast(uint)s
.length
;
135 void conOInt(T
) (T n
) nothrow @trusted @nogc if (__traits(isIntegral
, T
) && !is(T
== char) && !is(T
== wchar) && !is(T
== dchar) && !is(T
== bool) && !is(T
== enum)) {
136 import core
.stdc
.stdio
: snprintf
;
138 static if (__traits(isUnsigned
, T
)) {
139 static if (T
.sizeof
> 4) {
140 auto len
= snprintf(buf
.ptr
, buf
.length
, "%llu", n
);
142 auto len
= snprintf(buf
.ptr
, buf
.length
, "%u", cast(uint)n
);
145 static if (T
.sizeof
> 4) {
146 auto len
= snprintf(buf
.ptr
, buf
.length
, "%lld", n
);
148 auto len
= snprintf(buf
.ptr
, buf
.length
, "%d", cast(int)n
);
151 if (len
> 0) conOPut(buf
[0..len
]);
154 void conOColorFG (uint c
) nothrow @trusted @nogc {
155 conOPut("\x1b[38;5;");
156 conOInt(ttyRgb2Color((c
>>16)&0xff, (c
>>8)&0xff, c
&0xff));
160 void conOColorBG (uint c
) nothrow @trusted @nogc {
161 conOPut("\x1b[48;5;");
162 conOInt(ttyRgb2Color((c
>>16)&0xff, (c
>>8)&0xff, c
&0xff));
167 // initialize glcmdcon variables and commands, sets screen size and scale
168 // NOT THREAD-SAFE! also, should be called only once.
169 private void initConsole () {
170 conRegVar
!rConsoleVisible("r_console", "console visibility", ConVarAttr
.Archive
);
171 conRegVar
!rConsoleHeight(2, 4096, "r_conheight", "console height", ConVarAttr
.Archive
);
172 conRegVar
!rConTextColor("r_contextcolor", "console log text color, 0xrrggbb", ConVarAttr
.Archive
, ConVarAttr
.Hex
);
173 conRegVar
!rConCursorColor("r_concursorcolor", "console cursor color, 0xrrggbb", ConVarAttr
.Archive
, ConVarAttr
.Hex
);
174 conRegVar
!rConInputColor("r_coninputcolor", "console input color, 0xrrggbb", ConVarAttr
.Archive
, ConVarAttr
.Hex
);
175 conRegVar
!rConPromptColor("r_conpromptcolor", "console prompt color, 0xrrggbb", ConVarAttr
.Archive
, ConVarAttr
.Hex
);
176 conRegVar
!rConBackColor("r_conbackcolor", "console background color, 0xrrggbb", ConVarAttr
.Archive
, ConVarAttr
.Hex
);
177 conRegVar
!rPromptChar("r_conpromptchar", "console prompt character", ConVarAttr
.Archive
);
180 atomicStore(vquitRequested
, true);
184 shared static this () { initConsole(); }
187 // ////////////////////////////////////////////////////////////////////////// //
188 __gshared
bool lastWasVisible
= false;
189 __gshared
int lastConHgt
= 0;
190 //__gshared ConDump conoldcdump;
193 /// initialize ttycmdcon. it is ok to call it repeatedly.
195 public void ttyconInit () {
200 /// render console (if it is visible)
201 public void ttyconDraw () {
203 scope(exit
) consoleUnlock();
207 if (w
< 4 || h
< 2) return;
209 if (!rConsoleVisible
) {
210 //conDump = conoldcdump;
211 if (lastWasVisible
) {
215 scope(exit
) conOPut("\x1b8");
216 conOPut("\x1b[1;1H\x1b[0m");
217 if (lastConHgt
> h
) lastConHgt
= h
;
218 while (lastConHgt
-- > 0) {
219 if (lastConHgt
) conOPut("\r\x1b[B");
228 //if (!lastWasVisible || conDump != ConDump.none) conoldcdump = conDump;
229 //conDump = ConDump.none;
231 lastWasVisible
= true;
232 lastConHgt
= rConsoleHeight
;
233 conLastChange
= cbufLastChange
+1;
241 // ////////////////////////////////////////////////////////////////////////// //
242 __gshared
uint conLastChange
= 0;
243 __gshared
uint conLastIBChange
= 0;
244 __gshared
int prevCurX
= -1;
245 __gshared
int prevIXOfs
= 0;
248 bool renderConsole () nothrow @trusted @nogc {
249 if (conLastChange
== cbufLastChange
&& conLastIBChange
== conInputLastChange
) return false;
251 immutable sw
= ttyWidth
, sh
= ttyHeight
;
252 int skipLines
= conskiplines
;
253 conLastChange
= cbufLastChange
;
254 conLastIBChange
= conInputLastChange
;
256 int y
= rConsoleHeight
;
261 scope(exit
) conOPut("\x1b8");
265 conOPut(";1H\x1b[0m");
267 conOColorBG(rConBackColor
);
269 auto concli
= conInputBuffer
;
270 int conclilen
= cast(int)concli
.length
;
271 int concurx
= conInputBufferCurX();
275 int charsInLine
= sw
-1; // reserve room for cursor
276 if (rPromptChar
>= ' ') --charsInLine
;
277 if (charsInLine
< 2) charsInLine
= 2; // just in case
278 int stpos
= prevIXOfs
;
279 if (concurx
== conclilen
) {
280 stpos
= conclilen
-charsInLine
;
282 } else if (prevCurX
!= concurx
) {
283 // cursor position changed, fix x offset
284 if (concurx
<= prevIXOfs
) {
286 } else if (concurx
-prevIXOfs
>= charsInLine
-1) {
287 stpos
= concurx
-charsInLine
+1;
290 if (stpos
< 0) stpos
= 0;
294 if (rPromptChar
>= ' ') {
295 conOColorFG(rConPromptColor
);
296 conOPut(rPromptChar
);
298 conOColorFG(rConInputColor
);
299 foreach (int pos
; stpos
..stpos
+charsInLine
+1) {
300 if (pos
== concurx
) {
301 conOColorBG(rConCursorColor
);
304 conOColorBG(rConBackColor
);
305 conOColorFG(rConInputColor
);
307 if (pos
>= 0 && pos
< conclilen
) conOPut(concli
.ptr
[pos
]);
314 conOColorFG(rConTextColor
);
315 conOPut("\x1b[K\r\x1b[A");
317 void putLine(T
) (auto ref T line
, usize pos
=0) {
318 if (y
< 1 || pos
>= line
.length
) return;
319 int w
= 0, lastWordW
= -1;
320 usize sp
= pos
, lastWordEnd
= 0;
321 while (sp
< line
.length
) {
322 char ch
= line
[sp
++];
323 // remember last word position
324 if (/*lastWordW < 0 &&*/ (ch
== ' ' || ch
== '\t')) {
325 lastWordEnd
= sp
-1; // space will be put on next line (rough indication of line wrapping)
331 // current char is non-space, and, previous char is non-space, and we have a complete word?
332 if (lastWordW
> 0 && ch
!= ' ' && ch
!= '\t' && sp
> pos
&& line
[sp
-1] != ' ' && line
[sp
-1] != '\t') {
333 // yes, split on last word boundary
339 if (sp
< line
.length
) putLine(line
, sp
); // recursive put tail
341 if (skipLines
-- <= 0) {
342 while (pos
< sp
) conOPut(line
[pos
++]);
344 if (y
>= 1) conOPut("\x1b[K\r\x1b[A");
350 scope(exit
) consoleWriteUnlock();
351 foreach (/*auto*/ line
; conbufLinesRev
) {
357 conOPut("\x1b[K\r\x1b[A");
365 // ////////////////////////////////////////////////////////////////////////// //
366 public __gshared
char ttyconShowKey
= '`'; /// this key will be eaten
369 /// process tty event. returns `true` if event was eaten.
370 public bool ttyconEvent (TtyEvent event
) {
371 if (!rConsoleVisible
) {
372 if (event
.key
== TtyEvent
.Key
.Char
&& event
.ch
== ttyconShowKey
) {
373 concmd("r_console 1");
379 // console is visible
380 if (event
.key
== TtyEvent
.Key
.Char
&& event
.ch
== ttyconShowKey
) {
381 if (conInputBuffer
.length
== 0) concmd("r_console 0");
385 if (event
.key
== TtyEvent
.Key
.Char
) {
386 if (event
== "C-W") { ttyconCharInput(ConInputChar
.CtrlW
); return true; }
387 if (event
== "C-Y") { ttyconCharInput(ConInputChar
.CtrlY
); return true; }
388 if (event
.ch
>= ' ' && event
.ch
< 127) ttyconCharInput(cast(char)event
.ch
);
392 if (event
.key
== TtyEvent
.Key
.Escape
) { concmd("r_console 0"); return true; }
394 case TtyEvent
.Key
.Up
:
395 //if (event.modifierState&ModifierState.alt) ttyconCharInput(ConInputChar.LineUp); else ttyconCharInput(ConInputChar.Up);
396 ttyconCharInput(ConInputChar
.Up
);
398 case TtyEvent
.Key
.Down
:
399 //if (event.modifierState&ModifierState.alt) ttyconCharInput(ConInputChar.LineDown); else ttyconCharInput(ConInputChar.Down);
400 ttyconCharInput(ConInputChar
.Down
);
402 case TtyEvent
.Key
.Left
: ttyconCharInput(ConInputChar
.Left
); return true;
403 case TtyEvent
.Key
.Right
: ttyconCharInput(ConInputChar
.Right
); return true;
404 case TtyEvent
.Key
.Home
: ttyconCharInput(ConInputChar
.Home
); return true;
405 case TtyEvent
.Key
.End
: ttyconCharInput(ConInputChar
.End
); return true;
406 case TtyEvent
.Key
.PageUp
:
407 //if (event.modifierState&ModifierState.alt) ttyconCharInput(ConInputChar.LineUp); else ttyconCharInput(ConInputChar.PageUp);
408 ttyconCharInput(ConInputChar
.PageUp
);
410 case TtyEvent
.Key
.PageDown
:
411 //if (event.modifierState&ModifierState.alt) ttyconCharInput(ConInputChar.LineDown); else ttyconCharInput(ConInputChar.PageDown);
412 ttyconCharInput(ConInputChar
.PageDown
);
414 case TtyEvent
.Key
.Backspace
: ttyconCharInput(ConInputChar
.Backspace
); return true;
415 case TtyEvent
.Key
.Tab
: ttyconCharInput(ConInputChar
.Tab
); return true;
416 case TtyEvent
.Key
.Enter
: ttyconCharInput(ConInputChar
.Enter
); return true;
417 case TtyEvent
.Key
.Delete
: ttyconCharInput(ConInputChar
.Delete
); return true;
418 case TtyEvent
.Key
.Insert
: ttyconCharInput(ConInputChar
.Insert
); return true;
419 //case TtyEvent.Key.W: if (event.modifierState&ModifierState.ctrl) ttyconCharInput(ConInputChar.CtrlW); return true;
420 //case TtyEvent.Key.Y: if (event.modifierState&ModifierState.ctrl) ttyconCharInput(ConInputChar.CtrlY); return true;