source: mainline/uspace/lib/gui/window.c@ 3e6a98c5

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3e6a98c5 was 3e6a98c5, checked in by Jiri Svoboda <jiri@…>, 13 years ago

Standards-compliant boolean type.

  • Property mode set to 100644
File size: 17.9 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 <stdbool.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_bg_focus_color = PIXEL(255, 25, 25, 112);
69static pixel_t header_fg_focus_color = PIXEL(255, 255, 255, 255);
70static pixel_t header_bg_unfocus_color = PIXEL(255, 70, 130, 180);
71static pixel_t header_fg_unfocus_color = PIXEL(255, 255, 255, 255);
72
73static void paint_internal(widget_t *w)
74{
75 surface_t *surface = window_claim(w->window);
76 if (!surface) {
77 window_yield(w->window);
78 }
79
80 source_t source;
81 font_t font;
82 drawctx_t drawctx;
83
84 source_init(&source);
85 font_init(&font, FONT_DECODER_EMBEDDED, NULL, 16);
86 drawctx_init(&drawctx, surface);
87 drawctx_set_source(&drawctx, &source);
88 drawctx_set_font(&drawctx, &font);
89
90 source_set_color(&source, border_color);
91 drawctx_transfer(&drawctx, w->hpos, w->vpos, border_thickness, w->height);
92 drawctx_transfer(&drawctx, w->hpos + w->width - border_thickness,
93 w->vpos, border_thickness, w->height);
94 drawctx_transfer(&drawctx, w->hpos, w->vpos, w->width, border_thickness);
95 drawctx_transfer(&drawctx, w->hpos,
96 w->vpos + w->height - border_thickness, w->width, border_thickness);
97
98 source_set_color(&source,
99 w->window->is_focused ? header_bg_focus_color : header_bg_unfocus_color);
100 drawctx_transfer(&drawctx,
101 w->hpos + border_thickness, w->vpos + border_thickness,
102 w->width - 2 * border_thickness, header_height);
103
104 sysarg_t cpt_width;
105 sysarg_t cpt_height;
106 font_get_box(&font, w->window->caption, &cpt_width, &cpt_height);
107 sysarg_t cls_width;
108 sysarg_t cls_height;
109 char cls_pict[] = "x";
110 font_get_box(&font, cls_pict, &cls_width, &cls_height);
111 source_set_color(&source,
112 w->window->is_focused ? header_fg_focus_color : header_fg_unfocus_color);
113 sysarg_t cls_x = ((close_width - cls_width) / 2) + w->hpos + w->width -
114 border_thickness - close_width;
115 sysarg_t cls_y = ((header_height - cls_height) / 2) + w->vpos + border_thickness;
116 drawctx_print(&drawctx, cls_pict, cls_x, cls_y);
117
118 bool draw_title = (w->width >= 2 * border_thickness + close_width + cpt_width);
119 if (draw_title) {
120 sysarg_t cpt_x = ((w->width - cpt_width) / 2) + w->hpos;
121 sysarg_t cpt_y = ((header_height - cpt_height) / 2) + w->vpos + border_thickness;
122 if (w->window->caption) {
123 drawctx_print(&drawctx, w->window->caption, cpt_x, cpt_y);
124 }
125 }
126
127 font_release(&font);
128 window_yield(w->window);
129}
130
131static void root_destroy(widget_t *widget)
132{
133 widget_deinit(widget);
134}
135
136static void root_reconfigure(widget_t *widget)
137{
138 if (widget->window->is_decorated) {
139 list_foreach(widget->children, link) {
140 widget_t *child = list_get_instance(link, widget_t, link);
141 child->rearrange(child,
142 widget->hpos + border_thickness,
143 widget->vpos + border_thickness + header_height,
144 widget->width - 2 * border_thickness,
145 widget->height - 2 * border_thickness - header_height);
146 }
147 } else {
148 list_foreach(widget->children, link) {
149 widget_t *child = list_get_instance(link, widget_t, link);
150 child->rearrange(child, widget->hpos, widget->vpos,
151 widget->width, widget->height);
152 }
153 }
154}
155
156static void root_rearrange(widget_t *widget, sysarg_t hpos, sysarg_t vpos,
157 sysarg_t width, sysarg_t height)
158{
159 widget_modify(widget, hpos, vpos, width, height);
160 if (widget->window->is_decorated) {
161 paint_internal(widget);
162 list_foreach(widget->children, link) {
163 widget_t *child = list_get_instance(link, widget_t, link);
164 child->rearrange(child,
165 hpos + border_thickness,
166 vpos + border_thickness + header_height,
167 width - 2 * border_thickness,
168 height - 2 * border_thickness - header_height);
169 }
170 } else {
171 list_foreach(widget->children, link) {
172 widget_t *child = list_get_instance(link, widget_t, link);
173 child->rearrange(child, hpos, vpos, width, height);
174 }
175 }
176}
177
178static void root_repaint(widget_t *widget)
179{
180 if (widget->window->is_decorated) {
181 paint_internal(widget);
182 }
183 list_foreach(widget->children, link) {
184 widget_t *child = list_get_instance(link, widget_t, link);
185 child->repaint(child);
186 }
187 if (widget->window->is_decorated) {
188 window_damage(widget->window);
189 }
190}
191
192static void root_handle_keyboard_event(widget_t *widget, kbd_event_t event)
193{
194 if (!list_empty(&widget->children)) {
195 widget_t *child = (widget_t *) list_first(&widget->children);
196 child->handle_keyboard_event(child, event);
197 }
198}
199
200static void root_handle_position_event(widget_t *widget, pos_event_t event)
201{
202 if (widget->window->is_decorated) {
203 sysarg_t width = widget->width;
204 sysarg_t height = widget->height;
205
206 bool btn_left = (event.btn_num == 1) && (event.type == POS_PRESS);
207 bool btn_right = (event.btn_num == 2) && (event.type == POS_PRESS);
208 bool allowed_button = btn_left || btn_right;
209
210 bool left = (event.hpos < border_thickness);
211 bool right = (event.hpos >= width - border_thickness);
212 bool top = (event.vpos < border_thickness);
213 bool bottom = (event.vpos >= height - border_thickness);
214 bool header = (event.hpos >= border_thickness) &&
215 (event.hpos < width - border_thickness) &&
216 (event.vpos >= border_thickness) &&
217 (event.vpos < border_thickness + header_height);
218 bool close = header && (event.hpos >= width - border_thickness - close_width);
219
220 if (top && left && allowed_button) {
221 window_grab_flags_t flags = GF_EMPTY;
222 flags |= GF_MOVE_X;
223 flags |= GF_MOVE_Y;
224 flags |= btn_left ? GF_RESIZE_X : GF_SCALE_X;
225 flags |= btn_left ? GF_RESIZE_Y : GF_SCALE_Y;
226 win_grab(widget->window->osess, event.pos_id, flags);
227 } else if (bottom && left && allowed_button) {
228 window_grab_flags_t flags = GF_EMPTY;
229 flags |= GF_MOVE_X;
230 flags |= btn_left ? GF_RESIZE_X : GF_SCALE_X;
231 flags |= btn_left ? GF_RESIZE_Y : GF_SCALE_Y;
232 win_grab(widget->window->osess, event.pos_id, flags);
233 } else if (bottom && right && allowed_button) {
234 window_grab_flags_t flags = GF_EMPTY;
235 flags |= btn_left ? GF_RESIZE_X : GF_SCALE_X;
236 flags |= btn_left ? GF_RESIZE_Y : GF_SCALE_Y;
237 win_grab(widget->window->osess, event.pos_id, flags);
238 } else if (top && right && allowed_button) {
239 window_grab_flags_t flags = GF_EMPTY;
240 flags |= GF_MOVE_Y;
241 flags |= btn_left ? GF_RESIZE_X : GF_SCALE_X;
242 flags |= btn_left ? GF_RESIZE_Y : GF_SCALE_Y;
243 win_grab(widget->window->osess, event.pos_id, flags);
244 } else if (top && allowed_button) {
245 window_grab_flags_t flags = GF_EMPTY;
246 flags |= GF_MOVE_Y;
247 flags |= btn_left ? GF_RESIZE_Y : GF_SCALE_Y;
248 win_grab(widget->window->osess, event.pos_id, flags);
249 } else if (left && allowed_button) {
250 window_grab_flags_t flags = GF_EMPTY;
251 flags |= GF_MOVE_X;
252 flags |= btn_left ? GF_RESIZE_X : GF_SCALE_X;
253 win_grab(widget->window->osess, event.pos_id, flags);
254 } else if (bottom && allowed_button) {
255 window_grab_flags_t flags = GF_EMPTY;
256 flags |= btn_left ? GF_RESIZE_Y : GF_SCALE_Y;
257 win_grab(widget->window->osess, event.pos_id, flags);
258 } else if (right && allowed_button) {
259 window_grab_flags_t flags = GF_EMPTY;
260 flags |= btn_left ? GF_RESIZE_X : GF_SCALE_X;
261 win_grab(widget->window->osess, event.pos_id, flags);
262 } else if (close && btn_left) {
263 win_close_request(widget->window->osess);
264 } else if (header && btn_left) {
265 window_grab_flags_t flags = GF_EMPTY;
266 flags |= GF_MOVE_X;
267 flags |= GF_MOVE_Y;
268 win_grab(widget->window->osess, event.pos_id, flags);
269 } else {
270 list_foreach(widget->children, link) {
271 widget_t *child = list_get_instance(link, widget_t, link);
272 child->handle_position_event(child, event);
273 }
274 }
275 } else {
276 list_foreach(widget->children, link) {
277 widget_t *child = list_get_instance(link, widget_t, link);
278 child->handle_position_event(child, event);
279 }
280 }
281}
282
283static void deliver_keyboard_event(window_t *win, kbd_event_t event)
284{
285 if (win->focus) {
286 win->focus->handle_keyboard_event(win->focus, event);
287 } else {
288 win->root.handle_keyboard_event(&win->root, event);
289 }
290}
291
292static void deliver_position_event(window_t *win, pos_event_t event)
293{
294 if (win->grab) {
295 win->grab->handle_position_event(win->grab, event);
296 } else {
297 win->root.handle_position_event(&win->root, event);
298 }
299}
300
301static void handle_signal_event(window_t *win, sig_event_t event)
302{
303 widget_t *widget = (widget_t *) event.object;
304 slot_t slot = (slot_t) event.slot;
305 void *data = (void *) event.argument;
306
307 slot(widget, data);
308
309 free(data);
310}
311
312static void handle_resize(window_t *win, sysarg_t width, sysarg_t height)
313{
314 int rc;
315 surface_t *old_surface;
316 surface_t *new_surface;
317
318 if (width < 2 * border_thickness + header_min_width) {
319 win_damage(win->osess, 0, 0, 0, 0);
320 return;
321 }
322
323 if (height < 2 * border_thickness + header_height) {
324 win_damage(win->osess, 0, 0, 0, 0);
325 return;
326 }
327
328 /* Allocate resources for new surface. */
329 new_surface = surface_create(width, height, NULL, SURFACE_FLAG_SHARED);
330 if (!new_surface) {
331 return;
332 }
333
334 /* Switch new and old surface. */
335 fibril_mutex_lock(&win->guard);
336 old_surface = win->surface;
337 win->surface = new_surface;
338 fibril_mutex_unlock(&win->guard);
339
340 /* Let all widgets in the tree alter their position and size. Widgets might
341 * also paint themselves onto the new surface. */
342 win->root.rearrange(&win->root, 0, 0, width, height);
343
344 fibril_mutex_lock(&win->guard);
345 surface_reset_damaged_region(win->surface);
346 fibril_mutex_unlock(&win->guard);
347
348 /* Inform compositor about new surface. */
349 rc = win_resize(win->osess,
350 width, height, surface_direct_access(new_surface));
351
352 if (rc != EOK) {
353 /* Rollback to old surface. Reverse all changes. */
354
355 sysarg_t old_width = 0;
356 sysarg_t old_height = 0;
357 if (old_surface) {
358 surface_get_resolution(old_surface, &old_width, &old_height);
359 }
360
361 fibril_mutex_lock(&win->guard);
362 new_surface = win->surface;
363 win->surface = old_surface;
364 fibril_mutex_unlock(&win->guard);
365
366 win->root.rearrange(&win->root, 0, 0, old_width, old_height);
367
368 if (win->surface) {
369 fibril_mutex_lock(&win->guard);
370 surface_reset_damaged_region(win->surface);
371 fibril_mutex_unlock(&win->guard);
372 }
373
374 surface_destroy(new_surface);
375 return;
376 }
377
378 /* Finally deallocate old surface. */
379 if (old_surface) {
380 surface_destroy(old_surface);
381 }
382}
383
384static void handle_refresh(window_t *win)
385{
386 win->root.repaint(&win->root);
387}
388
389static void handle_damage(window_t *win)
390{
391 sysarg_t x, y, width, height;
392 fibril_mutex_lock(&win->guard);
393 surface_get_damaged_region(win->surface, &x, &y, &width, &height);
394 surface_reset_damaged_region(win->surface);
395 fibril_mutex_unlock(&win->guard);
396
397 if (width > 0 && height > 0) {
398 /* Notify compositor. */
399 win_damage(win->osess, x, y, width, height);
400 }
401}
402
403static void destroy_children(widget_t *widget)
404{
405 /* Recursively destroy widget tree in bottom-top order. */
406 while (!list_empty(&widget->children)) {
407 widget_t *child =
408 list_get_instance(list_first(&widget->children), widget_t, link);
409 destroy_children(child);
410 child->destroy(child);
411 }
412}
413
414static void handle_close(window_t *win)
415{
416 destroy_children(&win->root);
417 win->root.destroy(&win->root);
418 win->grab = NULL;
419 win->focus = NULL;
420
421 win_close(win->osess);
422 async_hangup(win->isess);
423 async_hangup(win->osess);
424
425 while (!list_empty(&win->events.list)) {
426 list_remove(list_first(&win->events.list));
427 }
428
429 if (win->surface) {
430 surface_destroy(win->surface);
431 }
432
433 free(win->caption);
434
435 free(win);
436}
437
438/* Window event loop. Runs in own dedicated fibril. */
439static int event_loop(void *arg)
440{
441 bool is_main = false;
442 bool terminate = false;
443 window_t *win = (window_t *) arg;
444
445 while (true) {
446 window_event_t *event = (window_event_t *) prodcons_consume(&win->events);
447
448 switch (event->type) {
449 case ET_KEYBOARD_EVENT:
450 deliver_keyboard_event(win, event->data.kbd);
451 break;
452 case ET_POSITION_EVENT:
453 if (!win->is_focused) {
454 win->is_focused = true;
455 handle_refresh(win);
456 }
457 deliver_position_event(win, event->data.pos);
458 break;
459 case ET_SIGNAL_EVENT:
460 handle_signal_event(win, event->data.sig);
461 break;
462 case ET_WINDOW_RESIZE:
463 handle_resize(win, event->data.rsz.width, event->data.rsz.height);
464 break;
465 case ET_WINDOW_FOCUS:
466 if (!win->is_focused) {
467 win->is_focused = true;
468 handle_refresh(win);
469 }
470 break;
471 case ET_WINDOW_UNFOCUS:
472 if (win->is_focused) {
473 win->is_focused = false;
474 handle_refresh(win);
475 }
476 break;
477 case ET_WINDOW_REFRESH:
478 handle_refresh(win);
479 break;
480 case ET_WINDOW_DAMAGE:
481 handle_damage(win);
482 break;
483 case ET_WINDOW_CLOSE:
484 is_main = win->is_main;
485 handle_close(win);
486 terminate = true;
487 break;
488 default:
489 break;
490 }
491
492 free(event);
493 if (terminate) {
494 break;
495 }
496 }
497
498 if (is_main) {
499 exit(0); /* Terminate whole task. */
500 }
501 return 0;
502}
503
504/* Input fetcher from compositor. Runs in own dedicated fibril. */
505static int fetch_input(void *arg)
506{
507 int rc;
508 bool terminate = false;
509 window_t *win = (window_t *) arg;
510
511 while (true) {
512 window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
513
514 if (event) {
515 rc = win_get_event(win->isess, event);
516 if (rc == EOK) {
517 terminate = (event->type == ET_WINDOW_CLOSE);
518 link_initialize(&event->link);
519 prodcons_produce(&win->events, &event->link);
520 } else {
521 free(event);
522 terminate = true;
523 }
524 } else {
525 terminate = true;
526 }
527
528 if (terminate) {
529 break;
530 }
531 }
532
533 return 0;
534}
535
536window_t *window_open(char *winreg, bool is_main, bool is_decorated,
537 const char *caption, sysarg_t x_offset, sysarg_t y_offset)
538{
539 int rc;
540
541 window_t *win = (window_t *) malloc(sizeof(window_t));
542 if (!win) {
543 return NULL;
544 }
545
546 win->is_main = is_main;
547 win->is_decorated = is_decorated;
548 win->is_focused = true;
549 prodcons_initialize(&win->events);
550 fibril_mutex_initialize(&win->guard);
551 widget_init(&win->root, NULL);
552 win->root.window = win;
553 win->root.destroy = root_destroy;
554 win->root.reconfigure = root_reconfigure;
555 win->root.rearrange = root_rearrange;
556 win->root.repaint = root_repaint;
557 win->root.handle_keyboard_event = root_handle_keyboard_event;
558 win->root.handle_position_event = root_handle_position_event;
559 win->grab = NULL;
560 win->focus = NULL;
561 win->surface = NULL;
562
563 service_id_t reg_dsid;
564 async_sess_t *reg_sess;
565
566 rc = loc_service_get_id(winreg, &reg_dsid, 0);
567 if (rc != EOK) {
568 free(win);
569 return NULL;
570 }
571
572 reg_sess = loc_service_connect(EXCHANGE_SERIALIZE, reg_dsid, 0);
573 if (reg_sess == NULL) {
574 free(win);
575 return NULL;
576 }
577
578 service_id_t in_dsid;
579 service_id_t out_dsid;
580
581 rc = win_register(reg_sess, &in_dsid, &out_dsid, x_offset, y_offset);
582 async_hangup(reg_sess);
583 if (rc != EOK) {
584 free(win);
585 return NULL;
586 }
587
588 win->osess = loc_service_connect(EXCHANGE_SERIALIZE, out_dsid, 0);
589 if (win->osess == NULL) {
590 free(win);
591 return NULL;
592 }
593
594 win->isess = loc_service_connect(EXCHANGE_SERIALIZE, in_dsid, 0);
595 if (win->isess == NULL) {
596 async_hangup(win->osess);
597 free(win);
598 return NULL;
599 }
600
601 if (caption == NULL) {
602 win->caption = NULL;
603 } else {
604 win->caption = str_dup(caption);
605 }
606
607 return win;
608}
609
610void window_resize(window_t *win, sysarg_t width, sysarg_t height)
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_RESIZE;
616 event->data.rsz.width = width;
617 event->data.rsz.height = height;
618 prodcons_produce(&win->events, &event->link);
619 }
620}
621
622void window_refresh(window_t *win)
623{
624 window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
625 if (event) {
626 link_initialize(&event->link);
627 event->type = ET_WINDOW_REFRESH;
628 prodcons_produce(&win->events, &event->link);
629 }
630}
631
632void window_damage(window_t *win)
633{
634 window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
635 if (event) {
636 link_initialize(&event->link);
637 event->type = ET_WINDOW_DAMAGE;
638 prodcons_produce(&win->events, &event->link);
639 }
640}
641
642widget_t *window_root(window_t *win)
643{
644 return &win->root;
645}
646
647void window_exec(window_t *win)
648{
649 fid_t ev_fid = fibril_create(event_loop, win);
650 fid_t fi_fid = fibril_create(fetch_input, win);
651 if (!ev_fid || !fi_fid) {
652 return;
653 }
654 fibril_add_ready(ev_fid);
655 fibril_add_ready(fi_fid);
656}
657
658surface_t *window_claim(window_t *win)
659{
660 fibril_mutex_lock(&win->guard);
661 return win->surface;
662}
663
664void window_yield(window_t *win)
665{
666 fibril_mutex_unlock(&win->guard);
667}
668
669void window_close(window_t *win)
670{
671 /* Request compositor to init closing cascade. */
672 win_close_request(win->osess);
673}
674
675/** @}
676 */
Note: See TracBrowser for help on using the repository browser.