]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/mag/plugins/client_fb/client_fb.cc
302229731e0040d8372e0aa004dcc1d0645befba
[l4.git] / l4 / pkg / mag / plugins / client_fb / client_fb.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 "client_fb.h"
11
12 #include <l4/mag-gfx/clip_guard>
13 #include <l4/mag-gfx/texture>
14
15 #include <l4/mag/server/view_stack>
16 #include <l4/mag/server/factory>
17
18 #include <l4/re/env>
19 #include <l4/re/event_enums.h>
20 #include <l4/re/error_helper>
21 #include <l4/re/util/cap_alloc>
22 #include <l4/sys/factory>
23 #include <l4/re/util/meta>
24 #include <l4/re/console>
25
26 #include <l4/mag/server/user_state>
27 #include <cstdio>
28 #include <cstring>
29
30
31 namespace Mag_server {
32
33 using L4Re::chksys;
34 using L4Re::chkcap;
35 using L4Re::Util::Auto_cap;
36
37 enum { Bar_height = 16 };
38
39 Client_fb::Client_fb(Core_api const *core)
40 : View(Rect(), F_need_frame),
41   Icu_svr(1, &_ev_irq),
42   _core(core), _fb(0),
43   _bar_height(Bar_height),
44   _flags(0)
45 {}
46
47 void
48 Client_fb::set_geometry_prop(Session *_s, Property_handler const *, cxx::String const &v)
49 {
50   Client_fb *s = static_cast<Client_fb*>(_s);
51   // ignore multiple geometry properties
52   if (s->_fb)
53     return;
54
55   int w, h, x=50, y=50;
56   int r;
57
58   cxx::String a = v;
59
60   r = a.from_dec(&w);
61   if (r >= a.len() || a[r] != 'x')
62     L4Re::chksys(-L4_EINVAL, "invalid geometry format");
63
64   a = a.substr(r + 1);
65   r = a.from_dec(&h);
66
67   if (r < a.len() && a[r] == '+')
68     {
69       a = a.substr(r + 1);
70       r = a.from_dec(&x);
71     }
72
73   if (r < a.len() && a[r] == '+')
74     {
75       a = a.substr(r + 1);
76       r = a.from_dec(&y);
77     }
78
79   if (w <= 0 || h <= 0 || w >= 10000 || h >= 10000)
80     L4Re::chksys(-L4_ERANGE, "invalid geometry (too big)");
81
82   Area sz = s->_core->user_state()->vstack()->canvas()->size();
83
84   if (x < 10 - w)
85     x = 10 - w;
86
87   if (x >= sz.w())
88     x = sz.w() - 10;
89
90   if (y < 10 - h)
91     y = 10 - h;
92
93   if (y >= sz.h())
94     y = sz.h() - 10;
95
96   s->set_geometry(Rect(Point(x, y), Area(w, h)));
97 }
98
99 void
100 Client_fb::set_flags_prop(Session *_s, Property_handler const *p, cxx::String const &)
101 {
102   Client_fb *s = static_cast<Client_fb*>(_s);
103
104   if (!strcmp(p->tag, "focus"))
105     s->_flags |= F_fb_focus;
106
107   if (!strcmp(p->tag, "shaded"))
108     s->_flags |= F_fb_shaded;
109
110   if (!strcmp(p->tag, "fixed"))
111     s->_flags |= F_fb_fixed_location;
112 }
113
114 void
115 Client_fb::set_bar_height_prop(Session *_s, Property_handler const *, cxx::String const &v)
116 {
117   Client_fb *s = static_cast<Client_fb*>(_s);
118   int r = v.from_dec(&s->_bar_height);
119   if (r < v.len())
120     L4Re::chksys(-L4_EINVAL, "invalid bar height format");
121
122   s->_bar_height = std::max(std::min(s->_bar_height, 100), 4);
123 }
124
125 int
126 Client_fb::setup()
127 {
128   using L4Re::Video::View;
129   using L4Re::Video::Color_component;
130   using L4Re::Video::Goos;
131
132   Area res(size());
133
134   Auto_cap<L4Re::Dataspace>::Cap ds(
135       L4Re::Util::cap_alloc.alloc<L4Re::Dataspace>());
136
137   Screen_factory *sf = dynamic_cast<Screen_factory*>(_core->user_state()->vstack()->canvas()->type()->factory);
138   //Screen_factory *sf = dynamic_cast<Screen_factory*>(Rgb16::type()->factory);
139
140   L4Re::chksys(L4Re::Env::env()->mem_alloc()->alloc(sf->get_texture_size(res),
141                                                     ds.get()));
142
143   L4Re::Rm::Auto_region<void *> dsa;
144   L4Re::chksys(L4Re::Env::env()->rm()->attach(&dsa, ds->size(), L4Re::Rm::Search_addr, ds.get(), 0, L4_SUPERPAGESHIFT));
145
146   _fb = sf->create_texture(res, dsa.get());
147
148   set_geometry(Rect(p1(), visible_size()));
149   dsa.release();
150   _fb_ds = ds.release();
151
152   _view_info.flags = View::F_none;
153
154   _view_info.view_index = 0;
155   _view_info.xpos = 0;
156   _view_info.ypos = 0;
157   _view_info.width = _fb->size().w();
158   _view_info.height = _fb->size().h();
159   _view_info.buffer_offset = 0;
160   _view_info.buffer_index = 0;
161   _view_info.bytes_per_line = _view_info.width * _fb->type()->bytes_per_pixel();
162   _view_info.pixel_info = *_fb->type();
163
164   _screen_info.flags = Goos::F_pointer;
165   _screen_info.width = _view_info.width;
166   _screen_info.height = _view_info.height;
167   _screen_info.num_static_views = 1;
168   _screen_info.num_static_buffers = 1;
169   _screen_info.pixel_info = _view_info.pixel_info;
170
171
172   L4Re::Env const *e = L4Re::Env::env();
173   _ev_ds = L4Re::Util::cap_alloc.alloc<L4Re::Dataspace>();
174
175
176   chksys(e->mem_alloc()->alloc(L4_PAGESIZE, _ev_ds.get()));
177   chksys(e->rm()->attach(&_ev_ds_m, L4_PAGESIZE, L4Re::Rm::Search_addr, _ev_ds.get(), 0, L4_PAGESHIFT));
178
179   _events = L4Re::Event_buffer(_ev_ds_m.get(), L4_PAGESIZE);
180
181   calc_label_sz(_core->label_font());
182   return 0;
183 }
184
185 void
186 Client_fb::view_setup()
187 {
188   if (_flags & F_fb_focus)
189     _core->user_state()->set_focus(this);
190 }
191
192 void
193 Client_fb::draw(Canvas *canvas, View_stack const *, Mode mode) const
194 {
195   /* use dimming in x-ray mode */
196   Canvas::Mix_mode op = mode.flat() ? Canvas::Solid : Canvas::Mixed;
197
198   /* do not dim the focused view in x-ray mode */
199   if (mode.xray() && !mode.kill() && focused())
200     op = Canvas::Solid;
201
202   /*
203    * The view content and label should never overdraw the
204    * frame of the view in non-flat Nitpicker modes. The frame
205    * is located outside the view area. By shrinking the
206    * clipping area to the view area, we protect the frame.
207    */
208   Clip_guard clip_guard(canvas, *this);
209
210   /*
211    * If the clipping area shrinked to zero,
212    * we do not process drawing operations.
213    */
214   if (!canvas->clip_valid()/* || !_session*/)
215     return;
216
217   /* draw view content */
218   Rgb32::Color mix_color = /*mode.kill() ? kill_color() :*/ session()->color();
219
220   canvas->draw_box(top(_bar_height), Rgb32::Color(56, 68, 88));
221
222   canvas->draw_texture(_fb, mix_color, p1() + Point(0, _bar_height), op);
223 }
224
225 Area
226 Client_fb::visible_size() const
227 {
228   if (_flags & F_fb_shaded)
229     return Area(_fb->size().w(), _bar_height);
230
231   return _fb->size() + Area(0, _bar_height);
232 }
233
234
235 void
236 Client_fb::toggle_shaded()
237 {
238   Rect r = *this;
239   _flags ^= F_fb_shaded;
240   Rect n = Rect(p1(), visible_size());
241   set_geometry(n);
242   _core->user_state()->vstack()->refresh_view(0, 0, r | n);
243 }
244
245 bool
246 Client_fb::handle_core_event(Hid_report *e, Point const &mouse)
247 {
248   static Point left_drag;
249   static l4_umword_t drag_dev;
250   bool consumed = false;
251
252   Valuator<int> const *abs = e->get_vals(3);
253   if (abs && abs->get(1).valid() && left_drag != Point())
254     {
255       Rect npos = Rect(p1() + mouse - left_drag, size());
256       left_drag = mouse;
257       _core->user_state()->vstack()->viewport(this, npos, true);
258       consumed = true;
259     }
260
261   Rect bar = top(_bar_height);
262
263   Hid_report::Key_event const *btn_left = e->find_key_event(L4RE_BTN_LEFT);
264   View_stack *_stack = _core->user_state()->vstack();
265   if (btn_left && btn_left->value == 1 && !_stack->on_top(this))
266     _stack->push_top(this);
267
268   if (btn_left && bar.contains(mouse) && !(_flags & F_fb_fixed_location))
269     {
270       if (btn_left->value == 1 && left_drag == Point())
271         {
272           left_drag = mouse;
273           drag_dev = e->device_id();
274         }
275       else if (btn_left->value == 0 && e->device_id() == drag_dev)
276         left_drag = Point();
277       consumed = true;
278     }
279
280   Hid_report::Key_event const *btn_middle = e->find_key_event(L4RE_BTN_MIDDLE);
281   if (btn_middle && btn_middle->value == 1 && bar.contains(mouse))
282     {
283       toggle_shaded();
284       consumed = true;
285     }
286
287   // no events if window is shaded
288   if (consumed || (_flags & F_fb_shaded))
289     return true;
290
291   return false;
292 }
293
294 namespace {
295   struct Abs_xfrm
296   {
297     Point p1;
298     Area sz;
299     Axis_info_vector const *v;
300
301     Abs_xfrm(Axis_info_vector const *v, Point const &p1, Area const &sz)
302     : p1(p1), sz(sz), v(v) {}
303
304     void operator () (unsigned axis, int &val) const
305     {
306       Axis_info const *ai = v->get(axis);
307       if (ai)
308         {
309           switch (ai->mode)
310             {
311             case 1:
312               val = cxx::min(sz.w(), cxx::max(0, val - p1.x()));
313               break;
314             case 2:
315               val = cxx::min(sz.h(), cxx::max(0, val - p1.y()));
316               break;
317             default:
318               break;
319             }
320         }
321     }
322   };
323 }
324
325 void
326 Client_fb::handle_event(Hid_report *e, Point const &mouse, bool core_dev)
327 {
328   static Point left_drag;
329
330   if (core_dev && handle_core_event(e, mouse))
331     return;
332
333   // no events if window is shaded
334   if (_flags & F_fb_shaded)
335     return;
336
337   Abs_xfrm xfrm(e->abs_infos(), p1() + Point(0, _bar_height), _fb->size());
338   bool trigger = post_hid_report(e, _events, xfrm);
339
340   if (trigger)
341     _ev_irq.trigger();
342 }
343
344
345 int
346 Client_fb::refresh(int x, int y, int w, int h)
347 {
348   _core->user_state()->vstack()->refresh_view(this, 0, Rect(p1() + Point(x, y + _bar_height), Area(w, h)));
349   return 0;
350 }
351
352 int
353 Client_fb::get_stream_info_for_id(l4_umword_t id, L4Re::Event_stream_info *info)
354 {
355   return _core->user_state()->get_input_stream_info_for_id(id, info);
356 }
357
358 int
359 Client_fb::get_abs_info(l4_umword_t id, unsigned naxes, unsigned *axes,
360                         L4Re::Event_absinfo *infos)
361 {
362   unsigned char ax_mode[naxes];
363   int i = _core->user_state()->get_input_axis_info(id, naxes, axes, infos, ax_mode);
364   //  < 0: means we got an error
365   // == 0: means the device is an non-core device and not translated
366   // == 1: means ths device is used as core input pointer (adjust X and Y)
367   if (i < 0)
368     return i;
369
370   for (unsigned a = 0; a < naxes; ++a)
371     {
372       switch (ax_mode[a])
373         {
374         default: break;
375         case 0: break;
376         case 1:
377                 infos[a].min = 0;
378                 infos[a].max = _fb->size().w();
379                 break;
380         case 2:
381                 infos[a].min = 0;
382                 infos[a].max = _fb->size().h();
383                 break;
384         }
385     }
386   return i;
387 }
388
389 inline int
390 Client_fb::event_get(L4::Ipc::Iostream &s)
391 {
392   s << _ev_ds.get();
393   return L4_EOK;
394 }
395
396 inline int
397 Client_fb::event_get_stream_info_for_id(L4::Ipc::Iostream &s)
398 {
399   L4Re::Event_stream_info info;
400   l4_umword_t id;
401
402   s >> id;
403   int i = get_stream_info_for_id(id, &info);
404   if (i < 0)
405     return i;
406
407   s.put(info);
408   return i;
409 }
410
411 inline int
412 Client_fb::event_get_axis_info(L4::Ipc::Iostream &s)
413 {
414   l4_umword_t id;
415   long unsigned naxes = L4RE_ABS_MAX;
416   unsigned axes[L4RE_ABS_MAX];
417   s >> id >> L4::Ipc::buf_cp_in(axes, naxes);
418   L4Re::Event_absinfo infos[naxes];
419   int i = get_abs_info(id, naxes, axes, infos);
420   if (i < 0)
421     return i;
422
423   s << L4::Ipc::buf_cp_out(infos, naxes);
424   return i;
425 }
426
427 inline int
428 Client_fb::event_dispatch(l4_umword_t, L4::Ipc::Iostream &s)
429 {
430   using namespace L4Re;
431
432   L4::Opcode op;
433   s >> op;
434   switch (op)
435     {
436     case Event_::Get:
437       return event_get(s);
438     case Event_::Get_stream_info_for_id:
439       return event_get_stream_info_for_id(s);
440     case Event_::Get_axis_info:
441       return event_get_axis_info(s);
442     default:
443       return -L4_ENOSYS;
444     }
445 }
446
447 int
448 Client_fb::dispatch(l4_umword_t obj, L4::Ipc::Iostream &s)
449 {
450   using namespace L4Re;
451
452   l4_msgtag_t tag;
453   s >> tag;
454
455   switch (tag.label())
456     {
457     case L4::Meta::Protocol:
458       return L4Re::Util::handle_meta_request<L4Re::Console>(s);
459     case L4_PROTO_IRQ:
460       return Icu_svr::dispatch(obj, s);
461     case Protocol::Goos:
462       return L4Re::Util::Video::Goos_svr::dispatch(obj, s);
463     case Protocol::Event:
464       return event_dispatch(obj, s);
465     default:
466       return -L4_EBADPROTO;
467     }
468 }
469
470
471 void
472 Client_fb::destroy()
473 {
474   _core->user_state()->forget_view(this);
475   delete _fb;
476   _fb = 0;
477 }
478
479 }