draw_border_cross(): Simplify lookup and describe the magic
[elinks/images.git] / src / terminal / draw.c
blob15134baed930b4d51a557c6894ec6a5419b60a5b
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 $ */
4 #ifdef HAVE_CONFIG_H
5 #include "config.h"
6 #endif
8 #include "elinks.h"
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"
16 #include "util/box.h"
18 /* Makes sure that @x and @y are within the dimensions of the terminal. */
19 #define check_range(term, x, y) \
20 do { \
21 int_bounds(&(x), 0, (term)->width - 1); \
22 int_bounds(&(y), 0, (term)->height - 1); \
23 } while (0)
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)
27 #else
28 #define clear_screen_char_color(schar) do { (schar)->color[0] = 0; } while (0)
29 #endif
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];
43 void
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);
54 unsigned int d;
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. */
66 d = dir>>1;
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"));
78 void
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);
93 void
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);
105 void
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. */
118 void
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);
122 int size;
124 assert(line);
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);
135 void
136 draw_border(struct terminal *term, struct box *box,
137 struct color_pair *color, int width)
139 static enum border_char p1[] = {
140 BORDER_SULCORNER,
141 BORDER_SURCORNER,
142 BORDER_SDLCORNER,
143 BORDER_SDRCORNER,
144 BORDER_SVLINE,
145 BORDER_SHLINE,
147 static enum border_char p2[] = {
148 BORDER_DULCORNER,
149 BORDER_DURCORNER,
150 BORDER_DDLCORNER,
151 BORDER_DDRCORNER,
152 BORDER_DVLINE,
153 BORDER_DHLINE,
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) {
162 struct box bbox;
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) {
174 struct box bbox;
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);
202 void
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);
219 void
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;
225 int width, height;
227 line = get_char(term, box->x, box->y);
228 if (!line) return;
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];
237 end->attr = attr;
238 end->data = data;
239 if (color) {
240 set_term_color(end, color, 0,
241 get_opt_int_tree(term->spec, "colors"));
242 } else {
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. */
253 pos = line;
254 while (--height) {
255 pos += term->width;
256 copy_screen_chars(pos, line, width);
259 set_screen_dirty(term->screen, box->y, box->y + box->height);
262 void
263 draw_shadow(struct terminal *term, struct box *box,
264 struct color_pair *color, int width, int height)
266 struct box dbox;
268 /* (horizontal) */
269 set_box(&dbox, box->x + width, box->y + box->height,
270 box->width - width, height);
272 draw_box(term, &dbox, ' ', 0, color);
274 /* (vertical) */
275 set_box(&dbox, box->x + box->width, box->y + height,
276 width, box->height);
278 draw_box(term, &dbox, ' ', 0, color);
281 void
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)
286 int end_pos;
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);
294 if (!pos) return;
296 end_pos = int_min(length, term->width - x) - 1;
298 #ifdef CONFIG_DEBUG
299 /* Detect attempt to set @end to a point outside @text,
300 * it may occur in case of bad calculations. --Zas */
301 if (end_pos < 0) {
302 INTERNAL("end_pos < 0 !!");
303 end_pos = 0;
304 } else {
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;
312 #endif
314 end = &pos[int_max(0, end_pos)];
316 if (color) {
317 /* Use the last char as template. */
318 end->attr = attr;
319 set_term_color(end, color, 0,
320 get_opt_int_tree(term->spec, "colors"));
322 for (; pos < end && *text; text++, pos++) {
323 end->data = *text;
324 copy_screen_chars(pos, end, 1);
327 end->data = *text;
329 } else {
330 for (; pos <= end && *text; text++, pos++) {
331 pos->data = *text;
335 set_screen_dirty(term->screen, y, y);
338 void
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")) {
345 x = term->width - 1;
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;
359 void
360 clear_terminal(struct terminal *term)
362 struct box box;
364 set_box(&box, 0, 0, term->width, term->height);
365 draw_box(term, &box, ' ', 0, NULL);
366 set_cursor(term, 0, 0, 1);