]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/mag/plugins/mag_client/mag_client.cc
Update
[l4.git] / l4 / pkg / mag / plugins / mag_client / mag_client.cc
1 /*
2  * (c) 2010 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
3  *          Alexander Warg <warg@os.inf.tu-dresden.de>
4  *     economic rights: Technische Universität Dresden (Germany)
5  *
6  * This file is part of TUD:OS and distributed under the terms of the
7  * GNU General Public License 2.
8  * Please see the COPYING-GPL-2 file for details.
9  */
10 #include <l4/re/env>
11 #include <l4/re/namespace>
12 #include <l4/re/event_enums.h>
13 #include <l4/re/event-sys.h>
14 #include <l4/re/error_helper>
15 #include <l4/re/rm>
16 #include <l4/re/dataspace>
17 #include <l4/re/util/video/goos_svr>
18 #include <l4/re/util/event_svr>
19 #include <l4/re/util/icu_svr>
20 #include <l4/re/video/goos-sys.h>
21 #include <l4/re/video/goos>
22 #include <l4/re/console>
23 #include <l4/re/util/meta>
24
25 #include <l4/mag/server/plugin>
26 #include <l4/mag/server/object>
27 #include <l4/mag/server/session>
28 #include <l4/mag/server/user_state>
29 #include <l4/mag-gfx/clip_guard>
30 #include <l4/mag-gfx/texture>
31 #include <l4/mag-gfx/factory>
32
33 #include <l4/cxx/list>
34 #include <l4/cxx/auto_ptr>
35
36 #include <cstring>
37 #include <cstdio>
38 #include <memory>
39 #include <vector>
40 #include <list>
41
42 namespace Mag_server { namespace {
43
44 using L4Re::Util::Auto_cap;
45 using std::auto_ptr;
46 using Mag_gfx::Texture;
47 using Mag_gfx::Area;
48 using L4Re::chksys;
49
50 class Mag_client :
51   public L4::Epiface_t<Mag_client, L4::Factory, Object>,
52   private Plugin
53 {
54 private:
55   Core_api const *_core;
56
57 public:
58   Mag_client() : Plugin("Mag client") {}
59   char const *type() const { return "Mag client"; }
60   void start(Core_api *core);
61
62   void destroy();
63   long op_create(L4::Factory::Rights rights, L4::Ipc::Cap<void> &obj,
64                  l4_mword_t proto, L4::Ipc::Varg_list<> const &args);
65 };
66
67 class Client_buffer;
68 class Client_view;
69
70 class Mag_goos
71 : public Session,
72   public L4::Epiface_t<Mag_goos, L4Re::Console, Object>,
73   public L4Re::Util::Icu_cap_array_svr<Mag_goos>
74 {
75 private:
76   typedef L4Re::Util::Icu_cap_array_svr<Mag_goos> Icu_svr;
77   typedef L4Re::Video::Goos::Rights Goos_rights;
78
79   Core_api const *_core;
80   L4Re::Util::Auto_cap<L4Re::Dataspace>::Cap _ev_ds;
81   Irq _ev_irq;
82   L4Re::Rm::Auto_region<void*> _ev_ds_m;
83   L4Re::Event_buffer _events;
84
85   typedef std::vector<cxx::Ref_ptr<Client_buffer> >  Buffer_vector;
86   typedef std::vector<cxx::Auto_ptr<Client_view> > View_vector;
87
88   Buffer_vector _buffers;
89   View_vector _views;
90
91 public:
92   long op_info(Goos_rights, L4Re::Video::Goos::Info &i);
93
94   long op_get_static_buffer(Goos_rights, unsigned idx,
95                             L4::Ipc::Cap<L4Re::Dataspace> &ds);
96
97   long op_create_buffer(Goos_rights, unsigned long, L4::Ipc::Cap<L4Re::Dataspace> &);
98
99   long op_delete_buffer(Goos_rights, unsigned)
100   { return -L4_ENOSYS; }
101
102   long op_create_view(Goos_rights);
103
104   long op_delete_view(Goos_rights, unsigned);
105
106   long op_view_info(Goos_rights, unsigned idx, L4Re::Video::View::Info &info);
107
108   long op_set_view_info(Goos_rights, unsigned, L4Re::Video::View::Info const &);
109
110   long op_view_stack(Goos_rights, unsigned view, unsigned pivot, bool behind);
111
112   long op_view_refresh(Goos_rights, unsigned idx, int x, int y, int w, int h);
113
114   long op_refresh(Goos_rights, int x, int y, int w, int h);
115
116   long op_get_buffer(L4Re::Event::Rights, L4::Ipc::Cap<L4Re::Dataspace> &ds)
117   {
118     _events.reset();
119     ds = L4::Ipc::Cap<L4Re::Dataspace>(_ev_ds.get(), L4_CAP_FPAGE_RW);
120     return L4_EOK;
121   }
122
123   long op_get_num_streams(L4Re::Event::Rights)
124   { return -L4_ENOSYS; }
125
126   long op_get_stream_info(L4Re::Event::Rights, int, L4Re::Event_stream_info &)
127   { return -L4_ENOSYS; }
128
129   long op_get_stream_info_for_id(L4Re::Event::Rights, l4_umword_t id,
130                                  L4Re::Event_stream_info &info)
131   { return _core->user_state()->get_input_stream_info_for_id(id, &info); }
132
133   long op_get_axis_info(L4Re::Event::Rights, l4_umword_t id,
134                         L4::Ipc::Array_in_buf<unsigned, unsigned long> const &axes,
135                         L4::Ipc::Array_ref<L4Re::Event_absinfo, unsigned long> &info)
136   {
137     unsigned naxes = cxx::min<unsigned>(L4RE_ABS_MAX, axes.length);
138
139     info.length = 0;
140
141     L4Re::Event_absinfo _info[naxes];
142     int r = _core->user_state()->get_input_axis_info(id, naxes, axes.data, _info, 0);
143     if (r < 0)
144       return r;
145
146     for (unsigned i = 0; i < naxes; ++i)
147       info.data[i] = _info[i];
148
149     info.length = naxes;
150     return r;
151   }
152
153   long op_get_stream_state_for_id(L4Re::Event::Rights, l4_umword_t,
154                                   L4Re::Event_stream_state &)
155   { return -L4_ENOSYS; }
156
157
158
159 public:
160   Mag_goos(Core_api const *core);
161
162   using Icu_svr::op_info;
163
164   void put_event(Hid_report *e, bool trigger);
165   void put_event(l4_umword_t stream, int type, int code, int value,
166                  l4_uint64_t time);
167
168   void destroy();
169
170   static void set_default_background(Session *_s, Property_handler const *, cxx::String const &);
171 };
172
173 class Client_buffer : public cxx::Ref_obj
174 {
175 private:
176   L4Re::Util::Auto_cap<L4Re::Dataspace>::Cap _ds;
177   L4Re::Rm::Auto_region<void *> _texture_mem;
178   unsigned long _size;
179
180 public:
181   unsigned index;
182
183   Client_buffer(Core_api const *core, unsigned long size);
184
185   L4::Cap<L4Re::Dataspace> ds_cap() const { return _ds.get(); }
186   void *local_addr() const { return _texture_mem.get(); }
187   unsigned long size() const { return _size; }
188 };
189
190
191 class Client_view : public View
192 {
193 private:
194   Core_api const *_core;
195   cxx::Ref_ptr<Client_buffer> _buffer;
196   Mag_goos *_screen;
197   Texture *_front_txt;
198   Texture *_back_txt;
199
200   unsigned long _buf_offset;
201
202   void swap_textures()
203   {
204     Texture *tmp = _front_txt;
205     asm volatile ("" : : : "memory");
206     _front_txt = _back_txt;
207     _back_txt = tmp;
208   }
209
210 public:
211   Client_view(Core_api const *core, Mag_goos *screen);
212   virtual ~Client_view();
213
214   void draw(Canvas *, View_stack const *, Mode) const;
215   void handle_event(Hid_report *e, Point const &mouse, bool core_dev);
216
217   void get_info(L4Re::Video::View::Info *inf) const;
218   void set_info(L4Re::Video::View::Info const &inf,
219                 cxx::Ref_ptr<Client_buffer> const &b);
220
221   Session *session() const { return _screen; }
222 };
223
224 Client_view::Client_view(Core_api const *core, Mag_goos *screen)
225 : View(Rect(), F_need_frame), _core(core), _buffer(0), _screen(screen),
226   _buf_offset(0)
227 {
228   Pixel_info const *pi = core->user_state()->vstack()->canvas()->type();
229
230   _front_txt = pi->factory->create_texture(Area(0,0), (void*)1);
231   _back_txt = pi->factory->create_texture(Area(0,0), (void*)1);
232   calc_label_sz(core->label_font());
233 }
234
235 Client_view::~Client_view()
236 {
237   if (_screen && _screen->background() == this)
238     {
239       // look for other background views below
240       View_stack *vs = _core->user_state()->vstack();
241       // We can either search below this view in the stack, or
242       // we can search from the top of the stack to find the uppermost
243       // view of our session that is tagged as background
244       View *v = vs->next_view(this); // search below this view
245       // View *v = vs->top(); // Search from the top of the stack
246       for (; v; v = vs->next_view(v))
247         if (v != this && v->session() == _screen && v->background())
248           break;
249       _screen->background(v);
250     }
251
252   _core->user_state()->forget_view(this);
253   delete _back_txt;
254   delete _front_txt;
255 }
256
257 inline
258 void
259 Client_view::get_info(L4Re::Video::View::Info *inf) const
260 {
261   using L4Re::Video::Color_component;
262   inf->flags = L4Re::Video::View::F_fully_dynamic;
263   // we do not support changing the pixel format
264   inf->flags &= ~L4Re::Video::View::F_set_pixel;
265   if (above())
266     inf->flags |= L4Re::Video::View::F_above;
267
268   inf->xpos = x1();
269   inf->ypos = y1();
270   inf->width = w();
271   inf->height = h();
272   inf->buffer_offset = _buf_offset;
273
274   Pixel_info const *pi = 0;
275   pi = _front_txt->type();
276
277   inf->bytes_per_line = pi->bytes_per_pixel() * _front_txt->size().w();
278   inf->pixel_info = *pi;
279
280   if (_buffer)
281     inf->buffer_index = _buffer->index;
282   else
283     inf->buffer_index = ~0;
284 }
285
286 inline
287 void
288 Client_view::set_info(L4Re::Video::View::Info const &inf,
289                       cxx::Ref_ptr<Client_buffer> const &b)
290 {
291   Pixel_info const *pi = _core->user_state()->vstack()->canvas()->type();
292
293   bool recalc_height = false;
294   _back_txt->size(_front_txt->size());
295   _back_txt->pixels(_front_txt->pixels());
296
297   if (inf.flags & L4Re::Video::View::F_set_flags)
298     set_above(inf.flags & L4Re::Video::View::F_above);
299
300   if (inf.flags & L4Re::Video::View::F_set_background)
301     {
302       _core->user_state()->vstack()->push_bottom(this);
303       set_as_background();
304       _screen->background(this);
305     }
306
307   if (inf.has_set_bytes_per_line())
308     {
309       _back_txt->size(Area(inf.bytes_per_line / pi->bytes_per_pixel(), 0));
310       recalc_height = true;
311     }
312
313   if (inf.has_set_buffer())
314     {
315       _back_txt->pixels((char *)b->local_addr() + _buf_offset);
316       _buffer = b;
317       recalc_height = true;
318     }
319
320   if (!_buffer)
321     {
322       _back_txt->size(Area(0, 0));
323       _back_txt->pixels((char *)0);
324       _front_txt->size(Area(0, 0));
325       _front_txt->pixels((char *)0);
326     }
327
328   if (inf.has_set_buffer_offset() && _buffer)
329     {
330       _back_txt->pixels((char *)_buffer->local_addr() + inf.buffer_offset);
331       _buf_offset = inf.buffer_offset;
332       recalc_height = true;
333     }
334
335   if (recalc_height && _buffer)
336     {
337       unsigned long w = _back_txt->size().w();
338       unsigned long bw = w * pi->bytes_per_pixel();
339       unsigned long h;
340
341       if (bw > 0 && w > 0)
342         {
343           h = _buffer->size();
344           if (h > _buf_offset)
345             h -= _buf_offset;
346           else
347             h = 0;
348
349           h /= bw;
350         }
351       else
352         {
353           w = 0;
354           h = 0;
355         }
356       _back_txt->size(Area(w, h));
357     }
358
359   if (_back_txt->size() != _front_txt->size()
360       || _back_txt->pixels() != _front_txt->pixels())
361     swap_textures();
362
363   if (inf.has_set_position())
364     _core->user_state()->vstack()->viewport(this, Rect(Point(inf.xpos,
365             inf.ypos), Area(inf.width, inf.height)), true);
366 }
367
368
369 Mag_goos::Mag_goos(Core_api const *core)
370 : Icu_svr(1, &_ev_irq), _core(core)
371 {
372   L4Re::Env const *e = L4Re::Env::env();
373   _ev_ds = L4Re::Util::cap_alloc.alloc<L4Re::Dataspace>();
374
375   chksys(e->mem_alloc()->alloc(L4_PAGESIZE, _ev_ds.get()));
376   chksys(e->rm()->attach(&_ev_ds_m, L4_PAGESIZE, L4Re::Rm::Search_addr,
377                          L4::Ipc::make_cap_rw(_ev_ds.get())));
378
379   _events = L4Re::Event_buffer(_ev_ds_m.get(), L4_PAGESIZE);
380 }
381
382 void Mag_client::start(Core_api *core)
383 {
384   _core = core;
385   core->registry()->register_obj(cxx::Ref_ptr<Mag_client>(this), "mag");
386   if (!obj_cap().is_valid())
387     printf("Service registration failed.\n");
388   else
389     printf("Plugin: Mag_client service started\n");
390 }
391
392 void Mag_goos::set_default_background(Session *_s, Property_handler const *, cxx::String const &)
393 {
394   Mag_goos *s = static_cast<Mag_goos *>(_s);
395
396   s->flags(F_default_background, 0);
397 }
398
399 namespace {
400   Session::Property_handler const _opts[] =
401     { { "default-background", false,  &Mag_goos::set_default_background },
402       { "dfl-bg",             false,  &Mag_goos::set_default_background },
403       { 0, 0, 0 }
404     };
405 };
406
407 long
408 Mag_client::op_create(L4::Factory::Rights, L4::Ipc::Cap<void> &obj,
409                       l4_mword_t proto, L4::Ipc::Varg_list<> const &args)
410 {
411   if (!L4::kobject_typeid<L4Re::Console>()->has_proto(proto))
412     return -L4_ENODEV;
413
414   cxx::Ref_ptr<Mag_goos> cf(new Mag_goos(_core));
415   _core->set_session_options(cf.get(), args, _opts);
416
417   _core->register_session(cf.get());
418   _core->registry()->register_obj(cf);
419   cf->obj_cap()->dec_refcnt(1);
420
421   obj = L4::Ipc::make_cap(cf->obj_cap(), L4_CAP_FPAGE_RWSD);
422   return L4_EOK;
423 }
424
425
426 void
427 Mag_client::destroy()
428 {
429 }
430
431 inline long
432 Mag_goos::op_info(Goos_rights, L4Re::Video::Goos::Info &i)
433 {
434   using L4Re::Video::Color_component;
435   using L4Re::Video::Goos;
436
437   Area a = _core->user_state()->vstack()->canvas()->size();
438   Pixel_info const *mag_pi = _core->user_state()->vstack()->canvas()->type();
439   i.width = a.w();
440   i.height = a.h();
441   i.flags = Goos::F_pointer
442     | Goos::F_dynamic_views
443     | Goos::F_dynamic_buffers;
444
445   i.num_static_views = 0;
446   i.num_static_buffers = 0;
447   i.pixel_info = *mag_pi;
448
449   return L4_EOK;
450 }
451
452 inline long
453 Mag_goos::op_create_buffer(Goos_rights, unsigned long size,
454                            L4::Ipc::Cap<L4Re::Dataspace> &ds)
455 {
456   cxx::Ref_ptr<Client_buffer> b(new Client_buffer(_core, size));
457   _buffers.push_back(b);
458   b->index = _buffers.size() - 1;
459   ds = L4::Ipc::Cap<L4Re::Dataspace>(b->ds_cap(), L4_CAP_FPAGE_RW);
460   return b->index;
461 }
462
463 inline long
464 Mag_goos::op_create_view(Goos_rights)
465 {
466   cxx::Auto_ptr<Client_view> v(new Client_view(_core, this));
467   unsigned idx = 0;
468   for (View_vector::iterator i = _views.begin(); i != _views.end();
469       ++i, ++idx)
470     if (!*i)
471       {
472         *i = v;
473         return idx;
474       }
475
476   _views.push_back(v);
477   return _views.size() - 1;
478 }
479
480 inline long
481 Mag_goos::op_delete_view(Goos_rights, unsigned idx)
482 {
483   if (idx >= _views.size())
484     return -L4_ERANGE;
485
486   _views[idx].reset(0);
487   return L4_EOK;
488 }
489
490 inline long
491 Mag_goos::op_get_static_buffer(Goos_rights, unsigned idx,
492                                L4::Ipc::Cap<L4Re::Dataspace> &ds)
493 {
494   if (idx >= _buffers.size())
495     return -L4_ERANGE;
496
497   ds = L4::Ipc::Cap<L4Re::Dataspace>(_buffers[idx]->ds_cap(), L4_CAP_FPAGE_RW);
498   return L4_EOK;
499 }
500
501 inline long
502 Mag_goos::op_view_info(Goos_rights, unsigned idx, L4Re::Video::View::Info &info)
503 {
504   if (idx >= _views.size())
505     return -L4_ERANGE;
506
507   Client_view *cv = _views[idx].get();
508
509   L4Re::Video::View::Info vi;
510   vi.view_index = idx;
511   cv->get_info(&info);
512
513   return L4_EOK;
514 }
515
516 inline long
517 Mag_goos::op_set_view_info(Goos_rights, unsigned idx,
518                            L4Re::Video::View::Info const &vi)
519 {
520   if (idx >= _views.size())
521     return -L4_ERANGE;
522
523   Client_view *cv = _views[idx].get();
524
525   cxx::Weak_ptr<Client_buffer> cb(0);
526   if (vi.has_set_buffer())
527     {
528       if (vi.buffer_index >= _buffers.size())
529         return -L4_ERANGE;
530
531       cb = _buffers[vi.buffer_index];
532     }
533
534   cv->set_info(vi, cb);
535   return L4_EOK;
536 }
537
538 inline long
539 Mag_goos::op_view_stack(Goos_rights, unsigned cvi, unsigned pvi, bool behind)
540 {
541   Client_view *pivot = 0;
542   Client_view *cv;
543
544   if (cvi >= _views.size())
545     return -L4_ERANGE;
546
547   cv = _views[cvi].get();
548
549   if (pvi < _views.size())
550     pivot = _views[pvi].get();
551
552   if (!pivot)
553     {
554       if (!behind)
555         _core->user_state()->vstack()->push_bottom(cv);
556       else
557         _core->user_state()->vstack()->push_top(cv);
558     }
559   else
560     _core->user_state()->vstack()->stack(cv, pivot, behind);
561
562   return L4_EOK;
563 }
564
565 inline long
566 Mag_goos::op_view_refresh(Goos_rights, unsigned idx, int x, int y, int w, int h)
567 {
568   if (idx >= _views.size())
569     return -L4_ERANGE;
570
571   Client_view *cv = _views[idx].get();
572   _core->user_state()->vstack()->refresh_view(cv, 0, Rect(cv->p1() + Point(x,y), Area(w,h)));
573
574   return L4_EOK;
575 }
576
577 inline long
578 Mag_goos::op_refresh(Goos_rights, int x, int y, int w, int h)
579 {
580   _core->user_state()->vstack()->refresh_view(0, 0, Rect(Point(x,y), Area(w,h)));
581
582   return L4_EOK;
583 }
584
585 void
586 Mag_goos::destroy()
587 {
588   _buffers.clear();
589   _views.clear();
590 }
591
592
593 Client_buffer::Client_buffer(Core_api const *, unsigned long size)
594 : _size(l4_round_page(size))
595 {
596   L4Re::Rm::Auto_region<void *> dsa;
597   _ds = L4Re::Util::cap_alloc.alloc<L4Re::Dataspace>();
598
599   L4Re::chksys(L4Re::Env::env()->mem_alloc()->alloc(_size, _ds.get()));
600   L4Re::chksys(L4Re::Env::env()->rm()
601                  ->attach(&dsa, _size, L4Re::Rm::Search_addr,
602                           L4::Ipc::make_cap_rw(_ds.get())));
603
604   _texture_mem = dsa;
605 }
606
607
608 void
609 Mag_goos::put_event(Hid_report *e, bool _trigger)
610 {
611   if (post_hid_report(e, _events, Axis_xfrm_noop()) && _trigger)
612     _ev_irq.trigger();
613 }
614
615 void
616 Mag_goos::put_event(l4_umword_t stream, int type, int code, int value,
617                     l4_uint64_t time)
618 {
619   L4Re::Event_buffer::Event e;
620   e.time = time;
621   e.payload.stream_id = stream;
622   e.payload.type = type;
623   e.payload.code = code;
624   e.payload.value = value;
625   _events.put(e);
626   _ev_irq.trigger();
627 }
628
629
630 void
631 Client_view::draw(Canvas *c, View_stack const *, Mode mode) const
632 {
633   Canvas::Mix_mode op = mode.flat() ? Canvas::Solid : Canvas::Mixed;
634   if (mode.xray() && !mode.kill() && focused())
635     op = Canvas::Solid;
636
637   Clip_guard cg(c, *this);
638
639   if (!c->clip_valid())
640     return;
641
642   Rgb32::Color mix_color = /*mode.kill() ? kill_color() :*/ session()->color();
643   Area s(0, 0);
644   if (_buffer)
645     {
646       c->draw_texture(_front_txt, mix_color, p1(), op);
647       s = _front_txt->size();
648     }
649
650   Area r = size() - s;
651   if (r.h() > 0)
652     c->draw_box(Rect(p1() + Point(0, s.h()), Area(size().w(), r.h())), mix_color);
653
654   if (r.w() > 0 && size().h() != r.h())
655     c->draw_box(Rect(p1() + Point(s.w(), 0), Area(r.w(), s.h())), mix_color);
656 }
657
658 void
659 Client_view::handle_event(Hid_report *e, Point const &, bool)
660 {
661   _screen->put_event(e, true);
662 }
663
664
665 static Mag_client _mag_client;
666
667 }}