@@ -8,8 +8,8 @@ neercs_SOURCES = \ | |||||
\ | \ | ||||
$(old_sources) \ | $(old_sources) \ | ||||
\ | \ | ||||
term/term.cpp term/term.h \ | |||||
term/pty.cpp term/pty.h \ | |||||
term/term.h term/term.cpp term/ansi.cpp \ | |||||
term/pty.h term/pty.cpp \ | |||||
\ | \ | ||||
video/render.cpp video/render.h \ | video/render.cpp video/render.h \ | ||||
video/text-render.cpp video/text-render.h \ | video/text-render.cpp video/text-render.h \ | ||||
@@ -70,6 +70,7 @@ | |||||
<ClCompile Include="old\server.c" /> | <ClCompile Include="old\server.c" /> | ||||
<ClCompile Include="old\widgets.c" /> | <ClCompile Include="old\widgets.c" /> | ||||
<ClCompile Include="old\wm.cpp" /> | <ClCompile Include="old\wm.cpp" /> | ||||
<ClInclude Include="term\ansi.cpp" /> | |||||
<ClInclude Include="term\pty.cpp" /> | <ClInclude Include="term\pty.cpp" /> | ||||
<ClInclude Include="term\term.cpp" /> | <ClInclude Include="term\term.cpp" /> | ||||
<ClCompile Include="video\render.cpp" /> | <ClCompile Include="video\render.cpp" /> | ||||
@@ -74,6 +74,9 @@ | |||||
<ClCompile Include="old\python\py_module.c"> | <ClCompile Include="old\python\py_module.c"> | ||||
<Filter>old\python</Filter> | <Filter>old\python</Filter> | ||||
</ClCompile> | </ClCompile> | ||||
<ClCompile Include="term\ansi.cpp"> | |||||
<Filter>term</Filter> | |||||
</ClCompile> | |||||
<ClCompile Include="term\pty.cpp"> | <ClCompile Include="term\pty.cpp"> | ||||
<Filter>term</Filter> | <Filter>term</Filter> | ||||
</ClCompile> | </ClCompile> | ||||
@@ -42,14 +42,19 @@ using namespace lol; | |||||
#include "neercs.h" | #include "neercs.h" | ||||
Pty::Pty(ivec2 size) | Pty::Pty(ivec2 size) | ||||
: m_size(size) | |||||
: m_fd(-1), | |||||
m_pid(-1), | |||||
m_size(size) | |||||
{ | { | ||||
; | ; | ||||
} | } | ||||
Pty::~Pty() | Pty::~Pty() | ||||
{ | { | ||||
if (m_fd >= 0) | |||||
{ | |||||
close((int)m_fd); | |||||
} | |||||
} | } | ||||
void Pty::Run(char const *command) | void Pty::Run(char const *command) | ||||
@@ -58,8 +63,8 @@ void Pty::Run(char const *command) | |||||
int fd; | int fd; | ||||
pid_t pid; | pid_t pid; | ||||
m_pid = 0; | |||||
m_fd = 0; | |||||
m_pid = -1; | |||||
m_fd = -1; | |||||
pid = forkpty(&fd, NULL, NULL, NULL); | pid = forkpty(&fd, NULL, NULL, NULL); | ||||
if (pid < 0) | if (pid < 0) | ||||
@@ -94,6 +99,45 @@ void Pty::Run(char const *command) | |||||
#endif | #endif | ||||
} | } | ||||
size_t Pty::ReadData(char *data, size_t maxlen) | |||||
{ | |||||
fd_set fdset; | |||||
int maxfd = -1; | |||||
FD_ZERO(&fdset); | |||||
if (m_fd >= 0) | |||||
{ | |||||
FD_SET((int)m_fd, &fdset); | |||||
maxfd = std::max(maxfd, (int)m_fd); | |||||
} | |||||
if (maxfd >= 0) | |||||
{ | |||||
struct timeval tv; | |||||
tv.tv_sec = 0; | |||||
tv.tv_usec = 50000; | |||||
int ret = select(maxfd + 1, &fdset, NULL, NULL, &tv); | |||||
if (ret < 0) | |||||
{ | |||||
Log::Error("cannot read from PTY\n"); | |||||
return 0; | |||||
} | |||||
if (ret) | |||||
{ | |||||
if (FD_ISSET((int)m_fd, &fdset)) | |||||
{ | |||||
ssize_t nr = read((int)m_fd, data, maxlen); | |||||
if (nr >= 0) | |||||
return nr; | |||||
} | |||||
} | |||||
} | |||||
return 0; | |||||
} | |||||
void Pty::SetWindowSize(ivec2 size) | void Pty::SetWindowSize(ivec2 size) | ||||
{ | { | ||||
#if defined HAVE_PTY_H || defined HAVE_UTIL_H || defined HAVE_LIBUTIL_H | #if defined HAVE_PTY_H || defined HAVE_UTIL_H || defined HAVE_LIBUTIL_H | ||||
@@ -12,11 +12,12 @@ public: | |||||
~Pty(); | ~Pty(); | ||||
void Run(char const *command); | void Run(char const *command); | ||||
size_t ReadData(char *data, size_t maxlen); | |||||
void SetWindowSize(ivec2 size); | void SetWindowSize(ivec2 size); | ||||
private: | private: | ||||
uint64_t m_fd; | |||||
uint64_t m_pid; | |||||
int64_t m_fd; | |||||
int64_t m_pid; | |||||
char const *m_argv[2]; | char const *m_argv[2]; | ||||
ivec2 m_size; | ivec2 m_size; | ||||
}; | }; | ||||
@@ -7,7 +7,6 @@ | |||||
#endif | #endif | ||||
#include "core.h" | #include "core.h" | ||||
#include "lolgl.h" | |||||
using namespace std; | using namespace std; | ||||
using namespace lol; | using namespace lol; | ||||
@@ -19,6 +18,14 @@ Term::Term(ivec2 size) | |||||
: m_time(0.f) | : m_time(0.f) | ||||
{ | { | ||||
m_caca = caca_create_canvas(size.x, size.y); | m_caca = caca_create_canvas(size.x, size.y); | ||||
#if defined HAVE_PTY_H || defined HAVE_UTIL_H || defined HAVE_LIBUTIL_H | |||||
m_pty = new Pty(size); | |||||
char const *shell = getenv("SHELL"); | |||||
if (!shell) | |||||
shell = "/bin/sh"; | |||||
m_pty->Run(shell); | |||||
#endif | |||||
} | } | ||||
void Term::TickGame(float seconds) | void Term::TickGame(float seconds) | ||||
@@ -29,7 +36,15 @@ void Term::TickGame(float seconds) | |||||
/* This is the real terminal code */ | /* This is the real terminal code */ | ||||
/* XXX: for now we draw fancy shit */ | /* XXX: for now we draw fancy shit */ | ||||
m_time += seconds; | m_time += seconds; | ||||
DrawFancyShit(); | |||||
for (;;) | |||||
{ | |||||
char buf[BUFSIZ]; | |||||
size_t bytes = m_pty->ReadData(buf, BUFSIZ); | |||||
if (bytes <= 0) | |||||
break; | |||||
ReadAnsi(buf, bytes); | |||||
} | |||||
#else | #else | ||||
/* Unsupported platform - draw some fancy shit instead */ | /* Unsupported platform - draw some fancy shit instead */ | ||||
m_time += seconds; | m_time += seconds; | ||||
@@ -44,6 +59,10 @@ void Term::TickDraw(float seconds) | |||||
Term::~Term() | Term::~Term() | ||||
{ | { | ||||
#if defined HAVE_PTY_H || defined HAVE_UTIL_H || defined HAVE_LIBUTIL_H | |||||
delete m_pty; | |||||
#endif | |||||
caca_free_canvas(m_caca); | |||||
} | } | ||||
/* | /* | ||||
@@ -7,6 +7,80 @@ | |||||
#include "term/pty.h" | #include "term/pty.h" | ||||
struct Iso2022Conversion | |||||
{ | |||||
Iso2022Conversion() { Reset(); } | |||||
void Reset(); | |||||
/* cs = coding system/coding method: */ | |||||
/* (with standard return) */ | |||||
/* '@' = ISO-2022, */ | |||||
/* 'G' = UTF-8 without implementation level, */ | |||||
/* '8' = UTF-8 (Linux console and imitators), */ | |||||
/* and many others that are rarely used; */ | |||||
/* (without standard return) */ | |||||
/* '/G' = UTF-8 Level 1, */ | |||||
/* '/H' = UTF-8 Level 2, */ | |||||
/* '/I' = UTF-8 Level 3, */ | |||||
/* and many others that are rarely used */ | |||||
uint32_t cs; | |||||
/* ctrl8bit = allow 8-bit controls */ | |||||
uint8_t ctrl8bit; | |||||
/* cn[0] = C0 control charset (0x00 ... 0x1f): | |||||
* '@' = ISO 646, | |||||
* '~' = empty, | |||||
* and many others that are rarely used */ | |||||
/* cn[1] = C1 control charset (0x80 ... 0x9f): | |||||
* 'C' = ISO 6429-1983, | |||||
* '~' = empty, | |||||
* and many others that are rarely used */ | |||||
uint32_t cn[2]; | |||||
/* glr[0] = GL graphic charset (94-char. 0x21 ... 0x7e, | |||||
* 94x94-char. 0x21/0x21 ... 0x7e/0x7e), | |||||
* and | |||||
* glr[1] = GR graphic charset (94-char. 0xa1 ... 0xfe, | |||||
* 96-char. 0xa0 ... 0xff, | |||||
* 94x94-char. 0xa1/0xa1 ... 0xfe/0xfe, | |||||
* 96x96-char. 0xa0/0xa0 ... 0xff/0xff): | |||||
* 0 = G0, 1 = G1, 2 = G2, 3 = G3 */ | |||||
uint8_t glr[2]; | |||||
/* gn[i] = G0/G1/G2/G3 graphic charset state: | |||||
* (94-char. sets) | |||||
* '0' = DEC ACS (VT100 and imitators), | |||||
* 'B' = US-ASCII, | |||||
* and many others that are rarely used for e.g. various national ASCII variations; | |||||
* (96-char. sets) | |||||
* '.A' = ISO 8859-1 "Latin 1" GR, | |||||
* '.~' = empty 96-char. set, | |||||
* and many others that are rarely used for e.g. ISO 8859-n GR; | |||||
* (double-byte 94x94-charsets) | |||||
* '$@' = Japanese Character Set ("old JIS") (JIS C 6226:1978), | |||||
* '$A' = Chinese Character Set (GB 2312), | |||||
* '$B' = Japanese Character Set (JIS X0208/JIS C 6226:1983), | |||||
* '$C' = Korean Graphic Character Set (KSC 5601:1987), | |||||
* '$D' = Supplementary Japanese Graphic Character Set (JIS X0212), | |||||
* '$E' = CCITT Chinese Set (GB 2312 + GB 8565), | |||||
* '$G' = CNS 11643 plane 1, | |||||
* '$H' = CNS 11643 plane 2, | |||||
* '$I' = CNS 11643 plane 3, | |||||
* '$J' = CNS 11643 plane 4, | |||||
* '$K' = CNS 11643 plane 5, | |||||
* '$L' = CNS 11643 plane 6, | |||||
* '$M' = CNS 11643 plane 7, | |||||
* '$O' = JIS X 0213 plane 1, | |||||
* '$P' = JIS X 0213 plane 2, | |||||
* '$Q' = JIS X 0213-2004 Plane 1, | |||||
* and many others that are rarely used for e.g. traditional | |||||
* ideographic Vietnamese and BlissSymbolics; | |||||
* (double-byte 96x96-charsets) | |||||
* none standardized or in use on terminals AFAIK (Mule does use | |||||
* some internally) | |||||
*/ | |||||
uint32_t gn[4]; | |||||
/* ss = single-shift state: 0 = GL, 2 = G2, 3 = G3 */ | |||||
uint8_t ss; | |||||
}; | |||||
class Term : public Entity | class Term : public Entity | ||||
{ | { | ||||
public: | public: | ||||
@@ -20,11 +94,35 @@ protected: | |||||
virtual void TickGame(float seconds); | virtual void TickGame(float seconds); | ||||
virtual void TickDraw(float seconds); | virtual void TickDraw(float seconds); | ||||
private: | |||||
/* Terminal emulation main entry point */ | |||||
size_t ReadAnsi(void const *data, size_t size); | |||||
size_t SendAnsi(char const *str); | |||||
/* Utility functions for terminal emulation */ | |||||
void ReadGrcm(unsigned int argc, unsigned int const *argv); | |||||
inline int ReadChar(unsigned char c, int *x, int *y); | |||||
inline int ReadDuplet(unsigned char const *buffer, unsigned int *skip, | |||||
int top, int bottom, int width, int height); | |||||
private: | private: | ||||
Pty *m_pty; | Pty *m_pty; | ||||
caca_canvas_t *m_caca; | caca_canvas_t *m_caca; | ||||
ivec2 m_size; | ivec2 m_size; | ||||
/* Terminal attributes */ | |||||
char *m_title; | |||||
int m_bell, m_init, m_report_mouse; | |||||
int m_changed; /* content was updated */ | |||||
Iso2022Conversion m_conv_state; /* charset mess */ | |||||
uint8_t m_fg, m_bg; /* ANSI-context fg/bg */ | |||||
uint8_t m_dfg, m_dbg; /* Default fg/bg */ | |||||
uint8_t m_bold, m_blink, m_italics, m_negative, m_concealed, m_underline; | |||||
uint8_t m_faint, m_strike, m_proportional; /* unsupported */ | |||||
uint32_t m_clearattr; | |||||
/* Mostly for fancy shit */ | /* Mostly for fancy shit */ | ||||
void DrawFancyShit(); | void DrawFancyShit(); | ||||
float m_time; | float m_time; | ||||