source: mainline/uspace/lib/gui/window.c@ 6d5e378

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 6d5e378 was 6d5e378, checked in by Martin Decky <martin@…>, 13 years ago

cherrypick GUI implementation (originally by Petr Koupy), with several major changes

  • for character-oriented devices a new output server and output protocol was created based on the original fb server
  • DDF visualizer drivers are pixel-oriented only
  • console and compositor can coexist in the same build
  • terminal widget is self-sufficient, no strange console nesting is needed
  • Property mode set to 100644
File size: 17.3 KB
Line 
1/*
2 * Copyright (c) 2012 Petr Koupy
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup gui
30 * @{
31 */
32/**
33 * @file
34 */
35
36#include <bool.h>
37#include <errno.h>
38#include <stdio.h>
39
40#include <as.h>
41#include <malloc.h>
42#include <str.h>
43
44#include <fibril.h>
45#include <task.h>
46#include <adt/prodcons.h>
47#include <adt/list.h>
48
49#include <async.h>
50#include <loc.h>
51
52#include <io/pixel.h>
53#include <source.h>
54#include <font.h>
55#include <drawctx.h>
56#include <surface.h>
57
58#include "connection.h"
59#include "widget.h"
60#include "window.h"
61
62static sysarg_t border_thickness = 5;
63static sysarg_t header_height = 20;
64static sysarg_t header_min_width = 40;
65static sysarg_t close_width = 20;
66
67static pixel_t border_color = PIXEL(255, 0, 0, 0);
68static pixel_t header_bgcolor = PIXEL(255, 25, 25, 112);
69static pixel_t header_fgcolor = PIXEL(255, 255, 255, 255);
70
71static void paint_internal(widget_t *w)
72{
73 surface_t *surface = window_claim(w->window);
74 if (!surface) {
75 window_yield(w->window);
76 }
77
78 source_t source;
79 font_t font;
80 drawctx_t drawctx;
81
82 source_init(&source);
83 font_init(&font, FONT_DECODER_EMBEDDED, NULL, 16);
84 drawctx_init(&drawctx, surface);
85 drawctx_set_source(&drawctx, &source);
86 drawctx_set_font(&drawctx, &font);
87
88 source_set_color(&source, border_color);
89 drawctx_transfer(&drawctx, w->hpos, w->vpos, border_thickness, w->height);
90 drawctx_transfer(&drawctx, w->hpos + w->width - border_thickness,
91 w->vpos, border_thickness, w->height);
92 drawctx_transfer(&drawctx, w->hpos, w->vpos, w->width, border_thickness);
93 drawctx_transfer(&drawctx, w->hpos,
94 w->vpos + w->height - border_thickness, w->width, border_thickness);
95
96 source_set_color(&source, header_bgcolor);
97 drawctx_transfer(&drawctx,
98 w->hpos + border_thickness, w->vpos + border_thickness,
99 w->width - 2 * border_thickness, header_height);
100
101 sysarg_t cpt_width;
102 sysarg_t cpt_height;
103 font_get_box(&font, w->window->caption, &cpt_width, &cpt_height);
104 sysarg_t cls_width;
105 sysarg_t cls_height;
106 char cls_pict[] = "x";
107 font_get_box(&font, cls_pict, &cls_width, &cls_height);
108 source_set_color(&source, header_fgcolor);
109 sysarg_t cls_x = ((close_width - cls_width) / 2) + w->hpos + w->width -
110 border_thickness - close_width;
111 sysarg_t cls_y = ((header_height - cls_height) / 2) + w->vpos + border_thickness;
112 drawctx_print(&drawctx, cls_pict, cls_x, cls_y);
113
114 bool draw_title = (w->width >= 2 * border_thickness + close_width + cpt_width);
115 if (draw_title) {
116 sysarg_t cpt_x = ((w->width - cpt_width) / 2) + w->hpos;
117 sysarg_t cpt_y = ((header_height - cpt_height) / 2) + w->vpos + border_thickness;
118 if (w->window->caption) {
119 drawctx_print(&drawctx, w->window->caption, cpt_x, cpt_y);
120 }
121 }
122
123 font_release(&font);
124 window_yield(w->window);
125}
126
127static void root_destroy(widget_t *widget)
128{
129 widget_deinit(widget);
130}
131
132static void root_reconfigure(widget_t *widget)
133{
134 if (widget->window->is_decorated) {
135 list_foreach(widget->children, link) {
136 widget_t *child = list_get_instance(link, widget_t, link);
137 child->rearrange(child,
138 widget->hpos + border_thickness,
139 widget->vpos + border_thickness + header_height,
140 widget->width - 2 * border_thickness,
141 widget->height - 2 * border_thickness - header_height);
142 }
143 } else {
144 list_foreach(widget->children, link) {
145 widget_t *child = list_get_instance(link, widget_t, link);
146 child->rearrange(child, widget->hpos, widget->vpos,
147 widget->width, widget->height);
148 }
149 }
150}
151
152static void root_rearrange(widget_t *widget, sysarg_t hpos, sysarg_t vpos,
153 sysarg_t width, sysarg_t height)
154{
155 widget_modify(widget, hpos, vpos, width, height);
156 if (widget->window->is_decorated) {
157 paint_internal(widget);
158 list_foreach(widget->children, link) {
159 widget_t *child = list_get_instance(link, widget_t, link);
160 child->rearrange(child,
161 hpos + border_thickness,
162 vpos + border_thickness + header_height,
163 width - 2 * border_thickness,
164 height - 2 * border_thickness - header_height);
165 }
166 } else {
167 list_foreach(widget->children, link) {
168 widget_t *child = list_get_instance(link, widget_t, link);
169 child->rearrange(child, hpos, vpos, width, height);
170 }
171 }
172}
173
174static void root_repaint(widget_t *widget)
175{
176 if (widget->window->is_decorated) {
177 paint_internal(widget);
178 }
179 list_foreach(widget->children, link) {
180 widget_t *child = list_get_instance(link, widget_t, link);
181 child->repaint(child);
182 }
183 if (widget->window->is_decorated) {
184 window_damage(widget->window);
185 }
186}
187
188static void root_handle_keyboard_event(widget_t *widget, kbd_event_t event)
189{
190 if (!list_empty(&widget->children)) {
191 widget_t *child = (widget_t *) list_first(&widget->children);
192 child->handle_keyboard_event(child, event);
193 }
194}
195
196static void root_handle_position_event(widget_t *widget, pos_event_t event)
197{
198 if (widget->window->is_decorated) {
199 sysarg_t width = widget->width;
200 sysarg_t height = widget->height;
201
202 bool btn_left = (event.btn_num == 1) && (event.type == POS_PRESS);
203 bool btn_right = (event.btn_num == 2) && (event.type == POS_PRESS);
204 bool allowed_button = btn_left || btn_right;
205
206 bool left = (event.hpos < border_thickness);
207 bool right = (event.hpos >= width - border_thickness);
208 bool top = (event.vpos < border_thickness);
209 bool bottom = (event.vpos >= height - border_thickness);
210 bool header = (event.hpos >= border_thickness) &&
211 (event.hpos < width - border_thickness) &&
212 (event.vpos >= border_thickness) &&
213 (event.vpos < border_thickness + header_height);
214 bool close = header && (event.hpos >= width - border_thickness - close_width);
215
216 if (top && left && allowed_button) {
217 window_grab_flags_t flags = GF_EMPTY;
218 flags |= GF_MOVE_X;
219 flags |= GF_MOVE_Y;
220 flags |= btn_left ? GF_RESIZE_X : GF_SCALE_X;
221 flags |= btn_left ? GF_RESIZE_Y : GF_SCALE_Y;
222 win_grab(widget->window->osess, event.pos_id, flags);
223 } else if (bottom && left && allowed_button) {
224 window_grab_flags_t flags = GF_EMPTY;
225 flags |= GF_MOVE_X;
226 flags |= btn_left ? GF_RESIZE_X : GF_SCALE_X;
227 flags |= btn_left ? GF_RESIZE_Y : GF_SCALE_Y;
228 win_grab(widget->window->osess, event.pos_id, flags);
229 } else if (bottom && right && allowed_button) {
230 window_grab_flags_t flags = GF_EMPTY;
231 flags |= btn_left ? GF_RESIZE_X : GF_SCALE_X;
232 flags |= btn_left ? GF_RESIZE_Y : GF_SCALE_Y;
233 win_grab(widget->window->osess, event.pos_id, flags);
234 } else if (top && right && allowed_button) {
235 window_grab_flags_t flags = GF_EMPTY;
236 flags |= GF_MOVE_Y;
237 flags |= btn_left ? GF_RESIZE_X : GF_SCALE_X;
238 flags |= btn_left ? GF_RESIZE_Y : GF_SCALE_Y;
239 win_grab(widget->window->osess, event.pos_id, flags);
240 } else if (top && allowed_button) {
241 window_grab_flags_t flags = GF_EMPTY;
242 flags |= GF_MOVE_Y;
243 flags |= btn_left ? GF_RESIZE_Y : GF_SCALE_Y;
244 win_grab(widget->window->osess, event.pos_id, flags);
245 } else if (left && allowed_button) {
246 window_grab_flags_t flags = GF_EMPTY;
247 flags |= GF_MOVE_X;
248 flags |= btn_left ? GF_RESIZE_X : GF_SCALE_X;
249 win_grab(widget->window->osess, event.pos_id, flags);
250 } else if (bottom && allowed_button) {
251 window_grab_flags_t flags = GF_EMPTY;
252 flags |= btn_left ? GF_RESIZE_Y : GF_SCALE_Y;
253 win_grab(widget->window->osess, event.pos_id, flags);
254 } else if (right && allowed_button) {
255 window_grab_flags_t flags = GF_EMPTY;
256 flags |= btn_left ? GF_RESIZE_X : GF_SCALE_X;
257 win_grab(widget->window->osess, event.pos_id, flags);
258 } else if (close && btn_left) {
259 win_close_request(widget->window->osess);
260 } else if (header && btn_left) {
261 window_grab_flags_t flags = GF_EMPTY;
262 flags |= GF_MOVE_X;
263 flags |= GF_MOVE_Y;
264 win_grab(widget->window->osess, event.pos_id, flags);
265 } else {
266 list_foreach(widget->children, link) {
267 widget_t *child = list_get_instance(link, widget_t, link);
268 child->handle_position_event(child, event);
269 }
270 }
271 } else {
272 list_foreach(widget->children, link) {
273 widget_t *child = list_get_instance(link, widget_t, link);
274 child->handle_position_event(child, event);
275 }
276 }
277}
278
279static void deliver_keyboard_event(window_t *win, kbd_event_t event)
280{
281 if (win->focus) {
282 win->focus->handle_keyboard_event(win->focus, event);
283 } else {
284 win->root.handle_keyboard_event(&win->root, event);
285 }
286}
287
288static void deliver_position_event(window_t *win, pos_event_t event)
289{
290 if (win->grab) {
291 win->grab->handle_position_event(win->grab, event);
292 } else {
293 win->root.handle_position_event(&win->root, event);
294 }
295}
296
297static void handle_signal_event(window_t *win, sig_event_t event)
298{
299 widget_t *widget = (widget_t *) event.object;
300 slot_t slot = (slot_t) event.slot;
301 void *data = (void *) event.argument;
302
303 slot(widget, data);
304
305 free(data);
306}
307
308static void handle_resize(window_t *win, sysarg_t width, sysarg_t height)
309{
310 int rc;
311 surface_t *old_surface;
312 surface_t *new_surface;
313
314 if (width < 2 * border_thickness + header_min_width) {
315 win_damage(win->osess, 0, 0, 0, 0);
316 return;
317 }
318
319 if (height < 2 * border_thickness + header_height) {
320 win_damage(win->osess, 0, 0, 0, 0);
321 return;
322 }
323
324 /* Allocate resources for new surface. */
325 new_surface = surface_create(width, height, NULL, SURFACE_FLAG_SHARED);
326 if (!new_surface) {
327 return;
328 }
329
330 /* Switch new and old surface. */
331 fibril_mutex_lock(&win->guard);
332 old_surface = win->surface;
333 win->surface = new_surface;
334 fibril_mutex_unlock(&win->guard);
335
336 /* Let all widgets in the tree alter their position and size. Widgets might
337 * also paint themselves onto the new surface. */
338 win->root.rearrange(&win->root, 0, 0, width, height);
339
340 fibril_mutex_lock(&win->guard);
341 surface_reset_damaged_region(win->surface);
342 fibril_mutex_unlock(&win->guard);
343
344 /* Inform compositor about new surface. */
345 rc = win_resize(win->osess,
346 width, height, surface_direct_access(new_surface));
347
348 if (rc != EOK) {
349 /* Rollback to old surface. Reverse all changes. */
350
351 sysarg_t old_width = 0;
352 sysarg_t old_height = 0;
353 if (old_surface) {
354 surface_get_resolution(old_surface, &old_width, &old_height);
355 }
356
357 fibril_mutex_lock(&win->guard);
358 new_surface = win->surface;
359 win->surface = old_surface;
360 fibril_mutex_unlock(&win->guard);
361
362 win->root.rearrange(&win->root, 0, 0, old_width, old_height);
363
364 if (win->surface) {
365 fibril_mutex_lock(&win->guard);
366 surface_reset_damaged_region(win->surface);
367 fibril_mutex_unlock(&win->guard);
368 }
369
370 surface_destroy(new_surface);
371 return;
372 }
373
374 /* Finally deallocate old surface. */
375 if (old_surface) {
376 surface_destroy(old_surface);
377 }
378}
379
380static void handle_refresh(window_t *win)
381{
382 win->root.repaint(&win->root);
383}
384
385static void handle_damage(window_t *win)
386{
387 sysarg_t x, y, width, height;
388 fibril_mutex_lock(&win->guard);
389 surface_get_damaged_region(win->surface, &x, &y, &width, &height);
390 surface_reset_damaged_region(win->surface);
391 fibril_mutex_unlock(&win->guard);
392
393 if (width > 0 && height > 0) {
394 /* Notify compositor. */
395 win_damage(win->osess, x, y, width, height);
396 }
397}
398
399static void destroy_children(widget_t *widget)
400{
401 /* Recursively destroy widget tree in bottom-top order. */
402 while (!list_empty(&widget->children)) {
403 widget_t *child =
404 list_get_instance(list_first(&widget->children), widget_t, link);
405 destroy_children(child);
406 child->destroy(child);
407 }
408}
409
410static void handle_close(window_t *win)
411{
412 destroy_children(&win->root);
413 win->root.destroy(&win->root);
414 win->grab = NULL;
415 win->focus = NULL;
416
417 win_close(win->osess);
418 async_hangup(win->isess);
419 async_hangup(win->osess);
420
421 while (!list_empty(&win->events.list)) {
422 list_remove(list_first(&win->events.list));
423 }
424
425 if (win->surface) {
426 surface_destroy(win->surface);
427 }
428
429 free(win->caption);
430
431 free(win);
432}
433
434/* Window event loop. Runs in own dedicated fibril. */
435static int event_loop(void *arg)
436{
437 bool is_main = false;
438 bool terminate = false;
439 window_t *win = (window_t *) arg;
440
441 while (true) {
442 window_event_t *event = (window_event_t *) prodcons_consume(&win->events);
443
444 switch (event->type) {
445 case ET_KEYBOARD_EVENT:
446 deliver_keyboard_event(win, event->data.kbd);
447 break;
448 case ET_POSITION_EVENT:
449 deliver_position_event(win, event->data.pos);
450 break;
451 case ET_SIGNAL_EVENT:
452 handle_signal_event(win, event->data.sig);
453 break;
454 case ET_WINDOW_RESIZE:
455 handle_resize(win, event->data.rsz.width, event->data.rsz.height);
456 break;
457 case ET_WINDOW_REFRESH:
458 handle_refresh(win);
459 break;
460 case ET_WINDOW_DAMAGE:
461 handle_damage(win);
462 break;
463 case ET_WINDOW_CLOSE:
464 is_main = win->is_main;
465 handle_close(win);
466 terminate = true;
467 break;
468 default:
469 break;
470 }
471
472 free(event);
473 if (terminate) {
474 break;
475 }
476 }
477
478 if (is_main) {
479 exit(0); /* Terminate whole task. */
480 }
481 return 0;
482}
483
484/* Input fetcher from compositor. Runs in own dedicated fibril. */
485static int fetch_input(void *arg)
486{
487 int rc;
488 bool terminate = false;
489 window_t *win = (window_t *) arg;
490
491 while (true) {
492 window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
493
494 if (event) {
495 rc = win_get_event(win->isess, event);
496 if (rc == EOK) {
497 terminate = (event->type == ET_WINDOW_CLOSE);
498 link_initialize(&event->link);
499 prodcons_produce(&win->events, &event->link);
500 } else {
501 free(event);
502 terminate = true;
503 }
504 } else {
505 terminate = true;
506 }
507
508 if (terminate) {
509 break;
510 }
511 }
512
513 return 0;
514}
515
516window_t *window_open(char *winreg, bool is_main, bool is_decorated, const char *caption)
517{
518 int rc;
519
520 window_t *win = (window_t *) malloc(sizeof(window_t));
521 if (!win) {
522 return NULL;
523 }
524
525 win->is_main = is_main;
526 win->is_decorated = is_decorated;
527 prodcons_initialize(&win->events);
528 fibril_mutex_initialize(&win->guard);
529 widget_init(&win->root, NULL);
530 win->root.window = win;
531 win->root.destroy = root_destroy;
532 win->root.reconfigure = root_reconfigure;
533 win->root.rearrange = root_rearrange;
534 win->root.repaint = root_repaint;
535 win->root.handle_keyboard_event = root_handle_keyboard_event;
536 win->root.handle_position_event = root_handle_position_event;
537 win->grab = NULL;
538 win->focus = NULL;
539 win->surface = NULL;
540
541 service_id_t reg_dsid;
542 async_sess_t *reg_sess;
543
544 rc = loc_service_get_id(winreg, &reg_dsid, 0);
545 if (rc != EOK) {
546 free(win);
547 return NULL;
548 }
549
550 reg_sess = loc_service_connect(EXCHANGE_SERIALIZE, reg_dsid, 0);
551 if (reg_sess == NULL) {
552 free(win);
553 return NULL;
554 }
555
556 service_id_t in_dsid;
557 service_id_t out_dsid;
558
559 rc = win_register(reg_sess, &in_dsid, &out_dsid);
560 async_hangup(reg_sess);
561 if (rc != EOK) {
562 free(win);
563 return NULL;
564 }
565
566 win->osess = loc_service_connect(EXCHANGE_SERIALIZE, out_dsid, 0);
567 if (win->osess == NULL) {
568 free(win);
569 return NULL;
570 }
571
572 win->isess = loc_service_connect(EXCHANGE_SERIALIZE, in_dsid, 0);
573 if (win->isess == NULL) {
574 async_hangup(win->osess);
575 free(win);
576 return NULL;
577 }
578
579 if (caption == NULL) {
580 win->caption = NULL;
581 } else {
582 win->caption = str_dup(caption);
583 }
584
585 return win;
586}
587
588void window_resize(window_t *win, sysarg_t width, sysarg_t height)
589{
590 window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
591 if (event) {
592 link_initialize(&event->link);
593 event->type = ET_WINDOW_RESIZE;
594 event->data.rsz.width = width;
595 event->data.rsz.height = height;
596 prodcons_produce(&win->events, &event->link);
597 }
598}
599
600void window_refresh(window_t *win)
601{
602 window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
603 if (event) {
604 link_initialize(&event->link);
605 event->type = ET_WINDOW_REFRESH;
606 prodcons_produce(&win->events, &event->link);
607 }
608}
609
610void window_damage(window_t *win)
611{
612 window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
613 if (event) {
614 link_initialize(&event->link);
615 event->type = ET_WINDOW_DAMAGE;
616 prodcons_produce(&win->events, &event->link);
617 }
618}
619
620widget_t *window_root(window_t *win)
621{
622 return &win->root;
623}
624
625void window_exec(window_t *win)
626{
627 fid_t ev_fid = fibril_create(event_loop, win);
628 fid_t fi_fid = fibril_create(fetch_input, win);
629 if (!ev_fid || !fi_fid) {
630 return;
631 }
632 fibril_add_ready(ev_fid);
633 fibril_add_ready(fi_fid);
634}
635
636surface_t *window_claim(window_t *win)
637{
638 fibril_mutex_lock(&win->guard);
639 return win->surface;
640}
641
642void window_yield(window_t *win)
643{
644 fibril_mutex_unlock(&win->guard);
645}
646
647void window_close(window_t *win)
648{
649 /* Request compositor to init closing cascade. */
650 win_close_request(win->osess);
651}
652
653/** @}
654 */
Note: See TracBrowser for help on using the repository browser.