source: mainline/uspace/lib/gui/window.c@ 34cb6c8

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

Winreg argument should be const.

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