1 /*
2  * vbi-tty  --  terminal videotext browser
3  *
4  *   (c) 2002 Gerd Knorr <kraxel@bytesex.org>
5  */
6 
7 #include "config.h"
8 
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <string.h>
13 #include <errno.h>
14 #include <iconv.h>
15 #include <locale.h>
16 #include <langinfo.h>
17 #include <termios.h>
18 #include <fcntl.h>
19 #include <sys/types.h>
20 #include <sys/ioctl.h>
21 
22 /*#include <linux/fb.h>*/
23 
24 #include "vbi-data.h"
25 #include "vbi-tty.h"
26 #include "fbtools.h"
27 
28 /* --------------------------------------------------------------------- */
29 
30 struct termios  saved_attributes;
31 int             saved_fl;
32 
tty_raw(void)33 static void tty_raw(void)
34 {
35     struct termios tattr;
36 
37     fcntl(0,F_GETFL,&saved_fl);
38     tcgetattr (0, &saved_attributes);
39 
40     fcntl(0,F_SETFL,O_NONBLOCK);
41     memcpy(&tattr,&saved_attributes,sizeof(struct termios));
42     tattr.c_lflag &= ~(ICANON|ECHO);
43     tattr.c_cc[VMIN] = 1;
44     tattr.c_cc[VTIME] = 0;
45     tcsetattr (0, TCSAFLUSH, &tattr);
46 }
47 
tty_restore(void)48 static void tty_restore(void)
49 {
50     fcntl(0,F_SETFL,saved_fl);
51     tcsetattr (0, TCSANOW, &saved_attributes);
52 }
53 
54 /* FIXME: Yes, I know, hardcoding ANSI sequences is bad.  ncurses
55  * can't handle multibyte locales like UTF-8 not yet, that's why that
56  * dirty hack for now ... */
tty_clear(void)57 static void tty_clear(void)
58 {
59     fprintf(stderr,"\033[H\033[2J");
60 }
61 
tty_goto(int x,int y)62 static void tty_goto(int x, int y)
63 {
64     fprintf(stderr,"\033[%d;%dH",y,x);
65 }
66 
67 /* --------------------------------------------------------------------- */
68 #ifdef WITH_LINUX_FB_H
69 static int have_fb = 0;
70 static int fb_fmt  = VBI_PIXFMT_RGBA32_LE;
71 static int switch_last;
72 
fb_clear(void)73 static void fb_clear(void)
74 {
75     fb_memset(fb_mem+fb_mem_offset,0,fb_fix.smem_len);
76 }
77 #endif
78 /* --------------------------------------------------------------------- */
79 
80 struct vbi_tty {
81     struct vbi_state  *vbi;
82     struct vbi_page   pg;
83     int               pgno,subno;
84     int               newpage;
85 };
86 
87 static void
vbi_fix_head(struct vbi_tty * tty,struct vbi_char * ch)88 vbi_fix_head(struct vbi_tty *tty, struct vbi_char *ch)
89 {
90     int showno,showsub,red,i;
91 
92     showno  = tty->pg.pgno;
93     showsub = tty->pg.subno;
94     red     = 0;
95     if (0 == showno) {
96 	showno  = tty->pgno;
97 	showsub = 0;
98 	red     = 1;
99     }
100     if (tty->newpage) {
101 	showno  = tty->newpage;
102 	showsub = 0;
103 	red     = 1;
104     }
105 
106     for (i = 1; i <= 6; i++)
107 	ch[i].unicode = ' ';
108     if (showno >= 0x100)
109 	ch[1].unicode = '0' + ((showno >> 8) & 0xf);
110     if (showno >= 0x10)
111 	ch[2].unicode = '0' + ((showno >> 4) & 0xf);
112     if (showno >= 0x1)
113 	ch[3].unicode = '0' + ((showno >> 0) & 0xf);
114     if (showsub) {
115 	ch[4].unicode = '/';
116 	ch[5].unicode = '0' + ((showsub >> 4) & 0xf);
117 	ch[6].unicode = '0' + ((showsub >> 0) & 0xf);
118     }
119     if (red) {
120 	ch[1].foreground = VBI_RED;
121 	ch[2].foreground = VBI_RED;
122 	ch[3].foreground = VBI_RED;
123     }
124 }
125 
126 static void
vbi_render_page(struct vbi_tty * tty)127 vbi_render_page(struct vbi_tty *tty)
128 {
129     char *data;
130     int len;
131 
132     data = malloc(25*41*24);
133     vbi_fetch_vt_page(tty->vbi->dec,&tty->pg,tty->pgno,tty->subno,
134 		      VBI_WST_LEVEL_1p5,25,1);
135     vbi_fix_head(tty,tty->pg.text);
136 #ifdef WITH_LINUX_FB_H
137     if (have_fb) {
138 	vbi_draw_vt_page_region(&tty->pg, fb_fmt,
139 				fb_mem + fb_mem_offset,
140 				fb_fix.line_length,
141 				0,0,
142 				tty->pg.columns, tty->pg.rows,
143 				0,1);
144 
145     } else {
146 #endif
147 	len = vbi_export_txt(data,nl_langinfo(CODESET),25*41*8,
148 			     &tty->pg,&vbi_fullrect,VBI_ANSICOLOR);
149 	tty_goto(0,0);
150 	fwrite(data,len,1,stderr);
151 	tty_goto(42,0);
152 	free(data);
153 #ifdef WITH_LINUX_FB_H
154     }
155 #endif
156 }
157 
158 static void
vbi_render_head(struct vbi_tty * tty,int pgno,int subno)159 vbi_render_head(struct vbi_tty *tty, int pgno, int subno)
160 {
161     static struct vbi_rect head = {
162 	.x1 =  0,
163 	.y1 =  0,
164 	.x2 = 41,
165 	.y2 =  1,
166     };
167     struct vbi_page pg;
168     char *data;
169     int len;
170 
171     data = malloc(41*24);
172     memset(&pg,0,sizeof(pg));
173     vbi_fetch_vt_page(tty->vbi->dec,&pg,pgno,subno,
174 		      VBI_WST_LEVEL_1p5,1,1);
175     vbi_fix_head(tty,pg.text);
176 #ifdef WITH_LINUX_FB_H
177     if (have_fb) {
178 	vbi_draw_vt_page_region(&pg, fb_fmt,
179 				fb_mem + fb_mem_offset,
180 				fb_fix.line_length,
181 				0,0,
182 				pg.columns, 1,
183 				0,1);
184     } else {
185 #endif
186 	len = vbi_export_txt(data,nl_langinfo(CODESET),41*8,
187 			     &pg,&head,VBI_ANSICOLOR);
188 	tty_goto(0,0);
189 	fwrite(data,len,1,stderr);
190 	tty_goto(42,0);
191 	free(data);
192 #ifdef WITH_LINUX_FB_H
193     }
194 #endif
195 }
196 
197 static void
vbi_newdata(struct vbi_event * ev,void * user)198 vbi_newdata(struct vbi_event *ev, void *user)
199 {
200     struct vbi_tty *tty = user;
201 
202     switch (ev->type) {
203     case VBI_EVENT_TTX_PAGE:
204 	if (tty->pgno  == ev->ev.ttx_page.pgno &&
205 	    (tty->subno == ev->ev.ttx_page.subno ||
206 	     tty->subno == VBI_ANY_SUBNO)) {
207 	    vbi_render_page(tty);
208 	} else {
209 	    vbi_render_head(tty,
210 			    ev->ev.ttx_page.pgno,
211 			    ev->ev.ttx_page.subno);
212 	}
213 	break;
214     }
215 }
216 
217 static void
vbi_setpage(struct vbi_tty * tty,int pgno,int subno)218 vbi_setpage(struct vbi_tty *tty, int pgno, int subno)
219 {
220     tty->pgno = pgno;
221     tty->subno = subno;
222     tty->newpage = 0;
223     memset(&tty->pg,0,sizeof(struct vbi_page));
224     vbi_fetch_vt_page(tty->vbi->dec,&tty->pg,tty->pgno,tty->subno,
225 		      VBI_WST_LEVEL_1p5,25,1);
226     vbi_render_page(tty);
227 }
228 
229 /* --------------------------------------------------------------------- */
230 
vbi_tty(char * device,int debug,int sim)231 void vbi_tty(char *device, int debug, int sim)
232 {
233     struct vbi_state  *vbi;
234     struct vbi_tty    *tty;
235     fd_set            set;
236     struct winsize    win;
237     struct timeval    tv;
238     char              key[11];
239     int               rc,subno,last;
240 
241     setlocale(LC_ALL,"");
242     vbi = vbi_open(device,debug,sim);
243     if (NULL == vbi)
244 	exit(1);
245 #ifdef WITH_LINUX_FB_H
246     if (0 /* 0 == fb_probe() */ ) {
247 	have_fb = 1;
248 	fb_init(NULL,NULL,0);
249 	fb_catch_exit_signals();
250 	fb_switch_init();
251 	switch_last = fb_switch_state;
252     } else {
253 #endif
254 	if (-1 != ioctl(0,TIOCGWINSZ,&win) && win.ws_row < 26) {
255 	    fprintf(stderr,"Terminal too small (need 26 rows, have %d)\n",
256 		    win.ws_row);
257 	    exit(1);
258 	}
259 #ifdef WITH_LINUX_FB_H
260     }
261 #endif
262     tty_raw();
263 #ifdef WITH_LINUX_FB_H
264     have_fb ? fb_clear() : tty_clear();
265 #else
266     tty_clear();
267 #endif
268 
269     tty = malloc(sizeof(*tty));
270     memset(tty,0,sizeof(*tty));
271     tty->vbi = vbi;
272     vbi_event_handler_add(vbi->dec,~0,vbi_newdata,tty);
273     vbi_setpage(tty,0x100,VBI_ANY_SUBNO);
274 
275     for (last = 0; !last;) {
276 	FD_ZERO(&set);
277 	FD_SET(0,&set);
278 	FD_SET(vbi->fd,&set);
279 	tv.tv_sec  = 1;
280 	tv.tv_usec = 0;
281 	rc = select(vbi->fd+1,&set,NULL,NULL,&tv);
282 	if (-1 == rc) {
283 	    tty_restore();
284 #ifdef WITH_LINUX_FB_H
285 	    if (have_fb)
286 		fb_cleanup();
287 #endif
288 	    perror("select");
289 	    exit(1);
290 	}
291 	if (0 == rc) {
292 #ifdef WITH_LINUX_FB_H
293 	    if (have_fb)
294 		fb_cleanup();
295 #endif
296 	    tty_restore();
297 	    fprintf(stderr,"oops: timeout\n");
298 	    exit(1);
299 	}
300 	if (FD_ISSET(0,&set)) {
301 	    /* tty input */
302 	    rc = read(0, key, 10);
303 	    key[rc] = 0;
304 	    if (1 == rc) {
305 		switch (key[0]) {
306 		case 'q':
307 		case 'Q':
308 		    /* quit */
309 		    last = 1;
310 		    break;
311 		case 'L' & 0x1f:
312 		    /* refresh */
313 #ifdef WITH_LINUX_FB_H
314 		    have_fb ? fb_clear() : tty_clear();
315 #else
316 		    tty_clear();
317 #endif
318 		    vbi_render_page(tty);
319 		    break;
320 		case 'i':
321 		case 'I':
322 		    /* index */
323 		    vbi_setpage(tty,0x100,VBI_ANY_SUBNO);
324 		    break;
325 		case ' ':
326 		case 'l':
327 		case 'L':
328 		    /* next page */
329 		    vbi_setpage(tty,vbi_calc_page(tty->pgno,+1),VBI_ANY_SUBNO);
330 		    break;
331 		case '\x7f':
332 		case 'h':
333 		case 'H':
334 		    /* prev page */
335 		    vbi_setpage(tty,vbi_calc_page(tty->pgno,-1),VBI_ANY_SUBNO);
336 		    break;
337 		case 'k':
338 		case 'K':
339 		    /* next subpage */
340 		    subno = (tty->subno != VBI_ANY_SUBNO) ?
341 			tty->subno : tty->pg.subno;
342 		    subno = vbi_calc_subpage(tty->vbi->dec,tty->pgno,subno,+1);
343 		    vbi_setpage(tty,tty->pgno,subno);
344 		    break;
345 		case 'j':
346 		case 'J':
347 		    /* prev subpage */
348 		    subno = (tty->subno != VBI_ANY_SUBNO) ?
349 			tty->subno : tty->pg.subno;
350 		    subno = vbi_calc_subpage(tty->vbi->dec,tty->pgno,subno,-1);
351 		    vbi_setpage(tty,tty->pgno,subno);
352 		    break;
353 		default:
354 		    if (key[0] >= '0'  &&  key[0] <= '9') {
355 			tty->newpage *= 16;
356 			tty->newpage += key[0] - '0';
357 			if (tty->newpage >= 0x100)
358 			    vbi_setpage(tty,tty->newpage,VBI_ANY_SUBNO);
359 		    }
360 		}
361 	    }
362 	}
363 	if (FD_ISSET(vbi->fd,&set)) {
364 	    vbi_hasdata(vbi);
365 	}
366     }
367 #ifdef WITH_LINUX_FB_H
368     if (have_fb)
369 	fb_cleanup();
370 #endif
371     tty_goto(0,0);
372     tty_restore();
373 }
374