4 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
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.
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.
19 #include <sys/types.h>
27 void screen_resize_x(struct screen
*, u_int
);
28 void screen_resize_y(struct screen
*, u_int
);
30 /* Create a new screen. */
32 screen_init(struct screen
*s
, u_int sx
, u_int sy
, u_int hlimit
)
34 char hn
[MAXHOSTNAMELEN
];
36 s
->grid
= grid_create(sx
, sy
, hlimit
);
38 if (gethostname(hn
, MAXHOSTNAMELEN
) == 0)
39 s
->title
= xstrdup(hn
);
41 s
->title
= xstrdup("");
44 s
->ccolour
= xstrdup("");
50 /* Reinitialise screen. */
52 screen_reinit(struct screen
*s
)
58 s
->rlower
= screen_size_y(s
) - 1;
60 s
->mode
= MODE_CURSOR
| MODE_WRAP
;
64 grid_clear_lines(s
->grid
, s
->grid
->hsize
, s
->grid
->sy
);
66 screen_clear_selection(s
);
69 /* Destroy a screen. */
71 screen_free(struct screen
*s
)
76 grid_destroy(s
->grid
);
79 /* Reset tabs to default, eight spaces apart. */
81 screen_reset_tabs(struct screen
*s
)
87 if ((s
->tabs
= bit_alloc(screen_size_x(s
))) == NULL
)
88 fatal("bit_alloc failed");
89 for (i
= 8; i
< screen_size_x(s
); i
+= 8)
93 /* Set screen cursor style. */
95 screen_set_cursor_style(struct screen
*s
, u_int style
)
101 /* Set screen cursor colour. */
103 screen_set_cursor_colour(struct screen
*s
, const char *colour_string
)
106 s
->ccolour
= xstrdup(colour_string
);
109 /* Set screen title. */
111 screen_set_title(struct screen
*s
, const char *title
)
115 strlcpy(tmp
, title
, sizeof tmp
);
118 s
->title
= xstrdup(tmp
);
123 screen_resize(struct screen
*s
, u_int sx
, u_int sy
, int reflow
)
130 if (sx
!= screen_size_x(s
)) {
131 screen_resize_x(s
, sx
);
134 * It is unclear what should happen to tabs on resize. xterm
135 * seems to try and maintain them, rxvt resets them. Resetting
136 * is simpler and more reliable so let's do that.
138 screen_reset_tabs(s
);
141 if (sy
!= screen_size_y(s
))
142 screen_resize_y(s
, sy
);
145 screen_reflow(s
, sx
);
149 screen_resize_x(struct screen
*s
, u_int sx
)
151 struct grid
*gd
= s
->grid
;
157 * Treat resizing horizontally simply: just ensure the cursor is
158 * on-screen and change the size. Don't bother to truncate any lines -
159 * then the data should be accessible if the size is then incrased.
161 * The only potential wrinkle is if UTF-8 double-width characters are
162 * left in the last column, but UTF-8 terminals should deal with this
171 screen_resize_y(struct screen
*s
, u_int sy
)
173 struct grid
*gd
= s
->grid
;
174 u_int needed
, available
, oldy
, i
;
178 oldy
= screen_size_y(s
);
183 * If the height is decreasing, delete lines from the bottom until
184 * hitting the cursor, then push lines from the top into the history.
186 * When increasing, pull as many lines as possible from the history to
187 * the top, then fill the remaining with blanks at the bottom.
190 /* Size decreasing. */
194 /* Delete as many lines as possible from the bottom. */
195 available
= oldy
- 1 - s
->cy
;
197 if (available
> needed
)
199 grid_view_delete_lines(gd
, oldy
- available
, available
);
204 * Now just increase the history size, if possible, to take
205 * over the lines which are left. If history is off, delete
206 * lines from the top.
208 * XXX Should apply history limit?
211 if (gd
->flags
& GRID_HISTORY
)
213 else if (needed
> 0 && available
> 0) {
214 if (available
> needed
)
216 grid_view_delete_lines(gd
, 0, available
);
221 /* Resize line arrays. */
222 gd
->linedata
= xrealloc(
223 gd
->linedata
, gd
->hsize
+ sy
, sizeof *gd
->linedata
);
225 /* Size increasing. */
230 * Try to pull as much as possible out of the history, if is
233 available
= gd
->hsize
;
234 if (gd
->flags
& GRID_HISTORY
&& available
> 0) {
235 if (available
> needed
)
237 gd
->hsize
-= available
;
243 /* Then fill the rest in with blanks. */
244 for (i
= gd
->hsize
+ sy
- needed
; i
< gd
->hsize
+ sy
; i
++)
245 memset(&gd
->linedata
[i
], 0, sizeof gd
->linedata
[i
]);
248 /* Set the new size, and reset the scroll region. */
251 s
->rlower
= screen_size_y(s
) - 1;
256 screen_set_selection(struct screen
*s
, u_int sx
, u_int sy
,
257 u_int ex
, u_int ey
, u_int rectflag
, struct grid_cell
*gc
)
259 struct screen_sel
*sel
= &s
->sel
;
261 memcpy(&sel
->cell
, gc
, sizeof sel
->cell
);
263 sel
->rectflag
= rectflag
;
265 sel
->sx
= sx
; sel
->sy
= sy
;
266 sel
->ex
= ex
; sel
->ey
= ey
;
269 /* Clear selection. */
271 screen_clear_selection(struct screen
*s
)
273 struct screen_sel
*sel
= &s
->sel
;
278 /* Check if cell in selection. */
280 screen_check_selection(struct screen
*s
, u_int px
, u_int py
)
282 struct screen_sel
*sel
= &s
->sel
;
288 if (sel
->sy
< sel
->ey
) {
289 /* start line < end line -- downward selection. */
290 if (py
< sel
->sy
|| py
> sel
->ey
)
292 } else if (sel
->sy
> sel
->ey
) {
293 /* start line > end line -- upward selection. */
294 if (py
> sel
->sy
|| py
< sel
->ey
)
297 /* starting line == ending line. */
303 * Need to include the selection start row, but not the cursor
304 * row, which means the selection changes depending on which
305 * one is on the left.
307 if (sel
->ex
< sel
->sx
) {
308 /* Cursor (ex) is on the left. */
315 /* Selection start (sx) is on the left. */
324 * Like emacs, keep the top-left-most character, and drop the
325 * bottom-right-most, regardless of copy direction.
327 if (sel
->sy
< sel
->ey
) {
328 /* starting line < ending line -- downward selection. */
329 if (py
< sel
->sy
|| py
> sel
->ey
)
332 if ((py
== sel
->sy
&& px
< sel
->sx
)
333 || (py
== sel
->ey
&& px
> sel
->ex
))
335 } else if (sel
->sy
> sel
->ey
) {
336 /* starting line > ending line -- upward selection. */
337 if (py
> sel
->sy
|| py
< sel
->ey
)
340 if ((py
== sel
->sy
&& px
>= sel
->sx
)
341 || (py
== sel
->ey
&& px
< sel
->ex
))
344 /* starting line == ending line. */
348 if (sel
->ex
< sel
->sx
) {
349 /* cursor (ex) is on the left */
350 if (px
> sel
->sx
|| px
< sel
->ex
)
353 /* selection start (sx) is on the left */
354 if (px
< sel
->sx
|| px
> sel
->ex
)
363 /* Reflow wrapped lines. */
365 screen_reflow(struct screen
*s
, u_int new_x
)
367 struct grid
*old
= s
->grid
;
370 s
->grid
= grid_create(old
->sx
, old
->sy
, old
->hlimit
);
372 change
= grid_reflow(s
->grid
, old
, new_x
);