]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/fbterminal/server/src/main.cc
update
[l4.git] / l4 / pkg / fbterminal / server / src / main.cc
1 /*!
2  * \file
3  * \brief  A terminal using an L4Re::Framebuffer via L4::Con
4  *
5  * \date
6  * \author Adam Lackorzynski <adam@os.inf.tu-dresden.de>
7  *
8  */
9 /*
10  * (c) 2009 Author(s)
11  *     economic rights: Technische Universität Dresden (Germany)
12  *
13  * This file is part of TUD:OS and distributed under the terms of the
14  * GNU General Public License 2.
15  * Please see the COPYING-GPL-2 file for details.
16  */
17
18 #include <l4/re/console>
19 #include <l4/re/env>
20 #include <l4/re/event>
21 #include <l4/re/event_enums.h>
22 #include <l4/re/namespace>
23 #include <l4/re/util/cap_alloc>
24 #include <l4/re/util/video/goos_fb>
25 #include <l4/re/util/object_registry>
26 #include <l4/sys/err.h>
27 #include <l4/sys/kdebug.h>
28 #include <l4/libgfxbitmap/font.h>
29 #include <l4/libgfxbitmap/support>
30 #include <l4/lib_vt100/vt100.h>
31 #include <l4/event/event>
32 #include <l4/cxx/ipc_stream>
33 #include <l4/cxx/ipc_server>
34 #include <l4/cxx/exceptions>
35 #include <l4/re/protocols>
36 #include <l4/re/log-sys.h>
37 #include <l4/util/util.h>
38 #include <l4/re/util/icu_svr>
39 #include <l4/re/util/vcon_svr>
40 #include <l4/sys/typeinfo_svr>
41
42 #include <pthread-l4.h>
43
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <errno.h>
48 #include <new>
49
50 static L4Re::Util::Video::Goos_fb fb;
51 static void *fb_addr;
52 static L4::Cap<L4Re::Dataspace> ev_ds;
53 static L4::Cap<L4::Irq> ev_irq;
54
55 static L4Re::Event_buffer ev_buffer;
56
57 static L4Re::Video::View::Info fbi;
58 static l4_uint32_t fn_x, fn_y;
59
60 // vt100 interpreter of ouput stream from client
61 termstate_t *term;
62
63 static L4::Cap<void> rcv_cap()
64 {
65   static L4::Cap<void> r = L4Re::Util::cap_alloc.alloc<void>();
66   return r;
67 }
68
69
70 class Terminal : public L4::Server_object,
71                  public L4Re::Util::Icu_cap_array_svr<Terminal>,
72                  public L4Re::Util::Vcon_svr<Terminal>
73 {
74 public:
75   typedef L4Re::Util::Icu_cap_array_svr<Terminal> Icu_svr;
76   typedef L4Re::Util::Vcon_svr<Terminal>          Vcon_svr;
77
78   explicit Terminal();
79   int dispatch(l4_umword_t obj, L4::Ipc_iostream &ios);
80
81   void trigger() { _irq.trigger(); }
82
83   static L4::Cap<void> rcv_cap() { return ::rcv_cap(); }
84
85   unsigned vcon_read(char *buf, unsigned size) throw();
86   void vcon_write(const char *buf, unsigned size) throw();
87   int vcon_set_attr(l4_vcon_attr_t const *attr) throw();
88   int vcon_get_attr(l4_vcon_attr_t *attr) throw();
89
90 private:
91   Icu_svr::Irq _irq;
92 };
93
94 Terminal *terminal;
95
96
97 // vt100 interpreter backend
98 static void con_puts(termstate_t *term, int x, int y, l4_int8_t *s, int len,
99                       unsigned fg, unsigned bg)
100 {
101   (void)term;
102   gfxbitmap_font_text(fb_addr, (l4re_video_view_info_t *)&fbi,
103                       0, (char*)s, len, fn_x * x, fn_y * y, fg, bg);
104
105   fb.refresh(fn_x * x, fn_y * y, fn_x * len, fn_y);
106 }
107
108 termstate_t *term_init(int cols, int rows, int hist)
109 {
110   // get new termstate
111   termstate_t *term = (termstate_t *)malloc(sizeof(termstate_t));
112   if (!term)
113     {
114       printf("malloc for new term failed");
115       return 0;
116     }
117
118   // explizitely init. these, as they may be freed in init_termstate
119   term->text = 0;
120   term->attrib = 0;
121   term->newline = 1;
122
123   // init termstate
124   if ((vt100_init(term, cols, rows, hist)))
125     {
126       free(term);
127       return 0;
128     }
129
130   return term;
131 }
132
133 static const char* init()
134 {
135   // initialize cons frontend
136
137   fb.setup("fb");
138
139   fb_addr = fb.attach_buffer();
140
141   if (fb.view_info(&fbi))
142     return "Cannot get framebuffer info\n";
143
144   gfxbitmap_font_init();
145   fn_x = gfxbitmap_font_width(0);
146   fn_y = gfxbitmap_font_height(0);
147
148   // convert colors to the format used by our framebuffer
149   libterm_init_colors(&fbi);
150
151   // initialize terminal library
152   term = term_init(fbi.width / fn_x, fbi.height / fn_y, 50);
153
154   // initialize input event handling
155
156   ev_ds = L4Re::Util::cap_alloc.alloc<L4Re::Dataspace>();
157   if (!ev_ds.is_valid())
158     return "Cannot allocate cap\n";
159
160   ev_irq = L4Re::Util::cap_alloc.alloc<L4::Irq>();
161   if (!ev_irq.is_valid())
162     return "Cannot allocate cap\n";
163
164   if (l4_error(L4Re::Env::env()->factory()->create_irq(ev_irq)))
165     return "Could not create event IRQ\n";
166
167   if (l4_error(L4::cap_cast<L4Re::Console>(fb.goos())->bind(0, ev_irq)))
168     return "Could not bind event IRQ\n";
169
170   if (L4::cap_cast<L4Re::Console>(fb.goos())->get_buffer(ev_ds))
171     return "Cannot get event dataspace and irq\n";
172
173   l4_addr_t sz = ev_ds->size();
174   void* _buf = 0;
175
176   if (L4Re::Env::env()->rm()->attach(&_buf, sz, L4Re::Rm::Search_addr, ev_ds))
177     return "Cannot attach event dataspace\n";
178
179   ev_buffer = L4Re::Event_buffer(_buf, sz);
180
181   return 0;
182 }
183
184 static void
185 touch_repeat(termstate_t * term, unsigned code, unsigned repeat)
186 {
187  (void)term; (void)code; (void)repeat;
188 }
189
190 void event_handler(void *data)
191 {
192   (void)data;
193   L4Re::Event_buffer::Event *e;
194   int do_trigger = 0;
195
196   while ((e = ev_buffer.next()))
197     {
198       switch (e->payload.type)
199         {
200         case L4RE_EV_KEY:
201             {
202               do_trigger = 1;
203           switch (e->payload.code)
204             {
205             case L4RE_KEY_RIGHTSHIFT:        // modifiers
206             case L4RE_KEY_LEFTSHIFT:
207               if (e->payload.value)
208                 term->__shift = 1;
209               else
210                 term->__shift = 0;
211               touch_repeat(term, e->payload.code, 0);
212               break;
213             case L4RE_KEY_LEFTCTRL:
214             case L4RE_KEY_RIGHTCTRL:
215               if (e->payload.value)
216                 term->__ctrl  = 1;
217               else
218                 term->__ctrl  = 0;
219               touch_repeat(term, e->payload.code, 0);
220               break;
221             case L4RE_KEY_LEFTALT:
222               if (e->payload.value)
223                 term->__alt  = 1;
224               else
225                 term->__alt  = 0;
226               touch_repeat(term, e->payload.code, 0);
227               break;
228             case L4RE_KEY_RIGHTALT:
229               if (e->payload.value)
230                 term->__altgr  = 1;
231               else
232                 term->__altgr  = 0;
233               touch_repeat(term, e->payload.code, 0);
234               break;
235             case L4RE_KEY_PAGEUP:            // special terminal movement chars
236               if (e->payload.value && term->__shift)
237                 {
238                   vt100_scroll_up(term, term->phys_h / 2); // scroll for half screen
239                   vt100_redraw(term);
240                   touch_repeat(term, e->payload.code, e->payload.value);
241                 }
242               break;
243             case L4RE_KEY_PAGEDOWN:
244               if (e->payload.value && term->__shift)
245                 {
246                   vt100_scroll_down(term, term->phys_h / 2); // scroll for half screen
247                   vt100_redraw(term);
248                   touch_repeat(term, e->payload.code, e->payload.value);
249                 }
250               break;
251             }
252           if (e->payload.value)         // regular chars
253             vt100_add_key(term, e->payload.code);
254           touch_repeat(term, e->payload.code, e->payload.value);
255           break;
256             }
257
258 #if 0
259         case L4RE_EV_CON:
260           switch (e->code) 
261             {
262             case EV_CON_REDRAW:
263               LOG("vt100_redraw()");
264               vt100_redraw(term);
265               break;
266             }
267           break;
268 #endif
269         default:
270           //LOGl("Event = %d", e->payload.type);
271           break;
272         }
273
274       e->free();
275     }
276   if (do_trigger)
277     terminal->trigger();
278 }
279
280
281 // convert attributes to corresponding gfxbitmap_color_pix_t
282 static void attribs_to_colors(l4_int8_t a, gfxbitmap_color_pix_t *fg,
283                               gfxbitmap_color_pix_t *bg)
284 {
285     int fg_i, bg_i, in;
286
287     unpack_attribs(a, &fg_i, &bg_i, &in);
288     *fg = libterm_get_color(in, fg_i);
289     *bg = libterm_get_color(in, bg_i);
290 }
291
292 // redraw whole screen
293 void vt100_redraw(termstate_t *term)
294 {
295   int x, y;
296   int old_x = 0, old_y = 0;
297   l4_int8_t *s = NULL;
298   l4_int8_t old_attrib = 0;
299   int old_index = 0;
300   gfxbitmap_color_pix_t fg, bg;
301
302   //    LOGl("term = %p, text = %p, color = %p", term, term->text, term->color);
303   if (term == NULL)
304     return;
305   // correct y for vis offset
306   for (y = 0 - term->vis_off; y < term->phys_h - term->vis_off; y++)
307     {
308       for (x = 0; x < term->w; x++)
309         {
310           //vt100_redraw_xy(term, x, y);
311
312           // if we observe a change in attributes, send the
313           // accumulated string
314           if (s != NULL && old_attrib != term->attrib[xy2index(term, x, y)])
315             {
316               attribs_to_colors(old_attrib, &fg, &bg);
317               // correct y for vis offset
318               con_puts(term, old_x, old_y + term->vis_off, s,
319                        xy2index(term, x, y) - old_index, fg, bg);
320               s = NULL;
321             }
322           // start a new string
323           if (s == NULL)
324             {
325               old_index = xy2index(term, x, y);
326               s = term->text + old_index;
327               old_attrib = term->attrib[old_index];
328               old_x = x;
329               old_y = y;
330             }
331         }
332       // care for end of line remainder strings
333       if (s != NULL)
334         {
335           attribs_to_colors(old_attrib, &fg, &bg);
336           // correct y for vis offset
337           con_puts(term, old_x, old_y + term->vis_off, s,
338               xy2index(term, x - 1, y) - old_index + 1, fg, bg);
339           s = NULL;
340         }
341     }
342 }
343
344 void vt100_redraw_xy(termstate_t *term, int x, int y)
345 {
346   gfxbitmap_color_pix_t fg, bg;
347   l4_int8_t a;
348   l4_int8_t *c;
349
350   // if out of bound, do nothing
351   if (y + term->vis_off >= term->phys_h)
352     {
353       return;
354     }
355
356   c = term->text + xy2index(term, x, y);
357   a = term->attrib[xy2index(term, x, y)];
358   attribs_to_colors(a, &fg, &bg);
359   // correct for moved vis
360   con_puts(term, x, y + term->vis_off, c, 1, fg, bg);
361 }
362
363 // vt100 backends, currently not implemented
364 void vt100_hide_cursor(termstate_t *term)
365 {
366   if (term->cursor_vis)
367     {
368       // if the cursor is in visible area...
369       if ( (term->cur_y + term->vis_off) <= term->phys_h )
370         {
371           int bg, fg, intensity;
372           int index = xy2index( term, term->cur_x, term->cur_y );
373
374           unpack_attribs( term->attrib[index], &fg, &bg, &intensity );
375           term->attrib[index] = pack_attribs( bg, fg, intensity );
376
377           // only redraw if cursor is visible
378           vt100_redraw_xy( term, term->cur_x, term->cur_y );
379         }
380     }
381 }
382
383 void vt100_show_cursor(termstate_t *term)
384 {
385   if (term->cursor_vis)
386     {
387       if ( (term->cur_y + term->vis_off) <= term->phys_h )
388         {
389           int bg, fg, intensity;
390           int index = xy2index( term, term->cur_x, term->cur_y );
391
392           unpack_attribs( term->attrib[index], &fg, &bg, &intensity );
393           term->attrib[index] = pack_attribs( bg, fg, intensity );
394
395           // only redraw if cursor is visible
396           vt100_redraw_xy( term, term->cur_x, term->cur_y );
397         }
398     }
399
400 }
401
402 static L4Re::Util::Object_registry term_registry;
403
404 Terminal::Terminal()
405   : Icu_svr(1, &_irq)
406 {
407 }
408
409 void
410 Terminal::vcon_write(const char *buf, unsigned len) throw()
411 {
412   char mbuf[len];
413   memcpy(mbuf, buf, len);
414
415   //printf("%d: %.*s\n", len, (int)len, mbuf);
416
417   if (term)
418     vt100_write(term, mbuf, len);
419   else
420     L4Re::Env::env()->log()->printn(mbuf, len);
421 }
422
423 unsigned
424 Terminal::vcon_read(char *buf, unsigned len) throw()
425 {
426   int c = 0;
427   char *b = buf;
428   while (len && (c = vt100_trygetchar(term)) != -1)
429     {
430       *b = c;
431       ++b;
432       --len;
433     }
434   return b - buf;
435 }
436
437 int
438 Terminal::vcon_set_attr(l4_vcon_attr_t const *attr) throw()
439 {
440   term->echo    = attr->l_flags & L4_VCON_ECHO;
441   term->newline = attr->o_flags & L4_VCON_ONLCR;
442   return -L4_EOK;
443 }
444
445 int
446 Terminal::vcon_get_attr(l4_vcon_attr_t *attr) throw()
447 {
448   attr->l_flags = term->echo ? L4_VCON_ECHO : 0;
449   attr->o_flags = term->newline ? L4_VCON_ONLCR : 0;
450   attr->i_flags = 0;
451   return -L4_EOK;
452 }
453
454 int
455 Terminal::dispatch(l4_umword_t obj, L4::Ipc_iostream &ios)
456 {
457   l4_msgtag_t tag;
458   ios >> tag;
459
460   switch (tag.label())
461     {
462     case L4::Meta::Protocol:
463       return L4::Util::handle_meta_request<L4::Vcon>(ios);
464     case L4::Irq::Protocol:
465       return Icu_svr::dispatch(obj, ios);
466     case L4::Vcon::Protocol:
467       return Vcon_svr::dispatch(obj, ios);
468     default:
469       return -L4_EBADPROTO;
470     }
471 }
472
473 class Controller : public L4::Server_object
474 {
475 public:
476   int dispatch(l4_umword_t obj, L4::Ipc_iostream &ios);
477 };
478
479 int
480 Controller::dispatch(l4_umword_t, L4::Ipc_iostream &ios)
481 {
482   l4_msgtag_t tag;
483   ios >> tag;
484
485   switch (tag.label())
486     {
487     case L4::Meta::Protocol:
488       return L4::Util::handle_meta_request<L4::Factory>(ios);
489     case L4::Factory::Protocol:
490       break;
491     default:
492       return -L4_EBADPROTO;
493     }
494
495   L4::Factory::Proto op;
496   ios >> op;
497
498   switch (op)
499   {
500   case L4::Vcon::Protocol:
501       {
502         try
503           {
504             Terminal *t = new Terminal;
505             ios << term_registry.register_obj(t);
506           }
507         catch (L4::Runtime_error const &e)
508           {
509             return e.err_no();
510           }
511         catch (std::bad_alloc const &)
512           {
513             return -L4_ENOMEM;
514           }
515       }
516       return L4_EOK;
517     default:
518       return -L4_ENOSYS;
519   }
520 }
521
522 struct My_hooks
523   : public L4::Ipc_svr::Default_timeout,
524     public L4::Ipc_svr::Ignore_errors,
525     public L4::Ipc_svr::Compound_reply
526 {
527   void setup_wait(L4::Ipc_istream &istr, L4::Ipc_svr::Reply_mode)
528   {
529     istr.reset();
530     istr << L4::Small_buf(rcv_cap().cap(), L4_RCV_ITEM_LOCAL_ID);
531     l4_utcb_br_u(istr.utcb())->bdr = 0;
532   }
533 };
534
535 static L4::Server<My_hooks> server(l4_utcb());
536
537 int main()
538 {
539   const char* error = init();
540   if (error)
541     {
542       printf("%s", error);
543       exit(1);
544     }
545
546   Event::Event event(ev_irq, event_handler, 0, 0xff);
547   if (!event.attached())
548     return 1;
549
550   Terminal _terminal;
551   term_registry.register_obj(&_terminal, "term");
552   terminal = &_terminal;
553   if (!term_registry.register_obj(&_terminal, "term"))
554     {
555       printf("Terminal registration failed.\n");
556       return 1;
557     }
558
559   if (0)
560     {
561       Controller ctrl;
562       if (!term_registry.register_obj(&ctrl, "terminal"))
563         {
564           printf("Terminal ctrl registration failed.\n");
565           return 1;
566         }
567     }
568
569   server.loop(term_registry);
570 }
571