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