1 /* Public terminal drawing API. Frontend for the screen image in memory. */
2 /* $Id: draw.c,v 1.109 2005/06/15 18:45:00 jonas Exp $ */
10 #include "config/options.h"
11 #include "terminal/color.h"
12 #include "terminal/draw.h"
13 #include "terminal/screen.h"
14 #include "terminal/terminal.h"
15 #include "util/color.h"
18 /* Makes sure that @x and @y are within the dimensions of the terminal. */
19 #define check_range(term, x, y) \
21 int_bounds(&(x), 0, (term)->width - 1); \
22 int_bounds(&(y), 0, (term)->height - 1); \
25 #if defined(CONFIG_88_COLORS) || defined(CONFIG_256_COLORS)
26 #define clear_screen_char_color(schar) do { memset((schar)->color, 0, 2); } while (0)
28 #define clear_screen_char_color(schar) do { (schar)->color[0] = 0; } while (0)
33 inline struct screen_char
*
34 get_char(struct terminal
*term
, int x
, int y
)
36 assert(term
&& term
->screen
&& term
->screen
->image
);
37 if_assert_failed
return NULL
;
38 check_range(term
, x
, y
);
40 return &term
->screen
->image
[x
+ term
->width
* y
];
44 draw_border_cross(struct terminal
*term
, int x
, int y
,
45 enum border_cross_direction dir
, struct color_pair
*color
)
47 static unsigned char border_trans
[2][4] = {
48 /* Used for BORDER_X_{RIGHT,LEFT}: */
49 { BORDER_SVLINE
, BORDER_SRTEE
, BORDER_SLTEE
},
50 /* Used for BORDER_X_{DOWN,UP}: */
51 { BORDER_SHLINE
, BORDER_SDTEE
, BORDER_SUTEE
},
53 struct screen_char
*screen_char
= get_char(term
, x
, y
);
56 if (!screen_char
) return;
57 if (!(screen_char
->attr
& SCREEN_ATTR_FRAME
)) return;
59 /* First check if there is already a horizontal/vertical line, so that
60 * we will have to replace with a T char. Example: if there is a '|'
61 * and the direction is right, replace with a '|-' T char.
63 * If this is not the case check if there is a T char and we are adding
64 * the direction so that we end up with a cross. Example : if there is
65 * a '|-' and the direction is left, replace with a '+' (cross) char. */
67 if (screen_char
->data
== border_trans
[d
][0]) {
68 screen_char
->data
= border_trans
[d
][1 + (dir
& 1)];
70 } else if (screen_char
->data
== border_trans
[d
][2 - (dir
& 1)]) {
71 screen_char
->data
= BORDER_SCROSS
;
74 set_term_color(screen_char
, color
, 0,
75 get_opt_int_tree(term
->spec
, "colors"));
79 draw_border_char(struct terminal
*term
, int x
, int y
,
80 enum border_char border
, struct color_pair
*color
)
82 struct screen_char
*screen_char
= get_char(term
, x
, y
);
84 if (!screen_char
) return;
86 screen_char
->data
= (unsigned char) border
;
87 screen_char
->attr
= SCREEN_ATTR_FRAME
;
88 set_term_color(screen_char
, color
, 0,
89 get_opt_int_tree(term
->spec
, "colors"));
90 set_screen_dirty(term
->screen
, y
, y
);
94 draw_char_color(struct terminal
*term
, int x
, int y
, struct color_pair
*color
)
96 struct screen_char
*screen_char
= get_char(term
, x
, y
);
98 if (!screen_char
) return;
100 set_term_color(screen_char
, color
, 0,
101 get_opt_int_tree(term
->spec
, "colors"));
102 set_screen_dirty(term
->screen
, y
, y
);
106 draw_char_data(struct terminal
*term
, int x
, int y
, unsigned char data
)
108 struct screen_char
*screen_char
= get_char(term
, x
, y
);
110 if (!screen_char
) return;
112 screen_char
->data
= data
;
113 set_screen_dirty(term
->screen
, y
, y
);
116 /* Updates a line in the terms screen. */
117 /* When doing frame drawing @x can be different than 0. */
119 draw_line(struct terminal
*term
, int x
, int y
, int l
, struct screen_char
*line
)
121 struct screen_char
*screen_char
= get_char(term
, x
, y
);
125 if_assert_failed
return;
126 if (!screen_char
) return;
128 size
= int_min(l
, term
->width
- x
);
129 if (size
== 0) return;
131 copy_screen_chars(screen_char
, line
, size
);
132 set_screen_dirty(term
->screen
, y
, y
);
136 draw_border(struct terminal
*term
, struct box
*box
,
137 struct color_pair
*color
, int width
)
139 static enum border_char p1
[] = {
147 static enum border_char p2
[] = {
155 enum border_char
*p
= (width
> 1) ? p2
: p1
;
156 struct box borderbox
;
158 set_box(&borderbox
, box
->x
- 1, box
->y
- 1,
159 box
->width
+ 2, box
->height
+ 2);
161 if (borderbox
.width
> 2) {
164 /* Horizontal top border */
165 set_box(&bbox
, box
->x
, borderbox
.y
, box
->width
, 1);
166 draw_box(term
, &bbox
, p
[5], SCREEN_ATTR_FRAME
, color
);
168 /* Horizontal bottom border */
169 bbox
.y
+= borderbox
.height
- 1;
170 draw_box(term
, &bbox
, p
[5], SCREEN_ATTR_FRAME
, color
);
173 if (borderbox
.height
> 2) {
176 /* Vertical left border */
177 set_box(&bbox
, borderbox
.x
, box
->y
, 1, box
->height
);
178 draw_box(term
, &bbox
, p
[4], SCREEN_ATTR_FRAME
, color
);
180 /* Vertical right border */
181 bbox
.x
+= borderbox
.width
- 1;
182 draw_box(term
, &bbox
, p
[4], SCREEN_ATTR_FRAME
, color
);
185 if (borderbox
.width
> 1 && borderbox
.height
> 1) {
186 int right
= borderbox
.x
+ borderbox
.width
- 1;
187 int bottom
= borderbox
.y
+ borderbox
.height
- 1;
189 /* Upper left corner */
190 draw_border_char(term
, borderbox
.x
, borderbox
.y
, p
[0], color
);
191 /* Upper right corner */
192 draw_border_char(term
, right
, borderbox
.y
, p
[1], color
);
193 /* Lower left corner */
194 draw_border_char(term
, borderbox
.x
, bottom
, p
[2], color
);
195 /* Lower right corner */
196 draw_border_char(term
, right
, bottom
, p
[3], color
);
199 set_screen_dirty(term
->screen
, borderbox
.y
, borderbox
.y
+ borderbox
.height
);
203 draw_char(struct terminal
*term
, int x
, int y
,
204 unsigned char data
, enum screen_char_attr attr
,
205 struct color_pair
*color
)
207 struct screen_char
*screen_char
= get_char(term
, x
, y
);
209 if (!screen_char
) return;
211 screen_char
->data
= data
;
212 screen_char
->attr
= attr
;
213 set_term_color(screen_char
, color
, 0,
214 get_opt_int_tree(term
->spec
, "colors"));
216 set_screen_dirty(term
->screen
, y
, y
);
220 draw_box(struct terminal
*term
, struct box
*box
,
221 unsigned char data
, enum screen_char_attr attr
,
222 struct color_pair
*color
)
224 struct screen_char
*line
, *pos
, *end
;
227 line
= get_char(term
, box
->x
, box
->y
);
230 height
= int_min(box
->height
, term
->height
- box
->y
);
231 width
= int_min(box
->width
, term
->width
- box
->x
);
233 if (height
<= 0 || width
<= 0) return;
235 /* Compose off the ending screen position in the areas first line. */
236 end
= &line
[width
- 1];
240 set_term_color(end
, color
, 0,
241 get_opt_int_tree(term
->spec
, "colors"));
243 clear_screen_char_color(end
);
246 /* Draw the first area line. */
247 for (pos
= line
; pos
< end
; pos
++) {
248 copy_screen_chars(pos
, end
, 1);
251 /* Now make @end point to the last line */
252 /* For the rest of the area use the first area line. */
256 copy_screen_chars(pos
, line
, width
);
259 set_screen_dirty(term
->screen
, box
->y
, box
->y
+ box
->height
);
263 draw_shadow(struct terminal
*term
, struct box
*box
,
264 struct color_pair
*color
, int width
, int height
)
269 set_box(&dbox
, box
->x
+ width
, box
->y
+ box
->height
,
270 box
->width
- width
, height
);
272 draw_box(term
, &dbox
, ' ', 0, color
);
275 set_box(&dbox
, box
->x
+ box
->width
, box
->y
+ height
,
278 draw_box(term
, &dbox
, ' ', 0, color
);
282 draw_text(struct terminal
*term
, int x
, int y
,
283 unsigned char *text
, int length
,
284 enum screen_char_attr attr
, struct color_pair
*color
)
287 struct screen_char
*pos
, *end
;
289 assert(text
&& length
>= 0);
290 if_assert_failed
return;
292 if (length
<= 0) return;
293 pos
= get_char(term
, x
, y
);
296 end_pos
= int_min(length
, term
->width
- x
) - 1;
299 /* Detect attempt to set @end to a point outside @text,
300 * it may occur in case of bad calculations. --Zas */
302 INTERNAL("end_pos < 0 !!");
305 int textlen
= strlen(text
);
307 if (end_pos
>= textlen
) {
308 INTERNAL("end_pos (%d) >= text length (%d) !!", end_pos
, textlen
);
309 end_pos
= textlen
- 1;
314 end
= &pos
[int_max(0, end_pos
)];
317 /* Use the last char as template. */
319 set_term_color(end
, color
, 0,
320 get_opt_int_tree(term
->spec
, "colors"));
322 for (; pos
< end
&& *text
; text
++, pos
++) {
324 copy_screen_chars(pos
, end
, 1);
330 for (; pos
<= end
&& *text
; text
++, pos
++) {
335 set_screen_dirty(term
->screen
, y
, y
);
339 set_cursor(struct terminal
*term
, int x
, int y
, int blockable
)
341 assert(term
&& term
->screen
);
342 if_assert_failed
return;
344 if (blockable
&& get_opt_bool_tree(term
->spec
, "block_cursor")) {
346 y
= term
->height
- 1;
349 if (term
->screen
->cx
!= x
|| term
->screen
->cy
!= y
) {
350 check_range(term
, x
, y
);
352 set_screen_dirty(term
->screen
, int_min(term
->screen
->cy
, y
),
353 int_max(term
->screen
->cy
, y
));
354 term
->screen
->cx
= x
;
355 term
->screen
->cy
= y
;
360 clear_terminal(struct terminal
*term
)
364 set_box(&box
, 0, 0, term
->width
, term
->height
);
365 draw_box(term
, &box
, ' ', 0, NULL
);
366 set_cursor(term
, 0, 0, 1);