]> 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       unsigned sf = 0;
95       if (v->session())
96         sf = v->session()->flags();
97
98       if (sf & Session::F_ignore)
99         continue;
100
101       if (!v->background())
102         return v;
103
104       if (v == _background || v == bg)
105         return v;
106
107       if (!bg && (sf & Session::F_default_background))
108         return v;
109     }
110
111   return 0;
112 }
113
114 Rect
115 View_stack::outline(View const *v) const
116 {
117   if (_mode.flat() || !v->need_frame())
118     return *v;
119   else
120     return v->grow(v->frame_width());
121 }
122
123 void
124 View_stack::viewport(View *v, Rect const &pos, bool) const
125 {
126   Rect old = outline(v);
127
128   /* take over new view parameters */
129   v->set_geometry(pos);
130
131   Rect compound = old | outline(v);
132
133   /* update labels (except when moving the mouse cursor) */
134   if (v != top())
135     place_labels(compound);
136
137   /* update area on screen */
138   rdq.q(compound);
139 //  draw_recursive(top(), 0, /*redraw ? 0 : view->session(),*/ compound);
140 }
141
142 void
143 View_stack::draw_frame(View const *v) const
144 {
145   if (_mode.flat() || !v->need_frame() || !v->session())
146     return;
147
148   Rgb32::Color color = v->session()->color();
149   Rgb32::Color outline = v->focused() ? Rgb32::White : Rgb32::Black;
150
151   int w = v->frame_width()-1;
152   _canvas->draw_rect(v->offset(-1-w, -1-w, 1+w, 1+w), outline);
153   _canvas->draw_rect(*v, color, -w);
154 }
155
156 static void
157 draw_string_outline(Canvas *c, Point const &pos, Font const *f, char const *txt)
158 {
159   for (int i = -1; i <= 1; ++i)
160     for (int k = -1; k <= 1; ++k)
161       if (i || k)
162         c->draw_string(pos + Point(i, k), f, Rgb32::Black, txt);
163 }
164
165 void
166 View_stack::draw_label(View const *v) const
167 {
168   if (_mode.flat() || !v->need_frame())
169     return;
170
171   char const *const sl = v->session()->label();
172   Point pos = v->label_pos() + Point(1, 1);
173   draw_string_outline(_canvas, pos, _label_font, sl);
174   _canvas->draw_string(pos, _label_font, Rgb32::White, sl);
175
176   char const *const vl = v->title();
177   if (!vl)
178     return;
179
180   pos = pos + Point(_label_font->str_w(sl) + View::Label_sep, 0);
181   draw_string_outline(_canvas, pos, _label_font, vl);
182   _canvas->draw_string(pos, _label_font, Rgb32::White, vl);
183 }
184
185 void
186 View_stack::set_focused(View *v)
187 {
188   _focused = v;
189   if (!v)
190     return;
191
192   // stack all 'above' tagged views of the focussed session
193   // to the top. We keep their respective order.
194   Session *s = v->session();
195   if (!s)
196     return;
197
198   View *top = _no_stay_top;
199   for (View *t = _top.first(); t; t = _top.next(t))
200     {
201       if (!t->above())
202         continue;
203
204       if (t->session() == s)
205         {
206           stack(t, top, true);
207           top = t;
208         }
209     }
210
211   // if the view is not a background than raise the view relative to
212   // all views of other sessions, but keep the stacking order within
213   // it's session
214   if (v->background())
215     return;
216
217   for (top = _top.prev(v); top != _no_stay_top && top && top->session() != s;
218        top = _top.prev(top))
219     ;
220
221   if (top != _top.prev(v))
222     stack(v, top, true);
223 }
224
225 void
226 View_stack::draw_recursive(View const *v, View const *dst, Rect const &rect) const
227 { draw_recursive(v, dst, rect, current_background()); }
228
229 void
230 View_stack::draw_recursive(View const *v, View const *dst, Rect const &rect,
231                            View const *bg) const
232 {
233   Rect clipped;
234
235   /* find next view that intersects with the current clipping rectangle */
236   while (v && !(clipped = outline(v) & rect).valid())
237     v = next_view(v, bg);
238
239   if (!v)
240     return;
241
242   View const *n = next_view(v, bg);
243   Rect_tuple border;
244
245   if (v->transparent() && n)
246     {
247       draw_recursive(n, dst, rect, bg);
248       n = 0;
249     }
250   else
251     border = rect - clipped;
252
253   if (n && border.t().valid())
254     draw_recursive(n, dst, border.t(), bg);
255   if (n && border.l().valid())
256     draw_recursive(n, dst, border.l(), bg);
257
258   if (!dst || dst == v || v->transparent())
259     {
260       Clip_guard g(_canvas, clipped);
261       draw_frame(v);
262       v->draw(_canvas, this, _mode);
263       draw_label(v);
264     }
265
266   if (n && border.r().valid())
267     draw_recursive(n, dst, border.r(), bg);
268   if (n && border.b().valid())
269     draw_recursive(n, dst, border.b(), bg);
270 }
271
272 void
273 View_stack::refresh_view(View const *v, View const *dst, Rect const &rect) const
274 {
275   (void)dst;
276   Rect r = rect;
277   if (v)
278     r = r & outline(v);
279
280   rdq.q(r);
281   //draw_recursive(top(), dst, r);
282 }
283
284 void
285 View_stack::flush()
286 {
287   //static int cnt = 0;
288   for (Redraw_queue::iterator i = rdq.begin(); i != rdq.end(); ++i)
289     {
290       //printf("redraw[%d] %d,%d-%d,%d\n", cnt++, i->x1(), i->y1(), i->x2(), i->y2());
291       draw_recursive(top(), 0, *i);
292       if (_canvas_view)
293         _canvas_view->refresh(i->x1(), i->y1(), i->w(), i->h());
294     }
295
296   rdq.clear();
297 }
298
299 void
300 View_stack::stack(View *v, View *pivot, bool behind)
301 {
302   remove(v);
303   if (behind)
304     insert_after(v, pivot);
305   else
306     insert_before(v, pivot);
307
308   place_labels(*v);
309
310   refresh_view(v, 0, outline(v));
311 }
312
313 void
314 View_stack::push_bottom(View *v)
315 {
316   Session *s = v->session();
317   View *b = s ? s->background() : 0;
318   stack(v, (b && (b != v)) ? b : _background, false);
319 }
320
321
322 View *
323 View_stack::find(Point const &pos) const
324 {
325   View *bg = current_background();
326   View *n = next_view(top(), bg);
327   while (n && !n->contains(pos))
328     n = next_view(n, bg);
329
330   return n;
331 }
332
333 void
334 View_stack::optimize_label_rec(View *cv, View *lv, Rect const &rect, Rect *optimal,
335                                View *bg) const
336 {
337   /* if label already fits in optimized rectangle, we are happy */
338   if (optimal->fits(lv->label_sz()))
339     return;
340
341   /* find next view that intersects with the rectangle or the target view */
342   Rect clipped;
343   while (cv && cv != lv && !(clipped = outline(cv) & rect).valid())
344     cv = next_view(cv, bg);
345
346   /* reached end of view stack */
347   if (!cv)
348     return;
349
350   if (cv != lv && next_view(cv, bg))
351     {
352       /* cut current view from rectangle and go into sub rectangles */
353       Rect r[4] =
354         { Rect(rect.p1(), Point(rect.x2(), clipped.y1() - 1)),
355           Rect(rect.p1(), Point(clipped.x1() - 1, rect.y2())),
356           Rect(Point(clipped.x2() + 1, rect.y1()), rect.p2()),
357           Rect(Point(rect.x1(), clipped.y2() + 1), rect.p2()) };
358       for (int i = 0; i < 4; i++)
359         optimize_label_rec(next_view(cv, bg), lv, r[i], optimal, bg);
360
361       return;
362     }
363
364   /*
365    * Now, cv equals lv and we must decide how to configure the
366    * optimal rectangle.
367    */
368
369   /* stop if label does not fit vertically */
370   if (rect.h() < lv->label_sz().h())
371     return;
372
373   /*
374    * If label fits completely within current rectangle, we are done.
375    * If label's width is not fully visible, choose the widest rectangle.
376    */
377   if (rect.fits(lv->label_sz()) || (rect.w() > optimal->w()))
378     *optimal = rect;
379 }
380
381 void
382 View_stack::do_place_labels(Rect const &rect) const
383 {
384   View *bg = current_background();
385   View *start = next_view(_top.first(), bg);
386   /* ignore mouse cursor */
387   for (View *view = start; view && next_view(view); view = next_view(view, bg))
388     if ((*view & rect).valid())
389       {
390         Rect old(view->label_pos(), view->label_sz());
391
392         /* calculate best visible label position */
393         Rect rect = Rect(Point(0, 0), _canvas->size()) & *view;
394         Rect best;
395         optimize_label_rec(start, view, rect, &best, bg);
396
397         /*
398          * If label is not fully visible, we ensure to display the first
399          * (most important) part. Otherwise, we center the label horizontally.
400          */
401         int x = best.x1();
402         if (best.fits(view->label_sz()))
403           x += (best.w() - view->label_sz().w()) / 2;
404
405         view->label_pos(Point(x, best.y1()));
406
407         /* refresh old and new label positions */
408         refresh_view(view, view, old);
409         Rect n = Rect(view->label_pos(), view->label_sz());
410         refresh_view(view, view, n);
411       }
412 }
413
414 }