updates.
[runtime.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         g_atexit(term_deinit);
99         return TRUE;
100 }
101
102 void term_deinit(void)
103 {
104         if (current_term != NULL) {
105                 g_source_remove(redraw_tag);
106
107                 term_common_deinit();
108                 terminfo_core_deinit(current_term);
109                 current_term = NULL;
110         }
111 }
112
113 static void term_move_real(void)
114 {
115         if (term_detached) return;
116
117         if (vcx != crealx || vcy != crealy || cforcemove) {
118                 if (curs_visible) {
119                         terminfo_set_cursor_visible(FALSE);
120                         curs_visible = FALSE;
121                 }
122
123                 if (cforcemove) {
124                         crealx = crealy = -1;
125                         cforcemove = FALSE;
126                 }
127                 terminfo_move_relative(crealx, crealy, vcx, vcy);
128                 crealx = vcx; crealy = vcy;
129         }
130
131         vcmove = FALSE;
132 }
133
134 /* Cursor position is unknown - move it immediately to known position */
135 static void term_move_reset(int x, int y)
136 {
137         if (x >= term_width) x = term_width-1;
138         if (y >= term_height) y = term_height-1;
139
140         vcx = x; vcy = y;
141         cforcemove = TRUE;
142         term_move_real();
143 }
144
145 /* Resize terminal - if width or height is negative,
146    the new size is unknown and should be figured out somehow */
147 void term_resize(int width, int height)
148 {
149         if (width < 0 || height < 0) {
150                 terminfo_resize(current_term);
151                 width = current_term->width;
152                 height = current_term->height;
153         }
154
155         if (term_width != width || term_height != height) {
156                 term_width = current_term->width = width;
157                 term_height = current_term->height = height;
158                 term_window_move(root_window, 0, 0, term_width, term_height);
159
160                 g_free(term_lines_empty);
161                 term_lines_empty = g_new0(char, term_height);
162         }
163
164         term_move_reset(0, 0);
165 }
166
167 void term_resize_final(int width, int height)
168 {
169 }
170
171 /* Returns TRUE if terminal has colors */
172 int term_has_colors(void)
173 {
174         return current_term->has_colors;
175 }
176
177 /* Force the colors on any way you can */
178 void term_force_colors(int set)
179 {
180         if (term_detached) return;
181
182         terminfo_setup_colors(current_term, set);
183 }
184
185 /* Clear screen */
186 void term_clear(void)
187 {
188         if (term_detached) return;
189
190         term_set_color(root_window, ATTR_RESET);
191         terminfo_clear();
192         term_move_reset(0, 0);
193
194         memset(term_lines_empty, 1, term_height);
195 }
196
197 /* Beep */
198 void term_beep(void)
199 {
200         if (term_detached) return;
201
202         terminfo_beep(current_term);
203 }
204
205 /* Create a new window in terminal */
206 TERM_WINDOW *term_window_create(int x, int y, int width, int height)
207 {
208         TERM_WINDOW *window;
209
210         window = g_new0(TERM_WINDOW, 1);
211         window->term = current_term;
212         window->x = x; window->y = y;
213         window->width = width; window->height = height;
214         return window;
215 }
216
217 /* Destroy a terminal window */
218 void term_window_destroy(TERM_WINDOW *window)
219 {
220         g_free(window);
221 }
222
223 /* Move/resize a window */
224 void term_window_move(TERM_WINDOW *window, int x, int y,
225                       int width, int height)
226 {
227         window->x = x;
228         window->y = y;
229         window->width = width;
230         window->height = height;
231 }
232
233 /* Clear window */
234 void term_window_clear(TERM_WINDOW *window)
235 {
236         int y;
237
238         if (term_detached) return;
239
240         terminfo_set_normal();
241         if (window->y == 0 && window->height == term_height) {
242                 term_clear();
243         } else {
244                 for (y = 0; y < window->height; y++) {
245                         term_move(window, 0, y);
246                         term_clrtoeol(window);
247                 }
248         }
249 }
250
251 /* Scroll window up/down */
252 void term_window_scroll(TERM_WINDOW *window, int count)
253 {
254         int y;
255
256         if (term_detached) return;
257
258         terminfo_scroll(window->y, window->y+window->height-1, count);
259         term_move_reset(vcx, vcy);
260
261         /* set the newly scrolled area dirty */
262         for (y = 0; y < window->height; y++)
263                 term_lines_empty[window->y+y] = FALSE;
264 }
265
266 /* Change active color */
267 void term_set_color(TERM_WINDOW *window, int col)
268 {
269         int set_normal;
270
271         if (term_detached) return;
272
273         set_normal = ((col & ATTR_RESETFG) && last_fg != -1) ||
274                 ((col & ATTR_RESETBG) && last_bg != -1);
275         if (((last_attrs & ATTR_BOLD) && (col & ATTR_BOLD) == 0) ||
276             ((last_attrs & ATTR_BLINK) && (col & ATTR_BLINK) == 0)) {
277                 /* we'll need to get rid of bold/blink - this can only be
278                    done with setting the default color */
279                 set_normal = TRUE;
280         }
281
282         if (set_normal) {
283                 last_fg = last_bg = -1;
284                 last_attrs = 0;
285                 terminfo_set_normal();
286         }
287
288         if (!term_use_colors && (col & 0xf0) != 0)
289                 col |= ATTR_REVERSE;
290
291         /* reversed text (use standout) */
292         if (col & ATTR_REVERSE) {
293                 if ((last_attrs & ATTR_REVERSE) == 0)
294                         terminfo_set_standout(TRUE);
295         } else if (last_attrs & ATTR_REVERSE)
296                 terminfo_set_standout(FALSE);
297
298         /* set foreground color */
299         if ((col & 0x0f) != last_fg &&
300             ((col & 0x0f) != 0 || (col & ATTR_RESETFG) == 0)) {
301                 if (term_use_colors) {
302                         last_fg = col & 0x0f;
303                         terminfo_set_fg(last_fg);
304                 }
305         }
306
307         /* set background color */
308         if (col & ATTR_BLINK)
309                 col |= 0x80;
310         else if (col & 0x80)
311                 col |= ATTR_BLINK;
312
313         if ((col & 0xf0) >> 4 != last_bg &&
314             ((col & 0xf0) != 0 || (col & ATTR_RESETBG) == 0)) {
315                 if (term_use_colors) {
316                         last_bg = (col & 0xf0) >> 4;
317                         terminfo_set_bg(last_bg);
318                 }
319         }
320
321         /* bold */
322         if (col & 0x08)
323                 col |= ATTR_BOLD;
324         else if (col & ATTR_BOLD)
325                 terminfo_set_bold();
326
327         /* underline */
328         if (col & ATTR_UNDERLINE) {
329                 if ((last_attrs & ATTR_UNDERLINE) == 0)
330                         terminfo_set_uline(TRUE);
331         } else if (last_attrs & ATTR_UNDERLINE)
332                 terminfo_set_uline(FALSE);
333
334         last_attrs = col & ~0xff;
335 }
336
337 void term_move(TERM_WINDOW *window, int x, int y)
338 {
339         vcmove = TRUE;
340         vcx = x+window->x;
341         vcy = y+window->y;
342
343         if (vcx >= term_width)
344                 vcx = term_width-1;
345         if (vcy >= term_height)
346                 vcy = term_height-1;
347 }
348
349 static void term_printed_text(int count)
350 {
351         term_lines_empty[vcy] = FALSE;
352
353         /* if we continued writing past the line, wrap to next line.
354            However, next term_move() really shouldn't try to cache
355            the move, otherwise terminals would try to combine the
356            last word in upper line with first word in lower line. */
357         cforcemove = TRUE;
358         vcx += count;
359         while (vcx >= term_width) {
360                 vcx -= term_width;
361                 if (vcy < term_height) vcy++;
362                 if (vcx > 0) term_lines_empty[vcy] = FALSE;
363         }
364 }
365
366 void term_addch(TERM_WINDOW *window, int chr)
367 {
368         if (term_detached) return;
369
370         if (vcmove) term_move_real();
371         term_printed_text(1);
372         if (vcy != term_height || vcx != 0)
373                 putc(chr, window->term->out);
374 }
375
376 void term_addstr(TERM_WINDOW *window, const char *str)
377 {
378         int len;
379
380         if (term_detached) return;
381
382         if (vcmove) term_move_real();
383         len = strlen(str);
384         term_printed_text(len);
385
386         if (vcy != term_height || vcx != 0)
387                 fputs(str, window->term->out);
388         else
389                 fwrite(str, 1, len-1, window->term->out);
390 }
391
392 void term_clrtoeol(TERM_WINDOW *window)
393 {
394         if (term_detached) return;
395
396         /* clrtoeol() doesn't necessarily understand colors */
397         if (last_fg == -1 && last_bg == -1 &&
398             (last_attrs & (ATTR_UNDERLINE|ATTR_REVERSE)) == 0) {
399                 if (!term_lines_empty[vcy]) {
400                         if (vcmove) term_move_real();
401                         terminfo_clrtoeol();
402                         if (vcx == 0) term_lines_empty[vcy] = TRUE;
403                 }
404         } else if (vcx < term_width) {
405                 /* we'll need to fill the line ourself. */
406                 if (vcmove) term_move_real();
407                 terminfo_repeat(' ', term_width-vcx);
408                 terminfo_move(vcx, vcy);
409                 term_lines_empty[vcy] = FALSE;
410         }
411 }
412
413 void term_move_cursor(int x, int y)
414 {
415         curs_x = x;
416         curs_y = y;
417 }
418
419 void term_refresh(TERM_WINDOW *window)
420 {
421         if (term_detached || freeze_counter > 0)
422                 return;
423
424         term_move(root_window, curs_x, curs_y);
425         term_move_real();
426
427         if (!curs_visible) {
428                 terminfo_set_cursor_visible(TRUE);
429                 curs_visible = TRUE;
430         }
431         term_set_color(window, ATTR_RESET);
432         fflush(window != NULL ? window->term->out : current_term->out);
433 }
434
435 void term_refresh_freeze(void)
436 {
437         freeze_counter++;
438
439         if (!term_detached && curs_visible) {
440                 terminfo_set_cursor_visible(FALSE);
441                 curs_visible = FALSE;
442         }
443 }
444
445 void term_refresh_thaw(void)
446 {
447         if (--freeze_counter == 0)
448                 term_refresh(NULL);
449 }
450
451 void term_auto_detach(int set)
452 {
453         auto_detach = set;
454 }
455
456 void term_detach(void)
457 {
458         terminfo_stop(current_term);
459
460         fclose(current_term->in);
461         fclose(current_term->out);
462
463         current_term->in = NULL;
464         current_term->out = NULL;
465         term_detached = TRUE;
466 }
467
468 void term_attach(FILE *in, FILE *out)
469 {
470         current_term->in = in;
471         current_term->out = out;
472         term_detached = FALSE;
473
474         terminfo_cont(current_term);
475         irssi_redraw();
476 }
477
478 void term_stop(void)
479 {
480         if (term_detached) {
481                 kill(getpid(), SIGSTOP);
482         } else {
483                 terminfo_stop(current_term);
484                 kill(getpid(), SIGSTOP);
485                 terminfo_cont(current_term);
486                 irssi_redraw();
487         }
488 }
489
490 int term_gets(unsigned char *buffer, int size)
491 {
492         int ret;
493
494         if (term_detached)
495                 return 0;
496
497         /* fread() doesn't work */
498         ret = read(fileno(current_term->in), buffer, size);
499         if (ret == 0) {
500                 /* EOF - terminal got lost */
501                 if (auto_detach)
502                         term_detach();
503                 ret = -1;
504         } else if (ret == -1 && (errno == EINTR || errno == EAGAIN))
505                 ret = 0;
506
507         return ret;
508 }