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