source: mainline/uspace/lib/gui/window.c@ 21eeb653

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 21eeb653 was 21eeb653, checked in by Petr Koupy <petr.koupy@…>, 12 years ago

Fixed memory leak in widget toolkit.

  • Property mode set to 100644
File size: 18.0 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 window_event_t *event = (window_event_t *) list_first(&win->events.list);
427 list_remove(&event->link);
428 free(event);
429 }
430
431 if (win->surface) {
432 surface_destroy(win->surface);
433 }
434
435 free(win->caption);
436
437 free(win);
438}
439
440/* Window event loop. Runs in own dedicated fibril. */
441static int event_loop(void *arg)
442{
443 bool is_main = false;
444 bool terminate = false;
445 window_t *win = (window_t *) arg;
446
447 while (true) {
448 window_event_t *event = (window_event_t *) prodcons_consume(&win->events);
449
450 switch (event->type) {
451 case ET_KEYBOARD_EVENT:
452 deliver_keyboard_event(win, event->data.kbd);
453 break;
454 case ET_POSITION_EVENT:
455 if (!win->is_focused) {
456 win->is_focused = true;
457 handle_refresh(win);
458 }
459 deliver_position_event(win, event->data.pos);
460 break;
461 case ET_SIGNAL_EVENT:
462 handle_signal_event(win, event->data.sig);
463 break;
464 case ET_WINDOW_RESIZE:
465 handle_resize(win, event->data.rsz.width, event->data.rsz.height);
466 break;
467 case ET_WINDOW_FOCUS:
468 if (!win->is_focused) {
469 win->is_focused = true;
470 handle_refresh(win);
471 }
472 break;
473 case ET_WINDOW_UNFOCUS:
474 if (win->is_focused) {
475 win->is_focused = false;
476 handle_refresh(win);
477 }
478 break;
479 case ET_WINDOW_REFRESH:
480 handle_refresh(win);
481 break;
482 case ET_WINDOW_DAMAGE:
483 handle_damage(win);
484 break;
485 case ET_WINDOW_CLOSE:
486 is_main = win->is_main;
487 handle_close(win);
488 terminate = true;
489 break;
490 default:
491 break;
492 }
493
494 free(event);
495 if (terminate) {
496 break;
497 }
498 }
499
500 if (is_main) {
501 exit(0); /* Terminate whole task. */
502 }
503 return 0;
504}
505
506/* Input fetcher from compositor. Runs in own dedicated fibril. */
507static int fetch_input(void *arg)
508{
509 int rc;
510 bool terminate = false;
511 window_t *win = (window_t *) arg;
512
513 while (true) {
514 window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
515
516 if (event) {
517 rc = win_get_event(win->isess, event);
518 if (rc == EOK) {
519 terminate = (event->type == ET_WINDOW_CLOSE);
520 link_initialize(&event->link);
521 prodcons_produce(&win->events, &event->link);
522 } else {
523 free(event);
524 terminate = true;
525 }
526 } else {
527 terminate = true;
528 }
529
530 if (terminate) {
531 break;
532 }
533 }
534
535 return 0;
536}
537
538window_t *window_open(char *winreg, bool is_main, bool is_decorated,
539 const char *caption, sysarg_t x_offset, sysarg_t y_offset)
540{
541 int rc;
542
543 window_t *win = (window_t *) malloc(sizeof(window_t));
544 if (!win) {
545 return NULL;
546 }
547
548 win->is_main = is_main;
549 win->is_decorated = is_decorated;
550 win->is_focused = true;
551 prodcons_initialize(&win->events);
552 fibril_mutex_initialize(&win->guard);
553 widget_init(&win->root, NULL);
554 win->root.window = win;
555 win->root.destroy = root_destroy;
556 win->root.reconfigure = root_reconfigure;
557 win->root.rearrange = root_rearrange;
558 win->root.repaint = root_repaint;
559 win->root.handle_keyboard_event = root_handle_keyboard_event;
560 win->root.handle_position_event = root_handle_position_event;
561 win->grab = NULL;
562 win->focus = NULL;
563 win->surface = NULL;
564
565 service_id_t reg_dsid;
566 async_sess_t *reg_sess;
567
568 rc = loc_service_get_id(winreg, &reg_dsid, 0);
569 if (rc != EOK) {
570 free(win);
571 return NULL;
572 }
573
574 reg_sess = loc_service_connect(EXCHANGE_SERIALIZE, reg_dsid, 0);
575 if (reg_sess == NULL) {
576 free(win);
577 return NULL;
578 }
579
580 service_id_t in_dsid;
581 service_id_t out_dsid;
582
583 rc = win_register(reg_sess, &in_dsid, &out_dsid, x_offset, y_offset);
584 async_hangup(reg_sess);
585 if (rc != EOK) {
586 free(win);
587 return NULL;
588 }
589
590 win->osess = loc_service_connect(EXCHANGE_SERIALIZE, out_dsid, 0);
591 if (win->osess == NULL) {
592 free(win);
593 return NULL;
594 }
595
596 win->isess = loc_service_connect(EXCHANGE_SERIALIZE, in_dsid, 0);
597 if (win->isess == NULL) {
598 async_hangup(win->osess);
599 free(win);
600 return NULL;
601 }
602
603 if (caption == NULL) {
604 win->caption = NULL;
605 } else {
606 win->caption = str_dup(caption);
607 }
608
609 return win;
610}
611
612void window_resize(window_t *win, sysarg_t width, sysarg_t height)
613{
614 window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
615 if (event) {
616 link_initialize(&event->link);
617 event->type = ET_WINDOW_RESIZE;
618 event->data.rsz.width = width;
619 event->data.rsz.height = height;
620 prodcons_produce(&win->events, &event->link);
621 }
622}
623
624void window_refresh(window_t *win)
625{
626 window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
627 if (event) {
628 link_initialize(&event->link);
629 event->type = ET_WINDOW_REFRESH;
630 prodcons_produce(&win->events, &event->link);
631 }
632}
633
634void window_damage(window_t *win)
635{
636 window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
637 if (event) {
638 link_initialize(&event->link);
639 event->type = ET_WINDOW_DAMAGE;
640 prodcons_produce(&win->events, &event->link);
641 }
642}
643
644widget_t *window_root(window_t *win)
645{
646 return &win->root;
647}
648
649void window_exec(window_t *win)
650{
651 fid_t ev_fid = fibril_create(event_loop, win);
652 fid_t fi_fid = fibril_create(fetch_input, win);
653 if (!ev_fid || !fi_fid) {
654 return;
655 }
656 fibril_add_ready(ev_fid);
657 fibril_add_ready(fi_fid);
658}
659
660surface_t *window_claim(window_t *win)
661{
662 fibril_mutex_lock(&win->guard);
663 return win->surface;
664}
665
666void window_yield(window_t *win)
667{
668 fibril_mutex_unlock(&win->guard);
669}
670
671void window_close(window_t *win)
672{
673 /* Request compositor to init closing cascade. */
674 win_close_request(win->osess);
675}
676
677/** @}
678 */
Note: See TracBrowser for help on using the repository browser.