screen.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. /* $OpenBSD$ */
  2. /*
  3. * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
  4. *
  5. * Permission to use, copy, modify, and distribute this software for any
  6. * purpose with or without fee is hereby granted, provided that the above
  7. * copyright notice and this permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  10. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  12. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13. * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
  14. * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
  15. * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16. */
  17. #include <sys/types.h>
  18. #include <stdlib.h>
  19. #include <string.h>
  20. #include <unistd.h>
  21. #include "tmux.h"
  22. void screen_resize_x(struct screen *, u_int);
  23. void screen_resize_y(struct screen *, u_int);
  24. /* Create a new screen. */
  25. void
  26. screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit)
  27. {
  28. s->grid = grid_create(sx, sy, hlimit);
  29. s->title = xstrdup("");
  30. s->cstyle = 0;
  31. s->ccolour = xstrdup("");
  32. s->tabs = NULL;
  33. screen_reinit(s);
  34. }
  35. /* Reinitialise screen. */
  36. void
  37. screen_reinit(struct screen *s)
  38. {
  39. s->cx = 0;
  40. s->cy = 0;
  41. s->rupper = 0;
  42. s->rlower = screen_size_y(s) - 1;
  43. s->mode = MODE_CURSOR | MODE_WRAP;
  44. screen_reset_tabs(s);
  45. grid_clear_lines(s->grid, s->grid->hsize, s->grid->sy);
  46. screen_clear_selection(s);
  47. }
  48. /* Destroy a screen. */
  49. void
  50. screen_free(struct screen *s)
  51. {
  52. free(s->tabs);
  53. free(s->title);
  54. free(s->ccolour);
  55. grid_destroy(s->grid);
  56. }
  57. /* Reset tabs to default, eight spaces apart. */
  58. void
  59. screen_reset_tabs(struct screen *s)
  60. {
  61. u_int i;
  62. free(s->tabs);
  63. if ((s->tabs = bit_alloc(screen_size_x(s))) == NULL)
  64. fatal("bit_alloc failed");
  65. for (i = 8; i < screen_size_x(s); i += 8)
  66. bit_set(s->tabs, i);
  67. }
  68. /* Set screen cursor style. */
  69. void
  70. screen_set_cursor_style(struct screen *s, u_int style)
  71. {
  72. if (style <= 6)
  73. s->cstyle = style;
  74. }
  75. /* Set screen cursor colour. */
  76. void
  77. screen_set_cursor_colour(struct screen *s, const char *colour)
  78. {
  79. free(s->ccolour);
  80. s->ccolour = xstrdup(colour);
  81. }
  82. /* Set screen title. */
  83. void
  84. screen_set_title(struct screen *s, const char *title)
  85. {
  86. free(s->title);
  87. s->title = xstrdup(title);
  88. }
  89. /* Resize screen. */
  90. void
  91. screen_resize(struct screen *s, u_int sx, u_int sy, int reflow)
  92. {
  93. if (sx < 1)
  94. sx = 1;
  95. if (sy < 1)
  96. sy = 1;
  97. if (sx != screen_size_x(s)) {
  98. screen_resize_x(s, sx);
  99. /*
  100. * It is unclear what should happen to tabs on resize. xterm
  101. * seems to try and maintain them, rxvt resets them. Resetting
  102. * is simpler and more reliable so let's do that.
  103. */
  104. screen_reset_tabs(s);
  105. }
  106. if (sy != screen_size_y(s))
  107. screen_resize_y(s, sy);
  108. if (reflow)
  109. screen_reflow(s, sx);
  110. }
  111. void
  112. screen_resize_x(struct screen *s, u_int sx)
  113. {
  114. struct grid *gd = s->grid;
  115. if (sx == 0)
  116. fatalx("zero size");
  117. /*
  118. * Treat resizing horizontally simply: just ensure the cursor is
  119. * on-screen and change the size. Don't bother to truncate any lines -
  120. * then the data should be accessible if the size is then incrased.
  121. *
  122. * The only potential wrinkle is if UTF-8 double-width characters are
  123. * left in the last column, but UTF-8 terminals should deal with this
  124. * sanely.
  125. */
  126. if (s->cx >= sx)
  127. s->cx = sx - 1;
  128. gd->sx = sx;
  129. }
  130. void
  131. screen_resize_y(struct screen *s, u_int sy)
  132. {
  133. struct grid *gd = s->grid;
  134. u_int needed, available, oldy, i;
  135. if (sy == 0)
  136. fatalx("zero size");
  137. oldy = screen_size_y(s);
  138. /*
  139. * When resizing:
  140. *
  141. * If the height is decreasing, delete lines from the bottom until
  142. * hitting the cursor, then push lines from the top into the history.
  143. *
  144. * When increasing, pull as many lines as possible from the history to
  145. * the top, then fill the remaining with blanks at the bottom.
  146. */
  147. /* Size decreasing. */
  148. if (sy < oldy) {
  149. needed = oldy - sy;
  150. /* Delete as many lines as possible from the bottom. */
  151. available = oldy - 1 - s->cy;
  152. if (available > 0) {
  153. if (available > needed)
  154. available = needed;
  155. grid_view_delete_lines(gd, oldy - available, available);
  156. }
  157. needed -= available;
  158. /*
  159. * Now just increase the history size, if possible, to take
  160. * over the lines which are left. If history is off, delete
  161. * lines from the top.
  162. */
  163. available = s->cy;
  164. if (gd->flags & GRID_HISTORY)
  165. gd->hsize += needed;
  166. else if (needed > 0 && available > 0) {
  167. if (available > needed)
  168. available = needed;
  169. grid_view_delete_lines(gd, 0, available);
  170. }
  171. s->cy -= needed;
  172. }
  173. /* Resize line arrays. */
  174. gd->linedata = xreallocarray(gd->linedata, gd->hsize + sy,
  175. sizeof *gd->linedata);
  176. /* Size increasing. */
  177. if (sy > oldy) {
  178. needed = sy - oldy;
  179. /*
  180. * Try to pull as much as possible out of the history, if is
  181. * is enabled.
  182. */
  183. available = gd->hsize;
  184. if (gd->flags & GRID_HISTORY && available > 0) {
  185. if (available > needed)
  186. available = needed;
  187. gd->hsize -= available;
  188. s->cy += available;
  189. } else
  190. available = 0;
  191. needed -= available;
  192. /* Then fill the rest in with blanks. */
  193. for (i = gd->hsize + sy - needed; i < gd->hsize + sy; i++)
  194. memset(&gd->linedata[i], 0, sizeof gd->linedata[i]);
  195. }
  196. /* Set the new size, and reset the scroll region. */
  197. gd->sy = sy;
  198. s->rupper = 0;
  199. s->rlower = screen_size_y(s) - 1;
  200. }
  201. /* Set selection. */
  202. void
  203. screen_set_selection(struct screen *s, u_int sx, u_int sy,
  204. u_int ex, u_int ey, u_int rectflag, struct grid_cell *gc)
  205. {
  206. struct screen_sel *sel = &s->sel;
  207. memcpy(&sel->cell, gc, sizeof sel->cell);
  208. sel->flag = 1;
  209. sel->rectflag = rectflag;
  210. sel->sx = sx; sel->sy = sy;
  211. sel->ex = ex; sel->ey = ey;
  212. }
  213. /* Clear selection. */
  214. void
  215. screen_clear_selection(struct screen *s)
  216. {
  217. struct screen_sel *sel = &s->sel;
  218. sel->flag = 0;
  219. sel->lineflag = LINE_SEL_NONE;
  220. }
  221. /* Check if cell in selection. */
  222. int
  223. screen_check_selection(struct screen *s, u_int px, u_int py)
  224. {
  225. struct screen_sel *sel = &s->sel;
  226. u_int xx;
  227. if (!sel->flag)
  228. return (0);
  229. if (sel->rectflag) {
  230. if (sel->sy < sel->ey) {
  231. /* start line < end line -- downward selection. */
  232. if (py < sel->sy || py > sel->ey)
  233. return (0);
  234. } else if (sel->sy > sel->ey) {
  235. /* start line > end line -- upward selection. */
  236. if (py > sel->sy || py < sel->ey)
  237. return (0);
  238. } else {
  239. /* starting line == ending line. */
  240. if (py != sel->sy)
  241. return (0);
  242. }
  243. /*
  244. * Need to include the selection start row, but not the cursor
  245. * row, which means the selection changes depending on which
  246. * one is on the left.
  247. */
  248. if (sel->ex < sel->sx) {
  249. /* Cursor (ex) is on the left. */
  250. if (px < sel->ex)
  251. return (0);
  252. if (px > sel->sx)
  253. return (0);
  254. } else {
  255. /* Selection start (sx) is on the left. */
  256. if (px < sel->sx)
  257. return (0);
  258. if (px > sel->ex)
  259. return (0);
  260. }
  261. } else {
  262. /*
  263. * Like emacs, keep the top-left-most character, and drop the
  264. * bottom-right-most, regardless of copy direction.
  265. */
  266. if (sel->sy < sel->ey) {
  267. /* starting line < ending line -- downward selection. */
  268. if (py < sel->sy || py > sel->ey)
  269. return (0);
  270. if (py == sel->sy && px < sel->sx)
  271. return (0);
  272. if (py == sel->ey && px > sel->ex)
  273. return (0);
  274. } else if (sel->sy > sel->ey) {
  275. /* starting line > ending line -- upward selection. */
  276. if (py > sel->sy || py < sel->ey)
  277. return (0);
  278. if (py == sel->ey && px < sel->ex)
  279. return (0);
  280. if (sel->modekeys == MODEKEY_EMACS)
  281. xx = sel->sx - 1;
  282. else
  283. xx = sel->sx;
  284. if (py == sel->sy && px > xx)
  285. return (0);
  286. } else {
  287. /* starting line == ending line. */
  288. if (py != sel->sy)
  289. return (0);
  290. if (sel->ex < sel->sx) {
  291. /* cursor (ex) is on the left */
  292. if (sel->modekeys == MODEKEY_EMACS)
  293. xx = sel->sx - 1;
  294. else
  295. xx = sel->sx;
  296. if (px > xx || px < sel->ex)
  297. return (0);
  298. } else {
  299. /* selection start (sx) is on the left */
  300. if (px < sel->sx || px > sel->ex)
  301. return (0);
  302. }
  303. }
  304. }
  305. return (1);
  306. }
  307. /* Reflow wrapped lines. */
  308. void
  309. screen_reflow(struct screen *s, u_int new_x)
  310. {
  311. struct grid *old = s->grid;
  312. u_int change;
  313. s->grid = grid_create(old->sx, old->sy, old->hlimit);
  314. change = grid_reflow(s->grid, old, new_x);
  315. if (change < s->cy)
  316. s->cy -= change;
  317. else
  318. s->cy = 0;
  319. }