]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/mag/server/src/view_stack.cc
update
[l4.git] / l4 / pkg / mag / server / src / view_stack.cc
1 /*
2  * (c) 2010 Alexander Warg <warg@os.inf.tu-dresden.de>
3  *     economic rights: Technische Universität Dresden (Germany)
4  *
5  * This file is part of TUD:OS and distributed under the terms of the
6  * GNU General Public License 2.
7  * Please see the COPYING-GPL-2 file for details.
8  */
9 #include <l4/mag-gfx/clip_guard>
10
11 #include "view_stack"
12 #include "view"
13 #include "session"
14
15 #include <cstdio>
16 #include <cstring>
17
18 namespace Mag_server {
19
20 class Redraw_queue
21 {
22 public:
23   enum { Num_entries = 100 };
24
25   // merge all overlapping rects in the queue
26   void merge()
27   {
28     for (unsigned i = last; i > 0; --i)
29       for (unsigned j = 0; j < last; ++j)
30         {
31           if (i - 1 == j)
32             continue;
33
34           if ((_q[j] & _q[i-1]).valid())
35             {
36               _q[j] = _q[j] | _q[i-1];
37               memmove(_q + i - 1, _q + i, (last - i) * sizeof(Rect));
38               --last;
39               break;
40             }
41         }
42   }
43
44   void q(Rect const &r)
45   {
46     if (!r.valid())
47       return;
48     //printf("q[%d,%d - %d,%d]\n", r.x1(), r.y1(), r.x2(), r.y2());
49     for (unsigned i = 0; i < last; ++i)
50       {
51         if ((_q[i] & r).valid())
52           {
53             // merge with overlapping rect
54             _q[i] = _q[i] | r;
55             merge();
56             return;
57           }
58       }
59
60     if (last < Num_entries)
61       _q[last++] = r; // add new entry
62     else
63       {
64         // queue is full, just merge with the last entry
65         _q[last-1] = _q[last - 1] | r;
66         merge();
67       }
68   }
69
70   void clear()
71   { last = 0; }
72
73   typedef Rect const *iterator;
74
75   iterator begin() const { return _q; }
76   iterator end() const { return _q + last; }
77
78 private:
79   Rect _q[Num_entries];
80   unsigned last;
81 };
82
83 static Redraw_queue rdq;
84
85 View const *
86 View_stack::next_view(View const *v, View const *bg) const
87 {
88   while (v)
89     {
90       v = _top.next(v);
91       if (!v)
92         return 0;
93
94       if (v->session() && v->session()->ignore())
95         continue;
96
97       if (!v->background())
98         return v;
99
100       if (v == _background || v == bg)
101         return v;
102     }
103
104   return 0;
105 }
106
107 Rect
108 View_stack::outline(View const *v) const
109 {
110   if (_mode.flat() || !v->need_frame())
111     return *v;
112   else
113     return v->grow(v->frame_width());
114 }
115
116 void
117 View_stack::viewport(View *v, Rect const &pos, bool) const
118 {
119   Rect old = outline(v);
120
121   /* take over new view parameters */
122   v->set_geometry(pos);
123
124   Rect compound = old | outline(v);
125
126   /* update labels (except when moving the mouse cursor) */
127   if (v != top())
128     place_labels(compound);
129
130   /* update area on screen */
131   rdq.q(compound);
132 //  draw_recursive(top(), 0, /*redraw ? 0 : view->session(),*/ compound);
133 }
134
135 void
136 View_stack::draw_frame(View const *v) const
137 {
138   if (_mode.flat() || !v->need_frame() || !v->session())
139     return;
140
141   Rgb32::Color color = v->session()->color();
142   Rgb32::Color outline = v->focused() ? Rgb32::White : Rgb32::Black;
143
144   int w = v->frame_width()-1;
145   _canvas->draw_rect(v->offset(-1-w, -1-w, 1+w, 1+w), outline);
146   _canvas->draw_rect(*v, color, -w);
147 }
148
149 static void
150 draw_string_outline(Canvas *c, Point const &pos, Font const *f, char const *txt)
151 {
152   for (int i = -1; i <= 1; ++i)
153     for (int k = -1; k <= 1; ++k)
154       if (i || k)
155         c->draw_string(pos + Point(i, k), f, Rgb32::Black, txt);
156 }
157
158 void
159 View_stack::draw_label(View const *v) const
160 {
161   if (_mode.flat() || !v->need_frame())
162     return;
163
164   char const *const sl = v->session()->label();
165   Point pos = v->label_pos() + Point(1, 1);
166   draw_string_outline(_canvas, pos, _label_font, sl);
167   _canvas->draw_string(pos, _label_font, Rgb32::White, sl);
168
169   char const *const vl = v->title();
170   if (!vl)
171     return;
172
173   pos = pos + Point(_label_font->str_w(sl) + View::Label_sep, 0);
174   draw_string_outline(_canvas, pos, _label_font, vl);
175   _canvas->draw_string(pos, _label_font, Rgb32::White, vl);
176 }
177
178 void
179 View_stack::set_focused(View *v)
180 {
181   _focused = v;
182   if (!v)
183     return;
184
185   // stack all 'above' tagged views of the focussed session
186   // to the top. We keep their respective order.
187   Session *s = v->session();
188   if (!s)
189     return;
190
191   View *top = _no_stay_top;
192   for (View *t = _top.first(); t; t = _top.next(t))
193     {
194       if (!t->above())
195         continue;
196
197       if (t->session() == s)
198         {
199           stack(t, top, true);
200           top = t;
201         }
202     }
203
204   // if the view is not a background than raise the view relative to
205   // all views of other sessions, but keep the stacking order within
206   // it's session
207   if (v->background())
208     return;
209
210   for (top = _top.prev(v); top != _no_stay_top && top && top->session() != s;
211        top = _top.prev(top))
212     ;
213
214   if (top != _top.prev(v))
215     stack(v, top, true);
216 }
217
218 void
219 View_stack::draw_recursive(View const *v, View const *dst, Rect const &rect) const
220 { draw_recursive(v, dst, rect, current_background()); }
221
222 void
223 View_stack::draw_recursive(View const *v, View const *dst, Rect const &rect,
224                            View const *bg) const
225 {
226   Rect clipped;
227
228   /* find next view that intersects with the current clipping rectangle */
229   while (v && !(clipped = outline(v) & rect).valid())
230     v = next_view(v, bg);
231
232   if (!v)
233     return;
234
235   View const *n = next_view(v, bg);
236   Rect_tuple border;
237
238   if (v->transparent() && n)
239     {
240       draw_recursive(n, dst, rect, bg);
241       n = 0;
242     }
243   else
244     border = rect - clipped;
245
246   if (n && border.t().valid())
247     draw_recursive(n, dst, border.t(), bg);
248   if (n && border.l().valid())
249     draw_recursive(n, dst, border.l(), bg);
250
251   if (!dst || dst == v || v->transparent())
252     {
253       Clip_guard g(_canvas, clipped);
254       draw_frame(v);
255       v->draw(_canvas, this, _mode);
256       draw_label(v);
257     }
258
259   if (n && border.r().valid())
260     draw_recursive(n, dst, border.r(), bg);
261   if (n && border.b().valid())
262     draw_recursive(n, dst, border.b(), bg);
263 }
264
265 void
266 View_stack::refresh_view(View const *v, View const *dst, Rect const &rect) const
267 {
268   (void)dst;
269   Rect r = rect;
270   if (v)
271     r = r & outline(v);
272
273   rdq.q(r);
274   //draw_recursive(top(), dst, r);
275 }
276
277 void
278 View_stack::flush()
279 {
280   //static int cnt = 0;
281   for (Redraw_queue::iterator i = rdq.begin(); i != rdq.end(); ++i)
282     {
283       //printf("redraw[%d] %d,%d-%d,%d\n", cnt++, i->x1(), i->y1(), i->x2(), i->y2());
284       draw_recursive(top(), 0, *i);
285       if (_canvas_view)
286         _canvas_view->refresh(i->x1(), i->y1(), i->w(), i->h());
287     }
288
289   rdq.clear();
290 }
291
292 void
293 View_stack::stack(View *v, View *pivot, bool behind)
294 {
295   remove(v);
296   if (behind)
297     insert_after(v, pivot);
298   else
299     insert_before(v, pivot);
300
301   place_labels(*v);
302
303   refresh_view(v, 0, outline(v));
304 }
305
306 void
307 View_stack::push_bottom(View *v)
308 {
309   Session *s = v->session();
310   View *b = s ? s->background() : 0;
311   stack(v, (b && (b != v)) ? b : _background, false);
312 }
313
314
315 View *
316 View_stack::find(Point const &pos) const
317 {
318   View *bg = current_background();
319   View *n = next_view(top(), bg);
320   while (n && !n->contains(pos))
321     n = next_view(n, bg);
322
323   return n;
324 }
325
326 void
327 View_stack::optimize_label_rec(View *cv, View *lv, Rect const &rect, Rect *optimal,
328                                View *bg) const
329 {
330   /* if label already fits in optimized rectangle, we are happy */
331   if (optimal->fits(lv->label_sz()))
332     return;
333
334   /* find next view that intersects with the rectangle or the target view */
335   Rect clipped;
336   while (cv && cv != lv && !(clipped = outline(cv) & rect).valid())
337     cv = next_view(cv, bg);
338
339   /* reached end of view stack */
340   if (!cv)
341     return;
342
343   if (cv != lv && next_view(cv, bg))
344     {
345       /* cut current view from rectangle and go into sub rectangles */
346       Rect r[4] =
347         { Rect(rect.p1(), Point(rect.x2(), clipped.y1() - 1)),
348           Rect(rect.p1(), Point(clipped.x1() - 1, rect.y2())),
349           Rect(Point(clipped.x2() + 1, rect.y1()), rect.p2()),
350           Rect(Point(rect.x1(), clipped.y2() + 1), rect.p2()) };
351       for (int i = 0; i < 4; i++)
352         optimize_label_rec(next_view(cv, bg), lv, r[i], optimal, bg);
353
354       return;
355     }
356
357   /*
358    * Now, cv equals lv and we must decide how to configure the
359    * optimal rectangle.
360    */
361
362   /* stop if label does not fit vertically */
363   if (rect.h() < lv->label_sz().h())
364     return;
365
366   /*
367    * If label fits completely within current rectangle, we are done.
368    * If label's width is not fully visible, choose the widest rectangle.
369    */
370   if (rect.fits(lv->label_sz()) || (rect.w() > optimal->w()))
371     *optimal = rect;
372 }
373
374 void
375 View_stack::do_place_labels(Rect const &rect) const
376 {
377   View *bg = current_background();
378   View *start = next_view(_top.first(), bg);
379   /* ignore mouse cursor */
380   for (View *view = start; view && next_view(view); view = next_view(view, bg))
381     if ((*view & rect).valid())
382       {
383         Rect old(view->label_pos(), view->label_sz());
384
385         /* calculate best visible label position */
386         Rect rect = Rect(Point(0, 0), _canvas->size()) & *view;
387         Rect best;
388         optimize_label_rec(start, view, rect, &best, bg);
389
390         /*
391          * If label is not fully visible, we ensure to display the first
392          * (most important) part. Otherwise, we center the label horizontally.
393          */
394         int x = best.x1();
395         if (best.fits(view->label_sz()))
396           x += (best.w() - view->label_sz().w()) / 2;
397
398         view->label_pos(Point(x, best.y1()));
399
400         /* refresh old and new label positions */
401         refresh_view(view, view, old);
402         Rect n = Rect(view->label_pos(), view->label_sz());
403         refresh_view(view, view, n);
404       }
405 }
406
407 }