vt.c 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961
  1. /*
  2. * Copyright © 2004 Bruno T. C. de Oliveira
  3. * Copyright © 2006 Pierre Habouzit
  4. * Copyright © 2008-2016 Marc André Tanner
  5. *
  6. * Permission to use, copy, modify, and distribute this software for any
  7. * purpose with or without fee is hereby granted, provided that the above
  8. * copyright notice and this permission notice appear in all copies.
  9. *
  10. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  11. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  12. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  13. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  14. * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
  15. * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
  16. * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  17. */
  18. #include <stdlib.h>
  19. #include <stdint.h>
  20. #include <unistd.h>
  21. #include <ctype.h>
  22. #include <errno.h>
  23. #include <fcntl.h>
  24. #include <langinfo.h>
  25. #include <limits.h>
  26. #include <signal.h>
  27. #include <stdio.h>
  28. #include <stdlib.h>
  29. #include <stddef.h>
  30. #include <string.h>
  31. #include <sys/ioctl.h>
  32. #include <sys/types.h>
  33. #include <termios.h>
  34. #include <wchar.h>
  35. #if defined(__linux__) || defined(__CYGWIN__)
  36. # include <pty.h>
  37. #elif defined(__FreeBSD__) || defined(__DragonFly__)
  38. # include <libutil.h>
  39. #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
  40. # include <util.h>
  41. #endif
  42. #include "vt.h"
  43. #ifdef _AIX
  44. # include "forkpty-aix.c"
  45. #elif defined __sun
  46. # include "forkpty-sunos.c"
  47. #endif
  48. #ifndef NCURSES_ATTR_SHIFT
  49. # define NCURSES_ATTR_SHIFT 8
  50. #endif
  51. #ifndef NCURSES_ACS
  52. # ifdef PDCURSES
  53. # define NCURSES_ACS(c) (acs_map[(unsigned char)(c)])
  54. # else /* BSD curses */
  55. # define NCURSES_ACS(c) (_acs_map[(unsigned char)(c)])
  56. # endif
  57. #endif
  58. #ifdef NCURSES_VERSION
  59. # ifndef NCURSES_EXT_COLORS
  60. # define NCURSES_EXT_COLORS 0
  61. # endif
  62. # if !NCURSES_EXT_COLORS
  63. # define MAX_COLOR_PAIRS 256
  64. # endif
  65. #endif
  66. #ifndef MAX_COLOR_PAIRS
  67. # define MAX_COLOR_PAIRS COLOR_PAIRS
  68. #endif
  69. #if defined _AIX && defined CTRL
  70. # undef CTRL
  71. #endif
  72. #ifndef CTRL
  73. # define CTRL(k) ((k) & 0x1F)
  74. #endif
  75. #define IS_CONTROL(ch) !((ch) & 0xffffff60UL)
  76. #define MIN(x, y) ((x) < (y) ? (x) : (y))
  77. #define LENGTH(arr) (sizeof(arr) / sizeof((arr)[0]))
  78. static bool is_utf8, has_default_colors;
  79. static short color_pairs_reserved, color_pairs_max, color_pair_current;
  80. static short *color2palette, default_fg, default_bg;
  81. static char vt_term[32];
  82. typedef struct {
  83. wchar_t text;
  84. attr_t attr;
  85. short fg;
  86. short bg;
  87. } Cell;
  88. typedef struct {
  89. Cell *cells;
  90. unsigned dirty:1;
  91. } Row;
  92. /* Buffer holding the current terminal window content (as an array) as well
  93. * as the scroll back buffer content (as a circular/ring buffer).
  94. *
  95. * If new content is added to terminal the view port slides down and the
  96. * previously top most line is moved into the scroll back buffer at postion
  97. * scroll_index. This index will eventually wrap around and thus overwrite
  98. * the oldest lines.
  99. *
  100. * In the scenerio below a scroll up has been performed. That is 'scroll_above'
  101. * lines still lie above the current view port. Further scrolling up will show
  102. * them. Similarly 'scroll_below' is the amount of lines below the current
  103. * viewport.
  104. *
  105. * The function buffer_boundary sets the row pointers to the start/end range
  106. * of the section delimiting the region before/after the viewport. The functions
  107. * buffer_row_{first,last} return the first/last logical row. And
  108. * buffer_row_{next,prev} allows to iterate over the logical lines in either
  109. * direction.
  110. *
  111. * scroll back buffer
  112. *
  113. * scroll_buf->+----------------+-----+
  114. * | | | ^ \
  115. * | before | | | |
  116. * current terminal content | viewport | | | |
  117. * | | | |
  118. * +----------------+-----+\ | | | s > scroll_above
  119. * ^ | | i | \ | | i | c |
  120. * | | | n | \ | | n | r |
  121. * | | v | \ | | v | o |
  122. * r | | i | \ | | i | l /
  123. * o | viewport | s | >|<- scroll_index | s | l \
  124. * w | | i | / | | i | |
  125. * s | | b | / | after | b | s > scroll_below
  126. * | | l | / | viewport | l | i |
  127. * v | | e | / | | e | z /
  128. * +----------------+-----+/ | unused | | e
  129. * <- maxcols -> | scroll back | |
  130. * <- cols -> | buffer | | |
  131. * | | | |
  132. * | | | v
  133. * roll_buf + scroll_size->+----------------+-----+
  134. * <- maxcols ->
  135. * <- cols ->
  136. */
  137. typedef struct {
  138. Row *lines; /* array of Row pointers of size 'rows' */
  139. Row *curs_row; /* row on which the cursor currently resides */
  140. Row *scroll_buf; /* a ring buffer holding the scroll back content */
  141. Row *scroll_top; /* row in lines where scrolling region starts */
  142. Row *scroll_bot; /* row in lines where scrolling region ends */
  143. bool *tabs; /* a boolean flag for each column whether it is a tab */
  144. int scroll_size; /* maximal capacity of scroll back buffer (in lines) */
  145. int scroll_index; /* current index into the ring buffer */
  146. int scroll_above; /* number of lines above current viewport */
  147. int scroll_below; /* number of lines below current viewport */
  148. int rows, cols; /* current dimension of buffer */
  149. int maxcols; /* allocated cells (maximal cols over time) */
  150. attr_t curattrs, savattrs; /* current and saved attributes for cells */
  151. int curs_col; /* current cursor column (zero based) */
  152. int curs_srow, curs_scol; /* saved cursor row/colmn (zero based) */
  153. short curfg, curbg; /* current fore and background colors */
  154. short savfg, savbg; /* saved colors */
  155. } Buffer;
  156. struct Vt {
  157. Buffer buffer_normal; /* normal screen buffer */
  158. Buffer buffer_alternate; /* alternate screen buffer */
  159. Buffer *buffer; /* currently active buffer (one of the above) */
  160. attr_t defattrs; /* attributes to use for normal/empty cells */
  161. short deffg, defbg; /* colors to use for back normal/empty cells (white/black) */
  162. int pty; /* master side pty file descriptor */
  163. pid_t pid; /* process id of the process running in this vt */
  164. /* flags */
  165. unsigned seen_input:1;
  166. unsigned insert:1;
  167. unsigned escaped:1;
  168. unsigned curshid:1;
  169. unsigned curskeymode:1;
  170. unsigned bell:1;
  171. unsigned relposmode:1;
  172. unsigned mousetrack:1;
  173. unsigned graphmode:1;
  174. unsigned savgraphmode:1;
  175. bool charsets[2];
  176. /* buffers and parsing state */
  177. char rbuf[BUFSIZ];
  178. char ebuf[BUFSIZ];
  179. unsigned int rlen, elen;
  180. int srow, scol; /* last known offset to display start row, start column */
  181. char title[256]; /* xterm style window title */
  182. vt_title_handler_t title_handler; /* hook which is called when title changes */
  183. vt_urgent_handler_t urgent_handler; /* hook which is called upon bell */
  184. void *data; /* user supplied data */
  185. };
  186. static const char *keytable[KEY_MAX+1] = {
  187. [KEY_ENTER] = "\r",
  188. ['\n'] = "\n",
  189. /* for the arrow keys the CSI / SS3 sequences are not stored here
  190. * because they depend on the current cursor terminal mode
  191. */
  192. [KEY_UP] = "A",
  193. [KEY_DOWN] = "B",
  194. [KEY_RIGHT] = "C",
  195. [KEY_LEFT] = "D",
  196. #ifdef KEY_SUP
  197. [KEY_SUP] = "\e[1;2A",
  198. #endif
  199. #ifdef KEY_SDOWN
  200. [KEY_SDOWN] = "\e[1;2B",
  201. #endif
  202. [KEY_SRIGHT] = "\e[1;2C",
  203. [KEY_SLEFT] = "\e[1;2D",
  204. [KEY_BACKSPACE] = "\177",
  205. [KEY_IC] = "\e[2~",
  206. [KEY_DC] = "\e[3~",
  207. [KEY_PPAGE] = "\e[5~",
  208. [KEY_NPAGE] = "\e[6~",
  209. [KEY_HOME] = "\e[7~",
  210. [KEY_END] = "\e[8~",
  211. [KEY_BTAB] = "\e[Z",
  212. [KEY_SUSPEND] = "\x1A", /* Ctrl+Z gets mapped to this */
  213. [KEY_F(1)] = "\e[11~",
  214. [KEY_F(2)] = "\e[12~",
  215. [KEY_F(3)] = "\e[13~",
  216. [KEY_F(4)] = "\e[14~",
  217. [KEY_F(5)] = "\e[15~",
  218. [KEY_F(6)] = "\e[17~",
  219. [KEY_F(7)] = "\e[18~",
  220. [KEY_F(8)] = "\e[19~",
  221. [KEY_F(9)] = "\e[20~",
  222. [KEY_F(10)] = "\e[21~",
  223. [KEY_F(11)] = "\e[23~",
  224. [KEY_F(12)] = "\e[24~",
  225. [KEY_F(13)] = "\e[23~",
  226. [KEY_F(14)] = "\e[24~",
  227. [KEY_F(15)] = "\e[25~",
  228. [KEY_F(16)] = "\e[26~",
  229. [KEY_F(17)] = "\e[28~",
  230. [KEY_F(18)] = "\e[29~",
  231. [KEY_F(19)] = "\e[31~",
  232. [KEY_F(20)] = "\e[32~",
  233. [KEY_F(21)] = "\e[33~",
  234. [KEY_F(22)] = "\e[34~",
  235. [KEY_RESIZE] = "",
  236. #ifdef KEY_EVENT
  237. [KEY_EVENT] = "",
  238. #endif
  239. };
  240. static void puttab(Vt *t, int count);
  241. static void process_nonprinting(Vt *t, wchar_t wc);
  242. static void send_curs(Vt *t);
  243. __attribute__ ((const))
  244. static attr_t build_attrs(attr_t curattrs)
  245. {
  246. return ((curattrs & ~A_COLOR) | COLOR_PAIR(curattrs & 0xff))
  247. >> NCURSES_ATTR_SHIFT;
  248. }
  249. static void row_set(Row *row, int start, int len, Buffer *t)
  250. {
  251. Cell cell = {
  252. .text = L'\0',
  253. .attr = t ? build_attrs(t->curattrs) : 0,
  254. .fg = t ? t->curfg : -1,
  255. .bg = t ? t->curbg : -1,
  256. };
  257. for (int i = start; i < len + start; i++)
  258. row->cells[i] = cell;
  259. row->dirty = true;
  260. }
  261. static void row_roll(Row *start, Row *end, int count)
  262. {
  263. int n = end - start;
  264. count %= n;
  265. if (count < 0)
  266. count += n;
  267. if (count) {
  268. char buf[count * sizeof(Row)];
  269. memcpy(buf, start, count * sizeof(Row));
  270. memmove(start, start + count, (n - count) * sizeof(Row));
  271. memcpy(end - count, buf, count * sizeof(Row));
  272. for (Row *row = start; row < end; row++)
  273. row->dirty = true;
  274. }
  275. }
  276. static void buffer_clear(Buffer *b)
  277. {
  278. Cell cell = {
  279. .text = L'\0',
  280. .attr = A_NORMAL,
  281. .fg = -1,
  282. .bg = -1,
  283. };
  284. for (int i = 0; i < b->rows; i++) {
  285. Row *row = b->lines + i;
  286. for (int j = 0; j < b->cols; j++) {
  287. row->cells[j] = cell;
  288. row->dirty = true;
  289. }
  290. }
  291. }
  292. static void buffer_free(Buffer *b)
  293. {
  294. for (int i = 0; i < b->rows; i++)
  295. free(b->lines[i].cells);
  296. free(b->lines);
  297. for (int i = 0; i < b->scroll_size; i++)
  298. free(b->scroll_buf[i].cells);
  299. free(b->scroll_buf);
  300. free(b->tabs);
  301. }
  302. static void buffer_scroll(Buffer *b, int s)
  303. {
  304. /* work in screenfuls */
  305. int ssz = b->scroll_bot - b->scroll_top;
  306. if (s > ssz) {
  307. buffer_scroll(b, ssz);
  308. buffer_scroll(b, s - ssz);
  309. return;
  310. }
  311. if (s < -ssz) {
  312. buffer_scroll(b, -ssz);
  313. buffer_scroll(b, s + ssz);
  314. return;
  315. }
  316. b->scroll_above += s;
  317. if (b->scroll_above >= b->scroll_size)
  318. b->scroll_above = b->scroll_size;
  319. if (s > 0 && b->scroll_size) {
  320. for (int i = 0; i < s; i++) {
  321. Row tmp = b->scroll_top[i];
  322. b->scroll_top[i] = b->scroll_buf[b->scroll_index];
  323. b->scroll_buf[b->scroll_index] = tmp;
  324. b->scroll_index++;
  325. if (b->scroll_index == b->scroll_size)
  326. b->scroll_index = 0;
  327. }
  328. }
  329. row_roll(b->scroll_top, b->scroll_bot, s);
  330. if (s < 0 && b->scroll_size) {
  331. for (int i = (-s) - 1; i >= 0; i--) {
  332. b->scroll_index--;
  333. if (b->scroll_index == -1)
  334. b->scroll_index = b->scroll_size - 1;
  335. Row tmp = b->scroll_top[i];
  336. b->scroll_top[i] = b->scroll_buf[b->scroll_index];
  337. b->scroll_buf[b->scroll_index] = tmp;
  338. b->scroll_top[i].dirty = true;
  339. }
  340. }
  341. }
  342. static void buffer_resize(Buffer *b, int rows, int cols)
  343. {
  344. Row *lines = b->lines;
  345. if (b->rows != rows) {
  346. if (b->curs_row >= lines + rows) {
  347. /* scroll up instead of simply chopping off bottom */
  348. buffer_scroll(b, (b->curs_row - b->lines) - rows + 1);
  349. }
  350. while (b->rows > rows) {
  351. free(lines[b->rows - 1].cells);
  352. b->rows--;
  353. }
  354. lines = realloc(lines, sizeof(Row) * rows);
  355. }
  356. if (b->maxcols < cols) {
  357. for (int row = 0; row < b->rows; row++) {
  358. lines[row].cells = realloc(lines[row].cells, sizeof(Cell) * cols);
  359. if (b->cols < cols)
  360. row_set(lines + row, b->cols, cols - b->cols, NULL);
  361. lines[row].dirty = true;
  362. }
  363. Row *sbuf = b->scroll_buf;
  364. for (int row = 0; row < b->scroll_size; row++) {
  365. sbuf[row].cells = realloc(sbuf[row].cells, sizeof(Cell) * cols);
  366. if (b->cols < cols)
  367. row_set(sbuf + row, b->cols, cols - b->cols, NULL);
  368. }
  369. b->tabs = realloc(b->tabs, sizeof(*b->tabs) * cols);
  370. for (int col = b->cols; col < cols; col++)
  371. b->tabs[col] = !(col & 7);
  372. b->maxcols = cols;
  373. b->cols = cols;
  374. } else if (b->cols != cols) {
  375. for (int row = 0; row < b->rows; row++)
  376. lines[row].dirty = true;
  377. b->cols = cols;
  378. }
  379. int deltarows = 0;
  380. if (b->rows < rows) {
  381. while (b->rows < rows) {
  382. lines[b->rows].cells = calloc(b->maxcols, sizeof(Cell));
  383. row_set(lines + b->rows, 0, b->maxcols, b);
  384. b->rows++;
  385. }
  386. /* prepare for backfill */
  387. if (b->curs_row >= b->scroll_bot - 1) {
  388. deltarows = b->lines + rows - b->curs_row - 1;
  389. if (deltarows > b->scroll_above)
  390. deltarows = b->scroll_above;
  391. }
  392. }
  393. b->curs_row += lines - b->lines;
  394. b->scroll_top = lines;
  395. b->scroll_bot = lines + rows;
  396. b->lines = lines;
  397. /* perform backfill */
  398. if (deltarows > 0) {
  399. buffer_scroll(b, -deltarows);
  400. b->curs_row += deltarows;
  401. }
  402. }
  403. static bool buffer_init(Buffer *b, int rows, int cols, int scroll_size)
  404. {
  405. b->curattrs = A_NORMAL; /* white text over black background */
  406. b->curfg = b->curbg = -1;
  407. if (scroll_size < 0)
  408. scroll_size = 0;
  409. if (scroll_size && !(b->scroll_buf = calloc(scroll_size, sizeof(Row))))
  410. return false;
  411. b->scroll_size = scroll_size;
  412. buffer_resize(b, rows, cols);
  413. return true;
  414. }
  415. static void buffer_boundry(Buffer *b, Row **bs, Row **be, Row **as, Row **ae) {
  416. if (bs)
  417. *bs = NULL;
  418. if (be)
  419. *be = NULL;
  420. if (as)
  421. *as = NULL;
  422. if (ae)
  423. *ae = NULL;
  424. if (!b->scroll_size)
  425. return;
  426. if (b->scroll_above) {
  427. if (bs)
  428. *bs = &b->scroll_buf[(b->scroll_index - b->scroll_above + b->scroll_size) % b->scroll_size];
  429. if (be)
  430. *be = &b->scroll_buf[(b->scroll_index-1 + b->scroll_size) % b->scroll_size];
  431. }
  432. if (b->scroll_below) {
  433. if (as)
  434. *as = &b->scroll_buf[b->scroll_index];
  435. if (ae)
  436. *ae = &b->scroll_buf[(b->scroll_index + b->scroll_below-1) % b->scroll_size];
  437. }
  438. }
  439. static Row *buffer_row_first(Buffer *b) {
  440. Row *bstart;
  441. if (!b->scroll_size || !b->scroll_above)
  442. return b->lines;
  443. buffer_boundry(b, &bstart, NULL, NULL, NULL);
  444. return bstart;
  445. }
  446. static Row *buffer_row_last(Buffer *b) {
  447. Row *aend;
  448. if (!b->scroll_size || !b->scroll_below)
  449. return b->lines + b->rows - 1;
  450. buffer_boundry(b, NULL, NULL, NULL, &aend);
  451. return aend;
  452. }
  453. static Row *buffer_row_next(Buffer *b, Row *row)
  454. {
  455. Row *before_start, *before_end, *after_start, *after_end;
  456. Row *first = b->lines, *last = b->lines + b->rows - 1;
  457. if (!row)
  458. return NULL;
  459. buffer_boundry(b, &before_start, &before_end, &after_start, &after_end);
  460. if (row >= first && row < last)
  461. return ++row;
  462. if (row == last)
  463. return after_start;
  464. if (row == before_end)
  465. return first;
  466. if (row == after_end)
  467. return NULL;
  468. if (row == &b->scroll_buf[b->scroll_size - 1])
  469. return b->scroll_buf;
  470. return ++row;
  471. }
  472. static Row *buffer_row_prev(Buffer *b, Row *row)
  473. {
  474. Row *before_start, *before_end, *after_start, *after_end;
  475. Row *first = b->lines, *last = b->lines + b->rows - 1;
  476. if (!row)
  477. return NULL;
  478. buffer_boundry(b, &before_start, &before_end, &after_start, &after_end);
  479. if (row > first && row <= last)
  480. return --row;
  481. if (row == first)
  482. return before_end;
  483. if (row == before_start)
  484. return NULL;
  485. if (row == after_start)
  486. return last;
  487. if (row == b->scroll_buf)
  488. return &b->scroll_buf[b->scroll_size - 1];
  489. return --row;
  490. }
  491. static void cursor_clamp(Vt *t)
  492. {
  493. Buffer *b = t->buffer;
  494. Row *lines = t->relposmode ? b->scroll_top : b->lines;
  495. int rows = t->relposmode ? b->scroll_bot - b->scroll_top : b->rows;
  496. if (b->curs_row < lines)
  497. b->curs_row = lines;
  498. if (b->curs_row >= lines + rows)
  499. b->curs_row = lines + rows - 1;
  500. if (b->curs_col < 0)
  501. b->curs_col = 0;
  502. if (b->curs_col >= b->cols)
  503. b->curs_col = b->cols - 1;
  504. }
  505. static void cursor_line_down(Vt *t)
  506. {
  507. Buffer *b = t->buffer;
  508. row_set(b->curs_row, b->cols, b->maxcols - b->cols, NULL);
  509. b->curs_row++;
  510. if (b->curs_row < b->scroll_bot)
  511. return;
  512. vt_noscroll(t);
  513. b->curs_row = b->scroll_bot - 1;
  514. buffer_scroll(b, 1);
  515. row_set(b->curs_row, 0, b->cols, b);
  516. }
  517. static void cursor_save(Vt *t)
  518. {
  519. Buffer *b = t->buffer;
  520. b->curs_srow = b->curs_row - b->lines;
  521. b->curs_scol = b->curs_col;
  522. }
  523. static void cursor_restore(Vt *t)
  524. {
  525. Buffer *b = t->buffer;
  526. b->curs_row = b->lines + b->curs_srow;
  527. b->curs_col = b->curs_scol;
  528. cursor_clamp(t);
  529. }
  530. static void attributes_save(Vt *t)
  531. {
  532. Buffer *b = t->buffer;
  533. b->savattrs = b->curattrs;
  534. b->savfg = b->curfg;
  535. b->savbg = b->curbg;
  536. t->savgraphmode = t->graphmode;
  537. }
  538. static void attributes_restore(Vt *t)
  539. {
  540. Buffer *b = t->buffer;
  541. b->curattrs = b->savattrs;
  542. b->curfg = b->savfg;
  543. b->curbg = b->savbg;
  544. t->graphmode = t->savgraphmode;
  545. }
  546. static void new_escape_sequence(Vt *t)
  547. {
  548. t->escaped = true;
  549. t->elen = 0;
  550. t->ebuf[0] = '\0';
  551. }
  552. static void cancel_escape_sequence(Vt *t)
  553. {
  554. t->escaped = false;
  555. t->elen = 0;
  556. t->ebuf[0] = '\0';
  557. }
  558. static bool is_valid_csi_ender(int c)
  559. {
  560. return (c >= 'a' && c <= 'z')
  561. || (c >= 'A' && c <= 'Z')
  562. || (c == '@' || c == '`');
  563. }
  564. /* interprets a 'set attribute' (SGR) CSI escape sequence */
  565. static void interpret_csi_sgr(Vt *t, int param[], int pcount)
  566. {
  567. Buffer *b = t->buffer;
  568. if (pcount == 0) {
  569. /* special case: reset attributes */
  570. b->curattrs = A_NORMAL;
  571. b->curfg = b->curbg = -1;
  572. return;
  573. }
  574. for (int i = 0; i < pcount; i++) {
  575. switch (param[i]) {
  576. case 0:
  577. b->curattrs = A_NORMAL;
  578. b->curfg = b->curbg = -1;
  579. break;
  580. case 1:
  581. b->curattrs |= A_BOLD;
  582. break;
  583. case 2:
  584. b->curattrs |= A_DIM;
  585. break;
  586. #ifdef A_ITALIC
  587. case 3:
  588. b->curattrs |= A_ITALIC;
  589. break;
  590. #endif
  591. case 4:
  592. b->curattrs |= A_UNDERLINE;
  593. break;
  594. case 5:
  595. b->curattrs |= A_BLINK;
  596. break;
  597. case 7:
  598. b->curattrs |= A_REVERSE;
  599. break;
  600. case 8:
  601. b->curattrs |= A_INVIS;
  602. break;
  603. case 22:
  604. b->curattrs &= ~(A_BOLD | A_DIM);
  605. break;
  606. #ifdef A_ITALIC
  607. case 23:
  608. b->curattrs &= ~A_ITALIC;
  609. break;
  610. #endif
  611. case 24:
  612. b->curattrs &= ~A_UNDERLINE;
  613. break;
  614. case 25:
  615. b->curattrs &= ~A_BLINK;
  616. break;
  617. case 27:
  618. b->curattrs &= ~A_REVERSE;
  619. break;
  620. case 28:
  621. b->curattrs &= ~A_INVIS;
  622. break;
  623. case 30 ... 37: /* fg */
  624. b->curfg = param[i] - 30;
  625. break;
  626. case 38:
  627. if ((i + 2) < pcount && param[i + 1] == 5) {
  628. b->curfg = param[i + 2];
  629. i += 2;
  630. }
  631. break;
  632. case 39:
  633. b->curfg = -1;
  634. break;
  635. case 40 ... 47: /* bg */
  636. b->curbg = param[i] - 40;
  637. break;
  638. case 48:
  639. if ((i + 2) < pcount && param[i + 1] == 5) {
  640. b->curbg = param[i + 2];
  641. i += 2;
  642. }
  643. break;
  644. case 49:
  645. b->curbg = -1;
  646. break;
  647. case 90 ... 97: /* hi fg */
  648. b->curfg = param[i] - 82;
  649. break;
  650. case 100 ... 107: /* hi bg */
  651. b->curbg = param[i] - 92;
  652. break;
  653. default:
  654. break;
  655. }
  656. }
  657. }
  658. /* interprets an 'erase display' (ED) escape sequence */
  659. static void interpret_csi_ed(Vt *t, int param[], int pcount)
  660. {
  661. Row *row, *start, *end;
  662. Buffer *b = t->buffer;
  663. attributes_save(t);
  664. b->curattrs = A_NORMAL;
  665. b->curfg = b->curbg = -1;
  666. if (pcount && param[0] == 2) {
  667. start = b->lines;
  668. end = b->lines + b->rows;
  669. } else if (pcount && param[0] == 1) {
  670. start = b->lines;
  671. end = b->curs_row;
  672. row_set(b->curs_row, 0, b->curs_col + 1, b);
  673. } else {
  674. row_set(b->curs_row, b->curs_col, b->cols - b->curs_col, b);
  675. start = b->curs_row + 1;
  676. end = b->lines + b->rows;
  677. }
  678. for (row = start; row < end; row++)
  679. row_set(row, 0, b->cols, b);
  680. attributes_restore(t);
  681. }
  682. /* interprets a 'move cursor' (CUP) escape sequence */
  683. static void interpret_csi_cup(Vt *t, int param[], int pcount)
  684. {
  685. Buffer *b = t->buffer;
  686. Row *lines = t->relposmode ? b->scroll_top : b->lines;
  687. if (pcount == 0) {
  688. b->curs_row = lines;
  689. b->curs_col = 0;
  690. } else if (pcount == 1) {
  691. b->curs_row = lines + param[0] - 1;
  692. b->curs_col = 0;
  693. } else {
  694. b->curs_row = lines + param[0] - 1;
  695. b->curs_col = param[1] - 1;
  696. }
  697. cursor_clamp(t);
  698. }
  699. /* Interpret the 'relative mode' sequences: CUU, CUD, CUF, CUB, CNL,
  700. * CPL, CHA, HPR, VPA, VPR, HPA */
  701. static void interpret_csi_c(Vt *t, char verb, int param[], int pcount)
  702. {
  703. Buffer *b = t->buffer;
  704. int n = (pcount && param[0] > 0) ? param[0] : 1;
  705. switch (verb) {
  706. case 'A':
  707. b->curs_row -= n;
  708. break;
  709. case 'B':
  710. case 'e':
  711. b->curs_row += n;
  712. break;
  713. case 'C':
  714. case 'a':
  715. b->curs_col += n;
  716. break;
  717. case 'D':
  718. b->curs_col -= n;
  719. break;
  720. case 'E':
  721. b->curs_row += n;
  722. b->curs_col = 0;
  723. break;
  724. case 'F':
  725. b->curs_row -= n;
  726. b->curs_col = 0;
  727. break;
  728. case 'G':
  729. case '`':
  730. b->curs_col = n - 1;
  731. break;
  732. case 'd':
  733. b->curs_row = b->lines + n - 1;
  734. break;
  735. }
  736. cursor_clamp(t);
  737. }
  738. /* Interpret the 'erase line' escape sequence */
  739. static void interpret_csi_el(Vt *t, int param[], int pcount)
  740. {
  741. Buffer *b = t->buffer;
  742. switch (pcount ? param[0] : 0) {
  743. case 1:
  744. row_set(b->curs_row, 0, b->curs_col + 1, b);
  745. break;
  746. case 2:
  747. row_set(b->curs_row, 0, b->cols, b);
  748. break;
  749. default:
  750. row_set(b->curs_row, b->curs_col, b->cols - b->curs_col, b);
  751. break;
  752. }
  753. }
  754. /* Interpret the 'insert blanks' sequence (ICH) */
  755. static void interpret_csi_ich(Vt *t, int param[], int pcount)
  756. {
  757. Buffer *b = t->buffer;
  758. Row *row = b->curs_row;
  759. int n = (pcount && param[0] > 0) ? param[0] : 1;
  760. if (b->curs_col + n > b->cols)
  761. n = b->cols - b->curs_col;
  762. for (int i = b->cols - 1; i >= b->curs_col + n; i--)
  763. row->cells[i] = row->cells[i - n];
  764. row_set(row, b->curs_col, n, b);
  765. }
  766. /* Interpret the 'delete chars' sequence (DCH) */
  767. static void interpret_csi_dch(Vt *t, int param[], int pcount)
  768. {
  769. Buffer *b = t->buffer;
  770. Row *row = b->curs_row;
  771. int n = (pcount && param[0] > 0) ? param[0] : 1;
  772. if (b->curs_col + n > b->cols)
  773. n = b->cols - b->curs_col;
  774. for (int i = b->curs_col; i < b->cols - n; i++)
  775. row->cells[i] = row->cells[i + n];
  776. row_set(row, b->cols - n, n, b);
  777. }
  778. /* Interpret an 'insert line' sequence (IL) */
  779. static void interpret_csi_il(Vt *t, int param[], int pcount)
  780. {
  781. Buffer *b = t->buffer;
  782. int n = (pcount && param[0] > 0) ? param[0] : 1;
  783. if (b->curs_row + n >= b->scroll_bot) {
  784. for (Row *row = b->curs_row; row < b->scroll_bot; row++)
  785. row_set(row, 0, b->cols, b);
  786. } else {
  787. row_roll(b->curs_row, b->scroll_bot, -n);
  788. for (Row *row = b->curs_row; row < b->curs_row + n; row++)
  789. row_set(row, 0, b->cols, b);
  790. }
  791. }
  792. /* Interpret a 'delete line' sequence (DL) */
  793. static void interpret_csi_dl(Vt *t, int param[], int pcount)
  794. {
  795. Buffer *b = t->buffer;
  796. int n = (pcount && param[0] > 0) ? param[0] : 1;
  797. if (b->curs_row + n >= b->scroll_bot) {
  798. for (Row *row = b->curs_row; row < b->scroll_bot; row++)
  799. row_set(row, 0, b->cols, b);
  800. } else {
  801. row_roll(b->curs_row, b->scroll_bot, n);
  802. for (Row *row = b->scroll_bot - n; row < b->scroll_bot; row++)
  803. row_set(row, 0, b->cols, b);
  804. }
  805. }
  806. /* Interpret an 'erase characters' (ECH) sequence */
  807. static void interpret_csi_ech(Vt *t, int param[], int pcount)
  808. {
  809. Buffer *b = t->buffer;
  810. int n = (pcount && param[0] > 0) ? param[0] : 1;
  811. if (b->curs_col + n > b->cols)
  812. n = b->cols - b->curs_col;
  813. row_set(b->curs_row, b->curs_col, n, b);
  814. }
  815. /* Interpret a 'set scrolling region' (DECSTBM) sequence */
  816. static void interpret_csi_decstbm(Vt *t, int param[], int pcount)
  817. {
  818. Buffer *b = t->buffer;
  819. int new_top, new_bot;
  820. switch (pcount) {
  821. case 0:
  822. b->scroll_top = b->lines;
  823. b->scroll_bot = b->lines + b->rows;
  824. break;
  825. case 2:
  826. new_top = param[0] - 1;
  827. new_bot = param[1];
  828. /* clamp to bounds */
  829. if (new_top < 0)
  830. new_top = 0;
  831. if (new_top >= b->rows)
  832. new_top = b->rows - 1;
  833. if (new_bot < 0)
  834. new_bot = 0;
  835. if (new_bot >= b->rows)
  836. new_bot = b->rows;
  837. /* check for range validity */
  838. if (new_top < new_bot) {
  839. b->scroll_top = b->lines + new_top;
  840. b->scroll_bot = b->lines + new_bot;
  841. }
  842. break;
  843. default:
  844. return; /* malformed */
  845. }
  846. b->curs_row = b->scroll_top;
  847. b->curs_col = 0;
  848. }
  849. static void interpret_csi_mode(Vt *t, int param[], int pcount, bool set)
  850. {
  851. for (int i = 0; i < pcount; i++) {
  852. switch (param[i]) {
  853. case 4: /* insert/replace mode */
  854. t->insert = set;
  855. break;
  856. }
  857. }
  858. }
  859. static void interpret_csi_priv_mode(Vt *t, int param[], int pcount, bool set)
  860. {
  861. for (int i = 0; i < pcount; i++) {
  862. switch (param[i]) {
  863. case 1: /* set application/normal cursor key mode (DECCKM) */
  864. t->curskeymode = set;
  865. break;
  866. case 6: /* set origin to relative/absolute (DECOM) */
  867. t->relposmode = set;
  868. break;
  869. case 25: /* make cursor visible/invisible (DECCM) */
  870. t->curshid = !set;
  871. break;
  872. case 1049: /* combine 1047 + 1048 */
  873. case 47: /* use alternate/normal screen buffer */
  874. case 1047:
  875. if (!set)
  876. buffer_clear(&t->buffer_alternate);
  877. t->buffer = set ? &t->buffer_alternate : &t->buffer_normal;
  878. vt_dirty(t);
  879. if (param[i] != 1049)
  880. break;
  881. /* fall through */
  882. case 1048: /* save/restore cursor */
  883. if (set)
  884. cursor_save(t);
  885. else
  886. cursor_restore(t);
  887. break;
  888. case 1000: /* enable/disable normal mouse tracking */
  889. t->mousetrack = set;
  890. break;
  891. }
  892. }
  893. }
  894. static void interpret_csi(Vt *t)
  895. {
  896. Buffer *b = t->buffer;
  897. int csiparam[16];
  898. unsigned int param_count = 0;
  899. const char *p = t->ebuf + 1;
  900. char verb = t->ebuf[t->elen - 1];
  901. /* parse numeric parameters */
  902. for (p += (t->ebuf[1] == '?'); *p; p++) {
  903. if (IS_CONTROL(*p)) {
  904. process_nonprinting(t, *p);
  905. } else if (*p == ';') {
  906. if (param_count >= LENGTH(csiparam))
  907. return; /* too long! */
  908. csiparam[param_count++] = 0;
  909. } else if (isdigit((unsigned char)*p)) {
  910. if (param_count == 0)
  911. csiparam[param_count++] = 0;
  912. csiparam[param_count - 1] *= 10;
  913. csiparam[param_count - 1] += *p - '0';
  914. }
  915. }
  916. if (t->ebuf[1] == '?') {
  917. switch (verb) {
  918. case 'h':
  919. case 'l': /* private set/reset mode */
  920. interpret_csi_priv_mode(t, csiparam, param_count, verb == 'h');
  921. break;
  922. }
  923. return;
  924. }
  925. /* delegate handling depending on command character (verb) */
  926. switch (verb) {
  927. case 'h':
  928. case 'l': /* set/reset mode */
  929. interpret_csi_mode(t, csiparam, param_count, verb == 'h');
  930. break;
  931. case 'm': /* set attribute */
  932. interpret_csi_sgr(t, csiparam, param_count);
  933. break;
  934. case 'J': /* erase display */
  935. interpret_csi_ed(t, csiparam, param_count);
  936. break;
  937. case 'H':
  938. case 'f': /* move cursor */
  939. interpret_csi_cup(t, csiparam, param_count);
  940. break;
  941. case 'A':
  942. case 'B':
  943. case 'C':
  944. case 'D':
  945. case 'E':
  946. case 'F':
  947. case 'G':
  948. case 'e':
  949. case 'a':
  950. case 'd':
  951. case '`': /* relative move */
  952. interpret_csi_c(t, verb, csiparam, param_count);
  953. break;
  954. case 'K': /* erase line */
  955. interpret_csi_el(t, csiparam, param_count);
  956. break;
  957. case '@': /* insert characters */
  958. interpret_csi_ich(t, csiparam, param_count);
  959. break;
  960. case 'P': /* delete characters */
  961. interpret_csi_dch(t, csiparam, param_count);
  962. break;
  963. case 'L': /* insert lines */
  964. interpret_csi_il(t, csiparam, param_count);
  965. break;
  966. case 'M': /* delete lines */
  967. interpret_csi_dl(t, csiparam, param_count);
  968. break;
  969. case 'X': /* erase chars */
  970. interpret_csi_ech(t, csiparam, param_count);
  971. break;
  972. case 'S': /* SU: scroll up */
  973. vt_scroll(t, param_count ? -csiparam[0] : -1);
  974. break;
  975. case 'T': /* SD: scroll down */
  976. vt_scroll(t, param_count ? csiparam[0] : 1);
  977. break;
  978. case 'Z': /* CBT: cursor backward tabulation */
  979. puttab(t, param_count ? -csiparam[0] : -1);
  980. break;
  981. case 'g': /* TBC: tabulation clear */
  982. switch (param_count ? csiparam[0] : 0) {
  983. case 0:
  984. b->tabs[b->curs_col] = false;
  985. break;
  986. case 3:
  987. memset(b->tabs, 0, sizeof(*b->tabs) * b->maxcols);
  988. break;
  989. }
  990. break;
  991. case 'r': /* set scrolling region */
  992. interpret_csi_decstbm(t, csiparam, param_count);
  993. break;
  994. case 's': /* save cursor location */
  995. cursor_save(t);
  996. break;
  997. case 'u': /* restore cursor location */
  998. cursor_restore(t);
  999. break;
  1000. case 'n': /* query cursor location */
  1001. if (param_count == 1 && csiparam[0] == 6)
  1002. send_curs(t);
  1003. break;
  1004. default:
  1005. break;
  1006. }
  1007. }
  1008. /* Interpret an 'index' (IND) sequence */
  1009. static void interpret_csi_ind(Vt *t)
  1010. {
  1011. Buffer *b = t->buffer;
  1012. if (b->curs_row < b->lines + b->rows - 1)
  1013. b->curs_row++;
  1014. }
  1015. /* Interpret a 'reverse index' (RI) sequence */
  1016. static void interpret_csi_ri(Vt *t)
  1017. {
  1018. Buffer *b = t->buffer;
  1019. if (b->curs_row > b->scroll_top)
  1020. b->curs_row--;
  1021. else {
  1022. row_roll(b->scroll_top, b->scroll_bot, -1);
  1023. row_set(b->scroll_top, 0, b->cols, b);
  1024. }
  1025. }
  1026. /* Interpret a 'next line' (NEL) sequence */
  1027. static void interpret_csi_nel(Vt *t)
  1028. {
  1029. Buffer *b = t->buffer;
  1030. if (b->curs_row < b->lines + b->rows - 1) {
  1031. b->curs_row++;
  1032. b->curs_col = 0;
  1033. }
  1034. }
  1035. /* Interpret a 'select character set' (SCS) sequence */
  1036. static void interpret_csi_scs(Vt *t)
  1037. {
  1038. /* ESC ( sets G0, ESC ) sets G1 */
  1039. t->charsets[!!(t->ebuf[0] == ')')] = (t->ebuf[1] == '0');
  1040. t->graphmode = t->charsets[0];
  1041. }
  1042. /* Interpret an 'operating system command' (OSC) sequence */
  1043. static void interpret_osc(Vt *t)
  1044. {
  1045. /* ESC ] command ; data BEL
  1046. * ESC ] command ; data ESC \\
  1047. * Note that BEL or ESC \\ have already been replaced with NUL.
  1048. */
  1049. char *data = NULL;
  1050. int command = strtoul(t->ebuf + 1, &data, 10);
  1051. if (data && *data == ';') {
  1052. switch (command) {
  1053. case 0: /* icon name and window title */
  1054. case 2: /* window title */
  1055. if (t->title_handler)
  1056. t->title_handler(t, data+1);
  1057. break;
  1058. case 1: /* icon name */
  1059. break;
  1060. default:
  1061. #ifndef NDEBUG
  1062. fprintf(stderr, "unknown OSC command: %d\n", command);
  1063. #endif
  1064. break;
  1065. }
  1066. }
  1067. }
  1068. static void try_interpret_escape_seq(Vt *t)
  1069. {
  1070. char lastchar = t->ebuf[t->elen - 1];
  1071. if (!*t->ebuf)
  1072. return;
  1073. switch (*t->ebuf) {
  1074. case '#': /* ignore DECDHL, DECSWL, DECDWL, DECHCP, DECFPP */
  1075. if (t->elen == 2) {
  1076. if (lastchar == '8') { /* DECALN */
  1077. interpret_csi_ed(t, (int []){ 2 }, 1);
  1078. goto handled;
  1079. }
  1080. goto cancel;
  1081. }
  1082. break;
  1083. case '(':
  1084. case ')':
  1085. if (t->elen == 2) {
  1086. interpret_csi_scs(t);
  1087. goto handled;
  1088. }
  1089. break;
  1090. case ']': /* OSC - operating system command */
  1091. if (lastchar == '\a' ||
  1092. (lastchar == '\\' && t->elen >= 2 && t->ebuf[t->elen - 2] == '\e')) {
  1093. t->elen -= lastchar == '\a' ? 1 : 2;
  1094. t->ebuf[t->elen] = '\0';
  1095. interpret_osc(t);
  1096. goto handled;
  1097. }
  1098. break;
  1099. case '[': /* CSI - control sequence introducer */
  1100. if (is_valid_csi_ender(lastchar)) {
  1101. interpret_csi(t);
  1102. goto handled;
  1103. }
  1104. break;
  1105. case '7': /* DECSC: save cursor and attributes */
  1106. attributes_save(t);
  1107. cursor_save(t);
  1108. goto handled;
  1109. case '8': /* DECRC: restore cursor and attributes */
  1110. attributes_restore(t);
  1111. cursor_restore(t);
  1112. goto handled;
  1113. case 'D': /* IND: index */
  1114. interpret_csi_ind(t);
  1115. goto handled;
  1116. case 'M': /* RI: reverse index */
  1117. interpret_csi_ri(t);
  1118. goto handled;
  1119. case 'E': /* NEL: next line */
  1120. interpret_csi_nel(t);
  1121. goto handled;
  1122. case 'H': /* HTS: horizontal tab set */
  1123. t->buffer->tabs[t->buffer->curs_col] = true;
  1124. goto handled;
  1125. default:
  1126. goto cancel;
  1127. }
  1128. if (t->elen + 1 >= sizeof(t->ebuf)) {
  1129. cancel:
  1130. #ifndef NDEBUG
  1131. fprintf(stderr, "cancelled: \\033");
  1132. for (unsigned int i = 0; i < t->elen; i++) {
  1133. if (isprint(t->ebuf[i])) {
  1134. fputc(t->ebuf[i], stderr);
  1135. } else {
  1136. fprintf(stderr, "\\%03o", t->ebuf[i]);
  1137. }
  1138. }
  1139. fputc('\n', stderr);
  1140. #endif
  1141. handled:
  1142. cancel_escape_sequence(t);
  1143. }
  1144. }
  1145. static void puttab(Vt *t, int count)
  1146. {
  1147. Buffer *b = t->buffer;
  1148. int direction = count >= 0 ? 1 : -1;
  1149. for (int col = b->curs_col + direction; count; col += direction) {
  1150. if (col < 0) {
  1151. b->curs_col = 0;
  1152. break;
  1153. }
  1154. if (col >= b->cols) {
  1155. b->curs_col = b->cols - 1;
  1156. break;
  1157. }
  1158. if (b->tabs[col]) {
  1159. b->curs_col = col;
  1160. count -= direction;
  1161. }
  1162. }
  1163. }
  1164. static void process_nonprinting(Vt *t, wchar_t wc)
  1165. {
  1166. Buffer *b = t->buffer;
  1167. switch (wc) {
  1168. case '\e': /* ESC */
  1169. new_escape_sequence(t);
  1170. break;
  1171. case '\a': /* BEL */
  1172. if (t->urgent_handler)
  1173. t->urgent_handler(t);
  1174. break;
  1175. case '\b': /* BS */
  1176. if (b->curs_col > 0)
  1177. b->curs_col--;
  1178. break;
  1179. case '\t': /* HT */
  1180. puttab(t, 1);
  1181. break;
  1182. case '\r': /* CR */
  1183. b->curs_col = 0;
  1184. break;
  1185. case '\v': /* VT */
  1186. case '\f': /* FF */
  1187. case '\n': /* LF */
  1188. cursor_line_down(t);
  1189. break;
  1190. case '\016': /* SO: shift out, invoke the G1 character set */
  1191. t->graphmode = t->charsets[1];
  1192. break;
  1193. case '\017': /* SI: shift in, invoke the G0 character set */
  1194. t->graphmode = t->charsets[0];
  1195. break;
  1196. }
  1197. }
  1198. static void is_utf8_locale(void)
  1199. {
  1200. const char *cset = nl_langinfo(CODESET);
  1201. if (!cset)
  1202. cset = "ANSI_X3.4-1968";
  1203. is_utf8 = !strcmp(cset, "UTF-8");
  1204. }
  1205. static wchar_t get_vt100_graphic(char c)
  1206. {
  1207. static char vt100_acs[] = "`afgjklmnopqrstuvwxyz{|}~";
  1208. /*
  1209. * 5f-7e standard vt100
  1210. * 40-5e rxvt extension for extra curses acs chars
  1211. */
  1212. static uint16_t const vt100_utf8[62] = {
  1213. 0x2191, 0x2193, 0x2192, 0x2190, 0x2588, 0x259a, 0x2603, // 41-47
  1214. 0, 0, 0, 0, 0, 0, 0, 0, // 48-4f
  1215. 0, 0, 0, 0, 0, 0, 0, 0, // 50-57
  1216. 0, 0, 0, 0, 0, 0, 0, 0x0020, // 58-5f
  1217. 0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1, // 60-67
  1218. 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba, // 68-6f
  1219. 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c, // 70-77
  1220. 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, // 78-7e
  1221. };
  1222. if (is_utf8)
  1223. return vt100_utf8[c - 0x41];
  1224. else if (strchr(vt100_acs, c))
  1225. return NCURSES_ACS(c);
  1226. return '\0';
  1227. }
  1228. static void put_wc(Vt *t, wchar_t wc)
  1229. {
  1230. int width = 0;
  1231. if (!t->seen_input) {
  1232. t->seen_input = 1;
  1233. kill(-t->pid, SIGWINCH);
  1234. }
  1235. if (t->escaped) {
  1236. if (t->elen + 1 < sizeof(t->ebuf)) {
  1237. t->ebuf[t->elen] = wc;
  1238. t->ebuf[++t->elen] = '\0';
  1239. try_interpret_escape_seq(t);
  1240. } else {
  1241. cancel_escape_sequence(t);
  1242. }
  1243. } else if (IS_CONTROL(wc)) {
  1244. process_nonprinting(t, wc);
  1245. } else {
  1246. if (t->graphmode) {
  1247. if (wc >= 0x41 && wc <= 0x7e) {
  1248. wchar_t gc = get_vt100_graphic(wc);
  1249. if (gc)
  1250. wc = gc;
  1251. }
  1252. width = 1;
  1253. } else if ((width = wcwidth(wc)) < 1) {
  1254. width = 1;
  1255. }
  1256. Buffer *b = t->buffer;
  1257. Cell blank_cell = { L'\0', build_attrs(b->curattrs), b->curfg, b->curbg };
  1258. if (width == 2 && b->curs_col == b->cols - 1) {
  1259. b->curs_row->cells[b->curs_col++] = blank_cell;
  1260. b->curs_row->dirty = true;
  1261. }
  1262. if (b->curs_col >= b->cols) {
  1263. b->curs_col = 0;
  1264. cursor_line_down(t);
  1265. }
  1266. if (t->insert) {
  1267. Cell *src = b->curs_row->cells + b->curs_col;
  1268. Cell *dest = src + width;
  1269. size_t len = b->cols - b->curs_col - width;
  1270. memmove(dest, src, len * sizeof *dest);
  1271. }
  1272. b->curs_row->cells[b->curs_col] = blank_cell;
  1273. b->curs_row->cells[b->curs_col++].text = wc;
  1274. b->curs_row->dirty = true;
  1275. if (width == 2)
  1276. b->curs_row->cells[b->curs_col++] = blank_cell;
  1277. }
  1278. }
  1279. int vt_process(Vt *t)
  1280. {
  1281. int res;
  1282. unsigned int pos = 0;
  1283. mbstate_t ps;
  1284. memset(&ps, 0, sizeof(ps));
  1285. if (t->pty < 0) {
  1286. errno = EINVAL;
  1287. return -1;
  1288. }
  1289. res = read(t->pty, t->rbuf + t->rlen, sizeof(t->rbuf) - t->rlen);
  1290. if (res < 0)
  1291. return -1;
  1292. t->rlen += res;
  1293. while (pos < t->rlen) {
  1294. wchar_t wc;
  1295. ssize_t len;
  1296. len = (ssize_t)mbrtowc(&wc, t->rbuf + pos, t->rlen - pos, &ps);
  1297. if (len == -2) {
  1298. t->rlen -= pos;
  1299. memmove(t->rbuf, t->rbuf + pos, t->rlen);
  1300. return 0;
  1301. }
  1302. if (len == -1) {
  1303. len = 1;
  1304. wc = t->rbuf[pos];
  1305. }
  1306. pos += len ? len : 1;
  1307. put_wc(t, wc);
  1308. }
  1309. t->rlen -= pos;
  1310. memmove(t->rbuf, t->rbuf + pos, t->rlen);
  1311. return 0;
  1312. }
  1313. void vt_default_colors_set(Vt *t, attr_t attrs, short fg, short bg)
  1314. {
  1315. t->defattrs = attrs;
  1316. t->deffg = fg;
  1317. t->defbg = bg;
  1318. }
  1319. Vt *vt_create(int rows, int cols, int scroll_size)
  1320. {
  1321. if (rows <= 0 || cols <= 0)
  1322. return NULL;
  1323. Vt *t = calloc(1, sizeof(Vt));
  1324. if (!t)
  1325. return NULL;
  1326. t->pty = -1;
  1327. t->deffg = t->defbg = -1;
  1328. t->buffer = &t->buffer_normal;
  1329. if (!buffer_init(&t->buffer_normal, rows, cols, scroll_size) ||
  1330. !buffer_init(&t->buffer_alternate, rows, cols, 0)) {
  1331. free(t);
  1332. return NULL;
  1333. }
  1334. return t;
  1335. }
  1336. void vt_resize(Vt *t, int rows, int cols)
  1337. {
  1338. struct winsize ws = { .ws_row = rows, .ws_col = cols };
  1339. if (rows <= 0 || cols <= 0)
  1340. return;
  1341. vt_noscroll(t);
  1342. buffer_resize(&t->buffer_normal, rows, cols);
  1343. buffer_resize(&t->buffer_alternate, rows, cols);
  1344. cursor_clamp(t);
  1345. ioctl(t->pty, TIOCSWINSZ, &ws);
  1346. kill(-t->pid, SIGWINCH);
  1347. }
  1348. void vt_destroy(Vt *t)
  1349. {
  1350. if (!t)
  1351. return;
  1352. buffer_free(&t->buffer_normal);
  1353. buffer_free(&t->buffer_alternate);
  1354. close(t->pty);
  1355. free(t);
  1356. }
  1357. void vt_dirty(Vt *t)
  1358. {
  1359. Buffer *b = t->buffer;
  1360. for (Row *row = b->lines, *end = row + b->rows; row < end; row++)
  1361. row->dirty = true;
  1362. }
  1363. void vt_draw(Vt *t, WINDOW *win, int srow, int scol)
  1364. {
  1365. Buffer *b = t->buffer;
  1366. if (srow != t->srow || scol != t->scol) {
  1367. vt_dirty(t);
  1368. t->srow = srow;
  1369. t->scol = scol;
  1370. }
  1371. for (int i = 0; i < b->rows; i++) {
  1372. Row *row = b->lines + i;
  1373. if (!row->dirty)
  1374. continue;
  1375. wmove(win, srow + i, scol);
  1376. Cell *cell = NULL;
  1377. for (int j = 0; j < b->cols; j++) {
  1378. Cell *prev_cell = cell;
  1379. cell = row->cells + j;
  1380. if (!prev_cell || cell->attr != prev_cell->attr
  1381. || cell->fg != prev_cell->fg
  1382. || cell->bg != prev_cell->bg) {
  1383. if (cell->attr == A_NORMAL)
  1384. cell->attr = t->defattrs;
  1385. if (cell->fg == -1)
  1386. cell->fg = t->deffg;
  1387. if (cell->bg == -1)
  1388. cell->bg = t->defbg;
  1389. wattrset(win, cell->attr << NCURSES_ATTR_SHIFT);
  1390. wcolor_set(win, vt_color_get(t, cell->fg, cell->bg), NULL);
  1391. }
  1392. if (is_utf8 && cell->text >= 128) {
  1393. char buf[MB_CUR_MAX + 1];
  1394. size_t len = wcrtomb(buf, cell->text, NULL);
  1395. if (len > 0) {
  1396. waddnstr(win, buf, len);
  1397. if (wcwidth(cell->text) > 1)
  1398. j++;
  1399. }
  1400. } else {
  1401. waddch(win, cell->text > ' ' ? cell->text : ' ');
  1402. }
  1403. }
  1404. int x, y;
  1405. getyx(win, y, x);
  1406. (void)y;
  1407. if (x && x < b->cols - 1)
  1408. whline(win, ' ', b->cols - x);
  1409. row->dirty = false;
  1410. }
  1411. wmove(win, srow + b->curs_row - b->lines, scol + b->curs_col);
  1412. }
  1413. void vt_scroll(Vt *t, int rows)
  1414. {
  1415. Buffer *b = t->buffer;
  1416. if (!b->scroll_size)
  1417. return;
  1418. if (rows < 0) { /* scroll back */
  1419. if (rows < -b->scroll_above)
  1420. rows = -b->scroll_above;
  1421. } else { /* scroll forward */
  1422. if (rows > b->scroll_below)
  1423. rows = b->scroll_below;
  1424. }
  1425. buffer_scroll(b, rows);
  1426. b->scroll_below -= rows;
  1427. }
  1428. void vt_noscroll(Vt *t)
  1429. {
  1430. int scroll_below = t->buffer->scroll_below;
  1431. if (scroll_below)
  1432. vt_scroll(t, scroll_below);
  1433. }
  1434. pid_t vt_forkpty(Vt *t, const char *p, const char *argv[], const char *cwd, const char *env[], int *to, int *from)
  1435. {
  1436. int vt2ed[2], ed2vt[2];
  1437. struct winsize ws;
  1438. ws.ws_row = t->buffer->rows;
  1439. ws.ws_col = t->buffer->cols;
  1440. ws.ws_xpixel = ws.ws_ypixel = 0;
  1441. if (to && pipe(vt2ed)) {
  1442. *to = -1;
  1443. to = NULL;
  1444. }
  1445. if (from && pipe(ed2vt)) {
  1446. *from = -1;
  1447. from = NULL;
  1448. }
  1449. pid_t pid = forkpty(&t->pty, NULL, NULL, &ws);
  1450. if (pid < 0)
  1451. return -1;
  1452. if (pid == 0) {
  1453. setsid();
  1454. sigset_t emptyset;
  1455. sigemptyset(&emptyset);
  1456. sigprocmask(SIG_SETMASK, &emptyset, NULL);
  1457. if (to) {
  1458. close(vt2ed[1]);
  1459. dup2(vt2ed[0], STDIN_FILENO);
  1460. close(vt2ed[0]);
  1461. }
  1462. if (from) {
  1463. close(ed2vt[0]);
  1464. dup2(ed2vt[1], STDOUT_FILENO);
  1465. close(ed2vt[1]);
  1466. }
  1467. int maxfd = sysconf(_SC_OPEN_MAX);
  1468. for (int fd = 3; fd < maxfd; fd++)
  1469. if (close(fd) == -1 && errno == EBADF)
  1470. break;
  1471. for (const char **envp = env; envp && envp[0]; envp += 2)
  1472. setenv(envp[0], envp[1], 1);
  1473. setenv("TERM", vt_term, 1);
  1474. if (cwd)
  1475. chdir(cwd);
  1476. execvp(p, (char *const *)argv);
  1477. fprintf(stderr, "\nexecv() failed.\nCommand: '%s'\n", argv[0]);
  1478. exit(1);
  1479. }
  1480. if (to) {
  1481. close(vt2ed[0]);
  1482. *to = vt2ed[1];
  1483. }
  1484. if (from) {
  1485. close(ed2vt[1]);
  1486. *from = ed2vt[0];
  1487. }
  1488. return t->pid = pid;
  1489. }
  1490. int vt_pty_get(Vt *t)
  1491. {
  1492. return t->pty;
  1493. }
  1494. ssize_t vt_write(Vt *t, const char *buf, size_t len)
  1495. {
  1496. ssize_t ret = len;
  1497. while (len > 0) {
  1498. ssize_t res = write(t->pty, buf, len);
  1499. if (res < 0) {
  1500. if (errno != EAGAIN && errno != EINTR)
  1501. return -1;
  1502. continue;
  1503. }
  1504. buf += res;
  1505. len -= res;
  1506. }
  1507. return ret;
  1508. }
  1509. static void send_curs(Vt *t)
  1510. {
  1511. Buffer *b = t->buffer;
  1512. char keyseq[16];
  1513. snprintf(keyseq, sizeof keyseq, "\e[%d;%dR", (int)(b->curs_row - b->lines), b->curs_col);
  1514. vt_write(t, keyseq, strlen(keyseq));
  1515. }
  1516. void vt_keypress(Vt *t, int keycode)
  1517. {
  1518. vt_noscroll(t);
  1519. if (keycode >= 0 && keycode <= KEY_MAX && keytable[keycode]) {
  1520. switch (keycode) {
  1521. case KEY_UP:
  1522. case KEY_DOWN:
  1523. case KEY_RIGHT:
  1524. case KEY_LEFT: {
  1525. char keyseq[3] = { '\e', (t->curskeymode ? 'O' : '['), keytable[keycode][0] };
  1526. vt_write(t, keyseq, sizeof keyseq);
  1527. break;
  1528. }
  1529. default:
  1530. vt_write(t, keytable[keycode], strlen(keytable[keycode]));
  1531. }
  1532. } else if (keycode <= UCHAR_MAX) {
  1533. char c = keycode;
  1534. vt_write(t, &c, 1);
  1535. } else {
  1536. #ifndef NDEBUG
  1537. fprintf(stderr, "unhandled key %#o\n", keycode);
  1538. #endif
  1539. }
  1540. }
  1541. void vt_mouse(Vt *t, int x, int y, mmask_t mask)
  1542. {
  1543. #ifdef NCURSES_MOUSE_VERSION
  1544. char seq[6] = { '\e', '[', 'M' }, state = 0, button = 0;
  1545. if (!t->mousetrack)
  1546. return;
  1547. if (mask & (BUTTON1_PRESSED | BUTTON1_CLICKED))
  1548. button = 0;
  1549. else if (mask & (BUTTON2_PRESSED | BUTTON2_CLICKED))
  1550. button = 1;
  1551. else if (mask & (BUTTON3_PRESSED | BUTTON3_CLICKED))
  1552. button = 2;
  1553. else if (mask & (BUTTON1_RELEASED | BUTTON2_RELEASED | BUTTON3_RELEASED))
  1554. button = 3;
  1555. if (mask & BUTTON_SHIFT)
  1556. state |= 4;
  1557. if (mask & BUTTON_ALT)
  1558. state |= 8;
  1559. if (mask & BUTTON_CTRL)
  1560. state |= 16;
  1561. seq[3] = 32 + button + state;
  1562. seq[4] = 32 + x;
  1563. seq[5] = 32 + y;
  1564. vt_write(t, seq, sizeof seq);
  1565. if (mask & (BUTTON1_CLICKED | BUTTON2_CLICKED | BUTTON3_CLICKED)) {
  1566. /* send a button release event */
  1567. button = 3;
  1568. seq[3] = 32 + button + state;
  1569. vt_write(t, seq, sizeof seq);
  1570. }
  1571. #endif /* NCURSES_MOUSE_VERSION */
  1572. }
  1573. static unsigned int color_hash(short fg, short bg)
  1574. {
  1575. if (fg == -1)
  1576. fg = COLORS;
  1577. if (bg == -1)
  1578. bg = COLORS + 1;
  1579. return fg * (COLORS + 2) + bg;
  1580. }
  1581. short vt_color_get(Vt *t, short fg, short bg)
  1582. {
  1583. if (fg >= COLORS)
  1584. fg = (t ? t->deffg : default_fg);
  1585. if (bg >= COLORS)
  1586. bg = (t ? t->defbg : default_bg);
  1587. if (!has_default_colors) {
  1588. if (fg == -1)
  1589. fg = (t && t->deffg != -1 ? t->deffg : default_fg);
  1590. if (bg == -1)
  1591. bg = (t && t->defbg != -1 ? t->defbg : default_bg);
  1592. }
  1593. if (!color2palette || (fg == -1 && bg == -1))
  1594. return 0;
  1595. unsigned int index = color_hash(fg, bg);
  1596. if (color2palette[index] == 0) {
  1597. short oldfg, oldbg;
  1598. for (;;) {
  1599. if (++color_pair_current >= color_pairs_max)
  1600. color_pair_current = color_pairs_reserved + 1;
  1601. pair_content(color_pair_current, &oldfg, &oldbg);
  1602. unsigned int old_index = color_hash(oldfg, oldbg);
  1603. if (color2palette[old_index] >= 0) {
  1604. if (init_pair(color_pair_current, fg, bg) == OK) {
  1605. color2palette[old_index] = 0;
  1606. color2palette[index] = color_pair_current;
  1607. }
  1608. break;
  1609. }
  1610. }
  1611. }
  1612. short color_pair = color2palette[index];
  1613. return color_pair >= 0 ? color_pair : -color_pair;
  1614. }
  1615. short vt_color_reserve(short fg, short bg)
  1616. {
  1617. if (!color2palette || fg >= COLORS || bg >= COLORS)
  1618. return 0;
  1619. if (!has_default_colors && fg == -1)
  1620. fg = default_fg;
  1621. if (!has_default_colors && bg == -1)
  1622. bg = default_bg;
  1623. if (fg == -1 && bg == -1)
  1624. return 0;
  1625. unsigned int index = color_hash(fg, bg);
  1626. if (color2palette[index] >= 0) {
  1627. if (init_pair(color_pairs_reserved + 1, fg, bg) == OK)
  1628. color2palette[index] = -(++color_pairs_reserved);
  1629. }
  1630. short color_pair = color2palette[index];
  1631. return color_pair >= 0 ? color_pair : -color_pair;
  1632. }
  1633. static void init_colors(void)
  1634. {
  1635. pair_content(0, &default_fg, &default_bg);
  1636. if (default_fg == -1)
  1637. default_fg = COLOR_WHITE;
  1638. if (default_bg == -1)
  1639. default_bg = COLOR_BLACK;
  1640. has_default_colors = (use_default_colors() == OK);
  1641. color_pairs_max = MIN(COLOR_PAIRS, MAX_COLOR_PAIRS);
  1642. if (COLORS)
  1643. color2palette = calloc((COLORS + 2) * (COLORS + 2), sizeof(short));
  1644. vt_color_reserve(COLOR_WHITE, COLOR_BLACK);
  1645. }
  1646. void vt_init(void)
  1647. {
  1648. init_colors();
  1649. is_utf8_locale();
  1650. char *term = getenv("DVTM_TERM");
  1651. if (!term)
  1652. term = "dvtm";
  1653. snprintf(vt_term, sizeof vt_term, "%s%s", term, COLORS >= 256 ? "-256color" : "");
  1654. }
  1655. void vt_keytable_set(const char * const keytable_overlay[], int count)
  1656. {
  1657. for (int k = 0; k < count && k < KEY_MAX; k++) {
  1658. const char *keyseq = keytable_overlay[k];
  1659. if (keyseq)
  1660. keytable[k] = keyseq;
  1661. }
  1662. }
  1663. void vt_shutdown(void)
  1664. {
  1665. free(color2palette);
  1666. }
  1667. void vt_title_handler_set(Vt *t, vt_title_handler_t handler)
  1668. {
  1669. t->title_handler = handler;
  1670. }
  1671. void vt_urgent_handler_set(Vt *t, vt_urgent_handler_t handler)
  1672. {
  1673. t->urgent_handler = handler;
  1674. }
  1675. void vt_data_set(Vt *t, void *data)
  1676. {
  1677. t->data = data;
  1678. }
  1679. void *vt_data_get(Vt *t)
  1680. {
  1681. return t->data;
  1682. }
  1683. bool vt_cursor_visible(Vt *t)
  1684. {
  1685. return t->buffer->scroll_below ? false : !t->curshid;
  1686. }
  1687. pid_t vt_pid_get(Vt *t)
  1688. {
  1689. return t->pid;
  1690. }
  1691. size_t vt_content_get(Vt *t, char **buf, bool colored)
  1692. {
  1693. Buffer *b = t->buffer;
  1694. int lines = b->scroll_above + b->scroll_below + b->rows + 1;
  1695. size_t size = lines * ((b->cols + 1) * ((colored ? 64 : 0) + MB_CUR_MAX));
  1696. mbstate_t ps;
  1697. memset(&ps, 0, sizeof(ps));
  1698. if (!(*buf = malloc(size)))
  1699. return 0;
  1700. char *s = *buf;
  1701. Cell *prev_cell = NULL;
  1702. for (Row *row = buffer_row_first(b); row; row = buffer_row_next(b, row)) {
  1703. size_t len = 0;
  1704. char *last_non_space = s;
  1705. for (int col = 0; col < b->cols; col++) {
  1706. Cell *cell = row->cells + col;
  1707. if (colored) {
  1708. int esclen = 0;
  1709. if (!prev_cell || cell->attr != prev_cell->attr) {
  1710. attr_t attr = cell->attr << NCURSES_ATTR_SHIFT;
  1711. esclen = sprintf(s, "\033[0%s%s%s%s%s%sm",
  1712. attr & A_BOLD ? ";1" : "",
  1713. attr & A_DIM ? ";2" : "",
  1714. attr & A_UNDERLINE ? ";4" : "",
  1715. attr & A_BLINK ? ";5" : "",
  1716. attr & A_REVERSE ? ";7" : "",
  1717. attr & A_INVIS ? ";8" : "");
  1718. if (esclen > 0)
  1719. s += esclen;
  1720. }
  1721. if (!prev_cell || cell->fg != prev_cell->fg || cell->attr != prev_cell->attr) {
  1722. if (cell->fg == -1)
  1723. esclen = sprintf(s, "\033[39m");
  1724. else
  1725. esclen = sprintf(s, "\033[38;5;%dm", cell->fg);
  1726. if (esclen > 0)
  1727. s += esclen;
  1728. }
  1729. if (!prev_cell || cell->bg != prev_cell->bg || cell->attr != prev_cell->attr) {
  1730. if (cell->bg == -1)
  1731. esclen = sprintf(s, "\033[49m");
  1732. else
  1733. esclen = sprintf(s, "\033[48;5;%dm", cell->bg);
  1734. if (esclen > 0)
  1735. s += esclen;
  1736. }
  1737. prev_cell = cell;
  1738. }
  1739. if (cell->text) {
  1740. len = wcrtomb(s, cell->text, &ps);
  1741. if (len > 0)
  1742. s += len;
  1743. last_non_space = s;
  1744. } else if (len) {
  1745. len = 0;
  1746. } else {
  1747. *s++ = ' ';
  1748. }
  1749. }
  1750. s = last_non_space;
  1751. *s++ = '\n';
  1752. }
  1753. return s - *buf;
  1754. }
  1755. int vt_content_start(Vt *t)
  1756. {
  1757. return t->buffer->scroll_above;
  1758. }