terminal.c 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504
  1. #ifndef macintosh
  2. #include <windows.h>
  3. #endif /* not macintosh */
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include "putty.h"
  8. #define cfg (s->cfg)
  9. #define back (s->back)
  10. #define rows (s->rows)
  11. #define cols (s->cols)
  12. #define savelines (s->savelines)
  13. #define font_width (s->font_width)
  14. #define font_height (s->font_height)
  15. #define has_focus (s->has_focus)
  16. #define inbuf (s->inbuf)
  17. #define inbuf_head (s->inbuf_head)
  18. #define inbuf_reap (s->inbuf_reap)
  19. #define app_cursor_keys (s->app_cursor_keys)
  20. #define app_keypad_keys (s->app_keypad_keys)
  21. #define attr_mask (s->attr_mask)
  22. #define text s->ts.text
  23. #define scrtop s->ts.scrtop
  24. #define disptop s->ts.disptop
  25. #define sbtop s->ts.sbtop
  26. #define cpos s->ts.cpos
  27. #define disptext s->ts.disptext
  28. #define wanttext s->ts.wanttext
  29. #define alttext s->ts.alttext
  30. #define selspace s->ts.selspace
  31. #define TSIZE (sizeof(*text))
  32. #define fix_cpos do { cpos = scrtop + curs_y * (cols+1) + curs_x; } while(0)
  33. #define curr_attr s->ts.curr_attr
  34. #define save_attr s->ts.save_attr
  35. #define curs_x s->ts.curs_x
  36. #define curs_y s->ts.curs_y
  37. #define save_x s->ts.save_x
  38. #define save_y s->ts.save_y
  39. #define marg_t s->ts.marg_t
  40. #define marg_b s->ts.marg_b
  41. #define curr_dec_om s->ts.dec_om
  42. #define wrap s->ts.wrap
  43. #define wrapnext s->ts.wrapnext
  44. #define insert s->ts.insert
  45. #define cset s->ts.cset
  46. #define save_cset s->ts.save_cset
  47. #define save_csattr s->ts.save_csattr
  48. #define rvideo s->ts.rvideo
  49. #define cset_attr s->ts.cset_attr
  50. #define alt_x s->ts.alt_x
  51. #define alt_y s->ts.alt_y
  52. #define alt_om s->ts.alt_om
  53. #define alt_wrap s->ts.alt_wrap
  54. #define alt_wnext s->ts.alt_wnext
  55. #define alt_ins s->ts.alt_ins
  56. #define alt_cset s->ts.alt_cset
  57. #define alt_t s->ts.alt_t
  58. #define alt_b s->ts.alt_b
  59. #define alt_which s->ts.alt_which
  60. #define ARG_DEFAULT -1 /* if an arg isn't specified */
  61. #define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) )
  62. #define esc_args s->ts.esc_args
  63. #define esc_nargs s->ts.esc_nargs
  64. #define esc_query s->ts.esc_query
  65. #define osc_strlen s->ts.osc_strlen
  66. #define osc_string s->ts.osc_string
  67. #define osc_w s->ts.osc_w
  68. #define tabs s->ts.tabs
  69. #define MAXNL 5
  70. #define nl_count s->ts.nl_count
  71. #define termstate (s->ts.termstate)
  72. #define selstate (s->ts.selstate)
  73. #define selmode (s->ts.selmode)
  74. #define selstart (s->ts.selstart)
  75. #define selend (s->ts.selend)
  76. #define selanchor (s->ts.selanchor)
  77. #define curr_wordness s->ts.wordness
  78. static unsigned char sel_nl[] = SEL_NL;
  79. /*
  80. * Internal prototypes.
  81. */
  82. static void do_paint (Session *, int);
  83. static void erase_lots (Session *, int, int, int);
  84. static void swap_screen (Session *, int);
  85. static void update_sbar (Session *);
  86. static void deselect (Session *);
  87. static void scroll_display(Session *, int, int, int);
  88. /*
  89. * Set up power-on settings for the terminal.
  90. */
  91. static void power_on(Session *s) {
  92. curs_x = curs_y = alt_x = alt_y = save_x = save_y = 0;
  93. alt_t = marg_t = 0;
  94. if (rows != -1)
  95. alt_b = marg_b = rows - 1;
  96. else
  97. alt_b = marg_b = 0;
  98. if (cols != -1) {
  99. int i;
  100. for (i = 0; i < cols; i++)
  101. tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
  102. }
  103. alt_om = curr_dec_om = cfg.dec_om;
  104. alt_wnext = wrapnext = alt_ins = insert = FALSE;
  105. alt_wrap = wrap = cfg.wrap_mode;
  106. alt_cset = cset = 0;
  107. cset_attr[0] = cset_attr[1] = ATTR_ASCII;
  108. rvideo = 0;
  109. save_attr = curr_attr = ATTR_DEFAULT;
  110. app_cursor_keys = cfg.app_cursor;
  111. app_keypad_keys = cfg.app_keypad;
  112. alt_which = 0;
  113. {
  114. int i;
  115. for (i = 0; i < 256; i++)
  116. curr_wordness[i] = cfg.wordness[i];
  117. }
  118. if (text) {
  119. swap_screen (s, 1);
  120. erase_lots (s, FALSE, TRUE, TRUE);
  121. swap_screen (s, 0);
  122. erase_lots (s, FALSE, TRUE, TRUE);
  123. }
  124. }
  125. /*
  126. * Force a screen update.
  127. */
  128. void term_update(Session *s) {
  129. pre_paint(s);
  130. do_paint(s, TRUE);
  131. post_paint(s);
  132. nl_count = 0;
  133. }
  134. /*
  135. * Same as power_on(), but an external function.
  136. */
  137. void term_pwron(Session *s) {
  138. power_on(s);
  139. fix_cpos;
  140. disptop = scrtop;
  141. deselect(s);
  142. term_update(s);
  143. }
  144. /*
  145. * Clear the scrollback.
  146. */
  147. void term_clrsb(Session *s) {
  148. disptop = sbtop = scrtop;
  149. update_sbar(s);
  150. }
  151. /*
  152. * Initialise the terminal.
  153. */
  154. void term_init(Session *s) {
  155. text = sbtop = scrtop = disptop = cpos = NULL;
  156. disptext = wanttext = NULL;
  157. tabs = NULL;
  158. selspace = NULL;
  159. deselect(s);
  160. rows = cols = -1;
  161. nl_count = 0;
  162. power_on(s);
  163. }
  164. /*
  165. * Set up the terminal for a given size.
  166. */
  167. void term_size(Session *s, int newrows, int newcols, int newsavelines) {
  168. unsigned long *newtext, *newdisp, *newwant, *newalt;
  169. int i, j, crows, ccols;
  170. if (newrows == rows && newcols == cols && newsavelines == savelines)
  171. return; /* nothing to do */
  172. alt_t = marg_t = 0;
  173. alt_b = marg_b = newrows - 1;
  174. newtext = smalloc ((newrows+newsavelines)*(newcols+1)*TSIZE);
  175. disptop = newtext + newsavelines*(newcols+1);
  176. for (i=0; i<(newrows+newsavelines)*(newcols+1); i++)
  177. newtext[i] = ERASE_CHAR;
  178. if (rows != -1) {
  179. crows = rows + (scrtop - sbtop) / (cols+1);
  180. if (crows > newrows+newsavelines)
  181. crows = newrows+newsavelines;
  182. ccols = (cols < newcols ? cols : newcols);
  183. for (i=0; i<crows; i++) {
  184. int oldidx = (rows + savelines - crows + i) * (cols+1);
  185. int newidx = (newrows + newsavelines - crows + i) * (newcols+1);
  186. for (j=0; j<ccols; j++)
  187. newtext[newidx+j] = text[oldidx+j];
  188. newtext[newidx+newcols] =
  189. (cols == newcols ? text[oldidx+cols] : 0);
  190. }
  191. sbtop = disptop - (crows - newrows) * (newcols+1);
  192. if (sbtop > disptop)
  193. sbtop = disptop;
  194. } else
  195. sbtop = disptop;
  196. scrtop = disptop;
  197. sfree (text);
  198. text = newtext;
  199. newdisp = smalloc (newrows*(newcols+1)*TSIZE);
  200. for (i=0; i<newrows*(newcols+1); i++)
  201. newdisp[i] = ATTR_INVALID;
  202. sfree (disptext);
  203. disptext = newdisp;
  204. newwant = smalloc (newrows*(newcols+1)*TSIZE);
  205. for (i=0; i<newrows*(newcols+1); i++)
  206. newwant[i] = ATTR_INVALID;
  207. sfree (wanttext);
  208. wanttext = newwant;
  209. newalt = smalloc (newrows*(newcols+1)*TSIZE);
  210. for (i=0; i<newrows*(newcols+1); i++)
  211. newalt[i] = ERASE_CHAR;
  212. sfree (alttext);
  213. alttext = newalt;
  214. sfree (selspace);
  215. selspace = smalloc ( (newrows+newsavelines) * (newcols+sizeof(sel_nl)) );
  216. tabs = srealloc (tabs, newcols*sizeof(*tabs));
  217. {
  218. int i;
  219. for (i = (cols > 0 ? cols : 0); i < newcols; i++)
  220. tabs[i] = (i % 8 == 0 ? TRUE : FALSE);
  221. }
  222. if (rows > 0)
  223. curs_y += newrows - rows;
  224. if (curs_y < 0)
  225. curs_y = 0;
  226. if (curs_y >= newrows)
  227. curs_y = newrows-1;
  228. if (curs_x >= newcols)
  229. curs_x = newcols-1;
  230. alt_x = alt_y = 0;
  231. wrapnext = alt_wnext = FALSE;
  232. rows = newrows;
  233. cols = newcols;
  234. savelines = newsavelines;
  235. fix_cpos;
  236. deselect(s);
  237. update_sbar(s);
  238. term_update(s);
  239. }
  240. /*
  241. * Swap screens.
  242. */
  243. static void swap_screen (Session *s, int which) {
  244. int t;
  245. unsigned long tt;
  246. if (which == alt_which)
  247. return;
  248. alt_which = which;
  249. for (t=0; t<rows*(cols+1); t++) {
  250. tt = scrtop[t]; scrtop[t] = alttext[t]; alttext[t] = tt;
  251. }
  252. t = curs_x; curs_x = alt_x; alt_x = t;
  253. t = curs_y; curs_y = alt_y; alt_y = t;
  254. t = marg_t; marg_t = alt_t; alt_t = t;
  255. t = marg_b; marg_b = alt_b; alt_b = t;
  256. t = curr_dec_om; curr_dec_om = alt_om; alt_om = t;
  257. t = wrap; wrap = alt_wrap; alt_wrap = t;
  258. t = wrapnext; wrapnext = alt_wnext; alt_wnext = t;
  259. t = insert; insert = alt_ins; alt_ins = t;
  260. t = cset; cset = alt_cset; alt_cset = t;
  261. fix_cpos;
  262. }
  263. /*
  264. * Retrieve a character from `inbuf'.
  265. */
  266. static int inbuf_getc(Session *s) {
  267. if (inbuf_head == inbuf_reap)
  268. return -1; /* EOF */
  269. else {
  270. int n = inbuf_reap;
  271. inbuf_reap = (inbuf_reap+1) & INBUF_MASK;
  272. return inbuf[n];
  273. }
  274. }
  275. /*
  276. * Update the scroll bar.
  277. */
  278. static void update_sbar(Session *s) {
  279. int min;
  280. min = (sbtop - text) / (cols+1);
  281. set_sbar(s, (scrtop - text) / (cols+1) + rows - min,
  282. (disptop - text) / (cols+1) - min,
  283. rows);
  284. }
  285. /*
  286. * Check whether the region bounded by the two pointers intersects
  287. * the selection, and de-select the on-screen selection if so.
  288. */
  289. static void check_selection(Session *s,
  290. unsigned long *from, unsigned long *to) {
  291. if (from < selend && selstart < to)
  292. deselect(s);
  293. }
  294. /*
  295. * Scroll the screen. (`lines' is +ve for scrolling forward, -ve
  296. * for backward.) `sb' is TRUE if the scrolling is permitted to
  297. * affect the scrollback buffer.
  298. */
  299. static void scroll (Session *s, int topline, int botline, int lines, int sb) {
  300. unsigned long *scroll_top;
  301. int scroll_size, size, i;
  302. scroll_top = scrtop + topline*(cols+1);
  303. size = (lines < 0 ? -lines : lines) * (cols+1);
  304. scroll_size = (botline - topline + 1) * (cols+1) - size;
  305. if (lines > 0 && topline == 0 && botline == (rows-1) && sb) {
  306. /*
  307. * Since we're going to scroll the whole screen upwards,
  308. * let's also affect the scrollback buffer.
  309. */
  310. sbtop -= lines * (cols+1);
  311. if (sbtop < text)
  312. sbtop = text;
  313. scroll_size += scroll_top - sbtop;
  314. scroll_top = sbtop;
  315. update_sbar(s);
  316. }
  317. if (scroll_size < 0) {
  318. size += scroll_size;
  319. scroll_size = 0;
  320. }
  321. if (lines > 0) {
  322. if (scroll_size)
  323. memmove (scroll_top, scroll_top + size, scroll_size*TSIZE);
  324. for (i = 0; i < size; i++)
  325. scroll_top[i+scroll_size] = ERASE_CHAR;
  326. if (selstart > scroll_top &&
  327. selstart < scroll_top + size + scroll_size) {
  328. selstart -= size;
  329. if (selstart < scroll_top)
  330. selstart = scroll_top;
  331. }
  332. if (selend > scroll_top &&
  333. selend < scroll_top + size + scroll_size) {
  334. selend -= size;
  335. if (selend < scroll_top)
  336. selend = scroll_top;
  337. }
  338. } else {
  339. if (scroll_size)
  340. memmove (scroll_top + size, scroll_top, scroll_size*TSIZE);
  341. for (i = 0; i < size; i++)
  342. scroll_top[i] = ERASE_CHAR;
  343. if (selstart > scroll_top &&
  344. selstart < scroll_top + size + scroll_size) {
  345. selstart += size;
  346. if (selstart > scroll_top + size + scroll_size)
  347. selstart = scroll_top + size + scroll_size;
  348. }
  349. if (selend > scroll_top &&
  350. selend < scroll_top + size + scroll_size) {
  351. selend += size;
  352. if (selend > scroll_top + size + scroll_size)
  353. selend = scroll_top + size + scroll_size;
  354. }
  355. }
  356. #ifdef OPTIMISE_SCROLL
  357. scroll_display(s, topline, botline, lines);
  358. #endif
  359. }
  360. #ifdef OPTIMISE_SCROLL
  361. static void scroll_display(Session *s, int topline, int botline, int lines) {
  362. unsigned long *start, *end;
  363. int distance, size, i;
  364. start = disptext + topline * (cols + 1);
  365. end = disptext + (botline + 1) * (cols + 1);
  366. distance = (lines > 0 ? lines : -lines) * (cols + 1);
  367. size = end - start - distance;
  368. if (lines > 0) {
  369. memmove(start, start + distance, size * TSIZE);
  370. for (i = 0; i < distance; i++)
  371. (start + size)[i] |= ATTR_INVALID;
  372. } else {
  373. memmove(start + distance, start, size * TSIZE);
  374. for (i = 0; i < distance; i++)
  375. start[i] |= ATTR_INVALID;
  376. }
  377. do_scroll(s, topline, botline, lines);
  378. }
  379. #endif /* OPTIMISE_SCROLL */
  380. /*
  381. * Move the cursor to a given position, clipping at boundaries. We
  382. * may or may not want to clip at the scroll margin: marg_clip is 0
  383. * not to, 1 to disallow _passing_ the margins, and 2 to disallow
  384. * even _being_ outside the margins.
  385. */
  386. static void move (Session *s, int x, int y, int marg_clip) {
  387. if (x < 0)
  388. x = 0;
  389. if (x >= cols)
  390. x = cols-1;
  391. if (marg_clip) {
  392. if ((curs_y >= marg_t || marg_clip == 2) && y < marg_t)
  393. y = marg_t;
  394. if ((curs_y <= marg_b || marg_clip == 2) && y > marg_b)
  395. y = marg_b;
  396. }
  397. if (y < 0)
  398. y = 0;
  399. if (y >= rows)
  400. y = rows-1;
  401. curs_x = x;
  402. curs_y = y;
  403. fix_cpos;
  404. wrapnext = FALSE;
  405. }
  406. /*
  407. * Save or restore the cursor and SGR mode.
  408. */
  409. static void save_cursor(Session *s, int save) {
  410. if (save) {
  411. save_x = curs_x;
  412. save_y = curs_y;
  413. save_attr = curr_attr;
  414. save_cset = cset;
  415. save_csattr = cset_attr[cset];
  416. } else {
  417. curs_x = save_x;
  418. curs_y = save_y;
  419. curr_attr = save_attr;
  420. cset = save_cset;
  421. cset_attr[cset] = save_csattr;
  422. fix_cpos;
  423. }
  424. }
  425. /*
  426. * Erase a large portion of the screen: the whole screen, or the
  427. * whole line, or parts thereof.
  428. */
  429. static void erase_lots(Session *s, int line_only, int from_begin, int to_end) {
  430. unsigned long *startpos, *endpos;
  431. if (line_only) {
  432. startpos = cpos - curs_x;
  433. endpos = startpos + cols+1;
  434. } else {
  435. startpos = scrtop;
  436. endpos = startpos + rows * (cols+1);
  437. }
  438. if (!from_begin)
  439. startpos = cpos;
  440. if (!to_end)
  441. endpos = cpos;
  442. check_selection(s, startpos, endpos);
  443. while (startpos < endpos)
  444. *startpos++ = ERASE_CHAR;
  445. }
  446. /*
  447. * Insert or delete characters within the current line. n is +ve if
  448. * insertion is desired, and -ve for deletion.
  449. */
  450. static void insch(Session *s, int n) {
  451. int dir = (n < 0 ? -1 : +1);
  452. int m;
  453. n = (n < 0 ? -n : n);
  454. if (n > cols - curs_x)
  455. n = cols - curs_x;
  456. m = cols - curs_x - n;
  457. check_selection(s, cpos, cpos+n);
  458. if (dir < 0) {
  459. memmove (cpos, cpos+n, m*TSIZE);
  460. while (n--)
  461. cpos[m++] = ERASE_CHAR;
  462. } else {
  463. memmove (cpos+n, cpos, m*TSIZE);
  464. while (n--)
  465. cpos[n] = ERASE_CHAR;
  466. }
  467. }
  468. /*
  469. * Toggle terminal mode `mode' to state `state'. (`query' indicates
  470. * whether the mode is a DEC private one or a normal one.)
  471. */
  472. static void toggle_mode(Session *s, int mode, int query, int state) {
  473. if (query) switch (mode) {
  474. case 1: /* application cursor keys */
  475. app_cursor_keys = state;
  476. break;
  477. case 3: /* 80/132 columns */
  478. deselect(s);
  479. request_resize(s, state ? 132 : 80, rows);
  480. break;
  481. case 5: /* reverse video */
  482. rvideo = state;
  483. disptop = scrtop;
  484. break;
  485. case 6: /* DEC origin mode */
  486. curr_dec_om = state;
  487. break;
  488. case 7: /* auto wrap */
  489. wrap = state;
  490. break;
  491. case 47: /* alternate screen */
  492. deselect(s);
  493. swap_screen(s, state);
  494. disptop = scrtop;
  495. break;
  496. } else switch (mode) {
  497. case 4: /* set insert mode */
  498. insert = state;
  499. break;
  500. }
  501. }
  502. /*
  503. * Process an OSC sequence: set window title or icon name.
  504. */
  505. static void do_osc(Session *s) {
  506. if (osc_w) {
  507. while (osc_strlen--)
  508. curr_wordness[(unsigned char)osc_string[osc_strlen]] = esc_args[0];
  509. } else {
  510. osc_string[osc_strlen] = '\0';
  511. switch (esc_args[0]) {
  512. case 0:
  513. case 1:
  514. set_icon (s, osc_string);
  515. if (esc_args[0] == 1)
  516. break;
  517. /* fall through: parameter 0 means set both */
  518. case 2:
  519. case 21:
  520. set_title(s, osc_string);
  521. break;
  522. }
  523. }
  524. }
  525. /*
  526. * Remove everything currently in `inbuf' and stick it up on the
  527. * in-memory display. There's a big state machine in here to
  528. * process escape sequences...
  529. */
  530. void term_out(Session *s) {
  531. int c;
  532. int must_update = FALSE;
  533. while ( (c = inbuf_getc(s)) != -1) {
  534. #ifdef LOG
  535. {
  536. static FILE *fp = NULL;
  537. if (!fp) fp = fopen("putty.log", "wb");
  538. if (fp) fputc (c, fp);
  539. }
  540. #endif
  541. switch (termstate) {
  542. case TOPLEVEL:
  543. do_toplevel:
  544. switch (c) {
  545. case '\005': /* terminal type query */
  546. back->send(s, "\033[?1;2c", 7);
  547. break;
  548. case '\007':
  549. beep(s);
  550. disptop = scrtop;
  551. must_update = TRUE;
  552. break;
  553. case '\b':
  554. if (curs_x == 0 && curs_y > 0)
  555. curs_x = cols-1, curs_y--;
  556. else if (wrapnext)
  557. wrapnext = FALSE;
  558. else
  559. curs_x--;
  560. fix_cpos;
  561. disptop = scrtop;
  562. must_update = TRUE;
  563. break;
  564. case '\016':
  565. cset = 1;
  566. break;
  567. case '\017':
  568. cset = 0;
  569. break;
  570. case '\033':
  571. termstate = SEEN_ESC;
  572. break;
  573. case 0233:
  574. termstate = SEEN_CSI;
  575. esc_nargs = 1;
  576. esc_args[0] = ARG_DEFAULT;
  577. esc_query = FALSE;
  578. break;
  579. case 0235:
  580. termstate = SEEN_OSC;
  581. esc_args[0] = 0;
  582. break;
  583. case '\015':
  584. curs_x = 0;
  585. wrapnext = FALSE;
  586. fix_cpos;
  587. disptop = scrtop;
  588. must_update = TRUE;
  589. break;
  590. case '\013':
  591. case '\014':
  592. case '\012':
  593. if (curs_y == marg_b)
  594. scroll(s, marg_t, marg_b, 1, TRUE);
  595. else if (curs_y < rows-1)
  596. curs_y++;
  597. if (cfg.lfhascr)
  598. curs_x = 0;
  599. fix_cpos;
  600. wrapnext = FALSE;
  601. disptop = scrtop;
  602. nl_count++;
  603. break;
  604. case '\t':
  605. do {
  606. curs_x++;
  607. } while (curs_x < cols-1 && !tabs[curs_x]);
  608. if (curs_x >= cols)
  609. curs_x = cols-1;
  610. {
  611. unsigned long *old_cpos = cpos;
  612. fix_cpos;
  613. check_selection(s, old_cpos, cpos);
  614. }
  615. disptop = scrtop;
  616. must_update = TRUE;
  617. break;
  618. default:
  619. if (c >= ' ' && c != 0234) {
  620. if (wrapnext) {
  621. cpos[1] = ATTR_WRAPPED;
  622. if (curs_y == marg_b)
  623. scroll(s, marg_t, marg_b, 1, TRUE);
  624. else if (curs_y < rows-1)
  625. curs_y++;
  626. curs_x = 0;
  627. fix_cpos;
  628. wrapnext = FALSE;
  629. nl_count++;
  630. }
  631. if (insert)
  632. insch(s, 1);
  633. check_selection(s, cpos, cpos+1);
  634. *cpos++ = c | curr_attr |
  635. (c <= 0x7F ? cset_attr[cset] : ATTR_ASCII);
  636. curs_x++;
  637. if (curs_x == cols) {
  638. cpos--;
  639. curs_x--;
  640. wrapnext = wrap;
  641. }
  642. disptop = scrtop;
  643. }
  644. }
  645. break;
  646. case IGNORE_NEXT:
  647. termstate = TOPLEVEL;
  648. break;
  649. case OSC_MAYBE_ST:
  650. /*
  651. * This state is virtually identical to SEEN_ESC, with the
  652. * exception that we have an OSC sequence in the pipeline,
  653. * and _if_ we see a backslash, we process it.
  654. */
  655. if (c == '\\') {
  656. do_osc(s);
  657. termstate = TOPLEVEL;
  658. break;
  659. }
  660. /* else fall through */
  661. case SEEN_ESC:
  662. termstate = TOPLEVEL;
  663. switch (c) {
  664. case '\005': case '\007': case '\b': case '\016': case '\017':
  665. case '\033': case 0233: case 0234: case 0235: case '\015':
  666. case '\013': case '\014': case '\012': case '\t':
  667. termstate = TOPLEVEL;
  668. goto do_toplevel; /* hack... */
  669. case ' ': /* some weird sequence? */
  670. termstate = IGNORE_NEXT;
  671. break;
  672. case '[': /* enter CSI mode */
  673. termstate = SEEN_CSI;
  674. esc_nargs = 1;
  675. esc_args[0] = ARG_DEFAULT;
  676. esc_query = FALSE;
  677. break;
  678. case ']': /* xterm escape sequences */
  679. termstate = SEEN_OSC;
  680. esc_args[0] = 0;
  681. break;
  682. case '(': /* should set GL */
  683. termstate = SET_GL;
  684. break;
  685. case ')': /* should set GR */
  686. termstate = SET_GR;
  687. break;
  688. case '7': /* save cursor */
  689. save_cursor(s, TRUE);
  690. break;
  691. case '8': /* restore cursor */
  692. save_cursor(s, FALSE);
  693. disptop = scrtop;
  694. must_update = TRUE;
  695. break;
  696. case '=':
  697. app_keypad_keys = TRUE;
  698. break;
  699. case '>':
  700. app_keypad_keys = FALSE;
  701. break;
  702. case 'D': /* exactly equivalent to LF */
  703. if (curs_y == marg_b)
  704. scroll(s, marg_t, marg_b, 1, TRUE);
  705. else if (curs_y < rows-1)
  706. curs_y++;
  707. fix_cpos;
  708. wrapnext = FALSE;
  709. disptop = scrtop;
  710. nl_count++;
  711. break;
  712. case 'E': /* exactly equivalent to CR-LF */
  713. curs_x = 0;
  714. wrapnext = FALSE;
  715. if (curs_y == marg_b)
  716. scroll(s, marg_t, marg_b, 1, TRUE);
  717. else if (curs_y < rows-1)
  718. curs_y++;
  719. fix_cpos;
  720. wrapnext = FALSE;
  721. nl_count++;
  722. disptop = scrtop;
  723. break;
  724. case 'M': /* reverse index - backwards LF */
  725. if (curs_y == marg_t)
  726. scroll(s, marg_t, marg_b, -1, TRUE);
  727. else if (curs_y > 0)
  728. curs_y--;
  729. fix_cpos;
  730. wrapnext = FALSE;
  731. disptop = scrtop;
  732. must_update = TRUE;
  733. break;
  734. case 'Z': /* terminal type query */
  735. back->send(s, "\033[?6c", 5);
  736. break;
  737. case 'c': /* restore power-on settings */
  738. power_on(s);
  739. fix_cpos;
  740. disptop = scrtop;
  741. must_update = TRUE;
  742. break;
  743. case '#': /* ESC # 8 fills screen with Es :-) */
  744. termstate = SEEN_ESCHASH;
  745. break;
  746. case 'H': /* set a tab */
  747. tabs[curs_x] = TRUE;
  748. break;
  749. }
  750. break;
  751. case SEEN_CSI:
  752. termstate = TOPLEVEL; /* default */
  753. switch (c) {
  754. case '\005': case '\007': case '\b': case '\016': case '\017':
  755. case '\033': case 0233: case 0234: case 0235: case '\015':
  756. case '\013': case '\014': case '\012': case '\t':
  757. termstate = TOPLEVEL;
  758. goto do_toplevel; /* hack... */
  759. case '0': case '1': case '2': case '3': case '4':
  760. case '5': case '6': case '7': case '8': case '9':
  761. if (esc_nargs <= ARGS_MAX) {
  762. if (esc_args[esc_nargs-1] == ARG_DEFAULT)
  763. esc_args[esc_nargs-1] = 0;
  764. esc_args[esc_nargs-1] =
  765. 10 * esc_args[esc_nargs-1] + c - '0';
  766. }
  767. termstate = SEEN_CSI;
  768. break;
  769. case ';':
  770. if (++esc_nargs <= ARGS_MAX)
  771. esc_args[esc_nargs-1] = ARG_DEFAULT;
  772. termstate = SEEN_CSI;
  773. break;
  774. case '?':
  775. esc_query = TRUE;
  776. termstate = SEEN_CSI;
  777. break;
  778. case 'A': /* move up N lines */
  779. move(s, curs_x, curs_y - def(esc_args[0], 1), 1);
  780. disptop = scrtop;
  781. must_update = TRUE;
  782. break;
  783. case 'B': case 'e': /* move down N lines */
  784. move(s, curs_x, curs_y + def(esc_args[0], 1), 1);
  785. disptop = scrtop;
  786. must_update = TRUE;
  787. break;
  788. case 'C': case 'a': /* move right N cols */
  789. move(s, curs_x + def(esc_args[0], 1), curs_y, 1);
  790. disptop = scrtop;
  791. must_update = TRUE;
  792. break;
  793. case 'D': /* move left N cols */
  794. move(s, curs_x - def(esc_args[0], 1), curs_y, 1);
  795. disptop = scrtop;
  796. must_update = TRUE;
  797. break;
  798. case 'E': /* move down N lines and CR */
  799. move(s, 0, curs_y + def(esc_args[0], 1), 1);
  800. disptop = scrtop;
  801. must_update = TRUE;
  802. break;
  803. case 'F': /* move up N lines and CR */
  804. move(s, 0, curs_y - def(esc_args[0], 1), 1);
  805. disptop = scrtop;
  806. must_update = TRUE;
  807. break;
  808. case 'G': case '`': /* set horizontal posn */
  809. move(s, def(esc_args[0], 1) - 1, curs_y, 0);
  810. disptop = scrtop;
  811. must_update = TRUE;
  812. break;
  813. case 'd': /* set vertical posn */
  814. move(s, curs_x, (curr_dec_om ? marg_t : 0) + def(esc_args[0], 1) - 1,
  815. (curr_dec_om ? 2 : 0));
  816. disptop = scrtop;
  817. must_update = TRUE;
  818. break;
  819. case 'H': case 'f': /* set horz and vert posns at once */
  820. if (esc_nargs < 2)
  821. esc_args[1] = ARG_DEFAULT;
  822. move(s, def(esc_args[1], 1) - 1,
  823. (curr_dec_om ? marg_t : 0) + def(esc_args[0], 1) - 1,
  824. (curr_dec_om ? 2 : 0));
  825. disptop = scrtop;
  826. must_update = TRUE;
  827. break;
  828. case 'J': /* erase screen or parts of it */
  829. {
  830. unsigned int i = def(esc_args[0], 0) + 1;
  831. if (i > 3)
  832. i = 0;
  833. erase_lots(s, FALSE, !!(i & 2), !!(i & 1));
  834. }
  835. disptop = scrtop;
  836. must_update = TRUE;
  837. break;
  838. case 'K': /* erase line or parts of it */
  839. {
  840. unsigned int i = def(esc_args[0], 0) + 1;
  841. if (i > 3)
  842. i = 0;
  843. erase_lots(s, TRUE, !!(i & 2), !!(i & 1));
  844. }
  845. disptop = scrtop;
  846. must_update = TRUE;
  847. break;
  848. case 'L': /* insert lines */
  849. if (curs_y <= marg_b)
  850. scroll(s, curs_y, marg_b, -def(esc_args[0], 1), FALSE);
  851. disptop = scrtop;
  852. must_update = TRUE;
  853. break;
  854. case 'M': /* delete lines */
  855. if (curs_y <= marg_b)
  856. scroll(s, curs_y, marg_b, def(esc_args[0], 1), FALSE);
  857. disptop = scrtop;
  858. must_update = TRUE;
  859. break;
  860. case '@': /* insert chars */
  861. insch(s, def(esc_args[0], 1));
  862. disptop = scrtop;
  863. must_update = TRUE;
  864. break;
  865. case 'P': /* delete chars */
  866. insch(s, -def(esc_args[0], 1));
  867. disptop = scrtop;
  868. must_update = TRUE;
  869. break;
  870. case 'c': /* terminal type query */
  871. back->send(s, "\033[?6c", 5);
  872. break;
  873. case 'n': /* cursor position query */
  874. if (esc_args[0] == 6) {
  875. char buf[32];
  876. sprintf (buf, "\033[%d;%dR", curs_y + 1, curs_x + 1);
  877. back->send(s, buf, strlen(buf));
  878. }
  879. break;
  880. case 'h': /* toggle a mode to high */
  881. toggle_mode(s, esc_args[0], esc_query, TRUE);
  882. break;
  883. case 'l': /* toggle a mode to low */
  884. toggle_mode(s, esc_args[0], esc_query, FALSE);
  885. break;
  886. case 'g': /* clear tabs */
  887. if (esc_nargs == 1) {
  888. if (esc_args[0] == 0) {
  889. tabs[curs_x] = FALSE;
  890. } else if (esc_args[0] == 3) {
  891. int i;
  892. for (i = 0; i < cols; i++)
  893. tabs[i] = FALSE;
  894. }
  895. }
  896. break;
  897. case 'r': /* set scroll margins */
  898. if (esc_nargs <= 2) {
  899. int top, bot;
  900. top = def(esc_args[0], 1) - 1;
  901. if (top < 0)
  902. top = 0;
  903. bot = (esc_nargs <= 1 || esc_args[1] == 0 ? rows :
  904. def(esc_args[1], rows)) - 1;
  905. if (bot >= rows)
  906. bot = rows-1;
  907. if (top <= bot) {
  908. marg_t = top;
  909. marg_b = bot;
  910. curs_x = 0;
  911. /*
  912. * I used to think the cursor should be
  913. * placed at the top of the newly marginned
  914. * area. Apparently not: VMS TPU falls over
  915. * if so.
  916. */
  917. curs_y = 0;
  918. fix_cpos;
  919. disptop = scrtop;
  920. must_update = TRUE;
  921. }
  922. }
  923. break;
  924. case 'm': /* set graphics rendition */
  925. {
  926. int i;
  927. for (i=0; i<esc_nargs; i++) {
  928. switch (def(esc_args[i], 0)) {
  929. case 0: /* restore defaults */
  930. curr_attr = ATTR_DEFAULT; break;
  931. case 1: /* enable bold */
  932. curr_attr |= ATTR_BOLD; break;
  933. case 4: /* enable underline */
  934. case 21: /* (enable double underline) */
  935. curr_attr |= ATTR_UNDER; break;
  936. case 7: /* enable reverse video */
  937. curr_attr |= ATTR_REVERSE; break;
  938. case 22: /* disable bold */
  939. curr_attr &= ~ATTR_BOLD; break;
  940. case 24: /* disable underline */
  941. curr_attr &= ~ATTR_UNDER; break;
  942. case 27: /* disable reverse video */
  943. curr_attr &= ~ATTR_REVERSE; break;
  944. case 30: case 31: case 32: case 33:
  945. case 34: case 35: case 36: case 37:
  946. /* foreground */
  947. curr_attr &= ~ATTR_FGMASK;
  948. curr_attr |= (esc_args[i] - 30) << ATTR_FGSHIFT;
  949. break;
  950. case 39: /* default-foreground */
  951. curr_attr &= ~ATTR_FGMASK;
  952. curr_attr |= ATTR_DEFFG;
  953. break;
  954. case 40: case 41: case 42: case 43:
  955. case 44: case 45: case 46: case 47:
  956. /* background */
  957. curr_attr &= ~ATTR_BGMASK;
  958. curr_attr |= (esc_args[i] - 40) << ATTR_BGSHIFT;
  959. break;
  960. case 49: /* default-background */
  961. curr_attr &= ~ATTR_BGMASK;
  962. curr_attr |= ATTR_DEFBG;
  963. break;
  964. }
  965. }
  966. }
  967. break;
  968. case 's': /* save cursor */
  969. save_cursor(s, TRUE);
  970. break;
  971. case 'u': /* restore cursor */
  972. save_cursor(s, FALSE);
  973. disptop = scrtop;
  974. must_update = TRUE;
  975. break;
  976. case 't': /* set page size - ie window height */
  977. request_resize(s, cols, def(esc_args[0], 24));
  978. deselect(s);
  979. break;
  980. case 'X': /* write N spaces w/o moving cursor */
  981. {
  982. int n = def(esc_args[0], 1);
  983. unsigned long *p = cpos;
  984. if (n > cols - curs_x)
  985. n = cols - curs_x;
  986. check_selection(s, cpos, cpos+n);
  987. while (n--)
  988. *p++ = ERASE_CHAR;
  989. disptop = scrtop;
  990. must_update = TRUE;
  991. }
  992. break;
  993. case 'x': /* report terminal characteristics */
  994. {
  995. char buf[32];
  996. int i = def(esc_args[0], 0);
  997. if (i == 0 || i == 1) {
  998. strcpy (buf, "\033[2;1;1;112;112;1;0x");
  999. buf[2] += i;
  1000. back->send(s, buf, 20);
  1001. }
  1002. }
  1003. break;
  1004. }
  1005. break;
  1006. case SET_GL:
  1007. case SET_GR:
  1008. switch (c) {
  1009. case 'A':
  1010. cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_GBCHR;
  1011. break;
  1012. case '0':
  1013. cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_LINEDRW;
  1014. break;
  1015. default: /* specifically, 'B' */
  1016. cset_attr[termstate == SET_GL ? 0 : 1] = ATTR_ASCII;
  1017. break;
  1018. }
  1019. termstate = TOPLEVEL;
  1020. break;
  1021. case SEEN_OSC:
  1022. osc_w = FALSE;
  1023. switch (c) {
  1024. case '\005': case '\007': case '\b': case '\016': case '\017':
  1025. case '\033': case 0233: case 0234: case 0235: case '\015':
  1026. case '\013': case '\014': case '\012': case '\t':
  1027. termstate = TOPLEVEL;
  1028. goto do_toplevel; /* hack... */
  1029. case 'P': /* Linux palette sequence */
  1030. termstate = SEEN_OSC_P;
  1031. osc_strlen = 0;
  1032. break;
  1033. case 'R': /* Linux palette reset */
  1034. palette_reset(s);
  1035. term_invalidate(s);
  1036. termstate = TOPLEVEL;
  1037. break;
  1038. case 'W': /* word-set */
  1039. termstate = SEEN_OSC_W;
  1040. osc_w = TRUE;
  1041. break;
  1042. case '0': case '1': case '2': case '3': case '4':
  1043. case '5': case '6': case '7': case '8': case '9':
  1044. esc_args[0] = 10 * esc_args[0] + c - '0';
  1045. break;
  1046. case 'L':
  1047. /*
  1048. * Grotty hack to support xterm and DECterm title
  1049. * sequences concurrently.
  1050. */
  1051. if (esc_args[0] == 2) {
  1052. esc_args[0] = 1;
  1053. break;
  1054. }
  1055. /* else fall through */
  1056. default:
  1057. termstate = OSC_STRING;
  1058. osc_strlen = 0;
  1059. }
  1060. break;
  1061. case OSC_STRING:
  1062. if (c == 0234 || c == '\007') {
  1063. /*
  1064. * These characters terminate the string; ST and BEL
  1065. * terminate the sequence and trigger instant
  1066. * processing of it, whereas ESC goes back to SEEN_ESC
  1067. * mode unless it is followed by \, in which case it is
  1068. * synonymous with ST in the first place.
  1069. */
  1070. do_osc(s);
  1071. termstate = TOPLEVEL;
  1072. } else if (c == '\033')
  1073. termstate = OSC_MAYBE_ST;
  1074. else if (osc_strlen < OSC_STR_MAX)
  1075. osc_string[osc_strlen++] = c;
  1076. break;
  1077. case SEEN_OSC_P:
  1078. {
  1079. int max = (osc_strlen == 0 ? 21 : 16);
  1080. int val;
  1081. if (c >= '0' && c <= '9')
  1082. val = c - '0';
  1083. else if (c >= 'A' && c <= 'A'+max-10)
  1084. val = c - 'A' + 10;
  1085. else if (c >= 'a' && c <= 'a'+max-10)
  1086. val = c - 'a' + 10;
  1087. else
  1088. termstate = TOPLEVEL;
  1089. osc_string[osc_strlen++] = val;
  1090. if (osc_strlen >= 7) {
  1091. palette_set (s, osc_string[0],
  1092. osc_string[1] * 16 + osc_string[2],
  1093. osc_string[3] * 16 + osc_string[4],
  1094. osc_string[5] * 16 + osc_string[6]);
  1095. termstate = TOPLEVEL;
  1096. }
  1097. }
  1098. break;
  1099. case SEEN_OSC_W:
  1100. switch (c) {
  1101. case '\005': case '\007': case '\b': case '\016': case '\017':
  1102. case '\033': case 0233: case 0234: case 0235: case '\015':
  1103. case '\013': case '\014': case '\012': case '\t':
  1104. termstate = TOPLEVEL;
  1105. goto do_toplevel; /* hack... */
  1106. case '0': case '1': case '2': case '3': case '4':
  1107. case '5': case '6': case '7': case '8': case '9':
  1108. esc_args[0] = 10 * esc_args[0] + c - '0';
  1109. break;
  1110. default:
  1111. termstate = OSC_STRING;
  1112. osc_strlen = 0;
  1113. }
  1114. break;
  1115. case SEEN_ESCHASH:
  1116. if (c == '8') {
  1117. unsigned long *p = scrtop;
  1118. int n = rows * (cols+1);
  1119. while (n--)
  1120. *p++ = ATTR_DEFAULT | 'E';
  1121. disptop = scrtop;
  1122. must_update = TRUE;
  1123. check_selection(s, scrtop, scrtop + rows * (cols+1));
  1124. }
  1125. termstate = TOPLEVEL;
  1126. break;
  1127. }
  1128. check_selection(s, cpos, cpos+1);
  1129. }
  1130. if (must_update || nl_count > MAXNL) {
  1131. update_sbar(s);
  1132. term_update(s);
  1133. }
  1134. }
  1135. /*
  1136. * Given a context, update the window. Out of paranoia, we don't
  1137. * allow WM_PAINT responses to do scrolling optimisations.
  1138. */
  1139. static void do_paint (Session *s, int may_optimise){
  1140. int i, j, start, our_curs_y;
  1141. unsigned long attr, rv, cursor;
  1142. char ch[1024];
  1143. cursor = (has_focus ? ATTR_ACTCURS : ATTR_PASCURS);
  1144. rv = (rvideo ? ATTR_REVERSE : 0);
  1145. our_curs_y = curs_y + (scrtop - disptop) / (cols+1);
  1146. for (i=0; i<rows; i++) {
  1147. int idx = i*(cols+1);
  1148. for (j=0; j<=cols; j++,idx++) {
  1149. unsigned long *d = disptop+idx;
  1150. wanttext[idx] = ((*d ^ rv
  1151. ^ (selstart <= d && d < selend ?
  1152. ATTR_REVERSE : 0)) |
  1153. (i==our_curs_y && j==curs_x ? cursor : 0));
  1154. }
  1155. }
  1156. /*
  1157. * We would perform scrolling optimisations in here, if they
  1158. * didn't have a nasty tendency to cause the whole sodding
  1159. * program to hang for a second at speed-critical moments.
  1160. * We'll leave it well alone...
  1161. */
  1162. for (i=0; i<rows; i++) {
  1163. int idx = i*(cols+1);
  1164. start = -1;
  1165. for (j=0; j<=cols; j++,idx++) {
  1166. unsigned long t = wanttext[idx];
  1167. int needs_update = (j < cols && t != disptext[idx]);
  1168. int keep_going = (start != -1 && needs_update &&
  1169. (t & attr_mask) == attr &&
  1170. j-start < sizeof(ch));
  1171. if (start != -1 && !keep_going) {
  1172. do_text(s, start, i, ch, j-start, attr);
  1173. start = -1;
  1174. }
  1175. if (needs_update) {
  1176. if (start == -1) {
  1177. start = j;
  1178. attr = t & attr_mask;
  1179. }
  1180. ch[j-start] = (char) (t & CHAR_MASK);
  1181. }
  1182. disptext[idx] = t;
  1183. }
  1184. }
  1185. }
  1186. /*
  1187. * Invalidate the whole screen so it will be repainted in full.
  1188. */
  1189. void term_invalidate(Session *s) {
  1190. int i;
  1191. for (i=0; i<rows*(cols+1); i++)
  1192. disptext[i] = ATTR_INVALID;
  1193. }
  1194. /*
  1195. * Paint the window in response to a WM_PAINT message.
  1196. */
  1197. void term_paint(Session *s, int l, int t, int r, int b) {
  1198. int i, j, left, top, right, bottom;
  1199. left = l / font_width;
  1200. right = (r - 1) / font_width;
  1201. top = t / font_height;
  1202. bottom = (b - 1) / font_height;
  1203. for (i = top; i <= bottom && i < rows ; i++)
  1204. for (j = left; j <= right && j < cols ; j++)
  1205. disptext[i*(cols+1)+j] = ATTR_INVALID;
  1206. do_paint (s, FALSE);
  1207. }
  1208. /*
  1209. * Attempt to scroll the scrollback. The second parameter gives the
  1210. * position we want to scroll to; the first is +1 to denote that
  1211. * this position is relative to the beginning of the scrollback, -1
  1212. * to denote it is relative to the end, and 0 to denote that it is
  1213. * relative to the current position.
  1214. */
  1215. void term_scroll(Session *s, int rel, int where) {
  1216. int n = where * (cols+1);
  1217. #ifdef OPTIMISE_SCROLL
  1218. unsigned long *olddisptop = disptop;
  1219. int shift;
  1220. #endif /* OPTIMISE_SCROLL */
  1221. disptop = (rel < 0 ? scrtop :
  1222. rel > 0 ? sbtop : disptop) + n;
  1223. if (disptop < sbtop)
  1224. disptop = sbtop;
  1225. if (disptop > scrtop)
  1226. disptop = scrtop;
  1227. update_sbar(s);
  1228. #ifdef OPTIMISE_SCROLL
  1229. shift = (disptop - olddisptop) / (cols + 1);
  1230. if (shift < rows && shift > -rows)
  1231. scroll_display(s, 0, rows - 1, shift);
  1232. #endif /* OPTIMISE_SCROLL */
  1233. term_update(s);
  1234. }
  1235. /*
  1236. * Spread the selection outwards according to the selection mode.
  1237. */
  1238. static unsigned long *sel_spread_half(Session *s, unsigned long *p, int dir) {
  1239. unsigned long *linestart, *lineend;
  1240. int x;
  1241. short wvalue;
  1242. x = (p - text) % (cols+1);
  1243. linestart = p - x;
  1244. lineend = linestart + cols;
  1245. switch (selmode) {
  1246. case SM_CHAR:
  1247. /*
  1248. * In this mode, every character is a separate unit, except
  1249. * for runs of spaces at the end of a non-wrapping line.
  1250. */
  1251. if (!(linestart[cols] & ATTR_WRAPPED)) {
  1252. unsigned long *q = lineend;
  1253. while (q > linestart && (q[-1] & CHAR_MASK) == 0x20)
  1254. q--;
  1255. if (q == lineend)
  1256. q--;
  1257. if (p >= q)
  1258. p = (dir == -1 ? q : lineend - 1);
  1259. }
  1260. break;
  1261. case SM_WORD:
  1262. /*
  1263. * In this mode, the units are maximal runs of characters
  1264. * whose `wordness' has the same value.
  1265. */
  1266. wvalue = curr_wordness[*p & CHAR_MASK];
  1267. if (dir == +1) {
  1268. while (p < lineend && curr_wordness[p[1] & CHAR_MASK] == wvalue)
  1269. p++;
  1270. } else {
  1271. while (p > linestart && curr_wordness[p[-1] & CHAR_MASK] == wvalue)
  1272. p--;
  1273. }
  1274. break;
  1275. case SM_LINE:
  1276. /*
  1277. * In this mode, every line is a unit.
  1278. */
  1279. p = (dir == -1 ? linestart : lineend - 1);
  1280. break;
  1281. }
  1282. return p;
  1283. }
  1284. static void sel_spread(Session *s) {
  1285. selstart = sel_spread_half(s, selstart, -1);
  1286. selend = sel_spread_half(s, selend - 1, +1) + 1;
  1287. }
  1288. void term_mouse(Session *s, Mouse_Button b, Mouse_Action a, int x, int y) {
  1289. unsigned long *selpoint;
  1290. if (x < 0) {
  1291. x = cols - 1;
  1292. y--;
  1293. } else if (x >= cols)
  1294. x = cols - 1;
  1295. selpoint = disptop + y * (cols + 1) + x;
  1296. if (selpoint < sbtop)
  1297. selpoint = sbtop;
  1298. else if (selpoint > scrtop + rows * (cols + 1) - 1)
  1299. /* XXX put this in a variable? */
  1300. selpoint = scrtop + rows * (cols + 1) - 1;
  1301. if (b == MB_SELECT && a == MA_CLICK) {
  1302. deselect(s);
  1303. selstate = ABOUT_TO;
  1304. selanchor = selpoint;
  1305. selmode = SM_CHAR;
  1306. } else if (b == MB_SELECT && (a == MA_2CLK || a == MA_3CLK)) {
  1307. deselect(s);
  1308. selmode = (a == MA_2CLK ? SM_WORD : SM_LINE);
  1309. selstate = DRAGGING;
  1310. selstart = selanchor = selpoint;
  1311. selend = selstart + 1;
  1312. sel_spread(s);
  1313. } else if ((b == MB_SELECT && a == MA_DRAG) ||
  1314. (b == MB_EXTEND && a != MA_RELEASE)) {
  1315. if (selstate == ABOUT_TO && selanchor == selpoint)
  1316. return;
  1317. if (b == MB_EXTEND && a != MA_DRAG && selstate == SELECTED) {
  1318. if (selpoint-selstart < (selend-selstart)/2)
  1319. selanchor = selend - 1;
  1320. else
  1321. selanchor = selstart;
  1322. selstate = DRAGGING;
  1323. }
  1324. if (selstate != ABOUT_TO && selstate != DRAGGING)
  1325. selanchor = selpoint;
  1326. selstate = DRAGGING;
  1327. if (selpoint < selanchor) {
  1328. selstart = selpoint;
  1329. selend = selanchor + 1;
  1330. } else {
  1331. selstart = selanchor;
  1332. selend = selpoint + 1;
  1333. }
  1334. sel_spread(s);
  1335. } else if ((b == MB_SELECT || b == MB_EXTEND) && a == MA_RELEASE)
  1336. if (selstate == DRAGGING) {
  1337. if (cfg.implicit_copy)
  1338. term_copy(s);
  1339. selstate = SELECTED;
  1340. } else
  1341. selstate = NO_SELECTION;
  1342. else if (b == MB_PASTE && (a==MA_CLICK || a==MA_2CLK || a==MA_3CLK))
  1343. term_paste(s);
  1344. term_update(s);
  1345. }
  1346. /*
  1347. * We've completed a selection. We now transfer the
  1348. * data to the clipboard.
  1349. */
  1350. void term_copy(Session *s) {
  1351. unsigned char *p = selspace;
  1352. unsigned long *q = selstart;
  1353. while (q < selend) {
  1354. int nl = FALSE;
  1355. unsigned long *lineend = q - (q-text) % (cols+1) + cols;
  1356. unsigned long *nlpos = lineend;
  1357. if (!(*nlpos & ATTR_WRAPPED)) {
  1358. while ((nlpos[-1] & CHAR_MASK) == 0x20 && nlpos > q)
  1359. nlpos--;
  1360. if (nlpos < selend)
  1361. nl = TRUE;
  1362. }
  1363. while (q < nlpos && q < selend)
  1364. *p++ = (unsigned char) (*q++ & CHAR_MASK);
  1365. if (nl) {
  1366. int i;
  1367. for (i=0; i<sizeof(sel_nl); i++)
  1368. *p++ = sel_nl[i];
  1369. }
  1370. q = lineend + 1; /* start of next line */
  1371. }
  1372. write_clip(selspace, p - selspace);
  1373. }
  1374. void term_paste(Session *s) {
  1375. char *data;
  1376. int len;
  1377. get_clip((void **) &data, &len);
  1378. if (data) {
  1379. char *p, *q;
  1380. p = q = data;
  1381. while (p < data+len) {
  1382. while (p < data+len &&
  1383. !(p <= data+len-sizeof(sel_nl) &&
  1384. !memcmp(p, sel_nl, sizeof(sel_nl))))
  1385. p++;
  1386. back->send(s, q, p-q);
  1387. if (p <= data+len-sizeof(sel_nl) &&
  1388. !memcmp(p, sel_nl, sizeof(sel_nl))) {
  1389. back->send(s, "\015", 1);
  1390. p += sizeof(sel_nl);
  1391. }
  1392. q = p;
  1393. }
  1394. }
  1395. get_clip(NULL, NULL);
  1396. }
  1397. /*
  1398. * Find out if there's a selection.
  1399. */
  1400. int term_hasselection(Session *s) {
  1401. return selstate == SELECTED;
  1402. }
  1403. static void deselect(Session *s) {
  1404. selstate = NO_SELECTION;
  1405. selstart = selend = scrtop;
  1406. }
  1407. void term_deselect(Session *s) {
  1408. deselect(s);
  1409. term_update(s);
  1410. }