Merged 0.7.99 irssi.
[crypto.git] / apps / irssi / src / fe-text / term-terminfo.c
1 /*
2  term-terminfo.c : irssi
3
4     Copyright (C) 2001 Timo Sirainen
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "module.h"
22 #include "signals.h"
23 #include "term.h"
24 #include "terminfo-core.h"
25
26 #include <signal.h>
27
28 struct _TERM_WINDOW {
29         /* Terminal to use for window */
30         TERM_REC *term;
31
32         /* Area for window in terminal */
33         int x, y;
34         int width, height;
35 };
36
37 TERM_WINDOW *root_window;
38 int term_width, term_height, term_detached;
39
40 static char *term_lines_empty; /* 1 if line is entirely empty */
41 static int vcmove, vcx, vcy, curs_visible;
42 static int crealx, crealy, cforcemove;
43 static int curs_x, curs_y;
44 static int auto_detach;
45
46 static int last_fg, last_bg, last_attrs;
47
48 static int redraw_needed, redraw_tag;
49 static int freeze_counter;
50
51 /* SIGCONT handler */
52 static void sig_cont(int p)
53 {
54         redraw_needed = TRUE;
55         terminfo_cont(current_term);
56 }
57
58 static int redraw_timeout(void)
59 {
60         if (redraw_needed) {
61                 irssi_redraw();
62                 redraw_needed = FALSE;
63         }
64
65         return 1;
66 }
67
68 int term_init(void)
69 {
70         struct sigaction act;
71
72         last_fg = last_bg = -1;
73         last_attrs = 0;
74         vcx = vcy = 0; crealx = crealy = -1;
75         vcmove = FALSE; cforcemove = TRUE;
76         curs_visible = TRUE;
77
78         current_term = terminfo_core_init(stdin, stdout);
79         if (current_term == NULL)
80                 return FALSE;
81
82         /* grab CONT signal */
83         sigemptyset(&act.sa_mask);
84         act.sa_flags = 0;
85         act.sa_handler = sig_cont;
86         sigaction(SIGCONT, &act, NULL);
87         redraw_tag = g_timeout_add(500, (GSourceFunc) redraw_timeout, NULL);
88
89         curs_x = curs_y = 0;
90         term_width = current_term->width;
91         term_height = current_term->height;
92         root_window = term_window_create(0, 0, term_width, term_height);
93         term_detached = FALSE;
94
95         term_lines_empty = g_new0(char, term_height);
96
97         term_common_init();
98         return TRUE;
99 }
100
101 void term_deinit(void)
102 {
103         g_source_remove(redraw_tag);
104
105         term_common_deinit();
106         terminfo_core_deinit(current_term);
107 }
108
109 static void term_move_real(void)
110 {
111         if (term_detached) return;
112
113         if (vcx != crealx || vcy != crealy || cforcemove) {
114                 if (curs_visible) {
115                         terminfo_set_cursor_visible(FALSE);
116                         curs_visible = FALSE;
117                 }
118
119                 if (cforcemove) {
120                         crealx = crealy = -1;
121                         cforcemove = FALSE;
122                 }
123                 terminfo_move_relative(crealx, crealy, vcx, vcy);
124                 crealx = vcx; crealy = vcy;
125         }
126
127         vcmove = FALSE;
128 }
129
130 /* Cursor position is unknown - move it immediately to known position */
131 static void term_move_reset(int x, int y)
132 {
133         if (x >= term_width) x = term_width-1;
134         if (y >= term_height) y = term_height-1;
135
136         vcx = x; vcy = y;
137         cforcemove = TRUE;
138         term_move_real();
139 }
140
141 /* Resize terminal - if width or height is negative,
142    the new size is unknown and should be figured out somehow */
143 void term_resize(int width, int height)
144 {
145         if (width < 0 || height < 0) {
146                 terminfo_resize(current_term);
147                 width = current_term->width;
148                 height = current_term->height;
149         }
150
151         if (term_width != width || term_height != height) {
152                 term_width = current_term->width = width;
153                 term_height = current_term->height = height;
154                 term_window_move(root_window, 0, 0, term_width, term_height);
155
156                 g_free(term_lines_empty);
157                 term_lines_empty = g_new0(char, term_height);
158         }
159
160         term_move_reset(0, 0);
161 }
162
163 void term_resize_final(int width, int height)
164 {
165 }
166
167 /* Returns TRUE if terminal has colors */
168 int term_has_colors(void)
169 {
170         return current_term->has_colors;
171 }
172
173 /* Force the colors on any way you can */
174 void term_force_colors(int set)
175 {
176         if (term_detached) return;
177
178         terminfo_setup_colors(current_term, set);
179 }
180
181 /* Clear screen */
182 void term_clear(void)
183 {
184         if (term_detached) return;
185
186         term_set_color(root_window, 0);
187         terminfo_clear();
188         term_move_reset(0, 0);
189
190         memset(term_lines_empty, 1, term_height);
191 }
192
193 /* Beep */
194 void term_beep(void)
195 {
196         if (term_detached) return;
197
198         terminfo_beep(current_term);
199 }
200
201 /* Create a new window in terminal */
202 TERM_WINDOW *term_window_create(int x, int y, int width, int height)
203 {
204         TERM_WINDOW *window;
205
206         window = g_new0(TERM_WINDOW, 1);
207         window->term = current_term;
208         window->x = x; window->y = y;
209         window->width = width; window->height = height;
210         return window;
211 }
212
213 /* Destroy a terminal window */
214 void term_window_destroy(TERM_WINDOW *window)
215 {
216         g_free(window);
217 }
218
219 /* Move/resize a window */
220 void term_window_move(TERM_WINDOW *window, int x, int y,
221                       int width, int height)
222 {
223         window->x = x;
224         window->y = y;
225         window->width = width;
226         window->height = height;
227 }
228
229 /* Clear window */
230 void term_window_clear(TERM_WINDOW *window)
231 {
232         int y;
233
234         if (term_detached) return;
235
236         terminfo_set_normal();
237         if (window->y == 0 && window->height == term_height) {
238                 term_clear();
239         } else {
240                 for (y = 0; y < window->height; y++) {
241                         term_move(window, 0, y);
242                         term_clrtoeol(window);
243                 }
244         }
245 }
246
247 /* Scroll window up/down */
248 void term_window_scroll(TERM_WINDOW *window, int count)
249 {
250         int y;
251
252         if (term_detached) return;
253
254         terminfo_scroll(window->y, window->y+window->height-1, count);
255         term_move_reset(vcx, vcy);
256
257         /* set the newly scrolled area dirty */
258         for (y = 0; y < window->height; y++)
259                 term_lines_empty[window->y+y] = FALSE;
260 }
261
262 /* Change active color */
263 void term_set_color(TERM_WINDOW *window, int col)
264 {
265         int set_normal;
266
267         if (term_detached) return;
268
269         set_normal = ((col & ATTR_RESETFG) && last_fg != -1) ||
270                 ((col & ATTR_RESETBG) && last_bg != -1);
271         if (((last_attrs & ATTR_BOLD) && (col & ATTR_BOLD) == 0) ||
272             ((last_attrs & ATTR_BLINK) && (col & ATTR_BLINK) == 0)) {
273                 /* we'll need to get rid of bold/blink - this can only be
274                    done with setting the default color */
275                 set_normal = TRUE;
276         }
277
278         if (set_normal) {
279                 last_fg = last_bg = -1;
280                 last_attrs = 0;
281                 terminfo_set_normal();
282         }
283
284         if (!term_use_colors && (col & 0xf0) != 0)
285                 col |= ATTR_REVERSE;
286
287         /* reversed text (use standout) */
288         if (col & ATTR_REVERSE) {
289                 if ((last_attrs & ATTR_REVERSE) == 0)
290                         terminfo_set_standout(TRUE);
291         } else if (last_attrs & ATTR_REVERSE)
292                 terminfo_set_standout(FALSE);
293
294         /* set foreground color */
295         if ((col & 0x0f) != last_fg &&
296             ((col & 0x0f) != 0 || (col & ATTR_RESETFG) == 0)) {
297                 if (term_use_colors) {
298                         last_fg = col & 0x0f;
299                         terminfo_set_fg(last_fg);
300                 }
301         }
302
303         /* set background color */
304         if (col & ATTR_BLINK)
305                 col |= 0x80;
306         else if (col & 0x80)
307                 col |= ATTR_BLINK;
308
309         if ((col & 0xf0) >> 4 != last_bg &&
310             ((col & 0xf0) != 0 || (col & ATTR_RESETBG) == 0)) {
311                 if (term_use_colors) {
312                         last_bg = (col & 0xf0) >> 4;
313                         terminfo_set_bg(last_bg);
314                 }
315         }
316
317         /* bold */
318         if (col & 0x08)
319                 col |= ATTR_BOLD;
320         else if (col & ATTR_BOLD)
321                 terminfo_set_bold();
322
323         /* underline */
324         if (col & ATTR_UNDERLINE) {
325                 if ((last_attrs & ATTR_UNDERLINE) == 0)
326                         terminfo_set_uline(TRUE);
327         } else if (last_attrs & ATTR_UNDERLINE)
328                 terminfo_set_uline(FALSE);
329
330         last_attrs = col & ~0xff;
331 }
332
333 void term_move(TERM_WINDOW *window, int x, int y)
334 {
335         vcmove = TRUE;
336         vcx = x+window->x;
337         vcy = y+window->y;
338
339         if (vcx >= term_width)
340                 vcx = term_width-1;
341         if (vcy >= term_height)
342                 vcy = term_height-1;
343 }
344
345 static void term_printed_text(int count)
346 {
347         term_lines_empty[vcy] = FALSE;
348
349         /* if we continued writing past the line, wrap to next line.
350            However, next term_move() really shouldn't try to cache
351            the move, otherwise terminals would try to combine the
352            last word in upper line with first word in lower line. */
353         cforcemove = TRUE;
354         vcx += count;
355         while (vcx >= term_width) {
356                 vcx -= term_width;
357                 if (vcy < term_height) vcy++;
358                 if (vcx > 0) term_lines_empty[vcy] = FALSE;
359         }
360 }
361
362 void term_addch(TERM_WINDOW *window, int chr)
363 {
364         if (term_detached) return;
365
366         if (vcmove) term_move_real();
367         term_printed_text(1);
368         if (vcy != term_height || vcx != 0)
369                 putc(chr, window->term->out);
370 }
371
372 void term_addstr(TERM_WINDOW *window, const char *str)
373 {
374         int len;
375
376         if (term_detached) return;
377
378         if (vcmove) term_move_real();
379         len = strlen(str);
380         term_printed_text(len);
381
382         if (vcy != term_height || vcx != 0)
383                 fputs(str, window->term->out);
384         else
385                 fwrite(str, 1, len-1, window->term->out);
386 }
387
388 void term_clrtoeol(TERM_WINDOW *window)
389 {
390         if (term_detached) return;
391
392         /* clrtoeol() doesn't necessarily understand colors */
393         if (last_fg == -1 && last_bg == -1 &&
394             (last_attrs & (ATTR_UNDERLINE|ATTR_REVERSE)) == 0) {
395                 if (!term_lines_empty[vcy]) {
396                         if (vcmove) term_move_real();
397                         terminfo_clrtoeol();
398                         if (vcx == 0) term_lines_empty[vcy] = TRUE;
399                 }
400         } else if (vcx < term_width) {
401                 /* we'll need to fill the line ourself. */
402                 if (vcmove) term_move_real();
403                 terminfo_repeat(' ', term_width-vcx);
404                 terminfo_move(vcx, vcy);
405                 term_lines_empty[vcy] = FALSE;
406         }
407 }
408
409 void term_move_cursor(int x, int y)
410 {
411         curs_x = x;
412         curs_y = y;
413 }
414
415 void term_refresh(TERM_WINDOW *window)
416 {
417         if (term_detached || freeze_counter > 0)
418                 return;
419
420         term_move(root_window, curs_x, curs_y);
421         term_move_real();
422
423         if (!curs_visible) {
424                 terminfo_set_cursor_visible(TRUE);
425                 curs_visible = TRUE;
426         }
427         term_set_color(window, ATTR_RESET);
428         fflush(window != NULL ? window->term->out : current_term->out);
429 }
430
431 void term_refresh_freeze(void)
432 {
433         freeze_counter++;
434
435         if (!term_detached && curs_visible) {
436                 terminfo_set_cursor_visible(FALSE);
437                 curs_visible = FALSE;
438         }
439 }
440
441 void term_refresh_thaw(void)
442 {
443         if (--freeze_counter == 0)
444                 term_refresh(NULL);
445 }
446
447 void term_auto_detach(int set)
448 {
449         auto_detach = set;
450 }
451
452 void term_detach(void)
453 {
454         terminfo_stop(current_term);
455
456         fclose(current_term->in);
457         fclose(current_term->out);
458
459         current_term->in = NULL;
460         current_term->out = NULL;
461         term_detached = TRUE;
462 }
463
464 void term_attach(FILE *in, FILE *out)
465 {
466         current_term->in = in;
467         current_term->out = out;
468         term_detached = FALSE;
469
470         terminfo_cont(current_term);
471         irssi_redraw();
472 }
473
474 void term_stop(void)
475 {
476         if (term_detached) {
477                 kill(getpid(), SIGSTOP);
478         } else {
479                 terminfo_stop(current_term);
480                 kill(getpid(), SIGSTOP);
481                 terminfo_cont(current_term);
482                 irssi_redraw();
483         }
484 }
485
486 int term_gets(unsigned char *buffer, int size)
487 {
488         int ret;
489
490         if (term_detached)
491                 return 0;
492
493         /* fread() doesn't work */
494         ret = read(fileno(current_term->in), buffer, size);
495         if (ret == 0) {
496                 /* EOF - terminal got lost */
497                 if (auto_detach)
498                         term_detach();
499                 ret = -1;
500         } else if (ret == -1 && (errno == EINTR || errno == EAGAIN))
501                 ret = 0;
502
503         return ret;
504 }