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